using System.Collections.Generic;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.Util;
using Caliburn.Micro;
using MECF.Framework.Common.OperationCenter;
using System;
using MECF.Framework.Common.DataCenter;
using MECF.Framework.UI.Client.CenterViews.Editors.Recipe;
using MECF.Framework.UI.Client.ClientBase;
using System.Windows;
using Caliburn.Micro.Core;
using System.Windows.Media;
using System.Windows.Threading;
using System.Data;
using System.Diagnostics;
using System.Threading.Tasks;
using Aitex.Core.Common.DeviceData.IoDevice;
using OpenSEMI.ClientBase;
using Sicentury.Core;
using MECF.Framework.UI.Client.CenterViews.Editors;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using Action = System.Action;
using Aitex.Core.RT.Device.PmDevices;
using MECF.Framework.Common.Aitex.Core.Common.DeviceData;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.GasFlow;
using System.Threading;
using Sicentury.Core.EventArgs;
using System.Linq;
using MECF.Framework.Common.Account.Permissions;
using MECF.Framework.UI.Client.Core;
using MECF.Framework.UI.Core.Accounts;
using MECF.Framework.Common.MECF.Framework.Common.RecipeCenter.Recipe;
using MECF.Framework.Common.Equipment;
using static MECF.Framework.Common.Aitex.Core.RT.EMS.MaintainerItemCollection;
using SciChart.Core.Extensions;

namespace MECF.Framework.UI.Client.CenterViews.Modules.PM
{
    public class PMProcessViewModel : SicModuleUIViewModelBase, ISupportMultipleSystem, IHandle<CommonAggregateEvents>
    {
        private bool _readRecipeFromLocal = true;
        private string _status = "";
        private int _recipeStepNumber = 0;

        private double _tmaFlowRatio = 1;
        private double _tcsFlowRatio = 1;
        private double _hclFlowRatio = 1;
        private double _C2H4FlowRatio = 1;
        private double _sih4FlowRatio = 1;
        private double _pn2FlowRatio = 1;

        private bool _isHideRecipeValue = false;
        private Visibility _allowSaveRecipe = Visibility.Visible;
        private Visibility _allowRipRecipe = Visibility.Visible;

        private int x = 0;
        private DispatcherTimer timer;

        private readonly object _lockerLoadingRecipe = new();
        private IRecipeGasFlowCalculator _recipeGasFlowCalculator;
        private string _currentProcessingRecipe;
        private string _selectedRecipe;
        private bool _needLoadRecipe = false;
        private IProgress<int> _progressRecipeStepChanged;
        private IProgress<string> _progressLoadRecipe;
        private CancellationTokenSource _cts;

        public PMProcessViewModel()
        {
            IsBusyGasFlowSum = false;
            _busyIndicatorContentExport = new Progress<ProgressUpdatingEventArgs>(e =>
            {
                IsBusyGasFlowSum = e.Message == "" ? false : true;
                BusyIndicatorContent = e.Message;
            });

            _progressLoadRecipe = new Progress<string>(ProgLoadRecipeOnUiThread);
            _progressRecipeStepChanged = new Progress<int>(RecipeStepInProcessChanged);

        }

        #region Properties

        public bool ReadRecipeFromLocal
        {
            get => _readRecipeFromLocal;
            set
            {
                _readRecipeFromLocal = value;
                NotifyOfPropertyChange();
            }
        }

        public string RecipeToolTip => string.IsNullOrEmpty(DisplayingRecipeName) ? "" : (ReadRecipeFromLocal ? "From Local" : "From Process");
        public string RecipeToolTip2 => string.IsNullOrEmpty(DisplayingRecipeName) ? "Local Recipe" : (ReadRecipeFromLocal ? "Local Recipe" : "Processing Recipe");

        public Visibility RecipeToolTip2Visibility => string.IsNullOrEmpty(DisplayingRecipeName) ? Visibility.Hidden : Visibility.Visible;
        public Visibility AllowSaveRecipe
        {
            get => _allowSaveRecipe;
            set
            {
                _allowSaveRecipe = value;
                NotifyOfPropertyChange();
            }
        }

        //public Visibility RecipeDifferenceVisiblity => CurrentRecipe.IsChanged ? Visibility.Visible : (_recipeDiff ? Visibility.Visible : Visibility.Hidden);

        /// <summary>
        /// 指示工艺中Recipe与local不同
        /// </summary>
        public Visibility RecipeDifferenceVisiblity
        {
            get
            {
                if (CurrentRecipe != null
               && CurrentRecipe.FullName != "\\")
                {
                    if (CurrentRecipe.HasHighLight || CurrentRecipe.IsChanged)
                    {
                        return Visibility.Visible;
                    }
                    else return Visibility.Hidden;
                }
                else
                    return Visibility.Hidden;
            }
        }

        public Visibility AllowRipRecipe
        {
            get => _allowRipRecipe;
            set
            {
                _allowRipRecipe = value;
                NotifyOfPropertyChange();
            }
        }
        public bool IsAllowPush => IsPMProcess && (CurrentRecipe?.IsCompatibleWithCurrentFormat ?? false);

        public bool IsAllowSave => CurrentRecipe?.IsCompatibleWithCurrentFormat ?? false;

        public string CarbonSource { get; set; }

        [Subscription("IsBusy")]
        public bool IsBusy { get; set; }

        [Subscription("Status")]
        public string Status
        {
            get => _status;
            set
            {
                if (_status != "Process" && value == "Process")
                {
                    ReLoadProcessingRecipe();
                }
                _status = value;
            }
        }

        public bool IsPMProcess => Status == "Process" || Status == "PostProcess" || Status == "Paused" || Status == "PMMacroPause" || Status == "PMMacro" || Status == "PostPMMacro";

        public bool IsPreProcess => Status == "PreProcess" || Status == "PrePMMacro";

        [Subscription("IsOnline")]
        public bool IsOnline { get; set; }

        [Subscription("Recipe.DeviceData")]
        public AITDeviceData Recipe { get; set; }
        public float[] RecipeData1 => RecipeData;
        public float[] RecipeData = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 1, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

        public string SelectedRecipe
        {
            set => _selectedRecipe = value;
            get => _selectedRecipe;
        }


        [Subscription("SelectedRecipeName")]
        public string CurrentProcessingRecipeName
        {
            get => _currentProcessingRecipe;
            set
            {
                if (_currentProcessingRecipe != value)
                {
                    //ReadRecipeFromLocal = false;
                    _currentProcessingRecipe = value;
                    //DisplayingRecipeName = value;
                }
            }
        }

        [Subscription("RecipeStepName")]
        public string RecipeStepName { get; set; }

        [Subscription("RecipeStepNumber")]
        public int RecipeStepNumber
        {
            get => _recipeStepNumber;
            set
            {
                if (IsPMProcess)
                {
                    if (_recipeStepNumber != value)
                    {
                        _progressRecipeStepChanged.Report(value);
                    }
                    _recipeStepNumber = value;
                }
                else if (IsPreProcess)
                    _recipeStepNumber = 0;
                else
                    _recipeStepNumber = -1;
            }
        }

        [Subscription("ArH2Switch")]
        public string ArH2Switch { get; set; }

        [Subscription("N2FlowMode")]
        public string N2FlowMode { get; set; }

        [Subscription("RecipeStepElapseTime")]
        public int RecipeStepElapseTime { get; set; }

        [Subscription("RecipeStepTime")]
        public int RecipeStepTime { get; set; }

        [Subscription("RecipeStepElapseTime2")]
        public int RecipeStepElapseTime2 { get; set; }

        [Subscription("RecipeTotalElapseTime")]
        public int RecipeTotalElapseTime { get; set; }

        [Subscription("RecipeTotalTime")]
        public int RecipeTotalTime { get; set; }

        [Subscription("RecipeTotalElapseTime2")]
        public int RecipeTotalElapseTime2 { get; set; }

        public string StepNumber => RecipeStepNumber == -1 ? "--" : RecipeStepNumber.ToString();


        public string StepName
        {
            get
            {
                if (IsPMProcess)
                {
                    return $"{RecipeStepName}";
                }
                else if (IsPreProcess)
                {
                    return "0";
                }
                return "--";
            }

        }

        public string StepTime
        {
            get
            {
                if (IsPMProcess)
                {
                    return $"{RecipeStepElapseTime}/{RecipeStepTime}";
                }
                else if (IsPreProcess)
                {
                    return "0";
                }
                return "--";

            }
        }

        public string RecipeTime
        {
            get
            {
                if (IsPMProcess)
                {
                    return $"{RecipeTotalElapseTime}/{RecipeTotalTime}";
                }
                else if (IsPreProcess)
                {
                    return "0";
                }
                return "--";
            }
        }

        private string _displayingRecipeName;
        public string DisplayingRecipeName
        {
            get => _displayingRecipeName;
            private set
            {
                if (_displayingRecipeName != value)
                {
                    _displayingRecipeName = value;
                    InvokeClient.Instance.Service.DoOperation("System.SetConfig", $"PM.{SystemName}.LastRecipeName", value);
                    _progressLoadRecipe.Report(value);
                    NotifyOfPropertyChange(nameof(DisplayingRecipeName));
                }
            }
        }

        private bool _isRecipeLoading;

        public bool IsRecipeLoading
        {
            get => _isRecipeLoading;
            set
            {
                if (_isRecipeLoading != value)
                {
                    _isRecipeLoading = value;
                    NotifyOfPropertyChange();
                }
            }

        }

        public RecipeData CurrentRecipe { get; set; }

        public List<EditorDataGridTemplateColumnBase> Columns { get; set; } = new List<EditorDataGridTemplateColumnBase>();

        private RecipeFormatBuilder _recipeFormatBuilder = new();

        public bool IsSelectButtonEnable => !string.IsNullOrEmpty(Status) && !Status.Equals("Process")
             && !Status.Equals("PreProcess") && !Status.Equals("PostProcess") && !Status.Equals("Paused")
             && !IsOnline && !IsRecipeLoading;

        public bool IsStartButtonEnable => !string.IsNullOrEmpty(SelectedRecipe) && !string.IsNullOrEmpty(Status)
            && !Status.Equals("Process") && !Status.Equals("PreProcess") && !Status.Equals("PostProcess") &&
            !Status.Equals("PrePMMacro") && !Status.Equals("PMMacro") && !Status.Equals("PostPMMacro") &&
            !Status.Equals("Paused") && !IsOnline && !IsRecipeLoading;// !IsProcessRunning;

        public bool IsStopButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Process") || Status.Equals("PMMacro")) && !IsOnline;

        public bool IsAbortButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Process") || Status.Equals("PMMacro")) && !IsOnline; //|| Status.Equals("PreProcess") || Status.Equals("PostProcess") || Status.Equals("Paused"));//IsProcessRunning;

        public bool IsPauseButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Process") || Status.Equals("PMMacro")) && !IsOnline;

        public bool IsSkipButtonEnable => !string.IsNullOrEmpty(Status) && Status.Equals("Process") && !IsOnline;

        public bool IsContinueButtonEnable => !string.IsNullOrEmpty(Status) && (Status.Equals("Paused") || Status.Equals("PMMacroPause")) && !IsOnline;

        public WaferInfo MLLWafer
        {
            get
            {
                if (ModuleManager.ModuleInfos["LLH"].WaferManager.Wafers.Count > 0)
                    return ModuleManager.ModuleInfos["LLH"].WaferManager.Wafers[0];
                return null;
            }
        }

        public bool MLLHasWafer => MLLWafer.WaferStatus > 0;

        public WaferInfo PMWafer
        {
            get
            {
                if (ModuleManager.ModuleInfos[SystemName].WaferManager.Wafers.Count > 0)
                    return ModuleManager.ModuleInfos[SystemName].WaferManager.Wafers[0];
                return null;
            }
        }

        public bool PMHasWafer => PMWafer.WaferStatus > 0;

        public Dictionary<int, string> DicGas { get; set; }

        public int MLLCurrentWafer { get; set; }
        public int MLLTotalWafer { get; set; }

        // public bool IsEnableGasMap => !IsBusy && IsMaintainMode;

        //public string SystemName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public object View { get; set; }



        #endregion

        /// <summary>
        /// 获取角色权限配置.
        /// </summary>
        /// <returns></returns>
        private void InitPermissions()
        {
            var roleId = BaseApp.Instance.UserContext.Role.RoleId;
            _isHideRecipeValue = AccountClient.Instance.Service.GetMenuPermission(roleId, "Recipe.Behaviour.ShowValueInProcessView") == (int)MenuPermissionEnum.MP_NONE;
            AllowSaveRecipe = (AccountClient.Instance.Service.GetMenuPermission(roleId, "Recipe.Behaviour.AllowSaveInProcessView") != (int)MenuPermissionEnum.MP_NONE) ? Visibility.Visible : Visibility.Hidden;
            AllowRipRecipe = (AccountClient.Instance.Service.GetMenuPermission(roleId, "Recipe.Behaviour.AllowRipInProcessView") != (int)MenuPermissionEnum.MP_NONE) ? Visibility.Visible : Visibility.Hidden;

        }

        protected override void OnActivate()
        {
            try
            {
                UpScConfig();

                InitPermissions();

                //if (string.IsNullOrEmpty(DisplayingRecipeName))
                //{
                //    _needLoadRecipe = false;
                //    var recipeName =
                //        (string)QueryDataClient.Instance.Service.GetConfig($"PM.{SystemName}.LastRecipeName");
                //    DisplayingRecipeName = recipeName;
                //    _selectedRecipe = recipeName;
                //}

                //更新Recipe格式
                UpdateRecipeFormat();
                // 更新Recipe参数值显示状态
                SetRecipeValueVisibilityByCurrentRole();

                CarbonSource= (string)QueryDataClient.Instance.Service.GetConfig($"PM.{SystemName}.CarbonSource");

                //若recipe已指定，重新加载recipe
                if (CurrentRecipe != null
               && CurrentRecipe.FullName != "\\")
                {
                    var obj = QueryDataClient.Instance.Service.GetData($"{SystemName}.Status");
                    if (obj is string status)
                    {
                        if (status == "Process")
                        {
                            _status = "";//从Status执行加载
                        }
                        else
                        {
                            if (ReadRecipeFromLocal)
                            {
                                _progressLoadRecipe.Report(CurrentRecipe.FullName);
                            }
                            else
                            {
                                ReLoadProcessingRecipe();
                            }
                            //var obj2 = QueryDataClient.Instance.Service.GetData($"{SystemName}.SelectedRecipeName");
                            //if (obj2 !=null && obj2 is string currentRecipe)
                            //{
                            //    if (currentRecipe == CurrentRecipe.FullName)
                            //    {
                            //        ReLoadProcessingRecipe();
                            //    }
                            //    else
                            //    {
                            //        ReadRecipeFromLocal = true;
                            //        _progressLoadRecipe.Report(CurrentRecipe.FullName);
                            //    }
                            //}
                            //else
                            //{
                            //    ReadRecipeFromLocal = true;
                            //    _progressLoadRecipe.Report(CurrentRecipe.FullName);
                            //}
                        }
                    }
                }
                base.OnActivate();
            }
            catch (Exception ex)
            { }
        }

        private void UpScConfig()
        {
            _recipeGasFlowCalculator = RecipeGasFlowCalculatorFactory.GetGasFlowCalculator(SystemName);
        }

        protected override void OnDeactivate(bool close)
        {
            //if (RecipeDifferenceVisiblity == Visibility.Visible)
            //{
            //    if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
            //            $"Recipe {CurrentRecipe.Name} content is changed, quit will lose this changes, do you still want to quit?") == DialogButton.No)
            //    {
            //        base.OnDeactivate(false);
            //        return;
            //    }
            //}
            ActiveUpdateData = true;
            base.OnDeactivate(close);
        }

        protected override void Poll()
        {
            try
            {
                base.Poll();
            }
            catch (Exception ex)
            {

            }
        }

        protected override void InvokeAfterUpdateProperty(Dictionary<string, object> data)
        {
            try
            {
                if (_needLoadRecipe)
                    _needLoadRecipe = false;

                base.InvokeAfterUpdateProperty(data);
            }
            catch (Exception ex)
            {

            }
        }


        public void ParamsExpanded(ExpanderColumn col)
        {
            int index = this.Columns.IndexOf(col);
            for (var i = index + 1; i < this.Columns.Count; i++)
            {
                if (this.Columns[i] is ExpanderColumn)
                    break;
                this.Columns[i].Visibility = Visibility.Visible;
            }
        }

        public void ParamsCollapsed(ExpanderColumn col)
        {
            int index = this.Columns.IndexOf(col);
            for (var i = index + 1; i < this.Columns.Count; i++)
            {
                if (this.Columns[i] is ExpanderColumn)
                    break;
                this.Columns[i].Visibility = Visibility.Collapsed;
            }
        }

        protected override void OnViewLoaded(object view)
        {
            View = view;
            base.OnViewLoaded(view);

            ViewWin = (PMProcessView)view;
            StartDateTime = DateTime.Now.Date;
            EndDateTime = DateTime.Now.Date.AddDays(1).AddTicks(-1);

            /*// 如果在ReadOnly模式，则禁止解锁Recipe。
            (((PMProcessView)View)!).recipeLocker.IsEnabled = BaseApp.Instance.UserMode != UserMode.ReadOnly;*/
        }


        public void SelectRecipe()
        {
            var dialog = new RecipeSelectDialogViewModel(false, false, RecipeType.Process,SystemName);
            dialog.DisplayName = "Select Recipe";

            var wm = new WindowManager();
            var bret = wm.ShowDialog(dialog);

            if ((bool)bret)
            {
                var recipeName = dialog.DialogResult;

                if (!ReadRecipeFromLocal)
                {
                    _displayingRecipeName = ""; //重新加载一下Recipe
                }
                ReadRecipeFromLocal = true;
                DisplayingRecipeName = recipeName;

                SelectedRecipe = recipeName;

                InvokeClient.Instance.Service.DoOperation($"{SystemName}.SelectRecipe", recipeName);
            }

        }

        public void DeleteProcessData()
        {

        }

        private static void RenderStepsWithSameValue(IReadOnlyList<Param> previousStep, IReadOnlyList<Param> currentStep)
        {
            for (var i = 0; i < currentStep.Count; i++)
            {
                if (currentStep[i] is DoubleParam currDblParam && previousStep[i] is DoubleParam prevDblParam)
                {
                    currDblParam.IsEqualsToPrevious =
                        GlobalDefs.IsDoubleValueEquals(currDblParam.Value, prevDblParam.Value);
                }

                else if (currentStep[i] is ComboxParam currCbxParam && previousStep[i] is ComboxParam prevCbxParam)
                {
                    if (currCbxParam.Value != prevCbxParam.Value)
                    {
                        currCbxParam.IsEqualsToPrevious = false;
                    }
                    else
                    {
                        currCbxParam.IsEqualsToPrevious = true;
                    }
                }

            }
        }

        /// <summary>
        /// 将发生变更的配方推送到当前工艺。
        /// </summary>
        public void PushRecipeToCurrentProcess()
        {
            // 如果配方没变，不需要推送     人为修改后直接保存，会认为配方无变换导致无法推送
            //if (!CurrentRecipe.IsChanged)
            //{
            //    DialogBox.ShowWarning("The recipe does not change.");
            //    return;
            //}
            if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
            {
                DialogBox.ShowWarning("Recipe is incompatible,please wait recipe loading");
                return;
            }
            if (!CurrentRecipe.Validate())
            {
                var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING,
                    $"{CurrentRecipe.ValidationErrorSummary}\r\n Are you sure to push changes to current process?");
                if (ret == DialogButton.No)
                    return;
                /*var mbr = MessageBox.Show($"{errors}\r\n Are you sure to push changes to current process", "Warning",
                    MessageBoxButton.YesNo, MessageBoxImage.Warning);
                if (mbr == MessageBoxResult.No)
                    return;*/
            }

            if (!CurrentRecipe.GetXmlString(out var xml, out var reason))
            {
                DialogBox.ShowError($"Unable to generate recipe content, {reason}");
                return;
            }

            CurrentRecipe.HighLight();

            InvokeClient.Instance.Service.DoOperation($"{SystemName}.ReloadRecipe", xml, CurrentRecipe.HighlightDic);

        }

        /// <summary>
        /// 将数据保存到本地配方文件中
        /// </summary>
        public void SaveToBaselineRecipe()
        {
            if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
            {
                DialogBox.ShowWarning("Recipe is incompatible,please wait recipe loading");
                return;
            }

            if (string.IsNullOrEmpty(CurrentRecipe.Name))
            {
                DialogBox.ShowError("Recipe name can't be empty");
                return;
            }

            // 如果配方没变，不需要推送
            if (RecipeDifferenceVisiblity != Visibility.Visible)
            {
                DialogBox.ShowInfo("The recipe does not change.");
                return;
            }

            if (!CurrentRecipe.Validate())
            {
                var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING,
                    $"{CurrentRecipe.ValidationErrorSummary}\r\n Are you sure to save to Local recipe?");
                if (ret == DialogButton.No)
                    return;
            }

            CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
            CurrentRecipe.ReviseTime = DateTime.Now;
            CopyRecipePm1ToPm2(CurrentRecipe);
            if (!CurrentRecipe.GetXmlString(out var xml, out var reason))
            {
                DialogBox.ShowError($"Unable to generate recipe content, {reason}");
                return;
            }
            var recipeProvider = new RecipeProvider();
            var result = recipeProvider.WriteRecipeFile(CurrentRecipe.PrefixPath, CurrentRecipe.Name, xml);
            if (result)
                CurrentRecipe.MarkAsSaved();
            else
                DialogBox.ShowError("Unable to save recipe.");
        }

        /// <summary>
        /// 先同步CurrentRecipe在内存(界面所看到的，不一定是XML)中PM1和PM2中的数据，
        /// </summary>
        /// <param name="recipe"></param>
        private void CopyRecipePm1ToPm2(RecipeData recipe)
        {
            if (!recipe.SaveTo(new string[] { "PM1", "PM2" }, out var reason))
            {
                DialogBox.ShowError($"Unable to save recipe, {reason}");
                return;
            }
        }

        /// <summary>
        /// 重新加载配方，当前修改的参数被抛弃。
        /// </summary>
        public void RestoreToBaselineRecipe()
        {
            // 如果改变，提示
            if (RecipeDifferenceVisiblity == Visibility.Visible)
            {
                if (DialogBox.Confirm(
                        "The recipe has changed, are you sure to discard changes and reload baseline recipe?") == false)
                    return;
            }

            //若recipe已指定，重新加载recipe
            if (CurrentRecipe != null
           && CurrentRecipe.FullName != "\\")
            {
                var obj = QueryDataClient.Instance.Service.GetData($"{SystemName}.Status");
                if (obj is string status)
                {
                    if (status == "Process")
                    {
                        _status = "";//从Status执行加载
                    }
                    else
                    {
                        if (ReadRecipeFromLocal)
                        {
                            _progressLoadRecipe.Report(CurrentRecipe.FullName);
                        }
                        else
                        {
                            ReLoadProcessingRecipe();
                        }
                    }
                }
            }
        }

        /// <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 ? Application.Current?.Dispatcher : null;
            if (dispatcher == null || !dispatcher.CheckAccess())
                return null;

            return dispatcher;
        }


        #region Recipe相关控制函数

        public void UpdateRecipeFormat()
        {
            var role = AccountClient.Instance.Service.GetRoleByID(BaseApp.Instance.UserContext.Role.RoleId);

            if (CurrentRecipe == null)
            {
                CurrentRecipe = new RecipeData(_recipeGasFlowCalculator, RecipeType.Process);
                CurrentRecipe.BuildFormat($"Sic\\Process", SystemName, role);
            }
            else
            {
                CurrentRecipe.UpdatePermission(role);
                CurrentRecipe.SetRecipeGasFlowCalculator(_recipeGasFlowCalculator);
            }
        }

        /// <summary>
        /// 根据权限设置Recipe参数的可见性。
        /// </summary>
        private void SetRecipeValueVisibilityByCurrentRole()
        {
            // 是否在编辑器中隐藏Recipe参数值
            if (_isHideRecipeValue) // 当权限为None时隐藏，其它时显示。
                CurrentRecipe?.Steps.HideValue();
            else
                CurrentRecipe?.Steps.ShowValue();

            // 白名单的Param显示出来
            ApplyRecipeCellAccessPermissionWhitelist();
        }

        /// <summary>
        /// 从数据库读取单元格访问白名单。
        /// </summary>
        /// <returns></returns>
        private DataTable ReadRecipeCellAccessPermissionWhitelist()
        {
            if (CurrentRecipe == null)
                return null;

            var cmd =
                $"select * from recipe_cell_access_permission_whitelist where \"recipeName\" = '{CurrentRecipe.FullName}'";
            var dt = QueryDataClient.Instance.Service.QueryData(cmd);
            return dt;
        }

        /// <summary>
        /// 如果Recipe参数设置为隐藏，将白名单中的单元格显示出来。
        /// </summary>
        private void ApplyRecipeCellAccessPermissionWhitelist()
        {
            var dt = ReadRecipeCellAccessPermissionWhitelist();

            if (dt is { Rows.Count: > 0 })
            {
                foreach (DataRow dataRow in dt.Rows)
                {
                    var stepUid = dataRow["stepUid"].ToString();
                    var controlName = dataRow["columnName"].ToString();

                    var step = CurrentRecipe.Steps.FirstOrDefault(s => s.StepUid == stepUid);
                    var param = step?.FirstOrDefault(p => p.ControlName == controlName);
                    if (param != null)
                        param.IsHideValue = false;
                }
            }
        }


        /// <summary>
        /// 正在工艺中的Recipe Step发生改变。
        /// </summary>
        /// <param name="currentStepNum"></param>
        private void RecipeStepInProcessChanged(int currentStepNum)
        {
            UpdateRecipeStepProcessedStatus(currentStepNum);
        }

        private void ReLoadProcessingRecipe()
        {
            ReadRecipeFromLocal = false;
            var obj = (string)QueryDataClient.Instance.Service.GetData($"{SystemName}.SelectedRecipeName");
            if (obj != null)
            {
                _displayingRecipeName = obj;
                _progressLoadRecipe.Report(obj);
                NotifyOfPropertyChange(nameof(DisplayingRecipeName));
            }
        }
        /// <summary>
        /// 更新Recipe步骤。
        /// </summary>
        /// <param name="currentStepNum"></param>
        private void UpdateRecipeStepProcessedStatus(int currentStepNum)
        {
            if (CurrentRecipe == null)
                return;

            // Recipe不是当前正在运行的Recipe。
            if (CurrentRecipe.FullName != DisplayingRecipeName)
                return;

            CurrentRecipe.SetProcessDone(currentStepNum);

            if (CurrentRecipe.Steps.Count <= 0)
                return;

            // 自动滚到当前做工艺的Step
            var dg = ((PMProcessView)View)?.dgRecipeEditor;
            if (currentStepNum > 0)
            {
                var stepScrollTo = currentStepNum - 1;
                if (stepScrollTo < 0)
                    stepScrollTo = 0;
                if (CurrentRecipe.Steps.Count > stepScrollTo)    //加载时滚动可能会报错
                    dg?.ScrollToRecipeStep(CurrentRecipe.Steps[stepScrollTo]);

            }
        }

        /// <summary>
        /// 在UI线程加载Recipe
        /// </summary>
        /// <param name="recipeName"></param>
        private void ProgLoadRecipeOnUiThread(string recipeName)
        {
            lock (_lockerLoadingRecipe)
            {
                if (IsRecipeLoading)
                    return;

                IsRecipeLoading = true;
                NotifyOfPropertyChange(nameof(IsSelectButtonEnable));
            }

            //UpdateRecipeFormat();
            LoadRecipe(recipeName).ContinueWith(t =>
            {
                if (!t.IsCanceled && !t.IsFaulted && t.IsCompleted)
                {
                    Application.Current?.Dispatcher.Invoke(() =>
                    {
                        UpdateRecipeStepProcessedStatus(RecipeStepNumber == -1 ? 0 : RecipeStepNumber);
                    });
                }
            }).ContinueWith(t =>
            {
                Application.Current?.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
                {
                    lock (_lockerLoadingRecipe)
                    {
                        IsRecipeLoading = false;
                    }
                }));
            });

        }


        #endregion

        private async Task LoadRecipe(string selectedRecipePath)
        {
            CurrentRecipe.Clear();

            var array = selectedRecipePath.Split(new char[] { '\\' });

            var recipeName = array.Last();

            int index = selectedRecipePath.LastIndexOf("\\");

            var prefixPath = selectedRecipePath.Substring(0, index);

            //var prefixPath = selectedRecipePath.Replace(recipeName, "").TrimEnd('\\');

            var recipeProvider = new RecipeProvider();
            var recipeContent = recipeProvider.ReadRecipeFile(prefixPath, recipeName);

            var obj = QueryDataClient.Instance.Service.GetData($"{SystemName}.RecipeStepList");
            List<RecipeStepInfo> sinfos = new List<RecipeStepInfo>();
            if (obj != null)
            {
                sinfos = obj as List<RecipeStepInfo>;
            }

            CurrentRecipe.RecipeChamberType = _recipeFormatBuilder.RecipeChamberType;
            CurrentRecipe.RecipeVersion = _recipeFormatBuilder.RecipeVersion;

            var role = AccountClient.Instance.Service.GetRoleByID(BaseApp.Instance.UserContext.Role.RoleId);
            if (ReadRecipeFromLocal)
                await CurrentRecipe.LoadFile(prefixPath, recipeName, recipeContent,
                     SystemName, role, GetLoadingDispatcher());
            else
                await CurrentRecipe.LoadFile(prefixPath, recipeName, recipeContent, sinfos,
                     SystemName, role, GetLoadingDispatcher());
            // 显示Cell访问白名单中的参数
            SetRecipeValueVisibilityByCurrentRole();
        }

        public void StartProcess()
        {
            var pollBypassInterlock = $"{SystemName}.IsBypassInterlock";
            var pollBypassEnableTable = $"{SystemName}.IsBypassEnableTable";
            var pollIsService = $"{SystemName}.IsService";

            var data = QueryDataClient.Instance.Service.PollData(new[]
                { pollBypassInterlock, pollBypassEnableTable, pollIsService });

            Debug.Assert(data != null, "Unable to poll data from RT.");
            Debug.Assert(data.Count == 3, "The count of polled data is incorrect.");

            var workenable = (bool)QueryDataClient.Instance.Service.GetConfig("System.MultiProcessMode");
            if (workenable)
            {
                var isConnect = (bool)QueryDataClient.Instance.Service.GetData("MultiProcessClient.IsConnected");
                if (!isConnect)
                {
                    DialogBox.ShowError("MultiProcessClient disconnected, can not start process.");
                    return;
                }
                else
                {
                    var infos = (string)QueryDataClient.Instance.Service.GetData("MultiProcessClient.ServiceInfo");
                    if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, infos) == DialogButton.No)
                    {
                        return;
                    }
                }
            }

            if ((bool)data[pollBypassInterlock] || (bool)data[pollBypassEnableTable])
            {
                DialogBox.ShowError("Interlock or EnableTable is bypassed, can not start process.");
                return;
            }

            if ((bool)data[pollIsService])
            {
                DialogBox.ShowError($"{SystemName} is in Service Mode, can not start process.");
                return;
            }

            if (RecipeDifferenceVisiblity == Visibility.Visible)
            {
                var ret1 = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING, $"There are some differences with Local and Process Recipe({DisplayingRecipeName})\r\n\r\nAre you sure to start process?");
                if (ret1 == DialogButton.No)
                    return;
            }
            ////确认同时Process的腔体数量是否达标；配置中屏蔽此项可立即生效
            //var workenable = (bool)QueryDataClient.Instance.Service.GetConfig("System.MultiProcessMode");
            //if (workenable)
            //{
            //    //先发送一次状态
            //    InvokeClient.Instance.Service.DoOperation("MultiProcessClient.UpdateChamberState");

            //    var r = QueryDataClient.Instance.Service.GetData($"MultiProcessClient.{SystemName}.CanEnterProcessNow");
            //    if (r == null || (bool)r == false)
            //    {
            //        DialogBox.ShowError("Can't Enter ProcessIdle because of MultiProcess Limit");
            //    }
            //}

            var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.WARNING, "Please Check All Gas Ready Before Start Process!\r\n\r\nAre you sure to start process?");
            if (ret == DialogButton.No)
                return;

            var array = DisplayingRecipeName.Split(new char[] { '\\' });
            if (array[2] == "Process")
            {
                InvokeClient.Instance.Service.DoOperation($"{SystemName}.RunRecipe", DisplayingRecipeName, false, true);
            }
            else if (array[2] == "Routine")
            {
                InvokeClient.Instance.Service.DoOperation($"{SystemName}.RunPMMacro", DisplayingRecipeName);
            }
        }

        public void Skip()
        {
            InvokeClient.Instance.Service.DoOperation($"{SystemName}.RecipeSkipStep");
        }

        public void Stop()
        {
            InvokeClient.Instance.Service.DoOperation($"{SystemName}.Abort");
        }

        public void Pause()
        {
            InvokeClient.Instance.Service.DoOperation($"{SystemName}.Pause");
        }

        public void Continue()
        {
            InvokeClient.Instance.Service.DoOperation($"{SystemName}.Continue", "Step continue");
        }

        public void Abort()
        {
            InvokeClient.Instance.Service.DoOperation($"{SystemName}.Abort");
        }

        #region 腔体绑定数据
        [Subscription("TC1.L1InputTempSetPoint")]
        public float L1InputTemp { get; set; }

        [Subscription("TC1.L2InputTempSetPoint")]
        public float L2InputTemp { get; set; }

        [Subscription("TC1.L3InputTempSetPoint")]
        public float L3InputTemp { get; set; }

        [Subscription("TC2.L3InputTempSetPoint")]
        public float SCRL3InputTemp { get; set; }


        [Subscription("TC1.L1TargetSPSetPoint")]
        public float L1TargetSP { get; set; }

        [Subscription("TC1.L2TargetSPSetPoint")]
        public float L2TargetSP { get; set; }

        [Subscription("TC1.L3TargetSPSetPoint")]
        public float L3TargetSP { get; set; }

        [Subscription("TC2.L3TargetSPSetPoint")]
        public float SCRL3TargetSP { get; set; }

        [Subscription("TC1.TempCtrlTCIN")]
        public float PM1Temprature { get; set; }

        //底部温度
        [Subscription("TC1.TempCtrlTCIN")]
        public float TC1Temp2 { get; set; }

        [Subscription("TC1.HeaterModeSetPoint")]
        public float TC1HeaterMode { get; set; }


        public string TC1Mode =>
            Enum.IsDefined(typeof(HeatStrategy), (int)TC1HeaterMode)
                ? Enum.GetName(typeof(HeatStrategy), (int)TC1HeaterMode)
                : "Power";

        [Subscription("TC2.HeaterModeSetPoint")]
        public float TC2HeaterMode { get; set; }


        public string TC2Mode =>
            Enum.IsDefined(typeof(HeatStrategy), (int)TC2HeaterMode)
                ? Enum.GetName(typeof(HeatStrategy), (int)TC2HeaterMode)
                : "Power";


        [Subscription("SCR1.PowerFeedBack")]
        public float SCR1Power { get; set; }

        [Subscription("SCR2.PowerFeedBack")]
        public float SCR2Power { get; set; }

        [Subscription("SCR3.PowerFeedBack")]
        public float SCR3Power { get; set; }

        [Subscription("PSU1.OutputPowerFeedBack")]
        public float PSU1Power { get; set; }

        [Subscription("PSU2.OutputPowerFeedBack")]
        public float PSU2Power { get; set; }

        [Subscription("PSU3.OutputPowerFeedBack")]
        public float PSU3Power { get; set; }


        [Subscription("PMServo.ActualSpeedFeedback")]
        public float ActualSpeedFeedback { get; set; }

        [Subscription("PT1.FeedBack")]
        public float PT1Pressure { get; set; }
        #endregion 腔体绑定数据

        #region MFC
        [Subscription("Mfc1.DeviceData")]
        public AITMfcData Mfc1Data { get; set; }

        [Subscription("Mfc2.DeviceData")]
        public AITMfcData Mfc2Data { get; set; }

        [Subscription("Mfc3.DeviceData")]
        public AITMfcData Mfc3Data { get; set; }

        [Subscription("Mfc4.DeviceData")]
        public AITMfcData Mfc4Data { get; set; }

        [Subscription("Mfc5.DeviceData")]
        public AITMfcData Mfc5Data { get; set; }

        [Subscription("Mfc6.DeviceData")]
        public AITMfcData Mfc6Data { get; set; }

        [Subscription("Mfc7.DeviceData")]
        public AITMfcData Mfc7Data { get; set; }

        [Subscription("Mfc8.DeviceData")]
        public AITMfcData Mfc8Data { get; set; }

        [Subscription("Mfc9.DeviceData")]
        public AITMfcData Mfc9Data { get; set; }

        [Subscription("Mfc10.DeviceData")]
        public AITMfcData Mfc10Data { get; set; }

        [Subscription("Mfc11.DeviceData")]
        public AITMfcData Mfc11Data { get; set; }

        [Subscription("Mfc12.DeviceData")]
        public AITMfcData Mfc12Data { get; set; }

        [Subscription("Mfc13.DeviceData")]
        public AITMfcData Mfc13Data { get; set; }

        [Subscription("Mfc14.DeviceData")]
        public AITMfcData Mfc14Data { get; set; }

        [Subscription("Mfc15.DeviceData")]
        public AITMfcData Mfc15Data { get; set; }

        [Subscription("Mfc16.DeviceData")]
        public AITMfcData Mfc16Data { get; set; }



        [Subscription("Mfc19.DeviceData")]
        public AITMfcData Mfc19Data { get; set; }

        [Subscription("Mfc20.DeviceData")]
        public AITMfcData Mfc20Data { get; set; }



        [Subscription("Mfc22.DeviceData")]
        public AITMfcData Mfc22Data { get; set; }

        [Subscription("Mfc23.DeviceData")]
        public AITMfcData Mfc23Data { get; set; }



        [Subscription("Mfc25.DeviceData")]
        public AITMfcData Mfc25Data { get; set; }

        [Subscription("Mfc26.DeviceData")]
        public AITMfcData Mfc26Data { get; set; }

        [Subscription("Mfc27.DeviceData")]
        public AITMfcData Mfc27Data { get; set; }

        [Subscription("Mfc28.DeviceData")]
        public AITMfcData Mfc28Data { get; set; }

        [Subscription("Mfc29.DeviceData")]
        public AITMfcData Mfc29Data { get; set; }


        [Subscription("Mfc30.DeviceData")]
        public AITMfcData Mfc30Data { get; set; }

        [Subscription("Mfc31.DeviceData")]
        public AITMfcData Mfc31Data { get; set; }



        [Subscription("Mfc32.DeviceData")]
        public AITMfcData Mfc32Data { get; set; }

        [Subscription("Mfc33.DeviceData")]
        public AITMfcData Mfc33Data { get; set; }



        [Subscription("Mfc35.DeviceData")]
        public AITMfcData Mfc35Data { get; set; }

        [Subscription("Mfc36.DeviceData")]
        public AITMfcData Mfc36Data { get; set; }

        [Subscription("Mfc37.DeviceData")]
        public AITMfcData Mfc37Data { get; set; }

        [Subscription("Mfc38.DeviceData")]
        public AITMfcData Mfc38Data { get; set; }

        [Subscription("Mfc40.DeviceData")]
        public AITMfcData Mfc40Data { get; set; }
        #endregion

        #region Valve

        [Subscription("V27.DeviceData")]
        public AITValveData V27 { get; set; }

        [Subscription("V25.DeviceData")]
        public AITValveData V25 { get; set; }

        [Subscription("V31.DeviceData")]
        public AITValveData V31 { get; set; }

        [Subscription("V32.DeviceData")]
        public AITValveData V32 { get; set; }

        [Subscription("V33.DeviceData")]
        public AITValveData V33 { get; set; }

        [Subscription("V33s.DeviceData")]
        public AITValveData V33s { get; set; }

        [Subscription("V35.DeviceData")]
        public AITValveData V35 { get; set; }

        [Subscription("V36.DeviceData")]
        public AITValveData V36 { get; set; }

        [Subscription("V37.DeviceData")]
        public AITValveData V37 { get; set; }

        [Subscription("V37s.DeviceData")]
        public AITValveData V37s { get; set; }


        [Subscription("V39.DeviceData")]
        public AITValveData V39 { get; set; }

        [Subscription("V39s.DeviceData")]
        public AITValveData V39s { get; set; }

        [Subscription("V40.DeviceData")]
        public AITValveData V40 { get; set; }

        [Subscription("V40s.DeviceData")]
        public AITValveData V40s { get; set; }


        [Subscription("V41.DeviceData")]
        public AITValveData V41 { get; set; }

        [Subscription("V41s.DeviceData")]
        public AITValveData V41s { get; set; }


        [Subscription("V42.DeviceData")]
        public AITValveData V42 { get; set; }

        [Subscription("V43.DeviceData")]
        public AITValveData V43 { get; set; }

        [Subscription("V43s.DeviceData")]
        public AITValveData V43s { get; set; }


        [Subscription("V45.DeviceData")]
        public AITValveData V45 { get; set; }


        [Subscription("V46.DeviceData")]
        public AITValveData V46 { get; set; }

        [Subscription("V46s.DeviceData")]
        public AITValveData V46s { get; set; }

        [Subscription("V48.DeviceData")]
        public AITValveData V48 { get; set; }

        [Subscription("V48s.DeviceData")]
        public AITValveData V48s { get; set; }


        [Subscription("V49.DeviceData")]
        public AITValveData V49 { get; set; }

        [Subscription("V50.DeviceData")]
        public AITValveData V50 { get; set; }

        [Subscription("V50s.DeviceData")]
        public AITValveData V50s { get; set; }

        [Subscription("V51.DeviceData")]
        public AITValveData V51 { get; set; }

        [Subscription("V51s.DeviceData")]
        public AITValveData V51s { get; set; }


        [Subscription("V52.DeviceData")]
        public AITValveData V52 { get; set; }

        [Subscription("V52s.DeviceData")]
        public AITValveData V52s { get; set; }

        [Subscription("V53.DeviceData")]
        public AITValveData V53 { get; set; }

        [Subscription("V53s.DeviceData")]
        public AITValveData V53s { get; set; }

        [Subscription("V54.DeviceData")]
        public AITValveData V54 { get; set; }

        [Subscription("V54s.DeviceData")]
        public AITValveData V54s { get; set; }

        [Subscription("V55.DeviceData")]
        public AITValveData V55 { get; set; }

        [Subscription("V56.DeviceData")]
        public AITValveData V56 { get; set; }

        [Subscription("V58.DeviceData")]
        public AITValveData V58 { get; set; }

        [Subscription("V58s.DeviceData")]
        public AITValveData V58s { get; set; }



        [Subscription("V59.DeviceData")]
        public AITValveData V59 { get; set; }

        [Subscription("V60.DeviceData")]
        public AITValveData V60 { get; set; }

        [Subscription("V61.DeviceData")]
        public AITValveData V61 { get; set; }

        [Subscription("V62.DeviceData")]
        public AITValveData V62 { get; set; }

        [Subscription("V63.DeviceData")]
        public AITValveData V63 { get; set; }

        //[Subscription("V64.DeviceData")]
        //public AITValveData V64 { get; set; }

        //[Subscription("V65.DeviceData")]
        //public AITValveData V65 { get; set; }


        [Subscription("V68.DeviceData")]
        public AITValveData V68 { get; set; }

        [Subscription("V69.DeviceData")]
        public AITValveData V69 { get; set; }

        [Subscription("V70.DeviceData")]
        public AITValveData V70 { get; set; }

        [Subscription("V72.DeviceData")]
        public AITValveData V72 { get; set; }

        [Subscription("V73.DeviceData")]
        public AITValveData V73 { get; set; }

        [Subscription("V74.DeviceData")]
        public AITValveData V74 { get; set; }

        [Subscription("V75.DeviceData")]
        public AITValveData V75 { get; set; }

        [Subscription("V76.DeviceData")]
        public AITValveData V76 { get; set; }

        [Subscription("V87.DeviceData")]
        public AITValveData V87 { get; set; }

        [Subscription("V88.DeviceData")]
        public AITValveData V88 { get; set; }

        [Subscription("V89.DeviceData")]
        public AITValveData V89 { get; set; }

        [Subscription("V90.DeviceData")]
        public AITValveData V90 { get; set; }

        [Subscription("V91.DeviceData")]
        public AITValveData V91 { get; set; }

        [Subscription("V92.DeviceData")]
        public AITValveData V92 { get; set; }

        [Subscription("V93.DeviceData")]
        public AITValveData V93 { get; set; }

        [Subscription("V94.DeviceData")]
        public AITValveData V94 { get; set; }

        [Subscription("V95.DeviceData")]
        public AITValveData V95 { get; set; }

        [Subscription("V96.DeviceData")]
        public AITValveData V96 { get; set; }

        [Subscription("V97.DeviceData")]
        public AITValveData V97 { get; set; }

        [Subscription("EPV1.DeviceData")]
        public AITValveData EPV1 { get; set; }

        [Subscription("EPV2.DeviceData")]
        public AITValveData EPV2 { get; set; }

        [Subscription("V99.DeviceData")]
        public AITValveData V99 { get; set; }

        [Subscription("V99s.DeviceData")]
        public AITValveData V99s { get; set; }


        #endregion

        #region Pressure
        [Subscription("Pressure1.DeviceData")]
        public AITPressureMeterData PT1Data { get; set; }

        [Subscription("Pressure2.DeviceData")]
        public AITPressureMeterData PT2Data { get; set; }
        [Subscription("Pressure3.DeviceData")]
        public AITPressureMeterData PT3Data { get; set; }


        [Subscription("Pressure4.DeviceData")]
        public AITPressureMeterData PT4Data { get; set; }
        [Subscription("Pressure5.DeviceData")]
        public AITPressureMeterData PT5Data { get; set; }
        [Subscription("Pressure6.DeviceData")]
        public AITPressureMeterData PT6Data { get; set; }

        [Subscription("Pressure7.DeviceData")]
        public AITPressureMeterData PT7Data { get; set; }

        [Subscription("PT1.DeviceData")]
        public AITPressureMeterData ChamPress { get; set; }

        [Subscription("PT2.DeviceData")]
        public AITPressureMeterData ForelinePress { get; set; }


        #endregion

        #region 特气显示颜色



        public SolidColorBrush PN2Color => IsPN2RunMode
            ? new SolidColorBrush(Color.FromRgb(0, 0, 0))
            : new SolidColorBrush(Color.FromRgb(255, 0, 0));

        public SolidColorBrush C2H4Color => IsC2H4RunMode
            ? new SolidColorBrush(Color.FromRgb(0, 0, 0))
            : new SolidColorBrush(Color.FromRgb(255, 0, 0));

        public SolidColorBrush SIH4Color => IsSiH4RunMode
            ? new SolidColorBrush(Color.FromRgb(0, 0, 0))
            : new SolidColorBrush(Color.FromRgb(255, 0, 0));

        public SolidColorBrush HCLColor => IsHCLRunMode
            ? new SolidColorBrush(Color.FromRgb(0, 0, 0))
            : new SolidColorBrush(Color.FromRgb(255, 0, 0));

        public SolidColorBrush TMAColor => IsTMARunMode
            ? new SolidColorBrush(Color.FromRgb(0, 0, 0))
            : new SolidColorBrush(Color.FromRgb(255, 0, 0));

        public SolidColorBrush TCSColor => IsTCSRunMode
            ? new SolidColorBrush(Color.FromRgb(0, 0, 0))
            : new SolidColorBrush(Color.FromRgb(255, 0, 0));

        public SolidColorBrush PN2FlowColor => IsPN2RunMode
            ? new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))
            : new SolidColorBrush(Color.FromArgb(255, 190, 190, 190));

        public SolidColorBrush C2H4FlowColor => IsC2H4RunMode
            ? new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))
            : new SolidColorBrush(Color.FromArgb(255, 190, 190, 190));

        public SolidColorBrush SIH4FlowColor => IsSiH4RunMode
            ? new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))
            : new SolidColorBrush(Color.FromArgb(255, 190, 190, 190));

        public SolidColorBrush HCLFlowColor => IsHCLRunMode
            ? new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))
            : new SolidColorBrush(Color.FromArgb(255, 190, 190, 190));

        public SolidColorBrush TMAFlowColor => IsTMARunMode
            ? new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))
            : new SolidColorBrush(Color.FromArgb(255, 190, 190, 190));

        public SolidColorBrush TCSFlowColor => IsTCSRunMode
            ? new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))
            : new SolidColorBrush(Color.FromArgb(255, 190, 190, 190));

        #endregion



        public bool IsPN2RunMode => V37s.IsOpen && (V39.IsOpen || V40.IsOpen);//N2FlowMode == "Run";按照洪膺要求修改 2023年6月9日10:34:54
        public bool IsHCLRunMode => V54.IsOpen;
        public bool IsSiH4RunMode => V55.IsOpen && !V56.IsOpen;
        public bool IsC2H4RunMode => V59.IsOpen && !V60.IsOpen;
        public bool IsTCSRunMode => V53.IsOpen;
        public bool IsTMARunMode => V41.IsOpen && !V42.IsOpen;



        [Subscription("GasRealTimeFlow.Ar_Run.FeedBack")]
        public double ArFlow_Run { get; set; }

        [Subscription("GasRealTimeFlow.Ar_Vent.FeedBack")]
        public double ArFlow_Vent { get; set; }

        public double ArFlow => ArFlow_Run > 0 ? ArFlow_Run : ArFlow_Vent;

        public double ArMol => GetMol(ArFlow_Run);

        [Subscription("GasRealTimeFlow.H2_Run.FeedBack")]
        public double H2_Run { get; set; }

        [Subscription("GasRealTimeFlow.H2_Vent.FeedBack")]
        public double H2_Vent { get; set; }

        public double H2Flow => H2_Run > 0 ? H2_Run : H2_Vent;

        public double H2Mol => GetMol(H2_Run);

        [Subscription("GasRealTimeFlow.PN2_Run.FeedBack")]
        public double PN2_Run { get; set; }

        [Subscription("GasRealTimeFlow.PN2_Vent.FeedBackSpecial")]
        public double PN2_Vent { get; set; }

        public double PN2Flow => PN2_Run + PN2_Vent;//=> GetPN2MFCFlow(IsPN2RunMode) * _pn2FlowRatio;

        public double PN2Mol => GetMol(PN2Flow);

        [Subscription("GasRealTimeFlow.HCL_Run.FeedBack")]
        public double HCL_Run { get; set; }

        [Subscription("GasRealTimeFlow.HCL_Vent.FeedBack")]
        public double HCL_Vent { get; set; }

        public double HCLFlow => HCL_Run + HCL_Vent;

        public double HCLMol => GetMol(HCLFlow);

        [Subscription("GasRealTimeFlow.SiH4_Run.FeedBack")]
        public double SiH4_Run { get; set; }

        [Subscription("GasRealTimeFlow.SiH4_Vent.FeedBack")]
        public double SiH4_Vent { get; set; }

        public double SiH4Flow => SiH4_Run + SiH4_Vent;

        public double SIH4Mol => GetMol(SiH4Flow);

        [Subscription("GasRealTimeFlow.C2H4_Run.FeedBack")]
        public double C2H4_Run { get; set; }

        [Subscription("GasRealTimeFlow.C2H4_Vent.FeedBack")]
        public double C2H4_Vent { get; set; }

        public double C2H4Flow => C2H4_Run + C2H4_Vent;

        public double C2H4Mol => GetMol(C2H4Flow);

        [Subscription("GasRealTimeFlow.TCS_Run.FeedBack")]
        public double TCS_Run { get; set; }

        [Subscription("GasRealTimeFlow.TCS_Vent.FeedBack")]
        public double TCS_Vent { get; set; }

        public double TCSFlow => TCS_Run + TCS_Vent;

        public double TCSMol => GetMol(TCSFlow);

        [Subscription("GasRealTimeFlow.TMA_Run.FeedBack")]
        public double TMA_Run { get; set; }

        [Subscription("GasRealTimeFlow.TMA_Vent.FeedBack")]
        public double TMA_Vent { get; set; }

        public double TMAFlow => TMA_Run + TMA_Vent;

        public double TMAMol => GetMol(TMAFlow);

        public bool ISPN2Flow => PN2Flow > 0 && IsPN2RunMode;
        public bool ISHCLFlow => HCLFlow > 0 && IsHCLRunMode;
        public bool ISSiH4Flow => SiH4Flow > 0 && IsSiH4RunMode;
        public bool ISC2H4Flow => C2H4Flow > 0 && IsC2H4RunMode;
        public bool ISArFlow => ArFlow_Run > 0;
        public bool ISH2Flow => H2_Run > 0;
        public bool ISTCSFlow => TCSFlow > 0 && IsTCSRunMode;
        public bool ISTMAFlow => TMAFlow > 0 && IsTMARunMode;

        private double GetMol(double fedback)
        {
            return fedback / 1000 / 22.4141;
        }

        #region Process Data Statistic

        [Subscription("ProcessDataStat.RealtimeList")]
        public List<AITProcessRunData> ProcessRunDataList { get; set; }

        #endregion

        #region 真值表方式统计流量  
        /// <summary>
        /// 返回ViewModel绑定的视图对象。
        /// </summary>
        public PMProcessView ViewWin { get; private set; }

        public DateTime StartDateTime
        {
            get => ViewWin.wfTimeFrom.Value;
            set => ViewWin.wfTimeFrom.Value = value;
        }

        public DateTime EndDateTime
        {
            get => ViewWin.wfTimeTo.Value;
            set => ViewWin.wfTimeTo.Value = value;
        }

        private List<GasFlowSum> _RTgasFlowSumList;
        /// <summary>
        /// 从真值表中初始化集合对象
        /// </summary>
        [Subscription("PMGasFlowSumList")]
        public List<GasFlowSum> _RTGasFlowSumList
        {
            get => _RTgasFlowSumList;
            set => _RTgasFlowSumList ??= value;
        }

        private List<GasFlowSum> _gasFlowSumList;
        public List<GasFlowSum> GasFlowSumList
        {
            get
            {
                _gasFlowSumList ??= _RTGasFlowSumList;//使用真值表初始化集合
                return _gasFlowSumList;
            }
            set
            {
                _gasFlowSumList = value;
                NotifyOfPropertyChange(nameof(GasFlowSumList));
            }
        }


        private bool _isBusyGasFlowSum;
        public bool IsBusyGasFlowSum
        {
            get => _isBusyGasFlowSum;
            set
            {
                _isBusyGasFlowSum = value;
                NotifyOfPropertyChange(nameof(IsBusyGasFlowSum));
            }
        }



        private string _busyIndicatorMessage;

        public string BusyIndicatorContent
        {
            get => _busyIndicatorMessage;
            set
            {
                _busyIndicatorMessage = value;
                NotifyOfPropertyChange(nameof(BusyIndicatorContent));
            }
        }

        private readonly IProgress<ProgressUpdatingEventArgs> _busyIndicatorContentExport;

        public void Query()
        {
            if (StartDateTime > EndDateTime)
            {
                MessageBox.Show("time range invalid, start time should be early than end time.", "Error",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
                return;
            }

            _cts = new CancellationTokenSource();

            Task.Run(() =>
            {
                _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(20, 100, "Exporting Start ..."));
                Query(SystemName, StartDateTime, EndDateTime);
            });

        }

        public void CancelQuery()
        {
            Task.Run(() =>
            {
                if (_cts?.Token.CanBeCanceled == true)
                    _cts.Cancel();

                Thread.Sleep(200);
                _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(100, 100, ""));

            });
        }
        public void Query(string module, DateTime start, DateTime end)
        {
            var daySlices = DateRangeHelper.SplitInToHours(new DateRangeHelper(start, end), 12);
            List<List<GasFlowSum>> gasFlowRunVent = new List<List<GasFlowSum>>();

            //按天查询数据库，单天返回数据类型为List<GasFlowSum>
            foreach (var daySlice in daySlices)
            {
                if (_cts.IsCancellationRequested)
                {
                    _cts?.Dispose();
                    return;
                }

                gasFlowRunVent.Add(GasFlowSqlHelp.GetDayGasFlowSum(daySlice, SystemName, GasFlowSumList));

                _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(200, 200, $"{daySlice}"));
            }
            List<GasFlowSum> dayGasFlowList = _RTGasFlowSumList;

            //解析数据库返回的数据
            foreach (var items in gasFlowRunVent)
            {
                foreach (var item in items)
                {
                    var gasFlowSum = dayGasFlowList.Where(obj => item.Name.Contains(obj.Name)).First();
                    if (item.Name.Contains("Run"))
                        gasFlowSum.RunVolume += item.Volume;
                    else
                        gasFlowSum.VentVolume += item.Volume;
                }
            }

            //使用公式转换单位
            double baseValue = 0.0166667 * Math.Pow(10, -6);
            foreach (var item in dayGasFlowList)
            {
                item.RunVolume = item.RunVolume * baseValue;
                item.VentVolume = item.VentVolume * baseValue;
                item.Volume = item.RunVolume + item.VentVolume;
            }

            GasFlowSumList = dayGasFlowList;
            _busyIndicatorContentExport.Report(new ProgressUpdatingEventArgs(100, 100, ""));
        }



        #endregion

        /// <summary>
        /// 打开 ProcessMonitor
        /// </summary>
        public void ShowMonitorWindow()
        {
            // 给 OverviewViewModel 发消息，打开Monitor窗口。
            if (EventAggregator?.HandlerExistsFor(typeof(ShowCloseMonitorWinEvent)) == true)
                EventAggregator?.PublishOnUIThread(new ShowCloseMonitorWinEvent(true));
            else
                MessageBox.Show("The process has not been activated, please open Operation->Overview tab to activate the process.", "Warn",
                    MessageBoxButton.OK, MessageBoxImage.Warning);

        }

        public void Handle(CommonAggregateEvents message)
        {
            switch (message)
            {
                case CommonAggregateEvents.EnterReadOnlyMode:
                    if (View is PMProcessView v)
                        v.recipeLocker.Lock();

                    break;
                default:
                    break;
            }
        }
    }
}
