code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.sdk.client.api.services;
  
  import java.util.Collections;
  import java.util.List;
  import java.util.concurrent.CompletableFuture;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  
  import com.google.common.collect.Streams;
  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.QualifiedName;
  import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
  import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
  import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryReadDetails;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryReadResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryReadValueId;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryUpdateDetails;
  import org.eclipse.milo.opcua.stack.core.types.structured.HistoryUpdateResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReadResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
  import org.eclipse.milo.opcua.stack.core.types.structured.WriteResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
  
  import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
  import static org.eclipse.milo.opcua.stack.core.util.ConversionUtil.l;
  
  public interface AttributeServices {
  
      /**
       * This service is used to read one or more attributes of one or more nodes.
       *
       * @param maxAge             the requested max age of the value, in milliseconds. If maxAge is set to 0, the Server
       *                           shall attempt to read a new value from the data source. If maxAge is set to the max
       *                           Int32 value or greater, the Server shall attempt to get a cached value. Negative values
       *                           are invalid for maxAge.
       * @param timestampsToReturn the requested {@link TimestampsToReturn}.
       * @param readValueIds       the {@link ReadValueId}s identifying the nodes and attributes to read.
       * @return {@link CompletableFuture} containing the {@link ReadResponse}.
       */
      CompletableFuture<ReadResponse> read(double maxAge,
                                           TimestampsToReturn timestampsToReturn,
                                           List<ReadValueId> readValueIds);
  
      /**
       * This service is used to read one or more attributes of one or more nodes.
       *
       * @param maxAge             the requested max age of the value, in milliseconds. If maxAge is set to 0, the Server
       *                           shall attempt to read a new value from the data source. If maxAge is set to the max
       *                           Int32 value or greater, the Server shall attempt to get a cached value. Negative values
       *                           are invalid for maxAge.
       * @param timestampsToReturn the requested {@link TimestampsToReturn}.
       * @param nodeIds            the {@link NodeId}s identifying the nodes to read.
       * @param attributeIds       the attribute ids to read, the size and order matching the provided {@link NodeId}s.
       * @return a {@link CompletableFuture} containing a list of {@link DataValue}s, the size and order matching the
       * provided {@link NodeId}s.
       */
      default CompletableFuture<List<DataValue>> read(double maxAge,
                                                      TimestampsToReturn timestampsToReturn,
                                                      List<NodeId> nodeIds,
                                                      List<UInteger> attributeIds) {
  
          if (nodeIds.size() != attributeIds.size()) {
              CompletableFuture<List<DataValue>> failed = new CompletableFuture<>();
              failed.completeExceptionally(new IllegalArgumentException("nodeIds.size() != attributeIds.size()"));
              return failed;
          } else {
              Stream<ReadValueId> stream = Streams.zip(
                  nodeIds.stream(),
                  attributeIds.stream(),
                  (nId, aId) -> new ReadValueId(nId, aId, null, QualifiedName.NULL_VALUE)
              );
  
              return read(maxAge, timestampsToReturn, stream.collect(Collectors.toList()))
                  .thenApply(r -> l(r.getResults()));
          }
      }
  
      /**
       * This service is used to read the value attribute of a single Node.
       *
       * @param maxAge             the requested max age of the value, in milliseconds. If maxAge is set to 0, the Server
       *                           shall attempt to read a new value from the data source. If maxAge is set to the max
       *                           Int32 value or greater, the Server shall attempt to get a cached value. Negative values
       *                           are invalid for maxAge.
       * @param timestampsToReturn the requested {@link TimestampsToReturn}.
       * @param nodeId             the {@link NodeId} identifying the node to read.
       * @return a {@link CompletableFuture} containing the {@link DataValue}.
       */
      default CompletableFuture<DataValue> readValue(double maxAge,
                                                     TimestampsToReturn timestampsToReturn,
                                                     NodeId nodeId) {
  
          return readValues(maxAge, timestampsToReturn, Collections.singletonList(nodeId))
              .thenApply(r -> r.get(0));
      }
  
      /**
       * This service is used to read the value attribute of one or more Nodes.
       *
       * @param maxAge             the requested max age of the value, in milliseconds. If maxAge is set to 0, the Server
       *                           shall attempt to read a new value from the data source. If maxAge is set to the max
       *                           Int32 value or greater, the Server shall attempt to get a cached value. Negative values
       *                           are invalid for maxAge.
       * @param timestampsToReturn the requested {@link TimestampsToReturn}.
       * @param nodeIds            the {@link NodeId}s identifying the nodes to read.
       * @return a {@link CompletableFuture} containing a list of {@link DataValue}s, the size and order matching the
       * provided {@link NodeId}s.
       */
      default CompletableFuture<List<DataValue>> readValues(double maxAge,
                                                            TimestampsToReturn timestampsToReturn,
                                                            List<NodeId> nodeIds) {
  
          List<ReadValueId> readValueIds = nodeIds.stream()
              .map(nodeId -> new ReadValueId(nodeId, AttributeId.Value.uid(), null, QualifiedName.NULL_VALUE))
              .collect(Collectors.toList());
  
          return read(maxAge, timestampsToReturn, readValueIds)
              .thenApply(r -> l(r.getResults()));
      }
  
      /**
       * This service is used to write values to one or more attributes of one or more nodes.
       *
       * @param writeValues the list of nodes and their attributes to write.
       * @return a {@link CompletableFuture} containing the {@link WriteResponse}.
       */
      CompletableFuture<WriteResponse> write(List<WriteValue> writeValues);
  
      /**
       * This service is used to write to the value attribute of one or more nodes.
       *
       * @param nodeIds the {@link NodeId}s identifying the nodes to write to.
       * @param values  the {@link DataValue}s to write.
       * @return a {@link CompletableFuture} containing a list of results for the writes.
       */
      default CompletableFuture<List<StatusCode>> writeValues(List<NodeId> nodeIds, List<DataValue> values) {
          if (nodeIds.size() != values.size()) {
              CompletableFuture<List<StatusCode>> failed = new CompletableFuture<>();
              failed.completeExceptionally(new IllegalArgumentException("nodeIds.size() != values.size()"));
              return failed;
          } else {
              Stream<WriteValue> stream = Streams.zip(
                  nodeIds.stream(),
                  values.stream(),
                  (nodeId, value) -> new WriteValue(nodeId, uint(13), null, value)
              );
  
              return write(stream.collect(Collectors.toList()))
                  .thenApply(response -> l(response.getResults()));
          }
      }
  
      /**
       * This service is used to write to the value attribute of one node.
       *
       * @param nodeId the {@link NodeId} identifying the node to write to.
       * @param value  the {@link DataValue} to write.
       * @return a {@link CompletableFuture} containing the result for the write.
       */
      default CompletableFuture<StatusCode> writeValue(NodeId nodeId, DataValue value) {
          return write(Collections.singletonList(new WriteValue(nodeId, uint(13), null, value)))
              .thenApply(response -> l(response.getResults()).get(0));
      }
  
      /**
       * This Service is used to read historical values or Events of one or more Nodes.
       *
       * @param historyReadDetails        defines the types of history read to be performed.
       * @param timestampsToReturn        specifies the timestamps to be returned for each requested value attribute.
       * @param releaseContinuationPoints if {@code true}, passed continuationPoints shall be reset to free resources in
       *                                  the Server. if {@code false}, passed continuationPoints shall be used to get the
       *                                  next set of historical information.
       * @param nodesToRead               the list of items upon which the historical retrieval is to be performed.
       * @return a {@link CompletableFuture} containing the {@link HistoryReadResponse}.
       */
      CompletableFuture<HistoryReadResponse> historyRead(HistoryReadDetails historyReadDetails,
                                                         TimestampsToReturn timestampsToReturn,
                                                         boolean releaseContinuationPoints,
                                                         List<HistoryReadValueId> nodesToRead);
  
  
      /**
       * This Service is used to update historical values or Events of one or more Nodes.
       *
       * @param historyUpdateDetails the details defined for this update.
       * @return a {@link CompletableFuture} containing the {@link HistoryUpdateResponse}.
       */
      CompletableFuture<HistoryUpdateResponse> historyUpdate(List<HistoryUpdateDetails> historyUpdateDetails);
  
  }
  