using System.Collections.Generic; using Aitex.Core.Common.DeviceData; using Aitex.Core.Util; using Caliburn.Micro; using MECF.Framework.Common.OperationCenter; using System; using MECF.Framework.Common.DataCenter; using MECF.Framework.UI.Client.CenterViews.Editors.Recipe; using MECF.Framework.UI.Client.ClientBase; using System.Windows; using Caliburn.Micro.Core; using System.Windows.Media; using System.Windows.Threading; using System.Data; using System.Diagnostics; using System.Threading.Tasks; using Aitex.Core.Common.DeviceData.IoDevice; using OpenSEMI.ClientBase; using Sicentury.Core; using MECF.Framework.UI.Client.CenterViews.Editors; using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn; using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel; using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params; using Action = System.Action; using Aitex.Core.RT.Device.PmDevices; using MECF.Framework.Common.Aitex.Core.Common.DeviceData; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.GasFlow; using System.Threading; using Sicentury.Core.EventArgs; using System.Linq; using MECF.Framework.Common.Account.Permissions; using MECF.Framework.UI.Client.Core; using MECF.Framework.UI.Core.Accounts; using System.Reflection; namespace MECF.Framework.UI.Client.CenterViews.Modules.PM { public class PMProcessViewModel : SicModuleUIViewModelBase, ISupportMultipleSystem, IHandle { private double _tmaFlowRatio = 1; private double _tcsFlowRatio = 1; private double _hclFlowRatio = 1; private double _C2H4FlowRatio = 1; private double _sih4FlowRatio = 1; private double _pn2FlowRatio = 1; private bool _isHideRecipeValue = false; private bool _allowSaveRecipe = false; private bool _allowRipRecipe = false; private int x = 0; private DispatcherTimer timer; private readonly object _lockerLoadingRecipe = new(); private readonly IRecipeGasFlowCalculator _recipeGasFlowCalculator; private string _currentProcessingRecipe; private string _selectedRecipe; private bool _needLoadRecipe = false; private IProgress _progressRecipeStepChanged; private IProgress _progressLoadRecipe; private CancellationTokenSource _cts; public PMProcessViewModel() { _recipeGasFlowCalculator = IoC.Get(); IsBusyGasFlowSum = false; _busyIndicatorContentExport = new Progress(e => { IsBusyGasFlowSum = e.Message == "" ? false : true; BusyIndicatorContent = e.Message; }); _progressLoadRecipe = new Progress(ProgLoadRecipeOnUiThread); _progressRecipeStepChanged = new Progress(RecipeStepInProcessChanged); } #region Properties public bool AllowSaveRecipe { get => _allowSaveRecipe; set { _allowSaveRecipe = value; NotifyOfPropertyChange(); } } public bool AllowRipRecipe { get => _allowRipRecipe; set { _allowRipRecipe = value; NotifyOfPropertyChange(); } } public string ChType { get; set; } [Subscription("IsBusy")] public bool IsBusy { get; set; } [Subscription("Status")] public string Status { get; set; } public bool IsPMProcess => Status == "Process" || Status == "PostProcess" || Status == "Paused" || Status == "PMMacroPause" || Status == "PMMacro" || Status == "PostPMMacro"; public bool IsPreProcess => Status == "PreProcess" || Status == "PrePMMacro"; [Subscription("IsOnline")] public bool IsOnline { get; set; } [Subscription("Recipe.DeviceData")] public AITDeviceData Recipe { get; set; } public float[] RecipeData1 => RecipeData; public float[] RecipeData = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 1, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; public string SelectedRecipe { set => _selectedRecipe = value; get => _selectedRecipe; } [Subscription("SelectedRecipeName")] public string CurrentProcessingRecipeName { get => _currentProcessingRecipe; set { if (_currentProcessingRecipe != value) { _currentProcessingRecipe = value; DisplayingRecipeName = value; } } } [Subscription("RecipeStepName")] public string RecipeStepName { get; set; } [Subscription("RecipeStepNumber")] public int RecipeStepNumber { get; set; } [Subscription("ArH2Switch")] public string ArH2Switch { get; set; } [Subscription("N2FlowMode")] public string N2FlowMode { get; set; } [Subscription("RecipeStepElapseTime")] public int RecipeStepElapseTime { get; set; } [Subscription("RecipeStepTime")] public int RecipeStepTime { get; set; } [Subscription("RecipeStepElapseTime2")] public int RecipeStepElapseTime2 { get; set; } [Subscription("RecipeTotalElapseTime")] public int RecipeTotalElapseTime { get; set; } [Subscription("RecipeTotalTime")] public int RecipeTotalTime { get; set; } [Subscription("RecipeTotalElapseTime2")] public int RecipeTotalElapseTime2 { get; set; } public string StepNumber { get { if (IsPMProcess) return $"{RecipeStepNumber}"; else if (IsPreProcess) return "0"; return "--"; } } public string StepName { get { if (IsPMProcess) { return $"{RecipeStepName}"; } else if (IsPreProcess) { return "0"; } return "--"; } } public string StepTime { get { if (IsPMProcess) { return $"{RecipeStepElapseTime}/{RecipeStepTime}"; } else if (IsPreProcess) { return "0"; } return "--"; } } public string RecipeTime { get { if (IsPMProcess) { return $"{RecipeTotalElapseTime}/{RecipeTotalTime}"; } else if (IsPreProcess) { return "0"; } return "--"; } } private string _displayingRecipeName; public string DisplayingRecipeName { get => _displayingRecipeName; private set { if (_displayingRecipeName != value) { _displayingRecipeName = value; _progressLoadRecipe.Report(value); NotifyOfPropertyChange(); } } } private bool _isRecipeLoading; public bool IsRecipeLoading { get => _isRecipeLoading; set { if (_isRecipeLoading != value) { _isRecipeLoading = value; NotifyOfPropertyChange(); } } } public RecipeData CurrentRecipe { get; set; } public List Columns { get; set; } = new List(); private RecipeFormatBuilder _recipeFormatBuilder = new (); public bool IsSelectButtonEnable => !string.IsNullOrEmpty(Status) && !Status.Equals("Process") && !Status.Equals("PreProcess") && !Status.Equals("PostProcess") && !Status.Equals("Paused") && !IsOnline && !IsRecipeLoading; public bool IsStartButtonEnable => !string.IsNullOrEmpty(SelectedRecipe) && !string.IsNullOrEmpty(Status) && !Status.Equals("Process") && !Status.Equals("PreProcess") && !Status.Equals("PostProcess") && !Status.Equals("PrePMMacro") && !Status.Equals("PMMacro") && !Status.Equals("PostPMMacro") && !Status.Equals("Paused") && !IsOnline && !IsRecipeLoading;// !IsProcessRunning; public bool IsStopButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Process") || Status.Equals("PMMacro")) && !IsOnline; public bool IsAbortButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Process") || Status.Equals("PMMacro")) && !IsOnline; //|| Status.Equals("PreProcess") || Status.Equals("PostProcess") || Status.Equals("Paused"));//IsProcessRunning; public bool IsPauseButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Process") || Status.Equals("PMMacro")) && !IsOnline; public bool IsSkipButtonEnable => !string.IsNullOrEmpty(Status) && Status.Equals("Process") && !IsOnline; public bool IsContinueButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Paused") || Status.Equals("PMMacroPause")) && !IsOnline; public WaferInfo MLLWafer { get { if (ModuleManager.ModuleInfos["LLH"].WaferManager.Wafers.Count > 0) return ModuleManager.ModuleInfos["LLH"].WaferManager.Wafers[0]; return null; } } public bool MLLHasWafer { get { return MLLWafer.WaferStatus > 0; } } public WaferInfo PMWafer { get { if (ModuleManager.ModuleInfos[SystemName].WaferManager.Wafers.Count > 0) return ModuleManager.ModuleInfos[SystemName].WaferManager.Wafers[0]; return null; } } public bool PMHasWafer => PMWafer.WaferStatus > 0; public Dictionary DicGas { get; set; } public int MLLCurrentWafer { get; set; } public int MLLTotalWafer { get; set; } // public bool IsEnableGasMap => !IsBusy && IsMaintainMode; //public string SystemName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public object View { get; set; } public string CurrentProcessType { get; set; } #endregion /// /// 获取角色权限配置. /// /// private void InitPermissions() { var roleId = BaseApp.Instance.UserContext.Role.RoleId; _isHideRecipeValue = AccountClient.Instance.Service.GetMenuPermission(roleId, "Recipe.Behaviour.ShowValueInProcessView") == (int)MenuPermissionEnum.MP_NONE; AllowSaveRecipe = AccountClient.Instance.Service.GetMenuPermission(roleId, "Recipe.Behaviour.AllowSaveInProcessView") != (int)MenuPermissionEnum.MP_NONE; AllowRipRecipe = AccountClient.Instance.Service.GetMenuPermission(roleId, "Recipe.Behaviour.AllowRipInProcessView") != (int)MenuPermissionEnum.MP_NONE; } protected override void OnActivate() { try { InitPermissions(); if (string.IsNullOrEmpty(DisplayingRecipeName)) { _needLoadRecipe = false; var recipeName = (string)QueryDataClient.Instance.Service.GetConfig($"PM.{SystemName}.LastRecipeName"); DisplayingRecipeName = recipeName; _selectedRecipe = recipeName; } UpdateRecipeFormat(); // 更新Recipe参数值显示状态 SetRecipeValueVisibilityByCurrentRole(); ChType= (string)QueryDataClient.Instance.Service.GetConfig($"PM.{SystemName}.ChType"); base.OnActivate(); } catch (Exception ex) { } } protected override void OnDeactivate(bool close) { ActiveUpdateData = true; base.OnDeactivate(close); } protected override void Poll() { try { base.Poll(); } catch (Exception ex) { } } protected override void InvokeAfterUpdateProperty(Dictionary data) { try { if (_needLoadRecipe) _needLoadRecipe = false; base.InvokeAfterUpdateProperty(data); } catch (Exception ex) { } } public void ParamsExpanded(ExpanderColumn col) { int index = this.Columns.IndexOf(col); for (var i = index + 1; i < this.Columns.Count; i++) { if (this.Columns[i] is ExpanderColumn) break; this.Columns[i].Visibility = Visibility.Visible; } } public void ParamsCollapsed(ExpanderColumn col) { int index = this.Columns.IndexOf(col); for (var i = index + 1; i < this.Columns.Count; i++) { if (this.Columns[i] is ExpanderColumn) break; this.Columns[i].Visibility = Visibility.Collapsed; } } protected override void OnViewLoaded(object view) { View = view; base.OnViewLoaded(view); ViewWin = (PMProcessView)view; StartDateTime = DateTime.Now.Date; EndDateTime = DateTime.Now.Date.AddDays(1).AddTicks(-1); /*// 如果在ReadOnly模式,则禁止解锁Recipe。 (((PMProcessView)View)!).recipeLocker.IsEnabled = BaseApp.Instance.UserMode != UserMode.ReadOnly;*/ } public void SelectRecipe() { var dialog = new RecipeSelectDialogViewModel(false, false, RecipeType.Process); dialog.DisplayName = "Select Recipe"; var wm = new WindowManager(); var bret = wm.ShowDialog(dialog); if ((bool)bret) { var recipeName = dialog.DialogResult; if (recipeName.StartsWith("Sic\\Process")) { CurrentProcessType = "Process"; } else if (recipeName.StartsWith("Sic\\Routine")) { CurrentProcessType = "Routine"; } DisplayingRecipeName = recipeName; SelectedRecipe = recipeName; InvokeClient.Instance.Service.DoOperation($"{SystemName}.SelectRecipe", recipeName); InvokeClient.Instance.Service.DoOperation("System.SetConfig", $"PM.{SystemName}.LastRecipeName", recipeName); } } public void DeleteProcessData() { } private static void RenderStepsWithSameValue(IReadOnlyList previousStep, IReadOnlyList currentStep) { for (var i = 0; i < currentStep.Count; i++) { if (currentStep[i] is DoubleParam currDblParam && previousStep[i] is DoubleParam prevDblParam) { currDblParam.IsEqualsToPrevious = GlobalDefs.IsDoubleValueEquals(currDblParam.Value, prevDblParam.Value); } else if (currentStep[i] is ComboxParam currCbxParam && previousStep[i] is ComboxParam prevCbxParam) { if (currCbxParam.Value != prevCbxParam.Value) { currCbxParam.IsEqualsToPrevious = false; } else { currCbxParam.IsEqualsToPrevious = true; } } } } /// /// 将发生变更的配方推送到当前工艺。 /// public void PushRecipeToCurrentProcess() { // 如果配方没变,不需要推送 if (!CurrentRecipe.IsChanged) { DialogBox.ShowWarning("The recipe does not change."); return; } if (!CurrentRecipe.Validate()) { var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING, $"{CurrentRecipe.ValidationErrorSummary}\r\n Are you sure to push changes to current process?"); if (ret == DialogButton.No) return; /*var mbr = MessageBox.Show($"{errors}\r\n Are you sure to push changes to current process", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning); if (mbr == MessageBoxResult.No) return;*/ } if (!CurrentRecipe.GetXmlString(out var xml, out var reason)) { DialogBox.ShowError($"Unable to generate recipe content, {reason}"); return; } InvokeClient.Instance.Service.DoOperation($"{SystemName}.ReloadRecipe", xml); } public void SaveToBaselineRecipe() { if (string.IsNullOrEmpty(CurrentRecipe.Name)) { DialogBox.ShowError("Recipe name can't be empty"); return; } // 如果配方没变,不需要推送 if (!CurrentRecipe.IsChanged) { DialogBox.ShowInfo("The recipe does not change."); return; } if (!CurrentRecipe.Validate()) { var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING, $"{CurrentRecipe.ValidationErrorSummary}\r\n Are you sure to save to baseline recipe?"); if (ret == DialogButton.No) return; } CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; CurrentRecipe.ReviseTime = DateTime.Now; if (!CurrentRecipe.GetXmlString(out var xml, out var reason)) { DialogBox.ShowError($"Unable to generate recipe content, {reason}"); return; } var recipeProvider = new RecipeProvider(); var result = recipeProvider.WriteRecipeFile(CurrentRecipe.PrefixPath, CurrentRecipe.Name, xml); if (result) CurrentRecipe.MarkAsSaved(); else DialogBox.ShowError("Unable to save recipe."); } /// /// 重新加载配方,当前修改的参数被抛弃。 /// public void RestoreToBaselineRecipe() { // 如果改变,提示 if (CurrentRecipe.IsChanged) { if (DialogBox.Confirm( "The recipe has changed, are you sure to discard changes and reload baseline recipe?") == false) return; } if (CurrentRecipe != null && !string.IsNullOrEmpty(CurrentRecipe.FullName)) _progressLoadRecipe.Report(CurrentRecipe.FullName); } /// /// 根据配置获取Cascade方式呈现Recipe的Dispatcher,或返回null。 /// /// private static Dispatcher GetLoadingDispatcher() { // 判断Step呈现方式 var isCascadeLoading = (bool)QueryDataClient.Instance.Service.GetConfig("System.RecipeCascadeLoading"); var dispatcher = isCascadeLoading ? Application.Current?.Dispatcher : null; if (dispatcher == null || !dispatcher.CheckAccess()) return null; return dispatcher; } #region Recipe相关控制函数 public void UpdateRecipeFormat() { var role = AccountClient.Instance.Service.GetRoleByID(BaseApp.Instance.UserContext.Role.RoleId); if (CurrentRecipe == null) { CurrentRecipe = new RecipeData(_recipeGasFlowCalculator, RecipeType.Process); CurrentRecipe.BuildFormat($"Sic\\Process", SystemName, role); } else { CurrentRecipe.UpdatePermission(role); } } /// /// 根据权限设置Recipe参数的可见性。 /// private void SetRecipeValueVisibilityByCurrentRole() { // 是否在编辑器中隐藏Recipe参数值 if(_isHideRecipeValue) // 当权限为None时隐藏,其它时显示。 CurrentRecipe?.Steps.HideValue(); else CurrentRecipe?.Steps.ShowValue(); // 白名单的Param显示出来 ApplyRecipeCellAccessPermissionWhitelist(); } /// /// 从数据库读取单元格访问白名单。 /// /// private DataTable ReadRecipeCellAccessPermissionWhitelist() { if (CurrentRecipe == null) return null; var cmd = $"select * from recipe_cell_access_permission_whitelist where \"recipeName\" = '{CurrentRecipe.FullName}'"; var dt = QueryDataClient.Instance.Service.QueryData(cmd); return dt; } /// /// 如果Recipe参数设置为隐藏,将白名单中的单元格显示出来。 /// private void ApplyRecipeCellAccessPermissionWhitelist() { var dt = ReadRecipeCellAccessPermissionWhitelist(); if (dt is { Rows.Count: > 0 }) { foreach (DataRow dataRow in dt.Rows) { var stepUid = dataRow["stepUid"].ToString(); var controlName = dataRow["columnName"].ToString(); var step = CurrentRecipe.Steps.FirstOrDefault(s => s.StepUid == stepUid); var param = step?.FirstOrDefault(p => p.Name == controlName); if (param != null) param.IsHideValue = false; } } } /// /// 正在工艺中的Recipe Step发生改变。 /// /// private void RecipeStepInProcessChanged(int currentStepNum) { UpdateRecipeStepProcessedStatus(currentStepNum); } /// /// 更新Recipe步骤。 /// /// private void UpdateRecipeStepProcessedStatus(int currentStepNum) { if (CurrentRecipe == null) return; if (CurrentRecipe.Steps.Count <= 0) return; // Recipe不是当前正在运行的Recipe。 if (CurrentRecipe.FullName != DisplayingRecipeName) return; foreach (var step in CurrentRecipe.Steps) { if (step.StepNo.HasValue) { if (step.StepNo < currentStepNum) step.IsProcessed = true; else if (step.StepNo == currentStepNum) { if (IsPMProcess) step.IsProcessed = false; else step.IsProcessed = true; } else { step.IsProcessed = false; } } } // 自动滚到当前做工艺的Step var dg = ((PMProcessView)View)?.dgRecipeEditor; if (currentStepNum > 0) { var stepScrollTo = currentStepNum - 1; if (stepScrollTo < 0) stepScrollTo = 0; dg?.ScrollToRecipeStep(CurrentRecipe.Steps[stepScrollTo]); } } /// /// 在UI线程加载Recipe /// /// private void ProgLoadRecipeOnUiThread(string recipeName) { lock (_lockerLoadingRecipe) { if (IsRecipeLoading) return; IsRecipeLoading = true; NotifyOfPropertyChange(nameof(IsSelectButtonEnable)); } if (recipeName.StartsWith("Sic\\Process")) { CurrentProcessType = "Process"; } else if (recipeName.StartsWith("Sic\\Routine")) { CurrentProcessType = "Routine"; } //UpdateRecipeFormat(); LoadRecipe(recipeName).ContinueWith(t => { if (!t.IsCanceled && !t.IsFaulted && t.IsCompleted) { Application.Current?.Dispatcher.Invoke(() => { UpdateRecipeStepProcessedStatus(RecipeStepNumber); }); } }).ContinueWith(t => { Application.Current?.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { lock (_lockerLoadingRecipe) { IsRecipeLoading = false; } })); }); } #endregion private async Task LoadRecipe(string selectedRecipePath) { CurrentRecipe.Clear(); var array = selectedRecipePath.Split(new char[] { '\\' }); var recipeName = array[array.Length - 1]; var prefixPath = selectedRecipePath.Replace(recipeName, "").TrimEnd('\\'); var recipeProvider = new RecipeProvider(); var recipeContent = recipeProvider.ReadRecipeFile(prefixPath, recipeName); if (string.IsNullOrEmpty(recipeContent)) { MessageBox.Show($"{prefixPath}\\{recipeName} is empty, please confirm the file is valid."); return; } CurrentRecipe.RecipeChamberType = _recipeFormatBuilder.RecipeChamberType; CurrentRecipe.RecipeVersion = _recipeFormatBuilder.RecipeVersion; var role = AccountClient.Instance.Service.GetRoleByID(BaseApp.Instance.UserContext.Role.RoleId); await CurrentRecipe.LoadFile(prefixPath, recipeName, recipeContent, SystemName, role, GetLoadingDispatcher()); // 显示Cell访问白名单中的参数 SetRecipeValueVisibilityByCurrentRole(); } public void StartProcess() { var pollBypassInterlock= $"{SystemName}.IsBypassInterlock"; var pollBypassEnableTable = $"{SystemName}.IsBypassEnableTable"; var pollIsService = $"{SystemName}.IsService"; var data = QueryDataClient.Instance.Service.PollData(new[] { pollBypassInterlock, pollBypassEnableTable, pollIsService }); Debug.Assert(data != null, "Unable to poll data from RT."); Debug.Assert(data.Count == 3, "The count of polled data is incorrect."); if ((bool)data[pollBypassInterlock] || (bool)data[pollBypassEnableTable]) { DialogBox.ShowError("Interlock or EnableTable is bypassed, can not start process."); return; } if ((bool)data[pollIsService]) { DialogBox.ShowError($"{SystemName} is in Service Mode, can not start process."); return; } var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING, "Please Check All Gas Ready Before Start Process!\r\n\r\nAre you sure to start process?"); if (ret == DialogButton.No) return; var array = DisplayingRecipeName.Split(new char[] { '\\' }); if (array[1] == "Process") { InvokeClient.Instance.Service.DoOperation($"{SystemName}.RunRecipe", DisplayingRecipeName, false, true); } else if (array[1] == "Routine") { InvokeClient.Instance.Service.DoOperation($"{SystemName}.RunPMMacro", DisplayingRecipeName); } } public void Skip() { InvokeClient.Instance.Service.DoOperation($"{SystemName}.RecipeSkipStep"); } public void Stop() { InvokeClient.Instance.Service.DoOperation($"{SystemName}.Abort"); } public void Pause() { InvokeClient.Instance.Service.DoOperation($"{SystemName}.Pause"); } public void Continue() { InvokeClient.Instance.Service.DoOperation($"{SystemName}.Continue", "Step continue"); } public void Abort() { InvokeClient.Instance.Service.DoOperation($"{SystemName}.Abort"); } #region 腔体绑定数据 [Subscription("TC1.L1InputTempSetPoint")] public float L1InputTemp { get; set; } [Subscription("TC1.L2InputTempSetPoint")] public float L2InputTemp { get; set; } [Subscription("TC1.L3InputTempSetPoint")] public float L3InputTemp { get; set; } [Subscription("TC2.L3InputTempSetPoint")] public float SCRL3InputTemp { get; set; } [Subscription("TC1.L1TargetSPSetPoint")] public float L1TargetSP { get; set; } [Subscription("TC1.L2TargetSPSetPoint")] public float L2TargetSP { get; set; } [Subscription("TC1.L3TargetSPSetPoint")] public float L3TargetSP { get; set; } [Subscription("TC2.L3TargetSPSetPoint")] public float SCRL3TargetSP { get; set; } [Subscription("TC1.TempCtrlTCIN")] public float PM1Temprature { get; set; } //底部温度 [Subscription("TC1.TempCtrlTCIN")] public float TC1Temp2 { get; set; } [Subscription("TC1.HeaterModeSetPoint")] public float TC1HeaterMode { get; set; } public string TC1Mode { get { int mode = (int)TC1HeaterMode; return Enum.GetName(typeof(DicMode.HeaterControlMode), mode); } } [Subscription("TC2.HeaterModeSetPoint")] public float TC2HeaterMode { get; set; } public string TC2Mode { get { switch (TC2HeaterMode) { case 0: return "Power"; case 1: return "TC"; case 2: return "Pyro"; } return "Power"; } } [Subscription("SCR1.PowerFeedBack")] public float SCR1Power { get; set; } [Subscription("SCR2.PowerFeedBack")] public float SCR2Power { get; set; } [Subscription("SCR3.PowerFeedBack")] public float SCR3Power { get; set; } [Subscription("PSU1.OutputPowerFeedBack")] public float PSU1Power { get; set; } [Subscription("PSU2.OutputPowerFeedBack")] public float PSU2Power { get; set; } [Subscription("PSU3.OutputPowerFeedBack")] public float PSU3Power { get; set; } [Subscription("PMServo.ActualSpeedFeedback")] public float ActualSpeedFeedback { get; set; } [Subscription("PT1.FeedBack")] public float PT1Pressure { get; set; } #endregion 腔体绑定数据 #region MFC [Subscription("Mfc1.DeviceData")] public AITMfcData Mfc1Data { get; set; } [Subscription("Mfc2.DeviceData")] public AITMfcData Mfc2Data { get; set; } [Subscription("Mfc3.DeviceData")] public AITMfcData Mfc3Data { get; set; } [Subscription("Mfc4.DeviceData")] public AITMfcData Mfc4Data { get; set; } [Subscription("Mfc5.DeviceData")] public AITMfcData Mfc5Data { get; set; } [Subscription("Mfc6.DeviceData")] public AITMfcData Mfc6Data { get; set; } [Subscription("Mfc7.DeviceData")] public AITMfcData Mfc7Data { get; set; } [Subscription("Mfc8.DeviceData")] public AITMfcData Mfc8Data { get; set; } [Subscription("Mfc9.DeviceData")] public AITMfcData Mfc9Data { get; set; } [Subscription("Mfc10.DeviceData")] public AITMfcData Mfc10Data { get; set; } [Subscription("Mfc11.DeviceData")] public AITMfcData Mfc11Data { get; set; } [Subscription("Mfc12.DeviceData")] public AITMfcData Mfc12Data { get; set; } [Subscription("Mfc13.DeviceData")] public AITMfcData Mfc13Data { get; set; } [Subscription("Mfc14.DeviceData")] public AITMfcData Mfc14Data { get; set; } [Subscription("Mfc15.DeviceData")] public AITMfcData Mfc15Data { get; set; } [Subscription("Mfc16.DeviceData")] public AITMfcData Mfc16Data { get; set; } [Subscription("Mfc19.DeviceData")] public AITMfcData Mfc19Data { get; set; } [Subscription("Mfc20.DeviceData")] public AITMfcData Mfc20Data { get; set; } [Subscription("Mfc22.DeviceData")] public AITMfcData Mfc22Data { get; set; } [Subscription("Mfc23.DeviceData")] public AITMfcData Mfc23Data { get; set; } [Subscription("Mfc25.DeviceData")] public AITMfcData Mfc25Data { get; set; } [Subscription("Mfc26.DeviceData")] public AITMfcData Mfc26Data { get; set; } [Subscription("Mfc27.DeviceData")] public AITMfcData Mfc27Data { get; set; } [Subscription("Mfc28.DeviceData")] public AITMfcData Mfc28Data { get; set; } [Subscription("Mfc29.DeviceData")] public AITMfcData Mfc29Data { get; set; } [Subscription("Mfc30.DeviceData")] public AITMfcData Mfc30Data { get; set; } [Subscription("Mfc31.DeviceData")] public AITMfcData Mfc31Data { get; set; } [Subscription("Mfc32.DeviceData")] public AITMfcData Mfc32Data { get; set; } [Subscription("Mfc33.DeviceData")] public AITMfcData Mfc33Data { get; set; } [Subscription("Mfc35.DeviceData")] public AITMfcData Mfc35Data { get; set; } [Subscription("Mfc36.DeviceData")] public AITMfcData Mfc36Data { get; set; } [Subscription("Mfc37.DeviceData")] public AITMfcData Mfc37Data { get; set; } [Subscription("Mfc38.DeviceData")] public AITMfcData Mfc38Data { get; set; } [Subscription("Mfc40.DeviceData")] public AITMfcData Mfc40Data { get; set; } #endregion #region Valve [Subscription("V27.DeviceData")] public AITValveData V27 { get; set; } [Subscription("V25.DeviceData")] public AITValveData V25 { get; set; } [Subscription("V31.DeviceData")] public AITValveData V31 { get; set; } [Subscription("V32.DeviceData")] public AITValveData V32 { get; set; } [Subscription("V33.DeviceData")] public AITValveData V33 { get; set; } [Subscription("V33s.DeviceData")] public AITValveData V33s { get; set; } [Subscription("V35.DeviceData")] public AITValveData V35 { get; set; } [Subscription("V36.DeviceData")] public AITValveData V36 { get; set; } [Subscription("V37.DeviceData")] public AITValveData V37 { get; set; } [Subscription("V37s.DeviceData")] public AITValveData V37s { get; set; } [Subscription("V39.DeviceData")] public AITValveData V39 { get; set; } [Subscription("V39s.DeviceData")] public AITValveData V39s { get; set; } [Subscription("V40.DeviceData")] public AITValveData V40 { get; set; } [Subscription("V40s.DeviceData")] public AITValveData V40s { get; set; } [Subscription("V41.DeviceData")] public AITValveData V41 { get; set; } [Subscription("V41s.DeviceData")] public AITValveData V41s { get; set; } [Subscription("V42.DeviceData")] public AITValveData V42 { get; set; } [Subscription("V43.DeviceData")] public AITValveData V43 { get; set; } [Subscription("V43s.DeviceData")] public AITValveData V43s { get; set; } [Subscription("V45.DeviceData")] public AITValveData V45 { get; set; } [Subscription("V46.DeviceData")] public AITValveData V46 { get; set; } [Subscription("V46s.DeviceData")] public AITValveData V46s { get; set; } [Subscription("V48.DeviceData")] public AITValveData V48 { get; set; } [Subscription("V48s.DeviceData")] public AITValveData V48s { get; set; } [Subscription("V49.DeviceData")] public AITValveData V49 { get; set; } [Subscription("V50.DeviceData")] public AITValveData V50 { get; set; } [Subscription("V50s.DeviceData")] public AITValveData V50s { get; set; } [Subscription("V51.DeviceData")] public AITValveData V51 { get; set; } [Subscription("V51s.DeviceData")] public AITValveData V51s { get; set; } [Subscription("V52.DeviceData")] public AITValveData V52 { get; set; } [Subscription("V52s.DeviceData")] public AITValveData V52s { get; set; } [Subscription("V53.DeviceData")] public AITValveData V53 { get; set; } [Subscription("V53s.DeviceData")] public AITValveData V53s { get; set; } [Subscription("V54.DeviceData")] public AITValveData V54 { get; set; } [Subscription("V54s.DeviceData")] public AITValveData V54s { get; set; } [Subscription("V55.DeviceData")] public AITValveData V55 { get; set; } [Subscription("V56.DeviceData")] public AITValveData V56 { get; set; } [Subscription("V58.DeviceData")] public AITValveData V58 { get; set; } [Subscription("V58s.DeviceData")] public AITValveData V58s { get; set; } [Subscription("V59.DeviceData")] public AITValveData V59 { get; set; } [Subscription("V60.DeviceData")] public AITValveData V60 { get; set; } [Subscription("V61.DeviceData")] public AITValveData V61 { get; set; } [Subscription("V62.DeviceData")] public AITValveData V62 { get; set; } [Subscription("V63.DeviceData")] public AITValveData V63 { get; set; } //[Subscription("V64.DeviceData")] //public AITValveData V64 { get; set; } //[Subscription("V65.DeviceData")] //public AITValveData V65 { get; set; } [Subscription("V68.DeviceData")] public AITValveData V68 { get; set; } [Subscription("V69.DeviceData")] public AITValveData V69 { get; set; } [Subscription("V70.DeviceData")] public AITValveData V70 { get; set; } [Subscription("V72.DeviceData")] public AITValveData V72 { get; set; } [Subscription("V73.DeviceData")] public AITValveData V73 { get; set; } [Subscription("V74.DeviceData")] public AITValveData V74 { get; set; } [Subscription("V75.DeviceData")] public AITValveData V75 { get; set; } [Subscription("V76.DeviceData")] public AITValveData V76 { get; set; } [Subscription("V87.DeviceData")] public AITValveData V87 { get; set; } [Subscription("V88.DeviceData")] public AITValveData V88 { get; set; } [Subscription("V89.DeviceData")] public AITValveData V89 { get; set; } [Subscription("V90.DeviceData")] public AITValveData V90 { get; set; } [Subscription("V91.DeviceData")] public AITValveData V91 { get; set; } [Subscription("V92.DeviceData")] public AITValveData V92 { get; set; } [Subscription("V93.DeviceData")] public AITValveData V93 { get; set; } [Subscription("V94.DeviceData")] public AITValveData V94 { get; set; } [Subscription("V95.DeviceData")] public AITValveData V95 { get; set; } [Subscription("V96.DeviceData")] public AITValveData V96 { get; set; } [Subscription("V97.DeviceData")] public AITValveData V97 { get; set; } [Subscription("EPV1.DeviceData")] public AITValveData EPV1 { get; set; } [Subscription("EPV2.DeviceData")] public AITValveData EPV2 { get; set; } [Subscription("V99.DeviceData")] public AITValveData V99 { get; set; } [Subscription("V99s.DeviceData")] public AITValveData V99s { get; set; } #endregion #region Pressure [Subscription("Pressure1.DeviceData")] public AITPressureMeterData PT1Data { get; set; } [Subscription("Pressure2.DeviceData")] public AITPressureMeterData PT2Data { get; set; } [Subscription("Pressure3.DeviceData")] public AITPressureMeterData PT3Data { get; set; } [Subscription("Pressure4.DeviceData")] public AITPressureMeterData PT4Data { get; set; } [Subscription("Pressure5.DeviceData")] public AITPressureMeterData PT5Data { get; set; } [Subscription("Pressure6.DeviceData")] public AITPressureMeterData PT6Data { get; set; } [Subscription("Pressure7.DeviceData")] public AITPressureMeterData PT7Data { get; set; } [Subscription("PT1.DeviceData")] public AITPressureMeterData ChamPress { get; set; } [Subscription("PT2.DeviceData")] public AITPressureMeterData ForelinePress { get; set; } #endregion #region 特气显示颜色 public SolidColorBrush PN2Color { get { if (IsPN2RunMode) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } else { return new SolidColorBrush(Color.FromRgb(255, 0, 0)); } } } public SolidColorBrush C2H4Color { get { if (IsC2H4RunMode) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } else { return new SolidColorBrush(Color.FromRgb(255, 0, 0)); } } } public SolidColorBrush SIH4Color { get { if (IsSiH4RunMode) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } else { return new SolidColorBrush(Color.FromRgb(255, 0, 0)); } } } public SolidColorBrush HCLColor { get { if (IsHCLRunMode) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } else { return new SolidColorBrush(Color.FromRgb(255, 0, 0)); } } } public SolidColorBrush TMAColor { get { if (IsTMARunMode) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } else { return new SolidColorBrush(Color.FromRgb(255, 0, 0)); } } } public SolidColorBrush TCSColor { get { if (IsTCSRunMode) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } else { return new SolidColorBrush(Color.FromRgb(255, 0, 0)); } } } public SolidColorBrush PN2FlowColor { get { if (IsPN2RunMode) { return new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)); } else { return new SolidColorBrush(Color.FromArgb(255, 190, 190, 190)); } } } public SolidColorBrush C2H4FlowColor { get { if (IsC2H4RunMode) { return new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)); } else { return new SolidColorBrush(Color.FromArgb(255, 190, 190, 190)); } } } public SolidColorBrush SIH4FlowColor { get { if (IsSiH4RunMode) { return new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)); } else { return new SolidColorBrush(Color.FromArgb(255, 190, 190, 190)); } } } public SolidColorBrush HCLFlowColor { get { if (IsHCLRunMode) { return new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)); } else { return new SolidColorBrush(Color.FromArgb(255, 190, 190, 190)); } } } public SolidColorBrush TMAFlowColor { get { if (IsTMARunMode) { return new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)); } else { return new SolidColorBrush(Color.FromArgb(255, 190, 190, 190)); } } } public SolidColorBrush TCSFlowColor { get { if (IsTCSRunMode) { return new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)); } else { return new SolidColorBrush(Color.FromArgb(255, 190, 190, 190)); } } } #endregion public bool IsPN2RunMode => V37s.IsOpen && (V39.IsOpen || V40.IsOpen);//N2FlowMode == "Run";按照洪膺要求修改 2023年6月9日10:34:54 public bool IsHCLRunMode => V54.IsOpen; public bool IsSiH4RunMode => V55.IsOpen && !V56.IsOpen; public bool IsC2H4RunMode => V59.IsOpen && !V60.IsOpen; public bool IsTCSRunMode => V53.IsOpen; public bool IsTMARunMode => V41.IsOpen && !V42.IsOpen; [Subscription("GasRealTimeFlow.Ar_Run.FeedBack")] public double ArFlow_Run { get; set; } [Subscription("GasRealTimeFlow.Ar_Vent.FeedBack")] public double ArFlow_Vent { get; set; } public double ArFlow => ArFlow_Run > 0 ? ArFlow_Run : ArFlow_Vent; public double ArMol => GetMol(ArFlow_Run); [Subscription("GasRealTimeFlow.H2_Run.FeedBack")] public double H2_Run { get; set; } [Subscription("GasRealTimeFlow.H2_Vent.FeedBack")] public double H2_Vent { get; set; } public double H2Flow => H2_Run > 0 ? H2_Run : H2_Vent; public double H2Mol => GetMol(H2_Run); [Subscription("GasRealTimeFlow.PN2_Run.FeedBack")] public double PN2_Run { get; set; } [Subscription("GasRealTimeFlow.PN2_Vent.FeedBackSpecial")] public double PN2_Vent { get; set; } public double PN2Flow => PN2_Run + PN2_Vent;//=> GetPN2MFCFlow(IsPN2RunMode) * _pn2FlowRatio; public double PN2Mol => GetMol(PN2Flow); [Subscription("GasRealTimeFlow.HCL_Run.FeedBack")] public double HCL_Run { get; set; } [Subscription("GasRealTimeFlow.HCL_Vent.FeedBack")] public double HCL_Vent { get; set; } public double HCLFlow => HCL_Run + HCL_Vent; public double HCLMol => GetMol(HCLFlow); [Subscription("GasRealTimeFlow.SiH4_Run.FeedBack")] public double SiH4_Run { get; set; } [Subscription("GasRealTimeFlow.SiH4_Vent.FeedBack")] public double SiH4_Vent { get; set; } public double SiH4Flow => SiH4_Run + SiH4_Vent; public double SIH4Mol => GetMol(SiH4Flow); [Subscription("GasRealTimeFlow.C2H4_Run.FeedBack")] public double C2H4_Run { get; set; } [Subscription("GasRealTimeFlow.C2H4_Vent.FeedBack")] public double C2H4_Vent { get; set; } public double C2H4Flow => C2H4_Run + C2H4_Vent; public double C2H4Mol => GetMol(C2H4Flow); [Subscription("GasRealTimeFlow.TCS_Run.FeedBack")] public double TCS_Run { get; set; } [Subscription("GasRealTimeFlow.TCS_Vent.FeedBack")] public double TCS_Vent { get; set; } public double TCSFlow => TCS_Run + TCS_Vent; public double TCSMol => GetMol(TCSFlow); [Subscription("GasRealTimeFlow.TMA_Run.FeedBack")] public double TMA_Run { get; set; } [Subscription("GasRealTimeFlow.TMA_Vent.FeedBack")] public double TMA_Vent { get; set; } public double TMAFlow => TMA_Run + TMA_Vent; public double TMAMol => GetMol(TMAFlow); public bool ISPN2Flow => PN2Flow > 0 && IsPN2RunMode; public bool ISHCLFlow => HCLFlow > 0 && IsHCLRunMode; public bool ISSiH4Flow => SiH4Flow > 0 && IsSiH4RunMode; public bool ISC2H4Flow => C2H4Flow > 0 && IsC2H4RunMode; public bool ISArFlow => ArFlow_Run > 0; public bool ISH2Flow => H2_Run > 0; public bool ISTCSFlow => TCSFlow > 0 && IsTCSRunMode; public bool ISTMAFlow => TMAFlow > 0 && IsTMARunMode; private double GetMol(double fedback) { return fedback / 1000 / 22.4141; } #region Process Data Statistic [Subscription("ProcessDataStat.RealtimeList")] public List ProcessRunDataList { get; set; } #endregion #region 真值表方式统计流量 /// /// 返回ViewModel绑定的视图对象。 /// public PMProcessView ViewWin { get; private set; } public DateTime StartDateTime { get => ViewWin.wfTimeFrom.Value; set { ViewWin.wfTimeFrom.Value = value; } } public DateTime EndDateTime { get => ViewWin.wfTimeTo.Value; set { ViewWin.wfTimeTo.Value = value; } } private List _RTgasFlowSumList; /// /// 从真值表中初始化集合对象 /// [Subscription("PMGasFlowSumList")] public List _RTGasFlowSumList { get => _RTgasFlowSumList; set { _RTgasFlowSumList ??= value; } } private List _gasFlowSumList; public List GasFlowSumList { get { _gasFlowSumList ??= _RTGasFlowSumList;//使用真值表初始化集合 return _gasFlowSumList; } set { _gasFlowSumList = value; NotifyOfPropertyChange(nameof(GasFlowSumList)); } } private bool _isBusyGasFlowSum; public bool IsBusyGasFlowSum { get => _isBusyGasFlowSum; set { _isBusyGasFlowSum = value; NotifyOfPropertyChange(nameof(IsBusyGasFlowSum)); } } private string _busyIndicatorMessage; public string BusyIndicatorContent { get => _busyIndicatorMessage; set { _busyIndicatorMessage = value; NotifyOfPropertyChange(nameof(BusyIndicatorContent)); } } private readonly IProgress _busyIndicatorContentExport; public void Query() { if (StartDateTime > EndDateTime) { MessageBox.Show("time range invalid, start time should be early than end time.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); return; } _cts = new CancellationTokenSource(); Task.Run(() => { _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(20, 100, "Exporting Start ...")); Query(SystemName, StartDateTime, EndDateTime); }); } public void CancelQuery() { Task.Run(() => { if (_cts?.Token.CanBeCanceled == true) { _cts.Cancel(); } Thread.Sleep(200); _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(100, 100, "")); }); } public void Query(string module, DateTime start, DateTime end) { var daySlices = DateRangeHelper.SplitInToHours(new DateRangeHelper(start, end), 12); List> gasFlowRunVent = new List>(); //按天查询数据库,单天返回数据类型为List foreach (var daySlice in daySlices) { if (_cts.IsCancellationRequested) return ; gasFlowRunVent.Add( GasFlowSqlHelp.GetDayGasFlowSum(daySlice, SystemName, GasFlowSumList)); _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(200, 200, $"{daySlice}")); } List dayGasFlowList = _RTGasFlowSumList; //解析数据库返回的数据 foreach (var items in gasFlowRunVent) { foreach (var item in items) { var gasFlowSum = dayGasFlowList.Where(obj => item.Name.Contains(obj.Name)).First(); if (item.Name.Contains("Run")) gasFlowSum.RunVolume += item.Volume; else gasFlowSum.VentVolume += item.Volume; } } //使用公式转换单位 double baseValue= 0.0166667 * Math.Pow(10, -6); foreach (var item in dayGasFlowList) { item.RunVolume = item.RunVolume * baseValue; item.VentVolume = item.VentVolume * baseValue; item.Volume = item.RunVolume + item.VentVolume; } GasFlowSumList = dayGasFlowList; _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(100, 100, "")); } #endregion /// /// 打开 ProcessMonitor /// public void ShowMonitorWindow() { // 给 OverviewViewModel 发消息,打开Monitor窗口。 if (EventAggregator?.HandlerExistsFor(typeof(ShowCloseMonitorWinEvent)) == true) EventAggregator?.PublishOnUIThread(new ShowCloseMonitorWinEvent(true)); else MessageBox.Show("The process has not been activated, please open Operation->Overview tab to activate the process.", "Warn", MessageBoxButton.OK, MessageBoxImage.Warning); } public void Handle(CommonAggregateEvents message) { switch (message) { case CommonAggregateEvents.EnterReadOnlyMode: if(View is PMProcessView v) v.recipeLocker.Lock(); break; default: break; } } } }