use libloading::{Library, Symbol};
use std::ffi::{CStr, CString, c_char, c_double, c_int, c_void};
#[repr(C)]
pub struct VecValues {
pub name: *mut c_char, pub creal: f64, pub cimag: f64, pub is_scale: bool, pub is_complex: bool, }
#[repr(C)]
pub struct VecValuesAll {
pub veccount: c_int, pub vecindex: c_int, pub vecsa: *mut *mut VecValues, }
#[repr(C)]
pub struct NgComplex {
pub cx_real: f64,
pub cx_imag: f64,
}
#[repr(C)]
pub struct VectorInfo {
pub v_name: *mut c_char,
pub v_type: c_int,
pub v_flags: i16,
pub v_realdata: *mut c_double,
pub v_compdata: *mut NgComplex,
pub v_length: c_int,
}
#[repr(C)]
pub struct VecInfo {
pub number: c_int, pub vecname: *mut c_char, pub is_real: bool, pub pdvec: *mut c_void, pub pdvecscale: *mut c_void, }
#[repr(C)]
pub struct VecInfoAll {
pub name: *mut c_char, pub title: *mut c_char, pub date: *mut c_char, pub type_: *mut c_char, pub veccount: c_int, pub vecs: *mut *mut VecInfo, }
pub type SendChar = unsafe extern "C" fn(*mut c_char, c_int, *mut c_void) -> c_int;
pub type SendStat = SendChar;
pub type ControlledExit = unsafe extern "C" fn(c_int, bool, bool, c_int, *mut c_void) -> c_int;
pub type SendData = unsafe extern "C" fn(*mut VecValuesAll, c_int, c_int, *mut c_void) -> c_int;
pub type SendInitData = unsafe extern "C" fn(*mut VecInfoAll, c_int, *mut c_void) -> c_int;
pub type BGThreadRunning = unsafe extern "C" fn(bool, c_int, *mut c_void) -> c_int;
pub type GetVSRCData = unsafe extern "C" fn(*mut c_double, c_double, *mut c_char, c_int, *mut c_void) -> c_int;
pub type GetISRCData = GetVSRCData;
pub type GetSyncData = unsafe extern "C" fn(c_double, *mut c_double, c_double, c_int, c_int, c_int, *mut c_void) -> c_int;
pub struct NgSpiceAPI {
lib: Library, }
pub enum VecData {
Real(Vec<f64>),
Complex(Vec<(f64, f64)>),
}
impl NgSpiceAPI {
pub fn new(lib: Library) -> Self {
Self { lib }
}
pub fn init(
&self,
send_char: Option<SendChar>,
send_stat: Option<SendStat>,
controlled_exit: Option<ControlledExit>,
send_data: Option<SendData>,
send_init_data: Option<SendInitData>,
bg_thread_running: Option<BGThreadRunning>,
user_data: *mut c_void,
) -> i32 {
unsafe {
let f: Symbol<unsafe extern "C" fn(
Option<SendChar>,
Option<SendStat>,
Option<ControlledExit>,
Option<SendData>,
Option<SendInitData>,
Option<BGThreadRunning>,
*mut c_void,
) -> i32> = self.lib.get(b"ngSpice_Init\0").unwrap();
f(send_char, send_stat, controlled_exit, send_data, send_init_data, bg_thread_running, user_data)
}
}
pub fn init_sync(
&self,
get_vsrc_data: Option<GetVSRCData>,
get_isrc_data: Option<GetISRCData>,
get_sync_data: Option<GetSyncData>,
ret_code: *mut i32,
user_data: *mut c_void,
) -> i32 {
unsafe {
let f: Symbol<unsafe extern "C" fn(
Option<GetVSRCData>,
Option<GetISRCData>,
Option<GetSyncData>,
*mut i32,
*mut c_void,
) -> i32> = self.lib.get(b"ngSpice_Init_Sync\0").unwrap();
f(get_vsrc_data, get_isrc_data, get_sync_data, ret_code, user_data)
}
}
pub fn command(&self, cmd: &str) -> Result<i32, std::ffi::NulError> {
let cstr = CString::new(cmd)?;
unsafe {
let f: Symbol<unsafe extern "C" fn(*const c_char) -> i32> = self.lib.get(b"ngSpice_Command\0").unwrap();
Ok(f(cstr.as_ptr()))
}
}
pub fn circ(&self, lines: &[&str]) -> Result<i32, std::ffi::NulError> {
let mut cstrings: Vec<CString> = lines.iter().map(|s| CString::new(*s)).collect::<Result<_, _>>()?;
let mut ptrs: Vec<*mut c_char> = cstrings.iter_mut().map(|s| s.as_ptr() as *mut c_char).collect();
ptrs.push(std::ptr::null_mut());
unsafe {
let f: Symbol<unsafe extern "C" fn(*mut *mut c_char) -> i32> = self.lib.get(b"ngSpice_Circ\0").unwrap();
Ok(f(ptrs.as_mut_ptr()))
}
}
pub fn cur_plot(&self) -> Option<String> {
unsafe {
let f: Symbol<unsafe extern "C" fn() -> *mut c_char> = self.lib.get(b"ngSpice_CurPlot\0").unwrap();
let ptr = f();
if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
}
}
}
pub fn all_plots(&self) -> Vec<String> {
unsafe {
let f: Symbol<unsafe extern "C" fn() -> *mut *mut c_char> = self.lib.get(b"ngSpice_AllPlots\0").unwrap();
let mut result = Vec::new();
let mut ptr = f();
while !ptr.is_null() && !(*ptr).is_null() {
result.push(CStr::from_ptr(*ptr).to_string_lossy().into_owned());
ptr = ptr.add(1);
}
result
}
}
pub fn all_vecs(&self, plotname: &str) -> Result<Vec<String>, std::ffi::NulError> {
let cstr = CString::new(plotname)?;
unsafe {
let f: Symbol<unsafe extern "C" fn(*mut c_char) -> *mut *mut c_char> = self.lib.get(b"ngSpice_AllVecs\0").unwrap();
let mut result = Vec::new();
let mut ptr = f(cstr.into_raw());
while !ptr.is_null() && !(*ptr).is_null() {
result.push(CStr::from_ptr(*ptr).to_string_lossy().into_owned());
ptr = ptr.add(1);
}
Ok(result)
}
}
pub fn running(&self) -> bool {
unsafe {
let f: Symbol<unsafe extern "C" fn() -> bool> = self.lib.get(b"ngSpice_running\0").unwrap();
f()
}
}
pub fn set_breakpoint(&self, time: f64) -> bool {
unsafe {
let f: Symbol<unsafe extern "C" fn(c_double) -> bool> = self.lib.get(b"ngSpice_SetBkpt\0").unwrap();
f(time)
}
}
pub fn get_vec_real_data(&self, name: &str) -> Result<Option<Vec<f64>>, std::ffi::NulError> {
let cstr = CString::new(name)?;
unsafe {
let f: Symbol<unsafe extern "C" fn(*mut c_char) -> *mut VectorInfo> =
self.lib.get(b"ngGet_Vec_Info\0").unwrap();
let ptr = f(cstr.into_raw());
if ptr.is_null() {
return Ok(None);
}
let info = &*ptr;
if info.v_realdata.is_null() || info.v_length <= 0 {
return Ok(None);
}
let len = info.v_length as usize;
let slice = std::slice::from_raw_parts(info.v_realdata, len);
Ok(Some(slice.to_vec()))
}
}
pub fn get_vec_complex_data(&self, name: &str) -> Result<Option<Vec<(f64, f64)>>, std::ffi::NulError> {
let cstr = CString::new(name)?;
unsafe {
let f: Symbol<unsafe extern "C" fn(*mut c_char) -> *mut VectorInfo> =
self.lib.get(b"ngGet_Vec_Info\0").unwrap();
let ptr = f(cstr.into_raw());
if ptr.is_null() {
return Ok(None);
}
let info = &*ptr;
if info.v_compdata.is_null() || info.v_length <= 0 {
return Ok(None);
}
let len = info.v_length as usize;
let slice = std::slice::from_raw_parts(info.v_compdata, len);
Ok(Some(slice.iter().map(|c| (c.cx_real, c.cx_imag)).collect()))
}
}
pub fn get_vec_data(&self, name: &str) -> Result<Option<VecData>, std::ffi::NulError> {
let cstr = CString::new(name)?;
unsafe {
let f: Symbol<unsafe extern "C" fn(*mut c_char) -> *mut VectorInfo> =
self.lib.get(b"ngGet_Vec_Info\0").unwrap();
let ptr = f(cstr.into_raw());
if ptr.is_null() {
return Ok(None);
}
let info = &*ptr;
if info.v_length <= 0 {
return Ok(None);
}
match (info.v_realdata.is_null(), info.v_compdata.is_null()) {
(false, true) => {
let len = info.v_length as usize;
let slice = std::slice::from_raw_parts(info.v_realdata, len);
Ok(Some(VecData::Real(slice.to_vec())))
}
(true, false) => {
let len = info.v_length as usize;
let slice = std::slice::from_raw_parts(info.v_compdata, len);
Ok(Some(VecData::Complex(slice.iter().map(|c| (c.cx_real, c.cx_imag)).collect())))
},
(_, _) => return Ok(None),
}
}
}
}