feat(AT): LINK 对外接口改为 S1/S2/C1/C2

- LINK 首参数由数字索引改为角色名(S1/S2/C1/C2),内部映射对用户隐藏

- LINK 查询与摘要回包统一输出角色名

- LINK 配置成功后返回当前记录,格式与查询一致

- 同步更新 AT 使用手册中的命令示例与字段说明
This commit is contained in:
2026-04-04 15:44:18 +08:00
parent d5b2506269
commit c5b2bdd2d2
2 changed files with 82 additions and 39 deletions
+25 -27
View File
@@ -30,7 +30,7 @@
1. `MUX`:全局数据承载模式开关 1. `MUX`:全局数据承载模式开关
2. `NET`:全局静态网络配置记录 2. `NET`:全局静态网络配置记录
3. `LINK[idx]`:按索引组织的链路配置记录 3. `LINK[ROLE]`:按角色名组织的链路配置记录`S1/S2/C1/C2`
约束如下: 约束如下:
@@ -127,18 +127,16 @@ NET = 192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01
### 7.3 LINK 默认值 ### 7.3 LINK 默认值
```text ```text
LINK0 = 1,8080,0.0.0.0,0,U0 LINK:S1 = 1,8080,0.0.0.0,0,U0
LINK1 = 0,8081,0.0.0.0,0,U1 LINK:S2 = 0,8081,0.0.0.0,0,U1
LINK2 = 1,9001,192.168.1.200,9000,U1 LINK:C1 = 1,9001,192.168.1.200,9000,U1
LINK3 = 0,9002,192.168.1.201,9001,U0 LINK:C2 = 0,9002,192.168.1.201,9001,U0
``` ```
固定索引映射 说明
- `0 = S1` - `S1/S2/C1/C2` 为对外可见角色名
- `1 = S2` - 内部索引映射由固件管理,不对外暴露
- `2 = C1`
- `3 = C2`
UART 记号约定: UART 记号约定:
@@ -174,10 +172,10 @@ AT+QUERY\r\n
```text ```text
+NET:IP=192.168.1.100,MASK=255.255.255.0,GW=192.168.1.1,MAC=02:00:00:00:00:01 +NET:IP=192.168.1.100,MASK=255.255.255.0,GW=192.168.1.1,MAC=02:00:00:00:00:01
+LINK:0,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0 +LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
+LINK:1,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1 +LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
+LINK:2,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1 +LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
+LINK:3,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0 +LINK:C2,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0
+MUX:0 +MUX:0
+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20 +MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20
+BAUD:U0=115200,U1=115200 +BAUD:U0=115200,U1=115200
@@ -242,19 +240,19 @@ OK
#### 设置单条 LINK 记录 #### 设置单条 LINK 记录
```text ```text
AT+LINK=0,1,8080,0.0.0.0,0,U0\r\n AT+LINK=S1,1,8080,0.0.0.0,0,U0\r\n
AT+LINK=2,1,9001,192.168.1.200,9000,U1\r\n AT+LINK=C1,1,9001,192.168.1.200,9000,U1\r\n
``` ```
字段顺序: 字段顺序:
```text ```text
IDX,EN,LPORT,RIP,RPORT,UART ROLE,EN,LPORT,RIP,RPORT,UART
``` ```
字段说明: 字段说明:
- `IDX`:实例索引,固定为 `0..3` - `ROLE`:链路角色名,固定为 `S1/S2/C1/C2`
- `EN``0/1` - `EN``0/1`
- `LPORT`:本地端口 - `LPORT`:本地端口
- `RIP`:对端 IP - `RIP`:对端 IP
@@ -270,13 +268,13 @@ IDX,EN,LPORT,RIP,RPORT,UART
#### 查询单条 LINK #### 查询单条 LINK
```text ```text
AT+LINK=0\r\n AT+LINK=S1\r\n
``` ```
返回示例: 返回示例:
```text ```text
+LINK:0,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0 +LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
OK OK
``` ```
@@ -289,10 +287,10 @@ AT+LINK?\r\n
返回示例: 返回示例:
```text ```text
+LINK:0,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0 +LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
+LINK:1,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1 +LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
+LINK:2,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1 +LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
+LINK:3,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0 +LINK:C2,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0
OK OK
``` ```
@@ -353,9 +351,9 @@ OK: Defaults restored
```text ```text
AT+NET=192.168.1.123,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n AT+NET=192.168.1.123,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n
AT+LINK=0,1,10001,0.0.0.0,0,U1\r\n AT+LINK=S1,1,10001,0.0.0.0,0,U1\r\n
AT+LINK=1,1,10003,0.0.0.0,0,U1\r\n AT+LINK=S2,1,10003,0.0.0.0,0,U1\r\n
AT+LINK=2,1,20001,192.168.1.201,10002,U0\r\n AT+LINK=C1,1,20001,192.168.1.201,10002,U0\r\n
AT+MUX=1\r\n AT+MUX=1\r\n
AT+SAVE\r\n AT+SAVE\r\n
AT+RESET\r\n AT+RESET\r\n
+57 -12
View File
@@ -158,6 +158,48 @@ static const char *link_uart_to_str(uint8_t uart)
return (uart == LINK_UART_U1) ? "U1" : "U0"; return (uart == LINK_UART_U1) ? "U1" : "U0";
} }
static const char *link_index_to_name(uint32_t index)
{
switch (index) {
case CONFIG_LINK_S1:
return "S1";
case CONFIG_LINK_S2:
return "S2";
case CONFIG_LINK_C1:
return "C1";
case CONFIG_LINK_C2:
return "C2";
default:
return "?";
}
}
static int parse_link_name(const char *value, uint32_t *index)
{
if (value == NULL || index == NULL) {
return -1;
}
if (equals_ignore_case(value, "S1")) {
*index = CONFIG_LINK_S1;
return 0;
}
if (equals_ignore_case(value, "S2")) {
*index = CONFIG_LINK_S2;
return 0;
}
if (equals_ignore_case(value, "C1")) {
*index = CONFIG_LINK_C1;
return 0;
}
if (equals_ignore_case(value, "C2")) {
*index = CONFIG_LINK_C2;
return 0;
}
return -1;
}
static bool parse_command_with_value(const char *cmd, const char *name, const char **value) static bool parse_command_with_value(const char *cmd, const char *name, const char **value)
{ {
size_t name_len; size_t name_len;
@@ -309,10 +351,10 @@ static at_result_t handle_summary_query(char *response, uint16_t max_len)
snprintf(response, max_len, snprintf(response, max_len,
"+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n" "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n"
"+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:S1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:S2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:C1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:C2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+MUX:%u\r\n" "+MUX:%u\r\n"
"+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20\r\n" "+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20\r\n"
"+BAUD:U0=%lu,U1=%lu\r\n" "+BAUD:U0=%lu,U1=%lu\r\n"
@@ -356,8 +398,8 @@ static at_result_t handle_link_query(uint32_t index, char *response, uint16_t ma
config_ip_to_str(g_config.links[index].remote_ip, rip_str); config_ip_to_str(g_config.links[index].remote_ip, rip_str);
snprintf(response, snprintf(response,
max_len, max_len,
"+LINK:%lu,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n", "+LINK:%s,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n",
index, link_index_to_name(index),
g_config.links[index].enabled, g_config.links[index].enabled,
g_config.links[index].local_port, g_config.links[index].local_port,
rip_str, rip_str,
@@ -376,10 +418,10 @@ static at_result_t handle_all_link_query(char *response, uint16_t max_len)
snprintf(response, snprintf(response,
max_len, max_len,
"+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:S1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:S2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:C1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" "+LINK:C2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"OK\r\n", "OK\r\n",
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart), g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart), g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
@@ -581,7 +623,7 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
cursor = value_copy; cursor = value_copy;
token = config_next_token(&cursor); token = config_next_token(&cursor);
if (token == NULL || parse_u32_value(token, 0u, CONFIG_LINK_COUNT - 1u, &index) != 0) { if (token == NULL || parse_link_name(token, &index) != 0) {
snprintf(response, max_len, "ERROR: Invalid route field\r\n"); snprintf(response, max_len, "ERROR: Invalid route field\r\n");
return AT_INVALID_PARAM; return AT_INVALID_PARAM;
} }
@@ -623,7 +665,10 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
memcpy(g_config.links[index].remote_ip, rip, sizeof(rip)); memcpy(g_config.links[index].remote_ip, rip, sizeof(rip));
g_config.links[index].remote_port = (uint16_t)remote_port; g_config.links[index].remote_port = (uint16_t)remote_port;
g_config.links[index].uart = uart; g_config.links[index].uart = uart;
snprintf(response, max_len, "OK\r\n"); if (handle_link_query(index, response, max_len) != AT_OK) {
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
return AT_INVALID_PARAM;
}
return AT_NEED_REBOOT; return AT_NEED_REBOOT;
} }