viva_genapi/nodes.rs
1//! Node type definitions for the GenApi node system.
2
3use std::cell::RefCell;
4use std::collections::HashMap;
5
6use viva_genapi_xml::{
7 AccessMode, Addressing, BitField, ByteOrder, EnumEntryDecl, FloatEncoding, Sign,
8};
9pub use viva_genapi_xml::{NodeMeta, PredicateRefs, Representation, SkOutput, Visibility};
10
11use crate::swissknife::{AstNode, Value};
12
13/// Node kinds supported by the Tier-1 subset.
14#[derive(Debug)]
15#[non_exhaustive]
16pub enum Node {
17 /// Signed integer feature stored in a fixed-width register block.
18 Integer(IntegerNode),
19 /// Floating point feature with optional scale/offset conversion.
20 Float(FloatNode),
21 /// Enumeration feature mapping integers to symbolic names.
22 Enum(EnumNode),
23 /// Boolean feature represented as an integer register.
24 Boolean(BooleanNode),
25 /// Command feature triggering a device-side action when written.
26 Command(CommandNode),
27 /// Category organising related features.
28 Category(CategoryNode),
29 /// SwissKnife expression producing a computed value.
30 SwissKnife(SkNode),
31 /// Converter transforming raw values to/from float values via formulas.
32 Converter(ConverterNode),
33 /// IntConverter transforming raw values to/from integer values via formulas.
34 IntConverter(IntConverterNode),
35 /// StringReg for string-typed register access.
36 String(StringNode),
37 /// Raw byte-array register access.
38 Register(RegisterNode),
39}
40
41impl Node {
42 /// Return the GenICam node type name (e.g. "Integer", "Float", "Enumeration").
43 pub fn kind_name(&self) -> &'static str {
44 match self {
45 Node::Integer(_) => "Integer",
46 Node::Float(_) => "Float",
47 Node::Enum(_) => "Enumeration",
48 Node::Boolean(_) => "Boolean",
49 Node::Command(_) => "Command",
50 Node::Category(_) => "Category",
51 Node::SwissKnife(_) => "SwissKnife",
52 Node::Converter(_) => "Converter",
53 Node::IntConverter(_) => "IntConverter",
54 Node::String(_) => "StringReg",
55 Node::Register(_) => "Register",
56 }
57 }
58
59 /// Return the access mode of the node, if applicable.
60 pub fn access_mode(&self) -> Option<viva_genapi_xml::AccessMode> {
61 match self {
62 Node::Integer(n) => Some(n.access),
63 Node::Float(n) => Some(n.access),
64 Node::Enum(n) => Some(n.access),
65 Node::Boolean(n) => Some(n.access),
66 Node::Command(_) => Some(viva_genapi_xml::AccessMode::WO),
67 Node::Category(_) => None,
68 Node::SwissKnife(_) => Some(viva_genapi_xml::AccessMode::RO),
69 Node::Converter(_) => Some(viva_genapi_xml::AccessMode::RO),
70 Node::IntConverter(_) => Some(viva_genapi_xml::AccessMode::RO),
71 Node::String(n) => Some(n.access),
72 Node::Register(n) => Some(n.access),
73 }
74 }
75
76 /// Return the node name.
77 pub fn name(&self) -> &str {
78 match self {
79 Node::Integer(n) => &n.name,
80 Node::Float(n) => &n.name,
81 Node::Enum(n) => &n.name,
82 Node::Boolean(n) => &n.name,
83 Node::Command(n) => &n.name,
84 Node::Category(n) => &n.name,
85 Node::SwissKnife(n) => &n.name,
86 Node::Converter(n) => &n.name,
87 Node::IntConverter(n) => &n.name,
88 Node::String(n) => &n.name,
89 Node::Register(n) => &n.name,
90 }
91 }
92
93 /// Return the shared metadata for this node.
94 pub fn meta(&self) -> &NodeMeta {
95 match self {
96 Node::Integer(n) => &n.meta,
97 Node::Float(n) => &n.meta,
98 Node::Enum(n) => &n.meta,
99 Node::Boolean(n) => &n.meta,
100 Node::Command(n) => &n.meta,
101 Node::Category(n) => &n.meta,
102 Node::SwissKnife(n) => &n.meta,
103 Node::Converter(n) => &n.meta,
104 Node::IntConverter(n) => &n.meta,
105 Node::String(n) => &n.meta,
106 Node::Register(n) => &n.meta,
107 }
108 }
109
110 /// Return the visibility level of this node.
111 pub fn visibility(&self) -> Visibility {
112 self.meta().visibility
113 }
114
115 /// Return the description of this node, if any.
116 pub fn description(&self) -> Option<&str> {
117 self.meta().description.as_deref()
118 }
119
120 /// Return the tooltip of this node, if any.
121 pub fn tooltip(&self) -> Option<&str> {
122 self.meta().tooltip.as_deref()
123 }
124
125 /// Return the display name of this node, if any.
126 pub fn display_name(&self) -> Option<&str> {
127 self.meta().display_name.as_deref()
128 }
129
130 /// Return the recommended representation for this node, if any.
131 pub fn representation(&self) -> Option<Representation> {
132 self.meta().representation
133 }
134
135 /// Return the predicate references (`pIsImplemented`, `pIsAvailable`,
136 /// `pIsLocked`) declared on this node.
137 pub fn predicates(&self) -> &PredicateRefs {
138 match self {
139 Node::Integer(n) => &n.predicates,
140 Node::Float(n) => &n.predicates,
141 Node::Enum(n) => &n.predicates,
142 Node::Boolean(n) => &n.predicates,
143 Node::Command(n) => &n.predicates,
144 Node::Category(n) => &n.predicates,
145 Node::SwissKnife(n) => &n.predicates,
146 Node::Converter(n) => &n.predicates,
147 Node::IntConverter(n) => &n.predicates,
148 Node::String(n) => &n.predicates,
149 Node::Register(n) => &n.predicates,
150 }
151 }
152
153 pub(crate) fn invalidate_cache(&self) {
154 match self {
155 Node::Integer(node) => {
156 node.cache.replace(None);
157 node.raw_cache.replace(None);
158 }
159 Node::Float(node) => {
160 node.cache.replace(None);
161 }
162 Node::Enum(node) => node.invalidate(),
163 Node::Boolean(node) => {
164 node.cache.replace(None);
165 node.raw_cache.replace(None);
166 }
167 Node::SwissKnife(node) => {
168 node.cache.replace(None);
169 }
170 Node::Converter(node) => {
171 node.cache.replace(None);
172 }
173 Node::IntConverter(node) => {
174 node.cache.replace(None);
175 }
176 Node::String(node) => {
177 node.cache.replace(None);
178 }
179 Node::Register(node) => {
180 node.cache.replace(None);
181 }
182 Node::Command(_) | Node::Category(_) => {}
183 }
184 }
185}
186
187/// Integer feature metadata extracted from the XML description.
188#[derive(Debug)]
189pub struct IntegerNode {
190 /// Unique feature name.
191 pub name: String,
192 /// Shared metadata (visibility, description, tooltip, etc.).
193 pub meta: NodeMeta,
194 /// Register addressing metadata (absent when delegated via `pvalue`).
195 pub addressing: Option<Addressing>,
196 /// Nominal register length in bytes.
197 pub len: u32,
198 /// Declared access rights.
199 pub access: AccessMode,
200 /// Minimum permitted user value.
201 pub min: i64,
202 /// Maximum permitted user value.
203 pub max: i64,
204 /// Optional increment step the value must respect.
205 pub inc: Option<i64>,
206 /// Optional engineering unit such as "us".
207 pub unit: Option<String>,
208 /// Optional bitfield metadata restricting active bits.
209 pub bitfield: Option<BitField>,
210 /// Whether the register payload is signed. GenICam defaults to unsigned.
211 pub sign: Sign,
212 /// Byte order of the register payload. GenICam defaults to big-endian.
213 pub byte_order: ByteOrder,
214 /// Selector nodes controlling the visibility of this node.
215 pub selectors: Vec<String>,
216 /// Selector gating rules in the form `(selector, allowed values)`.
217 pub selected_if: Vec<(String, Vec<String>)>,
218 /// Node providing the value (delegates read/write).
219 pub pvalue: Option<String>,
220 /// Node providing the dynamic maximum.
221 pub p_max: Option<String>,
222 /// Node providing the dynamic minimum.
223 pub p_min: Option<String>,
224 /// Static value for constant nodes.
225 pub value: Option<i64>,
226 /// Predicate refs gating implementation / availability / lock state.
227 pub predicates: PredicateRefs,
228 pub(crate) cache: RefCell<Option<i64>>,
229 pub(crate) raw_cache: RefCell<Option<Vec<u8>>>,
230}
231
232/// Floating point feature metadata.
233#[derive(Debug)]
234pub struct FloatNode {
235 pub name: String,
236 /// Shared metadata (visibility, description, tooltip, etc.).
237 pub meta: NodeMeta,
238 /// Register addressing metadata (absent when delegated via `pvalue`).
239 pub addressing: Option<Addressing>,
240 pub access: AccessMode,
241 pub min: f64,
242 pub max: f64,
243 pub unit: Option<String>,
244 /// Optional rational scale `(numerator, denominator)` applied to the raw value.
245 pub scale: Option<(i64, i64)>,
246 /// Optional offset added after scaling.
247 pub offset: Option<f64>,
248 pub selectors: Vec<String>,
249 pub selected_if: Vec<(String, Vec<String>)>,
250 /// Node providing the value (delegates read/write).
251 pub pvalue: Option<String>,
252 /// How the register payload is encoded (IEEE 754 or scaled integer).
253 pub encoding: FloatEncoding,
254 /// Byte order of the register payload.
255 pub byte_order: ByteOrder,
256 /// Predicate refs gating implementation / availability / lock state.
257 pub predicates: PredicateRefs,
258 pub(crate) cache: RefCell<Option<f64>>,
259}
260
261/// Enumeration feature metadata and mapping tables.
262#[derive(Debug)]
263pub struct EnumNode {
264 pub name: String,
265 /// Shared metadata (visibility, description, tooltip, etc.).
266 pub meta: NodeMeta,
267 /// Register addressing metadata (absent when delegated via `pvalue`).
268 pub addressing: Option<Addressing>,
269 pub access: AccessMode,
270 /// Node providing the integer value (delegates register read/write).
271 pub pvalue: Option<String>,
272 pub entries: Vec<EnumEntryDecl>,
273 pub default: Option<String>,
274 pub selectors: Vec<String>,
275 pub selected_if: Vec<(String, Vec<String>)>,
276 pub providers: Vec<String>,
277 /// Predicate refs gating implementation / availability / lock state.
278 pub predicates: PredicateRefs,
279 pub(crate) value_cache: RefCell<Option<String>>,
280 pub(crate) mapping_cache: RefCell<Option<EnumMapping>>,
281}
282
283#[derive(Debug, Clone)]
284pub(crate) struct EnumMapping {
285 pub by_value: HashMap<i64, String>,
286 pub by_name: HashMap<String, i64>,
287}
288
289impl EnumNode {
290 pub(crate) fn invalidate(&self) {
291 self.value_cache.replace(None);
292 self.mapping_cache.replace(None);
293 }
294}
295
296/// Boolean feature metadata.
297#[derive(Debug)]
298pub struct BooleanNode {
299 pub name: String,
300 /// Shared metadata (visibility, description, tooltip, etc.).
301 pub meta: NodeMeta,
302 /// Register addressing metadata (absent when delegated via `pvalue`).
303 pub addressing: Option<Addressing>,
304 pub len: u32,
305 pub access: AccessMode,
306 /// Optional bitfield (absent for pValue-backed booleans).
307 pub bitfield: Option<BitField>,
308 pub selectors: Vec<String>,
309 pub selected_if: Vec<(String, Vec<String>)>,
310 /// Node providing the value (delegates read/write).
311 pub pvalue: Option<String>,
312 /// On value for pValue-backed booleans.
313 pub on_value: Option<i64>,
314 /// Off value for pValue-backed booleans.
315 pub off_value: Option<i64>,
316 /// Predicate refs gating implementation / availability / lock state.
317 pub predicates: PredicateRefs,
318 pub(crate) cache: RefCell<Option<bool>>,
319 pub(crate) raw_cache: RefCell<Option<Vec<u8>>>,
320}
321
322/// SwissKnife node evaluating an arithmetic expression referencing other nodes.
323///
324/// `<IntSwissKnife>` (`output` = [`SkOutput::Integer`]) evaluates in integer
325/// arithmetic, where `/` truncates and 64-bit values stay exact;
326/// `<SwissKnife>` evaluates in floating point.
327#[derive(Debug)]
328pub struct SkNode {
329 /// Unique feature name.
330 pub name: String,
331 /// Shared metadata (visibility, description, tooltip, etc.).
332 pub meta: NodeMeta,
333 /// Desired output type as declared in the XML.
334 pub output: SkOutput,
335 /// Parsed expression AST.
336 pub ast: AstNode,
337 /// Mapping of variable identifiers to provider node names.
338 pub vars: Vec<(String, String)>,
339 /// Predicate refs gating implementation / availability.
340 pub predicates: PredicateRefs,
341 /// Cached value alongside the generation it was computed in.
342 pub cache: RefCell<Option<(Value, u64)>>,
343}
344
345/// Command feature metadata.
346#[derive(Debug)]
347pub struct CommandNode {
348 pub name: String,
349 /// Shared metadata (visibility, description, tooltip, etc.).
350 pub meta: NodeMeta,
351 /// Fixed register address (absent when delegated via `pvalue`).
352 pub address: Option<u64>,
353 pub len: u32,
354 /// Node providing the command register.
355 pub pvalue: Option<String>,
356 /// Value to write when executing the command.
357 pub command_value: Option<i64>,
358 /// Predicate refs gating implementation / availability / lock state.
359 pub predicates: PredicateRefs,
360}
361
362/// Category node describing child feature names.
363#[derive(Debug)]
364pub struct CategoryNode {
365 pub name: String,
366 /// Shared metadata (visibility, description, tooltip, etc.).
367 pub meta: NodeMeta,
368 pub children: Vec<String>,
369 /// Predicate refs gating implementation / availability.
370 pub predicates: PredicateRefs,
371}
372
373/// Converter node transforming a raw register value to/from a float feature
374/// value via two formulas.
375///
376/// The GenICam direction names read backwards at first glance, and getting
377/// them the wrong way round is silent — the identity converter behaves the
378/// same either way. They are named after the *target* of the conversion:
379///
380/// - `<FormulaFrom>` converts **from the raw value to the feature value** and
381/// is what a **read** evaluates. Its `TO` variable is bound to `p_value`.
382/// - `<FormulaTo>` converts **from the feature value to the raw value** and is
383/// what a **write** evaluates. Its `FROM` variable is bound to the incoming
384/// value and `OLD`, where declared, to the current raw value.
385#[derive(Debug)]
386pub struct ConverterNode {
387 /// Unique feature name.
388 pub name: String,
389 /// Shared metadata (visibility, description, tooltip, etc.).
390 pub meta: NodeMeta,
391 /// Name of the node providing the raw register value.
392 pub p_value: String,
393 /// Parsed `<FormulaTo>`: feature value → raw value, evaluated on write.
394 pub ast_to: AstNode,
395 /// Parsed `<FormulaFrom>`: raw value → feature value, evaluated on read.
396 pub ast_from: AstNode,
397 /// Variable mappings for `<FormulaTo>` (the write direction).
398 pub vars_to: Vec<(String, String)>,
399 /// Variable mappings for `<FormulaFrom>` (the read direction).
400 pub vars_from: Vec<(String, String)>,
401 /// Optional engineering unit.
402 pub unit: Option<String>,
403 /// Desired output type.
404 pub output: SkOutput,
405 /// Predicate refs gating implementation / availability / lock state.
406 pub predicates: PredicateRefs,
407 /// Cached user-facing value alongside the generation it was computed in.
408 pub cache: RefCell<Option<(Value, u64)>>,
409}
410
411/// IntConverter node transforming a raw register value to/from an integer
412/// feature value via two formulas.
413///
414/// Same direction convention as [`ConverterNode`], evaluated in integer
415/// arithmetic.
416#[derive(Debug)]
417pub struct IntConverterNode {
418 /// Unique feature name.
419 pub name: String,
420 /// Shared metadata (visibility, description, tooltip, etc.).
421 pub meta: NodeMeta,
422 /// Name of the node providing the raw register value.
423 pub p_value: String,
424 /// Parsed `<FormulaTo>`: feature value → raw value, evaluated on write.
425 pub ast_to: AstNode,
426 /// Parsed `<FormulaFrom>`: raw value → feature value, evaluated on read.
427 pub ast_from: AstNode,
428 /// Variable mappings for `<FormulaTo>` (the write direction).
429 pub vars_to: Vec<(String, String)>,
430 /// Variable mappings for `<FormulaFrom>` (the read direction).
431 pub vars_from: Vec<(String, String)>,
432 /// Optional engineering unit.
433 pub unit: Option<String>,
434 /// Predicate refs gating implementation / availability / lock state.
435 pub predicates: PredicateRefs,
436 /// Cached user-facing value alongside the generation it was computed in.
437 pub cache: RefCell<Option<(i64, u64)>>,
438}
439
440/// StringReg node for string-typed register access.
441#[derive(Debug)]
442pub struct StringNode {
443 /// Unique feature name.
444 pub name: String,
445 /// Shared metadata (visibility, description, tooltip, etc.).
446 pub meta: NodeMeta,
447 /// Register addressing metadata.
448 pub addressing: Addressing,
449 /// Declared access rights.
450 pub access: AccessMode,
451 /// Predicate refs gating implementation / availability / lock state.
452 pub predicates: PredicateRefs,
453 /// Cached string value alongside the generation it was computed in.
454 pub cache: RefCell<Option<(String, u64)>>,
455}
456
457/// `<Register>` node: raw byte-array access to a register block.
458///
459/// The base register type — an address, a byte count, and no interpretation of
460/// the bytes. `StringNode` is this plus UTF-8/NUL decoding.
461#[derive(Debug)]
462pub struct RegisterNode {
463 /// Unique feature name.
464 pub name: String,
465 /// Shared metadata (visibility, description, tooltip, etc.).
466 pub meta: NodeMeta,
467 /// Register addressing metadata, including the block length.
468 pub addressing: Addressing,
469 /// Declared access rights.
470 pub access: AccessMode,
471 /// `<pPort>` target; `None` or `"Device"` means the device port.
472 ///
473 /// Any other port is parsed and listed but cannot be read — see GA-12.
474 pub port: Option<String>,
475 /// Predicate refs gating implementation / availability / lock state.
476 pub predicates: PredicateRefs,
477 /// Cached payload alongside the generation it was read in.
478 pub cache: RefCell<Option<(Vec<u8>, u64)>>,
479}
480
481impl RegisterNode {
482 /// Declared length of the register block in bytes.
483 ///
484 /// `None` for a selector-mapped block, whose length depends on the current
485 /// selector value and therefore needs a transport to resolve — use
486 /// `NodeMap::register_address` for that.
487 ///
488 /// This exists so a consumer can report a register's size without naming
489 /// `Addressing`, which is not re-exported (backlog `API-02`).
490 pub fn declared_len(&self) -> Option<u32> {
491 match &self.addressing {
492 Addressing::Sum { len, .. } => Some(*len),
493 Addressing::BySelector { .. } => None,
494 }
495 }
496}