@@ -151,3 +151,158 @@ patched for runtime updates. Both paths are tracked in the AB CIP plan.
|
||||
per-family default values.
|
||||
- [`AbCipConnectionSize`](../../src/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipConnectionSize.cs) —
|
||||
range bounds + legacy-firmware threshold constants.
|
||||
|
||||
## Addressing mode
|
||||
|
||||
### What it is
|
||||
|
||||
CIP exposes two equivalent ways to address a Logix tag on the wire:
|
||||
|
||||
1. **Symbolic** — the request carries the tag's ASCII name and the controller
|
||||
parses + resolves the path on every read. This is the libplctag default
|
||||
and what every previous driver build has used.
|
||||
2. **Logical** — the request carries a CIP Symbol Object instance ID (a small
|
||||
integer assigned by the controller when the project was downloaded). The
|
||||
controller skips ASCII parsing entirely; the lookup is a single
|
||||
instance-table dereference.
|
||||
|
||||
Logical addressing is faster on the controller side and produces smaller
|
||||
request frames. The trade-off is that the driver has to learn the
|
||||
name → instance-id mapping once, by reading the `@tags` pseudo-tag at
|
||||
startup, and the resolution step has to repeat after a controller program
|
||||
download (instance IDs are re-assigned).
|
||||
|
||||
### Enum values
|
||||
|
||||
`AbCipDeviceOptions.AddressingMode` (`AddressingMode` enum, default
|
||||
`Auto`) takes one of three values:
|
||||
|
||||
| Value | Behaviour |
|
||||
|---|---|
|
||||
| `Auto` | Driver picks. **Currently resolves to `Symbolic`** — a future PR will plumb a real auto-detection heuristic (firmware version + symbol-table size). |
|
||||
| `Symbolic` | Force ASCII symbolic addressing on the wire. The historical default. |
|
||||
| `Logical` | Use CIP logical-segment / instance-ID addressing. Triggers a one-time `@tags` walk at the first read; subsequent reads consult the cached map. |
|
||||
|
||||
`Auto` is documented as "Symbolic-for-now" so deployments setting `Auto`
|
||||
explicitly today will silently flip to a real heuristic when one ships,
|
||||
matching the spirit of the toggle. Operators who want to pin the wire
|
||||
behaviour should set `Symbolic` or `Logical` directly.
|
||||
|
||||
### Family compatibility
|
||||
|
||||
Logical addressing depends on the controller implementing CIP Symbol Object
|
||||
class 0x6B with stable instance IDs. Older AB families don't:
|
||||
|
||||
| Family | Logical addressing supported? | Why |
|
||||
|---|---|---|
|
||||
| `ControlLogix` | yes | Native class 0x6B support, FW10+ |
|
||||
| `CompactLogix` | yes | Same wire protocol as ControlLogix |
|
||||
| `GuardLogix` | yes | Same wire protocol; safety partition is tag-level, not addressing-level |
|
||||
| `Micro800` | **no** | Firmware does not implement class 0x6B; instance-ID reads trip CIP "Path Segment Error" 0x04 |
|
||||
| `SLC500` / `PLC5` | **no** | Pre-CIP families; PCCC bridging only — no Symbol Object at all |
|
||||
|
||||
When `AddressingMode = Logical` is set on an unsupported family, the driver
|
||||
**falls back to Symbolic with a warning** (via `OnWarning`) instead of
|
||||
faulting. This keeps mixed-firmware deployments working — operators can ship
|
||||
a uniform "Logical" config across the fleet and let the driver downgrade
|
||||
the families that can't honour it.
|
||||
|
||||
The driver-level decision is exposed via
|
||||
`PlcFamilies.AbCipPlcFamilyProfile.SupportsLogicalAddressing` and resolved at
|
||||
`AbCipDriver.InitializeAsync` time; the resolved mode is stored on
|
||||
`DeviceState.AddressingMode` and threaded through every
|
||||
`AbCipTagCreateParams` from then on.
|
||||
|
||||
### One-time symbol-table walk
|
||||
|
||||
The first read on a Logical-mode device triggers a one-time `@tags` walk via
|
||||
`LibplctagTagEnumerator` (the same component used for opt-in controller
|
||||
browse). The driver caches the resulting name → instance-id map on
|
||||
`DeviceState.LogicalInstanceMap`; subsequent reads consult the cache without
|
||||
issuing another walk. The walk is gated by a per-device `SemaphoreSlim` so
|
||||
parallel first-reads serialise on a single dispatch.
|
||||
|
||||
The walk happens in `AbCipDriver.EnsureLogicalMappingsAsync` and runs only
|
||||
for devices that have actually resolved to `Logical`. Symbolic-mode devices
|
||||
skip the walk entirely. Walk failures are non-fatal: the
|
||||
`LogicalWalkComplete` flag still flips to `true` so the driver does not
|
||||
re-attempt indefinitely, and per-tag handles fall back to Symbolic addressing
|
||||
on the wire (libplctag's default).
|
||||
|
||||
A controller program download invalidates the instance IDs. There is no
|
||||
auto-invalidation today — operators trigger a fresh walk by either
|
||||
restarting the driver or calling `RebrowseAsync` (the same surface that
|
||||
clears the UDT template cache) with logic-mode plumbing extended in a
|
||||
future PR. For now, restart-on-download is the recommended workflow.
|
||||
|
||||
### libplctag wrapper limitation
|
||||
|
||||
The libplctag .NET wrapper (1.5.x) does **not** expose a public knob for
|
||||
instance-ID addressing. The driver translates Logical-mode params into
|
||||
libplctag attributes via reflection on
|
||||
`NativeTagWrapper.SetAttributeString("use_connected_msg", "1")` +
|
||||
`SetAttributeString("cip_addr", "0x6B,N")` — same best-effort fallback
|
||||
pattern as the Connection Size knob.
|
||||
|
||||
This means **Logical mode is observable end-to-end through the public
|
||||
driver surface and unit tests today**, but the actual wire behaviour
|
||||
remains Symbolic until either:
|
||||
|
||||
- the upstream libplctag .NET wrapper exposes the
|
||||
`UseConnectedMessaging` + `CipAddr` properties on `Tag` directly
|
||||
(planned in the upstream backlog), in which case the reflection no-ops
|
||||
cleanly, or
|
||||
- libplctag native gains post-create hot-update for `cip_addr`, in which
|
||||
case the call lands as intended.
|
||||
|
||||
The driver-level bookkeeping (resolved mode, instance-id map, family
|
||||
compatibility, fall-back warning) is fully wired so the upgrade path is
|
||||
purely a wrapper-version bump.
|
||||
|
||||
### Performance trade-off
|
||||
|
||||
| Symbolic addressing | Logical addressing |
|
||||
|---|---|
|
||||
| Works everywhere | Requires Symbol Object class 0x6B |
|
||||
| ASCII parse on every read (controller-side cost) | One-time walk; instance-id lookup thereafter |
|
||||
| No first-read latency | First read on a device pays the `@tags` walk |
|
||||
| Smaller code surface | Stale on program download — restart driver to re-walk |
|
||||
| Best for small / sparse tag sets | Best for >500-tag scans with stable controller |
|
||||
|
||||
For scan lists in the **single-digit-tag** range, the per-poll ASCII parse
|
||||
cost is invisible. For **medium** scan lists (~100 tags) the gain is real
|
||||
but small — typically 5–10% per CIP RTT depending on tag-name length. The
|
||||
break-even point is where the ASCII-parse overhead starts dominating,
|
||||
roughly **>500 tags** in a tight scan loop, which is also where libplctag's
|
||||
own request-packing benefits compound. Large MES / batch projects with
|
||||
many UDT instances are the canonical case.
|
||||
|
||||
### Driver config JSON
|
||||
|
||||
Bind the toggle through the driver-config JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"Devices": [
|
||||
{
|
||||
"HostAddress": "ab://10.0.0.5/1,0",
|
||||
"PlcFamily": "ControlLogix",
|
||||
"AddressingMode": "Logical"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`"Auto"`, `"Symbolic"`, and `"Logical"` parse case-insensitively. Omitting
|
||||
the field defaults to `"Auto"`.
|
||||
|
||||
### See also
|
||||
|
||||
- [`AbCipDriverOptions.AddressingMode`](../../src/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriverOptions.cs) —
|
||||
enum definition + per-value docstrings.
|
||||
- [`AbCipPlcFamilyProfile.SupportsLogicalAddressing`](../../src/ZB.MOM.WW.OtOpcUa.Driver.AbCip/PlcFamilies/AbCipPlcFamilyProfile.cs) —
|
||||
family compatibility table source-of-truth.
|
||||
- [`docs/drivers/AbServer-Test-Fixture.md`](AbServer-Test-Fixture.md) §
|
||||
"What it actually covers" — Logical-mode fixture coverage status.
|
||||
- [`AbCipAddressingModeBenchTests`](../../tests/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbCipAddressingModeBenchTests.cs) —
|
||||
scaffold for the wall-clock comparison; gated on `[AbServerFact]`.
|
||||
|
||||
Reference in New Issue
Block a user