/**
   * Query the GetEndpoints service at the given endpoint URL.
   *
   * @param endpointUrl the endpoint URL to get endpoints from.
   * @return the {@link EndpointDescription}s returned by the GetEndpoints service.
   */
  public static CompletableFuture<EndpointDescription[]> getEndpoints(String endpointUrl) {
    UaTcpStackClientConfig config =
        UaTcpStackClientConfig.builder().setEndpointUrl(endpointUrl).build();

    UaTcpStackClient client = new UaTcpStackClient(config);

    GetEndpointsRequest request =
        new GetEndpointsRequest(
            new RequestHeader(null, DateTime.now(), uint(1), uint(0), null, uint(5000), null),
            endpointUrl,
            null,
            new String[] {Stack.UA_TCP_BINARY_TRANSPORT_URI});

    return client
        .<GetEndpointsResponse>sendRequest(request)
        .whenComplete((r, ex) -> client.disconnect())
        .thenApply(GetEndpointsResponse::getEndpoints);
  }
  /**
   * Query the FindServers service at the given endpoint URL.
   *
   * <p>The endpoint URL(s) for each server {@link ApplicationDescription} returned can then be used
   * in a {@link #getEndpoints(String)} call to discover the endpoints for that server.
   *
   * @param endpointUrl the endpoint URL to find servers at.
   * @return the {@link ApplicationDescription}s returned by the FindServers service.
   */
  public static CompletableFuture<ApplicationDescription[]> findServers(String endpointUrl) {
    UaTcpStackClientConfig config =
        UaTcpStackClientConfig.builder().setEndpointUrl(endpointUrl).build();

    UaTcpStackClient client = new UaTcpStackClient(config);

    FindServersRequest request =
        new FindServersRequest(
            new RequestHeader(null, DateTime.now(), uint(1), uint(0), null, uint(5000), null),
            endpointUrl,
            null,
            null);

    return client
        .<FindServersResponse>sendRequest(request)
        .whenComplete((r, ex) -> client.disconnect())
        .thenApply(FindServersResponse::getServers);
  }