use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::sync::Arc;
use crate::enums::OpenOptions;
use crate::sys::icsneoc2_device_t;
use crate::sys::{self, icsneoc2_message_t};
use crate::error::{Error, Result};
use crate::event::Event;
use crate::functions::{api_error, check, ffi_string};
use crate::message::Message;
use crate::settings::Settings;
pub struct DeviceInfoList {
head: *mut sys::icsneoc2_device_info_t,
}
unsafe impl Send for DeviceInfoList {}
impl Drop for DeviceInfoList {
fn drop(&mut self) {
if !self.head.is_null() {
unsafe { sys::icsneoc2_enumeration_free(self.head) };
}
}
}
impl DeviceInfoList {
pub fn iter(&self) -> DeviceInfoIter<'_> {
DeviceInfoIter {
current: self.head,
_phantom: PhantomData,
}
}
pub fn is_empty(&self) -> bool {
self.head.is_null()
}
}
impl<'a> IntoIterator for &'a DeviceInfoList {
type Item = DeviceInfo<'a>;
type IntoIter = DeviceInfoIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct DeviceInfoIter<'a> {
current: *mut sys::icsneoc2_device_info_t,
_phantom: PhantomData<&'a DeviceInfoList>,
}
impl<'a> Iterator for DeviceInfoIter<'a> {
type Item = DeviceInfo<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.is_null() {
return None;
}
let info = DeviceInfo {
ptr: self.current,
_phantom: PhantomData,
};
self.current = unsafe { sys::icsneoc2_device_info_next(self.current) };
Some(info)
}
}
pub struct DeviceInfo<'a> {
ptr: *mut sys::icsneoc2_device_info_t,
_phantom: PhantomData<&'a DeviceInfoList>,
}
impl<'a> DeviceInfo<'a> {
pub(crate) fn as_ptr(&self) -> *mut sys::icsneoc2_device_info_t {
self.ptr
}
pub fn serial(&self) -> Result<String> {
ffi_string(64, |buf, len| unsafe {
sys::icsneoc2_device_info_serial_get(self.ptr, buf, len)
})
}
pub fn device_type(&self) -> Result<sys::Devicetype> {
let mut raw: sys::icsneoc2_devicetype_t = 0;
check(unsafe { sys::icsneoc2_device_info_type_get(self.ptr, &raw mut raw) })?;
Ok(sys::Devicetype::try_from(raw)?)
}
pub fn device_type_name(&self) -> Result<String> {
ffi_string(64, |buf, len| unsafe {
sys::icsneoc2_device_info_type_name_get(self.ptr, buf, len)
})
}
pub fn description(&self) -> Result<String> {
ffi_string(128, |buf, len| unsafe {
sys::icsneoc2_device_info_description_get(self.ptr, buf, len)
})
}
}
#[derive(Debug)]
struct DeviceInner {
ptr: NonNull<sys::icsneoc2_device_t>,
}
unsafe impl Send for DeviceInner {}
unsafe impl Sync for DeviceInner {}
impl Drop for DeviceInner {
fn drop(&mut self) {
let raw = unsafe { sys::icsneoc2_device_close(self.ptr.as_ptr()) };
match sys::Error::try_from(raw) {
Ok(sys::Error::Success) => {}
Ok(code) => eprintln!("Failed to close device: {}", api_error(code)),
Err(e) => eprintln!("Failed to close device: {e}"),
}
let raw = unsafe { sys::icsneoc2_device_free(self.ptr.as_ptr()) };
match sys::Error::try_from(raw) {
Ok(sys::Error::Success) => {}
Ok(code) => eprintln!("Failed to free device: {}", api_error(code)),
Err(e) => eprintln!("Failed to free device: {e}"),
}
}
}
#[derive(Debug, Clone)]
pub struct Device {
inner: Arc<DeviceInner>,
}
impl Device {
pub fn as_mut_ptr(&self) -> *mut icsneoc2_device_t {
self.inner.ptr.as_ptr()
}
pub fn settings(&self) -> Settings<'_> {
Settings::new(self.as_mut_ptr())
}
pub fn enumerate(device_type: sys::icsneoc2_devicetype_t) -> Result<DeviceInfoList> {
let mut head: *mut sys::icsneoc2_device_info_t = std::ptr::null_mut();
check(unsafe { sys::icsneoc2_device_enumerate(device_type, &raw mut head) })?;
Ok(DeviceInfoList { head })
}
pub fn from_info(info: &DeviceInfo<'_>) -> Result<Self> {
let mut device: *mut icsneoc2_device_t = std::ptr::null_mut();
check(unsafe { sys::icsneoc2_device_create(info.as_ptr(), &raw mut device) })?;
Ok(Device {
inner: Arc::new(DeviceInner {
ptr: NonNull::new(device).ok_or_else(|| {
Error::MemoryError(
"icsneoc2_device_create() returned a null pointer".to_string(),
)
})?,
}),
})
}
pub fn open(&self, options: OpenOptions) -> Result<()> {
check(unsafe { sys::icsneoc2_device_open(self.as_mut_ptr(), options.into()) })
}
pub fn open_first(
device_type: sys::icsneoc2_devicetype_t,
options: OpenOptions,
) -> Result<Self> {
let mut device: *mut icsneoc2_device_t = std::ptr::null_mut();
check(unsafe {
sys::icsneoc2_device_open_first(device_type, options.into(), &raw mut device)
})?;
Ok(Device {
inner: Arc::new(DeviceInner {
ptr: NonNull::new(device).ok_or_else(|| {
Error::MemoryError(
"icsneoc2_device_open_first() returned a null pointer".to_string(),
)
})?,
}),
})
}
pub fn open_serial(serial: &str, options: OpenOptions) -> Result<Self> {
let serial_c = CString::new(serial)?;
let mut device: *mut icsneoc2_device_t = std::ptr::null_mut();
check(unsafe {
sys::icsneoc2_device_open_serial(serial_c.as_ptr(), options.into(), &raw mut device)
})?;
Ok(Device {
inner: Arc::new(DeviceInner {
ptr: NonNull::new(device).ok_or_else(|| {
Error::MemoryError(
"icsneoc2_device_open_serial() returned a null pointer".to_string(),
)
})?,
}),
})
}
pub fn description(&self) -> Result<String> {
ffi_string(255, |buf, len| unsafe {
sys::icsneoc2_device_description_get(self.as_mut_ptr(), buf, len)
})
}
pub fn events(&self) -> Result<Vec<Event>> {
crate::event::events(Some(self))
}
pub fn go_online(&self, go_online: bool) -> Result<()> {
check(unsafe { sys::icsneoc2_device_go_online(self.as_mut_ptr(), go_online) })
}
pub fn is_online(&self) -> Result<bool> {
let mut value = false;
check(unsafe { sys::icsneoc2_device_is_online(self.as_mut_ptr(), &raw mut value) })?;
Ok(value)
}
pub fn is_online_supported(&self) -> Result<bool> {
let mut value = false;
check(unsafe {
sys::icsneoc2_device_is_online_supported(self.as_mut_ptr(), &raw mut value)
})?;
Ok(value)
}
pub fn is_valid(&self) -> Result<bool> {
let raw = unsafe { sys::icsneoc2_device_is_valid(self.as_mut_ptr()) };
match sys::Error::try_from(raw) {
Ok(sys::Error::Success) => Ok(true),
Ok(sys::Error::InvalidDevice) => Ok(false),
Ok(code) => Err(api_error(code)),
Err(e) => Err(Error::from(e)),
}
}
pub fn is_open(&self) -> Result<bool> {
let mut value = false;
check(unsafe { sys::icsneoc2_device_is_open(self.as_mut_ptr(), &raw mut value) })?;
Ok(value)
}
pub fn reconnect(&self, options: OpenOptions, timeout_ms: u32) -> Result<()> {
check(unsafe {
sys::icsneoc2_device_reconnect(self.as_mut_ptr(), options.into(), timeout_ms)
})
}
pub fn message_polling_limit(&self) -> Result<u32> {
let mut value: u32 = 0;
check(unsafe {
sys::icsneoc2_device_message_polling_limit_get(self.as_mut_ptr(), &raw mut value)
})?;
Ok(value)
}
pub fn set_message_polling_limit(&self, value: u32) -> Result<()> {
check(unsafe { sys::icsneoc2_device_message_polling_limit_set(self.as_mut_ptr(), value) })
}
pub fn message(&self, timeout_ms: u32) -> Result<Message> {
let mut ptr: *mut icsneoc2_message_t = std::ptr::null_mut();
check(unsafe {
sys::icsneoc2_device_message_get(self.as_mut_ptr(), &raw mut ptr, timeout_ms)
})?;
unsafe { Message::from_owned_ptr(ptr) }
}
pub fn transmit(&self, msg: &Message) -> Result<()> {
check(unsafe { sys::icsneoc2_device_message_transmit(self.as_mut_ptr(), msg.as_mut_ptr()) })
}
pub fn rtc(&self) -> Result<i64> {
let mut value: i64 = 0;
check(unsafe { sys::icsneoc2_device_rtc_get(self.as_mut_ptr(), &raw mut value) })?;
Ok(value)
}
pub fn set_rtc(&self, value: i64) -> Result<()> {
check(unsafe { sys::icsneoc2_device_rtc_set(self.as_mut_ptr(), value) })
}
pub fn serial(&self) -> Result<String> {
ffi_string(255, |buf, len| unsafe {
sys::icsneoc2_device_serial_get(self.as_mut_ptr(), buf, len)
})
}
pub fn supports_tc10(&self) -> Result<bool> {
let mut value = false;
check(unsafe { sys::icsneoc2_device_supports_tc10(self.as_mut_ptr(), &raw mut value) })?;
Ok(value)
}
pub fn timestamp_resolution(&self) -> Result<u32> {
let mut value: u32 = 0;
check(unsafe {
sys::icsneoc2_device_timestamp_resolution_get(self.as_mut_ptr(), &raw mut value)
})?;
Ok(value)
}
pub fn device_type(&self) -> Result<sys::Devicetype> {
let mut raw: sys::icsneoc2_devicetype_t = 0;
check(unsafe { sys::icsneoc2_device_type_get(self.as_mut_ptr(), &raw mut raw) })?;
Ok(sys::Devicetype::try_from(raw)?)
}
pub fn device_type_name(&self) -> Result<String> {
Ok(self.device_type()?.to_string())
}
pub fn digital_io(&self, io_type: sys::IoType, number: u32) -> Result<bool> {
let mut value = false;
check(unsafe {
sys::icsneoc2_device_digital_io_get(
self.as_mut_ptr(),
sys::icsneoc2_io_type_t::from(io_type),
number,
&raw mut value,
)
})?;
Ok(value)
}
pub fn set_digital_io(&self, io_type: sys::IoType, number: u32, value: bool) -> Result<()> {
check(unsafe {
sys::icsneoc2_device_digital_io_set(
self.as_mut_ptr(),
sys::icsneoc2_io_type_t::from(io_type),
number,
value,
)
})
}
pub fn supported_rx_networks(&self) -> Result<Vec<sys::Netid>> {
let mut count: usize = 0;
check(unsafe {
sys::icsneoc2_device_supported_rx_networks_get(
self.as_mut_ptr(),
std::ptr::null_mut(),
&raw mut count,
)
})?;
if count == 0 {
return Ok(vec![]);
}
let mut raw: Vec<sys::icsneoc2_netid_t> = vec![0; count];
check(unsafe {
sys::icsneoc2_device_supported_rx_networks_get(
self.as_mut_ptr(),
raw.as_mut_ptr(),
&raw mut count,
)
})?;
raw.truncate(count);
raw.into_iter()
.map(|v| Ok(sys::Netid::try_from(v)?))
.collect()
}
pub fn supported_tx_networks(&self) -> Result<Vec<sys::Netid>> {
let mut count: usize = 0;
check(unsafe {
sys::icsneoc2_device_supported_tx_networks_get(
self.as_mut_ptr(),
std::ptr::null_mut(),
&raw mut count,
)
})?;
if count == 0 {
return Ok(vec![]);
}
let mut raw: Vec<sys::icsneoc2_netid_t> = vec![0; count];
check(unsafe {
sys::icsneoc2_device_supported_tx_networks_get(
self.as_mut_ptr(),
raw.as_mut_ptr(),
&raw mut count,
)
})?;
raw.truncate(count);
raw.into_iter()
.map(|v| Ok(sys::Netid::try_from(v)?))
.collect()
}
pub fn supports_disk_formatting(&self) -> Result<bool> {
let mut value = false;
check(unsafe {
sys::icsneoc2_device_supports_disk_formatting(self.as_mut_ptr(), &raw mut value)
})?;
Ok(value)
}
pub fn disk_count(&self) -> Result<usize> {
let mut value: usize = 0;
check(unsafe { sys::icsneoc2_device_disk_count_get(self.as_mut_ptr(), &raw mut value) })?;
Ok(value)
}
pub fn disk_details(&self) -> Result<crate::disk::DiskDetails> {
let mut ptr: *mut sys::icsneoc2_disk_details_t = std::ptr::null_mut();
check(unsafe { sys::icsneoc2_device_disk_details_get(self.as_mut_ptr(), &raw mut ptr) })?;
NonNull::new(ptr)
.map(crate::disk::DiskDetails::new)
.ok_or_else(|| {
Error::MemoryError(
"icsneoc2_device_disk_details_get returned a null pointer".to_string(),
)
})
}
pub unsafe fn format_disk(
&self,
details: &crate::disk::DiskDetails,
progress: sys::icsneoc2_disk_format_progress_fn,
user_data: *mut std::ffi::c_void,
) -> Result<()> {
check(unsafe {
sys::icsneoc2_device_format_disk(
self.as_mut_ptr(),
details.as_ptr(),
progress,
user_data,
)
})
}
pub fn supports_coremini_script(&self) -> Result<bool> {
let mut value = false;
check(unsafe {
sys::icsneoc2_device_supports_coremini_script(self.as_mut_ptr(), &raw mut value)
})?;
Ok(value)
}
pub fn script_start(&self, memory_type: sys::MemoryType) -> Result<()> {
check(unsafe {
sys::icsneoc2_device_script_start(
self.as_mut_ptr(),
sys::icsneoc2_memory_type_t::from(memory_type),
)
})
}
pub fn script_stop(&self) -> Result<()> {
check(unsafe { sys::icsneoc2_device_script_stop(self.as_mut_ptr()) })
}
pub fn script_clear(&self, memory_type: sys::MemoryType) -> Result<()> {
check(unsafe {
sys::icsneoc2_device_script_clear(
self.as_mut_ptr(),
sys::icsneoc2_memory_type_t::from(memory_type),
)
})
}
pub fn script_prepare_load(&self) -> Result<i8> {
let mut status: i8 = 0;
check(unsafe {
sys::icsneoc2_device_script_prepare_load(self.as_mut_ptr(), &raw mut status)
})?;
Ok(status)
}
pub fn coremini_upload_file(
&self,
path: &std::path::Path,
memory_type: sys::MemoryType,
) -> Result<()> {
let path_c = CString::new(path.to_str().ok_or_else(|| {
Error::StringConversionError("path contains invalid UTF-8".to_string())
})?)?;
check(unsafe {
sys::icsneoc2_device_coremini_upload_file(
self.as_mut_ptr(),
path_c.as_ptr(),
sys::icsneoc2_memory_type_t::from(memory_type),
)
})
}
pub fn coremini_upload(&self, data: &[u8], memory_type: sys::MemoryType) -> Result<()> {
check(unsafe {
sys::icsneoc2_device_coremini_upload(
self.as_mut_ptr(),
data.as_ptr(),
data.len(),
sys::icsneoc2_memory_type_t::from(memory_type),
)
})
}
pub fn script_status(&self) -> Result<crate::script::ScriptStatus> {
let mut ptr: *mut sys::icsneoc2_script_status_t = std::ptr::null_mut();
check(unsafe { sys::icsneoc2_device_script_status_get(self.as_mut_ptr(), &raw mut ptr) })?;
NonNull::new(ptr)
.map(crate::script::ScriptStatus::new)
.ok_or_else(|| {
Error::MemoryError(
"icsneoc2_device_script_status_get returned a null pointer".to_string(),
)
})
}
}