FOCAS Tier-C PR E — ops glue: ProcessHostLauncher + post-mortem MMF + NSSM scripts #173

Merged
dohertj2 merged 1 commits from focas-tier-c-pr-e-ops-glue into v2 2026-04-20 14:26:37 -04:00
Owner

Final of 5 PRs for #220. Closes the Tier-C architecture work. Remaining FOCAS work is hardware-gated on a real CNC / licensed Fwlib32 dev-kit (#222 lab rig).

What lands

  • Proxy (.NET 10) — Supervisor/: ProcessHostLauncher (production IHostProcessLauncher using Process.Start + env-var propagation + pipe-readiness polling until ConnectTimeout); PostMortemReader (reads the Host's MMF after crash).
  • Host (net48 x86) — Stability/: PostMortemMmf ring-buffer of the last ~1000 IPC ops, magic OFPC format, 256-byte entries. Survives Host process death.
  • Scripts/install/: Install-FocasHost.ps1 / Uninstall-FocasHost.ps1 — idempotent NSSM wrappers for OtOpcUaFocasHost Windows service. Resolves SID, auto-generates shared secret per install, stages env vars in AppEnvironmentExtra (never on disk), DependOnService=OtOpcUa.
  • Tests (7 new): PostMortemMmf round-trip + ring-buffer wrap + message truncation + file re-open (4); PostMortemReader format compatibility with hand-written MMF (3).

Docs

  • docs/v2/implementation/focas-isolation-plan.md — promoted from DRAFT to 'PRs A-E shipped' with per-PR citations + test totals.
  • docs/drivers/FOCAS-Test-Fixture.md §5 — updated to describe the shipped Tier-C components + hardware-gated FwlibHostedBackend gap.

Post-ship totals

189 driver + 24 Shared + 13 Host = 226 FOCAS-family tests green.

What's NOT in this PR

  • Real FwlibHostedBackend (P/Invoke Fwlib32.dll) — hardware-gated on #222.
  • FOCAS runtime status into Admin /hosts FleetStatusHub — Galaxy Tier-C has the shape, FOCAS slots in as a follow-up.
Final of 5 PRs for #220. Closes the Tier-C architecture work. Remaining FOCAS work is hardware-gated on a real CNC / licensed Fwlib32 dev-kit (#222 lab rig). ## What lands - **Proxy (.NET 10) — Supervisor/**: `ProcessHostLauncher` (production IHostProcessLauncher using Process.Start + env-var propagation + pipe-readiness polling until ConnectTimeout); `PostMortemReader` (reads the Host's MMF after crash). - **Host (net48 x86) — Stability/**: `PostMortemMmf` ring-buffer of the last ~1000 IPC ops, magic `OFPC` format, 256-byte entries. Survives Host process death. - **Scripts/install/**: `Install-FocasHost.ps1` / `Uninstall-FocasHost.ps1` — idempotent NSSM wrappers for `OtOpcUaFocasHost` Windows service. Resolves SID, auto-generates shared secret per install, stages env vars in AppEnvironmentExtra (never on disk), DependOnService=OtOpcUa. - **Tests (7 new)**: PostMortemMmf round-trip + ring-buffer wrap + message truncation + file re-open (4); PostMortemReader format compatibility with hand-written MMF (3). ## Docs - `docs/v2/implementation/focas-isolation-plan.md` — promoted from DRAFT to 'PRs A-E shipped' with per-PR citations + test totals. - `docs/drivers/FOCAS-Test-Fixture.md` §5 — updated to describe the shipped Tier-C components + hardware-gated FwlibHostedBackend gap. ## Post-ship totals **189 driver + 24 Shared + 13 Host = 226 FOCAS-family tests green.** ## What's NOT in this PR - Real FwlibHostedBackend (P/Invoke Fwlib32.dll) — hardware-gated on #222. - FOCAS runtime status into Admin /hosts FleetStatusHub — Galaxy Tier-C has the shape, FOCAS slots in as a follow-up.
dohertj2 added 1 commit 2026-04-20 14:26:27 -04:00
Production IHostProcessLauncher (ProcessHostLauncher.cs): Process.Start spawns OtOpcUa.Driver.FOCAS.Host.exe with OTOPCUA_FOCAS_PIPE / OTOPCUA_ALLOWED_SID / OTOPCUA_FOCAS_SECRET / OTOPCUA_FOCAS_BACKEND in the environment (supervisor-owned, never disk), polls FocasIpcClient.ConnectAsync at 250ms cadence until the pipe is up or the Host exits or the ConnectTimeout deadline passes, then wraps the connected client in an IpcFocasClient. TerminateAsync kills the entire process tree + disposes the IPC stream. ProcessHostLauncherOptions carries HostExePath + PipeName + AllowedSid plus optional SharedSecret (auto-generated from a GUID when omitted so install scripts don't have to), Arguments, Backend (fwlib32/fake/unconfigured default-unconfigured), ConnectTimeout (15s), and Series for CNC pre-flight.

Post-mortem MMF (Host/Stability/PostMortemMmf.cs + Proxy/Supervisor/PostMortemReader.cs): ring-buffer of the last ~1000 IPC operations written by the Host into a memory-mapped file. On a Host crash the supervisor reads the MMF — which survives process death — to see what was in flight. File format: 16-byte header [magic 'OFPC' (0x4F465043) | version | capacity | writeIndex] + N × 256-byte entries [8-byte UTC unix ms | 8-byte opKind | 240-byte UTF-8 message + null terminator]. Magic distinguishes FOCAS MMFs from the Galaxy MMFs that ship the same format shape. Writer is single-producer (Host) with a lock_writeGate; reader is multi-consumer (Proxy + any diagnostic tool) using a separate MemoryMappedFile handle.

NSSM install wrappers (scripts/install/Install-FocasHost.ps1 + Uninstall-FocasHost.ps1): idempotent service registration for OtOpcUaFocasHost. Resolves SID from the ServiceAccount, generates a fresh shared secret per install if not supplied, stages OTOPCUA_FOCAS_PIPE/SID/SECRET/BACKEND in AppEnvironmentExtra so they never hit disk, rotates 10MB stdout/stderr logs under %ProgramData%\OtOpcUa, DependOnService=OtOpcUa so startup order is deterministic. Backend selector defaults to unconfigured so a fresh install doesn't accidentally load a half-configured Fwlib32.dll on first start.

Tests (7 new, 2 files): PostMortemMmfTests.cs in FOCAS.Host.Tests — round-trip write+read preserves order + content, ring-buffer wraps at capacity (writes 10 entries to a 3-slot buffer, asserts only op-7/8/9 survive in FIFO order), message truncation at the 240-byte cap is null-terminated + non-overflowing, reopening an existing file preserves entries. PostMortemReaderCompatibilityTests.cs in FOCAS.Tests — hand-writes a file in the exact host format (magic/entry layout) + asserts the Proxy reader decodes with correct ring-walk ordering when writeIndex != 0, empty-return on missing file + magic mismatch. Keeps the two codebases in format-lockstep without the net10 test project referencing the net48 Host assembly.

Docs updated: docs/v2/implementation/focas-isolation-plan.md promoted from DRAFT to PRs A-E shipped status with per-PR citations + post-ship test counts (189 + 24 + 13 = 226 FOCAS-family tests green). docs/drivers/FOCAS-Test-Fixture.md §5 updated from "architecture scoped but not implemented" to listing the shipped components with the FwlibHostedBackend gap explicitly labeled as hardware-gated. Install-FocasHost.ps1 documents the OTOPCUA_FOCAS_BACKEND selector + points at docs/v2/focas-deployment.md for Fwlib32.dll licensing.

What ISN'T in this PR: (1) the real FwlibHostedBackend implementing IFocasBackend with the P/Invoke — requires either a CNC on the bench or a licensed FANUC developer kit to validate, tracked under #220 as a single follow-up task; (2) Admin /hosts surface integration for FOCAS runtime status — Galaxy Tier-C already has the shape, FOCAS can slot in when someone wires ObservedCrashes/StickyAlertActive/BackoffAttempt to the FleetStatusHub; (3) a full integration test that actually spawns a real FOCAS Host process — ProcessHostLauncher is tested via its contract + the MMF is tested via round-trip, but no test spins up the real exe (the Galaxy Tier-C tests do this, but the FOCAS equivalent adds no new coverage over what's already in place).

Total FOCAS-family tests green after this PR: 189 driver + 24 Shared + 13 Host = 226.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit 340f580be0 into v2 2026-04-20 14:26:37 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#173