Index: dialin/common/hd_defs.py =================================================================== diff -u -r50c8c54ce46c7236d650fb7a2576b0b2b5a8dd5a -r96244d1d41c71d254f61e18d1d170dfaeaa695a2 --- dialin/common/hd_defs.py (.../hd_defs.py) (revision 50c8c54ce46c7236d650fb7a2576b0b2b5a8dd5a) +++ dialin/common/hd_defs.py (.../hd_defs.py) (revision 96244d1d41c71d254f61e18d1d170dfaeaa695a2) @@ -292,7 +292,8 @@ DIALYSIS_START_STATE = 0 # Start state of dialysis sub-mode state machine DIALYSIS_UF_STATE = 1 # Ultrafiltration state of the dialysis sub-mode state machine DIALYSIS_SALINE_BOLUS_STATE = 2 # Saline bolus state of the dialysis sub-mode state machine - NUM_OF_DIALYSIS_STATES = 3 # Number of dialysis sub-mode states + DIALYSIS_BLOOD_LEAK_ZEROING_STATE = 3 # Blood leak zeroing state + NUM_OF_DIALYSIS_STATES = 4 # Number of dialysis sub-mode states @unique class TreatmentStopStates(DialinEnum): @@ -366,7 +367,9 @@ HD_EVENT_DRY_SELF_TEST_PRESSURE_DECAY_WAIT_PERIOD = 28 # HD dry self test pressure decay wait period HD_EVENT_INSTIT_RECORD_UPDATE = 29 # HD new institutional record has been updated. HD_EVENT_PARTIAL_OCCLUSION_BASELINE = 30 # HD event partial occlusion baseline event - NUM_OF_EVENT_IDS = 31 # Total number of HD events + HD_EVENT_BLOOD_LEAK_ZEROING_REQUEST = 31 # HD event blood leak zeroing request + HD_EVENT_BLOOD_LEAK_ZEROING_REQUIRED = 32 # HD event blood leak zeroing required + NUM_OF_EVENT_IDS = 33 # Total number of HD events @unique class HDEventDataType(DialinEnum): @@ -431,3 +434,13 @@ PW_COMMAND_OPEN = 0 PW_TIMEOUT_CLOSE = 1 PW_REJECT_SHOW = 2 + +@unique +class BloodLeakZeroingStates(DialinEnum): + BLD_ZEROING_IDLE_STATE = 0 # Blood leak zeroing idle state. + BLD_ZEROING_FLUSH_RSRVR_2_DPI_STATE = 1 # Blood leak zeroing flush reservoir to DPi state. + BLD_ZEROING_FLUSH_DPI_2_BLD_STATE = 2 # Blood leak zeroing flush DPi to BLD state. + BLD_ZEROING_ZERO_STATE = 3 # Blood leak zeroing zero state. + BLD_ZEROING_VERIFY_ZEROING_STATE = 4 # Blood leak zeroing verify zeroing state. + BLD_ZEROING_COMPLETE_STATE = 5 # Blood leak zeroing complete state. + NUM_OF_BLD_ZEROING_STATES = 6 # Number of blood leak zeroing states. Index: dialin/common/msg_ids.py =================================================================== diff -u -rcdc433c39580475307d2cfa0b3ccef7873c630f4 -r96244d1d41c71d254f61e18d1d170dfaeaa695a2 --- dialin/common/msg_ids.py (.../msg_ids.py) (revision cdc433c39580475307d2cfa0b3ccef7873c630f4) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision 96244d1d41c71d254f61e18d1d170dfaeaa695a2) @@ -394,6 +394,9 @@ MSG_ID_HD_SEND_INSTITUTIONAL_RECORD = 0x80A0 MSG_ID_HD_PARTIAL_OCCLUSION_BLOOD_PUMP_OVERRIDE = 0x80A1 MSG_ID_HD_PARTIAL_OCCL_BLOOD_PUMP_BASELINE_OVERRIDE = 0x80A2 + MSG_ID_HD_BLOOD_LEAK_EMB_MODE_INFO_VALUES_OVERRIDE = 0x80A3 + MSG_ID_HD_BLOOD_LEAK_INTENSITY_MVG_AVERAGE_OVERRIDE = 0x80A4 + MSG_ID_HD_BLOOD_LEAK_ZEROING_INTERVAL_IN_MINS_OVERRIDE = 0x80A5 MSG_ID_DG_TESTER_LOGIN_REQUEST = 0xA000 MSG_ID_DG_ALARM_STATE_OVERRIDE = 0xA001 Index: dialin/hd/blood_leak.py =================================================================== diff -u -r32e628abcbbd3fd70866505d9f2836a6f732ef06 -r96244d1d41c71d254f61e18d1d170dfaeaa695a2 --- dialin/hd/blood_leak.py (.../blood_leak.py) (revision 32e628abcbbd3fd70866505d9f2836a6f732ef06) +++ dialin/hd/blood_leak.py (.../blood_leak.py) (revision 96244d1d41c71d254f61e18d1d170dfaeaa695a2) @@ -22,7 +22,7 @@ from ..protocols.CAN import DenaliMessage, DenaliChannels from ..utils.base import AbstractSubSystem, publish, DialinEnum from ..utils.conversions import integer_to_bytearray, bytearray_to_byte, bytearray_to_integer, \ - unsigned_short_to_bytearray, byte_to_bytearray + unsigned_short_to_bytearray, byte_to_bytearray, float_to_bytearray @unique @@ -49,9 +49,10 @@ BLOOD_LEAK_CHECK_SET_POINT_STATE = 1 BLOOD_LEAK_INIT_STATE = 2 BLOOD_LEAK_CHECK_ZERO_AND_SELF_TEST_STATE = 3 - BLOOD_LEAK_NORMAL_STATE = 4 - BLOOD_LEAK_RECOVER_BLOOD_DETECT_STATE = 5 - NUM_OF_BLOOD_LEAK_STATES = 6 + BLOOD_LEAK_VERIFY_INTENSITY_AFTER_ZEROING_STATE = 4 + BLOOD_LEAK_NORMAL_STATE = 5 + BLOOD_LEAK_RECOVER_BLOOD_DETECT_STATE = 6 + NUM_OF_BLOOD_LEAK_STATES = 7 class HDBloodLeak(AbstractSubSystem): @@ -92,6 +93,12 @@ self.blood_leak_emb_mode_cmds = dict() self.blood_leak_error_persistent_ctr = 0 self.blood_leak_serial_comm_state = 0 + self.blood_leak_intensity = 0 + self.blood_leak_blood_detect = 0 + self.blood_leak_intensity_moving_average = 0.0 + self.blood_leak_in_range_drift_status = 0 + self.blood_leak_upper_range_drift_status = 0 + self.blood_leak_time_elapsed_since_last_drift_zero_ms = 0 for cmd in EmbModeCommands.__members__: # Initialize all the embedded mode commands @@ -129,7 +136,11 @@ self.logger.debug("Invalid command!") @publish(['hd_blood_leak_status_timestamp', 'blood_leak_status', 'blood_leak_state', - 'blood_leak_error_persistent_ctr', 'blood_leak_serial_comm_state']) + 'blood_leak_error_persistent_ctr', 'blood_leak_serial_comm_state', 'blood_leak_intensity', + 'blood_leak_blood_detect', 'blood_leak_intensity_moving_average', 'blood_leak_time_since_last_zero_ms', + 'blood_leak_time_since_last_upper_zero_ms', + 'blood_leak_in_range_drift_status', + 'blood_leak_upper_range_drift_status']) def _handler_blood_leak_sync(self, message, timestamp=0.0): """ Handles published blood leak status messages. Blood leak status is captured @@ -146,6 +157,18 @@ message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] self.blood_leak_serial_comm_state = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] + self.blood_leak_intensity = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] + self.blood_leak_blood_detect = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] + self.blood_leak_intensity_moving_average = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] + self.blood_leak_time_elapsed_since_last_drift_zero_ms = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] + self.blood_leak_in_range_drift_status = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] + self.blood_leak_upper_range_drift_status = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10]))[0] self.hd_blood_leak_status_timestamp = timestamp def cmd_blood_leak_detector_override(self, detected: int, reset=NO_RESET): @@ -327,3 +350,107 @@ self.blood_leak_emb_mode_cmds[EmbModeCommands(cmd).name] = blood_leak_emb_mode_cmd_response self.hd_blood_leak_emb_mode_response_timestamp = timestamp + + def cmd_blood_leak_emb_mode_info_cmds_override(self, cmd: int, value_to_override: int, reset=NO_RESET): + """ + Constructs and sends the blood leak intensity override command + Constraints: + Must be logged into HD. + Can only override Intensity (I) and Blood Detect Level (V) + + @param cmd: unsigned int - the command that its info value is overridden (only I and V) + @param value_to_override: unsigned int - value to override the intensity to + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + rst = integer_to_bytearray(reset) + value = integer_to_bytearray(value_to_override) + index = integer_to_bytearray(EmbModeCommands(cmd).value) + payload = rst + value + index + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_BLOOD_LEAK_EMB_MODE_INFO_VALUES_OVERRIDE.value, + payload=payload) + + if reset == NO_RESET: + self.logger.debug("Override blood leak {} to {}".format(EmbModeCommands(cmd).name, value_to_override)) + else: + self.logger.debug("Reset override blood leak {} ".format(EmbModeCommands(cmd).name)) + + # 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_blood_leak_intensity_moving_average_override(self, value: float, reset=NO_RESET): + """ + Constructs and sends the blood leak intensity moving average + Constraints: + Must be logged into HD. + + @param value: float - blood leak intensity moving average + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + rst = integer_to_bytearray(reset) + avg = float_to_bytearray(value) + payload = rst + avg + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_BLOOD_LEAK_INTENSITY_MVG_AVERAGE_OVERRIDE.value, + payload=payload) + + self.logger.debug("Override blood leak intensity moving average to {:5.3f}".format(value)) + + # 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_blood_leak_zeroing_interval_in_minutes(self, upper_interval: int, value_mins: int, reset=NO_RESET): + """ + Constructs and sends the blood leak zeroing interval in minutes + Constraints: + Must be logged into HD. + + @param upper_interval: unsigned int - to override upper range or drift intervals (0 = drift interval, 1 = upper interval) + @param value_mins: unsigned int - blood leak zeroing interval in minutes + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + rst = integer_to_bytearray(reset) + mins = integer_to_bytearray(value_mins) + upper = integer_to_bytearray(upper_interval) + payload = rst + mins + upper + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_BLOOD_LEAK_ZEROING_INTERVAL_IN_MINS_OVERRIDE.value, + payload=payload) + text = 'intensity drift' + if upper_interval == 1: + text = 'upper intensity' + + self.logger.debug("Override blood leak zeroing {} interval to {} minute(s)".format(text,mins)) + + # 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 Index: dialin/hd/treatment.py =================================================================== diff -u -r32e628abcbbd3fd70866505d9f2836a6f732ef06 -r96244d1d41c71d254f61e18d1d170dfaeaa695a2 --- dialin/hd/treatment.py (.../treatment.py) (revision 32e628abcbbd3fd70866505d9f2836a6f732ef06) +++ dialin/hd/treatment.py (.../treatment.py) (revision 96244d1d41c71d254f61e18d1d170dfaeaa695a2) @@ -147,10 +147,14 @@ self.treatment_stop_state = 0 self.dialysis_state = 0 self.dialyzer_reprime_state = 0 + self.blood_leak_zeroing_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 + self.dialysate_inlet_pump_2_bld_flush_vol_ml = 0.0 + self.reservoir_2_dialysate_inlet_pump_flush_vol_ml = 0.0 + self.has_blood_leak_after_rsrvr_switch_been_rqstd = 0 # blood prime status self.blood_prime_tgt_vol = 0.0 self.blood_prime_cum_vol = 0.0 @@ -208,10 +212,12 @@ self.treatment_end_state = 0 self.treatment_stop_state = 0 self.dialysis_state = 0 + self.blood_leak_zeroing_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 + self.dialysate_inlet_pump_2_bld_flush_vol_ml = 0.0 # blood prime status self.blood_prime_tgt_vol = 0.0 self.blood_prime_cum_vol = 0.0 @@ -341,6 +347,14 @@ """ return self.dialyzer_reprime_state + def get_blood_leak_zeroing_state(self) -> int: + """ + Gets the current blood leak zeroing state + + @return: The current blood leak zeroing state + """ + return self.blood_leak_zeroing_state + def get_saline_bolus_max_volume(self) -> int: """ Returns maximum volume (in mL) saline that can be delivered to a patient @@ -365,6 +379,22 @@ """ return self.saline_bolus_bol_vol + def get_dialysate_inlet_pump_2_bld_flush_vol_ml(self) -> float: + """ + Returns dialysate inlet pump to blood leak detector flush volume (in mL) + + @return: Dialysate inlet pump to blood leak detector flush volume in milliliters + """ + return self.dialysate_inlet_pump_2_bld_flush_vol_ml + + def get_reservoir_2_dialysate_inlet_pump_flush_vol_ml(self) -> float: + """ + Returns reservoir to dialysate inlet flush volume (in mL) + + @return: Reservoir to dialysate inlet pump flush volume in milliliters + """ + return self.reservoir_2_dialysate_inlet_pump_flush_vol_ml + def get_blood_prime_target_volume(self) -> float: """ Returns blood prime target volume (in mL) @@ -523,7 +553,8 @@ "treatment_end_state", "treatment_stop_state", "dialysis_state", - "dialyzer_reprime_state" + "dialyzer_reprime_state", + "blood_leak_zeroing_state" ]) def _handler_treatment_state_sync(self, message, timestamp=0.0): """ @@ -554,6 +585,8 @@ message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9])) dia = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10])) + bld = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_11:MsgFieldPositions.END_POS_FIELD_11])) self.treatment_state = tst[0] self.treatment_uf_state = ufs[0] @@ -565,13 +598,17 @@ self.treatment_end_state = txe[0] self.treatment_stop_state = txs[0] self.dialysis_state = dia[0] + self.blood_leak_zeroing_state = bld[0] self.hd_treatment_state_timestamp = timestamp @publish([ "hd_saline_bolus_timestamp", "saline_bolus_max_vol", "saline_bolus_cum_vol", - "saline_bolus_bol_vol" + "saline_bolus_bol_vol", + "dialysate_inlet_pump_2_bld_flush_vol_ml", + "reservoir_2_dialysate_inlet_pump_flush_vol_ml", + "has_blood_leak_after_rsrvr_switch_been_rqstd" ]) def _handler_saline_bolus_data_sync(self, message, timestamp=0.0): """ @@ -588,10 +625,19 @@ message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) bol = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + vol = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) + rsvr_dpi_vol = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) + rsrvr_switch = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) self.saline_bolus_max_vol = mxm[0] self.saline_bolus_cum_vol = cum[0] self.saline_bolus_bol_vol = bol[0] + self.dialysate_inlet_pump_2_bld_flush_vol_ml = vol[0] + self.reservoir_2_dialysate_inlet_pump_flush_vol_ml = rsvr_dpi_vol[0] + self.has_blood_leak_after_rsrvr_switch_been_rqstd = rsrvr_switch[0] self.hd_saline_bolus_timestamp = timestamp @publish([