跳到主要内容

C# API 参考

C# SDK(程序集 DarraPnet,命名空间 DarraPnet.Pnet,netstandard2.0)是 6 语言 SDK 的基准实现,其他语言用法与命名对齐本页。P/Invoke 声明集中在 PnetDll.cs(仿 ETH DLL.cs 集中声明风格),序列导出自原生 DarraPnet.dll

using DarraPnet.Pnet;

概览

SDK 为 ETH 式单一入口(仿 DarraEtherCAT.Slave 形态):一切操作从 DarraPnet 实例出发。SDK 不做配置(配置由 GUI 导出运行配置 XML → Service 加载),只负责【使用】——启停 + IO 读写。站点名是设备身份(与控制器工程中的设备名一致),由 GUI / Service 配置,SDK 构造不再传;网卡同理由 GUI / Service 配置选择(单网口从站,SDK 仅枚举供诊断)。

单一 PROFINET 网口契约(audit-2026-08-11,用户裁定 2026-08-10):设备对外只开放一个 PROFINET 网口(工业现场常态 —— 设备只有一个 PROFINET 口)。网卡选择在 GUI / Service 配置层完成,服务侧单一权威 = NetworkInterfaceName(读取链:环境变量 DARRA_PNET_NIC > 运行配置 XML <Network/InterfaceName> > appsettings 出厂默认;驱动绑定 / pnal 绑定目标 / MAC 解析同源一致,无双配置歧义)。SDK EnumerateInterfaces() 仅作诊断/枚举用途,不改变服务侧绑定目标。

类型职责可用模式
DarraPnet单一入口:构造(native 直连)/ LoadConfig / Connect(服务模式)+ Start / Stop + 地址化 IO 读写全部
PnetPdoPdoPDO 结构体映射(过程数据 ↔ 结构体)全部
PnetVariablesVariables变量联想(从运行配置 XML 自动建表)全部
PnetSlaveSlave从站控制(状态 / 插拔 / 应用就绪)仅 native 直连
PnetIoIo槽位过程数据 + 底层地址化读写仅 native 直连
PnetAlarmAlarm报警发送 / ACK 应答仅 native 直连
PnetConfigConfig运行配置只读(诊断用)仅 native 直连
PnetDiagDiag诊断计数 / 诊断条目仅 native 直连

连接服务模式无本地子对象:状态 / 报警 / 诊断由服务端单一权威维护,访问 Slave / Io / Alarm / Config / DiagNotSupportedException,请经服务 HTTP API 查询(GET /api/status / GET /api/alarms)。地址化 IO 读写两种模式均可用(服务模式经 GET/POST /api/io 转发)。

创建实例(三种方式,构造决定模式)

// 1) native 直连快速测试:仅传 IP,站点名/网卡/掩码/网关取默认(正式部署走 LoadConfig)
var pnet = new DarraPnet("192.168.1.100");

// 2) native 直连正式部署:加载 GUI 导出的运行配置 XML
// (站点名/网卡/IP/槽位布局全从 XML 读,用户不接触配置对象)
var pnet = DarraPnet.LoadConfig(@"C:\Darra\Profinet\device.xml");

// 3) 连接服务模式(推荐):经服务 HTTP API(端口 18840 固定)连接,由服务承载从站运行
// ★ 服务仅监听本地回环 127.0.0.1(audit-2026-08-10):未开放局域网监听,
// 仅本地回环可达;host 传非回环地址连接必失败
var pnet = DarraPnet.Connect("127.0.0.1"); // 本地服务(唯一可达地址)
成员类型类别读写说明
new DarraPnet(string ipAddress)DarraPnet设备构造native 直连:仅传 IP,其余取默认(快速测试)
LoadConfig(string xmlPath)DarraPnet设备静态native 直连:加载运行配置 XML(PNETConfig schema,GUI ConfigWriter 输出)
Connect(string host, int port = DefaultServicePort)DarraPnet设备静态连接服务模式:经服务 HTTP API 转发启停 / IO(SDK 与 Web API 同层,由服务运行)
DefaultServicePortint设备只读服务 HTTP API 固定端口 18840(编译期常量,不可配置)
IsServiceModebool设备只读是否连接服务模式
ServiceHost / ServicePortstring / int设备只读服务主机 / 端口(连接服务模式)
Start()PnetErrorCode设备启动从站:native 初始化栈 + 注册过程映像槽位 + 1 ms 周期驱动线程;服务模式 POST /api/start
Stop()void设备停止(幂等;Stop 后本实例不可复用,需重新创建)
Dispose()void设备等价 Stop + 释放(幂等)
IsRunningbool设备只读是否运行中
StatePnetState设备只读当前运行状态(服务模式映射 GET /api/status)
Connectedbool设备只读是否已与 IO 控制器建立周期数据交换(服务模式,GET /api/status 的 connected 原值;与服务端状态同源透出,用户裁定 2026-08-10)
ErrorCodeint设备只读最近一次错误码原值(服务模式,GET /api/status 的 errorCode 原值透传;0 = 无错误,非零 = pnet 栈错误码,语义见 PnetRuntimeErrorCode
ErrorCodeEnumPnetRuntimeErrorCode设备只读最近一次错误码的枚举化映射(ErrorCode 原值映射;未识别值如实返回 Unknown,原始值仍经 ErrorCode 透传)
VersionDarraPnetVersionInfo设备只读SDK / native 栈版本信息(加载失败为 null)
EnumerateInterfaces()IReadOnlyList<string>设备静态枚举本机物理网卡(诊断用;正式部署网卡由 GUI / Service 配置选择)
LastServiceErrorstring设备只读最近一次连接服务模式 HTTP 错误(成功调用时清空)

生命周期:New → Start → [IO 读写] → Stop / Dispose。数据获取用属性(非 GetXxx),状态返回枚举(ETH SDK 规范)。

地址化读写(ETH 式地址,公共解析器 PnetAddress.Parse 单源)

西门子风格地址,统一由公共解析器 PnetAddress.Parse 解析(单源原则),两种运行模式语义一致:

区域地址格式方向语义
I 区(输入面)I0.0 / IB0 / IW2 / ID4设备 → 控制器;Write* 发送,Read* 读回本地影子值
Q 区(输出面)Q0.0 / QB0 / QW2 / QD4控制器 → 设备;Read* 读取,Write* 不支持(返回 NotSupported
M 区(内部存储)M0.0 / MB0 / MW0 / MD0映射输入面,仅本机可见(仿 PLC M 区)
DB 区(槽位数据块)DB1.DBX0.0 / DB1.DBB2 / DB1.DBW0 / DB1.DBD4槽位 1 数据块;写 = 输入面,读 = 输出面(DB 编号 = 槽位号)
  • 字 / 双字偏移即字节偏移,不强制 2 / 4 对齐(HSL 习惯:IW3 / DB1.DBW3 合法,与 Pdo.GetFieldAddress 生成的未对齐字段地址自洽)。
  • 字 / 双字均为大端序(PROFINET 网络序)。
  • 连接服务模式为服务端单一线性过程区(无槽位概念),DB 仅槽位 1 有效(槽位 > 1 返回 NotFound)。
  • 地址化读写返回 PnetErrorCode(SDK 调用返回错误码的契约,不抛出)。
// 写输入位 I0.0(设备 → 控制器)
pnet.WriteBool("I0.0", true);

// 写输入字 IW2(设备 → 控制器,大端序)
pnet.WriteInt16("IW2", counter);

// 读输出字 QW0(控制器 → 设备)
pnet.ReadInt16("QW0", out short output);

// 读输出位 Q0.0(控制器 → 设备)
pnet.ReadBool("Q0.0", out bool startCmd);
成员类型类别读写说明
ReadBool(string address, out bool value)PnetErrorCodeIO只读读位(I / Q / M / DB 位地址)
WriteBool(string address, bool value)PnetErrorCodeIO写位(I / M / DB;Q 区只读返回 NotSupported
ReadInt16(string address, out short value)PnetErrorCodeIO只读读字(IW2 / QW2 / MW0 / DB1.DBW0,大端)
WriteInt16(string address, short value)PnetErrorCodeIO写字(I / M / DB;Q 区只读返回 NotSupported
ReadInt32(string address, out int value)PnetErrorCodeIO只读读双字(ID4 / QD4 / MD0 / DB1.DBD0,大端)
WriteInt32(string address, int value)PnetErrorCodeIO写双字(I / M / DB;Q 区只读返回 NotSupported

PDO 结构体映射(ETH 式 PDO 子对象)

Pdo 把 C# 结构体按 [StructLayout(LayoutKind.Sequential, Pack = 1)] 偏移映射到过程映像字节区,与地址化读写同源同区

  • Pdo.Read(ref T):结构体 → 输入过程映像(I 区,设备 → 控制器);
  • Pdo.Write(ref T):输出过程映像(Q 区,控制器 → 设备)→ 结构体;
  • Pdo.GetFieldAddress<T>(name):字段名 → I/Q 地址(供地址化读写按字段访问)。
[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 } };
pnet.Pdo.Read(ref tx); // 或 pnet.ReadStruct(ref tx)

// 接收:输出过程映像(控制器 → 设备)→ 结构体
PdoData rx = new PdoData();
pnet.Pdo.Write(ref rx); // 或 pnet.WriteStruct(ref rx)

// 字段 → 地址(供地址化读写)
string addr = pnet.Pdo.GetFieldAddress<PdoData>("B"); // "IW1"
pnet.ReadInt16(addr, out short b);
成员类型类别读写说明
Pdo.Read<T>(ref T data)PnetErrorCodePDO结构体 → 输入过程映像(设备 → 控制器)
Pdo.Write<T>(ref T data)PnetErrorCodePDO只读输出过程映像 → 结构体(控制器 → 设备)
Pdo.GetFieldAddress<T>(string fieldName)stringPDO只读字段名 → I 区地址(默认 Input)
Pdo.GetFieldAddress<T>(string fieldName, PnetAddressArea area)stringPDO只读字段名 → I / Q 区地址(显式方向)
ReadStruct<T>(ref T data)PnetErrorCodePDOPdo.Read 便捷转发
WriteStruct<T>(ref T data)PnetErrorCodePDO只读Pdo.Write 便捷转发

支持字段类型:数值(sbyte / byte / short / ushort / int / uint / long / ulong / float / double)/ bool(1 字节)/ 枚举 / 定长 byte[](MarshalAs(UnmanagedType.ByValArray, SizeConst=N))/ 嵌套结构体(递归)。字 / 双字 / 8 字节均为大端序。结构体跨槽位边界时按槽位分段读写。

变量联想(仿 PLC SymbolTree 层级联想)

Variables 懒加载解析 LoadConfig 已加载的运行配置 XML(SlotLayout 的模块 / 子模块 / IoItem 名称 → 变量表;未走 LoadConfig 或 XML 无 SlotLayout 时为空表)。

// 层级联想:空前缀 = 全部;输 "X" 只匹配当前层,不命中深层(仿 SymbolTree)
foreach (PnetVariable v in pnet.SuggestVariables("数字量模块."))
{
Console.WriteLine($"{v.Name} = {v.Address} ({v.Direction})");
}

// 精确查找(完整层级名,大小写不敏感)→ 含地址,供 Read*/Write* 直接使用
PnetVariable v = pnet.GetVariable("输入输出模块 1.数字量 IO.数字输出 0");
pnet.WriteBool(v.BitAddress, true);
成员类型类别读写说明
VariablesPnetVariables变量只读变量表子对象(懒加载)
SuggestVariables(string prefix)IReadOnlyList<PnetVariable>变量只读前缀层级联想("模块.子模块." 下钻)
GetVariable(string name)PnetVariable变量只读精确查找(完整层级名),未找到返回 null

PnetVariable

成员类型类别读写说明
Namestring变量只读完整层级名(模块.子模块.IoItem,. 为层级分隔符)
Addressstring变量只读规范地址(IB/IW/IDQB/QW/QD,按长度与对齐推导)
BitAddressstring变量只读位地址(字节第 0 位),供 ReadBool / WriteBool 直接使用
DataTypePnetAddressDataType变量只读数据类型(字节 / 字 / 双字)
DirectionPnetIoDirection变量只读方向(输入 = 设备 → 控制器;输出 = 控制器 → 设备)
Slot / Subslot / Offset / Lengthushort / ushort / int / int变量只读槽位 / 子槽位 / 子槽内偏移 / 数据长度

子对象(ETH 式,仅 native 直连模式)

连接服务模式无本地子对象(状态 / 报警 / 诊断由服务端单一权威维护),访问抛 NotSupportedException

Slave — 从站控制

pnet.Slave.PlugSubmodule(1, 1, 1, 1, PnetIoDirection.InOut, 8, 8);
pnet.Slave.ApplicationReady();
var state = pnet.Slave.State;
成员类型类别读写说明
StatePnetState从站只读运行态:None / Idle / Connecting / Parameterized / ApplicationReady / DataExchange / Aborted
LastNativeResultint从站只读最近一次原生调用结果
Arepuint从站只读当前 AR 标识(未连接为 uint.MaxValue
ApplicationReady()PnetErrorCode从站应用就绪(APPLRDY,参数交换完成)
Abort()PnetErrorCode从站中止连接(释放 AR)
FactoryReset()PnetErrorCode从站恢复出厂设置
PlugModule(ushort slot, uint moduleIdent, uint api = 0)PnetErrorCode从站插入模块
PlugSubmodule(slot, subslot, moduleIdent, submoduleIdent, direction, lengthInput, lengthOutput)PnetErrorCode从站插入子模块(注册过程映像)
PullModule / PullSubmodulePnetErrorCode从站拔出模块 / 子模块
GetArErrorCodes(out errorClass, out errorCode)PnetErrorCode从站只读读取 AR 错误码(诊断)

Io — 过程数据(槽位级)

pnet.Io.RegisterSubslot(0, 1, 1, PnetIoDirection.InOut, 8, 8);
pnet.Io.WriteInput(1, 1, data); // 设备 → 控制器
pnet.Io.ReadOutput(1, 1, buf, out iops, out newData); // 控制器 → 设备
成员类型类别读写说明
RegisterSubslot(api, slot, subslot, direction, inputLength, outputLength)voidIO注册槽位并建过程映像缓冲(线性化映射按注册顺序拼接)
RegisteredSlotCountintIO只读已注册槽位数
WriteInput(slot, subslot, data, iops)PnetErrorCodeIO写输入区(设备 → 控制器),周期发出
ReadOutput(slot, subslot, data, out iops, out newData)PnetErrorCodeIO只读读输出区(控制器 → 设备),含质量与新增标志
ReadInputIocs(slot, subslot, out iocs)PnetErrorCodeIO只读读输入区质量状态(IOPS)
WriteOutputIocs(slot, subslot, iocs)PnetErrorCodeIO主动设置输出区 IOCS(默认驱动按接收结果维护)

Alarm — 报警

pnet.Alarm.SendProcessAlarm(new PnetLocation(0, 1, 1, 0), 0x0001, payload);
pnet.Alarm.SendAck(new PnetLocation(0, 1, 1, 0), PnetAlarmType.Process, sequenceNumber);
成员类型类别读写说明
SendProcessAlarm(PnetLocation location, ushort payloadUsi = 0, byte[] payload = null)PnetErrorCode报警发送过程报警(USI + 负载;在途未确认时返回 Busy
SendAck(PnetLocation location, PnetAlarmType alarmType, ushort sequenceNumber)PnetErrorCode报警应答控制器报警(超时未应答控制器会重发)
IsSendInFlightbool报警只读是否有报警发送在途(等待 ACK)

Config — 运行配置(只读诊断)

Console.WriteLine(pnet.Config.StationName);
Console.WriteLine(pnet.Config.IpAddress);
成员类型类别读写说明
StationNamestring配置只读站点名(PROFINET 设备名,单源 = GUI → 运行配置 XML)
ProductNamestring配置只读产品名
VendorId / DeviceIdushort / ushort配置只读厂商 / 设备标识(与 GSDML 一致)
MainNetifNamestring配置只读绑卡网卡名
IpAddress / IpMask / IpGatewayIPAddress配置只读网络配置
TickUsuint配置只读周期(µs,默认 1000 = 1 ms)
MinDeviceIntervalushort配置只读最小数据交换周期(31.25 µs 单位,RT Class 1 最小 32 = 1 ms)

用户不配置(配置由 GUI 导出 XML 经 LoadConfig 加载),仅诊断用。

Diag — 诊断

Console.WriteLine(pnet.Diag.AlarmSendCount);
pnet.Diag.AddStandardDiag(...);
成员类型类别读写说明
AlarmSendCount / AlarmSendFailedCountulong诊断只读报警发送计数 / 失败数
CycleCountulong诊断只读周期驱动计数
ActiveDiagCountint诊断只读存活诊断条目数
AddStandardDiag / UpdateStandardDiag / RemoveStandardDiagPnetErrorCode诊断标准格式诊断条目管理
AddUsiDiag(PnetLocation location, ushort usi, byte[] manufacturerData)PnetErrorCode诊断厂商特定(USI)诊断
ResetCounters()void诊断复位计数

枚举速查

枚举说明
PnetStateNone / Idle / Connecting / Parameterized / ApplicationReady / DataExchange / Aborted / Resetting / Unknown从站运行态
PnetErrorCodeSuccess / InvalidArgument / InvalidHandle / NotConnected / NativeError / LengthMismatch / NotFound / Busy / NotSupported / NativeDllNotFound / PlatformNotSupportedSDK 调用错误码
PnetRuntimeErrorCodeOk / ErrInit / ErrCfg / ErrThread / ErrRunning / ErrPdi / Unknown服务端运行错误码(ErrorCode 枚举化,值对齐服务端 pnet_stack_err_tOk=0 / ErrInit=-1 / ErrCfg=-2 / ErrThread=-3 / ErrRunning=-4 / ErrPdi=-5 / Unknown=-10000)
PnetAddressAreaInput / Output / Memory / Db地址区域
PnetAddressDataTypeBit / Byte / Word / DWord地址数据类型
PnetIoDirectionNone / Input / Output / InOutIO 数据方向
PnetIoxsBad / Good过程数据质量状态(线上固定不可改)
PnetAlarmTypeDiagnosis / Process / Pull / Plug / Status / Update / Redundancy / ControllerProcess / Manufacturer报警类型

完整示例(服务模式)

using DarraPnet.Pnet;

// 1. 连接服务模式(经服务 HTTP 18840 转发,由服务承载从站运行)
// ★ 服务仅监听本地回环 127.0.0.1(audit-2026-08-10):未开放局域网监听,
// 仅本地回环可达;host 传非回环地址连接必失败
using (DarraPnet.Pnet.DarraPnet pnet = DarraPnet.Pnet.DarraPnet.Connect("127.0.0.1"))
{
// 2. 启动从站(POST /api/start)
if (pnet.Start() != PnetErrorCode.Success)
{
Console.WriteLine("启动失败: " + pnet.LastServiceError);
return;
}

// 3. 周期地址化读写(设备 → 控制器 输入面 / 控制器 → 设备 输出面)
short counter = 0;
while (pnet.State == PnetState.Idle || pnet.State == PnetState.DataExchange)
{
pnet.WriteBool("I0.0", counter % 2 == 0); // 写输入位
pnet.WriteInt16("IW2", counter); // 写输入字(大端)
pnet.ReadInt16("QW0", out short output); // 读输出字(控制器 → 设备)
counter++;
Thread.Sleep(10);
}
}
// 4. Dispose 自动 Stop(POST /api/stop,幂等)

native 直连示例(LoadConfig)

using DarraPnet.Pnet;

using (DarraPnet.Pnet.DarraPnet pnet = DarraPnet.Pnet.DarraPnet.LoadConfig(@"C:\Darra\Profinet\device.xml"))
{
if (pnet.Start() != PnetErrorCode.Success) return;

// 变量联想(SlotLayout 自动建表,地址供 Read*/Write* 直接使用)
PnetVariable v = pnet.GetVariable("输入输出模块 1.数字量 IO.数字输出 0");
pnet.WriteBool(v.BitAddress, true);

// PDO 结构体映射(结构体 → 输入过程映像)
PdoData tx = new PdoData { A = 1, B = -2 };
pnet.Pdo.Read(ref tx);
}

相关