2 Commits

Author SHA1 Message Date
claude 501b5080e9 server: backfill event.project_info fields from compliance config
The 210-byte waveform record only stores "Project:" — client, operator,
sensor_location, and notes are device-level settings in SUB 1A, not
per-event fields. Backfill those into each event's project_info after
download, same pattern as the sample_rate backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:33:33 -04:00
claude 5948c833bd client: lower Tran search floor from 1000 to 44 in channel block extraction
The channel block is in frame C data (cfg[44:1071]) not deep in a
hypothetical frame D section.  The offset-1000 assumption was wrong —
searching from 44 lets us find it while unit string validation still
prevents false positives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:22:00 -04:00
2 changed files with 16 additions and 2 deletions
+2 -2
View File
@@ -702,8 +702,8 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
# Validated by checking unit strings "in.\0" at label-14 and "/s\0\0" at label-6.
# "Tran2" at a later position won't match because its surrounding bytes differ.
try:
tran_pos = data.find(b"Tran", 1000)
log.warning("compliance_config: 'Tran' search from 1000 → pos=%d cfg_len=%d", tran_pos, len(data))
tran_pos = data.find(b"Tran", 44)
log.warning("compliance_config: 'Tran' search from 44 → pos=%d cfg_len=%d", tran_pos, len(data))
if tran_pos >= 0:
pre = data[max(0, tran_pos - 30) : tran_pos + 8]
log.warning(
+14
View File
@@ -323,6 +323,20 @@ def device_events(
if ev.sample_rate is None:
ev.sample_rate = info.compliance_config.sample_rate
# Backfill event.project_info fields that the 210-byte waveform record doesn't carry.
# The waveform record only stores "Project:" — client/operator/sensor_location/notes
# live in the SUB 1A compliance config, not in the per-event record.
if info.compliance_config:
cc = info.compliance_config
for ev in events:
if ev.project_info is None:
ev.project_info = ProjectInfo()
pi = ev.project_info
if pi.client is None: pi.client = cc.client
if pi.operator is None: pi.operator = cc.operator
if pi.sensor_location is None: pi.sensor_location = cc.sensor_location
if pi.notes is None: pi.notes = cc.notes
return {
"device": _serialise_device_info(info),
"event_count": len(events),