using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using MECF.Framework.UI.Client.CenterViews.Configs.Roles; using MECF.Framework.UI.Client.ClientBase; using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params; namespace MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel { public class RecipeStep : ObservableCollection { #region Variables private MenuPermissionEnum _permission; private bool _isHideValue; private bool _isProcessed; private bool _isVisible; /// /// 当步骤的任意参数的属性发生变化时,引发此事件。 /// public event EventHandler SavedStateChanged; /// /// 当前步骤的选择状态发生变化时,引发此事件。 /// public event EventHandler SelectionStateChanged; /// /// 当前步骤的任意参数高亮状态发生变化时,引发此事件。 /// public event EventHandler HighlightStateChanged; /// /// 当前步骤的气体流量计算完成后,引发此事件。 /// public event EventHandler GasFlowCalculated; /// /// 当步骤的时长发生变化时,引发此事件。 /// public event EventHandler StepTimeChanged; #endregion #region Constructors public RecipeStep(RecipeStep previous) { Previous = previous; IsSaved = true; IsVisible = true; } #endregion #region Properties /// /// 设置或返回当前步骤的访问权限。 /// public MenuPermissionEnum Permission { get => _permission; set { _permission = value; this.ToList().ForEach(x => x.StepPermission = _permission); } } /// /// 父级配方集合。 /// public RecipeData Parent { get; internal set; } /// /// 返回配方步骤序号参数。 /// public StepParam StepNoParam => this.FirstOrDefault(param => param.Name == RecipeFormatBuilder.SPEC_COL_STEPNO) as StepParam; private StringParam UidParam => this.FirstOrDefault(param => param.Name == RecipeFormatBuilder.SPEC_COL_STEPUID) as StringParam; /// /// 返回当前步骤是否被选中。 /// public bool IsSelected { get => StepNoParam?.IsChecked ?? false; set { if (StepNoParam != null) StepNoParam.IsChecked = value; } } /// /// 当前配方步骤的唯一识别码。 /// public string StepUid { get => UidParam?.Value ?? null; set { var paramUid = UidParam; if (paramUid != null) { paramUid.Value = value; } } } /// /// 返回步骤序号。 /// public int? StepNo => StepNoParam?.Value; /// /// 返回步骤执行时长。 /// public double StepTime { get { var param = FindParamByControlName(RecipeControlNames.STEP_TIME); if(param is DoubleParam dblParam) return dblParam.Value; return 0d; } } /// /// 返回当前配方步骤是否已保存。 /// public bool IsSaved { get; private set; } /// /// 返回当前步骤是否已经跑完工艺。 /// public bool IsProcessed { get => _isProcessed; set { if (_isProcessed != value) { _isProcessed = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsProcessed))); } } } /// /// 返回前序配方。 /// public RecipeStep Previous { get; private set; } /// /// 返回后序配方。 /// public RecipeStep Next { get; private set; } /// /// 设置或返回当前步骤是否在DataGrid中显示。 /// public bool IsVisible { get => _isVisible; set { _isVisible = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsVisible))); } } #endregion #region Methods /// /// 当配方中的参数的属性发生变化时,触发此事件。 /// /// /// private void OnParamPropertyChanged(object sender, PropertyChangedEventArgs e) { if (sender is not IParam param) return; switch (e.PropertyName) { case nameof(Param.IsSaved): { if (param.IsSaved == false) IsSaved = false; else IsSaved = this.ToList().FirstOrDefault(x => x.IsSaved == false) == null; SavedStateChanged?.Invoke(this, IsSaved); break; } case nameof(StepParam.IsChecked): OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSelected))); SelectionStateChanged?.Invoke(this, IsSelected); break; case nameof(Param.IsHighlighted): HighlightStateChanged?.Invoke(this, EventArgs.Empty); break; } } /// /// 当配方中的参数的值发生变化时,触发此事件。 /// /// /// 该事件在DragToFill操作时会被触发。 /// 单元格编辑完成后参数值发生变化不会引发此事件,而是直接在CellEndEditing事件中处理。 /// /// /// /// private void OnParamValueChanged(object sender, object e) { if (sender is Param para and IValueParam ) { if (para.Name == RecipeControlNames.STEP_TIME) { StepTimeChanged?.Invoke(this, EventArgs.Empty); } else { //TODO 和气体不相关的参数修改后,不应该触发气体流量计算过程 CalculateGasFlow(); } } } /// /// 将指定的Param链接到前序Param。 /// /// 当前Step中的参数。 private void LinkWithPreviousParam(IParam param) { var preParam = Previous?.FirstOrDefault(x => x.Name == param.Name); param.Previous = preParam; if (preParam != null) preParam.Next = param; } /// /// 将指定的Param链接到后序Param。 /// /// 当前Step中的参数。 private void LinkWithNextParam(IParam param) { var nextParam = Next?.FirstOrDefault(x => x.Name == param.Name); param.Next = nextParam; if (nextParam != null) nextParam.Previous = param; } /// /// 从角色配置中加载当前步骤的访问权限。 /// /// internal void LoadPermission(MenuPermission mp) { var mpKey = $"Step{StepNo}"; if (mp.MenuPermissionDictionary.TryGetValue(mpKey, out var stepPerm)) Permission = stepPerm; else Permission = MenuPermissionEnum.MP_NONE; } /// /// 在当前步骤中查找指定控制名的参数。 /// /// /// 参数控制的名称。 /// /// /// 是否区分大小写。 /// /// public IParam FindParamByControlName(string controlName, bool isIgnoreCase = true) { return this.FirstOrDefault(x => string.Equals(x.Name, controlName, isIgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture)); } /// /// 校验步骤中的所有参数。 /// public void Validate() { this.ToList().ForEach(x => x.Validate()); } /// /// 计算当前步骤的气体流量。 /// public void CalculateGasFlow() { Parent?.CalculateGasFlow(this); GasFlowCalculated?.Invoke(this, EventArgs.Empty); } /// /// 取消所有参数的Highlight状态。 /// public void ResetHighlight() { this.ToList().ForEach(x => x.ResetHighlight()); } /// /// 获取高亮显示的参数。 /// /// public List GetHighlightedParams() { return this.ToList().Where(p => p.IsHighlighted).ToList(); } /// /// 保存当前配方步骤。 /// public void Save() { this.ToList().ForEach(x => x.Save()); } /// /// 設置前序步驟。 /// /// public void SetPreviousStep(RecipeStep step) { Previous = step; // 调整Step中Param的链接关系 this.ToList().ForEach(LinkWithPreviousParam); if (Previous == null) return; // 将前序步骤的后序步骤链接到我自己。 Previous.Next = this; } public void SetNextStep(RecipeStep step) { Next = step; this.ToList().ForEach(LinkWithNextParam); } /// /// 检查StepNo是否为有效值。 /// /// /// /// public static bool ValidateStepNo(int? stepNo, out int validatedStepNo) { validatedStepNo = int.MinValue; var isValid = stepNo >= RecipeFormatBuilder.STEP_INDEX_BASE; if (isValid) validatedStepNo = stepNo.Value; return isValid; } #endregion #region Override Methods protected override void InsertItem(int index, Param item) { if (item != null) { item.Parent = this; LinkWithPreviousParam(item); LinkWithNextParam(item); item.PropertyChanged += OnParamPropertyChanged; if(item is IValueParam vp) vp.OnValueChanged += OnParamValueChanged; } base.InsertItem(index, item); } protected override void SetItem(int index, Param item) { throw new NotSupportedException(); } protected override void RemoveItem(int index) { if (Count > 0) { if (index >= 0 && index < Count) { this[index].PropertyChanged -= OnParamPropertyChanged; if(this[index] is IValueParam vp) vp.OnValueChanged -= OnParamValueChanged; } } // 将前后两个Param链接起来 var preParam = this[index].Previous; var nextParam = this[index].Next; if (preParam != null && nextParam != null) { preParam.Next = nextParam; nextParam.Previous = preParam; } else if (preParam == null && nextParam != null) { nextParam.Previous = null; } else if (preParam != null) { preParam.Next = null; } IsSaved = false; SavedStateChanged?.Invoke(this, true); base.RemoveItem(index); } protected override void ClearItems() { this.ToList().ForEach(x => { x.PropertyChanged -= OnParamPropertyChanged; if(x is IValueParam vp) vp.OnValueChanged -= OnParamValueChanged; }); IsSaved = Count <= 0; SavedStateChanged?.Invoke(this, true); base.ClearItems(); } public override string ToString() { return Count > 0 ? (StepNoParam == null ? "Recipe Step, Item={Count}" : StepNoParam.ToString()) : "Empty Recipe Step"; } #endregion } }