using Aitex.Core.RT.Log; using Aitex.Core.RT.SCCore; using Caliburn.Micro; using Caliburn.Micro.Core; using MECF.Framework.Common.CommonData; using MECF.Framework.Common.DataCenter; using MECF.Framework.UI.Client.CenterViews.Editors; using MECF.Framework.UI.Client.CenterViews.Editors.Recipe; using MECF.Framework.UI.Client.CenterViews.Editors.Sequence; using MECF.Framework.UI.Client.ClientBase; using OpenSEMI.ClientBase; using OpenSEMI.ClientBase.Command; using RecipeEditorLib.DGExtension.CustomColumn; using RecipeEditorLib.RecipeModel.Params; using SicUI.Client; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using Aitex.Core.Util; using MECF.Framework.Common.OperationCenter; using MECF.Framework.UI.Client.CenterViews.Configs.Roles; namespace SicUI.Models.RecipeEditors { //Recipe列名编号(名称带_的为组名) enum RecipColNo { Step_Information = 0, Step, Name, StepTime, SHFlow_SHTotalFlow, ArH2Switch, SHTotalFlow, CarrayGasFlow, SHTotalFlowSplitRatio, FlowSetMode, Pressure_Rotation, PressureSet, RotationSet, Temperature_ControlMode, PSUControlMode, PSUSetMode, SCRControlMode, SCRSetMode, Temperature_TempSetting, PSUInnerTempSet, PSUMiddleTempSet, PSUOuterTempSet, SCRTempSet, Temperature_HeaterOutput, PSUInnerRatioSet, PSUMiddleRatioSet, PSUOuterRatioSet, SCRUpperRatioSet, SCRMiddleRatioSet, SCRLowerRatioSet, SHFlow_SiSourceSi_SourceTotalFlow, SiSourceTotalFlow, SHFlow_SiSource_SiH4Flow, SiH4FlowMode, SiH4Flow, SHFlow_SiSource_TCSFlow, TCSFlowMode, TCSBubbLowFlow, TCSBubbHighFlow, TCSPushFlow, TCSBubbPressure, SHFlow_SiSource_HCLFlow, HCLFlowMode, HCLFlow, SHFlow_SiSource_SiSourSplit, SiSourceSplitRatio, SiSourcePushPressure, SiSourceInnerFlow, SiSourceMiddleFlow, SiSourceOuterFlow, SHFlow_CSource_CSourceTotalFlow, CSourceTotalFlow, SHFlow_CSource_C2H4Flow, C2H4FlowMode, C2H4Flow, SHFlow_CSource_CSourceSplit, CSourceSplitRatio, CSourcePushPressure, CSourceInnerFlow, CSourceMiddleFlow, CSourceOuterFlow, SHFlow_Dope_DopeTotalFlow, DopeTotalFlow, SHFlow_Dope_N2Flow, N2FlowMode, N2ActualFlow, N2LowFlow, DilutFlowForN2, DilutedN2Flow, N2PostDilutPressure, N2HighFlowMode, N2HighFlow, SHFlow_Dope_TMAFlow, TMAFlowMode, TMABubbFlow, TMAPushFlow, TMABubbPressure, SHFlow_Dope_DopeSplit, DopeSplitRatio, DopePushPressure, DopeInnerFlow, DopeMiddleFlow, DopeOuterFlow, SHFlow_SHPushFlow, SHPushTotalFlow, SHInnerFlow, SHMiddleFlow, SHOuterFlow, InnerPushFlow, MiddlePushFlow, OuterPushFlow, Purge_SHPeripheryPurge, SHPurgeFlow, GRPurgeMainFlow, Purge_ChamberPeripheryPurge, OpticPurgeMainFlow, ChamberPurgeFlow, RotationUpPurgeFlow, ShutterPurgeFlow, HeaterWFPurgeFlow, Purge_VentFlow, TotalVentFlow, VentPushFlow, VentPreExhaustPressure } public class ProcessTypeFileItem : NotifiableItem { public string ProcessType { get; set; } public ObservableCollection FileListByProcessType { get; set; } private ObservableCollection filterFileListByProcessType; public ObservableCollection FilterFileListByProcessType { get { return filterFileListByProcessType; } set { filterFileListByProcessType = value; InvokePropertyChanged("FilterFileListByProcessType"); } } public ProcessTypeFileItem() { FileListByProcessType = new ObservableCollection(); FilterFileListByProcessType = new ObservableCollection(); } } public class ChamberTypeItem : NotifiableItem { public string ChamberType { get; set; } public ObservableCollection FileListByChamberType { get; set; } public ChamberTypeItem() { FileListByChamberType = new ObservableCollection(); } } public class RecipeEditorViewModel : UiViewModelBase //BaseModel { public enum FlowMode { Purge, Vent, Run, Close, } public bool IsPermission { get => this.Permission == 3; }//&& RtStatus != "AutoRunning"; private ICommand _RenameFolderCommand; public ICommand RenameFolderCommand { get { if (this._RenameFolderCommand == null) this._RenameFolderCommand = new BaseCommand(() => this.RenameFolder()); return this._RenameFolderCommand; } } private ICommand _DeleteFolderCommand; public ICommand DeleteFolderCommand { get { if (this._DeleteFolderCommand == null) this._DeleteFolderCommand = new BaseCommand(() => this.DeleteFolder()); return this._DeleteFolderCommand; } } private ICommand _NewFolderCommand; public ICommand NewFolderCommand { get { if (this._NewFolderCommand == null) this._NewFolderCommand = new BaseCommand(() => this.NewFolder()); return this._NewFolderCommand; } } private ICommand _NewFolderRootCommand; public ICommand NewFolderRootCommand { get { if (this._NewFolderRootCommand == null) this._NewFolderRootCommand = new BaseCommand(() => this.NewFolderRoot()); return this._NewFolderRootCommand; } } private ICommand _NewRecipeCommand; public ICommand NewRecipeCommand { get { if (this._NewRecipeCommand == null) this._NewRecipeCommand = new BaseCommand(() => this.NewRecipe()); return this._NewRecipeCommand; } } private ICommand _NewRecipeRootCommand; public ICommand NewRecipeRootCommand { get { if (this._NewRecipeRootCommand == null) this._NewRecipeRootCommand = new BaseCommand(() => this.NewRecipeRoot()); return this._NewRecipeRootCommand; } } private ICommand _RenameRecipeCommand; public ICommand RenameRecipeCommand { get { if (this._RenameRecipeCommand == null) this._RenameRecipeCommand = new BaseCommand(() => this.RenameRecipe()); return this._RenameRecipeCommand; } } private ICommand _DeleteRecipeCommand; public ICommand DeleteRecipeCommand { get { if (this._DeleteRecipeCommand == null) this._DeleteRecipeCommand = new BaseCommand(() => this.DeleteRecipe()); return this._DeleteRecipeCommand; } } private ICommand _SaveAsRecipeCommand; public ICommand SaveAsRecipeCommand { get { if (this._SaveAsRecipeCommand == null) this._SaveAsRecipeCommand = new BaseCommand(() => this.SaveAsRecipe()); return this._SaveAsRecipeCommand; } } private ObservableCollection processTypeFileList; public ObservableCollection ProcessTypeFileList { get { return processTypeFileList; } set { processTypeFileList = value; NotifyOfPropertyChange("ProcessTypeFileList"); } } public RecipeData currentRecipe; public RecipeData CurrentRecipe { get { return currentRecipe; } set { currentRecipe = value; NotifyOfPropertyChange("CurrentRecipe"); } } public FileNode CurrentFileNode { get; set; } private bool IsChanged { get { return editMode == EditMode.Edit || CurrentRecipe.IsChanged; } } public ObservableCollection Columns { get; set; } public Dictionary> DicColunms { get; set; } public bool EnableNew { get; set; } public bool EnableReName { get; set; } public bool EnableCopy { get; set; } public bool EnableDelete { get; set; } public bool EnableSave { get; set; } public bool EnableStep { get; set; } public bool EnableReload { get =>IsPermission; } public bool EnableSaveToAll { get; set; } public bool EnableSaveTo { get; set; } private RecipeFormatBuilder _columnBuilder = new RecipeFormatBuilder(); private EditMode editMode; private RecipeProvider _recipeProvider = new RecipeProvider(); public ObservableCollection ChamberType { get; set; } public int ChamberTypeIndexSelection { get; set; } public int ProcessTypeIndexSelection { get; set; } public string CurrentChamberType { get { return ChamberType[ChamberTypeIndexSelection]; } } public string CurrentProcessType { get { return ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType; } } public Visibility MultiChamberVisibility { get; set; } public ObservableCollection Chambers { get; set; } public string SelectedChamber { get; set; } public object View { get; set; } private bool isRunPrugeSwitch; private bool isTMANotInstall; private bool isHClNotInstall; private bool isHClFlowNotInstall; #region Password check private bool _editorEnabled = false; public bool EditorEnable { get { return _editorEnabled; } set { _editorEnabled = value; } } public Visibility EditorEnableV { get { return EditorEnable ? Visibility.Visible : Visibility.Hidden; } } private string _loginText = "Log In"; public string LoginText { get { return _loginText; } set { LoginText = value; } } [Subscription("PM1.Recipe.EditPassword")] public string EPassword { get; set; } public void ChangePassword() { var pcg = new EditorPassChangeView(this); pcg.Password = EPassword; var bRes = pcg.ShowDialog(); } public bool ChanagePasswordReal(string sNewPassword) { try { //EPassword = sNewPassword; InvokeClient.Instance.Service.DoOperation("PM1.Recipe.EditorChangePassword", sNewPassword); System.Threading.Thread.Sleep(1000); return (sNewPassword == EPassword); } catch { return false; } //return true; } public void EditLogin() { if (EditorEnable) { EditorEnable = false; _loginText = "Log In"; } else { EditorEnable = PasswordCheck(); if (EditorEnable) { _loginText = "Log out"; } } //已更换继承的父类,这里不再使用Notify //NotifyOfPropertyChange("LoginText"); //NotifyOfPropertyChange("EditorEnable"); } private bool PasswordCheck() { var pc = new EditorPassCheckView(this); pc.Password = EPassword; if (pc.Password == "0") { MessageBox.Show("First time to use. Please click ChgPass button to Change Password."); return false; } var bRes = pc.ShowDialog(); // if (bRes == true) { return true; } // return false; } // #endregion protected override void OnInitialize() { base.OnInitialize(); var chamberType = QueryDataClient.Instance.Service.GetConfig("System.Recipe.SupportedChamberType"); if (chamberType == null) { ChamberType = new ObservableCollection() { "Default" }; } else { ChamberType = new ObservableCollection(((string)(chamberType)).Split(',')); } ChamberTypeIndexSelection = 0; var processType = "Process,Routine,Clean"; ProcessTypeFileList = new ObservableCollection(); var recipeProcessType = ((string)processType).Split(','); for (var i = 0; i < recipeProcessType.Length; i++) { var type = new ProcessTypeFileItem(); type.ProcessType = recipeProcessType[i]; var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{recipeProcessType[i]}"; var recipes = _recipeProvider.GetXmlRecipeList(prefix); type.FileListByProcessType = RecipeSequenceTreeBuilder.BuildFileNode(prefix, "", false, recipes)[0].Files; type.FilterFileListByProcessType = type.FileListByProcessType; ProcessTypeFileList.Add(type); } DicColunms = new Dictionary>(); UpdateRecipeFormat(); } protected override void OnActivate() { //初始化RoleManager var roleManager = new RoleManager(); roleManager.Initialize(); //得到当前登录的RoleItem var roleItem = roleManager.GetRoleByName(ClientApp.Instance.UserContext.RoleName); var menuPermission = new MenuPermission(); menuPermission.ParsePermission(roleItem.Role.MenuPermission); foreach (var col in Columns) { if (menuPermission.MenuPermissionDictionary.ContainsKey(col.DisplayName.Replace(" ", "")) && menuPermission.MenuPermissionDictionary[col.DisplayName.Replace(" ", "")] == MenuPermissionEnum.MP_NONE) { col.Visibility = Visibility.Hidden; } if (col.ControlName == "StepNo") continue; if (CurrentProcessType.EndsWith("Process") && menuPermission.MenuPermissionDictionary.ContainsKey(col.DisplayName.Replace(" ", ""))) { RecipeFormatBuilder.SetCellEnable(col, menuPermission.MenuPermissionDictionary[col.DisplayName.Replace(" ", "")]); } } //this.Columns = this._columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true, ClientApp.Instance.UserContext.RoleName); base.OnActivate(); } protected override void OnDeactivate(bool close) { base.OnDeactivate(close); if (this.IsChanged) { if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} content is changed, do you want to save it?") == DialogButton.Yes) { this.SaveRecipe(); } } } protected override void OnViewLoaded(object view) { View = view; base.OnViewLoaded(view); RecipeFormatBuilder.ApplyTemplate((UserControl)view, this.Columns); var u = (RecipeEditorView)view; u.dgCustom.Columns.Clear(); this.Columns.Apply((c) => { c.Header = c; u.dgCustom.Columns.Add(c); }); u.dgCustom.ItemsSource = this.CurrentRecipe.Steps; u.dgCustom.LostFocus += DgCustom_LostFocus; u.dgCustom.FrozenColumnCount = 4; } private void LostFocusFunc() { isRunPrugeSwitch = false; isTMANotInstall = false; isHClNotInstall = false; isHClFlowNotInstall = false; CurrentRecipe.RecipeTotalTime = 0; for (var selectIndex = 0; selectIndex < this.CurrentRecipe.Steps.Count; selectIndex++) { var previousIndex = selectIndex == 0 ? 0 : selectIndex - 1; var currentIndex = selectIndex; var nextIndex = selectIndex == this.CurrentRecipe.Steps.Count - 1 ? selectIndex : selectIndex + 1; var previousStep = this.CurrentRecipe.Steps[previousIndex]; var currentStep = this.CurrentRecipe.Steps[currentIndex]; var nextStep = this.CurrentRecipe.Steps[nextIndex]; //判断每步与上一步值是否相同,不同则以颜色区分 JudgeStepSameValue(previousStep, currentStep); //如果是Routine则在下面代码不执行,在这里continue if (CurrentProcessType == ProcessTypeFileList[1].ProcessType) { //如果是Routine,则总时间需*循环次数 var loopTimes = int.Parse((currentStep[8] as DoubleParam).Value) == 0 ? 1 : int.Parse((currentStep[8] as DoubleParam).Value); CurrentRecipe.RecipeTotalTime += int.Parse((currentStep[(int)RecipColNo.StepTime] as DoubleParam).Value) * loopTimes; continue; } else { //如果是Recipe则总时间直接加 CurrentRecipe.RecipeTotalTime += int.Parse((currentStep[(int)RecipColNo.StepTime] as DoubleParam).Value); } double.TryParse((currentStep[(int)RecipColNo.StepTime] as DoubleParam).Value, out var stepTime); PurgeRunSwitch(previousStep, currentStep, nextStep); NotIntallDevice(currentStep); if (stepTime > 0) { CalRecipeParameterForRunVent(previousStep, currentStep, stepTime); } } //如果是Routine则在下面代码不执行,在这里返回 if (CurrentProcessType == ProcessTypeFileList[1].ProcessType) { return; } if (isRunPrugeSwitch) MessageBox.Show("Flow mode 'Run' and 'Purge' can't switch directly", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); if (isTMANotInstall) MessageBox.Show("TMA is not install, only Purge mode is valid", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); if (isHClNotInstall) MessageBox.Show("GR Purge HCl is not install, only Disable is valid", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); if (isHClFlowNotInstall) MessageBox.Show("GR Purge HCl Flow is not install, lock the value as zero", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); } private void DgCustom_LostFocus(object sender, RoutedEventArgs e) { LostFocusFunc(); } private void JudgeStepSameValue(ObservableCollection previousStep, ObservableCollection currentStep) { for (var i = 0; i < currentStep.Count; i++) { if(currentStep[i].GetType() == typeof(DoubleParam)) { if ((previousStep[i] as DoubleParam).Value != (currentStep[i] as DoubleParam).Value) { (currentStep[i] as DoubleParam).Foreground = "Green"; } else { (currentStep[i] as DoubleParam).Foreground = "Black"; } } if (currentStep[i].GetType() == typeof(ComboxParam)) { if ((previousStep[i] as ComboxParam).Value != (currentStep[i] as ComboxParam).Value) { (currentStep[i] as ComboxParam).Foreground = "Green"; } else { (currentStep[i] as ComboxParam).Foreground = "Black"; } } } } private void PurgeRunSwitch(ObservableCollection previousStep, ObservableCollection currentStep, ObservableCollection nextStep) { var previousSiH4FlowMode = previousStep[(int)RecipColNo.SiH4FlowMode] as ComboxParam; var previousTCSFlowMode = previousStep[(int)RecipColNo.TCSFlowMode] as ComboxParam; var previousHClFlowMode = previousStep[(int)RecipColNo.HCLFlowMode] as ComboxParam; var previousC2H4FlowMode = previousStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam; var previousN2FlowMode = previousStep[(int)RecipColNo.N2FlowMode] as ComboxParam; var previousN2HighFlowMode = previousStep[(int)RecipColNo.N2HighFlowMode] as ComboxParam; var previousTMAFlowMode = previousStep[(int)RecipColNo.TMAFlowMode] as ComboxParam; var currentSiH4FlowMode = currentStep[(int)RecipColNo.SiH4FlowMode] as ComboxParam; var currentTCSFlowMode = currentStep[(int)RecipColNo.TCSFlowMode] as ComboxParam; var currentHClFlowMode = currentStep[(int)RecipColNo.HCLFlowMode] as ComboxParam; var currentC2H4FlowMode = currentStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam; var currentN2FlowMode = currentStep[(int)RecipColNo.N2FlowMode] as ComboxParam; var currentN2HighFlowMode = currentStep[(int)RecipColNo.N2HighFlowMode] as ComboxParam; var currentTMAFlowMode = currentStep[(int)RecipColNo.TMAFlowMode] as ComboxParam; var nextSiH4FlowMode = nextStep[(int)RecipColNo.SiH4FlowMode] as ComboxParam; var nextTCSFlowMode = nextStep[(int)RecipColNo.TCSFlowMode] as ComboxParam; var nextHClFlowMode = nextStep[(int)RecipColNo.HCLFlowMode] as ComboxParam; var nextC2H4FlowMode = nextStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam; var nextN2FlowMode = nextStep[(int)RecipColNo.N2FlowMode] as ComboxParam; var nextN2HighFlowMode = nextStep[(int)RecipColNo.N2HighFlowMode] as ComboxParam; var nextTMAFlowMode = nextStep[(int)RecipColNo.TMAFlowMode] as ComboxParam; var previousFlowMode = new List() { previousSiH4FlowMode, previousTCSFlowMode, previousHClFlowMode, previousC2H4FlowMode, previousN2FlowMode,previousN2FlowMode, previousN2HighFlowMode, previousTMAFlowMode}; var currentFlowMode = new List() { currentSiH4FlowMode, currentTCSFlowMode, currentHClFlowMode, currentC2H4FlowMode, currentN2FlowMode,currentN2FlowMode, currentN2HighFlowMode, currentTMAFlowMode}; var nextFlowMode = new List() { nextSiH4FlowMode, nextTCSFlowMode, nextHClFlowMode, nextC2H4FlowMode, nextN2FlowMode,nextN2FlowMode, nextN2HighFlowMode, nextTMAFlowMode}; for (var i = 0; i < currentFlowMode.Count; i++) { if (previousFlowMode[i].Value == FlowMode.Run.ToString() && currentFlowMode[i].Value == FlowMode.Purge.ToString() || previousFlowMode[i].Value == FlowMode.Purge.ToString() && currentFlowMode[i].Value == FlowMode.Run.ToString()) { isRunPrugeSwitch = true; currentFlowMode[i].Value = FlowMode.Vent.ToString(); } if (nextFlowMode[i].Value == FlowMode.Run.ToString() && currentFlowMode[i].Value == FlowMode.Purge.ToString() || nextFlowMode[i].Value == FlowMode.Purge.ToString() && currentFlowMode[i].Value == FlowMode.Run.ToString()) { isRunPrugeSwitch = true; currentFlowMode[i].Value = FlowMode.Vent.ToString(); } } } private void NotIntallDevice(ObservableCollection currentStep) { var currentTMAFlowMode = currentStep[(int)RecipColNo.TMAFlowMode] as ComboxParam; //var currentGRPurgeHCl = currentStep[(int)RecipColNo.HeaterUpPurgeHCL] as ComboxParam; //var currentGRPurgeHClFlow = currentStep[(int)RecipColNo.GRPurgeHCLFlow] as DoubleParam; if (currentTMAFlowMode.Value != FlowMode.Purge.ToString()) { currentTMAFlowMode.Value = FlowMode.Purge.ToString(); isTMANotInstall = true; } //if (currentGRPurgeHCl.Value != "Disable") //{ // currentGRPurgeHCl.Value = "Disable"; // isHClNotInstall = true; //} //if(currentGRPurgeHClFlow != null) //{ // double.TryParse(currentGRPurgeHClFlow.Value, out double value); // if (value != 0) // { // currentGRPurgeHClFlow.Value = "0"; // isHClFlowNotInstall = true; // } //} } private void CalRecipeParameterForRunVent(ObservableCollection previousStep, ObservableCollection currentStep, double stepTime) { #region SH Total flow double.TryParse((currentStep[(int)RecipColNo.SHTotalFlow] as DoubleParam).Value, out var currentSHTotalFlow); double.TryParse((currentStep[(int)RecipColNo.CarrayGasFlow] as DoubleParam).Value, out var currentSurroundingFlow); var arrSHTotalFlowSplitRatio = (currentStep[(int)RecipColNo.SHTotalFlowSplitRatio] as StringParam).Value.Split(':'); var currentSHTotalFlowSplitRatio = new double[3]; if (arrSHTotalFlowSplitRatio.Length == 3) { double.TryParse(arrSHTotalFlowSplitRatio[0], out currentSHTotalFlowSplitRatio[0]); double.TryParse(arrSHTotalFlowSplitRatio[1], out currentSHTotalFlowSplitRatio[1]); double.TryParse(arrSHTotalFlowSplitRatio[2], out currentSHTotalFlowSplitRatio[2]); } #endregion #region Si Source double.TryParse((currentStep[(int)RecipColNo.SiH4Flow] as DoubleParam).Value, out var currentSiH4Flow); double.TryParse((currentStep[(int)RecipColNo.TCSBubbLowFlow] as DoubleParam).Value, out var currentTCSBubbLowFlow); double.TryParse((currentStep[(int)RecipColNo.TCSBubbHighFlow] as DoubleParam).Value, out var currentTCSBubbHighFlow); double.TryParse((currentStep[(int)RecipColNo.TCSPushFlow] as DoubleParam).Value, out var currentTCSPushFlow); double.TryParse((currentStep[(int)RecipColNo.HCLFlow] as DoubleParam).Value, out var currentHClFlow); var arrSiSourSplitRatio = (currentStep[(int)RecipColNo.SiSourceSplitRatio] as StringParam).Value.Split(':'); var currentSiSourSplitRatio = new double[3]; if (arrSiSourSplitRatio.Length == 3) { double.TryParse(arrSiSourSplitRatio[0], out currentSiSourSplitRatio[0]); double.TryParse(arrSiSourSplitRatio[1], out currentSiSourSplitRatio[1]); double.TryParse(arrSiSourSplitRatio[2], out currentSiSourSplitRatio[2]); } double.TryParse((currentStep[(int)RecipColNo.SiSourceTotalFlow] as DoubleParam).Value, out var currentSiSourTotalFlow); var currentSiH4FlowMode = (currentStep[(int)RecipColNo.SiH4FlowMode] as ComboxParam).Value; var currentTCSFlowMode = (currentStep[(int)RecipColNo.TCSFlowMode] as ComboxParam).Value; var currentHClFlowMode = (currentStep[(int)RecipColNo.HCLFlowMode] as ComboxParam).Value; double TCSFlow = 0; var TCS_Eff = (double)QueryDataClient.Instance.Service.GetConfig($"PM.{SelectedChamber}.Efficiency.TCS-Eff"); if (currentTCSFlowMode == FlowMode.Run.ToString() || currentTCSFlowMode == FlowMode.Vent.ToString()) TCSFlow = (currentTCSBubbHighFlow + currentTCSBubbLowFlow) * TCS_Eff + currentTCSPushFlow; else if (currentTCSFlowMode == FlowMode.Purge.ToString()) TCSFlow = currentTCSBubbHighFlow + currentTCSBubbLowFlow + currentTCSPushFlow; //double currentSiSourPushFlow = currentSiSourTotalFlow - (currentSiH4FlowMode == FlowMode.Run.ToString() ? currentSiH4Flow : 0) // - (currentTCSFlowMode == FlowMode.Run.ToString() ? TCSFlow : 0) - (currentHClFlowMode == FlowMode.Run.ToString() ? currentHClFlow : 0); //(currentStep[(int)RecipColNo.SiSourceOuterFlow] as DoubleParam).Value = currentSiSourPushFlow.ToString("F1"); double currentSiSourInnerFlow = 0; double currentSiSourMidFlow = 0; double currentSiSourOutFlow = 0; if (currentSiSourSplitRatio.Length == 3) { var rationSum = currentSiSourSplitRatio[0] + currentSiSourSplitRatio[1] + currentSiSourSplitRatio[2]; currentSiSourInnerFlow = currentSiSourTotalFlow / rationSum * currentSiSourSplitRatio[0]; currentSiSourMidFlow = currentSiSourTotalFlow / rationSum * currentSiSourSplitRatio[1]; currentSiSourOutFlow = currentSiSourTotalFlow / rationSum * currentSiSourSplitRatio[2]; } (currentStep[(int)RecipColNo.SiSourceInnerFlow] as DoubleParam).Value = currentSiSourInnerFlow.ToString("F1"); (currentStep[(int)RecipColNo.SiSourceMiddleFlow] as DoubleParam).Value = currentSiSourMidFlow.ToString("F1"); (currentStep[(int)RecipColNo.SiSourceOuterFlow] as DoubleParam).Value = currentSiSourOutFlow.ToString("F1"); #endregion #region C Source double.TryParse((currentStep[(int)RecipColNo.C2H4Flow] as DoubleParam).Value, out var currentC2H4Flow); var arrCSourSplitRatio = (currentStep[(int)RecipColNo.CSourceSplitRatio] as StringParam).Value.Split(':'); var currentCSourSplitRatio = new double[3]; if (arrCSourSplitRatio.Length == 3) { double.TryParse(arrCSourSplitRatio[0], out currentCSourSplitRatio[0]); double.TryParse(arrCSourSplitRatio[1], out currentCSourSplitRatio[1]); double.TryParse(arrCSourSplitRatio[2], out currentCSourSplitRatio[2]); } double.TryParse((currentStep[(int)RecipColNo.CSourceTotalFlow] as DoubleParam).Value, out var currentCSourTotalFlow); var currentC2H4FlowMode = (currentStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam).Value; var currentCSourPushFlow = currentCSourTotalFlow - (currentC2H4FlowMode == FlowMode.Run.ToString() ? currentC2H4Flow : 0); (currentStep[(int)RecipColNo.CSourceOuterFlow] as DoubleParam).Value = currentCSourPushFlow.ToString("F1"); double currentCSourInnerFlow = 0; double currentCSourMidFlow = 0; double currentCSourOuterFlow = 0; if (currentCSourSplitRatio.Length == 3) { var ratioSum = currentCSourSplitRatio[0] + currentCSourSplitRatio[1] + currentCSourSplitRatio[2]; currentCSourInnerFlow = currentCSourTotalFlow / ratioSum * currentCSourSplitRatio[0]; currentCSourMidFlow = currentCSourTotalFlow / ratioSum * currentCSourSplitRatio[1]; currentCSourOuterFlow= currentCSourTotalFlow / ratioSum * currentCSourSplitRatio[2]; } (currentStep[(int)RecipColNo.CSourceInnerFlow] as DoubleParam).Value = currentCSourInnerFlow.ToString("F1"); (currentStep[(int)RecipColNo.CSourceMiddleFlow] as DoubleParam).Value = currentCSourMidFlow.ToString("F1"); (currentStep[(int)RecipColNo.CSourceOuterFlow] as DoubleParam).Value = currentCSourOuterFlow.ToString("F1"); #endregion #region Dope double.TryParse((currentStep[(int)RecipColNo.DopeTotalFlow] as DoubleParam).Value, out var currentDopeTotalFlow); double.TryParse((currentStep[(int)RecipColNo.N2ActualFlow] as DoubleParam).Value, out var currentN2ActualFlow); double.TryParse((currentStep[(int)RecipColNo.N2LowFlow] as DoubleParam).Value, out var currentN2LowFlow); double.TryParse((currentStep[(int)RecipColNo.DilutFlowForN2] as DoubleParam).Value, out var currentDiluFlowForN2); double.TryParse((currentStep[(int)RecipColNo.N2HighFlow] as DoubleParam).Value, out var currentN2HighFlow); double.TryParse((currentStep[(int)RecipColNo.TMABubbFlow] as DoubleParam).Value, out var currentTMABubbFlow); double.TryParse((currentStep[(int)RecipColNo.TMAPushFlow] as DoubleParam).Value, out var currentTMAPushFlow); var currentDilutedN2Flow = (currentN2LowFlow + currentDiluFlowForN2) * currentN2ActualFlow / currentN2LowFlow; (currentStep[(int)RecipColNo.DilutedN2Flow] as DoubleParam).Value = currentDilutedN2Flow.ToString("F1"); var arrDopeSplitRatio = (currentStep[(int)RecipColNo.DopeSplitRatio] as StringParam).Value.Split(':'); var currentDopeSplitRatio = new double[3]; if (arrDopeSplitRatio.Length == 3) { double.TryParse(arrDopeSplitRatio[0], out currentDopeSplitRatio[0]); double.TryParse(arrDopeSplitRatio[1], out currentDopeSplitRatio[1]); double.TryParse(arrDopeSplitRatio[2], out currentDopeSplitRatio[2]); } var currentN2FlowMode = (currentStep[(int)RecipColNo.N2FlowMode] as ComboxParam).Value; var currentN2HighFlowMode = (currentStep[(int)RecipColNo.N2HighFlowMode] as ComboxParam).Value; var currentTMAFlowMode = (currentStep[(int)RecipColNo.TMAFlowMode] as ComboxParam).Value; double TMAFlow = 0; var TMA_Eff = (double)QueryDataClient.Instance.Service.GetConfig($"PM.{SelectedChamber}.Efficiency.TMA-Eff"); if (currentTMAFlowMode == FlowMode.Run.ToString() || currentTMAFlowMode == FlowMode.Vent.ToString()) TMAFlow = currentTMABubbFlow * TMA_Eff + currentTMAPushFlow; else if (currentTMAFlowMode == FlowMode.Purge.ToString()) TMAFlow = currentTMABubbFlow + currentTMAPushFlow; //double currentDopePushFlow = currentDopeTotalFlow // - (currentN2FlowMode == FlowMode.Run.ToString() ? currentDilutedN2Flow : 0) // - (currentN2HighFlowMode == FlowMode.Run.ToString() ? currentN2HighFlow : 0) // - (currentTMAFlowMode == FlowMode.Run.ToString() ? TMAFlow : 0); //(currentStep[(int)RecipColNo.DopeOuterFlow] as DoubleParam).Value = currentDopePushFlow.ToString("F1"); double currentDopeInnerFlow = 0; double currentDopeMidFlow = 0; double currentDopeOuterFlow = 0; if (currentDopeSplitRatio.Length == 3) { var ratioSum = currentDopeSplitRatio[0] + currentDopeSplitRatio[1] + currentDopeSplitRatio[2]; currentDopeInnerFlow = currentDopeTotalFlow / ratioSum * currentDopeSplitRatio[0]; currentDopeMidFlow = currentDopeTotalFlow / ratioSum * currentDopeSplitRatio[1]; currentDopeOuterFlow = currentDopeTotalFlow / ratioSum * currentDopeSplitRatio[2]; } (currentStep[(int)RecipColNo.DopeInnerFlow] as DoubleParam).Value = currentDopeInnerFlow.ToString("F1"); (currentStep[(int)RecipColNo.DopeMiddleFlow] as DoubleParam).Value = currentDopeMidFlow.ToString("F1"); (currentStep[(int)RecipColNo.DopeOuterFlow] as DoubleParam).Value = currentDopeOuterFlow.ToString("F1"); #endregion #region SH Supp. Flow var currentSHSuppTotalFlow = currentSHTotalFlow - currentSurroundingFlow - currentSiSourTotalFlow - currentCSourTotalFlow - currentDopeTotalFlow; var sumSHTotalFlowSplitRatio = currentSHTotalFlowSplitRatio[0] + currentSHTotalFlowSplitRatio[1] + currentSHTotalFlowSplitRatio[2]; var currentSHInnerFlow = (currentSHTotalFlow - currentSurroundingFlow) / sumSHTotalFlowSplitRatio * currentSHTotalFlowSplitRatio[0]; var currentSHMidFlow = (currentSHTotalFlow - currentSurroundingFlow) / sumSHTotalFlowSplitRatio * currentSHTotalFlowSplitRatio[1]; var currentSHOutterFlow = (currentSHTotalFlow - currentSurroundingFlow) / sumSHTotalFlowSplitRatio * currentSHTotalFlowSplitRatio[2]; var currentInnerSuppFlow = currentSHInnerFlow - currentSiSourInnerFlow - currentCSourInnerFlow - currentDopeInnerFlow; var currentMidSuppFlow = currentSHMidFlow - currentSiSourMidFlow - currentCSourMidFlow - currentDopeMidFlow; var currentOutterSuppFlow = currentSHSuppTotalFlow - currentInnerSuppFlow - currentMidSuppFlow; (currentStep[(int)RecipColNo.SHPushTotalFlow] as DoubleParam).Value = currentSHSuppTotalFlow.ToString("F1"); (currentStep[(int)RecipColNo.SHInnerFlow] as DoubleParam).Value = currentSHInnerFlow.ToString("F1"); (currentStep[(int)RecipColNo.SHMiddleFlow] as DoubleParam).Value = currentSHMidFlow.ToString("F1"); (currentStep[(int)RecipColNo.SHOuterFlow] as DoubleParam).Value = currentSHOutterFlow.ToString("F1"); (currentStep[(int)RecipColNo.InnerPushFlow] as DoubleParam).Value = currentInnerSuppFlow.ToString("F1"); (currentStep[(int)RecipColNo.MiddlePushFlow] as DoubleParam).Value = currentMidSuppFlow.ToString("F1"); (currentStep[(int)RecipColNo.OuterPushFlow] as DoubleParam).Value = currentOutterSuppFlow.ToString("F1"); #endregion #region Vent Flow double.TryParse((currentStep[(int)RecipColNo.TotalVentFlow] as DoubleParam).Value, out var currentTotalVentFlow); var currentSiSourTotalFlowForPurge = (currentSiH4FlowMode != FlowMode.Run.ToString() ? currentSiH4Flow : 0) + (currentTCSFlowMode != FlowMode.Run.ToString() ? TCSFlow : 0) + (currentHClFlowMode != FlowMode.Run.ToString() ? currentHClFlow : 0); var currentCSourTotalFlowForPurge = currentC2H4FlowMode != FlowMode.Run.ToString() ? currentC2H4Flow : 0; var N2Flow = (currentN2FlowMode != FlowMode.Run.ToString() ? currentDilutedN2Flow : 0) + (currentN2HighFlowMode != FlowMode.Run.ToString() ? currentN2HighFlow : 0); var currentDopeTotalFlowForPurge = N2Flow + (currentTMAFlowMode != FlowMode.Run.ToString() ? TMAFlow : 0); var currentVentPushFlow = currentTotalVentFlow - currentSiSourTotalFlowForPurge - currentCSourTotalFlowForPurge - currentDopeTotalFlowForPurge; (currentStep[(int)RecipColNo.VentPushFlow] as DoubleParam).Value = currentVentPushFlow.ToString("F1"); #endregion } public void TabSelectionChanged() { UpdateRecipeFormat(); OnViewLoaded(View); } public void UpdateRecipeFormat() { var chamber = QueryDataClient.Instance.Service.GetConfig("System.Recipe.ChamberModules"); if (chamber == null) { chamber = "PM1"; } Chambers = new ObservableCollection(((string)chamber).Split(',')); if (Chambers.Count > 1) { for (var i = 0; i < Chambers.Count; i++) { var isPmInstall = (bool)QueryDataClient.Instance.Service.GetConfig($"System.SetUp.Is{Chambers[i].ToString()}Installed"); { if (!isPmInstall) { Chambers.RemoveAt(i); } } } } if (Chambers.Count == 0) { Chambers = new ObservableCollection(new string[] { "PM1" }); } SelectedChamber = Chambers[0]; if (DicColunms.Keys.Contains(CurrentProcessType)) { this.Columns = DicColunms[CurrentProcessType]; } else { this.Columns = this._columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true,ClientApp.Instance.UserContext.RoleName); DicColunms[CurrentProcessType] = this.Columns; } this.CurrentRecipe = new RecipeData(); CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType; CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion; this.editMode = EditMode.None; MultiChamberVisibility = Chambers.Count > 1 ? Visibility.Visible : Visibility.Collapsed; } public void ChamberSelectionChanged() { if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } CurrentRecipe.ChangeChamber(Columns , _columnBuilder.Configs, SelectedChamber); } public void TreeSelectChanged(FileNode node) { if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } CurrentFileNode = node; if (node != null && node.IsFile) { this.LoadData(node.PrefixPath, node.FullPath); } else { this.ClearData(); this.editMode = EditMode.None; } this.UpdateView(); //OnViewLoaded(View);//zyx debug } #region folder public void NewFolder() { if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Cancel) return; if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } var dialog = new InputFileNameDialogViewModel("Input New Folder Name"); dialog.FileName = "new folder"; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var name = dialog.FileName.Trim(); if (string.IsNullOrEmpty(name)) { DialogBox.ShowWarning("Folder name should not be empty"); return; } var prefix = ChamberType[ChamberTypeIndexSelection] + "\\" + ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType; var processType = string.Empty; var newFolder = string.Empty; if (CurrentFileNode != null) { prefix = CurrentFileNode.PrefixPath; var folder = CurrentFileNode.FullPath; if (CurrentFileNode.IsFile) { folder = folder.Substring(0, folder.LastIndexOf("\\") + 1); if (!string.IsNullOrEmpty(folder)) newFolder = folder; } else { newFolder = folder + "\\"; } } newFolder = newFolder + name; if (IsExist(newFolder, false)) { DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder with the same name already exist."); return; } if (newFolder.Length > 200) { DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200."); return; } _recipeProvider.CreateRecipeFolder(prefix, newFolder); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true); } public void NewFolderRoot() { if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Cancel) return; if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } var dialog = new InputFileNameDialogViewModel("Input New Folder Name"); dialog.FileName = "new folder"; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var name = dialog.FileName.Trim(); if (string.IsNullOrEmpty(name)) { DialogBox.ShowWarning("Folder name should not be empty"); return; } if (IsExist(name, false)) { DialogBox.ShowWarning($"Can not create folder {name}, Folder with the same name already exist."); return; } if (name.Length > 200) { DialogBox.ShowWarning($"Can not create folder {name}, Folder name too long, should be less 200."); return; } var prefix = ChamberType[ChamberTypeIndexSelection] + "\\" + ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType; _recipeProvider.CreateRecipeFolder(prefix, name); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, name, true); } public void DeleteFolder() { if (CurrentFileNode == null || CurrentFileNode.IsFile) return; if (CurrentFileNode.Files.Count > 0) { DialogBox.ShowWarning($"Can not delete non-empty folder, Remove the files or folders under \r\n{CurrentFileNode.FullPath}."); return; } var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Are you sure you want to delete \r\n {CurrentFileNode.FullPath}?"); if (selection == DialogButton.No) return; var nextFocus = CurrentFileNode.Parent.FullPath; var isFolder = true; if (CurrentFileNode.Parent.Files.Count > 1) { for (var i = 0; i < CurrentFileNode.Parent.Files.Count; i++) { if (CurrentFileNode.Parent.Files[i] == CurrentFileNode) { if (i == 0) { nextFocus = CurrentFileNode.Parent.Files[i + 1].FullPath; isFolder = !CurrentFileNode.Parent.Files[i + 1].IsFile; } else { nextFocus = CurrentFileNode.Parent.Files[i - 1].FullPath; isFolder = !CurrentFileNode.Parent.Files[i - 1].IsFile; } } } } _recipeProvider.DeleteRecipeFolder(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder); } public void RenameFolder() { if (CurrentFileNode == null || CurrentFileNode.IsFile) return; var dialog = new InputFileNameDialogViewModel("Input New Folder Name"); dialog.FileName = CurrentFileNode.Name; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var name = dialog.FileName.Trim(); if (string.IsNullOrEmpty(name)) return; var newFolder = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1); if (!string.IsNullOrEmpty(newFolder)) newFolder = newFolder + name; else newFolder = name; if (newFolder == CurrentFileNode.FullPath) return; if (IsExist(newFolder, false)) { DialogBox.ShowWarning($"Can not rename to {newFolder}, Folder with the same name already exist."); return; } if (newFolder.Length > 200) { DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200."); return; } _recipeProvider.RenameFolder(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath, newFolder); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true); } #endregion #region recipe public void NewRecipe() { if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Cancel) return; if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } var dialog = new InputFileNameDialogViewModel("Input New Name"); dialog.FileName = ""; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var recipeName = dialog.FileName.Trim(); if (string.IsNullOrEmpty(dialog.FileName)) { DialogBox.ShowWarning("Recipe file name should not be empty"); return; } var prefix = CurrentChamberType + "\\" + CurrentProcessType; var processType = string.Empty; if (CurrentFileNode != null) { var folder = CurrentFileNode.FullPath; if (CurrentFileNode.IsFile) { folder = folder.Substring(0, folder.LastIndexOf("\\") + 1); //if (!string.IsNullOrEmpty(folder)) // folder = folder; } else { folder = folder + "\\"; } recipeName = folder + recipeName; } if (IsExist(recipeName, true)) { DialogBox.ShowWarning($"Can not create {recipeName}, Recipe with the same name already exist."); return; } if (recipeName.Length > 200) { DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200."); return; } var recipe = new RecipeData(); recipe.Name = recipeName; recipe.PrefixPath = prefix; recipe.Creator = BaseApp.Instance.UserContext.LoginName; recipe.CreateTime = DateTime.Now; recipe.Revisor = BaseApp.Instance.UserContext.LoginName; recipe.ReviseTime = DateTime.Now; recipe.Description = string.Empty; if (!Save(recipe, true)) return; var types = prefix.Split('\\'); ReloadRecipeFileList(types[0], types[1], recipeName, false); } public void NewRecipeRoot() { if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Cancel) return; if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } var dialog = new InputFileNameDialogViewModel("Input New Recipe Name"); dialog.FileName = "new recipe"; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var recipeName = dialog.FileName.Trim(); if (string.IsNullOrEmpty(dialog.FileName)) { DialogBox.ShowWarning("Recipe file name should not be empty"); return; } if (IsExist(recipeName, true)) { DialogBox.ShowWarning($"Can not create {recipeName}, Recipe with the same name already exist."); return; } if (recipeName.Length > 200) { DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200."); return; } var recipe = new RecipeData(); recipe.Name = recipeName; recipe.PrefixPath = CurrentChamberType + "\\" + CurrentProcessType; recipe.Creator = BaseApp.Instance.UserContext.LoginName; recipe.CreateTime = DateTime.Now; recipe.Revisor = BaseApp.Instance.UserContext.LoginName; recipe.ReviseTime = DateTime.Now; recipe.Description = string.Empty; if (!Save(recipe, true)) return; ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, recipeName, false); } private void ReloadRecipeFileList(string chamberType, string processType, string selectedFile, bool selectionIsFolder) { var item = ProcessTypeFileList.FirstOrDefault(x => x.ProcessType == processType); if (item == null) { LOG.Write("error reload recipe file list, type = " + processType); } var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{item.ProcessType}"; var recipes = _recipeProvider.GetXmlRecipeList(prefix); item.FileListByProcessType = RecipeSequenceTreeBuilder.BuildFileNode(prefix, selectedFile, selectionIsFolder, recipes)[0].Files; item.FilterFileListByProcessType = item.FileListByProcessType; item.InvokePropertyChanged(); } private bool IsExist(string fullPath, bool isFile) { for (var i = 0; i < ProcessTypeFileList.Count; i++) { if (ProcessTypeFileList[i].ProcessType == CurrentProcessType) { if (ProcessTypeFileList[i].FileListByProcessType.Count == 0) return false; return FindFile(fullPath, ProcessTypeFileList[i].FileListByProcessType[0].Parent, isFile); } } return true; } private bool FindFile(string path, FileNode root, bool isFile) { if (root.FullPath == path && !isFile) { return true; } foreach (var node in root.Files) { if (isFile && node.IsFile && node.FullPath == path) return true; if (!node.IsFile && FindFile(path, node, isFile)) return true; } return false; } public void SaveAsRecipe() { if (CurrentFileNode == null || !CurrentFileNode.IsFile) return; if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Cancel) return; if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } var dialog = new InputFileNameDialogViewModel("Input New Recipe Name"); dialog.FileName = CurrentFileNode.Name; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var recipeName = dialog.FileName.Trim(); if (string.IsNullOrEmpty(dialog.FileName)) { DialogBox.ShowWarning("Recipe file name should not be empty"); return; } var prefix = CurrentChamberType + "\\" + CurrentProcessType; var processType = string.Empty; var folder = CurrentFileNode.FullPath; if (CurrentFileNode.IsFile) { folder = folder.Substring(0, folder.LastIndexOf("\\") + 1); } if (!string.IsNullOrEmpty(folder)) recipeName = folder + "\\" + recipeName; if (CurrentFileNode.FullPath == recipeName) return; if (IsExist(recipeName, true)) { DialogBox.ShowWarning($"Can not copy to {recipeName}, Recipe with the same name already exist."); return; } if (recipeName.Length > 200) { DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200."); return; } CurrentRecipe.Creator = BaseApp.Instance.UserContext.LoginName; CurrentRecipe.CreateTime = DateTime.Now; CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; CurrentRecipe.ReviseTime = DateTime.Now; CurrentRecipe.Description = CurrentRecipe.Description + ". Renamed from " + CurrentFileNode.Name; _recipeProvider.SaveAsRecipe(prefix, recipeName, CurrentRecipe.GetXmlString()); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, recipeName, false); } public void RenameRecipe() { if (CurrentFileNode == null || !CurrentFileNode.IsFile) return; if (IsChanged) { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?"); if (selection == DialogButton.Cancel) return; if (selection == DialogButton.Yes) { this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName; this.CurrentRecipe.ReviseTime = DateTime.Now; this.Save(this.CurrentRecipe, false); } } var dialog = new InputFileNameDialogViewModel("Input New Recipe Name"); dialog.FileName = CurrentFileNode.Name; var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var recipeName = dialog.FileName.Trim(); if (string.IsNullOrEmpty(dialog.FileName)) { DialogBox.ShowWarning("Recipe file name should not be empty"); return; } var prefix = CurrentChamberType + "\\" + CurrentProcessType; var processType = string.Empty; var newName = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1); if (!string.IsNullOrEmpty(newName)) newName = newName + recipeName; else newName = recipeName; if (newName == CurrentFileNode.FullPath) return; if (IsExist(newName, true)) { DialogBox.ShowWarning($"Can not rename to {newName}, Recipe with the same name already exist."); return; } if (newName.Length > 200) { DialogBox.ShowWarning($"Can not create folder {newName}, Folder name too long, should be less 200."); return; } _recipeProvider.RenameRecipe(prefix, CurrentFileNode.FullPath, newName); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newName, false); } public void DeleteRecipe() { if (CurrentFileNode == null || !CurrentFileNode.IsFile) return; var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Are you sure you want to delete \r\n {CurrentFileNode.FullPath}?"); if (selection == DialogButton.No) return; var nextFocus = CurrentFileNode.Parent.FullPath; var isFolder = true; if (CurrentFileNode.Parent.Files.Count > 1) { for (var i = 0; i < CurrentFileNode.Parent.Files.Count; i++) { if (CurrentFileNode.Parent.Files[i] == CurrentFileNode) { if (i == 0) { nextFocus = CurrentFileNode.Parent.Files[i + 1].FullPath; isFolder = !CurrentFileNode.Parent.Files[i + 1].IsFile; } else { nextFocus = CurrentFileNode.Parent.Files[i - 1].FullPath; isFolder = !CurrentFileNode.Parent.Files[i - 1].IsFile; } } } } _recipeProvider.DeleteRecipe(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath); ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder); } public void RefreshRecipe() { ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, "", false); } public void ReloadRecipe() { if (this.editMode == EditMode.Normal || this.editMode == EditMode.Edit) { this.LoadData(CurrentRecipe.PrefixPath, CurrentRecipe.Name); this.UpdateView(); } } public void SaveToAll() { LostFocusFunc(); if (!CurrentRecipe.IsCompatibleWithCurrentFormat) { DialogBox.ShowWarning($"Save failed, {CurrentRecipe.Name} is not a valid recipe file"); return; } var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Do you want to save to all? \r\n This will replace all the other chamber recipe content"); if (selection == DialogButton.No) return; CurrentRecipe.SaveTo(Chambers.ToArray()); Save(this.CurrentRecipe, false); } public void SaveTo() { LostFocusFunc(); if (!CurrentRecipe.IsCompatibleWithCurrentFormat) { DialogBox.ShowWarning($"Save failed, {CurrentRecipe.Name} is not a valid recipe file"); return; } var dialog = new SaveToDialogViewModel("Select which chamber to copy to", SelectedChamber, Chambers.ToList()); var wm = new WindowManager(); var dialogReturn = wm.ShowDialog(dialog); if (!dialogReturn.HasValue || !dialogReturn.Value) return; var chambers = new List(); foreach (var dialogChamber in dialog.Chambers) { if (dialogChamber.IsEnabled && dialogChamber.IsChecked) chambers.Add(dialogChamber.Name); } if (chambers.Count == 0) return; CurrentRecipe.SaveTo(chambers.ToArray()); Save(this.CurrentRecipe, false); } #endregion #region Steps public void ParamsExpanded(ExpanderColumn col) { var 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) { var 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; } } public bool CheckColumnDataAvalible() { if (CurrentProcessType == ProcessTypeFileList[1].ProcessType) { return true; } var lstNotCorrectInfo = new List(); var lstOutOfRange = new List(); var lstSplitColums = new List() { (int)RecipColNo.SHTotalFlowSplitRatio, (int)RecipColNo.SiSourceSplitRatio, (int)RecipColNo.CSourceSplitRatio, (int)RecipColNo.DopeSplitRatio }; for (var a = 0; a < this.CurrentRecipe.Steps.Count; a++) { for (var i = 0; i < this.Columns.Count; i++) { if (this.Columns[i] is DoubleColumn) { var column = (DoubleColumn)this.Columns[i]; var maxValue = column.Maximun; var minValue = column.Minimun; if (maxValue != minValue) { double cValue = 0; if (!double.TryParse((this.CurrentRecipe.Steps[a][i] as DoubleParam).Value, out cValue)) { if ((this.CurrentRecipe.Steps[a][i] as DoubleParam).Value != "Hold") { lstNotCorrectInfo.Add($"Step {a + 1} {column.DisplayName}: value {(this.CurrentRecipe.Steps[a][i] as DoubleParam).Value} is incorrect numerical format "); (this.CurrentRecipe.Steps[a][i] as DoubleParam).Foreground = "Red"; } } else if (cValue > maxValue || cValue < minValue) { lstOutOfRange.Add($"Step {a + 1} {column.DisplayName}: value {cValue} is out of range {minValue}-{maxValue}"); (this.CurrentRecipe.Steps[a][i] as DoubleParam).Foreground = "Red"; } } } else if (lstSplitColums.Contains(i)) { double a1 = 0; double a2 = 0; double a3 = 0; var arrSplitRatio = (this.CurrentRecipe.Steps[a][i] as StringParam).Value.Split(':'); if (arrSplitRatio.Length != 3 || !double.TryParse(arrSplitRatio[0], out a1) || !double.TryParse(arrSplitRatio[1], out a2) || !double.TryParse(arrSplitRatio[2], out a3) || a1 <= 0 || a2 <= 0 || a3 <= 0) { lstNotCorrectInfo.Add($"(Step {i + 1}) {this.Columns[i].DisplayName}: value is not avalible!"); } } } } if (lstNotCorrectInfo.Count > 0 || lstOutOfRange.Count > 0) { var strInfo = ""; for (var i = 0; i < lstNotCorrectInfo.Count; i++) { strInfo += lstNotCorrectInfo[i] + "\r\n"; } for (var i = 0; i < lstOutOfRange.Count; i++) { strInfo += lstOutOfRange[i] + "\r\n"; } MessageBox.Show(strInfo); return false; } return true; } public void SaveRecipe() { //if (!CheckColumnDataAvalible()) //{ // return; //} //if (this.IsChanged) //{ this.Save(this.CurrentRecipe, false); //} } public void PopSetting(string controlName, Param paramData) { var stepNum = Convert.ToInt32(((StepParam)paramData.Parent[1]).Value); var dialog = new PublicPopSettingDialogViewModel(); dialog.DisplayName = paramData.DisplayName; var Parameters = new ObservableCollection(); Parameters = this.CurrentRecipe.PopSettingSteps[controlName][stepNum - 1]; var ControlParameters = new ObservableCollection(); var BrandParameters = new ObservableCollection(); foreach (var item in Parameters) { if (item.Name.Contains("Band")) { var name = item.Name.Replace("Wavelength", "").Replace("Bandwidth", ""); var displayName = item.DisplayName.Replace("Wavelength", "").Replace("Bandwidth", ""); if (BrandParameters.Where(x => x.Name == name).Count() == 0) { BrandParameters.Add(new BandParam() { Name = name, DisplayName = displayName }); } if (item.Name.Contains("Wavelength")) { BrandParameters.First(x => x.Name == name).WavelengthDoubleParam = item; } else if (item.Name.Contains("Bandwidth")) { BrandParameters.First(x => x.Name == name).BandwidthDoubleParam = item; } } else ControlParameters.Add(item); } dialog.Parameters = Parameters; dialog.ControlParameters = ControlParameters; dialog.BandParameters = BrandParameters; var wm = new WindowManager(); var bret = wm.ShowDialog(dialog); if ((bool)bret) { this.CurrentRecipe.PopSettingSteps[controlName][stepNum - 1] = dialog.Parameters; } } public bool Save(RecipeData recipe, bool createNew) { //保存时也要失去焦点计算一次 LostFocusFunc(); if (!CheckColumnDataAvalible()) { //return false; } var result = false; if (string.IsNullOrEmpty(recipe.Name)) { MessageBox.Show("Recipe name can't be empty"); return false; } recipe.Revisor = BaseApp.Instance.UserContext.LoginName; recipe.ReviseTime = DateTime.Now; result = this._recipeProvider.SaveRecipe(recipe.PrefixPath, recipe.Name, recipe.GetXmlString()); if (result) { recipe.DataSaved(); this.editMode = EditMode.Normal; this.UpdateView(); } else { MessageBox.Show("Save failed!"); } return result; } public void AddStep() { this.CurrentRecipe.Steps.Add(this.CurrentRecipe.CreateStep(this.Columns)); if (this.editMode != EditMode.New && this.editMode != EditMode.ReName) this.editMode = EditMode.Edit; var index = 1; foreach (var parameters in this.CurrentRecipe.Steps) { (parameters[1] as StepParam).Value = index.ToString(); index++; } this.UpdateView(); } public void AppendStep() { var index = -1; var found = false; for (var i = 0; i < this.CurrentRecipe.Steps.Count; i++) { if (this.CurrentRecipe.Steps[i][1] is StepParam && ((StepParam)this.CurrentRecipe.Steps[i][1]).Checked) { index = i; found = true; break; } } if (found) { if (this.editMode != EditMode.New && this.editMode != EditMode.ReName) this.editMode = EditMode.Edit; this.CurrentRecipe.Steps.Insert(index, this.CurrentRecipe.CreateStep(this.Columns)); index = 1; foreach (var parameters in this.CurrentRecipe.Steps) { (parameters[1] as StepParam).Value = index.ToString(); index++; } } } private ObservableCollection> copySteps = new ObservableCollection>(); private Dictionary>> popCopySteps = new Dictionary>>(); public void CopyStep() { this.copySteps.Clear(); this.popCopySteps.Clear(); for (var i = 0; i < this.CurrentRecipe.Steps.Count; i++) { if (this.CurrentRecipe.Steps[i][1] is StepParam && ((StepParam)this.CurrentRecipe.Steps[i][1]).Checked) { this.copySteps.Add(this.CurrentRecipe.CloneStep(this.Columns, this.CurrentRecipe.Steps[i])); } } CurrentRecipe.ValidLoopData(); } public void PasteFrontStep() { if (this.copySteps.Count > 0) { if (this.editMode != EditMode.New && this.editMode != EditMode.ReName) this.editMode = EditMode.Edit; for (var i = 0; i < this.CurrentRecipe.Steps.Count; i++) { if (this.CurrentRecipe.Steps[i][1] is StepParam && ((StepParam)this.CurrentRecipe.Steps[i][1]).Checked) { for (var copyindex = 0; copyindex < this.copySteps.Count; copyindex++) { this.CurrentRecipe.Steps.Insert(i, this.CurrentRecipe.CloneStep(this.Columns, this.copySteps[copyindex])); i++; } break; } } var index = 1; foreach (var parameters in this.CurrentRecipe.Steps) { (parameters[1] as StepParam).Value = index.ToString(); index++; } CurrentRecipe.ValidLoopData(); this.UpdateView(); } } public void PasteBackStep() { if (this.copySteps.Count > 0) { if (this.editMode != EditMode.New && this.editMode != EditMode.ReName) this.editMode = EditMode.Edit; for (var i = this.CurrentRecipe.Steps.Count - 1; i >= 0; i--) { if (this.CurrentRecipe.Steps[i][1] is StepParam && ((StepParam)this.CurrentRecipe.Steps[i][1]).Checked) { for (var copyindex = 0; copyindex < this.copySteps.Count; copyindex++) { this.CurrentRecipe.Steps.Insert(i + 1, this.CurrentRecipe.CloneStep(this.Columns, this.copySteps[copyindex])); i++; } break; } } var index = 1; foreach (var parameters in this.CurrentRecipe.Steps) { (parameters[1] as StepParam).Value = index.ToString(); index++; } CurrentRecipe.ValidLoopData(); this.UpdateView(); } } public void DeleteStep() { if (this.editMode != EditMode.New && this.editMode != EditMode.ReName) this.editMode = EditMode.Edit; var steps = this.CurrentRecipe.Steps.ToList(); for (var i = 0; i < steps.Count; i++) { if (steps[i][1] is StepParam && ((StepParam)steps[i][1]).Checked) { this.CurrentRecipe.Steps.Remove(steps[i]); } } var index = 1; foreach (var parameters in this.CurrentRecipe.Steps) { (parameters[1] as StepParam).Value = index.ToString(); index++; } } private TreeViewItem GetParentObjectEx(DependencyObject obj) where TreeViewItem : FrameworkElement { var parent = VisualTreeHelper.GetParent(obj); while (parent != null) { if (parent is TreeViewItem) { return (TreeViewItem)parent; } parent = VisualTreeHelper.GetParent(parent); } return null; } public void TreeRightMouseDown(MouseButtonEventArgs e) { var item = GetParentObjectEx(e.OriginalSource as DependencyObject) as TreeViewItem; if (item != null) { item.Focus(); } } #endregion private void ClearData() { this.editMode = EditMode.None; this.CurrentRecipe.Clear(); this.CurrentRecipe.Name = string.Empty; this.CurrentRecipe.Description = string.Empty; } private void LoadData(string prefixPath, string recipeName) { CurrentRecipe.Clear(); var recipeContent = _recipeProvider.LoadRecipe(prefixPath, recipeName); if (string.IsNullOrEmpty(recipeContent)) { MessageBox.Show($"{prefixPath}\\{recipeName} is empty, please confirm the file is valid."); return; } CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType; CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion; CurrentRecipe.InitData(prefixPath, recipeName, recipeContent, Columns, _columnBuilder.Configs, SelectedChamber); this.editMode = EditMode.Normal; } private void UpdateView() { var isFileSelected = CurrentFileNode != null && CurrentFileNode.IsFile; this.EnableNew = isFileSelected; this.EnableReName = isFileSelected; this.EnableCopy = isFileSelected; this.EnableDelete = isFileSelected; this.EnableSave = isFileSelected; this.EnableStep = isFileSelected; EnableSaveTo = isFileSelected; EnableSaveToAll = isFileSelected; if (this.editMode == EditMode.None) { this.EnableNew = true; this.EnableReName = false; this.EnableCopy = false; this.EnableDelete = false; this.EnableStep = false; this.EnableSave = false; } this.NotifyOfPropertyChange("EnableNew"); this.NotifyOfPropertyChange("EnableReName"); this.NotifyOfPropertyChange("EnableCopy"); this.NotifyOfPropertyChange("EnableDelete"); this.NotifyOfPropertyChange("EnableSave"); this.NotifyOfPropertyChange("EnableStep"); this.NotifyOfPropertyChange("EnableSaveTo"); this.NotifyOfPropertyChange("EnableSaveToAll"); this.NotifyOfPropertyChange("EnableReload"); this.NotifyOfPropertyChange("CurrentRecipe"); } private string _currentCriteria = String.Empty; public string CurrentCriteria { get { return _currentCriteria; } set { if (value == _currentCriteria) return; _currentCriteria = value; NotifyOfPropertyChange("CurrentCriteria"); ApplyFilter(); } } private void ApplyFilter() { ProcessTypeFileList[ProcessTypeIndexSelection].FilterFileListByProcessType = new ObservableCollection(ProcessTypeFileList[ProcessTypeIndexSelection].FileListByProcessType. Where(d => d.Name.IndexOf(CurrentCriteria, StringComparison.OrdinalIgnoreCase) >= 0)); } public void ClearFilter() { CurrentCriteria = ""; } } }