@@ -39,6 +39,21 @@
|
||||
client may have bumped it by more, so the comparison is `>=`). NodeId form:
|
||||
ns=<n>;s=AbLegacy/<gateway>/_Diagnostics/RequestCount. Mirrors the
|
||||
-SystemConnectionStatusNodeId knob on test-abcip.ps1.
|
||||
|
||||
.PARAMETER DiagnosticsDemoteCountNodeId
|
||||
Optional NodeId for the synthetic _Diagnostics/<host>/DemoteCount variable
|
||||
emitted by AB Legacy discovery (PR ablegacy-12 / #255). When supplied, the
|
||||
script runs the auto-demote assertion: kills the simulator container so
|
||||
reads start failing, hammers the user-tag BridgeNodeId at least
|
||||
FailureThreshold times to trip the demotion, then reads the diagnostic
|
||||
counter and asserts the value increased by >= 1. NodeId form:
|
||||
ns=<n>;s=AbLegacy/<gateway>/_Diagnostics/DemoteCount. The simulator
|
||||
must support `docker stop otopcua-ab-server-slc500` for the kill stage.
|
||||
|
||||
.PARAMETER FailureThresholdForDemote
|
||||
Failure threshold the server is configured with (default 3). The
|
||||
demote assertion writes/reads N+1 times against the killed simulator
|
||||
to guarantee the threshold trips even if some reads beat the kill.
|
||||
#>
|
||||
|
||||
param(
|
||||
@@ -47,7 +62,9 @@ param(
|
||||
[string]$Address = "N7:5",
|
||||
[string]$OpcUaUrl = "opc.tcp://localhost:4840",
|
||||
[Parameter(Mandatory)] [string]$BridgeNodeId,
|
||||
[string]$DiagnosticsRequestCountNodeId
|
||||
[string]$DiagnosticsRequestCountNodeId,
|
||||
[string]$DiagnosticsDemoteCountNodeId,
|
||||
[int]$FailureThresholdForDemote = 3
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
@@ -245,5 +262,67 @@ finally {
|
||||
Remove-Item -Path $importJsonPath -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# PR ablegacy-12 / #255 — auto-demote round-trip. Kill the simulator container,
|
||||
# hammer the bridge NodeId past the failure threshold, then assert the
|
||||
# DemoteCount diagnostic incremented. Restart the simulator at the end so the
|
||||
# next run gets a clean baseline. Gated on -DiagnosticsDemoteCountNodeId so
|
||||
# environments without docker-side control of the simulator can opt out.
|
||||
if ($DiagnosticsDemoteCountNodeId) {
|
||||
Write-Header "AutoDemote (kill simulator + observe DemoteCount from $DiagnosticsDemoteCountNodeId)"
|
||||
$baselineDemoteOut = & $opcUaCli.File @($opcUaCli.PrefixArgs) `
|
||||
@("read", "-u", $OpcUaUrl, "-n", $DiagnosticsDemoteCountNodeId) 2>&1
|
||||
$baselineDemote = 0
|
||||
if (($baselineDemoteOut -join "`n") -match '(\d+)') { $baselineDemote = [int64]$Matches[1] }
|
||||
|
||||
# Best-effort container kill — prefer the slc500 profile name; fall back to
|
||||
# micrologix / plc5 in case the operator pointed the e2e at a different family.
|
||||
$simContainers = @("otopcua-ab-server-slc500", "otopcua-ab-server-micrologix", "otopcua-ab-server-plc5")
|
||||
$killed = $false
|
||||
foreach ($c in $simContainers) {
|
||||
$stop = docker stop $c 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and $stop) {
|
||||
Write-Host "Stopped $c"
|
||||
$killed = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $killed) {
|
||||
Write-Fail "AutoDemote: no ab_server container found via 'docker stop' — skipping demote assertion"
|
||||
$results += @{ Passed = $false; Reason = "no simulator container to kill" }
|
||||
}
|
||||
else {
|
||||
# Hammer past the threshold. Each read against a now-unreachable simulator
|
||||
# surfaces BadCommunicationError; FailureThreshold consecutive ones trip
|
||||
# the demotion. We add 2 extra to absorb timing slack (one read may be
|
||||
# in-flight when the kill lands).
|
||||
$hammerCount = $FailureThresholdForDemote + 2
|
||||
for ($i = 0; $i -lt $hammerCount; $i++) {
|
||||
& $opcUaCli.File @($opcUaCli.PrefixArgs) `
|
||||
@("read", "-u", $OpcUaUrl, "-n", $BridgeNodeId) 2>&1 | Out-Null
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
$afterDemoteOut = & $opcUaCli.File @($opcUaCli.PrefixArgs) `
|
||||
@("read", "-u", $OpcUaUrl, "-n", $DiagnosticsDemoteCountNodeId) 2>&1
|
||||
$afterDemote = 0
|
||||
if (($afterDemoteOut -join "`n") -match '(\d+)') { $afterDemote = [int64]$Matches[1] }
|
||||
|
||||
$deltaDemote = $afterDemote - $baselineDemote
|
||||
if ($deltaDemote -ge 1) {
|
||||
Write-Pass "AutoDemote DemoteCount delta $deltaDemote >= 1 after $hammerCount failed reads"
|
||||
$results += @{ Passed = $true }
|
||||
} else {
|
||||
Write-Fail "AutoDemote DemoteCount delta $deltaDemote < 1 (baseline=$baselineDemote after=$afterDemote)"
|
||||
$results += @{ Passed = $false; Reason = "demote delta $deltaDemote" }
|
||||
}
|
||||
|
||||
# Restart the simulator so subsequent test runs have a clean baseline.
|
||||
# Best-effort — if docker-compose isn't on the path the operator can
|
||||
# bring it back manually via the Docker/docker-compose.yml profile.
|
||||
try { docker start (docker ps -aq -f "name=otopcua-ab-server-") | Out-Null } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
Write-Summary -Title "AB Legacy e2e" -Results $results
|
||||
if ($results | Where-Object { -not $_.Passed }) { exit 1 }
|
||||
|
||||
@@ -96,7 +96,12 @@ VALUES (@Gen, @DrvId, @ClusterId, @NsId, 'ablegacy-smoke', 'AbLegacy', N'{
|
||||
"PlcFamily": "Slc500",
|
||||
"DeviceName": "slc-500",
|
||||
"TimeoutMs": 500,
|
||||
"Retries": 1
|
||||
"Retries": 1,
|
||||
"Demote": {
|
||||
"FailureThreshold": 3,
|
||||
"DemoteForMs": 30000,
|
||||
"Enabled": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"Probe": { "Enabled": true, "IntervalMs": 5000, "TimeoutMs": 2000, "ProbeAddress": "S:0" },
|
||||
@@ -155,7 +160,15 @@ PRINT ' e.g. "ab://<plc-ip>:44818/" and re-run this seed.';
|
||||
PRINT '';
|
||||
PRINT 'PR ablegacy-10 / #253 — diagnostic counters auto-emit per device under';
|
||||
PRINT ' AbLegacy/<host>/_Diagnostics/<name>. No dbo.Tag rows needed — the';
|
||||
PRINT ' driver registers them at DiscoverAsync time. Seven counters per device:';
|
||||
PRINT ' driver registers them at DiscoverAsync time. Nine counters per device:';
|
||||
PRINT ' RequestCount, ResponseCount, ErrorCount, RetryCount, LastErrorCode,';
|
||||
PRINT ' LastErrorMessage, CommFailures. See docs/drivers/AbLegacy-Diagnostics.md';
|
||||
PRINT ' for the full surface + reset semantics.';
|
||||
PRINT ' LastErrorMessage, CommFailures, DemoteCount, LastDemotedUtc. See';
|
||||
PRINT ' docs/drivers/AbLegacy-Diagnostics.md for the full surface + reset';
|
||||
PRINT ' semantics.';
|
||||
PRINT '';
|
||||
PRINT 'PR ablegacy-12 / #255 — auto-demote on comm failure: 3 consecutive';
|
||||
PRINT ' failed reads / probes mark the device Demoted for DemoteFor=PT30S';
|
||||
PRINT ' (30 s); reads against a demoted device short-circuit with';
|
||||
PRINT ' BadCommunicationError so one slow PLC can''t starve the driver.';
|
||||
PRINT ' Tune via the Demote block on each Devices[] row. DemoteCount +';
|
||||
PRINT ' LastDemotedUtc on the _Diagnostics folder surface flapping links.';
|
||||
|
||||
Reference in New Issue
Block a user