use std::ffi::{c_char, c_int, c_long};
use playerone_sdk_sys::POABool::{POA_FALSE, POA_TRUE};
use playerone_sdk_sys::POAConfig::{POA_EXPOSURE, POA_GAIN};
use playerone_sdk_sys::{
FromPOAConfigValue, POACameraProperties, POACloseCamera, POAConfigAttributes, POAConfigValue,
POAErrors, POAGetCameraCount, POAGetCameraProperties, POAGetConfig, POAGetConfigAttributes,
POAGetConfigsCount, POAGetImageBin, POAGetImageData, POAGetImageFormat, POAGetImageSize,
POAGetImageStartPos, POAGetSensorMode, POAGetSensorModeCount, POAGetSensorModeInfo,
POAImageReady, POAInitCamera, POAOpenCamera, POASensorModeInfo, POASetConfig, POASetEnableDPS,
POASetImageBin, POASetImageFormat, POASetImageSize, POASetImageStartPos, POASetSensorMode,
POAStartExposure, POAStopExposure, _POABool as POABool, _POAConfig as POAConfig, _POAErrors,
_POAImgFormat as POAImgFormat,
};
use crate::{AllConfigBounds, CameraProperties, Error, ImageFormat, SensorMode};
type POAResult<T> = Result<T, Error>;
#[derive(Debug, Copy, Clone)]
pub struct ROI {
pub start_x: u32,
pub start_y: u32,
pub width: u32,
pub height: u32,
}
pub struct CameraDescription {
camera_id: i32,
properties: CameraProperties,
}
impl CameraDescription {
pub fn camera_id(&self) -> i32 {
self.camera_id
}
pub fn properties(&self) -> &CameraProperties {
&self.properties
}
pub fn open(self) -> POAResult<Camera> {
let mut camera = Camera {
camera_id: self.camera_id,
closed: false,
properties: self.properties,
};
camera.open()?;
Ok(camera)
}
}
#[derive(Debug)]
pub struct Camera {
camera_id: i32,
closed: bool,
properties: CameraProperties,
}
impl Drop for Camera {
fn drop(&mut self) {
if !self.closed {
let _ = unsafe { POACloseCamera(self.camera_id) };
}
}
}
impl Camera {
pub fn all_cameras() -> Vec<CameraDescription> {
let camera_count = unsafe { POAGetCameraCount() };
let mut cameras = Vec::with_capacity(camera_count as usize);
for i in 0..camera_count {
let mut camera_prop: POACameraProperties = POACameraProperties::default();
let error = unsafe { POAGetCameraProperties(i, &raw mut camera_prop) };
if error != _POAErrors::POA_OK {
continue;
}
cameras.push(CameraDescription {
camera_id: camera_prop.cameraID,
properties: camera_prop.into(),
});
}
cameras
}
fn open(&mut self) -> POAResult<()> {
let error = unsafe { POAOpenCamera(self.camera_id) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
let error = unsafe { POAInitCamera(self.camera_id) };
if error != _POAErrors::POA_OK {
unsafe { POACloseCamera(self.camera_id) };
return Err(error.into());
}
Ok(())
}
pub fn capture(&mut self, buffer: &mut [u8], timeout: Option<i32>) -> POAResult<()> {
let error = unsafe { POAStartExposure(self.camera_id, POA_TRUE) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
self.get_image_data(buffer, timeout)?;
self.stop_exposure()?;
Ok(())
}
pub fn stream(
&mut self,
timeout: Option<u32>,
mut callback: impl FnMut(&mut Camera, &[u8]) -> bool,
) -> POAResult<()> {
if let Some(timeout) = timeout {
if timeout > i32::MAX as u32 {
return Err(Error::OutOfBounds);
}
}
let mut buffer = self.create_image_buffer();
self.start_exposure()?;
loop {
match self.get_image_data(&mut buffer, timeout.map(|t| t as i32)) {
Ok(_) => (),
Err(e) => {
let _ = self.stop_exposure();
return Err(e);
}
}
if !callback(self, &buffer) {
break;
}
}
self.stop_exposure()?;
Ok(())
}
pub fn create_image_buffer(&self) -> Vec<u8> {
let (w, h) = self.image_size();
let format = self.image_format().unwrap();
vec![0; w as usize * h as usize * format.bytes_per_pixel()]
}
pub fn start_exposure(&mut self) -> POAResult<()> {
let error = unsafe { POAStartExposure(self.camera_id, POA_FALSE) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn is_image_ready(&self) -> POAResult<bool> {
let mut is_img_data_available = POA_FALSE;
let error = unsafe { POAImageReady(self.camera_id, &raw mut is_img_data_available) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(is_img_data_available.into())
}
pub fn get_image_data(&self, buffer: &mut [u8], timeout_ms: Option<i32>) -> POAResult<()> {
let error = unsafe {
POAGetImageData(
self.camera_id,
buffer.as_mut_ptr(),
buffer.len() as c_long,
timeout_ms.unwrap_or(-1),
)
};
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn stop_exposure(&mut self) -> POAResult<()> {
let error = unsafe { POAStopExposure(self.camera_id) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn close(mut self) -> POAResult<()> {
self.closed = true;
let error = unsafe { POACloseCamera(self.camera_id) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn config_bounds(&self) -> AllConfigBounds {
let mut config_count = 0;
safe_error(unsafe { POAGetConfigsCount(self.camera_id, &raw mut config_count) });
let mut attributes = Vec::with_capacity(40);
for i in 0..config_count {
let mut conf_attributes = POAConfigAttributes::default();
safe_error(unsafe {
POAGetConfigAttributes(self.camera_id, i, &raw mut conf_attributes)
});
attributes.push(conf_attributes);
}
AllConfigBounds::from(attributes)
}
pub fn set_dps(&mut self, dps: bool) -> POAResult<()> {
let b: POABool = dps.into();
let error = unsafe { POASetEnableDPS(self.camera_id, &raw const b) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn set_roi(&mut self, roi_area: &ROI) -> POAResult<()> {
self.set_image_size(roi_area.width, roi_area.height)?;
self.set_image_start_pos(roi_area.start_x, roi_area.start_y)?;
Ok(())
}
pub fn roi(&self) -> ROI {
let start_pos = self.image_start_pos().unwrap();
let size = self.image_size();
ROI {
start_x: start_pos.0,
start_y: start_pos.1,
width: size.0,
height: size.1,
}
}
pub fn set_image_size(&mut self, width: u32, height: u32) -> POAResult<()> {
if width > self.properties.max_width || height > self.properties.max_height {
return Err(Error::OutOfBounds);
}
let error = unsafe { POASetImageSize(self.camera_id, width as c_int, height as c_int) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn image_size(&self) -> (u32, u32) {
let mut width = 0;
let mut height = 0;
safe_error(unsafe { POAGetImageSize(self.camera_id, &raw mut width, &raw mut height) });
if width < 0 || height < 0 {
panic!("negative image size: {} {}", width, height);
}
(width as u32, height as u32)
}
pub fn set_image_start_pos(&mut self, start_x: u32, start_y: u32) -> POAResult<()> {
if start_x > self.properties.max_width || start_y > self.properties.max_height {
return Err(Error::OutOfBounds);
}
let error =
unsafe { POASetImageStartPos(self.camera_id, start_x as c_int, start_y as c_int) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn image_start_pos(&self) -> POAResult<(u32, u32)> {
let mut start_x = 0;
let mut start_y = 0;
safe_error(unsafe {
POAGetImageStartPos(self.camera_id, &raw mut start_x, &raw mut start_y)
});
if start_x < 0 || start_y < 0 {
panic!("negative image start position: {} {}", start_x, start_y);
}
Ok((start_x as u32, start_y as u32))
}
pub fn set_image_format(&mut self, image_format: ImageFormat) -> POAResult<()> {
let poa_img_format = image_format.into();
let error = unsafe { POASetImageFormat(self.camera_id, poa_img_format) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
pub fn image_format(&self) -> POAResult<ImageFormat> {
let mut poa_img_format = POAImgFormat::POA_END;
safe_error(unsafe { POAGetImageFormat(self.camera_id, &raw mut poa_img_format) });
Ok(poa_img_format.into())
}
pub fn set_bin(&mut self, bin: u32) -> POAResult<()> {
if !self.properties.bins.contains(&bin) {
return Err(Error::OutOfBounds);
}
let err = unsafe { POASetImageBin(self.camera_id, bin as c_int) };
if err != _POAErrors::POA_OK {
return Err(err.into());
}
Ok(())
}
pub fn bin(&self) -> u32 {
let mut bin = 0;
safe_error(unsafe { POAGetImageBin(self.camera_id, &raw mut bin) });
bin as u32
}
pub fn sensor_modes(&self) -> POAResult<Vec<SensorMode>> {
enumerate_sensor_modes(self.camera_id)
}
pub fn sensor_mode(&self) -> POAResult<u32> {
let mut index: c_int = 0;
let err = unsafe { POAGetSensorMode(self.camera_id, &raw mut index) };
if err != _POAErrors::POA_OK {
return Err(err.into());
}
if index < 0 {
return Err(Error::OperationFailed);
}
Ok(index as u32)
}
pub fn set_sensor_mode(&mut self, index: u32) -> POAResult<()> {
let err = unsafe { POASetSensorMode(self.camera_id, index as c_int) };
if err != _POAErrors::POA_OK {
return Err(err.into());
}
Ok(())
}
pub fn properties(&self) -> &CameraProperties {
&self.properties
}
pub fn id(&self) -> i32 {
self.camera_id
}
pub fn set_exposure(&mut self, exposure_micros: i64, is_auto: bool) -> POAResult<()> {
self.set_config(POA_EXPOSURE, exposure_micros, is_auto)
}
pub fn set_gain(&mut self, gain: i64, is_auto: bool) -> POAResult<()> {
self.set_config(POA_GAIN, gain, is_auto)
}
pub fn exposure(&self) -> POAResult<(i64, bool)> {
unsafe { self.get_config_auto(POA_EXPOSURE) }
}
pub fn gain(&self) -> POAResult<(i64, bool)> {
unsafe { self.get_config_auto(POA_GAIN) }
}
pub fn hardware_bin(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_HARDWARE_BIN) }
}
pub fn temperature(&self) -> POAResult<f64> {
unsafe { self.get_config(POAConfig::POA_TEMPERATURE) }
}
pub fn wb_r(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_WB_R) }
}
pub fn wb_g(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_WB_G) }
}
pub fn wb_b(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_WB_B) }
}
pub fn offset(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_OFFSET) }
}
pub fn auto_max_gain(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_AUTOEXPO_MAX_GAIN) }
}
pub fn auto_max_exposure_ms(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_AUTOEXPO_MAX_EXPOSURE) }
}
pub fn auto_target_brightness(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_AUTOEXPO_BRIGHTNESS) }
}
pub fn guide_north(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_GUIDE_NORTH) }
}
pub fn guide_south(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_GUIDE_SOUTH) }
}
pub fn guide_east(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_GUIDE_EAST) }
}
pub fn guide_west(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_GUIDE_WEST) }
}
pub fn egain(&self) -> POAResult<f64> {
unsafe { self.get_config(POAConfig::POA_EGAIN) }
}
pub fn cooler_power(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_COOLER_POWER) }
}
pub fn target_temp(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_TARGET_TEMP) }
}
pub fn cooler(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_COOLER) }
}
#[deprecated]
pub fn heater(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_HEATER) }
}
pub fn heater_power(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_HEATER_POWER) }
}
pub fn fan_power(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_FAN_POWER) }
}
pub fn frame_limit(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_FRAME_LIMIT) }
}
pub fn hqi(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_HQI) }
}
pub fn usb_bandwidth_limit(&self) -> POAResult<i64> {
unsafe { self.get_config(POAConfig::POA_USB_BANDWIDTH_LIMIT) }
}
pub fn pixel_bin_sum(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_PIXEL_BIN_SUM) }
}
pub fn mono_bin(&self) -> POAResult<bool> {
unsafe { self.get_config(POAConfig::POA_MONO_BIN) }
}
pub fn set_hardware_bin(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_HARDWARE_BIN, value, false)
}
pub fn set_wb_r(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_WB_R, value, false)
}
pub fn set_wb_g(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_WB_G, value, false)
}
pub fn set_wb_b(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_WB_B, value, false)
}
pub fn set_offset(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_OFFSET, value, false)
}
pub fn set_auto_max_gain(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_AUTOEXPO_MAX_GAIN, value, false)
}
pub fn set_auto_max_exposure_ms(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_AUTOEXPO_MAX_EXPOSURE, value, false)
}
pub fn set_auto_target_brightness(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_AUTOEXPO_BRIGHTNESS, value, false)
}
pub fn set_guide_north(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_GUIDE_NORTH, value, false)
}
pub fn set_guide_south(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_GUIDE_SOUTH, value, false)
}
pub fn set_guide_east(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_GUIDE_EAST, value, false)
}
pub fn set_guide_west(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_GUIDE_WEST, value, false)
}
pub fn set_target_temperature(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_TARGET_TEMP, value, false)
}
pub fn set_cooler(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_COOLER, value, false)
}
#[deprecated]
pub fn set_heater(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_HEATER, value, false)
}
pub fn set_heater_power(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_HEATER_POWER, value, false)
}
pub fn set_fan_power(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_FAN_POWER, value, false)
}
pub fn set_frame_limit(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_FRAME_LIMIT, value, false)
}
pub fn set_hqi(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_HQI, value, false)
}
pub fn set_usb_bandwidth_limit(&mut self, value: i64) -> POAResult<()> {
self.set_config(POAConfig::POA_USB_BANDWIDTH_LIMIT, value, false)
}
pub fn set_pixel_bin_sum(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_PIXEL_BIN_SUM, value, false)
}
pub fn set_mono_bin(&mut self, value: bool) -> POAResult<()> {
self.set_config(POAConfig::POA_MONO_BIN, value, false)
}
fn set_config(
&mut self,
poa_config: POAConfig,
value: impl Into<POAConfigValue>,
is_auto: bool,
) -> POAResult<()> {
let value = value.into();
let error = unsafe { POASetConfig(self.camera_id, poa_config, value, is_auto.into()) };
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok(())
}
unsafe fn get_config_auto<T: FromPOAConfigValue>(
&self,
poa_config: POAConfig,
) -> POAResult<(T, bool)> {
let mut config_value = POAConfigValue::default();
let mut is_auto = POABool::POA_FALSE;
let error = unsafe {
POAGetConfig(
self.camera_id,
poa_config,
&raw mut config_value,
&raw mut is_auto,
)
};
if error != _POAErrors::POA_OK {
return Err(error.into());
}
Ok((
FromPOAConfigValue::from_poa_config_value(config_value),
is_auto.into(),
))
}
unsafe fn get_config<T: FromPOAConfigValue>(&self, poa_config: POAConfig) -> POAResult<T> {
self.get_config_auto(poa_config).map(|(value, _)| value)
}
}
fn safe_error(error: POAErrors) {
if error == _POAErrors::POA_OK {
return;
}
panic!("unexpected POA error: {}", Error::from(error));
}
fn enumerate_sensor_modes(camera_id: i32) -> POAResult<Vec<SensorMode>> {
let mut count: c_int = 0;
let err = unsafe { POAGetSensorModeCount(camera_id, &raw mut count) };
if err != _POAErrors::POA_OK {
return Err(err.into());
}
if count <= 0 {
return Ok(Vec::new());
}
let mut modes = Vec::with_capacity(count as usize);
for index in 0..count {
let mut info = POASensorModeInfo::default();
let err = unsafe { POAGetSensorModeInfo(camera_id, index, &raw mut info) };
if err != _POAErrors::POA_OK {
return Err(err.into());
}
modes.push(SensorMode {
index: index as u32,
name: c_char_array_to_string(&info.name),
description: c_char_array_to_string(&info.desc),
});
}
Ok(modes)
}
fn c_char_array_to_string(buf: &[c_char]) -> String {
let nul = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
let bytes: Vec<u8> = buf[..nul].iter().map(|&c| c as u8).collect();
String::from_utf8_lossy(&bytes).trim().to_string()
}