DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
southbound.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 southbound.h
6 * @brief Southbound protocol-driver framework (DETWS_ENABLE_SOUTHBOUND).
7 *
8 * The northbound surface of this library is HTTP/WS/SNMP/etc. to a controller; the *southbound* surface
9 * is the field devices it polls and drives (a Modbus slave, a BACnet controller, a raw sensor over
10 * SPI/I2C/UART). Today Modbus master is the one such driver, hand-wired by the app. This is the uniform
11 * seam every southbound driver plugs into, so the app addresses any field device the same way regardless
12 * of the wire protocol underneath: register a driver (a small vtable + its instance context), then
13 * read/write *points* (registers, coils, objects) by driver name through one facade.
14 *
15 * One owner, one API: the framework owns driver lookup + dispatch; each driver owns its own transport
16 * (it is handed the point id and returns the value, doing whatever Modbus/BACnet/GPIO I/O it needs). The
17 * block (matrix) read/write is the atomic multi-point path - a contiguous span of points moved in one
18 * driver call, which a protocol like Modbus can satisfy as a single request.
19 *
20 * Pure registry + dispatch: no heap, no stdlib, host-testable with a fake driver. Drivers are borrowed
21 * (the caller keeps the SouthboundDriver + its ctx alive for the registry's lifetime).
22 */
23
24#ifndef DETERMINISTICESPASYNCWEBSERVER_SOUTHBOUND_H
25#define DETERMINISTICESPASYNCWEBSERVER_SOUTHBOUND_H
26
27#include "ServerConfig.h"
28#include <stddef.h>
29#include <stdint.h>
30
31#if DETWS_ENABLE_SOUTHBOUND
32
33/** @brief Result codes. A driver may also return its own negative transport error, passed through. */
34// Southbound result codes. The API returns int (SB_OK / a count on success, or a negative code), so
35// the return stays int and these are integer constants in a namespacing struct - callers keep their
36// `< 0` and `== SB_OK` checks cast-free.
37struct Sb
38{
39 static constexpr int SB_OK = 0; ///< success.
40 static constexpr int SB_ERR_NOT_FOUND = -1; ///< no registered driver by that name.
41 static constexpr int SB_ERR_UNSUPPORTED = -2; ///< the driver does not implement that operation.
42 static constexpr int SB_ERR_ARG = -3; ///< a null / out-of-range argument.
43 static constexpr int SB_ERR_FULL = -4; ///< the registry is full (registration only).
44 static constexpr int SB_ERR_DUP = -5; ///< a driver with that name is already registered.
45};
46
47/**
48 * @brief One southbound driver: a vtable over an application-owned transport context.
49 *
50 * @p read / @p write move a single point; @p read_block / @p write_block move a contiguous span of
51 * points atomically (may be null - the framework then reports SB_ERR_UNSUPPORTED). Every callback takes
52 * the driver's own @p ctx first. A callback returns SB_OK / a count on success, or a negative code on
53 * failure (its own transport error, propagated unchanged).
54 */
55struct SouthboundDriver
56{
57 const char *name; ///< unique driver name (borrowed).
58 int (*read)(void *ctx, uint32_t point, int32_t *value_out); ///< read one point.
59 int (*write)(void *ctx, uint32_t point, int32_t value); ///< write one point.
60 int (*read_block)(void *ctx, uint32_t first, int32_t *out, size_t n); ///< read n points -> out (>=0 count).
61 int (*write_block)(void *ctx, uint32_t first, const int32_t *in, size_t n); ///< write n points (>=0 count).
62 void *ctx; ///< driver instance state (borrowed).
63};
64
65/**
66 * @brief Register a driver (borrowed; must outlive the registry).
67 * @return SB_OK, SB_ERR_ARG (null / no name), SB_ERR_DUP (name taken), or SB_ERR_FULL.
68 */
69int detws_southbound_register(const SouthboundDriver *drv);
70
71/** @brief Drop all registrations (test / re-init helper). */
72void detws_southbound_clear(void);
73
74/** @brief Number of registered drivers. */
75size_t detws_southbound_count(void);
76
77/** @brief Look up a driver by name, or null. */
78const SouthboundDriver *detws_southbound_find(const char *name);
79
80/** @brief Read one point from a named driver. @return SB_OK / negative code. */
81int detws_southbound_read(const char *name, uint32_t point, int32_t *value_out);
82
83/** @brief Write one point to a named driver. @return SB_OK / negative code. */
84int detws_southbound_write(const char *name, uint32_t point, int32_t value);
85
86/** @brief Atomically read @p n points starting at @p first. @return count read (>=0) / negative code. */
87int detws_southbound_read_block(const char *name, uint32_t first, int32_t *out, size_t n);
88
89/** @brief Atomically write @p n points starting at @p first. @return count written (>=0) / negative code. */
90int detws_southbound_write_block(const char *name, uint32_t first, const int32_t *in, size_t n);
91
92#endif // DETWS_ENABLE_SOUTHBOUND
93#endif // DETERMINISTICESPASYNCWEBSERVER_SOUTHBOUND_H
User-facing configuration for DeterministicESPAsyncWebServer.