| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357 | using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.ComponentModel;using System.Diagnostics;using System.IO;using System.Linq;using System.Reflection;using System.Text.RegularExpressions;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Input;using System.Windows.Media;using System.Xml;using Aitex.UI.RecipeEditor.Core;using Aitex.UI.RecipeEditor.View;using ExcelLibrary.SpreadSheet;using Microsoft.Win32;using Xceed.Wpf.DataGrid;namespace Aitex.UI.RecipeEditor{    public class RecipeEditorControlViewModel : INotifyPropertyChanged    {        public List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>> UndoList { get; set; }        public List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>> RedoList { get; set; }        public bool IsUndoEnabled { get { return UndoList != null && UndoList.Count > 0; } }        public bool IsRedoEnabled { get { return RedoList != null && RedoList.Count > 0; } }        public ObservableCollection<RecipeRow> RecipeRows { get; set; }        private ObservableCollection<RecipeRow> _previousRecipeRows { get; set; }        private string _valueBeforeEditing { get; set; }        private string _currentRecipeVariationName = string.Empty;        private SmartCellData _currentEditingCellData { get; set; }        public List<Tuple<int/*row no*/, int/*col no*/, string/*var name*/, string/*reason*/>> Errors { get; set; }        public List<Tuple<string, string>> CreateRecipeList { get; set; }        public List<Tuple<string, string>> OpenRecipeList { get; set; }        public DataGridControl DataGridControl { get; set; }        public int CurrentRunningStepNo { get; set; }        public Visibility SingleAppElementVisibility { get; set; }        public Visibility RecipeInfoTextVisibility { get; set; }        public ICommand ShowDetailedErrInfoCommand { get; set; }        public ICommand EditRecipeInfoCommand { get; set; }        //public ICommand CellContentChangedCommand { get; set; }        public ICommand RightClickCommand { get; set; }        public ICommand OpenLocalRecipeCommand { get; set; }        public ICommand ExpandGroupCommand { get; set; }        public ICommand CollapseGroupCommand { get; set; }        public ICommand SaveRecipeCommand { get; set; }        public ICommand RecipeHelpDocCommand { get; set; }        public ICommand RecipeExport2ExcelCommand { get; set; }        public ICommand ToggleHideSameCommand { get; set; }        public ICommand UndoCommand { get; set; }        public ICommand RedoCommand { get; set; }        public ICommand SmartCellEditorLoadedCommand { get; set; }        public ICommand SmartCellEditorUnloadedCommand { get; set; }        public RecipeHead RecipeHead { get; set; }        public bool IsBusy { get; set; }        public bool IsHideSameContent { get; set; }        public string RecipeInfo { get; set; }        public HashSet<string> MaskedTechNames { get; set; }        public HashSet<string> MaskedCatalogNames { get; set; }        public event EventHandler OnSaveRecipeFile;        public event EventHandler OnLocalRecipeOpened;        public event EventHandler OnLoadRecipeContent;        public bool IsRecipeModified { get; set; }        public string UserName { set; get; }        //private List<Tuple<string, string, string>> SourceDilteInjectRestrictions { get; set; }        private Dictionary<string, string> _preDefinedRecipeVars { get; set; }        private List<Tuple<string/*varName*/, string/*check condition*/, string/*message*/>> _validationRules { get; set; }        private List<string> _checkVariables { get; set; }        private Dictionary<string, int> RowVarNameDic { get; set; }        private Dictionary<int, List<SmartCellData>> _copiedColumnDatas = new Dictionary<int, List<SmartCellData>>();        private Dictionary<string/*CatalogName*/, Dictionary<string/*GroupName*/, List<RecipeVariableDefine/*StepList*/>>> _recipeReadingFormat;        private Dictionary<string/*ModuleName*/, List<string>/*each item's control name*/> _recipeSavingFormat;        private string _currentRecipeFormatContent;        public bool IsBarcodeEnabled        {            get; set;        }        public Visibility IsBarcodeVisibility        {            get            {                return IsBarcodeEnabled ? Visibility.Visible : Visibility.Hidden;            }        }        private string _defaultEndPointValue { get; set; }        /// <summary>        /// class construction        /// </summary>        public RecipeEditorControlViewModel()        {            UndoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>();            RedoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>();            //if(SingleAppElementVisibility            CreateRecipeList = new List<Tuple<string, string>>();            OpenRecipeList = new List<Tuple<string, string>>();            foreach (var item in GetDefinedRecipeVariations())            {                CreateRecipeList.Add(new Tuple<string, string>("Configuration:" + item, item));                OpenRecipeList.Add(new Tuple<string, string>("Convert to configuration:" + item, item));            }            RecipeInfoTextVisibility = Visibility.Collapsed;            SingleAppElementVisibility = Visibility.Collapsed;            RightClickCommand = new DelegatedCommand((o) => true, (o) => RightClickAction());            ExpandGroupCommand = new DelegatedCommand((o) => true, (o) => ExpandGroups());            CollapseGroupCommand = new DelegatedCommand((o) => true, (o) => CollapseGroups());            SaveRecipeCommand = new DelegatedCommand((o) => true, (o) => SaveRecipeFile());            OpenLocalRecipeCommand = new DelegatedCommand((o) => true, (o) => OpenLocalRecipe());            ShowDetailedErrInfoCommand = new DelegatedCommand((o) => true, (o) => ShowDetailedErrInformation());            EditRecipeInfoCommand = new DelegatedCommand((o) => true, (o) => EditRecipeInformation());            RecipeHelpDocCommand = new DelegatedCommand((o) => true, (o) => ShowHelpDocView());            RecipeExport2ExcelCommand = new DelegatedCommand((o) => true, (o) => Export2Excel());            ToggleHideSameCommand = new DelegatedCommand((o) => true, (o) => ToggleHideSameContent());            UndoCommand = new DelegatedCommand((o) => true, (o) => UndoOperation(o));            RedoCommand = new DelegatedCommand((o) => true, (o) => RedoOperation(o));            SmartCellEditorLoadedCommand = new DelegatedCommand((o) => true, (o) => SmartCellEditorLoaded(o));            SmartCellEditorUnloadedCommand = new DelegatedCommand((o) => true, (o) => SmartCellEditorUnloaded(o));        }        /// <summary>        /// When smart cell editor loaded        /// </summary>        /// <param name="obj"></param>        private void SmartCellEditorLoaded(object obj)        {            _currentEditingCellData = obj as SmartCellData;            if (_currentEditingCellData == null) return;            _valueBeforeEditing = _currentEditingCellData.Value;            _previousRecipeRows = CloneRecipeRowData(RecipeRows);        }        /// <summary>        /// make a clone of current recipe row data        /// </summary>        /// <returns></returns>        private ObservableCollection<RecipeRow> CloneRecipeRowData(ObservableCollection<RecipeRow> oldData)        {            ObservableCollection<RecipeRow> newRecipeRows = new ObservableCollection<RecipeRow>();            foreach (var item in oldData)            {                var newRecipeRow = new RecipeRow() { CatalogName = item.CatalogName, FriendlyName = item.FriendlyName, TechnicalName = item.TechnicalName, RecipeItems = new ObservableCollection<SmartCellData>() };                newRecipeRows.Add(newRecipeRow);                foreach (var item2 in item.RecipeItems)                {                    var newRecipeItem = new SmartCellData() { Background = item2.Background, ErrInfo = item2.ErrInfo, FontWeight = item2.FontWeight, Foreground = item2.Foreground, IsHidden = item2.IsHidden, IsMasked = item2.IsMasked, IsRunning = item2.IsRunning, RecipeVariableDefine = item2.RecipeVariableDefine, Value = item2.Value };                    newRecipeRow.RecipeItems.Add(newRecipeItem);                }            }            return newRecipeRows;        }        /// <summary>        /// When smart cell editor unloaded        /// </summary>        /// <param name="obj"></param>        private void SmartCellEditorUnloaded(object obj)        {            if (_currentEditingCellData != null && _currentEditingCellData.Value != _valueBeforeEditing)            {                IsRecipeModified = true;                var ret = OverrideSelectedCellsData();                if (ret < 2)                    AddUndoHistory(string.Format("Step {0}({1})",                        _currentEditingCellData.ColIndex + 1,                        _currentEditingCellData.RecipeVariableDefine.CatalogName + _currentEditingCellData.RecipeVariableDefine.FriendlyName), _previousRecipeRows);                else                    AddUndoHistory(string.Format("Step {0} ({1}) and other {2} cell data",                        _currentEditingCellData.ColIndex + 1,                        _currentEditingCellData.RecipeVariableDefine.CatalogName + _currentEditingCellData.RecipeVariableDefine.FriendlyName, ret), _previousRecipeRows);            }            RefreshCellsDisplay(false);        }        /// <summary>        /// Adding undo history        /// </summary>        /// <param name="description"></param>        /// <param name="recipeRows"></param>        private void AddUndoHistory(string description, ObservableCollection<RecipeRow> recipeRows)        {            System.Diagnostics.Debug.WriteLine(description);            if (recipeRows == null)                return;            string shortDesc = "";            if (description.Length > 50)            {                shortDesc = description.Substring(0, 50);                shortDesc += "...";            }            else            {                shortDesc = description;            }            UndoList.Insert(0, new Tuple<string, string, ObservableCollection<RecipeRow>, Guid>(shortDesc, description, recipeRows, Guid.NewGuid()));            UndoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>(UndoList.ToList());            RedoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>();            InvokePropertyChanged("UndoList");            InvokePropertyChanged("RedoList");            InvokePropertyChanged("IsUndoEnabled");            InvokePropertyChanged("IsRedoEnabled");        }        /// <summary>        /// Redo something here        /// </summary>        private void RedoOperation(object obj)        {            try            {                if (RedoList == null || RedoList.Count == 0)                    return;                Guid redoId = Guid.Empty;                if (obj == null)                    redoId = RedoList[0].Item4;                else                    redoId = (Guid)obj;                var itemIndex = RedoList.FindIndex((o) => o.Item4 == redoId);                if (itemIndex < 0)                    return;                UndoList.Reverse();                for (int i = 0; i <= itemIndex; i++)                {                    if (i == 0)                        UndoList.Add(new Tuple<string, string, ObservableCollection<RecipeRow>, Guid>(RedoList[i].Item1, RedoList[i].Item2, CloneRecipeRowData(RecipeRows), RedoList[i].Item4));                    else                        UndoList.Add(new Tuple<string, string, ObservableCollection<RecipeRow>, Guid>(RedoList[i].Item1, RedoList[i].Item2, RedoList[i - 1].Item3, RedoList[i].Item4));                }                UndoList.Reverse();                UndoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>(UndoList.ToList());                RecipeRows = RedoList[itemIndex].Item3;                RedoList.RemoveRange(0, itemIndex + 1);                RedoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>(RedoList.ToList());                RefreshDataGrid();                RefreshCellsDisplay(false);                InvokePropertyChanged("UndoList");                InvokePropertyChanged("RedoList");                InvokePropertyChanged("IsUndoEnabled");                InvokePropertyChanged("IsRedoEnabled");            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);                MessageBox.Show("Recovery action failed!\r\n\r\n" + ex.Message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);            }        }        /// <summary>        /// Undo something here        /// </summary>        private void UndoOperation(object obj)        {            try            {                if (UndoList == null || UndoList.Count == 0)                    return;                Guid undoId = Guid.Empty;                if (obj == null)                    undoId = UndoList[0].Item4;                else                    undoId = (Guid)obj;                var itemIndex = UndoList.FindIndex((o) => o.Item4 == undoId);                if (itemIndex < 0)                    return;                RedoList.Reverse();                for (int i = 0; i <= itemIndex; i++)                {                    if (i == 0)                        RedoList.Add(new Tuple<string, string, ObservableCollection<RecipeRow>, Guid>(UndoList[i].Item1, UndoList[i].Item2, CloneRecipeRowData(RecipeRows), UndoList[i].Item4));                    else                        RedoList.Add(new Tuple<string, string, ObservableCollection<RecipeRow>, Guid>(UndoList[i].Item1, UndoList[i].Item2, UndoList[i - 1].Item3, UndoList[i].Item4));                }                RedoList.Reverse();                RedoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>(RedoList.ToList());                RecipeRows = UndoList[itemIndex].Item3;                UndoList.RemoveRange(0, itemIndex + 1);                UndoList = new List<Tuple<string, string, ObservableCollection<RecipeRow>, Guid>>(UndoList.ToList());                RefreshDataGrid();                RefreshCellsDisplay(false);                InvokePropertyChanged("UndoList");                InvokePropertyChanged("RedoList");                InvokePropertyChanged("IsUndoEnabled");                InvokePropertyChanged("IsRedoEnabled");            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);                MessageBox.Show("Revocation action failed!\r\n\r\n" + ex.Message, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);            }        }        /// <summary>        /// hide or show same content before and after one step        /// </summary>        private void ToggleHideSameContent()        {            IsHideSameContent = !IsHideSameContent;            RefreshCellsDisplay(false);        }        /// <summary>        /// get recipe variations        /// </summary>        /// <returns></returns>        public List<string> GetDefinedRecipeVariations()        {            var list1 = new List<string>();            var dir = new System.IO.FileInfo(Process.GetCurrentProcess().MainModule.FileName).Directory;            string cfgPath = dir + "\\Config\\";            var di = new System.IO.DirectoryInfo(cfgPath);            if (di.Exists)            {                foreach (var item in di.GetFiles("*.xml"))                {                    list1.Add(item.Name.Substring(0, item.Name.Length - item.Extension.Length));                }            }            return list1;        }        /// <summary>        /// Export to excel        /// </summary>        private void Export2Excel()        {            try            {                if (RecipeRows == null) return;                var dlg = new SaveFileDialog();                dlg.DefaultExt = ".xls";                dlg.Filter = "Excel Files (. xls) | *. xls | All Files (*.*)|*.*";                if (dlg.ShowDialog() == true)                {                    if (File.Exists(dlg.FileName))                        File.Delete(dlg.FileName);                    Workbook workbook = new Workbook();                    Worksheet worksheet = new Worksheet("First Sheet");                    XmlDocument doc = new XmlDocument();                    doc.LoadXml(_currentRecipeFormatContent);                    var catalogNodes = doc.SelectNodes("TableRecipeFormat/Catalog");                    int groupRowIndex = 0;                    int stepRowIndex = 0;                    int catalogRowIndex = 0;                    foreach (XmlElement cNode in catalogNodes)                    {                        XmlNodeList sNodes = cNode.SelectNodes("Group/Step");                        string catalogHeader = cNode.Attributes["DisplayName"].Value;                        worksheet.Cells[catalogRowIndex, 0] = new ExcelLibrary.SpreadSheet.Cell(catalogHeader);                        catalogRowIndex += sNodes.Count;                        XmlNodeList gNodes = cNode.SelectNodes("Group");                        foreach (XmlElement gNode in gNodes)                        {                            XmlNodeList stepNodes = gNode.SelectNodes("Step");                            string groupHeader = gNode.Attributes["DisplayName"].Value;                            worksheet.Cells[groupRowIndex, 1] = new ExcelLibrary.SpreadSheet.Cell(groupHeader);                            groupRowIndex += stepNodes.Count;                            foreach (XmlElement nd in stepNodes)                            {                                string stepHeader = nd.Attributes["DisplayName"].Value;                                worksheet.Cells[stepRowIndex, 2] = new ExcelLibrary.SpreadSheet.Cell(stepHeader);                                stepRowIndex++;                            }                        }                    }                    string recipeContent = GetRecipeContentString();                    XmlDocument rcp = new XmlDocument();                    rcp.LoadXml(recipeContent);                    XmlNodeList stepNodeList = rcp.SelectNodes("/TableRecipeData/Step");                    for (int stepNo = 0; stepNo < stepNodeList.Count; stepNo++)                    {                        var dataDic = new Dictionary<string, string>();                        foreach (XmlAttribute att in (stepNodeList[stepNo] as XmlElement).Attributes)                        {                            dataDic.Add(att.Name, att.Value);                        }                        //fill sub node's attributes                        foreach (XmlElement subStep in stepNodeList[stepNo].ChildNodes)                        {                            foreach (XmlAttribute att in subStep.Attributes)                            {                                dataDic.Add(att.Name, att.Value);                            }                            foreach (XmlElement subsubStep in subStep.ChildNodes)                            {                                foreach (XmlAttribute att2 in subsubStep.Attributes)                                {                                    dataDic.Add(att2.Name, att2.Value);                                }                            }                        }                        long length = dataDic.LongCount();                        int step = 0;                        foreach (string key in dataDic.Keys)                        {                            worksheet.Cells.ColumnWidth[(ushort)step] = 4000;                            var cellContent = dataDic[key];                            double v;                            if (double.TryParse(cellContent, out v))                            {                                worksheet.Cells[step, stepNo + 3] = new ExcelLibrary.SpreadSheet.Cell(v);                            }                            else                            {                                worksheet.Cells[step, stepNo + 3] = new ExcelLibrary.SpreadSheet.Cell(cellContent);                            }                            step++;                        }                    }                    workbook.Worksheets.Add(worksheet);                    workbook.Save(dlg.FileName);                    MessageBox.Show(string.Format("Successfully exported to Excel file!\r\n\r\n{0}", dlg.FileName), "Export", MessageBoxButton.OK, MessageBoxImage.Information);                }            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);                MessageBox.Show("Exception occurred while exporting Excel file!\r\n\r\n" + ex.Message, "Export", MessageBoxButton.OK, MessageBoxImage.Error);            }        }        /// <summary>        /// Show recipe information        /// </summary>        private void ShowHelpDocView()        {            var view = RecipeHelpView.Instance;            view.Owner = Application.Current.MainWindow;            view.DataContext = this;            if (view.Visibility == Visibility.Visible)                view.Hide();            else                view.Show();        }        /// <summary>        /// Edit recipe information        /// </summary>        private void EditRecipeInformation()        {            RecipeInfoEditor editor = new RecipeInfoEditor() { RecipeHead = this.RecipeHead, Owner = Application.Current.MainWindow };            editor.ShowDialog();            InvokePropertyChanged("RecipeHead");        }        public void EnableBarcode(bool enable)        {            IsBarcodeEnabled = enable;            InvokePropertyChanged("IsBarcodeVisibility");        }        public void SetCurrentUser(string user)        {            UserName = user;            InvokePropertyChanged("UserName");        }        public void SetEndPointDefaultValue(string defaultValue)        {            _defaultEndPointValue = defaultValue;        }        /// <summary>        /// Show recipe information        /// </summary>        private void ShowDetailedErrInformation()        {            var view = ErrorInformationDetailsView.Instance;            view.Owner = Application.Current.MainWindow;            view.DataContext = this;            if (view.Visibility == Visibility.Visible)                view.Hide();            else                view.ShowDialog();        }        /// <summary>        /// Update selected cells data        /// </summary>        /// <returns>overrided cells number</returns>        private int OverrideSelectedCellsData()        {            int overridedCellsNum = 0;            if (_currentEditingCellData != null)            {                var finalCellData = _currentEditingCellData;                bool containsEditingCell = false;                foreach (var item in DataGridControl.SelectedCellRanges)                {                    int startColIndex = Math.Min(item.ColumnRange.StartIndex, item.ColumnRange.EndIndex);                    int endColIndex = Math.Max(item.ColumnRange.StartIndex, item.ColumnRange.EndIndex);                    int startRowIndex = Math.Min(item.ItemRange.StartIndex, item.ItemRange.EndIndex);                    int endRowIndex = Math.Max(item.ItemRange.StartIndex, item.ItemRange.EndIndex);                    if (startColIndex > 0 && startColIndex <= RecipeRows[0].RecipeItems.Count &&                        endColIndex > 0 && endColIndex <= RecipeRows[0].RecipeItems.Count &&                        startRowIndex >= 0 && startRowIndex < RecipeRows.Count &&                        endRowIndex >= 0 && endRowIndex < RecipeRows.Count)                    {                        for (int m = startRowIndex; m <= endRowIndex; m++)                        {                            for (int n = startColIndex; n <= endColIndex; n++)                            {                                int rowIndex = m;                                int colIndex = n - 1;                                if (RecipeRows[rowIndex].RecipeItems[colIndex] == _currentEditingCellData)                                {                                    containsEditingCell = true;                                    break;                                }                            }                        }                    }                }                if (containsEditingCell)                {                    foreach (var item in DataGridControl.SelectedCellRanges)                    {                        int startColIndex = Math.Min(item.ColumnRange.StartIndex, item.ColumnRange.EndIndex);                        int endColIndex = Math.Max(item.ColumnRange.StartIndex, item.ColumnRange.EndIndex);                        int startRowIndex = Math.Min(item.ItemRange.StartIndex, item.ItemRange.EndIndex);                        int endRowIndex = Math.Max(item.ItemRange.StartIndex, item.ItemRange.EndIndex);                        if (startColIndex > 0 && startColIndex <= RecipeRows[0].RecipeItems.Count &&                            endColIndex > 0 && endColIndex <= RecipeRows[0].RecipeItems.Count &&                            startRowIndex >= 0 && startRowIndex < RecipeRows.Count &&                            endRowIndex >= 0 && endRowIndex < RecipeRows.Count)                        {                            for (int m = startRowIndex; m <= endRowIndex; m++)                            {                                for (int n = startColIndex; n <= endColIndex; n++)                                {                                    int rowIndex = m;                                    int colIndex = n - 1;                                    if (RecipeRows[rowIndex].RecipeItems[colIndex].RecipeVariableDefine.CellType == finalCellData.RecipeVariableDefine.CellType)                                    {                                        overridedCellsNum++;                                        RecipeRows[rowIndex].RecipeItems[colIndex].Value = finalCellData.Value;                                        RecipeRows[rowIndex].RecipeItems[colIndex].InvokePropertyChanged();                                    }                                }                            }                        }                    }                }            }            return overridedCellsNum;        }        /// <summary>        /// Open local recipe operation        /// </summary>        /// <param name="recipeDefineFilePath"></param>        public void OpenLocalRecipe(string recipeVariationName = "")        {            var dlg = new OpenFileDialog();            dlg.DefaultExt = ".rcp";            dlg.Filter = "Recipe File (.rcp)|*.rcp|All (*.*)|*.*";            if (dlg.ShowDialog() == true)            {                try                {                    XmlDocument rcpDoc = new XmlDocument();                    rcpDoc.Load(dlg.FileName);                    string recipeVariation = recipeVariationName;                    if (string.IsNullOrEmpty(recipeVariation))                    {                        var root = rcpDoc.SelectSingleNode("/TableRecipeData") as XmlElement;                        recipeVariation = root.Attributes["RecipeVersion"].Value;                    }                    var startupPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;                    string dir = System.IO.Path.GetDirectoryName(startupPath);                    List<Aitex.Controls.RecipeCompare.Item> map = CustomXmlSerializer.Deserialize<List<Aitex.Controls.RecipeCompare.Item>>(new System.IO.FileInfo(System.IO.Path.Combine(dir, "map.xml")));                    Func<Aitex.Controls.RecipeCompare.Item, bool> predicate = item => item.Name.Equals(recipeVariation);                    System.IO.FileInfo fi = new System.IO.FileInfo(string.Format("{0}\\config\\{1}.xml", dir, map.Any(predicate) ? map.Single(predicate).No : recipeVariation));                    if (fi.Exists)                    {                        XmlDocument doc = new XmlDocument();                        doc.Load(fi.FullName);                        string formatXml = doc.SelectSingleNode("/Aitex/TableRecipeFormat").OuterXml;                        string recipeXml = rcpDoc.SelectSingleNode("/TableRecipeData").OuterXml;                        LoadRecipe(formatXml, recipeXml);                        if (OnLocalRecipeOpened != null)                            OnLocalRecipeOpened.Invoke(new Tuple<string, string>(recipeVariation, dlg.FileName), null);                    }                    else                    {                        MessageBox.Show(string.Format("The description file for process program version {0} does not exist, and the process program file for this version cannot be correctly identified.", recipeVariation), "Process program failed to open",                             MessageBoxButton.OK, MessageBoxImage.Error);                    }                }                catch (Exception ex)                {                    System.Diagnostics.Debug.WriteLine(ex.Message);                    MessageBox.Show(string.Format("open recipe {0} failed,please confirm the recipe file is exist and valid。\r\n{1}", dlg.FileName, ex.Message), "Open recipe failed",                         MessageBoxButton.OK, MessageBoxImage.Error);                }            }        }        /// <summary>        /// Save recipe file        /// </summary>        private void SaveRecipeFile()        {            try            {                if (Errors != null && Errors.Count > 0)                {                    var ret = MessageBox.Show(string.Format("Recipe have {0} error,continue save?", Errors.Count), "Save", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);                    if (ret != MessageBoxResult.Yes)                        return;                }                if (OnSaveRecipeFile != null)                {                    IsRecipeModified = false;                    OnSaveRecipeFile.Invoke(GetRecipeContentString(), null);                }            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);                MessageBox.Show("save recipe failed!\r\n\r\n" + ex.Message, "Save", MessageBoxButton.OK, MessageBoxImage.Warning);            }        }        /// <summary>        /// get string typed recipe content        /// </summary>        /// <returns></returns>        public string GetRecipeContentString()        {            if (string.IsNullOrEmpty(UserName))            {                var dlg1 = new Aitex.UI.RecipeEditor.UserNameInput() { Owner = Application.Current.MainWindow };                var ret = dlg1.ShowDialog();                if (ret.HasValue && ret.Value)                {                    UserName = dlg1.UserName;                }            }            RecipeHead.LastRevisionTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");            RecipeHead.LastModifiedBy = UserName;            InvokePropertyChanged("RecipeHead");            if (RecipeRows == null || RecipeRows.Count == 0 || RecipeRows[0].RecipeItems == null || RecipeRows[0].RecipeItems.Count == 0)                return string.Empty;            //Dictionary<string/*CatalogName*/, Dictionary<string/*GroupName*/, List<RecipeVariableDefine/*StepList*/>>> _recipeReadingFormat;            XmlDocument doc = new XmlDocument();            var rootNode = doc.CreateElement("TableRecipeData");            rootNode.SetAttribute("RecipeVersion", RecipeHead.RecipeVariation);            rootNode.SetAttribute("CreatedBy", RecipeHead.CreatedBy);            rootNode.SetAttribute("CreationTime", RecipeHead.CreationTime);            rootNode.SetAttribute("LastRevisedBy", RecipeHead.LastModifiedBy);            rootNode.SetAttribute("LastRevisionTime", RecipeHead.LastRevisionTime);            if (!string.IsNullOrEmpty(RecipeHead.PressureMode))            {                rootNode.SetAttribute("PressureMode", RecipeHead.PressureMode);            }            if (!string.IsNullOrEmpty(RecipeHead.BasePressure))            {                rootNode.SetAttribute("BasePressure", RecipeHead.BasePressure);            }            if (!string.IsNullOrEmpty(RecipeHead.SubstrateTemp))            {                rootNode.SetAttribute("SubstrateTemp", RecipeHead.SubstrateTemp);            }            if (!string.IsNullOrEmpty(RecipeHead.PumpingPinState))            {                rootNode.SetAttribute("PumpingPinState", RecipeHead.PumpingPinState);            }            if(!string.IsNullOrEmpty(RecipeHead.VentingPinState))            {                rootNode.SetAttribute("VentingPinState", RecipeHead.VentingPinState);            }            if(!string.IsNullOrEmpty(RecipeHead.PinDownPressure))            {                rootNode.SetAttribute("PinDownPressure", RecipeHead.PinDownPressure);            }            if (!string.IsNullOrEmpty(RecipeHead.PumpDownLimit))            {                rootNode.SetAttribute("PumpDownLimit", RecipeHead.PumpDownLimit);            }            if (!string.IsNullOrEmpty(RecipeHead.PurgeActive))            {                rootNode.SetAttribute("PurgeActive", RecipeHead.PurgeActive);            }            if (!string.IsNullOrEmpty(RecipeHead.NotToPurgeOrVent))            {                rootNode.SetAttribute("NotToPurgeOrVent", RecipeHead.NotToPurgeOrVent);            }            rootNode.SetAttribute("Barcode", RecipeHead.Barcode);            rootNode.SetAttribute("Description", RecipeHead.Description);            doc.AppendChild(rootNode);            for (int stepIndex/* 0-> */ = 0; stepIndex < RecipeRows[0].RecipeItems.Count; stepIndex++)            {                var stepNode = doc.CreateElement("Step");                rootNode.AppendChild(stepNode);                foreach (var module in _recipeSavingFormat)                {                    var moduleName = module.Key;                    XmlElement subNode = stepNode;                    if (!string.IsNullOrEmpty(moduleName))                    {                        string[] pathArr = moduleName.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);                        for (int i = 0; i < pathArr.Length; i++)                        {                            var snd = subNode.SelectSingleNode(pathArr[i]) as XmlElement;                            if (snd == null)                            {                                var nd = doc.CreateElement(pathArr[i]);                                subNode.AppendChild(nd);                                subNode = nd;                            }                            else                            {                                subNode = snd;                            }                        }                    }                    foreach (var controlName in module.Value)                    {                        if (controlName == "StepNo")                        {                            subNode.SetAttribute(controlName, string.Format("Step {0}", stepIndex + 1));                        }                        else                        {                            var recipeRow = RecipeRows.First((o) => o.TechnicalName == controlName);                            if (recipeRow == null) continue;                            /*to compatiable with old recipe editor, always fill 'black space' to loop step*/                            if (controlName == "Loop" && string.IsNullOrEmpty(recipeRow.RecipeItems[stepIndex].Value))                                subNode.SetAttribute(controlName, " ");                            else                            {                                subNode.SetAttribute(controlName, recipeRow.RecipeItems[stepIndex].Value);                                //bool isJump = recipeRow.RecipeItems[stepIndex].IsJump;                                //if (isJump)                                //{                                //    string deviceType = recipeRow.RecipeItems[stepIndex].RecipeVariableDefine.DeviceType;                                //    if (deviceType == "MFC" || deviceType == "PC")                                //        subNode.SetAttribute(controlName + ".IsJump", isJump.ToString());                                //}                            }                        }                    }                }            }            return doc.OuterXml;        }        /// <summary>        /// expand groups        /// </summary>        private void ExpandGroups()        {            try            {                foreach (CollectionViewGroup item in DataGridControl.Items.Groups)                {                    DataGridControl.ExpandGroup(item);                }            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);            }        }        /// <summary>        /// collapse groups        /// </summary>        private void CollapseGroups()        {            try            {                foreach (CollectionViewGroup item in DataGridControl.Items.Groups)                {                    DataGridControl.CollapseGroup(item);                }            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);            }        }        /// <summary>        /// right click context menu        /// </summary>        private void RightClickAction()        {            DataGridControl.ContextMenu = null;            //get selected column information            List<int> selectedColumns = new List<int>();            foreach (var item in DataGridControl.SelectedCellRanges)            {                bool dir = item.ColumnRange.StartIndex <= item.ColumnRange.EndIndex;                if (dir)                {                    for (int i = item.ColumnRange.StartIndex; i <= item.ColumnRange.EndIndex; i++)                    {                        if (i > 0)                            if (!selectedColumns.Contains(i)) selectedColumns.Add(i);                    }                }                else                {                    for (int i = item.ColumnRange.StartIndex; i >= item.ColumnRange.EndIndex; i--)                    {                        if (i > 0)                            if (!selectedColumns.Contains(i)) selectedColumns.Add(i);                    }                }            }            selectedColumns.Sort();            //debug only            /*            string s = "";            foreach (var item in selectedColumns)            {                s += "," + item;            }            MessageBox.Show(s);             * */            //generate context menu            ContextMenu cms = new ContextMenu();            if (selectedColumns.Count > 0)            {                string colStrings = "";                foreach (var item in selectedColumns)                {                    if (string.IsNullOrEmpty(colStrings))                        colStrings = item.ToString();                    else                        colStrings += "," + item.ToString();                }                MenuItem mi;                cms.Items.Add(new MenuItem() { Header = string.Format("    Selected {0} Step", colStrings), FontFamily = new FontFamily("Arial,SimSun"), IsEnabled = false, Background = Brushes.Gray, Foreground = Brushes.White });                //copy                mi = new MenuItem() { Header = "    Copy", FontFamily = new FontFamily("Arial,SimSun"), Tag = selectedColumns.ToList() };                mi.Click += (s, e) =>                {                    try                    {                        var list = (s as MenuItem).Tag as List<int>;                        _copiedColumnDatas.Clear();                        foreach (var colId in list)                        {                            _copiedColumnDatas.Add(colId/*index from 1*/, new List<SmartCellData>());                            for (int i = 0; i < RecipeRows.Count; i++)                            {                                var oldCellData = RecipeRows[i].RecipeItems[colId - 1];                                var newCellData = new SmartCellData() { Value = oldCellData.Value, RecipeVariableDefine = oldCellData.RecipeVariableDefine };                                _copiedColumnDatas[colId].Add(newCellData);                            }                        }                    }                    catch (Exception ex)                    {                        System.Diagnostics.Debug.WriteLine(ex.Message);                    }                };                cms.Items.Add(mi);                if (selectedColumns.Count < RecipeRows[0].RecipeItems.Count)                {                    mi = new MenuItem() { Header = "    Cut", FontFamily = new FontFamily("Arial,SimSun"), Tag = selectedColumns.ToList() };                    mi.Click += (s, e) =>                    {                        try                        {                            AddUndoHistory("Cut", CloneRecipeRowData(RecipeRows));                            var list = (s as MenuItem).Tag as List<int>;                            _copiedColumnDatas.Clear();                            foreach (var colId in list)                            {                                _copiedColumnDatas.Add(colId/*index from 1*/, new List<SmartCellData>());                                for (int i = 0; i < RecipeRows.Count; i++)                                {                                    var oldCellData = RecipeRows[i].RecipeItems[colId - 1];                                    var newCellData = new SmartCellData() { Value = oldCellData.Value, RecipeVariableDefine = oldCellData.RecipeVariableDefine };                                    _copiedColumnDatas[colId].Add(newCellData);                                }                            }                            foreach (var item in RecipeRows)                            {                                var collection = new ObservableCollection<SmartCellData>();                                for (int index = 0; index < item.RecipeItems.Count; index++)                                {                                    if (list.Contains(index + 1))                                        continue;                                    collection.Add(item.RecipeItems[index]);                                }                                item.RecipeItems = collection;                            }                            RefreshDataGrid();                            RefreshCellsDisplay(false);                        }                        catch (Exception ex)                        {                            System.Diagnostics.Debug.WriteLine(ex.Message);                        }                    };                    cms.Items.Add(mi);                    mi = new MenuItem() { Header = "    Delete", FontFamily = new FontFamily("Arial,SimSun"), Tag = selectedColumns.ToList() };                    mi.Click += (s, e) =>                    {                        try                        {                            AddUndoHistory("Delete", CloneRecipeRowData(RecipeRows));                            var list = (s as MenuItem).Tag as List<int>;                            foreach (var item in RecipeRows)                            {                                var collection = new ObservableCollection<SmartCellData>();                                for (int index = 0; index < item.RecipeItems.Count; index++)                                {                                    if (list.Contains(index + 1))                                        continue;                                    collection.Add(item.RecipeItems[index]);                                }                                item.RecipeItems = collection;                            }                            RefreshDataGrid();                            RefreshCellsDisplay(false);                        }                        catch (Exception ex)                        {                            System.Diagnostics.Debug.WriteLine(ex.Message);                        }                    };                    cms.Items.Add(mi);                }                if (selectedColumns.Count == 1 && _copiedColumnDatas.Count > 0)                {                    mi = new MenuItem() { Header = string.Format("    After inserting step {0}", selectedColumns[0]/*, _copiedColumnDatas.Count*/), FontFamily = new FontFamily("Arial,SimSun"), Tag = selectedColumns[0] };                    mi.Click += (s, e) =>                    {                        try                        {                            AddUndoHistory("Insert", CloneRecipeRowData(RecipeRows));                            int insertColId = (int)(s as MenuItem).Tag;                            for (int rowIndex = 0; rowIndex < RecipeRows.Count; rowIndex++)                            {                                var collection = new ObservableCollection<SmartCellData>();                                for (int index = 0; index < insertColId; index++)                                {                                    collection.Add(RecipeRows[rowIndex].RecipeItems[index]);                                }                                foreach (var col in _copiedColumnDatas)                                {                                    collection.Add(new SmartCellData() { Value = col.Value[rowIndex].Value, RecipeVariableDefine = col.Value[rowIndex].RecipeVariableDefine });                                }                                for (int index = insertColId; index < RecipeRows[rowIndex].RecipeItems.Count; index++)                                {                                    collection.Add(RecipeRows[rowIndex].RecipeItems[index]);                                }                                RecipeRows[rowIndex].RecipeItems = collection;                            }                            RefreshDataGrid();                            RefreshCellsDisplay(false);                        }                        catch (Exception ex)                        {                            System.Diagnostics.Debug.WriteLine(ex.Message);                        }                    };                    cms.Items.Add(mi);                }            }            //show context menu            DataGridControl.ContextMenu = cms;            cms.Visibility = Visibility.Visible;        }        /// <summary>        /// Parse recipe content with recipe format        /// </summary>        /// <param name="recipeFormat"></param>        /// <param name="recipeContent"></param>        /// <param name="selectedStepNo"></param>        public void LoadRecipe(string recipeFormat, string recipeContent, int selectedStepNo = -1)        {            CurrentRunningStepNo = selectedStepNo;            IsHideSameContent = false;            IsRecipeModified = false;            RecipeHead = new RecipeEditor.RecipeHead();            DataGridControl.ContextMenu = null;            RecipeInfo = "";            IsBusy = true;            InvokePropertyChanged("IsBusy");            UndoList.Clear();            RedoList.Clear();            InvokePropertyChanged("UndoList");            InvokePropertyChanged("RedoList");            InvokePropertyChanged("IsUndoEnabled");            InvokePropertyChanged("IsRedoEnabled");            try            {                RecipeRows = new ObservableCollection<RecipeRow>();                _recipeReadingFormat = new Dictionary<string, Dictionary<string, List<RecipeVariableDefine>>>();                _recipeSavingFormat = new Dictionary<string, List<string>>();                //parse recipe format                XmlDocument formatXml = new XmlDocument();                formatXml.LoadXml(recipeFormat);                _currentRecipeFormatContent = formatXml.OuterXml;                //reading recipe variation                string recipeVariationName = formatXml.SelectSingleNode("TableRecipeFormat").Attributes["RecipeVersion"].Value;                if (String.Compare(recipeVariationName, _currentRecipeVariationName) != 0)                {                    //if variation name is different from previous loaded recipe -> clear all previous copied recipe columns                    _copiedColumnDatas.Clear();                }                _currentRecipeVariationName = recipeVariationName;                //reading recipe validation releated parameters                _preDefinedRecipeVars = new Dictionary<string, string>();                foreach (XmlElement nd in formatXml.SelectNodes("/TableRecipeFormat/Validation/Predefine/Item"))                {                    _preDefinedRecipeVars.Add(nd.Attributes["VarName"].Value, nd.Attributes["Value"].Value);                }                //reading check variables                _checkVariables = new List<string>();                foreach (XmlElement nd in formatXml.SelectNodes("/TableRecipeFormat/Validation/CheckVariable/Var"))                {                    string varName = nd.Attributes["Name"].Value;                    _checkVariables.Add(varName);                }                //reading validation rules                _validationRules = new List<Tuple<string, string, string>>();                foreach (XmlElement nd in formatXml.SelectNodes("/TableRecipeFormat/Validation/Restriction/Rule"))                {                    string varName = nd.Attributes["VarName"].Value;                    string checkCondition = nd.Attributes["CheckCondition"].Value.Replace("<", "<").Replace(">", ">");                    string message = nd.Attributes["Message"].Value.Replace("'", "\"");                    _validationRules.Add(new Tuple<string, string, string>(varName, checkCondition, message));                }                //parsing recipe format xml file                foreach (XmlElement catalogNode in formatXml.SelectNodes("/TableRecipeFormat/Catalog"))                {                    string catalogName = catalogNode.Attributes["DisplayName"].Value;                    _recipeReadingFormat.Add(catalogName, new Dictionary<string, List<RecipeVariableDefine>>());                    foreach (XmlElement groupNode in catalogNode.SelectNodes("Group"))                    {                        string groupName = groupNode.Attributes["DisplayName"].Value;                        _recipeReadingFormat[catalogName].Add(groupName, new List<RecipeVariableDefine>());                        foreach (XmlElement stepNode in groupNode.SelectNodes("Step"))                        {                            string stepModuleName = stepNode.Attributes["ModuleName"].Value;                            string stepDeviceType = stepNode.Attributes["DeviceType"].Value;                            string stepDisplayName = stepNode.Attributes["DisplayName"].Value;                            string stepControlName = stepNode.Attributes["ControlName"].Value;                            string stepInputType = stepNode.Attributes["InputType"].Value;                            string stepDescription = stepNode.HasAttribute("Desc") ? stepNode.Attributes["Desc"].Value : "";                            double stepMin = double.Parse(stepNode.HasAttribute("Min") ? stepNode.Attributes["Min"].Value : "0");                            double stepMax = double.Parse(stepNode.HasAttribute("Max") ? stepNode.Attributes["Max"].Value : "0");                            var selectionList = new List<Tuple<string, string>>();                            foreach (XmlElement selectionNode in stepNode.SelectNodes("Item"))                            {                                selectionList.Add(new Tuple<string, string>(selectionNode.Attributes["ControlName"].Value, selectionNode.Attributes["DisplayName"].Value));                            }                            if (!_recipeSavingFormat.ContainsKey(stepModuleName)) _recipeSavingFormat.Add(stepModuleName, new List<string>());                            _recipeSavingFormat[stepModuleName].Add(stepControlName);                            if (stepControlName == "StepNo")                                continue;                            _recipeReadingFormat[catalogName][groupName].Add(new RecipeVariableDefine()                            {                                CellType = (CellType)Enum.Parse(typeof(CellType), stepInputType),                                Description = stepDescription,                                DeviceType = stepDeviceType,                                DropdownItemList = selectionList,                                MaxValue = stepMax,                                //MinValue = stepDeviceType.Equals("MFC",StringComparison.InvariantCultureIgnoreCase)? stepMax*2.0/100: stepMin,                                MinValue = stepMin,                                GroupName = groupName,                                CatalogName = catalogName,                                TechnicalName = stepControlName,                                FriendlyName = stepDisplayName                            });                        }                    }                }                //parse recipe content                XmlDocument contentXml = new XmlDocument();                contentXml.LoadXml(recipeContent);                contentXml.DocumentElement.SetAttribute("RecipeVersion", _currentRecipeVariationName);                //read recipe head                //em. RecipeVersion="Ace" CreatedBy="tech" CreationTime="2013-02-02T12:36:02" LastRevisedBy="Peter" LastRevisionTime="2013-10-22T13:30:29" Description="5e46af80-49f9-4b4f-b04b-bc911fb292f8"                RecipeHead.CreatedBy = contentXml.DocumentElement.HasAttribute("CreatedBy") ? contentXml.DocumentElement.Attributes["CreatedBy"].Value : "(无)";                RecipeHead.CreationTime = contentXml.DocumentElement.HasAttribute("CreationTime") ? contentXml.DocumentElement.Attributes["CreationTime"].Value : "(无)";                RecipeHead.LastModifiedBy = contentXml.DocumentElement.HasAttribute("LastRevisedBy") ? contentXml.DocumentElement.Attributes["LastRevisedBy"].Value : "(无)";                RecipeHead.LastRevisionTime = contentXml.DocumentElement.HasAttribute("LastRevisionTime") ? contentXml.DocumentElement.Attributes["LastRevisionTime"].Value : "(无)";                if (contentXml.DocumentElement.HasAttribute("PressureMode"))                {                    RecipeHead.PressureMode = contentXml.DocumentElement.Attributes["PressureMode"].Value;                }                if (contentXml.DocumentElement.HasAttribute("BasePressure"))                {                    RecipeHead.BasePressure = contentXml.DocumentElement.Attributes["BasePressure"].Value;                }                if (contentXml.DocumentElement.HasAttribute("PumpDownLimit"))                {                    RecipeHead.PumpDownLimit = contentXml.DocumentElement.Attributes["PumpDownLimit"].Value;                }                  if (contentXml.DocumentElement.HasAttribute("SubstrateTemp"))                {                    RecipeHead.SubstrateTemp = contentXml.DocumentElement.Attributes["SubstrateTemp"].Value;                }                if (contentXml.DocumentElement.HasAttribute("PumpingPinState"))                {                    RecipeHead.PumpingPinState = contentXml.DocumentElement.Attributes["PumpingPinState"].Value;                }                if(contentXml.DocumentElement.HasAttribute("PinDownPressure"))                {                    RecipeHead.PinDownPressure = contentXml.DocumentElement.Attributes["PinDownPressure"].Value;                }                if(contentXml.DocumentElement.HasAttribute("VentingPinState"))                {                    RecipeHead.VentingPinState = contentXml.DocumentElement.Attributes["VentingPinState"].Value;                }                if (contentXml.DocumentElement.HasAttribute("PurgeActive"))                {                    RecipeHead.PurgeActive = contentXml.DocumentElement.Attributes["PurgeActive"].Value;                }                if (contentXml.DocumentElement.HasAttribute("NotToPurgeOrVent"))                {                    RecipeHead.NotToPurgeOrVent = contentXml.DocumentElement.Attributes["NotToPurgeOrVent"].Value;                }                if (contentXml.DocumentElement.HasAttribute("Barcode"))                {                    RecipeHead.Barcode = contentXml.DocumentElement.Attributes["Barcode"].Value;                }                RecipeHead.RecipeVariation = contentXml.DocumentElement.HasAttribute("RecipeVersion") ? contentXml.DocumentElement.Attributes["RecipeVersion"].Value : "(NULL)";                RecipeHead.Description = contentXml.DocumentElement.HasAttribute("Description") ? contentXml.DocumentElement.Attributes["Description"].Value : "(NULL)";                var allRecipeItems = new List<Dictionary<string, string>>();                foreach (XmlElement stepNode in contentXml.SelectNodes("/TableRecipeData/Step"))                {                    var stepDic = new Dictionary<string, string>();                    allRecipeItems.Add(stepDic);                    foreach (XmlAttribute att in stepNode.Attributes)                    {                        stepDic.Add(att.Name, att.Value);                    }                    foreach (XmlElement innerStep in stepNode.ChildNodes)                    {                        foreach (XmlAttribute att in innerStep.Attributes)                        {                            stepDic.Add(att.Name, att.Value);                        }                        foreach (XmlElement innerInnerStep in innerStep.ChildNodes)                        {                            foreach (XmlAttribute att in innerInnerStep.Attributes)                            {                                stepDic.Add(att.Name, att.Value);                            }                        }                    }                }                //generate view model                RowVarNameDic = new Dictionary<string, int>();                int rowIndex = 0;                foreach (var catalog in _recipeReadingFormat.Keys)                {                    foreach (var group in _recipeReadingFormat[catalog].Keys)                    {                        foreach (var var1 in _recipeReadingFormat[catalog][group])                        {                            var singleRow = new RecipeRow() { CatalogName = catalog, FriendlyName = /*group + */var1.FriendlyName, TechnicalName = var1.TechnicalName };                            for (int stepNo = 0; stepNo < allRecipeItems.Count; stepNo++)                            {                                var stepCell = new SmartCellData() { RecipeVariableDefine = var1 };                                if (allRecipeItems[stepNo].ContainsKey(var1.TechnicalName))                                {                                    stepCell.Value = allRecipeItems[stepNo][var1.TechnicalName];                                    //stepCell.ShowsJumpControl = false;                                    //if (var1.CellType == CellType.NumInput)                                    //{                                    //    if (var1.DeviceType == "MFC" || var1.DeviceType == "PC")                                    //    {                                    //        string jumpString = var1.TechnicalName + ".IsJump";                                    //        stepCell.IsJump = allRecipeItems[stepNo].ContainsKey(jumpString) ? bool.Parse(allRecipeItems[stepNo][jumpString]) : false;                                    //        stepCell.ShowsJumpControl = true;                                    //    }                                    //}                                }                                else                                    stepCell.Value = "n/a";                                singleRow.RecipeItems.Add(stepCell);                            }                            RecipeRows.Add(singleRow);                            RowVarNameDic.Add(var1.TechnicalName, rowIndex++);                        }                    }                }                //refresh datagrid                RefreshDataGrid();                //refresh recipe information                RefreshCellsDisplay();                if (OnLoadRecipeContent != null)                    OnLoadRecipeContent.Invoke(recipeContent, null);            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);                //LOG.Write("", ex);            }            IsBusy = false;            InvokePropertyChanged("IsBusy");        }        /// <summary>        /// 检查变量ramp rate        /// </summary>        /// <param name="stepNo"></param>        /// <param name="rampEnable"></param>        /// <param name="varName"></param>        /// <param name="rampTime"></param>        /// <param name="maxRampUpRate"></param>        /// <param name="maxRampDownRate"></param>        /// <returns>False:check ok,  True: check failed</returns>        public bool CheckRampRate(int stepNo, string rampEnable, string varName, string rampTime, double maxRampUpRate, double maxRampDownRate)        {            try            {                if (stepNo <= 0) return false;                int rowIndex = RowVarNameDic[varName];                double currentValue = double.Parse(RecipeRows[rowIndex].RecipeItems[stepNo].Value);                //if (varName == "AZone.Setpoint" || varName == "BZone.Setpoint" || varName == "CZone.Setpoint" || varName == "DZone.Setpoint")                //{                //    int heatControlModeIndex = RowVarNameDic["Heater.Mode"];                //    string curStepHeatCtrlMode = RecipeRows[heatControlModeIndex].RecipeItems[stepNo].Value;                //    string lastStepHeatCtrlMode = RecipeRows[heatControlModeIndex].RecipeItems[stepNo - 1].Value;                //    if (curStepHeatCtrlMode != lastStepHeatCtrlMode)                //    {                //        if (!(lastStepHeatCtrlMode.Equals("CurrentControl") && curStepHeatCtrlMode.Equals("PyroTempControl")))                //            return false;                //        int endbyIndex = RowVarNameDic["EndBy"];                //        int endValueIndex = RowVarNameDic["EndValue"];                //        string endby = RecipeRows[endbyIndex].RecipeItems[stepNo - 1].Value;                //        EndByCondition endbyEnum;                //        if (!Enum.TryParse(endby, out  endbyEnum)) return false;                //        if (endbyEnum != EndByCondition.PmTempGt && endbyEnum != EndByCondition.PmTempLt) return false;                //        string end = RecipeRows[endValueIndex].RecipeItems[stepNo - 1].Value;                //        double endValue;                //        if (!double.TryParse(end, out endValue)) return false;                //        return CheckRampRate(rampEnable, rampTime, maxRampUpRate, maxRampDownRate, endValue, currentValue);                //    }                //}                double prevValue = double.Parse(RecipeRows[rowIndex].RecipeItems[stepNo - 1].Value);                return CheckRampRate(rampEnable, rampTime, maxRampUpRate, maxRampDownRate, prevValue, currentValue);            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);                return true;            }        }        bool CheckRampRate(string rampEnable, string rampTime, double maxRampUpRate, double maxRampDownRate, double prevValue, double currentValue)        {            bool isRampEnable = bool.Parse(rampEnable);            //string[] timeStr = rampTime.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);            //string tt = string.Join(":", timeStr);            TimeSpan timeSpan;            TimeSpan.TryParse(rampTime, out timeSpan);            double totalTimeSec = timeSpan.TotalSeconds;            double diff = currentValue - prevValue;            if (!isRampEnable || totalTimeSec <= 0)            {                if (diff != 0) return true;                return false;            }            else            {                double rampRate = diff / totalTimeSec;                return (rampRate > 0 && rampRate >= maxRampUpRate) || (rampRate < 0 && rampRate <= -maxRampDownRate);            }        }        /// <summary>        /// Refresh datagrid cell data display        /// </summary>        /// <param name="moving2RunningStepPosition">True: moving to current running step position</param>        public void RefreshCellsDisplay(bool moving2RunningStepPosition = true)        {            try            {                if (RecipeRows == null || RecipeRows.Count < 2)                    return;                Errors = new List<Tuple<int, int, string, string>>();                Dictionary<int, string> dicEndby = new Dictionary<int, string>();                Dictionary<int, string> dicPressureMode = new Dictionary<int, string>();                Dictionary<int, string> dicRfMode = new Dictionary<int, string>();                Dictionary<int, string> dicRfMatchProcessMode = new Dictionary<int, string>();                Dictionary<int, string> dicSetMatchPositionC1 = new Dictionary<int, string>();                Dictionary<int, string> dicSetMatchPositionC2 = new Dictionary<int, string>();                for (int i = 0; i < RecipeRows.Count; i++)                {                    for (int j = 0; j < RecipeRows[i].RecipeItems.Count; j++)                    {                        var cell = RecipeRows[i].RecipeItems[j];                        cell.RowIndex = i;                        cell.ColIndex = j;                        cell.ErrInfo = string.Empty;                        bool logicEnable = true;                        if (cell.RecipeVariableDefine.TechnicalName == "EndBy")                            dicEndby[j] = cell.Value;                        if (cell.RecipeVariableDefine.TechnicalName == "ThrottleValve.SetMode")                        {                            dicPressureMode[j] = cell.Value;                        }                        if (cell.RecipeVariableDefine.TechnicalName == "PressureControl.SetTVMode")                        {                            dicPressureMode[j] = cell.Value;                        }                        if (cell.RecipeVariableDefine.TechnicalName == "Rf.SetMode")                            dicRfMode[j] = cell.Value;                        if (cell.RecipeVariableDefine.TechnicalName == "Match.SetMatchProcessMode")                            dicRfMatchProcessMode[j] = cell.Value;                        if (cell.RecipeVariableDefine.TechnicalName == "Match.SetMatchPositionC1")                            dicSetMatchPositionC1[j] = cell.Value;                        if (cell.RecipeVariableDefine.TechnicalName == "Match.SetMatchPositionC2")                            dicSetMatchPositionC2[j] = cell.Value;                        if (cell.RecipeVariableDefine.TechnicalName == "Time")                        {                            logicEnable = dicEndby.ContainsKey(j) && (dicEndby[j] == "EndByStepTime" ||                                                                      dicEndby[j] == "EndByHeatUp" ||                                                                      dicEndby[j] == "EndByEndPoint");                        }                        if (cell.RecipeVariableDefine.TechnicalName == "ThrottleValve.SetPosition")                        {                            logicEnable = dicPressureMode.ContainsKey(j) && dicPressureMode[j] == "TVPositionCtrl";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "ThrottleValve.SetPressure")                        {                            logicEnable = dicPressureMode.ContainsKey(j) && dicPressureMode[j] == "TVPressureCtrl";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "PressureControl.SetTVPosition")                        {                            logicEnable = dicPressureMode.ContainsKey(j) && dicPressureMode[j] == "TVPositionCtrl";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "PressureControl.SetTVPressure")                        {                            logicEnable = dicPressureMode.ContainsKey(j) && dicPressureMode[j] == "TVPressureCtrl";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "Rf.SetPulsingFrequency")                        {                            logicEnable = dicRfMode.ContainsKey(j) && dicRfMode[j] == "PulsingMode";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "Rf.SetPulsingDuty")                        {                            logicEnable = dicRfMode.ContainsKey(j) && dicRfMode[j] == "PulsingMode";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "Rf.SetPowerOnTime")                        {                            logicEnable = dicEndby.ContainsKey(j) && dicEndby[j] == "EndByRfTime";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "EPD.SetConfig")                        {                            logicEnable =  dicEndby.ContainsKey(j) && dicEndby[j] == "EndByEndPoint";                            if (string.IsNullOrEmpty(cell.Value) || cell.Value.StartsWith("n/a"))                                cell.Value = _defaultEndPointValue;                        }						                        if (cell.RecipeVariableDefine.TechnicalName == "Match.SetMatchProcessMode")                        {                            logicEnable = dicRfMatchProcessMode.ContainsKey(j);// && dicRfMatchProcessMode[j] == "TritonPresetMode";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "Match.SetMatchPositionC1")                        {                            logicEnable = dicSetMatchPositionC1.ContainsKey(j);// && dicRfMatchProcessMode[j] == "TritonPresetMode";                        }                        if (cell.RecipeVariableDefine.TechnicalName == "Match.SetMatchPositionC2")                        {                            logicEnable = dicSetMatchPositionC2.ContainsKey(j);// && dicRfMatchProcessMode[j] == "TritonPresetMode";                        }                        cell.IsMasked = !logicEnable;                        //update diff variable numbers                        if (cell.RecipeVariableDefine.TechnicalName == "Ramp")                        {                            //更新当前步骤中Ramp的变量数据                            if (j == 0)                            {                                cell.Tag = 0;                            }                            else                            {                                int rampNum = 0;                                for (int s1 = 0; s1 < RecipeRows.Count; s1++)                                {                                    if (RecipeRows[s1].CatalogName == "StepInfo")// RecipeRows[s1].RecipeItems[j].RecipeVariableDefine.CellType == CellType.ReadOnly)                                        continue;                                    string preValue = RecipeRows[s1].RecipeItems[j - 1].Value;                                    string curValue = RecipeRows[s1].RecipeItems[j].Value;                                    if (RecipeRows[s1].RecipeItems[j].RecipeVariableDefine.CellType == CellType.NumInput)                                    {                                        double pd, cd;                                        if (double.TryParse(preValue, out pd) && double.TryParse(curValue, out cd))                                            if (pd != cd) rampNum++;                                    }                                    else if (String.Compare(preValue, curValue, false) != 0)                                    {                                        rampNum++;                                    }                                }                                cell.Tag = rampNum;                            }                            cell.InvokePropertyChanged();                        }                        #region mask (hide or show cell content)                        if ((MaskedTechNames != null && MaskedTechNames.Contains(cell.RecipeVariableDefine.TechnicalName)) ||                            (MaskedCatalogNames != null && MaskedCatalogNames.Contains(cell.RecipeVariableDefine.CatalogName)) || !logicEnable)                            cell.IsMasked = true;                        else                            cell.IsMasked = false;                        #endregion mask (hide or show cell content)                        #region check cell data range                        switch (cell.RecipeVariableDefine.CellType)                        {                        case CellType.CheckBox:                            {                                if (string.Compare(cell.Value, "true", true) == 0 || string.Compare(cell.Value, "false", true) == 0)                                {                                    cell.ErrInfo = "";                                    cell.Background = Brushes.Transparent;                                }                                else                                {                                    string reason = string.Format("Value '{0}' not valid", cell.Value);                                    Errors.Add(new Tuple<int, int, string, string>(i, j, cell.RecipeVariableDefine.FriendlyName, reason));                                    cell.ErrInfo = reason;                                    cell.Background = Brushes.Pink;                                }                            }                            break;                        case CellType.EditableSelection:                            {                                cell.ErrInfo = "";                                cell.Background = Brushes.Transparent;                            }                            break;                        case CellType.NumInput:                            {                                double min = cell.RecipeVariableDefine.MinValue;                                double max = cell.RecipeVariableDefine.MaxValue;                                double v;                                if (!double.TryParse(cell.Value, out v))                                {                                    string reason = string.Format("Value '{0}' not valid", cell.Value);                                    Errors.Add(new Tuple<int, int, string, string>(i, j, cell.RecipeVariableDefine.FriendlyName, reason));                                    cell.ErrInfo = reason;                                    cell.Background = Brushes.Pink;                                }                                else if (v > max || v < 0 || (v < min && v > max * 0.1 / 100))                                {                                    string reason = string.Format("Value'{0}',Out of Range {1}~{2}", cell.Value, min, max);                                    Errors.Add(new Tuple<int, int, string, string>(i, j, cell.RecipeVariableDefine.FriendlyName, reason));                                    cell.ErrInfo = reason;                                    cell.Background = Brushes.Pink;                                }                                else                                {                                    cell.ErrInfo = "";                                    cell.Background = Brushes.Transparent;                                }                            }                            break;                        case CellType.ReadOnlySelection:                            {                                if (cell.RecipeVariableDefine.DropdownItemList != null && cell.RecipeVariableDefine.DropdownItemList.Find((o) => o.Item1 == cell.Value) != null)                                {                                    cell.ErrInfo = "";                                    cell.Background = Brushes.Transparent;                                }                                else                                {                                    string reason = string.Format("Value'{0}' is not valid", cell.Value);                                    Errors.Add(new Tuple<int, int, string, string>(i, j, cell.RecipeVariableDefine.FriendlyName, reason));                                    cell.ErrInfo = reason;                                    cell.Background = Brushes.Pink;                                }                            }                            break;                        case CellType.ReadOnly:                        case CellType.TextInput:                            cell.ErrInfo = "";                            cell.Background = Brushes.Transparent;                            break;                        case CellType.TimeInput:                            {                                string[] arr = cell.Value.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);                                int h, mi, s;                                if (arr.Length == 3 && int.TryParse(arr[0], out h) && int.TryParse(arr[1], out mi) && int.TryParse(arr[2], out s) && h >= 0 && mi >= 0 && mi <= 59 && s >= 0 && s <= 59)                                {                                    cell.ErrInfo = "";                                    cell.Background = Brushes.Transparent;                                }                                else                                {                                    string reason = string.Format("value'{0}' is not valid", cell.Value);                                    Errors.Add(new Tuple<int, int, string, string>(i, j, cell.RecipeVariableDefine.FriendlyName, reason));                                    cell.ErrInfo = reason;                                    cell.Background = Brushes.Pink;                                }                            }                            break;                        }                        #endregion check cell data range                        #region mark data trend / hide same content                        if (j == 0) //step one                        {                            cell.Foreground = Brushes.Black;                            cell.FontWeight = FontWeights.Normal;                            cell.IsHidden = false;                            cell.InvokePropertyChanged();                            cell.IsMasked = !logicEnable;                            continue;                        }                        var preCell = RecipeRows[i].RecipeItems[j - 1];                        if (cell.RecipeVariableDefine.CellType == CellType.NumInput)                        {                            double preValue = 0;                            double.TryParse(preCell.Value.ToString(), out preValue);                            double curValue = 0;                            double.TryParse(cell.Value.ToString(), out curValue);                            if (curValue > preValue)                            {                                cell.Foreground = Brushes.Red;                                cell.FontWeight = FontWeights.Bold;                            }                            else if (curValue < preValue)                            {                                cell.Foreground = Brushes.Blue;                                cell.FontWeight = FontWeights.Bold;                            }                            else                            {                                cell.Foreground = Brushes.Black;                                cell.FontWeight = FontWeights.Normal;                            }                        }                        else                        {                            //not number type                            cell.Foreground = Brushes.Black;                            cell.FontWeight = FontWeights.Normal;                        }                        if (IsHideSameContent && cell.Value == preCell.Value)                            cell.IsHidden = true;                        else                            cell.IsHidden = false;                        //!logicEnable;                        cell.IsMasked = !logicEnable;                        //if (!logicEnable)                        //{                        //    cell.Background = Brushes.DarkGray;                        //    cell.IsMasked = true;                        //}                        #endregion mark data trend / hide same content                        cell.InvokePropertyChanged();                    }                }                #region check loop                int loopRowId = -1;                for (int i = 0; i < RecipeRows.Count; i++)                {                    if (RecipeRows[i].TechnicalName == "Loop")                    {                        loopRowId = i;                        break;                    }                }                for (int j = 0; j < RecipeRows[loopRowId].RecipeItems.Count; j++)                {                    var cell = RecipeRows[loopRowId].RecipeItems[j];                    bool isLoopStart = Regex.IsMatch(cell.Value, @"^Loop\x20\d+$");                    bool isLoopEnd = Regex.IsMatch(cell.Value, @"^Loop End$");                    bool isNullOrEmpty = string.IsNullOrWhiteSpace(cell.Value);                    if (!isLoopEnd && !isLoopStart && !isNullOrEmpty)                    {                        string reason = string.Format("Value '{0}' not valid", cell.Value);                        cell.ErrInfo = reason;                        cell.Background = Brushes.Pink;                        Errors.Add(new Tuple<int, int, string, string>(loopRowId, j, cell.RecipeVariableDefine.FriendlyName, reason));                    }                    if (isLoopEnd)                    {                        string reason = "Loop Start Lack";                        cell.ErrInfo = reason;                        cell.Background = Brushes.Pink;                        Errors.Add(new Tuple<int, int, string, string>(loopRowId, j, cell.RecipeVariableDefine.FriendlyName, reason));                    }                    else if (isLoopStart)                    {                        for (int k = j + 1; k < RecipeRows[loopRowId].RecipeItems.Count; k++)                        {                            cell = RecipeRows[loopRowId].RecipeItems[k];                            bool isCurStepLoopStart = Regex.IsMatch(cell.Value, @"^Loop\x20\d+$");                            bool isCurStepLoopEnd = Regex.IsMatch(cell.Value, @"^Loop End$");                            isNullOrEmpty = string.IsNullOrWhiteSpace(cell.Value);                            if (!isCurStepLoopEnd && !isCurStepLoopStart && !isNullOrEmpty)                            {                                string reason = string.Format("Value '{0}' not valid", cell.Value);                                cell.ErrInfo = reason;                                cell.Background = Brushes.Pink;                                Errors.Add(new Tuple<int, int, string, string>(loopRowId, k, cell.RecipeVariableDefine.FriendlyName, reason));                            }                            else if (isCurStepLoopStart)                            {                                string reason = "The previous loop has not ended, and a new Loop Start flag cannot be set";                                cell.ErrInfo = reason;                                cell.Background = Brushes.Pink;                                Errors.Add(new Tuple<int, int, string, string>(loopRowId, k, cell.RecipeVariableDefine.FriendlyName, reason));                            }                            else if (isCurStepLoopEnd)                            {                                //mark loop steps with blue background                                for (int m1 = j; m1 <= k; m1++)                                {                                    var curCell = RecipeRows[loopRowId].RecipeItems[m1];                                    if (curCell.Background == Brushes.Transparent)                                        curCell.Background = Brushes.LightGreen;                                    curCell.InvokePropertyChanged();                                }                                j = k;                                break;                            }                            if (k == RecipeRows[loopRowId].RecipeItems.Count - 1)                            {                                j = k;                                string reason = "Loop End Lack";                                cell.ErrInfo = reason;                                cell.Background = Brushes.Pink;                                Errors.Add(new Tuple<int, int, string, string>(loopRowId, k, cell.RecipeVariableDefine.FriendlyName, reason));                            }                        }                    }                }                #endregion check loop                #region recipe parameter validation                //using (var lua = new LuaInterface.Lua())                //{                //    //is special recipe?                //    //bool IsSpecialRecipe = true;                //    //{                //    //    int heaterModeRowIndex = RowVarNameDic["Heater.Mode"];                //    //    int aZoneSetpointIndex = RowVarNameDic["AZone.Setpoint"];                //    //    int bZoneSetpointIndex = RowVarNameDic["BZone.Setpoint"];                //    //    int cZoneSetpointIndex = RowVarNameDic["CZone.Setpoint"];                //    //    int dZoneSetpointIndex = RowVarNameDic.ContainsKey("DZone.Setpoint") ? RowVarNameDic["DZone.Setpoint"] : aZoneSetpointIndex;                //    //    for (int stepIndex = 0; stepIndex < RecipeRows[0].RecipeItems.Count; stepIndex++)                //    //    {                //    //        if (RecipeRows[heaterModeRowIndex].RecipeItems[stepIndex].Value != "CurrentControl" ||                //    //            RecipeRows[aZoneSetpointIndex].RecipeItems[stepIndex].Value != "0" ||                //    //            RecipeRows[bZoneSetpointIndex].RecipeItems[stepIndex].Value != "0" ||                //    //            RecipeRows[cZoneSetpointIndex].RecipeItems[stepIndex].Value != "0" ||                //    //            RecipeRows[dZoneSetpointIndex].RecipeItems[stepIndex].Value != "0")                //    //        {                //    //            IsSpecialRecipe = false;                //    //            break;                //    //        }                //    //    }                //    //}                //    ////set get ramp rate function                //    //lua.RegisterFunction("CheckRampRate", this, GetType().GetMethod("CheckRampRate"));                //    //lua.DoString(string.Format("IsProductionRecipe={0};",(!IsSpecialRecipe).ToString().ToLower()));                //    for (int stepIndex = 0; stepIndex < RecipeRows[0].RecipeItems.Count; stepIndex++)                //    {                //        //set stepNo                //        lua.DoString(string.Format("StepNo={0};", stepIndex));                //        //reading var from current recipe step                //        foreach (var checkVar in _checkVariables)                //        {                //            if (!RowVarNameDic.ContainsKey(checkVar))                //            {                //                System.Diagnostics.Debug.WriteLine("检查不到变量定义," + checkVar);                //                continue;                //            }                //            try                //            {                //                int varId = RowVarNameDic[checkVar];                //                if (RecipeRows[varId].RecipeItems[stepIndex].RecipeVariableDefine.CellType == CellType.NumInput)                //                    lua.DoString(string.Format("{0}={1};", RecipeRows[varId].RecipeItems[stepIndex].RecipeVariableDefine.TechnicalName.Replace(".", "_"), RecipeRows[varId].RecipeItems[stepIndex].Value));                //                else                //                    lua.DoString(string.Format("{0}=\"{1}\";", RecipeRows[varId].RecipeItems[stepIndex].RecipeVariableDefine.TechnicalName.Replace(".", "_"), RecipeRows[varId].RecipeItems[stepIndex].Value));                //            }                //            catch (Exception ex)                //            {                //                System.Diagnostics.Debug.WriteLine("校验异常," + ex);                //            }                //        }                //        //reading from recipe format file                //        foreach (string key in _preDefinedRecipeVars.Keys)                //        {                //            string varValueString = _preDefinedRecipeVars[key];                //            double varValue;                //            if (double.TryParse(varValueString, out varValue))                //                lua.DoString(string.Format("{0}={1};", key, varValue));                //            else                //                lua.DoString(string.Format("{0}=\"{1}\";", key, varValueString));                //        }                //        //do valation                //        foreach (var rule in _validationRules)                //        {                //            lua.DoString(string.Format("if {0} then hasErr=1 else hasErr=0 end", rule.Item2));                //            bool hasError = ((int)lua.GetNumber("hasErr")) == 1;                //            if (hasError)                //            {                //                lua.DoString(string.Format("message=string.format({0});", rule.Item3));                //                string reason = lua.GetString("message");                //                var rowId = RowVarNameDic[rule.Item1];                //                var cell = RecipeRows[rowId].RecipeItems[stepIndex];                //                cell.ErrInfo = reason;                //                cell.Background = Brushes.Pink;                //                Errors.Add(new Tuple<int, int, string, string>(rowId, stepIndex, cell.RecipeVariableDefine.FriendlyName, reason));                //                cell.InvokePropertyChanged();                //            }                //        }                //    }                //}                #endregion recipe parameter validation                #region update material cell data                int materialRowId = -1;                for (int colId = 0; colId < RecipeRows[0].RecipeItems.Count; colId++)                {                    string materialName = "";                    for (int rowId = 0; rowId < RecipeRows.Count; rowId++)                    {                        string curMo = "";                        if (materialRowId == -1 && RecipeRows[rowId].RecipeItems[colId].RecipeVariableDefine.TechnicalName == "Material")                        {                            materialRowId = rowId;                        }                        var cell = RecipeRows[rowId].RecipeItems[colId];                        if (cell.RecipeVariableDefine.CellType == CellType.ReadOnlySelection && String.Compare(cell.Value, "flow", true) == 0)                        {                            string moName = cell.RecipeVariableDefine.TechnicalName.Split('.')[0].ToLower();                            string displayName = cell.RecipeVariableDefine.FriendlyName.Replace("_", ".").Split('.')[0].ToLower();                            if (moName.Contains("cp2mg_1") || displayName.Contains("cp2mg_1")) curMo = "Mg1";                            else if (moName.Contains("cp2mg_2") || displayName.Contains("cp2mg_2")) curMo = "Mg2";                            else if (moName.Contains("cp2mg_3") || displayName.Contains("cp2mg_3")) curMo = "Mg3";                            else if (moName.Contains("cp2mg_4") || displayName.Contains("cp2mg_4")) curMo = "Mg4";                            else if (moName.Contains("cp2mg") || displayName.Contains("cp2mg")) curMo = "Mg";                            else if (moName.Contains("tmin") || displayName.Contains("tmin")) curMo = "In";                            else if (moName.Contains("tmin_1") || displayName.Contains("tmin_1")) curMo = "In1";                            else if (moName.Contains("tmin_2") || displayName.Contains("tmin_2")) curMo = "In2";                            else if (moName.Contains("tmin_3") || displayName.Contains("tmin_3")) curMo = "In3";                            else if (moName.Contains("tmin_4") || displayName.Contains("tmin_4")) curMo = "In4";                            else if (moName.Contains("tmga") || displayName.Contains("tmga")) curMo = "TMG";                            else if (moName.Contains("tmga_1") || displayName.Contains("tmga_1")) curMo = "TMG1";                            else if (moName.Contains("tmga_2") || displayName.Contains("tmga_2")) curMo = "TMG2";                            else if (moName.Contains("tmga_3") || displayName.Contains("tmga_3")) curMo = "TMG3";                            else if (moName.Contains("tmga_4") || displayName.Contains("tmga_4")) curMo = "TMG4";                            else if (moName.Contains("tega") || displayName.Contains("tega")) curMo = "TEG";                            else if (moName.Contains("tega_1") || displayName.Contains("tega_1")) curMo = "TEG1";                            else if (moName.Contains("tega_2") || displayName.Contains("tega_2")) curMo = "TEG2";                            else if (moName.Contains("tega_3") || displayName.Contains("tega_3")) curMo = "TEG3";                            else if (moName.Contains("tega_4") || displayName.Contains("tega_4")) curMo = "TEG4";                            else if (moName.Contains("tmal") || displayName.Contains("tmal")) curMo = "TAl";                            else if (moName.Contains("tmal_1") || displayName.Contains("tmal_1")) curMo = "TAl1";                            else if (moName.Contains("tmal_2") || displayName.Contains("tmal_2")) curMo = "TAl2";                            else if (moName.Contains("tmal_3") || displayName.Contains("tmal_3")) curMo = "TAl3";                            else if (moName.Contains("tmal_4") || displayName.Contains("tmal_4")) curMo = "TAl4";                            else if (moName.Contains("nh3source") || displayName.Contains("nh3source")) curMo = "NH3";                            else if (moName.Contains("si")) curMo = "Si";                            if (!string.IsNullOrEmpty(curMo))                            {                                if (string.IsNullOrEmpty(materialName))                                    materialName = curMo;                                else                                    materialName += "|" + curMo;                            }                        }                    }                    if (materialRowId >= 0)                    {                        RecipeRows[materialRowId].RecipeItems[colId].Value = materialName;                        RecipeRows[materialRowId].RecipeItems[colId].InvokePropertyChanged();                    }                }                #endregion update material cell data                #region update column header                if (DataGridControl.Columns.Count - 1 == RecipeRows[0].RecipeItems.Count)                {                    var redColHeader = (DataTemplate)DataGridControl.FindResource("columnRedTitleTemplate");                    var blackColHeader = (DataTemplate)DataGridControl.FindResource("columnBlackTitleTemplate");                    for (int j = 0; j < RecipeRows[0].RecipeItems.Count; j++)                    {                        DataGridControl.Columns[j + 1].Title = string.Format("{0}|{2}\r\n{1}", j + 1, RecipeRows[0].RecipeItems[j].Value, RecipeRows[2].RecipeItems[j].Value);                        if (Errors.Find((o) => o.Item2 == j) != null)                            DataGridControl.Columns[j + 1].TitleTemplate = redColHeader;                        else                            DataGridControl.Columns[j + 1].TitleTemplate = blackColHeader;                    }                }                #endregion update column header                #region if running state, highlight current running step, move horizontal bar to current running step                if (CurrentRunningStepNo > 0 && DataGridControl.Columns.Count > 2)                {                    for (int rowId = 0; rowId < RecipeRows.Count; rowId++)                    {                        for (int colId = 0; colId < RecipeRows[0].RecipeItems.Count; colId++)                        {                            var cell = RecipeRows[rowId].RecipeItems[colId];                            if (colId + 1 == CurrentRunningStepNo)                            {                                cell.IsRunning = true;                            }                            else                            {                                cell.IsRunning = false;                            }                            cell.InvokePropertyChanged("IsRunning");                        }                    }                    if (moving2RunningStepPosition)                    {                        Task.Factory.StartNew(() =>                            {                                System.Threading.Thread.Sleep(200);                                DataGridControl.Dispatcher.Invoke(new Action(() =>                                    {                                        double singleWidth = DataGridControl.Columns[1].Width;                                        double horizontalOffset = (CurrentRunningStepNo - 2) * singleWidth;                                        if (DataGridControl.ScrollViewer != null)                                        {                                            if (horizontalOffset > DataGridControl.ScrollViewer.ScrollableWidth)                                                horizontalOffset = DataGridControl.ScrollViewer.ScrollableWidth;                                            if (horizontalOffset < 0)                                                horizontalOffset = 0;                                            DataGridControl.ScrollViewer.ScrollToHorizontalOffset(horizontalOffset);                                        }                                    }));                            });                    }                }                #endregion if running state, highlight current running step, move horizontal bar to current running step                //calc recipe time                CalcRecipeTime();                //invoke property                InvokePropertyChanged("Errors");            }            catch (Exception ex)            {                MessageBox.Show("Process program verification error!\r\n\r\n" + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);                System.Diagnostics.Debug.WriteLine(ex.Message);            }        }        /// <summary>        /// reload datagrid content        /// </summary>        private void RefreshDataGrid()        {            try            {                //generate columns in Grid                DataGridControl.CurrentColumn = null;                if (DataGridControl.Columns.Count > 0)                    DataGridControl.Columns.Clear();                var template = (DataTemplate)DataGridControl.FindResource("CustomTemplate");                var rowTemplate = (DataTemplate)DataGridControl.FindResource("RowHeadTemplate");                DataGridControl.Columns.Add(new Xceed.Wpf.DataGrid.Column()                {                    Width = 140,                    Title = " ",                    FieldName = ".",                    CellContentTemplate = rowTemplate                });                var cellEditor = DataGridControl.DefaultCellEditors[typeof(SmartCellData)];// DefaultCellEditorSelector.SelectCellEditor(typeof(SmartCellData));                for (int index = 0; index < RecipeRows[0].RecipeItems.Count; index++)                {                    //var col = new Xceed.Wpf.DataGrid.Column();                    DataGridControl.Columns.Add(new Xceed.Wpf.DataGrid.Column()                    {                        Title = string.Format("【{0}】\r\n{1}", index + 1, RecipeRows[0].RecipeItems[index].Value),                        FieldName = string.Format("RecipeItems[{0}]", index),                        CellContentTemplate = template,                        AllowSort = false,                        Width = 120,                        MaxWidth = 120,                        CellEditor = cellEditor                    });                }                InvokePropertyChanged("RecipeRows");                InvokePropertyChanged("RecipeHead");            }            catch (Exception ex)            {                System.Diagnostics.Debug.WriteLine(ex.Message);            }        }        /// <summary>        /// calc recipe total time        /// </summary>        private void CalcRecipeTime()        {            int timeStepRowId = -1;            int loopStepRowId = -1;            for (int i = 0; i < RecipeRows.Count; i++)            {                if (RecipeRows[i].TechnicalName == "Time")                    timeStepRowId = i;                if (RecipeRows[i].TechnicalName == "Loop")                    loopStepRowId = i;                if (loopStepRowId != -1 && timeStepRowId != -1)                    break;            }            TimeSpan tspan = new TimeSpan();            for (int stepNo = 0; stepNo < RecipeRows[timeStepRowId].RecipeItems.Count; stepNo++)            {                string loopStr = RecipeRows[loopStepRowId].RecipeItems[stepNo].Value;                bool isLoopStart = Regex.IsMatch(loopStr, @"^Loop\x20\d+$");                if (isLoopStart)                {                    int loopNum = int.Parse(loopStr.ToLower().Replace("loop", "").Replace(" ", ""));                    TimeSpan ts = new TimeSpan();                    for (int innerStepNo = stepNo; innerStepNo < RecipeRows[timeStepRowId].RecipeItems.Count; innerStepNo++)                    {                        loopStr = RecipeRows[loopStepRowId].RecipeItems[innerStepNo].Value;                        stepNo = innerStepNo;                        string timeDuration = RecipeRows[timeStepRowId].RecipeItems[innerStepNo].Value;                        string[] timeArr = timeDuration.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);                        if (timeArr.Length == 3)                        {                            int h, mi, s;                            if (int.TryParse(timeArr[0], out h) && int.TryParse(timeArr[1], out mi) && int.TryParse(timeArr[2], out s))                            {                                var tt = new TimeSpan(h, mi, s);                                ts += tt;                            }                        }                        bool isLoopEnd = Regex.IsMatch(loopStr, @"^Loop End$");                        if (isLoopEnd)                        {                            tspan += new TimeSpan(ts.Ticks * loopNum);                            break;                        }                    }                }                else                {                    string timeDuration = RecipeRows[timeStepRowId].RecipeItems[stepNo].Value;                    string[] timeArr = timeDuration.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);                    if (timeArr.Length == 3)                    {                        int h, mi, s;                        if (int.TryParse(timeArr[0], out h) && int.TryParse(timeArr[1], out mi) && int.TryParse(timeArr[2], out s))                        {                            var ts = new TimeSpan(h, mi, s);                            tspan += ts;                        }                    }                }            }            RecipeInfo = string.Format("Total {0} steps, total time {1}: {2}: {3}", RecipeRows[0].RecipeItems.Count, (int)tspan.TotalHours, tspan.Minutes.ToString("00"), tspan.Seconds.ToString("00"));            InvokePropertyChanged("RecipeInfo");        }        #region INotifyPropertyChanged        public event PropertyChangedEventHandler PropertyChanged;        public void InvokePropertyChanged(string propertyName)        {            if (PropertyChanged != null)            {                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));            }        }        #endregion INotifyPropertyChanged    }    public class RecipeRow    {        private ObservableCollection<SmartCellData> _recipeItems = new ObservableCollection<SmartCellData>();        public RecipeRow(params SmartCellData[] vars)        {            foreach (var var in vars)                _recipeItems.Add(var);        }        public string CatalogName { get; set; }        public string FriendlyName { get; set; }        public string TechnicalName { get; set; }        public ObservableCollection<SmartCellData> RecipeItems        {            get { return _recipeItems; }            set { _recipeItems = value; }        }    }    /// <summary>    /// Recipe head    /// </summary>    public class RecipeHead    {        public string RecipeVariation { get; set; }        public string CreationTime { get; set; }        public string LastRevisionTime { get; set; }        public string CreatedBy { get; set; }        public string LastModifiedBy { get; set; }        public string PressureMode { get; set; }        public string Description { get; set; }        public string BasePressure        {            get; set;        }        public string PumpDownLimit        {            get; set;        }        public string PurgeActive        {            get; set;        }        public string NotToPurgeOrVent        {            get; set;        }        public string Barcode        {            get; set;        }        public string SubstrateTemp        {            get; set;        }        public string PumpingPinState        {            get; set;        }        public string VentingPinState        {            get; set;        }        public string PinDownPressure        {            get; set;        }    }    public class EndPointConfigItem : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        public void InvokePropertyChanged(string propertyName)        {            if (PropertyChanged != null)            {                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));            }        }        public string ExposureTime { get; set; }        public string WaveLengthA { get; set; }        public string BinningA { get; set; }        public string WaveLengthB { get; set; }        public string BinningB { get; set; }        public string WaveLengthC { get; set; }        public string BinningC { get; set; }        public string WaveLengthD { get; set; }        public string BinningD { get; set; }        public string Fd { get; set; }        public string PrefilterTime { get; set; }        public string PostfilterTime { get; set; }        public string AlgorithmType { get; set; }        public string Criteria { get; set; }        public string DelayTime { get; set; }        public string ValidationTime { get; set; }        public string ValidationValue { get; set; }        public string TimeWindow { get; set; }        public string MinimalTime { get; set; }        public string PostponeTime { get; set; }        public bool Control { get; set; }        public bool Normalization { get; set; }        public bool EnablePostponePercent { get; set; }        public bool EnableCriterialPercent { get; set; }        public bool EnableEventTrigger { get; set; }        public bool IsFaultIfNoTrigger { get; set; }        public string ToValue()        {            return                $@"ExposureTime={ExposureTime};WaveLengthA={WaveLengthA};BinningA={BinningA};WaveLengthB={WaveLengthB};" +                $@"BinningB={BinningB};WaveLengthC={WaveLengthC};BinningC={BinningC};WaveLengthD={WaveLengthD};BinningD={BinningD};Fd={Fd};" +                $@"PrefilterTime={PrefilterTime};PostfilterTime={PostfilterTime};AlgorithmType={AlgorithmType};Criteria={Criteria};DelayTime={DelayTime};ValidationTime={ValidationTime};" +                $@"ValidationValue={ValidationValue};TimeWindow={TimeWindow};MinimalTime={MinimalTime};PostponeTime={PostponeTime};Control={Control};Normalization={Normalization};" +                $@"EnablePostponePercent={EnablePostponePercent};EnableCriterialPercent={EnableCriterialPercent};EnableEventTrigger={EnableEventTrigger};IsFaultIfNoTrigger={IsFaultIfNoTrigger};"                ;        }        public void SetValue(string config)        {            if (string.IsNullOrEmpty(config))                return;            string[] items = config.Split(';');            foreach (var item in items)            {                if (string.IsNullOrEmpty(item))                    continue;                string[] pairs = item.Split('=');                if (pairs.Length != 2)                    continue;                Parallel.ForEach(this.GetType().GetProperties(),                    property =>                    {                        PropertyInfo pi = (PropertyInfo) property;                        if (pi.Name == pairs[0])                        {                            try                            {                                var convertedValue = Convert.ChangeType(pairs[1], pi.PropertyType);                                var originValue = Convert.ChangeType(pi.GetValue(this, null), pi.PropertyType);                                if (originValue != convertedValue)                                {                                    pi.SetValue(this, convertedValue, null);                                }                            }                            catch (Exception)                            {                            }                        }                    });            }        }    }}
 |