From 0f097afa0e9287dfe805c0b420b8766f6abc2767 Mon Sep 17 00:00:00 2001 From: ChaoticSi1ence Date: Fri, 20 Mar 2026 13:06:12 -0400 Subject: [PATCH 1/2] Translate entire project from Chinese to English Translates all UI text, comments, log messages, error messages, installer strings, documentation, and scripts to English across 51 files. Firmware protocol values and backward-compatible string matching are intentionally preserved in Chinese. Also adds GitHub Actions CI workflow for automated builds. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 90 ++++ .vscode/tasks.json | 4 +- README.md | 322 ++++++------ app.go | 154 +++--- bridge/README.md | 76 +-- bridge/TempBridge/Program.cs | 36 +- build/windows/installer/project.nsi | 242 ++++----- build_bridge.bat | 14 +- cmd/core/app.go | 464 +++++++++--------- cmd/core/crash_report.go | 14 +- cmd/core/curve_profiles.go | 34 +- cmd/core/main.go | 14 +- frontend/src/app/components/AppFatalError.tsx | 4 +- frontend/src/app/components/AppShell.tsx | 12 +- frontend/src/app/components/ControlPanel.tsx | 222 ++++----- frontend/src/app/components/DeviceStatus.tsx | 62 +-- frontend/src/app/components/FanCurve.tsx | 152 +++--- .../app/components/FanCurveProfileSelect.tsx | 4 +- frontend/src/app/components/ui/index.tsx | 2 +- frontend/src/app/layout.tsx | 2 +- frontend/src/app/lib/manualGearPresets.ts | 34 +- frontend/src/app/services/api.ts | 40 +- frontend/src/app/store/app-store.ts | 26 +- frontend/src/app/types/app.ts | 78 +-- internal/autostart/autostart.go | 96 ++-- internal/bridge/bridge.go | 140 +++--- internal/config/config.go | 86 ++-- internal/device/device.go | 214 ++++---- internal/device/rgb.go | 12 +- internal/hotkey/manager.go | 20 +- internal/ipc/ipc.go | 168 +++---- internal/logger/logger.go | 52 +- internal/notifier/notifier.go | 10 +- internal/smartcontrol/config.go | 4 +- internal/smartcontrol/doc.go | 2 +- internal/smartcontrol/helpers.go | 8 +- internal/smartcontrol/learning.go | 4 +- internal/smartcontrol/target.go | 4 +- internal/temperature/temperature.go | 56 +-- internal/tray/tray.go | 204 ++++---- internal/types/types.go | 204 ++++---- internal/version/version.go | 8 +- main.go | 44 +- scripts/ble_data.md | 18 +- scripts/ble_read.py | 180 +++---- scripts/ble_write.py | 152 +++--- scripts/hid_controller.go | 156 +++--- scripts/hid_controller.py | 292 +++++------ scripts/hid_data.md | 110 ++--- scripts/rpm_rgb_probe.py | 54 +- scripts/temp/temp.go | 36 +- wails.json | 2 +- 52 files changed, 2264 insertions(+), 2174 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f6ff37d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,90 @@ +name: Build BS2PRO-Controller + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set up .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Install Bun + uses: oven-sh/setup-bun@v2 + + - name: Install Wails + run: go install github.com/wailsapp/wails/v2/cmd/wails@latest + + - name: Install go-winres + run: go install github.com/tc-hib/go-winres@latest + + - name: Install NSIS + run: | + choco install nsis -y + echo "C:\Program Files (x86)\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Install frontend dependencies + working-directory: frontend + run: bun install + + - name: Extract version + id: version + shell: pwsh + run: | + $json = Get-Content wails.json | ConvertFrom-Json + $ver = $json.info.productVersion + echo "VERSION=$ver" >> $env:GITHUB_OUTPUT + + - name: Set LDFLAGS + shell: pwsh + run: | + echo "LDFLAGS=-X github.com/TIANLI0/BS2PRO-Controller/internal/version.BuildVersion=${{ steps.version.outputs.VERSION }} -H=windowsgui" >> $env:GITHUB_ENV + + - name: Build core service + run: | + go-winres make --in cmd/core/winres/winres.json --out cmd/core/rsrc + go build -ldflags "${{ env.LDFLAGS }}" -o build/bin/BS2PRO-Core.exe ./cmd/core/ + + - name: Build TempBridge + run: | + dotnet restore bridge/TempBridge/TempBridge.csproj + dotnet publish bridge/TempBridge/TempBridge.csproj -c Release --self-contained false -o build/bin/bridge + + - name: Build main application (Wails + NSIS installer) + run: wails build -nsis -ldflags "${{ env.LDFLAGS }}" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: BS2PRO-Controller-${{ steps.version.outputs.VERSION }} + path: | + build/bin/*.exe + build/bin/bridge/ + retention-days: 30 + + - name: Upload installer + uses: actions/upload-artifact@v4 + with: + name: BS2PRO-Controller-Installer-${{ steps.version.outputs.VERSION }} + path: build/bin/*installer*.exe + retention-days: 30 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 91d0ea2..2d4be98 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -60,7 +60,7 @@ "-ExecutionPolicy", "Bypass", "-Command", - "$ErrorActionPreference='Stop'; if (-not (Get-Command riscv-none-elf-objcopy -ErrorAction SilentlyContinue)) { throw '未找到 riscv-none-elf-objcopy,请先安装 RISC-V GNU Toolchain 并加入 PATH。' }; if (-not (Get-Command riscv-none-elf-objdump -ErrorAction SilentlyContinue)) { throw '未找到 riscv-none-elf-objdump,请先安装 RISC-V GNU Toolchain 并加入 PATH。' }; riscv-none-elf-objcopy -I binary -O elf32-littleriscv -B riscv ota/CH591_For_BS2PRO_Ver0.0.3.5.bin ota/fw.elf; riscv-none-elf-objdump -D -M no-aliases ota/fw.elf > ota/fw.asm; Write-Host 'ELF/ASM generated: ota/fw.elf, ota/fw.asm'" + "$ErrorActionPreference='Stop'; if (-not (Get-Command riscv-none-elf-objcopy -ErrorAction SilentlyContinue)) { throw 'riscv-none-elf-objcopy not found. Please install RISC-V GNU Toolchain and add it to PATH.' }; if (-not (Get-Command riscv-none-elf-objdump -ErrorAction SilentlyContinue)) { throw 'riscv-none-elf-objdump not found. Please install RISC-V GNU Toolchain and add it to PATH.' }; riscv-none-elf-objcopy -I binary -O elf32-littleriscv -B riscv ota/CH591_For_BS2PRO_Ver0.0.3.5.bin ota/fw.elf; riscv-none-elf-objdump -D -M no-aliases ota/fw.elf > ota/fw.asm; Write-Host 'ELF/ASM generated: ota/fw.elf, ota/fw.asm'" ], "problemMatcher": [] }, @@ -106,7 +106,7 @@ "-ExecutionPolicy", "Bypass", "-Command", - "$ErrorActionPreference='Stop'; if (-not (Get-Command rizin -ErrorAction SilentlyContinue)) { throw '未找到 rizin,请先安装 rizin 并加入 PATH。' }; rizin -a riscv -b 32 -m 0x0 ota/CH591_For_BS2PRO_Ver0.0.3.5.bin" + "$ErrorActionPreference='Stop'; if (-not (Get-Command rizin -ErrorAction SilentlyContinue)) { throw 'rizin not found. Please install rizin and add it to PATH.' }; rizin -a riscv -b 32 -m 0x0 ota/CH591_For_BS2PRO_Ver0.0.3.5.bin" ], "problemMatcher": [] } diff --git a/README.md b/README.md index 8f9174c..36df4e5 100644 --- a/README.md +++ b/README.md @@ -1,278 +1,278 @@ # BS2PRO-Controller -> 飞智空间站 BS2/BS2PRO 的第三方替代控制器 +> A third-party alternative controller for FlyDigi Space Station BS2/BS2PRO -一个基于 Wails + Go + Next.js 开发的桌面应用,用于控制飞智空间站 BS2/BS2PRO 散热器设备,提供风扇控制、温度监控等功能。 +A desktop application built with Wails + Go + Next.js for controlling FlyDigi Space Station BS2/BS2PRO cooler devices, providing fan control, temperature monitoring, and more. -## 功能特性 +## Features -- 🎮 **设备支持**:支持飞智 BS2 和 BS2PRO 散热器 -- 🌡️ **温度监控**:实时监控 CPU/GPU 温度(支持多种温度数据桥接方式) -- 💨 **风扇控制**: - - 自动模式:根据温度自动调节风速 - - 学习控温:根据目标温度持续学习并微调曲线偏移 - - 手动模式:自定义固定风速 - - 曲线模式:自定义温度-风速曲线 -- 📊 **可视化面板**:直观的温度和风速实时显示 -- 🎯 **系统托盘**:支持最小化到系统托盘,后台运行 -- 🚀 **开机自启**:可设置开机自动启动并最小化运行 -- 🔧 **多进程架构**:GUI 和核心服务分离,稳定可靠 -- 🛠️ **灯带配置**:支持灯带复杂调控,感谢群友 @Whether +- **Device Support**: Supports FlyDigi BS2 and BS2PRO coolers +- **Temperature Monitoring**: Real-time CPU/GPU temperature monitoring (supports multiple temperature data bridging methods) +- **Fan Control**: + - Auto Mode: Automatically adjusts fan speed based on temperature + - Learning Temperature Control: Continuously learns and fine-tunes curve offset based on target temperature + - Manual Mode: Custom fixed fan speed + - Curve Mode: Custom temperature-fan speed curve +- **Visual Dashboard**: Intuitive real-time temperature and fan speed display +- **System Tray**: Supports minimizing to system tray for background operation +- **Auto-start on Boot**: Can be set to auto-start and minimize on boot +- **Multi-process Architecture**: GUI and core services are separated for stability and reliability +- **LED Strip Configuration**: Supports complex LED strip control, thanks to community member @Whether -## 系统架构 +## System Architecture -项目采用三进程架构: +The project uses a three-process architecture: -- **GUI 进程** (`BS2PRO-Controller.exe`):提供用户界面,使用 Wails 框架 -- **核心服务** (`BS2PRO-Core.exe`):后台运行,负责设备通信和温度监控 -- **温度桥接进程** (`TempBridge.exe`):通过 C# 程序获取系统温度数据 +- **GUI Process** (`BS2PRO-Controller.exe`): Provides the user interface, built with the Wails framework +- **Core Service** (`BS2PRO-Core.exe`): Runs in the background, responsible for device communication and temperature monitoring +- **Temperature Bridge Process** (`TempBridge.exe`): A C# program that retrieves system temperature data -三个进程通过 IPC (进程间通信) 进行数据交互。 +The three processes exchange data via IPC (Inter-Process Communication). -## 技术栈 +## Tech Stack -### 后端 -- **Go 1.25+**:主要开发语言 -- **Wails v2**:跨平台桌面应用框架 -- **go-hid**:HID 设备通信 -- **zap**:日志记录 +### Backend +- **Go 1.25+**: Primary development language +- **Wails v2**: Cross-platform desktop application framework +- **go-hid**: HID device communication +- **zap**: Logging -### 前端 -- **Next.js 16**:React 框架 -- **TypeScript**:类型安全 -- **Tailwind CSS 4**:样式框架 -- **Recharts**:图表可视化 +### Frontend +- **Next.js 16**: React framework +- **TypeScript**: Type safety +- **Tailwind CSS 4**: Styling framework +- **Recharts**: Chart visualization -### 温度桥接 -- **C# .NET Framework 4.7.2**:温度数据桥接程序 +### Temperature Bridge +- **C# .NET Framework 4.7.2**: Temperature data bridge program -## 开发环境要求 +## Development Environment Requirements -### 必需软件 -- **Go 1.21+**:[下载地址](https://golang.org/dl/) -- **Node.js 18+**:[下载地址](https://nodejs.org/) -- **Bun**:快速的 JavaScript 运行时 [安装说明](https://bun.sh/) -- **Wails CLI**:安装命令 `go install github.com/wailsapp/wails/v2/cmd/wails@latest` -- **.NET SDK 8.0+**:[下载地址](https://dotnet.microsoft.com/download) -- **go-winres**:Windows 资源工具 `go install github.com/tc-hib/go-winres@latest` +### Required Software +- **Go 1.21+**: [Download](https://golang.org/dl/) +- **Node.js 18+**: [Download](https://nodejs.org/) +- **Bun**: Fast JavaScript runtime [Installation Guide](https://bun.sh/) +- **Wails CLI**: Install with `go install github.com/wailsapp/wails/v2/cmd/wails@latest` +- **.NET SDK 8.0+**: [Download](https://dotnet.microsoft.com/download) +- **go-winres**: Windows resource tool `go install github.com/tc-hib/go-winres@latest` -### 可选软件 -- **NSIS 3.x**:用于生成安装程序 [下载地址](https://nsis.sourceforge.io/) +### Optional Software +- **NSIS 3.x**: For generating installers [Download](https://nsis.sourceforge.io/) -## 快速开始 +## Quick Start -### 1. 克隆项目 +### 1. Clone the Project ```bash git clone https://github.com/TIANLI0/BS2PRO-Controller.git cd BS2PRO-Controller ``` -### 2. 安装依赖 +### 2. Install Dependencies -#### 安装 Go 依赖 +#### Install Go Dependencies ```bash go mod tidy ``` -#### 安装前端依赖 +#### Install Frontend Dependencies ```bash cd frontend bun install cd .. ``` -### 3. 开发模式运行 +### 3. Run in Development Mode ```bash -# 启动 Wails 开发模式(包含热重载) +# Start Wails development mode (with hot reload) wails dev ``` -### 4. 构建生产版本 +### 4. Build Production Version -#### 构建温度桥接程序 +#### Build Temperature Bridge Program ```bash build_bridge.bat ``` -#### 构建完整应用 +#### Build Complete Application ```bash build.bat ``` -构建完成后,可执行文件位于 `build/bin/` 目录: -- `BS2PRO-Controller.exe` - GUI 主程序 -- `BS2PRO-Core.exe` - 核心服务 -- `bridge/TempBridge.exe` - 温度桥接程序 +After building, the executables are located in the `build/bin/` directory: +- `BS2PRO-Controller.exe` - GUI main program +- `BS2PRO-Core.exe` - Core service +- `bridge/TempBridge.exe` - Temperature bridge program -安装程序位于 `build/bin/` 目录: -- `BS2PRO-Controller-amd64-installer.exe` - Windows 安装程序 +The installer is located in the `build/bin/` directory: +- `BS2PRO-Controller-amd64-installer.exe` - Windows installer -## 项目结构 +## Project Structure ``` BS2PRO-Controller/ -├── main.go # GUI 主程序入口 -├── app.go # GUI 应用逻辑 -├── wails.json # Wails 配置文件 -├── build.bat # Windows 构建脚本 -├── build_bridge.bat # 桥接程序构建脚本 +├── main.go # GUI main program entry point +├── app.go # GUI application logic +├── wails.json # Wails configuration file +├── build.bat # Windows build script +├── build_bridge.bat # Bridge program build script ├── cmd/ -│ └── core/ # 核心服务程序 -│ ├── main.go # 服务入口 -│ └── app.go # 服务逻辑 +│ └── core/ # Core service program +│ ├── main.go # Service entry point +│ └── app.go # Service logic │ -├── internal/ # 内部包 -│ ├── autostart/ # 开机自启管理 -│ ├── bridge/ # 温度桥接通信 -│ ├── config/ # 配置管理 -│ ├── device/ # HID 设备通信 -│ ├── ipc/ # 进程间通信 -│ ├── logger/ # 日志模块 -│ ├── temperature/ # 温度监控 -│ ├── tray/ # 系统托盘 -│ ├── types/ # 类型定义 -│ └── version/ # 版本信息 +├── internal/ # Internal packages +│ ├── autostart/ # Auto-start on boot management +│ ├── bridge/ # Temperature bridge communication +│ ├── config/ # Configuration management +│ ├── device/ # HID device communication +│ ├── ipc/ # Inter-process communication +│ ├── logger/ # Logging module +│ ├── temperature/ # Temperature monitoring +│ ├── tray/ # System tray +│ ├── types/ # Type definitions +│ └── version/ # Version information │ ├── bridge/ -│ └── TempBridge/ # C# 温度桥接程序 -│ └── Program.cs # 桥接程序源码 +│ └── TempBridge/ # C# temperature bridge program +│ └── Program.cs # Bridge program source code │ -├── frontend/ # Next.js 前端 +├── frontend/ # Next.js frontend │ ├── src/ │ │ ├── app/ -│ │ │ ├── components/ # React 组件 -│ │ │ ├── services/ # API 服务 -│ │ │ └── types/ # TypeScript 类型 +│ │ │ ├── components/ # React components +│ │ │ ├── services/ # API services +│ │ │ └── types/ # TypeScript types │ │ └── ... │ └── package.json │ -└── build/ # 构建输出目录 +└── build/ # Build output directory ``` -## 使用说明 +## Usage Guide -### 首次运行 +### First Run -1. 运行 `BS2PRO-Controller.exe` 启动程序 -2. 程序会自动启动核心服务 `BS2PRO-Core.exe` -3. 连接你的 BS2/BS2PRO 设备(USB 连接) -4. 程序会自动检测并连接设备 +1. Run `BS2PRO-Controller.exe` to launch the program +2. The program will automatically start the core service `BS2PRO-Core.exe` +3. Connect your BS2/BS2PRO device (USB connection) +4. The program will automatically detect and connect to the device -### 风扇控制模式 +### Fan Control Modes -#### 自动模式 -- 根据当前温度自动调节风速 -- 适合日常使用 +#### Auto Mode +- Automatically adjusts fan speed based on current temperature +- Suitable for daily use -#### 手动模式 -- 设置固定的风速档位(0-9档) -- 适合特定需求场景 +#### Manual Mode +- Set a fixed fan speed level (levels 0-9) +- Suitable for specific use cases -#### 曲线模式 -- 自定义温度-风速曲线 -- 可添加多个控制点 -- 实现精细化的温度控制 +#### Curve Mode +- Custom temperature-fan speed curve +- Multiple control points can be added +- Achieves fine-grained temperature control -### 温度监控 +### Temperature Monitoring -程序支持多种温度监控方式: +The program supports multiple temperature monitoring methods: -1. **TempBridge**:通过 C# 桥接程序获取系统温度 +1. **TempBridge**: Retrieves system temperature via the C# bridge program -### 系统托盘 +### System Tray -- 点击托盘图标打开主窗口 -- 右键菜单提供快捷操作 -- 支持最小化到托盘后台运行 +- Click the tray icon to open the main window +- Right-click menu provides quick actions +- Supports minimizing to tray for background operation -## 配置文件 +## Configuration File -配置文件位于 `%APPDATA%\BS2PRO-Controller\config.json` +The configuration file is located at `%APPDATA%\BS2PRO-Controller\config.json` -主要配置项: +Main configuration options: ```json { - "autoStart": false, // 开机自启 - "minimizeToTray": true, // 关闭时最小化到托盘 - "temperatureSource": "auto", // 温度数据源 - "updateInterval": 1000, // 更新间隔(毫秒) - "fanCurve": [...], // 风扇曲线 - "fanMode": "auto" // 风扇模式 + "autoStart": false, // Auto-start on boot + "minimizeToTray": true, // Minimize to tray on close + "temperatureSource": "auto", // Temperature data source + "updateInterval": 1000, // Update interval (milliseconds) + "fanCurve": [...], // Fan curve + "fanMode": "auto" // Fan mode } ``` -## 日志文件 +## Log Files -日志文件位于 `build/bin/logs/` 目录: -- `core_YYYYMMDD.log` - 核心服务日志 -- `gui_YYYYMMDD.log` - GUI 程序日志 +Log files are located in the `build/bin/logs/` directory: +- `core_YYYYMMDD.log` - Core service log +- `gui_YYYYMMDD.log` - GUI program log -## 常见问题 +## FAQ -### 设备无法连接? -1. 确保 BS2/BS2PRO 设备已正确连接到电脑 -2. 检查设备驱动是否正常安装 -3. 尝试重新插拔设备 -4. 查看日志文件排查具体错误 +### Device won't connect? +1. Make sure the BS2/BS2PRO device is properly connected to the computer +2. Check that the device driver is installed correctly +3. Try unplugging and re-plugging the device +4. Check the log files for specific errors -### 温度无法显示? -1. 检查温度数据源设置 -2. 如使用 TempBridge,确保 `bridge` 目录下的文件完整 -3. 如使用 AIDA64/HWiNFO,确保软件正在运行并开启了共享内存功能 +### Temperature not showing? +1. Check the temperature data source settings +2. If using TempBridge, make sure the files in the `bridge` directory are complete +3. If using AIDA64/HWiNFO, make sure the software is running and shared memory is enabled -### 开机自启无效? -1. 以管理员身份运行程序后重新设置 -2. 检查注册表项:`HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run` +### Auto-start on boot not working? +1. Run the program as administrator and reconfigure the setting +2. Check the registry entry: `HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run` -## 构建说明 +## Build Instructions -### 版本号管理 +### Version Number Management -版本号在 `wails.json` 的 `info.productVersion` 字段中定义,构建脚本会自动读取并嵌入到程序中。 +The version number is defined in the `info.productVersion` field of `wails.json`. The build script automatically reads and embeds it into the program. ### LDFLAGS -构建时会注入版本信息: +Version information is injected during build: ```bash --ldflags "-X github.com/TIANLI0/BS2PRO-Controller/internal/version.BuildVersion=版本号 -H=windowsgui" +-ldflags "-X github.com/TIANLI0/BS2PRO-Controller/internal/version.BuildVersion=VERSION -H=windowsgui" ``` -### 生成安装程序 +### Generating the Installer -执行 `build.bat` 会自动生成 NSIS 安装程序(需要安装 NSIS)。 +Running `build.bat` will automatically generate an NSIS installer (NSIS must be installed). -## 贡献指南 +## Contributing -欢迎提交 Issue 和 Pull Request! +Issues and Pull Requests are welcome! -1. Fork 本项目 -2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) -3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) -4. 推送到分支 (`git push origin feature/AmazingFeature`) -5. 开启 Pull Request +1. Fork this project +2. Create a feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request -## 开源许可 +## License -本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情 +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. -## 作者 +## Author - **TIANLI0** - [GitHub](https://github.com/TIANLI0) - Email: wutianli@tianli0.top -## 致谢 +## Acknowledgements -- [Wails](https://wails.io/) - 优秀的 Go 桌面应用框架 -- [Next.js](https://nextjs.org/) - React 应用框架 -- 飞智- BS2/BS2PRO 硬件设备 +- [Wails](https://wails.io/) - Excellent Go desktop application framework +- [Next.js](https://nextjs.org/) - React application framework +- FlyDigi - BS2/BS2PRO hardware devices -## 免责声明 +## Disclaimer -本项目为第三方开源项目,与飞智官方无关。使用本软件产生的任何问题由用户自行承担。 +This is a third-party open-source project and is not affiliated with FlyDigi. Any issues arising from the use of this software are the sole responsibility of the user. --- -⭐ 如果这个项目对你有帮助,请给一个 Star! +If this project is helpful to you, please give it a Star! diff --git a/app.go b/app.go index 3781ed5..ac0e9df 100644 --- a/app.go +++ b/app.go @@ -13,18 +13,18 @@ import ( "go.uber.org/zap" ) -// App struct - GUI 应用程序结构 +// App struct - GUI application structure type App struct { ctx context.Context ipcClient *ipc.Client mutex sync.RWMutex - // 缓存的状态 + // Cached state isConnected bool currentTemp types.TemperatureData } -// 为了与前端 API 兼容,重新导出类型 +// Re-export types for frontend API compatibility type ( FanCurvePoint = types.FanCurvePoint FanCurveProfile = types.FanCurveProfile @@ -43,7 +43,7 @@ func init() { guiLogger = logger.Sugar() } -// NewApp 创建 GUI 应用实例 +// NewApp creates a GUI application instance func NewApp() *App { return &App{ ipcClient: ipc.NewClient(nil), @@ -51,32 +51,32 @@ func NewApp() *App { } } -// startup 应用启动时调用 +// startup called when the application starts func (a *App) startup(ctx context.Context) { a.ctx = ctx - guiLogger.Info("=== BS2PRO GUI 启动 ===") + guiLogger.Info("=== BS2PRO GUI starting ===") - // 连接到核心服务 + // Connect to core service if err := a.ipcClient.Connect(); err != nil { - guiLogger.Errorf("连接核心服务失败: %v", err) - runtime.EventsEmit(ctx, "core-service-error", "无法连接到核心服务") + guiLogger.Errorf("Failed to connect to core service: %v", err) + runtime.EventsEmit(ctx, "core-service-error", "Unable to connect to core service") } else { - guiLogger.Info("已连接到核心服务") + guiLogger.Info("Connected to core service") - // 设置事件处理器 + // Set event handler a.ipcClient.SetEventHandler(a.handleCoreEvent) } - guiLogger.Info("=== BS2PRO GUI 启动完成 ===") + guiLogger.Info("=== BS2PRO GUI startup complete ===") } -// GetAppVersion 返回应用版本号(来自版本模块) +// GetAppVersion returns the application version (from version module) func (a *App) GetAppVersion() string { return version.Get() } -// handleCoreEvent 处理核心服务推送的事件 +// handleCoreEvent handles events pushed by the core service func (a *App) handleCoreEvent(event ipc.Event) { if a.ctx == nil { return @@ -147,29 +147,29 @@ func (a *App) handleCoreEvent(event ipc.Event) { } } -// sendRequest 发送请求到核心服务 +// sendRequest sends a request to the core service func (a *App) sendRequest(reqType ipc.RequestType, data any) (*ipc.Response, error) { if !a.ipcClient.IsConnected() { - // 尝试重新连接 + // Try to reconnect if err := a.ipcClient.Connect(); err != nil { - return nil, fmt.Errorf("未连接到核心服务: %v", err) + return nil, fmt.Errorf("not connected to core service: %v", err) } } return a.ipcClient.SendRequest(reqType, data) } -// === 前端 API 方法 === -// 以下所有公开方法保持与原始 app.go 完全兼容 +// === Frontend API Methods === +// All public methods below maintain full compatibility with the original app.go -// ConnectDevice 连接HID设备 +// ConnectDevice connects to the HID device func (a *App) ConnectDevice() bool { resp, err := a.sendRequest(ipc.ReqConnect, nil) if err != nil { - guiLogger.Errorf("连接设备请求失败: %v", err) + guiLogger.Errorf("Connect device request failed: %v", err) return false } if !resp.Success { - guiLogger.Errorf("连接设备失败: %s", resp.Error) + guiLogger.Errorf("Failed to connect device: %s", resp.Error) return false } var success bool @@ -177,12 +177,12 @@ func (a *App) ConnectDevice() bool { return success } -// DisconnectDevice 断开设备连接 +// DisconnectDevice disconnects the device func (a *App) DisconnectDevice() { a.sendRequest(ipc.ReqDisconnect, nil) } -// GetDeviceStatus 获取设备连接状态 +// GetDeviceStatus gets the device connection status func (a *App) GetDeviceStatus() map[string]any { resp, err := a.sendRequest(ipc.ReqGetDeviceStatus, nil) if err != nil { @@ -196,15 +196,15 @@ func (a *App) GetDeviceStatus() map[string]any { return status } -// GetConfig 获取当前配置 +// GetConfig gets the current configuration func (a *App) GetConfig() AppConfig { resp, err := a.sendRequest(ipc.ReqGetConfig, nil) if err != nil { - guiLogger.Errorf("获取配置失败: %v", err) + guiLogger.Errorf("Failed to get config: %v", err) return types.GetDefaultConfig(false) } if !resp.Success { - guiLogger.Errorf("获取配置失败: %s", resp.Error) + guiLogger.Errorf("Failed to get config: %s", resp.Error) return types.GetDefaultConfig(false) } var cfg AppConfig @@ -212,7 +212,7 @@ func (a *App) GetConfig() AppConfig { return cfg } -// UpdateConfig 更新配置 +// UpdateConfig updates the configuration func (a *App) UpdateConfig(cfg AppConfig) error { resp, err := a.sendRequest(ipc.ReqUpdateConfig, cfg) if err != nil { @@ -224,7 +224,7 @@ func (a *App) UpdateConfig(cfg AppConfig) error { return nil } -// SetFanCurve 设置风扇曲线 +// SetFanCurve sets the fan curve func (a *App) SetFanCurve(curve []FanCurvePoint) error { resp, err := a.sendRequest(ipc.ReqSetFanCurve, curve) if err != nil { @@ -236,7 +236,7 @@ func (a *App) SetFanCurve(curve []FanCurvePoint) error { return nil } -// GetFanCurve 获取风扇曲线 +// GetFanCurve gets the fan curve func (a *App) GetFanCurve() []FanCurvePoint { resp, err := a.sendRequest(ipc.ReqGetFanCurve, nil) if err != nil { @@ -250,7 +250,7 @@ func (a *App) GetFanCurve() []FanCurvePoint { return curve } -// GetFanCurveProfiles 获取曲线方案列表 +// GetFanCurveProfiles gets the list of curve profiles func (a *App) GetFanCurveProfiles() FanCurveProfilesPayload { resp, err := a.sendRequest(ipc.ReqGetFanCurveProfiles, nil) if err != nil || !resp.Success { @@ -265,7 +265,7 @@ func (a *App) GetFanCurveProfiles() FanCurveProfilesPayload { return payload } -// SetActiveFanCurveProfile 设置当前激活曲线方案 +// SetActiveFanCurveProfile sets the currently active curve profile func (a *App) SetActiveFanCurveProfile(profileID string) error { resp, err := a.sendRequest(ipc.ReqSetActiveFanCurveProfile, ipc.SetActiveFanCurveProfileParams{ID: profileID}) if err != nil { @@ -277,7 +277,7 @@ func (a *App) SetActiveFanCurveProfile(profileID string) error { return nil } -// SaveFanCurveProfile 保存曲线方案 +// SaveFanCurveProfile saves a curve profile func (a *App) SaveFanCurveProfile(profileID, name string, curve []FanCurvePoint, setActive bool) (FanCurveProfile, error) { resp, err := a.sendRequest(ipc.ReqSaveFanCurveProfile, ipc.SaveFanCurveProfileParams{ ID: profileID, @@ -296,7 +296,7 @@ func (a *App) SaveFanCurveProfile(profileID, name string, curve []FanCurvePoint, return profile, nil } -// DeleteFanCurveProfile 删除曲线方案 +// DeleteFanCurveProfile deletes a curve profile func (a *App) DeleteFanCurveProfile(profileID string) error { resp, err := a.sendRequest(ipc.ReqDeleteFanCurveProfile, ipc.DeleteFanCurveProfileParams{ID: profileID}) if err != nil { @@ -308,7 +308,7 @@ func (a *App) DeleteFanCurveProfile(profileID string) error { return nil } -// ExportFanCurveProfiles 导出曲线方案 +// ExportFanCurveProfiles exports curve profiles func (a *App) ExportFanCurveProfiles() (string, error) { resp, err := a.sendRequest(ipc.ReqExportFanCurveProfiles, nil) if err != nil { @@ -322,7 +322,7 @@ func (a *App) ExportFanCurveProfiles() (string, error) { return code, nil } -// ImportFanCurveProfiles 导入曲线方案 +// ImportFanCurveProfiles imports curve profiles func (a *App) ImportFanCurveProfiles(code string) error { resp, err := a.sendRequest(ipc.ReqImportFanCurveProfiles, ipc.ImportFanCurveProfilesParams{Code: code}) if err != nil { @@ -334,7 +334,7 @@ func (a *App) ImportFanCurveProfiles(code string) error { return nil } -// SetAutoControl 设置智能变频 +// SetAutoControl sets smart fan control func (a *App) SetAutoControl(enabled bool) error { resp, err := a.sendRequest(ipc.ReqSetAutoControl, ipc.SetAutoControlParams{Enabled: enabled}) if err != nil { @@ -346,7 +346,7 @@ func (a *App) SetAutoControl(enabled bool) error { return nil } -// SetManualGear 设置手动挡位 +// SetManualGear sets the manual gear func (a *App) SetManualGear(gear, level string) bool { resp, err := a.sendRequest(ipc.ReqSetManualGear, ipc.SetManualGearParams{Gear: gear, Level: level}) if err != nil { @@ -360,7 +360,7 @@ func (a *App) SetManualGear(gear, level string) bool { return success } -// GetAvailableGears 获取可用挡位 +// GetAvailableGears gets available gears func (a *App) GetAvailableGears() map[string][]GearCommand { resp, err := a.sendRequest(ipc.ReqGetAvailableGears, nil) if err != nil { @@ -374,13 +374,13 @@ func (a *App) GetAvailableGears() map[string][]GearCommand { return gears } -// ManualSetFanSpeed 废弃方法 +// ManualSetFanSpeed deprecated method func (a *App) ManualSetFanSpeed(rpm int) bool { - guiLogger.Warn("ManualSetFanSpeed 已废弃,请使用 SetManualGear") + guiLogger.Warn("ManualSetFanSpeed is deprecated, use SetManualGear instead") return false } -// SetCustomSpeed 设置自定义转速 +// SetCustomSpeed sets custom fan speed func (a *App) SetCustomSpeed(enabled bool, rpm int) error { resp, err := a.sendRequest(ipc.ReqSetCustomSpeed, ipc.SetCustomSpeedParams{Enabled: enabled, RPM: rpm}) if err != nil { @@ -392,7 +392,7 @@ func (a *App) SetCustomSpeed(enabled bool, rpm int) error { return nil } -// SetGearLight 设置挡位灯 +// SetGearLight sets the gear indicator light func (a *App) SetGearLight(enabled bool) bool { resp, err := a.sendRequest(ipc.ReqSetGearLight, ipc.SetBoolParams{Enabled: enabled}) if err != nil { @@ -403,7 +403,7 @@ func (a *App) SetGearLight(enabled bool) bool { return success } -// SetPowerOnStart 设置通电自启动 +// SetPowerOnStart sets power-on auto-start func (a *App) SetPowerOnStart(enabled bool) bool { resp, err := a.sendRequest(ipc.ReqSetPowerOnStart, ipc.SetBoolParams{Enabled: enabled}) if err != nil { @@ -414,7 +414,7 @@ func (a *App) SetPowerOnStart(enabled bool) bool { return success } -// SetSmartStartStop 设置智能启停 +// SetSmartStartStop sets smart start/stop func (a *App) SetSmartStartStop(mode string) bool { resp, err := a.sendRequest(ipc.ReqSetSmartStartStop, ipc.SetStringParams{Value: mode}) if err != nil { @@ -425,7 +425,7 @@ func (a *App) SetSmartStartStop(mode string) bool { return success } -// SetBrightness 设置亮度 +// SetBrightness sets the brightness func (a *App) SetBrightness(percentage int) bool { resp, err := a.sendRequest(ipc.ReqSetBrightness, ipc.SetIntParams{Value: percentage}) if err != nil { @@ -436,7 +436,7 @@ func (a *App) SetBrightness(percentage int) bool { return success } -// SetLightStrip 设置灯带 +// SetLightStrip sets the light strip func (a *App) SetLightStrip(cfg types.LightStripConfig) error { resp, err := a.sendRequest(ipc.ReqSetLightStrip, ipc.SetLightStripParams{Config: cfg}) if err != nil { @@ -448,7 +448,7 @@ func (a *App) SetLightStrip(cfg types.LightStripConfig) error { return nil } -// GetTemperature 获取当前温度 +// GetTemperature gets the current temperature func (a *App) GetTemperature() TemperatureData { resp, err := a.sendRequest(ipc.ReqGetTemperature, nil) if err != nil { @@ -461,7 +461,7 @@ func (a *App) GetTemperature() TemperatureData { return temp } -// GetCurrentFanData 获取当前风扇数据 +// GetCurrentFanData gets the current fan data func (a *App) GetCurrentFanData() *FanData { resp, err := a.sendRequest(ipc.ReqGetCurrentFanData, nil) if err != nil { @@ -474,7 +474,7 @@ func (a *App) GetCurrentFanData() *FanData { return &fanData } -// TestTemperatureReading 测试温度读取 +// TestTemperatureReading tests temperature reading func (a *App) TestTemperatureReading() TemperatureData { resp, err := a.sendRequest(ipc.ReqTestTemperatureReading, nil) if err != nil { @@ -485,7 +485,7 @@ func (a *App) TestTemperatureReading() TemperatureData { return temp } -// TestBridgeProgram 测试桥接程序 +// TestBridgeProgram tests the bridge program func (a *App) TestBridgeProgram() BridgeTemperatureData { resp, err := a.sendRequest(ipc.ReqTestBridgeProgram, nil) if err != nil { @@ -496,7 +496,7 @@ func (a *App) TestBridgeProgram() BridgeTemperatureData { return data } -// GetBridgeProgramStatus 获取桥接程序状态 +// GetBridgeProgramStatus gets the bridge program status func (a *App) GetBridgeProgramStatus() map[string]any { resp, err := a.sendRequest(ipc.ReqGetBridgeProgramStatus, nil) if err != nil { @@ -507,7 +507,7 @@ func (a *App) GetBridgeProgramStatus() map[string]any { return status } -// SetWindowsAutoStart 设置Windows开机自启动 +// SetWindowsAutoStart sets Windows startup auto-launch func (a *App) SetWindowsAutoStart(enable bool) error { resp, err := a.sendRequest(ipc.ReqSetWindowsAutoStart, ipc.SetBoolParams{Enabled: enable}) if err != nil { @@ -519,7 +519,7 @@ func (a *App) SetWindowsAutoStart(enable bool) error { return nil } -// IsRunningAsAdmin 检查是否以管理员权限运行 +// IsRunningAsAdmin checks if running with administrator privileges func (a *App) IsRunningAsAdmin() bool { resp, err := a.sendRequest(ipc.ReqIsRunningAsAdmin, nil) if err != nil { @@ -530,7 +530,7 @@ func (a *App) IsRunningAsAdmin() bool { return isAdmin } -// GetAutoStartMethod 获取当前的自启动方式 +// GetAutoStartMethod gets the current auto-start method func (a *App) GetAutoStartMethod() string { resp, err := a.sendRequest(ipc.ReqGetAutoStartMethod, nil) if err != nil { @@ -541,7 +541,7 @@ func (a *App) GetAutoStartMethod() string { return method } -// SetAutoStartWithMethod 使用指定方式设置自启动 +// SetAutoStartWithMethod sets auto-start using the specified method func (a *App) SetAutoStartWithMethod(enable bool, method string) error { resp, err := a.sendRequest(ipc.ReqSetAutoStartWithMethod, ipc.SetAutoStartWithMethodParams{Enable: enable, Method: method}) if err != nil { @@ -553,7 +553,7 @@ func (a *App) SetAutoStartWithMethod(enable bool, method string) error { return nil } -// CheckWindowsAutoStart 检查Windows开机自启动状态 +// CheckWindowsAutoStart checks Windows startup auto-launch status func (a *App) CheckWindowsAutoStart() bool { resp, err := a.sendRequest(ipc.ReqCheckWindowsAutoStart, nil) if err != nil { @@ -564,7 +564,7 @@ func (a *App) CheckWindowsAutoStart() bool { return enabled } -// IsAutoStartLaunch 返回当前是否为自启动启动 +// IsAutoStartLaunch returns whether the current launch is an auto-start launch func (a *App) IsAutoStartLaunch() bool { resp, err := a.sendRequest(ipc.ReqIsAutoStartLaunch, nil) if err != nil { @@ -575,7 +575,7 @@ func (a *App) IsAutoStartLaunch() bool { return isAutoStart } -// ShowWindow 显示主窗口 +// ShowWindow shows the main window func (a *App) ShowWindow() { if a.ctx != nil { runtime.WindowShow(a.ctx) @@ -583,64 +583,64 @@ func (a *App) ShowWindow() { } } -// HideWindow 隐藏主窗口到托盘 +// HideWindow hides the main window to tray func (a *App) HideWindow() { if a.ctx != nil { runtime.WindowHide(a.ctx) } } -// QuitApp 完全退出应用 +// QuitApp fully quits the application func (a *App) QuitApp() { - guiLogger.Info("GUI 请求退出") + guiLogger.Info("GUI requesting quit") - // 关闭 IPC 连接 + // Close IPC connection if a.ipcClient != nil { a.ipcClient.Close() } - // 退出 GUI + // Quit GUI if a.ctx != nil { runtime.Quit(a.ctx) } } -// QuitAll 完全退出应用(包括核心服务) +// QuitAll fully quits the application (including core service) func (a *App) QuitAll() { - guiLogger.Info("GUI 请求完全退出(包括核心服务)") + guiLogger.Info("GUI requesting full quit (including core service)") - // 通知核心服务退出 + // Notify core service to quit a.sendRequest(ipc.ReqQuitApp, nil) - // 关闭 IPC 连接 + // Close IPC connection if a.ipcClient != nil { a.ipcClient.Close() } - // 退出 GUI + // Quit GUI if a.ctx != nil { runtime.Quit(a.ctx) } } -// OnWindowClosing 窗口关闭事件处理 +// OnWindowClosing handles the window close event func (a *App) OnWindowClosing(ctx context.Context) bool { - // 返回 false 允许窗口正常关闭并退出 GUI - // 核心服务会继续在后台运行 + // Return false to allow normal window close and quit GUI + // Core service will continue running in the background return false } -// InitSystemTray 初始化系统托盘(保持API兼容,实际由核心服务处理) +// InitSystemTray initializes the system tray (kept for API compatibility, actually handled by core service) func (a *App) InitSystemTray() { - // 托盘由核心服务管理,GUI 不需要处理 + // Tray is managed by core service, GUI does not need to handle it } -// UpdateGuiResponseTime 更新GUI响应时间(供前端调用) +// UpdateGuiResponseTime updates the GUI response time (called by frontend) func (a *App) UpdateGuiResponseTime() { a.sendRequest(ipc.ReqUpdateGuiResponseTime, nil) } -// GetDebugInfo 获取调试信息 +// GetDebugInfo gets debug information func (a *App) GetDebugInfo() map[string]any { resp, err := a.sendRequest(ipc.ReqGetDebugInfo, nil) if err != nil { @@ -651,7 +651,7 @@ func (a *App) GetDebugInfo() map[string]any { return info } -// SetDebugMode 设置调试模式 +// SetDebugMode sets the debug mode func (a *App) SetDebugMode(enabled bool) error { resp, err := a.sendRequest(ipc.ReqSetDebugMode, ipc.SetBoolParams{Enabled: enabled}) if err != nil { diff --git a/bridge/README.md b/bridge/README.md index 6409924..31ab1c4 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -1,34 +1,34 @@ -# 温度桥接程序 +# Temperature Bridge Program -## 概述 +## Overview -由于Go语言无法直接调用C#库,我们创建了一个C#桥接程序 `TempBridge.exe`,通过 NuGet 引用 `LibreHardwareMonitorLib` 获取准确的CPU和GPU温度数据。 +Since Go cannot directly call C# libraries, we created a C# bridge program `TempBridge.exe` that uses `LibreHardwareMonitorLib` via NuGet to obtain accurate CPU and GPU temperature data. -当前桥接程序使用 `LibreHardwareMonitorLib >= 0.9.6`,该版本基于 `PawnIO` 能力,不再打包 `WinRing0` 资源。 +The current bridge program uses `LibreHardwareMonitorLib >= 0.9.6`, which is based on `PawnIO` capabilities and no longer bundles `WinRing0` resources. -## 构建说明 +## Build Instructions -### 前提条件 +### Prerequisites -- 安装 [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) -- 可访问 NuGet 源(`dotnet restore` 会自动拉取 `LibreHardwareMonitorLib`) +- Install [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) +- Access to NuGet sources (`dotnet restore` will automatically fetch `LibreHardwareMonitorLib`) -### Windows 构建 +### Windows Build ```bash -# 在项目根目录运行 +# Run from project root directory build_bridge.bat ``` -### Linux/Mac 构建(交叉编译) +### Linux/Mac Build (Cross-compilation) ```bash -# 在项目根目录运行 +# Run from project root directory chmod +x build_bridge.sh ./build_bridge.sh ``` -### 手动构建 +### Manual Build ```bash cd bridge/TempBridge @@ -36,34 +36,34 @@ dotnet restore dotnet publish TempBridge.csproj -c Release --self-contained false -o ../../build/bin/bridge ``` -## 工作原理 +## How It Works -1. Go程序调用 `TempBridge.exe` -2. 桥接程序通过 NuGet 引入的 `LibreHardwareMonitorLib` 读取硬件温度 -3. 桥接程序以JSON格式输出温度数据 -4. Go程序解析JSON数据并使用 +1. The Go program calls `TempBridge.exe` +2. The bridge program reads hardware temperatures via `LibreHardwareMonitorLib` from NuGet +3. The bridge program outputs temperature data in JSON format +4. The Go program parses and uses the JSON data -## 直接启动排查 +## Direct Launch Diagnostics -在命令行里直接运行 `TempBridge.exe` 时,程序会进入诊断模式,而不是命名管道模式: +When running `TempBridge.exe` directly from the command line, the program enters diagnostic mode instead of named pipe mode: ```bash cd bridge/TempBridge/bin/Release/net472 TempBridge.exe ``` -诊断模式会直接在控制台输出: +Diagnostic mode outputs directly to the console: -- CPU/GPU/MAX 温度 -- 当前是否读取成功 -- 错误信息 -- 已发现的温度传感器名称和读数 +- CPU/GPU/MAX temperatures +- Whether reading was successful +- Error messages +- Discovered temperature sensor names and readings -如果需要强制使用原有的管道模式,可传入 `--pipe` 参数。 +To force the original pipe mode, pass the `--pipe` parameter. -`--pipe` 模式现在会使用固定命名管道和全局单实例互斥;如果系统里已经有一个 TempBridge 正在监听,新进程不会再启动第二个监听实例,而是直接附着到现有实例。 +`--pipe` mode now uses a fixed named pipe and a global single-instance mutex; if a TempBridge instance is already listening on the system, a new process will not start a second listener but will attach to the existing instance. -## 输出格式 +## Output Format ```json { @@ -76,17 +76,17 @@ TempBridge.exe } ``` -## 错误处理 +## Error Handling -如果桥接程序不可用或失败,Go程序会自动回退到原有的温度读取方法: +If the bridge program is unavailable or fails, the Go program automatically falls back to alternative temperature reading methods: -1. 使用 `gopsutil` 读取传感器数据 -2. 通过WMI读取Windows系统温度 -3. 使用 `nvidia-smi` 读取NVIDIA GPU温度 +1. Reading sensor data using `gopsutil` +2. Reading Windows system temperature via WMI +3. Reading NVIDIA GPU temperature using `nvidia-smi` -## 注意事项 +## Notes -- 桥接程序需要以管理员权限运行才能访问所有硬件传感器 -- 首次运行可能需要一些时间来初始化硬件监控 -- 如果遇到权限问题,请尝试以管理员身份运行主程序 -- 运行前请确保系统已安装 `PawnIO`(未安装时 `TempBridge` 会在启动阶段直接报错并退出) +- The bridge program requires administrator privileges to access all hardware sensors +- The first run may take some time to initialize hardware monitoring +- If you encounter permission issues, try running the main program as administrator +- Ensure `PawnIO` is installed on the system before running (TempBridge will report an error and exit at startup if not installed) diff --git a/bridge/TempBridge/Program.cs b/bridge/TempBridge/Program.cs index 562f76f..b28dd7a 100644 --- a/bridge/TempBridge/Program.cs +++ b/bridge/TempBridge/Program.cs @@ -74,7 +74,7 @@ static void Main(string[] args) return; } - // 初始化硬件监控 + // Initialize hardware monitoring using (var instanceHandle = AcquirePipeInstance()) { if (instanceHandle == null) @@ -86,11 +86,11 @@ static void Main(string[] args) InitializeHardwareMonitor(); - // 输出管道名称,让主程序知道如何连接 + // Output pipe name so the main program knows how to connect Console.WriteLine($"PIPE:{PipeName}|OWNER"); Console.Out.Flush(); - // 启动管道服务器 + // Start pipe server StartPipeServer(); } } @@ -98,8 +98,8 @@ static void Main(string[] args) { if (ShouldRunDiagnosticMode(args)) { - Console.Error.WriteLine("TempBridge 启动失败"); - Console.Error.WriteLine($"错误: {ex.Message}"); + Console.Error.WriteLine("TempBridge failed to start"); + Console.Error.WriteLine($"Error: {ex.Message}"); } else { @@ -168,8 +168,8 @@ static bool HasArg(string[] args, string expected) static void RunConsoleDiagnostics() { - Console.WriteLine("TempBridge 诊断模式"); - Console.WriteLine($"时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); + Console.WriteLine("TempBridge Diagnostic Mode"); + Console.WriteLine($"Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); Console.WriteLine(); InitializeHardwareMonitor(); @@ -187,7 +187,7 @@ static void RunConsoleDiagnostics() static void PrintTemperatureSummary(TemperatureData data) { - Console.WriteLine("温度结果"); + Console.WriteLine("Temperature Results"); Console.WriteLine($"CPU: {FormatTemperature(data.CpuTemp)}"); Console.WriteLine($"GPU: {FormatTemperature(data.GpuTemp)}"); Console.WriteLine($"MAX: {FormatTemperature(data.MaxTemp)}"); @@ -206,7 +206,7 @@ static string FormatTemperature(int value) static void PrintHardwareSnapshot() { - Console.WriteLine("温度传感器快照"); + Console.WriteLine("Temperature Sensor Snapshot"); bool foundAny = false; foreach (IHardware hardware in computer.Hardware) @@ -216,7 +216,7 @@ static void PrintHardwareSnapshot() if (!foundAny) { - Console.WriteLine("- 未发现可用的温度传感器"); + Console.WriteLine("- No temperature sensors found"); } } @@ -283,9 +283,9 @@ static void EnsurePawnIoInstalled() if (!PawnIo.IsInstalled) { throw new InvalidOperationException( - "检测到 LibreHardwareMonitor 需要 PawnIO 驱动,但系统未安装。" + - "请先安装 PawnIO(可从 LibreHardwareMonitor 发布包中的 PawnIO_setup.exe 安装)," + - "安装完成后重启程序。" + "LibreHardwareMonitor requires the PawnIO driver, but it is not installed. " + + "Please install PawnIO first (available from PawnIO_setup.exe in the LibreHardwareMonitor release package), " + + "then restart the program." ); } } @@ -298,7 +298,7 @@ static void StartPipeServer() { using (var pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.InOut)) { - // 等待客户端连接 + // Wait for client connection pipeServer.WaitForConnection(); using (var reader = new StreamReader(pipeServer)) @@ -345,8 +345,8 @@ static void StartPipeServer() { if (running) { - Console.WriteLine($"管道错误: {ex.Message}"); - Thread.Sleep(1000); // 等待一秒后重试 + Console.WriteLine($"Pipe error: {ex.Message}"); + Thread.Sleep(1000); // Wait one second before retrying } } } @@ -382,7 +382,7 @@ static Response ProcessCommand(Command command) return new Response { Success = false, - Error = "未知命令类型" + Error = "Unknown command type" }; } } @@ -444,7 +444,7 @@ static TemperatureData GetTemperatureData() if (cpuTemp == 0 && gpuTemp == 0) { result.Success = false; - result.Error = "未读取到有效的 CPU/GPU 温度(PawnIO 可能尚未就绪,请重启软件或重新安装驱动)"; + result.Error = "No valid CPU/GPU temperature readings (PawnIO may not be ready yet, please restart the software or reinstall the driver)"; } else { diff --git a/build/windows/installer/project.nsi b/build/windows/installer/project.nsi index 52afd61..64c65f9 100644 --- a/build/windows/installer/project.nsi +++ b/build/windows/installer/project.nsi @@ -70,7 +70,7 @@ ManifestDPIAware true # !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314 !define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps !define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_EXECUTABLE}" -!define MUI_FINISHPAGE_RUN_TEXT "安装完成后立即启动 BS2PRO 控制器" +!define MUI_FINISHPAGE_RUN_TEXT "Launch BS2PRO Controller after installation" !define MUI_ABORTWARNING # This will warn the user if they exit from the installer. !insertmacro MUI_PAGE_WELCOME # Welcome to the installer page. @@ -82,14 +82,14 @@ ManifestDPIAware true !insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page -!insertmacro MUI_LANGUAGE "SimpChinese" # Set the Language of the installer +!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer ## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1 #!uninstfinalize 'signtool --file "%1"' #!finalize 'signtool --file "%1"' Name "${INFO_PRODUCTNAME}" -Caption "${INFO_PRODUCTNAME} 安装程序 v${INFO_PRODUCTVERSION}" +Caption "${INFO_PRODUCTNAME} Installer v${INFO_PRODUCTVERSION}" BrandingText "${INFO_PRODUCTNAME} v${INFO_PRODUCTVERSION}" OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file. InstallDir "$PROGRAMFILES64\${INFO_PRODUCTNAME}" # Default installing folder (single level) @@ -102,7 +102,7 @@ Function .onInit !insertmacro CheckNetFramework 472 Pop $0 ${If} $0 == "false" - MessageBox MB_OK|MB_ICONSTOP "需要 .NET Framework 4.7.2 或更高版本。$\n$\n请先安装 .NET Framework 4.7.2。" + MessageBox MB_OK|MB_ICONSTOP ".NET Framework 4.7.2 or later is required.$\n$\nPlease install .NET Framework 4.7.2 first." Abort ${EndIf} @@ -115,7 +115,7 @@ FunctionEnd # Function to clean up legacy/duplicate registry keys Function CleanLegacyRegistryKeys - DetailPrint "正在清理历史注册表项..." + DetailPrint "Cleaning up legacy registry keys..." SetRegView 64 # List of known legacy/duplicate registry key names @@ -129,25 +129,25 @@ Function CleanLegacyRegistryKeys # Check and remove BS2PRO-controllerBS2PRO-controller ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BS2PRO-controllerBS2PRO-controller" "UninstallString" ${If} $R0 != "" - DetailPrint "发现重复注册表键: BS2PRO-controllerBS2PRO-controller" + DetailPrint "Found duplicate registry key: BS2PRO-controllerBS2PRO-controller" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BS2PRO-controllerBS2PRO-controller" - DetailPrint "已删除重复注册表键" + DetailPrint "Deleted duplicate registry key" ${EndIf} # Check and remove TIANLI0BS2PRO-Controller ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\TIANLI0BS2PRO-Controller" "UninstallString" ${If} $R0 != "" - DetailPrint "发现旧版注册表键: TIANLI0BS2PRO-Controller" + DetailPrint "Found legacy registry key: TIANLI0BS2PRO-Controller" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\TIANLI0BS2PRO-Controller" - DetailPrint "已删除旧版注册表键" + DetailPrint "Deleted legacy registry key" ${EndIf} # Check and remove TIANLI0BS2PRO (current wails.json would generate this) ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\TIANLI0BS2PRO" "UninstallString" ${If} $R0 != "" - DetailPrint "发现重复注册表键: TIANLI0BS2PRO" + DetailPrint "Found duplicate registry key: TIANLI0BS2PRO" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\TIANLI0BS2PRO" - DetailPrint "已删除重复注册表键" + DetailPrint "Deleted duplicate registry key" ${EndIf} Pop $R1 @@ -156,7 +156,7 @@ FunctionEnd # Function to detect existing installation and set install directory Function DetectExistingInstallation - DetailPrint "正在检查已有安装..." + DetailPrint "Checking for existing installation..." SetRegView 64 Push $R0 @@ -175,9 +175,9 @@ Function DetectExistingInstallation ReadRegStr $R2 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\TIANLI0BS2PRO" "DisplayVersion" ${EndIf} ${If} $R2 != "" - DetailPrint "本地已安装版本: $R2" + DetailPrint "Locally installed version: $R2" ${Else} - DetailPrint "本地未检测到已安装版本信息" + DetailPrint "No locally installed version detected" ${EndIf} # First, check all possible registry keys to find installation path @@ -188,12 +188,12 @@ Function DetectExistingInstallation ${If} $R0 != "" ${If} ${FileExists} "$R0\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R0 - DetailPrint "发现已有安装 (正确键-安装位置): $INSTDIR" + DetailPrint "Found existing installation (correct key - install location): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R0\BS2PRO-Core.exe" StrCpy $INSTDIR $R0 - DetailPrint "发现已有安装 (正确键-安装位置-Core): $INSTDIR" + DetailPrint "Found existing installation (correct key - install location - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -206,12 +206,12 @@ Function DetectExistingInstallation ${GetParent} $R0 $R1 ${If} ${FileExists} "$R1\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R1 - DetailPrint "发现已有安装 (从正确的注册表键): $INSTDIR" + DetailPrint "Found existing installation (from correct registry key): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R1\BS2PRO-Core.exe" StrCpy $INSTDIR $R1 - DetailPrint "发现已有安装 (从正确的注册表键-Core): $INSTDIR" + DetailPrint "Found existing installation (from correct registry key - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -222,12 +222,12 @@ Function DetectExistingInstallation ${If} $R0 != "" ${If} ${FileExists} "$R0\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R0 - DetailPrint "发现旧版安装 (重复键-安装位置): $INSTDIR" + DetailPrint "Found legacy installation (duplicate key - install location): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R0\BS2PRO-Core.exe" StrCpy $INSTDIR $R0 - DetailPrint "发现旧版安装 (重复键-安装位置-Core): $INSTDIR" + DetailPrint "Found legacy installation (duplicate key - install location - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -240,12 +240,12 @@ Function DetectExistingInstallation ${GetParent} $R0 $R1 ${If} ${FileExists} "$R1\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (重复键): $INSTDIR" + DetailPrint "Found legacy installation (duplicate key): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R1\BS2PRO-Core.exe" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (重复键-Core): $INSTDIR" + DetailPrint "Found legacy installation (duplicate key - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -259,12 +259,12 @@ Function DetectExistingInstallation ${GetParent} $R0 $R1 ${If} ${FileExists} "$R1\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (从图标路径): $INSTDIR" + DetailPrint "Found legacy installation (from icon path): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R1\BS2PRO-Core.exe" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (从图标路径-Core): $INSTDIR" + DetailPrint "Found legacy installation (from icon path - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -274,12 +274,12 @@ Function DetectExistingInstallation ${If} $R0 != "" ${If} ${FileExists} "$R0\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R0 - DetailPrint "发现旧版安装 (旧格式键-安装位置): $INSTDIR" + DetailPrint "Found legacy installation (old format key - install location): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R0\BS2PRO-Core.exe" StrCpy $INSTDIR $R0 - DetailPrint "发现旧版安装 (旧格式键-安装位置-Core): $INSTDIR" + DetailPrint "Found legacy installation (old format key - install location - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -292,12 +292,12 @@ Function DetectExistingInstallation ${GetParent} $R0 $R1 ${If} ${FileExists} "$R1\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (旧格式键): $INSTDIR" + DetailPrint "Found legacy installation (old format key): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R1\BS2PRO-Core.exe" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (旧格式键-Core): $INSTDIR" + DetailPrint "Found legacy installation (old format key - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -307,12 +307,12 @@ Function DetectExistingInstallation ${If} $R0 != "" ${If} ${FileExists} "$R0\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R0 - DetailPrint "发现旧版安装 (TIANLI0BS2PRO-安装位置): $INSTDIR" + DetailPrint "Found legacy installation (TIANLI0BS2PRO - install location): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R0\BS2PRO-Core.exe" StrCpy $INSTDIR $R0 - DetailPrint "发现旧版安装 (TIANLI0BS2PRO-安装位置-Core): $INSTDIR" + DetailPrint "Found legacy installation (TIANLI0BS2PRO - install location - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -325,12 +325,12 @@ Function DetectExistingInstallation ${GetParent} $R0 $R1 ${If} ${FileExists} "$R1\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (TIANLI0BS2PRO): $INSTDIR" + DetailPrint "Found legacy installation (TIANLI0BS2PRO): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R1\BS2PRO-Core.exe" StrCpy $INSTDIR $R1 - DetailPrint "发现旧版安装 (TIANLI0BS2PRO-Core): $INSTDIR" + DetailPrint "Found legacy installation (TIANLI0BS2PRO - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -346,12 +346,12 @@ Function DetectExistingInstallation ${GetParent} $R0 $R1 # Get parent directory ${If} ${FileExists} "$R1\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R1 - DetailPrint "发现已有安装 (从图标): $INSTDIR" + DetailPrint "Found existing installation (from icon): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R1\BS2PRO-Core.exe" StrCpy $INSTDIR $R1 - DetailPrint "发现已有安装 (从图标-Core): $INSTDIR" + DetailPrint "Found existing installation (from icon - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -361,12 +361,12 @@ Function DetectExistingInstallation ${If} $R0 != "" ${If} ${FileExists} "$R0\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR $R0 - DetailPrint "发现已有安装 (从安装位置): $INSTDIR" + DetailPrint "Found existing installation (from install location): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$R0\BS2PRO-Core.exe" StrCpy $INSTDIR $R0 - DetailPrint "发现已有安装 (从安装位置-Core): $INSTDIR" + DetailPrint "Found existing installation (from install location - Core): $INSTDIR" Goto found_installation ${EndIf} ${EndIf} @@ -374,57 +374,57 @@ Function DetectExistingInstallation # Fourth, check common installation locations (single level path) ${If} ${FileExists} "$PROGRAMFILES64\${INFO_PRODUCTNAME}\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR "$PROGRAMFILES64\${INFO_PRODUCTNAME}" - DetailPrint "发现已有安装: $INSTDIR" + DetailPrint "Found existing installation: $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$PROGRAMFILES32\${INFO_PRODUCTNAME}\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR "$PROGRAMFILES32\${INFO_PRODUCTNAME}" - DetailPrint "发现已有安装: $INSTDIR" + DetailPrint "Found existing installation: $INSTDIR" Goto found_installation ${EndIf} # Fifth, check legacy paths with Company\Product structure ${If} ${FileExists} "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" - DetailPrint "发现已有安装 (旧版路径): $INSTDIR" + DetailPrint "Found existing installation (legacy path): $INSTDIR" Goto found_installation ${EndIf} # Sixth, try alternative common paths ${If} ${FileExists} "$PROGRAMFILES64\BS2PRO-Controller\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR "$PROGRAMFILES64\BS2PRO-Controller" - DetailPrint "发现已有安装: $INSTDIR" + DetailPrint "Found existing installation: $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$PROGRAMFILES32\BS2PRO-Controller\${PRODUCT_EXECUTABLE}" StrCpy $INSTDIR "$PROGRAMFILES32\BS2PRO-Controller" - DetailPrint "发现已有安装: $INSTDIR" + DetailPrint "Found existing installation: $INSTDIR" Goto found_installation ${EndIf} # Seventh, check for BS2PRO-Core.exe in common paths ${If} ${FileExists} "$PROGRAMFILES64\${INFO_PRODUCTNAME}\BS2PRO-Core.exe" StrCpy $INSTDIR "$PROGRAMFILES64\${INFO_PRODUCTNAME}" - DetailPrint "发现已有安装 (Core): $INSTDIR" + DetailPrint "Found existing installation (Core): $INSTDIR" Goto found_installation ${EndIf} ${If} ${FileExists} "$PROGRAMFILES64\BS2PRO-Controller\BS2PRO-Core.exe" StrCpy $INSTDIR "$PROGRAMFILES64\BS2PRO-Controller" - DetailPrint "发现已有安装 (Core): $INSTDIR" + DetailPrint "Found existing installation (Core): $INSTDIR" Goto found_installation ${EndIf} # If no existing installation found, use simple product name for directory # Use BS2PRO-Controller instead of ${INFO_PRODUCTNAME} to ensure consistency StrCpy $INSTDIR "$PROGRAMFILES64\BS2PRO-Controller" - DetailPrint "未发现已有安装,使用默认目录: $INSTDIR" + DetailPrint "No existing installation found, using default directory: $INSTDIR" Goto end_detection found_installation: - DetailPrint "检测到已有安装 - 将执行升级到: $INSTDIR" + DetailPrint "Existing installation detected - will upgrade to: $INSTDIR" # Now clean up legacy registry keys AFTER we've found the install path Call CleanLegacyRegistryKeys @@ -442,7 +442,7 @@ Function WriteCurrentVersionInfo WriteRegStr HKLM "${UNINST_KEY}" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" - DetailPrint "已写入版本信息: ${INFO_PRODUCTVERSION}" + DetailPrint "Version info written: ${INFO_PRODUCTVERSION}" FunctionEnd # Helper function to trim quotes from a string @@ -468,7 +468,7 @@ FunctionEnd # Function to stop running instances and services Function StopRunningInstances - DetailPrint "正在检查运行中的进程..." + DetailPrint "Checking for running processes..." # Try to stop the core service first (it manages the fan control) # Use /FI with proper error handling @@ -477,7 +477,7 @@ Function StopRunningInstances Pop $0 Pop $1 ${If} $0 == 0 - DetailPrint "已请求关闭 BS2PRO-Core.exe..." + DetailPrint "Requested shutdown of BS2PRO-Core.exe..." Sleep 2000 ${EndIf} @@ -492,7 +492,7 @@ Function StopRunningInstances Pop $0 Pop $1 ${If} $0 == 0 - DetailPrint "已请求关闭 SpaceStationService.exe..." + DetailPrint "Requested shutdown of SpaceStationService.exe..." Sleep 1000 ${EndIf} @@ -507,7 +507,7 @@ Function StopRunningInstances Pop $0 Pop $1 ${If} $0 == 0 - DetailPrint "已请求关闭 ${PRODUCT_EXECUTABLE}..." + DetailPrint "Requested shutdown of ${PRODUCT_EXECUTABLE}..." Sleep 2000 ${EndIf} @@ -536,7 +536,7 @@ Function StopRunningInstances Call StopBridgeDriver # Remove scheduled task if exists (ignore errors) - DetailPrint "正在清理计划任务..." + DetailPrint "Cleaning up scheduled tasks..." nsExec::ExecToStack '"$SYSDIR\schtasks.exe" /delete /tn "BS2PRO-Controller" /f' Pop $0 Pop $1 @@ -545,15 +545,15 @@ Function StopRunningInstances Pop $1 # Wait a moment for processes to fully terminate - DetailPrint "等待进程完全终止..." + DetailPrint "Waiting for processes to fully terminate..." Sleep 2000 - DetailPrint "进程清理完成" + DetailPrint "Process cleanup completed" FunctionEnd # Function to stop and remove TempBridge kernel driver service Function StopBridgeDriver - DetailPrint "正在停止驱动服务 R0TempBridge..." + DetailPrint "Stopping driver service R0TempBridge..." # Stop running driver service (ignore failures if service does not exist) nsExec::ExecToStack '"$SYSDIR\sc.exe" stop "R0TempBridge"' @@ -612,7 +612,7 @@ FunctionEnd # Uninstall-side function (NSIS requires un.* functions in uninstall section) Function un.StopBridgeDriver - DetailPrint "正在停止驱动服务 R0TempBridge..." + DetailPrint "Stopping driver service R0TempBridge..." nsExec::ExecToStack '"$SYSDIR\sc.exe" stop "R0TempBridge"' Pop $0 @@ -666,41 +666,41 @@ FunctionEnd # Function to backup user data before upgrade Function BackupUserData - DetailPrint "正在备份用户配置..." + DetailPrint "Backing up user configuration..." # Backup configuration files if they exist ${If} ${FileExists} "$INSTDIR\config.json" CopyFiles "$INSTDIR\config.json" "$TEMP\bs2pro_config_backup.json" - DetailPrint "配置文件已备份" + DetailPrint "Configuration file backed up" ${EndIf} # Backup other important user files if needed ${If} ${FileExists} "$INSTDIR\settings.ini" CopyFiles "$INSTDIR\settings.ini" "$TEMP\bs2pro_settings_backup.ini" - DetailPrint "设置文件已备份" + DetailPrint "Settings file backed up" ${EndIf} FunctionEnd # Function to restore user data after upgrade Function RestoreUserData - DetailPrint "正在恢复用户配置..." + DetailPrint "Restoring user configuration..." # Restore configuration files if backup exists ${If} ${FileExists} "$TEMP\bs2pro_config_backup.json" CopyFiles "$TEMP\bs2pro_config_backup.json" "$INSTDIR\config.json" - DetailPrint "配置文件已恢复" + DetailPrint "Configuration file restored" ${EndIf} ${If} ${FileExists} "$TEMP\bs2pro_settings_backup.ini" CopyFiles "$TEMP\bs2pro_settings_backup.ini" "$INSTDIR\settings.ini" Delete "$TEMP\bs2pro_settings_backup.ini" # Clean up backup - DetailPrint "设置文件已恢复" + DetailPrint "Settings file restored" ${EndIf} FunctionEnd # Uninstall currently installed PawnIO via bundled installer Function UninstallPawnIO - DetailPrint "正在卸载已安装的 PawnIO..." + DetailPrint "Uninstalling existing PawnIO..." ${If} ${FileExists} "$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" nsExec::ExecToStack /TIMEOUT=60000 '"$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" -uninstall -silent' @@ -708,39 +708,39 @@ Function UninstallPawnIO Pop $1 ${If} $0 == "timeout" - DetailPrint "PawnIO 静默卸载 60 秒未响应,回退到交互卸载..." + DetailPrint "PawnIO silent uninstall timed out after 60 seconds, falling back to interactive uninstall..." nsExec::ExecToStack '"$SYSDIR\taskkill.exe" /F /IM "PawnIO_setup.exe" /T' Pop $2 Pop $3 ExecWait '"$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" -uninstall' $0 ${If} $0 != 0 - DetailPrint "PawnIO 交互卸载返回码: $0" + DetailPrint "PawnIO interactive uninstall return code: $0" ${EndIf} ${ElseIf} $0 == 0 - DetailPrint "PawnIO 卸载完成(静默)" + DetailPrint "PawnIO uninstall completed (silent)" ${Else} - DetailPrint "PawnIO 静默卸载失败,回退到交互卸载..." + DetailPrint "PawnIO silent uninstall failed, falling back to interactive uninstall..." ExecWait '"$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" -uninstall' $0 ${If} $0 != 0 - DetailPrint "PawnIO 交互卸载返回码: $0" + DetailPrint "PawnIO interactive uninstall return code: $0" ${EndIf} ${EndIf} ${Else} - DetailPrint "未找到 PawnIO_setup.exe,跳过卸载程序调用" + DetailPrint "PawnIO_setup.exe not found, skipping uninstall call" ${EndIf} - # 卸载后再做一次驱动服务清理,避免升级残留 + # Clean up driver services after uninstall to avoid upgrade leftovers Call StopBridgeDriver Sleep 1000 FunctionEnd -Section "主程序 (必需)" SEC_MAIN +Section "Main Program (Required)" SEC_MAIN SectionIn RO # Read-only, cannot be deselected !insertmacro wails.setShellContext # Check if this is an upgrade installation ${If} ${FileExists} "$INSTDIR\${PRODUCT_EXECUTABLE}" - DetailPrint "正在升级: $INSTDIR" + DetailPrint "Upgrading: $INSTDIR" # Backup important files before upgrade Call BackupUserData @@ -749,13 +749,13 @@ Section "主程序 (必需)" SEC_MAIN Call StopRunningInstances # Clean up old files but preserve user data - DetailPrint "正在清理旧版本文件..." + DetailPrint "Cleaning up old version files..." Delete "$INSTDIR\${PRODUCT_EXECUTABLE}" Delete "$INSTDIR\BS2PRO-Core.exe" RMDir /r "$INSTDIR\bridge" Delete "$INSTDIR\logs\*.log" # Keep log structure but remove old logs ${ElseIf} ${FileExists} "$INSTDIR\BS2PRO-Core.exe" - DetailPrint "正在升级 (发现Core): $INSTDIR" + DetailPrint "Upgrading (found Core): $INSTDIR" # Backup important files before upgrade Call BackupUserData @@ -764,19 +764,19 @@ Section "主程序 (必需)" SEC_MAIN Call StopRunningInstances # Clean up old files but preserve user data - DetailPrint "正在清理旧版本文件..." + DetailPrint "Cleaning up old version files..." Delete "$INSTDIR\${PRODUCT_EXECUTABLE}" Delete "$INSTDIR\BS2PRO-Core.exe" RMDir /r "$INSTDIR\bridge" Delete "$INSTDIR\logs\*.log" ${Else} - DetailPrint "全新安装: $INSTDIR" + DetailPrint "Fresh installation: $INSTDIR" # Ensure old instances are completely stopped before installing Call StopRunningInstances # Clean up any leftover files from previous installation - DetailPrint "正在清理残留文件..." + DetailPrint "Cleaning up leftover files..." RMDir /r "$INSTDIR\bridge" Delete "$INSTDIR\logs\*.*" ${EndIf} @@ -788,11 +788,11 @@ Section "主程序 (必需)" SEC_MAIN !insertmacro wails.files # Copy core service executable - DetailPrint "正在安装核心服务..." + DetailPrint "Installing core service..." File "..\..\bin\BS2PRO-Core.exe" # Copy bridge directory and its contents - DetailPrint "正在安装桥接组件..." + DetailPrint "Installing bridge components..." SetOutPath $INSTDIR\bridge File /r "..\..\bin\bridge\*.*" @@ -803,7 +803,7 @@ Section "主程序 (必需)" SEC_MAIN Call RestoreUserData # Create shortcuts - DetailPrint "正在创建快捷方式..." + DetailPrint "Creating shortcuts..." CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" @@ -813,23 +813,23 @@ Section "主程序 (必需)" SEC_MAIN !insertmacro wails.writeUninstaller Call WriteCurrentVersionInfo - DetailPrint "安装完成" + DetailPrint "Installation completed" # Show completion message ${If} ${FileExists} "$TEMP\bs2pro_config_backup.json" - MessageBox MB_OK|MB_ICONINFORMATION "BS2PRO 控制器升级成功!$\n$\n您的设置已保留。$\n$\n注意:完整功能需要管理员权限。" + MessageBox MB_OK|MB_ICONINFORMATION "BS2PRO Controller upgraded successfully!$\n$\nYour settings have been preserved.$\n$\nNote: Full functionality requires administrator privileges." Delete "$TEMP\bs2pro_config_backup.json" # Clean up backup ${Else} - MessageBox MB_OK|MB_ICONINFORMATION "BS2PRO 控制器安装成功!$\n$\n注意:完整功能需要管理员权限。" + MessageBox MB_OK|MB_ICONINFORMATION "BS2PRO Controller installed successfully!$\n$\nNote: Full functionality requires administrator privileges." ${EndIf} SectionEnd # Auto-start section (selected by default) -Section "开机自启动" SEC_AUTOSTART - DetailPrint "正在配置开机自启动..." +Section "Auto-start on boot" SEC_AUTOSTART + DetailPrint "Configuring auto-start on boot..." # First, remove any existing auto-start entries to ensure clean state - DetailPrint "正在清理现有自启动项..." + DetailPrint "Cleaning up existing auto-start entries..." nsExec::ExecToStack '"$SYSDIR\schtasks.exe" /delete /tn "BS2PRO-Controller" /f' Pop $0 Pop $1 @@ -842,7 +842,7 @@ Section "开机自启动" SEC_AUTOSTART DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "BS2PRO-Core" # Create new scheduled task for auto-start with admin privileges - DetailPrint "正在创建自启动计划任务..." + DetailPrint "Creating auto-start scheduled task..." # Use schtasks to create a task that runs at logon with highest privileges # The task will start BS2PRO-Core.exe with --autostart flag after 15 seconds delay @@ -850,19 +850,19 @@ Section "开机自启动" SEC_AUTOSTART Pop $0 Pop $1 ${If} $0 == 0 - DetailPrint "开机自启动配置成功(计划任务)" + DetailPrint "Auto-start configured successfully (scheduled task)" ${Else} - DetailPrint "计划任务创建失败,使用注册表方式..." + DetailPrint "Scheduled task creation failed, using registry method..." # Fallback: use registry auto-start (will trigger UAC on each login) WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "BS2PRO-Controller" '"$INSTDIR\BS2PRO-Core.exe" --autostart' - DetailPrint "开机自启动配置成功(注册表)" + DetailPrint "Auto-start configured successfully (registry)" ${EndIf} SectionEnd # Required PawnIO installer section -Section "安装 PawnIO (必需)" SEC_PAWNIO +Section "Install PawnIO (Required)" SEC_PAWNIO SectionIn RO - DetailPrint "正在准备安装 PawnIO..." + DetailPrint "Preparing to install PawnIO..." Push $6 Push $7 Push $8 @@ -871,7 +871,7 @@ Section "安装 PawnIO (必需)" SEC_PAWNIO SetOutPath "$TEMP\BS2PRO-PawnIO" File /nonfatal "..\..\bin\PawnIO_setup.exe" ${IfNot} ${FileExists} "$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" - MessageBox MB_OK|MB_ICONSTOP "未找到 PawnIO_setup.exe(build\\bin)。请先执行 build_bridge.bat 下载后再打包安装器。" + MessageBox MB_OK|MB_ICONSTOP "PawnIO_setup.exe not found (build\\bin). Please run build_bridge.bat to download it before building the installer." Abort ${EndIf} @@ -890,11 +890,11 @@ Section "安装 PawnIO (必需)" SEC_PAWNIO StrCpy $9 "1" ${If} $6 != "" - DetailPrint "检测到已安装 PawnIO (版本: $6),内置版本: ${PAWNIO_BUNDLED_VERSION}" + DetailPrint "Detected installed PawnIO (version: $6), bundled version: ${PAWNIO_BUNDLED_VERSION}" ${VersionCompare} "$6" "${PAWNIO_BUNDLED_VERSION}" $8 ${If} $8 == 2 - MessageBox MB_YESNO|MB_ICONQUESTION "检测到 PawnIO 旧版本:$6。$\n安装包内置版本:${PAWNIO_BUNDLED_VERSION}。$\n$\n是否先卸载旧版本再安装新版本?" IDYES pawnio_upgrade IDNO pawnio_skip + MessageBox MB_YESNO|MB_ICONQUESTION "Detected older PawnIO version: $6.$\nBundled version: ${PAWNIO_BUNDLED_VERSION}.$\n$\nUninstall the old version and install the new one?" IDYES pawnio_upgrade IDNO pawnio_skip pawnio_upgrade: StrCpy $9 "2" Goto pawnio_apply @@ -902,7 +902,7 @@ Section "安装 PawnIO (必需)" SEC_PAWNIO StrCpy $9 "0" Goto pawnio_apply ${Else} - MessageBox MB_YESNO|MB_ICONQUESTION "检测到 PawnIO 已安装(版本:$6)。$\n$\n是否执行 PawnIO 修复安装(卸载后重装)?" IDYES pawnio_repair IDNO pawnio_skip2 + MessageBox MB_YESNO|MB_ICONQUESTION "PawnIO is already installed (version: $6).$\n$\nPerform PawnIO repair installation (uninstall then reinstall)?" IDYES pawnio_repair IDNO pawnio_skip2 pawnio_repair: StrCpy $9 "2" Goto pawnio_apply @@ -914,7 +914,7 @@ Section "安装 PawnIO (必需)" SEC_PAWNIO pawnio_apply: ${If} $9 == "0" - DetailPrint "用户选择跳过 PawnIO 处理。" + DetailPrint "User chose to skip PawnIO processing." Goto pawnio_done ${EndIf} @@ -923,7 +923,7 @@ Section "安装 PawnIO (必需)" SEC_PAWNIO ${EndIf} # Pre-clean possible stale driver service state (avoids driver install error 1072) - DetailPrint "正在清理旧的 PawnIO 驱动服务..." + DetailPrint "Cleaning up old PawnIO driver services..." nsExec::ExecToStack '"$SYSDIR\sc.exe" stop "PawnIO"' Pop $4 Pop $5 @@ -938,31 +938,31 @@ Section "安装 PawnIO (必需)" SEC_PAWNIO Pop $5 Sleep 1200 - DetailPrint "正在静默安装 PawnIO(最多等待 60 秒)..." + DetailPrint "Installing PawnIO silently (up to 60 seconds)..." nsExec::ExecToStack /TIMEOUT=60000 '"$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" -install -silent' Pop $0 Pop $1 ${If} $0 == "timeout" - DetailPrint "PawnIO 静默安装 60 秒未响应,回退到交互安装..." + DetailPrint "PawnIO silent install timed out after 60 seconds, falling back to interactive install..." nsExec::ExecToStack '"$SYSDIR\taskkill.exe" /F /IM "PawnIO_setup.exe" /T' Pop $2 Pop $3 ExecWait '"$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" -install' $0 ${If} $0 == 0 - DetailPrint "PawnIO 安装完成(交互)" + DetailPrint "PawnIO installation completed (interactive)" ${Else} - MessageBox MB_OK|MB_ICONSTOP "PawnIO 交互安装失败(返回码: $0)。$\n$\n常见原因:驱动服务被系统标记删除(错误 1072)。$\n请先重启系统后重新运行安装程序。" + MessageBox MB_OK|MB_ICONSTOP "PawnIO interactive install failed (return code: $0).$\n$\nCommon cause: driver service marked for deletion by system (error 1072).$\nPlease restart the system and run the installer again." Abort ${EndIf} ${ElseIf} $0 == 0 - DetailPrint "PawnIO 安装完成(静默)" + DetailPrint "PawnIO installation completed (silent)" ${Else} - DetailPrint "PawnIO 静默安装失败,改为交互安装..." + DetailPrint "PawnIO silent install failed, switching to interactive install..." ExecWait '"$TEMP\BS2PRO-PawnIO\PawnIO_setup.exe" -install' $0 ${If} $0 == 0 - DetailPrint "PawnIO 安装完成(交互)" + DetailPrint "PawnIO installation completed (interactive)" ${Else} - MessageBox MB_OK|MB_ICONSTOP "PawnIO 安装失败(返回码: $0)。$\n$\n常见原因:驱动服务被系统标记删除(错误 1072)。$\n请先重启系统后重新运行安装程序。" + MessageBox MB_OK|MB_ICONSTOP "PawnIO installation failed (return code: $0).$\n$\nCommon cause: driver service marked for deletion by system (error 1072).$\nPlease restart the system and run the installer again." Abort ${EndIf} ${EndIf} @@ -976,19 +976,19 @@ SectionEnd # Section descriptions !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${SEC_MAIN} "BS2PRO 控制器主程序和核心服务文件。" - !insertmacro MUI_DESCRIPTION_TEXT ${SEC_AUTOSTART} "系统启动时自动运行 BS2PRO 控制器核心服务。推荐开启。" - !insertmacro MUI_DESCRIPTION_TEXT ${SEC_PAWNIO} "安装 PawnIO 驱动,PawnIO将用于获取硬件相关信息。" + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_MAIN} "BS2PRO Controller main program and core service files." + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_AUTOSTART} "Automatically run BS2PRO Controller core service at system startup. Recommended." + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_PAWNIO} "Install PawnIO driver, used for obtaining hardware information." !insertmacro MUI_FUNCTION_DESCRIPTION_END Section "uninstall" !insertmacro wails.setShellContext # Stop running instances before uninstalling - DetailPrint "正在停止运行中的进程..." + DetailPrint "Stopping running processes..." # Stop core service first (ignore errors) - DetailPrint "正在停止 BS2PRO-Core.exe..." + DetailPrint "Stopping BS2PRO-Core.exe..." nsExec::ExecToStack '"$SYSDIR\taskkill.exe" /IM "BS2PRO-Core.exe" /T' Pop $0 Pop $1 @@ -998,7 +998,7 @@ Section "uninstall" Pop $1 # Stop main application (ignore errors) - DetailPrint "正在停止 ${PRODUCT_EXECUTABLE}..." + DetailPrint "Stopping ${PRODUCT_EXECUTABLE}..." nsExec::ExecToStack '"$SYSDIR\taskkill.exe" /IM "${PRODUCT_EXECUTABLE}" /T' Pop $0 Pop $1 @@ -1019,7 +1019,7 @@ Section "uninstall" Pop $1 # Stop bridge processes (ignore errors) - DetailPrint "正在停止 TempBridge.exe..." + DetailPrint "Stopping TempBridge.exe..." nsExec::ExecToStack '"$SYSDIR\taskkill.exe" /IM "TempBridge.exe" /T' Pop $0 Pop $1 @@ -1032,7 +1032,7 @@ Section "uninstall" Call un.StopBridgeDriver # Remove auto-start entries - DetailPrint "正在移除自启动项..." + DetailPrint "Removing auto-start entries..." # Remove scheduled task (ignore errors if not exists) nsExec::ExecToStack '"$SYSDIR\schtasks.exe" /delete /tn "BS2PRO-Controller" /f' @@ -1056,29 +1056,29 @@ Section "uninstall" Sleep 2000 # Remove application data directories - DetailPrint "正在移除应用数据..." + DetailPrint "Removing application data..." RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath RMDir /r "$APPDATA\BS2PRO-Controller" RMDir /r "$LOCALAPPDATA\BS2PRO-Controller" RMDir /r "$TEMP\BS2PRO-Controller" # Remove installation directory and all contents - DetailPrint "正在移除安装文件..." + DetailPrint "Removing installation files..." # Remove bridge directory (contains TempBridge.exe and related files) - DetailPrint "正在删除桥接组件..." + DetailPrint "Deleting bridge components..." RMDir /r "$INSTDIR\bridge" # Remove logs directory - DetailPrint "正在删除日志文件..." + DetailPrint "Deleting log files..." RMDir /r "$INSTDIR\logs" # Remove entire installation directory - DetailPrint "正在删除安装目录..." + DetailPrint "Deleting installation directory..." RMDir /r $INSTDIR # Remove shortcuts - DetailPrint "正在移除快捷方式..." + DetailPrint "Removing shortcuts..." Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk" @@ -1087,10 +1087,10 @@ Section "uninstall" !insertmacro wails.deleteUninstaller - DetailPrint "卸载完成" + DetailPrint "Uninstallation completed" # Optional: Ask user if they want to remove configuration files - MessageBox MB_YESNO|MB_ICONQUESTION "是否删除所有配置文件和日志?" IDNO skip_config + MessageBox MB_YESNO|MB_ICONQUESTION "Delete all configuration files and logs?" IDNO skip_config RMDir /r "$APPDATA\BS2PRO" RMDir /r "$LOCALAPPDATA\BS2PRO" skip_config: diff --git a/build_bridge.bat b/build_bridge.bat index ca417cd..91fec91 100644 --- a/build_bridge.bat +++ b/build_bridge.bat @@ -1,5 +1,5 @@ @echo off -echo ڹ¶Žӳ... +echo Building temperature bridge program... set "ROOT=%~dp0" set "PROJECT=%ROOT%bridge\TempBridge\TempBridge.csproj" @@ -11,26 +11,26 @@ set "PAWNIO_OUT=%BUILDROOT%\PawnIO_setup.exe" if not exist "%OUTDIR%" mkdir "%OUTDIR%" if not exist "%BUILDROOT%" mkdir "%BUILDROOT%" -echo ԭNuGet... +echo Restoring NuGet packages... dotnet restore "%PROJECT%" if errorlevel 1 goto :error -echo 뷢汾... +echo Building release version... dotnet publish "%PROJECT%" -c Release --self-contained false -o "%OUTDIR%" if errorlevel 1 goto :error -echo PawnIO װ... +echo Downloading PawnIO installer... powershell -NoProfile -ExecutionPolicy Bypass -Command "try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri '%PAWNIO_URL%' -OutFile '%PAWNIO_OUT%' -UseBasicParsing; exit 0 } catch { Write-Error $_; exit 1 }" if errorlevel 1 goto :error if not exist "%PAWNIO_OUT%" goto :error -echo PawnIO : %PAWNIO_OUT% +echo PawnIO downloaded to: %PAWNIO_OUT% -echo ɣĿ¼: %OUTDIR% +echo Build completed, output directory: %OUTDIR% goto :end :error -echo ʧܣ鿴Ϸ־ +echo Build failed! Please check the logs above. :end pause diff --git a/cmd/core/app.go b/cmd/core/app.go index 7a79617..d224796 100644 --- a/cmd/core/app.go +++ b/cmd/core/app.go @@ -32,11 +32,11 @@ import ( //go:embed icon.ico var iconData []byte -// CoreApp 核心应用结构 +// CoreApp is the core application structure type CoreApp struct { ctx context.Context - // 管理器 + // Managers deviceManager *device.Manager bridgeManager *bridge.Manager tempReader *temperature.Reader @@ -48,7 +48,7 @@ type CoreApp struct { logger *logger.CustomLogger ipcServer *ipc.Server - // 状态 + // State isConnected bool monitoringTemp bool currentTemp types.TemperatureData @@ -57,36 +57,36 @@ type CoreApp struct { isAutoStartLaunch bool debugMode bool - // 监控相关 + // Monitoring related guiLastResponse int64 guiMonitorEnabled bool healthCheckTicker *time.Ticker cleanupChan chan bool quitChan chan bool - // 同步 + // Synchronization mutex sync.RWMutex stopMonitoring chan bool manualGearLevelMemory map[string]string } -// NewCoreApp 创建核心应用实例 +// NewCoreApp creates a new core application instance func NewCoreApp(debugMode, isAutoStart bool) *CoreApp { - // 初始化日志系统 + // Initialize logging system installDir := config.GetInstallDir() customLogger, err := logger.NewCustomLogger(debugMode, installDir) if err != nil { - // 如果初始化失败,无法记录,直接退出 - panic(fmt.Sprintf("初始化日志系统失败: %v", err)) + // If initialization fails, cannot log, exit directly + panic(fmt.Sprintf("Failed to initialize logging system: %v", err)) } else { - customLogger.Info("核心服务启动") - customLogger.Info("安装目录: %s", installDir) - customLogger.Info("调试模式: %v", debugMode) - customLogger.Info("自启动模式: %v", isAutoStart) + customLogger.Info("Core service started") + customLogger.Info("Install directory: %s", installDir) + customLogger.Info("Debug mode: %v", debugMode) + customLogger.Info("Auto-start mode: %v", isAutoStart) customLogger.CleanOldLogs() } - // 创建管理器 + // Create managers bridgeMgr := bridge.NewManager(customLogger) deviceMgr := device.NewManager(customLogger) tempReader := temperature.NewReader(bridgeMgr, customLogger) @@ -116,10 +116,10 @@ func NewCoreApp(debugMode, isAutoStart bool) *CoreApp { quitChan: make(chan bool, 1), guiMonitorEnabled: true, manualGearLevelMemory: map[string]string{ - "静音": "中", - "标准": "中", - "强劲": "中", - "超频": "中", + "Silent": "Mid", + "Standard": "Mid", + "Performance": "Mid", + "Overclock": "Mid", }, } app.notifier = notifier.NewManager(customLogger, iconData) @@ -128,122 +128,122 @@ func NewCoreApp(debugMode, isAutoStart bool) *CoreApp { return app } -// Start 启动核心服务 +// Start starts the core service func (a *CoreApp) Start() error { - a.logInfo("=== BS2PRO 核心服务启动 ===") - a.logInfo("版本: %s", version.Get()) - a.logInfo("安装目录: %s", config.GetInstallDir()) - a.logInfo("调试模式: %v", a.debugMode) - a.logInfo("当前工作目录: %s", config.GetCurrentWorkingDir()) + a.logInfo("=== BS2PRO Core Service Starting ===") + a.logInfo("Version: %s", version.Get()) + a.logInfo("Install directory: %s", config.GetInstallDir()) + a.logInfo("Debug mode: %v", a.debugMode) + a.logInfo("Current working directory: %s", config.GetCurrentWorkingDir()) - // 检测是否为自启动 + // Detect if this is an auto-start launch a.isAutoStartLaunch = autostart.DetectAutoStartLaunch(os.Args) - a.logInfo("自启动模式: %v", a.isAutoStartLaunch) + a.logInfo("Auto-start mode: %v", a.isAutoStartLaunch) - // 加载配置 - a.logInfo("开始加载配置文件") + // Load configuration + a.logInfo("Loading configuration file") cfg := a.configManager.Load(a.isAutoStartLaunch) if normalizedLight, changed := normalizeLightStripConfig(cfg.LightStrip); changed { cfg.LightStrip = normalizedLight a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存灯带默认配置失败: %v", err) + a.logError("Failed to save default light strip config: %v", err) } } if normalizedSmart, changed := smartcontrol.NormalizeConfig(cfg.SmartControl, cfg.FanCurve, cfg.DebugMode); changed { cfg.SmartControl = normalizedSmart a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存智能控温默认配置失败: %v", err) + a.logError("Failed to save default smart control config: %v", err) } } if normalizeHotkeyConfig(&cfg) { a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存快捷键默认配置失败: %v", err) + a.logError("Failed to save default hotkey config: %v", err) } } if normalizeCurveProfilesConfig(&cfg) { a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存温控曲线方案默认配置失败: %v", err) + a.logError("Failed to save default fan curve profile config: %v", err) } } if normalizeManualGearMemoryConfig(&cfg) { a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存挡位记忆默认配置失败: %v", err) + a.logError("Failed to save default gear memory config: %v", err) } } a.syncManualGearLevelMemory(cfg) - a.logInfo("配置加载完成,配置路径: %s", cfg.ConfigPath) + a.logInfo("Configuration loaded, config path: %s", cfg.ConfigPath) - // 同步调试模式配置 + // Sync debug mode from config if cfg.DebugMode { a.debugMode = true if a.logger != nil { a.logger.SetDebugMode(true) } - a.logInfo("从配置文件同步调试模式: 启用") + a.logInfo("Synced debug mode from config: enabled") } - // 检查并同步Windows自启动状态 - a.logInfo("检查Windows自启动状态") + // Check and sync Windows auto-start status + a.logInfo("Checking Windows auto-start status") actualAutoStart := a.autostartManager.CheckWindowsAutoStart() if actualAutoStart != cfg.WindowsAutoStart { cfg.WindowsAutoStart = actualAutoStart a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("同步Windows自启动状态时保存配置失败: %v", err) + a.logError("Failed to save config when syncing Windows auto-start status: %v", err) } else { - a.logInfo("已同步Windows自启动状态: %v", actualAutoStart) + a.logInfo("Synced Windows auto-start status: %v", actualAutoStart) } } - // 初始化HID - a.logInfo("初始化HID库") + // Initialize HID + a.logInfo("Initializing HID library") if err := a.deviceManager.Init(); err != nil { - a.logError("初始化HID库失败: %v", err) + a.logError("Failed to initialize HID library: %v", err) return err } - a.logInfo("HID库初始化成功") + a.logInfo("HID library initialized successfully") - // 设置设备回调 + // Set device callbacks a.deviceManager.SetCallbacks(a.onFanDataUpdate, a.onDeviceDisconnect) - // 启动 IPC 服务器 - a.logInfo("启动 IPC 服务器") + // Start IPC server + a.logInfo("Starting IPC server") a.ipcServer = ipc.NewServer(a.handleIPCRequest, a.logger) if err := a.ipcServer.Start(); err != nil { - a.logError("启动 IPC 服务器失败: %v", err) + a.logError("Failed to start IPC server: %v", err) return err } - // 初始化系统托盘 - a.logInfo("开始初始化系统托盘") + // Initialize system tray + a.logInfo("Initializing system tray") a.initSystemTray() a.applyHotkeyBindings(cfg) - // 启动健康监控 + // Start health monitoring if cfg.GuiMonitoring { - a.logInfo("启动健康监控") + a.logInfo("Starting health monitoring") a.safeGo("startHealthMonitoring", func() { a.startHealthMonitoring() }) } - a.logInfo("=== BS2PRO 核心服务启动完成 ===") + a.logInfo("=== BS2PRO Core Service Started ===") - // 软件启动后立即开始温度监控(与智能控温开关解耦) + // Start temperature monitoring immediately after launch (decoupled from smart control toggle) a.safeGo("startTemperatureMonitoring@Start", func() { a.startTemperatureMonitoring() }) - // 尝试连接设备 + // Try to connect device a.safeGo("delayedConnectDevice", func() { if a.isAutoStartLaunch { - // 自启动时等待更长时间,让设备固件有足够时间完成初始化 - a.logInfo("自启动模式:等待设备初始化(3秒)") + // Wait longer during auto-start to allow device firmware to finish initialization + a.logInfo("Auto-start mode: waiting for device initialization (3 seconds)") time.Sleep(3 * time.Second) } else { time.Sleep(1 * time.Second) @@ -254,35 +254,35 @@ func (a *CoreApp) Start() error { return nil } -// Stop 停止核心服务 +// Stop stops the core service func (a *CoreApp) Stop() { - a.logInfo("核心服务正在停止...") + a.logInfo("Core service stopping...") a.stopTemperatureMonitoring() if a.hotkeyManager != nil { a.hotkeyManager.Stop() } - // 清理资源 + // Clean up resources a.cleanup() - // 停止所有监控 + // Stop all monitoring a.DisconnectDevice() - // 停止桥接程序 + // Stop bridge program a.bridgeManager.Stop() - // 停止 IPC 服务器 + // Stop IPC server if a.ipcServer != nil { a.ipcServer.Stop() } - // 停止托盘 + // Stop tray a.trayManager.Quit() - a.logInfo("核心服务已停止") + a.logInfo("Core service stopped") } -// initSystemTray 初始化系统托盘 +// initSystemTray initializes the system tray func (a *CoreApp) initSystemTray() { a.trayManager.SetCallbacks( a.onShowWindowRequest, @@ -296,7 +296,7 @@ func (a *CoreApp) initSystemTray() { func(profileID string) string { profile, err := a.SetActiveFanCurveProfile(profileID) if err != nil { - a.logError("托盘设置温控曲线失败: %v", err) + a.logError("Tray failed to set fan curve profile: %v", err) return "" } return profile.Name @@ -310,7 +310,7 @@ func (a *CoreApp) initSystemTray() { } name := p.Name if strings.TrimSpace(name) == "" { - name = "默认" + name = "Default" } options = append(options, tray.CurveOption{ID: p.ID, Name: name}) } @@ -332,7 +332,7 @@ func (a *CoreApp) initSystemTray() { } name := p.Name if strings.TrimSpace(name) == "" { - name = "默认" + name = "Default" } curveOptions = append(curveOptions, tray.CurveOption{ID: p.ID, Name: name}) } @@ -351,42 +351,42 @@ func (a *CoreApp) initSystemTray() { a.trayManager.Init() } -// onShowWindowRequest 显示窗口请求回调 +// onShowWindowRequest handles show window request callback func (a *CoreApp) onShowWindowRequest() { - a.logInfo("收到显示窗口请求") + a.logInfo("Received show window request") - // 通知所有已连接的 GUI 客户端显示窗口 + // Notify all connected GUI clients to show window if a.ipcServer != nil && a.ipcServer.HasClients() { a.ipcServer.BroadcastEvent("show-window", nil) } else { - // 没有 GUI 连接,启动 GUI - a.logInfo("没有 GUI 连接,尝试启动 GUI") + // No GUI connection, launch GUI + a.logInfo("No GUI connection, attempting to launch GUI") if err := launchGUI(); err != nil { - a.logError("启动 GUI 失败: %v", err) + a.logError("Failed to launch GUI: %v", err) } } } -// onQuitRequest 退出请求回调 +// onQuitRequest handles quit request callback func (a *CoreApp) onQuitRequest() { - a.logInfo("收到退出请求") + a.logInfo("Received quit request") - // 通知所有 GUI 客户端退出 + // Notify all GUI clients to quit if a.ipcServer != nil { a.ipcServer.BroadcastEvent("quit", nil) } - // 发送退出信号 + // Send quit signal select { case a.quitChan <- true: default: } } -// handleIPCRequest 处理 IPC 请求 +// handleIPCRequest handles IPC requests func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { switch req.Type { - // 设备相关 + // Device related case ipc.ReqConnect: success := a.ConnectDevice() return a.successResponse(success) @@ -403,7 +403,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { data := a.deviceManager.GetCurrentFanData() return a.dataResponse(data) - // 配置相关 + // Config related case ipc.ReqGetConfig: cfg := a.configManager.Get() return a.dataResponse(cfg) @@ -411,7 +411,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqUpdateConfig: var cfg types.AppConfig if err := json.Unmarshal(req.Data, &cfg); err != nil { - return a.errorResponse("解析配置失败: " + err.Error()) + return a.errorResponse("Failed to parse config: " + err.Error()) } if err := a.UpdateConfig(cfg); err != nil { return a.errorResponse(err.Error()) @@ -421,7 +421,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetFanCurve: var curve []types.FanCurvePoint if err := json.Unmarshal(req.Data, &curve); err != nil { - return a.errorResponse("解析风扇曲线失败: " + err.Error()) + return a.errorResponse("Failed to parse fan curve: " + err.Error()) } if err := a.SetFanCurve(curve); err != nil { return a.errorResponse(err.Error()) @@ -438,7 +438,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetActiveFanCurveProfile: var params ipc.SetActiveFanCurveProfileParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } profile, err := a.SetActiveFanCurveProfile(params.ID) if err != nil { @@ -449,7 +449,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSaveFanCurveProfile: var params ipc.SaveFanCurveProfileParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } profile, err := a.SaveFanCurveProfile(params) if err != nil { @@ -460,7 +460,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqDeleteFanCurveProfile: var params ipc.DeleteFanCurveProfileParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.DeleteFanCurveProfile(params.ID); err != nil { return a.errorResponse(err.Error()) @@ -477,18 +477,18 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqImportFanCurveProfiles: var params ipc.ImportFanCurveProfilesParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.ImportFanCurveProfiles(params.Code); err != nil { return a.errorResponse(err.Error()) } return a.successResponse(true) - // 控制相关 + // Control related case ipc.ReqSetAutoControl: var params ipc.SetAutoControlParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.SetAutoControl(params.Enabled); err != nil { return a.errorResponse(err.Error()) @@ -498,7 +498,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetManualGear: var params ipc.SetManualGearParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } success := a.SetManualGear(params.Gear, params.Level) return a.successResponse(success) @@ -510,7 +510,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetCustomSpeed: var params ipc.SetCustomSpeedParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.SetCustomSpeed(params.Enabled, params.RPM); err != nil { return a.errorResponse(err.Error()) @@ -520,7 +520,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetGearLight: var params ipc.SetBoolParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } success := a.SetGearLight(params.Enabled) return a.successResponse(success) @@ -528,7 +528,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetPowerOnStart: var params ipc.SetBoolParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } success := a.SetPowerOnStart(params.Enabled) return a.successResponse(success) @@ -536,7 +536,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetSmartStartStop: var params ipc.SetStringParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } success := a.SetSmartStartStop(params.Value) return a.successResponse(success) @@ -544,7 +544,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetBrightness: var params ipc.SetIntParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } success := a.SetBrightness(params.Value) return a.successResponse(success) @@ -552,14 +552,14 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetLightStrip: var params ipc.SetLightStripParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.SetLightStrip(params.Config); err != nil { return a.errorResponse(err.Error()) } return a.successResponse(true) - // 温度相关 + // Temperature related case ipc.ReqGetTemperature: a.mutex.RLock() temp := a.currentTemp @@ -578,11 +578,11 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { status := a.bridgeManager.GetStatus() return a.dataResponse(status) - // 自启动相关 + // Auto-start related case ipc.ReqSetWindowsAutoStart: var params ipc.SetBoolParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.SetWindowsAutoStart(params.Enabled); err != nil { return a.errorResponse(err.Error()) @@ -604,20 +604,20 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetAutoStartWithMethod: var params ipc.SetAutoStartWithMethodParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.autostartManager.SetAutoStartWithMethod(params.Enable, params.Method); err != nil { return a.errorResponse(err.Error()) } return a.successResponse(true) - // 窗口相关 + // Window related case ipc.ReqShowWindow: a.onShowWindowRequest() return a.successResponse(true) case ipc.ReqHideWindow: - // GUI 自己处理隐藏 + // GUI handles hiding itself return a.successResponse(true) case ipc.ReqQuitApp: @@ -626,7 +626,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { }) return a.successResponse(true) - // 调试相关 + // Debug related case ipc.ReqGetDebugInfo: info := a.GetDebugInfo() return a.dataResponse(info) @@ -634,7 +634,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { case ipc.ReqSetDebugMode: var params ipc.SetBoolParams if err := json.Unmarshal(req.Data, ¶ms); err != nil { - return a.errorResponse("解析参数失败: " + err.Error()) + return a.errorResponse("Failed to parse parameters: " + err.Error()) } if err := a.SetDebugMode(params.Enabled); err != nil { return a.errorResponse(err.Error()) @@ -645,7 +645,7 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { atomic.StoreInt64(&a.guiLastResponse, time.Now().Unix()) return a.successResponse(true) - // 系统相关 + // System related case ipc.ReqPing: return a.dataResponse("pong") @@ -653,11 +653,11 @@ func (a *CoreApp) handleIPCRequest(req ipc.Request) ipc.Response { return a.dataResponse(a.isAutoStartLaunch) default: - return a.errorResponse(fmt.Sprintf("未知的请求类型: %s", req.Type)) + return a.errorResponse(fmt.Sprintf("Unknown request type: %s", req.Type)) } } -// 响应辅助方法 +// Response helper methods func (a *CoreApp) successResponse(success bool) ipc.Response { data, _ := json.Marshal(success) return ipc.Response{Success: true, Data: data} @@ -670,31 +670,31 @@ func (a *CoreApp) errorResponse(errMsg string) ipc.Response { func (a *CoreApp) dataResponse(data any) ipc.Response { dataBytes, err := json.Marshal(data) if err != nil { - return a.errorResponse("序列化数据失败: " + err.Error()) + return a.errorResponse("Failed to serialize data: " + err.Error()) } return ipc.Response{Success: true, Data: dataBytes} } -// onFanDataUpdate 风扇数据更新回调 +// onFanDataUpdate handles fan data update callback func (a *CoreApp) onFanDataUpdate(fanData *types.FanData) { a.mutex.Lock() cfg := a.configManager.Get() - // 检查工作模式变化 - // 如果开启了"断连保持配置模式",则忽略设备状态变化,避免误判 + // Check work mode changes + // If "keep config on disconnect" mode is enabled, ignore device state changes to avoid false detection if fanData.WorkMode == "挡位工作模式" && cfg.AutoControl && a.lastDeviceMode == "自动模式(实时转速)" && !a.userSetAutoControl && !cfg.IgnoreDeviceOnReconnect { - a.logInfo("检测到设备从自动模式切换到挡位工作模式,自动关闭智能变频") + a.logInfo("Detected device switched from auto mode to gear mode, automatically disabling smart fan control") cfg.AutoControl = false a.configManager.Set(cfg) a.configManager.Save() - // 广播配置更新 + // Broadcast config update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventConfigUpdate, cfg) } @@ -703,7 +703,7 @@ func (a *CoreApp) onFanDataUpdate(fanData *types.FanData) { a.lastDeviceMode == "自动模式(实时转速)" && !a.userSetAutoControl && cfg.IgnoreDeviceOnReconnect { - a.logInfo("检测到设备模式变化,但已开启断连保持配置模式,保持APP配置不变") + a.logInfo("Device mode change detected, but keep-config-on-disconnect mode is enabled, keeping app config unchanged") } a.lastDeviceMode = fanData.WorkMode @@ -714,13 +714,13 @@ func (a *CoreApp) onFanDataUpdate(fanData *types.FanData) { a.mutex.Unlock() - // 广播风扇数据更新 + // Broadcast fan data update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventFanDataUpdate, fanData) } } -// onDeviceDisconnect 设备断开回调 +// onDeviceDisconnect handles device disconnect callback func (a *CoreApp) onDeviceDisconnect() { a.mutex.Lock() wasConnected := a.isConnected @@ -728,22 +728,22 @@ func (a *CoreApp) onDeviceDisconnect() { a.mutex.Unlock() if wasConnected { - a.logInfo("设备连接已断开,将在健康检查时尝试自动重连") + a.logInfo("Device disconnected, will attempt auto-reconnect during health check") } if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventDeviceDisconnected, nil) } - // 启动自动重连机制 + // Start auto-reconnect mechanism a.safeGo("scheduleReconnect", func() { a.scheduleReconnect() }) } -// scheduleReconnect 安排设备重连 +// scheduleReconnect schedules device reconnection func (a *CoreApp) scheduleReconnect() { - // 延迟一段时间后尝试重连,避免频繁重试 + // Delay before reconnect attempts to avoid frequent retries retryDelays := []time.Duration{ 2 * time.Second, 5 * time.Second, @@ -752,49 +752,49 @@ func (a *CoreApp) scheduleReconnect() { } for i, delay := range retryDelays { - // 检查是否已经连接(可能其他途径已重连) + // Check if already connected (may have reconnected via other means) a.mutex.RLock() connected := a.isConnected a.mutex.RUnlock() if connected { - a.logInfo("设备已重新连接,停止重连尝试") + a.logInfo("Device reconnected, stopping reconnect attempts") return } - a.logInfo("等待 %v 后尝试第 %d 次重连...", delay, i+1) + a.logInfo("Waiting %v before reconnect attempt #%d...", delay, i+1) time.Sleep(delay) - // 再次检查连接状态 + // Check connection status again a.mutex.RLock() connected = a.isConnected a.mutex.RUnlock() if connected { - a.logInfo("设备已重新连接,停止重连尝试") + a.logInfo("Device reconnected, stopping reconnect attempts") return } - a.logInfo("尝试第 %d 次重连设备...", i+1) + a.logInfo("Attempting device reconnect #%d...", i+1) if a.ConnectDevice() { - a.logInfo("设备重连成功") + a.logInfo("Device reconnected successfully") - // 如果开启了断连保持配置模式,重新应用APP配置 + // If keep-config-on-disconnect mode is enabled, reapply app config cfg := a.configManager.Get() if cfg.IgnoreDeviceOnReconnect { - a.logInfo("断连保持配置模式已开启,重新应用APP配置") + a.logInfo("Keep-config-on-disconnect mode enabled, reapplying app config") a.reapplyConfigAfterReconnect() } return } - a.logError("第 %d 次重连失败", i+1) + a.logError("Reconnect attempt #%d failed", i+1) } - a.logError("所有重连尝试均失败,等待下次健康检查") + a.logError("All reconnect attempts failed, waiting for next health check") } -// ConnectDevice 连接设备 +// ConnectDevice connects to the device func (a *CoreApp) ConnectDevice() bool { success, deviceInfo := a.deviceManager.Connect() if success { @@ -807,18 +807,18 @@ func (a *CoreApp) ConnectDevice() bool { } if err := a.applyConfiguredLightStrip(); err != nil { - a.logError("应用灯带配置失败: %v", err) + a.logError("Failed to apply light strip config: %v", err) } a.safeGo("startTemperatureMonitoring@ConnectDevice", func() { a.startTemperatureMonitoring() }) } else if a.ipcServer != nil { - a.ipcServer.BroadcastEvent(ipc.EventDeviceError, "连接失败") + a.ipcServer.BroadcastEvent(ipc.EventDeviceError, "Connection failed") } return success } -// DisconnectDevice 断开设备连接 +// DisconnectDevice disconnects from the device func (a *CoreApp) DisconnectDevice() { a.mutex.Lock() a.isConnected = false @@ -831,43 +831,43 @@ func (a *CoreApp) DisconnectDevice() { } } -// reapplyConfigAfterReconnect 重连后重新应用APP配置 +// reapplyConfigAfterReconnect reapplies app config after device reconnection func (a *CoreApp) reapplyConfigAfterReconnect() { cfg := a.configManager.Get() - // 重新应用智能变频配置 + // Reapply smart fan control config if cfg.AutoControl { - a.logInfo("重新启动智能变频") + a.logInfo("Restarting smart fan control") } else if cfg.CustomSpeedEnabled { - // 重新应用自定义转速 - a.logInfo("重新应用自定义转速: %d RPM", cfg.CustomSpeedRPM) + // Reapply custom speed + a.logInfo("Reapplying custom speed: %d RPM", cfg.CustomSpeedRPM) if !a.deviceManager.SetCustomFanSpeed(cfg.CustomSpeedRPM) { - a.logError("重新应用自定义转速失败") + a.logError("Failed to reapply custom speed") } } - // 重新应用挡位灯配置 + // Reapply gear light config if cfg.GearLight { - a.logInfo("重新开启挡位灯") + a.logInfo("Re-enabling gear light") if !a.deviceManager.SetGearLight(true) { - a.logError("重新开启挡位灯失败") + a.logError("Failed to re-enable gear light") } } - // 重新应用通电自启动配置 + // Reapply power-on auto-start config if cfg.PowerOnStart { - a.logInfo("重新开启通电自启动") + a.logInfo("Re-enabling power-on auto-start") if !a.deviceManager.SetPowerOnStart(true) { - a.logError("重新开启通电自启动失败") + a.logError("Failed to re-enable power-on auto-start") } } if err := a.applyConfiguredLightStrip(); err != nil { - a.logError("重连后重新应用灯带配置失败: %v", err) + a.logError("Failed to reapply light strip config after reconnect: %v", err) } } -// GetDeviceStatus 获取设备状态 +// GetDeviceStatus gets device status func (a *CoreApp) GetDeviceStatus() map[string]any { a.mutex.RLock() defer a.mutex.RUnlock() @@ -896,7 +896,7 @@ func (a *CoreApp) GetDeviceStatus() map[string]any { } } -// UpdateConfig 更新配置 +// UpdateConfig updates the configuration func (a *CoreApp) UpdateConfig(cfg types.AppConfig) error { a.mutex.Lock() defer a.mutex.Unlock() @@ -928,7 +928,7 @@ func (a *CoreApp) UpdateConfig(cfg types.AppConfig) error { return nil } -// SetFanCurve 设置风扇曲线 +// SetFanCurve sets the fan curve func (a *CoreApp) SetFanCurve(curve []types.FanCurvePoint) error { a.mutex.Lock() defer a.mutex.Unlock() @@ -944,7 +944,7 @@ func (a *CoreApp) SetFanCurve(curve []types.FanCurvePoint) error { return a.configManager.Update(cfg) } -// SetAutoControl 设置智能变频 +// SetAutoControl sets smart fan control func (a *CoreApp) SetAutoControl(enabled bool) error { a.mutex.Lock() defer a.mutex.Unlock() @@ -952,7 +952,7 @@ func (a *CoreApp) SetAutoControl(enabled bool) error { cfg := a.configManager.Get() if enabled && cfg.CustomSpeedEnabled { - return fmt.Errorf("自定义转速模式下无法开启智能变频") + return fmt.Errorf("cannot enable smart fan control while custom speed mode is active") } cfg.AutoControl = enabled @@ -977,7 +977,7 @@ func (a *CoreApp) SetAutoControl(enabled bool) error { return err } -// applyCurrentGearSetting 应用当前挡位设置 +// applyCurrentGearSetting applies the current gear setting func (a *CoreApp) applyCurrentGearSetting() { fanData := a.deviceManager.GetCurrentFanData() if fanData == nil { @@ -991,11 +991,11 @@ func (a *CoreApp) applyCurrentGearSetting() { } level := a.getRememberedManualLevel(setGear, cfg.ManualLevel) - a.logInfo("应用当前挡位设置: %s %s", setGear, level) + a.logInfo("Applying current gear setting: %s %s", setGear, level) a.deviceManager.SetManualGear(setGear, level) } -// SetManualGear 设置手动挡位 +// SetManualGear sets the manual gear func (a *CoreApp) SetManualGear(gear, level string) bool { cfg := a.configManager.Get() cfg.ManualGear = gear @@ -1014,7 +1014,7 @@ func (a *CoreApp) SetManualGear(gear, level string) bool { return a.deviceManager.SetManualGear(gear, level) } -// SetCustomSpeed 设置自定义转速 +// SetCustomSpeed sets the custom fan speed func (a *CoreApp) SetCustomSpeed(enabled bool, rpm int) error { a.mutex.Lock() defer a.mutex.Unlock() @@ -1048,7 +1048,7 @@ func (a *CoreApp) SetCustomSpeed(enabled bool, rpm int) error { return err } -// SetGearLight 设置挡位灯 +// SetGearLight sets the gear indicator light func (a *CoreApp) SetGearLight(enabled bool) bool { if !a.deviceManager.SetGearLight(enabled) { return false @@ -1058,14 +1058,14 @@ func (a *CoreApp) SetGearLight(enabled bool) bool { cfg.GearLight = enabled a.configManager.Update(cfg) - // 广播配置更新 + // Broadcast config update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventConfigUpdate, cfg) } return true } -// SetPowerOnStart 设置通电自启动 +// SetPowerOnStart sets power-on auto-start func (a *CoreApp) SetPowerOnStart(enabled bool) bool { if !a.deviceManager.SetPowerOnStart(enabled) { return false @@ -1075,14 +1075,14 @@ func (a *CoreApp) SetPowerOnStart(enabled bool) bool { cfg.PowerOnStart = enabled a.configManager.Update(cfg) - // 广播配置更新 + // Broadcast config update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventConfigUpdate, cfg) } return true } -// SetSmartStartStop 设置智能启停 +// SetSmartStartStop sets smart start/stop func (a *CoreApp) SetSmartStartStop(mode string) bool { if !a.deviceManager.SetSmartStartStop(mode) { return false @@ -1092,14 +1092,14 @@ func (a *CoreApp) SetSmartStartStop(mode string) bool { cfg.SmartStartStop = mode a.configManager.Update(cfg) - // 广播配置更新 + // Broadcast config update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventConfigUpdate, cfg) } return true } -// SetBrightness 设置亮度 +// SetBrightness sets the brightness func (a *CoreApp) SetBrightness(percentage int) bool { if !a.deviceManager.SetBrightness(percentage) { return false @@ -1109,14 +1109,14 @@ func (a *CoreApp) SetBrightness(percentage int) bool { cfg.Brightness = percentage a.configManager.Update(cfg) - // 广播配置更新 + // Broadcast config update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventConfigUpdate, cfg) } return true } -// SetLightStrip 设置灯带 +// SetLightStrip sets the light strip configuration func (a *CoreApp) SetLightStrip(lightCfg types.LightStripConfig) error { lightCfg, _ = normalizeLightStripConfig(lightCfg) @@ -1148,7 +1148,7 @@ func (a *CoreApp) applyConfiguredLightStrip() error { cfg.LightStrip = lightCfg a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存灯带默认配置失败: %v", err) + a.logError("Failed to save default light strip config: %v", err) } } @@ -1225,9 +1225,9 @@ func normalizeManualGearMemoryConfig(cfg *types.AppConfig) bool { changed = true } - for _, gear := range []string{"静音", "标准", "强劲", "超频"} { + for _, gear := range []string{"Silent", "Standard", "Performance", "Overclock"} { if level, ok := cfg.ManualGearLevels[gear]; !ok { - cfg.ManualGearLevels[gear] = "中" + cfg.ManualGearLevels[gear] = "Mid" changed = true } else { normalized := normalizeManualLevel(level) @@ -1259,7 +1259,7 @@ func (a *CoreApp) applyHotkeyBindings(cfg types.AppConfig) { return } if err := a.hotkeyManager.UpdateBindings(cfg.ManualGearToggleHotkey, cfg.AutoControlToggleHotkey, cfg.CurveProfileToggleHotkey); err != nil { - a.logError("更新全局快捷键失败: %v", err) + a.logError("Failed to update global hotkeys: %v", err) } } @@ -1295,7 +1295,7 @@ func (a *CoreApp) handleHotkeyAction(action hotkeysvc.Action, shortcut string) { } default: success = false - message = "未知快捷键动作" + message = "Unknown hotkey action" } if a.ipcServer != nil { @@ -1307,9 +1307,9 @@ func (a *CoreApp) handleHotkeyAction(action hotkeysvc.Action, shortcut string) { }) } - title := "BS2PRO 快捷键" + title := "BS2PRO Hotkey" if !success { - title = "BS2PRO 快捷键失败" + title = "BS2PRO Hotkey Failed" } if a.notifier != nil { a.notifier.Notify(title, message) @@ -1322,7 +1322,7 @@ func (a *CoreApp) toggleCurveProfileByHotkey() (string, error) { if err != nil { return "", err } - return fmt.Sprintf("温控曲线已切换: %s", profile.Name), nil + return fmt.Sprintf("Fan curve switched to: %s", profile.Name), nil } func (a *CoreApp) toggleAutoControlByHotkey() (string, error) { @@ -1332,9 +1332,9 @@ func (a *CoreApp) toggleAutoControlByHotkey() (string, error) { return "", err } if target { - return "智能变频已开启", nil + return "Smart fan control enabled", nil } - return "智能变频已关闭", nil + return "Smart fan control disabled", nil } func (a *CoreApp) toggleManualGearByHotkey() (string, error) { @@ -1342,24 +1342,24 @@ func (a *CoreApp) toggleManualGearByHotkey() (string, error) { if cfg.AutoControl { if err := a.SetAutoControl(false); err != nil { - return "", fmt.Errorf("切换到手动模式失败: %w", err) + return "", fmt.Errorf("failed to switch to manual mode: %w", err) } } nextGear, nextLevel := a.getNextManualGearWithMemory(cfg.ManualGear, cfg.ManualLevel) if ok := a.SetManualGear(nextGear, nextLevel); !ok { - return "", fmt.Errorf("应用手动挡位失败") + return "", fmt.Errorf("failed to apply manual gear") } rpm := getManualGearRPM(nextGear, nextLevel) if rpm > 0 { - return fmt.Sprintf("手动挡位: %s %s (%d RPM)", nextGear, nextLevel, rpm), nil + return fmt.Sprintf("Manual gear: %s %s (%d RPM)", nextGear, nextLevel, rpm), nil } - return fmt.Sprintf("手动挡位: %s %s", nextGear, nextLevel), nil + return fmt.Sprintf("Manual gear: %s %s", nextGear, nextLevel), nil } func (a *CoreApp) getNextManualGearWithMemory(currentGear, currentLevel string) (string, string) { - sequence := []string{"静音", "标准", "强劲", "超频"} + sequence := []string{"Silent", "Standard", "Performance", "Overclock"} nextIndex := 0 for i, gear := range sequence { @@ -1377,17 +1377,17 @@ func (a *CoreApp) getNextManualGearWithMemory(currentGear, currentLevel string) } func normalizeManualLevel(level string) string { - if level == "低" || level == "中" || level == "高" { + if level == "Low" || level == "Mid" || level == "High" { return level } - return "中" + return "Mid" } func cloneManualGearLevels(source map[string]string) map[string]string { cloned := map[string]string{} - for _, gear := range []string{"静音", "标准", "强劲", "超频"} { + for _, gear := range []string{"Silent", "Standard", "Performance", "Overclock"} { if source == nil { - cloned[gear] = "中" + cloned[gear] = "Mid" continue } cloned[gear] = normalizeManualLevel(source[gear]) @@ -1409,7 +1409,7 @@ func (a *CoreApp) syncManualGearLevelMemoryLocked(cfg types.AppConfig) { } defaultLevel := normalizeManualLevel(cfg.ManualLevel) - for _, gear := range []string{"静音", "标准", "强劲", "超频"} { + for _, gear := range []string{"Silent", "Standard", "Performance", "Overclock"} { if fromCfg, ok := cfg.ManualGearLevels[gear]; ok { a.manualGearLevelMemory[gear] = normalizeManualLevel(fromCfg) continue @@ -1421,7 +1421,7 @@ func (a *CoreApp) syncManualGearLevelMemoryLocked(cfg types.AppConfig) { } func (a *CoreApp) rememberManualGearLevel(gear, level string) { - if gear != "静音" && gear != "标准" && gear != "强劲" && gear != "超频" { + if gear != "Silent" && gear != "Standard" && gear != "Performance" && gear != "Overclock" { return } @@ -1454,9 +1454,9 @@ func getManualGearRPM(gear, level string) int { } for _, cmd := range commands { - if (level == "低" && containsLevel(cmd.Name, "低")) || - (level == "中" && containsLevel(cmd.Name, "中")) || - (level == "高" && containsLevel(cmd.Name, "高")) { + if (level == "Low" && containsLevel(cmd.Name, "Low")) || + (level == "Mid" && containsLevel(cmd.Name, "Mid")) || + (level == "High" && containsLevel(cmd.Name, "High")) { return cmd.RPM } } @@ -1468,7 +1468,7 @@ func containsLevel(name, level string) bool { return strings.Contains(name, level) } -// SetWindowsAutoStart 设置Windows自启动 +// SetWindowsAutoStart sets Windows auto-start func (a *CoreApp) SetWindowsAutoStart(enable bool) error { err := a.autostartManager.SetWindowsAutoStart(enable) if err == nil { @@ -1476,7 +1476,7 @@ func (a *CoreApp) SetWindowsAutoStart(enable bool) error { cfg.WindowsAutoStart = enable a.configManager.Update(cfg) - // 广播配置更新 + // Broadcast config update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventConfigUpdate, cfg) } @@ -1484,7 +1484,7 @@ func (a *CoreApp) SetWindowsAutoStart(enable bool) error { return err } -// GetDebugInfo 获取调试信息 +// GetDebugInfo gets debug information func (a *CoreApp) GetDebugInfo() map[string]any { info := map[string]any{ "debugMode": a.debugMode, @@ -1499,7 +1499,7 @@ func (a *CoreApp) GetDebugInfo() map[string]any { return info } -// SetDebugMode 设置调试模式 +// SetDebugMode sets debug mode func (a *CoreApp) SetDebugMode(enabled bool) error { a.mutex.Lock() defer a.mutex.Unlock() @@ -1512,9 +1512,9 @@ func (a *CoreApp) SetDebugMode(enabled bool) error { if a.logger != nil { a.logger.SetDebugMode(enabled) if enabled { - a.logger.Info("调试模式已开启,后续日志将包含调试级别") + a.logger.Info("Debug mode enabled, subsequent logs will include debug level") } else { - a.logger.Info("调试模式已关闭,调试级别日志将被忽略") + a.logger.Info("Debug mode disabled, debug level logs will be suppressed") } } @@ -1541,13 +1541,13 @@ func (a *CoreApp) stopTemperatureMonitoring() { } } -// startTemperatureMonitoring 开始温度监控 +// startTemperatureMonitoring starts temperature monitoring func (a *CoreApp) startTemperatureMonitoring() { if a.monitoringTemp { return } - // 清理可能残留的停止信号,避免新监控循环被立即中断。 + // Clear any residual stop signals to avoid the new monitoring loop being interrupted immediately. select { case <-a.stopMonitoring: default: @@ -1555,14 +1555,14 @@ func (a *CoreApp) startTemperatureMonitoring() { a.monitoringTemp = true - // 注意:不在此处立即调用 EnterAutoMode,因为在启动时温度数据(桥接程序)可能尚未就绪。 - // 如果在温度读取成功之前切换到软件控制模式,设备将不会收到转速指令,导致风扇停转。 - // EnterAutoMode 和转速设置会在首次成功读取温度后,由 SetFanSpeed 内部统一完成。 + // Note: Do not call EnterAutoMode here immediately, because temperature data (bridge program) may not be ready at startup. + // If we switch to software control mode before temperature is successfully read, the device won't receive speed commands, causing the fan to stop. + // EnterAutoMode and speed settings will be handled internally by SetFanSpeed after the first successful temperature read. cfg := a.configManager.Get() updateInterval := time.Duration(cfg.TempUpdateRate) * time.Second - // 温度采样缓冲区 + // Temperature sample buffer sampleCount := max(cfg.TempSampleCount, 1) tempSamples := make([]int, 0, sampleCount) recentAvgTemps := make([]int, 0, 24) @@ -1585,7 +1585,7 @@ func (a *CoreApp) startTemperatureMonitoring() { a.currentTemp = temp a.mutex.Unlock() - // 广播温度更新 + // Broadcast temperature update if a.ipcServer != nil { a.ipcServer.BroadcastEvent(ipc.EventTemperatureUpdate, temp) } @@ -1596,25 +1596,25 @@ func (a *CoreApp) startTemperatureMonitoring() { cfg.SmartControl = smartCfg a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存智能控温配置失败: %v", err) + a.logError("Failed to save smart control config: %v", err) } } if cfg.AutoControl && temp.MaxTemp > 0 { - // 更新采样配置 + // Update sample config newSampleCount := max(cfg.TempSampleCount, 1) if newSampleCount != sampleCount { sampleCount = newSampleCount tempSamples = make([]int, 0, sampleCount) } - // 添加新采样 + // Add new sample tempSamples = append(tempSamples, temp.MaxTemp) if len(tempSamples) > sampleCount { tempSamples = tempSamples[len(tempSamples)-sampleCount:] } - // 计算平均温度 + // Calculate average temperature avgTemp := 0 for _, t := range tempSamples { avgTemp += t @@ -1687,7 +1687,7 @@ func (a *CoreApp) startTemperatureMonitoring() { if learningDirty && time.Since(lastLearningSave) >= 25*time.Second { if err := a.configManager.Save(); err != nil { - a.logError("保存学习曲线失败: %v", err) + a.logError("Failed to save learned curve: %v", err) } else { lastLearningSave = time.Now() learningDirty = false @@ -1699,7 +1699,7 @@ func (a *CoreApp) startTemperatureMonitoring() { } if baseRPM > 0 { - a.logDebug("智能控温: 温度=%d°C 平均=%d°C 控制温度=%d°C 基础=%dRPM 目标=%dRPM", temp.MaxTemp, avgTemp, controlTemp, baseRPM, targetRPM) + a.logDebug("Smart control: temp=%d°C avg=%d°C control_temp=%d°C base=%dRPM target=%dRPM", temp.MaxTemp, avgTemp, controlTemp, baseRPM, targetRPM) } lastControlTemp = controlTemp @@ -1709,14 +1709,14 @@ func (a *CoreApp) startTemperatureMonitoring() { if learningDirty { if err := a.configManager.Save(); err != nil { - a.logError("退出监控时保存学习曲线失败: %v", err) + a.logError("Failed to save learned curve when exiting monitoring: %v", err) } } } -// startHealthMonitoring 启动健康监控 +// startHealthMonitoring starts health monitoring func (a *CoreApp) startHealthMonitoring() { - a.logInfo("启动健康监控系统") + a.logInfo("Starting health monitoring system") a.healthCheckTicker = time.NewTicker(30 * time.Second) @@ -1728,7 +1728,7 @@ func (a *CoreApp) startHealthMonitoring() { case <-a.healthCheckTicker.C: a.performHealthCheck() case <-a.cleanupChan: - a.logInfo("健康监控系统已停止") + a.logInfo("Health monitoring system stopped") return } } @@ -1741,46 +1741,46 @@ func (a *CoreApp) startHealthMonitoring() { } } -// performHealthCheck 执行健康检查 +// performHealthCheck performs a health check func (a *CoreApp) performHealthCheck() { defer func() { if r := recover(); r != nil { - a.logError("健康检查中发生panic: %v", r) + a.logError("Panic during health check: %v", r) } }() a.trayManager.CheckHealth() a.checkDeviceHealth() - a.logDebug("健康检查完成 - 托盘:%v 设备连接:%v", + a.logDebug("Health check completed - tray:%v device_connected:%v", a.trayManager.IsInitialized(), a.isConnected) } -// checkDeviceHealth 检查设备健康状态 +// checkDeviceHealth checks device health status func (a *CoreApp) checkDeviceHealth() { a.mutex.RLock() connected := a.isConnected a.mutex.RUnlock() if !connected { - a.logInfo("健康检查: 设备未连接,尝试重新连接") + a.logInfo("Health check: device not connected, attempting reconnect") a.safeGo("healthReconnect", func() { if a.ConnectDevice() { - a.logInfo("健康检查: 设备重连成功") + a.logInfo("Health check: device reconnected successfully") } else { - a.logDebug("健康检查: 设备重连失败,等待下次检查") + a.logDebug("Health check: device reconnect failed, waiting for next check") } }) } else { - // 验证设备实际连接状态 + // Verify actual device connection status if !a.deviceManager.IsConnected() { - a.logError("健康检查: 检测到设备状态不一致,触发断开回调") + a.logError("Health check: detected inconsistent device status, triggering disconnect callback") a.onDeviceDisconnect() } } } -// cleanup 清理资源 +// cleanup cleans up resources func (a *CoreApp) cleanup() { if a.healthCheckTicker != nil { a.healthCheckTicker.Stop() @@ -1792,12 +1792,12 @@ func (a *CoreApp) cleanup() { } if a.logger != nil { - a.logger.Info("核心服务正在退出,清理资源") + a.logger.Info("Core service exiting, cleaning up resources") a.logger.Close() } } -// 日志辅助方法 +// Logging helper methods func (a *CoreApp) logInfo(format string, v ...any) { if a.logger != nil { a.logger.Info(format, v...) @@ -1828,11 +1828,11 @@ func (a *CoreApp) safeGo(name string, fn func()) { }() } -// launchGUI 启动 GUI 程序 +// launchGUI launches the GUI program func launchGUI() error { exePath, err := os.Executable() if err != nil { - return fmt.Errorf("获取可执行文件路径失败: %v", err) + return fmt.Errorf("failed to get executable path: %v", err) } exeDir := filepath.Dir(exePath) @@ -1841,7 +1841,7 @@ func launchGUI() error { if _, err := os.Stat(guiPath); os.IsNotExist(err) { guiPath = filepath.Join(exeDir, "..", "BS2PRO-Controller.exe") if _, err := os.Stat(guiPath); os.IsNotExist(err) { - return fmt.Errorf("GUI 程序不存在: %s", guiPath) + return fmt.Errorf("GUI program not found: %s", guiPath) } } @@ -1851,11 +1851,11 @@ func launchGUI() error { } if err := cmd.Start(); err != nil { - return fmt.Errorf("启动 GUI 程序失败: %v", err) + return fmt.Errorf("failed to launch GUI program: %v", err) } - // 使用 fmt 而非日志系统,避免循环依赖 - fmt.Printf("GUI 程序已启动,PID: %d\n", cmd.Process.Pid) + // Use fmt instead of logging system to avoid circular dependency + fmt.Printf("GUI program launched, PID: %d\n", cmd.Process.Pid) go func() { cmd.Wait() diff --git a/cmd/core/crash_report.go b/cmd/core/crash_report.go index b4fe80c..3aece97 100644 --- a/cmd/core/crash_report.go +++ b/cmd/core/crash_report.go @@ -16,8 +16,8 @@ func capturePanic(app *CoreApp, source string, recovered any) string { logDir := resolveCrashLogDir(app) if err := os.MkdirAll(logDir, 0755); err != nil { - fmt.Fprintf(os.Stderr, "创建崩溃日志目录失败: %v\n", err) - fmt.Fprintf(os.Stderr, "panic来源: %s, panic: %v\n%s\n", source, recovered, string(stack)) + fmt.Fprintf(os.Stderr, "Failed to create crash log directory: %v\n", err) + fmt.Fprintf(os.Stderr, "panic source: %s, panic: %v\n%s\n", source, recovered, string(stack)) return "" } @@ -36,20 +36,20 @@ func capturePanic(app *CoreApp, source string, recovered any) string { builder.WriteString("\n") if err := os.WriteFile(filePath, []byte(builder.String()), 0644); err != nil { - fmt.Fprintf(os.Stderr, "写入崩溃报告失败: %v\n", err) - fmt.Fprintf(os.Stderr, "panic来源: %s, panic: %v\n%s\n", source, recovered, string(stack)) + fmt.Fprintf(os.Stderr, "Failed to write crash report: %v\n", err) + fmt.Fprintf(os.Stderr, "panic source: %s, panic: %v\n%s\n", source, recovered, string(stack)) return "" } if app != nil { - app.logError("[%s] 捕获到panic: %v", source, recovered) - app.logError("[%s] panic堆栈:\n%s", source, string(stack)) + app.logError("[%s] Caught panic: %v", source, recovered) + app.logError("[%s] Panic stack trace:\n%s", source, string(stack)) if app.logger != nil { app.logger.Close() } } - fmt.Fprintf(os.Stderr, "程序发生panic,崩溃报告已写入: %s\n", filePath) + fmt.Fprintf(os.Stderr, "Program panic occurred, crash report written to: %s\n", filePath) return filePath } diff --git a/cmd/core/curve_profiles.go b/cmd/core/curve_profiles.go index 4f4fa72..5bbe6dd 100644 --- a/cmd/core/curve_profiles.go +++ b/cmd/core/curve_profiles.go @@ -96,7 +96,7 @@ func normalizeCurveProfilesConfig(cfg *types.AppConfig) bool { if len(cfg.FanCurveProfiles) == 0 { cfg.FanCurveProfiles = []types.FanCurveProfile{{ ID: "default", - Name: "默认", + Name: "Default", Curve: cloneFanCurve(baseCurve), }} changed = true @@ -112,7 +112,7 @@ func normalizeCurveProfilesConfig(cfg *types.AppConfig) bool { } seenIDs[profile.ID] = true - fallbackName := fmt.Sprintf("方案%d", i+1) + fallbackName := fmt.Sprintf("Profile%d", i+1) name := normalizeCurveProfileName(profile.Name, fallbackName) if name != profile.Name { profile.Name = name @@ -134,7 +134,7 @@ func normalizeCurveProfilesConfig(cfg *types.AppConfig) bool { if len(cfg.FanCurveProfiles) == 0 { cfg.FanCurveProfiles = []types.FanCurveProfile{{ ID: "default", - Name: "默认", + Name: "Default", Curve: cloneFanCurve(baseCurve), }} changed = true @@ -200,7 +200,7 @@ func (a *CoreApp) GetFanCurveProfiles() types.FanCurveProfilesPayload { if normalizeCurveProfilesConfig(&cfg) { a.configManager.Set(cfg) if err := a.configManager.Save(); err != nil { - a.logError("保存温控曲线方案默认配置失败: %v", err) + a.logError("Failed to save default fan curve profile config: %v", err) } } return a.fanCurveProfilesPayloadFromConfig(cfg) @@ -215,7 +215,7 @@ func (a *CoreApp) SetActiveFanCurveProfile(profileID string) (types.FanCurveProf idx := findCurveProfileIndex(cfg.FanCurveProfiles, profileID) if idx < 0 { - return types.FanCurveProfile{}, fmt.Errorf("未找到温控曲线方案") + return types.FanCurveProfile{}, fmt.Errorf("fan curve profile not found") } cfg.ActiveFanCurveProfileID = cfg.FanCurveProfiles[idx].ID @@ -238,7 +238,7 @@ func (a *CoreApp) CycleFanCurveProfile() (types.FanCurveProfile, error) { normalizeCurveProfilesConfig(&cfg) if len(cfg.FanCurveProfiles) == 0 { - return types.FanCurveProfile{}, fmt.Errorf("暂无可用温控曲线方案") + return types.FanCurveProfile{}, fmt.Errorf("no fan curve profiles available") } idx := max(findCurveProfileIndex(cfg.FanCurveProfiles, cfg.ActiveFanCurveProfileID), 0) @@ -269,7 +269,7 @@ func (a *CoreApp) SaveFanCurveProfile(params ipc.SaveFanCurveProfileParams) (typ return types.FanCurveProfile{}, err } - profileName := normalizeCurveProfileName(params.Name, "新曲线") + profileName := normalizeCurveProfileName(params.Name, "New") profileID := strings.TrimSpace(params.ID) idx := findCurveProfileIndex(cfg.FanCurveProfiles, profileID) if idx < 0 { @@ -306,17 +306,17 @@ func (a *CoreApp) DeleteFanCurveProfile(profileID string) error { normalizeCurveProfilesConfig(&cfg) if len(cfg.FanCurveProfiles) <= 1 { - return fmt.Errorf("至少保留一个温控曲线方案") + return fmt.Errorf("at least one fan curve profile must be kept") } idx := findCurveProfileIndex(cfg.FanCurveProfiles, profileID) if idx < 0 { - return fmt.Errorf("未找到温控曲线方案") + return fmt.Errorf("fan curve profile not found") } cfg.FanCurveProfiles = append(cfg.FanCurveProfiles[:idx], cfg.FanCurveProfiles[idx+1:]...) if len(cfg.FanCurveProfiles) == 0 { - return fmt.Errorf("至少保留一个温控曲线方案") + return fmt.Errorf("at least one fan curve profile must be kept") } if cfg.ActiveFanCurveProfileID == profileID { @@ -366,35 +366,35 @@ func (a *CoreApp) ExportFanCurveProfiles() (string, error) { func (a *CoreApp) ImportFanCurveProfiles(code string) error { trimmed := strings.TrimSpace(code) if trimmed == "" { - return fmt.Errorf("导入字符串不能为空") + return fmt.Errorf("import string cannot be empty") } if !strings.HasPrefix(trimmed, curveExportPrefix) { - return fmt.Errorf("导入字符串格式错误") + return fmt.Errorf("invalid import string format") } raw := strings.TrimPrefix(trimmed, curveExportPrefix) compressed, err := base64.RawURLEncoding.DecodeString(raw) if err != nil { - return fmt.Errorf("导入字符串解码失败") + return fmt.Errorf("failed to decode import string") } zr, err := zlib.NewReader(bytes.NewReader(compressed)) if err != nil { - return fmt.Errorf("导入字符串解压失败") + return fmt.Errorf("failed to decompress import string") } defer zr.Close() plain, err := io.ReadAll(zr) if err != nil { - return fmt.Errorf("导入数据读取失败") + return fmt.Errorf("failed to read import data") } var payload fanCurveExportPayload if err := json.Unmarshal(plain, &payload); err != nil { - return fmt.Errorf("导入数据格式错误") + return fmt.Errorf("invalid import data format") } if payload.V != 1 { - return fmt.Errorf("不支持的导入版本") + return fmt.Errorf("unsupported import version") } a.mutex.Lock() diff --git a/cmd/core/main.go b/cmd/core/main.go index 29c525b..54dcca5 100644 --- a/cmd/core/main.go +++ b/cmd/core/main.go @@ -29,7 +29,7 @@ func main() { } }() - // 检测命令行参数 + // Parse command line arguments debugMode := false isAutoStart := false @@ -42,23 +42,23 @@ func main() { } } - // 创建核心应用 + // Create core application app = NewCoreApp(debugMode, isAutoStart) - // 启动应用 + // Start application if err := app.Start(); err != nil { - panic(fmt.Sprintf("启动核心服务失败: %v", err)) + panic(fmt.Sprintf("Failed to start core service: %v", err)) } - // 等待退出信号 + // Wait for exit signal sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) select { case <-sigChan: - app.logInfo("收到系统退出信号") + app.logInfo("Received system exit signal") case <-app.quitChan: - app.logInfo("收到应用退出请求") + app.logInfo("Received application quit request") } app.Stop() diff --git a/frontend/src/app/components/AppFatalError.tsx b/frontend/src/app/components/AppFatalError.tsx index cd2a927..007b35a 100644 --- a/frontend/src/app/components/AppFatalError.tsx +++ b/frontend/src/app/components/AppFatalError.tsx @@ -12,12 +12,12 @@ export default function AppFatalError({ message, onRetry }: AppFatalErrorProps)
-

应用初始化失败

+

App Initialization Failed

{message}

diff --git a/frontend/src/app/components/AppShell.tsx b/frontend/src/app/components/AppShell.tsx index 36b5c7e..fa33349 100644 --- a/frontend/src/app/components/AppShell.tsx +++ b/frontend/src/app/components/AppShell.tsx @@ -18,9 +18,9 @@ import { import { types } from '../../../wailsjs/go/models'; const TAB_ITEMS = [ - { id: 'status', title: '状态', icon: LayoutGrid }, - { id: 'curve', title: '曲线', icon: LineChart }, - { id: 'control', title: '设置', icon: Settings2 }, + { id: 'status', title: 'Status', icon: LayoutGrid }, + { id: 'curve', title: 'Curve', icon: LineChart }, + { id: 'control', title: 'Settings', icon: Settings2 }, ] as const; type ActiveTab = (typeof TAB_ITEMS)[number]['id']; @@ -117,7 +117,7 @@ export default function AppShell({ }`} > {isConnected ? : } - {isConnected ? '设备已连接' : '设备离线'} + {isConnected ? 'Device Connected' : 'Device Offline'} - {autoControl ? '智能控制开启' : '手动模式'} + {autoControl ? 'Smart Control On' : 'Manual Mode'} @@ -221,7 +221,7 @@ export default function AppShell({

{bridgeWarning}

- {/* ═══════════ 2. 风扇控制 ═══════════ */} -
+ {/* ═══════════ 2. Fan Control ═══════════ */} +
{/* Auto control */} : } - title="自动温度控制" - description="根据温度曲线自动调节风扇转速" + title="Auto Temperature Control" + description="Automatically adjust fan speed based on temperature curve" disabled={(config as any).customSpeedEnabled} > } - title="采样时间" - description="降低频繁调整带来的轴噪,不知道默认即可" + title="Sample Time" + description="Reduce bearing noise from frequent adjustments. Leave default if unsure." >