08b950caee
Adds user-defined extended properties to an existing tag via the 2020 WCF
AddTEx (AddTagExtendedProperties) op. Write-enabled connection + uppercase
storage-session GUID handle; reuses the write orchestrator open/priming chain.
The AddTEx inBuff is the exact inverse of the R1.5 GetTepByNm read-response
framing, so the serializer mirrors the read parser:
uint32 groupCount + 0x01(group) + [0x09+u16+ASCII tag] + uint32 propCount
+ per prop{ 0x02 + [0x09+u16+ASCII name] + 0x43 VT_BSTR + u16 payloadLen
+ u16 charCount + UTF-16 value } + 0x01(group trailer) + 0x00(terminator).
The trailing 0x00 is required — without it inBuff is one byte short and the
server throws SErrorException in CHistStorage::AddTagExtendedProperties. The
golden fixture pins the clean inBuff the live server accepted (dumped via
AVEVA_HISTORIAN_TEP_DUMP); read-back verified via R1.5. String (0x43) values only.
Delete (DelTep) is deferred: the native DeleteTagExtendedPropertiesByName does a
client-side sync check and returns err 229 for a just-added property, so the
DelTep request never reaches the wire and its inBuff can't be captured yet.
Shipped: HistorianClient.AddTagExtendedPropertiesAsync/AddTagExtendedPropertyAsync;
HistorianTagExtendedPropertyProtocol.SerializeAddRequest; orchestrator path;
golden WcfTagExtendedPropertyWriteProtocolTests (4); gated live write/read-back test;
native-harness `add-tep` scenario + Capture-AddTagExtendedProperties.ps1 +
decode-add-tep-capture.py. Doc: wcf-add-tag-extended-properties.md. 233 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
105 lines
5.2 KiB
PowerShell
105 lines
5.2 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Captures the native AVEVA client's AddTagExtendedProperties / DeleteTagExtendedProperties wire
|
|
traffic (HCAL roadmap R1.11) so the AddTEx / DelTep inBuff layout can be decoded, not guessed.
|
|
|
|
.DESCRIPTION
|
|
Drives the .NET-Framework NativeTraceHarness's `add-tep` scenario against the live Historian with
|
|
an IL-rewritten copy of aahClientManaged.dll whose ClientMessageEncoder.WriteMessage AND
|
|
ReadMessage are instrumented. The harness opens a WRITE-enabled connection, creates a sandbox tag
|
|
(RetestSdkWrite...), and calls AddTagExtendedProperties(TagExtendedPropertyGroupList, out err) with
|
|
one string property (and DeleteTagExtendedPropertiesByName when -Delete).
|
|
|
|
Decode with scripts/decode-add-tep-capture.py: the WCF.WriteMessage.Body whose op is AddTEx carries
|
|
the inBuff (tag name + property name/value); DelTep carries the delete inBuff (tag + property names).
|
|
|
|
SAFETY: sandbox-guarded — the tag MUST start with 'RetestSdkWrite'. Default run leaves the tag +
|
|
property in place (unless -Delete); pass -Delete to also capture DelTep and remove the property.
|
|
|
|
.NOTES
|
|
Artifacts are diagnostic and gitignored. Sanitize before copying into docs/.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$ServerName = "localhost",
|
|
[int]$TcpPort = 32568,
|
|
[string]$TepTag = "RetestSdkWriteTepTag",
|
|
[string]$PropName = "SdkTestProp",
|
|
[string]$PropValue = "SdkTestValue",
|
|
[switch]$Delete,
|
|
[string]$Configuration = "Debug"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
Set-Location $repoRoot
|
|
if (-not $TepTag.StartsWith("RetestSdkWrite")) { throw "-TepTag must start with 'RetestSdkWrite' (sandbox guard)." }
|
|
|
|
$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-add-tep"
|
|
$currentCopy = Join-Path $captureDir "current-copy"
|
|
$instrDll = Join-Path $captureDir "aahClientManaged.dll"
|
|
$capturePath = Join-Path $captureDir "add-tep-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
|
|
$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 add-tep ($TepTag : $PropName=$PropValue) ==" -ForegroundColor Green
|
|
$harnessArgs = @(
|
|
"--scenario", "add-tep",
|
|
"--server-name", $ServerName,
|
|
"--tcp-port", "$TcpPort",
|
|
"--tep-tag", $TepTag,
|
|
"--tep-name", $PropName,
|
|
"--tep-value", $PropValue,
|
|
"--current-dir", $currentCopy,
|
|
"--managed-dll-path", $harnessDll
|
|
)
|
|
if ($Delete) { $harnessArgs += "--tep-delete" }
|
|
|
|
$harnessJson = $null
|
|
try {
|
|
$prevEap = $ErrorActionPreference
|
|
$ErrorActionPreference = "Continue"
|
|
$harnessJson = & dotnet run --no-build -c $Configuration --project $harnessProj -- @harnessArgs 2>&1
|
|
} catch {
|
|
Write-Host " (add-tep 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 (AddTagExtendedProperties / Rows):" -ForegroundColor Cyan
|
|
$harnessJson | Select-Object -Last 24
|
|
Write-Host "`nDecode with: python scripts\decode-add-tep-capture.py" -ForegroundColor Cyan
|