Sic.Framework/MECF.Framework.UI.Client/CenterViews/Modules/PM/PMProcessGasFlowCounterView...

527 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define _FAKE_DATA
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Data;
using Aitex.Core.RT.Log;
using MECF.Framework.Common.DataCenter;
using MECF.Framework.UI.Client.CenterViews.Modules.PM;
using MECF.Framework.UI.Client.ClientBase;
using Sicentury.Core;
#if FAKE_DATA
using System.Threading;
using System.Threading.Tasks;
#endif
namespace SicUI.Models.PMs
{
public class ObservableQueue<T> : Queue<T>, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
private readonly object _syncRoot = new object();
#region Constructors
public ObservableQueue()
{
LimitSize = 0;
}
public ObservableQueue(int capacity) : base(capacity)
{
LimitSize = capacity;
}
#endregion
#region Properties
public int LimitSize { get; }
public new virtual void Enqueue(T obj)
{
lock (_syncRoot)
{
base.Enqueue(obj);
if (LimitSize > 0 && base.Count > LimitSize)
base.Dequeue();
CollectionChanged?.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, obj));
}
}
public new virtual T Dequeue()
{
lock (_syncRoot)
{
var obj = base.Dequeue();
CollectionChanged?.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, obj));
return obj;
}
}
public new virtual void Clear()
{
lock (_syncRoot)
{
base.Clear();
CollectionChanged?.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
#endregion
}
public class GasFlowCounter : BindableBase
{
#region Variables
#endregion
#region Constructors
public GasFlowCounter(string gasName)
{
GasName = gasName;
TotalFlow = 0;
}
public GasFlowCounter(string gasName, double initValue) : this(gasName)
{
TotalFlow = initValue;
}
#endregion
#region Properties
public string GasName { get; }
public double TotalFlow { get; private set; }
#endregion
#region Methods
public void AddFlow(double increment)
{
TotalFlow += increment;
}
public override string ToString()
{
return $"{TotalFlow:F1}";
}
#endregion
}
public class PmGasFlowCountInfo : BindableBase
{
#region Variables
private DateTime _statStart;
private DateTime? _statEnd;
#endregion
#region Constructors
public PmGasFlowCountInfo(int index, Guid uid, string recipeName, string waferUid, DateTime beginTime, DateTime endTime, double h2,
double ar,
double pn2, double hcl, double sih4, double cxhx, double tcs, double tma)
{
Index = index;
StatisticsUid = uid;
RecipeName = recipeName;
WaferId = waferUid;
StatisticsStart = beginTime;
StatisticsEnd = endTime;
H2 = new GasFlowCounter(nameof(H2), h2);
Ar = new GasFlowCounter(nameof(Ar), ar);
PN2 = new GasFlowCounter(nameof(PN2), pn2);
HCL = new GasFlowCounter(nameof(HCL), hcl);
SiH4 = new GasFlowCounter(nameof(SiH4), sih4);
CxHx = new GasFlowCounter(nameof(CxHx), cxhx);
TCS = new GasFlowCounter(nameof(TCS), tcs);
TMA = new GasFlowCounter(nameof(TMA), tma);
}
public PmGasFlowCountInfo()
{
StatisticsUid = Guid.NewGuid();
WaferId = string.Empty;
RecipeName = string.Empty;
StatisticsStart = DateTime.Now;
H2 = new GasFlowCounter(nameof(H2));
Ar = new GasFlowCounter(nameof(Ar));
PN2 = new GasFlowCounter(nameof(PN2));
HCL = new GasFlowCounter(nameof(HCL));
SiH4 = new GasFlowCounter(nameof(SiH4));
CxHx = new GasFlowCounter(nameof(CxHx));
TCS = new GasFlowCounter(nameof(TCS));
TMA = new GasFlowCounter(nameof(TMA));
}
public PmGasFlowCountInfo(string waferId) : this()
{
WaferId = waferId;
}
#endregion
#region Properties
public int Index { get; set; }
public string WaferId { get; }
public string RecipeName { get; }
public Guid StatisticsUid { get; }
public DateTime StatisticsStart
{
get => _statStart;
set => Set(ref _statStart, value);
}
public DateTime? StatisticsEnd
{
get => _statEnd;
set => Set(ref _statEnd, value);
}
public GasFlowCounter H2 { get; }
public GasFlowCounter Ar { get; }
public GasFlowCounter PN2 { get; }
public GasFlowCounter HCL { get; }
public GasFlowCounter SiH4 { get; }
public GasFlowCounter CxHx { get; }
public GasFlowCounter TCS { get; }
public GasFlowCounter TMA { get; }
#endregion
}
public class PMProcessGasFlowCounterViewModel : UiViewModelBase
{
#region Variables
private const int MAX_ROWS = 10;
private readonly object _syncObject = new object();
private readonly PMProcessViewModel _pmProcessVm;
private string _previousStatus;
#if FAKE_DATA
private readonly Random _randomFlow;
#endif
#endregion
#region Constructors
public PMProcessGasFlowCounterViewModel(PMProcessViewModel vm)
{
_previousStatus = "Init";
_pmProcessVm = vm;
_pmProcessVm.PropertyChanged += VmOnPropertyChanged;
GasFlowCountCollection = new ObservableQueue<PmGasFlowCountInfo>(MAX_ROWS);
GasFlowCountCollection.CollectionChanged += GasFlowCountCollectionOnCollectionChanged;
// 加载历史记录。
ReadLastFlowCounts();
#if FAKE_DATA
_randomFlow = new Random();
//for (var i = 0; i < 10; i++)
//{
// var counter = new PmGasFlowCountInfo();
// 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);
// CurrentFlowCounter = counter;
//}
// Add new Flow Counter every 5s
Task.Run(() =>
{
while (true)
{
Thread.Sleep(5000);
if (CurrentFlowCounter != null)
{
// 结束当前的统计,随后开启新统计
CurrentFlowCounter.StatisticsEnd = DateTime.Now;
SaveFlowCount(CurrentFlowCounter);
}
var counter = new PmGasFlowCountInfo();
GasFlowCountCollection.Enqueue(counter);
CurrentFlowCounter = counter;
}
});
#endif
}
#endregion
#region Properties
public ObservableQueue<PmGasFlowCountInfo> GasFlowCountCollection { get; }
public PmGasFlowCountInfo CurrentFlowCounter { get; private set; }
public ICollectionView GasFlowDetailList
{
get
{
var view = CollectionViewSource.GetDefaultView(GasFlowCountCollection.ToList());
view.SortDescriptions.Add(new SortDescription(nameof(PmGasFlowCountInfo.Index),
ListSortDirection.Ascending));
return view;
}
}
#endregion
#region Methods
private void GasFlowCountCollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// 列表发生变化时重新排序。
if (e.Action == NotifyCollectionChangedAction.Add)
{
var i = 1;
GasFlowCountCollection.ToList().OrderByDescending(x => x.StatisticsStart).ToList().ForEach(x =>
{
x.Index = i;
i++;
});
}
}
/// <summary>
/// 监测Process的Status属性是否变化以确定Process的开始和结束时机。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void VmOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(PMProcessViewModel.Status))
{
var currentStatus = _pmProcessVm.Status;
if (CheckIfProcessStarted(_previousStatus, currentStatus))
{
// Process启动
lock (_syncObject)
{
CurrentFlowCounter = new PmGasFlowCountInfo();
GasFlowCountCollection.Enqueue(CurrentFlowCounter);
}
}
else if (CheckIfProcessStopped(_previousStatus, currentStatus))
{
lock (_syncObject)
{
// Process结束
if (CurrentFlowCounter != null)
{
CurrentFlowCounter.StatisticsEnd = DateTime.Now;
// 写数据库
SaveFlowCount(CurrentFlowCounter);
}
}
}
Debug.WriteLineIf(_previousStatus != currentStatus,
$"PM Process Status: {_previousStatus} to {currentStatus}");
_previousStatus = currentStatus;
}
}
private bool CheckIfProcessStarted(string previousStatus, string currentStatus)
{
if (previousStatus == "ProcessIdle" &&
currentStatus == "PreProcess")
return true;
return false;
}
private bool CheckIfProcessStopped(string previousStatus, string currentStatus)
{
if ((previousStatus != "ProcessIdle" &&
currentStatus == "ProcessIdle") ||
(previousStatus != "Error" &&
currentStatus == "Error"))
return true;
return false;
}
/// <summary>
/// 将统计值保存到数据库。
/// </summary>
/// <param name="count"></param>
private void SaveFlowCount(PmGasFlowCountInfo count)
{
try
{
var c = count;
var sql = $"INSERT INTO process_flow_data " +
$"(process_guid, module_name, recipe_name, wafer_guid, process_begin_time, process_end_time, h2, ar, pn2, hcl, sih4, c3h8, tcs, tma) " +
$"VALUES " +
$"('{c.StatisticsUid:D}', '{_pmProcessVm.SystemName}', '{_pmProcessVm.SelectedRecipe}', '{c.WaferId:D}', " +
$"'{c.StatisticsStart:yyyy/MM/dd HH:mm:ss.fff}', '{c.StatisticsEnd:yyyy/MM/dd HH:mm:ss.fff}', " +
$"{c.H2}, {c.Ar}, {c.PN2}, {c.HCL}, {c.SiH4}, {c.CxHx}, {c.TCS}, {c.TMA})";
QueryDataClient.Instance.Service.QueryData(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 = MAX_ROWS)
{
try
{
var sql =
$"SELECT * FROM process_flow_data where module_name = '{_pmProcessVm.SystemName}' " +
$" ORDER BY process_begin_time DESC LIMIT {limit}";
var dt = QueryDataClient.Instance.Service.QueryData(sql);
if (dt != null && dt.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["c3h8"].ToString());
var tcs = double.Parse(row["tcs"].ToString());
var tma = double.Parse(row["tma"].ToString());
var counter = new PmGasFlowCountInfo(i + 1, uid, recipeName, waferUid, beginTime, endTime, h2, ar, pn2,
hcl,
sih4, cxhx, tcs, tma);
GasFlowCountCollection.Enqueue(counter);
}
catch (Exception ex)
{
LOG.Error($"Unable to load process flow count history at row {i}, {ex.Message}", ex);
}
}
}
}
catch (Exception ex)
{
LOG.Error($"Unable to load process flow count history, {ex.Message}", ex);
}
}
protected override void Poll()
{
base.Poll();
lock (_syncObject)
{
if (CurrentFlowCounter != null && CurrentFlowCounter.StatisticsEnd.HasValue == false)
{
#if FAKE_DATA
CurrentFlowCounter.H2.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.Ar.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.PN2.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.HCL.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.SiH4.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.CxHx.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.TCS.AddFlow(_randomFlow.NextDouble());
CurrentFlowCounter.TMA.AddFlow(_randomFlow.NextDouble());
#else
CurrentFlowCounter.H2.AddFlow(_pmProcessVm.H2Flow);
CurrentFlowCounter.Ar.AddFlow(_pmProcessVm.ArFlow);
CurrentFlowCounter.PN2.AddFlow(_pmProcessVm.PN2Flow);
CurrentFlowCounter.HCL.AddFlow(_pmProcessVm.HCLFlow);
CurrentFlowCounter.SiH4.AddFlow(_pmProcessVm.SiH4Flow);
CurrentFlowCounter.CxHx.AddFlow(_pmProcessVm.C2H4Flow);
CurrentFlowCounter.TCS.AddFlow(_pmProcessVm.TCSFlow);
CurrentFlowCounter.TMA.AddFlow(_pmProcessVm.TMAFlow);
#endif
}
}
}
#endregion
}
}