2023-04-13 11:51:03 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Linq;
|
2023-05-10 16:21:45 +08:00
|
|
|
|
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
|
|
|
|
|
using Sicentury.Core;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-10 16:21:45 +08:00
|
|
|
|
namespace MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-05-10 23:51:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 配方步骤集合。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 该集合继承自<see cref="ObservableCollection{T}"/>。
|
|
|
|
|
/// </remarks>
|
2023-06-19 17:54:15 +08:00
|
|
|
|
public sealed class RecipeStepCollection : ObservableCollection<RecipeStep>, IDisposable
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
#region Variables
|
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
public event EventHandler<bool> OnSavedStateChanged;
|
2023-06-15 14:53:21 +08:00
|
|
|
|
public event EventHandler HighlightStateChanged;
|
2023-06-15 19:19:41 +08:00
|
|
|
|
public event EventHandler GasFlowCalculated;
|
2023-06-13 16:10:57 +08:00
|
|
|
|
|
2023-06-19 17:54:15 +08:00
|
|
|
|
private bool _isLoadingRecipe;
|
2023-06-13 16:10:57 +08:00
|
|
|
|
private bool? _isAllStepsSelected;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
private bool _isSaved;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
2023-05-10 23:51:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建配方步骤集合的实例。
|
|
|
|
|
/// </summary>
|
2023-05-11 00:29:29 +08:00
|
|
|
|
public RecipeStepCollection()
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
_isSaved = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-10 23:51:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建配方步骤集合的实例。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="parent">
|
|
|
|
|
/// 父级配方对象,请参考<see cref="RecipeData"/>。
|
|
|
|
|
/// </param>
|
2023-05-11 08:45:22 +08:00
|
|
|
|
public RecipeStepCollection(RecipeData parent) : this()
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-05-10 16:21:45 +08:00
|
|
|
|
Parent = parent;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-10 23:51:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建配方步骤集合的实例。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="steps">配方步骤数组。</param>
|
|
|
|
|
/// <param name="parent">
|
|
|
|
|
/// 父级配方对象,请参考<see cref="RecipeData"/>。
|
|
|
|
|
/// </param>
|
2023-05-11 00:29:29 +08:00
|
|
|
|
public RecipeStepCollection(IList<RecipeStep> steps, RecipeData parent)
|
|
|
|
|
: base(steps)
|
2023-05-10 16:21:45 +08:00
|
|
|
|
{
|
|
|
|
|
_isSaved = true;
|
|
|
|
|
Parent = parent;
|
|
|
|
|
|
|
|
|
|
foreach (var step in steps)
|
|
|
|
|
step.Parent = parent;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2023-05-10 16:21:45 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
#region Properties
|
|
|
|
|
|
2023-05-10 23:51:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 返回父级配方对象的实例。
|
|
|
|
|
/// </summary>
|
2023-05-10 16:21:45 +08:00
|
|
|
|
public RecipeData Parent { get; }
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 返回当前配方是否已经被保存。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsSaved
|
|
|
|
|
{
|
|
|
|
|
get => _isSaved;
|
2023-05-10 16:21:45 +08:00
|
|
|
|
private set
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
_isSaved = value;
|
|
|
|
|
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSaved)));
|
2023-06-13 16:10:57 +08:00
|
|
|
|
OnSavedStateChanged?.Invoke(this, _isSaved);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool? IsAllStepsSelected
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var groupSelection = this.GroupBy(x => x.IsSelected).ToList();
|
|
|
|
|
if (groupSelection.Count == 1)
|
|
|
|
|
_isAllStepsSelected = groupSelection[0].Key;
|
|
|
|
|
else
|
|
|
|
|
_isAllStepsSelected = null;
|
|
|
|
|
|
|
|
|
|
return _isAllStepsSelected;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (!_isAllStepsSelected.HasValue || _isAllStepsSelected == true)
|
|
|
|
|
DeselectAll();
|
|
|
|
|
else
|
|
|
|
|
SelectAll();
|
|
|
|
|
|
|
|
|
|
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAllStepsSelected)));
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 返回被选中的配方步骤。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IReadOnlyList<RecipeStep> SelectedSteps =>
|
|
|
|
|
this.ToList().Where(x => x.StepNoParam != null && x.StepNoParam.IsChecked).ToList();
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 当配方中的某步骤的IsSaved状态发生变化时,触发此事件。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
private void ItemOnSavedStateChanged(object sender, bool e)
|
|
|
|
|
{
|
|
|
|
|
if (e == false)
|
|
|
|
|
IsSaved = false;
|
|
|
|
|
else
|
|
|
|
|
IsSaved = (this.ToList().FirstOrDefault(x => x.IsSaved == false) == null);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 当配方步骤的选中状态发生变化时,触发此事件。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
private void StepOnSelectionStateChanged(object sender, bool e)
|
|
|
|
|
{
|
|
|
|
|
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAllStepsSelected)));
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 更新Step序列号。
|
|
|
|
|
/// </summary>
|
2023-05-11 00:29:29 +08:00
|
|
|
|
/// <param name="markAsSaved"></param>
|
|
|
|
|
public void UpdateStepIndex(bool markAsSaved = false)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
var step = this.FirstOrDefault();
|
|
|
|
|
|
2023-05-10 16:21:45 +08:00
|
|
|
|
var stepNo = step?.StepNoParam;
|
|
|
|
|
if (stepNo == null)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return;
|
|
|
|
|
|
2023-06-15 19:19:41 +08:00
|
|
|
|
var index = RecipeFormatBuilder.STEP_INDEX_BASE;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
while (true)
|
|
|
|
|
{
|
2023-05-10 16:21:45 +08:00
|
|
|
|
stepNo.Value = index;
|
2023-05-11 00:29:29 +08:00
|
|
|
|
if (markAsSaved)
|
2023-05-10 16:21:45 +08:00
|
|
|
|
stepNo.Save();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
//! 最后一个步骤的Next应该为Null,此时跳出循环。
|
2023-05-10 16:21:45 +08:00
|
|
|
|
if (stepNo.Next is StepParam nextParam)
|
|
|
|
|
stepNo = nextParam;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-19 17:54:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 开始加载Recipe。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IDisposable BeginLoading()
|
|
|
|
|
{
|
|
|
|
|
_isLoadingRecipe = true;
|
|
|
|
|
IsSaved = true;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 结束加载Recipe
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void EndLoading()
|
|
|
|
|
{
|
|
|
|
|
this.ToList().ForEach(step =>
|
|
|
|
|
{
|
|
|
|
|
step.Validate();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_isLoadingRecipe = false;
|
|
|
|
|
IsSaved = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 选择当前集合中的所有配方步骤。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void SelectAll()
|
|
|
|
|
{
|
|
|
|
|
foreach (var step in this)
|
|
|
|
|
{
|
|
|
|
|
step.IsSelected = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 取消选择当前集合中的所有配方步骤。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void DeselectAll()
|
|
|
|
|
{
|
|
|
|
|
foreach (var step in this)
|
|
|
|
|
{
|
|
|
|
|
step.IsSelected = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 反向选择当前集合中的所有配方步骤。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ReverseSelection()
|
|
|
|
|
{
|
|
|
|
|
foreach (var step in this)
|
|
|
|
|
{
|
|
|
|
|
step.IsSelected = !step.IsSelected;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 保存当前配方。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Save()
|
|
|
|
|
{
|
|
|
|
|
this.ToList().ForEach(x => x.Save());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 取消所有参数的Highlight状态。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ResetHighlight()
|
|
|
|
|
{
|
|
|
|
|
this.ToList().ForEach(x => x.ResetHighlight());
|
|
|
|
|
}
|
2023-06-15 14:53:21 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Overrided Methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
///
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="previous"></param>
|
|
|
|
|
/// <param name="next"></param>
|
|
|
|
|
private static void LinkSteps(RecipeStep previous, RecipeStep next)
|
|
|
|
|
{
|
|
|
|
|
if (previous != null && next != null)
|
|
|
|
|
{
|
|
|
|
|
previous.SetNextStep(next);
|
|
|
|
|
next.SetPreviousStep(previous);
|
|
|
|
|
}
|
|
|
|
|
else if (previous == null && next != null)
|
|
|
|
|
{
|
|
|
|
|
next.SetPreviousStep(null);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
previous?.SetNextStep(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void SetItem(int index, RecipeStep item)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
protected override void InsertItem(int index, RecipeStep step)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-05-10 16:21:45 +08:00
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
if (step != null)
|
2023-05-10 16:21:45 +08:00
|
|
|
|
{
|
|
|
|
|
// 如果Step没有分配Uid,先分配一个。
|
2023-06-13 16:10:57 +08:00
|
|
|
|
if (string.IsNullOrEmpty(step.StepUid))
|
2023-05-10 16:21:45 +08:00
|
|
|
|
{
|
|
|
|
|
// 尝试20次,如果均重号,则报错。
|
|
|
|
|
var retry = 0;
|
|
|
|
|
for (retry = 0; retry < 20; retry++)
|
|
|
|
|
{
|
|
|
|
|
var uid = GlobalDefs.GetShortUid();
|
|
|
|
|
if (this.FirstOrDefault(x => x.StepUid == uid) == null)
|
|
|
|
|
{
|
2023-06-13 16:10:57 +08:00
|
|
|
|
step.StepUid = uid;
|
2023-05-10 16:21:45 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (retry == 20)
|
|
|
|
|
throw new Exception("unable to add recipe step since the exclusive UID can not be assigned.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
base.InsertItem(index, step);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-06-13 16:10:57 +08:00
|
|
|
|
if (step != null)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-06-13 16:10:57 +08:00
|
|
|
|
step.Parent = Parent;
|
|
|
|
|
step.SavedStateChanged += ItemOnSavedStateChanged;
|
|
|
|
|
step.SelectionStateChanged += StepOnSelectionStateChanged;
|
2023-06-15 14:53:21 +08:00
|
|
|
|
step.HighlightStateChanged += HighlightStateChanged;
|
2023-06-15 19:19:41 +08:00
|
|
|
|
step.GasFlowCalculated += GasFlowCalculated;
|
2023-06-15 14:53:21 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
if (index > 0 && index < Count - 1)
|
|
|
|
|
{
|
|
|
|
|
LinkSteps(this[index - 1], this[index]);
|
|
|
|
|
LinkSteps(this[index], this[index + 1]);
|
|
|
|
|
}
|
|
|
|
|
else if (index == 0)
|
|
|
|
|
{
|
|
|
|
|
if (Count > 1)
|
|
|
|
|
{
|
|
|
|
|
LinkSteps(null, this[index]);
|
|
|
|
|
LinkSteps(this[index], this[index + 1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LinkSteps(null, this[index]);
|
|
|
|
|
LinkSteps(this[index], null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (index == Count - 1)
|
|
|
|
|
{
|
|
|
|
|
LinkSteps(this[index - 1], this[index]);
|
|
|
|
|
LinkSteps(this[index], null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-19 17:54:15 +08:00
|
|
|
|
// 如果不是加载Recipe状态,则为新增步骤,需要校验并更新保存状态
|
|
|
|
|
if (!_isLoadingRecipe)
|
|
|
|
|
{
|
|
|
|
|
UpdateStepIndex();
|
|
|
|
|
this[index].Validate();
|
2023-06-19 17:04:40 +08:00
|
|
|
|
|
2023-06-19 17:54:15 +08:00
|
|
|
|
// 强制标记为为保存状态
|
|
|
|
|
IsSaved = false;
|
|
|
|
|
}
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void RemoveItem(int index)
|
|
|
|
|
{
|
|
|
|
|
if (Count > 0)
|
|
|
|
|
{
|
|
|
|
|
if (index >= 0 && index < Count)
|
|
|
|
|
{
|
|
|
|
|
this[index].SavedStateChanged -= ItemOnSavedStateChanged;
|
2023-06-15 14:53:21 +08:00
|
|
|
|
this[index].SelectionStateChanged -= StepOnSelectionStateChanged;
|
|
|
|
|
this[index].HighlightStateChanged -= HighlightStateChanged;
|
2023-06-15 19:19:41 +08:00
|
|
|
|
this[index].GasFlowCalculated -= GasFlowCalculated;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IsSaved = false;
|
|
|
|
|
|
|
|
|
|
// 将前后两个Param链接起来
|
|
|
|
|
var preStep = this[index].Previous;
|
|
|
|
|
var nextStep = this[index].Next;
|
|
|
|
|
LinkSteps(preStep, nextStep);
|
|
|
|
|
preStep?.Validate();
|
|
|
|
|
nextStep?.Validate();
|
|
|
|
|
|
|
|
|
|
base.RemoveItem(index);
|
|
|
|
|
|
|
|
|
|
UpdateStepIndex();
|
2023-06-19 17:04:40 +08:00
|
|
|
|
|
|
|
|
|
// 强制标记为为保存状态
|
|
|
|
|
IsSaved = false;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void ClearItems()
|
|
|
|
|
{
|
|
|
|
|
if (Count > 0)
|
|
|
|
|
{
|
2023-06-15 14:53:21 +08:00
|
|
|
|
this.ToList().ForEach(x =>
|
|
|
|
|
{
|
|
|
|
|
x.SavedStateChanged -= ItemOnSavedStateChanged;
|
|
|
|
|
x.SelectionStateChanged -= StepOnSelectionStateChanged;
|
|
|
|
|
x.HighlightStateChanged -= HighlightStateChanged;
|
2023-06-15 19:19:41 +08:00
|
|
|
|
x.GasFlowCalculated -= GasFlowCalculated;
|
2023-06-15 14:53:21 +08:00
|
|
|
|
});
|
2023-04-13 11:51:03 +08:00
|
|
|
|
IsSaved = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
IsSaved = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base.ClearItems();
|
2023-06-19 17:04:40 +08:00
|
|
|
|
|
|
|
|
|
// 强制标记为为保存状态
|
|
|
|
|
IsSaved = false;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void MoveItem(int oldIndex, int newIndex)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2023-06-19 17:54:15 +08:00
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|