File syn_opcua.c¶
File List > proto > syn_opcua.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_OPCUA) || SYN_USE_OPCUA
#include "../port/syn_port_system.h"
#include "syn_opcua.h"
#include <string.h>
#define SYN_OPCUA_REQ_CREATE_SESSION 461U
#define SYN_OPCUA_RESP_CREATE_SESSION 464U
#define SYN_OPCUA_REQ_ACTIVATE_SESSION 467U
#define SYN_OPCUA_RESP_ACTIVATE_SESSION 470U
#define SYN_OPCUA_REQ_CLOSE_SESSION 473U
#define SYN_OPCUA_RESP_CLOSE_SESSION 476U
#define SYN_OPCUA_REQ_READ 631U
#define SYN_OPCUA_RESP_READ 634U
#define SYN_OPCUA_REQ_WRITE 673U
#define SYN_OPCUA_RESP_WRITE 676U
#define SYN_OPCUA_REQ_BROWSE 527U
#define SYN_OPCUA_RESP_BROWSE 530U
/* ── Byte Writer Helper ─────────────────────────────────────────────────── */
typedef struct {
uint8_t *data;
size_t size;
size_t pos;
bool error;
} OPCUA_Writer;
static void opcua_writer_init(OPCUA_Writer *w, uint8_t *buf, size_t size)
{
w->data = buf;
w->size = size;
w->pos = 0U;
w->error = (buf == NULL || size == 0U);
}
static bool opcua_write_u8(OPCUA_Writer *w, uint8_t v)
{
if (w->error || w->pos + 1U > w->size) {
w->error = true;
return false;
}
w->data[w->pos++] = v;
return true;
}
static bool opcua_write_u16(OPCUA_Writer *w, uint16_t v)
{
if (w->error || w->pos + 2U > w->size) {
w->error = true;
return false;
}
w->data[w->pos++] = (uint8_t)(v & 0xFFU);
w->data[w->pos++] = (uint8_t)((v >> 8U) & 0xFFU);
return true;
}
static bool opcua_write_u32(OPCUA_Writer *w, uint32_t v)
{
if (w->error || w->pos + 4U > w->size) {
w->error = true;
return false;
}
w->data[w->pos++] = (uint8_t)(v & 0xFFU);
w->data[w->pos++] = (uint8_t)((v >> 8U) & 0xFFU);
w->data[w->pos++] = (uint8_t)((v >> 16U) & 0xFFU);
w->data[w->pos++] = (uint8_t)((v >> 24U) & 0xFFU);
return true;
}
static bool opcua_write_u64(OPCUA_Writer *w, uint64_t v)
{
if (w->error || w->pos + 8U > w->size) {
w->error = true;
return false;
}
for (size_t i = 0U; i < 8U; i++) {
w->data[w->pos++] = (uint8_t)((v >> (i * 8U)) & 0xFFU);
}
return true;
}
static bool opcua_write_bytes(OPCUA_Writer *w, const uint8_t *buf, size_t len)
{
if (w->error || w->pos + len > w->size) {
w->error = true;
return false;
}
if (len > 0U && buf != NULL) {
(void)memcpy(&w->data[w->pos], buf, len);
w->pos += len;
}
return true;
}
static bool opcua_write_string(OPCUA_Writer *w, const char *str)
{
if (str == NULL) {
return opcua_write_u32(w, 0xFFFFFFFFU); /* Null string (-1) */
}
uint32_t len = (uint32_t)strlen(str);
if (!opcua_write_u32(w, len)) {
return false;
}
return opcua_write_bytes(w, (const uint8_t *)str, len);
}
static bool opcua_write_nodeid_num(OPCUA_Writer *w, uint16_t ns, uint32_t num)
{
if (ns == 0U && num <= 0xFFU) {
(void)opcua_write_u8(w, 0x00U); /* TwoByte NodeId */
return opcua_write_u8(w, (uint8_t)num);
}
if (ns <= 0xFFU && num <= 0xFFFFU) {
(void)opcua_write_u8(w, 0x01U); /* FourByte NodeId */
(void)opcua_write_u8(w, (uint8_t)ns);
return opcua_write_u16(w, (uint16_t)num);
}
(void)opcua_write_u8(w, 0x02U); /* Numeric NodeId */
(void)opcua_write_u16(w, ns);
return opcua_write_u32(w, num);
}
static bool opcua_write_variant(OPCUA_Writer *w, const SYN_OPCUA_Variant *var)
{
if (var == NULL || var->type == SYN_OPCUA_TYPE_NULL) {
return opcua_write_u8(w, 0x00U);
}
(void)opcua_write_u8(w, (uint8_t)var->type);
switch (var->type) {
case SYN_OPCUA_TYPE_BOOLEAN:
return opcua_write_u8(w, var->val.boolean ? 1U : 0U);
case SYN_OPCUA_TYPE_SBYTE:
case SYN_OPCUA_TYPE_BYTE:
return opcua_write_u8(w, var->val.byte);
case SYN_OPCUA_TYPE_INT16:
case SYN_OPCUA_TYPE_UINT16:
return opcua_write_u16(w, var->val.uint16);
case SYN_OPCUA_TYPE_INT32:
case SYN_OPCUA_TYPE_UINT32:
return opcua_write_u32(w, var->val.uint32);
case SYN_OPCUA_TYPE_FLOAT: {
uint32_t u;
(void)memcpy(&u, &var->val.float_val, sizeof(u));
return opcua_write_u32(w, u);
}
case SYN_OPCUA_TYPE_INT64:
case SYN_OPCUA_TYPE_UINT64:
case SYN_OPCUA_TYPE_DATETIME:
return opcua_write_u64(w, var->val.uint64);
case SYN_OPCUA_TYPE_DOUBLE: {
uint64_t u;
(void)memcpy(&u, &var->val.double_val, sizeof(u));
return opcua_write_u64(w, u);
}
case SYN_OPCUA_TYPE_STRING:
return opcua_write_string(w, var->val.string);
default:
return false;
}
}
static bool opcua_write_datavalue(OPCUA_Writer *w, const SYN_OPCUA_DataValue *dv)
{
uint8_t mask = 0x01U; /* Has Value */
if (dv->status_code != SYN_OPCUA_STATUS_GOOD) {
mask |= 0x02U; /* Has Status */
}
if (dv->source_ts_ms != 0U) {
mask |= 0x04U; /* Has SourceTimestamp */
}
(void)opcua_write_u8(w, mask);
(void)opcua_write_variant(w, &dv->value);
if ((mask & 0x02U) != 0U) {
(void)opcua_write_u32(w, dv->status_code);
}
if ((mask & 0x04U) != 0U) {
(void)opcua_write_u64(w, dv->source_ts_ms);
}
return !w->error;
}
/* ── Byte Reader Helper ─────────────────────────────────────────────────── */
typedef struct {
const uint8_t *data;
size_t size;
size_t pos;
bool error;
} OPCUA_Reader;
static void opcua_reader_init(OPCUA_Reader *r, const uint8_t *buf, size_t size)
{
r->data = buf;
r->size = size;
r->pos = 0U;
r->error = (buf == NULL || size == 0U);
}
static bool opcua_read_u8(OPCUA_Reader *r, uint8_t *out)
{
if (r->error || r->pos + 1U > r->size) {
r->error = true;
return false;
}
*out = r->data[r->pos++];
return true;
}
static bool opcua_read_u16(OPCUA_Reader *r, uint16_t *out)
{
if (r->error || r->pos + 2U > r->size) {
r->error = true;
return false;
}
*out = (uint16_t)r->data[r->pos] | ((uint16_t)r->data[r->pos + 1U] << 8U);
r->pos += 2U;
return true;
}
static bool opcua_read_u32(OPCUA_Reader *r, uint32_t *out)
{
if (r->error || r->pos + 4U > r->size) {
r->error = true;
return false;
}
*out = (uint32_t)r->data[r->pos] | ((uint32_t)r->data[r->pos + 1U] << 8U) |
((uint32_t)r->data[r->pos + 2U] << 16U) | ((uint32_t)r->data[r->pos + 3U] << 24U);
r->pos += 4U;
return true;
}
static bool opcua_read_nodeid_num(OPCUA_Reader *r, uint16_t *out_ns, uint32_t *out_num)
{
uint8_t enc = 0U;
if (!opcua_read_u8(r, &enc)) {
return false;
}
if (enc == 0x00U) { /* TwoByte */
uint8_t id = 0U;
if (!opcua_read_u8(r, &id)) {
return false;
}
*out_ns = 0U;
*out_num = id;
return true;
}
if (enc == 0x01U) { /* FourByte */
uint8_t ns = 0U;
uint16_t id = 0U;
if (!opcua_read_u8(r, &ns) || !opcua_read_u16(r, &id)) {
return false;
}
*out_ns = ns;
*out_num = id;
return true;
}
if (enc == 0x02U) { /* Numeric */
uint16_t ns = 0U;
uint32_t id = 0U;
if (!opcua_read_u16(r, &ns) || !opcua_read_u32(r, &id)) {
return false;
}
*out_ns = ns;
*out_num = id;
return true;
}
return false;
}
/* ── Standard Address Space Setup ────────────────────────────────────────── */
static void opcua_populate_standard_nodes(SYN_OPCUA_Server *srv)
{
SYN_OPCUA_Node root = {
.node_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_ROOT}},
.node_class = SYN_OPCUA_NODECLASS_OBJECT,
.browse_name = "Root",
.display_name = "Root",
.access_level = 0x01U,
};
(void)syn_opcua_server_register_node(srv, &root);
SYN_OPCUA_Node objects = {
.node_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_OBJECTS_FOLDER}},
.parent_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_ROOT}},
.node_class = SYN_OPCUA_NODECLASS_OBJECT,
.browse_name = "Objects",
.display_name = "Objects",
.access_level = 0x01U,
};
(void)syn_opcua_server_register_node(srv, &objects);
SYN_OPCUA_Node server_node = {
.node_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_SERVER}},
.parent_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_OBJECTS_FOLDER}},
.node_class = SYN_OPCUA_NODECLASS_OBJECT,
.browse_name = "Server",
.display_name = "Server",
.access_level = 0x01U,
};
(void)syn_opcua_server_register_node(srv, &server_node);
SYN_OPCUA_Node server_status = {
.node_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_SERVER_SERVERSTATUS}},
.parent_id = {.ns_index = SYN_OPCUA_NS_STANDARD,
.id_type = SYN_OPCUA_NODEID_NUMERIC,
.id = {.num = SYN_OPCUA_NODEID_SERVER}},
.node_class = SYN_OPCUA_NODECLASS_VARIABLE,
.browse_name = "ServerStatus",
.display_name = "ServerStatus",
.data_type = SYN_OPCUA_TYPE_INT32,
.value = {.value = {.type = SYN_OPCUA_TYPE_INT32, .val = {.int32 = 0}},
.status_code = SYN_OPCUA_STATUS_GOOD},
.access_level = 0x01U,
};
(void)syn_opcua_server_register_node(srv, &server_status);
}
/* ── Public API Implementation ───────────────────────────────────────────── */
SYN_Status syn_opcua_server_init(SYN_OPCUA_Server *srv, const SYN_OPCUA_Config *cfg)
{
if (srv == NULL || cfg == NULL) {
return SYN_INVALID_PARAM;
}
if (cfg->rx_buf == NULL || cfg->tx_buf == NULL || cfg->rx_buf_size < 128U ||
cfg->tx_buf_size < 128U) {
return SYN_INVALID_PARAM;
}
(void)memset(srv, 0, sizeof(*srv));
srv->cfg = *cfg;
if (srv->cfg.endpoint_url == NULL) {
srv->cfg.endpoint_url = "opc.tcp://0.0.0.0:4840";
}
if (srv->cfg.server_name == NULL) {
srv->cfg.server_name = "SyntropicOS OPC UA Server";
}
srv->state = SYN_OPCUA_STATE_CLOSED;
srv->secure_channel_id = 1U;
srv->security_token_id = 1U;
srv->sequence_number = 1U;
opcua_populate_standard_nodes(srv);
return SYN_OK;
}
SYN_Status syn_opcua_server_register_node(SYN_OPCUA_Server *srv, const SYN_OPCUA_Node *node)
{
if (srv == NULL || node == NULL) {
return SYN_INVALID_PARAM;
}
if (srv->node_count >= SYN_OPCUA_MAX_NODES) {
return SYN_ERROR;
}
srv->nodes[srv->node_count++] = *node;
return SYN_OK;
}
SYN_OPCUA_Node *syn_opcua_server_find_node_num(SYN_OPCUA_Server *srv, uint16_t ns, uint32_t num_id)
{
if (srv == NULL) {
return NULL;
}
for (size_t i = 0; i < srv->node_count; i++) {
SYN_OPCUA_Node *n = &srv->nodes[i];
if (n->node_id.ns_index == ns && n->node_id.id_type == SYN_OPCUA_NODEID_NUMERIC &&
n->node_id.id.num == num_id) {
return n;
}
}
return NULL;
}
SYN_Status syn_opcua_server_write_variable(SYN_OPCUA_Server *srv, uint16_t ns, uint32_t num_id,
const SYN_OPCUA_Variant *val)
{
if (srv == NULL || val == NULL) {
return SYN_INVALID_PARAM;
}
SYN_OPCUA_Node *node = syn_opcua_server_find_node_num(srv, ns, num_id);
if (node == NULL || node->node_class != SYN_OPCUA_NODECLASS_VARIABLE) {
return SYN_ERROR;
}
node->value.value = *val;
node->value.status_code = SYN_OPCUA_STATUS_GOOD;
node->value.source_ts_ms = syn_port_get_tick_ms();
if (node->on_write != NULL) {
return node->on_write(srv, node, &node->value, node->user_data);
}
return SYN_OK;
}
SYN_Status syn_opcua_server_read_variable(const SYN_OPCUA_Server *srv, uint16_t ns, uint32_t num_id,
SYN_OPCUA_Variant *out_val)
{
if (srv == NULL || out_val == NULL) {
return SYN_INVALID_PARAM;
}
for (size_t i = 0; i < srv->node_count; i++) {
const SYN_OPCUA_Node *n = &srv->nodes[i];
if (n->node_id.ns_index == ns && n->node_id.id_type == SYN_OPCUA_NODEID_NUMERIC &&
n->node_id.id.num == num_id) {
*out_val = n->value.value;
return SYN_OK;
}
}
return SYN_ERROR;
}
/* ── Service Processing ──────────────────────────────────────────────────── */
SYN_Status syn_opcua_server_process_message(SYN_OPCUA_Server *srv, const uint8_t *rx, size_t rx_len,
uint8_t *tx, size_t max_tx, size_t *out_len)
{
if (srv == NULL || rx == NULL || tx == NULL || out_len == NULL || rx_len < 8U) {
return SYN_INVALID_PARAM;
}
*out_len = 0U;
OPCUA_Reader r;
opcua_reader_init(&r, rx, rx_len);
char msg_type[4] = {0};
msg_type[0] = (char)rx[0];
msg_type[1] = (char)rx[1];
msg_type[2] = (char)rx[2];
msg_type[3] = (char)rx[3]; /* IsFinal: 'F' */
uint32_t message_size = 0U;
r.pos = 4U;
(void)opcua_read_u32(&r, &message_size);
OPCUA_Writer w;
opcua_writer_init(&w, tx, max_tx);
/* 1. Handle Hello Message (HEL) */
if (strncmp(msg_type, "HELF", 4) == 0) {
(void)opcua_write_bytes(&w, (const uint8_t *)"ACKF", 4);
(void)opcua_write_u32(&w, 28U); /* Length */
(void)opcua_write_u32(&w, SYN_OPCUA_PROTOCOL_VERSION);
(void)opcua_write_u32(&w, (uint32_t)srv->cfg.rx_buf_size);
(void)opcua_write_u32(&w, (uint32_t)srv->cfg.tx_buf_size);
(void)opcua_write_u32(&w, SYN_OPCUA_DEFAULT_BUFFER_SIZE);
(void)opcua_write_u32(&w, 1U); /* MaxChunkCount */
if (w.error) {
return SYN_ERROR;
}
srv->state = SYN_OPCUA_STATE_HELLO_RECEIVED;
*out_len = w.pos;
return SYN_OK;
}
/* 2. Handle OpenSecureChannel (OPN) */
if (strncmp(msg_type, "OPNF", 4) == 0) {
uint32_t channel_id = 0U;
(void)opcua_read_u32(&r, &channel_id);
(void)opcua_write_bytes(&w, (const uint8_t *)"OPNF", 4);
size_t len_pos = w.pos;
(void)opcua_write_u32(&w, 0U); /* Length placeholder */
(void)opcua_write_u32(&w, srv->secure_channel_id);
(void)opcua_write_u32(&w, srv->security_token_id);
(void)opcua_write_u32(&w, srv->sequence_number++);
(void)opcua_write_u32(&w, srv->request_id++);
/* Response TypeId: OpenSecureChannelResponse */
(void)opcua_write_nodeid_num(&w, 0U, 446U);
/* ResponseHeader: timestamp, requestHandle, serviceResult */
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, 0U);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
/* ServerProtocolVersion */
(void)opcua_write_u32(&w, SYN_OPCUA_PROTOCOL_VERSION);
/* SecurityToken: ChannelId, TokenId, CreatedAt, RevisedLifetime */
(void)opcua_write_u32(&w, srv->secure_channel_id);
(void)opcua_write_u32(&w, srv->security_token_id);
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, 3600000U); /* 1 hour lifetime */
if (w.error) {
return SYN_ERROR;
}
/* Patch length */
uint32_t total_len = (uint32_t)w.pos;
w.data[len_pos] = (uint8_t)(total_len & 0xFFU);
w.data[len_pos + 1U] = (uint8_t)((total_len >> 8U) & 0xFFU);
w.data[len_pos + 2U] = (uint8_t)((total_len >> 16U) & 0xFFU);
w.data[len_pos + 3U] = (uint8_t)((total_len >> 24U) & 0xFFU);
srv->state = SYN_OPCUA_STATE_SECURE_CHANNEL;
*out_len = w.pos;
return SYN_OK;
}
/* 3. Handle Service Messages (MSG) */
if (strncmp(msg_type, "MSGF", 4) == 0) {
uint32_t channel_id = 0U;
uint32_t token_id = 0U;
uint32_t seq_num = 0U;
uint32_t req_id = 0U;
(void)opcua_read_u32(&r, &channel_id);
(void)opcua_read_u32(&r, &token_id);
(void)opcua_read_u32(&r, &seq_num);
(void)opcua_read_u32(&r, &req_id);
uint16_t req_ns = 0U;
uint32_t req_type_id = 0U;
(void)opcua_read_nodeid_num(&r, &req_ns, &req_type_id);
(void)opcua_write_bytes(&w, (const uint8_t *)"MSGF", 4);
size_t len_pos = w.pos;
(void)opcua_write_u32(&w, 0U); /* Length placeholder */
(void)opcua_write_u32(&w, srv->secure_channel_id);
(void)opcua_write_u32(&w, srv->security_token_id);
(void)opcua_write_u32(&w, srv->sequence_number++);
(void)opcua_write_u32(&w, req_id);
/* Read Request Dispatch */
if (req_type_id == SYN_OPCUA_REQ_READ) {
(void)opcua_write_nodeid_num(&w, 0U, SYN_OPCUA_RESP_READ);
/* ResponseHeader */
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
/* Skip RequestHeader and read maxAge / timestampsToReturn */
r.pos += 16U;
uint32_t count = 0U;
(void)opcua_read_u32(&r, &count);
if (count > 16U) {
count = 16U;
}
(void)opcua_write_u32(&w, count);
for (uint32_t i = 0; i < count; i++) {
uint16_t ns = 0U;
uint32_t num = 0U;
(void)opcua_read_nodeid_num(&r, &ns, &num);
uint32_t attr_id = 0U;
(void)opcua_read_u32(&r, &attr_id);
SYN_OPCUA_Node *node = syn_opcua_server_find_node_num(srv, ns, num);
if (node != NULL) {
if (node->on_read != NULL) {
(void)node->on_read(srv, node, &node->value, node->user_data);
}
(void)opcua_write_datavalue(&w, &node->value);
} else {
SYN_OPCUA_DataValue bad = {.value = {.type = SYN_OPCUA_TYPE_NULL},
.status_code = SYN_OPCUA_STATUS_BAD_NODE_ID_UNKNOWN};
(void)opcua_write_datavalue(&w, &bad);
}
}
}
/* Write Request Dispatch */
else if (req_type_id == SYN_OPCUA_REQ_WRITE) {
(void)opcua_write_nodeid_num(&w, 0U, SYN_OPCUA_RESP_WRITE);
/* ResponseHeader */
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
/* Skip RequestHeader */
r.pos += 16U;
uint32_t count = 0U;
(void)opcua_read_u32(&r, &count);
if (count > 16U) {
count = 16U;
}
(void)opcua_write_u32(&w, count);
for (uint32_t i = 0; i < count; i++) {
uint16_t ns = 0U;
uint32_t num = 0U;
(void)opcua_read_nodeid_num(&r, &ns, &num);
uint32_t attr_id = 0U;
(void)opcua_read_u32(&r, &attr_id);
SYN_OPCUA_Node *node = syn_opcua_server_find_node_num(srv, ns, num);
if (node != NULL && (node->access_level & 0x02U) != 0U) {
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
} else if (node != NULL) {
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_BAD_NOT_WRITABLE);
} else {
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_BAD_NODE_ID_UNKNOWN);
}
}
}
/* Browse Request Dispatch */
else if (req_type_id == SYN_OPCUA_REQ_BROWSE) {
(void)opcua_write_nodeid_num(&w, 0U, SYN_OPCUA_RESP_BROWSE);
/* ResponseHeader */
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
/* Results count */
(void)opcua_write_u32(&w, 1U);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
(void)opcua_write_string(&w, NULL); /* ContinuationPoint = Null */
/* Return list of child nodes */
uint32_t ref_count = 0U;
for (size_t i = 0; i < srv->node_count; i++) {
if (srv->nodes[i].node_id.id.num != SYN_OPCUA_NODEID_ROOT) {
ref_count++;
}
}
(void)opcua_write_u32(&w, ref_count);
for (size_t i = 0; i < srv->node_count; i++) {
const SYN_OPCUA_Node *n = &srv->nodes[i];
if (n->node_id.id.num == SYN_OPCUA_NODEID_ROOT) {
continue;
}
(void)opcua_write_nodeid_num(&w, 0U, 47U); /* HasComponent ReferenceType */
(void)opcua_write_u8(&w, 0U); /* IsForward = true */
(void)opcua_write_nodeid_num(&w, n->node_id.ns_index, n->node_id.id.num);
(void)opcua_write_string(&w, n->browse_name);
(void)opcua_write_string(&w, n->display_name);
(void)opcua_write_u32(&w, (uint32_t)n->node_class);
(void)opcua_write_nodeid_num(&w, 0U, 0U); /* TypeDefinition */
}
}
/* Create Session Request Dispatch */
else if (req_type_id == SYN_OPCUA_REQ_CREATE_SESSION) {
(void)opcua_write_nodeid_num(&w, 0U, SYN_OPCUA_RESP_CREATE_SESSION);
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
(void)opcua_write_nodeid_num(&w, 1U, 1001U); /* SessionId */
(void)opcua_write_nodeid_num(&w, 1U, 1002U); /* AuthenticationToken */
(void)opcua_write_u64(&w, 3600000U); /* RevisedSessionTimeout */
srv->state = SYN_OPCUA_STATE_SESSION_ACTIVE;
}
/* Activate Session Request Dispatch */
else if (req_type_id == SYN_OPCUA_REQ_ACTIVATE_SESSION) {
(void)opcua_write_nodeid_num(&w, 0U, SYN_OPCUA_RESP_ACTIVATE_SESSION);
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
}
/* Close Session Request Dispatch */
else if (req_type_id == SYN_OPCUA_REQ_CLOSE_SESSION) {
(void)opcua_write_nodeid_num(&w, 0U, SYN_OPCUA_RESP_CLOSE_SESSION);
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_GOOD);
srv->state = SYN_OPCUA_STATE_CLOSED;
} else {
/* Unsupported Service */
(void)opcua_write_nodeid_num(&w, 0U, req_type_id + 3U);
(void)opcua_write_u64(&w, syn_port_get_tick_ms());
(void)opcua_write_u32(&w, req_id);
(void)opcua_write_u32(&w, SYN_OPCUA_STATUS_BAD_SERVICE_UNSUPPORTED);
}
if (w.error) {
return SYN_ERROR;
}
/* Patch Total Length */
uint32_t total_len = (uint32_t)w.pos;
w.data[len_pos] = (uint8_t)(total_len & 0xFFU);
w.data[len_pos + 1U] = (uint8_t)((total_len >> 8U) & 0xFFU);
w.data[len_pos + 2U] = (uint8_t)((total_len >> 16U) & 0xFFU);
w.data[len_pos + 3U] = (uint8_t)((total_len >> 24U) & 0xFFU);
*out_len = w.pos;
return SYN_OK;
}
return SYN_ERROR;
}
SYN_Status syn_opcua_server_step(SYN_OPCUA_Server *srv, uint32_t now_ms)
{
(void)now_ms;
if (srv == NULL || srv->cfg.transport == NULL) {
return SYN_INVALID_PARAM;
}
size_t rx_len = 0U;
if (syn_transport_recv(srv->cfg.transport, srv->cfg.rx_buf, srv->cfg.rx_buf_size, &rx_len) &&
rx_len >= 8U) {
size_t tx_len = 0U;
SYN_Status st = syn_opcua_server_process_message(
srv, srv->cfg.rx_buf, rx_len, srv->cfg.tx_buf, srv->cfg.tx_buf_size, &tx_len);
if (st == SYN_OK && tx_len > 0U) {
(void)syn_transport_send(srv->cfg.transport, srv->cfg.tx_buf, tx_len);
}
}
return SYN_OK;
}
SYN_PT_Status syn_opcua_server_pt(SYN_PT *pt, SYN_Task *task)
{
if (pt == NULL || task == NULL || task->user_data == NULL) {
return PT_ENDED;
}
SYN_OPCUA_Server *srv = (SYN_OPCUA_Server *)task->user_data;
PT_BEGIN(pt);
for (;;) {
uint32_t now = syn_port_get_tick_ms();
if (syn_opcua_server_step(srv, now) != SYN_OK) {
break;
}
PT_YIELD(pt);
}
PT_END(pt);
}
#endif /* SYN_USE_OPCUA */