Files
wwtools/mbproxy/README.md
T
Joseph Doherty 7466a46aa7 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>
2026-05-15 07:37:48 -04:00

127 lines
6.9 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.
# mbproxy
A .NET 10 Windows Service that sits inline as a Modbus TCP proxy in front of a fleet of AutomationDirect DirectLOGIC DL205/DL260 controllers, rewriting BCD-encoded registers bidirectionally so upstream clients can read and write them as plain integers. The proxy also offers an opt-in per-tag response cache (default OFF) for FC03/FC04 reads with bounded operator-configured staleness — see [`docs/Architecture/ResponseCache.md`](docs/Architecture/ResponseCache.md) before enabling it.
> ⚠ **32-bit BCD wire format is "two base-10000 digits in CDAB", not standard CDAB binary Int32.** A 32-bit BCD tag at address `A` decodes as `decimal = high * 10_000 + low` where `low` is the register at `A` and `high` is the register at `A+1`. Each word independently must be 09999. Standard Modbus clients (NModbus, FluentModbus, Wonderware DAServer) that interpret CDAB as straight binary Int32 will silently corrupt any value > 9999 on writes and read garbage on reads. Configure your client to send/receive each register as a separate base-10000 BCD digit pair, not as a single binary Int32. Full details in [`docs/Features/BcdRewriting.md`](docs/Features/BcdRewriting.md).
## Hard constraints / prerequisites
- **Windows 10 / Server 2019 or later, 64-bit.** No Linux or Docker support — the service uses `Microsoft.Extensions.Hosting.WindowsServices` and the Windows Event Log.
- **Modbus TCP backends reachable** from the proxy host on port 502 (or the port configured per PLC). The H2-ECOM100 module caps simultaneous connections at **4 per PLC** — a fifth upstream client will fail to connect.
- **Admin rights** to install the service (`install.ps1` requires elevation).
- **No COM dependency** — this is a pure .NET 10 socket-level proxy (unlike the `.NET Framework 4.8 / x86` siblings in this repo).
- **Python 3.10+** on the test machine to run the pymodbus-backed E2E simulator (not needed to run the service in production).
## Layout
```
src/Mbproxy/ Main C# project (net10.0, Microsoft.NET.Sdk.Worker)
tests/Mbproxy.Tests/ xUnit v3 test project (314 unit + 48 E2E tests)
install/ PowerShell install/uninstall scripts and config template
docs/ Architecture, features, operations, reference, and testing docs
```
## Resource index
| Task | Go to |
|---|---|
| Architecture entry point — listener topology, request flow, per-PLC isolation | [`docs/Architecture/Overview.md`](docs/Architecture/Overview.md) |
| DL205/DL260 Modbus quirks (BCD, CDAB, octal V-memory, FC limits) | [`docs/Reference/dl205.md`](docs/Reference/dl205.md) |
| pymodbus simulator profile (register seeds for E2E tests) | [`tests/sim/dl205.json`](tests/sim/dl205.json) |
| Agent-oriented coding guide (architecture bullets, device quirks, phase context) | [`CLAUDE.md`](CLAUDE.md) |
## Detailed documentation
The `docs/` tree is organized by topic. Start with [`Architecture/Overview.md`](docs/Architecture/Overview.md) for the end-to-end picture; jump to the focused pages below when you need depth on one area.
### Architecture
- [`Architecture/Overview.md`](docs/Architecture/Overview.md) — Listener topology, request flow, per-PLC isolation.
- [`Architecture/ConnectionModel.md`](docs/Architecture/ConnectionModel.md) — Single backend connection per PLC, TxId multiplexing, request-timeout watchdog, disconnect cascade.
- [`Architecture/ReadCoalescing.md`](docs/Architecture/ReadCoalescing.md) — In-flight FC03/FC04 deduplication via `InFlightByKeyMap`.
- [`Architecture/ResponseCache.md`](docs/Architecture/ResponseCache.md) — Opt-in per-tag response cache with bounded operator-configured staleness.
### Features
- [`Features/BcdRewriting.md`](docs/Features/BcdRewriting.md) — BCD codec, CDAB word order, FC03/04/06/16 scope, partial-overlap policy.
- [`Features/HotReload.md`](docs/Features/HotReload.md) — `IOptionsMonitor`-driven config reload with per-change-kind reconcile rules.
### Operations
- [`Operations/Configuration.md`](docs/Operations/Configuration.md) — Full `appsettings.json` reference: every `Mbproxy:*` key, default, and validation rule.
- [`Operations/StatusPage.md`](docs/Operations/StatusPage.md) — Admin endpoint surface (`/`, `/status.json`) with every JSON field documented.
- [`Operations/Troubleshooting.md`](docs/Operations/Troubleshooting.md) — Diagnosis playbook keyed to log events and status counters.
### Reference
- [`Reference/LogEvents.md`](docs/Reference/LogEvents.md) — Stable `mbproxy.*` event catalog (28 events across 7 categories).
### Testing
- [`Testing/Simulator.md`](docs/Testing/Simulator.md) — pymodbus DL205 fixture, skip policy, and the load-bearing pymodbus 3.13 framer quirk.
## Build and run
**Build (Debug, multi-file — fast for iteration):**
```powershell
dotnet build Mbproxy.slnx -c Debug
```
**Publish (Release, single-file, win-x64):**
```powershell
.\install\publish.ps1 -Clean
```
Produces both flavours under `publish-out\`:
| Flavour | Path | Size | Target prerequisite |
|---|---|---|---|
| Self-contained | `publish-out\self-contained\Mbproxy.exe` | ~100 MB | None — bundles .NET 10 + ASP.NET Core runtime |
| Framework-dependent | `publish-out\framework-dependent\Mbproxy.exe` | ~1.5 MB | .NET 10 + ASP.NET Core preinstalled |
Pass `-OutputDir <path>` to publish elsewhere; omit `-Clean` to skip the wipe. The script wraps `dotnet publish src/Mbproxy/Mbproxy.csproj -c Release -r win-x64 [-p:SelfContained=false]` — run those directly if you only need one flavour.
**Run tests:**
```powershell
dotnet test Mbproxy.slnx -c Debug # all tests
dotnet test Mbproxy.slnx -c Debug --filter Category=Unit # unit tests only (no Python required)
dotnet test Mbproxy.slnx -c Debug --filter Category=E2E # E2E tests (require Python + pymodbus)
```
**Run interactively (without installing as a service):**
```powershell
cd src/Mbproxy
dotnet run --configuration Debug
```
Edit `src/Mbproxy/appsettings.json` to configure PLCs before running. The admin status page will be at `http://localhost:8080/` by default.
## Install
The `install/` directory holds the publish, install, and uninstall scripts. Quick path:
```powershell
# 1. Publish (produces publish-out\self-contained\ and publish-out\framework-dependent\)
.\install\publish.ps1 -Clean
# 2. Install (elevated PowerShell) — point at the flavour you want to deploy
.\install\install.ps1 -PublishOutput .\publish-out\self-contained -Start
# 3. Edit the config that was placed at %ProgramData%\mbproxy\appsettings.json
# 4. Verify
Invoke-WebRequest http://localhost:8080/ -UseBasicParsing
```
## Maintenance
Documentation doctrine for this repo: [`../DOCS-GUIDE.md`](../DOCS-GUIDE.md).
- This README routes to deep docs — it does not duplicate them.
- Design decisions and rationale live in the `docs/` tree (Architecture, Features, Operations, Reference, Testing).
- When the service's public surface or task→tool mapping changes, update this README and the root [`../CLAUDE.md`](../CLAUDE.md) index row.