[Python] Add async components for WebBridgeController#13
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces an experimental asyncio-based bridge for the Constellation Python core, providing async equivalents of existing threaded ZMQ subscriber pooling and heartbeat monitoring, plus a prototype “combined bridge” that runs heartbeat/CMDP processing in an event loop while keeping CHIRP discovery in a thread.
Changes:
- Adds
AsyncSubscriberPool(async ZMQ SUB socket pool usingzmq.asyncio). - Adds
AsyncHeartbeatReceiver(async heartbeat polling with lives/stale-connection tracking). - Adds
CombinedBridgeprototype wiring CHIRP discovery (thread) + heartbeat + CMDP receive + CSCP command sending.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 13 comments.
| File | Description |
|---|---|
python/constellation/core/async_experimental/bridge.py |
Prototype combined bridge coordinating CHIRP discovery, async heartbeat/CMDP processing, and CSCP command dispatch. |
python/constellation/core/async_experimental/async_pools.py |
Async subscriber pool abstraction for CMDP-style PUB/SUB sockets. |
python/constellation/core/async_experimental/async_heartbeat.py |
Async heartbeat receiver that tracks satellite state and liveness via CHP messages. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@stephanlachnit Ready for your feedback. Copilot flagged some cleanup items (SPDX headers, unused imports, DEPART handling) I'll address those based on your direction on the overall approach. |
|
Woooow, sorry for the Copilot noise, this is incredibly annoying. I (think I) have switched it off now for the repo. |
|
I think this is quite promising - it looks like the code changes required to switch to async are not too big. If I get it right, we use |
|
@stephanlachnit Yes, that's exactly right. CHIRP stays as the one thread since it uses raw UDP multicast rather than ZeroMQ so One thing I wanted to check is whether you'd prefer we integrate with the existing |
|
@stephanlachnit |
Nice, this is how I imagined it! |
f72cead to
7c5d0a9
Compare
|
@stephanlachnit Implemented async CHIRP using |
There was a problem hiding this comment.
I've taken a look the async CHIRP code, and I think we need to restructure it a bit - it works well for discovering services at a single point and lays out the async part, but for a general purpose CHIRP class the API needs to be adjusted.
This is what I roughly have in mind:
- The socket should stay in a separate class like the
MulticastSocketright now - The socket should be owned and managed by a
CHIRPListenerclass, which takes care of the message decoding and calling callbacks - The
CHIRPListenerclass should have three public methods:register_callback(callback_id, callback_func)to register callbacksunregister_callback(callback_id)to remove callbackssend_chirp_message(message)to send a CHIRP message via the socket
- Then
CHIRPManagerclass (inheriting fromCHIRPListener) then takes care of responding to request - For this, a
request_callbackproperty in theCHIRPListeneris needed so that theCHIRPManagercan handle request - The
CHIRPManagershould have the following public methods:request(service_id)to request servicesregister_service(service_id, port)to register a serviceunregister_service(service_id)to unregister a serviceemit_offers()to emit offers of registered services
- In the service callbacks should take two arguments: an event type (enum for
SERVICE_CONNECTEDandSERVICE_DISCONNECTED) and a discovered service (containing the group ID, host ID, service ID, port and IP address).
If you think I missed something let me know!
9717e47 to
eec4510
Compare
|
@stephanlachnit Restructured async CHIRP per your feedback. Also applied the MR !1131 casing fixes to Results after both changes. 7 state changes (all transitional states now caught, up from 4), 7 logs, 6 metrics, 3 commands, 0 dedicated threads. Note: These changes are giving significantly better results than before (initially the transitional states weren't getting registered but now they are). I've attached a screenshot of the output.
|
One thing I added that wasn't in your spec, an async |
|
Overall quite nice, looks very promising! Just needs a few more checks from the original CHIRP code and then I think we can move on.
Ah right I see the problem, it's not so easy to separate. Maybe we can try the following:
This should separate the network communication from the CHIRP logic more cleanly |
|
@stephanlachnit Addressed all the review comments. Nothing seems to be broken, the output is being displayed the same as before. |
|
Very nice, this looks excellent now! For the bridge this should be sufficient, but I think we have two approaches from here:
Let's discuss this tomorrow. |
46adb18 to
bdb72b5
Compare
149e790 to
56e7958
Compare
|
@stephanlachnit Added async framework integration classes to Each registers its coroutine via MRO verified: |
b44f0bf to
6110357
Compare
|
@stephanlachnit While setting up WebControl as a separate package, importing I think that the editable install ( |
You need to disable build isolation (this is also noted in https://constellation.pages.desy.de/application_development/intro/install_from_source.html#installing-the-constellation-package): |
e49957d to
e0b7a47
Compare
…ibe semantics and CSCP thread safety
e0b7a47 to
1cdc041
Compare

Async equivalents of the threading components, explored as part of Issue #335.
AsyncSubscriberPool is an async equivalent of SubscriberPool using zmq.asyncio. AsyncHeartbeatReceiver is an async equivalent of HeartbeatChecker with full lives and stale connection logic. CombinedBridge is a prototype combining CHIRP discovery in a thread with heartbeat tracking, CMDP receiving, and CSCP commands all running in the asyncio event loop.
Tested against PyRandomTransmitter through a full FSM cycle. The bridge detected 4 state transitions, received 7 log messages, and sent 3 commands successfully. The architecture reduces threading from 5 threads plus task queue to 1 thread for CHIRP plus asyncio.
To run, start PyRandomTransmitter in one terminal with
python -m constellation.satellites.PyRandomTransmitter -g test -n Sat1and run the bridge in another withpython -m python.constellation.core.async_experimental.bridge.Related: Issue #335