Files
opcda-diagnozer/TODO.md
T
2026-10-08 07:57:21 +02:00

107 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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]**
- [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) +
`status_text(raw) -> "NAME / SUBCODE (0xNN)"` clear-text helper.
- [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 creates its own
per client; the throwaway-group read pattern is verified).
- [done] 5. `monitor/state.py` — connection state machine, exponential
backoff (1 s → 60 s cap, reset on success), reconnect accounting, D3
flapping window (pure, clock-fed).
- [done] 6. `monitor/detectors.py` — D4–D16 pure detectors + rolling window:
latency spike/streak/p95 (D4–D6), quality transitions/persistence/flapping
(D8–D11), stale/frozen (D12–D13), mass-bad latch (D14), **D16
device-link** (`ok_values` edge-level DOWN/UP).
- [done] 7. `monitor/poller.py` — coordinator + dedicated COM worker thread
(client born on the worker), D7 soft-hang flag, hard-hang watchdog
(abandon + rebuild), backoff between sessions.
- [open] Poll source default (CACHE vs DEVICE) — needs a spec decision:
Matrikon shows BAD qualities on some tags only via CACHE.
- [done] 8. `run` verb — foreground monitoring loop, console reporter with
clear-text status, heartbeat (D15), Ctrl+C stop (second Ctrl+C aborts).
- [done] 9. `opc/fake.py` — scenario engine: scripted polls (exact results /
`"good"` shorthand) and `PollScriptError` whole-call failures; hang clients
are plain sleeping fakes in tests.
- [open] D12 stale events re-emit every poll while a tag stays stale — the
spec defines the condition per-poll; ops may want debounce/latch (decide
before the 24 h soak; live sim output showed the spam clearly).
## Phase 2 — Reporters **[done]**
- [done] 1. `report/jsonl.py` — rotating JSONL (size + backups), config-digest
header line, single-line atomic-ish appends. Live-verified.
- [done] 2. `report/textlog.py` — plain-text anomaly log (spec §9.1):
`anomalies-YYYYMMDD.log`, one line per detected anomaly, clear-text status
`NAME / SUB (0xNN)`, recoveries logged (incident narrative), per-day files,
shared rotation. Live-verified.
- [done] 3. `report/eventlog.py` — Windows Event Log (WARN+), severity-mapped
via a RegisterEventSource handle + positional ReportEvent (keyword args
raise TypeError — found live). Source message-file registration lands with
`install` (Phase 3). Live-verified delivery.
- [done] 4. `report/http.py` — optional webhook (`report.http` config),
bounded queue + daemon sender, never blocks the poll loop; failures counted
and dropped, injectable `post` for tests.
- [done] 5. Heartbeat assembly (D15, Phase 1) + reporter fan-out dispatcher
(`report/base.py`): per-sink failure isolation, best-effort close. The
`run` verb wires console + JSONL + textlog + eventlog (+http when set).
## 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.