Index: leahi_dialin/dd/modules/valves.py =================================================================== diff -u -rb58eb26e4481c8c2ae3a7978d3564eaaee037ecd -rf66c019100e98b49cd94fa0a69951a1a7c952da0 --- leahi_dialin/dd/modules/valves.py (.../valves.py) (revision b58eb26e4481c8c2ae3a7978d3564eaaee037ecd) +++ leahi_dialin/dd/modules/valves.py (.../valves.py) (revision f66c019100e98b49cd94fa0a69951a1a7c952da0) @@ -7,24 +7,24 @@ # # @file valves.py # -# @author (last) Micahel Garthwaite -# @date (last) 17-Aug-2023 +# @author (last) Zoltan Miskolci +# @date (last) 07-Jan-2026 # @author (original) Peman Montazemi # @date (original) 19-May-2020 # ############################################################################ import struct -from enum import unique from logging import Logger from collections import OrderedDict -from .constants import NO_RESET +from leahi_dialin.common.constants import NO_RESET +from leahi_dialin.common.dd_defs import dd_enum_repository from leahi_dialin.common.msg_defs import MsgIds -from leahi_dialin.common.dd_defs import DDValveStates, DDValveNames +from leahi_dialin.common.override_templates import cmd_generic_broadcast_interval_override, cmd_generic_override 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.base import AbstractSubSystem, publish +from leahi_dialin.utils.base import AbstractSubSystem, publish from leahi_dialin.utils.conversions import integer_to_bytearray # Valve states @@ -49,10 +49,8 @@ def __init__(self, can_interface, logger: Logger): """ - @param can_interface: Denali CAN Messenger object """ - super().__init__() self.can_interface = can_interface self.logger = logger @@ -65,48 +63,50 @@ self.can_interface.register_receiving_publication_function(channel_id, self.msg_id_dd_valves_states_data, self._handler_valves_sync) self.valve_states_all = 0x00000000 - self.d14_valv = {"id": DDValveNames.D14_VALV.value, "state": DEENERGIZED} - self.d52_valv = {"id": DDValveNames.D52_VALV.value, "state": DEENERGIZED} - self.d8_valv = {"id": DDValveNames.D8_VALV.value, "state": DEENERGIZED} - self.d54_valv = {"id": DDValveNames.D54_VALV.value, "state": DEENERGIZED} - self.d53_valv = {"id": DDValveNames.D53_VALV.value, "state": DEENERGIZED} - self.d34_valv = {"id": DDValveNames.D34_VALV.value, "state": DEENERGIZED} - self.d64_valv = {"id": DDValveNames.D64_VALV.value, "state": DEENERGIZED} - self.d31_valv = {"id": DDValveNames.D31_VALV.value, "state": DEENERGIZED} - self.d65_valv = {"id": DDValveNames.D65_VALV.value, "state": DEENERGIZED} - self.d35_valv = {"id": DDValveNames.D35_VALV.value, "state": DEENERGIZED} - self.d40_valv = {"id": DDValveNames.D40_VALV.value, "state": DEENERGIZED} - self.d47_valv = {"id": DDValveNames.D47_VALV.value, "state": DEENERGIZED} - self.d3_valv = {"id": DDValveNames.D3_VALV.value, "state": DEENERGIZED} - self.d80_valv = {"id": DDValveNames.D80_VALV.value, "state": DEENERGIZED} - self.d81_valv = {"id": DDValveNames.D81_VALV.value, "state": DEENERGIZED} - self.d85_valv = {"id": DDValveNames.D85_VALV.value, "state": DEENERGIZED} - self.d23_valv = {"id": DDValveNames.D23_VALV.value, "state": DEENERGIZED} - self.d19_valv = {"id": DDValveNames.D19_VALV.value, "state": DEENERGIZED} - self.d25_valv = {"id": DDValveNames.D25_VALV.value, "state": DEENERGIZED} - self.d21_valv = {"id": DDValveNames.D26_VALV.value, "state": DEENERGIZED} - self.d24_valv = {"id": DDValveNames.D24_VALV.value, "state": DEENERGIZED} - self.d20_valv = {"id": DDValveNames.D20_VALV.value, "state": DEENERGIZED} - self.d26_valv = {"id": DDValveNames.D21_VALV.value, "state": DEENERGIZED} - self.d22_valv = {"id": DDValveNames.D22_VALV.value, "state": DEENERGIZED} + self.d14_valv = {"id": dd_enum_repository.DDValveNames.D14_VALV.value, "state": DEENERGIZED} + self.d52_valv = {"id": dd_enum_repository.DDValveNames.D52_VALV.value, "state": DEENERGIZED} + self.d8_valv = {"id": dd_enum_repository.DDValveNames.D8_VALV.value, "state": DEENERGIZED} + self.d54_valv = {"id": dd_enum_repository.DDValveNames.D54_VALV.value, "state": DEENERGIZED} + self.d53_valv = {"id": dd_enum_repository.DDValveNames.D53_VALV.value, "state": DEENERGIZED} + self.d34_valv = {"id": dd_enum_repository.DDValveNames.D34_VALV.value, "state": DEENERGIZED} + self.d64_valv = {"id": dd_enum_repository.DDValveNames.D64_VALV.value, "state": DEENERGIZED} + self.d31_valv = {"id": dd_enum_repository.DDValveNames.D31_VALV.value, "state": DEENERGIZED} + self.d65_valv = {"id": dd_enum_repository.DDValveNames.D65_VALV.value, "state": DEENERGIZED} + self.d35_valv = {"id": dd_enum_repository.DDValveNames.D35_VALV.value, "state": DEENERGIZED} + self.d40_valv = {"id": dd_enum_repository.DDValveNames.D40_VALV.value, "state": DEENERGIZED} + self.d47_valv = {"id": dd_enum_repository.DDValveNames.D47_VALV.value, "state": DEENERGIZED} + self.d3_valv = {"id": dd_enum_repository.DDValveNames.D3_VALV.value, "state": DEENERGIZED} + self.d80_valv = {"id": dd_enum_repository.DDValveNames.D80_VALV.value, "state": DEENERGIZED} + self.d81_valv = {"id": dd_enum_repository.DDValveNames.D81_VALV.value, "state": DEENERGIZED} + self.d85_valv = {"id": dd_enum_repository.DDValveNames.D85_VALV.value, "state": DEENERGIZED} - self.d79_pmp_valv = {"id": DDValveNames.D79_PMP_VALV.value, "state": DEENERGIZED} - self.spare1_valv = {"id": DDValveNames.SPARE1_VALV.value, "state": DEENERGIZED} - self.spare2_valv = {"id": DDValveNames.SPARE2_VALV.value, "state": DEENERGIZED} - self.spare3_valv = {"id": DDValveNames.SPARE3_VALV.value, "state": DEENERGIZED} - self.spare4_valv = {"id": DDValveNames.SPARE4_VALV.value, "state": DEENERGIZED} - self.spare5_valv = {"id": DDValveNames.SPARE5_VALV.value, "state": DEENERGIZED} - self.spare6_valv = {"id": DDValveNames.SPARE6_VALV.value, "state": DEENERGIZED} - self.spare7_valv = {"id": DDValveNames.SPARE7_VALV.value, "state": DEENERGIZED} + self.d23_valv = {"id": dd_enum_repository.DDValveNames.D23_VALV.value, "state": DEENERGIZED} + self.d19_valv = {"id": dd_enum_repository.DDValveNames.D19_VALV.value, "state": DEENERGIZED} + self.d25_valv = {"id": dd_enum_repository.DDValveNames.D25_VALV.value, "state": DEENERGIZED} + self.d21_valv = {"id": dd_enum_repository.DDValveNames.D26_VALV.value, "state": DEENERGIZED} + self.d24_valv = {"id": dd_enum_repository.DDValveNames.D24_VALV.value, "state": DEENERGIZED} + self.d20_valv = {"id": dd_enum_repository.DDValveNames.D20_VALV.value, "state": DEENERGIZED} + self.d26_valv = {"id": dd_enum_repository.DDValveNames.D21_VALV.value, "state": DEENERGIZED} + self.d22_valv = {"id": dd_enum_repository.DDValveNames.D22_VALV.value, "state": DEENERGIZED} + self.d79_pmp_valv = {"id": dd_enum_repository.DDValveNames.D79_PMP_VALV.value, "state": DEENERGIZED} + self.d83_valv = {"id": dd_enum_repository.DDValveNames.D83_VALV.value, "state": DEENERGIZED} + self.d91_valv = {"id": dd_enum_repository.DDValveNames.D91_VALV.value, "state": DEENERGIZED} + self.spare3_valv = {"id": dd_enum_repository.DDValveNames.SPARE3_VALV.value, "state": DEENERGIZED} + self.spare4_valv = {"id": dd_enum_repository.DDValveNames.SPARE4_VALV.value, "state": DEENERGIZED} + self.spare5_valv = {"id": dd_enum_repository.DDValveNames.SPARE5_VALV.value, "state": DEENERGIZED} + self.d100_valv = {"id": dd_enum_repository.DDValveNames.D100_VALV.value, "state": DEENERGIZED} + self.spare7_valv = {"id": dd_enum_repository.DDValveNames.SPARE7_VALV.value, "state": DEENERGIZED} + # NOTE: The len function counts the enums with the same number only once. This is not the case in the DD valves # class because each valve must have a unique ID. - self.valve_states_enum = [0 for _ in range(len(DDValveNames))] + self.valve_states_enum = [0 for _ in range(len(dd_enum_repository.DDValveNames))] - for valve in DDValveNames.__members__: + for valve in dd_enum_repository.DDValveNames.__members__: self.valves_sensed_states[valve] = '' + def get_valve_states(self): """ Gets the valve states @@ -141,15 +141,16 @@ self.d22_valv.get("state", None), self.d79_pmp_valv.get("state", None), - self.spare1_valv.get("state", None), - self.spare2_valv.get("state", None), + self.d83_valv.get("state", None), + self.d91_valv.get("state", None), self.spare3_valv.get("state", None), self.spare4_valv.get("state", None), self.spare5_valv.get("state", None), - self.spare6_valv.get("state", None), + self.d100_valv.get("state", None), self.spare7_valv.get("state", None) ] + @staticmethod def sort_by_id(observation): """ @@ -159,7 +160,6 @@ @param observation: dictionary of the observed valve states @return: a list of tuples of the valve states """ - result = [] for key, value in observation.items(): if isinstance(value, dict): @@ -168,18 +168,19 @@ result = sorted(result, key=lambda each: each[1]) return result + @staticmethod def _binary_to_valve_state(binary) -> bool: """ @param binary: binary value @return: 1 = energized, otherwise de-energized """ - if binary != 0: return ENERGIZED else: return DEENERGIZED + @publish(["msg_id_dd_valves_states_data", "valve_states_all", "d14_valv", @@ -207,12 +208,12 @@ "d26_valv", "d22_valv", "d79_pmp_valv", - "spare1_valv", - "spare2_valv", + "D83_valv", + "D91_valv", "spare3_valv", "spare4_valv", "spare5_valv", - "spare6_valv", + "D100_valv", "spare7_valv", "valve_states_enum", "valves_sensed_states", @@ -228,7 +229,6 @@ vsa = struct.unpack('I', bytearray(message['message'][self.START_POS_VALVES_STATES:self.END_POS_ALL_VALVES])) self.valve_states_all = vsa[0] - vst = struct.unpack('H', bytearray(message['message'][self.START_POS_VALVES_STATES:self.END_POS_VALVES_STATES])) # Extract each valve state from U16 valves states using bit-masking self.d14_valv["state"] = self._binary_to_valve_state(vst[0] & 1) @@ -259,25 +259,47 @@ self.d22_valv["state"] = self._binary_to_valve_state(bcv[0] & 128) spv = struct.unpack('B', bytearray(message['message'][self.START_POS_SPARE_VALVES_STATES:self.END_POS_SPARE_VALVES_STATES])) - self.d79_pmp_valv["state"] = self._binary_to_valve_state(bcv[0] & 1) - self.spare1_valv["state"] = self._binary_to_valve_state(bcv[0] & 2) - self.spare2_valv["state"] = self._binary_to_valve_state(bcv[0] & 4) - self.spare3_valv["state"] = self._binary_to_valve_state(bcv[0] & 8) - self.spare4_valv["state"] = self._binary_to_valve_state(bcv[0] & 16) - self.spare5_valv["state"] = self._binary_to_valve_state(bcv[0] & 32) - self.spare6_valv["state"] = self._binary_to_valve_state(bcv[0] & 64) - self.spare7_valv["state"] = self._binary_to_valve_state(bcv[0] & 128) + self.d79_pmp_valv["state"] = self._binary_to_valve_state(spv[0] & 1) + self.d83_valv["state"] = self._binary_to_valve_state(spv[0] & 2) + self.d91_valv["state"] = self._binary_to_valve_state(spv[0] & 4) + self.spare3_valv["state"] = self._binary_to_valve_state(spv[0] & 8) + self.spare4_valv["state"] = self._binary_to_valve_state(spv[0] & 16) + self.spare5_valv["state"] = self._binary_to_valve_state(spv[0] & 32) + self.d100_valv["state"] = self._binary_to_valve_state(spv[0] & 64) + self.spare7_valv["state"] = self._binary_to_valve_state(spv[0] & 128) start = self.END_POS_SPARE_VALVES_STATES end = start + 1 for valve_id in self.valves_sensed_states: valve_state_number = struct.unpack('B', bytearray(message['message'][start:end]))[0] - self.valves_sensed_states[valve_id] = DDValveNames(valve_state_number).name + self.valves_sensed_states[valve_id] = dd_enum_repository.DDValveNames(valve_state_number).name start = end end += 1 self.dd_valves_states_timestamp = timestamp + + def cmd_valve_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: + """ + Constructs and sends the valve state 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: unsigned int - broadcast interval (in ms) + @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 = DenaliChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_VALVE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, + module_name = 'DD Valves', + logger = self.logger, + can_interface = self.can_interface) + + def cmd_valve_sensed_state_override(self, valve: int, state: bool, reset: int = NO_RESET) -> int: """ Constructs and sends the valve sensed state override command. @@ -290,29 +312,23 @@ @param reset: integer - 1 to reset a previous override, 0 to override @return: 1 if successful, zero otherwise """ - rst = integer_to_bytearray(reset) ste = integer_to_bytearray(int(state)) vlv = integer_to_bytearray(valve) payload = rst + ste + vlv - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, - message_id=MsgIds.MSG_ID_DD_VALVE_SENSED_STATE_OVERRIDE_REQUEST.value, - payload=payload) + valve_name = dd_enum_repository.DDValveNames(valve).name.split('_')[0] + return cmd_generic_override( + payload = payload, + reset = reset, + channel_id = DenaliChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_VALVE_SENSED_STATE_OVERRIDE_REQUEST, + entity_name = f'DD {valve_name} Valve sensed state', + override_text = str(state), + logger = self.logger, + can_interface = self.can_interface) - self.logger.debug("Override valve sensed state") - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - # response payload is OK or not OK - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] - else: - self.logger.debug("Timeout!!!!") - return False - def cmd_valve_override(self, valve: int, state: bool, reset: int = NO_RESET) -> int: """ Constructs and sends the valve state override command. @@ -325,29 +341,23 @@ @param reset: integer - 1 to reset a previous override, 0 to override @return: 1 if successful, zero otherwise """ - rst = integer_to_bytearray(reset) ste = integer_to_bytearray(int(state)) vlv = integer_to_bytearray(valve) payload = rst + ste + vlv - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, - message_id=MsgIds.MSG_ID_DD_VALVE_STATE_OVERRIDE_REQUEST.value, - payload=payload) + valve_name = dd_enum_repository.DDValveNames(valve).name.split('_')[0] + return cmd_generic_override( + payload = payload, + reset = reset, + channel_id = DenaliChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_VALVE_STATE_OVERRIDE_REQUEST, + entity_name = f'DD {valve_name} Valve state', + override_text = str(state), + logger = self.logger, + can_interface = self.can_interface) - self.logger.debug("Override valve state") - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - # response payload is OK or not OK - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] - else: - self.logger.debug("Timeout!!!!") - return False - def cmd_bc_valves_override(self, bcv1: bool, bcv2: bool, bcv3: bool, bcv4: bool, bcv5: bool, bcv6: bool, bcv7: bool, bcv8: bool) -> int: """ Constructs and sends the balancing chamber valve states override command. @@ -365,7 +375,6 @@ @param bcv8: bool - valve state for D22_VALV (true=open, false=closed) @return: 1 if successful, zero otherwise """ - valve = 0 if bcv1: valve = valve | 0x1 @@ -386,61 +395,17 @@ payload = integer_to_bytearray(valve) - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, - message_id=MsgIds.MSG_ID_DD_BC_VALVE_STATES_OVERRIDE_REQUEST.value, - payload=payload) + return cmd_generic_override( + payload = payload, + reset = NO_RESET, + channel_id = DenaliChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_BC_VALVE_STATES_OVERRIDE_REQUEST, + entity_name = f'DD Balancing Chamber Valves state', + override_text = '', + logger = self.logger, + can_interface = self.can_interface) - self.logger.debug("Override balancing chamber valve states") - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - # response payload is OK or not OK - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] - else: - self.logger.debug("Timeout!!!!") - return False - - def cmd_valve_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: - """ - Constructs and sends the valve state 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: unsigned int - broadcast interval (in ms) - @param reset: integer - 1 to reset a previous override, 0 to override - @return: 1 if successful, zero otherwise - - """ - - if not check_broadcast_interval_override_ms(ms): - return False - - rst = integer_to_bytearray(reset) - ivl = integer_to_bytearray(ms) - payload = rst + ivl - - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, - message_id=MsgIds.MSG_ID_DD_VALVE_PUBLISH_INTERVAL_OVERRIDE_REQUEST.value, - payload=payload) - - self.logger.debug("override valves states publish interval") - - # 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 - - def cmd_valve_set_open_close(self, valve: int, state: int) -> int: """ Constructs and sends the valve open close command @@ -450,26 +415,18 @@ @param valve: unsigned int - valve ID @param state: int - value to command the valve @return: 1 if successful, zero otherwise - """ - vlv = integer_to_bytearray(valve) sts = integer_to_bytearray(state) payload = vlv + sts - message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_dd_ch_id, - message_id=MsgIds.MSG_ID_DD_VALVES_OPEN_CLOSE_STATE_OVERRIDE_REQUEST.value, - payload=payload) - - self.logger.debug("setting " + str(state) + " - for valve: " + str(valve)) - - # Send message - received_message = self.can_interface.send(message) - - # If there is content... - if received_message is not None: - # response payload is OK or not OK - return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] - else: - self.logger.error("Timeout!!!!") - return False \ No newline at end of file + valve_name = dd_enum_repository.DDValveNames(valve).name.split('_')[0] + return cmd_generic_override( + payload = payload, + reset = NO_RESET, + channel_id = DenaliChannels.dialin_to_dd_ch_id, + msg_id = MsgIds.MSG_ID_DD_VALVES_OPEN_CLOSE_STATE_OVERRIDE_REQUEST, + entity_name = f'DD {valve_name} Valve state', + override_text = str(state), + logger = self.logger, + can_interface = self.can_interface)