using System; using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; using System.Windows.Forms; using APS7100TestTool.Controllers; using APS7100TestTool.Services; using APS7100TestTool.Models; namespace APS7100TestTool.Forms { public partial class MainForm : Form { // Devices private ScpiDevice? _deviceAPS7100; private ScpiDevice? _devicePSW250; private IPowerSupplyController? _controllerAPS7100; private IPowerSupplyController? _controllerPSW250; // Services private ModbusTcpServerService? _modbusService; private ConfigService _configService; private AppSettings _appSettings; private List _modbusMappings = new List(); // Timers private System.Windows.Forms.Timer _measureTimer; // Child Windows private ManualTestForm? _manualTestFormAPS7100; private ManualTestForm? _manualTestFormPSW250; // 静默启动控制 private bool _startMinimizedToTray = false; private bool _hasShownOnce = false; // 缓存的设备标识(避免频繁查询阻塞UI) private string _cachedAPS7100IDN = ""; private string _cachedPSW250IDN = ""; // 连接中标志(防止定时器阻塞UI) private volatile bool _isConnectingAPS7100 = false; private volatile bool _isConnectingPSW250 = false; public MainForm() { InitializeComponent(); // 启用双缓冲减少闪烁 this.DoubleBuffered = true; this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); this.UpdateStyles(); _configService = new ConfigService(); _appSettings = _configService.LoadAppSettings(); // SCPI 命令调试日志(如需诊断,将 false 改为 true) ScpiDevice.EnableCommandLog = false; ScpiDevice.OnCommandLog += (msg) => { BeginInvoke(() => LogMessage(msg)); }; // 初始化测量定时器 _measureTimer = new System.Windows.Forms.Timer(); _measureTimer.Interval = 1000; _measureTimer.Tick += MeasureTimer_Tick; _measureTimer.Start(); // 初始化系统托盘 InitializeSystemTray(); // 如果设置启动时最小化,则标记为静默启动 _startMinimizedToTray = _appSettings.StartMinimized; InitializeUI(); } /// /// 初始化系统托盘图标 /// private void InitializeSystemTray() { // 创建托盘右键菜单 trayContextMenu = new ContextMenuStrip(); trayMenuShow = new ToolStripMenuItem("显示主窗口(&S)"); trayMenuShow.Font = new Font(trayMenuShow.Font, FontStyle.Bold); trayMenuShow.Click += (s, e) => ShowMainWindow(); trayMenuAPS7100 = new ToolStripMenuItem("APS-7100: 未连接"); trayMenuAPS7100.Enabled = false; trayMenuPSW250 = new ToolStripMenuItem("PSW-250: 未连接"); trayMenuPSW250.Enabled = false; trayMenuSep1 = new ToolStripSeparator(); trayMenuExit = new ToolStripMenuItem("退出(&X)"); trayMenuExit.Click += (s, e) => ExitApplication(); trayContextMenu.Items.AddRange(new ToolStripItem[] { trayMenuShow, new ToolStripSeparator(), trayMenuAPS7100, trayMenuPSW250, trayMenuSep1, trayMenuExit }); // 创建托盘图标 notifyIcon = new NotifyIcon(); notifyIcon.Text = "固纬电源测试工具"; notifyIcon.Icon = this.Icon ?? SystemIcons.Application; notifyIcon.ContextMenuStrip = trayContextMenu; notifyIcon.Visible = true; // 双击托盘图标显示窗口 notifyIcon.DoubleClick += (s, e) => ShowMainWindow(); // 托盘气泡提示点击 notifyIcon.BalloonTipClicked += (s, e) => ShowMainWindow(); } /// /// 重写 SetVisibleCore 以实现静默启动(开机自启动时不显示窗口) /// protected override void SetVisibleCore(bool value) { // 第一次显示时,如果是静默启动模式,则不显示窗口 if (!_hasShownOnce) { _hasShownOnce = true; if (_startMinimizedToTray) { // 静默启动:不显示窗口,只在托盘运行 base.SetVisibleCore(false); return; } } base.SetVisibleCore(value); } /// /// 显示主窗口 /// private void ShowMainWindow() { this.Show(); this.ShowInTaskbar = true; this.WindowState = FormWindowState.Normal; this.Activate(); this.BringToFront(); } /// /// 退出应用程序 /// private void ExitApplication() { // 先清理资源,再退出 CleanupResources(); notifyIcon.Visible = false; notifyIcon.Dispose(); Application.Exit(); } /// /// 清理所有资源(确保端口和连接被正确释放) /// private void CleanupResources() { try { _measureTimer?.Stop(); _measureTimer?.Dispose(); // 停止并释放 Modbus 服务 if (_modbusService != null) { _modbusService.Stop(); _modbusService.Dispose(); _modbusService = null; } // 释放设备连接 if (_deviceAPS7100 != null) { _deviceAPS7100.Disconnect(); _deviceAPS7100.Dispose(); _deviceAPS7100 = null; } if (_devicePSW250 != null) { _devicePSW250.Disconnect(); _devicePSW250.Dispose(); _devicePSW250 = null; } _controllerAPS7100 = null; _controllerPSW250 = null; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"清理资源时出错: {ex.Message}"); } } /// /// 窗口大小改变时处理(最小化到托盘) /// protected override void OnResize(EventArgs e) { base.OnResize(e); if (this.WindowState == FormWindowState.Minimized) { this.Hide(); this.ShowInTaskbar = false; } } /// /// 更新托盘菜单中的设备状态 /// private void UpdateTrayStatus() { if (trayMenuAPS7100 != null) { bool aps7100Connected = _deviceAPS7100?.IsConnected == true; trayMenuAPS7100.Text = aps7100Connected ? "APS-7100: ✓ 已连接" : "APS-7100: 未连接"; } if (trayMenuPSW250 != null) { bool psw250Connected = _devicePSW250?.IsConnected == true; trayMenuPSW250.Text = psw250Connected ? "PSW-250: ✓ 已连接" : "PSW-250: 未连接"; } // 更新托盘图标提示文字 if (notifyIcon != null) { bool aps = _deviceAPS7100?.IsConnected == true; bool psw = _devicePSW250?.IsConnected == true; string status = ""; if (aps) status += "APS7100 ✓ "; if (psw) status += "PSW250 ✓"; if (string.IsNullOrEmpty(status)) status = "未连接设备"; notifyIcon.Text = $"固纬电源测试工具\n{status}"; } } private void InitializeUI() { UpdateAPS7100Status(); UpdatePSW250Status(); UpdateModbusStatus(); LogMessage("程序启动完成"); // 自动加载 Modbus 配置 AutoLoadModbusConfig(); // 执行自动连接和 Modbus 启动(异步) _ = AutoConnectAndStartModbusAsync(); } /// /// 自动连接设备并启动 Modbus 服务(按顺序执行,确保时序正确) /// private async Task AutoConnectAndStartModbusAsync() { var connectTasks = new List(); // 自动连接设备(并行执行) if (_appSettings.AutoConnectAPS7100) { LogMessage("正在自动连接 APS-7100..."); connectTasks.Add(ConnectAPS7100Async()); } if (_appSettings.AutoConnectPSW250) { LogMessage("正在自动连接 PSW-250..."); connectTasks.Add(ConnectPSW250Async()); } // 等待所有连接任务完成(无论成功还是失败) if (connectTasks.Count > 0) { await Task.WhenAll(connectTasks); } // 连接完成后再启动 Modbus 服务(带重试机制) if (_appSettings.AutoStartModbus && _modbusMappings.Count > 0) { LogMessage("正在自动启动 Modbus 服务..."); await StartModbusServiceWithRetryAsync(maxRetries: 3, delayMs: 1000); } } /// /// 带重试机制的 Modbus 服务启动 /// private async Task StartModbusServiceWithRetryAsync(int maxRetries = 3, int delayMs = 1000) { for (int attempt = 1; attempt <= maxRetries; attempt++) { try { StartModbusService(); // 检查是否真的启动成功 if (_modbusService != null && _modbusService.IsRunning) { return; // 启动成功,退出 } else { LogMessage($"Modbus 服务启动后状态异常 (尝试 {attempt}/{maxRetries})"); } } catch (Exception ex) { LogMessage($"Modbus 服务启动异常 (尝试 {attempt}/{maxRetries}): {ex.Message}"); } // 如果不是最后一次尝试,等待后重试 if (attempt < maxRetries) { LogMessage($"将在 {delayMs}ms 后重试..."); await Task.Delay(delayMs); } } LogMessage("⚠ Modbus 服务自动启动失败,请手动启动"); } /// /// 将相对路径转换为绝对路径(相对于程序运行目录) /// private string ResolveConfigPath(string path) { if (string.IsNullOrEmpty(path)) return path; // 如果已经是绝对路径,直接返回 if (System.IO.Path.IsPathRooted(path)) return path; // 相对路径:转换为相对于程序运行目录的绝对路径 return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path); } /// /// 自动加载上次保存的 Modbus 配置 /// private void AutoLoadModbusConfig() { // 优先使用上次保存的配置文件路径(支持相对路径) string configPath = ResolveConfigPath(_appSettings.ModbusConfigPath); // 如果没有配置路径或文件不存在,尝试使用默认路径 if (string.IsNullOrEmpty(configPath) || !System.IO.File.Exists(configPath)) { configPath = ConfigService.DefaultModbusConfigPath; } if (System.IO.File.Exists(configPath)) { try { _modbusMappings = _configService.LoadModbusConfig(configPath); LogMessage($"✓ 自动加载 Modbus 配置: {configPath} ({_modbusMappings.Count} 条规则)"); UpdateModbusStatus(); } catch (Exception ex) { LogMessage($"自动加载 Modbus 配置失败: {ex.Message}"); } } } #region APS7100 Connection private async void btnConnectAPS7100_Click(object sender, EventArgs e) { // 如果正在连接中,忽略点击 if (_isConnectingAPS7100) { LogMessage("APS7100 正在连接中,请稍候..."); return; } if (_deviceAPS7100 != null && _deviceAPS7100.IsConnected) { DisconnectAPS7100(); } else { await ShowConnectionDialogAsync(DeviceType.APS7100); } } private async void menuConnectAPS7100_Click(object sender, EventArgs e) { if (_deviceAPS7100 != null && _deviceAPS7100.IsConnected) { DisconnectAPS7100(); } else { await ShowConnectionDialogAsync(DeviceType.APS7100); } } private async Task ConnectAPS7100Async() { // 禁用按钮,显示连接中状态 btnConnectAPS7100.Enabled = false; btnConnectAPS7100.Text = "连接中..."; menuConnectAPS7100.Enabled = false; _isConnectingAPS7100 = true; try { LogMessage("APS7100 开始连接..."); // 确保旧连接被正确清理 if (_deviceAPS7100 != null) { LogMessage(" 清理旧连接..."); try { _deviceAPS7100.Disconnect(); } catch { } try { _deviceAPS7100.Dispose(); } catch { } _deviceAPS7100 = null; _controllerAPS7100 = null; } // 等待后台任务完成和资源释放 await Task.Delay(500); // 强制垃圾回收以释放网络资源 GC.Collect(); GC.WaitForPendingFinalizers(); await Task.Delay(200); // 尝试连接(带重试) // 设备断电重启后可能需要时间初始化网络服务,使用递增间隔重试 int maxRetries = 5; int[] retryDelays = { 0, 1000, 2000, 3000, 5000 }; // 递增间隔 Exception? lastException = null; for (int retry = 0; retry < maxRetries; retry++) { if (retry > 0) { int delay = retryDelays[Math.Min(retry, retryDelays.Length - 1)]; LogMessage($" [APS7100] 等待 {delay / 1000.0:F1} 秒后重试连接 ({retry}/{maxRetries - 1})..."); await Task.Delay(delay); } try { await Task.Run(() => { _deviceAPS7100 = new ScpiDevice(); if (_appSettings.APS7100ConnectionType == "Serial") { if (string.IsNullOrEmpty(_appSettings.APS7100ComPort)) { throw new Exception("未配置串口"); } _deviceAPS7100.ConnectSerial(_appSettings.APS7100ComPort, _appSettings.APS7100BaudRate); } else { _deviceAPS7100.ConnectEthernet(_appSettings.APS7100IpAddress, _appSettings.APS7100Port); } _controllerAPS7100 = new APS7100Controller(_deviceAPS7100); }); string connInfo = _appSettings.APS7100ConnectionType == "Serial" ? $"{_appSettings.APS7100ComPort} @ {_appSettings.APS7100BaudRate}" : $"{_appSettings.APS7100IpAddress}:{_appSettings.APS7100Port}"; LogMessage($"APS7100 连接成功: {connInfo}"); // 获取设备标识(异步执行,避免阻塞UI) _cachedAPS7100IDN = await Task.Run(() => _controllerAPS7100!.GetIdentification()); LogMessage($"✓ APS7100 设备识别: {_cachedAPS7100IDN}"); // 进入远程控制模式(部分设备可能不支持) try { //发送*IDN? 命令后,会自动切换到远程模式,无需再发送远程切换 //await Task.Run(() => _controllerAPS7100!.SetRemoteMode()); LogMessage($"✓ APS7100 已进入远程控制模式"); } catch (Exception remEx) { LogMessage($"⚠ APS7100 远程模式命令失败(可忽略): {remEx.Message}"); } UpdateAPS7100Status(); UpdateManualTestController(DeviceType.APS7100); // 更新 Modbus 服务的控制器引用 _modbusService?.UpdateControllers(_controllerAPS7100, _controllerPSW250); // 连接成功,退出重试循环 break; } catch (Exception ex) { lastException = ex; LogMessage($" [APS7100] 连接尝试 {retry + 1} 失败: {ex.Message}"); // 清理本次失败的连接资源 if (_deviceAPS7100 != null) { try { _deviceAPS7100.Disconnect(); } catch { } try { _deviceAPS7100.Dispose(); } catch { } _deviceAPS7100 = null; } _controllerAPS7100 = null; // 如果不是最后一次重试,继续 if (retry < maxRetries - 1) { continue; } // 最后一次重试也失败了 int totalWaitTime = 0; for (int i = 0; i < maxRetries; i++) totalWaitTime += retryDelays[Math.Min(i, retryDelays.Length - 1)]; LogMessage($"[APS7100] 连接失败(已尝试 {maxRetries} 次,总等待约 {totalWaitTime / 1000 + maxRetries * 5} 秒)"); LogMessage($" 错误: {ex.Message}"); if (ex.InnerException != null) { LogMessage($" 内部错误: {ex.InnerException.Message}"); } LogMessage($" 提示: 如果设备刚断电重启,请等待设备完全启动后再试"); UpdateAPS7100Status(); } } // end for retry loop } finally { // 恢复按钮状态 _isConnectingAPS7100 = false; btnConnectAPS7100.Enabled = true; menuConnectAPS7100.Enabled = true; UpdateAPS7100Status(); // 更新按钮文字 } } // 防止重复断开 private volatile bool _isDisconnectingAPS7100 = false; private void DisconnectAPS7100() { // 防止重复断开 if (_isDisconnectingAPS7100) return; if (_deviceAPS7100 == null && _controllerAPS7100 == null) return; _isDisconnectingAPS7100 = true; try { // 断开前返回本地模式 if (_controllerAPS7100 != null && _deviceAPS7100?.IsConnected == true) { try { _controllerAPS7100.SetLocalMode(); } catch { /* 忽略断开时的错误 */ } } var device = _deviceAPS7100; _deviceAPS7100 = null; _controllerAPS7100 = null; if (device != null) { try { device.Disconnect(); } catch { } try { device.Dispose(); } catch { } } LogMessage("APS7100 已断开连接"); UpdateAPS7100Status(); UpdateManualTestController(DeviceType.APS7100); // 更新 Modbus 服务的控制器引用 _modbusService?.UpdateControllers(_controllerAPS7100, _controllerPSW250); } catch (Exception ex) { LogMessage($"断开APS7100失败: {ex.Message}"); } finally { _isDisconnectingAPS7100 = false; } } private async Task ShowConnectionDialogAsync(DeviceType deviceType) { using (var dialog = new ConnectionDialog(_appSettings, deviceType)) { if (dialog.ShowDialog() == DialogResult.OK) { _appSettings = dialog.Settings; _configService.SaveAppSettings(_appSettings); if (deviceType == DeviceType.APS7100) await ConnectAPS7100Async(); else await ConnectPSW250Async(); } } } #endregion #region PSW250 Connection private async void btnConnectPSW250_Click(object sender, EventArgs e) { // 如果正在连接中,忽略点击 if (_isConnectingPSW250) { LogMessage("PSW250 正在连接中,请稍候..."); return; } if (_devicePSW250 != null && _devicePSW250.IsConnected) { DisconnectPSW250(); } else { await ShowConnectionDialogAsync(DeviceType.PSW250); } } private async void menuConnectPSW250_Click(object sender, EventArgs e) { if (_devicePSW250 != null && _devicePSW250.IsConnected) { DisconnectPSW250(); } else { await ShowConnectionDialogAsync(DeviceType.PSW250); } } private async Task ConnectPSW250Async() { // 禁用按钮,显示连接中状态 btnConnectPSW250.Enabled = false; btnConnectPSW250.Text = "连接中..."; menuConnectPSW250.Enabled = false; _isConnectingPSW250 = true; try { LogMessage("PSW250 开始连接..."); // 确保旧连接被正确清理 if (_devicePSW250 != null) { LogMessage(" 清理旧连接..."); try { _devicePSW250.Disconnect(); } catch { } try { _devicePSW250.Dispose(); } catch { } _devicePSW250 = null; _controllerPSW250 = null; } // 等待后台任务完成和资源释放 await Task.Delay(500); // 强制垃圾回收以释放网络资源 GC.Collect(); GC.WaitForPendingFinalizers(); await Task.Delay(200); // 尝试连接(带重试) // 设备断电重启后可能需要时间初始化网络服务,使用递增间隔重试 int maxRetries = 5; int[] retryDelays = { 0, 1000, 2000, 3000, 5000 }; // 递增间隔 Exception? lastException = null; for (int retry = 0; retry < maxRetries; retry++) { if (retry > 0) { int delay = retryDelays[Math.Min(retry, retryDelays.Length - 1)]; LogMessage($" [PSW250] 等待 {delay / 1000.0:F1} 秒后重试连接 ({retry}/{maxRetries - 1})..."); await Task.Delay(delay); } try { await Task.Run(() => { _devicePSW250 = new ScpiDevice(); if (_appSettings.PSW250ConnectionType == "Serial") { if (string.IsNullOrEmpty(_appSettings.PSW250ComPort)) { throw new Exception("未配置串口"); } _devicePSW250.ConnectSerial(_appSettings.PSW250ComPort, _appSettings.PSW250BaudRate); } else { _devicePSW250.ConnectEthernet(_appSettings.PSW250IpAddress, _appSettings.PSW250Port); } _controllerPSW250 = new PSW250Controller(_devicePSW250); }); string connInfo = _appSettings.PSW250ConnectionType == "Serial" ? $"{_appSettings.PSW250ComPort} @ {_appSettings.PSW250BaudRate}" : $"{_appSettings.PSW250IpAddress}:{_appSettings.PSW250Port}"; LogMessage($"PSW250 连接成功: {connInfo}"); // 获取设备标识(异步执行,避免阻塞UI) _cachedPSW250IDN = await Task.Run(() => _controllerPSW250!.GetIdentification()); LogMessage($"✓ PSW250 设备识别: {_cachedPSW250IDN}"); // 进入远程控制模式 await Task.Run(() => _controllerPSW250!.SetRemoteMode()); LogMessage($"✓ PSW250 已进入远程控制模式"); UpdatePSW250Status(); UpdateManualTestController(DeviceType.PSW250); // 更新 Modbus 服务的控制器引用 _modbusService?.UpdateControllers(_controllerAPS7100, _controllerPSW250); // 连接成功,退出重试循环 break; } catch (Exception ex) { lastException = ex; LogMessage($" [PSW250] 连接尝试 {retry + 1} 失败: {ex.Message}"); // 清理本次失败的连接资源 if (_devicePSW250 != null) { try { _devicePSW250.Disconnect(); } catch { } try { _devicePSW250.Dispose(); } catch { } _devicePSW250 = null; } _controllerPSW250 = null; // 如果不是最后一次重试,继续 if (retry < maxRetries - 1) { continue; } // 最后一次重试也失败了 int totalWaitTime = 0; for (int i = 0; i < maxRetries; i++) totalWaitTime += retryDelays[Math.Min(i, retryDelays.Length - 1)]; LogMessage($"[PSW250] 连接失败(已尝试 {maxRetries} 次,总等待约 {totalWaitTime / 1000 + maxRetries * 5} 秒)"); LogMessage($" 错误: {ex.Message}"); if (ex.InnerException != null) { LogMessage($" 内部错误: {ex.InnerException.Message}"); } LogMessage($" 提示: 如果设备刚断电重启,请等待设备完全启动后再试"); UpdatePSW250Status(); } } // end for retry loop } finally { // 恢复按钮状态 _isConnectingPSW250 = false; btnConnectPSW250.Enabled = true; menuConnectPSW250.Enabled = true; UpdatePSW250Status(); // 更新按钮文字 } } // 防止重复断开 private volatile bool _isDisconnectingPSW250 = false; private void DisconnectPSW250() { // 防止重复断开 if (_isDisconnectingPSW250) return; if (_devicePSW250 == null && _controllerPSW250 == null) return; _isDisconnectingPSW250 = true; try { // 断开前返回本地模式 if (_controllerPSW250 != null && _devicePSW250?.IsConnected == true) { try { _controllerPSW250.SetLocalMode(); } catch { /* 忽略断开时的错误 */ } } var device = _devicePSW250; _devicePSW250 = null; _controllerPSW250 = null; if (device != null) { try { device.Disconnect(); } catch { } try { device.Dispose(); } catch { } } LogMessage("PSW250 已断开连接"); UpdatePSW250Status(); UpdateManualTestController(DeviceType.PSW250); // 更新 Modbus 服务的控制器引用 _modbusService?.UpdateControllers(_controllerAPS7100, _controllerPSW250); } catch (Exception ex) { LogMessage($"断开PSW250失败: {ex.Message}"); } finally { _isDisconnectingPSW250 = false; } } #endregion #region Status Updates private void UpdateAPS7100Status() { bool connected = _deviceAPS7100 != null && _deviceAPS7100.IsConnected; if (connected) { pnlAPS7100Indicator.BackColor = Color.LimeGreen; lblAPS7100ConnStatus.Text = "已连接"; lblAPS7100ConnStatus.ForeColor = Color.Green; string connInfo = _appSettings.APS7100ConnectionType == "Serial" ? $"串口: {_appSettings.APS7100ComPort}" : $"网口: {_appSettings.APS7100IpAddress}:{_appSettings.APS7100Port}"; lblAPS7100ConnInfo.Text = $"连接信息: {connInfo}"; // 使用缓存的设备标识,避免阻塞UI string idn = !string.IsNullOrEmpty(_cachedAPS7100IDN) ? _cachedAPS7100IDN : "--"; lblAPS7100IDN.Text = $"设备: {(idn.Length > 40 ? idn.Substring(0, 40) + "..." : idn)}"; btnConnectAPS7100.Text = "断 开"; btnConnectAPS7100.BackColor = Color.LightCoral; menuConnectAPS7100.Text = "断开 APS-7100"; } else { pnlAPS7100Indicator.BackColor = Color.Gray; lblAPS7100ConnStatus.Text = "未连接"; lblAPS7100ConnStatus.ForeColor = Color.Gray; lblAPS7100ConnInfo.Text = "连接信息: --"; lblAPS7100IDN.Text = "设备: --"; lblAPS7100OutputStatus.Text = "OFF"; lblAPS7100OutputStatus.ForeColor = Color.Gray; lblAPS7100VoltageValue.Text = "--- V"; lblAPS7100CurrentValue.Text = "--- A"; lblAPS7100PowerValue.Text = "--- W"; lblAPS7100FreqValue.Text = "--- Hz"; lblAPS7100PFValue.Text = "---"; btnConnectAPS7100.Text = "连 接"; btnConnectAPS7100.BackColor = Color.LightGreen; menuConnectAPS7100.Text = "连接 APS-7100..."; // 清除缓存 _cachedAPS7100IDN = ""; } UpdateTrayStatus(); } private void UpdatePSW250Status() { bool connected = _devicePSW250 != null && _devicePSW250.IsConnected; if (connected) { pnlPSW250Indicator.BackColor = Color.LimeGreen; lblPSW250ConnStatus.Text = "已连接"; lblPSW250ConnStatus.ForeColor = Color.Green; string connInfo = _appSettings.PSW250ConnectionType == "Serial" ? $"串口: {_appSettings.PSW250ComPort}" : $"网口: {_appSettings.PSW250IpAddress}:{_appSettings.PSW250Port}"; lblPSW250ConnInfo.Text = $"连接信息: {connInfo}"; // 使用缓存的设备标识,避免阻塞UI string idn = !string.IsNullOrEmpty(_cachedPSW250IDN) ? _cachedPSW250IDN : "--"; lblPSW250IDN.Text = $"设备: {(idn.Length > 40 ? idn.Substring(0, 40) + "..." : idn)}"; btnConnectPSW250.Text = "断 开"; btnConnectPSW250.BackColor = Color.LightCoral; menuConnectPSW250.Text = "断开 PSW-250"; } else { pnlPSW250Indicator.BackColor = Color.Gray; lblPSW250ConnStatus.Text = "未连接"; lblPSW250ConnStatus.ForeColor = Color.Gray; lblPSW250ConnInfo.Text = "连接信息: --"; lblPSW250IDN.Text = "设备: --"; lblPSW250OutputStatus.Text = "OFF"; lblPSW250OutputStatus.ForeColor = Color.Gray; lblPSW250ModeValue.Text = "--"; lblPSW250ModeValue.ForeColor = Color.DarkOrange; lblPSW250VoltageValue.Text = "--- V"; lblPSW250CurrentValue.Text = "--- A"; lblPSW250PowerValue.Text = "--- W"; btnConnectPSW250.Text = "连 接"; btnConnectPSW250.BackColor = Color.LightGreen; menuConnectPSW250.Text = "连接 PSW-250..."; // 清除缓存 _cachedPSW250IDN = ""; } UpdateTrayStatus(); } private void UpdateModbusStatus() { bool running = _modbusService != null && _modbusService.IsRunning; if (running) { lblModbusStatusText.Text = "● 运行中"; lblModbusStatusText.ForeColor = Color.Green; menuModbusStart.Text = "停止服务"; } else { lblModbusStatusText.Text = "● 未启动"; lblModbusStatusText.ForeColor = Color.Gray; menuModbusStart.Text = "启动服务"; // 服务未运行时重置客户端状态显示 lblModbusClientCount.Text = "客户端: --"; lblModbusClientCount.ForeColor = Color.Gray; lblModbusRequestCount.Text = "请求: --"; lblModbusLastRequest.Text = "最后通讯: --"; } lblModbusPort.Text = $"端口: {_appSettings.ModbusPort}"; lblModbusConfigCount.Text = $"规则: {_modbusMappings.Count} 条"; } /// /// 更新 Modbus 客户端连接状态显示 /// private void UpdateModbusClientStatus(int clientCount, int requestCount, DateTime lastRequest) { // 更新客户端数 lblModbusClientCount.Text = $"客户端: {clientCount}"; lblModbusClientCount.ForeColor = clientCount > 0 ? Color.Green : Color.Gray; // 更新请求数 lblModbusRequestCount.Text = $"请求: {requestCount}"; // 更新最后通讯时间 if (lastRequest > DateTime.MinValue) { var elapsed = DateTime.Now - lastRequest; if (elapsed.TotalSeconds < 60) lblModbusLastRequest.Text = $"最后通讯: {elapsed.TotalSeconds:F0}秒前"; else if (elapsed.TotalMinutes < 60) lblModbusLastRequest.Text = $"最后通讯: {elapsed.TotalMinutes:F0}分钟前"; else lblModbusLastRequest.Text = $"最后通讯: {lastRequest:HH:mm:ss}"; } else { lblModbusLastRequest.Text = "最后通讯: --"; } } /// /// 实时更新 Modbus 心跳和设备状态显示 /// private void UpdateModbusHeartbeatDisplay() { if (_modbusService != null && _modbusService.IsRunning) { // 显示心跳(地址:值) lblModbusHeartbeat.Text = $"心跳[{_appSettings.HeartbeatRegisterAddress}]: {_modbusService.HeartbeatValue}"; // 显示设备状态(地址:值) var (aps, psw) = _modbusService.DeviceStatus; ushort statusValue = 0; if (aps) statusValue |= 0x0001; if (psw) statusValue |= 0x0002; string statusText = ""; if (aps && psw) statusText = "APS+PSW"; else if (aps) statusText = "APS"; else if (psw) statusText = "PSW"; else statusText = "无"; lblModbusDeviceStatus.Text = $"设备[{_appSettings.DeviceStatusRegisterAddress}]: {statusValue} ({statusText})"; lblModbusDeviceStatus.ForeColor = (aps || psw) ? Color.DarkCyan : Color.Gray; } else { lblModbusHeartbeat.Text = "心跳: --"; lblModbusDeviceStatus.Text = "设备: --"; lblModbusDeviceStatus.ForeColor = Color.Gray; } } #endregion #region Timer Tick // 防止测量操作重叠 private volatile bool _isMeasuring = false; private async void MeasureTimer_Tick(object? sender, EventArgs e) { // 如果正在连接或正在测量,跳过本次 if (_isConnectingAPS7100 || _isConnectingPSW250 || _isMeasuring) return; _isMeasuring = true; try { bool aps7100Connected = _deviceAPS7100?.IsConnected == true && !_isConnectingAPS7100; bool psw250Connected = _devicePSW250?.IsConnected == true && !_isConnectingPSW250; // 更新 Modbus 服务的设备状态(用于心跳) _modbusService?.UpdateDeviceStatus(aps7100Connected, psw250Connected); // 实时更新 Modbus 心跳和设备状态显示 UpdateModbusHeartbeatDisplay(); // 并行执行两个设备的测量,避免串行等待 Task? aps7100Task = null; Task? psw250Task = null; // APS7100 测量任务 if (_controllerAPS7100 != null && aps7100Connected) { var controller = _controllerAPS7100; aps7100Task = Task.Run(() => { // 为每个命令单独处理异常,避免一个失败导致全部失败 double voltage = 0, current = 0, power = 0, frequency = 0, powerFactor = 0; bool outputOn = false; int failCount = 0; try { voltage = controller.MeasureVoltage(); } catch { failCount++; } try { current = controller.MeasureCurrent(); } catch { failCount++; } try { power = controller.MeasurePower(); } catch { failCount++; } try { frequency = controller.MeasureFrequency(); } catch { failCount++; } try { powerFactor = controller.MeasurePowerFactor(); } catch { failCount++; } try { outputOn = controller.GetOutputState(); } catch { failCount++; } return new { Voltage = voltage, Current = current, Power = power, Frequency = frequency, PowerFactor = powerFactor, OutputOn = outputOn, ConnectionLost = failCount >= 2 }; // 2个以上命令失败认为连接丢失 }).ContinueWith(t => { if (t.IsCompletedSuccessfully && !this.IsDisposed) { var m = t.Result; this.BeginInvoke((MethodInvoker)delegate { if (m.ConnectionLost) { // 连接丢失,自动断开 LogMessage("⚠ APS7100 连接丢失,设备可能已断电"); DisconnectAPS7100(); } else { lblAPS7100VoltageValue.Text = $"{m.Voltage:F2} V"; lblAPS7100CurrentValue.Text = $"{m.Current:F3} A"; lblAPS7100PowerValue.Text = $"{m.Power:F2} W"; lblAPS7100FreqValue.Text = $"{m.Frequency:F2} Hz"; lblAPS7100PFValue.Text = $"{m.PowerFactor:F3}"; lblAPS7100OutputStatus.Text = m.OutputOn ? "ON" : "OFF"; lblAPS7100OutputStatus.ForeColor = m.OutputOn ? Color.Green : Color.Gray; } }); } }, TaskScheduler.Default); } // PSW250 测量任务 if (_controllerPSW250 != null && psw250Connected) { var controller = _controllerPSW250; psw250Task = Task.Run(() => { double voltage = 0, current = 0, power = 0; bool outputOn = false; int failCount = 0; try { voltage = controller.MeasureVoltage(); } catch { failCount++; } try { current = controller.MeasureCurrent(); } catch { failCount++; } try { power = controller.MeasurePower(); } catch { failCount++; } try { outputOn = controller.GetOutputState(); } catch { failCount++; } // 获取 OUTP:MODE 控制优先级设置 string outputMode = "CVHS"; if (controller is Controllers.PSW250Controller pswController) { try { var mode = pswController.GetOutputMode(); outputMode = mode.ToString(); } catch { outputMode = "--"; } } // 判断实际运行状态(CV/CC) string runState = "CV"; try { var setCurrent = controller.GetCurrent(); if (setCurrent > 0.01 && current >= setCurrent * 0.98) { runState = "CC"; } } catch { } return new { Voltage = voltage, Current = current, Power = power, OutputOn = outputOn, OutputMode = outputMode, RunState = runState, ConnectionLost = failCount >= 2 // 2个以上命令失败认为连接丢失 }; }).ContinueWith(t => { if (t.IsCompletedSuccessfully && !this.IsDisposed) { var m = t.Result; this.BeginInvoke((MethodInvoker)delegate { if (m.ConnectionLost) { // 连接丢失,自动断开 LogMessage("⚠ PSW250 连接丢失,设备可能已断电"); DisconnectPSW250(); } else { lblPSW250VoltageValue.Text = $"{m.Voltage:F2} V"; lblPSW250CurrentValue.Text = $"{m.Current:F3} A"; lblPSW250PowerValue.Text = $"{m.Power:F2} W"; lblPSW250OutputStatus.Text = m.OutputOn ? "ON" : "OFF"; lblPSW250OutputStatus.ForeColor = m.OutputOn ? Color.Green : Color.Gray; // 显示: 运行状态(控制优先级),如 "CV(CVHS)" 或 "CC(CCHS)" lblPSW250ModeValue.Text = $"{m.RunState}({m.OutputMode})"; lblPSW250ModeValue.ForeColor = m.RunState == "CC" ? Color.OrangeRed : Color.DodgerBlue; } }); } }, TaskScheduler.Default); } // 等待所有任务完成(并行执行,不会互相阻塞) var tasks = new List(); if (aps7100Task != null) tasks.Add(aps7100Task); if (psw250Task != null) tasks.Add(psw250Task); if (tasks.Count > 0) { await Task.WhenAll(tasks); } } catch { } finally { _isMeasuring = false; } } #endregion #region Menu Events private void menuSettings_Click(object sender, EventArgs e) { using (var form = new SettingsForm()) { if (form.ShowDialog() == DialogResult.OK) { _appSettings = _configService.LoadAppSettings(); UpdateModbusStatus(); } } } private void menuExit_Click(object sender, EventArgs e) { this.Close(); } private void menuManualTestAPS7100_Click(object sender, EventArgs e) { if (_manualTestFormAPS7100 == null || _manualTestFormAPS7100.IsDisposed) { // 暂停主窗口测量定时器,避免与手动测试冲突 // ⚠ APS7100 收到任何 SCPI 命令后会自动进入远程模式 _measureTimer.Stop(); LogMessage("ℹ 主窗口测量已暂停(手动测试窗口已打开)"); _manualTestFormAPS7100 = new ManualTestForm(_controllerAPS7100, DeviceType.APS7100); // 设置 Modbus 轮询控制回调 _manualTestFormAPS7100.OnSuspendModbusPolling = () => { _modbusService?.SuspendPolling(); LogMessage("ℹ Modbus 服务轮询已暂停"); }; _manualTestFormAPS7100.OnResumeModbusPolling = () => { _modbusService?.ResumePolling(); LogMessage("ℹ Modbus 服务轮询已恢复"); }; _manualTestFormAPS7100.FormClosed += (s, args) => { // 手动测试窗口关闭后,恢复主窗口测量和 Modbus 轮询 _modbusService?.ResumePolling(); if (_deviceAPS7100?.IsConnected == true || _devicePSW250?.IsConnected == true) { _measureTimer.Start(); LogMessage("ℹ 主窗口测量已恢复"); } }; _manualTestFormAPS7100.Show(); } else { _manualTestFormAPS7100.Focus(); } } private void menuManualTestPSW250_Click(object sender, EventArgs e) { if (_manualTestFormPSW250 == null || _manualTestFormPSW250.IsDisposed) { // 暂停主窗口测量定时器,避免与手动测试冲突 _measureTimer.Stop(); LogMessage("ℹ 主窗口测量已暂停(手动测试窗口已打开)"); _manualTestFormPSW250 = new ManualTestForm(_controllerPSW250, DeviceType.PSW250); // 设置 Modbus 轮询控制回调 _manualTestFormPSW250.OnSuspendModbusPolling = () => { _modbusService?.SuspendPolling(); LogMessage("ℹ Modbus 服务轮询已暂停"); }; _manualTestFormPSW250.OnResumeModbusPolling = () => { _modbusService?.ResumePolling(); LogMessage("ℹ Modbus 服务轮询已恢复"); }; _manualTestFormPSW250.FormClosed += (s, args) => { // 手动测试窗口关闭后,恢复主窗口测量和 Modbus 轮询 _modbusService?.ResumePolling(); if (_deviceAPS7100?.IsConnected == true || _devicePSW250?.IsConnected == true) { _measureTimer.Start(); LogMessage("ℹ 主窗口测量已恢复"); } }; _manualTestFormPSW250.Show(); } else { _manualTestFormPSW250.Focus(); } } private void menuModbusConfig_Click(object sender, EventArgs e) { using (var form = new ModbusConfigForm(_modbusMappings)) { if (form.ShowDialog() == DialogResult.OK) { _modbusMappings = form.Mappings; // 确定配置文件路径 string configPath; if (!string.IsNullOrEmpty(form.LastLoadedFilePath)) { // 使用导入的配置文件路径 configPath = form.LastLoadedFilePath; } else if (!string.IsNullOrEmpty(_appSettings.ModbusConfigPath)) { // 使用之前保存的路径 configPath = _appSettings.ModbusConfigPath; } else { // 使用默认路径 configPath = ConfigService.DefaultModbusConfigPath; } // 自动保存配置到文件 try { _configService.SaveModbusConfig(configPath, _modbusMappings); _appSettings.ModbusConfigPath = configPath; _configService.SaveAppSettings(_appSettings); LogMessage($"✓ Modbus 配置已保存: {configPath} ({_modbusMappings.Count} 条规则)"); } catch (Exception ex) { LogMessage($"⚠ 保存配置文件失败: {ex.Message}"); MessageBox.Show($"保存配置文件失败: {ex.Message}\n配置仅在本次运行有效。", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); } UpdateModbusStatus(); // 如果 Modbus 服务正在运行,提示用户重启 if (_modbusService != null && _modbusService.IsRunning) { if (MessageBox.Show("配置已更新,是否重启 Modbus 服务以使更改生效?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { StopModbusService(); StartModbusService(); } } } } } private void menuModbusStart_Click(object sender, EventArgs e) { ToggleModbusService(); } private void menuModbusSimulation_Click(object sender, EventArgs e) { bool isSimulation = menuModbusSimulation.Checked; if (_modbusService != null) { _modbusService.SimulationMode = isSimulation; } if (isSimulation) { LogMessage("✓ 仿真模式已开启 - 设备未连接时也会生成命令(仅记录,不发送)"); } else { LogMessage("✗ 仿真模式已关闭 - 仅在设备连接时发送命令"); } } private void menuModbusRegisterViewer_Click(object sender, EventArgs e) { if (_modbusService == null || !_modbusService.IsRunning) { MessageBox.Show("请先启动 Modbus 服务后再查看寄存器。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } using (var form = new RegisterViewerForm(_modbusService)) { form.ShowDialog(this); } } #endregion #region Modbus Service private void ToggleModbusService() { if (_modbusService != null && _modbusService.IsRunning) { StopModbusService(); } else { StartModbusService(); } } private void StartModbusService() { if (_modbusService != null && _modbusService.IsRunning) return; if (_modbusMappings.Count == 0) { LogMessage("Modbus 配置为空,无法启动服务"); return; } try { // 传入两个控制器,支持同时控制两个设备 _modbusService = new ModbusTcpServerService(_controllerAPS7100, _controllerPSW250); _modbusService.OnLog += (msg) => { if (!this.IsDisposed && this.IsHandleCreated) { this.Invoke((MethodInvoker)delegate { LogMessage(msg); }); } }; // 订阅客户端状态变化事件 _modbusService.OnClientStatusChanged += (clientCount, requestCount, lastRequest) => { if (!this.IsDisposed && this.IsHandleCreated) { this.Invoke((MethodInvoker)delegate { UpdateModbusClientStatus(clientCount, requestCount, lastRequest); }); } }; // 设置心跳寄存器地址 _modbusService.SetHeartbeatAddresses( _appSettings.HeartbeatRegisterAddress, _appSettings.DeviceStatusRegisterAddress); // 立即更新设备状态 bool aps7100Connected = _deviceAPS7100?.IsConnected == true; bool psw250Connected = _devicePSW250?.IsConnected == true; _modbusService.UpdateDeviceStatus(aps7100Connected, psw250Connected); // 设置仿真模式状态 _modbusService.SimulationMode = menuModbusSimulation.Checked; // 获取绑定IP,如果为空则使用0.0.0.0(所有接口) string bindIp = string.IsNullOrEmpty(_appSettings.ModbusBindIpAddress) ? "0.0.0.0" : _appSettings.ModbusBindIpAddress; _modbusService.Start(bindIp, _appSettings.ModbusPort, _modbusMappings); string bindInfo = bindIp == "0.0.0.0" ? "所有接口" : bindIp; LogMessage($"Modbus TCP 服务已启动,绑定: {bindInfo}:{_appSettings.ModbusPort}"); LogMessage($" 心跳寄存器地址: {_appSettings.HeartbeatRegisterAddress}"); LogMessage($" 设备状态寄存器地址: {_appSettings.DeviceStatusRegisterAddress}"); } catch (Exception ex) { LogMessage($"启动 Modbus 服务失败: {ex.Message}"); _modbusService?.Stop(); _modbusService = null; } UpdateModbusStatus(); } private void StopModbusService() { if (_modbusService == null) return; _modbusService.Stop(); _modbusService.Dispose(); _modbusService = null; LogMessage("Modbus TCP 服务已停止"); UpdateModbusStatus(); } #endregion #region Helper Methods private void UpdateManualTestController(DeviceType deviceType) { if (deviceType == DeviceType.APS7100) { _manualTestFormAPS7100?.UpdateController(_controllerAPS7100); } else { _manualTestFormPSW250?.UpdateController(_controllerPSW250); } } private void btnClearLog_Click(object sender, EventArgs e) => txtLog.Clear(); private void LogMessage(string msg) { if (this.InvokeRequired) { this.Invoke(new Action(LogMessage), msg); return; } txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {msg}\r\n"); } protected override void OnFormClosing(FormClosingEventArgs e) { // 如果是用户点击关闭按钮,最小化到托盘而不是退出 if (e.CloseReason == CloseReason.UserClosing) { e.Cancel = true; this.WindowState = FormWindowState.Minimized; return; } // 真正退出时清理资源 CleanupResources(); if (notifyIcon != null) { notifyIcon.Visible = false; notifyIcon.Dispose(); } base.OnFormClosing(e); } #endregion } }