use auto_impl::auto_impl;
use tracing::info;
use super::{
genapi::{DefaultGenApiCtxt, FromXml, GenApiCtxt, ParamsCtxt},
payload::{channel, PayloadReceiver, PayloadSender},
CameleonError, CameleonResult, ControlResult, StreamError, StreamResult,
};
#[derive(Debug, Clone)]
pub struct Camera<Ctrl, Strm, Ctxt = DefaultGenApiCtxt> {
pub ctrl: Ctrl,
pub strm: Strm,
pub ctxt: Option<Ctxt>,
info: CameraInfo,
}
macro_rules! expect_node {
($ctxt:expr, $name:expr, $as_type:ident) => {{
let err_msg = std::concat!("missing ", $name);
let err_msg2 = std::concat!($name, " has invalid interface");
$ctxt
.node($name)
.ok_or_else(|| CameleonError::InvalidGenApiXml(err_msg.into()))?
.$as_type($ctxt)
.ok_or_else(|| CameleonError::InvalidGenApiXml(err_msg2.into()))?
}};
}
impl<Ctrl, Strm, Ctxt> Camera<Ctrl, Strm, Ctxt> {
#[tracing::instrument(skip(self),
level = "info",
fields(camera = ?self.info()))]
pub fn open(&mut self) -> CameleonResult<()>
where
Ctrl: DeviceControl,
Strm: PayloadStream,
{
info!("try opening the device");
self.ctrl.open()?;
self.strm.open()?;
info!("opened the device successfully");
Ok(())
}
#[tracing::instrument(skip(self),
level = "info",
fields(camera = ?self.info()))]
pub fn close(&mut self) -> CameleonResult<()>
where
Ctrl: DeviceControl,
Strm: PayloadStream,
Ctxt: GenApiCtxt,
{
info!("try closing the device");
self.stop_streaming()?;
self.ctrl.close()?;
self.strm.close()?;
if let Some(ctxt) = &mut self.ctxt {
ctxt.clear_cache()
}
info!("closed the device successfully");
Ok(())
}
pub fn load_context(&mut self) -> CameleonResult<String>
where
Ctrl: DeviceControl,
Strm: PayloadStream,
Ctxt: GenApiCtxt + FromXml,
{
let xml = self.ctrl.genapi()?;
self.ctxt = Some(Ctxt::from_xml(&xml)?);
Ok(xml)
}
#[tracing::instrument(skip(self),
level = "info",
fields(camera = ?self.info()))]
pub fn start_streaming(&mut self, cap: usize) -> CameleonResult<PayloadReceiver>
where
Ctrl: DeviceControl,
Strm: PayloadStream,
Ctxt: GenApiCtxt,
{
const DEFAULT_BUFFER_CAP: usize = 5;
info!("try starting streaming");
if self.strm.is_loop_running() {
return Err(StreamError::InStreaming.into());
}
self.ctrl.enable_streaming()?;
let mut ctxt = self.params_ctxt()?;
expect_node!(&ctxt, "TLParamsLocked", as_integer).set_value(&mut ctxt, 1)?;
expect_node!(&ctxt, "AcquisitionStart", as_command).execute(&mut ctxt)?;
let (sender, receiver) = channel(cap, DEFAULT_BUFFER_CAP);
self.strm.start_streaming_loop(sender, &mut self.ctrl)?;
info!("start streaming successfully");
Ok(receiver)
}
#[tracing::instrument(skip(self),
level = "info",
fields(camera = ?self.info()))]
pub fn stop_streaming(&mut self) -> CameleonResult<()>
where
Ctrl: DeviceControl,
Strm: PayloadStream,
Ctxt: GenApiCtxt,
{
info!("try stopping streaming");
if !self.strm.is_loop_running() {
return Ok(());
}
self.strm.stop_streaming_loop()?;
let mut ctxt = self.params_ctxt()?;
expect_node!(&ctxt, "AcquisitionStop", as_command).execute(&mut ctxt)?;
expect_node!(&ctxt, "TLParamsLocked", as_integer).set_value(&mut ctxt, 0)?;
self.ctrl.disable_streaming()?;
info!("stop streaming successfully");
Ok(())
}
pub fn params_ctxt(&mut self) -> CameleonResult<ParamsCtxt<&mut Ctrl, &mut Ctxt>>
where
Ctrl: DeviceControl,
Strm: PayloadStream,
Ctxt: GenApiCtxt,
{
if let Some(ctxt) = self.ctxt.as_mut() {
Ok(ParamsCtxt {
ctrl: &mut self.ctrl,
ctxt,
})
} else {
Err(CameleonError::GenApiContextMissing)
}
}
pub fn info(&self) -> &CameraInfo {
&self.info
}
pub fn new(ctrl: Ctrl, strm: Strm, ctxt: Option<Ctxt>, info: CameraInfo) -> Self {
Self {
ctrl,
strm,
ctxt,
info,
}
}
pub fn convert_from<Ctrl2, Strm2, Ctxt2>(from: Camera<Ctrl2, Strm2, Ctxt2>) -> Self
where
Ctrl: From<Ctrl2>,
Strm: From<Strm2>,
Ctxt: From<Ctxt2>,
{
Camera::new(
from.ctrl.into(),
from.strm.into(),
from.ctxt.map(|ctxt| ctxt.into()),
from.info,
)
}
pub fn convert_into<Ctrl2, Strm2, Ctxt2>(self) -> Camera<Ctrl2, Strm2, Ctxt2>
where
Ctrl: Into<Ctrl2>,
Strm: Into<Strm2>,
Ctxt: Into<Ctxt2>,
{
Camera::new(
self.ctrl.into(),
self.strm.into(),
self.ctxt.map(|ctxt| ctxt.into()),
self.info,
)
}
pub fn set_context<Ctxt2>(self, ctxt: Ctxt2) -> Camera<Ctrl, Strm, Ctxt2> {
Camera {
ctrl: self.ctrl,
strm: self.strm,
ctxt: Some(ctxt),
info: self.info,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CameraInfo {
pub vendor_name: String,
pub model_name: String,
pub serial_number: String,
}
#[auto_impl(&mut, Box)]
pub trait DeviceControl {
fn open(&mut self) -> ControlResult<()>;
fn close(&mut self) -> ControlResult<()>;
fn is_opened(&self) -> bool;
fn read(&mut self, address: u64, buf: &mut [u8]) -> ControlResult<()>;
fn write(&mut self, address: u64, data: &[u8]) -> ControlResult<()>;
fn genapi(&mut self) -> ControlResult<String>;
fn enable_streaming(&mut self) -> ControlResult<()>;
fn disable_streaming(&mut self) -> ControlResult<()>;
}
#[auto_impl(&mut, Box)]
pub trait PayloadStream {
fn open(&mut self) -> StreamResult<()>;
fn close(&mut self) -> StreamResult<()>;
fn start_streaming_loop(
&mut self,
sender: PayloadSender,
ctrl: &mut dyn DeviceControl,
) -> StreamResult<()>;
fn stop_streaming_loop(&mut self) -> StreamResult<()>;
fn is_loop_running(&self) -> bool;
}