package org.yamcs.parameter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.ConfigurationException;
import org.yamcs.InvalidIdentification;
import org.yamcs.Processor;
import org.yamcs.protobuf.Pvalue.AcquisitionStatus;
import org.yamcs.protobuf.Yamcs.NamedObjectId;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.utils.ValueUtility;
import org.yamcs.xtce.DataSource;
import org.yamcs.xtce.NamedDescriptionIndex;
import org.yamcs.xtce.Parameter;
import org.yamcs.xtce.XtceDb;
import org.yamcs.xtceproc.ParameterTypeProcessor;
import com.google.common.util.concurrent.AbstractService;
/**
* Implements software parameters - these are parameters that can be set from the clients.
*
*
* All the parameters are send from the executor thread.
*
* @author nm
*
*/
public class SoftwareParameterManager extends AbstractService implements ParameterProvider {
ExecutorService executor = Executors.newFixedThreadPool(1);
ParameterRequestManager prm;
private NamedDescriptionIndex<Parameter> params = new NamedDescriptionIndex<>();
Set<Parameter> subscribedParams = new HashSet<>();
private static final Logger log=LoggerFactory.getLogger(SoftwareParameterManager.class);
final String yamcsInstance;
Processor yproc;
public SoftwareParameterManager(String yamcsInstance) {
this.yamcsInstance = yamcsInstance;
}
public void init(XtceDb xtcedb) {
for(Parameter p:xtcedb.getParameters()) {
if(p.getDataSource() == DataSource.LOCAL) {
params.add(p);
}
}
log.debug("Found {} local parameters", params.size());
}
@Override
public void init(Processor yproc) throws ConfigurationException {
init(yproc.getXtceDb());
this.yproc = yproc;
}
@Override
public void setParameterListener(ParameterRequestManager parameterListener) {
this.prm = parameterListener;
}
//called on the execution thread to update
// TODO: convert from raw to engineering values
private void doUpdate(final List<org.yamcs.protobuf.Pvalue.ParameterValue> gpvList) {
ParameterValueList pvlist = new ParameterValueList();
for(org.yamcs.protobuf.Pvalue.ParameterValue gpv: gpvList) {
Parameter p = getParam(gpv.getId());
if(subscribedParams.contains(p)) {
org.yamcs.parameter.ParameterValue pv = org.yamcs.parameter.ParameterValue.fromGpb(p, gpv);
long t;
if(yproc!=null) {
t=yproc.getCurrentTime();
} else {
t = TimeEncoding.getWallclockTime();
}
if(gpv.hasAcquisitionStatus()) {
pv.setAcquisitionStatus(AcquisitionStatus.ACQUIRED);
}
if(!gpv.hasGenerationTime()) {
pv.setGenerationTime(t);
}
if(!gpv.hasAcquisitionTime()) {
pv.setAcquisitionTime(t);
}
pvlist.add(pv);
}
}
if(pvlist.size()>0) {
prm.update(pvlist);
}
}
/**
* update the list of parameters.
* - resolves NamedObjectId -> Parameter
* - sends the result to PRM
*/
public void updateParameters(final List<org.yamcs.protobuf.Pvalue.ParameterValue> gpvList) {
//first validate that the names are sofware parameters and the types match
for(org.yamcs.protobuf.Pvalue.ParameterValue gpv: gpvList) {
Parameter p = getParam(gpv.getId());
if(p==null) {
throw new IllegalArgumentException("Cannot find a local(software) parameter for '"+gpv.getId()+"'");
}
ParameterTypeProcessor.checkEngValueAssignment(p, ValueUtility.fromGpb(gpv.getEngValue()));
}
//then filter out the subscribed ones and send it to PRM
executor.submit(() -> doUpdate(gpvList));
}
/**
* Updates a parameter just with the engineering value
*/
public void updateParameter(final Parameter p, final Value engValue) {
if(p.getDataSource()!=DataSource.LOCAL) {
throw new IllegalArgumentException("DataSource of parameter "+p.getQualifiedName()+" is not local");
}
ParameterTypeProcessor.checkEngValueAssignment(p, engValue);
executor.submit(new Runnable() {
@Override
public void run() {
ParameterValue pv = new ParameterValue(p);
pv.setEngineeringValue(engValue);
long t = yproc.getCurrentTime();
pv.setAcquisitionTime(t);
pv.setGenerationTime(t);
prm.update(Arrays.asList(pv));
}
});
}
@Override
public void startProviding(final Parameter paramDef) {
log.debug("requested to provide {}", paramDef.getQualifiedName());
executor.submit(new Runnable() {
@Override
public void run() {
subscribedParams.add(paramDef);
}
});
}
@Override
public void startProvidingAll() {
log.debug("requested to provide all");
executor.submit(new Runnable() {
@Override
public void run() {
for(Parameter p:params) {
subscribedParams.add(p);
}
}
});
}
@Override
public void stopProviding(final Parameter paramDef) {
log.debug("requested to stop providing {}", paramDef.getQualifiedName());
executor.submit(() -> subscribedParams.remove(paramDef));
}
@Override
public boolean canProvide(NamedObjectId paraId) {
return getParam(paraId) != null;
}
private Parameter getParam(NamedObjectId paraId) {
Parameter p;
if(paraId.hasNamespace()) {
p = params.get(paraId.getNamespace(), paraId.getName());
} else {
p = params.get(paraId.getName());
}
return p;
}
@Override
public Parameter getParameter(NamedObjectId paraId) throws InvalidIdentification {
Parameter p = getParam(paraId);
if(p==null) {
log.info("throwing InvalidIdentification becasue cannot provide {}", paraId);
throw new InvalidIdentification(paraId);
}
return p;
}
@Override
public boolean canProvide(Parameter param) {
return params.get(param.getQualifiedName())!=null;
}
@Override
protected void doStart() {
notifyStarted();
}
@Override
protected void doStop() {
executor.shutdown();
notifyStopped();
}
}