Releases: powerpco/pwrp-edge-tools
Release list
S7 Probe v0.1.0 (diagnostic)
S7 read-only probe (.NET 10 single-file, ~190KB). Reads DBs from JSON config and prints values. Requires Microsoft.NETCore.App 10.x runtime. Sample config s7-probe-laflorida.json targets PCH La Florida S7-1500 @ 10.10.2.30 with WKV template DBs (DB120/121/281/191/280/290/100) for one unit.
pwrp-edge-s7 v0.2.0 — once + serve commands + YAML config
Adds:
- YAML config schema (Plc/Mqtt/Sparkplug/Runtime/Groups/Tags)
oncecommand: load config, single read cycle, print all tags, exit (no MQTT)servecommand: long-running daemon with Sparkplug B publish over MQTT, reconnect logic
Framework-dependent build (.NET 10 runtime required on gateway).
SHA256: e3f7e6b56577bae6486b4009c28b875afcbf1f8a7de5d7d902ee0d1ff0b83071
pwrp-edge-s7 v0.1.0 — initial scaffold + dump command
First release of the new S7 client (.NET 10).
This release: framework-dependent build, .NET 10 runtime required on the gateway.
Includes:
dumpsubcommand: bulk-read raw bytes from a DB and reinterpret as REAL/DINT/INT/WORD in parallel columns.read/once/servesubcommands: stubs (next iterations).
Tarball SHA256: cbe4130deec5507bf4507d484c73843ecec6f0956edb0b9924eaed85bb801b1e
pwrp-edge-mqtt2http — fix: pass Sparkplug BooleanValue (datatype 11) as 0/1 numeric
Fixes BOOL metrics from IEC-104 (TypeID 1, M_SP_NA_1) being silently dropped.
Root cause: SparkplugMessageConverter.TryCreateField() switch had no case for BooleanValue, falling to default → return false → metric rejected.
Fix: 3 lines, map true→1.0, false→0.0, append to F64 like the rest.
SHA256: ce8fdee4b28c6bc7caecaff2818e9eee6fa33bae541d04c5b12005016af1d5a1
Branch: fix/sparkplug-boolean-passthrough
pwrp-agent v0.6.3 — credential rotation snapshot/restore + CIS-compliant password gen
Hardens credential rotation against partial chpasswd failures on CIS-hardened Ubuntu fleet gateways.
Fixes
- pp-0054 incident (2026-05-06): chpasswd processed pp-admin successfully then failed on root (pwquality rule violation), leaving pp-admin with an unknown password and no automatic recovery path.
Changes
RotateCredentialsReportFirstnow snapshots/etc/shadowhashes before applying changes; on partial failure, restores viausermod -p '<old_hash>'(which writes to /etc/shadow without going through PAM, bypassing pam_pwhistory rejection of the prior password).- Per-user chpasswd application with abort-early semantics.
- Post-change verification: confirms both hashes actually changed before reporting success to cloud.
- Password generator now guarantees CIS pwquality compliance (
minclass=3,ucredit=-2,lcredit=-2,dcredit=-1,maxsequence=3,maxrepeat=3,usercheck). Validates each candidate in-process and retries on rule violation.
SHA256
614927c030b4bfac4221b9f595cddc7762a5fe1c235f73e4afb714e7ef606fee
pwrp-agent v0.6.2
Edge agent v0.6.2 - eliminates systemd service-name dependency by exiting cleanly (os.Exit) instead of calling systemctl restart from inside the service. Companion to server-side Fix B (transactional cert revoke).
SHA256: 82e32e5ff4cbb82dd47ef662923c3cc1e1981fada52a35a516faef32953cf285
Edge MQTT→HTTP forwarder v1.0.1 (net10)
Sparkplug MQTT subscriber forwarding batches via HTTP POST /ingest. TFM bumped to net10.0 to match runtime already deployed on gateways. Single-file no-self-contained; tarball includes pwrp-edge-mqtt2http + libnironcompress.so.
Edge MQTT→HTTP forwarder v1.0.0
Sparkplug MQTT subscriber that forwards batches via HTTP POST /ingest (with x-api-key). Single-file .NET 8, no-self-contained. Tarball includes pwrp-edge-mqtt2http (3.8MB) + libnironcompress.so (2MB native dep). Requires .NET 8 runtime (apt: aspnetcore-runtime-8.0 or similar). Sample env attached.
Edge MQTT→HTTP forwarder v1.1.0 (resilience: auto-heal + systemd watchdog)
Resilience release of pwrp-edge-mqtt2http after the 2026-05-01 silent-stall incident at pp-0022-gw (PCH La Florida) where the worker stopped forwarding for ~12.6h with the systemd unit reporting active running.
Includes the BooleanValue Sparkplug fix that was deployed as a custom binary on 2026-04-30 (originally PR #3 in pwrp-data-ingest).
Resilience changes (no new dependencies)
- No more silent stalls:
HttpBatchService.SendBatchAsynccatches all exception types (not onlyHttpRequestException/TaskCanceledException).ExecuteAsyncwraps the drain loop in an outer recovery loop — onlyOperationCanceledExceptionfrom shutdown stops the worker. - Per-request timeout via linked CancellationTokenSource: a hung socket cannot wedge the pipeline beyond
HTTP_TIMEOUT_SEC. Default is now 5s (was 30s) — tuned for realtime cadence. - Bounded retries with quarantine:
HTTP_MAX_ATTEMPTS=5(default) per batch with exponential backoff. After exhaustion the batch is marked Quarantined in the manifest and the loop continues. Terminal 4xx (except 408/429) skips retries. SocketsHttpHandlerwith bounded connection lifetimes:PooledConnectionLifetime=2m,PooledConnectionIdleTimeout=30s. Stale TCP sockets in the pool are recycled automatically.- Active recovery on Offline:
ConnectivityMonitor.OfflineEnteredevent fires when the FSM drops to Offline; the HTTP worker recreates itsHttpClientand underlying handler to drop every cached TCP connection. - systemd watchdog: unit must be
Type=notifywithWatchdogSec=120. The worker only sendsWATCHDOG=1when there has been a recent successful POST — a zombie loop cannot keep systemd happy. If the loop ever stalls, systemd SIGTERMs and restarts within ~120s. - Visible backpressure:
MqttWorkeremits a rate-limited LogWarning when the channel is full and the oldest batch is being dropped. New EdgeMetrics counters:channel_full_drops_total,batches_quarantined_total,http_client_recreations_total. - Liveness heartbeat: an Info-level summary every
HTTP_HEARTBEAT_SEC(default 60s) — alert onLastSuccessAgoSecrising without bound.
Default config changes
| Variable | Old | New | Why |
|---|---|---|---|
HTTP_TIMEOUT_SEC |
30 | 5 | timeout > realtime update interval makes channel drain slower than fill |
HTTP_INITIAL_RETRY_SEC |
2 | 1 | fail-fast and recover-fast |
HTTP_MAX_RETRY_SEC |
30 | 10 | bound worst-case stall per batch |
HTTP_MAX_ATTEMPTS |
(∞) | 5 | new — quarantine after budget exhausted |
HTTP_HEARTBEAT_SEC |
— | 60 | new — liveness log cadence |
Deployment
Required steps (existing deployments):
- Stop service:
systemctl stop pwrp-edge-mqtt2http - Backup binary:
cp -a /opt/pwrp-edge-mqtt2http /opt/pwrp-edge-mqtt2http.bak.$(date +%Y%m%d-%H%M%S) - Extract new binary:
tar xzf pwrp-edge-mqtt2http-resilience-v1.0.tar.gz -C /opt/pwrp-edge-mqtt2http - Replace systemd unit:
cp pwrp-edge-mqtt2http.service /etc/systemd/system/thensystemctl daemon-reload - Update env: merge new keys (
HTTP_MAX_ATTEMPTS,HTTP_HEARTBEAT_SEC) into/etc/pwrp/edge-mqtt2http/.env. BringHTTP_TIMEOUT_SEC,HTTP_INITIAL_RETRY_SEC,HTTP_MAX_RETRY_SECto the new defaults. - Start:
systemctl start pwrp-edge-mqtt2http - Validate:
journalctl -u pwrp-edge-mqtt2http -f— expect to seeHTTP forwarder alive. LastSuccessAgoSec=...heartbeats andHTTP ingest request succeededlines.
Requires .NET 10 runtime on the gateway (framework-dependent build).
SHA256
fb9c0e2aa818352bcc0b1e6fd58995592bf5d42941fb173749cc684458068fcb pwrp-edge-mqtt2http-resilience-v1.0.tar.gz
References
- Incident analysis:
docs/sites/pchlaflorida/incident-2026-05-01-mqtt2http-stall.mdin the operations repo. - Resilience design contract:
docs/MQTT2HTTP_RESILIENCE.mdinpwrp-data-ingest. - Source branch (pending PR + merge in pwrp-data-ingest):
fix/mqtt2http-resilience.
Modbus client v1.0.0 (gateway + console)
Modbus TCP client + Sparkplug B publisher (libmodbus, libmosquitto). Gateway = production runner. Console = diagnostic with --once / --tag / --list. x86_64 Linux, dynamic libmodbus + libmosquitto1 required (apt-get install libmodbus5 libmosquitto1).