File syn_router.c¶
File List > net > syn_router.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_ROUTER) || SYN_USE_ROUTER
#include "syn_router.h"
#include "syntropic/util/syn_assert.h"
#include "syntropic/util/syn_pack.h"
#include <string.h>
/* ── Serialization ──────────────────────────────────────────────────────── */
static size_t serialize_packet(const SYN_Packet *pkt, uint8_t *buf)
{
size_t pos = 0;
syn_pack_u8(buf, &pos, pkt->src);
syn_pack_u8(buf, &pos, pkt->dst);
syn_pack_u8(buf, &pos, pkt->type);
syn_pack_u8(buf, &pos, pkt->seq);
syn_pack_u8(buf, &pos, pkt->flags);
syn_pack_u8(buf, &pos, pkt->len);
if (pkt->len > 0) {
syn_pack_bytes(buf, &pos, pkt->payload, pkt->len);
}
return pos;
}
static bool deserialize_packet(const uint8_t *buf, size_t len, SYN_Packet *pkt)
{
if (len < SYN_ROUTER_HEADER_SIZE)
return false;
size_t pos = 0;
pkt->src = syn_unpack_u8(buf, &pos);
pkt->dst = syn_unpack_u8(buf, &pos);
pkt->type = syn_unpack_u8(buf, &pos);
pkt->seq = syn_unpack_u8(buf, &pos);
pkt->flags = syn_unpack_u8(buf, &pos);
pkt->len = syn_unpack_u8(buf, &pos);
if (pkt->len > SYN_ROUTER_MAX_PAYLOAD)
return false;
if (len < (size_t)SYN_ROUTER_HEADER_SIZE + pkt->len)
return false;
if (pkt->len > 0) {
syn_unpack_bytes(buf, &pos, pkt->payload, pkt->len);
}
return true;
}
/* ── ACK helpers ────────────────────────────────────────────────────────── */
static void send_ack(SYN_Router *r, uint8_t dst, uint8_t seq)
{
SYN_Packet ack;
memset(&ack, 0, sizeof(ack));
ack.src = r->node_id;
ack.dst = dst;
ack.type = SYN_MSG_ACK;
ack.seq = seq;
ack.flags = SYN_PKT_FLAG_IS_ACK;
ack.len = 0;
uint8_t buf[SYN_ROUTER_HEADER_SIZE];
size_t n = serialize_packet(&ack, buf);
syn_transport_send(r->transport, buf, n);
}
static void handle_ack(SYN_Router *r, uint8_t src, uint8_t seq)
{
if (r->pending == NULL)
return;
for (uint8_t i = 0; i < r->pending_cap; i++) {
SYN_PendingAck *p = &r->pending[i];
if (p->active && p->dst == src && p->seq == seq) {
p->active = false;
return;
}
}
}
static bool queue_pending(SYN_Router *r, uint8_t dst, uint8_t type, uint8_t seq,
const uint8_t *data, uint8_t len)
{
if (r->pending == NULL)
return false;
for (uint8_t i = 0; i < r->pending_cap; i++) {
SYN_PendingAck *p = &r->pending[i];
if (!p->active) {
p->dst = dst;
p->seq = seq;
p->type = type;
p->retries = 0;
p->sent_tick = syn_port_get_tick_ms();
p->len = len;
if (len > 0)
memcpy(p->payload, data, len);
p->active = true;
return true;
}
}
return false; /* pending table full */
}
static void check_retries(SYN_Router *r)
{
if (r == NULL || r->pending == NULL)
return;
uint32_t now = syn_port_get_tick_ms();
for (uint8_t i = 0; i < r->pending_cap; i++) {
SYN_PendingAck *p = &r->pending[i];
if (!p->active)
continue;
uint32_t elapsed = now - p->sent_tick;
if (elapsed < r->ack_timeout_ms)
continue;
if (p->retries >= r->max_retries) {
p->active = false;
r->drop_count++;
continue;
}
/* Retransmit */
SYN_Packet pkt;
memset(&pkt, 0, sizeof(pkt));
pkt.src = r->node_id;
pkt.dst = p->dst;
pkt.type = p->type;
pkt.seq = p->seq;
pkt.flags = SYN_PKT_FLAG_ACK_REQ;
pkt.len = p->len;
if (p->len > 0)
memcpy(pkt.payload, p->payload, p->len);
uint8_t buf[SYN_ROUTER_HEADER_SIZE + SYN_ROUTER_MAX_PAYLOAD];
size_t n = serialize_packet(&pkt, buf);
syn_transport_send(r->transport, buf, n);
p->retries++;
p->sent_tick = now;
}
}
/* ── Public API ─────────────────────────────────────────────────────────── */
void syn_router_init(SYN_Router *r, uint8_t node_id, SYN_Transport *transport,
SYN_RouterHandler *handlers, uint8_t handler_cap)
{
SYN_ASSERT(r != NULL);
SYN_ASSERT(transport != NULL);
SYN_ASSERT(handlers != NULL);
memset(r, 0, sizeof(*r));
r->node_id = node_id;
r->transport = transport;
r->handlers = handlers;
r->handler_cap = handler_cap;
memset(handlers, 0, sizeof(SYN_RouterHandler) * handler_cap);
}
void syn_router_enable_ack(SYN_Router *r, SYN_PendingAck *pending, uint8_t cap, uint16_t timeout_ms,
uint8_t max_retries)
{
SYN_ASSERT(r != NULL);
r->pending = pending;
r->pending_cap = cap;
r->ack_timeout_ms = timeout_ms;
r->max_retries = max_retries;
if (pending != NULL) {
memset(pending, 0, sizeof(SYN_PendingAck) * cap);
}
}
bool syn_router_register(SYN_Router *r, uint8_t type, SYN_RouterHandlerFn handler, void *ctx)
{
SYN_ASSERT(r != NULL);
if (r->handler_count >= r->handler_cap)
return false;
r->handlers[r->handler_count].type = type;
r->handlers[r->handler_count].handler = handler;
r->handlers[r->handler_count].ctx = ctx;
r->handler_count++;
return true;
}
bool syn_router_send(SYN_Router *r, uint8_t dst, uint8_t type, const uint8_t *data, uint8_t len,
bool reliable)
{
SYN_ASSERT(r != NULL);
SYN_ASSERT(len <= SYN_ROUTER_MAX_PAYLOAD);
SYN_Packet pkt;
memset(&pkt, 0, sizeof(pkt));
pkt.src = r->node_id;
pkt.dst = dst;
pkt.type = type;
pkt.seq = r->tx_seq++;
pkt.flags = reliable ? SYN_PKT_FLAG_ACK_REQ : 0;
pkt.len = len;
if (len > 0 && data != NULL) {
memcpy(pkt.payload, data, len);
}
uint8_t buf[SYN_ROUTER_HEADER_SIZE + SYN_ROUTER_MAX_PAYLOAD];
size_t n = serialize_packet(&pkt, buf);
bool ok = syn_transport_send(r->transport, buf, n);
if (ok) {
r->tx_count++;
if (reliable && r->pending != NULL) {
queue_pending(r, dst, type, pkt.seq, data, len);
}
}
return ok;
}
void syn_router_poll(SYN_Router *r)
{
SYN_ASSERT(r != NULL);
/* Check for ACK timeouts */
check_retries(r);
/* Receive packets */
uint8_t buf[SYN_ROUTER_HEADER_SIZE + SYN_ROUTER_MAX_PAYLOAD];
size_t len = 0;
while (syn_transport_recv(r->transport, buf, sizeof(buf), &len)) {
SYN_Packet pkt;
if (!deserialize_packet(buf, len, &pkt)) {
r->drop_count++;
continue;
}
/* Not for us? */
if (pkt.dst != r->node_id && pkt.dst != 0xFF) {
continue;
}
r->rx_count++;
/* Handle ACK response */
if (pkt.flags & SYN_PKT_FLAG_IS_ACK) {
handle_ack(r, pkt.src, pkt.seq);
continue;
}
/* Send ACK if requested */
if (pkt.flags & SYN_PKT_FLAG_ACK_REQ) {
send_ack(r, pkt.src, pkt.seq);
}
/* Dispatch to handler */
bool handled = false;
for (uint8_t i = 0; i < r->handler_count; i++) {
if (r->handlers[i].type == pkt.type && r->handlers[i].handler) {
r->handlers[i].handler(&pkt, r->handlers[i].ctx);
handled = true;
break;
}
}
if (!handled) {
r->drop_count++;
}
}
}
#endif /* SYN_USE_ROUTER */