Index: dialin/common/msg_defs.py =================================================================== diff -u -re6324506ed792e22dc419249a6e9225e602642c6 -r0392b232e0f257fb6946f6e8e2cdf4eadd05974d --- dialin/common/msg_defs.py (.../msg_defs.py) (revision e6324506ed792e22dc419249a6e9225e602642c6) +++ dialin/common/msg_defs.py (.../msg_defs.py) (revision 0392b232e0f257fb6946f6e8e2cdf4eadd05974d) @@ -159,6 +159,7 @@ MSG_ID_DIAL_OUT_PUMP_HOME_CMD = 0x8039 # Dialysate outlet pump home command MSG_ID_SUPER_CLEAR_ALARMS_CMD = 0x803A # Clears all alarms (even if non-recoverable or fault) MSG_ID_HD_REQUEST_CALIBRATION_DATA = 0x803B # Requests calibration data from HD + MSG_ID_HD_ERASE_CALIBRATION_DATA = 0x803C # Requests calibration data on HD be erased MSG_ID_DG_TESTER_LOGIN_REQUEST = 0XA000 # DG tester log-in MSG_ID_DG_ALARM_STATE_OVERRIDE = 0xA001 # DG alarm state override message ID @@ -194,13 +195,18 @@ MSG_ID_HEAT_DISINFECT_NO_OF_CYCLES_TO_RUN = 0xA020 # Heat disinfection number of cycles to run MSG_ID_HEAT_DISINFECT_PUBLISH_INTERVAL_OVERRIDE = 0xA021 # Heat disinfection data publish interval override request MSG_ID_DG_SOFTWARE_RESET_REQUEST = 0xA022 # DG reset request - MSG_ID_DG_OPERATION_MODE_REQUEST = 0xA023 # DG operation mode request + MSG_ID_DG_CONCENTRATE_PUMP_MEASURED_SPEED_OVERRIDE = 0xA023 # DG concentrate pump measured speed override request MSG_ID_CONCENTRATE_PUMP_TARGET_SPEED_OVERRIDE = 0xA024 # Concentrate pumps' target speed override request MSG_ID_UV_REACTORS_DATA_PUBLISH_INTERVAL_OVERRIDE = 0xA025 # UV reactors data publish interval override MSG_ID_CONCENTRATE_PUMP_STATE_CHANGE_REQUEST = 0xA026 # Concentrate pumps' state change request (on / off) MSG_ID_CONCENTRATE_PUMP_PUBLISH_INTERVAL_OVERRIDE = 0xA027 # Concentrate pumps' data publish interval override request - MSG_ID_DG_START_STOP_UV_REACTORS_OVERRIDE = 0xA028 # DG start/stop UV reactors override request + MSG_ID_DG_START_STOP_INLET_UV_REACTOR = 0xA028 # DG start/stop inlet UV reactor MSG_ID_DG_REQUEST_CALIBRATION_DATA = 0xA029 # Requests calibration data from DG + MSG_ID_DG_FANS_DATA_PUBLISH_OVERRIDE = 0xA02A # Fans data publish interval override request + MSG_ID_DG_START_STOP_OUTLET_UV_REACTOR = 0xA02B # DG start/stop outlet UV reactor + MSG_ID_DG_UV_REACTORS_HEALTH_OVERRIDE = 0xA02C # DG UV reactors health override request + MSG_ID_DG_THERMISTORS_DATA_PUBLISH_INTERVAL_OVERRIDE = 0xA02D # DG thermistors data publish interval override + MSG_ID_DG_THERMISTORS_VALUE_OVERRIDE = 0xA02E # DG thermistors value override MSG_ID_HD_DEBUG_EVENT = 0xFFF1 # HD debug event text to be logged in event log MSG_ID_DG_DEBUG_EVENT = 0xFFF2 # DG debug event text to be logged in event log @@ -235,7 +241,7 @@ NUM_OF_REQUEST_REJECT_REASONS = 23 -class MsgFldPositions: +class MsgFieldPositions: # Generic response msg field byte positions (where 32-bit data fields are used) START_POS_FIELD_1 = DenaliMessage.PAYLOAD_START_INDEX END_POS_FIELD_1 = START_POS_FIELD_1 + 4 Index: dialin/ui/hd_simulator.py =================================================================== diff -u -re6324506ed792e22dc419249a6e9225e602642c6 -r0392b232e0f257fb6946f6e8e2cdf4eadd05974d --- dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision e6324506ed792e22dc419249a6e9225e602642c6) +++ dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 0392b232e0f257fb6946f6e8e2cdf4eadd05974d) @@ -17,10 +17,13 @@ import struct from time import sleep from typing import List +import time +import subprocess +from . import utils, messageBuilder from .hd_simulator_alarms import HDProxyAlarms from ..common.msg_defs import RequestRejectReasons, MsgIds -from ..hd.buttons import HDButtons +from ..common.hd_defs import HDDefs from ..protocols.CAN import (DenaliMessage, DenaliCanMessenger, DenaliChannels) @@ -30,8 +33,6 @@ YES = 1 NO = 0 - - class TreatmentParameterRejections: def __init__(self): self.param_request_valid = RequestRejectReasons.REQUEST_REJECT_REASON_NONE @@ -74,6 +75,88 @@ self.__dict__[attr] = RequestRejectReasons.REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE +class TXStates: + # Saline states + SALINE_BOLUS_STATE_IDLE = 0 # No saline bolus delivery is in progress + SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP = 1 # Wait for pumps to stop before starting bolus + SALINE_BOLUS_STATE_IN_PROGRESS = 2 # A saline bolus delivery is in progress + SALINE_BOLUS_STATE_MAX_DELIVERED = 3 # Maximum saline bolus volume reached - no more saline bolus deliveries allowed + + # UF states + UF_START_STATE = 0 # Start state of the ultrafiltration state machine + UF_PAUSED_STATE = 1 # Paused state of the ultrafiltration state machine + UF_RUNNING_STATE = 2 # Running state of the ultrafiltration state machine + UF_OFF_STATE = 3 # Completed/off state of the ultrafiltration state machine + UF_COMPLETED_STATE = 4 # Completed state of ultrafiltration state machine + + # Sub Mode + # UI only cares about the actual state and _NOT could be any other state + TREATMENT_DIALYSIS_STATE_NOT = 0 + TREATMENT_DIALYSIS_STATE = 1 + + # Heparin states + HEPARIN_STATE_OFF = 0 # No heparin delivery is in progress + HEPARIN_STATE_PAUSED = 1 # Heparin delivery paused + HEPARIN_STATE_INITIAL_BOLUS = 2 # Initial heparin bolus delivery in progress + HEPARIN_STATE_DISPENSING = 3 # Gradual heparin dispensing in progress + HEPARIN_STATE_COMPLETED = 4 # Heparin delivery stopped due to the set stop time before treatment end + HEPARIN_STATE_EMPTY = 5 # Heparin Syringe empty + + +class EResponse: + Rejected = 0 + Accepted = 1 + + +class GuiActionType: + Unknown = 0 + PowerOff = 1 + KeepAlive = 7 + BloodFlow = 5 + DialysateInletFlow = 8 + DialysateOutletFlow = 11 + TreatmentTime = 13 + PowerOffBroadcast = 14 + + AlarmStatus = 2 + AlarmTriggered = 3 + AlarmCleared = 4 + + PressureOcclusion = 9 + + DGDrainPumpData = 36 + DGHeatersData = 44 + LoadCellReadings = 12 + DGPressuresData = 32 + TemperatureSensors = 45 + + CanBUSFaultCount = 2457 + HDDebugText = 0xFFF1 + DGDebugText = 0xFFF2 + + AdjustBloodDialysateReq = 23 + AdjustBloodDialysateRsp = 24 + + AdjustDurationReq = 22 + AdjustDurationRsp = 27 + + AdjustUltrafiltrationStateReq = 16 + AdjustUltrafiltrationStateRsp = 65 + AdjustUltrafiltrationEditReq = 17 + AdjustUltrafiltrationEditRsp = 19 + AdjustUltrafiltrationConfirmReq = 21 + AdjustUltrafiltrationConfirmRsp = 46 + + AdjustPressuresLimitsReq = 70 + AdjustPressuresLimitsRsp = 71 + + TreatmentRanges = 26 + + String = 65279 + Acknow = 65535 + AcknowGeneric = 0 # Generic Acknowledgment is not a unique message ID and inherits its Id from the actual message. Zero is a placeholder + + class HDSimulator(_AbstractSubSystem): NUM_TREATMENT_PARAMETERS = 18 @@ -135,7 +218,7 @@ return True - def cmd_send_treatment_parameter_manual_validation_response(self, rejections: int): + def cmd_send_treatment_parameter_manual_validation_response(self, rejections: List[int]): """ Sends a manually built treatment parameter validation response @@ -147,7 +230,7 @@ return False if not all([isinstance(each, int) for each in rejections]): - self.logger.error("Not all rejections are enums.") + self.logger.error("Not all rejections are the correct type.") return False payload = bytearray() @@ -266,12 +349,16 @@ request = struct.unpack('i', bytearray( message['message'][START_POS:END_POS]))[0] + if request == 0: self.logger.debug("Selecting treatment parameters") + self.cmd_send_hd_operation_mode(HDDefs.MODE_PRET.value) elif request == 1: self.logger.debug("Canceling treatment") + self.cmd_send_hd_operation_mode(HDDefs.MODE_STAN.value) elif request == 2: self.logger.debug("Starting treatment") + self.cmd_send_hd_operation_mode(HDDefs.MODE_TREA.value) self.cmd_send_start_treatment_response(YES, 0) @@ -282,7 +369,7 @@ @param response: 0=NO, 1=YES @return: None """ - print("Sending: {0}".format(response)) + self.logger.debug("Sending: {0}".format(response)) payload = integer_to_bytearray(response) payload += integer_to_bytearray(reason) @@ -293,6 +380,24 @@ self.can_interface.send(message, 0) + def cmd_send_hd_operation_mode(self, mode): + """ + Broadcasts the current HD operation mode + @param: (int) mode + @return: None + """ + + if not isinstance(mode, int): + raise ValueError("Provided mode is not of type 'int'") + + payload = integer_to_bytearray(mode) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_HD_OP_MODE.value, + payload=payload) + + self.can_interface.send(message, 0) + def cmd_send_uf_treatment_response(self, accepted, reason, volume): """ Sends the uf volume adjustment response message in pre-treatment @@ -324,6 +429,7 @@ payload=payload) self.can_interface.send(message, 0) + def _handler_ui_end_treatment(self, message): """ @@ -379,3 +485,1087 @@ """ self.logger.info("Test Completed") + def cmd_send_acknowledge_hd(self): + """ + the acknowledge from HD + :return: none + """ + + payload = ["A5", "01", "00", "FF", "FF", "00", "19", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.hd_to_ui_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_send_acknowledge_ui(self): + """ + the acknowledge from UI + :return: none + """ + + payload = ["A5", "01", "00", "FF", "FF", "00", "19", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.ui_to_hd_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_send_checkin_dg(self): + """ + check-in (keep alive) message from DG + :return: none + """ + + payload = ["A5", "01", "00", "06", "00", "00", "76", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.dg_to_hd_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_show_poweroff_dialog(self): + """ + the message from HD to UI to show the power off dialog + :return: none + """ + + payload = ["A5", "01", "00", "01", "00", "01", "00", "38"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.hd_to_ui_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_hide_poweroff_dialog(self): + """ + the message from HD to UI to hide the power off dialog + :return: none + """ + + payload = ["A5", "01", "00", "01", "00", "01", "01", "09"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.hd_to_ui_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_show_poweroff_notification_dialog(self): + """ + the message from HD to UI to show the shutting down notification box + :return: none + """ + + payload = ["A5", "01", "00", "0E", "00", "00", "24", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.hd_sync_broadcast_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_show_poweroff_rejection_dialog(self): + """ + the message from HD to UI to show the power off dialog + :return: none + """ + + payload = ["A5", "01", "00", "01", "00", "01", "02", "5A"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": DenaliChannels.hd_to_ui_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + @staticmethod + def wait_for_message_to_be_sent(delay=0.050): + """ + After each multi-frame message put a 50ms sleep, time.sleep(0.1) + it seems it's needed otherwise the test will check a value which has not been received yet. + :@param delay: the number of seconds to wait + :return: none + """ + time.sleep(delay) + + @staticmethod + def build_dg_debug_text(vText): + """ + the debug text message from DG builder method + :param vText: (str) the debug text + :return: none + """ + message_length = 40 + txt = messageBuilder.textToByte(vText, message_length) # + 1 null term + msg = messageBuilder.buildMessage(GuiActionType.DGDebugText, 1 * (message_length + 1), False, txt) + return messageBuilder.toFrames(msg) + + @staticmethod + def build_hd_debug_text(vText): + """ + the debug text message from HD builder method + :param vText: (str) the debug text + :return: none + """ + message_length = 40 + txt = messageBuilder.textToByte(vText, message_length) # + 1 null term + msg = messageBuilder.buildMessage(GuiActionType.HDDebugText, 1 * (message_length + 1), False, txt) + return messageBuilder.toFrames(msg) + + @staticmethod + def cmd_set_hd_debug_text(debug_text): + """ + the debug text message from HD setter/sender method + :param debug_text: (str) the debug text + :return: none + """ + + frames = HDSimulator.build_hd_debug_text(debug_text) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '020#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + @staticmethod + def cmd_set_dg_debug_text(debug_text): + """ + the debug text message from DG setter/sender method + :param debug_text: (str) the debug text + :return: none + """ + frames = HDSimulator.build_dg_debug_text(debug_text) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '070#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + def cmd_set_treatment_parameter_ranges(self, vMinTreatmentDuration, vMaxTreatmentDuration, vMinUFVolume, vMaxUFVolume, + vMinDialysateFlowRate, vMaxDialysateFlowRate): + """ + The Treatment adjustment parameter ranges data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(F32) | #4:(F32) | #5:(U32) | #6:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: | + |0x1A00| 0x020 | 6 | 1/60 Hz| Y | HD | UI | Treatment adjustment param ranges Data | \ref Data::mDuration_Min | \ref Data::mDuration_Max | \ref Data::mUltrafiltration_Volume_Min | \ref Data::mUltrafiltration_Volume_Max | \ref Data::mDialysate_Flow_Min | \ref Data::mDialysate_Flow_Max | + + :param vMinTreatmentDuration: (int) Min Treatment Duration + :param vMaxTreatmentDuration: (int) Max Treatment Duration + :param vMinUFVolume: (float) Min UF Volume + :param vMaxUFVolume: (float) Max UF Volume + :param vMinDialysateFlowRate: (int) Min Dialysate Flow Rate + :param vMaxDialysateFlowRate: (int) Max Dialysate Flow Rate + :return: None + """ + + payload = integer_to_bytearray(vMinTreatmentDuration) + payload += integer_to_bytearray(vMaxTreatmentDuration) + payload += float_to_bytearray(vMinUFVolume) + payload += float_to_bytearray(vMaxUFVolume) + payload += integer_to_bytearray(vMinDialysateFlowRate) + payload += integer_to_bytearray(vMaxDialysateFlowRate) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_TREATMENT_PARAM_CHANGE_RANGES.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_blood_flow_rate(self, vFlowSetPt, vMeasFlow, vRotSpd, vMotSpd, vMCSpd, vMCCurr, vPWM): + """ + The Blood Flow Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(S32) | #2:(F32) | #3:(F32) | #4:(F32) | #5:(F32) | #6:(F32) | #7:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: |:--: | + |0x0500| 0x040 | 7 | 1 Hz | N | HD | All | Blood Flow Data | \ref Data::mFlowSetPoint | \ref Data::mMeasuredFlow | \ref Data::mRotorSpeed | \ref Data::mMotorSpeed | \ref Data::mMotorCtlSpeed | \ref Data::mMotorCtlCurrent | \ref Data::mPWMDutyCycle | + + :param vFlowSetPt: (signed int) Flow Set Point + :param vMeasFlow: (float) Measured Flow + :param vRotSpd: (float) Rot Speed + :param vMotSpd: (float) Motor Speed + :param vMCSpd: (float) MC Speed + :param vMCCurr: (float) MC Current + :param vPWM: (float) PWM + :return: None + """ + + payload = integer_to_bytearray(vFlowSetPt) + payload += float_to_bytearray(vMeasFlow) + payload += float_to_bytearray(vRotSpd) + payload += float_to_bytearray(vMotSpd) + payload += float_to_bytearray(vMCSpd) + payload += float_to_bytearray(vMCCurr) + payload += float_to_bytearray(vPWM) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_BLOOD_FLOW_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_dialysate_flow_rate(self, vFlowSetPt, vMeasFlow, vRotSpd, vMotSpd, vMCSpd, vMCCurr, vPWM): + """ + The Dialysate Flow Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(S32) | #2:(F32) | #3:(F32) | #4:(F32) | #5:(F32) | #6:(F32) | #7:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: |:--: | + |0x0800| 0x040 | 7 | 1 Hz | N | HD | All | Dialysate Flow Data | mFlowSetPoint | mMeasuredFlow | mRotorSpeed | mMotorSpeed | mMotorCtlSpeed | mMotorCtlCurrent | mPWMDutyCycle | + + :param vFlowSetPt: (signed int) Flow Set Point + :param vMeasFlow: (float) Measured Flow + :param vRotSpd: (float) Rot Speed + :param vMotSpd: (float) Motor Speed + :param vMCSpd: (float) MC Speed + :param vMCCurr: (float) MC Current + :param vPWM: (float) PWM + :return: None + """ + + payload = integer_to_bytearray(vFlowSetPt) + payload += float_to_bytearray(vMeasFlow) + payload += float_to_bytearray(vRotSpd) + payload += float_to_bytearray(vMotSpd) + payload += float_to_bytearray(vMCSpd) + payload += float_to_bytearray(vMCCurr) + payload += float_to_bytearray(vPWM) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DIALYSATE_FLOW_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_treatment_adjust_blood_dialysate_response(self, accepted, reason, blood_rate, dialysate_flow_rate): + """ + The Blood/dialysate rate change Response message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | #4:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x1800| 0x020 | 6 | Rsp | Y | HD | UI | Blood/dialysate rate change Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mBloodRate | \ref Data::mDialysateRate | + + :param accepted: (int) boolean accept/reject response + :param reason: (int) rejection reason + :param blood_rate: (int) Blood Flow Rate + :param dialysate_flow_rate: (int) Dialysate Flow Rate + :return: None + """ + + if not isinstance(accepted, int): + accepted = int(accepted) + if not isinstance(reason, int): + reason = int(reason) + if not isinstance(blood_rate, int): + blood_rate = int(blood_rate) + if not isinstance(dialysate_flow_rate, int): + dialysate_flow_rate = int(dialysate_flow_rate) + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += integer_to_bytearray(blood_rate) + payload += integer_to_bytearray(dialysate_flow_rate) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_USER_BLOOD_DIAL_RATE_CHANGE_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_treatment_adjust_duration_response(self, vAccepted, vReason, vDuration, vUltrafiltration): + """ + The Treatment Duration change Response message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | #5:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x1B00| 0x020 | 6 | Rsp | Y | HD | UI | Treatment Duration change Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mDuration | \ref Data::mUFVolume | + + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vDuration: (int) Treatment Duration + :param vUltrafiltration: (float) Ultrafiltration Volume + :return: none + """ + + payload = integer_to_bytearray(vAccepted) + payload += integer_to_bytearray(vReason) + payload += integer_to_bytearray(vDuration) + payload += float_to_bytearray(vUltrafiltration) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_USER_TREATMENT_TIME_CHANGE_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_adjust_ultrafiltration_state_response(self, vAccepted, vReason, vState): + """ + the Treatment ultrafiltration adjustment response message setter/sender method + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vState: (int) Ultrafiltration State + :return: none + """ + + payload = integer_to_bytearray(vAccepted) + payload += integer_to_bytearray(vReason) + payload += integer_to_bytearray(vState) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_USER_UF_PAUSE_RESUME_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_adjust_ultrafiltration_accepted(self, vState): + """ + a convenient method for cmd_set_treatment_adjust_ultrafiltration_state_response which sends accept true + :return: none + """ + self.cmd_set_treatment_adjust_ultrafiltration_state_response(EResponse.Accepted, 0, vState) + + def cmd_set_treatment_adjust_ultrafiltration_rejected(self, vReason, vState): + """ + a convenient method for cmd_set_treatment_adjust_ultrafiltration_state_response which sends accept false + :return: none + """ + self.cmd_set_treatment_adjust_ultrafiltration_state_response(EResponse.Rejected, vReason, vState) + + def cmd_set_treatment_adjust_ultrafiltration_edit_response(self, vAccepted, vReason, vVolume, vDuration, vDurationDiff, + vRate, vRateDiff, vRateOld): + """ + the ultrafiltration volume change response message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #1:(U32) | #2:(U32) | #3:(F32) | #4:(U32) | #5:(F32) | #6:(U32) | #7:(U32) | #8:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: |:--: |:--: |:--: |:--: | + |0x1300| 0x020 | 6 | Rsp | Y | HD | UI | UF Vol. Change Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mVolume | \ref Data::mDuration | \ref Data::mRate | \ref Data::mDurationDiff | \ref Data::mRateDiff | \ref Data::mRateOld | + + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vVolume: (float) Ultrafiltration Volume + :param vDuration: (int) Treatment Duration + :param vDurationDiff: (int) Duration Difference + :param vRate: (float) Ultrafiltration Rate + :param vRateDiff: (float) Ultrafiltration Rate Difference + :param vRateOld: (float) Ultrafiltration Rate Old + :return: none + """ + + payload = integer_to_bytearray(vAccepted) + payload += integer_to_bytearray(vReason) + payload += float_to_bytearray(vVolume) + payload += integer_to_bytearray(vDuration) + payload += integer_to_bytearray(vDurationDiff) + payload += float_to_bytearray(vRate) + payload += float_to_bytearray(vRateDiff) + payload += float_to_bytearray(vRateOld) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_USER_UF_SETTINGS_CHANGE_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_adjust_ultrafiltration_edit_rejected(self, vReason): + """ + a convenient method for cmd_set_treatment_adjust_ultrafiltration_edit_response which only sends a rejection reason + and sends other values all as zero + :param vReason: (int) rejection reason + :return: none + """ + self.cmd_set_treatment_adjust_ultrafiltration_edit_response(0, vReason, 0, 0, 0, 0, 0, 0) + + def cmd_set_treatment_adjust_ultrafiltration_confirm_response(self, vAccepted, vReason, vVolume, vDuration, vRate): + """ + the ultrafiltration volume Change Confirmation Response message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(F32) | #4:(U32) | #5:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: | + |0x2E00| 0x020 | 6 | Rsp | Y | HD | UI | UF Vol. Change Confirmation Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mVolume | \ref Data::mDuration | \ref Data::mRate | + + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vVolume: (float) Ultrafiltration Volume + :param vDuration: (int) Treatment Duration + :param vRate: (float) Ultrafiltration Rate + :return: none + """ + + payload = integer_to_bytearray(vAccepted) + payload += integer_to_bytearray(vReason) + payload += float_to_bytearray(vVolume) + payload += integer_to_bytearray(vDuration) + payload += float_to_bytearray(vRate) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_USER_UF_SETTINGS_CHANGE_CONFIRMATION_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_adjust_ultrafiltration_confirm_rejected(self, vReason): + """ + a convenient method for cmd_set_treatment_adjust_ultrafiltration_confirm_response which only sends a rejection reason + and sends other values all as zero + + :param vReason: (int) rejection reason + :return: none + """ + self.cmd_set_treatment_adjust_ultrafiltration_confirm_response(0, vReason, 0, 0, 0) + + def cmd_set_treatment_time(self, vSecsTotal, vSecsElap, vSecsRem=None): + """ + the Treatment Time Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x0D00| 0x040 | 7 | 1 Hz | N | HD | All | Treatment Time Data | \ref Data::mTotal | \ref Data::mElapsed | \ref Data::mRemaining | + + :param vSecsTotal: (int) Treatment Total Duration in Seconds + :param vSecsElap: (int) Treatment Total Elapsed Time in Seconds + :param vSecsRem: (int) Treatment Remaining Time in Seconds + :return: none + """ + if vSecsRem is None: + vSecsRem = vSecsTotal - vSecsElap + + payload = integer_to_bytearray(vSecsTotal) + payload += integer_to_bytearray(vSecsElap) + payload += integer_to_bytearray(vSecsRem) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_TREATMENT_TIME.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_ultrafiltration_outlet_flow_data(self, vRefUFVol, vMeasUFVol, vRotSpd, vMotSpd, vMCSpd, vMCCurr, vPWM): + """ + the Outlet Flow Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | #4:(F32) | #5:(F32) | #6:(F32) | #7:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: |:--: | + |0x0B00| 0x040 | 7 | 1 Hz | N | HD | All | Outlet Flow Data | \ref Data::mRefUFVol | \ref Data::mMeasUFVol | \ref Data::mRotorSpeed | \ref Data::mMotorSpeed | \ref Data::mMotorCtlSpeed | \ref Data::mMotorCtlCurrent | \ref Data::mPWMDtCycle | + + :param vRefUFVol: (float) Ref UF Volume + :param vMeasUFVol: (float) Measured UF Volume + :param vRotSpd: (float) Rot Speed + :param vMotSpd: (float) Motor Speed + :param vMCSpd: (float) MC Speed + :param vMCCurr: (float) MC Current + :param vPWM: (float) PWM + :return: none + """ + + payload = float_to_bytearray(vRefUFVol) + payload += float_to_bytearray(vMeasUFVol) + payload += float_to_bytearray(vRotSpd) + payload += float_to_bytearray(vMotSpd) + payload += float_to_bytearray(vMCSpd) + payload += float_to_bytearray(vMCCurr) + payload += float_to_bytearray(vPWM) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DIALYSATE_OUT_FLOW_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_pressure_occlusion_data(self, vArterialPressure, vVenousPressure, vBloodPumpOcclusion, vDialysateInletPumpOcclusion, + vDialysateOutletPumpOcclusion): + """ + the Pressure/Occlusion Data messages setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(U32) | #4:(U32) | #5:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: | + |0x0900| 0x040 | 7 | 1 Hz | N | HD | All | PressureOcclusion Data | \ref Data::mArterialPressure | \ref Data::mVenousPressure | \ref Data::mBloodPumpOcclusion | \ref Data::mDialysateInletPumpOcclusion | \ref Data::mDialysateOutletPumpOcclusion | + + :param vArterialPressure: (float) Arterial Pressure + :param vVenousPressure: (float) Venous Pressure + :param vBloodPumpOcclusion: (uint) Blood Pump Occlusion + :param vDialysateInletPumpOcclusion: (uint) Dialysate Inlet Pump Occlusion + :param vDialysateOutletPumpOcclusion: (uint) Dialysate Outlet Pump Occlusion + :return: none + """ + + payload = float_to_bytearray(vArterialPressure) + payload += float_to_bytearray(vVenousPressure) + payload += integer_to_bytearray(vBloodPumpOcclusion) + payload += integer_to_bytearray(vDialysateInletPumpOcclusion) + payload += integer_to_bytearray(vDialysateOutletPumpOcclusion) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_PRESSURE_OCCLUSION_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_ro_pump_data(self, vSetPtPressure, vFlowRate, vPWM): + """ + the DG RO Pump Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(F32) | #3:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x1F00| 0x080 | 8 | 1 Hz | N | DG | All | DG RO Pump Data | \ref Data::mPressure | \ref Data::mFlowRate | \ref Data::mPWM | :param vSetPtPressure: + + :param vSetPtPressure: (int) set Point Pressure + :param vFlowRate: (float) Flow Rate + :param vPWM: (float) PWM + :return: none + """ + + payload = integer_to_bytearray(vSetPtPressure) + payload += float_to_bytearray(vFlowRate) + payload += float_to_bytearray(vPWM) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_RO_PUMP_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_pressures_data(self, vROInletPSI, vROOutletPSI, vDrainInletPSI, vDrainOutletPSI): + """ + the DG Pressures Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | #4:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x2000| 0x080 | 8 | 1 Hz | N | DG | All | DG Pressures Data | \ref Data::mROInletPSI | \ref Data::mROOutletPSI | \ref Data::mDrainInletPSI | \ref Data::mDrainOutletPSI | + + :param vROInletPSI: (float) RO Inlet PSI + :param vROOutletPSI: (float) RO Outlet PSI + :param vDrainInletPSI: (float) Drain Inlet PSI + :param vDrainOutletPSI: (float) Drain Outlet PSI + :return: none + """ + + payload = float_to_bytearray(vROInletPSI) + payload += float_to_bytearray(vROOutletPSI) + payload += float_to_bytearray(vDrainInletPSI) + payload += float_to_bytearray(vDrainOutletPSI) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DG_PRESSURES_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_drain_pump_data(self, vSetPtRPM, vDACValue): + """ + the DG Drain Pump Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: | + |0x2400| 0x080 | 8 | 1 Hz | N | DG | All | DG Drain Pump Data | \ref Data::mRPM | \ref Data::mDAC | + + :param vSetPtRPM: (int) Set Point RPM + :param vDACValue: (int) DAC Value + :return: none + """ + + payload = integer_to_bytearray(vSetPtRPM) + payload += integer_to_bytearray(vDACValue) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DRAIN_PUMP_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_operation_mode(self, vDGOpMode): + """ + the DG Operation Mode Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x2700| 0x080 | 8 | 1 Hz | N | DG | All | DG Operation Mode Data | \ref Data::mOpMode | + + :param vDGOpMode: (int) DG Operation Mode + :return: none + """ + + payload = integer_to_bytearray(vDGOpMode) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DG_OP_MODE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_reservoir_data(self, vActiveReservoir, vFillToVolML, vDrainToVolML): + """ + the DG Reservoir Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x2800| 0x080 | 8 | 1 Hz | N | DG | All | DG Reservoir Data | \ref Data::mActiveReservoir | \ref Data::mFillToVol | \ref Data::mDrainToVol | + + :param vActiveReservoir: (int) Active Reservoir + :param vFillToVolML: (int) Fill To Volume ML + :param vDrainToVolML: (int) Drain To Vol ML + :return: none + """ + + payload = integer_to_bytearray(vActiveReservoir) + payload += integer_to_bytearray(vFillToVolML) + payload += integer_to_bytearray(vDrainToVolML) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DG_RESERVOIR_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_valves_states(self, vValvesStates): + """ + the DG Valves States Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U16) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x2A00| 0x080 | 8 | 2 Hz | N | DG | All | DG Valves States Data | \ref Data::mStates | + + :param vValvesStates: (int)Valves states + :return: none + """ + + payload = integer_to_bytearray(vValvesStates) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DG_VALVES_STATES.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_heaters_data(self, vMainPriMaryDC, vSmallPrimaryDC, vTrimmerDC): + """ + the DG Heaters Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x2C00| 0x080 | 8 | 2 Hz | N | DG | All | DG Heaters Data | \ref Data::mMainPrimaryDC | \ref Data::mSmallPrimaryDC | \ref Data::mTrimmerDC | + + :param vMainPriMaryDC: (int) Main PriMary DC + :param vSmallPrimaryDC: (int) Small Primary DC + :param vTrimmerDC: (int) Trimmer DC + :return: none + """ + + payload = integer_to_bytearray(vMainPriMaryDC) + payload += integer_to_bytearray(vSmallPrimaryDC) + payload += integer_to_bytearray(vTrimmerDC) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DG_HEATERS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_load_cell_readings_data(self, vRs1Prim, vRs1Bkup, vRs2Prim, vRs2Bkup): + """ + The DG Load Cell Readings Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | #4:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x0C00| 0x080 | 8 | 10 Hz | N | DG | All | DG Load Cell Readings Data | \ref Data::mReservoir1Prim | \ref Data::mReservoir1Bkup | \ref Data::mReservoir2Prim | \ref Data::mReservoir2Bkup | + :param vRs1Prim: (float) Reservoir 1 Primary + :param vRs1Bkup: (float) Reservoir 1 Backup + :param vRs2Prim: (float) Reservoir 2 Primary + :param vRs2Bkup: (float) Reservoir 2 Backup + :return: none + """ + + payload = float_to_bytearray(vRs1Prim) + payload += float_to_bytearray(vRs1Bkup) + payload += float_to_bytearray(vRs2Prim) + payload += float_to_bytearray(vRs2Bkup) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_LOAD_CELL_READINGS.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dg_temperatures_data(self, vInletPrimaryHeater, vOutletPrimaryHeater, vConductivitySensor1, vConductivitySensor2, + vOutletRedundancy, vInletDialysate, vPrimaryHeaterThermocouple, vTrimmerHeaterThermocouple, + vPrimaryHeaterColdJunction, vTrimmerHeaterColdJunction, vPrimaryHeaterInternalTemperature, + vTrimmerHeaterInternalTemperature): + """ + the DG Temperatures Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x2D00| 0x080 | 8 | 2 Hz | N | DG | All | DG Temperatures Data | \ref Data::mInletPrimaryHeater | \ref Data::mOutletPrimaryHeater | \ref Data::mConductivitySensor1 | + + | #4:(F32) | #5:(F32) | #6:(F32) | #7:(F32) | #8:(F32) | + |:--: |:--: |:--: |:--: |:--: | + | \ref Data::mConductivitySensor2 | \ref Data::mOutletRedundancy | \ref Data::mInletDialysate | \ref Data::mPrimaryHeaterThermoCouple | \ref Data::mTrimmerHeaterThermoCouple | + + | #9:(F32) | #10:(F32) | #11:(F32) | #12:(F32) | + | :--: |:--: |:--: |:--: | + | \ref Data::mPrimaryHeaterColdJunction | \ref Data::mTrimmerHeaterColdJunction | \ref Data::mPrimaryHeaterInternal | \ref Data::mTrimmerHeaterInternal | + :param vInletPrimaryHeater: (float) Inlet Primary Heater + :param vOutletPrimaryHeater: (float) Outlet Primary Heater + :param vConductivitySensor1: (float) Conductivity Sensor 1 + :param vConductivitySensor2: (float) Conductivity Sensor 2 + :param vOutletRedundancy: (float) Outlet Redundancy + :param vInletDialysate: (float) Inlet Dialysate + :param vPrimaryHeaterThermocouple: (float) Primary Heater Thermocouple + :param vTrimmerHeaterThermocouple: (float) Trimmer Heater Thermocouple + :param vPrimaryHeaterColdJunction: (float) Primary Heater ColdJunction + :param vTrimmerHeaterColdJunction: (float) Trimmer Heater ColdJunction + :param vPrimaryHeaterInternalTemperature: (float) Primary Heater Internal Temperature + :param vTrimmerHeaterInternalTemperature: (float) Trimmer HeaterInternal Temperature + :return: none + """ + + payload = float_to_bytearray(vInletPrimaryHeater) + payload += float_to_bytearray(vOutletPrimaryHeater) + payload += float_to_bytearray(vConductivitySensor1) + payload += float_to_bytearray(vConductivitySensor2) + payload += float_to_bytearray(vOutletRedundancy) + payload += float_to_bytearray(vInletDialysate) + payload += float_to_bytearray(vPrimaryHeaterThermocouple) + payload += float_to_bytearray(vTrimmerHeaterThermocouple) + payload += float_to_bytearray(vPrimaryHeaterColdJunction) + payload += float_to_bytearray(vTrimmerHeaterColdJunction) + payload += float_to_bytearray(vPrimaryHeaterInternalTemperature) + payload += float_to_bytearray(vTrimmerHeaterInternalTemperature) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DG_TEMPERATURE_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_states_data(self, vSubMode, vUFState, vSalineState, vHeparingState): + """ + the Treatment States Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | #4:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x0F00| 0x040 | 7 | 1 Hz | N | HD | All | Treatment States Data | \ref Data::mSubMode | \ref Data::mUFState | \ref Data::mSalineState | \ref Data::mHeparinState| + :param vSubMode: (int) Sub-Mode + :param vUFState: (int) UF State + :param vSalineState: (int) Saline Bolus State + :param vHeparingState: (int) Saline Bolus State + :return: none + """ + + payload = integer_to_bytearray(vSubMode) + payload += integer_to_bytearray(vUFState) + payload += integer_to_bytearray(vSalineState) + payload += integer_to_bytearray(vHeparingState) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_TREATMENT_STATE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_start_state(self): + """ + starting the treatment for user convenience since Tx is not by default running + :return: none + """ + self.cmd_set_treatment_states_data(TXStates.TREATMENT_DIALYSIS_STATE, + TXStates.UF_OFF_STATE, + TXStates.SALINE_BOLUS_STATE_IDLE, + TXStates.HEPARIN_STATE_OFF) + + def cmd_set_hd_operation_mode_data(self, operation_mode): + """ + The HD Operation Mode Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x2500| 0x040 | 7 | 1 Hz | N | HD | All | HD Operation Mode Data | \ref Data::mOpMode | + :param operation_mode: (int) Operation Mode + :return: none + """ + + payload = integer_to_bytearray(operation_mode) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_HD_OP_MODE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_treatment_saline_bolus_data(self, vTarget, vCumulative, vDelivered): + """ + the Treatment Saline Bolus Data message sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(F32) | #3:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x2F00| 0x040 | 7 | 1 Hz | N | HD | All | Treatment Saline Bolus Data | \ref Data::mTarget | \ref Data::mCumulative | \ref Data::mDelivered | + + :param vTarget: (int) Saline Bolus Target Volume + :param vCumulative: (float) Saline Bolus Cumulative Volume + :param vDelivered: (float) Saline Bolus Delivered Volume + :return: none + """ + + payload = integer_to_bytearray(vTarget) + payload += float_to_bytearray(vCumulative) + payload += float_to_bytearray(vDelivered) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_SALINE_BOLUS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_saline_bolus_response(self, vAccepted, vReason, vTarget, vState): + """ + the Saline Bolus Response message sender method + | MSG | CAN ID | M.Box | Type | Ack | Src | Dest | Description | #1:(U32) | #2:(U32) | #3:(U32) | #3:(U32) | + |:---:|:------:|:-----:|:----:|:---:|:---:|:----:|:---------------------:|:--------------------:|:-------------------:|:-------------------:|:-------------------:| + | 20 | 0x020 | 6 | Rsp | Y | HD | UI | Saline Bolus Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mTarget | \ref Data::mState | + + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vTarget: (int) Saline Bolus Target Volume + :param vState: (int) Saline Bolus current State + :return: none + """ + + payload = integer_to_bytearray(vAccepted) + payload += integer_to_bytearray(vReason) + payload += integer_to_bytearray(vTarget) + payload += integer_to_bytearray(vState) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_USER_SALINE_BOLUS_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_canbus_fault_count(self, count): + """ + the CANBus fault count message setter/sender method + :param count: (int) Fault Count + :return: none + """ + + payload = integer_to_bytearray(count) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_CAN_ERROR_COUNT.value, + payload=payload) + + self.can_interface.send(message, 0) + HDSimulator.wait_for_message_to_be_sent() + + + def cmd_send_unknown_hd(self): + """ + the unknown message from HD setter/sender method + :return: none + """ + + payload = integer_to_bytearray(0) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_UNUSED.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_unknown_dg(self): + """ + the unknown message from DG setter/sender method + :return: none + """ + + payload = integer_to_bytearray(0) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dg_to_ui_ch_id, + message_id=MsgIds.MSG_ID_UNUSED.value, + payload=payload) + + self.can_interface.send(message, 0) + + @staticmethod + def buildTreatmentStatesData(vSubMode, vUFState, vSalineState, vHeparinState): + """ + the Treatment States Data message builder method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | #4:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x0F00| 0x040 | 7 | 1 Hz | N | HD | All | Treatment States Data | \ref Data::mSubMode | \ref Data::mUFState | \ref Data::mSalineState | \ref Data::mHeparinState | + :param vSubMode: (int) Sub-Mode + :param vUFState: (int) UF State + :param vSalineState: (int) Saline Bolus State + :param vHeparinState: (int) Heparin State + :return: (str) built message frame(s) + """ + msg = messageBuilder.buildMessage(0x000F, 4 + 4 + 4 + 4, False, + utils.toI32(vSubMode), + utils.toI32(vUFState), + utils.toI32(vSalineState), + utils.toI32(vHeparinState) + ) + return messageBuilder.toFrames(msg) + + @staticmethod + def setTreatmentStatesData(vSubMode, vUFState, vSalineState, vHeparinState): + """ + the Treatment States Data message setter/sender method + :param vSubMode: (int) Sub-Mode + :param vUFState: (int) UF State + :param vSalineState: (int) Saline Bolus State + :param vHeparinState: (int) Heparin State + :return: none + """ + frames = HDSimulator.buildTreatmentStatesData(vSubMode, vUFState, vSalineState, vHeparinState) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '040#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + @staticmethod + def setTreatmentStartState(): + """ + starting the treatment for user convenience since Tx is not by default running + :return: none + """ + HDSimulator.setTreatmentStatesData( + TXStates.TREATMENT_DIALYSIS_STATE, + TXStates.UF_OFF_STATE, + TXStates.SALINE_BOLUS_STATE_IDLE, + TXStates.HEPARIN_STATE_OFF + ) + + @staticmethod + def buildTreatmentHeparinData(vCumulative): + """ + the Treatment Heparin Data message builder method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x4D00| 0x040 | 7 | 1 Hz | N | HD | All | Treatment Heparin Data | \ref Data::mCumulative | + :param vCumulative: (float) Heparin Cumulative Volume + :return: (str) built message frame(s) + """ + msg = messageBuilder.buildMessage(0x004D, 4, False, + utils.toF32(vCumulative) + ) + return messageBuilder.toFrames(msg) + + @staticmethod + def setTreatmentHeparinData(vCumulative): + """ + the Treatment Heparin Data message setter/sender method + :param vCumulative: (float) Heparin Cumulative Volume + :return: none + """ + frames = HDSimulator.buildTreatmentHeparinData(vCumulative) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '040#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + @staticmethod + def buildHeparinResponse(vAccepted, vReason, vState): + """ + the Heparin Response message builder method + | MSG | CAN ID | M.Box | Type | Ack | Src | Dest | Description | #1:(U32) | #2:(U32) | #3:(U32) | + |:----:|:------:|:-----:|:----:|:---:|:---:|:----:|:----------------:|:--------------------:|:-------------------:|:-------------------:| + |0x4C00| 0x020 | 6 | Rsp | Y | HD | UI | Heparin Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mState | + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vState: (int) Heparin current State + :return: (str) built message frame(s) + """ + msg = messageBuilder.buildMessage(0x004C, 4 + 4 + 4, True, + utils.toI32(vAccepted), + utils.toI32(vReason), + utils.toI32(vState)) + return messageBuilder.toFrames(msg) + + @staticmethod + def setHeparinResponse(vAccepted, vReason, vState): + """ + the Heparin Response message setter/sender method + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param vState: (int) Heparin current State + :return: none + """ + frames = HDSimulator.buildHeparinResponse(vAccepted, vReason, vState) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '020#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + @staticmethod + def buildTreatmentAdjustPressuresLimitsResponse(vAccepted, vReason, vArterialLow, vArterialHigh, vVenousLow, + vVenousHigh): + """ + the Blood/dialysate rate change Response message builder method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(S32) | #4:(S32) | #3:(S32) | #4:(S32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: | + |0x4700| 0x020 | 6 | Rsp | Y | HD | UI | A/V BP Limit Change Response | \ref Data::mAccepted | \ref Data::mReason | \ref Data::mArterialLow | \ref Data::mArterialHigh | \ref Data::mVenousLow | \ref Data::mVenousHigh | + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param mArterialLow: (int) Arterial Pressure Limit Low (mmHg) + :param mArterialHigh: (int) Arterial Pressure Limit High (mmHg) + :param mVenousLow: (int) Venous Pressure Limit Low (mmHg) + :param mVenousHigh: (int) Venous Pressure Limit High (mmHg) + :return: (str) built message frame(s) + """ + msg = messageBuilder.buildMessage(GuiActionType.AdjustPressuresLimitsRsp, 6 * 4, True, + utils.toI32(vAccepted), + utils.toI32(vReason), + utils.toI32(vArterialLow), + utils.toI32(vArterialHigh), + utils.toI32(vVenousLow), + utils.toI32(vVenousHigh) + ) + return messageBuilder.toFrames(msg) + + @staticmethod + def sendTreatmentAdjustPressuresLimitsResponse(vAccepted, vReason, vArterialLow, vArterialHigh, vVenousLow, + vVenousHigh): + """ + the Blood/dialysate rate change Response message setter/sender method + :param vAccepted: (int) boolean accept/reject response + :param vReason: (int) rejection reason + :param mArterialLow: (int) Arterial Pressure Limit Low (mmHg) + :param mArterialHigh: (int) Arterial Pressure Limit High (mmHg) + :param mVenousLow: (int) Venous Pressure Limit Low (mmHg) + :param mVenousHigh: (int) Venous Pressure Limit High (mmHg) + :return: none + """ + frames = HDSimulator.buildTreatmentAdjustPressuresLimitsResponse(vAccepted, vReason, vArterialLow, vArterialHigh, + vVenousLow, vVenousHigh) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '020#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + @staticmethod + def buildAlarmClearedCondition(vAlarmID): + """ + the Alarm Condition Cleared message builder method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:---------:|:---:|:------:|:---:|:---:|:---:|:-----------------------:|:--: | + |0x3F00| 0x001,2,4 | 1 | Event | Y | HD | All | Alarm Condition Cleared | \ref Data::mAlarmID | + :param vAlarmID: (int) Alarm ID + :return: (str) built message frame(s) + """ + msg = messageBuilder.buildMessage(63, 4, True, + utils.toI32(vAlarmID)) + return messageBuilder.toFrames(msg) + + @staticmethod + def setAlarmClearedCondition(vAlarmID): + """ + the Alarm Condition Cleared message setter/sender method + :param vAlarmID: (int) Alarm ID + :return: none + """ + frames = HDSimulator.buildAlarmClearedCondition(vAlarmID) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '001#{}'.format(frame)]) + HDSimulator.wait_for_message_to_be_sent() + + + + + Index: dialin/ui/unittests.py =================================================================== diff -u -re5b68dd61c18dd97545d5e527a7f0a8f84061cb6 -r0392b232e0f257fb6946f6e8e2cdf4eadd05974d --- dialin/ui/unittests.py (.../unittests.py) (revision e5b68dd61c18dd97545d5e527a7f0a8f84061cb6) +++ dialin/ui/unittests.py (.../unittests.py) (revision 0392b232e0f257fb6946f6e8e2cdf4eadd05974d) @@ -16,10 +16,9 @@ import test import sys -from subprocess import check_output from dialin.ui import crc - +from dialin.ui.utils import check_can0 MICRO = [8, 9] @@ -67,16 +66,8 @@ def test_can0(): """ - check if the can0 bus driver presents + tests if the can0 bus driver presents :return: """ - canid = "can0" - ipa = "ip a" - ipa = check_output(ipa, shell=True) - loc = str(ipa).find(canid) - fnd = loc >= 0 - if (fnd): - msg = "can device '{}' found".format(canid) - else: - msg = "No can device registered as '{}'".format(canid) - test.compare(loc >= 0, True, msg) + ok, msg = check_can0() + test.compare(ok, True, msg) Index: dialin/ui/utils.py =================================================================== diff -u -ra37f46be740bbbbecbdbde1230d5f20d8854fb8e -r0392b232e0f257fb6946f6e8e2cdf4eadd05974d --- dialin/ui/utils.py (.../utils.py) (revision a37f46be740bbbbecbdbde1230d5f20d8854fb8e) +++ dialin/ui/utils.py (.../utils.py) (revision 0392b232e0f257fb6946f6e8e2cdf4eadd05974d) @@ -16,6 +16,7 @@ import time import struct +from subprocess import check_output def SRSUI(vSRSUI = ""): return "SRSUI " + "{}".format(vSRSUI).rjust(3, '0') @@ -185,3 +186,20 @@ obj = obj.copy() obj[key] = value return obj + + +def check_can0(): + """ + check if the can0 bus driver presents + :return: (list) false if can0 not exists, msg as a message + """ + canid = "can0" + ipa = "ip a" + ipa = check_output(ipa, shell=True) + loc = str(ipa).find(canid) + fnd = loc >= 0 + if (fnd): + msg = "can device '{}' found".format(canid) + else: + msg = "No can device registered as '{}'".format(canid) + return loc >= 0, msg