use std::ffi::CStr;
use std::os::raw::c_char;
use thiserror::Error;
use crate::utils::ffi;
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DarraKernelStatus {
Ok = 0,
NotInstalled = 1,
Stopped = 2,
AccessDenied = 3,
SignatureFail = 4,
Blocked = 5,
DeviceMissing = 6,
Disabled = 7,
UnknownError = 99,
}
impl DarraKernelStatus {
pub fn from_i32(code: i32) -> Self {
match code {
0 => DarraKernelStatus::Ok,
1 => DarraKernelStatus::NotInstalled,
2 => DarraKernelStatus::Stopped,
3 => DarraKernelStatus::AccessDenied,
4 => DarraKernelStatus::SignatureFail,
5 => DarraKernelStatus::Blocked,
6 => DarraKernelStatus::DeviceMissing,
7 => DarraKernelStatus::Disabled,
_ => DarraKernelStatus::UnknownError,
}
}
pub fn as_i32(self) -> i32 {
self as i32
}
}
impl std::fmt::Display for DarraKernelStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", get_status_message(*self))
}
}
#[derive(Error, Debug, Clone)]
#[error("EtherCAT 内核驱动不可用 ({status:?}): {message}\n安装链接: {installer_url}")]
pub struct DarraKernelNotAvailableError {
pub status: DarraKernelStatus,
pub message: String,
pub installer_url: String,
}
fn cstr_to_string(p: *const c_char, fallback: &str) -> String {
if p.is_null() {
return fallback.to_string();
}
unsafe { CStr::from_ptr(p).to_string_lossy().into_owned() }
}
pub fn probe() -> DarraKernelStatus {
let code = unsafe { ffi::DarraEcat_KernelProbe() };
DarraKernelStatus::from_i32(code as i32)
}
pub fn get_status_message(status: DarraKernelStatus) -> String {
let p = unsafe { ffi::DarraEcat_KernelStatusMessage(status.as_i32()) };
cstr_to_string(p, "EtherCAT 内核驱动状态未知")
}
pub fn get_installer_url() -> String {
let p = unsafe { ffi::DarraEcat_KernelInstallerUrl() };
cstr_to_string(p, "https://www.darrart.com/downloads/drivers")
}
pub fn assert_available() -> Result<(), DarraKernelNotAvailableError> {
let status = probe();
if status == DarraKernelStatus::Ok {
return Ok(());
}
let message = get_status_message(status);
let installer_url = get_installer_url();
Err(DarraKernelNotAvailableError {
status,
message,
installer_url,
})
}
pub fn open_installer_website() -> bool {
let url = get_installer_url();
if url.is_empty() {
return false;
}
open_url(&url)
}
#[cfg(target_os = "windows")]
fn open_url(url: &str) -> bool {
use std::process::Command;
Command::new("cmd")
.args(["/C", "start", "", url])
.spawn()
.is_ok()
}
#[cfg(target_os = "macos")]
fn open_url(url: &str) -> bool {
use std::process::Command;
Command::new("open").arg(url).spawn().is_ok()
}
#[cfg(all(unix, not(target_os = "macos")))]
fn open_url(url: &str) -> bool {
use std::process::Command;
Command::new("xdg-open").arg(url).spawn().is_ok()
}
#[cfg(not(any(target_os = "windows", target_os = "macos", unix)))]
fn open_url(_url: &str) -> bool {
false
}