BlueSky Network Client¶
Standalone ZMQ client adapted from the BlueSky simulator's own
bluesky.network package — it replicates the essential Client/Node
functionality without requiring the full BlueSky framework.
WebATM.bluesky_client¶
WebATM.bluesky_client ¶
Provide a BlueSky-compatible network client adapted from the BlueSky simulator.
This module contains networking code adapted from the BlueSky Air Traffic
Management simulator's network infrastructure (the bluesky.network package).
It replicates essential BlueSky Client/Node functionality without requiring the
full BlueSky framework dependency, following ZMQ best practices.
Original BlueSky project: https://github.com/TUDelft-CNS-ATM/bluesky BlueSky is developed by TU Delft (Delft University of Technology).
Key adaptations from BlueSky's networking components:
- Node ID generation algorithms (from
bluesky.network.common) - ZMQ socket management patterns (from
bluesky.network.client) - Message serialization/deserialization (from
bluesky.network) - Subscription and signal handling patterns
BlueSkySignal ¶
Provide a simple signal/slot implementation.
Adapted from BlueSky's signal patterns.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Human-readable signal name, used in warning logs. |
callbacks |
list
|
Callbacks currently connected to this signal. |
Initialize the signal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Human-readable signal name, used in warning logs. |
required |
Source code in WebATM/bluesky_client.py
connect ¶
Connect a callback to this signal.
Idempotent: a callback that is already connected is not added again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
callback
|
Callable
|
Callable invoked whenever the signal is emitted. |
required |
Source code in WebATM/bluesky_client.py
disconnect ¶
Disconnect a callback from this signal.
A callback that is not connected is silently ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
callback
|
Callable
|
Callback to remove. |
required |
Source code in WebATM/bluesky_client.py
emit ¶
Emit the signal to all connected callbacks.
Iterates over a snapshot of the callback list so callbacks may connect or disconnect during emission. Exceptions raised by a callback are logged and do not stop delivery to the remaining callbacks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments forwarded to each callback. |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to each callback. |
{}
|
Source code in WebATM/bluesky_client.py
BlueSkySubscriber ¶
Provide a simple topic-based subscriber system.
Adapted from BlueSky's subscriber patterns.
Attributes:
| Name | Type | Description |
|---|---|---|
subscribers |
dict[str, list]
|
Mapping of topic name to the callbacks subscribed to that topic. |
Initialize the subscriber registry with no subscriptions.
Source code in WebATM/bluesky_client.py
subscribe ¶
Subscribe a callback to a topic.
Idempotent, like BlueSkySignal.connect: a callback already
subscribed to the topic is not added again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
str
|
Topic name to subscribe to. |
required |
callback
|
Callable
|
Callable invoked when data is emitted on the topic. |
required |
Source code in WebATM/bluesky_client.py
emit ¶
Emit data to all subscribers of a topic.
Exceptions raised by a callback are logged and do not stop delivery to the remaining callbacks. Topics without subscribers are ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
str
|
Topic name to emit on. |
required |
*args
|
Any
|
Positional arguments forwarded to each callback. |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to each callback. |
{}
|
Source code in WebATM/bluesky_client.py
BlueSkyStack ¶
Provide a simple command stack.
Adapted from BlueSky's command stack patterns.
Attributes:
| Name | Type | Description |
|---|---|---|
cmdstack |
deque
|
Queued |
sender_id |
Sender ID of the command currently being processed, or None. |
|
current |
str
|
Command currently being processed, or an empty string. |
Initialize an empty command stack.
Source code in WebATM/bluesky_client.py
stack ¶
Queue one or more command lines.
Each command line is stripped of surrounding whitespace; empty lines are ignored. Semicolon-separated compound lines are split into individual commands, each queued with the given sender ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*cmdlines
|
str
|
One or more command lines to queue. |
()
|
sender_id
|
bytes | str | None
|
Identifier of the command originator, stored alongside each queued command. |
None
|
Source code in WebATM/bluesky_client.py
commands ¶
Iterate over queued commands with sender tracking.
Pops commands from the front of the stack, exposing each one through the
current and sender_id attributes while it is being processed.
Both attributes are reset when the stack is exhausted.
Yields:
| Type | Description |
|---|---|
str
|
The next queued command. |
Source code in WebATM/bluesky_client.py
BlueSkyContext ¶
Provide a simple simulation context object.
Adapted from BlueSky's simulation context patterns. Tracks the shared-state action and sender of the message currently being processed.
Attributes:
| Name | Type | Description |
|---|---|---|
action |
Action type of the message being processed (e.g. "RESET"), or None. |
|
sender_id |
Node ID of the message sender, or None. |
|
Reset |
str
|
BlueSky action constant for a simulation reset ("RESET"). |
ActChange |
str
|
BlueSky action constant for an active-node change ("ACTCHANGE"). |
Initialize the context with no active action or sender.
Source code in WebATM/bluesky_client.py
BlueSkyClient ¶
Provide a BlueSky-compatible network client.
Adapted from bluesky.network.client.Client. Replicates essential BlueSky
Client/Node functionality with proper ZMQ lifecycle management, including:
- ZMQ socket management and lifecycle
- Node discovery and server communication
- Message subscription and publishing patterns
- Command stacking and processing
Attributes:
| Name | Type | Description |
|---|---|---|
node_id |
bytes
|
Unique identifier of this client node. |
group_id |
bytes
|
Group prefix derived from the node ID. |
server_id |
bytes
|
Derived ID of the server this client belongs to. |
act_id |
bytes | None
|
ID of the active simulation node, or None. |
connected |
bool
|
Whether the client is connected to a server. |
running |
bool
|
Whether the client is running (accepting I/O). |
nodes |
set
|
Known simulation node IDs. |
servers |
set
|
Known server IDs. |
node_added |
BlueSkySignal
|
Emitted when a new simulation node appears. |
node_removed |
BlueSkySignal
|
Emitted when a simulation node disappears. |
server_added |
BlueSkySignal
|
Emitted when a new server appears. |
server_removed |
BlueSkySignal
|
Emitted when a server disappears. |
actnode_changed |
BlueSkySignal
|
Emitted when the active node changes. |
subscriber |
BlueSkySubscriber
|
Topic subscription registry. |
stack |
BlueSkyStack
|
Command stack. |
context |
BlueSkyContext
|
Shared-state processing context. |
Initialize the client and its identifiers, signals, and state.
Generates the node/group/server IDs, sets up the socket lock and network
state tracking, and auto-connects the node_added signal to active-node
selection and initial data requests. No network resources are created
until connect() is called.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
group_id
|
int
|
Group identifier for node ID generation. Defaults to
|
GROUPID_CLIENT
|
Source code in WebATM/bluesky_client.py
connect ¶
Connect to a BlueSky server following the ZMQ pattern.
Creates the ZMQ context, SUB (data) and XPUB (command) sockets, tunes
buffer and shutdown options, connects both sockets, registers them with a
poller, and subscribes to messages targeted at this node so the server
can discover it. On failure all partially-created resources are released
via close().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hostname
|
str
|
BlueSky server hostname or IP address. |
'localhost'
|
recv_port
|
int
|
Port for receiving simulation data. |
11000
|
send_port
|
int
|
Port for sending commands and events. |
11001
|
protocol
|
str
|
ZMQ transport protocol (e.g. "tcp"). |
'tcp'
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the connection was established, False otherwise. |
Source code in WebATM/bluesky_client.py
close ¶
Close all connections following ZMQ pattern.
Source code in WebATM/bluesky_client.py
update ¶
Update function - call periodically to receive and process data.
Source code in WebATM/bluesky_client.py
receive ¶
Receive and process incoming messages (following ZMQ recv pattern).
Socket I/O (poll + recv) is done under _sock_lock so it cannot race
a concurrent send() from a command thread. Every currently-available
message is drained in one pass — a single recv per tick would let a
fast-forward burst back up for many ticks (stale map, growing latency).
Handler dispatch runs outside the lock: handlers can re-enter the
client (e.g. node discovery triggers a REQUEST send), and keeping the
lock hold short means command sends are never blocked by serialisation.
Source code in WebATM/bluesky_client.py
send ¶
Send data to a topic.
Source code in WebATM/bluesky_client.py
subscribe ¶
Subscribe to a topic with callback.
Source code in WebATM/bluesky_client.py
actnode ¶
Set or get the active simulation node.
Source code in WebATM/bluesky_client.py
addnodes ¶
Tell server to add nodes.
Source code in WebATM/bluesky_client.py
delnode ¶
Tell the owning server to terminate a single simulation node.
The DELNODE message carries the raw node id and is addressed to the server that spawned the node (same group, sequence index 0). Servers without DELNODE support ignore unknown topics, so this is safe to send to older BlueSky versions.
Source code in WebATM/bluesky_client.py
on_node_added_request_data ¶
When a new node is announced, request the initial/current state of all subscribed shared states.
Source code in WebATM/bluesky_client.py
genid ¶
Generate a unique node identifier.
Adapted from bluesky.network.common.genid(). Builds an ID of IDLEN
bytes: the group prefix, padded with random bytes if needed (avoiding the
* wildcard byte), followed by a one-byte encoding of the sequence index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
group_id
|
int | str | bytes
|
Group identifier used as the ID prefix. Integers and strings are encoded via the charmap codec; bytes are used as-is. |
GROUPID_NOGROUP
|
seqidx
|
int
|
Sequence index encoded as the final byte of the ID. |
1
|
Returns:
| Type | Description |
|---|---|
bytes
|
A node ID of |
Source code in WebATM/bluesky_client.py
asbytestr ¶
Convert a value to a byte string.
Adapted from bluesky.network.common.asbytestr(). Integers are encoded as
a single character via the charmap codec, strings are charmap-encoded, and
any other value is returned unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
int | str | bytes
|
Value to convert. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
The byte-string representation of |
Source code in WebATM/bluesky_client.py
seqid2idx ¶
Convert a sequence ID byte to a sequence index.
Adapted from bluesky.network.common. The index is the byte value offset
by -128, clamped to a minimum of -1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seqid_byte
|
int | str
|
Sequence ID byte, as an integer value or a one-character string. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The sequence index (at least -1). |
Source code in WebATM/bluesky_client.py
seqidx2id ¶
Convert a sequence index to a sequence ID byte.
Adapted from bluesky.network.common. Inverse of seqid2idx: the byte
value is the index offset by +128.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seqidx
|
int
|
Sequence index to encode. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
A single charmap-encoded byte representing the index. |
Source code in WebATM/bluesky_client.py
safe_decode ¶
Decode bytes to a readable string without raising.
Attempts UTF-8 decoding first and returns the result only if it consists
entirely of printable ASCII characters; otherwise falls back to ASCII
decoding, and finally to an uppercase hexadecimal representation. Non-bytes
input is converted with str().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes | object
|
Value to decode or stringify. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A printable string representation of |