Una interfaz de software para protocolos industriales
Una interfaz de software para protocolos industriales
工业协议的软件接口
La integración de dispositivos de campo tropieza casi siempre con el mismo obstáculo: los equipos que hay que reunir en una misma vista no hablan el mismo protocolo, y ninguno de esos protocolos fue diseñado pensando en los demás. Una instalación industrial rara vez habla un solo protocolo. Conviven medidores Modbus TCP que solo responden cuando reciben una petición, sensores que publican por MQTT cada vez que tienen algo que comunicar y equipos que exponen sus datos por OPC UA, con su propio modelo de información y su propio sistema de tipos. Todos ellos deben aparecer en la misma vista. 现场设备的集成几乎总是遇到同一个障碍:需要汇集在同一视图中的设备并不使用相同的协议,而且这些协议在设计时都没有考虑到彼此。工业设施很少只使用一种协议。Modbus TCP 仪表(仅在收到请求时响应)、通过 MQTT 发布数据的传感器(每当有信息时就发布)以及通过 OPC UA 公开数据(具有自己的信息模型和类型系统)的设备往往共存。所有这些设备都必须出现在同一个视图中。
La dificultad no es de comunicación —cada protocolo está bien documentado y dispone de bibliotecas maduras—, sino de representación. El registro Modbus llega como 16 bits sin unidad ni marca de tiempo, y el instante de la lectura es el instante en que se preguntó. El mensaje MQTT llega como JSON con un timestamp generado en el dispositivo. El nodo OPC UA llega con su tipo declarado, su marca de tiempo de origen y su código de calidad. Son tres representaciones distintas de la misma idea —una medición— y la capa de presentación termina con una rama de código por cada una. 困难不在于通信——每个协议都有完善的文档和成熟的库——而在于表示方式。Modbus 寄存器以 16 位形式到达,没有单位或时间戳,读取时刻即为查询时刻。MQTT 消息以 JSON 格式到达,并带有设备生成的时间戳。OPC UA 节点带有声明的类型、源时间戳和质量代码。这是对同一概念(测量值)的三种不同表示,导致表示层最终不得不为每种协议编写专门的代码分支。
La capa intermedia
中间层
La solución es una capa entre la vista y los dispositivos: una interfaz de comunicación que exponga un contrato único, independiente del protocolo que haya debajo. Quien la consuma —una vista, un panel de supervisión, un servicio de alarmas— recibe siempre el mismo tipo de dato, con independencia de si procede de Modbus, de MQTT o de OPC UA. La traducción ocurre por debajo de la interfaz, dentro de cada canal, y no se propaga hacia arriba. Sin capa intermedia, cada protocolo llega hasta la lógica de negocio. Con ella, la aplicación programa contra un contrato único y la diferencia se resuelve por debajo. 解决方案是在视图和设备之间建立一个中间层:一个通信接口,它提供一个独立于底层协议的统一契约。任何使用者(视图、监控面板、报警服务)接收到的数据类型始终相同,无论其来自 Modbus、MQTT 还是 OPC UA。转换发生在接口之下、每个通道内部,不会向上传播。如果没有中间层,每种协议都会直接进入业务逻辑;有了它,应用程序只需针对单一契约进行编程,差异在底层得到解决。
Este planteamiento aporta, entre otras ventajas: 这种方法带来的优势包括:
- El programador de aplicación deja de aprender protocolos. No necesita saber que Modbus numera los registros desde 1 pero los direcciona desde 0, ni cómo se negocia un QoS en MQTT. 应用程序员无需再学习协议。 他不需要知道 Modbus 寄存器是从 1 开始编号但从 0 开始寻址,也不需要知道 MQTT 中如何协商 QoS。
- La información queda centralizada sin casarse con un fabricante. Pueden convivir marcas y protocolos distintos en la misma instalación, porque la diferencia se absorbe en el canal y no en la aplicación. 信息实现集中化,且不绑定特定厂商。 同一设施中可以共存不同的品牌和协议,因为差异被通道吸收,而不是由应用程序承担。
- Se prescinde del hardware traductor. Los gateways que convierten todo a un protocolo común suponen un coste, ocupan espacio en el tablero y añaden un punto de fallo. Si la traducción es software, desaparecen. 无需转换硬件。 将所有协议转换为通用协议的网关不仅成本高昂、占用机柜空间,还增加了故障点。如果转换通过软件实现,这些硬件便可省去。
Ese último punto merece detenerse, porque va contra lo que se hace habitualmente. Cuando se busca cómo integrar Modbus con MQTT, lo que se encuentra son puentes: un microcontrolador con su sistema operativo en tiempo real que consulta los registros por un lado y publica en temas por el otro, o un gateway comercial que unifica varios protocolos en el tablero. Funcionan, están bien documentados, y son la respuesta estándar del sector. La diferencia es dónde se pone la traducción. Un puente la pone en un equipo: hay que comprarlo, alimentarlo, configurarlo y mantenerlo, y su período de consulta es un parámetro suyo que la aplicación no ve ni controla. Una interfaz la sitúa en el código de la propia aplicación, y entonces ese período —y las demás decisiones de compromiso— dejan de estar ocultas en el firmware de un equipo y pasan a ser código que se puede leer, ajustar y versionar. 最后一点值得深思,因为它违背了常规做法。当寻求如何集成 Modbus 和 MQTT 时,通常找到的是“桥接器”:一个带有实时操作系统的微控制器,一端查询寄存器,另一端发布主题;或者是机柜中统一多种协议的商业网关。它们确实有效、文档齐全,是行业的标准方案。区别在于转换发生的位置。桥接器将其放在设备中:必须购买、供电、配置和维护,其查询周期是设备自身的参数,应用程序无法查看或控制。而接口将其置于应用程序代码中,这样该周期(以及其他权衡决策)就不再隐藏在设备固件中,而是变成了可阅读、可调整且可版本控制的代码。
1. La interfaz
1. 接口
El primer paso es acotar el conjunto mínimo de operaciones: aquellas que cualquier canal necesita, con independencia del protocolo. En una primera aproximación son cuatro: conectar, desconectar, leer y escribir. 第一步是确定最小操作集:无论协议如何,任何通道都需要的操作。初步构想包括四个:连接、断开连接、读取和写入。
public interface IDeviceChannel : IAsyncDisposable {
Task ConnectAsync(CancellationToken ct = default);
Task DisconnectAsync(CancellationToken ct = default);
Task<Reading> ReadAsync(DeviceData data, CancellationToken ct = default);
Task WriteAsync(DeviceData data, object value, CancellationToken ct = default);
}
Quien consume esta interfaz dispone de esos métodos y nada más: desconoce la implementación de cada protocolo, la forma de establecer la conexión y los tipos de dato nativos que maneja cada uno. Conviene notar que esos métodos no son todos de la misma naturaleza. ReadAsync y WriteAsync son operaciones sobre datos: entran y salen valores. ConnectAsync y DisconnectAsync pertenecen al ciclo de vida: no producen datos, sino que establecen y liberan el enlace, junto con los recursos asociados —el socket, la sesión con el intermediario, las suscripciones abiertas—. Y el ciclo de vida es donde reside la complejidad real: reconexión, expiración de plazos, el tratamiento de una escritura pendiente cuando el enlace se interrumpe, o qué debe ocurrir con una suscripción activa cuando se cierra el canal. 使用此接口的人仅拥有这些方法:他们不知道每个协议的实现细节、建立连接的方式以及各自处理的原生数据类型。值得注意的是,这些方法性质不同。ReadAsync 和 WriteAsync 是数据操作:涉及值的输入和输出。ConnectAsync 和 DisconnectAsync 属于生命周期:它们不产生数据,而是建立和释放链路及其关联资源(套接字、中间件会话、打开的订阅)。生命周期正是复杂性的所在:重连、超时处理、链路中断时待处理写入的逻辑,或者关闭通道时如何处理活跃订阅。
2. Dos modelos de comunicación diferentes
2. 两种不同的通信模型
Llegados a este punto aparece la cuestión que decide si el diseño funciona o se convierte en una capa que estorba: los protocolos no son equivalentes. La diferencia está en quién toma la iniciativa. En Modbus el dispositivo es pasivo: no comunica nada hasta que se le pregunta, y para saber si un valor ha cambiado hay que volver a preguntar. A ese preguntar de forma repetida se le llama sondeo (polling). En MQTT ocurre lo contrario: el dispositivo publica cuando tiene algo que comunicar y el intermediario lo reparte entre quienes se hayan suscrito; la aplicación no pregunta, solo espera. Es lo que se conoce como notificación o push. 至此,一个决定设计成败的问题出现了:协议并不等价。区别在于谁主动。在 Modbus 中,设备是被动的:除非被询问,否则不通信;要了解值是否改变,必须反复询问。这种重复询问被称为轮询(polling)。在 MQTT 中则相反:设备在有信息时发布,中间件将其分发给订阅者;应用程序不询问,只是等待。这就是所谓的通知或推送(push)。
No es una diferencia de formato que se resuelva con un conversor, y una interfaz debe decidir de qué lado se sitúa: 这不是一个可以通过转换器解决的格式差异,接口必须决定站在哪一边:
- Si el contrato es
ReadAsync(), se impone el sondeo sobre un protocolo que ya notificaba. El sensor MQTT publica un valor a las 10:00:03 y la aplicación lo lee a las 10:00:10: se han introducido siete segundos de latencia y se han perdido los valores intermedios que el dispositivo sí envió. 如果契约是ReadAsync(),则会对原本支持通知的协议强加轮询。MQTT 传感器在 10:00:03 发布值,而应用程序在 10:00:10 读取:这引入了 7 秒的延迟,并且丢失了设备发送的中间值。 - Si el contrato es
SubscribeAsync(), hay que construir la notificación donde no existe. El canal Modbus queda obligado a sondear internamente y emitir un valor cuando detecta un cambio. Funciona, pero el período de sondeo queda esc… 如果契约是SubscribeAsync(),则必须在原本不存在通知的地方构建通知。Modbus 通道被迫在内部进行轮询,并在检测到变化时发出值。这可行,但轮询周期会变得……