Index: leahi_dialin/dd/modules/records.py =================================================================== diff -u -ra9a280f1f678c984654c15a827da1bcd4ce0663c -r3cd59165eaecfa53c68486079707e6b3189a36ab --- leahi_dialin/dd/modules/records.py (.../records.py) (revision a9a280f1f678c984654c15a827da1bcd4ce0663c) +++ leahi_dialin/dd/modules/records.py (.../records.py) (revision 3cd59165eaecfa53c68486079707e6b3189a36ab) @@ -16,7 +16,7 @@ # Module imports from logging import Logger -from time import sleep +from functools import partial # Project imports from leahi_dialin.common import dd_enum_repository @@ -88,6 +88,7 @@ self.usage_info_records[usage_record.name] = None +# ================================================= CAN Message Handler Methods ================================================= @publish(["msg_id_dd_nvm_send_system_record", "system_records", "system_records_timestamp"]) def _handler_system_record_sync(self, message, timestamp = 0.0): """ @@ -241,6 +242,7 @@ self.usage_info_records_timestamp = timestamp +# ================================================= Go to Service Mode Method ================================================= def cmd_initiate_service_mode(self) -> int: """ Constructs and sends a request to change to Service operation mode via CAN bus. @@ -265,6 +267,8 @@ can_interface = self.can_interface) + +# ================================================= Request Records Methods ================================================= def cmd_request_system_records(self) -> int: """ Constructs and sends a request for System Records. @@ -375,6 +379,8 @@ can_interface = self.can_interface) + +# ================================================= Set Records Main Methods ================================================= def cmd_set_system_records(self, part_number: str, serial_number: str, @@ -469,81 +475,205 @@ @return: 1 if successful, zero otherwise """ - MAX_MESSAGE_SIZE = 256 - class CalibrationComponents(DialinEnum): - PRES_SENSORS = 0 - TEMP_SENSORS = 1 - - CalibrationComponents._from_str = { - 'PRES_SENSORS': ['pres'], - 'TEMP_SENSORS': ['temp'] + msg_id_pairing = { + 'pressure_sensors': MsgIds.TBD, + 'temperature_sensors': MsgIds.TBD, + 'concentrate_pumps': MsgIds.TBD, + 'dialysate_pumps': MsgIds.TBD, + 'acid': MsgIds.TBD, + 'bicarb': MsgIds.TBD, + 'accelerometer': MsgIds.TBD, + 'blood_leak': MsgIds.TBD, } - # Determine how many pages we will need to send the full data - page_payload_count = [] - payload_packet = [] - current_enum = 0 - max_page = 1 - while current_enum < dd_enum_repository.CalibrationRecordFields.NUM_OF_CALIBRATION_RECORD_FIELDS.value: - remaining_bytes = 256 - (3 * 4) - packet = b'' - for i in range(current_enum, dd_enum_repository.CalibrationRecordFields.NUM_OF_CALIBRATION_RECORD_FIELDS.value - 1): - e = dd_enum_repository.CalibrationRecordFields(i) - enum_split = e.split('__') - if len(enum_split) > 1: - module = CalibrationComponents.from_str(enum_split[0]) - sensor = enum_split[1] - if len(enum_split) == 2: - value = calibration_records[module][sensor] - else: - field = enum_split[2] - value = calibration_records[module][sensor][field] - else: - value = calibration_records[module] - - datatype = dd_enum_repository.CalibrationRecordFields(i).datatype() - if remaining_bytes > datatype.size(): - remaining_bytes -= datatype.size() - if datatype in [ DataTypes.U32, DataTypes.BOOL ]: - packet += integer_to_bytearray(value) - elif datatype in [ DataTypes.F32 ]: - packet += float_to_bytearray(value) - elif datatype in [ DataTypes.U08 ]: - packet += byte_to_bytearray(value) + # Make a dictionary to store all the send functions for later send + send_data = [] + for key in calibration_records: + if key.lower() in ['pressure_sensors', 'temperature_sensors', 'concentrate_pumps', 'dialysate_pumps']: + for sensor in calibration_records[key]: + # Store the function but do not execute it + send_data.append(partial(self.cmd_set_calibration_records_sensor(sensor_enum = sensor, + msg_id = msg_id_pairing[key], + forth_order_coeff = calibration_records[key][sensor]['forth_order_coeff'], + third_order_coeff = calibration_records[key][sensor]['third_order_coeff'], + second_order_coeff = calibration_records[key][sensor]['second_order_coeff'], + gain = calibration_records[key][sensor]['gain'], + offset = calibration_records[key][sensor]['offset'], + calibration_time = calibration_records[key][sensor]['calibration_time']))) + elif key.lower() in ['dialysate_pumps']: + for sensor in calibration_records[key]: + # Store the function but do not execute it + send_data.append(partial(self.cmd_set_calibration_records_sensor(sensor_enum = sensor, + msg_id = msg_id_pairing[key], + dialysate_pump_target_speed = calibration_records[key][sensor]['target_speed'], + forth_order_coeff = calibration_records[key][sensor]['forth_order_coeff'], + third_order_coeff = calibration_records[key][sensor]['third_order_coeff'], + second_order_coeff = calibration_records[key][sensor]['second_order_coeff'], + gain = calibration_records[key][sensor]['gain'], + offset = calibration_records[key][sensor]['offset'], + calibration_time = calibration_records[key][sensor]['calibration_time']))) + elif key.lower() in ['acid', 'bicarb']: + for sensor in calibration_records[key]: + # Store the function but do not execute it + send_data.append(partial(self.cmd_set_calibration_records_concentrate(msg_id = msg_id_pairing[key], + concentrate_mix_ratio = calibration_records[key][sensor]['concentrate_mix_ratio'], + volume_ml = calibration_records[key][sensor]['volume_ml'], + conductivity_uspcm = calibration_records[key][sensor]['conductivity_uspcm'], + temperature_c = calibration_records[key][sensor]['temperature_c'], + calibration_time = calibration_records[key][sensor]['calibration_time']))) + elif key.lower() in ['accelerometer']: + for sensor in calibration_records[key]: + # Store the function but do not execute it + send_data.append(partial(self.cmd_set_calibration_records_accelerometer(msg_id = msg_id_pairing[key], + accel_x_offset = calibration_records[key][sensor]['accel_x_offset'], + accel_y_offset = calibration_records[key][sensor]['accel_y_offset'], + accel_z_offset = calibration_records[key][sensor]['accel_z_offset'], + calibration_time = calibration_records[key][sensor]['calibration_time']))) + elif key.lower() in ['blood_leak']: + for sensor in calibration_records[key]: + # Store the function but do not execute it + send_data.append(partial(self.cmd_set_calibration_records_blood_leak(msg_id = msg_id_pairing[key], + set_point = calibration_records[key][sensor]['set_point'], + calibration_time = calibration_records[key][sensor]['calibration_time']))) + + # Execute the stored functions one by one + # Remove the ones that got 1 (successfully recieved) as response + # Retry the ones that are failed 2 more times + retry = 0 + while send_data != [] and retry < 3: + failed = [] + for func in send_data: + resp = func() + if resp == 1: + failed.append(func) + send_data = failed + retry += 1 - if i == dd_enum_repository.CalibrationRecordFields.NUM_OF_CALIBRATION_RECORD_FIELDS.value - 1: - # page_payload_count.append(i - current_enum + 1) - page_payload_count.append(MAX_MESSAGE_SIZE - remaining_bytes + (3 * 4)) - payload_packet.append(packet) - else: - max_page += 1 - # page_payload_count.append(i - current_enum + 1) - page_payload_count.append(MAX_MESSAGE_SIZE - remaining_bytes + (3 * 4)) - payload_packet.append(packet) - break - # Send the packets - current_page = 1 - while current_page <= max_page: - payload = integer_to_bytearray(current_page) # Current Page - payload += integer_to_bytearray(max_page) # All Page Count - payload += integer_to_bytearray(page_payload_count[current_page - 1]) # Payload count - payload.join(payload_packet[current_page - 1]) - cmd_generic_override( - payload = payload, - reset = None, - channel_id = CanChannels.dialin_to_dd_ch_id, - msg_id = MsgIds.MSG_ID_DD_NVM_SET_CALIBRATION_RECORD, - entity_name = f'New DD Calibration Record', - override_text = 'being set', - logger = self.logger, - can_interface = self.can_interface) - - current_page += 1 - sleep(0.5) +# ================================================= Set Records Support Methods ================================================= + def cmd_set_calibration_records_sensor(self, + sensor_enum: DialinEnum, + msg_id: MsgIds, + dialysate_pump_target_speed: float=0.0, + forth_order_coeff: float=0.0, + third_order_coeff: float=0.0, + second_order_coeff: float=0.0, + gain: float=0.0, + offset: float=0.0, + calibration_time: int = 0) -> int: + payload = integer_to_bytearray(sensor_enum.value) + if msg_id == MsgIds.TBD: + payload += float_to_bytearray(dialysate_pump_target_speed) + payload += float_to_bytearray(forth_order_coeff) + payload += float_to_bytearray(third_order_coeff) + payload += float_to_bytearray(second_order_coeff) + payload += float_to_bytearray(gain) + payload += float_to_bytearray(offset) + payload += integer_to_bytearray(calibration_time) + payload += integer_to_bytearray(crc16_modbus(payload)) + sensor_type = '' + if msg_id == MsgIds.TBD: + sensor_type = 'Pressure Sensor' + elif msg_id == MsgIds.TBD: + sensor_type = 'Temperature Sensor' + elif msg_id == MsgIds.TBD: + sensor_type = 'Concentrate Pump' + elif msg_id == MsgIds.TBD: + sensor_type = 'Dialysate Pump' + return cmd_generic_override(payload = payload, + reset = None, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = msg_id, + entity_name = f'DD {sensor_type} {sensor_enum.name} Calibration Record', + override_text = 'being set', + logger = self.logger, + can_interface = self.can_interface) + + + def cmd_set_calibration_records_concentrate(self, + msg_id: MsgIds, + concentrate_mix_ratio: float=0.0, + volume_ml: float=0.0, + conductivity_uspcm: float=0.0, + temperature_c: float=0.0, + calibration_time: int = 0) -> int: + + payload = float_to_bytearray(concentrate_mix_ratio) + payload += float_to_bytearray(volume_ml) + payload += float_to_bytearray(conductivity_uspcm) + payload += float_to_bytearray(temperature_c) + payload += integer_to_bytearray(calibration_time) + payload += integer_to_bytearray(crc16_modbus(payload)) + + conc_type = '' + if msg_id == MsgIds.TBD: + conc_type = 'Acid' + elif msg_id == MsgIds.TBD: + conc_type = 'Bicarb' + + return cmd_generic_override(payload = payload, + reset = None, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = msg_id, + entity_name = f'DD {conc_type} Concentrate Calibration Record', + override_text = 'being set', + logger = self.logger, + can_interface = self.can_interface) + + + def cmd_set_calibration_records_accelerometer(self, + msg_id: MsgIds, + accel_x_offset: float=0.0, + accel_y_offset: float=0.0, + accel_z_offset: float=0.0, + calibration_time: int = 0) -> int: + + payload = float_to_bytearray(accel_x_offset) + payload += float_to_bytearray(accel_y_offset) + payload += float_to_bytearray(accel_z_offset) + payload += integer_to_bytearray(calibration_time) + payload += integer_to_bytearray(crc16_modbus(payload)) + + conc_type = '' + if msg_id == MsgIds.TBD: + conc_type = 'Acid' + elif msg_id == MsgIds.TBD: + conc_type = 'Bicarb' + + return cmd_generic_override(payload = payload, + reset = None, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = msg_id, + entity_name = f'DD {conc_type} Concentrate Calibration Record', + override_text = 'being set', + logger = self.logger, + can_interface = self.can_interface) + + + + def cmd_set_calibration_records_blood_leak(self, + msg_id: MsgIds, + set_point: float=0.0, + calibration_time: int = 0) -> int: + + payload = float_to_bytearray(set_point) + payload += integer_to_bytearray(calibration_time) + payload += integer_to_bytearray(crc16_modbus(payload)) + + return cmd_generic_override(payload = payload, + reset = None, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = msg_id, + entity_name = f'DD Blood Leak Calibration Record', + override_text = 'being set', + logger = self.logger, + can_interface = self.can_interface) + + + # ================================================= Private Methods ================================================= def crc16_modbus(data: bytes) -> int: """Calculate CRC-16 Modbus (Poly: 0xA001, Init: 0xFFFF, Reflected)."""