Sic.Framework/MECF.Framework.UI.Client/CenterViews/Editors/Recipe/RecipeEditorViewModel.cs

1828 lines
60 KiB
C#
Raw Normal View History

2023-04-13 11:51:03 +08:00
using Aitex.Core.RT.Log;
using Caliburn.Micro;
using Caliburn.Micro.Core;
using MECF.Framework.Common.DataCenter;
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.Linq;
using System.Threading.Tasks;
2023-04-13 11:51:03 +08:00
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.IO;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using Microsoft.Win32;
using MECF.Framework.UI.Client.CenterViews.Configs.Roles;
2023-04-13 11:51:03 +08:00
namespace MECF.Framework.UI.Client.CenterViews.Editors.Recipe
{
public class RecipeEditorViewModel : UiViewModelBase, IHandle<UserMode> //BaseModel
2023-04-13 11:51:03 +08:00
{
#region Variables
private readonly IRecipeGasFlowCalculator _recipeGasFlowCalculator;
private RecipeData _currentRecipe;
private bool _isLoading;
private int _errorsCount;
private Window _winValidationInfo;
private EditMode _editMode;
private readonly RecipeFormatBuilder _columnBuilder;
private readonly RecipeProvider _recipeProvider;
private ObservableCollection<ProcessTypeFileItem> _processTypeFileList;
private Point? _lastPosOfValidationWin = null;
2023-04-13 11:51:03 +08:00
#endregion
2023-04-13 11:51:03 +08:00
#region Constructors
2023-04-13 11:51:03 +08:00
public RecipeEditorViewModel()
2023-04-13 11:51:03 +08:00
{
var eventAggregator = IoC.Get<IEventAggregator>();
eventAggregator?.Subscribe(this);
_recipeGasFlowCalculator = IoC.Get<IRecipeGasFlowCalculator>();
_columnBuilder = new RecipeFormatBuilder();
_recipeProvider = new RecipeProvider();
2023-04-13 11:51:03 +08:00
}
#endregion
#region Commands
2023-04-13 11:51:03 +08:00
private ICommand _RenameFolderCommand;
2023-04-13 11:51:03 +08:00
public ICommand RenameFolderCommand
{
get
{
if (_RenameFolderCommand == null)
_RenameFolderCommand = new BaseCommand(() => RenameFolder());
return _RenameFolderCommand;
2023-04-13 11:51:03 +08:00
}
}
private ICommand _DeleteFolderCommand;
2023-04-13 11:51:03 +08:00
public ICommand DeleteFolderCommand
{
get
{
if (_DeleteFolderCommand == null)
_DeleteFolderCommand = new BaseCommand(() => DeleteFolder());
return _DeleteFolderCommand;
2023-04-13 11:51:03 +08:00
}
}
private ICommand _NewFolderCommand;
2023-04-13 11:51:03 +08:00
public ICommand NewFolderCommand
{
get
{
if (_NewFolderCommand == null)
_NewFolderCommand = new BaseCommand(() => NewFolder());
return _NewFolderCommand;
2023-04-13 11:51:03 +08:00
}
}
2023-04-13 11:51:03 +08:00
private ICommand _NewFolderRootCommand;
2023-04-13 11:51:03 +08:00
public ICommand NewFolderRootCommand
{
get
{
if (_NewFolderRootCommand == null)
_NewFolderRootCommand = new BaseCommand(() => NewFolderRoot());
return _NewFolderRootCommand;
2023-04-13 11:51:03 +08:00
}
}
private ICommand _NewRecipeCommand;
2023-04-13 11:51:03 +08:00
public ICommand NewRecipeCommand
{
get
{
if (_NewRecipeCommand == null)
_NewRecipeCommand = new BaseCommand(() => NewRecipe());
return _NewRecipeCommand;
2023-04-13 11:51:03 +08:00
}
}
private ICommand _NewRecipeRootCommand;
2023-04-13 11:51:03 +08:00
public ICommand NewRecipeRootCommand
{
get
{
if (_NewRecipeRootCommand == null)
_NewRecipeRootCommand = new BaseCommand(() => NewRecipeRoot());
return _NewRecipeRootCommand;
2023-04-13 11:51:03 +08:00
}
}
2023-04-13 11:51:03 +08:00
private ICommand _RenameRecipeCommand;
2023-04-13 11:51:03 +08:00
public ICommand RenameRecipeCommand
{
get
{
if (_RenameRecipeCommand == null)
_RenameRecipeCommand = new BaseCommand(() => RenameRecipe());
return _RenameRecipeCommand;
2023-04-13 11:51:03 +08:00
}
}
2023-04-13 11:51:03 +08:00
private ICommand _DeleteRecipeCommand;
2023-04-13 11:51:03 +08:00
public ICommand DeleteRecipeCommand
{
get
{
if (_DeleteRecipeCommand == null)
_DeleteRecipeCommand = new BaseCommand(() => DeleteRecipe());
return _DeleteRecipeCommand;
2023-04-13 11:51:03 +08:00
}
}
2023-04-13 11:51:03 +08:00
private ICommand _SaveAsRecipeCommand;
2023-04-13 11:51:03 +08:00
public ICommand SaveAsRecipeCommand
{
get
{
if (_SaveAsRecipeCommand == null)
_SaveAsRecipeCommand = new BaseCommand(() => SaveAsRecipe());
return _SaveAsRecipeCommand;
2023-04-13 11:51:03 +08:00
}
}
#endregion
2023-04-13 11:51:03 +08:00
#region Properties
public ObservableCollection<ProcessTypeFileItem> ProcessTypeFileList
{
get => _processTypeFileList;
set
{
_processTypeFileList = value;
NotifyOfPropertyChange(nameof(ProcessTypeFileList));
}
}
public RecipeData CurrentRecipe
{
get => _currentRecipe;
set
{
_currentRecipe = value;
NotifyOfPropertyChange();
}
}
public bool IsPermission => Permission == 3; //&& RtStatus != "AutoRunning";
2023-04-13 11:51:03 +08:00
public FileNode CurrentFileNode { get; set; }
private bool IsChanged => _editMode == EditMode.Edit || CurrentRecipe.IsChanged;
2023-04-13 11:51:03 +08:00
public Dictionary<string, List<EditorDataGridTemplateColumnBase>> DicColunms { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableNew { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableReName { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableCopy { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableDelete { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableSave { get; set; }
public bool EnableImportExport { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableStep { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableReload { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableSaveToAll { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableSaveTo { get; set; }
public bool EnableLeftTabPanel { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableRefreshRecipeList { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableFilterTreeList { get; set; }
2023-04-13 11:51:03 +08:00
public bool EnableCellPermButton { get; set; }
2023-04-13 11:51:03 +08:00
/// <summary>
/// 返回是否显示单元格访问权限编辑按钮。
/// </summary>
public bool IsShowCellAccessPermEditButton { get; private set; }
2023-04-13 11:51:03 +08:00
public int ErrorsCount
2023-04-13 11:51:03 +08:00
{
set
2023-04-13 11:51:03 +08:00
{
// 如果错误数没发生变化不要修改Badge的值以免触发Badge动画。
if (value == _errorsCount)
return;
if(value == 0)
((RecipeEditorView)View).txtErrorCount.Badge = null;
else
((RecipeEditorView)View).txtErrorCount.Badge = value;
_errorsCount = value;
2023-04-13 11:51:03 +08:00
}
}
private int _countAccessibleWhitelist;
private int AccessibleWhitelistCount
2023-04-13 11:51:03 +08:00
{
set
2023-04-13 11:51:03 +08:00
{
if (value == _countAccessibleWhitelist)
return;
if (value == 0)
((RecipeEditorView)View).txtCellAccessPremCount.Badge = null;
else
((RecipeEditorView)View).txtCellAccessPremCount.Badge = value;
_countAccessibleWhitelist = value;
2023-04-13 11:51:03 +08:00
}
}
/// <summary>
/// 是否正在加载配方。
/// </summary>
public bool IsLoading
2023-04-13 11:51:03 +08:00
{
get => _isLoading;
private set
{
_isLoading = value;
NotifyOfPropertyChange(nameof(IsLoading));
}
2023-04-13 11:51:03 +08:00
}
public List<RecipeStep> SelectedRecipeSteps { get; set; }
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; }
2023-04-13 11:51:03 +08:00
public ObservableCollection<string> Chambers { get; set; }
public string SelectedChamber { get; set; }
public object View { get; set; }
#endregion
2023-04-13 11:51:03 +08:00
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(','));
}
2023-04-13 11:51:03 +08:00
ChamberTypeIndexSelection = 0;
var processType = "Process,Routine,Clean";
2023-04-13 11:51:03 +08:00
ProcessTypeFileList = new ObservableCollection<ProcessTypeFileItem>();
var recipeProcessType = ((string)processType).Split(',');
2023-04-13 11:51:03 +08:00
for (var i = 0; i < recipeProcessType.Length; i++)
2023-04-13 11:51:03 +08:00
{
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;
2023-04-13 11:51:03 +08:00
ProcessTypeFileList.Add(type);
}
DicColunms = new Dictionary<string, List<EditorDataGridTemplateColumnBase>>();
2023-04-13 11:51:03 +08:00
UpdateRecipeFormat();
}
2023-04-13 11:51:03 +08:00
protected override void OnActivate()
{
//初始化RoleManager
var roleManager = new RoleManager();
roleManager.Initialize();
//得到当前登录的RoleItem
var role = roleManager.GetRoleByName(BaseApp.Instance.UserContext.RoleName);
CurrentRecipe.ReloadRolePermission(role);
UpdateView();
// 根据角色权限设置判断是否显示按钮。
IsShowCellAccessPermEditButton = GetCapeModeButtonPerm();
NotifyOfPropertyChange(nameof(IsShowCellAccessPermEditButton));
2023-04-13 11:51:03 +08:00
base.OnActivate();
}
/// <summary>
/// 获取角色权限配置中“Recipe.Behaviour.AllowEditCellAccessPerm”的配置。
/// </summary>
/// <returns></returns>
private bool GetCapeModeButtonPerm()
{
var roleID = BaseApp.Instance.UserContext.RoleID;
var val = RoleAccountProvider.Instance.GetMenuPermission(roleID, "Recipe.Behaviour.AllowEditCellAccessPerm");
return val != (int)MenuPermissionEnum.MP_NONE;
}
2023-04-13 11:51:03 +08:00
protected override void OnDeactivate(bool close)
{
base.OnDeactivate(close);
if (IsChanged)
2023-04-13 11:51:03 +08:00
{
if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} content is changed, do you want to save it?") == DialogButton.Yes)
2023-04-13 11:51:03 +08:00
{
SaveRecipe();
2023-04-13 11:51:03 +08:00
}
}
_winValidationInfo?.Close();
// 锁定编辑器
((RecipeEditorView)View).editorLocker.Lock();
2023-04-13 11:51:03 +08:00
}
protected override void OnViewLoaded(object view)
{
View = view;
base.OnViewLoaded(view);
}
public void TabSelectionChanged()
2023-04-13 11:51:03 +08:00
{
UpdateRecipeFormat();
OnViewLoaded(View);
}
2023-04-13 11:51:03 +08:00
public void UpdateRecipeFormat()
{
var chamber = QueryDataClient.Instance.Service.GetConfig("System.Recipe.ChamberModules");
if (chamber == null)
2023-04-13 11:51:03 +08:00
{
chamber = "PM1";
2023-04-13 11:51:03 +08:00
}
Chambers = new ObservableCollection<string>(((string)chamber).Split(','));
if (Chambers.Count > 1)
2023-04-13 11:51:03 +08:00
{
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);
}
}
}
}
2023-04-13 11:51:03 +08:00
if (Chambers.Count == 0)
{
Chambers = new ObservableCollection<string>(new string[] { "PM1" });
2023-04-13 11:51:03 +08:00
}
SelectedChamber = Chambers[0];
CurrentRecipe = new RecipeData(_recipeGasFlowCalculator);
CurrentRecipe.BuildFormat($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, BaseApp.Instance.UserContext.RoleName);
CurrentRecipe.OnValidated += (sender, args) =>
{
ErrorsCount = CurrentRecipe.ValidationErrorCount;
};
2023-04-13 11:51:03 +08:00
CurrentRecipe.OnAccessibleWhitelistChanged += (sender, count) =>
{
AccessibleWhitelistCount = count;
};
_editMode = EditMode.None;
2023-04-13 11:51:03 +08:00
MultiChamberVisibility = Chambers.Count > 1 ? Visibility.Visible : Visibility.Collapsed;
}
2023-04-13 11:51:03 +08:00
/// <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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
2023-04-13 11:51:03 +08:00
IsLoading = true;
2023-04-13 11:51:03 +08:00
await CurrentRecipe.ChangeChamber(CurrentRecipe.Columns
, _columnBuilder.Configs, SelectedChamber, GetLoadingDispatcher());
2023-04-13 11:51:03 +08:00
IsLoading = false;
}
2023-04-13 11:51:03 +08:00
/// <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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
2023-04-13 11:51:03 +08:00
CurrentFileNode = node;
2023-04-13 11:51:03 +08:00
if (node != null && node.IsFile)
{
await LoadRecipe(node.PrefixPath, node.FullPath);
}
else
{
ClearData();
_editMode = EditMode.None;
}
2023-04-13 11:51:03 +08:00
UpdateView();
}
2023-04-13 11:51:03 +08:00
#region folder
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
var name = dialog.FileName.Trim();
if (string.IsNullOrEmpty(name))
2023-04-13 11:51:03 +08:00
{
DialogBox.ShowWarning("Folder name should not be empty");
return;
2023-04-13 11:51:03 +08:00
}
var prefix = ChamberType[ChamberTypeIndexSelection] + "\\" +
ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
var processType = string.Empty;
var newFolder = string.Empty;
if (CurrentFileNode != null)
2023-04-13 11:51:03 +08:00
{
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 + "\\";
}
2023-04-13 11:51:03 +08:00
}
2023-04-13 11:51:03 +08:00
newFolder = newFolder + name;
2023-04-13 11:51:03 +08:00
if (IsExist(newFolder, false))
2023-04-13 11:51:03 +08:00
{
DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder with the same name already exist.");
return;
2023-04-13 11:51:03 +08:00
}
if (newFolder.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200.");
return;
}
2023-04-13 11:51:03 +08:00
_recipeProvider.CreateRecipeFolder(prefix, newFolder);
2023-04-13 11:51:03 +08:00
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true);
}
2023-04-13 11:51:03 +08:00
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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
}
var dialog = new InputFileNameDialogViewModel("Input New Folder Name");
2023-04-13 11:51:03 +08:00
dialog.FileName = "new folder";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var name = dialog.FileName.Trim();
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
_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}.");
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
if (CurrentFileNode.Parent.Files.Count > 1)
{
for (var i = 0; i < CurrentFileNode.Parent.Files.Count; i++)
2023-04-13 11:51:03 +08:00
{
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");
2023-04-13 11:51:03 +08:00
dialog.FileName = CurrentFileNode.Name;
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var name = dialog.FileName.Trim();
2023-04-13 11:51:03 +08:00
if (string.IsNullOrEmpty(name))
return;
var newFolder = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1);
2023-04-13 11:51:03 +08:00
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
2023-04-13 11:51:03 +08:00
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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
}
var dialog = new InputFileNameDialogViewModel("Input New Name");
dialog.FileName = "";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
2023-04-13 11:51:03 +08:00
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
var prefix = CurrentChamberType + "\\" + CurrentProcessType;
var processType = string.Empty;
2023-04-13 11:51:03 +08:00
if (CurrentFileNode != null)
{
var folder = CurrentFileNode.FullPath;
2023-04-13 11:51:03 +08:00
if (CurrentFileNode.IsFile)
{
folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
//if (!string.IsNullOrEmpty(folder))
// folder = folder;
}
else
{
folder = folder + "\\";
}
2023-04-13 11:51:03 +08:00
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(_recipeGasFlowCalculator);
2023-04-13 11:51:03 +08:00
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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
}
var dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
2023-04-13 11:51:03 +08:00
dialog.FileName = "new recipe";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
2023-04-13 11:51:03 +08:00
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(_recipeGasFlowCalculator);
2023-04-13 11:51:03 +08:00
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)
2023-04-13 11:51:03 +08:00
{
var item = ProcessTypeFileList.FirstOrDefault(x => x.ProcessType == processType);
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
item.InvokePropertyChanged();
}
private bool IsExist(string fullPath, bool isFile)
{
for (var i = 0; i < ProcessTypeFileList.Count; i++)
2023-04-13 11:51:03 +08:00
{
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;
}
2023-04-13 11:51:03 +08:00
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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
}
var dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
2023-04-13 11:51:03 +08:00
dialog.FileName = CurrentFileNode.Name;
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
2023-04-13 11:51:03 +08:00
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
var prefix = CurrentChamberType + "\\" + CurrentProcessType;
var processType = string.Empty;
2023-04-13 11:51:03 +08:00
var folder = CurrentFileNode.FullPath;
2023-04-13 11:51:03 +08:00
if (CurrentFileNode.IsFile)
{
folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
}
2023-04-13 11:51:03 +08:00
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?");
2023-04-13 11:51:03 +08:00
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
}
var dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
2023-04-13 11:51:03 +08:00
dialog.FileName = CurrentFileNode.Name;
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
2023-04-13 11:51:03 +08:00
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
var prefix = CurrentChamberType + "\\" + CurrentProcessType;
var processType = string.Empty;
2023-04-13 11:51:03 +08:00
var newName = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1);
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
if (CurrentFileNode.Parent.Files.Count > 1)
{
for (var i = 0; i < CurrentFileNode.Parent.Files.Count; i++)
2023-04-13 11:51:03 +08:00
{
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);
CurrentRecipe.DeleteAccessibleWhiteList();
2023-04-13 11:51:03 +08:00
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder);
}
public void RefreshRecipe()
{
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, "", false);
}
public async void ReloadRecipe()
2023-04-13 11:51:03 +08:00
{
if (_editMode == EditMode.Normal || _editMode == EditMode.Edit)
2023-04-13 11:51:03 +08:00
{
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 LoadRecipe(CurrentRecipe.PrefixPath, CurrentRecipe.Name);
CurrentRecipe.Validate();
UpdateView();
foreach (var step in CurrentRecipe.Steps)
step.Save();
2023-04-13 11:51:03 +08:00
}
}
public void SaveToAll()
{
CurrentRecipe.Validate();
2023-04-13 11:51:03 +08:00
if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
{
DialogBox.ShowWarning($"Saving failed, {CurrentRecipe.Name} is not a valid recipe file");
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
CurrentRecipe.SaveTo(Chambers.ToArray());
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
public void SaveTo()
{
CurrentRecipe.Validate();
2023-04-13 11:51:03 +08:00
if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
{
DialogBox.ShowWarning($"Saving failed, {CurrentRecipe.Name} is not a valid recipe file.");
2023-04-13 11:51:03 +08:00
return;
}
var dialog =
new SaveToDialogViewModel("Select the chamber to copy to", SelectedChamber, Chambers.ToList());
2023-04-13 11:51:03 +08:00
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
2023-04-13 11:51:03 +08:00
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var chambers = new List<string>();
2023-04-13 11:51:03 +08:00
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);
}
/// <summary>
/// 导入Recipe。
/// </summary>
public void ImportRecipe()
{
try
{
IsLoading = true;
// 选择文件
var openFileDialog = new OpenFileDialog()
{
Filter = "Sic Recipe File|*.srp",
Title = "Import Recipe",
DefaultExt = ".srp",
Multiselect = true
};
var prefix = ProcessTypeFileItem.GetProcessFilesPrefix(ProcessTypeFileItem.ProcessFileTypes.Process);
if (openFileDialog.ShowDialog() != true)
return;
#region
var dialog = new RecipeSelectDialogViewModel(true, true, ProcessTypeFileItem.ProcessFileTypes.Process)
{
DisplayName = "Select Folder to Import ..."
};
var wm = new WindowManager();
var bret = wm.ShowDialog(dialog);
#endregion
if (bret != true)
return;
var recipeFolderToImport
= dialog.DialogResult;
// 从文件夹名移除Prefix
recipeFolderToImport = recipeFolderToImport.Replace(prefix + "\\", "");
var fns = openFileDialog.FileNames;
// 是否覆盖所有Recipe该选项通过Overwrite对话框给值。
var isOverwriteAll = false;
foreach (var fn in fns)
{
try
{
var recipePath = $"{recipeFolderToImport}\\{Path.GetFileNameWithoutExtension(fn)}";
// 检查Recipe是否已存在
if (IsExist(recipePath.TrimStart('\\'), true) && !isOverwriteAll)
{
var ret = DialogBox.ShowDialog(
DialogButton.Yes | DialogButton.YesToAll | DialogButton.Cancel,
DialogType.WARNING,
$"{recipePath} has existed, overwrite?\r\n\r\nATTENTION: Press 'Yes To All' to overwrite all files.");
if (ret == DialogButton.Cancel)
return;
if (ret == DialogButton.YesToAll)
isOverwriteAll = true;
}
RecipeData.ImportRecipe(fn, out var xmlContent);
// 保存文件。
_recipeProvider.WriteRecipeFile(prefix, recipePath, xmlContent);
}
catch (Exception ex)
{
DialogBox.ShowError($"Unable to import {fn}, {ex}");
}
}
// 刷新Recipe列表
ReloadRecipeFileList("Sic", "Process", "", false);
}
finally
{
IsLoading = false;
}
}
/// <summary>
/// 到处Recipe。
/// </summary>
public void ExportRecipe()
{
if (CurrentRecipe == null)
{
MessageBox.Show("No recipe selected.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
if (string.IsNullOrEmpty(CurrentRecipe.Name))
{
MessageBox.Show("No recipe loaded.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// 选择保存路径和文件名
var dialog = new SaveFileDialog
{
Filter = "Sic Recipe File|*.srp",
Title = "Export Recipe",
DefaultExt = ".srp",
FileName = CurrentRecipe.Name.Split('\\').Last()
};
var ret = dialog.ShowDialog();
if (!ret.HasValue || ret == false)
return;
// 保存文件
try
{
CurrentRecipe.ExportRecipe(dialog.FileName);
DialogBox.ShowInfo($"Export done! {dialog.FileName}");
}
catch (Exception ex)
{
DialogBox.ShowError($"Unable to export recipe, {ex}");
}
2023-04-13 11:51:03 +08:00
}
2023-04-13 11:51:03 +08:00
#endregion
#region Steps
2023-04-13 11:51:03 +08:00
public void SaveRecipe()
{
/*if (Save(CurrentRecipe, false))
MessageBox.Show($"The recipe has been saved successfully!", "Succeeded", MessageBoxButton.OK,
MessageBoxImage.Information);*/
Save(CurrentRecipe, false);
2023-04-13 11:51:03 +08:00
}
public void PopSetting(string controlName, Param paramData)
{
var stepNum = Convert.ToInt32(((StepParam)paramData.Parent[1]).Value);
2023-04-13 11:51:03 +08:00
var dialog = new PublicPopSettingDialogViewModel();
2023-04-13 11:51:03 +08:00
dialog.DisplayName = paramData.DisplayName;
var step = CurrentRecipe.PopSettingSteps[controlName][stepNum - 1];
2023-04-13 11:51:03 +08:00
var controlParameters = new RecipeStep(null);
var brandParameters = new ObservableCollection<BandParam>();
foreach (var item in step)
2023-04-13 11:51:03 +08:00
{
if (item.Name.Contains("Band"))
{
var name = item.Name.Replace("Wavelength", "").Replace("Bandwidth", "");
var displayName = item.DisplayName.Replace("Wavelength", "").Replace("Bandwidth", "");
2023-04-13 11:51:03 +08:00
if (brandParameters.All(x => x.Name != name))
2023-04-13 11:51:03 +08:00
{
brandParameters.Add(new BandParam()
2023-04-13 11:51:03 +08:00
{
Name = name,
DisplayName = displayName
});
}
if (item.Name.Contains("Wavelength"))
{
brandParameters.First(x => x.Name == name).WavelengthDoubleParam = item;
2023-04-13 11:51:03 +08:00
}
else if (item.Name.Contains("Bandwidth"))
{
brandParameters.First(x => x.Name == name).BandwidthDoubleParam = item;
2023-04-13 11:51:03 +08:00
}
}
else
controlParameters.Add(item);
2023-04-13 11:51:03 +08:00
}
dialog.Parameters = step;
dialog.ControlParameters = controlParameters;
dialog.BandParameters = brandParameters;
2023-04-13 11:51:03 +08:00
var wm = new WindowManager();
var bret = wm.ShowDialog(dialog);
if (bret == true)
2023-04-13 11:51:03 +08:00
{
CurrentRecipe.PopSettingSteps[controlName][stepNum - 1] = dialog.Parameters;
2023-04-13 11:51:03 +08:00
}
}
private bool Save(RecipeData recipe, bool createNew)
2023-04-13 11:51:03 +08:00
{
if (string.IsNullOrEmpty(recipe.Name))
{
MessageBox.Show("Recipe name can't be empty", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
2023-04-13 11:51:03 +08:00
return false;
}
if (CurrentRecipe.Validate() == false)
{
var mbr = DialogBox.ShowDialog(
DialogButton.Yes | DialogButton.No,
DialogType.WARNING,
$"Are you sure to continue to save with errors?\r\n\r\n" +
$"{CurrentRecipe.ValidationErrorSummary}");
if (mbr == DialogButton.No)
return false;
}
var result = false;
2023-04-13 11:51:03 +08:00
recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
recipe.ReviseTime = DateTime.Now;
result = _recipeProvider.WriteRecipeFile(recipe.PrefixPath, recipe.Name, recipe.GetXmlString());
2023-04-13 11:51:03 +08:00
if (result)
{
recipe.MarkAsSaved();
_editMode = EditMode.Normal;
UpdateView();
2023-04-13 11:51:03 +08:00
}
else
{
MessageBox.Show("Save failed!");
}
return result;
}
public void AddStep()
{
CurrentRecipe.AddStep();
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
UpdateView();
2023-04-13 11:51:03 +08:00
}
public void InsertStepToLeft()
2023-04-13 11:51:03 +08:00
{
if (CurrentRecipe == null)
return;
2023-04-13 11:51:03 +08:00
if (CurrentRecipe.Steps.SelectedSteps.Count != 1)
return;
var step = CurrentRecipe.Steps.SelectedSteps[0];
CurrentRecipe.InsertToLeft(step);
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
UpdateView();
}
public void InsertStepToRight()
{
if (CurrentRecipe == null)
return;
if(CurrentRecipe.Steps.SelectedSteps.Count != 1)
return;
var step = CurrentRecipe.Steps.SelectedSteps[0];
CurrentRecipe.InsertToRight(step);
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
UpdateView();
2023-04-13 11:51:03 +08:00
}
/// <summary>
/// 拷贝所有选中的Steps。
/// </summary>
2023-04-13 11:51:03 +08:00
public void CopyStep()
{
CurrentRecipe?.CopySteps();
2023-04-13 11:51:03 +08:00
}
/// <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)
2023-04-13 11:51:03 +08:00
{
if (CurrentRecipe == null)
return;
await CurrentRecipe.Paste(isToLeft);
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
UpdateView();
2023-04-13 11:51:03 +08:00
}
/// <summary>
/// 在左侧复制Step。
/// </summary>
public async void DuplicateStepToLeft()
2023-04-13 11:51:03 +08:00
{
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);
2023-04-13 11:51:03 +08:00
}
/// <summary>
/// 删除步骤。
/// </summary>
2023-04-13 11:51:03 +08:00
public void DeleteStep()
{
if (CurrentRecipe == null)
return;
CurrentRecipe.DeleteSteps();
2023-04-13 11:51:03 +08:00
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
2023-04-13 11:51:03 +08:00
UpdateView();
}
private void CreateValidationDetailWindow()
{
_winValidationInfo = new RecipeEditorValidationDetailWindow(
CurrentRecipe.ValidationErrorInfo,
_lastPosOfValidationWin);
_winValidationInfo.DataContext = this;
_winValidationInfo.Closed += (sender, args) =>
{
_lastPosOfValidationWin = new Point(_winValidationInfo.Left, _winValidationInfo.Top);
_currentRecipe.Steps.ResetHighlight();
};
}
public void ShowValidationDetailWindow()
{
CurrentRecipe.Validate();
if (Application.Current.Windows.OfType<RecipeEditorValidationDetailWindow>().Any())
_winValidationInfo.Activate();
else
2023-04-13 11:51:03 +08:00
{
CreateValidationDetailWindow();
_winValidationInfo.Show();
2023-04-13 11:51:03 +08:00
}
}
private TreeViewItem GetParentObjectEx<TreeViewItem>(DependencyObject obj) where TreeViewItem : FrameworkElement
{
var parent = VisualTreeHelper.GetParent(obj);
2023-04-13 11:51:03 +08:00
while (parent != null)
{
if (parent is TreeViewItem)
{
return (TreeViewItem)parent;
}
2023-04-13 11:51:03 +08:00
parent = VisualTreeHelper.GetParent(parent);
}
2023-04-13 11:51:03 +08:00
return null;
}
public void TreeRightMouseDown(MouseButtonEventArgs e)
{
var item = GetParentObjectEx<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
if (item != null)
{
item.Focus();
}
}
/// <summary>
/// 滚动到并高亮指定的参数单元格。
/// </summary>
/// <param name="param"></param>
public void FocusToParam(IParam param)
{
((RecipeEditorView)View).dgCustom.FocusToParam(param);
}
#endregion
#region Cell-Access-Perm-Edit Mode
public async Task SwitchCellAccessPermEditMode()
{
var mode = CurrentRecipe.IsAccessibleWhitelistEditMode;
if (mode)
{
await CurrentRecipe.LeaveAccessibleWhitelistEditMode();
_editMode = EditMode.Normal;
}
else
{
var ret = await CurrentRecipe.EnterAccessibleWhitelistEditMode();
if (!ret.isSucceeded)
{
DialogBox.ShowError($"Can not edit whitelist, {ret.reason}");
return;
}
_editMode = EditMode.EditWhitelist;
}
UpdateView();
}
2023-04-13 11:51:03 +08:00
#endregion
2023-04-13 11:51:03 +08:00
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.RecipeCascadeLoading");
var dispatcher = isCascadeLoading ? Dispatcher.CurrentDispatcher : null;
return dispatcher;
2023-04-13 11:51:03 +08:00
}
private async Task LoadRecipe(string prefixPath, string recipeName)
2023-04-13 11:51:03 +08:00
{
IsLoading = true;
2023-04-13 11:51:03 +08:00
CurrentRecipe.Clear();
CurrentRecipe.Steps.Save(); // 重置为已保存状态
ErrorsCount = 0;
2023-04-13 11:51:03 +08:00
var recipeContent = _recipeProvider.ReadRecipeFile(prefixPath, recipeName);
2023-04-13 11:51:03 +08:00
if (string.IsNullOrEmpty(recipeContent))
{
MessageBox.Show($"{prefixPath}\\{recipeName} is empty, please confirm the file is valid.",
"Error",
MessageBoxButton.OK, MessageBoxImage.Error);
2023-04-13 11:51:03 +08:00
return;
}
await CurrentRecipe.LoadFile(prefixPath, recipeName, recipeContent, SelectedChamber,
GetLoadingDispatcher());
_editMode = EditMode.Normal;
IsLoading = false;
2023-04-13 11:51:03 +08:00
}
private void UpdateView()
{
var isFileSelected = CurrentFileNode != null && CurrentFileNode.IsFile;
EnableImportExport = true;
EnableLeftTabPanel = true;
EnableFilterTreeList = true;
EnableRefreshRecipeList = true;
EnableNew = true;
EnableCellPermButton = isFileSelected;
EnableReload = isFileSelected;
EnableNew = isFileSelected;
EnableReName = isFileSelected;
EnableCopy = isFileSelected;
EnableDelete = isFileSelected;
EnableSave = isFileSelected;
EnableStep = isFileSelected;
2023-04-13 11:51:03 +08:00
EnableSaveTo = isFileSelected;
EnableSaveToAll = isFileSelected;
if (_editMode == EditMode.None)
{
EnableNew = true;
EnableReName = false;
EnableCopy = false;
EnableDelete = false;
EnableStep = false;
EnableSave = false;
EnableReload = true;
}
//else if (CurrentRecipe.IsAccessibleWhitelistEditMode)
else if (_editMode == EditMode.EditWhitelist)
{
EnableNew = false;
EnableReName = false;
EnableCopy = false;
EnableDelete = false;
EnableStep = false;
EnableSave = false;
EnableReload = false;
EnableSaveTo = false;
EnableSaveToAll = false;
EnableLeftTabPanel = false;
EnableFilterTreeList = false;
EnableRefreshRecipeList = false;
}
NotifyOfPropertyChange(nameof(EnableNew));
NotifyOfPropertyChange(nameof(EnableReName));
NotifyOfPropertyChange(nameof(EnableCopy));
NotifyOfPropertyChange(nameof(EnableDelete));
NotifyOfPropertyChange(nameof(EnableSave));
NotifyOfPropertyChange(nameof(EnableImportExport));
NotifyOfPropertyChange(nameof(EnableStep));
NotifyOfPropertyChange(nameof(EnableSaveTo));
NotifyOfPropertyChange(nameof(EnableSaveToAll));
NotifyOfPropertyChange(nameof(EnableLeftTabPanel));
NotifyOfPropertyChange(nameof(EnableFilterTreeList));
NotifyOfPropertyChange(nameof(EnableRefreshRecipeList));
NotifyOfPropertyChange(nameof(EnableCellPermButton));
NotifyOfPropertyChange(nameof(CurrentRecipe));
}
private string _currentCriteria = string.Empty;
public string CurrentCriteria
{
get => _currentCriteria;
set
2023-04-13 11:51:03 +08:00
{
if (value == _currentCriteria)
return;
2023-04-13 11:51:03 +08:00
_currentCriteria = value;
NotifyOfPropertyChange(nameof(CurrentCriteria));
ApplyFilter();
2023-04-13 11:51:03 +08:00
}
}
2023-04-13 11:51:03 +08:00
private void ApplyFilter()
{
ProcessTypeFileList[ProcessTypeIndexSelection].FilterFileListByProcessType =
new ObservableCollection<FileNode>(ProcessTypeFileList[ProcessTypeIndexSelection].FileListByProcessType
.Where(d => d.Name.IndexOf(CurrentCriteria, StringComparison.OrdinalIgnoreCase) >= 0));
2023-04-13 11:51:03 +08:00
}
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;
2023-04-13 11:51:03 +08:00
default:
// ignore
break;
}
}
}
2023-04-13 11:51:03 +08:00
}