node-red-contrib-omron-eip 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MANUAL.md ADDED
@@ -0,0 +1,457 @@
1
+ # Omron EtherNet/IP Nodes for Node-RED — User Manual
2
+
3
+ A complete guide to using the `node-red-contrib-omron-eip` nodes to read and write Omron
4
+ NX/NJ controller tags from your Node-RED flows. No programming required.
5
+
6
+ This manual assumes the nodes are already installed (see `DOCKER_INSTALL_AND_TEST.md` or the
7
+ README) and that you can open the Node-RED editor.
8
+
9
+ ---
10
+
11
+ ## Contents
12
+
13
+ 1. [Concepts: how it fits together](#1-concepts-how-it-fits-together)
14
+ 2. [The three nodes at a glance](#2-the-three-nodes-at-a-glance)
15
+ 3. [Setting up the PLC connection (config node)](#3-setting-up-the-plc-connection-config-node)
16
+ 4. [The read node — every field explained](#4-the-read-node-every-field-explained)
17
+ 5. [The write node — every field explained](#5-the-write-node-every-field-explained)
18
+ 6. [Variable names: scalars, arrays, structures, nesting](#6-variable-names-scalars-arrays-structures-nesting)
19
+ 7. [Message formats in and out](#7-message-formats-in-and-out)
20
+ 8. [Triggering: messages, polling, and on-deploy](#8-triggering-messages-polling-and-on-deploy)
21
+ 9. [Test Connection, Load and Validate Published Variables](#9-test-connection-load-and-validate-published-variables)
22
+ 10. [Error handling](#10-error-handling)
23
+ 11. [Common flow recipes](#11-common-flow-recipes)
24
+ 12. [Performance & best practices](#12-performance-best-practices)
25
+ 13. [Troubleshooting](#13-troubleshooting)
26
+
27
+ ---
28
+
29
+ ## 1. Concepts: how it fits together
30
+
31
+ Three pieces work together:
32
+
33
+ - **The controller (PLC).** Your Omron NX/NJ. It exposes *tags* (variables) over EtherNet/IP.
34
+ - **The connection (config node `omron-plc`).** A single shared connection to one controller.
35
+ You set it up once; many read/write nodes use it.
36
+ - **The operations (`omron read`, `omron write`).** The nodes you drag into flows to actually
37
+ read or write tags.
38
+
39
+ The key idea: **one connection, many operations.** You don't configure an IP on every node —
40
+ you create one PLC connection and point your read/write nodes at it. Behind the scenes, all
41
+ operations to that controller share a single, auto-reconnecting link.
42
+
43
+ A typical flow looks like:
44
+
45
+ ```
46
+ [ trigger ] → [ omron read ] → [ do something with the value ]
47
+ [ event ] → [ omron write ] → [ confirm / next step ]
48
+ ```
49
+
50
+ where *trigger* is anything that produces a message (an inject node, a timer, a dashboard
51
+ control, another flow).
52
+
53
+ ---
54
+
55
+ ## 2. The three nodes at a glance
56
+
57
+ | Node | Category | What it does |
58
+ |---|---|---|
59
+ | **omron-plc** | config | Holds the controller IP + messaging mode and the shared connection. Not placed on the canvas directly — created from a read/write node's PLC field. |
60
+ | **omron read** | Omron | Reads one or more tags. Outputs the values. Blue, download icon. |
61
+ | **omron write** | Omron | Writes one or more tags. Orange, upload icon. |
62
+
63
+ Both operational nodes have **two outputs**: the **top** output carries successful results,
64
+ the **bottom** output carries errors.
65
+
66
+ ---
67
+
68
+ ## 3. Setting up the PLC connection (config node)
69
+
70
+ You create the connection the first time you add a read or write node:
71
+
72
+ 1. Drag an **omron read** (or **omron write**) node onto the canvas and double-click it.
73
+ 2. Next to the **PLC** field, click the **pencil** icon to add a new connection.
74
+ 3. Fill in the config node:
75
+
76
+ | Field | What it is |
77
+ |---|---|
78
+ | **Name** | A friendly label, e.g. "Line 3 NX102". Optional but recommended — it's what you'll see in the PLC dropdown. |
79
+ | **Address** | The controller's IP address (the EtherNet/IP port your machine/container can reach), e.g. `192.168.250.1`. |
80
+ | **Messaging** | **UCMM** (recommended) or **Class 3 connected**. Leave on UCMM unless you have a specific reason (see below). |
81
+
82
+ 4. Click **Add** to save the connection, then **Done** on the node.
83
+ 5. **Deploy.** The connection is created when you deploy — not before.
84
+
85
+ ### UCMM vs Class 3
86
+
87
+ **Use UCMM** (the default) for virtually all setups, especially a direct PC-to-PLC link. It's
88
+ faster, parallelizes, and has no large-array limit.
89
+
90
+ **Class 3** only helps when traffic routes through a gateway, comms module, or backplane to
91
+ reach the controller. On a direct connection it's slower (it serializes requests) and can't
92
+ read very large arrays. If the controller rejects Class 3, the library silently falls back to
93
+ UCMM, so enabling it never breaks anything — it just may not help.
94
+
95
+ One config node per controller is the norm. If you have two PLCs, make two config nodes.
96
+
97
+ ---
98
+
99
+ ## 4. The read node — every field explained
100
+
101
+ Open an **omron read** node. Hover the small ⓘ icons in the editor for inline reminders; here's
102
+ the full reference.
103
+
104
+ **Connection**
105
+ - **Name** — optional canvas label. If blank, the node shows the tags it reads (e.g.
106
+ "read: Counter, Speed").
107
+ - **PLC** — which connection to use. Pick an existing config or create one.
108
+
109
+ **Variables**
110
+ - **Variables** — the list of tags to read, one per row. Free-form symbolic names (see
111
+ section 6). You can read scalars, whole arrays, whole structures, members, and elements.
112
+ - **Connect & validate** (button) — loads the live tag list from the controller for
113
+ autocomplete and checks each name (✓ found / ✗ not found). Requires a deployed PLC config
114
+ (section 9).
115
+
116
+ **Output**
117
+ - **Shape** — how results are emitted:
118
+ - *one message: payload = { name: value }* — all tags in a single message as an object.
119
+ - *one message per tag (topic = name)* — a separate message per tag, with `msg.topic` set
120
+ to the tag name and `msg.payload` to its value.
121
+ - **Msg override** — the name of a message property that, if present, replaces the configured
122
+ variable list at runtime. Default `variables` (i.e. set `msg.variables`). Lets a flow choose
123
+ what to read dynamically.
124
+
125
+ **Trigger**
126
+ - **Repeat** — milliseconds. If > 0, the node self-polls at that interval (in addition to
127
+ responding to incoming messages). 0 = only read when a message arrives.
128
+ - **Read once shortly after deploy** — performs one read ~1.5 s after deploy/restart, to fetch
129
+ an initial value with no trigger.
130
+
131
+ ---
132
+
133
+ ## 5. The write node — every field explained
134
+
135
+ Open an **omron write** node.
136
+
137
+ **Connection**
138
+ - **Name** — optional canvas label.
139
+ - **PLC** — which connection to use.
140
+
141
+ **Values**
142
+ - **Source** — where write values come from:
143
+ - *From incoming message* — values arrive in `msg.payload` when the node is triggered. The
144
+ Variables rows are **names only**.
145
+ - *Fixed value(s) set here* — values are configured in the node. Each Variables row gains a
146
+ **typed value** field (number / boolean / string / JSON). The incoming message only
147
+ triggers the write; its payload is ignored.
148
+
149
+ **Variables**
150
+ - **Variables** — the tags to write, one per row. In *Fixed* mode each row also has a value and
151
+ a type selector. In *From message* mode, names only.
152
+ - **Connect & validate** — same as the read node.
153
+
154
+ **Options**
155
+ - **Verified write** — after writing, the node reads the value back and confirms it matches
156
+ what you sent. A normal write already gets an accepted/error reply from the controller, which
157
+ is enough almost always; verified write additionally catches the rarer case where the
158
+ controller *accepts* the write but the value isn't what you intended afterward — e.g. the PLC
159
+ program clamps it to a limit (you send 5000, it stores 4000), or another device overwrites the
160
+ tag immediately. Use it for important values (setpoints, recipe parameters). It costs one
161
+ extra read per tag, so it's slightly slower — leave it off for normal or high-rate writes. It
162
+ only checks the moment right after writing; a later change to the tag can't be detected.
163
+
164
+ **Trigger**
165
+ - **Repeat** — milliseconds. If > 0, the node writes automatically at that interval. With
166
+ *Fixed* values, this can force/hold a tag with nothing wired upstream.
167
+ - **Write once shortly after deploy** — one write ~1.5 s after deploy/restart. With *Fixed*
168
+ values, sets a tag to a known state at startup.
169
+
170
+ ---
171
+
172
+ ## 6. Variable names: scalars, arrays, structures, nesting
173
+
174
+ Tags are addressed by their **symbolic name** exactly as defined in the controller. The same
175
+ text field handles every case — there are no separate "array mode" or "struct mode" controls.
176
+
177
+ | You want | Type the name | Read result / write value |
178
+ |---|---|---|
179
+ | A scalar | `Counter` | a number/boolean/string |
180
+ | A whole array | `MyArray` | a JS array `[…]` |
181
+ | One array element | `MyArray[3]` | a single value |
182
+ | A whole structure | `MyStruct` | a JS object `{…}` |
183
+ | A structure member | `MyStruct.Speed` | a single value |
184
+ | Element of an array in a struct | `MyStruct.History[2]` | a single value |
185
+ | Member of a struct in an array | `Machine.Axes[2].Position` | a single value |
186
+ | Deeper nesting | `Cell.Stations[1].Tools[3].Offset` | a single value |
187
+
188
+ **Whole vs. member.** Reading or writing a whole array/structure moves all of it in one
189
+ operation — the fast path for bulk data (section 12 of the install/test guide). Reading or
190
+ writing a single member touches just that piece.
191
+
192
+ **Writing whole structures safely.** A whole-structure write overwrites *every* member. If
193
+ another writer might change a member at the same time, write the specific member
194
+ (`MyStruct.Speed`) instead to avoid a read-modify-write race.
195
+
196
+ ---
197
+
198
+ ## 7. Message formats in and out
199
+
200
+ ### Read output
201
+
202
+ *One message* shape (default) — `msg.payload` is an object keyed by tag name:
203
+ ```json
204
+ { "Counter": 42, "Speed": 1500 }
205
+ ```
206
+ When reading a single tag, `msg.topic` is also set to that tag name.
207
+
208
+ *Per tag* shape — one message per tag:
209
+ ```
210
+ msg.topic = "Counter" msg.payload = 42
211
+ msg.topic = "Speed" msg.payload = 1500
212
+ ```
213
+
214
+ If some tags in a batch fail (e.g. one name is wrong), the good values still come through and
215
+ the failures appear on `msg.errors = { name: reason }`.
216
+
217
+ ### Write input (when Source = From incoming message)
218
+
219
+ **One configured variable** — send the bare value:
220
+ ```js
221
+ msg.payload = 1500
222
+ ```
223
+
224
+ **Multiple configured variables** — send an object keyed by name:
225
+ ```js
226
+ msg.payload = { "Setpoint": 1500, "Mode": 3, "Enable": true }
227
+ ```
228
+
229
+ **A whole structure or array as a value** — nest it under the variable name:
230
+ ```js
231
+ msg.payload = { "Recipe": { "Speed": 100, "Steps": [1, 2, 3] } }
232
+ msg.payload = { "Profile": [10, 20, 30, 40] }
233
+ ```
234
+
235
+ ### Write output
236
+
237
+ On success the message passes through with `msg.payload` set to the map of what was written,
238
+ e.g. `{ "Setpoint": 1500 }`. On failure, the error output carries `msg.error` (a readable
239
+ summary naming the variable and reason) and `msg.errors` (per-variable detail).
240
+
241
+ > **Data types matter.** Node-RED's inject node defaults `msg.payload` to **string**. Sending
242
+ > the string `"1500"` to a numeric tag fails. Set the inject (or your upstream node) to emit a
243
+ > **number** for numeric tags, **boolean** for BOOLs, **JSON** for arrays/objects.
244
+
245
+ ### Data type reference
246
+
247
+ The value type you choose (number / boolean / string / JSON) is a **Node-RED** type, not the
248
+ PLC type. The library knows each tag's real Omron type and encodes your value automatically.
249
+ The write node also has a built-in **Data type reference** button showing this table.
250
+
251
+ | Node-RED type | Sysmac / Omron types | How to enter the value | Range / notes |
252
+ |---|---|---|---|
253
+ | **number** | SINT | integer | -128 … 127 |
254
+ | **number** | INT | integer | -32,768 … 32,767 |
255
+ | **number** | DINT | integer | -2,147,483,648 … 2,147,483,647 |
256
+ | **number** | LINT | integer (64-bit) | ±9.22e18 — see 64-bit note |
257
+ | **number** | USINT | integer | 0 … 255 |
258
+ | **number** | UINT | integer | 0 … 65,535 |
259
+ | **number** | UDINT | integer | 0 … 4,294,967,295 |
260
+ | **number** | ULINT | integer (64-bit) | 0 … 1.84e19 — see 64-bit note |
261
+ | **number** | BYTE | integer (8-bit bit string) | 0 … 255 |
262
+ | **number** | WORD | integer (16-bit bit string) | 0 … 65,535 |
263
+ | **number** | DWORD | integer (32-bit bit string) | 0 … 4,294,967,295 |
264
+ | **number** | LWORD | integer (64-bit bit string) | 0 … 1.84e19 — see 64-bit note |
265
+ | **number** | REAL | float (single) | ~7 significant digits |
266
+ | **number** | LREAL | float (double) | ~15 significant digits |
267
+ | **number** | ENUM | integer (enum value) | 0 … 4,294,967,295 |
268
+ | **boolean** | BOOL | true / false | |
269
+ | **string** | STRING | text | up to 1986 characters (this controller) |
270
+ | **JSON** | ARRAY (of any type) | a JSON array, e.g. `[1,2,3]` | whole-array write |
271
+ | **JSON** | STRUCT (UDT) | a JSON object, e.g. `{"Speed":100}` | whole-structure write |
272
+ | **JSON** | UNION | a JSON object/array | layout is type-specific |
273
+ | **number** | DATE, DATE_AND_TIME | nanoseconds since 1970 (epoch ms × 1,000,000) | reads back as a date; verified to ms precision |
274
+ | **number** | TIME, TIME_OF_DAY | nanoseconds (a number) | duration; verified to ms precision |
275
+
276
+ **64-bit note (LINT / ULINT / LWORD):** these can exceed JavaScript's safe integer limit
277
+ (~9.0e15). Values within that range work as plain numbers; beyond it, precision can be lost
278
+ entering them as a JS number — handle very large 64-bit values with care.
279
+
280
+ **Date and time types (DATE, DATE_AND_TIME, TIME, TIME_OF_DAY):** send a **number of
281
+ nanoseconds**, with the value-type set to `number`.
282
+ - **DATE / DATE_AND_TIME** use nanoseconds since 1970-01-01 UTC — i.e. epoch milliseconds ×
283
+ 1,000,000. For example, `1767270896789000000` is 2026-01-01 12:34:56.789 UTC. They read back
284
+ as a date (an ISO timestamp in the payload).
285
+ - **TIME / TIME_OF_DAY** are a nanosecond duration — e.g. `5000000000` is 5 seconds.
286
+ - These are **verified to millisecond precision**. The full nanosecond value is a very large
287
+ (19-digit) integer, so digits below the millisecond can be lost when entered as a plain
288
+ number; values are reliable down to the millisecond. **String input is not accepted** for
289
+ these types — use a number.
290
+
291
+ **Out-of-range values are rejected** with a clear error (e.g. a BYTE only accepts 0–255).
292
+
293
+ ### Generating example JSON
294
+
295
+ Both nodes can generate an example of their message shape so you don't have to construct it by
296
+ hand:
297
+
298
+ - **Read node — "Show output JSON"** (in the Output section): generates an example of the
299
+ `msg.payload` this node will emit, based on the variables you've added and the selected
300
+ output shape. In one-message mode it shows the `{ name: value }` object; in per-tag mode it
301
+ shows the `{ topic, payload }` form.
302
+ - **Write node — "Show input JSON"** (shown in *From incoming message* mode): generates an
303
+ example of the `msg.payload` to send this node. For a single variable it shows the bare
304
+ value; for multiple it shows the `{ "VarName": value, ... }` object.
305
+
306
+ Both include a **Copy** button. Press **Test Connection, Load and Validate Published
307
+ Variables** first and the examples use the **real types** (numbers, booleans, strings,
308
+ arrays, structures) pulled from the controller; otherwise they show generic `<value>`
309
+ placeholders with a note reminding you to validate.
310
+
311
+ ---
312
+
313
+ ## 8. Triggering: messages, polling, and on-deploy
314
+
315
+ Both nodes act when triggered. There are three trigger sources, and they combine:
316
+
317
+ 1. **Incoming message** — the default. Any message arriving at the node's input fires it. Wire
318
+ an inject, a timer, a dashboard control, or another flow into it. (For writes in *From
319
+ message* mode, the message also carries the value.)
320
+
321
+ 2. **Repeat (self-poll)** — set Repeat to N ms and the node fires itself every N ms with no
322
+ upstream node. For reads, this is steady polling. For writes with *Fixed* values, this
323
+ forces/holds a tag.
324
+
325
+ 3. **Once after deploy** — fires a single operation ~1.5 s after the flow deploys/restarts.
326
+ Good for fetching an initial value or setting a startup state.
327
+
328
+ You can use any combination. A read node can self-poll *and* respond to messages. A write node
329
+ can fire once at deploy *and* on incoming events.
330
+
331
+ > **A note on writes that fire themselves:** continuously writing the same value is occasionally
332
+ > what you want (a heartbeat, holding an enable bit), but usually writes should happen in
333
+ > response to an event. Use Repeat/after-deploy on writes deliberately.
334
+
335
+ ---
336
+
337
+ ## 9. Test Connection, Load and Validate Published Variables
338
+
339
+ Both edit panels have a button labeled **"Test Connection, Load and Validate Published
340
+ Variables"** under the variable list. It:
341
+
342
+ - Connects to the controller (via the deployed PLC config),
343
+ - Loads every variable that is marked **"Publish Only"** (or Input/Output) on the controller,
344
+ - Gives you **autocomplete** as you type variable names,
345
+ - Marks each configured name with **✓** (exists on the controller) or **✗** (not found).
346
+
347
+ **It only works after you Deploy.** The button talks to the running flow, which only knows
348
+ about a PLC config once it's been deployed. If you click it on a brand-new, undeployed config
349
+ you'll see "config node not found or not deployed yet" — just Deploy first, then click it.
350
+
351
+ The button is a convenience for catching typos before runtime. It is **not required** —
352
+ reads and writes work whether or not you've validated. (If validation ever reports a connection
353
+ error, it means the editor backend couldn't reach the PLC; check the connection the same way as
354
+ any read.)
355
+
356
+ ---
357
+
358
+ ## 10. Error handling
359
+
360
+ Errors are surfaced three ways, so you can handle them however your flow prefers:
361
+
362
+ 1. **Second output (error port).** The bottom output emits a message on failure, with
363
+ `msg.error` describing what went wrong (and `msg.errors` for per-variable detail on writes).
364
+ Wire it to a debug node, a notification, a retry, etc.
365
+
366
+ 2. **Catch nodes.** Errors also raise through Node-RED's standard mechanism, so a **Catch** node
367
+ anywhere on the flow tab will receive them — useful for centralized error handling.
368
+
369
+ 3. **Status badge.** The colored dot under the node shows the latest state: green (ok), blue
370
+ (working), yellow (connecting / partial), red (error, with a short reason).
371
+
372
+ Common errors are decoded to plain language, e.g.:
373
+ - *"variable not found on the controller — check the name and that it is published"* (the tag
374
+ doesn't exist or isn't published to the network),
375
+ - *"type mismatch — expected an integer Number, got string"* (you sent the wrong data type),
376
+ - *"reply data too large — use UCMM for large arrays"* (a large array read over Class 3).
377
+
378
+ ---
379
+
380
+ ## 11. Common flow recipes
381
+
382
+ ### Read one tag on a button press
383
+ ```
384
+ [ inject (button) ] → [ omron read: Counter ] → [ debug ]
385
+ ```
386
+
387
+ ### Poll a dashboard of values every 250 ms
388
+ ```
389
+ [ omron read: Speed, Position, Status · Repeat 250ms ] → [ ui_text / chart ]
390
+ ```
391
+ No inject needed — the read self-polls. Put many tags in one read node; one request fetches
392
+ them all.
393
+
394
+ ### Write a setpoint from a dashboard slider
395
+ ```
396
+ [ ui_slider ] → [ change: set msg.payload to { "Setpoint": payload } ] → [ omron write (from message): Setpoint ]
397
+ ```
398
+ Or, for a single variable, skip the reshaping — the slider's numeric `msg.payload` goes
399
+ straight into a write node configured with the one variable `Setpoint`.
400
+
401
+ ### Set initial values when the flow starts
402
+ ```
403
+ [ omron write (fixed): Mode=1, Enable=true · Write once after deploy ]
404
+ ```
405
+ Nothing upstream needed; it fires once at deploy.
406
+
407
+ ### Heartbeat / watchdog
408
+ ```
409
+ [ omron write (fixed): Heartbeat=… · Repeat 1000ms ]
410
+ ```
411
+
412
+ ### Read, decide, write
413
+ ```
414
+ [ inject ] → [ omron read: Level ] → [ switch: Level > 80? ] → [ omron write (fixed): Pump=false ]
415
+ ```
416
+
417
+ ### Bulk-move a recipe (whole structure)
418
+ ```
419
+ [ inject (JSON: {"Recipe":{…}}) ] → [ omron write (from message): Recipe ]
420
+ [ inject ] → [ omron read: Recipe ] → [ debug ] // reads the whole struct back
421
+ ```
422
+
423
+ ---
424
+
425
+ ## 12. Performance & best practices
426
+
427
+ - **Batch reads.** Put multiple tags in one read node rather than many single-tag nodes — one
428
+ request fetches them all, far more efficiently.
429
+ - **Use whole arrays/structures for bulk data.** Reading `MyArray` in one go is dramatically
430
+ faster than reading `MyArray[0]`, `MyArray[1]`, … individually.
431
+ - **Pick a sensible poll rate.** On a direct connection an NX102 comfortably handles, for
432
+ example, ~50 tags every 100 ms, ~100 tags every 250 ms, ~200 tags every 500 ms. Don't poll
433
+ faster than you need; split fast-changing and slow-changing data onto separate read nodes
434
+ with different Repeat intervals.
435
+ - **Prefer member writes over whole-structure writes** unless you intend to set everything, to
436
+ avoid clobbering concurrent changes.
437
+ - **Match data types.** Numbers for numeric tags, booleans for BOOLs, JSON for arrays/objects.
438
+ - **Keep UCMM** unless you specifically route through a gateway.
439
+ - **One config node per PLC**, shared by all its read/write nodes.
440
+
441
+ ---
442
+
443
+ ## 13. Troubleshooting
444
+
445
+ | Symptom | Likely cause / fix |
446
+ |---|---|
447
+ | "config node not found or not deployed yet" | You used Connect & validate (or ran a flow) before deploying. **Deploy first.** |
448
+ | Write fails: "type mismatch …" | You sent the wrong type (commonly a string to a numeric tag). Set the inject/upstream payload type to number/boolean/JSON. |
449
+ | Read/write error: "variable not found" (CIP 0x05) | The tag name is wrong, or the tag isn't published to the network. In Sysmac Studio, make it a global variable with Network Publish = Publish Only, and transfer the project to the controller. |
450
+ | Node status stays yellow "connecting…" | The node can't reach the PLC. Verify the address, and that the machine/container running Node-RED can reach the PLC (e.g. ping it from the same host/container). |
451
+ | "reply data too large" on a big array | You're on Class 3; switch the config node to UCMM for large arrays. |
452
+ | Editor shows old node UI after an update | Hard-refresh the browser (Ctrl-Shift-R) to clear cached node HTML. |
453
+ | Connect & validate reports a connection error | The editor backend can't reach the PLC. Same checks as a failed read; confirm the deployed config's address is reachable. |
454
+ | Some tags read fine but others fail in a batch | The failures are on `msg.errors`; the good values still come through. Check the failed names. |
455
+
456
+ For protocol-level detail, performance numbers, and the underlying library, see the `omron-eip`
457
+ library docs (`README.md`, `EXAMPLES.md`, `PROTOCOL.md`, `NX102_PERFORMANCE.md`).