Index: dialin/common/msg_defs.py =================================================================== diff -u -r1e51132e2b4eae6110d673bd4af934235cd76e36 -r43e8345862c72340680c045e53b310aa291513a6 --- dialin/common/msg_defs.py (.../msg_defs.py) (revision 1e51132e2b4eae6110d673bd4af934235cd76e36) +++ dialin/common/msg_defs.py (.../msg_defs.py) (revision 43e8345862c72340680c045e53b310aa291513a6) @@ -216,10 +216,15 @@ MSG_ID_DG_RO_FLOW_RATE_OVERRIDE = 0xA030 # DG RO flow rate override MSG_ID_DG_RO_PUMP_TARGET_FLOW_OVERRIDE = 0xA031 # DG RO pump target flow rate override MSG_ID_DG_RO_PUMP_TARGET_PRESSURE_OVERRIDE = 0xA032 # DG RO pump target pressure override - MSG_ID_DG_SET_CALIBRATION_DATA = 0xA033 # DG set calibration data - MSG_ID_DG_GET_CALIBRATION_DATA = 0xA034 # DG get calibration data - MSG_ID_DG_SEND_CALIBRATION_DATA = 0xA035 # DG send calibration data - MSG_ID_DG_SET_SYSTEM_DATA = 0xA036 # DG send system data + MSG_ID_DG_SET_CALIBRATION_RECORD = 0xA033 # DG set calibration record + MSG_ID_DG_GET_CALIBRATION_RECORD = 0xA034 # DG get calibration record + MSG_ID_DG_SEND_CALIBRATION_RECORD = 0xA035 # DG send calibration record from firmware + MSG_ID_DG_SET_SYSTEM_RECORD = 0xA036 # DG send system record + MSG_ID_DG_GET_SYSTEM_RECORD = 0xA037 # DG get system record + MSG_ID_DG_SEND_SYSTEM_RECORD = 0xA038 # DG send system record from firmware + MSG_ID_DG_SET_SERVICE_RECORD = 0xA03A # DG set service record that is sent from Dialin + MSG_ID_DG_GET_SERVICE_RECORD = 0xA039 # DG get service record that is requested from Dialin + MSG_ID_DG_SEND_SERVICE_RECORD = 0xA03B # DG send service record that is requested from Dialin 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/calibration.py =================================================================== diff -u -r1e51132e2b4eae6110d673bd4af934235cd76e36 -r43e8345862c72340680c045e53b310aa291513a6 --- dialin/dg/calibration.py (.../calibration.py) (revision 1e51132e2b4eae6110d673bd4af934235cd76e36) +++ dialin/dg/calibration.py (.../calibration.py) (revision 43e8345862c72340680c045e53b310aa291513a6) @@ -69,13 +69,13 @@ if self.can_interface is not None: channel_id = DenaliChannels.dg_to_dialin_ch_id - msg_id = MsgIds.MSG_ID_DG_SEND_CALIBRATION_DATA.value + msg_id = MsgIds.MSG_ID_DG_SEND_CALIBRATION_RECORD.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_dg_calibration_sync) # Prepare the calibration record by putting sub-dictionaries together self._prepare_dg_calibration_record() - def get_dg_calibration_data(self): + def get_dg_calibration_record(self): """ Handles getting DG calibration data from firmware. @@ -84,16 +84,16 @@ # Clear the list for the next call self.raw_cal_record.clear() # Run the firmware commands to get the calibration record - self._request_dg_fw_calibration_data() + self._request_dg_fw_calibration_record() - def _request_dg_fw_calibration_data(self): + def _request_dg_fw_calibration_record(self): """ Handles getting DG calibration record from firmware. @return: None """ message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=MsgIds.MSG_ID_DG_GET_CALIBRATION_DATA.value) + message_id=MsgIds.MSG_ID_DG_GET_CALIBRATION_RECORD.value) self.logger.debug('Getting DG calibration record') @@ -709,7 +709,7 @@ print(len(payload), payload) # TODO remove message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=MsgIds.MSG_ID_DG_SET_CALIBRATION_DATA.value, + message_id=MsgIds.MSG_ID_DG_SET_CALIBRATION_RECORD.value, payload=payload) received_message = self.can_interface.send(message) Index: dialin/dg/dialysate_generator.py =================================================================== diff -u -r1e51132e2b4eae6110d673bd4af934235cd76e36 -r43e8345862c72340680c045e53b310aa291513a6 --- dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 1e51132e2b4eae6110d673bd4af934235cd76e36) +++ dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 43e8345862c72340680c045e53b310aa291513a6) @@ -32,7 +32,8 @@ from .uv_reactors import UVReactors from .concentrate_pumps import ConcentratePumps from .calibration import DGCalibration -from .system_record import DGSystem +from .system_record import DGSystemRecord +from .service_record import DGServicRecord from ..utils.conversions import integer_to_bytearray from ..protocols.CAN import (DenaliCanMessenger, DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish, _LogManager @@ -151,7 +152,8 @@ self.fans = Fans(self.can_interface, self.logger) self.uv_reactors = UVReactors(self.can_interface, self.logger) self.calibration = DGCalibration(self.can_interface, self.logger) - self.system_record = DGSystem(self.can_interface, self.logger) + self.system_record = DGSystemRecord(self.can_interface, self.logger) + self.service_record = DGServicRecord(self.can_interface, self.logger) def get_version(self): """ Index: dialin/dg/service_record.py =================================================================== diff -u --- dialin/dg/service_record.py (revision 0) +++ dialin/dg/service_record.py (revision 43e8345862c72340680c045e53b310aa291513a6) @@ -0,0 +1,406 @@ +import struct +import datetime +import time +import math +from collections import OrderedDict +from ..ui.crc import crc_16 +from ..common.msg_defs import MsgIds, MsgFieldPositions +from ..protocols.CAN import (DenaliMessage, DenaliChannels) +from ..utils.base import _AbstractSubSystem, DialinEnum +from logging import Logger +from enum import unique + + +@unique +class ServiceLocation(DialinEnum): + SERVICE_LOCATION_FACTORY = 0 + SERVICE_LOCATION_FIELD = 1 + + +class DGServicRecord(_AbstractSubSystem): + """ + + Dialysate Generator (DG) Dialin API sub-class for service record commands. + """ + + SERVICE_RECORD_START_INDEX = 6 + SERVICE_RECORD_SPECS_BYTES = 12 + SERVICE_RECORDS_SPECS_BYTE_ARRAY = 3 + + DEFAULT_SERVICE_LOCATION = ServiceLocation.SERVICE_LOCATION_FACTORY.value + DEFAULT_MFG_DATE = 0 + DEFAULT_SERVICE_DATE_VALUE = 0 + DEFAULT_SERVICE_CRC_VALUE = 0 + DEFAULT_SYS_PADDING_VALUE = 0 + + CURRENT_MESSAGE_NUM_INDEX = 0 + TOTAL_MESSAGES_NUM_INDEX = 4 + PAYLOAD_LENGTH_INDEX = 8 + PAYLOAD_START_INDEX = 12 + + SERVICE_DATA_TYPE_INDEX = 0 + SERVICE_VALUE_INDEX = 1 + + TARGET_BYTES_TO_SEND_TO_FW = 150 + MIN_PAYLOAD_BYTES_SPACE = 4 + + RTC_RAM_MAX_BYTES_TO_WRITE = 64 + + PAYLOAD_CURRENT_MSG_INDEX = 0 + PAYLOAD_TOTAL_MSG_INDEX = 1 + PAYLOAD_SERVICE_BYTES_INDEX = 2 + + # DG system main record as an ordered dictionary + DG_SERVICE_RECORD = OrderedDict() + + def __init__(self, can_interface, logger: Logger): + """ + + @param can_interface: Denali CAN Messenger object + """ + + super().__init__() + + self.can_interface = can_interface + self.logger = logger + self.current_message = 0 + self.total_messages = 0 + self.length = 0 + self.cal_data = 0 + self.raw_service_record = [] + + if self.can_interface is not None: + + channel_id = DenaliChannels.dg_to_dialin_ch_id + msg_id = MsgIds.MSG_ID_DG_SEND_SERVICE_RECORD.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_dg_service_record) + # Prepare the service record by putting sub-dictionaries together + self._prepare_dg_service_record() + + def get_dg_service_record(self): + """ + Handles getting DG service record from firmware. + + @return: None + """ + # Clear the list for the next call + self.raw_service_record.clear() + # Run the firmware commands to get the service record + self._request_dg_fw_service_record() + + def _request_dg_fw_service_record(self): + """ + Handles getting DG service record from firmware. + + @return: None + """ + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_GET_SERVICE_RECORD.value) + + self.logger.debug('Getting DG service record') + + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + def _handler_dg_service_record(self, message): + """ + Handles published DG service record messages. DG system records are captured for processing and + updating the DG service record. + + @param message: published DG system record data message + + @return: None + """ + curr = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + total = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] + length = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] + + self.current_message = curr + self.total_messages = total + self.length = length + # The end of system record payload is from the start index + 12 bytes for the current message + total + # messages + the length of system record. The rest is the CAN messaging CRC that is not needed to be kept. + end_of_data_index = self.SERVICE_RECORD_START_INDEX + self.SERVICE_RECORD_SPECS_BYTES + self.length + + # Get the calibration data only + self.cal_data = message['message'][self.SERVICE_RECORD_START_INDEX:end_of_data_index] + + # Continue getting calibration records until the all the calibration messages are received. Concatenate the + # calibration records to each other + if self.current_message <= self.total_messages: + self.raw_service_record += (message['message'][self.SERVICE_RECORD_START_INDEX + + self.SERVICE_RECORD_SPECS_BYTES:end_of_data_index]) + if self.current_message == self.total_messages: + # If all the messages have been received, call another function to process the raw data + self._update_dg_service_record_from_fw() + + def _update_dg_service_record_from_fw(self): + """ + Handles parsing the system messages that were received from DG firmware. + + @return: None + """ + raw_payload_temp_start_index = 0 + # Convert the concatenated raw data into a byte array since the struct library requires byte arrays. + self.raw_service_record = bytearray(self.raw_service_record) + + # Loop through the keys for the main calibration dictionary + # DG_Calibration : {pressure_sensors : { ppi : { gain: [' self.MIN_PAYLOAD_BYTES_SPACE: + current_payload_length += data_type_bytes + temp_buffer[self.PAYLOAD_TOTAL_MSG_INDEX] = struct.pack(' self.MIN_PAYLOAD_BYTES_SPACE: + current_payload_length += data_type_bytes + # Insert a 4-byte 0 to the index of the total messages. This is a place holder and it will + # be updated with the right value later. + temp_buffer[self.PAYLOAD_TOTAL_MSG_INDEX] = struct.pack('