跳到主要内容

邮箱网关 (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 中名字与语义完全一致。诊断脚本可在不同语言间无缝互译。

支持的协议

协议类型字节说明
CoE0x03SDO 读写,支持主站(ETG.1510)和从站对象字典
SoE0x04IDN 参数读写(ETG.1020 标准)
FoE0x05文件传输(当前仅支持单包)
VoE0x0F厂商特定协议透传

地址路由规则:

  • 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
SoEIDN 不存在0x1009
SoE写入失败0x7002
FoE文件未找到0x00008002
FoE非法文件名0x00008003
VoE无响应0x0003

完整错误码列表参考 错误处理

注意事项

网络安全

默认网关是强透传的,允许网络访问主站和从站的全部对象字典。建议:

  • 务必在受控网络中使用,或通过 IP 白名单 / VPN 隧道 限制访问
  • 配合操作系统防火墙规则,仅放行可信来源 IP
  • 生产环境中谨慎开启,避免暴露到公网
性能影响

邮箱网关运行在独立线程,对主循环性能影响极小。但大量同步 SDO/邮箱操作仍可能影响总体延迟——请合理限制并发与速率。

限制

已实现功能:

  • CoE 完整透传 / SoE IDN 参数访问 / FoE 单包文件传输 / VoE 厂商协议透传
  • 主站对象字典访问 + 从站邮箱透传
  • 错误处理和错误码映射

待实现功能:

  • FoE 分段传输(大文件支持)
  • Address 映射表 (+0x8000 虚拟映射)
  • 多播/广播请求
  • 会话状态管理