Skip to main content

OPC UA

Zeus.Protocols.OpcUa 面向 PLC、SCADA、网关和现场 Server 的 opc.tcp 接入。当前支持 SecurityPolicy None、匿名或用户名密码登录、会话、批量 Value 读写、NodeId 点表采集、按点名写回、JSON 配置和虚拟 Server 联调。

证书加密、订阅/MonitoredItem、Browse 和复杂 ExtensionObject 不在本包范围内。现场若必须使用 SignAndEncrypt,请先在 Server 上开放 None 策略,或单独沟通企业适配。

安装

dotnet add package Zeus.Communications
dotnet add package Zeus.Protocols.OpcUa

OPC UA 通常使用 TCP 4840 端口。通道仍只负责收发字节,HEL/ACK、安全通道、会话和 NodeId 编解码由 OpcUaClient 处理。

无硬件联调

先用内存 Server 验证点表和写回:

using Zeus;

var memory = new OpcUaServerMemory();
memory.SetInt32("ns=2;s=RawTemperature", 253, writable: true);
memory.SetText("ns=2;s=Location", "line-a", writable: true);

await using var app = ZeusHost.Create(builder =>
{
builder.AddAcquisition(TimeSpan.FromMilliseconds(500));
builder.AddVirtualChannel("opcua-link", new OpcUaServerResponder(memory));
builder.AddOpcUa(
"plc",
"opcua-link",
points: map => map
.Int32("temperature", "ns=2;s=RawTemperature", scale: 0.1)
.Writable("temperature")
.String("location", "ns=2;s=Location"));
});

await app.StartAsync();
Console.WriteLine(app.Points.Get<double>("temperature"));
await app.Points.WriteAsync("temperature", 18.6);

OpcUaServerMemory 预置了可写的 ns=2;s=ServerName 和只读的 i=2258(ServerStatus.CurrentTime),也可以通过 SetSetDoubleSetText 等方法添加现场节点。

连接真实设备

把虚拟通道替换成 TCP 客户端:

await using var app = ZeusHost.Create(builder =>
{
builder.AddTcpClient("opcua-link", "192.168.1.20", 4840);
builder.AddOpcUa(
"plc",
"opcua-link",
new OpcUaOptions
{
EndpointUrl = "opc.tcp://192.168.1.20:4840",
SessionName = "zeus-hmi",
Anonymous = true
},
points: map => map
.Double("temperature", "ns=2;s=Temperature")
.Boolean("running", "ns=2;s=Running"));
});

await app.StartAsync();

EndpointUrl 需与远端 Server 暴露的 endpoint 一致;不少现场 Server 会校验 HEL/CreateSession 中的 URL。设备若要求用户名密码,设置 Anonymous = false 并填写 Username / Password。Server 必须允许 SecurityPolicy None。

直接读写

var plc = app.Devices.Get<OpcUaDevice>("plc");

var temperature = await plc.Client.ReadAsync("ns=2;s=Temperature");
var name = await plc.Client.ReadTextAsync("ns=2;s=ServerName");
await plc.Client.WriteAsync("ns=2;s=Temperature", OpcUaValue.Double(18.6));

OpcUaClient 会串行化同一通道上的请求,并在首次读写前自动完成 HEL、OpenSecureChannel、CreateSession 和 ActivateSession。

点表类型

APIOPC UA 类型Zeus 值
BooleanBooleanbool
Int32 / UInt32 / Int64Int32 / UInt32 / Int64对应整数,可选 scale 后为 double
Float / DoubleFloat / Doubledouble
StringStringUTF-8 string
DateTimeDateTimeUTC DateTime
ByteStringByteStringbyte[]
Typed上表任一内置标量按指定类型装箱

数值点支持报警限。写回时 scale 会反向换算,例如采集值 25.3scale: 0.1 会向 Server 写入原始值 253

NodeId 使用规范文本:i=2258ns=2;s=Temperaturens=1;g=...ns=1;b=Base64

JSON 配置

{
"acquisition": {
"intervalMilliseconds": 500,
"pollImmediately": true
},
"channels": [
{
"name": "opcua-link",
"type": "virtual",
"options": {
"responder": "opcua"
}
}
],
"devices": [
{
"name": "plc",
"channel": "opcua-link",
"type": "opcua",
"options": {
"opcUaSessionName": "zeus-hmi"
},
"points": [
{
"name": "serverName",
"options": {
"nodeId": "ns=2;s=ServerName",
"dataType": "string",
"writable": true
}
},
{
"name": "temperature",
"options": {
"nodeId": "ns=2;s=RawTemperature",
"dataType": "int32",
"scale": 0.1,
"lowAlarmLimit": 0,
"highAlarmLimit": 80
}
}
]
}
]
}

设备类型支持 opcuaopc-uaopc.ua。虚拟通道的 responder 使用 opcua;真实设备则把通道改为 tcp,填写设备 IP 和 4840 端口。

排查

现象首先检查
HEL/ACK 超时设备 IP、TCP 4840、防火墙、Server 是否开启
SecurityPolicy 被拒绝Server 未开放 None,或要求证书加密
BadIdentityTokenInvalid用户名密码或匿名策略不匹配
BadNodeIdUnknownNodeId 文本、命名空间索引或标识符与地址空间不一致
BadTypeMismatchdataType 与节点实际内置类型不一致
点表没有值NodeId、数据类型、首次采集时间和点表错误快照

现场排查时打开通道报文追踪,确认 HEL、OPN、MSG 是否成对出现,以及 RequestId 是否匹配。

下一步:JSON 配置TCP 通道通信报文追踪