初始化
强烈建议使用 Darra 配置工具导出的 DENI 文件进行初始化。 DENI 文件包含完整的网络配置(网口、从站、PDO 映射、DC 设置等),可确保配置的一致性和正确性。 使用 DENI 文件管理设备硬件设施将获得更好的兼容性与通用性。
工厂方法
EtherCATMaster::from_json_file()
pub fn from_json_file(path: &str) -> Result<Self>
从 JSON/DENI 配置文件一步完成主站初始化。内部自动执行 Initialize -> SetNetwork -> SetState -> Start 全流程。
参数:
path(&str) — JSON 或 DENI 配置文件路径
返回值:
Result<Self>— 成功返回已启动的主站实例,失败返回错误
示例:
use ethercat::EtherCATMaster;
// Windows
let master = EtherCATMaster::from_json_file(r"C:\EtherCAT\config.json")?;
// Linux
let master = EtherCATMaster::from_json_file("/opt/ethercat/config.json")?;
println!("主站索引: {}, 从站数: {}", master.index(), master.slave_count());
EtherCATMaster::from_json()
pub fn from_json(json_config: &str) -> Result<Self>
通过 JSON 配置字符串一步完成主站初始化。与 from_json_file() 相同,但直接接受配置字符串。
参数:
json_config(&str) — JSON 格式的配置字符串
返回值:
Result<Self>— 已启动的主站实例
JSON 配置格式:
{
"adapter": "\\Device\\NPF_{YOUR-GUID}",
"adapter_redundant": "",
"target_state": "OP",
"cycle_time_ns": 1000000
}
示例:
let config = r#"{"adapter": "\\Device\\NPF_{...}", "target_state": "OP"}"#;
let master = EtherCATMaster::from_json(config)?;
EtherCATMaster::new()
pub fn new() -> Result<Self>
初始化一个新的主站实例(手动配置模式)。返回主站索引由 DLL 自动分配。必须手动调用 set_network() 和 set_state() 完成配置。
返回值:
Result<Self>— 主站实例
示例:
use ethercat::{EtherCATMaster, EcState};
let mut master = EtherCATMaster::new()?;
master.set_network(r"\Device\NPF_{YOUR-GUID}", "")?;
master.set_state(EcState::Operational)?;
master.start();
EtherCATMaster::with_index()
pub fn with_index(master_index: u16) -> Result<Self>
初始化指定索引的主站实例。用于多实例管理场景。
参数:
master_index(u16) — 指定的主站索引
返回值:
Result<Self>— 主站实例
构建器预检查
validate()
pub fn validate(&self) -> ValidationResult
在调用 build() 之前验证当前配置是否合理。不会执行实际初始化,仅检查配置参数。
返回值:
pub struct ValidationResult {
pub is_valid: bool, // 配置是否有效
pub errors: Vec<String>, // 错误信息列表 (为空表示无错误)
}
示例:
let builder = EtherCATMaster::builder()
.set_eni("config.xml");
let result = builder.validate();
if !result.is_valid {
for err in &result.errors {
println!("配置错误: {}", err);
}
} else {
let build_result = builder.build()?;
}
资源释放 (RAII)
EtherCATMaster 实现了 Drop trait,超出作用域时自动执行 Stop + Dispose 释放资源。
{
let mut master = EtherCATMaster::new()?;
master.set_network(adapter, "")?;
master.set_state(EcState::Operational)?;
master.start();
// ... 使用主站
} // master 超出作用域,自动 Stop + Dispose
dispose()
pub fn dispose(&mut self)
手动释放主站资源(不等待 Drop)。调用后主站不再拥有资源,后续 Drop 不会重复释放。
emergency_close_nics()
pub fn emergency_close_nics()
紧急关闭所有网卡(静态方法)。在进程异常退出时调用,释放网卡句柄和共享内存,避免资源泄漏。
示例:
// 注册 panic hook 进行紧急清理
std::panic::set_hook(Box::new(|info| {
EtherCATMaster::emergency_close_nics();
eprintln!("程序异常: {}", info);
}));
多实例管理
每个主站实例管理一条独立的 EtherCAT 总线,使用不同的网卡。创建新实例无需重启,即时生效。
- 每个实例必须使用不同的网卡 — 同一网卡不能被多个实例同时使用,SDK 会自动检测并阻止重复绑定
- CPU 核心自动分配 — 每个实例自动占用不同的 CPU 核心,无需手动设置
- Mailbox Gateway 端口自动偏移 — 每个实例自动分配不同的端口号
- 授权与日志共享 — 所有实例共享同一授权状态和日志系统,无冲突
max_master_instances()
pub fn max_master_instances() -> i32
获取允许的最大主站实例数量。
返回值:
i32— 最大实例数
示例:
let max = EtherCATMaster::max_master_instances();
println!("最大实例数: {}", max);
网络扫描
get_network_adapters()
pub fn get_network_adapters() -> Vec<NetworkInfo>
获取系统中可用的网络适配器列表(模块级函数)。
返回值:
Vec<NetworkInfo>— 网络适配器信息列表
相关结构:
pub struct NetworkInfo {
pub name: String, // 适配器设备路径(用于 set_network)
pub description: String, // 适配器描述(用户可读)
pub mac: [u8; 6], // MAC 地址
}
示例:
use ethercat::get_network_adapters;
let adapters = get_network_adapters();
for adapter in &adapters {
println!("{}: {}", adapter.description, adapter.name);
}
quick_slave_count()
pub fn quick_slave_count(adapter: &str) -> Result<i32>
快速从站计数(不读取 EEPROM,仅使用广播读取)。比 read_slave_info() 更快,适合快速检测从站数量。静态方法,不需要主站实例。
参数:
adapter(&str) — 网络适配器名称
返回值:
Result<i32>— 检测到的从站数量
scan_slave_count()
pub fn scan_slave_count(adapter: &str) -> Result<i32>
扫描从站数量。
参数:
adapter(&str) — 网络适配器名称
返回值:
Result<i32>— 从站数量
使用 DENI 文件时,set_network() 阶段的扫描结果会自动与 DENI 中的配置进行匹配验证。匹配策略由 config.scan_revision_match 控制(默认忽略 RevisionID)。如果网络拓扑与 DENI 不一致,set_state() 会返回详细的错误消息。
read_slave_info()
pub fn read_slave_info(adapter: &str) -> Result<i32>
读取从站详细信息(扫描并缓存),之后可通过 get_scanned_slaves() 获取详细信息。
参数:
adapter(&str) — 网络适配器名称
返回值:
Result<i32>— 扫描到的从站数量
get_scanned_slaves()
pub fn get_scanned_slaves() -> Vec<ScannedSlaveInfo>
获取已扫描的从站详细信息列表(模块级函数,需先调用 read_slave_info())。
相关结构:
pub struct ScannedSlaveInfo {
pub index: u16, // 从站索引 (1-based)
pub vendor_id: u32, // 厂商 ID
pub product_code: u32, // 产品代码
pub revision: u32, // 修订版本号
pub serial: u32, // 序列号
pub name: String, // 设备名称
pub config_addr: u16, // 配置地址
pub alias_addr: u16, // 别名地址
pub parent_index: u16, // 父从站索引 (0=主站是父节点)
pub topology: u8, // 拓扑类型 (0~4)
pub active_ports: u8, // 活动端口位图
pub entry_port: u8, // 入口端口
pub parent_port: u8, // 父从站上的端口号
pub physical_type: u8, // 物理端口类型
}
示例:
use ethercat::{EtherCATMaster, get_network_adapters, get_scanned_slaves};
let adapters = get_network_adapters();
for adapter in &adapters {
println!("{}: {}", adapter.description, adapter.name);
}
let count = EtherCATMaster::read_slave_info(&adapters[0].name)?;
println!("发现 {} 个从站", count);
let slaves = get_scanned_slaves();
for s in &slaves {
println!("[{}] {} (VID=0x{:08X}, PID=0x{:08X})",
s.index, s.name, s.vendor_id, s.product_code);
}
abort_scan()
pub fn abort_scan()
中止所有正在进行的扫描操作(静态方法)。用于关闭窗口或取消操作时快速中断阻塞的网络扫描。
示例:
use std::thread;
// 在后台线程中扫描
let handle = thread::spawn(|| {
EtherCATMaster::read_slave_info(r"\Device\NPF_{...}")
});
// 用户点击取消
EtherCATMaster::abort_scan();
let _ = handle.join();
版本与授权
serial_number()
pub fn serial_number() -> String
获取当前设备的序列号(静态方法,用于授权验证)。
返回值:
String— 设备序列号,获取失败时返回空字符串
verify_license()
pub fn verify_license() -> Result<bool>
验证授权是否有效。
返回值:
Result<bool>— 授权有效返回true
完整示例
使用 DENI/JSON 文件(推荐)
use ethercat::EtherCATMaster;
let master = EtherCATMaster::from_json_file(r"C:\EtherCAT\MyProject.json")?;
println!("主站索引: {}, 从站数: {}", master.index(), master.slave_count());
// master 超出作用域时自动释放
动态扫描网口
use ethercat::{EtherCATMaster, EcState, get_network_adapters};
let adapters = get_network_adapters();
let target = adapters.iter().find(|a| {
EtherCATMaster::quick_slave_count(&a.name).unwrap_or(0) > 0
});
if let Some(adapter) = target {
let mut master = EtherCATMaster::new()?;
master.set_network(&adapter.name, "")?;
master.set_state(EcState::Operational)?;
master.start();
}
冗余模式
use ethercat::{EtherCATMaster, EcState, get_network_adapters};
let adapters = get_network_adapters();
let mut master = EtherCATMaster::new()?;
master.set_network(&adapters[0].name, &adapters[1].name)?;
master.enable_redundancy(true)?;
master.set_state(EcState::Operational)?;
master.start();
多实例(多网卡独立总线)
use ethercat::{EtherCATMaster, EcState, get_network_adapters};
let adapters = get_network_adapters();
// 实例 1 — 绑定第一张网卡
let mut master1 = EtherCATMaster::new()?;
master1.set_network(&adapters[0].name, "")?;
master1.set_state(EcState::Operational)?;
master1.start();
// 实例 2 — 绑定第二张网卡(即时生效,无需重启)
let mut master2 = EtherCATMaster::new()?;
master2.set_network(&adapters[1].name, "")?;
master2.set_state(EcState::Operational)?;
master2.start();
// 各实例独立运行,互不干扰
master1.events().on_pdo_cyclic_async(|_| { /* 总线 1 的 PDO 处理 */ });
master2.events().on_pdo_cyclic_async(|_| { /* 总线 2 的 PDO 处理 */ });
// 查询实例信息
println!("最大实例数: {}", EtherCATMaster::max_master_instances());
// 释放时自动归还网卡和端口
drop(master2); // 释放后 adapters[1] 可被新实例使用
多实例资源分配:
- 网卡 — 每个实例使用不同的网卡
- CPU 核心 — 每个实例自动分配不同的专用核心
- Mailbox 端口 — 每个实例自动分配递增的端口号
- 工作线程 — 各实例独立