Index: dialin/dg/hd_proxy.py =================================================================== diff -u -r2cfc646e14640e82c5f6989a572737ce8256caa5 -rfdac759dbbec54375d4b3ebb21aebcc684ac1aed --- dialin/dg/hd_proxy.py (.../hd_proxy.py) (revision 2cfc646e14640e82c5f6989a572737ce8256caa5) +++ dialin/dg/hd_proxy.py (.../hd_proxy.py) (revision fdac759dbbec54375d4b3ebb21aebcc684ac1aed) @@ -1,23 +1,23 @@ ########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. # -# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN -# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. # -# @file hd_proxy.py +# @file hd_proxy.py # -# @date 14-Apr-2020 -# @author S. Nash +# @author (last) Peter Lucia +# @date (last) 26-Aug-2020 +# @author (original) Sean +# @date (original) 15-Apr-2020 # -# @brief -# -# ############################################################################ from ..utils.conversions import integer_to_bytearray from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish +from logging import Logger class DGHDProxy(_AbstractSubSystem): @@ -33,19 +33,20 @@ MSG_ID_HD_START_STOP_DG_CMD = 0x0026 MSG_ID_HD_START_STOP_DG_TRIMMER_HEATER = 0x002B - MSG_ID_DG_START_STOP_HEAT_DISINFECT = 0x2F + MSG_ID_DG_START_STOP_HEAT_DISINFECT = 0x30 # Reservoir IDs RESERVOIR1 = 0 RESERVOIR2 = 1 - def __init__(self, can_interface=None): + def __init__(self, can_interface, logger: Logger): """ @param can_interface: Denali CAN Messenger object """ super().__init__() self.can_interface = can_interface + self.logger = logger def cmd_switch_reservoirs(self, reservoirID=RESERVOIR1): """ @@ -69,7 +70,7 @@ message_id=self.MSG_ID_HD_SWITCH_RESERVOIRS_CMD, payload=payload) - print("switch reservoirs cmd sent to DG") + self.logger.debug("switch reservoirs cmd sent to DG") # Send message received_message = self.can_interface.send(message) @@ -79,7 +80,7 @@ # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] else: - print("Timeout!!!!") + self.logger.debug("Timeout!!!!") return False def cmd_fill(self, volume=1500): @@ -100,7 +101,7 @@ message_id=self.MSG_ID_HD_FILL_CMD, payload=payload) - print("fill cmd sent to DG") + self.logger.debug("fill cmd sent to DG") # Send message received_message = self.can_interface.send(message) @@ -110,7 +111,7 @@ # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] else: - print("Timeout!!!!") + self.logger.debug("Timeout!!!!") return False def cmd_drain(self, volume=200): @@ -131,7 +132,7 @@ message_id=self.MSG_ID_HD_DRAIN_CMD, payload=payload) - print("drain cmd sent to DG") + self.logger.debug("drain cmd sent to DG") # Send message received_message = self.can_interface.send(message) @@ -141,7 +142,7 @@ # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] else: - print("Timeout!!!!") + self.logger.debug("Timeout!!!!") return False def cmd_start_stop_dg(self, start=True): @@ -167,7 +168,7 @@ message_id=self.MSG_ID_HD_START_STOP_DG_CMD, payload=payload) - print(str+"DG cmd sent to DG") + self.logger.debug(str+"DG cmd sent to DG") # Send message received_message = self.can_interface.send(message) @@ -177,7 +178,7 @@ # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] else: - print("Timeout!!!!") + self.logger.debug("Timeout!!!!") return False def cmd_start_stop_trimmer_heater(self, start=True): @@ -200,7 +201,7 @@ message_id=self.MSG_ID_HD_START_STOP_DG_TRIMMER_HEATER, payload=payload) - print(str+"DG trimmer heater cmd sent to DG") + self.logger.debug(str+"DG trimmer heater cmd sent to DG") # Send message received_message = self.can_interface.send(message) @@ -210,7 +211,7 @@ # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] else: - print("Timeout!!!!") + self.logger.debug("Timeout!!!!") return False def cmd_start_heat_disinfection(self): Index: dialin/hd/treatment.py =================================================================== diff -u -r13a03d199a7b202d61423587ca7c72e00c0df439 -rfdac759dbbec54375d4b3ebb21aebcc684ac1aed --- dialin/hd/treatment.py (.../treatment.py) (revision 13a03d199a7b202d61423587ca7c72e00c0df439) +++ dialin/hd/treatment.py (.../treatment.py) (revision fdac759dbbec54375d4b3ebb21aebcc684ac1aed) @@ -1,26 +1,25 @@ ########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. # -# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN -# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. # -# @file treatment.py +# @file treatment.py # -# @date 31-Mar-2020 -# @author P. Lucia +# @author (last) Peter Lucia +# @date (last) 06-Aug-2020 +# @author (original) Peter Lucia +# @date (original) 02-Apr-2020 # -# @brief -# -# ############################################################################ import struct from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish +from logging import Logger from ..utils.conversions import integer_to_bytearray, float_to_bytearray - class HDTreatment(_AbstractSubSystem): """ @@ -83,14 +82,14 @@ START_POS_SALINE_BOLUS_IN_PROGRESS = END_POS_UF_STATE END_POS_SALINE_BOLUS_IN_PROGRESS = START_POS_SALINE_BOLUS_IN_PROGRESS + 4 - def __init__(self, can_interface=None): + def __init__(self, can_interface, logger: Logger): """ HDTreatment constructor """ super().__init__() - self.can_interface = can_interface + self.logger = logger if self.can_interface is not None: channel_id = DenaliChannels.hd_sync_broadcast_ch_id Index: dialin/hd/ui_proxy.py =================================================================== diff -u -r13a03d199a7b202d61423587ca7c72e00c0df439 -rfdac759dbbec54375d4b3ebb21aebcc684ac1aed --- dialin/hd/ui_proxy.py (.../ui_proxy.py) (revision 13a03d199a7b202d61423587ca7c72e00c0df439) +++ dialin/hd/ui_proxy.py (.../ui_proxy.py) (revision fdac759dbbec54375d4b3ebb21aebcc684ac1aed) @@ -1,24 +1,25 @@ ########################################################################### # -# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +# Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. # -# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN -# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. # -# @file ui.py +# @file ui_proxy.py # -# @date 31-Mar-2020 -# @author P. Lucia +# @author (last) Peter Lucia +# @date (last) 28-Aug-2020 +# @author (original) Sean +# @date (original) 15-Apr-2020 # -# @brief -# -# ############################################################################ from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.conversions import integer_to_bytearray, float_to_bytearray import struct from ..utils.base import _AbstractSubSystem, _publish +from collections import OrderedDict +from logging import Logger class HDUIProxy(_AbstractSubSystem): @@ -59,6 +60,23 @@ RESPONSE_REJECTED = 0 RESPONSE_ACCEPTED = 1 + REQUEST_REJECT_REASON_NONE = 0 + REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE = 1 + REQUEST_REJECT_REASON_TIMEOUT_WAITING_FOR_USER_CONFIRM = 2 + REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE = 3 + REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE = 4 + REQUEST_REJECT_REASON_TREATMENT_TOO_CLOSE_TO_FINISHED = 5 + REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE = 6 + REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT = 7 + REQUEST_REJECT_REASON_BLOOD_FLOW_OUT_OF_RANGE = 8 + REQUEST_REJECT_REASON_DIAL_FLOW_OUT_OF_RANGE = 9 + REQUEST_REJECT_REASON_DIAL_VOLUME_OUT_OF_RANGE = 10 + REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE = 11 + REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE = 12 + REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_MINIMUM = 13 + REQUEST_REJECT_REASON_UF_NOT_IN_PROGESS = 14 + REQUEST_REJECT_REASON_UF_NOT_PAUSED = 15 + REQUEST_REJECT_REASON_SALINE_BOLUS_IN_PROGRESS = 16 # start treatment command IDs START_TREATMENT_CMD_INITIATE_TREATMENT_WORKFLOW = 0 START_TREATMENT_CMD_CANCEL_TREATMENT_WORKFLOW = 1 @@ -74,6 +92,16 @@ START_POS_BUILD = END_POS_MICRO END_POS_BUILD = START_POS_BUILD + 2 + # FPGA + START_POS_FPGA_ID = END_POS_BUILD + END_POS_FPGA_ID = START_POS_FPGA_ID + 1 + START_POS_FPGA_MAJOR = END_POS_FPGA_ID + END_POS_FPGA_MAJOR = START_POS_FPGA_MAJOR + 1 + START_POS_FPGA_MINOR = END_POS_FPGA_MAJOR + END_POS_FPGA_MINOR = START_POS_FPGA_MINOR + 1 + START_POS_FPGA_LAB = END_POS_FPGA_MINOR + END_POS_FPGA_LAB = START_POS_FPGA_LAB + 1 + # HD update on valid treatment parameter ranges message field positions START_POS_MIN_TREAT_TIME = DenaliMessage.PAYLOAD_START_INDEX END_POS_MIN_TREAT_TIME = START_POS_MIN_TREAT_TIME + 4 @@ -138,15 +166,15 @@ START_POS_BLD_DIAL_CHG_RSP_DIAL_RATE = END_POS_BLD_DIAL_CHG_RSP_BLD_RATE END_POS_BLD_DIAL_CHG_RSP_DIAL_RATE = START_POS_BLD_DIAL_CHG_RSP_DIAL_RATE + 4 - def __init__(self, can_interface): + def __init__(self, can_interface, logger: Logger): """ @param can_interface: the denali can interface object """ super().__init__() - self.can_interface = can_interface + self.logger = logger # register function to handle HD response to UF change requests if self.can_interface is not None: @@ -155,8 +183,8 @@ self.MSG_ID_UF_SETTINGS_CHANGE_RESPONSE_FROM_HD, self._handler_uf_change_response) self.can_interface.register_receiving_publication_function(channel_id, - self.MSG_ID_UF_SETTINGS_CHANGE_CONFIRM_RESPONSE_FROM_HD, - self.handler_uf_change_confirm_response) + self.MSG_ID_UF_SETTINGS_CHANGE_CONFIRM_RESPONSE_FROM_HD, + self._handler_uf_change_confirm_response) self.can_interface.register_receiving_publication_function(channel_id, self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_RESPONSE_FROM_HD, self._handler_treatment_duration_change_response) @@ -172,6 +200,8 @@ # initialize variables that will be populated by HD version response self.hd_version = None + self.fpga_version = None + # initialize treatment parameters that Dialin user sets self.blood_flow_rate = 0 self.dialysate_flow_rate = 0 @@ -236,6 +266,12 @@ self.target_blood_flow_rate = 0 self.target_dialysate_flow_rate = 0 + self.reject_reasons = OrderedDict() + for attr in dir(self): + if not callable(getattr(self, attr)) and attr.startswith("REQUEST_REJECT"): + self.reject_reasons[attr] = getattr(self, attr) + self.reject_reasons = OrderedDict(sorted(self.reject_reasons.items(), key=lambda key: key[1])) + def get_hd_version(self): """ Gets the hd version @@ -244,6 +280,14 @@ """ return self.hd_version + def get_fpga_version(self): + """ + Gets the fpga version + + @return: the fpga version + """ + return self.fpga_version + def get_min_treatment_duration_min(self): """ Gets the min treatment duration @@ -308,6 +352,14 @@ """ return self.duration_change_reject_reason + def get_reject_reasons(self): + """ + Gets all possible reject reasons + + @return: OrderedDict(), {"": ... } + """ + return self.reject_reasons + def get_duration_change_time_min(self): """ Gets the duration change time @@ -328,7 +380,7 @@ """ Gets the uf change succeeded status - @return: The uf change succeeded status + @return: True if succeeded, False otherwise """ return self.uf_change_succeeded @@ -412,6 +464,7 @@ @_publish([ "hd_version" + "fpga_version" ]) def _handler_hd_version(self, message): """ @@ -434,11 +487,23 @@ build = struct.unpack('H', bytearray( message['message'][self.START_POS_BUILD:self.END_POS_BUILD])) - if len(major) > 0 and len(minor) > 0 and len(micro) > 0 and len(build) > 0: + fpga_id = struct.unpack('B', bytearray( + message['message'][self.START_POS_FPGA_ID:self.END_POS_FPGA_ID])) + fpga_major = struct.unpack('B', bytearray( + message['message'][self.START_POS_FPGA_MAJOR:self.END_POS_FPGA_MAJOR])) + fpga_minor = struct.unpack('B', bytearray( + message['message'][self.START_POS_FPGA_MINOR:self.END_POS_FPGA_MINOR])) + fpga_lab = struct.unpack('B', bytearray( + message['message'][self.START_POS_FPGA_LAB:self.END_POS_FPGA_LAB])) + + if all([len(each) > 0 for each in [major, minor, micro, build]]): self.hd_version = f"v{major[0]}.{minor[0]}.{micro[0]}-{build[0]}" - print(self.hd_version) - return self.hd_version + self.logger.debug(f"HD VERSION: {self.hd_version}") + if all([len(each) > 0 for each in [fpga_major, fpga_minor, fpga_id, fpga_lab]]): + self.fpga_version = f"ID: {fpga_id[0]} v{fpga_major[0]}.{fpga_minor[0]}.{fpga_lab[0]}" + self.logger.debug(f"HD FPGA VERSION: {self.fpga_version}") + @_publish([ "min_treatment_duration_min", "max_treatment_duration_min", @@ -650,18 +715,23 @@ self.uf_old_rate_ml_min = ort[0] - def handler_uf_change_confirm_response(self, message): + @_publish(["uf_change_succeeded", + "uf_change_reject_reason", + "uf_change_volume_ml", + "uf_change_time_min", + "uf_change_rate_ml_min"]) + def _handler_uf_change_confirm_response(self, message): """ Handler for response from HD regarding UF change confirmation. - \param message: response message from HD regarding confirmed ultrafiltration settings change.\n + @param message: response message from HD regarding confirmed ultrafiltration settings change.\n BOOL Accepted \n U32 RejectReason (if not accepted) F32 UF Volume (mL) - converted to Liters \n U32 treatment Time (min) \n F32 UF Rate (mL/min) \n - \returns none + @return: None """ rsp = struct.unpack('i', bytearray( message['message'][self.START_POS_UF_CNF_RSP_RESP:self.END_POS_UF_CNF_RSP_RESP])) @@ -694,7 +764,7 @@ message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, message_id=self.MSG_ID_UI_CHECKIN_WITH_HD) - print("Sending ui checkin w/ HD") + self.logger.debug("Sending ui checkin w/ HD") self.can_interface.send(message, 0) @@ -709,7 +779,7 @@ message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, message_id=self.MSG_ID_UI_REQUEST_HD_VERSION) - print("Sending ui request for version to HD") + self.logger.debug("Sending ui request for version to HD") self.can_interface.send(message, 0) @@ -731,7 +801,7 @@ str_cmd = "pause" else: str_cmd = "resume" - print("Sending UF " + str_cmd + " command.") + self.logger.debug("Sending UF " + str_cmd + " command.") self.can_interface.send(message, 0) @@ -754,7 +824,7 @@ message_id=self.MSG_ID_UF_SETTINGS_CHANGE_REQUEST_BY_USER, payload=volume) - print("Sending UF settings change request.") + self.logger.debug("Sending UF settings change request.") self.can_interface.send(message, 0) @@ -781,7 +851,7 @@ message_id=self.MSG_ID_UF_SETTINGS_CHANGE_REQUEST_BY_USER, payload=payload) - print("Sending UF settings change request.") + self.logger.debug("Sending UF settings change request.") self.can_interface.send(message, 0) @@ -808,7 +878,7 @@ message_id=self.MSG_ID_UF_SETTINGS_CHANGE_CONFIRMED_BY_USER, payload=payload) - print("Sending UF settings change confirmation.") + self.logger.debug("Sending UF settings change confirmation.") self.can_interface.send(message, 0) @@ -828,7 +898,7 @@ message_id=self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_REQUEST, payload=payload) - print("Sending treatment duration setting change request.") + self.logger.debug("Sending treatment duration setting change request.") # Send message self.can_interface.send(message, 0) @@ -852,7 +922,7 @@ message_id=self.MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_REQUEST_BY_USER, payload=payload) - print("Sending blood & dialysate flow rate settings change request.") + self.logger.debug("Sending blood & dialysate flow rate settings change request.") self.can_interface.send(message, 0) Index: tests/test_uf.py =================================================================== diff -u -ra2add7d5c768ab21eba0c61e5ec5c7e84b75eeab -rfdac759dbbec54375d4b3ebb21aebcc684ac1aed --- tests/test_uf.py (.../test_uf.py) (revision a2add7d5c768ab21eba0c61e5ec5c7e84b75eeab) +++ tests/test_uf.py (.../test_uf.py) (revision fdac759dbbec54375d4b3ebb21aebcc684ac1aed) @@ -27,6 +27,9 @@ dg = DG() sleep(2) +# hd.ui.cmd_ui_start_treatment_request() +# exit(0) + # log in to HD and DG as tester # if hd.cmd_log_in_to_hd() == 0: # exit(1)