Skip to main content

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    /// Selector nodes controlling the visibility of this node.
213    pub selectors: Vec<String>,
214    /// Selector gating rules in the form `(selector, allowed values)`.
215    pub selected_if: Vec<(String, Vec<String>)>,
216    /// Node providing the value (delegates read/write).
217    pub pvalue: Option<String>,
218    /// Node providing the dynamic maximum.
219    pub p_max: Option<String>,
220    /// Node providing the dynamic minimum.
221    pub p_min: Option<String>,
222    /// Static value for constant nodes.
223    pub value: Option<i64>,
224    /// Predicate refs gating implementation / availability / lock state.
225    pub predicates: PredicateRefs,
226    pub(crate) cache: RefCell<Option<i64>>,
227    pub(crate) raw_cache: RefCell<Option<Vec<u8>>>,
228}
229
230/// Floating point feature metadata.
231#[derive(Debug)]
232pub struct FloatNode {
233    pub name: String,
234    /// Shared metadata (visibility, description, tooltip, etc.).
235    pub meta: NodeMeta,
236    /// Register addressing metadata (absent when delegated via `pvalue`).
237    pub addressing: Option<Addressing>,
238    pub access: AccessMode,
239    pub min: f64,
240    pub max: f64,
241    pub unit: Option<String>,
242    /// Optional rational scale `(numerator, denominator)` applied to the raw value.
243    pub scale: Option<(i64, i64)>,
244    /// Optional offset added after scaling.
245    pub offset: Option<f64>,
246    pub selectors: Vec<String>,
247    pub selected_if: Vec<(String, Vec<String>)>,
248    /// Node providing the value (delegates read/write).
249    pub pvalue: Option<String>,
250    /// How the register payload is encoded (IEEE 754 or scaled integer).
251    pub encoding: FloatEncoding,
252    /// Byte order of the register payload.
253    pub byte_order: ByteOrder,
254    /// Predicate refs gating implementation / availability / lock state.
255    pub predicates: PredicateRefs,
256    pub(crate) cache: RefCell<Option<f64>>,
257}
258
259/// Enumeration feature metadata and mapping tables.
260#[derive(Debug)]
261pub struct EnumNode {
262    pub name: String,
263    /// Shared metadata (visibility, description, tooltip, etc.).
264    pub meta: NodeMeta,
265    /// Register addressing metadata (absent when delegated via `pvalue`).
266    pub addressing: Option<Addressing>,
267    pub access: AccessMode,
268    /// Node providing the integer value (delegates register read/write).
269    pub pvalue: Option<String>,
270    pub entries: Vec<EnumEntryDecl>,
271    pub default: Option<String>,
272    pub selectors: Vec<String>,
273    pub selected_if: Vec<(String, Vec<String>)>,
274    pub providers: Vec<String>,
275    /// Predicate refs gating implementation / availability / lock state.
276    pub predicates: PredicateRefs,
277    pub(crate) value_cache: RefCell<Option<String>>,
278    pub(crate) mapping_cache: RefCell<Option<EnumMapping>>,
279}
280
281#[derive(Debug, Clone)]
282pub(crate) struct EnumMapping {
283    pub by_value: HashMap<i64, String>,
284    pub by_name: HashMap<String, i64>,
285}
286
287impl EnumNode {
288    pub(crate) fn invalidate(&self) {
289        self.value_cache.replace(None);
290        self.mapping_cache.replace(None);
291    }
292}
293
294/// Boolean feature metadata.
295#[derive(Debug)]
296pub struct BooleanNode {
297    pub name: String,
298    /// Shared metadata (visibility, description, tooltip, etc.).
299    pub meta: NodeMeta,
300    /// Register addressing metadata (absent when delegated via `pvalue`).
301    pub addressing: Option<Addressing>,
302    pub len: u32,
303    pub access: AccessMode,
304    /// Optional bitfield (absent for pValue-backed booleans).
305    pub bitfield: Option<BitField>,
306    pub selectors: Vec<String>,
307    pub selected_if: Vec<(String, Vec<String>)>,
308    /// Node providing the value (delegates read/write).
309    pub pvalue: Option<String>,
310    /// On value for pValue-backed booleans.
311    pub on_value: Option<i64>,
312    /// Off value for pValue-backed booleans.
313    pub off_value: Option<i64>,
314    /// Predicate refs gating implementation / availability / lock state.
315    pub predicates: PredicateRefs,
316    pub(crate) cache: RefCell<Option<bool>>,
317    pub(crate) raw_cache: RefCell<Option<Vec<u8>>>,
318}
319
320/// SwissKnife node evaluating an arithmetic expression referencing other nodes.
321///
322/// `<IntSwissKnife>` (`output` = [`SkOutput::Integer`]) evaluates in integer
323/// arithmetic, where `/` truncates and 64-bit values stay exact;
324/// `<SwissKnife>` evaluates in floating point.
325#[derive(Debug)]
326pub struct SkNode {
327    /// Unique feature name.
328    pub name: String,
329    /// Shared metadata (visibility, description, tooltip, etc.).
330    pub meta: NodeMeta,
331    /// Desired output type as declared in the XML.
332    pub output: SkOutput,
333    /// Parsed expression AST.
334    pub ast: AstNode,
335    /// Mapping of variable identifiers to provider node names.
336    pub vars: Vec<(String, String)>,
337    /// Predicate refs gating implementation / availability.
338    pub predicates: PredicateRefs,
339    /// Cached value alongside the generation it was computed in.
340    pub cache: RefCell<Option<(Value, u64)>>,
341}
342
343/// Command feature metadata.
344#[derive(Debug)]
345pub struct CommandNode {
346    pub name: String,
347    /// Shared metadata (visibility, description, tooltip, etc.).
348    pub meta: NodeMeta,
349    /// Fixed register address (absent when delegated via `pvalue`).
350    pub address: Option<u64>,
351    pub len: u32,
352    /// Node providing the command register.
353    pub pvalue: Option<String>,
354    /// Value to write when executing the command.
355    pub command_value: Option<i64>,
356    /// Predicate refs gating implementation / availability / lock state.
357    pub predicates: PredicateRefs,
358}
359
360/// Category node describing child feature names.
361#[derive(Debug)]
362pub struct CategoryNode {
363    pub name: String,
364    /// Shared metadata (visibility, description, tooltip, etc.).
365    pub meta: NodeMeta,
366    pub children: Vec<String>,
367    /// Predicate refs gating implementation / availability.
368    pub predicates: PredicateRefs,
369}
370
371/// Converter node transforming a raw register value to/from a float feature
372/// value via two formulas.
373///
374/// The GenICam direction names read backwards at first glance, and getting
375/// them the wrong way round is silent — the identity converter behaves the
376/// same either way. They are named after the *target* of the conversion:
377///
378/// - `<FormulaFrom>` converts **from the raw value to the feature value** and
379///   is what a **read** evaluates. Its `TO` variable is bound to `p_value`.
380/// - `<FormulaTo>` converts **from the feature value to the raw value** and is
381///   what a **write** evaluates. Its `FROM` variable is bound to the incoming
382///   value and `OLD`, where declared, to the current raw value.
383#[derive(Debug)]
384pub struct ConverterNode {
385    /// Unique feature name.
386    pub name: String,
387    /// Shared metadata (visibility, description, tooltip, etc.).
388    pub meta: NodeMeta,
389    /// Name of the node providing the raw register value.
390    pub p_value: String,
391    /// Parsed `<FormulaTo>`: feature value → raw value, evaluated on write.
392    pub ast_to: AstNode,
393    /// Parsed `<FormulaFrom>`: raw value → feature value, evaluated on read.
394    pub ast_from: AstNode,
395    /// Variable mappings for `<FormulaTo>` (the write direction).
396    pub vars_to: Vec<(String, String)>,
397    /// Variable mappings for `<FormulaFrom>` (the read direction).
398    pub vars_from: Vec<(String, String)>,
399    /// Optional engineering unit.
400    pub unit: Option<String>,
401    /// Desired output type.
402    pub output: SkOutput,
403    /// Predicate refs gating implementation / availability / lock state.
404    pub predicates: PredicateRefs,
405    /// Cached user-facing value alongside the generation it was computed in.
406    pub cache: RefCell<Option<(Value, u64)>>,
407}
408
409/// IntConverter node transforming a raw register value to/from an integer
410/// feature value via two formulas.
411///
412/// Same direction convention as [`ConverterNode`], evaluated in integer
413/// arithmetic.
414#[derive(Debug)]
415pub struct IntConverterNode {
416    /// Unique feature name.
417    pub name: String,
418    /// Shared metadata (visibility, description, tooltip, etc.).
419    pub meta: NodeMeta,
420    /// Name of the node providing the raw register value.
421    pub p_value: String,
422    /// Parsed `<FormulaTo>`: feature value → raw value, evaluated on write.
423    pub ast_to: AstNode,
424    /// Parsed `<FormulaFrom>`: raw value → feature value, evaluated on read.
425    pub ast_from: AstNode,
426    /// Variable mappings for `<FormulaTo>` (the write direction).
427    pub vars_to: Vec<(String, String)>,
428    /// Variable mappings for `<FormulaFrom>` (the read direction).
429    pub vars_from: Vec<(String, String)>,
430    /// Optional engineering unit.
431    pub unit: Option<String>,
432    /// Predicate refs gating implementation / availability / lock state.
433    pub predicates: PredicateRefs,
434    /// Cached user-facing value alongside the generation it was computed in.
435    pub cache: RefCell<Option<(i64, u64)>>,
436}
437
438/// StringReg node for string-typed register access.
439#[derive(Debug)]
440pub struct StringNode {
441    /// Unique feature name.
442    pub name: String,
443    /// Shared metadata (visibility, description, tooltip, etc.).
444    pub meta: NodeMeta,
445    /// Register addressing metadata.
446    pub addressing: Addressing,
447    /// Declared access rights.
448    pub access: AccessMode,
449    /// Predicate refs gating implementation / availability / lock state.
450    pub predicates: PredicateRefs,
451    /// Cached string value alongside the generation it was computed in.
452    pub cache: RefCell<Option<(String, u64)>>,
453}
454
455/// `<Register>` node: raw byte-array access to a register block.
456///
457/// The base register type — an address, a byte count, and no interpretation of
458/// the bytes. `StringNode` is this plus UTF-8/NUL decoding.
459#[derive(Debug)]
460pub struct RegisterNode {
461    /// Unique feature name.
462    pub name: String,
463    /// Shared metadata (visibility, description, tooltip, etc.).
464    pub meta: NodeMeta,
465    /// Register addressing metadata, including the block length.
466    pub addressing: Addressing,
467    /// Declared access rights.
468    pub access: AccessMode,
469    /// `<pPort>` target; `None` or `"Device"` means the device port.
470    ///
471    /// Any other port is parsed and listed but cannot be read — see GA-12.
472    pub port: Option<String>,
473    /// Predicate refs gating implementation / availability / lock state.
474    pub predicates: PredicateRefs,
475    /// Cached payload alongside the generation it was read in.
476    pub cache: RefCell<Option<(Vec<u8>, u64)>>,
477}
478
479impl RegisterNode {
480    /// Declared length of the register block in bytes.
481    ///
482    /// `None` for a selector-mapped block, whose length depends on the current
483    /// selector value and therefore needs a transport to resolve — use
484    /// `NodeMap::register_address` for that.
485    ///
486    /// This exists so a consumer can report a register's size without naming
487    /// `Addressing`, which is not re-exported (backlog `API-02`).
488    pub fn declared_len(&self) -> Option<u32> {
489        match &self.addressing {
490            Addressing::Sum { len, .. } => Some(*len),
491            Addressing::BySelector { .. } => None,
492        }
493    }
494}