using AntdUI; using AntdUI.Svg; using DH.Commons.Base; using DH.Commons.Enums; using DH.Commons.Helper; using DH.Commons.Models; using DH.Devices.Camera; using DH.Devices.Motion; using DH.Devices.PLC; using DH.Devices.Vision; using DH.RBAC.Model.Sys; using DHSoftware.Languages; using DHSoftware.Models; using DHSoftware.Utils; using DHSoftware.Views; using DVPCameraType; using HalconDotNet; using Microsoft.VisualBasic.Logging; using Microsoft.Win32; using OpenCvSharp; using SqlSugar; using System; using System.CodeDom; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using XKRS.UI.Device.Winform; using static AntdUI.Math3D; using static DH.Commons.Enums.EnumHelper; using Button = System.Windows.Forms.Button; using Camera = DHSoftware.Models.Camera; using Label = AntdUI.Label; using LogLevel = DH.Commons.Enums.EnumHelper.LogLevel; using Point = System.Drawing.Point; using ResultState = DH.Commons.Base.ResultState; using Timer = System.Threading.Timer; namespace DHSoftware { public partial class MainWindow : AntdUI.Window { private Dictionary> _cameraRelatedDetectionDict = null; public event Action OnLog; public List CameraSummaries { get; set; } = new List(); public List ProductSummaries = new List(); static object _productSummaryLock = new object(); public event Action OnUpdateResult; public event Action OnUpdateCamResult; public List ButtonPermissionList { set { lbName.Text = DH.RBAC.Common.GlobalConfig.CurrentUser.Account; List list = value; SetPermission(list, this.Controls); } } public void ResetAllCameraCounts() { CameraSummaries.ForEach(camera => { camera.OKCount = 0; camera.NGCount = 0; camera.TiggerCount = 0; }); } private void SetPermission(List list, Control.ControlCollection controls) { foreach (Control control in controls) { if (control.HasChildren) { SetPermission(list, control.Controls); continue; } if (control is AntdUI.Button button) { HandleButtonVisibility(button, list); continue; } if (control is AntdUI.Label label) { HandleLabelVisibility(label, list); continue; } if (control is AntdUI.Segmented segmented && control.Name == "segmented1") { HandleSegmentedItems(segmented, list); } } } /// /// 处理 Segmented 控件的权限逻辑 /// private void HandleSegmentedItems(Segmented segmented, List permissions) { SysPermission permission = permissions.FirstOrDefault(p => p.EnCode == itemToHide.ID); bool itemExists = segmented.Items.Contains(itemToHide); if (permission != null) { if (!itemExists) { segmented.Items.Insert(4, itemToHide); } } else { if (itemExists) { segmented.Items.Remove(itemToHide); } } } /// /// 处理 Button 可见性 /// private void HandleButtonVisibility(AntdUI.Button button, List permissions) { if (button.Tag is string tag && !string.IsNullOrEmpty(tag)) { bool hasPermission = permissions.Any(p => p.EnCode == tag); button.Visible = hasPermission; } } /// /// 处理 Label 可见性 /// private void HandleLabelVisibility(AntdUI.Label label, List permissions) { if (label.Tag is string tag && !string.IsNullOrEmpty(tag)) { bool hasPermission = permissions.Any(p => p.EnCode == tag); label.Visible = hasPermission; } } public MainWindow() { InitializeComponent(); //refreshTimer.Start(); //初始化数据 InitData(); //绑定事件 BindEventHandler(); //UserConfigFrm userControlFrm = new UserConfigFrm(); //userControlFrm.Window = this; //userControlFrm.Dock = DockStyle.Fill; //tabPage2.Controls.Add(userControlFrm); } //#region OEE // public event Action OnProcessRunStateChanged; #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public virtual void Set(ref T field, T newValue, [CallerMemberName] string propName = null) { if (!field.Equals(newValue)) { field = newValue; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } } #endregion #region Properties private int uph = 0; private int upm = 0; private DateTime? startTime = null; private TimeSpan runTime = new TimeSpan(); private TimeSpan idleTime = new TimeSpan(); private TimeSpan downTime = new TimeSpan(); private TimeSpan totalTime = new TimeSpan(); private int qty_OEE = 0; private int qty_OEE_OK = 0; private float oee = 0; public int UPH { get => uph; set => Set(ref uph, value); } public int UPM { get => upm; set => Set(ref upm, value); } public DateTime? StartTime { get => startTime; set => Set(ref startTime, value); } /// /// 有效运行时间 /// public TimeSpan RunTime { get => runTime; set => Set(ref runTime, value); } /// /// 空闲待机时间 /// public TimeSpan IdleTime { get => idleTime; set => Set(ref idleTime, value); } /// /// 故障宕机时间 /// public TimeSpan DownTime { get => downTime; set => Set(ref downTime, value); } /// /// 总开机时间 /// public TimeSpan TotalTime { get => totalTime; set => Set(ref totalTime, value); } public float OEE { get => oee; set => Set(ref oee, value); } #endregion #region Timer System.Threading.Timer _runTimer = null; System.Threading.Timer _idleTimer = null; System.Threading.Timer _downTimer = null; System.Threading.Timer _checkIdleTimer = null; //System.Threading.Timer _calculateTimer = null; private RunState? currentState = null; public RunState? CurrentState { get => currentState; set { if (value != null) { if (value.Value == RunState.Running) { _checkIdleTimer?.Change(10 * 1000, -1); } if (currentState != value.Value) { switch (currentState) { case RunState.Idle: //AddRunEventInBuffer(DateTime.Now, RunEvent_EventType.Idle, false); break; case RunState.Down: //AddRunEventInBuffer(DateTime.Now, RunEvent_EventType.Down, false); break; } switch (value.Value) { case RunState.Stop: _runTimer?.Change(-1, -1); _idleTimer?.Change(-1, -1); _downTimer?.Change(-1, -1); break; case RunState.Running: _idleTimer?.Change(-1, -1); _downTimer?.Change(-1, -1); _runTimer?.Change(0, 1000); break; case RunState.Idle: _runTimer?.Change(-1, -1); _downTimer?.Change(-1, -1); _idleTimer?.Change(0, 1000); //AddRunEventInBuffer(DateTime.Now, RunEvent_EventType.Idle, true); break; case RunState.Down: _idleTimer?.Change(-1, -1); _runTimer?.Change(-1, -1); _downTimer?.Change(0, 1000); //AddRunEventInBuffer(DateTime.Now, RunEvent_EventType.Down, true); break; } currentState = value; } } } } private void CheckIdleTimeStart(object state) { if (CurrentState == RunState.Running) { RunTime = RunTime.Add(new TimeSpan(0, 0, 0 - 10)); IdleTime = IdleTime.Add(new TimeSpan(0, 0, 10)); CurrentState = RunState.Idle; } } public void InitialOEEStatistic() { InitialStatisticTimers(); ResetOEETimeDistribute(); ResetProductSummaries(); } public void ResetProductSummaries() { ProductSummaries = new List(); } private void InitialStatisticTimers() { if (_checkIdleTimer == null) { _checkIdleTimer = new Timer(new TimerCallback(CheckIdleTimeStart), null, -1, -1); } if (_runTimer == null) { _runTimer = new System.Threading.Timer(RunTimerCallBack, null, -1, -1); } if (_idleTimer == null) { _idleTimer = new System.Threading.Timer(IdleTimerCallBack, null, -1, -1); } if (_downTimer == null) { _downTimer = new System.Threading.Timer(DownTimerCallBack, null, -1, -1); } StartTime = DateTime.Now; DownTime = IdleTime = RunTime = new TimeSpan(0, 0, 0); CurrentState = RunState.Running; } private void DownTimerCallBack(object state) { DownTime = DownTime.Add(new TimeSpan(0, 0, 1)); GetTotalTime(); } private void IdleTimerCallBack(object state) { IdleTime = IdleTime.Add(new TimeSpan(0, 0, 1)); GetTotalTime(); } private void RunTimerCallBack(object state) { RunTime = RunTime.Add(new TimeSpan(0, 0, 1)); GetTotalTime(); } private void GetTotalTime() { TotalTime = RunTime + IdleTime + DownTime; } public void ResetOEETimeDistribute() { StartTime = DateTime.Now; DownTime = IdleTime = RunTime = new TimeSpan(0, 0, 0); ProductNum_Total = ProductNum_OK = 0; } public void CloseStatisticTimers() { CloseTimer(ref _checkIdleTimer); CloseTimer(ref _runTimer); CloseTimer(ref _idleTimer); CloseTimer(ref _downTimer); CurrentState = RunState.Stop; } private void CloseTimer(ref System.Threading.Timer timer) { timer?.Change(-1, -1); timer?.Dispose(); timer = null; } #endregion #region CameraSum private void InitialCameraSumsView() { dgvCamreaNums.Columns.Clear(); // 添加 CCD 列 dgvCamreaNums.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "CCD", DataPropertyName = "CameraName" }); // 添加 触发数 列 var TiggerCountColumn = new DataGridViewTextBoxColumn { HeaderText = "触发数", DataPropertyName = "TiggerCount" }; dgvCamreaNums.Columns.Add(TiggerCountColumn); // 添加 合格 列 var okColumn = new DataGridViewTextBoxColumn { HeaderText = "合格", DataPropertyName = "OKCount" }; okColumn.DefaultCellStyle.ForeColor = Color.Green; // 设置背景为绿色 dgvCamreaNums.Columns.Add(okColumn); // 添加 不合格 列 var ngColumn = new DataGridViewTextBoxColumn { HeaderText = "不合格", DataPropertyName = "NGCount" }; ngColumn.DefaultCellStyle.ForeColor = Color.LightCoral; // 设置背景为红色 dgvCamreaNums.Columns.Add(ngColumn); // 添加 总数 列 dgvCamreaNums.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "总数", DataPropertyName = "TotalCount" }); // 添加 良率 列 dgvCamreaNums.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "良率", DataPropertyName = "YieldStr" }); dgvCamreaNums.AutoGenerateColumns = false; dgvCamreaNums.DataSource = new BindingList(CameraSummaries); } #endregion /// /// 窗体对象实例 /// private static MainWindow _instance; internal static MainWindow Instance { get { if (_instance == null) _instance = new MainWindow(); return _instance; } } private SegmentedItem itemToHide; private void InitData() { itemToHide = segmented1.Items[4]; segmented1.Items.Remove(itemToHide); } public void ConnectCamera() { Cameras.Clear(); HKCameras.Clear(); if (ConfigModel.CameraBaseList.Count > 0) { for (int i = 0; i < ConfigModel.CameraBaseList.Count; i++) { var cameraBase = ConfigModel.CameraBaseList[i]; if (cameraBase.CamType == EnumCamType.度申Do3think) { Do3ThinkCamera cam = new Do3ThinkCamera(); cam.IsSavePicEnabled = cameraBase.IsSavePicEnabled; cam.CameraName = cameraBase.CameraName; cam.CameraIP = cameraBase.CameraIP; cam.IsEnabled = cameraBase.IsEnabled; cam.IsZoomCamera = cameraBase.IsZoomCamera; cam.Exposure = cameraBase.Exposure; cam.Gain = cameraBase.Gain; cam.RotateImage = cameraBase.RotateImage; cam.IsAllPicEnabled=cameraBase.IsAllPicEnabled; cam.ROIX= cameraBase.ROIX; cam.ROIH= cameraBase.ROIH; cam.ROIW= cameraBase.ROIW; cam.ROIY=cameraBase.ROIY; cam.ImageSaveDirectory = Path.Combine("D://Projects//Images", cameraBase.CameraName); Cameras.Add(cam); if (cameraBase.IsEnabled) { cam.OnLog -= _visionEngine_OnLog; cam.OnLog += _visionEngine_OnLog; cam.OnHImageOutput += OnCameraHImageOutput; Button CamLabel = new Button(); CamLabel.Name = cameraBase.CameraName; CamLabel.Text = cameraBase.CameraName; // 关键1:必须有文本 CamLabel.AutoSize = true; CamLabel.Size = new System.Drawing.Size(20, 20); // 关键2:自动调整大小 CamLabel.Location = new Point(20 + 50 * i, 12); // 关键3:明确位置 if (cam.CameraConnect()) CamLabel.BackColor = Color.Green; // 关键4:避免透明 else CamLabel.BackColor = Color.Yellow; // 关键4:避免透明 CamLabel.ForeColor = Color.Black; // 关键4:避免透明 CamLabel.Font = new Font("Microsoft YaHei", 9); // 可选:字体 // 关键5:确保添加到父控件 if (pageHeader1 != null && !pageHeader1.Controls.Contains(CamLabel)) { pageHeader1.Controls.Add(CamLabel); } } } else if (cameraBase.CamType == EnumCamType.海康hik) { HikVisionCamera cam = new HikVisionCamera(); cam.CameraName = cameraBase.CameraName; cam.CameraIP = cameraBase.CameraIP; cam.IsEnabled = cameraBase.IsEnabled; HKCameras.Add(cam); // cam.CameraConnect(); //cam.OnHImageOutput += OnCameraHImageOutput; } } } } public void ConnectPLC() { try { if (ConfigModel.PLCBaseList.Count > 0) { for (int i = 0; i < ConfigModel.PLCBaseList.Count; i++) { var plcBase = ConfigModel.PLCBaseList[i]; if (plcBase.PLCType == EnumPLCType.信捷XC网口 || plcBase.PLCType == EnumPLCType.信捷XD网口) { PLC.IP = plcBase.IP; PLC.PLCType = plcBase.PLCType; PLC.Enable = plcBase.Enable; PLC.PLCName = plcBase.PLCName; PLC.PLCItemList = plcBase.PLCItemList; PLC.Port = plcBase.Port; PLC.OnLog -= _visionEngine_OnLog; PLC.OnLog += _visionEngine_OnLog; if (PLC.Enable) { PLC.PLCConnect(); Button CamLabel = new Button(); CamLabel.Name = PLC.PLCName; CamLabel.Text = PLC.PLCName; // 关键1:必须有文本 CamLabel.AutoSize = true; CamLabel.Size = new System.Drawing.Size(20, 20); // 关键2:自动调整大小 CamLabel.Location = new Point(20 + 50 * (i + ConfigModel.CameraBaseList.Count), 12); // 关键3:明确位置 if (PLC.Connected) CamLabel.BackColor = Color.Green; // 关键4:避免透明 else CamLabel.BackColor = Color.Yellow; // 关键4:避免透明 CamLabel.ForeColor = Color.Black; // 关键4:避免透明 //CamLabel.ForeColor = Color.Green; // 关键4:避免透明 CamLabel.Font = new Font("Microsoft YaHei", 9); // 可选:字体 // 关键5:确保添加到父控件 if (pageHeader1 != null && !pageHeader1.Controls.Contains(CamLabel)) { pageHeader1.Controls.Add(CamLabel); } } } } } } catch (Exception ex) { } } FrmLog frmLog; public void InitModel() { Dectection.Clear(); _cameraRelatedDetectionDict = new(); if (ConfigModel.DetectionList.Count > 0) { for (int i = 0; i < ConfigModel.DetectionList.Count; i++) { DetectionConfig detectionConfig = ConfigModel.DetectionList[i]; var detection = ConfigModel.DetectionList[i]; detectionConfig.CameraCollects = detection.CameraCollects; detectionConfig.ModelconfThreshold = detection.ModelconfThreshold; detectionConfig.ModelWidth = detection.ModelWidth; detectionConfig.ModelHeight = detection.ModelHeight; detectionConfig.In_lable_path = detection.In_lable_path; detectionConfig.IsEnabled = detection.IsEnabled; detectionConfig.ImageSaveDirectory = "D://Projects//Images"; detectionConfig.ShowLocation.X = (i + 1) % 5 + (i + 1) / 5; // detectionConfig.ShowLocation.X = detection.ShowLocation.X; detectionConfig.ShowLocation.Y = (i + 1) / 5 + 1; // detectionConfig.ShowLocation.Y = detection.ShowLocation.Y; DetectionConfigs.Add(detectionConfig); } } DetectionConfigs.ForEach(detection => { detection.CameraCollects.ForEach(cam => { List Dets = new List { detection.Id }; if (!_cameraRelatedDetectionDict.ContainsKey(cam.CameraSourceId)) { _cameraRelatedDetectionDict.Add(cam.CameraSourceId, Dets); } else { _cameraRelatedDetectionDict[cam.CameraSourceId].Add(detection.Id); } } ); }); string inferenceDevice = "CPU"; frmLog = new FrmLog(); frmLog.Dock = DockStyle.Fill; pnlLog.Controls.Add(frmLog); // _visionEngine = new SimboVisionDriver(); _visionEngine.DetectionConfigs = DetectionConfigs; _visionEngine.LoggerHelper.LogPath = "D://PROJECTS//Logs//"; _visionEngine.LoggerHelper.LogPrefix = "Vision"; _visionEngine.OnLog += _visionEngine_OnLog; //初始化模型 加载模型 _visionEngine.Init(); CtrlVisionRunBase ctrlVisionRun = new CtrlVisionRunBase(_visionEngine); ctrlVisionRun.Dock = DockStyle.Fill; tabImgDisplay.Controls.Add(ctrlVisionRun); } private void _visionEngine_OnLog(LogMsg msg) { //OnLog?.Invoke(msg); LogDisplay(msg); } private void LogDisplay(LogMsg msg) { //frmLog?.LogDisplay(msg); frmLog?.AddLog(msg); } public LoggerHelper LoggerHelper { get; set; } = new LoggerHelper(); public virtual void LogAsync(LogMsg msg) { //if (IConfig != null) //{ // LoggerHelper.SetLogLevel(IConfig.DefaultLogLevel); //} //else //{ // LoggerHelper.SetLogLevel(LogLevel.Assist); //} msg.ThreadId = Thread.CurrentThread.ManagedThreadId; OnLog?.Invoke(msg); //if (IConfig?.IsLogEnabled ?? true) //{ LoggerHelper.LogAsync(msg); //} } public virtual void LogAsync(DateTime dt, LogLevel logLevel, string msg) { LogAsync(new LogMsg(dt, logLevel, msg)); } System.Windows.Forms.Timer _refreshUITimer = new System.Windows.Forms.Timer(); private void BindEventHandler() { btnAddProject.Click += BtnAddProject_Click; btnDeleteProject.Click += BtnDeleteProject_Click; btnLoadProject.Click += BtnLoadProject_Click; LoggerHelper.LogPath = "D://PROJECTS//Logs//"; LoggerHelper.LogPrefix = "Process"; OnLog -= LogDisplay; OnLog += LogDisplay; OnUpdateCamResult -= UpdateCamResult; OnUpdateCamResult += UpdateCamResult; OnUpdateResult -= UpdateResult; OnUpdateResult += UpdateResult; Load += (s, e) => { _refreshUITimer.Interval = 1000; _refreshUITimer.Tick += _refreshUITimer_Tick; _refreshUITimer.Enabled = true; }; lbInBackend.Click += LbInBackend_Click; } private void LbInBackend_Click(object? sender, EventArgs e) { DH.RBAC.RBACWindow.Instance.Show(); } private void _refreshUITimer_Tick(object sender, EventArgs e) { _refreshUITimer.Enabled = false; if (this != null) { lblStartTime.Text = StartTime == null ? "" : StartTime.Value.ToString("yyyy/MM/dd HH:mm:ss"); lblTotalTime.Text = TotalTime.ToString(); // 运行时间 // lblRunTime.Text = RunTime.ToString(); // 有效时间 // lblIdleTime.Text = ProcessControl.IdleTime.ToString(); // 空闲时间 // lblDownTime.Text = ProcessControl.DownTime.ToString(); // 宕机时间 lblOEE_Total.Text = ProductNum_Total.ToString(); // lblOEE_OK.Text = ProcessControl.ProductNum_OK.ToString(); } _refreshUITimer.Enabled = true; } private void UpdateCamResult(DateTime updateTime, object objData, string customMessage) { this.Invoke(new Action(() => { BindingList cameraSummaries = new BindingList(CameraSummaries); dgvCamreaNums.DataSource = cameraSummaries; })); } private void UpdateResult(DateTime updateTime, object objData, string result) { this.Invoke(new Action(() => { //if (dgvProductNums.Rows.Count > 0) //{ // dgvProductNums.Height = dgvProductNums.Rows[0].Height * dgvProductNums.Rows.Count + 15; //} //else //{ // dgvProductNums.Height = 35; //} //lblOEE_Rate.Text = ProcessControl.OEE.ToString("f2") + " %"; lblUPH.Text = UPM.ToString(); })); } public void LoadScheme() { try { // 初始化方案配置(会自动创建默认方案) SchemeHelper.Initialize(); // 读取方案列表 var schemes = SchemeHelper.GetAllSchemes(); sltProjects.Items.Clear(); // 绑定方案到下拉列表 foreach (var scheme in schemes) { sltProjects.Items.Add(scheme); } // 设置当前选中的方案 string currentScheme = SchemeHelper.GetCurrentScheme(); sltProjects.SelectedValue = currentScheme; SystemModel.CurrentScheme = currentScheme; // 加载当前方案配置 ConfigHelper.LoadConfig(); } catch (Exception ex) { AntdUI.Message.error(this, ex.Message, autoClose: 3); } } private void BtnDeleteProject_Click(object? sender, EventArgs e) { try { if (sltProjects.Items.Count == 0 || sltProjects.SelectedIndex == -1) return; var result = AntdUI.Modal.open(this, "删除警告!", "确认要删除该方案吗?", TType.Warn); if (result == DialogResult.OK) { string schemeToDelete = sltProjects.Text; int selectedIndex = sltProjects.SelectedIndex; // 删除方案(SchemeHelper会自动处理当前方案的切换) SchemeHelper.DeleteScheme(schemeToDelete); ConfigHelper.DeleteSchemeConfig(schemeToDelete); // 刷新UI LoadScheme(); AntdUI.Message.success(this, $"删除方案{schemeToDelete}成功!", autoClose: 3); // 如果没有方案了,提示用户 if (sltProjects.Items.Count == 0) { AntdUI.Modal.open(this, "空方案警告!", "当前方案全部删除,需重启程序!", TType.Warn); } } } catch (Exception ex) { AntdUI.Message.error(this, ex.Message, autoClose: 3); } } private void BtnLoadProject_Click(object? sender, EventArgs e) { try { if (sltProjects.Items.Count == 0 || sltProjects.SelectedIndex == -1) return; string selectedScheme = sltProjects.Text; if (SystemModel.CurrentScheme == selectedScheme) { AntdUI.Message.warn(this, "当前已是该方案,无需重复载入!", autoClose: 3); return; } // 设置当前方案 SchemeHelper.SetCurrentScheme(selectedScheme); SystemModel.CurrentScheme = selectedScheme; // 加载配置 ConfigHelper.LoadConfig(); AntdUI.Message.success(this, $"载入方案{selectedScheme}成功!", autoClose: 3); } catch (Exception ex) { AntdUI.Message.error(this, ex.Message, autoClose: 3); } } private void BtnAddProject_Click(object? sender, EventArgs e) { try { var form = new AddSchemeControl(this, "新增方案操作") { Size = new System.Drawing.Size(400, 300) }; AntdUI.Modal.open(new AntdUI.Modal.Config(this, "", form, TType.None) { BtnHeight = 0, }); if (form.submit) { string schemeName = form.SchemeName; // 保存到方案配置 SchemeHelper.AddScheme(schemeName); // 根据选择初始化配置 if (form.NullScheme) { ConfigHelper.InitializeScheme(schemeName); } else { ConfigHelper.DeriveScheme(schemeName); } // 刷新UI LoadScheme(); AntdUI.Message.success(this, $"新增方案{schemeName}成功!", autoClose: 3); } } catch (Exception ex) { AntdUI.Message.error(this, ex.Message, autoClose: 3); } } public List HKCameras { get; } = new List(); public List Cameras { get; } = new List(); public Dictionary Dectection { get; } = new Dictionary(); public XinJEPLCTcpNet PLC { get; } = XinJEPLCTcpNet.Instance; private SLDMotion sLDMotion = new SLDMotion(); // 线程控制标志 private volatile bool _isRunning = false; private Thread _monitorThread; private void MainWindow_Load(object sender, EventArgs e) { //开启按钮监听 // 启动所有监控线程 StartAllMonitors(); } private void StartAllMonitors() { if (PLC.Connected) { if (_monitorThread == null || !_monitorThread.IsAlive) { //关闭按钮自身功能(比如按按钮转盘自己启动等) PLC.WriteBool("M40", true); _isRunning = true; _monitorThread = new Thread(MonitorPlcButtons); _monitorThread.IsBackground = true; // 后台线程 _monitorThread.Start(); } } } private void MonitorPlcButtons(object? obj) { while (_isRunning) { try { // 读取 PLC 输入点状态 bool startPressed = PLC.ReadBool("X3"); bool resetPressed = PLC.ReadBool("X4"); bool stopPressed = PLC.ReadBool("X5"); bool eStopPressed = PLC.ReadBool("X6"); // 处理按钮状态变化 if (startPressed) { PLC.TurnSpeed(0); PLC.TurnStart(false); HandleStartButton(); } if (resetPressed) { //ResetProcess(); } if (stopPressed) { HandleStopButton(); } if (eStopPressed) { //EmergencyStop(null, null, null); } Thread.Sleep(50); } catch (Exception ex) { _isRunning = false; // 记录错误并停止线程 // throw new ProcessException($"按钮监听线程:{ex.Message}"); } } } // 停止监听线程 private void StopMonitoring() { _isRunning = false; if (_monitorThread != null && _monitorThread.IsAlive) { _monitorThread.Join(1000); // 等待线程退出 } PLC.WriteBool("M40", false); } private bool _isClosing = false; // 状态标志 private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) { if (_isClosing) return; _isClosing = true; // 取消默认关闭行为 e.Cancel = true; // 立即隐藏主窗口 this.Hide(); // 显示关闭界面 CloseWindow.Instance.Show(); Thread.Sleep(200); try { // 执行关闭操作 foreach (var camera in Cameras) { camera.CameraDisConnect(); } foreach (var camera in HKCameras) { camera.CameraDisConnect(); } StopMonitoring(); if (PLC != null) { PLC.CloseProcess(); } _visionEngine.Stop();//释放模型 CloseWindow.Instance.Close();// 关闭提示窗口 //Application.Exit(); System.Environment.Exit(0); } catch (Exception ex) { CloseWindow.Instance.Close(); System.Environment.Exit(0); } } private void segmented1_SelectIndexChanged(object sender, EventArgs e) { // Get the index of the selected item int selectedIndex = segmented1.SelectIndex; // Handle each button based on its index switch (selectedIndex) { case 0: // "启动" (Start) HandleStartButton(); break; case 1: // "停止" (Stop) HandleStopButton(); break; case 2: // "复位" (Reset) HandleResetButton(); break; case 3: // "设置" (Settings) HandleLoginButton(); break; case 4: // "登录" (Login) HandleSettingsButton(); break; default: break; } segmented1.SelectIndex = -1; } private void segmented2_SelectIndexChanged(object sender, IntEventArgs e) { int selectedIndex = segmented2.SelectIndex; switch (selectedIndex) { case 0: HandleVisualLocalizationButton(); break; default: break; } segmented2.SelectIndex = -1; } public string BatchNO { get; set; } public bool CurrentMachine = false; public volatile int ProductNum_Total = 0; public volatile int ProductNum_OK = 0; private readonly object _cameraSummaryLock = new object(); public SimboVisionDriver? _visionEngine = null; public PLCBase? _PLCConfig = null; private List DetectionConfigs = new List(); private List SimboStationMLEngineList = new List(); private Dictionary HalconToolDict = new Dictionary(); public List RecongnitionLabelList { get; set; } = new List(); public DateTime ProcessstartTime; private void PrepareBatchNO() { BatchNO = string.IsNullOrEmpty(BatchNO) ? SystemModel.CurrentScheme + "-" + DateTime.Now.ToString("yyyyMMddHHmmss") : BatchNO; // DataSavePath = string.IsNullOrEmpty(DataSavePath) ? Path.Combine(X018PLCConfig.ImgDirectory, DateTime.Now.ToString("yyyyMMdd"), BatchNO) : DataSavePath; } private void HandleStartButton() { lock (_stopLock) { if (_isStopProcessing) return; _isStopProcessing = true; } try { if (SystemModel.CurrentStatus == EnumStatus.待机中) { InitialCameraSumsView(); LogAsync(DateTime.Now, LogLevel.Information, "流程启动中,请稍候..."); ResetAllCameraCounts(); //开始流程 StartProcess(); SystemModel.CurrentStatus = EnumStatus.运行中; this.BeginInvoke(new MethodInvoker(delegate () { tagMachineStatus.Type = TTypeMini.Success; tagMachineStatus.Text = "运行中"; })); LogAsync(DateTime.Now, LogLevel.Action, "流程启动完成!"); } else if (SystemModel.CurrentStatus == EnumStatus.运行中) { LogAsync(DateTime.Now, LogLevel.Warning, "设备正在运行,无需启动!"); } else if (SystemModel.CurrentStatus == EnumStatus.清料中) { LogAsync(DateTime.Now, LogLevel.Warning, "设备正在清料,请稍候!"); } else if (SystemModel.CurrentStatus == EnumStatus.警告) { LogAsync(DateTime.Now, LogLevel.Warning, "设备报警,请复位后重试!"); } else if (SystemModel.CurrentStatus == EnumStatus.异常) { LogAsync(DateTime.Now, LogLevel.Warning, "设备异常,请检查!"); } } finally { lock (_stopLock) { _isStopProcessing = false; } } } private async void StartProcess() { try { BatchNO = textBoxBatchNO.Text; textBoxBatchNO.ReadOnly = true; btnCreateBatchNO.Enabled = false; PrepareBatchNO();//生成批次号 ProcessstartTime = DateTime.Now; lblStartTime.Text = ProcessstartTime.ToString("yyyy-MM-dd HH:mm:ss"); //计数清零 PieceCount = 0; //CameraSummaries.Clear(); if (PLC?.Enable == true) { PLC.OnNewPieces -= MainMotion_NewPieces; PLC.OnNewPieces += MainMotion_NewPieces; } Cameras.ForEach(d => { if (d is CameraBase cam) { cam.SnapshotCount = 0; } }); //PrepareBatchNO(); // isInPositionChecking = false; //isFullTrayChecking = false; //队列清空 // var temp = new List(); // temp.AddRange(XKRSPLCConfig.TriggerConfigCollection); // temp.AddRange(XKRSPLCConfig.SnapshotTriggerConfigCollection); //temp.ForEach(t => //{ // var diskInfo = XKRSPLCConfig.DiskInfoList.FirstOrDefault(u => u.DiskName == t.DiskName); // if (diskInfo != null) // { // t.AxisNum = diskInfo.DiskAxisNum; // } // else // { // t.AxisNum = -1; // } //}); //if (temp.Any(u => u.AxisNum < 0)) //{ // LogAsync(DateTime.Now, LogLevel.Error, "触发信号归属转盘未确认"); // return new ProcessResponse(false); //} // _diskInfoListInOrder = XKRSPLCConfig.DiskInfoList.OrderBy(u => u.DiskSequence).ToList(); // var axisNumList = _diskInfoListInOrder.Select(u => u.DiskAxisNum).ToList(); /// PrepareMLEngine(); // if (_PLCConfig?.Enable == true) //挡料电机操作 // _PLC.FeedingMotor(_PLCConfig.CunToZeroSpeed, _PLCConfig.CunPos, _PLCConfig.CunSpeed, _PLCConfig.CunDirection); //流程开启操作配置 // ProcessInitialAction(); // if (_PLC?Enabled == true) //皮带 // _PLC.Belt(true); //DeviceCollection.ForEach(d => //{ // if (d is CameraBase c) // { // c.OnHImageOutput -= OnCameraHImageOutput; // c.OnHImageOutput += OnCameraHImageOutput; // c.SnapshotCount = 0; // } //}); _productLists.Clear(); #region 虚拟相机 //mOfflineImageTimer = new System.Timers.Timer(); //mOfflineImageTimer.Elapsed += OnEmitSerialPortAsync; //mOfflineImageTimer.Interval = 1000; //mOfflineImageTimer.Start(); #endregion 虚拟相机 ///这里会执行完成后,会造成后台线程断断续续,会造成界面UI卡顿 //var settings = ConfigModel.DetectionList.Where(u => u.IsEnabled && u.IsAddStation).ToList(); //if (settings != null) //{ // settings = settings.Where(s => s.IsEnabled).ToList(); // ProductBaseCount = settings.Count; // for (int i = 0; i < ProductBaseCount * ProductListMulti; i++) // { // ConcurrentDictionary products = new ConcurrentDictionary(); // _productLists.Add(products); // } //} await Task.Run(() => { var settings = ConfigModel.DetectionList.Where(u => u.IsEnabled && u.IsAddStation).ToList(); if (settings != null) { ProductBaseCount = settings.Count; _productLists.Clear(); // 清空旧数据 // 预分配列表容量(减少动态扩容开销) _productLists.Capacity = ProductBaseCount * ProductListMulti; for (int i = 0; i < ProductBaseCount * ProductListMulti; i++) { _productLists.Add(new ConcurrentDictionary()); } } }); // _MGSCameraList = DeviceCollection //.OfType() // 直接筛选出 MGSCameraDriver 类型的元素 //.Where(camera => camera.IConfig != null && camera.IConfig.IsEnabled) // 进一步筛选 IConfig 不为 null 且 IsEnabled 为 true //.ToList(); // ProductBaseCount = _MGSCameraList.Count; InitialOEEStatistic(); //流程执行时PLC PLC.StartProcess(); CurrentMachine = true; } catch (Exception ex) { } } private uint PieceCount = 0; private List> _productLists = new List>(); private int ProductListMulti = 1; private int ProductBaseCount = 0; private int PieceNumberToIndex(uint pn) { // 物料编号,取余 集合数量 int ret = (int)(pn % (ProductBaseCount * ProductListMulti)); return ret; } private DateTime _ctTime = DateTime.Now; public async void MainMotion_NewPieces(int axisIndex, uint pieceNumber) { //if (MachineState != MachineState.Running && MachineState != MachineState.Warning) //{ // return; //} PieceCount++; int index = PieceNumberToIndex(pieceNumber); // productDatas.Add(pData); //转盘2 的物料是不是重新覆盖之前的pDta if (axisIndex == 1) { ProductData pData = new ProductData("", pieceNumber, ProductBaseCount); _productLists[index][pieceNumber] = pData; LogAsync(DateTime.Now, LogLevel.Action, $">> 轴{axisIndex}新产品{pieceNumber}加入队列{index}----板卡计数{PieceCount}"); } DateTime dtNow = DateTime.Now; UpdateCT(null, (float)(dtNow - _ctTime).TotalSeconds); _ctTime = dtNow; } public async Task UpdateCT(object objData, float ctTime) { await Task.Run(() => { //OnUpdateCT?.Invoke(objData, ctTime); }); } /// /// 相机回调 /// /// /// /// private void OnCameraHImageOutput(DateTime dt, CameraBase camera, MatSet imageSet) { //if (camera.CameraName.Equals("cam1", StringComparison.OrdinalIgnoreCase)) //{ // Console.WriteLine(); //} //if (camera.CameraName.Equals("cam2", StringComparison.OrdinalIgnoreCase)) //{ // Console.WriteLine(); //} // 获取该相机的拍照计数 uint productNumber = (uint)camera.SnapshotCount; Task.Run(async () => { //using (Mat localImageSet = imageSet._mat.Clone()) // 复制 Mat 避免并发问题 { // imageSet?.Dispose(); // 拍照计数与物件编号一致,查找对应的产品 ProductData product = null; //内外壁模组多个相机的处理方法 //计算队列的方法不变 int index = PieceNumberToIndex(productNumber); // 找到产品存放在哪个队列里 ConcurrentDictionary tmpDic = _productLists[index]; try { int retryTimes = 100; while (product == null && retryTimes > 0) { if (tmpDic.ContainsKey(productNumber)) { product = tmpDic[productNumber]; } else { Thread.Sleep(20); //await Task.Delay(20); } retryTimes--; } // 如果产品为空,则销毁图片,提示错误 if (null == product) { List pnList = tmpDic.Keys.ToList(); string pnStr = ""; if (pnList != null && pnList.Count > 0) { pnStr = string.Join(",", pnList); } //LogAsync(DateTime.Now, LogLevel.Error, $"{camera.Name} 未找到产品,编号:{productNumber},队列{index}数量:{tmpDic.Count},列表:{pnStr}"); imageSet.Dispose(); return; } LogAsync(DateTime.Now, LogLevel.Information, $"{camera.CameraName} 找到产品{productNumber},队列{index}数量:{tmpDic.Count}"); if (!_cameraRelatedDetectionDict.ContainsKey(camera.CameraName)) { imageSet.Dispose(); LogAsync(DateTime.Now, LogLevel.Warning, $"{camera.CameraName} 找到产品{productNumber},但是没有推理1"); return; } double totalTime = 0.0; List resultStates = new List(); List? detectionDict = _cameraRelatedDetectionDict[camera.CameraName]; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < detectionDict.Count; i++) { string detectionId = detectionDict[i]; var tmpImgSet = camera.CopyImageSet(imageSet as MatSet); //imageSet // using (Mat inferenceImage = localImageSet.Clone()) // 仅在此处克隆,确保推理过程中 Mat 有独立副本 DetectStationResult temp1 = _visionEngine.RunInference(tmpImgSet, detectionId); resultStates.Add(temp1.ResultState); product.ResultCollection.Add(temp1); if (temp1 != null) { UpdateResultTigger(dt, temp1.DetectName, (int)productNumber); //if (tmpModuleData.CamIDs.Count == 1) //{ // UpdateResultoverride(dt, temp1.DetectName, resultStates, totalTime, _cameraRelatedDetectionDict.Keys.Count); //} //else //合图的合成一列 //{ // UpdateResultoverride(dt, temp1.DetectName, resultStates, totalTime, _cameraRelatedDetectionDict.Keys.Count); //} if (product.ResultCollection.Count != 0) UpdateResultoverride(dt, temp1.DetectName, resultStates, totalTime, _cameraRelatedDetectionDict.Keys.Count); } } stopwatch.Stop(); product.InferenceOne(); // LogAsync(DateTime.Now, LogLevel.Information, $"{camera.Name} 推理完成,产品{productNumber}"); if (!product.InferenceFinished()) { return; } #region 6. 统计产品结果 product.ProductResult = product.ResultCollection.Any(u => u.ResultState != ResultState.OK) ? ResultState.B_NG : ResultState.OK; product.ProductLabelCategory = product.ProductResult.GetEnumDescription(); product.ProductLabel = product.ProductResult.GetEnumDescription(); UpdateResultPro(DateTime.Now, null, product.ProductResult.GetEnumDescription()); LogAsync(DateTime.Now, LogLevel.Information, $"产品{product.PieceNumber}获取结果:{product.ProductResult} {(product.IsA2B ? "产品IsA2B" : "")}"); if (product.ProductResult == ResultState.OK) { PLC.Blowing(productNumber, 1); LogAsync(DateTime.Now, LogLevel.Action, $"产品{product.PieceNumber}PLC,OK吹气"); } else { PLC.Blowing(productNumber, 2); LogAsync(DateTime.Now, LogLevel.Action, $"产品{product.PieceNumber}PLC,NG吹气"); } #endregion 6. 统计产品结果 // 出列 ProductData temp = null; int tryTimes = 10; while (temp == null && tryTimes > 0) { if (tmpDic.TryRemove(productNumber, out temp)) { break; } tryTimes--; Thread.Sleep(5); } if (temp == null) { string logStr = $"{DateTime.Now}产品{productNumber}出列失败:true," + $"当前队列产品数量:{tmpDic.Count}"; this.BeginInvoke(new MethodInvoker(delegate () { // int currentScrollPosition = richTextBox1.GetPositionFromCharIndex(richTextBox1.TextLength).Y; // richTextBox1.AppendText(logStr); // 设置回原来的滚动位置 // richTextBox1.SelectionStart = richTextBox1.TextLength; //richTextBox1.ScrollToCaret(); })); } else { try { string logStr = $"{DateTime.Now}产品{productNumber}出列成功:true," + $"产品结果:{temp.ProductResult.GetEnumDescription()}," + $"当前队列产品数量:{tmpDic.Count}"; this.BeginInvoke(new MethodInvoker(delegate () { //int currentScrollPosition = richTextBox1.GetPositionFromCharIndex(richTextBox1.TextLength).Y; // richTextBox1.AppendText(logStr); // 设置回原来的滚动位置 // richTextBox1.SelectionStart = richTextBox1.TextLength; //richTextBox1.ScrollToCaret(); })); //重新生成实例 销毁之前的实例 var saveData = temp.GetProductData(); } catch (Exception) { } finally { // temp.Dispose(); temp = null; } } // UpdateCT((float)(dtNow - _ctTime).TotalSeconds); //_ctTime = dtNow; // }); } catch (Exception ex) { //LogAsync(DateTime.Now, LogLevel.Error, $"流程检测未捕获的异常:{ex.GetExceptionMessage()}"); product?.Dispose(); } } }); } public virtual void AddOKProduct(string resultStr) { if (resultStr.ToLower() == "ok") { ProductNum_OK++; } } public async Task UpdateResultTigger(DateTime dt, string objData, int _cameraDictCount) { CurrentState = RunState.Running; // 根据相机名称找到对应的信息(假设有一个字典或其他集合保存相机相关信息) var cameraName = objData; // 假设 CameraBase 有 Name 属性 if (string.IsNullOrEmpty(cameraName)) { throw new ArgumentException("相机名称不能为空"); } lock (_cameraSummaryLock) { // 查找或添加相机统计项 var summary = CameraSummaries.FirstOrDefault(c => c.CameraName == cameraName) ?? new CameraSummary { CameraName = cameraName }; if (!CameraSummaries.Contains(summary)) { CameraSummaries.Add(summary); } summary.TiggerCount = _cameraDictCount; } await Task.Run(() => { OnUpdateCamResult?.Invoke(dt, objData, ""); }); } public async Task UpdateResultoverride(DateTime dt, string objData, List resultStr, double total, int _cameraDictCount) { // CurrentState = RunState.Running; // 根据相机名称找到对应的信息(假设有一个字典或其他集合保存相机相关信息) var cameraName = objData; // 假设 CameraBase 有 Name 属性 if (string.IsNullOrEmpty(cameraName)) { throw new ArgumentException("相机名称不能为空"); } lock (_cameraSummaryLock) { // 查找或添加相机统计项 var summary = CameraSummaries.FirstOrDefault(c => c.CameraName == cameraName) ?? new CameraSummary { CameraName = cameraName }; if (!CameraSummaries.Contains(summary)) { CameraSummaries.Add(summary); } if (resultStr.Any(u => u.ToString().ToLower() == "ok")) { summary.OKCount++; } else /*if (resultStr.Equals("TBD", StringComparison.OrdinalIgnoreCase))*/ { summary.NGCount++; } } await Task.Run(() => { OnUpdateCamResult?.Invoke(dt, objData, ""); }); } public async Task UpdateResultPro(DateTime dt, object objData, string resultStr) { CurrentState = RunState.Running; ProductNum_Total++; AddOKProduct(resultStr); lock (_productSummaryLock) { var product = ProductSummaries.FirstOrDefault(u => u.ResultDesc == resultStr); if (product != null) { product.ProductAmount++; } else { product = new ProductSummary(); product.ResultDesc = resultStr; product.ProductAmount = 1; ProductSummaries.Add(product); } int totalNum = ProductSummaries.Sum(p => p.ProductAmount); ProductSummaries.ForEach(p => p.PercentStr = ((double)p.ProductAmount * 100.0 / totalNum).ToString("f2") + " %"); } CalculateOEE(); await Task.Run(() => { OnUpdateResult?.Invoke(dt, objData, resultStr); }); lock (_cameraSummaryLock) { // 查找或添加相机统计项 var summary = CameraSummaries.FirstOrDefault(c => c.CameraName == "合计") ?? new CameraSummary { CameraName = "合计" }; summary.OKCount = ProductNum_OK; summary.NGCount = ProductNum_Total - ProductNum_OK; if (!CameraSummaries.Contains(summary)) { CameraSummaries.Add(summary); } } await Task.Run(() => { OnUpdateCamResult?.Invoke(dt, objData, "合计"); }); } private readonly object _stopLock = new object(); // 锁对象 private bool _isStopProcessing = false; // 状态标志 private async void HandleStopButton() { // 通过锁和状态标志实现双重检查 lock (_stopLock) { if (_isStopProcessing) return; _isStopProcessing = true; } try { if (SystemModel.CurrentStatus == EnumStatus.待机中) { LogAsync(DateTime.Now, LogLevel.Warning, "设备待机中,无需停止!"); } else if (SystemModel.CurrentStatus == EnumStatus.运行中) { textBoxBatchNO.ReadOnly = false; btnCreateBatchNO.Enabled = true; // Cameras.Clear(); // Dectection.Clear(); // Add the code for the "停止" button click here PLC.StopProcess(); SystemModel.CurrentStatus = EnumStatus.待机中; this.BeginInvoke(new MethodInvoker(delegate () { tagMachineStatus.Type = TTypeMini.Primary; tagMachineStatus.Text = "待机中"; })); LogAsync(DateTime.Now, LogLevel.Action, $"流程停止!"); await Task.Run(async () => { await ExecuteClearDelayAsync(); }); } else if (SystemModel.CurrentStatus == EnumStatus.清料中) { LogAsync(DateTime.Now, LogLevel.Warning, "设备正在清料,请稍候!"); } else if (SystemModel.CurrentStatus == EnumStatus.警告) { LogAsync(DateTime.Now, LogLevel.Warning, "设备报警,请复位后重试!"); } else if (SystemModel.CurrentStatus == EnumStatus.异常) { LogAsync(DateTime.Now, LogLevel.Warning, "设备异常,请检查!"); } } finally { lock (_stopLock) { _isStopProcessing = false; } } //sLDMotion.Stop(); } private async Task ExecuteClearDelayAsync() { LogAsync(DateTime.Now, LogLevel.Action, $"转盘清料开始"); SystemModel.CurrentStatus = EnumStatus.清料中; this.BeginInvoke(new MethodInvoker(delegate () { tagMachineStatus.Type = TTypeMini.Warn; tagMachineStatus.Text = "清料中"; })); await PLC.ExecuteClearDelayAsync(); LogAsync(DateTime.Now, LogLevel.Action, $"转盘清料完成"); SystemModel.CurrentStatus = EnumStatus.待机中; this.BeginInvoke(new MethodInvoker(delegate () { tagMachineStatus.Type = TTypeMini.Primary; tagMachineStatus.Text = "待机中"; })); } public void CalculateOEE() { if (TotalTime.TotalHours == 0) { UPH = 0; UPM = 0; } else { UPH = (int)(ProductNum_Total / RunTime.TotalHours) + 100; UPM = (int)UPH / 60; } //TimeSpan timeSpan = DateTime.Now - ProcessstartTime; //UPH = (int)(ProductNum_Total / timeSpan.TotalHours) + 100; ////UPM = (int)UPH / 60; //this.BeginInvoke(new MethodInvoker(delegate () //{ // lblNowtime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); // lblUPH.Text = UPH.ToString(); // lblNum.Text = ProductNum_Total.ToString(); //})); } private void HandleResetButton() { // Add the code for the "复位" button click here MessageBox.Show("复位按钮按下"); } private void HandleSettingsButton() { // Add the code for the "设置" button click here SettingWindow.Instance.Show(); } private void HandleLoginButton() { // Add the code for the "登录" button click here LoginWindow.Instance.parentForm = this; LoginWindow.Instance.Show(); } private void HandleVisualLocalizationButton() { VisualLocalizationWindow.Instance.Show(); } private void btnCreateBatchNO_Click(object sender, EventArgs e) { textBoxBatchNO.Text = SystemModel.CurrentScheme + "-" + DateTime.Now.ToString("yyyyMMddHHmmss"); } } }