Skip to main content

viva_camctl/
cmd_events.rs

1use std::net::Ipv4Addr;
2use std::time::Duration;
3
4use anyhow::{Context, Result, anyhow};
5use serde::Serialize;
6use tracing::{info, warn};
7
8use viva_gige::nic::IfaceSelector;
9
10use crate::common::{self, DEFAULT_DISCOVERY_TIMEOUT_MS};
11
12#[derive(Serialize)]
13struct EventRecord {
14    index: usize,
15    id: u16,
16    ts_dev: u64,
17    ts_host: String,
18    payload_len: usize,
19}
20
21fn parse_events(csv: &str) -> Vec<String> {
22    csv.split(',')
23        .map(|entry| entry.trim())
24        .filter(|entry| !entry.is_empty())
25        .map(|entry| entry.to_string())
26        .collect()
27}
28
29pub async fn run(
30    ip: Option<Ipv4Addr>,
31    index: Option<usize>,
32    iface: Option<IfaceSelector>,
33    port: u16,
34    enable: String,
35    count: u32,
36    json: bool,
37) -> Result<()> {
38    let timeout = Duration::from_millis(DEFAULT_DISCOVERY_TIMEOUT_MS);
39    let device = common::select_device(ip, index, iface.as_ref(), timeout).await?;
40
41    // The camera sends events *to* this address, so it must be the host's, not
42    // the camera's. With no --iface, probe which local interface routes there
43    // rather than refusing to run (backlog DX-08). `--iface` no longer *is*
44    // the address — it may name the interface instead — so it is resolved here
45    // rather than used verbatim (backlog DX-10).
46    let local_ip = common::resolve_receive_iface(iface.as_ref(), device.ip)?
47        .ipv4()
48        .ok_or_else(|| anyhow!("interface routing to {} has no IPv4 address", device.ip))?;
49    info!(ip = %device.ip, %local_ip, port, "configuring events");
50    let mut camera = common::open_camera(&device)
51        .await
52        .context("open camera for events")?;
53
54    let enable_list = parse_events(&enable);
55    let enable_refs: Vec<&str> = enable_list.iter().map(|s| s.as_str()).collect();
56    camera
57        .configure_events(local_ip, port, &enable_refs)
58        .await
59        .context("configure event channel")?;
60    let stream = camera
61        .open_event_stream(local_ip, port)
62        .await
63        .context("open event stream")?;
64    if let Ok(addr) = stream.local_addr() {
65        info!(local = %addr, "listening for events");
66    }
67
68    let mut records = Vec::new();
69    for idx in 0..usize::try_from(count).unwrap_or(0) {
70        match stream.next().await {
71            Ok(event) => {
72                let ts_host = common::format_system_time(event.ts_host)
73                    .unwrap_or_else(|_| "unknown".to_string());
74                if json {
75                    records.push(EventRecord {
76                        index: idx + 1,
77                        id: event.id,
78                        ts_dev: event.ts_dev,
79                        ts_host,
80                        payload_len: event.data.len(),
81                    });
82                } else {
83                    println!(
84                        "#{:02} host={} id=0x{:04X} ticks={} payload={} bytes",
85                        idx + 1,
86                        ts_host,
87                        event.id,
88                        event.ts_dev,
89                        event.data.len()
90                    );
91                }
92            }
93            Err(err) => {
94                warn!(error = %err, "failed to receive event");
95                break;
96            }
97        }
98    }
99
100    if json {
101        common::print_json(&records)?;
102    }
103
104    Ok(())
105}