Rust API 参考
Rust SDK(Cargo 包 darra-pnet)。命名采用 Rust 惯例(snake_case),语义与 C# 页一致。含两种形态:
- 服务模式(推荐):
PnetService单一入口,经 Pnet Service 内置 HTTP API(端口 18840 固定)连接,由服务承载从站运行(纯 stdTcpStreamHTTP/1.1,不依赖 native DLL / libloading); - native 直连(备选):
Device(ffi模块 + libloading 运行时加载DarraPnet.dll)。
单一 PROFINET 网口契约(audit-2026-08-11,用户裁定 2026-08-10):设备对外只开放一个 PROFINET 网口(工业现场常态)。网卡选择在 GUI / Service 配置层完成,服务侧单一权威 =
NetworkInterfaceName(读取链:环境变量DARRA_PNET_NIC> 运行配置 XML<Network/InterfaceName>> appsettings 出厂默认;驱动绑定 / pnal 绑定目标 / MAC 解析同源一致,无双配置歧义)。native 直连模式下配置的网卡名为栈绑定的唯一网口名;EnumerateInterfaces()仅作诊断/枚举用途。
use darra_pnet::{PnetService, Device};
概览
| 类型 | 职责 |
|---|---|
PnetService | 服务模式单一入口:连接服务 18840 / 启停 / 地址化 IO 读写 |
Device | native 直连:打开 / load_config / 启停 / 槽位 IO |
Slave / PnetIo / Alarm / PnetConfig / Diag | native 直连子对象 |
ServiceAddress / ServiceArea | 服务模式地址解析 |
PnetError / ServiceError | 错误类型 |
PnetService — 服务模式单一入口(推荐)
经服务 HTTP API(端口 18840 固定)连接服务,由服务承载从站运行;SDK 与 Web API 同层。启停(POST /api/start|stop)/ IO(GET|POST /api/io)全部经服务转发。
use darra_pnet::{PnetService, DEFAULT_PORT};
let service = PnetService::new("127.0.0.1", DEFAULT_PORT, None);
service.start()?; // POST /api/start
// 地址化读写(ETH 式地址,对齐 C# 基准)
service.write_bool("I0.0", true)?; // 写输入位(设备 → 控制器)
service.write_int16("IW2", counter)?; // 写输入字(大端)
let cmd = service.read_int16("QW0")?; // 读输出字(控制器 → 设备)
let start = service.read_bool("Q0.0")?; // 读输出位
let (io_i, io_q) = service.get_io()?; // 服务端过程映像快照
service.stop()?; // POST /api/stop(幂等)
service.close();
| 成员 | 类型 | 类别 | 读写 | 说明 |
|---|---|---|---|---|
PnetService::new(host, port, api_key) | PnetService | 设备 | 构造 | 连接服务(仅本地回环 127.0.0.1 可达,audit-2026-08-10:服务未开放局域网监听;端口固定 18840) |
DEFAULT_PORT | u16 | 设备 | 只读 | 服务 HTTP API 固定端口 18840(编译期常量) |
start() | ServiceResult<()> | 设备 | 写 | 启动从站(POST /api/start,由服务承载) |
stop() | ServiceResult<()> | 设备 | 写 | 停止从站(POST /api/stop,幂等) |
status() | ServiceResult<ServiceState> | 设备 | 只读 | 服务端运行态(GET /api/status 映射) |
status_ex() | ServiceResult<ServiceStatus> | 设备 | 只读 | 服务端运行态快照(GET /api/status 一次取回 state / connected / errorCode;用户裁定 2026-08-10:错误码是调用契约的一部分,SDK 必须透出服务端 connected / errorCode) |
read_bool(address) | ServiceResult<bool> | IO | 只读 | 读位(I0.0 / Q0.0 / M1.6 / DB1.DBX0.0) |
write_bool(address, value) | ServiceResult<()> | IO | 写 | 写位(I / M / DB;Q 区只读返回 ServiceError::OutputReadOnly) |
read_int16(address) | ServiceResult<i16> | IO | 只读 | 读字(IW2 / QW2 / MW0 / DB1.DBW0,大端) |
write_int16(address, value) | ServiceResult<()> | IO | 写 | 写字(I / M / DB;Q 区只读) |
read_int32(address) | ServiceResult<i32> | IO | 只读 | 读双字(ID4 / QD4 / MD0 / DB1.DBD0,大端) |
write_int32(address, value) | ServiceResult<()> | IO | 写 | 写双字(I / M / DB;Q 区只读) |
get_io() | ServiceResult<(Vec<u8>, Vec<u8>)> | IO | 只读 | 服务端过程映像快照(I 区 / Q 区) |
parse_address(text) | ServiceResult<ServiceAddress> | IO | 静态 | 解析 HSL 式地址(对齐 C# PnetAddress.Parse) |
is_connected() / is_started() | bool | 设备 | 只读 | 会话 / 本地启动状态 |
地址格式(与 C# 基准一致):I0.0 / IB0 / IW2 / ID4(I 区输入面)、Q0.0 / QB0 / QW2 / QD4(Q 区输出面,只读)、M0.0 / MB0 / MW100 / MD0(内部存储)、DB1.DBX0.0 / DB1.DBB2 / DB1.DBW0 / DB1.DBD4(DB 区槽位 1 数据块)。字 / 双字大端序,偏移即字节偏移、不强制 2 / 4 对齐(与 C# 基准一致,IW3 合法);连接服务模式为单一线性过程区,DB 仅槽位 1 有效(槽位 > 1 返回 ServiceError::DbSlotNotSupported)。
status_ex() 返回 ServiceStatus(state / connected / error_code / error_code_enum()):connected = 是否已与 IO 控制器建立周期数据交换(GET /api/status 的 connected 原值),error_code = 最近一次错误码原值(0 = 无错误;非零 = pnet 栈错误码),error_code_enum() = 枚举化映射(PnetRuntimeErrorCode:Ok=0 / ErrInit=-1 / ErrCfg=-2 / ErrThread=-3 / ErrRunning=-4 / ErrPdi=-5 / Unknown=-10000,#[repr(i32)] + From<i32>;未识别值如实返回 Unknown,原始值仍经 error_code 透传)。
Device — native 直连(备选)
use darra_pnet::Device;
let mut device = Device::open()?; // 运行时加载 DarraPnet.dll
device.load_config(r"C:\DARRA\Profinet_Slave\config.xml")?; // 加载运行配置 XML
device.start()?; // 启动协议栈,等待控制器连接
let mut output = vec![0u8; 16];
device.io().read_inputs(&mut output)?; // 控制器 → 设备
device.io().write_outputs(&[1u8, 2, 3, 4])?; // 设备 → 控制器
device.stop()?;
| 成员 | 类型 | 类别 | 读写 | 说明 |
|---|---|---|---|---|
Device::open() | Result<Device> | 设备 | 静态 | 打开设备(当前目录 / PATH 搜索 DarraPnet.dll) |
Device::builder() | DeviceBuilder | 设备 | 静态 | 构建器入口(可指定 DLL 路径) |
load_config(path) | Result<()> | 配置 | 写 | 加载运行配置 XML(GUI 输出) |
start() / stop() | Result<()> | 设备 | 写 | 启动 / 停止协议栈 |
application_ready() | Result<()> | 从站 | 写 | 应用就绪 |
state() | DeviceState | 设备 | 只读 | 设备状态 |
slave() | Slave<'_> | 从站 | 只读 | 从站控制(站名 / 网络配置 / 状态) |
io() | PnetIo<'_> | IO | 只读 | 过程数据(read_inputs / write_outputs / 帧) |
alarm() / config() / diag() | Alarm<'_> / PnetConfig<'_> / Diag<'_> | 各域 | 只读 | 报警 / 配置 / 诊断 |
dll_version() | Option<(u16, u16, u16, u16)> | 设备 | 静态 | DLL 版本(未部署返回 None) |
完整示例(服务模式)
use darra_pnet::{PnetService, DEFAULT_PORT};
use std::{thread, time::Duration};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let service = PnetService::new("127.0.0.1", DEFAULT_PORT, None);
service.start()?;
// 地址化周期 IO(大端序)
let mut counter: i16 = 0;
for _ in 0..100 {
service.write_bool("I0.0", counter % 2 == 0)?;
service.write_int16("IW2", counter)?;
let _cmd = service.read_int16("QW0")?;
counter = counter.wrapping_add(1);
thread::sleep(Duration::from_millis(10));
}
service.stop()?;
service.close();
Ok(())
}
Cargo.toml
[package]
name = "darra-pnet"
version = "1.0.0"
edition = "2021"
[dependencies]