Index: dialin/common/alarm_defs.py =================================================================== diff -u -r8ff49bb248b9b98b18dde057d836b5c2d308922c -r4d916d8daba73702a58aee58041a9c3cc089a48e --- dialin/common/alarm_defs.py (.../alarm_defs.py) (revision 8ff49bb248b9b98b18dde057d836b5c2d308922c) +++ dialin/common/alarm_defs.py (.../alarm_defs.py) (revision 4d916d8daba73702a58aee58041a9c3cc089a48e) @@ -116,8 +116,8 @@ ALARM_ID____AVAILABLE_15 = 93 ALARM_ID____AVAILABLE_16 = 94 ALARM_ID____AVAILABLE_17 = 95 - ALARM_ID____AVAILABLE_18 = 96 - ALARM_ID____AVAILABLE_19 = 97 + ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE = 96 + ALARM_ID_FILL_CONDUCTIVITY_OUT_OF_RANGE = 97 ALARM_ID_HD_BATTERY_COMM_FAULT = 98 ALARM_ID_HD_SYRINGE_PUMP_STALL = 99 ALARM_ID_HD_NO_CART_SELF_TEST_TIMEOUT = 100 @@ -126,7 +126,7 @@ ALARM_ID_HD_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID = 103 ALARM_ID_AIR_TRAP_ILLEGAL_LEVELS = 104 ALARM_ID_DG_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID = 105 - ALARM_ID____AVAILABLE_20 = 106 + ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY = 106 ALARM_ID_DG_RESTARTED_FAULT = 107 ALARM_ID_HD_SYRINGE_PUMP_ADC_ERROR = 108 ALARM_ID_HD_SYRINGE_PUMP_VOLUME_ERROR = 109 @@ -167,18 +167,18 @@ ALARM_ID_DG_SW_CONFIG_RECORD_INVALID_CRC = 144 ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE = 145 ALARM_ID_DIALYSATE_CONDUCTIVITY_OUT_OF_RANGE = 146 - ALARM_ID_DIALYSATE_CONDUCTIVITY_FAULT = 147 + ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT = 147 ALARM_ID_INLET_WATER_HIGH_TEMPERATURE = 148 ALARM_ID_INLET_WATER_LOW_TEMPERATURE = 149 - ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY = 150 + ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_WARNING_RANGE = 150 ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY = 151 ALARM_ID_INLET_WATER_LOW_PRESSURE = 152 ALARM_ID_PRIME_COMPLETED_HIGH = 153 ALARM_ID_NVDATA_EEPROM_OPS_FAILURE = 154 ALARM_ID_HD_SW_CONFIG_RECORD_INVALID_CRC = 155 ALARM_ID_NVDATA_HW_USAGE_DATA_CRC_ERROR = 156 ALARM_ID____AVAILABLE_23 = 157 - ALARM_ID____AVAILABLE_24 = 158 + ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE = 158 ALARM_ID_DIAL_IN_PUMP_FLOW_VS_MOTOR_SPEED_CHECK = 159 ALARM_ID_BLOOD_PUMP_ROTOR_SPEED_TOO_HIGH = 160 ALARM_ID_BLOOD_FLOW_SIGNAL_STRENGTH_TOO_LOW = 161 Index: dialin/common/dg_defs.py =================================================================== diff -u -r8ff49bb248b9b98b18dde057d836b5c2d308922c -r4d916d8daba73702a58aee58041a9c3cc089a48e --- dialin/common/dg_defs.py (.../dg_defs.py) (revision 8ff49bb248b9b98b18dde057d836b5c2d308922c) +++ dialin/common/dg_defs.py (.../dg_defs.py) (revision 4d916d8daba73702a58aee58041a9c3cc089a48e) @@ -84,20 +84,32 @@ @unique class DGGenIdleModeStates(DialinEnum): DG_GEN_IDLE_MODE_STATE_START = 0 - DG_GEN_IDLE_MODE_STATE_FLUSH_LINES = 1 - DG_GEN_IDLE_MODE_STATE_FLUSH_WATER = 2 + DG_GEN_IDLE_MODE_STATE_FLUSH_WATER = 1 + DG_GEN_IDLE_MODE_STATE_HANDLE_BAD_FILL = 2 NUM_OF_DG_GEN_IDLE_MODE_STATES = 3 @unique +class DGGenIdleModeBadFillSubStates(DialinEnum): + DG_HANDLE_BAD_FILL_STATE_START = 0 + DG_HANDLE_BAD_FILL_STATE_FIRST_DRAIN = 1 + DG_HANDLE_BAD_FILL_STATE_FLUSH_FILL = 2 + DG_HANDLE_BAD_FILL_STATE_SECOND_DRAIN = 3 + DG_HANDLE_BAD_FILL_STATE_REFILL = 4 + DG_HANDLE_BAD_FILL_STATE_CLEAR_ALARM = 5 + NUM_OF_DG_HANDLE_BAD_FILL_STATES = 6 + +@unique class DGFillModeStates(DialinEnum): - DG_FILL_MODE_STATE_START = 0 # Start fill mode state - DG_FILL_MODE_STATE_CHECK_INLET_WATER = 1 # Check inlet water quality state - DG_FILL_MODE_STATE_BICARB_PUMP_CHECK = 2 # Run bicarb concentrate pump and check conductivity range - DG_FILL_MODE_STATE_ACID_PUMP_CHECK = 3 # Run acid concentrate pump and check conductivity range - DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION = 4 # Dialysate production state - DG_FILL_MODE_STATE_DELIVER_DIALYSATE = 5 # Deliver dialysate state - DG_FILL_MODE_STATE_PAUSED = 6 # Dialysate generation pause state - NUM_OF_DG_FILL_MODE_STATES = 7 # Number of fill mode states + DG_FILL_MODE_STATE_START = 0 # Start fill mode state + DG_FILL_MODE_STATE_TEST_INLET_WATER = 1 # Test inlet water quality state + DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES = 2 # Prime the acid and bicarb concentrate lines + DG_FILL_MODE_STATE_FLUSH_BUBBLES = 3 # Flush the bubbles in the lines state + DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY = 4 # Test the conductivity range of the bicarb concentrate state + DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY = 5 # Test the conductivity range of the acid concentrate state + DG_FILL_MODE_STATE_PRODUCE_DIALYSATE = 6 # Dialysate production state + DG_FILL_MODE_STATE_DELIVER_DIALYSATE = 7 # Dialysate deliver state + DG_FILL_MODE_STATE_PAUSED = 8 # Dialysate generation pause state + NUM_OF_DG_FILL_MODE_STATES = 9 # Number of fill mode states @unique Index: dialin/common/msg_ids.py =================================================================== diff -u -rf58be254a175a40a9226f74ba6cf8bdb4c1de27f -r4d916d8daba73702a58aee58041a9c3cc089a48e --- dialin/common/msg_ids.py (.../msg_ids.py) (revision f58be254a175a40a9226f74ba6cf8bdb4c1de27f) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision 4d916d8daba73702a58aee58041a9c3cc089a48e) @@ -193,6 +193,9 @@ MSG_ID_DG_CONCENTRATE_MIXING_RATIOS_DATA = 0xAA MSG_ID_DG_SCHEDULED_RUNS_DATA = 0xAB MSG_ID_DG_SCHEDULED_RUNS_INFO = 0xAC + MSG_ID_UI_INSTALLATION_CONFIRM_RESPONSE = 0xAD + MSG_ID_DG_FILL_MODE_DATA = 0xAE + MSG_ID_DG_BAD_FILL_SUB_STATE = 0xAF MSG_ID_CAN_ERROR_COUNT = 0x999 @@ -396,9 +399,11 @@ MSG_ID_DG_SET_SW_CONFIG_RECORD = 0xA04B MSG_ID_DG_SEND_SW_CONFIG_RECORD = 0xA04C MSG_ID_DG_FANS_DUTY_CYCLE_OVERRIDE = 0xA04D - MSG_ID_DG_SET_FANS_RPM_ALARM_START_TIME_OFFSET = 0xA04E - MSG_ID_DG_USED_ACID_VOLUME_ML_OVERRIDE = 0xA04F + MSG_ID_DG_USED_ACID_VOLUME_ML_OVERRIDE = 0xA04E + MSG_ID_DG_SET_FANS_RPM_ALARM_START_TIME_OFFSET = 0xA04F MSG_ID_DG_USED_BICARB_VOLUME_ML_OVERRIDE = 0xA050 + MSG_ID_FILL_MODE_DATA_PUBLISH_INTERVAL_OVERRIDE = 0xA051 + MSG_ID_BAD_FILL_SUBSTATES_PUBLISH_INTERVAL_OVERRIDE = 0xA052 MSG_ID_HD_DEBUG_EVENT = 0xFFF1 MSG_ID_DG_DEBUG_EVENT = 0xFFF2 Index: dialin/dg/dialysate_fill.py =================================================================== diff -u -r0251ff11e97b1f71ddf669272a343639db9373c6 -r4d916d8daba73702a58aee58041a9c3cc089a48e --- dialin/dg/dialysate_fill.py (.../dialysate_fill.py) (revision 0251ff11e97b1f71ddf669272a343639db9373c6) +++ dialin/dg/dialysate_fill.py (.../dialysate_fill.py) (revision 4d916d8daba73702a58aee58041a9c3cc089a48e) @@ -46,49 +46,55 @@ self.can_interface = can_interface self.logger = logger + # fill mode data broadcast on CAN bus + self.avg_acid = 0.0 + self.avg_bicarb = 0.0 + self.first_fill = False + self.pctDiffConduct = 0.0 + self.used_acid = 0.0 + self.used_bicarb = 0.0 + if self.can_interface is not None: channel_id = DenaliChannels.dg_sync_broadcast_ch_id - msg_id = MsgIds.MSG_ID_DG_CONCENTRATE_PUMP_DATA.value + msg_id = MsgIds.MSG_ID_DG_FILL_MODE_DATA.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, - self._handler_dialysate_fill_monitor_sync) + self._handler_fill_mode_monitor_sync) - self.concentrate_pump_cp1_current_set_speed = 0.0 - self.concentrate_pump_cp1_measured_speed = 0.0 - self.concentrate_pump_cp2_current_set_speed = 0.0 - self.concentrate_pump_cp2_measured_speed = 0.0 - - def get_dialysate_fill(self): + def get_fill_mode_data(self): """ Gets the current concentrate pump data value - @return: List containing concentrate pump data values: - [ concentrate_pump_cp1_current_set_speed, concentrate_pump_cp1_measured_speed, - concentrate_pump_cp2_current_set_speed, concentrate_pump_cp2_measured_speed ] + @return: List containing fill mode data values: + [ avg_acid, avg_bicarb, first_fill, + pctDiffConduct, used_acid, used_bicarb ] """ - return [self.concentrate_pump_cp1_current_set_speed, self.concentrate_pump_cp1_measured_speed, - self.concentrate_pump_cp2_current_set_speed, self.concentrate_pump_cp2_measured_speed] + return [self.avg_acid, self.avg_bicarb, self.first_fill, + self.pctDiffConduct, self.used_acid, self.used_bicarb ] - @publish(["concentrate_pump_cp1_current_set_speed", "concentrate_pump_cp1_measured_speed", - "concentrate_pump_cp2_current_set_speed", "concentrate_pump_cp2_measured_speed"]) - def _handler_dialysate_fill_sync(self, message): + @publish(["avg_acid", "avg_bicarb","first_fill", + "pctDiffConduct", "used_acid", "used_bicarb"]) + def _handler_fill_mode_monitor_sync(self, message): """ Handles published dialysate fill monitor' data messages. Dialysate fill data are captured for reference. @param message: published dialysate fill' data message @return: None """ - - self.concentrate_pump_cp1_current_set_speed = struct.unpack('f', bytearray( + self.avg_acid = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.concentrate_pump_cp1_measured_speed = struct.unpack('f', bytearray( + self.avg_bicarb = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.concentrate_pump_cp2_current_set_speed = struct.unpack('f', bytearray( + self.first_fill = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.concentrate_pump_cp2_measured_speed = struct.unpack('f', bytearray( + self.pctDiffConduct = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] + self.used_acid = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] + self.used_bicarb = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - def cmd_dialysate_fill_used_acid_volume_override(self, volume: float) -> int: + def cmd_used_acid_volume_override(self, volume: float) -> int: """ Constructs and sends the concentrate pump target speed override command @@ -119,7 +125,7 @@ self.logger.error("Timeout!!!!") return False - def cmd_dialysate_fill_used_bicarb_volume_override(self, volume: float) -> int: + def cmd_used_bicarb_volume_override(self, volume: float) -> int: """ Constructs and sends the concentrate pump target speed override command @@ -150,9 +156,9 @@ self.logger.error("Timeout!!!!") return False - def cmd_dialysate_fill_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + def cmd_fill_mode_data_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ - Constructs and sends the dialysate fill data broadcast interval override command + Constructs and sends the fill mode data broadcast interval override command Constraints: Must be logged into DG. Given interval must be non-zero and a multiple of the DG general task interval (50 ms). @@ -170,10 +176,10 @@ payload = reset_byte_array + ms_byte_array message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=MsgIds.MSG_ID_CONCENTRATE_PUMP_PUBLISH_INTERVAL_OVERRIDE.value, + message_id=MsgIds.MSG_ID_FILL_MODE_DATA_PUBLISH_INTERVAL_OVERRIDE.value, payload=payload) - self.logger.debug("override dialysate fill data broadcast interval") + self.logger.debug("override used concentrate data broadcast interval") # Send message received_message = self.can_interface.send(message) Index: dialin/dg/idle_badfill_substates.py =================================================================== diff -u --- dialin/dg/idle_badfill_substates.py (revision 0) +++ dialin/dg/idle_badfill_substates.py (revision 4d916d8daba73702a58aee58041a9c3cc089a48e) @@ -0,0 +1,108 @@ +########################################################################### +# +# Copyright (c) 2021-2022 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 idle_badfill_substates.py +# +# @author (last) Sean Nash +# @date (last) 11-Nov-2021 +# @author (original) Hung Nguyen +# @date (original) 24-Mar-2022 +# +############################################################################ +import struct +from logging import Logger + +from .constants import RESET, NO_RESET +from ..common.msg_defs import MsgIds, MsgFieldPositions +from ..protocols.CAN import DenaliMessage, DenaliChannels +from ..utils.base import AbstractSubSystem, publish +from ..utils.checks import check_broadcast_interval_override_ms +from ..utils.conversions import integer_to_bytearray, float_to_bytearray + + +class BadfillSubstates(AbstractSubSystem): + """ + Dialysate Generation (DG) interface for bad fill sub-states related commands. + """ + + def __init__(self, can_interface, logger: Logger): + """ + Initialize CAN interface and logger + @param can_interface: Denali CAN Messenger object + @param: logger: (Logger) object + """ + + super().__init__() + + self.can_interface = can_interface + self.logger = logger + # The current bad fill sub-state + self.sub_state = 0 + + if self.can_interface is not None: + channel_id = DenaliChannels.dg_sync_broadcast_ch_id + msg_id = MsgIds.MSG_ID_DG_BAD_FILL_SUB_STATE.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_bad_fill_sub_state_sync) + + def get_sub_state(self): + """ + Gets current bad fill sub-state + + @param: sub state: (int) state index + @return: The current bad fill sub-state + """ + return self.sub_state + + @publish(['sub_state']) + def _handler_bad_fill_sub_state_sync(self, message): + """ + Handles published bad fill sub-states message. + + @param message: published bad fill sub-states message + @return: none + """ + substates = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + + self.bad_fill_substates = substates[0] + + def cmd_bad_fill_substates_data_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the fill mode data broadcast interval override command + Constraints: + Must be logged into DG. + Given interval must be non-zero and a multiple of the DG 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 + + reset_byte_array = integer_to_bytearray(reset) + ms_byte_array = integer_to_bytearray(ms) + payload = reset_byte_array + ms_byte_array + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_BAD_FILL_STATES_PUBLISH_INTERVAL_OVERRIDE.value, + payload=payload) + + self.logger.debug("override bad fill sub-states data broadcast interval") + + # 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.error("Timeout!!!!") + return False