/* * Copyright © Stéphane Raimbault * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef MODBUS_TCP_PRIVATE_H #define MODBUS_TCP_PRIVATE_H /* * Modbus TCP 协议的帧格式定义: * * TCP ADU 帧结构(基于 MBAP 头 + PDU): * ┌──────────────────────────┬───────────────┬──────────┐ * │ MBAP 头部(7字节) │ PDU(变长) │ ... │ * └──────────────────────────┴───────────────┴──────────┘ * * MBAP 头部分解: * ┌──────────────┬──────────────┬──────────────┬──────────┐ * │ 事务标识符 │ 协议标识符 │ 长度字段 │ 单元标识符 │ * │ (2字节) │ (2字节,=0) │ (2字节) │ (1字节) │ * └──────────────┴──────────────┴──────────────┴──────────┘ */ /* TCP MBAP 头长度:事务ID(2) + 协议ID(2) + 长度(2) + 单元ID(1) = 7 字节 */ #define _MODBUS_TCP_HEADER_LENGTH 7 /* TCP 预设请求帧的总长度:MBAP头(7) + PDU(5) = 12 字节 */ #define _MODBUS_TCP_PRESET_REQ_LENGTH 12 /* TCP 预设响应帧的总长度:MBAP头(7) + PDU(1) = 8 字节 */ #define _MODBUS_TCP_PRESET_RSP_LENGTH 8 /* * TCP 模式下没有独立的校验和字段。 * TCP 协议本身通过底层 TCP/IP 栈的校验和机制保证数据完整性, * 因此 Modbus TCP 的 checksum 长度为 0。 */ #define _MODBUS_TCP_CHECKSUM_LENGTH 0 /* * modbus_tcp_t — Modbus TCP 后端的私有数据结构体。 * 在两个结构体中,事务标识符(t_id)都必须放在第一个字段位置, * 这样无论使用哪个 TCP 后端变体,都可以通过相同的内存偏移量快速访问事务ID。 */ typedef struct _modbus_tcp { /* * 事务标识符。 * 摘录自《MODBUS Messaging on TCP/IP Implementation Guide V1.0b》 * (第 23/46 页): * 事务标识符用于将未来的响应与对应的请求关联起来。 * 在每个 TCP 连接上,此标识符是唯一的。 * 客户端生成一个事务ID,服务器在响应中原样返回, * 从而允许客户端匹配请求和响应。 */ uint16_t t_id; /* TCP 端口号(标准 Modbus TCP 端口为 502) */ int port; /* IP 地址字符串(如 "192.168.1.100"),最大 15 字符 + '\0' 结尾 */ char ip[16]; } modbus_tcp_t; /* * modbus_tcp_pi_t — Modbus TCP PI(协议独立)后端的私有数据结构体。 * "PI" 表示 Protocol Independent(协议独立),即使用 getaddrinfo() * 解析的主机名和服务名,而非仅限 IPv4 地址。 * 这使得该结构体同时支持 IPv4 和 IPv6 地址。 */ typedef struct _modbus_tcp_pi { /* 事务标识符(放在首位,便于快速访问) */ uint16_t t_id; /* TCP 端口号 */ int port; /* 主机节点名(如 "modbus-server.local" 或 IPv4/IPv6 地址字符串) */ char *node; /* 服务名(如 "502" 或 "/etc/services 中定义的服务名") */ char *service; } modbus_tcp_pi_t; #endif /* MODBUS_TCP_PRIVATE_H */