C# API
Darra PnSlave 的 .NET 类库。产品名 PnSlave;程序集暂名 DarraPnet,命名空间 DarraPnet.Pnet(标识符本波不改)。目标框架 netstandard2.0,是 6 语言 SDK 的基准实现。
using DarraPnet.Pnet;
SDK 只负责【使用】——连接过程映像 + 启停会话 + I/Q 读写。站点名、网卡、槽位由 GUI 导出运行配置 XML,交给 Service 加载。应用不要写用户周期:周期由驱动 / 服务持有。
推荐 — 高性能走结构体 /
PDO.In·PDO.Out;快速原型走地址化IW2/QW0。
快速开始
实时主路径:Connect() 映射 \Device\DarraRT_Pnet_GlobalIO,读写直接拷过程映像。不探测 HTTP,不打开 \\.\DarraRT_Pnet(B1 句柄独占)。
var dev = DarraPnet.Connect(); // 实时主路径,映射 GlobalIO
dev.Start();
dev.PDO.Write(ref tx); // 结构体 → I(只写)
dev.PDO.Read(ref rx); // Q → 结构体(只读)
dev.WriteInt16("IW2", 123);
short q = 0; dev.ReadInt16("QW0", out q); // 读 Q
// WriteInt16("QW0", 5) → NotSupported
dev.Stop(); // 在连只 unmap,不 POST /api/stop
HTTP 备用一行:DarraPnet.Connect("127.0.0.1")(探测 GET /api/info,失败抛 PnetServiceException)。仅 GUI / 远程诊断 / 报警 / I&M 用;实时 IO 仍优先共享内存。
不要 new DarraPnet(...)。native 直连(A1)Start() fail-fast:D_1003 pnet_wrapper_init 与 SDK 私有配置布局不匹配。LoadConfig 只供配置解析 / 变量联想建表。
Connect / Start / Stop
| 成员 | 类型 | 类别 | 读写 | 说明 |
|---|---|---|---|---|
Connect() | DarraPnet | 设备 | 静态 | 实时主路径。不经 Web API,不探测 HTTP。Start 映射 GlobalIO |
Connect(string host, int port = DefaultServicePort) | DarraPnet | 设备 | 静态 | HTTP 备用。创建时探测 GET /api/info。host 只应填 127.0.0.1 |
Start() | PnetErrorCode | 设备 | 写 | 映射 GlobalIO;已映射幂等返回 Success。Section 打不开才备用 HTTP 挂接 |
Stop() | void | 设备 | 写 | 卸载映射。主站在连(Connected == true)只 unmap,不 POST /api/stop,不撕 AR。Stop 后本实例不可复用 |
Dispose() | void | 设备 | 写 | 等价 Stop + 释放(幂等) |
DefaultServicePort | int | 设备 | 只读 | HTTP 备用端口 18840(编译期常量) |
IsServiceMode | bool | 设备 | 只读 | 是否 Connect 会话(实时 / HTTP 备用均为 true) |
IsRunning | bool | 设备 | 只读 | 本会话是否已成功 Start(本地标志;运行态以 Connected / State 为准) |
生命周期:Connect → Start → [I/Q 读写] → Stop / Dispose。数据用属性(非 GetXxx),状态返回枚举。
运行态
优先读共享内存头;未映射才走 GET /api/status。
| 成员 | 类型 | 类别 | 读写 | 说明 |
|---|---|---|---|---|
Connected | bool | 设备 | 只读 | 是否已与 IO 控制器建立周期数据交换。映射后读 GlobalIO 头;否则透出服务端 connected |
State | PnetState | 设备 | 只读 | 运行态枚举。服务不可达如实 Unknown,不伪装 None |
ErrorCode | PnetRuntimeErrorCode | 设备 | 只读 | 最近一次运行错误(枚举)。未识别值 Unknown |
ErrorCodeRaw | int | 设备 | 只读 | 运行错误原值(0 = 无错误)。未识别值仍经本属性透传 |
ErrorMessage | string | 设备 | 只读 | 运行错误中文消息 |
LastServiceError | string | 设备 | 只读 | 最近一次 HTTP 备用错误(成功调用清空) |
Connected == true 才表示周期数据在换。IsRunning 只说明本实例已 Start,不等于主站已连。
地址化 I / Q
西门子风格地址,公共解析器 PnetAddress.Parse 单源。字 / 双字偏移即字节偏移,不强制 2 / 4 对齐(IW3 合法)。字 / 双字为大端(PROFINET 网络序)。
| 区域 | 地址格式 | 方向 | 写 | 读 |
|---|---|---|---|---|
| I(输入面) | I0.0 / IB0 / IW2 / ID4 | 设备 → 控制器 | 发送 | 读回本地影子 |
| Q(输出面) | Q0.0 / QB0 / QW2 / QD4 | 控制器 → 设备 | 不支持(NotSupported) | 读控制器数据 |
dev.WriteInt16("IW2", 123); // 写 I
short q = 0;
dev.ReadInt16("QW0", out q); // 读 Q
// dev.WriteInt16("QW0", 5); // → NotSupported
| 成员 | 类型 | 类别 | 读写 | 说明 |
|---|---|---|---|---|
WriteBool(string address, bool value) | PnetErrorCode | IO | 写 | 写位。仅 I;写 Q 返回 NotSupported |
WriteInt16(string address, short value) | PnetErrorCode | IO | 写 | 写字(IW2,大端)。写 QW0 → NotSupported |
WriteInt32(string address, int value) | PnetErrorCode | IO | 写 | 写双字(ID4,大端)。写 Q → NotSupported |
ReadBool(string address, out bool value) | PnetErrorCode | IO | 只读 | 读位(I 影子 / Q 控制器值) |
ReadInt16(string address, out short value) | PnetErrorCode | IO | 只读 | 读字(IW2 / QW0,大端) |
ReadInt32(string address, out int value) | PnetErrorCode | IO | 只读 | 读双字(ID4 / QD4,大端) |
地址化读写返回 PnetErrorCode,不抛出。未 Start 返回 InvalidHandle。
前 4 字节回写是主站行为。 从站不要把 Q 抄回 I,也不要写用户周期做回声。测法:写 I,再读 Q,看主站是否把前 4 字节同步过来。
PDO
通过 dev.PDO 访问(Pdo 是同一实例的别名)。与地址化读写同源同区。无用户周期 API。
方向(2026-08-22 纠正旧倒置):
| 调用 | 方向 | 读写 |
|---|---|---|
PDO.Write(ref tx) | 结构体 → I(设备 → 控制器) | 只写 |
PDO.Read(ref rx) | Q → 结构体(控制器 → 设备) | 只读 |
PDO.Out[i] | 写 I 字节 | 可写 |
PDO.In[i] | 读 Q 字节 | 只读 |
PDO.Outputs | get = I 影子;set = 整段写 I | 读写 |
PDO.Inputs | 当前 Q 快照(每次新拷) | 只读 |
结构体映射
结构体必须 [StructLayout(LayoutKind.Sequential, Pack = 1)]。字段按声明顺序紧排;bool 占 1 字节;字 / 双字 / 8 字节大端。支持数值、bool、枚举、定长 byte[](MarshalAs(ByValArray, SizeConst=N))、嵌套结构体。
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PdoData
{
public byte A; // 偏移 0
public short B; // 偏移 1(大端)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] D; // 偏移 3
}
PdoData tx = new PdoData { A = 1, B = -2, D = new byte[] { 9, 8, 7, 6 } };
dev.PDO.Write(ref tx); // 结构体 → I(只写)
PdoData rx = new PdoData();
dev.PDO.Read(ref rx); // Q → 结构体(只读)
也可 dev.WriteStruct(ref tx) / dev.ReadStruct(ref rx),与上同义。
In / Out
byte q0 = dev.PDO.In[0].Content; // 读 Q
dev.PDO.Out[0].Content = 0x11; // 写 I
short iw = dev.PDO.Out[2].AsInt16; // 大端字
dev.PDO.Out[2].AsInt16 = 123;
// dev.PDO.In[0].Content = 1; // 抛 InvalidOperationException(In 只读)
In[i] / Out[i] 的 i 是过程映像字节偏移。PdoDataItem 另有 AsUInt16 / AsInt32 / AsFloat / GetBit / SetBit(SetBit 仅 Out)。
InputsMapping
将 Q 区映射为结构体快照。无 live 指针(每次从过程映像解码)。可选 1 ms 内部轮询 OnChanged——这是 SDK 内部定时器,不是用户周期 API。
InputPdoInstance<PdoData> mapped = dev.PDO.InputsMapping<PdoData>();
PdoData snap = mapped.ValueSnapshot;
var watch = dev.PDO.InputsMapping<PdoData>(e =>
{
// e.Previous / e.Current / e.Timestamp
});
OutputsMapping(ref T) 与 Write(ref T) 同义(结构体 → I)。
GetFieldAddress
字段名 → I/Q 地址,供地址化读写按字段访问。无参重载默认 Q(与 Read 同向)。
string qAddr = dev.PDO.GetFieldAddress<PdoData>("B"); // "QW1"
string iAddr = dev.PDO.GetFieldAddress<PdoData>("B", PnetAddressArea.Input); // "IW1"
short b;
dev.ReadInt16(qAddr, out b);
dev.WriteInt16(iAddr, 123);
| 成员 | 类型 | 类别 | 读写 | 说明 |
|---|---|---|---|---|
PDO / Pdo | PnetPdo | PDO | 只读 | PDO 子对象(同一实例) |
PDO.Write<T>(ref T data) | PnetErrorCode | PDO | 写 | 结构体 → I |
PDO.Read<T>(ref T data) | PnetErrorCode | PDO | 只读 | Q → 结构体 |
PDO.Out | PdoArrayInstance | PDO | 写 | Out[offset] 写 I |
PDO.In | PdoArrayInstance | PDO | 只读 | In[offset] 读 Q |
PDO.Outputs | byte[] | PDO | 读写 | get = I 影子;set = 写 I |
PDO.Inputs | byte[] | PDO | 只读 | 当前 Q 快照 |
PDO.InputsMapping<T>() | InputPdoInstance<T> | PDO | 只读 | Q → 结构体快照 |
PDO.InputsMapping<T>(onChanged) | InputPdoInstance<T> | PDO | 只读 | 同上 + 内部 1 ms 变化回调 |
PDO.GetFieldAddress<T>(name) | string | PDO | 只读 | 字段 → Q 区地址 |
PDO.GetFieldAddress<T>(name, area) | string | PDO | 只读 | 字段 → I / Q 地址 |
WriteStruct<T>(ref T data) | PnetErrorCode | PDO | 写 | PDO.Write 转发 |
ReadStruct<T>(ref T data) | PnetErrorCode | PDO | 只读 | PDO.Read 转发 |
失败码
两类码不要混用。
调用返回码 PnetErrorCode:Start / 地址化读写 / PDO.Read·Write 的返回值。IO 路径不抛异常。
| 值 | 含义 |
|---|---|
Success (0) | 成功 |
InvalidArgument | 地址非法 / 空缓冲 |
InvalidHandle | 未 Start 或已 Stop |
NotConnected | 无 AR / 未进入数据交换 |
NativeError | 栈 / 服务端错误 |
LengthMismatch | 长度越界 |
NotFound | 地址 / 条目不存在 |
Busy | 上一次操作未完成 |
NotSupported | 写 Q 等不支持的操作 |
NativeDllNotFound | native DLL 未加载 |
PlatformNotSupported | 仅 Windows x64 |
ServiceUnreachable | HTTP 备用探测 / 请求失败 |
运行态码 PnetRuntimeErrorCode(属性 ErrorCode):共享内存头 / GET /api/status 的 errorCode。Ok=0 / ErrInit=-1 / ErrCfg=-2 / ErrThread=-3 / ErrRunning=-4 / ErrPdi=-5 / ErrDap=-6 / Unknown=-10000。
HTTP 备用 Connect("127.0.0.1") 探测失败、以及 Section 打不开后 HTTP Start 失败,抛 PnetServiceException(带 PnetErrorCode + 原因)。实时主路径 Connect() 创建时不抛。
完整示例
using System.Runtime.InteropServices;
using DarraPnet.Pnet;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PdoData
{
public short Status;
public short Command;
}
var dev = DarraPnet.Connect();
if (dev.Start() != PnetErrorCode.Success)
{
Console.WriteLine("映射失败: " + dev.LastServiceError);
return;
}
PdoData tx = new PdoData { Status = 123 };
dev.PDO.Write(ref tx); // 结构体 → I
dev.WriteInt16("IW2", 123); // 地址化写 I
PdoData rx = new PdoData();
dev.PDO.Read(ref rx); // Q → 结构体
short q = 0;
dev.ReadInt16("QW0", out q); // 地址化读 Q
bool onBus = dev.Connected;
PnetRuntimeErrorCode err = dev.ErrorCode;
dev.Stop(); // 在连只 unmap