Fix firmware version display in Sonication Control page#562
Conversation
b1bf44b to
3c3fc80
Compare
There was a problem hiding this comment.
Pull request overview
Adds firmware/SDK version display to the Sonication Control module and improves thread-safety around hardware monitor events/logging, aligning with #482’s request to surface connected device version information in the UI.
Changes:
- Add three new UI labels to display SDK, console FW, and TX module FW versions.
- Populate/clear those labels on device connect/disconnect via a new
updateVersionLabels()helper. - Refactor hardware monitor callbacks to cross the monitor-thread → main-thread boundary via Qt signals (and prevent Slicer UI logging from background threads).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
OpenLIFUSonicationControl/Resources/UI/OpenLIFUSonicationControl.ui |
Adds SDK/console/TX version QLabels in the connection status frame. |
OpenLIFUSonicationControl/OpenLIFUSonicationControl.py |
Implements version label updates and adjusts device-connect plumbing (Qt signals, reinit logic, logging). |
OpenLIFULib/OpenLIFULib/util.py |
Guards Slicer UI log handler against non-main-thread emission to avoid Qt cross-thread crashes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def updateVersionLabels(self): | ||
| """Populate SDK / console / TX firmware version labels when both devices are connected.""" | ||
| if self._cur_device_connected_state == DeviceConnectedState.CONNECTED: |
There was a problem hiding this comment.
Version labels are only refreshed in onDeviceConnected/onDeviceDisconnected. In setup() you already call updateDeviceConnectedStateFromDevice(), but you never call updateVersionLabels(), so opening the module while the device is already connected will leave these labels blank until a reconnect event happens. Consider calling updateVersionLabels() after the initial connection-state sync (and after reinitialize/enter as well).
| self.monitoring_timer.stop() | ||
| self.stop_monitoring() | ||
| self.cur_lifu_interface.close() | ||
| except Exception as e: | ||
| logging.warning("[LIFU] Error during interface cleanup: %s", e) | ||
|
|
There was a problem hiding this comment.
reinitialize_lifu_interface() replaces _monitor_loop/_monitor_thread without joining the prior monitor thread or closing the prior event loop. If reinitialize is triggered repeatedly, this can leak resources and may leave old monitor threads running briefly against stale state. Consider waiting for the old thread to exit (join with timeout) and explicitly closing the old event loop once stopped.
| def emit(self, record): | ||
| # Qt UI calls are only safe on the main thread. If this log record was emitted | ||
| # from a background thread (e.g. UART monitor/read threads), skip Qt interactions | ||
| # entirely to avoid QObject::setParent and processEvents cross-thread crashes. | ||
| if qt.QThread.currentThread() is not slicer.app.thread(): | ||
| return |
There was a problem hiding this comment.
This early return drops all Slicer UI surfacing for log records emitted off the main thread (including warnings/errors). If this handler is the primary feedback path for these loggers, important messages may be silently lost. Consider queuing the UI work onto the main thread (e.g., via a singleShot/invokeMethod) or at least falling back to non-Qt output when off-thread, instead of returning.
There was a problem hiding this comment.
maybe at least log something so that we know it is dropping
There was a problem hiding this comment.
this needs to be fixed i had it hardcoded to my path for testing
220ff5d to
303db1b
Compare
| def emit(self, record): | ||
| # Qt UI calls are only safe on the main thread. If this log record was emitted | ||
| # from a background thread (e.g. UART monitor/read threads), skip Qt interactions | ||
| # entirely to avoid QObject::setParent and processEvents cross-thread crashes. | ||
| if qt.QThread.currentThread() is not slicer.app.thread(): | ||
| return |
There was a problem hiding this comment.
maybe at least log something so that we know it is dropping
303db1b to
35f03a5
Compare
ebrahimebrahim
left a comment
There was a problem hiding this comment.
This looks good! Just a couple of questions and we should be good to go
@alkagan Can you confirm that this works with openlifu v0.18.0 specifically? Or were you using some other test version of openlifu that fixed the crash? Just checking whether we need to update the python requirements within this PR.
minor: updateVersionLabels was not being called in setup and so it's not shown initially if there's nothing to trigger it to be called (e.g. if there's no device connected at all). I just added this myself.
Yes, openlifu will need to be updated because we also need to make a PR for the feature/bootloader-slave-enablement branch in order for this to work properly. Thanks for the addition! Edit: so with this change we expect |
I suppose it doesn't. |
42ea1bb to
349ae13
Compare
This closes #482 and replaces/closes #552