Index: dialin/ui/hd_simulator.py =================================================================== diff -u -r78dd086f1f9a3ce19b8b6f7e9c2435cee3883522 -r79234eea99779c6adc7d378c30a82dd94c6d01b1 --- dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 78dd086f1f9a3ce19b8b6f7e9c2435cee3883522) +++ dialin/ui/hd_simulator.py (.../hd_simulator.py) (revision 79234eea99779c6adc7d378c30a82dd94c6d01b1) @@ -108,15 +108,16 @@ self.demoCountdownId = None # progress message ID to send while counting down self.demoCountdownChannel = None # used to fake as DG sometimes + # default treatment settings, calculated parameters, accumulated status, and simulated data self.blood_flow_measured_ml_per_min = 188.8 # simulation self.blood_flow_set_point_ml_per_min = 190 # setting self.dialysate_flow_measured_ml_per_min = 229.9 # simulation self.dialysate_flow_set_point_ml_per_min = 250 # setting self.arterial_pressure_mmHg = -80.8 # simulation self.arterial_pressure_mid_mmHg = -118.9 # setting - self.venous_pressure_mmHg = 203.3 # simulation - self.venous_pressure_mid_mmHg = 165.2 # param + self.venous_pressure_mmHg = 213.3 # simulation + self.venous_pressure_mid_mmHg = 185.2 # param self.treatment_time_sec = 60 * 100 # setting self.uf_volume_set_l = 1.2 # setting self.uf_volume_out_l = 0 # status @@ -133,8 +134,8 @@ self.rinseback_volume_out_ml = 0 # status self.rinseback_volume_all_ml = 0 # status self.rinsebackVelocity_ml_per_sec = 100.0 / 60.0 # param - self.rinsebackVelocity_max_ml_per_sec = 150.0 / 60.0 # fixed R - self.rinsebackVelocity_min_ml_per_sec = 50.0 / 60.0 # fixed R + self.rinsebackVelocity_max_ml_per_sec = 300.0 / 60.0 # fixed R + self.rinsebackVelocity_min_ml_per_sec = 100.0 / 60.0 # fixed R self.heparin_rate_ml_per_hr = 0 # setting self.heparin_bolus_ml = 0 # setting self.heparin_stop_min = 0 # setting @@ -143,10 +144,12 @@ self.heparin_state = HeparinStates.HEPARIN_STATE_STOPPED # status self.saline_state = SalineBolusStates.SALINE_BOLUS_STATE_IDLE.value # status self.uf_state = TreatmentStates.TREATMENT_DIALYSIS_STATE.value # status + self.blood_prime_set_point_blood_flow = 0 self.demo_treatment_params = DenaliData() self.demoSemaphore = Semaphore(1) # keep message and timer handling separate + self.message_queue_mutex = threads_here.Lock() self.auto_response = auto_response self._log_manager = LogManager(log_level=log_level, log_filepath=self.__class__.__name__ + ".log") @@ -309,6 +312,20 @@ self.rinseback_volume_out_ml = self.rinseback_volume_set_ml - 1 self.rinseback_volume_all_ml = self.rinseback_volume_out_ml + elif (msg_id == MsgIds.MSG_ID_UI_CONFIRMATION_RESULT_RESPONSE.value) and \ + (params.data_int[0] == 6): + rsp_id = MsgIds.MSG_ID_HD_UI_CONFIRMATION_REQUEST + rsp_payload = integer_to_bytearray(6) + if params.data_int[1] == EResponse.Accepted: + rsp_payload += integer_to_bytearray(1) + rsp_payload += integer_to_bytearray(0) + self.demoSelection = 61 # setup event to change Mode + elif params.data_int[1] == 0: + rsp_payload += integer_to_bytearray(3) + rsp_payload += integer_to_bytearray(0) + self.demoCount = 0 + self.demoCounter = 0 + # INITIATE A NEW TREATMENT SETUP ---------------------------------- elif msg_id == MsgIds.MSG_ID_UI_INITIATE_TREATMENT_REQUEST.value: rsp_id = MsgIds.MSG_ID_HD_OP_MODE_DATA @@ -480,22 +497,10 @@ # TREATMENT: PATIENT CONNECT -------------------------------------- elif msg_id == MsgIds.MSG_ID_UI_PATIENT_CONNECTION_BEGIN_REQUEST.value: # 100 x64 'Continue' Priming Complete # TBD!!! Setup UF Range before this - rsp_id = MsgIds.MSG_ID_PRE_TREATMENT_STATE_DATA - rsp_payload = struct.pack( - "<" + str(PreTreatmentSubModes.NUM_OF_HD_PRE_TREATMENT_STATES.value + 1) + "i", - PreTreatmentSubModes.HD_PRE_TREATMENT_WATER_SAMPLE_STATE.value, - 0, # 0 Water sample state - 0, # 1 Consumable self-tests state - 0, # 2 No cartridge self-tests state - 0, # 3 Consumable and cartridge installation state - 0, # 4 Self-tests when the cartridge is dry state - 0, # 5 Prime blood and dialysate circuits and run wet self-tests state - 0, # 6 Re-circulate blood and dialysate circuits state - 0, # 7 TBD! Patient connection state - 0, - 0 - ) - self.demoSelection = 0 # Wait for user + rsp_id = MsgIds.MSG_ID_HD_PATIENT_CONNECTION_BEGIN_RESPONSE + rsp_payload = integer_to_bytearray(EResponse.Accepted) + rsp_payload += integer_to_bytearray(RequestRejectReasons.REQUEST_REJECT_REASON_NONE.value) + self.demoSelection = 12 elif msg_id == MsgIds.MSG_ID_UI_SET_UF_VOLUME_PARAMETER_REQUEST.value: # _79 x4F rsp_id = MsgIds.MSG_ID_HD_SET_UF_VOLUME_PARAMETER_RESPONSE @@ -583,8 +588,8 @@ params.data_int[1] self.dialysate_flow_set_point_ml_per_min = params.data_int[1] self.blood_flow_set_point_ml_per_min = params.data_int[0] - rsp_payload += integer_to_bytearray(params.data_int[1]) # reply with as set - rsp_payload += integer_to_bytearray(params.data_int[0]) + rsp_payload += integer_to_bytearray(self.blood_flow_set_point_ml_per_min) + rsp_payload += integer_to_bytearray(self.dialysate_flow_set_point_ml_per_min) # reply with as set # RINSEBACK ------------------------- elif msg_id == MsgIds.MSG_ID_UI_RINSEBACK_CMD_REQUEST.value: # _82 x52 @@ -604,21 +609,21 @@ self.demoSelection = 44 elif params.data_int[0] == 4: # 4 Resume self.demoSelection = 41 + elif params.data_int[0] == 5: # 5 = "End" during rinseback + rsp_id = MsgIds.MSG_ID_HD_OP_MODE_DATA + rsp_payload = integer_to_bytearray(HDOpModes.MODE_POST.value) + rsp_payload += integer_to_bytearray(PostTreatmentStates.HD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE.value) + self.demoSelection = 0 # Complete Treatment + self.demoCount = 0 # immediately elif params.data_int[0] == 6: # TBD!! Additional # TreatmentRinsebackStates.RINSEBACK_RUN_ADDITIONAL_STATE.value #TBD!! # 4 Additional rinseback volume (10 mL) state of the rinseback sub-mode state machine self.rinseback_volume_set_ml += 10.0 # ++10 mL (R) self.demoSelection = 45 - elif params.data_int[0] == 5 or\ - params.data_int[0] == 8: # TBD!! need real ID consts: "END" 8 = Disconnect without further rinseback 5 = "End" during rinseback - rsp_id = MsgIds.MSG_ID_HD_OP_MODE_DATA - rsp_payload = integer_to_bytearray(HDOpModes.MODE_POST.value) - rsp_payload += integer_to_bytearray(0) - self.demoSelection = 0 # Complete Treatment + elif params.data_int[0] == 8: # "END" 8 = Disconnect without further rinseback + self.demoSelection = 60 # Prompt User self.demoCount = 0 # immediately - # rsp_id = MsgIds.MSG_ID_HD_TREATMENT_LOG_DATA_RESPONSE - self._send_treatment_log() # TBD! pre-sending prior to final transition to better assure receipt(?) elif params.data_int[0] == 9: # TBD!! Additional not implemented 9 = return to treatment rsp_payload = integer_to_bytearray(EResponse.Rejected) @@ -708,7 +713,7 @@ rsp_payload = integer_to_bytearray(HDOpModes.MODE_PRET.value) rsp_payload += integer_to_bytearray(PreTreatmentSubModes.HD_PRE_TREATMENT_WATER_SAMPLE_STATE.value) self.demoSelection = 5 # = PreTreatmentPrimeStates.NUM_OF_HD_PRIME_STATES.value - 1: self.demoTimedIncValue = None ## TBD!! - self.demoSelection = 12 # on next countdown transition + self.demoSelection = 11 # on next countdown transition self.demoCount = 0 # immediate timeout # TBD!! Hack to make counter neat self.demoGroupCounter = self.demoGroupCount # aggregate counter # TBD else: @@ -887,25 +892,6 @@ self.demoSelection = 0 # Then wait for User self.demoGroupCount = None # clear aggregate count - elif self.demoSelection == 13: # Recirculate primed circuits - rsp_id = MsgIds.MSG_ID_PRE_TREATMENT_STATE_DATA - rsp_payload = struct.pack( - "<" + str(PreTreatmentSubModes.NUM_OF_HD_PRE_TREATMENT_STATES.value + 1) + "i", - PreTreatmentSubModes.HD_PRE_TREATMENT_PATIENT_CONNECTION_STATE.value, - 0, # 0 Water sample state - 0, # 1 Consumable self-tests state - 0, # 2 No cartridge self-tests state - 0, # 3 Consumable and cartridge installation state - 0, # 4 Self-tests when the cartridge is dry state - 0, # 5 Prime blood and dialysate circuits and run wet self-tests state - 0, # 6 Re-circulate blood and dialysate circuits state - PRE_TREATMENT_PAT_CONN_WAIT_FOR_UF_VOL_STATE.PRE_TREATMENT_PAT_CONN_WAIT_FOR_UF_VOL_STATE.value, # 7 Patient connection state - 0, - 0 - ) - self.demoSelection = 0 # Then wait for User - self.demoGroupCount = None # clear aggregate count - # ## START TREATMENT GROUP ----------------------------------------- elif self.demoSelection == 20: # Start Treatment @@ -918,6 +904,7 @@ # self.demoGroupCount = int(120 / 8) # TBD! fix const numbers # self.demoGroupCount = -1 self.demoSelection = 21 # Initiate Blood Prime + self.blood_prime_set_point_blood_flow = 20 elif self.demoSelection == 21: # Blood Prime rsp_id = MsgIds.MSG_ID_TREATMENT_STATE_DATA @@ -938,26 +925,6 @@ self.demoCount = int(120 / 8) # TBD! fix const numbers self.demoCounter = -1 # give slight delay - # MSG_ID_PRESSURE_OCCLUSION_DATA ## TBD! combine with Treatment update data - self.arterial_pressure_mmHg += (random() - 0.5) * 10 # TBD! replace magic wander numbers - delta = self.arterial_pressure_mmHg - self.arterial_pressure_mid_mmHg - if abs(delta) > 30: - self.arterial_pressure_mmHg -= copysign(5, delta) - delta = self.venous_pressure_mmHg - self.venous_pressure_mid_mmHg - if abs(delta) > 30: - self.venous_pressure_mmHg -= copysign(5, delta) - self.cmd_set_pressure_occlusion_data(arterial_prs=self.arterial_pressure_mmHg, - venous_prs=self.venous_pressure_mmHg, - blood_pump_occlusion=0, - pressure_limit_state=0, - arterial_min_limt=self.arterial_pressure_mid_mmHg - ( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW.value] / 2), - arterial_max_limt=self.arterial_pressure_mid_mmHg + ( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW.value] / 2), - venous_min_limit=self.venous_pressure_mid_mmHg -( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW.value] / 2), - venous_max_limit=self.venous_pressure_mid_mmHg + ( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW.value] / 2), - arterial_long_filtered_pressure=self.arterial_pressure_mmHg, - venous_long_filtered_pressure=self.venous_pressure_mmHg ) - - ## DIALYSIS GROUP ------------------------------------------------- elif self.demoSelection == 22: # Dialysis # preset timing info @@ -1008,7 +975,7 @@ measured_flow=self.blood_flow_measured_ml_per_min, rot_speed=0, mot_speed=0, mc_speed=0, mc_current=0, pwm=0, rotor_count=0, - pres_flow=0, rotor_hall=0) + pres_flow=self.blood_flow_set_point_ml_per_min, rotor_hall=0) # MSG_ID_DIALYSATE_FLOW_DATA self.dialysate_flow_measured_ml_per_min += (random() - 0.5) * 5 # TBD! replace magic numbers @@ -1022,24 +989,41 @@ mc_current=0.0, pwm=0.0, rotor_count=0, - presFlow=0, + presFlow=self.dialysate_flow_set_point_ml_per_min, rotorHall=0) # MSG_ID_PRESSURE_OCCLUSION_DATA + + arterial_min_limit = int(self.arterial_pressure_mid_mmHg - (self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW.value] / 2)) + arterial_max_limit = int(self.arterial_pressure_mid_mmHg + (self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW.value] / 2)) + venous_min_limit = int(self.venous_pressure_mid_mmHg - (self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW.value] / 2)) + venous_max_limit = int(self.venous_pressure_mid_mmHg + (self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW.value] / 2)) + self.arterial_pressure_mmHg += (random() - 0.5) * 10 # TBD! replace magic wander numbers if self.arterial_pressure_mmHg > (self.arterial_pressure_mid_mmHg + 30): self.arterial_pressure_mmHg -= 5 + # Stay within limits + if int(self.arterial_pressure_mmHg) < arterial_min_limit: + self.arterial_pressure_mmHg = arterial_min_limit + elif int(self.arterial_pressure_mmHg) > arterial_max_limit: + self.arterial_pressure_mmHg = arterial_max_limit + self.venous_pressure_mmHg += (random() - 0.5) * 10 if self.venous_pressure_mmHg > (self.venous_pressure_mid_mmHg + 30): self.venous_pressure_mmHg -= 5 + # Stay within limits + if int(self.venous_pressure_mmHg) < venous_min_limit: + self.venous_pressure_mmHg = venous_min_limit + elif int(self.venous_pressure_mmHg) > venous_max_limit: + self.venous_pressure_mmHg = venous_max_limit self.cmd_set_pressure_occlusion_data(arterial_prs=self.arterial_pressure_mmHg, venous_prs=self.venous_pressure_mmHg, blood_pump_occlusion=0, pressure_limit_state=0, - arterial_min_limt=self.arterial_pressure_mid_mmHg - ( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW.value] / 2), - arterial_max_limt=self.arterial_pressure_mid_mmHg + ( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW.value] / 2), - venous_min_limit=self.venous_pressure_mid_mmHg -( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW.value] / 2), - venous_max_limit=self.venous_pressure_mid_mmHg + ( self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW.value] / 2), + arterial_min_limt=arterial_min_limit, + arterial_max_limt=arterial_max_limit, + venous_min_limit=venous_min_limit, + venous_max_limit=venous_max_limit, arterial_long_filtered_pressure=self.arterial_pressure_mmHg, venous_long_filtered_pressure=self.venous_pressure_mmHg ) # MSG_ID_RTC_EPOCH @@ -1187,7 +1171,7 @@ rsp_payload = struct.pack("<2f 4i", self.rinseback_volume_set_ml, # 0 (float) the target volume in mL float(self.rinseback_volume_all_ml), # 1 (float) the current volume in mL - int(new_volume_ml * 60), # 2 (uint ) the current flow rate in mL/min + int(round(new_volume_ml * 60)), # 2 (uint ) the current flow rate in mL/min 120, # 3 (uint ) Total Timeout used_time, # 4 (uint ) Current Timeout count down (self.demoSelection == 43) # 5 (int/bool) complete @@ -1213,9 +1197,26 @@ 0, # 8 Stop state 0, # 9 Dialysis state ) + elif self.demoSelection == 60: + rsp_id = MsgIds.MSG_ID_HD_UI_CONFIRMATION_REQUEST + rsp_payload = integer_to_bytearray(6) + rsp_payload += integer_to_bytearray(0) + rsp_payload += integer_to_bytearray(0) + self.demoSelection = 0 # Wait for User + self.demoCount = 0 - elif self.demoSelection == 50: + elif self.demoSelection == 61: rsp_id = MsgIds.MSG_ID_HD_OP_MODE_DATA + rsp_payload = integer_to_bytearray(HDOpModes.MODE_POST.value) + rsp_payload += integer_to_bytearray(PostTreatmentStates.HD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE.value) + self.message_queue_mutex.acquire() + self._send_treatment_log() + self.message_queue_mutex.release() + self.demoSelection = 0 + self.demoCount = 0 + + elif self.demoSelection == 100: + rsp_id = MsgIds.MSG_ID_HD_OP_MODE_DATA rsp_payload = integer_to_bytearray(HDOpModes.MODE_STAN.value) rsp_payload += integer_to_bytearray(HDStandbyStates.STANDBY_WAIT_FOR_DISINFECT_STATE.value) self.demoSelection = 0 # wait for user @@ -1256,10 +1257,46 @@ ctr_id != MsgIds.MSG_ID_HD_RINSEBACK_PROGRESS: ctr_payload = integer_to_bytearray(ctr_count) ctr_payload += integer_to_bytearray(ctr_count - ctr_counter) - else: # MSG_ID_HD_BLOOD_PRIME_PROGRESS has special scaling using floats@ 8mL/sec TBD set as const + else: # MSG_ID_HD_BLOOD_PRIME_PROGRESS has special scaling using floats@ 8mL/sec TBD set as const and send flow/pressure ctr_payload = float_to_bytearray(ctr_count * 8.0) ctr_payload += float_to_bytearray(ctr_counter * 8.0) ctr_payload += float_to_bytearray(0.0) + + if self.demoCountdownId == MsgIds.MSG_ID_HD_BLOOD_PRIME_PROGRESS_DATA: + # Need to update pressure values in Blood priming screen. Handler only steps state in once + self.blood_prime_set_point_blood_flow += 4 + if self.blood_prime_set_point_blood_flow > self.blood_flow_set_point_ml_per_min: + self.blood_prime_set_point_blood_flow = self.blood_flow_set_point_ml_per_min + # MSG_ID_PRESSURE_OCCLUSION_DATA ## TBD! combine with Treatment update data + self.arterial_pressure_mmHg += (random() - 0.5) * 10 # TBD! replace magic wander numbers + delta = self.arterial_pressure_mmHg - self.arterial_pressure_mid_mmHg + if abs(delta) > 30: + self.arterial_pressure_mmHg -= copysign(5, delta) + self.venous_pressure_mmHg += (random() - 0.5) * 10 + delta = self.venous_pressure_mmHg - self.venous_pressure_mid_mmHg + if abs(delta) > 30: + self.venous_pressure_mmHg -= copysign(5, delta) + # Old CAN.py does not have mutexes for Tx and Rx. + # need to manually lock to prevent overrun and CRC issues. + self.message_queue_mutex.acquire() + self.cmd_set_pressure_occlusion_data(arterial_prs=self.arterial_pressure_mmHg, + venous_prs=self.venous_pressure_mmHg, + blood_pump_occlusion=0, + pressure_limit_state=0, + arterial_min_limt= PressureRanges.ARTERIAL_PRESSURE_LIMIT_MIN_MMHG, + arterial_max_limt= PressureRanges.ARTERIAL_PRESSURE_LIMIT_MAX_MMHG, + venous_min_limit= PressureRanges.VENOUS_PRESSURE_LIMIT_MIN_MMHG, + venous_max_limit= PressureRanges.VENOUS_PRESSURE_LIMIT_MAX_MMHG, + arterial_long_filtered_pressure=self.arterial_pressure_mmHg, + venous_long_filtered_pressure=self.venous_pressure_mmHg ) + self.message_queue_mutex.release() + self.message_queue_mutex.acquire() + self.cmd_set_treatment_blood_flow_rate(flow_set_pt= self.blood_prime_set_point_blood_flow, + measured_flow=self.blood_flow_measured_ml_per_min, + rot_speed=0, mot_speed=0, mc_speed=0, + mc_current=0, pwm=0, rotor_count=0, + pres_flow=self.blood_flow_set_point_ml_per_min, rotor_hall=0) + self.message_queue_mutex.release() # where to send message from if self.demoCountdownChannel is None: ctr_channel_id = DenaliChannels.hd_to_ui_ch_id # default @@ -1288,6 +1325,10 @@ @return: None """ tx_time_s = self.treatment_stop_time_s - self.treatment_start_time_s + if self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_HEPARIN_TYPE.value] == -1: + heparin_type = 0 + else: + heparin_type = self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_HEPARIN_TYPE.value] self.cmd_send_post_treatment_log_response( accepted=EResponse.Accepted, # bool reason=RequestRejectReasons.REQUEST_REJECT_REASON_NONE.value, # int, @@ -1335,7 +1376,7 @@ heparin_pre_stop=self.demo_treatment_params.data_int[ TreatmentParameters.TREATMENT_PARAM_HEPARIN_PRESTOP_MIN.value], # int heparin_delivered_volume=self.heparin_out_ml, # float - heparin_type=self.demo_treatment_params.data_int[TreatmentParameters.TREATMENT_PARAM_HEPARIN_TYPE.value], # int + heparin_type=heparin_type, # uint average_arterial_pressure=self.arterial_pressure_mid_mmHg, # float average_venous_pressure=self.venous_pressure_mid_mmHg, # float device_id=101, # int @@ -2878,6 +2919,21 @@ self.can_interface.send(message, 0) + def cmd_send_pre_treatment_patient_connection_begin_response(self, accepted: int, reason: int) -> None: + """ + send the pretreatment patient connection confirm response + @param accepted: (int) accept or reject + @param reason: (int) rejection reason + @return: None + """ + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.hd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_HD_PATIENT_CONNECTION_BEGIN_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) def cmd_send_pre_treatment_patient_connection_confirm_response(self, accepted: int, reason: int) -> None: """ send the pretreatment patient connection confirm response