package org.yamcs.parameterarchive;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.rocksdb.RocksDBException;
import org.slf4j.Logger;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.parameter.ParameterConsumer;
import org.yamcs.parameter.Value;
import org.yamcs.protobuf.Yamcs.Value.Type;
import org.yamcs.utils.LoggingUtils;
import org.yamcs.utils.SortedIntArray;
import org.yamcs.utils.TimeEncoding;
class ArchiveFillerTask implements ParameterConsumer {
final ParameterArchive parameterArchive;
final Logger log;
long numParams = 0;
//segment start -> ParameterGroup_id -> PGSegment
protected TreeMap<Long, Map<Integer, PGSegment>> pgSegments = new TreeMap<>();
protected final ParameterIdDb parameterIdMap;
protected final ParameterGroupIdDb parameterGroupIdMap;
//ignore any data older than this
protected long collectionSegmentStart;
long threshold = 60000;
public ArchiveFillerTask(ParameterArchive parameterArchive) {
this.parameterArchive = parameterArchive;
this.parameterIdMap = parameterArchive.getParameterIdDb();
this.parameterGroupIdMap = parameterArchive.getParameterGroupIdDb();
log = LoggingUtils.getLogger(this.getClass(), parameterArchive.getYamcsInstance());
}
void setCollectionSegmentStart(long collectionSegmentStart) {
this.collectionSegmentStart = collectionSegmentStart;
}
/**
* adds the parameters to the pgSegments structure and return the highest timestamp or -1 if all parameters have been ignored (because they were too old)
*
* parameters older than ignoreOlderThan are ignored.
*
*
* @param items
* @return
*/
protected long processParameters(List<ParameterValue> items) {
Map<Long, SortedParameterList> m = new HashMap<>();
for(ParameterValue pv: items) {
long t = pv.getGenerationTime();
if(t<collectionSegmentStart) {
continue;
}
if(pv.getParameterQualifiedNamed()==null) {
log.warn("No qualified name for parameter value {}, ignoring", pv);
continue;
}
SortedParameterList l = m.get(t);
if(l==null) {
l = new SortedParameterList();
m.put(t, l);
}
l.add(pv);
}
long maxTimestamp = -1;
for(Map.Entry<Long,SortedParameterList> entry: m.entrySet()) {
long t = entry.getKey();
SortedParameterList pvList = entry.getValue();
processParameters(t, pvList);
if(t>maxTimestamp) {
maxTimestamp = t;
}
}
return maxTimestamp;
}
private void processParameters(long t, SortedParameterList pvList) {
numParams+=pvList.size();
try {
int parameterGroupId = parameterGroupIdMap.createAndGet(pvList.parameterIdArray);
long segmentId = SortedTimeSegment.getSegmentId(t);
Map<Integer, PGSegment> m = pgSegments.get(segmentId);
if(m==null) {
m = new HashMap<Integer, PGSegment>();
pgSegments.put(segmentId, m);
}
PGSegment pgs = m.get(parameterGroupId);
if(pgs==null) {
pgs = new PGSegment(parameterGroupId, segmentId, pvList.parameterIdArray);
m.put(parameterGroupId, pgs);
}
pgs.addRecord(t, pvList.sortedPvList);
} catch (RocksDBException e) {
log.error("Error processing parameters", e);
}
}
void flush() {
log.info("Starting a consolidation process, number of intervals: {}", pgSegments.size());
for(Map<Integer, PGSegment> m: pgSegments.values()) {
consolidateAndWriteToArchive(m.values());
}
}
/**
* writes data into the archive
* @param pgList
*/
protected void consolidateAndWriteToArchive(Collection<PGSegment> pgList) {
for(PGSegment pgs: pgList) {
pgs.consolidate();
}
try {
parameterArchive.writeToArchive(pgList);
} catch (RocksDBException e) {
log.error("failed to write data to the archive", e);
}
}
@Override
public void updateItems(int subscriptionId, List<ParameterValue> items) {
long t = processParameters(items);
if(t<0){
return;
}
long nextSegmentStart = SortedTimeSegment.getNextSegmentStart(collectionSegmentStart);
while(t > nextSegmentStart + threshold) {
Map<Integer, PGSegment> m = pgSegments.remove(collectionSegmentStart);
if(m!=null) {
log.debug("Writing to archive the segment: [{} - {})", TimeEncoding.toString(collectionSegmentStart), TimeEncoding.toString(nextSegmentStart));
consolidateAndWriteToArchive(m.values());
}
collectionSegmentStart = nextSegmentStart;
nextSegmentStart = SortedTimeSegment.getNextSegmentStart(collectionSegmentStart);
}
}
public long getNumProcessedParameters() {
return numParams;
}
/*builds incrementally a list of parameter id and parameter value, sorted by parameter ids */
class SortedParameterList {
SortedIntArray parameterIdArray = new SortedIntArray();
List<ParameterValue> sortedPvList = new ArrayList<ParameterValue>();
void add(ParameterValue pv) {
String fqn = pv.getParameterQualifiedNamed();
Value engValue = pv.getEngValue();
if(engValue==null) {
log.warn("Ignoring parameter without engineering value: {} ", pv.getParameterQualifiedNamed());
return;
}
Value rawValue = pv.getRawValue();
Type engType = engValue.getType();
Type rawType = (rawValue==null)? null: rawValue.getType();
int parameterId = parameterIdMap.createAndGet(fqn, engType, rawType);
int pos = parameterIdArray.insert(parameterId);
sortedPvList.add(pos, pv);
}
public int size() {
return parameterIdArray.size();
}
}
}