Index: dialin/common/msg_ids.py =================================================================== diff -u -rd517baec135aaafcceb0142d83b9deb156dab283 -rd832b9ec145bf0c8bf3bf0bdc4844d146a60e600 --- dialin/common/msg_ids.py (.../msg_ids.py) (revision d517baec135aaafcceb0142d83b9deb156dab283) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision d832b9ec145bf0c8bf3bf0bdc4844d146a60e600) @@ -314,6 +314,7 @@ MSG_ID_HD_BLOCK_MESSAGE_TRANSMISSION = 0x8076 MSG_ID_HD_SYRINGE_PUMP_FORCE_SENSOR_DAC_CALIBRATE = 0x8077 MSG_ID_HD_STOP_RTC_CLOCK = 0x8078 + MSG_ID_HD_FANS_DUTY_CYCLE_OVERRIDE = 0x8079 MSG_ID_DG_TESTER_LOGIN_REQUEST = 0XA000 MSG_ID_DG_ALARM_STATE_OVERRIDE = 0XA001 @@ -389,6 +390,7 @@ MSG_ID_DG_GET_SW_CONFIG_RECORD = 0xA04A MSG_ID_DG_SET_SW_CONFIG_RECORD = 0xA04B MSG_ID_DG_SEND_SW_CONFIG_RECORD = 0xA04C + MSG_ID_DG_FANS_DUTY_CYCLE_OVERRIDE = 0xA04D MSG_ID_HD_DEBUG_EVENT = 0XFFF1 MSG_ID_DG_DEBUG_EVENT = 0XFFF2 Index: dialin/dg/fans.py =================================================================== diff -u -r7b757e5f7c3787ee2e8af80716767707ac2bab74 -rd832b9ec145bf0c8bf3bf0bdc4844d146a60e600 --- dialin/dg/fans.py (.../fans.py) (revision 7b757e5f7c3787ee2e8af80716767707ac2bab74) +++ dialin/dg/fans.py (.../fans.py) (revision d832b9ec145bf0c8bf3bf0bdc4844d146a60e600) @@ -242,7 +242,7 @@ """ Constructs and sends the HD fan RPM alarm start time override command Constraints: - Must be logged into HD. + Must be logged into DG. @param hours: (int) hours that the fan alarm start time must be overridden to @param minutes: (int) minutes that the fan alarm start time must be overridden to @@ -273,3 +273,37 @@ else: self.logger.debug("Timeout!!!!") return False + + def cmd_fans_duty_cycle_override(self, duty_cycle: float, reset: int = NO_RESET) -> int: + """ + Constructs and sends the DG fans duty cycle override command + Constraints: + Must be logged into DG. + + @param duty_cycle: (float) the duty cycle that the fans are overridden to + @param reset: (int) 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + dc = float_to_bytearray(duty_cycle / 100.0) + payload = reset_value + dc + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_FANS_DUTY_CYCLE_OVERRIDE.value, + payload=payload) + + self.logger.debug("Override fans duty cycle") + + # Send message + received_message = self.can_interface.send(message) + + # If there is no content... + if received_message is not None: + + self.logger.debug("Set fans duty cycle to: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False Index: dialin/dg/sw_config.py =================================================================== diff -u -r3e92417e035803f1401d5818f0117859ee049d9e -rd832b9ec145bf0c8bf3bf0bdc4844d146a60e600 --- dialin/dg/sw_config.py (.../sw_config.py) (revision 3e92417e035803f1401d5818f0117859ee049d9e) +++ dialin/dg/sw_config.py (.../sw_config.py) (revision d832b9ec145bf0c8bf3bf0bdc4844d146a60e600) @@ -71,9 +71,11 @@ @return: True if successful, False otherwise """ + # Get the default software configuration dictionary self.dg_sw_config_record = self._prepare_dg_sw_configs_record() - self.dg_sw_config_record = self._utilities.reset_fw_system_service_record(self.dg_sw_config_record) - status = self.cmd_set_dg_sw_config_record(self.dg_sw_config_record) + # Calculate the CRC for reset software configuration record + self._utilities.reset_fw_system_service_record(self.dg_sw_config_record) + status = self._cmd_set_dg_sw_config_record() return status @@ -155,16 +157,28 @@ """ self.logger.debug("Received a complete dg software configuration record.") - def cmd_set_dg_sw_config_record(self, excel_report_path: str) -> bool: + def cmd_update_dg_sw_config_record(self, excel_report_path: str): """ - Handles updating the DG software configuration record and sends it to FW. + Handles preparing the DG software configuration from the provided excel report @param excel_report_path: (str) the directory in which the excel report of the software configuration is located + @return: none + """ + # Pass the software configuration record dictionary to be updated with the excel document + status = self._utilities.get_sw_configs_from_excel(self.dg_sw_config_record, excel_report_path, + self._NON_VOLATILE_RECORD_NAME) + # The excel document was successfully read initiate a write command + if status: + self._cmd_set_dg_sw_config_record() + else: + self.logger.debug('Could not find the software configurations file') + + def _cmd_set_dg_sw_config_record(self) -> bool: + """ + Handles updating the DG software configuration record and sends it to FW. + @return: True upon success, False otherwise """ - self._utilities.get_sw_configs_from_excel(self.dg_sw_config_record, excel_report_path, - self._NON_VOLATILE_RECORD_NAME) - print(self.dg_sw_config_record) record_packets = self._utilities.prepare_record_to_send_to_fw(self.dg_sw_config_record) self.logger.debug('Setting DG sw config record') @@ -260,12 +274,14 @@ # Create the excel report self._utilities.prepare_excel_report(self._FIRMWARE_STACK_NAME, self._NON_VOLATILE_RECORD_NAME, report_address) - + # Request the latest software configuration record from firmware self._cmd_request_dg_sw_config_record() - + # Create ab object of the observer class to observe the dictionary observer = Observer("dg_sw_config_record") + # Attach the observer to the list self.attach(observer) + # Wait until data has been received from firmware while not observer.received: sleep(0.1) - + # Write the updated values from excel to firmware self._utilities.write_sw_config_to_excel(self.dg_sw_config_record) Index: dialin/hd/fans.py =================================================================== diff -u -r4b8f0afb5aafc2409327ec47aad264ab500e69f7 -rd832b9ec145bf0c8bf3bf0bdc4844d146a60e600 --- dialin/hd/fans.py (.../fans.py) (revision 4b8f0afb5aafc2409327ec47aad264ab500e69f7) +++ dialin/hd/fans.py (.../fans.py) (revision d832b9ec145bf0c8bf3bf0bdc4844d146a60e600) @@ -57,7 +57,6 @@ self.target_rpm = 0.0 self.inlet_1_rpm = 0.0 self.rpm_alarm_time_offset = 0 - self.alarm_elapsed = 0 def get_fans_target_duty_cycle(self): """ @@ -83,7 +82,7 @@ """ return self.target_rpm - @publish(['duty_cycle', 'target_rpm', 'inlet_1_rpm', 'rpm_alarm_time_offset', 'alarm_elapsed']) + @publish(['duty_cycle', 'target_rpm', 'inlet_1_rpm', 'rpm_alarm_time_offset']) def _handler_fans_sync(self, message): """ Handles published thermistors message. @@ -99,8 +98,6 @@ message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] self.rpm_alarm_time_offset = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.alarm_elapsed = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] def cmd_fans_rpm_override(self, fan: int, rpm: float, reset: int = NO_RESET) -> int: """ @@ -211,9 +208,43 @@ if received_message is not None: self.logger.debug("Set RPM alarm start time to: " + - str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) # response payload is OK or not OK return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] else: self.logger.debug("Timeout!!!!") return False + + def cmd_fans_duty_cycle_override(self, duty_cycle: float, reset: int = NO_RESET) -> int: + """ + Constructs and sends the HD fans duty cycle override command + Constraints: + Must be logged into HD. + + @param duty_cycle: (float) the duty cycle that the fans are overridden to + @param reset: (int) 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + dc = float_to_bytearray(duty_cycle / 100.0) + payload = reset_value + dc + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_FANS_DUTY_CYCLE_OVERRIDE.value, + payload=payload) + + self.logger.debug("Override fans duty cycle") + + # Send message + received_message = self.can_interface.send(message) + + # If there is no content... + if received_message is not None: + + self.logger.debug("Set fans duty cycle to: " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False Index: dialin/utils/nv_ops_utils.py =================================================================== diff -u -r3e92417e035803f1401d5818f0117859ee049d9e -rd832b9ec145bf0c8bf3bf0bdc4844d146a60e600 --- dialin/utils/nv_ops_utils.py (.../nv_ops_utils.py) (revision 3e92417e035803f1401d5818f0117859ee049d9e) +++ dialin/utils/nv_ops_utils.py (.../nv_ops_utils.py) (revision d832b9ec145bf0c8bf3bf0bdc4844d146a60e600) @@ -24,12 +24,22 @@ class Observer(AbstractObserver): + """ + + Observation class + """ def __init__(self, prop): self.received = False self.prop = prop def update(self, message): + """ + Publicly accessible function to provide an update of the object that is being observed + @param message: (str) the message to update its status + + @return none + """ self.received = message.get(self.prop, False) @@ -890,41 +900,61 @@ save_report(self._excel_workbook, self._workspace_dir, 'SW-Configs') def get_sw_configs_from_excel(self, sw_configs_dict: OrderedDict, excel_path: str, sw_config_excel_tab: str): + """ + Publicly accessible function to get the software configurations from excel + @param sw_configs_dict: (ordered dictionary) the configurations record to write to excel + @param excel_path: (str): the path to the excel report that its data is read + @param sw_config_excel_tab: (str): the name of the tab in the excel report that the values are located at + + @return status of the operations + """ row = 1 col = 1 title_col = None value_col = None max_col_to_go = 50 + status = False + # Load the excel report self._excel_workbook = load_excel_report(excel_path) active_sheet = self._excel_workbook[sw_config_excel_tab] while True: # Loop through the cells in the title row value = active_sheet.cell(row=row, column=col).value - # Remove the extra characters from the read value in the excel and compare it with the name of the alarm ID - # col title. If the search kept going beyond the max allowed column, break out of the loop + # If the col number exceeded the maximum column number, break out of the loop if col >= max_col_to_go: break + # Check if the value of the read cell is not none if value is not None: + # If the value of the title row is name of the sw configs title column name, update the title col number if value.strip() == self._SW_CONFIGS_TITLE_COL: title_col = col + # If the value of the title row is the name of the values of the sw configs, update the value col number if value.strip() == self._SW_CONFIGS_VALUE_COL: value_col = col - + # If the title col and value col numbers are both found and they are not none, then exit the while loop + # since the values have been found, otherwise, increment the column number and keep looking if title_col is not None and value_col is not None: break else: col += 1 - last_non_empty_row = active_sheet.max_row + if title_col is not None and value_col is not None: + # Get the last non-empty row number of the current active sheet + last_non_empty_row = active_sheet.max_row + # Get the dictionary of the provided sw configurations dictionary, this can be either HD or DG + fw_sw_configs = sw_configs_dict['sw_configs'] + # Loop through the excel from row 2 since row 1 is the titles row until the last non-empty row + for row in range(2, last_non_empty_row + 1): + config = active_sheet.cell(row=row, column=title_col).value - fw_sw_configs = sw_configs_dict['sw_configs'] + if config is not None: + # Check if the software configuration that has been read from excel exists in dictionary that has + # been prepared in Dialin + if config.strip() in fw_sw_configs: + fw_sw_configs[config.strip()][1] = active_sheet.cell(row=row, column=value_col).value + status = True - for row in range(2, last_non_empty_row + 1): - config = active_sheet.cell(row=row, column=title_col).value - - if config is not None: - if config.strip() in fw_sw_configs: - fw_sw_configs[config.strip()][1] = active_sheet.cell(row=row, column=value_col).value + return status Index: tests/dg_tests.py =================================================================== diff -u -r3e92417e035803f1401d5818f0117859ee049d9e -rd832b9ec145bf0c8bf3bf0bdc4844d146a60e600 --- tests/dg_tests.py (.../dg_tests.py) (revision 3e92417e035803f1401d5818f0117859ee049d9e) +++ tests/dg_tests.py (.../dg_tests.py) (revision d832b9ec145bf0c8bf3bf0bdc4844d146a60e600) @@ -203,14 +203,12 @@ def get_hd_fans_info(): info = ('HD_Fan_DC, {:5.3f}, Target_HD_RPM, {:5.3f}, Inlet1_RPM, {:5.3f}, HD_Board_temp, {:5.3f}, ' 'HD_Power_supply, {:5.3f}, HD_FPGA_temp, {:5.3f}, PBA_ADC_temp, {:5.3f}, Venous_temp, {:5.3f}, ' - 'Elapsed, {:5.3f}, ' .format(hd.fans.duty_cycle, hd.fans.target_rpm, hd.fans.inlet_1_rpm, hd.temperatures.hd_temperatures[HDTemperaturesNames.THERMISTOR_ONBOARD_NTC.name], hd.temperatures.hd_temperatures[HDTemperaturesNames.THERMISTOR_POWER_SUPPLY_1.name], hd.temperatures.hd_temperatures[HDTemperaturesNames.TEMPSENSOR_FPGA_BOARD.name], hd.temperatures.hd_temperatures[HDTemperaturesNames.TEMPSENSOR_PBA_ADC_SENSOR.name], - hd.temperatures.hd_temperatures[HDTemperaturesNames.TEMPSENSOR_VENOUS_PRESS_TEMP.name], - hd.fans.alarm_elapsed)) + hd.temperatures.hd_temperatures[HDTemperaturesNames.TEMPSENSOR_VENOUS_PRESS_TEMP.name])) return info @@ -540,7 +538,7 @@ hd.cmd_log_in_to_hd() sleep(1) - run_heat_disinfect() + # run_heat_disinfect() # run_chemical_disinfect() @@ -556,9 +554,9 @@ # test_dg_fans_alarms() + hd.fans.cmd_fans_duty_cycle_override(68, reset=1) + while True: + print(get_hd_fans_info()) - - - - + sleep(1)