/*
* Copyright 2016 Kevin Herron
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.digitalpetri.opcua.sdk.client.subscriptions;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import com.digitalpetri.opcua.sdk.client.OpcUaClient;
import com.digitalpetri.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
import com.digitalpetri.opcua.sdk.client.api.subscriptions.UaSubscription;
import com.digitalpetri.opcua.stack.core.types.builtin.StatusCode;
import com.digitalpetri.opcua.stack.core.types.builtin.unsigned.UByte;
import com.digitalpetri.opcua.stack.core.types.builtin.unsigned.UInteger;
import com.digitalpetri.opcua.stack.core.types.enumerated.MonitoringMode;
import com.digitalpetri.opcua.stack.core.types.enumerated.TimestampsToReturn;
import com.digitalpetri.opcua.stack.core.types.structured.ModifyMonitoredItemsResponse;
import com.digitalpetri.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import com.digitalpetri.opcua.stack.core.types.structured.MonitoredItemCreateResult;
import com.digitalpetri.opcua.stack.core.types.structured.MonitoredItemModifyRequest;
import com.digitalpetri.opcua.stack.core.types.structured.MonitoredItemModifyResult;
import com.digitalpetri.opcua.stack.core.types.structured.SetMonitoringModeResponse;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import static com.digitalpetri.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
import static com.google.common.collect.Lists.newArrayList;
public class OpcUaSubscription implements UaSubscription {
private final Map<UInteger, OpcUaMonitoredItem> itemsByClientHandle = Maps.newConcurrentMap();
private final Map<UInteger, OpcUaMonitoredItem> itemsByServerHandle = Maps.newConcurrentMap();
private volatile long lastSequenceNumber = 0L;
private volatile double revisedPublishingInterval = 0.0;
private volatile UInteger revisedLifetimeCount = uint(0);
private volatile UInteger revisedMaxKeepAliveCount = uint(0);
private volatile UInteger maxNotificationsPerPublish;
private volatile boolean publishingEnabled;
private volatile UByte priority;
private final OpcUaClient client;
private final UInteger subscriptionId;
public OpcUaSubscription(OpcUaClient client, UInteger subscriptionId,
double revisedPublishingInterval,
UInteger revisedLifetimeCount,
UInteger revisedMaxKeepAliveCount,
UInteger maxNotificationsPerPublish,
boolean publishingEnabled,
UByte priority) {
this.client = client;
this.subscriptionId = subscriptionId;
this.revisedPublishingInterval = revisedPublishingInterval;
this.revisedLifetimeCount = revisedLifetimeCount;
this.revisedMaxKeepAliveCount = revisedMaxKeepAliveCount;
this.maxNotificationsPerPublish = maxNotificationsPerPublish;
this.publishingEnabled = publishingEnabled;
this.priority = priority;
}
@Override
public CompletableFuture<List<UaMonitoredItem>> createMonitoredItems(TimestampsToReturn timestampsToReturn,
List<MonitoredItemCreateRequest> itemsToCreate) {
return client.createMonitoredItems(
subscriptionId,
timestampsToReturn,
itemsToCreate).thenApply(response -> {
List<UaMonitoredItem> createdItems = newArrayList();
MonitoredItemCreateResult[] results = response.getResults();
for (int i = 0; i < itemsToCreate.size(); i++) {
MonitoredItemCreateRequest request = itemsToCreate.get(i);
MonitoredItemCreateResult result = results[i];
OpcUaMonitoredItem item = new OpcUaMonitoredItem(
request.getRequestedParameters().getClientHandle(),
request.getItemToMonitor(),
result.getMonitoredItemId(),
result.getStatusCode(),
result.getRevisedSamplingInterval(),
result.getRevisedQueueSize(),
result.getFilterResult(),
request.getMonitoringMode());
if (item.getStatusCode().isGood()) {
itemsByClientHandle.put(item.getClientHandle(), item);
itemsByServerHandle.put(item.getMonitoredItemId(), item);
}
createdItems.add(item);
}
return createdItems;
});
}
@Override
public CompletableFuture<List<StatusCode>> modifyMonitoredItems(TimestampsToReturn timestampsToReturn,
List<MonitoredItemModifyRequest> itemsToModify) {
CompletableFuture<ModifyMonitoredItemsResponse> future =
client.modifyMonitoredItems(subscriptionId, timestampsToReturn, itemsToModify);
return future.thenApply(response -> {
List<StatusCode> statusCodes = newArrayList();
MonitoredItemModifyResult[] results = response.getResults();
for (int i = 0; i < results.length; i++) {
MonitoredItemModifyRequest request = itemsToModify.get(i);
MonitoredItemModifyResult result = results[i];
StatusCode statusCode = result.getStatusCode();
OpcUaMonitoredItem item = itemsByServerHandle.get(request.getMonitoredItemId());
if (item != null) {
item.setStatusCode(statusCode);
item.setRevisedSamplingInterval(result.getRevisedSamplingInterval());
item.setRevisedQueueSize(result.getRevisedQueueSize());
item.setFilterResult(result.getFilterResult());
}
statusCodes.add(statusCode);
}
return statusCodes;
});
}
@Override
public CompletableFuture<List<StatusCode>> deleteMonitoredItems(List<UaMonitoredItem> itemsToDelete) {
List<UInteger> monitoredItemIds = itemsToDelete.stream()
.map(UaMonitoredItem::getMonitoredItemId)
.collect(Collectors.toList());
return client.deleteMonitoredItems(subscriptionId, monitoredItemIds).thenApply(response -> {
StatusCode[] results = response.getResults();
for (UaMonitoredItem item : itemsToDelete) {
itemsByClientHandle.remove(item.getClientHandle());
itemsByServerHandle.remove(item.getMonitoredItemId());
}
return Arrays.asList(results);
});
}
@Override
public CompletableFuture<List<StatusCode>> setMonitoringMode(MonitoringMode monitoringMode,
List<UaMonitoredItem> items) {
List<UInteger> monitoredItemIds = items.stream()
.map(UaMonitoredItem::getMonitoredItemId)
.collect(Collectors.toList());
CompletableFuture<SetMonitoringModeResponse> future =
client.setMonitoringMode(subscriptionId, monitoringMode, monitoredItemIds);
return future.thenApply(response -> {
StatusCode[] results = response.getResults();
for (int i = 0; i < monitoredItemIds.size(); i++) {
UInteger id = monitoredItemIds.get(i);
OpcUaMonitoredItem item = itemsByServerHandle.get(id);
StatusCode result = results[i];
if (result.isGood() && item != null) {
item.setMonitoringMode(monitoringMode);
}
}
return Arrays.asList(results);
});
}
@Override
public CompletableFuture<StatusCode> setPublishingMode(boolean publishingEnabled) {
return client.setPublishingMode(publishingEnabled, newArrayList(subscriptionId))
.thenApply(response -> {
StatusCode statusCode = response.getResults()[0];
if (statusCode.isGood()) {
setPublishingEnabled(publishingEnabled);
}
return statusCode;
});
}
@Override
public UInteger getSubscriptionId() {
return subscriptionId;
}
@Override
public double getRevisedPublishingInterval() {
return revisedPublishingInterval;
}
@Override
public UInteger getRevisedLifetimeCount() {
return revisedLifetimeCount;
}
@Override
public UInteger getRevisedMaxKeepAliveCount() {
return revisedMaxKeepAliveCount;
}
@Override
public UInteger getMaxNotificationsPerPublish() {
return maxNotificationsPerPublish;
}
@Override
public boolean isPublishingEnabled() {
return publishingEnabled;
}
@Override
public UByte getPriority() {
return priority;
}
@Override
public ImmutableList<UaMonitoredItem> getMonitoredItems() {
return ImmutableList.copyOf(itemsByClientHandle.values());
}
Map<UInteger, OpcUaMonitoredItem> getItemsByClientHandle() {
return itemsByClientHandle;
}
Map<UInteger, OpcUaMonitoredItem> getItemsByServerHandle() {
return itemsByServerHandle;
}
long getLastSequenceNumber() {
return lastSequenceNumber;
}
void setRevisedPublishingInterval(double revisedPublishingInterval) {
this.revisedPublishingInterval = revisedPublishingInterval;
}
void setRevisedLifetimeCount(UInteger revisedLifetimeCount) {
this.revisedLifetimeCount = revisedLifetimeCount;
}
void setRevisedMaxKeepAliveCount(UInteger revisedMaxKeepAliveCount) {
this.revisedMaxKeepAliveCount = revisedMaxKeepAliveCount;
}
void setMaxNotificationsPerPublish(UInteger maxNotificationsPerPublish) {
this.maxNotificationsPerPublish = maxNotificationsPerPublish;
}
void setPublishingEnabled(boolean publishingEnabled) {
this.publishingEnabled = publishingEnabled;
}
void setPriority(UByte priority) {
this.priority = priority;
}
void setLastSequenceNumber(long lastSequenceNumber) {
this.lastSequenceNumber = lastSequenceNumber;
}
}