Index: HD/HemodialysisDevice.py =================================================================== diff -u -r68137f3ef7ad8dac0e1209b50272ba613543e2d9 -r082ea4c8b0c6a3c804453308990d0bb98a0a2156 --- HD/HemodialysisDevice.py (.../HemodialysisDevice.py) (revision 68137f3ef7ad8dac0e1209b50272ba613543e2d9) +++ HD/HemodialysisDevice.py (.../HemodialysisDevice.py) (revision 082ea4c8b0c6a3c804453308990d0bb98a0a2156) @@ -1,2643 +1,68 @@ ########################################################################### # -# Copyright (c) 2019-2019 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2020 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 HemodialysisDevice.py # -# @date 16-Oct-2019 -# @author S. Nash +# @date 31-Mar-2020 +# @author P. Lucia # # @brief This class provides the basic interface to communicate with # the HD board. # ############################################################################ - -from DialIn.CoreCANProtocol import DenaliCanMessenger -from DialIn.CoreCANProtocol import DenaliMessage -from DialIn.CoreCANProtocol import DenaliChannels +from DialIn.CoreCANProtocol import (DenaliCanMessenger) from time import sleep -from binascii import unhexlify -#from HD_DialOutFlow import HD_DialOut -#from HD_DialOutFlow import DialOutStates -import struct -import ctypes +from .Alarms import HDAlarms +from .Buttons import HDButtons +from .UI import HDUI +from .Watchdog import HDWatchdog +from .RTC import HDRTC +from .BloodFlow import HDBloodFlow +from .DialysateInletFlow import HDDialysateInletFlow +from .DialysateOutletFlow import HDDialysateOutletFlow +from .Treatment import HDTreatment +from .Basics import HDBasics +from .PressureOcclusion import HDPressureOcclusion class HD: # for reset param in override commands NO_RESET = 0 RESET = 1 - def __init__(self, can__interface="can0"): + def __init__(self, can_interface_name="can0"): """ - HD constructor using can bus + HD constructor using can bus - \param bus: can bus, e.g. "can0" + \param bus: can bus, e.g. "can0" + \returns HD object provides test/service commands for the HD sub-system. + \details For example: - \returns HD object provides test/service commands for the HD sub-system. - - \details For example: - - hd_object = HD(can_interface='can0') or - hd_object = HD('can0') + hd_object = HD(can_interface='can0') or + hd_object = HD('can0') """ # Create listener - self.can_interface = DenaliCanMessenger(can_interface=can__interface) + self.can_interface = DenaliCanMessenger(can_interface=can_interface_name) self.can_interface.start() + # Create command groups - self.Basics = HD.HD__Basics(self) - self.Alarms = HD.HD_Alarms(self, self.can_interface) - self.BloodFlow = HD.HD_BloodFlow(self, self.can_interface) - self.Buttons = HD.HD_Buttons(self) - self.DialysateInletFlow = HD.HD_DialysateInletFlow(self, self.can_interface) - self.DialysateOutletFlow = HD.HD_DialysateOutletFlow(self, self.can_interface) - self.Pressure_Occlusion = HD.HD_Pressure_Occlusion(self, self.can_interface) - self.RTC = HD.HD_RTC(self, self.can_interface) - self.Treatment = HD.HD_Treatment(self, self.can_interface) - self.UI = HD.HD_UI(self) - self.Watchdog = HD.HD_Watchdog(self) - ## DialOut is an HD_DialOutFlow object - # self.DialOut = HD_DialOut(self.can_interface) + self.Basics = HDBasics(self.can_interface) + self.Alarms = HDAlarms(self.can_interface) + self.Buttons = HDButtons(self.can_interface) + self.UI = HDUI(self.can_interface) + self.RTC = HDRTC(self.can_interface) + self.Watchdog = HDWatchdog(self.can_interface) + self.BloodFlow = HDBloodFlow(self.can_interface) + self.DialysateInletFlow = HDDialysateInletFlow(self.can_interface) + self.DialysateOutletFlow = HDDialysateOutletFlow(self.can_interface) + self.Treatment = HDTreatment(self.can_interface) + self.Pressure_Occlusion = HDPressureOcclusion(self.can_interface) - class HD__Basics: - """ - \class HD__Basics - \brief Hemodialysis Device (HD) Dialin API sub-class for basic HD commands. - """ - - # Basic message IDs - MSG_ID_LOGIN_TO_HD = 0x8000 - MSG_ID_HD_MSG = 0x8001 - - # HD login password - HD_LOGIN_PASSWORD = '123' - - def __init__(self, outer_instance): - self.outer_instance = outer_instance - - """ - HD__Basics constructor - - \param outer_instance: reference to the HD (outer) class. - - \returns HD__Basics object. - """ - - def CmdLogInToHD(self): - """ - Constructs and sends a login command via CAN bus. Login required before \n - other commands can be sent to the HD. - - \returns 1 if logged in, 0 if log in failed - """ - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_LOGIN_TO_HD, - payload=list(map(int, map(ord, self.HD_LOGIN_PASSWORD)))) - - print("login") - - # Send message - received_message = self.outer_instance.can_interface.send(message) - - if received_message is not None: - #print(received_message) - if received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] == 1: - print("Logged In") - else: - print("Log In Failed.") - #print("Logged In: " + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] - else: - print("Timeout!!!!") - return False - - def CmdHDMessageInsert(self, msg): - """ - Constructs and sends the insert HD message command. Given message will \n - be inserted into the HD's received message queue for processing. - - \param msg: byte array - properly formatted HD message to insert - - \returns 1 if successful, zero otherwise - """ - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_HD_MSG, - payload=msg) - - print("insert HD message") - - # Send message - received_message = self.outer_instance.can_interface.send(message) - - # If there is content... - if received_message is not None: - #print(received_message) - print("Inserted message: " + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) - # response payload is OK or not OK - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] - else: - print("Timeout!!!!") - return False - - class HD_Buttons: - """ - \class HD_Buttons - - \brief Hemodialysis Device (HD) Dialin API sub-class for button related commands. - """ - - # Buttons message IDs - MSG_ID_HD_OFF_BUTTON_OVERRIDE = 0x8002 - MSG_ID_HD_STOP_BUTTON_OVERRIDE = 0x8003 - - def __init__(self, outer_instance): - self.outer_instance = outer_instance - - """ - HD_Buttons constructor - - \param outer_instance: reference to the HD (outer) class. - - \returns HD_Buttons object. - """ - - def CmdOffButtonOverride(self, reset, state): - """ - Constructs and sends the Off button override command - - \param reset: integer - 1 to reset a previous override, 0 to override - \param state: integer - 1 to override off button to "pressed", 0 to "released" - \returns 1 if successful, zero otherwise - """ - - rst = self.outer_instance.integer2ByteArray(reset) - sta = self.outer_instance.integer2ByteArray(state) - payload = rst + sta - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_HD_OFF_BUTTON_OVERRIDE, - payload=payload) - - print("override off button") - - # Send message - received_message = self.outer_instance.can_interface.send(message) - - # If there is content... - if received_message is not None: - #print(received_message) - if reset == HD.RESET: - str_res = "reset back to normal" - else: - str_res = ("pressed" if state != 0 else "released") - - print("Off button 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: - print("Timeout!!!!") - return False - - def CmdStopButtonOverride(self, reset, state): - """ - Constructs and sends the Stop button override command - - \param reset: integer - 1 to reset a previous override, 0 to override - \param state: integer - 1 to override stop button to "pressed", 0 to "released" - \returns 1 if successful, zero otherwise - """ - - rst = self.outer_instance.integer2ByteArray(reset) - sta = self.outer_instance.integer2ByteArray(state) - payload = rst + sta - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_HD_STOP_BUTTON_OVERRIDE, - payload=payload) - - print("override stop button") - - # Send message - received_message = self.outer_instance.can_interface.send(message) - - # If there is content... - if received_message is not None: - #print(received_message) - if reset == HD.RESET: - str_res = "reset back to normal" - else: - str_res = ("pressed" if state != 0 else "released") - print("Stop button 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: - print("Timeout!!!!") - return False - - class HD_UI: - """ - \class HD_UI - - \brief Hemodialysis Device (HD) Dialin API sub-class for UI commands. - """ - - # UI message IDs - MSG_ID_UI_CHECKIN_WITH_HD = 0x0007 - MSG_ID_HD_UF_PAUSE_RESUME_REQUEST = 0x0010 - MSG_ID_HD_TREATMENT_PARAMS_RANGES = 0x001A - MSG_ID_UF_SETTINGS_CHANGE_REQUEST_BY_USER = 0x0011 - MSG_ID_UF_SETTINGS_CHANGE_RESPONSE_FROM_HD = 0x0013 - MSG_ID_UF_SETTINGS_CHANGE_CONFIRMED_BY_USER = 0x0015 - MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_REQUEST = 0x0016 - MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_RESPONSE_FROM_HD = 0x001B - MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_REQUEST_BY_USER = 0x0017 - MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_RESPONSE_FROM_HD = 0x0018 - - LITER_TO_ML_CONVERSION_FACTOR = 1000.0 - UF_CMD_PAUSE = 0 - UF_CMD_RESUME = 1 - UF_CMD_CHANGE_TIME_TO_ADJUST = 0 - UF_CMD_CHANGE_RATE_TO_ADJUST = 1 - RESPONSE_REJECTED = 0 - RESPONSE_ACCEPTED = 1 - - # HD update on valid treatment parameter ranges message field positions - START_POS_MIN_TREAT_TIME = DenaliMessage.PAYLOAD_START_INDEX - END_POS_MIN_TREAT_TIME = START_POS_MIN_TREAT_TIME + 4 - START_POS_MAX_TREAT_TIME = END_POS_MIN_TREAT_TIME - END_POS_MAX_TREAT_TIME = START_POS_MAX_TREAT_TIME + 4 - START_POS_MIN_UF_VOL = END_POS_MAX_TREAT_TIME - END_POS_MIN_UF_VOL = START_POS_MIN_UF_VOL + 4 - START_POS_MAX_UF_VOL = END_POS_MIN_UF_VOL - END_POS_MAX_UF_VOL = START_POS_MAX_UF_VOL + 4 - START_POS_MIN_DIAL_RATE = END_POS_MAX_UF_VOL - END_POS_MIN_DIAL_RATE = START_POS_MIN_DIAL_RATE + 4 - START_POS_MAX_DIAL_RATE = END_POS_MIN_DIAL_RATE - END_POS_MAX_DIAL_RATE = START_POS_MAX_DIAL_RATE + 4 - - # HD response to treatment duration change request message field positions - START_POS_TIME_CHG_RSP_ACCEPTED = DenaliMessage.PAYLOAD_START_INDEX - END_POS_TIME_CHG_RSP_ACCEPTED = START_POS_TIME_CHG_RSP_ACCEPTED + 4 - START_POS_TIME_CHG_RSP_REASON = END_POS_TIME_CHG_RSP_ACCEPTED - END_POS_TIME_CHG_RSP_REASON = START_POS_TIME_CHG_RSP_REASON + 4 - START_POS_TIME_CHG_RSP_TIME = END_POS_TIME_CHG_RSP_REASON - END_POS_TIME_CHG_RSP_TIME = START_POS_TIME_CHG_RSP_TIME + 4 - START_POS_TIME_CHG_RSP_UF_VOL = END_POS_TIME_CHG_RSP_TIME - END_POS_TIME_CHG_RSP_UF_VOL = START_POS_TIME_CHG_RSP_UF_VOL + 4 - - # HD response to UF volume change request message field positions - START_POS_UF_CHG_RSP_RESP = DenaliMessage.PAYLOAD_START_INDEX - END_POS_UF_CHG_RSP_RESP = START_POS_UF_CHG_RSP_RESP + 4 - START_POS_UF_CHG_RSP_REJECT_REASON = END_POS_UF_CHG_RSP_RESP - END_POS_UF_CHG_RSP_REJECT_REASON = START_POS_UF_CHG_RSP_REJECT_REASON + 4 - START_POS_UF_CHG_RSP_VOL = END_POS_UF_CHG_RSP_RESP - END_POS_UF_CHG_RSP_VOL = START_POS_UF_CHG_RSP_VOL + 4 - START_POS_UF_CHG_RSP_TIME = END_POS_UF_CHG_RSP_VOL - END_POS_UF_CHG_RSP_TIME = START_POS_UF_CHG_RSP_TIME + 4 - START_POS_UF_CHG_RSP_TIME_DIFF = END_POS_UF_CHG_RSP_TIME - END_POS_UF_CHG_RSP_TIME_DIFF = START_POS_UF_CHG_RSP_TIME_DIFF + 4 - START_POS_UF_CHG_RSP_RATE = END_POS_UF_CHG_RSP_TIME_DIFF - END_POS_UF_CHG_RSP_RATE = START_POS_UF_CHG_RSP_RATE + 4 - START_POS_UF_CHG_RSP_RATE_DIFF = END_POS_UF_CHG_RSP_RATE - END_POS_UF_CHG_RSP_RATE_DIFF = START_POS_UF_CHG_RSP_RATE_DIFF + 4 - - # HD response to blood/dialysate flow change request message field positions - START_POS_BLD_DIAL_CHG_RSP_ACCEPTED = DenaliMessage.PAYLOAD_START_INDEX - END_POS_BLD_DIAL_CHG_RSP_ACCEPTED = START_POS_BLD_DIAL_CHG_RSP_ACCEPTED + 4 - START_POS_BLD_DIAL_CHG_RSP_REASON = END_POS_BLD_DIAL_CHG_RSP_ACCEPTED - END_POS_BLD_DIAL_CHG_RSP_REASON = START_POS_BLD_DIAL_CHG_RSP_REASON + 4 - START_POS_BLD_DIAL_CHG_RSP_BLD_RATE = END_POS_BLD_DIAL_CHG_RSP_REASON - END_POS_BLD_DIAL_CHG_RSP_BLD_RATE = START_POS_BLD_DIAL_CHG_RSP_BLD_RATE + 4 - START_POS_BLD_DIAL_CHG_RSP_DIAL_RATE = END_POS_BLD_DIAL_CHG_RSP_BLD_RATE - END_POS_BLD_DIAL_CHG_RSP_DIAL_RATE = START_POS_BLD_DIAL_CHG_RSP_DIAL_RATE + 4 - - def __init__(self, outer_instance, can_interface=None): - self.outer_instance = outer_instance - """ - HD_UI constructor - - \param outer_instance: reference to the HD (outer) class. - \can_interface: interface to can device. - - \returns HD_UI object. - """ - - # register function to handle HD response to UF change requests - if can_interface is not None: - channel_id = DenaliChannels.hd_to_ui_ch_id - can_interface.register_receiving_publication_function(channel_id, self.MSG_ID_UF_SETTINGS_CHANGE_RESPONSE_FROM_HD, - self.handlerUFChangeResponseFunction) - can_interface.register_receiving_publication_function(channel_id, self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_RESPONSE_FROM_HD, - self.handlerTreatmentDurationChangeResponseFunction) - can_interface.register_receiving_publication_function(channel_id, self.MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_RESPONSE_FROM_HD, - self.handlerBloodAndDialysateChangeResponseFunction) - can_interface.register_receiving_publication_function(channel_id, self.MSG_ID_HD_TREATMENT_PARAMS_RANGES, - self.handlerTreatmentParamRangesFunction) - - # initialize variables that will be populated by treatment parameter ranges message - self.MinTreatmentDurationMin = 0 - self.MaxTreatmentDurationMin = 0 - self.MinUFVolumeMl = 0 - self.MaxUFVolumeMl = 0 - self.MinDialysateFlowRateMlMin = 0 - self.MaxDialysateFlowRateMlMin = 0 - # initialize variables that will be populated by response from HD to treatment duration change request - self.DurationChangeSucceeded = False - self.DurationChangeRejectReason = 0 - self.DurationChangeTimeMin = 0 - self.DurationChangeUFVolMl = 0 - # initialize variables that will be populated by response from HD to UF change request - self.UFChangeSucceeded = False - self.UFChangeRejectReason = 0 - self.UFChangeVolumeMl = 0 - self.UFChangeTimeMin = 0 - self.UFChangeRateMlMin = 0.0 - self.UFChangeTimeDiff = 0 - self.UFChangeRateDiff = 0.0 - # initialize variables that will be populated by response from HD to blood & dialysate flow rate change request - self.BloodAndDialysateFlowRateChangeSucceeded = False - self.BloodAndDialysateFlowRateChangeRejectReason = 0 - self.TargetBloodFlowRate = 0 - self.TargetDialysateFlowRate = 0 - - def handlerTreatmentParamRangesFunction(self, message): - """ - Handler for response from HD regarding valid treatment parameter ranges. - - \param message: response message from HD regarding valid treatment parameter ranges.\n - U32 Minimum treatment duration setting (in min.). \n - U32 Maximum treatment duration setting (in min.). \n - U32 Minimum ultrafiltration volume (in mL). \n - U32 Maximum ultrafiltration volume (in mL). \n - U32 Minimum dialysate flow rate (in mL/min). \n - U32 Maximum dialysate flow rate (in mL/min). - - \returns none - """ - minTime = struct.unpack('i',bytearray( - message['message'][self.START_POS_MIN_TREAT_TIME:self.END_POS_MIN_TREAT_TIME])) - maxTime = struct.unpack('i',bytearray( - message['message'][self.START_POS_MAX_TREAT_TIME:self.END_POS_MAX_TREAT_TIME])) - minUFVol = struct.unpack('i',bytearray( - message['message'][self.START_POS_MIN_UF_VOL:self.END_POS_MIN_UF_VOL])) - maxUFVol = struct.unpack('i',bytearray( - message['message'][self.START_POS_MAX_UF_VOL:self.END_POS_MAX_UF_VOL])) - minDialRt = struct.unpack('i',bytearray( - message['message'][self.START_POS_MIN_DIAL_RATE:self.END_POS_MIN_DIAL_RATE])) - maxDialRt = struct.unpack('i',bytearray( - message['message'][self.START_POS_MAX_DIAL_RATE:self.END_POS_MAX_DIAL_RATE])) - - self.MinTreatmentDurationMin = minTime[0] - self.MaxTreatmentDurationMin = maxTime[0] - self.MinUFVolumeMl = minUFVol[0] - self.MaxUFVolumeMl = maxUFVol[0] - self.MinDialysateFlowRateMlMin = minDialRt[0] - self.MaxDialysateFlowRateMlMin = maxDialRt[0] - - def handlerTreatmentDurationChangeResponseFunction(self, message): - """ - Handler for response from HD regarding treatment duration change request. - - \param message: response message from HD regarding treatment duration change.\n - BOOL Accepted \n - U32 Reject reason (if not accepted) \n - U32 Treatment duration (min) \n - U32 UF volue (mL) \n - - \returns none - """ - rsp = struct.unpack('i',bytearray( - message['message'][self.START_POS_TIME_CHG_RSP_ACCEPTED:self.END_POS_TIME_CHG_RSP_ACCEPTED])) - rea = struct.unpack('i',bytearray( - message['message'][self.START_POS_TIME_CHG_RSP_REASON:self.END_POS_TIME_CHG_RSP_REASON])) - tim = struct.unpack('i',bytearray( - message['message'][self.START_POS_TIME_CHG_RSP_TIME:self.END_POS_TIME_CHG_RSP_TIME])) - vol = struct.unpack('i',bytearray( - message['message'][self.START_POS_TIME_CHG_RSP_UF_VOL:self.END_POS_TIME_CHG_RSP_UF_VOL])) - - self.DurationChangeSucceeded = rsp[0] - self.DurationChangeRejectReason = rea[0] - self.DurationChangeTimeMin = tim[0] - self.DurationChangeUFVolMl = vol[0] - - def handlerBloodAndDialysateChangeResponseFunction(self, message): - """ - Handler for response from HD regarding blood & dialysate flow rate change request. - - \param message: response message from HD regarding requested blood & dialysate flow rate settings change.\n - BOOL Accepted \n - U32 Reject reason (if not accepted) \n - U32 Blood flow rate (mL/min) \n - U32 Dialysate flow rate (mL/min) \n - - \returns none - """ - rsp = struct.unpack('i',bytearray( - message['message'][self.START_POS_BLD_DIAL_CHG_RSP_ACCEPTED:self.END_POS_BLD_DIAL_CHG_RSP_ACCEPTED])) - rea = struct.unpack('i',bytearray( - message['message'][self.START_POS_BLD_DIAL_CHG_RSP_REASON:self.END_POS_BLD_DIAL_CHG_RSP_REASON])) - bld = struct.unpack('i',bytearray( - message['message'][self.START_POS_BLD_DIAL_CHG_RSP_BLD_RATE:self.END_POS_BLD_DIAL_CHG_RSP_BLD_RATE])) - dil = struct.unpack('i',bytearray( - message['message'][self.START_POS_BLD_DIAL_CHG_RSP_DIAL_RATE:self.END_POS_BLD_DIAL_CHG_RSP_DIAL_RATE])) - - if rsp[0] == self.RESPONSE_REJECTED: - resp = False - else: - resp = True - self.BloodAndDialysateFlowRateChangeSucceeded = resp - self.BloodAndDialysateFlowRateChangeRejectReason = rea[0] - self.TargetBloodFlowRate = bld[0] - self.TargetDialysateFlowRate = dil[0] - - def handlerUFChangeResponseFunction(self, message): - """ - Handler for response from HD regarding UF change request. - - \param message: response message from HD regarding requested ultrafiltration settings change.\n - BOOL Accepted \n - U32 RejectReason (if not accepted) - F32 UF Volume (mL) - converted to Liters \n - U32 Treatment Time (min) \n - S32 Treatment Time Change (min) \n - F32 UF Rate (mL/min) \n - F32 UF Rate Change (mL/min) - - \returns none - """ - rsp = struct.unpack('i',bytearray( - message['message'][self.START_POS_UF_CHG_RSP_RESP:self.END_POS_UF_CHG_RSP_RESP])) - rea = struct.unpack('i',bytearray( - message['message'][self.START_POS_UF_CHG_RSP_REJECT_REASON:self.END_POS_UF_CHG_RSP_REJECT_REASON])) - vol = struct.unpack('f',bytearray( - message['message'][self.START_POS_UF_CHG_RSP_VOL:self.END_POS_UF_CHG_RSP_VOL])) - tim = struct.unpack('i',bytearray( - message['message'][self.START_POS_UF_CHG_RSP_TIME:self.END_POS_UF_CHG_RSP_TIME])) - tmd = struct.unpack('i',bytearray( - message['message'][self.START_POS_UF_CHG_RSP_TIME_DIFF:self.END_POS_UF_CHG_RSP_TIME_DIFF])) - rat = struct.unpack('f', bytearray( - message['message'][self.START_POS_UF_CHG_RSP_RATE:self.END_POS_UF_CHG_RSP_RATE])) - rtd = struct.unpack('f', bytearray( - message['message'][self.START_POS_UF_CHG_RSP_RATE_DIFF:self.END_POS_UF_CHG_RSP_RATE_DIFF])) - - if rsp[0] == self.RESPONSE_REJECTED: - resp = False - else: - resp = True - self.UFChangeSucceeded = resp - self.UFChangeRejectReason = rea[0] - self.UFChangeVolumeL = vol[0] / self.LITER_TO_ML_CONVERSION_FACTOR - self.UFChangeTimeMin = tim[0] - self.UFChangeTimeDiff = tmd[0] - self.UFChangeRateMlMin = rat[0] - self.UFChangeRateDifff = rtd[0] - - def CmdUICheckinWithHD(self): - """ - Constructs and sends the UI check-in message - - \returns none - """ - - payload = None - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_UI_CHECKIN_WITH_HD) - - print("Sending UI checkin w/ HD") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - def CmdUIUFPauseResume(self, cmd=UF_CMD_PAUSE): - """ - Constructs and sends a UI UF command message - - \param cmd: 0 for pause, 1 for resume - - \returns none - """ - - payload = self.outer_instance.integer2ByteArray(cmd) - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_HD_UF_PAUSE_RESUME_REQUEST, - payload=payload) - - if cmd == self.UF_CMD_PAUSE: - str_cmd = "pause" - else: - str_cmd = "resume" - print("Sending UF " + str_cmd + " command.") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - def CmdUIUFSettingsChangeRequest(self, vol=0.0): - """ - Constructs and sends a UI UF change settings command message - - \param vol (float): new ultrafiltration volume setting (in L) - - \returns none - """ - - # reset response to this command so we can tell when response is received - self.UFChangeResponse = None - # build command message - volume = self.outer_instance.float2ByteArray(vol * self.LITER_TO_ML_CONVERSION_FACTOR) - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_UF_SETTINGS_CHANGE_REQUEST_BY_USER, - payload=volume) - - print("Sending UF settings change request.") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - def CmdUIUFSettingsChangeConfirm(self, vol=0.0, adj=UF_CMD_CHANGE_TIME_TO_ADJUST): - """ - Constructs and sends a UI UF change settings command message - - \param vol (float): new ultrafiltration volume setting (in L) - \param adj (int): 0 for adjust time, 1 for adjust rate - - \returns none - """ - - # reset response to this command so we can tell when response is received - self.UFChangeResponse = None - # build command message - volume = self.outer_instance.float2ByteArray(vol * self.LITER_TO_ML_CONVERSION_FACTOR) - adjust = self.outer_instance.integer2ByteArray(adj) - payload = volume + adjust - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_UF_SETTINGS_CHANGE_REQUEST_BY_USER, - payload=payload) - - print("Sending UF settings change request.") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - def CmdUIUFChangeSettingsConfirmedByUser(self, response=RESPONSE_REJECTED, vol=0.0, tm=0, rate=0.0): - """ - Constructs and sends a UI UF change settings confirmed by user message - - \param response (int): 0 for rejected, 1 for confirmed - \param vol (float): volume (in L) that was confirmed - \param tm (int): treatment time (in min) that was confirmed - \param rate (float): ultrafiltration rate (in mL/min) that was confirmed - - \returns none - """ - - resp = self.outer_instance.integer2ByteArray(response) - volume = self.outer_instance.float2ByteArray(vol * self.LITER_TO_ML_CONVERSION_FACTOR) - min = self.outer_instance.integer2ByteArray(tm) - ufRate = self.outer_instance.float2ByteArray(rate) - payload = resp + volume + min + ufRate - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_UF_SETTINGS_CHANGE_CONFIRMED_BY_USER, - payload=payload) - - print("Sending UF settings change confirmation.") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - def CmdUITreatmentDurationSettingChangeRequest(self, timeMin=0): - """ - Constructs and sends a UI UF change settings confirmed by user message - - \param timeMin (int): treatment time (in min). - - \returns none - """ - - payload = self.outer_instance.integer2ByteArray(timeMin) - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_REQUEST, - payload=payload) - - print("Sending treatment duration setting change request.") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - def CmdUIBloodAndDialysateFlowSettingsChangeRequest(self, bloodFlow, dialFlow): - """ - Constructs and sends a UI blood & dialysate flow settings change request by user message - - \param bloodFlow (int): blood flow rate set point (in mL/min). - \param dialFlow (int): dialysate flow rate set point (in mL/min). - - \returns none - """ - - bld = self.outer_instance.integer2ByteArray(bloodFlow) - dial = self.outer_instance.integer2ByteArray(dialFlow) - payload = bld + dial - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_REQUEST_BY_USER, - payload=payload) - - print("Sending blood & dialysate flow rate settings change request.") - - # Send message - received_message = self.outer_instance.can_interface.send(message, 0) - - return 0 - - class HD_Watchdog: - """ - \class HD_Watchdog - - \brief Hemodialysis Device (HD) Dialin API sub-class for watchdog related commands. - """ - - # Watchdog message IDs - MSG_ID_HD_WD_CHECKIN_OVERRIDE = 0x8005 - - def __init__(self, outer_instance): - self.outer_instance = outer_instance - - """ - HD_Watchdog constructor - - \param outer_instance: reference to the HD (outer) class. - - \returns HD_Watchdog object. - """ - - def CmdWatchdogTaskCheckinOverride(self, reset, state, task): - """ - Constructs and sends the watchdog task check-in override command - - \param reset: integer - 1 to reset a previous override, 0 to override - \param state: integer - 1 for task checked in, 0 for task not checked in - \param task: integer - ID of task to override - \returns 1 if successful, zero otherwise - """ - - rst = self.outer_instance.integer2ByteArray(reset) - sta = self.outer_instance.integer2ByteArray(state) - tsk = self.outer_instance.integer2ByteArray(task) - payload = rst + sta + tsk - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_HD_WD_CHECKIN_OVERRIDE, - payload=payload) - - print("override watchdog task check-in state") - - # Send message - received_message = self.outer_instance.can_interface.send(message) - - # If there is content... - if received_message is not None: - #print(received_message) - if reset == HD.RESET: - str_res = "reset back to normal" - else: - str_res = ("checked in" if state != 0 else "not checked in") - print("Watchdog task check-in 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: - print("Timeout!!!!") - return False - - class HD_Alarms: - """ - \class HD_Alarms - - \brief Hemodialysis Device (HD) Dialin API sub-class for alarm related commands. - """ - - # Alarms message IDs - MSG_ID_HD_ALARMS_PUBLISHED_STATUS = 0x0002 - MSG_ID_HD_ALARM_ACTIVATE = 0x0003 - MSG_ID_HD_ALARM_CLEAR = 0x0004 - MSG_ID_HD_ALARM_LAMP_OVERRIDE = 0x8004 - MSG_ID_HD_ALARM_STATE_OVERRIDE = 0x8006 - MSG_ID_HD_ALARM_TIME_OVERRIDE = 0x8007 - - # Alarm lamp patterns - HD_ALARM_LAMP_PATTERN_OFF = 0 - HD_ALARM_LAMP_PATTERN_OK = 1 - HD_ALARM_LAMP_PATTERN_FAULT = 2 - HD_ALARM_LAMP_PATTERN_HIGH = 3 - HD_ALARM_LAMP_PATTERN_MEDIUM = 4 - HD_ALARM_LAMP_PATTERN_LOW = 5 - HD_ALARM_LAMP_PATTERN_MANUAL = 6 - - # Alarm status message field positions - START_POS_ALARM_STATE = DenaliMessage.PAYLOAD_START_INDEX - END_POS_ALARM_STATE = START_POS_ALARM_STATE + 4 - START_POS_ALARM_TOP = END_POS_ALARM_STATE - END_POS_ALARM_TOP = START_POS_ALARM_TOP + 4 - START_POS_ALARM_SILENCE_EXPIRES_IN = END_POS_ALARM_TOP - END_POS_ALARM_SILENCE_EXPIRES_IN = START_POS_ALARM_SILENCE_EXPIRES_IN + 4 - START_POS_ALARM_ESCALATES_IN = END_POS_ALARM_SILENCE_EXPIRES_IN - END_POS_ALARM_ESCALATES_IN = START_POS_ALARM_ESCALATES_IN + 4 - START_POS_ALARMS_FLAGS = END_POS_ALARM_ESCALATES_IN - END_POS_ALARMS_FLAGS = START_POS_ALARMS_FLAGS + 2 - - START_POS_ALARM_ID = DenaliMessage.PAYLOAD_START_INDEX - END_POS_ALARM_ID = START_POS_ALARM_ID + 2 - - def __init__(self, outer_instance, can_interface=None): - """ - HD_Alarms constructor - - \param outer_instance: reference to the HD (outer) class. - - \returns HD_Alarms object. - """ - self.outer_instance = outer_instance - - if can_interface is not None: - channel_id = DenaliChannels.hd_alarm_broadcast_ch_id - msg_id = self.MSG_ID_HD_ALARMS_PUBLISHED_STATUS - can_interface.register_receiving_publication_function(channel_id, msg_id, - self.handlerAlarmsStatusSyncFunction) - - channel_id = DenaliChannels.hd_alarm_broadcast_ch_id - msg_id = self.MSG_ID_HD_ALARM_ACTIVATE - can_interface.register_receiving_publication_function(channel_id, msg_id, - self.handlerAlarmActivateFunction) - channel_id = DenaliChannels.hd_alarm_broadcast_ch_id - msg_id = self.MSG_ID_HD_ALARM_CLEAR - can_interface.register_receiving_publication_function(channel_id, msg_id, - self.handlerAlarmClearFunction) - - # composite alarm status based on latest HD alarm status broadcast message - self.alarmsState = 0 - self.alarmTop = 0 - self.alarmsSilenceExpiresIn = 0 - self.alarmsEscalatesIn = 0 - self.alarmsFlags = 0 - - # alarm states based on received HD alarm activation and alarm clear messages - self.alarmStates = [False] * 50 -# for x in range(0,511): -# self.alarmStates.append(False) - - def handlerAlarmsStatusSyncFunction(self, message): - """ - Handles published alarms status messages. Alarms status data are captured - for reference. - - \param message: published blood flow data message - \returns none - """ - - self.alarmsState = int.from_bytes(bytearray( - message['message'][self.START_POS_ALARM_STATE:self.END_POS_ALARM_STATE]), - byteorder=DenaliMessage.BYTE_ORDER) - self.alarmTop = int.from_bytes(bytearray( - message['message'][self.START_POS_ALARM_TOP:self.END_POS_ALARM_TOP]), - byteorder=DenaliMessage.BYTE_ORDER) - self.alarmsSilenceExpiresIn = int.from_bytes(bytearray( - message['message'][self.START_POS_ALARM_SILENCE_EXPIRES_IN:self.END_POS_ALARM_SILENCE_EXPIRES_IN]), - byteorder=DenaliMessage.BYTE_ORDER) - self.alarmsEscalatesIn = int.from_bytes(bytearray( - message['message'][self.START_POS_ALARM_ESCALATES_IN:self.END_POS_ALARM_ESCALATES_IN]), - byteorder=DenaliMessage.BYTE_ORDER) - self.alarmsFlags = int.from_bytes(bytearray( - message['message'][self.START_POS_ALARMS_FLAGS:self.END_POS_ALARMS_FLAGS]), - byteorder=DenaliMessage.BYTE_ORDER) - - def handlerAlarmActivateFunction(self, message): - """ - Handles published HD alarm activation messages. - - \param message: published HD alarm activation message - \returns none - """ - - alarmID = struct.unpack('