use std::ffi::CStr;
use std::fmt::{Display, Formatter};
use playerone_sdk_sys::{
_POABayerPattern, _POACameraProperties, _POAConfig, _POAImgFormat, _POAValueType, POABool,
POAConfig, POAConfigAttributes, POAErrors, POAImgFormat,
};
#[derive(Debug, Clone)]
pub struct CameraProperties {
pub camera_model_name: String,
pub user_custom_id: String,
pub camera_id: u32,
pub max_width: u32,
pub max_height: u32,
pub bit_depth: u32,
pub is_color_camera: bool,
pub is_has_st_4_port: bool,
pub is_has_cooler: bool,
pub is_usb_3_speed: bool,
pub bayer_pattern: BayerPattern,
pub pixel_size: f64,
pub serial_number: String,
pub sensor_model_name: String,
pub local_path: String,
pub bins: Vec<u32>,
pub img_formats: Vec<POAImgFormat>,
pub is_support_hard_bin: bool,
pub product_id: i32,
}
impl From<_POACameraProperties> for CameraProperties {
fn from(value: _POACameraProperties) -> Self {
let camera_model_name = unsafe {
CStr::from_ptr(value.cameraModelName.as_ptr())
.to_string_lossy()
.to_string()
};
let user_custom_id = unsafe {
CStr::from_ptr(value.userCustomID.as_ptr())
.to_string_lossy()
.to_string()
};
let sn = unsafe {
CStr::from_ptr(value.SN.as_ptr())
.to_string_lossy()
.to_string()
};
let sensor_model_name = unsafe {
CStr::from_ptr(value.sensorModelName.as_ptr())
.to_string_lossy()
.to_string()
};
let local_path = unsafe {
CStr::from_ptr(value.localPath.as_ptr())
.to_string_lossy()
.to_string()
};
let mut bins = Vec::with_capacity(value.bins.len());
for bin in value.bins {
if bin <= 0 {
break;
}
bins.push(bin as u32);
}
let mut img_formats = Vec::with_capacity(value.imgFormats.len());
for img_format in value.imgFormats {
if img_format == _POAImgFormat::POA_END {
break;
}
img_formats.push(img_format.into());
}
Self {
camera_model_name,
user_custom_id,
camera_id: value.cameraID as u32,
max_width: value.maxWidth as u32,
max_height: value.maxHeight as u32,
bit_depth: value.bitDepth as u32,
is_color_camera: value.isColorCamera == POABool::POA_TRUE,
is_has_st_4_port: value.isHasST4Port == POABool::POA_TRUE,
is_has_cooler: value.isHasCooler == POABool::POA_TRUE,
is_usb_3_speed: value.isUSB3Speed == POABool::POA_TRUE,
bayer_pattern: value.bayerPattern.into(),
pixel_size: value.pixelSize,
serial_number: sn,
sensor_model_name,
local_path,
bins,
img_formats,
is_support_hard_bin: value.isSupportHardBin == POABool::POA_TRUE,
product_id: value.pID.try_into().expect("c_int is not i32"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SensorMode {
pub index: u32,
pub name: String,
pub description: String,
}
impl std::fmt::Display for SensorMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ImageFormat {
RAW8,
RAW16,
RGB24,
MONO8,
}
impl ImageFormat {
pub fn bytes_per_pixel(&self) -> usize {
use ImageFormat::*;
match self {
RAW8 => 1,
RAW16 => 2,
RGB24 => 3,
MONO8 => 1,
}
}
}
impl From<_POAImgFormat> for ImageFormat {
fn from(value: _POAImgFormat) -> Self {
use ImageFormat::*;
use _POAImgFormat::*;
match value {
POA_RAW8 => RAW8,
POA_RAW16 => RAW16,
POA_RGB24 => RGB24,
POA_MONO8 => MONO8,
POA_END => unreachable!("POA_END should have been parsed before"),
}
}
}
impl Into<_POAImgFormat> for ImageFormat {
fn into(self) -> _POAImgFormat {
use ImageFormat::*;
use _POAImgFormat::*;
match self {
RAW8 => POA_RAW8,
RAW16 => POA_RAW16,
RGB24 => POA_RGB24,
MONO8 => POA_MONO8,
}
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum BayerPattern {
MONO,
RG,
BG,
GR,
GB,
}
impl From<_POABayerPattern> for BayerPattern {
fn from(value: _POABayerPattern) -> Self {
use BayerPattern::*;
use _POABayerPattern::*;
match value {
POA_BAYER_RG => RG,
POA_BAYER_BG => BG,
POA_BAYER_GR => GR,
POA_BAYER_GB => GB,
POA_BAYER_MONO => MONO,
}
}
}
#[derive(Debug)]
pub struct ConfigBounds<T> {
pub min: T,
pub max: T,
pub default: T,
pub conf_name: String,
pub description: String,
}
trait FromAttribute: Sized {
fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self);
}
impl FromAttribute for i64 {
fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
if value.valueType != _POAValueType::VAL_INT {
let name = unsafe {
CStr::from_ptr(value.szConfName.as_ptr())
.to_string_lossy()
.to_string()
};
panic!("valueType is not VAL_INT for {}", name);
}
unsafe {
(
value.minValue.intValue as i64,
value.maxValue.intValue as i64,
value.defaultValue.intValue as i64,
)
}
}
}
impl FromAttribute for f64 {
fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
if value.valueType != _POAValueType::VAL_FLOAT {
let name = unsafe {
CStr::from_ptr(value.szConfName.as_ptr())
.to_string_lossy()
.to_string()
};
panic!("valueType is not VAL_FLOAT for {}", name);
}
unsafe {
(
value.minValue.floatValue,
value.maxValue.floatValue,
value.defaultValue.floatValue,
)
}
}
}
impl FromAttribute for bool {
fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
if value.valueType != _POAValueType::VAL_BOOL {
let name = unsafe {
CStr::from_ptr(value.szConfName.as_ptr())
.to_string_lossy()
.to_string()
};
panic!("valueType is not VAL_BOOL for {}", name);
}
unsafe {
(
value.minValue.boolValue == POABool::POA_TRUE,
value.maxValue.boolValue == POABool::POA_TRUE,
value.defaultValue.boolValue == POABool::POA_TRUE,
)
}
}
}
impl<T: FromAttribute> From<POAConfigAttributes> for ConfigBounds<T> {
fn from(value: POAConfigAttributes) -> Self {
let (min, max, default) = T::from_attribute(value);
Self {
min,
max,
default,
conf_name: unsafe {
CStr::from_ptr(value.szConfName.as_ptr())
.to_string_lossy()
.to_string()
},
description: unsafe {
CStr::from_ptr(value.szDescription.as_ptr())
.to_string_lossy()
.to_string()
},
}
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ConfigKind {
Exposure,
Gain,
HardwareBin,
Temperature,
WbR,
WbG,
WbB,
Offset,
AutoexpoMaxGain,
AutoexpoMaxExposure,
AutoexpoBrightness,
GuideNorth,
GuideSouth,
GuideEast,
GuideWest,
Egain,
CoolerPower,
TargetTemp,
Cooler,
Heater,
HeaterPower,
FanPower,
FlipNone,
FlipHori,
FlipVert,
FlipBoth,
FrameLimit,
Hqi,
UsbBandwidthLimit,
PixelBinSum,
MonoBin,
}
#[derive(Debug)]
pub struct AllConfigBounds {
pub exposure: ConfigBounds<i64>,
pub gain: ConfigBounds<i64>,
pub wb_r: Option<ConfigBounds<i64>>,
pub wb_g: Option<ConfigBounds<i64>>,
pub wb_b: Option<ConfigBounds<i64>>,
pub offset: ConfigBounds<i64>,
pub auto_max_gain: ConfigBounds<i64>,
pub auto_max_exposure: ConfigBounds<i64>,
pub auto_target_brightness: ConfigBounds<i64>,
pub frame_limit: ConfigBounds<i64>,
pub usb_bandwidth_limit: ConfigBounds<i64>,
pub cooler_power: Option<ConfigBounds<i64>>,
pub target_temperature: Option<ConfigBounds<i64>>,
pub heater_power: Option<ConfigBounds<i64>>,
pub fan_power: Option<ConfigBounds<i64>>,
}
impl From<Vec<POAConfigAttributes>> for AllConfigBounds {
fn from(values: Vec<POAConfigAttributes>) -> Self {
let mut exposure: Option<ConfigBounds<i64>> = None;
let mut gain: Option<ConfigBounds<i64>> = None;
let mut wb_r: Option<ConfigBounds<i64>> = None;
let mut wb_g: Option<ConfigBounds<i64>> = None;
let mut wb_b: Option<ConfigBounds<i64>> = None;
let mut offset: Option<ConfigBounds<i64>> = None;
let mut autoexpo_max_gain: Option<ConfigBounds<i64>> = None;
let mut autoexpo_max_exposure: Option<ConfigBounds<i64>> = None;
let mut autoexpo_brightness: Option<ConfigBounds<i64>> = None;
let mut cooler_power: Option<ConfigBounds<i64>> = None;
let mut target_temp: Option<ConfigBounds<i64>> = None;
let mut heater_power: Option<ConfigBounds<i64>> = None;
let mut fan_power: Option<ConfigBounds<i64>> = None;
let mut frame_limit: Option<ConfigBounds<i64>> = None;
let mut usb_bandwidth_limit: Option<ConfigBounds<i64>> = None;
for value in values {
let kind = value.configID.into();
match kind {
ConfigKind::Exposure => {
exposure = Some(ConfigBounds::from(value));
}
ConfigKind::Gain => {
gain = Some(ConfigBounds::from(value));
}
ConfigKind::WbR => {
wb_r = Some(ConfigBounds::from(value));
}
ConfigKind::WbG => {
wb_g = Some(ConfigBounds::from(value));
}
ConfigKind::WbB => {
wb_b = Some(ConfigBounds::from(value));
}
ConfigKind::Offset => {
offset = Some(ConfigBounds::from(value));
}
ConfigKind::AutoexpoMaxGain => {
autoexpo_max_gain = Some(ConfigBounds::from(value));
}
ConfigKind::AutoexpoMaxExposure => {
autoexpo_max_exposure = Some(ConfigBounds::from(value));
}
ConfigKind::AutoexpoBrightness => {
autoexpo_brightness = Some(ConfigBounds::from(value));
}
ConfigKind::CoolerPower => {
cooler_power = Some(ConfigBounds::from(value));
}
ConfigKind::TargetTemp => {
target_temp = Some(ConfigBounds::from(value));
}
ConfigKind::HeaterPower => {
heater_power = Some(ConfigBounds::from(value));
}
ConfigKind::FanPower => {
fan_power = Some(ConfigBounds::from(value));
}
ConfigKind::FrameLimit => {
frame_limit = Some(ConfigBounds::from(value));
}
ConfigKind::UsbBandwidthLimit => {
usb_bandwidth_limit = Some(ConfigBounds::from(value));
}
_ => {}
}
}
Self {
exposure: exposure.expect("exposure is not found"),
gain: gain.expect("gain is not found"),
wb_r,
wb_g,
wb_b,
offset: offset.expect("offset is not found"),
auto_max_gain: autoexpo_max_gain.expect("autoexpo_max_gain is not found"),
auto_max_exposure: autoexpo_max_exposure.expect("autoexpo_max_exposure is not found"),
auto_target_brightness: autoexpo_brightness.expect("autoexpo_brightness is not found"),
cooler_power,
target_temperature: target_temp,
heater_power,
fan_power,
frame_limit: frame_limit.expect("frame_limit is not found"),
usb_bandwidth_limit: usb_bandwidth_limit.expect("usb_bandwidth_limit is not found"),
}
}
}
impl From<POAConfig> for ConfigKind {
fn from(value: POAConfig) -> Self {
use ConfigKind::*;
use _POAConfig::*;
match value {
POA_EXPOSURE => Exposure,
POA_GAIN => Gain,
POA_HARDWARE_BIN => HardwareBin,
POA_TEMPERATURE => Temperature,
POA_WB_R => WbR,
POA_WB_G => WbG,
POA_WB_B => WbB,
POA_OFFSET => Offset,
POA_AUTOEXPO_MAX_GAIN => AutoexpoMaxGain,
POA_AUTOEXPO_MAX_EXPOSURE => AutoexpoMaxExposure,
POA_AUTOEXPO_BRIGHTNESS => AutoexpoBrightness,
POA_GUIDE_NORTH => GuideNorth,
POA_GUIDE_SOUTH => GuideSouth,
POA_GUIDE_EAST => GuideEast,
POA_GUIDE_WEST => GuideWest,
POA_EGAIN => Egain,
POA_COOLER_POWER => CoolerPower,
POA_TARGET_TEMP => TargetTemp,
POA_COOLER => Cooler,
POA_HEATER => Heater,
POA_HEATER_POWER => HeaterPower,
POA_FAN_POWER => FanPower,
POA_FLIP_NONE => FlipNone,
POA_FLIP_HORI => FlipHori,
POA_FLIP_VERT => FlipVert,
POA_FLIP_BOTH => FlipBoth,
POA_FRAME_LIMIT => FrameLimit,
POA_HQI => Hqi,
POA_USB_BANDWIDTH_LIMIT => UsbBandwidthLimit,
POA_PIXEL_BIN_SUM => PixelBinSum,
POA_MONO_BIN => MonoBin,
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum Error {
InvalidIndex,
InvalidCameraId,
InvalidConfig,
InvalidArgument,
NotOpened,
DeviceNotFound,
OutOfBounds,
ExposureFailed,
Timeout,
BufferSizeTooSmall,
Exposing,
NullPointer,
ConfigNotWritable,
ConfigNotReadable,
AccessDenied,
OperationFailed,
MemoryAllocationFailed,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use Error::*;
write!(
f,
"{}",
match self {
InvalidIndex => "invalid index",
InvalidCameraId => "invalid camera id",
InvalidConfig => "invalid config",
InvalidArgument => "invalid argument",
NotOpened => "camera is not opened",
DeviceNotFound => "device not found",
OutOfBounds => "out of bounds",
ExposureFailed => "exposure failed",
Timeout => "timeout",
BufferSizeTooSmall => "buffer size too small",
Exposing => "camera is exposing",
NullPointer => "null pointer",
ConfigNotWritable => "config is not writable",
ConfigNotReadable => "config is not readable",
AccessDenied => "access denied",
OperationFailed => "operation failed",
MemoryAllocationFailed => "memory allocation failed",
}
)
}
}
impl std::error::Error for Error {}
impl From<POAErrors> for Error {
fn from(value: POAErrors) -> Self {
use Error::*;
use POAErrors::*;
match value {
POA_OK => unreachable!("POA_OK should have been checked before"),
POA_ERROR_INVALID_INDEX => InvalidIndex,
POA_ERROR_INVALID_ID => InvalidCameraId,
POA_ERROR_INVALID_CONFIG => InvalidConfig,
POA_ERROR_INVALID_ARGU => InvalidArgument,
POA_ERROR_NOT_OPENED => NotOpened,
POA_ERROR_DEVICE_NOT_FOUND => DeviceNotFound,
POA_ERROR_OUT_OF_LIMIT => OutOfBounds,
POA_ERROR_EXPOSURE_FAILED => ExposureFailed,
POA_ERROR_TIMEOUT => Timeout,
POA_ERROR_SIZE_LESS => BufferSizeTooSmall,
POA_ERROR_EXPOSING => Exposing,
POA_ERROR_POINTER => NullPointer,
POA_ERROR_CONF_CANNOT_WRITE => ConfigNotWritable,
POA_ERROR_CONF_CANNOT_READ => ConfigNotReadable,
POA_ERROR_ACCESS_DENIED => AccessDenied,
POA_ERROR_OPERATION_FAILED => OperationFailed,
POA_ERROR_MEMORY_FAILED => MemoryAllocationFailed,
}
}
}