Index: leahi_dialin/common/msg_ids.py =================================================================== diff -u -r3024c73fd79f1590c89b183089b85b46a3b3eb1a -r53ad62b20a323d405916edafb195fdcf64164f7a --- leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 3024c73fd79f1590c89b183089b85b46a3b3eb1a) +++ leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 53ad62b20a323d405916edafb195fdcf64164f7a) @@ -187,6 +187,7 @@ MSG_ID_UI_RECIRCULATE_REQUEST = 0xA6 MSG_ID_TD_RECIRCULATE_RESPONSE = 0xA7 MSG_ID_TD_RECIRCULATE_DATA = 0xA8 + MSG_ID_TD_SYRINGE_PUMP_DATA = 0xA9 MSG_ID_DD_PISTON_PUMP_CONTROL_DATA = 0xF0 @@ -260,6 +261,20 @@ MSG_ID_TD_TEMPERATURE_PUBLISH_INTERVAL_OVERRIDE_REQUEST = 0x8043 MSG_ID_TD_EJECTOR_OPT_SENSOR_OVERRIDE_REQUEST = 0x8044 MSG_ID_TD_ENABLE_VENOUS_BUBBLE_ALARM = 0x8047 + MSG_ID_TD_SYRINGE_PUMP_OPERATION_REQUEST = 0x8048 + MSG_ID_HD_SYRINGE_PUMP_PUBLISH_INTERVAL_OVERRIDE = 0x8049 + MSG_ID_TD_SYRINGE_PUMP_RATE_OVERRIDE_REQUEST = 0x8050 + MSG_ID_TD_SYRINGE_PUMP_FORCE_OVERRIDE_REQUEST = 0x8051 + MSG_ID_TD_SYRINGE_PUMP_SYRINGE_DETECT_OVERRIDE_REQUEST = 0x8052 + MSG_ID_TD_SYRINGE_PUMP_HOME_OVERRIDE_REQUEST = 0x8053 + MSG_ID_TD_SYRINGE_PUMP_POSITION_OVERRIDE_REQUEST = 0x8054 + MSG_ID_TD_SYRINGE_PUMP_VOLUME_OVERRIDE_REQUEST = 0x8055 + MSG_ID_TD_SYRINGE_PUMP_STATUS_OVERRIDE_REQUEST = 0x8056 + MSG_ID_TD_SYRINGE_PUMP_ENCODER_STATUS_OVERRIDE_REQUEST = 0x8057 + MSG_ID_TD_SYRINGE_PUMP_ADC_DAC_STATUS_OVERRIDE_REQUEST = 0x8058 + MSG_ID_TD_SYRINGE_PUMP_ADC_READ_COUNTER_OVERRIDE_REQUEST = 0x8059 + MSG_ID_TD_SYRINGE_PUMP_FORCE_SENSOR_DAC_OVERRIDE_REQUEST = 0x8060 + MSG_ID_TD_HEPARIN_BOLUS_TARGET_RATE_OVERRIDE_REQUEST = 0x8061 MSG_ID_TD_TRAINING_TEST_OVERRIDE_REQUEST = 0x8999 Index: leahi_dialin/common/td_defs.py =================================================================== diff -u -r3ca042a93b178bc02327efec6cf08dd2348f9a8e -r53ad62b20a323d405916edafb195fdcf64164f7a --- leahi_dialin/common/td_defs.py (.../td_defs.py) (revision 3ca042a93b178bc02327efec6cf08dd2348f9a8e) +++ leahi_dialin/common/td_defs.py (.../td_defs.py) (revision 53ad62b20a323d405916edafb195fdcf64164f7a) @@ -572,7 +572,20 @@ 'NUM_OF_DIALYSIS_STATES': [], } +# Syringe pump states +@unique +class TDTreaSyringePumpStates(DialinEnum): + SYRINGE_PUMP_INIT_STATE = 0 # Syringe pump initialize state + SYRINGE_PUMP_OFF_STATE = 1 # Syringe pump off state + SYRINGE_PUMP_RETRACT_STATE = 2 # Syringe pump retract state + SYRINGE_PUMP_PRELOAD_STATE = 3 # Syringe pump preload state + SYRINGE_PUMP_SEEK_STATE = 4 # Syringe pump seek state + SYRINGE_PUMP_PRIME_STATE = 5 # Syringe pump prime state + SYRINGE_PUMP_HEP_BOLUS_STATE = 6 # Syringe pump bolus state + SYRINGE_PUMP_HEP_CONTINUOUS_STATE = 7 # Syringe pump continuous state + SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE = 8 # Syringe pump configure force sensor state + # TDTreatmentStates.TREATMENT_END_STATE sub states @unique class TDTreaEndStates(DialinEnum): @@ -1129,6 +1142,16 @@ 'NUM_OF_SWITCH_STATES': [], } +# Syringe pump operations +@unique +class SyringePumpOperations(DialinEnum): + SYRINGE_PUMP_OP_STOP = 0 # Stop syringe pump + SYRINGE_PUMP_OP_RETRACT = 1 # Retract syringe pump + SYRINGE_PUMP_OP_SEEK = 2 # Seek plunger + SYRINGE_PUMP_OP_PRIME = 3 # Prime Heparin line + SYRINGE_PUMP_OP_BOLUS = 4 # Deliver Heparin bolus of set volume over 5 minutes + SYRINGE_PUMP_OP_CONTINUOUS = 5 # Continuous dispense of Heparin at set rate + SYRINGE_PUMP_OP_PRELOAD = 6 # Preload syringe pump @unique class TDValvePositions(DialinEnum): @@ -1221,4 +1244,4 @@ 'TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME': ['heparin volume'], 'TREATMENT_PARAM_UF_VOLUME': ['uf volume', 'ultrafiltration volume'], 'NUM_OF_TREATMENT_PARAMS': [], -} +} \ No newline at end of file Index: leahi_dialin/td/modules/syringe_pump.py =================================================================== diff -u --- leahi_dialin/td/modules/syringe_pump.py (revision 0) +++ leahi_dialin/td/modules/syringe_pump.py (revision 53ad62b20a323d405916edafb195fdcf64164f7a) @@ -0,0 +1,794 @@ +########################################################################### +# +# Copyright (c) 2021-2025 Diality Inc. - All Rights Reserved. +# +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# +# @file syringe_pump.py +# +# @author (last) Micahel Garthwaite +# @date (last) 17-Aug-2023 +# @author (original) Sean Nash +# @date (original) 12-Mar-2021 +# +############################################################################ +import struct +from logging import Logger + +from leahi_dialin.common.constants import RESET, NO_RESET +from leahi_dialin.common.td_defs import TDTreaHeparinStates, TDTreaSyringePumpStates, SyringePumpOperations +from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels +from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.utils.checks import check_broadcast_interval_override_ms +from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray + + +class TDSyringePump(AbstractSubSystem): + """ + TDSyringePump + + Treatment Delivery (TD) Dialin API sub-class for syringe pump related commands. + """ + + def __init__(self, can_interface, logger: Logger): + """ + + @param can_interface: Denali Can Messenger object + """ + super().__init__() + self.can_interface = can_interface + self.logger = logger + + if self.can_interface is not None: + channel_id = DenaliChannels.td_sync_broadcast_ch_id + msg_id = MsgIds.MSG_ID_TD_SYRINGE_PUMP_DATA.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_syringe_pump_data) + + self.syringe_pump_state = TDTreaSyringePumpStates.SYRINGE_PUMP_INIT_STATE.value + self.heparin_state = TDTreaHeparinStates.HEPARIN_STATE_OFF.value + self.syringe_pump_set_rate_ml_hr = 0.0 + self.syringe_pump_meas_rate_ml_hr = 0.0 + self.syringe_pump_position = 0 + self.syringe_pump_volume_ml = 0.0 + self.syringe_pump_safety_volume_ml = 0.0 + self.syringe_pump_home_v = 0.0 + self.syringe_pump_switch_v = 0.0 + self.syringe_pump_force_v = 0.0 + self.syringe_pump_status = 0 + self.syringe_pump_encoder_status = 0 + self.syringe_pump_adc_dac_status = 0 + self.syringe_pump_adc_read_counter = 0 + self.td_syringe_pump_timestamp = 0.0 + + def get_syringe_pump_state(self): + """ + Gets the current syringe pump state. + + @return: latest published syringe pump state. + SYRINGE_PUMP_INIT_STATE = 0 + SYRINGE_PUMP_OFF_STATE = 1 + SYRINGE_PUMP_RETRACT_STATE = 2 + SYRINGE_PUMP_SEEK_STATE = 3 + SYRINGE_PUMP_PRIME_STATE = 4 + SYRINGE_PUMP_HEP_BOLUS_STATE = 5 + SYRINGE_PUMP_HEP_CONTINUOUS_STATE = 6 + SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE = 7 + """ + return self.syringe_pump_state + + def get_heparin_state(self): + """ + Gets the current Heparin state. + + @return: latest published Heparin state. + HEPARIN_STATE_OFF = 0 + HEPARIN_STATE_STOPPED = 1 + HEPARIN_STATE_PAUSED = 2 + HEPARIN_STATE_INITIAL_BOLUS = 3 + HEPARIN_STATE_DISPENSING = 4 + HEPARIN_STATE_COMPLETED = 5 + HEPARIN_STATE_EMPTY = 6 + """ + return self.heparin_state + + def get_syringe_pump_set_rate(self): + """ + Gets the current set syringe pump rate. + + @return: latest published syringe pump set rate (in mL/hr). + """ + return self.syringe_pump_set_rate_ml_hr + + def get_syringe_pump_meas_rate(self): + """ + Gets the current measured syringe pump rate. + + @return: latest published syringe pump measured rate (in mL/hr). + """ + return self.syringe_pump_meas_rate_ml_hr + + def get_syringe_pump_position(self): + """ + Gets the current syringe pump position. + + @return: latest published syringe pump position (in encoder counts). + """ + return self.syringe_pump_position + + def get_syringe_pump_volume_delivered_ml(self): + """ + Gets the current syringe pump volume delivered. + + @return: latest published syringe pump volume delivered (in mL). + """ + return self.syringe_pump_volume_ml + + def get_syringe_pump_home_v(self): + """ + Gets the current syringe pump home voltage reading + + @return: latest published voltage read from the home optical sensor + """ + return self.syringe_pump_home_v + + def get_syringe_pump_switch_v(self): + """ + Gets the current syringe pump switch voltage reading + + @return: latest published voltage read from the syringe detection switch + """ + return self.syringe_pump_switch_v + + def get_syringe_pump_force_v(self): + """ + Gets the current syringe pump force voltage reading + + @return: latest published voltage read from the force sensor + """ + return self.syringe_pump_force_v + + def get_syringe_pump_safety_volume(self): + """ + Gets the current syringe pump safety volume reading + + @return: latest published safety volume calculated by TD firmware + """ + return self.syringe_pump_safety_volume_ml + + def get_syringe_pump_status(self): + """ + Gets the current syringe pump status + + @return: latest published syringe pump status by TD firmware + """ + return self.syringe_pump_status + + def get_syringe_pump_encoder_status(self): + """ + Gets the current syringe pump encoder status + + @return: latest published syringe pump encoder status by TD firmware + """ + return self.syringe_pump_encoder_status + + def get_syringe_pump_adc_dac_status(self): + """ + Gets the current syringe pump ADC & DAC status + + @return: latest published syringe pump ADC & DAC status by TD firmware + """ + return self.syringe_pump_adc_dac_status + + def get_syringe_pump_adc_read_counter(self): + """ + Gets the current syringe pump ADC read counter + + @return: latest published ADC read counter by TD firmware + """ + return self.syringe_pump_adc_read_counter + + @publish(["td_syringe_pump_timestamp", + "syringe_pump_state", "syringe_pump_set_rate_ml_hr", + "syringe_pump_meas_rate_ml_hr", "syringe_pump_position", + "syringe_pump_volume_ml", "syringe_pump_home_v", + "syringe_pump_switch_v", "syringe_pump_force_v", + "heparin_state", "syringe_pump_safety_volume_ml", + "syringe_pump_status", "syringe_pump_encoder_status", + "syringe_pump_adc_dac_status", "syringe_pump_adc_read_counter"]) + def _handler_syringe_pump_data(self, message, timestamp=0.0): + """ + Handles published syringe pump data messages. Syringe pump data are captured + for reference. + + @param message: published syringe pump data message + @return: None + """ + + sta = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + hep = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + srt = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + mrt = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) + pos = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) + vol = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) + hom = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) + det = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) + frc = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9])) + saf = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10])) + sts = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_11:MsgFieldPositions.END_POS_FIELD_11])) + + self.syringe_pump_state = sta[0] + self.heparin_state = hep[0] + self.syringe_pump_set_rate_ml_hr = srt[0] + self.syringe_pump_meas_rate_ml_hr = mrt[0] + self.syringe_pump_position = pos[0] + self.syringe_pump_volume_ml = vol[0] + self.syringe_pump_home_v = hom[0] + self.syringe_pump_switch_v = det[0] + self.syringe_pump_force_v = frc[0] + self.syringe_pump_safety_volume_ml = saf[0] + self.syringe_pump_status = (sts[0] & 0xFF000000) >> 24 + self.syringe_pump_encoder_status = (sts[0] & 0x00FF0000) >> 16 + self.syringe_pump_adc_dac_status = (sts[0] & 0x0000FF00) >> 8 + self.syringe_pump_adc_read_counter = (sts[0] & 0x000000FF) + self.td_syringe_pump_timestamp = timestamp + + + def cmd_syringe_pump_operation(self, operation: int = SyringePumpOperations.SYRINGE_PUMP_OP_STOP.value, + rate: float = 0.0, + volume: float = 0.0) -> int: + """ + Constructs and sends the syringe pump operation command + Constraints: + Must be logged into TD. + Syringe pump must be in appropriate state for the given operation. + Given rate/volume (when applicable) must be within valid range for the given operation. + The following Treatment parameters should be set before executing operations 3,4 & 5: + - Treatment Duration + - Heparin Pre-Stop Time + - Rate if volume is being sent + - Volume if rate is being sent + Failure to set these treatment parameters may result in faulting the TD device. + + @param operation: unsigned int - ID of operation being requested + @param rate: float - target rate for given operation (if applicable) + @param volume: float - target volume for given operation (if applicable) + @return: 1 if successful, zero otherwise + + Syringe pump operation IDs: + SYRINGE_PUMP_OP_STOP = 0 + SYRINGE_PUMP_OP_RETRACT = 1 + SYRINGE_PUMP_OP_SEEK = 2 + SYRINGE_PUMP_OP_PRIME = 3 + SYRINGE_PUMP_OP_BOLUS = 4 + SYRINGE_PUMP_OP_CONTINUOUS = 5 + """ + + op = integer_to_bytearray(operation) + rat = float_to_bytearray(rate) + vol = float_to_bytearray(volume) + payload = op + rat + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_OPERATION_REQUEST.value, + payload=payload) + + self.logger.debug("requesting syringe pump operation " + str(operation) + + ", rate=" + str(rate) + + ", volume=" + str(volume)) + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_data_broadcast_interval_override(self, ms: int = 1000, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump data broadcast interval override command + Constraints: + Must be logged into TD. + Given interval must be non-zero and a multiple of the TD general task interval (50 ms). + + @param ms: integer - interval (in ms) to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + if not check_broadcast_interval_override_ms(ms): + return False + + rst = integer_to_bytearray(reset) + mis = integer_to_bytearray(ms) + payload = rst + mis + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_HD_SYRINGE_PUMP_PUBLISH_INTERVAL_OVERRIDE.value, + payload=payload) + + self.logger.debug("override TD syringe pump data broadcast interval") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(ms) + " ms: " + self.logger.debug("Syringe pump data broadcast interval overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_meas_rate_override(self, rate: float = 0.0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump measured rate override command + Constraints: + Must be logged into TD. + + @param rate: float - rate (in mL/hr) to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + rat = float_to_bytearray(rate) + payload = rst + rat + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_RATE_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump measured rate") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(rate) + " mL/hr: " + self.logger.debug("Syringe pump measured rate overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_meas_force_override(self, volts: float = 0.0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump measured force override command + Constraints: + Must be logged into TD. + + @param volts: float - volts to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + vol = float_to_bytearray(volts) + payload = rst + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_FORCE_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump measured force") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(volts) + " volts: " + self.logger.debug("Syringe pump measured force overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_meas_detect_override(self, volts: float = 0.0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump measured syringe detect override command + Constraints: + Must be logged into TD. + + @param volts: float - volts to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + vol = float_to_bytearray(volts) + payload = rst + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_SYRINGE_DETECT_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump measured syringe detection") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(volts) + " volts: " + self.logger.debug("Syringe pump measured syringe detection overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + + def cmd_syringe_pump_meas_home_override(self, volts: float = 0.0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump measured home override command + Constraints: + Must be logged into TD. + + @param volts: float - volts to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + vol = float_to_bytearray(volts) + payload = rst + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_HOME_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump measured home") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(volts) + " volts: " + self.logger.debug("Syringe pump measured home overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_meas_position_override(self, position: int = 0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump measured position override command + Constraints: + Must be logged into TD. + + @param position: integer - position (in encoder counts) to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + pos = integer_to_bytearray(position) + payload = rst + pos + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_POSITION_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump measured position") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(position) + " encoder counts: " + self.logger.debug("Syringe pump measured position overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_meas_volume_override(self, volume: float = 0.0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump measured volume override command + Constraints: + Must be logged into TD. + + @param volume: float - volume (in mL) to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + vol = float_to_bytearray(volume) + payload = rst + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_VOLUME_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump measured volume delivered") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(volume) + " mL: " + self.logger.debug("Syringe pump measured volume overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_status_override(self, status: int = 0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump status override command + Constraints: + Must be logged into TD. + + @param status: integer - status (0..255) + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + sts = integer_to_bytearray(status & 0x000000FF) + payload = rst + sts + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_STATUS_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump status") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(status) + self.logger.debug("Syringe pump status overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_encoder_status_override(self, status: int = 0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump encoder status override command + Constraints: + Must be logged into TD. + + @param status: integer - encoder status (0..255) + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + sts = integer_to_bytearray(status & 0x000000FF) + payload = rst + sts + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_ENCODER_STATUS_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump encoder status") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(status) + self.logger.debug("Syringe pump encoder status overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_adc_dac_status_override(self, status: int = 0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump ADC & DAC status override command + Constraints: + Must be logged into TD. + + @param status: integer - ADC and DAC status (0..255) + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + sts = integer_to_bytearray(status & 0x000000FF) + payload = rst + sts + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_ADC_DAC_STATUS_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump ADC & DAC status") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(status) + self.logger.debug("Syringe pump ADC & DAC status overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_syringe_pump_adc_read_counter_override(self, counter: int = 0, reset: int = NO_RESET) -> int: + """ + Constructs and sends the syringe pump ADC read counter override command + Constraints: + Must be logged into TD. + + @param counter: integer - status (0..255) + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + ctr = integer_to_bytearray(counter & 0x000000FF) + payload = rst + ctr + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_ADC_READ_COUNTER_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("override TD syringe pump ADC read counter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(counter) + self.logger.debug("Syringe pump ADC read counter overridden to " + str_res + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + def cmd_set_syringe_pump_dac_ref_voltage_override(self) -> int: + """ + Constructs and sends the set syringe pump DAC vRef. The value the DAC is set to is within TD Calibration Record. + Constraints: + Must be logged into TD. + + @return: 1 if successful, zero otherwise + """ + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_SYRINGE_PUMP_FORCE_SENSOR_DAC_OVERRIDE_REQUEST.value, + ) + + self.logger.debug("Set TD syringe pump DAC reference voltage") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + self.logger.debug("Syringe pump DAC reference voltage set.") + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + + def cmd_heparin_target_rate_override(self, rate: float, reset: int = NO_RESET) -> int: + """ + Constructs and sends the heparin bolus target rate value override command + Constraints: + Must be logged into TD. + + @param rate: (float) the heparin bolus target rate to be set in mL/hour + @param reset: (int) 1 to reset a previous override, 0 to override + @return 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + vlu = float_to_bytearray(rate) # TD expects the rate in mL/hour + payload = reset_value + vlu + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_TD_HEPARIN_BOLUS_TARGET_RATE_OVERRIDE_REQUEST.value, + payload=payload) + self.logger.debug("Overriding heparin bolus target rate value override") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Heparin bolus target value override Timeout!!!") + return False Index: leahi_dialin/td/treatment_delivery.py =================================================================== diff -u -r3f6c0e94db8379f00cb7fbc91daa189d6b06f379 -r53ad62b20a323d405916edafb195fdcf64164f7a --- leahi_dialin/td/treatment_delivery.py (.../treatment_delivery.py) (revision 3f6c0e94db8379f00cb7fbc91daa189d6b06f379) +++ leahi_dialin/td/treatment_delivery.py (.../treatment_delivery.py) (revision 53ad62b20a323d405916edafb195fdcf64164f7a) @@ -29,6 +29,7 @@ from .modules.events import TDEvents from .modules.pressure_sensors import TDPressureSensors from .modules.switches import TDSwitches +from .modules.syringe_pump import TDSyringePump from .modules.temperature_sensors import TDTemperatureSensors from .modules.treatment import TDTreatment from .modules.td_test_configs import TDTestConfig @@ -135,7 +136,8 @@ self.events = TDEvents(self.can_interface, self.logger) #: The Events module self.pressure_sensors = TDPressureSensors(self.can_interface, self.logger) #: The Pressure Sensors module self.switches = TDSwitches(self.can_interface, self.logger) #: The Switches module - self.temperature_sensors = TDTemperatureSensors(self.can_interface, self.logger) #: The Temperature Sensprs module + self.syringe_pump = TDSyringePump(self.can_interface, self.logger) #: The Syringe Pump module + self.temperature_sensors = TDTemperatureSensors(self.can_interface, self.logger) #: The Temperature Sensors module self.treatment = TDTreatment(self.can_interface, self.logger) #: The Treatment module self.test_configs = TDTestConfig(self.can_interface, self.logger) #: The Test Configs module self.valves = TDValves(self.can_interface, self.logger) #: The Valves module Index: tools/Log_dd_new.py =================================================================== diff -u --- tools/Log_dd_new.py (revision 0) +++ tools/Log_dd_new.py (revision 53ad62b20a323d405916edafb195fdcf64164f7a) @@ -0,0 +1,243 @@ +########################################################################### +# +# Copyright (c) 2024-2024 Diality Inc. - All Rights Reserved. +# +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# + +# @file test_dd.py +# +# @author (last) Vinayakam Mani +# @date (last) 26-Nov-2024 +# @author (original) Vinayakam Mani +# @date (original) 26-Nov-2024 +# +############################################################################ + +import sys +sys.path.append("..") +#from leahi_dialin.td.treatment_delivery import TD +from leahi_dialin import DD +from leahi_dialin.common import dd_enum_repository +#from leahi_dialin.td.valves import ValvesEnum +#from leahi_dialin.dd.temperatures import DGTemperaturesNames +from leahi_dialin.common.constants import NO_RESET, RESET +from time import sleep + +if __name__ == "__main__": + # create an TD object called td + #td = TD(log_level="DEBUG") + # create a DD object called dd + dd = DD(log_level="DEBUG") + sleep(2) + +# td.ui.cmd_ui_start_treatment_request() +# exit(0) + + # log in to TD and DD as tester +# if td.cmd_log_in_to_td() == 0: +# exit(1) + #while True: + # sleep(1) + if dd.cmd_log_in_to_dd() == 0: + #sleep(1) + exit(1) + sleep(5) + + #dd.dialysate_pumps.cmd_dialysate_set_start_stop(0,1,500) + #sleep(20) + + # valves = '{:4X}'.format(dd.valves.valve_states_all) + # print("DD Valves: " + valves) + # dd.valves.cmd_valve_override(1, True, NO_RESET) + # sleep(2) + # valves = '{:4X}'.format(dd.valves.valve_states_all) + # print("DD Valves: " + valves) + # sleep(5) + # dd.valves.cmd_valve_override(1, False, NO_RESET) + # exit(1) + + #dd.dialysate_pumps.cmd_dialysate_set_start_stop(0,1,300) + #sleep(30) + #dd.dialysate_pumps.cmd_dialysate_set_start_stop(0, 0, 300) + #sleep(5) + #dd.dialysate_pumps.cmd_dialysate_pump_target_speed_override(0, 500.0, NO_RESET) + #sleep(5) + #dd.dialysate_pumps.cmd_dialysate_pump_target_speed_override(0, 0.0, NO_RESET) + #exit(1) + + # dd.pressure_sensors.cmd_pressure_readings_override(1,10.0, RESET) + + #dd.pressure_sensors.cmd_pressure_broadcast_interval_override(50,0) + #sleep(0.5) + dd.balancing_chamber.cmd_balancing_chamber_broadcast_interval_override(50,0) + #sleep(0.5) + #dd.valves.cmd_valve_broadcast_interval_override(50,0) + #sleep(0.5) + #dd.concentrate_pumps.cmd_concentrate_pump_broadcast_interval_override(50,0) + #sleep(0.5) + #dd.spent_chamber_fill.cmd_spent_chamber_fill_broadcast_interval_override(50,0) + #sleep(0.5) + #dd.levels.cmd_levels_broadcast_interval_override(50,0) + #sleep(0.5) + #dd.dialysate_pumps.cmd_dialysate_pump_broadcast_interval_override(50,0) + #sleep(0.5) + #dd.drybicart.cmd_dry_bicart_broadcast_interval_override(50,0) + # dd.heaters.cmd_heaters_broadcast_interval_override(50,0) + # sleep(0.1) + # dd.temperature_sensors.cmd_temperatures_data_broadcast_interval_override(50,0) + + # create log file + with open("DD_test.log", "w") as f: + + # collect sensors log from DD + while True: + #if dd.levels.floater1 == 2: + #print (" float level is high") + #dd.valves.cmd_valve_override(12,False,0) + #else: + #print (" float level is low") + #dd.valves.cmd_valve_override(12,True,0) + #sleep(0.050) + sleep(0.05) + dialPump = ", D12.fr, " + '{:7.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_RPM.name]) + \ + ", D48.sr, " + '{:7.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_RPM.name]) + \ + ", D12.fs, " + '{:6.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_SPEED.name]) + \ + ", D48.ss, " + '{:7.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_SPEED.name]) + \ + ", D12.fc, " + '{:7.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CURRENT_SPEED.name]) + \ + ", D48.sc, " + '{:6.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CURRENT_SPEED.name]) + \ + ", D12.s1, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.STATE.name]) + \ + ", D48.s2, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.STATE.name]) + \ + ", D12.ft, " + '{:6.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_PRESSURE.name]) + \ + ", D48.st, " + '{:6.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_PRESSURE.name]) + \ + ", D12.fp, " + '{:7.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_PRESSURE.name]) + \ + ", D48.sp, " + '{:7.1f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_PRESSURE.name]) + \ + ", D12.fm, " + '{:8.2f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_CURRENT.name]) + \ + ", D48.sm, " + '{:8.2f}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_CURRENT.name]) + \ + ", D12.c1, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CONTROL.name]) + \ + ", D48.c2, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CONTROL.name]) + \ + ", D12.fd, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.DIRECTION_ERROR_COUNT.name]) + \ + ", D48.sd, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.DIRECTION_ERROR_COUNT.name]) + \ + ", D12.d1, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_DIRECTION.name]) + \ + ", D48.d2, " + '{:2d}'.format(dd.dialysate_pumps.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_DIRECTION.name]) + Levels = "L.d6, " + '{:2d}'.format(dd.levels.dd_level_sensors[dd_enum_repository.DDLevelSensorNames.D6_LEVEL.name]) + \ + ", L.d63, " + '{:2d}'.format(dd.levels.dd_level_sensors[dd_enum_repository.DDLevelSensorNames.D63_LEVEL.name]) + \ + ", L.d46, " + '{:2d}'.format(dd.levels.dd_level_sensors[dd_enum_repository.DDLevelSensorNames.D46_LEVEL.name]) + pressure = "P.d9, " + '{:8.1f}'.format(dd.pressure_sensors.dd_pressures[dd_enum_repository.DDPressureSensorNames.D9_PRES.name]) + \ + ", P.d66, " + '{:8.1f}'.format(dd.pressure_sensors.dd_pressures[dd_enum_repository.DDPressureSensorNames.D66_PRES.name]) + \ + ", P.d51, " + '{:8.1f}'.format(dd.pressure_sensors.dd_pressures[dd_enum_repository.DDPressureSensorNames.D51_PRES.name]) + \ + ", P.d18, " + '{:8.1f}'.format(dd.pressure_sensors.dd_pressures[dd_enum_repository.DDPressureSensorNames.D18_PRES.name]) + \ + ", P.d41, " + '{:8.1f}'.format(dd.pressure_sensors.dd_pressures[dd_enum_repository.DDPressureSensorNames.D41_PRES.name]) + valves = ", DDVlvs, " + '{:8X}'.format(dd.valves.valve_states_all) + #", V.d79, " + '{:2d}'.format(dd.valves.d79_pmp_valv) + Temperature = "Temp " + '{}'.format(dd.temperature_sensors.dd_temperatures) + ConcPumps = ", D11.cs, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name]) + \ + ", D11.MeS, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name]) + \ + ", D10.cs, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name] ) + \ + ", D10.MeS, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name]) + \ + ", D11.Trev, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name]) + \ + ", D11.Mrev, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name]) + \ + ", D10.Trev, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name]) + \ + ", D10.Mrev, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name]) + \ + ", D11.ex, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name]) + \ + ", D10.ex, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name]) + \ + ", D11.pul, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name]) + \ + ", D10.pul, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name]) + \ + ", D11.ts, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name]) + \ + ", D10.ts, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name]) + \ + ", D11.p, " + '{:b}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.PARKED.name]) + \ + ", D10.p, " + '{:b}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.PARKED.name]) + \ + ", D11.PF, " + '{:b}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.PARK_FAULT.name]) + \ + ", D10.PF, " + '{:b}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.PARK_FAULT.name] ) + \ + ", D76.ts, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name]) + \ + ", D76.cs, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name]) + \ + ", D76.MeS, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name]) + \ + ", D76.Trev, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name]) + \ + ", D76.Mrev, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name]) + \ + ", D76.ex, " + '{:2d}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name]) + \ + ", D76.pul, " + '{:8.1f}'.format(dd.concentrate_pumps.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name]) + # Heaters = ",D5.DC, " + '{:8.1f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_DUTY_CYCLE.name]) + \ + # ", D45.DC, " + '{:8.1f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D45_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_DUTY_CYCLE.name]) + \ + # ", D5.TT, " + '{:8.1f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_TARGET_TEMP.name]) + \ + # ", D45.TT, " + '{:8.1f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D45_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_TARGET_TEMP.name]) + \ + # ", D5.S, " + '{:2d}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_STATE.name] ) + \ + # ", D45.S, " + '{:2d}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D45_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_STATE.name]) + \ + # ", D5.duty, " + '{:8.3f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.DUTY_CYCLE_COUNT.name]) + \ + # ", D5.period, " + '{:8.3f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.PWM_PERIOD.name]) + \ + # ", D5.adjTar, " + '{:8.3f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.ADJUSTED_TARGET_TEMP.name]) + \ + # ", D5.TdTar, " + '{:8.3f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.TARGET_TEMP_TD.name]) + \ + # ", D5.eff, " + '{:8.3f}'.format(dd.heaters.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_EFFICIENCY.name]) + \ + # ", dbg1, " + '{:8.3f}'.format(dd.heaters.dbg1) + \ + # ", dbg2, " + '{:8.3f}'.format(dd.heaters.dbg2) + \ + # ", dbg3, " + '{:8.3f}'.format(dd.heaters.dbg3) + \ + # ", dbg4, " + '{:8.3f}'.format(dd.heaters.dbg4) + \ + # ", dbg5, " + '{:8.3f}'.format(dd.heaters.dbg5) + \ + # ", dbg6, " + '{:8.3f}'.format(dd.heaters.dbg6) + \ + # ", dbg7, " + '{:8.3f}'.format(dd.heaters.dbg7) + \ + # ", dbg8, " + '{:8.3f}'.format(dd.heaters.dbg8) + # # ", dbg9, " + '{:8.3f}'.format(dd.heaters.dbg9) + BalChamber = ", ExSt, " + '{:2d}'.format(dd.balancing_chamber.execution_state) + \ + ", SwSt, " + '{:2d}'.format(dd.balancing_chamber.switching_state) + \ + ", SwFr, " + '{:8.1f}'.format(dd.balancing_chamber.switching_frequency) + \ + ", SwPd, " + '{:2d}'.format(dd.balancing_chamber.switching_period) + \ + ", Fill, " + '{:2d}'.format(dd.balancing_chamber.bal_chamber_fill_in_progress) + \ + ", Cnt, " + '{:2d}'.format(dd.balancing_chamber.current_bal_chamber_switching_counter) + \ + ", PSt, " + '{:2d}'.format(dd.balancing_chamber.is_pressure_stabilized_during_fill) + \ + ", BSo, " + '{:2d}'.format(dd.balancing_chamber.bal_chamber_switch_only_state) + \ + ", BcSwEnbl, " + '{:2d}'.format(dd.balancing_chamber.is_bal_chamber_switching_active) +# GenDialysate = ", ExSt, " + '{:2d}'.format(dd.gen_dialysate.execution_state) + \ +# ", InPro, " + '{:2d}'.format(dd.gen_dialysate.dialysate_delivery_in_progress) + \ +# ", D9.p, " + '{:8.1f}'.format(dd.gen_dialysate.d9_pressure) + \ +# ", D18.p, " + '{:8.1f}'.format(dd.gen_dialysate.d18_pressure) + \ +# ", D51.p, " + '{:8.1f}'.format(dd.gen_dialysate.d51_pressure ) +# ", Del, " + '{:2d}'.format(dd.gen_dialysate.dialysate_good_to_deliver) + Ultrafiltration = ", ExSt, " + '{:2d}'.format(dd.ultrafiltration.uf_exec_state) + \ + ", isUFreq, " + '{:2d}'.format(dd.ultrafiltration.is_uf_requested) + \ + ", ufRate, " + '{:8.1f}'.format(dd.ultrafiltration.uf_rate) + \ + ", CompUF, " + '{:8.1f}'.format(dd.ultrafiltration.compensated_uf_rate) + OperationMode = ", OpMode, " + '{:2d}'.format(dd.dd_operation_mode) + \ + ", SubMode, " + '{:2d}'.format(dd.dd_operation_sub_mode) + SpentChamberFill = ", ExSt, " + '{:2d}'.format(dd.spent_chamber_fill.execution_state) + \ + ", Swprd, " + '{:2d}'.format(dd.spent_chamber_fill.switching_period) + \ + ", Cnt, " + '{:2d}'.format(dd.spent_chamber_fill.total_spent_chamber_fill_counter) + #Drybicart = ", CrtFillExSt, " + '{:2d}'.format(dd.drybicart.dd_dry_bicart_fill_execution_state) + \ + # ", ChFillExSt, " + '{:2d}'.format(dd.drybicart.dd_bicarb_chamber_fill_execution_state) + \ + # ", FillCycCnt, " + '{:2d}'.format(dd.drybicart.dd_dry_bicart_fill_cycle_counter) + \ + # ", MaxfillCycleCount, " + '{:2d}'.format(dd.drybicart.dd_dry_bicart_max_fill_cycle_count) + \ + # ", bicartfillreq, " + '{:2d}'.format(dd.drybicart.dd_dry_bicart_fill_request) + \ + # ", bicarbfillreq, " + '{:2d}'.format(dd.drybicart.dd_bicarb_chamber_fill_request) + \ + # ", lastfilltime, " + '{:2d}'.format(dd.drybicart.dd_dry_bicart_last_fill_time) + \ + # ", currFilltime, " + '{:2d}'.format(dd.drybicart.dd_dry_bicart_current_fill_time) + + # log data + f.write(dialPump) + f.write(Levels) + f.write(pressure) + f.write(Temperature) + f.write(valves) + f.write(ConcPumps) + # f.write(Heaters) + f.write(BalChamber) + #f.write(GenDialysate) + f.write(Ultrafiltration) + f.write(OperationMode) + f.write(SpentChamberFill) + #f.write(Drybicart) + f.write("\n") + + # print to console + #print("Dialysate Pumps: "+dialPump) + #print(" Levels: "+Levels) + #print(" Piston Pumps: " + PistonPumps) + # print(" Heaters: " +Heaters) + #print(" Pressure: "+pressure) + #print(" SpentChamberFill: " + SpentChamberFill) + # print(" Temperature: "+Temperature) + #print(" ConcenPumps: "+ConcPumps) + #print(" DD Valves: "+valves) + print("Balancing Chamb: "+BalChamber) + #print(" Gen Dialysate: "+GenDialysate) + #print(" UF: "+Ultrafiltration) + #print(" OP: "+OperationMode) + #print("drybicart Chamb: "+Drybicart)