Files
opcda-diagnozer/CHANGELOG.md
T
2026-10-08 07:26:04 +02:00

6.2 KiB
Raw Blame History

Changelog

All notable changes to the OPC DA Diagnozer are documented here. The format follows Keep a Changelog; versions bump per SemVer. Dates are YYYY-MM-DD.

[Unreleased]

Added — Phase 1 core engine (the continuous monitoring loop)

  • run verb: the foreground monitoring loop — coordinator + dedicated COM worker thread (the client is born on the worker; COM is per-thread), 1-second default polling of configured tags and device indicators, heartbeat (D15) every heartbeat_s, clean Ctrl+C stop.
  • Detection engine, all pure and fake-client tested: latency spike / sustained streak / rolling p95 (D4–D6), per-tag quality transitions, persistence and flapping (D8–D11), stale timestamp & frozen value (D12–D13), mass-quality degradation with latch (D14), device-link indicators (D16, edge-level DOWN/UP against configurable ok_values).
  • Hang watchdog (spec §10.4): polls exceeding timeout_ms raise the D7 soft-hang event and keep the thread; silence beyond hard_hang_s abandons the stuck COM thread (never joined — dead RPC channel) and rebuilds.
  • Connection state machine with exponential backoff (1 s → 60 s, reset on success) and reconnect accounting (D1/D2 with outage duration, D3 flapping).
  • Fake-client scenario engine: scripted polls and whole-call failures power the entire poller/detector test suite without COM.
  • Clear-text status rendering (status_text): BAD / COMM FAILURE (0x18) everywhere events are shown.

Added — server discovery & browsing

  • list-servers verb: enumerates OPC DA servers locally or on a DCOM host via the Automation Wrapper (GetOPCServers), with a no-COM registry-scan fallback (--source registry|automation|auto), JSON output, and DA-version metadata.
  • Browsing of the connected server's address space, unified in the list-items [all|branches|tags] verb: all prints the full structure tree (branches with /, tags with [ItemID: ...]), branches the structure-only flat list, tags the flat ItemID list for config tags:. --branch (dotted path or repeated segments), --depth N (1 default, 0 = whole subtree), --json. Replaces the earlier split list-devices/list-tags.
  • browse.max_depth config key (default 32) as the address-space walk guard, validated by check-config; cycle-safe tree walk in opc/browse.py.
  • SpaceNode tree data model (branch/leaf) replacing the earlier device-centric DeviceInfo.

Added — reading & CLI

  • read verb: one-shot read of explicit tags (or the config's tags) with a table or --json, --source device|cache, and a non-zero exit when any tag doesn't read GOOD — scriptable as a health check.
  • probe verb: connect once and read every configured tag once.
  • --server / --host overrides on every connecting verb; with --server no config file is needed at all.
  • Frozen-exe default config path: config.yaml next to the exe (a Windows service starts with CWD = System32), not the working directory.

Added — monitoring design (D16) & config

  • devices: config section with DeviceEntry(name, tag, ok_values): D16 device-link indicators. The normal (connected) reading is configurable per indicator (ok_values, default [true]) — inverted fault flags [false, 0] and string states ["RUN"] are first-class. Strict validation: duplicate indicators and indicator/tag-list overlap are rejected; check-config prints the indicators.
  • Detection rules D16 (DEVICE_LINK_DOWN/DEVICE_LINK_UP) and the plain-text anomaly log reporter (report/textlog.py task) specified in the docs (SPECIFICATION §6.1, §9.1; IMPLEMENTATION_PLAN Phases 1–2) — implementation pending, see TODO.md.
  • Clear-text status rule (spec §7): every reporter writes NAME (0xNN) — e.g. BAD / communication failure (0x18).

Added — packaging

  • scripts/build_exe.ps1: PyInstaller build of the 32-bit console exe with hidden imports for the deferred COM modules (incl. win32timezone — see Fixed), embedded version metadata, a PE-bitness check, and smoke tests including a frozen-read canary against a live server.
  • --version and this CHANGELOG.md / TODO.md.

Fixed

  • COM lifecycle: all COM references (browsers, groups, items) are released inside the apartment before CoUninitialize; retained exception tracebacks are cleared in as_com_error — both caused "Win32 exception occurred releasing IUnknown" noise and hard crashes at exit.
  • Frozen exe: every tag read failed with SystemError: ... InvokeTypes ... exception set because pywin32 lazily imports win32timezone during VARIANT-DATE conversion and PyInstaller never bundles it — now a hidden import, with the build canary guarding it.
  • Reads: per-tag failures now carry the underlying exception's own type and text (_per_tag_error) instead of a bare "failed"; a degenerate device timestamp (never-updated alias, DST math) can no longer fail a read — the timestamp is metadata and is dropped to None.
  • Matrikon browse quirk: servers may return an empty ItemID for special leaves (alias roots); the adapter falls back to the bare browse name.
  • HRESULT labels: OPC_E_UNKNOWNITEMID (0xC0040007) and OPC_E_INVALIDITEMID (0xC0040008) were swapped; now corrected everywhere.

Verified

  • All CLI verbs live-tested against Matrikon OPC Simulation Server (Matrikon.OPC.Simulation.1): discovery, browse (all kinds/depths), reads (incl. alias tags after server-side alias repair), config-driven max_depth, and the frozen exe (onedir + onefile) including the read canary.
  • Test suite: 108 tests, all passing (pure logic + fake-client driven, runs on any OS; COM paths are live-smoke-tested on dev boxes only).

[0.1.0] - 2026-10-07

Added

  • Initial project skeleton: typed YAML config with strict actionable validation (check-config), OPC DA quality decode tables (spec §7), event model skeletons (DiagEvent, D1–D15 EventKinds), client/protocol abstractions, and the fake in-process client powering cross-OS tests.
  • Phase 0 decisions recorded: 32-bit interpreter mandatory (Automation Wrapper is a 32-bit in-proc DLL), ruamel.yaml over PyYAML (no 32-bit cp314 wheels), COM imports import-guarded for cross-OS test runs.