Index: dialin/common/__init__.py =================================================================== diff -u -r9fdaf53a622f2c87cc30080dde9b5e4c42dbef65 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/__init__.py (.../__init__.py) (revision 9fdaf53a622f2c87cc30080dde9b5e4c42dbef65) +++ dialin/common/__init__.py (.../__init__.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -5,3 +5,8 @@ from .hd_defs import * from .dg_defs import * from .ui_defs import * +from .leahi.leahi_msg_defs import * +from .leahi.leahi_prs_defs import * +from .leahi.leahi_ui_defs import * +from .leahi.td_defs import * +#from .dd_defs import * Index: dialin/common/alarm_defs.py =================================================================== diff -u -ra7253ef8e885d542d22539cd1e4c608f7ce63eb5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/alarm_defs.py (.../alarm_defs.py) (revision a7253ef8e885d542d22539cd1e4c608f7ce63eb5) +++ dialin/common/alarm_defs.py (.../alarm_defs.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import AlarmEnum +from dialin.utils.base import AlarmEnum # Branch: staging Index: dialin/common/alarm_priorities.py =================================================================== diff -u -ra7253ef8e885d542d22539cd1e4c608f7ce63eb5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/alarm_priorities.py (.../alarm_priorities.py) (revision a7253ef8e885d542d22539cd1e4c608f7ce63eb5) +++ dialin/common/alarm_priorities.py (.../alarm_priorities.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from dialin.utils.base import DialinEnum @unique Index: dialin/common/dg_defs.py =================================================================== diff -u -ra7253ef8e885d542d22539cd1e4c608f7ce63eb5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/dg_defs.py (.../dg_defs.py) (revision a7253ef8e885d542d22539cd1e4c608f7ce63eb5) +++ dialin/common/dg_defs.py (.../dg_defs.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from dialin.utils.base import DialinEnum @unique Index: dialin/common/hd_defs.py =================================================================== diff -u -ra7253ef8e885d542d22539cd1e4c608f7ce63eb5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/hd_defs.py (.../hd_defs.py) (revision a7253ef8e885d542d22539cd1e4c608f7ce63eb5) +++ dialin/common/hd_defs.py (.../hd_defs.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from dialin.utils.base import DialinEnum @unique Index: dialin/common/leahi/leahi_alarm_defs.py =================================================================== diff -u --- dialin/common/leahi/leahi_alarm_defs.py (revision 0) +++ dialin/common/leahi/leahi_alarm_defs.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,346 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 alarm_defs.py +# +# @author (last) Dara Navaei +# @date (last) 21-Jan-2025 +# @author (original) Peter Lucia +# @date (original) 07-Aug-2020 +# +############################################################ ################ +from enum import unique +from dialin.utils.base import AlarmEnum + + +# Branch: staging +@unique +class AlarmList(AlarmEnum): + ALARM_ID_NO_ALARM = 0 + ALARM_ID_TD_STUCK_BUTTON_TEST_FAILED = 1 + ALARM_ID_TD_FPGA_POST_TEST_FAILED = 2 + ALARM_ID_DD_FPGA_POST_TEST_FAILED = 3 + ALARM_ID_TD_WATCTDOG_POST_TEST_FAILED = 4 + ALARM_ID_DD_WATCTDOG_POST_TEST_FAILED = 5 + ALARM_ID_TD_UI_COMM_POST_FAILED = 6 + ALARM_ID_TD_RTC_BATTERY_LOW = 7 + ALARM_ID_TD_ACCELEROMETER_SELF_TEST_FAILURE = 8 + ALARM_ID_DD_ACCELEROMETER_SELF_TEST_FAILURE = 9 + ALARM_ID_TD_BLOOD_LEAK_SENSOR_EMBEDDED_MODE_FAILURE = 10 + ALARM_ID_TD_DIALYSATE_TEMP_BELOW_TARGET_TEMP = 11 + ALARM_ID_TD_INTEGRITY_POST_TEST_FAILED = 12 + ALARM_ID_DD_INTEGRITY_POST_TEST_FAILED = 13 + ALARM_ID_DD_INVALID_USAGE_RECORD_CRC = 14 + ALARM_ID_TD_DIALYSATE_TEMP_ABOVE_SAFETY_TEMP = 15 + ALARM_ID_TD_ALARM_AUDIO_SELF_TEST_FAILURE = 16 + ALARM_ID_TD_UI_POST_TIMEOUT = 17 + ALARM_ID_TD_CAN_MESSAGE_NOT_ACKED_BY_DD = 18 + ALARM_ID_TD_DIALYSATE_TEMP_ABOVE_TARGET_TEMP = 19 + ALARM_ID_DD_COND_SENSORS_INVALID_CAL_RECORD = 20 + ALARM_ID_DD_DRAIN_LINE_VOLUME_INVALID_CAL_RECORD = 21 + ALARM_ID_DD_RESERVOIRS_INVALID_CAL_RECORD = 22 + ALARM_ID_DD_ACID_CONCENTRATE_INVALID_CAL_RECORD = 23 + ALARM_ID_DD_BICARB_CONCENTRATE_INVALID_CAL_RECORD = 24 + ALARM_ID_DD_ACCELEROMETERS_INVALID_CAL_RECORD = 25 + ALARM_ID_TD_ACCELEROMETERS_INVALID_CAL_RECORD = 26 + ALARM_ID_TD_BLOOD_LEAK_SENSOR_ZERO_SEQUENCE_FAILED = 27 + ALARM_ID_DD_TWO_WIRE_SENSORS_FPGA_FAULT = 28 + ALARM_ID_TD_HEPARIN_FORCE_SENSOR_INVALID_CAL_RECORD = 29 + ALARM_ID_TD_SOFTWARE_FAULT = 30 + ALARM_ID_TD_BLOOD_PUMP_MC_CURRENT_CHECK = 31 + ALARM_ID_TD_BLOOD_PUMP_OFF_CHECK = 32 + ALARM_ID_TD_BLOOD_PUMP_MC_DIRECTION_CHECK = 33 + ALARM_ID_TD_BLOOD_PUMP_ROTOR_SPEED_CHECK = 34 + ALARM_ID_TD_DIAL_IN_PUMP_MC_CURRENT_CHECK = 35 + ALARM_ID_TD_DIAL_IN_PUMP_OFF_CHECK = 36 + ALARM_ID_TD_DIAL_IN_PUMP_MC_DIRECTION_CHECK = 37 + ALARM_ID_TD_DIAL_IN_PUMP_ROTOR_SPEED_CHECK = 38 + ALARM_ID_TD_DIAL_OUT_PUMP_MC_CURRENT_CHECK = 39 + ALARM_ID_TD_DIAL_OUT_PUMP_OFF_CHECK = 40 + ALARM_ID_TD_DIAL_OUT_PUMP_MC_DIRECTION_CHECK = 41 + ALARM_ID_TD_DIAL_OUT_PUMP_ROTOR_SPEED_CHECK = 42 + ALARM_ID_DD_MAIN_PRIMARY_HEATER_FPGA_FAULT = 43 + ALARM_ID_TD_UI_COMM_TIMEOUT = 44 + ALARM_ID_TD_COMM_TOO_MANY_BAD_CRCS = 45 + ALARM_ID_TD_CAN_MESSAGE_NOT_ACKED_BY_UI = 46 + ALARM_ID_DD_MAX_RO_PUMP_PWM_EXCEEDED = 47 + ALARM_ID_TD_UF_VOLUME_ACCURACY_ERROR = 48 + ALARM_ID_TD_FPGA_COMM_TIMEOUT = 49 + ALARM_ID_DD_VALVE_CONTROL_FAILURE = 50 + ALARM_ID_TD_BLOOD_PUMP_MOTOR_SPEED_CHECK = 51 + ALARM_ID_TD_DIAL_IN_PUMP_MOTOR_SPEED_CHECK = 52 + ALARM_ID_TD_DIAL_OUT_PUMP_MOTOR_SPEED_CHECK = 53 + ALARM_ID_TD_CRITICAL_DATA_ERROR = 54 + ALARM_ID_DD_CRITICAL_DATA_ERROR = 55 + ALARM_ID_TD_ACCELEROMETER_FAILURE = 56 + ALARM_ID_DD_ACCELEROMETER_FAILURE = 57 + ALARM_ID_TD_VALVE_HOMING_FAILED = 58 + ALARM_ID_TD_VALVE_TRANSITION_TIMEOUT = 59 + ALARM_ID_TD_VALVE_NOT_FUNCTIONAL = 60 + ALARM_ID_TD_VALVE_CURRENT_OUT_OF_RANGE = 61 + ALARM_ID_TD_VALVE_POSITION_OUT_OF_RANGE = 62 + ALARM_ID_TD_SYRINGE_PUMP_PRIME_TIMEOUT = 63 + ALARM_ID_DD_BARO_PRESSURE_OUT_OF_RANGE = 64 + ALARM_ID_TD_DD_COMMAND_INVALID_PARAMETER_FAULT = 65 + ALARM_ID_TD_BLOOD_LEAK_SENSOR_SET_POINT_SET_FAILURE = 66 + ALARM_ID_TD_BP_OCCLUSION_SELF_TEST_FAILURE = 67 + ALARM_ID_TD_ACTIVE_RESERVOIR_RECIRCULATION_OUT_OF_RANGE = 68 + ALARM_ID_TD_BLOOD_LEAK_INVALID_CAL_RECORD = 69 + ALARM_ID_TD_ARTERIAL_PRESSURE_SELF_TEST_FAILURE = 70 + ALARM_ID_TD_VENOUS_PRESSURE_SELF_TEST_FAILURE = 71 + ALARM_ID_TD_NEW_LOAD_CELL_DATA_MESSAGE_NOT_RECEIVE = 72 + ALARM_ID_TD_NEW_DIALYSATE_TEMP_DATA_MESSAGE_NOT_RECEIVE = 73 + ALARM_ID_DD_INLET_UV_REACTOR_ON_WITH_NO_FLOW = 74 + ALARM_ID_TD_SYRINGE_PUMP_SELF_TEST_FAILURE = 75 + ALARM_ID_TD_VOLTAGE_OUT_OF_RANGE = 76 + ALARM_ID_DD_VOLTAGE_OUT_OF_RANGE = 77 + ALARM_ID_TD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR = 78 + ALARM_ID_TD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR = 79 + ALARM_ID_TD_SYRINGE_PUMP_FAULT = 80 + ALARM_ID_TD_SYRINGE_PUMP_OVER_TRAVEL_ERROR = 81 + ALARM_ID_TD_SYRINGE_PUMP_DAC_WRITE_ERROR = 82 + ALARM_ID_TD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR = 83 + ALARM_ID_DD_SET_RTC_YEAR_INVALID = 84 + ALARM_ID_TD_PUMP_TRACK_LATCH_OPENED = 85 + ALARM_ID_TD_SET_RTC_YEAR_INVALID = 86 + ALARM_ID_DD_HEATING_INVALID_CAL_RECORD = 87 + ALARM_ID_DD_CONC_PUMP_HALL_SENSOR_OUT_OF_RANGE = 88 + ALARM_ID_DD_OUTLET_UV_REACTOR_ON_WITH_NO_FLOW = 89 + ALARM_ID_DD_LOAD_CELL_A1_B1_FPGA_FAULT = 90 + ALARM_ID_TD_DIALYSATE_FLOW_DATA_NOT_RECEIVE = 91 + ALARM_ID_DD_TEMPERATURE_SENSORS_INVALID_CAL_RECORD = 92 + ALARM_ID_DD_OUTLET_PRIMARY_CONDUCTIVITY_OUT_OF_RANGE = 93 + ALARM_ID_DD_PRESSURE_OUT_OF_RANGE = 94 + ALARM_ID_DD_WATCTDOG_EXPIRED = 95 + ALARM_ID_DD_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE = 96 + ALARM_ID_DD_FILL_CONDUCTIVITY_OUT_OF_RANGE = 97 + ALARM_ID_TD_BATTERY_COMM_FAULT = 98 + ALARM_ID_TD_SYRINGE_PUMP_STALL = 99 + ALARM_ID_DD_COND_SENSORS_INVALID_TEMP_COMP_CAL_RECORD = 100 + ALARM_ID_UI_POST_TD_COMM = 101 + ALARM_ID_DD_HEAT_DISINFECT_TEMP_GRAD_OUT_OF_RANGE = 102 + ALARM_ID_TD_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID = 103 + ALARM_ID_TD_AIR_TRAP_ILLEGAL_LEVELS = 104 + ALARM_ID_DD_NVDATAMGMT_CAL_GROUP_RECORD_CRC_INVALID = 105 + ALARM_ID_DD_FLOW_SENSORS_INVALID_CAL_RECORD = 106 + ALARM_ID_TD_DD_RESTARTED_FAULT = 107 + ALARM_ID_TD_SYRINGE_PUMP_FPGA_ADC_FAULT = 108 + ALARM_ID_TD_SYRINGE_PUMP_VOLUME_ERROR = 109 + ALARM_ID_TD_SYRINGE_PUMP_SPEED_ERROR = 110 + ALARM_ID_TD_SYRINGE_PUMP_NOT_STOPPED_ERROR = 111 + ALARM_ID_DD_LOAD_CELL_A2_B2_FPGA_FAULT = 112 + ALARM_ID_DD_DRAIN_PUMP_CURRENT_OUT_OF_RANGE = 113 + ALARM_ID_TD_VENOUS_BUBBLE_SELF_TEST_FAILURE = 114 + ALARM_ID_DD_TEMPERATURE_SENSOR_OUT_OF_RANGE = 115 + ALARM_ID_TD_UI_SDCARD_FAILURE = 116 + ALARM_ID_DD_CAN_MESSAGE_NOT_ACKED = 117 + ALARM_ID_DD_RTC_CONFIG_ERROR = 118 + ALARM_ID_DD_RTC_BATTERY_LOW = 119 + ALARM_ID_TD_PRE_TREATMENT_WET_PRIME_TEST_FAILURE = 120 + ALARM_ID_DD_MAIN_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE = 121 + ALARM_ID_DD_SMALL_PRIMARY_HEATER_VOLTAGE_OUT_OF_RANGE = 122 + ALARM_ID_DD_TRIMMER_HEATER_VOLTAGE_OUT_OF_RANGE = 123 + ALARM_ID_TD_END_OF_TREATMENT_HIGH = 124 + ALARM_ID_TD_TREATMENT_STOPPED_NO_RINSEBACK = 125 + ALARM_ID_TD_BLOOD_LEAK_DETECTED = 126 + ALARM_ID_TD_VENOUS_PRESSURE_LOW = 127 + ALARM_ID_TD_VENOUS_BUBBLE_DETECTED = 128 + ALARM_ID_TD_BLOOD_LEAK_RECOVERING_PLEASE_WAIT = 129 + ALARM_ID_TD_VENOUS_PRESSURE_HIGH = 130 + ALARM_ID_TD_ARTERIAL_PRESSURE_LOW = 131 + ALARM_ID_TD_ARTERIAL_PRESSURE_HIGH = 132 + ALARM_ID_DD_FLUID_LEAK_DETECTED = 133 + ALARM_ID_TD_FLUID_LEAK_DETECTED = 134 + ALARM_ID_TD_SHOCK = 135 + ALARM_ID_DD_SHOCK = 136 + ALARM_ID_TD_EXCESSIVE_TILT = 137 + ALARM_ID_DD_EXCESSIVE_TILT = 138 + ALARM_ID_TD_AC_POWER_LOST = 139 + ALARM_ID_TD_DD_COMM_TIMEOUT = 140 + ALARM_ID_TD_AIR_TRAP_FILL_DURING_TREATMENT = 141 + ALARM_ID_TD_OCCLUSION_BLOOD_PUMP = 142 + ALARM_ID_DD_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE = 143 + ALARM_ID_DD_CLEANING_MODE_INLET_WATER_COND_TOO_HIGH = 144 + ALARM_ID_DD_ACID_CONDUCTIVITY_OUT_OF_RANGE = 145 + ALARM_ID_DD_RTC_OR_TIMER_ACCURACY_FAILURE = 146 + ALARM_ID_DD_CREATING_DIALYSATE_PLEASE_WAIT = 147 + ALARM_ID_DD_CLEANING_MODE_INLET_WATER_TEMP_TOO_HIGH = 148 + ALARM_ID_TD_WATCTDOG_EXPIRED = 149 + ALARM_ID_DD_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE = 150 + ALARM_ID_DD_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE = 151 + ALARM_ID_DD_INLET_WATER_PRESSURE_IN_LOW_RANGE = 152 + ALARM_ID_TD_PRIME_COMPLETED_HIGH = 153 + ALARM_ID_DD_CLEANING_MODE_INLET_WATER_COND_TOO_LOW = 154 + ALARM_ID_TD_NEW_RESERVOIRS_DATA_MESSAGE_NOT_RECEIVE = 155 + ALARM_ID_TD_DD_NEW_OPERATION_MODE_MESSAGE_NOT_RECEIVE = 156 + ALARM_ID_DD_CHEM_DISINFECT_PRIME_ACID_LINE_TIME_OUT = 157 + ALARM_ID_DD_INLET_WATER_TEMPERATURE_IN_LOW_RANGE = 158 + ALARM_ID_DD_CHEM_DISINFECT_FLUSH_REMOVE_ACID = 159 + ALARM_ID_TD_BLOOD_PUMP_ROTOR_SPEED_TOO_HIGH = 160 + ALARM_ID_DD_COMM_TOO_MANY_BAD_CRCS = 161 + ALARM_ID_DD_FPGA_CLOCK_SPEED_CHECK_FAILURE = 162 + ALARM_ID_TD_LOAD_CELL_ACCELERATION_RES_1_ALARM = 163 + ALARM_ID_TD_LOAD_CELL_ACCELERATION_RES_2_ALARM = 164 + ALARM_ID_TD_TREATMENT_RINSEBACK_TIMEOUT_ALARM = 165 + ALARM_ID_TEST_ALARM_NO_BLOOD_RECIRC_CLR_TOP_ONLY = 166 + ALARM_ID_TD_CARTRIDDE_DOOR_OPENED = 167 + ALARM_ID_TEST_ALARM_NO_DIAL_RECIRC_CLR_TOP_ONLY = 168 + ALARM_ID_DD_DIALYSATE_FLOW_RATE_OUT_OF_MAX_RANGE = 169 + ALARM_ID_TD_SYRINGE_PUMP_SYRINGE_EMPTY = 170 + ALARM_ID_TD_SYRINGE_PUMP_OCCLUSION = 171 + ALARM_ID_TD_SYRINGE_PUMP_NOT_ENOUGH_HEPARIN_ALARM = 172 + ALARM_ID_TD_RTC_CONFIG_ERROR = 173 + ALARM_ID_TD_RTC_OR_TIMER_ACCURACY_FAILURE = 174 + ALARM_ID_TD_PUMP_DIRECTION_STATUS_ERROR = 175 + ALARM_ID_TEST_ALARM_NO_RECIRC_CLR_TOP_ONLY = 176 + ALARM_ID_DD_SOFTWARE_FAULT = 177 + ALARM_ID_TD_COMM_TIMEOUT = 178 + ALARM_ID_DD_FPGA_COMM_TIMEOUT = 179 + ALARM_ID_DD_RO_FLOW_RATE_OUT_OF_MAX_RANGE = 180 + ALARM_ID_DD_LOAD_CELLS_TARE_WEIGHT_OUT_OF_RANGE = 181 + ALARM_ID_DD_LOAD_CELLS_INVALID_CAL_RECORD = 182 + ALARM_ID_DD_INVALID_LOAD_CELL_VALUE = 183 + ALARM_ID_DD_INLET_UV_REACTOR_NOT_HEALTHY = 184 + ALARM_ID_DD_FAN_RPM_OUT_OF_RANGE = 185 + ALARM_ID_DD_CONCENTRATE_PUMP_FAULT = 186 + ALARM_ID_DD_CP1_SPEED_CONTROL_ERROR = 187 + ALARM_ID_DD_CP2_SPEED_CONTROL_ERROR = 188 + ALARM_ID_DD_DRAIN_PUMP_RPM_OUT_OF_RANGE = 189 + ALARM_ID_DD_DRAIN_PUMP_OFF_FAULT = 190 + ALARM_ID_DD_FLOW_RATE_OUT_OF_UPPER_RANGE = 191 + ALARM_ID_DD_FLOW_RATE_OUT_OF_LOWER_RANGE = 192 + ALARM_ID_TD_PARTIAL_OCCLUSION_BLOOD_PUMP = 193 + ALARM_ID_DD_RO_PUMP_DUTY_CYCLE_OUT_OF_RANGE = 194 + ALARM_ID_DD_RO_PUMP_PRESSURE_OUT_OF_RANGE = 195 + ALARM_ID_DD_CPI_CPO_SENSORS_FPGA_FAULT = 196 + ALARM_ID_DD_CD1_CD2_SENSORS_FPGA_FAULT = 197 + ALARM_ID_DD_RO_FLOW_TOO_LOW_WHILE_PRIMARY_HEATER_IS_ON = 198 + ALARM_ID_DD_DIALYSATE_FLOW_TOO_LOW_WHILE_TRIMMER_HEATER_IS_ON = 199 + ALARM_ID_DD_THERMISTORS_TEMPERATURE_OUT_OF_RANGE = 200 + ALARM_ID_TD_PRE_TREATMENT_WET_FLOW_TEST_FAILURE = 201 + ALARM_ID_TD_PRE_TREATMENT_DRY_PRESSURE_NORMAL_TEST_FAILURE = 202 + ALARM_ID_DD_FILL_CONDUCTIVITIES_INVALID_CAL_RECORD = 203 + ALARM_ID_DD_RO_REJECTION_RATIO_OUT_OF_RANGE = 204 + ALARM_ID_DD_CONDUCTIVITY_SENSOR_FAULT = 205 + ALARM_ID_DD_DIALYSATE_FILL_OUT_OF_TIME = 206 + ALARM_ID_DD_FLOW_METER_CHECK_FAILURE = 207 + ALARM_ID_TD_VENOUS_LINE_OCCLUSION = 208 + ALARM_ID_DD_DRAIN_CIRCULATION_LINE_TIMEOUT = 209 + ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED = 210 + ALARM_ID_TD_BLOOD_SITTING_WARNING = 211 + ALARM_ID_TD_END_OF_TREATMENT_ALARM = 212 + ALARM_ID_TD_PRIME_COMPLETED_MEDIUM = 213 + ALARM_ID_DD_CONCENTRATE_PUMPS_INVALID_CAL_RECORD = 214 + ALARM_ID_AVAILABLE_31 = 215 + ALARM_ID_TD_END_TREATMENT_TIMEOUT_ALARM = 216 + ALARM_ID_AVAILABLE_32 = 217 + ALARM_ID_TD_SYRINGE_DETECTED = 218 + ALARM_ID_TD_SYRINGE_PUMP_SYRINGE_REMOVED = 219 + ALARM_ID_DD_TTD_SENSORS_FPGA_FAULT = 220 + ALARM_ID_TD_EMPTY_SALINE_BAG = 221 + ALARM_ID_TD_OCCLUSION_SENSOR_FPGA_FAULT = 222 + ALARM_ID_TD_ARTERIAL_SENSOR_FPGA_FAULT = 223 + ALARM_ID_TD_TREATMENT_STOPPED_BY_USER = 224 + ALARM_ID_TD_END_OF_TREATMENT_WARNING = 225 + ALARM_ID_TD_PRIME_COMPLETED_LOW_PRIORITY = 226 + ALARM_ID_AVAILABLE_15 = 227 + ALARM_ID_TD_PRIME_SALINE_PURGE_AIR_TIME_OUT = 228 + ALARM_ID_TD_PRIME_DIALYSATE_DIALYZER_TIME_OUT = 229 + ALARM_ID_TD_PRIME_DIALYSATE_BYPASS_TIME_OUT = 230 + ALARM_ID_TD_PRE_TREATMENT_DRY_PRESSURE_TEST_FAILURE = 231 + ALARM_ID_TD_PRE_TREATMENT_WET_LC_TEST_FAILURE = 232 + ALARM_ID_DD_BAROMETRIC_SENSOR_COEFFS_BAD_CRC = 233 + ALARM_ID_DD_INLET_WATER_PRESSURE_IN_HIGH_RANGE = 234 + ALARM_ID_TD_TREATMENT_STOPPED_AFTER_RINSEBACK = 235 + ALARM_ID_TD_INSTALL_NEW_CARTRIDDE = 236 + ALARM_ID_AVAILABLE_13 = 237 + ALARM_ID_TD_NO_CARTRIDDE_LOADED = 238 + ALARM_ID_TD_CARTRIDDE_REMOVAL_FAILURE = 239 + ALARM_ID_DD_BICARB_CONDUCTIVITY_OUT_OF_RANGE = 240 + ALARM_ID_DD_RESERVOIR_DRAIN_TIMEOUT = 241 + ALARM_ID_DD_RESERVOIR_FILL_TIMEOUT = 242 + ALARM_ID_DD_RESERVOIR_LEAK_TIMEOUT = 243 + ALARM_ID_DD_CLEANING_MODE_TEMP_SENSORS_DIFF_OUT_OF_RANGE = 244 + ALARM_ID_DD_HEAT_DISINFECT_TARGET_TEMP_TIMEOUT = 245 + ALARM_ID_DD_CLEANING_MODE_COND_SENSORS_OUT_OF_RANGE = 246 + ALARM_ID_AVAILABLE_7 = 247 + ALARM_ID_DD_CHEM_DISINFECT_TARGET_TEMP_OUT_OF_RANGE = 248 + ALARM_ID_DD_CHEM_DISINFECT_TARGET_COND_OUT_OF_RANGE = 249 + ALARM_ID_DD_CHEM_DISINFECT_INSERT_ACID = 250 + ALARM_ID_TD_INVALID_SYSTEM_RECORD_CRC = 251 + ALARM_ID_TD_INVALID_SERVICE_RECORD_CRC = 252 + ALARM_ID_DD_INVALID_SYSTEM_RECORD_CRC = 253 + ALARM_ID_DD_INVALID_SERVICE_RECORD_CRC = 254 + ALARM_ID_TD_UI_COMPATIBILITY_ERROR = 255 + ALARM_ID_TD_DISINFECT_CHEM_FLUSH = 256 + ALARM_ID_TD_INVALID_INSTITUTIONAL_RECORD_CRC = 257 + ALARM_ID_TD_UI_POST_FAILURE_OS_VERSION = 258 + ALARM_ID_TD_TEMPERATURES_OUT_OF_RANGE = 259 + ALARM_ID_TD_UI_POST_FAILURE_SHASUM = 260 + ALARM_ID_TD_UI_POST_FAILURE_CANBUS = 261 + ALARM_ID_TD_UI_POST_FAILURE_DISPLAY = 262 + ALARM_ID_TD_UI_POST_FAILURE_TOUCH = 263 + ALARM_ID_TD_UI_POST_FAILURE_SDCARD = 264 + ALARM_ID_TD_UI_POST_FAILURE_RTC = 265 + ALARM_ID_TD_UI_POST_FAILURE_WIFI = 266 + ALARM_ID_TD_UI_POST_FAILURE_BLUETOOTH = 267 + ALARM_ID_TD_UI_POST_FAILURE_ETHERNET = 268 + ALARM_ID_TD_UI_POST_FAILURE_SOUND = 269 + ALARM_ID_TD_SAFETY_SHUTDOWN_POST_TEST_FAILED = 270 + ALARM_ID_DD_SAFETY_SHUTDOWN_POST_TEST_FAILED = 271 + ALARM_ID_TD_FAN_RPM_OUT_OF_RANGE = 272 + ALARM_ID_AVAILABLE_10 = 273 + ALARM_ID_DD_INACTIVE_RESERVOIR_WEIGHT_OUT_OF_RANGE = 274 + ALARM_ID_TD_ARTERIAL_PRESSURE_OUT_OF_RANGE = 275 + ALARM_ID_TD_VENOUS_PRESSURE_OUT_OF_RANGE = 276 + ALARM_ID_TD_BP_OCCLUSION_OUT_OF_RANGE = 277 + ALARM_ID_TD_ACTIVE_RESERVOIR_WEIGHT_OUT_OF_RANGE = 278 + ALARM_ID_DD_DIALYSATE_DRAIN_TIME_OUT = 279 + ALARM_ID_DD_DRAIN_PUMP_DIRECTION_INVALID = 280 + ALARM_ID_DD_ACID_BOTTLE_LOW_VOLUME = 281 + ALARM_ID_DD_BICARB_BOTTLE_LOW_VOLUME = 282 + ALARM_ID_DD_LOAD_CELL_WEIGHT_OUT_OF_RANGE = 283 + ALARM_ID_DD_LOAD_CELL_PRIMARY_BACKUP_DRIFT_OUT_OF_RANGE = 284 + ALARM_ID_TD_RO_PERMEATE_SAMPLE = 285 + ALARM_ID_DD_CONCENTRATE_CAP_NOT_IN_PROPER_POSITION = 286 + ALARM_ID_TD_FPGA_CLOCK_SPEED_CHECK_FAILURE = 287 + ALARM_ID_TD_LOAD_CELL_PRIMARY_BACKUP_DRIFT_OUT_OF_RANGE = 288 + ALARM_ID_DD_DIALYSATE_CAP_NOT_IN_PROPER_POSITION = 289 + ALARM_ID_TD_VENOUS_SENSOR_FPGA_FAULT = 290 + ALARM_ID_TD_DISINFECT_FLUSH = 291 + ALARM_ID_TD_DISINFECT_HEAT = 292 + ALARM_ID_TD_DISINFECT_CHEM = 293 + ALARM_ID_TD_DISINFECT_HEAT_COOL = 294 + ALARM_ID_TD_UI_POST_FAILURE_CLOUDSYNC = 295 + ALARM_ID_DD_TDI_SENSORS_FPGA_FAULT = 296 + ALARM_ID_DD_TRO_SENSORS_FPGA_FAULT = 297 + ALARM_ID_DD_BARO_SENSOR_FPGA_FAULT = 298 + ALARM_ID_DD_INVALID_SERIAL_NUMBER = 299 + ALARM_ID_TD_INVALID_SERIAL_NUMBER = 300 + ALARM_ID_DD_RO_PERMEATE_SAMPLE_REMOVE_DIA_CAP = 301 + ALARM_ID_DD_CPU_RAM_ERROR = 302 + ALARM_ID_DD_DRAIN_PUMP_DIRECTION_FPGA_FAULT = 303 + ALARM_ID_TD_INVALID_USAGE_RECORD_CRC = 304 + ALARM_ID_TD_CPU_RAM_ERROR = 305 + ALARM_ID_DD_TURN_OFF_INLET_WATER_VALVES = 306 + ALARM_ID_TD_AC_POWER_LOST_IN_TREATMENT = 307 + ALARM_ID_DD_CPI_COND_SENSOR_INVALID_CHAR = 308 + ALARM_ID_DD_CPO_COND_SENSOR_INVALID_CHAR = 309 + ALARM_ID_DD_CD1_COND_SENSOR_INVALID_CHAR = 310 + ALARM_ID_DD_CD2_COND_SENSOR_INVALID_CHAR = 311 + ALARM_ID_TD_DIAL_IN_FLOW_CHECK_FAILURE = 312 + ALARM_ID_DD_CLEANING_MODE_INLET_WATER_TEMP_TOO_LOW = 313 + ALARM_ID_DD_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_HIGH = 314 + ALARM_ID_DD_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_LOW = 315 + ALARM_ID_AVAILABLE_20 = 316 + ALARM_ID_AVAILABLE_21 = 317 + ALARM_ID_AVAILABLE_2 = 318 + ALARM_ID_DD_CHEM_DISINFECT_FLUSH_FLUSH_SAMPLE = 319 + ALARM_ID_DD_CHEM_DISINFECT_FLUSH_SAMPLE_TIMEOUT = 320 + ALARM_ID_DD_OUTLET_UV_REACTOR_NOT_HEALTHY = 321 + ALARM_ID_TD_UI_POST_FAILURE_INVALID_YEAR = 322 + ALARM_ID_TD_UI_POST_FAILURE_SETTINGS_BAD = 323 Index: dialin/common/leahi/leahi_msg_defs.py =================================================================== diff -u --- dialin/common/leahi/leahi_msg_defs.py (revision 0) +++ dialin/common/leahi/leahi_msg_defs.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,203 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 msg_defs.py +# +# @author (last) Dara Navaei +# @date (last) 08-May-2024 +# @author (original) Peter Lucia +# @date (original) 07-Aug-2020 +# +############################################################################ +from enum import unique +from dialin.utils.base import DialinEnum +from .leahi_msg_ids import MsgIds + + +# Define msg ids that are not yet added to common but are needed in dialin + +@unique +class MsgIdsDialin(DialinEnum): + + MSG_DIALIN_ID_TD_SERIAL_NUMBER_RESPONSE = 0x87 + MSG_DIALIN_ID_DD_SERIAL_NUMBER_RESPONSE = 0x88 + MSG_DIALIN_ID_UI_SYSTEM_USAGE_REQUEST = 0x89 + MSG_DIALIN_ID_TD_SYSTEM_USAGE_RESPONSE = 0x8A + MSG_DIALIN_ID_DD_SYSTEM_USAGE_RESPONSE = 0x8C + MSG_DIALIN_ID_TD_FLUID_LEAK_STATE_DETECTOR_OVERRIDE = 0x8047 + MSG_DIALIN_ID_TD_VALVES_POSITION_COUNT_OVERRIDE = 0x8058 + MSG_DIALIN_ID_TD_DISINFECT_STATE = 0x7E + MSG_DIALIN_ID_TD_VERSION_REQUEST = 0x9E + MSG_DIALIN_ID_UI_POST_REPORT_VERSION = 0x9F + + +ACK_NOT_REQUIRED = [ + MsgIds.MSG_ID_ALARM_STATUS_DATA.value, + MsgIds.MSG_ID_ALARM_TRIGGERED.value, + MsgIds.MSG_ID_ALARM_CLEARED.value, + MsgIds.MSG_ID_ALARM_CONDITION_CLEARED.value, + MsgIds.MSG_ID_TD_ALARM_INFORMATION_DATA.value, + MsgIds.MSG_ID_DD_ALARM_INFO_DATA.value, + MsgIds.MSG_ID_TD_BLOOD_PUMP_DATA.value, + MsgIds.MSG_ID_TD_OP_MODE_DATA.value, + MsgIds.MSG_ID_DD_OP_MODE_DATA.value, + MsgIds.MSG_ID_DD_VALVES_STATES_DATA.value, + MsgIds.MSG_ID_DD_PRESSURES_DATA.value, + MsgIds.MSG_ID_TD_VOLTAGES_DATA.value, + MsgIds.MSG_ID_TD_BUBBLES_DATA.value, + MsgIds.MSG_ID_DD_CONDUCTIVITY_DATA.value, + MsgIds.MSG_ID_TD_AIR_PUMP_DATA.value, + MsgIds.MSG_ID_TD_SWITCHES_DATA.value, + MsgIds.MSG_ID_POWER_OFF_WARNING.value, + MsgIds.MSG_ID_TD_PRESSURE_DATA.value, + MsgIds.MSG_ID_DD_CONCENTRATE_PUMP_DATA.value, + MsgIds.MSG_ID_DD_TEMPERATURE_DATA.value, + MsgIds.MSG_ID_DIALYSATE_PUMPS_DATA.value, + MsgIds.MSG_ID_DD_HEATERS_DATA.value, + MsgIds.MSG_ID_DD_LEVEL_DATA.value, + MsgIds.MSG_ID_TD_AIR_TRAP_DATA.value, + MsgIds.MSG_ID_TD_VALVES_DATA.value, + MsgIds.MSG_ID_RO_ALARM_INFO_DATA.value, + MsgIds.MSG_ID_DD_BAL_CHAMBER_DATA.value, + MsgIds.MSG_ID_DD_GEN_DIALYSATE_MODE_DATA.value, + MsgIds.MSG_ID_RO_VALVES_STATES_DATA.value, + MsgIds.MSG_ID_RO_PUMP_DATA.value, + MsgIds.MSG_ID_RO_OP_MODE_DATA.value, + MsgIds.MSG_ID_RO_PRESSURES_DATA.value, + MsgIds.MSG_ID_RO_LEVEL_DATA.value, + MsgIds.MSG_ID_RO_FLOW_DATA.value, + MsgIds.MSG_ID_RO_CONDUCTIVITY_DATA.value, + MsgIds.MSG_ID_RO_TEMPERATURE_DATA.value, + MsgIds.MSG_ID_RO_HEATER_DATA.value, + MsgIds.MSG_ID_DD_PISTON_PUMP_CONTROL_DATA.value +] + + +@unique +class RequestRejectReasons(DialinEnum): + REQUEST_REJECT_REASON_NONE = 0 + REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE = 1 + REQUEST_REJECT_REASON_TIMEOUT_WAITING_FOR_USER_CONFIRM = 2 + REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE = 3 + REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE = 4 + REQUEST_REJECT_REASON_TREATMENT_TOO_CLOSE_TO_FINISHED = 5 + REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE = 6 + REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT = 7 + REQUEST_REJECT_REASON_BLOOD_FLOW_OUT_OF_RANGE = 8 + REQUEST_REJECT_REASON_DIAL_FLOW_OUT_OF_RANGE = 9 + REQUEST_REJECT_REASON_DIAL_VOLUME_OUT_OF_RANGE = 10 + REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE = 11 + REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE = 12 + REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_MINIMUM = 13 + REQUEST_REJECT_REASON_UF_NOT_IN_PROGESS = 14 + REQUEST_REJECT_REASON_UF_NOT_PAUSED = 15 + REQUEST_REJECT_REASON_SALINE_BOLUS_IN_PROGRESS = 16 + REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE = 17 + REQUEST_REJECT_REASON_HEPARIN_PRESTOP_EXCEEDS_DURATION = 18 + REQUEST_REJECT_REASON_DD_FILTER_FLUSH_HAS_BEEN_EXPIRED = 19 + REQUEST_REJECT_REASON_AVAILABLE_2 = 20 + REQUEST_REJECT_REASON_SALINE_MAX_VOLUME_REACHED = 21 + REQUEST_REJECT_REASON_SALINE_BOLUS_NOT_IN_PROGRESS = 22 + REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE = 23 + REQUEST_REJECT_REASON_ALARM_IS_ACTIVE = 24 + REQUEST_REJECT_REASON_INVALID_COMMAND = 25 + REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED = 26 + REQUEST_REJECT_REASON_ADDL_RINSEBACK_MAX_VOLUME_REACHED = 27 + REQUEST_REJECT_REASON_DIALYZER_NOT_INVERTED = 28 + REQUEST_REJECT_REASON_NO_PATIENT_CONNECTION_CONFIRM = 29 + REQUEST_REJECT_REASON_HEPARIN_PAUSE_INVALID_IN_THIS_STATE = 30 + REQUEST_REJECT_REASON_HEPARIN_NOT_PAUSED = 31 + REQUEST_REJECT_REASON_DD_COMM_LOST = 32 + REQUEST_REJECT_REASON_DRAIN_NOT_COMPLETE = 33 + REQUEST_REJECT_REASON_DD_NOT_IN_STANDBY_IDLE_STATE = 34 + REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT = 35 + REQUEST_REJECT_REASON_INVALID_DATE_OR_TIME = 36 + REQUEST_REJECT_REASON_TREATMENT_IN_PROGRESS = 37 + REQUEST_REJECT_REASON_BATTERY_IS_NOT_CHARGED = 38 + REQUEST_REJECT_REASON_RINSEBACK_NOT_COMPLETED = 39 + REQUEST_REJECT_REASON_RESERVOIR_ONE_IS_NOT_READY = 40 + REQUEST_REJECT_REASON_PUMP_TRACK_NOT_CLOSED = 41 + REQUEST_REJECT_REASON_DOOR_NOT_CLOSED = 42 + REQUEST_REJECT_REASON_SYRINGE_NOT_PRESENT = 43 + REQUEST_REJECT_REASON_DD_DIALYSATE_CAP_OPEN = 44 + REQUEST_REJECT_REASON_DD_CONCENTRATE_CAP_OPEN = 45 + REQUEST_REJECT_REASON_DD_DISINFECT_HAS_BEEN_EXPIRED = 46 + REQUEST_REJECT_REASON_DD_SERVICE_IS_DUE = 47 + REQUEST_REJECT_REASON_TD_SERVICE_IS_DUE = 48 + REQUEST_REJECT_REASON_DD_CHEM_FLUSH_NOT_COMPLETED = 49 + REQUEST_REJECT_REASON_DD_RO_FILTER_TEMPERATURE_OUT_OF_RANGE = 50 + REQUEST_REJECT_REASON_DD_INCOMPATIBLE = 51 + REQUEST_REJECT_REASON_HEPARIN_PRESTOP_WITH_NO_DISPENSE = 52 + REQUEST_REJECT_REASON_DIALYZER_REPRIME_IN_PROGRESS = 53 + REQUEST_REJECT_REASON_DD_RO_ONLY_MODE_DD_BUSY = 54 + REQUEST_REJECT_REASON_DD_RO_ONLY_MODE_INVALID_PARAMETER = 55 + REQUEST_REJECT_REASON_DD_RO_ONLY_MODE_INVALID_PAYLOAD_LENGTH = 56 + REQUEST_REJECT_REASON_TREATMENT_CANNOT_BE_RESUMED = 57 + REQUEST_REJECT_REASON_EXCEEDS_MAXIMUM_HEPARIN_VOLUME = 58 + REQUEST_REJECT_REASON_CHEM_DISINFECT_NOT_ENABLED_INST_CONFIG = 59 + NUM_OF_REQUEST_REJECT_REASONS = 60 + +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.LeahiMessage 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 + START_POS_FIELD_3 = END_POS_FIELD_2 + END_POS_FIELD_3 = START_POS_FIELD_3 + 4 + START_POS_FIELD_4 = END_POS_FIELD_3 + END_POS_FIELD_4 = START_POS_FIELD_4 + 4 + START_POS_FIELD_5 = END_POS_FIELD_4 + END_POS_FIELD_5 = START_POS_FIELD_5 + 4 + START_POS_FIELD_6 = END_POS_FIELD_5 + END_POS_FIELD_6 = START_POS_FIELD_6 + 4 + START_POS_FIELD_7 = END_POS_FIELD_6 + END_POS_FIELD_7 = START_POS_FIELD_7 + 4 + START_POS_FIELD_8 = END_POS_FIELD_7 + END_POS_FIELD_8 = START_POS_FIELD_8 + 4 + START_POS_FIELD_9 = END_POS_FIELD_8 + END_POS_FIELD_9 = START_POS_FIELD_9 + 4 + START_POS_FIELD_10 = END_POS_FIELD_9 + END_POS_FIELD_10 = START_POS_FIELD_10 + 4 + START_POS_FIELD_11 = END_POS_FIELD_10 + END_POS_FIELD_11 = START_POS_FIELD_11 + 4 + START_POS_FIELD_12 = END_POS_FIELD_11 + END_POS_FIELD_12 = START_POS_FIELD_12 + 4 + START_POS_FIELD_13 = END_POS_FIELD_12 + END_POS_FIELD_13 = START_POS_FIELD_13 + 4 + START_POS_FIELD_14 = END_POS_FIELD_13 + END_POS_FIELD_14 = START_POS_FIELD_14 + 4 + START_POS_FIELD_15 = END_POS_FIELD_14 + END_POS_FIELD_15 = START_POS_FIELD_15 + 4 + START_POS_FIELD_16 = END_POS_FIELD_15 + END_POS_FIELD_16 = START_POS_FIELD_16 + 4 + START_POS_FIELD_17 = END_POS_FIELD_16 + END_POS_FIELD_17 = START_POS_FIELD_17 + 4 + START_POS_FIELD_18 = END_POS_FIELD_17 + END_POS_FIELD_18 = START_POS_FIELD_18 + 4 + START_POS_FIELD_19 = END_POS_FIELD_18 + END_POS_FIELD_19 = START_POS_FIELD_19 + 4 + START_POS_FIELD_20 = END_POS_FIELD_19 + END_POS_FIELD_20 = START_POS_FIELD_20 + 4 + START_POS_FIELD_21 = END_POS_FIELD_20 + END_POS_FIELD_21 = START_POS_FIELD_21 + 4 + START_POS_FIELD_22 = END_POS_FIELD_21 + END_POS_FIELD_22 = START_POS_FIELD_22 + 4 + START_POS_FIELD_23 = END_POS_FIELD_22 + END_POS_FIELD_23 = START_POS_FIELD_23 + 4 + START_POS_FIELD_24 = END_POS_FIELD_23 + END_POS_FIELD_24 = START_POS_FIELD_24 + 4 + START_POS_FIELD_25 = END_POS_FIELD_24 + END_POS_FIELD_25 = START_POS_FIELD_25 + 4 + START_POS_FIELD_26 = END_POS_FIELD_25 + END_POS_FIELD_26 = START_POS_FIELD_26 + 4 + START_POS_FIELD_27 = END_POS_FIELD_26 + END_POS_FIELD_27 = START_POS_FIELD_27 + 4 + START_POS_FIELD_28 = END_POS_FIELD_27 + END_POS_FIELD_28 = START_POS_FIELD_28 + 4 + START_POS_FIELD_29 = END_POS_FIELD_28 + END_POS_FIELD_29 = START_POS_FIELD_29 + 4 Index: dialin/common/leahi/leahi_msg_ids.py =================================================================== diff -u --- dialin/common/leahi/leahi_msg_ids.py (revision 0) +++ dialin/common/leahi/leahi_msg_ids.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,87 @@ +########################################################################### +# +# Copyright (c) 2021-2025 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 msg_ids.py +# +# @author (last) Dara Navaei +# @date (last) 24-Oct-2024 +# @author (original) Peter Lucia +# @date (original) 06-Apr-2021 +# +############################################################################ +from enum import unique +from dialin.utils.base import DialinEnum + + +# Branch: staging +@unique +class MsgIds(DialinEnum): + MSG_ID_UI_CHECK_IN = 0x77 + + MSG_ID_UNUSED = 0x0 + MSG_ID_ALARM_STATUS_DATA = 0x1 # DEC: 1 CAN_TD_ALARM : 0x001 SRC: td-firmware MODULE: Messaging + MSG_ID_ALARM_TRIGGERED = 0x2 # DEC: 2 CAN_TD_ALARM : 0x001 SRC: td-firmware MODULE: AlarmMgmtTD + MSG_ID_ALARM_CLEARED = 0x3 # DEC: 3 CAN_TD_ALARM : 0x001 SRC: td-firmware MODULE: AlarmMgmtTD + MSG_ID_ALARM_CONDITION_CLEARED = 0x4 # DEC: 4 CAN_RO_ALARM : 0x003 SRC: leahi-fwcommon MODULE: AlarmMgmt + MSG_ID_TD_ALARM_INFORMATION_DATA = 0x7 # DEC: 7 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: AlarmMgmtTD + MSG_ID_DD_ALARM_INFO_DATA = 0x8 # DEC: 8 CAN_DD_ALARM : 0x002 SRC: dd-firmware MODULE: AlarmMgmtDD + MSG_ID_TD_BLOOD_PUMP_DATA = 0x11 # DEC: 17 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: BloodFlow + MSG_ID_TD_OP_MODE_DATA = 0x12 # DEC: 18 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: OperationModes + MSG_ID_DD_OP_MODE_DATA = 0x13 # DEC: 19 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: OperationModes + MSG_ID_DD_VALVES_STATES_DATA = 0x1B # DEC: 27 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: Valves + MSG_ID_DD_PRESSURES_DATA = 0x1C # DEC: 28 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: Pressure + MSG_ID_TD_VOLTAGES_DATA = 0x1D # DEC: 29 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: Voltages + MSG_ID_TD_BUBBLES_DATA = 0x1E # DEC: 30 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: Bubbles + MSG_ID_DD_CONDUCTIVITY_DATA = 0x1F # DEC: 31 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: Conductivity + MSG_ID_TD_AIR_PUMP_DATA = 0x20 # DEC: 32 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: AirPump + MSG_ID_TD_SWITCHES_DATA = 0x21 # DEC: 33 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: Switches + MSG_ID_POWER_OFF_WARNING = 0x22 # DEC: 34 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: Buttons + MSG_ID_TD_PRESSURE_DATA = 0x24 # DEC: 36 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: Pressures + MSG_ID_DD_CONCENTRATE_PUMP_DATA = 0x25 # DEC: 37 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: ConcentratePumps + MSG_ID_DD_TEMPERATURE_DATA = 0x26 # DEC: 38 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: Temperature + MSG_ID_DIALYSATE_PUMPS_DATA = 0x27 # DEC: 39 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: DialysatePumps + MSG_ID_DD_HEATERS_DATA = 0x28 # DEC: 40 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: Heaters + MSG_ID_DD_LEVEL_DATA = 0x29 # DEC: 41 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: Level + MSG_ID_TD_AIR_TRAP_DATA = 0x2A # DEC: 42 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: AirTrap + MSG_ID_TD_VALVES_DATA = 0x2B # DEC: 43 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: Valves + MSG_ID_RO_ALARM_INFO_DATA = 0x2D # DEC: 45 CAN_RO_ALARM : 0x003 SRC: ro-firmware MODULE: AlarmMgmtRO + MSG_ID_DD_BAL_CHAMBER_DATA = 0x2E # DEC: 46 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: BalancingChamber + MSG_ID_DD_GEN_DIALYSATE_MODE_DATA = 0x2F # DEC: 47 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: ModeGenDialysate + MSG_ID_RO_VALVES_STATES_DATA = 0x31 # DEC: 49 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Valves + MSG_ID_RO_PUMP_DATA = 0x32 # DEC: 50 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: ROPump + MSG_ID_RO_OP_MODE_DATA = 0x33 # DEC: 51 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: OperationModes + MSG_ID_RO_PRESSURES_DATA = 0x34 # DEC: 52 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Pressure + MSG_ID_RO_LEVEL_DATA = 0x35 # DEC: 53 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Level + MSG_ID_RO_FLOW_DATA = 0x36 # DEC: 54 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Flow + MSG_ID_RO_CONDUCTIVITY_DATA = 0x37 # DEC: 55 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Conductivity + MSG_ID_RO_TEMPERATURE_DATA = 0x39 # DEC: 57 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Temperature + MSG_ID_RO_HEATER_DATA = 0x3A # DEC: 58 CAN_RO_BROADCAST: 0x102 SRC: ro-firmware MODULE: Heaters + MSG_ID_DD_PISTON_PUMP_CONTROL_DATA = 0x3B # DEC: 59 CAN_DD_BROADCAST: 0x101 SRC: dd-firmware MODULE: PistonPumpControl MSG_ID_TD_DEBUG_EVENT = 0xFFF1 + MSG_ID_DD_DEBUG_EVENT = 0xFFF2 + MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK = 0xFFFF + MSG_ID_TD_EVENT_DATA = 0XA4 + + # TODO: TO BE ANNOUNCED + MSG_ID_TD_BLOOD_FLOW_CMD_REQUEST = 0x8080 + MSG_ID_TD_BLOOD_FLOW_CMD_RESPONSE = 0x8081 + MSG_ID_TD_BLOOD_FLOW_SET_POINT_CMD_REQUEST = 0x8082 + MSG_ID_TD_BLOOD_FLOW_SET_POINT_CMD_RESPONSE = 0x8083 + MSG_ID_TD_BLOOD_FLOW_PRESSURE_CMD_REQUEST = 0x8084 + MSG_ID_TD_BLOOD_FLOW_PRESSURE_CMD_RESPONSE = 0x8085 + MSG_ID_TD_CURRENT_MOTOR_SPEED_CMD_REQUEST = 0x8086 + MSG_ID_TD_CURRENT_MOTOR_SPEED_CMD_RESPONSE = 0x8087 + MSG_ID_TD_MOTOR_SPEED_SET_POINT_CMD_REQUEST = 0x8088 + MSG_ID_TD_MOTOR_SPEED_SET_POINT_CMD_RESPONSE = 0x8089 + MSG_ID_TD_ROTOR_COUNT_CMD_REQUEST = 0x808A + MSG_ID_TD_ROTOR_COUNT_CMD_RESPONSE = 0x808B + MSG_ID_TD_ROTER_SPEED_CMD_REQUEST = 0x808C + MSG_ID_TD_ROTER_SPEED_CMD_RESPONSE = 0x808D + MSG_ID_TD_RPM_CMD_REQUEST = 0x808E + MSG_ID_TD_RPM_CMD_RESPONSE = 0x808F + MSG_ID_TD_ROTOR_HALL_CMD_REQUEST = 0x8090 + MSG_ID_TD_ROTOR_HALL_CMD_RESPONSE = 0x8091 + \ No newline at end of file Index: dialin/common/leahi/leahi_prs_defs.py =================================================================== diff -u --- dialin/common/leahi/leahi_prs_defs.py (revision 0) +++ dialin/common/leahi/leahi_prs_defs.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,70 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 prs_defs.py +# +# @author (last) Quang Nguyen +# @date (last) 06-Jul-2021 +# @author (original) Behrouz NematiPour +# @date (original) 05-Nov-2020 +# +############################################################################ +class Ranges: + PRESSURE_STEPS = 10 + + ARTERIAL_PRESSURE_MINIMUM = -400 + ARTERIAL_PRESSURE_MAXIMUM = +600 + ARTERIAL_PRESSURE_LOW_MIN = -300 + ARTERIAL_PRESSURE_LOW_DEF = -300 + ARTERIAL_PRESSURE_LOW_MAX = +200 + ARTERIAL_PRESSURE_HIGH_MIN = -300 + ARTERIAL_PRESSURE_HIGH_DEF = +100 + ARTERIAL_PRESSURE_HIGH_MAX = +200 + + VENOUS_PRESSURE_MINIMUM = -100 + VENOUS_PRESSURE_MAXIMUM = +700 + VENOUS_PRESSURE_LOW_MIN = -100 + VENOUS_PRESSURE_LOW_DEF = -100 + VENOUS_PRESSURE_LOW_MAX = +600 + VENOUS_PRESSURE_HIGH_MIN = +100 + VENOUS_PRESSURE_HIGH_DEF = +400 + VENOUS_PRESSURE_HIGH_MAX = +600 + + +class AlarmPriority: + ALARM_HIGH = 3 + ALARM_MED = 2 + ALARM_LOW = 1 + ALARM_NONE = 0 + + +class AlarmFlags: + ALARM_STATE_FLAG_BIT_POS_SYSTEM_FAULT = 0 + ALARM_STATE_FLAG_BIT_POS_STOP = 1 + ALARM_STATE_FLAG_BIT_POS_NO_CLEAR = 2 + ALARM_STATE_FLAG_BIT_POS_NO_RESUME = 3 + ALARM_STATE_FLAG_BIT_POS_NO_RINSEBACK = 4 + ALARM_STATE_FLAG_BIT_POS_NO_END_TREATMENT = 5 + ALARM_STATE_FLAG_BIT_POS_NO_NEW_TREATMENT = 6 + ALARM_STATE_FLAG_BIT_POS_USER_MUST_ACK = 7 + ALARM_STATE_FLAG_BIT_POS_ALARMS_TO_ESCALATE = 8 + ALARM_STATE_FLAG_BIT_POS_ALARMS_SILENCED = 9 + ALARM_STATE_FLAG_BIT_POS_LAMP_ON = 10 + ALARM_STATE_FLAG_BIT_POS_UNUSED_1 = 11 + ALARM_STATE_FLAG_BIT_POS_UNUSED_2 = 12 + ALARM_STATE_FLAG_BIT_POS_UNUSED_3 = 13 + ALARM_STATE_FLAG_BIT_POS_UNUSED_4 = 14 + ALARM_STATE_FLAG_BIT_POS_TOP_CONDITION = 15 + + +class AlarmDataTypes: + ALARM_DATA_TYPE_NONE = 0 # No data given. + ALARM_DATA_TYPE_U32 = 1 # Alarm data is unsigned 32-bit integer type. + ALARM_DATA_TYPE_S32 = 2 # Alarm data is signed 32-bit integer type. + ALARM_DATA_TYPE_F32 = 3 # Alarm data is 32-bit floating point type. + ALARM_DATA_TYPE_BOOL = 4 # Alarm data is 32-bit boolean type. + NUM_OF_ALARM_DATA_TYPES = 5 # Total number of alarm data types. Index: dialin/common/leahi/leahi_ui_defs.py =================================================================== diff -u --- dialin/common/leahi/leahi_ui_defs.py (revision 0) +++ dialin/common/leahi/leahi_ui_defs.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,172 @@ +########################################################################### +# +# Copyright (c) 2021-2025 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 leahi_ui_defs.py +# +# @author (last) Vy +# @date (last) 10-Oct-2023 +# @author (original) Peter Lucia +# @date (original) 06-Apr-2021 +# +############################################################################ + +from dialin.ui.leahi.td_simulator import RequestRejectReasons + + +class EResponse: + Rejected = 0 + Accepted = 1 + + +class GuiActionType: + Unknown = 0 + PowerOff = 1 + KeepAlive = 7 + BloodFlow = 5 + DialysateInletFlow = 8 + DialysateOutletFlow = 11 + TreatmentTime = 13 + PowerOffBroadcast = 14 + + AlarmStatus = 2 + AlarmTriggered = 3 + AlarmCleared = 4 + + PressureOcclusion = 9 + + DDDrainPumpData = 36 + DDHeatersData = 44 + LoadCellReadings = 12 + DDPressuresData = 32 + TemperatureSensors = 45 + + CanBUSFaultCount = 2457 + TDDebugText = 0xFFF1 + DDDebugText = 0xFFF2 + + AdjustBloodDialysateReq = 23 + AdjustBloodDialysateRsp = 24 + + AdjustDurationReq = 22 + AdjustDurationRsp = 27 + + AdjustUltrafiltrationStateReq = 16 + AdjustUltrafiltrationStateRsp = 65 + AdjustUltrafiltrationEditReq = 17 + AdjustUltrafiltrationEditRsp = 19 + AdjustUltrafiltrationConfirmReq = 21 + AdjustUltrafiltrationConfirmRsp = 46 + + AdjustPressuresLimitsReq = 70 + AdjustPressuresLimitsRsp = 71 + + TreatmentRanges = 26 + + String = 65279 + Acknow = 65535 + # Generic Acknowledgment is not a unique message ID and + # inherits its Id from the actual message. Zero is a placeholder + AcknowGeneric = 0 + + +class GuiActionTypeLength: + TREATMENT_LOG_LENGTH = 33 + + +class TXStates: + # Sub Mode + TREATMENT_START_STATE = 0 # Start treatment - initialize treatment and go to blood prime state + TREATMENT_BLOOD_PRIME_STATE = 1 # Prime blood-side of dialyzer with gradual ramp for 1 min. + TREATMENT_DIALYSIS_STATE = 2 # Perform dialysis. + TREATMENT_STOP_STATE = 3 # Treatment stopped. All pumps off. Dializer bypassed + TREATMENT_RINSEBACK_STATE = 4 # Perform rinseback with saline. Dialyzer bypassed. Dialysate recirculating + TREATMENT_RECIRC_STATE = 5 # Recirculate saline and dialysate while patient disconnected + TREATMENT_END_STATE = 6 # Dialysis has ended + + # Saline states + SALINE_BOLUS_STATE_IDLE = 0 # No saline bolus delivery is in progress + SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP = 1 # Wait for pumps to stop before starting bolus + SALINE_BOLUS_STATE_IN_PROGRESS = 2 # A saline bolus delivery is in progress + SALINE_BOLUS_STATE_MAX_DELIVERED = 3 # Maximum saline bolus volume reached + + # UF states + UF_PAUSED_STATE = 0 # Paused state of the ultrafiltration state machine + UF_RUNNING_STATE = 1 # Running state of the ultrafiltration state machine + UF_OFF_STATE = 3 # Completed/off state of the ultrafiltration state machine + UF_COMPLETED_STATE = 4 # Completed state of ultrafiltration state machine + + # Heparin states + HEPARIN_STATE_OFF = 0 # No heparin delivery is in progress + HEPARIN_STATE_PAUSED = 1 # Heparin delivery paused + HEPARIN_STATE_INITIAL_BOLUS = 2 # Initial heparin bolus delivery in progress + HEPARIN_STATE_DISPENSING = 3 # Gradual heparin dispensing in progress + HEPARIN_STATE_COMPLETED = 4 # Heparin delivery stopped due to the set stop time before treatment end + HEPARIN_STATE_EMPTY = 5 # Heparin Syringe empty + + # Rinseback states + RINSEBACK_STOP_INIT_STATE = 0 # Start state (stopped) of the rinseback sub-mode state machine + RINSEBACK_RUN_STATE = 1 # Rinseback running state of the rinseback sub-mode state machine + RINSEBACK_PAUSED_STATE = 2 # Rinseback paused state of the rinseback sub-mode state machine + RINSEBACK_STOP_STATE = 3 # Rinseback stopped (done) state of the rinseback sub-mode state machine + RINSEBACK_RUN_ADDITIONAL_STATE = 4 # Additional rinseback volume (10 mL) state + + # Recirculate + TREATMENT_RECIRC_RECIRC_STATE = 0 # Re-circulate state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_STOPPED_STATE = 1 # Stopped state of the treatment re-circulate sub-mode state machine + + # Blood Prime + BLOOD_PRIME_RAMP_STATE = 0 # Ramp state of the blood prime sub-mode state machine + + # Treatment End + TREATMENT_END_WAIT_FOR_RINSEBACK_STATE = 0 # Wait for rinseback state of the treatment end sub-mode state machine + TREATMENT_END_PAUSED_STATE = 1 # Paused state of the treatment end sub-mode state machine + + # Treatment Stop + TREATMENT_STOP_RECIRC_STATE = 0 # Dialysate re-circulation state + TREATMENT_STOP_NO_RECIRC_STATE = 1 # No dialysate re-circulation state + + +class TreatmentParameterRejections: + def __init__(self): + self.param_request_valid = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_blood_flow_rate = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_dialysate_flow_rate = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_duration = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_heparin_stop_time = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_saline_bolus = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_acid_concentrate = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_bicarbonate_concentrate = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_dialyzer_type = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_blood_pressure_measure_interval = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_rinseback_flow_rate = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_arterial_pressure_limit_low = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_arterial_pressure_limit_high = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_venous_pressure_limit_low = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_venous_pressure_limit_high = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_heparin_dispensing_rate = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_heparin_bolus_volume = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + self.param_dialysate_temp = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + + def set_all_valid(self): + """ + Sets all parameters as valid + + @return: None + """ + for attr in dir(self): + if not callable(getattr(self, attr)) and attr.startswith("param_"): + self.__dict__[attr] = RequestRejectReasons.REQUEST_REJECT_REASON_NONE + + def set_all_invalid(self): + """ + Sets all treatment parameters to be invalid + + @return: None + """ + for attr in dir(self): + if not callable(getattr(self, attr)) and attr.startswith("param_"): + self.__dict__[attr] = RequestRejectReasons.REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE Index: dialin/common/leahi/td_defs.py =================================================================== diff -u --- dialin/common/leahi/td_defs.py (revision 0) +++ dialin/common/leahi/td_defs.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,446 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 td_defs.py +# +# @author (last) Dara Navaei +# @date (last) 01-Nov-2024 +# @author (original) Peter Lucia +# @date (original) 04-Dec-2020 +# +############################################################################ +from enum import unique +from dialin.utils.base import DialinEnum + + +@unique +class TDOpModes(DialinEnum): + MODE_FAUL = 0 # Fault mode + MODE_SERV = 1 # Service mode + MODE_INIT = 2 # Initialization & POST mode + MODE_STAN = 3 # Standby mode + MODE_TPAR = 4 # Treatment Parameters mode + MODE_PRET = 5 # Pre-Treatment mode + MODE_TREA = 6 # Treatment mode + MODE_POST = 7 # Post-Treatment mode + MODE_NLEG = 8 # Not legal - an illegal mode transition occurred + NUM_OF_MODES = 9 # Number of TD operation modes + + +@unique +class TDStandbyStates(DialinEnum): + STANDBY_START_STATE = 0 # Start standby (home actuators). + STANDBY_WAIT_FOR_TREATMENT_STATE = 1 # Wait for treatment. + STANDBY_WAIT_FOR_DISINFECT_STATE = 2 # Wait for UI to send disinfect option. + STANDBY_WAIT_FOR_DD_CLEANING_MODE_CMD_RESPONSE_STATE = 3 # Wait for DD cleaning mode command response state. + STANDBY_WAIT_FOR_DD_CLEANING_MODE_TO_START_STATE = 4 # Wait for DD cleaning mode to start state. + STANDBY_CLEANING_MODE_IN_PROGRESS_STATE = 5 # Cleaning mode in progress state. + NUM_OF_STANDBY_STATES = 6 # Number of standby states (sub-modes). + +@unique +class TDInitStates(DialinEnum): + POST_STATE_START = 0 # Start initialize & POST mode state + POST_STATE_FW_INTEGRITY = 1 # Run firmware integrity test state + POST_STATE_RTC = 2 # Run RTC test state + POST_STATE_NVDATAMGMT = 3 # Run NV Data Mgmt. test state + POST_STATE_WATCTDOG = 4 # Run watchdog test state + POST_STATE_SAFETY_SHUTDOWN = 5 # Run safety shutdown test state + POST_STATE_BLOOD_FLOW = 6 # Run blood flow test state + POST_STATE_DIALYSATE_INLET_FLOW = 7 # Run dialysate inlet flow test state + POST_STATE_DIALYSATE_OUTLET_FLOW = 8 # Run dialysate outlet flow test state + POST_STATE_BLOOD_LEAK = 9 # Run blood leak sensor test state + POST_STATE_VALVES = 10 # Run valves test state + POST_STATE_SYRINGE_PUMP = 11 # Run syringe pump test state + POST_STATE_PRES_OCCL = 12 # Run pressure occlusion state + POST_STATE_ALARM_AUDIO = 13 # Run alarm audio test state + POST_STATE_ALARM_LAMP = 14 # Run alarm lamp test state + POST_STATE_ACCELEROMETER = 15 # Run Accelerometer test state + POST_STATE_TEMPERATURES = 16 # Run temperatures POST state + POST_STATE_FANS = 17 # Run fans POST state + POST_STATE_STUCK_BUTTON = 18 # Run stuck button test state + POST_STATE_UI_POST = 19 # Check whether UI passed its POST tests + POST_STATE_FW_COMPATIBILITY = 20 # Run firmware compatibility test state + POST_STATE_FPGA = 21 # Run FPGA test state + POST_STATE_COMPLETED = 22 # POST self-tests completed state + POST_STATE_FAILED = 23 # POST self-tests failed state + NUM_OF_POST_STATES = 24 # Number of initialize & POST mode states + +@unique +class PreTreatmentSubModes(DialinEnum): + TD_PRE_TREATMENT_WATER_SAMPLE_STATE = 0 # Water sample state + TD_PRE_TREATMENT_SELF_TEST_CONSUMABLE_STATE = 1 # Consumable self-tests state + TD_PRE_TREATMENT_SELF_TEST_NO_CART_STATE = 2 # No cartridge self-tests state + TD_PRE_TREATMENT_CART_INSTALL_STATE = 3 # Consumable and cartridge installation state + TD_PRE_TREATMENT_SELF_TEST_DRY_STATE = 4 # Self-tests when the cartridge is dry state + TD_PRE_TREATMENT_PRIME_STATE = 5 # Prime blood and dialysate circuits and run wet self-tests state + TD_PRE_TREATMENT_RECIRCULATE_STATE = 6 # Re-circulate blood and dialysate circuits state + TD_PRE_TREATMENT_PATIENT_CONNECTION_STATE = 7 # Patient connection state + NUM_OF_TD_PRE_TREATMENT_STATES = 8 # Number of pre-treatment mode states + + +@unique +class PreTreatmentSampleWaterStates(DialinEnum): + SAMPLE_WATER_SETUP_STATE = 0 # Sample water setup (flush filter) state + SAMPLE_WATER_STATE = 1 # Sample water state, receiving sample water commands from the user + SAMPLE_WATER_COMPLETE_STATE = 2 # Sample water complete state + NUM_OF_SAMPLE_WATER_STATES = 3 # Number of sample water sub-mode states + + +@unique +class PreTreatmentConsumableSelfTestStates(DialinEnum): + CONSUMABLE_SELF_TESTS_INSTALL_STATE = 0 # Consumable self-tests install state + CONSUMABLE_SELF_TESTS_PRIME_STATE = 1 # Consumable self-tests prime concentrate lines state + CONSUMABLE_SELF_TESTS_BICARB_PUMP_CHECK_STATE = 2 # Consumable self-tests bicarbonate concentrate pump check state + CONSUMABLE_SELF_TESTS_ACID_PUMP_CHECK_STATE = 3 # Consumable self-tests acid concentrate pump check state + CONSUMABLE_SELF_TESTS_COMPLETE_STATE = 4 # Consumable self-tests complete state + NUM_OF_CONSUMABLE_SELF_TESTS_STATES = 5 # Number of consumable install sub-mode states + + +@unique +class PreTreatmentNoCartSelfTestStates(DialinEnum): + NO_CART_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE = 0 # Wait for door to be closed before running self-tests + NO_CART_SELF_TESTS_PRESSURE_CHECKS_STATE = 1 # No cartridge pressure sensors self-test state + NO_CART_SELF_TESTS_HOME_VALVES_STATE = 2 # No cartridge home valves state + NO_CART_SELF_TESTS_HOME_SYRINGE_PUMP_STATE = 3 # No cartridge home syringe pump state + NO_CART_SELF_TESTS_PUMPS_STATE = 4 # No cartridge self-test for blood pump, dialysate in pump, dialysate out pump state + NO_CART_SELF_TESTS_HOME_IDLE_STATE = 5 # Wait for valves and pumps finish homing state + NO_CART_SELF_TESTS_STOPPED_STATE = 6 # No cart self-test stopped state + NO_CART_SELF_TESTS_COMPLETE_STATE = 7 # No cartridge self-test complete state + NUM_OF_NO_CART_SELF_TESTS_STATES = 8 # Number of no cartridge self-tests states + + +@unique +class PreTreatmentWetSelfTestStates(DialinEnum): + WET_SELF_TESTS_START_STATE = 0 + WET_SELF_TESTS_BUBBLE_CHECK_SETUP_STATE = 1 + WET_SELF_TESTS_BUBBLE_CHECK_STATE = 2 + WET_SELF_TESTS_PRIME_CHECK_STATE = 3 + WET_SELF_TESTS_BLOOD_LEAK_DETECTOR_DEBUBBLE_STATE = 4 + WET_SELF_TESTS_BLOOD_LEAK_DETECTOR_STATE = 5 + WET_SELF_TESTS_FIRST_DISPLACEMENT_SETUP_STATE = 6 + WET_SELF_TESTS_FIRST_DISPLACEMENT_STATE = 7 + WET_SELF_TESTS_FIRST_DISPLACEMENT_VERIFY_STATE = 8 + WET_SELF_TESTS_SECOND_DISPLACEMENT_SETUP_STATE = 9 + WET_SELF_TESTS_SECOND_DISPLACEMENT_STATE = 10 + WET_SELF_TESTS_SECOND_DISPLACEMENT_VERIFY_STATE = 11 + WET_SELF_TESTS_STOPPED_STATE = 12 + WET_SELF_TESTS_COMPLETE_STATE = 13 + +@unique +class TDPreTreatmentReservoirMgmtStates(DialinEnum): + PRE_TREATMENT_RESERVOIR_MGMT_START_STATE = 0 # Wait for signal to start drain and fill reservoirs + PRE_TREATMENT_RESERVOIR_MGMT_DRAIN_CMD_STATE = 1 # Command DD to start draining reservoir + PRE_TREATMENT_RESERVOIR_MGMT_DRAIN_CMD_RESP_STATE = 2 # After sending drain command, process DD drain command response + PRE_TREATMENT_RESERVOIR_MGMT_START_FILL_STATE = 3 # Command DD to start filling reservoir + PRE_TREATMENT_RESERVOIR_MGMT_FILL_CMD_RESP_STATE = 4 # After sending fill command, process DD fill command response + PRE_TREATMENT_RESERVOIR_MGMT_FILL_COMPLETE_STATE = 5 # Reservoir fill has completed + PRE_TREATMENT_RESERVOIR_MGMT_REQUEST_RESERVOIR_SWITCH_STATE = 6 # Command DD to switch (toggle) reservoirs + PRE_TREATMENT_RESERVOIR_MGMT_WAIT_FOR_RESERVOIR_SWITCH_STATE = 7 # After sending switch command, process DD fill command response + PRE_TREATMENT_RESERVOIR_MGMT_COMPLETE_STATE = 8 # Pre-treatment reservoir management complete state + NUM_OF_PRE_TREATMENT_RESERVOIR_MGMT_STATES = 9 # Number of pre-treatments reservoir mgmt. states + +# Heparin states +@unique +class HeparinStates(DialinEnum): + HEPARIN_STATE_OFF = 0 # No heparin delivery is in progress + HEPARIN_STATE_STOPPED = 1 # Heparin delivery stopped by alarm or not yet started + HEPARIN_STATE_PAUSED = 2 # Heparin delivery paused + HEPARIN_STATE_INITIAL_BOLUS = 3 # Initial heparin bolus delivery in progress + HEPARIN_STATE_DISPENSING = 4 # Gradual heparin dispensing in progress + HEPARIN_STATE_COMPLETED = 5 # Heparin delivery stopped due to the set stop time before treatment end + HEPARIN_STATE_EMPTY = 6 # Heparin Syringe empty + NUM_OF_HEPARIN_STATES = 7 # Number of saline bolus states + + +# Syringe pump states +@unique +class SyringePumpStates(DialinEnum): + SYRINGE_PUMP_INIT_STATE = 0 # Syringe pump initialize state + SYRINGE_PUMP_OFF_STATE = 1 # Syringe pump off state + SYRINGE_PUMP_RETRACT_STATE = 2 # Syringe pump retract state + SYRINGE_PUMP_PRELOAD_STATE = 3 # Syringe pump preload state + SYRINGE_PUMP_SEEK_STATE = 4 # Syringe pump seek state + SYRINGE_PUMP_PRIME_STATE = 5 # Syringe pump prime state + SYRINGE_PUMP_HEP_BOLUS_STATE = 6 # Syringe pump bolus state + SYRINGE_PUMP_HEP_CONTINUOUS_STATE = 7 # Syringe pump continuous state + SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE = 8 # Syringe pump configure force sensor state + + +# Syringe pump operations +@unique +class SyringePumpOperations(DialinEnum): + SYRINGE_PUMP_OP_STOP = 0 # Stop syringe pump + SYRINGE_PUMP_OP_RETRACT = 1 # Retract syringe pump + SYRINGE_PUMP_OP_SEEK = 2 # Seek plunger + SYRINGE_PUMP_OP_PRIME = 3 # Prime Heparin line + SYRINGE_PUMP_OP_BOLUS = 4 # Deliver Heparin bolus of set volume over 5 minutes + SYRINGE_PUMP_OP_CONTINUOUS = 5 # Continuous dispense of Heparin at set rate + + +@unique +class PostTreatmentStates(DialinEnum): + TD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE = 0 # Drain reservoirs state + TD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE = 1 # Patient disconnection state + TD_POST_TREATMENT_DISPOSABLE_REMOVAL_STATE = 2 # Disposable removal state + TD_POST_TREATMENT_VERIFY_STATE = 3 # Verify cartridge removed, syringe removed, and reservoirs drained state + NUM_OF_TD_POST_TREATMENT_STATES = 4 # Number of post-treatment mode states + + +@unique +class PreTreatmentCartridgeInstallStates(DialinEnum): + CARTRIDDE_INSTALL_STATE = 0 # Pre-treatment Cartridge Install state. + NUM_OF_CARTRIDDE_INSTALL_STATES = 1 # Number of pre-treatment Cartridge Install states. + + +@unique +class PreTreatmentDrySelfTestsStates(DialinEnum): + """ + The TD Pre-Treatment dry self test states + """ + 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_CARTRIDDE_CHECK_STATE = 2 # Used cartridge check dry self-test state + DRY_SELF_TESTS_CARTRIDDE_LOADED_CHECK_STATE = 3 # Cartridge loaded check dry self-test state + DRY_SELF_TESTS_SYRINGE_PUMP_SEEK_STATE = 4 # Seek syringe pumps state + DRY_SELF_TESTS_PRESSURE_SENSORS_NORMAL_SETUP_STATE = 5 # Pressure sensor setup state. + DRY_SELF_TESTS_PRESSURE_SENSORS_VENOUS_SETUP_STATE = 6 # Venous pressure sensor dry self-test setup valves and pump state + DRY_SELF_TESTS_PRESSURE_SENSORS_VENOUS = 7 # Venous pressure sensor dry self-test + DRY_SELF_TESTS_PRESSURE_SENSORS_ARTERIAL_SETUP_STATE = 8 # Arterial pressure sensor dry self-test setup valves and pump state + DRY_SELF_TESTS_PRESSURE_SENSORS_ARTERIAL = 9 # Arterial pressure sensor dry self-test + DRY_SELF_TESTS_PRESSURE_SENSORS_DECAY_STATE = 10 # Pressure sensors verify pressure loss state + DRY_SELF_TESTS_PRESSURE_SENSORS_STABILITY_STATE = 11 # Pressure sensors verify pressure stability state + DRY_SELF_TESTS_PRESSURE_SENSORS_NORMAL_STATE = 12 # Pressure sensors verify normal pressure readings state + DRY_SELF_TESTS_SYRINGE_PUMP_PRIME_STATE = 13 # Prime syringe pump state + DRY_SELF_TESTS_SYRINGE_PUMP_OCCLUSION_DETECTION_STATE = 14 # Occlusion detection state + DRY_SELF_TESTS_STOPPED_STATE = 15 # Dry self-test stopped state + DRY_SELF_TESTS_COMPLETE_STATE = 16 # Dry self-test complete state + NUM_OF_DRY_SELF_TESTS_STATES = 17 # Number of dry self-tests states + + +@unique +class PreTreatmentPrimeStates(DialinEnum): + TD_PRIME_WAIT_FOR_USER_START_STATE = 0 # Wait for user to start prime state + TD_PRIME_SALINE_SETUP_STATE = 1 # Saline setup state + TD_PRIME_SALINE_PURGE_AIR_STATE = 2 # Saline purge air state + TD_PRIME_SALINE_CIRC_BLOOD_CIRCUIT_STATE = 3 # Circulate blood circuit state + TD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE = 4 # Wait for reservoir 1 fill complete + TD_PRIME_DIALYSATE_DIALYZER_STATE = 5 # Dialysate dialyzer fluid path state + TD_PRIME_SALINE_DIALYZER_SETUP_STATE = 6 # Saline dialyzer setup state + TD_PRIME_SALINE_DIALYZER_STATE = 7 # Saline dialyzer fluid path state + TD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE = 8 # Wait for reservoir 2 fill complete + TD_PRIME_DIALYSATE_BYPASS_STATE = 9 # Dialysate bypass fluid path state + TD_PRIME_WET_SELF_TESTS_STATE = 10 # Perform wet self-tests after priming complete + TD_PRIME_PAUSE = 11 # Prime pause state, waits to be resumed + TD_PRIME_COMPLETE = 12 # Prime complete state + NUM_OF_TD_PRIME_STATES = 13 # Number of prime sub-mode states + + +@unique +class PreTreatmentRecircStates(DialinEnum): + PRE_TREATMENT_RECIRC_STATE = 0 # Pre-treatment recirculate state + PRE_TREATMENT_RECIRC_STOPPED_STATE = 1 # Pre-treatment recirculate stopped state + NUM_OF_PRE_TREATMENT_RECIRC_STATES = 2 # Number of pre-treatment recirculate states + + +@unique +class TreatmentRecircStates(DialinEnum): + TREATMENT_RECIRC_DISCONNECT_PATIENT_STATE = 0 # Disconnect patient state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_RECIRC_STATE = 1 # Re-circulate Dialysate state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_STOPPED_STATE = 2 # Stopped state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_RECONNECT_PATIENT_STATE = 3 # Reconnect patient state of the treatment re-circulate sub-mode state machine + NUM_OF_TREATMENT_RECIRC_STATES = 4 # Number of treatment re-circulate sub-mode states + + +@unique +class PreTreatmentPatientConnectionStates(DialinEnum): + PRE_TREATMENT_PAT_CONN_WAIT_FOR_UF_VOL_STATE = 0 # Pre-treatment patient connect wait for UF volume setting state + PRE_TREATMENT_PAT_CONN_WAIT_FOR_DLZR_INVERT_STATE = 1 # Pre-treatment patient connect wait for dialyzer inverted state + PRE_TREATMENT_PAT_CONN_WAIT_FOR_USER_CONFIRM_STATE = 2 # Pre-treatment patient connect wait for user confirm state + PRE_TREATMENT_PAT_CONN_WAIT_FOR_TREATMENT_START_STATE = 3 # Pre-treatment patient connect wait for treatment start state + NUM_OF_PRE_TREATMENT_PAT_CONN_STATES = 4 # Number of pre-treatment patient connect states + + +@unique +class TreatmentParametersStates(DialinEnum): + TD_TREATMENT_PARAMS_MODE_STATE_WAIT_4_UI_2_SEND = 0 # Wait for UI to send treatment params mode state + TD_TREATMENT_PARAMS_MODE_STATE_WAIT_4_UI_2_CONFIRM = 1 # Wait for UI to confirm treatment params mode state + NUM_OF_TD_TREATMENT_PARAMS_MODE_STATES = 2 # Number of treatment params mode states + + +@unique +class TreatmentStates(DialinEnum): + TREATMENT_START_STATE = 0 # Start treatment - initialize treatment and go to blood prime state + TREATMENT_BLOOD_PRIME_STATE = 1 # Prime blood-side of dialyzer with gradual ramp for 1 min. while dialyzer is bypassed. No dialysis or UF taking place. No treatment time. + TREATMENT_DIALYSIS_STATE = 2 # Perform dialysis. Deliver Heparin as prescribed. Deliver UF as prescribed. Handle saline boluses as requested + TREATMENT_STOP_STATE = 3 # Treatment stopped. All pumps off. Dializer bypassed + TREATMENT_RINSEBACK_STATE = 4 # Perform rinseback with saline. Dialyzer bypassed. Dialysate recirculating + TREATMENT_RECIRC_STATE = 5 # Recirculate saline and dialysate while patient disconnected. Blood lines open and shunted. Dialyzer is bypassed + TREATMENT_END_STATE = 6 # Dialysis has ended. Blood pump slowed. Dialyzer is bypassed. Dialysate is recirculated. User can rinseback + NUM_OF_TREATMENT_STATES = 7 # Number of treatment states (sub-modes) + +@unique +class TreatmentBloodPrimeStates(DialinEnum): + BLOOD_PRIME_RAMP_STATE = 0 # Ramp state of the blood prime sub-mode state machine + NUM_OF_BLOOD_PRIME_STATES = 1 # Number of blood prime sub-mode states + +@unique +class TreatmentDialysisStates(DialinEnum): + DIALYSIS_START_STATE = 0 # Start state of dialysis sub-mode state machine + DIALYSIS_UF_STATE = 1 # Ultrafiltration state of the dialysis sub-mode state machine + DIALYSIS_SALINE_BOLUS_STATE = 2 # Saline bolus state of the dialysis sub-mode state machine + DIALYSIS_BLOOD_LEAK_ZEROING_STATE = 3 # Blood leak zeroing state + NUM_OF_DIALYSIS_STATES = 4 # Number of dialysis sub-mode states + +@unique +class TreatmentStopStates(DialinEnum): + TREATMENT_STOP_RECIRC_STATE = 0 # Dialysate and Blood re-circulation state of the treatment stop sub-mode state machine + TREATMENT_STOP_RECIRC_DIALYSATE_ONLY_STATE = 1 # Re-circulate Dialysate only state of the treatment re-circulate sub-mode state machine + TREATMENT_STOP_RECIRC_BLOOD_ONLY_STATE = 2 # Re-circulate Blood only state of the treatment re-circulate sub-mode state machine + TREATMENT_STOP_NO_RECIRC_STATE = 3 # No re-circulation state of the treatment stop sub-mode state machine + NUM_OF_TREATMENT_STOP_STATES = 4 # Number of treatment stop sub-mode states + +@unique +class TreatmentRinsebackStates(DialinEnum): + RINSEBACK_STOP_INIT_STATE = 0 # Start state (stopped) of the rinseback sub-mode state machine + RINSEBACK_RUN_STATE = 1 # Rinseback running state of the rinseback sub-mode state machine + RINSEBACK_PAUSED_STATE = 2 # Rinseback paused state of the rinseback sub-mode state machine + RINSEBACK_STOP_STATE = 3 # Rinseback stopped (done) state of the rinseback sub-mode state machine + RINSEBACK_RUN_ADDITIONAL_STATE = 4 # Additional rinseback volume (10 mL) state of the rinseback sub-mode state machine + RINSEBACK_RECONNECT_PATIENT_STATE = 5 # Rinseback reconnect patient state of the rinseback sub-mode state machine + NUM_OF_RINSEBACK_STATES = 6 # Number of rinseback sub-mode states + +@unique +class TreatmentRecircStates(DialinEnum): + TREATMENT_RECIRC_DISCONNECT_PATIENT_STATE = 0 # Disconnect patient state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_RECIRC_STATE = 1 # Re-circulate Dialysate state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_STOPPED_STATE = 2 # Stopped state of the treatment re-circulate sub-mode state machine + TREATMENT_RECIRC_RECONNECT_PATIENT_STATE = 3 # Reconnect patient state of the treatment re-circulate sub-mode state machine + NUM_OF_TREATMENT_RECIRC_STATES = 4 # Number of treatment re-circulate sub-mode states + +@unique +class TreatmentEndStates(DialinEnum): + TREATMENT_END_WAIT_FOR_RINSEBACK_STATE = 0 # Wait for rinseback state of the treatment end sub-mode state machine + TREATMENT_END_PAUSED_STATE = 1 # Paused state of the treatment end sub-mode state machine + NUM_OF_TREATMENT_END_STATES = 2 # Number of treatment end sub-mode states + +@unique +class TDFaultStates(DialinEnum): + TD_FAULT_STATE_START = 0 # Start fault state + TD_FAULT_STATE_RUN_NV_POSTS = 1 # TD fault run NV posts state + TD_FAULT_STATE_COMPLETE = 2 # TD fault run complete state + NUM_OF_TD_FAULT_STATES = 3 # Number of fault mode states + +@unique +class TDEventList(DialinEnum): + TD_EVENT_STARTUP = 0 # TD startup event + TD_EVENT_OP_MODE_CHANGE = 1 # TD Op mode change event + TD_EVENT_SUB_MODE_CHANGE = 2 # TD Op sub-mode change event + TD_EVENT_DRY_SELF_TEST_CARTRIDDE_RESULT = 3 # TD dry self test cartridge result + TD_EVENT_DRY_SELF_TEST_PRESSURE_RESULT = 4 # TD dry self test pressure result + TD_EVENT_WET_SELF_TEST_DISPLACEMENT_RESULT = 5 # TD wet self test displacement result + TD_EVENT_CPU_RAM_ERROR_STATUS = 6 # TD CPU RAM error status + TD_EVENT_CAL_RECORD_UPDATE = 7 # TD new calibration record updated + TD_EVENT_SYSTEM_RECORD_UPDATE = 8 # TD new system record has been updated + TD_EVENT_SERVICE_UPDATE = 9 # TD new service record has been updated + TD_EVENT_USAGE_INFO_UPDATE = 10 # TD new usage information has been updated + TD_EVENT_SW_CONFIG_UPDATE = 11 # TD new software configuration has been updated + TD_EVENT_BUTTON = 12 # TD button pressed/released + TD_EVENT_SAFETY_LINE = 13 # TD safety line pulled/released + TD_EVENT_RSRVR_1_LOAD_CELL_START_VALUES = 14 # TD reservoir 1 load cells start values + TD_EVENT_RSRVR_1_LOAD_CELL_END_VALUES = 15 # TD reservoir 2 load cells end values + TD_EVENT_RSRVR_2_LOAD_CELL_START_VALUES = 16 # TD reservoir 2 load cells start values + TD_EVENT_RSRVR_2_LOAD_CELL_END_VALUES = 17 # TD reservoir 2 load cells end values + TD_EVENT_SUB_STATE_CHANGE = 18 # TD Op sub-state change event + TD_EVENT_SYRINGE_PUMP_STATE = 19 # TD syringe pump state change event + TD_EVENT_OCCLUSION_BASELINE = 20 # TD event occlusion baseline event + TD_EVENT_RSRVR_UF_VOLUME_AND_TIME = 21 # TD ultrafiltration volume and time for a reservoir use + TD_EVENT_RSRVR_UF_RATE = 22 # TD ultrafiltration measured and expected rates + TD_EVENT_OPERATION_STATUS = 23 # TD operation status event. + TD_EVENT_AIR_TRAP_FILL = 24 # TD initiated an air trap fill (opened VBT briefly). + TD_EVENT_AIR_PUMP_ON_OFF = 25 # TD turned air pump on or off. + TD_EVENT_BLOOD_LEAK_SELF_TEST_RESULT = 26 # TD Blood leak self test result. + TD_EVENT_BLOOD_LEAK_NUM_OF_SET_POINT_CHECK_FAILURES = 27 # TD blood leak number of setpoint check failures + TD_EVENT_DRY_SELF_TEST_PRESSURE_DECAY_WAIT_PERIOD = 28 # TD dry self test pressure decay wait period + TD_EVENT_INSTIT_RECORD_UPDATE = 29 # TD new institutional record has been updated. + TD_EVENT_PARTIAL_OCCLUSION_BASELINE = 30 # TD event partial occlusion baseline event + TD_EVENT_BLOOD_LEAK_ZEROING_REQUEST = 31 # TD event blood leak zeroing request + TD_EVENT_BLOOD_LEAK_ZEROING_REQUIRED = 32 # TD event blood leak zeroing required + NUM_OF_EVENT_IDS = 33 # Total number of TD events + +@unique +class TDEventDataType(DialinEnum): + EVENT_DATA_TYPE_NONE = 0 + EVENT_DATA_TYPE_U32 = 1 + EVENT_DATA_TYPE_S32 = 2 + EVENT_DATA_TYPE_F32 = 3 + EVENT_DATA_TYPE_BOOL = 4 + NUM_OF_EVENT_DATA_TYPES = 5 + +@unique +class UFStates(DialinEnum): + UF_PAUSED_STATE = 0 # Paused state of the ultrafiltration state machine + UF_RUNNING_STATE = 1 # Running state of the ultrafiltration state machine + NUM_OF_UF_STATES = 2 # Number of ultrafiltration states + +@unique +class SalineBolusStates(DialinEnum): + SALINE_BOLUS_STATE_IDLE = 0 # No saline bolus delivery is in progress + SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP = 1 # Wait for pumps to stop before starting bolus + SALINE_BOLUS_STATE_IN_PROGRESS = 2 # A saline bolus delivery is in progress + SALINE_BOLUS_STATE_MAX_DELIVERED = 3 # Maximum saline bolus volume reached - no more saline bolus deliveries allowed + NUM_OF_SALINE_BOLUS_STATES = 4 # Number of saline bolus states + +@unique +class TreatmentParameters(DialinEnum): + TREATMENT_PARAM_BLOOD_FLOW_RATE_ML_MIN = 0 + TREATMENT_PARAM_DIALYSATE_FLOW_RATE_ML_MIN = 1 + TREATMENT_PARAM_TREATMENT_DURATION_MIN = 2 + TREATMENT_PARAM_HEPARIN_PRESTOP_MIN = 3 + TREATMENT_PARAM_SALINE_BOLUS_VOLUME_ML = 4 + TREATMENT_PARAM_ACID_CONCENTRATE = 5 + TREATMENT_PARAM_BICARB_CONCENTRATE = 6 + TREATMENT_PARAM_DIALYZER_TYPE = 7 + TREATMENT_PARAM_HEPARIN_TYPE = 8 + TREATMENT_PARAM_BLOOD_PRESSURE_MEAS_INTERVAL_MIN = 9 + TREATMENT_PARAM_RINSEBACK_FLOW_RATE_ML_MIN = 10 + TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW = 11 + TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW = 12 + TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC = 13 + TREATMENT_PARAM_HEPARIN_DISPENSE_RATE_ML_HR = 14 + TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME_ML = 15 + TREATMENT_PARAM_DIALYSATE_TEMPERATURE_C = 16 + TREATMENT_PARAM_UF_VOLUME_L = 17 + NUM_OF_TREATMENT_PARAMS = 18 + +class Acid_Concentrates(DialinEnum): + ACID_CONC_TYPE_FRESENIUS_08_1251_1 = 0 + ACID_CONC_TYPE_FRESENIUS_08_2251_0 = 1 + ACID_CONC_TYPE_FRESENIUS_08_3251_9 = 2 + NUM_OF_ACID_CONC_TYPES = 3 + +class Bicarb_Concentrates(DialinEnum): + BICARB_CONC_TYPE_FRESENIUS_CENTRISOL = 0 + NUM_OF_BICARB_CONC_TYPES = 1 + +@unique +class PowerOffCommands(DialinEnum): + """ + power off commands enum + """ + PW_COMMAND_OPEN = 0 + PW_TIMEOUT_CLOSE = 1 + PW_REJECT_SHOW = 2 + +@unique +class BloodLeakZeroingStates(DialinEnum): + BLD_ZEROING_IDLE_STATE = 0 # Blood leak zeroing idle state. + BLD_ZEROING_FLUSH_RSRVR_2_DPI_STATE = 1 # Blood leak zeroing flush reservoir to DPi state. + BLD_ZEROING_FLUSH_DPI_2_BLD_STATE = 2 # Blood leak zeroing flush DPi to BLD state. + BLD_ZEROING_ZERO_STATE = 3 # Blood leak zeroing zero state. + BLD_ZEROING_VERIFY_ZEROING_STATE = 4 # Blood leak zeroing verify zeroing state. + BLD_ZEROING_COMPLETE_STATE = 5 # Blood leak zeroing complete state. + NUM_OF_BLD_ZEROING_STATES = 6 # Number of blood leak zeroing states. Index: dialin/common/msg_defs.py =================================================================== diff -u -ra7253ef8e885d542d22539cd1e4c608f7ce63eb5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/msg_defs.py (.../msg_defs.py) (revision a7253ef8e885d542d22539cd1e4c608f7ce63eb5) +++ dialin/common/msg_defs.py (.../msg_defs.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from dialin.utils.base import DialinEnum from .msg_ids import MsgIds Index: dialin/common/msg_ids.py =================================================================== diff -u -ra7253ef8e885d542d22539cd1e4c608f7ce63eb5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/msg_ids.py (.../msg_ids.py) (revision a7253ef8e885d542d22539cd1e4c608f7ce63eb5) +++ dialin/common/msg_ids.py (.../msg_ids.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -14,7 +14,7 @@ # ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from dialin.utils.base import DialinEnum # Branch: staging Index: dialin/common/test_config_defs.py =================================================================== diff -u -r059b92a6fe57357f517c4299e5c1200d4d2fb7d5 -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/common/test_config_defs.py (.../test_config_defs.py) (revision 059b92a6fe57357f517c4299e5c1200d4d2fb7d5) +++ dialin/common/test_config_defs.py (.../test_config_defs.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -15,7 +15,7 @@ ############################################################################ from enum import unique -from ..utils.base import DialinEnum +from dialin.utils.base import DialinEnum @unique Index: dialin/protocols/__init__.py =================================================================== diff -u -ra505f80795643e531cb0b0120a38a7765cbf7b9c -r5ca05704da5cc2dfea17182bea02b048c37620f8 --- dialin/protocols/__init__.py (.../__init__.py) (revision a505f80795643e531cb0b0120a38a7765cbf7b9c) +++ dialin/protocols/__init__.py (.../__init__.py) (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -2,3 +2,7 @@ from .CAN import DenaliChannels from .CAN import LongDenaliMessageBuilder from .CAN import DenaliMessage +from .leahi.leahi_can import LeahiCanMessenger +from .leahi.leahi_can import LeahiChannels +from .leahi.leahi_can import LongLeahiMessageBuilder +from .leahi.leahi_can import LeahiMessage Index: dialin/protocols/leahi/leahi_can.py =================================================================== diff -u --- dialin/protocols/leahi/leahi_can.py (revision 0) +++ dialin/protocols/leahi/leahi_can.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,893 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 CAN.py +# +# @author (last) Micahel Garthwaite +# @date (last) 30-Jun-2023 +# @author (original) Peter Lucia +# @date (original) 02-Apr-2020 +# +############################################################################ + +import threading +from collections import deque +import asyncio +from typing import Callable + +import can +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 +from ...utils import SingletonMeta, IntervalTimer +from concurrent.futures import ThreadPoolExecutor + + +class LeahiMessage: + 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_LENGTH_FIRST_PACKET = 1 + HEADER_LENGTH = 6 + PACKET_LENGTH = 8 + CRC_LENGTH = 1 + MAX_MSG_ID_NUMBER = 65535 + MAX_NUMBER_OF_PAYLOAD_BYTES = 254 + CRC_LIST = [ + 0, 49, 98, 83, 196, 245, 166, 151, 185, 136, 219, 234, 125, 76, 31, 46, + 67, 114, 33, 16, 135, 182, 229, 212, 250, 203, 152, 169, 62, 15, 92, 109, + 134, 183, 228, 213, 66, 115, 32, 17, 63, 14, 93, 108, 251, 202, 153, 168, + 197, 244, 167, 150, 1, 48, 99, 82, 124, 77, 30, 47, 184, 137, 218, 235, + 61, 12, 95, 110, 249, 200, 155, 170, 132, 181, 230, 215, 64, 113, 34, 19, + 126, 79, 28, 45, 186, 139, 216, 233, 199, 246, 165, 148, 3, 50, 97, 80, + 187, 138, 217, 232, 127, 78, 29, 44, 2, 51, 96, 81, 198, 247, 164, 149, + 248, 201, 154, 171, 60, 13, 94, 111, 65, 112, 35, 18, 133, 180, 231, 214, + 122, 75, 24, 41, 190, 143, 220, 237, 195, 242, 161, 144, 7, 54, 101, 84, + 57, 8, 91, 106, 253, 204, 159, 174, 128, 177, 226, 211, 68, 117, 38, 23, + 252, 205, 158, 175, 56, 9, 90, 107, 69, 116, 39, 22, 129, 176, 227, 210, + 191, 142, 221, 236, 123, 74, 25, 40, 6, 55, 100, 85, 194, 243, 160, 145, + 71, 118, 37, 20, 131, 178, 225, 208, 254, 207, 156, 173, 58, 11, 88, 105, + 4, 53, 102, 87, 192, 241, 162, 147, 189, 140, 223, 238, 121, 72, 27, 42, + 193, 240, 163, 146, 5, 52, 103, 86, 120, 73, 26, 43, 188, 141, 222, 239, + 130, 179, 224, 209, 70, 119, 36, 21, 59, 10, 89, 104, 255, 206, 157, 172 + ] + + _seq_num = 1 + + @staticmethod + def build_basic_message(channel_id=0, message=None): + """ + Builds a basic message dictionary containing the channel id and message + + @param channel_id: (int) indicates the channel + @param message: (list) integers forming the message + @return:: dictionary with channel_id and message keys + """ + if message is None: + message = [] + return {'channel_id': channel_id, 'message': message} + + @classmethod + def build_message(cls, channel_id=0, message_id=0, payload=None, seq=None): + """ + Builds a Leahi message + + @param channel_id: (int) indicates the channel + @param message_id: (int) indicating the request type + @param payload: (list) contains the payload + @param seq: (int) Overrides current sequence number if set + @return:: dictionary with channel_id and 8-byte padded message + """ + + if payload is None: + payload = [] + + message_list = [LeahiCanMessenger.START_BYTE] + + if 0 <= message_id <= LeahiMessage.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: + seq *= -1 + + message_seq_in_bytes = seq.to_bytes(2, byteorder=LeahiMessage.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=LeahiMessage.BYTE_ORDER) + + message_list += [message_id_in_bytes[0]] + message_list += [message_id_in_bytes[1]] + + cls._seq_num += 1 + + else: + + return [] + + # Check payload length + payload_length = len(payload) + + # if payload is larger than 255 return nothing + if payload_length <= LeahiMessage.MAX_NUMBER_OF_PAYLOAD_BYTES: + # payload has to be a list + message_list += [payload_length] + else: + return [] + + message_list += payload + + # Because CRC does not include first byte, then we pass a list with out it + message_list += [LeahiMessage.crc8(message_list[1:])] + + message_list = LeahiMessage.pad_message_with_zeros(message_list) + + return LeahiMessage.build_basic_message(channel_id=channel_id, message=message_list) + + @staticmethod + def crc8(message_list): + """ + Returns the calculated crc from a message list + + @param message_list: is a list of integer numbers containing the message + @return:: integer containing a unsigned byte + """ + crc = 0 + for byte in message_list: + unsigned_byte = byte ^ crc + crc = LeahiMessage.CRC_LIST[unsigned_byte] + + return crc + + @staticmethod + def pad_message_with_zeros(message): + """ + Returns a packet padded with zeros that guarantees that the packet is a multiple of 8 bytes. + + @param message: packet that may or may not be multiple of 8 bytes + @return:: packet that is 8-byte multiple + + """ + message_length = len(message) + + # message must be multiple of 8 + if message_length % LeahiMessage.PACKET_LENGTH != 0: + # We need to pad the message with trailing zeros + add_these_many_zeros = math.ceil(message_length / LeahiMessage.PACKET_LENGTH) * \ + LeahiMessage.PACKET_LENGTH - message_length + + message += [0] * add_these_many_zeros + + return message + + @staticmethod + def get_crc(message): + """ + Gets the CRC in message + + @param message: Dialin complete message with CRC + + @return:: CRC in message + """ + + crc_index = LeahiMessage.PAYLOAD_START_INDEX + LeahiMessage.get_payload_length(message) + + return message['message'][crc_index] + + @staticmethod + def verify_crc(message): + """ + Verifies message CRC equals calculated message CRC + + @return:: TRUE if CRC matches, FALSE otherwise + """ + + if message is None: + return False + else: + message_list = message['message'] + + message_length = LeahiMessage.PAYLOAD_START_INDEX + LeahiMessage.get_payload_length(message) + calculated_crc = LeahiMessage.crc8(message_list[1:message_length]) + actual_crc = LeahiMessage.get_crc(message) + + return calculated_crc == actual_crc + + @staticmethod + def get_channel_id(message: dict) -> int: + """ + Returns request ID from message + + @param message: dictionary with channel_id and message keys + @return:: integer with channel id + + """ + + return message['channel_id'] + + @staticmethod + def get_sequence_number(message: dict) -> int: + """ + Returns sequence number from the message + + @param message: dictionary containing the message + @return:: (int) the sequence number + """ + seq = message['message'][LeahiMessage.MSG_SEQ_INDEX:LeahiMessage.MSG_ID_INDEX] + return int.from_bytes(seq, byteorder=LeahiMessage.BYTE_ORDER, signed=True) + + @staticmethod + def create_ack_message(message: dict, passive_mode: bool = True): + """ + Negates the sequence number and replaces the original message's sequence number with the + negated sequence number to create the ACK message. + @param message: (dict) a complete dialin message + @param passive_mode: (dict) true if in passive mode, false otherwise + @return: (dict) ACK message for the input message + """ + message = message.copy() + + seq = struct.unpack('h', bytearray( + message['message'][LeahiMessage.MSG_SEQ_INDEX:LeahiMessage.MSG_ID_INDEX]))[0] + + # send back empty payload since this is an ACK + payload = bytearray() + + channel_id_rx = LeahiMessage.get_channel_id(message) + + if passive_mode: + channel_rx_tx_pairs = { + LeahiChannels.td_alarm_broadcast_ch_id: None, + LeahiChannels.dd_alarm_broadcast_ch_id: None, + LeahiChannels.ui_alarm_broadcast_ch_id: None, + LeahiChannels.td_to_dd_ch_id: None, + LeahiChannels.dd_to_td_ch_id: None, + LeahiChannels.td_to_ui_ch_id: None, + LeahiChannels.td_sync_broadcast_ch_id: None, + LeahiChannels.dd_to_ui_ch_id: None, + LeahiChannels.dd_sync_broadcast_ch_id: None, + LeahiChannels.ui_to_td_ch_id: None, + LeahiChannels.ui_sync_broadcast_ch_id: None, + LeahiChannels.td_to_dialin_ch_id: LeahiChannels.dialin_to_td_ch_id, + LeahiChannels.dd_to_dialin_ch_id: LeahiChannels.dialin_to_dd_ch_id, + LeahiChannels.ui_to_dialin_ch_id: LeahiChannels.dialin_to_ui_ch_id + } + else: + channel_rx_tx_pairs = { + LeahiChannels.td_alarm_broadcast_ch_id: None, + LeahiChannels.dd_alarm_broadcast_ch_id: None, + LeahiChannels.ui_alarm_broadcast_ch_id: None, + LeahiChannels.td_to_dd_ch_id: LeahiChannels.dialin_to_td_ch_id, + LeahiChannels.dd_to_td_ch_id: LeahiChannels.dialin_to_dd_ch_id, + LeahiChannels.td_to_ui_ch_id: LeahiChannels.dialin_to_td_ch_id, + LeahiChannels.td_sync_broadcast_ch_id: None, + LeahiChannels.dd_to_ui_ch_id: LeahiChannels.dialin_to_dd_ch_id, + LeahiChannels.dd_sync_broadcast_ch_id: None, + LeahiChannels.ui_to_td_ch_id: LeahiChannels.dialin_to_ui_ch_id, + LeahiChannels.ui_sync_broadcast_ch_id: LeahiChannels.dialin_to_ui_ch_id, + LeahiChannels.td_to_dialin_ch_id: LeahiChannels.dialin_to_td_ch_id, + LeahiChannels.dd_to_dialin_ch_id: LeahiChannels.dialin_to_dd_ch_id, + LeahiChannels.ui_to_dialin_ch_id: LeahiChannels.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 = LeahiMessage.build_message( + channel_id=channel_id_tx, + message_id=common.msg_defs.MsgIds.MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK.value, + payload=payload, + seq=-seq) + + return message + + @staticmethod + def get_message_id(message): + """ + Returns request ID from packet + @param message: complete Diality Packet + @return:: integer with request ID + + """ + msg_id_array = message['message'][LeahiMessage.MSG_ID_INDEX: + LeahiMessage.PAYLOAD_LENGTH_INDEX] + return int.from_bytes(msg_id_array, LeahiMessage.BYTE_ORDER) + + @staticmethod + def get_message_id_xstr(message): + """ + Returns request ID from packet in hex string + @param message: complete Diality Packet + @return:: integer with request ID + """ + msg_id = "" + for index in range(LeahiMessage.MSG_ID_INDEX, LeahiMessage.PAYLOAD_LENGTH_INDEX): + msg_id += "{0:02X}" .format(message['message'][index]) + return msg_id + + @staticmethod + def get_payload_length(message): + """ + Returns payload length from message + + @param message: dictionary with channel_id and message keys + @return:: a unsigned payload length + """ + return message['message'][LeahiMessage.PAYLOAD_LENGTH_INDEX] + + @staticmethod + def get_payload(message): + """ + Returns payload array from message + + @param message: dictionary with channel_id and message keys + @return:: a payload array if exist + """ + + payload_length = LeahiMessage.get_payload_length(message) + + if payload_length == 0: + return None + else: + return message['message'][LeahiMessage.PAYLOAD_START_INDEX:] + + @staticmethod + def get_total_packets(message, is_array=True): + """ + Returns the number of packets needed to transmit Leahi 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[LeahiMessage.PAYLOAD_LENGTH_INDEX] + + LeahiMessage.HEADER_LENGTH + LeahiMessage.CRC_LENGTH) / + LeahiMessage.PACKET_LENGTH) + + +class LeahiChannels: + """ + Convenience class listing all the possible CAN channels used in the Leahi system. + 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. + """ + + td_alarm_broadcast_ch_id = 0x001 + dd_alarm_broadcast_ch_id = 0x002 + ui_alarm_broadcast_ch_id = 0x004 + td_to_dd_ch_id = 0x008 + dd_to_td_ch_id = 0x010 + td_to_ui_ch_id = 0x020 + td_sync_broadcast_ch_id = 0x040 + dd_to_ui_ch_id = 0x070 + dd_sync_broadcast_ch_id = 0x080 + ui_to_td_ch_id = 0x100 + ui_to_dd_ch_id = 0x110 + ui_sync_broadcast_ch_id = 0x200 + dialin_to_td_ch_id = 0x400 + td_to_dialin_ch_id = 0x401 + dialin_to_dd_ch_id = 0x402 + dd_to_dialin_ch_id = 0x403 + dialin_to_ui_ch_id = 0x404 + ui_to_dialin_ch_id = 0x405 + + +class LongLeahiMessageBuilder: + + def __init__(self, message: can.Message): + """ + LongDialityMessageBuilder is a utility object that helps construct a Leahi 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) + + message = obj.push(msg2), returns None, message is not complete + + message = obj.push(msg3), return the packet which is the concatenation of msg1, msg2 and msg3 + + @param message: a CAN message + """ + self.message_data = [b for b in message.data] + self.number_of_can_packets_needed = LeahiMessage.get_total_packets(self.message_data) + self.number_of_can_packets_up_to_now = 1 + + def push(self, message: can.Message, first_packet=False): + """ + push appends the CAN message to the current list of messages + + @param message: 8-byte message + + @param first_packet: True if it is the first packet received + + @return:: None if the packet is not completed, otherwise returns the complete packet + """ + message_data = [b for b in message.data] + if first_packet: + self.message_data = message_data + self.number_of_can_packets_needed = LeahiMessage.get_total_packets(message_data) + self.number_of_can_packets_up_to_now = 1 + + else: + self.message_data += message_data + self.number_of_can_packets_up_to_now += 1 + + if self.number_of_can_packets_up_to_now == self.number_of_can_packets_needed: + return_message = self.message_data + self.message_data = None + return return_message + + else: + return None + + +class LeahiCanMessenger(metaclass=SingletonMeta): + START_BYTE = LeahiMessage.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): + """ + LeahiCanMessenger constructor + + @param can_interface - string containing the can interface, e.g., 'can0" + @return: DialityCanMessenger object + + """ + self.message_queue_mutex = threading.Lock() + self.response_dictionary_mutex = threading.Lock() + self.transmitting_mutex = threading.Lock() + self.logger = logger + self.message_queue = deque() + self.callback_listener_complete_messages = None + self.callback_listener_invalid_messages = None + self.thread_pool_executor = ThreadPoolExecutor(max_workers=1) + + # try to setup can bus and exit if the can bus has not ben setup to use. + try: + self.bus = socketcan.SocketcanBus(channel=can_interface) + self.loop = asyncio.get_event_loop() + if self.bus is not None: + self.thread_canbus = threading.Thread(target=self.listener, daemon=True) + self.thread_message_queue = threading.Thread(target=self.handle_messages, daemon=True) + else: + self.thread_canbus = None + s = "Can connection is not valid" + self.logger.debug(s) + sys.exit(s) + self.listener_buffer = can.AsyncBufferedReader() + self.notifier = can.Notifier(bus=self.bus, listeners=[self.listener_buffer], loop=self.loop) + except Exception as e: + s = str(e) + self.logger.error(s) + print(s) + sys.exit(19) + + self.passive_mode = passive_mode + self.console_out = console_out + self.send_event = threading.Event() + self.long_message_builders = {} + self.long_msg_channel_id_set = set() + self.messages = None + self.command_response_message = None + self.response_channel_id = -1 + self.run = False + self.sync_response_dictionary = {} + self.ui_received_function_ptr = None + self.pending_requests = {} + self.transmit_interval_dictionary = {} + + def start(self): + """ + starts listening to the can interface. + + """ + + if self.bus is None: + self.logger.error("Cannot start can listener.") + return + else: + self.run = True + if self.thread_message_queue is not None and self.thread_canbus is not None: + if not self.thread_canbus.is_alive(): + self.thread_canbus.start() + self.logger.info("Canbus thread has started.") + if not self.thread_message_queue.is_alive(): + self.thread_message_queue.start() + self.logger.info("Message queue thread has started.") + else: + self.logger.error("Cannot start listener...") + + def stop(self): + """ + Stop listening the can interface + + """ + self.run = False + self.logger.debug("\nCan listener has stopped.") + + def listener(self): + """ + Listens for diality message on the can interface passed during construction. + """ + async def _listener(): + while True: + message = await self.listener_buffer.get_message() + + if message is not None: + self.message_queue.append(message) + + else: # no new packets in receive buffer + # Careful here, making this any shorter will start limiting CPU time for other threads + sleep(0.01) + + if not self.loop.is_running(): + self.loop.run_until_complete(_listener()) + else: + self.loop.create_task(_listener()) + + def handle_messages(self): + """ + Handles messages added to the dialin canbus message queue + @return: None + """ + + while True: + + if not self.message_queue: + # Careful here, making this any shorter will start limiting CPU time for other threads + sleep(0.01) + else: + self.message_queue_mutex.acquire() + message: can.Message = self.message_queue.popleft() + + if message.dlc == LeahiMessage.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 LeahiMessage.PAYLOAD_LENGTH_INDEX < len(can_data): + self.logger.error("Invalid Leahi 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[LeahiMessage.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] == LeahiMessage.START_BYTE and \ + message_length <= LeahiMessage.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 > LeahiMessage.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 Leahi message builder yet, create it + if channel_id not in self.long_message_builders.keys(): + self.long_message_builders[channel_id] = LongLeahiMessageBuilder(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) Leahi 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 = LeahiMessage.build_basic_message(channel_id=channel_id, + message=self.messages) + dialin_msg_id = LeahiMessage.get_message_id(complete_dialin_message) + dialin_ch_id = LeahiMessage.get_channel_id(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 LeahiMessage.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, LeahiMessage.get_crc(complete_dialin_message), + LeahiMessage.crc8(self.messages))) + + if message_valid: + if self.console_out: + self.do_console_out(complete_dialin_message) + # Send an ack if required + if LeahiMessage.get_sequence_number(complete_dialin_message) < 0: + # ACK required. Send back the received message with the sequence sign bit flipped + msg = LeahiMessage.create_ack_message(message=complete_dialin_message, + passive_mode=False) + if msg is not None: + self.send(msg, 0, is_ack=True) + + # We first check if this is a response to a send request that is pending + if self.pending_requests and dialin_msg_id in self.pending_requests: + + self.pending_requests[dialin_msg_id] = complete_dialin_message + self.send_event.set() + + # If it is not, this is a publication message and we need to call it's register function + else: + self.response_dictionary_mutex.acquire() + + if LeahiCanMessenger.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, + complete_dialin_message, message.timestamp + ) + + if dialin_ch_id in self.sync_response_dictionary.keys() and \ + dialin_msg_id in self.sync_response_dictionary[channel_id].keys(): + for function_id in self.sync_response_dictionary[dialin_ch_id][dialin_msg_id]: + self.thread_pool_executor.submit( + self.sync_response_dictionary[dialin_ch_id][dialin_msg_id][function_id], + complete_dialin_message, message.timestamp) + + self.response_dictionary_mutex.release() + else: + self.logger.critical("Invalid message: {}\n".format(self.messages)) + + # Done with this message, let's get the next one + self.messages = None + self.message_queue_mutex.release() + + @staticmethod + def is_ui_received_channel(channel_id: int) -> bool: + """ + checks if the channel id, channel_id is ui channel. + @param channel_id: the channel id to check + @return: true, if the channel is the ui channel. + """ + if channel_id in { + LeahiChannels.ui_to_td_ch_id, + LeahiChannels.ui_to_dd_ch_id, + LeahiChannels.ui_to_dialin_ch_id, + LeahiChannels.ui_sync_broadcast_ch_id, + LeahiChannels.ui_alarm_broadcast_ch_id + }: # check if the channel is in ui channels + return True + else: + return False + + def register_receiving_publication_function(self, channel_id, message_id, function): + """ + Assign a function with packet parameter to an sync request id, e.g., + def function(packet). + + @param channel_id: can channel number where messages are received + @param message_id: Diality request ID in message + @param function: function reference + """ + # function_id is a UID for each callback per channel,msg pair. + function_id = id(function) + # if the channel_id exist, we update the dictionary for the channel_id + self.response_dictionary_mutex.acquire() + if channel_id in self.sync_response_dictionary.keys(): + if message_id in self.sync_response_dictionary[channel_id].keys(): + self.sync_response_dictionary[channel_id][message_id].update({function_id: function}) + else: + self.sync_response_dictionary[channel_id].update( {message_id: {function_id: function}}) + + # otherwise, we need to create the dictionary for the channel_id, msg_id pair + else: + self.sync_response_dictionary[channel_id] = {message_id: {function_id: function}} + self.response_dictionary_mutex.release() + + def register_received_all_ui_publication_function(self, function_ptr: Callable): + """ + Assign a function with packet parameter to an sync request id, e.g., + def function(packet). + + @param function_ptr: function reference + """ + + self.ui_received_function_ptr = function_ptr + + def send(self, + built_message: dict, + time_out: float = DIALIN_MSG_RESP_TO, + resend: bool = False, + is_ack: bool = False): + """ + Sends a Leahi 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 Leahi packet. If a timeout occurs returns None + """ + + msg_sent = False + msg_id = -1 + + # keep trying to send message until we get a response + while not msg_sent: + + channel_id = LeahiMessage.get_channel_id(built_message) + + padded_can_message_array = built_message['message'] + + msg_id = LeahiMessage.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 = LeahiMessage.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 * LeahiMessage.PACKET_LENGTH: + (n + 1) * LeahiMessage.PACKET_LENGTH] + + # Sending one packet at a time + packet = can.Message(arbitration_id=channel_id, + data=packet, + is_extended_id=False) + + self.bus.send(packet, 0) # 0.1) + + self.transmitting_mutex.release() + + # After all messages have been sent, we clear a flag + self.send_event.clear() + + # Block until the timeout completes or until the threading event's flag is set in self.listener, + # indicating a response has been received + self.send_event.wait(time_out) + + # if we're sending an ack, nothing left to do + if is_ack: + return None + + # only resend the message if resend is enabled and we haven't received a response yet + if resend and self.pending_requests.get(msg_id, None) is None: + msg_sent = False + self.logger.debug("No response. Re-sending message.") + else: + msg_sent = True + + response = self.pending_requests.get(msg_id, None) + if response is not None: + del self.pending_requests[msg_id] + return response + + @staticmethod + def _format_message_candump_style(message: can.Message, channel: str, send: bool = True) -> str: + """ + Formats a packet + @param message: (can.Message) The packet to log + @param channel: (str) The channel send or received on + @param send: (bool) Whether we're sending or receiving this packet + @return: The styled message + """ + + tmp = str(message) + + data = tmp[-41:-18].upper() + if send: + data = tmp[-23:].upper() + + return " {0} {1} [{2}] {3}\n".format(channel, str(hex(message.arbitration_id)[2:]).zfill(3), message.dlc, data) + + def do_log_can(self, packet: can.Message, style="candump", channel="can0", send=True): + """ + Logs all packets sent or received by dialin in candump, or non-candump style format + + @param packet: (can.Message) The packet to log + @param style: (str) The style to log in (candump, non-candump) + @param channel: (str) The channel send or received on + @param send: (bool) Whether we're sending or receiving this packet + @return: None + """ + filename = "Dialin_CAN_Send.log" + if not send: + filename = "Dialin_CAN_Receive.log" + if style == "candump": + with open(filename, 'a') as f: + styled_message = self._format_message_candump_style(message=packet, channel=channel, send=send) + f.write(styled_message) + else: + with open(filename, 'a') as f: + f.write("{0}\n".format(packet)) + + @staticmethod + def convert_message_to_string(complete_dialin_message: dict) -> str: + """ + Converts the LeahiMessage to hex string (data len is not hex) + @param complete_dialin_message: the complete can message in dictionary + @return: + """ + channel = "{0:03X}" .format(LeahiMessage.get_channel_id(complete_dialin_message)) + msg_id = LeahiMessage.get_message_id_xstr(complete_dialin_message) + data_len = LeahiMessage.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'][LeahiMessage.PAYLOAD_START_INDEX + i ]) + data += "{0:02X}".format(complete_dialin_message['message'][LeahiMessage.PAYLOAD_START_INDEX + i + 1]) + data += "{0:02X}".format(complete_dialin_message['message'][LeahiMessage.PAYLOAD_START_INDEX + i + 2]) + data += "{0:02X}".format(complete_dialin_message['message'][LeahiMessage.PAYLOAD_START_INDEX + i + 3]) + data += " " + message = "{} {} {} {}".format(channel, msg_id, data_len, data) + return message + + @staticmethod + def do_console_out(complete_dialin_message: dict) -> None: + """ + prints out the message in hex format similar to the candump + @return: None + """ + exception_msg_id = { + MsgIds.MSG_ID_UI_CHECK_IN.value, + MsgIds.MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK + } + msg_id = LeahiMessage.get_message_id(complete_dialin_message) + if msg_id in exception_msg_id: + return + + message = "# " + LeahiCanMessenger.convert_message_to_string(complete_dialin_message) + print(message) + + def register_transmitting_interval_message(self, interval: float, function) ->None: + """ + registers a callback function with a specified time interval to a dictionary + @return: None + """ + function_id = id(function) + + if function_id in self.transmit_interval_dictionary.keys(): + self.logger.error("ERROR: Attempting to assign more than one timed interval per given method.") + self.transmit_interval_dictionary[function_id].stop() + self.transmit_interval_dictionary[function_id].start() + else: + self.transmit_interval_dictionary[function_id] = IntervalTimer(interval, function) + + return function_id Index: dialin/protocols/leahi/leahi_message_builder.py =================================================================== diff -u --- dialin/protocols/leahi/leahi_message_builder.py (revision 0) +++ dialin/protocols/leahi/leahi_message_builder.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,97 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 messageBuilder.py +# +# @author (last) Quang Nguyen +# @date (last) 22-Jul-2021 +# @author (original) Peter Lucia +# @date (original) 11-Nov-2020 +# +############################################################################ + +from dialin.ui import utils, crc + +syncByte = 'A5' + + +def toCandumpFormat(msg: any) -> str: + """ + the converter method which converts the message vMsg to the string that candump tool understands. + @param msg: the message + @return: converted string to candump tool format + """ + if type(msg) == list: + for index, value in enumerate(msg): + msg[index] = ".".join(utils.partition(value, 2, False)) + else: + msg = ".".join(utils.partition(msg, 2, False)) + return msg + + +def toFrames(msg: any) -> list: + """ + converts the message vMsg to frames + @param msg: adds the crc8 checksum at the end of the message vMsg + @return: the frames + """ + mlen = 16 + padded = utils.padding(msg, mlen) + frames = utils.partition(padded, mlen, False) + return frames + + +def addCRC8(string: str, delimiter: str = "") -> str: + """ + adds the crc8 checksum at the end of the string vString + @param string: (str) the string to be used + @param delimiter: (str) the string delimiter + @return: the string with crc8 + """ + return string + delimiter + crc.calc_crc8(string, delimiter) + + +def textToByte(text: str, length) -> str: + """ + converts the string text to bytes by the given length + @param text: (str) given string + @param length: (int) given length + @return: converted text + """ + new_text = "" + text_len = len(text) + for i in range(length): + if i < text_len: + new_text += utils.toI08(ord(text[i])) + else: + new_text += utils.toI08(0) + new_text += utils.toI08(0) # null /0 + return new_text + + +def buildMessage(msg_id: int, length: int, ack: bool, *args) -> str: + """ + builds message from the parameter givven + @param msg_id: (int) the message ID + @param length: (int) length of the message payload in bytes + @param ack: (bool) if true the message requires acknowledge back + @param args: payload arguments. + @return: built message + """ + msg = "" + if ack: + seq = -1 + else: + seq = 1 + + msg += utils.toI16(seq) # always used seq# (-)1 (for now) + msg += utils.toI16(msg_id) + msg += utils.toI08(length) + for arg in args: + msg += arg + msg += crc.calc_crc8(msg) + return syncByte + msg Index: dialin/ui/leahi/dd_simulator.py =================================================================== diff -u --- dialin/ui/leahi/dd_simulator.py (revision 0) +++ dialin/ui/leahi/dd_simulator.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,737 @@ +########################################################################### +# +# Copyright (c) 2021-2025 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 dd_simulator.py +# +# @author (last) Behrouz NematiPour +# @date (last) 07-Jun-2024 +# @author (original) Peter Lucia +# @date (original) 16-Mar-2021 +# +############################################################################ +from ...common import * +from ...protocols import LeahiMessage, LeahiCanMessenger, LeahiChannels +from ...utils import * +from ...utils.base import AbstractSubSystem, LogManager +from .. import leahi_message_builder + + +class DDSimulator(AbstractSubSystem): + instance_count = 0 + + def __init__(self, can_interface: str = "can0", + log_level: str = None, + console_out: bool = False, + passive_mode: bool = False, + auto_response: bool = False): + + super().__init__() + DDSimulator.instance_count = DDSimulator.instance_count + 1 + + self._log_manager = LogManager(log_level=log_level, log_filepath=self.__class__.__name__ + ".log") + self.logger = self._log_manager.logger + self.console_out = console_out + self.can_interface = LeahiCanMessenger(can_interface=can_interface, + logger=self.logger, + console_out=console_out, + passive_mode=passive_mode) + self.can_interface.start() + + self.ui_dd_set_rtc_req_timestamp = 0.0 + self.ui_dd_fw_version_req_timestamp = 0.0 + self.ui_service_info_req_timestamp = 0.0 + + if self.can_interface is not None: + channel_id = LeahiChannels.ui_to_dd_ch_id + if auto_response: + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_DD_SET_RTC_REQUEST.value, + self._handler_set_rtc_request) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_sync_broadcast_ch_id, + MsgIds.MSG_ID_FW_VERSIONS_REQUEST.value, + self._handler_request_dd_version) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_sync_broadcast_ch_id, + MsgIds.MSG_ID_UI_SERVICE_INFO_REQUEST.value, + self._handler_system_usage_response) + + def _handler_system_usage_response(self,message,timestamp=0.0) -> None: + """ + Handles a request for system usage + + @return: None + """ + self.logger.debug("Handling request for system usage.") + self.ui_service_info_req_timestamp = timestamp + + payload = integer_to_bytearray(1619628663) + payload += integer_to_bytearray(1619887863) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_SERVICE_SCHEDULE_DATA_TO_UI.value, + payload=payload) + + self.can_interface.send(message, 0) + + def _handler_set_rtc_request(self, message: dict,timestamp=0.0) -> None: + """ + Handles a request to set the DD RTC + @param message: (dict) the message content + @return: None + """ + + epoch = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + self.ui_dd_set_rtc_req_timestamp = timestamp + self.logger.debug("DD: Request to set the DD epoch to {0}".format(epoch)) + + self.cmd_send_set_rtc_response(YES, 0) + + def cmd_send_set_rtc_response(self, response: int, reason: int) -> None: + """ + Sends a set RTC response message + + @param response: integer - 0=NO, 1=YES + @param reason: integer - the rejection reason + @return: None + """ + self.logger.debug("DD: Sending response {0} reason {1}".format(response, reason)) + + payload = integer_to_bytearray(response) + payload += integer_to_bytearray(reason) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_UI_SET_RTC_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_checkin_dg(self) -> None: + """ + check-in (keep alive) message from DD + @return: none + """ + + payload = ["A5", "01", "00", "06", "00", "00", "76", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": LeahiChannels.dd_to_td_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def cmd_set_dd_ro_pump_data(self, set_pt_pressure: int, flow_rate: float, pwm: float) -> None: + """ + the DD RO Pump Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(F32) | #3:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x1F00| 0x080 | 8 | 1 Hz | N | DD | All | DD RO Pump Data | \ref Data::mPressure | \ref Data::mFlowRate | \ref Data::mPWM | @param vSetPtPressure: + + @param set_pt_pressure: (int) set Point Pressure + @param flow_rate: float - Flow Rate + @param pwm: float - PWM + @return: none + """ + + payload = integer_to_bytearray(set_pt_pressure) + payload += float_to_bytearray(flow_rate) + payload += float_to_bytearray(pwm) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_RO_PUMP_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_pressures_data(self, ro_inlet_pressure: float, ro_outlet_pressure: float, + drain_inlet_pressure: float, drain_outlet_pressure: float, + barometric_pressure : float) -> None: + """ + the DD Pressures Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | #4:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x2000| 0x080 | 8 | 1 Hz | N | DD | All | DD Pressures Data | \ref Data::mROInletPSI | \ref Data::mROOutletPSI | \ref Data::mDrainInletPSI | \ref Data::mDrainOutletPSI | + + @param ro_inlet_pressure: float - RO Inlet PSI + @param ro_outlet_pressure: float - RO Outlet PSI + @param drain_inlet_pressure: float - Drain Inlet PSI + @param drain_outlet_pressure: float - Drain Outlet PSI + @return: none + """ + + payload = float_to_bytearray(ro_inlet_pressure) + payload += float_to_bytearray(ro_outlet_pressure) + payload += float_to_bytearray(drain_inlet_pressure) + payload += float_to_bytearray(drain_outlet_pressure) + payload += float_to_bytearray(barometric_pressure) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_PRESSURES_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_drain_pump_data(self, set_pt_pwm: int, dac_value: int) -> None: + """ + the DD Drain Pump Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: | + |0x2400| 0x080 | 8 | 1 Hz | N | DD | All | DD Drain Pump Data | \ref Data::mRPM | \ref Data::mDAC | + + @param set_pt_pwm: integer - Set Point RPM + @param dac_value: integer - DAC Value + @return: none + """ + + payload = integer_to_bytearray(set_pt_pwm) + payload += integer_to_bytearray(dac_value) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DRAIN_PUMP_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_operation_mode(self, dd_op_mode: int) -> None: + """ + the DD Operation Mode Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x2700| 0x080 | 8 | 1 Hz | N | DD | All | DD Operation Mode Data | \ref Data::mOpMode | + + @param dd_op_mode: integer - DD Operation Mode + @return: none + """ + + payload = integer_to_bytearray(dd_op_mode) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_OP_MODE_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_reservoir_data(self, active_reservoir: int, fill_to_vol_ml: int, drain_to_vol_ml: int) -> None: + """ + the DD Reservoir Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x2800| 0x080 | 8 | 1 Hz | N | DD | All | DD Reservoir Data | \ref Data::mActiveReservoir | \ref Data::mFillToVol | \ref Data::mDrainToVol | + + @param active_reservoir: integer - Active Reservoir + @param fill_to_vol_ml: integer - Fill To Volume ML + @param drain_to_vol_ml: integer - Drain To Vol ML + @return: none + """ + + payload = integer_to_bytearray(active_reservoir) + payload += integer_to_bytearray(fill_to_vol_ml) + payload += integer_to_bytearray(drain_to_vol_ml) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_RESERVOIRS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_valves_states(self, valves_states): + """ + the DD Valves States Data message setter/sender method + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U16) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x2A00| 0x080 | 8 | 2 Hz | N | DD | All | DD Valves States Data | \ref Data::mStates | + + @param valves_states: integer - Valves states + @return: none + """ + + payload = integer_to_bytearray(valves_states) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_VALVES_STATES_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_heaters_data(self, + mMainPrimayHeaterDC : float , + mSmallPrimaryHeaterDC : float , + mTrimmerHeaterDC : float , + mPrimaryTargetTemp : float , + mTrimmerTargetTemp : float , + mPrimaryHeaterState : int , + mTrimmerHeaterState : int , + mPrimaryEfficiency : float , + mPrimaryCalcTargetTemp : float , + mTrimmerCalcCurrentTemp : float , + mTrimmerUseLastDC : int , + mTrimmerPreviousFlow : float , + mTrimmerContolCounter : int ) -> None: + """ + the DD Heaters Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: | + |0x2C00| 0x080 | 8 | 2 Hz | N | DD | All | DD Heaters Data | + + | Payload || + | || + | # 1:(F32) | \ref Data::mMainPrimayHeaterDC | + | # 2:(F32) | \ref Data::mSmallPrimaryHeaterDC | + | # 3:(F32) | \ref Data::mTrimmerHeaterDC | + | # 4:(F32) | \ref Data::mPrimaryTargetTemp | + | # 5:(F32) | \ref Data::mTrimmerTargetTemp | + | # 6:(U32) | \ref Data::mPrimaryHeaterState | + | # 7:(U32) | \ref Data::mTrimmerHeaterState | + | # 8:(F32) | \ref Data::mPrimaryEfficiency | + | # 9:(F32) | \ref Data::mPrimaryCalcTargetTemp | + | #10:(F32) | \ref Data::mTrimmerCalcCurrentTemp | + | #11:(U32) | \ref Data::mTrimmerUseLastDC | + | #12:(F32) | \ref Data::mTrimmerPreviousFlow | + | #13:(U32) | \ref Data::mTrimmerContolCounter | + + @param mMainPrimayHeaterDC : ( float ) Main primary heater DC + @param mSmallPrimaryHeaterDC : ( float ) Small primary heater DC + @param mTrimmerHeaterDC : ( float ) Trimmer heater DC + @param mPrimaryTargetTemp : ( float ) Primary heater target temperature + @param mTrimmerTargetTemp : ( float ) Trimmer heater target temperature + @param mPrimaryHeaterState : ( int ) Primary heater state + @param mTrimmerHeaterState : ( int ) Trimmer heater state + @param mPrimaryEfficiency : ( float ) Primary heater efficiency + @param mPrimaryCalcTargetTemp : ( float ) Primary heater calculated target temperature + @param mTrimmerCalcCurrentTemp : ( float ) Trimmer heater calculated current temperature + @param mTrimmerUseLastDC : ( int ) Trimmer heater use last duty cycle + @param mTrimmerPreviousFlow : ( float ) Trimmer previous flow + @param mTrimmerContolCounter : ( int ) Trimmer control counter + @return: none + """ + + payload = float_to_bytearray ( mMainPrimayHeaterDC ) + payload += float_to_bytearray ( mSmallPrimaryHeaterDC ) + payload += float_to_bytearray ( mTrimmerHeaterDC ) + payload += float_to_bytearray ( mPrimaryTargetTemp ) + payload += float_to_bytearray ( mTrimmerTargetTemp ) + payload += integer_to_bytearray ( mPrimaryHeaterState ) + payload += integer_to_bytearray ( mTrimmerHeaterState ) + payload += float_to_bytearray ( mPrimaryEfficiency ) + payload += float_to_bytearray ( mPrimaryCalcTargetTemp ) + payload += float_to_bytearray ( mTrimmerCalcCurrentTemp ) + payload += integer_to_bytearray ( mTrimmerUseLastDC ) + payload += float_to_bytearray ( mTrimmerPreviousFlow ) + payload += integer_to_bytearray ( mTrimmerContolCounter ) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_HEATERS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_load_cell_readings_data(self, reservoir1_primary: float, reservoir1_backup: float, + reservoir2_primary: float, reservoir2_backup: float) -> None: + """ + The DD Load Cell Readings Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | #4:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: | + |0x0C00| 0x080 | 8 | 10 Hz | N | DD | All | DD Load Cell Readings Data | \ref Data::mReservoir1Prim | \ref Data::mReservoir1Bkup | \ref Data::mReservoir2Prim | \ref Data::mReservoir2Bkup | + @param reservoir1_primary: float - Reservoir 1 Primary + @param reservoir1_backup: float - Reservoir 1 Backup + @param reservoir2_primary: float - Reservoir 2 Primary + @param reservoir2_backup: float - Reservoir 2 Backup + @return: none + """ + + payload = float_to_bytearray(reservoir1_primary) + payload += float_to_bytearray(reservoir1_backup) + payload += float_to_bytearray(reservoir2_primary) + payload += float_to_bytearray(reservoir2_backup) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_LOAD_CELL_READINGS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_dd_temperatures_data(self, inlet_primary_heater: float, outlet_primary_heater: float, + conductivity_sensor1: float, conductivity_sensor2: float, + outlet_redundancy: float, inlet_dialysate: float, + primary_heater_thermocouple: float, trimmer_heater_thermocouple: float, + primary_heater_cold_junction: float, trimmer_heater_cold_junction: float, + primary_heater_internal_temp: float, trimmer_heater_internal_temp: float) -> None: + """ + the DD Temperatures Data message setter/sender method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(F32) | #2:(F32) | #3:(F32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: | + |0x2D00| 0x080 | 8 | 2 Hz | N | DD | All | DD Temperatures Data | \ref Data::mInletPrimaryHeater | \ref Data::mOutletPrimaryHeater | \ref Data::mConductivitySensor1 | + + | #4:(F32) | #5:(F32) | #6:(F32) | #7:(F32) | #8:(F32) | + |:--: |:--: |:--: |:--: |:--: | + | \ref Data::mConductivitySensor2 | \ref Data::mOutletRedundancy | \ref Data::mInletDialysate | \ref Data::mPrimaryHeaterThermoCouple | \ref Data::mTrimmerHeaterThermoCouple | + + | #9:(F32) | #10:(F32) | #11:(F32) | #12:(F32) | + | :--: |:--: |:--: |:--: | + | \ref Data::mPrimaryHeaterColdJunction | \ref Data::mTrimmerHeaterColdJunction | \ref Data::mPrimaryHeaterInternal | \ref Data::mTrimmerHeaterInternal | + @param inlet_primary_heater: (float) Inlet Primary Heater + @param outlet_primary_heater: (float) Outlet Primary Heater + @param conductivity_sensor1: (float) Conductivity Sensor 1 + @param conductivity_sensor2: (float) Conductivity Sensor 2 + @param outlet_redundancy: (float) Outlet Redundancy + @param inlet_dialysate: (float) Inlet Dialysate + @param primary_heater_thermocouple: (float) Primary Heater Thermocouple + @param trimmer_heater_thermocouple: (float) Trimmer Heater Thermocouple + @param primary_heater_cold_junction: (float) Primary Heater ColdJunction + @param trimmer_heater_cold_junction: (float) Trimmer Heater ColdJunction + @param primary_heater_internal_temp: (float) Primary Heater Internal Temperature + @param trimmer_heater_internal_temp: (float) Trimmer HeaterInternal Temperature + @return: none + """ + + payload = float_to_bytearray(inlet_primary_heater) + payload += float_to_bytearray(outlet_primary_heater) + payload += float_to_bytearray(conductivity_sensor1) + payload += float_to_bytearray(conductivity_sensor2) + payload += float_to_bytearray(outlet_redundancy) + payload += float_to_bytearray(inlet_dialysate) + payload += float_to_bytearray(primary_heater_thermocouple) + payload += float_to_bytearray(trimmer_heater_thermocouple) + payload += float_to_bytearray(primary_heater_cold_junction) + payload += float_to_bytearray(trimmer_heater_cold_junction) + payload += float_to_bytearray(primary_heater_internal_temp) + payload += float_to_bytearray(trimmer_heater_internal_temp) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_TEMPERATURE_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_unknown_dg(self) -> None: + """ + the unknown message from DD setter/sender method + @return: none + """ + + payload = integer_to_bytearray(0) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_UNUSED.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_accelerometer_dd_data(self, x: float, y: float, z: float, + x_max: float, y_max: float, z_max: float, + x_tilt: float, y_tilt: float, z_tilt: float) -> None: + """ + the accelerometer hd data message method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: | + |0x3400| 0x080 | 8 | 1Hz | N | TD | UI | DD Accelerometer data | + + | #1:(F32) | #2:(F32) | #3:(U32) | + |:--: |:--: |:--: | + | \ref Data::mX | \ref Data::mY | \ref Data::mX | + + | #4:(F32) | #5:(F32) | #6:(U32) | + |:--: |:--: |:--: | + | \ref Data::mXMax | \ref Data::mYMax | \ref Data::mXMax | + + | #7:(F32) | #8:(F32) | #9:(U32) | + |:--: |:--: |:--: | + | \ref Data::mXTilt | \ref Data::mYTilt | \ref Data::mXTilt | + + @param x: float - x axis + @param y: float - y axis + @param z: float - z axis + @param x_max: float - x axis max + @param y_max: float - y axis max + @param z_max: float - z axis max + @param x_tilt: float - x axis tilt + @param y_tilt: float - y axis tilt + @param z_tilt: float - z axis tilt + @return: None + """ + + payload = float_to_bytearray(x) + payload += float_to_bytearray(y) + payload += float_to_bytearray(z) + payload += float_to_bytearray(x_max) + payload += float_to_bytearray(y_max) + payload += float_to_bytearray(z_max) + payload += float_to_bytearray(x_tilt) + payload += float_to_bytearray(y_tilt) + payload += float_to_bytearray(z_tilt) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_ACCELEROMETER_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def _handler_request_dd_version(self, message,timestamp=0.0) -> None: + """ + Handles a request for the TD version + + @return: None + """ + self.logger.debug("Handling request for dg version.") + self.ui_dd_fw_version_req_timestamp = timestamp + self.cmd_send_version_dd_data(9, 9, 9, 9, 9, 9, 9, 9) + self.cmd_send_dd_serial_number() + + def cmd_send_dd_serial_number(self) -> None: + """ + Sends the dg serial number + @return: None + """ + payload = b'0123456789\0' + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_SERIAL_NUMBER_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_version_dd_data(self, major: int, minor: int, micro: int, build: int, + fpga_id: int, fpga_major: int, fpga_minor: int, fpga_lab: int, + compatibility_rev: int) -> None: + """ + [0x1E00] # 30 0x080 Rsp Y DD All + DD f/w version + U08=Major + U08=Minor + U08=Micro + U16=Build + U08=FPGA ID + U08=FPGA Major + U08=FPGA Minor + U08=FPGA Lab + U32=compatibility rev + --- + the dg version response message method + @param major: (uint) - Major version number + @param minor: (uint) - Minor version number + @param micro: (uint) - Micro version number + @param build: (uint) - Build version number + @param fpga_id: (int) - FPGA id version number + @param fpga_major: (int) - FPGA Major version number + @param fpga_minor: (int) - FPGA Minor version number + @param fpga_lab: (int) - FPGA Lab version number + @param compatibility_rev: (uint) - The FWs/UI compatibility revision + @return: None + """ + + payload = unsigned_byte_to_bytearray(major) + payload += unsigned_byte_to_bytearray(minor) + payload += unsigned_byte_to_bytearray(micro) + payload += unsigned_short_to_bytearray(build) + payload += unsigned_byte_to_bytearray(fpga_id) + payload += unsigned_byte_to_bytearray(fpga_major) + payload += unsigned_byte_to_bytearray(fpga_minor) + payload += unsigned_byte_to_bytearray(fpga_lab) + payload += unsigned_integer_to_bytearray(compatibility_rev) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_VERSION_REPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_serial_dd_data(self, serial: str): + """ + the dg version serial response message method + @param serial: serial number + @return: None + """ + + payload = bytes(serial, 'ascii') + b'\x00' + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_SERIAL_NUMBER_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + @staticmethod + def build_dd_debug_text(text: str) -> list: + """ + the debug text message from DD builder method + @param text: string - the debug text + @return: none + """ + message_length = 40 + txt = leahi_message_builder.textToByte(text, message_length) # + 1 null term + msg = leahi_message_builder.buildMessage(GuiActionType.DDDebugText, 1 * (message_length + 1), False, txt) + return leahi_message_builder.toFrames(msg) + + def cmd_send_dd_pre_treatment_filter_flush_progress_data(self, total, countdown) -> None: + """ + send the pretreatment filter flush progress data + @param total: (U32) Total time in second + @param countdown: (U32) count down time in second + @return: None + """ + payload = integer_to_bytearray(total) + payload += integer_to_bytearray(countdown) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_FILTER_FLUSH_PROGRESS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_dd_disinfect_progress_time_heat(self, total: int, countdown: int) -> None: + """ + the broadcast progress heat disinfect time + @param total: the total time + @param countdown: the gradual countdown time + @return: None + """ + payload = integer_to_bytearray(total) + payload += integer_to_bytearray(countdown) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_HEAT_DISINFECT_TIME_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_dd_post(self, item: int, passed: bool, done: bool = False) -> None: + """ + send hd post message the single(item) or the final(done) + @param item: the post state/item index + @param passed: the post result single or final + @param done: if this is the final post message this should be true + @return: None + """ + payload = integer_to_bytearray(passed) + if not done: + payload += integer_to_bytearray(item) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_POST_FINAL_TEST_RESULT.value if done + else MsgIds.MSG_ID_DD_POST_SINGLE_TEST_RESULT.value, + payload=payload) + + self.can_interface.send(message, 0) + + # ------------------------------------------------ GENERAL MESSAGES ------------------------------------------------ + + def cmd_send_dd_disinfect_progress_time_checmical(self, total: int, countdown: int) -> None: + """ + the broadcast progress chemical disinfect time + @param total: the total time + @param countdown: the gradual countdown time + @return: None + """ + payload = integer_to_bytearray(total) + payload += integer_to_bytearray(countdown) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=MsgIds.MSG_ID_DD_CHEM_DISINFECT_TIME_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_dd_general_response(self, message_id: int, accepted: int, reason: int, + is_pure_data: bool = False, + has_parameters: bool = False, + parameters_payload: any = 0x00, + channel_id=LeahiChannels.dd_to_ui_ch_id) -> None: + """ + a general method to send any standard response message, by it's id and list of parameters. + @param message_id: the id of the message + @param accepted: the standard accepted parameter of any response message + @param reason: the standard rejection reason parameter of any response message + @param is_pure_data: The message only has data + @param has_parameters: if the message has parameter this needs to be true. + @param parameters_payload: the list of parameters pre-converted and ready to be concatenated to the payload. + @param channel_id: (int) indicates the channel + @return: None + """ + payload = "" + + if not is_pure_data: + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + + if has_parameters: + payload = parameters_payload + + message = LeahiMessage.build_message(channel_id=channel_id, + message_id=message_id, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_dd_general_progress_data(self, message_id: int, total: int, countdown: int) -> None: + """ + a general method t send any standard progress data message, by id + @param message_id: the id of the message + @param total: the total value of the progress data + @param countdown: the remaining or countdown value + @return: None + """ + payload = integer_to_bytearray(total) + payload += integer_to_bytearray(countdown) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=message_id, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_send_dd_ack(self, seq: int) -> None: + """ + sending dg ack message by the sequence seq + @param seq: the message sequence number + @return: None + """ + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_to_ui_ch_id, + message_id=GuiActionType.Acknow, + seq=seq) + + self.can_interface.send(message, 0) + + def cmd_send_dd_conductivity_data(self, ro_rejection_ratio: float, cpi_conductivity: float, + cpo_conductivity: float, cd1_conductivity: float, + cd2_conductivity: float, CPi_raw : float, CPo_raw:float, + CD1_raw: float, CD2_raw:float, CPi_sensor_status:int, + CPo_sensor_status : int, CD1_sensor_status:int, + CD2_sensor_status: int) -> None: + """ + A simulated DD broadcast message of conductivity data. + @param ro_rejection_ratio: (float) RO Pump rejection ratio + @param cpi_conductivity: (float) CPi conductivity + @param cpo_conductivity: (float) CPo conductivity + @param cd1_conductivity: (float) CD1 conductivity + @param cd2_conductivity: (float) CD2 conductivity + @return: None + """ + payload = float_to_bytearray(ro_rejection_ratio) + payload += float_to_bytearray(cpi_conductivity) + payload += float_to_bytearray(cpo_conductivity) + payload += float_to_bytearray(cd1_conductivity) + payload += float_to_bytearray(cd2_conductivity) + payload += float_to_bytearray(CPi_raw) + payload += float_to_bytearray(CPo_raw) + payload += float_to_bytearray(CD1_raw) + payload += float_to_bytearray(CD2_raw) + payload += integer_to_bytearray(CPi_sensor_status) + payload += integer_to_bytearray(CPo_sensor_status) + payload += integer_to_bytearray(CD1_sensor_status) + payload += integer_to_bytearray(CD2_sensor_status) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.dd_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_DD_CONDUCTIVITY_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + Index: dialin/ui/leahi/td_simulator.py =================================================================== diff -u --- dialin/ui/leahi/td_simulator.py (revision 0) +++ dialin/ui/leahi/td_simulator.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,892 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 td_simulator.py +# +# @author (last) Dara Navaei +# @date (last) 22-Jan-2025 +# @author (original) Peter Lucia +# @date (original) 06-Aug-2020 +# +############################################################################ +#from . import messageBuilder + +import subprocess +import enum +from typing import Callable, List +from inspect import signature +from ...common import * +from ...protocols.leahi.leahi_can import LeahiMessage, LeahiCanMessenger, LeahiChannels +from ...protocols.leahi.leahi_message_builder import * +from ..utils import * +from ...utils.base import AbstractSubSystem, LogManager +from dialin.utils.conversions import * +from dialin.ui import messageBuilder + +TD_PLUGINS_PATH = './dialin/td' + +class TDSimulator(AbstractSubSystem): + NUM_BROADCAST_PARAMETERS = 10 + instance_count = 0 + + # UI version message field positions + START_POS_MAJOR = LeahiMessage.PAYLOAD_START_INDEX + END_POS_MAJOR = START_POS_MAJOR + 1 + START_POS_MINOR = END_POS_MAJOR + END_POS_MINOR = START_POS_MINOR + 1 + START_POS_MICRO = END_POS_MINOR + END_POS_MICRO = START_POS_MICRO + 1 + START_POS_BUILD = END_POS_MICRO + END_POS_BUILD = START_POS_BUILD + 2 + START_POS_COMPAT = END_POS_BUILD + END_POS_COMPAT = START_POS_COMPAT + 4 + + + def __init__(self, can_interface: str = "can0", + log_level: bool = None, + console_out: bool = False, + passive_mode: bool = True, + auto_response: bool = False): + """ + The TDSimulator constructor + + @param can_interface: (str) the can interface name + @param log_level: (str) or (None) if not set, contains the logging level + @param console_out: (bool) If True, write each dialin message to the console. + """ + super().__init__() + + + TDSimulator.instance_count = TDSimulator.instance_count + 1 + + self.auto_response = auto_response + self._log_manager = LogManager(log_level="DEBUG", log_filepath=self.__class__.__name__ + ".log") + self.logger = self._log_manager.logger + self.console_out = console_out + self.can_interface = LeahiCanMessenger(can_interface=can_interface, + logger=self.logger, + console_out=console_out, + passive_mode=passive_mode) + self.can_interface.start() + + + self.ui_initiate_treatment_req_timestamp = 0.0 + self.ui_set_uf_volume_parameter_timestamp = 0.0 + self.ui_new_treatment_parameters_timestamp = 0.0 + self.ui_user_confirm_treatment_parameters_timestamp = 0.0 + self.ui_tx_end_cmd_timestamp = 0.0 + self.ui_td_set_rtc_req_timestamp = 0.0 + self.ui_fw_versions_req_timestamp = 0.0 + self.ui_service_info_req_timestamp = 0.0 + self.ui_version_info_response_timestamp = 0.0 + self.ui_version_info_compatibility_timestamp = 0.0 + + if self.can_interface is not None: + channel_id = LeahiChannels.ui_to_td_ch_id + if auto_response: + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_INITIATE_TREATMENT_REQUEST.value, + self._handler_ui_initiate_treatment) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_SET_UF_VOLUME_PARAMETER_REQUEST.value, + self._handler_ui_pre_treatment_uf_request) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_NEW_TREATMENT_PARAMS_REQUEST.value, + self._handler_ui_validate_parameters) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_USER_CONFIRM_TREATMENT_PARAMS_REQUEST.value, + self._handler_ui_confirm_treatment) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_TX_END_CMD_REQUEST.value, + self._handler_ui_end_treatment) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_td_SET_RTC_REQUEST.value, + self._handler_set_rtc_request) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_sync_broadcast_ch_id, + MsgIds.MSG_ID_FW_VERSIONS_REQUEST.value, + self._handler_request_td_version) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_sync_broadcast_ch_id, + MsgIds.MSG_ID_UI_SERVICE_INFO_REQUEST.value, + self._handler_system_usage_response) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_to_td_ch_id, + MsgIds.MSG_ID_UI_VERSION_INFO_RESPONSE.value, + self._handler_ui_post_ui_version_compatibility) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_to_td_ch_id, + MsgIds.MSG_ID_UI_VERSION_INFO_RESPONSE.value, + self._handler_ui_version) + + self.treatment_parameter_rejections = TreatmentParameterRejections() + + # initialize variables that will be populated by UI version response + self.ui_version = None + + + def add_publication(self, channel_id: LeahiChannels, message_id: MsgIds, function_ptr: Callable) -> None: + """ + Allows later addition of publication to the TDSimulator + @param channel_id: (LeahiChannels) the channel id of the message + @param message_id: (MsgIds) the message id + @param function_ptr: (Callable) the pointer to the message handler function + @return: None + """ + if self.auto_response: + if channel_id > 0 and message_id != MsgIds.MSG_ID_UNUSED and function_ptr is not None: + self.can_interface.register_receiving_publication_function(channel_id, + message_id, + function_ptr) + else: + self.logger.debug("rejected publication registration {0}, {1}, {2}".format(channel_id, + message_id, + function_ptr)) + + def set_ui_all_publication(self, function_ptr: Callable) -> None: + """ + Allows later addition of publication to the TDSimulator + This function needs improvements, it has been implemented to quickly being used by Development Testing team. + @param function_ptr: (Callable) the pointer to the message handler function + @return: None + """ + function_signature_exp = "(message:dict)->None" # TODO: update later to get param name and type. + if not callable(function_ptr): + print("ui all publication rejected (not a function)") + self.logger.debug("ui all publication rejected (not a function)") + else: + function_signature_act = str(signature(function_ptr)).replace(" ", "") + if function_signature_act == function_signature_exp: + self.can_interface.register_received_all_ui_publication_function(function_ptr) + else: + print("ui all publication rejected {0},{1}" + .format(function_signature_exp, + function_signature_act)) + self.logger.debug("rejected ui all messages publication registration, expected function signature {0}, got {1} " + .format(function_signature_exp, + function_signature_act)) + + ############################################################################ + # Test Methods + ############################################################################ + + def test_started(self, test_name: str): + """ + Logs that a test was started + + @param test_name: The name of the test + @return: None + """ + self.logger.info("Test Started: {0}".format(test_name)) + + def test_completed(self): + """ + Logs that a test was completed + + @return: None + """ + self.logger.info("Test Completed") + + def get_ui_version(self): + """ + Gets the ui version + + @return: The ui version + """ + return self.ui_version + + ############################################################################ + # Actnowledge Methods + ############################################################################ + + def send_acknowledge_td(self): + """ + the acknowledge from TD + @return: none + """ + + payload = ["A5", "01", "00", "FF", "FF", "00", "19", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": LeahiChannels.td_sync_broadcast_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + def send_acknowledge_ui(self): + """ + the acknowledge from UI + @return: none + """ + + payload = ["A5", "01", "00", "FF", "FF", "00", "19", "00"] + payload = [int(each, 16) for each in payload] + + message = {"channel_id": LeahiChannels.ui_to_td_ch_id, + "message": payload} + + self.can_interface.send(message, 0) + + ############################################################################ + # Utility Methods + ############################################################################ + @staticmethod + def wait_for_message_to_be_sent(delay=0.050): + """ + After each multi-frame message put a 50ms sleep, time.sleep(0.1) + it seems it's needed otherwise the test will check a value which has not been received yet. + :@param delay: the number of seconds to wait + @return: none + """ + time.sleep(delay) + + @staticmethod + def build_td_debug_text(vtext): + """ + the debug text message from TD builder method + @param vtext: (str) the debug text + @return: none + """ + message_length = 40 + txt = messageBuilder.textToByte(vtext, message_length) # + 1 null term + msg = messageBuilder.buildMessage(GuiActionType.TDDebugText, 1 * (message_length + 1), False, txt) + return messageBuilder.toFrames(msg) + + @staticmethod + def send_td_debug_text(debug_text): + """ + the debug text message from TD setter/sender method + @param debug_text: (str) the debug text + @return: none + """ + + frames = TDSimulator.build_td_debug_text(debug_text) + frames = messageBuilder.toCandumpFormat(frames) + for frame in frames: + subprocess.call(['cansend', 'can0', '020#{}'.format(frame)]) + + TDSimulator.wait_for_message_to_be_sent() + + ############################################################################ + # Send / Broadcast TD State Mode + ############################################################################ + def send_td_operation_mode(self, op_mode: int, sub_mode: int = 0): + """ + Broadcasts the current TD operation mode + @param op_mode: td operation mode + @param sub_mode: td operation sub-mode + @return: None + """ + + if not isinstance(op_mode, int): + raise ValueError("Provided mode is not of type 'int'") + if not isinstance(sub_mode, int): + raise ValueError("Provided mode is not of type 'int'") + + payload = integer_to_bytearray(op_mode) + payload += integer_to_bytearray(sub_mode) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_OP_MODE_DATA.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send / Broadcast TD Blood Pump Data + ############################################################################ + def send_blood_pump_data(self, + flow_set_pt: int, measured_flow: float, rotor_speed: int, + mot_speed : float, mot_current : float, set_rpm : float, + rotor_count: float, pres_flow : float, rotor_hall : int) -> None: + """ + The Blood Pump Data message + + | MSG | CAN ID | Ack | Src | Dst | Description | + |:----:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | N | TD | All | Blood Pump Data | + | Payload || + | || + | #1:(S32) | \ref Data::mSetFlowRate \ref Data::flow_set_pt | + | #2:(F32) | \ref Data::mMeasuredFlow \ref Data::measured_flow | + | #8:(U32) | \ref Data::mRotorSpeed \ref Data::rotor_speed | + | #4:(F32) | \ref Data::mMotorSpeed \ref Data::mot_speed | + | #5:(F32) | \ref Data::mMotorCurrent \ref Data::mot_current | + | #6:(F32) | \ref Data::mSetRPM \ref Data::set_rpm | + | #7:(U32) | \ref Data::mRotorCount \ref Data::rotor_count | + | #3:(F32) | \ref Data::mPressureFlow \ref Data::measured_flow | + | #9:(U32) | \ref Data::mRotorHallState \ref Data::rotor_hall | + + @param flow_set_pt : (S32) Flow Set Point + @param measured_flow : (float) Measured Flow + @param rot_speed : (float) Rotor Speed + @param mot_speed : (float) Motor Speed + @param mot_current : (float) Motor Current + @param set_rpm : (float) Set RPM + @param rotor_count : (U32) Rotor Count + @param pres_flow : (U32) Pressure flow rate + @param rot_hall : (U32) Rotor hall sensor + @return: None + """ + + payload = integer_to_bytearray (flow_set_pt) + payload += float_to_bytearray (measured_flow) + payload += unsigned_integer_to_bytearray (rotor_speed) + payload += float_to_bytearray (mot_speed) + payload += float_to_bytearray (mot_current) + payload += float_to_bytearray (set_rpm) + payload += unsigned_integer_to_bytearray (rotor_count) + payload += float_to_bytearray (pres_flow) + payload += unsigned_integer_to_bytearray (rotor_hall) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_DATA.value, + payload = payload) + + self.can_interface.send(message, 0) + + ############################################################################ + # Send TD Blood Pump Response To UI + ############################################################################ + def send_blood_flow_set_point_response(self, accepted: int, reason: int, value: int) -> None : + """ + The Blood Flow Set Point Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Blood Flow Set Point Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mBloodFlowSetPoint | + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param blood_rate : (int) Blood Flow Set Point + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += integer_to_bytearray(value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_BLOOD_FLOW_SET_POINT_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send TD Blood Flow Response To UI + ############################################################################ + def send_current_blood_flow_response(self, accepted: int, reason: int, value: float) -> None : + """ + The Blood Flow Rate Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Blood Flow Rate Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mBloodFlowRate | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Blood Flow Rate + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += float_to_bytearray (value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_BLOOD_FLOW_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + ############################################################################ + # Send TD Blood Flow Pressure Response To UI + ############################################################################ + def send_current_blood_flow_pressure_response(self, accepted: int, reason: int, value: float) -> None : + """ + The Blood Flow Pressure change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Blood Flow Pressure change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mBloodFlowPressureRate | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Blood Flow Pressure Rate + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += float_to_bytearray (value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_BLOOD_FLOW_PRESSURE_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + ############################################################################ + # Send TD Blood Flow Response To UI + ############################################################################ + def send_current_motor_speed_response(self, accepted: int, reason: int, value: float) -> None : + """ + The Current Motor Speed Rate Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Current Motor Speed Rate Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mCurrentMotorSpeed | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Current Motor Speed + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += float_to_bytearray (value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_CURRENT_MOTOR_SPEED_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send TD Motor Speet Set Point Response To UI + ############################################################################ + def send_motor_speed_set_point_response(self, accepted: int, reason: int, value: float) -> None : + """ + The Motor Speed Set Point Rate Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Motor Speed Set Point Rate Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mBloodRate | + | #4:(U32) | \ref Data::mMotorSpeedSetPoint | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Motor Speed Set Point + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += float_to_bytearray (value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_MOTOR_SPEED_SET_POINT_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send TD Rotor Count Response To UI + ############################################################################ + def send_rotor_count_response(self, accepted: int, reason: int, value: int) -> None : + """ + The Rotor Count Rate Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Rotor Count Rate Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mBloodRate | + | #4:(U32) | \ref Data::mRotorCount | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Rotor Count + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += integer_to_bytearray(value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_ROTOR_COUNT_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send TD Rotor Speed Response To UI + ############################################################################ + def send_rotor_speed_response(self, accepted: int, reason: int, value: int) -> None : + """ + The Rotor Speed Rate Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Rotor Speed Rate Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mBloodRate | + | #4:(U32) | \ref Data::mRotorSpeed | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Rotor Speed + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += integer_to_bytearray(value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_ROTER_SPEED_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send TD Set RPM Response To UI + ############################################################################ + def send_set_rpm_response(self, accepted: int, reason: int, value: float) -> None : + """ + The Set RPM Rate Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Set RPM Rate Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mSetRPM | + + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Set RPM + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += float_to_bytearray (value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_RPM_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################################ + # Send TD Rotor Hall State Response To UI + ############################################################################ + def send_rotor_hall_state_response(self, accepted: int, reason: int, value: int) -> None : + """ + The Rotor Hall Change Response message + + | MSG | CAN ID | Type | Ack | Src | Dst | Description | + |:----:|:------:|:------:|:---:|:---:|:---:|:-----------: | + |0x1100| 0x100 | Rsp | Y | TD | UI | Rotor Hall Change Response | + | Payload || + | || + | #1:(U32) | \ref Data::mAccepted | + | #2:(U32) | \ref Data::mReason | + | #3:(U32) | \ref Data::mRotorHall | + + + @param accepted : (int) boolean accept/reject response + @param reason : (int) rejection reason + @param value : (int) Rotor Hall State + @return : None + """ + + payload = integer_to_bytearray(accepted) + payload += integer_to_bytearray(reason) + payload += integer_to_bytearray(value) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_ROTOR_HALL_CMD_RESPONSE.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ############################################################ + # Binding Methodes Between GUI-Simulator and TC-Simulator: + # Send Request For Individual Blood Pump Data + ############################################################ + def send_blood_flow_set_point_data(self, value : int) -> None: + """Request Send blood flow set point + + Args: + value (int): _description_ + """ + self.send_blood_pump_data(value, 0, 0, + 0, 0, 0, + 0, 0, 0) + pass + + + def send_current_blood_flow_data(self, value : float) -> None: + """Request Send current blood flow + + Args: + value (float): _description_ + """ + self.send_blood_pump_data(0, value, 0, + 0, 0, 0, + 0, 0, 0) + pass + + + def send_rotor_speed_data(self, value : float) -> None: + """Request Send rotor speed + + Args: + value (float): _description_ + """ + self.send_blood_pump_data(0, 0, value, + 0, 0, 0, + 0, 0, 0) + pass + + + def send_motor_speed_set_point_data(self, value : float) -> None: + """Request Send motor speed set point + + Args: + value (float): _description_ + """ + self.send_blood_pump_data(0, 0, 0, + value, 0, 0, + 0, 0, 0) + pass + + def send_current_motor_speed_data(self, value : float) -> None: + """Request Send current motor speed + + Args: + value (float): _description_ + """ + self.send_blood_pump_data(0, 0, 0, + 0, value, 0, + 0, 0, 0) + pass + + + def send_set_rpm_data(self, value : float) -> None: + """Request Send set rpm + + Args: + value (float): _description_ + """ + self.send_blood_pump_data(0, 0, 0, + 0, 0, value, + 0, 0, 0) + pass + + + def send_rotor_count_data(self, value : int) -> None: + """Request Send rotor count + + Args: + value (int): _description_ + """ + self.send_blood_pump_data(0, 0, 0, + 0, 0, 0, + value, 0, 0) + pass + + + def send_blood_flow_pressure_data(self, value : float) -> None: + """Request Send blood flow pressure + + Args: + value (float): _description_ + """ + self.send_blood_pump_data(0, 0, 0, + 0, 0, 0, + 0, value, 0) + pass + + def send_rotor_hall_state_data(self, value : int) -> None: + """Request Send hall state + + Args: + value (int): _description_ + """ + self.send_blood_pump_data(0, 0, 0, + 0, 0, 0, + 0, 0, value) + pass + + + + ########################################################################## + # TODO: + ########################################################################## + def send_blood_pressure_data(self, + mArtPres : float, mVenPres : float, mLimitState : int, + mArtMin : int, mArtMax : int, mVenMin : int, mVenMax : int, + mArtLong : float, mVenLong : float, + mTmpPres : float, mTmpMin : float, mTmpMax : float ) -> None: + """ + The Pressure Data message + + | MSG | CAN ID | Ack | Src | Dst | Description | + |:----:|:------:|:---:|:---:|:---:|:-----------: | + |0x2A00| 0x100 | N | TD | All | Pressure Data | + | Payload || + | || + | #1:(F32) | \ref Data::mArtPres | + | #2:(F32) | \ref Data::mVenPres | + | #3:(U32) | \ref Data::mLimitState | + | #4:(S32) | \ref Data::mArtMin | + | #5:(S32) | \ref Data::mArtMax | + | #6:(S32) | \ref Data::mVenMin | + | #7:(S32) | \ref Data::mVenMax | + | #8:(F32) | \ref Data::mArtLong | + | #9:(F32) | \ref Data::mVenMin | + | #10:(F32) | \ref Data::mTmpPres | + | #11:(F32) | \ref Data::mTmpMin | + | #12:(F32) | \ref Data::mTmpMax | + + @param mArtPres : (F32), Arterial Pressure + @param mVenPres : (F32), Venous Pressure + @param mLimitState : (U32), Limit State + @param mArtMin : (S32), Min Arterial Pressure + @param mArtMax : (S32), Max Arterial Pressure + @param mVenMin : (S32), Min Venous Pressure + @param mVenMax : (S32), Max Venous Pressure + @param mArtLong : (F32), Arterial Long + @param mVenLong : (F32), Venous Long + @param mTmpPres : (F32), Tmp Pressure + @param mTmpMin : (F32), Tmp Min + @param mTmpMax : (F32), Tmp Max + @return: None + """ + + payload = float_to_bytearray (mArtPres) + payload += float_to_bytearray (mVenPres) + payload += unsigned_integer_to_bytearray(mLimitState) + payload += integer_to_bytearray (mArtMin) + payload += integer_to_bytearray (mArtMax) + payload += integer_to_bit_array (mVenMin) + payload = integer_to_bytearray (mVenMax) + payload += float_to_bytearray (mArtLong) + payload += float_to_bytearray (mVenLong) + payload += float_to_bytearray (mTmpPres) + payload += float_to_bytearray (mTmpMin) + payload += float_to_bytearray (mTmpMax) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_sync_broadcast_ch_id, # td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_PRESSURE_DATA.value, + payload = payload) + + self.can_interface.send(message, 0) + + + ########################################################################## + # TODO: + ########################################################################## + def send_air_trap_data(self, + mLowLevel : int, mUpLevel : int, + mLowLevRaw: int, mUpLevRaw : int, + mVlvState : int, mControlling: bool ) -> None: + """ + The Air Trap Data message + + | MSG | CAN ID | Ack | Src | Dst | Description | #1:(U32) | #2:(U32) | #3:(U32) | #4:(U32) | #5:(U32) | #6:(BOOL) + |:----:|:------:|:---:|:---:|:---:|:-----------: |:--: |:--: |:--: |:--: |:--: |:--: + |0x2A00| 0x100 | N | TD | All | Air Trap Data | \ref Data::mLowLevel | \ref Data::mUpLevel | \ref Data::mLowLevRaw | \ref Data::mUpLevRaw | \ref Data::mVlvState | \ref Data::mControlling + + @param mLowLevel : (U32) , Valve Low Level + @param mUpLevel : (U32) , Valve Up Level + @param mLowLevRaw : (U32) , Valve Low Level Raw + @param mUpLevRaw : (U32) , Valve Up Level Raw + @param mVlvState : (U32) , Valve State + @param mControlling : (BOOL), Valve Controlling + @return: None + """ + + payload = unsigned_integer_to_bytearray(mLowLevel) + payload += unsigned_integer_to_bytearray(mUpLevel) + payload += unsigned_integer_to_bytearray(mLowLevRaw) + payload += unsigned_integer_to_bytearray(mUpLevRaw) + payload += unsigned_integer_to_bytearray(mVlvState) + payload += integer_to_bit_array (mControlling) # TODO: AMIR: How to handle BOOL + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_sync_broadcast_ch_id, # td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_BLOOD_PUMP_DATA.value, + payload = payload) + + self.can_interface.send(message, 0) + + + + def send_treatment_event(self, event_id: int, + type1: int, data1: float, + type2: int, data2: float) -> None: + """ + send the treatment log data + @param event_id: (U32) event ID + @param type1: (U32) type 1 + @param data1: (F32) data 1 + @param type2: (U32) type 2 + @param data2: (F32) data 2 + @return: none + """ + payload = integer_to_bytearray (event_id) + payload += unsigned_integer_to_bytearray(type1) + payload += float_to_bytearray (data1) + payload += unsigned_integer_to_bytearray(type2) + payload += float_to_bytearray (data2) + + message = LeahiMessage.build_message(channel_id = LeahiChannels.td_sync_broadcast_ch_id, # td_to_ui_ch_id, + message_id = MsgIds.MSG_ID_TD_EVENT_DATA.value, + payload = payload) + + self.can_interface.send(message, 0) + Index: dialin/ui/leahi/td_simulator_alarms.py =================================================================== diff -u --- dialin/ui/leahi/td_simulator_alarms.py (revision 0) +++ dialin/ui/leahi/td_simulator_alarms.py (revision 5ca05704da5cc2dfea17182bea02b048c37620f8) @@ -0,0 +1,493 @@ +########################################################################### +# +# Copyright (c) 2020-2025 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 td_simulator_alarms.py +# +# @author (last) Micahel Garthwaite +# @date (last) 17-Aug-2023 +# @author (original) Peter Lucia +# @date (original) 28-Sep-2020 +# +############################################################################ +import struct +from time import sleep, time + +from ...utils import YES, NO +from ...protocols.leahi.leahi_can import LeahiMessage, LeahiCanMessenger, LeahiChannels +from logging import Logger +from ...utils.base import AbstractSubSystem +from ...utils.conversions import * +from ...common.leahi.leahi_msg_defs import MsgIds, MsgFieldPositions +from ...common.leahi.leahi_alarm_defs import AlarmList +from dialin.common.prs_defs import AlarmDataTypes + +HIGH = 3 +MED = 2 +LOW = 1 +NONE = 0 + +HIGH_PRIORITY_COLOR = "#c53b33" +MED_LOW_PRIORITY_COLOR = "#f5a623" + + +class Alarms: + + # TODO: this should be generated from FW + # ALARM_ID = (priority, alarmID, escalates in, silent_espires_in, flags) + MSG_ID_ALARM_ID_NO_ALARM = (NONE, 0, 0, 0, 0) + MSG_ID_ALARM_STATUS_DATA = (NONE, 1, 0, 0, 0) # 0x1 # DEC: 1 CAN_TD_ALARM : 0x001 SRC: td-firmware MODULE: Messaging + MSG_ID_ALARM_TRIGGERED = (NONE, 2, 0, 0, 0) # 0x2 # DEC: 2 CAN_TD_ALARM : 0x001 SRC: td-firmware MODULE: AlarmMgmtTD + MSG_ID_ALARM_CLEARED = (NONE, 3, 0, 0, 0) # 0x3 # DEC: 3 CAN_TD_ALARM : 0x001 SRC: td-firmware MODULE: AlarmMgmtTD + MSG_ID_ALARM_CONDITION_CLEARED = (NONE, 4, 0, 0, 0) # 0x4 # DEC: 4 CAN_TD_ALARM : 0x001 SRC: leahi-fwcommon MODULE: AlarmMgmt + MSG_ID_TD_ALARM_INFORMATION_DATA = (NONE, 7, 0, 0, 0) # 0x7 # DEC: 7 CAN_TD_BROADCAST: 0x100 SRC: td-firmware MODULE: AlarmMgmtTD + + + + +class TDAlarmsSimulator(AbstractSubSystem): + instance_count = 0 + + def __init__(self, can_interface: LeahiCanMessenger, logger: Logger): + """ + @param can_interface: Leahi Can Messenger object + """ + super().__init__() + TDAlarmsSimulator.instance_count = TDAlarmsSimulator.instance_count + 1 + + self.can_interface = can_interface + self.logger = logger + self.flags = 0 + self.clear_after_user_action = False + self.current_alarm_volume = 5 + + if self.can_interface is not None: + channel_id = LeahiChannels.ui_to_td_ch_id + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_USER_ALARM_SILENCE_REQUEST.value, + self._handler_alarm_silence) + self.can_interface.register_receiving_publication_function(channel_id, + MsgIds.MSG_ID_UI_ALARM_USER_ACTION_REQUEST.value, + self._handler_user_action) + self.can_interface.register_receiving_publication_function(LeahiChannels.ui_to_td_ch_id, + MsgIds.MSG_ID_UI_SET_ALARM_AUDIO_VOLUME_LEVEL_CMD_REQUEST.value, + self._handler_request_override_alarm_volume) + + def _send_alarm_volume_broadcast(self): + """ + Sends the alarm volume broadcast message + @return: None + """ + + payload = integer_to_bytearray(5) # alarm volume + payload += float_to_bytearray(1.0) # alarm audio current high gain (mA) + payload += float_to_bytearray(1.0) # alarm audio current low gain (mA) + payload += float_to_bytearray(1.0) # alarm backup audio current (mA) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_TD_ALARM_INFORMATION_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def _handler_request_override_alarm_volume(self, message: dict) -> None: + """ + Handler for a UI request to override the alarm volume level + @param message: + @return: + """ + vol = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.START_POS_FIELD_1 + 4]))[0] + + self.logger.debug("Received request to override alarm volume to {0}".format(vol)) + + if 1 <= vol <= 5: + self.current_alarm_volume = vol + payload = integer_to_bytearray(YES) + payload += integer_to_bytearray(0) + else: + payload = integer_to_bytearray(NO) + payload += integer_to_bytearray(1) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_sync_broadcast_ch_id, + message_id=MsgIds.MSG_ID_TD_ALARM_AUDIO_VOLUME_SET_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_activate_alarm_id(self, state: int = HIGH, + alarm: int = 0, + escalates_in: int = 0, + silence_expires: int = 0, + flags: int = 0): + """ + Activates the specified alarm + + @param state: (int) Alarm priority + @param alarm: (int) the alarm id + @param escalates_in: (int) how long until the alarm escalates + @param silence_expires: (int) seconds until silence expires + @param flags: (int) See 'cmd_make_alarm_flags' + @return: None + """ + + state = integer_to_bytearray(state) + top = integer_to_bytearray(alarm) + escalates_in = integer_to_bytearray(escalates_in) + silence_expires = integer_to_bytearray(silence_expires) + flags = integer_to_bytearray(flags) + + payload = state + top + escalates_in + silence_expires + flags + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_STATUS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_activate_alarm(self, alarm: AlarmList, + state: int = HIGH, + escalates_in: int = 0, + silence_expires: int = 0, + flags: int = 0): + """ + Activates the specified alarm + + + @param alarm: the alarm enum + @param state: Alarm priority + @param escalates_in: how long until the alarm escalates + @param silence_expires: seconds until silence expires + @param flags: additional alarm flags + Alarm flags: + eFlag_systemFault = 0 + eFlag_stop = 1 + eFlag_noClear = 2 + eFlag_noResume = 3 + eFlag_noRinseback = 4 + eFlag_noEndTreatment = 5 + eFlag_noNewTreatment = 6 + eFlag_bypassDialyzer = 7 + eFlag_alarmsToEscalate = 8 + eFlag_alarmsSilenced = 9 + eFlag_userAcknowledged = 10 + ... unused = 11 - 16 + @return: None + """ + + state = integer_to_bytearray(state) + top = integer_to_bytearray(alarm.value) + escalates_in = integer_to_bytearray(escalates_in) + silence_expires = integer_to_bytearray(silence_expires) + flags = integer_to_bytearray(flags) + + payload = state + top + escalates_in + silence_expires + flags + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_STATUS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_make_alarm_flags(self, + system_fault=0, + stop=0, + no_clear=0, + no_resume=0, + no_rinseback=0, + no_end_treatment=0, + no_new_treatment=0, + user_must_ack=0, + alarms_to_escalate=0, + alarms_silenced=0, + lamp_on=0, + unused_1=0, + unused_2=0, + unused_3=0, + no_minimize=0, + top_condition=0 + ): + """ + Helper function to construct the flags + + @param system_fault: One or more system faults has been triggered + @param stop: Alarm(s) have stopped treatment/activity and placed system in a safe state + @param no_clear: One or more active alarms is not recoverable + @param no_resume: The "resume" user recovery option is disabled + @param no_rinseback: The "rinseback" user recovery option is disabled + @param no_end_treatment: The "end treatment" user recovery option is disabled + @param no_new_treatment: A new treatment may not be started without cycling power to system + @param user_must_ack: The "ok" user recovery option is enabled + @param alarms_to_escalate: One or more active alarms will escalate in time + @param alarms_silenced: Alarms have been temporarily silenced by user + @param lamp_on: Alarm lamp is currently on (for syncing to UI) + @param unused_1: unused + @param unused_2: unused + @param unused_3: unused + @param no_minimize: Prevent user from minimizing alarm window + @param top_condition: The top alarm's condition is still being detected + @return: (int) containing all the flags + """ + flags = 0 + flags ^= system_fault * 2 ** 0 \ + | stop * 2 ** 1 \ + | no_clear * 2 ** 2 \ + | no_resume * 2 ** 3 \ + | no_rinseback * 2 ** 4 \ + | no_end_treatment * 2 ** 5 \ + | no_new_treatment * 2 ** 6 \ + | user_must_ack * 2 ** 7 \ + | alarms_to_escalate * 2 ** 8 \ + | alarms_silenced * 2 ** 9 \ + | lamp_on * 2 ** 10 \ + | unused_1 * 2 ** 11 \ + | unused_2 * 2 ** 12 \ + | unused_3 * 2 ** 13 \ + | no_minimize * 2 ** 14 \ + | top_condition * 2 ** 15 + return flags + + def cmd_send_clear_alarms(self): + """ + Broadcasts a clear alarms message + + @return: None + """ + + state = integer_to_bytearray(0) + top = integer_to_bytearray(0) + escalates_in = integer_to_bytearray(0) + silence_expires = integer_to_bytearray(0) + flags = integer_to_bytearray(0) + + payload = state + top + escalates_in + silence_expires + flags + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_STATUS_DATA.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_alarm_cleared(self, alarm_id: int = 0): + """ + Broadcasts to clear a specific alarm ID + + the Alarm Cleared message builder method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x0400| 0x001 | 1 | Event | Y | TD | All | Alarm Cleared | \ref Data::mAlarmID | + + @return: None + """ + + payload = integer_to_bytearray(alarm_id) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_CLEARED.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_alarm_cleared_condition(self, alarm_id: int = 0): + """ + Broadcasts that the alarm condition has been cleared + + the Alarm Cleared message builder method + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:---------:|:---:|:------:|:---:|:---:|:---:|:---------------------: |:--: | + |0x3F00| 0x001,2,4 | 1 | Event | Y | TD | All | Alarm Condition Cleared | \ref Data::mAlarmID | + + @return: None + """ + + payload = integer_to_bytearray(alarm_id) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_CONDITION_CLEARED.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_set_alarm_triggered(self, alarm_id, field_descriptor_1: int, data_field_1: str, + field_descriptor_2: int, data_field_2: str, + priority: int, rank: int, clear_top: int) -> None: + """ + Triggers an alarm. + + | MSG | CAN ID | Box | Type | Ack | Src | Dst | Description | #1:(U32) | + |:----:|:------:|:---:|:------:|:---:|:---:|:---:|:-----------: |:--: | + |0x0300| 0x001 | 1 | Event | Y | TD | All | Alarm Triggered | \ref Data::mAlarmID | + + @param alarm_id: (int) the alarm id to trigger + @param field_descriptor_1: (int) alarm data 1 type + @param data_field_1: (str) alarm data 1 + @param field_descriptor_2: (int) alarm data 2 type + @param data_field_2: (str) alarm data 2 + @param priority: (int) alarm priority + @param rank: (int) alarm rank + @param clear_top: (int) clear top only + + @return: None + """ + zero = integer_to_bytearray(0) + payload = integer_to_bytearray(alarm_id) + if field_descriptor_1 == AlarmDataTypes.ALARM_DATA_TYPE_NONE: + payload += zero + payload += zero + payload += zero + payload += zero + else: + if field_descriptor_1 == AlarmDataTypes.ALARM_DATA_TYPE_F32: + payload += integer_to_bytearray(field_descriptor_1) + payload += float_to_bytearray(float(data_field_1)) + else: # BOOL, S32, U32 + payload += integer_to_bytearray(field_descriptor_1) + payload += integer_to_bytearray(int(data_field_1)) + if field_descriptor_2 == AlarmDataTypes.ALARM_DATA_TYPE_NONE: + payload += zero + payload += zero + else: + if field_descriptor_2 == AlarmDataTypes.ALARM_DATA_TYPE_F32: + payload += integer_to_bytearray(field_descriptor_2) + payload += float_to_bytearray(float(data_field_2)) + else: # BOOL, S32, U32 + payload += integer_to_bytearray(field_descriptor_2) + payload += integer_to_bytearray(int(data_field_2)) + + payload += unsigned_integer_to_bytearray(priority) + payload += unsigned_integer_to_bytearray(rank) + payload += unsigned_integer_to_bytearray(clear_top) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_TRIGGERED.value, + payload=payload) + + self.can_interface.send(message, 0) + + def cmd_repeat_broadcast_alarm(self, freq: int = 4, timeout: float = float('inf'), **kwargs): + """ + Broadcast the specified alarm message at particular frequency + + + @param freq: cycles / s of the broadcast + @param timeout: How long to broadcast the alarm for + @param kwargs: arguments to pass to cmd_activate_alarm + @return: None + """ + start = time() + current = time() + counter = 0 + silence_expires = 60 + + while current - start < timeout: + self.cmd_activate_alarm(flags=self.flags, silence_expires=silence_expires, **kwargs) + sleep(1.0 / freq) + current = time() + counter += 1 + if silence_expires > 0: + silence_expires -= 1 + else: + self.flags = self.cmd_make_alarm_flags(alarms_silenced=0) + + def set_flags(self, flags): + """ + Sets the alarm flags + + @param flags: + @return: None + """ + self.flags = flags + + def _handler_alarm_acknowledge(self) -> None: + """ + TODO: Remove + Handles the alarm acknowledge message + + @return: None + """ + + self.logger.debug("Alarm acknowledged") + self.flags = self.flags | 2 ** 10 + + def _handler_alarm_silence(self, message): + """ + Handles the alarm silence message + + @param message: the message with 0 = cancel, 1 = silence + @return: None + """ + + request = message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1][0] + if request: + self.logger.debug("Alarm Silence Request Start {0}".format(request)) + self.flags = self.flags | 2 ** 9 + else: + self.logger.debug("Alarm Silence Request Cancel {0}".format(request)) + self.flags = self.flags & ~2 ** 9 + + def cmd_alarm_condition_cleared(self, alarm_id: int): + """ + Sends the alarm condition cleared message + @param alarm_id: (int) The alarm ID + @return: None + """ + payload = integer_to_bytearray(alarm_id) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_alarm_broadcast_ch_id, + message_id=MsgIds.MSG_ID_ALARM_CONDITION_CLEARED.value, + payload=payload) + + self.can_interface.send(message, 0) + + def _handler_user_action(self, message): + """ + Called when the user responds to an alarm + @return: None + """ + response = struct.unpack('i', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + + self.logger.debug("User response to alarm: {0}".format(response)) + if self.clear_after_user_action: + self.cmd_send_clear_alarms() + + def cmd_send_active_list_response(self, accept: bool, reason: int = 0, + a0: int = 0, a1: int = 0, a2: int = 0, a3: int = 0, a4: int = 0, + a5: int = 0, a6: int = 0, a7: int = 0, a8: int = 0, a9: int = 0) -> None: + """ + send the list of active alarms + @param accept: boolean value true if the request accepted + @param reason: the rejection reason + @param a0: alarm id 0 in the list - First + @param a1: alarm id 1 in the list + @param a2: alarm id 2 in the list + @param a3: alarm id 3 in the list + @param a4: alarm id 4 in the list + @param a5: alarm id 5 in the list + @param a6: alarm id 6 in the list + @param a7: alarm id 7 in the list + @param a8: alarm id 8 in the list + @param a9: alarm id 9 in the list - Last + @return: None + """ + payload = integer_to_bytearray(accept) + payload += integer_to_bytearray(reason) + payload += integer_to_bytearray(a0) + payload += integer_to_bytearray(a1) + payload += integer_to_bytearray(a2) + payload += integer_to_bytearray(a3) + payload += integer_to_bytearray(a4) + payload += integer_to_bytearray(a5) + payload += integer_to_bytearray(a6) + payload += integer_to_bytearray(a7) + payload += integer_to_bytearray(a8) + payload += integer_to_bytearray(a9) + + message = LeahiMessage.build_message(channel_id=LeahiChannels.td_to_ui_ch_id, + message_id=MsgIds.MSG_ID_TD_ACTIVE_ALARMS_LIST_REQUEST_RESPONSE.value, + payload=payload) + + self.can_interface.send(message, 0)