Index: HemodialysisDevice.py =================================================================== diff -u -r680670f948d164932a6a68effa940bd8455e820e -r7e8f523c6ecb0a8fe78714e66507af7bc1e286cb --- HemodialysisDevice.py (.../HemodialysisDevice.py) (revision 680670f948d164932a6a68effa940bd8455e820e) +++ HemodialysisDevice.py (.../HemodialysisDevice.py) (revision 7e8f523c6ecb0a8fe78714e66507af7bc1e286cb) @@ -8,505 +8,2274 @@ # @file HemodialysisDevice.py # # @date 16-Oct-2019 -# @author L. Baloa +# @author S. Nash # -# @brief This class allows sending to and receiving from the HD device. +# @brief This class provides the basic interface to communicate with +# the HD board. # ############################################################################ -from DialityCoreCanProtocol import DialityCanMessenger -from DialityCoreCanProtocol import DialityPacket +from DialityCoreCanProtocol import DenaliCanMessenger +from DialityCoreCanProtocol import DenaliMessage +from DialityCoreCanProtocol import DenaliChannels from time import sleep from binascii import unhexlify +#from HD_DialOutFlow import HD_DialOut +#from HD_DialOutFlow import DialOutStates +import struct +import ctypes + class HD: - """ - \class HD + # for reset param in override commands + NO_RESET = 0 + RESET = 1 - \brief Hemodialysis Device (HD) Dialin object API. It provides the basic interface to communicate with - the HD board - - """ - def __init__(self, can__interface="can0"): """ HD constructor using can bus \param bus: can bus, e.g. "can0" - \returns HD object that allows communication with board via port + \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') """ # Create listener - self.__can_interface = DialityCanMessenger(can_interface=can__interface) - self.__can_interface.start() + self.can_interface = DenaliCanMessenger(can_interface=can__interface) + 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) - def login(self): + class HD__Basics: """ - send login command via serial port + \class HD__Basics - \returns 1 if logged in, 0 if log in failed + \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 - channel_id = 0x400 - request_id = 0x8000 - cargo = b'123' + \param outer_instance: reference to the HD (outer) class. - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + \returns HD__Basics object. + """ - print("login") + def CmdLogInToHD(self): + """ + Constructs and sends a login command via CAN bus. Login required before \n + other commands can be sent to the HD. - # Send message - received_message = self.__can_interface.send(channel_id, message) + \returns 1 if logged in, 0 if log in failed + """ - if received_message is not None: - print(received_message) - print("Logged In: " + str(received_message[4])) - return received_message[4] - else: - print("Timeout!!!!") - return False + message = DenaliMessage.buildMessage(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) - def HDMessageInsert(self, msg): - """ - sends the insert HD message command + 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 - \param msg: byte array - properly formatted HD message to insert - \returns 1 if successful, zero otherwise - """ + 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. - channel_id = 0x400 - request_id = 0x8001 - cargo = msg + \param msg: byte array - properly formatted HD message to insert - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + \returns 1 if successful, zero otherwise + """ - print("insert HD message") + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_MSG, + payload=msg) - # Send message - received_message = self.__can_interface.send(channel_id, message) + print("insert HD message") - # If there is content... - if received_message is not None: - print(received_message) - print("Inserted message: " + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + # 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. + """ - def OffButtonOverride(self, reset, state): + # 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 + """ - sends the Off button override command + HD_Buttons constructor + + \param outer_instance: reference to the HD (outer) class. - \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 + \returns HD_Buttons object. """ - channel_id = 0x400 - request_id = 0x8002 + def CmdOffButtonOverride(self, reset, state): + """ + Constructs and sends the Off button override command - rst = self.__integer2ByteArray(reset) - sta = self.__integer2ByteArray(state) - cargo = rst + sta + \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 + """ - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + rst = self.outer_instance.integer2ByteArray(reset) + sta = self.outer_instance.integer2ByteArray(state) + payload = rst + sta - print("override off button") + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_OFF_BUTTON_OVERRIDE, + payload=payload) - # Send message - received_message = self.__can_interface.send(channel_id, message) + print("override off button") - # If there is content... - if received_message is not None: - print(received_message) - print("Off button overridden to " + ("pressed" if state != 0 else "released") + ":" + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + # 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 StopButtonOverride(self, reset, state): - """ - sends the Stop button override command + 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 - """ + \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 + """ - channel_id = 0x400 - request_id = 0x8003 + rst = self.outer_instance.integer2ByteArray(reset) + sta = self.outer_instance.integer2ByteArray(state) + payload = rst + sta - rst = self.__integer2ByteArray(reset) - sta = self.__integer2ByteArray(state) - cargo = rst + sta + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_STOP_BUTTON_OVERRIDE, + payload=payload) - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + print("override stop button") - print("override stop button") + # Send message + received_message = self.outer_instance.can_interface.send(message) - # Send message - received_message = self.__can_interface.send(channel_id, 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 - # If there is content... - if received_message is not None: - print(received_message) - print("Stop button overridden to " + ("pressed" if state != 0 else "released") + ":" + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - 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 - def AlarmLampPatternOverride(self, reset, pattern): + def __init__(self, outer_instance): + self.outer_instance = outer_instance + """ - sends the alarm lamp pattern override command. + HD_UI constructor - \param reset: integer - 1 to reset a previous override, 0 to override - \param pattern: integer - ID of alarm lamp pattern to override with - \returns 1 if successful, zero otherwise + \param outer_instance: reference to the HD (outer) class. - \details Patterns: \n - 0 = off \n - 1 = ok \n - 2 = fault \n - 3 = high \n - 4 = medium \n - 5 = low \n - 6 = manual + \returns HD_UI object. """ - channel_id = 0x400 - request_id = 0x8004 + def CmdUICheckinWithHD(self): + """ + Constructs and sends the UI checkin message - rst = self.__integer2ByteArray(reset) - pat = self.__integer2ByteArray(pattern) - cargo = rst + pat + \returns none + """ - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + payload = None - print("override alarm lamp pattern") + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_UI_CHECKIN_WITH_HD) - # Send message - received_message = self.__can_interface.send(channel_id, message) + print("Sending UI checkin w/ HD") - # If there is content... - if received_message is not None: - print(received_message) - if pattern == 0: - str_pat = "off" - elif pattern == 1: - str_pat = "ok" - elif pattern == 2: - str_pat = "fault" - elif pattern == 3: - str_pat = "high" - elif pattern == 4: - str_pat = "medium" - elif pattern == 5: - str_pat = "low" - else: - str_pat = "manual" - print("Alarm lamp pattern overridden to " + str_pat + ":" + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + # Send message + received_message = self.outer_instance.can_interface.send(message, 0) + return 0 + class HD_Watchdog: + """ + \class HD_Watchdog - def WatchdogTaskCheckinOverride(self, reset, state, task): + \brief Hemodialysis Device (HD) Dialin API sub-class for watchdog related commands. """ - 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 + # Watchdog message IDs + MSG_ID_HD_WD_CHECKIN_OVERRIDE = 0x8005 + + def __init__(self, outer_instance): + self.outer_instance = outer_instance + """ + HD_Watchdog constructor - channel_id = 0x400 - request_id = 0x8005 + \param outer_instance: reference to the HD (outer) class. - rst = self.__integer2ByteArray(reset) - sta = self.__integer2ByteArray(state) - tsk = self.__integer2ByteArray(task) - cargo = rst + sta + tsk + \returns HD_Watchdog object. + """ - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + def CmdWatchdogTaskCheckinOverride(self, reset, state, task): + """ + Constructs and sends the watchdog task check-in override command - print("override watchdog task check-in state") + \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 + """ - # Send message - received_message = self.__can_interface.send(channel_id, message) + rst = self.outer_instance.integer2ByteArray(reset) + sta = self.outer_instance.integer2ByteArray(state) + tsk = self.outer_instance.integer2ByteArray(task) + payload = rst + sta + tsk - # If there is content... - if received_message is not None: - print(received_message) - print("Watchdog task check-in overridden to " + ("checked in" if state != 0 else "not checked in") + ":" + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + message = DenaliMessage.buildMessage(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) - def AlarmStateOverride(self, reset, state, alarm): + # 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: """ - sends the alarm state override command + \class HD_Alarms - \param reset: integer - 1 to reset a previous override, 0 to override - \param state: integer - 1 for alarm active, 0 for alarm inactive - \param alarm: integer - ID of alarm to override - \returns 1 if successful, zero otherwise + \brief Hemodialysis Device (HD) Dialin API sub-class for alarm related commands. """ - channel_id = 0x400 - request_id = 0x8006 + # Alarms message IDs + MSG_ID_HD_ALARMS_PUBLISHED_STATUS = 0x0002 + MSG_ID_HD_ALARM_LAMP_OVERRIDE = 0x8004 + MSG_ID_HD_ALARM_STATE_OVERRIDE = 0x8006 + MSG_ID_HD_ALARM_TIME_OVERRIDE = 0x8007 - rst = self.__integer2ByteArray(reset) - sta = self.__integer2ByteArray(state) - alm = self.__integer2ByteArray(alarm) - cargo = rst + sta + alm + # 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 - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + # 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 - print("override alarm state") + END_POS_ALARMS_FLAGS = START_POS_ALARMS_FLAGS + 2 - # Send message - received_message = self.__can_interface.send(channel_id, message) + def __init__(self, outer_instance, can_interface=None): + """ + HD_Alarms constructor - # If there is content... - if received_message is not None: - print(received_message) - print("Alarm state overridden to " + ("active" if state != 0 else "inactive") + ":" + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + \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.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerAlarmsStatusSyncFunction) - def AlarmTimeOverride(self, reset, time_ms, alarm): + self.alarmsState = 0 + self.alarmTop = 0 + self.alarmsSilenceExpiresIn = 0 + self.alarmsEscalatesIn = 0 + self.alarmsFlags = 0 + + 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 CmdAlarmStateOverride(self, reset, state, alarm): + """ + Constructs and sends the alarm state override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param state: integer - 1 for alarm active, 0 for alarm inactive + \param alarm: integer - ID of alarm to override + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + sta = self.outer_instance.integer2ByteArray(state) + alm = self.outer_instance.integer2ByteArray(alarm) + payload = rst + sta + alm + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_ALARM_STATE_OVERRIDE, + payload=payload) + + print("override alarm 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 = ("active" if state != 0 else "inactive") + print("Alarm state 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 CmdAlarmTimeOverride(self, reset, time_ms, alarm): + """ + Constructs and sends the alarm time override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param time_ms: integer - time (in ms) since alarm was activated + \param alarm: integer - ID of alarm to override + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + ms = self.outer_instance.integer2ByteArray(time_ms) + alm = self.outer_instance.integer2ByteArray(alarm) + payload = rst + ms + alm + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_ALARM_TIME_OVERRIDE, + payload=payload) + + print("override alarm time since activated") + + # 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 = str(time_ms) + print("Alarm time since activated overridden to " + str_res + " ms: " + 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 CmdAlarmLampPatternOverride(self, reset, pattern): + """ + Constructs and sends the alarm lamp pattern override command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param pattern: integer - ID of alarm lamp pattern to override with + \returns 1 if successful, zero otherwise + + \details Patterns: \n + 0 = off \n + 1 = ok \n + 2 = fault \n + 3 = high \n + 4 = medium \n + 5 = low \n + 6 = manual + """ + rst = self.outer_instance.integer2ByteArray(reset) + pat = self.outer_instance.integer2ByteArray(pattern) + payload = rst + pat + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.CmdAlarmLampPatternOverride(), + payload=payload) + + print("override alarm lamp pattern") + + # 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_pat = "reset back to normal" + elif pattern == self.HD_ALARM_LAMP_PATTERN_OFF: + str_pat = "off" + elif pattern == self.HD_ALARM_LAMP_PATTERN_OK: + str_pat = "ok" + elif pattern == self.HD_ALARM_LAMP_PATTERN_FAULT: + str_pat = "fault" + elif pattern == self.HD_ALARM_LAMP_PATTERN_HIGH: + str_pat = "high" + elif pattern == self.HD_ALARM_LAMP_PATTERN_MEDIUM: + str_pat = "medium" + elif pattern == self.HD_ALARM_LAMP_PATTERN_LOW: + str_pat = "low" + else: + str_pat = "manual" + print("Alarm lamp pattern overridden to " + str_pat + ":" + + 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_RTC: + + MSG_ID_SET_RTC_DATE_TIME = 0x801D + MSG_ID_RTC_EPOCH = 0x000A + + START_POS_SET_PT = DenaliMessage.PAYLOAD_START_INDEX + END_POS_SET_PT = START_POS_SET_PT + 4 + + def __init__(self, outer_instance, can_interface=None): + """ + HD_BloodFlow constructor + + \param outer_instance: reference to the HD (outer) class. + + \returns HD_BloodFlow object. + """ + self.outer_instance = outer_instance + + if can_interface is not None: + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = self.MSG_ID_RTC_EPOCH + can_interface.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerRTCEpoch) + self.RTCEpoch = 0 + + def handlerRTCEpoch(self, message): + """ + Publishes the RTC time in epoch + + \param message: published RTC epoch message + \returns none + """ + epoch = int.from_bytes(bytearray( + message['message'][self.START_POS_SET_PT:self.END_POS_SET_PT]), + byteorder=DenaliMessage.BYTE_ORDER) + self.RTCEpoch = ctypes.c_uint32(epoch) + + def CmdSetRTCTimeAndDate(self, secs, mins, hours, days, months, years): + """ + Constructs and sends the time and date to be written to RTC + + \returns 1 if successful, zero otherwise + """ + sec = bytes([secs]) + min = bytes([mins]) + hour = bytes([hours]) + day = bytes([days]) + month = bytes([months]) + year = self.outer_instance.integer2ByteArray(years) + payload = sec + min + hour + day + month + year + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_SET_RTC_DATE_TIME, + payload=payload) + + print("Setting time and date to RTC") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + print(received_message) + #str_res = str(flow) + print("Time and Date in RTC set to seconds: " + str(sec) + " minutes: " + str(min) + " hours: " + + str(hour) + " days: " + str(day) + " months: " + str(month) + " years: " + str(year) + + 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_BloodFlow: """ - sends the alarm time override command + \class HD_BloodFlow - \param reset: integer - 1 to reset a previous override, 0 to override - \param time_ms: integer - time (in ms) since alarm was activated - \param alarm: integer - ID of alarm to override - \returns 1 if successful, zero otherwise + \brief Hemodialysis Device (HD) Dialin API sub-class for blood-flow related commands. + """ - channel_id = 0x400 - request_id = 0x8007 + # BloodFlow message IDs + MSG_ID_HD_BLOOD_FLOW_PUBLISHED_DATA = 0x0005 + MSG_ID_HD_BLOOD_FLOW_SET_RATE_OVERRIDE = 0x8008 + MSG_ID_HD_BLOOD_FLOW_MEAS_RATE_OVERRIDE = 0x8009 + MSG_ID_HD_BLOOD_PUMP_MC_MEAS_SPEED_OVERRIDE = 0x800A + MSG_ID_HD_BLOOD_PUMP_MC_MEAS_CURRENT_OVERRIDE = 0x800B + MSG_ID_HD_BLOOD_FLOW_PUBLISH_INTERVAL_OVERRIDE = 0x800C + MSG_ID_HD_BLOOD_PUMP_MEAS_SPEED_OVERRIDE = 0x800E + MSG_ID_HD_BLOOD_PUMP_ROTOR_MEAS_SPEED_OVERRIDE = 0x800F - rst = self.__integer2ByteArray(reset) - ms = self.__integer2ByteArray(time_ms) - alm = self.__integer2ByteArray(alarm) - cargo = rst + ms + alm + # BloodFlow broadcast message field positions + START_POS_SET_PT = DenaliMessage.PAYLOAD_START_INDEX + END_POS_SET_PT = START_POS_SET_PT + 4 + START_POS_MEAS_FLOW = END_POS_SET_PT + END_POS_MEAS_FLOW = START_POS_MEAS_FLOW + 4 + START_POS_MEAS_ROT_SPEED = END_POS_MEAS_FLOW + END_POS_MEAS_ROT_SPEED = START_POS_MEAS_ROT_SPEED + 4 + START_POS_MEAS_SPEED = END_POS_MEAS_ROT_SPEED + END_POS_MEAS_SPEED = START_POS_MEAS_SPEED + 4 + START_POS_MEAS_MC_SPEED = END_POS_MEAS_SPEED + END_POS_MEAS_MC_SPEED = START_POS_MEAS_MC_SPEED + 4 + START_POS_MEAS_MC_CURR = END_POS_MEAS_MC_SPEED + END_POS_MEAS_MC_CURR = START_POS_MEAS_MC_CURR + 4 + START_POS_PWM_DC = END_POS_MEAS_MC_CURR + END_POS_PWM_DC = START_POS_PWM_DC + 4 - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + def __init__(self, outer_instance, can_interface=None): + """ + HD_BloodFlow constructor - print("override alarm time since activated") + \param outer_instance: reference to the HD (outer) class. - # Send message - received_message = self.__can_interface.send(channel_id, message) + \returns HD_BloodFlow object. + """ + self.outer_instance = outer_instance - # If there is content... - if received_message is not None: - print(received_message) - print("Alarm time since activated overridden to " + str(time_ms) + " ms: " + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + if can_interface is not None: + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = self.MSG_ID_HD_BLOOD_FLOW_PUBLISHED_DATA + can_interface.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerBloodFlowSyncFunction) + self.TargetBloodFlowRate = 0 + self.MeasuredBloodFlowRate = 0.0 + self.MeasuredBloodPumpRotorSpeed = 0.0 + self.MeasuredBloodPumpSpeed = 0.0 + self.MeasuredBloodPumpMCSpeed = 0.0 + self.MeasuredBloodPumpMCCurrent = 0.0 + self.PWMDutyCyclePct = 0.0 + def handlerBloodFlowSyncFunction(self, message): + """ + Handles published blood flow data messages. Blood flow data are captured + for reference. - def BloodFlowSetPointOverride(self, reset, flow): + \param message: published blood flow data message + \returns none + """ + + #self.TargetBloodFlowRate = int.from_bytes(bytearray( + # message['message'][self.START_POS_SET_PT:self.END_POS_SET_PT]), + # byteorder=DenaliMessage.BYTE_ORDER) + tgt = struct.unpack('i',bytearray( + message['message'][self.START_POS_SET_PT:self.END_POS_SET_PT])) + + flow = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_FLOW:self.END_POS_MEAS_FLOW])) + rotor = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_ROT_SPEED:self.END_POS_MEAS_ROT_SPEED])) + speed = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_SPEED:self.END_POS_MEAS_SPEED])) + mcspeed = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_MC_SPEED:self.END_POS_MEAS_MC_SPEED])) + mccurr = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_MC_CURR:self.END_POS_MEAS_MC_CURR])) + pwm = struct.unpack('f', bytearray( + message['message'][self.START_POS_PWM_DC:self.END_POS_PWM_DC])) + + self.TargetBloodFlowRate = tgt[0] + self.MeasuredBloodFlowRate = flow[0] + self.MeasuredBloodPumpRotorSpeed = rotor[0] + self.MeasuredBloodPumpSpeed = speed[0] + self.MeasuredBloodPumpMCSpeed = mcspeed[0] + self.MeasuredBloodPumpMCCurrent = mccurr[0] + self.PWMDutyCyclePct = pwm[0] + + def CmdBloodFlowSetPointOverride(self, reset, flow): + """ + Constructs and sends the blood flow set point override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param flow: integer - flow set point (in mL/min) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + flo = self.outer_instance.integer2ByteArray(flow) + payload = rst + flo + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_FLOW_SET_RATE_OVERRIDE, + payload=payload) + + print("override blood flow set point") + + # 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 = str(flow) + print( + "Blood flow set point overridden to " + str_res + " mL/min: " + + 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 CmdBloodFlowMeasuredOverride(self, reset, flow): + """ + Constructs and sends the measured blood flow override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param flow: integer - measured flow (in mL/min) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + flo = self.outer_instance.float2ByteArray(flow) + payload = rst + flo + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_FLOW_MEAS_RATE_OVERRIDE, + payload=payload) + + print("override measured blood flow") + + # 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 = str(flow) + print("Blood flow (measured)) overridden to " + str_res + " mL/min: " + + 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 CmdBloodPumpMCMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured blood pump motor controller speed \n + override command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: integer - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_PUMP_MC_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured blood pump motor controller speed") + + # 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 = str(speed) + print("Blood pump MC speed (measured) overridden to " + str_res + " RPM: " + + 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 CmdBloodPumpMeasuredCurrentOverride(self, reset, curr): + """ + Constructs and sends the measured blood pump motor current override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param curr: float - current (in mA) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + cur = self.outer_instance.float2ByteArray(curr) + payload = rst + cur + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_PUMP_MC_MEAS_CURRENT_OVERRIDE, + payload=payload) + + print("override measured blood pump motor controller current") + + # 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 = str(curr) + print("Blood pump MC current (measured) overridden to " + str_res + " mA: " + + 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 CmdBloodPumpMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured blood pump motor speed override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: integer - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_PUMP_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured blood pump speed") + + # 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 = str(speed) + print("Blood pump speed (measured) overridden to " + str_res + " RPM: " + + 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 CmdBloodPumpRotorMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured blood pump rotor speed override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: integer - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_PUMP_ROTOR_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured blood pump rotor speed") + + # 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 = str(speed) + print("Blood pump rotor speed (measured) overridden to " + str_res + " RPM: " + + 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 CmdBloodFlowBroadcastIntervalOverride(self, reset, ms): + """ + Constructs and sends the measured blood flow broadcast interval override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param ms: integer - interval (in ms) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + mis = self.outer_instance.integer2ByteArray(ms) + payload = rst + mis + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_FLOW_PUBLISH_INTERVAL_OVERRIDE, + payload=payload) + + print("override blood flow broadcast interval") + + # 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 = str(ms) + " ms: " + print("Blood flow 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: + print("Timeout!!!!") + return False + + class HD_DialysateInletFlow: """ - sends the blood flow set point override command + \class HD_DialysateInletFlow - \param reset: integer - 1 to reset a previous override, 0 to override - \param flow: integer - flow set point (in mL/min) to override with - \returns 1 if successful, zero otherwise + \brief Hemodialysis Device (HD) Dialin API sub-class for dialysate inlet pump related commands. + """ - channel_id = 0x400 - request_id = 0x8008 + # DialysateFlow message IDs + MSG_ID_HD_DIAL_IN_FLOW_PUBLISHED_DATA = 0x0008 + MSG_ID_HD_DIAL_IN_FLOW_SET_RATE_OVERRIDE = 0x8010 + MSG_ID_HD_DIAL_IN_FLOW_MEAS_RATE_OVERRIDE = 0x8011 + MSG_ID_HD_DIAL_IN_PUMP_MC_MEAS_SPEED_OVERRIDE = 0x8012 + MSG_ID_HD_DIAL_IN_PUMP_MC_MEAS_CURRENT_OVERRIDE = 0x8013 + MSG_ID_HD_DIAL_IN_FLOW_PUBLISH_INTERVAL_OVERRIDE = 0x8014 + MSG_ID_HD_DIAL_IN_PUMP_MEAS_SPEED_OVERRIDE = 0x8015 + MSG_ID_HD_DIAL_IN_PUMP_ROTOR_MEAS_SPEED_OVERRIDE = 0x8016 - rst = self.__integer2ByteArray(reset) - flo = self.__integer2ByteArray(flow) - cargo = rst + flo + # BloodFlow broadcast message field positions + START_POS_SET_PT = DenaliMessage.PAYLOAD_START_INDEX + END_POS_SET_PT = START_POS_SET_PT + 4 + START_POS_MEAS_FLOW = END_POS_SET_PT + END_POS_MEAS_FLOW = START_POS_MEAS_FLOW + 4 + START_POS_MEAS_ROT_SPEED = END_POS_MEAS_FLOW + END_POS_MEAS_ROT_SPEED = START_POS_MEAS_ROT_SPEED + 4 + START_POS_MEAS_SPEED = END_POS_MEAS_ROT_SPEED + END_POS_MEAS_SPEED = START_POS_MEAS_SPEED + 4 + START_POS_MEAS_MC_SPEED = END_POS_MEAS_SPEED + END_POS_MEAS_MC_SPEED = START_POS_MEAS_MC_SPEED + 4 + START_POS_MEAS_MC_CURR = END_POS_MEAS_MC_SPEED + END_POS_MEAS_MC_CURR = START_POS_MEAS_MC_CURR + 4 + START_POS_PWM_DC = END_POS_MEAS_MC_CURR + END_POS_PWM_DC = START_POS_PWM_DC + 4 - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + def __init__(self, outer_instance, can_interface=None): + """ + HD_DialysateFlow constructor - print("override blood flow set point") + \param outer_instance: reference to the HD (outer) class. - # Send message - received_message = self.__can_interface.send(channel_id, message) + \returns HD_DialysateFlow object. + """ + self.outer_instance = outer_instance - # If there is content... - if received_message is not None: - print(received_message) - print("Blood flow set point overridden to " + str(flow) + " mL/min: " + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + if can_interface is not None: + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = self.MSG_ID_HD_DIAL_IN_FLOW_PUBLISHED_DATA + can_interface.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerDialysateInletFlowSyncFunction) + self.TargetDialysateInletFlowRate = 0 + self.MeasuredDialysateInletFlowRate = 0.0 + self.MeasuredDialysateInletPumpRotorSpeed = 0.0 + self.MeasuredDialysateInletPumpSpeed = 0.0 + self.MeasuredDialysateInletPumpMCSpeed = 0.0 + self.MeasuredDialysateInletPumpMCCurrent = 0.0 + self.PWMDutyCyclePct = 0.0 + def handlerDialysateInletFlowSyncFunction(self, message): + """ + Handles published dialysate inlet flow data messages. Dialysate flow data are captured + for reference. - def BloodFlowMeasuredOverride(self, reset, flow): + \param message: published dialysate flow data message + \returns none + """ + + self.TargetDialysateInletFlowRate = int.from_bytes(bytearray( + message['message'][self.START_POS_SET_PT:self.END_POS_SET_PT]), + byteorder=DenaliMessage.BYTE_ORDER) + flow = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_FLOW:self.END_POS_MEAS_FLOW])) + rotor = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_ROT_SPEED:self.END_POS_MEAS_ROT_SPEED])) + speed = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_SPEED:self.END_POS_MEAS_SPEED])) + mcspeed = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_MC_SPEED:self.END_POS_MEAS_MC_SPEED])) + mccurr = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_MC_CURR:self.END_POS_MEAS_MC_CURR])) + pwm = struct.unpack('f', bytearray( + message['message'][self.START_POS_PWM_DC:self.END_POS_PWM_DC])) + + self.MeasuredDialysateInletFlowRate = flow[0] + self.MeasuredDialysateInletPumpRotorSpeed = rotor[0] + self.MeasuredDialysateInletPumpSpeed = speed[0] + self.MeasuredDialysateInletPumpMCSpeed = mcspeed[0] + self.MeasuredDialysateInletPumpMCCurrent = mccurr[0] + self.PWMDutyCyclePct = pwm[0] + + def CmdDialysateInletFlowSetPointOverride(self, reset, flow): + """ + Constructs and sends the dialysate flow set point override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param flow: integer - flow set point (in mL/min) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + flo = self.outer_instance.integer2ByteArray(flow) + payload = rst + flo + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_FLOW_SET_RATE_OVERRIDE, + payload=payload) + + print("override dialysate flow set point") + + # 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 = str(flow) + print( + "Dialysate flow set point overridden to " + str_res + " mL/min: " + + 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 CmdDialysateInletFlowMeasuredOverride(self, reset, flow): + """ + Constructs and sends the measured dialysate flow override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param flow: integer - measured flow (in mL/min) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + flo = self.outer_instance.float2ByteArray(flow) + payload = rst + flo + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_FLOW_MEAS_RATE_OVERRIDE, + payload=payload) + + print("override measured dialysate flow") + + # 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 = str(flow) + print("Dialysate flow (measured)) overridden to " + str_res + " mL/min: " + + 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 CmdDialysateInletPumpMCMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured dialysate inlet pump motor controller speed \n + override command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: integer - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_PUMP_MC_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured dialysate inlet pump motor controller speed") + + # 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 = str(speed) + print("Dialysate pump MC speed (measured) overridden to " + str_res + " RPM: " + + 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 CmdDialysateInletPumpMeasuredCurrentOverride(self, reset, curr): + """ + Constructs and sends the measured dialysate inlet pump motor current override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param curr: integer - current (in mA) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + cur = self.outer_instance.float2ByteArray(curr) + payload = rst + cur + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_PUMP_MC_MEAS_CURRENT_OVERRIDE, + payload=payload) + + print("override measured dialysate inlet pump motor controller current") + + # 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 = str(curr) + print("Dialysate inlet pump MC current (measured) overridden to " + str_res + " mA: " + + 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 CmdDialysateInletPumpMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured dialysate inlet pump motor speed override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: integer - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_PUMP_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured dialysate inlet pump speed") + + # 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 = str(speed) + print("Dialysate inlet pump speed (measured) overridden to " + str_res + " RPM: " + + 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 CmdDialysateInletPumpRotorMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured dialysate inlet pump rotor speed override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: integer - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_PUMP_ROTOR_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured dialysate inlet pump rotor speed") + + # 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 = str(speed) + print("Dialysate inlet pump rotor speed (measured) overridden to " + str_res + " RPM: " + + 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 CmdDialysateInletFlowBroadcastIntervalOverride(self, reset, ms): + """ + Constructs and sends the measured dialysate inlet flow broadcast interval override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param ms: integer - interval (in ms) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + mis = self.outer_instance.integer2ByteArray(ms) + payload = rst + mis + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_FLOW_PUBLISH_INTERVAL_OVERRIDE, + payload=payload) + + print("override dialysate inlet flow broadcast interval") + + # 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 = str(ms) + " ms: " + print("Dialysate inlet flow 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: + print("Timeout!!!!") + return False + + class HD_DialysateOutletFlow: """ - sends the measured blood flow override command + \class HD_DialysateOutletFlow - \param reset: integer - 1 to reset a previous override, 0 to override - \param flow: integer - measured flow (in mL/min) to override with - \returns 1 if successful, zero otherwise + \brief Hemodialysis Device (HD) Dialin API sub-class for dialysate outlet pump related commands. + """ - channel_id = 0x400 - request_id = 0x8009 + # DialysateFlow message IDs + MSG_ID_HD_DIAL_OUT_FLOW_PUBLISHED_DATA = 0x000B + MSG_ID_HD_LOAD_CELL_READINGS = 0x000C + MSG_ID_HD_DIAL_OUT_FLOW_PUBLISH_INTERVAL_OVERRIDE = 0x801E + MSG_ID_HD_DIAL_OUT_UF_REF_VOLUME_OVERRIDE = 0x801F + MSG_ID_HD_DIAL_OUT_UF_MEAS_VOLUME_OVERRIDE = 0x8020 + MSG_ID_HD_DIAL_OUT_PUMP_MC_MEAS_SPEED_OVERRIDE = 0x8021 + MSG_ID_HD_DIAL_OUT_PUMP_MC_MEAS_CURRENT_OVERRIDE = 0x8022 + MSG_ID_HD_DIAL_OUT_PUMP_MEAS_SPEED_OVERRIDE = 0x8023 + MSG_ID_HD_DIAL_OUT_PUMP_ROTOR_MEAS_SPEED_OVERRIDE = 0x8024 + MSG_ID_HD_DIAL_OUT_LOAD_CELL_WEIGHT_OVERRIDE = 0x8025 - rst = self.__integer2ByteArray(reset) - flo = self.__integer2ByteArray(flow) - cargo = rst + flo + # BloodFlow broadcast message field positions + START_POS_REF_VOL = DenaliMessage.PAYLOAD_START_INDEX + END_POS_REF_VOL = START_POS_REF_VOL + 4 + START_POS_MEAS_VOL = END_POS_REF_VOL + END_POS_MEAS_VOL = START_POS_MEAS_VOL + 4 + START_POS_MEAS_ROT_SPEED = END_POS_MEAS_VOL + END_POS_MEAS_ROT_SPEED = START_POS_MEAS_ROT_SPEED + 4 + START_POS_MEAS_SPEED = END_POS_MEAS_ROT_SPEED + END_POS_MEAS_SPEED = START_POS_MEAS_SPEED + 4 + START_POS_MEAS_MC_SPEED = END_POS_MEAS_SPEED + END_POS_MEAS_MC_SPEED = START_POS_MEAS_MC_SPEED + 4 + START_POS_MEAS_MC_CURR = END_POS_MEAS_MC_SPEED + END_POS_MEAS_MC_CURR = START_POS_MEAS_MC_CURR + 4 + START_POS_PWM_DC = END_POS_MEAS_MC_CURR + END_POS_PWM_DC = START_POS_PWM_DC + 4 - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + def __init__(self, outer_instance, can_interface=None): + """ + HD_DialysateFlow constructor - print("override measured blood flow") + \param outer_instance: reference to the HD (outer) class. - # Send message - received_message = self.__can_interface.send(channel_id, message) + \returns HD_DialysateFlow object. + """ + self.outer_instance = outer_instance - # If there is content... - if received_message is not None: - print(received_message) - print("Blood flow (measured)) overridden to " + str(flow) + " mL/min: " + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + if can_interface is not None: + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = self.MSG_ID_HD_DIAL_OUT_FLOW_PUBLISHED_DATA + can_interface.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerDialysateOutletFlowSyncFunction) + self.ReferenceDialysateOutletUFVolume = 0.0 + self.MeasuredDialysateOutletUFVolume = 0.0 + self.MeasuredDialysateOutletPumpRotorSpeed = 0.0 + self.MeasuredDialysateOutletPumpSpeed = 0.0 + self.MeasuredDialysateOutletPumpMCSpeed = 0.0 + self.MeasuredDialysateOutletPumpMCCurrent = 0.0 + self.PWMDutyCyclePct = 0.0 + def handlerDialysateOutletFlowSyncFunction(self, message): + """ + Handles published dialysate outlet flow data messages. Dialysate flow data are captured + for reference. - def BloodPumpMeasuredSpeedOverride(self, reset, speed): + \param message: published dialysate outlet flow data message + \returns none + """ + + refVol = struct.unpack('f', bytearray( + message['message'][self.START_POS_REF_VOL:self.END_POS_REF_VOL])) + measVol = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_VOL:self.END_POS_MEAS_VOL])) + rotor = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_ROT_SPEED:self.END_POS_MEAS_ROT_SPEED])) + speed = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_SPEED:self.END_POS_MEAS_SPEED])) + mcspeed = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_MC_SPEED:self.END_POS_MEAS_MC_SPEED])) + mccurr = struct.unpack('f', bytearray( + message['message'][self.START_POS_MEAS_MC_CURR:self.END_POS_MEAS_MC_CURR])) + pwm = struct.unpack('f', bytearray( + message['message'][self.START_POS_PWM_DC:self.END_POS_PWM_DC])) + + self.ReferenceDialysateOutletUFVolume = refVol[0] + self.MeasuredDialysateOutletUFVolume = measVol[0] + self.MeasuredDialysateOutletPumpRotorSpeed = rotor[0] + self.MeasuredDialysateOutletPumpSpeed = speed[0] + self.MeasuredDialysateOutletPumpMCSpeed = mcspeed[0] + self.MeasuredDialysateOutletPumpMCCurrent = mccurr[0] + self.PWMDutyCyclePct = pwm[0] + + def CmdDialysateOutletReferenceUFVolumeOverride(self, reset, refVol): + """ + Constructs and sends the UF reference volume override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param refVol: float - reference UF volume (in mL) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + vol = self.outer_instance.float2ByteArray(refVol) + payload = rst + vol + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_UF_REF_VOLUME_OVERRIDE, + payload=payload) + + print("override UF reference volume with " + str(refVol) + "mL.") + + # 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 = "overridden to " + str(refVol) + " mL. " + print( + "UF reference volume " + 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 CmdDialysateOutletMeasuredUFVolumeOverride(self, reset, measVol): + """ + Constructs and sends the measured UF volume override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param measVol: float - measured UF volume (in mL) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + vol = self.outer_instance.float2ByteArray(measVol) + payload = rst + vol + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_UF_MEAS_VOLUME_OVERRIDE, + payload=payload) + + print("override measured UF volume with " + str(measVol) + " mL.") + + # 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 = "overridden to " + str(measVol) + " mL. " + print("UF measured volume " + 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 CmdDialysateOutletPumpMCMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured dialysate outlet pump motor controller speed \n + override command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: float - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MeasuredDialysateOutletPumpMCSpeed, + payload=payload) + + print("override measured dialysate outlet pump motor controller speed to " + str(spd) + " RPM.") + + # 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 = "overridden to " + str(speed) + " RPM. " + print("Dialysate outlet pump MC speed (measured) " + 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 CmdDialysateOutletPumpMeasuredCurrentOverride(self, reset, curr): + """ + Constructs and sends the measured dialysate outlet pump motor current override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param curr: float - current (in mA) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + cur = self.outer_instance.float2ByteArray(curr) + payload = rst + cur + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_PUMP_MC_MEAS_CURRENT_OVERRIDE, + payload=payload) + + print("override measured dialysate outlet pump motor controller current") + + # 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 = "overridden to " + str(curr) + " mA. " + print("Dialysate outlet pump MC current (measured) " + 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 CmdDialysateOutletPumpMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured dialysate outlet pump motor speed override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: float - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_PUMP_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured dialysate outlet pump speed to " + str(speed) + " RPM.") + + # 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 = "overridden to " + str(speed) + " RPM. " + print("Dialysate outlet pump speed (measured) " + 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 CmdDialysateOutletPumpRotorMeasuredSpeedOverride(self, reset, speed): + """ + Constructs and sends the measured dialysate outlet pump rotor speed override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param speed: float - speed (in RPM) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(speed) + payload = rst + spd + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_PUMP_ROTOR_MEAS_SPEED_OVERRIDE, + payload=payload) + + print("override measured dialysate outlet pump rotor speed to " + str(speed) + " RPM.") + + # 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 = "overridden to " + str(speed) + " RPM. " + print("Dialysate outlet pump rotor speed (measured) " + 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 CmdDialysateOutletFlowBroadcastIntervalOverride(self, reset, ms): + """ + Constructs and sends the measured dialysate outlet flow broadcast interval override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param ms: integer - interval (in ms) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + mis = self.outer_instance.integer2ByteArray(ms) + payload = rst + mis + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_FLOW_PUBLISH_INTERVAL_OVERRIDE, + payload=payload) + + print("override dialysate outlet flow broadcast interval to " + str(ms) + " ms.") + + # 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 = "overridden to " + str(ms) + " ms. " + print("Dialysate outlet flow broadcast interval " + 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 CmdDialysateOutletPumpLoadCellWeightOverride(self, reset, weight, sensor): + """ + Constructs and sends the measured load cell weight override command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param weight: float - weight (in g) to override with + \param sensor: integer - ID of load cell to override + \returns 1 if successful, zero otherwise + + \details Load Cells: \n + 0 = reservoir 1 primary \n + 1 = reservoir 1 backup \n + 2 = reservoir 2 primary \n + 3 = reservoir 2 backup \n + """ + + rst = self.outer_instance.integer2ByteArray(reset) + spd = self.outer_instance.float2ByteArray(weight) + sen = self.outer_instance.integer2ByteArray(sensor) + payload = rst + spd + sen + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_LOAD_CELL_WEIGHT_OVERRIDE, + payload=payload) + + print("override measured load cell weight to " + str(weight) + " grams for load cell # " + str(sensor)) + + # 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 = "overridden to " + str(weight) + " grams. " + print("Load cell # " + str(sensor) + " " + 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 CmdSetLoadCellWeights(self, r1p, r1b, r2p, r2b): + """ + Constructs and sends the set load cell weights command. + + \param r1p: float - weight (in g) for reservoir 1 primary load cell + \param r1b: float - weight (in g) for reservoir 1 backup load cell + \param r2p: float - weight (in g) for reservoir 2 primary load cell + \param r2b: float - weight (in g) for reservoir 2 backup load cell + \returns 0 - no response will come from HD for this message + """ + + r1pb = self.outer_instance.float2ByteArray(r1p) + r1bb = self.outer_instance.float2ByteArray(r1b) + r2pb = self.outer_instance.float2ByteArray(r2p) + r2bb = self.outer_instance.float2ByteArray(r2b) + payload = r1pb + r1bb + r2pb + r2bb + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_LOAD_CELL_READINGS, + payload=payload) + + print("measured load cell weights set.") + + # Send message + received_message = self.outer_instance.can_interface.send(message, 0) + + return 0 + + class HD_Treatment: """ - sends the measured blood pump speed override command + \class HD_Treatment - \param reset: integer - 1 to reset a previous override, 0 to override - \param speed: integer - speed (in RPM) to override with - \returns 1 if successful, zero otherwise + \brief Hemodialysis Device (HD) Dialin API sub-class for treatment related commands. + """ - channel_id = 0x400 - request_id = 0x800A + # BloodFlow message IDs + MSG_ID_HD_TREATMENT_TIME_PUBLISHED_DATA = 0x000D - rst = self.__integer2ByteArray(reset) - spd = self.__integer2ByteArray(speed) - cargo = rst + spd + # BloodFlow broadcast message field positions + START_POS_TIME_PRES = DenaliMessage.PAYLOAD_START_INDEX + END_POS_TIME_PRES = START_POS_TIME_PRES + 4 + START_POS_TIME_ELAPSED = END_POS_TIME_PRES + END_POS_TIME_ELAPSED = START_POS_TIME_ELAPSED + 4 + START_POS_TIME_REMAINING = END_POS_TIME_ELAPSED + END_POS_TIME_REMAINING = START_POS_TIME_REMAINING + 4 - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + def __init__(self, outer_instance, can_interface=None): + """ + HD_Treatment constructor - print("override measured blood pump speed") + \param outer_instance: reference to the HD (outer) class. - # Send message - received_message = self.__can_interface.send(channel_id, message) + \returns HD_Treatment object. + """ + self.outer_instance = outer_instance - # If there is content... - if received_message is not None: - print(received_message) - print("Blood pump speed (measured) overridden to " + str(speed) + " RPM: " + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + if can_interface is not None: + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = self.MSG_ID_HD_TREATMENT_TIME_PUBLISHED_DATA + can_interface.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerTreatmentTimeSyncFunction) + self.TreatmentTimePrescribed = 0 + self.TreatmentTimeElapsed = 0 + self.TreatmentTimeRemaining = 0 + def handlerTreatmentTimeSyncFunction(self, message): + """ + Handles published treatment time data messages. Treatment time data are captured + for reference. - def BloodPumpMeasuredCurrentOverride(self, reset, curr): + \param message: published treatment time data message + \returns none + """ + + tot = struct.unpack('i',bytearray( + message['message'][self.START_POS_TIME_PRES:self.END_POS_TIME_PRES])) + ela = struct.unpack('i',bytearray( + message['message'][self.START_POS_TIME_ELAPSED:self.END_POS_TIME_ELAPSED])) + rem = struct.unpack('i',bytearray( + message['message'][self.START_POS_TIME_REMAINING:self.END_POS_TIME_REMAINING])) + + self.TreatmentTimePrescribed = tot[0] + self.TreatmentTimeElapsed = ela[0] + self.TreatmentTimeRemaining = rem[0] + + class HD_Pressure_Occlusion: """ - sends the measured blood pump motor current override command + \class HD_Pressure_Occlusion - \param reset: integer - 1 to reset a previous override, 0 to override - \param curr: integer - current (in mA) to override with - \returns 1 if successful, zero otherwise + \brief Hemodialysis Device (HD) Dialin API sub-class for pressure related commands. + """ - channel_id = 0x400 - request_id = 0x800B + # Pressure/Occlusion message IDs + MSG_ID_HD_PRESSURE_OCCLUSION_DATA = 0x0009 + MSG_ID_HD_PRESSURE_ARTERIAL_OVERRIDE = 0x8017 + MSG_ID_HD_PRESSURE_VENOUS_OVERRIDE = 0x8018 + MSG_ID_HD_OCCLUSION_BLOOD_PUMP_OVERRIDE = 0x8019 + MSG_ID_HD_OCCLUSION_DIAL_IN_PUMP_OVERRIDE = 0x801A + MSG_ID_HD_OCCLUSION_DIAL_OUT_PUMP_OVERRIDE = 0x801B + MSG_ID_HD_PRES_OCCL_SEND_INTERVAL_OVERRIDE = 0x801C - rst = self.__integer2ByteArray(reset) - cur = self.__integer2ByteArray(curr) - cargo = rst + cur + # Pressure/Occlusion broadcast message field positions + START_POS_ART_PRES = DenaliMessage.PAYLOAD_START_INDEX + END_POS_ART_PRES = START_POS_ART_PRES + 4 + START_POS_VEN_PRES = END_POS_ART_PRES + END_POS_VEN_PRES = START_POS_VEN_PRES + 4 + START_POS_BP_OCCL = END_POS_VEN_PRES + END_POS_BP_OCCL = START_POS_BP_OCCL + 4 + START_POS_DIP_OCCL = END_POS_BP_OCCL + END_POS_DIP_OCCL = START_POS_DIP_OCCL + 4 + START_POS_DOP_OCCL = END_POS_DIP_OCCL + END_POS_DOP_OCCL = START_POS_DOP_OCCL + 4 - message = DialityPacket.buildPacket(request_id=request_id, cargo=cargo) + def __init__(self, outer_instance, can_interface=None): + """ + HD_Pressure_Occlusion constructor - print("override measured blood pump motor current") + \param outer_instance: reference to the HD (outer) class. - # Send message - received_message = self.__can_interface.send(channel_id, message) + \returns HD_Pressure_Occlusion object. + """ + self.outer_instance = outer_instance - # If there is content... - if received_message is not None: - print(received_message) - print("Blood pump current (measured) overridden to " + str(curr) + " mA: " + str(received_message[4])) - # 5th element is OK or not OK - return received_message[4] - else: - print("Timeout!!!!") - return False + if can_interface is not None: + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = self.MSG_ID_HD_PRESSURE_OCCLUSION_DATA + can_interface.registerReceivingPublicationFunction(channel_id, msg_id, + self.handlerPresOcclSyncFunction) + self.arterialPressure = 0 + self.venousPressure = 0.0 + self.bloodPumpOcclusion = 0.0 + self.dialysateInletPumpOcclusion = 0.0 + self.dialysateOutletPumpOcclusion = 0.0 + def handlerPresOcclSyncFunction(self, message): + """ + Handles published pressure & occlusion data messages. Pressure data are captured + for reference. - def __integer2ByteArray(self, val): - fmt = '%08x' # integer to hex string formatter - b = unhexlify(fmt % val) # convert reset int to byte array - b = b[::-1] # little endian byte order + \param message: published pressure & occlusion data message + \returns none + """ + + art = struct.unpack('f', bytearray( + message['message'][self.START_POS_ART_PRES:self.END_POS_ART_PRES])) + ven = struct.unpack('f', bytearray( + message['message'][self.START_POS_VEN_PRES:self.END_POS_VEN_PRES])) + bp = struct.unpack('f', bytearray( + message['message'][self.START_POS_BP_OCCL:self.END_POS_BP_OCCL])) + dpi = struct.unpack('f', bytearray( + message['message'][self.START_POS_DIP_OCCL:self.END_POS_DIP_OCCL])) + dpo = struct.unpack('f', bytearray( + message['message'][self.START_POS_DOP_OCCL:self.END_POS_DOP_OCCL])) + + self.arterialPressure = art[0] + self.venousPressure = ven[0] + self.bloodPumpOcclusion = bp[0] + self.dialysateInletPumpOcclusion = dpi[0] + self.dialysateOutletPumpOcclusion = dpo[0] + + def CmdArterialPressureMeasuredOverride(self, reset, pres): + """ + Constructs and sends the measured arterial pressure override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param pres: float - measured arterial pressure (in mmHg) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + prs = self.outer_instance.float2ByteArray(pres) + payload = rst + prs + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_PRESSURE_ARTERIAL_OVERRIDE, + payload=payload) + + print("override measured arterial pressure") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == HD.RESET: + str_res = "reset back to normal. " + else: + str_res = str(pres) + " mmHg. " + print("Arterial 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: + print("Timeout!!!!") + return False + + def CmdVenousPressureMeasuredOverride(self, reset, pres): + """ + Constructs and sends the measured venous pressure \n + override command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param pres: float - venous pressure (in mmHg) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + prs = self.outer_instance.float2ByteArray(pres) + payload = rst + prs + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_PRESSURE_VENOUS_OVERRIDE, + payload=payload) + + print("override measured venous pressure") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == HD.RESET: + str_res = "reset back to normal. " + else: + str_res = str(pres) + " mmHg. " + print("Venous 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: + print("Timeout!!!!") + return False + + def CmdBloodPumpMeasuredOcclusionOverride(self, reset, occl): + """ + Constructs and sends the measured blood pump occlusion pressure override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param occl: float - pressure (in mmHg) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + occ = self.outer_instance.float2ByteArray(occl) + payload = rst + occ + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_OCCLUSION_BLOOD_PUMP_OVERRIDE, + payload=payload) + + print("override measured blood pump occlusion pressure") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == HD.RESET: + str_res = "reset back to normal. " + else: + str_res = str(occl) + " mmHg. " + print("Blood 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: + print("Timeout!!!!") + return False + + def CmdDialysateInletPumpMeasuredOcclusionOverride(self, reset, occl): + """ + Constructs and sends the measured dialysate inlet pump occlusion pressure override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param occl: float - pressure (in mmHg) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + occ = self.outer_instance.float2ByteArray(occl) + payload = rst + occ + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_OCCLUSION_DIAL_IN_PUMP_OVERRIDE, + payload=payload) + + print("override measured dialysate inlet pump occlusion pressure") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == HD.RESET: + str_res = "reset back to normal. " + else: + str_res = str(occl) + " mmHg. " + print("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: + print("Timeout!!!!") + return False + + def CmdDialysateOutletPumpMeasuredOcclusionOverride(self, reset, occl): + """ + Constructs and sends the measured dialysate outlet pump occlusion pressure override \n + command. + + \param reset: integer - 1 to reset a previous override, 0 to override + \param occl: float - pressure (in mmHg) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + occ = self.outer_instance.float2ByteArray(occl) + payload = rst + occ + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_OCCLUSION_DIAL_OUT_PUMP_OVERRIDE, + payload=payload) + + print("override measured dialysate outlet pump occlusion pressure") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == HD.RESET: + str_res = "reset back to normal. " + else: + str_res = str(occl) + " mmHg. " + print("Dialysate outlet 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: + print("Timeout!!!!") + return False + + def CmdPressureOcclusionBroadcastIntervalOverride(self, reset, ms): + """ + Constructs and sends the pressure/occlusion broadcast interval override command + + \param reset: integer - 1 to reset a previous override, 0 to override + \param ms: integer - interval (in ms) to override with + \returns 1 if successful, zero otherwise + """ + + rst = self.outer_instance.integer2ByteArray(reset) + mis = self.outer_instance.integer2ByteArray(ms) + payload = rst + mis + + message = DenaliMessage.buildMessage(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_PRES_OCCL_SEND_INTERVAL_OVERRIDE, + payload=payload) + + print("override pressure/occlusion broadcast interval") + + # Send message + received_message = self.outer_instance.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == HD.RESET: + str_res = "reset back to normal: " + else: + str_res = str(ms) + " ms: " + print("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: + print("Timeout!!!!") + return False + + def integer2ByteArray(self, val): + """ + Converts an integer value into a byte array (little endian) + + \param val: integer to convert to byte array + \returns byte array + """ + fmt = '%08x' # integer to hex string formatter + b = unhexlify(fmt % val) # convert int to byte array + b = b[::-1] # little endian byte order return b + def float2ByteArray(self, val): + """ + Converts a float value into a byte array (little endian) + \param val: float to convert to byte array + \returns byte array + """ + #s = hex(struct.unpack(' After STOP:{} <---".format(hd.DialOut.DialOutBroadcast)) + + sleep(3) + hd.DialOut.setUFState(DialOutStates.RUN) + sleep(2) + state_stop = hd.DialOut.DialOutBroadcast['state'] + print("After RUN again: {}".format(hd.DialOut.DialOutBroadcast)) + + sleep(3) + hd.DialOut.setUFState(DialOutStates.STOP) + sleep(2) + state_stop = hd.DialOut.DialOutBroadcast['state'] + print("After STOP: {}".format(hd.DialOut.DialOutBroadcast)) + + sleep(5) + hd.DialOut.plotBroadCastSignals() + + exit() + tgtRate = 0 + hd.BloodFlow.CmdBloodFlowBroadcastIntervalOverride(hd.NO_RESET, 2000) + + while True: + if hd.BloodFlow.TargetBloodFlowRate == 0: + if tgtRate != 0: + hd.BloodFlow.CmdBloodFlowBroadcastIntervalOverride(hd.NO_RESET, 2000) + tgtRate = 0 + else: + if tgtRate == 0: + hd.BloodFlow.CmdBloodFlowBroadcastIntervalOverride(hd.NO_RESET, 200) + tgtRate = hd.BloodFlow.TargetBloodFlowRate + + sleep(1) + + print(hd.BloodFlow.MeasuredBloodFlowRate) + +# hd.BloodFlow.CmdBloodFlowBroadcastIntervalOverride(hd.RESET,0) +"""