diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ee0f8ce --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: Application CI + +on: + pull_request: + branches: + - master + push: + branches: + - master + +permissions: + contents: read + +concurrency: + group: dnp-tester-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: windows-2022 + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET 8 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Restore application + run: dotnet restore Dnp3MasterTester/Dnp3MasterTester.csproj + + - name: Build application + run: dotnet build Dnp3MasterTester/Dnp3MasterTester.csproj -c Debug --no-restore -p:ContinuousIntegrationBuild=true diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7a4ccc3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,112 @@ +# AGENTS.md — DNP3 Interoperability Tester Engineering Contract + +This is the repository-level operating contract for AI/code agents and maintainers. The application-specific rules in `Dnp3MasterTester/AGENTS.md` are also authoritative for files under that directory and may impose stricter requirements. + +DNPTester is commissioning/test software. Protocol correctness, evidence integrity, command safety, failure containment, responsiveness, and reproducible release behavior are product requirements. + +## Prime directive + +Do not begin with a disposable, intentionally naive, or demo-only implementation when the production architecture is knowable. Prefer the smallest coherent production-quality change that preserves existing evidence and operator workflows. + +Priority order: +1. wire/protocol correctness and command safety; +2. evidence/timestamp/data integrity; +3. failure containment and recoverability; +4. regression compatibility; +5. responsive bounded runtime behavior; +6. maintainability; +7. convenience. + +## Mandatory workflow + +For non-trivial changes: + +RECONNAISSANCE -> REPRODUCE/BASELINE -> ROOT CAUSE -> INVARIANTS -> ARCHITECTURE IMPACT -> IMPLEMENT -> REGRESSION TEST -> FAILURE TEST -> PERFORMANCE/LIFECYCLE CHECK -> BUILD/CI -> DEVICE/SIMULATOR VALIDATION AS APPLICABLE. + +Before editing, locate the authoritative protocol/master state, point cache, command lifecycle, timestamp source, evidence/report pipeline, and UI consumer. Do not create parallel truth merely to make a screen update. + +If three successive patches in the same subsystem still treat symptoms, STOP. Patch four requires a fresh root-cause/state-ownership/architecture audit. + +## Repository boundaries + +- The native C# DNP3 master stack is the authoritative active protocol engine. +- UI/reporting/installer/docs must not invent protocol state or device evidence. +- `Value Viewer` represents latest-known point state, not an append-only event dump. +- SCADA Events/SOE preserve source timestamp quality and provenance. +- Command Testing must preserve explicit prepared/requested/accepted/feedback/final-verdict lifecycle semantics. +- Report Preview and export must use the same authoritative evidence model/render path. +- Installer/release changes must preserve portable user operation and legal/third-party notices. + +## Result-oriented failure architecture + +Expected or recoverable outcomes must not use exceptions as normal control flow in protocol decode, poll/task processing, point normalization, command lifecycle handling, serial/TCP loops, or high-frequency evidence processing. + +Prefer `TryXxx`, one coherent typed Result/status model per subsystem, compact enums/records, or nullable results only when failure detail is unnecessary. + +Normal runtime conditions such as timeout, disconnect, malformed/truncated response, unsupported object/variation, invalid qualifier, missing source timestamp, command rejection, feedback timeout, queue saturation, or unavailable optional metadata must produce explicit status/evidence rather than repeated throw/catch behavior. + +Exceptions from sockets, serial drivers, filesystem, PDF/reporting, WPF, or other infrastructure may still occur. Catch them at the nearest meaningful boundary and convert them into structured application failure/diagnostic state. Do not silently swallow them and do not scatter broad catch-all blocks through inner protocol loops. + +## Bounded asynchronous diagnostics + +High-rate protocol/transport paths must not perform expensive string formatting, synchronous file logging, stack-trace generation, JSON serialization, or one UI update per event. + +Emit compact structured diagnostic events/counters into a bounded non-blocking pipeline. Aggregate/deduplicate/rate-limit repeated failures and retain occurrence/drop counters. Queue saturation must have an explicit policy. + +Formatting, persistence, and UI presentation belong on lower-rate/background consumers. Diagnostic failure must never block transport processing, polling, commands, shutdown, or UI responsiveness. + +## Zero UI blocking and backpressure + +Do not perform blocking serial/TCP I/O, large report generation, heavy parsing, file I/O, or long protocol operations on the WPF UI thread. + +Polling/event acquisition rate and rendering rate are independent. Batch/coalesce display updates and virtualize/bound large evidence grids. Do not create unbounded ObservableCollections or one render per DNP3 frame/object. + +## Transactional state changes + +Connection settings, mapping/profile state, command candidates, report configuration, and other multi-field state changes must be validated before activation where practical. + +Use candidate -> validate -> commit. On failure retain last-known-good state or transition explicitly to a safe stopped/faulted state. Never leave partially applied command/session/mapping state. + +## Command safety + +Do not infer command success from transport send alone. Preserve command mode/operate response, protocol status, feedback matching, timeout, and final verdict as separate evidence. + +Never auto-repeat an operate solely because a timeout/exception occurred unless an explicitly approved bounded retry policy proves the operation is safe and idempotent for that workflow. + +## Large data and evidence + +Keep live point state bounded and keyed. Evidence retention must have explicit bounds/export policy. Reports must not fabricate missing evidence or convert `not executed` into `failed`. + +Raw protocol evidence remains available for forensic use, but operator-facing views should remain meaning-first. + +## Performance/resource discipline + +For relevant changes measure where practical: +- poll/event throughput; +- UI update latency; +- queue high-water marks/drop counts; +- allocation rate and memory growth; +- reconnect/stop latency; +- report generation time; +- long-session resource stability. + +Do not add workers, caches, queues, pools, or retries without a demonstrated need and explicit lifecycle owner. + +## Release discipline + +A user-facing release must be reproducible and must preserve version, installer/package identity, license/notices, and checksum/provenance expectations. Do not publish from an unvalidated working tree or replace a requested binary artifact with an unrelated fallback. + +## Definition of done + +A change is not complete because it compiles. Validate as applicable: +BUILD + STATIC ANALYSIS + UNIT/DETERMINISTIC TESTS + REGRESSION TEST + MALFORMED/FAILURE PATH + TRANSPORT/STOP LIFECYCLE + PERFORMANCE CHECK + REPORT/PACKAGE CHECK + SIMULATOR/AUTHORIZED DEVICE VALIDATION. + +Never claim a validation step was run when it was not. + +## Completion report + +Report: changed, demonstrated root cause, architecture/state owner, invariants preserved, Result/failure contract, regression protection, performance/lifecycle impact, exact validation, and genuine remaining limitations. + +## Final rule + +Treat every connection, point, timestamp, command, and report row as engineering evidence. Fix root causes, keep runtime work bounded, make failures explicit, and never let UI convenience redefine protocol truth. diff --git a/Dnp3MasterTester/AGENTS.md b/Dnp3MasterTester/AGENTS.md index b26b420..680fa32 100644 --- a/Dnp3MasterTester/AGENTS.md +++ b/Dnp3MasterTester/AGENTS.md @@ -1,79 +1,126 @@ -# AGENTS.md - -## Purpose -This repository is a lightweight WPF `net8.0-windows` DNP3 Master Tester for FAT, troubleshooting, and operator-facing diagnostics. - -The product priority is: -1. Protocol correctness on the wire -2. Stable operator workflow -3. Clear observability for SCADA/event analysis -4. UI polish - -Do not trade protocol correctness for architecture experiments or decorative UI changes. - -## Hard rules for Codex -- Active DNP3 master communication must go through this repository's native C# master stack. -- Do not add proprietary DNP3 protocol packages back to `Dnp3MasterTester` unless the user explicitly reverses the licensing direction. -- Analyzer/UI code must remain passive: - - it may receive callbacks - - normalize values - - build operator logs - - build SOE audit trails - - build link trace views -- Analyzer/UI code must not: - - fabricate unsolicited/event behavior not emitted by the native master stack - - guess object values when decoding fails - - silently treat malformed frames as valid data -- Prefer decoded protocol data and explicit unknown/error fields first. -- If engine data is incomplete, prefer `Unknown` or an empty field over guessing. - -## Working rules for Codex -- Follow IEEE 1815/DNP-UG protocol behavior and the native stack boundaries first. -- Keep the application operator-facing: - - `Value Viewer` is the latest point state view - - `Event Logs SCADA` is operational journaling - - `SOE Event Audit` is event/timestamp evidence - - `Link Trace` is protocol/runtime diagnostics -- Do not reintroduce IEC-101-specific assumptions, naming, or lib60870 code paths into the active DNP3 path. -- Do not add redundancy-link features unless explicitly requested. -- Do not add link timeline monitor features unless explicitly requested. -- Keep transport support practical: - - `TCP` and `Serial` are the current primary transports - - add `TLS` only if requested or if the task clearly requires it - -## Current protocol principles -- `Integrity Poll` is operator-triggered or startup-policy-driven, not spammed continuously. -- `Event Poll` is for background event retrieval. -- `Link Status` should come from official engine operations, not inferred UI state. -- `SOE` should prefer engine timestamps/qualities when available. -- `Value Viewer` should be last-known state per point key (`PointType + Index`), not a raw event stream dump. -- `Event Logs SCADA` should stay readable for operators and testers, not become a full debug console. -- `Link Trace` may include decoded runtime/protocol log lines, but it should remain clearly labeled as trace data. - -## Important files -- `C:\Git\DNPTester\Dnp3MasterTester\Services\Dnp3MasterService.cs` -- `C:\Git\DNPTester\Dnp3MasterTester\Services\IDnp3MasterService.cs` -- `C:\Git\DNPTester\Dnp3MasterTester\ViewModels\MainViewModel.cs` -- `C:\Git\DNPTester\Dnp3MasterTester\Models\ConnectionSettings.cs` -- `C:\Git\DNPTester\Dnp3MasterTester\MainWindow.xaml` -- `C:\Git\DNPTester\Dnp3MasterTester\MainWindow.xaml.cs` -- `C:\Git\DNPTester\Dnp3MasterTester\Protocol` - -## Protocol reference posture -Use IEEE 1815/DNP-UG materials as the source of truth for protocol behavior. -Permissive public stacks such as OpenDNP3 may inform architecture, but do not -copy proprietary DNP3 implementation code into this repository. - -## Build command -Use: -`dotnet build C:\Git\DNPTester\Dnp3MasterTester\Dnp3MasterTester.csproj` - -## Documentation baseline -Before making large changes, inspect: -- `AGENTS.md` -- `PROJECT_CONTEXT.md` - -If protocol/architecture intent is unclear: -- preserve the current event flow -- preserve operator-focused outputs -- prefer minimal invasive changes +# AGENTS.md — DNP3 Master Tester Application Contract + +The repository root `AGENTS.md` is authoritative. This file adds stricter application rules for `Dnp3MasterTester/**`. + +## Purpose and priority + +Dnp3MasterTester is a WPF `net8.0-windows` DNP3 master tester for FAT, commissioning, troubleshooting, command verification, and operator-facing evidence. + +Priority: +1. protocol correctness on the wire; +2. command and evidence integrity; +3. stable operator workflow and recoverability; +4. clear SCADA/SOE observability; +5. bounded performance; +6. UI polish. + +Do not trade protocol truth for architecture experiments, screenshots, or decorative UI changes. + +## Authoritative boundaries + +- Active DNP3 communication goes through this repository's native C# master stack. +- Do not add proprietary DNP3 packages unless the user explicitly reverses that direction. +- Analyzer/UI/reporting code may consume callbacks, normalize decoded values, build operator evidence, SOE, traces, and reports. +- Analyzer/UI/reporting code must not fabricate events/unsolicited behavior, guess values when decoding fails, or silently promote malformed data to valid state. +- Prefer explicit `Unknown`, unavailable, invalid-quality, or typed failure state over invented values. +- Do not reintroduce IEC-101/lib60870 assumptions into the active DNP3 path. + +## Root-cause-first work + +Before changing protocol/runtime behavior identify: +- transport/session owner; +- request/task owner; +- parser/object decoder; +- authoritative point cache; +- event/SOE timestamp provenance; +- command lifecycle owner; +- UI/report consumers. + +Use REPRODUCE -> TRACE -> ROOT CAUSE -> FIX -> REGRESSION TEST. If three symptom patches fail in the same subsystem, stop before patch four and re-audit ownership and architecture. + +## Protocol principles + +- Integrity Poll is operator/startup-policy driven, not spammed continuously. +- Event Poll is background event retrieval. +- Link Status comes from engine operations/evidence, not inferred UI state. +- SOE prefers source/IED timestamps and quality when available. +- Value Viewer is last-known state keyed by `PointType + Index`, not a raw event stream. +- SCADA Events remains operator-readable; Link Trace remains explicitly forensic/diagnostic. +- TCP and Serial are current primary transports. TLS/redundancy/link-timeline expansion requires explicit product scope. + +## Exception-free protocol hot paths + +Expected/recoverable failures must not use exceptions as normal control flow inside frame/object decoding, poll/event processing, point normalization, command state advancement, transport receive loops, or high-frequency evidence handling. + +Prefer `TryXxx`, a coherent typed Result/status record, compact enum/status, or nullable output only where failure detail is unnecessary. + +Expected conditions include timeout/no data, malformed/truncated frame, unsupported group/variation, invalid qualifier/count, CRC/length rejection, missing timestamp, disconnect, DFC/busy/rejection, command feedback timeout, and bounded queue saturation. + +Exceptions from `Socket`, serial drivers, filesystem, WPF, PDF/report/export or other infrastructure may still happen. Catch at the nearest meaningful adapter/application boundary and convert to structured status/diagnostics. Never use broad catch/retry loops as a substitute for explicit state transitions. + +## Defensive parsing + +Before indexing based on received metadata validate all link/transport/application lengths, offsets, counts, qualifier/range semantics, object sizes, numeric conversions, and timestamp bounds. + +Malformed traffic must not crash the master session or UI and must not partially mutate authoritative point/event state. Preserve raw evidence sufficient for diagnosis. + +## Bounded diagnostics + +Transport/protocol hot paths may emit only compact structured diagnostic events/counters. No per-frame file logging, JSON serialization, stack-trace formatting, or synchronous WPF notification. + +Diagnostic queues must be bounded and non-blocking for high-rate producers. Aggregate/deduplicate/rate-limit repeated failures. Preserve occurrence/drop counts and severity according to explicit overload policy. + +Formatting and persistence belong to a background/lower-rate consumer. Diagnostic failure must never stall polling, commands, reconnect, Stop, or rendering. + +## Point/event state ownership + +Build decoded updates as validated candidates. Commit to Value Viewer/event/SOE state only after the complete object and provenance are valid enough for the intended view. + +Do not let a partial parse overwrite last-known-good point state. If quality/timestamp is invalid, represent that explicitly rather than silently reusing an unrelated timestamp/value. + +Value Viewer order/state should remain stable during live updates; do not rebuild or reorder the whole collection for each callback. + +## Command lifecycle safety + +A command is not successful merely because bytes were sent or a request received a transport response. + +Preserve distinct evidence for prepared -> requested -> protocol accepted/rejected -> feedback observed/not observed -> final verdict. + +Do not retry an operate automatically after ambiguous timeout unless the workflow has an explicitly reviewed bounded/idempotent retry contract. Unknown command outcome is safer than a duplicate operate. + +## WPF responsiveness + +No blocking TCP/serial I/O, heavy protocol parsing, report generation, file I/O, or long synchronization waits on the UI thread. + +Batch/coalesce point, event, trace, and diagnostics updates. Use virtualization/recycling for large grids and bound retained live evidence. Never create one Dispatcher invocation/render per incoming DNP object/frame under sustained traffic. + +UI status must reflect authoritative engine state rather than requested button state. + +## Stop/reconnect lifecycle + +Workers, sockets/serial ports, timers, subscriptions, cancellation sources, and callbacks need explicit owners. Stop must prevent new work, cancel/close transport as required to release blocking I/O, retire workers, then restore a coherent operator state. + +Do not detach workers or use arbitrary sleeps to hide shutdown races. Reconnect loops must be bounded/cancellable and must not create overlapping sessions. + +## Reporting integrity + +Report preview/export consume the same authoritative evidence model. Distinguish not-executed, passed, warning, and failed. Do not fabricate missing evidence to make a report look complete. + +Source timestamp vs captured-time fallback must remain visible where relevant. Raw trace evidence can support forensic review but should not replace operator meaning. + +## Performance evidence + +For hot-path/runtime changes measure where applicable: sustained poll/event throughput, allocations, queue depth/drop count, memory growth, UI latency, reconnect/Stop latency, and report generation time. Do not claim performance improvement without before/after evidence. + +## Build and validation + +Build from repository-relative paths; do not hardcode `C:\Git\...` assumptions in production scripts or new documentation. + +A protocol bug fix should add a deterministic regression fixture when practical, including the exact malformed frame, timeout/state transition, qualifier/variation, timestamp, command, or lifecycle failure. + +Definition of done as applicable: +BUILD + REGRESSION TEST + MALFORMED/FAILURE TEST + TRANSPORT LIFECYCLE + UI BATCHING/RESPONSIVENESS + REPORT EVIDENCE + AUTHORIZED DEVICE/SIMULATOR VALIDATION. + +## Final rule + +The native master engine owns protocol truth. UI and reports explain that truth; they never repair or invent it. Make expected failures explicit, keep queues/work bounded, and treat ambiguous command outcomes conservatively.