RTU_ALL_AI/docs/public/libcomm/API-libcomm.md

367 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API-libcomm — 通讯抽象层模块
> 版本: v1.0
> 日期: 2026-07-08
> 状态: 已实现TCP/UDP 完整, UART 待实现)
---
## 1. 模块概述
libcomm 是 RTU 通讯装置的**通讯抽象层**为上层协议栈Modbus、IEC104 等)提供统一的 TCP/UDP/UART 通讯接口。
### 设计思想
- **类型统一**:同一套 API 操作 TCP 服务器/客户端、UDP、UART
- **异步事件驱动**:基于 epoll 单线程事件循环,非阻塞 I/O
- **回调解耦**:接收数据和状态变化通过回调通知上层
- **纯 C 接口**`extern "C"` 包裹C/C++ 均可调用
### 架构
```
┌──────────────────────────────────────────────┐
│ 协议层libmodbus / lib60870
│ 调用 comm_create / comm_send / recv_cb │
├──────────────────────────────────────────────┤
│ libcomm纯 C 接口层) │
│ ┌──────────┬──────────┬──────────────────┐ │
│ │ TCP 服务 │ TCP 客户 │ UDP 服务 / 客户 │ │
│ │ 器端 │ 端 │ │ │
│ └──────────┴──────────┴──────────────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ epoll 事件循环 + fd 管理 + 状态机 │ │
│ └──────────────────────────────────────────┘ │
├──────────────────────────────────────────────┤
│ 实现层C++: map/mutex + POSIX socket
└──────────────────────────────────────────────┘
```
### 源文件结构
```
libcomm/
├── inc/
│ └── myComm.h # 对外 C 接口头文件
└── src/
├── comm_internal.h # 内部共享类型/全局变量/函数声明
├── comm_core.cpp # 核心: create/destroy/run/stop + epoll 事件循环
├── comm_tcp.cpp # TCP 服务器/客户端
├── comm_udp.cpp # UDP 通讯
└── comm_uart.cpp # UART 串口通讯
```
---
## 2. 数据结构
### 2.1 通讯类型
```c
typedef enum
{
COMM_TYPE_TCP_SERVER = 0, /** TCP 服务器(监听+accept */
COMM_TYPE_TCP_CLIENT, /** TCP 客户端 */
COMM_TYPE_UDP_SERVER, /** UDP 服务器 */
COMM_TYPE_UDP_CLIENT, /** UDP 客户端 */
COMM_TYPE_UART /** UART 串口 */
} enum_comm_type;
```
### 2.2 通讯状态
```c
typedef enum
{
COMM_STATE_DISCONNECTED = 0, /** 未连接 */
COMM_STATE_CONNECTING, /** 连接中 */
COMM_STATE_CONNECTED, /** 已连接 */
COMM_STATE_ERROR /** 错误状态 */
} enum_comm_state;
```
### 2.3 参数结构体
```c
/** TCP 参数 */
typedef struct
{
char local_ip[64]; /** 本地 IP服务器用 */
int local_port; /** 本地端口(服务器用) */
char remote_ip[64]; /** 远端 IP客户端用 */
int remote_port; /** 远端端口(客户端用) */
} stru_tcp_para;
/** UDP 参数 */
typedef struct
{
char local_ip[64]; /** 本地 IP */
int local_port; /** 本地端口 */
char remote_ip[64]; /** 远端 IP客户端用 */
int remote_port; /** 远端端口(客户端用) */
} stru_udp_para;
/** UART 参数 */
typedef struct
{
char device[128]; /** 串口设备路径(如 /dev/ttyS0 */
int baudrate; /** 波特率 */
int data_bits; /** 数据位5/6/7/8 */
int stop_bits; /** 停止位1/2 */
char parity; /** 校验位('N'/'E'/'O' */
} stru_uart_para;
/** 通讯参数联合体 */
typedef union
{
stru_tcp_para tcp;
stru_udp_para udp;
stru_uart_para uart;
} union_comm_para;
```
### 2.4 回调函数类型
```c
/** 接收数据回调 */
typedef void (*comm_recv_cb)(int id, int fd, const unsigned char *data, int len);
/** 状态变化回调 */
typedef void (*comm_state_cb)(int id, int fd, int state);
```
### 2.5 错误码
| 常量 | 值 | 说明 |
|------|-----|------|
| `COMM_OK` | 0 | 成功 |
| `COMM_ERR_PARAM` | -1 | 参数错误 |
| `COMM_ERR_MEM` | -2 | 内存不足 |
| `COMM_ERR_SOCKET` | -3 | Socket 创建失败 |
| `COMM_ERR_BIND` | -4 | 绑定失败 |
| `COMM_ERR_LISTEN` | -5 | 监听失败 |
| `COMM_ERR_CONNECT` | -6 | 连接失败 |
| `COMM_ERR_SEND` | -7 | 发送失败 |
| `COMM_ERR_NOT_FOUND` | -8 | 实例不存在 |
| `COMM_ERR_STATE` | -9 | 状态错误 |
| `COMM_ERR_INTERNAL` | -10 | 内部错误 |
---
## 3. 接口函数
### 3.1 comm_create — 创建通讯实例
```c
int comm_create(int type, int debug, const union_comm_para *para);
```
**参数**
- `type`通讯类型enum_comm_type
- `debug`是否打印调试信息1=打印, 0=静默)
- `para`:通讯参数(根据 type 选择对应成员)
**返回值**`>=0` 实例 ID`<0` 错误码
**使用场景**协议栈初始化时创建对应的通讯通道
---
### 3.2 comm_connect — 建立连接
```c
int comm_connect(int id);
```
**行为**按类型
- TCP 服务器bind + listen注册到 epoll
- TCP 客户端非阻塞 connect监听 EPOLLOUT 等待完成
- UDPbind 本地端口
- UARTopen 串口设备 + 配置波特率/数据位/停止位/校验
**返回值**`COMM_OK` 成功`<0` 错误码
---
### 3.3 comm_disconnect — 断开连接
```c
int comm_disconnect(int id);
```
关闭所有 fd监听 + 客户端 + 本地保留参数可重新 connect
---
### 3.4 comm_send — 发送数据
```c
int comm_send(int id, int fd, const unsigned char *data, int len);
```
**参数**
- `fd`目标 fdTCP 服务器需指定客户端 fd
- `data`/`len`数据与长度
**返回值**`>=0` 实际发送字节数,`<0` 错误码
**注意**UDP 客户端会自动 sendto 到创建时指定的远端地址
---
### 3.5 comm_recv_register — 注册接收回调
```c
int comm_recv_register(int id, comm_recv_cb cb);
```
epoll 检测到可读后自动调用此回调传入 `(id, fd, data, len)`
---
### 3.6 comm_state_register — 注册状态回调
```c
int comm_state_register(int id, comm_state_cb cb);
```
连接建立/断开/错误时回调传入 `(id, fd, state)`
---
### 3.7 comm_destroy — 销毁实例
```c
int comm_destroy(int id);
```
关闭所有 fd释放内存
---
### 3.8 comm_run / comm_stop — 事件循环
```c
int comm_run(void); /** 阻塞运行 epoll 事件循环 */
void comm_stop(void); /** 停止事件循环 */
```
**约定**调用者在独立线程中调用 `comm_run()`停止时调用 `comm_stop()`
---
## 4. 使用示例
### 4.1 TCP 客户端
```c
#include "myComm.h"
static void on_recv(int id, int fd, const unsigned char *data, int len)
{
printf("recv %d bytes on fd=%d\n", len, fd);
}
static void on_state(int id, int fd, int state)
{
if (state == COMM_STATE_CONNECTED)
{
printf("connected!\n");
}
}
void tcp_client_example(void)
{
union_comm_para para;
strcpy(para.tcp.remote_ip, "192.168.1.100");
para.tcp.remote_port = 502;
para.tcp.local_ip[0] = '\0';
para.tcp.local_port = 0;
int id = comm_create(COMM_TYPE_TCP_CLIENT, 1, &para);
comm_recv_register(id, on_recv);
comm_state_register(id, on_state);
comm_connect(id);
/* 在另一个线程启动事件循环 */
comm_run();
}
```
### 4.2 TCP 服务器
```c
void tcp_server_example(void)
{
union_comm_para para;
strcpy(para.tcp.local_ip, "0.0.0.0");
para.tcp.local_port = 502;
int id = comm_create(COMM_TYPE_TCP_SERVER, 1, &para);
comm_recv_register(id, on_recv);
comm_state_register(id, on_state);
comm_connect(id);
comm_run();
}
```
### 4.3 UDP
```c
void udp_example(void)
{
union_comm_para para;
strcpy(para.udp.local_ip, "0.0.0.0");
para.udp.local_port = 2404;
int id = comm_create(COMM_TYPE_UDP_SERVER, 1, &para);
comm_recv_register(id, on_recv);
comm_connect(id);
comm_run();
}
```
---
## 5. 依赖关系
```
myBase.h → libcomm
(无其他模块依赖)
```
libcomm 是阶段 2 的第一个模块被后续 `libmodbus`、`lib60870` 等协议栈依赖
---
## 6. 程序运行流程
```
comm_create(type, para)
→ 分配 stru_comm_instance拷贝参数返回 id
→ 首次调用时初始化 epoll + eventfd
comm_connect(id)
→ TCP_SERVER: socket → bind → listen → epoll_add(listen_fd)
→ TCP_CLIENT: socket → nonblock-connect → epoll_add(fd, EPOLLOUT)
→ UDP: socket → bind → epoll_add(fd)
→ UART: open(device) → tcsetattr → epoll_add(fd)
comm_run() [阻塞,需在独立线程中调用]
→ epoll_wait 循环
→ EPOLLIN (listen_fd) → accept → epoll_add(client_fd) → state_cb
→ EPOLLIN (data_fd) → recv → recv_cb
→ EPOLLOUT → getsockopt(SO_ERROR) → connect 完成 → state_cb
→ EPOLLERR/HUP/RDHUP → close_fd → state_cb(DISCONNECTED)
comm_stop()
→ write(eventfd) → epoll 收到 → g_running = false → 退出循环
```