hub only.Expand description
An async GridConnect hub (feature hub).
Every frame received on one Port is forwarded to every other port.
Ports can be TCP GridConnect clients (JMRI, desktop apps, other tools),
uplinks to another hub, or serial adapters (feature serial).
Applications can also attach their own ports with Hub::port, e.g. to
run an OpenLCB node inside the same process as the hub.
§Semantics
The hub behaves like a CAN bus segment for GridConnect links:
- Fan-out. A frame sent by one port is delivered to every other attached port, in order.
- No echo. A port never receives its own frames. (On a real CAN bus a transmitter does not receive its own frame either, and OpenLCB alias logic depends on that: seeing “your” alias from someone else means a collision.)
- No routing or filtering. Frames are relayed verbatim; the hub does not parse messages or track aliases, so it is not an OpenLCB node and never appears on the network. Malformed GridConnect input is dropped.
- Back-pressure by dropping. All ports share one broadcast ring of
4096 frames (a
tokio::sync::broadcastchannel); each port reads it at its own pace. A port that falls more than 4096 frames behind skips the frames it missed and carries on from the oldest one still in the ring, like a congested CAN interface, instead of slowing down everybody else. The skip is reported through thelogfeature. - Reads never wait for writes. Each stream is read and written concurrently, so a peer that stops reading cannot stop the hub from draining what that peer sends.
§Logging
The library prints nothing by default. With the log feature,
connections, disconnections, accept errors and lagging ports are
reported through the log crate (target
rolcb::hub). The rolcb-hub binary enables it and prints to stderr.
JMRI ──TCP:12021──┐ ┌── desktop app (TCP :12021)
│ ┌───────┐ │
other hub ◄─uplink┼─────►│ Hub │◄─────┼── your app: hub.port()
│ └───────┘ │
USB buffer ─serial┘ └── rolcb-hub -v monitorOutgoing frames are written as newline-terminated GridConnect text; frames already queued are coalesced into a single write (up to 64 frames) to keep latency and syscall counts low.
§Example
use rolcb::hub::{self, Hub};
use tokio::net::TcpListener;
let hub = Hub::new();
tokio::spawn(hub::serve_tcp(hub.clone(), TcpListener::bind("0.0.0.0:12021").await?));
tokio::spawn(hub::uplink_tcp(hub.clone(), "jmri.local:12021".to_string()));
// Watch the traffic from inside the process.
let mut port = hub.port();
while let Some(frame) = port.recv().await {
println!("{}", rolcb::gridconnect::format(&frame));
}See the hub guide for the command-line tool and for embedding the hub in your own program.
Structs§
Constants§
- CHANNEL_
DEPTH - Capacity of the broadcast ring shared by all ports, in frames.
Functions§
- run_
gridconnect_ stream - Relay GridConnect text between a byte stream (TCP socket, serial port) and the hub until the stream closes.
- serve_
tcp - Accept GridConnect TCP clients forever.
- uplink_
serial serial - Keep a USB/serial GridConnect adapter attached, reopening it forever.
- uplink_
tcp - Keep a connection to an upstream GridConnect TCP hub, reconnecting forever.