blob: 78b196b9bb97f36f652ce9bd83da4cae6045a6af [file] [log] [blame]
package org.eclipse.basyx.extensions.submodel.mqtt;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.basyx.extensions.shared.mqtt.MqttEventService;
import org.eclipse.basyx.submodel.metamodel.api.ISubModel;
import org.eclipse.basyx.submodel.metamodel.api.reference.IKey;
import org.eclipse.basyx.submodel.metamodel.api.reference.IReference;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.ISubmodelElement;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.operation.IOperation;
import org.eclipse.basyx.submodel.restapi.api.ISubmodelAPI;
import org.eclipse.basyx.vab.modelprovider.VABPathTools;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation variant for the SubmodelAPI that triggers MQTT events for
* different CRUD operations on the submodel. Has to be based on a backend
* implementation of the ISubmodelAPI to forward its method calls.
*
* @author espen
*
*/
public class MqttSubmodelAPI extends MqttEventService implements ISubmodelAPI {
private static Logger logger = LoggerFactory.getLogger(MqttSubmodelAPI.class);
// List of topics
public static final String TOPIC_CREATESUBMODEL = "BaSyxSubmodel_createdSubmodel";
public static final String TOPIC_ADDELEMENT = "BaSyxSubmodel_addedSubmodelElement";
public static final String TOPIC_DELETEELEMENT = "BaSyxSubmodel_removedSubmodelElement";
public static final String TOPIC_UPDATEELEMENT = "BaSyxSubmodel_updatedSubmodelElement";
// The underlying SubmodelAPI
protected ISubmodelAPI observedAPI;
// Submodel Element whitelist for filtering
protected boolean useWhitelist = false;
protected Set<String> whitelist = new HashSet<>();
/**
* Constructor for adding this MQTT extension on top of another SubmodelAPI
*
* @param observedAPI The underlying submodelAPI
* @throws MqttException
*/
public MqttSubmodelAPI(ISubmodelAPI observedAPI, String serverEndpoint, String clientId) throws MqttException {
super(serverEndpoint, clientId);
logger.info("Create new MQTT submodel for endpoint " + serverEndpoint);
this.observedAPI = observedAPI;
sendMqttMessage(TOPIC_CREATESUBMODEL, observedAPI.getSubmodel().getIdentification().getId());
}
/**
* Constructor for adding this MQTT extension on top of another SubmodelAPI
*
* @param observedAPI The underlying submodelAPI
* @throws MqttException
*/
public MqttSubmodelAPI(ISubmodelAPI observedAPI, String serverEndpoint, String clientId, String user, char[] pw)
throws MqttException {
super(serverEndpoint, clientId, user, pw);
logger.info("Create new MQTT submodel for endpoint " + serverEndpoint);
this.observedAPI = observedAPI;
sendMqttMessage(TOPIC_CREATESUBMODEL, observedAPI.getSubmodel().getIdentification().getId());
}
/**
* Constructor for adding this MQTT extension on top of another SubmodelAPI.
*
* @param observedAPI The underlying submodelAPI
* @param client An already connected mqtt client
* @throws MqttException
*/
public MqttSubmodelAPI(ISubmodelAPI observedAPI, MqttClient client) throws MqttException {
super(client);
this.observedAPI = observedAPI;
sendMqttMessage(TOPIC_CREATESUBMODEL, observedAPI.getSubmodel().getIdentification().getId());
}
/**
* Adds a submodel element to the filter whitelist. Can also be a path for nested submodel elements.
*
* @param element
*/
public void observeSubmodelElement(String shortId) {
whitelist.add(VABPathTools.stripSlashes(shortId));
}
/**
* Sets a new filter whitelist.
*
* @param element
*/
public void setWhitelist(Set<String> shortIds) {
this.whitelist.clear();
for (String entry : shortIds) {
this.whitelist.add(VABPathTools.stripSlashes(entry));
}
}
/**
* Disables the submodel element filter whitelist
*
* @param element
*/
public void disableWhitelist() {
useWhitelist = false;
}
/**
* Enables the submodel element filter whitelist
*
* @param element
*/
public void enableWhitelist() {
useWhitelist = true;
}
@Override
public ISubModel getSubmodel() {
return observedAPI.getSubmodel();
}
@Override
public void addSubmodelElement(ISubmodelElement elem) {
observedAPI.addSubmodelElement(elem);
if (filter(elem.getIdShort())) {
sendMqttMessage(TOPIC_ADDELEMENT, getCombinedMessage(getAASId(), getSubmodelId(), elem.getIdShort()));
}
}
@Override
public void addSubmodelElement(String idShortPath, ISubmodelElement elem) {
observedAPI.addSubmodelElement(idShortPath, elem);
if (filter(idShortPath)) {
sendMqttMessage(TOPIC_ADDELEMENT, getCombinedMessage(getAASId(), getSubmodelId(), idShortPath));
}
}
@Override
public ISubmodelElement getSubmodelElement(String idShortPath) {
return observedAPI.getSubmodelElement(idShortPath);
}
@Override
public void deleteSubmodelElement(String idShortPath) {
observedAPI.deleteSubmodelElement(idShortPath);
if (filter(idShortPath)) {
sendMqttMessage(TOPIC_DELETEELEMENT, getCombinedMessage(getAASId(), getSubmodelId(), idShortPath));
}
}
@Override
public Collection<IOperation> getOperations() {
return observedAPI.getOperations();
}
@Override
public Collection<ISubmodelElement> getSubmodelElements() {
return observedAPI.getSubmodelElements();
}
@Override
public void updateSubmodelElement(String idShortPath, Object newValue) {
observedAPI.updateSubmodelElement(idShortPath, newValue);
if (filter(idShortPath)) {
sendMqttMessage(TOPIC_UPDATEELEMENT, getCombinedMessage(getAASId(), getSubmodelId(), idShortPath));
}
}
@Override
public Object getSubmodelElementValue(String idShortPath) {
return observedAPI.getSubmodelElementValue(idShortPath);
}
@Override
public Object invokeOperation(String idShortPath, Object... params) {
return observedAPI.invokeOperation(idShortPath, params);
}
@Override
public Object invokeAsync(String idShortPath, Object... params) {
return observedAPI.invokeAsync(idShortPath, params);
}
@Override
public Object getOperationResult(String idShort, String requestId) {
return observedAPI.getOperationResult(idShort, requestId);
}
public static String getCombinedMessage(String aasId, String submodelId, String elementPart) {
elementPart = VABPathTools.stripSlashes(elementPart);
return "(" + aasId + "," + submodelId + "," + elementPart + ")";
}
private boolean filter(String idShort) {
idShort = VABPathTools.stripSlashes(idShort);
return !useWhitelist || whitelist.contains(idShort);
}
private String getSubmodelId() {
ISubModel submodel = getSubmodel();
return submodel.getIdentification().getId();
}
private String getAASId() {
ISubModel submodel = getSubmodel();
IReference parentReference = submodel.getParent();
if (parentReference != null) {
List<IKey> keys = parentReference.getKeys();
if (keys != null && keys.size() > 0) {
return keys.get(0).getValue();
}
}
return null;
}
}