/*
* ALMA - Atacama Large Millimiter Array (c) European Southern Observatory, 2007
*
* 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.acsplugins.alarmsystem.gui.undocumented.table;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import javax.swing.table.AbstractTableModel;
import alma.acs.gui.util.threadsupport.EDTExecutor;
import alma.acs.util.IsoDateFormat;
import alma.acsplugins.alarmsystem.gui.CernAlSysTabbedPane;
import cern.laser.client.data.Alarm;
public class UndocAlarmTableModel extends AbstractTableModel {
/**
* The titles of the columns
*
* @author acaproni
*
*/
public enum ColumnTitles {
TIME("Time",String.class),
COMPONENT("Component",String.class),
FAMILY("Family",String.class),
CODE("Code",Integer.class);
/**
* The title of the column as it appears in the table header
*/
public final String title;
/**
* The class of each column
*/
public final Class theClass;
/**
* Constructor
*
* @param title The title of the column
*/
private ColumnTitles(String title, Class theClass) {
this.title=title;
this.theClass=theClass;
}
}
/**
* No point to store a whole {@link Alarm} data as in
* this case we need only a few items.
*
* @author acaproni
*
*/
public class AlarmData implements Comparable<AlarmData >{
public final String component;
public final String family;
public final int code;
public final Timestamp timestamp;
public final boolean active;
/**
* Constructor
*
* @param FF FaultFamily
* @param FM FaultMember
* @param FC FaultCode
* @param timestamp Timestamp
* @param active <code>true</code> if the alarm is active
*/
public AlarmData(String FF, String FM, int FC, Timestamp timestamp,boolean active) {
if (FF==null || FF.isEmpty()) {
throw new IllegalArgumentException("Invalid FF");
}
if (FM==null || FM.isEmpty()) {
throw new IllegalArgumentException("Invalid FM");
}
if (timestamp==null ) {
throw new IllegalArgumentException("Invalid timestamp");
}
this.family=FF;
this.component=FM;
this.code=FC;
this.timestamp=timestamp;
this.active = active;
}
@Override
public int compareTo(AlarmData o) {
String alarmID=family+":"+component+":"+code;
String otherAlarmID=o.family+":"+o.component+":"+o.code;
return alarmID.compareTo(otherAlarmID);
}
@Override
public int hashCode() {
String alarmID=family+":"+component+":"+code;
return alarmID.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj==null) {
return false;
}
if (!(obj instanceof AlarmData)) {
return false;
}
String alarmID=family+":"+component+":"+code;
AlarmData o =(AlarmData)obj;
String otherAlarmID=o.family+":"+o.component+":"+o.code;
return alarmID.equals(otherAlarmID);
}
}
private static final int QUEUE_SIZE= 30000;
/**
* The date format
*/
private final SimpleDateFormat dateFormat = new IsoDateFormat();
/**
* The queue of alarms received from the <code>CategoryClient</code> that will be
* injected in the table
*/
private final List<AlarmData> items = Collections.synchronizedList(new Vector<AlarmData>());
/**
* The tabbed pane to hide show this tab depending if there are undocumented
* alarms or not.
*/
private final CernAlSysTabbedPane tabbedPane;
/**
* The number of active alarms in the table
*/
private volatile int numOfActiveAlarms=0;
/**
* Constructor
*
* @param tabbedPane The tabbed pane to hide/show the undocumented alarm tab
*/
public UndocAlarmTableModel(CernAlSysTabbedPane tabbedPane) {
if (tabbedPane==null) {
throw new IllegalArgumentException("The tabbed pane can't be null");
}
this.tabbedPane=tabbedPane;
}
@Override
public int getRowCount() {
return items.size();
}
@Override
public int getColumnCount() {
return ColumnTitles.values().length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
AlarmData data = getRowEntry(rowIndex);
ColumnTitles col = ColumnTitles.values()[columnIndex];
switch (col) {
case TIME: synchronized (dateFormat) {
return dateFormat.format(data.timestamp);
}
case COMPONENT: return data.component;
case FAMILY: return data.family;
case CODE: return data.code;
}
return null;
}
@Override
public String getColumnName(int col) {
return ColumnTitles.values()[col].title;
}
/**
* Add a undocumented alarm in the model.
* The thread will get the alarm from the queue and update the model.
*
* @param alarm The alarm to add to the table.
* @see AlarmSelectionListener
*/
public synchronized void onAlarm(Alarm alarm) {
String FF=alarm.getTriplet().getFaultFamily();
String FM=alarm.getTriplet().getFaultMember();
int code =alarm.getTriplet().getFaultCode();
Timestamp timestamp = alarm.getStatus().getSourceTimestamp();
boolean active = alarm.getStatus().isActive();
final AlarmData data = new AlarmData(FF, FM, code, timestamp,active);
// Calc. the number of active alarms in table
EDTExecutor.instance().execute(new Runnable() {
@Override
public void run() {
synchronized (items) {
int idx=items.indexOf(data);
AlarmData old=null;
if (idx!=-1) {
old=items.get(idx);
}
if (old==null) {
if (data.active) {
numOfActiveAlarms++;
}
} else {
if (old.active && !data.active) {
numOfActiveAlarms--;
} else if (!old.active && data.active) {
numOfActiveAlarms++;
}
}
// Remove old instance if any
if (old!=null) {
items.remove(data);
}
// too many items in table?
while (items.size()>=QUEUE_SIZE) {
items.remove(items.size()-1);
}
items.add(data);
}
UndocAlarmTableModel.this.fireTableDataChanged();
}
});
}
/**
* Return the item at the given row
*
* @param row The row of the item to get
* @return the item at row position
*/
public AlarmData getRowEntry(int row) {
synchronized(items) {
if (row<0 || row>=items.size()) {
throw new IllegalArgumentException("Invalid row index");
}
return items.get(row);
}
}
/**
* Clear all the alarms from the table
*/
public synchronized void clearAll() {
EDTExecutor.instance().execute(new Runnable() {
public void run() {
items.clear();
numOfActiveAlarms=0;
fireTableDataChanged();
}
});
}
/**
* Remove all the inactive alarms from the table
*/
public synchronized void clearInactiveAlarms() {
EDTExecutor.instance().execute(new Runnable() {
public void run() {
int t=0;
synchronized(items) {
while(t<items.size()) {
AlarmData al = items.get(t);
if (!al.active) {
items.remove(al);
} else {
t++;
}
}
}
fireTableDataChanged();
}
});
}
/**
* Getter
*
* @return the number of active alarms in the table
*/
public synchronized int getNumOfActiveAlarms() {
return numOfActiveAlarms;
}
/**
* Override to hide/show the tab depending on the number
* of alarms in table
*/
@Override
public void fireTableDataChanged() {
EDTExecutor.instance().execute(new Runnable() {
@Override
public void run() {
UndocAlarmTableModel.super.fireTableDataChanged();
}
});
tabbedPane.undocTabVisible(getRowCount()>0);
}
}