From 4b98b61f105820c019b2ea7d3bd62bd65c9c81e9 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Tue, 2 Dec 2025 14:23:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E7=89=88=E6=9C=AC=EF=BC=9A=20?= =?UTF-8?q?=E8=BF=94=E5=9B=9EURL=E9=93=BE=E6=8E=A5=EF=BC=8C=20=E5=90=8E?= =?UTF-8?q?=E9=9D=A2=E5=B0=B1=E4=B8=8D=E9=9C=80=E8=A6=81=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=9F=A5=E7=9C=8B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FATrace.OEMApp/FATrace.OEMApp.csproj | 1 - FATrace.OEMApp/MainApp.Designer.cs | 23 +- FATrace.OEMApp/MainApp.cs | 18 +- FATrace.OEMApp/MainApp.resx | 4 +- FATrace.OEMApp/Services/PLCDataService.cs | 291 ++++++++++++++++++++++ 5 files changed, 313 insertions(+), 24 deletions(-) create mode 100644 FATrace.OEMApp/Services/PLCDataService.cs diff --git a/FATrace.OEMApp/FATrace.OEMApp.csproj b/FATrace.OEMApp/FATrace.OEMApp.csproj index c4912f5..fcff596 100644 --- a/FATrace.OEMApp/FATrace.OEMApp.csproj +++ b/FATrace.OEMApp/FATrace.OEMApp.csproj @@ -433,7 +433,6 @@ - diff --git a/FATrace.OEMApp/MainApp.Designer.cs b/FATrace.OEMApp/MainApp.Designer.cs index 0dfc42b..1684c00 100644 --- a/FATrace.OEMApp/MainApp.Designer.cs +++ b/FATrace.OEMApp/MainApp.Designer.cs @@ -57,7 +57,6 @@ namespace FATrace.OEMApp label8 = new Label(); materialCard2 = new ReaLTaiizor.Controls.MaterialCard(); gridRULog = new DataGridView(); - ((System.ComponentModel.ISupportInitialize)gridRULog).BeginInit(); btnRawStopLoadVideo = new Button(); DownloadProgressBarMain = new ProgressBar(); btnTestAction = new Button(); @@ -91,6 +90,7 @@ namespace FATrace.OEMApp materialCard4.SuspendLayout(); materialCard3.SuspendLayout(); materialCard2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)gridRULog).BeginInit(); tabPage2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit(); materialCard1.SuspendLayout(); @@ -410,22 +410,21 @@ namespace FATrace.OEMApp // // gridRULog // - gridRULog = new DataGridView(); - gridRULog.Location = new Point(557, 9); - gridRULog.Name = "gridRULog"; - gridRULog.Size = new Size(1309, 338); - gridRULog.TabIndex = 8; gridRULog.AllowUserToAddRows = false; gridRULog.AllowUserToDeleteRows = false; + gridRULog.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + gridRULog.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; + gridRULog.Location = new Point(557, 9); + gridRULog.Name = "gridRULog"; gridRULog.ReadOnly = true; gridRULog.RowHeadersVisible = false; gridRULog.SelectionMode = DataGridViewSelectionMode.FullRowSelect; - gridRULog.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; - gridRULog.Anchor = ((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left) | AnchorStyles.Right; + gridRULog.Size = new Size(1309, 338); + gridRULog.TabIndex = 8; // // btnRawStopLoadVideo // - btnRawStopLoadVideo.Location = new Point(189, 223); + btnRawStopLoadVideo.Location = new Point(292, 271); btnRawStopLoadVideo.Name = "btnRawStopLoadVideo"; btnRawStopLoadVideo.Size = new Size(142, 44); btnRawStopLoadVideo.TabIndex = 7; @@ -435,14 +434,14 @@ namespace FATrace.OEMApp // // DownloadProgressBarMain // - DownloadProgressBarMain.Location = new Point(1, 324); + DownloadProgressBarMain.Location = new Point(7, 321); DownloadProgressBarMain.Name = "DownloadProgressBarMain"; DownloadProgressBarMain.Size = new Size(540, 23); DownloadProgressBarMain.TabIndex = 6; // // btnTestAction // - btnTestAction.Location = new Point(41, 223); + btnTestAction.Location = new Point(144, 271); btnTestAction.Name = "btnTestAction"; btnTestAction.Size = new Size(142, 44); btnTestAction.TabIndex = 5; @@ -746,13 +745,13 @@ namespace FATrace.OEMApp materialCard3.PerformLayout(); materialCard2.ResumeLayout(false); materialCard2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)gridRULog).EndInit(); tabPage2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit(); materialCard1.ResumeLayout(false); materialCard1.PerformLayout(); ((System.ComponentModel.ISupportInitialize)videoView1).EndInit(); tabPage3.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)gridRULog).EndInit(); ResumeLayout(false); PerformLayout(); } diff --git a/FATrace.OEMApp/MainApp.cs b/FATrace.OEMApp/MainApp.cs index 67deab5..db7ba5f 100644 --- a/FATrace.OEMApp/MainApp.cs +++ b/FATrace.OEMApp/MainApp.cs @@ -2,22 +2,23 @@ using FATrace.Com; using FATrace.HKNetLib.Hardware; using FATrace.HKNetLib.Wrapper; using FATrace.Model; -using LibVLCSharp.Shared; -using ReaLTaiizor.Forms; -using System.ComponentModel; using FATrace.OEMApp.Services; -using System.Threading; -using System.Threading.Tasks; +using LibVLCSharp.Shared; +using NLog; +using ReaLTaiizor.Forms; using System.Collections.Concurrent; -using System.Linq; +using System.ComponentModel; using TaskStatus = FATrace.Model.TaskStatus; -using System.Windows.Forms; -using System.Drawing; namespace FATrace.OEMApp { public partial class MainApp : MaterialForm { + /// + /// 日志 + /// + private Logger Logger { get; set; } = LogManager.GetCurrentClassLogger(); + /// /// 海康Camera 客户端 /// @@ -64,7 +65,6 @@ namespace FATrace.OEMApp //NVR登录 NVRLogin(); - InitMediaPlayer(); InitHistoryGridBinding(); diff --git a/FATrace.OEMApp/MainApp.resx b/FATrace.OEMApp/MainApp.resx index 0254f3b..c83729e 100644 --- a/FATrace.OEMApp/MainApp.resx +++ b/FATrace.OEMApp/MainApp.resx @@ -128,7 +128,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAaiEAAAJNU0Z0AUkBTAIBAQgB - AAFIAQEBSAEBARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAATADAAEBAQABIAYAATD/ + AAFQAQEBUAEBARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAATADAAEBAQABIAYAATD/ AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AFIAAdsBlgESAf8B2wGWARIB/wQAAxIBGANJAYgB 2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsB lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAHbAZYB @@ -217,7 +217,7 @@ 2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wMqAUAY AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B 2wGWARIB/xgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB - lgESAf8B2wGWARIB/wJ8AVkB+BAAAXMBbwFRAfcB2wGWARIB/wwAAzMBUAHbAZYBEgH/BAAB2wGWARIB + lgESAf8B2wGWARIB/wJ8AVoB+BAAAXEBbwFRAfcB2wGWARIB/wwAAzMBUAHbAZYBEgH/BAAB2wGWARIB /xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB lgESAf8MAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsB lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB diff --git a/FATrace.OEMApp/Services/PLCDataService.cs b/FATrace.OEMApp/Services/PLCDataService.cs new file mode 100644 index 0000000..bf40bd8 --- /dev/null +++ b/FATrace.OEMApp/Services/PLCDataService.cs @@ -0,0 +1,291 @@ +using FATrace.Com; +using HslCommunication; +using HslCommunication.Profinet.Keyence; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FATrace.OEMApp.Services +{ + public class PLCDataService + { + /// + /// 日志 + /// + private Logger Logger { get; set; } = LogManager.GetCurrentClassLogger(); + + public PLCDataService() + { + + + PLCLinkInitial(); + StartPlcScan(); + } + + /// + /// 扫码事件 + /// + public event EventHandler ScanCodeEventHandler; + + /// + /// PLC连接状态事件 + /// + public event EventHandler PlcConnectedEventHandler; + + /// + /// 基恩士通信组件 + /// + private KeyenceMcNet KeyencePlcMcNet { get; set; } = null; + + private bool _PlcConnected; + /// + /// PLC连接状态 + /// + public bool PlcConnected + { + get { return _PlcConnected; } + set + { + if (_PlcConnected != value) + { + if (value) + { + PlcConnectedEventHandler.Invoke(this, "OK"); + } + else + { + PlcConnectedEventHandler.Invoke(this, "NG"); + } + _PlcConnected = value; + } + } + } + + + // 扫描控制 + private CancellationTokenSource? _plcScanCts; + private Task? _plcScanTask; + private volatile bool _isScanning = false; + public bool IsScanning => _isScanning; + private bool _disposed = false; + + /// + /// PLC通信初始化 + /// + /// + /// + private void PLCLinkInitial() + { + try + { + // 读取 PLC 配置(大小写敏感,采用公共 ConfigHelper 并提供默认值) + string PLCIP = ConfigHelper.GetStringOrDefault("PLCIP", "192.0.1.10"); + int Port = ConfigHelper.GetIntOrDefault("PLCPort", 5000); + + //PLC通信的连接 + KeyencePlcMcNet = new KeyenceMcNet(); + KeyencePlcMcNet.IpAddress = PLCIP; + KeyencePlcMcNet.Port = Port; + KeyencePlcMcNet.ConnectClose(); + + KeyencePlcMcNet.ConnectTimeOut = 3000; // 连接3秒超时 + OperateResult connect = KeyencePlcMcNet.ConnectServer(); + if (connect.IsSuccess)//初始连接状态的显示判断 + { + Console.WriteLine($"PLC-连接 OK"); + PlcConnected = true; + } + else + { + MessageBox.Show(connect.Message + Environment.NewLine + "ErrorCode: " + connect.ErrorCode); + PlcConnected = false; + } + } + catch (Exception ex) + { + Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); + //insertLogToDBDelegate.BeginInvoke(1, "UpdateUIMethod异常", ex.Message.ToString() + ex.StackTrace.Substring(ex.StackTrace.Length - 40, 40), null, null); + PlcConnected = false; + } + + } + + /// + /// 启动PLC扫描。若已在扫描则忽略重复启动。 + /// + /// 扫描周期,若为 null 则读取 App.config 的 PLCScan(毫秒),默认600ms + /// 可选:外部取消令牌,用于联动取消 + public void StartPlcScan(TimeSpan? scanPeriod = null, CancellationToken externalToken = default) + { + if (_isScanning) return; + + var period = scanPeriod ?? ConfigHelper.GetTimeSpanOrDefault("PLCScan", TimeSpan.FromMilliseconds(600)); + + _plcScanCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken); + var token = _plcScanCts.Token; + _isScanning = true; + + _plcScanTask = Task.Run(() => PlcScanLoopAsync(period, token), token); + } + + /// + /// 停止PLC扫描,等待后台任务结束。 + /// + public async Task StopPlcScanAsync() + { + try + { + if (_plcScanCts != null) + { + _plcScanCts.Cancel(); + } + if (_plcScanTask != null) + { + try { await _plcScanTask.ConfigureAwait(false); } catch { /* ignore */ } + } + } + finally + { + _isScanning = false; + _plcScanTask = null; + _plcScanCts?.Dispose(); + _plcScanCts = null; + } + } + + /// + /// 扫描循环:按周期轮询PLC寄存器并更新模型。 + /// - 读取配置项 Addr_WeightPhotoEnable(默认 M100)为布尔量 + /// - 读失败时尝试重连一次 + /// + private async Task PlcScanLoopAsync(TimeSpan period, CancellationToken token) + { + string addrWeight = ConfigHelper.GetStringOrDefault("Addr_WeightPhotoEnable", "M100"); + bool? lastWeight = null; + + while (!token.IsCancellationRequested) + { + try + { + if (KeyencePlcMcNet == null) + { + PLCLinkInitial(); + } + + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + Logger.Error($"PLC 扫描异常: {ex}"); + } + finally + { + try + { + await Task.Delay(period, token).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + // 不允许从 finally 中 break/return, + // 这里吞掉异常,循环将在下一次 while 判断时依据 token 自动退出 + } + } + } + _isScanning = false; + } + + /// + /// 尝试重连 PLC(快速一次) + /// + private async Task TryReconnectAsync(CancellationToken token) + { + try + { + if (KeyencePlcMcNet == null) { PLCLinkInitial(); return; } + KeyencePlcMcNet.ConnectClose(); + await Task.Delay(100, token).ConfigureAwait(false); + KeyencePlcMcNet.ConnectTimeOut = 3000; + var ret = KeyencePlcMcNet.ConnectServer(); + if (!ret.IsSuccess) + { + Logger.Warn($"PLC重连失败: {ret.Message} (Code: {ret.ErrorCode})"); + PlcConnected = false; + } + else { PlcConnected = true; } + } + catch (Exception ex) + { + Logger.Error($"PLC重连异常: {ex.Message}"); + PlcConnected = false; + } + } + + /// + /// 释放资源,确保扫描停止并断开PLC连接。 + /// + public void Dispose() + { + if (_disposed) return; + _disposed = true; + try + { + // 停止扫描 + try { StopPlcScanAsync().GetAwaiter().GetResult(); } catch { } + + // 解除事件订阅(当前未订阅自定义事件,保留占位以便后续扩展) + // try { /* no event to unsubscribe */ } catch { } + + // 关闭PLC连接 + try { KeyencePlcMcNet?.ConnectClose(); } catch { } + } + finally + { + KeyencePlcMcNet = null; + } + } + + /// + /// 把字符串对调 + /// + public string RevData(string Code) + { + // 需求:PLC 字符串按“字”(2字节)进行高低字节对调,导致 ABCDEF -> BADCFE + // 纠正方式:按两个字符一组交换顺序,奇数长度保留最后一个字符 + if (string.IsNullOrEmpty(Code)) return string.Empty; + + // 去掉读取中可能包含的 '\0' 空字符 + var src = Code.Replace("\0", string.Empty); + + var sb = new System.Text.StringBuilder(src.Length); + for (int i = 0; i < src.Length; i += 2) + { + if (i + 1 < src.Length) + { + sb.Append(src[i + 1]); + sb.Append(src[i]); + } + else + { + // 奇数长度,最后一个字符原样保留 + //sb.Append((char)0x0); + sb.Append('\0'); + sb.Append(src[i]); + } + } + + return sb.ToString(); + } + + + + } + + + +}