| 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(); |
| } |
| } |