Slave 从站
通过 master.GetSlave(index) 获取从站引用(1-based 索引)。
概述
Slave 类封装了对单个 EtherCAT 从站的所有操作,包括状态管理、协议访问、IO 数据读写等。从站对象由 EtherCATMaster 创建和管理,不可直接构造。
所有协议子对象(CoE、SoE、FoE 等)采用延迟初始化策略,首次访问时自动创建。
快速开始
索引访问
#include "ethercat.hpp"
using namespace darra;
auto& slave = master.GetSlave(1); // 第1个从站(1-based)
int count = master.SlaveCount(); // 从站总数
按组查询
using namespace darra::ethercat;
uint16_t groupCount = master_other::get_group_slave_count(master, 0); // 默认组从站数
提示
组相关属性详见 从站分组。
地址访问
uint16_t addr = slave.ConfigAddr(); // 物理配置地址
uint16_t alias = slave.AliasAddress(); // 别名地址
标识访问
std::string name = slave.Name(); // 设备名称
uint32_t vid = slave.VendorId(); // 厂商 ID
uint32_t pid = slave.ProductId(); // 产品 ID
uint32_t rev = slave.RevId(); // 修订号
uint32_t sn = slave.SerialNumber(); // 序列号
// 读取状态
EcState state = slave.State();
printf("VendorID=0x%08X ProductID=0x%08X\n", vid, pid);
协议访问
所有协议接口通过成员方法延迟获取,首次调用时自动创建:
auto& slave = master.GetSlave(1);
// 各协议接口
CoE& coe = slave.GetCoE(); // CoE (CANopen over EtherCAT)
SoE& soe = slave.GetSoE(); // SoE (Servo over EtherCAT)
FoE& foe = slave.GetFoE(); // FoE (File over EtherCAT)
EoE& eoe = slave.GetEoE(); // EoE (Ethernet over EtherCAT)
AoE& aoe = slave.GetAoE(); // AoE (ADS over EtherCAT)
VoE& voe = slave.GetVoE(); // VoE (Vendor over EtherCAT)
FSoE& fsoe = slave.GetFSoE(); // FSoE (功能安全)
CiA402& cia402 = slave.GetCiA402(); // CiA 402 (伺服驱动)
| 协议 | 方法 | 说明 | 文档 |
|---|---|---|---|
| CoE | slave.GetCoE() | SDO 对象字典访问 | CoE |
| SoE | slave.GetSoE() | SERCOS IDN 参数 | SoE |
| FoE | slave.GetFoE() | 文件传输 | FoE |
| EoE | slave.GetEoE() | 以太网隧道 | EoE |
| AoE | slave.GetAoE() | Beckhoff ADS | AoE |
| VoE | slave.GetVoE() | 厂商特定协议 | VoE |
| FSoE | slave.GetFSoE() | 功能安全协议 | FSoE |
| CiA 402 | slave.GetCiA402() | 伺服状态机 | CiA 402 |
从站属性与方法
详细的属性列表、状态管理、看门狗、DC 配置等,参见 属性与方法。
PDO 数据读写
通过零拷贝指针映射访问 PDO 数据:
auto& slave = master.GetSlave(1);
// 零拷贝指针
void* input = slave.InputDataPointer();
void* output = slave.OutputDataPointer();
// 结构体映射
#pragma pack(push, 1)
struct ServoOutput { uint16_t controlWord; int32_t targetPosition; };
struct ServoInput { uint16_t statusWord; int32_t actualPosition; };
#pragma pack(pop)
auto* out = reinterpret_cast<ServoOutput*>(output);
auto* in = reinterpret_cast<ServoInput*>(input);
if (out && in) {
out->controlWord = 0x000F;
printf("位置: %d\n", in->actualPosition);
}
或使用直接读写:
// 读取输入数据到缓冲区
uint8_t buf[64];
int bytes = slave.ReadInputDirect(buf, sizeof(buf));
// 写入输出数据
uint8_t data[] = {0x0F, 0x00, 0, 0, 0, 0};
slave.WriteOutputDirect(data, sizeof(data));
详见 PDO 输入输出。
PDO 直接读写
通过零拷贝指针访问 PDO 数据, 详见 PDO 输入输出。
EMCY 紧急消息
EtherCAT 从站在检测到异常时会发送 EMCY (Emergency) 紧急消息。SDK 提供两套接口:
- SDO 0x1003 历史 —
coe.GetEmergencyHistory() / ClearEmergencyHistory() - 实时缓存 —
darra::ethercat::slave_emcy命名空间下的自由函数
using namespace darra::ethercat;
int n = slave_emcy::emcy_count(dll, mi, si);
auto records = slave_emcy::emcy_history(dll, mi, si);
for (auto& r : records) {
printf("EMCY t=%u ms code=0x%04X reg=0x%02X\n",
r.timestamp_ms, r.error_code, r.error_register);
}
slave_emcy::clear_emcy(dll, mi, si);
详见 CoE - 紧急消息历史。
拓扑查询
EtherCAT 通过每个 ESC 的端口连接关系自然形成线性 / 环 / 分支拓扑。SDK 在进入 OP 状态后可以从 DLL 一次性拉取所有节点的父子关系, 缓存到 TopologyTree, 再做 DFS / BFS / 深度查询等。
using namespace darra::ethercat;
auto tree = BuildTopology(master.Dll(), master.MasterNumber());
if (!tree.IsBuilt()) return;
// 打印根 (有 N 张主网卡或冗余环时可能多个根)
for (auto rootIdx : tree.RootSlaves()) {
auto* root = tree.FindNode(rootIdx);
if (root) printf("根: Slave[%u] ports=0x%02X\n", root->SlaveIndex, root->ActivePorts);
}
// DFS / BFS 整棵树, visitor 返回 false 立即中止
tree.DFS([&](const TopologyNode& n, int depth) {
printf("%*sSlave[%u] addr=%s 子=%zu\n",
depth * 2, "", n.SlaveIndex, n.Address.c_str(), n.Children.size());
return true;
});
相关结构:
struct TopologyNode {
uint16_t SlaveIndex; // 从站索引 (1-based)
uint16_t ConfigAddr; // 配置地址
std::string Address; // 配置地址十六进制字符串 (如 "0x1001")
uint16_t ParentIndex; // 父节点索引, 0 表示根节点
uint8_t EntryPort; // 入口端口 (0-3)
uint8_t ActivePorts; // 活动端口位图 (Bit0..Bit3 对应 P0..P3)
uint8_t Topology; // 0=无链接 / 1=端点 / 2=中间 / 3=分支 / 4=交叉
int Depth; // 到根节点的深度
std::vector<uint16_t> Children; // 子节点索引列表
};
便捷方法: IsRoot() / ActivePortCount() / GetPortType(port) / EntryPortType()。TopologyTree 提供 RootSlaves() / GetChildren() / GetChildCount() / FindNode() / DFS() / BFS()。
完整示例
#include "ethercat.hpp"
using namespace darra;
int main() {
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}")
.SetENI("config.deni")
.Build();
// 遍历所有从站
for (uint16_t i = 1; i <= master.SlaveCount(); ++i) {
auto& slave = master.GetSlave(i);
printf("[%d] VID=0x%08X PID=0x%08X 状态=%d\n",
i, slave.VendorId(), slave.ProductId(),
static_cast<int>(slave.State()));
}
// 使用协议
auto& slave1 = master.GetSlave(1);
auto data = slave1.GetCoE().SDORead(0x1018, 0x01);
if (!data.empty()) {
uint32_t vendor_id;
std::memcpy(&vendor_id, data.data(), 4);
printf("VendorID: 0x%08X\n", vendor_id);
}
return 0;
} // 析构自动 Stop + Dispose