Index: leahi_dialin/common/msg_ids.py =================================================================== diff -u -rb0527bdf20a053b81f4c3771b2b31bb066c9a5a7 -r256ffed682a9c78ee890a3f3132eb6c6518e7ee8 --- leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision b0527bdf20a053b81f4c3771b2b31bb066c9a5a7) +++ leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) @@ -105,7 +105,7 @@ MSG_ID_DD_SERIAL_RESPONSE = 0x52 MSG_ID_TD_TEMPERATURE_DATA = 0x53 MSG_ID_TD_BATTERY_DATA = 0x54 - MSG_ID_FP_FILTER_FLUSH_DATA = 0x55 + MSG_ID_DD_UF_DATA = 0x55 MSG_ID_FP_PERMEATE_FLUSH_DATA = 0x56 MSG_ID_FP_CONCENTRATE_FLUSH_DATA = 0x57 MSG_ID_FP_GENP_DEF_DATA = 0x58 @@ -216,7 +216,7 @@ MSD_ID_DD_TD_COMMUNICATION_STATUS_OVERRIDE_REQUEST = 0xA027 MSG_ID_DD_OP_MODE_STATUS_OVERRIDE_REQUEST = 0xA028 MSG_ID_DD_SET_OPERATION_MODE_OVERRIDE_REQUEST = 0xA029 - MSG_ID_DD_START_GEN_DIALYSATE_MODE_OVERRIDE_REQUEST = 0xA02A + MSG_ID_DD_UF_DATA_PUBLISH_OVERRIDE_REQUEST = 0xA02A MSG_ID_DD_DIALYSATE_PUMPS_START_STOP_OVERRIDE_REQUEST = 0xA02B MSG_ID_DD_GEND_MODE_DATA_PUBLISH_OVERRIDE_REQUEST = 0xA02C MSG_ID_DD_CONCENTRATE_PUMPS_START_STOP_OVERRIDE_REQUEST = 0xA02D Index: leahi_dialin/dd/dialysate_delivery.py =================================================================== diff -u -rb0527bdf20a053b81f4c3771b2b31bb066c9a5a7 -r256ffed682a9c78ee890a3f3132eb6c6518e7ee8 --- leahi_dialin/dd/dialysate_delivery.py (.../dialysate_delivery.py) (revision b0527bdf20a053b81f4c3771b2b31bb066c9a5a7) +++ leahi_dialin/dd/dialysate_delivery.py (.../dialysate_delivery.py) (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) @@ -29,6 +29,7 @@ from .modules.pressure_sensors import DDPressureSensors from .modules.pre_gen_dialysate import DDPreGenDialysate from .modules.temperature_sensors import DDTemperatureSensors +from .modules.ultrafiltration import DDUltrafiltration from .modules.valves import DDValves from .proxies.ro_proxy import ROProxy @@ -119,6 +120,7 @@ self.pressure_sensors = DDPressureSensors(self.can_interface, self.logger) self.pre_gen_dialysate = DDPreGenDialysate(self.can_interface, self.logger) self.temperature_sensors = DDTemperatureSensors(self.can_interface, self.logger) + self.ultrafiltration = DDUltrafiltration(self.can_interface, self.logger) self.valves = DDValves(self.can_interface, self.logger) self.ro_proxy = ROProxy(self.can_interface, self.logger) Index: leahi_dialin/dd/modules/temperature_sensors.py =================================================================== diff -u -r7a5f836ab5effbca89452d0b07f32135f618bc64 -r256ffed682a9c78ee890a3f3132eb6c6518e7ee8 --- leahi_dialin/dd/modules/temperature_sensors.py (.../temperature_sensors.py) (revision 7a5f836ab5effbca89452d0b07f32135f618bc64) +++ leahi_dialin/dd/modules/temperature_sensors.py (.../temperature_sensors.py) (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) @@ -40,6 +40,8 @@ D75_TEMP = 10 # D75 temperature value D4_AVG_TEMP = 11 # D4 Filtered value D50_AVG_TEMP = 12 # D50 Filtered value + D28_AVG_TEMP = 13 # D28 Filtered value + D30_AVG_TEMP = 14 # D30 Filtered value class DDTemperatureSensors(AbstractSubSystem): @@ -64,7 +66,9 @@ DDTemperaturesNames.D44_TEMP.name: 0.0, DDTemperaturesNames.D75_TEMP.name: 0.0, DDTemperaturesNames.D4_AVG_TEMP.name: 0.0, - DDTemperaturesNames.D50_AVG_TEMP.name: 0.0} + DDTemperaturesNames.D50_AVG_TEMP.name: 0.0, + DDTemperaturesNames.D28_AVG_TEMP.name: 0.0, + DDTemperaturesNames.D30_AVG_TEMP.name: 0.0} if self.can_interface is not None: channel_id = DenaliChannels.dd_sync_broadcast_ch_id @@ -119,6 +123,12 @@ self.dd_temperatures[DDTemperaturesNames.D50_AVG_TEMP.name] = struct.unpack('f', bytearray( message['message'][MsgFieldPositions.START_POS_FIELD_13:MsgFieldPositions.END_POS_FIELD_13]))[0] + self.dd_temperatures[DDTemperaturesNames.D28_AVG_TEMP.name] = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_14:MsgFieldPositions.END_POS_FIELD_14]))[0] + + self.dd_temperatures[DDTemperaturesNames.D30_AVG_TEMP.name] = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_15:MsgFieldPositions.END_POS_FIELD_15]))[0] + self.dd_temperatures_timestamp = timestamp def cmd_temperatures_data_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: Index: leahi_dialin/dd/modules/ultrafiltration.py =================================================================== diff -u --- leahi_dialin/dd/modules/ultrafiltration.py (revision 0) +++ leahi_dialin/dd/modules/ultrafiltration.py (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) @@ -0,0 +1,108 @@ +########################################################################### +# +# Copyright (c) 2020-2024 Diality Inc. - All Rights Reserved. +# +# THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +# WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +# +# @file ultrafiltration.py +# +# @author (last) Micahel Garthwaite +# @date (last) 07-Mar-2023 +# @author (original) Micahel Garthwaite +# @date (original) 29-Oct-2020 +# +############################################################################ +import struct +from enum import unique +from logging import Logger + +from .constants import RESET, NO_RESET +from leahi_dialin.common.msg_defs import MsgIds, MsgFieldPositions +from leahi_dialin.protocols.CAN import DenaliMessage, DenaliChannels +from leahi_dialin.utils.base import AbstractSubSystem, publish, DialinEnum +from leahi_dialin.utils.checks import check_broadcast_interval_override_ms +from leahi_dialin.utils.conversions import integer_to_bytearray + +class DDUltrafiltration(AbstractSubSystem): + """ + Ultrafiltration + + Dialysate Delivery (DD) Dialin API sub-class for post gen dialysate related commands. + """ + + def __init__(self, can_interface, logger: Logger): + """ + + @param can_interface: Leahi Can Messenger object + """ + super().__init__() + + self.can_interface = can_interface + self.logger = logger + + if self.can_interface is not None: + channel_id = DenaliChannels.dd_sync_broadcast_ch_id + msg_id = MsgIds.MSG_ID_DD_UF_DATA.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_ultrafiltration_sync) + + self.uf_exec_state = 0 + self.uf_rate = 0.0 + self.compensated_uf_rate = 0.0 + self.is_uf_requested = 0 + self.uf_timestamp = 0.0 + + @publish(["uf_timestamp", "uf_exec_state","uf_rate","compensated_uf_rate","is_uf_requested"]) + def _handler_ultrafiltration_sync(self, message, timestamp=0.0): + """ + Handles published ultrafiltration data messages. + + @param message: published ultrafiltration data message + @return: None + """ + + self.uf_exec_state = struct.unpack('I', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + self.uf_rate = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_2:MsgFieldPositions.END_POS_FIELD_2]))[0] + self.compensated_uf_rate = struct.unpack('f', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_3:MsgFieldPositions.END_POS_FIELD_3]))[0] + self.is_uf_requested = struct.unpack('I', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_4:MsgFieldPositions.END_POS_FIELD_4]))[0] + + self.uf_timestamp = timestamp + + def cmd_uf_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the broadcast time interval override for ultrafiltration data. + 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: (int) Publish time interval in ms + @param reset: (int) 1 to reset a previous override, 0 to override + @returns 1 if successful, zero otherwise + """ + if not check_broadcast_interval_override_ms(ms): + return False + + reset_value = integer_to_bytearray(reset) + interval_value = integer_to_bytearray(ms) + payload = reset_value + interval_value + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, + message_id=MsgIds.MSG_ID_DD_UF_DATA_PUBLISH_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("Sending {} ms publish interval to the Ultrafiltration module".format(ms)) + # Send message + received_message = self.can_interface.send(message) + + # If there is content in message + if received_message is not None: + # Response payload is OK or not + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False \ No newline at end of file Index: tests/data_capture_helpers/fp_helpers.py =================================================================== diff -u -r68422d08c4141999a13496343264483a32314d37 -r256ffed682a9c78ee890a3f3132eb6c6518e7ee8 --- tests/data_capture_helpers/fp_helpers.py (.../fp_helpers.py) (revision 68422d08c4141999a13496343264483a32314d37) +++ tests/data_capture_helpers/fp_helpers.py (.../fp_helpers.py) (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) @@ -89,38 +89,30 @@ write_mutex.acquire() with open(log_path, "a") as f: # collect data and log it - temp = "{}_TEMP, ".format(FPFlowSensorNames.P7_FLOW.name) + \ - '{}'.format(fp.flows.p7_temp) + \ - ", {}_TEMP, ".format(FPFlowSensorNames.P16_FLOW.name) + \ - '{}'.format(fp.flows.p16_temp) + \ - ", {}, ".format(FPTemperaturesNames.P23_TEMP.name) + \ - '{}'.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P23_TEMP.name]) + \ - ", {}, ".format(FPTemperaturesNames.P22_TEMP.name) + \ - '{}'.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P22_TEMP.name]) + \ - ", {}, ".format(FPTemperaturesNames.P10_TEMP.name) + \ - '{}'.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P10_TEMP.name]) + \ - ", {}, ".format(FPTemperaturesNames.P19_TEMP.name) + \ - '{}'.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P19_TEMP.name]) + \ - ", {}, ".format(FPTemperaturesNames.FP_BOARD_TEMP.name) + \ - '{}'.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.FP_BOARD_TEMP.name]) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.M1_PRES.name) + \ - '{}'.format(fp.pressures.m1_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.M3_PRES.name) + \ - '{}'.format(fp.pressures.m3_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.P8_PRES.name) + \ - '{}'.format(fp.pressures.p8_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.P13_PRES.name) + \ - '{}'.format(fp.pressures.p13_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.P17_PRES.name) + \ - '{}'.format(fp.pressures.p17_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.X1_PRES.name) + \ - '{}'.format(fp.pressures.x1_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.X2_PRES.name) + \ - '{}'.format(fp.pressures.x2_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.X3_PRES.name) + \ - '{}'.format(fp.pressures.x3_pres_temp) + \ - ", {}_TEMP, ".format(FPPressureSensorNames.X4_PRES.name) + \ - '{}'.format(fp.pressures.x4_pres_temp) + \ + temp = "{}, ".format(FPTemperaturesNames.P10_TEMP.name) + \ + '{},'.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P10_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.P19_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P19_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.M1_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.M1_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.M3_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.M3_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.P8_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P8_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.P13_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P13_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.X1_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.X1_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.X2_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.X2_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.X3_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.X3_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.X4_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.X4_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.P7_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P7_TEMP.name]) + \ + "{}, ".format(FPTemperaturesNames.P16_TEMP.name) + \ + '{}, '.format(fp.temperatures.fp_temperatures[FPTemperaturesNames.P16_TEMP.name]) + \ ", {}\n".format(fp.temperatures.fp_temperature_sensors_timestamp) f.write(temp) Index: tests/fp_dianogistic_logger.py =================================================================== diff -u -rcf5e82faaf502b2828ffb6e43c2a64b71f6e31f2 -r256ffed682a9c78ee890a3f3132eb6c6518e7ee8 --- tests/fp_dianogistic_logger.py (.../fp_dianogistic_logger.py) (revision cf5e82faaf502b2828ffb6e43c2a64b71f6e31f2) +++ tests/fp_dianogistic_logger.py (.../fp_dianogistic_logger.py) (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) @@ -63,8 +63,8 @@ fp_log_setup() print ("Starting threads...") - float_control_thread = threading.Thread(target=fp_float_controller, args=(fp, CONTROL_INTERVAL),daemon=True) - float_control_thread.start() + #float_control_thread = threading.Thread(target=fp_float_controller, args=(fp, CONTROL_INTERVAL),daemon=True) + #float_control_thread.start() pressure_log_writer_thread = threading.Thread(target=fp_pressure_recorder, args=(fp, RECORDING_INTERVAL), daemon=True) temp_log_writer_thread = threading.Thread(target=fp_temperature_recorder, args=(fp, RECORDING_INTERVAL), daemon=True)