Sic.Framework-Nanjing-Baishi/MECF.Framework.RT.Core/Managers/PDS/ProcessDataStatManager.cs

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
}
}