308 lines
11 KiB
C#
308 lines
11 KiB
C#
|
|
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();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|