跳到主要内容

PnSService

PROFINET IO Device 的入口。启动设备、写 Q / 读 I、看运行态和诊断。crate 名 darra_pns,入口类型 PnSService

本 SDK 只做 PROFINET IO Device,不提供其它现场总线协议的入口。

类结构

PnSService                                         // 设备实例

├── process_data() → ProcessData // 写 Q / 读 I
│ ├── inputs_mapping::<T>() / outputs_mapping::<T>() // 0 拷贝,映射一次
│ ├── read_struct / write_struct // 快照,值不会自己变
│ ├── in_i8(offset) … in_bit(offset, bit) // 读 I(按项标量),只读
│ └── out_i8(offset, v) … out_bit(offset, bit, v) // 写 Q(按项标量),可写
├── events() → ServiceEvents // 运行态事件
│ ├── on_connected_changed // 周期交换通断
│ ├── on_state_changed // 运行态变化
│ ├── on_error_occurred // 运行错误
│ └── on_logged // 上面每条都会再打这条
├── variables() → PnSVariables // 变量联想
├── diagnostics() → Diagnostics // 诊断子对象(快照 / IOPS / 报警 / 记录)
│ ├── snapshot() // 诊断快照
│ ├── io_status() // IOPS / IOCS
│ ├── alarms() / alarm_history() // 本机报警 / 历史
│ └── records() // 用户区记录

├── connected() / state() / wait_reason() // 周期交换 / 运行态 / 等待原因
├── ever_connected() / error_code() // 曾经连上 / 运行错误
└── im_data() // I&M 标识

写 Q(模块输出,设备 → 控制器),读 I(模块输入,控制器 → 设备)。扁平方法 diag_snapshot() / alarms() 仍可用,与 diagnostics() 同一数据。

diagnostics() 返回的 Diagnostics 没有在 crate 根重导出:要写出这个类型名就写全路径 darra_pns::service::Diagnosticsuse darra_pns::Diagnostics; 编不过)。PnSService / ServiceResult / ServiceState / ProcessData 这些是重导出过的,直接 use darra_pns::… 就行。

用法

启停

use darra_pns::PnSService;

let mut device = PnSService::new();
device.start()?;

while running {
if !device.connected() {
// device.wait_reason()
continue;
}
device.write_bool("Q0.0", true)?; // 写 Q
let from_plc = device.read_bool("I0.0")?; // 读 I
let _ = from_plc;
}

device.stop()?;

看状态不必 newPnSService::is_service_ready() / service_connected() / service_wait_reason()

过程数据

方式访问说明
过程数据device.process_data().inputs_mapping::<T>() / outputs_mapping::<T>()0 拷贝,映射一次
过程数据device.process_data()快照 read_struct / write_struct、按项标量 in_i8(offset) / out_i16(offset, v) 系列

标识

let im = device.im_data()?;
let name = im.station_name;
let vendor = im.vendor_id;
let product = im.device_id;

过程数据读写

字 / 双字为大端。详见 过程数据

金路径是 0 拷贝:映射一次,源头变字段自己变(T#[repr(C, packed)])。

#[repr(C, packed)]
struct FromPlc {
a: u8,
b: i16,
flag: u8,
}

// 0 拷贝:映射一次
let inp = unsafe { device.process_data().inputs_mapping::<FromPlc>()? };
let outp = unsafe { device.process_data().outputs_mapping::<FromPlc>()? };
let present = inp.a; // 总线刷新后自己变
outp.a = 1; // 直接落 Q 过程映像
let _ = present;

device.process_data().out_u8(0, 0x11)?;
let v = device.process_data().in_u8(0)?;

快照走 read_struct / write_struct(结构体实现 ProcessDataPacked:必写 packed_size / unpack_from / pack_into 三个,另有一个带默认实现pack_to_vec,手写实现不必重复一份)—— 拷出来的实例不会自己变。

诊断与报警

通过 device.diagnostics() 读诊断(只读快照:链路、帧计数、看门狗、抖动与报警观测)。详见 诊断。事件见 事件

let snap = device.diagnostics().snapshot()?;
if !snap.ok {
println!("{}", snap.message);
}

for a in device.diagnostics().alarms()? {
println!("{}", a.message);
}