添加新的测温功能

This commit is contained in:
hanqiangqiang 2026-08-18 14:04:28 +08:00
parent 7580afe92f
commit dc3d0f85f6
8 changed files with 744 additions and 86 deletions

View File

@ -8,6 +8,10 @@
<AKunTemp id="AKunTemp" module="" display="AKunTemp" scBasePath="TempSensors" MaxChannels="4" MinimalTemp="600"/>
</AKunTemps>
<AKunTemps classType="MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps.AKunTriggerTemp" assembly="MECF.Framework.RT.EquipmentLibrary">
<AKunTemp id="AKunTriggerTemp" module="PM1" display="AKunTriggerTemp" scBasePath="TempSensors" MaxChannels="1" WaferNum="1" TrayNum="1" MinimalTemp="500"/>
</AKunTemps>
<SensorTemps classType="MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps.SensorTemp" assembly="MECF.Framework.RT.EquipmentLibrary">
<SensorTemp id="SensorTemp1" module="" display="SensorTemp1" scBasePath="TempSensors.Sensor" MaxChannels="1" MinimalTemp="600"/>
<SensorTemp id="SensorTemp2" module="" display="SensorTemp2" scBasePath="TempSensors.Sensor" MaxChannels="1" MinimalTemp="600"/>

View File

@ -23,8 +23,8 @@
<DI_ITEM Index="20" Name="DI_CDAPressureSWReady" BufferOffset="20" Addr="20" Description="" />
<DI_ITEM Index="21" Name="DI_WaterFlowSWReady" BufferOffset="21" Addr="21" Description="" />
<DI_ITEM Index="22" Name="DI_LoadLockGateClosed" BufferOffset="22" Addr="22" Description="" />
<DI_ITEM Index="24" Name="DI_LoadLSideDoorClosed" BufferOffset="24" Addr="24" Description="" Visible="false"/>
<DI_ITEM Index="25" Name="DI_LoadLSideDoorOpened" BufferOffset="25" Addr="25" Description="" Visible="false"/>
<DI_ITEM Index="24" Name="DI_LoadLSideDoorClosed" BufferOffset="24" Addr="24" Description="" />
<DI_ITEM Index="25" Name="DI_LoadLSideDoorOpened" BufferOffset="25" Addr="25" Description="" />
<DI_ITEM Index="26" Name="DI_LoadRSideDoorClosed" BufferOffset="26" Addr="26" Description="" Visible="false"/>
<DI_ITEM Index="27" Name="DI_LoadRSideDoorOpened" BufferOffset="27" Addr="27" Description="" Visible="false"/>
<DI_ITEM Index="31" Name="DI_RobotOnBackPos" BufferOffset="31" Addr="31" Description="" />
@ -34,7 +34,7 @@
<DI_ITEM Index="35" Name="DI_LoadWaferPlaced" BufferOffset="35" Addr="35" Description="" />
<DI_ITEM Index="36" Name="DI_LoaderTraySensor" BufferOffset="36" Addr="36" Description="" />
<DI_ITEM Index="37" Name="DI_BufferStationOptSensorHigh" BufferOffset="37" Addr="37" Description="" />
<DI_ITEM Index="38" Name="DI_BufferStationOptSensorMid" BufferOffset="38" Addr="38" Description="" Visible="false"/>
<DI_ITEM Index="38" Name="DI_BufferStationOptSensorMid" BufferOffset="38" Addr="38" Description="" />
<DI_ITEM Index="39" Name="DI_BufferStationOptSensorLow" BufferOffset="39" Addr="39" Description="" Visible="false"/>
<DI_ITEM Index="40" Name="DI_UnloadStationWaferPresence" BufferOffset="40" Addr="40" Description="" Visible="false"/>
<DI_ITEM Index="41" Name="DI_UnloadStationGateINTK" BufferOffset="41" Addr="41" Description="" Visible="false"/>

View File

@ -1216,9 +1216,9 @@
<config default="AKunTemp.t2" name="OuterTemp" description="设置Outer温度数据通道重启生效" max="" min="" paramter="AETemp.t1;AETemp.t2;AETemp.t3;AETemp.t4;AKunTemp.t1;AKunTemp.t2;AKunTemp.t3;AKunTemp.t4;SensorTemp1.t1;SensorTemp2.t1;SensorTemp3.t1;SensorTemp4.t1;StrongTemp1.t1;StrongTemp2.t1;StrongTemp3.t1;StrongTemp4.t1;Disabled" tag="ReadOnlySelection" unit="" type="String" visible="false"/>
<config default="Disabled" name="InnerTemp" description="设置Inner温度数据通道重启生效" max="" min="" paramter="AETemp.t1;AETemp.t2;AETemp.t3;AETemp.t4;AKunTemp.t1;AKunTemp.t2;AKunTemp.t3;AKunTemp.t4;SensorTemp1.t1;SensorTemp2.t1;SensorTemp3.t1;SensorTemp4.t1;StrongTemp1.t1;StrongTemp2.t1;StrongTemp3.t1;StrongTemp4.t1;Disabled" tag="ReadOnlySelection" unit="" type="String" visible="false"/>
<config default="AKunTemp.CH1" name="Middle" description="设置Middle温度数据通道重启生效" max="" min="" paramter="AETemp.CH1;AETemp.CH2;AETemp.CH3;AETemp.CH4;AKunTemp.CH1;AKunTemp.CH2;AKunTemp.CH3;AKunTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;StrongTemp1.CH1;StrongTemp2.CH1;StrongTemp3.CH1;StrongTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="AKunTemp.CH2" name="Outer" description="设置Outer温度数据通道重启生效" max="" min="" paramter="AETemp.CH1;AETemp.CH2;AETemp.CH3;AETemp.CH4;AKunTemp.CH1;AKunTemp.CH2;AKunTemp.CH3;AKunTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;StrongTemp1.CH1;StrongTemp2.CH1;StrongTemp3.CH1;StrongTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Inner" description="设置Inner温度数据通道重启生效" max="" min="" paramter="AETemp.CH1;AETemp.CH2;AETemp.CH3;AETemp.CH4;AKunTemp.CH1;AKunTemp.CH2;AKunTemp.CH3;AKunTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;StrongTemp1.CH1;StrongTemp2.CH1;StrongTemp3.CH1;StrongTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Middle" description="设置Middle温度数据通道重启生效" max="" min="" paramter="AKunTriggerTemp.CH1;AKunTriggerTemp.CH2;AKunTriggerTemp.CH3;AKunTriggerTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Outer" description="设置Outer温度数据通道重启生效" max="" min="" paramter="AKunTriggerTemp.CH1;AKunTriggerTemp.CH2;AKunTriggerTemp.CH3;AKunTriggerTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Inner" description="设置Inner温度数据通道重启生效" max="" min="" paramter="AKunTriggerTemp.CH1;AKunTriggerTemp.CH2;AKunTriggerTemp.CH3;AKunTriggerTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="None" name="FilterType" description="温度滤波器类型" max="0" min="0" paramter="None;MAF" tag="ReadOnlySelection" unit="" type="String" />
<configs name="Filters">
<configs name="MAF">
@ -1855,9 +1855,9 @@
<config default="AKunTemp.t4" name="OuterTemp" description="设置Outer温度数据通道重启生效" max="" min="" paramter="AETemp.t1;AETemp.t2;AETemp.t3;AETemp.t4;AKunTemp.t1;AKunTemp.t2;AKunTemp.t3;AKunTemp.t4;SensorTemp1.t1;SensorTemp2.t1;SensorTemp3.t1;SensorTemp4.t1;StrongTemp1.t1;StrongTemp2.t1;StrongTemp3.t1;StrongTemp4.t1;Disabled" tag="ReadOnlySelection" unit="" type="String" visible="false"/>
<config default="Disabled" name="InnerTemp" description="设置Inner温度数据通道重启生效" max="" min="" paramter="AETemp.t1;AETemp.t2;AETemp.t3;AETemp.t4;AKunTemp.t1;AKunTemp.t2;AKunTemp.t3;AKunTemp.t4;SensorTemp1.t1;SensorTemp2.t1;SensorTemp3.t1;SensorTemp4.t1;StrongTemp1.t1;StrongTemp2.t1;StrongTemp3.t1;StrongTemp4.t1;Disabled" tag="ReadOnlySelection" unit="" type="String" visible="false"/>
<config default="Disabled" name="Middle" description="设置Middle温度数据通道重启生效" max="" min="" paramter="AETemp.CH1;AETemp.CH2;AETemp.CH3;AETemp.CH4;AKunTemp.CH1;AKunTemp.CH2;AKunTemp.CH3;AKunTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;StrongTemp1.CH1;StrongTemp2.CH1;StrongTemp3.CH1;StrongTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Outer" description="设置Outer温度数据通道重启生效" max="" min="" paramter="AETemp.CH1;AETemp.CH2;AETemp.CH3;AETemp.CH4;AKunTemp.CH1;AKunTemp.CH2;AKunTemp.CH3;AKunTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;StrongTemp1.CH1;StrongTemp2.CH1;StrongTemp3.CH1;StrongTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Inner" description="设置Inner温度数据通道重启生效" max="" min="" paramter="AETemp.CH1;AETemp.CH2;AETemp.CH3;AETemp.CH4;AKunTemp.CH1;AKunTemp.CH2;AKunTemp.CH3;AKunTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;StrongTemp1.CH1;StrongTemp2.CH1;StrongTemp3.CH1;StrongTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Middle" description="设置Middle温度数据通道重启生效" max="" min="" paramter="AKunTriggerTemp.CH1;AKunTriggerTemp.CH2;AKunTriggerTemp.CH3;AKunTriggerTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Outer" description="设置Outer温度数据通道重启生效" max="" min="" paramter="AKunTriggerTemp.CH1;AKunTriggerTemp.CH2;AKunTriggerTemp.CH3;AKunTriggerTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="Disabled" name="Inner" description="设置Inner温度数据通道重启生效" max="" min="" paramter="AKunTriggerTemp.CH1;AKunTriggerTemp.CH2;AKunTriggerTemp.CH3;AKunTriggerTemp.CH4;SensorTemp1.CH1;SensorTemp2.CH1;SensorTemp3.CH1;SensorTemp4.CH1;Disabled" tag="ReadOnlySelection" unit="" type="String" />
<config default="None" name="FilterType" description="温度滤波器类型" max="0" min="0" paramter="None;MAF" tag="ReadOnlySelection" unit="" type="String" />
<configs name="Filters">
<configs name="MAF">
@ -2239,6 +2239,12 @@
<config default="10" name="ControllerAverageTime" description="AKun温度平滑时间,AKun间隔/ms采样温度设置后立即生效" max="10000" min="1" paramter="" tag="" unit="ms" type="Integer" />
</configs>
<configs name="AKunTriggerTemp">
<config default="false" name="EnableDevice" description="AKun带Trigger设备启用设置后重启生效" max="" min="" paramter="" tag="" unit="" type="Bool" />
<config default="127.0.0.1:55888" name="Address" description="AKunTrigger IP addressDefault IP: 127.0.0.1:55888" max="" min="" paramter="" tag="" unit="" type="String" />
</configs>
<configs name="Sensor">
<configs name="SensorTemp1">
<config default="false" name="EnableDevice" description="Sensor1启用设置后重启生效" max="" min="" paramter="" tag="" unit="" type="Bool" />

View File

@ -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,
}
/// <summary>
/// 测温点位置
/// </summary>
public enum EnumZoneNum
{
Center,
CenterEdge,
Edge
}
public class AKunTriggerChannelWaferTempData
{
/// <summary>
/// 创建通道对应的单个晶圆温度对象
/// </summary>
/// <param name="waferNum">晶圆编号</param>
/// <param name="ch">测温设备通道编号</param>
/// <param name="name">当前晶圆对象的名称Wafer or Tray,测温对象也能反馈Tray温度</param>
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
}
/// <summary>
/// 通道编号默认为1
/// </summary>
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; }
/// <summary>
/// 0表示无效晶圆代表Dummy晶圆或者空位此位置的温度不能参与STC控温
/// </summary>
public int PocketNum { get; set; }
/// <summary>
/// The reflection of 404nm
/// </summary>
public double Reflect1 { get; set; }
/// <summary>
/// The reflection of 633nm.
/// </summary>
public double Reflect2 { get; set; }
/// <summary>
/// The reflection of 950nm.
/// </summary>
public double Reflect3 { get; set; }
public double Curvature { get; set; }
/// <summary>
/// false不参与控温晶圆温度是否参与区域计算不放片或者放Dummy时不参与区域计算
/// </summary>
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<AKunTriggerChannelWaferTempData> _waferTempDataList = new();
private static readonly object _lock = new object();
#region
public bool ProcessReceivedData(string tcpReceive)
{
lock (_lock)
{
List<AKunTriggerChannelWaferTempData> tempDataList = new List<AKunTriggerChannelWaferTempData>();
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<double[]> GetReflectValue()
{
List<double[]> 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;
}
}
}

View File

@ -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
{
/// <summary>
/// 昂坤测温使用Trigger触发
/// 返回温度数组依次包含CH平均Wafer平均Tray平均
/// 目前仅支持一个托盘计算
/// </summary>
public class AKunTriggerTemp : TempSensorBase
{
private TcpSocketServer _socketServer;
private TcpSocketSession _session;
private int _port;
private string _ip;
/// <summary>
/// 温度不变Trig
/// </summary>
private readonly R_TRIG _rTrigTempNoChange = new();
/// <summary>
/// 通讯超时时间秒
/// </summary>
private const int TCP_TIME_OUT = 6;
/// <summary>
/// 晶圆的数量
/// </summary>
private readonly int _waferNum=1;
/// <summary>
/// 晶圆的数量
/// </summary>
private readonly int _trayNum=1;
///// <summary>
///// 一个测温通道对应一个集合索引
///// </summary>
//private readonly List<AKunTriggerChannelWaferTempData[]> _chWaferTempDataList=new ();
private double _trayTemp;
//private bool[] _calZone;
/// <summary>
/// 反射率数组
/// </summary>
private List<double[]> _reflects;
//private AKunTriggerChannelWaferTempData[] _tempDataArray;
private AKunTriggerTempDataManager _aKunTempManager;
/// <summary>
/// 定义时钟监视数据发送,有可能通讯还在数据不发送
/// </summary>
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();
}
}
}

View File

@ -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;
/// <summary>
/// 统计温度相同次数
/// 统计单路温度相同次数
/// </summary>
private int noChangeNum;
private int _chNoChangeNum;
/// <summary>
/// 统计单路温度相同次数
/// </summary>
private int _allChNoChangeNum;
/// <summary>
/// 记录温度不变时数据最多记录10次
/// </summary>
private StringBuilder strNoChangeMeg = new();
private readonly StringBuilder _strChNoChangeMeg = new();
private readonly StringBuilder _strAllChNoChangeMeg = new();
/// <summary>
/// 测温硬件最低测试温度,传入温度为此数值,不进行温度数据变化对比
@ -44,16 +58,24 @@ namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps
/// </summary>
/// <param name="deviceName">测温硬件名称</param>
/// <param name="tempMin">最低温度显示AE和昂坤都是600</param>
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<bool>($"System.SetUp.IsPM1Installed");
IsPM2Installed = SC.GetValue<bool>($"System.SetUp.IsPM2Installed");
}
public void SetTempCh(int numberOfChannels)
{
_numberOfChannels = numberOfChannels;
_chTempChangeCache = new double[numberOfChannels];
_allChTempChangeCache = new double[numberOfChannels];
}
public void PM1PM2PostLog(string str, Action<string, string, int> 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 _);
}
/// <summary>
@ -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;
}
/// <summary>
/// 温度是否发生改变,返回数据不发生改变次数
/// 温度是否发生改变(单路温度不变记录),返回数据不发生改变次数
/// </summary>
/// <param name="outputTemp"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 温度是否发生改变(所有温度不变记录),返回数据不发生改变次数
/// </summary>
/// <param name="outputTemp"></param>
/// <returns></returns>
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;
}
}

View File

@ -1,12 +1,13 @@
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;
@ -19,15 +20,12 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
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
@ -51,7 +49,7 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
else
MinimalTemp = 600.0;
if(IsSimMode)
if (IsSimMode)
_swSimSineWave.Start();
}
@ -67,7 +65,10 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
public int MaxChannels { get; }
public double[] Temp { get; private set; }
/// <summary>
/// 硬件输出的温度
/// </summary>
public double[] Temp { get; protected set; }
#endregion
@ -77,10 +78,10 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
/// 生成随机温度。
/// </summary>
/// <returns></returns>
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,10 +92,12 @@ 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,23 +115,13 @@ 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++)
@ -138,8 +131,6 @@ 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);
}
}
@ -165,8 +156,6 @@ 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);
@ -174,9 +163,6 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
SC.RegisterValueChangedCallback($"{ScBasePath}.PollInterval",
(obj) => { _tReadTemp.ChangeInterval((int)obj); });
//SC.RegisterValueChangedCallback($"{ScBasePath}.FilterType",
// (obj) => { _filterType = ConvertToFilterType(obj?.ToString() ?? ""); });
SC.RegisterValueChangedCallback($"TempSensors.EnableLogMessage",
(obj) =>
{
@ -201,6 +187,11 @@ public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSe
}
}
public bool IsProcess()
{
return DATA.Poll($"{Module}.Status").ToString() == "Process";
}
public virtual bool Connect()
{
return true;

View File

@ -336,6 +336,8 @@
<Compile Include="HardwareUnits\Temps\AE\AETempConnection.cs" />
<Compile Include="HardwareUnits\Temps\AE\AETempHandler.cs" />
<Compile Include="HardwareUnits\Temps\AKunTemp\AKunTemp.cs" />
<Compile Include="HardwareUnits\Temps\AKunTriggerTemp\AKunTriggerChannelWaferTempData.cs" />
<Compile Include="HardwareUnits\Temps\AKunTriggerTemp\AKunTriggerTemp.cs" />
<Compile Include="HardwareUnits\Temps\Eurotherm\P116PIDTC.cs" />
<Compile Include="HardwareUnits\Temps\Eurotherm\P116PIDTCHandler.cs" />
<Compile Include="HardwareUnits\Temps\Eurotherm\P116PIDTCConnection.cs" />