Skip to content

feat: support stateful multi-signal inputs for script stages #5

Description

@mbreissi

Summary

Add stateful, named multi-signal inputs to script pipeline stages. A script should run when any configured input changes and should receive the current value of every configured input.

Allow the computed script result to be published to an explicitly configured output topic. Scripts must not be limited to mutating the triggering message body and republishing it on the original input topic.

EdgeCommons calls these telemetry values signals; this is the multi-tag input behavior commonly expected by industrial users.

Motivation

The current script stage evaluates one arriving ProcMsg at a time. Lua and Rhai receive bindings for only that triggering message, including body, samples, first-sample value/quality, topic, envelope data, source identity, and route context.

That model cannot directly calculate values such as OEE when the operands arrive as independent signals. For example, an OEE script may need the current values of planned production time, running state, ideal cycle time, total count, good count, and reject count. The calculation should be refreshed when any one of those inputs changes.

A derived value such as OEE is a new signal with its own semantic identity and UNS topic. Publishing it only by modifying and republishing an arbitrary triggering input message incorrectly couples the derived output to whichever source signal changed most recently.

Proposed behavior

  • Allow a script stage to declare multiple named inputs, each with an unambiguous signal selector. Selectors should support signal ID/name and enough source identity (device/component/instance and/or topic) to avoid collisions across devices.
  • Maintain the latest value for every input inside the route worker/script stage.
  • When a configured input value or quality changes, update that cached entry and invoke the script once with a consistent snapshot of all current inputs.
  • By default, do not invoke the script until every required input has been initialized. The behavior for optional or missing inputs should be explicit and documented.
  • Expose the triggering input name as well as the current input snapshot.
  • Allow the script stage to declare an optional output topic for the computed result.
  • When an output topic is configured, publish the successful script result as a new outbound message on that topic. Do not mutate or republish the triggering input message unless an explicit future configuration requests both behaviors.
  • Construct a valid EdgeCommons envelope for the derived output, with the telemetry-processor route/stage as the producer and with source/correlation provenance sufficient to trace the calculation back to the triggering input.
  • Validate configured output topics against the UNS grammar and reserved-topic ownership rules. The implementation must guard against accidental feedback loops when an output topic overlaps the route subscriptions.
  • Preserve the current in-place result behavior when no output topic is configured so existing routes remain backward compatible.
  • Partition cached input state by the configured route key/source identity so values from different devices or production lines can never be combined accidentally.
  • State may be in-memory and restart-empty initially, but startup/reinitialization behavior must be deterministic and observable.
  • Preserve both existing script forms ({"script": "..."} and {"script": {"file": "..."}}) without behavior changes.
  • Keep the shared Lua/Rhai script contract aligned. Lua support is required for the OEE use case; Rhai should expose the equivalent input map.

Illustrative configuration

Exact field names can be refined during design, but the configuration should support a shape equivalent to:

{
  "script": {
    "file": "oee.lua",
    "inputs": {
      "plannedRunSeconds": { "device": "gw-fill-01", "signalId": "PlannedRunSeconds" },
      "running":           { "device": "gw-fill-01", "signalId": "FillerRunning" },
      "idealCycleSeconds": { "device": "gw-fill-01", "signalId": "IdealCycleSeconds" },
      "totalCount":        { "device": "gw-fill-01", "signalId": "TotalBottleCount" },
      "goodCount":         { "device": "gw-fill-01", "signalId": "GoodBottleCount" }
    },
    "output": {
      "topic": "ecv1/gw-fill-01/telemetry-processor/oee/data/current"
    }
  }
}

The script should receive a structure equivalent to:

inputs.plannedRunSeconds.value
inputs.running.value
inputs.idealCycleSeconds.value
inputs.totalCount.value
inputs.goodCount.value
trigger.name
trigger.value

Each input entry should also make quality and timestamps available when present so scripts can reject stale or bad-quality calculations.

On successful evaluation, the returned script value should become the body of a new message published to ecv1/gw-fill-01/telemetry-processor/oee/data/current, independent of which input triggered the calculation.

Acceptance criteria

  • A route with two or more configured inputs does not execute before all required inputs are initialized.
  • Updating any configured input invokes the script with the latest values of every input.
  • The script can identify which input triggered the evaluation.
  • Input quality and source/receive timestamps are available alongside values.
  • When an output topic is configured, every successful evaluation produces one output message on that topic.
  • The output message is a valid EdgeCommons envelope and identifies the telemetry-processor route/stage as its producer while retaining useful trigger provenance.
  • In configured-output mode, the triggering input is not mutated or republished on its source topic.
  • Without an output topic, existing single-message Lua and Rhai scripts retain their current behavior.
  • Output-topic configuration rejects invalid/reserved topics and prevents accidental subscription feedback loops.
  • State is isolated by route key/source identity; tests prove that two devices using the same signal IDs cannot contaminate each other.
  • Unknown or ambiguous selectors fail configuration validation rather than silently binding the wrong signal.
  • Unit tests cover out-of-order initialization, updates from each input, bad quality, repeated unchanged values, restart-empty state, device isolation, distinct input/output topics, and loop protection.
  • Configuration reference, scripting documentation, and an OEE-oriented example are added.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions