Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e15f901
feat: add socket Command integration types
LK-Simon Aug 20, 2026
dd60862
feat: add socket Command structured protocol
LK-Simon Aug 20, 2026
de58251
feat: add host-testable socket Command session
LK-Simon Aug 20, 2026
db37013
feat: add TCP Command server
LK-Simon Aug 20, 2026
5e3dd92
test: add comprehensive socket Command protocol coverage
LK-Simon Aug 20, 2026
df31d95
test: add host test build for Sockets
LK-Simon Aug 20, 2026
ed9b385
ci: add Sockets host test workflow
LK-Simon Aug 20, 2026
4f55cfe
chore: add temporary Command feature validation
LK-Simon Aug 20, 2026
2125056
chore: record Command feature validation
github-actions[bot] Aug 20, 2026
fcb4097
test: add minimal Arduino host stub
LK-Simon Aug 20, 2026
68c750c
test: include Arduino host stubs
LK-Simon Aug 20, 2026
2c40981
chore: patch and revalidate Command session policy
LK-Simon Aug 20, 2026
afe211e
fix: apply socket policy to line Commands
github-actions[bot] Aug 20, 2026
678ddea
chore: record Command feature validation
github-actions[bot] Aug 20, 2026
080796c
chore: finalize Command integration release metadata
LK-Simon Aug 20, 2026
d68313c
chore: bump Sockets metadata to 0.3.0
LK-Simon Aug 20, 2026
70c48e0
chore: update Sockets 0.3.0 package description
LK-Simon Aug 20, 2026
bf94b82
chore: bump Sockets component version to 0.3.0
LK-Simon Aug 20, 2026
520719c
docs: document optional Command headers
LK-Simon Aug 20, 2026
da71e25
docs: add Sockets 0.3.0 changelog
LK-Simon Aug 20, 2026
c9da495
docs: add TCP Command server example
LK-Simon Aug 20, 2026
ad90baa
docs: add socket Command integration guide
LK-Simon Aug 20, 2026
d6eef52
chore: align README with Sockets 0.3.0
LK-Simon Aug 20, 2026
d5ded02
docs: align Sockets 0.3.0 Command integration
github-actions[bot] Aug 20, 2026
d76699e
test: add IPAddress host stub
LK-Simon Aug 20, 2026
21ca572
chore: revalidate Command feature
LK-Simon Aug 20, 2026
c3bd4a1
chore: record final Command feature validation
github-actions[bot] Aug 20, 2026
e340e9d
chore: finalize and validate socket Command tests
LK-Simon Aug 20, 2026
b157376
test: tighten socket Command protocol coverage
github-actions[bot] Aug 20, 2026
7e521df
chore: record final socket Command validation
github-actions[bot] Aug 20, 2026
9634c52
test: add FreeRTOS host stub
LK-Simon Aug 20, 2026
8d95b72
test: add FreeRTOS task host stub
LK-Simon Aug 20, 2026
8b3ce40
test: verify core Sockets remains Command-independent
LK-Simon Aug 20, 2026
4cc67db
test: add Command-independent core regression
LK-Simon Aug 20, 2026
325e4d4
chore: validate final Sockets 0.3.0 host suite
LK-Simon Aug 20, 2026
dc24484
chore: record final Sockets 0.3.0 host validation
github-actions[bot] Aug 20, 2026
b6a3736
chore: remove temporary validation workflow
LK-Simon Aug 20, 2026
933b368
chore: remove temporary finalization workflow
LK-Simon Aug 20, 2026
5a270fc
chore: remove temporary README patch workflow
LK-Simon Aug 20, 2026
5585e1b
chore: remove temporary validation workflow
LK-Simon Aug 20, 2026
1f631ef
chore: remove temporary validation workflow
LK-Simon Aug 20, 2026
719202a
chore: remove temporary final-suite workflow
LK-Simon Aug 20, 2026
f07893b
chore: remove temporary validation report
LK-Simon Aug 20, 2026
6ac7fc3
chore: remove temporary validation report
LK-Simon Aug 20, 2026
8f941a6
chore: remove temporary validation report
LK-Simon Aug 20, 2026
109a7b9
chore: remove temporary validation report
LK-Simon Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/host-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Host Tests

on:
push:
branches:
- main
- feature/command-socket-integration
pull_request:

jobs:
host-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout Sockets
uses: actions/checkout@v4

- name: Checkout ESPressio Command 0.2.0
uses: actions/checkout@v4
with:
repository: Flowduino/ESPressio-Command
ref: 0.2.0
path: deps/ESPressio-Command

- name: Checkout ESPressio Timing 2.2.2
uses: actions/checkout@v4
with:
repository: Flowduino/ESPressio-Timing
ref: 2.2.2
path: deps/ESPressio-Timing

- name: Checkout ESPressio Units 0.2.1
uses: actions/checkout@v4
with:
repository: Flowduino/ESPressio-Units
ref: 0.2.1
path: deps/ESPressio-Units

- name: Checkout ESPressio Observable 3.0.1
uses: actions/checkout@v4
with:
repository: Flowduino/ESPressio-Observable
ref: 3.0.1
path: deps/ESPressio-Observable

- name: Configure
run: >-
cmake -S tests -B build
-DESPRESSIO_COMMAND_INCLUDE_DIR="$GITHUB_WORKSPACE/deps/ESPressio-Command/src"
-DESPRESSIO_TIMING_INCLUDE_DIR="$GITHUB_WORKSPACE/deps/ESPressio-Timing/src"
-DESPRESSIO_UNITS_INCLUDE_DIR="$GITHUB_WORKSPACE/deps/ESPressio-Units/src"
-DESPRESSIO_OBSERVABLE_INCLUDE_DIR="$GITHUB_WORKSPACE/deps/ESPressio-Observable/src"

- name: Build
run: cmake --build build --parallel

- name: Test
run: ctest --test-dir build --output-on-failure
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## 0.3.0 — 2026-08-20

### Added
- Added opt-in ESPressio Command 0.2.x integration for remote Command invocation over sockets.
- Added host-testable `SocketCommandSession` with line-oriented and structured-binary request modes.
- Added `TCPCommandServer` with isolated per-client Command sessions.
- Added request/connection metadata, policy hooks, result observers, correlation IDs, bounded request handling, and structured request/response framing.
- Added a TCP Command server example and comprehensive host tests for Command framing, dispatch, validation, session isolation, policy and error paths.
- Added a permanent GitHub Actions host-test workflow pinned to released ESPressio dependencies.

### Changed
- Updated package/component version metadata to 0.3.0.
- Updated README and ESPressio dependency documentation for optional Command integration.
- Expanded host regression testing to retain coverage of the existing socket clock-synchronization protocol.

### Compatibility
- Core ESPressio Sockets remains independent of ESPressio Command.
- Existing Event Transport and Timing synchronization APIs remain source-compatible.
- ESPressio Command is required only when Command integration headers are selected.

## 0.2.3 — 2026-08-20

### Changed
Expand Down
262 changes: 262 additions & 0 deletions COMMAND_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
# ESPressio Sockets Command Integration

ESPressio Sockets 0.3.0 adds opt-in integration with **ESPressio Command >= 0.2.0 < 1.0.0**.

Core ESPressio Sockets remains independent of ESPressio Command. Command support is activated only when the corresponding integration headers are selected.

## Dependency direction

```text
ESPressio Sockets core
-> no Command dependency

Socket Command integration
- - -> ESPressio Command >= 0.2.0 < 1.0.0
```

`SocketCommandSession` owns byte-stream framing, bounded request accumulation, socket-side metadata, policy hooks and result transport. ESPressio Command continues to own Command definition, parsing, typed parameter validation, routing and callback execution.

## Architecture

```text
TCP client
|
v
TCPCommandServer
|
+-- per-client SocketCommandSession
+-- framing / bounded buffering
+-- connection metadata
+-- policy / result hooks
|
v
ESPressio CommandRegistry
|
v
typed Command callback
|
v
CommandResult
|
v
originating TCP client
```

## Public headers

```cpp
#include <ESPressio_SocketCommandTypes.hpp>
#include <ESPressio_SocketCommandProtocol.hpp>
#include <ESPressio_SocketCommandSession.hpp>
#include <ESPressio_TCPCommandServer.hpp>
```

These headers are deliberately not included by `ESPressio_Sockets.hpp`, preserving the optional dependency.

## TCP Command server

`TCPCommandServer` accepts multiple TCP clients and assigns an independent `SocketCommandSession` to each connection.

```cpp
#include <ESPressio_Command.hpp>
#include <ESPressio_TCPCommandServer.hpp>

using namespace ESPressio;

Sockets::TCPCommandServer server;

auto& commands = Command::CommandRegistry::GetInstance();
commands.Command("system")
.Command("status")
.OnExecute([](const Command::CommandContext&) {
return Command::CommandResult::Ok("System OK");
});

Sockets::TCPCommandServerConfig config;
config.Port = 2323;
config.MaximumClients = 4;
config.Session.Mode = Sockets::SocketCommandMode::Line;
config.Session.MaximumRequestBytes = 512;

server.Initialize(config, commands);
```

The consuming application remains responsible for establishing Wi-Fi/network connectivity before the server is initialized.

## Line-oriented mode

Line mode is intended for interactive or simple text clients.

Requests use normal ESPressio Command syntax:

```text
system status
gpio write 2 high
```

Each completed request produces one newline-delimited response:

```text
OK 0 System OK
ERR 1 Unknown command 'example'
```

LF and CRLF input are accepted. Quoting, escaping, Command-tree resolution and typed parameter parsing are delegated to ESPressio Command.

Fragmented TCP reads are accumulated until a complete line is available. Multiple complete commands received in one TCP read are processed independently.

## Structured-binary mode

Machine callers can avoid manufacturing command-line text by sending a structured `CommandInvocation` representation.

The version-1 request contains:

```text
magic
protocol version
request/correlation ID
Command path
positional parameters
named parameters
raw caller string
```

The version-1 response contains:

```text
magic
protocol version
matching request/correlation ID
success/failure
CommandResult code
CommandResult message
```

Structured payloads use a 32-bit length prefix and an ESPressio-owned binary encoding.

This mode intentionally does **not** require ESPressio Serializable or JSON.

The protocol helpers are exposed through `ESPressio_SocketCommandProtocol.hpp`:

```cpp
SocketCommandProtocol::EncodeRequest(...)
SocketCommandProtocol::DecodeRequest(...)
SocketCommandProtocol::EncodeResponse(...)
SocketCommandProtocol::DecodeResponse(...)
SocketCommandProtocol::FrameStructuredPayload(...)
```

## Session isolation

Each TCP client receives an independent `SocketCommandSession`.

The following state is therefore isolated per connection:

- partial line input;
- structured-frame accumulation;
- request/correlation state;
- remote address/port metadata;
- session ID;
- protocol recovery state.

A partial request from one client can never be completed by bytes arriving from another client.

## Resource limits

`SocketCommandSessionConfig` exposes:

```text
Mode
MaximumRequestBytes
DisconnectOnProtocolError
IgnoreEmptyLines
```

Line requests are bounded by `MaximumRequestBytes`. Oversized lines are discarded through the next newline before normal processing resumes.

Structured requests declare their payload length before execution and are rejected if the declared payload exceeds the configured limit.

`TCPCommandServerConfig::MaximumClients` is additionally bounded by `ESPRESSIO_SOCKETS_MAX_TCP_CLIENTS`.

## Connection metadata

Each invocation is associated with `SocketCommandMetadata`:

```text
Transport
RemoteAddress
RemotePort
SessionID
RequestID
```

`TCPCommandServer` populates the TCP connection fields automatically.

This metadata is supplied to the socket-side policy and result-observer hooks so applications can implement authorization, rate limiting, audit, diagnostics or transport-specific policy without coupling those concerns to Command callbacks.

## Policy hook

A server can apply a policy before a remote Command is executed:

```cpp
server.SetPolicy(
[](const Sockets::SocketCommandInvocationContext& context) {
if (context.Metadata.SessionID == 0) {
return Command::CommandResult::Error(
"Invalid remote session"
);
}

return Command::CommandResult::Ok();
}
);
```

Returning an error prevents the Command callback from executing and returns that `CommandResult` to the originating client.

## Result observation

Completed remote invocations can be observed independently of application Command callbacks:

```cpp
server.SetResultObserver(
[](const Sockets::SocketCommandInvocationContext& context,
const Command::CommandResult& result) {
// Diagnostics or audit handling.
}
);
```

## Command and Event semantics

Socket Command and Event integrations are complementary:

```text
Command
remote caller requests an action

Event
a device reports that something happened
```

A typical application may therefore receive a Command over TCP, perform the requested operation, then dispatch an Event describing the resulting state change.

## PlatformIO

```ini
lib_deps =
flowduino/ESPressio-Sockets@^0.3.0
flowduino/ESPressio-Command@^0.2.0
```

The existing Event and Timing dependencies remain required only when their corresponding Sockets integrations are selected.

## Example

See:

```text
examples/TCPCommandServer/TCPCommandServer.ino
```

for an ESP32 example registering application Commands and exposing them through the TCP Command server.
5 changes: 5 additions & 0 deletions ESPRESSIO_DEPENDENCY_CHART.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,8 @@ Units + Serializable
```

This keeps the individual libraries independently useful while allowing progressively richer ESPressio compositions without imposing unnecessary dependencies on applications that do not use those integrations.


## ESPressio Sockets → ESPressio Command — opt-in

Sockets 0.3.0 optionally consumes **ESPressio Command >= 0.2.0 < 1.0.0** when `SocketCommandSession` or `TCPCommandServer` is selected. Core Sockets remains independent of Command. The integration owns socket framing, bounded per-client state, connection/request metadata and result transport; ESPressio Command continues to own Command definition, typed parsing/validation, routing and callback execution. The structured socket protocol does not require ESPressio Serializable.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ ESPressio Sockets provides IP/socket-oriented communication adapters separately

## Latest Stable Version

The current repository version is **0.2.3**.
The current repository version is **0.3.0**.

## Compatibility

ESPressio Sockets `0.2.3` targets the **ESP32 family under Arduino-ESP32** and uses C++17.
ESPressio Sockets `0.3.0` targets the **ESP32 family under Arduino-ESP32** and uses C++17.

The library uses Arduino-ESP32 native networking classes for UDP, TCP and TLS. WebSocket support is provided through the mature Links2004 `arduinoWebSockets` library.

Expand Down Expand Up @@ -61,6 +61,8 @@ ESPressio Event >= 5.7.1 < 6.0.0

and therefore the Serializable support used by ESPressio Event Transport.

Command invocation is also opt-in and targets **ESPressio Command >= 0.2.0 < 1.0.0**. See [Command Integration](COMMAND_INTEGRATION.md) for the TCP server, line/structured protocols, session metadata, policy hooks, limits, and examples.

WebSocket adapters additionally use:

```text
Expand Down Expand Up @@ -570,7 +572,7 @@ build_flags =
-std=gnu++17

lib_deps =
flowduino/ESPressio-Sockets@^0.2.3
flowduino/ESPressio-Sockets@^0.3.0
flowduino/ESPressio-Event@^5.7.1
links2004/WebSockets@^2.3.6
knolleary/PubSubClient@^2.8
Expand Down
6 changes: 3 additions & 3 deletions component.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ CXXFLAGS += -std=gnu++17
CPPFLAGS += \
-DESPRESSIO_SOCKETS \
-DESPRESSIO_SOCKETS_VERSION_MAJOR=0 \
-DESPRESSIO_SOCKETS_VERSION_MINOR=2 \
-DESPRESSIO_SOCKETS_VERSION_PATCH=3 \
-DESPRESSIO_SOCKETS_VERSION_STRING=\"0.2.3\"
-DESPRESSIO_SOCKETS_VERSION_MINOR=3 \
-DESPRESSIO_SOCKETS_VERSION_PATCH=0 \
-DESPRESSIO_SOCKETS_VERSION_STRING=\"0.3.0\"
Loading
Loading