Index: dialin/common/msg_defs.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/common/msg_defs.py (.../msg_defs.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/common/msg_defs.py (.../msg_defs.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -15,7 +15,6 @@ ############################################################################ import enum - class MsgIds(enum.Enum): MSG_ID_UNUSED = 0 # Zero is an undefined (un-used) message ID) MSG_ID_OFF_BUTTON_PRESS = 1 # HD/UI off button interactions @@ -75,6 +74,11 @@ MSG_ID_DG_HEAT_DISINFECT_DATA = 0x37 # DG heat disinfection publish data MSG_ID_UI_START_TREATMENT = 0x38 # UI user request to initiate a treatment MSG_ID_HD_START_TREATMENT_RESPONSE = 0x39 # HD response to user request to initiate a treatment + MSG_ID_HD_VALVES_DATA = 0x3A # HD broadcast of valves data + MSG_ID_UI_USER_CONFIRM_TREATMENT_PARAMS = 0x3B # UI user confirmation of treatment parameters + MSG_ID_UI_TREATMENT_END_REQUEST = 0x3C # UI user treatment end request + MSG_ID_HD_TREATMENT_END_RESPONSE = 0x3D # HD response to user request to end treatment + MSG_ID_HD_AIR_TRAP_DATA = 0x003E # HD broadcast of air trap data MSG_ID_CAN_ERROR_COUNT = 0x999 # test code in support of EMC testing @@ -126,6 +130,14 @@ MSG_ID_HD_ACCEL_SET_CALIBRATION = 0x802A # HD accelerometer set calibration factors request MSG_ID_HD_BLOOD_FLOW_SET_CALIBRATION = 0x802B # Blood flow set calibration factors request MSG_ID_HD_DIALYSATE_FLOW_SET_CALIBRATION = 0x802C # Dialysate flow set calibration factors request + MSG_ID_HD_SET_PARAMETER_TREATMENT_PARAMETER = 0x802D # Set a treatment parameter (will change actual setting, not an override) + MSG_ID_HD_VALVES_HOME = 0x802E # Home an HD Valve + MSG_ID_HD_VALVES_POSITION_OVERRIDE = 0x802F # Set an HD valve to a position in counts + MSG_ID_HD_VALVES_SET_AIR_TRAP_VALVE = 0x8030 # Set the HD blood trap valve status + MSG_ID_HD_VALVES_SET_PWM_OVERRIDE = 0x8031 # Set an HD valve PWM + MSG_ID_DG_AIR_TRAP_DATA_BROADCAST_INTERVAL_OVERRIDE = 0x8032 # HD air trap data broadcast interval override request + MSG_ID_DG_AIR_TRAP_LEVEL_SENSOR_OVERRIDE = 0x8033 # HD air trap level sensor override request + MSG_ID_HD_SOFTWARE_RESET_REQUEST = 0x8034 # HD reset request MSG_ID_FIRST_DG_TESTER_MESSAGE = 0xA000 # First DG test message ID MSG_ID_DG_TESTER_LOGIN_REQUEST = MSG_ID_FIRST_DG_TESTER_MESSAGE # DG tester log-in @@ -161,6 +173,7 @@ MSG_ID_HEAT_DISINFECT_RSRVR2_TO_RSRVR1_DURATION_MINS = 0xA01F # Heat disinfection reservoir 2 to reservoir 1 duration in minutes 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_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 Index: dialin/dg/dialysate_generator.py =================================================================== diff -u -r7536c3e8660b31ca142b2eb9cd0a76fba81d29fe -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 7536c3e8660b31ca142b2eb9cd0a76fba81d29fe) +++ dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -14,6 +14,7 @@ # ############################################################################ import struct +from .accelerometer import DGAccelerometer from .alarms import DGAlarms from .drain_pump import DGDrainPump from .hd_proxy import DGHDProxy @@ -41,6 +42,7 @@ MSG_ID_DG_DG_VERSION_RESPONSE = 0x001E MSG_ID_LOGIN_TO_DG = 0xA000 MSG_ID_DG_SAFETY_SHUTDOWN_OVERRIDE = 0xA014 + MSG_ID_DG_SOFTWARE_RESET_REQUEST = 0xA022 # HD login password DG_LOGIN_PASSWORD = '123' @@ -136,6 +138,7 @@ self.dg_operation_sub_mode = 0 # Create command groups + self.accel = DGAccelerometer(self.can_interface, self.logger) self.hd_proxy = DGHDProxy(self.can_interface, self.logger) self.load_cells = DGLoadCells(self.can_interface, self.logger) self.pressures = DGPressures(self.can_interface, self.logger) @@ -164,14 +167,6 @@ """ return self.fpga_version - def get_version(self): - """ - Gets the DG fpga version - - @return: The fpga version - """ - return self.dg_version - def get_operation_mode(self): """ Gets the operation mode @@ -201,14 +196,10 @@ @return: None if unsuccessful, the version string if unpacked successfully """ - major = struct.unpack('B', bytearray( - message['message'][self.START_POS_MAJOR:self.END_POS_MAJOR])) - minor = struct.unpack('B', bytearray( - message['message'][self.START_POS_MINOR:self.END_POS_MINOR])) - micro = struct.unpack('B', bytearray( - message['message'][self.START_POS_MICRO:self.END_POS_MICRO])) - build = struct.unpack('H', bytearray( - message['message'][self.START_POS_BUILD:self.END_POS_BUILD])) + major = struct.unpack('B', bytearray(message['message'][self.START_POS_MAJOR:self.END_POS_MAJOR])) + minor = struct.unpack('B', bytearray(message['message'][self.START_POS_MINOR:self.END_POS_MINOR])) + micro = struct.unpack('B', bytearray(message['message'][self.START_POS_MICRO:self.END_POS_MICRO])) + build = struct.unpack('H', bytearray(message['message'][self.START_POS_BUILD:self.END_POS_BUILD])) fpga_id = struct.unpack('B', bytearray( message['message'][self.START_POS_FPGA_ID:self.END_POS_FPGA_ID])) @@ -290,7 +281,6 @@ Constructs and sends an DG safety shutdown override command via CAN bus. \returns response message if received, False if no response received - @param msg: byte array - properly formatted DG message to insert @return: 1 if successful, zero otherwise """ @@ -313,3 +303,32 @@ self.logger.debug("Timeout!!!!") return False + def cmd_dg_software_reset_request(self): + """ + Constructs and sends an DG software reset request via CAN bus. + Constraints: + Must be logged into DG. + + \returns response message if received, False if no response received + + @return: 1 if successful, zero otherwise + + """ + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=self.MSG_ID_DG_SOFTWARE_RESET_REQUEST) + + self.logger.debug("requesting DG software reset") + + # Send message + received_message = self.can_interface.send(message) + + if received_message is not None: + if received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] == 1: + self.logger.debug("DG is resetting...") + else: + self.logger.debug("DG reset request failed.") + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False Index: dialin/dg/hd_proxy.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/dg/hd_proxy.py (.../hd_proxy.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/dg/hd_proxy.py (.../hd_proxy.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -33,6 +33,8 @@ 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 + # Reservoir IDs RESERVOIR1 = 0 RESERVOIR2 = 1 @@ -212,3 +214,48 @@ self.logger.debug("Timeout!!!!") return False + def cmd_start_heat_disinfection(self): + """ + Constructs and sends start heat disinfect command + Constraints: + N/A + + @returns none + """ + # 1 is to start + payload = integer_to_bytearray(1) + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=self.MSG_ID_DG_START_STOP_HEAT_DISINFECT, + payload=payload) + print("Starting heat disinfection process") + received_message = self.can_interface.send(message) + + def cmd_stop_heat_disinfection(self): + """ + Constructs and sends stop heat disinfect command + Constraints: + N/A + + @returns none + """ + # 1 is to start + payload = integer_to_bytearray(0) + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=self.MSG_ID_DG_START_STOP_HEAT_DISINFECT, + payload=payload) + print("Stopping heat disinfection process") + received_message = self.can_interface.send(message) + + def cmd_stop_primary_heater(self): + """ + Constructs and sends stop heat disinfect command + + @returns none + """ + # 0 is to stop + payload = integer_to_bytearray(0) + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=self.MSG_ID_HD_START_STOP_DG_TRIMMER_HEATER, + payload=payload) + print("Stopping heat disinfection process") + self.can_interface.send(message, 0) \ No newline at end of file Index: dialin/dg/heaters.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/dg/heaters.py (.../heaters.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/dg/heaters.py (.../heaters.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -180,7 +180,3 @@ else: self.logger.debug("Timeout!!!!") return False - - - - Index: dialin/dg/load_cells.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/dg/load_cells.py (.../load_cells.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/dg/load_cells.py (.../load_cells.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -29,7 +29,7 @@ Dialysate Generator (DG) Dialin API sub-class for load cell related commands. """ - # Pressure/Occlusion message IDs + # Load cell message IDs MSG_ID_DG_LOAD_CELL_DATA = 0x000C MSG_ID_DG_LOAD_CELL_OVERRIDE = 0xA005 MSG_ID_DG_LOAD_CELL_DATA_BROADCAST_INTERVAL_OVERRIDE = 0xA00D Index: dialin/dg/ro_pump.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/dg/ro_pump.py (.../ro_pump.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/dg/ro_pump.py (.../ro_pump.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -32,8 +32,9 @@ # RO pump message IDs MSG_ID_DG_RO_PUMP_PUBLISHED_DATA = 0x001F MSG_ID_DG_RO_PUMP_PRESSURE_SET_PT_OVERRIDE = 0xA008 - MSG_ID_DG_RO_FLOW_RATE_OVERRIDE = 0xA009 + #MSG_ID_DG_RO_FLOW_RATE_OVERRIDE = 0xA009 MSG_ID_DG_RO_PUMP_BROADCAST_INTERVAL_OVERRIDE = 0xA00A + MSG_ID_RO_PUMP_SET_PT_OVERRIDE = 0xA008 # RO pump broadcast message field positions START_POS_PRES_SET_PT = DenaliMessage.PAYLOAD_START_INDEX @@ -42,7 +43,13 @@ END_POS_MEAS_FLOW = START_POS_MEAS_FLOW + 4 START_POS_PWM = END_POS_MEAS_FLOW END_POS_PWM = START_POS_PWM + 4 + START_POS_STATE = END_POS_PWM + END_POS_STATE = START_POS_STATE + 4 + #Enums + RAMP_UP_STATE = 0 + + def __init__(self, can_interface, logger: Logger): """ DGROPump constructor @@ -61,6 +68,7 @@ self.target_pressure_psi = 0 self.measured_flow_rate_lpm = 0.0 self.pwm_duty_cycle_pct = 0.0 + self.ro_pump_state = 0.0 def get_target_pressure(self): """ @@ -86,7 +94,8 @@ @_publish([ "target_pressure_psi", "measured_flow_rate_lpm", - "pwm_duty_cycle_pct" + "pwm_duty_cycle_pct", + "ro_pump_state" ]) def _handler_ro_pump_sync(self, message): """ @@ -97,17 +106,33 @@ @return: None """ - tgt = struct.unpack('i', bytearray( + tgt = struct.unpack('f', bytearray( message['message'][self.START_POS_PRES_SET_PT:self.END_POS_PRES_SET_PT])) flow = struct.unpack('f', bytearray( message['message'][self.START_POS_MEAS_FLOW:self.END_POS_MEAS_FLOW])) pwm = struct.unpack('f', bytearray( message['message'][self.START_POS_PWM:self.END_POS_PWM])) + ro_state = struct.unpack('i', bytearray( + message['message'][self.START_POS_STATE:self.END_POS_STATE])) self.target_pressure_psi = tgt[0] self.measured_flow_rate_lpm = flow[0] self.pwm_duty_cycle_pct = pwm[0] + raw_ro_pump_state = ro_state[0] + #self.ro_pump_state = self.ro_pump_states.get("RO_PUMP_OFF_STATE", -1) + + if raw_ro_pump_state == 0: + self.ro_pump_state = "RO_PUMP_OFF_STATE" + elif raw_ro_pump_state == 1: + self.ro_pump_state = "RO_PUMP_RAMP_UP_STATE" + elif raw_ro_pump_state == 2: + self.ro_pump_state = "RO_PUMP_VERIFY_FLOW_STATE" + elif raw_ro_pump_state == 3: + self.ro_pump_state = "RO_PUMP_CONTROL_TO_TARGET_STATE" + elif raw_ro_pump_state == 4: + self.ro_pump_state = "RO_PUMP_OPEN_LOOP_STATE" + def cmd_ro_pump_set_point_override(self, pressure, reset=NO_RESET): """ Constructs and sends the RO pump set point override command. @@ -124,7 +149,7 @@ payload = rst + prs message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=self.MSG_ID_DG_RO_PUMP_PRESSURE_SET_PT_OVERRIDE, + message_id=self.MSG_ID_RO_PUMP_SET_PT_OVERRIDE, payload=payload) self.logger.debug("override RO pump pressure set point") @@ -164,7 +189,7 @@ payload = rst + flo message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=self.MSG_ID_DG_RO_FLOW_RATE_OVERRIDE, + message_id=self.MSG_ID_RO_PUMP_SET_PT_OVERRIDE, payload=payload) self.logger.debug("override RO pump pressure set point") Index: dialin/hd/alarms.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/alarms.py (.../alarms.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/alarms.py (.../alarms.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -224,7 +224,6 @@ TBD = result & 32768 TBD = result & 65536 - @return: (int) The alarms flags value """ return self.alarms_flags Index: dialin/hd/blood_flow.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/blood_flow.py (.../blood_flow.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/blood_flow.py (.../blood_flow.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -17,6 +17,7 @@ DenaliChannels) from ..utils.conversions import integer_to_bytearray, float_to_bytearray from .constants import RESET, NO_RESET +from .constants import PUMP_CONTROL_MODE_CLOSED_LOOP, PUMP_CONTROL_MODE_OPEN_LOOP from ..utils.base import _AbstractSubSystem, _publish import struct from logging import Logger @@ -39,6 +40,7 @@ MSG_ID_HD_BLOOD_FLOW_PUBLISH_INTERVAL_OVERRIDE = 0x800C MSG_ID_HD_BLOOD_PUMP_MEAS_SPEED_OVERRIDE = 0x800E MSG_ID_HD_BLOOD_PUMP_ROTOR_MEAS_SPEED_OVERRIDE = 0x800F + MSG_ID_HD_BLOOD_FLOW_SET_CALIBRATION = 0x802B # bloodflow broadcast message field positions START_POS_SET_PT = DenaliMessage.PAYLOAD_START_INDEX @@ -59,8 +61,6 @@ def __init__(self, can_interface, logger: Logger): """ HD_BloodFlow constructor - - @param outer_instance: reference to the HD (outer) class. """ super().__init__() self.can_interface = can_interface @@ -175,21 +175,23 @@ self.measured_blood_pump_mc_current = mccurr[0] self.pwm_duty_cycle_pct = pwm[0] - def cmd_blood_flow_set_point_override(self, flow, reset=NO_RESET): + def cmd_blood_flow_set_point_override(self, flow, mode=PUMP_CONTROL_MODE_CLOSED_LOOP, reset=NO_RESET): """ Constructs and sends the blood flow set point override command Constraints: Must be logged into HD. - Given flow rate must be valid (<= 500 mL/min). + Given flow rate must be valid (<= +/-500 mL/min). @param flow: integer - flow set point (in mL/min) to override with (negative for reverse direction) + @param mode: integer - 0 for closed loop control mode or 1 for open loop control mode @param reset: integer - 1 to reset a previous override, 0 to override @return: 1 if successful, zero otherwise """ rst = integer_to_bytearray(reset) flo = integer_to_bytearray(flow) - payload = rst + flo + mod = integer_to_bytearray(mode) + payload = rst + flo + mod message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, message_id=self.MSG_ID_HD_BLOOD_FLOW_SET_RATE_OVERRIDE, @@ -207,8 +209,12 @@ str_res = "reset back to normal" else: str_res = str(flow) + if mode == PUMP_CONTROL_MODE_OPEN_LOOP: + str_mode = " (open loop): " + else: + str_mode = " (closed loop): " self.logger.debug( - "Blood flow set point overridden to " + str_res + " mL/min: " + + "Blood flow set point overridden to " + str_res + " mL/min" + str_mode + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] @@ -453,3 +459,34 @@ else: self.logger.debug("Timeout!!!!") return False + + def cmd_set_blood_flow_calibration(self, gain, offset): + """ + Constructs and sends a blood flow calibration message to the HD. + + @return: 1 if successful, zero otherwise + """ + gan = float_to_bytearray(gain) + off = float_to_bytearray(offset) + payload = gan + off + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_BLOOD_FLOW_SET_CALIBRATION, + payload=payload) + + print("Setting blood flow calibration factors") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + print(received_message) + # str_res = str(flow) + print("Blood flow calibration factors set to : " + str(gain) + ", " + str(offset) + ". " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False Index: dialin/hd/dialysate_inlet_flow.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/dialysate_inlet_flow.py (.../dialysate_inlet_flow.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/dialysate_inlet_flow.py (.../dialysate_inlet_flow.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -18,6 +18,7 @@ DenaliChannels) from ..utils.conversions import integer_to_bytearray, float_to_bytearray from .constants import RESET, NO_RESET +from .constants import PUMP_CONTROL_MODE_CLOSED_LOOP, PUMP_CONTROL_MODE_OPEN_LOOP from ..utils.base import _AbstractSubSystem, _publish from logging import Logger @@ -38,6 +39,7 @@ MSG_ID_HD_DIAL_IN_FLOW_PUBLISH_INTERVAL_OVERRIDE = 0x8014 MSG_ID_HD_DIAL_IN_PUMP_MEAS_SPEED_OVERRIDE = 0x8015 MSG_ID_HD_DIAL_IN_PUMP_ROTOR_MEAS_SPEED_OVERRIDE = 0x8016 + MSG_ID_HD_DIAL_IN_FLOW_SET_CALIBRATION = 0x802C # bloodflow broadcast message field positions START_POS_SET_PT = DenaliMessage.PAYLOAD_START_INDEX @@ -174,21 +176,23 @@ self.measured_dialysate_inlet_pump_mc_current = mccurr[0] self.pwm_duty_cycle_pct = pwm[0] - def cmd_dialysate_inlet_flow_set_point_override(self, flow, reset=NO_RESET): + def cmd_dialysate_inlet_flow_set_point_override(self, flow, mode=PUMP_CONTROL_MODE_CLOSED_LOOP, reset=NO_RESET): """ Constructs and sends the dialysate flow set point override command Constraints: Must be logged into HD. - Given flow must be valid (<= 600 mL/min). + Given flow must be valid (<= +/-600 mL/min). @param flow: integer - flow set point (in mL/min) to override with + @param mode: integer - 0 for closed loop control mode or 1 for open loop control mode @param reset: integer - 1 to reset a previous override, 0 to override @return: 1 if successful, zero otherwise """ rst = integer_to_bytearray(reset) flo = integer_to_bytearray(flow) - payload = rst + flo + mod = integer_to_bytearray(mode) + payload = rst + flo + mod message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, message_id=self.MSG_ID_HD_DIAL_IN_FLOW_SET_RATE_OVERRIDE, @@ -206,8 +210,12 @@ str_res = "reset back to normal" else: str_res = str(flow) + if mode == PUMP_CONTROL_MODE_OPEN_LOOP: + str_mode = " (open loop): " + else: + str_mode = " (closed loop): " self.logger.debug( - "Dialysate flow set point overridden to " + str_res + " mL/min: " + + "Dialysate flow set point overridden to " + str_res + " mL/min" + str_mode + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] @@ -451,3 +459,34 @@ else: self.logger.debug("Timeout!!!!") return False + + def cmd_set_dialysate_flow_calibration(self, gain, offset): + """ + Constructs and sends a dialysate flow calibration message to the HD. + + @return: 1 if successful, zero otherwise + """ + gan = float_to_bytearray(gain) + off = float_to_bytearray(offset) + payload = gan + off + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_IN_FLOW_SET_CALIBRATION, + payload=payload) + + print("Setting dialysate flow calibration factors") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + print(received_message) + # str_res = str(flow) + print("Dialysate flow calibration factors set to : " + str(gain) + ", " + str(offset) + ". " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False Index: dialin/hd/dialysate_outlet_flow.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/dialysate_outlet_flow.py (.../dialysate_outlet_flow.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/dialysate_outlet_flow.py (.../dialysate_outlet_flow.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -18,6 +18,7 @@ DenaliChannels) from ..utils.conversions import integer_to_bytearray, float_to_bytearray from .constants import RESET, NO_RESET +from .constants import PUMP_CONTROL_MODE_CLOSED_LOOP, PUMP_CONTROL_MODE_OPEN_LOOP from ..utils.base import _AbstractSubSystem, _publish from logging import Logger @@ -30,6 +31,7 @@ # DialysateFlow message IDs MSG_ID_HD_DIAL_OUT_FLOW_PUBLISHED_DATA = 0x000B MSG_ID_HD_LOAD_CELL_READINGS = 0x000C + MSG_ID_HD_DIAL_OUT_FLOW_SET_RATE_OVERRIDE = 0x8001 MSG_ID_HD_DIAL_OUT_FLOW_PUBLISH_INTERVAL_OVERRIDE = 0x801E MSG_ID_HD_DIAL_OUT_UF_REF_VOLUME_OVERRIDE = 0x801F MSG_ID_HD_DIAL_OUT_UF_MEAS_VOLUME_OVERRIDE = 0x8020 @@ -172,6 +174,53 @@ self.measured_dialysate_outlet_pump_mc_current = mccurr[0] self.pwm_duty_cycle_pct = pwm[0] + def cmd_dialysate_outlet_flow_set_point_override(self, flow, mode=PUMP_CONTROL_MODE_CLOSED_LOOP, reset=NO_RESET): + """ + Constructs and sends the dialysate outlet pump set point override command + Constraints: + Must be logged into HD. + Given flow must be valid (<= +/-600 mL/min). + + @param flow: integer - flow set point (in mL/min) to override with + @param mode: integer - 0 for closed loop control mode or 1 for open loop control mode + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + flo = integer_to_bytearray(flow) + mod = integer_to_bytearray(mode) + payload = rst + flo + mod + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_DIAL_OUT_FLOW_SET_RATE_OVERRIDE, + payload=payload) + + print("override dialysate outlet pump set point") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + if reset == RESET: + str_res = "reset back to stopped" + else: + str_res = str(flow) + if mode == PUMP_CONTROL_MODE_OPEN_LOOP: + str_mode = " (open loop): " + else: + str_mode = " (closed loop): " + print( + "Dialysate outlet pump set point overridden to " + str_res + " mL/min" + str_mode + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + def cmd_dialysate_outlet_reference_uf_volume_override(self, refvol, reset=NO_RESET): """ Constructs and sends the UF reference volume override command Index: dialin/hd/hemodialysis_device.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/hemodialysis_device.py (.../hemodialysis_device.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/hemodialysis_device.py (.../hemodialysis_device.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -14,6 +14,8 @@ # ############################################################################ import struct +from .accelerometer import HDAccelerometer +from .air_trap import HDAirTrap from .alarms import HDAlarms from .buttons import HDButtons from .ui_proxy import HDUIProxy @@ -27,8 +29,10 @@ from ..protocols.CAN import (DenaliMessage, DenaliCanMessenger, DenaliChannels) +from ..utils.conversions import integer_to_bytearray from ..utils.base import _AbstractSubSystem, _publish, _LogManager from .constants import NO_RESET +from ..common.msg_defs import MsgIds class HD(_AbstractSubSystem): @@ -37,12 +41,6 @@ the HD firmware. """ - # HD message IDs - MSG_ID_HD_OPERATION_MODE_BROADCAST = 0x0025 - MSG_ID_LOGIN_TO_HD = 0x8000 - MSG_ID_HD_SAFETY_SHUTDOWN_OVERRIDE = 0x8026 - MSG_ID_POWEROFF_NOTIFY = 0x0020 - # broadcast message field positions START_POS_HD_OP_MODE = DenaliMessage.PAYLOAD_START_INDEX END_POS_HD_OP_MODE = START_POS_HD_OP_MODE + 4 @@ -91,7 +89,7 @@ # register handler for HD operation mode broadcast messages if self.can_interface is not None: channel_id = DenaliChannels.hd_sync_broadcast_ch_id - msg_id = self.MSG_ID_HD_OPERATION_MODE_BROADCAST + msg_id = MsgIds.MSG_ID_HD_OP_MODE self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_hd_op_mode_sync) @@ -100,6 +98,8 @@ self.hd_operation_sub_mode = 0 # Create command groups + self.accel = HDAccelerometer(self.can_interface, self.logger) + self.air_trap = HDAirTrap(self.can_interface, self.logger) self.alarms = HDAlarms(self.can_interface, self.logger) self.buttons = HDButtons(self.can_interface, self.logger) self.ui = HDUIProxy(self.can_interface, self.logger) @@ -145,7 +145,7 @@ """ message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_LOGIN_TO_HD, + message_id=MsgIds.MSG_ID_TESTER_LOGIN_REQUEST, payload=list(map(int, map(ord, self.HD_LOGIN_PASSWORD)))) self.logger.debug("Logging in...") @@ -186,7 +186,7 @@ payload = rst + saf message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, - message_id=self.MSG_ID_HD_SAFETY_SHUTDOWN_OVERRIDE, + message_id=MsgIds.MSG_ID_HD_SAFETY_SHUTDOWN_OVERRIDE, payload=payload) self.logger.debug("overriding HD safety shutdown") @@ -203,3 +203,33 @@ else: self.logger.debug("Timeout!!!!") return False + + def cmd_hd_software_reset_request(self): + """ + Constructs and sends an HD software reset request via CAN bus. + Constraints: + Must be logged into HD. + + \returns response message if received, False if no response received + + @return: 1 if successful, zero otherwise + + """ + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_SOFTWARE_RESET_REQUEST) + + self.logger.debug("requesting HD software reset") + + # Send message + received_message = self.can_interface.send(message) + + if received_message is not None: + if received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] == 1: + self.logger.debug("HD is resetting...") + else: + self.logger.debug("HD reset request failed.") + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False Index: dialin/hd/treatment.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/treatment.py (.../treatment.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/treatment.py (.../treatment.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -17,6 +17,7 @@ from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish +from ..utils.conversions import integer_to_bytearray, float_to_bytearray from logging import Logger @@ -29,8 +30,43 @@ # treatment message IDs MSG_ID_HD_TREATMENT_TIME_PUBLISHED_DATA = 0x000D - MSG_ID_HD_TREATMENT_STATE_PUBLISHED_DATA = 0X000F + MSG_ID_HD_TREATMENT_STATE_PUBLISHED_DATA = 0x000F + MSG_ID_HD_SET_TREATMENT_PARAMETER = 0x802D + # Treatment Parameter IDs + HD_TREATMENT_PARAMETER_BLOOD_FLOW = 0 + HD_TREATMENT_PARAMETER_DIALYSATE_FLOW = 1 + HD_TREATMENT_PARAMETER_TREATMENT_DURATION = 2 + HD_TREATMENT_PARAMETER_HEPARIN_PRE_STOP_TIME = 3 + HD_TREATMENT_PARAMETER_SALINE_BOLUS_VOLUME = 4 + HD_TREATMENT_PARAMETER_ACID_CONCENTRATE = 5 + HD_TREATMENT_PARAMETER_BICARB_CONCENTRATE = 6 + HD_TREATMENT_PARAMETER_DIALYZER_TYPE = 7 + HD_TREATMENT_PARAMETER_BP_MEAS_INTERVAL = 8 + HD_TREATMENT_PARAMETER_RINSEBACK_FLOW_RATE = 9 + HD_TREATMENT_PARAMETER_ART_PRESSURE_LOW_LIMIT = 10 + HD_TREATMENT_PARAMETER_ART_PRESSURE_HIGH_LIMIT = 11 + HD_TREATMENT_PARAMETER_VEN_PRESSURE_LOW_LIMIT = 12 + HD_TREATMENT_PARAMETER_VEN_PRESSURE_HIGH_LIMIT = 13 + HD_TREATMENT_PARAMETER_HEPARIN_DISPENSE_RATE = 14 + HD_TREATMENT_PARAMETER_HEPARIN_BOLUS_VOLUME = 15 + HD_TREATMENT_PARAMETER_DIALYSATE_TEMPERATURE = 16 + HD_TREATMENT_PARAMETER_UF_VOLUME = 17 + + # Dialyzer Type IDs + DIALYZER_TYPE_NIPRO_ELISIO_H_17 = 0 + DIALYZER_TYPE_NIPRO_ELISIO_H_19 = 1 + DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F160NRE = 2 + DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F180NRE = 3 + + # Acid Concentrate IDs + ACID_CONC_TYPE_FRESENIUS_08_1251_1 = 0 + ACID_CONC_TYPE_FRESENIUS_08_2251_0 = 1 + ACID_CONC_TYPE_FRESENIUS_08_3251_9 = 2 + + # Bicarbonate Concentrate IDs + BICARB_CONC_TYPE_FRESENIUS_CENTRISOL = 0 + # treatment time broadcast message field positions START_POS_TIME_PRES = DenaliMessage.PAYLOAD_START_INDEX END_POS_TIME_PRES = START_POS_TIME_PRES + 4 @@ -172,3 +208,663 @@ self.saline_bolus_in_progress = True else: self.saline_bolus_in_progress = False + + def cmd_set_treatment_param_blood_flow_rate(self, flow): + """ + Constructs and sends the set blood flow rate treatment parameter command. + This will only set the treatment parameter setting. It will not immediately + set the blood pump on with this set point. + Constraints: + Must be logged into HD. + Flow must be positive integer and should be between 100 and 500 mL/min + + @param flow: integer - set blood flow rate (in mL/min) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_BLOOD_FLOW) + flo = integer_to_bytearray(flow) + payload = par + flo + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting blood flow rate") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_flo = str(flow) + print("Blood flow rate parameter set to " + str_flo + " mL/min: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_treatment_param_dialysate_flow_rate(self, flow): + """ + Constructs and sends the set dialysate flow rate treatment parameter command. + This will only set the treatment parameter setting. It will not immediately + set the dialysate inlet pump on with this set point. + Constraints: + Must be logged into HD. + Flow must be positive integer and should be between 100 and 600 mL/min + + @param flow: integer - set blood flow rate (in mL/min) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_DIALYSATE_FLOW) + flo = integer_to_bytearray(flow) + payload = par + flo + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting dialysate flow rate") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_flo = str(flow) + print("Dialysate flow rate parameter set to " + str_flo + " mL/min: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_treatment_param_duration(self, duration): + """ + Constructs and sends the set treatment duration parameter command. + Constraints: + Must be logged into HD. + Duration must be positive integer and should be between 60 and 480 min + + @param duration: integer - set treatment duration (in min) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_TREATMENT_DURATION) + dur = integer_to_bytearray(duration) + payload = par + dur + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting treatment duration") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_dur = str(duration) + print("Treatment duration parameter set to " + str_dur + " min: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_heparin_pre_stop_time(self, pre_stop): + """ + Constructs and sends the set Heparin pre-stop time parameter command. + Constraints: + Must be logged into HD. + Pre-stop time for Heparin must be positive integer and should be between 0 and 120 min + + @param pre_stop: integer - set Heparin pre-stop time (in min) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_HEPARIN_PRE_STOP_TIME) + sto = integer_to_bytearray(pre_stop) + payload = par + sto + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting Heparin pre-stop time") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_sto = str(pre_stop) + print("Heparin pre-stop time parameter set to " + str_sto + " min: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_saline_bolus_volume(self, volume): + """ + Constructs and sends the set saline bolus volume parameter command. + Constraints: + Must be logged into HD. + Volume must be positive integer and should be between 0 and 300 mL + + @param volume: integer - set saline bolus volume (in mL) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_SALINE_BOLUS_VOLUME) + vol = integer_to_bytearray(volume) + payload = par + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting saline bolus volume") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_vol = str(volume) + print("Saline bolus volume parameter set to " + str_vol + " mL: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_acid_concentrate(self, acid): + """ + Constructs and sends the set acid concentrate parameter command. + Constraints: + Must be logged into HD. + Acid ID must be positive integer and should be between 0 and 2 (see below) + ACID_CONC_TYPE_FRESENIUS_08_1251_1 = 0 + ACID_CONC_TYPE_FRESENIUS_08_2251_0 = 1 + ACID_CONC_TYPE_FRESENIUS_08_3251_9 = 2 + + @param acid: integer - set acid concentrate type + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_ACID_CONCENTRATE) + acd = integer_to_bytearray(acid) + payload = par + acd + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting acid concentrate parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_acd = str(acid) + print("Acid concentrate parameter set to " + str_acd + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_bicarb_concentrate(self, bicarb): + """ + Constructs and sends the set bicarbonate concentrate parameter command. + Constraints: + Must be logged into HD. + Bicarb ID must be positive integer and should be between 0 and 0 (see below) + BICARB_CONC_TYPE_FRESENIUS_CENTRISOL = 0 + + @param bicarb: integer - set bicarbonate concentrate type + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_BICARB_CONCENTRATE) + bic = integer_to_bytearray(bicarb) + payload = par + bic + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting bicarbonate concentrate parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_bic = str(bicarb) + print("Bicarbonate concentrate parameter set to " + str_bic + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_dialyzer_type(self, dialyzer): + """ + Constructs and sends the set dialyzer type parameter command. + Constraints: + Must be logged into HD. + Dialyzer ID must be positive integer and should be between 0 and 3 (see below) + DIALYZER_TYPE_NIPRO_ELISIO_H_17 = 0 + DIALYZER_TYPE_NIPRO_ELISIO_H_19 = 1 + DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F160NRE = 2 + DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F180NRE = 3 + + @param dialyzer: integer - set dialyzer type + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_DIALYZER_TYPE) + dia = integer_to_bytearray(dialyzer) + payload = par + dia + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting dialyzer type parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_dia = str(dialyzer) + print("Dialyzer type parameter set to " + str_dia + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_bp_measurement_interval(self, intvl): + """ + Constructs and sends the set blood pressure measurement interval parameter command. + Constraints: + Must be logged into HD. + Interval must be positive integer and should be between 0 and 60 min + + @param intvl: integer - set blood pressure measurement interval (in min) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_BP_MEAS_INTERVAL) + bpi = integer_to_bytearray(intvl) + payload = par + bpi + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting BP measurement interval parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_bpi = str(intvl) + print("BP measurement interval parameter set to " + str_bpi + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_rinseback_flow_rate(self, flow): + """ + Constructs and sends the set rinseback flow rate parameter command. + Constraints: + Must be logged into HD. + Flow must be positive integer and should be between 50 and 175 mL/min + + @param flow: integer - set rinseback flow rate (in mL/min) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_RINSEBACK_FLOW_RATE) + flo = integer_to_bytearray(flow) + payload = par + flo + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting rinseback flow rate parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_flo = str(flow) + print("Rinseback flow rate parameter set to " + str_flo + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_arterial_pressure_low_alarm_limit(self, pres): + """ + Constructs and sends the set arterial pressure lower alarm limit parameter command. + Constraints: + Must be logged into HD. + Pressure must be integer and should be between -300 and +200 mmHg + + @param pres: integer - set arterial pressure lower alarm limit (in mmHg) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_ART_PRESSURE_LOW_LIMIT) + pre = integer_to_bytearray(pres) + payload = par + pre + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting arterial pressure lower alarm limit parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_pre = str(pres) + print("Arterial pressure lower alarm limit parameter set to " + str_pre + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_arterial_pressure_high_alarm_limit(self, pres): + """ + Constructs and sends the set arterial pressure upper alarm limit parameter command. + Constraints: + Must be logged into HD. + Pressure must be integer and should be between -300 and +200 mmHg + + @param pres: integer - set arterial pressure upper alarm limit (in mmHg) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_ART_PRESSURE_HIGH_LIMIT) + pre = integer_to_bytearray(pres) + payload = par + pre + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting arterial pressure upper alarm limit parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_pre = str(pres) + print("Arterial pressure upper alarm limit parameter set to " + str_pre + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_venous_pressure_low_alarm_limit(self, pres): + """ + Constructs and sends the set venous pressure lower alarm limit parameter command. + Constraints: + Must be logged into HD. + Pressure must be integer and should be between -100 and +600 mmHg + + @param pres: integer - set venous pressure lower alarm limit (in mmHg) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_VEN_PRESSURE_LOW_LIMIT) + pre = integer_to_bytearray(pres) + payload = par + pre + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting venous pressure lower alarm limit parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_pre = str(pres) + print("Venous pressure lower alarm limit parameter set to " + str_pre + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_venous_pressure_high_alarm_limit(self, pres): + """ + Constructs and sends the set venous pressure upper alarm limit parameter command. + Constraints: + Must be logged into HD. + Pressure must be integer and should be between -100 and +600 mmHg + + @param pres: integer - set venous pressure upper alarm limit (in mmHg) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_VEN_PRESSURE_HIGH_LIMIT) + pre = integer_to_bytearray(pres) + payload = par + pre + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting venous pressure upper alarm limit parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_pre = str(pres) + print("Venous pressure upper alarm limit parameter set to " + str_pre + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_heparin_dispense_rate(self, rate): + """ + Constructs and sends the set Heparin dispense rate parameter command. + Constraints: + Must be logged into HD. + Rate must be floating point value and should be between 0 and 1.0 mL/hr + + @param rate: float - set Heparin dispense rate (in mL/hr) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_HEPARIN_DISPENSE_RATE) + rat = float_to_bytearray(rate) + payload = par + rat + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting Heparin dispense rate parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_rat = str(rate) + print("Heparin dispense rate parameter set to " + str_rat + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_heparin_bolus_volume(self, volume): + """ + Constructs and sends the set Heparin bolus volume parameter command. + Constraints: + Must be logged into HD. + Volume must be floating point value and should be between 0 and 2.0 mL + + @param volume: float - set Heparin bolus volume (in mL) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_HEPARIN_BOLUS_VOLUME) + vol = float_to_bytearray(volume) + payload = par + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting Heparin bolus volume parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_vol = str(volume) + print("Heparin bolus volume parameter set to " + str_vol + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_dialysate_tempeature(self, temp): + """ + Constructs and sends the set dialysate temperature parameter command. + Constraints: + Must be logged into HD. + Temperature must be floating point value and should be between 35.0 and 38.0 deg C + + @param temp: float - set dialysate temperature (in deg C) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_DIALYSATE_TEMPERATURE) + tmp = float_to_bytearray(temp) + payload = par + tmp + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting dialysate temperature parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_tmp = str(temp) + print("Dialysate temperature parameter set to " + str_tmp + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False + + def cmd_set_ultrafiltration_volume(self, volume): + """ + Constructs and sends the set ultrafiltration volume parameter command. + Constraints: + Must be logged into HD. + Volume must be floating point value and should be between 0.0 and 8.0L + + @param volume: float - set ultrafiltration volume (in L) + @return: 1 if successful, zero otherwise + """ + + par = integer_to_bytearray(self.HD_TREATMENT_PARAMETER_UF_VOLUME) + vol = float_to_bytearray(volume) + payload = par + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_HD_SET_TREATMENT_PARAMETER, + payload=payload) + + print("setting ultrafiltration volume parameter") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # print(received_message) + str_vol = str(volume) + print("Ultrafiltration volume parameter set to " + str_vol + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + print("Timeout!!!!") + return False Index: dialin/hd/ui_proxy.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- dialin/hd/ui_proxy.py (.../ui_proxy.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ dialin/hd/ui_proxy.py (.../ui_proxy.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -41,15 +41,30 @@ MSG_ID_UI_REQUEST_HD_VERSION = 0x001C MSG_ID_UI_HD_VERSION_RESPONSE = 0x001D MSG_ID_UF_SETTINGS_CHANGE_CONFIRM_RESPONSE_FROM_HD = 0x002E + MSG_ID_UI_NEW_TREATMENT_PARAMS = 0x35 + MSG_ID_HD_NEW_TREATMENT_PARAMS_RESPONSE = 0x36 + MSG_ID_UI_START_TREATMENT = 0x38 + MSG_ID_UI_USER_CONFIRM_TREATMENT_PARAMS = 0x3B LITER_TO_ML_CONVERSION_FACTOR = 1000.0 + + # UF pause/resume command IDs UF_CMD_PAUSE = 0 UF_CMD_RESUME = 1 + + # UF change option IDs UF_CMD_CHANGE_TIME_TO_ADJUST = 0 UF_CMD_CHANGE_RATE_TO_ADJUST = 1 + + # in-treatment change response codes RESPONSE_REJECTED = 0 RESPONSE_ACCEPTED = 1 + # start treatment command IDs + START_TREATMENT_CMD_INITIATE_TREATMENT_WORKFLOW = 0 + START_TREATMENT_CMD_CANCEL_TREATMENT_WORKFLOW = 1 + START_TREATMENT_CMD_START_TREATMENT = 2 + REQUEST_REJECT_REASON_NONE = 0 REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE = 1 REQUEST_REJECT_REASON_TIMEOUT_WAITING_FOR_USER_CONFIRM = 2 @@ -166,25 +181,64 @@ if self.can_interface is not None: channel_id = DenaliChannels.hd_to_ui_ch_id self.can_interface.register_receiving_publication_function(channel_id, - self.MSG_ID_UF_SETTINGS_CHANGE_RESPONSE_FROM_HD, - self._handler_uf_change_response) + 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.can_interface.register_receiving_publication_function(channel_id, - self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_RESPONSE_FROM_HD, - self._handler_treatment_duration_change_response) + self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_RESPONSE_FROM_HD, + self._handler_treatment_duration_change_response) self.can_interface.register_receiving_publication_function(channel_id, - self.MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_RESPONSE_FROM_HD, - self._handler_blood_and_dialysate_change_response) + self.MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_RESPONSE_FROM_HD, + self._handler_blood_and_dialysate_change_response) self.can_interface.register_receiving_publication_function(channel_id, self.MSG_ID_HD_TREATMENT_PARAMS_RANGES, - self._handler_treatment_param_ranges) + self._handler_treatment_param_ranges) self.can_interface.register_receiving_publication_function(DenaliChannels.hd_to_ui_ch_id, self.MSG_ID_UI_HD_VERSION_RESPONSE, - self._handler_hd_version) + self._handler_hd_version) + self.can_interface.register_receiving_publication_function(DenaliChannels.hd_to_ui_ch_id, self.MSG_ID_HD_NEW_TREATMENT_PARAMS_RESPONSE, + self._handler_treatment_param_settings) # 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 + self.treatment_duration = 0 + self.heparin_dispense_rate = 0.0 + self.heparin_bolus_volume = 0.0 + self.heparin_pre_stop_time = 0 + self.saline_bolus_volume = 0 + self.acid_concentrate = 0 + self.bicarb_concentrate = 0 + self.dialyzer_type = 0 + self.dialysate_temperature = 0.0 + self.arterial_pressure_low_alarm_limit = 0 + self.arterial_pressure_high_alarm_limit = 0 + self.venous_pressure_low_alarm_limit = 0 + self.venous_pressure_high_alarm_limit = 0 + self.blood_pressure_meas_interval = 0 + self.rinseback_flow_rate = 0 + # initialize variables that will be populated by treatment parameters response message + self.treatment_parameters_valid = False + self.blood_flow_rate_reject_reason = 0 + self.dialysate_flow_rate_reject_reason = 0 + self.treatment_duration_reject_reason = 0 + self.heparin_dispense_rate_reject_reason = 0 + self.heparin_bolus_volume_reject_reason = 0 + self.heparin_pre_stop_time_reject_reason = 0 + self.saline_bolus_volume_reject_reason = 0 + self.acid_concentrate_reject_reason = 0 + self.bicarb_concentrate_reject_reason = 0 + self.dialyzer_type_reject_reason = 0 + self.dialysate_temperature_reject_reason = 0 + self.arterial_pressure_low_alarm_limit_reject_reason = 0 + self.arterial_pressure_high_alarm_limit_reject_reason = 0 + self.venous_pressure_low_alarm_limit_reject_reason = 0 + self.venous_pressure_high_alarm_limit_reject_reason = 0 + self.blood_pressure_meas_interval_reject_reason = 0 + self.rinseback_flow_rate_reject_reason = 0 # initialize variables that will be populated by treatment parameter ranges message self.min_treatment_duration_min = 0 self.max_treatment_duration_min = 0 @@ -408,6 +462,168 @@ """ return self.target_dialysate_flow_rate + def get_treatment_parameters_valid(self): + """ + Gets the T/F flag for whether treatment parameters + are considered valid by HD. + + @return: True if treatment parameters are valid, False if not + """ + return self.treatment_parameters_valid + + def get_blood_flow_rate_reject_reason(self): + """ + Gets the reject reason code for the blood flow rate + treatment parameter. + + @return: The reject reason code for the blood flow rate + """ + return self.blood_flow_rate_reject_reason + + def get_dialysate_flow_rate_reject_reason(self): + """ + Gets the reject reason code for the dialysate flow rate + treatment parameter. + + @return: The reject reason code for the dialysate flow rate + """ + return self.dialysate_flow_rate_reject_reason + + def get_treatment_duration_reject_reason(self): + """ + Gets the reject reason code for the treatment duration + treatment parameter. + + @return: The reject reason code for the treatment duration + """ + return self.treatment_duration_reject_reason + + def get_heparin_pre_stop_time_reject_reason(self): + """ + Gets the reject reason code for the Heparin pre-stop + time treatment parameter. + + @return: The reject reason code for the Heparin pre-stop time + """ + return self.heparin_pre_stop_time_reject_reason + + def get_saline_bolus_volume_reject_reason(self): + """ + Gets the reject reason code for the saline bolus volume + treatment parameter. + + @return: The reject reason code for the saline bolus volume + """ + return self.saline_bolus_volume_reject_reason + + def get_acid_concentrate_reject_reason(self): + """ + Gets the reject reason code for the acid concentrate + treatment parameter. + + @return: The reject reason code for the acid concentrate + """ + return self.acid_concentrate_reject_reason + + def get_bicarb_concentrate_reject_reason(self): + """ + Gets the reject reason code for the bicarbonate + concentrate treatment parameter. + + @return: The reject reason code for the bicarbonate concentrate + """ + return self.bicarb_concentrate_reject_reason + + def get_dialyzer_type_reject_reason(self): + """ + Gets the reject reason code for the dialyzer type treatment + parameter. + + @return: The reject reason code for the dialyzer type + """ + return self.dialyzer_type_reject_reason + + def get_blood_pressure_meas_interval_reject_reason(self): + """ + Gets the reject reason code for the BP measurement interval + treatment parameter. + + @return: The reject reason code for the BP measurement interval + """ + return self.blood_pressure_meas_interval_reject_reason + + def get_rinseback_flow_rate_reject_reason(self): + """ + Gets the reject reason code for the rinseback flow rate + treatment parameter. + + @return: The reject reason code for the rinseback flow rate + """ + return self.rinseback_flow_rate_reject_reason + + def get_arterial_pressure_low_alarm_limit_reject_reason(self): + """ + Gets the reject reason code for the arterial pressure low alarm + limit treatment parameter. + + @return: The reject reason code for the arterial pressure low alarm limit + """ + return self.arterial_pressure_low_alarm_limit_reject_reason + + def get_arterial_pressure_high_alarm_limit_reject_reason(self): + """ + Gets the reject reason code for the arterial pressure high alarm + limit treatment parameter. + + @return: The reject reason code for the arterial pressure high alarm limit + """ + return self.arterial_pressure_high_alarm_limit_reject_reason + + def get_venous_pressure_low_alarm_limit_reject_reason(self): + """ + Gets the reject reason code for the venous pressure low alarm + limit treatment parameter. + + @return: The reject reason code for the venous pressure low alarm limit + """ + return self.venous_pressure_low_alarm_limit_reject_reason + + def get_venous_pressure_high_alarm_limit_reject_reason(self): + """ + Gets the reject reason code for the venous pressure high alarm + limit treatment parameter. + + @return: The reject reason code for the venous pressure high alarm limit + """ + return self.venous_pressure_high_alarm_limit_reject_reason + + def get_heparin_dispense_rate_reject_reason(self): + """ + Gets the reject reason code for the Heparin dispense rate + treatment parameter. + + @return: The reject reason code for the Heparin dispense rate + """ + return self.heparin_dispense_rate_reject_reason + + def get_heparin_bolus_volume_reject_reason(self): + """ + Gets the reject reason code for the Heparin bolus volume + treatment parameter. + + @return: The reject reason code for the Heparin bolus volume + """ + return self.heparin_bolus_volume_reject_reason + + def get_dialysate_temperature_reject_reason(self): + """ + Gets the reject reason code for the dialysate temperature + treatment parameter. + + @return: The reject reason code for dialysate temperature + """ + return self.dialysate_temperature_reject_reason + @_publish([ "hd_version" "fpga_version" @@ -451,6 +667,111 @@ self.logger.debug(f"HD FPGA VERSION: {self.fpga_version}") @_publish([ + "treatment_parameters_valid", + "blood_flow_rate_reject_reason", + "dialysate_flow_rate_reject_reason", + "treatment_duration_reject_reason", + "heparin_pre_stop_time_reject_reason", + "saline_bolus_volume_reject_reason", + "acid_concentrate_reject_reason", + "bicarb_concentrate_reject_reason", + "dialyzer_type_reject_reason", + "blood_pressure_meas_interval_reject_reason", + "rinseback_flow_rate_reject_reason", + "arterial_pressure_low_alarm_limit_reject_reason", + "arterial_pressure_high_alarm_limit_reject_reason", + "venous_pressure_low_alarm_limit_reject_reason", + "venous_pressure_high_alarm_limit_reject_reason", + "heparin_dispense_rate_reject_reason", + "heparin_bolus_volume_reject_reason", + "dialysate_temperature_reject_reason" + ]) + def _handler_treatment_param_settings(self, message): + """ + Handler for response from HD regarding validation of treatment parameters. + + @param message: response message from HD regarding validity of provided treatment parameters.\n + U32 0=Treatment parameters are valid, 1=Treatment parameters are invalid. \n + U32 Reject reason code for blood flow rate (0=valid). \n + U32 Reject reason code for dialysate flow rate (0=valid). \n + U32 Reject reason code for treatment duration (0=valid). \n + U32 Reject reason code for Heparin pre-stop time (0=valid). \n + U32 Reject reason code for saline bolus volume (0=valid). \n + U32 Reject reason code for acid concentrate (0=valid). \n + U32 Reject reason code for bicarb concentrate (0=valid). \n + U32 Reject reason code for dialyzer type (0=valid). \n + U32 Reject reason code for BP measurement interval (0=valid). \n + U32 Reject reason code for rinseback flow rate (0=valid). \n + U32 Reject reason code for arterial pressure low alarm limit (0=valid). \n + U32 Reject reason code for arterial pressure high alarm limit (0=valid). \n + U32 Reject reason code for venous pressure low alarm limit (0=valid). \n + U32 Reject reason code for venous pressure high alarm limit (0=valid). \n + U32 Reject reason code for Heparin dispense rate (0=valid). \n + U32 Reject reason code for Heparin bolus volume (0=valid). \n + U32 Reject reason code for dialysate temperature (0=valid). \n + + @return: none + """ + p = DenaliMessage.PAYLOAD_START_INDEX + val = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + bld = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + dia = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + dur = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + sto = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + sal = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + acd = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + bic = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + dlz = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + bpi = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + rbf = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + apl = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + aph = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + vpl = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + vph = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + hdr = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + hbv = struct.unpack('i', bytearray(message['message'][p:p+4])) + p += 4 + tmp = struct.unpack('i', bytearray(message['message'][p:p+4])) + + if val[0] == 1: + self.treatment_parameters_valid = True + else: + self.treatment_parameters_valid = False + self.blood_flow_rate_reject_reason = bld[0] + self.dialysate_flow_rate_reject_reason = dia[0] + self.treatment_duration_reject_reason = dur[0] + self.heparin_pre_stop_time_reject_reason = sto[0] + self.saline_bolus_volume_reject_reason = sal[0] + self.acid_concentrate_reject_reason = acd[0] + self.bicarb_concentrate_reject_reason = bic[0] + self.dialyzer_type_reject_reason = dlz[0] + self.blood_pressure_meas_interval_reject_reason = bpi[0] + self.rinseback_flow_rate_reject_reason = rbf[0] + self.arterial_pressure_low_alarm_limit_reject_reason = apl[0] + self.arterial_pressure_high_alarm_limit_reject_reason = aph[0] + self.venous_pressure_low_alarm_limit_reject_reason = vpl[0] + self.venous_pressure_high_alarm_limit_reject_reason = vph[0] + self.heparin_dispense_rate_reject_reason = hdr[0] + self.heparin_bolus_volume_reject_reason = hbv[0] + self.dialysate_temperature_reject_reason = tmp[0] + + @_publish([ "min_treatment_duration_min", "max_treatment_duration_min", "min_uf_volume_ml", @@ -608,10 +929,8 @@ self.uf_change_succeeded = resp self.uf_change_reject_reason = rea[0] - self.uf_change_volume_ml = vol[0] / self.LITER_TO_ML_CONVERSION_FACTOR + self.uf_change_volume_ml = vol[0] #/ self.LITER_TO_ML_CONVERSION_FACTOR - #self.uf_change_volume_ml = vol[0] In the staging branch commented out - self.uf_change_time_min = tim[0] self.uf_change_time_diff = tmd[0] self.uf_change_rate_ml_min = rat[0] @@ -715,13 +1034,13 @@ """ Constructs and sends a ui UF change settings command message - @param vol (float): new ultrafiltration volume setting (in L) + @param vol: (float) new ultrafiltration volume setting (in L) @return: none """ # reset response to this command so we can tell when response is received - self.UFChangeResponse = None + #self.UFChangeResponse = None # build command message volume = float_to_bytearray(vol * self.LITER_TO_ML_CONVERSION_FACTOR) message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, @@ -738,14 +1057,14 @@ """ Constructs and sends a ui UF change settings command message - @param vol (float): new ultrafiltration volume setting (in L) - @param adj (int): 0 for adjust time, 1 for adjust rate + @param vol: (float) new ultrafiltration volume setting (in L) + @param adj: (int) 0 for adjust time, 1 for adjust rate @return: none """ # reset response to this command so we can tell when response is received - self.UFChangeResponse = None + #self.UFChangeResponse = None # build command message volume = float_to_bytearray(vol * self.LITER_TO_ML_CONVERSION_FACTOR) @@ -787,16 +1106,16 @@ return 0 - def cmd_ui_treatment_duration_setting_change_request(self, timeMin=0): + def cmd_ui_treatment_duration_setting_change_request(self, time_min=0): """ Constructs and sends a ui UF change settings confirmed by user message - @param timeMin (int): treatment time (in min). + @param time_min: (int) treatment time (in min). @return: none """ - payload = integer_to_bytearray(timeMin) + payload = integer_to_bytearray(time_min) message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, message_id=self.MSG_ID_TREATMENT_DURATION_SETTING_CHANGE_REQUEST, payload=payload) @@ -808,18 +1127,18 @@ return 0 - def cmd_ui_blood_and_dialysate_flow_settings_change_request(self, bloodFlow, dialFlow): + def cmd_ui_blood_and_dialysate_flow_settings_change_request(self, blood_flow, dial_flow): """ Constructs and sends a ui blood & dialysate flow settings change request by user message - @param bloodFlow (int): blood flow rate set point (in mL/min). - @param dialFlow (int): dialysate flow rate set point (in mL/min). + @param blood_flow: (int) blood flow rate set point (in mL/min). + @param dial_flow: (int) dialysate flow rate set point (in mL/min). @return: none """ - bld = integer_to_bytearray(bloodFlow) - dial = integer_to_bytearray(dialFlow) + bld = integer_to_bytearray(blood_flow) + dial = integer_to_bytearray(dial_flow) payload = bld + dial message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, message_id=self.MSG_ID_BLOOD_DIALYSATE_FLOW_SETTING_CHANGE_REQUEST_BY_USER, @@ -830,3 +1149,120 @@ self.can_interface.send(message, 0) return 0 + + def cmd_ui_start_treatment_request(self, cmnd=START_TREATMENT_CMD_INITIATE_TREATMENT_WORKFLOW): + """ + Constructs and sends a ui start treatment command message + Constraints: + Command must be one of the following: + START_TREATMENT_CMD_INITIATE_TREATMENT_WORKFLOW = 0 + START_TREATMENT_CMD_CANCEL_TREATMENT_WORKFLOW = 1 + START_TREATMENT_CMD_START_TREATMENT = 2 + + @param cmnd: (int) start treatment command code + + @return: none + """ + + cmd = integer_to_bytearray(cmnd) + payload = cmd + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_UI_START_TREATMENT, + payload=payload) + + self.logger.debug("Sending start treatment command request.") + + self.can_interface.send(message, 0) + + return 0 + + def set_treatment_parameters(self, bld_flow, dia_flow, duration, hep_rate, hep_bol, hep_stop, sal_bol, + acid, bicarb, dialyzer, dia_temp, art_low, art_high, ven_low, ven_high, + bp_intvl, rb_flow): + """ + Constructs and sends a ui set treatment parameters message + Constraints: + HD must be in treatment parameters mode + + @param bld_flow: (int) blood flow rate (in mL/min) + @param dia_flow: (int) dialysate flow rate (in mL/min) + @param duration: (int) treatment duration (in min) + @param hep_rate: (float) Heparin dispense rate (in mL/hr) + @param hep_bol: (float) Heparin bolus volume (in mL) + @param hep_stop: (int) Heparin pre-stop time (in min) + @param sal_bol: (int) Saline bolus volume (in mL) + @param acid: (int) acid concentrate type + @param bicarb: (int) bicarbonate concentrate type + @param dialyzer: (int) dialyzer type + @param dia_temp: (float) dialysate temperature (in deg C) + @param art_low: (int) arterial pressure low alarm limit (in mmHg) + @param art_high: (int) arterial pressure high alarm limit (in mmHg) + @param ven_low: (int) venous pressure low alarm limit (in mmHg) + @param ven_high: (int) venous pressure high alarm limit (in mmHg) + @param bp_intvl: (int) blood pressure measurement interval (in min) + @param rb_flow: (int) rinseback flow rate (in mL/min) + + @return: none + """ + self.blood_flow_rate = bld_flow + self.dialysate_flow_rate = dia_flow + self.treatment_duration = duration + self.heparin_pre_stop_time = hep_stop + self.saline_bolus_volume = sal_bol + self.acid_concentrate = acid + self.bicarb_concentrate = bicarb + self.dialyzer_type = dialyzer + self.blood_pressure_meas_interval = bp_intvl + self.rinseback_flow_rate = rb_flow + self.arterial_pressure_low_alarm_limit = art_low + self.arterial_pressure_high_alarm_limit = art_high + self.venous_pressure_low_alarm_limit = ven_low + self.venous_pressure_high_alarm_limit = ven_high + self.heparin_dispense_rate = hep_rate + self.heparin_bolus_volume = hep_bol + self.dialysate_temperature = dia_temp + + bld = integer_to_bytearray(bld_flow) + dia = integer_to_bytearray(dia_flow) + dur = integer_to_bytearray(duration) + hps = integer_to_bytearray(hep_stop) + sal = integer_to_bytearray(sal_bol) + acc = integer_to_bytearray(acid) + bic = integer_to_bytearray(bicarb) + dzr = integer_to_bytearray(dialyzer) + bpi = integer_to_bytearray(bp_intvl) + rbf = integer_to_bytearray(rb_flow) + apl = integer_to_bytearray(art_low) + aph = integer_to_bytearray(art_high) + vpl = integer_to_bytearray(ven_low) + vph = integer_to_bytearray(ven_high) + hdr = float_to_bytearray(hep_rate) + hbv = float_to_bytearray(hep_bol) + tmp = float_to_bytearray(dia_temp) + + payload = bld+dia+dur+hps+sal+acc+bic+dzr+bpi+rbf+apl+aph+vpl+vph+hdr+hbv+tmp + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_UI_NEW_TREATMENT_PARAMS, + payload=payload) + + self.logger.debug("Sending treatment parameters to HD.") + + self.can_interface.send(message, 0) + + return 0 + + def cmd_ui_confirm_treatment_parameters(self): + """ + Constructs and sends a ui confirm treatment parameters message + + @return: none + """ + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=self.MSG_ID_UI_USER_CONFIRM_TREATMENT_PARAMS) + + self.logger.debug("Sending confirm treatment parameters messge.") + + self.can_interface.send(message, 0) + + return 0 Index: tests/test_hd.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- tests/test_hd.py (.../test_hd.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ tests/test_hd.py (.../test_hd.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -76,10 +76,9 @@ # hd.bloodflow.cmd_blood_flow_broadcast_interval_override(RESET,0) - # FIXME: Update passing criteria + # TODO: Update passing criteria self.assertTrue(True) if __name__ == '__main__': unittest.main(verbosity=2) - Index: tests/test_uf.py =================================================================== diff -u -r8ea13ae6dd10732bfcc456798f4785c4d88c95d3 -re172ddc204389ba1eb69e14de52bd3b924087031 --- tests/test_uf.py (.../test_uf.py) (revision 8ea13ae6dd10732bfcc456798f4785c4d88c95d3) +++ tests/test_uf.py (.../test_uf.py) (revision e172ddc204389ba1eb69e14de52bd3b924087031) @@ -75,8 +75,17 @@ ", DRo, " + '{:9.2f}'.format(dg.pressures.drain_pump_outlet_pressure) alarms = ", AL.s, " + hd.alarms.get_current_alarms_state() + \ ", AL.t, " + '{:4d}'.format(hd.alarms.alarm_top) + accels = ", AC.x, " + '{:9.4f}'.format(hd.accel.get_accel_vector().x) + \ + ", AC.y, " + '{:9.4f}'.format(hd.accel.get_accel_vector().y) + \ + ", AC.z, " + '{:9.4f}'.format(hd.accel.get_accel_vector().z) + \ + ", ACm.x, " + '{:9.4f}'.format(hd.accel.get_accel_max_vector().x) + \ + ", ACm.y, " + '{:9.4f}'.format(hd.accel.get_accel_max_vector().y) + \ + ", ACm.z, " + '{:9.4f}'.format(hd.accel.get_accel_max_vector().z) + \ + ", ACt.x, " + '{:9.4f}'.format(hd.accel.get_accel_tilts().x) + \ + ", ACt.y, " + '{:9.4f}'.format(hd.accel.get_accel_tilts().y) + \ + ", ACt.z, " + '{:9.4f}'.format(hd.accel.get_accel_tilts().z) - # log data + # log data f.write(modes) f.write(loadCells) f.write(ultraFilt) @@ -86,6 +95,7 @@ f.write(pumpPWMs) f.write(dgPres) f.write(alarms) + f.write(accels) f.write("\n") # print to console @@ -97,6 +107,6 @@ print(" Pump Speeds: "+pumpMeasSpds) print(" Pump PWMs/DACs: "+pumpPWMs) print(" DG Pressures: "+dgPres) + print(" Accelerometer: "+accels) print(" Alarms: "+alarms) -