Index: leahi_dialin/common/msg_ids.py =================================================================== diff -u -r9a2c2774952a68e8f075f32a28d3f99d77bf6f43 -rf771138f7dc3c56d711fd5bae09d2adf45f4af68 --- leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 9a2c2774952a68e8f075f32a28d3f99d77bf6f43) +++ leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision f771138f7dc3c56d711fd5bae09d2adf45f4af68) @@ -531,6 +531,9 @@ MSG_ID_DD_BICART_DEPRESSURISE_REQUEST_OVERRIDE_REQUEST = 0xA074 # DD dry bicart depressurise overrride request MSG_ID_DD_TREATMENT_PARAMS_OVERRIDE_REQUEST = 0xA075 # DD Treatment parameters override request MSG_ID_DD_NVM_RECORD_CRC_OVERRIDE_REQUEST = 0xA076 # DD NVM Record CRC override Request + MSG_ID_DD_HEAT_DISINFECT_STATE_DATA = 0xA077 # DD Heat Disinfection state data + MSG_ID_DD_HEAT_DISINFECT_REQUEST_DATA = 0xA078 # DD heat disinfect start/stop request + MSG_ID_DD_HEAT_DISINFECT_MODE_DATA_PUBLISH_OVERRIDE_REQUEST = 0xA079 # DD heat disinfect mode data publish interval override request MSG_ID_DD_ALARM_STATE_OVERRIDE_REQUEST = 0xAF03 # DD alarm state override request MSG_ID_DD_STOP_GEN_DIALYSATE_MODE_OVERRIDE_REQUEST = 0xAF04 # DD stop general dialysate mode override request Index: leahi_dialin/dd/dialysate_delivery.py =================================================================== diff -u -r489ce5ef49041b863501b7fecd6e4716e7dcaa9f -rf771138f7dc3c56d711fd5bae09d2adf45f4af68 --- leahi_dialin/dd/dialysate_delivery.py (.../dialysate_delivery.py) (revision 489ce5ef49041b863501b7fecd6e4716e7dcaa9f) +++ leahi_dialin/dd/dialysate_delivery.py (.../dialysate_delivery.py) (revision f771138f7dc3c56d711fd5bae09d2adf45f4af68) @@ -39,7 +39,7 @@ from .modules.ultrafiltration import DDUltrafiltration from .modules.valves import DDValves from .modules.voltages import DDVoltages - +from .modules.heatdisinfect import DDHeatDisinfect from .proxies.ro_proxy import ROProxy from .proxies.td_proxy import TDProxy @@ -143,6 +143,7 @@ self.ultrafiltration = DDUltrafiltration(self.can_interface, self.logger) #: The Ultrafiltration module self.valves = DDValves(self.can_interface, self.logger) #: The Valves module self.voltages = DDVoltages(self.can_interface, self.logger) #: The Voltages module + self.heatdisinfect = DDHeatDisinfect(self.can_interface, self.logger) #: The heat disinfect module self.ro_proxy = ROProxy(self.can_interface, self.logger) #: The RO Proxy module (imitates commands sent by DD) self.td_proxy = TDProxy(self.can_interface, self.logger) #: The TD Proxy module (imitates commands sent by UI) Index: leahi_dialin/dd/modules/heatdisinfect.py =================================================================== diff -u --- leahi_dialin/dd/modules/heatdisinfect.py (revision 0) +++ leahi_dialin/dd/modules/heatdisinfect.py (revision f771138f7dc3c56d711fd5bae09d2adf45f4af68) @@ -0,0 +1,181 @@ +########################################################################### +# +# Copyright (c) 2020-2026 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 heat_disinfect.py +# +# @author (last) Jashwant Gantyada +# @date (last) 19-Aug-2026 +# @author (original) Jashwant Gantyada +# @date (original) 19-Aug-2026 +# +############################################################################ + +# Module imports +from logging import Logger + +# Project imports +from leahi_dialin.common.constants import NO_RESET +from leahi_dialin.common.generic_defs import DataTypes +from leahi_dialin.common.msg_ids import MsgIds +from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override +from leahi_dialin.protocols.CAN import CanMessenger, CanChannels +from leahi_dialin.utils.abstract_classes import AbstractSubSystem +from leahi_dialin.utils.base import publish +from leahi_dialin.utils.conversions import integer_to_bytearray + + +class DDHeatDisinfect(AbstractSubSystem): + """ + Heat Disinfect + + Dialysate Delivery (DD) Dialin API sub-class for Heat Disinfect related commands. + """ + + def __init__(self, can_interface: CanMessenger, logger: Logger): + """ + @param can_interface: Can Messenger object + """ + super().__init__() + + self.can_interface = can_interface + self.logger = logger + + if self.can_interface is not None: + self.can_interface.register_receiving_publication_function(channel_id = CanChannels.dd_sync_broadcast_ch_id, + message_id = MsgIds.MSG_ID_DD_HEAT_DISINFECT_STATE_DATA.value, + function = self._handler_heat_disinfect_sync) + + self.execution_state = 0 #: heatDisinfectExecState + self.sub_state = 0 #: heatDisinfectSubState + self.dd_recirc_dwell_time_ms = 0 #: ddRecircDwellTimeMS + self.wcid_recirc_dwell_time_ms = 0 #: wcidRecircDwellTimeMS + self.dd_dwell_at_target = 0 #: ddDwellAtTarget + self.wcid_dwell_at_target = 0 #: wcidDwellAtTarget + self.dd_heat_disinfect_timestamp = 0 #: The timestamp of the last message + + + @property + def execution_state(self) -> int: + return self._execution_state + + @execution_state.setter + def execution_state(self, value): + self._execution_state = value + + + @property + def sub_state(self) -> int: + return self._sub_state + + @sub_state.setter + def sub_state(self, value): + self._sub_state = value + + + @property + def dd_recirc_dwell_time_ms(self) -> int: + return self._dd_recirc_dwell_time_ms + + @dd_recirc_dwell_time_ms.setter + def dd_recirc_dwell_time_ms(self, value): + self._dd_recirc_dwell_time_ms = value + + + @property + def wcid_recirc_dwell_time_ms(self) -> int: + return self._wcid_recirc_dwell_time_ms + + @wcid_recirc_dwell_time_ms.setter + def wcid_recirc_dwell_time_ms(self, value): + self._wcid_recirc_dwell_time_ms = value + + + @property + def dd_dwell_at_target(self) -> int: + return self._dd_dwell_at_target + + @dd_dwell_at_target.setter + def dd_dwell_at_target(self, value): + self._dd_dwell_at_target = value + + + @property + def wcid_dwell_at_target(self) -> int: + return self._wcid_dwell_at_target + + @wcid_dwell_at_target.setter + def wcid_dwell_at_target(self, value): + self._wcid_dwell_at_target = value + + + @publish(["msg_id_dd_heat_disinfect_state_data", + "execution_state", "sub_state", + "dd_recirc_dwell_time_ms", "wcid_recirc_dwell_time_ms", + "dd_dwell_at_target", "wcid_dwell_at_target", + "dd_heat_disinfect_timestamp"]) + def _handler_heat_disinfect_sync(self, message, timestamp=0.0): + """ + Handles published heat disinfect data messages. + + @param message: published heat disinfect data message + @return: None + """ + msg_list = [] + msg_list.append((type(self).execution_state, DataTypes.U32)) + msg_list.append((type(self).sub_state, DataTypes.U32)) + msg_list.append((type(self).dd_recirc_dwell_time_ms, DataTypes.U32)) + msg_list.append((type(self).wcid_recirc_dwell_time_ms, DataTypes.U32)) + msg_list.append((type(self).dd_dwell_at_target, DataTypes.U32)) + msg_list.append((type(self).wcid_dwell_at_target, DataTypes.U32)) + + self.process_into_vars(decoder_list = msg_list, + message = message) + + self.dd_heat_disinfect_timestamp = timestamp + + + def cmd_heat_disinfect_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the heat disinfect data broadcast interval override command + Constraints: + Must be logged into DD. + Given interval must be non-zero and a multiple of the DD general task interval (50 ms). + + @param ms: integer - interval (in ms) to override with + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + return cmd_generic_broadcast_interval_override( + ms = ms, + reset = reset, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_HEAT_DISINFECT_MODE_DATA_PUBLISH_OVERRIDE_REQUEST, + module_name = 'DD Heat Disinfect', + logger = self.logger, + can_interface = self.can_interface) + + + def cmd_heat_disinfect_start_stop(self, start: int) -> int: + """ + Constructs and sends the heat disinfect start stop request + Constraints: + Must be logged into DD. + + @param start: int - value to start or stop heat disinfect ( start = 1, stop = 0 ) + @return: 1 if successful, zero otherwise + """ + payload = integer_to_bytearray(start) + + return cmd_generic_override( + payload = payload, + reset = NO_RESET, + channel_id = CanChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_HEAT_DISINFECT_REQUEST_DATA, + entity_name = 'Heat Disinfect Start' if start == 1 else 'Heat Disinfect Stop', + override_text = str(start), + logger = self.logger, + can_interface = self.can_interface)