邮箱网关 (Mailbox Gateway)
邮箱网关实现 ETG.8200 标准,允许外部诊断工具通过 UDP/IP 访问 EtherCAT 主站和从站的对象字典。
C SDK 提供两种实现:
- DLL 内置网关 — 直接复用主站当前对象字典与从站邮箱栈,适合常规部署
- SDK 自带 UDP 服务 (
master/gateway.h) — 用户提供回调,适合定制路由 / 鉴权 / 限速
ETG.8200 标准
邮箱网关服务符合 ETG.8200 标准规范,使用标准 UDP 端口 0x88A4 (34980),帧结构完全符合 Table 1 要求。
服务控制
DarraGateway* gateway_create(uint16_t master_index);
void gateway_destroy(DarraGateway* gw);
int gateway_set_port(DarraGateway* gw, uint16_t port);
uint16_t gateway_get_port(const DarraGateway* gw);
int gateway_start(DarraGateway* gw);
void gateway_stop(DarraGateway* gw);
bool gateway_is_running(const DarraGateway* gw);
gateway_create()创建网关实例,默认端口0x88A4 + master_index,返回句柄(NULL=分配失败)gateway_set_port()必须在启动前调用,返回 0 成功、-1 已在运行gateway_start()启动后台 UDP 监听线程,返回 0 成功、-1 端口绑定失败、-2 已在运行gateway_destroy()销毁实例(如果服务仍在运行,内部自动 stop)
多实例自动偏移
运行多个主站实例时,端口按 master_index 自动偏移:实例 0=34980、实例 1=34981、实例 N=34980+N。被占用时启动阶段顺移最多 10 个槽位。
端口策略
默认端口 0x88A4 (34980)
实例 0: 34980
实例 1: 34981
实例 N: 34980 + N
被占用时启动阶段顺移最多 10 个槽位,全部失败由系统分配。gateway_get_port() 启动后返回实际绑定端口。
协议回调
typedef int (*DarraGatewayMasterOdCallback)(uint8_t mbx_type,
const uint8_t* request, int request_len,
uint8_t* response_buf, int response_buf_size, void* user);
typedef int (*DarraGatewaySlaveCallback)(uint16_t slave_addr, uint8_t mbx_type,
const uint8_t* request, int request_len,
uint8_t* response_buf, int response_buf_size, void* user);
void gateway_set_master_od_callback(DarraGateway* gw,
DarraGatewayMasterOdCallback cb, void* user);
void gateway_set_slave_callback(DarraGateway* gw,
DarraGatewaySlaveCallback cb, void* user);
- 主站对象字典回调: 收到
Address == 0x0000的请求时调用 - 从站邮箱回调: 收到
Address != 0x0000的请求时调用 - 回调返回写入响应缓冲的字节数;0=无响应,负值=丢弃
统计信息
typedef struct {
uint64_t rxPackets; /* 收到的 UDP 包数 */
uint64_t txPackets; /* 发送的响应包数 */
uint64_t rxBytes; /* 收到的字节数 */
uint64_t txBytes; /* 发送的字节数 */
uint64_t masterOdRequests; /* 路由到主站 OD 的请求数 */
uint64_t slaveRequests; /* 路由到从站邮箱的请求数 */
uint64_t dropMalformed; /* 帧格式错误丢弃数 */
uint64_t dropTimeout; /* 处理超时丢弃数 */
} DarraGatewayStats;
void gateway_get_statistics(const DarraGateway* gw, DarraGatewayStats* out_stats);
void gateway_reset_statistics(DarraGateway* gw);
get_statistics 单次原子复制;reset_statistics 清零所有计数器,不影响运行状态。
跨语言对齐
统计字段在所有 SDK 中名字与语义完全一致。诊断脚本可在不同语言间无缝互译。
支持的协议
| 协议 | 类型字节 | 说明 |
|---|---|---|
| CoE | 0x03 | SDO 读写,支持主站(ETG.1510)和从站对象字典 |
| SoE | 0x04 | IDN 参数读写(ETG.1020 标准) |
| FoE | 0x05 | 文件传输(当前仅支持单包) |
| VoE | 0x0F | 厂商特定协议透传 |
地址路由规则:
- Address
0x0000→ 主站对象字典(仅 CoE) - Address = 从站站地址 → 透传到对应从站(CoE / SoE / FoE / VoE)
帧结构
EtherCAT Header (2B): Length(11bit) + Type=0x05
Mailbox Header (6B): Length(2) + Address(2) + 通道+优先级(1) + 类型+计数器(1)
Data (NB): 协议负载
EtherCAT Header:
- Length: Mailbox Header + Data 的总长度
- Data Type: 固定为 0x05 (Mailbox communication)
Mailbox Header:
- Length: 邮箱数据部分长度
- Address: 0x0000=主站, 其他=从站站地址
- Type: 0x03=CoE, 0x04=SoE, 0x05=FoE, 0x0F=VoE
- Cnt: 邮箱计数器 (1-7 循环)
完整示例
自己启动案例
#include "master/gateway.h"
static int on_master_od(uint8_t mbx_type, const uint8_t* req, int req_len,
uint8_t* resp, int resp_cap, void* user)
{
/* 从主站 OD 取数据, 写入 resp, 返回字节数 */
return build_master_od_response(mbx_type, req, req_len, resp, resp_cap);
}
static int on_slave_mbx(uint16_t addr, uint8_t mbx_type,
const uint8_t* req, int req_len,
uint8_t* resp, int resp_cap, void* user)
{
/* 转发到对应从站邮箱 */
return forward_to_slave(addr, mbx_type, req, req_len, resp, resp_cap);
}
int main(void) {
DarraGateway* gw = gateway_create(0);
gateway_set_port(gw, 0x88A4);
gateway_set_master_od_callback(gw, on_master_od, NULL);
gateway_set_slave_callback(gw, on_slave_mbx, NULL);
if (gateway_start(gw) != 0) {
fprintf(stderr, "网关启动失败\n");
gateway_destroy(gw);
return 1;
}
/* ... 主循环 ... */
DarraGatewayStats s;
gateway_get_statistics(gw, &s);
printf("rx=%llu tx=%llu master_od=%llu drop=%llu\n",
(unsigned long long)s.rxPackets, (unsigned long long)s.txPackets,
(unsigned long long)s.masterOdRequests,
(unsigned long long)(s.dropMalformed + s.dropTimeout));
gateway_stop(gw);
gateway_destroy(gw);
return 0;
}
外部代码案例 (C 客户端)
#include <winsock2.h>
/* ETG.8200 帧: EtherCAT Header (2) + Mailbox Header (6) + Data */
static int build_frame(uint8_t* frame, uint16_t address, uint8_t mbx_type,
const uint8_t* mbx_data, uint16_t mbx_len)
{
uint16_t ecat_len = 6 + mbx_len;
/* EtherCAT Header: Length[10:0] | DataType[15:12]=0x05 */
*(uint16_t*)(frame + 0) = (uint16_t)((0x05 << 12) | (ecat_len & 0x07FF));
*(uint16_t*)(frame + 2) = mbx_len;
*(uint16_t*)(frame + 4) = address;
frame[6] = 0x00; /* Channel + Priority */
frame[7] = (uint8_t)(mbx_type << 4); /* Type[7:4] */
memcpy(frame + 8, mbx_data, mbx_len);
return 8 + mbx_len;
}
/* CoE SDO Upload: address=0x0000 主站 OD, 其他=从站 */
int coe_sdo_read(SOCKET sock, uint16_t address, uint16_t index, uint8_t subindex,
uint8_t* resp, int resp_cap)
{
uint8_t co_data[8] = {0};
co_data[1] = 0x20; /* CoE Type: SDO Request */
co_data[2] = 0x40; /* SDO Upload (Read) */
*(uint16_t*)(co_data + 3) = index;
co_data[5] = subindex;
uint8_t frame[64];
int n = build_frame(frame, address, 0x03 /* CoE */, co_data, sizeof(co_data));
/* sendto + recvfrom 略 */
return n;
}
错误处理
| 协议 | 错误类型 | 错误码 |
|---|---|---|
| CoE | 对象不存在 | 0x06020000 |
| CoE | 不支持的访问 | 0x06010000 |
| CoE | 写保护 | 0x06010002 |
| CoE | 命令未实现 | 0x05040001 |
| SoE | IDN 不存在 | 0x1009 |
| SoE | 写入失败 | 0x7002 |
| FoE | 文件未找到 | 0x00008002 |
| FoE | 非法文件名 | 0x00008003 |
| VoE | 无响应 | 0x0003 |
完整错误码列表参考 错误处理。
注意事项
网络安全
默认网关是强透传的,允许网络访问主站和从站的全部对象字典。建议:
- 务必在受控网络中使用,或通过 IP 白名单 / VPN 隧道 限制访问
- 配合操作系统防火墙规则,仅放行可信来源 IP
- 生产环境中谨慎开启,避免暴露到公网
性能影响
邮箱网关运行在独立线程,对主循环性能影响极小。但大量同步 SDO/邮箱操作仍可能影响总体延迟——请合理限制并发与速率。
限制
已实现功能:
- CoE 完整透传 / SoE IDN 参数访问 / FoE 单包文件传输 / VoE 厂商协议透传
- 主站对象字典访问 + 从站邮箱透传
- 错误处理和错误码映射
待实现功能:
- FoE 分段传输(大文件支持)
- Address 映射表 (+0x8000 虚拟映射)
- 多播/广播请求
- 会话状态管理