🚧 Implement service stub

This commit is contained in:
Jonas Bengtsson
2026-10-08 11:01:03 +02:00
parent ca0bb0d048
commit e5675ec559
6 changed files with 936 additions and 51 deletions
+64 -12
View File
@@ -11,9 +11,10 @@ and flapping quality — as structured events (JSONL logs, Windows Event Log, op
HTTP webhook).
> **Status:** in active development — discovery, browsing (`list-items`), one-shot
> reads (`read`/`probe`) and the exe build are **live and field-verified** against a
> real server; the continuous monitoring loop, reporters and Windows service host are
> next. See the [TODO](TODO.md) for current status and the
> reads (`read`/`probe`), the continuous monitoring loop (`run`), all reporters,
> the exe build and the Windows service host are **live and field-verified**
> (service: in-process debug host verified; the elevated SCM pass is the remaining
> live check). See the [TODO](TODO.md) for current status and the
> [implementation plan](docs/IMPLEMENTATION_PLAN.md) for the phases; read the
> [specification](docs/SPECIFICATION.md) for detection rules and behavior.
@@ -32,9 +33,9 @@ answers the triage question *where is the problem?* with evidence:
See the full interpretation matrix in the [specification](docs/SPECIFICATION.md).
## Planned features
## Features
- Windows service (auto-start, SCM crash recovery) with a foreground `run` mode for testing
- Windows service (auto-start, SCM crash recovery) with a foreground `run` mode for testing — live
- Local or remote (DCOM) connections to any OPC DA server
- Server browsing: `list-servers` (discovery) and `list-items`
(address-space browse: full tree / branches / tags) — live now
@@ -83,8 +84,16 @@ opcda-diagnozer probe
# foreground run (console) — continuous monitoring; Ctrl+C stops
opcda-diagnozer run
# install as Windows service (dedicated account required for DCOM auth)
opcda-diagnozer install --account DOMAIN\svc-opcdiag
# install as a Windows service (auto-start + SCM crash recovery; admin shell)
# LocalSystem default; a dedicated account is required for remote DCOM auth
opcda-diagnozer install
opcda-diagnozer install --account DOMAIN\svc-opcdiag --password ****
# service lifecycle + state
opcda-diagnozer start
opcda-diagnozer stop
opcda-diagnozer status
opcda-diagnozer uninstall
```
`list-servers` uses two discovery mechanisms (`--source`): the OPC DA Automation
Wrapper's `GetOPCServers` over COM (works remotely via DCOM; needs Launch+Activation
@@ -117,6 +126,48 @@ exist (and `probe`/`read` read the config's tag list unless tags are passed).
a non-GOOD or failing tag makes the exit code non-zero, so it doubles as a
scriptable health check. `--source cache` reads the server cache instead of
forcing a device round-trip.
## Windows service
The monitor runs unattended as a **Windows service** (auto-start, SCM crash
recovery) — `opcda-diagnozer install` registers the exe itself as the service
binary (no `pythonservice.exe`, no Python install on the diag host):
- **Auto-start** at boot and **restart on failure**: the SCM restarts the
service process 5 s after the first and second failure (the failure counter
resets after 24 h of clean running) — `sc qfailure OPCDA-Diagnozer` shows it.
- **Config path** is recorded at install time (`install --config ...`); the
service never depends on its working directory (the SCM starts services
with CWD = System32). The default is `config.yaml` next to the exe.
- **Clean stop**: a stop request parks the service in `STOP_PENDING` and
gives the poll loop 30 s to finish; a COM poll stuck in a dead RPC longer
than that is abandoned (the thread is never joined — spec §10.4).
- **Event Log** records service start/stop and every configured reporter
event; `install` also registers the event source so messages render
correctly (needs the admin shell anyway).
- **Status** works without admin: `opcda-diagnozer status` prints the SCM
state (`STOPPED` / `RUNNING` / …).
Service **account** (spec §11): `LocalSystem` (the default) works for OPC
DA servers on the same machine. Remote DCOM connections need a **dedicated
domain account** that has Launch+Activation and Access permissions on the
OPC server host (`dcomcnfg` — see the checklist above) and can authenticate
across the network; install with `--account DOMAIN\USER --password ****`
(the password is only used once, to register the account with the SCM).
**Deployment layout** (frozen exe):
```
C:\Program Files\OPCDA-Diagnozer\
opcda-diagnozer.exe # the console exe: every CLI verb AND the service host
config.yaml # the deployed config (install records its path)
C:\ProgramData\OPCDA-Diagnozer\
logs\ # events.jsonl + anomalies-YYYYMMDD.log (report.dir)
```
Log directories are created on demand; `%ProgramData%` is the default because
the service account must be able to write the evidence logs.
### Configuration example (`config.example.yaml`)
```yaml
@@ -253,7 +304,7 @@ The script refuses to run from a 64-bit interpreter (the exe must load the
32-bit OPC DA wrapper DLLs, SPECIFICATION §11), re-verifies the produced PE
really is 32-bit, embeds the project version in the exe properties, declares
the hidden imports the code loads lazily (pywin32 COM, browse modules, the
Phase 3 service host), and smoke-tests the artifact (`--version`,
service host + its SCM modules), and smoke-tests the artifact (`--version`,
`check-config`, `list-servers --source registry`) after building. It also
installs PyInstaller into the venv automatically if it is missing.
@@ -266,10 +317,11 @@ Deployment notes:
`CWD = C:\Windows\System32`, so the relative default would silently point
there; the exe-dir default keeps the deployment layout simple
(`C:\Program Files\OPCDA-Diagnozer\{opcda-diagnozer.exe, config.yaml}`).
- The exe is a console application that hosts every CLI verb, including the
future `run` / `probe` / service verbs. The Phase 3 Windows service
(`install` verb) will point `sc.exe` at this same exe with the service-host
entry point.
- The exe is a console application that hosts every CLI verb **and the
Windows service**: `install` registers this same exe with the SCM, and
when the SCM starts it the process recognizes the fixed service-start
argv shape (`exe service`, CWD = System32) and dispatches to the service
host. No `pythonservice.exe`, no Python install on the diag host.
- The Automation Wrapper DLL (`OPCAutomation.dll`) is **not** bundled — it is
a machine-installed COM component (registered in the registry), exactly like
the OPC DA servers the tool connects to. Only Python code and its