0.23.1
CI / lint (push) Successful in 36s
CI / test (3.12) (push) Successful in 1m2s
Docs build and publish / docker (push) Successful in 5s
CI / test (3.14) (push) Successful in 1m4s
CI / test (3.13) (push) Successful in 1m8s
CI / test-with-beamline-plugins (pxi_bec) (push) Successful in 1m9s
Build and Publish / release (push) Successful in 8s
CI / test-with-beamline-plugins (pxii_bec) (push) Successful in 1m16s
CI / test-with-beamline-plugins (pxiii_bec) (push) Successful in 1m25s
CI / test-with-coverage (push) Successful in 1m30s
CI / coverage-analysis (push) Skipped
CI / lint (push) Successful in 36s
CI / test (3.12) (push) Successful in 1m2s
Docs build and publish / docker (push) Successful in 5s
CI / test (3.14) (push) Successful in 1m4s
CI / test (3.13) (push) Successful in 1m8s
CI / test-with-beamline-plugins (pxi_bec) (push) Successful in 1m9s
Build and Publish / release (push) Successful in 8s
CI / test-with-beamline-plugins (pxii_bec) (push) Successful in 1m16s
CI / test-with-beamline-plugins (pxiii_bec) (push) Successful in 1m25s
CI / test-with-coverage (push) Successful in 1m30s
CI / coverage-analysis (push) Skipped
Automatically generated by python-semantic-release
This commit is contained in:
+148
@@ -1,6 +1,154 @@
|
||||
# CHANGELOG
|
||||
|
||||
|
||||
## v0.23.1 (2026-09-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Bound the sample camera SUB receive queue
|
||||
([`4c3b0ce`](https://gitea.psi.ch/mx/AareDAQ/commit/4c3b0ce64d59fa401913158a15d6a2c33232d56d))
|
||||
|
||||
The socket used library defaults, so a GUI that stalls builds its own private FIFO of up to 1000
|
||||
messages — 20 s of video at 50 Hz — plus whatever the autotuned kernel receive buffer holds on
|
||||
top. PUB/SUB pipes are per-subscriber, which is why only aareGUI lagged while the other receivers
|
||||
stayed current.
|
||||
|
||||
RCVHWM=4 makes the producer drop frames for this subscriber once it is muted, instead of queueing
|
||||
them, and a fixed RCVBUF stops the kernel hiding a further few dozen frames behind the HWM.
|
||||
Measured against a flooding publisher, the buffered backlog drops from 1000 frames to 4.
|
||||
|
||||
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||||
|
||||
Claude-Session: https://claude.ai/code/session_01DVqbXPoeHyqrQq5Vc8EcYP
|
||||
|
||||
- Build the sample camera QPixmap on the GUI thread
|
||||
([`c842856`](https://gitea.psi.ch/mx/AareDAQ/commit/c842856fe0482e93374c561f3fb83de7d39e59fe))
|
||||
|
||||
QPixmap is a GUI-thread-only class in Qt; the worker was constructing one per frame via
|
||||
QPixmap.fromImage. It happens to work with the raster backend, which is why this has not bitten
|
||||
us, but it is not supported and the guarantee is not ours to rely on.
|
||||
|
||||
The subscriber now emits QImage — which is explicitly safe to build and move between threads — and
|
||||
_on_sample_camera_frame converts once on the GUI thread before handing the pixmap to the visible
|
||||
view. No extra copy: the .copy() that detaches the QImage from the numpy buffer was already there.
|
||||
|
||||
Verified end to end against a real PUB socket: the payload delivered is QImage, the slot runs only
|
||||
on the main thread, and frames still decode.
|
||||
|
||||
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||||
|
||||
Claude-Session: https://claude.ai/code/session_01DVqbXPoeHyqrQq5Vc8EcYP
|
||||
|
||||
- Drain the sample camera SUB socket to the newest frame
|
||||
([`d63b4ad`](https://gitea.psi.ch/mx/AareDAQ/commit/d63b4ad1799c5c97eb6992eebeb56b77b23f7253))
|
||||
|
||||
The SUB socket is strictly FIFO, so once the GUI falls behind at 50 Hz it stays behind: every recv
|
||||
returns the oldest queued frame and the lag never recovers on its own. zmq.CONFLATE cannot fix
|
||||
this because it keeps only the last *part* of a message, which shreds the multipart header+payload
|
||||
frames the producer sends.
|
||||
|
||||
recv_multipart is atomic, though — a non-blocking recv either yields a whole message or raises Again
|
||||
— so draining in a loop is a multipart-safe conflate. The drain runs after the previous frame's
|
||||
decode, so it discards exactly the backlog that built up while we were busy, and the socket is
|
||||
consumed at line rate regardless of how long a frame takes to decode.
|
||||
|
||||
Verified against a real PUB/SUB pair: 100 queued multipart messages collapse to the newest one with
|
||||
both parts intact, and RCVTIMEO is still honoured when the queue is empty.
|
||||
|
||||
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||||
|
||||
Claude-Session: https://claude.ai/code/session_01DVqbXPoeHyqrQq5Vc8EcYP
|
||||
|
||||
- Pace sample camera frames to what the GUI can paint
|
||||
([`a4a8b80`](https://gitea.psi.ch/mx/AareDAQ/commit/a4a8b8029abdb14d1aafd380486562216af369e4))
|
||||
|
||||
image is a queued cross-thread signal and Qt's event queue has no high-water mark, so this queue
|
||||
grew without bound independently of ZMQ: with a 50 Hz feed and a GUI painting slower, the
|
||||
displayed frame fell further behind for as long as the stream ran. Measured on a stand-in at 15 ms
|
||||
decode / 25 ms paint, the displayed frame aged from 40 ms to 2010 ms over three seconds and kept
|
||||
going.
|
||||
|
||||
Two changes:
|
||||
|
||||
- Emit once and fan out on the GUI thread to whichever view is actually on screen, rather than
|
||||
connecting all three SampleCameraImageLabels. Two of the three are always hidden (tab / compact
|
||||
page / portrait page), so this is one repaint per frame instead of three.
|
||||
|
||||
- Cap the frames in flight and drop rather than queue past it. The cap is 2, not 1, so a frame can
|
||||
be queued while the GUI paints the other: at 1 the GUI idles waiting for the next decode (33.3 fps
|
||||
displayed), at 2 it does not (40.0 fps) for one extra frame of latency. Both stay bounded — 40 ms
|
||||
and 62 ms respectively, flat over the run.
|
||||
|
||||
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||||
|
||||
Claude-Session: https://claude.ai/code/session_01DVqbXPoeHyqrQq5Vc8EcYP
|
||||
|
||||
- Satisfy basedpyright on the Optional subscriber and socket
|
||||
([`c0b0994`](https://gitea.psi.ch/mx/AareDAQ/commit/c0b0994feddc8a51d49fef66f601a183d16b7f4d))
|
||||
|
||||
CI lint failed on reportOptionalMemberAccess: prediction_thread is None when the GUI runs without a
|
||||
sample feed, and _sock is nulled by run()'s cleanup, so both attributes are Optional to the
|
||||
checker even though the flagged call sites cannot see None at runtime. Guard the slot and bind the
|
||||
socket to a local asserted non-None in _recv_latest.
|
||||
|
||||
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||||
|
||||
### Code Style
|
||||
|
||||
- One PSI red for every busy camera badge, blue only for robot cooling
|
||||
([`7c1efe4`](https://gitea.psi.ch/mx/AareDAQ/commit/7c1efe43847d5e0216b8889b84f82eb39a77551a))
|
||||
|
||||
AUTO CENTERING, ROBOT MOUNTING/UNMOUNTING/DRYING and BEAMLINE BUSY each had their own accent
|
||||
(purple, red, orange, yellow, maroon), so the sample camera read as a per-activity rainbow instead
|
||||
of one "hands off" signal. All busy states now share BUSY_PSI_RED; ROBOT COOLING keeps blue
|
||||
because it is the long, harmless phase the operator should read as "wait".
|
||||
|
||||
The five near-identical BusyOverlayStyle blocks collapse into one _animated() helper plus a text
|
||||
lookup, and the now-unused purple/orange/ red-badge constants are dropped from styles.py.
|
||||
|
||||
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||||
|
||||
- Paint the unknown-session badge with the viewing-mode style
|
||||
([`dbd5b90`](https://gitea.psi.ch/mx/AareDAQ/commit/dbd5b9078f6223ff81bc5af1448fa952a09ea141))
|
||||
|
||||
The sample camera drew its own gold/orange/red "Session Vacant / Baton Requested / Guest Mode" box
|
||||
whenever the busy overlay had nothing to show. Since the busy-style builder already receives the
|
||||
session state, that box only ever appeared while the session was still unknown (no status yet, or
|
||||
one without a session), so it flashed a different color and shape than the yellow "Viewing mode,
|
||||
click here to grab the baton" badge every known not-owned state gets.
|
||||
|
||||
Hoist that badge into VIEWING_MODE_STYLE and paint it from both paths; the hover fill is shared
|
||||
through _badge_fill. The three MARK_TOOLTIP constants had no other users and are dropped.
|
||||
|
||||
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||||
|
||||
### Testing
|
||||
|
||||
- Cover the sample camera pacing, the newest-frame drain and the frame slot
|
||||
([`c3e7b9a`](https://gitea.psi.ch/mx/AareDAQ/commit/c3e7b9a2cc2670e6a4cf83e5e00f6aabc0a5918b))
|
||||
|
||||
CI's diff-coverage gate sat at 29% because the ZMQ fixes live in code no test reached: the
|
||||
subscriber's socket setup, _emit_image/notify_frame_ displayed, _recv_latest and the run() frame
|
||||
path, plus the main window's frame slot. The subscriber now runs against a real inproc socket (the
|
||||
drain and the in-flight cap are the fix, so a mock would prove nothing); run() is driven with a
|
||||
stubbed _recv_latest that hands over one JPEG and then stops. The busy overlay gets a check that
|
||||
cooling is the only blue busy state.
|
||||
|
||||
The subscriber fixture takes qapp so no QImage is built before the QApplication exists.
|
||||
|
||||
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||||
|
||||
- Format the mocked token and cast the tell-state stand-in for basedpyright
|
||||
([`ccb71bf`](https://gitea.psi.ch/mx/AareDAQ/commit/ccb71bf43fb511eed4bde5d594a032075cc48734))
|
||||
|
||||
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||||
|
||||
- Route the tell-state stand-in through object for basedpyright
|
||||
([`a56457d`](https://gitea.psi.ch/mx/AareDAQ/commit/a56457d03bb6fe7f4f1e3bef0fec64703f46a226))
|
||||
|
||||
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||||
|
||||
|
||||
## v0.23.0 (2026-09-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "aaredaq"
|
||||
version = "0.23.0"
|
||||
version = "0.23.1"
|
||||
description = "AareDAQ (with GUI)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
Reference in New Issue
Block a user