Index: leahi_dialin/common/__init__.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/__init__.py (.../__init__.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/common/__init__.py (.../__init__.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -3,12 +3,13 @@ # mandatory import from .msg_defs import * +from .msg_ids import * file_name = "config.json" sw_key = "SW" config = {} ok: bool = False -module_name = os.path.basename(os.path.dirname(__file__)) +module_name = os.path.basename(os.getcwd()) try: with open(file_name, 'r') as file: config = json.load(file); ok = config[sw_key] @@ -17,7 +18,7 @@ except Exception as e : print(f"Error ({module_name}): An unexpected error occurred: {e}" ) if ok: - print(f"{sw_key} is set to bypass the auto imports in '{module_name}' module.") + print(f"{sw_key} is set to bypass the auto imports.") else: from .alarm_defs import * from .alarm_priorities import * Index: leahi_dialin/common/constants.py =================================================================== diff -u -r7ce778dc51b7dba5a7a7ca14244ed7eea7bf029d -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/constants.py (.../constants.py) (revision 7ce778dc51b7dba5a7a7ca14244ed7eea7bf029d) +++ leahi_dialin/common/constants.py (.../constants.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -25,3 +25,5 @@ PUMP_CONTROL_MODE_CLOSED_LOOP = 0 PUMP_CONTROL_MODE_OPEN_LOOP = 1 + +MSG_HEADER_SIZE = 6 # Hardcoded for now to avoid cyclic import issue. See protocols.CAN.CanMessage class Index: leahi_dialin/common/msg_defs.py =================================================================== diff -u -r41de945f9c773e54e965e80d9e46def828beb732 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/msg_defs.py (.../msg_defs.py) (revision 41de945f9c773e54e965e80d9e46def828beb732) +++ leahi_dialin/common/msg_defs.py (.../msg_defs.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -14,15 +14,12 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum -from .msg_ids import MsgIds +from ..utils.enums import DialinEnum +from .constants import MSG_HEADER_SIZE +ACK_NOT_REQUIRED = [ ] -ACK_NOT_REQUIRED = [ -] - - @unique class RequestRejectReasons(DialinEnum): REQUEST_REJECT_REASON_NONE = 0 @@ -75,9 +72,10 @@ REQUEST_REJECT_REASON_TREATMENT_CANNOT_BE_RESUMED = 47 RequestRejectReasons._str_list = {} + class MsgFieldPositions: # Generic response msg field byte positions (where 32-bit data fields are used) - START_POS_FIELD_1 = 6 # Hardcoded for now to avoid cyclic import issue. See protocols.CAN.DenaliMessage class + START_POS_FIELD_1 = MSG_HEADER_SIZE # Hardcoded for now to avoid cyclic import issue. See protocols.CAN.CanMessage class END_POS_FIELD_1 = START_POS_FIELD_1 + 4 START_POS_FIELD_2 = END_POS_FIELD_1 END_POS_FIELD_2 = START_POS_FIELD_2 + 4 @@ -139,7 +137,7 @@ class MsgFieldPositionsFWVersions: # UI version message field positions - START_POS_MAJOR = 6 # Hardcoded for now to avoid cyclic import issue. See protocols.CAN.DenaliMessage class + START_POS_MAJOR = MSG_HEADER_SIZE # Hardcoded for now to avoid cyclic import issue. See protocols.CAN.CanMessage class END_POS_MAJOR = START_POS_MAJOR + 1 START_POS_MINOR = END_POS_MAJOR END_POS_MINOR = START_POS_MINOR + 1 Index: leahi_dialin/common/msg_ids.py =================================================================== diff -u -raa585f94d10cc4c13caa70e5529c7b39a94a5706 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision aa585f94d10cc4c13caa70e5529c7b39a94a5706) +++ leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from ..utils.enums import DialinEnum # Branch: staging Index: leahi_dialin/common/override_templates.py =================================================================== diff -u -r47a803c6dd859a5bcc7a6c82e0cb160c350473ea -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/override_templates.py (.../override_templates.py) (revision 47a803c6dd859a5bcc7a6c82e0cb160c350473ea) +++ leahi_dialin/common/override_templates.py (.../override_templates.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -19,8 +19,8 @@ # Project imports from .constants import RESET -from .msg_defs import MsgIds -from ..protocols.CAN import DenaliMessage, DenaliChannels +from .msg_ids import MsgIds +from ..protocols.CAN import CanMessage, CanChannels from ..utils.checks import check_broadcast_interval_override_ms from ..utils.conversions import integer_to_bytearray @@ -29,7 +29,7 @@ ms: int, reset: int, msg_id: MsgIds, - channel_id: DenaliChannels, + channel_id: CanChannels, module_name: str, logger: Logger, can_interface) -> int: @@ -42,7 +42,7 @@ @param ms: integer - interval (in ms) to override with @param reset: integer - 1 to reset a previous override, 0 to override @param msg_id: MsgIds - The override's msg id - @param channel_id: DenaliChannels - The override's channel id + @param channel_id: Channels - The override's channel id @param module_name: str - The module's name to put into the debug prints @param logger: str - The logger for printing debug and error messages @param can_interface: The can_interface @@ -71,7 +71,7 @@ payload: bytearray, reset: int, msg_id: MsgIds, - channel_id: DenaliChannels, + channel_id: CanChannels, entity_name: str, override_text: str, logger: Logger, @@ -84,14 +84,14 @@ @param payload: bytearray - the actual payload @param reset: integer - 1 to reset a previous override, 0 to override @param msg_id: MsgIds - The override's msg id - @param channel_id: DenaliChannels - The override's channel id + @param channel_id: Channels - The override's channel id @param entity_name: str - The entity's name that will be overriden to put into the debug prints @param override_text: str - The value to be overriden with to put into the debug prints @param logger: str - The logger for printing debug and error messages @param can_interface: The can_interface @return: 1 if successful, zero otherwise """ - message = DenaliMessage.build_message(channel_id=channel_id, + message = CanMessage.build_message(channel_id=channel_id, message_id=msg_id.value, payload=payload) @@ -107,13 +107,13 @@ if received_message is not None: if payload is None or reset is None: str_res = f'not called' if reset is not None and reset == RESET else 'Called' - logger.debug(f'{entity_name} is {str_res} {str(received_message["message"][DenaliMessage.PAYLOAD_START_INDEX])}') + logger.debug(f'{entity_name} is {str_res} {str(received_message["message"][CanMessage.PAYLOAD_START_INDEX])}') else: str_res = f'its default value' if reset == RESET else override_text - logger.debug(f'{entity_name} is overridden to {str_res} {str(received_message["message"][DenaliMessage.PAYLOAD_START_INDEX])}') + logger.debug(f'{entity_name} is overridden to {str_res} {str(received_message["message"][CanMessage.PAYLOAD_START_INDEX])}') # response payload is OK or not OK - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + return received_message['message'][CanMessage.PAYLOAD_START_INDEX] else: logger.error("Timeout!!!!") return False Index: leahi_dialin/common/td_defs.py =================================================================== diff -u -r3ca042a93b178bc02327efec6cf08dd2348f9a8e -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/td_defs.py (.../td_defs.py) (revision 3ca042a93b178bc02327efec6cf08dd2348f9a8e) +++ leahi_dialin/common/td_defs.py (.../td_defs.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -16,7 +16,7 @@ from enum import unique -from ..utils.base import DialinEnum +from ..utils.enums import DialinEnum # ================================================== Enum Creators: Operations Lvl 1 ================================================== @@ -323,34 +323,32 @@ # TDPreTreatmentModesStates.TD_PRE_TREATMENT_SELF_TEST_DRY_STATE sub states @unique class TDPreTreaDrySelfTestStates(DialinEnum): - DRY_SELF_TESTS_START_STATE = 0 # Dry self-tests starting state - DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE = 1 # Wait for door to close before executing self-tests - DRY_SELF_TESTS_USED_CARTRIDGE_CHECK_STATE = 2 # Used cartridge check dry self-test state - DRY_SELF_TESTS_CARTRIDGE_LOADED_CHECK_STATE = 3 # Cartridge loaded check dry self-test state - DRY_SELF_TESTS_TUBE_SET_AUTHENTICATION_STATE = 4 # Tube set authentication state - DRY_SELF_TESTS_SYRINGE_PUMP_SEEK_STATE = 5 # Syringe pump seek state - DRY_SELF_TESTS_PRESSURE_SENSORS_NORMAL_SETUP_STATE = 6 # Pressure sensor normal setup state - DRY_SELF_TESTS_PRESSURE_VENOUS_SETUP_STATE = 7 # Venous pressure sensor dry self-test setup valves and pump state - DRY_SELF_TESTS_VENOUS_PRESSURE_STABILIZATION_STATE = 8 # Venous pressure verify pressure stability state - DRY_SELF_TESTS_VENOUS_PRESSURE_LEAK_CHECK_STATE = 9 # Venous pressure leak check state - DRY_SELF_TESTS_VENOUS_PRESSURE_RELIEF_STATE = 10 # Venous pressure relief state - DRY_SELF_TESTS_PRESSURE_ARTERIAL_SETUP_STATE = 11 # Arterial pressure sensor dry self-test setup valves and pump state - DRY_SELF_TESTS_ARTERIAL_PRESSURE_STABILIZATION_STATE = 12 # Arterial pressure verify pressure stability state - DRY_SELF_TESTS_ARTERIAL_PRESSURE_LEAK_CHECK_STATE = 13 # Arterial pressure leak check state - DRY_SELF_TESTS_ARTERIAL_PRESSURE_RELIEF_STATE = 14 # Arterial pressure relief state - DRY_SELF_TESTS_SYRINGE_PRIME_STATE = 15 # Prime syringe pump state - DRY_SELF_TESTS_SYRINGE_PUMP_OCCLUSION_CHECK_STATE = 16 # Occlusion detection state - DRY_SELF_TESTS_COMPLETE_STATE = 17 # Dry self-test complete state - DRY_SELF_TESTS_STOPPED_STATE = 18 # Dry self-test stopped state - NUM_OF_DRY_SELF_TESTS_STATES = 19 # Number of dry self-tests states + DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE = 0 # Wait for door to close before executing self-tests + DRY_SELF_TESTS_USED_TUBING_CHECK_STATE = 1 # Used tubing check dry self-test state + DRY_SELF_TESTS_TUBING_LOADED_CHECK_STATE = 2 # Tubing loaded check dry self-test state + DRY_SELF_TESTS_TUBING_SET_AUTHENTICATION_STATE = 3 # Tubing set authentication state + DRY_SELF_TESTS_SYRINGE_PUMP_SEEK_STATE = 4 # Syringe pump seek state + DRY_SELF_TESTS_PRESSURE_SENSORS_NORMAL_SETUP_STATE = 5 # Pressure sensor normal setup state + DRY_SELF_TESTS_PRESSURE_VENOUS_SETUP_STATE = 6 # Venous pressure sensor dry self-test setup valves and pump state + DRY_SELF_TESTS_VENOUS_PRESSURE_STABILIZATION_STATE = 7 # Venous pressure verify pressure stability state + DRY_SELF_TESTS_VENOUS_PRESSURE_LEAK_CHECK_STATE = 8 # Venous pressure leak check state + DRY_SELF_TESTS_VENOUS_PRESSURE_RELIEF_STATE = 9 # Venous pressure relief state + DRY_SELF_TESTS_PRESSURE_ARTERIAL_SETUP_STATE = 10 # Arterial pressure sensor dry self-test setup valves and pump state + DRY_SELF_TESTS_ARTERIAL_PRESSURE_STABILIZATION_STATE = 11 # Arterial pressure verify pressure stability state + DRY_SELF_TESTS_ARTERIAL_PRESSURE_LEAK_CHECK_STATE = 12 # Arterial pressure leak check state + DRY_SELF_TESTS_ARTERIAL_PRESSURE_RELIEF_STATE = 13 # Arterial pressure relief state + DRY_SELF_TESTS_SYRINGE_PRIME_STATE = 14 # Prime syringe pump state + DRY_SELF_TESTS_SYRINGE_PUMP_OCCLUSION_CHECK_STATE = 15 # Occlusion detection state + DRY_SELF_TESTS_COMPLETE_STATE = 16 # Dry self-test complete state + DRY_SELF_TESTS_STOPPED_STATE = 17 # Dry self-test stopped state + NUM_OF_DRY_SELF_TESTS_STATES = 18 # Number of dry self-tests states TDPreTreaDrySelfTestStates._str_list = { # Official Name : Accepted strings - 'DRY_SELF_TESTS_START_STATE': ['start'], 'DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE': ['wait for front door to close', 'wait for door close'], - 'DRY_SELF_TESTS_USED_CARTRIDGE_CHECK_STATE': ['used cartridge check'], - 'DRY_SELF_TESTS_CARTRIDGE_LOADED_CHECK_STATE': ['cartridge loaded check'], - 'DRY_SELF_TESTS_TUBE_SET_AUTHENTICATION_STATE': ['tube set authentication', 'tube authentication'], + 'DRY_SELF_TESTS_USED_TUBING_CHECK_STATE': ['used tubing check'], + 'DRY_SELF_TESTS_TUBING_LOADED_CHECK_STATE': ['tubing loaded check'], + 'DRY_SELF_TESTS_TUBING_SET_AUTHENTICATION_STATE': ['tubing set authentication', 'tubing authentication'], 'DRY_SELF_TESTS_SYRINGE_PUMP_SEEK_STATE': ['srynge pump seek', 'pump seek'], 'DRY_SELF_TESTS_PRESSURE_SENSORS_NORMAL_SETUP_STATE': ['pressure sensors normal setup'], 'DRY_SELF_TESTS_PRESSURE_VENOUS_SETUP_STATE': ['pressure venous setup', 'venous setup'], @@ -784,27 +782,7 @@ } -@unique -class TDEventDataTypes(DialinEnum): - EVENT_DATA_TYPE_NONE = 0 # No Event Data Type - EVENT_DATA_TYPE_U32 = 1 # Unsigned 32bit Event Data Type - EVENT_DATA_TYPE_S32 = 2 # Signed 32bit Event Data Type - EVENT_DATA_TYPE_F32 = 3 # Float 32bit Event Data Type - EVENT_DATA_TYPE_BOOL = 4 # Boolean Event Data Type - NUM_OF_EVENT_DATA_TYPES = 5 # Number of Event Data Types -TDEventDataTypes._str_list = { - # Official Name : Accepted strings - 'EVENT_DATA_TYPE_NONE': ['none'], - 'EVENT_DATA_TYPE_U32': ['u32'], - 'EVENT_DATA_TYPE_S32': ['s32'], - 'EVENT_DATA_TYPE_F32': ['f32'], - 'EVENT_DATA_TYPE_BOOL': ['bool', 'boolean'], - 'NUM_OF_EVENT_DATA_TYPES': [], -} - - - # ================================================== Enum Creators: Names ================================================== @unique class TDAirPumpNames(DialinEnum): @@ -1133,18 +1111,20 @@ @unique class TDValvePositions(DialinEnum): VALVE_POSITION_NOT_IN_POSITION = 0 # Valve not in Position - VALVE_POSITION_A_INSERT_EJECT = 1 # Insert/Eject Valve Position - VALVE_POSITION_B_OPEN = 2 # Open Valve Position - VALVE_POSITION_C_CLOSE = 3 # Closed Valve Position + VALVE_POSITION_A_INSERT_LOAD = 1 # Insert/Eject Valve Position + VALVE_POSITION_B_OPEN_BLOOD = 2 # Open Valve Position + VALVE_POSITION_C_CLOSE_SALINE = 3 # Closed Valve Position VALVE_POSITION_C_PARTIAL_CLOSE = 4 # Partial Close Valve Position + NUM_OF_VALVE_POSITIONS = 5 # Partial Close Valve Position TDValvePositions._str_list = { # Official Name : Accepted strings 'VALVE_POSITION_NOT_IN_POSITION': ['not in position'], - 'VALVE_POSITION_A_INSERT_EJECT': ['a', 'insert/eject', 'insert', 'eject'], - 'VALVE_POSITION_B_OPEN': ['b', 'open'], - 'VALVE_POSITION_C_CLOSE': ['c', 'close', 'closed'], + 'VALVE_POSITION_A_INSERT_LOAD': ['a', 'insert/eject', 'insert', 'eject', 'load'], + 'VALVE_POSITION_B_OPEN_BLOOD': ['b', 'open', 'blood'], + 'VALVE_POSITION_C_CLOSE_SALINE': ['c', 'close', 'closed', 'saline'], 'VALVE_POSITION_C_PARTIAL_CLOSE': [], + 'NUM_OF_VALVE_POSITIONS': [], } @@ -1174,51 +1154,106 @@ } +# ================================================== Enum Creators: Treatment Parameters ================================================== @unique class TDTreatmentParameters(DialinEnum): - TREATMENT_PARAM_BLOOD_FLOW = 0 # Blood flow rate (in mL/min) - TREATMENT_PARAM_DIALYSATE_FLOW = 1 # Dialysate flow rate (in mL/min) - TREATMENT_PARAM_TREATMENT_DURATION = 2 # Treatment duration (in minutes) - TREATMENT_PARAM_SALINE_BOLUS_VOLUME = 3 # Saline bolus volume (in mL) - TREATMENT_PARAM_HEPARIN_STOP_TIME = 4 # Heparin stop time (in minutes) - TREATMENT_PARAM_HEPARIN_TYPE = 5 # Heparin type (enum) - TREATMENT_PARAM_ACID_CONCENTRATE = 6 # Acid concentrate type (enum) - TREATMENT_PARAM_BICARB_CONCENTRATE = 7 # Bicarbonate concentrate type (enum) - TREATMENT_PARAM_DIALYZER_TYPE = 8 # Dialysate type (enum) - TREATMENT_PARAM_BP_MEAS_INTERVAL = 9 # Blood pressure measurement interval (in minutes) - TREATMENT_PARAM_RINSEBACK_FLOW_RATE = 10 # Rinseback flow rate (in mL/min) - TREATMENT_PARAM_RINSEBACK_VOLUME = 11 # Rinseback volume (in mL) - TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW = 12 # Arterial pressure alarm limit window (in mmHg) - TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW = 13 # Venous pressure alarm limit window (in mmHg) - TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC = 14 # Venous pressure alarm limit asymmetric (in mmHg) - TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW = 15 # TMP alarm limit window (in mmHg) - TREATMENT_PARAM_DIALYSATE_TEMPERATURE = 16 # Dialysate temperature (in degC) - TREATMENT_PARAM_HEPARIN_DISPENSE_RATE = 17 # Heparin dispense rate (in mL/hr) - TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME = 18 # Heparin bolus volume (in mL) - TREATMENT_PARAM_UF_VOLUME = 19 # Ultrafiltration volume (in liters) - provided separately by UI - NUM_OF_TREATMENT_PARAMS = 20 # Total number of treatment parameters + TREATMENT_PARAM_TREATMENT_MODALITY = 0 # Treatment modality type (enum) + TREATMENT_PARAM_HDF_DILUTION = 1 # HDF dilution (enum) + TREATMENT_PARAM_BLOOD_FLOW = 2 # Blood flow rate (in mL/min) + TREATMENT_PARAM_DIALYSATE_FLOW = 3 # Dialysate flow rate (in mL/min) + TREATMENT_PARAM_TREATMENT_DURATION = 4 # Treatment duration (in minutes) + TREATMENT_PARAM_HEPARIN_DELIVERY_DURATION = 5 # Heparin delivery duration (in minutes) + TREATMENT_PARAM_HEPARIN_TYPE = 6 # Heparin type (enum) + TREATMENT_PARAM_DRY_BICARB_CART_SIZE = 7 # Dry bicarb cartrideg size (enum) + TREATMENT_PARAM_SODIUM = 8 # Sodium concentrate (mEq/L) + TREATMENT_PARAM_BICARB_CONCENTRATE = 9 # Bicarbonate concentrate (mEq/L) + TREATMENT_PARAM_DIALYZER_TYPE = 10 # Dialysate type (enum) + TREATMENT_PARAM_FLUID_BOLUS_VOLUME = 11 # Fluid bolus volume (in mL) + TREATMENT_PARAM_BP_MEAS_INTERVAL = 12 # Blood pressure measurement interval (in minutes) + TREATMENT_PARAM_PRIME_RINSEBACK_VOLUME = 13 # Prime rinseback volume (in mL) + TREATMENT_PARAM_HEPATITIS_B = 14 # Hepatitis B status (enum) + TREATMENT_PARAM_ACID_CONCENTRATE = 15 # Acid concentrate type (enum) + TREATMENT_PARAM_SUBST_FLUID_VOLUME = 16 # Substitution fluid volume (in liters) + TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME = 17 # Heparin bolus volume (in mL) + TREATMENT_PARAM_HEPARIN_DELIVERY_RATE = 18 # Heparin delivery rate (in mL/hr) + TREATMENT_PARAM_DIALYSATE_TEMPERATURE = 19 # Dialysate temperature (in degC) + TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR = 20 # Acid concentrate conversion factor + TREATMENT_PARAM_PRE_WEIGHT = 21 # Weight of patient prior to treatment + TREATMENT_PARAM_EST_TARGET_WEIGHT = 22 # Estimated target weight of patient post treatment + TREATMENT_PARAM_UF_VOLUME = 23 # Ultrafiltration volume (in liters) - provided separately by UI + NUM_OF_TREATMENT_PARAMS = 24 # Total number of treatment parameters TDTreatmentParameters._str_list = { # Official Name : Accepted strings + 'TREATMENT_PARAM_TREATMENT_MODALITY': ['treatment modality', 'treatment type'], + 'TREATMENT_PARAM_HDF_DILUTION': ['hdf dilution'], 'TREATMENT_PARAM_BLOOD_FLOW': ['blood flow', 'blood flow rate'], 'TREATMENT_PARAM_DIALYSATE_FLOW': ['sialysate flow', 'dialysate flow rate'], 'TREATMENT_PARAM_TREATMENT_DURATION': ['treatment duration'], - 'TREATMENT_PARAM_SALINE_BOLUS_VOLUME': ['saline bolus volume'], - 'TREATMENT_PARAM_HEPARIN_STOP_TIME': ['heparin stop time'], + 'TREATMENT_PARAM_HEPARIN_DELIVERY_DURATION': ['heparin delivery duration', 'heparin duration'], 'TREATMENT_PARAM_HEPARIN_TYPE': ['heparin', 'heparin type'], 'TREATMENT_PARAM_ACID_CONCENTRATE': ['acid', 'acid concentrate'], 'TREATMENT_PARAM_BICARB_CONCENTRATE': ['bicarb', 'bicarb concentrate'], 'TREATMENT_PARAM_DIALYZER_TYPE': ['dialyzer', 'dialyzer type'], + 'TREATMENT_PARAM_FLUID_BOLUS_VOLUME': ['fluid bolus volume', 'fluid bolus'], 'TREATMENT_PARAM_BP_MEAS_INTERVAL': ['blood pressure measure interval', 'bp measure interval'], - 'TREATMENT_PARAM_RINSEBACK_FLOW_RATE': ['rinseback flow rate'], - 'TREATMENT_PARAM_RINSEBACK_VOLUME': ['rinseback volume'], - 'TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW': ['arterial pressure limit window'], - 'TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW': ['venous pressure limit window'], - 'TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC': ['venous asymmetric pressure limit'], - 'TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW': ['tmp pres limit window'], - 'TREATMENT_PARAM_DIALYSATE_TEMPERATURE': ['dialysate temperature'], - 'TREATMENT_PARAM_HEPARIN_DISPENSE_RATE': ['heparin dispense rate'], + 'TREATMENT_PARAM_PRIME_RINSEBACK_VOLUME': ['rinseback volume', 'prime rinseback volume'], + 'TREATMENT_PARAM_HEPATITIS_B': ['hepatitis', 'hepatitis b'], + 'TREATMENT_PARAM_SUBST_FLUID_VOLUME': ['subst fluid volume', 'substitute fluid volume'], 'TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME': ['heparin volume'], + 'TREATMENT_PARAM_HEPARIN_DELIVERY_RATE': ['heparin delivery rate'], + 'TREATMENT_PARAM_DIALYSATE_TEMPERATURE': ['dialysate temperature'], + 'TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR': ['acid concentrate conv factor', 'acid concentrate conversion factor'], + 'TREATMENT_PARAM_PRE_WEIGHT': ['pre weight'], + 'TREATMENT_PARAM_EST_TARGET_WEIGHT': ['est target weight', 'target weight', 'estimated target weight'], 'TREATMENT_PARAM_UF_VOLUME': ['uf volume', 'ultrafiltration volume'], 'NUM_OF_TREATMENT_PARAMS': [], } + + +@unique +class TDTreatmentModalityTypes(DialinEnum): + HD = 0 # Treatment modality Hemodialysis + HDF = 1 # Treatment modality Hemodiafiltration + NUM_OF_TREATMENT_MODALITY_TYPES = 2 # Total number of treatment modality types + +TDTreatmentModalityTypes._str_list = { + # Official Name : Accepted strings + 'HD': ['treatment modality hd', 'hemodialysis'], + 'HDF': ['treatment modality hdf', 'hemodiafiltration'], + 'NUM_OF_TREATMENT_MODALITY_TYPES': [], +} + + +@unique +class TDTreatmentHDFDilution(DialinEnum): + HDF_NOT_APPLICABLE = 0 # Treatment HDF not applicable + HDF_PRE_DILUTION = 1 # Treatment HDF pre-dilution + HDF_POST_DILUTION = 2 # Treatment HDF psot-dilution + NUM_OF_TREATMENT_HDF_TYPES = 3 # Total number of HDF treatment dilution types + +TDTreatmentHDFDilution._str_list = { + # Official Name : Accepted strings + 'HDF_NOT_APPLICABLE': ['n/a', 'not applicable'], + 'HDF_PRE_DILUTION': ['pre dilution'], + 'HDF_POST_DILUTION': ['post dilution'], + 'NUM_OF_TREATMENT_HDF_TYPES': [], +} + + +@unique +class TDTreatmentHepatitisB(DialinEnum): + HEPATITIS_B_NOT_APPLICABLE = 0 # Hepatitis B status not applicable + HEPATITIS_B_UNKNOWN = 1 # Hepatitis B status unknowm + HEPATITIS_B_POSITIVE = 2 # Hepatitis B status positive + HEPATITIS_B_NEGATIVE = 3 # Hepatitis B status negative + NUM_OF_HEPATITIS_B_STATUS = 4 # Total number of Hepatitis status types + +TDTreatmentHepatitisB._str_list = { + # Official Name : Accepted strings + 'HEPATITIS_B_NOT_APPLICABLE': ['n/a', 'not applicable'], + 'HEPATITIS_B_UNKNOWN': ['unknown'], + 'HEPATITIS_B_POSITIVE': ['positive'], + 'HEPATITIS_B_NEGATIVE': ['negative'], + 'NUM_OF_HEPATITIS_B_STATUS': [], +} Index: leahi_dialin/common/test_config_defs.py =================================================================== diff -u -rd0391ac112347f9c7021b0995bd4b22657fe9f66 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/common/test_config_defs.py (.../test_config_defs.py) (revision d0391ac112347f9c7021b0995bd4b22657fe9f66) +++ leahi_dialin/common/test_config_defs.py (.../test_config_defs.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -15,7 +15,7 @@ ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from ..utils.enums import DialinEnum @unique @@ -44,6 +44,5 @@ TEST_CONFIG_ENABLE_BETA_1_HW = 0 # Test configuration using Beta 1 Hardware TEST_CONFIG_SKIP_BLOOD_PRIME = 1 # Test configuration to skip blood priming TEST_CONFIG_TD_NO_CARTRIDGE = 2 # Test configuration to operate without cartridge - TEST_CONFIG_TD_DISABLE_FAULT_MODE_FROM_DD_FP = 3 # Test configuration to disable TD going to Fault mode because of alarms from other subsystems - NUM_OF_TEST_CONFIGS = 4 # Number of Test Configs + NUM_OF_TEST_CONFIGS = 3 # Number of Test Configs TDTestConfigOptions._str_list = {} Index: leahi_dialin/dd/dialysate_delivery.py =================================================================== diff -u -rd0391ac112347f9c7021b0995bd4b22657fe9f66 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/dialysate_delivery.py (.../dialysate_delivery.py) (revision d0391ac112347f9c7021b0995bd4b22657fe9f66) +++ leahi_dialin/dd/dialysate_delivery.py (.../dialysate_delivery.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,14 +8,13 @@ # @file dialysate_delivery.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct - +# Project imports from .modules.alarms import DDAlarms from .modules.balancing_chamber import DDBalancingChamber from .modules.blood_leak import DDBloodLeak @@ -44,10 +43,13 @@ from ..common.constants import NO_RESET from ..common import dd_enum_repository -from ..common.msg_defs import MsgIds, MsgFieldPositions, MsgFieldPositionsFWVersions +from ..common.generic_defs import DataTypes +from ..common.msg_defs import MsgFieldPositions +from ..common.msg_ids import MsgIds from ..common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from ..protocols.CAN import DenaliMessage, DenaliCanMessenger, DenaliChannels -from ..utils.base import AbstractSubSystem, publish, LogManager +from ..protocols.CAN import CanMessage, CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish, LogManager from ..utils.conversions import integer_to_bytearray, bytearray_to_byte @@ -81,26 +83,24 @@ self.logger = self._log_manager.logger # Create listener - self.can_interface = DenaliCanMessenger(can_interface=can_interface, + self.can_interface = CanMessenger(can_interface=can_interface, logger=self.logger) self.can_interface.start() self.callback_id = None # register handler for DD operation mode broadcast messages if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_op_mode_data = MsgIds.MSG_ID_DD_OP_MODE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_op_mode_data, - self._handler_dd_op_mode_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_OP_MODE_DATA.value, + function = self._handler_dd_op_mode_sync) - self.msg_id_dd_version_response = MsgIds.MSG_ID_DD_VERSION_RESPONSE.value - self.can_interface.register_receiving_publication_function(channel_id, - self.msg_id_dd_version_response, - self._handler_dd_version_response_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_VERSION_RESPONSE.value, + function = self._handler_dd_version_response_sync) - self.msg_id_dd_debug_event = MsgIds.MSG_ID_DD_DEBUG_EVENT.value - self.can_interface.register_receiving_publication_function(channel_id, - self.msg_id_dd_debug_event, - self._handler_dd_debug_event_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_DEBUG_EVENT.value, + function = self._handler_dd_debug_event_sync) + # Dialin will send a login message during construction. This is for the leahi subsystems to start # publishing CAN data when there is no UI connected as the UI typically does this job. self.cmd_log_in_to_dd() @@ -188,13 +188,12 @@ @param message: published DD operation mode broadcast message @return: None """ - mode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - smode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + msg_list = [] + msg_list.append(('self.dd_operation_mode', DataTypes.U32)) + msg_list.append(('self.dd_operation_sub_mode', DataTypes.U32)) - self.dd_operation_mode = mode[0] - self.dd_operation_sub_mode = smode[0] + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_op_mode_timestamp = timestamp @@ -207,33 +206,56 @@ @return: None if not successful, the version string if unpacked successfully """ - major = struct.unpack(' 0 for each in [result['major'], result['minor'], result['micro'], result['build'], result['compatibility']]]): + self.dd_version = f"v{result['major']}.{result['minor']}.{result['micro']}-{result['build']}.{result['compatibility']}" + self.logger.debug(f'DD VERSION: {self.dd_version}') + + if all([len(each) > 0 for each in [result['fpga_id'], result['fpga_major'], result['fpga_minor'], result['fpga_lab']]]): + self.dd_fpga_version = f"v{result['fpga_id']}.{result['fpga_major']}.{result['fpga_minor']}-{result['fpga_lab']}" + self.logger.debug(f'DD FPGA VERSION: {self.dd_fpga_version}') + + """ + major = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray( message['message'][MsgFieldPositionsFWVersions.START_POS_MAJOR:MsgFieldPositionsFWVersions.END_POS_MAJOR])) - minor = struct.unpack(' 0 for each in [major, minor, micro, build, compatibility]]): self.dd_version = f"v{major[0]}.{minor[0]}.{micro[0]}-{build[0]}.{compatibility[0]}" self.logger.debug(f"DD VERSION: {self.dd_version}") - + if all([len(each) > 0 for each in [fpga_id, fpga_major, fpga_minor, fpga_lab]]): self.dd_fpga_version = f"v{fpga_id[0]}.{fpga_major[0]}.{fpga_minor[0]}-{fpga_lab[0]}" self.logger.debug(f"DD FPGA VERSION: {self.dd_fpga_version}") - + """ self.dd_version_response_timestamp = timestamp @@ -251,7 +273,7 @@ # return cmd_generic_broadcast_interval_override( # ms = ms, # reset = reset, - # channel_id = DenaliChannels.dialin_to_dd_ch_id, + # channel_id = Channels.dialin_to_dd_ch_id, # msg_id = MsgIds.MSG_ID_DD_OP_MODE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, # module_name = 'TD Operation Mode', # logger = self.logger, @@ -266,7 +288,7 @@ @param resend: (bool) if False (default), try to login once. Otherwise, tries to login indefinitely @return: 1 if logged in, 0 if log in failed """ - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, + message = CanMessage.build_message(channel_id=CanChannels.dialin_to_dd_ch_id, message_id=MsgIds.MSG_ID_DD_TESTER_LOGIN_REQUEST.value, payload=list(map(int, map(ord, self.DD_LOGIN_PASSWORD)))) @@ -276,14 +298,14 @@ received_message = self.can_interface.send(message, resend=resend) if received_message is not None: - if received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] == 1: + if received_message['message'][CanMessage.PAYLOAD_START_INDEX] == 1: self.logger.debug("Success: Logged In") self.dd_set_logged_in_status(True) #self._send_dd_checkin_message() # Timer starts interval first #self.can_interface.transmit_interval_dictionary[self.callback_id].start() else: self.logger.debug("Failure: Log In Failed.") - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + return received_message['message'][CanMessage.PAYLOAD_START_INDEX] else: self.logger.debug("Login Timeout!!!!") return False @@ -306,7 +328,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_SET_OPERATION_MODE_OVERRIDE_REQUEST, entity_name = 'DD Operation Mode', override_text = dd_enum_repository.DDOpModes(new_mode).name, @@ -325,7 +347,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_SOFTWARE_RESET_REQUEST, entity_name = 'DD Software Reset', override_text = '', @@ -350,7 +372,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_SET_OPERATION_MODE_OVERRIDE_REQUEST, entity_name = 'DD Safety Shutdown', override_text = str(active), Index: leahi_dialin/dd/modules/alarms.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/alarms.py (.../alarms.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/alarms.py (.../alarms.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,26 @@ # @file alarms.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Quang Nguyen # @date (original) 02-Sep-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +import struct +# Project imports from leahi_dialin.common.alarm_defs import AlarmList from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanMessage, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish from leahi_dialin.utils.conversions import integer_to_bytearray @@ -32,21 +36,21 @@ DD interface containing alarm related commands. """ _ALARM_ID_MAX_ALARMS = 500 - START_POS_ALARM_ID = DenaliMessage.PAYLOAD_START_INDEX + START_POS_ALARM_ID = CanMessage.PAYLOAD_START_INDEX END_POS_ALARM_ID = START_POS_ALARM_ID + 2 - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_alarm_broadcast_ch_id - self.msg_id_dd_alarm_triggered = MsgIds.MSG_ID_ALARM_TRIGGERED.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_alarm_triggered, self._handler_alarm_triggered) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_alarm_broadcast_ch_id, + message_id = MsgIds.MSG_ID_ALARM_TRIGGERED.value, + function = self._handler_alarm_triggered) self.alarm_safety_shutdown_status = 0.0 #: The Alarm's Safety Shutdown status value @@ -66,26 +70,7 @@ self.last_alarm_data_1 = 0.0 #: The last Alarm's data part 1 self.last_alarm_data_2 = 0.0 #: The last Alarm's data part 2 - # alarm information - self.alarm_data_type = dict() #: The Alarm's Data type in dict format - # Loop through the list of the event data type enum and update the dictionary - for data_type in dd_enum_repository.DDEventDataTypes: - event_data_type = dd_enum_repository.DDEventDataTypes(data_type).name - struct_unpack_type = None - - # If U32 is in the data type enum (i.e. EVENT_DATA_TYPE_U32), then the key is the enum and the value is - # the corresponding format in the python struct - if 'U32' in event_data_type or 'BOOL' in event_data_type or 'NONE' in event_data_type: - struct_unpack_type = 'I' - elif 'S32' in event_data_type: - struct_unpack_type = 'i' - elif 'F32' in event_data_type: - struct_unpack_type = 'f' - - self.alarm_data_type[event_data_type] = struct_unpack_type - - @publish(["msg_id_dd_alarm_triggered", "alarm_states", "alarm_conditions", "alarm_data", "alarm_priorities", "alarm_ranks", "alarm_clear_top_only_flags", "dd_alarm_triggered_timestamp"]) def _handler_alarm_triggered(self, message, timestamp = 0.0): @@ -95,35 +80,40 @@ @param message: published DD alarm activation message @return: none """ - self.logger.debug("Alarm activated!") - alarm_id = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + result = {} + msg_list = [] + msg_list.append(('alarm_id', DataTypes.U32)) + msg_list.append(('data_typ_1', DataTypes.U32)) + msg_list.append(('data_1', 'data_typ_1')) + msg_list.append(('data_typ_2', DataTypes.U32)) + msg_list.append(('data_2', 'data_typ_2')) + msg_list.append(('priority', DataTypes.U32)) + msg_list.append(('rank', DataTypes.U32)) + msg_list.append(('clr_top_only', DataTypes.U32)) - data_typ_1 = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) - # Get the corresponding structure format - struct_data_type_1 = self.alarm_data_type[dd_enum_repository.DDEventDataTypes(data_typ_1[0]).name] - # Get the data value by unpacking the data type - data_1 = struct.unpack(struct_data_type_1, bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + i = 1 + for msg_detail in msg_list: + start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') + end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') + if isinstance(msg_detail[1], DataTypes): + unpack_attrib = msg_detail[1].unpack_attrib() + else: + unpack_attrib = DataTypes(result[msg_detail[1]]).unpack_attrib() + + value = struct.unpack(unpack_attrib, bytearray(message['message'][start_pos:end_pos]))[0] + result[msg_detail[0]] = value + i += 1 - data_typ_2 = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) - # Get the corresponding structure format - struct_data_type_2 = self.alarm_data_type[dd_enum_repository.DDEventDataTypes(data_typ_2[0]).name] - # Get the data value by unpacking the data type - data_2 = struct.unpack(struct_data_type_2, bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) - - priority = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) - rank = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) - clr_top_only = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) - - self.logger.debug("Alarm ID: %d %d %d" % (alarm_id[0], data_1[0], data_2[0])) - self.alarm_states[alarm_id[0]] = True - self.alarm_conditions[alarm_id[0]] = True - self.alarm_priorities[alarm_id[0]] = priority[0] - self.alarm_ranks[alarm_id[0]] = rank[0] - self.alarm_clear_top_only_flags[alarm_id[0]] = clr_top_only[0] - self.alarm_data[alarm_id[0]] = [data_1[0], data_2[0]] - self.last_alarm_triggered = alarm_id[0] - self.last_alarm_data_1 = data_1[0] - self.last_alarm_data_2 = data_2[0] + self.logger.debug("Alarm ID: %d %d %d" % (result['alarm_id'], result['data_1'], result['data_2'])) + self.alarm_states[result['alarm_id']] = True + self.alarm_conditions[result['alarm_id']] = True + self.alarm_priorities[result['alarm_id']] = result['priority'] + self.alarm_ranks[result['alarm_id']] = result['rank'] + self.alarm_clear_top_only_flags[result['alarm_id']] = result['clr_top_only'] + self.alarm_data[result['alarm_id']] = [result['data_1'], result['data_2']] + self.last_alarm_triggered = result['alarm_id'] + self.last_alarm_data_1 = result['data_1'] + self.last_alarm_data_2 = result['data_2'] self.dd_alarm_triggered_timestamp = timestamp @@ -135,7 +125,7 @@ @param message: published DD alarm info message @return: none """ - self.alarm_safety_shutdown_status = struct.unpack('I', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + self.alarm_safety_shutdown_status = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) self.dd_alarm_info_timestamp = timestamp @@ -181,7 +171,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = '', module_name = 'DD Alarm state', logger = self.logger, @@ -205,7 +195,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = '', module_name = 'DD Alarm info', logger = self.logger, @@ -229,7 +219,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = '', module_name = 'DD Alarm cleared', logger = self.logger, @@ -253,7 +243,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = '', module_name = 'DD Alarm condition cleared', logger = self.logger, @@ -283,7 +273,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_ALARM_STATE_OVERRIDE_REQUEST, entity_name = f'DD {alarm_name} Alarm state', override_text = f'{state_name}', Index: leahi_dialin/dd/modules/balancing_chamber.py =================================================================== diff -u -r039d9f5987f79862f10c026e6ca103d8139be1fa -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/balancing_chamber.py (.../balancing_chamber.py) (revision 039d9f5987f79862f10c026e6ca103d8139be1fa) +++ leahi_dialin/dd/modules/balancing_chamber.py (.../balancing_chamber.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file balancing_chamber.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,20 +35,19 @@ Dialysate Delivery (DD) Dialin API sub-class for Balancing Chamber related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_bal_chamber_data = MsgIds.MSG_ID_DD_BAL_CHAMBER_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_bal_chamber_data, - self._handler_balancing_chamber_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_BAL_CHAMBER_DATA.value, + function = self._handler_balancing_chamber_sync) self.execution_state = 0 #: The Execution state self.switching_state = 0 #: The Switching state @@ -73,25 +75,19 @@ @param message: published balancing chamber data message @return: None """ - self.execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.switching_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.switching_frequency = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.switching_period = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.bal_chamber_fill_in_progress = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.current_bal_chamber_switching_counter = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.is_pressure_stabilized_during_fill = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] - self.bal_chamber_switch_only_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] - self.is_bal_chamber_switching_active = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] - + msg_list = [] + msg_list.append(('self.execution_state', DataTypes.U32)) + msg_list.append(('self.switching_state', DataTypes.U32)) + msg_list.append(('self.switching_frequency', DataTypes.F32)) + msg_list.append(('self.switching_period', DataTypes.U32)) + msg_list.append(('self.bal_chamber_fill_in_progress', DataTypes.U32)) + msg_list.append(('self.current_bal_chamber_switching_counter', DataTypes.U32)) + msg_list.append(('self.is_pressure_stabilized_during_fill', DataTypes.U32)) + msg_list.append(('self.bal_chamber_switch_only_state', DataTypes.U32)) + msg_list.append(('self.is_bal_chamber_switching_active', DataTypes.U32)) + + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_bal_chamber_timestamp = timestamp @@ -109,7 +105,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BAL_CHAMBER_DATA_PUBLISH_OVERRIDE_REQUEST, module_name = 'DD Balancing Chamber', logger = self.logger, @@ -133,7 +129,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BAL_CHAMBER_SWITCH_FREQ_OVERRIDE_REQUEST, entity_name = 'Switch Frequency', override_text = str(frequency), @@ -158,7 +154,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BC_SWITCH_ONLY_START_STOP_OVERRIDE_REQUEST, entity_name = 'Switch Only Start' if start_stop == 1 else 'Switch Only Stop', override_text = str(flow), @@ -183,7 +179,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_ACID_DOSING_VOLUME_OVERRIDE_REQUEST, entity_name = 'Acid dose volume', override_text = str(volume), @@ -208,7 +204,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BICARB_DOSING_VOLUME_OVERRIDE_REQUEST, entity_name = 'Bicarb dose volume', override_text = str(volume), Index: leahi_dialin/dd/modules/blood_leak.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/blood_leak.py (.../blood_leak.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/blood_leak.py (.../blood_leak.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,27 @@ # @file blood_leak.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Dara Navaei # @date (original) 21-Aug-2025 # ############################################################################ -import struct +# Module imports from logging import Logger from enum import unique +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish, DialinEnum +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, bytearray_to_byte, bytearray_to_integer, \ unsigned_short_to_bytearray, byte_to_bytearray, float_to_bytearray @@ -54,7 +59,7 @@ BLOOD_LEAK_DETECTED = 0 # Blood detected NO_BLOOD_LEAK_DETECTED = 1 # No blood detected - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ @param can_interface: Leahi Can Messenger object """ @@ -63,15 +68,13 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_blood_leak_data = MsgIds.MSG_ID_DD_BLOOD_LEAK_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_blood_leak_data, - self._handler_blood_leak_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_DATA.value, + function = self._handler_blood_leak_sync) - channel_id = DenaliChannels.dd_to_dialin_ch_id - self.msg_id_dd_send_blood_leak_emb_mode_response = MsgIds.MSG_ID_DD_SEND_BLOOD_LEAK_EMB_MODE_RESPONSE.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_send_blood_leak_emb_mode_response, - self._handler_blood_leak_emb_mode_cmd_resp) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_to_dialin_ch_id, + message_id = MsgIds.MSG_ID_DD_SEND_BLOOD_LEAK_EMB_MODE_RESPONSE.value, + function = self._handler_blood_leak_emb_mode_cmd_resp) self.dd_blood_leak_status_timestamp = 0.0 #: The timestamp of the last Blood Leak data message self.dd_blood_leak_emb_mode_response_timestamp = 0.0 #: The timestamp of the last Emb Mode response message @@ -140,29 +143,23 @@ @param message: published blood leak status message @return: None """ - self.blood_leak_status = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.blood_leak_state = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.blood_leak_error_persistent_ctr = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.blood_leak_serial_comm_state = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.blood_leak_intensity = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.blood_leak_blood_detect = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.blood_leak_intensity_moving_average = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] - self.blood_leak_time_elapsed_since_last_drift_zero_ms = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] - self.blood_leak_in_range_drift_status = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] - self.blood_leak_upper_range_drift_status = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10]))[0] - self.dd_blood_leak_status_timestamp = timestamp + msg_list =[] + msg_list.append(('self.blood_leak_status', DataTypes.U32)) + msg_list.append(('self.blood_leak_state', DataTypes.U32)) + msg_list.append(('self.blood_leak_error_persistent_ctr', DataTypes.U32)) + msg_list.append(('self.blood_leak_serial_comm_state', DataTypes.U32)) + msg_list.append(('self.blood_leak_intensity', DataTypes.U32)) + msg_list.append(('self.blood_leak_blood_detect', DataTypes.U32)) + msg_list.append(('self.blood_leak_intensity_moving_average', DataTypes.F32)) + msg_list.append(('self.blood_leak_time_elapsed_since_last_drift_zero_ms', DataTypes.U32)) + msg_list.append(('self.blood_leak_in_range_drift_status', DataTypes.U32)) + msg_list.append(('self.blood_leak_upper_range_drift_status', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) + self.dd_conductivity_timestamp = timestamp + @publish(['msg_id_dd_send_blood_leak_emb_mode_response', 'blood_leak_emb_mode_cmd_response', 'dd_blood_leak_emb_mode_response_timestamp']) def _handler_blood_leak_emb_mode_cmd_resp(self, message, timestamp=0.0): @@ -203,7 +200,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_DATA_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Blood Leak', logger = self.logger, @@ -227,7 +224,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_STATUS_OVERRIDE_REQUEST, entity_name = 'DD Blood Leak Detector state', override_text = str(detected), @@ -246,7 +243,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_ZERO_REQUEST, entity_name = 'DD Blood Leak Zeroing', override_text = 'Active', @@ -265,7 +262,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_SET_TO_EMBEDDED_MODE_REQUEST, entity_name = 'DD Blood Leak Embedded mode', override_text = 'Active', @@ -298,7 +295,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_SET_EMBEDDED_MODE_CMD_REQUEST, entity_name = f'DD {str(EmbModeCommands(command).name)} to the blood leak sensor', override_text = 'Active', @@ -326,7 +323,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_EMBEDDED_MODE_INFO_OVERRIDE_REQUEST, entity_name = f'DD Blood Leak {EmbModeCommands(cmd).name}', override_text = str(value_to_override), @@ -351,7 +348,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_INTENSITY_MOVING_AVERAGE_OVERRIDE_REQUEST, entity_name = 'DD Blood Leak Intensity Moving Average', override_text = str(value), @@ -379,7 +376,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BLOOD_LEAK_INTENSITY_MOVING_AVERAGE_OVERRIDE_REQUEST, entity_name = f'DD Blood Leak Zeroing {text} interval', override_text = f'{str(value_mins)} minutes', Index: leahi_dialin/dd/modules/concentrate_pump.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/concentrate_pump.py (.../concentrate_pump.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/concentrate_pump.py (.../concentrate_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file concentrate_pumps.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,20 +34,19 @@ Dialin API sub-class for concentrate pumps related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_conc_pump_data = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_conc_pump_data, - self._handler_concentrate_pumps_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_DATA.value, + function = self._handler_concentrate_pumps_sync) self.dd_concentrate_pump_timestamp = 0.0 #: The timestamp of the last message @@ -95,57 +97,36 @@ @param message: published concentrate pumps' data message @return: None """ - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_11:MsgFieldPositions.END_POS_FIELD_11])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_12:MsgFieldPositions.END_POS_FIELD_12])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_13:MsgFieldPositions.END_POS_FIELD_13])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_14:MsgFieldPositions.END_POS_FIELD_14])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.PARKED.name] = \ - True if struct.unpack('I', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_15:MsgFieldPositions.END_POS_FIELD_15]))[0] == 1 else False - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.PARKED.name] = \ - True if struct.unpack('I', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_16:MsgFieldPositions.END_POS_FIELD_16]))[0] == 1 else False - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name][dd_enum_repository.DDConcentratePumpAttributes.PARK_FAULT.name] = \ - True if struct.unpack('I', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_17:MsgFieldPositions.END_POS_FIELD_17]))[0] == 1 else False - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name][dd_enum_repository.DDConcentratePumpAttributes.PARK_FAULT.name] = \ - True if struct.unpack('I', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_18:MsgFieldPositions.END_POS_FIELD_18]))[0] == 1 else False - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_19:MsgFieldPositions.END_POS_FIELD_19])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_20:MsgFieldPositions.END_POS_FIELD_20])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_21:MsgFieldPositions.END_POS_FIELD_21])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_22:MsgFieldPositions.END_POS_FIELD_22])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_23:MsgFieldPositions.END_POS_FIELD_23])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_24:MsgFieldPositions.END_POS_FIELD_24])))[0] - self.dd_concentrate_pumps[dd_enum_repository.DDConcentratePumpNames.D76_UF.name][dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_25:MsgFieldPositions.END_POS_FIELD_25])))[0] + sensor_list =[] + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.PARKED.name, DataTypes.BOOL)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.PARKED.name, DataTypes.BOOL)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D11_CP1_ACID.name, dd_enum_repository.DDConcentratePumpAttributes.PARK_FAULT.name, DataTypes.BOOL)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D10_CP2_BICARB.name, dd_enum_repository.DDConcentratePumpAttributes.PARK_FAULT.name, DataTypes.BOOL)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.TARGET_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.CURRENT_SET_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.MEASURED_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.TARGET_REV_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.MEASURED_REV_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.CURRENT_STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConcentratePumpNames.D76_UF.name, dd_enum_repository.DDConcentratePumpAttributes.PULSE_US.name, DataTypes.F32)) + self.process_into_dict(dict_to_update = self.dd_concentrate_pumps, + decoder_list = sensor_list, + message = message) self.dd_concentrate_pump_timestamp = timestamp @@ -163,7 +144,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Concentrate Pump', logger = self.logger, @@ -186,7 +167,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_TARGET_SPEED_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDConcentratePumpNames(pump_id).name} Concentrate Pump target speed', override_text = str(speed), @@ -211,7 +192,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_MEASURED_SPEED_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDConcentratePumpNames(pump_id).name} Concentrate Pump measured speed', override_text = str(speed), @@ -236,7 +217,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_PARKED_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDConcentratePumpNames(pump_id).name} Concentrate Pump parked status', override_text = str(status), @@ -261,7 +242,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_PARK_FAULT_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDConcentratePumpNames(pump_id).name} Concentrate Pump parked fault state', override_text = str(status), @@ -281,7 +262,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_PARK_REQUEST_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDConcentratePumpNames(pump_id).name} Concentrate Pump to zeroing', override_text = 'Active', @@ -308,7 +289,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONCENTRATE_PUMPS_START_STOP_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDConcentratePumpNames(pump_id).name} Concentrate Pump speed', override_text = str(spd), Index: leahi_dialin/dd/modules/conductivity_sensors.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/conductivity_sensors.py (.../conductivity_sensors.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/conductivity_sensors.py (.../conductivity_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,24 @@ # @file conductivity_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Quang Nguyen # @date (original) 20-Jul-2020 # ############################################################################ - -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -34,20 +36,19 @@ Dialysate Delivery (DD) API sub-class for conductivity sensors related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_conductivity_data = MsgIds.MSG_ID_DD_CONDUCTIVITY_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_conductivity_data, - self._handler_conductivity_sensors_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_CONDUCTIVITY_DATA.value, + function = self._handler_conductivity_sensors_sync) self.dd_conductivity_timestamp = 0.0 #: The timestamp of the last message @@ -71,19 +72,15 @@ @return: None """ sensor_list =[] - sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D17_COND, 'f')) - sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D27_COND, 'f')) - sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D29_COND, 'f')) - sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D43_COND, 'f')) - sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D74_COND, 'f')) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D17_COND.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D27_COND.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D29_COND.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D43_COND.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D74_COND.name, DataTypes.F32)) - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - self.dd_conductivity[sensor[0].name] = struct.unpack(sensor[1],bytearray(message['message'][start_pos:end_pos]))[0] - i += 1 - + self.process_into_dict(dict_to_update = self.dd_conductivity, + decoder_list = sensor_list, + message = message) self.dd_conductivity_timestamp = timestamp @@ -101,7 +98,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONDUCTIVITY_SENSOR_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Conductivity Sensors', logger = self.logger, @@ -129,7 +126,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONDUCTIVITY_SENSOR_READINGS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Conductivity Sensor reading', override_text = f'{str(conductivity)} microsiemens/cm', @@ -158,7 +155,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONDUCTIVITY_SENSOR_READINGS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Conductivity Sensor read counter', override_text = str(counter), @@ -183,7 +180,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONDUCTIVITY_SENSOR_ERROR_COUNTER_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Conductivity Sensor error counter', override_text = str(counter), @@ -209,7 +206,7 @@ return cmd_generic_override( payload = payload, reset = 0, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_SET_CONDUCTIVITY_MODEL_REQUEST, entity_name = f'DD set Conductivity Model ID', override_text = str(model_id), @@ -239,7 +236,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_CONDUCTIVITY_SENSOR_RESISTANCE_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Conductivity Sensor resistance', override_text = f'{str(conductivity)} ohms', @@ -267,7 +264,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_FILTERED_COND_SENSOR_READINGS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Conductivity Sensor reading', override_text = f'{str(conductivity)} microsiemens/cm', Index: leahi_dialin/dd/modules/dd_test_configs.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/dd_test_configs.py (.../dd_test_configs.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/dd_test_configs.py (.../dd_test_configs.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,43 +8,45 @@ # @file dd_test_configs.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Jonny Paguio # @date (original) 20-Aug-2025 # ############################################################################ +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_override from leahi_dialin.common.test_config_defs import DDFPTestConfigOptions -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, bytearray_to_integer - class DDTestConfig(AbstractSubSystem): """ Dialysate Delivery (DD) Dialin API sub-class for setting and getting the test configurations. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_to_dialin_ch_id - self.msg_id_dd_send_test_config = MsgIds.MSG_ID_DD_SEND_TEST_CONFIGURATION.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_send_test_config, - self._handler_dd_test_config_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_to_dialin_ch_id, + message_id = MsgIds.MSG_ID_DD_SEND_TEST_CONFIGURATION.value, + function = self._handler_dd_test_config_sync) self.dd_test_configs_response_timestamp = 0.0 #: The timestamp of the latest message self.dd_test_configs = dict() #: The Test Config data in dictionary format @@ -90,14 +92,20 @@ @param reset: (int) 1 to reset a previous override, 0 to override @return: 1 if successful, zero otherwise """ + if reset == NO_RESET and \ + config in [DDFPTestConfigOptions.TEST_CONFIG_FP_ENABLE_DEFEATURE, DDFPTestConfigOptions.TEST_CONFIG_FP_ENABLE_BOOST_PUMP] and \ + (self.dd_test_configs[DDFPTestConfigOptions.TEST_CONFIG_FP_ENABLE_DEFEATURE] == 1 or self.dd_test_configs[DDFPTestConfigOptions.TEST_CONFIG_FP_ENABLE_BOOST_PUMP] == 1): + self.logger.info('Boost pump and defeatured test configs are mutually exclusive, can\'t activate them while on of them is active') + return 0 + reset_value = integer_to_bytearray(reset) c = integer_to_bytearray(config) payload = reset_value + c response = cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_SET_TEST_CONFIGURATION, entity_name = f'DD {DDFPTestConfigOptions(config).name} Test Config', override_text = 'Active', @@ -120,7 +128,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_GET_TEST_CONFIGURATION, entity_name = f'Get DD Test Configuration Record', override_text = 'Active', @@ -139,7 +147,7 @@ response = cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_RESET_ALL_TEST_CONFIGURATIONS, entity_name = f'Reset all DD & FP Test Configurations', override_text = 'Active', Index: leahi_dialin/dd/modules/dialysate_pump.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/dialysate_pump.py (.../dialysate_pump.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/dialysate_pump.py (.../dialysate_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file dialysate_pump.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -33,20 +36,19 @@ Dialysate Delivery (DD) Dialin API sub-class for dialysate pumps related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_dialysate_pumps_data = MsgIds.MSG_ID_DIALYSATE_PUMPS_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_dialysate_pumps_data, - self._handler_dialysate_pumps_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DIALYSATE_PUMPS_DATA.value, + function = self._handler_dialysate_pumps_sync) self.dd_dialysate_pump_timestamp = 0 #: The timestamp of the latest message @@ -88,56 +90,31 @@ @param message: published dialysate pumps' data message @return: None """ - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_RPM.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_RPM.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])))[0] + sensor_list =[] + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.TARGET_RPM.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.TARGET_RPM.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.CURRENT_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.CURRENT_SPEED.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.TARGET_PRESSURE.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.TARGET_PRESSURE.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_PRESSURE.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_PRESSURE.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_CURRENT.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_CURRENT.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.CONTROL.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.CONTROL.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.DIRECTION_ERROR_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.DIRECTION_ERROR_COUNT.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D12_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_DIRECTION.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDConductivitySensorNames.D48_PUMP.name, dd_enum_repository.DDConductivitySensorNames.MEASURED_DIRECTION.name, DataTypes.U32)) - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CURRENT_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CURRENT_SPEED.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.STATE.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.STATE.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_PRESSURE.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.TARGET_PRESSURE.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_PRESSURE.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_11:MsgFieldPositions.END_POS_FIELD_11])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_PRESSURE.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_12:MsgFieldPositions.END_POS_FIELD_12])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_CURRENT.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_13:MsgFieldPositions.END_POS_FIELD_13])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_CURRENT.name] = ( - struct.unpack('f', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_14:MsgFieldPositions.END_POS_FIELD_14])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CONTROL.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_15:MsgFieldPositions.END_POS_FIELD_15])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.CONTROL.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_16:MsgFieldPositions.END_POS_FIELD_16])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.DIRECTION_ERROR_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_17:MsgFieldPositions.END_POS_FIELD_17])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.DIRECTION_ERROR_COUNT.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_18:MsgFieldPositions.END_POS_FIELD_18])))[0] - - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D12_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_DIRECTION.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_19:MsgFieldPositions.END_POS_FIELD_19])))[0] - self.dd_dialysate_pumps[dd_enum_repository.DDDialysatePumpNames.D48_PUMP.name][dd_enum_repository.DDDialysatePumpAttributes.MEASURED_DIRECTION.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_20:MsgFieldPositions.END_POS_FIELD_20])))[0] - + self.process_into_dict(dict_to_update = self.dd_dialysate_pumps, + decoder_list = sensor_list, + message = message) self.dd_dialysate_pump_timestamp = timestamp @@ -155,7 +132,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Dialysate Pump', logger = self.logger, @@ -179,7 +156,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_TARGET_SPEED_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDDialysatePumpNames(pump_id).name} Dialysate Pump target speed', override_text = str(speed), @@ -204,7 +181,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_MEASURED_SPEED_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDDialysatePumpNames(pump_id).name} Dialysate Pump measured speed', override_text = str(speed), @@ -229,7 +206,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_TARGET_PRESSURE_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDDialysatePumpNames(pump_id).name} Dialysate Pump target pressure', override_text = str(pressure), @@ -254,7 +231,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_MEASURED_CURRENT_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDDialysatePumpNames(pump_id).name} Dialysate Pump measured current', override_text = str(current), @@ -279,7 +256,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_MEASURED_DIRECTION_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDDialysatePumpNames(pump_id).name} Dialysate Pump measured direction', override_text = str(direction), @@ -305,7 +282,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIALYSATE_PUMPS_START_STOP_OVERRIDE_REQUEST, entity_name = f'DD {dd_enum_repository.DDDialysatePumpNames(pump_id).name} Dialysate Pump start & stop', override_text = str(cmd), Index: leahi_dialin/dd/modules/drybicart.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/drybicart.py (.../drybicart.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/drybicart.py (.../drybicart.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -7,44 +7,48 @@ # # @file drybicart.py # -# @author (last) Sameer Poyil -# @date (last) 19-Nov-2025 +# @author (last) Zoltan Miskolci +# @date (last) 04-May-2026 # @author (original) Sameer Poyil # @date (original) 19-Nov-2025 # ############################################################################ -import struct + +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET , RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish -from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray +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.conversions import integer_to_bytearray + class DDDryBicart(AbstractSubSystem): """ DryBicart Dialysate Delivery (DD) Dialin API sub-class for Dry Bicart related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_dry_bicart_data = MsgIds.MSG_ID_DD_DRY_BICART_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_dry_bicart_data, - self._handler_dry_bicart_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_DRY_BICART_DATA.value, + function = self._handler_dry_bicart_sync) self.dd_dry_bicart_timestamp = 0 #: The timestamp of the last message self.dd_dry_bicart_fill_execution_state = 0 #: The Dry Bicart fill execution state @@ -74,31 +78,22 @@ @param message: published dry bicart data message @return: None """ - self.dd_dry_bicart_fill_execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.dd_bicarb_chamber_fill_execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.dd_dry_bicart_drain_execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.dd_dry_bicart_fill_cycle_counter = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.dd_dry_bicart_max_fill_cycle_count = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.ddd_dry_bicart_fill_request = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.dd_bicarb_chamber_fill_request = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] - self.ddd_dry_bicart_drain_request = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] - self.dd_dry_bicart_last_fill_time = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] - self.dd_dry_bicart_current_fill_time = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10]))[0] - self.dd_dryBiCartType = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_11:MsgFieldPositions.END_POS_FIELD_11]))[0] - self.dd_dryBiCartDrainTimePeriod = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_12:MsgFieldPositions.END_POS_FIELD_12]))[0] - + msg_list = [] + msg_list.append(('self.dd_dry_bicart_fill_execution_state', DataTypes.U32)) + msg_list.append(('self.dd_bicarb_chamber_fill_execution_state', DataTypes.U32)) + msg_list.append(('self.dd_dry_bicart_drain_execution_state', DataTypes.F32)) + msg_list.append(('self.dd_dry_bicart_fill_cycle_counter', DataTypes.U32)) + msg_list.append(('self.dd_dry_bicart_max_fill_cycle_count', DataTypes.U32)) + msg_list.append(('self.dd_dry_bicart_fill_request', DataTypes.U32)) + msg_list.append(('self.dd_bicarb_chamber_fill_request', DataTypes.U32)) + msg_list.append(('self.dd_dry_bicart_drain_request', DataTypes.U32)) + msg_list.append(('self.dd_dry_bicart_last_fill_time', DataTypes.U32)) + msg_list.append(('self.dd_dry_bicart_current_fill_time', DataTypes.U32)) + msg_list.append(('self.dd_dryBiCartType', DataTypes.U32)) + msg_list.append(('self.dd_dryBiCartDrainTimePeriod', DataTypes.U32)) + + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_dry_bicart_timestamp = timestamp @@ -116,7 +111,7 @@ return cmd_generic_broadcast_interval_override( ms=ms, reset=reset, - channel_id=DenaliChannels.dialin_to_dd_ch_id, + channel_id=CanChannels.dialin_to_dd_ch_id, msg_id=MsgIds.MSG_ID_DD_DRY_BICART_DATA_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name='DD Dry Bicart', logger=self.logger, @@ -140,7 +135,7 @@ return cmd_generic_override( payload=payload, reset=reset, - channel_id=DenaliChannels.dialin_to_dd_ch_id, + channel_id=CanChannels.dialin_to_dd_ch_id, msg_id=MsgIds.MSG_ID_DD_DRY_BICART_FILL_CYCLE_MAX_OVERRIDE_REQUEST, entity_name='max fill cycle count', override_text=str(count), @@ -164,7 +159,7 @@ return cmd_generic_override( payload=payload, reset=reset, - channel_id=DenaliChannels.dialin_to_dd_ch_id, + channel_id=CanChannels.dialin_to_dd_ch_id, msg_id=MsgIds.MSG_ID_DD_DRY_BICART_FILL_REQUEST_OVERRIDE_REQUEST, entity_name='Bicart Fill Request', override_text=str(start_stop), @@ -188,7 +183,7 @@ return cmd_generic_override( payload=payload, reset=NO_RESET, - channel_id=DenaliChannels.dialin_to_dd_ch_id, + channel_id=CanChannels.dialin_to_dd_ch_id, msg_id=MsgIds.MSG_ID_DD_BICARB_CHAMBER_FILL_REQUEST_OVERRIDE_REQUEST, entity_name='Bicarb Fill Request', override_text=str(start_stop), @@ -212,7 +207,7 @@ return cmd_generic_override( payload=payload, reset=reset, - channel_id=DenaliChannels.dialin_to_dd_ch_id, + channel_id=CanChannels.dialin_to_dd_ch_id, msg_id=MsgIds.MSG_ID_DD_BICART_DRAIN_REQUEST_OVERRIDE_REQUEST, entity_name='Bicart Drain Request', override_text=str(start_stop), @@ -236,7 +231,7 @@ return cmd_generic_override( payload=payload, reset=reset, - channel_id=DenaliChannels.dialin_to_dd_ch_id, + channel_id=CanChannels.dialin_to_dd_ch_id, msg_id=MsgIds.MSG_ID_DD_BICART_CARTRIDGE_SELECT_OVERRIDE_REQUEST, entity_name='Bicart Cartridge Size', override_text=str(small_large), Index: leahi_dialin/dd/modules/events.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/events.py (.../events.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/events.py (.../events.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,25 @@ # @file events.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Dara Navaei # @date (original) 12-Oct-2021 # ############################################################################ +# Module imports import struct from logging import Logger from datetime import datetime -from time import time +# Project imports from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish @@ -33,69 +37,49 @@ """ UNKNOWN_STATE = "UNKNOWN_PREVIOUS_STATE" - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_event = MsgIds.MSG_ID_DD_EVENT.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_event, self._handler_events_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_EVENT.value, + function = self._handler_events_sync) + + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_OP_MODE_DATA.value, + function = self._handler_dd_op_mode_sync) - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_op_mode_data = MsgIds.MSG_ID_DD_OP_MODE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_op_mode_data, - self._handler_dd_op_mode_sync) + self.events_timestamp = 0.0 #: The timestamp of the last Event message + self.op_mode = 0 #: The new Operation Mode value + self.sub_mode = 0 #: The new Operation Sub-Mode value + self.event_op_mode_timestamp = 0.0 #: The timestamp of the last Operation Mode change message - self.dd_events_timestamp = 0.0 #: The timestamp of the last Event message - self.dd_event_op_mode = 0 #: The new Operation Mode value - self.dd_event_sub_mode = 0 #: The new Operation Sub-Mode value - self.dd_event_op_mode_timestamp = 0.0 #: The timestamp of the last Operation Mode change message - # Dictionary of the mode as key and the sub mode states enum class as the value - self._dd_op_mode_2_sub_mode = {dd_enum_repository.DDOpModes.MODE_FAUL.name: dd_enum_repository.DDFaultStates, - dd_enum_repository.DDOpModes.MODE_SERV.name: dd_enum_repository.DDServiceStates, - dd_enum_repository.DDOpModes.MODE_INIT.name: dd_enum_repository.DDInitStates, - dd_enum_repository.DDOpModes.MODE_STAN.name: dd_enum_repository.DDStandbyStates, - dd_enum_repository.DDOpModes.MODE_PREG.name: dd_enum_repository.DDPreGenDialysateStates, - dd_enum_repository.DDOpModes.MODE_GEND.name: dd_enum_repository.DDGenDialysateModeStates, - dd_enum_repository.DDOpModes.MODE_POSG.name: dd_enum_repository.DDPostGenDialysateStates, - dd_enum_repository.DDOpModes.MODE_HEAT.name: dd_enum_repository.DDHeatDisinfectStates, - dd_enum_repository.DDOpModes.MODE_HCOL.name: dd_enum_repository.DDHeaterCoolingStates, - dd_enum_repository.DDOpModes.MODE_ROPS.name: dd_enum_repository.DDROPermeateStates, - dd_enum_repository.DDOpModes.MODE_NLEG.name: dd_enum_repository.DDNotLegalStates} + self._op_mode_2_sub_mode = {dd_enum_repository.DDOpModes.MODE_FAUL.name: dd_enum_repository.DDFaultStates, + dd_enum_repository.DDOpModes.MODE_SERV.name: dd_enum_repository.DDServiceStates, + dd_enum_repository.DDOpModes.MODE_INIT.name: dd_enum_repository.DDInitStates, + dd_enum_repository.DDOpModes.MODE_STAN.name: dd_enum_repository.DDStandbyStates, + dd_enum_repository.DDOpModes.MODE_PREG.name: dd_enum_repository.DDPreGenDialysateStates, + dd_enum_repository.DDOpModes.MODE_GEND.name: dd_enum_repository.DDGenDialysateModeStates, + dd_enum_repository.DDOpModes.MODE_POSG.name: dd_enum_repository.DDPostGenDialysateStates, + dd_enum_repository.DDOpModes.MODE_HEAT.name: dd_enum_repository.DDHeatDisinfectStates, + dd_enum_repository.DDOpModes.MODE_HCOL.name: dd_enum_repository.DDHeaterCoolingStates, + dd_enum_repository.DDOpModes.MODE_ROPS.name: dd_enum_repository.DDROPermeateStates, + dd_enum_repository.DDOpModes.MODE_NLEG.name: dd_enum_repository.DDNotLegalStates} - # Define the dictionaries - self._dd_event_dictionary = dict() - self._dd_event_data_type = dict() - # Loop through the list of the DD events enums and initial the event dictionary. Each event is a key in the # dictionary and the value is a list. + self._event_dictionary = dict() for event in dd_enum_repository.DDEventList: - self._dd_event_dictionary[dd_enum_repository.DDEventList(event).name] = [] + self._event_dictionary[dd_enum_repository.DDEventList(event).name] = [] - # Loop through the list of the event data type enum and update the dictionary - for data_type in dd_enum_repository.DDEventDataTypes: - event_data_type = dd_enum_repository.DDEventDataTypes(data_type).name - struct_unpack_type = None - # If U32 is in the data type enum (i.e. EVENT_DATA_TYPE_U32), then the key is the enum and the value is - # the corresponding format in the python struct - if 'U32' in event_data_type or 'BOOL' in event_data_type: - struct_unpack_type = 'I' - elif 'S32' in event_data_type: - struct_unpack_type = 'i' - elif 'F32' in event_data_type: - struct_unpack_type = 'f' - - self._dd_event_data_type[event_data_type] = struct_unpack_type - - def get_dd_nth_event(self, event_id, event_number=0): """ Returns the nth requested DD event @@ -105,14 +89,14 @@ @returns the requested DD event number """ - list_length = len(self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name]) + list_length = len(self._event_dictionary[dd_enum_repository.DDEventList(event_id).name]) if list_length == 0: event = [] elif event_number > list_length: - event = self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name][list_length - 1] + event = self._event_dictionary[dd_enum_repository.DDEventList(event_id).name][list_length - 1] else: - event = self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name][list_length - event_number - 1] + event = self._event_dictionary[dd_enum_repository.DDEventList(event_id).name][list_length - event_number - 1] return event @@ -123,8 +107,8 @@ @returns none """ - for key in self._dd_event_dictionary: - self._dd_event_dictionary[key].clear() + for key in self._event_dictionary: + self._event_dictionary[key].clear() def get_dd_events(self, event_id, number_of_events=1): @@ -139,23 +123,23 @@ list_of_events = [] # If there are not enough event lists send all the events that are available - if len(self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name]) <= number_of_events: - list_of_events = self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name] + if len(self._event_dictionary[dd_enum_repository.DDEventList(event_id).name]) <= number_of_events: + list_of_events = self._event_dictionary[dd_enum_repository.DDEventList(event_id).name] else: # Get the all the events - complete_list = self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name] + complete_list = self._event_dictionary[dd_enum_repository.DDEventList(event_id).name] # Since the last are located at the end of the list, iterate backwards for the defined # event messages for i in range(len(complete_list) - 1, len(complete_list) - number_of_events - 1, -1): list_of_events.append(complete_list[i]) if number_of_events == 0: - list_of_events = self._dd_event_dictionary[dd_enum_repository.DDEventList(event_id).name] + list_of_events = self._event_dictionary[dd_enum_repository.DDEventList(event_id).name] return list_of_events - @publish(["msg_id_dd_event", "dd_events_timestamp", '_dd_event_dictionary']) + @publish(["msg_id_dd_event", "events_timestamp", '_event_dictionary']) def _handler_events_sync(self, message, timestamp=0.0): """ Handles published events message @@ -170,132 +154,145 @@ sub_state = 0 current_sub_tuple = [] - event_id = struct.unpack('i', bytearray( + event_id = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + # Convert the event ID to enum + event_enum = dd_enum_repository.DDEventList(event_id) + current_timestamp = datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S.%f') - if event_id == dd_enum_repository.DDEventList.DD_EVENT_OPERATION_STATUS.value: - # Get the data type - event_data_type_1 = struct.unpack('i', bytearray( + if event_enum is dd_enum_repository.DDEventList.DD_EVENT_OPERATION_STATUS: + # Get the data type - irrelevant + event_data_type_1 = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - struct_data_type = self._dd_event_data_type[dd_enum_repository.DDEventDataTypes(event_data_type_1).name] - op_mode = struct.unpack(' current_sub_mode_timestamp: - # If the previous and current of the last two tuples do not match, then an operation mode transition - # has occurred and the previous state is converted from the previous class and the current op mode - # is converted from current operation states enum class. - # i.e last = (timestamp, event type, 3, 8) and one before = (timestamp, event type, 8, 3) - # previous and current do not match so in the last type (timestamp, event type, 8, 3) the prev state - # should be from op mode 8 and the current state should be from op mode 3 - previous_op_mode = last_op_tuple[len(last_op_tuple) - 2] - if previous_op_mode != DDEvents.UNKNOWN_STATE: - previous_sub_mode_enum_class = self._dd_op_mode_2_sub_mode[previous_op_mode] - event_data_1 = previous_sub_mode_enum_class(event_data_1).name - # Unknown previous state. Display value instead of name. + # Go through the Operation Status Change message list starting from the back + # Index description: + # 0: Timestamp + # 1: Op Mode + # 2: Sub Mode + # 3: State + for i in range(len(op_status_list) - 1, -1, -1): + op_status_msg = op_status_list[i] + + # Look for a match for event_data_2 + if not data_2_found: + if op_status_msg[2] == event_data_2: + event_2_op_mode = dd_enum_repository.DDOpModes(op_status_msg[1]).name + data_2_found = True + + # Look for a match for event_data_1 after event_data_2 is found + # Criteria is that the opmode, submode pair can't be the same as the one found for event_data_2 else: - event_data_1 = str(event_data_1) - event_data_2 = current_sub_mode_enum_class(event_data_2).name - else: + if op_status_msg[2] == event_data_1 and \ + (op_status_msg[2] != event_data_2 or dd_enum_repository.DDOpModes(op_status_msg[1]).name != event_2_op_mode): + event_1_op_mode = dd_enum_repository.DDOpModes(op_status_msg[1]).name + + # If op mode for event_data_2 found but not found for event_data_1 and run out of operation states + # assume it's the start of the unit start up and the going to standby is not logged yet + if event_2_op_mode != self.UNKNOWN_STATE and event_1_op_mode == self.UNKNOWN_STATE: + event_1_op_mode = dd_enum_repository.DDOpModes.MODE_STAN.name - if event_data_2 != 0: - event_data_1 = current_sub_mode_enum_class(event_data_1).name - event_data_2 = current_sub_mode_enum_class(event_data_2).name - else: - previous_sub_mode = current_sub_tuple[len(current_sub_tuple) - 2] - previous_sub_mode_enum_class = self._dd_op_mode_2_sub_mode[previous_sub_mode] - event_data_1 = previous_sub_mode_enum_class(event_data_1).name - event_data_2 = current_sub_mode_enum_class(event_data_2).name - event_tuple = (datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S.%f'), event_state_name, event_data_1, event_data_2) + # Update the event_data values + event_data_1 = self._op_mode_2_sub_mode[event_1_op_mode](event_data_1).name + event_data_2 = self._op_mode_2_sub_mode[event_2_op_mode](event_data_2).name + + # Update the tuple + event_tuple = (current_timestamp, event_enum.name, event_data_1, event_data_2) - elif event_state_name == dd_enum_repository.DDEventList.DD_EVENT_OPERATION_STATUS.name: - event_tuple = (time(), op_mode, sub_mode, sub_state) + elif event_enum is dd_enum_repository.DDEventList.DD_EVENT_OPERATION_STATUS: + event_tuple = (current_timestamp, op_mode, sub_mode, sub_state) # Update event dictionary - self._dd_event_dictionary[event_state_name].append(event_tuple) - self.dd_events_timestamp = timestamp + self._event_dictionary[event_enum.name].append(event_tuple) + self.events_timestamp = timestamp - @publish(["msg_id_dd_op_mode_data", "dd_event_op_mode_timestamp", "dd_event_op_mode", "dd_event_sub_mode"]) + + @publish(["msg_id_dd_op_mode_data", "event_op_mode_timestamp", "op_mode", "sub_mode"]) def _handler_dd_op_mode_sync(self, message, timestamp=0.0): """ Handles published DD operation mode messages. Current DD operation mode @@ -304,11 +301,10 @@ @param message: published DD operation mode broadcast message @return: None """ - mode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - smode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + msg_list = [] + msg_list.append(('self.op_mode', DataTypes.U32)) + msg_list.append(('self.sub_mode', DataTypes.U32)) - self.dd_event_op_mode = mode[0] - self.dd_event_sub_mode = smode[0] - self.dd_event_op_mode_timestamp = timestamp + self.process_into_vars(decoder_list = msg_list, + message = message) + self.event_op_mode_timestamp = timestamp Index: leahi_dialin/dd/modules/gen_dialysate.py =================================================================== diff -u -r39190f5b592ef515e19ae1b5455a32b84f853cf5 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/gen_dialysate.py (.../gen_dialysate.py) (revision 39190f5b592ef515e19ae1b5455a32b84f853cf5) +++ leahi_dialin/dd/modules/gen_dialysate.py (.../gen_dialysate.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file gen_dialysate.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,20 +35,19 @@ Dialysate Delivery (DD) Dialin API sub-class for gen dialysate related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_gen_dialysate_mode_data = MsgIds.MSG_ID_DD_GEN_DIALYSATE_MODE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_gen_dialysate_mode_data, - self._handler_gen_dialysate_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_GEN_DIALYSATE_MODE_DATA.value, + function = self._handler_gen_dialysate_sync) self.dd_gen_dialysate_timestamp = 0 #: The timestamp of the last message self.execution_state = 0 #: The Execution state @@ -66,26 +68,14 @@ @param message: published gen dialysate data message @return: None """ - sensor_list =[] - sensor_list.append(['self.execution_state', 'I']) - sensor_list.append(['self.dialysate_delivery_in_progress', 'I']) - # sensor_list.append(['self.d6_level', 'I']) - # sensor_list.append(['self.d63_level', 'f']) - # sensor_list.append(['self.d46_level', 'I']) - # sensor_list.append(['self.d9_pressure', 'f']) - # sensor_list.append(['self.d18_pressure', 'f']) - # sensor_list.append(['self.d51_pressure', 'f']) - sensor_list.append(['self.dialysate_good_to_deliver', 'I']) - sensor_list.append(['self.target_qd', 'f']) - - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - value = struct.unpack(sensor[1],bytearray(message['message'][start_pos:end_pos]))[0] - exec(f'{sensor[0]} = {value}') - i += 1 + msg_list = [] + msg_list.append(('self.execution_state', DataTypes.U32)) + msg_list.append(('self.dialysate_delivery_in_progress', DataTypes.U32)) + msg_list.append(('self.dialysate_good_to_deliver', DataTypes.U32)) + msg_list.append(('self.target_qd', DataTypes.F32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_gen_dialysate_timestamp = timestamp @@ -103,7 +93,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_GEND_MODE_DATA_PUBLISH_OVERRIDE_REQUEST, module_name = 'DD Gen Dialysate', logger = self.logger, @@ -125,7 +115,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIAL_DELIVERY_IN_PROGRESS_OVERRIDE_REQUEST, entity_name = f'DD Dialysate Delivery in progress', override_text = str(in_progress), @@ -148,7 +138,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_DIAL_DELIVERY_GOOD_TO_DELIVER_OVERRIDE_REQUEST, entity_name = f'DD Dialysate Delivery is good to deliver', override_text = str(delivery), @@ -171,7 +161,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_HYD_CHAMBER_TARGET_TEMP_OVERRIDE_REQUEST, entity_name = f'DD Hydraulics Chamber target Temperature', override_text = str(temperature), @@ -193,7 +183,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_GEND_EXEC_STATE_OVERRIDE_REQUEST, entity_name = f'DD Dialysate Delivery set state', override_text = str(state), @@ -216,7 +206,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_STOP_GEN_DIALYSATE_MODE_OVERRIDE_REQUEST, entity_name = f'DD Dialysate Delivery pre gen mode', override_text = str(state), Index: leahi_dialin/dd/modules/heaters.py =================================================================== diff -u -rda47fb6cd59dfe360546408c65e1ce152848d7a0 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/heaters.py (.../heaters.py) (revision da47fb6cd59dfe360546408c65e1ce152848d7a0) +++ leahi_dialin/dd/modules/heaters.py (.../heaters.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file heaters.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Dara Navaei # @date (original) 29-May-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,19 +34,19 @@ Dialysate Delivery (DD) Dialin API sub-class for heaters related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_heaters_data = MsgIds.MSG_ID_DD_HEATERS_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_heaters_data, self._handler_heaters_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_HEATERS_DATA.value, + function = self._handler_heaters_sync) self.dd_heaters_timestamp = 0.0 #: The timestamp of the latest message @@ -91,37 +94,34 @@ @param message: published DD heaters data message @returns none """ - sensor_list =[] - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_DUTY_CYCLE.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D45_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_DUTY_CYCLE.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_TARGET_TEMP.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D45_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_TARGET_TEMP.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_STATE.name]', 'i']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D45_HEAT.name][dd_enum_repository.DDHeaterAttributes.HEAT_STATE.name]', 'i']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.FEED_FORWARD.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.PWM_PERIOD.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.ADJUSTED_TARGET_TEMP.name]', 'f']) - sensor_list.append(['self.dd_heaters[dd_enum_repository.DDHeaterNames.D5_HEAT.name][dd_enum_repository.DDHeaterAttributes.TARGET_TEMP_TD.name]', 'f']) - sensor_list.append(['self.dbg1', 'f']) - sensor_list.append(['self.dbg2', 'f']) - sensor_list.append(['self.dbg3', 'f']) - sensor_list.append(['self.dbg4', 'f']) - sensor_list.append(['self.dbg5', 'f']) - sensor_list.append(['self.dbg6', 'f']) - sensor_list.append(['self.dbg7', 'f']) - sensor_list.append(['self.dbg8', 'f']) - sensor_list.append(['self.dbg9', 'f']) + sensor_list = [] + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.HEAT_DUTY_CYCLE.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D45_HEAT.name, dd_enum_repository.DDHeaterAttributes.HEAT_DUTY_CYCLE.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.HEAT_TARGET_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D45_HEAT.name, dd_enum_repository.DDHeaterAttributes.HEAT_TARGET_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.HEAT_STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D45_HEAT.name, dd_enum_repository.DDHeaterAttributes.HEAT_STATE.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.FEED_FORWARD.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.PWM_PERIOD.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.ADJUSTED_TARGET_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDHeaterNames.D5_HEAT.name, dd_enum_repository.DDHeaterAttributes.TARGET_TEMP_TD.name, DataTypes.F32)) + msg_list =[] + msg_list.append(('self.dbg1', DataTypes.F32)) + msg_list.append(('self.dbg2', DataTypes.F32)) + msg_list.append(('self.dbg3', DataTypes.F32)) + msg_list.append(('self.dbg4', DataTypes.F32)) + msg_list.append(('self.dbg5', DataTypes.F32)) + msg_list.append(('self.dbg6', DataTypes.F32)) + msg_list.append(('self.dbg7', DataTypes.F32)) + msg_list.append(('self.dbg8', DataTypes.F32)) + msg_list.append(('self.dbg9', DataTypes.F32)) - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - value = struct.unpack(sensor[1],bytearray(message['message'][start_pos:end_pos]))[0] - if 'nan' in str(value).lower(): - value = 0.0 - exec(f'{sensor[0]} = {value}') - i += 1 - + self.process_into_dict(dict_to_update = self.dd_heaters, + decoder_list = sensor_list, + message = message) + self.process_into_vars(decoder_list = msg_list, + message = message, + start_from_byte = len(sensor_list) * DataTypes.U32.size()) self.dd_heaters_timestamp = timestamp @@ -139,7 +139,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_HEATERS_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Heaters', logger = self.logger, @@ -165,7 +165,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_HEATERS_DUTY_CYCLE_OVERRIDE_REQUEST, entity_name = f'DD {heater_name} Heaters Duty Cycle', override_text = str(duty_cycle), @@ -192,7 +192,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_HEATERS_START_STOP_OVERRIDE_REQUEST, entity_name = f'DD {heater_name} Heaters {start_or_stop} temperature', override_text = str(temperature), @@ -218,7 +218,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_HEATERS_TARGET_TEMPERATURE_OVERRIDE_REQUEST, entity_name = f'DD {heater_name} Heaters target temperature', override_text = str(temperature), @@ -244,7 +244,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_HEATERS_PWM_PERIOD_OVERRIDE_REQUEST, entity_name = f'DD {heater_name} Heaters PWM period', override_text = str(period), Index: leahi_dialin/dd/modules/levels.py =================================================================== diff -u -r344ffd41f5460e1f0a9275192fdf5c47caeb6771 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/levels.py (.../levels.py) (revision 344ffd41f5460e1f0a9275192fdf5c47caeb6771) +++ leahi_dialin/dd/modules/levels.py (.../levels.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file levels.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Dara Navaei # @date (original) 29-May-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray @@ -31,19 +34,19 @@ Dialysate Delivery (DD) Dialin API sub-class for levels related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_level_data = MsgIds.MSG_ID_DD_LEVEL_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_level_data, self._handler_levels_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_LEVEL_DATA.value, + function = self._handler_levels_sync) self.dd_levels_timestamp = 0 #: The timestamp of the latest message @@ -66,19 +69,19 @@ @returns none """ sensor_list =[] - sensor_list.append((dd_enum_repository.DDLevelSensorNames.D46_LEVEL, 'i')) - sensor_list.append((dd_enum_repository.DDLevelSensorNames.D63_LEVEL, 'i')) - sensor_list.append((dd_enum_repository.DDLevelSensorNames.D98_LEVEL, 'i')) - sensor_list.append((dd_enum_repository.DDLevelSensorNames.D6_LEVEL, 'i')) + sensor_list.append((dd_enum_repository.DDLevelSensorNames.D46_LEVEL.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDLevelSensorNames.D63_LEVEL.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDLevelSensorNames.D98_LEVEL.name, DataTypes.U32)) + sensor_list.append((dd_enum_repository.DDLevelSensorNames.D6_LEVEL.name, DataTypes.U32)) + msg_list =[] + msg_list.append(('self.dd_bicarb_level', DataTypes.U32)) - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - self.dd_level_sensors[sensor[0].name] = struct.unpack(sensor[1], bytearray(message['message'][start_pos:end_pos]))[0] - i += 1 - self.dd_bicarb_level = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] + self.process_into_dict(dict_to_update = self.dd_level_sensors, + decoder_list = sensor_list, + message = message) + self.process_into_vars(decoder_list = msg_list, + message = message, + start_from_byte = len(sensor_list) * DataTypes.U32.size()) self.dd_levels_timestamp = timestamp @@ -96,7 +99,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_LEVELS_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Levels', logger = self.logger, @@ -122,7 +125,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_LEVELS_STATUS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Level Status', override_text = str(status), @@ -148,7 +151,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_DD_FLOATER_LEVEL_OVERRIDE_REQUEST, entity_name = f'DD Floater Levels status', override_text = f'{str(status)}', Index: leahi_dialin/dd/modules/post_gen_dialysate.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/post_gen_dialysate.py (.../post_gen_dialysate.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/post_gen_dialysate.py (.../post_gen_dialysate.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file post_gen_dialysate.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class DDPostGenDialysate(AbstractSubSystem): @@ -31,7 +34,7 @@ Dialysate Delivery (DD) Dialin API sub-class for post gen dialysate related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ @param can_interface: Leahi Can Messenger object """ @@ -41,10 +44,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_post_gen_dialysate_state_data = MsgIds.MSG_ID_DD_POST_GEN_DIALYSATE_STATE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_post_gen_dialysate_state_data, - self._handler_post_gen_dialysate_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_POST_GEN_DIALYSATE_STATE_DATA.value, + function = self._handler_post_gen_dialysate_sync) self.post_gen_state_timestamp = 0 #: The timestamp of the latest message self.execution_state = 0 #: The execution state @@ -58,9 +60,11 @@ @param message: published post gen dialysate data message @return: None """ - self.execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + msg_list =[] + msg_list.append(('self.execution_state', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.post_gen_state_timestamp = timestamp @@ -78,7 +82,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_POST_GEND_MODE_DATA_PUBLISH_OVERRIDE_REQUEST, module_name = 'DD Post-Gen Dialysate', logger = self.logger, Index: leahi_dialin/dd/modules/pre_gen_dialysate.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/pre_gen_dialysate.py (.../pre_gen_dialysate.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/pre_gen_dialysate.py (.../pre_gen_dialysate.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file pre_gen_dialysate.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class DDPreGenDialysate(AbstractSubSystem): @@ -31,7 +34,7 @@ Dialysate Delivery (DD) Dialin API sub-class for pre gen dialysate related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ @param can_interface: Leahi Can Messenger object """ @@ -41,15 +44,13 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_pre_gen_dialysate_state_data = MsgIds.MSG_ID_DD_PRE_GEN_DIALYSATE_STATE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_pre_gen_dialysate_state_data, - self._handler_pre_gen_state_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_PRE_GEN_DIALYSATE_STATE_DATA.value, + function = self._handler_pre_gen_state_sync) - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_pre_gen_dialysate_request_data = MsgIds.MSG_ID_DD_PRE_GEN_DIALYSATE_REQUEST_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_pre_gen_dialysate_request_data, - self._handler_pre_gen_request_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_PRE_GEN_DIALYSATE_REQUEST_DATA.value, + function = self._handler_pre_gen_request_sync) self.pre_gen_state_timestamp = 0 #: The timestamp of the latest Pre-Gen State message self.execution_state = 0 #: The execution state @@ -70,9 +71,12 @@ @param message: published pre gen dialysate data message @return: None """ - self.execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + msg_list =[] + msg_list.append(('self.execution_state', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) + self.pre_gen_state_timestamp = timestamp @@ -87,17 +91,15 @@ @param message: published pre gen dialysate request message @return: None """ - self.pre_gen_start = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.pre_gen_dial_rate = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.pre_gen_dial_temp = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.pre_gen_acid = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.pre_gen_bicarb = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] + msg_list =[] + msg_list.append(('self.pre_gen_start', DataTypes.U32)) + msg_list.append(('self.pre_gen_dial_rate', DataTypes.F32)) + msg_list.append(('self.pre_gen_dial_temp', DataTypes.F32)) + msg_list.append(('self.pre_gen_acid', DataTypes.U32)) + msg_list.append(('self.pre_gen_bicarb', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.pre_gen_request_timestamp = timestamp @@ -115,7 +117,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRE_GEND_MODE_DATA_PUBLISH_OVERRIDE_REQUEST, module_name = 'DD Pre-Gen Dialysate', logger = self.logger, Index: leahi_dialin/dd/modules/pressure_sensors.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/pressure_sensors.py (.../pressure_sensors.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/pressure_sensors.py (.../pressure_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,24 @@ # @file pressure_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Sean # @date (original) 14-Apr-2020 # ############################################################################ - -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,19 +34,18 @@ DD interface containing pressure related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: The DenaliCANMessenger object + @param can_interface: The CanMessenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_pressures_data = MsgIds.MSG_ID_DD_PRESSURES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_pressures_data, - self._handler_pressures_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_PRESSURES_DATA.value, + function = self._handler_pressures_sync) self.dd_pressures_timestamp = 0 #: The timestamp of the latest message @@ -68,19 +69,17 @@ @param message: published DD pressure data message @return: none """ - self.dd_pressures[dd_enum_repository.DDPressureSensorNames.D9_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.dd_pressures[dd_enum_repository.DDPressureSensorNames.D66_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.dd_pressures[dd_enum_repository.DDPressureSensorNames.D51_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.dd_pressures[dd_enum_repository.DDPressureSensorNames.D18_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.dd_pressures[dd_enum_repository.DDPressureSensorNames.D41_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.dd_pressures[dd_enum_repository.DDPressureSensorNames.D87_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] + sensor_list =[] + sensor_list.append((dd_enum_repository.DDPressureSensorNames.D9_PRES.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDPressureSensorNames.D66_PRES.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDPressureSensorNames.D51_PRES.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDPressureSensorNames.D18_PRES.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDPressureSensorNames.D41_PRES.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDPressureSensorNames.D87_PRES.name, DataTypes.F32)) + self.process_into_dict(dict_to_update = self.dd_pressures, + decoder_list = sensor_list, + message = message) self.dd_pressures_timestamp = timestamp @@ -98,7 +97,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRESSURE_SENSOR_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Pressure Sensors', logger = self.logger, @@ -125,7 +124,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRESSURE_SENSOR_READINGS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Pressure Sensors pressure', override_text = f'{str(pressure)} psi', @@ -153,7 +152,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRESSURE_SENSOR_READ_COUNTER_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Pressure Sensors read counter', override_text = str(counter), @@ -181,7 +180,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRESSURE_SENSOR_ERROR_COUNTER_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Pressure Sensors error counter', override_text = str(counter), @@ -209,7 +208,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRESSURE_SENSOR_FILTER_READINGS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Pressure Sensors filtered pressure', override_text = f'{str(pressure)} psi', Index: leahi_dialin/dd/modules/rinse_pump.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/rinse_pump.py (.../rinse_pump.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/rinse_pump.py (.../rinse_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file rinse_pump.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Jonny Paguio # @date (original) 13-Oct-2025 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,20 +35,19 @@ Dialysate Delivery (DD) Dialin API sub-class for Rinse Pump related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_rinse_pump_data = MsgIds.MSG_ID_DD_RINSE_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_rinse_pump_data, - self._handler_rinse_pump_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_RINSE_PUMP_DATA.value, + function = self._handler_rinse_pump_sync) self.dd_rinse_pump_timestamp = 0 #: The timestamp of the latest message self.d79_state = 0 #: The State of the D79 Pump @@ -63,13 +65,13 @@ @param message: published rinse pump data message @return: None """ - self.d79_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.d79_pump_pwm = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.d79_pump_rpm = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] + msg_list =[] + msg_list.append(('self.d79_state', DataTypes.U32)) + msg_list.append(('self.d79_pump_pwm', DataTypes.F32)) + msg_list.append(('self.d79_pump_rpm', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_rinse_pump_timestamp = timestamp @@ -87,7 +89,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_RINSE_PUMP_DATA_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Rinse Pump', logger = self.logger, @@ -112,7 +114,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_RINSE_PUMP_PWM_PERCENT_OVERRIDE_REQUEST, entity_name = f'DD Rinse Pump PWM', override_text = str(pwm_pct), @@ -136,7 +138,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_RINSE_PUMP_TURN_ON_OFF_REQUEST, entity_name = f'DD Rinse Pump state', override_text = 'On' if on_off == 1 else 'Off', Index: leahi_dialin/dd/modules/spent_chamber_fill.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/spent_chamber_fill.py (.../spent_chamber_fill.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/spent_chamber_fill.py (.../spent_chamber_fill.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file spent_chamber_fill.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Jonny Paguio # @date (original) 26-Aug-2025 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class DDSpentChamberFill(AbstractSubSystem): @@ -30,21 +33,20 @@ Dialysate Delivery (DD) Dialin API sub-class for Spent Chamber Fill related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_spent_chamber_fill_data = MsgIds.MSG_ID_DD_SPENT_CHAMBER_FILL_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_spent_chamber_fill_data, - self._handler_spent_chamber_fill_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_SPENT_CHAMBER_FILL_DATA.value, + function = self._handler_spent_chamber_fill_sync) self.dd_spent_chamber_timestamp = 0 #: The timestamp of the latest message self.execution_state = 0 #: The execution state @@ -63,13 +65,13 @@ @param message: published spent chamber fill data message @return: None """ - self.execution_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.switching_period = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.total_spent_chamber_fill_counter = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] + msg_list =[] + msg_list.append(('self.execution_state', DataTypes.U32)) + msg_list.append(('self.switching_period', DataTypes.U32)) + msg_list.append(('self.total_spent_chamber_fill_counter', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_spent_chamber_timestamp = timestamp @@ -87,7 +89,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_SPENT_CHAMB_FILL_DATA_PUBLISH_OVERRIDE_REQUEST, module_name = 'DD Spent Chamber Fill', logger = self.logger, Index: leahi_dialin/dd/modules/substitution_pump.py =================================================================== diff -u -rd0391ac112347f9c7021b0995bd4b22657fe9f66 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/substitution_pump.py (.../substitution_pump.py) (revision d0391ac112347f9c7021b0995bd4b22657fe9f66) +++ leahi_dialin/dd/modules/substitution_pump.py (.../substitution_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -18,10 +18,12 @@ from logging import Logger from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,7 +34,7 @@ Dialysate Delivery (DD) Dialin API sub-class for substitution Pump related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ @param can_interface: Denali Can Messenger object """ @@ -42,14 +44,14 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_substitution_pump_data = MsgIds.MSG_ID_DD_SUBSTITUTION_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_substitution_pump_data, - self._handler_substitution_pump_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_SUBSTITUTION_PUMP_DATA.value, + function = self._handler_substitution_pump_sync) self.dd_substitution_pump_timestamp = 0 #: The timestamp of the latest message self.d92_set_ml_min = 0 #: Set speed of d92 in mL/min + @publish(["msg_id_dd_rinse_pump_data", "d92_set_ml_min", "dd_substitution_pump_timestamp"]) @@ -60,11 +62,15 @@ @param message: published substitution pump data message @return: None """ - self.d92_set_ml_min = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + msg_list =[] + msg_list.append(('self.d92_set_ml_min', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) + self.dd_substitution_pump_timestamp = timestamp + def cmd_substitution_pump_data_publish_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ Constructs and sends the substitution pump data publish interval override command @@ -77,14 +83,15 @@ @return: 1 if successful, zero otherwise """ return cmd_generic_broadcast_interval_override( - ms=ms, - reset=reset, - channel_id=DenaliChannels.dialin_to_dd_ch_id, - msg_id=MsgIds.MSG_ID_DD_SUBSTITUTION_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, - module_name='DD Rinse Pump', - logger=self.logger, - can_interface=self.can_interface) + ms = ms, + reset = reset, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_SUBSTITUTION_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, + module_name = 'DD Substitution Pump', + logger = self.logger, + can_interface = self.can_interface) + def cmd_substitution_pump_set_start_stop(self, pump_id: int, command: int, speed: float) -> int: """ Constructs and sends the substitution pump start stop command @@ -100,13 +107,11 @@ payload = pmp + cmd + spd return cmd_generic_override( - payload=payload, - reset=NO_RESET, - channel_id=DenaliChannels.dialin_to_dd_ch_id, - msg_id=MsgIds.MSG_ID_DD_SUBSTITUTION_PUMP_START_STOP_OVERRIDE_REQUEST, - entity_name=f'DD Substitution Pump speed', - override_text=str(spd), - logger=self.logger, - can_interface=self.can_interface) - - + payload = payload, + reset = NO_RESET, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_SUBSTITUTION_PUMP_START_STOP_OVERRIDE_REQUEST, + entity_name = f'DD Substitution Pump speed', + override_text = str(spd), + logger = self.logger, + can_interface = self.can_interface) Index: leahi_dialin/dd/modules/temperature_sensors.py =================================================================== diff -u -rc93ef62631552b2ba3ffb77006d29f902e7a1128 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/temperature_sensors.py (.../temperature_sensors.py) (revision c93ef62631552b2ba3ffb77006d29f902e7a1128) +++ leahi_dialin/dd/modules/temperature_sensors.py (.../temperature_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,38 +8,40 @@ # @file temperature_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Dara Navaei # @date (original) 01-Dec-2021 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray class DDTemperatureSensors(AbstractSubSystem): - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): super().__init__() self.can_interface = can_interface self.logger: Logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_temperature_data = MsgIds.MSG_ID_DD_TEMPERATURE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_temperature_data, - self._handler_temperature_sensors_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_TEMPERATURE_DATA.value, + function = self._handler_temperature_sensors_sync) self.dd_temperatures_timestamp = 0.0 #: The timestamp of the latest message @@ -80,37 +82,33 @@ @returns none """ sensor_list =[] - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D1_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D78_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D4_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D50_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D99_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.BRD_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D16_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D28_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D30_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D44_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D75_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D4_AVG_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D50_AVG_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D99_AVG_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D28_AVG_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D30_AVG_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D78_AVG_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D9_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D66_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D51_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D18_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D41_TEMP, 'f')) - sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D87_TEMP, 'f')) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D1_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D78_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D4_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D50_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D99_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.BRD_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D16_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D28_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D30_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D44_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D75_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D4_AVG_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D50_AVG_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D99_AVG_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D28_AVG_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D30_AVG_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D78_AVG_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D9_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D66_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D51_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D18_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D41_TEMP.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDTemperatureSensorNames.D87_TEMP.name, DataTypes.F32)) - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - self.dd_temperatures[sensor[0].name] = struct.unpack(sensor[1], bytearray(message['message'][start_pos:end_pos]))[0] - i += 1 - + self.process_into_dict(dict_to_update = self.dd_temperatures, + decoder_list = sensor_list, + message = message) self.dd_temperatures_timestamp = timestamp @@ -128,7 +126,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_TEMPERATURE_SENSOR_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Temperature Sensors', logger = self.logger, @@ -178,7 +176,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = message_id, entity_name = f'DD {sensor_name} Temperature Sensor temperature', override_text = f'{str(sensor_value)} Celsius', @@ -204,7 +202,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_TEMPERATURE_SENSOR_READ_COUNTER_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Temperature Sensor read counter', override_text = str(counter), @@ -227,7 +225,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_TEMPERATURE_SENSOR_BARO_READ_COUNTER_OVERRIDE_REQUEST, entity_name = f'DD Barometer Sensor read counter', override_text = str(counter), @@ -250,7 +248,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_TEMPERATURE_SENSOR_BARO_CRC_OVERRIDE_REQUEST, entity_name = f'DD Barometer Sensor CRC', override_text = str(crc), @@ -300,7 +298,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = message_id, entity_name = f'DD {sensor_name} Temperature Sensor filtered temperature', override_text = f'{str(temperature)} Celsius', Index: leahi_dialin/dd/modules/ultrafiltration.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/ultrafiltration.py (.../ultrafiltration.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/ultrafiltration.py (.../ultrafiltration.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,24 @@ # @file ultrafiltration.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class DDUltrafiltration(AbstractSubSystem): @@ -31,7 +35,7 @@ Dialysate Delivery (DD) Dialin API sub-class for post gen dialysate related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ @param can_interface: Leahi Can Messenger object """ @@ -41,10 +45,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_uf_data = MsgIds.MSG_ID_DD_UF_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_uf_data, - self._handler_ultrafiltration_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_UF_DATA.value, + function = self._handler_ultrafiltration_sync) self.uf_timestamp = 0.0 #: The timestamp of the latest message self.uf_exec_state = 0 #: The Ultrafiltration execution state @@ -63,15 +66,14 @@ @param message: published ultrafiltration data message @return: None """ - self.uf_exec_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.uf_rate = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.compensated_uf_rate = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.is_uf_requested = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] + msg_list =[] + msg_list.append(('self.uf_exec_state', DataTypes.U32)) + msg_list.append(('self.uf_rate', DataTypes.F32)) + msg_list.append(('self.compensated_uf_rate', DataTypes.F32)) + msg_list.append(('self.is_uf_requested', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.uf_timestamp = timestamp @@ -89,7 +91,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_UF_DATA_PUBLISH_OVERRIDE_REQUEST, module_name = 'DD Ultrafiltration', logger = self.logger, Index: leahi_dialin/dd/modules/valves.py =================================================================== diff -u -rd089b54d2fde7f8c36acce41a47d7acee4ef5da6 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/valves.py (.../valves.py) (revision d089b54d2fde7f8c36acce41a47d7acee4ef5da6) +++ leahi_dialin/dd/modules/valves.py (.../valves.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,23 +8,28 @@ # @file valves.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Peman Montazemi # @date (original) 19-May-2020 # ############################################################################ -import struct -from logging import Logger +# Module imports from collections import OrderedDict +from logging import Logger +import struct +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanMessage, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish from leahi_dialin.utils.conversions import integer_to_bytearray # Valve states @@ -39,26 +44,26 @@ """ # Valves states publish message field positions - START_POS_VALVES_STATES = DenaliMessage.PAYLOAD_START_INDEX + START_POS_VALVES_STATES = CanMessage.PAYLOAD_START_INDEX END_POS_VALVES_STATES = START_POS_VALVES_STATES + 2 # Valves States come in as a U16 value (2 bytes) START_POS_BCV_VALVES_STATES = END_POS_VALVES_STATES END_POS_BCV_VALVES_STATES = START_POS_BCV_VALVES_STATES + 1 START_POS_SPARE_VALVES_STATES = END_POS_BCV_VALVES_STATES END_POS_SPARE_VALVES_STATES = START_POS_SPARE_VALVES_STATES + 1 END_POS_ALL_VALVES = START_POS_VALVES_STATES + 4 - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_valves_states_data = MsgIds.MSG_ID_DD_VALVES_STATES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_valves_states_data, self._handler_valves_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_VALVES_STATES_DATA.value, + function = self._handler_valves_sync) self.dd_valves_states_timestamp = 0.0 #: The timestamp of the latest message self.valve_states_all = 0x00000000 #: States of all the vales in binary format @@ -225,10 +230,10 @@ @param message: published DD valves states message @return: none """ - vsa = struct.unpack('I', bytearray(message['message'][self.START_POS_VALVES_STATES:self.END_POS_ALL_VALVES])) + vsa = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray(message['message'][self.START_POS_VALVES_STATES:self.END_POS_ALL_VALVES])) self.valve_states_all = vsa[0] - vst = struct.unpack('H', bytearray(message['message'][self.START_POS_VALVES_STATES:self.END_POS_VALVES_STATES])) + vst = struct.unpack(DataTypes.U16.unpack_attrib(), bytearray(message['message'][self.START_POS_VALVES_STATES:self.END_POS_VALVES_STATES])) # Extract each valve state from U16 valves states using bit-masking self.d14_valv["state"] = self._binary_to_valve_state(vst[0] & 1) self.d52_valv["state"] = self._binary_to_valve_state(vst[0] & 2) @@ -247,7 +252,7 @@ self.d81_valv["state"] = self._binary_to_valve_state(vst[0] & 16384) self.d85_valv["state"] = self._binary_to_valve_state(vst[0] & 32768) - bcv = struct.unpack('B', bytearray(message['message'][self.START_POS_BCV_VALVES_STATES:self.END_POS_BCV_VALVES_STATES])) + bcv = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray(message['message'][self.START_POS_BCV_VALVES_STATES:self.END_POS_BCV_VALVES_STATES])) self.d23_valv["state"] = self._binary_to_valve_state(bcv[0] & 1) self.d19_valv["state"] = self._binary_to_valve_state(bcv[0] & 2) self.d25_valv["state"] = self._binary_to_valve_state(bcv[0] & 4) @@ -257,7 +262,7 @@ self.d26_valv["state"] = self._binary_to_valve_state(bcv[0] & 64) self.d22_valv["state"] = self._binary_to_valve_state(bcv[0] & 128) - spv = struct.unpack('B', bytearray(message['message'][self.START_POS_SPARE_VALVES_STATES:self.END_POS_SPARE_VALVES_STATES])) + spv = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray(message['message'][self.START_POS_SPARE_VALVES_STATES:self.END_POS_SPARE_VALVES_STATES])) self.d88_d79_valv["state"] = self._binary_to_valve_state(spv[0] & 1) self.d83_valv["state"] = self._binary_to_valve_state(spv[0] & 2) self.d91_valv["state"] = self._binary_to_valve_state(spv[0] & 4) @@ -275,11 +280,10 @@ start = self.END_POS_SPARE_VALVES_STATES end = start + 1 for valve_id in self.valves_sensed_states: - valve_state_number = struct.unpack('B', bytearray(message['message'][start:end]))[0] + valve_state_number = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray(message['message'][start:end]))[0] self.valves_sensed_states[valve_id] = valve_state_number start = end end += 1 - self.dd_valves_states_timestamp = timestamp @@ -297,7 +301,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_VALVE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Valves', logger = self.logger, @@ -325,7 +329,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_VALVE_SENSED_STATE_OVERRIDE_REQUEST, entity_name = f'DD {valve_name} Valve sensed state', override_text = str(state), @@ -354,7 +358,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_VALVE_STATE_OVERRIDE_REQUEST, entity_name = f'DD {valve_name} Valve state', override_text = str(state), @@ -405,7 +409,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_BC_VALVE_STATES_OVERRIDE_REQUEST, entity_name = f'DD Balancing Chamber Valves state', override_text = '', @@ -431,7 +435,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_VALVES_OPEN_CLOSE_STATE_OVERRIDE_REQUEST, entity_name = f'DD {valve_name} Valve state', override_text = str(state), Index: leahi_dialin/dd/modules/voltages.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/modules/voltages.py (.../voltages.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/dd/modules/voltages.py (.../voltages.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file voltages.py # # @author (last) Zoltan Miskolci -# @date (last) 07-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Sean Nash # @date (original) 15-Apr-2021 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import dd_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,7 +34,7 @@ Dialysate Delivery (dD) Dialin API sub-class for voltage monitor related commands and data. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ DDVoltages constructor """ @@ -40,10 +43,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_dd_voltages_data = MsgIds.MSG_ID_DD_VOLTAGES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_voltages_data, - self._handler_monitored_voltages_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_VOLTAGES_DATA.value, + function = self._handler_monitored_voltages_sync) self.dd_voltages_timestamp = 0.0 #: The timestamp of the latest message @@ -60,32 +62,19 @@ @param message: published monitored voltages data message @return: none """ - v12 = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - v33 = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) - v5l = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) - v24_1 = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) - v24_2 = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) - vfc = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) - vfa = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) - vfp = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) + sensor_list = [] + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_1_2V.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_3_3V.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_5V_LOGIC.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_24V_1.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_24V_2.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_FPGA_VCC_V.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_FPGA_AUX_V.name, DataTypes.F32)) + sensor_list.append((dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_FPGA_PVN_V.name, DataTypes.F32)) - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_1_2V.value] = v12[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_3_3V.value] = v33[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_5V_LOGIC.value] = v5l[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_24V_1.value] = v24_1[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_24V_2.value] = v24_2[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_FPGA_VCC_V.value] = vfc[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_FPGA_AUX_V.value] = vfa[0] - self.monitored_voltages[dd_enum_repository.DDMonitoredVoltages.MONITORED_LINE_FPGA_PVN_V.value] = vfp[0] - + self.process_into_dict(dict_to_update = self.monitored_voltages, + decoder_list = sensor_list, + message = message) self.dd_voltages_timestamp = timestamp @@ -103,7 +92,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_VOLTAGE_DATA_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'DD Voltages', logger = self.logger, @@ -131,7 +120,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_MONITORED_VOLTAGE_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} voltage', override_text = f'{str(volts)} V', Index: leahi_dialin/dd/proxies/ro_proxy.py =================================================================== diff -u -r18c90a1b2b6c7339bdd192a2d2fac32f2b8a35df -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/proxies/ro_proxy.py (.../ro_proxy.py) (revision 18c90a1b2b6c7339bdd192a2d2fac32f2b8a35df) +++ leahi_dialin/dd/proxies/ro_proxy.py (.../ro_proxy.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,17 +8,20 @@ # @file ro_proxy.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 04-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ +# Module imports from logging import Logger -from leahi_dialin.common.msg_defs import MsgIds -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliCanMessenger, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem +# Project imports +from leahi_dialin.common.msg_ids import MsgIds +from leahi_dialin.common.override_templates import cmd_generic_override +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray @@ -27,11 +30,11 @@ Dialysate Delivery (DD) Dialin API sub-class for RO proxy ( injection ) related commands. """ - def __init__(self, can_interface: DenaliCanMessenger, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ ROProxy constructor - @param can_interface: the Denali CAN interface object + @param can_interface: the CAN interface object """ super().__init__() self.can_interface = can_interface @@ -53,9 +56,12 @@ stt = integer_to_bytearray(start) rtt = float_to_bytearray(ro_rate) payload = cmd + stt + rtt - message = DenaliMessage.build_message(channel_id=DenaliChannels.dd_to_ro_ch_id, - message_id=MsgIds.MSG_ID_DD_FP_START_STOP_CMD_REQUEST.value, - payload=payload) - self.logger.debug("Sending DD start stop request to RO.") - self.can_interface.send(message, 0) + cmd_generic_override(payload = payload, + reset = None, + channel_id = CanChannels.dd_to_ro_ch_id, + msg_id = MsgIds.MSG_ID_DD_FP_START_STOP_CMD_REQUEST, + entity_name = 'DD Start Stop Request to RO', + override_text = 'N/A', + logger = self.logger, + can_interface = self.can_interface) Index: leahi_dialin/dd/proxies/td_proxy.py =================================================================== diff -u -r941ca56f3f41c92c4b025a0713ee1d99c792aab8 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/dd/proxies/td_proxy.py (.../td_proxy.py) (revision 941ca56f3f41c92c4b025a0713ee1d99c792aab8) +++ leahi_dialin/dd/proxies/td_proxy.py (.../td_proxy.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -7,20 +7,23 @@ # # @file td_proxy.py # -# @author (last) Micahel Garthwaite -# @date (last) 18-Aug-2023 +# @author (last) Zoltan Miskolci +# @date (last) 04-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +# Project imports +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliCanMessenger, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -29,21 +32,20 @@ Dialysate Delivery (DD) Dialin API sub-class for TD proxy ( injection ) related commands. """ - def __init__(self, can_interface: DenaliCanMessenger, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ TDProxy constructor - @param can_interface: the Denali CAN interface object + @param can_interface: the CAN interface object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_to_dd_ch_id - msg_id = MsgIds.MSG_ID_DD_GEN_DIALYSATE_REQUEST_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, msg_id, - self._handler_dialysate_delivery_request_response) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_to_dd_ch_id, + message_id = MsgIds.MSG_ID_DD_GEN_DIALYSATE_REQUEST_DATA.value, + function = self._handler_dialysate_delivery_request_response) self.dialysate_delivery_request_start = 0 self.dialysate_delivery_request_dial_rate = 0.0 @@ -68,21 +70,17 @@ @param message: published dialysate delivery request response data message @return: None """ - self.dialysate_delivery_request_start = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.dialysate_delivery_request_dial_rate = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.dialysate_delivery_request_uf_rate = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.dialysate_delivery_request_dial_temp = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.dialysate_delivery_request_bypass = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.dialysate_delivery_request_acid = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.dialysate_delivery_request_bicarb = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] + msg_list =[] + msg_list.append(('self.dialysate_delivery_request_start', DataTypes.U32)) + msg_list.append(('self.dialysate_delivery_request_dial_rate', DataTypes.F32)) + msg_list.append(('self.dialysate_delivery_request_uf_rate', DataTypes.F32)) + msg_list.append(('self.dialysate_delivery_request_dial_temp', DataTypes.F32)) + msg_list.append(('self.dialysate_delivery_request_bypass', DataTypes.U32)) + msg_list.append(('self.dialysate_delivery_request_acid', DataTypes.U32)) + msg_list.append(('self.dialysate_delivery_request_bicarb', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.dd_td_to_dd_request_response_timestamp = timestamp @@ -106,7 +104,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_GEN_DIALYSATE_REQUEST_DATA, entity_name = 'TD Gen Dialysate Request', override_text = 'N/A', @@ -136,7 +134,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.dialin_to_dd_ch_id, + channel_id = CanChannels.dialin_to_dd_ch_id, msg_id = MsgIds.MSG_ID_DD_PRE_GEN_DIALYSATE_REQUEST_DATA, entity_name = 'TD Pre-Gen Dialysate Request', override_text = 'N/A', Index: leahi_dialin/fp/filtration_purification.py =================================================================== diff -u -r31d0d53df3f8bff9f9f401beb10200c6af711e63 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/filtration_purification.py (.../filtration_purification.py) (revision 31d0d53df3f8bff9f9f401beb10200c6af711e63) +++ leahi_dialin/fp/filtration_purification.py (.../filtration_purification.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,14 +8,13 @@ # @file filtration_purification.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct - +# Project imports from .modules.alarms import FPAlarms from .modules.boost_pump import FPBoostPump from .modules.conductivity_sensors import FPConductivitySensors @@ -34,10 +33,13 @@ from ..common.constants import NO_RESET from ..common import fp_enum_repository -from ..common.msg_defs import MsgIds, MsgFieldPositions, MsgFieldPositionsFWVersions +from ..common.generic_defs import DataTypes +from ..common.msg_defs import MsgFieldPositions +from ..common.msg_ids import MsgIds from ..common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from ..protocols.CAN import DenaliMessage, DenaliCanMessenger, DenaliChannels -from ..utils.base import AbstractSubSystem, publish, LogManager +from ..protocols.CAN import CanMessage, CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish, LogManager from ..utils.conversions import integer_to_bytearray, bytearray_to_byte @@ -71,31 +73,32 @@ self.logger = self._log_manager.logger # Create listener - self.can_interface = DenaliCanMessenger(can_interface=can_interface, + self.can_interface = CanMessenger(can_interface=can_interface, logger=self.logger) self.can_interface.start() self.callback_id = None # register handler for FP operation mode broadcast messages if self.can_interface is not None: - self.can_interface.register_receiving_publication_function(DenaliChannels.fp_sync_broadcast_ch_id, - MsgIds.MSG_ID_FP_OP_MODE_DATA.value, - self._handler_fp_op_mode_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_OP_MODE_DATA.value, + function = self._handler_fp_op_mode_sync) # FP's version is DD's version since they are the same FW. - self.can_interface.register_receiving_publication_function(DenaliChannels.dd_sync_broadcast_ch_id, - MsgIds.MSG_ID_DD_VERSION_RESPONSE.value, - self._handler_fp_version_response_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_VERSION_RESPONSE.value, + function = self._handler_fp_version_response_sync) - self.can_interface.register_receiving_publication_function(DenaliChannels.fp_sync_broadcast_ch_id, - MsgIds.MSG_ID_FP_DEBUG_EVENT.value, - self._handler_fp_debug_event_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_DEBUG_EVENT.value, + function = self._handler_fp_debug_event_sync) - self.can_interface.register_receiving_publication_function(DenaliChannels.dd_to_dialin_ch_id, - MsgIds.MSG_ID_FP_DEF_STATUS_RESPONSE.value, - self._handler_fp_defeatured_response) - self.can_interface.register_receiving_publication_function(DenaliChannels.dd_to_dialin_ch_id, - MsgIds.MSG_ID_FP_BOOST_PUMP_INSTALL_STATUS_RESPONSE.value, - self._handler_fp_boost_pump_installed_response) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_to_dialin_ch_id, + message_id = MsgIds.MSG_ID_FP_DEF_STATUS_RESPONSE.value, + function = self._handler_fp_defeatured_response) + + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_to_dialin_ch_id, + message_id = MsgIds.MSG_ID_FP_BOOST_PUMP_INSTALL_STATUS_RESPONSE.value, + function = self._handler_fp_boost_pump_installed_response) # Dialin will send a login message during construction. This is for the leahi subsystems to start # publishing CAN data when there is no UI connected as the UI typically does this job. self.cmd_log_in_to_fp() @@ -174,33 +177,27 @@ @return: None if not successful, the version string if unpacked successfully """ - major = struct.unpack(' 0 for each in [major, minor, micro, build, compatibility]]): - self.fp_version = f"v{major[0]}.{minor[0]}.{micro[0]}-{build[0]}.{compatibility[0]}" - self.logger.debug(f"FP VERSION: {self.fp_version}") + result = self.process_into_vars(decoder_list = msg_list, + message = message) - if all([len(each) > 0 for each in [fpga_id, fpga_major, fpga_minor, fpga_lab]]): - self.fp_fpga_version = f"v{fpga_id[0]}.{fpga_major[0]}.{fpga_minor[0]}-{fpga_lab[0]}" - self.logger.debug(f"FP FPGA VERSION: {self.fp_fpga_version}") + if all([len(each) > 0 for each in [result['major'], result['minor'], result['micro'], result['build'], result['compatibility']]]): + self.fp_version = f"v{result['major']}.{result['minor']}.{result['micro']}-{result['build']}.{result['compatibility']}" + self.logger.debug(f'FP VERSION: {self.fp_version}') + if all([len(each) > 0 for each in [result['fpga_id'], result['fpga_major'], result['fpga_minor'], result['fpga_lab']]]): + self.fp_fpga_version = f"v{result['fpga_id']}.{result['fpga_major']}.{result['fpga_minor']}-{result['fpga_lab']}" + self.logger.debug(f'FP FPGA VERSION: {self.fp_fpga_version}') self.fp_version_response_timestamp = timestamp @@ -213,13 +210,12 @@ @param message: published FP operation mode broadcast message @return: None """ - mode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - smode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + msg_list = [] + msg_list.append(('self.fp_operation_mode', DataTypes.U32)) + msg_list.append(('self.fp_operation_sub_mode', DataTypes.U32)) - self.fp_operation_mode = mode[0] - self.fp_operation_sub_mode = smode[0] + self.process_into_vars(decoder_list = msg_list, + message = message) self.fp_op_mode_timestamp = timestamp @@ -231,9 +227,11 @@ @param message: defeatured response from FP @return: None """ - self.fp_defeatured = True if struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] == 1 else False + msg_list = [] + msg_list.append(('self.fp_defeatured', DataTypes.BOOL)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.fp_defeatured_timestamp = timestamp @@ -245,9 +243,11 @@ @param message: defeatured response from FP @return: None """ - self.fp_p40_installed = True if struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] == 1 else False + msg_list = [] + msg_list.append(('self.fp_p40_installed', DataTypes.BOOL)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.fp_p40_installed_timestamp = timestamp @@ -262,7 +262,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_DEF_STATUS_REQUEST, entity_name = 'FP Defeatured Status', override_text = '', @@ -281,7 +281,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_BOOST_PUMP_INSTALL_STATUS_REQUEST, entity_name = 'FP P40 Boost Pump installed status', override_text = '', @@ -303,7 +303,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_OPERATION_MODE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Operation Mode', logger = self.logger, @@ -318,7 +318,7 @@ @param resend: (bool) if False (default), try to login once. Otherwise, tries to login indefinitely @return: 1 if logged in, 0 if log in failed """ - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_fp_ch_id, + message = CanMessage.build_message(channel_id=CanChannels.dialin_to_fp_ch_id, message_id=MsgIds.MSG_ID_FP_TESTER_LOGIN_REQUEST.value, payload=list(map(int, map(ord, self.FP_LOGIN_PASSWORD)))) @@ -328,15 +328,15 @@ received_message = self.can_interface.send(message, resend=resend) if received_message is not None: - if received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] == 1: + if received_message['message'][CanMessage.PAYLOAD_START_INDEX] == 1: self.logger.debug("Success: Logged In") self.fp_logged_in = True self.cmd_request_defeatured_status() #self._send_ro_checkin_message() # Timer starts interval first #self.can_interface.transmit_interval_dictionary[self.callback_id].start() else: self.logger.debug("Failure: Log In Failed.") - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + return received_message['message'][CanMessage.PAYLOAD_START_INDEX] else: self.logger.debug("Login Timeout!!!!") return False @@ -358,7 +358,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_SET_OP_MODE_REQUEST, entity_name = 'FP Operation Mode', override_text = fp_enum_repository.FPOpModes(new_mode).name, @@ -401,7 +401,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_SET_OP_SUB_MODE_REQUEST, entity_name = 'FP Operation Sub Mode', override_text = new_sub_mode_enum.name, @@ -420,7 +420,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_SOFTWARE_RESET_REQUEST, entity_name = 'FP Software Reset', override_text = '', @@ -445,7 +445,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_SAFETY_SHUTDOWN_OVERRIDE_REQUEST, entity_name = 'FP Safety Shutdown', override_text = str(active), Index: leahi_dialin/fp/modules/alarms.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/alarms.py (.../alarms.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/fp/modules/alarms.py (.../alarms.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,25 @@ # @file alarms.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Quang Nguyen # @date (original) 02-Sep-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +import struct +# Project imports from leahi_dialin.common.alarm_defs import AlarmList from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanMessage, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish from leahi_dialin.utils.conversions import integer_to_bytearray @@ -32,21 +35,21 @@ FP interface containing alarm related commands. """ _ALARM_ID_MAX_ALARMS = 500 - START_POS_ALARM_ID = DenaliMessage.PAYLOAD_START_INDEX + START_POS_ALARM_ID = CanMessage.PAYLOAD_START_INDEX END_POS_ALARM_ID = START_POS_ALARM_ID + 2 - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_alarm_broadcast_ch_id - self.msg_id_fp_alarm_triggered = MsgIds.MSG_ID_ALARM_TRIGGERED.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_alarm_triggered, self._handler_alarm_triggered) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_alarm_broadcast_ch_id, + message_id = MsgIds.MSG_ID_ALARM_TRIGGERED.value, + function = self._handler_alarm_triggered) self.fp_alarm_triggered_timestamp = 0.0 #: The timestamp of the last Alarm Triggered message @@ -61,26 +64,7 @@ self.last_alarm_data_1 = 0.0 #: The last Alarm's data part 1 self.last_alarm_data_2 = 0.0 #: The last Alarm's data part 2 - # alarm information - self.alarm_data_type = dict() #: The Alarm's Data type in dict format - # Loop through the list of the event data type enum and update the dictionary - for data_type in fp_enum_repository.FPEventDataTypes: - event_data_type = fp_enum_repository.FPEventDataTypes(data_type).name - struct_unpack_type = None - - # If U32 is in the data type enum (i.e. EVENT_DATA_TYPE_U32), then the key is the enum and the value is - # the corresponding format in the python struct - if 'U32' in event_data_type or 'BOOL' in event_data_type or 'NONE' in event_data_type: - struct_unpack_type = 'I' - elif 'S32' in event_data_type: - struct_unpack_type = 'i' - elif 'F32' in event_data_type: - struct_unpack_type = 'f' - - self.alarm_data_type[event_data_type] = struct_unpack_type - - @publish(["msg_id_fp_alarm_triggered", "alarm_states", "alarm_conditions", "alarm_data", "alarm_priorities", "alarm_ranks", "alarm_clear_top_only_flags", "fp_alarm_triggered_timestamp"]) @@ -92,34 +76,41 @@ @return: none """ self.logger.debug("Alarm activated!") - alarm_id = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) + + result = {} + msg_list = [] + msg_list.append(('alarm_id', DataTypes.U32)) + msg_list.append(('data_typ_1', DataTypes.U32)) + msg_list.append(('data_1', 'data_typ_1')) + msg_list.append(('data_typ_2', DataTypes.U32)) + msg_list.append(('data_2', 'data_typ_2')) + msg_list.append(('priority', DataTypes.U32)) + msg_list.append(('rank', DataTypes.U32)) + msg_list.append(('clr_top_only', DataTypes.U32)) - data_typ_1 = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) - # Get the corresponding structure format - struct_data_type_1 = self.alarm_data_type[fp_enum_repository.FPEventDataTypes(data_typ_1[0]).name] - # Get the data value by unpacking the data type - data_1 = struct.unpack(struct_data_type_1, bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + i = 1 + for msg_detail in msg_list: + start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') + end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') + if isinstance(msg_detail[1], DataTypes): + unpack_attrib = msg_detail[1].unpack_attrib() + else: + unpack_attrib = DataTypes(result[msg_detail[1]]).unpack_attrib() + + value = struct.unpack(unpack_attrib, bytearray(message['message'][start_pos:end_pos]))[0] + result[msg_detail[0]] = value + i += 1 - data_typ_2 = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) - # Get the corresponding structure format - struct_data_type_2 = self.alarm_data_type[fp_enum_repository.FPEventDataTypes(data_typ_2[0]).name] - # Get the data value by unpacking the data type - data_2 = struct.unpack(struct_data_type_2, bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) - - priority = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) - rank = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) - clr_top_only = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) - - self.logger.debug("Alarm ID: %d %d %d" % (alarm_id[0], data_1[0], data_2[0])) - self.alarm_states[alarm_id[0]] = True - self.alarm_conditions[alarm_id[0]] = True - self.alarm_priorities[alarm_id[0]] = priority[0] - self.alarm_ranks[alarm_id[0]] = rank[0] - self.alarm_clear_top_only_flags[alarm_id[0]] = clr_top_only[0] - self.alarm_data[alarm_id[0]] = [data_1[0], data_2[0]] - self.last_alarm_triggered = alarm_id[0] - self.last_alarm_data_1 = data_1[0] - self.last_alarm_data_2 = data_2[0] + self.logger.debug("Alarm ID: %d %d %d" % (result['alarm_id'], result['data_1'], result['data_2'])) + self.alarm_states[result['alarm_id']] = True + self.alarm_conditions[result['alarm_id']] = True + self.alarm_priorities[result['alarm_id']] = result['priority'] + self.alarm_ranks[result['alarm_id']] = result['rank'] + self.alarm_clear_top_only_flags[result['alarm_id']] = result['clr_top_only'] + self.alarm_data[result['alarm_id']] = [result['data_1'], result['data_2']] + self.last_alarm_triggered = result['alarm_id'] + self.last_alarm_data_1 = result['data_1'] + self.last_alarm_data_2 = result['data_2'] self.fp_alarm_triggered_timestamp = timestamp @@ -140,7 +131,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = '', module_name = 'FP Alarm state', logger = self.logger, @@ -165,7 +156,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = '', module_name = 'FP Alarm info', logger = self.logger, @@ -196,7 +187,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_ALARM_STATE_OVERRIDE_REQUEST, entity_name = f'FP {alarm_name} Alarm state', override_text = f'{state_name}', @@ -219,7 +210,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_ALARM_CLEAR_ALL_ALARMS_REQUEST, entity_name = f'FP Alarms', override_text = f'Cleared', Index: leahi_dialin/fp/modules/boost_pump.py =================================================================== diff -u -r99f9233b24e6115bb1384ec8f5a077aefc37ab4b -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/boost_pump.py (.../boost_pump.py) (revision 99f9233b24e6115bb1384ec8f5a077aefc37ab4b) +++ leahi_dialin/fp/modules/boost_pump.py (.../boost_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file boost_pump.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,7 +34,7 @@ Dialin API sub-class for FP Boost pump controller related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ FPBoostPump constructor """ @@ -40,10 +43,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_boost_pump_data = MsgIds.MSG_ID_FP_BOOST_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_boost_pump_data, - self._handler_pump_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_BOOST_PUMP_DATA.value, + function = self._handler_pump_sync) self.boost_pump_timestamp = 0.0 #: The timestamp of the last message @@ -72,25 +74,20 @@ @param message: published FP ro pump data message @return: none """ - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.STATE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.DUTY_CYCLE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.SPEED.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.TARGET_PRESSURE.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.TARGET_FLOW.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.TARGET_DUTY_CYCLE_PCT.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.DUTY_CYCLE_PCT.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] - self.boost_pump[fp_enum_repository.FPBoostPumpNames.P40_PUMP.name][fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE_PCT.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.STATE.name, DataTypes.U32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.DUTY_CYCLE.name, DataTypes.U32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE.name, DataTypes.U32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.SPEED.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.TARGET_PRESSURE.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.TARGET_FLOW.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.TARGET_DUTY_CYCLE_PCT.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.DUTY_CYCLE_PCT.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPBoostPumpNames.P40_PUMP.name, fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE_PCT.name, DataTypes.F32)) + self.process_into_dict(dict_to_update = self.boost_pump, + decoder_list = sensor_list, + message = message) self.boost_pump_timestamp = timestamp @@ -108,7 +105,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_BOOST_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Boost Pump', logger = self.logger, @@ -133,7 +130,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_BOOST_PUMP_TARGET_PRESSURE_OVERRIDE_REQUEST, entity_name = f'FP P40 Boost Pump target pressure', override_text = f'{str(pressure)} psi', @@ -159,7 +156,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_BOOST_PUMP_TARGET_FLOW_OVERRIDE_REQUEST, entity_name = f'FP P40 Boost Pump target flow', override_text = f'{str(flow)}', @@ -185,7 +182,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_BOOST_PUMP_TARGET_PWM_OVERRIDE_REQUEST, entity_name = f'FP P40 Boost Pump target PWM', override_text = f'{str(duty_cycle)}', @@ -205,7 +202,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_BOOST_PUMP_STOP_REQUEST, entity_name = f'FP P40 Boost Pump hard stop', override_text = f'Active', Index: leahi_dialin/fp/modules/conductivity_sensors.py =================================================================== diff -u -r21460e78e780dd07e083c3812c27f53507464786 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/conductivity_sensors.py (.../conductivity_sensors.py) (revision 21460e78e780dd07e083c3812c27f53507464786) +++ leahi_dialin/fp/modules/conductivity_sensors.py (.../conductivity_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,26 @@ # @file conductivity_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Quang Nguyen # @date (original) 20-Jul-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository from leahi_dialin.common.fp_defs import BETA_IOFP_COND_SENSOR_OFFSET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,21 +36,23 @@ Dialin API sub-class for conductivity sensors related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.can_interface.register_receiving_publication_function(channel_id, MsgIds.MSG_ID_FP_CONDUCTIVITY_DATA.value, - self._handler_conductivity_sensors_sync) - self.can_interface.register_receiving_publication_function(channel_id, MsgIds.MSG_ID_FP_RO_REJECTION_RATIO_DATA.value, - self._handler_ro_rejection_ratio_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_CONDUCTIVITY_DATA.value, + function = self._handler_conductivity_sensors_sync) + + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_RO_REJECTION_RATIO_DATA.value, + function = self._handler_ro_rejection_ratio_sync) self.fp_conductivity_timestamp = 0.0 #: The timestamp of the last Conductivity data message self.ro_rejection_ratio_timestamp = 0.0 #: The timestamp of the last RO Rejection Ratio data message @@ -76,11 +82,13 @@ @param message: published conductivity sensor data message @return: None """ - self.fp_conductivity[fp_enum_repository.FPConductivitySensorNames.P9_COND.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.fp_conductivity[fp_enum_repository.FPConductivitySensorNames.P18_COND.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPConductivitySensorNames.P9_COND.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPConductivitySensorNames.P18_COND.name, DataTypes.F32)) + self.process_into_dict(dict_to_update = self.fp_conductivity, + decoder_list = sensor_list, + message = message) self.fp_conductivity_timestamp = timestamp @@ -93,17 +101,16 @@ @param message: Published RO Rejection Ratio data message @return: None """ - self.ro_rejection_ratio[fp_enum_repository.FPRORejectionRatioNames.RAW_RO_REJECTION_RATIO.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.ro_rejection_ratio[fp_enum_repository.FPRORejectionRatioNames.RAW_RO_REJECTION_RATIO_TANK_FILL.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.ro_rejection_ratio[fp_enum_repository.FPRORejectionRatioNames.AVG_RO_REJECTION_RATIO.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.ro_rejection_ratio[fp_enum_repository.FPRORejectionRatioNames.AVG_RO_REJECTION_RATIO_TANK_FILL.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.ro_rejection_ratio[fp_enum_repository.FPRORejectionRatioNames.GEN_PERMEATE_STATE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPRORejectionRatioNames.RAW_RO_REJECTION_RATIO.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPRORejectionRatioNames.RAW_RO_REJECTION_RATIO_TANK_FILL.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPRORejectionRatioNames.AVG_RO_REJECTION_RATIO.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPRORejectionRatioNames.AVG_RO_REJECTION_RATIO_TANK_FILL.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPRORejectionRatioNames.GEN_PERMEATE_STATE.name, DataTypes.U32)) + self.process_into_dict(dict_to_update = self.ro_rejection_ratio, + decoder_list = sensor_list, + message = message) self.ro_rejection_ratio_timestamp = timestamp @@ -121,7 +128,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_CONDUCTIVITY_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Conductivity Sensor', logger = self.logger, @@ -142,7 +149,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_REJECTION_RATIO_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP RO Rejection Ratio', logger = self.logger, @@ -169,7 +176,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_CONDUCTIVITY_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Conductivity Sensor reading', override_text = f'{str(conductivity)} microsiemens/cm', @@ -196,7 +203,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_CONDUCTIVITY_READ_COUNT_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Conductivity Sensor read counter', override_text = str(counter), @@ -223,7 +230,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_CONDUCTIVITY_ERROR_COUNT_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Conductivity Sensor error counter', override_text = str(counter), @@ -246,7 +253,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_FILTERED_REJECTION_RATIO_OVERRIDE_REQUEST, entity_name = f'FP RO Rejection Ratio', override_text = str(value), @@ -277,7 +284,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_CONDUCTIVITY_SENSOR_RESISTANCE_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Conductivity Sensor resistance', override_text = f'{str(conductivity)} ohms', @@ -306,7 +313,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FILTERED_COND_SENSOR_READINGS_OVERRIDE_REQUEST, entity_name = f'DD {sensor_name} Conductivity Sensor reading', override_text = f'{str(conductivity)} microsiemens/cm', Index: leahi_dialin/fp/modules/events.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/events.py (.../events.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/fp/modules/events.py (.../events.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,7 +8,7 @@ # @file events.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Dara Navaei # @date (original) 12-Oct-2021 # @@ -19,10 +19,13 @@ from datetime import datetime from time import time -from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.common import dd_enum_repository, fp_enum_repository +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels, CanMessenger +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class FPEvents(AbstractSubSystem): @@ -31,67 +34,47 @@ """ UNKNOWN_STATE = "UNKNOWN_PREVIOUS_STATE" - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() - self.can_interface = can_interface + self.can_interface: CanMessenger = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_event = MsgIds.MSG_ID_FP_EVENT.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_event, self._handler_events_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_EVENT.value, + function = self._handler_events_sync) - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_op_mode_data = MsgIds.MSG_ID_FP_OP_MODE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_op_mode_data, - self._handler_fp_op_mode_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_OP_MODE_DATA.value, + function = self._handler_fp_op_mode_sync) - self.fp_events_timestamp = 0.0 #: The timestamp of the last Event message - self.fp_event_op_mode = 0 #: The new Operation Mode value - self.fp_event_sub_mode = 0 #: The new Operation Sub-Mode value - self.fp_event_op_mode_timestamp = 0.0 #: The timestamp of the last Operation Mode change message + self.events_timestamp = 0.0 #: The timestamp of the last Event message + self.op_mode = 0 #: The new Operation Mode value + self.sub_mode = 0 #: The new Operation Sub-Mode value + self.event_op_mode_timestamp = 0.0 #: The timestamp of the last Operation Mode change message # Dictionary of the mode as key and the sub mode states enum class as the value - self._fp_op_mode_2_sub_mode = {fp_enum_repository.FPOpModes.MODE_FAUL.name: fp_enum_repository.FPFaultStates, - fp_enum_repository.FPOpModes.MODE_SERV.name: fp_enum_repository.FPServiceStates, - fp_enum_repository.FPOpModes.MODE_INIT.name: fp_enum_repository.FPPostStates, - fp_enum_repository.FPOpModes.MODE_STAN.name: fp_enum_repository.FPStandbyStates, - fp_enum_repository.FPOpModes.MODE_PRE_GENP.name: fp_enum_repository.FPPreGenPermeateStates, - fp_enum_repository.FPOpModes.MODE_GENP.name: fp_enum_repository.FPGenPermeateStates, - fp_enum_repository.FPOpModes.MODE_DPGP.name: fp_enum_repository.FPPreGenPDefStates, - fp_enum_repository.FPOpModes.MODE_DEGP.name: fp_enum_repository.FPGenPermeateDefStates, - fp_enum_repository.FPOpModes.MODE_NLEG.name: fp_enum_repository.FPNotLegalStates} + self._op_mode_2_sub_mode = {fp_enum_repository.FPOpModes.MODE_FAUL.name: fp_enum_repository.FPFaultStates, + fp_enum_repository.FPOpModes.MODE_SERV.name: fp_enum_repository.FPServiceStates, + fp_enum_repository.FPOpModes.MODE_INIT.name: fp_enum_repository.FPPostStates, + fp_enum_repository.FPOpModes.MODE_STAN.name: fp_enum_repository.FPStandbyStates, + fp_enum_repository.FPOpModes.MODE_PRE_GENP.name: fp_enum_repository.FPPreGenPermeateStates, + fp_enum_repository.FPOpModes.MODE_GENP.name: fp_enum_repository.FPGenPermeateStates, + fp_enum_repository.FPOpModes.MODE_DPGP.name: fp_enum_repository.FPPreGenPDefStates, + fp_enum_repository.FPOpModes.MODE_DEGP.name: fp_enum_repository.FPGenPermeateDefStates, + fp_enum_repository.FPOpModes.MODE_NLEG.name: fp_enum_repository.FPNotLegalStates} - # Define the dictionaries - self._fp_event_dictionary = dict() - self._fp_event_data_type = dict() - # Loop through the list of the FP events enums and initial the event dictionary. Each event is a key in the # dictionary and the value is a list. + self._event_dictionary = dict() for event in fp_enum_repository.FPEventList: - self._fp_event_dictionary[fp_enum_repository.FPEventList(event).name] = [] + self._event_dictionary[fp_enum_repository.FPEventList(event).name] = [] - # Loop through the list of the event data type enum and update the dictionary - for data_type in fp_enum_repository.FPEventDataTypes: - event_data_type = fp_enum_repository.FPEventDataTypes(data_type).name - struct_unpack_type = None - # If U32 is in the data type enum (i.e. EVENT_DATA_TYPE_U32), then the key is the enum and the value is - # the corresponding format in the python struct - if 'U32' in event_data_type or 'BOOL' in event_data_type: - struct_unpack_type = 'I' - elif 'S32' in event_data_type: - struct_unpack_type = 'i' - elif 'F32' in event_data_type: - struct_unpack_type = 'f' - - self._fp_event_data_type[event_data_type] = struct_unpack_type - - def get_fp_nth_event(self, event_id, event_number=0): """ Returns the nth requested FP event @@ -101,14 +84,14 @@ @returns the requested FP event number """ - list_length = len(self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name]) + list_length = len(self._event_dictionary[fp_enum_repository.FPEventList(event_id).name]) if list_length == 0: event = [] elif event_number > list_length: - event = self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name][list_length - 1] + event = self._event_dictionary[fp_enum_repository.FPEventList(event_id).name][list_length - 1] else: - event = self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name][list_length - event_number - 1] + event = self._event_dictionary[fp_enum_repository.FPEventList(event_id).name][list_length - event_number - 1] return event @@ -119,8 +102,8 @@ @returns none """ - for key in self._fp_event_dictionary: - self._fp_event_dictionary[key].clear() + for key in self._event_dictionary: + self._event_dictionary[key].clear() def get_fp_events(self, event_id, number_of_events=1): @@ -135,23 +118,23 @@ list_of_events = [] # If there are not enough event lists send all the events that are available - if len(self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name]) <= number_of_events: - list_of_events = self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name] + if len(self._event_dictionary[fp_enum_repository.FPEventList(event_id).name]) <= number_of_events: + list_of_events = self._event_dictionary[fp_enum_repository.FPEventList(event_id).name] else: # Get the all the events - complete_list = self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name] + complete_list = self._event_dictionary[fp_enum_repository.FPEventList(event_id).name] # Since the last are located at the end of the list, iterate backwards for the defined # event messages for i in range(len(complete_list) - 1, len(complete_list) - number_of_events - 1, -1): list_of_events.append(complete_list[i]) if number_of_events == 0: - list_of_events = self._fp_event_dictionary[fp_enum_repository.FPEventList(event_id).name] + list_of_events = self._event_dictionary[fp_enum_repository.FPEventList(event_id).name] return list_of_events - @publish(["msg_id_fp_event", "fp_events_timestamp", '_fp_event_dictionary']) + @publish(["msg_id_fp_event", "events_timestamp", '_event_dictionary']) def _handler_events_sync(self, message, timestamp=0.0): """ Handles published events message @@ -164,134 +147,151 @@ op_mode = 0 sub_mode = 0 sub_state = 0 - current_sub_tuple = [] - event_id = struct.unpack('i', bytearray( + event_id = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + + # Convert the event ID to enum + compensated_event_id = event_id - dd_enum_repository.DDEventList.NUM_OF_DD_EVENT_IDS.value + event_enum = fp_enum_repository.FPEventList(compensated_event_id) + current_timestamp = datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S.%f') - if event_id == fp_enum_repository.FPEventList.FP_EVENT_OPERATION_STATUS.value: - # Get the data type - event_data_type_1 = struct.unpack('i', bytearray( + if event_enum is fp_enum_repository.FPEventList.FP_EVENT_OPERATION_STATUS: + # Get the data type - irrelevant + event_data_type_1 = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - struct_data_type = self._fp_event_data_type[fp_enum_repository.FPEventDataTypes(event_data_type_1).name] - op_mode = struct.unpack(' current_sub_mode_timestamp: - # If the previous and current of the last two tuples do not match, then an operation mode transition - # has occurred and the previous state is converted from the previous class and the current op mode - # is converted from current operation states enum class. - # i.e last = (timestamp, event type, 3, 8) and one before = (timestamp, event type, 8, 3) - # previous and current do not match so in the last type (timestamp, event type, 8, 3) the prev state - # should be from op mode 8 and the current state should be from op mode 3 - previous_op_mode = last_op_tuple[len(last_op_tuple) - 2] - if previous_op_mode != FPEvents.UNKNOWN_STATE: - previous_sub_mode_enum_class = self._fp_op_mode_2_sub_mode[previous_op_mode] - event_data_1 = previous_sub_mode_enum_class(event_data_1).name - # Unknown previous state. Display value instead of name. + # Go through the Operation Status Change message list starting from the back + # Index description: + # 0: Timestamp + # 1: Op Mode + # 2: Sub Mode + # 3: State + for i in range(len(op_status_list) - 1, -1, -1): + op_status_msg = op_status_list[i] + + # Look for a match for event_data_2 + if not data_2_found: + if op_status_msg[2] == event_data_2: + event_2_op_mode = fp_enum_repository.FPOpModes(op_status_msg[1]).name + data_2_found = True + + # Look for a match for event_data_1 after event_data_2 is found + # Criteria is that the opmode, submode pair can't be the same as the one found for event_data_2 else: - event_data_1 = str(event_data_1) - event_data_2 = current_sub_mode_enum_class(event_data_2).name - else: + if op_status_msg[2] == event_data_1 and \ + (op_status_msg[2] != event_data_2 or fp_enum_repository.FPOpModes(op_status_msg[1]).name != event_2_op_mode): + event_1_op_mode = fp_enum_repository.FPOpModes(op_status_msg[1]).name + + # If op mode for event_data_2 found but not found for event_data_1 and run out of operation states + # assume it's the start of the unit start up and the going to standby is not logged yet + if event_2_op_mode != self.UNKNOWN_STATE and event_1_op_mode == self.UNKNOWN_STATE: + event_1_op_mode = fp_enum_repository.FPOpModes.MODE_STAN.name - if event_data_2 != 0: - event_data_1 = current_sub_mode_enum_class(event_data_1).name - event_data_2 = current_sub_mode_enum_class(event_data_2).name - else: - previous_sub_mode = current_sub_tuple[len(current_sub_tuple) - 2] - previous_sub_mode_enum_class = self._fp_op_mode_2_sub_mode[previous_sub_mode] - event_data_1 = previous_sub_mode_enum_class(event_data_1).name - event_data_2 = current_sub_mode_enum_class(event_data_2).name - event_tuple = (datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S.%f'), event_state_name, event_data_1, event_data_2) + # Update the event_data values + event_data_1 = self._op_mode_2_sub_mode[event_1_op_mode](event_data_1).name + event_data_2 = self._op_mode_2_sub_mode[event_2_op_mode](event_data_2).name + + # Update the tuple + event_tuple = (current_timestamp, event_enum.name, event_data_1, event_data_2) - elif event_state_name == fp_enum_repository.FPEventList.FP_EVENT_OPERATION_STATUS.name: - event_tuple = (time(), op_mode, sub_mode, sub_state) + elif event_enum is fp_enum_repository.FPEventList.FP_EVENT_OPERATION_STATUS: + event_tuple = (current_timestamp, op_mode, sub_mode, sub_state) # Update event dictionary - self._fp_event_dictionary[event_state_name].append(event_tuple) - self.fp_events_timestamp = timestamp + self._event_dictionary[event_enum.name].append(event_tuple) + self.events_timestamp = timestamp - @publish(["msg_id_fp_op_mode_data", "fp_event_op_mode_timestamp", "fp_event_op_mode", "fp_event_sub_mode"]) + @publish(["msg_id_fp_op_mode_data", "event_op_mode_timestamp", "op_mode", "sub_mode"]) def _handler_fp_op_mode_sync(self, message, timestamp=0.0): """ Handles published FP operation mode messages. Current FP operation mode @@ -300,11 +300,10 @@ @param message: published FP operation mode broadcast message @return: None """ - mode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - smode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + msg_list = [] + msg_list.append(('self.op_mode', DataTypes.U32)) + msg_list.append(('self.sub_mode', DataTypes.U32)) - self.fp_event_op_mode = mode[0] - self.fp_event_sub_mode = smode[0] - self.fp_event_op_mode_timestamp = timestamp + self.process_into_vars(decoder_list = msg_list, + message = message) + self.event_op_mode_timestamp = timestamp Index: leahi_dialin/fp/modules/flow_sensors.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/flow_sensors.py (.../flow_sensors.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/fp/modules/flow_sensors.py (.../flow_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file flow_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,19 +34,19 @@ API sub-class for FP flow sensor related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_flow_data = MsgIds.MSG_ID_FP_FLOW_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_flow_data, self._handler_flow_sensor_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_FLOW_DATA.value, + function = self._handler_flow_sensor_sync) self.fp_flow_timestamp = 0 #: The timestamp of the last message @@ -62,11 +65,13 @@ @param message: published flow sensor data message @returns none """ - self.fp_flows[fp_enum_repository.FPFlowSensorNames.P7_FLOW.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.fp_flows[fp_enum_repository.FPFlowSensorNames.P16_FLOW.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPFlowSensorNames.P7_FLOW.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPFlowSensorNames.P16_FLOW.name, DataTypes.F32)) + self.process_into_dict(dict_to_update = self.fp_flows, + decoder_list = sensor_list, + message = message) self.fp_flow_timestamp = timestamp @@ -84,7 +89,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLOWS_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Flow Sensor', logger = self.logger, @@ -110,7 +115,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLOW_RATE_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Flow Sensor flow rate', override_text = f'{str(rate)}', @@ -137,7 +142,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLOW_TEMP_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Flow Sensor temperature', override_text = f'{str(temp)} C', @@ -164,7 +169,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FILTERED_FLOW_RATE_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Flow Sensor filtered flow rate', override_text = f'{str(rate)}', @@ -191,7 +196,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FILTERED_FLOW_TEMP_OVERRIDE_REQUEST, entity_name = f'FP {sensor_name} Flow Sensor filtered temperature', override_text = f'{str(temp)} C', Index: leahi_dialin/fp/modules/fp_test_configs.py =================================================================== diff -u -r21a1d644c7d02e7588781b9025b422aa9d74c9ca -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/fp_test_configs.py (.../fp_test_configs.py) (revision 21a1d644c7d02e7588781b9025b422aa9d74c9ca) +++ leahi_dialin/fp/modules/fp_test_configs.py (.../fp_test_configs.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file fp_test_configs.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Jonny Paguio # @date (original) 20-Aug-2025 # ############################################################################ +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.global_vars import GlobalVariables -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_override from leahi_dialin.common.test_config_defs import DDFPTestConfigOptions -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, bytearray_to_integer @@ -31,19 +34,19 @@ FP Dialin API sub-class for setting and getting the test configurations. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_to_dialin_ch_id - self.can_interface.register_receiving_publication_function(channel_id, MsgIds.MSG_ID_FP_SEND_TEST_CONFIGURATION.value, - self._handler_fp_test_config_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_to_dialin_ch_id, + message_id = MsgIds.MSG_ID_FP_SEND_TEST_CONFIGURATION.value, + function = self._handler_fp_test_config_sync) self.fp_test_configs_response_timestamp = 0.0 #: The timestamp of the last message self.fp_test_configs = dict() #: The Test Config data in dictionary format @@ -95,7 +98,7 @@ response = cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_SET_TEST_CONFIGURATION, entity_name = f'FP {DDFPTestConfigOptions(config).name} Test Config', override_text = 'Active', @@ -118,7 +121,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_GET_TEST_CONFIGURATION, entity_name = f'Get FP Test Configuration Record', override_text = 'Active', @@ -137,7 +140,7 @@ response = cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RESET_ALL_TEST_CONFIGURATIONS, entity_name = f'Reset all DD & FP Test Configurations', override_text = 'Active', Index: leahi_dialin/fp/modules/levels.py =================================================================== diff -u -rb0929a0985784c80e2d184413995a87e6aa23d4b -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/levels.py (.../levels.py) (revision b0929a0985784c80e2d184413995a87e6aa23d4b) +++ leahi_dialin/fp/modules/levels.py (.../levels.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,25 @@ # @file levels.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Dara Navaei # @date (original) 29-May-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.fp_defs import BETA_IOFP_LEVEL_OFFSET from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.fp_defs import BETA_IOFP_LEVEL_OFFSET +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray @@ -32,19 +35,19 @@ Dialin API sub-class for levels related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_level_data = MsgIds.MSG_ID_FP_LEVEL_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_level_data, self._handler_levels_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_LEVEL_DATA.value, + function = self._handler_levels_sync) self.fp_levels_timestamp = 0 #: The timestamp of the last message @@ -64,9 +67,12 @@ @param message: published levels data message @returns none """ - self.fp_levels[fp_enum_repository.FPFloaterLevelSensorNames.P25_LEVEL.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPFloaterLevelSensorNames.P25_LEVEL.name, DataTypes.U32)) + self.process_into_dict(dict_to_update = self.fp_levels, + decoder_list = sensor_list, + message = message) self.fp_levels_timestamp = timestamp @@ -84,7 +90,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_LEVEL_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Levels', logger = self.logger, @@ -110,7 +116,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLOATER_LEVEL_OVERRIDE_REQUEST, entity_name = f'FP Levels status', override_text = f'{str(status)}', Index: leahi_dialin/fp/modules/permeate_tank.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/permeate_tank.py (.../permeate_tank.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/fp/modules/permeate_tank.py (.../permeate_tank.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,23 @@ # @file permeate_tank.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Micahel Garthwaite # @date (original) 29-Oct-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class FPPermeateTank(AbstractSubSystem): @@ -31,7 +34,7 @@ Filtration Purification (FP) Dialin API sub-class for permeate tank related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ @param can_interface: Leahi Can Messenger object """ @@ -41,10 +44,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.dd_sync_broadcast_ch_id - self.msg_id_fp_permeate_tank_data = MsgIds.MSG_ID_FP_PERMEATE_TANK_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_permeate_tank_data, - self._handler_permeate_tank_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, # Channels.dd_sync_broadcast_ch_id ?? + message_id = MsgIds.MSG_ID_FP_PERMEATE_TANK_DATA.value, + function = self._handler_permeate_tank_sync) self.permeate_tank_timestamp = 0.0 #: The timestamp of the last message self.permeate_tank_state = 0 #: The Permeate Tank's State value @@ -58,9 +60,11 @@ @param message: published permeate tank data message @return: None """ - self.permeate_tank_state = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + msg_list =[] + msg_list.append(('self.permeate_tank_state', DataTypes.U32)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.permeate_tank_timestamp = timestamp @@ -78,7 +82,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_PERMEATE_TANK_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Permeate Tank', logger = self.logger, Index: leahi_dialin/fp/modules/pressure_sensors.py =================================================================== diff -u -r70ede35f42770305f04b251b3372ef42a9cecbf5 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/pressure_sensors.py (.../pressure_sensors.py) (revision 70ede35f42770305f04b251b3372ef42a9cecbf5) +++ leahi_dialin/fp/modules/pressure_sensors.py (.../pressure_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,25 @@ # @file pressure_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project importsk from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository from leahi_dialin.common.fp_defs import BETA_IOFP_PRES_SENSOR_OFFSET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,7 +35,7 @@ Dialin API sub-class for FP pressure sensor related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ FPPressureSensors constructor """ @@ -41,10 +44,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_pressures_data = MsgIds.MSG_ID_FP_PRESSURES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_pressures_data, - self._handler_pressure_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_PRESSURES_DATA.value, + function = self._handler_pressure_sync) self.fp_pressure_timestamp = 0.0 #: The timestamp of the last message @@ -67,17 +69,16 @@ @param message: published FP pressure sensor data message @return: none """ - self.fp_pressures[fp_enum_repository.FPPressureSensorNames.M3_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.fp_pressures[fp_enum_repository.FPPressureSensorNames.P8_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.fp_pressures[fp_enum_repository.FPPressureSensorNames.P13_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.fp_pressures[fp_enum_repository.FPPressureSensorNames.P17_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.fp_pressures[fp_enum_repository.FPPressureSensorNames.P46_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPPressureSensorNames.M3_PRES.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPPressureSensorNames.P8_PRES.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPPressureSensorNames.P13_PRES.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPPressureSensorNames.P17_PRES.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPPressureSensorNames.P46_PRES.name, DataTypes.F32)) + self.process_into_dict(dict_to_update = self.fp_pressures, + decoder_list = sensor_list, + message = message) self.fp_pressure_timestamp = timestamp @@ -95,7 +96,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_PRESSURE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Pressure Sensors', logger = self.logger, @@ -123,7 +124,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_PRESSURE_OVERRIDE_REQUEST, entity_name = f'TD {sensor_name} Pressure Sensor pressure', override_text = f'{str(pressure)} psi', @@ -151,10 +152,9 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_PRESSURE_SENSOR_FILTER_READINGS_OVERRIDE_REQUEST, entity_name = f'TD {sensor_name} Pressure Sensor filtered pressure', override_text = f'{str(pressure)} psi', logger = self.logger, can_interface = self.can_interface) - Index: leahi_dialin/fp/modules/ro_pump.py =================================================================== diff -u -r99f9233b24e6115bb1384ec8f5a077aefc37ab4b -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/ro_pump.py (.../ro_pump.py) (revision 99f9233b24e6115bb1384ec8f5a077aefc37ab4b) +++ leahi_dialin/fp/modules/ro_pump.py (.../ro_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file ro_pump.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,7 +34,7 @@ Dialin API sub-class for FP RO pump controller related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ FPROPump constructor """ @@ -40,10 +43,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_ro_pump_data = MsgIds.MSG_ID_FP_RO_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_ro_pump_data, - self._handler_pump_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_RO_PUMP_DATA.value, + function = self._handler_pump_sync) self.ro_pump_timestamp = 0.0 #: The timestamp of the last message @@ -72,25 +74,21 @@ @param message: published FP ro pump data message @return: none """ - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.STATE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.DUTY_CYCLE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.SPEED.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.TARGET_PRES.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.TARGET_FLOW.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.TARGET_DUTY_CYCLE_PCT.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.DUTY_CYCLE_PCT.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] - self.ro_pump[fp_enum_repository.FPROPumpNames.P12_PUMP.name][fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE_PCT.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] + sensor_list =[] + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.STATE.name, DataTypes.U32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.DUTY_CYCLE.name, DataTypes.U32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE.name, DataTypes.U32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.SPEED.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.TARGET_PRES.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.TARGET_FLOW.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.TARGET_DUTY_CYCLE_PCT.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.DUTY_CYCLE_PCT.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPROPumpNames.P12_PUMP.name, fp_enum_repository.FPPumpAttributes.FB_DUTY_CYCLE_PCT.name, DataTypes.F32)) + sensor_list.appen + self.process_into_dict(dict_to_update = self.ro_pump, + decoder_list = sensor_list, + message = message) self.ro_pump_timestamp = timestamp @@ -108,7 +106,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP RO Pump', logger = self.logger, @@ -133,7 +131,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_PUMP_TARGET_PRESSURE_OVERRIDE_REQUEST, entity_name = f'FP P12 RO Pump target pressure', override_text = f'{str(pressure)} psi', @@ -159,7 +157,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_PUMP_TARGET_FLOW_OVERRIDE_REQUEST, entity_name = f'FP P12 RO Pump target flow', override_text = f'{str(flow)}', @@ -185,7 +183,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_PUMP_TARGET_PWM_OVERRIDE_REQUEST, entity_name = f'FP P12 RO Pump target PWM', override_text = f'{str(duty_cycle)}', @@ -205,7 +203,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_RO_PUMP_STOP_REQUEST, entity_name = f'FP P12 RO Pump hard stop', override_text = f'Active', Index: leahi_dialin/fp/modules/temperatures.py =================================================================== diff -u -r9f947930678d5b13aa8f788f4e3d58cd1370a541 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/temperatures.py (.../temperatures.py) (revision 9f947930678d5b13aa8f788f4e3d58cd1370a541) +++ leahi_dialin/fp/modules/temperatures.py (.../temperatures.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,38 +8,40 @@ # @file temperature_sensors.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Dara Navaei # @date (original) 01-Dec-2021 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository from leahi_dialin.common.fp_defs import BETA_IOFP_COND_SENSOR_OFFSET, BETA_IOFP_PRES_SENSOR_OFFSET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray class FPTemperatureSensors(AbstractSubSystem): - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_temperature_data = MsgIds.MSG_ID_FP_TEMPERATURE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_temperature_data, - self._handler_temperature_sensors_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_TEMPERATURE_DATA.value, + function = self._handler_temperature_sensors_sync) self.fp_temperature_sensors_timestamp = 0 #: The timestamp of the last message @@ -68,25 +70,21 @@ @returns none """ sensor_list =[] - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.M3_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P8_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P13_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P17_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P46_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P10_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P19_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P7_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P16_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P7_INTERNAL_TEMP, 'f')) - sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P16_INTERNAL_TEMP, 'f')) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.M3_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P8_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P13_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P17_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P46_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P10_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P19_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P7_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P16_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P7_INTERNAL_TEMP.name, DataTypes.F32)) + sensor_list.append((fp_enum_repository.FPTemperatureSensorNames.P16_INTERNAL_TEMP.name, DataTypes.F32)) - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - self.fp_temperatures[sensor[0].name] = struct.unpack(sensor[1], bytearray(message['message'][start_pos:end_pos]))[0] - i += 1 - + self.process_into_dict(dict_to_update = self.fp_temperatures, + decoder_list = sensor_list, + message = message) self.fp_temperature_sensors_timestamp = timestamp @@ -104,7 +102,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_TEMPERATURE_SENSOR_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Temperature Sensors', logger = self.logger, @@ -147,7 +145,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = msg_id, entity_name = f'FP {sensor_name} Temperature Sensor temperature', override_text = str(sensor_value), @@ -192,7 +190,7 @@ return cmd_generic_override( payload=payload, reset=reset, - channel_id=DenaliChannels.dialin_to_fp_ch_id, + channel_id=CanChannels.dialin_to_fp_ch_id, msg_id=msg_id, entity_name=f'FP {sensor_name} Temperature Sensor filtered temperature', override_text=str(sensor_value), Index: leahi_dialin/fp/modules/valves.py =================================================================== diff -u -r1b36df741816a4ef251028d721235daebea88558 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/valves.py (.../valves.py) (revision 1b36df741816a4ef251028d721235daebea88558) +++ leahi_dialin/fp/modules/valves.py (.../valves.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,23 +8,27 @@ # @file valves.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peman Montazemi # @date (original) 19-May-2020 # ############################################################################ +# Module imports import struct from logging import Logger from collections import OrderedDict +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository +from leahi_dialin.common.generic_defs import DataTypes from leahi_dialin.common.fp_defs import BETA_IOFP_VALVE_OFFSET -from leahi_dialin.common.msg_defs import MsgIds +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanMessage, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish from leahi_dialin.utils.conversions import integer_to_bytearray @@ -39,23 +43,23 @@ """ # Valves states publish message field positions - START_IO_VALVES_STATES = DenaliMessage.PAYLOAD_START_INDEX + START_IO_VALVES_STATES = CanMessage.PAYLOAD_START_INDEX END_IO_VALVES_STATES = START_IO_VALVES_STATES + 1 # IO Valves States come in as a U08 value (1 byte) START_FP_VALVES_STATES = END_IO_VALVES_STATES END_FP_VALVES_STATES = START_FP_VALVES_STATES + 1 # FP Valves States come in as a U08 value (1 byte) - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_sync_broadcast_ch_id - self.msg_id_fp_valves_states_data = MsgIds.MSG_ID_FP_VALVES_STATES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_fp_valves_states_data, self._handler_valves_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.fp_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_FP_VALVES_STATES_DATA.value, + function = self._handler_valves_sync) self.fp_valves_states_timestamp = 0.0 #: The timestamp of the last message self.valve_states_all = 0x0000 #: States of all the vales in binary format @@ -129,13 +133,13 @@ @return: none """ # Extract each valve state from U08 valves states using bit-masking - io_valves = struct.unpack('B', bytearray(message['message'][self.START_IO_VALVES_STATES:self.END_IO_VALVES_STATES])) + io_valves = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray(message['message'][self.START_IO_VALVES_STATES:self.END_IO_VALVES_STATES])) self.m4_valv["state"] = self._binary_to_valve_state(io_valves[0] & 1) self.m12_valv["state"] = self._binary_to_valve_state(io_valves[0] & 2) self.valve_states_all = io_valves[0] # Extract each valve state from U08 valves states using bit-masking - fp_valves = struct.unpack('B', bytearray(message['message'][self.START_FP_VALVES_STATES:self.END_FP_VALVES_STATES])) + fp_valves = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray(message['message'][self.START_FP_VALVES_STATES:self.END_FP_VALVES_STATES])) self.p11_valv["state"] = self._binary_to_valve_state(fp_valves[0] & 1) self.p33_valv["state"] = self._binary_to_valve_state(fp_valves[0] & 2) self.p34_valv["state"] = self._binary_to_valve_state(fp_valves[0] & 4) @@ -153,7 +157,7 @@ start = self.END_FP_VALVES_STATES end = start + 1 for valve_id in self.valves_sensed_states: - self.valves_sensed_states[valve_id] = struct.unpack('B', bytearray(message['message'][start:end]))[0] + self.valves_sensed_states[valve_id] = struct.unpack(DataTypes.U08.unpack_attrib(), bytearray(message['message'][start:end]))[0] start = end end += 1 self.fp_valves_states_timestamp = timestamp @@ -173,7 +177,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_VALVE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'FP Valves', logger = self.logger, @@ -201,7 +205,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_VALVE_SENSED_STATE_OVERRIDE_REQUEST, entity_name = f'FP {valve_name} Valve sensed state', override_text = str(state), @@ -230,7 +234,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_VALVE_CMD_STATE_OVERRIDE_REQUEST, entity_name = f'FP {valve_name} Valve state', override_text = str(state), Index: leahi_dialin/fp/modules/water_pumps.py =================================================================== diff -u -rfc671af6d1a18c900812e68c42ca14da3280e4f1 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/modules/water_pumps.py (.../water_pumps.py) (revision fc671af6d1a18c900812e68c42ca14da3280e4f1) +++ leahi_dialin/fp/modules/water_pumps.py (.../water_pumps.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,23 @@ # @file water_pumps.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ +# Module imports from logging import Logger from enum import unique +# Project imports from leahi_dialin.common.constants import NO_RESET from leahi_dialin.common import fp_enum_repository -from leahi_dialin.common.msg_defs import MsgIds +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, DialinEnum +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray @@ -31,7 +33,7 @@ Dialin API sub-class for FP water pump driver related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ FPPumps constructor """ @@ -42,25 +44,25 @@ # no current registered call back methods - def cmd_pump_set_speed_rate_override(self, pump: int, rpm: int, reset: int = NO_RESET) -> int: + def cmd_pump_set_speed_rate_override(self, pump: int, speed: int, reset: int = NO_RESET) -> int: """ Constructs and sends the boost pump set speed rate command Constraints: Must be logged into FP. - @param rpm: integer - speed set point (in rpm) + @param speed: integer - speed set point (in rpm) @return: 1 if successful, zero otherwise """ rst = integer_to_bytearray(reset) - rpm = integer_to_bytearray(rpm) + rpm = integer_to_bytearray(speed) pmp = integer_to_bytearray(pump) payload = rst + rpm + pmp pump_name = fp_enum_repository.FPAllPumpNames(pump).name return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLUID_PUMP_SPEED_OVERRIDE_REQUEST, entity_name = f'FP {pump_name} Water Pump speed', override_text = str(rpm), @@ -87,7 +89,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLUID_PUMP_SET_PWM_REQUEST, entity_name = f'FP {pump_name} Water Pump PWM', override_text = str(pwm), @@ -115,7 +117,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_fp_ch_id, + channel_id = CanChannels.dialin_to_fp_ch_id, msg_id = MsgIds.MSG_ID_FP_FLUID_PUMP_SET_PWM_REQUEST, entity_name = f'FP {pump_name} Water Pump read PWM', override_text = str(pwm), Index: leahi_dialin/fp/proxies/dd_proxy.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/fp/proxies/dd_proxy.py (.../dd_proxy.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/fp/proxies/dd_proxy.py (.../dd_proxy.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,19 +8,20 @@ # @file dd_proxy.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ +# Module imports from logging import Logger -from leahi_dialin.common.msg_defs import MsgIds -from leahi_dialin.common import fp_enum_repository +# Project imports +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.test_config_defs import DDFPTestConfigOptions -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliCanMessenger, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem +from leahi_dialin.protocols.CAN import CanMessenger, CanMessage, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray @@ -29,11 +30,11 @@ Filtration Purification (FP) Dialin API sub-class for DD proxy ( injection ) related commands. """ - def __init__(self, can_interface: DenaliCanMessenger, logger: Logger, fp): + def __init__(self, can_interface: CanMessenger, logger: Logger, fp): """ ROProxy constructor - @param can_interface: the Denali CAN interface object + @param can_interface: the CAN interface object """ super().__init__() self._fp = fp @@ -57,7 +58,7 @@ rtt = float_to_bytearray(ro_rate) payload = cmd + stt + rtt - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_fp_ch_id, + message = CanMessage.build_message(channel_id=CanChannels.dialin_to_fp_ch_id, message_id=MsgIds.MSG_ID_DD_FP_START_STOP_CMD_REQUEST.value, payload=payload) Index: leahi_dialin/protocols/CAN.py =================================================================== diff -u -rc93ef62631552b2ba3ffb77006d29f902e7a1128 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/protocols/CAN.py (.../CAN.py) (revision c93ef62631552b2ba3ffb77006d29f902e7a1128) +++ leahi_dialin/protocols/CAN.py (.../CAN.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -24,25 +24,22 @@ from can.interfaces import socketcan import math from time import sleep -from datetime import datetime import sys from logging import Logger import struct -from .. import common -from ..common import MsgIds, msg_defs +from ..common import MsgIds, MSG_HEADER_SIZE, ACK_NOT_REQUIRED from ..utils import SingletonMeta, IntervalTimer from concurrent.futures import ThreadPoolExecutor -import os -class DenaliMessage: +class CanMessage: BYTE_ORDER = 'little' START_BYTE = 0xA5 START_INDEX = 0 MSG_SEQ_INDEX = 1 MSG_ID_INDEX = 3 PAYLOAD_LENGTH_INDEX = 5 - PAYLOAD_START_INDEX = 6 + PAYLOAD_START_INDEX = MSG_HEADER_SIZE PAYLOAD_LENGTH_FIRST_PACKET = 1 HEADER_LENGTH = 6 PACKET_LENGTH = 8 @@ -86,7 +83,7 @@ @classmethod def build_message(cls, channel_id=0, message_id=0, payload=None, seq=None): """ - Builds a Denali message + Builds a CAN message @param channel_id: (int) indicates the channel @param message_id: (int) indicating the request type @@ -97,26 +94,26 @@ if payload is None: payload = [] - message_list = [DenaliCanMessenger.START_BYTE] + message_list = [CanMessenger.START_BYTE] - if 0 <= message_id <= DenaliMessage.MAX_MSG_ID_NUMBER: + if 0 <= message_id <= CanMessage.MAX_MSG_ID_NUMBER: if seq is None: # Wrap sequence number if it hits a max int16 if cls._seq_num >= 32767: cls._seq_num = 1 seq = cls._seq_num - if message_id not in common.msg_defs.ACK_NOT_REQUIRED: + if message_id not in ACK_NOT_REQUIRED: seq *= -1 - message_seq_in_bytes = seq.to_bytes(2, byteorder=DenaliMessage.BYTE_ORDER, signed=True) + message_seq_in_bytes = seq.to_bytes(2, byteorder=CanMessage.BYTE_ORDER, signed=True) message_list += [message_seq_in_bytes[0]] message_list += [message_seq_in_bytes[1]] # Add message ID as unsigned 16-bit # - message_id_in_bytes = message_id.to_bytes(2, byteorder=DenaliMessage.BYTE_ORDER) + message_id_in_bytes = message_id.to_bytes(2, byteorder=CanMessage.BYTE_ORDER) message_list += [message_id_in_bytes[0]] message_list += [message_id_in_bytes[1]] @@ -131,7 +128,7 @@ payload_length = len(payload) # if payload is larger than 255 return nothing - if payload_length <= DenaliMessage.MAX_NUMBER_OF_PAYLOAD_BYTES: + if payload_length <= CanMessage.MAX_NUMBER_OF_PAYLOAD_BYTES: # payload has to be a list message_list += [payload_length] else: @@ -140,11 +137,11 @@ message_list += payload # Because CRC does not include first byte, then we pass a list with out it - message_list += [DenaliMessage.crc8(message_list[1:])] + message_list += [CanMessage.crc8(message_list[1:])] - message_list = DenaliMessage.pad_message_with_zeros(message_list) + message_list = CanMessage.pad_message_with_zeros(message_list) - return DenaliMessage.build_basic_message(channel_id=channel_id, message=message_list) + return CanMessage.build_basic_message(channel_id=channel_id, message=message_list) @staticmethod def crc8(message_list): @@ -157,7 +154,7 @@ crc = 0 for byte in message_list: unsigned_byte = byte ^ crc - crc = DenaliMessage.CRC_LIST[unsigned_byte] + crc = CanMessage.CRC_LIST[unsigned_byte] return crc @@ -172,10 +169,10 @@ message_length = len(message) # message must be multiple of 8 - if message_length % DenaliMessage.PACKET_LENGTH != 0: + if message_length % CanMessage.PACKET_LENGTH != 0: # We need to pad the message with trailing zeros - add_these_many_zeros = math.ceil(message_length / DenaliMessage.PACKET_LENGTH) * \ - DenaliMessage.PACKET_LENGTH - message_length + add_these_many_zeros = math.ceil(message_length / CanMessage.PACKET_LENGTH) * \ + CanMessage.PACKET_LENGTH - message_length message += [0] * add_these_many_zeros @@ -190,7 +187,7 @@ @return:: CRC in message """ - crc_index = DenaliMessage.PAYLOAD_START_INDEX + DenaliMessage.get_payload_length(message) + crc_index = CanMessage.PAYLOAD_START_INDEX + CanMessage.get_payload_length(message) return message['message'][crc_index] @@ -206,9 +203,9 @@ else: message_list = message['message'] - message_length = DenaliMessage.PAYLOAD_START_INDEX + DenaliMessage.get_payload_length(message) - calculated_crc = DenaliMessage.crc8(message_list[1:message_length]) - actual_crc = DenaliMessage.get_crc(message) + message_length = CanMessage.PAYLOAD_START_INDEX + CanMessage.get_payload_length(message) + calculated_crc = CanMessage.crc8(message_list[1:message_length]) + actual_crc = CanMessage.get_crc(message) return calculated_crc == actual_crc @@ -230,8 +227,8 @@ @param message: dictionary containing the message @return:: (int) the sequence number """ - seq = message['message'][DenaliMessage.MSG_SEQ_INDEX:DenaliMessage.MSG_ID_INDEX] - return int.from_bytes(seq, byteorder=DenaliMessage.BYTE_ORDER, signed=True) + seq = message['message'][CanMessage.MSG_SEQ_INDEX:CanMessage.MSG_ID_INDEX] + return int.from_bytes(seq, byteorder=CanMessage.BYTE_ORDER, signed=True) @staticmethod def create_ack_message(message: dict, passive_mode: bool = True): @@ -245,59 +242,59 @@ message = message.copy() seq = struct.unpack('h', bytearray( - message['message'][DenaliMessage.MSG_SEQ_INDEX:DenaliMessage.MSG_ID_INDEX]))[0] + message['message'][CanMessage.MSG_SEQ_INDEX:CanMessage.MSG_ID_INDEX]))[0] # send back empty payload since this is an ACK payload = bytearray() - channel_id_rx = DenaliMessage.get_channel_id(message) + channel_id_rx = CanMessage.get_channel_id(message) if passive_mode: channel_rx_tx_pairs = { - DenaliChannels.td_alarm_broadcast_ch_id: None, - DenaliChannels.dd_alarm_broadcast_ch_id: None, - DenaliChannels.fp_alarm_broadcast_ch_id: None, - DenaliChannels.ui_alarm_broadcast_ch_id: None, - DenaliChannels.td_to_dd_ch_id: None, - DenaliChannels.dd_to_td_ch_id: None, - DenaliChannels.td_to_ui_ch_id: None, - DenaliChannels.td_sync_broadcast_ch_id: None, - DenaliChannels.dd_sync_broadcast_ch_id: None, - DenaliChannels.fp_sync_broadcast_ch_id: None, - DenaliChannels.ui_to_td_ch_id: None, - DenaliChannels.ui_sync_broadcast_ch_id: None, - DenaliChannels.td_to_dialin_ch_id: DenaliChannels.dialin_to_td_ch_id, - DenaliChannels.dd_to_dialin_ch_id: DenaliChannels.dialin_to_dd_ch_id, - DenaliChannels.fp_to_dialin_ch_id: DenaliChannels.dialin_to_fp_ch_id, - DenaliChannels.ui_to_dialin_ch_id: DenaliChannels.dialin_to_ui_ch_id + CanChannels.td_alarm_broadcast_ch_id: None, + CanChannels.dd_alarm_broadcast_ch_id: None, + CanChannels.fp_alarm_broadcast_ch_id: None, + CanChannels.ui_alarm_broadcast_ch_id: None, + CanChannels.td_to_dd_ch_id: None, + CanChannels.dd_to_td_ch_id: None, + CanChannels.td_to_ui_ch_id: None, + CanChannels.td_sync_broadcast_ch_id: None, + CanChannels.dd_sync_broadcast_ch_id: None, + CanChannels.fp_sync_broadcast_ch_id: None, + CanChannels.ui_to_td_ch_id: None, + CanChannels.ui_sync_broadcast_ch_id: None, + CanChannels.td_to_dialin_ch_id: CanChannels.dialin_to_td_ch_id, + CanChannels.dd_to_dialin_ch_id: CanChannels.dialin_to_dd_ch_id, + CanChannels.fp_to_dialin_ch_id: CanChannels.dialin_to_fp_ch_id, + CanChannels.ui_to_dialin_ch_id: CanChannels.dialin_to_ui_ch_id } else: channel_rx_tx_pairs = { - DenaliChannels.td_alarm_broadcast_ch_id: None, - DenaliChannels.dd_alarm_broadcast_ch_id: None, - DenaliChannels.fp_alarm_broadcast_ch_id: None, - DenaliChannels.ui_alarm_broadcast_ch_id: None, - DenaliChannels.td_to_dd_ch_id: DenaliChannels.dialin_to_td_ch_id, - DenaliChannels.dd_to_td_ch_id: DenaliChannels.dialin_to_dd_ch_id, - DenaliChannels.td_to_ui_ch_id: DenaliChannels.dialin_to_td_ch_id, - DenaliChannels.td_sync_broadcast_ch_id: DenaliChannels.dialin_to_td_ch_id, - DenaliChannels.dd_sync_broadcast_ch_id: DenaliChannels.dialin_to_dd_ch_id, - DenaliChannels.fp_sync_broadcast_ch_id: DenaliChannels.dialin_to_fp_ch_id, - DenaliChannels.ui_to_td_ch_id: DenaliChannels.td_to_ui_ch_id, - DenaliChannels.ui_sync_broadcast_ch_id: DenaliChannels.dialin_to_ui_ch_id, - DenaliChannels.td_to_dialin_ch_id: DenaliChannels.dialin_to_td_ch_id, - DenaliChannels.dd_to_dialin_ch_id: DenaliChannels.dialin_to_dd_ch_id, - DenaliChannels.ui_to_dialin_ch_id: DenaliChannels.dialin_to_ui_ch_id + CanChannels.td_alarm_broadcast_ch_id: None, + CanChannels.dd_alarm_broadcast_ch_id: None, + CanChannels.fp_alarm_broadcast_ch_id: None, + CanChannels.ui_alarm_broadcast_ch_id: None, + CanChannels.td_to_dd_ch_id: CanChannels.dialin_to_td_ch_id, + CanChannels.dd_to_td_ch_id: CanChannels.dialin_to_dd_ch_id, + CanChannels.td_to_ui_ch_id: CanChannels.dialin_to_td_ch_id, + CanChannels.td_sync_broadcast_ch_id: CanChannels.dialin_to_td_ch_id, + CanChannels.dd_sync_broadcast_ch_id: CanChannels.dialin_to_dd_ch_id, + CanChannels.fp_sync_broadcast_ch_id: CanChannels.dialin_to_fp_ch_id, + CanChannels.ui_to_td_ch_id: CanChannels.td_to_ui_ch_id, + CanChannels.ui_sync_broadcast_ch_id: CanChannels.dialin_to_ui_ch_id, + CanChannels.td_to_dialin_ch_id: CanChannels.dialin_to_td_ch_id, + CanChannels.dd_to_dialin_ch_id: CanChannels.dialin_to_dd_ch_id, + CanChannels.ui_to_dialin_ch_id: CanChannels.dialin_to_ui_ch_id } channel_id_tx = channel_rx_tx_pairs.get(channel_id_rx, None) if channel_id_tx is None: return None - message = DenaliMessage.build_message( + message = CanMessage.build_message( channel_id=channel_id_tx, - message_id=common.msg_defs.MsgIds.MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK.value, + message_id=MsgIds.MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK.value, payload=payload, seq=-seq) @@ -310,9 +307,9 @@ @param message: complete Diality Packet @return:: integer with request ID """ - msg_id_array = message['message'][DenaliMessage.MSG_ID_INDEX: - DenaliMessage.PAYLOAD_LENGTH_INDEX] - return int.from_bytes(msg_id_array, DenaliMessage.BYTE_ORDER) + msg_id_array = message['message'][CanMessage.MSG_ID_INDEX: + CanMessage.PAYLOAD_LENGTH_INDEX] + return int.from_bytes(msg_id_array, CanMessage.BYTE_ORDER) @staticmethod def get_message_id_xstr(message): @@ -322,7 +319,7 @@ @return:: integer with request ID """ msg_id = "" - for index in range(DenaliMessage.MSG_ID_INDEX, DenaliMessage.PAYLOAD_LENGTH_INDEX): + for index in range(CanMessage.MSG_ID_INDEX, CanMessage.PAYLOAD_LENGTH_INDEX): msg_id += "{0:02X}" .format(message['message'][index]) return msg_id @@ -334,7 +331,7 @@ @param message: dictionary with channel_id and message keys @return:: a unsigned payload length """ - return message['message'][DenaliMessage.PAYLOAD_LENGTH_INDEX] + return message['message'][CanMessage.PAYLOAD_LENGTH_INDEX] @staticmethod def get_payload(message): @@ -344,32 +341,32 @@ @param message: dictionary with channel_id and message keys @return:: a payload array if exist """ - payload_length = DenaliMessage.get_payload_length(message) + payload_length = CanMessage.get_payload_length(message) if payload_length == 0: return None else: - return message['message'][DenaliMessage.PAYLOAD_START_INDEX:] + return message['message'][CanMessage.PAYLOAD_START_INDEX:] @staticmethod def get_total_packets(message, is_array=True): """ - Returns the number of packets needed to transmit Denali Message + Returns the number of packets needed to transmit CAN Message @param message: dictionary with channel_id and message keys or raw message @param is_array: True if message is an array and not a dictionary @return:: number of packets """ the_message = message if is_array else message['message'] - return math.ceil((the_message[DenaliMessage.PAYLOAD_LENGTH_INDEX] + - DenaliMessage.HEADER_LENGTH + DenaliMessage.CRC_LENGTH) / - DenaliMessage.PACKET_LENGTH) + return math.ceil((the_message[CanMessage.PAYLOAD_LENGTH_INDEX] + + CanMessage.HEADER_LENGTH + CanMessage.CRC_LENGTH) / + CanMessage.PACKET_LENGTH) -class DenaliChannels: +class CanChannels: """ - Convenience class listing all the possible CAN channels used in the Denali system. + Convenience class listing all the possible CAN channels used in the Diality systems. Updates made to the "Message List.xlsx" document found in the Diality Software Team SharePoint, specifically in the CAN Channels sheet, should also be applied here. """ @@ -397,17 +394,17 @@ ui_to_dialin_ch_id = 0x407 -class LongDenaliMessageBuilder: +class LongMessageBuilder: def __init__(self, message: can.Message): """ - LongDialityMessageBuilder is a utility object that helps construct a Denali message + LongMessageBuilder is a utility object that helps construct a message that is longer than 8 bytes. It is only called when we don't yet have a long message builder for the current channel. Basic principle is to construct an object with the first 8 byte message which contains the length of the message, and the later push the remaining messages. e.g., let's imagine a 3 message packet. - obj = LongDialityMessageBuilder(msg1) + obj = LongMessageBuilder(msg1) message = obj.push(msg2), returns None, message is not complete @@ -416,7 +413,7 @@ @param message: a CAN message """ self.message_data = [b for b in message.data] - self.number_of_can_packets_needed = DenaliMessage.get_total_packets(self.message_data) + self.number_of_can_packets_needed = CanMessage.get_total_packets(self.message_data) self.number_of_can_packets_up_to_now = 1 def push(self, message: can.Message, first_packet=False): @@ -432,7 +429,7 @@ message_data = [b for b in message.data] if first_packet: self.message_data = message_data - self.number_of_can_packets_needed = DenaliMessage.get_total_packets(message_data) + self.number_of_can_packets_needed = CanMessage.get_total_packets(message_data) self.number_of_can_packets_up_to_now = 1 else: @@ -448,16 +445,16 @@ return None -class DenaliCanMessenger(metaclass=SingletonMeta): - START_BYTE = DenaliMessage.START_BYTE +class CanMessenger(metaclass=SingletonMeta): + START_BYTE = CanMessage.START_BYTE DIALIN_MSG_RESP_TO = 0.5 # number of seconds to wait for a response to a send command def __init__(self, can_interface: str, logger: Logger, passive_mode=True, console_out=False): """ - DenaliCanMessenger constructor + CanMessenger constructor @param can_interface - string containing the can interface, e.g., 'can0" @return: DialityCanMessenger object @@ -575,72 +572,72 @@ self.message_queue_mutex.acquire() message: can.Message = self.message_queue.popleft() - if message.dlc == DenaliMessage.PACKET_LENGTH: + if message.dlc == CanMessage.PACKET_LENGTH: # We have received a legit CAN message of 8 bytes can_data = [b for b in message.data] channel_id = message.arbitration_id - if not DenaliMessage.PAYLOAD_LENGTH_INDEX < len(can_data): - self.logger.error("Invalid Denali message received: {0}".format(message)) + if not CanMessage.PAYLOAD_LENGTH_INDEX < len(can_data): + self.logger.error("Invalid CAN message received: {0}".format(message)) self.messages = None # Can't process this message, get the next one self.message_queue_mutex.release() continue else: - message_length = can_data[DenaliMessage.PAYLOAD_LENGTH_INDEX] + message_length = can_data[CanMessage.PAYLOAD_LENGTH_INDEX] # if we are building a long message, then proceed to push it to the channel dictionary if channel_id in self.long_msg_channel_id_set: self.messages = self.long_message_builders[channel_id].push(message) - elif can_data[0] == DenaliMessage.START_BYTE and \ - message_length <= DenaliMessage.PAYLOAD_LENGTH_FIRST_PACKET: # This is a short packet + elif can_data[0] == CanMessage.START_BYTE and \ + message_length <= CanMessage.PAYLOAD_LENGTH_FIRST_PACKET: # This is a short packet # This is the first time that we are building a message self.messages = can_data # deliver the packet elif can_data[0] == self.START_BYTE and \ - message_length > DenaliMessage.PAYLOAD_LENGTH_FIRST_PACKET: # Long packet start + message_length > CanMessage.PAYLOAD_LENGTH_FIRST_PACKET: # Long packet start # We are starting to build a long message, include it in the lonMsgChannelIDSet self.long_msg_channel_id_set.add(channel_id) - # if we don't have a long Denali message builder yet, create it + # if we don't have a long CAN message builder yet, create it if channel_id not in self.long_message_builders.keys(): - self.long_message_builders[channel_id] = LongDenaliMessageBuilder(message) + self.long_message_builders[channel_id] = LongMessageBuilder(message) self.messages = None else: # if we do have a builder. This is the first time self.long_message_builders[channel_id].push(message, first_packet=True) self.messages = None - # Do we have a complete (long or short) Denali Message? + # Do we have a complete (long or short) CAN Message? if self.messages is not None: message_valid = True # assume true for now, set to false if CRC check fails below - complete_dialin_message = DenaliMessage.build_basic_message(channel_id=channel_id, + complete_dialin_message = CanMessage.build_basic_message(channel_id=channel_id, message=self.messages) - dialin_msg_id = DenaliMessage.get_message_id(complete_dialin_message) - dialin_ch_id = DenaliMessage.get_channel_id(complete_dialin_message) + dialin_msg_id = CanMessage.get_message_id(complete_dialin_message) + dialin_ch_id = CanMessage.get_channel_id(complete_dialin_message) # TODO for debugging purposes. Change to configurable setting. #self.temp_dialin_processed_logger.write(str(complete_dialin_message)) if dialin_ch_id in self.long_msg_channel_id_set: # We need to remove channel ID from the long message set self.long_msg_channel_id_set.remove(dialin_ch_id) # Need to verify CRC at this point - if not DenaliMessage.verify_crc(complete_dialin_message): + if not CanMessage.verify_crc(complete_dialin_message): # if verify is False, let's drop (ignore) this message message_valid = False dialin_ch_id = None dialin_msg_id = None self.logger.critical( "Incorrect CRC, received message: {}, crc: {}, calculated crc: {}\n".format( - self.messages, DenaliMessage.get_crc(complete_dialin_message), - DenaliMessage.crc8(self.messages))) + self.messages, CanMessage.get_crc(complete_dialin_message), + CanMessage.crc8(self.messages))) if message_valid: if self.console_out: self.do_console_out(complete_dialin_message) # Send an ack if required - if DenaliMessage.get_sequence_number(complete_dialin_message) < 0: + if CanMessage.get_sequence_number(complete_dialin_message) < 0: # ACK required. Send back the received message with the sequence sign bit flipped - msg = DenaliMessage.create_ack_message(message=complete_dialin_message, + msg = CanMessage.create_ack_message(message=complete_dialin_message, passive_mode=False) if msg is not None: self.send(msg, 0, is_ack=True) @@ -655,7 +652,7 @@ else: self.response_dictionary_mutex.acquire() - if DenaliCanMessenger.is_ui_received_channel(dialin_ch_id): # check if the channel is in ui channels + if CanMessenger.is_ui_received_channel(dialin_ch_id): # check if the channel is in ui channels if self.ui_received_function_ptr is not None: self.thread_pool_executor.submit( self.ui_received_function_ptr, @@ -685,10 +682,10 @@ @return: true, if the channel is the ui channel. """ if channel_id in { - DenaliChannels.ui_to_td_ch_id, - DenaliChannels.ui_to_dialin_ch_id, - DenaliChannels.ui_sync_broadcast_ch_id, - DenaliChannels.ui_alarm_broadcast_ch_id + CanChannels.ui_to_td_ch_id, + CanChannels.ui_to_dialin_ch_id, + CanChannels.ui_sync_broadcast_ch_id, + CanChannels.ui_alarm_broadcast_ch_id }: # check if the channel is in ui channels return True else: @@ -733,13 +730,13 @@ resend: bool = False, is_ack: bool = False): """ - Sends a Denali message + Sends a CAN message @param built_message: (dict) message built using DialinMessage class @param time_out: (float) time it will wait for a response in seconds @param resend: (bool) Allow resending the message when no response is received. Disabled by default @param is_ack: (bool) If we're sending an ACK, False by default - @return: (dict) The Denali packet. If a timeout occurs returns None + @return: (dict) The CAN packet. If a timeout occurs returns None """ data = str(time.time()) + ',' + str(built_message) + '\r' # TODO for debugging purposes. Change to configurable setting. @@ -750,25 +747,25 @@ # keep trying to send message until we get a response while not msg_sent: - channel_id = DenaliMessage.get_channel_id(built_message) + channel_id = CanMessage.get_channel_id(built_message) padded_can_message_array = built_message['message'] - msg_id = DenaliMessage.get_message_id(built_message) + msg_id = CanMessage.get_message_id(built_message) if not is_ack and msg_id not in self.pending_requests: self.pending_requests[msg_id] = None # A message can be longer than 8 bytes, so we need to split it # into 8 bytes packets. - number_of_packets = DenaliMessage.get_total_packets(padded_can_message_array) + number_of_packets = CanMessage.get_total_packets(padded_can_message_array) # We are sending one message at a time on CAN self.transmitting_mutex.acquire() for n in range(number_of_packets): - packet = padded_can_message_array[n * DenaliMessage.PACKET_LENGTH: - (n + 1) * DenaliMessage.PACKET_LENGTH] + packet = padded_can_message_array[n * CanMessage.PACKET_LENGTH: + (n + 1) * CanMessage.PACKET_LENGTH] # Sending one packet at a time packet = can.Message(arbitration_id=channel_id, @@ -843,23 +840,23 @@ @staticmethod def convert_message_to_string(complete_dialin_message: dict) -> str: """ - Converts the DenaliMessage to hex string (data len is not hex) + Converts the CanMessage to hex string (data len is not hex) @param complete_dialin_message: the complete can message in dictionary @return: """ - channel = "{0:03X}" .format(DenaliMessage.get_channel_id(complete_dialin_message)) - msg_id = DenaliMessage.get_message_id_xstr(complete_dialin_message) - data_len = DenaliMessage.get_payload_length(complete_dialin_message) + channel = "{0:03X}" .format(CanMessage.get_channel_id(complete_dialin_message)) + msg_id = CanMessage.get_message_id_xstr(complete_dialin_message) + data_len = CanMessage.get_payload_length(complete_dialin_message) length = "{0:02X}" .format(data_len) data = "" pram_len = 0 if data_len != 0: pram_len = int(data_len / 4) for i in range(pram_len): - data += "{0:02X}".format(complete_dialin_message['message'][DenaliMessage.PAYLOAD_START_INDEX + i ]) - data += "{0:02X}".format(complete_dialin_message['message'][DenaliMessage.PAYLOAD_START_INDEX + i + 1]) - data += "{0:02X}".format(complete_dialin_message['message'][DenaliMessage.PAYLOAD_START_INDEX + i + 2]) - data += "{0:02X}".format(complete_dialin_message['message'][DenaliMessage.PAYLOAD_START_INDEX + i + 3]) + data += "{0:02X}".format(complete_dialin_message['message'][CanMessage.PAYLOAD_START_INDEX + i ]) + data += "{0:02X}".format(complete_dialin_message['message'][CanMessage.PAYLOAD_START_INDEX + i + 1]) + data += "{0:02X}".format(complete_dialin_message['message'][CanMessage.PAYLOAD_START_INDEX + i + 2]) + data += "{0:02X}".format(complete_dialin_message['message'][CanMessage.PAYLOAD_START_INDEX + i + 3]) data += " " message = "{} {} {} {}".format(channel, msg_id, data_len, data) return message @@ -874,11 +871,11 @@ MsgIds.MSG_ID_UI_CHECK_IN.value, MsgIds.MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK } - msg_id = DenaliMessage.get_message_id(complete_dialin_message) + msg_id = CanMessage.get_message_id(complete_dialin_message) if msg_id in exception_msg_id: return - message = "# " + DenaliCanMessenger.convert_message_to_string(complete_dialin_message) + message = "# " + CanMessenger.convert_message_to_string(complete_dialin_message) print(message) def register_transmitting_interval_message(self, interval: float, function) ->None: Index: leahi_dialin/protocols/__init__.py =================================================================== diff -u -r3715028b608b10e1d440a8a738c8a4f70d3d411b -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/protocols/__init__.py (.../__init__.py) (revision 3715028b608b10e1d440a8a738c8a4f70d3d411b) +++ leahi_dialin/protocols/__init__.py (.../__init__.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -16,7 +16,7 @@ if ok: print(f"{sw_key} is set to bypass the auto imports.") else: - from .CAN import DenaliCanMessenger - from .CAN import DenaliChannels - from .CAN import LongDenaliMessageBuilder - from .CAN import DenaliMessage + from .CAN import CanMessenger + from .CAN import CanChannels + from .CAN import LongMessageBuilder + from .CAN import CanMessage Index: leahi_dialin/td/modules/air_pump.py =================================================================== diff -u -r3f6c0e94db8379f00cb7fbc91daa189d6b06f379 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/air_pump.py (.../air_pump.py) (revision 3f6c0e94db8379f00cb7fbc91daa189d6b06f379) +++ leahi_dialin/td/modules/air_pump.py (.../air_pump.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file air_pump.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Micahel Garthwaite # @date (original) 01-Nov-2024 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray MAX_AIR_PUMP_POWER_LEVEL = 255 #Maximum air pump power allowed @@ -34,19 +37,18 @@ Treatment Delivery (TD) Dialin API sub-class for air pump related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_air_pump_data = MsgIds.MSG_ID_TD_AIR_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_air_pump_data, - self._handler_air_pump_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_AIR_PUMP_DATA.value, + function = self._handler_air_pump_sync) self.td_air_pump_timestamp = 0.0 #: The timestamp of the last message @@ -73,21 +75,17 @@ @return: None """ sensor_list =[] - sensor_list.append((td_enum_repository.TDAirPumpAttributes.STATE.name, 'i')) - sensor_list.append((td_enum_repository.TDAirPumpAttributes.DUTY_CYCLE.name, 'f')) - sensor_list.append((td_enum_repository.TDAirPumpAttributes.RPM.name, 'i')) + sensor_list.append((td_enum_repository.TDAirPumpNames.H12_AIR_PUMP.name, td_enum_repository.TDAirPumpAttributes.STATE.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDAirPumpNames.H12_AIR_PUMP.name, td_enum_repository.TDAirPumpAttributes.DUTY_CYCLE.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDAirPumpNames.H12_AIR_PUMP.name, td_enum_repository.TDAirPumpAttributes.RPM.name, DataTypes.U32)) # TODO remove after FPGA air pump speed validation - sensor_list.append((td_enum_repository.TDAirPumpAttributes.FPGA_RPM.name, 'i')) + sensor_list.append((td_enum_repository.TDAirPumpNames.H12_AIR_PUMP.name, td_enum_repository.TDAirPumpAttributes.FPGA_RPM.name, DataTypes.U32)) # TODO remove after FPGA air pump speed validation - sensor_list.append((td_enum_repository.TDAirPumpAttributes.SCALAR_POWER.name, 'i')) + sensor_list.append((td_enum_repository.TDAirPumpNames.H12_AIR_PUMP.name, td_enum_repository.TDAirPumpAttributes.SCALAR_POWER.name, DataTypes.U32)) - i = 1 - for sensor in sensor_list: - start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') - end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - self.td_air_pump[td_enum_repository.TDAirPumpNames.H12_AIR_PUMP.name][sensor[0]] = struct.unpack(sensor[1], bytearray(message['message'][start_pos:end_pos]))[0] - i += 1 - + self.process_into_dict(dict_to_update = self.td_air_pump, + decoder_list = sensor_list, + message = message) self.td_air_pump_timestamp = timestamp @@ -105,7 +103,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Air Pump', logger = self.logger, @@ -135,7 +133,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_PUMP_SET_STATE_REQUEST, entity_name = f'TD Air Pump to {state_name}', override_text = f'{power_value}', Index: leahi_dialin/td/modules/air_trap.py =================================================================== diff -u -r6f76dccbc46cfa9f663285d2febd7588cec49bb3 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/air_trap.py (.../air_trap.py) (revision 6f76dccbc46cfa9f663285d2febd7588cec49bb3) +++ leahi_dialin/td/modules/air_trap.py (.../air_trap.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file air_trap.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Sean Nash # @date (original) 21-Sep-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -37,19 +40,18 @@ AIR_DETECTED_AT_LEVEL = 0 FLUID_DETECTED_AT_LEVEL = 1 - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_air_trap_data = MsgIds.MSG_ID_TD_AIR_TRAP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_air_trap_data, - self._handler_air_trap_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_AIR_TRAP_DATA.value, + function = self._handler_air_trap_sync) self.td_air_trap_timestamp = 0.0 #: The timestamp of the last message @@ -91,39 +93,30 @@ @param message: published air trap data message @return: None """ - lower = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - upper = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) - raw_lower = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) - raw_upper = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) - h13_vlv_st = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) - h20_vlv_st = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) - controlling = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) - air_trap_state = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) - #TODO remove after testing - lower_power = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9])) - # TODO remove after testing - raise_power = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_10:MsgFieldPositions.END_POS_FIELD_10])) + sensor_list_1 =[] + sensor_list_1.append((td_enum_repository.TDAirTrapLevelSensorNames.H17_LEVEL.name, td_enum_repository.TDAirTrapLevelSensorAttributes.LEVEL.name, DataTypes.U32)) + sensor_list_1.append((td_enum_repository.TDAirTrapLevelSensorNames.H16_LEVEL.name, td_enum_repository.TDAirTrapLevelSensorAttributes.LEVEL.name, DataTypes.U32)) + sensor_list_1.append((td_enum_repository.TDAirTrapLevelSensorNames.H17_LEVEL.name, td_enum_repository.TDAirTrapLevelSensorAttributes.RAW.name, DataTypes.U32)) + sensor_list_1.append((td_enum_repository.TDAirTrapLevelSensorNames.H16_LEVEL.name, td_enum_repository.TDAirTrapLevelSensorAttributes.RAW.name, DataTypes.U32)) + sensor_list_2 =[] + sensor_list_2.append((td_enum_repository.TDAirTrapValveNames.H13_VALV.name, DataTypes.U32)) + sensor_list_2.append((td_enum_repository.TDAirTrapValveNames.H20_VALV.name, DataTypes.U32)) + msg_list =[] + msg_list.append(('self.air_control', DataTypes.U32)) + msg_list.append(('self.airTrapState', DataTypes.U32)) + msg_list.append(('self.AirPumpLowerPowerLevel', DataTypes.U32)) + msg_list.append(('self.AirPumpRaisePowerLevel', DataTypes.U32)) - self.air_trap_level_sensors[td_enum_repository.TDAirTrapLevelSensorNames.H17_LEVEL.name][td_enum_repository.TDAirTrapLevelSensorAttributes.LEVEL.name] = lower[0] - self.air_trap_level_sensors[td_enum_repository.TDAirTrapLevelSensorNames.H16_LEVEL.name][td_enum_repository.TDAirTrapLevelSensorAttributes.LEVEL.name] = upper[0] - self.air_trap_level_sensors[td_enum_repository.TDAirTrapLevelSensorNames.H17_LEVEL.name][td_enum_repository.TDAirTrapLevelSensorAttributes.RAW.name] = raw_lower[0] - self.air_trap_level_sensors[td_enum_repository.TDAirTrapLevelSensorNames.H16_LEVEL.name][td_enum_repository.TDAirTrapLevelSensorAttributes.RAW.name] = raw_upper[0] - self.air_trap_valve_states[td_enum_repository.TDAirTrapValveNames.H13_VALV.name] = h13_vlv_st[0] - self.air_trap_valve_states[td_enum_repository.TDAirTrapValveNames.H20_VALV.name] = h20_vlv_st[0] - self.air_control = controlling[0] - self.airTrapState = air_trap_state[0] - self.AirPumpLowerPowerLevel = lower_power[0] - self.AirPumpRaisePowerLevel = raise_power[0] + self.process_into_dict(dict_to_update = self.air_trap_level_sensors, + decoder_list = sensor_list_1, + message = message) + self.process_into_dict(dict_to_update = self.air_trap_valve_states, + decoder_list = sensor_list_2, + message = message, + start_from_byte = len(sensor_list_1) * DataTypes.U32.size()) + self.process_into_vars(decoder_list = msg_list, + message = message, + start_from_byte = (len(sensor_list_1) + len(sensor_list_2)) * DataTypes.U32.size()) self.td_air_trap_timestamp = timestamp @@ -141,7 +134,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_TRAP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Air Trap', logger = self.logger, @@ -168,7 +161,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_TRAP_LEVEL_OVERRIDE_REQUEST, entity_name = f'TD {sensor_name} Air Trap level sensor', override_text = f'{str(detected)}', @@ -196,7 +189,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_TRAP_LEVEL_RAW_OVERRIDE_REQUEST, entity_name = f'TD {sensor_name} Air Trap level sensor raw', override_text = f'{str(detected)}', @@ -221,7 +214,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_2_WAY_VALVE_SET_STATE_REQUEST, entity_name = f'TD Air Trap {valve_name} Valve state', override_text = f'{str(valve_state)}', @@ -242,7 +235,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_SET_AIR_TRAP_CONTROL, entity_name = f'TD Air Trap control', override_text = f'{str(control)}', @@ -268,7 +261,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_PUMP_POWER_RAISE_OVERRIDE_REQUEST, entity_name = f'TD Air Pump Raise power', override_text = f'{str(power)}', @@ -294,7 +287,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_AIR_PUMP_POWER_LOWER_OVERRIDE_REQUEST, entity_name = f'TD Air Pump Lower power', override_text = f'{str(power)}', Index: leahi_dialin/td/modules/alarms.py =================================================================== diff -u -r47a803c6dd859a5bcc7a6c82e0cb160c350473ea -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/alarms.py (.../alarms.py) (revision 47a803c6dd859a5bcc7a6c82e0cb160c350473ea) +++ leahi_dialin/td/modules/alarms.py (.../alarms.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,24 +8,29 @@ # @file alarms.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +import struct from enum import unique +# Project imports from leahi_dialin.common.alarm_defs import AlarmList from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, DialinEnum, publish +from leahi_dialin.protocols.CAN import CanMessenger, CanMessage, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish from leahi_dialin.utils.conversions import integer_to_bytearray, float_to_bytearray +from leahi_dialin.utils.enums import DialinEnum class TDAlarms(AbstractSubSystem): @@ -58,7 +63,7 @@ AlarmResponseButtons._str_list = {} # Alarm status message field positions - START_POS_ALARM_STATE = DenaliMessage.PAYLOAD_START_INDEX + START_POS_ALARM_STATE = CanMessage.PAYLOAD_START_INDEX END_POS_ALARM_STATE = START_POS_ALARM_STATE + 4 START_POS_ALARM_TOP = END_POS_ALARM_STATE END_POS_ALARM_TOP = START_POS_ALARM_TOP + 4 @@ -69,36 +74,36 @@ START_POS_ALARMS_FLAGS = END_POS_ALARM_SILENCE_EXPIRES_IN END_POS_ALARMS_FLAGS = START_POS_ALARMS_FLAGS + 2 - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - self.can_interface.register_receiving_publication_function(channel_id = DenaliChannels.td_alarm_broadcast_ch_id, + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_alarm_broadcast_ch_id, message_id = MsgIds.MSG_ID_ALARM_STATUS_DATA.value, function = self._handler_alarms_status_sync) - self.can_interface.register_receiving_publication_function(channel_id = DenaliChannels.td_alarm_broadcast_ch_id, + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_alarm_broadcast_ch_id, message_id = MsgIds.MSG_ID_ALARM_TRIGGERED.value, function = self._handler_alarm_trigger) - self.can_interface.register_receiving_publication_function(channel_id = DenaliChannels.td_alarm_broadcast_ch_id, + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_alarm_broadcast_ch_id, message_id = MsgIds.MSG_ID_ALARM_CLEARED.value, function = self._handler_alarm_clear) - self.can_interface.register_receiving_publication_function(channel_id = DenaliChannels.td_alarm_broadcast_ch_id, + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_alarm_broadcast_ch_id, message_id = MsgIds.MSG_ID_ALARM_CONDITION_CLEARED.value, function = self._handler_alarm_condition_clear) - self.can_interface.register_receiving_publication_function(channel_id = DenaliChannels.td_sync_broadcast_ch_id, + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, message_id = MsgIds.MSG_ID_TD_ALARM_INFORMATION_DATA.value, function = self._handler_alarm_information_sync) - self.can_interface.register_receiving_publication_function(channel_id = DenaliChannels.td_sync_broadcast_ch_id, + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, message_id = MsgIds.MSG_ID_TD_ACTIVE_ALARMS_LIST_REQUEST_RESPONSE.value, function = self._handler_alarm_list_response) @@ -126,7 +131,6 @@ self.last_alarm_data_2 = 0.0 #: The last Alarm's data part 2 # alarm information - self.alarm_data_type = dict() #: The Alarm's Data type in dict format self.alarm_volume = 0 #: The Alarm's Volume level self.alarm_audio_curr_hg = 0.0 #: The Alarm's Audio high current self.alarm_audio_curr_lg = 0.0 #: The Alarm's Audio low current @@ -142,23 +146,7 @@ self.ui_alarm_list = [] #: The UI Alarm List content self.ui_alarm_list_timestamp = 0.0 #: The timestamp of the last Alarm List - # Loop through the list of the event data type enum and update the dictionary - for data_type in td_enum_repository.TDEventDataTypes: - event_data_type = td_enum_repository.TDEventDataTypes(data_type).name - struct_unpack_type = None - # If U32 is in the data type enum (i.e. EVENT_DATA_TYPE_U32), then the key is the enum and the value is - # the corresponding format in the python struct - if 'U32' in event_data_type or 'BOOL' in event_data_type or 'NONE' in event_data_type: - struct_unpack_type = 'I' - elif 'S32' in event_data_type: - struct_unpack_type = 'i' - elif 'F32' in event_data_type: - struct_unpack_type = 'f' - - self.alarm_data_type[event_data_type] = struct_unpack_type - - def clear_dialin_alarms(self): """ Clears the alarms states in Dialin. @@ -180,12 +168,15 @@ @param message: published alarm status data message @return: none """ - self.alarms_priority_state = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.alarm_top = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.alarms_escalates_in = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.alarms_silence_expires_in = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.alarms_flags = struct.unpack('H', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.START_POS_FIELD_5+2]))[0] + msg_list = [] + msg_list.append(('self.alarms_priority_state', DataTypes.U32)) + msg_list.append(('self.alarm_top', DataTypes.U32)) + msg_list.append(('self.alarms_escalates_in', DataTypes.U32)) + msg_list.append(('self.alarms_silence_expires_in', DataTypes.U32)) + msg_list.append(('self.alarms_flags', DataTypes.U16)) + self.process_into_vars(decoder_list = msg_list, + message = message) self.td_alarm_status_timestamp = timestamp @@ -200,34 +191,41 @@ @return: none """ self.logger.debug("Alarm activated!") - alarm_id = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - data_typ_1 = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) - # Get the corresponding structure format - struct_data_type_1 = self.alarm_data_type[td_enum_repository.TDEventDataTypes(data_typ_1[0]).name] - # Get the data value by unpacking the data type - data_1 = struct.unpack(struct_data_type_1, bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3])) + result = {} + msg_list = [] + msg_list.append(('alarm_id', DataTypes.U32)) + msg_list.append(('data_typ_1', DataTypes.U32)) + msg_list.append(('data_1', 'data_typ_1')) + msg_list.append(('data_typ_2', DataTypes.U32)) + msg_list.append(('data_2', 'data_typ_2')) + msg_list.append(('priority', DataTypes.U32)) + msg_list.append(('rank', DataTypes.U32)) + msg_list.append(('clr_top_only', DataTypes.U32)) - data_typ_2 = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4])) - # Get the corresponding structure format - struct_data_type_2 = self.alarm_data_type[td_enum_repository.TDEventDataTypes(data_typ_2[0]).name] - # Get the data value by unpacking the data type - data_2 = struct.unpack(struct_data_type_2, bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5])) + i = 1 + for msg_detail in msg_list: + start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') + end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') + if isinstance(msg_detail[1], DataTypes): + unpack_attrib = msg_detail[1].unpack_attrib() + else: + unpack_attrib = DataTypes(result[msg_detail[1]]).unpack_attrib() + + value = struct.unpack(unpack_attrib, bytearray(message['message'][start_pos:end_pos]))[0] + result[msg_detail[0]] = value + i += 1 - priority = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6])) - rank = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7])) - clr_top_only = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8])) - - self.logger.debug("Alarm ID: %d %d %d" % (alarm_id[0], data_1[0], data_2[0])) - self.alarm_states[alarm_id[0]] = True - self.alarm_conditions[alarm_id[0]] = True - self.alarm_priorities[alarm_id[0]] = priority[0] - self.alarm_ranks[alarm_id[0]] = rank[0] - self.alarm_clear_top_only_flags[alarm_id[0]] = clr_top_only[0] - self.alarm_data[alarm_id[0]] = [data_1[0], data_2[0]] - self.last_alarm_triggered = alarm_id[0] - self.last_alarm_data_1 = data_1[0] - self.last_alarm_data_2 = data_2[0] + self.logger.debug("Alarm ID: %d %d %d" % (result['alarm_id'], result['data_1'], result['data_2'])) + self.alarm_states[result['alarm_id']] = True + self.alarm_conditions[result['alarm_id']] = True + self.alarm_priorities[result['alarm_id']] = result['priority'] + self.alarm_ranks[result['alarm_id']] = result['rank'] + self.alarm_clear_top_only_flags[result['alarm_id']] = result['clr_top_only'] + self.alarm_data[result['alarm_id']] = [result['data_1'], result['data_2']] + self.last_alarm_triggered = result['alarm_id'] + self.last_alarm_data_1 = result['data_1'] + self.last_alarm_data_2 = result['data_2'] self.td_alarm_triggered_timestamp = timestamp @@ -239,9 +237,13 @@ @param message: published TD alarm clear message @return: none """ - alarm_id = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - self.alarm_states[alarm_id[0]] = False - self.alarm_conditions[alarm_id[0]] = False + msg_list = [] + msg_list.append(('alarm_id', DataTypes.U32)) + + result = self.process_into_vars(decoder_list = msg_list, + message = message) + self.alarm_states[result['alarm_id']] = False + self.alarm_conditions[result['alarm_id']] = False self.td_alarm_cleared_timestamp = timestamp @@ -253,8 +255,13 @@ @param message: published TD alarm clear alarm condition message @return: none """ - alarm_id = struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - self.alarm_conditions[alarm_id[0]] = False + msg_list = [] + msg_list.append(('alarm_id', DataTypes.U32)) + + result = self.process_into_vars(decoder_list = msg_list, + message = message) + + self.alarm_conditions[result['alarm_id']] = False self.td_alarm_clr_condition_timestamp = timestamp @@ -269,6 +276,28 @@ @param message: published TD alarm information message @return: none """ + msg_list = [] + msg_list.append(('self.alarm_volume', DataTypes.U32)) + msg_list.append(('self.alarm_audio_curr_hg', DataTypes.F32)) + msg_list.append(('self.alarm_audio_curr_lg', DataTypes.F32)) + msg_list.append(('self.alarm_backup_audio_curr', DataTypes.U32)) + msg_list.append(('self.safety_shutdown_active', DataTypes.BOOL)) + msg_list.append(('self.ac_power_lost', DataTypes.BOOL)) + sensor_list = [] + sensor_list.append((self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_RESUME.value, DataTypes.BOOL_U08)) + sensor_list.append((self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_RINSEBACK.value, DataTypes.BOOL_U08)) + sensor_list.append((self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_END_TREATMENT.value, DataTypes.BOOL_U08)) + sensor_list.append((self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_RESUME.value, DataTypes.BOOL_U08)) + sensor_list.append((self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_RINSEBACK.value, DataTypes.BOOL_U08)) + sensor_list.append((self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_END_TREATMENT.value, DataTypes.BOOL_U08)) + + self.process_into_vars(decoder_list = msg_list, + message = message) + self.process_into_dict(dict_to_update = self.alarm_table_button_blockers, + decoder_list = sensor_list, + message = message, + start_from_byte = len(msg_list) * DataTypes.U32.size()) + """ vol = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) ach = struct.unpack('f', bytearray( @@ -306,6 +335,7 @@ self.alarm_state_button_blockers[self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_RESUME.value] = True if srs[0] else False self.alarm_state_button_blockers[self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_RINSEBACK.value] = True if srb[0] else False self.alarm_state_button_blockers[self.AlarmResponseButtons.TD_ALARM_RESPONSE_BUTTON_END_TREATMENT.value] = True if set[0] else False + """ self.td_alarm_information_timestamp = timestamp @@ -317,17 +347,16 @@ @param message: published TD alarm clear alarm condition message @return: none """ - self.ui_alarm_list_accepted = struct.unpack('i',bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.ui_alarm_list_reject_reason = struct.unpack('i',bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] + self.ui_alarm_list_accepted = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + self.ui_alarm_list_reject_reason = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] i = 3 for _ in range(0,10): start_pos = eval(f'MsgFieldPositions.START_POS_FIELD_{i}') end_pos = eval(f'MsgFieldPositions.END_POS_FIELD_{i}') - value = struct.unpack('i',bytearray(message['message'][start_pos:end_pos]))[0] + value = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray(message['message'][start_pos:end_pos]))[0] self.ui_alarm_list.append(value) i += 1 - self.ui_alarm_list_timestamp = timestamp @@ -345,7 +374,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_STATUS_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Alarm Status', logger = self.logger, @@ -366,7 +395,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_INFO_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Alarm Info', logger = self.logger, @@ -396,7 +425,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_STATE_OVERRIDE_REQUEST, entity_name = f'TD {alarm_name} Alarm state', override_text = f'{state_name}', @@ -419,7 +448,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_CLEAR_ALL_ALARMS_REQUEST, entity_name = f'TD Alarms', override_text = f'Cleared', @@ -448,7 +477,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_START_TIME_OVERRIDE_REQUEST, entity_name = f'TD {alarm_name} Alarms time', override_text = f'{time_ms} ms', @@ -498,7 +527,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_AUDIO_LEVEL_OVERRIDE_REQUEST, entity_name = f'TD Alarms lamp pattern', override_text = f'{str_pat}', @@ -524,7 +553,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_AUDIO_LEVEL_OVERRIDE_REQUEST, entity_name = f'TD Alarms audio volume', override_text = f'{str(volume)}', @@ -549,7 +578,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_AUDIO_CURRENT_HG_OVERRIDE_REQUEST, entity_name = f'TD Alarms audio high gain current', override_text = f'{str(current)} mA', @@ -574,7 +603,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_ALARM_AUDIO_CURRENT_LG_OVERRIDE_REQUEST, entity_name = f'TD Alarms audio low gain current', override_text = f'{str(current)} mA', @@ -599,7 +628,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BACKUP_ALARM_AUDIO_CURRENT_OVERRIDE_REQUEST, entity_name = f'TD Alarms backup audio current', override_text = f'{str(current)} mA', Index: leahi_dialin/td/modules/blood_flow.py =================================================================== diff -u -r33150daab2a54bde85830da329b931202ab5f961 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/blood_flow.py (.../blood_flow.py) (revision 33150daab2a54bde85830da329b931202ab5f961) +++ leahi_dialin/td/modules/blood_flow.py (.../blood_flow.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file blood_flow.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET, PUMP_CONTROL_MODE_CLOSED_LOOP -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -31,7 +34,7 @@ Treatment Device (TD) Dialin API sub-class for blood-flow related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ TDBloodFlow constructor """ @@ -40,10 +43,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_blood_pump_data = MsgIds.MSG_ID_TD_BLOOD_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_blood_pump_data, - self._handler_blood_flow_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_DATA.value, + function = self._handler_blood_flow_sync) self.td_blood_flow_timestamp = 0.0 #: The timestamp of the last message @@ -71,25 +73,20 @@ @param message: published blood flow data message @return: none """ - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.SET_BLOOD_FLOW_RATE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_FLOW_RATE.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_PUMP_ROTOR_SPEED.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_PUMP_SPEED.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_PUMP_TORQUE.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_5:MsgFieldPositions.END_POS_FIELD_5]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.SET_RPM.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_6:MsgFieldPositions.END_POS_FIELD_6]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.ROTOR_COUNT.name] = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_7:MsgFieldPositions.END_POS_FIELD_7]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.PRES_BLOOD_FLOW_RATE.name] = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_8:MsgFieldPositions.END_POS_FIELD_8]))[0] - self.h4_motor[td_enum_repository.TDBloodFlowMotorAttributes.ROTOR_HALL_STATE.name] = struct.unpack('I', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_9:MsgFieldPositions.END_POS_FIELD_9]))[0] + sensor_list =[] + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.SET_BLOOD_FLOW_RATE.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_FLOW_RATE.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_PUMP_ROTOR_SPEED.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_PUMP_SPEED.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.MEASURED_BLOOD_PUMP_TORQUE.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.SET_RPM.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.ROTOR_COUNT.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.PRES_BLOOD_FLOW_RATE.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDBloodFlowMotorAttributes.ROTOR_HALL_STATE.name, DataTypes.U32)) + self.process_into_dict(dict_to_update = self.h4_motor, + decoder_list = sensor_list, + message = message) self.td_blood_flow_timestamp = timestamp @@ -107,7 +104,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Blood Flow', logger = self.logger, @@ -131,7 +128,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_SET_FLOW_RATE_REQUEST, entity_name = f'TD Blood Flow flow rate', override_text = f'{str(flow)} mL/min', @@ -154,7 +151,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_SET_SPEED_REQUEST, entity_name = f'TD Blood Flow RPM', override_text = f'{str(rpm)}', @@ -179,7 +176,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_MEASURED_FLOW_RATE_OVERRIDE_REQUEST, entity_name = f'TD Blood Flow measured flow rate', override_text = f'{str(flow)} mL/min', @@ -205,7 +202,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_MEASURED_MOTOR_SPEED_OVERRIDE_REQUEST, entity_name = f'TD Blood Flow measured motor RPM', override_text = f'{str(spd)}', @@ -231,7 +228,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_MEASURED_ROTOR_SPEED_OVERRIDE_REQUEST, entity_name = f'TD Blood Flow measured rotor RPM', override_text = f'{str(spd)}', @@ -258,7 +255,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_ROTOR_COUNT_OVERRIDE_REQUEST, entity_name = f'TD Blood Flow rotor count', override_text = f'{str(rot_count)}', @@ -277,7 +274,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_HOME_BLOOD_PUMP_REQUEST, entity_name = f'TD Blood Flow homing', override_text = f'Active', @@ -296,7 +293,7 @@ return cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_HARD_STOP_BLOOD_PUMP, entity_name = f'TD Blood Flow Pump hard stop', override_text = f'Active', @@ -320,7 +317,7 @@ return cmd_generic_override( payload=payload, reset=reset, - channel_id=DenaliChannels.dialin_to_td_ch_id, + channel_id=CanChannels.dialin_to_td_ch_id, msg_id=MsgIds.MSG_ID_TD_BLOOD_FLOW_STROKE_VOLUME_OVERRIDE_REQUEST, entity_name='TD Blood Flow stroke volume', override_text=f'{volume}', @@ -346,7 +343,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_FLOW_WEAR_A_TERM_OVERRIDE_REQUEST, entity_name = f'TD Blood Flow wear A term', override_text = f'{str(a_term)}', @@ -371,7 +368,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BLOOD_FLOW_WEAR_B_TERM_OVERRIDE_REQUEST, entity_name = f'TD Blood Flow wear B term', override_text = f'{str(b_term)}', Index: leahi_dialin/td/modules/bubble_detector.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/bubble_detector.py (.../bubble_detector.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/td/modules/bubble_detector.py (.../bubble_detector.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,21 +8,24 @@ # @file air_bubbles.py # # @author (last) Zoltan Miskolci -# @date (last) 09-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peman Montazemi # @date (original) 18-May-2021 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray @@ -43,19 +46,18 @@ AIR_BUBBLE_NORMAL_STATE = 0 # Normal state AIR_BUBBLE_SELF_TEST_STATE = 1 # Self-test state - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_bubbles_data = MsgIds.MSG_ID_TD_BUBBLES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_bubbles_data, - self._handler_air_bubbles_data_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_BUBBLES_DATA.value, + function = self._handler_air_bubbles_data_sync) self.td_air_bubbles_timestamp = 0.0 #: The timestamp of the last message @@ -78,11 +80,13 @@ @param message: published air bubbles data message as: h18 bubble status, h18 bubble state @return: None """ - self.h18_bubble_detector[td_enum_repository.TDAirBubbleDetectorNames.H18_ADV.name][td_enum_repository.TDAirBubbleDetectorAttributes.STATUS.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])))[0] - self.h18_bubble_detector[td_enum_repository.TDAirBubbleDetectorNames.H18_ADV.name][td_enum_repository.TDAirBubbleDetectorAttributes.STATE.name] = ( - struct.unpack('i', bytearray(message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])))[0] + sensor_list =[] + sensor_list.append((td_enum_repository.TDAirBubbleDetectorNames.H18_ADV.name, td_enum_repository.TDAirBubbleDetectorAttributes.STATUS.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDAirBubbleDetectorNames.H18_ADV.name, td_enum_repository.TDAirBubbleDetectorAttributes.STATE.name, DataTypes.U32)) + self.process_into_dict(dict_to_update = self.h18_bubble_detector, + decoder_list = sensor_list, + message = message) self.td_air_bubbles_timestamp = timestamp @@ -100,7 +104,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BUBBLE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Air Bubble Detector', logger = self.logger, @@ -128,7 +132,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_BUBBLE_OVERRIDE_REQUEST, entity_name = f'TD {sensor_name} Bubble Detector status', override_text = f'{str(status)}', @@ -150,7 +154,7 @@ return cmd_generic_override( payload=payload, reset=NO_RESET, - channel_id=DenaliChannels.dialin_to_td_ch_id, + channel_id=CanChannels.dialin_to_td_ch_id, msg_id=MsgIds.MSG_ID_TD_ENABLE_VENOUS_BUBBLE_ALARM, entity_name='TD Enable Venous Bubble Alarm', override_text=f'{ena}', Index: leahi_dialin/td/modules/buttons.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/buttons.py (.../buttons.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/td/modules/buttons.py (.../buttons.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,20 +8,25 @@ # @file buttons.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 14-Oct-2024 # ############################################################################ -import struct +# Module imports from logging import Logger +import struct +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_override -from leahi_dialin.protocols.CAN import DenaliCanMessenger, DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray @@ -30,21 +35,20 @@ Treatment Delivery (TD) Dialin API sub-class for button related commands. """ - def __init__(self, can_interface: DenaliCanMessenger, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ TD_Buttons constructor - @param can_interface: the Denali CAN interface object + @param can_interface: the CAN interface object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - self.msg_id_off_button_press_request = MsgIds.MSG_ID_OFF_BUTTON_PRESS_REQUEST.value - self.can_interface.register_receiving_publication_function(DenaliChannels.td_to_ui_ch_id, - self.msg_id_off_button_press_request, - self._handler_poweroff_timeout_occurred) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_OFF_BUTTON_PRESS_REQUEST.value, + function = self._handler_poweroff_timeout_occurred) self.td_power_off_timestamp = 0.0 #: The timestamp of the last message self.poweroff_timeout_expired = False #: The Power Off timeout value @@ -71,7 +75,7 @@ self.logger.debug("Poweroff message id detected, but was the wrong length.") return - mode = struct.unpack('h', bytearray( + mode = struct.unpack(DataTypes.U16.unpack_attrib(), bytearray( message["message"][MsgFieldPositions.START_POS_FIELD_1: MsgFieldPositions.START_POS_FIELD_1 + 2])) if len(mode) > 0: @@ -99,7 +103,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_OFF_BUTTON_OVERRIDE_REQUEST, entity_name = f'TD Off Button', override_text = f'{state_name}', @@ -127,7 +131,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_STOP_BUTTON_OVERRIDE_REQUEST, entity_name = f'TD Stop Button', override_text = f'{state_name}', Index: leahi_dialin/td/modules/ejector.py =================================================================== diff -u -rc93ef62631552b2ba3ffb77006d29f902e7a1128 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/ejector.py (.../ejector.py) (revision c93ef62631552b2ba3ffb77006d29f902e7a1128) +++ leahi_dialin/td/modules/ejector.py (.../ejector.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -7,23 +7,25 @@ # # @file ejector.py # -# @author (last) Micahel Garthwaite -# @date (last) 10-Mar-2023 +# @author (last) Zoltan Miskolci +# @date (last) 05-May-2026 # @author (original) Micahel Garthwaite # @date (original) 01-Nov-2024 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -34,19 +36,18 @@ Treatment Delivery (TD) Dialin API sub-class for ejector motor related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali Can Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_ejector_data = MsgIds.MSG_ID_TD_EJECTOR_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_ejector_data, - self._handler_ejector_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_EJECTOR_DATA.value, + function = self._handler_ejector_sync) self.td_ejector_timestamp = 0 #: The timestamp of the last message @@ -55,8 +56,8 @@ td_enum_repository.TDEjectorNames.H5_MOTOR.name: { td_enum_repository.TDEjectorAttributes.STATE.name: td_enum_repository.TDEjectorStates.EJECTOR_STATE_INIT.value, td_enum_repository.TDEjectorAttributes.SET_SPEED.name: 0.0, - td_enum_repository.TDEjectorAttributes.OPT_SNSR_RETRACT: 0, # H25 Optical Sensor - td_enum_repository.TDEjectorAttributes.OPT_SNSR_ENGAGE: 0 # H24 Optical Sensor + td_enum_repository.TDEjectorAttributes.OPT_SNSR_RETRACT.name: 0, # H25 Optical Sensor + td_enum_repository.TDEjectorAttributes.OPT_SNSR_ENGAGE.name: 0 # H24 Optical Sensor } } @@ -69,15 +70,15 @@ @param message: published ejector data message as: ejector state @return: None """ - self.h5_motor[td_enum_repository.TDEjectorNames.H5_MOTOR.name][td_enum_repository.TDEjectorAttributes.STATE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] - self.h5_motor[td_enum_repository.TDEjectorNames.H5_MOTOR.name][td_enum_repository.TDEjectorAttributes.SET_SPEED.name] = struct.unpack('f', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - self.h5_motor[td_enum_repository.TDEjectorNames.H5_MOTOR.name][td_enum_repository.TDEjectorAttributes.OPT_SNSR_RETRACT.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] - self.h5_motor[td_enum_repository.TDEjectorNames.H5_MOTOR.name][td_enum_repository.TDEjectorAttributes.OPT_SNSR_ENGAGE.name] = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] + sensor_list =[] + sensor_list.append((td_enum_repository.TDEjectorNames.H5_MOTOR.name, td_enum_repository.TDEjectorAttributes.STATE.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDEjectorNames.H5_MOTOR.name, td_enum_repository.TDEjectorAttributes.SET_SPEED.name, DataTypes.F32)) + sensor_list.append((td_enum_repository.TDEjectorNames.H5_MOTOR.name, td_enum_repository.TDEjectorAttributes.OPT_SNSR_RETRACT.name, DataTypes.U32)) + sensor_list.append((td_enum_repository.TDEjectorNames.H5_MOTOR.name, td_enum_repository.TDEjectorAttributes.OPT_SNSR_ENGAGE.name, DataTypes.U32)) + self.process_into_dict(dict_to_update = self.h5_motor, + decoder_list = sensor_list, + message = message) self.td_ejector_timestamp = timestamp @@ -95,7 +96,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_EJECTOR_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Ejector', logger = self.logger, @@ -120,7 +121,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_EJECTOR_COMMAND, entity_name = f'TD Ejector state', override_text = f'{str(cmd)}', @@ -144,7 +145,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_EJECTOR_MOTOR_SET_SPEED_REQUEST, entity_name = f'TD Ejector speed', override_text = f'{str(speed)}', @@ -178,7 +179,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_EJECTOR_OPT_SENSOR_OVERRIDE_REQUEST, entity_name = f'TD Ejector {sensor_name} Optical Sensor', override_text = f'{str(sensor_active)}', Index: leahi_dialin/td/modules/events.py =================================================================== diff -u -r20c821bd230fc7689a0275a2918981669ff5cc19 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/events.py (.../events.py) (revision 20c821bd230fc7689a0275a2918981669ff5cc19) +++ leahi_dialin/td/modules/events.py (.../events.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,7 +8,7 @@ # @file events.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Dara Navaei # @date (original) 12-Oct-2021 # @@ -18,11 +18,17 @@ from logging import Logger from datetime import datetime from time import time +# Module imports +from logging import Logger +# Project imports from leahi_dialin.common import td_enum_repository -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish class TDEvents(AbstractSubSystem): @@ -31,65 +37,45 @@ """ UNKNOWN_STATE = "UNKNOWN_PREVIOUS_STATE" - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ - @param can_interface: Denali CAN Messenger object + @param can_interface: Can Messenger object """ super().__init__() self.can_interface = can_interface self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_event = MsgIds.MSG_ID_TD_EVENT.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_event, self._handler_events_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_EVENT.value, + function = self._handler_events_sync) - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_op_mode_data = MsgIds.MSG_ID_TD_OP_MODE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_op_mode_data, - self._handler_td_op_mode_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_OP_MODE_DATA.value, + function = self._handler_td_op_mode_sync) - self.td_events_timestamp = 0.0 #: The timestamp of the last Event message - self.td_event_op_mode = 0 #: The new Operation Mode value - self.td_event_sub_mode = 0 #: The new Operation Sub-Mode value - self.td_event_op_mode_timestamp = 0.0 #: The timestamp of the last Operation Mode change message + self.events_timestamp = 0.0 #: The timestamp of the last Event message + self.op_mode = 0 #: The new Operation Mode value + self.sub_mode = 0 #: The new Operation Sub-Mode value + self.event_op_mode_timestamp = 0.0 #: The timestamp of the last Operation Mode change message - # Dictionary of the mode as key and the sub mode states enum class as the value - self._td_op_mode_2_sub_mode = {td_enum_repository.TDOpModes.MODE_FAUL.name: td_enum_repository.TDFaultStates, - td_enum_repository.TDOpModes.MODE_SERV.name: td_enum_repository.TDServiceStates, - td_enum_repository.TDOpModes.MODE_INIT.name: td_enum_repository.TDInitStates, - td_enum_repository.TDOpModes.MODE_STAN.name: td_enum_repository.TDStandbyStates, - td_enum_repository.TDOpModes.MODE_PRET.name: td_enum_repository.TDPreTreatmentModesStates, - td_enum_repository.TDOpModes.MODE_TREA.name: td_enum_repository.TDTreatmentStates, - td_enum_repository.TDOpModes.MODE_POST.name: td_enum_repository.TDPostTreatmentStates, - td_enum_repository.TDOpModes.MODE_NLEG.name: td_enum_repository.TDNotLegalStates} + # Dictionary of the mode as key and the sub mode states enum class as the valued + self._op_mode_2_sub_mode = {td_enum_repository.TDOpModes.MODE_FAUL.name: td_enum_repository.TDFaultStates, + td_enum_repository.TDOpModes.MODE_SERV.name: td_enum_repository.TDServiceStates, + td_enum_repository.TDOpModes.MODE_INIT.name: td_enum_repository.TDInitStates, + td_enum_repository.TDOpModes.MODE_STAN.name: td_enum_repository.TDStandbyStates, + td_enum_repository.TDOpModes.MODE_PRET.name: td_enum_repository.TDPreTreatmentModesStates, + td_enum_repository.TDOpModes.MODE_TREA.name: td_enum_repository.TDTreatmentStates, + td_enum_repository.TDOpModes.MODE_POST.name: td_enum_repository.TDPostTreatmentStates, + td_enum_repository.TDOpModes.MODE_NLEG.name: td_enum_repository.TDNotLegalStates} - # Define the dictionaries - self._td_event_dictionary = dict() - self._td_event_data_type = dict() - # Loop through the list of the TD events enums and initial the event dictionary. Each event is a key in the # dictionary and the value is a list. + self._event_dictionary = dict() for event in td_enum_repository.TDEventList: - self._td_event_dictionary[td_enum_repository.TDEventList(event).name] = [] + self._event_dictionary[td_enum_repository.TDEventList(event).name] = [] - # Loop through the list of the event data type enum and update the dictionary - for data_type in td_enum_repository.TDEventDataTypes: - event_data_type = td_enum_repository.TDEventDataTypes(data_type).name - struct_unpack_type = None - # If U32 is in the data type enum (i.e. EVENT_DATA_TYPE_U32), then the key is the enum and the value is - # the corresponding format in the python struct - if 'U32' in event_data_type or 'BOOL' in event_data_type: - struct_unpack_type = 'I' - elif 'S32' in event_data_type: - struct_unpack_type = 'i' - elif 'F32' in event_data_type: - struct_unpack_type = 'f' - - self._td_event_data_type[event_data_type] = struct_unpack_type - - def get_td_nth_event(self, event_id, event_number=0): """ Returns the nth requested TD event @@ -99,14 +85,14 @@ @returns the requested TD event number """ - list_length = len(self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name]) + list_length = len(self._event_dictionary[td_enum_repository.TDEventList(event_id).name]) if list_length == 0: event = [] elif event_number > list_length: - event = self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name][list_length - 1] + event = self._event_dictionary[td_enum_repository.TDEventList(event_id).name][list_length - 1] else: - event = self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name][list_length - event_number - 1] + event = self._event_dictionary[td_enum_repository.TDEventList(event_id).name][list_length - event_number - 1] return event @@ -117,8 +103,8 @@ @returns none """ - for key in self._td_event_dictionary: - self._td_event_dictionary[key].clear() + for key in self._event_dictionary: + self._event_dictionary[key].clear() def get_td_events(self, event_id, number_of_events=1): @@ -133,23 +119,23 @@ list_of_events = [] # If there are not enough event lists send all the events that are available - if len(self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name]) <= number_of_events: - list_of_events = self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name] + if len(self._event_dictionary[td_enum_repository.TDEventList(event_id).name]) <= number_of_events: + list_of_events = self._event_dictionary[td_enum_repository.TDEventList(event_id).name] else: # Get the all the events - complete_list = self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name] + complete_list = self._event_dictionary[td_enum_repository.TDEventList(event_id).name] # Since the last are located at the end of the list, iterate backwards for the defined # event messages for i in range(len(complete_list) - 1, len(complete_list) - number_of_events - 1, -1): list_of_events.append(complete_list[i]) if number_of_events == 0: - list_of_events = self._td_event_dictionary[td_enum_repository.TDEventList(event_id).name] + list_of_events = self._event_dictionary[td_enum_repository.TDEventList(event_id).name] return list_of_events - @publish(["msg_id_td_event", "td_events_timestamp", '_td_event_dictionary']) + @publish(["msg_id_td_event", "events_timestamp", '_event_dictionary']) def _handler_events_sync(self, message, timestamp=0.0): """ Handles published events message @@ -162,134 +148,145 @@ op_mode = 0 sub_mode = 0 sub_state = 0 - current_sub_tuple = [] - event_id = struct.unpack('i', bytearray( + event_id = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + # Convert the event ID to enum + event_enum = td_enum_repository.TDEventList(event_id) + current_timestamp = datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S.%f') - if event_id == td_enum_repository.TDEventList.TD_EVENT_OPERATION_STATUS.value: - # Get the data type - event_data_type_1 = struct.unpack('i', bytearray( + if event_enum is td_enum_repository.TDEventList.TD_EVENT_OPERATION_STATUS: + # Get the data type - irrelevant + event_data_type_1 = struct.unpack(DataTypes.U32.unpack_attrib(), bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] - struct_data_type = self._td_event_data_type[td_enum_repository.TDEventDataTypes(event_data_type_1).name] - op_mode = struct.unpack(' current_sub_mode_timestamp: - # If the previous and current of the last two tuples do not match, then an operation mode transition - # has occurred and the previous state is converted from the previous class and the current op mode - # is converted from current operation states enum class. - # i.e last = (timestamp, event type, 3, 8) and one before = (timestamp, event type, 8, 3) - # previous and current do not match so in the last type (timestamp, event type, 8, 3) the prev state - # should be from op mode 8 and the current state should be from op mode 3 - previous_op_mode = last_op_tuple[len(last_op_tuple) - 2] - if previous_op_mode != TDEvents.UNKNOWN_STATE: - previous_sub_mode_enum_class = self._td_op_mode_2_sub_mode[previous_op_mode] - event_data_1 = previous_sub_mode_enum_class(event_data_1).name - # Unknown previous state. Display value instead of name. + # Go through the Operation Status Change message list starting from the back + # Index description: + # 0: Timestamp + # 1: Op Mode + # 2: Sub Mode + # 3: State + for i in range(len(op_status_list) - 1, -1, -1): + op_status_msg = op_status_list[i] + + # Look for a match for event_data_2 + if not data_2_found: + if op_status_msg[2] == event_data_2: + event_2_op_mode = td_enum_repository.TDOpModes(op_status_msg[1]).name + data_2_found = True + + # Look for a match for event_data_1 after event_data_2 is found + # Criteria is that the opmode, submode pair can't be the same as the one found for event_data_2 else: - event_data_1 = str(event_data_1) - event_data_2 = current_sub_mode_enum_class(event_data_2).name - else: + if op_status_msg[2] == event_data_1 and \ + (op_status_msg[2] != event_data_2 or td_enum_repository.TDOpModes(op_status_msg[1]).name != event_2_op_mode): + event_1_op_mode = td_enum_repository.TDOpModes(op_status_msg[1]).name + + # If op mode for event_data_2 found but not found for event_data_1 and run out of operation states + # assume it's the start of the unit start up and the going to standby is not logged yet + if event_2_op_mode != self.UNKNOWN_STATE and event_1_op_mode == self.UNKNOWN_STATE: + event_1_op_mode = td_enum_repository.TDOpModes.MODE_STAN.name - if event_data_2 != 0: - event_data_1 = current_sub_mode_enum_class(event_data_1).name - event_data_2 = current_sub_mode_enum_class(event_data_2).name - else: - previous_sub_mode = current_sub_tuple[len(current_sub_tuple) - 2] - previous_sub_mode_enum_class = self._td_op_mode_2_sub_mode[previous_sub_mode] - event_data_1 = previous_sub_mode_enum_class(event_data_1).name - event_data_2 = current_sub_mode_enum_class(event_data_2).name - event_tuple = (datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S.%f'), event_state_name, event_data_1, event_data_2) + # Update the event_data values + event_data_1 = self._op_mode_2_sub_mode[event_1_op_mode](event_data_1).name + event_data_2 = self._op_mode_2_sub_mode[event_2_op_mode](event_data_2).name + + # Update the tuple + event_tuple = (current_timestamp, event_enum.name, event_data_1, event_data_2) - elif event_state_name == td_enum_repository.TDEventList.TD_EVENT_OPERATION_STATUS.name: - event_tuple = (time(), op_mode, sub_mode, sub_state) + elif event_enum is td_enum_repository.TDEventList.TD_EVENT_OPERATION_STATUS: + event_tuple = (current_timestamp, op_mode, sub_mode, sub_state) # Update event dictionary - self._td_event_dictionary[event_state_name].append(event_tuple) - self.td_events_timestamp = timestamp + self._event_dictionary[event_enum.name].append(event_tuple) + self.events_timestamp = timestamp - @publish(["msg_id_td_op_mode_data", "td_event_op_mode_timestamp", "td_event_op_mode", "td_event_sub_mode"]) + @publish(["msg_id_td_op_mode_data", "event_op_mode_timestamp", "op_mode", "sub_mode"]) def _handler_td_op_mode_sync(self, message, timestamp=0.0): """ Handles published TD operation mode messages. Current TD operation mode @@ -298,12 +295,10 @@ @param message: published TD operation mode broadcast message @return: None """ + msg_list = [] + msg_list.append(('self.op_mode', DataTypes.U32)) + msg_list.append(('self.sub_mode', DataTypes.U32)) - mode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - smode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) - - self.td_event_op_mode = mode[0] - self.td_event_sub_mode = smode[0] - self.td_event_op_mode_timestamp = timestamp + self.process_into_vars(decoder_list = msg_list, + message = message) + self.event_op_mode_timestamp = timestamp Index: leahi_dialin/td/modules/pressure_sensors.py =================================================================== diff -u -r21a1d644c7d02e7588781b9025b422aa9d74c9ca -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/modules/pressure_sensors.py (.../pressure_sensors.py) (revision 21a1d644c7d02e7588781b9025b422aa9d74c9ca) +++ leahi_dialin/td/modules/pressure_sensors.py (.../pressure_sensors.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,22 +8,26 @@ # @file pressure_occlusion.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET -from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_defs import MsgFieldPositions +from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override from leahi_dialin.common import td_enum_repository -from leahi_dialin.protocols.CAN import DenaliChannels -from leahi_dialin.utils.base import AbstractSubSystem, publish +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.conversions import integer_to_bytearray, float_to_bytearray @@ -32,7 +36,7 @@ Treatment Delivery (TD) Dialin API sub-class for pressure related commands. """ - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ TDPressureSensors constructor """ @@ -41,10 +45,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_pressure_data = MsgIds.MSG_ID_TD_PRESSURE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_pressure_data, - self._handler_pressure_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_PRESSURE_DATA.value, + function = self._handler_pressure_sync) self.td_pressure_timestamp = 0.0 #: The timestamp of the latest message @@ -64,14 +67,16 @@ }, td_enum_repository.TDPressureSensorNames.H23_PRES.name: { td_enum_repository.TDPressureSensorAttributes.PRES.name : 0.0 + }, + 'TMP_PRES': { + td_enum_repository.TDPressureSensorAttributes.PRES.name: 0.0, + td_enum_repository.TDPressureSensorAttributes.LONG_FILTERED_PRESSURE.name: 0.0, + td_enum_repository.TDPressureSensorAttributes.PRES_LIMIT_MIN.name: 0, + td_enum_repository.TDPressureSensorAttributes.PRES_LIMIT_MAX.name: 0 } } self.pressure_limits_state = 0 #: Pressure limits state value - self.tmp_pressure = 0.0 #: Transmembrane pressure value - self.tmp_long_filtered_pressure = 0.0 #: Transmembrane average (over 10 seconds) pressure value - self.tmp_min_limit = 0.0 #: Transmembrane pressure minimum limit value - self.tmp_max_limit = 0.0 #: Transmembrane pressure maximum limit value @publish([ @@ -92,33 +97,39 @@ @param message: published pressure & occlusion data message @return: none """ - sensor_list =[] - sensor_list.append(['self.td_pressure_sensors[td_enum_repository.TDPressureSensorNames.H2_PRES.name][td_enum_repository.TDPressureSensorAttributes.PRES.name]', 'f']) - sensor_list.append(['self.td_pressure_sensors[td_enum_repository.TDPressureSensorNames.H14_PRES.name][td_enum_repository.TDPressureSensorAttributes.PRES.name]', 'f']) - sensor_list.append(['self.pressure_limits_state', 'i']) - # Every singed integer should be little endian - # TODO: update to 'i' after LDT-473 is merged - sensor_list.append(['self.td_pressure_sensors[td_enum_repository.TDPressureSensorNames.H2_PRES.name][td_enum_repository.TDPressureSensorAttributes.PRES_LIMIT_MIN.name]', ' None: + uf_pre_weight: float, + uf_est_target_weight: float, + uf_volume: float) -> None: """ Constructs and sends a UI set treatment parameters message. - + :param final_confirmation: (int) 0 - if only validating parameters, 1 - if validating and moving the state forward + :param treatment_modality: (int) type of treatment: 0-HD, 1-HDF + :param hdf_dilution: (int) HDF dilution option (TDTreatmentHDFDilution) :param blood_flow_rate: (int) blood flow rate (in mL/min) :param dialysate_flow_rate: (int) dialysate flow rate (in mL/min) :param treatment_duration: (int) treatment duration (in min) - :param saline_bolus_volume: (int) saline bolus volume (in mL) - :param heparin_stop_time: (int) heparin stop time (in minutes) - :param heparin_type: (int) heparin Type - :param acid_concentrate: (int) acid concentrate type - :param bicarb_concentrate: (int) bicarbonate concentrate type - :param dialyzer_type: (int) dialyzer type + :param heparin_duration: (int) heparin delivery duration (in min) + :param heparin_type: (int) heparin Type (HeparinTypes) + :param dry_bicarb_cart_size: (int) dry bicarb cart size option + :param sodium: (int) sodium (in mEq/L) + :param bicarb_concentrate: (int) bicarbonate concentrate type (BicarbTypes) + :param dialyzer_type: (int) dialyzer type (DialyzerTypes) + :param fluid_bolus_volume: (int) fluid bolus volume (in mL) :param bp_interval: (int) blood pressure measurement interval (in min) - :param rinseback_flow_rate: (int) rinse back flow rate (in mL/min) :param rinseback_volume: (int) rinse back volume (in mL) - :param arterial_pressure_limit: (int) arterial pressure alarm limit window (in mmHg) - :param venous_pressure_limit: (int) venous pressure alarm limit window (in mmHg) - :param venous_pressure_asymmetric: (int) venous pressure alarm limit asymmetric (in mmHg) - :param tmp_pressure_limit: (int) transmembrane pressure alarm limit window (in mmHg) - :param dialysate_temp: (float) dialysate temperature (in deg C) - :param heparin_dispense_rate: (float) heparin dispense rate (in mL/hr) + :param hepatitis_b_status: (int) Hepatitis B status option (TDTreatmentHepatitisB) + :param acid_concentrate: (int) acid concentrate type (AcidTypes) + :param subst_fluid_volume: (float) substitution fluid volume (in L) :param heparin_bolus_volume: (float) heparin bolus volume (in mL) - + :param heparin_delivery_rate: (float) heparin delivery rate (in mL/hr) + :param dialysate_temp: (float) dialysate temperature (in deg C) + :param uf_pre_weight: (float) patient pre weight prior to treatment (in Kilogram) + :param uf_est_target_weight: (float) patient estimated target weight after the treatment (in Kilogram) + :param uf_volume: (float) ultrafiltration volume (in L) :return: None """ - bld = integer_to_bytearray(blood_flow_rate) - dia = integer_to_bytearray(dialysate_flow_rate) - dur = integer_to_bytearray(treatment_duration) - sal = integer_to_bytearray(saline_bolus_volume) - hst = integer_to_bytearray(heparin_stop_time) - het = integer_to_bytearray(heparin_type) - acc = integer_to_bytearray(acid_concentrate) - bic = integer_to_bytearray(bicarb_concentrate) - dzr = integer_to_bytearray(dialyzer_type) - bpi = integer_to_bytearray(bp_interval) - rbf = integer_to_bytearray(rinseback_flow_rate) - rbv = integer_to_bytearray(rinseback_volume) - apw = integer_to_bytearray(arterial_pressure_limit) - vpw = integer_to_bytearray(venous_pressure_limit) - vpa = integer_to_bytearray(venous_pressure_asymmetric) - tmp = integer_to_bytearray(tmp_pressure_limit) - dtp = float_to_bytearray(dialysate_temp) - hdr = float_to_bytearray(heparin_dispense_rate) - hbv = float_to_bytearray(heparin_bolus_volume) - payload = bld + dia + dur + sal + hst + het + acc + bic + dzr + bpi + rbf + rbv + apw + vpw + vpa + tmp + dtp + hdr + hbv + payload = integer_to_bytearray(final_confirmation) + payload += integer_to_bytearray(treatment_modality) + payload += integer_to_bytearray(hdf_dilution) + payload += integer_to_bytearray(blood_flow_rate) + payload += integer_to_bytearray(dialysate_flow_rate) + payload += integer_to_bytearray(treatment_duration) + payload += integer_to_bytearray(heparin_duration) + payload += integer_to_bytearray(heparin_type) + payload += integer_to_bytearray(dry_bicarb_cart_size) + payload += integer_to_bytearray(sodium) + payload += integer_to_bytearray(bicarb_concentrate) + payload += integer_to_bytearray(dialyzer_type) + payload += integer_to_bytearray(fluid_bolus_volume) + payload += integer_to_bytearray(bp_interval) + payload += integer_to_bytearray(rinseback_volume) + payload += integer_to_bytearray(hepatitis_b_status) + payload += integer_to_bytearray(acid_concentrate) + payload += float_to_bytearray(subst_fluid_volume) + payload += float_to_bytearray(heparin_bolus_volume) + payload += float_to_bytearray(heparin_delivery_rate) + payload += float_to_bytearray(dialysate_temp) + payload += float_to_bytearray(uf_pre_weight) + payload += float_to_bytearray(uf_est_target_weight) + payload += float_to_bytearray(uf_volume) + cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_UI_TREATMENT_PARAMS_TO_VALIDATE, entity_name = 'Sneding UI Treatment Parameter to TD', override_text = 'N/A', @@ -286,7 +308,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_sync_broadcast_ch_id, + channel_id = CanChannels.ui_sync_broadcast_ch_id, msg_id = MsgIds.MSG_ID_FW_VERSIONS_REQUEST, entity_name = 'UI Version Request', override_text = 'N/A', @@ -306,7 +328,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_UI_INITIATE_TREATMENT_WORKFLOW, entity_name = 'UI Initiate Treatment Workflow', override_text = 'N/A', @@ -326,7 +348,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_UI_TREATMENT_PARAMS_CONFIRMED, entity_name = 'UI Confirm Treatment Parameters', override_text = 'N/A', @@ -348,7 +370,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_UI_UF_PAUSE_RESUME_REQUEST, entity_name = 'UI Pause or Resume UF Request', override_text = 'N/A', @@ -368,7 +390,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_UI_ULTRAFILTRATION_VOLUME_TO_VALIDATE, entity_name = 'Validate UI Ultrafiltration Volume', override_text = 'N/A', @@ -384,7 +406,7 @@ """ cmd_generic_override(payload = None, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_UI_ACTIVE_ALARMS_LIST_REQUEST, entity_name = 'UI Active Alarm List Request', override_text = 'N/A', @@ -404,7 +426,7 @@ cmd_generic_override(payload = payload, reset = None, - channel_id = DenaliChannels.ui_to_td_ch_id, + channel_id = CanChannels.ui_to_td_ch_id, msg_id = MsgIds.MSG_ID_USER_ALARM_SILENCE_REQUEST, entity_name = 'UI Alarm Silence Request', override_text = 'N/A', Index: leahi_dialin/td/treatment_delivery.py =================================================================== diff -u -r3f6c0e94db8379f00cb7fbc91daa189d6b06f379 -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/td/treatment_delivery.py (.../treatment_delivery.py) (revision 3f6c0e94db8379f00cb7fbc91daa189d6b06f379) +++ leahi_dialin/td/treatment_delivery.py (.../treatment_delivery.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -8,7 +8,7 @@ # @file treatment_delivery.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Peter Lucia # @date (original) 02-Apr-2020 # @@ -39,11 +39,14 @@ from .proxies.ui_proxy import UIProxy from ..common.constants import NO_RESET -from ..common.msg_defs import MsgIds, MsgFieldPositions, MsgFieldPositionsFWVersions +from ..common.msg_defs import MsgFieldPositions +from ..common.msg_ids import MsgIds from ..common import td_enum_repository +from ..common.generic_defs import DataTypes from ..common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override -from ..protocols.CAN import DenaliMessage, DenaliCanMessenger, DenaliChannels -from ..utils.base import AbstractSubSystem, publish, LogManager +from ..protocols.CAN import CanMessage, CanMessenger, CanChannels +from ..utils.abstract_classes import AbstractSubSystem +from ..utils.base import publish, LogManager from ..utils.conversions import integer_to_bytearray, bytearray_to_byte @@ -78,31 +81,28 @@ self.logger = self._log_manager.logger # Create listener - self.can_interface = DenaliCanMessenger(can_interface=can_interface, + self.can_interface = CanMessenger(can_interface=can_interface, logger=self.logger) self.can_interface.start() self.callback_id = None # register handler for TD operation mode broadcast messages if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - self.msg_id_td_op_mode_data = MsgIds.MSG_ID_TD_OP_MODE_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_op_mode_data, - self._handler_td_op_mode_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_OP_MODE_DATA.value, + function = self._handler_td_op_mode_sync) - self.msg_id_td_debug_event = MsgIds.MSG_ID_TD_DEBUG_EVENT.value - self.can_interface.register_receiving_publication_function(channel_id, - self.msg_id_td_debug_event, - self._handler_td_debug_event_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_DEBUG_EVENT.value, + function = self._handler_td_debug_event_sync) - self.msg_id_td_version_response = MsgIds.MSG_ID_TD_VERSION_RESPONSE.value - self.can_interface.register_receiving_publication_function(channel_id, - self.msg_id_td_version_response, - self._handler_td_version_response_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_VERSION_RESPONSE.value, + function = self._handler_td_version_response_sync) - self.msg_id_ui_version_info_response = MsgIds.MSG_ID_UI_VERSION_INFO_RESPONSE.value - self.can_interface.register_receiving_publication_function(DenaliChannels.ui_to_td_ch_id, - self.msg_id_ui_version_info_response, - self._handler_ui_version_response_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_UI_VERSION_INFO_RESPONSE.value, + function = self._handler_ui_version_response_sync) + # Dialin will send a login message during construction. This is for the leahi subsystems to start # publishing CAN data when there is no UI connected as the UI typically does this job. self.cmd_log_in_to_td() @@ -187,13 +187,12 @@ @param message: published TD operation mode broadcast message @return: None """ - mode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) - smode = struct.unpack('i', bytearray( - message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2])) + msg_list = [] + msg_list.append(('self.td_operation_mode', DataTypes.U32)) + msg_list.append(('self.td_operation_sub_mode', DataTypes.U32)) - self.td_operation_mode = mode[0] - self.td_operation_sub_mode = smode[0] + self.process_into_vars(decoder_list = msg_list, + message = message) self.td_op_mode_timestamp = timestamp @@ -206,33 +205,28 @@ @return: None if not successful, the version string if unpacked successfully """ - major = struct.unpack(' 0 for each in [major, minor, micro, build, compatibility]]): - self.td_version = f"v{major[0]}.{minor[0]}.{micro[0]}-{build[0]}.{compatibility[0]}" - self.logger.debug(f"TD VERSION: {self.td_version}") + result = self.process_into_vars(decoder_list = msg_list, + message = message) - if all([len(each) > 0 for each in [fpga_id, fpga_major, fpga_minor, fpga_lab]]): - self.td_fpga_version = f"v{fpga_id[0]}.{fpga_major[0]}.{fpga_minor[0]}-{fpga_lab[0]}" - self.logger.debug(f"TD FPGA VERSION: {self.td_fpga_version}") + if all([len(each) > 0 for each in [result['major'], result['minor'], result['micro'], result['build'], result['compatibility']]]): + self.td_version = f"v{result['major']}.{result['minor']}.{result['micro']}-{result['build']}.{result['compatibility']}" + self.logger.debug(f'TD VERSION: {self.td_version}') + if all([len(each) > 0 for each in [result['fpga_id'], result['fpga_major'], result['fpga_minor'], result['fpga_lab']]]): + self.td_fpga_version = f"v{result['fpga_id']}.{result['fpga_major']}.{result['fpga_minor']}-{result['fpga_lab']}" + self.logger.debug(f'TD FPGA VERSION: {self.td_fpga_version}') + self.td_version_response_timestamp = timestamp @@ -250,28 +244,24 @@ @return: None if not successful, the version string if unpacked successfully """ - major = struct.unpack(' 0 for each in [major, minor, micro, build, compatibility]]): - self.ui_version = f"v{major[0]}.{minor[0]}.{micro[0]}-{build[0]}.{compatibility[0]}" - self.logger.debug(f"UI VERSION: {self.ui_version}") - + if all([len(each) > 0 for each in [result['major'], result['minor'], result['micro'], result['build'], result['compatibility']]]): + self.ui_version = f"v{result['major']}.{result['minor']}.{result['micro']}-{result['build']}.{result['compatibility']}" + self.logger.debug(f'UI VERSION: {self.ui_version}') else: self.ui_version = None self.logger.debug("Failed to retrieve UI Version.") + self.ui_version_info_response_timestamp = timestamp - def cmd_op_mode_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ Constructs and sends the measured op mode broadcast interval override command @@ -286,7 +276,7 @@ return cmd_generic_broadcast_interval_override( ms = ms, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_OP_MODE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Operation Mode', logger = self.logger, @@ -301,7 +291,7 @@ @param resend: (bool) if False (default), try to login once. Otherwise, tries to login indefinitely @return: 1 if logged in, 0 if log in failed """ - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message = CanMessage.build_message(channel_id=CanChannels.dialin_to_td_ch_id, message_id=MsgIds.MSG_ID_TD_TESTER_LOGIN_REQUEST.value, payload=list(map(int, map(ord, self.TD_LOGIN_PASSWORD)))) @@ -311,14 +301,14 @@ received_message = self.can_interface.send(message, resend=resend) if received_message is not None: - if received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] == 1: + if received_message['message'][CanMessage.PAYLOAD_START_INDEX] == 1: self.logger.debug("Success: Logged In") self.td_set_logged_in_status(True) #self._send_td_checkin_message() # Timer starts interval first #self.can_interface.transmit_interval_dictionary[self.callback_id].start() else: self.logger.debug("Failure: Log In Failed.") - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + return received_message['message'][CanMessage.PAYLOAD_START_INDEX] else: self.logger.debug("Login Timeout!!!!") return False @@ -349,7 +339,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_OP_MODE_OVERRIDE_REQUEST, entity_name = 'TD Operation Mode', override_text = td_enum_repository.TDOpModes(new_mode).name, @@ -368,7 +358,7 @@ resp = cmd_generic_override( payload = None, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_SOFTWARE_RESET_REQUEST, entity_name = 'TD Software Reset', override_text = '', @@ -397,7 +387,7 @@ return cmd_generic_override( payload = payload, reset = NO_RESET, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_SAFETY_SHUTDOWN_OVERRIDE_REQUEST, entity_name = 'TD Safety Shutdown', override_text = str(active), @@ -411,7 +401,7 @@ @return: none """ - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message = CanMessage.build_message(channel_id=CanChannels.dialin_to_td_ch_id, message_id=MsgIds.MSG_ID_TD_UI_VERSION_INFO_REQUEST.value) self.logger.debug("Sending an UI version request to the TD.") @@ -433,7 +423,7 @@ return cmd_generic_override( payload = payload, reset = reset, - channel_id = DenaliChannels.dialin_to_td_ch_id, + channel_id = CanChannels.dialin_to_td_ch_id, msg_id = MsgIds.MSG_ID_TD_TRAINING_TEST_OVERRIDE_REQUEST, entity_name = 'TD Training Test Override', override_text = str(value), Index: leahi_dialin/utils/__init__.py =================================================================== diff -u -r3715028b608b10e1d440a8a738c8a4f70d3d411b -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- leahi_dialin/utils/__init__.py (.../__init__.py) (revision 3715028b608b10e1d440a8a738c8a4f70d3d411b) +++ leahi_dialin/utils/__init__.py (.../__init__.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -1,9 +1,6 @@ -import os -import json - -# mandatory imports from .base import * from .singleton import * +from .enums import * file_name = "config.json" sw_key = "SW" Index: leahi_dialin/utils/abstract_classes.py =================================================================== diff -u --- leahi_dialin/utils/abstract_classes.py (revision 0) +++ leahi_dialin/utils/abstract_classes.py (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -0,0 +1,157 @@ +########################################################################### +# +# Copyright (c) 2020-2024 Diality Inc. - All Rights Reserved. +# +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# +# @file abstract_classes.py +# +# @author (last) Zoltan Miskolci +# @date (last) 04-May-2026 +# @author (original) Zoltan Miskolci +# @date (original) 04-May-2026 +# +############################################################################ + +# Module imports +from abc import ABC, abstractmethod +from typing import List, Tuple +import struct + +# Project imports +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.constants import MSG_HEADER_SIZE + + +class AbstractObserver(ABC): + """ + Publicly accessible parent class for all observers. + + The update method will receive data when data is made available + """ + + @abstractmethod + def update(self): + """ + Attach an observer + """ + pass + + +class AbstractSubSystem: + + @abstractmethod + def __init__(self): + """ + Initialization function for the sub system + # The abstract base class requires all abstract methods are overridden by children classes + """ + self._observers = [] + self._datetime_fmt = "%m.%d.%Y_%I.%M.%S.%f" + pass + + + def attach(self, observer: AbstractObserver): + """ + Attach an observer so it is updated upon published events + """ + self._observers.append(observer) + + + def detach(self, observer: AbstractObserver): + """ + Detach an observer + """ + self._observers.remove(observer) + + + def process_into_vars(self, decoder_list: List[Tuple], message, start_from_byte: int=0, debug: bool=False) -> dict: + """ + Process the CAN message with the help of the decoder list into variables and a dictionary. + Note: updating variables will only be done when it's class wide one, aka "self.attr_name". + For local attributes to avoid namespace issues use the returned dictionary. Format: {attr_name : value} + + :param decoder_list: (List[Tuple[String, DataTypes]]) Contains the variable name and DataType pair of the indexed message + :param message: (Bytearray) The raw CAN message + :param start_from_byte: (Integer) Start from the nth byte after the header + :param debug: (Boolean) Prints for debugging + :return: (Dictionary) A dictionary for the variable_name and value pair + """ + start_pos = MSG_HEADER_SIZE + start_from_byte + results = {} + if debug: + print(f'\n\ndecoder_list: {decoder_list}') + print(f'len: {len(decoder_list[0])}') + for decode_details in decoder_list: + # Content of the decode list + variable_name = decode_details[0] + datatype: DataTypes = decode_details[-1] + end_pos = start_pos + datatype.size() + value = struct.unpack(datatype.unpack_attrib(), bytearray(message['message'][start_pos:end_pos]))[0] + if debug: + print(f'value: {value} ({datatype.name})') + print(f'pos: {start_pos} - {end_pos}') + if 'nan' in str(value).lower(): + raise ValueError(f'{value} is not an accepted value!') + if datatype is DataTypes.BOOL: + value = True if value == 1 else False + results[variable_name] = value + + # If it's a instance variable (self.) then set it's value + if variable_name.startswith('self'): + attr_name = variable_name[5:] + if not attr_name.isidentifier(): + print(f'Invalid attribute name: "{attr_name}"!') + raise ValueError('Invalid attribute name') + setattr(self, attr_name, value) + start_pos = end_pos + if debug: + print('Finished cycle\n') + if debug: + print(f'results: {results}\n') + return results + + + def process_into_dict(self, dict_to_update: dict, decoder_list: List[Tuple], message, start_from_byte: int=0, debug: bool=False): + """ + Process the CAN message with the help of the decoder list into a dictionary. + + :param decoder_list: (List[Tuple[DialEnum, DialEnum, DataTypes]]) Contains the dictioarny key names and DataType of the indexed message + :param message: (Bytearray) The raw CAN message + :param start_from_byte: (Integer) Start from the nth byte after the header + :param debug: (Boolean) Prints for debugging + :return: (Dictionary) The updated dictionary + """ + start_pos = MSG_HEADER_SIZE + start_from_byte + if debug: + print(f'\n\ndecoder_list: {decoder_list}') + print(f'len: {len(decoder_list[0])}') + for decode_details in decoder_list: + # Content of the decode list + key_1 = decode_details[0] + key_2 = decode_details[1] if len(decode_details) >= 3 else None + datatype: DataTypes = decode_details[-1] + + end_pos = start_pos + datatype.size() + value = struct.unpack(datatype.unpack_attrib(), bytearray(message['message'][start_pos:end_pos]))[0] + if debug: + print(f'key_1: {key_1}') + print(f'key_2: {key_2}') + print(f'value: {value} ({datatype.name})') + print(f'pos: {start_pos} - {end_pos}') + if 'nan' in str(value).lower(): + raise ValueError(f'{value} is not an accepted value!') + # If the type is Bool, convert the value from Integer to Boolean + if datatype is DataTypes.BOOL: + value = True if value == 1 else False + # Save the value into the Dictionary + if len(decode_details) == 2: + dict_to_update[key_1] = value + elif len(decode_details) == 3: + dict_to_update[key_1][key_2] = value + start_pos = end_pos + if debug: + print('Finished cycle\n') + if debug: + print('done\n') Index: tests/unit_tests/test_enums.py =================================================================== diff -u -r72c423da1d07c40699f40b5da5bee6d992d3082c -re45b20cdc5d4c5dcff8cef530b173ca94cb2e422 --- tests/unit_tests/test_enums.py (.../test_enums.py) (revision 72c423da1d07c40699f40b5da5bee6d992d3082c) +++ tests/unit_tests/test_enums.py (.../test_enums.py) (revision e45b20cdc5d4c5dcff8cef530b173ca94cb2e422) @@ -22,7 +22,7 @@ def test_msg_defs(self): try: - from leahi_dialin.common.msg_defs import MsgIds + from leahi_dialin.common.msg_ids import MsgIds from leahi_dialin.common.msg_defs import RequestRejectReasons except ValueError as e: self.fail("Could not import msg_defs: {0}".format(e))