This commit is contained in:
Jonas Bengtsson
2026-10-08 06:30:54 +02:00
parent 76b1cc5ccf
commit a1af8d68c9
12 changed files with 966 additions and 21 deletions
+98
View File
@@ -0,0 +1,98 @@
# TODO — implementation status
Companion to `docs/IMPLEMENTATION_PLAN.md` (the authoritative plan) and
`CHANGELOG.md` (what shipped). One row per plan task; updated as work lands.
Legend: **[done]** complete & verified · **[partial]** some of it landed ·
**[todo]** not started · **[skip]** dropped by decision.
## Phase 0 — Feasibility & environment **[done]**
- [done] Wrapper backend decision: OPC DA Automation Wrapper (`OPC.Automation`)
→ 32-bit interpreter mandatory (spec §11).
- [done] Test server: Matrikon OPC Simulation Server, local ProgID
`Matrikon.OPC.Simulation.1`; baseline read RTT + wrapper error shapes
recorded (repo memory).
- [done] Dependencies chosen for a 32-bit, no-compiler install:
ruamel.yaml (PyYAML has no cp314 win32 wheels), pywin32.
## Phase 1 — Core engine
- [done] 1. `config.py` — typed YAML config + strict validation; **D16
`devices:` indicators with configurable `ok_values`**; `check-config`
prints them.
- [done] 2. `quality.py` — decode tables (spec §7).
- [todo] `status_text(raw) -> "NAME (0xNN)"` clear-text helper (spec §7
closing rule; used by every reporter).
- [done] 3. `opc/base.py` — client/browser protocols, `ReadResult`,
`OpcError` with decoded HRESULT.
- [done] 4. `opc/automation.py` — connect / browse / read (per-item
`Item.Read`; `SyncRead` is unmarshallable from pywin32 — verified), per-tag
errors, safe COM teardown.
- [skip] Single persistent group + static items (poller will create its own
per client; the throwaway-group read pattern is verified).
- [todo] 5. `monitor/state.py` — connection state machine, exponential
backoff (1 s → 60 s cap), reconnect accounting (only the enum exists).
- [todo] 6. `monitor/detectors.py` — D1–D15 pure detectors + rolling window
(only the `PollOutcome` dataclass exists).
- [todo] **D16 device-link detector** (`ok_values` semantics, edge-level,
`DEVICE_LINK_DOWN`/`DEVICE_LINK_UP`).
- [todo] 7. `monitor/poller.py` — the COM thread poll loop + hang watchdog
(abandon & rebuild past `hard_hang_s`).
- [todo] Startup tag validation: probe every configured tag (incl. device
indicators) before entering the loop; fail fast on invalid ItemIDs.
- [open] Poll source default (CACHE vs DEVICE) — needs a spec decision:
Matrikon shows BAD qualities on some tags only via CACHE.
- [todo] 8. `run` verb — foreground monitoring loop, event pipeline to
reporters, heartbeat (D15).
- [done] 9. `opc/fake.py` — scriptable fake client (reads already scriptable
via `read_results`; hang/error sequences land with the poller).
## Phase 2 — Reporters
- [todo] 1. `report/jsonl.py` — rotating JSONL (size + backups), config-digest
header, atomic-ish append.
- [todo] 2. `report/textlog.py` — **plain-text anomaly log** (spec §9.1):
`anomalies-YYYYMMDD.log`, one line per detected anomaly, clear-text status
`NAME (0xNN)`, recoveries logged so it reads as an incident narrative.
- [todo] 3. `report/eventlog.py` — Windows Event Log (WARN+), source
registration via `install`.
- [todo] 4. `report/http.py` — optional webhook, bounded queue, never blocks
polling.
- [todo] 5. Heartbeat assembly (D15) + reporter fan-out dispatcher.
## Phase 3 — Windows service
- [todo] 1. `service.py` — `ServiceFramework` host; clean stop
(`STOP_PENDING`, join-with-timeout, abandon hung COM thread).
- [todo] 2. CLI verbs `install`/`uninstall`/`start`/`stop` (sc.exe semantics).
- [todo] 3. SCM recovery (`sc failure ... restart`), auto-start, service
account docs.
- [todo] 4. `%ProgramData%\OPCDA-Diagnozer\` config/log dir conventions
(frozen exe already defaults there).
- [open] Frozen-exe SCM hosting: console exe must expose `ServiceMain` — one
careful build pass (pywin32 `ServiceControlDispatcher`).
## Phase 4 — Integration & chaos testing
- [todo] Scenario matrix vs the sim server: server kill / service stop /
firewall silent drop / latency injection / device comm failure / quality
oscillation / stale+frozen / mass bad / config errors / **D16: flip a device
indicator (e.g. a `P500x.Connected` alias)**.
- [todo] 24 h soak (AC10): no leaks, reconnect accounting correct, heartbeats
every 5 min, ≤ ~50 MB RAM, ~0 % CPU idle.
## Phase 5 — Packaging & docs
- [done] PyInstaller exe build (`scripts/build_exe.ps1`, onedir + onefile,
read canary) — live-verified.
- [done] README (install, config, DCOM checklist, HRESULT table).
- [todo] Ops runbook: reading events, kind meanings (spec §12 matrix),
threshold tuning, evidence locations.
- [todo] 1.0.0 tag once AC1–AC10 pass.
## Housekeeping
- [todo] Wire `status_text` into `read`/`probe` output once it exists
(currently shows `raw_hex` + decoded name separately).
- [todo] Record the CACHE-vs-DEVICE poll-source decision in the spec once
made; add fake-client scripted reads for both sources to the poller tests.