package org.yamcs.tctm;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.yamcs.ConfigurationException;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.YConfiguration;
import org.yamcs.YamcsServer;
import org.yamcs.management.ManagementService;
import org.yamcs.protobuf.Yamcs.NamedObjectId;
import org.yamcs.tctm.ParameterSink;
import org.yamcs.time.TimeService;
import org.yamcs.utils.LoggingUtils;
import org.yamcs.utils.YObjectLoader;
import org.yamcs.yarch.DataType;
import org.yamcs.yarch.Stream;
import org.yamcs.yarch.Tuple;
import org.yamcs.yarch.TupleDefinition;
import org.yamcs.yarch.YarchDatabase;
import com.google.common.util.concurrent.AbstractService;
/**
*
* Injects processed parameters from PpDataLinks into yamcs streams.
*
* To the base definition there is one column for each parameter name with the type PROTOBUF({@link org.yamcs.protobuf.Pvalue.ParameterValue})
* @author nm
*
*/
public class ParameterDataLinkInitialiser extends AbstractService {
public static final String KEY_PARAMETER_DATA_LINKS = "parameterDataLinks";
public static final String PARAMETER_TUPLE_COL_RECTIME = "rectime";
public static final String PARAMETER_TUPLE_COL_SEQ_NUM = "seqNum";
public static final String PARAMETER_TUPLE_COL_GROUP = "group";
public static final String PARAMETER_TUPLE_COL_GENTIME = "gentime";
String yamcsInstance;
private Collection<ParameterDataLink> parameterDataLinks = new ArrayList<ParameterDataLink>();
final private Logger log;
static public final TupleDefinition PARAMETER_TUPLE_DEFINITION = new TupleDefinition();
//first columns from the PP tuples
//the actual values are encoded as separated columns (umi_0x010203040506, value) value is ParameterValue
static {
PARAMETER_TUPLE_DEFINITION.addColumn(PARAMETER_TUPLE_COL_GENTIME, DataType.TIMESTAMP); //generation time
PARAMETER_TUPLE_DEFINITION.addColumn(PARAMETER_TUPLE_COL_GROUP, DataType.ENUM); //group - used for partitioning (i.e. splitting the archive in multiple files)
PARAMETER_TUPLE_DEFINITION.addColumn(PARAMETER_TUPLE_COL_SEQ_NUM, DataType.INT); //sequence number
PARAMETER_TUPLE_DEFINITION.addColumn(PARAMETER_TUPLE_COL_RECTIME, DataType.TIMESTAMP); //recording time
}
final TimeService timeService;
public ParameterDataLinkInitialiser(String yamcsInstance) throws IOException, ConfigurationException {
this.yamcsInstance = yamcsInstance;
YarchDatabase ydb = YarchDatabase.getInstance(yamcsInstance);
log = LoggingUtils.getLogger(this.getClass(), yamcsInstance);
YConfiguration c = YConfiguration.getConfiguration("yamcs."+yamcsInstance);
this.timeService = YamcsServer.getTimeService(yamcsInstance);
List<Object> providers = c.getList(KEY_PARAMETER_DATA_LINKS);
int count=1;
for(Object o:providers) {
if(!(o instanceof Map)) {
throw new ConfigurationException("ppProvider has to be a Map and not a "+o.getClass());
}
Map<String, Object> m = (Map<String, Object>)o;
Object args=null;
if(m.containsKey("args")) {
args=m.get("args");
} else if(m.containsKey("config")) {
args=m.get("config");
} else if(m.containsKey("spec")) {
args=m.get("spec");
}
String streamName = YConfiguration.getString(m, "stream");
String providerName="pp"+count;
boolean enabledAtStartup=true;
if(m.containsKey("enabledAtStartup")) {
enabledAtStartup=YConfiguration.getBoolean(m, "enabledAtStartup");
}
final Stream stream = ydb.getStream(streamName);
if(stream==null) {
throw new ConfigurationException("Cannot find stream '"+streamName+"'");
}
ParameterDataLink prov = YObjectLoader.loadObject(m, yamcsInstance, providerName);
if(!enabledAtStartup) {
prov.disable();
}
prov.setParameterSink(new MyPpListener(stream));
ManagementService.getInstance().registerLink(yamcsInstance, providerName, streamName, args!=null?args.toString():"", prov);
parameterDataLinks .add(prov);
count++;
}
}
@Override
protected void doStart() {
for(ParameterDataLink prov:parameterDataLinks ) {
prov.startAsync();
}
notifyStarted();
}
static public void main(String[] args) throws Exception {
new ParameterDataLinkInitialiser("test").startAsync();
}
@Override
protected void doStop() {
for(ParameterDataLink prov:parameterDataLinks ) {
prov.stopAsync();
}
notifyStopped();
}
class MyPpListener implements ParameterSink {
final Stream stream;
final DataType paraDataType = DataType.PARAMETER_VALUE;
public MyPpListener(Stream stream) {
this.stream = stream;
}
@Override
public void updateParameters(long gentime, String group, int seqNum, Collection<ParameterValue> params) {
TupleDefinition tdef = PARAMETER_TUPLE_DEFINITION.copy();
List<Object> cols=new ArrayList<Object>(4+params.size());
cols.add(gentime);
cols.add(group);
cols.add(seqNum);
cols.add(timeService.getMissionTime());
for(ParameterValue pv:params) {
String qualifiedName = pv.getParameterQualifiedNamed();
int idx = tdef.getColumnIndex(qualifiedName);
if(idx!=-1) {
log.warn("duplicate value for {} \nfirst: {}"+"\n second: {} ", pv.getParameter(), cols.get(idx), pv);
continue;
}
tdef.addColumn(qualifiedName, DataType.PARAMETER_VALUE);
cols.add(pv);
}
Tuple t = new Tuple(tdef, cols);
stream.emitTuple(t);
}
@Override
public void updateParams(long gentime, String group, int seqNum, Collection<org.yamcs.protobuf.Pvalue.ParameterValue> params) {
List<ParameterValue> plist = new ArrayList<>(params.size());
for(org.yamcs.protobuf.Pvalue.ParameterValue pbv:params) {
NamedObjectId id = pbv.getId();
String qualifiedName = id.getName();
if(id.hasNamespace()) {
log.trace("Using namespaced name for parameter {} because fully qualified name not available.", id);
}
ParameterValue pv = ParameterValue.fromGpb(qualifiedName, pbv);
plist.add(pv);
}
updateParameters(gentime, group, seqNum, plist);
}
}
}