All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yamcs.alarms.AlarmServer Maven / Gradle / Ivy

There is a newer version: 5.10.9
Show newest version
package org.yamcs.alarms;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.ProcessorConfig;
import org.yamcs.mdb.ParameterAlarmChecker;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.protobuf.Event.EventSeverity;
import org.yamcs.protobuf.Pvalue.MonitoringResult;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.xtce.Parameter;
import org.yamcs.yarch.protobuf.Db.Event;

/**
 * Maintains a list of active alarms.
 * 

* (S,T) can be one of: (Parameter,ParamerValue) or (EventId, Event) *

* This class implements functionality common for parameter alarms and event alarms. *

* Specific functionality for each alarm type (e.g. disabling alarms) should be implemented in the respective * {@link ParameterAlarmChecker} or {@link EventAlarmServer}. * */ public abstract class AlarmServer extends AbstractAlarmServer { static private final Logger log = LoggerFactory.getLogger(AlarmServer.class); final private ScheduledThreadPoolExecutor timer; public AlarmServer(String yamcsInstance, ProcessorConfig procConfig, ScheduledThreadPoolExecutor timer) { super(yamcsInstance); this.timer = timer; if (procConfig.getAlarmLoadDays() > 0) { loadAlarmsFromDb(procConfig.getAlarmLoadDays(), activeAlarms); } } /** * Returns the active alarm for the specified {@code subject} if it also matches the specified {@code id}. * * @param subject * the subject to look for. * @param id * the expected id of the active alarm. * @return the active alarm, or {@code null} if no alarm was found * @throws AlarmSequenceException * when the specified id does not match the id of the active alarm */ public ActiveAlarm getActiveAlarm(S subject, int id) throws AlarmSequenceException { var lock = getLock(subject); synchronized (lock) { ActiveAlarm alarm = activeAlarms.get(subject); if (alarm != null) { if (alarm.getId() != id) { throw new AlarmSequenceException(alarm.getId(), id); } return alarm; } return null; } } /** * Returns the active alarm for the specified {@code subject}. * * @param subject * the subject to look for. * @return the active alarm, or {@code null} if no alarm was found */ public ActiveAlarm getActiveAlarm(S subject) { return activeAlarms.get(subject); } /** * Acknowledges an active alarm instance. If the alarm state is no longer applicable, the alarm is also cleared, * otherwise the alarm will remain active. * * @param alarm * the alarm to acknowledge * @param username * the acknowledging user * @param ackTime * the time associated with the acknowledgment * @param message * reason message. Leave null when no reason is given. * @return the updated alarm instance or null if the alarm was not found */ public ActiveAlarm acknowledge(ActiveAlarm alarm, String username, long ackTime, String message) { var subject = getSubject(alarm.getTriggerValue()); var lock = getLock(subject); synchronized (lock) { var alarm1 = activeAlarms.get(subject); if (alarm1 != alarm) { return null; } alarm.acknowledge(username, ackTime, message); notifyUpdate(AlarmNotificationType.ACKNOWLEDGED, alarm); if (alarm.isNormal()) { activeAlarms.remove(subject); notifyUpdate(AlarmNotificationType.CLEARED, alarm); } return alarm; } } /** * Reset a latched alarm * * @param alarm * @param username * @param resetTime * @param message * @return the updated alarm instance or null if the alarm was not found */ public ActiveAlarm reset(ActiveAlarm alarm, String username, long resetTime, String message) { S subject = getSubject(alarm.getTriggerValue()); var lock = getLock(subject); synchronized (lock) { var alarm1 = activeAlarms.get(subject); if (alarm1 != alarm) { return null; } alarm.reset(username, resetTime, message); return alarm; } } /** * Clears an active alarm instance. * * @param alarm * the alarm to clear * @param username * the user that cleared the alarm * @param message * reason message. Leave null when no reason is given. * @return the updated alarm instance or null if the alarm was not found */ public ActiveAlarm clear(ActiveAlarm alarm, String username, long clearTime, String message) { S subject = getSubject(alarm.getTriggerValue()); var lock = getLock(subject); synchronized (lock) { if (!activeAlarms.remove(subject, alarm)) { return null; } alarm.clear(username, clearTime, message); notifyUpdate(AlarmNotificationType.CLEARED, alarm); return alarm; } } /** * Shelve an alarm * * @param alarm * @param username * @param message * @param shelveDuration * shelve duration in milliseconds * * @return the updated alarm instance or null if the alarm was not found */ public ActiveAlarm shelve(ActiveAlarm alarm, String username, String message, long shelveDuration) { S subject = getSubject(alarm.getTriggerValue()); var lock = getLock(subject); synchronized (lock) { var alarm1 = activeAlarms.get(subject); if (alarm1 != alarm) { return null; } alarm.shelve(username, message, shelveDuration); notifyUpdate(AlarmNotificationType.SHELVED, alarm); timer.schedule(this::checkShelved, shelveDuration, TimeUnit.MILLISECONDS); return alarm; } } private void checkShelved() { long t = TimeEncoding.getWallclockTime(); for (ActiveAlarm alarm : activeAlarms.values()) { if (alarm.isShelved()) { long exp = alarm.getShelveExpiration(); if (exp == -1) { continue; } if (exp <= t) { alarm.unshelve(); notifyUpdate(AlarmNotificationType.UNSHELVED, alarm); } } } } /** * Un-shelve an alarm * * @param alarm * @param username * @return the updated alarm instance or null if the alarm was not found */ public ActiveAlarm unshelve(ActiveAlarm alarm, String username) { S subject = getSubject(alarm.getTriggerValue()); var lock = getLock(subject); synchronized (lock) { var alarm1 = activeAlarms.get(subject); if (alarm1 != alarm) { return null; } alarm.unshelve(); notifyUpdate(AlarmNotificationType.UNSHELVED, alarm); return alarm; } } public void update(T pv, int minViolations) { update(pv, minViolations, false, false); } public void update(T value, int minViolations, boolean autoAck, boolean latching) { S subject = getSubject(value); var lock = getLock(subject); boolean noAlarm = isOkNoAlarm(value); if (noAlarm && !activeAlarms.containsKey(subject)) { // fast return path for parameters in limits and not in an alarm state from a previous value // no locking necessary return; } synchronized (lock) { ActiveAlarm activeAlarm = activeAlarms.get(subject); if (noAlarm) { if (activeAlarm == null) { // this check was already performed above but another thread may have removed the // alarm before the lock acquisition return; } if (activeAlarm.isPending()) { log.debug("Clearing glitch for {}", getName(subject)); activeAlarms.remove(subject); notifyUpdate(AlarmNotificationType.CLEARED, activeAlarm); return; } boolean updated = activeAlarm.processRTN(timeService.getMissionTime()); activeAlarm.setCurrentValue(value); activeAlarm.incrementValueCount(); notifyValueUpdate(activeAlarm); if (updated) { notifyUpdate(AlarmNotificationType.RTN, activeAlarm); if (activeAlarm.isNormal()) { activeAlarms.remove(subject); notifyUpdate(AlarmNotificationType.CLEARED, activeAlarm); } } } else { // alarm if (activeAlarm == null) { activeAlarm = new ActiveAlarm<>(value, autoAck, latching); activeAlarms.put(subject, activeAlarm); if (activeAlarm.getViolations() >= minViolations) { activeAlarm.trigger(); notifyUpdate(AlarmNotificationType.TRIGGERED, activeAlarm); } else { notifyUpdate(AlarmNotificationType.TRIGGERED_PENDING, activeAlarm); } } else { activeAlarm.setCurrentValue(value); activeAlarm.incrementViolations(); activeAlarm.incrementValueCount(); boolean notifySimpleUpdate = true; if (activeAlarm.isPending() && activeAlarm.getViolations() >= minViolations) { activeAlarm.trigger(); notifyUpdate(AlarmNotificationType.TRIGGERED, activeAlarm); notifySimpleUpdate = false; } if (moreSevere(value, activeAlarm.getMostSevereValue())) { activeAlarm.setMostSevereValue(value); notifySeverityIncrease(activeAlarm); notifySimpleUpdate = false; } if (notifySimpleUpdate) { notifyValueUpdate(activeAlarm); } } } } } static private String getName(Object subject) { if (subject instanceof Parameter) { return ((Parameter) subject).getQualifiedName(); } else if (subject instanceof EventId) { EventId eid = (EventId) subject; return eid.source + "." + eid.type; } else { throw new IllegalStateException(); } } static private boolean isOkNoAlarm(Object value) { if (value instanceof ParameterValue) { ParameterValue pv = (ParameterValue) value; return (pv.getMonitoringResult() == null || pv.getMonitoringResult() == MonitoringResult.IN_LIMITS || pv.getMonitoringResult() == MonitoringResult.DISABLED); } else if (value instanceof Event) { Event ev = (Event) value; return ev.getSeverity() == null || ev.getSeverity() == EventSeverity.INFO; } else { throw new IllegalStateException("Unknown value " + value.getClass()); } } protected static boolean moreSevere(Object newValue, Object oldValue) { if (newValue instanceof ParameterValue) { return moreSevere(((ParameterValue) newValue).getMonitoringResult(), ((ParameterValue) oldValue).getMonitoringResult()); } else if (newValue instanceof Event) { return moreSevere(((Event) newValue).getSeverity(), ((Event) oldValue).getSeverity()); } else { throw new IllegalStateException(); } } protected static boolean moreSevere(MonitoringResult mr1, MonitoringResult mr2) { return mr1.getNumber() > mr2.getNumber(); } protected static boolean moreSevere(EventSeverity es1, EventSeverity es2) { return es1.getNumber() > es2.getNumber(); } @Override public void doStart() { timer.execute(this::checkShelved); notifyStarted(); } @Override public void doStop() { // run the notifyShutdown in order to save the latest alarm information to the database for (var alarm : activeAlarms.values()) { alarmListeners.forEach(l -> l.notifyShutdown(alarm)); } notifyStopped(); } /** * Removes all active alarms without acknowledgement !use only for unit tests! */ public void clearAll() { activeAlarms.clear(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy