This commit is contained in:
gcw_4spBpAfv
2026-02-05 12:45:52 +08:00
parent f476545172
commit 61096ba190
2 changed files with 141 additions and 1 deletions

View File

@@ -679,6 +679,128 @@ class NetworkManager:
self.logger.error(f"[WIFI-TCP] 接收数据异常: {e}") self.logger.error(f"[WIFI-TCP] 接收数据异常: {e}")
return b"" return b""
def _upload_log_file(self, upload_url, wifi_ssid=None, wifi_password=None):
"""上传日志文件到指定URL
Args:
upload_url: 上传目标URL例如 "https://example.com/upload/"
wifi_ssid: WiFi SSID可选如果未连接WiFi则尝试连接
wifi_password: WiFi 密码(可选)
Note:
该功能仅在 WiFi 连接时可用4G 网络暂不支持文件上传
"""
import requests
import shutil
from datetime import datetime
try:
# 检查 WiFi 连接状态,如果未连接则尝试连接
if not self.is_wifi_connected():
if wifi_ssid and wifi_password:
self.logger.info(f"[LOG_UPLOAD] WiFi 未连接,尝试连接 WiFi: {wifi_ssid}")
self.safe_enqueue({"result": "log_upload_connecting_wifi", "ssid": wifi_ssid}, 2)
ip, error = self.connect_wifi(wifi_ssid, wifi_password)
if error:
self.logger.error(f"[LOG_UPLOAD] WiFi 连接失败: {error}")
self.safe_enqueue({
"result": "log_upload_failed",
"reason": "wifi_connect_failed",
"detail": error
}, 2)
return
self.logger.info(f"[LOG_UPLOAD] WiFi 连接成功IP: {ip}")
else:
self.logger.warning("[LOG_UPLOAD] WiFi 未连接且未提供 WiFi 凭证,无法上传日志")
self.safe_enqueue({
"result": "log_upload_failed",
"reason": "wifi_not_connected",
"detail": "WiFi not connected and no credentials provided"
}, 2)
return
else:
self.logger.info("[LOG_UPLOAD] WiFi 已连接,跳过连接步骤")
self.logger.info(f"[LOG_UPLOAD] 开始上传日志文件...")
# 获取日志文件路径
log_file_path = config.LOG_FILE # /maixapp/apps/t11/app.log
if not os.path.exists(log_file_path):
self.logger.error(f"[LOG_UPLOAD] 日志文件不存在: {log_file_path}")
self.safe_enqueue({"result": "log_upload_failed", "reason": "log_file_not_found"}, 2)
return
# 生成带时间戳的文件名
# 格式: app_20260131_143025_d19566161359c372.log
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
device_id = self._device_id or "unknown"
new_filename = f"app_{timestamp}_{device_id}.log"
# 创建临时文件(带时间戳的名字)
temp_dir = "/tmp"
temp_file_path = os.path.join(temp_dir, new_filename)
# 复制日志文件到临时位置
shutil.copy2(log_file_path, temp_file_path)
self.logger.info(f"[LOG_UPLOAD] 日志文件已复制到: {temp_file_path}")
# 使用 multipart/form-data 上传文件
with open(temp_file_path, 'rb') as f:
files = {'file': (new_filename, f, 'text/plain')}
# 添加额外的头部信息
headers = {
'User-Agent': 'Archery-Device/1.0',
'X-Device-ID': device_id,
}
# 如果是 ngrok-free.dev添加绕过警告页面的头
if 'ngrok-free.dev' in upload_url or 'ngrok.io' in upload_url:
headers['ngrok-skip-browser-warning'] = 'true'
self.logger.info(f"[LOG_UPLOAD] 正在上传到: {upload_url}")
# 禁用 SSL 警告(用于自签名证书或 SSL 兼容性问题)
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 发送请求verify=False 跳过 SSL 证书验证(解决 MaixCAM SSL 兼容性问题)
response = requests.post(upload_url, files=files, headers=headers, timeout=60, verify=False)
if response.status_code in (200, 201, 204):
self.logger.info(f"[LOG_UPLOAD] 上传成功! 状态码: {response.status_code}")
self.safe_enqueue({
"result": "log_upload_ok",
"filename": new_filename,
"status_code": response.status_code
}, 2)
else:
self.logger.error(f"[LOG_UPLOAD] 上传失败! 状态码: {response.status_code}, 响应: {response.text[:200]}")
self.safe_enqueue({
"result": "log_upload_failed",
"reason": f"http_{response.status_code}",
"detail": response.text[:100]
}, 2)
# 清理临时文件
try:
os.remove(temp_file_path)
self.logger.debug(f"[LOG_UPLOAD] 临时文件已删除: {temp_file_path}")
except Exception as e:
self.logger.warning(f"[LOG_UPLOAD] 删除临时文件失败: {e}")
except requests.exceptions.Timeout:
self.logger.error("[LOG_UPLOAD] 上传超时")
self.safe_enqueue({"result": "log_upload_failed", "reason": "timeout"}, 2)
except requests.exceptions.ConnectionError as e:
self.logger.error(f"[LOG_UPLOAD] 连接失败: {e}")
self.safe_enqueue({"result": "log_upload_failed", "reason": "connection_error"}, 2)
except Exception as e:
self.logger.error(f"[LOG_UPLOAD] 上传异常: {e}")
self.safe_enqueue({"result": "log_upload_failed", "reason": str(e)[:100]}, 2)
def generate_token(self, device_id): def generate_token(self, device_id):
"""生成用于 HTTP 接口鉴权的 TokenHMAC-SHA256""" """生成用于 HTTP 接口鉴权的 TokenHMAC-SHA256"""
@@ -970,6 +1092,21 @@ class NetworkManager:
time.sleep_ms(2000) time.sleep_ms(2000)
os.system("poweroff") os.system("poweroff")
return return
elif inner_cmd == 43: # 上传日志命令
# 格式: {"cmd":43, "data":{"ssid":"xxx","password":"xxx","url":"xxx"}}
inner_data = data_obj.get("data", {})
upload_url = inner_data.get("url")
wifi_ssid = inner_data.get("ssid")
wifi_password = inner_data.get("password")
if not upload_url:
self.logger.error("[LOG_UPLOAD] 缺少 url 参数")
self.safe_enqueue({"result": "log_upload_failed", "reason": "missing_url"}, 2)
else:
self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令目标URL: {upload_url}")
# 在新线程中执行上传,避免阻塞主循环
import _thread
_thread.start_new_thread(self._upload_log_file, (upload_url, wifi_ssid, wifi_password))
else: else:
time.sleep_ms(5) time.sleep_ms(5)

View File

@@ -4,11 +4,14 @@
应用版本号 应用版本号
每次 OTA 更新时,只需要更新这个文件中的版本号 每次 OTA 更新时,只需要更新这个文件中的版本号
""" """
VERSION = '1.2.1' VERSION = '1.2.3'
# 1.2.0 开始使用C++编译成.so替换部分代码 # 1.2.0 开始使用C++编译成.so替换部分代码
# 1.2.1 ota使用加密包 # 1.2.1 ota使用加密包
# 1.2.2 支持wifi ota并且设定时区并使用单独线程保存图片 # 1.2.2 支持wifi ota并且设定时区并使用单独线程保存图片
# 1.2.3 修改ADC_TRIGGER_THRESHOLD 为2300支持上传日志到服务器