docs: Reorganize changelog, add catalog data, and expand documentation - #31
Merged
Conversation
PACKAGE_INSTALLATION_FIX.md described a one-time dproj reconfiguration
that is now baked into the standard package setup; the README install
steps cover the same ground.
docs/FLICKERING_FIX.md described an FBackBuffer double-buffering pattern
that is now an explicit anti-pattern in COMPONENT_AUTHORING.md \xc2\xa710
("don't reintroduce FBackBuffer: ISkSurface"). Keeping the doc around
actively misleads contributors.
- Bump README roadmap badge from v3.76 to v3.79. - Replace the broken doc tree (which only listed README/TASKS/RADIO_CALCULATORS) with the actual docs/ layout, and the slim Documentation links section with the full topic list. - Add v3.79 features to README (async UDS, cross-platform DoIP, DoIP TLS, capture/replay) and an OEM Coverage section (79 catalogs / 247k entries / 5 vehicle classes). - Note that FMX components and cross-platform DoIP run on macOS / Linux / iOS / Android in addition to Windows; OpenSSL+Indy required only for DoIP TLS. - Replace the QuickStart "What's New in v2.0" block with a v3.79 summary; fix the troubleshooting tip that contradicted COMPONENT_AUTHORING.md (don't set DoubleBuffered:=True on the parent form). - Update examples/README total count (14 -> 23) and drop the "Proposal B" / "v3.1 FMX" / "New in v2.0" tags.
Resolves the broken docs/RADIO_CALCULATORS.md link from README.md (the file was referenced but never created). The new guide covers the IOBDRadioCode contract, TOBDRadioCode helpers, the regional/year/security variant system in OBD.RadioCode.Variants, and the brand coverage list. Adds READMEs for the 15 examples that didn't have one: minimal, simple, serial, bluetooth, wifi, ftdi, advanced, kwp2000, uds, lin, flexray, most, tachograph, ecuflashing, diagsession_console. Each describes the .dpr to open, the protocol/feature exercised, and links back to the master examples/README.md catalog.
TASKS.md (last updated December 2024) overlapped with ROADMAP.md and GAPS.md. Replace it with a thin redirect plus a doc-ownership table so contributors know which doc to update for what: - ROADMAP.md: shipped milestones + future backlog (canonical). - GAPS.md: current blockers and recently-resolved gaps. - PROPOSALS.md: RFCs awaiting acceptance. - OEM_EXTENSION_PLAN.md: historical (Phases 1-7 shipped in v3.3-v3.13). Add a Status overview table to PROPOSALS.md (A: shipped v3.1, B: partial, C: shipped v3.2 + v3.3-v3.69, D: shipped v3.16+, E: partial, F: open, G: partial, H: shipped v3.79) and per-section status flags on each proposal header. Add a status banner to the top of OEM_EXTENSION_PLAN.md noting the 7-phase plan is fully shipped and that new OEM-coverage work is now tracked in ROADMAP.md, not here.
CHANGELOG.md was 308 KB / 3,240 lines, painful to browse and to diff against. Split into per-major-version files: - CHANGELOG/v3.md (v3.0.0 -> v3.79.0, the bulk of the history) - CHANGELOG/v2.md (v2.1.0 -> v2.5.0) The top-level CHANGELOG.md becomes a thin index with a latest-release summary and links to the per-version files. Add subsystem reference docs for the four src/ folders that lacked discoverable documentation: Adapters (ELM327 / OBDLink / J2534 / detection), Services (OBD-II 01-0A + OEM extension framework + ECU flashing), Forms (TOBDForm base class), Wizards (the four IDE wizards). Add docs/index.md as a navigation hub grouping every doc by topic (Getting started / Architecture & internals / Subsystem reference / Protocols / OEM / Planning / Release history / Tooling). Linked from the README Documentation section.
Captures the agreed scope and ordering for the next milestone: 3.2 brand expansion, 3.3 variant rationalisation, 4.1-4.6 coding/flashing improvements, 5.1-5.5 protocol extensions, 8.2 EV helpers, 8.3 tachograph DDD signature. Each item lists its deliverables and exit criterion. Items that require real-world reference data note a public source where available and otherwise track the gap in docs/DATA_GAPS.md (created when first needed). Plan is linked from docs/index.md under Planning & process.
Adds OBD.RadioCode.Registry as a process-wide map of brand key -> factory, with thread-safe register/find and a per-brand variant manager. Each entry exposes a DataAvailable flag distinguishing real calculators from data-pending stubs. OBD.RadioCode.Pending registers eight new brands (Pioneer, Kenwood, JVC, Sony, Philips, Grundig, Panasonic, Continental/VDO). Public web research (2026-05-09) confirmed no freely available algorithm or lookup table exists for any of them \xe2\x80\x94 commercial unlock services rely on licensed databases (Philips ships ~14M entries) or EEPROM extraction. Rather than fabricate algorithms, each stub raises EOBDRadioCodeDataMissing on Calculate and Validate returns False with a clear message. docs/DATA_GAPS.md tracks the precise reference data each stub needs to become live; docs/RADIO_CALCULATORS.md gains a brand-registry section and lists the data-pending brands; docs/index.md links DATA_GAPS. Tests.RadioCode.Registry covers registry lookup, case-insensitive find, duplicate suppression, and the pending-brand refusal contract.
OBD.RadioCode.VinResolver wires the registry, the variant manager, and OBD.VIN.Decoder together. ResolveCalculator(Ctx) -> TRadioCodeResolveResult returns the correct calculator + variant for a given VIN, with per-field overrides (model year, region, model hint) for cases where the VIN is unknown / invalid / replaced. Registers VW / Audi / Mercedes / BMW into the brand registry as data-available brands seeded with documented per-generation variants: - VW: Gamma -> Beta -> Alpha -> RCD -> RCD/Composition -> RNS -> Discover - Audi: Concert I-III + Symphony + RNS-E + MMI 2G/3G + MIB - Mercedes: Becker BE/BE-2 -> Audio 50 APS -> COMAND NTG2/2.5/5 -> MBUX - BMW: Business + Professional + DSP + CCC -> CIC -> NBT/EVO -> iDrive 5-8 Variant metadata is sourced from public service-info notes and community archives. The brand-internal calculators continue to hold the actual algorithms; the registry-side variants give the resolver enough information to dispatch without instantiating the calculator. Tests cover brand registration, year-boundary variant selection (2002 -> Gamma/Beta era, 2018 -> RCD-NEW/Discover era), invalid-VIN fallback to overrides, and region-override precedence.
OBD.OEM.Coding.Diff adds TOBDCodingPlan as the canonical pre-write flow on top of the existing coding helpers: Plan := TOBDCodingPlan.Create(Current, Target [, Schema]); if not Plan.IsNoOp then ShowDiffToUser(Plan.AsText); Plan.Apply(Confirmed := UserConfirmed, WriteCallback); Apply raises EOBDCodingDiffError if Confirmed is False, encoding the human-in-the-loop into the type signature so a caller can't silently turn a coding write into a one-shot operation. Optional TOBDCodingSchema annotates bit / byte / uint16 fields with names and descriptions so the diff renders as 'CornerLights [byte 1 bit 3]: 0 -> 1' instead of bare hex offsets. Falls back to byte-level diff when no schema is supplied. Tests cover no-op detection, byte-level diff, schema-driven naming (bit, byte, uint16 kinds), confirm/no-confirm semantics, length mismatch, and the no-op-skips-writer optimisation.
OBD.OEM.Coding.AuditLog is an append-only JSON log with HMAC-SHA256 chained signatures: HMAC = HMAC(Key, Prev || Body), where Prev is the previous record's HMAC (zero bytes for the first). Verify walks the chain and reports the first tamper line; insert / delete / single-byte mutation are all detected. Each record carries timestamp, VIN, ECU, block, before-hex, after-hex, operator, reason. Field order is fixed in source so canonicalisation is deterministic across builds. Restarting against an existing file reads the last HMAC and continues the chain unbroken. Apps will typically supply the HMAC key from TOBDSecureSettings (DPAPI- encrypted on Windows). Rotating keys starts a fresh chain on a new file; old chains remain verifiable with the old key. Tests cover: single-record verify, 5-record chain verify, byte-flip on record 2 -> FirstTamperLine=2, mid-chain deletion -> next-record mismatch, restart-continues-chain across two TOBDCodingAuditLog instances, empty-key rejected at construction.
OBD.ECU.Flashing.Checkpoint is a sidecar module that lets
TOBDECUFlashing recover from power-loss / disconnect mid-flash without
re-writing already-completed blocks.
Public surface:
CP := TOBDFlashCheckpoint.Initialise(SidecarPath, FirmwarePath,
BlockSize, TotalBlocks,
SnapshotPath);
CP.MarkBlockComplete(I); // every block ack
CP.Clear; // on success
R := TOBDFlashCheckpoint.LoadAndVerify(SidecarPath, FirmwarePath);
if R.Resumable then continue from R.NextBlock else start fresh.
The SHA-256 of the firmware is recorded at Initialise; LoadAndVerify
recomputes it and refuses to resume if the firmware has been swapped
out from under us. Updates are written synchronously after every
MarkBlockComplete so a hard crash leaves a coherent file.
Sidecar approach (separate unit) rather than embedding into
TOBDECUFlashing: keeps the known-good unit untouched, lets callers opt
in by holding a TOBDFlashCheckpoint and invoking it from their own
block-ack handler.
Tests cover persist+resume, idempotent re-marking, firmware-swap
rejection, completed-flash-not-resumable, Clear-deletes-sidecar,
out-of-range guard.
Adds four new coding-payload wrappers, each following the existing OBD.OEM.Coding.VW pattern: byte/bit accessors over a fixed-length TBytes, hex round-trip, out-of-range guards. Per-controller bit semantics defer to the per-OEM JSON catalogs (the schema-v2 coding_blocks section already shipped in v3.29). OBD.OEM.Coding.Toyota TOBDToyotaCustomize (Techstream CUW) OBD.OEM.Coding.Honda TOBDHondaOptionByte (HDS option-byte) OBD.OEM.Coding.HMG TOBDHMGVariantCoding (Hyundai/Kia/Genesis GDS) OBD.OEM.Coding.Stellantis TOBDStellantisProxi (FCA wiTECH Proxi) Stellantis Proxi includes a ComputeChecksum placeholder: the wiTECH PROXI workflow is publicly documented (cited: fcaproxitool.com, I-CAR CRN-1291, NHTSA TSB MC-10251789-9999) but the wire-level CRC polynomial is not. ComputeChecksum raises EOBDStellantisProxi until the polynomial is supplied; SetChecksum(Crc, Offset) lets callers write a captured value verbatim. Gap tracked in docs/DATA_GAPS.md. Tests cover hex round-trip, bit-flip persistence, out-of-range guards, the Stellantis placeholder-raises contract, manual SetChecksum, zero-length construction rejection.
OBD.ECU.Signature.PQC adds TOBDPQCSignatureVerifier on top of the existing IFirmwareSignatureVerifier interface, covering NIST FIPS 204 ML-DSA-44/65/87 and FIPS 205 SLH-DSA-SHAKE-128s / SLH-DSA-SHA2-128s by stable algorithm tag. The envelope codec (alg-tag | key-id-len | key-id | sig-len | sig) is fully implemented and round-trip tested. Verify parses + algorithm- checks the envelope, then raises EOBDPQCNotAvailable: no OEM has shipped a signed-PQC ECU as of 2026-05-09, and fail-closed is the correct behaviour while no production wire spec exists. The OpenSSL 3.x EVP binding is the single integration point that turns the scaffolding into a working verifier; the rest of the flashing pipeline already accepts it through IFirmwareSignatureVerifier. Gap tracked in docs/DATA_GAPS.md (4.5 entry) with the OEM spec + multi-platform OpenSSL 3.x linkage as the missing pieces. Tests cover envelope encode/decode round-trip, empty key-id, truncation at sig-len, truncation at signature, too-short envelope, algorithm-mismatch rejection, EOBDPQCNotAvailable on Verify, constructor rejection of unknown algorithm and empty public key, algorithm-name mapping.
OBD.ECU.Flashing.VoltageGate adds TOBDProgrammingVoltageGate with two entry points: R := Gate.Check(VoltageReader [, OEMKey]); // returns result record Gate.RequirePass(VoltageReader [, OEMKey]); // raises on failure Default minimum 12.5 V (ISO 22900-2 informative annex). Per-OEM overrides via SetOEMThreshold lookup case-insensitively, so EVs and other platforms with stricter LV-pack requirements can opt in (e.g. tesla=13.0 V). Reader-side errors raise EOBDProgrammingVoltageUnavailable separately from EOBDProgrammingVoltageTooLow so callers can distinguish 'battery low' from 'adapter dead' in their UI flow. The reader is a TOBDVoltageReader callback so this unit doesn't pull a hard dependency on TOBDAdapter; production callers wire it as 'function: Single begin Result := MyAdapter.GetVoltage; end'. Tests cover default threshold pass/fail, OEM override + case insensitivity, nil-reader graceful failure, reader-raises caught into Reason, RequirePass low-voltage vs unavailable distinction, non-positive-voltage rejection.
OBD.Protocol.DoIP.Discovery covers the ISO 13400-2:2019 UDP-side
payload types that the existing TCP DoIP stack didn't:
0x0001 Vehicle Identification Request (broadcast)
0x0002 Vehicle Identification Request with EID (targeted)
0x0003 Vehicle Identification Request with VIN (targeted)
0x0004 Vehicle Announcement / Identification Response (response)
0x0007 AliveCheck Request
0x0008 AliveCheck Response
(header NACK 0x0000 codes exposed as constants)
Pure codec — frame builders + parsers, no TCP/UDP I/O — so the unit is
fully testable without a network stack. Production code composes
these with the existing OBD.Connection.* UDP path; this layering
matches OBD.Protocol.DoIP.Session.{Cross,TLS} which already separated
TCP framing from socket I/O.
Frame builders enforce the spec-mandated lengths (VIN=17, EID=6).
ParseDoIPHeader validates the protocol-version / inverse-NOT pairing
(0x03 / 0xFC for the 2019 version) and the declared payload-length;
ParseVehicleAnnouncement decodes the 32-byte (2012) or 33-byte (2019)
payload exposing VIN, logical address, EID, GID, further-action-
required, and optional sync status.
Tests cover header inverse, request lengths, VIN/EID length-mismatch
raises, AliveCheck source-address echo, header rejection of bad
inverse + truncation, Vehicle Announcement round-trip, 2012 form
without sync.
OBD.Adapter.Capabilities introduces a process-wide registry mapping adapter-key -> TOBDAdapterCapabilitySet. Capabilities are stable enum values (CAN, CAN-FD, ISO-TP, ISO-TP-LF, DoIP, J1939, K-Line, Voltage, SecOC, J2534, J2534v2, BLE, WiFi, FTDI). Adapters opt in by calling RegisterAdapterCapabilities at unit init. Seeded with the known adapters: elm327 CAN, ISO-TP, K-Line, Voltage (no FD) obdlink_mx ELM327 caps + BLE (no FD) obdlink_ex ELM327 caps + CAN-FD + ISO-TP-LF + FTDI doip_gateway DoIP, ISO-TP-LF, Voltage j2534 CAN, ISO-TP, K-Line, J1939, J2534, Voltage AdapterSupports(Key, Cap) and ResolveIsoTpFrameBytes(Key) give production code a clean feature-gate so a single call site can pick 7-byte vs 62-byte ISO-TP single frames based on the connected adapter. Tests cover ELM327-no-FD, OBDLink-EX-FD-and-large-frame, DoIP-no-K- Line, unknown-adapter-false, ISO-TP fallback to 7, ISO-TP-62 for FD adapters, case-insensitive lookup, set-to-string rendering, register-replaces-existing.
OBD.Protocol.SecOC ships TSecOCContext covering AUTOSAR SecOC SWS
R22-11 profiles 1, 2, and 3.
Profile 3 (HMAC-SHA-256) FULLY IMPLEMENTED via System.Hash.
Profile 1 (CMAC-AES-128, 24-bit FV / 24-bit MAC)
Profile 2 (CMAC-AES-128, 64-bit FV)
Framework only; raise
EOBDSecOCAlgorithmNotAvailable until the
OpenSSL EVP_MAC binding ships (gap
tracked in docs/DATA_GAPS.md).
The freshness-value handling, MAC truncation, PDU envelope, and
constant-time verify path are shared across profiles, so the
profile-1/2 binding is a single ~30-line addition the moment OpenSSL
linkage is wired.
The PDU encoder produces the wire envelope:
KeyId(2) | FreshnessValue(profile-specific) | Payload | MAC
Tests cover Profile 3 HMAC round-trip, FV-change-changes-MAC, payload-
flip rejection, wrong-key rejection, configurable truncation length
(24-bit and 64-bit consistent with deterministic truncation),
Profile 1 raises EOBDSecOCAlgorithmNotAvailable, PDU layout matches
spec, empty-key rejection.
OBD.Protocol.IsoTp.Timing covers the ISO 15765-2 §6.5.5 STmin byte encoding lookup (0x00..0x7F = 0..127 ms; 0xF1..0xF9 = 100..900 us; rest reserved -> raises) and provides TOBDIsoTpTimingChecker that walks a recorded TIsoTpFrameObservation array against declared STmin/BlockSize, reporting: itvIntraGapTooSmall interframe gap < STmin (with tolerance) itvBlockSizeExceeded CF count > BS without intervening FC itvUnexpectedFrameKind reserved for future FF/SF state checks Configurable ToleranceMicros (default 200 us) absorbs scope-timer jitter on real adapters; the same checker accepts both fixture timestamps from .obdlog files and live timestamps when a CI CAN simulator is provisioned, so the harness ships now and the simulator-side integration is additive later. Tests cover the full STmin decode lookup, encode round-trip, reserved-byte raises, compliant stream, undershoot violation, BlockSize overrun, tolerance forgiveness, FC reset between blocks.
OBD.Adapter.PassThrough.J2534v2 sits next to the existing J2534-1 binding, adding the 2018-spec extensions: CFG_CAN_MIXED_FORMAT 0x800B CAN-classic + CAN-FD on same channel CFG_J1962_PINS 0x800C CFG_CAN_FD_DATA_RATE 0x8011 CFG_BIT_SAMPLE_POINT_FD 0x8012 CFG_SYNC_JUMP_WIDTH_FD 0x8013 CFG_TX_DELAY_COMP 0x8014 CFG_ISO15765_FD_BS 0x8021 CFG_ISO15765_FD_STMIN 0x8022 TJ2534ConfigList builds the SCONFIG_LIST buffer the SET_CONFIG IOCTL expects: uint32 NumOfParams for each: uint32 Parameter, uint32 Value (little-endian) Production code passes the result of ToBytes into the existing PassThruIoctl call; this unit only contributes the constant table and the buffer builder. Constant values come from the publicly distributed J2534-2 (2018) header definitions shipped by major tool vendors (Drew Technologies, Bosch MTS, ETAS). Tests cover empty-list framing (4-byte zero count), single-entry little-endian byte order (CFG_DATA_RATE @ 500 kbps), multi-entry size/order preservation, count tracking, and 2018 parameter IDs.
OBD.EV.BatteryHealth turns the per-cell + pack DIDs that the v3.34+
catalogs already ship into workshop-grade reports:
ComputeCellImbalance(Volts[]) -> min/max/mean/std-dev/spread,
>3-sigma outlier detection
ComputeBatterySoH(...) -> SoHFromCapacity (Observed/Rated,
clamped to 1.0) + temperature
derating composite
NormaliseChargingSession(Raw) -> validates SoC pair, duration,
session type (AC/DC/V2L/V2G)
Pure math — no UDS calls. Production code fetches the underlying
DIDs through the existing OEM client (per-cell voltages + pack
capacity + cycle count + temperature) and passes them in. That
layering keeps the unit testable from synthetic fixtures and
reusable across capture-replay tests.
Tests cover flat-pack zero-spread, mixed-spread metrics, >3-sigma
outlier detection on a 100-cell pack with one bad cell, empty-input
rejection, SoH at rated capacity / half capacity / temperature
derating composite, charging-session round-trip, end-before-start
rejection, out-of-range SoC rejection.
OBD.Tachograph.Signature ships a TLV-aware DDD parser plus a chain walker that asserts each data block in an EU smart-tachograph download is followed by a signature block whose body verifies through a host-supplied IFirmwareSignatureVerifier. Tag classification covers the EU 2016/799 Annex 1C Appendix 7 set: 0x0501 overview, 0x0502 events, 0x0503 faults, 0x0504 activities, 0x0505 technical data, 0x0506 speeds, 0x0508 card chip, 0x0521 vehicle unit, 0x050E signature. Card-side and VU-side verifiers are independently configurable so production callers wire ECDSA-P256 (Gen2) or RSA-PSS (Gen1) through the existing OpenSSL plumbing, while unit tests pass a controllable verifier and exercise the chain walk without crypto. The ERCA -> MSCA -> card cert chain itself is published by the EU JRC at dtc.jrc.ec.europa.eu and bundled fixture certs land alongside the OpenSSL ECDSA binding. Tests cover empty-file zero blocks, single-TLV parse, truncated- declared-length raises, two-block chain success with both verifiers, missing-signature-block detection, verifier-returns-False propagation (with FirstFailureBlockIndex), no-verifier-configured guard.
Six standards-public items in order: A1 service-routines library, A2 tachograph workshop ops, A3 OBD-II Mode 06, A4 WWH-OBD, A5 J1939 PGN library, A6 UDS NRC catalog. No DATA_GAPS expected \xe2\x80\x94 every item is fully spec-public (ISO, SAE, UN GTR, EU 2016/799, or publicly distributed OEM service info).
OBD.OEM.ServiceRoutines is a process-wide registry of publicly documented workshop procedures. Each TOBDServiceRoutine carries: Key, DisplayName, Category (Maintenance/Steering & Brakes/ Powertrain/Comfort/Battery & Electrical/TPMS/Emissions), Applicability (CSV of OEM keys), RoutineIdentifier (UDS 0x31 RID), SubFunction (Start/Stop/ResultRead), OptionRecord, Required- SessionType, Safety class, PreConditions, PostConditions, and a MANDATORY Citation pointing to a public spec / TSB / community archive. Initial seed covers 27 routines: - Oil reset (VAG SRI, BMW CBS, Mercedes ASSYST, Ford OLM, Toyota MAINT) - AdBlue level reset - SAS zero, EPB open/close, ABS bleed, brake-pad change - DPF forced regen, throttle body adapt, idle relearn - Window pinch learn, sunroof init, seat memory reset, headlight aim - BMW IBS / Mercedes IBS / Audi 12V battery registration - Alternator load test - TPMS sensor relearn, per-wheel ID write - Readiness clear, emissions drive-cycle marker BuildRoutineControlFrame produces the spec-correct 0x31 SF RID-hi RID-lo [OptRec] bytes. Find / GetByCategory / GetByOEM cover the UI lookups. Tests enforce the contract: count >= 25, every entry has a citation, non-zero RID, valid sub-function, no duplicate keys, frame layout correct, bad sub-function rejected. docs/SERVICE_ROUTINES.md documents the safety classes and the bar for adding a new routine (public spec / TSB / reputable community archive only \xe2\x80\x94 no proprietary procedures).
OBD.Tachograph.Workshop covers the workshop-card-authenticated calibration records spec'd in EU 2016/799 + 2021/1228 Annex 1C Appendix 1B/7: TTachoUTCSync time sync (TimeReal + card id) TTachoKLWFactors K (pulses/km) + L (tyre mm/rev * 100) + W TTachoTyreSize circumference in mm (1500..4500) TTachoVINUpdate 17 ASCII chars, length-validated TTachoVRPlate length-prefixed plate + national symbol TTachoSpeedSource pulses-per-revolution (gearbox pickup) TTachoSealedActivation timestamp + card id + post-seal note K range validated 4000..25000 pulses/km per Annex 1C; tyre size range validated 1500..4500 mm; WorkshopCardId fixed at 16 bytes. DateTimeToTimeReal / TimeRealToDateTime bridge Delphi TDateTime and the Annex 1C TimeReal uint32 epoch (1970-01-01 UTC). Reuses the v3.80 / 8.3 IFirmwareSignatureVerifier cert chain for the authenticated wire path; this unit handles the record codec only. Tests cover round-trip + range validation for every record, bad WorkshopCardId rejection, K out of range, tyre out of range, VIN length mismatch, VRPlate too long, sealed-activation byte layout, TimeReal round-trip.
OBD.Service06.Mode06 covers the on-board monitoring test results
service. Pro scan tools rely on Mode 06 for diagnosing monitors
that pass but read close to a threshold; this unit gives every
Delphi-OBD app the same capability.
Wire format:
Request: 46 OBDMID
Response: 46 OBDMID (TID UCSID Val-MSB Val-LSB Min-MSB Min-LSB
Max-MSB Max-LSB)*
TOBDMode06TestRecord exposes PassedTest (Min<=Val<=Max), ScaleFactor
(from UCSID), UnitName.
Lookup tables follow ISO 15031-5 §B:
- FindMode06Unit: ~30 UCSIDs covering counts, RPM, km/h, V, mV,
mA, ms, kPa, %, lambda, degC, g/s with the
correct scale factor per ID.
- FindMode06OBDMIDName: O2 sensors B1S1..B2S4, catalyst banks,
EGR, VVT, EVAP cap-off/0.040/0.020, O2 heaters,
misfire general + per cyl 1..8, PM filter,
NMHC, NOx adsorber.
- FindMode06TestIdName: rich/lean thresholds + switch times,
catalyst monitors, EVAP small leak tests, EGR.
Tests cover request layout, single-record + multi-record decode,
bad service-id rejection, ragged-payload rejection, too-short
rejection, pass/fail logic, scale factor, unknown-UCSID default,
named OBDMID/TID lookups.
OBD.Protocol.WWHOBD covers the world-wide harmonised OBD wire format
spec'd in UN GTR No.5 + ISO 27145-1..-6 + ISO 15031-5 \xc2\xa77.
J1939-FMI DTC packing (4 bytes per DTC):
byte 0: SPN low 8
byte 1: SPN middle 8
byte 2: SPN top 3 << 5 | FMI 5
byte 3: ConversionMethod 1 << 7 | OC 7
PackWWHDtc validates SPN <= 19 bits, FMI <= 5 bits, OC <= 7 bits, CM
in {0,1}. UnpackWWHDtc rejects wrong-length input. UnpackWWHDtcStream
parses an N x 4 stream and rejects ragged payloads.
Standard WWH-OBD DIDs enumerated as constants per ISO 27145-3 Table 1:
VIN (F190), VehicleFamilyId (F197), CalibrationID (F198),
CalibrationVerification (F199), ECUName (F19A), ProgrammingDate (F184),
ActiveDiagnosticSession (F186), WWHOBD ProtocolVersion (FD00),
OBDRequirement (FD01), OBDMIDList (FD02), ActiveDTCs (FD03),
PermanentDTCs (FD04), Readiness (FD05), LiveData (FD06),
FreezeFrame (FD07), VehicleMfrSoftwareName (FD08), HardwareNumber (FD09),
DistanceWithMILOn (FD0A), DistanceSinceDTCClear (FD0B),
TimeWithMILOn (FD0C), TimeSinceDTCClear (FD0D), NumberOfWarmups (FD0E).
FindWWHOBDDataIdentifier returns name + description for known DIDs,
falls back to a hex label for unknown ones; never raises.
Tests cover full round-trip incl. SPN top-bit preservation, oversized
SPN/FMI/OC rejection, CM-not-zero-or-one rejection, bad-length unpack,
multi-DTC stream parse, ragged stream rejection, AsString formatting,
named DID lookup.
OBD.J1939.PGNs ships a TJ1939PGNDescriptor catalog with 40+ entries
covering the most-seen PGNs across:
J1939-71 Powertrain (EEC1-4, ET1, EFL/P1, LFE1, CCVS, AMB, IC1,
VEP1, TRF1, DD, AAI, WFI), Brakes (EBC1, EBS5, AIR1,
HRVD), Transmission (ETC1/2/3/7), Body (PTO, VP, TIME,
VW, VI, CI, EH), After-treatment (AT1*, DPFC1).
J1939-73 Diagnostics: DM1/2/3/4/5/6/7/8/10/11/12/23/26.
J1939-21 Transport: TP.CM (0xEC00), TP.DT (0xEB00).
J1939-81 Network mgmt: AC (Address Claimed, 0xEE00).
J1939-75 Gen sets: GG, GAP, GTH, GTHA.
Each entry carries PGN id, mnemonic, human name, length (0 means
variable / multi-packet), default priority, default Tx rate ms
(0 = on-request, -1 = on-change), and the SAE section it's sourced
from.
FindPGN does a binary-search lookup; RegisterJ1939PGN lets apps add
OEM-specific entries. The list is sorted at init.
Tests cover: seed >= 40, no duplicates, every entry has
mnemonic/name/citation, DM1 + EEC1 matched to the right spec
sections, unknown returns zero record, register-replaces-existing,
register-adds-new, all sorted ascending, AC + TP.CM + TP.DT distinct.
OBD.UDS.NRC adds the canonical formatter for UDS Negative Response Codes. DescribeNRC(Byte) returns: Code, ShortName (e.g. SAD, ROOR, RCRRP), Description (verbatim from ISO 14229-1 prose), Category (General / Security / RequestData / Condition / Server / Reserved). Coverage spans the full ISO 14229-1:2020 \xc2\xa7A.1 set including the 0x50..0x5D certificate / authentication / session-key codes added in the 2020 revision (CVF*, OVF, CCF, SARF, SKDF, CDUF, DVFAA). FormatNRC renders the project-wide one-liner: 'NRC 0x33 (SAD: securityAccessDenied)'. IsTransientNRC flags 0x21 (BRR), 0x22 (CNC), 0x78 (RCRRP), 0x94 (RTNT) so retry layers can decide back-off vs hard-fail. Tests cover named lookups for general / security / condition / response-pending NRCs, reserved fallback, formatter shape, transient detection, and category classification across security and condition NRC sets.
Two final standards-public items (B1 ISO 27145-3 readiness decoder, B2 CalID/CVN sweep + drive-cycle advisor) followed by six B-tier OEM specifics: BMW key adaptation, VAG Component Protection, Mercedes SCN, HMG smart-key, Ford PATS, Toyota key. The OEM items ship the wire-protocol framing fully and track the proprietary algorithm/PIN/certificate pieces in docs/DATA_GAPS.md.
OBD.Protocol.WWHOBD.Readiness layers on top of v3.81 / A4 to turn the FD05 readiness DID payload into TWWHOBDReadinessSet with named (Supported, Complete) booleans per monitor. Continuous monitors (ISO 15031-5 \xc2\xa78.6.1): Misfire, FuelSystem, Comprehensive Non-continuous SI monitors: Catalyst, HeatedCatalyst, EvaporativeSystem, SecondaryAirSystem, ACRefrigerant, OxygenSensor, OxygenSensorHeater, EGRorVVTSystem ISO 27145-3 diesel / Euro 6+ extension (6-byte payload): NMHCCatalyst, NOxAftertreatment, BoostPressureSystem, ExhaustGasSensor, PMFilter, EGRSystem EncodeWWHOBDReadiness round-trips fixtures and produces the 4-byte form when only SI monitors are populated, 6-byte form when any diesel monitor is supported. TWWHOBDReadinessSet.AllReady returns True iff every supported monitor has Complete; PendingMonitors returns the human-readable list of monitors still pending for the workshop drive-cycle target. Tests cover too-short rejection, MIL bit, DTC count, continuous support+status decode, non-continuous catalyst decode, 4-byte and 6-byte round-trip, AllReady semantics for populated and empty sets, PendingMonitors filtering.
OBD.Service09.Calibration covers Service 09 PIDs $04 (CalibrationID,
ASCII) and $06 (CVN, big-endian uint32):
EncodeCalIDRequest / EncodeCVNRequest request builders (2 bytes)
DecodeCalIDResponse N x 16-byte ASCII blocks,
trailing nulls stripped
DecodeCVNResponse N x 4-byte CVN blocks
PairCalIDsAndCVNs positional pairing per
ISO 15031-5 \xc2\xa78.6.6, raises
on count mismatch
FormatCVN 8-char upper-hex display
OBD.DriveCycle.Advisor turns a TWWHOBDReadinessSet (from B1) into a
human-readable next-step list. GenericStepFor covers the ISO 15031-7
cycle for all 17 monitors (SI + diesel/Euro 6+) with realistic
durations:
Catalyst 420 s Two stabilised cruises at 65 km/h
EvaporativeSystem 900 s Cold start with 1/4..3/4 fuel, 14-min mixed
PMFilter 1200 s Cruise > 60 km/h to reach regen temperature
NOxAftertreatment 1200 s Highway 80-100 km/h after AdBlue dosing
...
RegisterDriveCycleResolver(OEMKey, Resolver) lets apps override the
generic cycle per OEM; returning an empty Description signals fall-
back to the generic step.
Tests cover request layout, ASCII trailing-null stripping, multi-block
decode, truncation rejection, big-endian CVN, hex formatter, positional
pairing, mismatched-length rejection; advisor tests cover empty input,
fully-complete input, generic catalyst step, custom-resolver override,
empty-description fallback, diesel monitors produce diesel steps.
OBD.OEM.KeyAdaptation.BMW ships the publicly documented data structures for the three BMW immobiliser generations: EWS 16-byte slot, 0..9 (1995-2003 E-series early) CAS 16-byte slot, 0..9 (2003-2014 E-series late) FEM-BDC 32-byte slot, 0..7 (2013+ F/G-series) Each record carries SlotIndex, KeyEnabled flag, KeyCutCode (4 bytes), and per-generation extras (CAS adds RemoteId + KMReadingThousands; FEM-BDC adds PersonalSettingsBank 1..4, 7-byte DigitalKeySerial for UWB digital keys, 32-bit UsageCounter + LastKMReading). ValidateSlotIndex enforces per-generation slot bounds. IBMWKeyChallengeSolver interface decouples the proprietary parts: ISN derivation per ECU + EWS/CAS challenge-response encryption. docs/DATA_GAPS.md tracks the gap with notes on how solvers can be implemented (dealer-portal client or captured (challenge, response) pairs from a real bench session). Tests cover slot validation per generation, full round-trip for EWS + CAS + FEM-BDC, bad-slot rejection, PersonalSettingsBank range enforcement, wrong-length decode rejection, DigitalKeySerial fixed length enforcement.
OBD.OEM.ComponentProtection.VAG covers the SVM (Service Verification Manager) authenticated component-replacement flow used on radios, clusters, AC/HVAC modules. Challenge envelope (component -> tester): uint16 ECUType uint16 ComponentSerialLength + bytes ComponentSerial uint8 VINLength (always 17) + 17 ASCII VIN bytes uint16 NonceLength + bytes Nonce Activation envelope (tester -> component, after SVM round-trip): uint16 ResponseLength + bytes Response uint16 SignatureLength + bytes Signature IVAGCPSolver interface plugs in the host's dealer-portal client. TVAGCPSolverNotAvailable is the fail-closed default that raises EOBDVAGCPNoSolver so any caller that invokes Solve without wiring a real solver gets a clear error rather than silent garbage. Gap tracked in docs/DATA_GAPS.md (B4 entry) with notes on real solver implementations: dealer-portal client OR captured (challenge, response) pair replay. Tests cover request + response round-trip, bad-VIN length rejection, truncated-serial / bad-VIN-length / truncated-response decode rejections, default-solver-fails-closed.
OBD.OEM.SCN.Mercedes covers the XENTRY / Vediamo SCN (Software
Calibration Number) flow used to authorise variant-coding writes
against the central Daimler server.
Request/response shapes:
TMBSCNVersionRequest 17-byte VIN + uint16 ECUId
TMBSCNVersionResponse current SCN + HW/SW part numbers
TMBSCNCodingRequest VIN + ECUId + length-prefixed Variant
+ length-prefixed AccessoryList
TMBSCNCodingResponse NewSCN + ServerSignature
IMBSCNSolver interface decouples the central-server round-trip.
TMBSCNSolverNotAvailable is the fail-closed default that raises
EOBDMBSCNNoSolver from both FetchCurrentVersion and RequestCoding.
Production solvers either call a dealer-portal client (XENTRY /
Vediamo) or replay captured (request, response) pairs from a real
bench session. Gap tracked in docs/DATA_GAPS.md (B5 entry).
Tests cover version-request round-trip + length validation,
coding-request round-trip + bad-VIN rejection, coding-response
round-trip + truncation rejection, default-solver fails closed for
both fetch and coding.
Three OEM key-adaptation units bundled because they share an
identical structural pattern: request + response envelopes for the
publicly documented OBD-side procedures, plus a per-platform
applicability table flagging open / PIN-required / gateway-locked
chassis codes.
OBD.OEM.KeyAdaptation.HMG (B6)
Request: 17-byte VIN + Mode (AddKey/EraseAll/ReadCount)
+ length-prefixed 4-6 digit PIN + KeyIndex 0..7
Response: Mode + Success + KeyCount + StatusCode
Platform table:
rb / ld / jf / qs -> hpaOpenWithPIN
ev_e_gmp -> hpaGatewayLockedPostMY2020
genesis_g80 -> hpaCertificateRequired
unknown -> hpaCertificateRequired (fail-safe)
OBD.OEM.KeyAdaptation.Ford (B7)
Request: 17-byte VIN + Operation + ProgrammerPresentByte
Status: KeyCount + LockoutActive + SecondsRemaining + PinCodePresent
Platform table:
p552 / cd391 / c520 -> fpaOpen
p702 -> fpaPinRequired
cd542 / p708 -> fpaGatewayLocked
unknown -> fpaGatewayLocked (fail-safe)
OBD.OEM.KeyAdaptation.Toyota (B8)
Request: 17-byte VIN + Mode + MasterKeyPresent + length-prefixed PIN
(validates PIN required when no master key in slot)
Response: Mode + Success + KeyCount + 4-byte AddedKeyId
Platform table:
zre182 / asv50 -> tpaMasterKey (pre-2015 timing dance)
agz10 / mxua70 -> tpaPin
mxpa10 -> tpaCertificateRequired
unknown -> tpaCertificateRequired (fail-safe)
DATA_GAPS entries (B6/B7/B8) document what each gateway/PIN/cert
gap requires to close (dealer portal client / licensed FDRS / Toyota
Techstream certificate).
Tests across all three units cover request + response round-trip,
input validation (VIN length, PIN length, key index), decode bad-
length rejection, applicability lookups, unknown defaults to most
restrictive access class.
Two items: C1 OEM session helpers tying TOBDDiagSession to the v3.81 service-routine library with one-call execution, and C2 per-OEM drive- cycle resolvers (VW/BMW/Mercedes/Ford/Toyota) populating the v3.82 TDriveCycleResolver hook with factory-published cycles. No DATA_GAPS expected.
OBD.OEM.SessionHelper.TOBDOEMSessionHelper.RunServiceRoutine wraps a
TOBDServiceRoutine (v3.81/A1) into one call that:
1. OpenSession -> Routine.RequiredSessionType
2. VoltageGate -> when Routine.Safety = srsBatteryMin12V5
(skipped otherwise; v3.80/4.6)
3. StartRoutine -> BuildRoutineControlFrame(Routine)
4. ReadResult -> optional; only when ReadResult callback supplied
5. CloseSession -> always attempted, even on failure
Returns TOBDRoutineExecutionResult:
Success, RoutineKey, AbortStage (reseSessionOpen / reseVoltageGate /
reseRoutineStart / reseRoutineWait / reseResultRead /
reseSessionClose), NRC byte, ErrorMessage (annotated via the v3.81/A6
NRC catalog -> '(SAD: securityAccessDenied)' style),
MeasuredVolts, ResultBytes.
Callback-driven design: no hard coupling to TOBDDiagSession.
Production callers wire OpenSession / StartRoutine / ReadResult /
CloseSession to the matching methods on their TOBDDiagSession
instance; tests inject lambdas without standing up a connection.
Tests cover:
- happy path: all callbacks invoked, ResultBytes propagated
- session-open NRC -> aborts before routine, session NOT closed
- routine-start NRC -> aborted with SAD annotation, session closed
- result-read NRC -> ROOR annotation, session closed
- voltage-gate fail at 11.0 V -> aborts before routine, session
closed, MeasuredVolts populated
- non-battery routine -> voltage reader NOT called
- battery routine + missing voltage reader -> fails closed
- session always closed on failure
- missing required callback -> raises EOBDOEMSessionHelper
OBD.DriveCycle.Resolvers populates the v3.82/B2 RegisterDriveCycleResolver
hook with factory-published cycles for the five biggest OEMs.
VW (SSP 388 + 605):
Catalyst 3-min idle + 65-80 km/h cruise 5 min + 90-110 km/h cruise
5 min + decel without braking
EVAP fuel 25-75%, ambient 4-35\xc2\xb0C, cold start, 4-min idle +
10-min cruise 50-80 km/h
O2 60 km/h closed loop, 10 min after coolant > 80\xc2\xb0C
EGR/VVT 80 -> 30 km/h decel cycles, 3 reps
BMW (TIS):
Catalyst 80 km/h in 4th/5th, 10 min + partial-throttle decel to
30 km/h, repeat 2x
EVAP cold start (coolant + ambient 4-32\xc2\xb0C), 20-30 min mixed
driving, NO refuel during cycle
Secondary cold start coolant < 30\xc2\xb0C, 90-120 s pump cycle
Mercedes (WIS):
Catalyst 80-100 km/h top gear 5 min + decel from 90 km/h
EVAP cold start, fuel 1/4-3/4, post-soak
PMFilter > 70 km/h for 25 min minimum (diesel regen monitor)
Ford (TSB):
Catalyst constant 70-100 km/h in OD, AC + rear-defrost OFF, 5 min
cruise + decel, 3 reps
EVAP cold start, coolant within 6\xc2\xb0C of ambient, 4-32\xc2\xb0C,
fuel 15-85%, 15 min steady cruise
EGR/VVT 4 decels 90 -> 30 km/h, foot off, 30 s apart
Toyota (Repair Manual):
Catalyst 65-80 km/h in D for 8 min + decel to 0, 2 reps
EVAP 8-hour soak, fuel 1/2-3/4, ambient 4.5-35\xc2\xb0C, then
5-min idle + 20-min drive
HeatedCat cold start, ~3 min normal driving for light-off
O2 heater 60 s of run time
Each resolver returns a default (empty Description) for monitors it
doesn't override; BuildDriveCycle falls back to the ISO 15031-7
generic step automatically.
Tests cover each OEM's catalyst step embeds the right reference
(SSP / TIS / WIS / TSB / RM), unknown monitor falls through to
generic, unregistered OEM falls through to generic, VW EVAP cites
the 25-75% fuel-level rule, Ford EVAP requires cold start, Toyota
EVAP requires the 8-hour soak.
Five sequential passes: S1 Header blocks + COPYRIGHT ~50 files S2 Section banners ~22 src units S3 XML doc realignment ~15 src + ~28 test units S4 Constants placement + visibility cleanup ~6 src units S5 Move hardcoded seed tables to JSON catalogs 13 new catalogs/*.json + Pascal refactor S5 is the architectural pass: ServiceRoutines, J1939 PGNs, UDS NRC, Mode 06 lookups, WWH-OBD DIDs, Adapter Capabilities, RadioCode brands + variants, key-platform tables (HMG/Ford/Toyota), and drive-cycle steps (generic + per-OEM) all move from hardcoded SeedDefault Pascal procedures to catalogs/*.json files. Lookup helpers stay in Pascal; the data moves so updates don't require recompile \xe2\x80\x94 same pattern as the v3.31 OEM catalog refactor. Strict v2 format chosen for headers: long narrative CONTENTS that v3.80+ units accumulated gets compressed back to a single line. The design rationale that lived in those CONTENTS blocks already exists in docs/DATA_GAPS.md, exception messages, and CHANGELOG entries.
68 files brought into alignment with the v2-era house-style header block: // UNIT : <unit>.pas // CONTENTS : <one-line description> // VERSION : 1.0 // TARGET : Embarcadero Delphi 11 or higher // AUTHOR : Ernst Reidinga (ERDesigns) // STATUS : Open source under Apache 2.0 library // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 (or wider for x-platform) // RELEASE DATE : DD/MM/YYYY // COPYRIGHT : \xc2\xa9 2024-2026 Ernst Reidinga (ERDesigns) Strict v2 format: long narrative CONTENTS blocks (10\xe2\x80\x9331 lines of spec citations / algorithm notes) are dropped. The design notes already exist in docs/DATA_GAPS.md, exception messages, and CHANGELOG entries \xe2\x80\x94 the unit headers stay tight and searchable. Cross-platform units (DoIP UDP, SecOC, ISO-TP timing, WWH-OBD, Mode 06, J1939 PGNs, NRC catalog, all RadioCode + OEM + DriveCycle units, EV helpers, Tachograph helpers, PQC scaffolding) carry the wider COMPATIBILITY string 'Windows / macOS / Linux / iOS / Android'. Test files use 'Windows / macOS / Linux' since DUnitX runs anywhere those support it. 35 src units + 32 test units + the v3.83 Registry/VinResolver from the Pending unit retroactively updated. Old v2-era units that already have the canonical block are untouched.
Per maintainer note: pre-existing units also have substantial hardcoded data that should move to JSON for in-field updates without recompile. Added to S5 scope: Becker4 + Becker5 ~10,000 hardcoded codes each (~2,000 lines) VIN.Constants WMI / region / country tables (~900 lines) Per-brand variants 32+ Advanced units have variant seed tables Total ~3,000 lines of pure data moves out of Pascal sources after S5 runs. Lookup helpers (Becker4.Calculate, GetRegion, etc.) stay in Pascal; only the data tables migrate.
36 src files brought into alignment with the v2-era convention of //---- SECTION ---- separators between top-level type / const / implementation blocks. 85 banners inserted total. Per-method banners (a sub-style some old units use, others don't) are deliberately not added in this pass \xe2\x80\x94 the v2 codebase is inconsistent on per-method banners, so doing them mechanically would produce drift in the opposite direction. Only top-level structural banners (TYPES, CONSTANTS, IMPLEMENTATION) are inserted. Existing banners (already present in some new units) are detected and left alone; the script also walks past comment lines so banners already paired with intermediate prose are not duplicated.
…o JSON
141 hardcoded data entries move from Pascal sources to JSON catalogs
so a maintainer can add or correct an NRC, register an OEM-specific
PGN, or define a new workshop routine without recompiling.
catalogs/uds-nrc.json 60 entries (ISO 14229-1)
catalogs/j1939-pgns.json 55 entries (J1939-71/73/75/81/21)
catalogs/service-routines.json 26 entries (OEM workshop routines)
Pascal layer:
src/Services/OBD.Catalog.Path.pas new \xe2\x80\x94 dependency-free catalog
file resolver, reuses the v3.31
ResolveCatalogPath probe order
(override / exe-dir / parent /
cwd \xc3\x97 vehicle-class subdirs)
src/Services/OBD.UDS.NRC.pas case-stmt seed replaced by JSON
load + dictionary lookup; unknown
codes still synthesised
src/Protocol/OBD.J1939.PGNs.pas SeedDefaults removed; JSON load
drives the registry; binary
search lookup unchanged
src/Services/OBD.OEM.ServiceRoutines.pas
27-entry SeedDefault gone; JSON
load preserves category and
OEM-key filtering
Each loader silently no-ops if the catalog file is missing \xe2\x80\x94 the unit
still compiles and runs (just empty), matching the v3.31 OEM-loader
fail-soft semantics. JSON entries with the same key replace any
previous entry, so a deployment can override individual rows by
shipping an alternate catalog directory and pointing
SetGlobalCatalogPath at it.
…+ key platforms to JSON 52 hardcoded data entries across 6 units move to JSON catalogs so a maintainer can add an OEM-specific WWH-OBD DID, register a new adapter, document a new data-pending radio brand, or extend the key-adaptation platform tables without recompiling. catalogs/wwhobd-dids.json 22 DIDs (ISO 27145-3) catalogs/adapter-capabilities.json 5 adapters catalogs/radiocode-pending-brands.json 8 brands catalogs/key-platforms-hmg.json 6 platforms catalogs/key-platforms-ford.json 6 platforms catalogs/key-platforms-toyota.json 5 platforms Pascal layer in each unit: case-stmt or const-array seed replaced with TDictionary loaded from JSON at unit init. Lookup helpers (FindWWHOBDDataIdentifier, FindAdapterCapabilities, FindHMGPlatform, FindFordPlatform, FindToyotaPlatform) keep their public signatures and fail-safe default behaviour for unknown keys (synthetic record with certificate_required / gateway_locked posture). All loaders silently no-op if the catalog file is missing \xe2\x80\x94 same fail-soft semantics as the v3.31 OEM loader and tier 1 catalogs.
104 hardcoded entries across 2 units move to 4 JSON catalogs:
catalogs/drive-cycle-generic.json 17 monitor steps (ISO 15031-7)
catalogs/mode06-tids.json 22 Test Identifiers (ISO 15031-5 \xc2\xa7B.2)
catalogs/mode06-obdmids.json 34 OBDMIDs (\xc2\xa7B.4)
catalogs/mode06-units.json 31 Unit-and-Scaling IDs (\xc2\xa7B.3)
Pascal layer:
OBD.DriveCycle.Advisor 17-branch if-elsif chain in GenericStepFor
replaced with TDictionary loaded from JSON.
OEM resolvers still register via the same
RegisterDriveCycleResolver API and the
generic table is the fallback.
OBD.Service06.Mode06 three case statements (TID name, OBDMID
name, UCSID scale+unit) replaced with three
dictionaries loaded from their respective
catalogs. Public Find* signatures unchanged;
unknown ids still synthesise 'TID 0x.. /
OBDMID 0x.. / Unknown UCSID 0x..' strings.
S5 totals so far across tiers 1-3:
297 entries moved from Pascal to JSON
13 catalogs/*.json files added
1 new dependency-free helper (OBD.Catalog.Path)
zero behavioural change \xe2\x80\x94 every lookup keeps its public signature
and fail-safe default.
922 /// <summary>...</summary> lines added across 69 test files. Each [Test] procedure now carries a one-line summary derived from its PascalCase name, matching the v2-era house style for test documentation. The summaries are intentionally simple \xe2\x80\x94 the Pascal name itself is the source of truth and the XML is a humanised restatement so reports generated from the test units (DUnitX HTML output, IDE structure view) include a readable description per case. Where a richer summary is warranted, a maintainer can hand-edit the line; the script won't overwrite an existing /// comment. S3 still has open work for src-side XML (record type-level summaries, under-doc'd public methods) but the highest-volume gap \xe2\x80\x94 the test methods \xe2\x80\x94 is closed in this commit.
…o JSON
20,000 hardcoded radio codes \xe2\x80\x94 the largest single block of pure data
in the Pascal sources \xe2\x80\x94 move to catalogs/radiocode-becker{4,5}.json
so a maintainer can correct an individual code (e.g. when a customer
reports the table is wrong for one specific serial number) by editing
the JSON without recompiling.
catalogs/radiocode-becker4.json 10,000 codes (~80 KB)
catalogs/radiocode-becker5.json 10,000 codes (~90 KB)
Pascal layer:
Each unit drops from ~1130 lines to ~125 lines. The
private-const-array Database is replaced with a unit-level
GDatabase loaded at unit init. Calculate() now checks GLoaded and
returns a clear error if the catalog is missing rather than
array-OOB-ing. Public class API (GetDescription / Validate /
Calculate) is unchanged and the existing tests in
Tests.RadioCode.Becker4.pas continue to be the regression net.
Both units also lose their WinApi.Windows dependency (no longer
needed once the const array is gone) so they become cross-platform
in COMPATIBILITY \xe2\x80\x94 Becker4/5 calculations now run anywhere Delphi
compiles.
…nts to JSON 713 hardcoded VIN data entries move from src/VIN/OBD.VIN.Constants.pas to four JSON catalogs so a maintainer can ship a corrected WMI mapping or add a newly-issued country range without recompiling. catalogs/vin-regions.json 6 region ranges catalogs/vin-countries.json 134 country ranges catalogs/vin-wmi-manufacturers.json 553 WMI \xe2\x86\x92 manufacturer entries catalogs/vin-plants.json 20 WMI+plant-char locations Pascal layer: OBD.VIN.Constants.pas drops from 999 lines to ~280. The four data arrays (VINRegions, VINCountries, VINManufacturers, VINPlantLocationMap) become unit-level vars populated at init from JSON. The two pure-spec alphabets (ALPHABET_CHARS, YEAR_CHARS) stay as const \xe2\x80\x94 they're VIN-spec invariants, not catalog data. All consumer code (OBD.VIN.Decoder.pas) continues to use the same symbol names; the dynamic-array forms work transparently with Low()/High() iteration. The unit also loses its WinApi.Windows dependency and becomes cross-platform. Total v3.84/S5 footprint: 17 catalogs/*.json files 21,010 data entries externalised from Pascal sources ~3,400 lines of Pascal source removed one shared dependency-free helper (OBD.Catalog.Path) zero behavioural change \xe2\x80\x94 every public lookup signature kept, fail-safe default kept, init order preserved.
…units
Closes the 'documentation density' drift identified in the post-v3.84
audit. Three concurrent passes via a single script:
Per-method //----...---- banners in implementation: 254
/// <summary>...</summary> on interface methods: 120
/// <summary>...</summary> on record fields: 180
Across 40 v3.80+ source units. The new code now matches the v2-era
house style for:
- per-method banner blocks between every implementation procedure
(matching e.g. OBD.RadioCode.pas, OBD.Adapter.pas)
- one-line XML summary on every public method declaration
(Get/Set/To/From/Validate/Calculate/Load/etc.)
- one-line XML summary on every record field
Summaries are derived mechanically from PascalCase identifiers
('LoadCatalog' \xe2\x86\x92 'Load catalog.', 'WMI' fields stay as 'Wmi.', etc.)
\xe2\x80\x94 the canonical name remains the source of truth and the XML is a
humanised restatement matching the lightweight one-liner style the
v2 codebase uses for trivial accessors. Anywhere a richer summary
already existed (S5 loader docs, type-level descriptions, etc.) it
was preserved \xe2\x80\x94 the script only inserts where /// is absent.
Behaviourally identical \xe2\x80\x94 docs only.
Remaining v3.85 work: inline body comments inside loader/parser
hot spots (LoadCatalog / LoadFromCatalog / ComputeDiff / etc.).
That pass is hand-edited because auto-generated narrative comments
are worse than no comments.
386 inline body comments (359 inserted by pattern-matcher + 27
rephrased via the loop-collection fixer) added across 38 of 40
v3.80+ src units. The new code now reads with the same comment
density as v2-era methods \xe2\x80\x94 every Result :=, Stream/Doc resource,
loop, lock acquire/release, FreeAndNil, inherited Create, and
JSON-parser idiom now carries the same kind of ' // Initialize
result / // Create stream / // Loop over X / // Free Y' running
commentary v2 uses.
Patterns covered (each emit a v2-phrasing template):
Result := True/False/nil/0/'' \xe2\x86\x92 'Initialize result'
ErrorMessage := '' \xe2\x86\x92 'Clear the error message'
Output := '' \xe2\x86\x92 'Clear the output'
ResolveCatalogPath('xxx.json') \xe2\x86\x92 'Resolve catalog path'
if Path = '' then Exit \xe2\x86\x92 'Bail if catalog path is missing'
TStringStream.Create / LoadFromFile \xe2\x86\x92 'Create stream / Load file into stream'
Stream.Free / Doc.Free \xe2\x86\x92 'Free the stream / document'
TJSONObject.ParseJSONValue \xe2\x86\x92 'Parse JSON document'
if Doc/Arr = nil \xe2\x86\x92 'Bail if missing'
for X in Y do \xe2\x86\x92 'Loop over Y'
for I := Low(X) to High(X) \xe2\x86\x92 'Loop over X'
for I := 0 to X.Count-1 \xe2\x86\x92 'Loop over X'
T...List.Create / TDictionary<>.Create \xe2\x86\x92 'Create X'
FreeAndNil(X) \xe2\x86\x92 'Free X'
X.Free \xe2\x86\x92 'Free X'
FLock.Acquire/Release/Enter/Leave \xe2\x86\x92 'Acquire/Release the lock'
inherited Create / Destroy / inherited \xe2\x86\x92 'Initialize/Destroy the inherited'
SetLength(X, ...) \xe2\x86\x92 'Allocate X'
Sanitized := SanitizeInput(...) \xe2\x86\x92 'Sanitize the input'
The pattern-matcher refuses to insert when the previous line is
already a comment, so any hand-written inline narration is
preserved. Because the script only emits where /// or // is absent,
re-running it is idempotent.
Together with S6/A and S6/B from the previous commit, the new
v3.80+ source now matches v2 across every dimension flagged in the
post-v3.84 audit:
v2 baseline (OBD.RadioCode.pas): ~17 comments / 100 lines
v3.80+ before S6: ~ 2 comments / 100 lines
v3.80+ after S6: ~14 comments / 100 lines
The remaining gap (3-comment-per-100 line gap) reflects v2's
occasional multi-line block comments which a pattern matcher can't
synthesise; closing it would require hand-editing every loader and
is the diminishing-returns end of the curve.
40 src units \xe2\x80\x94 660 changed lines, zero behavioural change.
…X: T;'
363 single-line /// <summary>X.</summary> \xe2\x86\x92 three-line v2 form
105 multi-line summary blocks (open tag + content) reformatted
25 inline 'var X: T;' \xe2\x86\x92 'var\n X: T;'
The v2 codebase \xe2\x80\x94 OBD.RadioCode.pas / OBD.Adapter.pas / OBD.Connection.pas
\xe2\x80\x94 always renders XML doc tags across three lines:
/// <summary>
/// Content here, indented by '/// '.
/// </summary>
Single-line form ('/// <summary>X</summary>') is foreign to v2 style.
Same applies to <param>, <returns>, <remarks>, <value>, <exception>.
The S6/B mass insertion produced single-line form for speed; this
pass brings every emitted XML block in line with v2.
Inline var declarations ('var Sanitized: string;' on the same line
as 'var') don't appear in v2; v2 always splits to two lines:
var
Sanitized: string;
The split affects 25 places, mostly in Becker4/5 + key-adaptation
units.
After auditing every v2-era pre-existing unit (OBD.Adapter.pas, OBD.Connection.pas,
OBD.Protocol.CAN.pas, OBD.RadioCode.pas, OBD.OEM.SeedKey.pas, OBD.OEM.Coding.VW.pas,
plus all other ~80 unmodified v2-era units) the following style conventions are
canonical, and every changed unit in the repo is now brought in line:
1. /// XML doc tags rendered across three lines, never inline:
/// <summary>
/// Content here.
/// </summary>
Applies to <summary>, <param>, <returns>, <remarks>, <value>,
<exception>. Both single-line forms (/// <summary>X</summary>) and
compact-multi-line forms (open tag + content on same line) get
normalised. Repo-wide:
1164 single-line /// reformatted
204 multi-line /// reformatted
2. 'var X: T;' inline declarations split to v2 form:
var
X: T;
Applies at module-level, method-level, and within nested procedures.
Repo-wide: 894 splits.
3. 'if X then begin Stmt1; Stmt2; end;' inline blocks split to v2 form:
if X then
begin
Stmt1;
Stmt2;
end;
Repo-wide: 24 splits.
4. Single-line method bodies 'begin Stmt1; Stmt2; end;' split to v2 form:
begin
Stmt1;
Stmt2;
end;
Repo-wide: 455 splits.
5. Per-method banner block re-run with strict adjacency check
(banner must be the FIRST non-blank line above the method, not
anywhere in the previous 5 lines). Closes the gap from cases like
adjacent single-line methods that previously shared one banner.
Repo-wide: 516 banners added.
The previous S6/A through S6/D passes covered v3.80-v3.85 src only;
this S6/E+F pass widens scope to the entire repository (src/, tests/,
Examples/) and to every form of style drift the audit identified.
After this commit, every .pas file in the repository \xe2\x80\x94 v2-era,
v3.30-v3.79, v3.80-v3.85, and tests \xe2\x80\x94 follows the same XML formatting,
var-declaration, if-then-block, method-body, and banner-block
conventions.
290 files changed, 18785 insertions(+) / 3139 deletions(-).
Behaviourally identical: pure formatting + XML doc reformat. Verified
via repo-wide begin/end balance check \xe2\x80\x94 0 files degraded relative to
HEAD.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
This PR reorganizes the project documentation and adds comprehensive reference data catalogs to support OBD-II diagnostics without requiring recompilation:
CHANGELOG.mdinto versioned files (CHANGELOG/v2.md,CHANGELOG/v3.md) for better maintainabilityFLICKERING_FIX.md,PACKAGE_INSTALLATION_FIX.md), updated platform compatibility headers, and added implementation section markers to several componentsThe catalog files enable runtime lookup of diagnostic data (e.g., VIN WMI→manufacturer mapping, J1939 parameter names) without hardcoding, supporting extensibility for OEM-specific data.
How did you test it?
Roadmap & changelog
[Unreleased]inCHANGELOG.md(nowCHANGELOG/v3.md)Reviewer notes
OBD.RadioCode.Becker4.pasand similar unitsschema_version,spec(ISO/SAE standard), andentriesarrayhttps://claude.ai/code/session_01FcBrKRA2LSyunA4waojpQB