Index: dialin/common/msg_defs.py =================================================================== diff -u -r817f108b234f6652fecbda170dcaae9636feec7b -ra198f8e6ce34ced17036f7558f5d9819e784dfbd --- dialin/common/msg_defs.py (.../msg_defs.py) (revision 817f108b234f6652fecbda170dcaae9636feec7b) +++ dialin/common/msg_defs.py (.../msg_defs.py) (revision a198f8e6ce34ced17036f7558f5d9819e784dfbd) @@ -23,6 +23,7 @@ MSG_DIALIN_ID_UI_SYSTEM_USAGE_REQUEST = 0x89 MSG_DIALIN_ID_HD_SYSTEM_USAGE_RESPONSE = 0x8A MSG_DIALIN_ID_DG_SYSTEM_USAGE_RESPONSE = 0x8C + MSG_DIALIN_ID_HD_VALVES_POSITION_COUNT_OVERRIDE = 0x8058 MSG_DIALIN_ID_HD_DISINFECT_STATE = 0x7E MSG_DIALIN_ID_HD_VERSION_REQUEST = 0x9E MSG_DIALIN_ID_UI_POST_REPORT_VERSION = 0x9F Index: dialin/hd/alarms.py =================================================================== diff -u -r817f108b234f6652fecbda170dcaae9636feec7b -ra198f8e6ce34ced17036f7558f5d9819e784dfbd --- dialin/hd/alarms.py (.../alarms.py) (revision 817f108b234f6652fecbda170dcaae9636feec7b) +++ dialin/hd/alarms.py (.../alarms.py) (revision a198f8e6ce34ced17036f7558f5d9819e784dfbd) @@ -17,11 +17,12 @@ from logging import Logger from .constants import RESET, NO_RESET -from ..common.msg_defs import MsgIds +from ..common.msg_defs import MsgIds, MsgFieldPositions from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish -from ..utils.conversions import integer_to_bytearray +from ..utils.conversions import integer_to_bytearray, float_to_bytearray +from ..utils.checks import check_broadcast_interval_override_ms class HDAlarms(_AbstractSubSystem): @@ -77,15 +78,16 @@ msg_id = MsgIds.MSG_ID_ALARM_TRIGGERED.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_activate) - channel_id = DenaliChannels.hd_alarm_broadcast_ch_id msg_id = MsgIds.MSG_ID_ALARM_CLEARED.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_clear) - - channel_id = DenaliChannels.hd_alarm_broadcast_ch_id msg_id = MsgIds.MSG_ID_ALARM_CONDITION_CLEARED.value self.can_interface.register_receiving_publication_function(channel_id, msg_id, self._handler_alarm_condition_clear) + channel_id = DenaliChannels.hd_sync_broadcast_ch_id + msg_id = MsgIds.MSG_ID_HD_ALARM_INFORMATION.value + self.can_interface.register_receiving_publication_function(channel_id, msg_id, + self._handler_alarm_information_sync) # composite alarm status based on latest HD alarm status broadcast message self.alarms_state = 0 @@ -99,6 +101,13 @@ # alarm condition states based on received HD alarm activation and clear condition messages self.alarm_conditions = [False] * 500 + # alarm information + self.alarm_volume = 0 + self.alarm_audio_curr_hg = 0.0 + self.alarm_audio_curr_lg = 0.0 + self.alarm_backup_audio_curr = 0.0 + self.safety_shutdown_active = False + def get_current_alarms_state(self): """ Gets the current alarms state. @@ -178,13 +187,55 @@ TBD = result & 2048 TBD = result & 4096 TBD = result & 8192 - TBD = result & 16384 + No Minimize = result & 16384 Condition Detected = result & 32768 @return: (int) The alarms flags value """ return self.alarms_flags + def get_alarm_volume(self): + """ + Gets the alarm audio volume level. + + @return: (int) current alarm audio volume (1..5) + """ + return self.alarm_volume + + def get_alarm_audio_current_hg(self): + """ + Gets the alarm audio current - high gain. + + @return: (float) alarm audio current - high gain (in mA) + """ + return self.alarm_audio_curr_hg + + + def get_alarm_audio_current_lg(self): + """ + Gets the alarm audio current - low gain. + + @return: (float) alarm audio current - low gain (in mA) + """ + return self.alarm_audio_curr_lg + + + def get_alarm_backup_audio_current(self): + """ + Gets the alarm backup audio current. + + @return: (float) alarm backup audio current (in mA) + """ + return self.alarm_backup_audio_curr + + def get_safety_shutdown_activated(self): + """ + Gets the state of the HD safety shutdown signal. + + @return: (bool) safety shutdown line is activated (T/F) + """ + return self.safety_shutdown_active + @_publish(["alarms_state", "alarm_top", "alarms_silence_expires_in", "alarms_escalates_in", "alarms_flags"]) def _handler_alarms_status_sync(self, message): """ @@ -255,6 +306,27 @@ alarm_id = struct.unpack(' int: """ Constructs and sends the clear all active alarms command. @@ -421,3 +493,198 @@ self.logger.debug("Timeout!!!!") return False + def cmd_alarm_info_broadcast_interval_override(self, ms:int=1000, reset:int=NO_RESET): + """ + Constructs and sends the alarm information broadcast interval override command + Constraints: + Must be logged into HD. + Given interval must be non-zero and a multiple of the HD 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 + """ + + if not check_broadcast_interval_override_ms(ms): + return False + + rst = integer_to_bytearray(reset) + mis = integer_to_bytearray(ms) + payload = rst + mis + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_ALARM_INFO_SEND_INTERVAL_OVERRIDE.value, + payload=payload) + + self.logger.debug("override alarm information broadcast interval") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(ms) + " ms: " + self.logger.debug("Alarm information broadcast interval overridden to " + 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_alarm_audio_volume_override(self, volume:int=5, reset:int=NO_RESET): + """ + Constructs and sends the alarm audio volume override command + Constraints: + Must be logged into HD. + Given volume must be an integer between 1 and 5. + + @param volume: integer - alarm audio volume level (1..5) + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + vol = integer_to_bytearray(volume) + payload = rst + vol + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_ALARM_AUDIO_VOLUME_LEVEL_OVERRIDE.value, + payload=payload) + + self.logger.debug("override alarm audio volume level") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(volume) + ": " + self.logger.debug("Alarm audio volume level overridden to " + 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_alarm_audio_current_hg_override(self, current:float=0.0, reset:int=NO_RESET): + """ + Constructs and sends the alarm audio current (high gain) override command + Constraints: + Must be logged into HD. + + @param current: float - current (in mA) for high gain alarm audio + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + cur = float_to_bytearray(current) + payload = rst + cur + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_ALARM_AUDIO_CURRENT_HG_OVERRIDE.value, + payload=payload) + + self.logger.debug("override alarm audio high gain current") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(current) + " mA: " + self.logger.debug("Alarm audio high gain current overridden to " + 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_alarm_audio_current_lg_override(self, current:float=0.0, reset:int=NO_RESET): + """ + Constructs and sends the alarm audio current (low gain) override command + Constraints: + Must be logged into HD. + + @param current: float - current (in mA) for low gain alarm audio + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + cur = float_to_bytearray(current) + payload = rst + cur + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_ALARM_AUDIO_CURRENT_LG_OVERRIDE.value, + payload=payload) + + self.logger.debug("override alarm audio high gain current") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(current) + " mA: " + self.logger.debug("Alarm audio low gain current overridden to " + 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_alarm_backup_audio_current_override(self, current:float=0.0, reset:int=NO_RESET): + """ + Constructs and sends the backup alarm audio current override command + Constraints: + Must be logged into HD. + + @param current: float - current (in mA) for backup alarm audio + @param reset: integer - 1 to reset a previous override, 0 to override + @return: 1 if successful, zero otherwise + """ + + rst = integer_to_bytearray(reset) + cur = float_to_bytearray(current) + payload = rst + cur + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_ALARM_BACKUP_AUDIO_CURRENT_OVERRIDE.value, + payload=payload) + + self.logger.debug("override alarm backup audio current") + + # Send message + received_message = self.can_interface.send(message) + + # If there is content... + if received_message is not None: + if reset == RESET: + str_res = "reset back to normal: " + else: + str_res = str(current) + " mA: " + self.logger.debug("Alarm backup audio current overridden to " + 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 + Index: dialin/hd/valves.py =================================================================== diff -u -r8ba3986f8fe9ef801c9f592a37f90933effbd34d -ra198f8e6ce34ced17036f7558f5d9819e784dfbd --- dialin/hd/valves.py (.../valves.py) (revision 8ba3986f8fe9ef801c9f592a37f90933effbd34d) +++ dialin/hd/valves.py (.../valves.py) (revision a198f8e6ce34ced17036f7558f5d9819e784dfbd) @@ -16,11 +16,12 @@ import struct -from ..utils.conversions import integer_to_bytearray +from ..utils.checks import check_broadcast_interval_override_ms +from ..utils.conversions import integer_to_bytearray, float_to_bytearray from .constants import NO_RESET from ..protocols.CAN import (DenaliMessage, DenaliChannels) from ..utils.base import _AbstractSubSystem, _publish, DialinEnum -from ..common import MsgIds +from ..common import MsgIds, MsgIdsDialin from logging import Logger from enum import unique @@ -35,7 +36,6 @@ @unique class ValvesPositions(DialinEnum): - VALVE_POSITION_NOT_IN_POSITION = 0 VALVE_POSITION_A_INSERT_EJECT = 1 VALVE_POSITION_B_OPEN = 2 VALVE_POSITION_C_CLOSE = 3 @@ -140,11 +140,17 @@ def cmd_hd_valves_broadcast_interval_override(self, ms: int, reset: int = NO_RESET) -> int: """ Constructs and sends broadcast time interval + Constraints: + Must be logged into HD. + Given interval must be non-zero and a multiple of the HD general task interval (50 ms). @param ms: Publish time interval in ms @param reset: integer - 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 @@ -200,8 +206,78 @@ self.logger.debug("HD cmd_valve_override Timeout!!!") return False - def cmd_set_hd_valve_pwm(self, valve: int, pwm: int, direction: int, reset: int = NO_RESET) -> int: + def cmd_set_hd_valve_current_override(self, valve:int, current:float, reset:int=NO_RESET) -> int: """ + Constructs and sends the HD valves set position for a valve + + @param valve: integer - Valve number: + VDI = 0 + VDO = 1 + VBA = 2 + VBV = 3 + @param current: float value to override current + @param reset: integer - 1 to reset a previous override, 0 to override + @returns 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + vlv = integer_to_bytearray(valve) + cur = float_to_bytearray(current) + payload = reset_value + cur + vlv + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIds.MSG_ID_HD_VALVES_CURRENT_OVERRIDE.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("Setting {} current to {:5.3f} A".format(str(ValvesEnum(valve).name), current)) + + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("HD current override Timeout!!!") + return False + + def cmd_set_hd_valve_position_count_override(self, valve:int, position_count:int, reset:int=NO_RESET) -> int: + """ + Constructs and sends the HD valves set position for a valve + + @param valve: integer - Valve number: + VDI = 0 + VDO = 1 + VBA = 2 + VBV = 3 + @param position_count: integer value + @param reset: integer - 1 to reset a previous override, 0 to override + @returns 1 if successful, zero otherwise + """ + reset_value = integer_to_bytearray(reset) + vlv = integer_to_bytearray(valve) + pos = integer_to_bytearray(position_count) + payload = reset_value + pos + vlv + + message = DenaliMessage.build_message(channel_id=DenaliChannels.dialin_to_hd_ch_id, + message_id=MsgIdsDialin.MSG_DIALIN_ID_HD_VALVES_POSITION_COUNT_OVERRIDE.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("Setting {} position to {} ".format(str(ValvesEnum(valve).name), position_count)) + + # response payload is OK or not OK + return received_message['message'][DenaliMessage.PAYLOAD_START_INDEX] + else: + self.logger.debug("HD current override Timeout!!!") + return False + + def cmd_set_hd_valve_pwm(self, valve:int, pwm:int, direction:int, reset:int=NO_RESET) -> int: + """ Constructs and sends the HD valves PWM command @param valve: integer - Valve number: @@ -271,7 +347,7 @@ @returns 1 if successful, zero otherwise """ - if valve_state == AirTrapState.STATE_OPEN: + if valve_state == AirTrapState.STATE_OPEN.value: payload = integer_to_bytearray(1) else: payload = integer_to_bytearray(0) @@ -292,7 +368,7 @@ return False @_publish(["valves_status", "hd_air_trap_status"]) - def _handler_hd_valves_sync(self, message): + def _handler_hd_valves_sync(self, message:dict) -> None: """ Handles published HD valves data messages. HD valves data are captured for reference. @@ -330,7 +406,5 @@ self.valves_status[vlv_name] = {'Valve': vlv_name, 'PosID': ValvesPositions(pos_ID).name, 'PosCnt': pos_cnt, 'Cmd': next_pos, 'State': ValvesStates(state_ID).name, 'Current': current, 'PosA': pos_a, 'PosB': pos_b, 'PosC': pos_c, 'PWM': pwm} - - if AirTrapState.has_value(air_trap): - self.hd_air_trap_status = AirTrapState(air_trap).name - + # Update the air trap valve's status + self.hd_air_trap_status = air_trap