Generic serial and TCP
通过串行线路、TCP 或 UDP 接入说自有文本或字节协议的现场设备,例如称重仪表和条码阅读器:它们的消息如何结束、如何校验,以及每个点位如何从中取值。
Generic serial and TCP 驱动读写那些说自有协议的现场设备,协议由文本或简单字节组成,没有哪个标准协议适用:称重仪表、条码或 RFID 阅读器、老式仪表、标签打印机。这类现场设备无法被问出自己是什么,所以不做任何发现。你要告诉设备,它发来的消息如何结束、校验和如何核对;还要告诉每个点位,它如何得到数值、哪些消息属于它、消息中的哪些字符是数值。
设备通过以下三种方式之一到达现场设备,在创建时选定:
| 传输方式 | 设备如何到达 | 它的点位能做什么 |
|---|---|---|
| 在串行线路上 | 本驱动的一条串行线路,与线路上的其他设备共用,一次只发一个请求。 | 请求与应答、监听、写入。 |
| TCP 客户端 | 到现场设备主机和端口的独立连接。 | 请求与应答、监听、写入。 |
| UDP 监听 | 本工作站上的一个端口,现场设备向它发送数据报。工作站不发送任何东西。 | 只能监听,只读。 |
串行线路上的设备挂在它的线路下面,所以树的这一部分和 Modbus RTU 一样读作驱动 → 线路 → 设备 → 点位。网络设备挂在驱动下面,可以像任何其他驱动的设备一样归入文件夹,即驱动 → 文件夹 → 设备 → 点位。设备留在创建时所在的一侧:TCP 和 UDP 可以互换,但设备绝不会在线路和网络之间移动。
驱动页面
标题栏下面那行写着“串行线路下挂共用一条总线的仪表,网络设备直接挂在驱动下。添加一条线路或一台设备,然后打开它描述其消息。”,页面上下排着两张表:
| 表 | 用什么增加 | 列出什么 |
|---|---|---|
| 串行线路 | 标题右侧的添加线路。 | 驱动的线路:线路、物理参数组(端口 · 波特率 · 格式,不含超时,因为本驱动的线路没有超时)、设备、状态,以及打开、启用、禁用和删除操作。为空时显示“还没有串行线路。” |
| 设备 | 标题右侧的添加设备。它创建一台网络设备,即 TCP 客户端。 | 驱动的所有设备,包括线路上的设备,列与任何驱动的设备表相同。为空时显示“还没有设备。” |
标题栏在括号里显示设备数;只要有一条线路,树中的驱动行就会同时计数两层(“1 条线路 · 2 台设备”)。驱动的命令栏上没有发现。右键单击驱动行会提供添加设备、添加线路、新建文件夹和刷新,一条分隔线之后是移除驱动;在栏底的“新建…”菜单里,本驱动那一项提供新建设备、新建串行线路和新建文件夹,然后是它的线路、文件夹和设备。驱动添加之后就出现在树里的“驱动”文件夹下面,不论有没有线路或设备。
串行线路
本驱动的线路就是串行线路中描述的那种线路,只是字段更少。它的页面写着“一条物理串行总线:其上仪表共用的端口设置,一次只发一个请求。”,“物理线路”卡片包含:
| 字段 | 含义 | 默认值 |
|---|---|---|
| 名称 | 总线的易读名称,显示在树中和线路上的每台设备上。 | “新建线路” |
| 串行端口 | Windows 的 COM 端口,物理、虚拟或重定向的都可以,手动输入或从 Windows 报告的端口中挑选(刷新重新读取)。一个端口只属于一条线路,无论线路属于哪个驱动。 | 第一个空闲端口 |
| 波特率 | 总线的速度,由线路上的所有设备共用。 | 9600 |
| 串行格式 | 数据位、校验位和停止位:8N1、8N2、8E1、8E2、8O1、8O2、7N2、7E1、7E2 或 7O1。十种全部可选,因为本驱动不要求 8 个数据位。 |
8E1 |
在这里创建的线路一开始是禁用的,好让你在端口打开之前填好端口设置。端口、波特率和格式只能在线路禁用时更改,名称随时可改。本驱动的线路没有传输模式、响应超时、最小事务间隔和自动静默:线路上的每台设备在自己的面板中说明其消息如何结束、等待应答多久,线路下面的“设备”卡片也这样写:“这条线路上的所有设备共用它的端口设置。每个设备在自己的面板中说明其消息如何结束、等待应答多久。”它没有“发现从站”卡片,设备表也没有单元 ID 列,因为仪表靠请求的内容区分,而不是靠地址。
在线路上,或在它的右键菜单中点添加设备,会在这条线路上创建一台本驱动的设备。线路永远属于创建它时所在的驱动:Modbus RTU 设备和本驱动的设备绝不共用一条线路,把设备指到另一个驱动线路上的智能体会被拒绝。线路的状态(已关闭、空闲、正在连接…、在线、端口故障)与所有串行线路相同,见启用与健康状态。
在线路上,驱动为线路上的所有设备只打开一次端口,一次只发一个请求,等到它的应答后再发下一个。请求之间到达的内容会交给线路上的每台设备,各自按自己的规则切分成消息。端口出故障时,线路会为它的所有设备重新打开端口,故障持续期间两次尝试之间的等待越来越长。
设备
连接
| 字段 | 含义 | 默认值 |
|---|---|---|
| 传输方式 | 只用于网络设备。TCP 客户端:“工作站连接到设备的主机和端口。”UDP 监听:“工作站在本地端口上接收设备发送的内容,自身不发送任何东西。”两者可以互换;线路上的设备在这里显示它的线路。 | TCP 客户端 |
| 主机 | TCP:现场设备的主机名或 IP 地址。可以粘贴完整地址,前面带 tcp://、主机后面带端口。 |
空 |
| 端口 | TCP:现场设备应答的端口,1 到 65535。这类现场设备没有标准端口,所以新设备没有端口。 | 空 |
| 仅接受来自 | UDP 时代替主机出现:“只读取来自这个地址的数据报。留空:接受任何发送方。” | 空 |
| 本地端口 | UDP 时代替端口出现:“设备发往的本工作站端口。Windows 防火墙必须放行它。” | 空 |
| 串行线路 · 固定 | 线路上的设备:线路名称,下面是它的端口、波特率和格式,只读。 | 添加时所在的线路 |
新的 TCP 设备显示“尚未填写主机”,在有了主机和端口之前不会连接。有主机却没有端口的设备,以及没有本地端口的 UDP 设备,都无法保存:“请填写端口:TCP 时是设备的端口,UDP 时是用于接收的本地端口。”线路上的设备没有主机、端口、文件夹,也没有单元 ID。
标题栏显示的 Base URI 是运行时地址:generic://tcp/<host>:<port>、generic://udp/<local port>(填了“仅接受来自”时带 from=<sender>)或 generic://serial/<line id>/<port>/<baud>/<format>,后面跟着消息如何结束和校验(end、start、terminator、length、gap、timeout、checksum 等)。其中携带的是字节本身,而不是设备的输入方式。线路上设备的副本留在那条线路上;网络设备的副本落在驱动本身这一层。
消息卡片
在连接卡片下面,消息卡片规定字节如何输入、消息如何结束。
| 字段 | 含义 | 默认值 |
|---|---|---|
| 输入方式 | 起始标记、结束符,以及每个点位的请求、过滤条件、分隔符、写入消息和预期回复如何书写。带转义的文本:可打印字符照原样写,其他字节用 \r、\n、\t、\\ 和 \xNN。十六进制字节:每个字节两位数字,例如 02 30 31 0D。切换时会改写设备及其点位的每个字节字段,让它们继续发送相同的字节;只要其中有一个不是有效字节,切换就会被拒绝:“表示法未更改:这台设备或其点位有一个字节字段在当前表示法下无效。请先改正。” |
带转义的文本 |
| 消息结束方式 | 结束符、固定长度或静默间隔。走 UDP 时,无论这里怎么选,一个数据报就是一条消息。 | 结束符 |
| 结束符 | 选结束符时:结束每条消息的字节。它们不属于点位读取的内容。 | \r\n |
| 消息长度(字节) | 选固定长度时,1 到 1024:“每条消息都是这么多字节,包括起始标记和校验和。” | |
| 静默间隔(ms) | 选静默间隔时,5 到 10000:“这段时间内没有收到任何内容,即视为消息结束。” | 50 |
| 起始标记 | 可选。每条消息开头的字节,例如 \x02。它之前的字节会被丢弃,这也让从消息中途开始接收的数据流重新对齐;它本身计为消息的最前面几个字符。 |
空 |
| 响应超时(ms) | 50 到 60000:“请求等待应答、写入等待预期回复的时间。” | 1000 |
| 无消息的最长时间(秒) | 0 到 86400,0 表示从不:“在这段时间内收不到任何内容时,监听的点位不再算作读数。0:从不过期,适合条码阅读器这类只偶尔发送的设备。对秤这类持续发送的设备,请设置一个值。” | 0 |
所选结束方式不读取的字段不会显示,并保留其值,以备换回那种结束方式时使用。设备无法使用的值不会保存,记录条会说明原因,例如“请填写消息结尾的结束符,例如 \r\n。”或“消息长度必须在 1 到 1024 字节之间。”
校验和卡片
| 字段 | 含义 | 默认值 |
|---|---|---|
| 算法 | 每条消息携带的校验和:无、求和(8 位)、和的二进制补码(LRC)、7 位和的二进制补码(Toledo)、异或(8 位)、CRC-16/MODBUS 或 CRC-16/CCITT。“校验和不符的消息会被丢弃并在诊断日志中计数,绝不当作数值读取。” | 无 |
| 写法 | 十六进制文本是用十六进制数字写出的校验和,原始字节是校验和本身的字节。 | 十六进制文本 |
| 位置 | 结尾之前:结束符之前的最后几个字节,或消息的最后几个字节。结束符之后:紧跟在结束符后面,此时结束符也在校验范围内;只有消息以结束符结束时才提供。 | 结尾之前 |
| 开头不计的字节数 | 0 到 255:“消息开头不在校验和范围内的字节,例如 STX 或 $。” | 0 |
| 紧挨其前不计的字节数 | 0 到 255:“紧挨校验和之前、不在其范围内的字节,例如 NMEA 语句中的 *。点位也不读取它们。” | 0 |
除算法外的字段只在选定校验和后才显示。校验和覆盖从消息第一个字节到校验和前一个字节的范围,不计的字节除外。校验和不符的消息绝不会被当作数值读取:它会被丢弃,诊断日志在设备名下记录,每分钟最多一次。校验和只做核对:请求和写入完全按输入的样子发出,所以需要校验和的请求要在自己的字节里带上它。
| 算法 | 常见用途 |
|---|---|
| 求和(8 位) | 仪表。 |
| 和的二进制补码(LRC) | 仪表、Modbus ASCII 风格的帧。 |
| 7 位和的二进制补码(Toledo) | Toledo 仪表的连续输出,在 CR 之后以原始字节出现。 |
| 异或(8 位) | NMEA 0183 语句($…*hh)、仪表、RFID 阅读器。 |
| CRC-16/MODBUS | 二进制帧;以原始字节写时低字节在前。 |
| CRC-16/CCITT | 二进制帧(CCITT-FALSE 变体);以原始字节写时高字节在前。 |
点位寻址
本驱动的点位没有要输入的地址:它的信号源卡片用下面的字段询问它如何得到数值。字节按设备的输入方式输入。
| 字段 | 含义 | 默认值 |
|---|---|---|
| 取值方式 | 请求与应答:“工作站按点位的轮询间隔发送请求,并从应答中读取数值。”监听:“设备自行发送,点位保留其过滤条件接受的最后一条消息。”UDP 设备只提供监听:“这台设备通过 UDP 只接收不发送:它的点位只能监听,并且只读。” | 请求与应答 |
| 请求 | 仅用于请求与应答:“工作站按点位的轮询间隔发送的内容,按设备规定的方式输入。在响应超时内被过滤条件接受的第一条消息即为应答。” | 必填 |
| 消息过滤条件 | 哪些消息属于这个点位:所有消息、开头为(填写开头的字节)或匹配正则表达式(在正则表达式中填写)。用于请求时:“哪条消息作为请求的应答。其余的交给监听的点位。” | 所有消息 |
| 数值取自 | 如何从消息中切出数值:整条消息、固定位置(起始字符和长度)、分隔符之间的字段(分隔符和字段序号)或模式中的捕获组(模式)。“位置和字段都从 1 开始数。模式必须恰好有一个捕获组,即数值。数字按小数点为句点读取。” | 整条消息 |
| 设备用逗号作小数点 | 仅限 Float 和 Double 点位。读取和写入时都把逗号当作小数点。 | 关 |
| 写入消息 | 用于会被写入的点位,UDP 上没有:“写入时发送的内容,在数值所在位置放 ,例如 SP \r;要格式化时写 。对 Boolean 点位,不含 的写入消息是一条命令,写入 true 时发送。” | 空 |
| 预期回复 | “可选。设备对写入的回复以什么开头。只有在响应超时内收到这样的回复,写入才算成功。” | 空 |
字符从消息开头算起,包括起始标记;结束符、校验和以及校验和紧挨其前不计的字节都不属于点位读取的内容。模式是 .NET 正则表达式,读取一条消息超过 250 ms 时不读取这条消息。只写的点位没有请求、过滤条件和取值字段;没有写入消息的点位是只读的。
数据类型有 Boolean、Int32、Int64、Float、Double 和 String。切出的文本先按点位的类型读取,再由点位自己的各个环节换算,和所有驱动一样(见数值转换链):Boolean 读 1、true、on、yes 和 0、false、off、no;整数读整数(12.0 读作 12,12.5 被拒绝);Float 或 Double 读小数;String 照原样取字符。仪表会用空格填充数字,所以数字前后的空格以及符号和数字之间的空格都会忽略。字段无法成立的点位不会保存,点位页面会说明缺少什么,例如“请填写这个点位发送的请求。”或“模式必须恰好有一个包住数值的捕获组。”
只读的线上地址预览显示 ask 或 listen,后面跟着适用的部分,例如 listen?extract=position&start=5&length=6;发送 01RD 和 CR、读取应答中第三个逗号分隔字段的点位则是 ask?send=01RD%0D&extract=field&separator=,&field=3。
读取
| 情形 | 请求与应答的点位 | 监听的点位 |
|---|---|---|
| 数值 | 取自请求之后、在响应超时内被过滤条件接受的第一条已校验消息。 | 取自被过滤条件接受的最后一条已校验消息,时间戳为它的到达时间。 |
| 何时读取 | 按点位的轮询间隔。 | 每当现场设备发送时;点位按轮询间隔显示最后一条消息。 |
| 第一条消息之前 | 不适用。 | 点位等待:没有数值也没有错误,与任何点位在第一次应答之前一样。 |
| 没有应答,或现场设备沉默 | 响应超时内没有应答就是一次读取失败,由点位针对设备没有应答的策略决定显示什么。 | 超过设备的无消息的最长时间(秒)后,每个监听的点位都读取失败,由各自的策略决定显示什么。TCP 连接断开时,无论这个时间怎么设,它们都会失败。 |
| 点位读不了的消息 | 一次带原因的读取失败:缺少字段、数值超出消息末尾,或类型容纳不了的文本。 | 相同。 |
没有回应任何请求的消息会交给同一设备的监听点位,所以一台设备可以同时有两种点位。过滤条件相同的监听点位共享收到的内容,可以从同一条消息中取不同的字段。
写入
写入和其他任何驱动走同一个通道:写入框接受工程值,各个换算环节按相反顺序执行后再发送原始值(见 Connector)。
| 写入消息 | 发送什么 | 写入报告什么 |
|---|---|---|
含 {value} |
输入的字节,{value} 的位置放入数值:数字按固定区域格式(点位这样指定时以逗号作小数点),或按 {value:FORMAT} 用 .NET 数字格式书写(W{value:000.0}\r 把 12.5 发成 W012.5 和一个 CR);Boolean 发成 1 或 0;文本照原样。 |
发出即完成。设了预期回复时,只有在响应超时内收到以它开头的回复才算完成;收到别的回复则以那条回复为原因拒绝写入,什么都没收到则为无应答。 |
不含 {value},Boolean 点位 |
一条命令,例如去皮或打印:写入 true 时发送,写入 false 被拒绝。 | 相同。 |
| 无 | 什么都不发:点位只读。 |
写入和所有驱动一样在设备的队列中排队:在线路上,它们共用线路一次一个请求的轮次;在 TCP 上,使用设备自己的连接。UDP 设备什么都不发送,所以它的点位不能写入。
示例:RS-232 上的称重仪表
采用 Toledo 连续输出格式的仪表每秒多次发送:一个 STX、三个状态字节、六位重量数字、六位皮重数字、一个 CR 和一个校验和字节。在工作站上:
- 在驱动页面上点添加线路;命名为
Scale line,选COM3,波特率保持 9600,串行格式选7E1,然后启用这条线路。 - 在线路上点添加设备;命名为
Scale。在消息卡片上保持带转义的文本和结束符,把结束符设为\r,起始标记设为\x02,无消息的最长时间(秒)设为 10,这样拔掉电缆就会显示为通信中断。 - 在校验和卡片上选7 位和的二进制补码(Toledo)、原始字节和结束符之后,两个不计字节数都为 0。
- 点添加点位;命名为
Weight,Double,只读。在信号源卡片上选监听、所有消息和固定位置,起始字符为 5,长度为 6:STX 是第 1 个字符,三个状态字节是 2 到 4,重量是 5 到 10。 - 这些数字不带小数点,所以由点位的量程换算环节放上小数点,例如仪表设为两位小数时用系数 0.01。
智能体用三次工具调用完成同样的事:
serial_line_upsert driver=GenericSerialTcp name="Scale line" serialPort=COM3 baudRate=9600 serialFormat=7E1
device_upsert driver=GenericSerialTcp name=Scale transport=SerialLine serialLineId=<id> messageEnd=Terminator terminator=\r startMarker=\x02 checksum=SumTwosComplement7Bit checksumFormat=Raw checksumPlacement=AfterEnd silenceTimeoutSeconds=10
tag_upsert device=Scale name=Weight opcUaType=Double tagMode=Listen valueExtraction=FixedPosition valueStart=5 valueLength=6
示例:TCP 上的条码阅读器
带以太网口的条码阅读器每行发送一个条码,而且只在读到条码时发送:
- 在驱动页面上点添加设备;命名为
Reader,保持 TCP 客户端,主机填 192.168.0.40,端口填 2001。 - 在消息卡片上保持带转义的文本、结束符
\r\n,无消息的最长时间(秒)为 0:一个小时什么都不发的阅读器并没有故障。校验和卡片保持无。 - 点添加点位;命名为
Code,String,只读。在信号源卡片上选监听、所有消息和整条消息。
用智能体的工具也一样:
device_upsert driver=GenericSerialTcp name=Reader transport=TcpClient host=192.168.0.40 port=2001 messageEnd=Terminator terminator=\r\n
tag_upsert device=Reader name=Code opcUaType=String tagMode=Listen valueExtraction=WholeMessage
driver_capabilities 在 genericSerialTcp 下描述本驱动,并附上这两个示例;serial_line_upsert 用 driver=GenericSerialTcp 创建本驱动的线路;其他工具见工具,按范围列出。
值得了解的限制
- 不做任何发现:没有协议可以向这类现场设备询问它是什么、有什么。
- 在帧头中声明自身长度的二进制帧无法读取;消息以结束符、固定长度、静默间隔或数据报结束。
- 校验和只做核对:工作站发送的内容不会被加上校验和。
- 点位从消息中取出一个数值,作为文本按其类型读取;消息中以二进制整数或浮点数打包的数字不会被解码。
- UDP 设备只监听:不发送请求,也不接受写入。
- 设备不能在串行线路和网络之间移动,也不能移到另一条线路:请在它该在的地方新建,再删除旧的。