-
Notifications
You must be signed in to change notification settings - Fork 3
ProtocolExchange
ProtocolExchangeis the SysMod that decodes incoming framed messages, dispatches REST requests to theRestAPIEndpointManager, and owns the file/stream sessions used for uploads, downloads and OTA. It sits between the Comms Channels layer and the application's REST endpoints.For the wider picture see Communications Stack Overview.
-
Register the standard protocol codecs (
RICSerial,RICFrame,RICJSON) with the Comms Channel Manager so any channel that names one of these protocols gets a working codec. -
Receive every framed
CommsChannelMsgthat comes back from those codecs. -
Decode the inner RICREST message when the protocol is
MSG_PROTOCOL_RICRESTand switch on its element code. -
Dispatch URL-style requests to
RestAPIEndpointManager::handleApiRequest()and wrap the response into aCmdRespJSONelement to return on the originating channel. - Own file/stream sessions for chunked uploads, downloads (see File Download Protocol), and firmware updates.
-
Handle bridge envelopes (
MSG_PROTOCOL_BRIDGE_RICREST) by unwrapping and forwarding to the appropriate bridge. - Notify SysManager about file/stream/firmware activity via a hook function.
ProtocolExchange is itself a SysMod and is created early by RaftCoreApp (or whatever code creates SysManager). At construction it does very little; the protocol set-up happens in addCommsChannels():
void ProtocolExchange::addCommsChannels(CommsCoreIF& commsCore)
{
// Each ProtocolCodecFactoryHelper carries:
// - protocol name (matches the name used in registerChannel())
// - factory function
// - the SysMod's config + a prefix
// - inbound rx callback (= processEndpointMsg)
// - inbound canAccept callback (= canProcessEndpointMsg)
commsCore.addProtocol(/* RICSerial helper */);
commsCore.addProtocol(/* RICFrame helper */);
commsCore.addProtocol(/* RICJSON helper */);
}Because all three codecs share the same rx callback (processEndpointMsg), every framed message — from any transport using any of these codecs — is funnelled through one method.
This is the central dispatch:
CommsChannelMsg → switch(getProtocol())
├─ MSG_PROTOCOL_RICREST → decode RICRESTMsg, switch on RICRESTElemCode
│ ├─ URL → processRICRESTURL → RestAPIEndpointManager::handleApiRequest
│ ├─ Body → processRICRESTBody → endpoint body callback
│ ├─ CmdRespJSON → processRICRESTCmdRespJSON (handles incoming responses)
│ ├─ CommandFrame → processRICRESTCmdFrame → cmdName + JSON args → handleApiRequest
│ └─ FileBlock → processRICRESTFileStreamBlock → FileStreamSession
├─ MSG_PROTOCOL_BRIDGE_RICREST → unwrap + bridgeHandleInboundMsg
├─ MSG_PROTOCOL_RAWCMDFRAME → JSON cmdName + query string → handleApiRequest
└─ MSG_PROTOCOL_ROSSERIAL → not used in this direction
For successful URL/CommandFrame requests the response string is wrapped in a CmdRespJSON RICREST message, marked as a response (endpointMsg.setAsResponse(cmdMsg) copies channelID and msgNumber), and sent via commsCore.outboundHandleMsg().
If processing exceeds MSG_PROC_SLOW_PROC_THRESH_MS (50 ms) a warning is logged — handlers should return quickly and offload long work to their own loop.
| Code | Direction | Purpose |
|---|---|---|
URL |
Inbound | A REST request as a URL string (endpoint/path?query=...). Mapped 1:1 to RestAPIEndpointManager::handleApiRequest. |
Body |
Inbound | Body data accompanying a previously-issued URL request (chunked POST-style flow). |
CmdRespJSON |
Both | JSON command/response payload. Used for responses, and as an alternative inbound request format that carries reqStr + body in one JSON. |
CommandFrame |
Inbound | A JSON object with cmdName and arbitrary fields, treated as cmdName?key=value&.... |
FileBlock |
Inbound | A binary chunk of an active file/stream session (streamID + 24-bit file position + payload). |
For the wire layout of each element see RICREST Protocol.
ProtocolExchange owns up to MAX_SIMULTANEOUS_FILE_STREAM_SESSIONS (3) FileStreamSession objects. A session is created on demand when:
- A
dfStartcommand (file download — see File Download Protocol) is received. - An upload is started with
ufStart. - A real-time stream is started with
ufStartandfileType: "rtstream"(see Real-Time Streams). - A firmware update is initiated.
Each session is identified by a unique streamID, a channel ID (so concurrent sessions on different channels don't collide), and a content type / flow type. Stream IDs on the wire are non-zero values; 0 is reserved as FILE_STREAM_ID_ANY for internal lookup cases where the stream ID is unspecified. Normal FILEBLOCK data for an allocated upload, download, or real-time stream must carry the non-zero session ID in the FILEBLOCK header.
Inbound FileBlock RICREST messages are routed to the matching session by stream ID. The FILEBLOCK header encodes stream identity and position as one big-endian 32-bit word:
(streamID << 24) | (filePos & 0x00ffffff)
The low 24 bits are the position within the file/stream. The high byte is not available for file position data.
ProtocolExchange::loop() services every active session and removes inactive ones. When file/stream/firmware-update activity changes, the registered fileStreamActivityHookFn is invoked so SysManager can update its busy-state indicator.
void setFileStreamActivityHook(FileStreamActivityHookFnType cb);
void setFWUpdateHandler(RaftSysMod* pFirmwareUpdater);
// HTTP file uploads use this entry point directly
RaftRetCode handleFileUploadBlock(const String& req,
FileStreamBlock& block,
const APISourceInfo& sourceInfo,
FileStreamBase::FileStreamContentType contentType,
const char* restAPIEndpointName);setFWUpdateHandler lets the application plug in a firmware-update SysMod (e.g. ESPOTAUpdate from RaftSysMods); ProtocolExchange routes firmware streams to it.
ProtocolExchange itself takes no SysType configuration today — its behaviour is determined entirely by the standard protocol set it registers and the channels created by other SysMods.
getDebugJSON() returns a JSON array describing each active file/stream session — name, channel ID, stream ID, current state. Visible via sysmoddebug/<protoExchSysModName> if reporting is enabled.
Getting Started
- Quick Start
- Architecture at a Glance
- Writing Your First SysMod
- Adding a Comms Channel
- Adding an I2C Device Type
- PlatformIO / Arduino
Scaffolding & Building
- Raft CLI
- SysTypes
- Top-Level SysType
- Build Process
- WebUI Build Pipeline
- File System
- Partitions & Flash
- Local Dev Libraries
- Library Developer Guide
Architecture
Built-in SysMods
- NetworkManager
- BLEManager
- WebServer
- MQTTManager
- SerialConsole
- CommandSerial
- CommandSocket
- CommandFile
- FileManager
- LogManager
- ESPOTAUpdate
- StatePublisher
- Remote Logging
- Data Source Registration
Comms & Protocols
- Stack Overview
- Comms Channels
- ProtocolExchange
- RICREST Protocol
- Real-Time Streams
- Adding REST Endpoints
- Built-in REST Endpoints
- File Download (OKTO)
- OTA Update Flow
Devices & Buses
- DeviceManager
- Device Manager REST API
- Device Factory & Classes
- Device Type Records
- Adding an I2C Device Type
- Device Data Publishing
- Data Logger
- I2C Bus
- I2C Device Scanning
- I2C ID & Polling
- MotorControl Overview
- MotorControl Config
- MotorControl Commands
Helpers
Reference