Index: dialin/common/msg_ids.py =================================================================== diff -u -radae506afce35a0063c6c2baf7e8580986f3bee7 -rd517baec135aaafcceb0142d83b9deb156dab283 --- dialin/common/msg_ids.py (.../msg_ids.py) (revision adae506afce35a0063c6c2baf7e8580986f3bee7) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision d517baec135aaafcceb0142d83b9deb156dab283) @@ -247,8 +247,8 @@ MSG_ID_HD_AIR_TRAP_SEND_INTERVAL_OVERRIDE = 0X8032 MSG_ID_HD_AIR_TRAP_LEVEL_SENSOR_OVERRIDE = 0X8033 MSG_ID_HD_SOFTWARE_RESET_REQUEST = 0X8034 - MSG_ID___AVAILABLE_3 = 0X8035 - MSG_ID___AVAILABLE_4 = 0X8036 + MSG_ID_HD_GET_SW_CONFIG_RECORD = 0x8035 + MSG_ID_HD_SET_SW_CONFIG_RECORD = 0x8036 MSG_ID_BLOOD_PUMP_HOME_CMD = 0X8037 MSG_ID_DIAL_IN_PUMP_HOME_CMD = 0X8038 MSG_ID_DIAL_OUT_PUMP_HOME_CMD = 0X8039 @@ -299,7 +299,7 @@ MSG_ID_HD_FANS_PUBLISH_INTERVAL_OVERRIDE = 0x8067 MSG_ID_HD_FANS_RPM_OVERRIDE = 0x8068 MSG_ID_HD_RINSEBACK_VOLUME_OVERRIDE = 0x8069 - MSG_ID_HD___AVAILABLE_2 = 0x806A + MSG_ID_HD_SEND_SW_CONFIG_RECORD = 0x806A MSG_ID_HD_ALARM_STATUS_PUBLISH_INTERVAL_OVERRIDE = 0x806B MSG_ID_HD_TREATMENT_TIME_DATA_PUBLISH_INTERVAL_OVERRIDE = 0x806C MSG_ID_HD_TREATMENT_RANGES_PUBLISH_INTERVAL_OVERRIDE = 0x806D @@ -386,6 +386,9 @@ MSG_ID_DG_SUPER_CLEAR_ALARMS_CMD = 0XA047 MSG_ID_DG_ALARM_INFO_SEND_INTERVAL_OVERRIDE = 0XA048 MSG_ID_DG_FAN_RPM_ALARM_START_TIME_OFFSET_OVERRIDE = 0xA049 + 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_HD_DEBUG_EVENT = 0XFFF1 MSG_ID_DG_DEBUG_EVENT = 0XFFF2 Index: dialin/dg/dialysate_generator.py =================================================================== diff -u -radae506afce35a0063c6c2baf7e8580986f3bee7 -rd517baec135aaafcceb0142d83b9deb156dab283 --- dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision adae506afce35a0063c6c2baf7e8580986f3bee7) +++ dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision d517baec135aaafcceb0142d83b9deb156dab283) @@ -47,6 +47,7 @@ from .valves import DGValves from .voltages import DGVoltages from .events import DGEvents +from .sw_config import DGSoftwareConfigs from ..common.msg_defs import MsgIds, MsgFieldPositions from ..protocols.CAN import DenaliCanMessenger, DenaliMessage, DenaliChannels from ..utils import * @@ -184,6 +185,7 @@ self.valves = DGValves(self.can_interface, self.logger) self.voltages = DGVoltages(self.can_interface, self.logger) self.events = DGEvents(self.can_interface, self.logger) + self.sw_configs = DGSoftwareConfigs(self.can_interface, self.logger) def get_version(self): """ Index: dialin/dg/sw_config.py =================================================================== diff -u --- dialin/dg/sw_config.py (revision 0) +++ dialin/dg/sw_config.py (revision d517baec135aaafcceb0142d83b9deb156dab283) @@ -0,0 +1,246 @@ + + +import struct +import time +from collections import OrderedDict +from enum import unique +from logging import Logger + +from ..common.msg_defs import MsgIds, MsgFieldPositions +from ..protocols.CAN import DenaliMessage, DenaliChannels +from ..utils.base import AbstractSubSystem, DialinEnum, publish +from ..utils.nv_ops_utils import NVOpsUtils + + +@unique +class SWConfigs(DialinEnum): + SW_CONFIG_DISABLE_HEATERS_MONITOR = 0 + SW_CONFIG_THD_USING_TRO_CONNECTOR = 1 + SW_CONFIG_DISABLE_CAL_CHECK = 2 + SW_CONFIG_ALARMS_DEBUG = 3 + SW_CONFIG_DISABLE_RO_PUMP_MONITOR = 4 + SW_CONFIG_DISABLE_RO_RATIO_CHECK = 5 + SW_CONFIG_DISABLE_COND_SENSOR_CHECK = 6 + NUM_OF_SW_CONFIGS = 7 + + +class DGSoftwareConfigs(AbstractSubSystem): + """ + + Dialysate Generator (DG) Dialin API sub-class for setting and getting the software configurations. + """ + + _DEFAULT_SW_CONFIG_STATUS = 0 + _DEFAULT_CRC_VALUE = 0 + _RECORD_SPECS_BYTES = 12 + # Maximum allowed bytes to be written to RTC RAM + _RTC_RAM_MAX_BYTES_TO_WRITE = 64 + _PAYLOAD_TRANSFER_DELAY_S = 0.2 + + 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._received_msg_length = 0 + self._sw_config_data = 0 + self._is_getting_sw_config_in_progress = False + self._raw_sw_config_record = [] + self._utilities = NVOpsUtils(logger=self.logger) + self.dg_sw_config_record = self._prepare_dg_sw_configs_record() + + if self.can_interface is not None: + channel_id = DenaliChannels.dg_to_dialin_ch_id + msg_id = MsgIds.MSG_ID_DG_SEND_SW_CONFIG_RECORD.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_dg_sw_config_sync) + + def cmd_reset_dg_sw_config_record(self) -> bool: + """ + Handles resetting DG software configuration record. + + @return: True if successful, False otherwise + """ + 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) + + return status + + def cmd_request_dg_sw_config_record(self) -> int: + """ + Handles getting DG software config record from firmware. + + @return: 1 upon success, False otherwise + """ + if self._is_getting_sw_config_in_progress is not True: + self._is_getting_sw_config_in_progress = True + # Clear the list for the next call + self._raw_sw_config_record.clear() + # Run the firmware commands to get the record + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_GET_SW_CONFIG_RECORD.value) + + self.logger.debug('Getting DG software configuration record') + + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + self.logger.debug("Received FW ACK after requesting DG service record.") + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + self.logger.debug("Request cancelled: an existing request is in progress.") + return False + + def _handler_dg_sw_config_sync(self, message): + """ + Handles published DG software configuration record messages. DG software configuration records are captured for + processing and updating the DG software configuration record. + + @param message: published DG software configuration 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._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 = MsgFieldPositions.START_POS_FIELD_1 + self._RECORD_SPECS_BYTES + self._received_msg_length + + # Get the data only and not specs of it (i.e current message number) + self._sw_config_data = message['message'][MsgFieldPositions.START_POS_FIELD_1:end_of_data_index] + + # Continue getting calibration_record records until the all the calibration_record messages are received. + # Concatenate the calibration_record records to each other + if self._current_message <= self._total_messages: + self._raw_sw_config_record += (message['message'][MsgFieldPositions.START_POS_FIELD_1 + + self._RECORD_SPECS_BYTES:end_of_data_index]) + if self._current_message == self._total_messages: + # Done with receiving the messages + self._is_getting_sw_config_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_sw_config_record, self._raw_sw_config_record) + self._handler_received_complete_dg_sw_config_record() + + @publish(["dg_sw_config_record"]) + def _handler_received_complete_dg_sw_config_record(self): + """ + Publishes the received software configuration record + + @return: None + """ + self.logger.debug("Received a complete dg software configuration record.") + + def cmd_set_dg_sw_config_record(self, dg_sw_config_record: OrderedDict) -> bool: + """ + Handles updating the DG software configuration record and sends it to FW. + + @param dg_sw_config_record: (OrderedDict) the dg software configuration record to be sent + @return: True upon success, False otherwise + """ + record_packets = self._utilities.prepare_record_to_send_to_fw(dg_sw_config_record) + + self.logger.debug('Setting DG sw config record') + + # Update all the data packets with the last message count since is the number of messages that firmware + # should receive + for packet in record_packets: + # Sleep to let the firmware receive and process the data + time.sleep(self._PAYLOAD_TRANSFER_DELAY_S) + + # Convert the list packet to a bytearray + payload = b''.join(packet) + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dg_ch_id, + message_id=MsgIds.MSG_ID_DG_SET_SW_CONFIG_RECORD.value, + payload=payload) + + received_message = self.can_interface.send(message) + + # If there is no content... + if received_message is None: + self.logger.debug("Timeout!!!!") + return False + + self.logger.debug("Finished sending DG software configuration record.") + return True + + def _prepare_dg_sw_configs_record(self) -> OrderedDict: + """ + Handles assembling the sub dictionaries of each group to make a blank dg software configuration record. + + @return: (OrderedDict) the assembled dg software configuration record + """ + record = OrderedDict() + + groups_byte_size = 0 + # create a list of the functions of the sub dictionaries + functions = [self._prepare_sw_configs_record()] + + for function in functions: + # Update the groups bytes size so far to be use to padding later + groups_byte_size += function[1] + # Update the calibration record + record.update(function[0]) + + # Build the CRC of the main calibration_record record + record_crc = OrderedDict({'crc': [' tuple: + """ + Handles creating the software configuration record dictionary. + + @return: software configuration record dictionary and the byte size of this group + """ + groups_byte_size = 0 + name = 'sw_configs' + # Create an ordered dictionary + sw_configs = OrderedDict({name: {}}) + + # Loop through the members of the SWConfigs enum class + for config in SWConfigs.__members__: + # Insert the enum name into the dictionary with the default software config. Each config is one byte + sw_configs[name].update({config: [' 7 and dg.dg_operation_sub_mode == 2: + dg.hd_proxy.cmd_start_stop_dg(start=False) + f.close() + break + + elif DGOperationModes(dg.dg_operation_mode).name == DGOperationModes.DG_OP_MODE_GEN_IDLE.name and \ + dg.dg_operation_sub_mode == 2 and counter == 1: + + if recirc_delay < 5: + recirc_delay += 1 + else: + if timer == 1: + dg.hd_proxy.cmd_switch_reservoirs(reservoirID=1) + timer += 1 + + if timer > ((1 / sleep_time) * 1): + timer = 1 + recirc_delay = 1 + counter += 1 + + elif DGOperationModes(dg.dg_operation_mode).name == DGOperationModes.DG_OP_MODE_GEN_IDLE.name and \ + dg.dg_operation_sub_mode == 2 and counter == 2: + dg.hd_proxy.cmd_drain(tare_load_cell=True) + counter += 1 + timer = 1 + + elif DGOperationModes(dg.dg_operation_mode).name == DGOperationModes.DG_OP_MODE_GEN_IDLE.name and \ + dg.dg_operation_sub_mode == 2 and counter == 3: + + timer += 1 + if timer > 4: + dg.hd_proxy.cmd_fill(volume=1700) + counter += 1 + + elif DGOperationModes(dg.dg_operation_mode).name == DGOperationModes.DG_OP_MODE_GEN_IDLE.name and \ + dg.dg_operation_sub_mode == 2 and counter == 4: + counter = 1 + run_number += 1 + + sleep(sleep_time) + + except KeyboardInterrupt: + dg.hd_proxy.cmd_start_stop_dg(start=False) + f.close() + pass + + +def collect_treatment_data(): + f = open("/home/fw/projects/dialin/tests/treatment_run.log", "w") + + #dg.cmd_dg_software_reset_request() + + try: + while True: + hd_run = get_hd_run_info() + hd_rsrvrs = get_hd_reservoirs_info() + dg_rsrvrs = get_dg_reservoirs_info() + dg_run = get_dg_run_info() + drain = get_drain_states_info() + load_cell = get_load_cells_info() + valves = get_dg_valves_states() + ro = get_ro_info() + temp = get_temperature_sensors_info() + heaters = get_heaters_info() + dg_fans = get_dg_fans_info() + conc_pumps = get_concentrate_pumps_info() + + var = str(datetime.now()) + ', ' + hd_run + dg_run + hd_rsrvrs + dg_rsrvrs + load_cell + drain + ro + \ + temp + heaters + conc_pumps + dg_fans + valves + '\r' + + print(var) + f.write(var) + sleep(1) + except KeyboardInterrupt: + dg.hd_proxy.cmd_start_stop_dg(start=False) + f.close() + + +def collect_hd_treatment(): + f = open("/home/fw/projects/dialin/tests/treatment_run_hd.log", "w") + + hd.cmd_hd_software_reset_request() + + try: + while True: + + sleep(1) + except KeyboardInterrupt: + events = hd.hd_events.get_hd_events(2, 0) + for event in events: + print(event) + f.close() + + +def run_heat_disinfect(): + complete_counter = 1 + f = open("/home/fw/projects/dialin/tests/Heat_disinfect.log", "w") + dg.hd_proxy.cmd_start_stop_heat_disinfect() + + try: + while True: + + disinfect = get_heat_disinfect_mode_info() + drain = get_drain_states_info() + load_cell = get_load_cells_info() + valves = get_dg_valves_states() + ro = get_ro_info() + temp = get_temperature_sensors_info() + heaters = get_heaters_info() + dg_fans = get_dg_fans_info() + hd_fans = get_hd_fans_info() + + var = disinfect + load_cell + drain + ro + temp + heaters + dg_fans + hd_fans + valves + '\r' + + print(var) + f.write(var) + sleep(1) + + # If the mode came back to standby or standby solo + if dg.dg_operation_mode == 3 or dg.dg_operation_mode == 4: + # If it is the first call, stop heat disinfect + if complete_counter == 1: + dg.hd_proxy.cmd_start_stop_heat_disinfect(start=False) + + # Write a few more complete states to make sure the complete state items are recorded + elif complete_counter == 3: + # pass + f.close() + break + + complete_counter += 1 + + except KeyboardInterrupt: + dg.hd_proxy.cmd_start_stop_heat_disinfect(start=False) + f.close() + + +def run_chemical_disinfect(): + complete_counter = 1 + f = open("/home/fw/projects/dialin/tests/chemical_disinfect.log", "w") + dg.hd_proxy.cmd_start_stop_dg_chemical_disinfect() + + try: + while True: + + disinfect = get_chemical_disinfect_mode_info() + drain = get_drain_states_info() + load_cell = get_load_cells_info() + conc = get_concentrate_pumps_info() + valves = get_dg_valves_states() + ro = get_ro_info() + temp = get_temperature_sensors_info() + heaters = get_heaters_info() + fans = get_dg_fans_info() + + var = disinfect + load_cell + conc + drain + ro + temp + heaters + fans + valves + '\r' + + print(var) + f.write(var) + sleep(1) + + # If the mode came back to standby or standby solo + if dg.dg_operation_mode == 3 or dg.dg_operation_mode == 4: + + # Write a few more complete states to make sure the complete state items are recorded + if complete_counter == 3: + # pass + f.close() + break + + complete_counter += 1 + + except KeyboardInterrupt: + dg.hd_proxy.cmd_start_stop_dg_chemical_disinfect(start=False) + f.close() + + +def cmd_set_disinfect_ui_screen(): + hd = HD() + sleep(0.5) + hd.ui.cmd_ui_set_standby_submode_to_disinfect() + + while True: + print(hd.ui.disinfects_hd_submode, hd.ui.disinfects_dg_mode) + sleep(1) + + +def test_fans_alarms(): + + counter = 0 + while counter < 1: + + hd.cmd_hd_software_reset_request() + + while True: + if hd.hd_operation_mode == HDOpModes.MODE_STAN.value: + + hd.alarms.cmd_alarm_info_broadcast_interval_override(50, reset=0) + sleep(1) + hd.fans.cmd_fans_rpm_override(0, 1200.0, reset=0) + start_time = datetime.now() + + while True: + if hd.alarms.get_alarm_state(AlarmList.ALARM_ID_HD_FAN_RPM_OUT_OF_RANGE.value): + print(datetime.now() - start_time) + + hd.ui.cmd_ui_user_alarm_response(3) + break + + counter += 1 + break + + +if __name__ == "__main__": + + dg = DG(log_level='DEBUG') + dg.cmd_log_in_to_dg() + sleep(1) + hd = HD(log_level='DEBUG') + hd.cmd_log_in_to_hd() + sleep(1) + + # run_heat_disinfect() + + # run_chemical_disinfect() + + # run_dg() + + # cmd_set_disinfect_ui_screen() + + # collect_treatment_data() + + # collect_hd_treatment() + + test_fans_alarms() + + + + + + + Index: tests/peter/test_dg_records.py =================================================================== diff -u -r42100367185571479b8e07191b2c423314f23aef -rd517baec135aaafcceb0142d83b9deb156dab283 --- tests/peter/test_dg_records.py (.../test_dg_records.py) (revision 42100367185571479b8e07191b2c423314f23aef) +++ tests/peter/test_dg_records.py (.../test_dg_records.py) (revision d517baec135aaafcceb0142d83b9deb156dab283) @@ -170,6 +170,20 @@ self.received = message.get(self.prop, False) +def test_dg_sw_config_record(): + + dg = DG(log_level="DEBUG") + if dg.cmd_log_in_to_dg(): + print(dg.cmd_ui_request_dg_version()) + dg.sw_configs.cmd_reset_dg_sw_config_record() + observer = Observer("dg_sw_config_record") + dg.sw_configs.attach(observer) + while not observer.received: + sleep(0.2) + + print(dg.sw_configs.dg_sw_config_record) + + def test_dg_calibration_record(): dg = DG(log_level="DEBUG") @@ -230,11 +244,11 @@ dg.calibration_record.cmd_reset_dg_calibration_record() sleep(0.25) print(dg.calibration_record.dg_calibration_record) - #dg.scheduled_runs_record.cmd_reset_dg_calibration_record() - #sleep(0.25) - #dg.system_record.cmd_reset_dg_system_record() - #sleep(0.25) - #dg.service_record.cmd_reset_dg_service_record() + dg.scheduled_runs_record.cmd_reset_dg_calibration_record() + sleep(0.25) + dg.system_record.cmd_reset_dg_system_record() + sleep(0.25) + dg.service_record.cmd_reset_dg_service_record() def test_dg_service_record(): @@ -274,7 +288,6 @@ dg.system_record.cmd_set_dg_system_record(dg.system_record.dg_system_record) - def test_dg_scheduled_runs_record(): dg = DG(log_level="DEBUG") if dg.cmd_log_in_to_dg(): @@ -290,6 +303,7 @@ if __name__ == "__main__": test_dg_reset_record() + #test_dg_sw_config_record() #test_dg_calibration_record() # test_dg_service_record() #test_dg_system_record()