diff --git a/.gitignore b/.gitignore index c18dd8d..158b43a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ __pycache__/ +backend/build/ +backend/dist/ diff --git a/README_ARCHITECTURE.md b/README_ARCHITECTURE.md index d701cfe..91e115d 100644 --- a/README_ARCHITECTURE.md +++ b/README_ARCHITECTURE.md @@ -197,3 +197,5 @@ chmod +x start_frontend.sh + + diff --git a/SIMULATOR_README.md b/SIMULATOR_README.md index ea49b9b..4353a4f 100644 --- a/SIMULATOR_README.md +++ b/SIMULATOR_README.md @@ -111,3 +111,5 @@ simulator = WrenchSimulator( + + diff --git a/SSH_TUNNEL_README.md b/SSH_TUNNEL_README.md index c7e5006..e7a8a4a 100644 --- a/SSH_TUNNEL_README.md +++ b/SSH_TUNNEL_README.md @@ -248,3 +248,5 @@ ssh -p 2222 root@localhost 如有问题,请检查日志或联系技术支持。 + + diff --git a/backend/backend_api.spec b/backend/backend_api.spec new file mode 100644 index 0000000..7b3952b --- /dev/null +++ b/backend/backend_api.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['app.py'], + pathex=['..'], + binaries=[], + datas=[], + hiddenimports=['wrench_controller'], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='backend_api', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='backend_api', +) diff --git a/backend/build_backend.bat b/backend/build_backend.bat new file mode 100644 index 0000000..6154606 --- /dev/null +++ b/backend/build_backend.bat @@ -0,0 +1,66 @@ +@echo off +REM Simple backend build script (ASCII only, avoid encoding issues) + +cd /d "%~dp0" + +echo ======================================== +echo Building backend_api with PyInstaller... +echo ======================================== + +echo Checking Python environment... +python -c "import PyInstaller" 2>nul +if errorlevel 1 ( + echo PyInstaller not found, installing... + pip install --upgrade setuptools + pip install pyinstaller + if errorlevel 1 ( + echo Failed to install PyInstaller. Please run: pip install pyinstaller + pause + exit /b 1 + ) +) else ( + echo Checking PyInstaller dependencies... + python -c "import jaraco" 2>nul + if errorlevel 1 ( + echo Missing dependencies detected, fixing... + pip install --upgrade setuptools + pip install --upgrade --force-reinstall pyinstaller + ) +) + +echo Cleaning old build/dist... +if exist build ( + echo Removing build directory... + rmdir /s /q build 2>nul + timeout /t 1 /nobreak >nul +) +if exist dist ( + echo Removing dist directory... + rmdir /s /q dist 2>nul + timeout /t 1 /nobreak >nul +) +if exist backend_api.spec ( + echo Removing old spec file... + del /q backend_api.spec 2>nul +) + +echo Running PyInstaller with --clean flag... +python -m PyInstaller --clean --name backend_api --console --paths .. --hidden-import wrench_controller app.py + +if errorlevel 1 ( + echo Build failed! + pause + exit /b 1 +) + +echo. +echo Build success! +echo Executable: dist\backend_api\backend_api.exe +echo. +echo If you have an existing wrench.db, copy it to: +echo dist\backend_api\wrench.db +echo. +echo Then run: +echo dist\backend_api\backend_api.exe +echo. +pause diff --git a/backend/build_backend.sh b/backend/build_backend.sh new file mode 100644 index 0000000..adcdeb1 --- /dev/null +++ b/backend/build_backend.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# 电动扳手后端 API 打包脚本(Linux/Mac) + +echo "========================================" +echo "电动扳手后端 API 打包脚本" +echo "========================================" +echo "" + +# 确保在 backend 目录下执行 +cd "$(dirname "$0")" + +# 检查 PyInstaller 是否已安装 +if ! python3 -c "import PyInstaller" 2>/dev/null; then + echo "[错误] PyInstaller 未安装" + echo "正在安装 PyInstaller..." + pip3 install pyinstaller + if [ $? -ne 0 ]; then + echo "[错误] PyInstaller 安装失败,请手动执行: pip3 install pyinstaller" + exit 1 + fi +fi + +echo "[信息] PyInstaller 已安装" +echo "" + +# 清理之前的构建文件 +echo "[信息] 清理之前的构建文件..." +rm -rf build dist + +echo "" +echo "[信息] 当前目录: $(pwd)" +echo "[信息] 开始打包后端 API 服务..." +echo "" + +# 使用 onedir 模式打包,并显式包含父目录中的 wrench_controller 模块 +# --paths .. 把项目根目录加入模块搜索路径 +# --hidden-import ... 确保 wrench_controller 被打包进可执行文件 +pyinstaller --name backend_api --console --paths .. --hidden-import wrench_controller app.py + +if [ $? -ne 0 ]; then + echo "" + echo "[错误] 打包失败!" + exit 1 +fi + +echo "" +echo "========================================" +echo "后端打包完成!" +echo "========================================" +echo "" +echo "可执行文件位置: dist/backend_api/backend_api" +echo "" +echo "下一步操作:" +echo "1. 如果已有生产用数据库,可将当前目录的 wrench.db 复制到 dist/backend_api/ 目录:" +echo " cp wrench.db dist/backend_api/wrench.db" +echo "" +echo "2. 运行后端服务:" +echo " ./dist/backend_api/backend_api" +echo "" +echo "3. 确保前端或第三方程序访问的地址仍为:" +echo " http://localhost:5000" +echo "" + + diff --git a/backend/check_db.py b/backend/check_db.py index 052abf0..481cdf0 100644 --- a/backend/check_db.py +++ b/backend/check_db.py @@ -77,3 +77,5 @@ print("\n" + "="*60) + + diff --git a/backend/check_results.py b/backend/check_results.py index 937caec..ecf0089 100644 --- a/backend/check_results.py +++ b/backend/check_results.py @@ -28,3 +28,5 @@ conn.close() + + diff --git a/backend/diagnose.py b/backend/diagnose.py index 003b906..db6aec2 100644 --- a/backend/diagnose.py +++ b/backend/diagnose.py @@ -68,3 +68,5 @@ print("="*60) + + diff --git a/backend/migrate_add_device_info.py b/backend/migrate_add_device_info.py index f5a4a2a..f76c4f9 100644 --- a/backend/migrate_add_device_info.py +++ b/backend/migrate_add_device_info.py @@ -113,3 +113,5 @@ if __name__ == "__main__": + + diff --git a/backend/quick_test.py b/backend/quick_test.py index c7bff49..f697a9a 100644 --- a/backend/quick_test.py +++ b/backend/quick_test.py @@ -55,3 +55,5 @@ print("="*60) + + diff --git a/backend/requirements.txt b/backend/requirements.txt index 2a62ec4..8e0b23d 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,3 +3,5 @@ flask-cors==4.0.0 + + diff --git a/backend/start_backend.bat b/backend/start_backend.bat index 1ec6b6f..890379c 100644 --- a/backend/start_backend.bat +++ b/backend/start_backend.bat @@ -10,3 +10,5 @@ pause + + diff --git a/backend/start_backend.sh b/backend/start_backend.sh index f39731c..9c7bec9 100644 --- a/backend/start_backend.sh +++ b/backend/start_backend.sh @@ -8,3 +8,5 @@ python3 app.py + + diff --git a/backend/test_api.py b/backend/test_api.py index ac8c2d8..6849774 100644 --- a/backend/test_api.py +++ b/backend/test_api.py @@ -42,3 +42,5 @@ if __name__ == "__main__": + + diff --git a/backend/后端打包说明.md b/backend/后端打包说明.md new file mode 100644 index 0000000..38d66ca --- /dev/null +++ b/backend/后端打包说明.md @@ -0,0 +1,176 @@ +## 后端(Flask API)打包与运行说明 + +### 1. 是否可以用和前端相同的方式打包? + +**可以。** +后端同样可以用 **PyInstaller** 打包成一个可执行文件,只是: +- 前端是 **GUI 程序**(`console=False`), +- 后端是 **控制台服务程序**(`console=True`,启动后常驻,监听 5000 端口)。 + +本项目已经在 `backend` 目录下生成了两个打包脚本: +- `build_backend.bat`(Windows) +- `build_backend.sh`(Linux/Mac) + +--- + +### 2. 打包前准备 + +在打包前,确保: + +- 已安装 Python 3 环境 +- 已安装后端依赖: + +```bash +cd backend +pip install -r requirements.txt +``` + +(如果使用虚拟环境,请先激活虚拟环境再执行上面的命令) + +--- + +### 3. Windows 下打包后端 + +1. 打开命令提示符或 PowerShell + +2. 进入 `backend` 目录: + +```cmd +cd F:\PyPro\TorqueWrench\backend +``` + +3. 执行打包脚本: + +```cmd +build_backend.bat +``` + +脚本会自动: +- 检查并安装 PyInstaller +- 清理旧的 `build/`、`dist/` 目录 +- 调用: + +```cmd +pyinstaller --name backend_api --console --paths .. --hidden-import wrench_controller app.py +``` + +4. 打包完成后,可执行文件位置: + +```text +backend\dist\backend_api\backend_api.exe +``` + +5. 如果你已经有一个正在使用的数据库文件 `wrench.db`,可以复制过去: + +```cmd +copy wrench.db dist\backend_api\wrench.db +``` + +6. 启动后端服务: + +```cmd +dist\backend_api\backend_api.exe +``` + +启动后,后端会监听 `http://localhost:5000`,前端照常访问。 + +--- + +### 4. Linux / Mac 下打包后端 + +1. 打开终端 + +2. 进入 `backend` 目录: + +```bash +cd /path/to/TorqueWrench/backend +``` + +3. 赋予脚本执行权限(首次): + +```bash +chmod +x build_backend.sh +``` + +4. 执行打包脚本: + +```bash +./build_backend.sh +``` + +脚本会自动: +- 检查并安装 PyInstaller +- 清理旧的 `build/`、`dist/` 目录 +- 调用: + +```bash +pyinstaller --name backend_api --console --paths .. --hidden-import wrench_controller app.py +``` + +5. 打包完成后,可执行文件位置: + +```text +backend/dist/backend_api/backend_api +``` + +6. 如果已有生产数据库,复制到打包目录: + +```bash +cp wrench.db dist/backend_api/wrench.db +``` + +7. 启动后端服务: + +```bash +./dist/backend_api/backend_api +``` + +同样会监听 `http://localhost:5000`。 + +--- + +### 5. 和直接用 Python 运行的对比 + +- **直接运行(开发环境常用):** + +```bash +cd backend +python app.py +``` + +- **打包运行(部署 / 发给其他机器):** + - 不需要安装源码,只要有打包后的目录和配置/数据库文件即可。 + - 更适合发给不熟悉 Python 环境的用户。 + +两种方式使用的是同一套代码逻辑,差别只是启动方式。 + +--- + +### 6. 注意事项 + +- **端口**:打包后默认仍然是监听 5000 端口,如果需要改端口,需要改 `app.py` 启动代码(或用环境变量/参数)。 +- **数据库文件**: + - 代码中使用的是: + - `db_path = os.path.join(os.path.dirname(__file__), "wrench.db")` + - 打包后的 `__file__` 会指向打包目录内的脚本位置,所以: + - 你只要把 `wrench.db` 放到 `dist/backend_api/` 目录下,程序就能正常使用。 +- **日志输出**:后端是控制台应用,会在命令行窗口打印日志(方便排查问题)。 + +--- + +### 7. 简短回答你的问题 + +- **问**:后端服务要如何打包?也用同样方式可以吗? +- **答**:可以。后端已经提供了和前端类似的一键打包脚本: + - Windows:`backend/build_backend.bat` + - Linux/Mac:`backend/build_backend.sh` + +你只需要: + +1. 进入 `backend` 目录 +2. 安装依赖:`pip install -r requirements.txt` +3. 运行打包脚本(Windows 用 `.bat`,Linux/Mac 用 `.sh`) +4. 把 `wrench.db` 复制到 `dist/backend_api/`,运行生成的 `backend_api` 即可。 + + + diff --git a/config.json b/config.json index 49f2da5..a602df5 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,8 @@ { + "api": { + "base_url": "http://localhost:5000", + "description": "后端API服务地址,例如:http://localhost:5000 或 http://192.168.1.100:5000" + }, "wrench": { "host": "127.0.0.1", "port": 7888, diff --git a/frontend/build_exe.bat b/frontend/build_exe.bat new file mode 100644 index 0000000..de99673 --- /dev/null +++ b/frontend/build_exe.bat @@ -0,0 +1,47 @@ +@echo off +REM Simple frontend build script (ASCII only, avoid encoding issues) + +cd /d "%~dp0" + +echo ======================================== +echo Building wrench_gui with PyInstaller... +echo ======================================== + +python -c "import PyInstaller" 2>nul +if errorlevel 1 ( + echo PyInstaller not found, installing... + pip install pyinstaller + if errorlevel 1 ( + echo Failed to install PyInstaller. Please run: pip install pyinstaller + pause + exit /b 1 + ) +) + +echo Cleaning old build/dist... +if exist build rmdir /s /q build +if exist dist rmdir /s /q dist + +echo Running PyInstaller... +python -m PyInstaller wrench_gui.spec + +if errorlevel 1 ( + echo Build failed! + pause + exit /b 1 +) + +echo. +echo Build success! +echo Executable: dist\wrench_gui\wrench_gui.exe +echo. +echo Next steps: +echo 1. Copy config.json to executable directory: +echo copy ..\config.json dist\wrench_gui\config.json +echo. +echo 2. Make sure backend API is running (http://localhost:5000) +echo. +echo 3. Run the program: +echo dist\wrench_gui\wrench_gui.exe +echo. +pause diff --git a/frontend/build_exe.sh b/frontend/build_exe.sh new file mode 100644 index 0000000..f74c83e --- /dev/null +++ b/frontend/build_exe.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# 电动扳手GUI打包脚本(Linux/Mac) + +echo "========================================" +echo "电动扳手GUI打包脚本" +echo "========================================" +echo "" + +# 检查 PyInstaller 是否已安装 +if ! python3 -c "import PyInstaller" 2>/dev/null; then + echo "[错误] PyInstaller 未安装" + echo "正在安装 PyInstaller..." + pip3 install pyinstaller + if [ $? -ne 0 ]; then + echo "[错误] PyInstaller 安装失败,请手动执行: pip3 install pyinstaller" + exit 1 + fi +fi + +echo "[信息] PyInstaller 已安装" +echo "" + +# 确保在 frontend 目录下执行 +cd "$(dirname "$0")" + +# 清理之前的构建文件 +echo "[信息] 清理之前的构建文件..." +rm -rf build dist + +echo "" +echo "[信息] 当前目录: $(pwd)" +echo "[信息] 开始打包..." +echo "" + +# 执行打包 +pyinstaller wrench_gui.spec + +if [ $? -ne 0 ]; then + echo "" + echo "[错误] 打包失败!" + exit 1 +fi + +echo "" +echo "========================================" +echo "打包完成!" +echo "========================================" +echo "" +echo "可执行文件位置: dist/wrench_gui/wrench_gui" +echo "" +echo "下一步操作:" +echo "1. 复制配置文件到可执行文件目录:" +echo " cp ../config.json dist/wrench_gui/config.json" +echo "" +echo "2. 确保后端API服务已启动(http://localhost:5000)" +echo "" +echo "3. 运行程序:" +echo " ./dist/wrench_gui/wrench_gui" +echo "" + diff --git a/frontend/requirements.txt b/frontend/requirements.txt index 2f4a072..d1b72f7 100644 --- a/frontend/requirements.txt +++ b/frontend/requirements.txt @@ -2,3 +2,5 @@ requests==2.31.0 + + diff --git a/frontend/start_frontend.bat b/frontend/start_frontend.bat index 337ecc6..41c7550 100644 --- a/frontend/start_frontend.bat +++ b/frontend/start_frontend.bat @@ -10,3 +10,5 @@ pause + + diff --git a/frontend/start_frontend.sh b/frontend/start_frontend.sh index ab1a459..60a61af 100644 --- a/frontend/start_frontend.sh +++ b/frontend/start_frontend.sh @@ -8,3 +8,5 @@ python3 wrench_gui.py + + diff --git a/frontend/wrench_gui.py b/frontend/wrench_gui.py index 808bb7f..ac25385 100644 --- a/frontend/wrench_gui.py +++ b/frontend/wrench_gui.py @@ -49,8 +49,8 @@ class WrenchGUI: screen_height = self.root.winfo_screenheight() self.root.geometry(f"{screen_width}x{screen_height}") - # API配置 - self.api_base_url = "http://localhost:5000/api" + # API配置(从配置文件读取,如果没有则使用默认值) + self.api_base_url = self.load_api_config() self.poll_interval = 3 # 轮询间隔(秒) # 数据 @@ -82,6 +82,9 @@ class WrenchGUI: # 检查测试模式 self.check_test_mode() + # 初始化螺栓列表显示(确保始终可见) + self.update_bolt_list() + # 加载设备列表 self.load_wrench_devices() @@ -90,10 +93,21 @@ class WrenchGUI: def _create_widgets(self): """创建界面组件""" - # 标题 + # 使用左右布局:左侧工单列表,右侧其他内容 + self.root.update_idletasks() + screen_height = self.root.winfo_screenheight() + + # 配置列权重:左侧40%,右侧60%,支持1920和1600分辨率 + # 使用相对权重,自适应不同分辨率 + self.root.grid_columnconfigure(0, weight=2, minsize=350) # 左侧工单列表和日志,最小350像素 + self.root.grid_columnconfigure(1, weight=3, minsize=450) # 右侧内容区域,最小450像素 + # 配置行权重:从row=2开始的内容区域可扩展 + self.root.grid_rowconfigure(2, weight=1) + + # 标题(横跨两列) title_frame = tk.Frame(self.root, bg="#2c3e50", height=60) - title_frame.pack(fill=tk.X) - title_frame.pack_propagate(False) + title_frame.grid(row=0, column=0, columnspan=2, sticky="ew") + title_frame.grid_propagate(False) title_label = tk.Label( title_frame, @@ -114,9 +128,9 @@ class WrenchGUI: ) self.test_mode_label.place(relx=1.0, rely=0.5, anchor=tk.E, x=-20) - # 工单列表状态区域 + # 工单列表状态区域(横跨两列) status_frame = tk.Frame(self.root, padx=10, pady=5) - status_frame.pack(fill=tk.X) + status_frame.grid(row=1, column=0, columnspan=2, sticky="ew") self.poll_status_label = tk.Label( status_frame, @@ -146,13 +160,24 @@ class WrenchGUI: ) self.refresh_button.pack(side=tk.RIGHT, padx=5) - # 工单列表区域 - order_list_frame = tk.LabelFrame(self.root, text="可用工单列表", font=("微软雅黑", 12), padx=10, pady=10) - order_list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + # ========== 左侧:工单列表和操作控制 ========== + # 创建左侧主容器(上下布局:工单列表 + 操作控制) + left_container = tk.Frame(self.root) + left_container.grid(row=2, column=0, sticky="nsew", padx=(10, 5), pady=5) + # 配置行权重:工单列表可扩展,操作控制固定不压缩 + left_container.grid_rowconfigure(0, weight=3) # 工单列表权重较大 + left_container.grid_rowconfigure(1, weight=0, minsize=120) # 操作控制固定,最小120像素,不压缩 + left_container.grid_columnconfigure(0, weight=1) - # 创建工单列表表格 + # 工单列表区域 + order_list_frame = tk.LabelFrame(left_container, text="可用工单列表", font=("微软雅黑", 12), padx=10, pady=10) + order_list_frame.grid(row=0, column=0, sticky="nsew", padx=0, pady=(0, 5)) + order_list_frame.grid_rowconfigure(0, weight=1) + order_list_frame.grid_columnconfigure(0, weight=1) + + # 创建工单列表表格(使用相对高度,不固定) order_columns = ("追溯号", "工序号", "工序名称", "产品", "螺栓数", "状态") - self.order_tree = ttk.Treeview(order_list_frame, columns=order_columns, show="headings", height=5) + self.order_tree = ttk.Treeview(order_list_frame, columns=order_columns, show="headings") for col in order_columns: self.order_tree.heading(col, text=col) @@ -164,9 +189,119 @@ class WrenchGUI: self.order_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) order_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) - # 工单信息区域 - info_frame = tk.LabelFrame(self.root, text="当前工单信息", font=("微软雅黑", 12), padx=10, pady=10) - info_frame.pack(fill=tk.X, padx=10, pady=10) + # 操作控制区域(移到左侧,放在工单列表下方) + button_frame = tk.LabelFrame(left_container, text="操作控制", font=("微软雅黑", 12), padx=10, pady=8) + button_frame.grid(row=1, column=0, sticky="ew", padx=0, pady=0) + + # 使用grid布局,将所有按钮放在同一行确保可见 + # 第一列:工单操作 + col1_frame = tk.Frame(button_frame) + col1_frame.grid(row=0, column=0, padx=(0, 20), sticky=tk.W) + + tk.Label(col1_frame, text="工单操作", font=("微软雅黑", 9, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 3)) + self.claim_button = tk.Button( + col1_frame, + text="认领工单", + font=("微软雅黑", 10), + bg="#f39c12", + fg="white", + width=10, + height=1, + cursor="hand2", + command=self.claim_work_order + ) + self.claim_button.pack() + + # 第二列:设备选择 + col2_frame = tk.Frame(button_frame) + col2_frame.grid(row=0, column=1, padx=(0, 20), sticky=tk.W) + + tk.Label(col2_frame, text="设备选择", font=("微软雅黑", 9, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 3)) + device_select_inner = tk.Frame(col2_frame) + device_select_inner.pack() + + tk.Label(device_select_inner, text="选择扳手:", font=("微软雅黑", 9)).pack(side=tk.LEFT, padx=(0, 5)) + + # 设备状态指示器(圆点) + self.device_status_indicator = tk.Label( + device_select_inner, + text="●", + font=("微软雅黑", 12), + fg="#7f8c8d" # 默认灰色 + ) + self.device_status_indicator.pack(side=tk.LEFT, padx=(0, 3)) + + self.device_combo = ttk.Combobox(device_select_inner, width=18, state="readonly", font=("微软雅黑", 9)) + self.device_combo.pack(side=tk.LEFT, padx=(0, 5)) + self.device_combo.bind("<>", self.on_device_selected) + + self.device_manage_button = tk.Button( + device_select_inner, + text="设备管理", + font=("微软雅黑", 8), + bg="#3498db", + fg="white", + width=8, + height=1, + command=self.open_device_manager + ) + self.device_manage_button.pack(side=tk.LEFT) + + # 第三列:执行控制(放在同一行,确保可见) + col3_frame = tk.Frame(button_frame) + col3_frame.grid(row=0, column=2, padx=(0, 0), sticky=tk.W) + + tk.Label(col3_frame, text="执行控制", font=("微软雅黑", 9, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 3)) + exec_control_inner = tk.Frame(col3_frame) + exec_control_inner.pack() + + self.start_button = tk.Button( + exec_control_inner, + text="开始拧紧", + font=("微软雅黑", 10), + bg="#27ae60", + fg="white", + width=10, + height=1, + cursor="hand2", + command=self.start_process, + state=tk.DISABLED + ) + self.start_button.pack(side=tk.LEFT, padx=(0, 10)) + + self.stop_button = tk.Button( + exec_control_inner, + text="停止", + font=("微软雅黑", 10), + bg="#e74c3c", + fg="white", + width=10, + height=1, + cursor="hand2", + state=tk.DISABLED, + command=self.stop_process + ) + self.stop_button.pack(side=tk.LEFT) + + # 配置grid列权重 + button_frame.grid_columnconfigure(0, weight=0) + button_frame.grid_columnconfigure(1, weight=1) + button_frame.grid_columnconfigure(2, weight=0) + + # ========== 右侧:内容区域 ========== + # 创建右侧主容器(上下布局) + right_container = tk.Frame(self.root) + right_container.grid(row=2, column=1, sticky="nsew", padx=(5, 10), pady=5) + # 配置行权重:日志区域可压缩,螺栓列表可压缩 + right_container.grid_rowconfigure(0, weight=0) # 工单信息不扩展 + right_container.grid_rowconfigure(1, weight=0) # 当前螺栓不扩展 + right_container.grid_rowconfigure(2, weight=3) # 螺栓列表可扩展可压缩(主要区域) + right_container.grid_rowconfigure(3, weight=1, minsize=100) # 日志区域可压缩,最小100像素 + right_container.grid_columnconfigure(0, weight=1) + + # 工单信息区域(上下布局,第一行) + info_frame = tk.LabelFrame(right_container, text="当前工单信息", font=("微软雅黑", 12), padx=10, pady=10) + info_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5) self.trace_id_label = tk.Label(info_frame, text="追溯号: --", font=("微软雅黑", 10)) self.trace_id_label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=2) @@ -186,9 +321,9 @@ class WrenchGUI: self.device_label = tk.Label(info_frame, text="扳手设备: --", font=("微软雅黑", 10), fg="#2c3e50") self.device_label.grid(row=1, column=2, sticky=tk.W, padx=5, pady=2) - # 当前螺栓信息 - current_frame = tk.LabelFrame(self.root, text="当前螺栓", font=("微软雅黑", 12), padx=10, pady=10) - current_frame.pack(fill=tk.X, padx=10, pady=10) + # 当前螺栓信息(上下布局,第二行) + current_frame = tk.LabelFrame(right_container, text="当前螺栓", font=("微软雅黑", 12), padx=10, pady=10) + current_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) self.current_bolt_label = tk.Label( current_frame, @@ -213,9 +348,9 @@ class WrenchGUI: ) self.current_status_label.pack(pady=2) - # 进度条 - progress_frame = tk.Frame(self.root, padx=10, pady=5) - progress_frame.pack(fill=tk.X, padx=10) + # 进度条(放在current_frame内部) + progress_frame = tk.Frame(current_frame, padx=10, pady=5) + progress_frame.pack(fill=tk.X, pady=5) self.progress_label = tk.Label(progress_frame, text="总进度: 0/0", font=("微软雅黑", 10)) self.progress_label.pack(anchor=tk.W) @@ -223,13 +358,29 @@ class WrenchGUI: self.progress_bar = ttk.Progressbar(progress_frame, mode='determinate', length=400) self.progress_bar.pack(fill=tk.X, pady=5) - # 螺栓列表 - list_frame = tk.LabelFrame(self.root, text="螺栓列表", font=("微软雅黑", 12), padx=10, pady=10) - list_frame.pack(fill=tk.BOTH, expand=False, padx=10, pady=10) + # 螺栓列表(固定在"当前螺栓"下方、"操作控制"上方,可压缩但始终可见) + # 创建螺栓列表框架 + list_frame = tk.LabelFrame(right_container, text="螺栓列表", font=("微软雅黑", 12), padx=10, pady=10) + list_frame.grid(row=2, column=0, sticky="nsew", padx=5, pady=5) + # 螺栓列表通过right_container的row=2的weight=1来控制,可以压缩 + # 设置最小高度,支持1920和1600分辨率 + self.root.update_idletasks() + screen_height = self.root.winfo_screenheight() + # 根据分辨率动态调整最小高度:1920约180像素,1600约150像素 + if screen_height >= 1920: + min_list_height = max(180, int(screen_height * 0.15)) + elif screen_height >= 1600: + min_list_height = max(150, int(screen_height * 0.15)) + else: + min_list_height = max(120, int(screen_height * 0.15)) + right_container.grid_rowconfigure(2, weight=1, minsize=min_list_height) - # 创建表格(减少高度,确保按钮区域可见) + # 保存list_frame引用,确保始终可见 + self.list_frame = list_frame + + # 创建表格(使用相对布局,不固定高度) columns = ("序号", "名称", "扭矩(Nm)", "状态", "实际扭矩", "时间") - self.tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=4) + self.tree = ttk.Treeview(list_frame, columns=columns, show="headings") # 设置列 for col in columns: @@ -255,129 +406,62 @@ class WrenchGUI: self.tree.tag_configure("success", foreground="#27ae60", font=("微软雅黑", 10, "bold")) self.tree.tag_configure("failed", foreground="#e74c3c") - # 控制按钮区域(放在螺栓列表之后,确保可见) - button_frame = tk.LabelFrame(self.root, text="操作控制", font=("微软雅黑", 12), padx=15, pady=15) - button_frame.pack(fill=tk.X, padx=10, pady=10) + # 操作日志区域(移到右侧,放在螺栓列表下方) + log_frame = tk.LabelFrame(right_container, text="操作日志", font=("微软雅黑", 9), padx=5, pady=3) + log_frame.grid(row=3, column=0, sticky="nsew", padx=5, pady=(0, 5)) + log_frame.grid_rowconfigure(0, weight=1) + log_frame.grid_columnconfigure(0, weight=1) - # 使用grid布局,将所有按钮放在同一行确保可见 - # 第一列:工单操作 - col1_frame = tk.Frame(button_frame) - col1_frame.grid(row=0, column=0, padx=(0, 20), sticky=tk.W) - - tk.Label(col1_frame, text="工单操作", font=("微软雅黑", 10, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 5)) - self.claim_button = tk.Button( - col1_frame, - text="认领工单", - font=("微软雅黑", 11, "bold"), - bg="#f39c12", - fg="white", - width=12, - height=2, - cursor="hand2", - command=self.claim_work_order - ) - self.claim_button.pack() - - # 第二列:设备选择 - col2_frame = tk.Frame(button_frame) - col2_frame.grid(row=0, column=1, padx=(0, 20), sticky=tk.W) - - tk.Label(col2_frame, text="设备选择", font=("微软雅黑", 10, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 5)) - device_select_inner = tk.Frame(col2_frame) - device_select_inner.pack() - - tk.Label(device_select_inner, text="选择扳手:", font=("微软雅黑", 10)).pack(side=tk.LEFT, padx=(0, 5)) - - # 设备状态指示器(圆点) - self.device_status_indicator = tk.Label( - device_select_inner, - text="●", - font=("微软雅黑", 12), - fg="#7f8c8d" # 默认灰色 - ) - self.device_status_indicator.pack(side=tk.LEFT, padx=(0, 3)) - - self.device_combo = ttk.Combobox(device_select_inner, width=20, state="readonly", font=("微软雅黑", 10)) - self.device_combo.pack(side=tk.LEFT, padx=(0, 5)) - self.device_combo.bind("<>", self.on_device_selected) - - self.device_manage_button = tk.Button( - device_select_inner, - text="设备管理", - font=("微软雅黑", 9), - bg="#3498db", - fg="white", - width=10, - height=1, - command=self.open_device_manager - ) - self.device_manage_button.pack(side=tk.LEFT) - - # 第三列:执行控制(放在同一行,确保可见) - col3_frame = tk.Frame(button_frame) - col3_frame.grid(row=0, column=2, padx=(0, 0), sticky=tk.W) - - tk.Label(col3_frame, text="执行控制", font=("微软雅黑", 10, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 5)) - exec_control_inner = tk.Frame(col3_frame) - exec_control_inner.pack() - - self.start_button = tk.Button( - exec_control_inner, - text="开始拧紧", - font=("微软雅黑", 11, "bold"), - bg="#27ae60", - fg="white", - width=12, - height=2, - cursor="hand2", - command=self.start_process, - state=tk.DISABLED - ) - self.start_button.pack(side=tk.LEFT, padx=(0, 10)) - - self.stop_button = tk.Button( - exec_control_inner, - text="停止", - font=("微软雅黑", 11, "bold"), - bg="#e74c3c", - fg="white", - width=12, - height=2, - cursor="hand2", - state=tk.DISABLED, - command=self.stop_process - ) - self.stop_button.pack(side=tk.LEFT) - - # 配置grid列权重 - button_frame.grid_columnconfigure(0, weight=0) - button_frame.grid_columnconfigure(1, weight=1) - button_frame.grid_columnconfigure(2, weight=0) - - # 日志区域 - log_frame = tk.LabelFrame(self.root, text="操作日志", font=("微软雅黑", 10), padx=5, pady=5) - log_frame.pack(fill=tk.X, padx=10, pady=(0, 10)) - - self.log_text = tk.Text(log_frame, height=5, font=("Consolas", 9)) + self.log_text = tk.Text(log_frame, font=("Consolas", 9)) self.log_text.pack(fill=tk.BOTH, expand=True) log_scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview) self.log_text.configure(yscrollcommand=log_scrollbar.set) log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + def get_config_path(self): + """获取配置文件路径(支持开发环境和打包后的环境)""" + # 如果是打包后的可执行文件,config.json 应该在可执行文件同一目录 + if getattr(sys, 'frozen', False): + # 打包后的环境 + base_path = os.path.dirname(sys.executable) + else: + # 开发环境,在项目根目录 + base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + return os.path.join(base_path, "config.json") + + def load_api_config(self): + """从配置文件读取API地址,如果没有则使用默认值""" + default_url = "http://localhost:5000/api" + try: + config_path = self.get_config_path() + if os.path.exists(config_path): + with open(config_path, "r", encoding="utf-8") as f: + config = json.load(f) + api_config = config.get("api", {}) + api_url = api_config.get("base_url", default_url) + # 确保URL以/api结尾 + if not api_url.endswith("/api"): + api_url = api_url.rstrip("/") + "/api" + return api_url + except Exception as e: + print(f"Warning: Failed to load API config: {e}, using default: {default_url}") + return default_url + def check_test_mode(self): """检查测试模式""" try: - config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json") - with open(config_path, "r", encoding="utf-8") as f: - config = json.load(f) - self.test_mode = config.get("test_mode", {}).get("enabled", False) - - if self.test_mode: - self.test_mode_label.config(text="⚠️ 测试模式") - self.log("⚠️ 测试模式已启用:失败也算成功", "WARN") - else: - self.test_mode_label.config(text="") + config_path = self.get_config_path() + if os.path.exists(config_path): + with open(config_path, "r", encoding="utf-8") as f: + config = json.load(f) + self.test_mode = config.get("test_mode", {}).get("enabled", False) + + if self.test_mode: + self.test_mode_label.config(text="⚠️ 测试模式") + self.log("⚠️ 测试模式已启用:失败也算成功", "WARN") + else: + self.test_mode_label.config(text="") except Exception as e: self.log(f"读取配置失败: {e}", "ERROR") @@ -605,11 +689,19 @@ class WrenchGUI: self.device_label.config(text="扳手设备: 未选择", foreground="#2c3e50") def update_bolt_list(self): - """更新螺栓列表""" + """更新螺栓列表(始终显示,即使没有工单)""" + # 确保螺栓列表框架始终显示,无论连接状态如何 + if hasattr(self, 'list_frame'): + self.list_frame.grid() # 强制显示,防止被隐藏 + + # 始终清空表格,确保表格框架始终可见 + self.tree.delete(*self.tree.get_children()) + if not self.work_order: + # 没有工单时,显示空表格,但表格框架保持可见 return - self.tree.delete(*self.tree.get_children()) + # 有工单时,显示螺栓数据 bolts = self.work_order.get('bolts', []) for bolt in bolts: self.tree.insert("", tk.END, values=( diff --git a/frontend/wrench_gui.spec b/frontend/wrench_gui.spec new file mode 100644 index 0000000..e8e3f19 --- /dev/null +++ b/frontend/wrench_gui.spec @@ -0,0 +1,75 @@ +# -*- mode: python ; coding: utf-8 -*- +""" +PyInstaller 打包配置文件 +用于打包 wrench_gui.py 为可执行文件 +""" + +block_cipher = None + +import os + +# 获取当前脚本所在目录的父目录(项目根目录) +# 这样 PyInstaller 可以找到 wrench_controller.py +current_dir = os.path.dirname(os.path.abspath('wrench_gui.spec')) +parent_dir = os.path.dirname(current_dir) + +a = Analysis( + ['wrench_gui.py'], + pathex=[ + parent_dir, # 添加父目录到搜索路径,让 PyInstaller 能找到 wrench_controller.py + current_dir, # 当前目录 + ], + binaries=[], + datas=[ + # 包含配置文件(如果程序需要读取) + (os.path.join(parent_dir, 'config.json'), '.'), # 将 config.json 复制到可执行文件目录 + ], + hiddenimports=[ + 'wrench_controller', + 'device_manager', + 'tkinter', + 'tkinter.ttk', + 'tkinter.messagebox', + 'requests', + 'json', + 'threading', + 'socket', + 'struct', + 'datetime', + 'pathlib', + ], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='wrench_gui', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, # 不显示控制台窗口(GUI应用) + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=None, # 可以添加图标文件路径,如 'icon.ico' +) + diff --git a/frontend/快速打包指南.txt b/frontend/快速打包指南.txt new file mode 100644 index 0000000..af6cea7 --- /dev/null +++ b/frontend/快速打包指南.txt @@ -0,0 +1,87 @@ +======================================== +wrench_gui.py 快速打包指南 +======================================== + +【Windows 系统】 + +1. 打开命令提示符(CMD)或 PowerShell + +2. 进入 frontend 目录: + cd F:\PyPro\TorqueWrench\frontend + +3. 执行打包脚本: + build_exe.bat + +4. 等待打包完成(约 1-3 分钟) + +5. 打包完成后,可执行文件在: + dist\wrench_gui\wrench_gui.exe + +6. 复制配置文件: + copy ..\config.json dist\wrench_gui\config.json + +7. 运行程序: + dist\wrench_gui\wrench_gui.exe + + +【Linux/Mac 系统】 + +1. 打开终端 + +2. 进入 frontend 目录: + cd /path/to/TorqueWrench/frontend + +3. 给脚本添加执行权限: + chmod +x build_exe.sh + +4. 执行打包脚本: + ./build_exe.sh + +5. 等待打包完成(约 1-3 分钟) + +6. 打包完成后,可执行文件在: + dist/wrench_gui/wrench_gui + +7. 复制配置文件: + cp ../config.json dist/wrench_gui/config.json + +8. 运行程序: + ./dist/wrench_gui/wrench_gui + + +【注意事项】 + +1. 首次打包前,确保已安装 PyInstaller: + pip install pyinstaller + +2. 确保已安装所有依赖: + pip install -r requirements.txt + +3. 打包前确保后端服务可以正常启动 + +4. 打包后的程序需要 config.json 配置文件才能运行 + +5. 如果打包失败,查看错误信息,通常是缺少依赖包 + + +【常见问题】 + +Q: 打包失败,提示找不到模块? +A: 检查是否已安装所有依赖:pip install -r requirements.txt + +Q: 打包后的程序无法启动? +A: 确保 config.json 在可执行文件同一目录下 + +Q: 打包后的程序很大? +A: 这是正常的,PyInstaller 会打包所有依赖,通常 15-30MB + +Q: 如何减小打包后的文件大小? +A: 使用虚拟环境,只安装必需的包,或在 spec 文件中排除不需要的模块 + + +【技术支持】 + +详细说明请查看:打包说明.md + + + diff --git a/frontend/打包说明.md b/frontend/打包说明.md new file mode 100644 index 0000000..5fae879 --- /dev/null +++ b/frontend/打包说明.md @@ -0,0 +1,298 @@ +# wrench_gui.py 打包说明 + +## 一、环境准备 + +### 1. 安装 Python 依赖 + +确保已安装所有必需的 Python 包: + +```bash +# 进入 frontend 目录 +cd frontend + +# 安装依赖 +pip install -r requirements.txt + +# 安装 PyInstaller(如果未安装) +pip install pyinstaller +``` + +### 2. 检查文件结构 + +确保以下文件存在: + +``` +TorqueWrench/ +├── frontend/ +│ ├── wrench_gui.py # 主程序 +│ ├── device_manager.py # 设备管理模块 +│ ├── wrench_gui.spec # PyInstaller 配置文件 +│ ├── build_exe.bat # Windows 打包脚本 +│ └── build_exe.sh # Linux/Mac 打包脚本 +├── wrench_controller.py # 扳手控制模块(父目录) +└── config.json # 配置文件(父目录) +``` + +--- + +## 二、打包步骤 + +### Windows 系统 + +1. **打开命令提示符或 PowerShell** + +2. **进入 frontend 目录** + ```cmd + cd F:\PyPro\TorqueWrench\frontend + ``` + +3. **执行打包脚本** + ```cmd + build_exe.bat + ``` + + 或者手动执行: + ```cmd + pyinstaller wrench_gui.spec + ``` + +### Linux/Mac 系统 + +1. **打开终端** + +2. **进入 frontend 目录** + ```bash + cd /path/to/TorqueWrench/frontend + ``` + +3. **给脚本添加执行权限** + ```bash + chmod +x build_exe.sh + ``` + +4. **执行打包脚本** + ```bash + ./build_exe.sh + ``` + + 或者手动执行: + ```bash + pyinstaller wrench_gui.spec + ``` + +--- + +## 三、打包输出 + +打包完成后,会在 `frontend/dist/` 目录下生成可执行文件: + +### Windows +- **位置**: `frontend/dist/wrench_gui/wrench_gui.exe` +- **大小**: 约 15-30 MB(取决于依赖) + +### Linux/Mac +- **位置**: `frontend/dist/wrench_gui/wrench_gui` +- **大小**: 约 15-30 MB + +--- + +## 四、运行打包后的程序 + +### 1. 准备配置文件 + +将 `config.json` 复制到可执行文件所在目录: + +**Windows:** +```cmd +copy ..\config.json dist\wrench_gui\config.json +``` + +**Linux/Mac:** +```bash +cp ../config.json dist/wrench_gui/config.json +``` + +### 2. 启动后端服务 + +确保后端 API 服务已启动: + +```bash +# 进入 backend 目录 +cd ../backend + +# 启动后端(Windows) +start_backend.bat + +# 或启动后端(Linux/Mac) +./start_backend.sh +``` + +### 3. 运行可执行文件 + +**Windows:** +- 双击 `dist/wrench_gui/wrench_gui.exe` +- 或在命令行执行: + ```cmd + dist\wrench_gui\wrench_gui.exe + ``` + +**Linux/Mac:** +```bash +./dist/wrench_gui/wrench_gui +``` + +--- + +## 五、常见问题 + +### 1. 打包失败:找不到模块 + +**问题**: `ModuleNotFoundError: No module named 'xxx'` + +**解决**: +- 检查是否已安装所有依赖:`pip install -r requirements.txt` +- 检查 `wrench_gui.spec` 中的 `hiddenimports` 是否包含缺失的模块 + +### 2. 运行时报错:找不到 config.json + +**问题**: 程序启动时报错找不到配置文件 + +**解决**: +- 确保 `config.json` 在可执行文件同一目录下 +- 检查 `wrench_gui.spec` 中的 `datas` 配置是否正确 + +### 3. 打包后的程序很大(>50MB) + +**原因**: PyInstaller 会打包所有依赖库 + +**优化**: +- 使用虚拟环境,只安装必需的包 +- 在 `wrench_gui.spec` 的 `excludes` 中添加不需要的模块 + +### 4. 控制台窗口也显示(Windows) + +**问题**: 打包后的程序同时显示 GUI 和控制台窗口 + +**解决**: +- 检查 `wrench_gui.spec` 中 `console=False` 是否设置正确 + +### 5. 图标设置 + +如果需要自定义程序图标: + +1. 准备图标文件(`.ico` 格式,Windows) +2. 修改 `wrench_gui.spec`: + ```python + icon='icon.ico', # 图标文件路径 + ``` +3. 重新打包 + +--- + +## 六、打包选项说明 + +### wrench_gui.spec 关键配置 + +- **`console=False`**: 不显示控制台窗口(GUI应用) +- **`upx=True`**: 使用 UPX 压缩(减小文件大小) +- **`datas`**: 包含的数据文件(如配置文件) +- **`hiddenimports`**: 需要显式导入的隐藏模块 + +### 单文件打包(可选) + +如果需要打包成单个 `.exe` 文件,修改 `wrench_gui.spec`: + +```python +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, # 改为 True + name='wrench_gui', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, +) +``` + +然后添加 `COLLECT` 部分(PyInstaller 会自动处理)。 + +--- + +## 七、分发打包后的程序 + +### 1. 最小分发包 + +需要包含的文件: + +``` +wrench_gui/ +├── wrench_gui.exe # 可执行文件 +├── config.json # 配置文件 +└── README.txt # 使用说明(可选) +``` + +### 2. 完整分发包(推荐) + +``` +wrench_gui/ +├── wrench_gui.exe +├── config.json +├── README.txt +└── 使用说明.pdf # 用户手册 +``` + +### 3. 注意事项 + +- 确保目标机器已安装必要的运行时库(如 Visual C++ Redistributable,Windows) +- 如果使用了 UPX 压缩,某些杀毒软件可能会误报,建议关闭 UPX 或添加白名单 + +--- + +## 八、快速打包命令(一键打包) + +### Windows +```cmd +cd frontend && build_exe.bat +``` + +### Linux/Mac +```bash +cd frontend && chmod +x build_exe.sh && ./build_exe.sh +``` + +--- + +## 九、验证打包结果 + +打包完成后,建议测试: + +1. **运行可执行文件** + - 检查是否能正常启动 + - 检查是否能连接到后端 API + - 检查设备管理功能是否正常 + +2. **检查文件大小** + - 正常大小:15-30 MB + - 如果过大(>50MB),检查依赖 + +3. **测试功能** + - 工单列表加载 + - 设备选择 + - 拧紧操作 + - 数据提交 + +--- + +## 十、技术支持 + +如果遇到打包问题: + +1. 查看 PyInstaller 官方文档:https://pyinstaller.org/ +2. 检查错误日志:`build/wrench_gui/warn-wrench_gui.txt` +3. 使用调试模式:在 `wrench_gui.spec` 中设置 `debug=True` + + + diff --git a/ssh_tunnel_auto.sh b/ssh_tunnel_auto.sh index 4582c16..01dc21a 100644 --- a/ssh_tunnel_auto.sh +++ b/ssh_tunnel_auto.sh @@ -79,3 +79,5 @@ while true; do done + + diff --git a/ssh_tunnel_service.sh b/ssh_tunnel_service.sh index 32cdf74..259f28d 100644 --- a/ssh_tunnel_service.sh +++ b/ssh_tunnel_service.sh @@ -72,3 +72,5 @@ echo " 查看日志: sudo journalctl -u ssh-tunnel -f" echo " 禁用自启: sudo systemctl disable ssh-tunnel" + + diff --git a/ssh_tunnel_setup.sh b/ssh_tunnel_setup.sh index 82ba33b..84ee798 100644 --- a/ssh_tunnel_setup.sh +++ b/ssh_tunnel_setup.sh @@ -105,3 +105,5 @@ echo "" echo " 或者使用自动重连脚本(见下方)" + + diff --git a/start_simulator.bat b/start_simulator.bat index cb4a335..3ea3417 100644 --- a/start_simulator.bat +++ b/start_simulator.bat @@ -9,3 +9,5 @@ pause + + diff --git a/start_simulator.sh b/start_simulator.sh index 924cd5e..e0dff04 100644 --- a/start_simulator.sh +++ b/start_simulator.sh @@ -7,3 +7,5 @@ python3 wrench_simulator.py + + diff --git a/test_device_connection.py b/test_device_connection.py index 14f04aa..8c30146 100644 --- a/test_device_connection.py +++ b/test_device_connection.py @@ -81,3 +81,5 @@ if __name__ == "__main__": + + diff --git a/test_simulator.py b/test_simulator.py index 1a6c6b1..7abe8fa 100644 --- a/test_simulator.py +++ b/test_simulator.py @@ -72,3 +72,5 @@ if __name__ == "__main__": + +