Index: leahi_dialin/common/msg_ids.py =================================================================== diff -u -r256ffed682a9c78ee890a3f3132eb6c6518e7ee8 -r1c0302ebcb0d4d70249ab0932f43b0143e19a1d9 --- leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 256ffed682a9c78ee890a3f3132eb6c6518e7ee8) +++ leahi_dialin/common/msg_ids.py (.../msg_ids.py) (revision 1c0302ebcb0d4d70249ab0932f43b0143e19a1d9) @@ -111,6 +111,7 @@ MSG_ID_FP_GENP_DEF_DATA = 0x58 MSG_ID_FP_PRE_GEN_DEF_DATA = 0x59 MSG_ID_FP_VERSION_RESPONSE = 0x5A + MSG_ID_FP_PERMEATE_TANK_DATA = 0x5C MSG_ID_DD_PISTON_PUMP_CONTROL_DATA = 0xF0 @@ -241,6 +242,7 @@ MSG_ID_DD_STOP_PRE_GEN_DIALYSATE_MODE_OVERRIDE_REQUEST = 0xA040 MSG_ID_DD_STOP_GEN_DIALYSATE_MODE_OVERRIDE_REQUEST = 0xA041 MSG_ID_DD_SAFETY_SHUTDOWN_OVERRIDE_REQUEST = 0xA042 + MSG_ID_DD_ALARM_STATE_OVERRIDE_REQUEST = 0xA043 MSG_ID_DD_PISTON_PUMP_DATA_PUBLISH_OVERRIDE_REQUEST = 0xAF00 MSG_ID_DD_PISTON_PUMP_START_STOP_OVERRIDE_REQUEST = 0xAF01 @@ -288,6 +290,9 @@ MSG_ID_FP_BOOST_PUMP_STOP_REQUEST = 0xB027 MSG_ID_FP_RO_PUMP_STOP_REQUEST = 0xB028 MSG_ID_FP_SAFETY_SHUTDOWN_OVERRIDE_REQUEST = 0xB029 + MSG_ID_FP_PERMEATE_TANK_PUBLISH_INTERVAL_OVERRIDE_REQUEST = 0xB02A + MSG_ID_FP_ALARM_STATE_OVERRIDE_REQUEST = 0x802B + MSG_ID_FP_ALARM_CLEAR_ALL_ALARMS_REQUEST = 0x802C MSG_ID_TD_DEBUG_EVENT = 0xFFF1 MSG_ID_DD_DEBUG_EVENT = 0xFFF2 Index: leahi_dialin/dd/modules/alarms.py =================================================================== diff -u -r7b57215288f7346175091c6f69e0edddca5018b5 -r1c0302ebcb0d4d70249ab0932f43b0143e19a1d9 --- leahi_dialin/dd/modules/alarms.py (.../alarms.py) (revision 7b57215288f7346175091c6f69e0edddca5018b5) +++ leahi_dialin/dd/modules/alarms.py (.../alarms.py) (revision 1c0302ebcb0d4d70249ab0932f43b0143e19a1d9) @@ -41,7 +41,7 @@ self.logger = logger if self.can_interface is not None: - channel_id = DenaliChannels.fp_alarm_broadcast_ch_id + channel_id = DenaliChannels.dd_alarm_broadcast_ch_id msg_id = MsgIds.MSG_ID_ALARM_TRIGGERED.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_triggered) @@ -126,5 +126,45 @@ def _handler_alarm_info(self, message, timestamp = 0.0): pass + def cmd_alarm_state_override(self, alarm: int, state: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the alarm state override command + Constraints: + Must be logged into DD. + Given alarm must be valid. + If inactivating alarm, given alarm must be recoverable (clearable). + @param alarm: integer - ID of alarm to override + @param state: integer - 1 for alarm active, 0 for alarm inactive + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + sta = integer_to_bytearray(state) + alm = integer_to_bytearray(alarm) + payload = rst + sta + alm + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, + message_id=MsgIds.MSG_ID_DD_ALARM_STATE_OVERRIDE_REQUEST.value, + payload=payload) + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # self.logger.debug(received_message) + if reset == RESET: + str_res = "reset back to normal" + else: + str_res = ("active" if state != 0 else "inactive") + self.logger.debug("Alarm " + str(alarm) + " " + str_res + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + #TODO: alarm state override, alarm info pubish override Index: leahi_dialin/fp/modules/alarms.py =================================================================== diff -u -r7b57215288f7346175091c6f69e0edddca5018b5 -r1c0302ebcb0d4d70249ab0932f43b0143e19a1d9 --- leahi_dialin/fp/modules/alarms.py (.../alarms.py) (revision 7b57215288f7346175091c6f69e0edddca5018b5) +++ leahi_dialin/fp/modules/alarms.py (.../alarms.py) (revision 1c0302ebcb0d4d70249ab0932f43b0143e19a1d9) @@ -127,4 +127,72 @@ pass + def cmd_alarm_state_override(self, alarm: int, state: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the alarm state override command + Constraints: + Must be logged into FP. + Given alarm must be valid. + If inactivating alarm, given alarm must be recoverable (clearable). + + @param alarm: integer - ID of alarm to override + @param state: integer - 1 for alarm active, 0 for alarm inactive + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + sta = integer_to_bytearray(state) + alm = integer_to_bytearray(alarm) + payload = rst + sta + alm + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_fp_ch_id, + message_id=MsgIds.MSG_ID_FP_ALARM_STATE_OVERRIDE_REQUEST.value, + payload=payload) + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + # self.logger.debug(received_message) + if reset == RESET: + str_res = "reset back to normal" + else: + str_res = ("active" if state != 0 else "inactive") + self.logger.debug("Alarm " + str(alarm) + " " + str_res + ": " + + str(received_message['message'][DenaliMessage.PAYLOAD_START_INDEX])) + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("Timeout!!!!") + return False + + def cmd_clear_all_alarms(self) -> int: + """ + Constructs and sends the clear all active alarms command. + This will clear even non-recoverable alarms. + Constraints: + Must be logged into FP. + + @return: 1 if successful, zero otherwise + """ + + key = integer_to_bytearray(-758926171) # 0xD2C3B4A5 + payload = key + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_td_ch_id, + message_id=MsgIds.MSG_ID_FP_ALARM_CLEAR_ALL_ALARMS_REQUEST.value, + payload=payload) + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + self.logger.debug("All alarms cleared.") + # response payload is OK or not OK + return 1 == received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + return False + #TODO: alarm state override, alarm info pubish override Index: leahi_dialin/fp/modules/permeate_tank.py =================================================================== diff -u --- leahi_dialin/fp/modules/permeate_tank.py (revision 0) +++ leahi_dialin/fp/modules/permeate_tank.py (revision 1c0302ebcb0d4d70249ab0932f43b0143e19a1d9) @@ -0,0 +1,98 @@ +########################################################################### +# +# 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 permeate_tank.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, float_to_bytearray + +class FPPermeateTank(AbstractSubSystem): + """ + Permeate Tank + + Filtration Purification (FP) Dialin API sub-class for permeate tank 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_FP_PERMEATE_TANK_DATA.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_permeate_tank_sync) + + self.permeate_tank_state = 0 + self.permeate_tank_timestamp = 0.0 + + def _handler_permeate_tank_sync(self, message, timestamp=0.0): + """ + Handles permeate tank data messages. + + @param message: published permeate tank data message + @return: None + """ + + self.permeate_tank_state = struct.unpack('I', bytearray( + message['message'][MsgFieldPositions.START_POS_FIELD_1:MsgFieldPositions.END_POS_FIELD_1]))[0] + + self.permeate_tank_timestamp = timestamp + + def cmd_permeate_tank_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the broadcast time interval override for permeate tank data. + Constraints: + Must be logged into FP. + Given interval must be non-zero and a multiple of the FP 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_fp_ch_id, + message_id=MsgIds.MSG_ID_FP_PERMEATE_TANK_PUBLISH_INTERVAL_OVERRIDE_REQUEST.value, + payload=payload) + + self.logger.debug("Sending {} ms publish interval to the Permeate Tank 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: leahi_dialin/protocols/CAN.py =================================================================== diff -u -rf485dc0c84964939ae58374bab5b928f29689362 -r1c0302ebcb0d4d70249ab0932f43b0143e19a1d9 --- leahi_dialin/protocols/CAN.py (.../CAN.py) (revision f485dc0c84964939ae58374bab5b928f29689362) +++ leahi_dialin/protocols/CAN.py (.../CAN.py) (revision 1c0302ebcb0d4d70249ab0932f43b0143e19a1d9) @@ -480,14 +480,14 @@ self.callback_listener_complete_messages = None self.callback_listener_invalid_messages = None self.thread_pool_executor = ThreadPoolExecutor(max_workers=1) + # TODO for debugging purposes. Change to configurable setting. + #if os.path.exists('Listener_can_dump.log'): os.remove('Listener_can_dump.log') + #self.temp_logger = open('Listener_can_dump.log', 'w') + #if os.path.exists('Send_can_dump.log'): os.remove('Send_can_dump.log') + #self.temp_send_logger = open('Send_can_dump.log', 'w') + #if os.path.exists('dialin_processed_msg.log'): os.remove('dialin_processed_msg.log') + #self.temp_dialin_processed_logger = open('dialin_processed_msg.log', 'w') # TODO for debugging purposes - if os.path.exists('Listener_can_dump.log'): os.remove('Listener_can_dump.log') - self.temp_logger = open('Listener_can_dump.log', 'w') - if os.path.exists('Send_can_dump.log'): os.remove('Send_can_dump.log') - self.temp_send_logger = open('Send_can_dump.log', 'w') - if os.path.exists('dialin_processed_msg.log'): os.remove('dialin_processed_msg.log') - self.temp_dialin_processed_logger = open('dialin_processed_msg.log', 'w') - # TODO for debugging purposes # 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) Index: leahi_dialin/version.py =================================================================== diff -u -r2138d06d100fdcf23f2e9069f35ee2fdee62008f -r1c0302ebcb0d4d70249ab0932f43b0143e19a1d9 --- leahi_dialin/version.py (.../version.py) (revision 2138d06d100fdcf23f2e9069f35ee2fdee62008f) +++ leahi_dialin/version.py (.../version.py) (revision 1c0302ebcb0d4d70249ab0932f43b0143e19a1d9) @@ -14,6 +14,7 @@ # ############################################################################ import subprocess +import os.path VERSION = "0.9.0" @@ -26,7 +27,12 @@ """ try: - return subprocess.check_output("git rev-parse --abbrev-ref HEAD", shell=True).decode("utf-8").strip() + # Change the folder to where this file is for git versioning and then back to the original position + curdir = os.path.abspath(os.curdir) + os.chdir(os.path.dirname(__file__)) + res = subprocess.check_output("git rev-parse --abbrev-ref HEAD", shell=True).decode("utf-8").strip() + os.chdir(curdir) + return res except subprocess.CalledProcessError: return None @@ -38,19 +44,27 @@ @return: (str) the latest commit in the current git repository, None if it can't be determined """ try: - return subprocess.check_output("git rev-parse --short=7 HEAD", shell=True).decode("utf-8").strip() + # Change the folder to where this file is for git versioning and then back to the original position + curdir = os.path.abspath(os.curdir) + os.chdir(os.path.dirname(__file__)) + res = subprocess.check_output("git rev-parse --short=7 HEAD", shell=True).decode("utf-8").strip() + os.chdir(curdir) + return res except subprocess.CalledProcessError: return None - def check_if_git_repo(): """ Checks if we're in a git repo or not to know if we can get the git branch and commit @return: True if in a git repo, False otherwise """ - return subprocess.call(["git", "branch"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) == 0 + curdir = os.path.abspath(os.curdir) + os.chdir(os.path.dirname(__file__)) + res = subprocess.call(["git", "branch"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) == 0 + os.chdir(curdir) + return res branch = None