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.examples.client;
  
  import java.util.concurrent.CompletableFuture;
  
  import org.eclipse.milo.examples.server.types.CustomEnumType;
  import org.eclipse.milo.examples.server.types.CustomStructType;
  import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
  import org.eclipse.milo.opcua.sdk.client.nodes.UaVariableNode;
  import org.eclipse.milo.opcua.stack.core.types.OpcUaDefaultBinaryEncoding;
  import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
  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.Variant;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
  
  public class ReadWriteCustomDataTypeNodeExample implements ClientExample {
  
      public static void main(String[] args) throws Exception {
          ReadWriteCustomDataTypeNodeExample example = new ReadWriteCustomDataTypeNodeExample();
  
          new ClientExampleRunner(example).run();
      }
  
      private final Logger logger = LoggerFactory.getLogger(getClass());
  
      @Override
      public void run(OpcUaClient client, CompletableFuture<OpcUaClient> future) throws Exception {
          // synchronous connect
          client.connect().get();
  
          registerCustomCodec(client);
  
          // synchronous read request via VariableNode
          UaVariableNode node = client.getAddressSpace().getVariableNode(
              new NodeId(2, "HelloWorld/CustomStructTypeVariable")
          );
  
          logger.info("DataType={}", node.getDataType());
  
          // Read the current value
          DataValue value = node.readValue();
          logger.info("Value={}", value);
  
          Variant variant = value.getValue();
          ExtensionObject xo = (ExtensionObject) variant.getValue();
  
          CustomStructType decoded = (CustomStructType) xo.decode(
              client.getStaticSerializationContext()
          );
          logger.info("Decoded={}", decoded);
  
          // Write a modified value
          CustomStructType modified = new CustomStructType(
              decoded.getFoo() + "bar",
              uint(decoded.getBar().intValue() + 1),
              !decoded.isBaz(),
              CustomEnumType.Field1
          );
          ExtensionObject modifiedXo = ExtensionObject.encode(
              client.getStaticSerializationContext(),
              modified,
              xo.getEncodingId(),
              OpcUaDefaultBinaryEncoding.getInstance()
          );
  
          node.writeValue(new DataValue(new Variant(modifiedXo)));
  
          // Read the modified value back
          value = node.readValue();
          logger.info("Value={}", value);
  
          variant = value.getValue();
          xo = (ExtensionObject) variant.getValue();
  
          decoded = (CustomStructType) xo.decode(
              client.getStaticSerializationContext()
          );
          logger.info("Decoded={}", decoded);
  
          future.complete(client);
      }
  
      private void registerCustomCodec(OpcUaClient client) {
          NodeId dataTypeId = CustomStructType.TYPE_ID
              .toNodeId(client.getNamespaceTable())
              .orElseThrow(() -> new IllegalStateException("namespace not found"));
  
          NodeId binaryEncodingId = CustomStructType.BINARY_ENCODING_ID
              .toNodeId(client.getNamespaceTable())
              .orElseThrow(() -> new IllegalStateException("namespace not found"));
  
          // Register codec with the client's DataTypeManager instance.
          // We need to register it by both its encodingId and its dataTypeId because it may be
          // looked up by either depending on the context.
  
          client.getStaticDataTypeManager().registerCodec(
              binaryEncodingId,
              new CustomStructType.Codec().asBinaryCodec()
          );
  
          client.getStaticDataTypeManager().registerCodec(
              new QualifiedName(dataTypeId.getNamespaceIndex(), "CustomStructType"),
              dataTypeId,
              new CustomStructType.Codec().asBinaryCodec()
          );
      }
  
  }
  