diff --git a/DBNMQTTool/dbn_mqtt_tool/device_manager.py b/DBNMQTTool/dbn_mqtt_tool/device_manager.py index a2ce78b..3e36a2b 100644 --- a/DBNMQTTool/dbn_mqtt_tool/device_manager.py +++ b/DBNMQTTool/dbn_mqtt_tool/device_manager.py @@ -22,6 +22,7 @@ class DeviceInfo: online: bool = False loop_data: Optional[dict] = None events: list = field(default_factory=list) + extra_info: dict = field(default_factory=dict) # from initialize 消息 class DeviceManager: @@ -91,6 +92,17 @@ class DeviceManager: dev.online = True self._notify() + def mark_online(self, dev_serial: str, extra_info: Optional[dict] = None): + """V1.03: 设备上线初始化标记""" + with self._lock: + dev = self.get_or_create(dev_serial) + dev.online = True + dev.last_seen = datetime.now() + if extra_info: + dev.extra_info = extra_info + dev.soft_ver = extra_info.get("version", dev.soft_ver) + self._notify() + def set_offline(self): """标记所有设备离线(断开连接时调用)""" with self._lock: diff --git a/DBNMQTTool/dbn_mqtt_tool/protocol.py b/DBNMQTTool/dbn_mqtt_tool/protocol.py index 7b7fbab..33d1e51 100644 --- a/DBNMQTTool/dbn_mqtt_tool/protocol.py +++ b/DBNMQTTool/dbn_mqtt_tool/protocol.py @@ -65,6 +65,7 @@ CMD_REPORT_CONFIG = "report_config" CMD_LOOP_DATA = "loop_data" CMD_EVENT_REPORT = "event_report" CMD_HEARTBEAT = "heartbeat" +CMD_INITIALIZE = "initialize" # V1.03: 设备上电初始化 # 配置类命令 → topic CONFIG_SET_COMMANDS = {CMD_DEV_SERIAL_SET, CMD_SSC_NET_SET, CMD_IOT_NET_SET, CMD_IOT_TOPIC_SET, CMD_LOOP_PARAM_SET} diff --git a/DBNMQTTool/main.py b/DBNMQTTool/main.py index 47008b7..7d05f1c 100644 --- a/DBNMQTTool/main.py +++ b/DBNMQTTool/main.py @@ -30,7 +30,7 @@ from dbn_mqtt_tool.protocol import ( CMD_IOT_TOPIC_QUERY, CMD_LOOP_PARAM_QUERY, CMD_REPORT_CONFIG, CMD_SSC_NET_SET, CMD_IOT_NET_SET, CMD_IOT_TOPIC_SET, CMD_PWD_VERIFY, CMD_PWD_SET, CMD_FACTORY_RESET, CMD_DEVICE_RESET, - CMD_LOOP_DATA, CMD_EVENT_REPORT, CMD_HEARTBEAT, + CMD_LOOP_DATA, CMD_EVENT_REPORT, CMD_HEARTBEAT, CMD_INITIALIZE, ERROR_MSGS, FREQ_LEVELS, OUTPUT_MODES, EVENT_TYPES, data_ssc_net_set, data_iot_net_set, data_iot_topic_set, data_pwd_verify, data_pwd_set, data_report_config, @@ -857,6 +857,15 @@ class MainWindow(QMainWindow): self._devmgr.update_heartbeat(dev_serial, data) self._log_recv(topic, payload, f"heartbeat uptime={data.get('uptime', 0)}s") + elif cmd == CMD_INITIALIZE: + # V1.03: 设备上线初始化 + data = payload.get("data", {}) + dev_sn = data.get("dev_serial", dev_serial) + extra = data.get("extra_info", {}) + self._devmgr.mark_online(dev_sn, extra) + self._log_recv(topic, payload, f"initialize sn={dev_sn} ver={extra.get('version','?')}") + self._show_json({"online": dev_sn, "info": extra}) + # 旧协议 / 无 cmd 字段 的消息(如 Initialize) elif "Method" in payload: self._log_recv(topic, payload, f"Method={payload.get('Method', '?')}") diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/net_srv.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/net_srv.h index 32c55d9..971fe3b 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/net_srv.h +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/net_srv.h @@ -279,6 +279,7 @@ void manage_tcp_message(uint8_t socket, uint8_t *buf, uint32_t len); void manage_mqtt_recv_message(char * msg, int length); void poll_mqtt(void); void mqtt_publish(char *topic, char *message, int req_qos); +void dev_initialize_pub(void); // 发布上线 initialize 消息 (V1.03) void GetMacAddr(unsigned char *pMAC); diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c index 9bfa2ed..6b1b760 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c @@ -110,23 +110,17 @@ static int iot_mqtt_send_connect(void) { return iot_mqtt_send(buf, (uint16_t)len); } -/* 发送 MQTT SUBSCRIBE */ +/* 发送 MQTT SUBSCRIBE — V1.01 双主题协议 */ static int iot_mqtt_send_subscribe(void) { static uint8_t buf[256]; // static: 避免栈溢出 int len; char topic[IOT_MQTT_TOPIC_MAX_LEN]; - MQTTString topics[3]; - int qos[3] = {1, 1, 1}; + MQTTString topics[1]; + int qos[1] = {1}; int count = 0; - // 订阅服务器下发消息 - iot_make_topic(topic, sizeof(topic), "srv", "config", "set"); - topics[count].cstring = topic; topics[count].lenstring.len = 0; count++; - - iot_make_topic(topic, sizeof(topic), "srv", "config", "query"); - topics[count].cstring = topic; topics[count].lenstring.len = 0; count++; - - iot_make_topic(topic, sizeof(topic), "srv", "ctrl", NULL); + // V1.01: 仅订阅 dld960/{sn}/srv 双主题 + snprintf(topic, sizeof(topic), "dld960/%s/srv", g_iot_dev_serial); topics[count].cstring = topic; topics[count].lenstring.len = 0; count++; len = MQTTSerialize_subscribe(buf, sizeof(buf), 0, @@ -192,9 +186,9 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_ return; } - /* 构建响应 topic */ + /* 构建响应 topic — V1.01 双主题 */ char resp_topic[IOT_MQTT_TOPIC_MAX_LEN]; - iot_make_topic(resp_topic, sizeof(resp_topic), "dev", "config", "resp"); + snprintf(resp_topic, sizeof(resp_topic), "dld960/%s/dev", g_iot_dev_serial); /* 处理命令 — 复用 TCP JSON 的命令逻辑 */ /* 目前仅实现基本响应框架, 后续逐步对接 Loop MCU 命令 */ @@ -268,6 +262,9 @@ static void iot_handle_suback(void) { PRINT("IOT: ← SUBACK, topics subscribed\n"); g_iot_state = IOT_STATE_READY; _iot_reconnect_backoff = 0; // 连接成功, 重置退避 + + // V1.03: 订阅成功后发布 initialize 告知服务器上线 + dev_initialize_pub(); } /* 处理收到的 MQTT 数据 */ @@ -386,10 +383,9 @@ void iot_mqtt_publish_sensor(void) { return; } - /* 全通道一次性上报,字段按 DLD960_TCP_JSON协议.md §5.1: - ch, freq_level, has_car, loop_ok, freq_current, freq_diff, - sensitivity, condition, misc.type, misc.value - 注意: mqttBuf 需 ≥ 1024 才能容纳 4 通道全字段封包 (~846 字节) */ + /* 全通道一次性上报,字段按 DLD960_IoT_MQTT协议.md §5.2 V1.02: + ch, level, iscar, loop_ok, freq, diff, sens, cndtn, misc.{type,value} + 注意: mqttBuf 需 ≥ 1024 才能容纳 4 通道全字段封包 (~800 字节) */ static char data_json[1024]; static char payload[1400]; char *p = data_json; diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/net_srv.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/net_srv.c index 5404afc..5e05601 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/net_srv.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/net_srv.c @@ -183,15 +183,25 @@ void dev_initialize_pub(void) return; } memset(mBuff, 0, 256); - - sprintf((char *)mBuff, "{\"Method\":\"Initialize\", \"Device_id\": \"%s\"," - "\"Extra_Info\":{\"Code\":\"%s\",\"CSQ\":%d, \"Version\":\"%s\"}}", g_dev_number_str, "0", 0, FIRMWARE_VER); - //mqtt_publish((char *)"gtpc/display/Initialize", gbufSend, 0); - - PRINT("\r\nWill publish initialize_topic:\n%s\r\n", mBuff); - + + // V1.03: 上线后发布 initialize,告知服务器设备信息 + // 格式: {msg_id, cmd:"initialize", ts, data:{dev_serial, extra_info:{code,csq,location,version}}} + uint32_t now = (mstick() > 0) ? (uint32_t)(mstick() / 1000) : 0; + sprintf((char *)mBuff, + "{\"msg_id\":%lu,\"cmd\":\"initialize\",\"ts\":%lu," + "\"data\":{\"dev_serial\":\"%s\"," + "\"extra_info\":{\"code\":\"%s\",\"csq\":\"%d\"," + "\"location\":\"%s\",\"version\":\"%s\"}}}", + (unsigned long)++g_msg_id, (unsigned long)now, + g_dev_number_str, + "0", 0, "", FIRMWARE_VER); + + PRINT(" +\nWill publish initialize_topic:\n%s +\n", mBuff); + mqtt_publish((char *)(g_iot_topic.topic_pub), (char *)mBuff, 0); - + free(mBuff); } diff --git a/vd960DBN/docs/devlog.md b/vd960DBN/docs/devlog.md index f39e346..c042047 100644 --- a/vd960DBN/docs/devlog.md +++ b/vd960DBN/docs/devlog.md @@ -210,6 +210,7 @@ topic 修正为 V1.01 双主题协议 `dld960/{sn}/dev`(此前遗留旧 topic | 版本 | 时间 | 说明 | |------|------|------| +| V3.1 | 2026-07-09 | V1.03: 订阅后发 initialize 上线消息; iot_mqtt_srv 订阅改双主题 | | V3.0 | 2026-07-08 | MQTT 稳定性修复: socket初始化/buffer溢出/分批发送/hex dump/PINGREQ | | V2.9 | 2026-07-07 | MQTT V1.01 双主题协议 + 命令分发 + report_config 7参数 | | V2.8 | 2026-07-07 | MQTT 网络配置: ssc_net_set / iot_net_set / iot_topic_set |