Просмотр исходного кода

新增锁付压力采集与自动保存功能

本次提交实现了锁付过程压力数据的自动采集与归档:
- 新增压力采集相关字段与控制逻辑,支持采样间隔与超时自动停止;
- 锁付开始时自动启动压力采集,结束时自动停止并保存为 CSV 文件,目录结构包含日期、产品名、产品编号等信息;
- 采集与保存过程增加异常捕获和日志,提升系统健壮性。
孝锋 徐 7 месяцев назад
Родитель
Сommit
75a9e1b60c
1 измененных файлов с 175 добавлено и 2 удалено
  1. 175 2
      TeamAAS-VM/Core/Management.cs

+ 175 - 2
TeamAAS-VM/Core/Management.cs

@@ -2,6 +2,7 @@
 using Cognex.VisionPro;
 using Cognex.VisionPro.ToolBlock;
 using CSScripting;
+using CSScriptLib;
 using MathNet.Numerics.Distributions;
 using MathNet.Numerics.LinearAlgebra;
 using MathNet.Numerics.RootFinding;
@@ -10,6 +11,7 @@ using NPOI.Util;
 using Opc.Ua;
 using OpenCvSharp;
 using OpenCvSharp.Flann;
+using Org.BouncyCastle.Crypto;
 using Prism.Events;
 using Prism.Ioc;
 using Prism.Mvvm;
@@ -52,6 +54,7 @@ using TeamAAS_VP.Models.Product;
 using TeamAAS_VP.Resources.Languages;
 using TeamAAS_VP.Services;
 using TeamAAS_VP.ViewModels.Home;
+using TeamAAS_VP.Views.Product;
 using TouchSocket.Core;
 using TouchSocket.SerialPorts;
 using TouchSocket.Sockets;
@@ -83,6 +86,31 @@ namespace TeamAAS_VP.Core
         Timer yieldtimer;
         private DateTime StartTime = DateTime.Now;
         SerialPortClient client;
+        // Pressure collection
+        /// <summary>
+        /// 压力采集信号控制CTS
+        /// </summary>
+        private CancellationTokenSource _pressureCts;
+        /// <summary>
+        /// 压力采集超时CTS
+        /// </summary>
+        private CancellationTokenSource _pressureTimeoutCts;
+        /// <summary>
+        /// 压力采集CTS
+        /// </summary>
+        private CancellationTokenSource _pressureLinkedCts;
+        /// <summary>
+        /// 压力数据集合
+        /// </summary>
+        private List<(DateTime Timestamp, float Value)> _pressureSamples = new List<(DateTime, float)>();
+        /// <summary>
+        /// 采样间隔毫秒
+        /// </summary>
+        private int _pressureSampleIntervalMs = 50; //采样间隔毫秒
+        /// <summary>
+        /// 超时自动停止秒数
+        /// </summary>
+        private int _pressureMaxDurationSeconds = 30; //超时自动停止秒数
         #endregion
 
         #region 属性
@@ -1423,10 +1451,10 @@ namespace TeamAAS_VP.Core
                             Guid procedureId = currentProduct.MoveDownCameraLockPhotoProcedureId;
 
                             //获取锁付拍照的点位
-                            var pos=currentProduct.ScrewCameraPoints.FirstOrDefault(p => p.Number == cameraIndex);
+                            var pos = currentProduct.ScrewCameraPoints.FirstOrDefault(p => p.Number == cameraIndex);
                             if (pos != null)
                             {
-                                procedureId= pos.VisionProcessID;
+                                procedureId = pos.VisionProcessID;
                             }
 
                             // 获取流程
@@ -1608,6 +1636,8 @@ namespace TeamAAS_VP.Core
                             //位置编号
                             Int16 positionIndex = plc.ReadNode<Int16>(addressConfig.In_ProductNum.Address);
                             SendTaskMessage(string.Format(Lang.编号0, positionIndex), MessageLevel.Info);
+                            // 开始采集压力值
+                            StartPressureCollection(currentProduct.Name, _productService.CurrentProductCode, positionIndex);
                         }
                         else
                         {
@@ -1718,6 +1748,8 @@ namespace TeamAAS_VP.Core
                             NumStatistics.NozzleNum = await _systemDatabaseService.IncrementLatestNozzleLockCountAsync(currentUserName);
 
                             await plc.WriteNodeAsync(addressConfig.In_WorkDone.Address, (Int16)0);
+                            // 停止采集压力并保存
+                            await StopAndSavePressureCollectionAsync(currentProduct.Name, _productService.CurrentProductCode, positionIndex);
                         }
                         else
                         {
@@ -1905,6 +1937,147 @@ namespace TeamAAS_VP.Core
             });
         }
 
+        #region Pressure collection helpers
+        /// <summary>
+        /// 开始采集压力值
+        /// </summary>
+        /// <param name="productName">配方名称</param>
+        /// <param name="productCode">产品编号</param>
+        /// <param name="positionIndex">锁付点编号</param>
+        private void StartPressureCollection(string productName,string productCode, int positionIndex)
+        {
+            try
+            {
+                // cancel existing if any
+                _pressureCts?.Cancel();
+                _pressureTimeoutCts?.Cancel();
+                _pressureLinkedCts?.Dispose();
+                _pressureSamples.Clear();
+
+                _pressureCts = new CancellationTokenSource();
+                _pressureTimeoutCts = new CancellationTokenSource();
+
+                // timeout after configured seconds
+                _pressureTimeoutCts.CancelAfter(TimeSpan.FromSeconds(_pressureMaxDurationSeconds));
+
+                // link tokens so either stop or timeout cancels
+                _pressureLinkedCts = CancellationTokenSource.CreateLinkedTokenSource(_pressureCts.Token, _pressureTimeoutCts.Token);
+
+                var token = _pressureLinkedCts.Token;
+                var addressConfig = _configService.GetPlcAddresses();
+                var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
+                if (plc == null || !plc.IsConnected) return;
+                Task.Run(async () =>
+                {
+                    SendTaskMessage($"Pressure collection started for position {positionIndex}", MessageLevel.Debug);
+                    while (!token.IsCancellationRequested)
+                    {
+                        try
+                        {
+                            float pressure = plc.ReadNode<float>(addressConfig.In_PressureValue.Address);
+                            lock (_pressureSamples)
+                            {
+                                _pressureSamples.Add((DateTime.Now, pressure));
+                            }
+                        }
+                        catch (Exception ex)
+                        {
+                            LogHelper.WriteLogError("采集压力值时出错", ex);
+                        }
+                        await Task.Delay(_pressureSampleIntervalMs);
+                    }
+                    // if cancelled due to timeout
+                    if (_pressureTimeoutCts.IsCancellationRequested && !_pressureCts.IsCancellationRequested)
+                    {
+                        SendTaskMessage("Pressure collection timed out, saving collected data.", MessageLevel.Alarm);
+                        await StopAndSavePressureCollectionAsync(productName, productCode,positionIndex);
+                    }
+                }, token);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("StartPressureCollection error", ex);
+            }
+        }
+
+        /// <summary>
+        /// 停止采集并保存压力数据
+        /// </summary>
+        /// <param name="productName"></param>
+        /// <param name="productCode"></param>
+        /// <param name="positionIndex"></param>
+        /// <returns></returns>
+        private async Task StopAndSavePressureCollectionAsync(string productName, string productCode, int positionIndex)
+        {
+            try
+            {
+                // cancel sampling
+                _pressureCts?.Cancel();
+                // allow worker to observe cancel
+                await Task.Delay(50);
+
+                List<(DateTime Timestamp, float Value)> snapshot;
+                lock (_pressureSamples)
+                {
+                    snapshot = new List<(DateTime, float)>(_pressureSamples);
+                }
+
+                if (snapshot.Count == 0)
+                {
+                    SendTaskMessage("No pressure samples collected.", MessageLevel.Info);
+                    return;
+                }
+
+                // save to file under configured path
+                try
+                {
+                    //获取系统参数
+                    var systemConfig = _configService.GetSystemConfiguration();
+                    //判断是否保存锁付曲线
+                    if (systemConfig.IsSaveScrewCurve)
+                    {
+                        string basePath = systemConfig?.ScrewCurvePath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+                        string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
+                        string dirPath = Path.Combine(basePath, datePath, productName ?? "UnknownProduct", productCode ?? DateTime.Now.ToString("yyyyMMddHHmmssfff"));
+                        if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
+                        string fileName = $"pressure-{productCode}-{positionIndex}-{DateTime.Now.ToString("HHmmss")}.csv";
+                        string fullPath = Path.Combine(dirPath, fileName);
+                        var sb = new StringBuilder();
+                        sb.AppendLine("Timestamp,Value");
+                        foreach (var s in snapshot)
+                        {
+                            sb.AppendLine($"{s.Timestamp:O},{s.Value}");
+                        }
+
+                        File.WriteAllText(fullPath, sb.ToString());
+                        SendTaskMessage($"Pressure samples saved to {fullPath}", MessageLevel.Debug);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError("保存压力采样文件时出错", ex);
+                    SendTaskMessage("保存压力采样文件时出错: " + ex.Message, MessageLevel.Error);
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("StopAndSavePressureCollectionAsync error", ex);
+            }
+            finally
+            {
+                try
+                {
+                    _pressureLinkedCts?.Cancel();
+                    _pressureLinkedCts?.Dispose();
+                    _pressureTimeoutCts?.Dispose();
+                    _pressureCts?.Dispose();
+                    _pressureSamples.Clear();
+                }
+                catch { }
+            }
+        }
+        #endregion
+
         private async void DoYieldTime(object state)
         {
             try