DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
opcua_client.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file opcua_client.h
6 * @brief OPC UA Binary client - request builders + response parsers (DETWS_ENABLE_OPCUA_CLIENT).
7 *
8 * The client side of the OPC UA Binary protocol, the mirror of services/opcua: it
9 * builds the request messages a client sends (Hello, OpenSecureChannel,
10 * CreateSession, ActivateSession, Read, Browse, CloseSession, CloseSecureChannel)
11 * and parses the server's responses, reusing the same little-endian codec
12 * (UaWriter / UaReader / NodeId / Variant / DataValue / QualifiedName /
13 * LocalizedText) declared in opcua.h.
14 *
15 * It is **transport-agnostic**: every function works on caller-provided byte
16 * buffers, so the application owns the outbound socket (for example an Arduino
17 * WiFiClient) and just pumps these bytes. An OpcUaClient holds the small amount of
18 * per-connection state (SecureChannelId, security TokenId, the session's
19 * AuthenticationToken, and the monotonically increasing sequence / request ids).
20 *
21 * SecurityPolicy is None (no signing/encryption), matching the server. The response
22 * parsers target that encoding (empty diagnostics, null string tables). No heap,
23 * no stdlib.
24 *
25 * @author Douglas Quigg (dstroy0)
26 * @date 2026
27 */
28
29#ifndef DETERMINISTICESPASYNCWEBSERVER_OPCUA_CLIENT_H
30#define DETERMINISTICESPASYNCWEBSERVER_OPCUA_CLIENT_H
31
32#include "ServerConfig.h"
34#include <stddef.h>
35#include <stdint.h>
36
37#if DETWS_ENABLE_OPCUA_CLIENT
38
39// Dependency (OPCUA_CLIENT requires OPCUA) is enforced centrally in ServerConfig.h.
40
41/** @brief Per-connection OPC UA client state (SecureChannel + Session + counters). */
42struct OpcUaClient
43{
44 uint32_t channel_id; ///< SecureChannelId (from the OPN response).
45 uint32_t token_id; ///< security TokenId (from the OPN response; sent in every MSG).
46 uint16_t session_auth_ns; ///< session AuthenticationToken NodeId namespace (from CreateSession).
47 uint32_t session_auth_id; ///< session AuthenticationToken NodeId identifier.
48 bool session_auth_numeric; ///< whether the AuthenticationToken is numeric.
49 uint32_t seq; ///< client SequenceNumber (incremented per message).
50 uint32_t request_id; ///< client RequestId (incremented per message).
51 uint32_t request_handle; ///< client RequestHandle (incremented per request).
52};
53
54/** @brief Zero a client's state before the first connection. */
55void opcua_client_init(OpcUaClient *c);
56
57// ---------------------------------------------------------------------------
58// Request builders (return bytes written to @p out, or 0 if it does not fit)
59// ---------------------------------------------------------------------------
60
61/** @brief Build a `HEL` Hello, advertising DETWS_OPCUA_BUF buffer sizes. */
62size_t opcua_client_hello(const char *endpoint_url, uint8_t *out, size_t cap);
63
64/** @brief Build an `OPN` OpenSecureChannelRequest (Issue, SecurityPolicy None). */
65size_t opcua_client_open(OpcUaClient *c, uint8_t *out, size_t cap);
66
67/** @brief Build a `MSG` GetEndpointsRequest (no session needed). */
68size_t opcua_client_get_endpoints(OpcUaClient *c, const char *endpoint_url, uint8_t *out, size_t cap);
69
70/** @brief Build a `MSG` CreateSessionRequest. */
71size_t opcua_client_create_session(OpcUaClient *c, const char *session_name, const char *endpoint_url, uint8_t *out,
72 size_t cap);
73
74/** @brief Build a `MSG` ActivateSessionRequest (anonymous user identity token). */
75size_t opcua_client_activate_session(OpcUaClient *c, uint8_t *out, size_t cap);
76
77/** @brief Build a `MSG` ReadRequest for @p n nodes (each a NodeId + AttributeId). */
78size_t opcua_client_read(OpcUaClient *c, const OpcUaReadItem *items, uint32_t n, uint8_t *out, size_t cap);
79
80/** @brief Build a `MSG` BrowseRequest for one node (forward references). */
81size_t opcua_client_browse(OpcUaClient *c, uint16_t ns, uint32_t id, uint8_t *out, size_t cap);
82
83/** @brief Build a `MSG` WriteRequest writing @p n values (each item carries its Variant). */
84size_t opcua_client_write(OpcUaClient *c, const OpcUaWriteItem *items, uint32_t n, uint8_t *out, size_t cap);
85
86/** @brief Build a `MSG` CloseSessionRequest. */
87size_t opcua_client_close_session(OpcUaClient *c, uint8_t *out, size_t cap);
88
89/** @brief Build a `CLO` CloseSecureChannel message (the server closes the socket). */
90size_t opcua_client_close_channel(uint8_t *out, size_t cap);
91
92// ---------------------------------------------------------------------------
93// Response parsers (consume the server reply; update client state)
94// ---------------------------------------------------------------------------
95
96/** @brief Negotiated buffer sizes from an `ACK`. */
97struct OpcUaAckInfo
98{
99 uint32_t recv_buf_size;
100 uint32_t send_buf_size;
101 uint32_t max_msg_size;
102 uint32_t max_chunk_count;
103};
104
105/** @brief Parse an `ACK`. @return true if valid. */
106bool opcua_client_on_ack(const uint8_t *msg, size_t len, OpcUaAckInfo *out);
107
108/** @brief Parse an `OPN` OpenSecureChannelResponse; sets channel_id + token_id. @return true if Good. */
109bool opcua_client_on_open(OpcUaClient *c, const uint8_t *msg, size_t len);
110
111/** @brief Parse a GetEndpointsResponse. @return the advertised endpoint count, or -1 on error. */
112int32_t opcua_client_on_get_endpoints(const uint8_t *msg, size_t len);
113
114/** @brief Parse a CreateSessionResponse; stores the session AuthenticationToken. @return true if Good. */
115bool opcua_client_on_create_session(OpcUaClient *c, const uint8_t *msg, size_t len);
116
117/** @brief Parse an ActivateSessionResponse. @return true if ServiceResult is Good. */
118bool opcua_client_on_activate_session(const uint8_t *msg, size_t len);
119
120/**
121 * @brief Parse a ReadResponse into @p vals / @p statuses (one per result, capped at @p max).
122 * @note A returned OpcUaVariantType::OPCUA_VAR_STRING value points into @p msg (keep it alive while used).
123 * @return number of results, or -1 on a malformed/non-Good response.
124 */
125int32_t opcua_client_on_read(const uint8_t *msg, size_t len, OpcUaVariant *vals, uint32_t *statuses, uint32_t max);
126
127/** @brief One reference parsed from a BrowseResponse (self-contained; name is copied). */
128struct OpcUaClientRef
129{
130 uint32_t ref_type_id;
131 bool is_forward;
132 uint16_t target_ns;
133 uint32_t target_id;
134 uint32_t node_class;
135 char browse_name[32];
136};
137
138/**
139 * @brief Parse a BrowseResponse, flattening all results' references into @p refs (capped at @p max).
140 * @return number of references, or -1 on a malformed/non-Good response.
141 */
142int32_t opcua_client_on_browse(const uint8_t *msg, size_t len, OpcUaClientRef *refs, uint32_t max);
143
144/**
145 * @brief Parse a WriteResponse into @p results (one StatusCode per written node, capped at @p max).
146 * @return number of results, or -1 on a malformed/non-Good response.
147 */
148int32_t opcua_client_on_write(const uint8_t *msg, size_t len, uint32_t *results, uint32_t max);
149
150#endif // DETWS_ENABLE_OPCUA_CLIENT
151#endif // DETERMINISTICESPASYNCWEBSERVER_OPCUA_CLIENT_H
User-facing configuration for DeterministicESPAsyncWebServer.
OPC UA Binary server: handshake + SecureChannel + Session + Read/Write + Browse (DETWS_ENABLE_OPCUA).