8fc147d8d4
Adds the driver-expansion program design (umbrella: universal Discover-backed browser + MTConnect, MQTT/Sparkplug B, BACnet/IP, SQL poll, Omron, Modbus RTU; MELSEC deferred) plus the per-driver research reports. All docs went through a 7-agent parallel review against the codebase before this commit. Highlights fixed in review: - universal browser: FOCAS FixedTree fills post-connect -> UntilStable settle + FixedTree.Enabled patch; MQTT reconciled to bespoke (was contradicting the program doc's SupportsOnlineDiscovery=false verdict) - modbus-rtu: SerialPort.ReadTimeout doesn't bound async BaseStream reads -> linked-CTS per-op deadline (R2-01 class); BCL enum reuse would leak System.IO.Ports into Contracts - bacnet: DiscoveryRediscoverPolicy enum name; UDP 47808 contention; live suite rewritten around unicast Who-Is + BBMD (broadcast doesn't cross VMs) - sql-poll: real tier registration via DriverFactoryRegistry.Register; blackhole gate must not docker-pause the shared central SQL Server - mqtt: Sparkplug v3.0 STATE topic form; first-in-repo proto codegen noted - omron: host hardcodes isIdempotent:false today (retry seam unshipped); v1 scopes UDTs to dotted-leaf access - mtconnect: SecurityClassification.ViewOnly; factory ParseEnum<T> pattern - program doc: both valid enum-serialization patterns; IRediscoverable is change-signal-gated; RTU P2 adds System.IO.Ports; label is host-side
362 lines
23 KiB
Markdown
362 lines
23 KiB
Markdown
# Omron PLC Driver — Research & Implementation Design
|
||
|
||
**Status:** Research / design proposal (no code yet).
|
||
**Author context:** Standard **Equipment-kind driver** for the OtOpcUa server, same shape as
|
||
Modbus / S7 / AbCip / TwinCAT / FOCAS — an in-process `IDriver` implementing the composable
|
||
capability interfaces (`IReadable` / `IWritable` / `ISubscribable` / `ITagDiscovery` /
|
||
`IHostConnectivityProbe`, optionally `IDriverBrowser` in AdminUI). Points are ordinary equipment
|
||
`Tag`s bound to the driver via `TagConfig.FullName`, authored on the `/uns` Tags tab.
|
||
|
||
---
|
||
|
||
## 0. TL;DR recommendation
|
||
|
||
- **Support both transports, CIP-first.** Ship an **Omron CIP (EtherNet/IP)** path for modern
|
||
**NJ/NX/NY (Sysmac)** controllers as Phase 1, then an **Omron FINS** path for legacy
|
||
**CJ/CS/CP/CV** as Phase 2. They cover disjoint hardware generations; neither subsumes the other.
|
||
- **Library:** `libplctag` (via `libplctag.NET`, MPL-2.0) for the CIP path — **reuses almost the
|
||
entire AbCip wire layer** (`plc=omron-njnx`). For the FINS path, **hand-roll** the framing
|
||
(it is a small, well-documented binary protocol) rather than take `HslCommunication` (see §1.3).
|
||
- **Browseability:** **CIP NJ/NX is only *conditionally* browsable, and NOT via libplctag's
|
||
`@tags` walker** — that is the single most important finding. **FINS is never browsable** (flat
|
||
memory banks, no symbol table). Details in §4.
|
||
|
||
---
|
||
|
||
## 1. Transport decision
|
||
|
||
Omron exposes two entirely different Ethernet protocols depending on controller generation.
|
||
|
||
### 1.1 The two transports and what they cover
|
||
|
||
| Transport | Port / framing | Controller families | Addressing model | Browsable? |
|
||
|---|---|---|---|---|
|
||
| **EtherNet/IP CIP** | TCP/UDP **44818** (EtherNet/IP), CIP explicit messaging | **NJ / NX / NY (Sysmac)**, and CJ2/CP-family with a CIP-capable EtherNet/IP unit | **Named variables** (symbolic tags), like Rockwell Logix | **Conditionally** — see §4 |
|
||
| **FINS** | **FINS/TCP** and **FINS/UDP** on port **9600** | **CJ / CS / CP / CV**, NSJ; NJ/NX also support *FINS-over-EtherNet/IP* for back-compat | **Memory areas** (CIO/WR/HR/AR/DM/EM banks) + word/bit offset | **Never** (flat memory, no symbol table) |
|
||
|
||
Key architectural fact: **NJ/NX are CIP-native**. Their "variables" are named tags reached by CIP
|
||
explicit messaging using a symbolic-segment request path — the same mechanism ControlLogix uses,
|
||
which is why AbCip's libplctag layer already speaks it (`plc=omron-njnx`). Sources:
|
||
[Omron store KB — Using CIP to Read/Write Network Variables with NJ/NX](https://store.omron.com.au/knowledge-base/1807412-using-cip-to-read-write-network-variables-with-nj-nx),
|
||
[NJ/NX CPU Built-in EtherNet/IP Port User's Manual (W506)](https://files.omron.eu/downloads/latest/manual/en/w506_nj_nx-series_cpu_unit_built-in_ethernet_ip_port_users_manual_en.pdf).
|
||
|
||
**FINS** is the classic protocol: a compact binary header (ICF/RSV/GCT, destination & source
|
||
net/node/unit addresses, SID) followed by a 2-byte command code and its parameters. **Memory Area
|
||
Read = `0x0101`**, **Memory Area Write = `0x0102`**. FINS/TCP prepends a 16-byte "FINS" TCP header
|
||
(the ASCII magic `46 49 4E 53` = "FINS", a length, and a command/error field) and requires a small
|
||
handshake frame to obtain a node address before the first command. Sources:
|
||
[Omron W227 FINS Commands Reference Manual](https://www.myomron.com/downloads/1.Manuals/Networks/W227E12_FINS_Commands_Reference_Manual.pdf),
|
||
[Wireshark FINS dissector](https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-omron-fins.c),
|
||
[FieldServer Omron FINS driver sheet](https://cdn.chipkin.com/assets/uploads/2016/jan/PDS_Omron_FINS.pdf).
|
||
|
||
### 1.2 FINS memory-area codes (for the TagConfig addressing model)
|
||
|
||
FINS memory-area codes are **access-width-specific** (a different code for word vs. bit access of
|
||
the same bank). Representative word-access codes:
|
||
|
||
| Bank | Word code | Bit code | Notes |
|
||
|---|---|---|---|
|
||
| CIO (Core I/O) | `0xB0` | `0x30` | word + bit addressable |
|
||
| WR (Work) | `0xB1` | `0x31` | word + bit addressable |
|
||
| HR (Holding) | `0xB2` | `0x32` | word + bit addressable |
|
||
| AR (Auxiliary) | `0xB3` | `0x33` | word + bit addressable |
|
||
| DM (Data Memory) | `0x82` | `0x02` | primary bulk data bank; word addressable |
|
||
| EM (Extended, bank 0) | `0xA0` | `0x20` | banked; code varies per EM bank (`0xA0`+bank) |
|
||
|
||
(Exact codes vary slightly by CPU family — the authoritative table is per-controller in W227 /
|
||
the CS/CJ comms manual. The driver must treat the code table as CPU-family-parameterised.) Source:
|
||
[Omron memory-area discussion](https://community.oxmaint.com/discussion-forum/omron-plc-memory-areas-understanding-ci-hr-ar-lr-dm-wr-em-extended-em-usage),
|
||
[W227 reference](https://www.myomron.com/downloads/1.Manuals/Networks/W227E12_FINS_Commands_Reference_Manual.pdf).
|
||
|
||
### 1.3 .NET library options — license analysis
|
||
|
||
| Library | Transports | License | Maturity / .NET 10 | Verdict |
|
||
|---|---|---|---|---|
|
||
| **`libplctag.NET`** (wraps native `libplctag`) | **CIP** (`plc=omron-njnx`); **also Modbus** | **native core dual-licensed MPL-2.0 / LGPL-2.1; .NET wrapper MPL-2.0** — permissive, commercial-OK, already vetted & shipped in this repo for AbCip | Mature, actively maintained; **already a proven dependency** (AbCip runs on it, net10.0) | **CIP path: adopt.** Zero new license review — same dep AbCip already carries. |
|
||
| **`HslCommunication`** | FINS **and** CIP (`OmronFinsNet`, `OmronCipNet`) | **The maintained/commercial builds are NOT free** — the original author moved HSL to a **paid commercial license**; only a frozen old community fork ([`HslCommunication-Community`](https://github.com/HslCommunication-Community/HslCommunication-Community)) remains under the last free (MIT-era) snapshot. The "MIT" claim in casual sources refers to that **stale fork**, not current releases. | Broad but the free fork is unmaintained | **Do NOT take a dependency.** License risk + the free build is abandoned. Use only as a *protocol reference* to cross-check hand-rolled framing. |
|
||
| **Hand-rolled FINS** | FINS/TCP + FINS/UDP | our own code, no third-party license | n/a — FINS framing is ~200 lines | **FINS path: hand-roll.** Framing is simple and fully documented (W227 + Wireshark dissector); mirrors how `ModbusTcpTransport` is hand-rolled in this repo. |
|
||
| `Sres.Net.EEIP` | EtherNet/IP (generic CIP) | permissive | Focused on generic CIP/assembly, not Omron symbolic variable access; weaker than libplctag for named-tag read/write | Not needed — libplctag already covers CIP. |
|
||
| `Plc.Omron.Standard` (NuGet) | FINS | permissive but tiny/low-adoption | Immature | Reference only. |
|
||
|
||
**Recommendation:** **libplctag.NET for CIP** (reuse AbCip's proven stack), **hand-rolled FINS
|
||
transport** for the memory-area path. This keeps the dependency surface identical to what the repo
|
||
already ships and dodges the HslCommunication license trap.
|
||
|
||
---
|
||
|
||
## 2. Capability mapping
|
||
|
||
The driver implements the same capability set as AbCip/Modbus. Both transports map cleanly onto the
|
||
composable interfaces in `Core.Abstractions`.
|
||
|
||
### 2.1 CIP path (NJ/NX) — reuses AbCip machinery
|
||
|
||
- **Connect / `InitializeAsync`:** libplctag Forward-Open per device. Canonical host address form
|
||
mirroring AbCip's `ab://gateway[:port]/cip-path`, e.g. `omron://gateway/path` — but for NJ/NX the
|
||
libplctag attribute string is `protocol=ab-eip&gateway=<ip>&path=<cip-path>&plc=omron-njnx&name=<tag>`.
|
||
(Community-confirmed: NJ/NX needs `plc=omron-njnx` and a CIP-bridging `path`, e.g. `18,<ip>`.)
|
||
Source: [libplctag Omron NJ/NX group thread](https://groups.google.com/g/libplctag/c/bU5MVQrPsOw).
|
||
- **Read (`IReadable`):** direct reuse of AbCip's per-tag runtime pattern — create tag handle, `ReadAsync`,
|
||
`GetStatus`, decode. Batch-read + per-host resilience (`IPerCallHostResolver`) reuse verbatim.
|
||
- **Write (`IWritable`):** full read/write. NJ/NX BOOL-in-word and array handling map onto AbCip's
|
||
existing `EncodeValue` / bit-RMW code paths.
|
||
- **Subscribe (`ISubscribable`):** **poll-based** via the shared `PollGroupEngine` (identical to
|
||
AbCip/Modbus — Omron CIP has no native subscription/unsolicited push for explicit messaging).
|
||
- **Discover (`ITagDiscovery`):** emit pre-declared tags always; controller-side browse is the
|
||
**caveat** — see §4 (libplctag's `@tags` walker does **not** work on Omron).
|
||
- **Health/Probe (`IHostConnectivityProbe`):** reuse AbCip's probe-loop pattern (cheap tag read at
|
||
interval → `HostState` transitions).
|
||
|
||
**Reuse estimate: ~70–80% of AbCip's driver body is directly reusable for the CIP path.** The
|
||
honest way to capture this is to **extract a shared CIP core** (tag runtime, poll wiring, status
|
||
mapping, host-address parsing, resilience seam) that both AbCip and OmronCip consume — rather than
|
||
copy-paste. The Omron-specific deltas are: the `plc=omron-njnx` attribute string, Omron's data-type
|
||
code set (see §2.3), string encoding differences, and the browse story.
|
||
|
||
### 2.2 FINS path (CJ/CS/CP) — new transport, Modbus-shaped
|
||
|
||
- **Connect:** open TCP to `:9600`, send the FINS/TCP node-address-request handshake, cache the
|
||
assigned client node number. (FINS/UDP variant skips the handshake.)
|
||
- **Read:** build a `0x0101` Memory Area Read frame (area code + 3-byte address + word count),
|
||
send, parse the 2-byte end-code + payload. Word-oriented; multi-word reads for 32/64-bit types.
|
||
- **Write:** `0x0102` Memory Area Write, symmetric. Bit writes use the bit-access area codes; word
|
||
writes use the word codes. RMW not required for FINS bit writes (bit codes address bits directly).
|
||
- **Subscribe:** poll-based via `PollGroupEngine` (FINS has no subscription).
|
||
- **Discover:** **pre-declared tags only** — there is no symbol table to enumerate (§4).
|
||
- **Probe:** cheap DM-word read at interval.
|
||
|
||
The FINS transport is structurally a sibling of `ModbusTcpTransport` (`IModbusTransport`): a thin
|
||
framed request/response codec behind an `IFinsTransport` seam, unit-testable against a byte-level
|
||
fake.
|
||
|
||
### 2.3 Data-type mapping
|
||
|
||
Omron CIP (NJ/NX) uses IEC 61131 variable types that align closely with the existing
|
||
`AbCipDataType` / `DriverDataType` set; FINS is raw words that the TagConfig must type explicitly.
|
||
|
||
| Omron type | CIP (NJ/NX) | FINS (word interpretation) | OPC UA / `DriverDataType` |
|
||
|---|---|---|---|
|
||
| BOOL | native BOOL | bit-area read, or bit N of a word | Boolean |
|
||
| BYTE/USINT/SINT | 1-byte | low byte of word | Byte / SByte |
|
||
| WORD/UINT/INT | 2-byte | 1 word | UInt16 / Int16 |
|
||
| DWORD/UDINT/DINT | 4-byte | 2 words (endianness matters) | UInt32 / Int32 |
|
||
| LWORD/ULINT/LINT | 8-byte | 4 words | UInt64 / Int64 |
|
||
| REAL | IEEE-754 32 | 2 words | Float |
|
||
| LREAL | IEEE-754 64 | 4 words | Double |
|
||
| STRING | CIP string | word-packed ASCII, fixed length | String |
|
||
| DATE_AND_TIME/TIME | Sysmac time types | banked words | DateTime (best-effort) |
|
||
|
||
**Word/byte-order caveat (FINS):** Omron stores multi-word values with a specific word order that
|
||
differs from naive concatenation; the FINS codec must expose a per-tag or per-device
|
||
byte/word-swap option (same class of concern the S7/Modbus drivers already handle). CIP via
|
||
libplctag handles endianness internally.
|
||
|
||
---
|
||
|
||
## 3. TagConfig JSON shape
|
||
|
||
`TagConfig.FullName` (or a JSON blob under it) carries the driver-specific addressing. Following the
|
||
AbCip equipment-tag parser convention (`AbCipEquipmentTagParser`: a leading `{` marks a TagConfig
|
||
blob; camelCase property names; strict enum reads via `TagConfigJson.TryReadEnumStrict`), the two
|
||
transports need distinct shapes discriminated by a `transport` field.
|
||
|
||
### 3.1 CIP (NJ/NX) — named-variable addressing
|
||
|
||
```json
|
||
{
|
||
"transport": "Cip",
|
||
"deviceHostAddress": "omron://10.100.0.40/1,0",
|
||
"tagName": "Conveyor.Speed",
|
||
"dataType": "Real",
|
||
"isArray": false,
|
||
"arrayLength": 1,
|
||
"writable": true
|
||
}
|
||
```
|
||
|
||
(`tagName` is the Sysmac global-variable name; near-identical to AbCip's `tagPath`. The CIP path
|
||
reuses AbCip's `isArray`/`arrayLength`/`writable`/`dataType` semantics verbatim.)
|
||
|
||
### 3.2 FINS — memory-area addressing
|
||
|
||
```json
|
||
{
|
||
"transport": "Fins",
|
||
"deviceHostAddress": "fins://10.100.0.41:9600/0.1.0",
|
||
"memoryArea": "DM",
|
||
"address": 100,
|
||
"bit": null,
|
||
"dataType": "Int16",
|
||
"wordSwap": false,
|
||
"isArray": false,
|
||
"arrayLength": 1,
|
||
"writable": true
|
||
}
|
||
```
|
||
|
||
- `memoryArea` ∈ `{CIO, WR, HR, AR, DM, EM}` (+ optional `emBank` for extended memory); the driver
|
||
maps the pair `(memoryArea, bit==null?word:bit)` to the correct FINS area code (§1.2).
|
||
- `address` = word offset; `bit` = 0–15 for bit access (null ⇒ word access).
|
||
- `deviceHostAddress` encodes the FINS destination `net.node.unit` triple.
|
||
- `wordSwap` handles Omron multi-word ordering for 32/64-bit types.
|
||
|
||
---
|
||
|
||
## 4. BROWSEABILITY VERDICT (transport-dependent) — the critical finding
|
||
|
||
### 4.1 FINS — **NOT browsable. Full stop.**
|
||
FINS addresses flat memory banks with no symbol/metadata table on the wire. There is nothing to
|
||
enumerate. The AdminUI must offer only manual entry (memory-area dropdown + word/bit offset). No
|
||
`IDriverBrowser` for the FINS transport.
|
||
|
||
### 4.2 CIP (NJ/NX) — **browsable in principle, but NOT via libplctag's `@tags` walker.**
|
||
|
||
This is the finding to flag loudly and to **coordinate with the AbCip-browseability audit**:
|
||
|
||
- The AbCip browse mechanism relies on libplctag's `@tags` pseudo-tag (CIP Symbol Object class
|
||
`0x6B`) to walk the controller symbol table. **libplctag's tag-listing works only on
|
||
ControlLogix / CompactLogix — it does NOT work on Omron NJ/NX**, returning `ErrorUnsupported` /
|
||
`PLCTAG_ERR_NOT_FOUND`. Confirmed upstream:
|
||
[libplctag #466 "Support for Omron NX/NJ variable listing"](https://github.com/libplctag/libplctag/issues/466),
|
||
[libplctag.NET #371 "List all tags in Omron NJ: ErrorUnsupported"](https://github.com/libplctag/libplctag.NET/issues/371).
|
||
- So the naive assumption "CIP controller-tag enumeration IS browsable, reuse the AbCip walker"
|
||
is **only half true for Omron**: the *protocol capability* exists (published variables are
|
||
reachable), but Omron NJ/NX exposes the tag list through a **different CIP object/service than
|
||
Rockwell** and libplctag hasn't implemented the Omron variant.
|
||
- **Additional gate:** on NJ/NX a variable is only reachable over the network at all if the
|
||
programmer set its **Network Publish** attribute (`Publish Only`) in **Sysmac Studio**. Tags
|
||
without Network Publish are invisible to any external client. And when *no* user variables are
|
||
published, an enumeration returns only the reserved system variables (names starting with `_`).
|
||
Source: [Omron NJ/NX EtherNet/IP User's Manual (W506)](https://files.omron.eu/downloads/latest/manual/en/w506_nj_nx-series_cpu_unit_built-in_ethernet_ip_port_users_manual_en.pdf),
|
||
[Omron store KB](https://store.omron.com.au/knowledge-base/1807412-using-cip-to-read-write-network-variables-with-nj-nx).
|
||
|
||
**Verdict:** CIP-Omron browse is achievable but requires **new protocol work, not free reuse**.
|
||
Two viable routes, in preference order:
|
||
|
||
1. **Import the Sysmac Studio tag export** (offline). NJ/NX projects export the published global
|
||
variable table (CSV/tag file). An AdminUI importer that parses this into candidate TagConfigs is
|
||
the *lowest-risk, highest-value* browse experience and needs no online CIP enumeration at all.
|
||
2. **Implement the Omron CIP tag-list service directly** (online `IBrowseSession`). Omron NJ/NX
|
||
supports reading the published-variable list via a CIP service Rockwell-differently; this means
|
||
hand-writing the request/response against the NJ/NX EtherNet/IP object model (not going through
|
||
libplctag's `@tags`). This is a research-grade effort — feasible (third-party tools like Kepware
|
||
discover Omron published tags) but non-trivial.
|
||
|
||
### 4.3 Browse-seam design (CIP path only)
|
||
|
||
If/when online CIP browse is implemented, it fits the existing seam exactly (mirroring
|
||
`OpcUaClient.Browser` / `Galaxy.Browser`, which live as separate `*.Browser` projects registered in
|
||
AdminUI DI and indexed by `DriverType`):
|
||
|
||
- New project `ZB.MOM.WW.OtOpcUa.Driver.OmronCip.Browser` implementing `IDriverBrowser`
|
||
(`DriverType = "OmronCip"`), `OpenAsync(configJson)` → `IBrowseSession`.
|
||
- `IBrowseSession`: `RootAsync` = one folder per device; `ExpandAsync` = published global variables
|
||
(and struct-member fan-out for UDT-typed variables, reusing AbCip's template-decode pattern);
|
||
`AttributesAsync` = data type / access / array shape side-panel.
|
||
- **Share a CIP-browse core with AbCip *only if* the enumeration transport is unified.** Since
|
||
Omron's tag-list service differs from Rockwell's, the honest boundary is: **share the
|
||
`BrowseNode`/`IBrowseSession` plumbing and struct-fan-out logic; do NOT expect to share the
|
||
wire-level enumerator.** Recommend a common `Cip.Browsing` helper library the AbCip browser (if
|
||
built) and OmronCip browser both consume for tree-shaping, with transport-specific enumerators
|
||
underneath. **Coordinate this boundary with the AbCip-browseability audit** so the two efforts
|
||
agree on the shared-core seam.
|
||
|
||
Note: the driver is still fully usable **without** a browser — like every driver, unmapped types
|
||
fall back to the raw-JSON TagConfig editor, and pre-declared tags always work. Browse is a
|
||
convenience layer, not a correctness dependency.
|
||
|
||
---
|
||
|
||
## 5. Test-fixture strategy
|
||
|
||
Consistent with the repo's Docker-fixture model (driver sims on the Linux host `10.100.0.35`,
|
||
controlled via `lmxopcua-fix`, compose files under `tests/.../Docker/`).
|
||
|
||
### 5.1 FINS
|
||
- **Hand-rolled byte-level fake** for unit tests (mirror the Modbus `IModbusTransport` fake) — the
|
||
primary and most valuable test surface; deterministic, no container.
|
||
- **Open-source FINS simulators** for an integration fixture:
|
||
[`hiroeorz/omron-fins-simulator`](https://github.com/hiroeorz/omron-fins-simulator) (Ruby, UDP
|
||
9600) and [`ahmadfarisfs/fins_simulator_omron`](https://github.com/ahmadfarisfs/fins_simulator_omron).
|
||
Neither ships a Docker image → wrap in a small `Dockerfile` under
|
||
`tests/Drivers/.../Omron/Docker/` and register with the `project=lmxopcua` label. Both are
|
||
UDP-oriented; FINS/TCP handshake coverage stays on the hand-rolled fake.
|
||
- Omron's own **CX-Simulator** speaks FINS but is Windows-only, license-gated (bundled in CX-One,
|
||
not separately purchasable) → **not** a CI fixture; reserve for a manual/live gate against real
|
||
hardware if available.
|
||
|
||
### 5.2 CIP (NJ/NX)
|
||
- **No credible free NJ/NX CIP simulator exists.** libplctag's own test harness targets Rockwell.
|
||
Options, in order:
|
||
- **Hand-rolled `IAbCipTagRuntime`-style fake** (Omron variant) for read/write/decode unit tests —
|
||
the same seam AbCip uses (`IAbCipTagRuntime` / factory injection) makes the driver fully
|
||
unit-testable without hardware. This is the primary CI surface.
|
||
- **Live gate against real NJ/NX hardware** (env-gated `Category=LiveIntegration`, skips cleanly
|
||
when the env var is absent — the pattern the HistorianGateway live suite already uses). This is
|
||
the only way to truly validate the `plc=omron-njnx` libplctag path, Network-Publish behavior,
|
||
and Omron string/array quirks. Flag as an **infra-gated known limitation** until hardware is on
|
||
the bench.
|
||
|
||
---
|
||
|
||
## 6. Effort / risk / phasing
|
||
|
||
**Recommended order: CIP-first, then FINS.**
|
||
|
||
Rationale: (a) CIP targets the modern/current Omron line (NJ/NX/NY) most likely in new installs;
|
||
(b) the CIP path **reuses 70–80% of the already-shipped, already-hardened AbCip stack** (libplctag,
|
||
poll engine, resilience, status mapping), so it is the fastest path to a working read/write driver;
|
||
(c) FINS, while simpler as a protocol, is a *net-new* transport codec with no reuse and targets
|
||
legacy hardware.
|
||
|
||
### Phase 1 — Omron CIP read/write (NJ/NX) · *Low–Medium effort*
|
||
- Extract a shared CIP core from AbCip (or, pragmatically, copy AbCip and specialise) →
|
||
`Driver.OmronCip` + `Driver.OmronCip.Contracts`, `DriverType="OmronCip"`, `plc=omron-njnx`.
|
||
- Capabilities: Connect/Read/Write/Subscribe(poll)/Probe; pre-declared tags via `ITagDiscovery`.
|
||
- Typed tag editor: copy `AbCipTagConfigModel`/editor, register in `TagConfigEditorMap` +
|
||
`TagConfigValidator`.
|
||
- Tests: hand-rolled runtime fake + env-gated live gate.
|
||
- **Risk:** libplctag Omron string/array edge cases (community-reported); word/UDT quirks. Medium.
|
||
|
||
### Phase 2 — Omron FINS read/write (CJ/CS/CP) · *Medium effort*
|
||
- New `IFinsTransport` codec (TCP+UDP, `0x0101`/`0x0102`, area-code table, word-swap) behind the
|
||
same `IDriver` shell; memory-area TagConfig shape (§3.2); manual-entry editor (no browse).
|
||
- Tests: byte-level fake (primary) + open-source simulator container (secondary).
|
||
- **Risk:** per-CPU-family area-code/word-order variance. Medium. No dependency/license risk (hand-rolled).
|
||
|
||
### Phase 3 — CIP online browse (NJ/NX) · *High effort / research-grade*
|
||
- Sysmac Studio tag-export importer first (cheap, high value), then optional online Omron CIP
|
||
tag-list `IBrowseSession` (`Driver.OmronCip.Browser`) — **not** libplctag `@tags`.
|
||
- **Coordinate the shared CIP-browse-core boundary with the AbCip-browseability audit.**
|
||
- **Risk:** High — Omron's tag-list service is undocumented-in-libplctag and Rockwell-different.
|
||
|
||
### Top risks (summary)
|
||
1. **CIP browse is not free reuse.** libplctag's tag-list walker does not work on Omron NJ/NX
|
||
(`ErrorUnsupported`); online browse needs net-new Omron-specific CIP work, and even then only
|
||
Network-Published variables are visible. Mitigate by shipping the offline Sysmac-export importer
|
||
first and treating online browse as a later research phase.
|
||
2. **No free NJ/NX CIP simulator** → CIP correctness for the real wire (string/array/UDT/Network-
|
||
Publish semantics) can only be proven on live hardware behind an env-gated `LiveIntegration`
|
||
suite; treat as an infra-gated known limitation until an NJ/NX is on the bench.
|
||
3. **(Secondary) FINS per-family variance** in memory-area codes and multi-word ordering — contain
|
||
with a CPU-family-parameterised code table + per-tag `wordSwap`.
|
||
|
||
---
|
||
|
||
## Sources
|
||
- [Omron store KB — Using CIP to Read/Write Network Variables with NJ/NX](https://store.omron.com.au/knowledge-base/1807412-using-cip-to-read-write-network-variables-with-nj-nx)
|
||
- [Omron NJ/NX CPU Built-in EtherNet/IP Port User's Manual (W506)](https://files.omron.eu/downloads/latest/manual/en/w506_nj_nx-series_cpu_unit_built-in_ethernet_ip_port_users_manual_en.pdf)
|
||
- [libplctag #466 — Support for Omron NX/NJ variable listing](https://github.com/libplctag/libplctag/issues/466)
|
||
- [libplctag.NET #371 — List all tags in Omron NJ: ErrorUnsupported](https://github.com/libplctag/libplctag.NET/issues/371)
|
||
- [libplctag Omron NJ/NX group thread (plc=omron-njnx, CIP path)](https://groups.google.com/g/libplctag/c/bU5MVQrPsOw)
|
||
- [Omron W227 FINS Commands Reference Manual](https://www.myomron.com/downloads/1.Manuals/Networks/W227E12_FINS_Commands_Reference_Manual.pdf)
|
||
- [Omron NX-series CPU Unit FINS Function User's Manual (W596)](https://files.omron.eu/downloads/latest/manual/en/w596_nx-series_-_cpu_unit_fins_function_users_manual_en.pdf)
|
||
- [Wireshark FINS dissector (framing reference)](https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-omron-fins.c)
|
||
- [FieldServer Omron FINS protocol driver sheet](https://cdn.chipkin.com/assets/uploads/2016/jan/PDS_Omron_FINS.pdf)
|
||
- [Omron memory-area overview (CIO/HR/AR/DM/WR/EM)](https://community.oxmaint.com/discussion-forum/omron-plc-memory-areas-understanding-ci-hr-ar-lr-dm-wr-em-extended-em-usage)
|
||
- [HslCommunication — commercial-license fork situation](https://github.com/HslCommunication-Community/HslCommunication-Community)
|
||
- [omron-fins-simulator (open-source FINS sim)](https://github.com/hiroeorz/omron-fins-simulator)
|
||
- [fins_simulator_omron (open-source FINS sim)](https://github.com/ahmadfarisfs/fins_simulator_omron)
|
||
</content>
|
||
</invoke>
|