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();
+ }
+
+
+
+ }
+
+
+
+}