use crate::data::error::{DarraError, Result};
use crate::utils::ffi::{self, StartupParam, TRANS_IP, TRANS_PS, TRANS_SO, TRANS_OS, TRANS_SP, TRANS_PI, TIMING_BEFORE, TIMING_AFTER};
use crate::data::types::{StartupTransition, StartupWriteTiming};
#[derive(Debug, Clone)]
pub struct StartupParameter {
pub index: u16,
pub sub_index: u8,
pub data: Vec<u8>,
pub transition: StartupTransition,
pub write_timing: StartupWriteTiming,
pub priority: i32,
pub complete_access: bool,
pub description: String,
}
impl StartupParameter {
pub fn new(index: u16, sub_index: u8, data: Vec<u8>) -> Self {
Self {
index,
sub_index,
data,
transition: StartupTransition::IP,
write_timing: StartupWriteTiming::AfterTransition,
priority: 100,
complete_access: false,
description: String::new(),
}
}
pub fn with_transition(index: u16, sub_index: u8, data: Vec<u8>, transition: StartupTransition) -> Self {
let timing = default_write_timing(transition);
Self {
index,
sub_index,
data,
transition,
write_timing: timing,
priority: 100,
complete_access: false,
description: String::new(),
}
}
fn to_ffi(&self) -> StartupParam {
let (trans, timing) = transition_to_ffi(self.transition, self.write_timing);
let mut param = StartupParam {
index: self.index,
sub_index: self.sub_index,
data: [0u8; 256],
data_len: std::cmp::min(self.data.len(), 256) as u16,
transition: trans,
timing,
complete_access: if self.complete_access { 1 } else { 0 },
is_register_write: 0,
priority: self.priority as u16,
};
let copy_len = std::cmp::min(self.data.len(), 256);
param.data[..copy_len].copy_from_slice(&self.data[..copy_len]);
param
}
}
pub struct StartupParameterList {
master_index: u16,
slave_index: u16,
params: Vec<StartupParameter>,
}
impl StartupParameterList {
pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
Self {
master_index,
slave_index,
params: Vec::new(),
}
}
pub fn add(&mut self, param: StartupParameter) {
self.params.push(param);
}
pub fn add_sdo_write(
&mut self,
index: u16,
sub_index: u8,
data: Vec<u8>,
transition: StartupTransition,
) {
self.params.push(StartupParameter::with_transition(index, sub_index, data, transition));
}
pub fn remove(&mut self, idx: usize) -> Option<StartupParameter> {
if idx < self.params.len() {
Some(self.params.remove(idx))
} else {
None
}
}
pub fn clear(&mut self) {
self.params.clear();
unsafe { ffi::ClearStartupParameters(self.master_index, self.slave_index) };
}
pub fn count(&self) -> usize {
self.params.len()
}
pub fn params(&self) -> &[StartupParameter] {
&self.params
}
pub fn sort_by_priority(&mut self) {
self.params.sort_by_key(|p| p.priority);
}
pub fn sync_to_dll(&self) -> Result<i32> {
unsafe { ffi::ClearStartupParameters(self.master_index, self.slave_index) };
if self.params.is_empty() {
return Ok(0);
}
let ffi_params: Vec<StartupParam> = self.params.iter().map(|p| p.to_ffi()).collect();
let ret = unsafe {
ffi::AddStartupParameterBatch(
self.master_index,
self.slave_index,
ffi_params.as_ptr(),
ffi_params.len() as i32,
)
};
if ret >= 0 {
Ok(ret)
} else {
Err(DarraError::Other(format!("同步启动参数到 DLL 失败 (返回码: {})", ret)))
}
}
pub fn apply_single(&self, param: &StartupParameter) -> Result<()> {
let ffi_param = param.to_ffi();
let ret = unsafe {
ffi::AddStartupParameter(self.master_index, self.slave_index, &ffi_param)
};
if ret >= 0 {
Ok(())
} else {
Err(DarraError::Other(format!("添加启动参数失败 (返回码: {})", ret)))
}
}
pub fn apply(&self, transition: u8, timing: u8) -> Result<i32> {
let ret = unsafe {
ffi::DarraCoreInvoke(
self.master_index,
ffi::CORE_OP_26,
self.slave_index as u32,
1u32 << transition,
1u32 << timing)
};
if ret >= 0 {
Ok(ret)
} else {
Err(DarraError::Other(format!("执行启动参数失败 (返回码: {})", ret)))
}
}
pub fn dll_count(&self) -> i32 {
unsafe { ffi::GetStartupParameterCount(self.master_index, self.slave_index) }
}
}
pub fn default_write_timing(transition: StartupTransition) -> StartupWriteTiming {
match transition {
StartupTransition::IP => StartupWriteTiming::AfterTransition, StartupTransition::PS => StartupWriteTiming::BeforeTransition, StartupTransition::SO => StartupWriteTiming::BeforeTransition,
StartupTransition::OS => StartupWriteTiming::AfterTransition,
StartupTransition::SP => StartupWriteTiming::AfterTransition,
StartupTransition::PI => StartupWriteTiming::BeforeTransition,
}
}
fn transition_to_ffi(transition: StartupTransition, timing: StartupWriteTiming) -> (u8, u8) {
let trans = match transition {
StartupTransition::IP => TRANS_IP,
StartupTransition::PS => TRANS_PS,
StartupTransition::SO => TRANS_SO,
StartupTransition::OS => TRANS_OS,
StartupTransition::SP => TRANS_SP,
StartupTransition::PI => TRANS_PI,
};
let time = match timing {
StartupWriteTiming::BeforeTransition => TIMING_BEFORE,
StartupWriteTiming::AfterTransition => TIMING_AFTER,
};
(trans, time)
}
pub fn parse_transition(s: &str) -> StartupTransition {
match s.trim().to_uppercase().as_str() {
"IP" => StartupTransition::IP,
"PS" => StartupTransition::PS,
"SO" => StartupTransition::SO,
"OS" => StartupTransition::OS,
"SP" => StartupTransition::SP,
"PI" | "SI" => StartupTransition::PI,
_ => StartupTransition::IP,
}
}
pub fn transition_to_string(transition: StartupTransition) -> &'static str {
match transition {
StartupTransition::IP => "IP",
StartupTransition::PS => "PS",
StartupTransition::SO => "SO",
StartupTransition::OS => "OS",
StartupTransition::SP => "SP",
StartupTransition::PI => "PI",
}
}
pub fn transition_description(transition: StartupTransition) -> &'static str {
match transition {
StartupTransition::IP => "I>P (Init\u{2192}PreOp)",
StartupTransition::PS => "P>S (PreOp\u{2192}SafeOp)",
StartupTransition::SO => "S>O (SafeOp\u{2192}Op)",
StartupTransition::OS => "O>S (Op\u{2192}SafeOp)",
StartupTransition::SP => "S>P (SafeOp\u{2192}PreOp)",
StartupTransition::PI => "P>I (PreOp\u{2192}Init)",
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StartupAutoCapabilities {
pub default_pdo_enumeration: bool,
pub esi_auto_startup: bool,
pub mdp_auto_startup: bool,
pub sm_auto_configuration: bool,
}
pub struct StartupAutoConfig {
master_index: u16,
slave_index: u16,
has_executed: bool,
config_source: String,
}
impl StartupAutoConfig {
pub fn new(master_index: u16, slave_index: u16) -> Self {
Self {
master_index,
slave_index,
has_executed: false,
config_source: String::new(),
}
}
pub fn capabilities(&self) -> StartupAutoCapabilities {
StartupAutoCapabilities {
default_pdo_enumeration: true,
esi_auto_startup: false,
mdp_auto_startup: false,
sm_auto_configuration: false,
}
}
pub fn supports_full_auto_startup(&self) -> bool {
let caps = self.capabilities();
caps.esi_auto_startup || caps.mdp_auto_startup || caps.sm_auto_configuration
}
pub fn apply_full_auto_startup(&mut self) -> Result<i32> {
Err(DarraError::Other(
"Rust SDK 尚未公开完整 ESI/MDP/SM 自动启动执行能力; 请显式构造启动参数或调用默认 PDO 枚举".into(),
))
}
pub fn enumerate_default_pdo(&self, direction: i32, max_count: usize) -> Vec<u16> {
self.enumerate_default_pdo_result(direction, max_count)
.unwrap_or_default()
}
pub fn enumerate_default_pdo_result(&self, direction: i32, max_count: usize) -> Result<Vec<u16>> {
if direction != 0 && direction != 1 {
return Err(DarraError::InvalidParameter("direction 必须是 0(RxPDO) 或 1(TxPDO)".into()));
}
if max_count == 0 {
return Ok(Vec::new());
}
let mut buf: Vec<u16> = vec![0u16; max_count];
let n = unsafe {
ffi::EnumerateDefaultPdo(
self.master_index,
self.slave_index,
direction as std::os::raw::c_int,
buf.as_mut_ptr(),
max_count as std::os::raw::c_int,
)
};
if n < 0 {
return Err(DarraError::PdoFailed(format!("默认 PDO 枚举失败 (返回码: {})", n)));
}
if n == 0 {
return Ok(Vec::new());
}
let n = (n as usize).min(max_count);
buf.truncate(n);
Ok(buf)
}
pub fn set_config_source_eni(&mut self) {
self.config_source = "ENI".to_string();
}
pub fn set_config_source_deni(&mut self) {
self.config_source = "DENI".to_string();
}
pub fn has_executed(&self) -> bool { self.has_executed }
pub fn config_source(&self) -> &str { &self.config_source }
}