10.20PLC+相机2.3视觉修改
This commit is contained in:
		
							
								
								
									
										316
									
								
								Check.Main/FrmMain.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								Check.Main/FrmMain.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,316 @@ | ||||
| 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(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user