mbproxy/docs: retire superseded design/plan docs and dissolve DL260/
The standalone design.md, kpi.md, operations.md, and the docs/plan/ phase tree were point-in-time planning artefacts now superseded by the topic-organized docs/ tree (Architecture/, Features/, Operations/, Reference/, Testing/). The DL260/ folder mixed a device-reference doc, a test fixture, a sample test, and a screenshot; its contents now live in their natural homes (dl205.md + mbtcp_settings.JPG under docs/Reference/, dl205.json next to its launcher in tests/sim/, sample test dropped). All cross-references in the surviving docs, README, CLAUDE.md, the config template, and source comments are repointed to the new locations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+19
-18
@@ -21,7 +21,7 @@ The integration win is that upstream consumers (Wonderware / Historian / OPC UA
|
||||
|
||||
## Architecture
|
||||
|
||||
The full design plan is in **[`docs/design.md`](docs/design.md)** — settled 2026-05-13, updated for Phase 9 multiplexing on 2026-05-14. Headline choices the agent should keep in mind without opening that file:
|
||||
The full architecture is documented under **[`docs/`](docs/)** — see the `Architecture/`, `Features/`, `Operations/`, `Reference/`, and `Testing/` pages. Headline choices the agent should keep in mind without opening those files:
|
||||
|
||||
- **One `TcpListener` per PLC** (54 distinct ports). Each PLC has **one shared backend socket** owned by a `PlcMultiplexer`; many upstream clients are multiplexed onto that single backend via MBAP TxId rewriting (Phase 9). The H2-ECOM100's 4-client cap no longer caps upstream connections.
|
||||
- **Transparent by default; opt-in cached** (Phase 11). Every byte passes through unchanged except the MBAP TxId field (rewritten by the multiplexer on each request and restored on each response) and FC03/FC04 response payloads + FC06/FC16 request payloads at configured BCD addresses (re-encoded between BCD nibbles and binary integers). With Phase 11, FC03/FC04 reads for tags whose `CacheTtlMs > 0` may be served from a per-PLC in-process cache without backend traffic; the cache is **OFF by default** per tag.
|
||||
@@ -33,11 +33,11 @@ The full design plan is in **[`docs/design.md`](docs/design.md)** — settled 20
|
||||
- **Backend disconnect cascades upstream**: when the shared backend socket dies, every attached upstream pipe is closed in the same cycle (counter `BackendDisconnectCascades`); clients reconnect on their next request.
|
||||
- **Read-only Kestrel admin port** (default 8080) exposes `GET /` (auto-refreshing HTML) and `GET /status.json` with service-wide and per-PLC counters (including Phase-9 mux fields, Phase-10 coalescing fields, and Phase-11 cache fields `cacheHitCount`, `cacheMissCount`, `cacheInvalidations`, `cacheEntryCount`, `cacheBytes`).
|
||||
|
||||
Anything beyond this short list — JSON schema, propagation table, stable log event names, status counter catalog, test plan — lives in `docs/design.md`. Open that doc before writing code; keep it in sync when decisions change.
|
||||
Anything beyond this short list lives in the `docs/` tree: the appsettings.json schema in [`docs/Operations/Configuration.md`](docs/Operations/Configuration.md), config propagation in [`docs/Features/HotReload.md`](docs/Features/HotReload.md), stable log event names in [`docs/Reference/LogEvents.md`](docs/Reference/LogEvents.md), the status counter catalog in [`docs/Operations/StatusPage.md`](docs/Operations/StatusPage.md), and the simulator-backed test fixture in [`docs/Testing/Simulator.md`](docs/Testing/Simulator.md). Open the relevant page before writing code; keep it in sync when decisions change.
|
||||
|
||||
## Current state
|
||||
|
||||
**Implementation complete through Phase 11.** Phases 00–08 shipped the production-ready 1:1-model service; Phase 9 swapped the connection layer for the TxId-multiplexed model; Phase 10 added in-flight read coalescing on top; Phase 11 added an opt-in per-tag response cache (bounded staleness, OFF by default — see "Response cache" in `docs/design.md`). The service is production-ready as a Windows Service:
|
||||
**Implementation complete through Phase 11.** Phases 00–08 shipped the production-ready 1:1-model service; Phase 9 swapped the connection layer for the TxId-multiplexed model; Phase 10 added in-flight read coalescing on top; Phase 11 added an opt-in per-tag response cache (bounded staleness, OFF by default — see [`docs/Architecture/ResponseCache.md`](docs/Architecture/ResponseCache.md)). The service is production-ready as a Windows Service:
|
||||
|
||||
- Test count grew through Phase 11 (see `tests/Mbproxy.Tests/` for the current suite; previous baseline was 325 = 282 unit + 43 E2E).
|
||||
- Single-file self-contained publish (`dotnet publish -c Release -r win-x64`).
|
||||
@@ -49,42 +49,43 @@ Anything beyond this short list — JSON schema, propagation table, stable log e
|
||||
- Phase 9 per-request watchdog defends against any backend that drops or mis-echoes a response (real-world packet loss; pymodbus 3.13 simulator's concurrent-multiplexed-request bug).
|
||||
- `AssemblyInformationalVersion` set to `1.0.0` (CI can override via `/p:InformationalVersion=...`).
|
||||
|
||||
The human-facing entry point is **[`README.md`](README.md)**. All design decisions remain in [`docs/design.md`](docs/design.md).
|
||||
The human-facing entry point is **[`README.md`](README.md)**. All design decisions live in the [`docs/`](docs/) tree.
|
||||
|
||||
Constraints that still apply to this codebase (do not change without updating the design doc):
|
||||
Constraints that still apply to this codebase (do not change without updating the relevant `docs/` page):
|
||||
- The csproj targets **.NET 10** (`net10.0`). This is the **only** tool in `wwtools/` not pinned to .NET Framework 4.8 / x86.
|
||||
- The sample test `DL260/DL205BcdQuirkTests.cs` is a pattern reference only — its types are not available in this project.
|
||||
|
||||
## Device quirks (read before writing Modbus code)
|
||||
|
||||
The DL205/DL260 family is *almost* Modbus-spec-compliant, but every category below has at least one trap. The authoritative reference is **[`DL260/dl205.md`](DL260/dl205.md)** — read it end-to-end before touching the wire protocol. Highlights that bear directly on this proxy:
|
||||
The DL205/DL260 family is *almost* Modbus-spec-compliant, but every category below has at least one trap. The authoritative reference is **[`docs/Reference/dl205.md`](docs/Reference/dl205.md)** — read it end-to-end before touching the wire protocol. Highlights that bear directly on this proxy:
|
||||
|
||||
- **BCD-by-default numeric encoding.** `V2000 = 1234` stores `0x1234` on the wire, not `0x04D2`. This is the entire reason this service exists.
|
||||
- **CDAB word order for 32-bit values.** Low word first, big-endian bytes within each word. `0xAABBCCDD` lands as `[0xCC 0xDD][0xAA 0xBB]`.
|
||||
- **Octal V-memory ↔ decimal Modbus translation.** `V2000` octal = decimal 1024 = Modbus PDU `0x0400`. Config addresses are PDU-decimal, **not** octal V-memory and **not** 1-based 4xxxx.
|
||||
- **FC03/FC04 max qty = 128** (above spec's 125). **FC16 max qty = 100** (below spec's 123). The proxy passes these through; the PLC enforces the cap with exception 03.
|
||||
- **Max 4 concurrent TCP clients per ECOM100.** Direct constraint on this proxy's 1:1 connection model — see [`docs/design.md`](docs/design.md) → "Connection model" for the band-aid-vs-rearchitect decision tree if this becomes a real problem.
|
||||
- **Max 4 concurrent TCP clients per ECOM100.** This is why the proxy uses a single TxId-multiplexed backend socket per PLC — see [`docs/Architecture/ConnectionModel.md`](docs/Architecture/ConnectionModel.md) for how the connection model lifts this cap.
|
||||
- **No TCP keepalive from the device.** Middleboxes typically drop idle sockets at 2–5 min. With the 1:1 model, backend liveness tracks upstream client liveness; if both are idle long enough, the path dies on its own and the next request reconnects.
|
||||
- **Register 0 is valid** on DL205/DL260 in factory "absolute" addressing mode — don't probe-skip it.
|
||||
- **As-deployed PLC parameters** (captured in `DL260/mbtcp_settings.JPG`): port 502, "Use Concept data structures (Longs/Reals)" enabled, "Swap bytes" enabled, "Use Zero Based Addressing" **unchecked**, Register type = Binary, max coil read 1976 / coil write 800 / register read 122 / register write 100. The proxy must speak Modbus as-is; these settings describe the wire it'll see.
|
||||
- **As-deployed PLC parameters** (captured in `docs/Reference/mbtcp_settings.JPG`): port 502, "Use Concept data structures (Longs/Reals)" enabled, "Swap bytes" enabled, "Use Zero Based Addressing" **unchecked**, Register type = Binary, max coil read 1976 / coil write 800 / register read 122 / register write 100. The proxy must speak Modbus as-is; these settings describe the wire it'll see.
|
||||
|
||||
## Resource index
|
||||
|
||||
| Task | Go to |
|
||||
| --- | --- |
|
||||
| Full architecture / design plan (decisions, schema, log events, status counters, test plan) | [`docs/design.md`](docs/design.md) |
|
||||
| Phase-by-phase implementation plan (parallel-safety, phase gates, per-phase test list) | [`docs/plan/README.md`](docs/plan/README.md) |
|
||||
| Dashboard KPI catalogue — what's exposed today and proposed additions (rates, percentiles, availability, fleet aggregates) | [`docs/kpi.md`](docs/kpi.md) |
|
||||
| DL205/DL260 Modbus quirks (BCD, CDAB, octal V-memory, FC limits, exception codes, oddities) | [`DL260/dl205.md`](DL260/dl205.md) |
|
||||
| pymodbus simulator profile that models those quirks as concrete register values | [`DL260/dl205.json`](DL260/dl205.json) |
|
||||
| Example integration test pattern (xUnit + Shouldly + simulator fixture) | [`DL260/DL205BcdQuirkTests.cs`](DL260/DL205BcdQuirkTests.cs) |
|
||||
| As-deployed PLC Modbus parameters screenshot | [`DL260/mbtcp_settings.JPG`](DL260/mbtcp_settings.JPG) |
|
||||
| Architecture — listener topology, request flow, per-PLC isolation | [`docs/Architecture/Overview.md`](docs/Architecture/Overview.md) |
|
||||
| Connection model — single backend socket per PLC, TxId multiplexing, request-timeout watchdog, disconnect cascade | [`docs/Architecture/ConnectionModel.md`](docs/Architecture/ConnectionModel.md) |
|
||||
| In-flight read coalescing / opt-in response cache | [`docs/Architecture/ReadCoalescing.md`](docs/Architecture/ReadCoalescing.md), [`docs/Architecture/ResponseCache.md`](docs/Architecture/ResponseCache.md) |
|
||||
| BCD rewriting (codec, CDAB word order, FC03/04/06/16 scope) and config hot-reload | [`docs/Features/BcdRewriting.md`](docs/Features/BcdRewriting.md), [`docs/Features/HotReload.md`](docs/Features/HotReload.md) |
|
||||
| Operations — full appsettings.json reference, status page / JSON fields, troubleshooting playbook | [`docs/Operations/Configuration.md`](docs/Operations/Configuration.md), [`docs/Operations/StatusPage.md`](docs/Operations/StatusPage.md), [`docs/Operations/Troubleshooting.md`](docs/Operations/Troubleshooting.md) |
|
||||
| Stable `mbproxy.*` log event-name catalog | [`docs/Reference/LogEvents.md`](docs/Reference/LogEvents.md) |
|
||||
| DL205/DL260 Modbus quirks (BCD, CDAB, octal V-memory, FC limits, exception codes, oddities) | [`docs/Reference/dl205.md`](docs/Reference/dl205.md) |
|
||||
| pymodbus simulator profile that models those quirks as concrete register values | [`tests/sim/dl205.json`](tests/sim/dl205.json) |
|
||||
| As-deployed PLC Modbus parameters screenshot | [`docs/Reference/mbtcp_settings.JPG`](docs/Reference/mbtcp_settings.JPG) |
|
||||
|
||||
## Maintenance
|
||||
|
||||
Documentation doctrine for `wwtools/` lives in [`../DOCS-GUIDE.md`](../DOCS-GUIDE.md). The three-layer rules apply:
|
||||
|
||||
- **[`README.md`](README.md)** is the canonical human entry point (Layer-2 per DOCS-GUIDE). It routes to deep docs; it does not duplicate them. Update it when the service's public surface or install steps change.
|
||||
- This `CLAUDE.md` stays a router for LLM coding agents. Deep design decisions live in [`docs/design.md`](docs/design.md); device quirks live in [`DL260/dl205.md`](DL260/dl205.md). When you change a design decision, update `docs/design.md` first (it's the source of truth) and only mirror the change into the Architecture summary above if it shifts one of the headline bullets.
|
||||
- This `CLAUDE.md` stays a router for LLM coding agents. Deep design decisions live in the [`docs/`](docs/) tree; device quirks live in [`docs/Reference/dl205.md`](docs/Reference/dl205.md). When you change a design decision, update the relevant page under `docs/` first (it's the source of truth) and only mirror the change into the Architecture summary above if it shifts one of the headline bullets.
|
||||
- When the service's task→tool mapping changes in the root index, update [`../CLAUDE.md`](../CLAUDE.md) too.
|
||||
- Any further work beyond Phase 08 belongs in a new design revision (dated, in `docs/design.md`) and a new phase plan.
|
||||
- Any further design changes belong in the relevant `docs/` page (`Architecture/`, `Features/`, `Operations/`, `Reference/`, or `Testing/`).
|
||||
|
||||
Reference in New Issue
Block a user