Inter-process communication
Every process that is not RepRapFirmware reaches DCS through a single Unix domain socket, by default
/var/run/dsf/dcs.sock. DuetWebServer, the CLI tools, and plugins all use it through the
DuetAPIClient library; the protocol itself is line-framed JSON.
- Server:
src/DuetControlServer/IPC/Server.cs,IPC/Connection.cs - Processors:
src/DuetControlServer/IPC/Processors/ - Client:
src/DuetAPIClient/
You do not have to use the .NET client. The DuetAPI and DuetAPIClient libraries are published on
NuGet (LGPL-3.0-or-later), and there is a separately maintained Python client - but any language that
can open a Unix socket and exchange JSON can speak the protocol directly, as shown below.
Connecting
sequenceDiagram
participant C as Client (DuetAPIClient)
participant S as DCS IPC server
C->>S: connect to /var/run/dsf/dcs.sock
S->>S: assign permissions from PID/UID/GID
S->>C: ServerInitMessage (connection id)
C->>S: ClientInitMessage (mode + protocol version)
S->>S: validate version, create processor for the mode
S-->>C: success
Note over C,S: connection now runs the chosen processor until disconnect
- The client opens the socket. The server creates a
Connectionwith a unique id and works out the client's permissions from its PID/UID/GID: root processes get everything; a plugin process is matched against thePluginsmodel (walking the parent-process tree) to inherit its manifest permissions; other external programs get everything exceptSuperUser. - The server sends a
ServerInitMessage; the client replies with aClientInitMessagenaming the desired connection mode and protocol version. - The server validates the version and instantiates the matching processor
(
IPC/Processors/ProcessorFactory.cs). From here the connection behaves according to its mode.
Permissions are enforced per command: each command type carries a RequiredPermissionsAttribute, and
Connection.CheckPermissions requires the client to hold at least one of the listed permissions
before the command runs.
On the wire
Each message is a single JSON object terminated by a newline. The handshake and a Command-mode
exchange look like this (server messages prefixed <-, client ->):
<- {"id": 12, "version": 11} # ServerInitMessage: connection id + protocol version
-> {"mode": "Command", "version": 11} # ClientInitMessage: chosen mode
<- {"success": true} # server acknowledges
-> {"command": "SimpleCode", "code": "G4 S3"}
<- {"success": true, "result": ""} # sent once the code completes
The same framing carries every command and response; the mode chosen in the ClientInitMessage
decides which processor handles the rest of the connection.
Connection modes
The mode is chosen at connect time (DuetAPI/Connection/Modes/ConnectionMode.cs) and selects the
processor that drives the connection:
| Mode | Processor | Purpose |
|---|---|---|
Command |
Command.cs |
Run commands (including codes), query/patch the model, manage files, plugins, sessions |
CodeStream |
CodeStream.cs |
Stream newline-delimited codes on a channel and read replies, no per-code JSON |
Intercept |
CodeInterception.cs |
Plugin hook into the code pipeline |
Subscribe |
ModelSubscription.cs |
Receive the object model and its patches |
PluginService |
PluginService.cs |
Internal channel between DCS and DuetPluginService |
Command
The general-purpose request/response mode. The client sends a command as JSON; the processor
deserializes it, checks permissions, runs ExecuteAsync, and returns the result. This is what
DuetWebServer uses for every HTTP request. The command set is grouped
under src/DuetControlServer/Commands/:
| Group | Examples | Permission (typical) |
|---|---|---|
| Generic | Code, SimpleCode, EvaluateExpression, Flush, WriteMessage, CheckPassword |
CommandExecution |
| Files | GetFileInfo, ResolvePath |
CommandExecution / filesystem |
| ObjectModel | GetObjectModel, QueryObjectModel, LockObjectModel/UnlockObjectModel, SetObjectModel, PatchObjectModel, SyncObjectModel |
ObjectModelRead / ObjectModelReadWrite |
| Plugins | InstallPlugin, StartPlugin(s), StopPlugin(s), ReloadPlugin, UninstallPlugin, SetPluginData, SetPluginProcess, NotifyPluginStarted |
ManagePlugins |
| HttpEndpoints | AddHttpEndpoint, RemoveHttpEndpoint |
RegisterHttpEndpoints |
| UserSessions | AddUserSession, RemoveUserSession |
ManageUserSessions |
| Packages | InstallSystemPackage, UninstallSystemPackage |
ManagePlugins |
A Code command runs a full Code object; SimpleCode parses one
or more codes from a text string. Both enter the code pipeline.
CodeStream
A throughput-oriented mode: the client writes raw code lines to the socket's stream and reads replies back, bound to one code channel. There is no per-code JSON envelope, so it suits feeding many codes (for example from a generator or a file) with minimal overhead.
Intercept
Lets a plugin observe and alter codes as they pass through the pipeline. The client declares which channels, code types, and interception stage it wants:
- Pre - before DCS processes the code internally,
- Post - after internal processing, before the code is sent to RRF,
- Executed - after the code has run.
For each intercepted code the plugin may Resolve it (supply a result and stop further processing),
Cancel it, Ignore it (let it continue untouched), or Rewrite it (replace it). See how these
stages sit in the pipeline in G-code flow.
CodeLogger and DuetPiManagementPlugin are working examples (Components).
Subscribe
Streams object-model state to the client, either in
Full mode (the whole model after every update) or Patch mode (only the JSON diff). An optional
set of filter paths restricts what is sent. The DuetWebServer WebSocket
and the ModelObserver tool both use this mode.
PluginService
An internal mode used only by DuetPluginService. It is bidirectional: DCS queues
commands (such as "start this plugin") for the plugin service to execute, and the plugin service uses
ordinary Command-mode connections back to DCS. The PID of the non-root plugin service is recorded so
plugin processes cannot impersonate it.
Custom HTTP endpoints
A plugin with the RegisterHttpEndpoints permission can extend the HTTP API
served by DuetWebServer. It sends an AddHttpEndpoint command on a
Command connection:
{
"command": "AddHttpEndpoint",
"endpointType": "GET",
"namespace": "third-party-app",
"path": "test",
"uploadRequest": false
}
DCS replies with the path of a new Unix socket. The plugin listens on that socket; whenever a matching
request arrives at /machine/{namespace}/{path}, DuetWebServer's custom-endpoint middleware forwards
it there and relays the plugin's response back to the HTTP client. Setting endpointType to
WebSocket instead of an HTTP verb registers a bidirectional WebSocket endpoint. RemoveHttpEndpoint
unregisters it. The CustomHttpEndpoint
example is a complete reference.
See also
- Components - the client libraries that speak this protocol
- Object model - what the
Subscribemode streams - G-code flow - what the
Command,CodeStream, andInterceptmodes feed into - Plugins - the permission model enforced on every command