# Omron

> 连接 Omron PLC：CJ、CS 和 CP 控制器通过 FINS（UDP、TCP 或串行线路上的 Host Link）按存储区地址读取，NJ、NX 和 NY 控制器通过 EtherNet/IP 按变量名读取；FINS 设置、地址写法、数据类型、读取、写入和设备发现。

**Omron** 驱动用 Omron PLC 的两种语言之一与之对话，用哪一种按设备选择。CJ、CS 或 CP 控制器，以及设成 CJ 存储模式的 NJ 或 NX，通过 FINS 按存储区地址读取，地址写法与 Omron 编程软件一致：`D100`、`W0.01`、`CIO100.05`。NJ、NX 或 NY 控制器通过 EtherNet/IP 按它发布到网络的变量名读取，例如 `Level` 或 `Motor.Speed`。同一台工作站可以在同一个驱动下并排读取这两类设备。在 [Connector](connector) 里的 Omron 驱动下面添加一台设备，并选择它的**协议**；面板只显示这个协议用得到的字段。

## 协议 {#protocols}

| 协议 | 控制器 | 点位按什么寻址 | 端口 |
| --- | --- | --- | --- |
| **FINS** | CJ1、CJ2、CS1、CP1 以及其他带以太网端口或以太网单元的 CJ、CS 和 CP 控制器；处于 CJ 存储模式的 NJ 或 NX | **存储区地址**，例如 `D100` 或 `W0.01` | 默认 9600 |
| **NJ/NX 点位（按名称）** | NJ、NX 和 NY 控制器，例如 NJ501、NJ301、NJ101、NX1P2、NX102、NX502、NX701 和 NY5 | **变量名**，例如 `Level` 或 `Motor.Speed` | 无：EtherNet/IP 在它的标准端口 44818 上应答 |

默认是 FINS，新设备从 FINS 开始。协议是设备的一项设置，所以一台 CJ2 和一台 NX1P2 可以挂在同一个驱动下，各自用自己的语言读取。切换协议时，FINS 设置按离开时的样子保留下来，等 FINS 回来时再用；设备的每个点位从此按新协议读它的地址：它的**数据类型**会调整为该地址在新协议下能容纳的类型，而新协议读不了的地址会在点位页面上一直标出，直到改正为止。

## 连接 {#connection}

| 字段 | 含义 | 默认值 |
| --- | --- | --- |
| **主机** | PLC 以太网端口或以太网单元的 IP 地址或主机名。 | 空 |
| **协议** | **FINS** 或 **NJ/NX 点位（按名称）**。下面一行说明当前协议读取什么：“CJ、CS 和 CP 控制器的存储区，以及处于 CJ 存储模式的 NJ/NX 的存储区。”或“NJ、NX 或 NY 控制器发布到网络的变量，按名称通过 EtherNet/IP 读取。” | FINS |
| **端口** | 仅 FINS：PLC 应答 FINS 的端口，UDP 和 TCP 相同，1 到 65535。框留空时 9600 以灰色显示，设备就在这个端口上被拨号连接。按名称读取的设备没有端口框。 | 9600 |
| **轮询间隔（ms）** | 整台设备的读取周期，用于没有自己周期的点位。 | 1,000 |

设备标题栏显示拼好的 endpoint。FINS 设备把全部设置都写进去，节点为 0 表示自动，例如 `omron://192.168.250.1:9600?protocol=fins&transport=udp&network=0&node=0&unit=0&source=0&timeoutMs=2000`。按名称读取的设备只写出主机和协议：`omron://192.168.250.1?protocol=njnx`。每个点位可以设置自己的**轮询间隔（ms）**；点位上的值为 0 时回到设备的间隔。

## 准备控制器 {#prepare-the-controller}

按名称读取的 NJ、NX 或 NY 控制器只提供它发布到网络的全局变量。发布是每个变量的一个属性，在 Sysmac Studio 中设置并传送到控制器。通过 FINS 读取的控制器不需要这种设置。

| 设置 | 在 Sysmac Studio 中的位置 | 没有它时 |
| --- | --- | --- |
| 工作站读写的每一个全局变量，**Network Publish** 都设为 **Publish Only**（如果标签数据链接也使用该变量，则设为 Input 或 Output） | **Programming > Data > Global Variables** 中该变量的 **Network Publish** 列。把工程传送到控制器（**Controller > Synchronize**）。 | 控制器应答说它不提供这个名称的变量，与不存在的名称完全一样。该点位读作质量不可用，设备的其他点位照常读取。 |

程序的变量（局部变量）无法发布：请把它声明为全局变量，或在程序中把它复制到一个全局变量。

当控制器应答说它不提供某个点位名称的变量时，设备页面会用一条提示列出该变量和读取它的点位，并给出两种原因（名称不存在，或变量未发布）和这些步骤；变量能读到值后，提示会自行消失。点位自己的页面上也有同样的提示。诊断日志和代理读到的是同样的步骤。

## FINS 卡片 {#the-fins-card}

通过 FINS 读取的设备在“连接”卡片下面多一张 **FINS** 卡片：请求用什么承载、发给谁、每个请求等待应答多久。按名称读取的设备没有这张卡片。

| 字段 | 含义 | 默认值 |
| --- | --- | --- |
| **传输协议** | **UDP**：“每个请求一个数据报，应答回到发出它的端口：这是 Omron 以太网端口的默认方式。”**TCP**：“一条连接，PLC 在其中为本工作站分配 FINS 节点。” | UDP |
| **网络** | “PLC 的 FINS 网络：本工作站所在的网络为 0，经由 PLC 路由表时为 1 到 127。” | 0 |
| **PLC 节点** | PLC 的 FINS 节点，1 到 254。留空时框里显示“自动”：UDP 下取 PLC IP 地址的最后一个数（位于 `192.168.250.1` 的 PLC 是节点 1），TCP 下取连接建立时 PLC 通告的节点。 | 自动 |
| **单元** | “PLC 内负责应答的单元：CPU 单元为 0。”最大 255。 | 0 |
| **工作站节点** | 仅 UDP：本工作站的 FINS 节点，1 到 254。留空时框里显示“自动”：本工作站在通往 PLC 的网络上的 IP 地址的最后一个数。TCP 下不绘制这个框，因为 PLC 在连接中分配节点。 | 自动 |
| **响应超时（ms）** | “FINS 请求等待应答的时间。”100 到 60,000。 | 2,000 |

超出范围的数字会在框下被拒绝，例如“请输入 0 到 127 之间的数字。”，框随即回到当前生效的值。把传输切换为 TCP 时，为 UDP 填写的工作站节点仍然保留，但设备不再使用它。

## 串行线路上的 Host Link {#host-link-on-a-serial-line}

只有串口的 CP1E、CP1L、CP1H、CJ 或 CS 控制器（RS-232C，或通过选件板或转换器的 RS-485 和 RS-422A）通过 Omron 驱动的串行线路，以 Host Link 上的 FINS 读取：一个 COM 端口最多由 32 台 PLC 共用，每台按自己的单元号应答。在驱动页面的串行线路表格上用**添加线路**添加线路，把**串行端口**、**波特率**和**串行格式**设成与 PLC 串口一致（线路页面的其余部分见[串行线路](connector-lines)），启用线路，然后在线路页面上用**添加设备**逐台添加 PLC。

| 设置 | 位置 | 值 |
| --- | --- | --- |
| 端口模式 | PLC 的 PLC Setup 中，线路所接的串口 | Host Link |
| 波特率和格式 | 线路 | 与 PLC 端口的设置一致；Host Link 端口出厂为 9600 波特和 `7E2` |
| **单元 ID** | 设备的“连接”卡片，**串行线路 · 固定**旁边 | PLC 的 Host Link 单元号，0 到 31，按其 PLC Setup 的设置。新设备取线路上第一个空闲的号；线路上其他 PLC 已占用的号会被拒绝 |
| **响应超时（ms）** | 设备的 FINS 卡片 | 100 到 60,000；默认 2,000 |

线路上的设备始终通过 FINS 读取：没有**协议**、**主机**或**端口**输入框，FINS 卡片只显示“在这条串行线路上通过 Host Link 发送 FINS，目标是单元号（在 PLC Setup 中为其串口设置）等于上方单元 ID 的 PLC。”和**响应超时（ms）**，因为请求发往总线所寻址的 PLC 的 CPU 单元，网络字段都不适用。它不会移到网络上，网络上的设备也不会移到线路上。点位的地址写法与网络相同（`D100`、`W0.01`），endpoint 指向线路和单元，例如 `omron://serial/<line id>/COM5/9600/7E2?protocol=fins&transport=hostlink&hostLinkUnit=0&timeoutMs=2000`。

| 在串行线路上 | 会发生什么 |
| --- | --- |
| 一条线路上有多台 PLC | 整条线路一次只处理一个请求及其应答；其他单元的应答或噪声会被跳过 |
| PLC 在超时时间内没有应答 | 该设备重新连接；线路上的其他 PLC 继续读取 |
| PLC 认为帧已损坏，并以 Host Link 结束码应答，如 `13 (FCS error)` | 该设备重新连接 |
| 端口出故障，例如拔掉了适配器 | 线路上的每个设备都重新连接；端口在暂停 1 秒后重新打开，暂停时间每次加倍，最长一分钟 |
| 文本点位 | 最多 534 个字符，即一个 Host Link 帧能承载的量 |

扫描不会搜索串行线路：请按单元号逐台添加 PLC。

## 寻址 {#addressing}

点位在它的“信号源”卡片里用一个框寻址，框的名称随设备的协议而定：FINS 下是**存储区地址**，按名称读取的设备是**变量名**，下面附有该协议的提示。驱动读不了的地址会在点位页面上输入的地方被拒绝，并给出原因。“信号源”卡片里只读的**源地址（总线）**一行显示驱动收到的地址。

### 存储区地址（FINS） {#memory-addresses-fins}

存储区地址由区域字母、十进制的字编号组成，单个位再加一个点和位号（00 到 15）。字母不区分大小写，旧写法也能读。

| 区域 | 示例 | 字 | 其他写法 |
| --- | --- | --- | --- |
| CIO | `CIO100`、`CIO100.05`、`100.05` | 0 到 6143 | 只写数字也是 CIO 地址 |
| 工作区（W） | `W0`、`W0.01` | 0 到 511 | `WR` |
| 保持区（H） | `H10`、`H10.00` | 0 到 1535 | `HR` |
| 辅助区（A） | `A100`、`A100.03` | 0 到 1471 | `AR` |
| 数据存储区（D） | `D100`、`D100.15` | 0 到 32767 | `DM` |
| 扩展数据存储区（E） | `E0_100`、`EA_0`、`E18_0` | 0 到 32767 | E 和下划线之间是十六进制的存储体编号，0 到 18 |

每个区域的上限按 CJ、CS、CP 控制器中最大的那一个来定（CJ2 有 1,536 个保持字，扩展数据存储区的存储体一直到 18）。较小的控制器没有的字或存储体，由那台 PLC 自己来回答，在点位第一次读取时表现为错误（见[读取](#reading)）。点位页面在输入的地方拒绝的内容：

| 输入的内容 | 点位页面显示 |
| --- | --- |
| 不是区域、字和位的东西，例如 `X100` 或 `D100.` | “FINS 存储区地址由区域和字组成，位地址再加一个点和位号，例如 D100、W0.01、CIO100.05、H10、A100 或 E0_100。” |
| 超出区域末尾的字，例如 `D40000` 或 `W512` | “这个字超出了任何 CJ、CS 或 CP 控制器上该区域的最后一个字。” |
| 超过 15 的位，例如 `W0.16` | “字的位编号从 00 到 15。” |
| 超过 18 的存储体，例如 `E19_0` | “扩展数据存储区的存储体按十六进制编号，从 0 到 18。” |

总线地址是统一成一种写法的地址，位写成两位数：`100.5` 以 `CIO100.05` 发送，`DM100` 以 `D100` 发送。文本点位在 `?` 后面加上它占用的字符数，所以 D200 处一段 16 个字符的文本以 `D200?16` 发送。

### 变量名（NJ/NX） {#variable-names-njnx}

NJ、NX 或 NY 控制器按它发布到网络的全局变量名读取，在 Sysmac Studio 里就是 Network Publish 属性为 Publish Only、Input 或 Output 的变量（见[准备控制器](#prepare-the-controller)）。控制器没有发布的变量不能按名称读取。变量名由 ASCII 字母、数字和下划线组成，不以数字开头，结构体成员前加点，索引写在方括号中：`Level`、`Motor.Speed`、`Temps[3]`、`Grid[1,2]`。其他写法都会在点位页面上被拒绝：“变量名由英文字母、数字和下划线组成，不以数字开头，成员前加点，索引写在方括号中，例如 Level、Motor.Speed 或 Temps[3]。”用 Sysmac Studio 允许的其他文字写的名称同样被拒绝，因为 EtherNet/IP 请求带不了它。

控制器列出变量的方式本驱动读不了，所以按名称读取的设备上每个点位都要手工填写。控制器有没有发布某个名称，由控制器自己在点位第一次读取时回答。没有偏移量字段：成员或元素按它自己的名称指定，BOOL 数组的元素也按自己的名称读取，例如 `Flags[5]`。总线地址是名称后面加上 `?0`，例如 `Motor.Speed?0`。

## 数据类型 {#data-types}

FINS 下可选的**数据类型**随地址而定：

| 地址指向 | 数据类型 | 读取什么 |
| --- | --- | --- |
| 一个位，例如 `W0.01` | Boolean | 只读那一个位。 |
| 一个字，例如 `D100` | Int16 或 UInt16 | 那一个字。 |
| 一个字及其后一个字 | Int32、UInt32 或 Float | 两个字，地址处是低位字，后面是高位字，与 Omron 自己的指令存放双字的方式一致。区域的最后一个字不能作为一对的开头，所以只提供 Int16、UInt16 和 String。 |
| 一个字及其后的若干字，作为文本 | String | 点位声明的**长度（字符数，留空为 16）**，1 到 1998 个字符，每个字两个字符，第一个在高位字节。文本在第一个 NUL 处结束。在串行线路上为 1 到 534 个字符。 |

输入一个容纳不了点位类型的地址时，类型会改成该地址读起来最自然的类型：位为 Boolean，字为 Int16。地址容纳不了的类型会被拒绝，并列出它能容纳的类型，例如“地址 D100 按以下数据类型之一读取：Int16, UInt16, Int32, UInt32, Float, String。”没有字节顺序或字顺序选项：FINS 的每个字都是高位字节在前，由驱动处理。

按名称读取的设备提供控制器的所有原子类型：

| 数据类型 | Sysmac Studio 中的变量类型 |
| --- | --- |
| Boolean | BOOL |
| SByte | SINT |
| Byte | USINT |
| Int16 | INT |
| UInt16 | UINT |
| Int32 | DINT |
| UInt32 | UDINT |
| Int64 | LINT |
| UInt64 | ULINT |
| Float | REAL |
| Double | LREAL |

WORD 变量按 UInt16 读取，DWORD 变量按 UInt32 读取。STRING 变量不读取，也不提供 String。列表之外的类型会被拒绝，并附上这个列表：“NJ/NX 控制器的变量按以下数据类型之一读取：Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double。”

## 读取 {#reading}

FINS 下，设备与 PLC 保持一段会话，一次只发一个请求，每个点位就是对它那些字的一次存储区读取；只有带着该请求自身编号的应答才被当作应答，所以迟到的应答绝不会被当成下一个请求的应答。读取任何点位之前，PLC 必须应答一次控制器数据读取，结束码是什么都可以；它报出的型号写进诊断日志。之后由 PLC 的应答决定每次读取的结果：

| PLC 应答 | 读取 |
| --- | --- |
| 正常结束 | 值。 |
| 关于该点的结束码，例如 PLC 没有的地址 | 该点位没有值，它的错误写出结束码及其在 Omron 手册中的含义，例如 `end code 0x1103 (address range error)`。设备的其他点位继续读取。 |
| 关于通往 PLC 的路径的结束码（0x01xx、0x02xx、0x03xx、0x05xx）、途中的中继节点超时，或在**响应超时（ms）**内没有任何应答 | 设备重新连接，在 PLC 再次应答之前它的点位读取都会失败。显示什么由每个点位针对[设备没有应答](connector-value-pipeline#the-device-did-not-answer)的策略决定。 |

按名称读取的设备通过向控制器询问一个没有任何程序声明的变量来确认连接：控制器回答这个名称不存在，这本身就是来自控制器的应答。它的请求经过与 [Rockwell EtherNet/IP](driver-rockwell) 驱动相同的 EtherNet/IP 代码，10 秒后超时。

## 写入 {#writes}

**访问**为读写或只写的点位可以被写；新点位一开始是只读。写入在设备的队列里与读取一起排队，只发送所要求的值，别的什么都不发：

| 点位读取的内容 | 写入发送什么 |
| --- | --- |
| FINS 位（`W0.01`） | 只发那一个位：PLC 就地改变它，字里的其他位保持原样。 |
| FINS 字或双字 | 整个值，写到地址处，两个字时还写到后一个字。 |
| FINS 文本 | 声明的整个长度，文本在前、NUL 在后，所以之前写入的更长文本不会有残留在它后面被读回来。放不进长度的文本会被拒绝，绝不截断。 |
| NJ/NX 变量 | 值，按名称写到那个变量；BOOL 数组的元素只写那一个 BOOL。 |

| PLC 对 FINS 写入的应答 | 写入 |
| --- | --- |
| 正常结束 | 已送达。 |
| 拒绝写入的结束码 | 被拒绝，原因写出结束码及其含义，例如 `end code 0x2101 (read-only)`。 |
| 在**响应超时（ms）**内没有任何应答，或途中的中继节点超时（0x0205） | 无应答。 |

点位类型容纳不了值的 FINS 写入，会在发送任何内容之前被拒绝。

## 设备发现 {#discovery}

Omron 驱动的页面有一张 **扫描范围** 卡片，里面只有一个可选的框：“用于 FINS 探测的 PLC 地址，例如 192.168.250.1（留空则只通过广播查找 NJ/NX）”。发现设备随后最多依次问两个问题：

| 问题 | 何时 | 提供什么 |
| --- | --- | --- |
| 通过 FINS 和 UDP 进行一次控制器数据读取，所有节点为自动，发给框里的 PLC：一个 IP 地址或名称，PLC 在 9600 以外的端口应答 FINS 时加上 `:port`。进度行显示“正在通过 FINS 请 192.168.250.1 报告自身标识…”。 | 只在框里有地址时。 | 这台 PLC，作为以它应答的型号命名的 FINS 设备，地址为 `omron://<host>:<port>?protocol=fins&transport=udp&network=0&node=0&unit=0&source=0&timeoutMs=2000`。没有任何应答的 PLC 什么也不提供。 |
| EtherNet/IP 身份请求，向本机所在的每个网络广播。进度行列出这些网络：“正在 192.168.0.255 上查找 Omron NJ/NX 控制器…”。 | 每次都问。扫描一直监听，直到 5 秒内没有新的应答。 | 每台应答的 NJ、NX 或 NY 控制器，作为按名称读取的设备，以它的产品名称命名（例如 NX1P2）。产品名称以 `NX-` 开头的 NX I/O 耦合器不含变量，会被跳过，其他 Omron 产品也一样。 |

两个问题都找不到点位：FINS PLC 不发布它的地址列表，NJ/NX 列出变量的方式本驱动读不了，所以每个候选都显示“没有报告任何点位。把设备添加进来，再在它的编辑器里配置点位。”框里不是主机的内容会在框下被拒绝，发现设备变灰：“Omron 扫描通过 FINS 请这里填写的 PLC 报告自身标识：填写它的 IP 地址或名称，需要时带上端口（例如 192.168.250.1 或 192.168.250.1:9600）。留空则只通过广播查找 NJ/NX 控制器。”不在本机所在网络上的控制器不会应答广播；请手工添加。Rockwell EtherNet/IP 的扫描也会听到同一个广播，但会把每台 Omron 控制器留给本驱动。扫描的其他行为见[设备发现](connector-discovery)。

## 智能体工具 {#agent-tools}

`driver_capabilities` 在 `omron` 下描述本驱动：两种协议各自接受的设备字段、地址写法和数据类型，FINS 的传输方式和取值范围，写入和设备发现。`device_upsert driver=Omron` 接受 `omronProtocol`（`Fins` 或 `NjNx`），FINS 下还接受 `finsTransport`（`Udp` 或 `Tcp`）、`finsNetwork`、`finsNode`（0 为自动）、`finsUnit`、`finsSourceNode`（仅 UDP，0 为自动）和 `finsResponseTimeoutMs`；按名称读取的设备既不接受端口，也不接受 FINS 字段。`tag_upsert` 用 `address` 接受存储区地址或变量名，用 `opcUaType` 接受类型（省略时位取 Boolean，字取 Int16），用 `wireQuantity` 接受 FINS 文本的长度。`device_discover driver=Omron` 执行扫描，FINS 探测的目标用 `target` 传入。下面是一台按 UDP 读取的 CJ2 和一台按名称读取的 NX1P2：

```
device_upsert driver=Omron name=Press host=192.168.250.1 omronProtocol=Fins finsTransport=Udp
tag_upsert device=Press name=Level address=D100 opcUaType=Float
device_upsert driver=Omron name=Filler host=192.168.250.2 omronProtocol=NjNx
tag_upsert device=Filler name=Speed address=Motor.Speed opcUaType=Float
```

串行线路上的 PLC 挂在本驱动的线路下：`serial_line_upsert driver=Omron` 创建线路，`device_upsert driver=Omron serialLineId=<id>` 以 Host Link 添加 PLC（由线路决定 `finsTransport=HostLink`），`unitId` 传其单元号 0 到 31（省略时取第一个空闲的号），还可传 `finsResponseTimeoutMs`；网络字段在这里会被拒绝。COM5 上单元 0 的 CP1L 示例：

```
serial_line_upsert driver=Omron name=HostLink serialPort=COM5 baudRate=9600 serialFormat=7E2
device_upsert driver=Omron name=Mixer serialLineId=<the line's id> unitId=0
tag_upsert device=Mixer name=Level address=D100 opcUaType=Int16
```

其他工具见[智能体工具](agent-tools)。

## 值得了解的限制 {#limits-worth-knowing}

- 只使用旧的 C 模式 Host Link 命令的控制器（如 CPM1、CPM2、CQM1、C200HS）无法读取，无论是在[串行线路](#host-link-on-a-serial-line)上还是通过转换器：驱动只发送 FINS 命令。
- FINS 下不提供定时器和计数器：它们的当前值按 PLC 的设置以 BCD 或二进制保存，而 FINS 应答里没有任何信息说明是哪一种。请通过程序把该值复制过去的字来读取它。
- NJ/NX 控制器的变量从不列出，点位页面不列，扫描也不列：每个名称都要手工填写。
- 按名称读取的设备每个点位读取一个原子值。STRING 变量、整个结构体和整个数组都不读取；成员或元素按名称读取。
- 驱动只发送三条 FINS 命令，没有别的：存储区读取、存储区写入和控制器数据读取。它从不改变 PLC 的运行模式、时钟或程序。

## 需要知道的事 {#good-to-know}

- UDP 下，工作站从自己的一个端口发出每个请求，PLC 应答到这个端口，这正是 Omron 以太网端口出厂时的设置。设成静态地址转换的以太网单元则会应答到工作站的 9600 端口，而驱动并不在那里监听：这样的 PLC 请用 TCP 读取。
- 0 以外的 FINS 网络要经由 PLC 中设置的路由表到达；工作站自己不转发任何东西。
- 对 NJ/NX 控制器先在 CIP 路径 `1,0` 上询问，得不到应答时再用 18 加上控制器自己的 IP 地址组成的路径询问，例如 `18,192.168.250.1`；应答过的路径下次优先尝试。没有路径字段。
- 每个 FINS 点位都是单独的请求，设备一次只发一个，所以一轮读取所需的时间随点位数量增长。
