client: update channel block layout documentation and improve validation checks for 'Tran' label
This commit is contained in:
@@ -609,6 +609,15 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
|
||||
for "faster" mode, shifting all subsequent offsets by 1. The 10-byte
|
||||
anchor is stable across all modes.
|
||||
|
||||
Channel block layout (✅ confirmed 2026-04-02 from 3-11-26 E5 frame 78
|
||||
and 1-2-26 A5 frame 77):
|
||||
"Tran" label at tran_pos
|
||||
tran_pos + 28 = max_range float32_BE (e.g. 6.206053 in/s)
|
||||
tran_pos + 34 = trigger_level float32_BE (e.g. 0.600000 in/s)
|
||||
tran_pos + 38 = "in.\\x00" (unit string anchor)
|
||||
tran_pos + 42 = alarm_level float32_BE (e.g. 1.250000 in/s)
|
||||
tran_pos + 46 = "/s\\x00\\x00" (unit string anchor)
|
||||
|
||||
Modifies info.compliance_config in-place.
|
||||
"""
|
||||
if not data or len(data) < 40:
|
||||
@@ -693,48 +702,45 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
|
||||
|
||||
# ── Channel block: trigger_level_geo, alarm_level_geo, max_range_geo ─────
|
||||
# The channel block is only present in the full cfg (frame D delivered,
|
||||
# ~2126 bytes). Per §7.6, each channel record ends with the label string:
|
||||
# [00 00][max_range f32][00 00][trigger f32]["in.\0"][alarm f32]["/s\0\0"][00 01][label]
|
||||
# Relative offsets from the "Tran" label position:
|
||||
# trigger = float32_BE at label - 18
|
||||
# alarm = float32_BE at label - 10
|
||||
# max_range = float32_BE at label - 24
|
||||
# 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.
|
||||
# ~2126 bytes). Layout confirmed 2026-04-02 from both E5 frame 78 of the
|
||||
# 3-11-26 compliance-config capture and A5 frame 77 of the 1-2-26 event
|
||||
# download capture:
|
||||
#
|
||||
# "Tran" label at tran_pos (+0 to +3)
|
||||
# max_range float32_BE at tran_pos + 28 (e.g. 6.206053 in/s)
|
||||
# trigger float32_BE at tran_pos + 34 (e.g. 0.600000 in/s)
|
||||
# "in.\x00" unit string at tran_pos + 38 ✅ confirmed
|
||||
# alarm float32_BE at tran_pos + 42 (e.g. 1.250000 in/s)
|
||||
# "/s\x00\x00" unit string at tran_pos + 46 ✅ confirmed
|
||||
#
|
||||
# Unit strings serve as layout anchors — if they match, the float offsets
|
||||
# are reliable. Skip "Tran2" (a later repeated label) via the +4 check.
|
||||
try:
|
||||
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(
|
||||
"compliance_config: bytes around 'Tran'@%d: %s",
|
||||
tran_pos, pre.hex(),
|
||||
)
|
||||
if (
|
||||
tran_pos >= 24
|
||||
tran_pos >= 0
|
||||
and data[tran_pos + 4 : tran_pos + 5] != b"2" # not "Tran2"
|
||||
and data[tran_pos - 14 : tran_pos - 10] == b"in.\x00"
|
||||
and data[tran_pos - 6 : tran_pos - 2 ] == b"/s\x00\x00"
|
||||
and tran_pos + 50 <= len(data)
|
||||
and data[tran_pos + 38 : tran_pos + 42] == b"in.\x00"
|
||||
and data[tran_pos + 46 : tran_pos + 50] == b"/s\x00\x00"
|
||||
):
|
||||
config.trigger_level_geo = struct.unpack_from(">f", data, tran_pos - 18)[0]
|
||||
config.alarm_level_geo = struct.unpack_from(">f", data, tran_pos - 10)[0]
|
||||
config.max_range_geo = struct.unpack_from(">f", data, tran_pos - 24)[0]
|
||||
log.warning(
|
||||
config.max_range_geo = struct.unpack_from(">f", data, tran_pos + 28)[0]
|
||||
config.trigger_level_geo = struct.unpack_from(">f", data, tran_pos + 34)[0]
|
||||
config.alarm_level_geo = struct.unpack_from(">f", data, tran_pos + 42)[0]
|
||||
log.debug(
|
||||
"compliance_config: trigger=%.4f alarm=%.4f max_range=%.4f in/s",
|
||||
config.trigger_level_geo, config.alarm_level_geo, config.max_range_geo,
|
||||
)
|
||||
elif tran_pos >= 24:
|
||||
elif tran_pos >= 0:
|
||||
log.warning(
|
||||
"compliance_config: 'Tran' at %d — unit string check failed: "
|
||||
"label-14..label-10=%s (want 696e2e00) label-6..label-2=%s (want 2f730000)",
|
||||
"+38..+42=%s (want 696e2e00) +46..+50=%s (want 2f730000)",
|
||||
tran_pos,
|
||||
data[tran_pos - 14 : tran_pos - 10].hex(),
|
||||
data[tran_pos - 6 : tran_pos - 2 ].hex(),
|
||||
data[tran_pos + 38 : tran_pos + 42].hex() if tran_pos + 42 <= len(data) else "??",
|
||||
data[tran_pos + 46 : tran_pos + 50].hex() if tran_pos + 50 <= len(data) else "??",
|
||||
)
|
||||
elif tran_pos >= 0:
|
||||
log.warning("compliance_config: 'Tran' at %d but too close to start (< 24)", tran_pos)
|
||||
else:
|
||||
log.warning("compliance_config: channel block not present in cfg (len=%d)", len(data))
|
||||
log.debug("compliance_config: channel block not present in cfg (len=%d)", len(data))
|
||||
except Exception as exc:
|
||||
log.warning("compliance_config: channel block extraction failed: %s", exc)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user