From e9c24ae736d9adcefe4fb01cfb4f8311c447167d Mon Sep 17 00:00:00 2001 From: wangfq Date: Thu, 2 Jul 2026 10:33:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20DBNetClient=20Loop=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E5=AE=8C=E5=96=84=20+=20vd960DBN=20=E5=8F=91=E9=80=81=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vd960DBN: - loop_uart_proto.c: 所有发送函数添加 LUP Tx 调试打印 - tcp_json_srv.c: 新增 loop_version_query/loop_reset/loop_factory_init/ loop_sens_read/loop_sens_write 命令处理器 + 延迟响应解析 - 修复 loop_sens_write 未设置命令状态机和错误使用解析函数的问题 DBNetClient: - tcp_json_client.py: 新增 full Loop MCU API (6 条命令) - main.py: 线圈参数标签页增加版本/复位/出厂/灵敏度操作按钮 --- DBNetClient/main.py | 64 +++++- DBNetClient/tcp_json_client.py | 31 ++- .../APP/loop_uart_proto.c | 18 +- .../APP/tcp_json_srv.c | 210 ++++++++++++++++-- 4 files changed, 296 insertions(+), 27 deletions(-) diff --git a/DBNetClient/main.py b/DBNetClient/main.py index 449b29c..6c1ba68 100644 --- a/DBNetClient/main.py +++ b/DBNetClient/main.py @@ -184,14 +184,44 @@ class DBNetApp: def _tab_loop(self, parent): f = ttk.Frame(parent, padding=10) - ttk.Button(f, text="查询线圈参数 (loop_param_query)", - command=self._do_loop_param_query).pack(anchor=tk.W, pady=3) - ttk.Label(f, text="参数设置通过 BLE/小程序,或 Raw JSON 标签页发送。", - foreground="gray").pack(anchor=tk.W, pady=5) - self.loop_text = scrolledtext.ScrolledText(f, height=12, wrap=tk.WORD, + # Row 0: Version + System + ttk.Label(f, text="地感MCU 操作:", font=("", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=(0,5)) + btn_row = ttk.Frame(f) + btn_row.grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=2) + ttk.Button(btn_row, text="查询版本", command=self._do_loop_version).pack(side=tk.LEFT, padx=2) + ttk.Button(btn_row, text="复位MCU", command=self._do_loop_reset).pack(side=tk.LEFT, padx=2) + ttk.Button(btn_row, text="出厂初始化", command=self._do_loop_factory_init).pack(side=tk.LEFT, padx=2) + + ttk.Separator(f, orient=tk.HORIZONTAL).grid(row=2, column=0, columnspan=2, + sticky=tk.EW, pady=8) + + # Row 3: 灵敏度 + ttk.Label(f, text="线圈灵敏度 (0x8A):", font=("", 10, "bold")).grid(row=3, column=0, sticky=tk.W) + sens_row = ttk.Frame(f) + sens_row.grid(row=4, column=0, columnspan=2, sticky=tk.W, pady=2) + ttk.Button(sens_row, text="读取灵敏度", command=self._do_loop_sens_read).pack(side=tk.LEFT, padx=2) + + ttk.Separator(f, orient=tk.HORIZONTAL).grid(row=5, column=0, columnspan=2, + sticky=tk.EW, pady=8) + + # Row 6: 参数 + ttk.Label(f, text="线圈参数 (0x63/0x64):", font=("", 10, "bold")).grid(row=6, column=0, sticky=tk.W) + param_row = ttk.Frame(f) + param_row.grid(row=7, column=0, columnspan=2, sticky=tk.W, pady=2) + ttk.Button(param_row, text="查询参数", command=self._do_loop_param_query).pack(side=tk.LEFT, padx=2) + ttk.Label(param_row, text="参数设置通过 Raw JSON 标签页发送 loop_param_set。", + foreground="gray").pack(side=tk.LEFT, padx=10) + + ttk.Separator(f, orient=tk.HORIZONTAL).grid(row=8, column=0, columnspan=2, + sticky=tk.EW, pady=8) + + # Response display + self.loop_text = scrolledtext.ScrolledText(f, height=14, wrap=tk.WORD, font=("Consolas", 9)) - self.loop_text.pack(fill=tk.BOTH, expand=True) + self.loop_text.grid(row=9, column=0, columnspan=2, sticky="nsew") + f.rowconfigure(9, weight=1) + f.columnconfigure(0, weight=1) return f def _tab_system(self, parent): @@ -418,7 +448,27 @@ class DBNetApp: topic_sub=self.iot_vars["topic_sub"].get().strip()), lambda r: self._show_label(self.iot_resp, r)) - # Loop + # Loop MCU + def _do_loop_version(self): + self._bg_run(lambda: self.client.loop_version_query(), + lambda r: self._show_resp(self.loop_text, r)) + + def _do_loop_reset(self): + if not messagebox.askyesno("确认", "复位地感MCU,确定?"): + return + self._bg_run(lambda: self.client.loop_reset(), + lambda r: self._show_resp(self.loop_text, r)) + + def _do_loop_factory_init(self): + if not messagebox.askyesno("确认", "地感MCU恢复出厂设置,确定?"): + return + self._bg_run(lambda: self.client.loop_factory_init(), + lambda r: self._show_resp(self.loop_text, r)) + + def _do_loop_sens_read(self): + self._bg_run(lambda: self.client.loop_sens_read(), + lambda r: self._show_resp(self.loop_text, r)) + def _do_loop_param_query(self): self._bg_run(lambda: self.client.loop_param_query(), lambda r: self._show_resp(self.loop_text, r)) diff --git a/DBNetClient/tcp_json_client.py b/DBNetClient/tcp_json_client.py index 40f48bc..b0aef1a 100644 --- a/DBNetClient/tcp_json_client.py +++ b/DBNetClient/tcp_json_client.py @@ -273,12 +273,41 @@ class DBNetClient: def device_reset(self) -> dict: return self._tcp.send_command("device_reset") - # Loop + # Loop MCU Commands + def loop_version_query(self) -> dict: + """获取地感MCU版本号 (CMD 0x4A)""" + return self._tcp.send_command("loop_version_query") + + def loop_reset(self) -> dict: + """复位地感MCU (CMD 0x6D)""" + return self._tcp.send_command("loop_reset") + + def loop_factory_init(self) -> dict: + """地感MCU出厂初始化 (CMD 0x92)""" + return self._tcp.send_command("loop_factory_init") + + def loop_sens_read(self) -> dict: + """读取线圈灵敏度列表 (CMD 0x8A Read)""" + return self._tcp.send_command("loop_sens_read") + + def loop_sens_write(self, channels: list[dict]) -> dict: + """写入线圈灵敏度列表 (CMD 0x8A Write) + + channels: [{"ch": 1, "sens_in": 100, "sens_out": 80}, ...] + """ + return self._tcp.send_command("loop_sens_write", + {"channels": channels}) + def loop_param_query(self) -> dict: + """读取地感多路参数 (CMD 0x64)""" return self._tcp.send_command("loop_param_query") def loop_param_set(self, channels: list[dict], auto_mode: bool = False) -> dict: + """设置地感多路参数 (CMD 0x63) + + channels: [{"ch": 1, "sensitivity": 7, "freq_level": 0, ...}, ...] + """ return self._tcp.send_command("loop_param_set", {"auto_mode": auto_mode, "channels": channels}) diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c index 69ebd91..411491b 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c @@ -483,18 +483,30 @@ void lup_cmd_on_response(const uint8_t *pkg, uint16_t len) * High-level Send Commands *===========================================================================*/ +static void lup_debug_tx(const uint8_t *buf, uint16_t len) +{ + uint8_t i; + PRINT("LUP Tx:"); + for (i = 0; i < len; i++) { + PRINT(" %02X", buf[i]); + } + PRINT("\n"); +} + void lup_send_get_version(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_get_version(buf); + lup_debug_tx(buf, len); UART2_SendString(buf, len); - lup_cmd_begin(LUP_CMD_GET_VERSION, 200); // 200ms timeout + lup_cmd_begin(LUP_CMD_GET_VERSION, 200); } void lup_send_reset(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_reset(buf); + lup_debug_tx(buf, len); UART2_SendString(buf, len); // No response expected } @@ -503,6 +515,7 @@ void lup_send_factory_init(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_factory_init(buf); + lup_debug_tx(buf, len); UART2_SendString(buf, len); lup_cmd_begin(LUP_CMD_FACTORY_INIT, 500); } @@ -511,6 +524,7 @@ void lup_send_sensitivity_read(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_sensitivity_read(buf); + lup_debug_tx(buf, len); UART2_SendString(buf, len); lup_cmd_begin(LUP_CMD_SENSITIVITY, 200); } @@ -519,6 +533,7 @@ void lup_send_set_param(const LUP_ParamSet *ps) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_set_param(buf, ps); + lup_debug_tx(buf, len); UART2_SendString(buf, len); lup_cmd_begin(LUP_CMD_SET_PARAM, 500); } @@ -527,6 +542,7 @@ void lup_send_get_param(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_get_param(buf); + lup_debug_tx(buf, len); UART2_SendString(buf, len); lup_cmd_begin(LUP_CMD_GET_PARAM, 500); } diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c index 533f770..63f835c 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c @@ -562,6 +562,93 @@ static void handle_report_config(uint8_t socket, uint32_t msg_id, const char *js PRINT("JSON: report_config (push not yet implemented)\n"); } +/* 4.16 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */ +static void handle_loop_version_query(uint8_t socket, uint32_t msg_id, const char *json) { + lup_send_get_version(); + g_json_pending.active = 1; + g_json_pending.socket = socket; + g_json_pending.msg_id = msg_id; + strncpy(g_json_pending.cmd, "loop_version_query", sizeof(g_json_pending.cmd) - 1); + g_json_pending.deadline = mstick() + 300; + PRINT("JSON: loop_version_query sent\n"); +} + +/* 4.17 loop_reset — 复位地感MCU (CMD 0x6D, 无回复) */ +static void handle_loop_reset(uint8_t socket, uint32_t msg_id, const char *json) { + lup_send_reset(); + json_send_ok(socket, msg_id, "loop_reset", NULL); + PRINT("JSON: loop_reset sent (no response expected)\n"); +} + +/* 4.18 loop_factory_init — 地感MCU出厂初始化 (CMD 0x92) */ +static void handle_loop_factory_init(uint8_t socket, uint32_t msg_id, const char *json) { + lup_send_factory_init(); + g_json_pending.active = 1; + g_json_pending.socket = socket; + g_json_pending.msg_id = msg_id; + strncpy(g_json_pending.cmd, "loop_factory_init", sizeof(g_json_pending.cmd) - 1); + g_json_pending.deadline = mstick() + 600; + PRINT("JSON: loop_factory_init sent\n"); +} + +/* 4.19 loop_sens_read — 读取线圈灵敏度列表 (CMD 0x8A Read) */ +static void handle_loop_sens_read(uint8_t socket, uint32_t msg_id, const char *json) { + lup_send_sensitivity_read(); + g_json_pending.active = 1; + g_json_pending.socket = socket; + g_json_pending.msg_id = msg_id; + strncpy(g_json_pending.cmd, "loop_sens_read", sizeof(g_json_pending.cmd) - 1); + g_json_pending.deadline = mstick() + 300; + PRINT("JSON: loop_sens_read sent\n"); +} + +/* 4.20 loop_sens_write — 写入线圈灵敏度列表 (CMD 0x8A Write) */ +static void handle_loop_sens_write(uint8_t socket, uint32_t msg_id, const char *json) { + char *data = json_get_data_str(json); + if (!data) { + json_send_error(socket, msg_id, "loop_sens_write", JSON_CODE_PARAM_ERR, "missing data"); + return; + } + + LUP_Sensitivity sens; + memset(&sens, 0, sizeof(sens)); + sens.rw = LUP_SENS_RW_WRITE; + + uint32_t amount = json_get_uint_field(data, "\"amount\""); + if (amount == 0 || amount > LUP_COIL_COUNT) amount = LUP_COIL_COUNT; + sens.amount = (uint8_t)amount; + + char key[16]; + uint8_t i; + for (i = 0; i < sens.amount; i++) { + snprintf(key, sizeof(key), "\"ch%d\"", i + 1); + char ch_data[256] = {0}; + reset_tmp(); + simple_parse_json(data, key, g_tmp_value); + if (strlen(g_tmp_value) == 0) continue; + strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1); + + sens.sens_in[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_in\""); + sens.sens_out[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_out\""); + } + free(data); + + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_sensitivity_write(buf, &sens); + UART2_SendString(buf, len); + PRINT("LUP Tx:"); + for (i = 0; i < len; i++) PRINT(" %02X", buf[i]); + PRINT("\n"); + lup_cmd_begin(LUP_CMD_SENSITIVITY, 300); + + g_json_pending.active = 1; + g_json_pending.socket = socket; + g_json_pending.msg_id = msg_id; + strncpy(g_json_pending.cmd, "loop_sens_write", sizeof(g_json_pending.cmd) - 1); + g_json_pending.deadline = mstick() + 300; + PRINT("JSON: loop_sens_write sent (ch=%d)\n", sens.amount); +} + /*=========================================================================== * Command Dispatch Table @@ -573,21 +660,26 @@ typedef struct { } JsonCmdEntry; static const JsonCmdEntry g_cmd_table[] = { - {"pwd_verify", handle_pwd_verify, 0}, - {"dev_serial_set", handle_dev_serial_set, 1}, - {"dev_info_query", handle_dev_info_query, 1}, - {"ssc_net_set", handle_ssc_net_set, 1}, - {"ssc_net_query", handle_ssc_net_query, 1}, - {"iot_net_set", handle_iot_net_set, 1}, - {"iot_net_query", handle_iot_net_query, 1}, - {"iot_topic_set", handle_iot_topic_set, 1}, - {"iot_topic_query", handle_iot_topic_query, 1}, - {"pwd_set", handle_pwd_set, 1}, - {"factory_reset", handle_factory_reset, 1}, - {"device_reset", handle_device_reset, 1}, - {"loop_param_set", handle_loop_param_set, 1}, - {"loop_param_query", handle_loop_param_query, 1}, - {"report_config", handle_report_config, 1}, + {"pwd_verify", handle_pwd_verify, 0}, + {"dev_serial_set", handle_dev_serial_set, 1}, + {"dev_info_query", handle_dev_info_query, 1}, + {"ssc_net_set", handle_ssc_net_set, 1}, + {"ssc_net_query", handle_ssc_net_query, 1}, + {"iot_net_set", handle_iot_net_set, 1}, + {"iot_net_query", handle_iot_net_query, 1}, + {"iot_topic_set", handle_iot_topic_set, 1}, + {"iot_topic_query", handle_iot_topic_query, 1}, + {"pwd_set", handle_pwd_set, 1}, + {"factory_reset", handle_factory_reset, 1}, + {"device_reset", handle_device_reset, 1}, + {"loop_param_set", handle_loop_param_set, 1}, + {"loop_param_query", handle_loop_param_query, 1}, + {"loop_version_query", handle_loop_version_query, 1}, + {"loop_reset", handle_loop_reset, 1}, + {"loop_factory_init", handle_loop_factory_init, 1}, + {"loop_sens_read", handle_loop_sens_read, 1}, + {"loop_sens_write", handle_loop_sens_write, 1}, + {"report_config", handle_report_config, 1}, }; #define JSON_CMD_COUNT (sizeof(g_cmd_table) / sizeof(g_cmd_table[0])) @@ -830,10 +922,7 @@ static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorRepo static void json_check_pending(void) { if (!g_json_pending.active) return; - uint8_t cmd = g_lup_cmd.pending_cmd; - if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) { - // Response received! if (strcmp(g_json_pending.cmd, "loop_param_set") == 0) { uint8_t success = 0; int ret = lup_parse_set_param_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success); @@ -864,6 +953,91 @@ static void json_check_pending(void) { g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, "Failed to parse Loop MCU response"); } + } else if (strcmp(g_json_pending.cmd, "loop_version_query") == 0) { + LUP_VersionInfo info; + memset(&info, 0, sizeof(info)); + int ret = lup_parse_version(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &info); + if (ret == 0) { + snprintf(info.version_str, sizeof(info.version_str), + "V%d.%d.%d (HW:%d.%d.%d)", + info.soft_main, info.soft_sub, info.soft_ssub, + info.hard_main, info.hard_sub, info.hard_ssub); + char data_json[256]; + snprintf(data_json, sizeof(data_json), + "{\"soft_ver\":\"%d.%d.%d\",\"hard_ver\":\"%d.%d.%d\"," + "\"version_str\":\"%s\"}", + info.soft_main, info.soft_sub, info.soft_ssub, + info.hard_main, info.hard_sub, info.hard_ssub, + info.version_str); + json_send_ok(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, data_json); + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + "Failed to parse version"); + } + } else if (strcmp(g_json_pending.cmd, "loop_factory_init") == 0) { + uint8_t success = 0; + int ret = lup_parse_factory_init_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success); + if (ret == 0 && success) { + json_send_ok(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, NULL); + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + ret == 0 ? "Factory init failed" : "Parse error"); + } + } else if (strcmp(g_json_pending.cmd, "loop_sens_read") == 0) { + LUP_Sensitivity sens; + memset(&sens, 0, sizeof(sens)); + int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens); + if (ret == 0) { + char data_json[512]; + char *p = data_json; + int rem = sizeof(data_json); + int w = snprintf(p, rem, "{\"amount\":%d,\"channels\":[", sens.amount); + p += w; rem -= w; + uint8_t i; + for (i = 0; i < sens.amount; i++) { + w = snprintf(p, rem, + "%s{\"ch\":%d,\"sens_value\":%d}", + (i > 0) ? "," : "", i + 1, sens.sens_in[i]); + p += w; rem -= w; + } + snprintf(p, rem, "]}"); + json_send_ok(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, data_json); + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + "Failed to parse sensitivity"); + } + } else if (strcmp(g_json_pending.cmd, "loop_sens_write") == 0) { + // 0x8A Write response returns sensitivity list + LUP_Sensitivity sens; + memset(&sens, 0, sizeof(sens)); + int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens); + if (ret == 0) { + char data_json[512]; + char *p = data_json; + int rem = sizeof(data_json); + int w = snprintf(p, rem, "{\"amount\":%d,\"channels\":[", sens.amount); + p += w; rem -= w; + uint8_t i; + for (i = 0; i < sens.amount; i++) { + w = snprintf(p, rem, + "%s{\"ch\":%d,\"sens_value\":%d}", + (i > 0) ? "," : "", i + 1, sens.sens_in[i]); + p += w; rem -= w; + } + snprintf(p, rem, "]}"); + json_send_ok(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, data_json); + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + "Failed to parse sensitivity response"); + } } lup_cmd_done();