package de.desy.tine.alarmUtils;
import java.util.*;
import de.desy.tine.client.*;
import de.desy.tine.dataUtils.TDataType;
import de.desy.tine.definitions.TErrorList;
import de.desy.tine.server.alarms.*;
import de.desy.tine.server.logger.DbgLog;
import de.desy.tine.server.logger.MsgLog;
public class AlarmMonitorCallback implements TLinkCallback
{
private long alarmDepth = 7200000; // depth in milliseconds (2 hours)
private long timeNow = 0;
private String ctx;
private String srv = null;
private String sys;
private int sev = 7;
private long lastAcquired = 0;
private long lastNotified = 0;
private int lastAcquiredTableEntry = -1;
private LinkedList<TAlarmMessage[]> almTbl = new LinkedList<TAlarmMessage[]>();
private TAlarmMessage[] alms = null;
private int linkStatus;
private long almTimeStamp = 0;
private AlarmMonitorHandler almHandler = null;
private AlarmMonitor almMonitor = null;
private boolean almStateChanged = false;
private boolean includeTerminated = false;
private boolean acquireAll = false;
private boolean needsToNotify = false;
private class GetAlarmsCallback implements TLinkCallback
{
TAlarmMessage[] ams = null;
public void setAlarms(TAlarmMessage[] ams) { this.ams = ams; }
public TAlarmMessage[] getAlarms() { return ams; }
public boolean pending = false;
protected int lastnalarms = 0;
public void callback(TLink link)
{
int cc;
if ((cc=link.getLinkStatus()) != 0)
{
MsgLog.log("GetAlarmsCallback","getAlarms for " + link.getDeviceName() + " failed",cc,null,0);
ams = null;
}
else
{
TDataType amst = link.getOutputDataObject();
int n = amst.getArrayLength();
int nret = amst.getCompletionLength();
// the link god-damn worked! so it's okay to update the friggen lastAcquired !!!!
almTimeStamp = thisTimeStamp; //timeNow;
if (nret > 0)
{
almTimeStamp = link.getLastTimeStamp();
lastAcquired = link.getLastTimeStamp(); //timeNow;
}
if (debugOutput)
{
Date d = new Date(almTimeStamp);
System.out.println(link.getDeviceName()+" "+n+" alarms requested; "+nret+" returned; alarm time "+d.toString());
}
lastnalarms = nret;
if (TLinkFactory.debugLevel > 0)
DbgLog.log("GetAlarmsCallback","getAlarms for "+link.getDeviceName()+" : "+nret+" alarms returned ");
if (nret == 0) ams = null;
else if (nret < n)
{
TAlarmMessage[] a = new TAlarmMessage[nret];
for (int i=0; i<nret; i++) a[i] = ams[i];
ams = a;
}
if (ams != null) Arrays.sort(ams);
updateAlarmList(ams);
if (almStateChanged)
{
needsToNotify = true;
if (TLinkFactory.debugLevel > 0)
DbgLog.log("GetAlarmsCallback", "signal need to notify for "+link.getDeviceName());
}
}
pending = false;
link.close();
}
}
private GetAlarmsCallback getAlarmsCallback = new GetAlarmsCallback();
public void setAlarmMonitorHandler(AlarmMonitorHandler myHandler)
{
almHandler = myHandler;
}
public void setAlarmMonitor(AlarmMonitor myMonitor)
{
almMonitor = myMonitor;
if (almMonitor != null) almHandler = almMonitor.getAmHdlr();
}
public AlarmMonitorCallback(String context, String server, String system, int severity)
{
this(context,server,system,severity,false,false);
}
public AlarmMonitorCallback(String context, String server, String system, int severity,boolean includeTerminated,boolean acquireAll)
{
ctx = context;
srv = server;
sys = system;
sev = severity;
this.includeTerminated = includeTerminated;
this.acquireAll = acquireAll;
String dbgval = System.getProperty("alarmUtils.debug");
if (dbgval != null)
{
if (dbgval.compareToIgnoreCase("true") == 0) debugOutput = true;
else
{
try {if (Integer.parseInt(dbgval) > 0) debugOutput = true;} catch (Exception e) {};
}
}
}
private long getAlarmSetTimeStamp(TAlarmMessage[] almset)
{
if (almset == null) return 0;
long ts = 0, ats;
for (int i=0; i<almset.length; i++)
{
ats = almset[i].getTimeStamp();
if (ats > ts) ts = ats;
}
return ts;
}
private void updateAlarmList(TAlarmMessage[] newAlarms)
{
almStateChanged = true; // just set it to true
synchronized (almTbl)
{
if (!isIncludeTerminated())
{
almTbl.clear();
lastAcquiredTableEntry = -1;
}
if (newAlarms != null)
{
almTbl.add(newAlarms);
}
List<TAlarmMessage> almsets = new ArrayList<TAlarmMessage>();
ListIterator<TAlarmMessage[]> it = almTbl.listIterator(0);
while (it.hasNext())
{
TAlarmMessage[] m = it.next();
if (getAlarmSetTimeStamp(m) > timeNow - alarmDepth)
{
for (int i = 0; i < m.length; i++)
{ // TODO: maybe use a hash list to avoid double entries ...
almsets.add(m[i]);
}
}
else
{ // remove old alarms from the current table
almStateChanged = true; // this is already true !?
it.remove();
if (lastAcquiredTableEntry >= 0) lastAcquiredTableEntry--;
}
}
alms = almsets.isEmpty() ? null : almsets.toArray(new TAlarmMessage[almsets.size()]);
}
}
private void notifyCaller()
{
needsToNotify = false;
lastNotified = lastAcquired;
if (almHandler != null) almHandler.alarmsHandler(almMonitor);
}
private boolean debugOutput = false;
private long lastCallStart = 0;
private long lastCallStop = 0;
private long thisTimeStamp;
private int idleCount = 0;
private int waitPendingCount = 0;
private int linkErrors = 0;
private static final int ERROR_THRESHOLD = 5;
private static final int NALARMS_CUSHION = 10;
private int numDisabledAlarms;
public int getNumberDisableAlarms() { return numDisabledAlarms; }
public void callback(TLink link)
{ // callback of 'NALARMS' or 'NUMALARMS'
if (almMonitor == null)
{
thisTimeStamp = lastCallStart = lastCallStop = waitPendingCount = 0;
return;
}
linkStatus = link.getLinkStatus();
if (linkStatus != 0)
{
if (TLinkFactory.debugLevel > 0) DbgLog.log("AlarmMonitorCallback",link.devName + " alarm monitor callback : " + TErrorList.getErrorString(linkStatus));
if (linkErrors++ > ERROR_THRESHOLD)
{
almMonitor.setConnectionStatus(linkStatus);
notifyCaller();
linkErrors = 0;
}
return;
}
almMonitor.setConnectionStatus(0);
linkErrors = 0;
if (getAlarmsCallback.pending)
{
waitPendingCount++;
if (TLinkFactory.debugLevel > 0) DbgLog.log("AlarmMonitorCallback",link.devName + " still acquiring last alarm set");
return;
}
if (TLinkFactory.debugLevel > 0 && waitPendingCount > 0)
DbgLog.log("AlarmMonitorCallback",link.devName+" acquiring new alarm set after "+waitPendingCount+" attempts");
waitPendingCount = 0;
// don't suppress the heartbeats (use them to clear old alarms)
boolean acquire = false;
boolean reacquire = false;
thisTimeStamp = link.getLastTimeStamp();
if (thisTimeStamp > almTimeStamp)
{ // set in getAlarmsCallback : almTimeStamp = thisTimeStamp;
acquire = true;
if (TLinkFactory.debugLevel > 0)
DbgLog.log("AlarmMonitorCallback",link.devName + " signal reason to acquire new alarm set (timestamp "+thisTimeStamp+" vs "+almTimeStamp+")");
idleCount = 0;
}
else
{ // keep track of number of passes with 'nothing to do' (reset in getLastAcquiredAlarms)
// n.b.: this is probably NOT necessary ...
if (includeTerminated)
{
idleCount++;
if (idleCount > 60)
{
//needsToNotify = true; // set acquire to true if problems 'missing' still persist
reacquire = acquire = true;
idleCount = 0;
if (debugOutput)
System.out.println(sys+" : signalling idle phase readout of last alarms ...");
}
}
}
TDataType dt = link.getOutputDataObject();
int[] n = new int[dt.getArrayLength()];
dt.getData(n);
numDisabledAlarms = n[2]; // n.b. all of what follows assumes we're talking to the CAS
synchronized (almTbl)
{
// almStateChanged = false; <- don't do this here (only when getLastAcquired is called)
//timeNow = System.currentTimeMillis();
timeNow = thisTimeStamp;
//TODO: tighten this up a bit ....
if (includeTerminated)
{ // incremental acquire
if (n[0] > 0 || reacquire)
{ // there are alarms still showing
if (acquire)
{ // there are new alarms to get ! (n.b. start and stop times are inclusive!)
if (TLinkFactory.debugLevel > 0)
DbgLog.log("AlarmMonitorCallback",link.devName+" acquiring new alarm set ("+n[0]+" alarms");
if (lastAcquired == 0)
{
timeNow = System.currentTimeMillis();
lastAcquired = timeNow - alarmDepth;
}
int nget = n[0] + NALARMS_CUSHION;
if (n[0] == 0) nget += getAlarmsCallback.lastnalarms;
getAlarmsCallback.pending = true;
if (debugOutput)
{ // turn on if problems persist ....
System.out.print(sys+" ("+n[0]+") alarms -> acquire "+nget );
Date d = new Date(lastAcquired);
System.out.print(" (range: "+d.toString()+" to ");
d.setTime(timeNow);
System.out.println(d.toString()+")");
}
lastCallStart = lastAcquired; lastCallStop = timeNow;
if (reacquire)
{ // oh, desperation .......
lastCallStart -= 60000;
lastCallStop = System.currentTimeMillis();
}
getAlarmsCallback.ams = TAlarmSystem.getAlarmsAsync(nget, ctx, srv, sys, lastCallStart, lastCallStop, sev, includeTerminated, acquireAll, getAlarmsCallback);
if (getAlarmsCallback.ams == null)
{
DbgLog.log("AlarmMonitorCallback",link.devName+"getAlarmsAsync failed !");
getAlarmsCallback.pending = false;
}
}
else
{ // nothing to do
if (needsToNotify)
{
if (TLinkFactory.debugLevel > 0)
DbgLog.log("AlarmMonitorCallback",link.devName + " notifying caller of results");
notifyCaller();
}
return;
}
}
else
{ // no alarms to show
if (acquire)
{
if (TLinkFactory.debugLevel > 0)
DbgLog.log("AlarmMonitorCallback",link.devName+" no new alarms to acquire (alarms to remove!); last = "+new Date(lastAcquired).toString());
//if (!needsToNotify) lastAcquired = timeNow;
updateAlarmList(null);
needsToNotify = true;
almTimeStamp = thisTimeStamp;
}
else
{ // nothing to do
if (needsToNotify)
{
if (TLinkFactory.debugLevel > 0)
DbgLog.log("AlarmMonitorCallback",link.devName+" notifying caller of results (no new alarms)");
notifyCaller();
}
return;
}
}
}
else
{ // not includeTerminated: just get this set and update
if (n[1] > 0)
{ // there are alarms showing
if (acquire)
{ // this should always be true for the non-terminated case
getAlarmsCallback.pending = true;
getAlarmsCallback.ams = TAlarmSystem.getAlarmsAsync(n[0]+NALARMS_CUSHION, ctx, srv, sys, timeNow - alarmDepth, timeNow, sev, includeTerminated,getAlarmsCallback);
if (getAlarmsCallback.ams == null)
{
DbgLog.log("AlarmMonitorCallback",link.devName+"getAlarmsAsync failed !");
getAlarmsCallback.pending = false;
}
}
else
{ // nothing to do
if (needsToNotify)
{
DbgLog.log("AlarmMonitorCallback",link.devName+"notifying caller of results (post-callback)");
notifyCaller();
}
return;
}
}
else
{ // no alarms to show
if (acquire)
{
//if (!needsToNotify) lastAcquired = timeNow;
updateAlarmList(null);
needsToNotify = true;
}
else
{ // nothing to do
if (needsToNotify) notifyCaller();
return;
}
}
}
}
}
public TAlarmMessage[] getAlarms()
{
return alms;
}
public TAlarmMessage[] getLastAcquiredAlarms()
{
synchronized (almTbl)
{
almStateChanged = false; // is being read by caller
int nalmSets = almTbl.size();
int first = lastAcquiredTableEntry;
if (nalmSets == 0) return null;
if (first > nalmSets - 1)
{
DbgLog.log("getLastAcquiredAlarms",getSubsystem()+ " last acquired index " + first + " larger than alarm table size : " + almTbl.size());
first = 0;
}
int last = nalmSets - 2; // was nalmSets -1;
if (last < 0) last = 0;
if (first < 0) first = 0;
List<TAlarmMessage> lastLst = new ArrayList<TAlarmMessage>();
ListIterator<TAlarmMessage[]> it = almTbl.listIterator(first);
while (it.hasNext())
{
TAlarmMessage[] m = it.next();
for (int i = 0; i < m.length; i++)
{
lastLst.add(m[i]);
if (debugOutput)
{ // turn on if problems persist ...
if ((m[i].getAlarmDescriptor() & TAlarmDescriptor.NEW) == TAlarmDescriptor.NEW ||
(m[i].getAlarmDescriptor() & TAlarmDescriptor.TERMINATE) == TAlarmDescriptor.TERMINATE)
{
Date d = new Date(m[i].getTimeStamp());
System.out.print(m[i].getAlarmSystem()+" "+m[i].getDevice()+" "+m[i].getAlarmTag()+" "+
m[i].getAlarmDescriptorAsString()+" "+d.toString());
d.setTime(lastCallStart); System.out.print(" (range : "+d.toString());
d.setTime(lastCallStop); System.out.println(" to "+d.toString()+")");
}
}
}
}
lastAcquiredTableEntry = last;
return lastLst.toArray(new TAlarmMessage[lastLst.size()]);
}
}
public String getContext()
{
return ctx;
}
public long getLastAcquired()
{
return lastAcquired;
}
public String getServer()
{
return srv;
}
public String getSubsystem()
{
return sys;
}
public boolean isIncludeTerminated()
{
return includeTerminated;
}
public void setIncludeTerminated(boolean includeTerminated)
{
this.includeTerminated = includeTerminated;
}
public void setAcquireAll(boolean value)
{
acquireAll = value;
}
}