From dde463b43baed27b31b2fe961d29955b84994340 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Tue, 11 Nov 2025 17:26:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E7=89=88=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FATrace.App/App.config | 4 +- FATrace.App/FATrace.App.csproj | 2 +- FATrace.App/ZebraPrint.cs | 5 - FATrace.App/frmMain.Designer.cs | 1 + FATrace.App/frmMain.cs | 13 +- FATrace.Model/LineTempCode.cs | 29 + FATrace.Model/RawProUse.cs | 6 + FATrace.WPLApp/App.config | 12 +- FATrace.WPLApp/App.xaml.cs | 7 +- FATrace.WPLApp/ConfigHelper.cs | 7 +- FATrace.WPLApp/FATrace.WPLApp.csproj | 18 +- FATrace.WPLApp/Models/LineSglModel.cs | 105 ++++ FATrace.WPLApp/NLog.config | 52 ++ FATrace.WPLApp/Services/CsvServices.cs | 20 + FATrace.WPLApp/Services/DataServices.cs | 549 +++++++++++++++++- FATrace.WPLApp/Services/ILogService.cs | 8 +- FATrace.WPLApp/Services/LogService.cs | 39 +- FATrace.WPLApp/Services/NavigationServices.cs | 29 +- FATrace.WPLApp/Services/SysRunService.cs | 11 + FATrace.WPLApp/ViewModels/MainViewModel.cs | 5 - FATrace.WPLApp/Views/RawProUseView.xaml | 313 +++++++--- 21 files changed, 1038 insertions(+), 197 deletions(-) create mode 100644 FATrace.Model/LineTempCode.cs create mode 100644 FATrace.WPLApp/Models/LineSglModel.cs create mode 100644 FATrace.WPLApp/NLog.config create mode 100644 FATrace.WPLApp/Services/CsvServices.cs diff --git a/FATrace.App/App.config b/FATrace.App/App.config index 66f68f9..3f0878c 100644 --- a/FATrace.App/App.config +++ b/FATrace.App/App.config @@ -1,9 +1,9 @@  - - + + diff --git a/FATrace.App/FATrace.App.csproj b/FATrace.App/FATrace.App.csproj index 7a2ffe7..217fcb0 100644 --- a/FATrace.App/FATrace.App.csproj +++ b/FATrace.App/FATrace.App.csproj @@ -2,7 +2,7 @@ WinExe - net8.0-windows10.0.19041 + net8.0-windows10.0.19041.0 enable true enable diff --git a/FATrace.App/ZebraPrint.cs b/FATrace.App/ZebraPrint.cs index a3551eb..4523888 100644 --- a/FATrace.App/ZebraPrint.cs +++ b/FATrace.App/ZebraPrint.cs @@ -1,10 +1,5 @@ using FATrace.App.UniCodeToZPL; -using FATrace.Model; -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Zebra.Sdk.Comm; using Zebra.Sdk.Printer; diff --git a/FATrace.App/frmMain.Designer.cs b/FATrace.App/frmMain.Designer.cs index 40e0097..52bf7a0 100644 --- a/FATrace.App/frmMain.Designer.cs +++ b/FATrace.App/frmMain.Designer.cs @@ -924,6 +924,7 @@ namespace FATrace.App Name = "frmMain"; StartPosition = FormStartPosition.CenterScreen; Text = "管理界面"; + WindowState = FormWindowState.Maximized; Load += frmMain_Load; statusStrip1.ResumeLayout(false); statusStrip1.PerformLayout(); diff --git a/FATrace.App/frmMain.cs b/FATrace.App/frmMain.cs index 8d1f69a..5dcccc6 100644 --- a/FATrace.App/frmMain.cs +++ b/FATrace.App/frmMain.cs @@ -1,18 +1,9 @@ using FATrace.App.Model; using FATrace.Model; using NLog; -using System.Net.Sockets; -using System.Net.NetworkInformation; -using System; -using System.Collections.Generic; -using System.ComponentModel; using System.Data; -using System.Drawing; -using System.Linq; +using System.Net.NetworkInformation; using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using static System.Runtime.CompilerServices.RuntimeHelpers; namespace FATrace.App { @@ -45,7 +36,7 @@ namespace FATrace.App private TScalTcp? _scaleTcp; // 打印机连接参数(用于状态检测) - private string _printerIp = "192.168.0.40"; + private string _printerIp = "192.0.1.21"; private int _printerPort = 9100; // 状态缓存,避免频繁重复写日志 diff --git a/FATrace.Model/LineTempCode.cs b/FATrace.Model/LineTempCode.cs new file mode 100644 index 0000000..3de96fb --- /dev/null +++ b/FATrace.Model/LineTempCode.cs @@ -0,0 +1,29 @@ +using FreeSql.DataAnnotations; + +namespace FATrace.Model +{ + /// + /// 产线临时条码数据 + /// + [Table(Name = "LineTempCode")] + public class LineTempCode + { + /// + /// 主键 + /// + [Column(IsPrimary = true, IsIdentity = true)] + public long Id { get; set; } + + /// + /// 编号 + /// + [Column(Name = "Code", IsNullable = false, StringLength = 100)] + public string? Code { get; set; } + + /// + /// 创建时间 + /// + [Column(ServerTime = DateTimeKind.Local, CanUpdate = true)] + public DateTime CreateTime { get; set; } + } +} diff --git a/FATrace.Model/RawProUse.cs b/FATrace.Model/RawProUse.cs index a0ac1f3..f5a6bc7 100644 --- a/FATrace.Model/RawProUse.cs +++ b/FATrace.Model/RawProUse.cs @@ -81,6 +81,12 @@ namespace FATrace.Model [Column(Name = "WeightTime")] public DateTime WeightTime { get; set; } + /// + /// 称重扫码时间 + /// + [Column(Name = "WeightScanTime")] + public DateTime WeightScanTime { get; set; } + /// /// 操作者 /// diff --git a/FATrace.WPLApp/App.config b/FATrace.WPLApp/App.config index 66f68f9..f0bd202 100644 --- a/FATrace.WPLApp/App.config +++ b/FATrace.WPLApp/App.config @@ -1,13 +1,13 @@ - + - - + - - + + + + - \ No newline at end of file diff --git a/FATrace.WPLApp/App.xaml.cs b/FATrace.WPLApp/App.xaml.cs index db34595..691b665 100644 --- a/FATrace.WPLApp/App.xaml.cs +++ b/FATrace.WPLApp/App.xaml.cs @@ -44,7 +44,7 @@ namespace FATrace.WPLApp //System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN"); // 授权 a717c797-59e3-48de-b6b4-574a4e03dc79 - if (!HslCommunication.Authorization.SetAuthorizationCode("b23b00e2-ce46-4bfc-b33c-71c47c2c11c2")) + if (!HslCommunication.Authorization.SetAuthorizationCode("c04165f1-2578-4d86-ab08-a293b7552a5f")) { //active failed MessageBox.Show("授权失败!当前程序只能使用8小时!"); @@ -114,6 +114,7 @@ namespace FATrace.WPLApp { var fsql = Container.Resolve(); var sysRun = Container.Resolve(); + var DataServices = Container.Resolve(); LogService.Info("Background services initialized"); } catch (Exception ex) @@ -197,8 +198,10 @@ namespace FATrace.WPLApp //containerRegistry.RegisterSingleton(); //containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); - + + //// 注册 Quartz 配置选项 //containerRegistry.RegisterInstance>( diff --git a/FATrace.WPLApp/ConfigHelper.cs b/FATrace.WPLApp/ConfigHelper.cs index 6ed391e..7e06f18 100644 --- a/FATrace.WPLApp/ConfigHelper.cs +++ b/FATrace.WPLApp/ConfigHelper.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Configuration; namespace FATrace.WPLApp { diff --git a/FATrace.WPLApp/FATrace.WPLApp.csproj b/FATrace.WPLApp/FATrace.WPLApp.csproj index 3397d77..bc81944 100644 --- a/FATrace.WPLApp/FATrace.WPLApp.csproj +++ b/FATrace.WPLApp/FATrace.WPLApp.csproj @@ -1,13 +1,23 @@ - + - WinExe + Exe net8.0-windows enable enable true + + + + + + + PreserveNewest + + + @@ -51,9 +61,9 @@ - + - + PreserveNewest diff --git a/FATrace.WPLApp/Models/LineSglModel.cs b/FATrace.WPLApp/Models/LineSglModel.cs new file mode 100644 index 0000000..44ef8c3 --- /dev/null +++ b/FATrace.WPLApp/Models/LineSglModel.cs @@ -0,0 +1,105 @@ +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FATrace.WPLApp.Models +{ + /// + /// 产线信号模型 + /// + public class LineSglModel : BindableBase + { + public LineSglModel() + { + + } + + /// + /// 称重扫码信号准备OK信号 + /// 开始获取拍照数据,并检验数据 + /// + public event EventHandler WeightScanCodeHandle; //产线信号改变 + + /// + /// 外箱喷码Handle请求信号 + /// + public event EventHandler BoxSprayCodeReqHandle; + + /// + /// 外箱扫码Handle信号 + /// + public event EventHandler BoxScanCodeReqHandle; + + private Int16 _WeightScanCodeEnable; + /// + /// 称重的拍照信号 + /// 开始获取拍照数据,并检验数据 + /// + public Int16 WeightScanCodeEnable + { + get { return _WeightScanCodeEnable; } + set + { + if (_WeightScanCodeEnable != value) + { + if (value == 1) WeightScanCodeHandle.Invoke(this, "WeightScanCodeHandle"); + _WeightScanCodeEnable = value; + } + + } + } + + + + + + + private Int16 _BoxSprayCodeReqEnable; + /// + /// 外箱喷码请求新信号 + /// 给PLC数据喷码数据 + /// + public Int16 BoxSprayCodeReqEnable + { + get { return _BoxSprayCodeReqEnable; } + set + { + if (_BoxSprayCodeReqEnable != value) + { + if (value == 1) BoxSprayCodeReqHandle.Invoke(this, "BoxSprayCodeReqEnable");//外箱喷码请求信号 + + _BoxSprayCodeReqEnable = value; + } + + } + } + + + + + private Int16 _BoxScanCodeEnable; + /// + /// 外箱扫码信号 + /// 开始获取扫码数据 + /// + public Int16 BoxScanCodeEnable + { + get { return _BoxScanCodeEnable; } + set + { + if (_BoxScanCodeEnable != value) + { + if (value==1) BoxScanCodeReqHandle.Invoke(this, "BoxScanCodeEnable"); //外箱扫码信号 + _BoxScanCodeEnable = value; + } + } + } + + + } + + +} diff --git a/FATrace.WPLApp/NLog.config b/FATrace.WPLApp/NLog.config new file mode 100644 index 0000000..92386bc --- /dev/null +++ b/FATrace.WPLApp/NLog.config @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FATrace.WPLApp/Services/CsvServices.cs b/FATrace.WPLApp/Services/CsvServices.cs new file mode 100644 index 0000000..f8ae3f2 --- /dev/null +++ b/FATrace.WPLApp/Services/CsvServices.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FATrace.WPLApp.Services +{ + /// + /// CSV服务 + ///导出CSV文件 + /// + public class CsvServices + { + public CsvServices() + { + + } + } +} diff --git a/FATrace.WPLApp/Services/DataServices.cs b/FATrace.WPLApp/Services/DataServices.cs index f88ced1..2a40b20 100644 --- a/FATrace.WPLApp/Services/DataServices.cs +++ b/FATrace.WPLApp/Services/DataServices.cs @@ -1,8 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using FATrace.Model; +using FATrace.WPLApp.Models; +using HslCommunication; +using HslCommunication.Profinet.Keyence; +using Prism.Mvvm; +using System.Collections.Concurrent; +using System.Windows; +using ComConfigHelper = FATrace.Com.ConfigHelper; namespace FATrace.WPLApp.Services { @@ -10,8 +13,542 @@ namespace FATrace.WPLApp.Services /// 数据服务 /// 产线PLC数据交互的服务 /// - public class DataServices + public class DataServices : BindableBase, IDisposable { + public DataServices(ILogService logService, IFreeSql freeSql) + { + LogService = logService; + FreeSql = freeSql; + + LineSglModel = new LineSglModel(); + LineSglModel.WeightScanCodeHandle += LineSglModel_WeightScanCodeHandle; + LineSglModel.BoxSprayCodeReqHandle += LineSglModel_BoxSprayCodeReqHandle; + LineSglModel.BoxScanCodeReqHandle += LineSglModel_BoxScanCodeReqHandle; + + PLCLinkInitial(); + StartPlcScan(); + //var DD= RevData("DYG05030013,20250923,802,3,01,0001,"); + } + + + /// + ///外箱喷码请求 + /// + /// + /// + private void LineSglModel_BoxSprayCodeReqHandle(object? sender, string e) + { + //首先复位PLC信号 + KeyencePlcMcNet!.Write("D1100", (Int16)0); + + //获取箱子喷码的数据+A + var ReqData = FreeSql.Select() + .OrderBy(a => a.Id) + .Limit(1) + .ToList(); + if (ReqData.Count > 0) + { + var CodeItem = ReqData.FirstOrDefault(); + KeyencePlcMcNet.Write("D1150", RevData(CodeItem!.Code + ",A")); + + Console.WriteLine($"外箱喷码:{CodeItem.Code}-发送OK"); + + //这里需要删除数据吗?还是等扫码后删除,会不会过来新的箱子 + + } + else + { + //不存在请求的数据,报错 + KeyencePlcMcNet.Write("D1102", (Int16)1); + Console.WriteLine($"外箱喷码:不存在请求的数据"); + } + + + Task.Run(async () => + { + await Task.Delay(1000).ConfigureAwait(false); + KeyencePlcMcNet!.Write("D1102", (Int16)0); + + }); + + + } + + /// + /// 箱子扫描码请求 + /// + /// + /// + /// + private void LineSglModel_BoxScanCodeReqHandle(object? sender, string e) + { + //首先复位PLC信号 + KeyencePlcMcNet!.Write("D1200", (Int16)0); + + if (!string.IsNullOrEmpty(BoxScanCode)) + { + //var IsExist = FreeSql.Select().Where(a => a.BoxCode == BoxScanCode); + var IsExist = FreeSql.Select().Where(a => a.BoxCode!.Contains(BoxScanCode));// 内包和外包一样的条码测试用 ??????????????? + if (IsExist.Count() > 0) + { + //存在条码数据 OK,返回PLC结果 + KeyencePlcMcNet!.Write("D1210", (Int16)1); + + Console.WriteLine($"外箱扫描码:{BoxScanCode}-存在找到"); + //// 加入队列 + //EnqueueMessage(WeightScanCode); + LogService.Info($"外箱扫描码:{BoxScanCode}"); + + //删除临时的队列条码数据 + var DeleteResult = FreeSql.Delete() + .Where(p => BoxScanCode.Contains(p.Code!)) + .ExecuteAffrows(); + if (DeleteResult > 0) + { + Console.WriteLine($"外箱扫描码:{WeightScanCode}-删除临时队列数据成功"); + LogService.Info($"外箱扫描码:{WeightScanCode}-删除临时队列数据成功"); + } + else + { + Console.WriteLine($"外箱扫描码:{WeightScanCode}-删除临时队列数据失败"); + LogService.Info($"外箱扫描码:{WeightScanCode}-删除临时队列数据失败"); + } + + var Result = FreeSql.Update() + .Set(p => p.OutTime, DateTime.Now) + .Where(p => p.BoxCode == BoxScanCode+",A")//外箱二维码就是外箱扫描码 + .ExecuteAffrows(); + if (Result > 0) + { + Console.WriteLine($"外箱扫描码:{WeightScanCode}更新时间成功"); + LogService.Info($"外箱扫描码:{WeightScanCode}更新时间成功"); + } + else + { + Console.WriteLine($"外箱扫描码:{WeightScanCode}更新时间失败"); + LogService.Warn($"外箱扫描码:{WeightScanCode}更新时间失败"); + } + } + else + { + //不存在条码数据 NG,返回PLC结果报警 + KeyencePlcMcNet!.Write("D1210", (Int16)2); + + Console.WriteLine($"外箱扫描码:{WeightScanCode}-未发现外箱扫码数据在数据库中"); + LogService.Warn($"外箱扫描码:{WeightScanCode}-未发现外箱扫码数据在数据库中"); + } + } + else + { + Console.WriteLine($"外箱扫描码为空"); + LogService.Warn("外箱扫描码为空"); + } + + Task.Run(async () => + { + await Task.Delay(1000).ConfigureAwait(false); + KeyencePlcMcNet!.Write("D1210", (Int16)0); + + KeyencePlcMcNet.Write("D1250", new Int16[30]); + }); + + } + + /// + /// 称重扫描码请求数据 + /// + /// + /// + /// + private void LineSglModel_WeightScanCodeHandle(object? sender, string e) + { + //首先复位PLC信号 + KeyencePlcMcNet!.Write("D1000", (Int16)0); + + if (!string.IsNullOrEmpty(WeightScanCode)) + { + var IsExist = FreeSql.Select().Where(a => a.InBagCode == WeightScanCode); + if (IsExist.Count() > 0) + { + //存在条码数据 OK,返回PLC结果 + KeyencePlcMcNet!.Write("D1010", (Int16)1); + + Console.WriteLine($"称重扫描码:{WeightScanCode}"); + //// 加入队列 + //EnqueueMessage(WeightScanCode); + LogService.Info($"称重扫描码:{WeightScanCode}"); + + var TempData = FreeSql.Insert(new LineTempCode() + { + Code = WeightScanCode + }).ExecuteAffrows(); + + var Result = FreeSql.Update() + .Set(p => p.WeightScanTime, DateTime.Now) + .Where(p => p.InBagCode == WeightScanCode)//内箱二维码就是称重扫描码 + .ExecuteAffrows(); + if (Result > 0) + { + Console.WriteLine($"称重扫描码:{WeightScanCode}更新时间成功"); + LogService.Info($"称重扫描码:{WeightScanCode}更新时间成功"); + } + else + { + Console.WriteLine($"称重扫描码:{WeightScanCode}更新时间失败"); + LogService.Warn($"称重扫描码:{WeightScanCode}更新时间失败"); + } + } + else + { + //不存在条码数据 NG,返回PLC结果报警 + KeyencePlcMcNet!.Write("D1010", (Int16)2); + + Console.WriteLine($"称重扫描码:{WeightScanCode}-未发现内包扫码数据在数据库中"); + LogService.Warn($"称重扫描码:{WeightScanCode}-未发现内包扫码数据在数据库中"); + } + + Task.Run(async () => + { + await Task.Delay(1000).ConfigureAwait(false); + KeyencePlcMcNet!.Write("D1010", (Int16)0); + + KeyencePlcMcNet.Write("D1050", new Int16[30]); + }); + + + } + else + { + Console.WriteLine($"称重扫描码为空"); + LogService.Warn("称重扫描码为空"); + } + + } + + public ILogService LogService { get; } + public IFreeSql FreeSql { get; } + + //通信组件 + private KeyenceMcNet KeyencePlcMcNet { get; set; } = null; + + private string _WeightScanCode; + /// + /// 称重扫描码 + /// + public string WeightScanCode + { + get { return _WeightScanCode; } + set { _WeightScanCode = value; RaisePropertyChanged(); } + } + + + private string _BoxScanCode; + /// + /// 外箱喷码扫码数据 + /// + public string BoxScanCode + { + get { return _BoxScanCode; } + set { _BoxScanCode = value; RaisePropertyChanged(); } + } + + + + // 扫描控制 + private CancellationTokenSource? _plcScanCts; + private Task? _plcScanTask; + private volatile bool _isScanning = false; + public bool IsScanning => _isScanning; + private bool _disposed = false; + + /// + /// 简单字符串队列(FIFO) + /// + private readonly ConcurrentQueue _messageQueue = new(); + + /// + /// 入队:将字符串加入 FIFO 队列(先进先出)。允许空字符串,不允许 null。 + /// + /// 要加入队列的字符串 + /// 当 message 为 null 时抛出 + public void EnqueueMessage(string message) + { + if (message is null) throw new ArgumentNullException(nameof(message)); + _messageQueue.Enqueue(message); + } + + /// + /// 出队:尝试从 FIFO 队列取出一个字符串。 + /// + /// 输出参数:若成功则为取出的字符串;若失败(队列为空)则为 default + /// 是否成功取出 + public bool TryDequeueMessage(out string message) + { + return _messageQueue.TryDequeue(out message); + } + + /// + /// 产线信号模型 + /// + public LineSglModel LineSglModel { get; set; } + + /// + /// PLC通信初始化 + /// + /// + /// + private void PLCLinkInitial() + { + try + { + // 读取 PLC 配置(大小写敏感,采用公共 ConfigHelper 并提供默认值) + string PLCIP = ComConfigHelper.GetStringOrDefault("PLCIP", "192.0.1.10"); + int Port = ComConfigHelper.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"); + } + else + { + MessageBox.Show(connect.Message + Environment.NewLine + "ErrorCode: " + connect.ErrorCode); + } + } + catch (Exception ex) + { + LogService.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); + } + } + + /// + /// 启动PLC扫描。若已在扫描则忽略重复启动。 + /// + /// 扫描周期,若为 null 则读取 App.config 的 PLCScan(毫秒),默认600ms + /// 可选:外部取消令牌,用于联动取消 + public void StartPlcScan(TimeSpan? scanPeriod = null, CancellationToken externalToken = default) + { + if (_isScanning) return; + + var period = scanPeriod ?? ComConfigHelper.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; + } + } + + /// + /// 称重扫描条码OperateResult结果 + /// + private OperateResult OperateResultWeightScanCode { get; set; } + + /// + /// 称重扫描条码OK信号 + /// + private OperateResult OperateResultWeightScanSgl { get; set; } + + /// + /// 外箱喷码请求信号 + /// + private OperateResult OperateResultBoxSprayCodeReqSgl { get; set; } + + /// + /// 外箱扫码条码OperateResult结果 + /// + private OperateResult OperateResultBoxScanCode { get; set; } + + /// + /// 外箱扫码请求信号 + /// + private OperateResult OperateResultBoxScanSgl { get; set; } + + /// + /// 扫描循环:按周期轮询PLC寄存器并更新模型。 + /// - 读取配置项 Addr_WeightPhotoEnable(默认 M100)为布尔量 + /// - 读失败时尝试重连一次 + /// + private async Task PlcScanLoopAsync(TimeSpan period, CancellationToken token) + { + string addrWeight = ComConfigHelper.GetStringOrDefault("Addr_WeightPhotoEnable", "M100"); + bool? lastWeight = null; + + while (!token.IsCancellationRequested) + { + try + { + if (KeyencePlcMcNet == null) + { + PLCLinkInitial(); + } + + //内包扫码 + OperateResultWeightScanCode = KeyencePlcMcNet!.ReadString("D1050", 20); + if (OperateResultWeightScanCode.IsSuccess) + { + WeightScanCode = RevData(OperateResultWeightScanCode.Content).Replace("\r", "").Replace("\n", ""); + } + OperateResultWeightScanSgl = KeyencePlcMcNet!.ReadInt16("D1000"); + if (OperateResultWeightScanSgl.IsSuccess) + { + LineSglModel.WeightScanCodeEnable = OperateResultWeightScanSgl.Content; + } + + //外箱喷码 + OperateResultBoxSprayCodeReqSgl = KeyencePlcMcNet!.ReadInt16("D1100"); + if (OperateResultBoxSprayCodeReqSgl.IsSuccess) + { + LineSglModel.BoxSprayCodeReqEnable = OperateResultBoxSprayCodeReqSgl.Content; + } + + //外箱扫码 + OperateResultBoxScanCode = KeyencePlcMcNet!.ReadString("D1250", 20); + if (OperateResultBoxScanCode.IsSuccess) + { + BoxScanCode = RevData(OperateResultBoxScanCode.Content).Replace("\r", "").Replace("\n", ""); + } + OperateResultBoxScanSgl = KeyencePlcMcNet!.ReadInt16("D1200"); + if (OperateResultBoxScanSgl.IsSuccess) + { + LineSglModel.BoxScanCodeEnable = OperateResultBoxScanSgl.Content; + } + + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + LogService.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) + { + LogService.Warn($"PLC重连失败: {ret.Message} (Code: {ret.ErrorCode})"); + } + } + catch (Exception ex) + { + LogService.Error($"PLC重连异常: {ex.Message}"); + } + } + + /// + /// 释放资源,确保扫描停止并断开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; + } + } + + /// + /// 把字符串对调 + /// + private 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(src[i]); + } + } + + return sb.ToString(); + } } } diff --git a/FATrace.WPLApp/Services/ILogService.cs b/FATrace.WPLApp/Services/ILogService.cs index afe4b95..95d6517 100644 --- a/FATrace.WPLApp/Services/ILogService.cs +++ b/FATrace.WPLApp/Services/ILogService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,9 +12,15 @@ namespace FATrace.WPLApp.Services public interface ILogService { void Debug(string msg); + void Debug(string msg, Exception ex); void Error(string msg); + void Error(string msg, Exception ex); + void Error(Exception ex); void Fatal(string msg); + void Fatal(string msg, Exception ex); void Info(string msg); + void Info(string msg, Exception ex); void Warn(string msg); + void Warn(string msg, Exception ex); } } diff --git a/FATrace.WPLApp/Services/LogService.cs b/FATrace.WPLApp/Services/LogService.cs index ee1d93a..462347d 100644 --- a/FATrace.WPLApp/Services/LogService.cs +++ b/FATrace.WPLApp/Services/LogService.cs @@ -1,4 +1,4 @@ -using NLog; +using NLog; using System; using System.Collections.Generic; using System.Linq; @@ -12,16 +12,14 @@ namespace FATrace.WPLApp.Services /// public class LogService : ILogService { - private static Logger Logger = LogManager.GetCurrentClassLogger(); //初始化日志类 + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); // 初始化日志类 /// /// 调试日志 /// /// 日志内容 - public void Debug(string msg) - { - Logger.Debug(msg); - } + public void Debug(string msg) => Logger.Debug(msg); + public void Debug(string msg, Exception ex) => Logger.Debug(ex, msg); /// /// 信息日志 @@ -31,10 +29,8 @@ namespace FATrace.WPLApp.Services /// 适用大部分场景 /// 1.记录日志文件 /// - public void Info(string msg) - { - Logger.Info(msg); - } + public void Info(string msg) => Logger.Info(msg); + public void Info(string msg, Exception ex) => Logger.Info(ex, msg); /// /// 错误日志 @@ -44,10 +40,9 @@ namespace FATrace.WPLApp.Services /// 适用异常,错误日志记录 /// 1.记录日志文件 /// - public void Error(string msg) - { - Logger.Error(msg); - } + public void Error(string msg) => Logger.Error(msg); + public void Error(string msg, Exception ex) => Logger.Error(ex, msg); + public void Error(Exception ex) => Logger.Error(ex); /// /// 严重致命错误日志 @@ -57,10 +52,8 @@ namespace FATrace.WPLApp.Services /// 1.记录日志文件 /// 2.控制台输出 /// - public void Fatal(string msg) - { - Logger.Fatal(msg); - } + public void Fatal(string msg) => Logger.Fatal(msg); + public void Fatal(string msg, Exception ex) => Logger.Fatal(ex, msg); /// /// 警告日志 @@ -70,13 +63,7 @@ namespace FATrace.WPLApp.Services /// 1.记录日志文件 /// 2.发送日志邮件 /// - public void Warn(string msg) - { - try - { - Logger.Warn(msg); - } - catch { } - } + public void Warn(string msg) => Logger.Warn(msg); + public void Warn(string msg, Exception ex) => Logger.Warn(ex, msg); } } diff --git a/FATrace.WPLApp/Services/NavigationServices.cs b/FATrace.WPLApp/Services/NavigationServices.cs index f3198ac..58eafdb 100644 --- a/FATrace.WPLApp/Services/NavigationServices.cs +++ b/FATrace.WPLApp/Services/NavigationServices.cs @@ -24,34 +24,7 @@ namespace FATrace.WPLApp.Services CmdPar = "Dashboard", Icon = "\ued78", IsParent = false, - }, - - // 工艺监控导航项(父级) - new NavItemDto() - { - Name = "工艺监控", - CmdPar = "", - Icon = "\uea12", - IsParent = true, - ChildrenNavItemDtos = new ObservableCollection() - { - // 基础数据的子项 - new NavItemDto() - { - Name = "3D工艺流程", - CmdPar = "3D工艺流程", - Icon = "\uee12", - IsParent = false, - }, - new NavItemDto() - { - Name = "管道线路", - CmdPar = "管道线路", - Icon = "\ue650", - IsParent = false, - }, - } - }, + }, // 生产管理导航项(父级) - 新增测试导航 new NavItemDto() diff --git a/FATrace.WPLApp/Services/SysRunService.cs b/FATrace.WPLApp/Services/SysRunService.cs index f25ca7d..5d26cad 100644 --- a/FATrace.WPLApp/Services/SysRunService.cs +++ b/FATrace.WPLApp/Services/SysRunService.cs @@ -23,6 +23,17 @@ namespace FATrace.WPLApp.Services } + private bool _PlcLinkState = false; + /// + /// PLC连接状态 + /// + public bool PlcLinkState + { + get { return _PlcLinkState; } + set { _PlcLinkState = value;RaisePropertyChanged(); } + } + + private string? _CurUser; /// /// 当前的用户 diff --git a/FATrace.WPLApp/ViewModels/MainViewModel.cs b/FATrace.WPLApp/ViewModels/MainViewModel.cs index fe3caae..b419dd2 100644 --- a/FATrace.WPLApp/ViewModels/MainViewModel.cs +++ b/FATrace.WPLApp/ViewModels/MainViewModel.cs @@ -6,12 +6,7 @@ using Prism.Commands; using Prism.Events; using Prism.Regions; using Prism.Services.Dialogs; -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace FATrace.WPLApp.ViewModels { diff --git a/FATrace.WPLApp/Views/RawProUseView.xaml b/FATrace.WPLApp/Views/RawProUseView.xaml index 03c85a4..ad558ca 100644 --- a/FATrace.WPLApp/Views/RawProUseView.xaml +++ b/FATrace.WPLApp/Views/RawProUseView.xaml @@ -2,14 +2,15 @@ x:Class="FATrace.WPLApp.Views.RawProUseView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prism="http://prismlibrary.com/" + d:DesignHeight="720" + d:DesignWidth="1280" prism:ViewModelLocator.AutoWireViewModel="True" - mc:Ignorable="d" - d:DesignWidth="1280" d:DesignHeight="720"> + mc:Ignorable="d"> - + @@ -19,160 +20,284 @@ - - + + - - - - - + + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - -