AI 提示词
复制下面这一句给 AI
Please read https://opcua.darra.xyz/ai-prompts.txt as your context, then help me ...
AI 会自动读取本 SDK 的 API 上下文, 给你写正确的代码.
Darra OPC UA Client SDK — C# / .NET 提示词
完全免费 / 商用闭源亦可 · NuGet:
Darra.OpcUa
下面是 AI 通过上述 URL 实际读取到的 C# 上下文 (用户也可直接复制使用). 完整文档: C# SDK 文档.
1. 安装
NuGet 安装 (推荐):
dotnet add package Darra.OpcUa
或 Package Manager:
Install-Package Darra.OpcUa
支持框架: .NET Standard 2.0 / .NET 6+ / .NET Framework 4.6.1+
引用包:
using DarraOpcUa_Client;
NuGet 包内含原生 Darra.OpcUa.Core.dll (Stack), 自动复制到消费者 bin, 无需手动管理.
2. 主类 DarraOpcUa — 单一入口
最简连接:
using DarraOpcUa_Client;
using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Connect();
// ... 业务代码
// 离开 using 块自动 Disconnect + Dispose
完整构造函数:
new DarraOpcUa(
string endpointUrl,
MessageSecurityMode securityMode = MessageSecurityMode.None,
string username = null, // 用户名密码 (可选)
string password = null,
uint sessionTimeoutMs = 600_000, // 10 分钟会话超时
uint requestTimeoutMs = 10_000,
uint connectTimeoutMs = 10_000,
string clientCertPath = null, // PFX 证书路径 (Sign 模式必需)
string clientKeyPath = null, // PFX 密码 (字段名复用 client_key_path)
string serverCertPath = null, // 可选, 不提供自动 None→Secure 升级
string securityPolicyUri = null // 默认按 mode 自动选 None / Basic256Sha256
)
索引器节点访问 (索引器 + 懒加载):
var nid = ua.Nodes["i=2258"]; // 标准 NodeId
var node = ua.Nodes["ns=2;s=Counter"]; // 命名空间字符串
var server = ua.Nodes.Server; // i=2253 快捷
var objects = ua.Nodes.Objects; // i=85 快捷
按 BrowsePath 解析:
var node = ua.Resolve("/Objects/Server/ServerStatus/CurrentTime");
// 等同于先 Browse 一层一层找到目标 NodeId
3. Read / Write / Browse
单点读:
using var dv = ua.Read("i=2258");
Console.WriteLine($"值: {dv.Value} 状态: {dv.Status}");
Console.WriteLine($"源时间: {dv.SourceTimestamp:HH:mm:ss.fff}");
// 必须 using ! DataValue 持有 native handle
批量读 (一次 RPC):
var results = ua.ReadMany(new[] { "i=2258", "i=2261", "ns=2;s=Counter" });
foreach (var dv in results) using (dv) { Console.WriteLine(dv.Value); }
写:
var v = new Variant();
v.SetInt32(42);
StatusCode st = ua.Write("ns=2;s=Counter", v);
if (st != StatusCode.Good) Console.WriteLine($"写入失败: {st}");
浏览:
foreach (var r in ua.Browse("i=85"))
Console.WriteLine($"{r.NodeId,-20} [{r.NodeClass}] {r.DisplayName}");
批量浏览 (一次 RPC):
var refsList = ua.BrowseMany(new[] { "i=85", "i=86" });
// refsList[0] = i=85 子节点, refsList[1] = i=86 子节点
分页浏览 (大数据集):
var page = ua.BrowseWithPaging("ns=3;s=BigTree");
while (page.ContinuationPoint != null)
{
foreach (var r in page.References) Console.WriteLine(r);
page = ua.BrowseNext(page.ContinuationPoint);
}
路径批量解析:
var results = ua.TranslateBrowsePaths(new[]
{
("i=85", "/Server/ServerStatus/CurrentTime"),
("i=85", "/Server/NamespaceArray"),
});
4. 订阅 + MonitoredItem
最简订阅:
using var sub = ua.CreateSubscription(500); // 500ms publish interval
sub.DataChanged += (s, e) =>
{
Console.WriteLine($"{e.NodeId} = {e.ValueString} [{e.Status}]");
};
sub.Add("i=2258"); // 订阅 ServerStatus.CurrentTime
sub.Add("ns=2;s=Counter"); // 多个 MI
await Task.Delay(10000); // 收 10 秒数据
批量加 MI (一次 RPC):
var results = sub.AddMany(new[] { "i=2258", "ns=2;s=A", "ns=2;s=B" });
foreach (var (handle, status) in results)
Console.WriteLine($"mi={handle} status={status}");
修改订阅参数:
var revised = sub.Modify(publishingIntervalMs: 1000);
Console.WriteLine($"实际生效 publish interval: {revised.RevisedPublishingIntervalMs}ms");
设置 MI Monitoring Mode:
sub.SetMonitoringMode(MonitoringMode.Disabled, new[] { miHandle1, miHandle2 });
// 暂停某些 MI 推送, 但保留订阅
修改 MI 采样率 / 队列:
sub.ModifyMonitoredItems(new[]
{
new MonitoredItemModifyRequest { MonitoredItemHandle = mi1, SamplingIntervalMs = 100 }
});
移除 MI:
sub.Remove(miHandle);
sub.RemoveByNodeId("i=2258");
5. 方法调用 (Method Call)
var inputs = new[] { new Variant().SetInt32(3), new Variant().SetInt32(5) };
var outputs = ua.Call("ns=2;s=Demo", "ns=2;s=Add", inputs);
foreach (var v in outputs) using (v) { Console.WriteLine(v); }
6. 历史读 (5 模式)
// Raw 原始数据
var raw = ua.ReadHistoryRaw("ns=3;s=Counter", DateTime.UtcNow.AddHours(-1), DateTime.UtcNow);
// 修改记录
var modified = ua.ReadHistoryModified(nid, start, end);
// 指定时间点 (内插)
var atTime = ua.ReadHistoryAtTime(nid, new[] { t1, t2, t3 });
// 聚合 (Average / Min / Max / Count)
var processed = ua.ReadHistoryProcessed(nid, start, end,
TimeSpan.FromMinutes(1), HistoryAggregate.Average);
// 历史事件
var events = ua.ReadHistoryEvents(nid, start, end);
// 写历史
var values = new[] { /* DataValue ... */ };
ua.UpdateHistory(nid, HistoryUpdateType.Insert, values);
7. Discovery (无 Session)
var endpoints = OpcUaDiscovery.GetEndpoints("opc.tcp://server:4840");
foreach (var ep in endpoints)
Console.WriteLine($"{ep.SecurityMode} {ep.SecurityPolicyUri}");
// 找最强加密 endpoint
var best = endpoints.Where(e => e.SecurityMode == MessageSecurityMode.SignAndEncrypt)
.OrderByDescending(e => e.SecurityLevel).FirstOrDefault();
8. 加密连接 (Sign / SignAndEncrypt)
using var ua = new DarraOpcUa("opc.tcp://server:4840",
securityMode: MessageSecurityMode.SignAndEncrypt,
securityPolicyUri: "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
clientCertPath: @"C:\certs\my_client.pfx",
clientKeyPath: "pfx_password");
ua.Connect();
// 内部走 OPN(SignAndEncrypt) + RSA-OAEP-SHA1 + AES-256-CBC + HMAC-SHA256
// 用户名 / 证书 UserToken
using var ua2 = new DarraOpcUa("opc.tcp://server:4840",
username: "admin", password: "secret");
9. 事件订阅 (Events 集合)
ua.Events.Connected += (s, e) => Console.WriteLine($"✓ {e.EndpointUrl}");
ua.Events.Disconnected += (s, e) => Console.WriteLine($"✗ {e.StatusCode}");
ua.Events.Reconnecting += (s, e) => Console.WriteLine("正在重连...");
ua.Events.KeepAlive += (s, e) => Console.WriteLine($"心跳: {e.ServerTimeUtc}");
ua.Events.CommunicationError += (s, e) => Console.WriteLine($"通讯错误: {e.Message}");
ua.Events.Any += (s, e) => // 统一通道, 收所有事件
Console.WriteLine($"[{e.Category}] [{e.Severity}] {e.Message}");
10. C# 易错点 (AI 生成代码常踩的坑)
| 错 | 对 | 理由 |
|---|---|---|
var dv = ua.Read("i=2258"); ... | using var dv = ua.Read("i=2258"); ... | DataValue 持有 native handle, 必须 Dispose |
if (dv.Status == 0) ... | if (dv.IsGood) ... 或 if (dv.Status == StatusCode.Good) | 用枚举/属性, 不要直接比 0 |
var v = new Variant(42); | var v = new Variant(); v.SetInt32(42); | SDK 用显式 setter, 避免类型推断误判 |
| 没 try/catch | 包裹 try { ... } catch (OpcUaException ex) { ... } | 网络/协议错误统一 OpcUaException |
sub.Start(); (不存在) | sub = ua.CreateSubscription(500); sub.Add("...") | AutoPublish 默认开, 自动启动 |
| 回调里直接更新 UI | Invoke(() => UpdateUI(...)) | 回调在非 UI 线程, WinForms/WPF 必须 Invoke |
ua.Call("ns=2;s=Add", 3, 5); (缺 ObjectNodeId) | ua.Call("ns=2;s=ParentObject", "ns=2;s=Add", v3, v5) | Method Call 需要 (ObjectNodeId, MethodNodeId) 二元组 |
11. 完整 Demo (Connect + Browse + Subscribe + 优雅退出)
using DarraOpcUa_Client;
class Program
{
static async Task Main()
{
using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Events.Connected += (s, e) => Console.WriteLine($"已连接 {e.EndpointUrl}");
ua.Events.Disconnected += (s, e) => Console.WriteLine("已断开");
try
{
ua.Connect();
// 列命名空间
for (int i = 0; i < ua.Namespaces.Count; i++)
Console.WriteLine($"ns={i}: {ua.Namespaces[i]}");
// 浏览根
foreach (var r in ua.Browse("i=85"))
Console.WriteLine($" {r.NodeId,-20} [{r.NodeClass}]");
// 订阅 ServerStatus.CurrentTime
using var sub = ua.CreateSubscription(500);
sub.DataChanged += (s, e) =>
Console.WriteLine($"⏰ {e.NodeId} = {e.ValueString}");
sub.Add(WellKnownNodes.Server_ServerStatus_CurrentTime);
Console.WriteLine("收 5 秒数据后退出...");
await Task.Delay(5000);
}
catch (OpcUaException ex)
{
Console.WriteLine($"❌ {ex.StatusCode}: {ex.Message}");
}
}
}
12. WellKnownNodes (内置常量)
| 常量 | NodeId |
|---|---|
WellKnownNodes.Server | i=2253 |
WellKnownNodes.Server_ServerStatus | i=2256 |
WellKnownNodes.Server_ServerStatus_CurrentTime | i=2258 |
WellKnownNodes.Server_ServerStatus_State | i=2259 |
WellKnownNodes.Server_ServerStatus_BuildInfo_ProductName | i=2261 |
WellKnownNodes.Server_NamespaceArray | i=2255 |
WellKnownNodes.ObjectsFolder | i=85 |
WellKnownNodes.RootFolder | i=84 |
13. StatusCode 速查 (常见错误)
| StatusCode | 值 | 含义 |
|---|---|---|
StatusCode.Good | 0x00000000 | 成功 |
StatusCode.BadCommunicationError | 0x80050000 | 网络通讯失败 |
StatusCode.BadConnectionRejected | 0x80AC0000 | server 拒绝 |
StatusCode.BadCertificateInvalid | 0x80120000 | 证书无效 |
StatusCode.BadIdentityTokenRejected | 0x80210000 | 用户名/证书被拒 |
StatusCode.BadSessionIdInvalid | 0x80250000 | 会话已失效 |
StatusCode.BadNodeIdUnknown | 0x80340000 | NodeId 不存在 |
StatusCode.BadAttributeIdInvalid | 0x80350000 | 属性不存在 |
StatusCode.BadNotReadable | 0x80380000 | 节点不可读 |
StatusCode.BadNotWritable | 0x80390000 | 节点不可写 |
StatusCode.BadServiceUnsupported | 0x800B0000 | server 不支持该服务 |