Files
2026-10-08 06:30:54 +02:00

90 lines
5.0 KiB
Markdown
Raw Permalink 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.
# Changelog
All notable changes to the OPC DA Diagnozer are documented here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
bump per [SemVer](https://semver.org/). Dates are YYYY-MM-DD.
## [Unreleased]
### 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 `EventKind`s), 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.