PDO 输入输出
PDO(Process Data Object)过程数据的读写方式汇总。所有方法通过 slave.PDO 访问。
PDO 周期与邮箱的关系
邮箱(SDO/CoE/FoE 等)的入站读取通过 Piggyback 机制附带在 PDO 帧中,不占用额外周期时间。
每个 PDO 周期的 LRW 帧自动携带 mbxstatus。当检测到从站有待读邮箱时,下一周期的 PDO 帧附带 FPRD 数据报读取邮箱内容(与 DC FRMW 相同方式)。邮箱响应延迟约 2 个 PDO 周期。
出站邮箱(如 SDO 写入)仍为独立帧,系统根据 PDO 周期自适应调度:
| PDO 周期 | 出站邮箱策略 | 说明 |
|---|---|---|
| 高速 (<2ms) | 降频处理,每 ~5ms 处理一次 | 避免阻塞帧拉长 PDO 周期 |
| 正常 (2-10ms) | 每周期处理 | 每周期 1-4 条出站邮箱操作 |
| 慢速 (>10ms) | 每周期批量处理 | 充分利用富余时间 |
推荐
高性能场景使用 结构体映射(零拷贝),快速原型使用 索引访问。
结构体映射(零拷贝)
InputsMapping<T>()
public ref T InputsMapping<T>() where T : struct
将输入 PDO 数据映射为结构体引用(零拷贝,约 5-10 纳秒)。
说明: 结构体大小必须与 Ibytes 完全匹配。返回的引用直接指向 IOmap 内存。
示例:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoInput
{
public ushort StatusWord;
public int ActualPosition;
public int ActualVelocity;
}
ref ServoInput input = ref slave.PDO.InputsMapping<ServoInput>();
Console.WriteLine($"当前位置: {input.ActualPosition}");
InputsMapping<T>(onChanged)
public InputPdoInstance<T> InputsMapping<T>(Action<PdoStructChangedEventArgs<T>>? onChanged = null) where T : struct
public InputPdoInstance<T> InputsMapping<T>(Action onChanged) where T : struct
将输入 PDO 数据映射为结构体实例,支持数据变化回调。
返回值: InputPdoInstance<T> — 通过 .Value 获取当前输入 PDO 结构体引用(零拷贝)。
回调参数:
public sealed class PdoStructChangedEventArgs<T> : EventArgs where T : struct
{
public ushort SlaveIndex { get; } // 从站索引
public bool IsInput { get; } // 是否为输入PDO
public T Previous { get; } // 变化前的值
public T Current { get; } // 变化后的值
public DateTime Timestamp { get; } // 时间戳
}
示例:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoInput
{
public ushort StatusWord;
public int ActualPosition;
public int ActualVelocity;
}
var inputInstance = slave.PDO.InputsMapping<ServoInput>(e =>
{
Console.WriteLine($"位置变化: {e.Previous.ActualPosition} -> {e.Current.ActualPosition}");
});
ref ServoInput input = ref inputInstance.Value;
InputsSliceMapping<T>(int offset)
public ref T InputsSliceMapping<T>(int offset) where T : struct
将输入 PDO 数据的指定偏移位置映射为结构体引用。用于模块化设备。
OutputsMapping<T>()
public ref T OutputsMapping<T>() where T : struct
将输出 PDO 数据映射为结构体引用(零拷贝)。结构体大小必须与 Obytes 匹配。
示例:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoOutput
{
public ushort ControlWord;
public int TargetPosition;
public int TargetVelocity;
}
ref ServoOutput output = ref slave.PDO.OutputsMapping<ServoOutput>();
output.ControlWord = 0x000F;
output.TargetPosition = 10000;
OutputsSliceMapping<T>(int offset)
public ref T OutputsSliceMapping<T>(int offset) where T : struct
将输出 PDO 数据的指定偏移位置映射为结构体引用。
BindPdoStruct<T>(...)
public PdoStructBinding<T> BindPdoStruct<T>(
PDOManager manager, bool isInput,
int offset = 0, Func<T, T, bool>? comparer = null) where T : struct
绑定 PDO 结构体变化事件(基于 PDOManager 的数据变化通知)。
参数:
manager— PDOManager 实例isInput— true=输入(TxPDO), false=输出(RxPDO)offset— 结构体在 IOmap 中的偏移字节comparer— 可选的自定义比较器
字节数组访问
Inputs / Outputs
public byte[] Inputs { get; }
public byte[] Outputs { get; set; }
直接读写 PDO 字节数组。每次访问从 IOmap 读取最新数据。写入 Outputs 时数据长度必须与 Obytes 匹配。
In / Out(索引访问)
public PDOArrayInstance In { get; }
public PDOArrayInstance Out { get; }
基于数组的 PDO 实例,支持按索引访问。
示例:
var value = slave.PDO.In[0].Content; // 读取第一个输入 PDO
slave.PDO.Out[0].Content = 0xFF; // 写入第一个输出 PDO