blob: c01fbd94d46ce6a63cb1fbee83d7aa4b74a733af [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2021 the Eclipse BaSyx Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/
package org.eclipse.basyx.submodel.metamodel.map.submodelelement.operation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import org.eclipse.basyx.aas.metamodel.exception.MetamodelConstructionException;
import org.eclipse.basyx.submodel.metamodel.api.reference.IReference;
import org.eclipse.basyx.submodel.metamodel.api.reference.enums.KeyElements;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.operation.IAsyncInvocation;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.operation.IOperation;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.operation.IOperationVariable;
import org.eclipse.basyx.submodel.metamodel.map.modeltype.ModelType;
import org.eclipse.basyx.submodel.metamodel.map.qualifier.HasDataSpecification;
import org.eclipse.basyx.submodel.metamodel.map.qualifier.LangStrings;
import org.eclipse.basyx.submodel.metamodel.map.qualifier.Referable;
import org.eclipse.basyx.submodel.metamodel.map.submodelelement.SubmodelElement;
import org.eclipse.basyx.vab.exception.provider.WrongNumberOfParametersException;
/**
* Operation as defined in DAAS document <br/>
* An operation is a submodel element with input and output variables.
*
* @author schnicke
*
*/
public class Operation extends SubmodelElement implements IOperation {
// Default timeout for asynchronous operation calls
public static final int DEFAULT_ASYNC_TIMEOUT = 10000;
public static final String IN = "inputVariables";
public static final String OUT = "outputVariables";
public static final String INOUT = "inoutputVariables";
public static final String INVOKABLE = "invokable";
public static final String MODELTYPE = "Operation";
public static final String INVOKE = "invoke";
/**
* Constructor
*/
public Operation() {
// Add model type
putAll(new ModelType(MODELTYPE));
// Input variables
put(IN, new ArrayList<OperationVariable>());
// Output variables
put(OUT, new ArrayList<OperationVariable>());
// Variables, that are input and output
put(INOUT, new ArrayList<OperationVariable>());
}
/**
* Constructor accepting only mandatory attribute
* @param idShort
*/
public Operation(String idShort) {
super(idShort);
// Add model type
putAll(new ModelType(MODELTYPE));
// Input variables
setInputVariables(new ArrayList<OperationVariable>());
// Output variables
setOutputVariables(new ArrayList<OperationVariable>());
// Variables, that are input and output
setInOutputVariables(new ArrayList<OperationVariable>());
}
/**
*
* @param in
* Input parameter of the operation.
* @param out
* Output parameter of the operation.
* @param inout
* Inoutput parameter of the operation.
* @param function
* the concrete function
*
*/
public Operation(Collection<OperationVariable> in, Collection<OperationVariable> out,
Collection<OperationVariable> inout, Function<Object[], Object> function) {
// Add model type
putAll(new ModelType(MODELTYPE));
// Input variables
put(IN, in);
// Output variables
put(OUT, out);
// Output variables
put(INOUT, inout);
// Extension of DAAS specification for function storage
put(INVOKABLE, function);
}
/**
* Create Operations w/o endpoint
*
* @param operation
* @param function
* @return
*/
public Operation(Function<Object[], Object> function) {
this();
setInvocable(function);
}
/**
* Creates an Operation object from a map
*
* @param obj
* an Operation object as raw map
* @return an Operation object, that behaves like a facade for the given map
*/
public static Operation createAsFacade(Map<String, Object> obj) {
if (obj == null) {
return null;
}
if (!isValid(obj)) {
throw new MetamodelConstructionException(Operation.class, obj);
}
Operation ret = new Operation();
ret.setMap(obj);
return ret;
}
/**
* Check whether all mandatory elements for the metamodel
* exist in a map
* @return true/false
*/
public static boolean isValid(Map<String, Object> obj) {
return SubmodelElement.isValid(obj);
}
/**
* Returns true if the given submodel element map is recognized as an operation
*/
@SuppressWarnings("unchecked")
public static boolean isOperation(Object value) {
if(!(value instanceof Map<?, ?>)) {
return false;
}
Map<String, Object> map = (Map<String, Object>) value;
String modelType = ModelType.createAsFacade(map).getName();
// Either model type is set or the element type specific attributes are contained
return MODELTYPE.equals(modelType)
|| (modelType == null && (map.containsKey(IN) && map.containsKey(OUT) && map.containsKey(INOUT)));
}
@Override
public Collection<IOperationVariable> getInputVariables() {
return transformToOperationVariables(get(Operation.IN));
}
@Override
public Collection<IOperationVariable> getOutputVariables() {
return transformToOperationVariables(get(Operation.OUT));
}
@Override
public Collection<IOperationVariable> getInOutputVariables() {
return transformToOperationVariables(get(Operation.INOUT));
}
@SuppressWarnings("unchecked")
private Collection<IOperationVariable> transformToOperationVariables(Object obj) {
if (obj instanceof Collection<?>) {
Collection<Map<String, Object>> map = (Collection<Map<String, Object>>) obj;
Collection<IOperationVariable> ret = new ArrayList<>();
for (Map<String, Object> m : map) {
ret.add(OperationVariable.createAsFacade(m));
}
return ret;
} else {
return new ArrayList<>();
}
}
@SuppressWarnings("unchecked")
@Override
public Object invoke(Object... params) {
if (params.length != getInputVariables().size()) {
throw new WrongNumberOfParametersException(getIdShort(), getInputVariables(), params);
}
return ((Function<Object[], Object>) get(INVOKABLE)).apply(params);
}
@Override
public SubmodelElement[] invoke(SubmodelElement... elems) {
throw new UnsupportedOperationException(
"SubmodelElement matching logic is only supported for connected Operations");
}
@Override
public AsyncInvocation invokeAsync(Object... params) {
return new AsyncInvocation(this, 10000, params);
}
@Override
public IAsyncInvocation invokeAsyncWithTimeout(int timeout, Object... params) {
return new AsyncInvocation(this, timeout, params);
}
public void setInputVariables(Collection<OperationVariable> in) {
put(Operation.IN, in);
}
public void setOutputVariables(Collection<OperationVariable> out) {
put(Operation.OUT, out);
}
public void setInOutputVariables(Collection<OperationVariable> inOut) {
put(Operation.INOUT, inOut);
}
public void setInvocable(Function<Object[], Object> endpoint) {
put(Operation.INVOKABLE, endpoint);
}
@Override
public Collection<IReference> getDataSpecificationReferences() {
return HasDataSpecification.createAsFacade(this).getDataSpecificationReferences();
}
@Override
public void setDataSpecificationReferences(Collection<IReference> ref) {
HasDataSpecification.createAsFacade(this).setDataSpecificationReferences(ref);
}
@Override
public String getIdShort() {
return Referable.createAsFacade(this, getKeyElement()).getIdShort();
}
@Override
public String getCategory() {
return Referable.createAsFacade(this, getKeyElement()).getCategory();
}
@Override
public LangStrings getDescription() {
return Referable.createAsFacade(this, getKeyElement()).getDescription();
}
@Override
protected KeyElements getKeyElement() {
return KeyElements.OPERATION;
}
@Override
public Object getValue() {
throw new UnsupportedOperationException("An Operation has no value");
}
@Override
public void setValue(Object value) {
throw new UnsupportedOperationException("An Operation has no value");
}
@Override
public Operation getLocalCopy() {
// Create a shallow copy
Operation copy = new Operation();
copy.putAll(this);
// Copy InputVariables
Collection<IOperationVariable> inVars = copy.getInputVariables();
Collection<OperationVariable> inVarCopy = new ArrayList<>();
inVars.stream().forEach(v -> inVarCopy.add(new OperationVariable(v.getValue().getLocalCopy())));
copy.setInputVariables(inVarCopy);
// Copy OutputVariables
Collection<IOperationVariable> outVars = copy.getOutputVariables();
Collection<OperationVariable> outVarCopy = new ArrayList<>();
outVars.stream().forEach(v -> outVarCopy.add(new OperationVariable(v.getValue().getLocalCopy())));
copy.setOutputVariables(outVarCopy);
// Copy Input/Output-Variables
Collection<IOperationVariable> inoutVars = copy.getInOutputVariables();
Collection<OperationVariable> inoutVarCopy = new ArrayList<>();
inoutVars.stream().forEach(v -> inoutVarCopy.add(new OperationVariable(v.getValue().getLocalCopy())));
copy.setInOutputVariables(inoutVarCopy);
return copy;
}
}