Task #253 follow-up — driver-side e2e debug: port fixes + HR[200] scratch register #214
@@ -51,6 +51,30 @@ knows. Those NodeIds live in `e2e-config.json` (see below). The
|
|||||||
published tag must be **writable** — stages 4 + 5 will fail against a
|
published tag must be **writable** — stages 4 + 5 will fail against a
|
||||||
read-only tag.
|
read-only tag.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Stages 1 + 2 (driver-side probe + loopback) are verified end-to-end
|
||||||
|
against the pymodbus / ab_server / python-snap7 fixtures. Stages 3-5
|
||||||
|
(anything crossing the OtOpcUa server) are **blocked** on server-side
|
||||||
|
driver factory wiring:
|
||||||
|
|
||||||
|
- `src/ZB.MOM.WW.OtOpcUa.Server/Program.cs` only registers Galaxy +
|
||||||
|
FOCAS factories. `DriverInstanceBootstrapper` skips any `DriverType`
|
||||||
|
without a registered factory — so Modbus / AB CIP / AB Legacy / S7 /
|
||||||
|
TwinCAT rows in the Config DB are silently no-op'd even when the seed
|
||||||
|
is perfect.
|
||||||
|
- No Config DB seed script exists for non-Galaxy drivers; Admin UI is
|
||||||
|
currently the only path to author one.
|
||||||
|
|
||||||
|
Tracking: **#209** (umbrella) → #210 (Modbus), #211 (AB CIP), #212 (S7),
|
||||||
|
#213 (AB Legacy, also hardware-gated — #222). Each child issue lists
|
||||||
|
the factory class to write + the seed SQL shape + the verification
|
||||||
|
command.
|
||||||
|
|
||||||
|
Until those ship, stages 3-5 will fail with "read failed" (nothing
|
||||||
|
published at that NodeId) and `[FAIL]` the suite even on a running
|
||||||
|
server.
|
||||||
|
|
||||||
## Prereqs
|
## Prereqs
|
||||||
|
|
||||||
1. **OtOpcUa server** running on `opc.tcp://localhost:4840` (or pass
|
1. **OtOpcUa server** running on `opc.tcp://localhost:4840` (or pass
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
"$comment": "Copy this file to e2e-config.json and replace the NodeIds with the ones your Config DB publishes. Fields named `opcUaUrl` override the -OpcUaUrl parameter on test-all.ps1 per-driver. Omit a top-level key to skip that driver.",
|
"$comment": "Copy this file to e2e-config.json and replace the NodeIds with the ones your Config DB publishes. Fields named `opcUaUrl` override the -OpcUaUrl parameter on test-all.ps1 per-driver. Omit a top-level key to skip that driver.",
|
||||||
|
|
||||||
"modbus": {
|
"modbus": {
|
||||||
"endpoint": "127.0.0.1:5502",
|
"$comment": "Port 5020 matches tests/.../Modbus.IntegrationTests/Docker/docker-compose.yml — `docker compose --profile standard up -d`.",
|
||||||
"bridgeNodeId": "ns=2;s=Modbus/HR100",
|
"endpoint": "127.0.0.1:5020",
|
||||||
|
"bridgeNodeId": "ns=2;s=Modbus/HR200",
|
||||||
"opcUaUrl": "opc.tcp://localhost:4840"
|
"opcUaUrl": "opc.tcp://localhost:4840"
|
||||||
},
|
},
|
||||||
|
|
||||||
"abcip": {
|
"abcip": {
|
||||||
"gateway": "ab://127.0.0.1/1,0",
|
"$comment": "ab_server listens on port 44818 (default CIP/EIP). `docker compose --profile controllogix up -d`.",
|
||||||
|
"gateway": "ab://127.0.0.1:44818/1,0",
|
||||||
"family": "ControlLogix",
|
"family": "ControlLogix",
|
||||||
"tagPath": "TestDINT",
|
"tagPath": "TestDINT",
|
||||||
"bridgeNodeId": "ns=2;s=AbCip/TestDINT"
|
"bridgeNodeId": "ns=2;s=AbCip/TestDINT"
|
||||||
@@ -23,7 +25,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"s7": {
|
"s7": {
|
||||||
"endpoint": "127.0.0.1:102",
|
"$comment": "Port 1102 matches tests/.../S7.IntegrationTests/Docker/docker-compose.yml (python-snap7 needs non-priv port). `docker compose --profile s7_1500 up -d`. Real S7 PLCs listen on 102.",
|
||||||
|
"endpoint": "127.0.0.1:1102",
|
||||||
"cpu": "S71500",
|
"cpu": "S71500",
|
||||||
"slot": 0,
|
"slot": 0,
|
||||||
"address": "DB1.DBW0",
|
"address": "DB1.DBW0",
|
||||||
|
|||||||
@@ -53,18 +53,12 @@ $opcUaCli = Get-CliInvocation `
|
|||||||
$commonAbCip = @("-g", $Gateway, "-f", $Family)
|
$commonAbCip = @("-g", $Gateway, "-f", $Family)
|
||||||
$results = @()
|
$results = @()
|
||||||
|
|
||||||
# Probe via @raw_cpu_type for ControlLogix; against ab_server this surfaces the
|
# The AbCip driver's TagPath parser rejects CIP attribute syntax like
|
||||||
# string 'Controllogix'. Other families use the main TestDINT.
|
# `@raw_cpu_type` ("malformed TagPath"), so probe uses the real TagPath for
|
||||||
$probeTag = if ($Family -eq "ControlLogix" -or $Family -eq "CompactLogix" -or $Family -eq "GuardLogix") {
|
# every family. Works against ab_server + real controllers alike.
|
||||||
"@raw_cpu_type"
|
|
||||||
} else {
|
|
||||||
$TagPath
|
|
||||||
}
|
|
||||||
$probeType = if ($probeTag -eq "@raw_cpu_type") { "String" } else { "DInt" }
|
|
||||||
|
|
||||||
$results += Test-Probe `
|
$results += Test-Probe `
|
||||||
-Cli $abcipCli `
|
-Cli $abcipCli `
|
||||||
-ProbeArgs (@("probe") + $commonAbCip + @("-t", $probeTag, "--type", $probeType))
|
-ProbeArgs (@("probe") + $commonAbCip + @("-t", $TagPath, "--type", "DInt"))
|
||||||
|
|
||||||
$writeValue = Get-Random -Minimum 1 -Maximum 9999
|
$writeValue = Get-Random -Minimum 1 -Maximum 9999
|
||||||
$results += Test-DriverLoopback `
|
$results += Test-DriverLoopback `
|
||||||
|
|||||||
@@ -7,17 +7,22 @@
|
|||||||
Five assertions:
|
Five assertions:
|
||||||
1. `otopcua-modbus-cli probe` hits the simulator
|
1. `otopcua-modbus-cli probe` hits the simulator
|
||||||
2. Driver-loopback write + read-back via modbus-cli
|
2. Driver-loopback write + read-back via modbus-cli
|
||||||
3. Forward bridge: modbus-cli writes HR[100], OPC UA client reads the bridged NodeId
|
3. Forward bridge: modbus-cli writes HR[200], OPC UA client reads the bridged NodeId
|
||||||
4. Reverse bridge: OPC UA client writes the NodeId, modbus-cli reads HR[100]
|
4. Reverse bridge: OPC UA client writes the NodeId, modbus-cli reads HR[200]
|
||||||
5. Subscribe-sees-change: OPC UA subscription observes a modbus-cli write
|
5. Subscribe-sees-change: OPC UA subscription observes a modbus-cli write
|
||||||
|
|
||||||
Requires a running Modbus simulator on localhost:5502 (the pymodbus fixture
|
Requires a running Modbus simulator on localhost:5020 (the pymodbus fixture
|
||||||
default per docs/drivers/Modbus-Test-Fixture.md) and a running OtOpcUa server
|
default — see tests/.../Modbus.IntegrationTests/Docker/docker-compose.yml)
|
||||||
whose config DB has a Modbus DriverInstance bound to that simulator + a Tag
|
and a running OtOpcUa server whose config DB has a Modbus DriverInstance
|
||||||
at HR[100] Int16 published under the NodeId passed via -BridgeNodeId.
|
bound to that simulator + a Tag at HR[200] UInt16 published under the
|
||||||
|
NodeId passed via -BridgeNodeId.
|
||||||
|
|
||||||
|
NOTE: HR[200] (not HR[100]) — pymodbus standard.json makes HR[100] an
|
||||||
|
auto-incrementing register that mutates every poll, so loopback writes
|
||||||
|
can't be verified there.
|
||||||
|
|
||||||
.PARAMETER ModbusHost
|
.PARAMETER ModbusHost
|
||||||
Host:port of the Modbus simulator. Default 127.0.0.1:5502.
|
Host:port of the Modbus simulator. Default 127.0.0.1:5020.
|
||||||
|
|
||||||
.PARAMETER OpcUaUrl
|
.PARAMETER OpcUaUrl
|
||||||
Endpoint URL of the OtOpcUa server. Default opc.tcp://localhost:4840.
|
Endpoint URL of the OtOpcUa server. Default opc.tcp://localhost:4840.
|
||||||
@@ -31,7 +36,7 @@
|
|||||||
#>
|
#>
|
||||||
|
|
||||||
param(
|
param(
|
||||||
[string]$ModbusHost = "127.0.0.1:5502",
|
[string]$ModbusHost = "127.0.0.1:5020",
|
||||||
[string]$OpcUaUrl = "opc.tcp://localhost:4840",
|
[string]$OpcUaUrl = "opc.tcp://localhost:4840",
|
||||||
[Parameter(Mandatory)] [string]$BridgeNodeId
|
[Parameter(Mandatory)] [string]$BridgeNodeId
|
||||||
)
|
)
|
||||||
@@ -59,14 +64,14 @@ $results += Test-Probe `
|
|||||||
$writeValue = Get-Random -Minimum 1 -Maximum 9999
|
$writeValue = Get-Random -Minimum 1 -Maximum 9999
|
||||||
$results += Test-DriverLoopback `
|
$results += Test-DriverLoopback `
|
||||||
-Cli $modbusCli `
|
-Cli $modbusCli `
|
||||||
-WriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "100", "-t", "UInt16", "-v", $writeValue)) `
|
-WriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16", "-v", $writeValue)) `
|
||||||
-ReadArgs (@("read") + $commonModbus + @("-r", "HoldingRegisters", "-a", "100", "-t", "UInt16")) `
|
-ReadArgs (@("read") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16")) `
|
||||||
-ExpectedValue "$writeValue"
|
-ExpectedValue "$writeValue"
|
||||||
|
|
||||||
$bridgeValue = Get-Random -Minimum 10000 -Maximum 19999
|
$bridgeValue = Get-Random -Minimum 10000 -Maximum 19999
|
||||||
$results += Test-ServerBridge `
|
$results += Test-ServerBridge `
|
||||||
-DriverCli $modbusCli `
|
-DriverCli $modbusCli `
|
||||||
-DriverWriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "100", "-t", "UInt16", "-v", $bridgeValue)) `
|
-DriverWriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16", "-v", $bridgeValue)) `
|
||||||
-OpcUaCli $opcUaCli `
|
-OpcUaCli $opcUaCli `
|
||||||
-OpcUaUrl $OpcUaUrl `
|
-OpcUaUrl $OpcUaUrl `
|
||||||
-OpcUaNodeId $BridgeNodeId `
|
-OpcUaNodeId $BridgeNodeId `
|
||||||
@@ -78,7 +83,7 @@ $results += Test-OpcUaWriteBridge `
|
|||||||
-OpcUaUrl $OpcUaUrl `
|
-OpcUaUrl $OpcUaUrl `
|
||||||
-OpcUaNodeId $BridgeNodeId `
|
-OpcUaNodeId $BridgeNodeId `
|
||||||
-DriverCli $modbusCli `
|
-DriverCli $modbusCli `
|
||||||
-DriverReadArgs (@("read") + $commonModbus + @("-r", "HoldingRegisters", "-a", "100", "-t", "UInt16")) `
|
-DriverReadArgs (@("read") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16")) `
|
||||||
-ExpectedValue "$reverseValue"
|
-ExpectedValue "$reverseValue"
|
||||||
|
|
||||||
$subValue = Get-Random -Minimum 30000 -Maximum 39999
|
$subValue = Get-Random -Minimum 30000 -Maximum 39999
|
||||||
@@ -87,7 +92,7 @@ $results += Test-SubscribeSeesChange `
|
|||||||
-OpcUaUrl $OpcUaUrl `
|
-OpcUaUrl $OpcUaUrl `
|
||||||
-OpcUaNodeId $BridgeNodeId `
|
-OpcUaNodeId $BridgeNodeId `
|
||||||
-DriverCli $modbusCli `
|
-DriverCli $modbusCli `
|
||||||
-DriverWriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "100", "-t", "UInt16", "-v", $subValue)) `
|
-DriverWriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16", "-v", $subValue)) `
|
||||||
-ExpectedValue "$subValue"
|
-ExpectedValue "$subValue"
|
||||||
|
|
||||||
Write-Summary -Title "Modbus e2e" -Results $results
|
Write-Summary -Title "Modbus e2e" -Results $results
|
||||||
|
|||||||
Reference in New Issue
Block a user