Index: dialin/common/alarm_defs.py =================================================================== diff -u -re485785be0282bc516d09c86f7e90b07c7ca03f1 -rec4566a8f8f9731331270672df92100023cb733a --- dialin/common/alarm_defs.py (.../alarm_defs.py) (revision e485785be0282bc516d09c86f7e90b07c7ca03f1) +++ dialin/common/alarm_defs.py (.../alarm_defs.py) (revision ec4566a8f8f9731331270672df92100023cb733a) @@ -142,7 +142,22 @@ ALARM_ID_DG_COMMAND_INVALID_PARAMETER_FAULT = 121 # HD requests DG command with invalid parameter fault ALARM_ID_HD_LOAD_CELL_ACCELERATION_RES_1_ALARM = 122 # HD sees primary load cell for reservoir 1 change too much too fast ALARM_ID_HD_LOAD_CELL_ACCELERATION_RES_2_ALARM = 123 # HD sees primary load cell for reservoir 1 change too much too fast - NUM_OF_ALARM_IDS = 124 # Total number of alarms + ALARM_ID_TREATMENT_STOPPED_AFTER_RINSEBACK = 124 # HD in treatment stopped sub-mode after rinseback completed (no escalation) + ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM = 125 # HD in treatment rinseback operation timeout + ALARM_ID_TREATMENT_RECIRC_TIMEOUT_ALARM = 126 # HD in treatment re-circ sub-mode for too long + ALARM_ID_CARTRIDGE_DOOR_OPENED = 127 # HD cartridge door opened alarm + ALARM_ID_PRE_TREATMENT_SELF_TEST_FAILURE = 128 # HD pre-treatment mode self-tests failure + ALARM_ID_INSTALL_NEW_CARTRIDGE = 129 # HD needs new cartridge to be installed + ALARM_ID_CARTRIDGE_INSTALLED_IMPROPERLY = 130 # HD cartridge installed improperly alarm + ALARM_ID_RINSEBACK_VOLUME_CHECK_FAILURE = 131 # HD rinseback volume check failure + ALARM_ID_NO_CARTRIDGE_LOADED = 132 # HD no cartridge loaded alarm + ALARM_ID_END_TREATMENT_TIMEOUT_ALARM = 133 # HD end treatment sub-mode timeout alarm + ALARM_ID_BLOOD_PRIME_VOLUME_CHECK_FAILURE = 134 # HD blood prime volume check failure + ALARM_ID_DIALYSATE_TEMPERATURE_TOO_HIGH = 135 # HD dialysate temperature too high alarm + ALARM_ID_DIALYSATE_TEMPERATRUE_TOO_LOW = 136 # HD dialysate temperature too low alarm + ALARM_ID_PRE_TREATMENT_DRY_SELF_TEST_FAILURE = 137 # HD pre-treatment mode dry self-tests failure + ALARM_ID_PRE_TREATMENT_WET_SELF_TEST_FAILURE = 138 # HD pre-treatment mode wet self-tests failure + NUM_OF_ALARM_IDS = 139 # Total number of alarms @unique class AlarmPriorities(DialinEnum): Index: dialin/common/msg_defs.py =================================================================== diff -u -re485785be0282bc516d09c86f7e90b07c7ca03f1 -rec4566a8f8f9731331270672df92100023cb733a --- dialin/common/msg_defs.py (.../msg_defs.py) (revision e485785be0282bc516d09c86f7e90b07c7ca03f1) +++ dialin/common/msg_defs.py (.../msg_defs.py) (revision ec4566a8f8f9731331270672df92100023cb733a) @@ -106,6 +106,10 @@ MSG_ID_UI_RECIRC_CMD = 0x54 # UI re-circ request MSG_ID_HD_RECIRC_CMD_RESPONSE = 0x55 # HD re-circ request response MSG_ID_HD_RINSEBACK_PROGRESS = 0x56 # HD broadcast of rinseback progress + MSG_ID_UI_TX_END_CMD = 0x57 # UI end treatment sub-mode user request + MSG_ID_HD_TX_END_CMD_RESPONSE = 0x58 # HD end treatment sub-mode user request response + MSG_ID_HD_BLOOD_PRIME_PROGRESS = 0x59 # HD broadcast of blood prime progress + MSG_ID_HD_RECIRC_PROGRESS = 0x5A # HD broadcast of treatment re-circulate progress MSG_ID_CAN_ERROR_COUNT = 0x999 # test code in support of EMC testing Index: dialin/hd/treatment.py =================================================================== diff -u -r363867c876456821000c189f86fbb55b4d583c50 -rec4566a8f8f9731331270672df92100023cb733a --- dialin/hd/treatment.py (.../treatment.py) (revision 363867c876456821000c189f86fbb55b4d583c50) +++ dialin/hd/treatment.py (.../treatment.py) (revision ec4566a8f8f9731331270672df92100023cb733a) @@ -96,16 +96,46 @@ msg_id = MsgIds.MSG_ID_SALINE_BOLUS_DATA.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_saline_bolus_data_sync) + msg_id = MsgIds.MSG_ID_HD_RINSEBACK_PROGRESS.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_rinseback_data_sync) + msg_id = MsgIds.MSG_ID_HD_BLOOD_PRIME_PROGRESS.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_blood_prime_data_sync) + msg_id = MsgIds.MSG_ID_HD_RECIRC_PROGRESS.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_recirculate_data_sync) + # treatment duration data self.treatment_time_prescribed = 0 self.treatment_time_elapsed = 0 self.treatment_time_remaining = 0 + # treatment state data self.treatment_state = 0 self.treatment_uf_state = 0 self.saline_bolus_state = 0 + self.heparin_state = 0 + self.rinseback_state = 0 + self.treatment_recirculate_state = 0 + self.blood_prime_state = 0 + self.treatment_end_state = 0 + self.treatment_stop_state = 0 + # saline bolus status self.saline_bolus_max_vol = 0 self.saline_bolus_cum_vol = 0.0 self.saline_bolus_bol_vol = 0.0 + # blood prime status + self.blood_prime_tgt_vol = 0.0 + self.blood_prime_cum_vol = 0.0 + # rinseback status + self.rinseback_tgt_vol = 0.0 + self.rinseback_cum_vol = 0.0 + self.rinseback_cur_rate = 0 + self.rinseback_timeout_secs = 0 + self.rinseback_countdown_secs = 0 + # re-circulation status + self.recirc_timeout_secs = 0 + self.recirc_countdown_secs = 0 def get_treatment_time_prescribed(self): """ @@ -149,12 +179,36 @@ def get_saline_bolus_state(self): """ - Returns whether saline bolus state + Gets the current saline bolus state @return: The saline bolus state """ return self.saline_bolus_state + def get_heparin_state(self): + """ + Gets the current Heparin state + + @return: The Heparin state + """ + return self.heparin_state + + def get_rinseback_state(self): + """ + Gets the current rinseback state + + @return: The rinseback state + """ + return self.rinseback_state + + def get_treatment_recirc_state(self): + """ + Gets the current treatment recirculate state + + @return: The treatment recirculate state + """ + return self.treatment_recirculate_state + def get_saline_bolus_max_volume(self): """ Returns maximum volume (in mL) saline that can be delivered to a patient @@ -208,7 +262,13 @@ @_publish([ "treatment_state", "treatment_uf_state", - "saline_bolus_in_progress" + "saline_bolus_in_progress", + "heparin_state", + "rinseback_state", + "treatment_recirculate_state", + "blood_prime_state", + "treatment_end_state", + "treatment_stop_state" ]) def _handler_treatment_state_sync(self, message): """ @@ -225,10 +285,28 @@ message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) bol = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + hep = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) + rin = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) + rec = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) + bpr = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) + txe = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) + txs = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9])) self.treatment_state = tst[0] self.treatment_uf_state = ufs[0] self.saline_bolus_state = bol[0] + self.heparin_state = hep[0] + self.rinseback_state = rin[0] + self.treatment_recirculate_state = rec[0] + self.blood_prime_state = bpr[0] + self.treatment_end_state = txe[0] + self.treatment_stop_state = txs[0] @_publish([ "saline_bolus_max_vol", @@ -255,6 +333,81 @@ self.saline_bolus_cum_vol = cum[0] self.saline_bolus_bol_vol = bol[0] + @_publish([ + "rinseback_tgt_vol", + "rinseback_cum_vol", + "rinseback_cur_rate", + "rinseback_timeout_secs", + "rinseback_countdown_secs" + ]) + def _handler_rinseback_data_sync(self, message): + """ + Handles published rinseback data (progress) messages. Rinseback data are captured + for reference. + + @param message: published rinseback data message + @return: none + """ + + tgt = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + cum = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + rat = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + tmo = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) + cdn = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) + + self.rinseback_tgt_vol = tgt[0] + self.rinseback_cum_vol = cum[0] + self.rinseback_cur_rate = rat[0] + self.rinseback_timeout_secs = tmo[0] + self.rinseback_countdown_secs = cdn[0] + + @_publish([ + "blood_prime_tgt_vol", + "blood_prime_cum_vol" + ]) + def _handler_blood_prime_data_sync(self, message): + """ + Handles published blood prime data (progress) messages. Blood prime data are captured + for reference. + + @param message: published blood prime data message + @return: none + """ + + tgt = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + cum = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + + self.blood_prime_tgt_vol = tgt[0] + self.blood_prime_cum_vol = cum[0] + + @_publish([ + "recirc_timeout_secs", + "recirc_countdown_secs" + ]) + def _handler_recirculate_data_sync(self, message): + """ + Handles published recirculate data (progress) messages. Recirculate data are captured + for reference. + + @param message: published recirculate data message + @return: none + """ + + tmo = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) + cdn = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) + + self.recirc_timeout_secs = tmo[0] + self.recirc_countdown_secs = cdn[0] + def cmd_set_treatment_param_blood_flow_rate(self, flow): """ Constructs and sends the set blood flow rate treatment parameter command. Index: dialin/hd/ui_proxy.py =================================================================== diff -u -r18c04cbcd10619250868cec39470a2b29c86121b -rec4566a8f8f9731331270672df92100023cb733a --- dialin/hd/ui_proxy.py (.../ui_proxy.py) (revision 18c04cbcd10619250868cec39470a2b29c86121b) +++ dialin/hd/ui_proxy.py (.../ui_proxy.py) (revision ec4566a8f8f9731331270672df92100023cb733a) @@ -108,6 +108,45 @@ def has_value(cls, value): return value in cls._value2member_map_ + class RinsebackUserActions(enum.Enum): + + REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_START = 0, + REQUESTED_USER_ACTION_RINSEBACK_INCREASE_RATE = 1, + REQUESTED_USER_ACTION_RINSEBACK_DECREASE_RATE = 2, + REQUESTED_USER_ACTION_RINSEBACK_PAUSE = 3, + REQUESTED_USER_ACTION_RINSEBACK_RESUME = 4, + REQUESTED_USER_ACTION_RINSEBACK_END = 5, + REQUESTED_USER_ACTION_RINSEBACK_ADDITIONAL = 6, + REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_DISCONNECT = 7, + REQUESTED_USER_ACTION_RINSEBACK_END_TREATMENT = 8, + REQUESTED_USER_ACTION_RINSEBACK_BACK_TO_TREATMENT = 9, + NUM_OF_REQUESTED_RINSEBACK_USER_ACTIONS = 10 + + @classmethod + def has_value(cls, value): + return value in cls._value2member_map_ + + class RecircUserActions(enum.Enum): + + REQUESTED_USER_ACTION_TX_RECIRC_RECONNECT = 0, + REQUESTED_USER_ACTION_TX_RECIRC_CONFIRM_RECONNECT = 1, + REQUESTED_USER_ACTION_TX_RECIRC_RESUME_RC = 2, + REQUESTED_USER_ACTION_TX_RECIRC_END_TREATMENT = 3, + NUM_OF_REQUESTED_TX_RECIRC_USER_ACTIONS = 4 + + @classmethod + def has_value(cls, value): + return value in cls._value2member_map_ + + class TreatmentEndUserActions(enum.Enum): + + REQUESTED_USER_ACTION_TX_END_RINSEBACK_START = 0, + NUM_OF_REQUESTED_TX_END_USER_ACTIONS = 1 + + @classmethod + def has_value(cls, value): + return value in cls._value2member_map_ + def __init__(self, can_interface, logger: Logger): """ @@ -145,6 +184,7 @@ self._handler_saline_bolus_response) self.can_interface.register_receiving_publication_function(DenaliChannels.ui_to_hd_ch_id, MsgIds.MSG_ID_UI_SET_UF_VOLUME_PARAMETER.value, self._handler_uf_volume_setting_response) + # TODO - handle response to rinseback, recirc, and end treatment command messages. # initialize variables that will be populated by HD version response self.hd_version = None @@ -1463,6 +1503,87 @@ self.can_interface.send(message, 0) + def cmd_ui_rinseback_user_action(self, action=RinsebackUserActions.REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_START.value): + """ + Constructs and sends a UI rinseback user action message + Constraints: + HD must be in treatment mode, rinseback sub-mode. + + @param action: (enumerated int) User action + CONFIRM_START = 0 (start rinseback) + INCREASE_RATE = 1 (increase rinseback flow rate by 25 mL/min) + DECREASE_RATE = 2 (decrease rinseback flow rate by 25 mL/min) + PAUSE = 3 (pause rinseback) + RESUME = 4 (resume paused rinseback) + RINSEBACK_END = 5 (end rinseback now) + ADDITIONAL = 6 (10 mL more) + CONFIRM_DISCONNECT = 7 (go to re-circulate sub-mode) + END_TREATMENT = 8 (go to post-treatment mode) + BACK_TO_TREATMENT = 9 (ready to resume treatment) + + @return: none + """ + + cmd = integer_to_bytearray(action) + + payload = cmd + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_UI_RINSEBACK_CMD.value, + payload=payload) + + self.logger.debug("Sending rinseback command " + str(action) + " to HD.") + + self.can_interface.send(message, 0) + + def cmd_ui_recirculate_user_action(self, action=RecircUserActions.REQUESTED_USER_ACTION_TX_RECIRC_RECONNECT.value): + """ + Constructs and sends a UI recirculate user action message + Constraints: + HD must be in treatment mode, recirculate sub-mode. + + @param action: (enumerated int) User action + RECONNECT = 0 (ready to reconnect patient) + CONFIRM_RECONNECT = 1 (confirm patient is reconnected - ready to resume treatment) + RESUME_RC = 2 (resume recirculation - not ready to reconnect patient) + END_TREATMENT = 3 (go to post-treatment mode) + + @return: none + """ + + cmd = integer_to_bytearray(action) + + payload = cmd + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_UI_RECIRC_CMD.value, + payload=payload) + + self.logger.debug("Sending recirculate command " + str(action) + " to HD.") + + self.can_interface.send(message, 0) + + def cmd_ui_treatment_end_user_action(self, action=TreatmentEndUserActions.REQUESTED_USER_ACTION_TX_END_RINSEBACK_START.value): + """ + Constructs and sends a UI treatment end user action message + Constraints: + HD must be in treatment mode, treatment end sub-mode. + + @param action: (enumerated int) User action + RINSEBACK_START = 0 (start final rinseback) + + @return: none + """ + + cmd = integer_to_bytearray(action) + + payload = cmd + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_UI_TX_END_CMD.value, + payload=payload) + + self.logger.debug("Sending treatment end command " + str(action) + " to HD.") + + self.can_interface.send(message, 0) + def cmd_ui_user_alarm_response(self, option=AlarmUserOptions.ALARM_USER_ACTION_ACK.value): """ Constructs and sends a ui alarm response message.