/**
* Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
*/
package org.diirt.datasource.formula.channel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import org.diirt.datasource.expression.DesiredRateExpression;
import org.diirt.datasource.formula.DynamicFormulaFunction;
import org.diirt.vtype.VStringArray;
import org.diirt.vtype.VTable;
import org.diirt.vtype.VType;
import org.diirt.vtype.table.VTableFactory;
/**
* Formula function that accepts a list of strings and returns a table where
* each row is the value of the channel matching the name.
*
* @author carcassi
*/
public class ChannelsFormulaFunction extends DynamicFormulaFunction {
@Override
public boolean isVarArgs() {
return false;
}
@Override
public String getName() {
return "channel";
}
@Override
public String getDescription() {
return "Returns a table with the values of the given pv names";
}
@Override
public List<Class<?>> getArgumentTypes() {
return Arrays.<Class<?>>asList(VStringArray.class);
}
@Override
public List<String> getArgumentNames() {
return Arrays.asList("pvNames");
}
@Override
public Class<?> getReturnType() {
return VTable.class;
}
private List<String> previousNames;
private List<DesiredRateExpression<?>> currentExpressions;
Object calculateImpl(final List<String> newNames) {
// If the name does not match, disconnect and connect
if (!Objects.equals(newNames, previousNames)) {
List<DesiredRateExpression<?>> newExpressions = new ArrayList<>();
if (newNames != null) {
newExpressions.addAll(Collections.nCopies(newNames.size(), (DesiredRateExpression<?>) null));
}
// Iterate throgh the previous names, and extract
// the expressions that match the new names
if (previousNames != null) {
if (newNames != null) {
for (int previousIndex = 0; previousIndex < previousNames.size(); previousIndex++) {
int newIndex = newNames.indexOf(previousNames.get(previousIndex));
if (newIndex != -1) {
newExpressions.set(newIndex, currentExpressions.get(previousIndex));
currentExpressions.set(previousIndex, null);
}
}
}
// Disconnect previous expressions no longer used
for (DesiredRateExpression<?> desiredRateExpression : currentExpressions) {
if (desiredRateExpression != null) {
getDirector().disconnectReadExpression(desiredRateExpression);
}
}
}
// Connect new expressions
if (newNames != null) {
for (int i = 0; i < newNames.size(); i++) {
if (newNames.get(i) != null && newExpressions.get(i) == null) {
DesiredRateExpression<?> newExpression = channel(newNames.get(i), Object.class);
getDirector().connectReadExpression(newExpression);
newExpressions.set(i, newExpression);
}
}
}
previousNames = newNames;
currentExpressions = newExpressions;
}
// Return value
if (newNames == null) {
return null;
}
// Extract values
List<VType> values = new ArrayList<>();
for (DesiredRateExpression<?> desiredRateExpression : currentExpressions) {
if (desiredRateExpression != null) {
Object value = desiredRateExpression.getFunction().readValue();
if (value != null && !(value instanceof VType)) {
throw new IllegalArgumentException("Only VTypes allowed in value tables");
} else {
values.add((VType) value);
}
}
}
return VTableFactory.valueTable(previousNames, values);
}
@Override
public Object calculate(final List<Object> args) {
// Retrieve the new names
VStringArray value = (VStringArray) args.get(0);
List<String> newNames = null;
if (value != null) {
newNames = value.getData();
}
return calculateImpl(newNames);
}
@Override
public void dispose() {
// Disconnect everything on dispose
if (currentExpressions != null) {
for (DesiredRateExpression<?> desiredRateExpression : new HashSet<>(currentExpressions)) {
getDirector().disconnectReadExpression(desiredRateExpression);
}
}
currentExpressions = null;
previousNames = null;
}
}