/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.epics.archiverappliance.engine.model;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.config.ArchDBRTypes;
import org.epics.archiverappliance.data.DBRTimeEvent;
import org.epics.archiverappliance.engine.membuf.ArrayListEventStream;
import org.epics.archiverappliance.engine.pv.PVMetrics;
import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc;
/**
* Buffer for the samples of one channel.
* <p>
* Assumes that one thread adds samples, while a different thread removes them.
* When the queue size is reached, older samples get dropped.
*
* @author Kay Kasemir
* @version Initial version:CSS
* @version 4-Jun-2012, Luofeng Li:added codes to support for the new archiver
*/
public class SampleBuffer {
/**
* Name of channel that writes to this buffer. (we keep only the name, not
* the full channel, to decouple stuff).
*/
final private String channel_name;
/**
* current ArrayListEventStream
*/
private ArrayListEventStream currentSamples;
/**
* previous ArrayListEventStream
*/
private ArrayListEventStream previousSamples;
/**
* year listener for this buffer.
*/
private YearListener yearListener;
/**
* pVMetrics kept for this sample buffer
*/
private PVMetrics pVMetrics;
/**
* Is the buffer in an error state because of RDB write errors? Note that
* this is global for all buffers, not per instance!
*/
private static volatile boolean error = false;
/**
* the buffer size
*/
final private int capacity;
/**
* the arch dbr type of the pv who has this sample buffer
*/
final private ArchDBRTypes archdbrtype;
private short year;
private static Logger logger = Logger.getLogger(SampleBuffer.class.getName());
/** Create sample buffer of given capacity
*
* @param channel_name
* @param capacity
* @param archdbrtype ArchDBRTypes
* @param pVMetrics PVMetrics
*/
public SampleBuffer(final String channel_name, final int capacity,
ArchDBRTypes archdbrtype, PVMetrics pVMetrics) {
this.channel_name = channel_name;
this.archdbrtype = archdbrtype;
this.pVMetrics = pVMetrics;
RemotableEventStreamDesc desc = new RemotableEventStreamDesc(
archdbrtype, channel_name, (short) 0);
currentSamples = new ArrayListEventStream(capacity, desc);
this.capacity = capacity;
}
/** @return channel name of this buffer */
public String getChannelName() {
return channel_name;
}
/**
* get current ArrayListEventStream
* @return ArrayListEventStream
*/
public ArrayListEventStream getCurrentSamples() {
return currentSamples;
}
/**
* get the combined ArrayListEventStream of the previous and the current
* @return ArrayListEventStream
*/
public ArrayListEventStream getCombinedSamples() {
RemotableEventStreamDesc desc = new RemotableEventStreamDesc(
archdbrtype, channel_name, (short) 0);
ArrayListEventStream combinedSamples = new ArrayListEventStream(
capacity * 3, desc);
if (previousSamples != null) {
for (int mm = 0; mm < previousSamples.size(); mm++) {
DBRTimeEvent timeEvent = (DBRTimeEvent) previousSamples.get(mm);
combinedSamples.add(timeEvent);
}
}
if (currentSamples != null) {
for (int mm2 = 0; mm2 < currentSamples.size(); mm2++) {
DBRTimeEvent timeEvent22 = (DBRTimeEvent) currentSamples
.get(mm2);
combinedSamples.add(timeEvent22);
}
}
return combinedSamples;
}
public void resetSamples() {
RemotableEventStreamDesc desc = new RemotableEventStreamDesc(
archdbrtype, channel_name, this.year);
synchronized (this) {
previousSamples = currentSamples;
currentSamples = new ArrayListEventStream(capacity, desc);
}
}
/**
* get the previous ArrayListEventStream
* @return ArrayListEventStream
*/
public ArrayListEventStream getPreviousSamples() {
return previousSamples;
}
/** @return Queue capacity, i.e. maximum queue size. */
public int getCapacity() {
return capacity;
}
/** @return Current queue size, i.e. number of samples in the queue. */
public int getQueueSize() {
return currentSamples.size();
}
/** @return <code>true</code> if currently experiencing write errors */
public static boolean isInErrorState() {
return error;
}
/** Set the error state.
* @param error
*/
public static void setErrorState(final boolean error) {
SampleBuffer.error = error;
}
/**
* Add a sample to the queue, maybe dropping older samples
*
* @param value DBRTimeEvent
* @return boolean true if we need to increment the event count.
*/
@SuppressWarnings("nls")
public boolean add(final DBRTimeEvent value) {
boolean retval = true;
if(this.archdbrtype != value.getDBRType()) {
pVMetrics.incrementInvalidTypeLostEventCount();
return false;
}
@SuppressWarnings("deprecation")
short yearTemp = (short) (value.getEventTimeStamp().getYear() + 1900);
// value.getEventTimeStamp().
if (currentSamples.getYear() == 0) {
currentSamples.setYear(yearTemp);
this.year = yearTemp;
} else if (yearTemp != this.year) {
this.year = yearTemp;
yearListener.yearChanged(this);
}
try {
synchronized (this) {
int remainSize = capacity - currentSamples.size();
if (remainSize < 1) {
retval = false;
// The buffer is full, drop the first sample and increment the bufferFull lost event count.
currentSamples.remove(0);
pVMetrics.addSampleBufferFullLostEventCount();
}
currentSamples.add(value);
}
return retval;
} catch (Exception e) {
//throw e;
logger.error(
"Exception when add data into sample buffer of pv: "+channel_name,
e);
return false;
}
}
@SuppressWarnings("nls")
@Override
public String toString() {
return String.format("Sample buffer '%s': %d samples, %d samples max", channel_name, getQueueSize(), this.capacity);
}
/**
* add the year listener to this buffer
* @param yearListener the interface of yearListener
*/
public void addYearListener(YearListener yearListener) {
this.yearListener = yearListener;
}
}