1use grafton_visca_macros::ViscaEnum;
14
15use crate::{
16 command::encode::ViscaCommand,
17 error::Error,
18 timeout::CommandCategory,
19 types::{FocusPosition, SpeedLevel},
20 visca_command,
21};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, ViscaEnum)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
27#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
29pub enum FocusMode {
30 Auto = 0x02,
32 Manual = 0x03,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, ViscaEnum)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
40#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
41#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
42pub enum FocusRange {
43 Normal = 0x00,
45 Range10x = 0x01,
47 Range4_3x = 0x02,
49 Range2_1x = 0x03,
51 Range1x = 0x04,
53 Range0_35x = 0x05,
55}
56
57crate::visca_range_type! {
58 FocusSpeed: u8 {
62 min: 0,
63 max: 7
64 }
65}
66
67impl From<SpeedLevel> for FocusSpeed {
68 fn from(level: SpeedLevel) -> Self {
69 Self(level.to_focus_speed())
70 }
71}
72
73#[derive(Debug, Copy, Clone)]
77pub enum Focus {
78 Stop,
80 Far,
82 Near,
84 FarWithSpeed(FocusSpeed),
86 NearWithSpeed(FocusSpeed),
88 Position(FocusPosition),
90 Auto,
92 Manual,
94 OnePushTrigger,
96 Infinity,
98 Toggle,
102 Snap,
109}
110
111impl ViscaCommand for Focus {
112 const MAX_SIZE: usize = 9;
113 const TIMEOUT_CATEGORY: CommandCategory = CommandCategory::Movement;
114
115 fn write_into(
116 &self,
117 camera_id: crate::camera_id::CameraId,
118 buffer: &mut [u8],
119 ) -> Result<usize, Error> {
120 use crate::command::bytes::{constants, ConstCommandBuilder};
121
122 match self {
123 Self::Stop | Self::Far | Self::Near => {
124 if matches!(self, Self::Stop) {
126 let builder = ConstCommandBuilder::<6>::new()
128 .append(constants::focus::MOVEMENT_PREFIX)
129 .push(0x00)
130 .with_camera_id(camera_id)
131 .terminate();
132
133 builder.build_into(buffer)
135 } else {
136 let mut builder = ConstCommandBuilder::<6>::new();
138 builder.append_mut(constants::focus::MOVEMENT_PREFIX);
139 builder.push_mut(match self {
140 Self::Far => 0x02,
141 Self::Near => 0x03,
142 _ => unreachable!(),
143 });
144 builder.with_camera_id_mut(camera_id);
145 builder.terminate().build_into(buffer)
146 }
147 }
148 Self::FarWithSpeed(_) | Self::NearWithSpeed(_) => {
149 let mut builder = ConstCommandBuilder::<6>::new();
150 builder.append_mut(constants::focus::MOVEMENT_PREFIX);
151 builder.push_mut(match self {
152 Self::FarWithSpeed(s) => 0x20 | s.value(),
153 Self::NearWithSpeed(s) => 0x30 | s.value(),
154 _ => unreachable!(),
155 });
156 builder.with_camera_id_mut(camera_id);
157 builder.terminate().build_into(buffer)
158 }
159 Self::Position(position) => {
160 let builder = ConstCommandBuilder::<9>::new()
161 .append(constants::focus::POSITION_PREFIX)
162 .push_visca_u16(position.value())
163 .with_camera_id(camera_id)
164 .terminate();
165 builder.build_into(buffer)
166 }
167 Self::Auto | Self::Manual | Self::Toggle | Self::Snap => {
168 let mut builder = ConstCommandBuilder::<6>::new();
169 builder.append_mut(constants::focus::MODE_PREFIX);
170 builder.push_mut(match self {
171 Self::Auto => 0x02,
172 Self::Manual => 0x03,
173 Self::Snap => 0x04,
174 Self::Toggle => 0x10,
175 _ => unreachable!(),
176 });
177 builder.with_camera_id_mut(camera_id);
178 builder.terminate().build_into(buffer)
179 }
180 Self::OnePushTrigger | Self::Infinity => {
181 let mut builder = ConstCommandBuilder::<6>::new();
182 builder.append_mut(constants::focus::ONE_PUSH_PREFIX);
183 builder.push_mut(match self {
184 Self::OnePushTrigger => 0x01,
185 Self::Infinity => 0x02,
186 _ => unreachable!(),
187 });
188 builder.with_camera_id_mut(camera_id);
189 builder.terminate().build_into(buffer)
190 }
191 }
192 }
193}
194
195#[derive(Debug, Copy, Clone, PartialEq, Eq, ViscaEnum)]
199#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
200#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
201#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
202#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
203pub enum FocusZone {
204 Top = 0x00,
206 Center = 0x01,
208 Bottom = 0x02,
210}
211
212visca_command! {
213 pub struct FocusZoneCommand {
215 zone: FocusZone,
216 };
217 prefix = [0x01, 0x04, 0xAA];
218 param = match *zone {
219 FocusZone::Top => 0x00u8,
220 FocusZone::Center => 0x01u8,
221 FocusZone::Bottom => 0x02u8,
222 };
223 max_param_size = 1;
224 category = CommandCategory::Quick;
225}
226
227impl FocusZoneCommand {
228 pub fn new(zone: FocusZone) -> Self {
230 Self { zone }
231 }
232}
233
234#[derive(Debug, Copy, Clone, PartialEq, Eq, ViscaEnum)]
238#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
239#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
240#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
241#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
242pub enum AutoFocusSensitivity {
243 Low = 0x00,
245 Normal = 0x01,
247 High = 0x02,
249}
250
251visca_command! {
252 pub struct AutoFocusSensitivityCommand {
254 sensitivity: AutoFocusSensitivity,
255 };
256 prefix = [0x01, 0x04, 0x58];
257 param = match *sensitivity {
258 AutoFocusSensitivity::High => 0x02u8,
259 AutoFocusSensitivity::Normal => 0x01u8,
260 AutoFocusSensitivity::Low => 0x00u8,
261 };
262 max_param_size = 1;
263 category = CommandCategory::Quick;
264}
265
266impl AutoFocusSensitivityCommand {
267 pub fn new(sensitivity: AutoFocusSensitivity) -> Self {
269 Self { sensitivity }
270 }
271}
272
273visca_command! {
274 pub struct FocusNearLimitCommand {
279 position: FocusPosition,
280 };
281 prefix = [0x01, 0x04, 0x28];
282 param = {
283 let value = position.value();
284 [
285 ((value >> 12) & 0x0F) as u8,
286 ((value >> 8) & 0x0F) as u8,
287 ((value >> 4) & 0x0F) as u8,
288 (value & 0x0F) as u8,
289 ]
290 };
291 max_param_size = 4;
292 category = CommandCategory::Quick;
293}
294
295impl FocusNearLimitCommand {
296 pub fn new(position: FocusPosition) -> Self {
298 Self { position }
299 }
300}
301
302#[derive(Debug, Copy, Clone)]
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
310#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
311#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
312pub enum FocusLock {
313 On,
315 Off,
317}
318
319impl ViscaCommand for FocusLock {
320 const MAX_SIZE: usize = 6;
321 const TIMEOUT_CATEGORY: CommandCategory = CommandCategory::Quick;
322
323 fn write_into(
324 &self,
325 camera_id: crate::camera_id::CameraId,
326 buffer: &mut [u8],
327 ) -> Result<usize, Error> {
328 use crate::command::bytes::ConstCommandBuilder;
329
330 let builder = match self {
331 Self::On => ConstCommandBuilder::<6>::new()
332 .append(crate::command::bytes::constants::focus::LOCK_PREFIX)
333 .push(0x02),
334 Self::Off => ConstCommandBuilder::<6>::new()
335 .append(crate::command::bytes::constants::focus::LOCK_PREFIX)
336 .push(0x03),
337 };
338
339 builder
340 .with_camera_id(camera_id)
341 .terminate()
342 .build_into(buffer)
343 }
344}
345
346#[derive(Debug, Copy, Clone)]
353#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
354#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
355#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
356#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
357pub enum PushAF {
358 Press,
360 Release,
362}
363
364impl ViscaCommand for PushAF {
365 const MAX_SIZE: usize = 8;
366 const TIMEOUT_CATEGORY: CommandCategory = CommandCategory::Quick;
367
368 fn write_into(
369 &self,
370 camera_id: crate::camera_id::CameraId,
371 buffer: &mut [u8],
372 ) -> Result<usize, Error> {
373 use crate::command::bytes::{constants, ConstCommandBuilder};
374
375 let mut builder = ConstCommandBuilder::<8>::new();
376 builder.append_mut(constants::focus::PUSH_AF_PREFIX);
377 builder.push_mut(match self {
378 Self::Press => 0x01,
379 Self::Release => 0x00,
380 });
381 builder
382 .with_camera_id(camera_id)
383 .terminate()
384 .build_into(buffer)
385 }
386}
387
388#[cfg(test)]
389#[allow(clippy::panic)]
390mod tests {
391 use super::*;
392 use crate::command::bytes::VISCA_TERMINATOR;
393 use crate::command::encode::ViscaCommand;
394 use crate::macros::test_utils::visca_test;
395 use crate::timeout::CommandTimeout;
396
397 visca_test!(
398 Focus,
399 test_focus_command_stop,
400 Focus::Stop,
401 &[0x81, 0x01, 0x04, 0x08, 0x00, VISCA_TERMINATOR]
402 );
403
404 visca_test!(
405 Focus,
406 test_focus_command_far_standard,
407 Focus::Far,
408 &[0x81, 0x01, 0x04, 0x08, 0x02, VISCA_TERMINATOR]
409 );
410
411 visca_test!(
412 Focus,
413 test_focus_command_near_standard,
414 Focus::Near,
415 &[0x81, 0x01, 0x04, 0x08, 0x03, VISCA_TERMINATOR]
416 );
417
418 #[test]
419 fn test_focus_command_far_variable() {
420 for speed_val in 0..=7 {
422 let speed = FocusSpeed::new(speed_val)
423 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}"));
424 let cmd = Focus::FarWithSpeed(speed);
425 assert_eq!(
426 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
427 .map(|b| b.to_vec())
428 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
429 vec![0x81, 0x01, 0x04, 0x08, 0x20 | speed_val, VISCA_TERMINATOR]
430 );
431 }
432 }
433
434 #[test]
435 fn test_focus_command_near_variable() {
436 for speed_val in 0..=7 {
438 let speed = FocusSpeed::new(speed_val)
439 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}"));
440 let cmd = Focus::NearWithSpeed(speed);
441 assert_eq!(
442 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
443 .map(|b| b.to_vec())
444 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
445 vec![0x81, 0x01, 0x04, 0x08, 0x30 | speed_val, VISCA_TERMINATOR]
446 );
447 }
448 }
449
450 #[test]
451 fn test_focus_speed_validation() {
452 assert!(FocusSpeed::new(0).is_ok());
454 assert!(FocusSpeed::new(7).is_ok());
455
456 assert!(matches!(
458 FocusSpeed::new(8),
459 Err(Error::InvalidParameter { .. })
460 ));
461 }
462
463 #[test]
464 fn test_focus_command_position() {
465 let cmd = Focus::Position(FocusPosition::new(0x1234));
466 assert_eq!(
467 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
468 .map(|b| b.to_vec())
469 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
470 vec![
471 0x81,
472 0x01,
473 0x04,
474 0x48,
475 0x01,
476 0x02,
477 0x03,
478 0x04,
479 VISCA_TERMINATOR
480 ]
481 );
482
483 let cmd = Focus::Position(FocusPosition::new(0xF000));
484 assert_eq!(
485 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
486 .map(|b| b.to_vec())
487 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
488 vec![
489 0x81,
490 0x01,
491 0x04,
492 0x48,
493 0x0F,
494 0x00,
495 0x00,
496 0x00,
497 VISCA_TERMINATOR
498 ]
499 );
500 }
501
502 visca_test!(
503 Focus,
504 test_focus_command_auto,
505 Focus::Auto,
506 &[0x81, 0x01, 0x04, 0x38, 0x02, VISCA_TERMINATOR]
507 );
508
509 visca_test!(
510 Focus,
511 test_focus_command_manual,
512 Focus::Manual,
513 &[0x81, 0x01, 0x04, 0x38, 0x03, VISCA_TERMINATOR]
514 );
515
516 visca_test!(
517 Focus,
518 test_focus_command_toggle,
519 Focus::Toggle,
520 &[0x81, 0x01, 0x04, 0x38, 0x10, VISCA_TERMINATOR]
521 );
522
523 visca_test!(
524 Focus,
525 test_focus_command_snap,
526 Focus::Snap,
527 &[0x81, 0x01, 0x04, 0x38, 0x04, VISCA_TERMINATOR]
528 );
529
530 visca_test!(
531 Focus,
532 test_focus_command_one_push_trigger,
533 Focus::OnePushTrigger,
534 &[0x81, 0x01, 0x04, 0x18, 0x01, VISCA_TERMINATOR]
535 );
536
537 visca_test!(
538 Focus,
539 test_focus_command_infinity,
540 Focus::Infinity,
541 &[0x81, 0x01, 0x04, 0x18, 0x02, VISCA_TERMINATOR]
542 );
543
544 #[test]
545 fn test_focus_zone_command() {
546 let cmd = FocusZoneCommand {
547 zone: FocusZone::Top,
548 };
549 assert_eq!(
550 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
551 .map(|b| b.to_vec())
552 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
553 vec![0x81, 0x01, 0x04, 0xAA, 0x00, VISCA_TERMINATOR]
554 );
555
556 let cmd = FocusZoneCommand {
557 zone: FocusZone::Center,
558 };
559 assert_eq!(
560 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
561 .map(|b| b.to_vec())
562 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
563 vec![0x81, 0x01, 0x04, 0xAA, 0x01, VISCA_TERMINATOR]
564 );
565
566 let cmd = FocusZoneCommand {
567 zone: FocusZone::Bottom,
568 };
569 assert_eq!(
570 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
571 .map(|b| b.to_vec())
572 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
573 vec![0x81, 0x01, 0x04, 0xAA, 0x02, VISCA_TERMINATOR]
574 );
575 }
576
577 #[test]
578 fn test_auto_focus_sensitivity_command() {
579 let cmd = AutoFocusSensitivityCommand {
580 sensitivity: AutoFocusSensitivity::High,
581 };
582 assert_eq!(
583 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
584 .map(|b| b.to_vec())
585 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
586 vec![0x81, 0x01, 0x04, 0x58, 0x02, VISCA_TERMINATOR]
587 );
588
589 let cmd = AutoFocusSensitivityCommand {
590 sensitivity: AutoFocusSensitivity::Normal,
591 };
592 assert_eq!(
593 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
594 .map(|b| b.to_vec())
595 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
596 vec![0x81, 0x01, 0x04, 0x58, 0x01, VISCA_TERMINATOR]
597 );
598
599 let cmd = AutoFocusSensitivityCommand {
600 sensitivity: AutoFocusSensitivity::Low,
601 };
602 assert_eq!(
603 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
604 .map(|b| b.to_vec())
605 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
606 vec![0x81, 0x01, 0x04, 0x58, 0x00, VISCA_TERMINATOR]
607 );
608 }
609
610 #[test]
611 fn test_focus_near_limit_command() {
612 let cmd = FocusNearLimitCommand {
613 position: FocusPosition::new(0x1234),
614 };
615 assert_eq!(
616 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
617 .map(|b| b.to_vec())
618 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
619 vec![
620 0x81,
621 0x01,
622 0x04,
623 0x28,
624 0x01,
625 0x02,
626 0x03,
627 0x04,
628 VISCA_TERMINATOR
629 ]
630 );
631
632 let cmd = FocusNearLimitCommand {
633 position: FocusPosition::new(0x1000),
634 };
635 assert_eq!(
636 cmd.to_bytes(crate::camera_id::CameraId::CAMERA_1)
637 .map(|b| b.to_vec())
638 .unwrap_or_else(|e| panic!("Test assertion failed: {e:?}")),
639 vec![
640 0x81,
641 0x01,
642 0x04,
643 0x28,
644 0x01,
645 0x00,
646 0x00,
647 0x00,
648 VISCA_TERMINATOR
649 ]
650 );
651 }
652
653 #[test]
654 fn test_command_categories() {
655 assert_eq!(Focus::Stop.timeout_class(), CommandCategory::Movement);
656 assert_eq!(Focus::Auto.timeout_class(), CommandCategory::Movement);
657 assert_eq!(
658 FocusZoneCommand {
659 zone: FocusZone::Top
660 }
661 .timeout_class(),
662 CommandCategory::Quick
663 );
664 assert_eq!(
665 AutoFocusSensitivityCommand {
666 sensitivity: AutoFocusSensitivity::High
667 }
668 .timeout_class(),
669 CommandCategory::Quick
670 );
671 assert_eq!(
672 FocusNearLimitCommand {
673 position: FocusPosition::new(0x1000)
674 }
675 .timeout_class(),
676 CommandCategory::Quick
677 );
678 }
679}