Index: dialin/common/msg_ids.py =================================================================== diff -u -r9d116c25f6482d61d324558b8a1f745468030740 -r8cc6f4843c77fc7bfe358c9f752740919c25f65f --- dialin/common/msg_ids.py (.../msg_ids.py) (revision 9d116c25f6482d61d324558b8a1f745468030740) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision 8cc6f4843c77fc7bfe358c9f752740919c25f65f) @@ -140,6 +140,28 @@ MSG_ID_HD_ALARM_INFORMATION = 0X7D MSG_ID_HD_STANDBY_STATE = 0X7E MSG_ID_UI_DISINFECT_REQUEST = 0X7F + MSG_ID_DG_VOLTAGES_DATA = 0X86 + MSG_ID_DG_CHEM_DISINFECT_DATA = 0X87 + MSG_ID_DG_SERIAL_NUMBER = 0x88 + MSG_ID_UI_REQUEST_SYSTEM_USAGE_INFO = 0x89 + MSG_ID_HD_SERVICE_SCHEDULE_DATA = 0x8A + MSG_ID_HD_USAGE_DATA = 0x8B + MSG_ID_DG_SERVICE_SCHEDULE_DATA = 0x8C + MSG_ID_DG_USAGE_DATA = 0x8D + MSG_ID_HD_POST_SINGLE_TEST_RESULT = 0x8E + MSG_ID_HD_POST_FINAL_TEST_RESULT = 0x8F + MSG_ID_DG_POST_SINGLE_TEST_RESULT = 0x90 + MSG_ID_DG_POST_FINAL_TEST_RESULT = 0x91 + MSG_ID_UI_POST_FINAL_TEST_RESULT = 0x92 + MSG_ID_HD_BUBBLES_DATA = 0x93 + MSG_ID_HD_TREATMENT_LOG_PERIODIC_DATA = 0x94 + MSG_ID_HD_TREATMENT_LOG_ALARM_EVENT = 0x95 + MSG_ID_HD_TREATMENT_LOG_EVENT = 0x96 + MSG_ID_UI_ACTIVE_ALARMS_LIST_REQUEST = 0x97 + MSG_ID_HD_ACTIVE_ALARMS_LIST_REQUEST_RESPONSE = 0x98 + MSG_ID_HD_SERIAL_NUMBER = 0x99, + MSG_ID_DG_CHEM_DISINFECT_TO_UI_DATA_PUBLISH = 0x9A + MSG_ID_DG_HEAT_DISINFECT_TO_UI_DATA_PUBLISH = 0x9B MSG_ID_DIALYSATE_FLOW_DATA = 0X8 MSG_ID_HD_DISINFECT_RESPONSE = 0X80 MSG_ID_TESTER_LOGIN_REQUEST = 0X8000 @@ -229,6 +251,7 @@ MSG_ID_HD_ALARM_AUDIO_CURRENT_LG_OVERRIDE = 0X8055 MSG_ID_HD_ALARM_BACKUP_AUDIO_CURRENT_OVERRIDE = 0X8056 MSG_ID_HD_VALVES_CURRENT_OVERRIDE = 0X8057 + MSD_ID_HD_VALVES_POSITION_COUNT_OVERRIDE = 0x8058 MSG_ID_HD_SYRINGE_PUMP_STATUS_OVERRIDE = 0X8059 MSG_ID_HD_SYRINGE_PUMP_ENCODER_STATUS_OVERRIDE = 0X805A MSG_ID_HD_SYRINGE_PUMP_ADC_DAC_STATUS_OVERRIDE = 0X805B @@ -241,27 +264,7 @@ MSG_ID_DG_FLUSH_TIME_DATA = 0X83 MSG_ID_DG_HEAT_DISINFECT_TIME_DATA = 0X84 MSG_ID_DG_CHEM_DISINFECT_TIME_DATA = 0X85 - MSG_ID_DG_VOLTAGES_DATA = 0X86 - MSG_ID_DG_CHEM_DISINFECT_DATA = 0X87 - MSG_ID_DG_SERIAL_NUMBER = 0X88 - MSG_ID_UI_REQUEST_SYSTEM_USAGE_INFO = 0X89 - MSG_ID_HD_SERVICE_SCHEDULE_DATA = 0X8A - MSG_ID_HD_USAGE_DATA = 0X8B - MSG_ID_DG_SERVICE_SCHEDULE_DATA = 0X8C - MSG_ID_DG_USAGE_DATA = 0X8D - MSG_ID_HD_POST_SINGLE_TEST_RESULT = 0X8E - MSG_ID_HD_POST_FINAL_TEST_RESULT = 0X8F MSG_ID_PRESSURE_OCCLUSION_DATA = 0X9 - MSG_ID_DG_POST_SINGLE_TEST_RESULT = 0X90 - MSG_ID_DG_POST_FINAL_TEST_RESULT = 0X91 - MSG_ID_UI_POST_FINAL_TEST_RESULT = 0X92 - MSG_ID_HD_BUBBLES_DATA = 0X93 - MSG_ID_HD_TREATMENT_LOG_PERIODIC_DATA = 0X94 - MSG_ID_HD_TREATMENT_LOG_ALARM_EVENT = 0X95 - MSG_ID_HD_TREATMENT_LOG_EVENT = 0X96 - MSG_ID_UI_ACTIVE_ALARMS_LIST_REQUEST = 0X97 - MSG_ID_HD_ACTIVE_ALARMS_LIST_REQUEST_RESPONSE = 0X98 - MSG_ID_HD_SERIAL_NUMBER = 0X99 MSG_ID_CAN_ERROR_COUNT = 0X999 MSG_ID_RTC_EPOCH = 0XA MSG_ID_DG_TESTER_LOGIN_REQUEST = 0XA000 Index: dialin/dg/calibration_record.py =================================================================== diff -u -rdd42e4d9cfe821b0a755ccc86cc1a4a2a3dd2f37 -r8cc6f4843c77fc7bfe358c9f752740919c25f65f --- dialin/dg/calibration_record.py (.../calibration_record.py) (revision dd42e4d9cfe821b0a755ccc86cc1a4a2a3dd2f37) +++ dialin/dg/calibration_record.py (.../calibration_record.py) (revision 8cc6f4843c77fc7bfe358c9f752740919c25f65f) @@ -47,72 +47,48 @@ self.logger = logger self.current_message = 0 self.total_messages = 0 - self.received_msg_length = 0 + self._is_getting_cal_in_progress = False self.cal_data = 0 self._raw_cal_record = [] - self._write_fw_data_to_excel = True - self._is_getting_cal_in_progress = False - self._utilities = NVOpsUtils() - # DG calibration_record main record - self.dg_calibration_record = OrderedDict() + self._utilities = NVOpsUtils(logger=self.logger) + # DG Calibration_record main record + self.dg_calibration_record = self._prepare_dg_calibration_record() if self.can_interface is not None: channel_id = DenaliChannels.dg_to_dialin_ch_id 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 record by putting sub-dictionaries together - self._prepare_dg_calibration_record() - # Prepare the excel report and workspace - self._utilities.prepare_excel_report('DG', 'Calibration') - - def is_reading_record_done(self): + def cmd_request_dg_calibration_record(self): """ - Handles getting the status of reading record + Handles getting DG calibration_record record from firmware. - @return: True if reading is done otherwise False + @return: True if successful, False otherwise """ - return self._utilities.get_writing_to_excel_status() - def get_dg_calibration_record(self): - """ - Handles getting DG calibration data from firmware. + self.logger.debug("Requesting a dg calibration record...") - @return: None - """ - - # If getting the calibration is in progress, do not start another process - if self._is_getting_cal_in_progress is not True: + if not self._is_getting_cal_in_progress: self._is_getting_cal_in_progress = True - # Clear the list for the next call self._raw_cal_record.clear() - # Run the firmware commands to get the calibration_record record - self._request_dg_fw_calibration_record() - def _request_dg_fw_calibration_record(self): - """ - Handles getting DG calibration_record record from firmware. + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_GET_CALIBRATION_RECORD.value) - @return: None - """ - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, - message_id=MsgIds.MSG_ID_DG_GET_CALIBRATION_RECORD.value) + received_message = self.can_interface.send(message, time_out=5) - self.logger.debug('Getting DG calibration record') + # 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] - 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 - @_publish(["current_message", "total_messages", "received_msg_length", "service_data"]) + self.logger.warning("Request cancelled: an existing request is in progress.") + return False + def _handler_dg_calibration_sync(self, message): """ Handles published DG calibration_record record messages. DG calibration records are captured for @@ -122,6 +98,7 @@ @return: None """ + self.logger.debug("DG calibration sync handler...") curr = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] total = struct.unpack('i', bytearray( @@ -131,11 +108,10 @@ self.current_message = curr self.total_messages = total - self.received_msg_length = length # The end of calibration_record record payload is from the start index + 12 bytes for the current message +total # messages + the length of calibration_record. The rest is the CAN messaging CRC that is not needed # to be kept - end_of_data_index = self._RECORD_START_INDEX + self._RECORD_SPECS_BYTES + self.received_msg_length + end_of_data_index = self._RECORD_START_INDEX + self._RECORD_SPECS_BYTES + length # Get the calibration_record data only self.cal_data = message['message'][self._RECORD_START_INDEX:end_of_data_index] @@ -145,36 +121,37 @@ if self.current_message <= self.total_messages: self._raw_cal_record += (message['message'][self._RECORD_START_INDEX + self._RECORD_SPECS_BYTES:end_of_data_index]) + if self.current_message == self.total_messages: # Done with getting all the calibration data. self._is_getting_cal_in_progress = False # If all the messages have been received, call another function to process the raw data - self._utilities.process_received_record_from_fw(self.dg_calibration_record, self._raw_cal_record, - write_to_excel=self._write_fw_data_to_excel) + self._utilities.process_received_record_from_fw(self.dg_calibration_record, self._raw_cal_record) + self._handler_received_complete_dg_calibration_record() + # TODO remove + print(self.dg_calibration_record) + # TODO remove - def set_dg_calibration_record(self): + @_publish(["dg_calibration_record"]) + def _handler_received_complete_dg_calibration_record(self): """ + Publishes the received calibration record + + @return: None + """ + self.logger.debug("Received a complete dg calibration record.") + + def cmd_set_dg_calibration_record(self, dg_calibration_record: OrderedDict): + """ Handles updating the DG calibration record with the newest calibration_record data of a hardware and sends it to FW. - @return: none + @param dg_calibration_record: (OrderedDict) the dg calibration record to be sent + @return: True upon success, False otherwise """ - # Read the data from firmware but do not update the excel document - # At this step, another read from firmware is requested internally to update the dictionary. - # The values in the dictionary is compared against the excel report and if they are different, it automatically - # sets the calibration time and calculates the CRC for that group. By default once a read from firmware is - # requested, the excel report is updated automatically. - self._write_fw_data_to_excel = False - self.get_dg_calibration_record() - # Wait until reading calibration record from firmware is updated - while self._utilities.get_reading_record_status() is not True: - time.sleep(self._DIALIN_RECORD_UPDATE_DELAY_S) - # Write the excel record - self._utilities.write_excel_record_to_dialin_record(self.dg_calibration_record) + record_packets = self._utilities.prepare_record_to_send_to_fw(dg_calibration_record) - record_packets = self._utilities.prepare_record_to_send_to_fw(self.dg_calibration_record) - self.logger.debug('Setting DG calibration started') # Update all the data packets with the last message count since is the number of messages that firmware @@ -197,28 +174,33 @@ self.logger.debug("Timeout!!!!") return False + self.logger.debug("Finished sending DG calibration record.") + return True + def _prepare_dg_calibration_record(self): """ - Handles assembling the sub dictionaries of each hardware group to make the main DG calibration record. + Handles assembling the sub dictionaries of each hardware group to make a DG calibration record. - @return: None + @return: (OrderedDict) the assembled record """ + result = OrderedDict() + groups_byte_size = 0 # Call the other functions to get the dictionaries of each hardware group. All the dictionaries are # ordered dictionaries to maintain the order in which they are inserted. The results are a tuple, the first # element is the dictionary that was built and the second element is the byte size of the dictionary. - functions = [self._prepare_pressure_sensors_cal_record(), self._prepare_flow_sensors_cal_record(), + records_with_sizes = [self._prepare_pressure_sensors_cal_record(), self._prepare_flow_sensors_cal_record(), self._prepare_load_cells_record(), self._prepare_temperature_sensors_record(), self._prepare_conductivity_sensors_record(), self._prepare_pumps_record(), self._prepare_volume_record(), self._prepare_acid_concentrates_record(), self._prepare_bicarb_concentrates_record(), self._prepare_filters_record(), self._prepare_fans_record(), self._prepare_accelerometer_sensor_record()] - for function in functions: + for record, byte_size in records_with_sizes: # Update the groups bytes size so far to be use to padding later - groups_byte_size += function[1] + groups_byte_size += byte_size # Update the calibration record - self.dg_calibration_record.update(function[0]) + result.update(record) # Build the CRC of the main calibration_record record record_crc = OrderedDict({'crc': [' int: @@ -306,8 +306,8 @@ 7 = Valve Drain \n 8 = Valve Pressure Inlet \n 9 = Valve Sampling Port \n - 10 = Valve Reservoir 1 (spare for now including DG FPGA, as valve is of passive air relief type) \n - 11 = Valve Reservoir 2 (spare for now including DG FPGA, as valve is of passive air relief type) \n + 10 = Valve Reservoir 1 Drain \n + 11 = Valve Reservoir 2 Drain \n 12 = Valve Production Drain \n """ Index: dialin/hd/valves.py =================================================================== diff -u -r2e392c92d55178f457a67423ba8c503a86dcf3c8 -r8cc6f4843c77fc7bfe358c9f752740919c25f65f --- dialin/hd/valves.py (.../valves.py) (revision 2e392c92d55178f457a67423ba8c503a86dcf3c8) +++ dialin/hd/valves.py (.../valves.py) (revision 8cc6f4843c77fc7bfe358c9f752740919c25f65f) @@ -16,7 +16,7 @@ import struct -from ..utils.conversions import integer_to_bytearray +from ..utils.conversions import integer_to_bytearray, float_to_bytearray from .constants import NO_RESET from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish, DialinEnum @@ -35,7 +35,6 @@ @unique class ValvesPositions(DialinEnum): - VALVE_POSITION_NOT_IN_POSITION = 0 VALVE_POSITION_A_INSERT_EJECT = 1 VALVE_POSITION_B_OPEN = 2 VALVE_POSITION_C_CLOSE = 3 @@ -200,6 +199,76 @@ self.logger.debug("HD cmd_valve_override Timeout!!!") return False + def cmd_set_hd_valve_current_override(self, valve:int, current:float, reset:int=NO_RESET) -> int: + """ + Constructs and sends the HD valves set position for a valve + + @param valve: integer - Valve number: + VDI = 0 + VDO = 1 + VBA = 2 + VBV = 3 + @param current: float value to override current + @param reset: integer - 1 to reset a previous override, 0 to override + @returns 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + vlv = integer_to_bytearray(valve) + cur = float_to_bytearray(current) + payload = reset_value + cur + vlv + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_VALVES_CURRENT_OVERRIDE.value, + payload=payload) + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + + self.logger.debug("Setting {} current to {:5.3f} A".format(str(ValvesEnum(valve).name), current)) + + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("HD current override Timeout!!!") + return False + + def cmd_set_hd_valve_position_count_override(self, valve:int, position_count:int, reset:int=NO_RESET) -> int: + """ + Constructs and sends the HD valves set position for a valve + + @param valve: integer - Valve number: + VDI = 0 + VDO = 1 + VBA = 2 + VBV = 3 + @param position_count: integer value + @param reset: integer - 1 to reset a previous override, 0 to override + @returns 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + vlv = integer_to_bytearray(valve) + pos = integer_to_bytearray(position_count) + payload = reset_value + pos + vlv + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSD_ID_HD_VALVES_POSITION_COUNT_OVERRIDE.value, + payload=payload) + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + + self.logger.debug("Setting {} position to {} ".format(str(ValvesEnum(valve).name), position_count)) + + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("HD current override Timeout!!!") + return False + def cmd_set_hd_valve_pwm(self, valve: int, pwm: int, direction: int, reset: int = NO_RESET) -> int: """ Constructs and sends the HD valves PWM command @@ -271,7 +340,7 @@ @returns 1 if successful, zero otherwise """ - if valve_state == AirTrapState.STATE_OPEN: + if valve_state == AirTrapState.STATE_OPEN.value: payload = integer_to_bytearray(1) else: payload = integer_to_bytearray(0) @@ -292,7 +361,7 @@ return False @_publish(["valves_status"]) - def _handler_hd_valves_sync(self, message): + def _handler_hd_valves_sync(self, message:dict) -> None: """ Handles published HD valves data messages. HD valves data are captured for reference. @@ -330,7 +399,5 @@ self.valves_status[vlv_name] = {'Valve': vlv_name, 'PosID': ValvesPositions(pos_ID).name, 'PosCnt': pos_cnt, 'Cmd': next_pos, 'State': ValvesStates(state_ID).name, 'Current': current, 'PosA': pos_a, 'PosB': pos_b, 'PosC': pos_c, 'PWM': pwm} - - if AirTrapState.has_value(air_trap): - self.hd_air_trap_status = AirTrapState(air_trap).name - + # Update the air trap valve's status + self.hd_air_trap_status = air_trap Index: dialin/utils/nv_ops_utils.py =================================================================== diff -u -r1c507eb2cac4a66d8d0fd567b8eed90374c1d809 -r8cc6f4843c77fc7bfe358c9f752740919c25f65f --- dialin/utils/nv_ops_utils.py (.../nv_ops_utils.py) (revision 1c507eb2cac4a66d8d0fd567b8eed90374c1d809) +++ dialin/utils/nv_ops_utils.py (.../nv_ops_utils.py) (revision 8cc6f4843c77fc7bfe358c9f752740919c25f65f) @@ -1,18 +1,17 @@ - import struct -import datetime import time -import math -import os -import shutil -from .excel_ops import ExcelOps +from logging import Logger +from typing import List +from collections import OrderedDict +from .excel_ops import * + + class NVOpsUtils: """ - Dialysate Generator (DG) Dialin API sub-class for _utilities commands. The commands are used to process - the calibration_record records, service records, system records, and the scheduled runs records. The records are - prepared to be sent to firmware to be received from firmware. + Processes the calibration_record records, service records, system records, and the scheduled runs records. + The records are prepared to be sent to firmware or to be received from firmware. """ CRC_16_TABLE = ( 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, @@ -67,16 +66,21 @@ _PAYLOAD_TOTAL_MSG_INDEX = 1 _PAYLOAD_TOTAL_BYTES_INDEX = 2 - def __init__(self): + def __init__(self, logger: Logger): + """ + Constructor for the NVOptsUtils class + @param logger: (Logger) the logger + """ + + self.logger = logger self._workspace_dir = '' self._excel_workbook = '' self._record_name = '' self._firmware_stack = '' self._is_writing_to_excel_done = False self._is_read_done = False - self.excel_ops = ExcelOps() # This list contains different data packet lists # i.e. [[message 1, payload], [message 2, payload], ...] self._record_packets_to_send_to_fw = [] @@ -85,22 +89,25 @@ # of the group. self._temp_groups_data_to_calculate_crc = [] - def prepare_excel_report(self, firmware_stack, record_name): + def prepare_excel_report(self, firmware_stack: str, record_name: str, directory: str): """ Publicly accessible function to prepare the excel report - @param firmware_stack: firmware stack name such as HD or DG - @param record_name: record type to check such as calibration, system, ... - + @param firmware_stack: (str) firmware stack name (e.g. "HD" or "DG") + @param record_name: (str) record type to check such as calibration, system, ... + @param directory: (str) the directory in which to write the excel doc @return none """ path = '' is_report_found = False - # Create the workspace name - workspace_name = str(firmware_stack) + '_NV_Records' - self.create_workspace(workspace_name) + if not os.path.isdir(directory): + # Create the directory and go to it + os.mkdir(directory) + + self._workspace_dir = directory + self._record_name = record_name self._firmware_stack = firmware_stack @@ -119,306 +126,312 @@ if is_report_found: # Load the excel workbook - self._excel_workbook = self.excel_ops.load_excel_report(path) + self._excel_workbook = load_excel_report(path) else: # Get an excel workbook object - self._excel_workbook = self.excel_ops.get_an_excel_workbook() + self._excel_workbook = get_an_excel_workbook() # Setup worksheet and create the current tab - self.excel_ops.setup_excel_worksheet(self._excel_workbook, self._record_name) + setup_excel_worksheet(self._excel_workbook, self._record_name) - def write_fw_record_to_excel(self, dialin_record): + def write_fw_record_to_excel(self, calibration_record: dict): + """ + Writes a calibration record to excel + @param calibration_record: (dict) the record to write to excel + @return: None + """ - row = 1 - for group in dialin_record.keys(): + try: - start_row = row - start_col = 1 - col = 1 - if isinstance(dialin_record[group], dict): + row = 1 + for group in calibration_record.keys(): - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, group, bold=True, - max_col_len=len(group)) + start_row = row + start_col = 1 + col = 1 + if isinstance(calibration_record[group], dict): - for hardware in dialin_record[group].keys(): - list_of_keys = list(dialin_record[group].keys()) + write_to_excel(self._excel_workbook, self._record_name, row, col, group, bold=True, + max_col_len=len(group)) - col = 2 - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, hardware) - col += 1 + for hardware in calibration_record[group].keys(): + list_of_keys = list(calibration_record[group].keys()) - if isinstance(dialin_record[group][hardware], dict): - for spec in dialin_record[group][hardware]: + col = 2 + write_to_excel(self._excel_workbook, self._record_name, row, col, hardware) + col += 1 - spec_value = dialin_record[group][hardware][spec][1] + if isinstance(calibration_record[group][hardware], dict): + for spec in calibration_record[group][hardware]: - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, spec) - col += 1 - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, spec_value) - col += 1 - else: - spec_value = dialin_record[group][hardware][1] - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, spec_value) + spec_value = calibration_record[group][hardware][spec][1] - if list_of_keys.index(hardware) == len(list_of_keys) - 1: - self.excel_ops.merge_cells(self._excel_workbook, self._record_name, start_row, start_col, - row, start_col) - row += 1 + write_to_excel(self._excel_workbook, self._record_name, row, col, spec) + col += 1 + write_to_excel(self._excel_workbook, self._record_name, row, col, spec_value) + col += 1 + else: + spec_value = calibration_record[group][hardware][1] + write_to_excel(self._excel_workbook, self._record_name, row, col, spec_value) - row += 1 + if list_of_keys.index(hardware) == len(list_of_keys) - 1: + merge_cells(self._excel_workbook, self._record_name, start_row, start_col, + row, start_col) + row += 1 - self.excel_ops.save_report(self._excel_workbook, self._workspace_dir, self._firmware_stack) - # Signal reading is done - self._is_writing_to_excel_done = True + row += 1 - def write_excel_record_to_dialin_record(self, dialin_record): - """ - Publicly accessible function to write excel record to Dialin record - @param dialin_record: Dialin record dictionary(i.e calibration record) + save_report(self._excel_workbook, self._workspace_dir, self._firmware_stack) + # Signal reading is done + self._is_writing_to_excel_done = True + except Exception as e: + self.logger.error("Failed to write calibration record to excel: {0}".format(e)) + def write_excel_record_to_calibration_record(self, calibration_record: dict): + """ + Writes an excel record to a calibration record + + @param calibration_record: (dict) the calibration record to write @return True if reading to firmware records to excel is done otherwise False """ - temp_buffer = [] - is_value_different = False - row = 1 - for group in dialin_record.keys(): + try: + temp_buffer = [] + is_value_different = False + row = 1 + for group in calibration_record.keys(): - col = 1 - cell_value = self.excel_ops.get_cell_value(self._excel_workbook, self._record_name, row, col) + col = 1 + cell_value = get_cell_value(self._excel_workbook, self._record_name, row, col) - if cell_value == group: + if cell_value == group: - if isinstance(dialin_record[group], dict): - for hardware in dialin_record[group].keys(): - if isinstance(dialin_record[group][hardware], dict): - col = 4 - for spec in dialin_record[group][hardware]: - cell_value = self.excel_ops.get_cell_value(self._excel_workbook, self._record_name, row, - col) - # Check if the cell value is not none. If it is none, it cannot be converted to a number - # like float or integer - is_cell_value_valid = True if cell_value is not None else False - if 'crc' not in spec and 'time' not in spec and is_cell_value_valid: - temp_buffer.append(struct.pack(dialin_record[group][hardware][spec][0], cell_value)) - if dialin_record[group][hardware][spec][1] != cell_value: - is_value_different = True - dialin_record[group][hardware][spec][1] = cell_value + if isinstance(calibration_record[group], dict): + for hardware in calibration_record[group].keys(): + if isinstance(calibration_record[group][hardware], dict): + col = 4 + for spec in calibration_record[group][hardware]: + cell_value = get_cell_value(self._excel_workbook, self._record_name, row, + col) + # Check if the cell value is not none. If it is none, it cannot be converted to a number + # like float or integer + is_cell_value_valid = True if cell_value is not None else False + if 'crc' not in spec and 'time' not in spec and is_cell_value_valid: + temp_buffer.append(struct.pack(calibration_record[group][hardware][spec][0], cell_value)) + if calibration_record[group][hardware][spec][1] != cell_value: + is_value_different = True + calibration_record[group][hardware][spec][1] = cell_value - if is_value_different and 'time' in spec: - epoch_time = self.get_current_time_in_epoch() - dialin_record[group][hardware][spec][1] = epoch_time - temp_buffer.append(struct.pack(dialin_record[group][hardware][spec][0], epoch_time)) - date_time = self.get_current_date_time(epoch_time) - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, - date_time) - elif 'time' in spec and isinstance(cell_value, str) and is_cell_value_valid: - epoch_time = self.get_date_time_in_epoch(cell_value) - dialin_record[group][hardware][spec][1] = epoch_time + if is_value_different and 'time' in spec: + epoch_time = self.get_current_time_in_epoch() + calibration_record[group][hardware][spec][1] = epoch_time + temp_buffer.append(struct.pack(calibration_record[group][hardware][spec][0], epoch_time)) + date_time = self.get_current_date_time(epoch_time) + write_to_excel(self._excel_workbook, self._record_name, row, col, + date_time) + elif 'time' in spec and isinstance(cell_value, str) and is_cell_value_valid: + epoch_time = self.get_date_time_in_epoch(cell_value) + calibration_record[group][hardware][spec][1] = epoch_time - if is_value_different and 'crc' in spec: - # Convert the data in the temp buffer to bytes and calculate its crc. - data = b''.join(temp_buffer) - crc_value = self.crc_16(data) - # Clear the temp buffer for the next round of data - is_value_different = False - dialin_record[group][hardware][spec][1] = crc_value - self.excel_ops.write_to_excel(self._excel_workbook, self._record_name, row, col, - crc_value) - col += 2 - else: - col = 3 - for spec in dialin_record[group][hardware]: - cell_value = self.excel_ops.get_cell_value(self._excel_workbook, self._record_name, row, - col) - # Check if the cell value is not none. If it is none, it cannot be converted to a number - # like float or integer - is_cell_value_valid = True if cell_value is not None else False - if is_cell_value_valid: - dialin_record[group][hardware][1] = cell_value - temp_buffer.clear() - row += 1 - row += 1 + if is_value_different and 'crc' in spec: + # Convert the data in the temp buffer to bytes and calculate its crc. + data = b''.join(temp_buffer) + crc_value = self.crc_16(data) + # Clear the temp buffer for the next round of data + is_value_different = False + calibration_record[group][hardware][spec][1] = crc_value + write_to_excel(self._excel_workbook, self._record_name, row, col, + crc_value) + col += 2 + else: + col = 3 + for spec in calibration_record[group][hardware]: + cell_value = get_cell_value(self._excel_workbook, self._record_name, row, + col) + # Check if the cell value is not none. If it is none, it cannot be converted to a number + # like float or integer + is_cell_value_valid = True if cell_value is not None else False + if is_cell_value_valid: + calibration_record[group][hardware][1] = cell_value + temp_buffer.clear() + row += 1 + row += 1 - self.excel_ops.save_report(self._excel_workbook, self._workspace_dir, self._firmware_stack) + save_report(self._excel_workbook, self._workspace_dir, self._firmware_stack) + except Exception as e: + self.logger.error("Failed to load the excel record as a calibration record: {0}".format(e)) + def get_writing_to_excel_status(self): """ Publicly accessible function to get the reading status @return True if reading to firmware records to excel is done otherwise False """ + return self._is_writing_to_excel_done def get_reading_record_status(self): - - return self._is_read_done - - def create_workspace(self, dir_name): """ - Publicly accessible function to get create a workspace for the script that is running. - - @param dir_name: Name of the workspace directory - - @return none + Gets the reading record status + @return: True if done, false otherwise """ - # Get the root directory of the current script - scripts_root_dir = os.path.dirname(os.path.dirname(__file__)) - # Get the root directory of the entire scripts folder. The workspace that holds the - # code review reports and clones other scripts and repositories must be outside of the scripts - root_dir = os.path.dirname(os.path.dirname(scripts_root_dir)) - # Create the address of the workspace - self._workspace_dir = os.path.join(root_dir, dir_name) - # If the path does not exist, make it, otherwise, change to that directory - if not os.path.isdir(self._workspace_dir): - # Create the directory and go to it - os.mkdir(self._workspace_dir) - os.chdir(self._workspace_dir) + return self._is_read_done + @staticmethod def get_current_time_in_epoch(): """ Returns the current date and time in epoch in integer format. This is a static method. - @return: data and time in epoch in integer format + @return: (int) data and time in epoch """ return int(datetime.datetime.now().timestamp()) @staticmethod - def get_current_date_time(epoch): + def get_current_date_time(epoch: int): """ Returns the current date and time from an epoch time + @param: (int) the epoch @return: data and time in string """ return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(epoch)) @staticmethod - def get_date_time_in_epoch(data_time): + def get_date_time_in_epoch(data_time: str): """ Returns the date time in epoch - @return: data and time in epoch + @return: (str) data and time in epoch """ time_struct = time.strptime(data_time, '%Y-%m-%d %H:%M:%S') return int(time.mktime(time_struct)) - def process_received_record_from_fw(self, dialin_record, fw_raw_records_bytes, write_to_excel=True): + def process_received_record_from_fw(self, cal_record: dict, fw_raw_records_bytes: List[int]): """ Handles processing the received record from firmware - @param dialin_record: dictionary that is being updated from firmware - @param fw_raw_records_bytes: list of the firmware record from firmware in bytes - @param write_to_excel: flag to whether write the latest data to the excel document (default is True) + @param cal_record: (dict) calibration record from firmware + @param fw_raw_records_bytes: (List[int]) list of the firmware record from firmware in bytes @return: none """ - raw_payload_temp_start_index = 0 - # Convert the concatenated raw data into a byte array since the struct library requires byte arrays. - fw_raw_records_bytes = bytearray(fw_raw_records_bytes) + try: + raw_payload_temp_start_index = 0 + # Convert the concatenated raw data into a byte array since the struct library requires byte arrays. + fw_raw_records_bytes = bytearray(fw_raw_records_bytes) - # Loop through the keys for the main calibration_record 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 + 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(' 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('> 8) & 0x0000FFFF - crc = left ^ self.CRC_16_TABLE[data[i] ^ (right & 0x00FF)] + crc = left ^ cls.CRC_16_TABLE[data[i] ^ (right & 0x00FF)] l -= 1 i += 1 Index: tests/peter/test_calibration.py =================================================================== diff -u -r91993c31b1fb88a3937ea9ae4b549d357b53e14e -r8cc6f4843c77fc7bfe358c9f752740919c25f65f --- tests/peter/test_calibration.py (.../test_calibration.py) (revision 91993c31b1fb88a3937ea9ae4b549d357b53e14e) +++ tests/peter/test_calibration.py (.../test_calibration.py) (revision 8cc6f4843c77fc7bfe358c9f752740919c25f65f) @@ -284,11 +284,12 @@ if __name__ == "__main__": - # test_dg_calibration_record() + test_dg_calibration_record() # test_dg_service_record() # test_dg_system_record() # test_dg_scheduled_runs_record() # test_hd_calibration_record() # test_hd_service_record() - # test_hd_system_record() + #test_hd_system_record() + # test_crc() \ No newline at end of file Index: tests/test_dg_records.py =================================================================== diff -u -re8afa8d6323ede293e1fb094a461555fcf7ce0d9 -r8cc6f4843c77fc7bfe358c9f752740919c25f65f --- tests/test_dg_records.py (.../test_dg_records.py) (revision e8afa8d6323ede293e1fb094a461555fcf7ce0d9) +++ tests/test_dg_records.py (.../test_dg_records.py) (revision 8cc6f4843c77fc7bfe358c9f752740919c25f65f) @@ -9,14 +9,15 @@ try: if read: - cal.get_dg_calibration_record() + cal.cmd_request_dg_calibration_record() while True: sleep(0.5) if cal.cal_data != 0: - if cal.is_reading_record_done(): - break + #if cal.is_reading_record_done(): + sleep(4) + break else: - cal.set_dg_calibration_record() + cal.cmd_set_dg_calibration_record() print(cal.dg_calibration_record) @@ -76,12 +77,21 @@ exit(1) sleep(2) - process_calibration_record(read=False) + dg.calibration_record.cmd_request_dg_calibration_record() + sleep(5) + dg.calibration_record.cmd_set_dg_calibration_record(dg.calibration_record.dg_calibration_record) + sleep(5) + + dg.calibration_record.cmd_request_dg_calibration_record() + sleep(5) + dg.calibration_record.cmd_set_dg_calibration_record(dg.calibration_record.dg_calibration_record) + sleep(5) + + #process_calibration_record(read=True) #process_system_record(read=True) #process_service_record(read=False) -