13#if DETWS_ENABLE_OPCUA_CLIENT
17void opcua_client_init(OpcUaClient *c)
19 memset(c, 0,
sizeof(*c));
25static size_t cw_patch(UaWriter *w)
29 w->o[4] = (uint8_t)w->n;
30 w->o[5] = (uint8_t)(w->n >> 8);
31 w->o[6] = (uint8_t)(w->n >> 16);
32 w->o[7] = (uint8_t)(w->n >> 24);
38static void cw_request_header(OpcUaClient *c, UaWriter *w,
bool with_session_token)
40 if (with_session_token && c->session_auth_id)
41 ua_w_nodeid_numeric(w, c->session_auth_ns, c->session_auth_id);
43 ua_w_nodeid_numeric(w, 0, 0);
45 ua_w_u32(w, ++c->request_handle);
47 ua_w_string(w,
nullptr, -1);
49 ua_w_nodeid_numeric(w, 0, 0);
55static void cw_msg(OpcUaClient *c, UaWriter *w, uint32_t type_id)
62 ua_w_u32(w, c->channel_id);
63 ua_w_u32(w, c->token_id);
64 ua_w_u32(w, ++c->seq);
65 ua_w_u32(w, ++c->request_id);
66 ua_w_nodeid_numeric(w, 0, type_id);
72size_t opcua_client_hello(
const char *endpoint_url, uint8_t *out,
size_t cap)
74 UaWriter w = {out, cap, 0,
true};
81 ua_w_u32(&w, DETWS_OPCUA_BUF);
82 ua_w_u32(&w, DETWS_OPCUA_BUF);
85 ua_w_string(&w, endpoint_url, endpoint_url ? (int32_t)strnlen(endpoint_url, w.cap) : -1);
89size_t opcua_client_open(OpcUaClient *c, uint8_t *out,
size_t cap)
91 UaWriter w = {out, cap, 0,
true};
98 ua_w_string(&w, OPCUA_POLICY_NONE_URI, (int32_t)(
sizeof(OPCUA_POLICY_NONE_URI) - 1));
99 ua_w_string(&w,
nullptr, -1);
100 ua_w_string(&w,
nullptr, -1);
101 ua_w_u32(&w, ++c->seq);
102 ua_w_u32(&w, ++c->request_id);
103 ua_w_nodeid_numeric(&w, 0, OPCUA_ID_OPEN_REQ);
104 cw_request_header(c, &w,
false);
108 ua_w_string(&w,
nullptr, -1);
109 ua_w_u32(&w, 3600000);
113size_t opcua_client_get_endpoints(OpcUaClient *c,
const char *endpoint_url, uint8_t *out,
size_t cap)
115 UaWriter w = {out, cap, 0,
true};
116 cw_msg(c, &w, OPCUA_ID_GET_ENDPOINTS_REQ);
117 cw_request_header(c, &w,
false);
118 ua_w_string(&w, endpoint_url, endpoint_url ? (int32_t)strnlen(endpoint_url, w.cap) : -1);
124size_t opcua_client_create_session(OpcUaClient *c,
const char *session_name,
const char *endpoint_url, uint8_t *out,
127 UaWriter w = {out, cap, 0,
true};
128 cw_msg(c, &w, OPCUA_ID_CREATE_SESSION_REQ);
129 cw_request_header(c, &w,
false);
131 ua_w_string(&w,
"urn:det:opcua:client", 20);
132 ua_w_string(&w,
"urn:det:opcua", 13);
133 ua_w_localizedtext(&w,
nullptr,
"DetOpcUaClient");
135 ua_w_string(&w,
nullptr, -1);
136 ua_w_string(&w,
nullptr, -1);
138 ua_w_string(&w,
nullptr, -1);
139 ua_w_string(&w, endpoint_url, endpoint_url ? (int32_t)strnlen(endpoint_url, w.cap) : -1);
140 ua_w_string(&w, session_name, session_name ? (int32_t)strnlen(session_name, w.cap) : -1);
141 ua_w_string(&w,
nullptr, -1);
142 ua_w_string(&w,
nullptr, -1);
143 ua_w_f64(&w, 1200000.0);
148size_t opcua_client_activate_session(OpcUaClient *c, uint8_t *out,
size_t cap)
150 UaWriter w = {out, cap, 0,
true};
151 cw_msg(c, &w, OPCUA_ID_ACTIVATE_SESSION_REQ);
152 cw_request_header(c, &w,
true);
153 ua_w_string(&w,
nullptr, -1);
154 ua_w_string(&w,
nullptr, -1);
158 ua_w_nodeid_numeric(&w, 0, 321);
161 ua_w_string(&w,
"anonymous", 9);
162 ua_w_string(&w,
nullptr, -1);
163 ua_w_string(&w,
nullptr, -1);
167size_t opcua_client_read(OpcUaClient *c,
const OpcUaReadItem *items, uint32_t n, uint8_t *out,
size_t cap)
169 UaWriter w = {out, cap, 0,
true};
170 cw_msg(c, &w, OPCUA_ID_READ_REQ);
171 cw_request_header(c, &w,
true);
174 ua_w_i32(&w, (int32_t)n);
175 for (uint32_t i = 0; i < n; i++)
177 ua_w_nodeid_numeric(&w, items[i].ns, items[i].
id);
178 ua_w_u32(&w, items[i].attribute);
179 ua_w_string(&w,
nullptr, -1);
181 ua_w_string(&w,
nullptr, -1);
186size_t opcua_client_browse(OpcUaClient *c, uint16_t ns, uint32_t
id, uint8_t *out,
size_t cap)
188 UaWriter w = {out, cap, 0,
true};
189 cw_msg(c, &w, OPCUA_ID_BROWSE_REQ);
190 cw_request_header(c, &w,
true);
191 ua_w_nodeid_numeric(&w, 0, 0);
196 ua_w_nodeid_numeric(&w, ns,
id);
198 ua_w_nodeid_numeric(&w, 0, 0);
205size_t opcua_client_write(OpcUaClient *c,
const OpcUaWriteItem *items, uint32_t n, uint8_t *out,
size_t cap)
207 UaWriter w = {out, cap, 0,
true};
208 cw_msg(c, &w, OPCUA_ID_WRITE_REQ);
209 cw_request_header(c, &w,
true);
210 ua_w_i32(&w, (int32_t)n);
211 for (uint32_t i = 0; i < n; i++)
213 ua_w_nodeid_numeric(&w, items[i].ns, items[i].
id);
214 ua_w_u32(&w, items[i].attribute);
215 ua_w_string(&w,
nullptr, -1);
216 ua_w_datavalue(&w, &items[i].value, OPCUA_STATUS_GOOD);
221size_t opcua_client_close_session(OpcUaClient *c, uint8_t *out,
size_t cap)
223 UaWriter w = {out, cap, 0,
true};
224 cw_msg(c, &w, OPCUA_ID_CLOSE_SESSION_REQ);
225 cw_request_header(c, &w,
true);
230size_t opcua_client_close_channel(uint8_t *out,
size_t cap)
232 UaWriter w = {out, cap, 0,
true};
244static void cr_skip(UaReader *r,
size_t n)
246 if (r->err || r->off + n > r->len)
254static void cr_skip_string(UaReader *r)
256 int32_t l = ua_r_i32(r);
258 cr_skip(r, (
size_t)l);
261static void cr_skip_string_array(UaReader *r)
263 int32_t n = ua_r_i32(r);
264 for (int32_t i = 0; i < n; i++)
270static bool cr_response_header(UaReader *r, uint32_t *service_result)
274 uint32_t svc = ua_r_u32(r);
276 *service_result = svc;
278 cr_skip_string_array(r);
286static bool cr_msg_open(
const uint8_t *msg,
size_t len, UaReader *r, uint32_t expect_type, uint32_t *service_result)
289 if (!opcua_parse_header(msg, len, &h) || memcmp(h.type,
"MSG", 3) != 0 || h.size != len)
291 *r = UaReader{msg + 8, len - 8, 0,
false};
297 if (!ua_r_nodeid(r, &tid))
299 if (!(tid.numeric && tid.id == expect_type))
301 return cr_response_header(r, service_result);
307bool opcua_client_on_ack(
const uint8_t *msg,
size_t len, OpcUaAckInfo *out)
310 if (!opcua_parse_header(msg, len, &h) || memcmp(h.type,
"ACK", 3) != 0)
312 if (h.size != len || len < 8 + 20)
314 UaReader r = {msg + 8, len - 8, 0,
false};
316 out->recv_buf_size = ua_r_u32(&r);
317 out->send_buf_size = ua_r_u32(&r);
318 out->max_msg_size = ua_r_u32(&r);
319 out->max_chunk_count = ua_r_u32(&r);
323bool opcua_client_on_open(OpcUaClient *c,
const uint8_t *msg,
size_t len)
326 if (!opcua_parse_header(msg, len, &h) || memcmp(h.type,
"OPN", 3) != 0 || h.size != len)
328 UaReader r = {msg + 8, len - 8, 0,
false};
336 if (!ua_r_nodeid(&r, &tid) || !(tid.numeric && tid.id == OPCUA_ID_OPEN_RESP))
339 if (!cr_response_header(&r, &svc))
342 c->channel_id = ua_r_u32(&r);
343 c->token_id = ua_r_u32(&r);
346 return !r.err && svc == OPCUA_STATUS_GOOD;
349int32_t opcua_client_on_get_endpoints(
const uint8_t *msg,
size_t len)
353 if (!cr_msg_open(msg, len, &r, OPCUA_ID_GET_ENDPOINTS_RESP, &svc) || svc != OPCUA_STATUS_GOOD)
355 int32_t cnt = ua_r_i32(&r);
356 return (cnt < 0 || r.err) ? -1 : cnt;
359bool opcua_client_on_create_session(OpcUaClient *c,
const uint8_t *msg,
size_t len)
363 if (!cr_msg_open(msg, len, &r, OPCUA_ID_CREATE_SESSION_RESP, &svc))
366 ua_r_nodeid(&r, &sid);
368 ua_r_nodeid(&r, &atok);
369 c->session_auth_ns = atok.ns;
370 c->session_auth_id = atok.id;
371 c->session_auth_numeric = atok.numeric;
372 return !r.err && svc == OPCUA_STATUS_GOOD;
375bool opcua_client_on_activate_session(
const uint8_t *msg,
size_t len)
379 if (!cr_msg_open(msg, len, &r, OPCUA_ID_ACTIVATE_SESSION_RESP, &svc))
381 return svc == OPCUA_STATUS_GOOD;
384int32_t opcua_client_on_read(
const uint8_t *msg,
size_t len, OpcUaVariant *vals, uint32_t *statuses, uint32_t max)
388 if (!cr_msg_open(msg, len, &r, OPCUA_ID_READ_RESP, &svc) || svc != OPCUA_STATUS_GOOD)
390 int32_t cnt = ua_r_i32(&r);
394 for (int32_t i = 0; i < cnt; i++)
396 uint8_t mask = ua_r_u8(&r);
398 memset(&v, 0,
sizeof(v));
399 uint32_t st = OPCUA_STATUS_GOOD;
402 OpcUaVariantType vt = (OpcUaVariantType)ua_r_u8(&r);
406 case OpcUaVariantType::OPCUA_VAR_BOOL:
409 case OpcUaVariantType::OPCUA_VAR_INT32:
410 v.i32 = ua_r_i32(&r);
412 case OpcUaVariantType::OPCUA_VAR_UINT32:
413 v.u32 = ua_r_u32(&r);
415 case OpcUaVariantType::OPCUA_VAR_FLOAT:
416 v.f32 = ua_r_f32(&r);
418 case OpcUaVariantType::OPCUA_VAR_DOUBLE:
419 v.f64 = ua_r_f64(&r);
421 case OpcUaVariantType::OPCUA_VAR_STRING: {
422 int32_t sl = ua_r_i32(&r);
426 v.str = (
const char *)(r.p + r.off);
427 cr_skip(&r, (
size_t)sl);
443 statuses[out_n] = st;
447 return r.err ? -1 : (int32_t)out_n;
450int32_t opcua_client_on_write(
const uint8_t *msg,
size_t len, uint32_t *results, uint32_t max)
454 if (!cr_msg_open(msg, len, &r, OPCUA_ID_WRITE_RESP, &svc) || svc != OPCUA_STATUS_GOOD)
456 int32_t cnt = ua_r_i32(&r);
460 for (int32_t i = 0; i < cnt; i++)
462 uint32_t st = ua_r_u32(&r);
470 return r.err ? -1 : (int32_t)out_n;
473int32_t opcua_client_on_browse(
const uint8_t *msg,
size_t len, OpcUaClientRef *refs, uint32_t max)
477 if (!cr_msg_open(msg, len, &r, OPCUA_ID_BROWSE_RESP, &svc) || svc != OPCUA_STATUS_GOOD)
479 int32_t nres = ua_r_i32(&r);
483 for (int32_t ri = 0; ri < nres; ri++)
487 int32_t nrefs = ua_r_i32(&r);
488 for (int32_t j = 0; j < nrefs; j++)
491 memset(&ref, 0,
sizeof(ref));
493 ua_r_nodeid(&r, &rt);
494 ref.ref_type_id = rt.id;
495 ref.is_forward = ua_r_bool(&r);
497 ua_r_nodeid(&r, &tgt);
498 ref.target_ns = tgt.ns;
499 ref.target_id = tgt.id;
502 ua_r_string(&r, ref.browse_name,
sizeof(ref.browse_name), &bnl);
503 uint8_t m = ua_r_u8(&r);
508 ref.node_class = ua_r_u32(&r);
510 ua_r_nodeid(&r, &td);
519 return r.err ? -1 : (int32_t)out_n;
OPC UA Binary client - request builders + response parsers (DETWS_ENABLE_OPCUA_CLIENT).