use crate::{
file::OsdpFileOps, Channel, OsdpCommand, OsdpError, OsdpEvent, OsdpFlag, PdCapability, PdId,
PdInfoBuilder,
};
use alloc::{boxed::Box, vec::Vec};
use core::ffi::c_void;
#[cfg(feature = "defmt-03")]
use defmt::{debug, error, info, warn};
#[cfg(all(feature = "log", not(feature = "defmt-03")))]
use log::{debug, error, info, warn};
type Result<T> = core::result::Result<T, OsdpError>;
unsafe extern "C" fn log_handler(
_log_level: ::core::ffi::c_int,
_file: *const ::core::ffi::c_char,
_line: ::core::ffi::c_ulong,
_msg: *const ::core::ffi::c_char,
) {
#[cfg(any(feature = "log", feature = "defmt-03"))]
{
let msg = crate::cstr_to_string(_msg);
let msg = msg.trim();
match _log_level as libosdp_sys::osdp_log_level_e {
libosdp_sys::osdp_log_level_e_OSDP_LOG_EMERG => error!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_ALERT => error!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_CRIT => error!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_ERROR => error!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_WARNING => warn!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_NOTICE => warn!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_INFO => info!("CP: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_DEBUG => debug!("CP: {}", msg),
_ => panic!("Unknown log level"),
};
}
}
extern "C" fn trampoline<F>(data: *mut c_void, pd: i32, event: *mut libosdp_sys::osdp_event) -> i32
where
F: FnMut(i32, OsdpEvent) -> i32,
{
let event: OsdpEvent = unsafe { (*event).into() };
let callback: &mut F = unsafe { &mut *(data as *mut F) };
callback(pd, event)
}
type EventCallback =
unsafe extern "C" fn(data: *mut c_void, pd: i32, event: *mut libosdp_sys::osdp_event) -> i32;
fn get_trampoline<F>(_closure: &F) -> EventCallback
where
F: FnMut(i32, OsdpEvent) -> i32,
{
trampoline::<F>
}
fn cp_setup(info: Vec<crate::OsdpPdInfoHandle>) -> Result<*mut c_void> {
let ctx = unsafe { libosdp_sys::osdp_cp_setup(info.len() as i32, info.as_ptr() as *const _) };
if ctx.is_null() {
Err(OsdpError::Setup)
} else {
Ok(ctx)
}
}
#[derive(Debug, Default)]
pub struct ControlPanelBuilder {
channel_pds: Vec<(Box<dyn Channel>, Vec<PdInfoBuilder>)>,
}
impl ControlPanelBuilder {
pub const fn new() -> Self {
Self {
channel_pds: Vec::new(),
}
}
pub fn add_channel(mut self, channel: Box<dyn Channel>, pd_info: Vec<PdInfoBuilder>) -> Self {
self.channel_pds.push((channel, pd_info));
self
}
pub fn build(self) -> Result<ControlPanel> {
if self.channel_pds.len() > 126 {
return Err(OsdpError::PdInfo("max PD count exceeded"));
}
let info: Vec<crate::OsdpPdInfoHandle> = self
.channel_pds
.into_iter()
.map(|(channel, pd_info)| {
let channel: libosdp_sys::osdp_channel = channel.into();
pd_info
.into_iter()
.map(move |pd| pd.channel(channel).build().into())
})
.flatten()
.collect();
unsafe { libosdp_sys::osdp_set_log_callback(Some(log_handler)) };
Ok(ControlPanel {
ctx: cp_setup(info)?,
})
}
}
#[derive(Debug)]
pub struct ControlPanel {
ctx: *mut core::ffi::c_void,
}
unsafe impl Send for ControlPanel {}
impl ControlPanel {
pub fn refresh(&mut self) {
unsafe { libosdp_sys::osdp_cp_refresh(self.ctx) }
}
pub fn send_command(&mut self, pd: i32, cmd: OsdpCommand) -> Result<()> {
let rc = unsafe { libosdp_sys::osdp_cp_send_command(self.ctx, pd, &cmd.into()) };
if rc < 0 {
Err(OsdpError::Command)
} else {
Ok(())
}
}
pub fn set_event_callback<F>(&mut self, closure: F)
where
F: FnMut(i32, OsdpEvent) -> i32,
{
unsafe {
let callback = get_trampoline(&closure);
libosdp_sys::osdp_cp_set_event_callback(
self.ctx,
Some(callback),
Box::into_raw(Box::new(closure)).cast(),
);
}
}
pub fn get_pd_id(&self, pd: i32) -> Result<PdId> {
let mut pd_id: libosdp_sys::osdp_pd_id =
unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let rc = unsafe { libosdp_sys::osdp_cp_get_pd_id(self.ctx, pd, &mut pd_id) };
if rc < 0 {
Err(OsdpError::Query("PdId"))
} else {
Ok(pd_id.into())
}
}
pub fn get_capability(&self, pd: i32, cap: PdCapability) -> Result<PdCapability> {
let mut cap = cap.into();
let rc = unsafe { libosdp_sys::osdp_cp_get_capability(self.ctx, pd, &mut cap) };
if rc < 0 {
Err(OsdpError::Query("capability"))
} else {
Ok(cap.into())
}
}
pub fn set_flag(&mut self, pd: i32, flags: OsdpFlag, value: bool) {
let rc = unsafe { libosdp_sys::osdp_cp_modify_flag(self.ctx, pd, flags.bits(), value) };
if rc < 0 {
panic!("osdp_cp_modify_flag failed!")
}
}
pub fn is_online(&self, pd: i32) -> bool {
let mut buf: [u8; 16] = [0; 16];
unsafe { libosdp_sys::osdp_get_status_mask(self.ctx, &mut buf as *mut u8) };
let pos = pd / 8;
let idx = pd % 8;
buf[pos as usize] & (1 << idx) != 0
}
pub fn is_sc_active(&self, pd: i32) -> bool {
let mut buf: [u8; 16] = [0; 16];
unsafe { libosdp_sys::osdp_get_sc_status_mask(self.ctx, &mut buf as *mut u8) };
let pos = pd / 8;
let idx = pd % 8;
buf[pos as usize] & (1 << idx) != 0
}
pub fn file_transfer_status(&self, pd: i32) -> Result<(i32, i32)> {
let mut size: i32 = 0;
let mut offset: i32 = 0;
let rc = unsafe {
libosdp_sys::osdp_get_file_tx_status(
self.ctx,
pd,
&mut size as *mut i32,
&mut offset as *mut i32,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("Not not in progress"))
} else {
Ok((size, offset))
}
}
pub fn register_file_ops(&mut self, pd: i32, fops: Box<dyn OsdpFileOps>) -> Result<()> {
let mut fops: libosdp_sys::osdp_file_ops = fops.into();
let rc = unsafe {
libosdp_sys::osdp_file_register_ops(
self.ctx,
pd,
&mut fops as *mut libosdp_sys::osdp_file_ops,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("ops register"))
} else {
Ok(())
}
}
}
impl Drop for ControlPanel {
fn drop(&mut self) {
unsafe { libosdp_sys::osdp_cp_teardown(self.ctx) }
}
}