<# .SYNOPSIS Captures the native AVEVA client's event-SEND wire traffic (HCAL roadmap R2.1) to determine whether AddStreamedValue(HistorianEvent) rides the WCF MDAS path (capturable + implementable as a pure-managed-WCF SDK op) or the storage-engine shared-memory pipe (like revision writes — which would block M2 as a WCF SDK). .DESCRIPTION Drives the .NET-Framework NativeTraceHarness's `event-send` scenario against the live Historian with an IL-rewritten copy of aahClientManaged.dll whose ClientMessageEncoder.WriteMessage AND ReadMessage are instrumented to log every MDAS body (the same pipeline that produced every other proven request/response shape). The harness opens an Event connection (ReadOnly=false), builds a clearly-marked test HistorianEvent, calls AddStreamedValue(HistorianEvent), then CloseStorageConnection to flush the queued event onto the wire. Decode with scripts/decode-event-send-capture.py: if a StartStorage/AddStreamValues/ EnqueueEventDataPacket body appears on WCF.WriteMessage.Body, M2 is viable over WCF and the body carries the PackToVtq event value blob to decode (R2.2). If NOTHING event-shaped appears on the WCF path even though the native AddStreamedValue returned success, the delivery used the storage-engine pipe and M2 is architecturally blocked over WCF — the same conclusion as the revision-write path (docs/plans/revision-write-path.md). .NOTES Writes a real (clearly-marked) test event into the historian's event history. Artifacts are diagnostic and gitignored. Sanitize before copying anything into docs/ — never commit raw capture NDJSON, credentials, hostnames, or customer tag names. #> [CmdletBinding()] param( [string]$ServerName = "localhost", [int]$TcpPort = 32568, [string]$EventType = "User.Write", [int]$FlushSeconds = 6, [string]$Configuration = "Debug" ) $ErrorActionPreference = "Stop" $repoRoot = Split-Path -Parent $PSScriptRoot Set-Location $repoRoot $reProj = Join-Path $repoRoot "tools\AVEVA.Historian.ReverseEngineering\AVEVA.Historian.ReverseEngineering.csproj" $harnessProj = Join-Path $repoRoot "tools\AVEVA.Historian.NativeTraceHarness\AVEVA.Historian.NativeTraceHarness.csproj" $instrProj = Join-Path $repoRoot "tools\AVEVA.Historian.ReverseInstrumentation\AVEVA.Historian.ReverseInstrumentation.csproj" $captureDir = Join-Path $repoRoot "artifacts\reverse-engineering\instrumented-wcf-event-send" $currentCopy = Join-Path $captureDir "current-copy" $instrDll = Join-Path $captureDir "aahClientManaged.dll" $capturePath = Join-Path $captureDir "event-send-capture-latest.ndjson" Write-Host "== Building tooling ($Configuration) ==" -ForegroundColor Cyan dotnet build $reProj -c $Configuration --nologo -v q | Out-Null dotnet build $instrProj -c $Configuration --nologo -v q | Out-Null dotnet build $harnessProj -c $Configuration --nologo -v q | Out-Null $instrSourceDll = Get-ChildItem -Recurse (Join-Path $repoRoot "tools\AVEVA.Historian.ReverseInstrumentation\bin\$Configuration") ` -Filter "AVEVA.Historian.ReverseInstrumentation.dll" | Select-Object -First 1 -ExpandProperty FullName if (-not $instrSourceDll) { throw "ReverseInstrumentation.dll not found under bin\$Configuration." } Write-Host "== Instrumenting WriteMessage + ReadMessage ==" -ForegroundColor Cyan New-Item -ItemType Directory -Force -Path $captureDir | Out-Null # Chain via a distinct intermediate file (reading+writing the same path drops the second # hook on the mixed-mode native image). Final dll carries both hooks with distinct Phase # strings: WCF.WriteMessage.Body and WCF.ReadMessage.Body. $writeOnly = Join-Path $captureDir "aahClientManaged.write.dll" dotnet run --no-build -c $Configuration --project $reProj -- ` instrument-wcf-writemessage (Join-Path $repoRoot "current\aahClientManaged.dll") $writeOnly | Out-Null dotnet run --no-build -c $Configuration --project $reProj -- ` instrument-wcf-readmessage $writeOnly $instrDll | Out-Null Write-Host "== Staging current-copy ==" -ForegroundColor Cyan robocopy (Join-Path $repoRoot "current") $currentCopy /MIR /NJH /NJS /NDL /NP /NC /NS | Out-Null Copy-Item -Force $instrDll (Join-Path $currentCopy "aahClientManaged.dll") Copy-Item -Force $instrSourceDll (Join-Path $currentCopy "AVEVA.Historian.ReverseInstrumentation.dll") $harnessDll = Join-Path $currentCopy "aahClientManaged.dll" if (Test-Path $capturePath) { Remove-Item -Force $capturePath } $env:AVEVA_HISTORIAN_RE_CAPTURE = $capturePath Write-Host "== Capturing event-send ==" -ForegroundColor Green $harnessArgs = @( "--scenario", "event-send", "--event-send-confirm", "--server-name", $ServerName, "--tcp-port", "$TcpPort", "--event-type", $EventType, "--event-send-flush-seconds", "$FlushSeconds", "--current-dir", $currentCopy, "--managed-dll-path", $harnessDll ) $harnessJson = $null try { $prevEap = $ErrorActionPreference $ErrorActionPreference = "Continue" $harnessJson = & dotnet run --no-build -c $Configuration --project $harnessProj -- @harnessArgs 2>&1 } catch { Write-Host " (event-send raised: $($_.Exception.Message))" -ForegroundColor Yellow } finally { $ErrorActionPreference = $prevEap } Remove-Item Env:\AVEVA_HISTORIAN_RE_CAPTURE -ErrorAction SilentlyContinue $recCount = if (Test-Path $capturePath) { (Get-Content $capturePath | Where-Object { $_.Trim() }).Count } else { 0 } Write-Host "`n== Capture summary ==" -ForegroundColor Cyan Write-Host " -> $recCount records -> $capturePath" Write-Host "Harness output (look for AddStreamedEvent Success / ErrorCode):" -ForegroundColor Cyan $harnessJson | Select-Object -Last 60 Write-Host "`nDecode with: python scripts\decode-event-send-capture.py" -ForegroundColor Cyan