Index: DialityCoreCanProtocol.py =================================================================== diff -u -rc172ebf4d713b654fb5a75113c96bf7f5ef5d858 -r519260ce5bc660d7d44c612848412ea4e50ca662 --- DialityCoreCanProtocol.py (.../DialityCoreCanProtocol.py) (revision c172ebf4d713b654fb5a75113c96bf7f5ef5d858) +++ DialityCoreCanProtocol.py (.../DialityCoreCanProtocol.py) (revision 519260ce5bc660d7d44c612848412ea4e50ca662) @@ -19,7 +19,7 @@ import can import math from time import sleep -from ctypes import c_int8 +import sys class DenaliMessage: @@ -65,9 +65,9 @@ def buildBasicMessage(channel_id=0, message=None): """ build a message using channel_id and dialin message - :param channel_id: integer with channel id - :param message: array of int with message - :return: dictionary with channel_id and message keys + \param channel_id: integer with channel id + \param message: array of int with message + \return: dictionary with channel_id and message keys """ if message is None: message = [] @@ -78,23 +78,24 @@ """ buildPacket builds a Diality Packet - :param channel_id: is an integer with channel ID - :param message_id: is an integer indicating request ID - :param payload: list with payload. It does not include length + \param channel_id: is an integer with channel ID + \param message_id: is an integer indicating request ID + \param payload: list with payload. It does not include length - :return dictionary with channel_id and 8-byte padded message + \return dictionary with channel_id and 8-byte padded message """ if payload is None: payload = [] - #message = [DenaliCanMessenger.START_BYTE] + message_list = [DenaliCanMessenger.START_BYTE] + if 0 <= message_id <= DenaliMessage.MAX_MSG_ID_NUMBER: # Make sure an unsigned int was passed message_id_in_bytes = message_id.to_bytes(2, byteorder=DenaliMessage.BYTE_ORDER) - message = [message_id_in_bytes[0]] - message += [message_id_in_bytes[1]] + message_list += [message_id_in_bytes[0]] + message_list += [message_id_in_bytes[1]] else: @@ -106,37 +107,41 @@ # if payload is larger than 255 return nothing if payload_length <= DenaliMessage.MAX_NUMBER_OF_PAYLOAD_BYTES: # payload has to be a list - message += [payload_length] + message_list += [payload_length] else: return [] - message += payload + message_list += payload - message += DenaliMessage.crc8(message) + # Because CRC does not include first byte, then we pass a list with out it + message_list += [DenaliMessage.crc8(message_list[1:])] - message = [DenaliCanMessenger.START_BYTE] + message + message_list = DenaliMessage.__padMessageWithZeros(message_list) - message = DenaliMessage.__padMessageWithZeros(message) + return DenaliMessage.buildBasicMessage(channel_id=channel_id, message=message_list) - return DenaliMessage.buildBasicMessage(channel_id=channel_id, message=message) @staticmethod - def crc8(message): + def crc8(message_list): + """ + returns the calculated crc from a message list + \param message_list: is a list of integer numbers containing the message + \return: integer containing a unsigned byte + """ crc = 0 - - for byte in message: - unsigned_byte = int(byte) ^ crc + for byte in message_list: + unsigned_byte = byte ^ crc crc = DenaliMessage.CRC_LIST[unsigned_byte] - return bytes(c_int8(crc)) + return crc @staticmethod def __padMessageWithZeros(message): """ returns a packet padded with zeros that guarantees that the packet is a multiple of 8 bytes. - :param message: packet that may or may not be multiple of 8 bytes + \param message: packet that may or may not be multiple of 8 bytes - :return: packet that is 8-byte multiple + \return: packet that is 8-byte multiple """ message_length = len(message) @@ -152,12 +157,45 @@ return message @staticmethod + def getCRC(message): + """ + gets the CRC in message + + \param message: Dialin complete message with CRC + + \return: CRC in message + """ + + crc_index = DenaliMessage.PAYLOAD_START_INDEX + DenaliMessage.getPayloadLength(message) + + return message['message'][crc_index] + + @staticmethod + def verifyCRC(message): + """ + verifies message CRC equals calculated message CRC + + \return: TRUE if CRC matches, FALSE otherwise + """ + + if message is None: + return False + else: + message_list = message['message'] + + message_length = DenaliMessage.PAYLOAD_START_INDEX + DenaliMessage.getPayloadLength(message) + calculated_crc = DenaliMessage.crc8(message_list[1:message_length]) + actual_crc = DenaliMessage.getCRC(message) + + return calculated_crc == actual_crc + + @staticmethod def getChannelID(message): """ returns request ID from message - :param message: dictionary with channel_id and message keys - :return: integer with channel id + \param message: dictionary with channel_id and message keys + \return: integer with channel id """ @@ -168,9 +206,9 @@ """ returns request ID from packet - :param message: complete Diality Packet + \param message: complete Diality Packet - :return: integer with request ID + \return: integer with request ID """ msg_id_array = message['message'][DenaliMessage.MSG_ID_INDEX: @@ -182,18 +220,17 @@ def getPayloadLength(message): """ returns payload length from message - :param message: dictionary with channel_id and message keys - :return: a unsigned payload length + \param message: dictionary with channel_id and message keys + \return: a unsigned payload length """ - return message['message'][DenaliMessage.PAYLOAD_LENGTH_INDEX] @staticmethod def getPayload(message): """ returns payload array from message - :param message: dictionary with channel_id and message keys - :return: a payload array if exist + \param message: dictionary with channel_id and message keys + \return: a payload array if exist """ payload_length = DenaliMessage.getPayloadLength(message) @@ -207,15 +244,15 @@ def getTotalPackets(message, is_array=True): """ returns the number of packets needed to transmit Denali Message - :param message: dictionary with channel_id and message keys or raw message - :param is_array: True if message is an array and not a dictionary - :return: number of packets + \param message: dictionary with channel_id and message keys or raw message + \param is_array: True if message is an array and not a dictionary + \return: number of packets """ the_message = message if is_array else message['message'] return math.ceil((the_message[DenaliMessage.PAYLOAD_LENGTH_INDEX] + - DenaliMessage.HEADER_LENGTH + DenaliMessage.CRC_LENGTH) / - DenaliMessage.PACKET_LENGTH) + DenaliMessage.HEADER_LENGTH + DenaliMessage.CRC_LENGTH) / + DenaliMessage.PACKET_LENGTH) class DenaliChannels: @@ -254,9 +291,9 @@ message = obj.push(msg3), return the packet which is the concatenation of msg1, msg2 and msg3 - :param can_message: an 8 byte message needed to build a diality message. + \param can_message: an 8 byte message needed to build a diality message. - :returns object + \returns object """ self.__message = can_message @@ -267,11 +304,11 @@ """ push appends the can_message to the current packet. - :param can_message: 8-byte message + \param can_message: 8-byte message - :param first_packet: True if it is the first packet received + \param first_packet: True if it is the first packet received - :return: None if the packet is not completed, otherwise returns the complete packet + \return: None if the packet is not completed, otherwise returns the complete packet """ if first_packet: self.__message = can_message @@ -298,9 +335,9 @@ """ DenaliCanMessenger constructor - :param can_interface - string containing the can interface, e.g., 'can0" + \param can_interface - string containing the can interface, e.g., 'can0" - :returns DialityCanMessenger object + \returns DialityCanMessenger object """ self.__bus = can.interfaces.socketcan.SocketcanBus(channel=can_interface) @@ -313,7 +350,7 @@ self.__longMessageBuilders = {} self.__longMsgChannelIDSet = set() - self.__dialinMessage = None + self.__dialinMessageList = None self.__dialinCommandResponseMessage = None self.__dialinResponseChannelID = -1 @@ -368,12 +405,12 @@ # if we are building a long message, then proceed to push it to the channel dictionary if channel_id in self.__longMsgChannelIDSet: - self.__dialinMessage = self.__longMessageBuilders[channel_id].push(can_data) + self.__dialinMessageList = self.__longMessageBuilders[channel_id].push(can_data) elif can_data[0] == self.START_BYTE and \ message_length <= DenaliMessage.PAYLOAD_LENGTH_FIRST_PACKET: # This is a short packet # This is the first time that we are building a message - self.__dialinMessage = can_data # deliver the packet + self.__dialinMessageList = can_data # deliver the packet elif can_data[0] == self.START_BYTE and \ message_length > DenaliMessage.PAYLOAD_LENGTH_FIRST_PACKET: # Long packet start @@ -382,40 +419,48 @@ if channel_id not in self.__longMessageBuilders.keys(): # if we don't have a builder. Create it! self.__longMessageBuilders[channel_id] = LongDenaliMessageBuilder(can_data) - self.__dialinMessage = None + self.__dialinMessageList = None else: # if we do have a builder. This is the first time - self.__dialinMessage = self.__longMessageBuilders[channel_id].push(can_data, first_packet=True) + self.__dialinMessageList = self.__longMessageBuilders[channel_id].push(can_data, first_packet=True) - # Need to verify CRC at this point - # At this point we have a complete (long or short) Diality Packet - if self.__dialinMessage is not None: - completeDialinMessage = DenaliMessage.buildBasicMessage(channel_id=channel_id, - message=self.__dialinMessage) - dialin_msg_id = DenaliMessage.getMessageID(completeDialinMessage) - dialin_ch_id = DenaliMessage.getChannelID(completeDialinMessage) + if self.__dialinMessageList is not None: + complete_dialin_message = DenaliMessage.buildBasicMessage(channel_id=channel_id, + message=self.__dialinMessageList) + dialin_msg_id = DenaliMessage.getMessageID(complete_dialin_message) + dialin_ch_id = DenaliMessage.getChannelID(complete_dialin_message) + # Need to verify CRC at this point + + if DenaliMessage.verifyCRC(complete_dialin_message) is False: + # if verify is False, let's drop (ignore) this message + dialin_ch_id = None + dialin_msg_id = None + sys.stderr.write("Incorrect CRC, received message: {}, crc: {}, calculated crc: {}\n".format( + self.__dialinMessageList, DenaliMessage.getCRC(complete_dialin_message), + DenaliMessage.crc8(self.__dialinMessageList))) + if dialin_ch_id in self.__longMsgChannelIDSet: # We need to remove channel ID from the long message set self.__longMsgChannelIDSet.remove(dialin_ch_id) # We first check if this is a response to a send request that is pending if dialin_msg_id == self.__sendPacketRequestID: - self.__dialinCommandResponseMessage = completeDialinMessage + self.__dialinCommandResponseMessage = complete_dialin_message self.__sendEvent.set() self.__sendPacketRequestID = -1 # If it is not, this is a publication message and we need to call it's register function elif dialin_ch_id in self.__sync_response_dictionary.keys() and \ dialin_msg_id in self.__sync_response_dictionary[channel_id].keys(): - self.__sync_response_dictionary[dialin_ch_id][dialin_msg_id](completeDialinMessage) + self.__sync_response_dictionary[dialin_ch_id][dialin_msg_id](complete_dialin_message) # Done with this message, let's get the next one - self.__dialinMessage = None + self.__dialinMessageList = None else: @@ -427,9 +472,9 @@ assign a function with packet parameter to an sync request id, e.g., def function(packet). - :param channel_id: can channel number where messages are received - :param message_id: Diality request ID in message - :param function: function reference + \param channel_id: can channel number where messages are received + \param message_id: Diality request ID in message + \param function: function reference """ # if the channel_id exist, we just update the dictionary for the channel_id @@ -444,10 +489,10 @@ """ sends can_packet to channel_id. - :param built_message: message built using DialinMessage class - :param time_out: time it will wait for a response in seconds + \param built_message: message built using DialinMessage class + \param time_out: time it will wait for a response in seconds - :returns: Diality Packet, it it times out it returns None + \returns: Diality Packet, it it times out it returns None """ channel_id = DenaliMessage.getChannelID(built_message) @@ -490,7 +535,7 @@ return self.__dialinCommandResponseMessage -def test_print_received_packet(message, sync=False): +def tst_print_received_packet(message, sync=False): channel_id = message[0] message[0] = DenaliMessage.START_BYTE introduction = "Received: " @@ -501,22 +546,22 @@ print(introduction, message, " in channel: ", channel_id) -def test_print_to_screen(message): +def tst_print_to_screen(message): if message is None: print("Timeout!!!") else: - test_print_received_packet(message) + tst_print_received_packet(message) -def test_function_for_sync(message): - test_print_received_packet(message, sync=True) +def tst_function_for_sync(message): + tst_print_received_packet(message, sync=True) -def test_print_sending_dg_board(message): +def tst_print_sending_dg_board(message): print("Sending to board: ", message['message'], " in channel: ", message['channel_id']) -def test_print_sending_dg_sim(message): +def tst_print_sending_dg_sim(message): print("Sending to DG simulator: ", message['message'], " in channel", message['channel_id'], end=" ---> ") @@ -528,15 +573,15 @@ test_received_message_id = 0x100 test_messenger.registerReceivingPublicationFunction(test_received_channel_id, test_received_message_id, - test_function_for_sync) + tst_function_for_sync) test_dg_simulator_received_channel_id = DenaliChannels.dialin_to_dg_ch_id test_dg_simulator_sync_msg_id = 0x05 test_dg_simulator_msg_id = 0x03 test_messenger.registerReceivingPublicationFunction(test_dg_simulator_received_channel_id, test_dg_simulator_sync_msg_id, - test_function_for_sync) + tst_function_for_sync) test_messenger.start() @@ -548,21 +593,21 @@ payload=[]) sleep(3.0) - test_print_sending_dg_board(test_msg) + tst_print_sending_dg_board(test_msg) test_response = test_messenger.send(test_msg) - test_print_to_screen(test_response) + tst_print_to_screen(test_response) sleep(3.0) - test_print_sending_dg_board(test_msg) + tst_print_sending_dg_board(test_msg) test_response = test_messenger.send(test_msg) - test_print_to_screen(test_response) + tst_print_to_screen(test_response) sleep(3.0) - test_print_sending_dg_sim(test_dg_msg) + tst_print_sending_dg_sim(test_dg_msg) test_response = test_messenger.send(test_dg_msg) - test_print_to_screen(test_response) + tst_print_to_screen(test_response) sleep(3.0) - test_print_sending_dg_sim(test_dg_msg) + tst_print_sending_dg_sim(test_dg_msg) test_response = test_messenger.send(test_dg_msg) - test_print_to_screen(test_response) + tst_print_to_screen(test_response) Index: HemodialysisDevice.py =================================================================== diff -u -r90bbd3f735b72658325f9c4124916cc75d62422a -r519260ce5bc660d7d44c612848412ea4e50ca662 --- HemodialysisDevice.py (.../HemodialysisDevice.py) (revision 90bbd3f735b72658325f9c4124916cc75d62422a) +++ HemodialysisDevice.py (.../HemodialysisDevice.py) (revision 519260ce5bc660d7d44c612848412ea4e50ca662) @@ -20,9 +20,10 @@ from DialityCoreCanProtocol import DenaliChannels from time import sleep from binascii import unhexlify -from HD_DialOutFlow import HD_DialOut -from HD_DialOutFlow import DialOutStates +#from HD_DialOutFlow import HD_DialOut +#from HD_DialOutFlow import DialOutStates import struct +import ctypes class HD: @@ -48,12 +49,14 @@ self.can_interface.start() # Create command groups self._Basics = HD.HD__Basics(self) - self.Alarms = HD.HD_Alarms(self) + self.Alarms = HD.HD_Alarms(self, self.can_interface) self.Buttons = HD.HD_Buttons(self) self.BloodFlow = HD.HD_BloodFlow(self, self.can_interface) + self.RTC = HD.HD_RTC(self, self.can_interface) + #self.DialysateInletFlow = HD.HD_DialysateInletFlow(self, self.can_interface) self.Watchdog = HD.HD_Watchdog(self) ## DialOut is an HD_DialOutFlow object - self.DialOut = HD_DialOut(self.can_interface) + # self.DialOut = HD_DialOut(self.can_interface) class HD__Basics: """ @@ -98,8 +101,12 @@ received_message = self.outer_instance.can_interface.send(message) if received_message is not None: - print(received_message) - print("Logged In: " + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + #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!!!!") @@ -126,7 +133,7 @@ # If there is content... if received_message is not None: - print(received_message) + #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] @@ -180,7 +187,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -218,7 +225,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -278,7 +285,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -320,7 +327,10 @@ 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_ALARMS_FLAGS = END_POS_ALARM_SILENCE_EXPIRES_IN + 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 def __init__(self, outer_instance, can_interface=None): @@ -334,14 +344,15 @@ self.outer_instance = outer_instance if can_interface is not None: - channel_id = DenaliChannels.hd_sync_broadcast_ch_id + 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) self.alarmsState = 0 self.alarmTop = 0 self.alarmsSilenceExpiresIn = 0 + self.alarmsEscalatesIn = 0 self.alarmsFlags = 0 def handlerAlarmsStatusSyncFunction(self, message): @@ -362,6 +373,9 @@ 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) @@ -392,7 +406,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -431,7 +445,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -501,6 +515,81 @@ 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: """ \class HD_BloodFlow @@ -556,7 +645,7 @@ self.MeasuredBloodPumpRotorSpeed = 0.0 self.MeasuredBloodPumpSpeed = 0.0 self.MeasuredBloodPumpMCSpeed = 0.0 - self.MeasuredBloodPumpMCurrent = 0.0 + self.MeasuredBloodPumpMCCurrent = 0.0 self.PWMDutyCyclePct = 0.0 def handlerBloodFlowSyncFunction(self, message): @@ -568,9 +657,12 @@ \returns none """ - self.TargetBloodFlowRate = int.from_bytes(bytearray( - message['message'][self.START_POS_SET_PT:self.END_POS_SET_PT]), - byteorder=DenaliMessage.BYTE_ORDER) + #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( @@ -584,6 +676,7 @@ 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] @@ -615,7 +708,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -653,7 +746,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -666,9 +759,6 @@ print("Timeout!!!!") return False - # TODO - add override command for blood pump motor speed (motor hall sensor via FPGA) - # TODO - add override command for blood pump speed (pump hall sensor via NHET?) - def CmdBloodPumpMCMeasuredSpeedOverride(self, reset, speed): """ Constructs and sends the measured blood pump motor controller speed \n @@ -694,7 +784,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -731,7 +821,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -769,7 +859,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -807,7 +897,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal" else: @@ -822,7 +912,7 @@ def CmdBloodFlowBroadcastIntervalOverride(self, reset, ms): """ - Constructs and sends the measured flow broadcast interval override command + 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 @@ -844,7 +934,7 @@ # If there is content... if received_message is not None: - print(received_message) + #print(received_message) if reset == HD.RESET: str_res = "reset back to normal: " else: @@ -857,6 +947,359 @@ print("Timeout!!!!") return False + class HD_DialysateInletFlow: + """ + \class HD_DialysateInletFlow + + \brief Hemodialysis Device (HD) Dialin API sub-class for dialysate inlet pump related commands. + + """ + + # DialysateFlow message IDs + MSG_ID_HD_DIAL_IN_FLOW_PUBLISHED_DATA = 0x0006 + 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 + + # 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 + + def __init__(self, outer_instance, can_interface=None): + """ + HD_DialysateFlow constructor + + \param outer_instance: reference to the HD (outer) class. + + \returns HD_DialysateFlow 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_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. + + \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.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_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.integer2ByteArray(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.integer2ByteArray(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.integer2ByteArray(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.integer2ByteArray(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 + def integer2ByteArray(self, val): """ Converts an integer value into a byte array (little endian) @@ -927,6 +1370,13 @@ sleep(2) hd._Basics.CmdLogInToHD() + #hd.RTC.CmdSetRTCTimeAndDate(0, 2, 1, 5, 1, 2020) + + while True: + print(hd.RTC.RTCEpoch) + sleep(1) +"""" + hd.BloodFlow.CmdBloodPumpMeasuredCurrentOverride(hd.NO_RESET,149) totalVolumeInMl = 2400 rxTimeInMins = 30 flowRateMlmin = 100 @@ -998,3 +1448,4 @@ print(hd.BloodFlow.MeasuredBloodFlowRate) # hd.BloodFlow.CmdBloodFlowBroadcastIntervalOverride(hd.RESET,0) +""" Index: setupcanbus.sh =================================================================== diff -u --- setupcanbus.sh (revision 0) +++ setupcanbus.sh (revision 519260ce5bc660d7d44c612848412ea4e50ca662) @@ -0,0 +1,5 @@ +#!/bin/bash + + +sudo ip link set can0 up type can bitrate 250000 +