Sic.Framework/UIDebug/SicUI/Models/RecipeEditors/RecipeEditorViewModel.cs

2580 lines
102 KiB
C#
Raw Normal View History

using Aitex.Core.RT.Log;
using Caliburn.Micro;
using Caliburn.Micro.Core;
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 System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using MECF.Framework.UI.Client.CenterViews.Configs.Roles;
using MECF.Framework.UI.Client.CenterViews.Core;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using Sicentury.Core;
using Action = System.Action;
using DataGridCellEditEndingEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridCellEditEndingEventArgs;
using DataGridRowEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridRowEventArgs;
using Sicentury.Core.Collections;
namespace SicUI.Models.RecipeEditors
{
public class RecipeEditorViewModel : UiViewModelBase, IHandle<UserMode> //BaseModel
{
#region Variables
private readonly IEventAggregator _eventAggregator;
private RecipeData _currentRecipe;
private bool _isRunPurgeSwitch;
private bool _isTmaNotInstall;
private bool _isHClNotInstall;
private bool _isHClFlowNotInstall;
private bool _isLoading;
private Window _winValidationInfo;
private EditMode _editMode;
private int _errorsCount;
private readonly RecipeFormatBuilder _columnBuilder;
private readonly RecipeProvider _recipeProvider;
private ObservableCollection<ProcessTypeFileItem> _processTypeFileList;
private readonly ObservableRangeCollection<RecipeStepValidationInfo> _validationResultList;
private Point? _lastPosOfValidationWin = null;
#endregion
#region Constructors
public RecipeEditorViewModel()
{
_eventAggregator = IoC.Get<IEventAggregator>();
_eventAggregator?.Subscribe(this);
_columnBuilder = new RecipeFormatBuilder();
_recipeProvider = new RecipeProvider();
_validationResultList = new ObservableRangeCollection<RecipeStepValidationInfo>();
}
#endregion
#region Commands
private ICommand _RenameFolderCommand;
public ICommand RenameFolderCommand
{
get
{
if (_RenameFolderCommand == null)
_RenameFolderCommand = new BaseCommand(() => RenameFolder());
return _RenameFolderCommand;
}
}
private ICommand _DeleteFolderCommand;
public ICommand DeleteFolderCommand
{
get
{
if (_DeleteFolderCommand == null)
_DeleteFolderCommand = new BaseCommand(() => DeleteFolder());
return _DeleteFolderCommand;
}
}
private ICommand _NewFolderCommand;
public ICommand NewFolderCommand
{
get
{
if (_NewFolderCommand == null)
_NewFolderCommand = new BaseCommand(() => NewFolder());
return _NewFolderCommand;
}
}
private ICommand _NewFolderRootCommand;
public ICommand NewFolderRootCommand
{
get
{
if (_NewFolderRootCommand == null)
_NewFolderRootCommand = new BaseCommand(() => NewFolderRoot());
return _NewFolderRootCommand;
}
}
private ICommand _NewRecipeCommand;
public ICommand NewRecipeCommand
{
get
{
if (_NewRecipeCommand == null)
_NewRecipeCommand = new BaseCommand(() => NewRecipe());
return _NewRecipeCommand;
}
}
private ICommand _NewRecipeRootCommand;
public ICommand NewRecipeRootCommand
{
get
{
if (_NewRecipeRootCommand == null)
_NewRecipeRootCommand = new BaseCommand(() => NewRecipeRoot());
return _NewRecipeRootCommand;
}
}
private ICommand _RenameRecipeCommand;
public ICommand RenameRecipeCommand
{
get
{
if (_RenameRecipeCommand == null)
_RenameRecipeCommand = new BaseCommand(() => RenameRecipe());
return _RenameRecipeCommand;
}
}
private ICommand _DeleteRecipeCommand;
public ICommand DeleteRecipeCommand
{
get
{
if (_DeleteRecipeCommand == null)
_DeleteRecipeCommand = new BaseCommand(() => DeleteRecipe());
return _DeleteRecipeCommand;
}
}
private ICommand _SaveAsRecipeCommand;
public ICommand SaveAsRecipeCommand
{
get
{
if (_SaveAsRecipeCommand == null)
_SaveAsRecipeCommand = new BaseCommand(() => SaveAsRecipe());
return _SaveAsRecipeCommand;
}
}
#endregion
#region Properties
public ObservableCollection<ProcessTypeFileItem> ProcessTypeFileList
{
get => _processTypeFileList;
set
{
_processTypeFileList = value;
NotifyOfPropertyChange(nameof(ProcessTypeFileList));
}
}
public RecipeData CurrentRecipe
{
get => _currentRecipe;
set
{
_currentRecipe = value;
NotifyOfPropertyChange(nameof(CurrentRecipe));
}
}
public bool IsPermission => Permission == 3; //&& RtStatus != "AutoRunning";
public FileNode CurrentFileNode { get; set; }
private bool IsChanged => _editMode == EditMode.Edit || CurrentRecipe.IsChanged;
public ObservableCollection<EditorDataGridTemplateColumnBase> Columns { get; set; }
public Dictionary<string, ObservableCollection<EditorDataGridTemplateColumnBase>> 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; set; }
public bool EnableSaveToAll { get; set; }
public bool EnableSaveTo { get; set; }
public int ErrorsCount
{
set
{
// 如果错误数没发生变化不要修改Badge的值以免触发Badge动画。
if (value == _errorsCount)
return;
if(value == 0)
((RecipeEditorView)View).txtErrorCount.Badge = null;
else
((RecipeEditorView)View).txtErrorCount.Badge = value;
_errorsCount = value;
}
}
/// <summary>
/// 是否正在加载配方。
/// </summary>
public bool IsLoading
{
get => _isLoading;
private set
{
_isLoading = value;
NotifyOfPropertyChange(nameof(IsLoading));
}
}
public RecipeStepCollection SelectedRecipeSteps { get; set; }
public ICollectionView ParamValidationInfoCollection =>
CollectionViewSource.GetDefaultView(_validationResultList);
public ObservableCollection<string> ChamberType { get; set; }
public int ChamberTypeIndexSelection { get; set; }
public int ProcessTypeIndexSelection { get; set; }
public string CurrentChamberType => ChamberType[ChamberTypeIndexSelection];
public string CurrentProcessType => ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
public Visibility MultiChamberVisibility { get; set; }
public ObservableCollection<string> Chambers { get; set; }
public string SelectedChamber { get; set; }
public object View { get; set; }
#endregion
protected override void OnInitialize()
{
base.OnInitialize();
var chamberType = QueryDataClient.Instance.Service.GetConfig("System.Recipe.SupportedChamberType");
if (chamberType == null)
{
ChamberType = new ObservableCollection<string>() { "Default" };
}
else
{
ChamberType = new ObservableCollection<string>(((string)(chamberType)).Split(','));
}
ChamberTypeIndexSelection = 0;
var processType = "Process,Routine,Clean";
ProcessTypeFileList = new ObservableCollection<ProcessTypeFileItem>();
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<string, ObservableCollection<EditorDataGridTemplateColumnBase>>();
UpdateRecipeFormat();
}
protected override void OnActivate()
{
//初始化RoleManager
var roleManager = new RoleManager();
roleManager.Initialize();
//得到当前登录的RoleItem
var roleItem = roleManager.GetRoleByName(BaseApp.Instance.UserContext.RoleName);
var menuPermission = new MenuPermission();
menuPermission.ParsePermission(roleItem.Role.MenuPermission);
foreach (var col in Columns)
{
if (col.ControlName == "StepNo")
continue;
RecipeFormatBuilder.SetPermission(col, menuPermission);
}
//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 (IsChanged)
{
if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} content is changed, do you want to save it?") == DialogButton.Yes)
{
SaveRecipe();
}
}
_winValidationInfo?.Close();
// 锁定编辑器
((RecipeEditorView)View).editorLocker.Lock();
}
protected override void OnViewLoaded(object view)
{
View = view;
base.OnViewLoaded(view);
RecipeFormatBuilder.ApplyTemplate((UserControl)view, Columns);
var u = (RecipeEditorView)view;
u.dgCustom.Columns.Clear();
Columns.Apply((c) =>
{
c.Header = c;
u.dgCustom.Columns.Add(c);
});
u.dgCustom.ItemsSource = CurrentRecipe.Steps;
u.dgCustom.LostFocus += DgCustom_LostFocus;
u.dgCustom.CellEditEnding += DgCustomOnCellEditEnding;
u.dgCustom.LoadingRow += DgCustomOnLoadingRow;
u.dgCustom.FrozenColumnCount = 4;
}
private void DgCustomOnLoadingRow(object sender, DataGridRowEventArgs e)
{
if (sender is XDataGrid dg && e.Row.DataContext is RecipeStep step)
{
for (var i = 0; i < step.Count; i++)
{
step[i].RowOwner = e.Row;
step[i].ColumnOwner = dg.Columns[i];
}
}
}
private void DgCustomOnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
// ValidateEntireRecipe();
if (SelectedRecipeSteps != null && SelectedRecipeSteps.Count == 1)
{
CalRecipeParameterForRunVent(SelectedRecipeSteps[0]);
CheckIfAllCellsValid(out _);
}
}
private void DgCustom_LostFocus(object sender, RoutedEventArgs e)
{
// ValidateEntireRecipe();
if (SelectedRecipeSteps != null && SelectedRecipeSteps.Count == 1)
{
CalRecipeParameterForRunVent(SelectedRecipeSteps[0]);
CheckIfAllCellsValid(out _);
}
}
/// <summary>
/// 校验整个Recipe。
/// </summary>
private void ValidateEntireRecipe()
{
_isRunPurgeSwitch = false;
_isTmaNotInstall = false;
_isHClNotInstall = false;
_isHClFlowNotInstall = false;
CurrentRecipe.RecipeTotalTime = 0;
for (var selectIndex = 0; selectIndex < CurrentRecipe.Steps.Count; selectIndex++)
{
var previousIndex = selectIndex == 0 ? 0 : selectIndex - 1;
var currentIndex = selectIndex;
var nextIndex = selectIndex == CurrentRecipe.Steps.Count - 1 ? selectIndex : selectIndex + 1;
var previousStep = CurrentRecipe.Steps[previousIndex];
var currentStep = CurrentRecipe.Steps[currentIndex];
var nextStep = CurrentRecipe.Steps[nextIndex];
//判断每步与上一步值是否相同,不同则以颜色区分
RenderStepsWithSameValue(previousStep, currentStep);
//如果是Routine则在下面代码不执行在这里continue
if (CurrentProcessType == ProcessTypeFileList[1].ProcessType)
{
//如果是Routine,则总时间需*循环次数
var loopTimes = ((DoubleParam)currentStep[8]).Value <= 0
? 1
: (int)((DoubleParam)currentStep[8]).Value;
CurrentRecipe.RecipeTotalTime +=
(int)((DoubleParam)currentStep[(int)RecipColNo.StepTime]).Value * loopTimes;
continue;
}
else
{
//如果是Recipe则总时间直接加
CurrentRecipe.RecipeTotalTime +=
(int)((DoubleParam)currentStep[(int)RecipColNo.StepTime]).Value;
}
var stepTime = ((DoubleParam)currentStep[(int)RecipColNo.StepTime]).Value;
PurgeRunSwitch(previousStep, currentStep, nextStep);
NotInstallDevice(currentStep);
/*if (stepTime > 0)
{*/
CalRecipeParameterForRunVent(currentStep);
//}
// 校验单元格内容
foreach (var param in currentStep)
{
if (param is IParam p)
p.Validate();
}
}
//如果是Routine则在下面代码不执行在这里返回
if (CurrentProcessType == ProcessTypeFileList[1].ProcessType)
{
return;
}
if (_isRunPurgeSwitch)
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 static void RenderStepsWithSameValue(IReadOnlyList<Param> previousStep, IReadOnlyList<Param> currentStep)
{
for (var i = 0; i < currentStep.Count; i++)
{
if (currentStep[i] is DoubleParam dblParamCurr && previousStep[i] is DoubleParam dblParamPrev)
{
dblParamCurr.IsEqualsToPrevious =
GlobalDefs.IsDoubleValueEquals(dblParamCurr.Value, dblParamPrev.Value);
}
else if (currentStep[i] is ComboxParam cbxParamCurr && previousStep[i] is ComboxParam cbxParamPrev)
{
if (cbxParamCurr.Value != cbxParamPrev.Value)
{
cbxParamCurr.IsEqualsToPrevious = false;
}
else
{
cbxParamCurr.IsEqualsToPrevious = true;
}
}
}
}
[Obsolete("验证过程已在FlowModeParam.Validate()中实现,保留此函数仅做测试使用,后续版本将被删除。")]
private void PurgeRunSwitch(RecipeStep previousStep, RecipeStep currentStep,
RecipeStep 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<ComboxParam>()
{
previousSiH4FlowMode, previousTcsFlowMode, previousHClFlowMode,
previousC2H4FlowMode, previousN2FlowMode, previousN2FlowMode, previousN2HighFlowMode,
previousTmaFlowMode
};
var currentFlowMode = new List<ComboxParam>()
{
currentSiH4FlowMode, currentTcsFlowMode, currentHClFlowMode,
currentC2H4FlowMode, currentN2FlowMode, currentN2FlowMode, currentN2HighFlowMode, currentTmaFlowMode
};
var nextFlowMode = new List<ComboxParam>()
{
nextSiH4FlowMode, nextTcsFlowMode, nextHClFlowMode,
nextC2H4FlowMode, nextN2FlowMode, nextN2FlowMode, nextN2HighFlowMode, nextTmaFlowMode
};
for (var i = 0; i < currentFlowMode.Count; i++)
{
if (string.Equals(previousFlowMode[i].Value, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
&& string.Equals(currentFlowMode[i].Value, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase)
|| string.Equals(previousFlowMode[i].Value, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase)
&& string.Equals(currentFlowMode[i].Value, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase))
{
_isRunPurgeSwitch = true;
currentFlowMode[i].Value = FlowModeParam.FlowModeEnum.Vent.ToString();
}
if (string.Equals(nextFlowMode[i].Value, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
&& string.Equals(currentFlowMode[i].Value, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase)
|| string.Equals(nextFlowMode[i].Value, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase)
&& string.Equals(currentFlowMode[i].Value, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase))
{
_isRunPurgeSwitch = true;
currentFlowMode[i].Value = FlowModeParam.FlowModeEnum.Vent.ToString();
}
}
}
private void NotInstallDevice(RecipeStep currentStep)
{
if (!(currentStep[(int)RecipColNo.TMAFlowMode] is ComboxParam currentTmaFlowMode))
throw new InvalidOperationException(
$"The column {RecipColNo.TMAFlowMode} is not defined in recipe format.");
if (string.Equals(currentTmaFlowMode.Value,
FlowModeParam.FlowModeEnum.Purge.ToString(), StringComparison.OrdinalIgnoreCase))
return;
currentTmaFlowMode.Value = FlowModeParam.FlowModeEnum.Purge.ToString();
_isTmaNotInstall = true;
}
#region Gas Flow Calculation
private static double GetTcsFlow(RecipeStep currentStep, string selectedChamber)
{
var currentTcsBubbleLowFlow = ((DoubleParam)currentStep[(int)RecipColNo.TCSBubbLowFlow]).Value;
var currentTcsBubbleHighFlow = ((DoubleParam)currentStep[(int)RecipColNo.TCSBubbHighFlow]).Value;
var currentTcsPushFlow = ((DoubleParam)currentStep[(int)RecipColNo.TCSPushFlow]).Value;
var currentTcsFlowMode = (currentStep[(int)RecipColNo.TCSFlowMode] as ComboxParam)?.Value;
var tcsFlow = 0d;
var tcsEff =
(double)QueryDataClient.Instance.Service.GetConfig($"PM.{selectedChamber}.Efficiency.TCS-Eff");
if (string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
|| string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Vent.ToString(),
StringComparison.OrdinalIgnoreCase))
{
tcsFlow = (currentTcsBubbleHighFlow + currentTcsBubbleLowFlow) * tcsEff + currentTcsPushFlow;
}
else if (string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase))
{
tcsFlow = currentTcsBubbleHighFlow + currentTcsBubbleLowFlow + currentTcsPushFlow;
}
return tcsFlow;
}
private static double GetTmaFlow(RecipeStep currentStep, string selectedChamber)
{
var currentTmaBubbleFlow = ((DoubleParam)currentStep[(int)RecipColNo.TMABubbFlow]).Value;
var currentTmaPushFlow = ((DoubleParam)currentStep[(int)RecipColNo.TMAPushFlow]).Value;
var currentTmaFlowMode = ((ComboxParam)currentStep[(int)RecipColNo.TMAFlowMode]).Value;
var tmaFlow = 0d;
var tmaEff =
(double)QueryDataClient.Instance.Service.GetConfig($"PM.{selectedChamber}.Efficiency.TMA-Eff");
if (string.Equals(currentTmaFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
|| string.Equals(currentTmaFlowMode, FlowModeParam.FlowModeEnum.Vent.ToString(),
StringComparison.OrdinalIgnoreCase))
tmaFlow = currentTmaBubbleFlow * tmaEff + currentTmaPushFlow;
else if (string.Equals(currentTmaFlowMode, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase))
tmaFlow = currentTmaBubbleFlow + currentTmaPushFlow;
return tmaFlow;
}
private static void CalSiSourceFlow(RecipeStep currentStep)
{
var currentSiSourSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.SiSourceSplitRatio] is Sets3RatioParam siSourceRatio)
currentSiSourSplitRatio = siSourceRatio.Ratio;
var currentSiSourTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.SiSourceTotalFlow]).Value;
//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];
}
((DoubleParam)currentStep[(int)RecipColNo.SiSourceInnerFlow]).Value = Math.Round(currentSiSourInnerFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SiSourceMiddleFlow]).Value = Math.Round(currentSiSourMidFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SiSourceOuterFlow]).Value = Math.Round(currentSiSourOutFlow, 1);
}
private static void CalCSourceFlow(RecipeStep currentStep)
{
var currentC2H4Flow = ((DoubleParam)currentStep[(int)RecipColNo.C2H4Flow]).Value;
var currentCSourSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.CSourceSplitRatio] is Sets3RatioParam cSourceRatio)
currentCSourSplitRatio = cSourceRatio.Ratio;
var currentCSourTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.CSourceTotalFlow]).Value;
var currentC2H4FlowMode = (currentStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam)?.Value;
var currentCSourPushFlow = currentCSourTotalFlow -
(string.Equals(currentC2H4FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
? currentC2H4Flow
: 0d);
((DoubleParam)currentStep[(int)RecipColNo.CSourceOuterFlow]).Value = Math.Round(currentCSourPushFlow, 1);
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];
}
((DoubleParam)currentStep[(int)RecipColNo.CSourceInnerFlow]).Value = Math.Round(currentCSourInnerFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.CSourceMiddleFlow]).Value = Math.Round(currentCSourMidFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.CSourceOuterFlow]).Value = Math.Round(currentCSourOuterFlow, 1);
}
private static void CalDopeFlow(RecipeStep currentStep)
{
var currentDopeTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.DopeTotalFlow]).Value;
var currentN2ActualFlow = ((DoubleParam)currentStep[(int)RecipColNo.N2ActualFlow]).Value;
var currentN2LowFlow = ((DoubleParam)currentStep[(int)RecipColNo.N2LowFlow]).Value;
var currentDiluFlowForN2 = ((DoubleParam)currentStep[(int)RecipColNo.DilutFlowForN2]).Value;
var currentDilutedN2Flow =
(currentN2LowFlow + currentDiluFlowForN2) * currentN2ActualFlow / currentN2LowFlow;
((DoubleParam)currentStep[(int)RecipColNo.DilutedN2Flow]).Value = Math.Round(currentDilutedN2Flow, 1);
var currentDopeSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.DopeSplitRatio] is Sets3RatioParam dopRatio)
currentDopeSplitRatio = dopRatio.Ratio;
//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];
}
((DoubleParam)currentStep[(int)RecipColNo.DopeInnerFlow]).Value = Math.Round(currentDopeInnerFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.DopeMiddleFlow]).Value = Math.Round(currentDopeMidFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.DopeOuterFlow]).Value = Math.Round(currentDopeOuterFlow, 1);
}
private static void CalShSupplementFlow(RecipeStep currentStep)
{
var currentSiSourTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.SiSourceTotalFlow]).Value;
var currentCSourTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.CSourceTotalFlow]).Value;
var currentDopeTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.DopeTotalFlow]).Value;
var currentShTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.SHTotalFlow]).Value;
var currentSurroundingFlow = ((DoubleParam)currentStep[(int)RecipColNo.CarrayGasFlow]).Value;
var currentSiSourInnerFlow = ((DoubleParam)currentStep[(int)RecipColNo.SiSourceInnerFlow]).Value;
var currentSiSourMidFlow = ((DoubleParam)currentStep[(int)RecipColNo.SiSourceMiddleFlow]).Value;
var currentCSourInnerFlow = ((DoubleParam)currentStep[(int)RecipColNo.CSourceInnerFlow]).Value;
var currentCSourMidFlow = ((DoubleParam)currentStep[(int)RecipColNo.CSourceMiddleFlow]).Value;
var currentDopeInnerFlow = ((DoubleParam)currentStep[(int)RecipColNo.DopeInnerFlow]).Value;
var currentDopeMidFlow = ((DoubleParam)currentStep[(int)RecipColNo.DopeMiddleFlow]).Value;
var currentShTotalFlowSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.SHTotalFlowSplitRatio] is Sets3RatioParam shTotalRatio)
currentShTotalFlowSplitRatio = shTotalRatio.Ratio;
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;
((DoubleParam)currentStep[(int)RecipColNo.SHPushTotalFlow]).Value = Math.Round(currentShSuppTotalFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SHInnerFlow]).Value = Math.Round(currentShInnerFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SHMiddleFlow]).Value = Math.Round(currentShMidFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SHOuterFlow]).Value = Math.Round(currentShOutterFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.InnerPushFlow]).Value = Math.Round(currentInnerSuppFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.MiddlePushFlow]).Value = Math.Round(currentMidSuppFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.OuterPushFlow]).Value = Math.Round(currentOutterSuppFlow, 1);
}
private static void CalVentFlow(RecipeStep currentStep, string selectedChamber)
{
var currentSiH4Flow = ((DoubleParam)currentStep[(int)RecipColNo.SiH4Flow]).Value;
var currentTotalVentFlow = ((DoubleParam)currentStep[(int)RecipColNo.TotalVentFlow]).Value;
var currentHClFlow = ((DoubleParam)currentStep[(int)RecipColNo.HCLFlow]).Value;
var currentN2HighFlow = ((DoubleParam)currentStep[(int)RecipColNo.N2HighFlow]).Value;
var currentC2H4Flow = ((DoubleParam)currentStep[(int)RecipColNo.C2H4Flow]).Value;
var currentDilutedN2Flow = ((DoubleParam)currentStep[(int)RecipColNo.DilutedN2Flow]).Value;
var currentN2FlowMode = ((ComboxParam)currentStep[(int)RecipColNo.N2FlowMode]).Value;
var currentN2HighFlowMode = ((ComboxParam)currentStep[(int)RecipColNo.N2HighFlowMode]).Value;
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;
var currentC2H4FlowMode = (currentStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam)?.Value;
var currentTmaFlowMode = ((ComboxParam)currentStep[(int)RecipColNo.TMAFlowMode]).Value;
var tcsFlow = GetTcsFlow(currentStep, selectedChamber);
var tmaFlow = GetTmaFlow(currentStep, selectedChamber);
var currentSiSourTotalFlowForPurge =
(string.Equals(currentSiH4FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentSiH4Flow
: 0d)
+ (string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? tcsFlow
: 0d) +
(string.Equals(currentHClFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentHClFlow
: 0d);
var currentCSourTotalFlowForPurge =
string.Equals(currentC2H4FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentC2H4Flow
: 0d;
var n2Flow = (string.Equals(currentN2FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentDilutedN2Flow
: 0d) +
(string.Equals(currentN2HighFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentN2HighFlow
: 0d);
var currentDopeTotalFlowForPurge = n2Flow + (string.Equals(currentTmaFlowMode,
FlowModeParam.FlowModeEnum.Run.ToString(), StringComparison.OrdinalIgnoreCase) == false
? tmaFlow
: 0d);
var currentVentPushFlow = currentTotalVentFlow - currentSiSourTotalFlowForPurge -
currentCSourTotalFlowForPurge - currentDopeTotalFlowForPurge;
((DoubleParam)currentStep[(int)RecipColNo.VentPushFlow]).Value = Math.Round(currentVentPushFlow, 1);
}
private void CalRecipeParameterForRunVent(RecipeStep currentStep)
{
/*if (!(currentStep.StepTime > 0))
return;*/
CalSiSourceFlow(currentStep);
CalCSourceFlow(currentStep);
CalDopeFlow(currentStep);
CalShSupplementFlow(currentStep);
CalVentFlow(currentStep, CurrentRecipe.Module);
}
[Obsolete("该函数已废除,请使用分步计算函数。")]
private void CalRecipeParameterForRunVent(RecipeStep previousStep,
IReadOnlyList<Param> currentStep, double stepTime)
{
#region SH Total flow
var currentShTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.SHTotalFlow]).Value;
var currentSurroundingFlow = ((DoubleParam)currentStep[(int)RecipColNo.CarrayGasFlow]).Value;
var currentShTotalFlowSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.SHTotalFlowSplitRatio] is Sets3RatioParam shTotalRatio)
currentShTotalFlowSplitRatio = shTotalRatio.Ratio;
#endregion
#region Si Source
var currentSiH4Flow = ((DoubleParam)currentStep[(int)RecipColNo.SiH4Flow]).Value;
var currentTcsBubbleLowFlow = ((DoubleParam)currentStep[(int)RecipColNo.TCSBubbLowFlow]).Value;
var currentTcsBubbleHighFlow = ((DoubleParam)currentStep[(int)RecipColNo.TCSBubbHighFlow]).Value;
var currentTcsPushFlow = ((DoubleParam)currentStep[(int)RecipColNo.TCSPushFlow]).Value;
var currentHClFlow = ((DoubleParam)currentStep[(int)RecipColNo.HCLFlow]).Value;
var currentSiSourSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.SiSourceSplitRatio] is Sets3RatioParam siSourceRatio)
currentSiSourSplitRatio = siSourceRatio.Ratio;
var currentSiSourTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.SiSourceTotalFlow]).Value;
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 = 0d;
var tcsEff =
(double)QueryDataClient.Instance.Service.GetConfig($"PM.{SelectedChamber}.Efficiency.TCS-Eff");
if (string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
|| string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Vent.ToString(),
StringComparison.OrdinalIgnoreCase))
{
tcsFlow = (currentTcsBubbleHighFlow + currentTcsBubbleLowFlow) * tcsEff + currentTcsPushFlow;
}
else if (string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase))
{
tcsFlow = currentTcsBubbleHighFlow + currentTcsBubbleLowFlow + 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];
}
((DoubleParam)currentStep[(int)RecipColNo.SiSourceInnerFlow]).Value = Math.Round(currentSiSourInnerFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SiSourceMiddleFlow]).Value = Math.Round(currentSiSourMidFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.SiSourceOuterFlow]).Value = Math.Round(currentSiSourOutFlow, 1);
#endregion
#region C Source
var currentC2H4Flow = ((DoubleParam)currentStep[(int)RecipColNo.C2H4Flow]).Value;
var currentCSourSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.CSourceSplitRatio] is Sets3RatioParam cSourceRatio)
currentCSourSplitRatio = cSourceRatio.Ratio;
var currentCSourTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.CSourceTotalFlow]).Value;
var currentC2H4FlowMode = (currentStep[(int)RecipColNo.C2H4FlowMode] as ComboxParam)?.Value;
var currentCSourPushFlow = currentCSourTotalFlow -
(string.Equals(currentC2H4FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
? currentC2H4Flow
: 0d);
((DoubleParam)currentStep[(int)RecipColNo.CSourceOuterFlow]).Value = Math.Round(currentCSourPushFlow, 1);
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];
}
((DoubleParam)currentStep[(int)RecipColNo.CSourceInnerFlow]).Value = Math.Round(currentCSourInnerFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.CSourceMiddleFlow]).Value = Math.Round(currentCSourMidFlow, 1);
((DoubleParam)currentStep[(int)RecipColNo.CSourceOuterFlow]).Value = Math.Round(currentCSourOuterFlow, 1);
#endregion
#region Dope
var currentDopeTotalFlow = ((DoubleParam)currentStep[(int)RecipColNo.DopeTotalFlow]).Value;
var currentN2ActualFlow = ((DoubleParam)currentStep[(int)RecipColNo.N2ActualFlow]).Value;
var currentN2LowFlow = ((DoubleParam)currentStep[(int)RecipColNo.N2LowFlow]).Value;
var currentDiluFlowForN2 = ((DoubleParam)currentStep[(int)RecipColNo.DilutFlowForN2]).Value;
var currentN2HighFlow = ((DoubleParam)currentStep[(int)RecipColNo.N2HighFlow]).Value;
var currentTmaBubbFlow = ((DoubleParam)currentStep[(int)RecipColNo.TMABubbFlow]).Value;
var currentTmaPushFlow = ((DoubleParam)currentStep[(int)RecipColNo.TMAPushFlow]).Value;
var currentDilutedN2Flow =
(currentN2LowFlow + currentDiluFlowForN2) * currentN2ActualFlow / currentN2LowFlow;
((DoubleParam)currentStep[(int)RecipColNo.DilutedN2Flow]).Value = Math.Round(currentDilutedN2Flow, 1);
var currentDopeSplitRatio = new double[3];
if (currentStep[(int)RecipColNo.DopeSplitRatio] is Sets3RatioParam dopRatio)
currentDopeSplitRatio = dopRatio.Ratio;
var currentN2FlowMode = ((ComboxParam)currentStep[(int)RecipColNo.N2FlowMode]).Value;
var currentN2HighFlowMode = ((ComboxParam)currentStep[(int)RecipColNo.N2HighFlowMode]).Value;
var currentTmaFlowMode = ((ComboxParam)currentStep[(int)RecipColNo.TMAFlowMode]).Value;
double tmaFlow = 0;
var tmaEff =
(double)QueryDataClient.Instance.Service.GetConfig($"PM.{SelectedChamber}.Efficiency.TMA-Eff");
if (string.Equals(currentTmaFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase)
|| string.Equals(currentTmaFlowMode, FlowModeParam.FlowModeEnum.Vent.ToString(),
StringComparison.OrdinalIgnoreCase))
tmaFlow = currentTmaBubbFlow * tmaEff + currentTmaPushFlow;
else if (string.Equals(currentTmaFlowMode, FlowModeParam.FlowModeEnum.Purge.ToString(),
StringComparison.OrdinalIgnoreCase))
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 = Math.Round(currentDopeInnerFlow, 1);
(currentStep[(int)RecipColNo.DopeMiddleFlow] as DoubleParam).Value = Math.Round(currentDopeMidFlow, 1);
(currentStep[(int)RecipColNo.DopeOuterFlow] as DoubleParam).Value = Math.Round(currentDopeOuterFlow, 1);
#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 = Math.Round(currentShSuppTotalFlow, 1);
(currentStep[(int)RecipColNo.SHInnerFlow] as DoubleParam).Value = Math.Round(currentShInnerFlow, 1);
(currentStep[(int)RecipColNo.SHMiddleFlow] as DoubleParam).Value = Math.Round(currentShMidFlow, 1);
(currentStep[(int)RecipColNo.SHOuterFlow] as DoubleParam).Value = Math.Round(currentShOutterFlow, 1);
(currentStep[(int)RecipColNo.InnerPushFlow] as DoubleParam).Value = Math.Round(currentInnerSuppFlow, 1);
(currentStep[(int)RecipColNo.MiddlePushFlow] as DoubleParam).Value = Math.Round(currentMidSuppFlow, 1);
(currentStep[(int)RecipColNo.OuterPushFlow] as DoubleParam).Value = Math.Round(currentOutterSuppFlow, 1);
#endregion
#region Vent Flow
var currentTotalVentFlow = (currentStep[(int)RecipColNo.TotalVentFlow] as DoubleParam).Value;
var currentSiSourTotalFlowForPurge =
(string.Equals(currentSiH4FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentSiH4Flow
: 0d)
+ (string.Equals(currentTcsFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? tcsFlow
: 0d) +
(string.Equals(currentHClFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentHClFlow
: 0d);
var currentCSourTotalFlowForPurge =
string.Equals(currentC2H4FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentC2H4Flow
: 0d;
var n2Flow = (string.Equals(currentN2FlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentDilutedN2Flow
: 0d) +
(string.Equals(currentN2HighFlowMode, FlowModeParam.FlowModeEnum.Run.ToString(),
StringComparison.OrdinalIgnoreCase) == false
? currentN2HighFlow
: 0d);
var currentDopeTotalFlowForPurge = n2Flow + (string.Equals(currentTmaFlowMode,
FlowModeParam.FlowModeEnum.Run.ToString(), StringComparison.OrdinalIgnoreCase) == false
? tmaFlow
: 0d);
var currentVentPushFlow = currentTotalVentFlow - currentSiSourTotalFlowForPurge -
currentCSourTotalFlowForPurge - currentDopeTotalFlowForPurge;
(currentStep[(int)RecipColNo.VentPushFlow] as DoubleParam).Value = Math.Round(currentVentPushFlow, 1);
#endregion
}
#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>(((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<string>(new string[] { "PM1" });
}
SelectedChamber = Chambers[0];
if (DicColunms.Keys.Contains(CurrentProcessType))
{
Columns = DicColunms[CurrentProcessType];
}
else
{
Columns = _columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true,
BaseApp.Instance.UserContext.RoleName);
DicColunms[CurrentProcessType] = Columns;
}
CurrentRecipe = new RecipeData();
CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType;
CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion;
_editMode = EditMode.None;
MultiChamberVisibility = Chambers.Count > 1 ? Visibility.Visible : Visibility.Collapsed;
}
/// <summary>
/// Chamber变更时重新加载Recipe。
/// </summary>
public async 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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
IsLoading = true;
await CurrentRecipe.ChangeChamber(Columns
, _columnBuilder.Configs, SelectedChamber, GetLoadingDispatcher());
IsLoading = false;
}
/// <summary>
/// 左侧Recipe列表选择改变时重新加载Recipe。
/// </summary>
/// <param name="node"></param>
public async 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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
CurrentFileNode = node;
if (node != null && node.IsFile)
{
await LoadData(node.PrefixPath, node.FullPath);
}
else
{
ClearData();
_editMode = EditMode.None;
}
UpdateView();
}
#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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(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)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(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 async void ReloadRecipe()
{
if (_editMode == EditMode.Normal || _editMode == EditMode.Edit)
{
if (CurrentRecipe == null)
return;
if (CurrentRecipe.IsChanged)
{
var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} has changed, discard and reload?");
if (ret == DialogButton.No)
return;
}
await LoadData(CurrentRecipe.PrefixPath, CurrentRecipe.Name);
UpdateView();
ValidateEntireRecipe();
// ValidateEntireRecipe()会重新计算比例相关的值导致IsChanged == true, 刷新一下。
foreach (var step in CurrentRecipe.Steps)
step.Save();
}
}
public void SaveToAll()
{
ValidateEntireRecipe();
if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
{
DialogBox.ShowWarning($"Saving 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 chambers? \r\n This will replace all the other chambers recipe content");
//if (selection == DialogButton.No)
// return;
CurrentRecipe.SaveTo(Chambers.ToArray());
Save(CurrentRecipe, false);
}
public void SaveTo()
{
ValidateEntireRecipe();
if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
{
DialogBox.ShowWarning($"Saving failed, {CurrentRecipe.Name} is not a valid recipe file.");
return;
}
var dialog =
new SaveToDialogViewModel("Select the 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<string>();
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(CurrentRecipe, false);
}
#endregion
#region Steps
/// <summary>
/// 展开DataGrid列组。
/// </summary>
/// <param name="col"></param>
public void ExpandColumnsGroup(ExpanderColumn col)
{
foreach (var subCol in col.ChildColumns)
{
subCol.UpdateVisibility();
}
}
/// <summary>
/// 折叠DataGrid列组。
/// </summary>
/// <param name="col"></param>
public void CollapseColumnsGroup(ExpanderColumn col)
{
foreach (var subCol in col.ChildColumns)
{
subCol.Visibility = Visibility.Collapsed;
}
}
/// <summary>
/// 检查是否所有的参数均有效。
/// </summary>
/// <returns></returns>
private bool CheckIfAllCellsValid(out string errorMessage)
{
errorMessage = string.Empty;
if (CurrentProcessType == ProcessTypeFileList[1].ProcessType)
{
return true;
}
/*var lstError = new List<string>();*/
_validationResultList.Clear();
for (var a = 0; a < CurrentRecipe.Steps.Count; a++)
{
var invalidParam =
CurrentRecipe.Steps[a]
.Where(x => x is IParam p && p.IsValidated == false).ToList();
_validationResultList.AddRange(invalidParam.Cast<IParam>()
.Select(x => new RecipeStepValidationInfo(
_currentRecipe.Steps[a],
x,
x.ValidationError)));
}
if (_validationResultList.Count <= 0)
{
ErrorsCount = 0;
return true;
}
ErrorsCount = _validationResultList.Count;
var errCnt = 0;
var strInfo = new StringBuilder();
foreach (var t in _validationResultList)
{
strInfo.AppendLine(t.ToString());
errCnt++;
if (errCnt > 10)
break;
}
if (errCnt < _validationResultList.Count)
strInfo.AppendLine("\r\n<please check for more errors...>");
// MessageBox.Show(strInfo.ToString(), "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
errorMessage = strInfo.ToString();
return false;
}
public void SaveRecipe()
{
/*if (Save(CurrentRecipe, false))
MessageBox.Show($"The recipe has been saved successfully!", "Succeeded", MessageBoxButton.OK,
MessageBoxImage.Information);*/
Save(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 RecipeStep(null);
Parameters = CurrentRecipe.PopSettingSteps[controlName][stepNum - 1];
var ControlParameters = new RecipeStep(null);
var BrandParameters = new ObservableCollection<BandParam>();
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 (bret == true)
{
CurrentRecipe.PopSettingSteps[controlName][stepNum - 1] = dialog.Parameters;
}
}
private bool Save(RecipeData recipe, bool createNew)
{
if (string.IsNullOrEmpty(recipe.Name))
{
MessageBox.Show("Recipe name can't be empty", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
ValidateEntireRecipe();
if (CheckIfAllCellsValid(out var errors) == false)
{
var mbr = MessageBox.Show($"{errors}\r\n Are you sure to continue to save?", "Warning",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (mbr == MessageBoxResult.No)
return false;
}
var result = false;
recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
recipe.ReviseTime = DateTime.Now;
result = _recipeProvider.WriteRecipeFile(recipe.PrefixPath, recipe.Name, recipe.GetXmlString());
if (result)
{
recipe.DataSaved();
_editMode = EditMode.Normal;
UpdateView();
}
else
{
MessageBox.Show("Save failed!");
}
return result;
}
public void AddStep()
{
Columns.Clear();
//add时重新从配方中加载数据
Columns = _columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true, BaseApp.Instance.UserContext.RoleName);
CurrentRecipe.Steps.Add(CurrentRecipe.CreateStep(Columns));
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
ValidateEntireRecipe();
CheckIfAllCellsValid(out _);
UpdateView();
}
public void InsertStepToLeft()
{
if (SelectedRecipeSteps.Count != 1)
return;
var stepNo = SelectedRecipeSteps[0]?.StepNo;
if (RecipeStep.ValidateStepNo(stepNo, out var vStepNo) == false)
return;
var index = vStepNo - RecipeStep.START_INDEX;
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
CurrentRecipe.Steps.Insert(index, CurrentRecipe.CreateStep(Columns));
}
public void InsertStepToRight()
{
if (SelectedRecipeSteps.Count != 1)
return;
var stepNo = SelectedRecipeSteps[0]?.StepNo;
if (RecipeStep.ValidateStepNo(stepNo, out var vStepNo) == false)
return;
var index = vStepNo - RecipeStep.START_INDEX;
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
if (index < _currentRecipe.Steps.Count - 1)
CurrentRecipe.Steps.Insert(index + 1, CurrentRecipe.CreateStep(Columns));
else
CurrentRecipe.Steps.Add(CurrentRecipe.CreateStep(Columns));
}
private readonly RecipeStepCollection _copySteps =
new RecipeStepCollection();
private readonly Dictionary<string, ObservableCollection<RecipeStep>> _popCopySteps =
new Dictionary<string, ObservableCollection<RecipeStep>>();
/// <summary>
/// 拷贝所有选中的Steps。
/// </summary>
/// <param name="steps"></param>
public void CopyStep()
{
_copySteps.Clear();
_popCopySteps.Clear();
if (SelectedRecipeSteps == null || SelectedRecipeSteps.Count <= 0)
return;
foreach (var step in SelectedRecipeSteps)
_copySteps.Add(_currentRecipe.CloneStep(Columns, step));
CurrentRecipe.ValidLoopData();
}
/// <summary>
/// 在左侧粘贴Step。
/// </summary>
public async void PasteStepToLeft()
{
await Paste(true);
}
/// <summary>
/// 在右侧粘贴Step。
/// </summary>
public async void PasteStepToRight()
{
await Paste(false);
}
/// <summary>
/// 粘贴Step。
/// <para>注意async Task 标记的方法不能直接应用于Caliburn.Message.Attach 方法。</para>
/// </summary>
/// <param name="isToLeft">是否粘贴到当前选中的Step的左侧。</param>
/// <returns></returns>
private async Task Paste(bool isToLeft)
{
if (SelectedRecipeSteps == null || SelectedRecipeSteps.Count != 1)
return;
if (_copySteps.Count <= 0)
return;
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
var stepNo = SelectedRecipeSteps[0].StepNo;
if (RecipeStep.ValidateStepNo(stepNo, out var vStepNo) == false)
return;
foreach (var t in _copySteps)
{
var cloned = CurrentRecipe.CloneStep(Columns, t);
var pos = isToLeft ? vStepNo - RecipeStep.START_INDEX : vStepNo;
await Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(Action)(() => { CurrentRecipe.Steps.Insert(pos, cloned); }));
}
ValidateEntireRecipe();
CurrentRecipe.ValidLoopData();
CheckIfAllCellsValid(out _);
UpdateView();
}
/// <summary>
/// 在左侧复制Step。
/// </summary>
public async void DuplicateStepToLeft()
{
await Duplicate(true);
}
/// <summary>
/// 在右侧复制Step。
/// </summary>
public async void DuplicateStepToRight()
{
await Duplicate(false);
}
/// <summary>
/// 复制选中的Step。
/// </summary>
/// <param name="isToLeft">是否复制到选中的Step的左侧。</param>
private async Task Duplicate(bool isToLeft)
{
if (SelectedRecipeSteps == null || SelectedRecipeSteps.Count != 1)
return;
CopyStep();
await Paste(isToLeft);
}
/// <summary>
/// 删除步骤。
/// </summary>
public void DeleteStep()
{
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
for (var i = SelectedRecipeSteps.Count - 1; i >= 0; i--)
{
_currentRecipe.Steps.Remove(SelectedRecipeSteps[i]);
}
CheckIfAllCellsValid(out _);
}
private void CreateValidationDetailWindow()
{
_winValidationInfo = new RecipeEditorValidationDetailWindow(_validationResultList, _lastPosOfValidationWin);
_winValidationInfo.DataContext = this;
_winValidationInfo.Closed += (sender, args) =>
{
_lastPosOfValidationWin = new Point(_winValidationInfo.Left, _winValidationInfo.Top);
ResetHighlight();
};
}
public void ShowValidationDetailWindow()
{
ValidateEntireRecipe();
CheckIfAllCellsValid(out _);
if (Application.Current.Windows.OfType<RecipeEditorValidationDetailWindow>().Any())
_winValidationInfo.Activate();
else
{
CreateValidationDetailWindow();
_winValidationInfo.Show();
}
}
public void ShowHideParamValues()
{
CurrentRecipe.Steps.IsHideValue = !CurrentRecipe.Steps.IsHideValue;
}
private TreeViewItem GetParentObjectEx<TreeViewItem>(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<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
if (item != null)
{
item.Focus();
}
}
/// <summary>
/// 在DataGrid中聚焦并高亮指定的参数。
/// </summary>
/// <param name="param"></param>
public void FocusToParam(IParam param)
{
if (param == null)
return;
ResetHighlight();
var row = param.RowOwner;
var col = param.ColumnOwner;
if (row != null && col != null)
{
((RecipeEditorView)View).dgCustom.ScrollIntoView(param.Parent, col);
param.Highlight();
}
}
public void ResetHighlight()
{
_currentRecipe.Steps.ResetHighlight();
}
#endregion
private void ClearData()
{
_editMode = EditMode.None;
CurrentRecipe.Clear();
CurrentRecipe.Name = string.Empty;
CurrentRecipe.Description = string.Empty;
}
/// <summary>
/// 根据配置获取Cascade方式呈现Recipe的Dispatcher或返回null。
/// </summary>
/// <returns></returns>
private static Dispatcher GetLoadingDispatcher()
{
// 判断Step呈现方式
var isCascadeLoading = (bool)QueryDataClient.Instance.Service.GetConfig("System.Recipe.CascadeLoading");
var dispatcher = isCascadeLoading ? Dispatcher.CurrentDispatcher : null;
return dispatcher;
}
/// <summary>
/// 从配置文件中获取是否加载Recipe后自动隐藏参数值的配置。
/// </summary>
/// <returns></returns>
private static bool GetIsHideValueAfterLoading()
{
try
{
return false;
//return (bool)QueryDataClient.Instance.Service.GetConfig("System.Recipe.HideParamValueAfterLoading");
}
catch
{
// 如果没有找到参数,默认隐藏参数值。
return false;
}
}
private async Task LoadData(string prefixPath, string recipeName)
{
var dg = ((RecipeEditorView)View).dgCustom;
IsLoading = true;
CurrentRecipe.Clear();
CurrentRecipe.Steps.Save(); // 重置为已保存状态
ErrorsCount = 0;
var recipeContent = _recipeProvider.ReadRecipeFile(prefixPath, recipeName);
if (string.IsNullOrEmpty(recipeContent))
{
MessageBox.Show($"{prefixPath}\\{recipeName} is empty, please confirm the file is valid.",
"Error",
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType;
CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion;
CurrentRecipe.Steps.IsHideValue = GetIsHideValueAfterLoading();
await CurrentRecipe.InitData(prefixPath, recipeName, recipeContent, Columns,
_columnBuilder.Configs, SelectedChamber, GetLoadingDispatcher());
_editMode = EditMode.Normal;
IsLoading = false;
// 如果当前Recipe非本机Recipe比例相关的参数可能由于算法不同导致结果不同
// 重新计算一次Recipe以更新比例计算值并校验其合法性。
ValidateEntireRecipe();
CheckIfAllCellsValid(out var _);
}
private void UpdateView()
{
var isFileSelected = CurrentFileNode != null && CurrentFileNode.IsFile;
EnableNew = isFileSelected;
EnableReName = isFileSelected;
EnableCopy = isFileSelected;
EnableDelete = isFileSelected;
EnableSave = isFileSelected;
EnableStep = isFileSelected;
EnableSaveTo = isFileSelected;
EnableSaveToAll = isFileSelected;
if (_editMode == EditMode.None)
{
EnableNew = true;
EnableReName = false;
EnableCopy = false;
EnableDelete = false;
EnableStep = false;
EnableSave = false;
EnableReload = true;
}
NotifyOfPropertyChange(nameof(EnableNew));
NotifyOfPropertyChange(nameof(EnableReName));
NotifyOfPropertyChange(nameof(EnableCopy));
NotifyOfPropertyChange(nameof(EnableDelete));
NotifyOfPropertyChange(nameof(EnableSave));
NotifyOfPropertyChange(nameof(EnableStep));
NotifyOfPropertyChange(nameof(EnableSaveTo));
NotifyOfPropertyChange(nameof(EnableSaveToAll));
NotifyOfPropertyChange(nameof(EnableReload));
NotifyOfPropertyChange(nameof(CurrentRecipe));
}
private string _currentCriteria = string.Empty;
public string CurrentCriteria
{
get => _currentCriteria;
set
{
if (value == _currentCriteria)
return;
_currentCriteria = value;
NotifyOfPropertyChange(nameof(CurrentCriteria));
ApplyFilter();
}
}
private void ApplyFilter()
{
ProcessTypeFileList[ProcessTypeIndexSelection].FilterFileListByProcessType =
new ObservableCollection<FileNode>(ProcessTypeFileList[ProcessTypeIndexSelection].FileListByProcessType
.Where(d => d.Name.IndexOf(CurrentCriteria, StringComparison.OrdinalIgnoreCase) >= 0));
}
public void ClearFilter()
{
CurrentCriteria = "";
}
/// <summary>
/// 当主窗口状态Login状态发生变化时处理当前窗口状态。
/// </summary>
/// <param name="message"></param>
public void Handle(UserMode message)
{
switch (message)
{
case UserMode.None:
break;
case UserMode.Normal:
break;
case UserMode.Lock:
case UserMode.Logoff:
case UserMode.Exit:
case UserMode.Shutdown:
case UserMode.Breakdown:
_winValidationInfo?.Close();
break;
default:
// ignore
break;
}
}
}
}