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:
parent
9560ea7653
commit
8cf00ad14d
|
|
@ -15,7 +15,8 @@
|
||||||
"type": "tcp_server",
|
"type": "tcp_server",
|
||||||
"local_ip": "0.0.0.0",
|
"local_ip": "0.0.0.0",
|
||||||
"local_port": 2404,
|
"local_port": 2404,
|
||||||
"desc": "主站通道 (libiec直管, 不走com_router路由)"
|
"bind_app": "iec",
|
||||||
|
"desc": "IEC-104主站通道 (:2404) — com_router直接投递给libiec"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"forward_rules": [
|
"forward_rules": [
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,18 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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_fd_get(const char *key);
|
||||||
int com_router_id_get_by_bind(const char *bind_app);
|
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);
|
int app_com_router_init1(void *arg);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,53 @@ int g_forward_count = 0;
|
||||||
|
|
||||||
static int g_config_loaded = 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);
|
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 *key = cJSON_GetObjectItem(item, "key");
|
||||||
cJSON *type = cJSON_GetObjectItem(item, "type");
|
cJSON *type = cJSON_GetObjectItem(item, "type");
|
||||||
|
cJSON *bind = cJSON_GetObjectItem(item, "bind_app");
|
||||||
cJSON *desc = cJSON_GetObjectItem(item, "desc");
|
cJSON *desc = cJSON_GetObjectItem(item, "desc");
|
||||||
|
|
||||||
if(NULL == key || NULL == type) continue;
|
if(NULL == key || NULL == type) continue;
|
||||||
|
|
||||||
strncpy(ch->key, key->valuestring, 31);
|
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"))
|
if(0 == strcmp(type->valuestring, "uart"))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@
|
||||||
|
|
||||||
#include <string.h>
|
#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[];
|
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)
|
void com_router_recv_cb(int id, int fd, const unsigned char *p_rx, int rx_len)
|
||||||
{
|
{
|
||||||
com_channel_t *ch = NULL;
|
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;
|
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) */
|
/* a) ICP67 维护软件回复 (dev_addr=0) */
|
||||||
if(rx_len >= (int)sizeof(stru_head)
|
if(rx_len >= (int)sizeof(stru_head)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue