Skip to content

Conversation

@palandri
Copy link

feat(opcua): support constant datapoints (attributes + one-time telemetry)

Summary

Adds first-class support for constant datapoints in the OPC-UA connector:

  • Attributes with type: "constant" are sent when a device is created and on every reconnection.
  • Telemetry with type: "constant" is sent once per device per gateway process (idempotent).
  • Values are auto-cast (bool/int/float) when possible; otherwise kept as string.
  • Telemetry is timestamped with the gateway time.
  • Report strategy is respected at datapoint level via TBUtility.

Motivation

Previously, OPC-UA mapping handled datapoints backed by NodeId or path expressions but ignored constants. This feature enables:

  • Populating device attributes with fixed metadata (e.g., Customer, Location).
  • Emitting one-time constant telemetry (e.g., initial setup values) without requiring OPC-UA nodes.

Implementation Details

Behavior

  • Attributes (type: "constant")
    • Sent on device creation and every reconnection (devices are recreated on reconnect).
  • Telemetry (type: "constant")
    • Sent exactly once per device per process (not re-sent on reconnects).

Configuration Example (new mapping format)

{
  "mapping": [
    {
      "deviceNodePattern": "Root\\.Objects\\.Device1",
      "deviceInfo": {
        "deviceNameExpressionSource": "path",
        "deviceNameExpression": "Device ${Root\\.Objects\\.Device1\\.serialNumber}",
        "deviceProfileExpressionSource": "constant",
        "deviceProfileExpression": "default"
      },
      "attributes": [
        { "key": "Customer", "type": "constant", "value": "ACME Corp" }
      ],
      "timeseries": [
        { "key": "InitialBatchSize", "type": "constant", "value": "12" }
      ]
    }
  ]
}

Casting Rules

  • "true"/"false" (case-insensitive) → boolean
  • -123 → integer
  • 45.67 → float
  • Anything else → string

Backward Compatibility

  • No breaking changes. Existing path/identifier-based datapoints remain unchanged.
  • The backward-compatibility adapter continues to set type: "constant" where applicable; the new pipeline now handles those entries.

Tests / Verification

Documentation Impact

  • Update OPC-UA connector mapping docs to include:
    • Support for type: "constant" in attributes and timeseries.
    • Semantics (attributes on create/reconnect, telemetry one-time).
    • Casting rules and gateway timestamp.
    • Example mapping similar to the snippet above.

Touched Code

Checklist

  • Parse/store constant datapoints (no OPC-UA read required)
  • Send constant attributes on create/reconnect
  • Send constant telemetry once per device per process
  • Type inference and gateway timestamp
  • Datapoint-level report strategy
  • Unit tests
  • Blackbox validation on TB test environment
  • Documentation and CHANGELOG updates

…emetry)

Parse/store type: constant datapoints; send constant attributes on device creation and reconnection; send constant telemetry once per device per process; type inference (bool/int/float/string) with gateway timestamp; respect reportStrategy via TBUtility.convert_key_to_datapoint_key; backward compatible path/identifier behavior.
…ime telemetry)

Add fixture config and unit test validating parsing buckets and idempotent constant telemetry send.
@samson0v
Copy link
Contributor

Hi @palandri, thanks for your contribution! We really appreciate it.
I will take a look at your PR soon.

@samson0v samson0v added this to the 3.7.9 milestone Oct 14, 2025
@palandri
Copy link
Author

Hello @samson0v. Do not worry, there is no hurry.
I'm looking forward your opinion on this.

Cheers.

@samson0v samson0v modified the milestones: 3.7.9, 3.8.0 Oct 21, 2025
@samson0v
Copy link
Contributor

Hi @palandri, I am reviewing your PR and have a question about this:

Attributes (type: "constant")
    Sent on device creation and every reconnection (devices are recreated on reconnect).
Telemetry (type: "constant")
    Sent exactly once per device per process (not re-sent on reconnects).

Why did you decide to use different strategies for sending?

new_device = Device(path=device_path, name=device_name, device_profile=device_profile,
config=device_config,
converter=converter(device_config, self.__converter_log),
converter_for_sub=converter(device_config, self.__converter_log) if self.__enable_subscriptions else None,
Copy link
Contributor

Choose a reason for hiding this comment

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

Please, fix formatting

Comment on lines +1109 to +1110
has_attributes = False
has_telemetry = False
Copy link
Contributor

Choose a reason for hiding this comment

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

We can check these using ConvertedData telemetry_datapoints_count and attributes_datapoints_count

converted_data = ConvertedData(device_name=device.name, device_type=device.device_profile)

# Constant attributes
for entry in getattr(device, 'constant_attributes', []):
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it necessary to check?

# Constant telemetry (send once per device name per process)
if device.name not in self.__constant_telemetry_sent_devices:
telemetry_values = {}
for entry in getattr(device, 'constant_timeseries', []):
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it necessary to check?

has_telemetry = True
self.__constant_telemetry_sent_devices.add(device.name)

if has_attributes or has_telemetry:
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment above

@samson0v samson0v removed this from the 3.8.0 milestone Nov 26, 2025
@samson0v
Copy link
Contributor

@palandri

📢 a little announcement:
We just launched our official Discord server, we'd love to see you there!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants