autd3/
controller.rs

1use autd3_core::{
2    datagram::{Inspectable, InspectionResult},
3    derive::{Datagram, DeviceFilter},
4    link::{Ack, Link, MsgId, RxMessage},
5    sleep::Sleep,
6};
7pub use autd3_driver::firmware::driver::{
8    Driver, FPGAState, FixedDelay, FixedSchedule, ParallelMode, SenderOption, TimerStrategy,
9};
10use autd3_driver::{
11    error::AUTDDriverError,
12    firmware::{self, auto::Auto, driver::Sender, version::FirmwareVersion},
13    geometry::{Device, Geometry},
14};
15
16use derive_more::{Deref, DerefMut};
17use getset::{Getters, MutGetters};
18
19/// A controller for the AUTD devices.
20///
21/// All operations to the devices are done through this struct.
22#[derive(Deref, DerefMut, Getters, MutGetters)]
23pub struct Controller<L: Link, V: Driver> {
24    /// The link to the devices.
25    #[getset(get = "pub", get_mut = "pub")]
26    link: L,
27    #[doc(hidden)]
28    #[getset(get = "pub")]
29    driver: V,
30    /// The geometry of the devices.
31    #[getset(get = "pub", get_mut = "pub")]
32    #[deref]
33    #[deref_mut]
34    geometry: Geometry,
35    msg_id: MsgId,
36    sent_flags: smallvec::SmallVec<[bool; 32]>,
37    rx_buf: Vec<RxMessage>,
38    /// The default sender option used for [`send`](Controller::send).
39    pub default_sender_option: SenderOption,
40}
41
42impl<L: Link> Controller<L, Auto> {
43    /// Equivalent to [`Self::open_with_option`] with default [`SenderOption`], [`FixedSchedule`] and [`Auto`] diver.
44    pub fn open<D: Into<Device>, F: IntoIterator<Item = D>>(
45        devices: F,
46        link: L,
47    ) -> Result<Self, AUTDDriverError> {
48        Self::open_with_option(devices, link, Default::default(), FixedSchedule::default())
49    }
50}
51
52impl<L: Link, V: Driver> Controller<L, V> {
53    /// Equivalent to [`Self::open_with_option`] with default [`SenderOption`] and [`FixedSchedule`].
54    pub fn open_with<D: Into<Device>, F: IntoIterator<Item = D>>(
55        devices: F,
56        link: L,
57    ) -> Result<Self, AUTDDriverError> {
58        Self::open_with_option(devices, link, Default::default(), FixedSchedule::default())
59    }
60
61    /// Opens a controller with a [`SenderOption`].
62    ///
63    /// Opens link, and then initialize and synchronize the devices. The `timeout` is used to send data for initialization and synchronization.
64    pub fn open_with_option<
65        D: Into<Device>,
66        F: IntoIterator<Item = D>,
67        S: Sleep,
68        T: TimerStrategy<S>,
69    >(
70        devices: F,
71        mut link: L,
72        option: SenderOption,
73        timer_strategy: T,
74    ) -> Result<Self, AUTDDriverError> {
75        let geometry = Geometry::new(devices.into_iter().map(|d| d.into()).collect());
76
77        link.open(&geometry)?;
78
79        let mut msg_id = MsgId::new(0);
80        let mut sent_flags = smallvec::smallvec![false; geometry.len()];
81        let mut rx_buf = vec![RxMessage::new(0, Ack::new()); geometry.len()];
82
83        let mut driver = V::new();
84        driver.detect_version(
85            &mut msg_id,
86            &mut link,
87            &geometry,
88            &mut sent_flags,
89            &mut rx_buf,
90        )?;
91
92        let mut cnt = Controller {
93            link,
94            driver,
95            msg_id,
96            sent_flags,
97            rx_buf,
98            geometry,
99            default_sender_option: option,
100        };
101
102        cnt.sender(option, timer_strategy).initialize_devices()?;
103
104        Ok(cnt)
105    }
106
107    /// Returns the [`Sender`] to send data to the devices.
108    pub fn sender<S: Sleep, T: TimerStrategy<S>>(
109        &mut self,
110        option: SenderOption,
111        timer_strategy: T,
112    ) -> V::Sender<'_, L, S, T> {
113        self.driver.sender(
114            &mut self.msg_id,
115            &mut self.link,
116            &self.geometry,
117            &mut self.sent_flags,
118            &mut self.rx_buf,
119            option,
120            timer_strategy,
121        )
122    }
123
124    /// Returns the inspection result.
125    pub fn inspect<I: Inspectable>(&self, s: I) -> Result<InspectionResult<I::Result>, I::Error> {
126        s.inspect(
127            &self.geometry,
128            &DeviceFilter::all_enabled(),
129            &self.driver.firmware_limits(),
130        )
131    }
132
133    /// Closes the controller.
134    pub fn close(mut self) -> Result<(), AUTDDriverError> {
135        self.close_impl(self.default_sender_option, FixedSchedule::default())
136    }
137
138    /// Returns the firmware version of the devices.
139    pub fn firmware_version(&mut self) -> Result<Vec<FirmwareVersion>, AUTDDriverError> {
140        self.sender(self.default_sender_option, FixedSchedule::default())
141            .firmware_version()
142    }
143
144    /// Returns the FPGA state of the devices.
145    ///
146    /// To get the state of devices, enable reads FPGA state mode by [`ReadsFPGAState`] before calling this method.
147    /// The returned value is [`None`] if the reads FPGA state mode is disabled for the device.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// # use autd3::prelude::*;
153    /// # fn main() -> Result<(), AUTDDriverError> {
154    /// let mut autd = Controller::open([AUTD3::default()], Nop::new())?;
155    ///
156    /// autd.send(ReadsFPGAState::new(|_| true))?;
157    ///
158    /// let states = autd.fpga_state()?;
159    /// Ok(())
160    /// # }
161    /// ```
162    ///
163    /// [`ReadsFPGAState`]: autd3_driver::datagram::ReadsFPGAState
164    pub fn fpga_state(&mut self) -> Result<Vec<Option<V::FPGAState>>, AUTDDriverError> {
165        self.link.ensure_is_open()?;
166        self.link.receive(&mut self.rx_buf)?;
167        Ok(self.rx_buf.iter().map(V::FPGAState::from_rx).collect())
168    }
169}
170
171impl<L: Link, V: Driver> Controller<L, V> {
172    fn close_impl<S: Sleep, T: TimerStrategy<S>>(
173        &mut self,
174        option: SenderOption,
175        timer_strategy: T,
176    ) -> Result<(), AUTDDriverError> {
177        if !self.link.is_open() {
178            return Ok(());
179        }
180
181        self.sender(option, timer_strategy).close()
182    }
183}
184
185impl<'a, L: Link, V: Driver> IntoIterator for &'a Controller<L, V> {
186    type Item = &'a Device;
187    type IntoIter = std::slice::Iter<'a, Device>;
188
189    fn into_iter(self) -> Self::IntoIter {
190        self.geometry.iter()
191    }
192}
193
194impl<'a, L: Link, V: Driver> IntoIterator for &'a mut Controller<L, V> {
195    type Item = &'a mut Device;
196    type IntoIter = std::slice::IterMut<'a, Device>;
197
198    fn into_iter(self) -> Self::IntoIter {
199        self.geometry.iter_mut()
200    }
201}
202
203impl<L: Link + 'static, V: Driver> Controller<L, V> {
204    /// Converts `Controller<L>` into a `Controller<Box<dyn Link>>`.
205    pub fn into_boxed_link(self) -> Controller<Box<dyn Link>, V> {
206        let cnt = std::mem::ManuallyDrop::new(self);
207        let msg_id = unsafe { std::ptr::read(&cnt.msg_id) };
208        let driver = unsafe { std::ptr::read(&cnt.driver) };
209        let link = unsafe { std::ptr::read(&cnt.link) };
210        let geometry = unsafe { std::ptr::read(&cnt.geometry) };
211        let sent_flags = unsafe { std::ptr::read(&cnt.sent_flags) };
212        let rx_buf = unsafe { std::ptr::read(&cnt.rx_buf) };
213        let default_sender_option = unsafe { std::ptr::read(&cnt.default_sender_option) };
214        Controller {
215            msg_id,
216            driver,
217            link: Box::new(link) as _,
218            geometry,
219            sent_flags,
220            rx_buf,
221            default_sender_option,
222        }
223    }
224
225    /// Converts `Controller<Box<dyn Link>>` into a `Controller<L>`.
226    ///
227    /// # Safety
228    ///
229    /// This function must be used only when converting an instance created by [`Controller::into_boxed_link`] back to the original [`Controller`].
230    pub unsafe fn from_boxed_link(cnt: Controller<Box<dyn Link>, V>) -> Controller<L, V> {
231        let cnt = std::mem::ManuallyDrop::new(cnt);
232        let msg_id = unsafe { std::ptr::read(&cnt.msg_id) };
233        let driver = unsafe { std::ptr::read(&cnt.driver) };
234        let link = unsafe { std::ptr::read(&cnt.link) };
235        let geometry = unsafe { std::ptr::read(&cnt.geometry) };
236        let sent_flags = unsafe { std::ptr::read(&cnt.sent_flags) };
237        let rx_buf = unsafe { std::ptr::read(&cnt.rx_buf) };
238        let default_sender_option = unsafe { std::ptr::read(&cnt.default_sender_option) };
239        Controller {
240            msg_id,
241            driver,
242            link: unsafe { *Box::from_raw(Box::into_raw(link) as *mut L) },
243            geometry,
244            sent_flags,
245            rx_buf,
246            default_sender_option,
247        }
248    }
249}
250
251impl<L: Link, V: Driver> Drop for Controller<L, V> {
252    fn drop(&mut self) {
253        if !self.link.is_open() {
254            return;
255        }
256        let _ = self.close_impl(self.default_sender_option, FixedSchedule::default());
257    }
258}
259
260// The following implementations are necessary because Rust does not have associated traits.
261// https://github.com/rust-lang/rfcs/issues/2190
262
263impl<L: Link> Controller<L, firmware::v12::V12> {
264    /// Sends a data to the devices. This is a shortcut for [`Sender::send`].
265    ///
266    /// [`Sender::send`]: autd3_driver::firmware::v12::transmission::Sender::send
267    pub fn send<D: Datagram>(&mut self, s: D) -> Result<(), AUTDDriverError>
268    where
269        AUTDDriverError: From<D::Error>,
270        D::G: autd3_driver::firmware::v12::operation::OperationGenerator,
271        AUTDDriverError: From<<<D::G as autd3_driver::firmware::v12::operation::OperationGenerator>::O1 as autd3_driver::firmware::driver::Operation>::Error>
272            + From<<<D::G as autd3_driver::firmware::v12::operation::OperationGenerator>::O2 as autd3_driver::firmware::driver::Operation>::Error>,
273    {
274        self.sender(self.default_sender_option, FixedSchedule::default())
275            .send(s)
276    }
277}
278
279impl<L: Link> Controller<L, firmware::v11::V11> {
280    /// Sends a data to the devices. This is a shortcut for [`Sender::send`].
281    ///
282    /// [`Sender::send`]: autd3_driver::firmware::v11::transmission::Sender::send
283    pub fn send<D: Datagram>(&mut self, s: D) -> Result<(), AUTDDriverError>
284    where
285        AUTDDriverError: From<D::Error>,
286        D::G: autd3_driver::firmware::v11::operation::OperationGenerator,
287        AUTDDriverError: From<<<D::G as autd3_driver::firmware::v11::operation::OperationGenerator>::O1 as autd3_driver::firmware::driver::Operation>::Error>
288            + From<<<D::G as autd3_driver::firmware::v11::operation::OperationGenerator>::O2 as autd3_driver::firmware::driver::Operation>::Error>,
289    {
290        self.sender(self.default_sender_option, FixedSchedule::default())
291            .send(s)
292    }
293}
294
295impl<L: Link> Controller<L, firmware::v10::V10> {
296    /// Sends a data to the devices. This is a shortcut for [`Sender::send`].
297    ///
298    /// [`Sender::send`]: autd3_driver::firmware::v10::transmission::Sender::send
299    pub fn send<D: Datagram>(&mut self, s: D) -> Result<(), AUTDDriverError>
300    where
301        AUTDDriverError: From<D::Error>,
302        D::G: autd3_driver::firmware::v10::operation::OperationGenerator,
303        AUTDDriverError: From<<<D::G as autd3_driver::firmware::v10::operation::OperationGenerator>::O1 as autd3_driver::firmware::driver::Operation>::Error>
304            + From<<<D::G as autd3_driver::firmware::v10::operation::OperationGenerator>::O2 as autd3_driver::firmware::driver::Operation>::Error>,
305    {
306        self.sender(self.default_sender_option, FixedSchedule::default())
307            .send(s)
308    }
309}
310
311impl<L: Link> Controller<L, firmware::auto::Auto> {
312    /// Sends a data to the devices. This is a shortcut for [`Sender::send`].
313    ///
314    /// [`Sender::send`]: autd3_driver::firmware::auto::transmission::Sender::send
315    pub fn send<D: Datagram>(&mut self, s: D) -> Result<(), AUTDDriverError>
316    where
317        AUTDDriverError: From<D::Error>,
318        D::G: autd3_driver::firmware::auto::operation::OperationGenerator,
319        AUTDDriverError: From<<<D::G as autd3_driver::firmware::auto::operation::OperationGenerator>::O1 as autd3_driver::firmware::driver::Operation>::Error>
320            + From<<<D::G as autd3_driver::firmware::auto::operation::OperationGenerator>::O2 as autd3_driver::firmware::driver::Operation>::Error>,
321    {
322        self.sender(self.default_sender_option, FixedSchedule::default())
323            .send(s)
324    }
325}
326
327#[cfg(test)]
328pub(crate) mod tests {
329    use autd3_driver::firmware::driver::BoxedDatagram;
330
331    use crate::{
332        core::{
333            common::mm,
334            derive::*,
335            gain::{Gain, GainCalculator, GainCalculatorGenerator},
336            link::LinkError,
337        },
338        driver::{
339            autd3_device::AUTD3,
340            common::Hz,
341            datagram::{GainSTM, ReadsFPGAState},
342            firmware,
343            firmware::latest::Latest,
344        },
345        gain::Uniform,
346        link::{Audit, AuditOption, audit::version},
347        modulation::{Sine, Static},
348    };
349
350    use super::*;
351
352    pub fn create_controller(
353        dev_num: usize,
354    ) -> anyhow::Result<Controller<Audit<version::Latest>, Latest>> {
355        Ok(Controller::open_with(
356            (0..dev_num).map(|_| AUTD3::default()),
357            Audit::latest(AuditOption::default()),
358        )?)
359    }
360
361    #[test]
362    fn open_failed() {
363        assert_eq!(
364            Some(AUTDDriverError::Link(LinkError::new("broken"))),
365            Controller::<_, Latest>::open_with(
366                [AUTD3::default()],
367                Audit::latest(AuditOption {
368                    broken: true,
369                    ..Default::default()
370                }),
371            )
372            .err()
373        );
374    }
375
376    #[test]
377    fn send() -> anyhow::Result<()> {
378        let mut autd = create_controller(1)?;
379        autd.send((
380            Sine {
381                freq: 150. * Hz,
382                option: Default::default(),
383            },
384            GainSTM {
385                gains: vec![
386                    Uniform {
387                        intensity: Intensity(0x80),
388                        phase: Phase::ZERO,
389                    },
390                    Uniform {
391                        intensity: Intensity(0x81),
392                        phase: Phase::ZERO,
393                    },
394                ],
395                config: 1. * Hz,
396                option: Default::default(),
397            },
398        ))?;
399
400        autd.iter().try_for_each(|dev| {
401            assert_eq!(
402                *Sine {
403                    freq: 150. * Hz,
404                    option: Default::default(),
405                }
406                .calc(&Latest.firmware_limits())?,
407                autd.link[dev.idx()].fpga().modulation_buffer(Segment::S0)
408            );
409            let f = Uniform {
410                intensity: Intensity(0x80),
411                phase: Phase::ZERO,
412            }
413            .init(&autd.geometry, &TransducerFilter::all_enabled())?
414            .generate(dev);
415            assert_eq!(
416                dev.iter().map(|tr| f.calc(tr)).collect::<Vec<_>>(),
417                autd.link[dev.idx()].fpga().drives_at(Segment::S0, 0)
418            );
419            let f = Uniform {
420                intensity: Intensity(0x81),
421                phase: Phase::ZERO,
422            }
423            .init(&autd.geometry, &TransducerFilter::all_enabled())?
424            .generate(dev);
425            assert_eq!(
426                dev.iter().map(|tr| f.calc(tr)).collect::<Vec<_>>(),
427                autd.link[dev.idx()].fpga().drives_at(Segment::S0, 1)
428            );
429            anyhow::Ok(())
430        })?;
431
432        autd.close()?;
433
434        Ok(())
435    }
436
437    #[test]
438    fn inspect() -> anyhow::Result<()> {
439        let autd = create_controller(2)?;
440
441        let r = autd.inspect(autd3_driver::datagram::Group::new(
442            |dev| (dev.idx() == 0).then_some(()),
443            HashMap::from([((), Static::default())]),
444        ))?;
445        assert_eq!(autd.geometry.len(), r.len());
446        assert_eq!(
447            Some(ModulationInspectionResult {
448                name: "Static".to_string(),
449                data: vec![0xFF, 0xFF],
450                config: Static::default().sampling_config(),
451                loop_behavior: LoopBehavior::Infinite,
452                segment: Segment::S0,
453                transition_mode: None
454            }),
455            r[0]
456        );
457        assert_eq!(None, r[1]);
458
459        autd.close()?;
460
461        Ok(())
462    }
463
464    #[test]
465    fn firmware_version() -> anyhow::Result<()> {
466        use autd3_driver::firmware::version::{CPUVersion, FPGAVersion};
467
468        let mut autd = create_controller(1)?;
469        assert_eq!(
470            vec![FirmwareVersion {
471                idx: 0,
472                cpu: CPUVersion {
473                    major: FirmwareVersion::LATEST_VERSION_NUM_MAJOR,
474                    minor: FirmwareVersion::LATEST_VERSION_NUM_MINOR
475                },
476                fpga: FPGAVersion {
477                    major: FirmwareVersion::LATEST_VERSION_NUM_MAJOR,
478                    minor: FirmwareVersion::LATEST_VERSION_NUM_MINOR,
479                    function_bits: FPGAVersion::ENABLED_EMULATOR_BIT
480                }
481            }],
482            autd.firmware_version()?
483        );
484        Ok(())
485    }
486
487    #[test]
488    fn firmware_version_err() -> anyhow::Result<()> {
489        let mut autd = create_controller(2)?;
490        autd.link_mut().break_down();
491        assert_eq!(
492            Err(AUTDDriverError::ReadFirmwareVersionFailed(vec![
493                false, false
494            ])),
495            autd.firmware_version()
496        );
497        Ok(())
498    }
499
500    #[test]
501    fn close() -> anyhow::Result<()> {
502        {
503            let mut autd = create_controller(1)?;
504            autd.close_impl(SenderOption::default(), FixedSchedule::default())?;
505            autd.close()?;
506        }
507
508        {
509            let mut autd = create_controller(1)?;
510            autd.link_mut().break_down();
511            assert_eq!(
512                Err(AUTDDriverError::Link(LinkError::new("broken"))),
513                autd.close()
514            );
515        }
516
517        Ok(())
518    }
519
520    #[test]
521    fn fpga_state() -> anyhow::Result<()> {
522        let mut autd = Controller::<_, Latest>::open_with(
523            [AUTD3::default(), AUTD3::default()],
524            Audit::latest(AuditOption::default()),
525        )?;
526
527        autd.send(ReadsFPGAState::new(|_| true))?;
528        {
529            autd.link_mut()[0].fpga_mut().assert_thermal_sensor();
530
531            let states = autd.fpga_state()?;
532            assert_eq!(2, states.len());
533            assert!(
534                states[0]
535                    .ok_or(anyhow::anyhow!("state shouldn't be None here"))?
536                    .is_thermal_assert()
537            );
538            assert!(
539                !states[1]
540                    .ok_or(anyhow::anyhow!("state shouldn't be None here"))?
541                    .is_thermal_assert()
542            );
543        }
544
545        {
546            autd.link_mut()[0].fpga_mut().deassert_thermal_sensor();
547            autd.link_mut()[1].fpga_mut().assert_thermal_sensor();
548
549            let states = autd.fpga_state()?;
550            assert_eq!(2, states.len());
551            assert!(
552                !states[0]
553                    .ok_or(anyhow::anyhow!("state shouldn't be None here"))?
554                    .is_thermal_assert()
555            );
556            assert!(
557                states[1]
558                    .ok_or(anyhow::anyhow!("state shouldn't be None here"))?
559                    .is_thermal_assert()
560            );
561        }
562
563        autd.send(ReadsFPGAState::new(|dev| dev.idx() == 1))?;
564        {
565            let states = autd.fpga_state()?;
566            assert_eq!(2, states.len());
567            assert!(states[0].is_none());
568            assert!(
569                states[1]
570                    .ok_or(anyhow::anyhow!("state shouldn't be None here"))?
571                    .is_thermal_assert()
572            );
573        }
574
575        Ok(())
576    }
577
578    #[test]
579    fn into_iter() -> anyhow::Result<()> {
580        let mut autd = create_controller(1)?;
581
582        for dev in &mut autd {
583            dev.sound_speed = 300e3 * mm;
584        }
585
586        for dev in &autd {
587            assert_eq!(300e3 * mm, dev.sound_speed);
588        }
589
590        Ok(())
591    }
592
593    #[test]
594    fn with_boxed_link() -> anyhow::Result<()> {
595        let link: Box<dyn Link> = Box::new(Audit::latest(AuditOption::default()));
596        let mut autd = Controller::<_, Latest>::open_with([AUTD3::default()], link)?;
597
598        autd.send(Sine {
599            freq: 150. * Hz,
600            option: Default::default(),
601        })?;
602
603        autd.close()?;
604
605        Ok(())
606    }
607
608    #[test]
609    fn into_boxed_link_unsafe() -> anyhow::Result<()> {
610        let autd = Controller::<_, Latest>::open_with_option(
611            [AUTD3::default()],
612            Audit::latest(AuditOption::default()),
613            SenderOption::default(),
614            FixedSchedule::default(),
615        )?;
616
617        let mut autd = autd.into_boxed_link();
618
619        autd.send((
620            Sine {
621                freq: 150. * Hz,
622                option: Default::default(),
623            },
624            GainSTM {
625                gains: vec![
626                    Uniform {
627                        intensity: Intensity(0x80),
628                        phase: Phase::ZERO,
629                    },
630                    Uniform {
631                        intensity: Intensity(0x81),
632                        phase: Phase::ZERO,
633                    },
634                ],
635                config: 1. * Hz,
636                option: Default::default(),
637            },
638        ))?;
639
640        let autd = unsafe { Controller::<Audit<version::Latest>, _>::from_boxed_link(autd) };
641
642        autd.iter().try_for_each(|dev| {
643            assert_eq!(
644                *Sine {
645                    freq: 150. * Hz,
646                    option: Default::default(),
647                }
648                .calc(&Latest.firmware_limits())?,
649                autd.link[dev.idx()].fpga().modulation_buffer(Segment::S0)
650            );
651            let f = Uniform {
652                intensity: Intensity(0x80),
653                phase: Phase::ZERO,
654            }
655            .init(&autd.geometry, &TransducerFilter::all_enabled())?
656            .generate(dev);
657            assert_eq!(
658                dev.iter().map(|tr| f.calc(tr)).collect::<Vec<_>>(),
659                autd.link[dev.idx()].fpga().drives_at(Segment::S0, 0)
660            );
661            let f = Uniform {
662                intensity: Intensity(0x81),
663                phase: Phase::ZERO,
664            }
665            .init(&autd.geometry, &TransducerFilter::all_enabled())?
666            .generate(dev);
667            assert_eq!(
668                dev.iter().map(|tr| f.calc(tr)).collect::<Vec<_>>(),
669                autd.link[dev.idx()].fpga().drives_at(Segment::S0, 1)
670            );
671            anyhow::Ok(())
672        })?;
673
674        autd.close()?;
675
676        Ok(())
677    }
678
679    #[test]
680    fn into_boxed_link_close() -> anyhow::Result<()> {
681        let autd = create_controller(1)?;
682        let autd = autd.into_boxed_link();
683
684        autd.close()?;
685
686        Ok(())
687    }
688
689    #[test]
690    fn send_boxed() -> anyhow::Result<()> {
691        use crate::gain::Null;
692
693        {
694            let mut autd = Controller::<_, firmware::v12::V12>::open_with(
695                [AUTD3::default()],
696                Audit::<version::V12>::new(AuditOption::default()),
697            )?;
698
699            autd.send(BoxedDatagram::new(Null))?;
700
701            autd.close()?;
702        }
703
704        {
705            let mut autd = Controller::<_, firmware::v11::V11>::open_with(
706                [AUTD3::default()],
707                Audit::<version::V11>::new(AuditOption::default()),
708            )?;
709
710            autd.send(BoxedDatagram::new(Null))?;
711
712            autd.close()?;
713        }
714
715        {
716            let mut autd = Controller::<_, firmware::v10::V10>::open_with(
717                [AUTD3::default()],
718                Audit::<version::V10>::new(AuditOption::default()),
719            )?;
720
721            autd.send(BoxedDatagram::new(Null))?;
722
723            autd.close()?;
724        }
725
726        {
727            let mut autd = Controller::<_, firmware::auto::Auto>::open_with(
728                [AUTD3::default()],
729                Audit::<version::Latest>::new(AuditOption::default()),
730            )?;
731
732            autd.send(BoxedDatagram::new(Null))?;
733
734            autd.close()?;
735        }
736
737        Ok(())
738    }
739}