Index: leahi_dialin/dd/modules/records.py =================================================================== diff -u -r1f7b5ac840adf6740b023706fcb73691c33a9be7 -r5d919ffa14be669314a6a1c233ea820e7302a44a --- leahi_dialin/dd/modules/records.py (.../records.py) (revision 1f7b5ac840adf6740b023706fcb73691c33a9be7) +++ leahi_dialin/dd/modules/records.py (.../records.py) (revision 5d919ffa14be669314a6a1c233ea820e7302a44a) @@ -16,6 +16,7 @@ # Module imports from logging import Logger +from time import sleep # Project imports from leahi_dialin.common import dd_enum_repository @@ -25,6 +26,7 @@ from leahi_dialin.protocols.CAN import CanMessenger, CanChannels from leahi_dialin.utils.abstract_classes import AbstractSubSystem from leahi_dialin.utils.base import publish +from leahi_dialin.utils.enums import DialinEnum from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray, byte_to_bytearray @@ -115,15 +117,32 @@ for i in range(0, result['payload_count']): sys_record = dd_enum_repository.SystemRecordFields(i + self.pager_system_record) if sys_record.multichar_length > 1: - name = f'{sys_record.name}_1' + for j in range(0, sys_record.multichar_length - 1): + name = f'{sys_record.name}_{j}' + record_list.append((name, sys_record.datatype)) else: name = sys_record.name - record_list.append((name, sys_record.datatype)) - self.process_into_dict(dict_to_update = self.system_records, - decoder_list = record_list, - message = message, - start_from_byte = len(msg_list) * DataTypes.U32.size) + record_list.append((name, sys_record.datatype)) + result_payload = self.process_into_vars(decoder_list = record_list, + message = message, + start_from_byte = len(msg_list) * DataTypes.U32.size) + # Saving data into System Records + self.system_records[dd_enum_repository.SystemRecordFields.TOP_LEVEL_PN.name] = '' + self.system_records[dd_enum_repository.SystemRecordFields.TOP_LEVEL_SN.name] = '' + self.system_records[dd_enum_repository.SystemRecordFields.MFG_LOCATION.name] = 0 + self.system_records[dd_enum_repository.SystemRecordFields.MFG_DATE.name] = 0 + self.system_records[dd_enum_repository.SystemRecordFields.CRC.name] = 0 + for key in result_payload: + if key.startswith(dd_enum_repository.SystemRecordFields.TOP_LEVEL_PN.name): + value = result_payload[key] + self.system_records[dd_enum_repository.SystemRecordFields.TOP_LEVEL_PN.name] += value + elif key.startswith(dd_enum_repository.SystemRecordFields.TOP_LEVEL_SN.name): + value = result_payload[key] + self.system_records[dd_enum_repository.SystemRecordFields.TOP_LEVEL_SN.name] += value + else: + self.system_records[key] = result_payload[key] + # Increase the pager by the payload count to know where the next message should resume, or reset when it's the last message if result['current_page'] == result['all_pages']: self.pager_system_record = 0 @@ -370,20 +389,28 @@ @return: 1 if successful, zero otherwise """ - payload = integer_to_bytearray(1) # Current Page - payload += integer_to_bytearray(1) # All Page Count - payload += integer_to_bytearray(dd_enum_repository.SystemRecordFields.NUM_OF_SYSTEM_RECORD_FIELDS.value) # Payload count + payload_count = 0 + for e in dd_enum_repository.SystemRecordFields: + if e != dd_enum_repository.SystemRecordFields.NUM_OF_SYSTEM_RECORD_FIELDS: + payload_count += e.datatype().size() * e.multichar_length() + payload_base = integer_to_bytearray(1) # Current Page + payload_base += integer_to_bytearray(1) # All Page Count + payload_base += integer_to_bytearray(payload_count) # Payload count in bytes + + payload = b'' for c in part_number: payload += byte_to_bytearray(c) for c in serial_number: payload += byte_to_bytearray(c) payload += integer_to_bytearray(manufacturing_location) payload += integer_to_bytearray(manufacturing_date) - crc = '' + crc = crc16_modbus(payload) payload += integer_to_bytearray(crc) + total_payload = payload_base + payload + return cmd_generic_override( - payload = payload, + payload = total_payload, reset = None, channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_NVM_SET_SYSTEM_RECORD, @@ -393,7 +420,90 @@ can_interface = self.can_interface) + def cmd_set_calibration_records(self, calibration_records: dict) -> int: + """ + Constructs and sends a command for setting the Calibration Records. + Constraints: + Must be logged into DD. + Must be in Service mode. + @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'] + } + + # 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) + + 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) + + # ================================================= Private Methods ================================================= def _generic_handler(self: DDRecords, message, service_name, pager_record): # Get the payload related values @@ -408,7 +518,7 @@ record_list = [] for i in range(0, result['payload_count']): record = service_name(i + pager_record) - record_list.append((record.name, record.datatype)) + record_list.append((record.name, record.datatype)) self.process_into_dict(dict_to_update = self.service_records, decoder_list = record_list, message = message, @@ -418,4 +528,30 @@ if result['current_page'] == result['all_pages']: pager_record = 0 else: - pager_record =+ result['payload_count'] \ No newline at end of file + pager_record =+ result['payload_count'] + + +def crc16_modbus(data: bytes) -> int: + """Calculate CRC-16 Modbus (Poly: 0xA001, Init: 0xFFFF, Reflected).""" + crc = 0xFFFF + for byte in data: + crc ^= byte + for _ in range(8): + if crc & 1: + crc = (crc >> 1) ^ 0xA001 + else: + crc >>= 1 + return crc + + +def crc16_xmodem(data: bytes) -> int: + """Calculate CRC-16 XModem (Poly: 0x1021, Init: 0x0000, Not Reflected).""" + crc = 0x0000 + for byte in data: + crc ^= (byte << 8) + for _ in range(8): + if crc & 0x8000: + crc = ((crc << 1) ^ 0x1021) & 0xFFFF + else: + crc = (crc << 1) & 0xFFFF + return crc