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