Skip to main content

viva_camctl/
cmd_xml.rs

1//! `viva-camctl xml` — dump a camera's GenApi XML.
2//!
3//! Every camera-specific bug found so far was diagnosed from the reporter's
4//! own XML, and until now the library offered no supported way to produce one:
5//! the fetch existed but only behind [`common::open_camera`], which builds a
6//! nodemap first. That is exactly the step that fails on the cameras whose XML
7//! we most need — the reporter of issue #45 was told camctl could dump it,
8//! could not, and had to supply four other models instead.
9//!
10//! So this command stops at the fetch. Nothing is parsed, so nothing about the
11//! document's contents can make it fail.
12
13use std::net::Ipv4Addr;
14use std::path::PathBuf;
15use std::sync::Arc;
16use std::time::Duration;
17
18use anyhow::{Context, Result};
19use tokio::sync::Mutex;
20use tracing::info;
21
22use viva_gige::nic::IfaceSelector;
23
24use crate::common::{self, DEFAULT_DISCOVERY_TIMEOUT_MS};
25
26pub struct XmlArgs {
27    pub ip: Option<Ipv4Addr>,
28    pub index: Option<usize>,
29    pub iface: Option<IfaceSelector>,
30    pub out: Option<PathBuf>,
31}
32
33pub async fn run(args: XmlArgs) -> Result<()> {
34    let XmlArgs {
35        ip,
36        index,
37        iface,
38        out,
39    } = args;
40    let timeout = Duration::from_millis(DEFAULT_DISCOVERY_TIMEOUT_MS);
41    let device = common::select_device(ip, index, iface.as_ref(), timeout).await?;
42    info!(ip = %device.ip, "fetching GenApi XML");
43
44    let control = Arc::new(Mutex::new(common::open_control(&device).await?));
45    let xml = common::fetch_xml(control).await?;
46
47    match out {
48        Some(path) => {
49            std::fs::write(&path, xml.as_bytes())
50                .with_context(|| format!("write {}", path.display()))?;
51            // On stderr so `--out /dev/stdout` still yields a clean document.
52            eprintln!("wrote {} bytes to {}", xml.len(), path.display());
53        }
54        None => print!("{xml}"),
55    }
56    Ok(())
57}