1use std::ffi::CStr;
2use std::fmt::{Display, Formatter};
3
4use playerone_sdk_sys::{
5 _POABayerPattern, _POACameraProperties, _POAConfig, _POAImgFormat, _POAValueType, POABool,
6 POAConfig, POAConfigAttributes, POAErrors, POAImgFormat,
7};
8
9#[derive(Debug, Clone)]
10pub struct CameraProperties {
11 pub camera_model_name: String,
13 pub user_custom_id: String,
15 pub camera_id: u32,
16 pub max_width: u32,
18 pub max_height: u32,
20 pub bit_depth: u32,
22 pub is_color_camera: bool,
24 pub is_has_st_4_port: bool,
26 pub is_has_cooler: bool,
28 pub is_usb_3_speed: bool,
30 pub bayer_pattern: BayerPattern,
32 pub pixel_size: f64,
34 pub serial_number: String,
36 pub sensor_model_name: String,
38 pub local_path: String,
40 pub bins: Vec<u32>,
42 pub img_formats: Vec<POAImgFormat>,
44 pub is_support_hard_bin: bool,
46 pub product_id: i32,
48}
49
50impl From<_POACameraProperties> for CameraProperties {
51 fn from(value: _POACameraProperties) -> Self {
52 let camera_model_name = unsafe {
53 CStr::from_ptr(value.cameraModelName.as_ptr())
54 .to_string_lossy()
55 .to_string()
56 };
57 let user_custom_id = unsafe {
58 CStr::from_ptr(value.userCustomID.as_ptr())
59 .to_string_lossy()
60 .to_string()
61 };
62 let sn = unsafe {
63 CStr::from_ptr(value.SN.as_ptr())
64 .to_string_lossy()
65 .to_string()
66 };
67 let sensor_model_name = unsafe {
68 CStr::from_ptr(value.sensorModelName.as_ptr())
69 .to_string_lossy()
70 .to_string()
71 };
72 let local_path = unsafe {
73 CStr::from_ptr(value.localPath.as_ptr())
74 .to_string_lossy()
75 .to_string()
76 };
77
78 let mut bins = Vec::with_capacity(value.bins.len());
79 for bin in value.bins {
80 if bin <= 0 {
81 break;
82 }
83 bins.push(bin as u32);
84 }
85
86 let mut img_formats = Vec::with_capacity(value.imgFormats.len());
87 for img_format in value.imgFormats {
88 if img_format == _POAImgFormat::POA_END {
89 break;
90 }
91 img_formats.push(img_format.into());
92 }
93
94 Self {
95 camera_model_name,
96 user_custom_id,
97 camera_id: value.cameraID as u32,
98 max_width: value.maxWidth as u32,
99 max_height: value.maxHeight as u32,
100 bit_depth: value.bitDepth as u32,
101 is_color_camera: value.isColorCamera == POABool::POA_TRUE,
102 is_has_st_4_port: value.isHasST4Port == POABool::POA_TRUE,
103 is_has_cooler: value.isHasCooler == POABool::POA_TRUE,
104 is_usb_3_speed: value.isUSB3Speed == POABool::POA_TRUE,
105 bayer_pattern: value.bayerPattern.into(),
106 pixel_size: value.pixelSize,
107 serial_number: sn,
108 sensor_model_name,
109 local_path,
110 bins,
111 img_formats,
112 is_support_hard_bin: value.isSupportHardBin == POABool::POA_TRUE,
113 product_id: value.pID.try_into().expect("c_int is not i32"),
114 }
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct SensorMode {
124 pub index: u32,
126 pub name: String,
128 pub description: String,
130}
131
132impl std::fmt::Display for SensorMode {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "{}", self.name)
135 }
136}
137
138#[repr(i32)]
139#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
140pub enum ImageFormat {
141 RAW8,
143 RAW16,
145 RGB24,
147 MONO8,
149}
150
151impl ImageFormat {
152 pub fn bytes_per_pixel(&self) -> usize {
153 use ImageFormat::*;
154 match self {
155 RAW8 => 1,
156 RAW16 => 2,
157 RGB24 => 3,
158 MONO8 => 1,
159 }
160 }
161}
162
163impl From<_POAImgFormat> for ImageFormat {
164 fn from(value: _POAImgFormat) -> Self {
165 use ImageFormat::*;
166 use _POAImgFormat::*;
167 match value {
168 POA_RAW8 => RAW8,
169 POA_RAW16 => RAW16,
170 POA_RGB24 => RGB24,
171 POA_MONO8 => MONO8,
172 POA_END => unreachable!("POA_END should have been parsed before"),
173 }
174 }
175}
176
177impl Into<_POAImgFormat> for ImageFormat {
178 fn into(self) -> _POAImgFormat {
179 use ImageFormat::*;
180 use _POAImgFormat::*;
181 match self {
182 RAW8 => POA_RAW8,
183 RAW16 => POA_RAW16,
184 RGB24 => POA_RGB24,
185 MONO8 => POA_MONO8,
186 }
187 }
188}
189
190#[repr(i32)]
191#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
192pub enum BayerPattern {
193 MONO,
195 RG,
197 BG,
199 GR,
201 GB,
203}
204
205impl From<_POABayerPattern> for BayerPattern {
206 fn from(value: _POABayerPattern) -> Self {
207 use BayerPattern::*;
208 use _POABayerPattern::*;
209 match value {
210 POA_BAYER_RG => RG,
211 POA_BAYER_BG => BG,
212 POA_BAYER_GR => GR,
213 POA_BAYER_GB => GB,
214 POA_BAYER_MONO => MONO,
215 }
216 }
217}
218
219#[derive(Debug)]
220pub struct ConfigBounds<T> {
221 pub min: T,
222 pub max: T,
223 pub default: T,
224 pub conf_name: String,
225 pub description: String,
226}
227
228trait FromAttribute: Sized {
229 fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self);
230}
231
232impl FromAttribute for i64 {
233 fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
234 if value.valueType != _POAValueType::VAL_INT {
235 let name = unsafe {
236 CStr::from_ptr(value.szConfName.as_ptr())
237 .to_string_lossy()
238 .to_string()
239 };
240 panic!("valueType is not VAL_INT for {}", name);
241 }
242 unsafe {
243 (
244 value.minValue.intValue as i64,
245 value.maxValue.intValue as i64,
246 value.defaultValue.intValue as i64,
247 )
248 }
249 }
250}
251
252impl FromAttribute for f64 {
253 fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
254 if value.valueType != _POAValueType::VAL_FLOAT {
255 let name = unsafe {
256 CStr::from_ptr(value.szConfName.as_ptr())
257 .to_string_lossy()
258 .to_string()
259 };
260 panic!("valueType is not VAL_FLOAT for {}", name);
261 }
262 unsafe {
263 (
264 value.minValue.floatValue,
265 value.maxValue.floatValue,
266 value.defaultValue.floatValue,
267 )
268 }
269 }
270}
271
272impl FromAttribute for bool {
273 fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
274 if value.valueType != _POAValueType::VAL_BOOL {
275 let name = unsafe {
276 CStr::from_ptr(value.szConfName.as_ptr())
277 .to_string_lossy()
278 .to_string()
279 };
280 panic!("valueType is not VAL_BOOL for {}", name);
281 }
282 unsafe {
283 (
284 value.minValue.boolValue == POABool::POA_TRUE,
285 value.maxValue.boolValue == POABool::POA_TRUE,
286 value.defaultValue.boolValue == POABool::POA_TRUE,
287 )
288 }
289 }
290}
291
292impl<T: FromAttribute> From<POAConfigAttributes> for ConfigBounds<T> {
293 fn from(value: POAConfigAttributes) -> Self {
294 let (min, max, default) = T::from_attribute(value);
295
296 Self {
297 min,
298 max,
299 default,
300 conf_name: unsafe {
301 CStr::from_ptr(value.szConfName.as_ptr())
302 .to_string_lossy()
303 .to_string()
304 },
305 description: unsafe {
306 CStr::from_ptr(value.szDescription.as_ptr())
307 .to_string_lossy()
308 .to_string()
309 },
310 }
311 }
312}
313
314#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
315pub enum ConfigKind {
316 Exposure,
318 Gain,
320 HardwareBin,
322 Temperature,
324 WbR,
326 WbG,
328 WbB,
330 Offset,
332 AutoexpoMaxGain,
334 AutoexpoMaxExposure,
336 AutoexpoBrightness,
338 GuideNorth,
340 GuideSouth,
342 GuideEast,
344 GuideWest,
346 Egain,
348 CoolerPower,
350 TargetTemp,
352 Cooler,
354 Heater,
356 HeaterPower,
358 FanPower,
360 FlipNone,
362 FlipHori,
364 FlipVert,
366 FlipBoth,
368 FrameLimit,
370 Hqi,
372 UsbBandwidthLimit,
374 PixelBinSum,
376 MonoBin,
378}
379
380#[derive(Debug)]
381pub struct AllConfigBounds {
382 pub exposure: ConfigBounds<i64>,
384 pub gain: ConfigBounds<i64>,
385 pub wb_r: Option<ConfigBounds<i64>>,
387 pub wb_g: Option<ConfigBounds<i64>>,
389 pub wb_b: Option<ConfigBounds<i64>>,
391 pub offset: ConfigBounds<i64>,
393 pub auto_max_gain: ConfigBounds<i64>,
395 pub auto_max_exposure: ConfigBounds<i64>,
397 pub auto_target_brightness: ConfigBounds<i64>,
399 pub frame_limit: ConfigBounds<i64>,
401 pub usb_bandwidth_limit: ConfigBounds<i64>,
403 pub cooler_power: Option<ConfigBounds<i64>>,
405 pub target_temperature: Option<ConfigBounds<i64>>,
407 pub heater_power: Option<ConfigBounds<i64>>,
409 pub fan_power: Option<ConfigBounds<i64>>,
411}
412
413impl From<Vec<POAConfigAttributes>> for AllConfigBounds {
414 fn from(values: Vec<POAConfigAttributes>) -> Self {
415 let mut exposure: Option<ConfigBounds<i64>> = None;
416 let mut gain: Option<ConfigBounds<i64>> = None;
417 let mut wb_r: Option<ConfigBounds<i64>> = None;
418 let mut wb_g: Option<ConfigBounds<i64>> = None;
419 let mut wb_b: Option<ConfigBounds<i64>> = None;
420 let mut offset: Option<ConfigBounds<i64>> = None;
421 let mut autoexpo_max_gain: Option<ConfigBounds<i64>> = None;
422 let mut autoexpo_max_exposure: Option<ConfigBounds<i64>> = None;
423 let mut autoexpo_brightness: Option<ConfigBounds<i64>> = None;
424 let mut cooler_power: Option<ConfigBounds<i64>> = None;
425 let mut target_temp: Option<ConfigBounds<i64>> = None;
426 let mut heater_power: Option<ConfigBounds<i64>> = None;
427 let mut fan_power: Option<ConfigBounds<i64>> = None;
428 let mut frame_limit: Option<ConfigBounds<i64>> = None;
429 let mut usb_bandwidth_limit: Option<ConfigBounds<i64>> = None;
430
431 for value in values {
432 let kind = value.configID.into();
433 match kind {
434 ConfigKind::Exposure => {
435 exposure = Some(ConfigBounds::from(value));
436 }
437 ConfigKind::Gain => {
438 gain = Some(ConfigBounds::from(value));
439 }
440 ConfigKind::WbR => {
441 wb_r = Some(ConfigBounds::from(value));
442 }
443 ConfigKind::WbG => {
444 wb_g = Some(ConfigBounds::from(value));
445 }
446 ConfigKind::WbB => {
447 wb_b = Some(ConfigBounds::from(value));
448 }
449 ConfigKind::Offset => {
450 offset = Some(ConfigBounds::from(value));
451 }
452 ConfigKind::AutoexpoMaxGain => {
453 autoexpo_max_gain = Some(ConfigBounds::from(value));
454 }
455 ConfigKind::AutoexpoMaxExposure => {
456 autoexpo_max_exposure = Some(ConfigBounds::from(value));
457 }
458 ConfigKind::AutoexpoBrightness => {
459 autoexpo_brightness = Some(ConfigBounds::from(value));
460 }
461 ConfigKind::CoolerPower => {
462 cooler_power = Some(ConfigBounds::from(value));
463 }
464 ConfigKind::TargetTemp => {
465 target_temp = Some(ConfigBounds::from(value));
466 }
467 ConfigKind::HeaterPower => {
468 heater_power = Some(ConfigBounds::from(value));
469 }
470 ConfigKind::FanPower => {
471 fan_power = Some(ConfigBounds::from(value));
472 }
473 ConfigKind::FrameLimit => {
474 frame_limit = Some(ConfigBounds::from(value));
475 }
476 ConfigKind::UsbBandwidthLimit => {
477 usb_bandwidth_limit = Some(ConfigBounds::from(value));
478 }
479 _ => {}
480 }
481 }
482
483 Self {
484 exposure: exposure.expect("exposure is not found"),
485 gain: gain.expect("gain is not found"),
486 wb_r,
487 wb_g,
488 wb_b,
489 offset: offset.expect("offset is not found"),
490 auto_max_gain: autoexpo_max_gain.expect("autoexpo_max_gain is not found"),
491 auto_max_exposure: autoexpo_max_exposure.expect("autoexpo_max_exposure is not found"),
492 auto_target_brightness: autoexpo_brightness.expect("autoexpo_brightness is not found"),
493 cooler_power,
494 target_temperature: target_temp,
495 heater_power,
496 fan_power,
497 frame_limit: frame_limit.expect("frame_limit is not found"),
498 usb_bandwidth_limit: usb_bandwidth_limit.expect("usb_bandwidth_limit is not found"),
499 }
500 }
501}
502
503impl From<POAConfig> for ConfigKind {
504 fn from(value: POAConfig) -> Self {
505 use ConfigKind::*;
506 use _POAConfig::*;
507 match value {
508 POA_EXPOSURE => Exposure,
509 POA_GAIN => Gain,
510 POA_HARDWARE_BIN => HardwareBin,
511 POA_TEMPERATURE => Temperature,
512 POA_WB_R => WbR,
513 POA_WB_G => WbG,
514 POA_WB_B => WbB,
515 POA_OFFSET => Offset,
516 POA_AUTOEXPO_MAX_GAIN => AutoexpoMaxGain,
517 POA_AUTOEXPO_MAX_EXPOSURE => AutoexpoMaxExposure,
518 POA_AUTOEXPO_BRIGHTNESS => AutoexpoBrightness,
519 POA_GUIDE_NORTH => GuideNorth,
520 POA_GUIDE_SOUTH => GuideSouth,
521 POA_GUIDE_EAST => GuideEast,
522 POA_GUIDE_WEST => GuideWest,
523 POA_EGAIN => Egain,
524 POA_COOLER_POWER => CoolerPower,
525 POA_TARGET_TEMP => TargetTemp,
526 POA_COOLER => Cooler,
527 POA_HEATER => Heater,
528 POA_HEATER_POWER => HeaterPower,
529 POA_FAN_POWER => FanPower,
530 POA_FLIP_NONE => FlipNone,
531 POA_FLIP_HORI => FlipHori,
532 POA_FLIP_VERT => FlipVert,
533 POA_FLIP_BOTH => FlipBoth,
534 POA_FRAME_LIMIT => FrameLimit,
535 POA_HQI => Hqi,
536 POA_USB_BANDWIDTH_LIMIT => UsbBandwidthLimit,
537 POA_PIXEL_BIN_SUM => PixelBinSum,
538 POA_MONO_BIN => MonoBin,
539 }
540 }
541}
542
543#[repr(u32)]
544#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
545pub enum Error {
546 InvalidIndex,
548 InvalidCameraId,
549 InvalidConfig,
550 InvalidArgument,
551 NotOpened,
552 DeviceNotFound,
553 OutOfBounds,
554 ExposureFailed,
555 Timeout,
556 BufferSizeTooSmall,
557 Exposing,
559 NullPointer,
560 ConfigNotWritable,
561 ConfigNotReadable,
562 AccessDenied,
563 OperationFailed,
565 MemoryAllocationFailed,
566}
567
568impl Display for Error {
569 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
570 use Error::*;
571 write!(
572 f,
573 "{}",
574 match self {
575 InvalidIndex => "invalid index",
576 InvalidCameraId => "invalid camera id",
577 InvalidConfig => "invalid config",
578 InvalidArgument => "invalid argument",
579 NotOpened => "camera is not opened",
580 DeviceNotFound => "device not found",
581 OutOfBounds => "out of bounds",
582 ExposureFailed => "exposure failed",
583 Timeout => "timeout",
584 BufferSizeTooSmall => "buffer size too small",
585 Exposing => "camera is exposing",
586 NullPointer => "null pointer",
587 ConfigNotWritable => "config is not writable",
588 ConfigNotReadable => "config is not readable",
589 AccessDenied => "access denied",
590 OperationFailed => "operation failed",
591 MemoryAllocationFailed => "memory allocation failed",
592 }
593 )
594 }
595}
596
597impl std::error::Error for Error {}
598
599impl From<POAErrors> for Error {
600 fn from(value: POAErrors) -> Self {
601 use Error::*;
602 use POAErrors::*;
603 match value {
604 POA_OK => unreachable!("POA_OK should have been checked before"),
605 POA_ERROR_INVALID_INDEX => InvalidIndex,
606 POA_ERROR_INVALID_ID => InvalidCameraId,
607 POA_ERROR_INVALID_CONFIG => InvalidConfig,
608 POA_ERROR_INVALID_ARGU => InvalidArgument,
609 POA_ERROR_NOT_OPENED => NotOpened,
610 POA_ERROR_DEVICE_NOT_FOUND => DeviceNotFound,
611 POA_ERROR_OUT_OF_LIMIT => OutOfBounds,
612 POA_ERROR_EXPOSURE_FAILED => ExposureFailed,
613 POA_ERROR_TIMEOUT => Timeout,
614 POA_ERROR_SIZE_LESS => BufferSizeTooSmall,
615 POA_ERROR_EXPOSING => Exposing,
616 POA_ERROR_POINTER => NullPointer,
617 POA_ERROR_CONF_CANNOT_WRITE => ConfigNotWritable,
618 POA_ERROR_CONF_CANNOT_READ => ConfigNotReadable,
619 POA_ERROR_ACCESS_DENIED => AccessDenied,
620 POA_ERROR_OPERATION_FAILED => OperationFailed,
621 POA_ERROR_MEMORY_FAILED => MemoryAllocationFailed,
622 }
623 }
624}