@@ -51,6 +51,7 @@ type MonitoringParameters struct {
5151type NodeSettings struct {
5252 FieldName string `toml:"name"`
5353 Namespace string `toml:"namespace"`
54+ NamespaceURI string `toml:"namespace_uri"`
5455 IdentifierType string `toml:"identifier_type"`
5556 Identifier string `toml:"identifier"`
5657 DefaultTags map [string ]string `toml:"default_tags"`
@@ -59,13 +60,17 @@ type NodeSettings struct {
5960
6061// NodeID returns the OPC UA node id
6162func (tag * NodeSettings ) NodeID () string {
63+ if tag .NamespaceURI != "" {
64+ return "nsu=" + tag .NamespaceURI + ";" + tag .IdentifierType + "=" + tag .Identifier
65+ }
6266 return "ns=" + tag .Namespace + ";" + tag .IdentifierType + "=" + tag .Identifier
6367}
6468
6569// NodeGroupSettings describes a mapping of group of nodes to Metrics
6670type NodeGroupSettings struct {
6771 MetricName string `toml:"name"` // Overrides plugin's setting
6872 Namespace string `toml:"namespace"` // Can be overridden by node setting
73+ NamespaceURI string `toml:"namespace_uri"` // Can be overridden by node setting
6974 IdentifierType string `toml:"identifier_type"` // Can be overridden by node setting
7075 Nodes []NodeSettings `toml:"nodes"`
7176 DefaultTags map [string ]string `toml:"default_tags"`
@@ -74,11 +79,15 @@ type NodeGroupSettings struct {
7479
7580type EventNodeSettings struct {
7681 Namespace string `toml:"namespace"`
82+ NamespaceURI string `toml:"namespace_uri"`
7783 IdentifierType string `toml:"identifier_type"`
7884 Identifier string `toml:"identifier"`
7985}
8086
8187func (e * EventNodeSettings ) NodeID () string {
88+ if e .NamespaceURI != "" {
89+ return "nsu=" + e .NamespaceURI + ";" + e .IdentifierType + "=" + e .Identifier
90+ }
8291 return "ns=" + e .Namespace + ";" + e .IdentifierType + "=" + e .Identifier
8392}
8493
@@ -87,6 +96,7 @@ type EventGroupSettings struct {
8796 QueueSize uint32 `toml:"queue_size"`
8897 EventTypeNode EventNodeSettings `toml:"event_type_node"`
8998 Namespace string `toml:"namespace"`
99+ NamespaceURI string `toml:"namespace_uri"`
90100 IdentifierType string `toml:"identifier_type"`
91101 NodeIDSettings []EventNodeSettings `toml:"node_ids"`
92102 SourceNames []string `toml:"source_names"`
@@ -99,6 +109,9 @@ func (e *EventGroupSettings) UpdateNodeIDSettings() {
99109 if n .Namespace == "" {
100110 n .Namespace = e .Namespace
101111 }
112+ if n .NamespaceURI == "" {
113+ n .NamespaceURI = e .NamespaceURI
114+ }
102115 if n .IdentifierType == "" {
103116 n .IdentifierType = e .IdentifierType
104117 }
@@ -138,11 +151,23 @@ func (e EventNodeSettings) validateEventNodeSettings() error {
138151 }
139152 if e .Identifier == "" {
140153 return errors .New ("identifier must be set" )
141- } else if e .IdentifierType == "" {
154+ }
155+ if e .IdentifierType == "" {
142156 return errors .New ("identifier_type must be set" )
143- } else if e .Namespace == "" {
144- return errors .New ("namespace must be set" )
145157 }
158+
159+ // Validate namespace configuration
160+ hasNamespace := len (e .Namespace ) > 0
161+ hasNamespaceURI := len (e .NamespaceURI ) > 0
162+
163+ if hasNamespace && hasNamespaceURI {
164+ return errors .New ("cannot specify both 'namespace' and 'namespace_uri', use only one" )
165+ }
166+
167+ if ! hasNamespace && ! hasNamespaceURI {
168+ return errors .New ("must specify either 'namespace' or 'namespace_uri'" )
169+ }
170+
146171 return nil
147172}
148173
@@ -334,8 +359,16 @@ func validateNodeToAdd(existing map[metricParts]struct{}, nmm *NodeMetricMapping
334359 return fmt .Errorf ("empty name in %q" , nmm .Tag .FieldName )
335360 }
336361
337- if len (nmm .Tag .Namespace ) == 0 {
338- return errors .New ("empty node namespace not allowed" )
362+ // Validate namespace configuration
363+ hasNamespace := len (nmm .Tag .Namespace ) > 0
364+ hasNamespaceURI := len (nmm .Tag .NamespaceURI ) > 0
365+
366+ if hasNamespace && hasNamespaceURI {
367+ return fmt .Errorf ("node %q: cannot specify both 'namespace' and 'namespace_uri', use only one" , nmm .Tag .FieldName )
368+ }
369+
370+ if ! hasNamespace && ! hasNamespaceURI {
371+ return fmt .Errorf ("node %q: must specify either 'namespace' or 'namespace_uri'" , nmm .Tag .FieldName )
339372 }
340373
341374 if len (nmm .Tag .Identifier ) == 0 {
@@ -396,6 +429,9 @@ func (o *OpcUAInputClient) InitNodeMetricMapping() error {
396429 if node .Namespace == "" {
397430 node .Namespace = group .Namespace
398431 }
432+ if node .NamespaceURI == "" {
433+ node .NamespaceURI = group .NamespaceURI
434+ }
399435 if node .IdentifierType == "" {
400436 node .IdentifierType = group .IdentifierType
401437 }
@@ -420,29 +456,91 @@ func (o *OpcUAInputClient) InitNodeMetricMapping() error {
420456
421457func (o * OpcUAInputClient ) InitNodeIDs () error {
422458 o .NodeIDs = make ([]* ua.NodeID , 0 , len (o .NodeMetricMapping ))
459+ namespaceArray := o .NamespaceArray ()
460+
423461 for _ , node := range o .NodeMetricMapping {
424- nid , err := ua .ParseNodeID (node .Tag .NodeID ())
425- if err != nil {
426- return err
462+ nodeIDStr := node .Tag .NodeID ()
463+
464+ // Check if this uses namespace URI (nsu=) format
465+ if strings .HasPrefix (nodeIDStr , "nsu=" ) {
466+ // Namespace URI format requires namespace array
467+ if len (namespaceArray ) == 0 {
468+ return fmt .Errorf ("node ID %q uses namespace URI (nsu=) but namespace array is not available - connection to server may be required" , nodeIDStr )
469+ }
470+ // Use ParseExpandedNodeID for namespace URI support
471+ expandedNodeID , err := ua .ParseExpandedNodeID (nodeIDStr , namespaceArray )
472+ if err != nil {
473+ return fmt .Errorf ("failed to parse node ID %q: %w" , nodeIDStr , err )
474+ }
475+ o .NodeIDs = append (o .NodeIDs , expandedNodeID .NodeID )
476+ } else {
477+ // Use ParseNodeID for namespace index (ns=) format
478+ nid , err := ua .ParseNodeID (nodeIDStr )
479+ if err != nil {
480+ return fmt .Errorf ("failed to parse node ID %q: %w" , nodeIDStr , err )
481+ }
482+ o .NodeIDs = append (o .NodeIDs , nid )
427483 }
428- o .NodeIDs = append (o .NodeIDs , nid )
429484 }
430485
431486 return nil
432487}
433488
434489func (o * OpcUAInputClient ) InitEventNodeIDs () error {
490+ namespaceArray := o .NamespaceArray ()
491+
435492 for _ , eventSetting := range o .EventGroups {
436- eid , err := ua .ParseNodeID (eventSetting .EventTypeNode .NodeID ())
437- if err != nil {
438- return err
493+ eventTypeNodeIDStr := eventSetting .EventTypeNode .NodeID ()
494+ var eid * ua.NodeID
495+
496+ // Parse event type node ID
497+ if strings .HasPrefix (eventTypeNodeIDStr , "nsu=" ) {
498+ if len (namespaceArray ) == 0 {
499+ return fmt .Errorf (
500+ "event type node ID %q uses namespace URI (nsu=) but namespace array is not available - " +
501+ "connection to server may be required" ,
502+ eventTypeNodeIDStr ,
503+ )
504+ }
505+ expandedNodeID , err := ua .ParseExpandedNodeID (eventTypeNodeIDStr , namespaceArray )
506+ if err != nil {
507+ return fmt .Errorf ("failed to parse event type node ID %q: %w" , eventTypeNodeIDStr , err )
508+ }
509+ eid = expandedNodeID .NodeID
510+ } else {
511+ parsedID , err := ua .ParseNodeID (eventTypeNodeIDStr )
512+ if err != nil {
513+ return fmt .Errorf ("failed to parse event type node ID %q: %w" , eventTypeNodeIDStr , err )
514+ }
515+ eid = parsedID
439516 }
440- for _ , node := range eventSetting .NodeIDSettings {
441- nid , err := ua .ParseNodeID (node .NodeID ())
442517
443- if err != nil {
444- return err
518+ for _ , node := range eventSetting .NodeIDSettings {
519+ nodeIDStr := node .NodeID ()
520+ var nid * ua.NodeID
521+
522+ // Parse node ID
523+ if strings .HasPrefix (nodeIDStr , "nsu=" ) {
524+ if len (namespaceArray ) == 0 {
525+ return fmt .Errorf (
526+ "event node ID %q uses namespace URI (nsu=) but namespace array is not available - " +
527+ "connection to server may be required" ,
528+ nodeIDStr ,
529+ )
530+ }
531+ expandedNodeID , err := ua .ParseExpandedNodeID (nodeIDStr , namespaceArray )
532+ if err != nil {
533+ return fmt .Errorf ("failed to parse node ID %q: %w" , nodeIDStr , err )
534+ }
535+ nid = expandedNodeID .NodeID
536+ } else {
537+ parsedID , err := ua .ParseNodeID (nodeIDStr )
538+ if err != nil {
539+ return fmt .Errorf ("failed to parse node ID %q: %w" , nodeIDStr , err )
540+ }
541+ nid = parsedID
445542 }
543+
446544 nmm := EventNodeMetricMapping {
447545 NodeID : nid ,
448546 SamplingInterval : & eventSetting .SamplingInterval ,
0 commit comments