317 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Check.Main.Camera;
 | ||
| using Check.Main.Common;
 | ||
| using Check.Main.UI;
 | ||
| using HslCommunication;
 | ||
| using System;
 | ||
| using System.Collections.Generic;
 | ||
| using System.ComponentModel;
 | ||
| using System.Data;
 | ||
| using System.Drawing;
 | ||
| using System.IO;
 | ||
| using System.Linq;
 | ||
| using System.Text;
 | ||
| using System.Threading.Tasks;
 | ||
| using System.Windows.Forms;
 | ||
| using WeifenLuo.WinFormsUI.Docking;
 | ||
| 
 | ||
| namespace Check.Main
 | ||
| {
 | ||
|     public partial class FrmMain : Form
 | ||
|     {
 | ||
|         // private FrmCamConfig _formCameraConfig;
 | ||
|         public static ModbusTcpService PlcClient;//定义全局PLC对象 --- 10.10添加①
 | ||
| 
 | ||
|         private FrmConfig _frmConfig;
 | ||
|         private FrmLog _formLog;
 | ||
|         private ThemeBase _theme = new VS2015BlueTheme(); // 外观主题
 | ||
|         public DockPanel MainDockPanel => this.dockPanel1;
 | ||
| 
 | ||
|         private FormControlPanel _formControlPanel;
 | ||
| 
 | ||
|         private FormStatistics _formStatistics;
 | ||
| 
 | ||
|         private readonly string _layoutConfigFile = Path.Combine(Application.StartupPath, "layout.xml");
 | ||
|         //用于反序列化时创建窗体的委托
 | ||
|         private DeserializeDockContent _deserializeDockContent;
 | ||
| 
 | ||
|         public FrmMain()
 | ||
|         {
 | ||
|             InitializeComponent();
 | ||
|             dockPanel1.Theme = _theme;
 | ||
|             IsMdiContainer = true;
 | ||
|             _deserializeDockContent = new DeserializeDockContent(GetContentFromPersistString);
 | ||
|         }
 | ||
| 
 | ||
|         //添加 PLC 初始化方法 --- 10.10添加②
 | ||
|         private async Task InitPlcConnection()
 | ||
|         {
 | ||
|             try
 | ||
|             {
 | ||
|                 PlcClient = new ModbusTcpService("192.168.1.88", 502); 
 | ||
|                // await PlcClient.ConnectAsync();
 | ||
|                 ThreadSafeLogger.Log("[PLC] 已成功连接到汇川PLC");
 | ||
|             }
 | ||
|             catch (Exception ex)
 | ||
|             {
 | ||
|                 ThreadSafeLogger.Log($"[PLC] 连接失败: {ex.Message}");
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         private async void FrmMain_Load(object sender, EventArgs e)//添加了一个async,原来是private void FrmMain_Load-----10.10修改
 | ||
|         {
 | ||
|             // 初始化PLC连接-----10.10添加
 | ||
|             await InitPlcConnection();
 | ||
| 
 | ||
|             ModbusTcpService easyPlcClient = new ModbusTcpService("192.168.1.88", 502, 1);
 | ||
|             //easyPlcClient.ConnectAsync();
 | ||
|             _frmConfig = new FrmConfig { Text = "主程序配置" };
 | ||
|             _formLog = new FrmLog { Text = "运行日志" };
 | ||
|             _formStatistics = new FormStatistics { Text = "生产统计" };
 | ||
|             _formControlPanel = new FormControlPanel { Text = "控制面板" };
 | ||
|             // 为每个子窗体订阅 FormClosing 事件
 | ||
|             _frmConfig.FormClosing += DockContent_FormClosing;
 | ||
|             _formLog.FormClosing += DockContent_FormClosing;
 | ||
|             _formStatistics.FormClosing += DockContent_FormClosing;
 | ||
|             _formControlPanel.FormClosing += DockContent_FormClosing;
 | ||
| 
 | ||
|             ThreadSafeLogger.Initialize();
 | ||
|             ThreadSafeLogger.OnLogMessage += (msg) => { _formLog.AddLog(msg); };
 | ||
| 
 | ||
|             // 2. 尝试加载布局文件
 | ||
|             if (File.Exists(_layoutConfigFile))
 | ||
|             {
 | ||
|                 try
 | ||
|                 {
 | ||
|                     // 使用委托加载布局
 | ||
|                     dockPanel1.LoadFromXml(_layoutConfigFile, _deserializeDockContent);
 | ||
|                     ThreadSafeLogger.Log("成功加载用户布局。");
 | ||
|                 }
 | ||
|                 catch (Exception ex)
 | ||
|                 {
 | ||
|                     ThreadSafeLogger.Log($"加载布局失败: {ex.Message}。将使用默认布局。");
 | ||
|                     // 如果加载失败,则使用默认布局
 | ||
|                     ShowDefaultLayout();
 | ||
|                 }
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 // 3. 如果布局文件不存在,则显示默认布局
 | ||
|                 ThreadSafeLogger.Log("未找到布局文件,使用默认布局。");
 | ||
|                 ShowDefaultLayout();
 | ||
|             }
 | ||
|         }
 | ||
|         /// <summary>
 | ||
|         /// 将相机名称添加到状态栏
 | ||
|         /// </summary>
 | ||
|         public void AddCameraToStatusStrip(string name)
 | ||
|         {
 | ||
|             if (statusStrip1.InvokeRequired)
 | ||
|             {
 | ||
|                 statusStrip1.Invoke(new Action(() => AddCameraToStatusStrip(name)));
 | ||
|                 return;
 | ||
|             }
 | ||
|             var label = new ToolStripStatusLabel(name)
 | ||
|             {
 | ||
|                 Name = "status_" + name,
 | ||
|                 BorderSides = ToolStripStatusLabelBorderSides.All,
 | ||
|                 Spring = false,
 | ||
|             };
 | ||
|             label.Click += StatusLabel_Click;
 | ||
|             statusStrip1.Items.Add(label);
 | ||
|         }
 | ||
|         private void 控制面板ToolStripMenuItem_Click(object sender, EventArgs e)
 | ||
|         {
 | ||
|             if (_formControlPanel == null || _formControlPanel.IsDisposed)
 | ||
|             {
 | ||
|                 _formControlPanel = new FormControlPanel { Text = "控制面板" };
 | ||
|                 _formControlPanel.FormClosing += DockContent_FormClosing;
 | ||
|             }
 | ||
|             _formControlPanel.Show(this.dockPanel1);
 | ||
|         }
 | ||
|         /// <summary>
 | ||
|         /// 清空状态栏中的相机名称
 | ||
|         /// </summary>
 | ||
|         public void ClearStatusStrip()
 | ||
|         {
 | ||
|             if (statusStrip1.InvokeRequired)
 | ||
|             {
 | ||
|                 statusStrip1.Invoke(new Action(ClearStatusStrip));
 | ||
|                 return;
 | ||
|             }
 | ||
|             // 从后往前删,避免索引问题
 | ||
|             for (int i = statusStrip1.Items.Count - 1; i >= 0; i--)
 | ||
|             {
 | ||
|                 if (statusStrip1.Items[i] is ToolStripStatusLabel && statusStrip1.Items[i].Name.StartsWith("status_"))
 | ||
|                 {
 | ||
|                     statusStrip1.Items.RemoveAt(i);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         private void StatusLabel_Click(object sender, EventArgs e)
 | ||
|         {
 | ||
|             if (sender is ToolStripStatusLabel label)
 | ||
|             {
 | ||
|                 string cameraName = label.Text;
 | ||
|                 ThreadSafeLogger.Log($"用户点击状态栏标签: {cameraName}");
 | ||
| 
 | ||
|                 bool foundOriginal = false;
 | ||
|                 bool foundResult = false;
 | ||
| 
 | ||
|                 // 1. 尝试重新显示“原图”窗口
 | ||
|                 if (CameraManager.OriginalImageDisplays.TryGetValue(cameraName, out var originalDisplayForm))
 | ||
|                 {
 | ||
|                     // 使用 Show() 方法来重新激活或显示隐藏的窗口
 | ||
|                     // 传入 DockPanel 确保它知道在哪里显示
 | ||
|                     originalDisplayForm.Show(this.dockPanel1);
 | ||
|                     originalDisplayForm.Activate(); // 调用 Activate() 确保它成为当前焦点窗口
 | ||
|                     foundOriginal = true;
 | ||
|                 }
 | ||
| 
 | ||
|                 // 2. 尝试重新显示“结果”窗口
 | ||
|                 if (CameraManager.ResultImageDisplays.TryGetValue(cameraName, out var resultDisplayForm))
 | ||
|                 {
 | ||
|                     resultDisplayForm.Show(this.dockPanel1);
 | ||
|                     resultDisplayForm.Activate();
 | ||
|                     foundResult = true;
 | ||
|                 }
 | ||
| 
 | ||
|                 // 3. 提供反馈
 | ||
|                 if (foundOriginal || foundResult)
 | ||
|                 {
 | ||
|                     ThreadSafeLogger.Log($"已重新激活相机 '{cameraName}' 的显示窗口。");
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     ThreadSafeLogger.Log($"[警告] 未能找到相机 '{cameraName}' 对应的活动显示窗口。可能设备已关闭。");
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 根据持久化字符串创建或返回对应的窗体实例。
 | ||
|         /// 这是DockPanel Suite反序列化布局时需要的回调方法。
 | ||
|         /// </summary>
 | ||
|         /// <param name="persistString">在XML中代表一个窗体的唯一字符串(通常是其类型名)。</param>
 | ||
|         /// <returns>对应的窗体实例。</returns>
 | ||
|         private IDockContent GetContentFromPersistString(string persistString)
 | ||
|         {
 | ||
|             if (persistString == typeof(FormControlPanel).ToString())
 | ||
|                 return _formControlPanel;
 | ||
|             if (persistString == typeof(FrmConfig).ToString())
 | ||
|                 return _frmConfig;
 | ||
|             if (persistString == typeof(FrmLog).ToString())
 | ||
|                 return _formLog;
 | ||
|             if (persistString == typeof(FormStatistics).ToString())
 | ||
|                 return _formStatistics;
 | ||
| 
 | ||
|             // 对于图像显示窗口,由于它们是动态创建的,情况会更复杂。
 | ||
|             // 在当前设计中,我们不保存图像窗口的布局,它们会在应用相机配置时重新创建。
 | ||
|             // 如果需要保存它们,需要在CameraManager中管理它们的持久化字符串。
 | ||
|             // 目前的设计下,返回null是安全的。
 | ||
|             return null;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 显示程序的默认窗口布局。
 | ||
|         /// </summary>
 | ||
|         private void ShowDefaultLayout()
 | ||
|         {
 | ||
|             // 确保所有窗体都未被意外关闭
 | ||
|             if (_formControlPanel.IsDisposed) _formControlPanel = new FormControlPanel();
 | ||
|             if (_frmConfig.IsDisposed) _frmConfig = new FrmConfig();
 | ||
|             if (_formLog.IsDisposed) _formLog = new FrmLog();
 | ||
|             if (_formStatistics.IsDisposed) _formStatistics = new FormStatistics();
 | ||
| 
 | ||
|             // 显示默认窗口
 | ||
|             _frmConfig.Show(dockPanel1, DockState.DockLeft);
 | ||
|             _formLog.Show(dockPanel1, DockState.DockBottom);
 | ||
|             _formControlPanel.Show(dockPanel1, DockState.DockRight);
 | ||
|             _formStatistics.Show(dockPanel1, DockState.DockTop);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 【新增】保存当前窗口布局到XML文件。
 | ||
|         /// </summary>
 | ||
|         private void SaveLayout()
 | ||
|         {
 | ||
|             try
 | ||
|             {
 | ||
|                 // 将当前DockPanel的布局保存到指定文件
 | ||
|                 dockPanel1.SaveAsXml(_layoutConfigFile);
 | ||
|                 ThreadSafeLogger.Log("布局已成功保存!");
 | ||
|                 //MessageBox.Show("布局已成功保存!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
 | ||
|             }
 | ||
|             catch (Exception ex)
 | ||
|             {
 | ||
|                 ThreadSafeLogger.Log("保存布局失败: " + ex.Message);
 | ||
|                 //MessageBox.Show("保存布局失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
 | ||
|             }
 | ||
|         }
 | ||
|         private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
 | ||
|         {
 | ||
|             if (_formStatistics != null && !_formStatistics.IsDisposed)
 | ||
|             {
 | ||
|                 StatisticsExporter.ExportToExcel(_formStatistics.CurrentStatistics, "Shutdown");
 | ||
|             }
 | ||
| 
 | ||
|             CameraManager.Shutdown();
 | ||
|             ThreadSafeLogger.Shutdown();
 | ||
|         }
 | ||
| 
 | ||
|         private void ToolStripMenuSaveLayou_Click(object sender, EventArgs e)
 | ||
|         {
 | ||
|             SaveLayout();
 | ||
|         }
 | ||
| 
 | ||
|         private void 配置ToolStripMenuItem_Click(object sender, EventArgs e)
 | ||
|         {
 | ||
|             //_frmConfig.Show();
 | ||
|             // 如果窗体因意外被销毁,则重新创建它
 | ||
|             if (_frmConfig == null || _frmConfig.IsDisposed)
 | ||
|             {
 | ||
|                 _frmConfig = new FrmConfig() { Text = "主程序配置" };
 | ||
|                 _frmConfig.FormClosing += DockContent_FormClosing;
 | ||
|             }
 | ||
|             // 调用 Show() 会自动处理隐藏和显示逻辑
 | ||
|             _frmConfig.Show(this.dockPanel1);
 | ||
|         }
 | ||
|         private void 日志ToolStripMenuItem_Click(object sender, EventArgs e)
 | ||
|         {
 | ||
|             if (_formLog == null || _formLog.IsDisposed)
 | ||
|             {
 | ||
|                 _formLog = new FrmLog { Text = "运行日志" };
 | ||
|                 _formLog.FormClosing += DockContent_FormClosing;
 | ||
|             }
 | ||
|             _formLog.Show(this.dockPanel1);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 处理所有可停靠子窗体关闭事件的通用方法。
 | ||
|         /// 通过取消关闭并隐藏窗口,来实现“假关闭”,以便后续能重新显示。
 | ||
|         /// </summary>
 | ||
|         private void DockContent_FormClosing(object sender, FormClosingEventArgs e)
 | ||
|         {
 | ||
|             // 检查关闭原因是否为用户点击了关闭按钮
 | ||
|             if (e.CloseReason == CloseReason.UserClosing)
 | ||
|             {
 | ||
|                 // 1. 取消真正的关闭(Dispose)操作,防止窗体被销毁
 | ||
|                 e.Cancel = true;
 | ||
| 
 | ||
|                 // 2. 将窗口隐藏起来
 | ||
|                 // 我们需要将 sender 转换为 DockContent 类型来访问 Hide() 方法
 | ||
|                 if (sender is DockContent dockContent)
 | ||
|                 {
 | ||
|                     if (dockContent is FormImageDisplay imageDisplay)
 | ||
|                     {
 | ||
|                         ThreadSafeLogger.Log($"用户关闭了窗口 '{imageDisplay.Text}',已将其隐藏。");
 | ||
|                     }
 | ||
|                     dockContent.Hide();
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|        
 | ||
|     }
 | ||
| }
 |