Skip to content

Conversation

@skartikey
Copy link
Contributor

Summary

Adds support for specifying OPC UA node IDs using the standard string format (e.g., ns=0;i=2262 or nsu=http://...;s=Name) as an alternative to specifying namespace, identifier_type, and identifier separately.

  • Users can now configure nodes using the simpler node_id string that matches what OPC UA browsers display
  • The existing configuration style using individual fields (namespace, identifier_type, identifier) continues to work unchanged
  • Both styles can be used in the same configuration file (but not for the same node)

New style (recommended for simplicity):

  [[inputs.opcua.nodes]]                                                                                                                                                                                                           
    name = "ProductUri"                                                                                                                                                                                                            
    node_id = "ns=0;i=2262"    

Existing style (still fully supported):

  [[inputs.opcua.nodes]]                                                                                                                                                                                                           
    name = "ProductUri"                                                                                                                                                                                                            
    namespace = "0"                                                                                                                                                                                                                
    identifier_type = "i"                                                                                                                                                                                                          
    identifier = "2262"   

Supported node ID formats:

Checklist

Related issues

resolves #12396

…ration option

- Add support for specifying OPC UA node IDs using the standard string format
  (e.g., "ns=0;i=2262" or "nsu=http://...;s=Name") as an alternative to
  specifying namespace, identifier_type, and identifier separately.
- This simplifies configuration and matches the format used in OPC UA browsers.

Example usage:

  {name="ProductUri", node_id="ns=0;i=2262"}

Instead of:

  {name="ProductUri", namespace="0", identifier_type="i", identifier="2262"}
@telegraf-tiger telegraf-tiger bot added the feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin label Jan 19, 2026
@skartikey skartikey self-assigned this Jan 19, 2026
@skartikey skartikey changed the title feat(inputs.opcua, inputs.opcua_listener): Add node_id string configu… feat(inputs.opcua, inputs.opcua_listener): Add node_id string configuration option Jan 20, 2026
@skartikey skartikey assigned srebhan and mstrandboge and unassigned skartikey Jan 20, 2026
@srebhan srebhan changed the title feat(inputs.opcua, inputs.opcua_listener): Add node_id string configuration option feat(common.opcua): Add string configuration option for node ID Jan 28, 2026
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @skartikey! Some comments from my side.

// NodeSettings describes how to map from a OPC UA node to a Metric
type NodeSettings struct {
FieldName string `toml:"name"`
NodeIDStr string `toml:"node_id"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we name this option just id? Asking because it kind of sounds strange if you have node_id in a nodes section...
Same below...

Comment on lines 96 to 144
// nodeIDParts holds the components of a parsed OPC UA node ID string
type nodeIDParts struct {
namespace string
namespaceURI string
identifierType string
identifier string
}

// parseNodeIDString parses an OPC UA node ID string (e.g., "ns=0;i=2262" or "nsu=http://...;s=Name")
// and returns the parsed components.
func parseNodeIDString(nodeIDStr string) (nodeIDParts, error) {
var result nodeIDParts

// Split on semicolon to get namespace part and identifier part
parts := strings.SplitN(nodeIDStr, ";", 2)
if len(parts) != 2 {
return result, fmt.Errorf("invalid node ID format %q: expected 'ns=X;Y=Z' or 'nsu=URI;Y=Z'", nodeIDStr)
}

// Parse namespace part (ns= or nsu=)
nsPart := parts[0]
switch {
case strings.HasPrefix(nsPart, "ns="):
result.namespace = strings.TrimPrefix(nsPart, "ns=")
case strings.HasPrefix(nsPart, "nsu="):
result.namespaceURI = strings.TrimPrefix(nsPart, "nsu=")
default:
return result, fmt.Errorf("invalid node ID format %q: namespace must start with 'ns=' or 'nsu='", nodeIDStr)
}

// Parse identifier part (i=, s=, g=, or b=)
idPart := parts[1]
if len(idPart) < 2 || idPart[1] != '=' {
return result, fmt.Errorf("invalid node ID format %q: identifier must be in format 'X=value'", nodeIDStr)
}

result.identifierType = string(idPart[0])
result.identifier = idPart[2:]

// Validate identifier type
switch result.identifierType {
case "i", "s", "g", "b":
// Valid
default:
return result, fmt.Errorf("invalid identifier type %q in node ID %q: expected i, s, g, or b", result.identifierType, nodeIDStr)
}

return result, nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? Can't we use the library function for parsing?

Comment on lines 146 to 192
// SetFromNodeIDString parses the NodeIDStr field and populates the individual fields.
// Returns an error if NodeIDStr is set but invalid, or if both NodeIDStr and individual fields are set.
func (tag *NodeSettings) SetFromNodeIDString() error {
if tag.NodeIDStr == "" {
return nil
}

// Check for conflicting configuration
if tag.Namespace != "" || tag.NamespaceURI != "" || tag.IdentifierType != "" || tag.Identifier != "" {
return fmt.Errorf("node %q: cannot specify both 'node_id' and individual fields (namespace/namespace_uri/identifier_type/identifier)", tag.FieldName)
}

parsed, err := parseNodeIDString(tag.NodeIDStr)
if err != nil {
return fmt.Errorf("node %q: %w", tag.FieldName, err)
}

tag.Namespace = parsed.namespace
tag.NamespaceURI = parsed.namespaceURI
tag.IdentifierType = parsed.identifierType
tag.Identifier = parsed.identifier
return nil
}

// SetFromNodeIDString parses the NodeIDStr field and populates the individual fields.
// Returns an error if NodeIDStr is set but invalid, or if both NodeIDStr and individual fields are set.
func (e *EventNodeSettings) SetFromNodeIDString() error {
if e.NodeIDStr == "" {
return nil
}

// Check for conflicting configuration
if e.Namespace != "" || e.NamespaceURI != "" || e.IdentifierType != "" || e.Identifier != "" {
return errors.New("cannot specify both 'node_id' and individual fields (namespace/namespace_uri/identifier_type/identifier)")
}

parsed, err := parseNodeIDString(e.NodeIDStr)
if err != nil {
return err
}

e.Namespace = parsed.namespace
e.NamespaceURI = parsed.namespaceURI
e.IdentifierType = parsed.identifierType
e.Identifier = parsed.identifier
return nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go the other way and directly parse this to a node-id type as that's what we need later on.

Comment on lines 283 to 285
> **Note:** Use either `node_id` OR the combination of
> `namespace`/`namespace_uri` + `identifier_type` + `identifier`.
> Do not mix both formats for the same node.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use markdown alerts for this!

Comment on lines 353 to 356
> **Note:** Use either `node_id` OR the combination of
> `namespace`/`namespace_uri` + `identifier_type` + `identifier`.
> Do not mix both formats for the same node.

Copy link
Member

@srebhan srebhan Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

@telegraf-tiger
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/opcua feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(inputs.opcua+inputs.opcua_listener) Support node id string in config

3 participants