package org.yamcs.api;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.yamcs.protobuf.Yamcs.Event;
import org.yamcs.protobuf.Yamcs.Event.EventSeverity;
/**
* Default implementation of an EventProducer that provides shortcut methods for
* sending message of different severity types.
*/
public abstract class AbstractEventProducer implements EventProducer {
SimpleString address;
String source;
AtomicInteger seqNo = new AtomicInteger();
private boolean repeatedEventReduction; // Whether to check for message repetitions
private Event originalEvent; // Original evt of a series of repeated events
private Event lastRepeat; // Last evt of a series of repeated events
private int repeatCounter = 0;
private long repeatedEventTimeout = 60000; //how long in milliseconds to buffer repeated events
// Flushes the Event Buffer about each minute
private Timer flusher;
/* (non-Javadoc)
* @see org.yamcs.api.EventProducer#setSource(java.lang.String)
*/
@Override
public void setSource(String source) {
this.source=source;
}
/* (non-Javadoc)
* @see org.yamcs.api.EventProducer#setSeqNo(int)
*/
@Override
public void setSeqNo(int sn) {
this.seqNo.set(sn);
}
/* (non-Javadoc)
* @see org.yamcs.api.EventProducer#sendError(java.lang.String, java.lang.String)
*/
@Override
public synchronized void sendError(String type, String msg) {
sendMessage(EventSeverity.ERROR, type, msg);
}
/* (non-Javadoc)
* @see org.yamcs.api.EventProducer#sendWarning(java.lang.String, java.lang.String)
*/
@Override
public synchronized void sendWarning(String type, String msg) {
sendMessage(EventSeverity.WARNING, type, msg);
}
/* (non-Javadoc)
* @see org.yamcs.api.EventProducer#sendInfo(java.lang.String, java.lang.String)
*/
@Override
public synchronized void sendInfo(String type, String msg) {
sendMessage(EventSeverity.INFO, type, msg);
}
private void sendMessage(EventSeverity severity, String type, String msg) {
Event e = newEvent().setSeverity(severity).setType(type).setMessage(msg).build();
if (!repeatedEventReduction) {
sendEvent(e);
} else {
if (originalEvent == null) {
sendEvent(e);
originalEvent = e;
} else if (isRepeat(e)) {
if (flusher == null) { // Prevent buffering repeated events forever
flusher = new Timer(true);
flusher.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
flushEventBuffer(false);
}
}, repeatedEventTimeout, repeatedEventTimeout);
}
lastRepeat = e;
repeatCounter++;
} else { // No more repeats
if (flusher != null) {
flusher.cancel();
flusher = null;
}
flushEventBuffer(true);
sendEvent(e);
originalEvent = e;
lastRepeat = null;
}
}
}
/**
* By default event repetitions are checked for possible reduction. Disable if
* 'realtime' events are required.
*/
public synchronized void setRepeatedEventReduction(boolean repeatedEventReduction, long repeatedEventTimeoutMillisec) {
this.repeatedEventReduction = repeatedEventReduction;
if (!repeatedEventReduction) {
if (flusher != null) {
flusher.cancel();
flusher = null;
}
flushEventBuffer(true);
}
}
protected synchronized void flushEventBuffer(boolean startNewSequence) {
if (repeatCounter > 1) {
sendEvent(Event.newBuilder(lastRepeat)
.setMessage("Repeated "+repeatCounter+" times: "+lastRepeat.getMessage())
.build());
} else if (repeatCounter == 1) {
sendEvent(lastRepeat);
lastRepeat = null;
}
if (startNewSequence) {
originalEvent = null;
}
repeatCounter = 0;
}
/**
* Checks whether the specified Event is a repeat of the previous Event.
*/
private boolean isRepeat(Event e) {
if (originalEvent == e) {
return true;
}
return originalEvent.getMessage().equals(e.getMessage())
&& originalEvent.getSeverity().equals(e.getSeverity())
&& originalEvent.getSource().equals(e.getSource())
&& originalEvent.getType().equals(e.getType());
}
@Override
public Event.Builder newEvent() {
long t = getMissionTime();
return Event.newBuilder().setSource(source).
setSeqNumber(seqNo.getAndIncrement()).setGenerationTime(t).
setReceptionTime(t);
}
public abstract long getMissionTime() ;
}