1use std::{
5 convert::TryInto,
6 sync::{
7 Arc, Mutex, OnceLock,
8 atomic::{AtomicBool, Ordering},
9 },
10};
11use tracing::{error, warn};
12
13use machine_vision_formats as formats;
14
15use ci2::{AcquisitionMode, AutoMode, DynamicFrameWithInfo, HostTimingInfo, TriggerMode};
16use formats::PixFmt;
17
18use std::sync::mpsc::{Receiver, SyncSender};
19use strand_dynamic_frame::DynamicFrameOwned;
20
21const N_BUFFER_FRAMES: usize = 10;
23const N_CHANNEL_FRAMES: usize = 10;
25
26struct FrameSender {
27 handle: CamHandle,
28 tx: SyncSender<std::result::Result<InvalidHostFramenumber, ci2::Error>>,
29}
30
31struct CamHandle {
32 inner: vmbc_sys::VmbHandle_t,
33}
34
35unsafe impl Sync for CamHandle {}
36unsafe impl Send for CamHandle {}
37
38static VIMBA_LIB: OnceLock<vimba::VimbaLibrary> = OnceLock::new();
43static IS_DONE: AtomicBool = AtomicBool::new(false);
44static SENDERS: Mutex<Vec<FrameSender>> = Mutex::new(Vec::new());
45
46fn vimba_lib() -> &'static vimba::VimbaLibrary {
52 VIMBA_LIB
53 .get()
54 .expect("Vimba library accessed before new_module() loaded it")
55}
56
57fn ve2ce(orig: vimba::Error) -> ci2::Error {
59 ci2::Error::from(anyhow::anyhow!("vimba::Error: {orig:?}"))
63}
64
65fn callback_rust(
66 camera_handle: vmbc_sys::VmbHandle_t,
67 frame: *mut vmbc_sys::VmbFrame_t,
68) -> ci2::Result<()> {
69 let now = chrono::Utc::now(); let frame_status = unsafe { (*frame).receiveStatus };
71 if !IS_DONE.load(Ordering::Relaxed) {
72 let msg = if frame_status == vmbc_sys::VmbFrameStatusType::VmbFrameStatusComplete {
75 let buf_ref = unsafe {
77 let buf_ref1 = (*frame).buffer;
78 let buf_len = (*frame).bufferSize as usize;
79 std::slice::from_raw_parts(buf_ref1 as *const u8, buf_len)
80 };
81 let image_data = buf_ref.to_vec(); let code = unsafe { (*frame).pixelFormat };
86
87 let flags = unsafe { (*frame).receiveFlags };
88 let frame_id = if flags & vmbc_sys::VmbFrameFlagsType::VmbFrameFlagsFrameID.0 != 0 {
89 unsafe { (*frame).frameID }
90 } else {
91 eprintln!("no frame number data in frame");
92 0
93 };
94
95 let device_timestamp =
96 if flags & vmbc_sys::VmbFrameFlagsType::VmbFrameFlagsTimestamp.0 != 0 {
97 unsafe { (*frame).timestamp }
98 } else {
99 eprintln!("no timestamp data in frame");
100 0
101 };
102
103 let pixel_format = vimba::pixel_format_code(code).map_vimba_err()?;
104
105 {
106 let extra = Box::new(ci2_vimba_types::VimbaExtra {
107 frame_id,
108 device_timestamp,
109 });
110
111 let width = unsafe { (*frame).width };
112 let height = unsafe { (*frame).height };
113
114 let min_stride = width as usize * pixel_format.bits_per_pixel() as usize / 8;
116 debug_assert!(min_stride * height as usize == image_data.len());
117 let image = Arc::new(
118 DynamicFrameOwned::from_buf(
119 width,
120 height,
121 min_stride,
122 image_data,
123 pixel_format,
124 )
125 .unwrap(),
126 );
127
128 Ok(InvalidHostFramenumber(DynamicFrameWithInfo {
129 image,
130 host_timing: HostTimingInfo {
131 fno: 0, datetime: now,
133 },
134 backend_data: Some(extra),
135 }))
136 }
137 } else {
138 let str_msg = match frame_status {
139 vmbc_sys::VmbFrameStatusType::VmbFrameStatusIncomplete => {
140 "Frame could not be filled to the end"
141 }
142 vmbc_sys::VmbFrameStatusType::VmbFrameStatusTooSmall => {
143 "Frame buffer was too small"
144 }
145 vmbc_sys::VmbFrameStatusType::VmbFrameStatusInvalid => "Frame buffer was invalid",
146 other => {
147 if other == -4 {
148 eprintln!("undocumented frame status -4: was VmbShutdown() called?");
149 }
150 panic!("undocumented frame status received {}", other);
151 }
152 };
153 Err(ci2::Error::SingleFrameError(str_msg.into()))
154 };
155
156 let err_code = {
158 unsafe {
159 vimba_lib()
160 .vimba_lib
161 .VmbCaptureFrameQueue(camera_handle, frame, Some(callback_c))
162 }
163 };
164
165 if err_code != vmbc_sys::VmbErrorType::VmbErrorSuccess {
166 let e = vimba::Error::from(vimba::VimbaError::from(err_code));
167 return Err(ve2ce(e));
168 }
169
170 let tx = {
171 let vec_senders = &mut *SENDERS.lock().unwrap();
173 if let Some(idx) = vec_senders
174 .iter()
175 .position(|x| x.handle.inner == camera_handle)
176 {
177 let sender = &vec_senders[idx];
178 sender.tx.clone()
179 } else {
180 return Err(ci2::Error::from(format!(
181 "CB: no sender found for camera: {:?}",
182 camera_handle
183 )));
184 }
185 };
186
187 match tx.try_send(msg) {
188 Ok(()) => {}
189 Err(std::sync::mpsc::TrySendError::Full(_msg)) => {
190 warn!("channel full");
191 }
192 Err(std::sync::mpsc::TrySendError::Disconnected(_frame_result)) => {
193 error!("disconnected channel");
194 IS_DONE.store(true, Ordering::Relaxed); }
196 }
197 }
198 Ok(())
199}
200
201#[unsafe(no_mangle)]
207pub unsafe extern "C" fn callback_c(
208 camera_handle: vmbc_sys::VmbHandle_t,
209 _stream_handle: vmbc_sys::VmbHandle_t,
210 frame: *mut vmbc_sys::VmbFrame_t,
211) {
212 match std::panic::catch_unwind(|| {
213 callback_rust(camera_handle, frame).unwrap();
214 }) {
215 Ok(()) => {}
216 Err(e) => {
217 eprintln!("CB: Error: Panic {:?}", e);
218 IS_DONE.store(true, Ordering::Relaxed); }
220 }
221}
222
223trait ExtendedError<T> {
224 fn map_vimba_err(self) -> ci2::Result<T>;
225}
226
227impl<T> ExtendedError<T> for std::result::Result<T, vimba::Error> {
228 fn map_vimba_err(self) -> ci2::Result<T> {
229 self.map_err(ve2ce)
230 }
231}
232
233pub type Result<M> = std::result::Result<M, vimba::Error>;
234
235#[derive(Clone)]
236pub struct WrappedModule {}
237
238impl WrappedModule {
239 fn camera_infos(&self) -> ci2::Result<Vec<VimbaCameraInfo>> {
240 let n_cams = vimba_lib().n_cameras().map_vimba_err()?;
241 let vimba_infos = vimba_lib().camera_info(n_cams).map_vimba_err()?;
242
243 let infos = vimba_infos
244 .into_iter()
245 .map(|info| {
246 let serial = info.serial_string;
247 let model = info.camera_name;
248 let vendor = "Allied Vision".to_string(); let name = info.camera_id_string;
250 VimbaCameraInfo {
251 name,
252 serial,
253 model,
254 vendor,
255 }
256 })
257 .collect();
258 Ok(infos)
259 }
260}
261
262fn dir_has_vimba_transport_layer(dir: &std::path::Path) -> bool {
264 let Ok(entries) = std::fs::read_dir(dir) else {
265 return false;
266 };
267 entries.flatten().any(|entry| {
268 let name = entry.file_name();
269 let name = name.to_string_lossy();
270 name.starts_with("Vimba") && name.ends_with(".cti")
271 })
272}
273
274fn gentl_path_hint() -> Option<String> {
284 let has_vimba_tl = std::env::var_os("GENICAM_GENTL64_PATH")
285 .map(|paths| std::env::split_paths(&paths).any(|dir| dir_has_vimba_transport_layer(&dir)))
286 .unwrap_or(false);
287 if has_vimba_tl {
288 return None;
289 }
290
291 let installer = std::cfg_select! {
292 target_os = "linux" => "Complete the Vimba SDK installation by running its GenTL path \
293 installer (for example `sudo /opt/VimbaX_2024-1/cti/Install_GenTL_Path.sh`) and \
294 then starting a new login shell so that `/etc/profile.d/VimbaX_GenTL_Path_64bit.sh` \
295 sets the variable.",
296 _ => "Complete the Vimba SDK installation as described in Allied Vision's \
297 documentation so that this variable is set.",
298 };
299
300 Some(format!(
301 "The GENICAM_GENTL64_PATH environment variable does not point to a Vimba GenTL \
302 transport layer, which is the usual cause of VmbErrorNoTL. {installer}"
303 ))
304}
305
306pub fn new_module() -> ci2::Result<WrappedModule> {
307 if VIMBA_LIB.get().is_none() {
312 let lib = vimba::VimbaLibrary::new().map_err(|e| {
313 let mut msg = format!(
314 "Could not initialize the Allied Vision Vimba SDK (is it installed?). \
315 The underlying error was: {e:?}"
316 );
317 if let Some(hint) = gentl_path_hint() {
318 msg = format!("{msg}\n\nHint: {hint}");
319 }
320 ci2::Error::from(anyhow::anyhow!(msg))
321 })?;
322 let _ = VIMBA_LIB.set(lib);
326 }
327 Ok(WrappedModule {})
328}
329
330pub struct VimbaTerminateGuard {
331 already_dropped: bool,
332}
333
334impl Drop for VimbaTerminateGuard {
335 fn drop(&mut self) {
336 if !self.already_dropped {
337 if let Some(lib) = VIMBA_LIB.get() {
340 unsafe {
341 lib.shutdown();
342 }
343 }
344 self.already_dropped = true;
345 }
346 }
347}
348
349pub fn make_singleton_guard(
350 _vimba_module: &dyn ci2::CameraModule<CameraType = WrappedCamera<'static>, Guard = VimbaTerminateGuard>,
351) -> ci2::Result<VimbaTerminateGuard> {
352 Ok(VimbaTerminateGuard {
353 already_dropped: false,
354 })
355}
356
357impl<'a> ci2::CameraModule for &'a WrappedModule {
358 type CameraType = WrappedCamera<'static>;
363 type Guard = VimbaTerminateGuard;
364
365 fn name(self: &&'a WrappedModule) -> &'static str {
366 "vimba"
367 }
368 fn camera_infos(self: &&'a WrappedModule) -> ci2::Result<Vec<Box<dyn ci2::CameraInfo>>> {
369 let vec1 = WrappedModule::camera_infos(self)?;
370 let infos = vec1
371 .into_iter()
372 .map(|vci| {
373 let pci = Box::new(vci);
374 let ci: Box<dyn ci2::CameraInfo> = pci; ci
376 })
377 .collect();
378 Ok(infos)
379 }
380 fn camera(self: &mut &'a WrappedModule, name: &str) -> ci2::Result<Self::CameraType> {
381 let camera = vimba::Camera::open(name, vimba::access_mode::FULL, &vimba_lib().vimba_lib)
382 .map_vimba_err()?;
383
384 let vimba_infos = WrappedModule::camera_infos(self)?;
385 let mut my_info = None;
386 for ci in vimba_infos.into_iter() {
387 if ci.name.as_str() == name {
388 my_info = Some(ci);
389 break;
390 }
391 }
392 let info = my_info.unwrap();
393
394 let rx = {
395 let vec_senders = &mut *SENDERS.lock().unwrap();
397 let (tx, rx) = std::sync::mpsc::sync_channel(N_CHANNEL_FRAMES);
398 let sender = FrameSender {
399 handle: CamHandle {
400 inner: camera.handle(),
401 },
402 tx,
403 };
404 vec_senders.push(sender);
405 rx
406 };
407
408 Ok(WrappedCamera {
409 camera: Arc::new(Mutex::new(camera)),
410 acquisition_started: false,
411 info,
412 frames: Vec::with_capacity(N_BUFFER_FRAMES),
413 rx,
414 store_fno: 0,
415 })
416 }
417
418 fn settings_file_extension(&self) -> &str {
419 "xml"
420 }
421}
422
423#[derive(Debug)]
424pub struct VimbaCameraInfo {
425 name: String,
426 serial: String,
427 model: String,
428 vendor: String,
429}
430
431impl ci2::CameraInfo for VimbaCameraInfo {
432 fn name(&self) -> &str {
433 &self.name
434 }
435 fn serial(&self) -> &str {
436 &self.serial
437 }
438 fn model(&self) -> &str {
439 &self.model
440 }
441 fn vendor(&self) -> &str {
442 &self.vendor
443 }
444}
445
446struct InvalidHostFramenumber(DynamicFrameWithInfo);
448
449impl InvalidHostFramenumber {
450 fn into_valid(self, fno: usize) -> DynamicFrameWithInfo {
451 let mut result = self.0;
452 result.host_timing.fno = fno;
453 result
454 }
455}
456
457pub struct WrappedCamera<'lib> {
458 pub camera: Arc<Mutex<vimba::Camera<'lib>>>,
459 pub info: VimbaCameraInfo,
460 acquisition_started: bool,
461 frames: Vec<vimba::Frame>,
462 rx: Receiver<std::result::Result<InvalidHostFramenumber, ci2::Error>>,
463 store_fno: usize,
464}
465
466fn _test_camera_is_send() {
467 fn implements<T: Send>() {}
469 implements::<WrappedCamera>();
470}
471
472impl<'lib> ci2::CameraInfo for WrappedCamera<'lib> {
473 fn name(&self) -> &str {
474 self.info.name()
475 }
476 fn serial(&self) -> &str {
477 self.info.serial()
478 }
479 fn model(&self) -> &str {
480 self.info.model()
481 }
482 fn vendor(&self) -> &str {
483 self.info.vendor()
484 }
485}
486
487impl<'lib> ci2::Camera for WrappedCamera<'lib> {
488 fn command_execute(&self, name: &str, _verify: bool) -> ci2::Result<()> {
503 self.camera
504 .lock()
505 .unwrap()
506 .command_run(name)
507 .map_vimba_err()
508 }
509
510 fn feature_bool(&self, name: &str) -> ci2::Result<bool> {
511 self.camera
512 .lock()
513 .unwrap()
514 .feature_boolean(name)
515 .map_vimba_err()
516 }
517
518 fn feature_bool_set(&self, name: &str, value: bool) -> ci2::Result<()> {
519 self.camera
520 .lock()
521 .unwrap()
522 .feature_boolean_set(name, value)
523 .map_vimba_err()
524 }
525
526 fn feature_enum(&self, name: &str) -> ci2::Result<String> {
527 self.camera
528 .lock()
529 .unwrap()
530 .feature_enum(name)
531 .map_vimba_err()
532 .map(Into::into)
533 }
534
535 fn feature_enum_set(&self, name: &str, value: &str) -> ci2::Result<()> {
536 self.camera
537 .lock()
538 .unwrap()
539 .feature_enum_set(name, value)
540 .map_vimba_err()
541 }
542
543 fn feature_float(&self, name: &str) -> ci2::Result<f64> {
544 self.camera
545 .lock()
546 .unwrap()
547 .feature_float(name)
548 .map_vimba_err()
549 }
550
551 fn feature_float_set(&self, name: &str, value: f64) -> ci2::Result<()> {
552 self.camera
553 .lock()
554 .unwrap()
555 .feature_float_set(name, value)
556 .map_vimba_err()
557 }
558
559 fn feature_int(&self, name: &str) -> ci2::Result<i64> {
560 self.camera
561 .lock()
562 .unwrap()
563 .feature_int(name)
564 .map_vimba_err()
565 }
566
567 fn feature_int_set(&self, name: &str, value: i64) -> ci2::Result<()> {
568 self.camera
569 .lock()
570 .unwrap()
571 .feature_int_set(name, value)
572 .map_vimba_err()
573 }
574
575 fn node_map_load(&self, settings: &str) -> std::result::Result<(), ci2::Error> {
578 let dir = tempfile::tempdir()?;
579
580 let settings_path = dir.path().join("settings.xml");
582 {
583 use std::io::Write;
584
585 let mut file = std::fs::File::create(&settings_path)?;
587 file.write_all(settings.as_bytes())?;
588 file.flush()?;
589 }
591
592 let settings_settings = vimba::default_feature_persist_settings(); self.camera
594 .lock()
595 .unwrap()
596 .camera_settings_load(&settings_path, &settings_settings)
597 .map_vimba_err()
598
599 }
601
602 fn node_map_save(&self) -> std::result::Result<String, ci2::Error> {
603 let dir = tempfile::tempdir()?;
604
605 let settings_path = dir.path().join("settings.xml");
607
608 let settings_settings = vimba::default_feature_persist_settings(); self.camera
610 .lock()
611 .unwrap()
612 .camera_settings_save(&settings_path, &settings_settings)
613 .map_vimba_err()?;
614
615 let buf = std::fs::read_to_string(&settings_path)?;
616 Ok(buf)
617 }
619
620 fn width(&self) -> std::result::Result<u32, ci2::Error> {
621 Ok(self
622 .camera
623 .lock()
624 .unwrap()
625 .feature_int("Width")
626 .map_vimba_err()?
627 .try_into()?)
628 }
629 fn height(&self) -> std::result::Result<u32, ci2::Error> {
630 Ok(self
631 .camera
632 .lock()
633 .unwrap()
634 .feature_int("Height")
635 .map_vimba_err()?
636 .try_into()?)
637 }
638 fn pixel_format(&self) -> std::result::Result<PixFmt, ci2::Error> {
639 self.camera.lock().unwrap().pixel_format().map_vimba_err()
640 }
641 fn possible_pixel_formats(&self) -> std::result::Result<Vec<PixFmt>, ci2::Error> {
642 let fmts = self
643 .camera
644 .lock()
645 .unwrap()
646 .feature_enum_range_query("PixelFormat")
647 .map_vimba_err()?;
648 Ok(fmts
649 .iter()
650 .filter_map(|fmt_str| vimba::str_to_pixel_format(fmt_str).map_vimba_err().ok())
652 .collect())
653 }
654 fn set_pixel_format(&mut self, pixfmt: PixFmt) -> std::result::Result<(), ci2::Error> {
655 let pixfmt_vimba = vimba::pixel_format_to_str(pixfmt).map_vimba_err()?;
656 self.camera
657 .lock()
658 .unwrap()
659 .feature_enum_set("PixelFormat", pixfmt_vimba)
660 .map_vimba_err()?;
661 Ok(())
662 }
663 fn exposure_time(&self) -> std::result::Result<f64, ci2::Error> {
664 self.camera
665 .lock()
666 .unwrap()
667 .feature_float("ExposureTime")
668 .map_vimba_err()
669 }
670 fn exposure_time_range(&self) -> std::result::Result<(f64, f64), ci2::Error> {
671 self.camera
672 .lock()
673 .unwrap()
674 .feature_float_range_query("ExposureTime")
675 .map_vimba_err()
676 }
677 fn set_exposure_time(&mut self, value: f64) -> std::result::Result<(), ci2::Error> {
678 self.camera
679 .lock()
680 .unwrap()
681 .feature_float_set("ExposureTime", value)
682 .map_vimba_err()
683 }
684 fn exposure_auto(&self) -> std::result::Result<AutoMode, ci2::Error> {
685 let c = self.camera.lock().unwrap();
686 let mystr = c.feature_enum("ExposureAuto").map_vimba_err()?;
687 str_to_auto_mode(mystr)
688 }
689 fn set_exposure_auto(&mut self, value: AutoMode) -> std::result::Result<(), ci2::Error> {
690 let valstr = auto_mode_to_str(value);
691 let c = self.camera.lock().unwrap();
692 c.feature_enum_set("ExposureAuto", valstr).map_vimba_err()
693 }
694 fn gain(&self) -> std::result::Result<f64, ci2::Error> {
695 self.camera
696 .lock()
697 .unwrap()
698 .feature_float("Gain")
699 .map_vimba_err()
700 }
701 fn gain_range(&self) -> std::result::Result<(f64, f64), ci2::Error> {
702 self.camera
703 .lock()
704 .unwrap()
705 .feature_float_range_query("Gain")
706 .map_vimba_err()
707 }
708 fn set_gain(&mut self, value: f64) -> std::result::Result<(), ci2::Error> {
709 self.camera
710 .lock()
711 .unwrap()
712 .feature_float_set("Gain", value)
713 .map_vimba_err()
714 }
715 fn gain_auto(&self) -> std::result::Result<AutoMode, ci2::Error> {
716 let c = self.camera.lock().unwrap();
717 let mystr = c.feature_enum("GainAuto").map_vimba_err()?;
718 str_to_auto_mode(mystr)
719 }
720 fn set_gain_auto(&mut self, value: AutoMode) -> std::result::Result<(), ci2::Error> {
721 let valstr = auto_mode_to_str(value);
722 let c = self.camera.lock().unwrap();
723 c.feature_enum_set("GainAuto", valstr).map_vimba_err()
724 }
725
726 fn start_default_external_triggering(&mut self) -> std::result::Result<(), ci2::Error> {
727 let restart = if self.acquisition_started {
728 self.acquisition_stop()?;
729 true
730 } else {
731 false
732 };
733
734 self.set_trigger_selector(ci2::TriggerSelector::FrameStart)?;
736 {
737 let c = self.camera.lock().unwrap();
738 c.feature_enum_set("TriggerSource", "Line0")
739 .map_vimba_err()?;
740 }
741 self.set_trigger_mode(ci2::TriggerMode::On)?;
742 if restart {
743 self.acquisition_start()?;
744 }
745 Ok(())
746 }
747
748 fn set_software_frame_rate_limit(
749 &mut self,
750 fps_limit: f64,
751 ) -> std::result::Result<(), ci2::Error> {
752 let restart = if self.acquisition_started {
753 self.acquisition_stop()?;
754 true
755 } else {
756 false
757 };
758
759 self.set_acquisition_frame_rate_enable(true)?;
760 self.set_acquisition_frame_rate(fps_limit)?;
761
762 if restart {
763 self.acquisition_start()?;
764 }
765 Ok(())
766 }
767
768 fn trigger_mode(&self) -> std::result::Result<TriggerMode, ci2::Error> {
769 let c = self.camera.lock().unwrap();
770 let val = c.feature_enum("TriggerMode").map_vimba_err()?;
771 match val {
772 "Off" => Ok(ci2::TriggerMode::Off),
773 "On" => Ok(ci2::TriggerMode::On),
774 s => Err(ci2::Error::from(format!(
775 "unexpected TriggerMode enum string: {}",
776 s
777 ))),
778 }
779 }
780 fn set_trigger_mode(&mut self, val: TriggerMode) -> std::result::Result<(), ci2::Error> {
781 let valstr = match val {
782 ci2::TriggerMode::Off => "Off",
783 ci2::TriggerMode::On => "On",
784 };
785 let c = self.camera.lock().unwrap();
786 c.feature_enum_set("TriggerMode", valstr).map_vimba_err()
787 }
788 fn acquisition_frame_rate_enable(&self) -> std::result::Result<bool, ci2::Error> {
789 self.camera
790 .lock()
791 .unwrap()
792 .feature_boolean("AcquisitionFrameRateEnable")
793 .map_vimba_err()
794 }
795 fn set_acquisition_frame_rate_enable(
796 &mut self,
797 value: bool,
798 ) -> std::result::Result<(), ci2::Error> {
799 self.camera
800 .lock()
801 .unwrap()
802 .feature_boolean_set("AcquisitionFrameRateEnable", value)
803 .map_vimba_err()
804 }
805 fn acquisition_frame_rate(&self) -> std::result::Result<f64, ci2::Error> {
806 self.camera
807 .lock()
808 .unwrap()
809 .feature_float("AcquisitionFrameRate")
810 .map_vimba_err()
811 }
812 fn acquisition_frame_rate_range(&self) -> std::result::Result<(f64, f64), ci2::Error> {
813 self.camera
814 .lock()
815 .unwrap()
816 .feature_float_range_query("AcquisitionFrameRate")
817 .map_vimba_err()
818 }
819 fn set_acquisition_frame_rate(&mut self, value: f64) -> std::result::Result<(), ci2::Error> {
820 self.camera
821 .lock()
822 .unwrap()
823 .feature_float_set("AcquisitionFrameRate", value)
824 .map_vimba_err()
825 }
826 fn trigger_selector(&self) -> std::result::Result<ci2::TriggerSelector, ci2::Error> {
827 let c = self.camera.lock().unwrap();
828 let val = c.feature_enum("TriggerSelector").map_vimba_err()?;
829 match val {
830 "AcquisitionStart" => Ok(ci2::TriggerSelector::AcquisitionStart),
831 "FrameBurstStart" => Ok(ci2::TriggerSelector::FrameBurstStart),
832 "FrameStart" => Ok(ci2::TriggerSelector::FrameStart),
833 "ExposureActive" => Ok(ci2::TriggerSelector::ExposureActive),
834 s => Err(ci2::Error::from(format!(
835 "unexpected TriggerSelector enum string: {}",
836 s
837 ))),
838 }
839 }
840 fn set_trigger_selector(
841 &mut self,
842 val: ci2::TriggerSelector,
843 ) -> std::result::Result<(), ci2::Error> {
844 let valstr = match val {
845 ci2::TriggerSelector::AcquisitionStart => "AcquisitionStart",
846 ci2::TriggerSelector::FrameStart => "FrameStart",
847 ci2::TriggerSelector::FrameBurstStart => "FrameBurstStart",
848 ci2::TriggerSelector::ExposureActive => "ExposureActive",
849 _ => {
850 return Err(ci2::Error::from(format!(
851 "unknown TriggerSelector mode: {:?}",
852 val
853 )));
854 }
855 };
856 let c = self.camera.lock().unwrap();
857 c.feature_enum_set("TriggerSelector", valstr)
858 .map_vimba_err()
859 }
860 fn acquisition_mode(&self) -> std::result::Result<AcquisitionMode, ci2::Error> {
861 let val = self
862 .camera
863 .lock()
864 .unwrap()
865 .feature_enum("AcquisitionMode")
866 .map_vimba_err()?;
867 Ok(match val {
868 "Continuous" => AcquisitionMode::Continuous,
869 "SingleFrame" => AcquisitionMode::SingleFrame,
870 "MultiFrame" => AcquisitionMode::MultiFrame,
871 val => {
872 return Err(ci2::Error::from(format!(
873 "unknown AcquisitionMode: {:?}",
874 val
875 )));
876 }
877 })
878 }
879 fn set_acquisition_mode(
880 &mut self,
881 value: AcquisitionMode,
882 ) -> std::result::Result<(), ci2::Error> {
883 let modes = self
884 .camera
885 .lock()
886 .unwrap()
887 .feature_enum_range_query("AcquisitionMode")
888 .map_vimba_err()?;
889 println!("modes {:?}", modes);
890
891 let sval = match value {
892 AcquisitionMode::Continuous => "Continuous",
893 AcquisitionMode::SingleFrame => "SingleFrame",
894 AcquisitionMode::MultiFrame => "MultiFrame",
895 };
896 self.camera
897 .lock()
898 .unwrap()
899 .feature_enum_set("AcquisitionMode", sval)
900 .map_vimba_err()
901 }
902 fn acquisition_start(&mut self) -> std::result::Result<(), ci2::Error> {
903 IS_DONE.store(false, Ordering::Relaxed); let camera = self.camera.lock().unwrap();
906
907 for _ in 0..N_BUFFER_FRAMES {
908 let buffer = camera.allocate_buffer().map_vimba_err()?;
909 let mut frame = vimba::Frame::new(buffer);
910 camera.frame_announce(&mut frame).map_vimba_err()?;
911 self.frames.push(frame);
912 }
913
914 {
917 camera.capture_start().map_vimba_err()?;
918
919 for frame in self.frames.iter_mut() {
920 camera
921 .capture_frame_queue_with_callback(frame, Some(callback_c))
922 .map_vimba_err()?;
923 }
924
925 camera.command_run("AcquisitionStart").map_vimba_err()?;
926 }
927
928 self.acquisition_started = true;
929 Ok(())
930 }
931 fn acquisition_stop(&mut self) -> std::result::Result<(), ci2::Error> {
932 let camera = self.camera.lock().unwrap();
933
934 IS_DONE.store(true, Ordering::Relaxed); {
937 camera.command_run("AcquisitionStop").map_vimba_err()?;
938 camera.capture_end().map_vimba_err()?;
939 camera.capture_queue_flush().map_vimba_err()?;
940 for mut frame in self.frames.drain(..) {
941 camera.frame_revoke(&mut frame).map_vimba_err()?;
942 }
943 }
944 self.acquisition_started = false;
945 Ok(())
946 }
947 fn next_frame(&mut self) -> std::result::Result<DynamicFrameWithInfo, ci2::Error> {
948 let msg = match self.rx.recv() {
949 Ok(msg) => msg,
950 Err(err) => {
951 return Err(ci2::Error::BackendError(anyhow::anyhow!(
952 "Error receiving frame : {}",
953 err
954 )));
955 }
956 };
957 let frame = msg?.into_valid(self.store_fno);
958 self.store_fno += 1;
959 Ok(frame)
960 }
961}
962
963fn str_to_auto_mode(val: &str) -> ci2::Result<ci2::AutoMode> {
964 match val {
965 "Off" => Ok(ci2::AutoMode::Off),
966 "Once" => Ok(ci2::AutoMode::Once),
967 "Continuous" => Ok(ci2::AutoMode::Continuous),
968 s => Err(ci2::Error::from(format!(
969 "unexpected AutoMode enum string: {}",
970 s
971 ))),
972 }
973}
974
975fn auto_mode_to_str(value: ci2::AutoMode) -> &'static str {
976 use ci2::AutoMode::*;
977 match value {
978 Off => "Off",
979 Once => "Once",
980 Continuous => "Continuous",
981 }
982}