ba9015fac8
- HeidenhainControlFamily + HeidenhainControlFamilies.Classify (Contracts): one authority, ported from pyLSV2 (MIT) dat_cls.py VersionInfo.control (strip spaces, case-insensitive prefix, ITNC530 first). Families: ITnc530, Tnc (640/620/320/128), Tnc7, Lathe (CNC PILOT 640 / MANUALplus 620), MillPlus, LegacyTnc (426/430, not a pyLSV2 class), Unknown. - New additive read-only leaf Controller/Family (String, Identity feature, derived from the same R_VR reply: no extra request). Lsv2Versions.Family is computed, so record equality and the #831 identity-change rediscovery signal are unchanged. Probe message + /hosts diagnostics (<host>.family). - DATA gate now goes through SupportsDataPath(family) (pyLSV2 client.py read_data_path is iTNC-only): non-iTNC and Unknown => Capabilities/DataLogin Unsupported, DATA login never sent. Replaces the "model contains itnc" substring check (same result on the real iTNC530 controls). - No per-family selector pre-gates: pyLSV2's TNC 640 observations (27/28/51/30-34 unknown telegram, 25/26 need DNC) are already handled by the reply-driven refusal/suppression path; tests pin it. - Lsv2ReadOnlyPolicy, HeidenhainTagWritable and the no-IWritable rule are untouched. - Simulator: per-family ControlProfiles (strings from the pyLSV2 README tables / protocol.rst), both R_VR field counts, 124-byte R_PR; HeidenhainControlFamilyTests (classification, gates, decoding, wire log inside the read-only set, refused selectors asked once). - docs/drivers/Heidenhain.md: Control families matrix (iTNC 530 hardware-proven; the rest per pyLSV2, hardware-unproven) + open questions (C_CC buffer size, encrypted passwords, R_RI 20/21). Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk