GenApi XML
Goal of this tutorial:
- Understand what the GenICam XML is and where it lives.
- See how
viva-genapi-xmlfetches it from the device and parses it. - Fetch it yourself, from the CLI and from Rust.
- Know when you actually need to look at it.
You should already have worked through Discovery and Registers & features.
1. What is the GenICam XML?
Every GenICam-compliant device carries a self-description document:
- It lists every feature the device supports — name, type, access mode, range.
- It defines how those features map to device registers.
- It encodes categories, selectors, and SwissKnife expressions.
- It declares which GenApi schema version the document uses.
The document normally lives in the device’s non-volatile memory. To get it, the host:
- Reads the
GevFirstURLregister at0x0200. - Interprets the result as a URL saying where the document actually is —
usually
local:plus a memory address and length, in principle alsohttp://orfile://. - Reads those bytes.
- Hands the string to a GenApi implementation.
Two details that the specification permits and real devices use:
- If
GevFirstURLis empty or its document cannot be retrieved,GevSecondURLat0x0400is tried next. - The document is often ZIP-compressed.
viva-genapi-xmldecompresses it transparently, subject to a 64 MiB cap so a malformed length field cannot exhaust memory.
2. The shape of the API
viva-genapi-xml exposes three things you are likely to call:
// Follow the URL registers and return the document.
pub async fn fetch_and_load_xml<F, Fut>(read_mem: F) -> Result<String, XmlError>
where
F: FnMut(u64, usize) -> Fut,
Fut: Future<Output = Result<Vec<u8>, XmlError>>;
// Cheap, deliberately lossy: schema version and top-level names.
pub fn parse_into_minimal_nodes(xml: &str) -> Result<MinimalXmlInfo, XmlError>;
// The full parse: every node declaration, plus the ones that had to be skipped.
pub fn parse(xml: &str) -> Result<XmlModel, XmlError>;
Application code rarely calls these directly — connect_gige does it for you.
They matter when you are debugging why a feature behaves as it does, inspecting
how a vendor encoded something, or adding support for a construct
viva-genapi does not handle yet.
3. Getting the XML
3.1. From the command line
cargo run -p viva-camctl -- xml --ip 192.168.0.10 --out camera.xml
This stops before the nodemap is built, so it works on a camera the library cannot open — which is the only camera anyone ever needs it for. If you are reporting a problem, send this: see Reporting a camera we can’t open.
3.2. From Rust
fetch_and_load_xml knows nothing about GVCP, sockets or cameras. It calls a
closure with (address, length) and expects bytes back, so any transport that
can read device memory can drive it:
// `fetch_and_load_xml` knows nothing about GVCP, sockets or cameras. It
// asks for `(address, length)` and expects bytes back, so any transport
// that can read device memory can supply the closure.
let xml = {
let cam = Arc::clone(&camera);
viva_genapi_xml::fetch_and_load_xml(move |address, length| {
let cam = Arc::clone(&cam);
async move {
let mut guard = cam.lock().await;
guard
.read_mem(address, length)
.await
.map_err(|err| XmlError::Transport(err.to_string()))
}
})
.await?
};
Run it with:
cargo run -p viva-genicam --example fetch_xml
4. Inspecting the document
parse_into_minimal_nodes answers the cheap questions — which schema version,
what is at the top level, does this look broken at all:
// A deliberately lossy parse: enough to answer "which schema is this, and
// what is at the top level", robust to node types we do not yet handle.
let meta = viva_genapi_xml::parse_into_minimal_nodes(&xml)?;
if let Some(version) = meta.schema_version.as_deref() {
println!("Schema version: {version}");
}
println!("Top level features ({}):", meta.top_level_features.len());
for feature in meta.top_level_features.iter().take(8) {
println!(" - {feature}");
}
It is intentionally lossy. It does not understand every node type; its job is to be fast and to survive schema extensions that are not implemented yet.
parse is the full path, and it is what NodeMap is built from. It returns an
XmlModel with a flat list of node declarations carrying:
- Feature name and type (Integer, Float, Enumeration, Boolean, Command, Category, SwissKnife, Converter, …).
- Addressing: fixed, selector-based, or indirect through
pAddress. - Access mode, bitfield layout and byte order.
- Selector relationships and expression text.
Skipped nodes
A construct the parser cannot handle no longer fails the whole document — it
goes into XmlModel::skipped, and the corresponding GenApi-level list is
NodeMap::skipped(). Both are logged.
This matters because a single unhandled construct used to make a camera unopenable — that is exactly what #35 and #45 were. Degrading to “this one feature is missing” is far better than “this camera does not work”, and the corpus tests fail on any skip that is not on their allowlist, so new gaps surface rather than accumulate.
5. From XML to a NodeMap
viva-genapi takes the XmlModel and:
- Instantiates a
NodeMap. - Resolves feature dependencies,
pValuedelegation, selectors and expressions at access time rather than at load time. - Invalidates cached values when something they depend on changes.
You do not do this plumbing yourself in an application: connect_gige fetches,
parses and builds, and viva-camctl get / set use the same pipeline. See the
viva-genapi chapter for the internals.
6. When should you look at the XML?
Most of the time, treat it as an implementation detail. Crack it open when:
- A feature behaves differently from what SFNC describes.
- Selectors are not doing what you expect.
- You hit a SwissKnife or bitfield corner case.
- You are adding support for a vendor-specific wrinkle.
A workable order: dump it with viva-camctl xml, run fetch_xml for the schema
version and skip list, then read the document itself in an XML viewer for the
category you care about.
If you have a camera the library cannot open, that document is the single most useful thing you can send us — see Reporting a camera we can’t open.
7. Recap
You should now:
- Know what the GenICam XML is, where it lives, and how the URL registers point at it.
- Be able to fetch it with
viva-camctl xmlorfetch_and_load_xml. - Know the difference between the minimal scan and the full parse, and what a skipped node means.
Next: Streaming — getting image data out, now that you know how the camera describes itself.