Files
lmxopcua/docs/drivers/AbCip-Operability.md
2026-04-26 02:15:50 -04:00

6.6 KiB
Raw Blame History

AB CIP — Operability knobs

Phase 4 of the AB CIP driver plan introduces operator-tunable behaviour that changes how the driver schedules per-tag traffic, deduplicates updates, and surfaces health — knobs that an operator typically reaches for after the address space is in place and the deployment is past the green-field phase. The Phase 3 doc (AbCip-Performance.md) covers connection-shape and read-strategy knobs; this doc is the home for the per-tag scheduling and operability levers as PRs land.

PR abcip-4.1 ships the first knob: per-tag Scan Rate (Kepware-parity scan classes).

Per-tag scan rate

What it is

A per-tag override of the OPC UA subscription's publishingInterval. The AB CIP driver mirrors the Galaxy hierarchy as a single OPC UA address space, so every tag served from one driver normally ticks at the publishing interval the client requested when it created the Subscription. This knob lets specific tags publish at a different cadence — fast HMI tags at 100 ms, batch / historian tags at 110 s — without forcing the operator to split tags into separate subscriptions or driver instances.

It is the Kepware "scan classes" model expressed per-tag. The same shape is already shipped in the S7 driver (S7TagDefinition.ScanGroup) and the AB Legacy / TwinCAT drivers; AB CIP adopts a leaner per-tag-only form because the CIP single-connection model means the practical knob a deployment reaches for is "this one tag, faster", not "every tag in this group".

How it interacts with OPC UA publishingInterval

OPC UA semantics:

  • The Subscription's publishingInterval is the upper bound on how often the server publishes a NotificationMessage. Each MonitoredItem also has its own samplingInterval; that's where this knob lands.
  • A per-tag samplingInterval shorter than the Subscription's publishingInterval means the server samples faster but only publishes at the next Subscription tick — clients may receive multiple values for one tag in a single Publish response.
  • A per-tag samplingInterval longer than the Subscription's publishingInterval is legal too — the server simply skips ticks for that tag.

AB CIP-side: the driver's SubscribeAsync receives one publishingInterval plus a list of tag references. With per-tag ScanRateMs it buckets the input list by resolved interval and registers one PollGroupEngine subscription per bucket. Each bucket runs an independent timer, so a 100 ms tag never waits for a 1000 ms tag's Task.Delay to expire.

Override knob

AbCipTagDefinition.ScanRateMs (int?, default null). null = use the subscription's default publishingInterval (legacy behaviour). Bind via driver config JSON:

{
  "Tags": [
    {
      "Name": "Motor1.Speed",
      "DeviceHostAddress": "ab://10.0.0.5/1,0",
      "TagPath": "Motor1.Speed",
      "DataType": "DInt",
      "ScanRateMs": 100
    },
    {
      "Name": "Motor1.RunHours",
      "DeviceHostAddress": "ab://10.0.0.5/1,0",
      "TagPath": "Motor1.RunHours",
      "DataType": "DInt",
      "ScanRateMs": 5000
    },
    {
      "Name": "Motor1.NamePlate",
      "DeviceHostAddress": "ab://10.0.0.5/1,0",
      "TagPath": "Motor1.NamePlate",
      "DataType": "String"
    }
  ]
}

Result: three buckets — 100 ms, 5000 ms, and the subscription default for NamePlate. UDT members inherit the parent tag's ScanRateMs at fan-out time, so a UDT declared at 100 ms publishes every member at 100 ms without the operator having to repeat the override on each member.

Floor and degenerate cases

  • PollGroupEngine floors every bucket at 100 ms — a ScanRateMs: 25 is clamped up. The floor matches the Modbus / S7 / TwinCAT floors and protects the wire from sub-mailbox-scan polling.
  • ScanRateMs: 0 and negative values are treated as unset — the tag falls back to the subscription default. Mis-typed config degrades, doesn't fault.
  • A ScanRateMs equal to the subscription default collapses into the same bucket as plain tags. The driver doesn't fragment poll loops when the override is redundant.
  • Tags whose names don't appear in the driver's tag map (typo / discovery miss) fall through to the subscription default — same "config typo degrades" stance as the rest of the driver.

Wire impact

Per-bucket independent timers do not parallelise CIP traffic. The driver serializes wire-side reads through its per-device libplctag handles, so a fast bucket and a slow bucket trade off against each other on the wire — the multi-rate split decouples cadence (the 100 ms bucket isn't queued behind the 1000 ms bucket's Task.Delay), not throughput. The wire still moves one CIP request at a time per device.

If you're reading a large tag set and the slow bucket starves the fast bucket, the lever is AbCipDeviceOptions.ConnectionSize (Phase 3) — pack more tags into one CIP RTT so the slow bucket finishes faster. Per-tag scan rate is a scheduling knob, not a throughput knob.

Comparison to Kepware scan classes

Kepware concept AB CIP equivalent
Scan class table (named groups → rate) implicit: each distinct ScanRateMs value is its own bucket
Default scan class OPC UA Subscription's publishingInterval
Per-tag scan class assignment AbCipTagDefinition.ScanRateMs
"Scan mode: Respect client" always — the OPC UA publishingInterval is the default
"Force write" / "Write through cache" not exposed — AB CIP writes always go to the wire

The leaner shape (per-tag rate, not named groups) keeps the JSON config flat and reflects how operators tend to use the knob in practice — a handful of "this specific tag needs to be fast" overrides on top of a sensible default, rather than a separate tier of scan-class definitions.

Verification

  • Unit: AbCipPerTagScanRateTests (tests/.../AbCip.Tests). Asserts bucketing math, default-rate collapse, UDT member inheritance, JSON DTO round-trip, and end-to-end cadence against the in-process fake.
  • Integration: AbCipPerTagScanRateTests (tests/.../AbCip.IntegrationTests). Drives two tags at 100 ms / 1000 ms against a live ab_server and asserts the bucket count + each tag receives the initial-data push.
  • E2E: scripts/e2e/test-abcip.ps1 — see the PerTagScanRate assertion.

Cross-references

  • docs/Driver.AbCip.Cli.md — there is no CLI surface change for this knob; scan rate is a config-time concern.
  • docs/drivers/AbCip-Performance.md — Phase 3 throughput knobs that pair with per-tag scan rate when a slow bucket starves a fast one.
  • S7 driver ScanGroup model in src/.../S7DriverOptions.cs — the named-group form of the same idea.