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(); } } }