Index: dialin/common/alarm_defs.py =================================================================== diff -u -r46342bac3e23428e90666525bb5a89c4532da945 -r6356891100bf5058c4c77ac388eb23415d6435c6 --- dialin/common/alarm_defs.py (.../alarm_defs.py) (revision 46342bac3e23428e90666525bb5a89c4532da945) +++ dialin/common/alarm_defs.py (.../alarm_defs.py) (revision 6356891100bf5058c4c77ac388eb23415d6435c6) @@ -8,9 +8,9 @@ # @file alarm_defs.py # # @author (last) Peter Lucia -# @date (last) 04-Jun-2021 +# @date (last) 15-Jun-2021 # @author (original) Peter Lucia -# @date (original) 04-Jun-2021 +# @date (original) 15-Jun-2021 # ############################################################################ from enum import unique Index: dialin/common/msg_ids.py =================================================================== diff -u -r46342bac3e23428e90666525bb5a89c4532da945 -r6356891100bf5058c4c77ac388eb23415d6435c6 --- dialin/common/msg_ids.py (.../msg_ids.py) (revision 46342bac3e23428e90666525bb5a89c4532da945) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision 6356891100bf5058c4c77ac388eb23415d6435c6) @@ -8,9 +8,9 @@ # @file msg_ids.py # # @author (last) Peter Lucia -# @date (last) 04-Jun-2021 +# @date (last) 15-Jun-2021 # @author (original) Peter Lucia -# @date (original) 04-Jun-2021 +# @date (original) 15-Jun-2021 # ############################################################################ from enum import unique @@ -174,8 +174,10 @@ MSG_ID_UI_ACTIVE_ALARMS_LIST_REQUEST = 0X97 MSG_ID_HD_ACTIVE_ALARMS_LIST_REQUEST_RESPONSE = 0X98 MSG_ID_HD_SERIAL_NUMBER = 0X99 - MSG_ID_DG_CHEM_DISINFECT_TO_UI_DATA_PUBLISH = 0X9A - MSG_ID_DG_HEAT_DISINFECT_TO_UI_DATA_PUBLISH = 0X9B + MSG_ID_HD_SET_STANDBY_DISINFECT_SUB_MODE_REQUEST = 0X9A + MSG_ID_HD_SET_STANDBY_DISINFECT_SUB_MODE_RESPONSE = 0X9B + MSG_ID_HD_DISINFECTS_UI_STATE_READINGS_FROM_DG = 0X9C + MSG_ID_HD_DISINFECTS_UI_STATES_DATA = 0X9D MSG_ID_CAN_ERROR_COUNT = 0X999 MSG_ID_TESTER_LOGIN_REQUEST = 0X8000 MSG_ID_DIAL_OUT_FLOW_SET_PT_OVERRIDE = 0X8001 Index: dialin/protocols/CAN.py =================================================================== diff -u -r93629045e925eea85b2808a0e7a7b0ea77426f7a -r6356891100bf5058c4c77ac388eb23415d6435c6 --- dialin/protocols/CAN.py (.../CAN.py) (revision 93629045e925eea85b2808a0e7a7b0ea77426f7a) +++ dialin/protocols/CAN.py (.../CAN.py) (revision 6356891100bf5058c4c77ac388eb23415d6435c6) @@ -27,6 +27,7 @@ from .. import common from ..utils import SingletonMeta from concurrent.futures import ThreadPoolExecutor +from typing import Callable, List class DenaliMessage: @@ -211,7 +212,7 @@ return calculated_crc == actual_crc @staticmethod - def get_channel_id(message): + def get_channel_id(message: dict) -> int: """ Returns request ID from message @@ -223,7 +224,7 @@ return message['channel_id'] @staticmethod - def get_sequence_number(message): + def get_sequence_number(message: dict) -> int: """ Returns sequence number from the message @@ -385,10 +386,11 @@ class LongDenaliMessageBuilder: - def __init__(self, can_message): + def __init__(self, frame: can.Message): """ - LongDialityMessageBuilder is a utility object that helps construct a diality message - that is longer than 8 bytes. Basic principle is to construct an object with the + LongDialityMessageBuilder is a utility object that helps construct a denali message + that is longer than 8 bytes. It is only called when we don't yet have a long message + builder for the current channel. Basic principle is to construct an object with the first 8 byte message which contains the length of the message, and the later push the remaining messages. e.g., let's imagine a 3 message packet. @@ -398,37 +400,50 @@ 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. - @return: object - + @param frame: a can frame """ - self.message = can_message - self.number_of_can_packets_needed = DenaliMessage.get_total_packets(can_message) + self.frames = [frame] + self.message_data = [b for b in frame.data] + self.number_of_can_packets_needed = DenaliMessage.get_total_packets(self.message_data) self.number_of_can_packets_up_to_now = 1 - def push(self, can_message, first_packet=False): + def frames(self) -> List[can.Message]: """ - push appends the can_message to the current packet. + Gets the list of frames for the long denali message - @param can_message: 8-byte message + @return: A list of can messages + """ + return self.frames + + def push(self, message: can.Message, first_packet=False): + """ + push appends the can message to the current list of messages + + @param message: 8-byte message + @param first_packet: True if it is the first packet received @return:: None if the packet is not completed, otherwise returns the complete packet """ + message_data = [b for b in message.data] if first_packet: - self.message = can_message - self.number_of_can_packets_needed = DenaliMessage.get_total_packets(can_message) + self.frames = [message] + self.message_data = message_data + self.number_of_can_packets_needed = DenaliMessage.get_total_packets(message_data) self.number_of_can_packets_up_to_now = 1 else: - self.message += can_message + self.frames.append(message) + self.message_data += message_data self.number_of_can_packets_up_to_now += 1 if self.number_of_can_packets_up_to_now == self.number_of_can_packets_needed: - return_message = self.message - self.message = None - return return_message + return_message = self.message_data + return_frames = self.frames + self.message_data = None + self.frames = [] + return return_frames, return_message else: return None @@ -438,7 +453,11 @@ START_BYTE = DenaliMessage.START_BYTE DIALIN_MSG_RESP_TO = 0.5 # number of seconds to wait for a response to a send command - def __init__(self, can_interface: str, logger: Logger, log_can=False, passive_mode=True, console_out=False): + def __init__(self, can_interface: str, + logger: Logger, + log_can=False, + passive_mode=True, + console_out=False): """ DenaliCanMessenger constructor @@ -450,6 +469,8 @@ self.logger = logger self.log_can = log_can self.message_queue = deque() + self.callback_listener_complete_messages = None + self.callback_listener_invalid_messages = None self.thread_pool_executor = ThreadPoolExecutor(max_workers=1) # try to setup can bus and exit if the can bus has not ben setup to use. @@ -483,6 +504,32 @@ self.run = False self.sync_response_dictionary = {} self.pending_requests = {} + + def set_callback_complete_messages(self, callback: Callable) -> bool: + """ + Sets the callback for complete denali messages + + :param callback: a callable function that accepts a DenaliMessage as a parameter + :return: True if set is successful, False otherwise + """ + if callable(self.callback_listener_complete_messages): + return False + + self.callback_listener_complete_messages = callback + return True + + def set_callback_invalid_messages(self, callback: Callable) -> bool: + """ + Sets the callback function for invalid denali messages + + :param callback: a callable function that accepts a DenaliMessage as a parameter + :return: True if set is successful, False otherwise + """ + if callable(self.callback_listener_invalid_messages): + return False + + self.callback_listener_invalid_messages = callback + return True def start(self): """ @@ -545,7 +592,7 @@ # Careful here, making this any shorter will start limiting CPU time for other threads sleep(0.01) else: - message: dict = self.message_queue.popleft() + message: can.Message = self.message_queue.popleft() if message.dlc == DenaliMessage.PACKET_LENGTH: # We have received a legit can message of 8 bytes @@ -563,32 +610,32 @@ # if we are building a long message, then proceed to push it to the channel dictionary if channel_id in self.long_msg_channel_id_set: - self.messages = self.long_message_builders[channel_id].push(can_data) + self.messages = self.long_message_builders[channel_id].push(message) elif can_data[0] == DenaliMessage.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.messages = can_data # deliver the packet + self.messages = ([message], can_data) # deliver the packet elif can_data[0] == self.START_BYTE and \ message_length > DenaliMessage.PAYLOAD_LENGTH_FIRST_PACKET: # Long packet start # We are starting to build a long message, include it in the lonMsgChannelIDSet self.long_msg_channel_id_set.add(channel_id) - if channel_id not in self.long_message_builders.keys(): # if we don't have a builder. Create it! - self.long_message_builders[channel_id] = LongDenaliMessageBuilder(can_data) + # if we don't have a long denali message builder yet, create it + if channel_id not in self.long_message_builders.keys(): + self.long_message_builders[channel_id] = LongDenaliMessageBuilder(message) self.messages = None else: # if we do have a builder. This is the first time - # self.messages = self.long_message_builders[channel_id].push(can_data, first_packet=True) - self.long_message_builders[channel_id].push(can_data, first_packet=True) + self.long_message_builders[channel_id].push(message, first_packet=True) self.messages = None # Do we have a complete (long or short) Denali Message? if self.messages is not None: message_valid = True # assume true for now, set to false if CRC check fails below complete_dialin_message = DenaliMessage.build_basic_message(channel_id=channel_id, - message=self.messages) + message=self.messages[1]) dialin_msg_id = DenaliMessage.get_message_id(complete_dialin_message) dialin_ch_id = DenaliMessage.get_channel_id(complete_dialin_message) @@ -604,8 +651,8 @@ dialin_msg_id = None self.logger.critical( "Incorrect CRC, received message: {}, crc: {}, calculated crc: {}\n".format( - self.messages, DenaliMessage.get_crc(complete_dialin_message), - DenaliMessage.crc8(self.messages))) + self.messages[1], DenaliMessage.get_crc(complete_dialin_message), + DenaliMessage.crc8(self.messages[1]))) if message_valid: if self.console_out: @@ -631,6 +678,13 @@ self.sync_response_dictionary[dialin_ch_id][dialin_msg_id], complete_dialin_message ) + if callable(self.callback_listener_complete_messages): + self.thread_pool_executor.submit( + self.callback_listener_complete_messages(self.messages[0])) + else: + if callable(self.callback_listener_invalid_messages): + self.thread_pool_executor.submit( + self.callback_listener_invalid_messages(self.messages[0])) # Done with this message, let's get the next one self.messages = None @@ -724,6 +778,29 @@ del self.pending_requests[msg_id] return response + @staticmethod + def _format_message_candump_style(message: can.Message, channel: str, send: bool = True) -> str: + """ + Formats a packet + @param message: (can.Message) The packet to log + @param channel: (str) The channel send or received on + @param send: (bool) Whether we're sending or receiving this packet + @return: The styled message + """ + + tmp = str(message) + + if send: + return "{0} {1} [{2}] {3}\n".format(channel, + hex(message.arbitration_id)[2:], + message.dlc, + tmp[-23:].upper()) + else: + return "{0} {1} [{2}] {3}\n".format(channel, + hex(message.arbitration_id)[2:], + message.dlc, + tmp[-41:-18].upper()) + def do_log_can(self, packet: can.Message, style="candump", channel="can0", send=True): """ Logs all packets sent or received by dialin in candump, or non-candump style format @@ -739,17 +816,8 @@ filename = "Dialin_CAN_Receive.log" if style == "candump": with open(filename, 'a') as f: - tmp = str(packet) - if send: - f.write("{0} {1} [{2}] {3}\n".format(channel, - hex(packet.arbitration_id)[2:], - packet.dlc, - tmp[-23:].upper())) - else: - f.write("{0} {1} [{2}] {3}\n".format(channel, - hex(packet.arbitration_id)[2:], - packet.dlc, - tmp[-41:-18].upper())) + styled_message = self._format_message_candump_style(message=packet, channel=channel, send=send) + f.write(styled_message) else: with open(filename, 'a') as f: f.write("{0}\n".format(packet)) Index: dialin/ui/hd_simulator.py =================================================================== diff -u -r817f108b234f6652fecbda170dcaae9636feec7b -r6356891100bf5058c4c77ac388eb23415d6435c6 --- dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 817f108b234f6652fecbda170dcaae9636feec7b) +++ dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 6356891100bf5058c4c77ac388eb23415d6435c6) @@ -15,7 +15,7 @@ ############################################################################ import enum from time import sleep -from typing import List +from typing import List, Union import time from . import messageBuilder @@ -345,7 +345,7 @@ self.cmd_initiate_treatment_response(YES, 0) - def cmd_initiate_treatment_response(self, response, reason): + def cmd_initiate_treatment_response(self, response: int, reason: int): """ Sends a start treatment response message