All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy