add UBX-MON-VER support and surface device identification in CLI/GUI
Add the Ver message parser and a fixed UbxQuery (was unused and had a broken semaphore/state bug) to poll MON-VER once on connect. The CLI prints hw/sw version and extensions; the GUI polls it per receiver, pre-fills the read-only ID field with a connection counter and rewrites it from the MOD= extension once the device answers, and propagates the resolved display name to the Sky and Plot tabs so they no longer show the stale placeholder ID. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XM4FNUjhu9x8df5Dp1hHvd
This commit is contained in:
+46
-1
@@ -1,4 +1,4 @@
|
||||
import sys, os, json, dataclasses, time
|
||||
import sys, os, json, dataclasses, time, threading
|
||||
from dataclasses import dataclass
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSignal
|
||||
|
||||
@@ -13,6 +13,8 @@ from nmea.packetizer import NmeaPacketizer
|
||||
from ubx.packetizer import UbxPacketizer
|
||||
from backend.network_backend import NetworkBackend
|
||||
from ubx.ubx import frame_create
|
||||
from ubx.query import UbxQuery
|
||||
from ubx.messages import Ver
|
||||
|
||||
GNSS_NAMES = {
|
||||
0: 'GPS', 1: 'SBAS', 2: 'Galileo', 3: 'BeiDou',
|
||||
@@ -35,6 +37,14 @@ LRU_PATH = os.path.expanduser('~/.nmea_client_gui.json')
|
||||
LRU_MAX = 20
|
||||
|
||||
|
||||
def _device_id_from_ver(ver: dict) -> str:
|
||||
for ext in ver.get('extensions', []):
|
||||
s = ext.get('extension', '')
|
||||
if s.startswith('MOD='):
|
||||
return s[len('MOD='):]
|
||||
return ver.get('hw_version', '')
|
||||
|
||||
|
||||
@dataclass
|
||||
class SatelliteData:
|
||||
gnss_id: int
|
||||
@@ -85,12 +95,15 @@ class _ReceiverState:
|
||||
self.backend = None
|
||||
self.transceiver = None
|
||||
self.poll_timer = None
|
||||
self.device_id: str = ''
|
||||
self.sw_version: str = ''
|
||||
|
||||
|
||||
class ReceiverManager(QObject):
|
||||
satellite_update = pyqtSignal(str, list) # rid, [SatelliteData]
|
||||
pdop_update = pyqtSignal(str, float, float) # rid, pdop, timestamp
|
||||
connection_changed = pyqtSignal(str, bool) # rid, connected
|
||||
version_update = pyqtSignal(str, str, str) # rid, device_id, sw_version
|
||||
receiver_added = pyqtSignal(str)
|
||||
receiver_removed = pyqtSignal(str)
|
||||
_connection_lost = pyqtSignal(str) # internal: backend thread -> GUI thread
|
||||
@@ -101,6 +114,7 @@ class ReceiverManager(QObject):
|
||||
self._configs: dict = {}
|
||||
self._lru: list = self._load_lru()
|
||||
self._connection_lost.connect(self._handle_connection_lost)
|
||||
self.version_update.connect(self._on_version)
|
||||
|
||||
# ── LRU ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -197,6 +211,11 @@ class ReceiverManager(QObject):
|
||||
GsaQtListener(rid, lambda p, t, r=rid: self._on_pdop(r, p, t))
|
||||
)
|
||||
|
||||
ver_query = None
|
||||
if config.conn_type != 'file':
|
||||
ver_query = UbxQuery(xcvr, Ver())
|
||||
ubx_sink.register_listener(ver_query)
|
||||
|
||||
backend.connect()
|
||||
backend.start()
|
||||
|
||||
@@ -211,9 +230,18 @@ class ReceiverManager(QObject):
|
||||
timer.start()
|
||||
state.poll_timer = timer
|
||||
|
||||
threading.Thread(target=self._poll_version, args=(rid, ver_query),
|
||||
daemon=True).start()
|
||||
|
||||
self._push_lru(config)
|
||||
self.connection_changed.emit(rid, True)
|
||||
|
||||
def _poll_version(self, rid: str, query: UbxQuery):
|
||||
ver = query.poll()
|
||||
if ver is None:
|
||||
return
|
||||
self.version_update.emit(rid, _device_id_from_ver(ver), ver['sw_version'])
|
||||
|
||||
def _handle_connection_lost(self, rid: str):
|
||||
state = self._states.get(rid)
|
||||
if not state or not state.connected:
|
||||
@@ -237,7 +265,10 @@ class ReceiverManager(QObject):
|
||||
state.backend = None
|
||||
state.transceiver = None
|
||||
state.satellites.clear()
|
||||
state.device_id = ''
|
||||
state.sw_version = ''
|
||||
self.connection_changed.emit(rid, False)
|
||||
self.version_update.emit(rid, '', '')
|
||||
|
||||
def _poll_ubx(self, rid: str):
|
||||
state = self._states.get(rid)
|
||||
@@ -263,6 +294,14 @@ class ReceiverManager(QObject):
|
||||
def get_config(self, rid: str):
|
||||
return self._configs.get(rid)
|
||||
|
||||
def get_version_info(self, rid: str):
|
||||
s = self._states.get(rid)
|
||||
return (s.device_id, s.sw_version) if s else ('', '')
|
||||
|
||||
def display_name(self, rid: str) -> str:
|
||||
s = self._states.get(rid)
|
||||
return (s.device_id if s and s.device_id else rid)
|
||||
|
||||
def get_satellites(self, rids: list) -> list:
|
||||
result = []
|
||||
for rid in rids:
|
||||
@@ -288,3 +327,9 @@ class ReceiverManager(QObject):
|
||||
return
|
||||
state.pdop = pdop
|
||||
self.pdop_update.emit(rid, pdop, ts)
|
||||
|
||||
def _on_version(self, rid: str, device_id: str, sw_version: str):
|
||||
state = self._states.get(rid)
|
||||
if state:
|
||||
state.device_id = device_id
|
||||
state.sw_version = sw_version
|
||||
|
||||
Reference in New Issue
Block a user