1use std::ffi::{c_char, c_int, c_long};
2
3use playerone_sdk_sys::POABool::{POA_FALSE, POA_TRUE};
4use playerone_sdk_sys::POAConfig::{POA_EXPOSURE, POA_GAIN};
5use playerone_sdk_sys::{
6 FromPOAConfigValue, POACameraProperties, POACloseCamera, POAConfigAttributes, POAConfigValue,
7 POAErrors, POAGetCameraCount, POAGetCameraProperties, POAGetConfig, POAGetConfigAttributes,
8 POAGetConfigsCount, POAGetImageBin, POAGetImageData, POAGetImageFormat, POAGetImageSize,
9 POAGetImageStartPos, POAGetSensorMode, POAGetSensorModeCount, POAGetSensorModeInfo,
10 POAImageReady, POAInitCamera, POAOpenCamera, POASensorModeInfo, POASetConfig, POASetEnableDPS,
11 POASetImageBin, POASetImageFormat, POASetImageSize, POASetImageStartPos, POASetSensorMode,
12 POAStartExposure, POAStopExposure, _POABool as POABool, _POAConfig as POAConfig, _POAErrors,
13 _POAImgFormat as POAImgFormat,
14};
15
16use crate::{AllConfigBounds, CameraProperties, Error, ImageFormat, SensorMode};
17
18type POAResult<T> = Result<T, Error>;
19
20#[derive(Debug, Copy, Clone)]
22pub struct ROI {
23 pub start_x: u32,
24 pub start_y: u32,
25 pub width: u32,
26 pub height: u32,
27}
28
29pub struct CameraDescription {
32 camera_id: i32,
33 properties: CameraProperties,
34}
35
36impl CameraDescription {
37 pub fn camera_id(&self) -> i32 {
38 self.camera_id
39 }
40
41 pub fn properties(&self) -> &CameraProperties {
42 &self.properties
43 }
44
45 pub fn open(self) -> POAResult<Camera> {
46 let mut camera = Camera {
47 camera_id: self.camera_id,
48 closed: false,
49 properties: self.properties,
50 };
51 camera.open()?;
52 Ok(camera)
53 }
54}
55
56#[derive(Debug)]
57pub struct Camera {
58 camera_id: i32,
59 closed: bool,
60 properties: CameraProperties,
61}
62
63impl Drop for Camera {
64 fn drop(&mut self) {
65 if !self.closed {
66 let _ = unsafe { POACloseCamera(self.camera_id) };
68 }
69 }
70}
71
72impl Camera {
73 pub fn all_cameras() -> Vec<CameraDescription> {
76 let camera_count = unsafe { POAGetCameraCount() };
77 let mut cameras = Vec::with_capacity(camera_count as usize);
78
79 for i in 0..camera_count {
80 let mut camera_prop: POACameraProperties = POACameraProperties::default();
81 let error = unsafe { POAGetCameraProperties(i, &raw mut camera_prop) };
82
83 if error != _POAErrors::POA_OK {
84 continue;
85 }
86 cameras.push(CameraDescription {
87 camera_id: camera_prop.cameraID,
88 properties: camera_prop.into(),
89 });
90 }
91
92 cameras
93 }
94
95 fn open(&mut self) -> POAResult<()> {
96 let error = unsafe { POAOpenCamera(self.camera_id) };
97 if error != _POAErrors::POA_OK {
98 return Err(error.into());
99 }
100
101 let error = unsafe { POAInitCamera(self.camera_id) };
102 if error != _POAErrors::POA_OK {
103 unsafe { POACloseCamera(self.camera_id) };
104
105 return Err(error.into());
106 }
107
108 Ok(())
109 }
110
111 pub fn capture(&mut self, buffer: &mut [u8], timeout: Option<i32>) -> POAResult<()> {
117 let error = unsafe { POAStartExposure(self.camera_id, POA_TRUE) };
118 if error != _POAErrors::POA_OK {
119 return Err(error.into());
120 }
121 self.get_image_data(buffer, timeout)?;
122 self.stop_exposure()?;
123 Ok(())
124 }
125
126 pub fn stream(
129 &mut self,
130 timeout: Option<u32>,
131 mut callback: impl FnMut(&mut Camera, &[u8]) -> bool,
132 ) -> POAResult<()> {
133 if let Some(timeout) = timeout {
134 if timeout > i32::MAX as u32 {
135 return Err(Error::OutOfBounds);
136 }
137 }
138
139 let mut buffer = self.create_image_buffer();
140
141 self.start_exposure()?;
142 loop {
143 match self.get_image_data(&mut buffer, timeout.map(|t| t as i32)) {
144 Ok(_) => (),
145 Err(e) => {
146 let _ = self.stop_exposure();
147 return Err(e);
148 }
149 }
150 if !callback(self, &buffer) {
151 break;
152 }
153 }
154
155 self.stop_exposure()?;
156 Ok(())
157 }
158
159 pub fn create_image_buffer(&self) -> Vec<u8> {
161 let (w, h) = self.image_size();
162 let format = self.image_format().unwrap();
163 vec![0; w as usize * h as usize * format.bytes_per_pixel()]
164 }
165
166 pub fn start_exposure(&mut self) -> POAResult<()> {
169 let error = unsafe { POAStartExposure(self.camera_id, POA_FALSE) };
170 if error != _POAErrors::POA_OK {
171 return Err(error.into());
172 }
173 Ok(())
174 }
175
176 pub fn is_image_ready(&self) -> POAResult<bool> {
178 let mut is_img_data_available = POA_FALSE;
179 let error = unsafe { POAImageReady(self.camera_id, &raw mut is_img_data_available) };
180 if error != _POAErrors::POA_OK {
181 return Err(error.into());
182 }
183 Ok(is_img_data_available.into())
184 }
185
186 pub fn get_image_data(&self, buffer: &mut [u8], timeout_ms: Option<i32>) -> POAResult<()> {
191 let error = unsafe {
192 POAGetImageData(
193 self.camera_id,
194 buffer.as_mut_ptr(),
195 buffer.len() as c_long,
196 timeout_ms.unwrap_or(-1),
197 )
198 };
199 if error != _POAErrors::POA_OK {
200 return Err(error.into());
201 }
202 Ok(())
203 }
204
205 pub fn stop_exposure(&mut self) -> POAResult<()> {
207 let error = unsafe { POAStopExposure(self.camera_id) };
208 if error != _POAErrors::POA_OK {
209 return Err(error.into());
210 }
211 Ok(())
212 }
213
214 pub fn close(mut self) -> POAResult<()> {
217 self.closed = true;
218
219 let error = unsafe { POACloseCamera(self.camera_id) };
220 if error != _POAErrors::POA_OK {
221 return Err(error.into());
222 }
223 Ok(())
224 }
225
226 pub fn config_bounds(&self) -> AllConfigBounds {
229 let mut config_count = 0;
230 safe_error(unsafe { POAGetConfigsCount(self.camera_id, &raw mut config_count) });
231
232 let mut attributes = Vec::with_capacity(40);
233
234 for i in 0..config_count {
235 let mut conf_attributes = POAConfigAttributes::default();
236
237 safe_error(unsafe {
238 POAGetConfigAttributes(self.camera_id, i, &raw mut conf_attributes)
239 });
240
241 attributes.push(conf_attributes);
242 }
243
244 AllConfigBounds::from(attributes)
245 }
246
247 pub fn set_dps(&mut self, dps: bool) -> POAResult<()> {
248 let b: POABool = dps.into();
249 let error = unsafe { POASetEnableDPS(self.camera_id, &raw const b) };
250 if error != _POAErrors::POA_OK {
251 return Err(error.into());
252 }
253 Ok(())
254 }
255
256 pub fn set_roi(&mut self, roi_area: &ROI) -> POAResult<()> {
258 self.set_image_size(roi_area.width, roi_area.height)?;
259 self.set_image_start_pos(roi_area.start_x, roi_area.start_y)?;
260 Ok(())
261 }
262
263 pub fn roi(&self) -> ROI {
265 let start_pos = self.image_start_pos().unwrap();
266 let size = self.image_size();
267
268 ROI {
269 start_x: start_pos.0,
270 start_y: start_pos.1,
271 width: size.0,
272 height: size.1,
273 }
274 }
275
276 pub fn set_image_size(&mut self, width: u32, height: u32) -> POAResult<()> {
278 if width > self.properties.max_width || height > self.properties.max_height {
279 return Err(Error::OutOfBounds);
280 }
281
282 let error = unsafe { POASetImageSize(self.camera_id, width as c_int, height as c_int) };
283 if error != _POAErrors::POA_OK {
284 return Err(error.into());
285 }
286 Ok(())
287 }
288
289 pub fn image_size(&self) -> (u32, u32) {
292 let mut width = 0;
293 let mut height = 0;
294
295 safe_error(unsafe { POAGetImageSize(self.camera_id, &raw mut width, &raw mut height) });
296
297 if width < 0 || height < 0 {
298 panic!("negative image size: {} {}", width, height);
299 }
300
301 (width as u32, height as u32)
302 }
303
304 pub fn set_image_start_pos(&mut self, start_x: u32, start_y: u32) -> POAResult<()> {
306 if start_x > self.properties.max_width || start_y > self.properties.max_height {
307 return Err(Error::OutOfBounds);
308 }
309
310 let error =
311 unsafe { POASetImageStartPos(self.camera_id, start_x as c_int, start_y as c_int) };
312 if error != _POAErrors::POA_OK {
313 return Err(error.into());
314 }
315 Ok(())
316 }
317
318 pub fn image_start_pos(&self) -> POAResult<(u32, u32)> {
321 let mut start_x = 0;
322 let mut start_y = 0;
323
324 safe_error(unsafe {
325 POAGetImageStartPos(self.camera_id, &raw mut start_x, &raw mut start_y)
326 });
327
328 if start_x < 0 || start_y < 0 {
329 panic!("negative image start position: {} {}", start_x, start_y);
330 }
331
332 Ok((start_x as u32, start_y as u32))
333 }
334
335 pub fn set_image_format(&mut self, image_format: ImageFormat) -> POAResult<()> {
336 let poa_img_format = image_format.into();
337
338 let error = unsafe { POASetImageFormat(self.camera_id, poa_img_format) };
339 if error != _POAErrors::POA_OK {
340 return Err(error.into());
341 }
342 Ok(())
343 }
344
345 pub fn image_format(&self) -> POAResult<ImageFormat> {
346 let mut poa_img_format = POAImgFormat::POA_END;
347
348 safe_error(unsafe { POAGetImageFormat(self.camera_id, &raw mut poa_img_format) });
349 Ok(poa_img_format.into())
350 }
351
352 pub fn set_bin(&mut self, bin: u32) -> POAResult<()> {
359 if !self.properties.bins.contains(&bin) {
360 return Err(Error::OutOfBounds);
361 }
362
363 let err = unsafe { POASetImageBin(self.camera_id, bin as c_int) };
364 if err != _POAErrors::POA_OK {
365 return Err(err.into());
366 }
367 Ok(())
368 }
369
370 pub fn bin(&self) -> u32 {
372 let mut bin = 0;
373 safe_error(unsafe { POAGetImageBin(self.camera_id, &raw mut bin) });
374 bin as u32
375 }
376
377 pub fn sensor_modes(&self) -> POAResult<Vec<SensorMode>> {
382 enumerate_sensor_modes(self.camera_id)
383 }
384
385 pub fn sensor_mode(&self) -> POAResult<u32> {
390 let mut index: c_int = 0;
391 let err = unsafe { POAGetSensorMode(self.camera_id, &raw mut index) };
392 if err != _POAErrors::POA_OK {
393 return Err(err.into());
394 }
395 if index < 0 {
396 return Err(Error::OperationFailed);
397 }
398 Ok(index as u32)
399 }
400
401 pub fn set_sensor_mode(&mut self, index: u32) -> POAResult<()> {
406 let err = unsafe { POASetSensorMode(self.camera_id, index as c_int) };
407 if err != _POAErrors::POA_OK {
408 return Err(err.into());
409 }
410 Ok(())
411 }
412
413 pub fn properties(&self) -> &CameraProperties {
414 &self.properties
415 }
416
417 pub fn id(&self) -> i32 {
418 self.camera_id
419 }
420
421 pub fn set_exposure(&mut self, exposure_micros: i64, is_auto: bool) -> POAResult<()> {
423 self.set_config(POA_EXPOSURE, exposure_micros, is_auto)
424 }
425
426 pub fn set_gain(&mut self, gain: i64, is_auto: bool) -> POAResult<()> {
427 self.set_config(POA_GAIN, gain, is_auto)
428 }
429
430 pub fn exposure(&self) -> POAResult<(i64, bool)> {
432 unsafe { self.get_config_auto(POA_EXPOSURE) }
433 }
434
435 pub fn gain(&self) -> POAResult<(i64, bool)> {
437 unsafe { self.get_config_auto(POA_GAIN) }
438 }
439
440 pub fn hardware_bin(&self) -> POAResult<bool> {
441 unsafe { self.get_config(POAConfig::POA_HARDWARE_BIN) }
442 }
443
444 pub fn temperature(&self) -> POAResult<f64> {
446 unsafe { self.get_config(POAConfig::POA_TEMPERATURE) }
447 }
448
449 pub fn wb_r(&self) -> POAResult<i64> {
451 unsafe { self.get_config(POAConfig::POA_WB_R) }
452 }
453
454 pub fn wb_g(&self) -> POAResult<i64> {
456 unsafe { self.get_config(POAConfig::POA_WB_G) }
457 }
458
459 pub fn wb_b(&self) -> POAResult<i64> {
461 unsafe { self.get_config(POAConfig::POA_WB_B) }
462 }
463
464 pub fn offset(&self) -> POAResult<i64> {
465 unsafe { self.get_config(POAConfig::POA_OFFSET) }
466 }
467
468 pub fn auto_max_gain(&self) -> POAResult<i64> {
470 unsafe { self.get_config(POAConfig::POA_AUTOEXPO_MAX_GAIN) }
471 }
472
473 pub fn auto_max_exposure_ms(&self) -> POAResult<i64> {
475 unsafe { self.get_config(POAConfig::POA_AUTOEXPO_MAX_EXPOSURE) }
476 }
477
478 pub fn auto_target_brightness(&self) -> POAResult<i64> {
480 unsafe { self.get_config(POAConfig::POA_AUTOEXPO_BRIGHTNESS) }
481 }
482
483 pub fn guide_north(&self) -> POAResult<bool> {
485 unsafe { self.get_config(POAConfig::POA_GUIDE_NORTH) }
486 }
487
488 pub fn guide_south(&self) -> POAResult<bool> {
490 unsafe { self.get_config(POAConfig::POA_GUIDE_SOUTH) }
491 }
492
493 pub fn guide_east(&self) -> POAResult<bool> {
495 unsafe { self.get_config(POAConfig::POA_GUIDE_EAST) }
496 }
497
498 pub fn guide_west(&self) -> POAResult<bool> {
500 unsafe { self.get_config(POAConfig::POA_GUIDE_WEST) }
501 }
502
503 pub fn egain(&self) -> POAResult<f64> {
505 unsafe { self.get_config(POAConfig::POA_EGAIN) }
506 }
507
508 pub fn cooler_power(&self) -> POAResult<i64> {
510 unsafe { self.get_config(POAConfig::POA_COOLER_POWER) }
511 }
512
513 pub fn target_temp(&self) -> POAResult<i64> {
515 unsafe { self.get_config(POAConfig::POA_TARGET_TEMP) }
516 }
517
518 pub fn cooler(&self) -> POAResult<bool> {
520 unsafe { self.get_config(POAConfig::POA_COOLER) }
521 }
522
523 #[deprecated]
524 pub fn heater(&self) -> POAResult<bool> {
526 unsafe { self.get_config(POAConfig::POA_HEATER) }
527 }
528
529 pub fn heater_power(&self) -> POAResult<i64> {
531 unsafe { self.get_config(POAConfig::POA_HEATER_POWER) }
532 }
533
534 pub fn fan_power(&self) -> POAResult<i64> {
536 unsafe { self.get_config(POAConfig::POA_FAN_POWER) }
537 }
538
539 pub fn frame_limit(&self) -> POAResult<i64> {
542 unsafe { self.get_config(POAConfig::POA_FRAME_LIMIT) }
543 }
544
545 pub fn hqi(&self) -> POAResult<bool> {
548 unsafe { self.get_config(POAConfig::POA_HQI) }
549 }
550
551 pub fn usb_bandwidth_limit(&self) -> POAResult<i64> {
553 unsafe { self.get_config(POAConfig::POA_USB_BANDWIDTH_LIMIT) }
554 }
555
556 pub fn pixel_bin_sum(&self) -> POAResult<bool> {
558 unsafe { self.get_config(POAConfig::POA_PIXEL_BIN_SUM) }
559 }
560
561 pub fn mono_bin(&self) -> POAResult<bool> {
564 unsafe { self.get_config(POAConfig::POA_MONO_BIN) }
565 }
566
567 pub fn set_hardware_bin(&mut self, value: bool) -> POAResult<()> {
568 self.set_config(POAConfig::POA_HARDWARE_BIN, value, false)
569 }
570
571 pub fn set_wb_r(&mut self, value: i64) -> POAResult<()> {
573 self.set_config(POAConfig::POA_WB_R, value, false)
574 }
575
576 pub fn set_wb_g(&mut self, value: i64) -> POAResult<()> {
578 self.set_config(POAConfig::POA_WB_G, value, false)
579 }
580
581 pub fn set_wb_b(&mut self, value: i64) -> POAResult<()> {
583 self.set_config(POAConfig::POA_WB_B, value, false)
584 }
585
586 pub fn set_offset(&mut self, value: i64) -> POAResult<()> {
587 self.set_config(POAConfig::POA_OFFSET, value, false)
588 }
589
590 pub fn set_auto_max_gain(&mut self, value: i64) -> POAResult<()> {
592 self.set_config(POAConfig::POA_AUTOEXPO_MAX_GAIN, value, false)
593 }
594
595 pub fn set_auto_max_exposure_ms(&mut self, value: i64) -> POAResult<()> {
597 self.set_config(POAConfig::POA_AUTOEXPO_MAX_EXPOSURE, value, false)
598 }
599
600 pub fn set_auto_target_brightness(&mut self, value: i64) -> POAResult<()> {
602 self.set_config(POAConfig::POA_AUTOEXPO_BRIGHTNESS, value, false)
603 }
604
605 pub fn set_guide_north(&mut self, value: bool) -> POAResult<()> {
607 self.set_config(POAConfig::POA_GUIDE_NORTH, value, false)
608 }
609
610 pub fn set_guide_south(&mut self, value: bool) -> POAResult<()> {
612 self.set_config(POAConfig::POA_GUIDE_SOUTH, value, false)
613 }
614
615 pub fn set_guide_east(&mut self, value: bool) -> POAResult<()> {
617 self.set_config(POAConfig::POA_GUIDE_EAST, value, false)
618 }
619
620 pub fn set_guide_west(&mut self, value: bool) -> POAResult<()> {
622 self.set_config(POAConfig::POA_GUIDE_WEST, value, false)
623 }
624
625 pub fn set_target_temperature(&mut self, value: i64) -> POAResult<()> {
627 self.set_config(POAConfig::POA_TARGET_TEMP, value, false)
628 }
629
630 pub fn set_cooler(&mut self, value: bool) -> POAResult<()> {
632 self.set_config(POAConfig::POA_COOLER, value, false)
633 }
634
635 #[deprecated]
637 pub fn set_heater(&mut self, value: bool) -> POAResult<()> {
638 self.set_config(POAConfig::POA_HEATER, value, false)
639 }
640
641 pub fn set_heater_power(&mut self, value: i64) -> POAResult<()> {
643 self.set_config(POAConfig::POA_HEATER_POWER, value, false)
644 }
645
646 pub fn set_fan_power(&mut self, value: i64) -> POAResult<()> {
648 self.set_config(POAConfig::POA_FAN_POWER, value, false)
649 }
650
651 pub fn set_frame_limit(&mut self, value: i64) -> POAResult<()> {
654 self.set_config(POAConfig::POA_FRAME_LIMIT, value, false)
655 }
656
657 pub fn set_hqi(&mut self, value: bool) -> POAResult<()> {
661 self.set_config(POAConfig::POA_HQI, value, false)
662 }
663
664 pub fn set_usb_bandwidth_limit(&mut self, value: i64) -> POAResult<()> {
666 self.set_config(POAConfig::POA_USB_BANDWIDTH_LIMIT, value, false)
667 }
668
669 pub fn set_pixel_bin_sum(&mut self, value: bool) -> POAResult<()> {
671 self.set_config(POAConfig::POA_PIXEL_BIN_SUM, value, false)
672 }
673
674 pub fn set_mono_bin(&mut self, value: bool) -> POAResult<()> {
677 self.set_config(POAConfig::POA_MONO_BIN, value, false)
678 }
679
680 fn set_config(
681 &mut self,
682 poa_config: POAConfig,
683
684 value: impl Into<POAConfigValue>,
685 is_auto: bool,
686 ) -> POAResult<()> {
687 let value = value.into();
688 let error = unsafe { POASetConfig(self.camera_id, poa_config, value, is_auto.into()) };
689 if error != _POAErrors::POA_OK {
690 return Err(error.into());
691 }
692 Ok(())
693 }
694
695 unsafe fn get_config_auto<T: FromPOAConfigValue>(
699 &self,
700 poa_config: POAConfig,
701 ) -> POAResult<(T, bool)> {
702 let mut config_value = POAConfigValue::default();
703 let mut is_auto = POABool::POA_FALSE;
704
705 let error = unsafe {
706 POAGetConfig(
707 self.camera_id,
708 poa_config,
709 &raw mut config_value,
710 &raw mut is_auto,
711 )
712 };
713 if error != _POAErrors::POA_OK {
714 return Err(error.into());
715 }
716
717 Ok((
718 FromPOAConfigValue::from_poa_config_value(config_value),
719 is_auto.into(),
720 ))
721 }
722
723 unsafe fn get_config<T: FromPOAConfigValue>(&self, poa_config: POAConfig) -> POAResult<T> {
727 self.get_config_auto(poa_config).map(|(value, _)| value)
728 }
729}
730
731fn safe_error(error: POAErrors) {
734 if error == _POAErrors::POA_OK {
735 return;
736 }
737 panic!("unexpected POA error: {}", Error::from(error));
738}
739
740fn enumerate_sensor_modes(camera_id: i32) -> POAResult<Vec<SensorMode>> {
745 let mut count: c_int = 0;
746 let err = unsafe { POAGetSensorModeCount(camera_id, &raw mut count) };
747 if err != _POAErrors::POA_OK {
748 return Err(err.into());
749 }
750 if count <= 0 {
751 return Ok(Vec::new());
752 }
753
754 let mut modes = Vec::with_capacity(count as usize);
755 for index in 0..count {
756 let mut info = POASensorModeInfo::default();
757 let err = unsafe { POAGetSensorModeInfo(camera_id, index, &raw mut info) };
758 if err != _POAErrors::POA_OK {
759 return Err(err.into());
760 }
761 modes.push(SensorMode {
762 index: index as u32,
763 name: c_char_array_to_string(&info.name),
764 description: c_char_array_to_string(&info.desc),
765 });
766 }
767 Ok(modes)
768}
769
770fn c_char_array_to_string(buf: &[c_char]) -> String {
775 let nul = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
776 let bytes: Vec<u8> = buf[..nul].iter().map(|&c| c as u8).collect();
777 String::from_utf8_lossy(&bytes).trim().to_string()
778}