318 lines
11 KiB
C#
318 lines
11 KiB
C#
#define _FAKE_DATA
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using Aitex.Core.Common.DeviceData.IoDevice;
|
|
using Aitex.Core.RT.DataCenter;
|
|
using Aitex.Core.RT.DBCore;
|
|
using Aitex.Core.RT.Log;
|
|
using Aitex.Core.Util;
|
|
using Sicentury.Core.Collections;
|
|
#if FAKE_DATA
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
#endif
|
|
|
|
|
|
namespace MECF.Framework.RT.Core.Managers.PDS
|
|
{
|
|
public class ProcessDataStatManager
|
|
{
|
|
#region Variables
|
|
|
|
/// <summary>
|
|
/// 工艺数据统计列表中显示的最大行数
|
|
/// </summary>
|
|
private const int DISPLAYED_ROWS_MAX = 10;
|
|
|
|
private readonly object _syncObject = new();
|
|
|
|
/// <summary>
|
|
/// 后台统计工艺数据的线程
|
|
/// </summary>
|
|
private readonly PeriodicJob _tStatistic;
|
|
|
|
#if FAKE_DATA
|
|
|
|
private readonly Random _randomFlow;
|
|
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
public ProcessDataStatManager(string module)
|
|
{
|
|
Debug.Assert(!string.IsNullOrEmpty(module), "the module is not assigned");
|
|
|
|
Module = module;
|
|
GasFlowCountCollection = new ObservableQueue<ProcessDataStatPerRun>(DISPLAYED_ROWS_MAX);
|
|
GasFlowCountCollection.CollectionChanged += GasFlowCountCollectionOnCollectionChanged;
|
|
|
|
// 加载历史记录。
|
|
ReadLastFlowCounts();
|
|
|
|
DATA.Subscribe($"{Module}.ProcessDataStat.RealtimeList", ()=> ProcessRunDataList);
|
|
|
|
_tStatistic = new PeriodicJob(1000, OnTimer, "MonitorProcDataStatPerRun", false);
|
|
|
|
#if FAKE_DATA
|
|
|
|
_randomFlow = new Random();
|
|
|
|
//for (var i = 0; i < 10; i++)
|
|
//{
|
|
// var counter = new GasFlowPerRun();
|
|
// counter.H2.AddFlow(_randomFlow.NextDouble());
|
|
// counter.Ar.AddFlow(_randomFlow.NextDouble());
|
|
// counter.PN2.AddFlow(_randomFlow.NextDouble());
|
|
// counter.HCL.AddFlow(_randomFlow.NextDouble());
|
|
// counter.SiH4.AddFlow(_randomFlow.NextDouble());
|
|
// counter.C3H8.AddFlow(_randomFlow.NextDouble());
|
|
// counter.TCS.AddFlow(_randomFlow.NextDouble());
|
|
// counter.TMA.AddFlow(_randomFlow.NextDouble());
|
|
// counter.StatisticsEnd = DateTime.Now;
|
|
|
|
// GasFlowCountCollection.Enqueue(counter);
|
|
|
|
// CurrentDataStat = counter;
|
|
//}
|
|
|
|
// Add new Flow Counter every 5s
|
|
Task.Run(() =>
|
|
{
|
|
while (true)
|
|
{
|
|
Thread.Sleep(5000);
|
|
if (CurrentDataStat != null)
|
|
{
|
|
// 结束当前的统计,随后开启新统计
|
|
CurrentDataStat.StatisticsEnd = DateTime.Now;
|
|
SaveFlowCount(CurrentDataStat);
|
|
}
|
|
|
|
var counter = new GasFlowPerRun();
|
|
GasFlowCountCollection.Enqueue(counter);
|
|
CurrentDataStat = counter;
|
|
}
|
|
});
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public string Module { get; private set; }
|
|
|
|
public ObservableQueue<ProcessDataStatPerRun> GasFlowCountCollection { get; }
|
|
|
|
public ProcessDataStatPerRun CurrentDataStat { get; private set; }
|
|
|
|
public List<AITProcessRunData> ProcessRunDataList
|
|
{
|
|
get
|
|
{
|
|
var list = new List<AITProcessRunData>();
|
|
lock (_syncObject)
|
|
{
|
|
list.AddRange(GasFlowCountCollection.OrderBy(x => x.Index)
|
|
.ToList()
|
|
.Select(item => new AITProcessRunData
|
|
{
|
|
Uid = item.StatisticsUid,
|
|
Index = item.Index,
|
|
Module = item.Module,
|
|
RecipeName = item.RecipeName.Replace("Sic\\Process\\", ""),
|
|
WaferId = item.WaferId,
|
|
ProcessBegin = item.StatisticsStart,
|
|
ProcessEnd = item.StatisticsEnd,
|
|
H2 = item.H2.Total,
|
|
Ar = item.Ar.Total,
|
|
PN2 = item.PN2.Total,
|
|
HCL = item.HCL.Total,
|
|
SiH4 = item.SiH4.Total,
|
|
C2H4 = item.C2H4.Total,
|
|
TCS = item.TCS.Total,
|
|
TMA = item.TMA.Total,
|
|
HeaterPowerConsumption = item.HeaterPowerConsumption.Total
|
|
}));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
private bool OnTimer()
|
|
{
|
|
Monitor();
|
|
return true;
|
|
}
|
|
|
|
private void GasFlowCountCollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
// 列表发生变化时重新排序。
|
|
if (e.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
var i = 1;
|
|
GasFlowCountCollection.ToList().OrderBy(x => x.StatisticsStart).ToList().ForEach(x =>
|
|
{
|
|
x.Index = i;
|
|
i++;
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将统计值保存到数据库。
|
|
/// </summary>
|
|
/// <param name="runData"></param>
|
|
private void SaveFlowCount(ProcessDataStatPerRun runData)
|
|
{
|
|
try
|
|
{
|
|
var sql = $"INSERT INTO process_data_stat_per_run " +
|
|
$"(process_guid, module_name, recipe_name, wafer_guid, process_begin_time, process_end_time, h2, ar, pn2, hcl, sih4, c2h4, tcs, tma, heater_power_consumption) " +
|
|
$"VALUES " +
|
|
$"('{runData.StatisticsUid:D}', '{Module}', '{runData.RecipeName}', '{runData.WaferId:D}', " +
|
|
$"'{runData.StatisticsStart:yyyy/MM/dd HH:mm:ss.fff}', '{runData.StatisticsEnd:yyyy/MM/dd HH:mm:ss.fff}', " +
|
|
$"{runData.H2.Total}, {runData.Ar.Total}, {runData.PN2.Total}, {runData.HCL.Total}, {runData.SiH4.Total}, {runData.C2H4.Total}, {runData.TCS.Total}, {runData.TMA.Total}, {runData.HeaterPowerConsumption.Total})";
|
|
|
|
DB.ExecuteNonQuery(sql);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LOG.Error($"Unable to save total gas flow to database, {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从数据库加载指定的记录。
|
|
/// </summary>
|
|
/// <param name="limit"></param>
|
|
private void ReadLastFlowCounts(int limit = DISPLAYED_ROWS_MAX)
|
|
{
|
|
try
|
|
{
|
|
var sql =
|
|
$"SELECT * FROM process_data_stat_per_run where module_name = '{Module}' " +
|
|
$" ORDER BY process_begin_time DESC LIMIT {limit}";
|
|
var dt = DB.ExecuteDataTable(sql);
|
|
if (dt is { Rows.Count: > 0 })
|
|
{
|
|
for (var i = 0; i < dt.Rows.Count; i++)
|
|
{
|
|
try
|
|
{
|
|
var row = dt.Rows[i];
|
|
var uid = Guid.Parse(row["process_guid"].ToString());
|
|
var recipeName = row["recipe_name"].ToString();
|
|
var waferUid = row["wafer_guid"].ToString();
|
|
var beginTime = DateTime.Parse(row["process_begin_time"].ToString());
|
|
var endTime = DateTime.Parse(row["process_end_time"].ToString());
|
|
var h2 = double.Parse(row["h2"].ToString());
|
|
var ar = double.Parse(row["ar"].ToString());
|
|
var pn2 = double.Parse(row["pn2"].ToString());
|
|
var hcl = double.Parse(row["hcl"].ToString());
|
|
var sih4 = double.Parse(row["sih4"].ToString());
|
|
var cxhx = double.Parse(row["c2h4"].ToString());
|
|
var tcs = double.Parse(row["tcs"].ToString());
|
|
var tma = double.Parse(row["tma"].ToString());
|
|
var heaterPowerConsumption = double.Parse(row["heater_power_consumption"].ToString());
|
|
var run = new ProcessDataStatPerRun(i + 1, uid, Module, recipeName, waferUid, beginTime, endTime, h2, ar, pn2,
|
|
hcl, sih4, cxhx, tcs, tma, heaterPowerConsumption);
|
|
|
|
GasFlowCountCollection.Enqueue(run);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LOG.Error($"Unable to load process data statistic history at row {i}, {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LOG.Error($"Unable to load process data statistic history, {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
|
|
public void Monitor()
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
if (CurrentDataStat != null && CurrentDataStat.StatisticsEnd.HasValue == false)
|
|
{
|
|
#if FAKE_DATA
|
|
CurrentDataStat.H2.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.Ar.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.PN2.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.HCL.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.SiH4.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.CxHx.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.TCS.AddFlow(_randomFlow.NextDouble());
|
|
CurrentDataStat.TMA.AddFlow(_randomFlow.NextDouble());
|
|
#else
|
|
|
|
CurrentDataStat.Accumulate();
|
|
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动统计。
|
|
/// </summary>
|
|
public void Begin(string recipeName)
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
CurrentDataStat = new ProcessDataStatPerRun(Module);
|
|
CurrentDataStat.RecipeName = recipeName;
|
|
GasFlowCountCollection.Enqueue(CurrentDataStat);
|
|
|
|
// 启动数据采集线程
|
|
_tStatistic.Start();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 结束上次统计。
|
|
/// </summary>
|
|
public void End()
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
_tStatistic.Pause();
|
|
|
|
// Process结束
|
|
if (CurrentDataStat != null)
|
|
{
|
|
CurrentDataStat.StatisticsEnd = DateTime.Now;
|
|
|
|
// 写数据库
|
|
SaveFlowCount(CurrentDataStat);
|
|
|
|
CurrentDataStat = null;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|