feat(com_router): bind_app 协议绑定路由 — 端口→规约直投

## 设计
通道配置新增 bind_app 字段:
- bind_app="" (空)  → 现有帧内容分路 (FTU串口)
- bind_app="iec"    → 整帧直投 libiec (TCP :2404 主站)
- bind_app="self_ptl" → 整帧直投 self_ptl (备用)

新增 com_router_register_dispatch(bind_app, cb):
- 协议模块在 app_wiring() 注册回调
- recv_cb 发现通道 bind_app 匹配, 直接调用 dispatch
- 不需要帧解析 — 协议模块自行解码

## 路由优先级
1. forward_rule → 维护透传
2. bind_app 非空 → dispatch 投递
3. bind_app 空 → 帧内容分路 (ICP67 dev=0/IEC/ICP67 dev≠0)

## 配置示例
channel_cfg.json: tcp_master(:2404) bind_app="iec"
  → 外部主站连接 :2404 → com_router → libiec 回调
This commit is contained in:
ypc 2026-07-10 10:40:24 +08:00
parent 9560ea7653
commit 8cf00ad14d
4 changed files with 106 additions and 2 deletions

View File

@ -15,7 +15,8 @@
"type": "tcp_server",
"local_ip": "0.0.0.0",
"local_port": 2404,
"desc": "主站通道 (libiec直管, 不走com_router路由)"
"bind_app": "iec",
"desc": "IEC-104主站通道 (:2404) — com_router直接投递给libiec"
}
],
"forward_rules": [

View File

@ -21,6 +21,18 @@
extern "C" {
#endif
/* =========================== 协议分发回调 =========================== */
#define MAX_DISPATCH_APPS 8
/**
* @brief bind_app ,
* @param interface (ch_id<<16)|fd
* @param data (, )
* @param len
*/
typedef void (*com_router_dispatch_cb)(uint32_t interface, const unsigned char *data, int len);
/* =========================== 通道配置结构 =========================== */
/**
@ -76,6 +88,18 @@ uint32_t com_router_interface_get(const char *key);
int com_router_fd_get(const char *key);
int com_router_id_get_by_bind(const char *bind_app);
/**
* @brief bind_app
* @param bind_app ( "iec", "self_ptl", "iec61850")
* @param cb ,
* @return 0 , -1 ()
*
* @note : app_wiring() , app_com_router_init1
* bind_app ,
* ()
*/
int com_router_register_dispatch(const char *bind_app, com_router_dispatch_cb cb);
/* =========================== 应用线程入口 =========================== */
int app_com_router_init1(void *arg);

View File

@ -29,6 +29,53 @@ int g_forward_count = 0;
static int g_config_loaded = 0;
/* =========================== 协议分发注册表 =========================== */
typedef struct
{
char bind_app[16];
com_router_dispatch_cb cb;
} dispatch_entry_t;
static dispatch_entry_t g_dispatch_table[MAX_DISPATCH_APPS];
static int g_dispatch_count = 0;
int com_router_register_dispatch(const char *bind_app, com_router_dispatch_cb cb)
{
if(NULL == bind_app || NULL == cb || g_dispatch_count >= MAX_DISPATCH_APPS)
{
return -1;
}
strncpy(g_dispatch_table[g_dispatch_count].bind_app, bind_app, 15);
g_dispatch_table[g_dispatch_count].cb = cb;
g_dispatch_count++;
LOG_I("com_router: registered dispatch for [%s]", bind_app);
return 0;
}
/* 内部查找: 按 bind_app 取回调, 未找到返回 NULL */
com_router_dispatch_cb com_router_dispatch_find(const char *bind_app)
{
int i;
if(NULL == bind_app || '\0' == bind_app[0])
{
return NULL;
}
for(i = 0; i < g_dispatch_count; i++)
{
if(0 == strcmp(g_dispatch_table[i].bind_app, bind_app))
{
return g_dispatch_table[i].cb;
}
}
return NULL;
}
/* =========================== 前向声明 =========================== */
extern void com_router_recv_cb(int id, int fd, const unsigned char *data, int len);
@ -59,11 +106,14 @@ static int com_router_parse_channels(cJSON *arr)
cJSON *key = cJSON_GetObjectItem(item, "key");
cJSON *type = cJSON_GetObjectItem(item, "type");
cJSON *bind = cJSON_GetObjectItem(item, "bind_app");
cJSON *desc = cJSON_GetObjectItem(item, "desc");
if(NULL == key || NULL == type) continue;
strncpy(ch->key, key->valuestring, 31);
if(NULL != bind) { strncpy(ch->bind_app, bind->valuestring, 15); }
else { ch->bind_app[0] = '\0'; }
if(0 == strcmp(type->valuestring, "uart"))
{

View File

@ -11,6 +11,11 @@
#include <string.h>
/* =========================== 前向声明 =========================== */
/* 来自 com_router.cpp 的内部函数 */
extern com_router_dispatch_cb com_router_dispatch_find(const char *bind_app);
/* =========================== 全局变量引用 =========================== */
extern com_channel_t g_channels[];
@ -119,6 +124,13 @@ static int com_router_fwd_send_back(int serial_ch_id,
/* =========================== 核心: 数据接收回调 =========================== */
/*
* :
* 1. forward_rule.sock_fd ()
* 2. bind_app
* 3. bind_app (FTU ) (ICP67/IEC )
*/
void com_router_recv_cb(int id, int fd, const unsigned char *p_rx, int rx_len)
{
com_channel_t *ch = NULL;
@ -155,7 +167,24 @@ void com_router_recv_cb(int id, int fd, const unsigned char *p_rx, int rx_len)
return;
}
/* Step 2: FTU 串口接收 → 按帧分路 */
/* Step 2: bind_app 协议绑定 → 直接投递给注册的协议模块 */
if('\0' != ch->bind_app[0])
{
com_router_dispatch_cb dispatch = com_router_dispatch_find(ch->bind_app);
if(NULL != dispatch)
{
uint32_t interface = ((uint32_t)id << 16) | (uint32_t)fd;
dispatch(interface, p_rx, rx_len);
return;
}
LOG_E("com_router: bind_app [%s] has no dispatch registered", ch->bind_app);
return;
}
/* Step 3: bind_app 为空 → FTU 串口按帧内容分路 */
/* a) ICP67 维护软件回复 (dev_addr=0) */
if(rx_len >= (int)sizeof(stru_head)