diff --git a/docs/PHYSICAL_BENCH_QUICK_START.md b/docs/PHYSICAL_BENCH_QUICK_START.md new file mode 100644 index 00000000..f0440830 --- /dev/null +++ b/docs/PHYSICAL_BENCH_QUICK_START.md @@ -0,0 +1,76 @@ +# Physical test quick start — DigSubAnalyzer / Process Bus Insight + +This folder is included in the **Windows x64 release candidate ZIP** so the bench +does not need a .NET SDK. Run only on an authorized, isolated engineering process +bus. The analyzer is **receive-only** and cannot operate or protect an IED. + +## 1. Run the desktop application + +1. Extract the entire ZIP, not just the EXE, to a local folder. +2. Install Npcap separately with appropriate raw Ethernet capture rights. +3. Run `ProcessBusInsight.exe` in the package root. +4. Select the verified adapter connected to an independent TAP/SPAN (preserve + 802.1Q tags and multicast). Start capture and select the **canonical** SV + stream, not the diagnostic mirror. +5. Load the actual deployed `.scd`/`.cid`/`.iid` via the SCL workflow. +6. In **Validation → Inspect PCAP**, select a local classic Ethernet PCAP + recording. Read the short verdict; detailed evidence is in the report from + the CLI below. For large captures progress/Cancel are supported. + +If canonical VLAN multicast traffic is not visible but `AR_DIAG_SV1` is, +the result is **mirror-only**. This does not prove the injector dropped VLAN +or ignored SCL; verify TAP/SPAN and NIC filtering. PTP messages observed on +the NIC do **not** establish clock discipline or lock. + +## 2. Screen the same wire recording with the bundled CLI + +Open PowerShell in the extracted package directory. Replace file paths with +files collected on your **own local bench**: + +```powershell +.\Bench\ProcessBus.InteropCli.exe --help +.\Bench\ProcessBus.InteropCli.exe --pcap "C:\lab\sv-ptp.pcap" --scl "C:\lab\deployed.scd" --max-frames 50000000 --json "C:\lab\interop-report.json" +``` + +The bundled CLI is a **self-contained Windows executable**. It parses classic +Ethernet PCAP, not PCAPNG. Convert PCAPNG to classic PCAP locally without +losing Ethernet/VLAN headers if necessary. A full 60-minute recording with +both canonical SV and a mirror may exceed 28 million SV packets alone +(at 4000 samples/s and nofASDU=1). Use the explicit frame limit or split +the PCAP into indexed windows; **CAPTURE_INCOMPLETE** must not be recorded as +a clean acceptance. + +Exit codes: **0** screening completed with no failing configured comparison +(not IED conformance); **3** missing/ambiguous/conflicting/incomplete evidence; +**2** invalid or unsupported input. For a trustworthy SCL comparison always +supply `--scl`. The local JSON contains wire MAC and IED identities; do +**not** upload it or raw SCL/PCAP to a public issue. + +## 3. Measure application load and physical truth separately + +While the desktop app receives simultaneous 4000/4800-profile SV, diagnostic +mirror and L2 PTP, record local process metrics: + +```powershell +.\Bench\measure-m4-bench.ps1 -DurationMinutes 60 -IntervalSeconds 5 -ProcessName ProcessBusInsight -OutputPath "C:\lab\m4-process-metrics.csv" +``` + +If PowerShell blocks scripts, follow your organization's signed-script policy; +do not turn off system-wide execution protection for this task. Collect +Npcap capture-driver/interface drops and ARStack device TX failure/missed-slot +telemetry separately; a zero `smpCnt` gap count does not prove zero wire loss. + +Before any **physical acceptance** claim, independently verify: + +- Canonical APPID/MAC/VLAN/PCP/svID/confRev/nofASDU/layout and 4000/4800 + cadence with the actual deployed SCL and applicable counter policy; +- `AR_DIAG_SV1` (broadcast, APPID 0x4F01) remains a separate debug stream; +- PTP Announce/Sync/Follow_Up/peer-delay and sync flags remain observed facts, + not unmeasured lock or precision claims; +- 60-minute concurrent stability, driver drops, resource behavior and IED + subscriber acceptance with qualified bench equipment. + +The detailed step-by-step sign-off matrix is in +`Bench/M4_ARSTACK_INTEROP_ACCEPTANCE.md`. Record the candidate artifact run +ID and checksums with your evidence. This candidate is not a stable/certified +release and provides no switching, control or protection-action authority. diff --git a/scripts/publish-windows-portable.ps1 b/scripts/publish-windows-portable.ps1 index 6be03e71..b2305baf 100644 --- a/scripts/publish-windows-portable.ps1 +++ b/scripts/publish-windows-portable.ps1 @@ -81,6 +81,33 @@ if ($singleFile) { Copy-Item (Join-Path $publishDir "*") $appStageDir -Recurse -Force } +# Physical acceptance needs a reproducible capture-vs-SCL inspector, not only +# a Windows UI EXE. Publish the existing read-only CLI self-contained into a +# separate tools folder; never stage raw PCAP/SCL or customer identities. +$interopProject = Join-Path $repoRoot "tools/ProcessBus.InteropCli/ProcessBus.InteropCli.csproj" +$interopPublishDir = Join-Path $repoRoot "$OutputRoot/publish/$portableName/interop-cli" +$interopStageDir = Join-Path $stageDir "Bench" +New-Item -ItemType Directory -Path $interopStageDir -Force | Out-Null +dotnet restore $interopProject -r $Runtime /p:ContinuousIntegrationBuild=true +if ($LASTEXITCODE -ne 0) { throw "Interop CLI runtime-specific restore failed." } +$interopPublishArgs = @( + "publish", $interopProject, "--no-restore", "-c", $Configuration, + "-r", $Runtime, "-o", $interopPublishDir, + "/p:Version=$Version", "/p:VersionPrefix=$versionPrefix", "/p:VersionSuffix=$versionSuffix", + "/p:PublishSingleFile=true", "/p:IncludeNativeLibrariesForSelfExtract=true", + "/p:EnableCompressionInSingleFile=true", "/p:PublishTrimmed=false", + "/p:DebugType=None", "/p:DebugSymbols=false", + "/p:ErrorOnDuplicatePublishOutputFiles=true", "--self-contained", "true" +) +dotnet @interopPublishArgs +if ($LASTEXITCODE -ne 0) { throw "Interop CLI publish failed." } +$interopExe = Join-Path $interopPublishDir "ProcessBus.InteropCli.exe" +if (-not (Test-Path $interopExe)) { throw "Interop CLI publish produced no executable." } +Copy-Item $interopExe (Join-Path $interopStageDir "ProcessBus.InteropCli.exe") -Force +Copy-Item (Join-Path $repoRoot "docs/M4_ARSTACK_INTEROP_ACCEPTANCE.md") (Join-Path $interopStageDir "M4_ARSTACK_INTEROP_ACCEPTANCE.md") -Force +Copy-Item (Join-Path $repoRoot "scripts/measure-m4-bench.ps1") (Join-Path $interopStageDir "measure-m4-bench.ps1") -Force +Copy-Item (Join-Path $repoRoot "docs/PHYSICAL_BENCH_QUICK_START.md") (Join-Path $interopStageDir "START_HERE.md") -Force + $packageReadme = @" Process Bus Insight / DigSubAnalyzer Windows portable package @@ -91,6 +118,10 @@ Community license: GPL-3.0-or-later 2. Extract this ZIP to a local folder. 3. Run $AppName.exe. 4. Use an authorized TAP, mirror port, or isolated engineering test network. +5. For physical SV/PTP test evidence read Bench/START_HERE.md. +6. Bench/ProcessBus.InteropCli.exe works offline and does not need the .NET SDK. +7. Bench/measure-m4-bench.ps1 records local process CPU/memory during a bench run. + A diagnostic broadcast mirror does NOT prove the canonical VLAN SV path. Included legal documents: LICENSE.txt, NOTICE.txt, COMMERCIAL-LICENSE.md, COPYRIGHT.md, TRADEMARK.md, diff --git a/scripts/verify-release-package.ps1 b/scripts/verify-release-package.ps1 index 6398a046..6845680d 100644 --- a/scripts/verify-release-package.ps1 +++ b/scripts/verify-release-package.ps1 @@ -24,7 +24,11 @@ try { "COPYRIGHT.md", "TRADEMARK.md", "THIRD_PARTY_NOTICES.md", - "Licensing.md" + "Licensing.md", + "Bench/ProcessBus.InteropCli.exe", + "Bench/M4_ARSTACK_INTEROP_ACCEPTANCE.md", + "Bench/START_HERE.md", + "Bench/measure-m4-bench.ps1" ) foreach ($relative in $required) { if (-not (Test-Path (Join-Path $temp $relative))) { @@ -68,6 +72,24 @@ try { throw "Package verification failed. Expected exactly one root EXE: $AppName.exe. Found: $names" } + # The GUI remains the only ROOT executable. A separate, self-contained + # read-only inspector is required so field users can screen their actual + # PCAP+SCL evidence without installing the developer SDK. + $interopExe = Join-Path $temp "Bench/ProcessBus.InteropCli.exe" + if ((Get-Item $interopExe).Length -lt 1024) { + throw "Package verification failed. The interop CLI executable is too small." + } + $interopHelp = & $interopExe --help 2>&1 | Out-String + if ($LASTEXITCODE -ne 0 -or $interopHelp -notmatch 'offline interop screening') { + throw "Package verification failed. Bundled offline interop CLI cannot run --help." + } + $benchGuide = Get-Content (Join-Path $temp "Bench/START_HERE.md") -Raw + foreach ($marker in @('ProcessBus.InteropCli.exe', '60-minute', 'TAP/SPAN', 'AR_DIAG_SV1')) { + if ($benchGuide -notmatch [regex]::Escape($marker)) { + throw "Package verification failed. Physical bench quickstart is missing: $marker" + } + } + $size = (Get-Item (Join-Path $temp "$AppName.exe")).Length if ($size -lt 1024) { throw "Package verification failed. Executable looks too small: $size bytes" }