|
|
@@ -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
|