/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) ESO - European Southern Observatory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* 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;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import org.omg.CORBA.ORB;
import alma.acs.logging.AcsLogLevel;
import alma.acs.logging.AcsLogger;
import alma.acsplugins.alarmsystem.gui.detail.AlarmDetailTable;
import alma.acsplugins.alarmsystem.gui.sound.AlarmSound;
import alma.acsplugins.alarmsystem.gui.statusline.StatusLine;
import alma.acsplugins.alarmsystem.gui.table.AlarmTable;
import alma.acsplugins.alarmsystem.gui.table.AlarmTableModel;
import alma.acsplugins.alarmsystem.gui.toolbar.Toolbar;
import alma.acsplugins.alarmsystem.gui.undocumented.table.UndocAlarmTableModel;
import alma.alarmsystem.clients.AlarmCategoryClient;
import alma.alarmsystem.clients.alarm.AlarmClientException;
import alma.maciErrType.wrappers.AcsJCannotGetComponentEx;
import cern.laser.client.data.Alarm;
import cern.laser.client.services.selection.AlarmSelectionListener;
/**
* The panel shown while the CERN AS is in use and the
* alarm client is connected.
* <P>
* @author acaproni
*
*/
public class CernSysPanel extends JPanel {
/**
* The toolbar
*/
private Toolbar toolbar;
/**
* The status line
*/
private StatusLine statusLine;
/**
* The startup option for reduction rules
*/
public final boolean ACTIVATE_RDUCTION_RULES=true;
/**
* The model of the table of alarms
*/
private AlarmTableModel model;
/**
* The table of alarms
*/
private AlarmTable alarmTable;
/**
* The table with the details of an alarm
*/
private AlarmDetailTable detailTable;
/**
* The object to play sounds for alarms
*/
private AlarmSound alarmSound;
/**
* The scroll pane of the table
*/
private JScrollPane tableScrollPane = new JScrollPane(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
/**
* The scroll pane of the details table
*/
private JScrollPane detailsScrollPane = new JScrollPane(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
/**
* The split pane dividing the table of alarms and the detail view
*/
private JSplitPane splitPane;
/**
* The panel showing this container
*/
private final AlarmPanel alarmPanel;
/**
* Say if there is an attempt to connect
*/
private volatile boolean connecting=false;
/**
* <code>true</code> if the panel has been closed.
* It helps stopping the connection thread
*/
private volatile boolean closed=false;
/**
* The listener of the connection
*/
private ConnectionListener connectionListener;
/**
* The thread to connect/disconnect
*/
private Thread connectThread;
/**
* Signal the thread to terminate
*/
private Thread disconnectThread;
/**
* The client to listen alarms from categories
*/
private AlarmCategoryClient categoryClient=null;
/**
* The ORB
*/
private ORB orb;
/**
* The logger
*/
private volatile AcsLogger logger;
/**
* The panel to show messages while connecting
*/
private final AlSysNotAvailPanel notAvaiPnl;
/**
* The name of the system property to set to define to set the initial auto-acknowledge.
* <P>
* The value of this property can be an integer in the range [1,3] because
* auto acknowledge is not allowed for the alarms with the highest priority (0).
* If a value of 0 is set then the auto acknowledge falls down to priority 1 and a warning log is issued.
* To disable auto acknowledge, the value of this property must be set to -1
*
* @see CernSysPanel#getInitialAutoAckLevel()
*/
public static final String AutoAckLevelPropName="alma.acs.alarmsystem.alarmpanel.initialAutoAckLevel";
/**
* Default auto acknowledge set if {@value CernSysPanel#AutoAckLevelPropName} property has not
* been defined.
*/
public static final Integer defaultAutoAckLevel=2;
/**
* Constructor
*
* @param owner The panel showing this container
* @param notAvaiPnl The panel when the AS is not available
*/
public CernSysPanel(AlarmPanel owner, AlSysNotAvailPanel notAvaiPnl, UndocAlarmTableModel undocModel) {
if (notAvaiPnl==null) {
throw new IllegalArgumentException("AlSysNotAvailPanel can't be null");
}
alarmPanel=owner;
this.notAvaiPnl=notAvaiPnl;
initialize(undocModel);
}
/**
* Init the GUI
*
*/
private void initialize(UndocAlarmTableModel undocModel) {
setLayout(new BorderLayout());
// Build GUI objects
model = new AlarmTableModel(this,ACTIVATE_RDUCTION_RULES,false,undocModel);
model.start();
alarmSound= new AlarmSound(model);
statusLine = new StatusLine(model,this);
alarmTable = new AlarmTable(model, this, undocModel, statusLine);
connectionListener=statusLine;
model.setConnectionListener(statusLine);
detailTable = new AlarmDetailTable();
// The table of alarms
tableScrollPane.setViewportView(alarmTable);
Dimension minimumSize = new Dimension(300, 150);
tableScrollPane.setMinimumSize(minimumSize);
tableScrollPane.setPreferredSize(minimumSize);
// The details table
detailsScrollPane.setViewportView(detailTable);
// The panel with the details
JPanel detailsPanel = new JPanel();
BoxLayout layout = new BoxLayout(detailsPanel,BoxLayout.Y_AXIS);
detailsPanel.setLayout(new BorderLayout());
JPanel lblPnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
lblPnl.add(new JLabel("Alarm details"));
detailsPanel.add(lblPnl,BorderLayout.PAGE_START);
detailsPanel.add(detailsScrollPane,BorderLayout.CENTER);
minimumSize = new Dimension(120, 150);
detailsPanel.setMinimumSize(minimumSize);
detailsPanel.setPreferredSize(minimumSize);
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,tableScrollPane,detailsPanel);
splitPane.setOneTouchExpandable(true);
splitPane.setResizeWeight(1);
//splitPane.setDividerLocation(tableScrollPane.getMinimumSize().width);
add(splitPane,BorderLayout.CENTER);
// Add the toolbar
toolbar=new Toolbar(alarmTable,model,alarmSound,ACTIVATE_RDUCTION_RULES,this);
add(toolbar,BorderLayout.NORTH);
// Add the status line
add(statusLine,BorderLayout.SOUTH);
// Set the initial auto ack level.
toolbar.setAutoAckLevel(getInitialAutoAckLevel());
}
AlarmTable getAlarmTable() {
return alarmTable;
}
/**
* Closes the panel
*/
public void close() {
alarmSound.close();
model.close();
alarmTable.close();
}
/**
* @see IpauseResume
*/
public void pause() throws Exception {
model.pause(true);
statusLine.pause();
toolbar.updatePauseBtn(true);
}
/**
* @see IPauseResume
*/
public void resume() throws Exception {
model.pause(false);
statusLine.resume();
toolbar.updatePauseBtn(false);
}
/**
* Show a message in the status line
*
* @param mesg
* @param red
*
* @see StatusLine
*/
public void showMessage(String mesg, boolean red) {
statusLine.showMessage(mesg, red);
}
/**
* Show the alarm in the details table
*
* @param alarm The alarm to show in the details panel;
* if <code>null</code> the details table is cleared.
*/
public void showAlarmDetails(Alarm alarm) {
detailTable.showAlarmDetails(alarm);
}
public void setModel(AlarmTableModel model) {
this.model = model;
}
/**
* Connect
*/
public void connect() {
if (connecting || closed) {
return;
}
connecting=true;
connectionListener.connecting();
notAvaiPnl.addMessage("Connecting to the alarm service");
notAvaiPnl.addMessage("Instantiating the category client");
try {
categoryClient = new AlarmCategoryClient(orb,logger);
} catch (Throwable t) {
System.err.println("Error instantiating the CategoryClient: "+t.getMessage());
notAvaiPnl.addMessage("Error instantiating the CategoryClient: "+t.getMessage());
t.printStackTrace(System.err);
connectionListener.disconnected();
categoryClient=null;
connecting=false;
return;
}
/**
* Try to connect to the alarm service until it becomes available
*/
categoryClient.addAlarmListener((AlarmSelectionListener)model);
while (true && !closed) {
notAvaiPnl.addMessage("Connecting to the categories");
try {
categoryClient.connect();
notAvaiPnl.addMessage("CategoryClient connected");
// If the connection succeeded then exit the loop
break;
} catch (AcsJCannotGetComponentEx cgc) {
// Wait 30 secs before retrying
// but checks if it is closed every second.
int t=0;
while (t<30) {
if (closed) {
return;
}
try {
Thread.sleep(1000);
} catch (Exception e) {}
t++;
}
cgc.printStackTrace();
continue; // Try again
} catch (Throwable t) {
System.err.println("Error connecting CategoryClient: "+t.getMessage()+", "+t.getClass().getName());
notAvaiPnl.addMessage("Error connecting CategoryClient: "+t.getMessage()+", "+t.getClass().getName());
t.printStackTrace(System.err);
connectionListener.disconnected();
connecting=false;
return;
}
}
if (closed) {
model.setCategoryClient(null);
return;
}
notAvaiPnl.addMessage("Connected to the alarm service");
connecting=false;
connectionListener.connected();
statusLine.start();
model.setCategoryClient(categoryClient);
alarmPanel.showPanel(AlarmPanel.cernSysName);
}
/**
* Disconnect
*/
public void disconnect() {
statusLine.stop();
model.setCategoryClient(null);
// wait until the connect thread terminates (if it is running)
while (connectThread!=null && connectThread.isAlive()) {
try {
Thread.sleep(1500);
} catch (Exception e) {}
}
try {
categoryClient.close();
} catch (AlarmClientException e) {
if (logger!=null) {
logger.log(AcsLogLevel.WARNING,"Ignored exception closing the AlarmCategoryClient",e);
} else {
System.err.println("Ignored exception closing the AlarmCategoryClient: "+e.getMessage());
e.printStackTrace();
}
} finally {
categoryClient=null;
connectionListener.disconnected();
}
}
/**
* @return <code>true</code> if an attempt to connect is running
*/
public boolean isConnecting() {
return connecting;
}
public void setServices(ORB orb, AcsLogger logger) {
if (orb==null) {
throw new IllegalArgumentException("The ORB can't be null");
}
this.orb=orb;
if (logger==null) {
throw new IllegalArgumentException("The Logger can't be null");
}
this.logger=logger;
}
/**
* Connect the Client and listens to the categories.
*
* The <code>CategoryClient</code> is built only if its reference is null.
* Otherwise it means that the client is still trying to connect and the user
* restarted the plugin.
*
* @see SubsystemPlugin
*/
public void start() throws Exception {
// Check if the CS have been set
if (orb==null || logger==null) {
throw new IllegalStateException("ORB?Logger not set");
}
class StartAlarmPanel extends Thread {
public void run() {
CernSysPanel.this.connect();
}
}
closed=false;
// Connect the categoryClient only if it is null
if (categoryClient==null) {
connectThread = new StartAlarmPanel();
connectThread.setName("StartAlarmPanel");
connectThread.setDaemon(true);
connectThread.start();
}
}
/**
* @see SubsystemPlugin
*/
public void stop() throws Exception {
class StopAlarmPanel extends Thread {
public void run() {
try {
CernSysPanel.this.disconnect();
} catch (Throwable t) {
System.err.println("Ignored error while disconnecting category client: "+t.getMessage());
t.printStackTrace(System.err);
}
}
}
closed=true;
disconnectThread = new StopAlarmPanel();
disconnectThread.setName("StopAlarmPanel");
disconnectThread.start();
close();
}
/**
* Get the initial auto-acknowledge level from the {@link CernSysPanel#AutoAckLevelPropName} java property.
*
* @return The initial auto acknowledge level
* @see CernSysPanel#AutoAckLevelPropName
*/
private int getInitialAutoAckLevel() {
int ackLevel=Integer.getInteger(AutoAckLevelPropName, defaultAutoAckLevel);
if (ackLevel==0) {
String msg="Auto acknowledge for priority 0 alarms is not allowed: falling back to priority 1";
if (logger!=null) {
logger.log(AcsLogLevel.WARNING, msg);
} else {
System.out.println(msg);
}
ackLevel=1;
}
if (ackLevel<0 || ackLevel>3) {
ackLevel=-1;
}
return ackLevel;
}
}