Index: leahi_dialin/common/constants.py =================================================================== diff -u -r7ce778dc51b7dba5a7a7ca14244ed7eea7bf029d -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/common/constants.py (.../constants.py) (revision 7ce778dc51b7dba5a7a7ca14244ed7eea7bf029d) +++ leahi_dialin/common/constants.py (.../constants.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -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/generic_defs.py =================================================================== diff -u --- leahi_dialin/common/generic_defs.py (revision 0) +++ leahi_dialin/common/generic_defs.py (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -0,0 +1,61 @@ +########################################################################### +# +# Copyright (c) 2021-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 generic_defs.py +# +# @author (last) Zoltan Miskolci +# @date (last) 04-May-2026 +# @author (last) Zoltan Miskolci +# @date (last) 01-May-2026 +# +############################################################################ + +from enum import unique + +from ..utils.enums import DialinEnum + + +@unique +class DataTypes(DialinEnum): + NONE = (0, ' Integer + S32 = (2, ' Integer + F32 = (3, ' Float + BOOL = (4, ' Integer + # After this point the Data Types are extra ones used only in Dialin + U08 = (5, ' Integer + U16 = (6, ' Integer + S16 = (7, ' Integer + BOOL_U08 = (8, ' Integer + NUM_OF_DATA_TYPES = (9, None, 0) # Number of Data Types - None + + def __new__(cls, value, unpack_fmt, unpack_size): + obj = object.__new__(cls) + obj._value_ = value # keeps .value as int + obj.unpack_fmt = unpack_fmt # attach extra attribute + obj.unpack_size = unpack_size # attach extra attribute + return obj + + def unpack_attrib(self) -> str: + return self.unpack_fmt + + def size(self) -> int: + return self.unpack_size + +DataTypes._str_list = { + # Official Name : Accepted strings + 'NONE': [], + 'U32': [], + 'S32': [], + 'F32': [], + 'BOOL': ['boolean'], + 'U08': ['u8'], + 'U16': [], + 'S16': [], + 'BOOL_U08': [], + 'NUM_OF_DATA_TYPES': [], +} Index: leahi_dialin/common/msg_defs.py =================================================================== diff -u -r41de945f9c773e54e965e80d9e46def828beb732 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/common/msg_defs.py (.../msg_defs.py) (revision 41de945f9c773e54e965e80d9e46def828beb732) +++ leahi_dialin/common/msg_defs.py (.../msg_defs.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -14,15 +14,13 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from ..utils.enums import DialinEnum from .msg_ids import MsgIds +from .constants import MSG_HEADER_SIZE +ACK_NOT_REQUIRED = [ ] -ACK_NOT_REQUIRED = [ -] - - @unique class RequestRejectReasons(DialinEnum): REQUEST_REJECT_REASON_NONE = 0 @@ -75,9 +73,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 +138,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 -r6d104d3185ac3ed7c18c97ecdc13fd59bf53a8d1 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 6d104d3185ac3ed7c18c97ecdc13fd59bf53a8d1) +++ leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -187,7 +187,11 @@ MSG_ID_UI_RECIRCULATE_REQUEST = 0xA6 MSG_ID_TD_RECIRCULATE_RESPONSE = 0xA7 MSG_ID_TD_RECIRCULATE_DATA = 0xA8 + MSG_ID_TD_SYRINGE_PUMP_DATA = 0xA9 + MSG_ID_TD_WATER_SAMPLE_RESULT_RESPONSE = 0xAB + MSG_ID_TD_WATER_SAMPLE_DATA = 0xAC + MSG_ID_DD_PISTON_PUMP_CONTROL_DATA = 0xF0 MSG_ID_TD_TESTER_LOGIN_REQUEST = 0x8000 @@ -260,6 +264,19 @@ MSG_ID_TD_TEMPERATURE_PUBLISH_INTERVAL_OVERRIDE_REQUEST = 0x8043 MSG_ID_TD_EJECTOR_OPT_SENSOR_OVERRIDE_REQUEST = 0x8044 MSG_ID_TD_ENABLE_VENOUS_BUBBLE_ALARM = 0x8047 + MSG_ID_TD_SYRINGE_PUMP_OPERATION_REQUEST = 0x8048 + MSG_ID_TD_SYRINGE_PUMP_PUBLISH_INTERVAL_OVERRIDE = 0x8049 + MSG_ID_TD_SYRINGE_PUMP_RATE_OVERRIDE_REQUEST = 0x8050 + MSG_ID_TD_SYRINGE_PUMP_FORCE_OVERRIDE_REQUEST = 0x8051 + MSG_ID_TD_SYRINGE_PUMP_HOME_OVERRIDE_REQUEST = 0x8052 + MSG_ID_TD_SYRINGE_PUMP_POSITION_OVERRIDE_REQUEST = 0x8053 + MSG_ID_TD_SYRINGE_PUMP_VOLUME_OVERRIDE_REQUEST = 0x8054 + MSG_ID_TD_SYRINGE_PUMP_STATUS_OVERRIDE_REQUEST = 0x8055 + MSG_ID_TD_SYRINGE_PUMP_ENCODER_STATUS_OVERRIDE_REQUEST = 0x8056 + MSG_ID_TD_SYRINGE_PUMP_ADC_DAC_STATUS_OVERRIDE_REQUEST = 0x8057 + MSG_ID_TD_SYRINGE_PUMP_ADC_READ_COUNTER_OVERRIDE_REQUEST = 0x8058 + MSG_ID_TD_HEPARIN_BOLUS_TARGET_RATE_OVERRIDE_REQUEST = 0x8059 + MSG_ID_TD_SYRINGE_PUMP_FORCE_SENSOR_CALIBRATION_REQUEST = 0x8060 MSG_ID_TD_TRAINING_TEST_OVERRIDE_REQUEST = 0x8999 @@ -354,7 +371,6 @@ MSG_ID_DD_BICARB_CHAMBER_FILL_REQUEST_OVERRIDE_REQUEST = 0xA059 MSG_ID_DD_BICART_DRAIN_REQUEST_OVERRIDE_REQUEST = 0xA05A MSG_ID_DD_BICART_CARTRIDGE_SELECT_OVERRIDE_REQUEST = 0xA05B - MSG_ID_DD_FLOATER_LEVEL_OVERRIDE_REQUEST = 0xA05C MSG_ID_DD_SET_CONDUCTIVITY_MODEL_REQUEST = 0xA100 MSG_ID_DD_CONDUCTIVITY_SENSOR_RESISTANCE_OVERRIDE_REQUEST = 0xA101 @@ -379,7 +395,7 @@ MSG_ID_FP_PRESSURE_SENSOR_FILTER_TEMPERATURE_OVERRIDE_REQUEST = 0xB00D MSG_ID_FP_PRESSURE_PUBLISH_INTERVAL_OVERRIDE_REQUEST = 0xB00E MSG_ID_FP_LEVEL_PUBLISH_INTERVAL_OVERRIDE_REQUEST = 0xB00F - MSG_ID_FP_FLOATER_LEVEL_OVERRIDE_REQUEST = 0xB010 + MSG_ID_FP_LEVEL_OVERRIDE_REQUEST = 0xB010 MSG_ID_FP_FLOWS_PUBLISH_INTERVAL_OVERRIDE_REQUEST = 0xB011 MSG_ID_FP_FLOW_RATE_OVERRIDE_REQUEST = 0xB012 MSG_ID_FP_FLOW_TEMP_OVERRIDE_REQUEST = 0xB013 Index: leahi_dialin/common/td_defs.py =================================================================== diff -u -r53ad62b20a323d405916edafb195fdcf64164f7a -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/common/td_defs.py (.../td_defs.py) (revision 53ad62b20a323d405916edafb195fdcf64164f7a) +++ leahi_dialin/common/td_defs.py (.../td_defs.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -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'], @@ -797,27 +795,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): @@ -1156,18 +1134,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': [], } @@ -1197,51 +1177,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': [], -} \ No newline at end of file +} + + +@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 -r27cd8e0e7b47617083e8c12f73306f0ba648e687 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/common/test_config_defs.py (.../test_config_defs.py) (revision 27cd8e0e7b47617083e8c12f73306f0ba648e687) +++ leahi_dialin/common/test_config_defs.py (.../test_config_defs.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -44,5 +44,6 @@ 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 - NUM_OF_TEST_CONFIGS = 3 # Number of Test Configs + TEST_CONFIG_ENABLE_SYRINGE_PUMP_TESTING = 3 # Test configuration to skip checks in syringe pump during testing + NUM_OF_TEST_CONFIGS = 4 # Number of Test Configs TDTestConfigOptions._str_list = {} Index: leahi_dialin/td/modules/syringe_pump.py =================================================================== diff -u -r8fbb7b6eda15b41620cccfbc4760047ccc9d1043 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/td/modules/syringe_pump.py (.../syringe_pump.py) (revision 8fbb7b6eda15b41620cccfbc4760047ccc9d1043) +++ leahi_dialin/td/modules/syringe_pump.py (.../syringe_pump.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -17,12 +17,13 @@ from logging import Logger from leahi_dialin.common.constants import RESET, NO_RESET +from leahi_dialin.common.generic_defs import DataTypes from leahi_dialin.common.td_defs import TDTreaHeparinStates, TDTreaSyringePumpStates, SyringePumpOperations from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions 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.checks import check_broadcast_interval_override_ms +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 TDSyringePump(AbstractSubSystem): @@ -32,7 +33,7 @@ Treatment Delivery (TD) Dialin API sub-class for syringe 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,10 +43,9 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.td_sync_broadcast_ch_id - msg_id = MsgIds.MSG_ID_TD_SYRINGE_PUMP_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, msg_id, - self._handler_syringe_pump_data) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_SYRINGE_PUMP_DATA.value, + function = self._handler_syringe_pump_data) self.syringe_pump_state = TDTreaSyringePumpStates.SYRINGE_PUMP_INIT_STATE.value #: current syringe pump state self.heparin_state = TDTreaHeparinStates.HEPARIN_STATE_OFF.value #: current heparin state @@ -63,6 +63,7 @@ self.syringe_pump_adc_read_counter = 0 #: current syringe pump ADC read counter self.td_syringe_pump_timestamp = 0.0 #: The timestamp of the syringe pump message + @publish(["td_syringe_pump_timestamp", "syringe_pump_state", "syringe_pump_set_rate_ml_hr", "syringe_pump_meas_rate_ml_hr", "syringe_pump_position", @@ -80,6 +81,24 @@ @return: None """ + msg_list = [] + msg_list.append(('self.syringe_pump_state', DataTypes.U32)) + msg_list.append(('self.heparin_state', DataTypes.U32)) + msg_list.append(('self.syringe_pump_set_rate_ml_hr', DataTypes.F32)) + msg_list.append(('self.syringe_pump_meas_rate_ml_hr', DataTypes.F32)) + msg_list.append(('self.syringe_pump_position', DataTypes.U32)) + msg_list.append(('self.syringe_pump_volume_ml', DataTypes.F32)) + msg_list.append(('self.syringe_pump_home_v', DataTypes.F32)) + msg_list.append(('self.syringe_pump_switch_v', DataTypes.F32)) + msg_list.append(('self.syringe_pump_force_v', DataTypes.F32)) + msg_list.append(('self.syringe_pump_safety_volume_ml', DataTypes.F32)) + msg_list.append(('self.syringe_pump_status', DataTypes.U08)) + msg_list.append(('self.syringe_pump_encoder_status', DataTypes.U08)) + msg_list.append(('self.syringe_pump_adc_dac_status', DataTypes.U08)) + msg_list.append(('self.syringe_pump_adc_read_counter', DataTypes.U08)) + + self.process_into_vars(decoder_list = msg_list, + message = message) sta = struct.unpack('i', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1])) hep = struct.unpack('i', bytearray( @@ -149,7 +168,6 @@ SYRINGE_PUMP_OP_BOLUS = 4 SYRINGE_PUMP_OP_CONTINUOUS = 5 """ - op = integer_to_bytearray(operation) rat = float_to_bytearray(rate) vol = float_to_bytearray(volume) @@ -158,7 +176,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_SYRINGE_PUMP_OPERATION_REQUEST, entity_name = f'TD Syringe Pump Operation', override_text = f'{operation} ', @@ -181,7 +199,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_HD_SYRINGE_PUMP_PUBLISH_INTERVAL_OVERRIDE, module_name = 'TD Syringe Pump', logger = self.logger, @@ -206,7 +224,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_SYRINGE_PUMP_RATE_OVERRIDE_REQUEST, entity_name = f'TD syringe pump measured rate', override_text = f'{rate} ', @@ -232,7 +250,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_SYRINGE_PUMP_FORCE_OVERRIDE_REQUEST, entity_name = f'TD syringe pump measured force', override_text = f'{volts} ', @@ -258,7 +276,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_SYRINGE_PUMP_HOME_OVERRIDE_REQUEST, entity_name = f'TD syringe pump measured home', override_text = f'{volts} ', @@ -284,7 +302,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_SYRINGE_PUMP_POSITION_OVERRIDE_REQUEST, entity_name = f'TD syringe pump measured position', override_text = f'{position} ', @@ -310,7 +328,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_SYRINGE_PUMP_VOLUME_OVERRIDE_REQUEST, entity_name = f'TD syringe pump volume delivered', override_text = f'{volume} ', @@ -336,7 +354,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_SYRINGE_PUMP_STATUS_OVERRIDE_REQUEST, entity_name = f'TD syringe pump status', override_text = f'{status} ', @@ -362,7 +380,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_SYRINGE_PUMP_ENCODER_STATUS_OVERRIDE_REQUEST, entity_name = f'TD syringe pump encoder status', override_text = f'{status} ', @@ -388,7 +406,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_SYRINGE_PUMP_ADC_DAC_STATUS_OVERRIDE_REQUEST, entity_name=f'TD syringe pump ADC & DAC status', override_text=f'{status} ', @@ -414,7 +432,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_SYRINGE_PUMP_ADC_READ_COUNTER_OVERRIDE_REQUEST, entity_name=f'TD syringe pump ADC read counter', override_text=f'{counter} ', @@ -439,13 +457,14 @@ 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_HEPARIN_BOLUS_TARGET_RATE_OVERRIDE_REQUEST, entity_name=f'TD heparin bolus target rate value', override_text=f'{rate} ', logger=self.logger, can_interface=self.can_interface) + def cmd_calibrate_force_sensor(self) -> int: """ Constructs and sends the set syringe pump DAC vRef. The value the DAC is set to is within TD Calibration Record. @@ -460,7 +479,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_SYRINGE_PUMP_FORCE_SENSOR_CALIBRATION_REQUEST, entity_name=f'TD syringe pump calibrate force sensor', override_text=f' ', Index: leahi_dialin/td/modules/valves.py =================================================================== diff -u -r62e0f169dcd17b68ab7ec40a3f58f51ace9af24a -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/td/modules/valves.py (.../valves.py) (revision 62e0f169dcd17b68ab7ec40a3f58f51ace9af24a) +++ leahi_dialin/td/modules/valves.py (.../valves.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -8,72 +8,47 @@ # @file valves.py # # @author (last) Zoltan Miskolci -# @date (last) 08-Jan-2026 +# @date (last) 05-May-2026 # @author (original) Dara Navaei # @date (original) 19-Aug-2020 # ############################################################################ -import struct +# Module imports from logging import Logger +# Project imports from leahi_dialin.common.constants import NO_RESET +from leahi_dialin.common.generic_defs import DataTypes from leahi_dialin.common.msg_defs 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, 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 class TDValves(AbstractSubSystem): """ Treatment Delivery (TD) Dialin API sub-class for valves related commands. """ - # Valves states publish message field positions - # Note the MsgFieldPosition was not used since some of the published data are S16 - START_POS_VALVES_ID = DenaliMessage.PAYLOAD_START_INDEX - END_POS_VALVES_ID = START_POS_VALVES_ID + 4 - START_VALVES_STATE = END_POS_VALVES_ID - END_VALVES_STATE = START_VALVES_STATE + 4 - - START_POS_VALVES_CURR_POS_ID = END_VALVES_STATE - END_POS_VALVES_CURR_POS_ID = START_POS_VALVES_CURR_POS_ID + 4 - - START_POS_VALVES_CURR_POS = END_POS_VALVES_CURR_POS_ID - END_POS_VALVES_CURR_POS = START_POS_VALVES_CURR_POS + 2 - - START_POS_VALVES_NEXT_POS = END_POS_VALVES_CURR_POS - END_POS_VALVES_NEXT_POS = START_POS_VALVES_NEXT_POS + 2 - - START_POS_A = END_POS_VALVES_NEXT_POS - END_POS_A = START_POS_A + 2 - START_POS_B = END_POS_A - END_POS_B = START_POS_B + 2 - START_POS_C = END_POS_B - END_POS_C = START_POS_C + 2 - START_POS_D = END_POS_C - END_POS_D = START_POS_D + 2 - START_MAX_HOMING_ENC = END_POS_D - END_MAX_HOMING_ENC = START_MAX_HOMING_ENC + 2 - - def __init__(self, can_interface, logger: Logger): + def __init__(self, can_interface: CanMessenger, logger: Logger): """ TDValves constructor - @param can_interface: (DenaliCanMessenger) - Denali CAN messenger object. + @param can_interface: (CanMessenger) - Can Messenger object. @param logger: (Logger) - Dialin logger """ 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_valves_data = MsgIds.MSG_ID_TD_VALVES_DATA.value - self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_td_valves_data, - self._handler_valves_sync) + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_VALVES_DATA.value, + function = self._handler_valves_sync) self.td_valves_timestamp = 0.0 #: The timestamp of the latest message @@ -91,31 +66,29 @@ @param message: published TD valves data message @returns none """ - vlv_id = struct.unpack('i', bytearray( - message['message'][self.START_POS_VALVES_ID:self.END_POS_VALVES_ID]))[0] - state_id = struct.unpack('i', bytearray( - message['message'][self.START_VALVES_STATE:self.END_VALVES_STATE]))[0] - pos_id = struct.unpack('i', bytearray( - message['message'][self.START_POS_VALVES_CURR_POS_ID:self.END_POS_VALVES_CURR_POS_ID]))[0] - pos_cnt = struct.unpack('h', bytearray( - message['message'][self.START_POS_VALVES_CURR_POS:self.END_POS_VALVES_CURR_POS]))[0] - cmd_pos = struct.unpack('h', bytearray( - message['message'][self.START_POS_VALVES_NEXT_POS:self.END_POS_VALVES_NEXT_POS]))[0] + msg_list = [] + msg_list.append(('Valve', DataTypes.U32)) + msg_list.append(('State', DataTypes.U32)) + msg_list.append(('PosID', DataTypes.U32)) + msg_list.append(('PosCnt', DataTypes.S16)) + msg_list.append(('Cmd', DataTypes.S16)) + msg_list.append(('PosA', DataTypes.S16)) + msg_list.append(('PosB', DataTypes.S16)) + msg_list.append(('PosC', DataTypes.S16)) + msg_list.append(('PosD', DataTypes.S16)) + msg_list.append(('Max_homing_enc', DataTypes.S16)) - pos_a = struct.unpack('h', bytearray(message['message'][self.START_POS_A:self.END_POS_A]))[0] - pos_b = struct.unpack('h', bytearray(message['message'][self.START_POS_B:self.END_POS_B]))[0] - pos_c = struct.unpack('h', bytearray(message['message'][self.START_POS_C:self.END_POS_C]))[0] - pos_d = struct.unpack('h', bytearray(message['message'][self.START_POS_D:self.END_POS_D]))[0] - max_homing_enc = struct.unpack('h', bytearray( - message['message'][self.START_MAX_HOMING_ENC:self.END_MAX_HOMING_ENC]))[0] + result = self.process_into_vars(decoder_list = msg_list, + message = message) # To make sure values of the enums are not out of range - if td_enum_repository.TDValveNames.has_value(vlv_id) and td_enum_repository.TDValvePositions.has_value(pos_id) and td_enum_repository.TDValveStates.has_value(pos_id): - vlv_name = td_enum_repository.TDValveNames(vlv_id).name + if td_enum_repository.TDValveNames.has_value(result['Valve']) and td_enum_repository.TDValvePositions.has_value(result['PosID']) and td_enum_repository.TDValveStates.has_value(result['State']): + # Updating fields + result['Valve'] = td_enum_repository.TDValveNames(result['Valve']).name + result['State'] = td_enum_repository.TDValveStates(result['State']).name + result['PosID'] = td_enum_repository.TDValvePositions(result['PosID']).name # Update the valves dictionary - self.valves_status[vlv_name] = {'Valve': vlv_name, 'PosID': td_enum_repository.TDValvePositions(pos_id).name, 'PosCnt': pos_cnt, - 'Cmd': cmd_pos, 'State': td_enum_repository.TDValveStates(state_id).name, 'PosA': pos_a, - 'PosB': pos_b, 'PosC': pos_c, 'PosD': pos_d, 'Max_homing_enc': max_homing_enc} + self.valves_status[result['Valve']] = result self.td_valves_timestamp = timestamp @@ -133,7 +106,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_VALVES_PUBLISH_INTERVAL_OVERRIDE_REQUEST, module_name = 'TD Valves', logger = self.logger, @@ -157,7 +130,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_PINCH_VALVE_SET_POSITION_REQUEST, entity_name = f'TD {valve_name} Valve position', override_text = str(position), @@ -183,7 +156,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_PINCH_VALVE_HOME_REQUEST, entity_name = f'TD {valve_name} Valve homing', override_text = 'Forcefully' if force_home == 1 else 'Normally', @@ -209,7 +182,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_ROTARY_PINCH_VALVE_POSITION_OVERRIDE_REQUEST, entity_name = f'TD {valve_name} Valve position', override_text = f'move by {str(position_count)}', @@ -235,7 +208,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_ROTARY_PINCH_VALVE_STATUS_OVERRIDE_REQUEST, entity_name = f'TD {valve_name} Valve status', override_text = str(status), @@ -260,7 +233,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_ROTARY_PINCH_VALVE_POSITION_OVERRIDE_REQUEST, entity_name = f'TD {valve_name} Valve encoder position offset', override_text = str(counts), Index: leahi_dialin/td/treatment_delivery.py =================================================================== diff -u -r53ad62b20a323d405916edafb195fdcf64164f7a -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/td/treatment_delivery.py (.../treatment_delivery.py) (revision 53ad62b20a323d405916edafb195fdcf64164f7a) +++ leahi_dialin/td/treatment_delivery.py (.../treatment_delivery.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -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 # @@ -40,11 +40,13 @@ 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 MsgIds, MsgFieldPositions 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 @@ -79,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() @@ -189,13 +188,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 @@ -208,33 +206,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 @@ -252,28 +245,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 @@ -288,7 +277,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, @@ -303,7 +292,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)))) @@ -313,14 +302,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 @@ -351,7 +340,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, @@ -370,7 +359,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 = '', @@ -399,7 +388,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), @@ -413,7 +402,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.") @@ -435,7 +424,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/ui/dd_messaging.py =================================================================== diff -u -r84e4345fc09b9796d6430c49f11cc2b0a1e33522 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/ui/dd_messaging.py (.../dd_messaging.py) (revision 84e4345fc09b9796d6430c49f11cc2b0a1e33522) +++ leahi_dialin/ui/dd_messaging.py (.../dd_messaging.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -24,7 +24,7 @@ def info(a1, a2): pass - self.can_interface = CAN.DenaliCanMessenger(can_interface=self.can_Channel, logger=fakeLogger() ) + self.can_interface = CAN.CanMessenger(can_interface=self.can_Channel, logger=fakeLogger() ) self.can_interface.start() if self.can_interface is not None: @@ -52,8 +52,8 @@ payload += conversions.float_to_bytearray(D43 ) payload += conversions.float_to_bytearray(D74 ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_CONDUCTIVITY_DATA.value, payload=payload) @@ -80,8 +80,8 @@ payload += conversions.unsigned_integer_to_bytearray(is_dial_good ) payload += conversions.float_to_bytearray (currentQd ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_GEN_DIALYSATE_MODE_DATA.value, payload=payload) @@ -117,8 +117,8 @@ payload += conversions.unsigned_byte_to_bytearray (fpga_lab ) payload += conversions.unsigned_integer_to_bytearray(compatibility_rev) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_VERSION_RESPONSE.value, payload=payload) @@ -134,8 +134,8 @@ # TODO: replace with proper payload and message ID once message is defined payload = bytes(serial, 'ascii') + b'\x00' - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_SERIAL_RESPONSE.value, payload=payload) @@ -155,8 +155,8 @@ payload += conversions.unsigned_integer_to_bytearray(d63Level ) payload += conversions.unsigned_integer_to_bytearray(d46Level ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_LEVEL_DATA.value, payload=payload) @@ -209,8 +209,8 @@ payload += conversions.float_to_bytearray(d18PresTemp ) payload += conversions.float_to_bytearray(d41PresTemp ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_PRESSURES_DATA.value, payload=payload) @@ -266,8 +266,8 @@ payload += conversions.float_to_bytearray(d28AvgTemp ) payload += conversions.float_to_bytearray(d30AvgTemp ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_TEMPERATURE_DATA.value, payload=payload) @@ -329,8 +329,8 @@ payload += conversions.float_to_bytearray (dbg8 ) payload += conversions.float_to_bytearray (dbg9 ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_HEATERS_DATA.value, payload=payload) @@ -416,8 +416,8 @@ payload += conversions.unsigned_integer_to_bytearray(d76PumpState ) payload += conversions.float_to_bytearray (d76PumpPulseUS ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_DATA.value, payload=payload) @@ -489,8 +489,8 @@ payload += conversions.unsigned_integer_to_bytearray(d12PumpMeasuredDir ) payload += conversions.unsigned_integer_to_bytearray(d48PumpMeasuredDir ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DIALYSATE_PUMPS_DATA.value, payload=payload) @@ -504,8 +504,8 @@ """ payload = conversions.unsigned_integer_to_bytearray(blood_leak ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_BLOOD_LEAK_DATA.value, payload=payload) @@ -521,8 +521,8 @@ payload = conversions.integer_to_bytearray(1 if vRejectionReason == 0 else 0) payload += conversions.integer_to_bytearray(vRejectionReason) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_DATE_AND_TIME_RESPONSE.value, payload=payload) self.can_interface.send(message, 0) @@ -665,8 +665,8 @@ for i in range(len(valvesensedState)): payload += conversions.unsigned_byte_to_bytearray(valvesensedState[i]) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.dd_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.dd_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_DD_VALVES_STATES_DATA.value, payload=payload) Index: leahi_dialin/ui/fp_messaging.py =================================================================== diff -u -re4ddc3ed6d05ef9f2a69e8130dfdcc4377b00262 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/ui/fp_messaging.py (.../fp_messaging.py) (revision e4ddc3ed6d05ef9f2a69e8130dfdcc4377b00262) +++ leahi_dialin/ui/fp_messaging.py (.../fp_messaging.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -24,7 +24,7 @@ def info(a1, a2): pass - self.can_interface = CAN.DenaliCanMessenger(can_interface=self.can_Channel, logger=fakeLogger() ) + self.can_interface = CAN.CanMessenger(can_interface=self.can_Channel, logger=fakeLogger() ) self.can_interface.start() if self.can_interface is not None: @@ -60,8 +60,8 @@ payload += conversions.unsigned_byte_to_bytearray (fpga_lab ) payload += conversions.unsigned_integer_to_bytearray(compatibility_rev) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_VERSION_RESPONSE.value, payload=payload) @@ -100,8 +100,8 @@ payload += conversions.float_to_bytearray(x3Pressure ) payload += conversions.float_to_bytearray(x4Pressure ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_PRESSURES_DATA.value, payload=payload) @@ -140,8 +140,8 @@ payload += conversions.float_to_bytearray (p12PumpDutyCyclePct ) payload += conversions.float_to_bytearray (p12PumpFBDutyCyclePct ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_RO_PUMP_DATA.value, payload=payload) @@ -156,8 +156,8 @@ """ payload = conversions.unsigned_integer_to_bytearray(p25Level ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_LEVEL_DATA.value, payload=payload) @@ -181,8 +181,8 @@ payload += conversions.float_to_bytearray(p7Temp ) payload += conversions.float_to_bytearray(p16Temp ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_FLOW_DATA.value, payload=payload) @@ -200,8 +200,8 @@ payload = conversions.float_to_bytearray(p9Conductivity ) payload += conversions.float_to_bytearray(p18Conductivity ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_CONDUCTIVITY_DATA.value, payload=payload) @@ -252,8 +252,8 @@ payload += conversions.float_to_bytearray(p7Temp ) payload += conversions.float_to_bytearray(p16Temp ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_TEMPERATURE_DATA.value, payload=payload) @@ -292,8 +292,8 @@ payload += conversions.float_to_bytearray (p40PumpDutyCyclePct ) payload += conversions.float_to_bytearray (p40PumpFBDutyCyclePct ) - message = CAN.DenaliMessage.build_message( - channel_id=CAN.DenaliChannels.fp_sync_broadcast_ch_id, + message = CAN.CanMessage.build_message( + channel_id=CAN.CanChannels.fp_sync_broadcast_ch_id, message_id=msg_ids.MsgIds.MSG_ID_FP_BOOST_PUMP_DATA.value, payload=payload) Index: leahi_dialin/ui/td_messaging.py =================================================================== diff -u -r1f2bf6d939eb4033dbedb7d7005494cc12fccbc6 -r6c90336ac2cd8cf34ac620cff431a847d9ddf557 --- leahi_dialin/ui/td_messaging.py (.../td_messaging.py) (revision 1f2bf6d939eb4033dbedb7d7005494cc12fccbc6) +++ leahi_dialin/ui/td_messaging.py (.../td_messaging.py) (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -349,8 +349,10 @@ H2_arterial_long : float , H14_venous_long : float , tmp_pressure : float , + tmp_long : float , tmp_min : float , - tmp_max : float ): + tmp_max : float , + barometric : float ): """ Broadcasts the current TD Pressures data (Msg ID: 0x24, 36) Args: @@ -364,8 +366,10 @@ @param H2_arterial_long (float) : Arterial Long @param H14_venous_long (float) : Venous Long @param tmp_pressure (float) : TMP pressure + @param tmp_long (float) : TMP long @param tmp_min (float) : TMP minimum @param tmp_max (float) : TMP maximum + @param barometric (float) : barometric @return: None """ if not self.can_enabled: @@ -382,8 +386,10 @@ payload += conversions.float_to_bytearray (H2_arterial_long ) payload += conversions.float_to_bytearray (H14_venous_long ) payload += conversions.float_to_bytearray (tmp_pressure ) + payload += conversions.float_to_bytearray (tmp_long ) payload += conversions.float_to_bytearray (tmp_min ) payload += conversions.float_to_bytearray (tmp_max ) + payload += conversions.float_to_bytearray (barometric ) message = CAN.CanMessage.build_message( channel_id=CAN.CanChannels.td_sync_broadcast_ch_id, @@ -557,6 +563,50 @@ self.can_interface.send(message, 0) + + def td_water_sample( self, valve : int , + timeout : int ): + """ + Broadcasts the current TD Water Sample data (Msg ID: 0xAB) + Args: + @param valve (int) : water sample valve that is openend + @param timeout (int) : water sample timeout + @return: None + """ + if not self.can_enabled: + raise ValueError("CAN Interface is not enabled") + + + payload = conversions.unsigned_integer_to_bytearray (valve ) + payload += conversions.unsigned_integer_to_bytearray (timeout ) + + message = CAN.DenaliMessage.build_message( + channel_id=CAN.DenaliChannels.td_sync_broadcast_ch_id, + message_id=msg_ids.MsgIds.MSG_ID_TD_WATER_SAMPLE_DATA.value, + payload=payload) + self.can_interface.send(message, 0) + + + def td_water_sample_result_response(self, vAccepted : int , + vRejectionReason : int , + vValue : float ): + """ + the water sample result response message method(Msg ID: 0xXX, XXX) + Args: + None + @return: None + """ + payload = conversions.integer_to_bytearray (vAccepted ) + payload += conversions.integer_to_bytearray (vRejectionReason) + payload += conversions.float_to_bytearray (vValue ) + + message = CAN.DenaliMessage.build_message( + channel_id=CAN.DenaliChannels.td_to_ui_ch_id, + message_id=msg_ids.MsgIds.MSG_ID_TD_WATER_SAMPLE_RESULT_RESPONSE.value, + payload=payload) + self.can_interface.send(message, 0) + + def td_param_ranges(self, min_tx_time_s : int, max_tx_time_s : int, @@ -887,6 +937,7 @@ payload=payload) self.can_interface.send(message, 0) + def td_institutional_response(self, vRejectionReason : int, vBloodFlowMin : int, vBloodFlowMax : int, @@ -1346,21 +1397,27 @@ payload=payload) self.can_interface.send(message, 0) - def td_duration_validate_response(self, vRejectionReason: int, vDuration: int, vUFVolume: float, vUFRate: float): + def td_duration_validate_response(self, vRejectionReason: int, + vDuration: int, + vHeparinDuration: int, + vUFVolume: float, + vUFRate: float): """ the duration validate response message method(Msg ID: 0x84, 132) Args: @param vRejectionReason (int) : response rejection reason, if rejection reason is 0, then accepted (0) will be sent, otherwise rejected (1) @param vDuration (int) : treatment duration (min) - @param vUFVolume (float) : UF volume goal (mL) - @param vUFRate (float) : UF rate (L/hr) + @param vHeparinDuration (int) : heparin duration (min) + @param vUFVolume (float) : UF volume goal (L) + @param vUFRate (float) : UF rate (L/hr) @return: None """ payload = conversions.integer_to_bytearray(1 if vRejectionReason == 0 else 0 ) payload += conversions.integer_to_bytearray(vRejectionReason ) payload += conversions.integer_to_bytearray(vDuration ) + payload += conversions.integer_to_bytearray(vHeparinDuration ) payload += conversions.float_to_bytearray(vUFVolume ) payload += conversions.float_to_bytearray(vUFRate ) @@ -1496,14 +1553,14 @@ payload=payload) self.can_interface.send(message, 0) - def td_blood_set_auto_eject_response(self,vRejectionReason: int): + def td_blood_set_auto_eject_response(self, vAccepted: int, vRejectionReason: int): """ - the heparin adjustment response message method(Msg ID: 0xXX, XXX) + the blood set aito eject response message method(Msg ID: 0xXX, XXX) Args: None @return: None """ - payload = conversions.integer_to_bytearray(1 if vRejectionReason == 0 else 0) + payload = conversions.integer_to_bytearray(vAccepted) payload += conversions.integer_to_bytearray(vRejectionReason) message = CAN.CanMessage.build_message( @@ -1512,6 +1569,84 @@ payload=payload) self.can_interface.send(message, 0) + + def td_pressures_adjustment_response(self, vAccepted : int, + vArterialWindowRejectReason : int, + vVenousWindowRejectReason : int, + vVenousAsymmetricRejectReason: int, + vTmpWindowRejectReason : int): + """ + the pressures adjustment response message method(Msg ID: 0x73, 115) + Args: + None + @return: None + """ + payload = conversions.integer_to_bytearray(vAccepted ) + payload += conversions.integer_to_bytearray(vArterialWindowRejectReason ) + payload += conversions.integer_to_bytearray(vVenousWindowRejectReason ) + payload += conversions.integer_to_bytearray(vVenousAsymmetricRejectReason ) + payload += conversions.integer_to_bytearray(vTmpWindowRejectReason ) + + message = CAN.DenaliMessage.build_message( + channel_id=CAN.DenaliChannels.td_to_ui_ch_id, + message_id=msg_ids.MsgIds.MSG_ID_TD_PRESSURE_LIMITS_CHANGE_RESPONSE.value, + payload=payload) + self.can_interface.send(message, 0) + + + def td_set_points_adjustment_response(self, vAccepted : int, + vBloodFlowRejectReason : int, + vDialysateFlowRateRejectReason : int, + vspnDialysateTempRejectReason : int, + vAcidConcentrateRejectReason : int, + vAcidConcentrateFactorRejectReason: int, + vBicarbConcentrateRejectReason : int, + vTreatmentModalityRejectReason : int, + vHepatitisRejectReason : int, + vSodiumRejectReason : int, + vBicarbonateRejectReason : int): + """ + the set points adjustment response message method(Msg ID: 0x7B, 123) + Args: + None + @return: None + """ + payload = conversions.integer_to_bytearray(vAccepted ) + payload += conversions.integer_to_bytearray(vBloodFlowRejectReason ) + payload += conversions.integer_to_bytearray(vDialysateFlowRateRejectReason ) + payload += conversions.integer_to_bytearray(vspnDialysateTempRejectReason ) + payload += conversions.integer_to_bytearray(vAcidConcentrateRejectReason ) + payload += conversions.integer_to_bytearray(vAcidConcentrateFactorRejectReason ) + payload += conversions.integer_to_bytearray(vBicarbConcentrateRejectReason ) + payload += conversions.integer_to_bytearray(vTreatmentModalityRejectReason ) + payload += conversions.integer_to_bytearray(vHepatitisRejectReason ) + payload += conversions.integer_to_bytearray(vSodiumRejectReason ) + payload += conversions.integer_to_bytearray(vBicarbonateRejectReason ) + + message = CAN.DenaliMessage.build_message( + channel_id=CAN.DenaliChannels.td_to_ui_ch_id, + message_id=msg_ids.MsgIds.MSG_ID_TD_TREATMENT_SET_POINTS_CHANGE_RESPONSE.value, + payload=payload) + self.can_interface.send(message, 0) + + + def td_patient_disconnect_confirm_response(self, vAccepted: int, vRejectionReason: int): + """ + the patient disconnect confirm response message method(Msg ID: 0xXX, XXX) + Args: + None + @return: None + """ + payload = conversions.integer_to_bytearray(vAccepted) + payload += conversions.integer_to_bytearray(vRejectionReason) + + message = CAN.DenaliMessage.build_message( + channel_id=CAN.DenaliChannels.td_to_ui_ch_id, + message_id=msg_ids.MsgIds.MSG_ID_TD_ADJUST_PATIENT_DISCONNECT_CONFIRM_RESPONSE.value, + payload=payload) + self.can_interface.send(message, 0) + + def td_Treatment_Parameters_CreateRx(self, vRejectionReason: int): """ TD response to in initiate Treatment and enter Create Rx (Msg ID: 0x46, 70) @@ -1532,6 +1667,149 @@ self.can_interface.send(message, 0) + def td_post_tx_logs_response( self, + vAccepted : int, + vRejectionReason : int, + vBloodFlowRate : int, + vBloodFlowRateActual : int, + vDialysateFlowRate : int, + vDialysateFlowRateActual : int, + vTreatmentDuration : int, + vTreatmentDurationActual : int, + vAcidConcentrateType : int, + vAcidConcentrateTypeActual : int, + vBicarbonateCartridgeSize : int, + vBicarbonateCartridgeSizeActual : int, + vPotassiumConcentration : int, + vPotassiumConcentrationActual : int, + vCalciumConcentration : int, + vCalciumConcentrationActual : int, + vBicarbonateConcentration : int, + vBicarbonateConcentrationActual : int, + vSodiumConcentration : int, + vSodiumConcentrationActual : int, + vVitalsInterval : int, + vVitalsIntervalActual : int, + vDialyzerType : int, + vTreatmentStartEpoch : int, + vTreatmentEndEpoch : int, + vFluidBolusVolume : int, + vFluidBolusVolumeActual : int, + vHeparinType : int, + vHeparinDeliveryDuration : int, + vHeparinDeliveryDurationActual : int, + vTreatmentModality : int, + vTreatmentModalityActual : int, + vHDFOperatingMode : int, + vHDFOperatingModeActual : int, + vHepatitisStatus : int, + vDialysateTemperature : float, + vDialysateTemperatureActual : float, + vDialysateVolumeUsed : float, + vHeparinBolusVolume : float, + vHeparinBolusVolumeActual : float, + vHeparinDispenseRate : float, + vHeparinDispenseRateActual : float, + vHeparinDeliveredVolume : float, + vUFVolume : float, + vUFVolumeActual : float, + vUFVolumeTarget : float, + vUFRate : float, + vUFRateActual : float, + vUFRateTarget : float, + vSubstitutionVolume : float, + vSubstitutionVolumeActual : float, + vSubstitutionVolumeTarget : float, + vSubstitutionRate : float, + vSubstitutionRateActual : float, + vSubstitutionRateTarget : float, + vIsoUfVolume : float, + vIsoUfVolumeActual : float, + vIsoUfVolumeTarget : float, + vIsoUfRate : float, + vIsoUfRateActual : float, + vIsoUfRateTarget : float, + vWaterSampleTestResult : float + ): + # """ + # the Post Treatment logs records response message method(Msg ID: 0xXX, XXX) + # Args: + # None + # @return: None + # """ + # if not self.can_enabled: + # raise ValueError("CAN Interface is not enabled") + + payload = conversions.integer_to_bytearray(vAccepted ) + payload += conversions.integer_to_bytearray(vRejectionReason ) + payload += conversions.integer_to_bytearray(vBloodFlowRate ) + payload += conversions.integer_to_bytearray(vBloodFlowRateActual ) + payload += conversions.integer_to_bytearray(vDialysateFlowRate ) + payload += conversions.integer_to_bytearray(vDialysateFlowRateActual ) + payload += conversions.integer_to_bytearray(vTreatmentDuration ) + payload += conversions.integer_to_bytearray(vTreatmentDurationActual ) + payload += conversions.integer_to_bytearray(vAcidConcentrateType ) + payload += conversions.integer_to_bytearray(vAcidConcentrateTypeActual ) + payload += conversions.integer_to_bytearray(vBicarbonateCartridgeSize ) + payload += conversions.integer_to_bytearray(vBicarbonateCartridgeSizeActual ) + payload += conversions.integer_to_bytearray(vPotassiumConcentration ) + payload += conversions.integer_to_bytearray(vPotassiumConcentrationActual ) + payload += conversions.integer_to_bytearray(vCalciumConcentration ) + payload += conversions.integer_to_bytearray(vCalciumConcentrationActual ) + payload += conversions.integer_to_bytearray(vBicarbonateConcentration ) + payload += conversions.integer_to_bytearray(vBicarbonateConcentrationActual ) + payload += conversions.integer_to_bytearray(vSodiumConcentration ) + payload += conversions.integer_to_bytearray(vSodiumConcentrationActual ) + payload += conversions.integer_to_bytearray(vVitalsInterval ) + payload += conversions.integer_to_bytearray(vVitalsIntervalActual ) + payload += conversions.integer_to_bytearray(vDialyzerType ) + payload += conversions.integer_to_bytearray(vTreatmentStartEpoch ) + payload += conversions.integer_to_bytearray(vTreatmentEndEpoch ) + payload += conversions.integer_to_bytearray(vFluidBolusVolume ) + payload += conversions.integer_to_bytearray(vFluidBolusVolumeActual ) + payload += conversions.integer_to_bytearray(vHeparinType ) + payload += conversions.integer_to_bytearray(vHeparinDeliveryDuration ) + payload += conversions.integer_to_bytearray(vHeparinDeliveryDurationActual ) + payload += conversions.integer_to_bytearray(vTreatmentModality ) + payload += conversions.integer_to_bytearray(vTreatmentModalityActual ) + payload += conversions.integer_to_bytearray(vHDFOperatingMode ) + payload += conversions.integer_to_bytearray(vHDFOperatingModeActual ) + payload += conversions.integer_to_bytearray(vHepatitisStatus ) + payload += conversions.float_to_bytearray( vDialysateTemperature ) + payload += conversions.float_to_bytearray( vDialysateTemperatureActual ) + payload += conversions.float_to_bytearray( vDialysateVolumeUsed ) + payload += conversions.float_to_bytearray( vHeparinBolusVolume ) + payload += conversions.float_to_bytearray( vHeparinBolusVolumeActual ) + payload += conversions.float_to_bytearray( vHeparinDispenseRate ) + payload += conversions.float_to_bytearray( vHeparinDispenseRateActual ) + payload += conversions.float_to_bytearray( vHeparinDeliveredVolume ) + payload += conversions.float_to_bytearray( vUFVolume ) + payload += conversions.float_to_bytearray( vUFVolumeActual ) + payload += conversions.float_to_bytearray( vUFVolumeTarget ) + payload += conversions.float_to_bytearray( vUFRate ) + payload += conversions.float_to_bytearray( vUFRateActual ) + payload += conversions.float_to_bytearray( vUFRateTarget ) + payload += conversions.float_to_bytearray( vSubstitutionVolume ) + payload += conversions.float_to_bytearray( vSubstitutionVolumeActual ) + payload += conversions.float_to_bytearray( vSubstitutionVolumeTarget ) + payload += conversions.float_to_bytearray( vSubstitutionRate ) + payload += conversions.float_to_bytearray( vSubstitutionRateActual ) + payload += conversions.float_to_bytearray( vSubstitutionRateTarget ) + payload += conversions.float_to_bytearray( vIsoUfVolume ) + payload += conversions.float_to_bytearray( vIsoUfVolumeActual ) + payload += conversions.float_to_bytearray( vIsoUfVolumeTarget ) + payload += conversions.float_to_bytearray( vIsoUfRate ) + payload += conversions.float_to_bytearray( vIsoUfRateActual ) + payload += conversions.float_to_bytearray( vIsoUfRateTarget ) + payload += conversions.float_to_bytearray( vWaterSampleTestResult ) + + message = CAN.DenaliMessage.build_message( + channel_id=CAN.DenaliChannels.td_to_ui_ch_id, + message_id=msg_ids.MsgIds.MSG_ID_TD_ADJUST_TREATMENT_LOGS_RESPONSE.value, + payload=payload) + self.can_interface.send(message, 0) + + def td_Treatment_Parameters_Validation( self, vAccepted : int = 1, vTreatmentModalityRejectReason : int = 0, Index: leahi_dialin/utils/abstract_classes.py =================================================================== diff -u --- leahi_dialin/utils/abstract_classes.py (revision 0) +++ leahi_dialin/utils/abstract_classes.py (revision 6c90336ac2cd8cf34ac620cff431a847d9ddf557) @@ -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')