From dc3d0f85f64e6f67005faf1e7a2b48986de44ad9 Mon Sep 17 00:00:00 2001
From: hanqiangqiang <1364346467@qq.com>
Date: Tue, 18 Aug 2026 14:04:28 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E7=9A=84=E6=B5=8B?=
=?UTF-8?q?=E6=B8=A9=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../SicModules/Config/Common/Common.xml | 4 +
.../SicModules/Config/TM/IODefinePlatform.xml | 6 +-
App/SicRT/Config/System.sccfg | 18 +-
.../AKunTriggerChannelWaferTempData.cs | 288 ++++++++++++++++
.../Temps/AKunTriggerTemp/AKunTriggerTemp.cs | 308 ++++++++++++++++++
.../HardwareUnits/Temps/TempBasFunction.cs | 105 ++++--
.../HardwareUnits/Temps/TempSensorBase.cs | 99 +++---
.../MECF.Framework.RT.EquipmentLibrary.csproj | 2 +
8 files changed, 744 insertions(+), 86 deletions(-)
create mode 100644 Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerChannelWaferTempData.cs
create mode 100644 Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerTemp.cs
diff --git a/App/Modules/SicModules/Config/Common/Common.xml b/App/Modules/SicModules/Config/Common/Common.xml
index d78efe5..a94910b 100644
--- a/App/Modules/SicModules/Config/Common/Common.xml
+++ b/App/Modules/SicModules/Config/Common/Common.xml
@@ -8,6 +8,10 @@
+
+
+
+
diff --git a/App/Modules/SicModules/Config/TM/IODefinePlatform.xml b/App/Modules/SicModules/Config/TM/IODefinePlatform.xml
index d07b84b..34eee9e 100644
--- a/App/Modules/SicModules/Config/TM/IODefinePlatform.xml
+++ b/App/Modules/SicModules/Config/TM/IODefinePlatform.xml
@@ -23,8 +23,8 @@
-
-
+
+
@@ -34,7 +34,7 @@
-
+
diff --git a/App/SicRT/Config/System.sccfg b/App/SicRT/Config/System.sccfg
index d18ec2d..f56b7a6 100644
--- a/App/SicRT/Config/System.sccfg
+++ b/App/SicRT/Config/System.sccfg
@@ -1216,9 +1216,9 @@
-
-
-
+
+
+
@@ -1855,9 +1855,9 @@
-
-
-
+
+
+
@@ -2239,6 +2239,12 @@
+
+
+
+
+
+
diff --git a/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerChannelWaferTempData.cs b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerChannelWaferTempData.cs
new file mode 100644
index 0000000..badccdc
--- /dev/null
+++ b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerChannelWaferTempData.cs
@@ -0,0 +1,288 @@
+using Aitex.Core.RT.DataCenter;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
+{
+ public enum DataType
+ {
+ Wafer,
+ Tray,
+ }
+
+ ///
+ /// 测温点位置
+ ///
+ public enum EnumZoneNum
+ {
+ Center,
+ CenterEdge,
+ Edge
+ }
+
+ public class AKunTriggerChannelWaferTempData
+ {
+ ///
+ /// 创建通道对应的单个晶圆温度对象
+ ///
+ /// 晶圆编号
+ /// 测温设备通道编号
+ /// 当前晶圆对象的名称,Wafer or Tray,测温对象也能反馈Tray温度
+ public AKunTriggerChannelWaferTempData(int waferNum, int ch = 1, string name = "Wafer")
+ {
+ Detector = ch;
+ WaferNum = waferNum;
+ WaferName = $"CH{Detector}.{name}{waferNum}"; //eg: CH1.Wafer1 CH1.Wafer2 CH2.Wafer1 CH2.Wafer2
+ }
+
+ ///
+ /// 通道编号,默认为1
+ ///
+ public int Detector { get; set; }
+
+ public string WaferName { get; set; }
+
+ public int WaferNum { get; set; }
+
+ public DataType Type { get; set; }
+
+ public double Temp { get; set; }
+
+ public int ZoneNum { get; set; }
+
+ public EnumZoneNum ZoneNumSelect { get; set; }
+
+ ///
+ /// 0表示无效晶圆代表Dummy晶圆或者空位,此位置的温度不能参与STC控温
+ ///
+ public int PocketNum { get; set; }
+
+ ///
+ /// The reflection of 404nm
+ ///
+ public double Reflect1 { get; set; }
+
+ ///
+ /// The reflection of 633nm.
+ ///
+ public double Reflect2 { get; set; }
+
+ ///
+ /// The reflection of 950nm.
+ ///
+ public double Reflect3 { get; set; }
+
+ public double Curvature { get; set; }
+
+ ///
+ /// false不参与控温,晶圆温度是否参与区域计算,不放片或者放Dummy时,不参与区域计算
+ ///
+ public bool CalZone { get; set; }
+
+ public void UpData(AKunTriggerChannelWaferTempData newData)
+ {
+ // 确保传入的 newData 对象不为空
+ if (newData == null)
+ {
+ throw new ArgumentNullException(nameof(newData));
+ }
+
+ Detector = newData.Detector;
+ WaferNum = newData.WaferNum;
+ Temp = newData.Temp;
+ ZoneNum = newData.ZoneNum;
+ ZoneNumSelect = newData.ZoneNumSelect;
+ Reflect1 = newData.Reflect1;
+ Curvature = newData.Curvature;
+ }
+ }
+
+ public class AKunTriggerTempDataManager(int waferNum, double minTemp)
+ {
+ private readonly int _waferNum = waferNum;
+ private double _minTemp = minTemp;
+
+ private List _waferTempDataList = new();
+ private static readonly object _lock = new object();
+
+ #region 重新整理温度解析
+
+ public bool ProcessReceivedData(string tcpReceive)
+ {
+ lock (_lock)
+ {
+ List tempDataList = new List();
+ AKunTriggerChannelWaferTempData aKunChWaferTempData;
+ string[] keyValuePairs = tcpReceive.Split('\0');
+
+ for (int i = 0; i < keyValuePairs.Length - 1; i++)
+ {
+ aKunChWaferTempData = new AKunTriggerChannelWaferTempData(-999);
+ string[] keyValues = keyValuePairs[i].Split(',');
+ foreach (string pair in keyValues)
+ {
+ string[] keyValue = pair.Split('=');
+ switch (keyValue[0])
+ {
+ case "Detector":
+ aKunChWaferTempData.Detector = int.Parse(keyValue[1]);
+ break;
+
+ case "PocketNum":
+ aKunChWaferTempData.PocketNum = int.Parse(keyValue[1]);
+ break;
+
+ case "WaferNum":
+ aKunChWaferTempData.WaferNum = int.Parse(keyValue[1]);
+ break;
+
+ case "CalZone":
+ aKunChWaferTempData.CalZone = int.Parse(keyValue[1]) == 1;
+ break;
+
+ case "TTemp":
+ aKunChWaferTempData.Temp = double.Parse(keyValue[1]);
+ break;
+
+ case "ZoneNum":
+ aKunChWaferTempData.ZoneNum = int.Parse(keyValue[1]);
+ aKunChWaferTempData.ZoneNumSelect =
+ (EnumZoneNum)Enum.Parse(typeof(EnumZoneNum), keyValue[1]);
+ break;
+
+ case "Reflec1":
+ aKunChWaferTempData.Reflect1 = double.Parse(keyValue[1]);
+ break;
+
+ case "Reflec2":
+ aKunChWaferTempData.Reflect2 = double.Parse(keyValue[1]);
+ break;
+
+ case "Reflec3":
+ aKunChWaferTempData.Reflect3 = double.Parse(keyValue[1]);
+ break;
+
+ case "Curvature":
+ aKunChWaferTempData.Curvature = double.Parse(keyValue[1]);
+ break;
+ }
+ }
+
+ if (aKunChWaferTempData.WaferNum == -999)
+ return true;
+
+ tempDataList.Add(aKunChWaferTempData);
+ }
+
+ _waferTempDataList = tempDataList;
+
+ return true;
+ }
+ }
+
+ public bool[] GetCalZone()
+ {
+ bool[] calZone = new bool[_waferNum];
+ if (_waferTempDataList.Count < _waferNum)
+ return calZone;
+
+ for (int i = 0; i < _waferNum; i++) //只有前面几个数据,是需要统计是否参与计算
+ {
+ calZone[i] = _waferTempDataList[i].CalZone;
+ }
+
+ return calZone;
+ }
+
+ //public double[] GetWaferTemp()
+ //{
+ // double[] waferTemps = Enumerable.Repeat(_minTemp, _waferNum).ToArray();
+ // if (_waferTempDataList.Count < _waferNum)
+ // return waferTemps;
+
+ // int count = (_waferTempDataList.Count - 1) / _waferNum ;//分区数计算,最后一个数据是托盘的,所以减一,
+ // if ((_waferTempDataList.Count - 1) % _waferNum != 0)//分区数量不是整数,温度不解析防呆,
+ // return waferTemps;
+
+ // for (int i = 0; i < _waferNum; i++) //Wafer温度只取每个晶圆的第一个设置的中心温度
+ // {
+ // var index = i * count;
+ // waferTemps[i] = _waferTempDataList[index].Temp;//直接使用第一个中心温度作为中心温度
+
+ // //要求不判断中心点,只用第一个分区温度
+ // //for (int j = index; j < index + count ; j++)
+ // //{
+ // // if(_waferTempDataList[j].ZoneNum==0)
+ // // {
+ // // waferTemps[i] = _waferTempDataList[j].Temp;//有中心温度设置,获取第一个中心温度作为晶圆温度
+ // // break;
+ // // }
+ // //}
+ // }
+
+ // return waferTemps;
+ //}
+
+ public double[] GetWaferTemp()
+ {
+ double[] waferTemps = Enumerable.Repeat(_minTemp, _waferNum).ToArray();
+ if (_waferTempDataList.Count < _waferNum)
+ return waferTemps;
+
+ //这届
+ for (int i = 0; i < _waferNum; i++) //只有前面几个温度数据,才是需要统计的
+ {
+ waferTemps[i] = _waferTempDataList[i].Temp;
+ }
+
+ //_waferTempDataList[i].CalZone
+ //for (int i = 0; i < _waferNum; i++)
+ //{
+ // //根据编号,获取同一分区所有温度数据
+ // var tempCaches = _waferTempDataList.Where(item => item.PocketNum == i + 1);
+ // if (tempCaches != null && tempCaches.Count() > 0)
+ // {
+ // //同一分区所有温度数据,查找第一个CalZone为true的温度数据
+ // var matchedItem = tempCaches.Where(item => item.CalZone).FirstOrDefault();
+
+ // if (matchedItem != null)
+ // waferTemps[i] = matchedItem.Temp;
+ // else
+ // waferTemps[i] = tempCaches.First().Temp;//未设置CalZone为true,使用当前分区第一个数据作为默认温度值
+ // }
+ // else
+ // waferTemps[i] = -1;//没有分区温度是乱的无法解析,使用-1作为提示
+ //}
+
+ return waferTemps;
+ }
+
+ public double GetTrayTemp()
+ {
+ if (_waferTempDataList.Count == 0)
+ return _minTemp;
+
+ return _waferTempDataList[_waferTempDataList.Count - 1].Temp; //最后一个数据才是Tray温度
+ }
+
+ #endregion 重新整理温度解析
+
+ public List GetReflectValue()
+ {
+ List reflectList = Enumerable.Range(0, _waferNum)
+ .Select(_ => new double[3]) // 每个数组默认元素为 0
+ .ToList();
+ if (_waferTempDataList.Count < _waferNum)
+ return reflectList;
+
+ for (int i = 0; i < _waferNum; i++) //只有前面几个反射率数据,才是需要统计的
+ {
+ var data = _waferTempDataList[i];
+ reflectList.Add([data.Reflect1, data.Reflect2, data.Reflect3]);
+ }
+
+ return reflectList;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerTemp.cs b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerTemp.cs
new file mode 100644
index 0000000..65f6cc1
--- /dev/null
+++ b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/AKunTriggerTemp/AKunTriggerTemp.cs
@@ -0,0 +1,308 @@
+using Aitex.Core.RT.DataCenter;
+using Aitex.Core.RT.Event;
+using Aitex.Core.RT.IOCore;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.OperationCenter;
+using Aitex.Core.RT.SCCore;
+using Aitex.Core.Util;
+using Kxware.Connectivity.Remoting.Messages;
+using MECF.Framework.Common.Communications.Tcp.Socket.Framing;
+using MECF.Framework.Common.Communications.Tcp.Socket.Server.APM;
+using MECF.Framework.Common.Communications.Tcp.Socket.Server.APM.EventArgs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Xml;
+
+namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
+{
+ ///
+ /// 昂坤测温使用Trigger触发,
+ /// 返回温度数组,依次包含CH平均,Wafer平均,Tray平均
+ /// 目前仅支持一个托盘计算
+ ///
+ public class AKunTriggerTemp : TempSensorBase
+ {
+ private TcpSocketServer _socketServer;
+ private TcpSocketSession _session;
+ private int _port;
+ private string _ip;
+
+ ///
+ /// 温度不变Trig
+ ///
+ private readonly R_TRIG _rTrigTempNoChange = new();
+
+ ///
+ /// 通讯超时时间秒
+ ///
+ private const int TCP_TIME_OUT = 6;
+
+ ///
+ /// 晶圆的数量
+ ///
+ private readonly int _waferNum=1;
+
+ ///
+ /// 晶圆的数量
+ ///
+ private readonly int _trayNum=1;
+
+ /////
+ ///// 一个测温通道对应一个集合索引
+ /////
+ //private readonly List _chWaferTempDataList=new ();
+
+ private double _trayTemp;
+ //private bool[] _calZone;
+
+ ///
+ /// 反射率数组
+ ///
+ private List _reflects;
+
+ //private AKunTriggerChannelWaferTempData[] _tempDataArray;
+
+ private AKunTriggerTempDataManager _aKunTempManager;
+
+ ///
+ /// 定义时钟监视数据发送,有可能通讯还在数据不发送
+ ///
+ private DeviceTimer _timer = new DeviceTimer();
+
+ public AKunTriggerTemp(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
+ {
+ var maxWaferNum = node.GetAttribute("WaferNum");
+
+ //if (string.IsNullOrWhiteSpace(maxWaferNum))
+ // _waferNum = SC.GetStringValue($"System.InchType") == "Inch6" ? 8 : 5;
+ //else
+ // int.TryParse(maxWaferNum, out _waferNum);
+
+ //var trayNum = node.GetAttribute("TrayNum");
+ //if (!string.IsNullOrWhiteSpace(trayNum) && int.TryParse(trayNum, out var maxT))
+ // _trayNum = maxT;
+
+ RTrigs.Add(_rTrigTempNoChange);
+ }
+
+ protected override void InitTempDataFilter()
+ {
+ TempBasFunction = new TempBasFunction(Name, MinimalTemp, _waferNum);
+ _aKunTempManager = new(_waferNum, MinimalTemp);
+
+ _trayTemp = MinimalTemp;
+
+ TempBasFunction.SetTempCh(_waferNum);
+
+ Temp = Enumerable.Repeat(MinimalTemp, _waferNum).ToArray();
+ //_calZone = new bool[_waferNum];
+ //PM可以选的Wafer注册,初始索引需要加上通道数
+ for (var i = 0; i < _waferNum; i++)
+ {
+ var index = i;
+ DATA.Subscribe($"TempSensor.{Name}.Wafer{i + 1}.Temp", () => Temp[index]);
+ }
+
+ //PM可以选的Tray注册,初始索引需要加上通道数和Wafer数量
+ DATA.Subscribe($"TempSensor.{Name}.Tray.Temp", () => _trayTemp);
+
+ //DATA.Subscribe($"TempSensor.{Name}.CalZone", () => _calZone);
+
+ #region 注册反射率数据
+ _reflects = Enumerable
+ .Range(0, _waferNum)
+ .Select(i => new double[3])
+ .ToList();
+
+ for (int i = 0; i < _waferNum; i++)
+ {
+ var index = i;
+ DATA.Subscribe($"TempSensor.{Name}.Wafer{index + 1}.Reflect1", () => _reflects[index][0]);
+ DATA.Subscribe($"TempSensor.{Name}.Wafer{index + 1}.Reflect2", () => _reflects[index][1]);
+ DATA.Subscribe($"TempSensor.{Name}.Wafer{index + 1}.Reflect3", () => _reflects[index][2]);
+ }
+
+ #endregion 注册反射率数据
+
+ OP.Subscribe($"{Module}.AKunTriggerTemp.MarkerActive", (function, args) =>
+ {
+ ProcessWriteMessage($"MarkerActive={args[0]}");
+ return true;
+ });
+
+ OP.Subscribe($"{Module}.AKunTriggerTemp.RunID", (function, args) =>
+ {
+ ProcessWriteMessage($"RunID={args[0]}");
+ return true;
+ });
+
+ OP.Subscribe($"{Module}.AKunTriggerTemp.StepCode", (function, args) =>
+ {
+ ProcessWriteMessage($"StepCode={args[0]}");
+ return true;
+ });
+ }
+
+ protected override bool HandleInitialize()
+ {
+ InitController();
+ return true;
+ }
+
+ private void InitController()
+ {
+ var data = SC.GetStringValue($"{ScBasePath}.{Name}.Address").Split(':');
+ _ip = data[0]; ;
+ _port = Convert.ToInt32(data[1]);
+ TcpSocketServerConfiguration config = new TcpSocketServerConfiguration()
+ {
+ FrameBuilder = new LineBasedFrameBuilder(new LineDelimiter("\r\n")),
+ };
+ _socketServer = new TcpSocketServer(IPAddress.Parse(_ip), _port, config);
+ _socketServer.ClientConnected += ClientConnected;
+ _socketServer.ClientDisconnected += ClientDisconnected;
+ _socketServer.ClientDataReceived += ClientDataReceived;
+ }
+
+ protected override void HandleMonitor()
+ {
+ if (_socketServer == null)
+ return;
+
+ if (!_socketServer.IsListening)
+ {
+ _socketServer.Listen();
+ }
+
+ if (_timer.IsTimeout())
+ {
+ _timer.Stop();
+ if (IsProcess())
+ {
+ TempBasFunction.PMPostLog(Module, $"TCP server received temp data timeout {TCP_TIME_OUT}s, {_ip}:{_port}, {Module}.{Name}", EV.PostWarningLog);
+ TempBasFunction.SetPmIoForInterlock(Module, true);
+ }
+ }
+ }
+
+ private void ClientDataReceived(object sender, TcpClientDataReceivedEventArgs e)
+ {
+ OnReadMessage(Encoding.UTF8.GetString(e.Data, e.DataOffset, e.DataLength));
+ }
+
+ private void OnReadMessage(string message)
+ {
+ _timer.Stop();
+ if (IsEnableLog)
+ LOG.Write(message);
+
+ if (_aKunTempManager.ProcessReceivedData(message))
+ TempReadThread();
+ else
+ TempBasFunction.PMPostLog(Module, $"TCP server received error temp data from AKun {_ip}:{_port}, {Module}.{Name}\r\ninf:'{message}'", EV.PostWarningLog);
+
+ _timer.Start(TCP_TIME_OUT * 1000);
+ }
+
+ private void ClientDisconnected(object sender, TcpClientDisconnectedEventArgs e)
+ {
+ _socketServer.CloseSession(_session.SessionKey);
+ _session = null;
+ TempBasFunction.PMPostLog(Module, $"Can not connect with {_ip}:{_port}, {Module}.{Name}", EV.PostWarningLog);
+ TempBasFunction.SetPmIoForInterlock(Module, true);
+ }
+
+ private void ClientConnected(object sender, TcpClientConnectedEventArgs e)
+ {
+ _session = e.Session;
+ TempBasFunction.PMPostLog(Module, $"Connected with {_ip}:{_port}, {Module}.{Name}", EV.PostInfoLog);
+ TempBasFunction.SetPmIoForInterlock(Module, false);
+ _timer.Start(TCP_TIME_OUT * 1000);
+ }
+
+ private void ProcessWriteMessage(string msg)
+ {
+ TempBasFunction.PMPostLog(Module, $"{Name}: Tcp Socket Write Message {msg}", EV.PostInfoLog);
+ if (_session != null)
+ _session.Send(Encoding.ASCII.GetBytes(msg));
+ else
+ TempBasFunction.PMPostLog(Module, $"Tcp Socket Server is null {_ip}:{_port}, {Module}.{Name}", EV.PostWarningLog);
+ }
+
+ protected override bool DoTempReadThread()
+ {
+ return true;
+ }
+
+ private bool TempReadThread()
+ {
+ // get temp. points according to the "IsSimulatorMode" system config.
+
+ var temps = IsSimMode ? RandomTemps(_waferNum) : HandleReadTemp();
+ _rTrigReadTempFailed.CLK = temps == null || temps.Length != (_waferNum);
+ if (_rTrigReadTempFailed.Q)
+ {
+ LOG.Error(temps != null
+ ? $"{this} {_waferNum} temperature data wanted but {temps.Length} points read."
+ : $"{this} None temperature data read from controller.");
+ }
+
+ // Too less temp. points read from the sensor.
+ if (_rTrigReadTempFailed.M)
+ return true;
+
+ if (IsProcess() && AllTempNoChange(temps))//检测所有通道温度是否变化
+ return true;
+
+ // Get the right temp value from the filter
+ for (var i = 0; i < Temp.Length; i++)
+ {
+ Temp[i] = temps[i];
+ }
+ return true;
+ }
+
+ private bool AllTempNoChange(double[] tempArray)
+ {
+ int noChangeCount = TempBasFunction.AllTempNoChangeCount(tempArray, out string resuonInf);
+ if (noChangeCount >= 18)
+ {
+ _rTrigTempNoChange.CLK = true;
+ if (_rTrigTempNoChange.Q)
+ {
+ TempBasFunction.PMPostLog(Module, $"{this} all temp no change: \r\n" +
+ $"{resuonInf}", EV.PostWarningLog);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override double[] HandleReadTemp()
+ {
+ //昂坤的温度数据上位机在这里被动接受
+ //_calZone = _aKunTempManager.GetCalZone();
+ _trayTemp = _aKunTempManager.GetTrayTemp();
+ _reflects = _aKunTempManager.GetReflectValue();
+
+ return _aKunTempManager.GetWaferTemp();
+ }
+
+ public override void Terminate()
+ {
+ _socketServer.Shutdown();
+ }
+
+ public override void Reset()
+ {
+ _timer.Start(TCP_TIME_OUT * 1000);
+ base.Reset();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempBasFunction.cs b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempBasFunction.cs
index f01e8d9..666904c 100644
--- a/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempBasFunction.cs
+++ b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempBasFunction.cs
@@ -1,7 +1,10 @@
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.SCCore;
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Text;
+using System.Threading.Tasks;
namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
{
@@ -19,20 +22,31 @@ namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
private string DeviceName { get; set; }
- private float[] tempChangeCache;
+ private double[] _chTempChangeCache;
+
+ private double[] _allChTempChangeCache;
//硬件通道数
- private int NumberOfChannels;
+ private int _numberOfChannels;
+
+ private const double Epsilon = 0.001;
///
- /// 统计温度相同次数
+ /// 统计单路温度相同次数
///
- private int noChangeNum;
+ private int _chNoChangeNum;
+
+ ///
+ /// 统计单路温度相同次数
+ ///
+ private int _allChNoChangeNum;
///
/// 记录温度不变时数据,最多记录10次
///
- private StringBuilder strNoChangeMeg = new();
+ private readonly StringBuilder _strChNoChangeMeg = new();
+
+ private readonly StringBuilder _strAllChNoChangeMeg = new();
///
/// 测温硬件最低测试温度,传入温度为此数值,不进行温度数据变化对比
@@ -44,16 +58,24 @@ namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
///
/// 测温硬件名称
/// 最低温度显示,AE和昂坤都是600
- public TempBasFunction(string deviceName , double tempMin,int numberOfChannels)
+ public TempBasFunction(string deviceName, double tempMin, int numberOfChannels)
{
DeviceName = deviceName;
TempMin = tempMin;
- NumberOfChannels = numberOfChannels;
- tempChangeCache = new float[NumberOfChannels];
+ _numberOfChannels = numberOfChannels;
+ _chTempChangeCache = new double[numberOfChannels];
+ _allChTempChangeCache = new double[numberOfChannels];
IsPM1Installed = SC.GetValue($"System.SetUp.IsPM1Installed");
IsPM2Installed = SC.GetValue($"System.SetUp.IsPM2Installed");
}
+ public void SetTempCh(int numberOfChannels)
+ {
+ _numberOfChannels = numberOfChannels;
+ _chTempChangeCache = new double[numberOfChannels];
+ _allChTempChangeCache = new double[numberOfChannels];
+ }
+
public void PM1PM2PostLog(string str, Action action)
{
if (IsPM1Installed)
@@ -84,7 +106,7 @@ namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
{
string PM = pm.ToUpper();
if (IsMP(PM))
- IO.DO[$"PM1.DO_PyroCommunicationError"].SetValue(isReset, out _);
+ IO.DO[$"{PM}.DO_PyroCommunicationError"].SetValue(isReset, out _);
}
///
@@ -102,38 +124,75 @@ namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
private bool IsMP(string pm)
{
- return (pm == "PM1" || pm == "PM2")? true : false;
+ return (pm == "PM1" || pm == "PM2") ? true : false;
}
///
- /// 温度是否发生改变,返回数据不发生改变次数
+ /// 温度是否发生改变(单路温度不变记录),返回数据不发生改变次数
///
///
///
- public int TempInvariantCount(float[] outputTemp, out string errorInf)
+ public int TempNoChangeCount(float[] outputTemp, out string errorInf)
{
errorInf = "";
- if (noChangeNum > 999999)//防止数据溢出
- noChangeNum = 0;
+ if (_chNoChangeNum > 999999)//防止数据溢出
+ _chNoChangeNum = 0;
for (int i = 0; i < outputTemp.Length; i++)
{
if (outputTemp[i] <= TempMin)//低于温度下限,数据不做对比
continue;
-
- if (outputTemp[i] == tempChangeCache[i])
+ if (Math.Abs(outputTemp[i] - _chTempChangeCache[i]) < Epsilon)
{
- if (noChangeNum < 10)//记录前10次的数据
- strNoChangeMeg.Append($"#{i + 1}:{outputTemp[i]:0.00} ");
+ if (_chNoChangeNum < 10)//记录前10次的数据
+ _strChNoChangeMeg.Append($"#{i + 1}:{outputTemp[i]:0.00} ");
- errorInf = strNoChangeMeg.ToString();
- return noChangeNum++;
+ errorInf = _strChNoChangeMeg.ToString();
+ return _chNoChangeNum++;
}
- tempChangeCache[i] = outputTemp[i];
+ _chTempChangeCache[i] = outputTemp[i];
}
//只要数据不同,就可以复位计数和缓存
- strNoChangeMeg.Clear();
- noChangeNum = 0;
+ _strChNoChangeMeg.Clear();
+ _chNoChangeNum = 0;
+ return 0;
+ }
+
+ ///
+ /// 温度是否发生改变(所有温度不变记录),返回数据不发生改变次数
+ ///
+ ///
+ ///
+ public int AllTempNoChangeCount(double[] outputTemp, out string errorInf)
+ {
+ errorInf = "";
+ if (_allChNoChangeNum > 999999)//防止数据溢出
+ _allChNoChangeNum = 0;
+
+ int noChangeCount = 0;
+ for (int i = 0; i < outputTemp.Length; i++)
+ {
+ if (outputTemp[i] <= TempMin)//低于温度下限,数据不做对比
+ continue;
+ if (Math.Abs(outputTemp[i] - _allChTempChangeCache[i]) < Epsilon)
+ {
+ if (_allChNoChangeNum < 10) //记录前10次的数据
+ _strAllChNoChangeMeg.Append($"#{i + 1}: {outputTemp[i]:0.00}; ");
+
+ errorInf = _strAllChNoChangeMeg.ToString();
+ noChangeCount += 1;
+ }
+ _allChTempChangeCache[i] = outputTemp[i];
+ }
+
+ if (noChangeCount == outputTemp.Length)//不变次数等于数组长度时,断定所有数据均无变化
+ {
+ _strAllChNoChangeMeg.Append("\r\n");
+ return _allChNoChangeNum++;
+ }
+ //只要数据不同,就可以复位计数和缓存
+ _strAllChNoChangeMeg.Clear();
+ _allChNoChangeNum = 0;
return 0;
}
}
diff --git a/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempSensorBase.cs b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempSensorBase.cs
index b0623e1..f89e43c 100644
--- a/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempSensorBase.cs
+++ b/Framework/MECF.Framework.RT.EquipmentLibrary/HardwareUnits/Temps/TempSensorBase.cs
@@ -1,73 +1,74 @@
-using System;
-using System.Diagnostics;
-using System.Xml;
+using Aitex.Core.Account;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Communications;
+using System;
+using System.Diagnostics;
+using System.Xml;
namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps;
public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSensor
{
#region Variables
-
+
protected readonly bool IsSimMode;
protected bool IsEnableLog;
protected TempBasFunction TempBasFunction;
private readonly Random _rndTempGen = new();
- private readonly R_TRIG _rTrigReadTempFailed = new();
+ protected readonly R_TRIG _rTrigReadTempFailed = new();
private PeriodicJob _tReadTemp;
- //private Misc.FilterTypes _filterType;
- //private TempDataFilter[] _tempFilters;
- private readonly Stopwatch _swSimSineWave = new ();
+ private readonly Stopwatch _swSimSineWave = new();
private const double SINE_T = 5.0;
private const double SINE_F = 2.0 * Math.PI * (1 / SINE_T);
-
#endregion
-
+
#region Ctor
protected TempSensorBase(string module, XmlElement node, string ioModule = "")
: base(module, node, ioModule)
{
IsSimMode = SC.SafeGetValue("System.IsSimulatorMode", false);
-
+
RTrigs.Add(_rTrigReadTempFailed);
-
+
var maxChStr = node.GetAttribute("MaxChannels");
if (!string.IsNullOrEmpty(maxChStr) && int.TryParse(maxChStr, out var maxCh))
MaxChannels = maxCh;
else
MaxChannels = 4;
-
+
var minTempStr = node.GetAttribute("MinimalTemp");
if (!string.IsNullOrEmpty(minTempStr) && double.TryParse(minTempStr, out var minTemp))
MinimalTemp = minTemp;
else
MinimalTemp = 600.0;
- if(IsSimMode)
+ if (IsSimMode)
_swSimSineWave.Start();
}
#endregion
-
+
#region Properties
public virtual string Address { get; protected set; }
-
+
public virtual bool IsConnected { get; }
-
+
public double MinimalTemp { get; }
-
+
public int MaxChannels { get; }
-
- public double[] Temp { get; private set; }
+
+ ///
+ /// 硬件输出的温度
+ ///
+ public double[] Temp { get; protected set; }
#endregion
@@ -77,10 +78,10 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
/// 生成随机温度。
///
///
- private double[] RandomTemps(double baseTemp = 800, double peakPeak = 200, double jitter = 50.0)
+ protected double[] RandomTemps(int channels, double baseTemp = 800, double peakPeak = 200, double jitter = 50.0)
{
- var rndTemps = new double[MaxChannels];
- for (var i = 0; i < MaxChannels; i++)
+ var rndTemps = new double[channels];
+ for (var i = 0; i < channels; i++)
{
var t = _swSimSineWave.Elapsed.TotalSeconds; // current moment
var tempSine = peakPeak * Math.Sin(SINE_F * t + (i * Math.PI / 3)); //temperature following sine wave with 60 deg(π/3 rad) phase-diff per channel
@@ -91,11 +92,13 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
return rndTemps;
}
- private bool DoTempReadThread()
+
+
+ protected virtual bool DoTempReadThread()
{
// get temp. points according to the "IsSimulatorMode" system config.
- var temps = IsSimMode ? RandomTemps() : HandleReadTemp();
-
+ var temps = IsSimMode ? RandomTemps(MaxChannels) : HandleReadTemp();
+
_rTrigReadTempFailed.CLK = temps == null || temps.Length != MaxChannels;
if (_rTrigReadTempFailed.Q)
{
@@ -112,25 +115,15 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
for (var i = 0; i < MaxChannels; i++)
{
Temp[i] = temps[i];
-
- //_tempFilters[i].AddRawTemp(temps![i]);
-
- //Temp[i] = _filterType switch
- //{
- // Misc.FilterTypes.None => _tempFilters[i].Raw,
- // Misc.FilterTypes.MAF => _tempFilters[i].FilteredMAF,
- // _ => _tempFilters[i].Raw
- //};
}
return true;
}
-
- private void InitTempDataFilter()
+
+ protected virtual void InitTempDataFilter()
{
- //_tempFilters = new TempDataFilter[MaxChannels];
Temp = new double[MaxChannels];
-
+
for (var i = 0; i < MaxChannels; i++)
{
//_tempFilters[i] = new TempDataFilter(this, (i + 1).ToString(), MinimalTemp, ScBasePath);
@@ -138,21 +131,19 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
var ch = i;
DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}", () => Temp[ch]);
- //DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}MAF", () => _tempFilters[ch].FilteredMAF);
- //DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}Raw", () => _tempFilters[ch].Raw);
}
}
-
+
protected virtual double[] HandleReadTemp()
{
return new double[MaxChannels];
}
-
+
protected virtual bool HandleInitialize()
{
return true;
}
-
+
public bool Initialize()
{
try
@@ -164,25 +155,20 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
}
IsEnableLog = SC.SafeGetValue($"TempSensors.EnableLogMessage", false);
-
- //_filterType = ConvertToFilterType(SC.SafeGetStringValue($"{ScBasePath}.FilterType", "None"));
-
+
var pollInvMs = SC.SafeGetValue($"{ScBasePath}.PollInterval", 100);
-
+
// 系统参数变更回调
SC.RegisterValueChangedCallback($"{ScBasePath}.PollInterval",
(obj) => { _tReadTemp.ChangeInterval((int)obj); });
- //SC.RegisterValueChangedCallback($"{ScBasePath}.FilterType",
- // (obj) => { _filterType = ConvertToFilterType(obj?.ToString() ?? ""); });
-
SC.RegisterValueChangedCallback($"TempSensors.EnableLogMessage",
(obj) =>
{
IsEnableLog = bool.TryParse(obj.ToString(), out var en) && en;
});
-
+
TempBasFunction = new TempBasFunction(Name, MinimalTemp, MaxChannels);
TempBasFunction.SetPm1Pm2IoForInterlock(false);
@@ -200,7 +186,12 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
return false;
}
}
-
+
+ public bool IsProcess()
+ {
+ return DATA.Poll($"{Module}.Status").ToString() == "Process";
+ }
+
public virtual bool Connect()
{
return true;
@@ -210,7 +201,7 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
{
return true;
}
-
+
public virtual void Terminate()
{
}
diff --git a/Framework/MECF.Framework.RT.EquipmentLibrary/MECF.Framework.RT.EquipmentLibrary.csproj b/Framework/MECF.Framework.RT.EquipmentLibrary/MECF.Framework.RT.EquipmentLibrary.csproj
index a472f9b..0344c8d 100644
--- a/Framework/MECF.Framework.RT.EquipmentLibrary/MECF.Framework.RT.EquipmentLibrary.csproj
+++ b/Framework/MECF.Framework.RT.EquipmentLibrary/MECF.Framework.RT.EquipmentLibrary.csproj
@@ -336,6 +336,8 @@
+
+