跳到主要内容

初始化

推荐方式

强烈建议使用 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 优势

使用 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 端口 — 每个实例自动分配递增的端口号
  • 工作线程 — 各实例独立