info.unterrainer.commons.opcuabrowser.parts.SubscriptionManager Maven / Gradle / Ivy
The newest version!
package info.unterrainer.commons.opcuabrowser.parts;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscriptionManager.SubscriptionListener;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import info.unterrainer.commons.jreutils.SetIntersection;
import info.unterrainer.commons.opcuabrowser.OpcUaClient;
import info.unterrainer.commons.opcuabrowser.dtos.ReadResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SubscriptionManager {
private final OpcUaClient client;
private final AtomicLong clientHandles = new AtomicLong(1L);
private static int counter = 0;
public SubscriptionManager(final OpcUaClient client) {
super();
this.client = client;
}
public OpcUaSubscriptionGroup createForSeconds(final int seconds, final int publishingInterval, final int samplingInterval,
final int queueSize, final Supplier> getNodeIds) {
OpcUaSubscriptionGroup sub = null;
try {
sub = create(publishingInterval, samplingInterval, queueSize, this::onSubscriptionValue, null, getNodeIds);
Thread.sleep(seconds * 1000);
client.deleteSubscription(sub.data().subscription().getSubscriptionId());
} catch (InterruptedException e) {
log.error("Creating subscriptions failed: [{}] cause: [{}]", e.getMessage(), e.getCause().getMessage());
}
return sub;
}
public OpcUaSubscriptionGroup create(final int publishingInterval, final int samplingInterval, final int queueSize,
final BiConsumer onSubscriptionValue,
final SubscriptionListener subscriptionListener, final Supplier> getNodeIds) {
OpcUaSubscriptionGroup group = new OpcUaSubscriptionGroup(null, null);
SubscriptionConfig config = SubscriptionConfig.builder()
.publishingInterval(publishingInterval)
.samplingInterval(samplingInterval)
.queueSize(queueSize)
.subscriptionListener(subscriptionListener)
.onSubscriptionValue(onSubscriptionValue)
.getNodeIds(getNodeIds)
.build();
group.config(config);
config.onSubscriptionTransferFailed((sub, statusCode) -> {
List monitoredItems = new ArrayList<>(
group.data().nodeIdToMonitoredItems().values());
sub.deleteMonitoredItems(monitoredItems);
UInteger subscriptionId = group.data().subscription().getSubscriptionId();
client.deleteSubscription(subscriptionId);
createSubscriptionGroup(group);
});
createSubscriptionGroup(group);
if (subscriptionListener != null)
client.addSubscriptionListener(subscriptionListener);
client.addReconnectListener(group.config().onSubscriptionTransferFailed());
return group;
}
private void createSubscriptionGroup(final OpcUaSubscriptionGroup group) {
try {
UaSubscription uaSubscription = client.createSubscription(group.config().publishingInterval());
group.data(SubscriptionData.builder()
.subscription(uaSubscription)
.clientHandleToNodeIds(new HashMap<>())
.nodeIdToMonitoredItems(new HashMap<>())
.build());
modifySubscriptionGroup(group);
} catch (InterruptedException | ExecutionException e) {
log.error("Creating subscriptions failed: [{}] cause: [{}]", e.getMessage(), e.getCause().getMessage());
}
}
public SetIntersection modifySubscriptionGroup(final OpcUaSubscriptionGroup group) {
SetIntersection intersect = null;
SubscriptionConfig config = group.config();
UaSubscription uaSubscription = group.data().subscription();
try {
intersect = SetIntersection.of(new HashSet<>(group.data().clientHandleToNodeIds().values()),
new HashSet<>(config.getNodeIds().get()));
Map newClientHandleToNodeIds = new HashMap<>();
for (Entry entrySet : group.data().clientHandleToNodeIds().entrySet())
if (intersect.getLeave().contains(entrySet.getValue()))
newClientHandleToNodeIds.put(entrySet.getKey(), entrySet.getValue());
List monitoredItemsToDelete = intersect.getDelete()
.stream()
.map(e -> group.data().nodeIdToMonitoredItems().get(e))
.collect(Collectors.toList());
if (!monitoredItemsToDelete.isEmpty())
group.data().subscription().deleteMonitoredItems(monitoredItemsToDelete).get();
List requests = new ArrayList<>();
for (String nodeId : intersect.getCreate()) {
UInteger clientHandle = UInteger.valueOf(clientHandles.getAndIncrement());
MonitoringParameters parameters = new MonitoringParameters(clientHandle,
(double) config.samplingInterval, null, UInteger.valueOf(config.queueSize), true);
ReadValueId readValueId = new ReadValueId(NodeId.parse(nodeId), AttributeId.Value.uid(), null, null);
MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId,
MonitoringMode.Reporting, parameters);
requests.add(request);
newClientHandleToNodeIds.put(clientHandle, nodeId);
}
if (!requests.isEmpty())
uaSubscription
.createMonitoredItems(TimestampsToReturn.Both, requests, (item, id) -> item
.setValueConsumer(dataValue -> config.onSubscriptionValue().accept(item, dataValue)))
.get();
List allItems = uaSubscription.getMonitoredItems();
Set allItemHandles = allItems.stream()
.map(UaMonitoredItem::getClientHandle)
.collect(Collectors.toSet());
List handlesToDelete = new ArrayList<>();
for (UInteger key : newClientHandleToNodeIds.keySet())
if (!allItemHandles.contains(key))
handlesToDelete.add(key);
for (UInteger key : handlesToDelete)
newClientHandleToNodeIds.remove(key);
group.data(group.data()
.clientHandleToNodeIds(newClientHandleToNodeIds)
.nodeIdToMonitoredItems(allItems.stream()
.collect(
Collectors.toMap(e -> newClientHandleToNodeIds.get(e.getClientHandle()), e -> e))));
for (UaMonitoredItem item : allItems) {
log.debug("Fetching first value for node: [{}]", item.getReadValueId().getNodeId());
callMethodFor(item, group.config().onSubscriptionValue());
}
} catch (InterruptedException | ExecutionException e) {
log.error("Modifying subscriptions failed: [{}] cause: [{}]", e.getMessage(), e.getCause().getMessage());
}
return intersect;
}
private void callMethodFor(final UaMonitoredItem item,
final BiConsumer onSubscriptionValue) {
ValueReader valueReader = new ValueReader(client);
try {
valueReader.readForReadValueIds(item.getReadValueId());
ReadResult r = valueReader.getResults().get(0);
DataValue value = new DataValue(new Variant(r.getValue()), StatusCode.GOOD);
onSubscriptionValue.accept(item, value);
} catch (InterruptedException | ExecutionException e) {
log.warn("Failed to retrieve initial value for: [{}] cause: [{}]", item.getReadValueId().getNodeId(),
e.getCause());
}
}
private void onSubscriptionValue(final UaMonitoredItem item, final DataValue value) {
log.debug("Subscription value recieved --- item:[{}] value:[{}] count:[{}]", item, value, ++counter);
}
}