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." >