跳到主要内容

初始化

推荐方式

强烈建议使用 Darra 配置工具导出的 DENI 文件进行初始化。 DENI 文件包含完整的网络配置(网口、从站、PDO 映射、DC 设置等),可确保配置的一致性和正确性。 使用 DENI 文件管理设备硬件设施将获得更好的兼容性与通用性。

工厂方法​

EtherCATMaster::from_json_file()​

pub fn from_json_file(path: &str) -> Result<Self>

从 JSON 配置文件一步完成主站初始化(内部依次完成网络绑定、配置加载、状态切换与启动)。

只接受 JSON

from_json_file() 走 native EcInitFromFile,只解析 JSON —— 传入 DENI/ENI(XML)文件会返回 DarraError::ConfigLoadFailed(-1)。加载 DENI 请用 EtherCATMaster::builder().set_eni(...)(见页首推荐方式)。

参数:

  • path (&str) — JSON 配置文件路径

返回值:

  • Result<Self> — 成功返回已启动的主站实例,失败返回错误

示例:

use darra_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 darra_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> — 主站实例

构建器配置​

产品入口是 EtherCATMaster::builder() 链式配置。网络配置走 set_eni / load_config_json,ESI 走 set_esi_file / set_esi_files。utils::xml 为 pub(crate),没有公开的 save_xml / load_and_apply / apply_xml。保存配置 / 启动快照在主站自愈窗口内采用有限等待(秒级上限,短轮询),超时返回错误,不会长时间挂起。

set_eni()​

pub fn set_eni(self, path: &str) -> Self

设置 ENI/DENI 配置文件路径(延迟到 build() 生效)。

参数:

  • path (&str) — ENI/DENI 配置文件路径

返回值:

  • Self — 构建器自身,支持链式调用

示例:

let result = EtherCATMaster::builder()
.set_eni(r"C:\EtherCAT\MyProject.deni")
.build()?;

set_esi_file() / set_esi_files()​

pub fn set_esi_file(self, path: &str) -> Self
pub fn set_esi_files(self, path: &str) -> Self

set_esi_file() 与 set_esi_files() 等价,均可传 ESI 文件或目录(build() 时按 is_dir() 自动区分)。均可多次调用。延迟到 build() 生效。

参数:

  • path (&str) — ESI 文件路径,或目录路径

返回值:

  • Self — 构建器自身,支持链式调用
备注

已用 DENI(set_eni)时通常不必再单独加载 ESI。

set_network()​

pub fn set_network(self, primary: &str, redundant: Option<&str>) -> Self

设置主网口和可选冗余网口(延迟到 build() 生效)。

参数:

  • primary (&str) — 主网口设备路径
  • redundant (Option<&str>) — 冗余网口,无冗余传 None

返回值:

  • Self — 构建器自身,支持链式调用
覆盖规则

set_eni() 和 set_network() 都可设置网口。后调用的覆盖前面的网口配置;从站 / PDO / DC 等 DENI 内容始终保留。

enable_auto_startup()​

pub fn enable_auto_startup(self) -> Self

Build 时自动配置尚未有启动参数的从站。已有 ENI/DENI 启动参数的从站会被跳过。

构建器预检查​

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.deni");
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);

网络扫描​

网卡枚举范围

默认只列出已绑定 DarraRT_Eth 的物理有线口。虚拟网卡、WiFi、TAP 不进列表。ASIX 等 USB 有线网卡按物理有线口保留。未绑定口不要当作主口候选。

get_network_adapters()​

pub fn get_network_adapters() -> Result<Vec<NetworkInfo>>

获取网卡列表(模块级函数)。默认只含已绑定的物理有线口。枚举失败返回 Err,不用空表冒充。

返回值:

  • Result<Vec<NetworkInfo>> — 网络适配器信息列表

相关结构:

pub struct NetworkInfo {
pub name: String, // 适配器设备路径(用于 set_network)
pub description: String, // 适配器描述(用户可读)
pub mac: Option<[u8; 6]>, // MAC 地址,无物理地址时为 None
}

示例:

use darra_ethercat::get_network_adapters;

let adapters = get_network_adapters().unwrap_or_default();
for adapter in &adapters {
println!("{}: {}", adapter.description, adapter.name);
}

quick_slave_count()​

pub fn quick_slave_count(adapter: &str) -> Result<i32>

快速从站计数(不读取 EEPROM,仅 BRD)。DENI / 下拉数量走这条。scan_slave_count() 同口径。完整详情走 read_slave_info() 扫描缓存后再用 get_scanned_slaves() 取。数量不得当 0 丢弃。

参数:

  • 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 darra_ethercat::{EtherCATMaster, get_network_adapters, get_scanned_slaves};

let adapters = get_network_adapters().unwrap_or_default();
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 — 设备序列号,获取失败时返回空字符串

license_status()​

查询授权状态、Authorization::activate(激活码) 在线激活,激活入口见 授权与激活。

完整示例​

使用 DENI/JSON 文件(推荐)​

下面的示例走 JSON 路径。DENI 文件是 XML,请用 set_eni() 加载。

use darra_ethercat::EtherCATMaster;

let master = EtherCATMaster::from_json_file(r"C:\EtherCAT\MyProject.json")?;
println!("主站索引: {}, 从站数: {}", master.index(), master.slave_count());

// master 超出作用域时自动释放

动态扫描网口​

use darra_ethercat::{EtherCATMaster, EcState, get_network_adapters};

let adapters = get_network_adapters().unwrap_or_default();
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 darra_ethercat::{EtherCATMaster, EcState, get_network_adapters};

let adapters = get_network_adapters().unwrap_or_default();

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 darra_ethercat::{EtherCATMaster, EcState, get_network_adapters};

let adapters = get_network_adapters().unwrap_or_default();

// 实例 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_sync(|_| { /* 总线 1 的 PDO 同步处理 */ });
master2.events().on_pdo_cyclic_sync(|_| { /* 总线 2 的 PDO 同步处理 */ });

// 查询实例信息
println!("最大实例数: {}", EtherCATMaster::max_master_instances());

// 释放时自动归还网卡和端口
drop(master2); // 释放后 adapters[1] 可被新实例使用

多实例资源分配:

  • 网卡 — 每个实例使用不同的网卡
  • CPU 核心 — 每个实例自动分配不同的专用核心
  • Mailbox 端口 — 每个实例自动分配递增的端口号
  • 工作线程 — 各实例独立