using MainShell.Common; using MainShell.Filewritable; using MainShell.Models; using MainShell.Recipe.Models; using MainShell.Recipe.Services; using MaxwellFramework.Core.Common.Command; using Newtonsoft.Json.Linq; using Stylet; using StyletIoC; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows.Input; namespace MainShell.Recipe.ViewModel { public class SubstrateRecipeViewModel : RecipeViewModelBase, IHandle { private readonly RecipeWrapManager _wrapManager; private readonly IEventAggregator _eventAggregator; private SubstrateRecipe _substrateRecipe; public SubstrateRecipe SubstrateRecipe { get { return _substrateRecipe; } set { if (SetAndNotify(ref _substrateRecipe, value)) { if (SubstrateTeachViewModel != null) { SubstrateTeachViewModel.SubstrateRecipe = value; } if (SubstrateHeightMeasureViewModel != null) { SubstrateHeightMeasureViewModel.SubstrateRecipe = value; } } } } protected override SubstrateRecipe CurrentRecipe { get => SubstrateRecipe; set => SubstrateRecipe = value; } protected override string RecipeFolderPath => Paths.SubstrateRecipe; protected override string EmptyRecipeDisplayName => "当前基板配方"; protected override string GetActiveRecipeName() { return RecipeManager.CurrentSubstrateRecipe?.RecipeName; } protected override void SwitchActiveRecipe(string recipeName) { RecipeManager.SwitchSubstrateRecipe(recipeName); } protected override void ClearActiveRecipe() { RecipeManager.ClearSubstrateRecipe(); } private int _selectedTabIndex; /// /// 当前选中的 Tab 索引:0=基础设置(显示配方列表),1=参数示教(隐藏配方列表) /// public int SelectedTabIndex { get => _selectedTabIndex; set { if (SetAndNotify(ref _selectedTabIndex, value)) HideRecipePanel = value != 0; } } private SubstrateTeachViewModel _substrateTeachViewModel; [Inject] public SubstrateTeachViewModel SubstrateTeachViewModel { get { return _substrateTeachViewModel; } set { if (SetAndNotify(ref _substrateTeachViewModel, value) && value != null) { value.SubstrateRecipe = SubstrateRecipe; } } } private SubstrateHeightMeasureViewModel _substrateHeightMeasureViewModel; [Inject] public SubstrateHeightMeasureViewModel SubstrateHeightMeasureViewModel { get { return _substrateHeightMeasureViewModel; } set { if (SetAndNotify(ref _substrateHeightMeasureViewModel, value) && value != null) { value.SubstrateRecipe = SubstrateRecipe; } } } public ObservableCollection WaferRecipes => _wrapManager.WaferRecipeWraps; public SubstrateRecipeViewModel(RecipeWrapManager wrapManager, IEventAggregator eventAggregator, RecipeManager recipeManager) : base(recipeManager) { _wrapManager = wrapManager; RecipeWraps = wrapManager.SubstrateRecipeWraps; RegisterButtonGroupEvents(); _eventAggregator = eventAggregator; _eventAggregator.Unsubscribe(this); _eventAggregator.Subscribe(this); DeleteSelectWaferInfoCmd = new DelegateCommand(DeleteSelectWaferInfo); } #region//Commands public ICommand DeleteSelectWaferInfoCmd { get; private set; } #endregion protected override string GetUnsavedChangesMessage() { var differenceSummary = GetRecipeDifferenceSummary(); return string.IsNullOrWhiteSpace(differenceSummary) ? $"配方“{CurrentRecipeDisplayName}”有未保存修改,是否先保存?" : $"配方“{CurrentRecipeDisplayName}”有未保存修改:\r\n{differenceSummary}\r\n是否先保存?"; } protected override void OnViewLoaded() { base.OnViewLoaded(); InitializeSelection(RecipeManager.CurrentSubstrateRecipe?.RecipeName); } protected override void OnRecipeLoaded(SubstrateRecipe recipe, RecipeWrap recipeWrap) { } protected override void AfterDeleteCurrentRecipe(string deletedRecipeName) { RecipeManager.DeleteSubstrateRecipe(deletedRecipeName); _eventAggregator.Publish(new SubstrateDeletedEventArgs { DeletedName = deletedRecipeName, }); } public override void OnCopyRecipe(object sender, EventArgs eventArgs) { base.OnCopyRecipe(sender, eventArgs); } public override void OnImportRecipe(object sender, EventArgs eventArgs) { if (eventArgs is RecipeEventArgs args) { } } public override void OnExportRecipe(object sender, EventArgs eventArgs) { if (eventArgs is RecipeEventArgs args) { } } public override void OnReNameRecipe(object sender, EventArgs eventArgs) { if (eventArgs is RecipeRenameEventArgs args) { if (TryRenameRecipe(args)) { _eventAggregator.Publish(new SubstrateNameChangedEventArgs { OldName = args.OldRecipeName, NewName = args.NewRecipeName, }); } } } public void Handle(WaferNameChangedEventArgs message) { } private string GetRecipeDifferenceSummary() { if (SubstrateRecipe == null) return string.Empty; var path = System.IO.Path.Combine(SubstrateRecipe.Dir ?? string.Empty, SubstrateRecipe.FileName ?? string.Empty); if (!System.IO.File.Exists(path)) return "1. 当前配方文件尚未保存到磁盘"; var currentSnapshot = SubstrateRecipe.CreateSnapshot(); if (string.Equals(LastSavedSnapshot ?? string.Empty, currentSnapshot, StringComparison.Ordinal)) return string.Empty; try { var originalToken = JToken.Parse(LastSavedSnapshot ?? string.Empty); var currentToken = JToken.Parse(currentSnapshot); var differences = new List(); CollectDifferences(originalToken, currentToken, string.Empty, differences, 6); if (differences.Count == 0) return "1. 当前配方内容与上次保存快照不一致"; return string.Join("\r\n", differences); } catch { return "1. 当前配方内容与上次保存快照不一致"; } } private void CollectDifferences(JToken originalToken, JToken currentToken, string path, IList differences, int maxCount) { if (differences.Count >= maxCount) return; if (JToken.DeepEquals(originalToken, currentToken)) return; if (originalToken == null || currentToken == null) { differences.Add($"{differences.Count + 1}. {FormatDifference(path, originalToken, currentToken)}"); return; } if (originalToken.Type == JTokenType.Object && currentToken.Type == JTokenType.Object) { var originalObject = (JObject)originalToken; var currentObject = (JObject)currentToken; var propertyNames = originalObject.Properties().Select(p => p.Name) .Union(currentObject.Properties().Select(p => p.Name)) .Distinct() .ToList(); foreach (var propertyName in propertyNames) { var nextPath = string.IsNullOrWhiteSpace(path) ? propertyName : $"{path}.{propertyName}"; CollectDifferences(originalObject[propertyName], currentObject[propertyName], nextPath, differences, maxCount); if (differences.Count >= maxCount) return; } return; } if (originalToken.Type == JTokenType.Array && currentToken.Type == JTokenType.Array) { var originalArray = (JArray)originalToken; var currentArray = (JArray)currentToken; var maxLength = Math.Max(originalArray.Count, currentArray.Count); for (var i = 0; i < maxLength; i++) { var nextPath = $"{path}[{i}]"; var originalItem = i < originalArray.Count ? originalArray[i] : null; var currentItem = i < currentArray.Count ? currentArray[i] : null; CollectDifferences(originalItem, currentItem, nextPath, differences, maxCount); if (differences.Count >= maxCount) return; } return; } differences.Add($"{differences.Count + 1}. {FormatDifference(path, originalToken, currentToken)}"); } private string FormatDifference(string path, JToken originalToken, JToken currentToken) { var displayPath = string.IsNullOrWhiteSpace(path) ? "配方根节点" : path; return $"{displayPath}:{FormatTokenValue(originalToken)} -> {FormatTokenValue(currentToken)}"; } private string FormatTokenValue(JToken token) { if (token == null || token.Type == JTokenType.Null || token.Type == JTokenType.Undefined) return "空"; var value = token.ToString(Newtonsoft.Json.Formatting.None); if (value.Length > 80) return value.Substring(0, 80) + "..."; return value; } #region//Methods public void AddWaferSelectInfo() { if (SubstrateRecipe == null) { MwMessageBox.Show("请先选择或创建基板配方"); return; } SubstrateRecipe.WaferSelectInfos.Add(new Models.SubstrateParameter.WaferSelectInfo()); } private void DeleteSelectWaferInfo(object obj) { if (obj is Models.SubstrateParameter.WaferSelectInfo info) { SubstrateRecipe.WaferSelectInfos.Remove(info); } } #endregion } }