/* * ALMA - Atacama Large Millimiter Array (c) European Southern Observatory, 2010 * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package alma.acs.alarmsystemprofiler.document; import java.sql.Timestamp; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jface.viewers.TableViewer; import alma.acs.alarmsystemprofiler.engine.AlarmUtils; import alma.acs.alarmsystemprofiler.save.TableData; import alma.alarmsystem.clients.source.SourceListener; import cern.laser.source.alarmsysteminterface.FaultState; /** * The container of chattering alarms i.e. alarms whose state changes at least 3 times per minute. * <P> * Each chattering alarms is an entry of the table. If the same alarm at a certain minute chatters * more then it happened previously, its number and dates are updated to show the peak. * <P> * While counting for chattering alarms, this containers remember the last registered state because if * an alarm is terminated three times but never activated this is not a chatter. * <P> * The number of activation/termination can instead be investigated in the MFA table. * <P> * This container has one list for the ChatteringAlrms (<code>chatteringAlarms</code>) to be sent to the table model, * and another one (<code>tempAlarms</code>) where received alarms are updated every minute. * At the end of the time interval, this new list is flushed in the the list for the view. * * @author acaproni * */ public class ChatteringAlarmsContainer extends DocumentBase implements SourceListener, Runnable { /** * A chattering alarm. * <P> * {@link Comparable} orders items by number of activations. * * @author acaproni * */ public class ChatteringAlarm implements Comparable<ChatteringAlarm> { /** * The ID of the alarm */ public final String ID; /** * The number of ACTIVE alarms received in a minute */ protected int numActive=0; /** * The number of Terminate alarms received in a minute */ protected int numTerminate=0; /** * The time when the chattering event has been registered */ protected Timestamp timestamp; /** * Build a ChatteringAlarm with no alarms. * * @param id The ID of the alarm * @param time The time when the chattering has been registered */ protected ChatteringAlarm(String id, Timestamp time) { this.ID=id; numActive=numTerminate=0; timestamp=time; } /** * Constructor * * @param id The ID of the alarm * @param nAct Number of active alarms in a minute * @param nTerm Number of terminate alarms in a minute * @param time The time when the chattering has been registered */ public ChatteringAlarm(String id, int nAct, int nTerm, Timestamp time) { this.ID=id; numActive=nAct; numTerminate=nTerm; timestamp=time; } /** * Getter */ public int getNumActive() { return numActive; } /** * Getter */ public int getNumTerminate() { return numTerminate; } /** * @return The total of alarms (active/inactive) received for this alarm */ public int getTotAlarms() { return numActive+numTerminate; } /** * Update the number of this alarm but only if * the new ones are worst then previously registered. * * @param nAct Number of active alarms in a minute * @param nTerm Number of terminate alarms in a minute * @param time The time when the chattering has been registered */ public void update(int nAct, int nTerm, Timestamp time) { int tot=nAct+nTerm; if (tot>getTotAlarms()) { numActive=nAct; numTerminate=nTerm; timestamp=time; } } /** * Getter */ public Timestamp getTimestamp() { return timestamp; } @Override public int compareTo(ChatteringAlarm o) { if (o==null) { throw new NullPointerException(); } return Integer.valueOf(numActive).compareTo(o.getNumActive()); } } /** * The type of alarm recorded every minute. * the only difference between AlarmCounter and {@link ChatteringAlarm} is that we need * to remember the last activation state in this context. * * @author acaproni * */ private class AlarmCounter extends ChatteringAlarm { /** * Record the last received state for updating */ private String lastRecordedState; /** * * @param id The ID of the alarm * @param time The time when the chattering has been registered * @param activation The state ACTIVE/Terminate of the last received FaultState */ public AlarmCounter(String id, Timestamp time, String state) { super(id,time); lastRecordedState=""; updateCounter(state); } /** * Update the counters of the chattering alarms * * @param state The state ACTIVE/TERMINATE */ public void updateCounter(String state) { if (lastRecordedState.equals(state)) { return; } if (state.equals(FaultState.ACTIVE)) { numActive++; lastRecordedState=state; } else if (state.equals(FaultState.TERMINATE)) { numTerminate++; lastRecordedState=state; } else { System.out.println("Unknown state "+state); } } } /** * The chattering alarms */ private ConcurrentHashMap<String, ChatteringAlarm> chatteringAlarms=new ConcurrentHashMap<String, ChatteringAlarmsContainer.ChatteringAlarm>(); /** * The alarms received every minute */ private ConcurrentHashMap<String, AlarmCounter> tempAlarms = new ConcurrentHashMap<String, ChatteringAlarmsContainer.AlarmCounter>(); /** * The number of changes of state to accept an alarm as chattering */ private static final int STATECHANGES=3; /** * The singleton */ private static ChatteringAlarmsContainer singleton=null; public static ChatteringAlarmsContainer getInstance() { if (singleton==null) { singleton = new ChatteringAlarmsContainer(); } return singleton; } /** * Constructor */ private ChatteringAlarmsContainer() { super("Chattering alarms", new String[] { "Alarm ID", "# ACTIVE", "# TERMINATE", "# state changes", "Peak time" }); } /** * Ovveride to start the thread to refresh the vie */ @Override public void setTableViewer(TableViewer table) { super.setTableViewer(table); Thread t = new Thread(this,this.getClass().getName()); t.setDaemon(true); t.start(); } @Override public synchronized Collection<?> getNumbers() { return chatteringAlarms.values(); } @Override public void run() { while (!shutdown) { for (int count=0; count<60 && !shutdown; count++) { try { Thread.sleep(1000); } catch (InterruptedException e) { continue; } } if (shutdown) { return; } synchronized (this) { removeNoChattertingAlarms(); flush(); tempAlarms.clear(); } } } /** * Each received {@link FaultState} is stored in <code>tempAlarms</code>. */ @Override public synchronized void faultStateReceived(FaultState faultState) { String id=AlarmUtils.getID(faultState); // Alarm already in the temporary container if (tempAlarms.containsKey(id)) { AlarmCounter alarm= tempAlarms.get(id); alarm.updateCounter(faultState.getDescriptor()); return; } // Add an entry in the temporary container AlarmCounter alarm = new AlarmCounter(id, faultState.getUserTimestamp(), faultState.getDescriptor()); tempAlarms.put(id, alarm); } /** * Remove form the <code>tempAlarms</code> all the alarms that did not chatter * i.e. whose number of changes is less then <code>STATECHANGES</code>. */ private void removeNoChattertingAlarms() { Set<String> keys=tempAlarms.keySet(); for (String key: keys) { AlarmCounter alarm=tempAlarms.get(key); if (alarm.getTotAlarms()<STATECHANGES) { tempAlarms.remove(key); } } } /** * Flush the chattering alarms in the temporary list within the * list to send to the table, <code>chatteringAlarms</code>. */ private void flush() { Set<String> keys=tempAlarms.keySet(); for (String key: keys) { AlarmCounter alarm=tempAlarms.get(key); ChatteringAlarm chattering = chatteringAlarms.get(key); if (chattering==null) { // new entry chatteringAlarms.put(key, alarm); } else { // Update an existing entry chattering.update(alarm.numActive, alarm.numTerminate, alarm.timestamp); } } } @Override public void sourceXMLMsgReceived(String asiMessage) {} @Override public void setTableContent(TableData tData) { Vector<ChatteringAlarm> vals = new Vector<ChatteringAlarm>(chatteringAlarms.values()); Collections.sort(vals); for (ChatteringAlarm val: vals) { String[] row = new String[5]; row[0]="="+val.ID+"="; row[1]=Integer.valueOf(val.getNumActive()).toString(); row[2]=Integer.valueOf(val.getNumTerminate()).toString(); row[3]=Integer.valueOf(val.getTotAlarms()).toString(); row[4]=val.getTimestamp().toString(); tData.addRowData(row); } } }