Index: dialin/dg/dialysate_generator.py =================================================================== diff -u -r7b0e7a8562d035de041244e38e1811ddba69694b -r5e834984727c1798784d95ae17f686cb8d72fdca --- dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 7b0e7a8562d035de041244e38e1811ddba69694b) +++ dialin/dg/dialysate_generator.py (.../dialysate_generator.py) (revision 5e834984727c1798784d95ae17f686cb8d72fdca) @@ -25,8 +25,10 @@ DenaliMessage, DenaliChannels) +from ..utils.base import _AbstractSystem -class DG: + +class DG(_AbstractSystem): """ Dialysate Generator (DG) Dialin object API. It provides the basic interface to communicate with the DG firmware. Index: dialin/dg/load_cells.py =================================================================== diff -u -r7b0e7a8562d035de041244e38e1811ddba69694b -r5e834984727c1798784d95ae17f686cb8d72fdca --- dialin/dg/load_cells.py (.../load_cells.py) (revision 7b0e7a8562d035de041244e38e1811ddba69694b) +++ dialin/dg/load_cells.py (.../load_cells.py) (revision 5e834984727c1798784d95ae17f686cb8d72fdca) @@ -19,13 +19,14 @@ from .constants import RESET,NO_RESET from ..protocols.CAN import (DenaliMessage, DenaliChannels) +from ..utils.base import _AbstractSubSystem, _publish -class DGLoadCells: +class DGLoadCells(_AbstractSubSystem): """ - \class DGLoadCells + DGLoadCells - \brief Dialysate Generator (DG) Dialin API sub-class for load cell related commands. + Dialysate Generator (DG) Dialin API sub-class for load cell related commands. """ # Pressure/Occlusion message IDs @@ -51,10 +52,11 @@ def __init__(self, can_interface=None): """ - DGLoadCells constructor - @param outer_instance: reference to the DG (outer) class. + @param can_interface: Denali Can Messenger object """ + super().__init__(can_interface) + self.can_interface = can_interface if self.can_interface is not None: @@ -76,6 +78,7 @@ """ return [self.load_cell_A1, self.load_cell_A2, self.load_cell_B1, self.load_cell_B2] + @_publish(["load_cell_A1", "load_cell_A2", "load_cell_B1", "load_cell_B2"]) def _handler_load_cells_sync(self, message): """ Handles published load cell data messages. Load cell data are captured Index: dialin/hd/alarms.py =================================================================== diff -u -r044087c3a2f8b762452ebd58eaeb27db968b31a2 -r5e834984727c1798784d95ae17f686cb8d72fdca --- dialin/hd/alarms.py (.../alarms.py) (revision 044087c3a2f8b762452ebd58eaeb27db968b31a2) +++ dialin/hd/alarms.py (.../alarms.py) (revision 5e834984727c1798784d95ae17f686cb8d72fdca) @@ -16,12 +16,13 @@ from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.conversions import integer_to_bytearray +from ..utils.base import _AbstractSubSystem, _publish from .constants import RESET, NO_RESET from collections import OrderedDict import struct -class HDAlarms: +class HDAlarms(_AbstractSubSystem): """ HD interface containing alarm related commands. """ @@ -121,6 +122,7 @@ """ @param can_interface: Denali Can Messenger object """ + super().__init__(can_interface) self.can_interface = can_interface if self.can_interface is not None: @@ -190,6 +192,8 @@ @return: (int) how long until the alarm silence expires """ + return self.alarms_silence_expires_in + def get_alarms_escalates_in(self): """ Gets the alarms escalates in time (s) @@ -222,6 +226,7 @@ """ return self.ids + @_publish(["alarms_state", "alarm_top", "alarms_silence_expires_in", "alarms_escalates_in", "alarms_flags"]) def _handler_alarms_status_sync(self, message): """ Handles published alarms status messages. alarms status data are captured @@ -247,6 +252,7 @@ message['message'][self.START_POS_ALARMS_FLAGS:self.END_POS_ALARMS_FLAGS]), byteorder=DenaliMessage.BYTE_ORDER) + @_publish(["alarm_states"]) def _handler_alarm_activate(self, message): """ Handles published HD alarm activation messages. @@ -258,6 +264,7 @@ alarm_id = struct.unpack('> hd_object = HD(can_interface='can0') >> hd_object = HD('can0') """ + super().__init__() + # Create listener self.can_interface = DenaliCanMessenger(can_interface=can_interface, log_level=log_level) self.can_interface.start() Index: dialin/utils/base.py =================================================================== diff -u --- dialin/utils/base.py (revision 0) +++ dialin/utils/base.py (revision 5e834984727c1798784d95ae17f686cb8d72fdca) @@ -0,0 +1,95 @@ +from abc import ABC, abstractmethod +from datetime import datetime + + +class AbstractObserver(ABC): + """ + Publicly accessible parent class for all observers. + + The update method will receive data when data is made available + """ + + @abstractmethod + def update(self): + """ + Attach an observer + """ + pass + + +# abstract base class requires all abstract methods are overridden by children classes +class _AbstractSubSystem(ABC): + + @abstractmethod + def __init__(self, can_interface: str = "can0"): + """ + Initialization function for the sub system + + """ + self._observers = [] + self._datetime_fmt = "%m.%d.%Y_%I.%M.%S.%f" + pass + + def attach(self, observer: AbstractObserver): + """ + Attach an observer so it is updated upon published events + """ + self._observers.append(observer) + + def detach(self, observer: AbstractObserver): + """ + Detach an observer + """ + self._observers.remove(observer) + +def _publish(keys): + """ + Decorator that accepts a list of variable names to publish + To be used in any _AbstractSubSystem + + @param keys: The variable names to publish + @return: A function that will take a function and return another function + """ + + def _decorator(func): + """ + + @param func: The handler function + @return: The function to wrap around _publish + """ + + def _wrapper(self, *args, **kwargs): + func(self, *args, **kwargs) + result = {} + + if not self._observers: + return None + + result["datetime"] = datetime.now().strftime(self._datetime_fmt) + + for key in keys: + result[key] = getattr(self, key) + + for observer in self._observers: + observer.update(result) + + return _wrapper + + return _decorator + + +class _AbstractSystem(ABC): + """ + Parent class for all system classes + """ + + @abstractmethod + def __init__(self): + """ + Initialization function for the system + + """ + pass + + + Index: dialin/utils/fw.py =================================================================== diff -u --- dialin/utils/fw.py (revision 0) +++ dialin/utils/fw.py (revision 5e834984727c1798784d95ae17f686cb8d72fdca) @@ -0,0 +1,22 @@ +########################################################################### +# +# Copyright (c) 2019-2020 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 alarms.py +# +# @date 04-Jun-2020 +# @author P. Lucia +# +# @brief Firmware source file paths to be parsed by Dialin +# +############################################################################ +import os +current_file_dir = os.path.abspath(os.path.join(os.path.dirname(__file__))) +parent_dir = os.path.abspath(os.path.join(current_file_dir, "..")) +FW_HD_ALARM_MGMT_C = os.path.join(parent_dir, "hdfirmware/firmware/App/Services/AlarmMgmt.c") +FW_HD_ALARM_MGMT_H = os.path.join(parent_dir, "hdfirmware/firmware/App/Services/AlarmMgmt.h") +FW_COMMON_ALARM_DEFS_H = os.path.join(parent_dir, "common/AlarmDefs.h") +FW_COMMON_MSG_DEFS_H = os.path.join(parent_dir, "common/MsgDefs.h") Index: tests/test_alarms.py =================================================================== diff -u -r05176be46b1bcb2cd406be63b7888900b3086b67 -r5e834984727c1798784d95ae17f686cb8d72fdca --- tests/test_alarms.py (.../test_alarms.py) (revision 05176be46b1bcb2cd406be63b7888900b3086b67) +++ tests/test_alarms.py (.../test_alarms.py) (revision 5e834984727c1798784d95ae17f686cb8d72fdca) @@ -16,8 +16,23 @@ import sys sys.path.append("..") from dialin.hd.hemodialysis_device import HD +from dialin.utils.base import AbstractObserver import time + +class Observer(AbstractObserver): + + def __init__(self): + pass + + def update(self, result): + print(result) + + # Store the state + + def check_something(self): + pass + def test_disable_all_hd_alarms(): """ Disables all hd alarms @@ -65,6 +80,10 @@ 'ALARM_ID_VALVE_CONTROL_FAILURE' ] + alarms_observer = Observer() + + hd.alarms.attach(alarms_observer) + if hd.cmd_log_in_to_hd(): hd.ui.cmd_ui_request_hd_version() time.sleep(0.5)