Index: dialin/common/hd_defs.py =================================================================== diff -u -r965b384855cf17d6b975f9989f13a26248e2d919 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/common/hd_defs.py (.../hd_defs.py) (revision 965b384855cf17d6b975f9989f13a26248e2d919) +++ dialin/common/hd_defs.py (.../hd_defs.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -1,16 +1,16 @@ -################################################################ +########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2021 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 alarm_defs.py +# @file hd_defs.py # -# @author (last) Peter Lucia -# @date (last) 30-Nov-2020 -# @author (original) Peter Lucia -# @date (original) 30-Nov-2020 +# @author (last) Quang Nguyen +# @date (last) 22-Jul-2021 +# @author (original) Peter Lucia +# @date (original) 04-Dec-2020 # ############################################################################ from enum import unique Index: dialin/common/msg_ids.py =================================================================== diff -u -rd1e3f50dceb4c11cff5af632b39c64b4407aad68 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/common/msg_ids.py (.../msg_ids.py) (revision d1e3f50dceb4c11cff5af632b39c64b4407aad68) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -5,12 +5,12 @@ # 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 msg_ids.py +# @file msg_ids.py # -# @author (last) Peter Lucia -# @date (last) 05-Aug-2021 -# @author (original) Peter Lucia -# @date (original) 05-Aug-2021 +# @author (last) Dara Navaei +# @date (last) 10-Nov-2021 +# @author (original) Peter Lucia +# @date (original) 06-Apr-2021 # ############################################################################ from enum import unique @@ -184,9 +184,13 @@ MSG_ID_DG_SWITCHES_DATA = 0XA1 MSG_ID_HD_SWITCHES_DATA = 0XA2 MSG_ID_HD_FANS_DATA = 0XA3 + MSG_ID_HD_EVENT = 0xA4 + MSG_ID_DG_EVENT = 0xA5 MSG_ID_DG_DIALYSATE_FLOW_METER_DATA = 0xA6 MSG_ID_DG_ALARM_INFO = 0xA7 + MSG_ID_CAN_ERROR_COUNT = 0X999 + MSG_ID_TESTER_LOGIN_REQUEST = 0X8000 MSG_ID_DIAL_OUT_FLOW_SET_PT_OVERRIDE = 0X8001 MSG_ID_OFF_BUTTON_STATE_OVERRIDE = 0X8002 @@ -306,6 +310,7 @@ MSG_ID_HD_POST_TREATMENT_DATA_PUBLISH_INTERVAL_OVERRIDE = 0x8075 MSG_ID_HD_BLOCK_MESSAGE_TRANSMISSION = 0x8076 MSG_ID_HD_SYRINGE_PUMP_FORCE_SENSOR_DAC_CALIBRATE = 0x8077 + MSG_ID_HD_STOP_RTC_CLOCK = 0x8078 MSG_ID_DG_TESTER_LOGIN_REQUEST = 0XA000 MSG_ID_DG_ALARM_STATE_OVERRIDE = 0XA001 @@ -316,7 +321,7 @@ MSG_ID_PRESSURE_SEND_INTERVAL_OVERRIDE = 0XA007 MSG_ID_RO_MEASURED_FLOW_OVERRIDE = 0XA009 MSG_ID_RO_PUMP_SEND_INTERVAL_OVERRIDE = 0XA00A - MSG_ID_DRAIN_PUMP_SET_RPM_OVERRIDE = 0XA00B + MSG_ID_DRAIN_PUMP_SET_RPM = 0XA00B MSG_ID_DRAIN_PUMP_SEND_INTERVAL_OVERRIDE = 0XA00C MSG_ID_LOAD_CELL_SEND_INTERVAL_OVERRIDE = 0XA00D MSG_ID_VALVE_STATE_OVERRIDE = 0XA00E @@ -333,7 +338,7 @@ MSG_ID_DG_ACCEL_SEND_INTERVAL_OVERRIDE = 0XA019 MSG_ID_DG_MONITORED_VOLTAGES_SEND_INTERVAL_OVERRIDE = 0XA01A MSG_ID_DG_MONITORED_VOLTAGES_OVERRIDE = 0XA01B - MSG_ID_DRAIN_PUMP_SET_DELTA_PRESSURE_OVERRIDE = 0XA01C + MSG_ID_DRAIN_PUMP_TARGET_OUTLET_PRESSURE = 0XA01C MSG_ID_DG_SWITCHES_STATUS_OVERRIDE = 0XA01D MSG_ID_DG_SWITCHES_PUBLISH_INTERVAL_OVERRIDE = 0XA01E MSG_ID_DG_OP_MODE_PUBLISH_INTERVAL_OVERRIDE = 0XA01F @@ -353,7 +358,7 @@ MSG_ID_DG_THERMISTORS_VALUE_OVERRIDE = 0XA02E MSG_ID_DG_RO_PUMP_DUTY_CYCLE_OVERRIDE = 0XA02F MSG_ID_DG_RO_FLOW_RATE_OVERRIDE = 0XA030 - MSG_ID_DG_RO_PUMP_TARGET_FLOW_OVERRIDE = 0XA031 + MSG_ID_DG_SET_RO_PUMP_TARGET_FLOW = 0XA031 MSG_ID_DG_RO_PUMP_TARGET_PRESSURE_OVERRIDE = 0XA032 MSG_ID_DG_SET_CALIBRATION_RECORD = 0XA033 MSG_ID_DG_GET_CALIBRATION_RECORD = 0XA034 @@ -372,11 +377,11 @@ MSG_ID_DG_FLUSH_PUBLISH_INTERVAL_OVERRIDE = 0XA041 MSG_ID_FILTER_FLUSH_TIME_PERIOD_OVERRIDE = 0XA042 MSG_ID_DG_FANS_RPM_OVERRIDE = 0XA043 - MSG_ID_DIALYSATE_FLOW_SEND_INTERVAL_OVERRIDE = 0xA044 + MSG_ID_DIALYSATE_FLOW_SEND_INTERVAL_OVERRIDE = 0XA044 MSG_ID_DG_STOP_RTC_CLOCK = 0XA045 MSG_ID_DG_DRAIN_PUMP_MEASURED_RPM_OVERRIDE = 0XA046 MSG_ID_DG_SUPER_CLEAR_ALARMS_CMD = 0XA047 - MSG_ID_DG_ALARM_INFO_SEND_INTERVAL_OVERRIDE = 0xA048 + MSG_ID_DG_ALARM_INFO_SEND_INTERVAL_OVERRIDE = 0XA048 MSG_ID_HD_DEBUG_EVENT = 0XFFF1 MSG_ID_DG_DEBUG_EVENT = 0XFFF2 Index: dialin/dg/alarms.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/dg/alarms.py (.../alarms.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/dg/alarms.py (.../alarms.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -17,7 +17,8 @@ from logging import Logger from .constants import RESET, NO_RESET -from ..common.msg_defs import MsgIds +from ..common.msg_defs import MsgIds, MsgFieldPositions +from ..utils.checks import check_broadcast_interval_override_ms from ..protocols.CAN import DenaliMessage, DenaliChannels from ..utils.base import AbstractSubSystem, publish from ..utils.conversions import integer_to_bytearray @@ -44,12 +45,20 @@ msg_id = MsgIds.MSG_ID_ALARM_TRIGGERED.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_activate) - channel_id = DenaliChannels.dg_alarm_broadcast_ch_id msg_id = MsgIds.MSG_ID_ALARM_CLEARED.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_clear) + msg_id = MsgIds.MSG_ID_ALARM_CONDITION_CLEARED.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_condition_clear) + + msg_id = MsgIds.MSG_ID_DG_ALARM_INFO.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_information_sync) + # alarm states based on received DG alarm activation and alarm clear messages self.alarm_states = [False] * 500 + # alarm condition states based on received DG alarm activation and clear condition messages + self.alarm_conditions = [False] * 500 + self.safety_shutdown_active = False def get_alarm_states(self): """ @@ -59,6 +68,14 @@ """ return self.alarm_states + def get_alarm_conditions(self): + """ + Gets all alarm condition states for all alarms + + @return: List of booleans of size 500 + """ + return self.alarm_conditions + def get_alarm_state(self, alarm_id): """ Gets alarm state for given alarm @@ -67,6 +84,14 @@ """ return self.alarm_states[alarm_id] + def get_safety_shutdown_activated(self): + """ + Gets the state of the DG safety shutdown signal. + + @return: (bool) safety shutdown line is activated (T/F) + """ + return self.safety_shutdown_active + @publish(["alarm_states"]) def _handler_alarm_activate(self, message): """ @@ -78,6 +103,7 @@ alarm_id = struct.unpack(' int: """ Constructs and sends the alarm state override command @@ -129,3 +181,72 @@ # response payload is OK or not OK return 1 == received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] return False + + def cmd_clear_all_alarms(self) -> int: + """ + Constructs and sends the clear all active alarms command to the DG. + This will clear even non-recoverable alarms. + Constraints: + Must be logged into DG. + + @return: 1 if successful, zero otherwise + """ + + key = integer_to_bytearray(-758926171) # 0xD2C3B4A5 + payload = key + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_SUPER_CLEAR_ALARMS_CMD.value, + payload=payload) + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + self.logger.debug("All DG alarms cleared.") + # response payload is OK or not OK + return 1 == received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + return False + + def cmd_alarm_info_broadcast_interval_override(self, ms: int = 1000, reset: int = NO_RESET): + """ + Constructs and sends the alarm information 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 + + rst = integer_to_bytearray(reset) + mis = integer_to_bytearray(ms) + payload = rst + mis + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_ALARM_INFO_SEND_INTERVAL_OVERRIDE.value, + payload=payload) + + self.logger.debug("override DG alarm information 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("DG alarm information 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 Index: dialin/dg/dialysate_generator.py =================================================================== diff -u -r3dc6f8a5072178c107f7f273aae5f34ca1d06e4d -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 3dc6f8a5072178c107f7f273aae5f34ca1d06e4d) +++ dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -1,16 +1,16 @@ ########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2021 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 dialysate_generator.py +# @file dialysate_generator.py # -# @author (last) Peter Lucia -# @date (last) 10-Nov-2020 -# @author (original) Peter Lucia -# @date (original) 02-Apr-2020 +# @author (last) Dara Navaei +# @date (last) 29-Oct-2021 +# @author (original) Peter Lucia +# @date (original) 02-Apr-2020 # ############################################################################ @@ -46,6 +46,7 @@ from .uv_reactors import UVReactors from .valves import DGValves from .voltages import DGVoltages +from .events import DGEvents from ..common.msg_defs import MsgIds, MsgFieldPositions from ..protocols.CAN import DenaliCanMessenger, DenaliMessage, DenaliChannels from ..utils import * @@ -61,7 +62,7 @@ DG_OP_MODE_INIT_POST = 2 DG_OP_MODE_STANDBY = 3 DG_OP_MODE_STANDBY_SOLO = 4 - DG_OP_MODE_RECIRCULATE = 5 + DG_OP_MODE_GEN_IDLE = 5 DG_OP_MODE_FILL = 6 DG_OP_MODE_DRAIN = 7 DG_OP_MODE_FLUSH = 8 @@ -182,6 +183,7 @@ self.uv_reactors = UVReactors(self.can_interface, self.logger) self.valves = DGValves(self.can_interface, self.logger) self.voltages = DGVoltages(self.can_interface, self.logger) + self.events = DGEvents(self.can_interface, self.logger) def get_version(self): """ Index: dialin/dg/load_cells.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/dg/load_cells.py (.../load_cells.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/dg/load_cells.py (.../load_cells.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -149,7 +149,7 @@ payload = rst + mis message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=MsgIds.MSG_ID_LOAD_CELLL_SEND_INTERVAL_OVERRIDE.value, + message_id=MsgIds.MSG_ID_LOAD_CELL_SEND_INTERVAL_OVERRIDE.value, payload=payload) self.logger.debug("override DG load cell broadcast interval") Index: dialin/hd/alarms.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/hd/alarms.py (.../alarms.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/hd/alarms.py (.../alarms.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -142,6 +142,14 @@ """ return self.alarm_conditions + def get_alarm_state(self, alarm_id): + """ + Gets alarm state for given alarm + + @return: Alarm state + """ + return self.alarm_states[alarm_id] + def get_alarms_top(self): """ Gets the top alarm @@ -336,7 +344,7 @@ for x in range(500): self.alarm_states[x] = False - @publish(["alarm_states"]) + @publish(["alarm_states","alarm_conditions"]) def _handler_alarm_activate(self, message): """ Handles published HD alarm activation messages. @@ -350,7 +358,7 @@ self.alarm_states[alarm_id[0]] = True self.alarm_conditions[alarm_id[0]] = True - @publish(["alarm_states"]) + @publish(["alarm_states","alarm_conditions"]) def _handler_alarm_clear(self, message): """ Handles published HD alarm clear messages. @@ -363,7 +371,7 @@ self.alarm_states[alarm_id[0]] = False self.alarm_conditions[alarm_id[0]] = False - @publish(["alarm_conditions"]) + @publish(["alarm_conditions","alarm_conditions"]) def _handler_alarm_condition_clear(self, message): """ Handles published HD alarm clear alarm condition messages. @@ -375,7 +383,7 @@ alarm_id = struct.unpack(' int: @@ -403,46 +403,6 @@ self.logger.debug("Timeout!!!!") return False - def cmd_blood_flow_signal_strength_override(self, signal_strength: float, reset: int = NO_RESET) -> int: - """ - Constructs and sends the measured blood flow signal strength % override \n - command. - Constraints: - Must be logged into HD. - - @param signal_strength: float - pct (0..100) 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) - sig = float_to_bytearray(signal_strength) - payload = rst + sig - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=MsgIds.MSG_ID_BLOOD_FLOW_SIG_STRENGTH_OVERRIDE.value, - payload=payload) - - self.logger.debug("override measured blood flow signal strength") - - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - # self.logger.debug(received_message) - if reset == RESET: - str_sig = "reset back to normal" - else: - str_sig = str(signal_strength) - self.logger.debug("Blood pump flow signal strength overridden to " + str_sig + " %: " + - 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_blood_flow_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ Constructs and sends the measured blood flow broadcast interval override command @@ -512,3 +472,44 @@ else: self.logger.debug("Timeout!!!!") return False + + def cmd_blood_pump_rotor_count_override(self, rot_count: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the blood pump rotor count override command + Constraints: + Must be logged into HD. + Given count must be zero or positive integer. + + @param rot_count: integer - rotor count 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) + cnt = integer_to_bytearray(rot_count) + payload = rst + cnt + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_BLOOD_PUMP_ROTOR_COUNT_OVERRIDE.value, + payload=payload) + + self.logger.debug("override blood pump rotor count") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # self.logger.debug(received_message) + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(rot_count) + ": " + self.logger.debug("Blood pump rotor count 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 + Index: dialin/hd/dialysate_inlet_flow.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/hd/dialysate_inlet_flow.py (.../dialysate_inlet_flow.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/hd/dialysate_inlet_flow.py (.../dialysate_inlet_flow.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -53,7 +53,6 @@ self.measured_dialysate_inlet_pump_mc_speed = 0.0 self.measured_dialysate_inlet_pump_mc_current = 0.0 self.pwm_duty_cycle_pct = 0.0 - self.flow_signal_strength = 0.0 def get_target_dialysate_inlet_flow_rate(self): """ @@ -111,23 +110,14 @@ """ return self.pwm_duty_cycle_pct - def get_flow_signal_strength(self): - """ - Gets the dialysate flow signal strength pct - - @return: the dialysate flow signal strength pct (0..100) - """ - return self.flow_signal_strength - @publish([ "target_dialysate_inlet_flow_rate", "measured_dialysate_inlet_flow_rate", "measured_dialysate_inlet_pump_rotor_speed", "measured_dialysate_inlet_pump_speed", "measured_dialysate_inlet_pump_mc_speed", "measured_dialysate_inlet_pump_mc_current", - "pwm_duty_cycle_pct", - "flow_signal_strength" + "pwm_duty_cycle_pct" ]) def _handler_dialysate_inlet_flow_sync(self, message): """ @@ -152,8 +142,6 @@ message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) pwm = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) - sig = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) self.target_dialysate_inlet_flow_rate = tgt[0] self.measured_dialysate_inlet_flow_rate = flow[0] @@ -162,7 +150,6 @@ self.measured_dialysate_inlet_pump_mc_speed = mcspeed[0] self.measured_dialysate_inlet_pump_mc_current = mccurr[0] self.pwm_duty_cycle_pct = pwm[0] - self.flow_signal_strength = sig[0] def cmd_dialysate_inlet_flow_set_point_override(self, flow: int, @@ -416,46 +403,6 @@ self.logger.debug("Timeout!!!!") return False - def cmd_dialysate_flow_signal_strength_override(self, signal_strength: float, reset: int = NO_RESET) -> int: - """ - Constructs and sends the measured dialysate flow signal strength % override \n - command. - Constraints: - Must be logged into HD. - - @param signal_strength: float - pct (0..100) 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) - sig = float_to_bytearray(signal_strength) - payload = rst + sig - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=MsgIds.MSG_ID_DIAL_IN_FLOW_SIG_STRENGTH_OVERRIDE.value, - payload=payload) - - self.logger.debug("override measured dialysate flow signal strength") - - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - # self.logger.debug(received_message) - if reset == RESET: - str_sig = "reset back to normal" - else: - str_sig = str(signal_strength) - self.logger.debug("Dialysate pump flow signal strength overridden to " + str_sig + " %: " + - 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_dialysate_inlet_flow_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ Constructs and sends the measured dialysate inlet flow broadcast interval override command Index: dialin/hd/hemodialysis_device.py =================================================================== diff -u -r965b384855cf17d6b975f9989f13a26248e2d919 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/hd/hemodialysis_device.py (.../hemodialysis_device.py) (revision 965b384855cf17d6b975f9989f13a26248e2d919) +++ dialin/hd/hemodialysis_device.py (.../hemodialysis_device.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -1,16 +1,16 @@ ########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2021 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 hemodialysis_device.py +# @file hemodialysis_device.py # -# @author (last) Peter Lucia -# @date (last) 10-Nov-2020 -# @author (original) Peter Lucia -# @date (original) 02-Apr-2020 +# @author (last) Dara Navaei +# @date (last) 14-Oct-2021 +# @author (original) Peter Lucia +# @date (original) 02-Apr-2020 # ############################################################################ import struct @@ -23,7 +23,6 @@ from .blood_leak import HDBloodLeak from .buttons import HDButtons from .calibration_record import HDCalibrationNVRecord -from .constants import NO_RESET, RESET from .dialysate_inlet_flow import HDDialysateInletFlow from .dialysate_outlet_flow import HDDialysateOutletFlow from .fluid_leak import HDFluidLeak @@ -32,6 +31,9 @@ from .rtc import HDRTC from .service_record import HDServiceNVRecords from .switches import HDSwitches +from .temperatures import HDTemperatures +from .fans import HDFans +from .constants import NO_RESET, RESET from .syringe_pump import HDSyringePump from .system_record import HDSystemNVRecords from .treatment import HDTreatment @@ -117,6 +119,12 @@ self.ui = HDUIProxy(self.can_interface, self.logger) self.valves = HDValves(self.can_interface, self.logger) self.voltages = HDVoltages(self.can_interface, self.logger) + self.calibration_record = HDCalibrationNVRecord(self.can_interface, self.logger) + self.system_record = HDSystemNVRecords(self.can_interface, self.logger) + self.service_record = HDServiceNVRecords(self.can_interface, self.logger) + self.switches = HDSwitches(self.can_interface, self.logger) + self.temperatures = HDTemperatures(self.can_interface, self.logger) + self.fans = HDFans(self.can_interface, self.logger) self.watchdog = HDWatchdog(self.can_interface, self.logger) def get_operation_mode(self): Index: dialin/hd/pressure_occlusion.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/hd/pressure_occlusion.py (.../pressure_occlusion.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/hd/pressure_occlusion.py (.../pressure_occlusion.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -47,8 +47,6 @@ self.arterial_pressure = 0.0 self.venous_pressure = 0.0 self.blood_pump_occlusion = 0 - self.dialysate_inlet_pump_occlusion = 0 - self.dialysate_outlet_pump_occlusion = 0 def get_arterial_pressure(self): """ @@ -73,22 +71,6 @@ """ return self.blood_pump_occlusion - def get_dialysate_inlet_pump_occlusion(self): - """ - Gets the dialysate inlet pump occlusion - - @return: (int)The dialysate inlet pump occlusion - """ - return self.dialysate_inlet_pump_occlusion - - def get_dialysate_outlet_pump_occlusion(self): - """ - Gets the dialysate outlet pump occlusion - - @return: (int)The dialysate outlet pump occlusion - """ - return self.dialysate_outlet_pump_occlusion - @publish([ "arterial_pressure", "venous_pressure", @@ -237,108 +219,68 @@ self.logger.debug("Timeout!!!!") return False - def cmd_dialysate_inlet_pump_measured_occlusion_override(self, occl: int, reset: int = NO_RESET) -> int: + def cmd_pressure_occlusion_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ - Constructs and sends the measured dialysate inlet pump occlusion pressure override \n - command. + Constructs and sends the pressure/occlusion broadcast interval override command Constraints: Must be logged into HD. + Given interval must be non-zero and a multiple of the HD general task interval (50 ms). - @param occl: integer - pressure (in counts) to override with + @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 """ - rst = integer_to_bytearray(reset) - occ = integer_to_bytearray(occl) - payload = rst + occ - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=MsgIds.MSG_ID_OCCLUSION_DIAL_IN_PUMP_OVERRIDE.value, - payload=payload) - - self.logger.debug("override measured dialysate inlet pump occlusion pressure") - - # 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(occl) + " mmHg. " - self.logger.debug("Dialysate inlet pump occlusion pressure (measured) 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!!!!") + if not check_broadcast_interval_override_ms(ms): return False - def cmd_dialysate_outlet_pump_measured_occlusion_override(self, occl: int, reset: int = NO_RESET) -> int: - """ - Constructs and sends the measured dialysate outlet pump occlusion pressure override \n - command. - Constraints: - Must be logged into HD. - - @param occl: integer - pressure (in 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) - occ = integer_to_bytearray(occl) - payload = rst + occ + mis = integer_to_bytearray(ms) + payload = rst + mis message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=MsgIds.MSG_ID_OCCLUSION_DIAL_OUT_PUMP_OVERRIDE.value, + message_id=MsgIds.MSG_ID_PRES_OCCL_SEND_INTERVAL_OVERRIDE.value, payload=payload) - self.logger.debug("override measured dialysate outlet pump occlusion pressure") + self.logger.debug("override pressure/occlusion 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. " + str_res = "reset back to normal: " else: - str_res = str(occl) + " mmHg. " - self.logger.debug("Dialysate outlet pump occlusion pressure (measured) overridden to " + str_res + + str_res = str(ms) + " ms: " + self.logger.debug("Pressure/occlusion 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_pressure_occlusion_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + def cmd_arterial_pressure_offset_override(self, offset: float, reset: int = NO_RESET) -> int: """ - Constructs and sends the pressure/occlusion broadcast interval override command + Constructs and sends the arterial pressure offset override command Constraints: Must be logged into HD. - Given interval must be non-zero and a multiple of the HD general task interval (50 ms). - @param ms: integer - interval (in ms) to override with + @param offset: float - offset (in mmHg) for arterial pressure sensor @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 + off = float_to_bytearray(offset) + payload = rst + off message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=MsgIds.MSG_ID_PRES_OCCL_SEND_INTERVAL_OVERRIDE.value, + message_id=MsgIds.MSG_ID_SET_ARTERIAL_PRESSURE_OFFSET.value, payload=payload) - self.logger.debug("override pressure/occlusion broadcast interval") + self.logger.debug("override arterial pressure offset") # Send message received_message = self.can_interface.send(message) @@ -348,8 +290,8 @@ if reset == RESET: str_res = "reset back to normal: " else: - str_res = str(ms) + " ms: " - self.logger.debug("Pressure/occlusion broadcast interval overridden to " + str_res + + str_res = str(offset) + " mmHg: " + self.logger.debug("Arterial pressure offset 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] Index: dialin/hd/syringe_pump.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/hd/syringe_pump.py (.../syringe_pump.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/hd/syringe_pump.py (.../syringe_pump.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -713,3 +713,35 @@ self.logger.debug("Timeout!!!!") return False + def cmd_set_syringe_pump_dac_ref_voltage(self, dac_v_ref: float = 0.15) -> int: + """ + Constructs and sends the set syringe pump DAC pressure sensor reference voltag command + Constraints: + Must be logged into HD. + + @param dac_v_ref: float - status (0.0..3.3) + @return: 1 if successful, zero otherwise + """ + + payload = float_to_bytearray(dac_v_ref) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_SYRINGE_PUMP_FORCE_SENSOR_DAC_CALIBRATE.value, + payload=payload) + + self.logger.debug("set HD syringe pump DAC reference voltage") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + str_res = str(dac_v_ref) + self.logger.debug("Syringe pump DAC reference voltage set to " + str_res + "V. " + + 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 + Index: dialin/hd/treatment.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/hd/treatment.py (.../treatment.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ dialin/hd/treatment.py (.../treatment.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -178,7 +178,6 @@ self.rinseback_cur_rate = 0 self.rinseback_timeout_secs = 0 self.rinseback_countdown_secs = 0 - self.rinseback_safety_cum_vol = 0.0 # re-circulation status self.recirc_timeout_secs = 0 self.recirc_countdown_secs = 0 @@ -327,14 +326,6 @@ """ return self.blood_prime_cum_vol - def get_blood_prime_safety_volume_delivered(self) -> float: - """ - Returns blood prime safety volume (in mL) delivered - - @return: The blood prime safety volume delivered - """ - return self.blood_prime_ind_cum_vol - def get_rinseback_target_volume(self) -> float: """ Returns rinseback target volume (in mL) @@ -351,14 +342,6 @@ """ return self.rinseback_cum_vol - def get_rinseback_safety_volume_delivered(self) -> float: - """ - Returns rinseback safety volume (in mL) delivered - - @return: The rinseback safety volume delivered - """ - return self.rinseback_safety_cum_vol - def get_rinseback_current_rate(self) -> int: """ Returns rinseback current rate (in mL/min) @@ -528,15 +511,12 @@ 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])) - rsv = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) 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] - self.rinseback_safety_cum_vol = rsv[0] @publish([ "blood_prime_tgt_vol", @@ -1325,40 +1305,6 @@ self.logger.debug("Timeout!!!!") return False - def cmd_blood_prime_safety_volume_delivered_override(self, volume: float, reset: int = NO_RESET) -> int: - """ - Constructs and sends the blood prime safety volume delivered override command - Constraints: - Must be logged into HD. - - @param volume: float - safety volume (in mL) of blood delivered during blood prime (must be positive) - @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_hd_ch_id, - message_id=MsgIds.MSG_ID_HD_BLOOD_PRIME_SAFETY_VOLUME_OVERRIDE.value, - payload=payload) - - self.logger.debug("override HD blood prime safety volume delivered (in mL).") - - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - self.logger.debug("Blood prime safety volume delivered overridden to " + str(volume) + " mL. " + - 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_rinseback_volume_delivered_override(self, volume: float, reset: int = NO_RESET) -> int: """ Constructs and sends the rinseback volume delivered override command @@ -1393,40 +1339,6 @@ self.logger.debug("Timeout!!!!") return False - def cmd_rinseback_safety_volume_delivered_override(self, volume: float, reset: int = NO_RESET) -> int: - """ - Constructs and sends the rinseback safety volume delivered override command - Constraints: - Must be logged into HD. - - @param volume: float - safety volume (in mL) of blood returned during rinseback (must be positive) - @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_hd_ch_id, - message_id=MsgIds.MSG_ID_HD_RINSEBACK_SAFETY_VOLUME_OVERRIDE.value, - payload=payload) - - self.logger.debug("override HD rinseback safety volume delivered (in mL).") - - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - self.logger.debug("Rinseback safety volume delivered overridden to " + str(volume) + " mL. " + - 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_blood_prime_submode_broadcast_interval_override(self, ms: int = 250, reset: int = NO_RESET): """ Constructs and sends the treatment blood prime sub-mode broadcast interval override command Index: dialin/utils/conversions.py =================================================================== diff -u -r965b384855cf17d6b975f9989f13a26248e2d919 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- dialin/utils/conversions.py (.../conversions.py) (revision 965b384855cf17d6b975f9989f13a26248e2d919) +++ dialin/utils/conversions.py (.../conversions.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -1,16 +1,16 @@ ########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2021 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 conversions.py +# @file conversions.py # -# @author (last) Peter Lucia -# @date (last) 06-Oct-2020 -# @author (original) Peter Lucia -# @date (original) 02-Apr-2020 +# @author (last) Quang Nguyen +# @date (last) 07-Jul-2021 +# @author (original) Peter Lucia +# @date (original) 02-Apr-2020 # ############################################################################ import struct Index: tests/test_uf.py =================================================================== diff -u -r8474f8e345f165187d4dde17840575ee4e98a9b4 -r6e5fe18fa126baffc94a09c050312ac00d98c288 --- tests/test_uf.py (.../test_uf.py) (revision 8474f8e345f165187d4dde17840575ee4e98a9b4) +++ tests/test_uf.py (.../test_uf.py) (revision 6e5fe18fa126baffc94a09c050312ac00d98c288) @@ -71,6 +71,7 @@ ", DPi.f, " + '{:7.1f}'.format(hd.dialysate_inlet_flow.measured_dialysate_inlet_flow_rate) + \ ", DPo.m, " + '{:7.1f}'.format(hd.dialysate_outlet_flow.measured_dialysate_outlet_pump_speed) + \ ", DPo.r, " + '{:6.1f}'.format(hd.dialysate_outlet_flow.measured_dialysate_outlet_pump_rotor_speed) + \ + ", FMD.f, " + '{:7.4f}'.format(dg.dialysate_flow_sensor.get_flow_rate()) + \ ", ROP.f, " + '{:9.3f}'.format(dg.ro_pump.measured_flow_rate_lpm) pumpPWMs = ", BP.w, "+'{:6.1f}'.format(hd.bloodflow.pwm_duty_cycle_pct) + \ ", DPi.w, " + '{:6.1f}'.format(hd.dialysate_inlet_flow.pwm_duty_cycle_pct) + \ @@ -83,9 +84,7 @@ ", PDr, " + '{:9.2f}'.format(dg.pressures.drain_pump_outlet_pressure) hdPress = ", PBA, " + '{:9.2f}'.format(hd.pressure_occlusion.arterial_pressure) + \ ", PBV, " + '{:9.2f}'.format(hd.pressure_occlusion.venous_pressure) + \ - ", OB, " + '{:7d}'.format(hd.pressure_occlusion.blood_pump_occlusion) + \ - ", ODi, " + '{:7d}'.format(hd.pressure_occlusion.dialysate_inlet_pump_occlusion) + \ - ", ODo, " + '{:7d}'.format(hd.pressure_occlusion.dialysate_outlet_pump_occlusion) + ", OB, " + '{:7d}'.format(hd.pressure_occlusion.blood_pump_occlusion) Temps = ", TPi, " + '{:9.2f}'.format(dg.temperature_sensors.temperature_sensors[TemperatureSensorsNames.INLET_PRIMARY_HEATER.name]) + \ ", TPo, " + '{:9.2f}'.format(dg.temperature_sensors.temperature_sensors[TemperatureSensorsNames.OUTLET_PRIMARY_HEATER.name]) + \ ", TRo, " + '{:9.2f}'.format(dg.temperature_sensors.temperature_sensors[TemperatureSensorsNames.INLET_DIALYSATE.name]) + \