diff --git a/archery_netcore.cpython-311-riscv64-linux-gnu.so b/archery_netcore.cpython-311-riscv64-linux-gnu.so index 1eb8d81..a075e41 100644 Binary files a/archery_netcore.cpython-311-riscv64-linux-gnu.so and b/archery_netcore.cpython-311-riscv64-linux-gnu.so differ diff --git a/cpp_ext/CMakeLists.txt b/cpp_ext/CMakeLists.txt index ebe9d5a..476d84d 100644 --- a/cpp_ext/CMakeLists.txt +++ b/cpp_ext/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(archery_netcore MODULE utils.cpp decrypt_ota_file.cpp msg_handler.cpp + tcp_ssl_password.cpp ) target_include_directories(archery_netcore PRIVATE diff --git a/cpp_ext/archery_netcore.cpp b/cpp_ext/archery_netcore.cpp index 6970753..06162a6 100644 --- a/cpp_ext/archery_netcore.cpp +++ b/cpp_ext/archery_netcore.cpp @@ -12,6 +12,7 @@ #include "native_logger.hpp" #include "decrypt_ota_file.hpp" #include "utils.hpp" +#include "tcp_ssl_password.hpp" namespace py = pybind11; using json = nlohmann::json; @@ -61,6 +62,14 @@ PYBIND11_MODULE(archery_netcore, m) { m.def("get_config", &get_config, "Get system configuration"); + m.def( + "calculate_tcp_ssl_password", + &netcore::calculate_tcp_ssl_password, + "Calculate TCP SSL password: hex(md5(hex(md5(device_id)) + iccid))", + py::arg("device_id"), + py::arg("iccid") + ); + m.def( "decrypt_ota_file", [](const std::string& input_path, const std::string& output_zip_path) { diff --git a/network.py b/network.py index 18cdd63..af4bee9 100644 --- a/network.py +++ b/network.py @@ -19,21 +19,10 @@ import config from hardware import hardware_manager from power import get_bus_voltage, voltage_to_percent -# from laser import laser_manager -# from ota import ota_manager from logger_manager import logger_manager from wifi import wifi_manager -def _calculate_tcp_ssl_password(device_id, iccid): - """ - 与服务器 calculatePassword(deviceId, iccid) 一致: - hex(md5(hex(md5(deviceId)) + iccid)),iccid 为空则不拼接。 - """ - md5_device_hex = hashlib.md5(device_id.encode("utf-8")).hexdigest() - if iccid: - md5_device_hex = md5_device_hex + iccid - return hashlib.md5(md5_device_hex.encode("utf-8")).hexdigest() def _wifi_tls_would_block(exc): @@ -219,10 +208,10 @@ class NetworkManager: iccid = self.get_4g_mccid() iccid = iccid if iccid else "" print(f"iccid: {iccid}") - self._password = _calculate_tcp_ssl_password(device_id, iccid) + self._password = self._netcore.calculate_tcp_ssl_password(device_id, iccid) else: - self._password = device_id + "." - print(f"self._password: {self._password}") + self.logger.error("[SSL] TCP SSL NOT enabled! exit!") + exit(1) try: with open("/device_key", "r") as f: @@ -652,46 +641,9 @@ class NetworkManager: """线程安全地将消息加入队列(公共方法)""" self._enqueue((msg_type, data_dict), high) - def make_packet(self, msg_type: int, body_dict: dict) -> bytes: - """打包 TCP 数据包:头部(长度+类型+校验)+ JSON 正文""" - body = json.dumps(body_dict).encode() - body_len = len(body) - checksum = body_len + msg_type - header = struct.pack(">III", body_len, msg_type, checksum) - return header + body - - def parse_packet(self, data: bytes): - """解析 TCP 数据包,返回 (类型, 正文字典)""" - if len(data) < 12: - return None, None - body_len, msg_type, checksum = struct.unpack(">III", data[:12]) - expected_len = 12 + body_len - - # 防御性检查:如果 data 比预期长,说明可能有粘包 - if len(data) > expected_len: - self.logger.warning( - f"[TCP] parse_packet: data length ({len(data)}) > expected ({expected_len}), " - f"possible packet concatenation. body_len={body_len}, msg_type={msg_type}" - ) - # 只解析第一个包,忽略多余数据(或者可以返回剩余部分) - # data = data[:expected_len] - # TODO: 是否需要解析剩余部分? - - # 如果 data 比预期短,说明包不完整(半包) - if len(data) < expected_len: - self.logger.warning( - f"[TCP] parse_packet: data length ({len(data)}) < expected ({expected_len}), " - f"incomplete packet. body_len={body_len}, msg_type={msg_type}" - ) - return None, None - - body = data[12:12 + body_len] - try: - return msg_type, json.loads(body.decode()) - except: - return msg_type, {"raw": body.decode(errors="ignore")} + def connect_server(self): """ 连接到服务器(自动选择WiFi或4G) @@ -1512,12 +1464,6 @@ class NetworkManager: "reason": str(e)[:100] }, 2) - def generate_token(self, device_id): - """生成用于 HTTP 接口鉴权的 Token(HMAC-SHA256)""" - SALT = "shootMessageFire" - SALT2 = "shoot" - return "Arrow_" + hmac.new((SALT + device_id).encode(), SALT2.encode(), hashlib.sha256).hexdigest() - def tcp_main(self): """TCP 主通信循环:登录、心跳、处理指令、发送数据""" import _thread diff --git a/wifi_config_httpd.py b/wifi_config_httpd.py index 2a0f083..fd19d73 100644 --- a/wifi_config_httpd.py +++ b/wifi_config_httpd.py @@ -383,6 +383,27 @@ def _ensure_hostapd_modern_security(logger=None) -> bool: return False +def _cleanup_ap_flag_if_needed(logger): + """若 /boot/wifi.ap 残留,删除它并恢复 /boot/wifi.sta,避免 main.py 误判为 AP 配网模式。""" + ap_flag = "/boot/wifi.ap" + sta_flag = "/boot/wifi.sta" + if not os.path.exists(ap_flag): + return + try: + os.remove(ap_flag) + logger.info(f"[WIFI-AP] 已清理残留标记 {ap_flag}") + except Exception as e: + logger.warning(f"[WIFI-AP] 清理 {ap_flag} 失败: {e}") + return + if not os.path.exists(sta_flag): + try: + with open(sta_flag, "w", encoding="utf-8") as f: + f.write("") + logger.info(f"[WIFI-AP] 已恢复 {sta_flag}") + except Exception as e: + logger.warning(f"[WIFI-AP] 恢复 {sta_flag} 失败: {e}") + + def _switch_boot_to_ap_mode(logger): """ 去掉 STA 标志、建立 AP 标志,由 S30wifi 起 hostapd(与 Maix start_ap 二选一,以系统脚本为准)。 @@ -449,6 +470,8 @@ def maybe_start_wifi_ap_fallback(logger=None): logger.info(f"[WIFI-AP] 兜底检测(quick):sta关联={wifi_ok}, 4g={g4_ok}") if wifi_ok or g4_ok: logger.info("[WIFI-AP] STA 或 4G 可用,不启动热点配网") + # 清理上次开机可能残留的 /boot/wifi.ap 标记,避免 main.py 误判为 AP 配网模式 + _cleanup_ap_flag_if_needed(logger) return # 两者均不可用:再按配置等待一段时间后复检,避免开机瞬态误判 @@ -466,6 +489,7 @@ def maybe_start_wifi_ap_fallback(logger=None): if wifi_ok or g4_ok: logger.info("[WIFI-AP] STA 或 4G 可用,不启动热点配网") + _cleanup_ap_flag_if_needed(logger) return logger.warning("[WIFI-AP] STA 与 4G 均不可用,启动热点配网(/boot/wifi.ap + HTTP)")