blob: 8c9de7bfb36a6e441a1090312d7011c0aa1aa81c [file] [log] [blame]
package org.eclipse.basyx.wrapper.receiver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.basyx.aas.metamodel.map.descriptor.AASDescriptor;
import org.eclipse.basyx.aas.metamodel.map.descriptor.SubmodelDescriptor;
import org.eclipse.basyx.aas.registration.api.IAASRegistryService;
import org.eclipse.basyx.submodel.metamodel.api.identifier.IIdentifier;
import org.eclipse.basyx.submodel.metamodel.api.identifier.IdentifierType;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.dataelement.IProperty;
import org.eclipse.basyx.submodel.metamodel.connected.submodelelement.dataelement.ConnectedProperty;
import org.eclipse.basyx.submodel.metamodel.map.identifier.Identifier;
import org.eclipse.basyx.submodel.restapi.MultiSubmodelElementProvider;
import org.eclipse.basyx.vab.factory.java.ModelProxyFactory;
import org.eclipse.basyx.vab.modelprovider.VABPathTools;
import org.eclipse.basyx.vab.protocol.http.connector.HTTPConnectorProvider;
import org.eclipse.basyx.wrapper.exception.WrapperRequestException;
import org.eclipse.basyx.wrapper.receiver.configuration.AASPropertyConfiguration;
import org.eclipse.basyx.wrapper.receiver.configuration.PropertyConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Wrapper service for seperately querying the configured properties.
*
* @author espen
*
*/
public class SeparateAASService implements IPropertyWrapperService {
private static final Logger logger = LoggerFactory.getLogger(SeparateAASService.class);
// Registry
protected IAASRegistryService registry;
// Property configurations
protected Map<String, String> aasIds = new HashMap<>();
protected Map<String, String> submodelIds = new HashMap<>();
protected Map<String, String> shortIds = new HashMap<>();
protected Map<String, Integer> maxValues = new HashMap<>();
protected Set<String> activeProperties = new HashSet<>();
// Current data
protected Map<String, PropertyResult> propertyResults = new HashMap<>();
// Additional
protected Map<String, String> submodelCachedEndpoints = new HashMap<>();
protected Map<String, Thread> threads = new HashMap<>();
protected List<IWrapperListener> listeners = new ArrayList<>();
public SeparateAASService(IAASRegistryService registry, Collection<PropertyConfiguration> configs) {
this.registry = registry;
configs.forEach(this::addPropertyConfig);
}
@Override
public void addPropertyConfig(PropertyConfiguration config) {
if (config instanceof AASPropertyConfiguration) {
AASPropertyConfiguration aasConfig = (AASPropertyConfiguration) config;
String propId = config.getId();
logger.info("Initialize " + propId);
aasIds.put(propId, aasConfig.getAAS());
submodelIds.put(propId, aasConfig.getSubmodel());
shortIds.put(propId, aasConfig.getShortId());
propertyResults.put(propId, new PropertyResult(propId, config.getMaxValues()));
if (config.getActive()) {
activeProperties.add(propId);
int interval = config.getInterval(); // => in ms
threads.put(propId, createPropertyThread(propId, interval));
}
}
}
private AASDescriptor getAASDescriptorFromRegistry(String aasId) {
// Assume custom AAS identifier => aas identifier type doesn't really matter for lookup
IIdentifier aasIdentifier = new Identifier(IdentifierType.CUSTOM, aasId);
return registry.lookupAAS(aasIdentifier);
}
private String getSubmodelEndpoint(String aasId, String submodelId) {
String aasSmId = aasId + "::" + submodelId;
String smEndpoint = submodelCachedEndpoints.get(aasSmId);
if (smEndpoint == null) {
// Retrieve AAS descriptor from the registry to parse the submodel endpoint
try {
AASDescriptor aasDescriptor = getAASDescriptorFromRegistry(aasId);
SubmodelDescriptor smDescriptor = aasDescriptor.getSubmodelDescriptorFromIdShort(submodelId);
if (smDescriptor == null) {
throw new WrapperRequestException("Could not look up descriptor for SubModel '" + submodelId
+ "' since it does not exist in AAS '" + aasId + "'");
}
smEndpoint = smDescriptor.getFirstEndpoint();
// Cache retrieved submodel endpoints
submodelCachedEndpoints.put(aasSmId, smEndpoint);
} catch (Exception e) {
throw new WrapperRequestException("Could not look up descriptor for AAS '" + aasId
+ "' since it does not exist or is not available");
}
}
return smEndpoint;
}
private IProperty getConnectedProperty(String smEndpoint, String shortId) {
// Build the property address from that (and assume it's there)
String propEndpoint = VABPathTools.concatenatePaths(smEndpoint, MultiSubmodelElementProvider.ELEMENTS, shortId);
// Create a "ConnectedProperty" that gives access to the property
ModelProxyFactory mpf = new ModelProxyFactory(new HTTPConnectorProvider());
return new ConnectedProperty(mpf.createProxy(propEndpoint));
}
private Object getSinglePropertyValue(String propId) {
try {
String aasId = aasIds.get(propId);
String submodelId = submodelIds.get(propId);
String shortId = shortIds.get(propId);
String smEndpoint = getSubmodelEndpoint(aasId, submodelId);
IProperty property = getConnectedProperty(smEndpoint, shortId);
return property.getValue();
} catch (Exception e) {
throw new WrapperRequestException(e.getLocalizedMessage(), e);
}
}
@Override
public PropertyResult getPropertyValue(String propId) {
// Clone a result to prevent changes in the returned value
return new PropertyResult(propertyResults.get(propId));
}
@Override
public void setPropertyValue(String propId, Object newValue) {
try {
String aasId = aasIds.get(propId);
String submodelId = submodelIds.get(propId);
String shortId = shortIds.get(propId);
String smEndpoint = getSubmodelEndpoint(aasId, submodelId);
IProperty property = getConnectedProperty(smEndpoint, shortId);
property.setValue(newValue);
} catch (Exception e) {
throw new WrapperRequestException(e.getLocalizedMessage(), e);
}
}
@Override
public void generatePassiveValue(String propId) {
if (activeProperties.contains(propId)) {
throw new WrapperRequestException("Property with id '" + propId + "' is active");
}
retrieveValue(propId);
}
private Thread createPropertyThread(final String propId, final int intervalTime) {
return new Thread(() -> {
Thread thisThread = Thread.currentThread();
while (thisThread == threads.get(propId)) {
retrieveValue(propId);
try {
Thread.sleep(intervalTime);
} catch (InterruptedException e) {
}
}
});
}
private void retrieveValue(String propId) {
PropertyResult values = propertyResults.get(propId);
if (values == null) {
throw new WrapperRequestException("Property with id '" + propId + "' does not exist");
}
Object newValue = getSinglePropertyValue(propId);
DataPoint dataPoint = new DataPoint(newValue);
values.addDataPoint(dataPoint);
// inform listeners
for (IWrapperListener listener : listeners) {
listener.newValue(propId, new PropertyResult(values));
}
}
@Override
public void start() {
logger.info("Starting " + threads.size() + " threads");
for (Thread t : threads.values()) {
t.start();
}
}
@Override
public void stop() {
threads.clear();
}
@Override
public void addProxyListener(IWrapperListener listener) {
this.listeners.add(listener);
}
@Override
public Set<String> getValidProperties() {
return propertyResults.keySet();
}
}