Index: dialin/protocols/CAN.py =================================================================== diff -u -rde55c65a57e9136945d3d1dba299f301b55092ad -r9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8 --- dialin/protocols/CAN.py (.../CAN.py) (revision de55c65a57e9136945d3d1dba299f301b55092ad) +++ dialin/protocols/CAN.py (.../CAN.py) (revision 9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8) @@ -25,6 +25,9 @@ from logging import Logger import struct from .. import common +from ..utils import SingletonMeta +from concurrent.futures import ThreadPoolExecutor +from typing import Callable, List class DenaliMessage: @@ -209,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 @@ -221,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 @@ -356,7 +359,7 @@ class DenaliChannels: """ - Convenience class listing all the possible CAN channels used in the denali system. + Convenience class listing all the possible CAN channels used in the Denali system. Updates made to the "Message List.xlsx" document found in the Diality Software Team SharePoint, specifically in the CAN Channels sheet, should also be applied here. """ @@ -383,10 +386,11 @@ class LongDenaliMessageBuilder: - def __init__(self, can_message): + def __init__(self, message: 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. @@ -396,69 +400,74 @@ 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 message: a CAN message """ - self.message = can_message - self.number_of_can_packets_needed = DenaliMessage.get_total_packets(can_message) + self.message_data = [b for b in message.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 push(self, message: can.Message, first_packet=False): """ - push appends the can_message to the current packet. + push appends the CAN message to the current list of messages - @param can_message: 8-byte message + @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.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.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_message = self.message_data + self.message_data = None return return_message else: return None -class DenaliCanMessenger: +class DenaliCanMessenger(metaclass=SingletonMeta): 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 @param can_interface - string containing the can interface, e.g., 'can0" @return: DialityCanMessenger object """ - super().__init__() 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. try: self.bus = can.interfaces.socketcan.SocketcanBus(channel=can_interface) self.loop = asyncio.get_event_loop() if self.bus is not None: - self.canbus_thread = threading.Thread(target=self.listener, daemon=True) - self.message_queue_thread = threading.Thread(target=self.handle_messages, daemon=True) + self.thread_canbus = threading.Thread(target=self.listener, daemon=True) + self.thread_message_queue = threading.Thread(target=self.handle_messages, daemon=True) else: - self.canbus_thread = None + self.thread_canbus = None s = "Can connection is not valid" self.logger.debug(s) sys.exit(s) @@ -493,10 +502,13 @@ return else: self.run = True - if self.message_queue_thread is not None and self.canbus_thread is not None: - self.canbus_thread.start() - self.message_queue_thread.start() - self.logger.info("Can listener has started.") + if self.thread_message_queue is not None and self.thread_canbus is not None: + if not self.thread_canbus.is_alive(): + self.thread_canbus.start() + self.logger.info("Canbus thread has started.") + if not self.thread_message_queue.is_alive(): + self.thread_message_queue.start() + self.logger.info("Message queue thread has started.") else: self.logger.error("Cannot start listener...") @@ -528,7 +540,6 @@ else: self.loop.create_task(_listener()) - def handle_messages(self): """ Handles messages added to the dialin canbus message queue @@ -541,14 +552,14 @@ # 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 + # We have received a legit CAN message of 8 bytes can_data = [b for b in message.data] channel_id = message.arbitration_id if not DenaliMessage.PAYLOAD_LENGTH_INDEX < len(can_data): - self.logger.error("Invalid denali message received: {0}".format(message)) + self.logger.error("Invalid Denali message received: {0}".format(message)) self.messages = None # Can't process this message, get the next one continue else: @@ -559,25 +570,25 @@ # 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 = 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? @@ -623,9 +634,13 @@ # 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.thread_pool_executor.submit( + self.sync_response_dictionary[dialin_ch_id][dialin_msg_id], + complete_dialin_message + ) + else: + self.logger.critical("Invalid message: {}\n".format(self.messages)) - self.sync_response_dictionary[dialin_ch_id][dialin_msg_id](complete_dialin_message) - # Done with this message, let's get the next one self.messages = None @@ -653,13 +668,13 @@ resend: bool = False, is_ack: bool = False): """ - Sends a denali message + Sends a Denali message @param built_message: (dict) message built using DialinMessage class @param time_out: (float) time it will wait for a response in seconds @param resend: (bool) Allow resending the message when no response is received. Disabled by default @param is_ack: (bool) If we're sending an ACK, False by default - @return: (dict) The denali packet. If a timeout occurs returns None + @return: (dict) The Denali packet. If a timeout occurs returns None """ msg_sent = False @@ -718,6 +733,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 @@ -733,17 +771,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/dg_simulator.py =================================================================== diff -u -r817f108b234f6652fecbda170dcaae9636feec7b -r9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8 --- dialin/ui/dg_simulator.py (.../dg_simulator.py) (revision 817f108b234f6652fecbda170dcaae9636feec7b) +++ dialin/ui/dg_simulator.py (.../dg_simulator.py) (revision 9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8) @@ -28,9 +28,12 @@ def __init__(self, can_interface:str="can0", log_level:str=None, console_out:bool=False, - passive_mode:bool=False): + passive_mode:bool=False, + auto_response:bool=False): super().__init__() + DGSimulator.instance_count = DGSimulator.instance_count + 1 + self._log_manager = _LogManager(log_level=log_level, log_filepath=self.__class__.__name__ + ".log") self.logger = self._log_manager.logger self.console_out = console_out @@ -42,47 +45,18 @@ self.can_interface.start() if self.can_interface is not None: - channel_id = DenaliChannels.ui_to_hd_ch_id - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_DG_SET_RTC_REQUEST.value, - self._handler_set_rtc_request) - self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, - MsgIds.MSG_ID_REQUEST_FW_VERSIONS.value, - self._handler_request_dg_version) - self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, - MsgIdsDialin.MSG_DIALIN_ID_UI_SYSTEM_USAGE_REQUEST.value, - self._handler_system_usage_response) + channel_id = DenaliChannels.ui_to_dg_ch_id + if auto_response: + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_DG_SET_RTC_REQUEST.value, + self._handler_set_rtc_request) + self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, + MsgIds.MSG_ID_REQUEST_FW_VERSIONS.value, + self._handler_request_dg_version) + self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, + MsgIdsDialin.MSG_DIALIN_ID_UI_SYSTEM_USAGE_REQUEST.value, + self._handler_system_usage_response) - def cmd_send_dg_post_single_result(self, result: int, test_num: int) -> None: - """ - Reports a single result for power on self test - @param result: success or fail, where success = 1, 0 = fail - @param test_num: the test index [0-n] - @return: None - """ - payload = integer_to_bytearray(result) - payload += integer_to_bytearray(test_num) - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, - message_id=MsgIds.MSG_ID_DG_POST_SINGLE_TEST_RESULT.value, - payload=payload) - - self.can_interface.send(message, 0) - - def cmd_send_dg_post_final_result(self, result: int) -> None: - """ - Reports a final result for power on self test - @param result: success or fail, where success = 1, 0 = fail - @return: None - """ - payload = integer_to_bytearray(result) - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, - message_id=MsgIds.MSG_ID_DG_POST_FINAL_TEST_RESULT.value, - payload=payload) - - self.can_interface.send(message, 0) - def _handler_system_usage_response(self, message: dict) -> None: """ Handles a request for system usage @@ -492,12 +466,27 @@ payload += byte_to_bytearray(vFPGA_Minor) payload += byte_to_bytearray(vFPGA_Lab) - message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_to_ui_ch_id, + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, message_id=MsgIds.MSG_ID_DG_VERSION.value, payload=payload) self.can_interface.send(message, 0) + def cmd_send_serial_dg_data(self, vSerial: str ): + """ + the dg version serial response message method + :param vSerial: serial number + :return: None + """ + + payload = bytes(vSerial, 'ascii') + b'\x00' + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DG_SERIAL_NUMBER.value, + payload=payload) + + self.can_interface.send(message, 0) + @staticmethod def build_dg_debug_text(vText): """ @@ -510,7 +499,7 @@ msg = messageBuilder.buildMessage(GuiActionType.DGDebugText, 1 * (message_length + 1), False, txt) return messageBuilder.toFrames(msg) - def cmd_send_pre_treatment_filter_flush_progress_data(self, total, countdown): + def cmd_send_dg_pre_treatment_filter_flush_progress_data(self, total, countdown): """ send the pretreatment filter flush progress data :param accepted: (U32) Total time in second @@ -526,25 +515,6 @@ self.can_interface.send(message, 0) - def cmd_send_dg_disinfection_state(self, subMode, flushMode, heatMode, chemicalMode): - """ - Broadcasts the current DG disinfection mode - :param subMode (int): disinfect states - :param flushMode (int): flush states - :param heatMode (int): heat states - :param ChemicalMode (int): chemical states - :return: - """ - payload = integer_to_bytearray(subMode) - payload += integer_to_bytearray(flushMode) - payload += integer_to_bytearray(heatMode) - payload += integer_to_bytearray(chemicalMode) - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, - message_id=MsgIdsDialin.MSG_DIALIN_ID_HD_DISINFECT_STATE.value, - payload=payload) - self.can_interface.send(message, 0) - def cmd_send_dg_disinfect_progress_time_flush(self, total: int, countdown: int) -> None: """ the broadcast progress water flush time @@ -577,6 +547,27 @@ self.can_interface.send(message, 0) + def cmd_send_dg_post(self, item: int, passed: bool, done: bool = False) -> None: + """ + send hd post message the single(item) or the final(done) + :param item: the post state/item index + :param passed: the post result single or final + :param done: if this is the final post message this should be true + :return: None + """ + payload = integer_to_bytearray(passed) + if not done: + payload += integer_to_bytearray(item) + + message = DenaliMessage.build_message(channel_id= DenaliChannels.dg_sync_broadcast_ch_id, + message_id= MsgIds.MSG_ID_DG_POST_FINAL_TEST_RESULT.value if done + else MsgIds.MSG_ID_DG_POST_SINGLE_TEST_RESULT.value, + payload=payload) + + self.can_interface.send(message, 0) + + # ------------------------------------------------ GENERAL MESSAGES ------------------------------------------------ + def cmd_send_dg_disinfect_progress_time_checmical(self, total: int, countdown: int) -> None: """ the broadcast progress chemical disinfect time @@ -593,7 +584,7 @@ self.can_interface.send(message, 0) - def cmd_send_general_dg_response(self, message_id: int, accepted: int, reason: int, + def cmd_send_dg_general_response(self, message_id: int, accepted: int, reason: int, is_pure_data: bool = False, has_parameters: bool = False, parameters_payload: any = 0x00) -> None: @@ -622,7 +613,7 @@ self.can_interface.send(message, 0) - def cmd_send_general_dg_progress_data(self, message_id: int, total: int, countdown: int) -> None: + def cmd_send_sg_general_progress_data(self, message_id: int, total: int, countdown: int) -> None: """ a general method t send any standard progress data message, by it's id :param message_id: the id of the message @@ -639,7 +630,7 @@ self.can_interface.send(message, 0) - def cmd_ack_send_dg(self, seq: int) -> None: + def cmd_send_dg_ack(self, seq: int) -> None: """ sending dg ack message by the sequence seq :param seq: the message sequence number Index: dialin/ui/hd_simulator.py =================================================================== diff -u -r6356891100bf5058c4c77ac388eb23415d6435c6 -r9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8 --- dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 6356891100bf5058c4c77ac388eb23415d6435c6) +++ dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8) @@ -19,7 +19,6 @@ import time from . import messageBuilder -from .hd_simulator_alarms import HDAlarmsSimulator from ..common import * from ..protocols.CAN import (DenaliMessage, DenaliCanMessenger, @@ -30,12 +29,13 @@ class HDSimulator(_AbstractSubSystem): NUM_TREATMENT_PARAMETERS = 18 - instanceCount = 0 + instance_count = 0 def __init__(self, can_interface:str="can0", log_level:bool= None, console_out:bool= False, - passive_mode:bool=False): + passive_mode:bool=False, + auto_response:bool=False): """ The HDSimulator constructor @@ -44,7 +44,7 @@ @param console_out: (bool) If True, write each dialin message to the console. """ super().__init__() - HDSimulator.instanceCount = HDSimulator.instanceCount + 1 + HDSimulator.instance_count = HDSimulator.instance_count + 1 self._log_manager = _LogManager(log_level=log_level, log_filepath=self.__class__.__name__ + ".log") self.logger = self._log_manager.logger @@ -58,67 +58,38 @@ if self.can_interface is not None: channel_id = DenaliChannels.ui_to_hd_ch_id - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_INITIATE_TREATMENT_REQUEST.value, - self._handler_ui_initiate_treatment) - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_SET_UF_VOLUME_PARAMETER.value, - self._handler_ui_pre_treatment_uf_request) - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_NEW_TREATMENT_PARAMS.value, - self._handler_ui_validate_parameters) - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_USER_CONFIRM_TREATMENT_PARAMS.value, - self._handler_ui_confirm_treatment) - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_TX_END_CMD.value, - self._handler_ui_end_treatment) - self.can_interface.register_receiving_publication_function(channel_id, - MsgIds.MSG_ID_UI_HD_SET_RTC_REQUEST.value, - self._handler_set_rtc_request) - self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, - MsgIds.MSG_ID_REQUEST_FW_VERSIONS.value, - self._handler_request_hd_version) - self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, - MsgIdsDialin.MSG_DIALIN_ID_UI_SYSTEM_USAGE_REQUEST.value, - self._handler_system_usage_response) - self.can_interface.register_receiving_publication_function(DenaliChannels.ui_to_hd_ch_id, - MsgIdsDialin.MSG_DIALIN_ID_UI_POST_REPORT_VERSION.value, - self._handler_ui_post_ui_version_compatibility) - self.alarms_simulator = HDAlarmsSimulator(self.can_interface, self.logger) + if auto_response: + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_INITIATE_TREATMENT_REQUEST.value, + self._handler_ui_initiate_treatment) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_SET_UF_VOLUME_PARAMETER.value, + self._handler_ui_pre_treatment_uf_request) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_NEW_TREATMENT_PARAMS.value, + self._handler_ui_validate_parameters) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_USER_CONFIRM_TREATMENT_PARAMS.value, + self._handler_ui_confirm_treatment) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_TX_END_CMD.value, + self._handler_ui_end_treatment) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_HD_SET_RTC_REQUEST.value, + self._handler_set_rtc_request) + self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, + MsgIds.MSG_ID_REQUEST_FW_VERSIONS.value, + self._handler_request_hd_version) + self.can_interface.register_receiving_publication_function(DenaliChannels.ui_sync_broadcast_ch_id, + MsgIdsDialin.MSG_DIALIN_ID_UI_SYSTEM_USAGE_REQUEST.value, + self._handler_system_usage_response) + self.can_interface.register_receiving_publication_function(DenaliChannels.ui_to_hd_ch_id, + MsgIdsDialin.MSG_DIALIN_ID_UI_POST_REPORT_VERSION.value, + self._handler_ui_post_ui_version_compatibility) + self.treatment_parameter_rejections = TreatmentParameterRejections() - def cmd_send_hd_post_single_result(self, result: int, test_num: int) -> None: - """ - Reports a single result for power on self test - @param result: success or fail, where success = 1, 0 = fail - @param test_num: the test index [0-n] - @return: None - """ - payload = integer_to_bytearray(result) - payload += integer_to_bytearray(test_num) - - message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, - message_id=MsgIds.MSG_ID_HD_POST_SINGLE_TEST_RESULT.value, - payload=payload) - - self.can_interface.send(message, 0) - - def cmd_send_hd_post_final_result(self, result: int) -> None: - """ - Reports a final result for power on self test - @param result: success or fail, where success = 1, 0 = fail - @return: None - """ - payload = integer_to_bytearray(result) - - message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, - message_id=MsgIds.MSG_ID_HD_POST_FINAL_TEST_RESULT.value, - payload=payload) - - self.can_interface.send(message, 0) - def _handler_system_usage_response(self, message: dict) -> None: """ Handles a request for system usage @@ -1457,19 +1428,28 @@ payload += byte_to_bytearray(vFPGA_Minor) payload += byte_to_bytearray(vFPGA_Lab) - message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, message_id=MsgIds.MSG_ID_HD_VERSION.value, payload=payload) self.can_interface.send(message, 0) - def alarm(self) -> HDAlarmsSimulator: + def cmd_send_serial_hd_data(self, vSerial: str ): """ - Gets the alarm simulator object - @return: (HDAlarmsSimulator) the alarms simulator + the hd version serial response message method + :param vSerial: serial number + :return: None """ - return self.alarms_simulator + payload = bytes(vSerial, 'ascii') + b'\x00' + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_HD_SERIAL_NUMBER.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_pre_treatment_state_data(self, sub_mode, water_sample_state, @@ -1668,7 +1648,8 @@ sodium_concentration: int, dialysate_temperature: float, dialyzer_type: int, - treatment_date_time: int, + treatment_start_date_time: int, + treatment_end_date_time: int, average_blood_flow: float, average_dialysate_flow: float, dialysate_volume_used: float, @@ -1678,15 +1659,12 @@ target_uf_rate: float, actual_uf_rate: float, saline_bolus_volume: int, - heparin_type: int, - heparin_concentration: int, heparin_bolus_volume: float, heparin_dispense_rate: float, heparin_pre_stop: int, heparin_delivered_volume: float, average_arterial_pressure: float, average_venous_pressure: float, - end_treatment_early_alarm: int, device_id: int, water_sample_test_result: int ) -> None: @@ -1706,7 +1684,8 @@ :param sodium_concentration: sodium concentration :param dialysate_temperature: dialysate temperature :param dialyzer_type: dialyzer type - :param treatment_date_time: treatment date time + :param treatment_start_date_time: treatment start date time + :param treatment_end_date_time: treatment end date time :param average_blood_flow: average blood flow :param average_dialysate_flow: average dialysate flow :param dialysate_volume_used: dialysate volume used @@ -1744,7 +1723,8 @@ payload += unsigned_to_bytearray(int(sodium_concentration)) payload += float_to_bytearray(float(dialysate_temperature)) payload += unsigned_to_bytearray(int(dialyzer_type)) - payload += unsigned_to_bytearray(int(treatment_date_time)) + payload += unsigned_to_bytearray(int(treatment_start_date_time)) + payload += unsigned_to_bytearray(int(treatment_end_date_time)) payload += float_to_bytearray(float(average_blood_flow)) payload += float_to_bytearray(float(average_dialysate_flow)) payload += float_to_bytearray(float(dialysate_volume_used)) @@ -1754,15 +1734,12 @@ payload += float_to_bytearray(float(target_uf_rate)) payload += float_to_bytearray(float(actual_uf_rate)) payload += unsigned_to_bytearray(int(saline_bolus_volume)) - payload += unsigned_to_bytearray(int(heparin_type)) - payload += unsigned_to_bytearray(int(heparin_concentration)) payload += float_to_bytearray(float(heparin_bolus_volume)) payload += float_to_bytearray(float(heparin_dispense_rate)) payload += unsigned_to_bytearray(int(heparin_pre_stop)) payload += float_to_bytearray(float(heparin_delivered_volume)) payload += float_to_bytearray(float(average_arterial_pressure)) payload += float_to_bytearray(float(average_venous_pressure)) - payload += unsigned_to_bytearray(int(end_treatment_early_alarm)) payload += unsigned_to_bytearray(int(device_id)) payload += unsigned_to_bytearray(int(water_sample_test_result)) @@ -1830,6 +1807,25 @@ self.can_interface.send(message, 0) + def cmd_send_hd_disinfection_state(self, subMode, flushMode, heatMode, chemicalMode): + """ + Broadcasts the current DG disinfection mode + :param subMode (int): disinfect states + :param flushMode (int): flush states + :param heatMode (int): heat states + :param ChemicalMode (int): chemical states + :return: + """ + payload = integer_to_bytearray(subMode) + payload += integer_to_bytearray(flushMode) + payload += integer_to_bytearray(heatMode) + payload += integer_to_bytearray(chemicalMode) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIdsDialin.MSG_DIALIN_ID_HD_DISINFECT_STATE.value, + payload=payload) + self.can_interface.send(message, 0) + def cmd_send_hd_disinfect_response(self, accepted: bool, reason: int) -> None: """ the HD response to the request from UI to initiate a disinfection/flush @@ -1862,9 +1858,28 @@ self.can_interface.send(message, 0) + def cmd_send_hd_post(self, item: int, passed: bool, done: bool = False) -> None: + """ + send hd post message the single(item) or the final(done) + :param item: the post state/item index + :param passed: the post result single or final + :param done: if this is the final post message this should be true + :return: None + """ + payload = integer_to_bytearray(passed) + if not done: + payload += integer_to_bytearray(item) + + message = DenaliMessage.build_message(channel_id= DenaliChannels.hd_sync_broadcast_ch_id, + message_id= MsgIds.MSG_ID_HD_POST_FINAL_TEST_RESULT.value if done + else MsgIds.MSG_ID_HD_POST_SINGLE_TEST_RESULT.value, + payload=payload) + + self.can_interface.send(message, 0) + # ------------------------------------------------ GENERAL MESSAGES ------------------------------------------------ - def cmd_send_general_hd_response(self, message_id: int, accepted: int, reason: int, + def cmd_send_hd_general_response(self, message_id: int, accepted: int, reason: int, is_pure_data: bool = False, has_parameters: bool = False, parameters_payload: any = 0x00) -> None: @@ -1893,7 +1908,7 @@ self.can_interface.send(message, 0) - def cmd_send_general_hd_progress_data(self, message_id: int, total: int, countdown: int) -> None: + def cmd_send_hd_general_progress_data(self, message_id: int, total: int, countdown: int) -> None: """ a general method t send any standard progress data message, by it's id :param message_id: the id of the message @@ -1909,7 +1924,7 @@ payload=payload) self.can_interface.send(message, 0) - def cmd_ack_send_hd(self, seq: int) -> None: + def cmd_send_hd_ack(self, seq: int) -> None: """ sending hd ack message by the sequence seq :param seq: the message sequence number Index: dialin/ui/hd_simulator_alarms.py =================================================================== diff -u -r4d32882a7d9092d5c7bb179f33e1de5ce087f54d -r9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8 --- dialin/ui/hd_simulator_alarms.py (.../hd_simulator_alarms.py) (revision 4d32882a7d9092d5c7bb179f33e1de5ce087f54d) +++ dialin/ui/hd_simulator_alarms.py (.../hd_simulator_alarms.py) (revision 9ae90e7b14dc11b1f6fa631caecc75d68d7f68e8) @@ -25,7 +25,7 @@ from ..utils.conversions import integer_to_bytearray, integer_to_bit_array, float_to_bytearray from ..common.msg_defs import MsgIds, MsgFieldPositions from ..common.alarm_defs import AlarmList -from dialin.common.prs_defs import Alarm_Data_Types +from dialin.common.prs_defs import AlarmDataTypes HIGH = 3 MED = 2 @@ -37,6 +37,7 @@ class Alarms: + # TODO: this should be generated from FW # ALARM_ID = (priority, alarmID, escalates in, silent_espires_in, flags) ALARM_ID_NO_ALARM = (NONE, 0, 0, 0, 0) ALARM_ID_SOFTWARE_FAULT = (HIGH, 1, 0, 0, 0) @@ -374,23 +375,23 @@ """ zero = integer_to_bytearray(0) payload = integer_to_bytearray(alarm_id) - if (field_descriptor_1 == Alarm_Data_Types.ALARM_DATA_TYPE_NONE): + if (field_descriptor_1 == AlarmDataTypes.ALARM_DATA_TYPE_NONE): payload += zero payload += zero payload += zero payload += zero else: - if (field_descriptor_1 == Alarm_Data_Types.ALARM_DATA_TYPE_F32): + if (field_descriptor_1 == AlarmDataTypes.ALARM_DATA_TYPE_F32): payload += integer_to_bytearray(field_descriptor_1) payload += float_to_bytearray(float(data_field_1)) else: # BOOL, S32, U32 payload += integer_to_bytearray(field_descriptor_1) payload += integer_to_bytearray(int(data_field_1)) - if (field_descriptor_2 == Alarm_Data_Types.ALARM_DATA_TYPE_NONE): + if (field_descriptor_2 == AlarmDataTypes.ALARM_DATA_TYPE_NONE): payload += zero payload += zero else: - if (field_descriptor_2 == Alarm_Data_Types.ALARM_DATA_TYPE_F32): + if (field_descriptor_2 == AlarmDataTypes.ALARM_DATA_TYPE_F32): payload += integer_to_bytearray(field_descriptor_2) payload += float_to_bytearray(float(data_field_2)) else: # BOOL, S32, U32