#Requires -Version 7.0 <# .SYNOPSIS End-to-end CLI test for the Modbus-TCP driver bridged through the OtOpcUa server. .DESCRIPTION Five assertions: 1. `otopcua-modbus-cli probe` hits the simulator 2. Driver-loopback write + read-back via modbus-cli 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[200] 5. Subscribe-sees-change: OPC UA subscription observes a modbus-cli write Requires a running Modbus simulator on localhost:5020 (the pymodbus fixture default — see tests/.../Modbus.IntegrationTests/Docker/docker-compose.yml) and a running OtOpcUa server whose config DB has a Modbus DriverInstance 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 Host:port of the Modbus simulator. Default 127.0.0.1:5020. .PARAMETER OpcUaUrl Endpoint URL of the OtOpcUa server. Default opc.tcp://localhost:4840. .PARAMETER BridgeNodeId OPC UA NodeId the OtOpcUa server publishes the HR[100] tag at. Set per your server config — e.g. 'ns=2;s=/warsaw/modbus-sim/HR_100'. Required. .EXAMPLE .\test-modbus.ps1 -BridgeNodeId "ns=2;s=/warsaw/modbus-sim/HR_100" #> param( [string]$ModbusHost = "127.0.0.1:5020", [string]$OpcUaUrl = "opc.tcp://localhost:4840", [Parameter(Mandatory)] [string]$BridgeNodeId ) $ErrorActionPreference = "Stop" . "$PSScriptRoot/_common.ps1" $hostPart, $portPart = $ModbusHost.Split(":") $port = [int]$portPart $modbusCli = Get-CliInvocation ` -ProjectFolder "src/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli" ` -ExeName "otopcua-modbus-cli" $opcUaCli = Get-CliInvocation ` -ProjectFolder "src/ZB.MOM.WW.OtOpcUa.Client.CLI" ` -ExeName "otopcua-cli" $commonModbus = @("-h", $hostPart, "-p", $port) $results = @() $results += Test-Probe ` -Cli $modbusCli ` -ProbeArgs (@("probe") + $commonModbus) $writeValue = Get-Random -Minimum 1 -Maximum 9999 $results += Test-DriverLoopback ` -Cli $modbusCli ` -WriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16", "-v", $writeValue)) ` -ReadArgs (@("read") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16")) ` -ExpectedValue "$writeValue" $bridgeValue = Get-Random -Minimum 10000 -Maximum 19999 $results += Test-ServerBridge ` -DriverCli $modbusCli ` -DriverWriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16", "-v", $bridgeValue)) ` -OpcUaCli $opcUaCli ` -OpcUaUrl $OpcUaUrl ` -OpcUaNodeId $BridgeNodeId ` -ExpectedValue "$bridgeValue" $reverseValue = Get-Random -Minimum 20000 -Maximum 29999 $results += Test-OpcUaWriteBridge ` -OpcUaCli $opcUaCli ` -OpcUaUrl $OpcUaUrl ` -OpcUaNodeId $BridgeNodeId ` -DriverCli $modbusCli ` -DriverReadArgs (@("read") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16")) ` -ExpectedValue "$reverseValue" $subValue = Get-Random -Minimum 30000 -Maximum 39999 $results += Test-SubscribeSeesChange ` -OpcUaCli $opcUaCli ` -OpcUaUrl $OpcUaUrl ` -OpcUaNodeId $BridgeNodeId ` -DriverCli $modbusCli ` -DriverWriteArgs (@("write") + $commonModbus + @("-r", "HoldingRegisters", "-a", "200", "-t", "UInt16", "-v", $subValue)) ` -ExpectedValue "$subValue" Write-Summary -Title "Modbus e2e" -Results $results if ($results | Where-Object { -not $_.Passed }) { exit 1 }