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    /// 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}