PDO 输入输出
通过 slave.CopyInputPdo / slave.WriteOutputPdo 访问。
邮箱通信(SDO/CoE/FoE 等)自动附带在 PDO 周期中,不占用额外帧。邮箱响应延迟约 2 个 PDO 周期。
C# 应用层当前主路径是 generation 整帧复制:读走 CopyInputPdo,写走 WriteOutputPdo。调用方预分配完整帧缓冲区,SDK 把这一代过程数据拷进/拷出托管缓冲。
内核 SHM 零拷贝只存在于驱动内部(IOmap / GlobalIO),不向应用层暴露指针。不要再把 InputsMapping<T>()、GetIO、GetInputDataPointer / live Span 当成可用主路径——这些入口已退场(Obsolete,返回空或抛错),也不是第二套 API。
主路径:generation 整帧
CopyInputPdo()
public PdoIoStatus CopyInputPdo(
Span<byte> destination, out int copiedSize, out int requiredSize)
把当前 generation 的完整 TxPDO(从站→主站)复制到调用方缓冲区。无每周期托管分配。
返回值: PdoIoStatus。成功时 copiedSize == requiredSize,且必须等于当前输入映射长度。
查询尺寸: 传入空 Span 可读 requiredSize(此时不拷数据)。
示例:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoInput
{
public ushort StatusWord;
public int ActualPosition;
public int ActualVelocity;
}
byte[] inputFrame = new byte[slave.InputsByteCount];
PdoIoStatus status = slave.CopyInputPdo(
inputFrame, out int copied, out int required);
if (status == PdoIoStatus.Success &&
copied == required && required == inputFrame.Length)
{
ServoInput input = MemoryMarshal.Read<ServoInput>(inputFrame);
Console.WriteLine($"当前位置: {input.ActualPosition}");
}
CopyOutputPdo()
public PdoIoStatus CopyOutputPdo(
Span<byte> destination, out int copiedSize, out int requiredSize)
把当前 generation 的完整 RxPDO 镜像(主站→从站)复制到调用方缓冲区。用于读回已下发的输出映像;局部改写请在托管缓冲上改完后,再用 WriteOutputPdo 提交完整帧。
WriteOutputPdo()
public PdoIoStatus WriteOutputPdo(
ReadOnlySpan<byte> source, out int writtenSize, out int requiredSize)
单 owner 提交完整 RxPDO。source 长度必须与当前输出映射完全相等,无静默截断。此入口不提供并发 CAS,禁止当局部/typed/batch 读改写用。
示例:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoOutput
{
public ushort ControlWord;
public int TargetPosition;
public int TargetVelocity;
}
var output = new ServoOutput
{
ControlWord = 0x000F,
TargetPosition = 10000,
};
byte[] outputFrame = new byte[slave.OutputsByteCount];
MemoryMarshal.Write(outputFrame, ref output);
PdoIoStatus status = slave.WriteOutputPdo(
outputFrame, out int written, out int required);
if (status != PdoIoStatus.Success ||
written != outputFrame.Length || required != outputFrame.Length)
{
// 如实处理 Busy / SizeMismatch 等,不要假定已写入
}
PdoIoStatus
public enum PdoIoStatus : byte
{
Failed = 0,
Success = 1,
BufferTooSmall = 2,
SizeMismatch = 3,
Conflict = 4,
Stale = 5,
Busy = 6,
}
Busy 表示这一代 IOmap 被实时线程瞬占,可按调用方策略有界重试同一完整帧。不要把失败粉饰成成功。
同一路径上的便捷包装
下列入口不是第二套 PDO API,内部仍走 CopyInputPdo / WriteOutputPdo 整帧。
Inputs / Outputs
public byte[] Inputs { get; } // 每次访问分配一份完整托管快照
public byte[] Outputs { get; set; } // getter 同样是快照;setter 只接受完整帧
高频周期请预分配缓冲区,直接调 CopyInputPdo / WriteOutputPdo,避免每周期 byte[] 分配。
In / Out(索引访问)
public PDOArrayInstance In { get; }
public PDOArrayInstance Out { get; }
按字段索引读写,底层仍是 generation 整帧复制,不是 IOmap 指针。
var value = slave.PDO.In[0].Content;
slave.PDO.Out[0].Content = 0xFF;
已退场(不要再用)
| 旧写法 | 现状 |
|---|---|
slave.PDO.InputsMapping<T>() / OutputsMapping<T>() 返回 ref T | Obsolete,抛 NotSupportedException |
InputsSliceMapping<T>(offset) / OutputsSliceMapping<T>(offset) 返回 ref T | 同上 |
GetInputDataPointer() / GetOutputDataPointer() | Obsolete,返回 IntPtr.Zero |
GetInputDataSpan() / GetOutputDataSpan() / live InputSpan() | Obsolete,返回空 Span |
GetIO 指针主路径 | 应用层已退场 |
需要结构体视图时:先 CopyInputPdo 到调用方缓冲区,再 MemoryMarshal.Read<T> / Write<T>。不要假设能拿到跨周期仍有效的 IOmap 指针。