wenxiangchao
4 天以前 559890c482f5973f06aafdfec9550fee5ba3c7f2
Plugin.TsExecution/TsExecution.xaml.cs
@@ -6,11 +6,14 @@
using HMI.Core.Setting;
using HMI.Core.Util;
using HMI.MQUtil;
using HMI.Simple.Util;
using HMI.UI.Util;
using HMI.Util;
using log4net;
using NationalInstruments.TestStand.Interop.API;
using NationalInstruments.TestStand.Interop.UI.Ax;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using OpenTap;
using Plugin.TsSupport;
using ScottPlot;
using ScottPlot.Plottables;
@@ -20,11 +23,8 @@
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.DataVisualization.Charting;
namespace Plugin.TsExecution
{
@@ -37,28 +37,27 @@
        /// <summary>
        /// TS 引擎
        /// </summary>
        private Engine engine = null;
        //private Engine engine = null;
        ///// <summary>
        ///// 测试序列
        ///// </summary>
        //private SequenceFile testSeq = null;
        ///// <summary>
        ///// 测试模型序列
        ///// </summary>
        //private SequenceFile modeSeq = null;
        //private Execution execution = null;
        private string testSeqFile = string.Empty;
        /// <summary>
        /// 测试序列
        /// </summary>
        private SequenceFile testSeq = null;
        /// <summary>
        /// 测试模型序列
        /// </summary>
        private SequenceFile modeSeq = null;
        public AxApplicationMgr AxApplicationMgr;
        private Execution execution = null;
        private SequenceContext sequenceContext = null;
        private Chart chart;
        private ITestExecutionWrapper testExecutionWrapper;
        #endregion
        public static ILog logger { get; private set; }
        private List<SingleControl> tsSockets = new List<SingleControl>();
        public List<SingleControl> tsSockets { get; private set; } = new List<SingleControl>();
        /// <summary>
        /// 样件模式状态
        /// </summary>
@@ -83,7 +82,6 @@
        private bool isLoadOK = false;
        private Timer GCTimer;
        private int PassAmount = 0;
        private int FailAmount = 0;
@@ -109,6 +107,7 @@
                    foreach (SingleControl socket in tsSockets)
                    {
                        socket.RefreshProduct(value);
                        // socket.RefreshProbeCount(value);
                    }
                }
                SetTestSeqFile(value);
@@ -182,9 +181,12 @@
            get => _enableCanLog;
            set
            {
                if (WriteConfig("EnableCanLog", value))
                if (RequirePassword() == true)
                {
                    Set(ref _enableCanLog, value);
                    if (WriteConfig("EnableCanLog", value))
                    {
                        Set(ref _enableCanLog, value);
                    }
                }
            }
        }
@@ -227,10 +229,13 @@
            get => _enableSimpleMode;
            set
            {
                if (WriteConfig("EnableSimpleMode", value))
                if (RequirePassword() == true)
                {
                    Set(ref _enableSimpleMode, value);
                    foreach (var cell in tsSockets) cell.SetSimpleUI(value);
                    if (WriteConfig("EnableSimpleMode", value))
                    {
                        Set(ref _enableSimpleMode, value);
                        foreach (var cell in tsSockets) cell.SetSimpleUI(value);
                    }
                }
            }
        }
@@ -241,10 +246,13 @@
            get => _ui_showInfoDock;
            set
            {
                if (WriteConfig("UI_ShowInfoDock", value))
                if (RequirePassword() == true)
                {
                    Set(ref _ui_showInfoDock, value);
                    detailPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
                    if (WriteConfig("UI_ShowInfoDock", value))
                    {
                        Set(ref _ui_showInfoDock, value);
                        detailPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
                    }
                }
            }
        }
@@ -268,8 +276,8 @@
                if (WriteIni("UI_CurrentWorkorder", value))
                {
                    Set(ref currentWorkorder, value);
                    engine?.Globals.SetValString("StationState.WorkOrder", 1, value);
                    engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                    testExecutionWrapper?.SetStationGlobalValue("StationState.WorkOrder", 1, value);
                    testExecutionWrapper?.CommitToDisk(false);
                }
            }
        }
@@ -280,14 +288,32 @@
            get => _DisableScanner;
            set
            {
                if (WriteConfig("DisableScanner", value))
                if (RequirePassword() == true)
                {
                    Set(ref _DisableScanner, value);
                    if (WriteConfig("DisableScanner", value))
                    {
                        Set(ref _DisableScanner, value);
                    }
                }
            }
        }
        private string tsModelPath;
        private bool enableManualDebug;
        public bool EnableManualDebug
        {
            get => enableManualDebug;
            set
            {
                if (IsTesting)
                {
                    MsgBoxHelper2.Warn("请先结束自动测试");
                    return;
                }
                Set(ref enableManualDebug, value);
            }
        }
        public string tsModelPath { get; private set; }
        private string dataLogPath;
        private SqliteHelper sqliteHelper;
@@ -297,13 +323,8 @@
        public TsExecution()
        {
            DataContext = this;
            GCTimer = new Timer();
            InitializeComponent();
            InitChart();
            InitTsEngine();
            //初始化TestStand引擎
            this.InitTestStandEngine();
        }
        public override void SetBaseFolder(string folder)
@@ -355,6 +376,7 @@
        public override void InitFinish()
        {
            InitTsEngine();
            this.ReadCount(out int pass, out int fail);
            this.DisplayCount(pass, fail);
            ReadConfig();
@@ -373,12 +395,40 @@
            {
                UserInfo user = UserInfo.User;
                CurrentUserCode = user?.Code;
                engine?.Globals.SetValString("StationState.OPID", 1, CurrentUserCode);
                engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                testExecutionWrapper?.SetStationGlobalValue("StationState.OPID", 1, CurrentUserCode);
                testExecutionWrapper?.CommitToDisk(false);
            }
            catch (Exception ex)
            {
                log.Warn("Get Login User Failed.", ex);
            }
            RefreshUIConfig();
        }
        /// <summary>
        /// 刷新UI配置
        /// </summary>
        private void RefreshUIConfig()
        {
            try
            {
                bool enableManualDebug = ParameterManager.GetCommonParameterByName("EnableManualDebug", false);
                if (enableManualDebug)
                {
                    Cb_ManualDebug.Visibility = Visibility.Visible;
                    EnableManualDebug = false;
                }
                else
                {
                    Cb_ManualDebug.Visibility = Visibility.Collapsed;
                    EnableManualDebug = false;
                }
            }
            catch (Exception ex)
            {
                log.Error("RefreshUIConfig Error.", ex);
            }
        }
@@ -443,7 +493,7 @@
            Products = ProductManager.GetProducts<ObservableCollection<ProductData>>();
            if (Products != null && Products.Count > 0)
            {
                string lastModel = engine?.Globals.GetValString("StationState.ProductModel", 1);
                string lastModel = testExecutionWrapper?.GetStationGlobalValue<string>("StationState.ProductModel", 1);
                var tempProduct = Products[0];
                if (!string.IsNullOrEmpty(lastModel))
                {
@@ -487,25 +537,61 @@
        /// </summary>
        private void InitTsEngine()
        {
            AxApplicationMgr = TestAppManager.GetAxApplicationMgr();
            if (ParamSetting.EngineType == "Tap")
            {
                var wrapper = new TapWrapper();
                wrapper.RegisteUserInput(this);
                log.Debug($"Wrapper UI is {wrapper.ui}");
                testExecutionWrapper = wrapper;
            }
            else
            {
                testExecutionWrapper = new TeststandWrapper();
            }
            //AxApplicationMgr. += AxApplicationMgr_EndExecution;
            AxApplicationMgr.EndExecution += AxApplicationMgr_EndExecution;
            AxApplicationMgr.ExitApplication += this.AxApplicationMgr_ExitApplication;
            AxApplicationMgr.UIMessageEvent += this.AxApplicationMgr_UIMessageEvent;
            AxApplicationMgr.ExecutionClosed += this.AxApplicationMgr_ExecutionClosed;
            ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).BeginInit();
            host.Child = AxApplicationMgr;
            ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).EndInit();
            testExecutionWrapper.EndExecutionEvent += TestExecutionWrapper_EndExecutionEvent;
            testExecutionWrapper.UIMessageEvent += TestExecutionWrapper_UIMessageEvent;
            if (testExecutionWrapper is TeststandWrapper tsw)
            {
                var AxApplicationMgr = tsw.AxApplicationMgr;
                ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).BeginInit();
                host.Child = AxApplicationMgr;
                ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).EndInit();
            }
            testExecutionWrapper.Init();
        }
        private volatile bool canSwitchModel = false;
        private volatile Action switchAction = null;
        private void TestExecutionWrapper_UIMessageEvent(object sender, UIMessageEvent e)
        {
            try
            {
                int eventCode = Convert.ToInt32(e.EventCode);
                int index = Convert.ToInt16(e.NumberData);//SocketIndex
                string strData = e.StrData;
        private void AxApplicationMgr_EndExecution(object sender, _ApplicationMgrEvents_EndExecutionEvent e)
                try
                {
                    log.Debug($"Event: {eventCode}, NumericData: {index}, StringData: {strData}");
                }
                catch
                {
                }
                if (eventCode == 10001)
                {
                    testExecutionWrapper?.SetContext((SequenceContext)e.Data);
                    tsSockets[index].SequenceFileContext = (SequenceContext)e.Data;
                    return;
                }
                HandleUIMessage(eventCode, index, strData);
            }
            catch (Exception ex)
            {
                log.Warn("Ts Event Error.", ex);
            }
        }
        private void TestExecutionWrapper_EndExecutionEvent(object sender, ExecutionEndEvent e)
        {
            log.Info("AxApplicationMgr_EndExecution");
            if (ReleaseSequenceFile())
@@ -520,15 +606,8 @@
            }
        }
        /// <summary>
        /// 进程结束事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AxApplicationMgr_ExecutionClosed(object sender, _ApplicationMgrEvents_ExecutionClosedEvent e)
        {
            //ReleaseSequenceFile();
        }
        private volatile bool canSwitchModel = false;
        private volatile Action switchAction = null;
        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
        {
@@ -547,17 +626,6 @@
                {
                    log.Error("TsExecution Loaded Failed.", ex);
                    MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
                }
                try
                {
                    if (Environment.Version.ToString() != this.AxApplicationMgr.GetEngine().DotNetCLRVersion)
                        this.GCTimer.Enabled = true;
                }
                catch (Exception ex)
                {
                    log.Error("TsExecution GCTimer Start Failed", ex);
                    MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.LoadFailed")}: {ex.Message}");
                }
                Dispatcher.Invoke(() =>
@@ -633,6 +701,7 @@
                {
                    SocketName = name
                };
                control.parent = this;
                panel.Children.Add(control);
                tsSockets.Add(control);
@@ -649,26 +718,6 @@
            else
            {
                panel.Columns = 2;
            }
        }
        /// <summary>
        /// 初始化TestStand引擎
        /// </summary>
        private void InitTestStandEngine()
        {
            try
            {
                this.engine = this.AxApplicationMgr.GetEngine();
                this.engine.PersistBreakpoints = false;
                this.AxApplicationMgr.IsEditor = true;
                this.AxApplicationMgr.LoginOnStart = false;
                this.AxApplicationMgr.Start();
                //this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
            }
            catch (Exception ex)
            {
                throw new Exception(LangUtil.Get("Ts.InitTestStandEngineFail"), ex);
            }
        }
@@ -695,7 +744,7 @@
        /// <param name="fail"></param>
        private void ReadConfig()
        {
            ParameterData value;
            HMI.Core.Entity.ParameterData value;
            bool flag = false;
@@ -870,7 +919,7 @@
            {
                value = ParameterManager.GetCommonParameterByName("TestReportFolder");
                csvDir = value.Value;
                csvDir = value?.Value ?? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestReport");
                if (!Directory.Exists(csvDir))
                {
                    Directory.CreateDirectory(csvDir);
@@ -885,7 +934,7 @@
            {
                value = ParameterManager.GetCommonParameterByName("IsOptRequirePassword");
                IsOptRequirePassword = bool.Parse(value.Value);
                IsOptRequirePassword = value == null ? false : bool.Parse(value.Value);
            }
            catch
            {
@@ -955,7 +1004,7 @@
                    foreach (var ts in tsSockets)
                    {
                        ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.{name}", 1, value);
                        if (ts?.SequenceFileContext?.Sequence != null)
                        if (name == "EnableDebugMode" && ts?.SequenceFileContext?.Sequence != null)
                        {
                            ts.SequenceFileContext.Sequence.FailureAction =
                                value ? FailureActions.FailureAction_None : FailureActions.FailureAction_GotoCleanup;
@@ -1061,8 +1110,16 @@
        /// </summary>
        private void StartSequence()
        {
            log.Info($"Start ATE Seq isLoadOK:{isLoadOK}");
            if (!isLoadOK) return;
            engine?.Globals.SetValBoolean("StationState.StopTest", 0, false);
            try
            {
                testExecutionWrapper?.SetStationGlobalValue("StationState.StopTest", 1, false);
            }
            catch (Exception ex)
            {
                log.Error("Reset StopTest Failed.", ex);
            }
            if (ParamSetting.EnableAutoModel)
            {
                try
@@ -1077,9 +1134,10 @@
                        return;
                    }
                    this.testSeqFile = path;
                    this.modeSeq = this.engine.GetSequenceFileEx(tsModelPath);
                    this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 2, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                    this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                    testExecutionWrapper?.StartSequence(this.testSeqFile, this.tsModelPath);
                    //this.modeSeq = this.engine.GetSequenceFileEx(tsModelPath);
                    //this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 2, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                    //this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                    IsTesting = true;
                }
                catch (Exception ex)
@@ -1096,11 +1154,12 @@
                //MaskWindow mask = new MaskWindow();
                try
                {
                    engine?.Globals.SetValString("StationState.ProductModel", 1, CurrentProductModel);
                    engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                    this.modeSeq = this.engine.GetSequenceFileEx(tsModelPath);
                    this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 2, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                    this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                    testExecutionWrapper?.SetStationGlobalValue("StationState.ProductModel", 1, CurrentProductModel);
                    testExecutionWrapper?.CommitToDisk(false);
                    testExecutionWrapper?.StartSequence(this.testSeqFile, this.tsModelPath);
                    //this.modeSeq = this.engine.GetSequenceFileEx(tsModelPath);
                    //this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 2, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                    //this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                    IsTesting = true;
                }
                catch (Exception ex)
@@ -1135,20 +1194,14 @@
        {
            try
            {
                if (this.tsSockets.Count > 0)
                {
                    for (int i = 0; i < tsSockets.Count; i++)
                    {
                        var context = this.tsSockets[i].SequenceFileContext;
                        context?.StationGlobals.SetValBoolean("StationState.StopTest", 1, true);
                    }
                }
                if (this.execution == null) return true;
                testExecutionWrapper.SetStationGlobalValue("StationState.StopTest", 1, true);
                //bool res = this.execution.WaitForEndEx(-1, true, null, null);
                bool res = this.execution?.WaitForEndEx(-1, true, Type.Missing, null) == true;
                Marshal.ReleaseComObject(this.execution);
                this.execution = null;
                bool res = this.testExecutionWrapper.WaitForExit();
                //if (this.execution != null)
                //    Marshal.ReleaseComObject(this.execution);
                //this.execution = null;
                return res;
            }
            catch (Exception ex)
@@ -1208,22 +1261,232 @@
            //    }).Info(LangUtil.Get("Ts.SequenceIsStarting"));
        }
        /// <summary>
        /// 退出应用
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AxApplicationMgr_ExitApplication(object sender, EventArgs e)
        {
            //Environment.ExitCode = this.AxApplicationMgr.ExitCode;
            //this.Close();
        }
        public void UpdatePositionStatus(int index, string status)
        {
            this.tsSockets[index].UpdateStatus(status);
            this.UpdateTestCounter();
            Dispatcher.Invoke(() =>
            {
                this.tsSockets[index].UpdateStatus(status);
                OnStatusPublished(index, status);
                this.UpdateTestCounter();
            });
        }
        private void OnStatusPublished(int index, string status)
        {
            if (ParamSetting.CustomerCode != "SOUND")
            {
                return;
            }
            if (status == "Passed" || status == "Failed")
            {
                SaveSOUNDExcel(index, status);
            }
        }
        private string ConvertStatus(string status)
        {
            if (string.Equals(status, "passed", StringComparison.OrdinalIgnoreCase))
            {
                return "Pass";
            }
            else
            {
                return "Fail";
            }
        }
        #region 桑德定制
        private ICell CreateCell(ISheet sheet, int r, int c, ICellStyle style)
        {
            var row = sheet.GetRow(r);
            if (row == null)
            {
                row = sheet.CreateRow(r);
            }
            var cell = row.GetCell(c);
            if (cell == null)
            {
                cell = row.CreateCell(c);
            }
            if (style != null)
            {
                cell.CellStyle = style;
            }
            return cell;
        }
        private string GetOutputFolder()
        {
            var value = ParameterManager.GetCommonParameterByName("SOUND_Output");
            if (value != null)
            {
                return value.Value;
            }
            return ParamSetting.DataLogFolder;
        }
        private void SaveSOUNDExcel(int index, string status)
        {
            lock (this)
            {
                try
                {
                    string outputFolder = GetOutputFolder();
                    var socket = this.tsSockets[index];
                    DateTime testTime = DateTime.Now;
                    string filePath = Path.Combine(outputFolder, $"{testTime.ToString("yy-MM-dd")}_{CurrentProductModel}.xls");
                    if (!File.Exists(filePath))
                    {
                        Dictionary<string, int> columnMap = new Dictionary<string, int>();
                        HSSFWorkbook workbook = new HSSFWorkbook();
                        ISheet sheet = workbook.CreateSheet("Sheet1");
                        ICellStyle border = workbook.CreateCellStyle();
                        border.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
                        border.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
                        border.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
                        border.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
                        border.BottomBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                        border.TopBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                        border.LeftBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                        border.RightBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                        ICellStyle red = workbook.CreateCellStyle();
                        red.CloneStyleFrom(border);
                        red.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Red.Index;
                        red.FillPattern = FillPattern.SolidForeground;
                        ICellStyle green = workbook.CreateCellStyle();
                        green.CloneStyleFrom(border);
                        green.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.BrightGreen.Index;
                        green.FillPattern = FillPattern.SolidForeground;
                        CreateCell(sheet, 1, 0, border);
                        CreateCell(sheet, 1, 1, border);
                        CreateCell(sheet, 1, 2, border);
                        CreateCell(sheet, 1, 3, border);
                        CreateCell(sheet, 1, 4, border);
                        CreateCell(sheet, 2, 0, border);
                        CreateCell(sheet, 2, 1, border);
                        CreateCell(sheet, 2, 2, border);
                        CreateCell(sheet, 2, 3, border);
                        CreateCell(sheet, 2, 4, border).SetCellValue("上限");
                        CreateCell(sheet, 3, 0, border).SetCellValue("测试时间");
                        CreateCell(sheet, 3, 1, border).SetCellValue("测试人");
                        CreateCell(sheet, 3, 2, border).SetCellValue("测试设备");
                        CreateCell(sheet, 3, 3, border).SetCellValue("条形码");
                        CreateCell(sheet, 3, 4, border).SetCellValue("下限");
                        int column = 5;
                        int row = 4;
                        CreateCell(sheet, row, 0, border).SetCellValue(testTime.ToString("*HH:mm:ss*"));
                        CreateCell(sheet, row, 1, border).SetCellValue(UserInfo.User.Name);
                        CreateCell(sheet, row, 2, border).SetCellValue(ParamSetting.StationName);
                        CreateCell(sheet, row, 3, border).SetCellValue(socket.Barcode);
                        CreateCell(sheet, row, 4, status == "Passed" ? green : red).SetCellValue(ConvertStatus(status));
                        foreach (var data in socket.Datasource)
                        {
                            int col = column;
                            if (columnMap.ContainsKey(data.Name))
                            {
                                col = columnMap[data.Name];
                            }
                            else
                            {
                                columnMap[data.Name] = column++;
                            }
                            CreateCell(sheet, 1, col, border).SetCellValue(data.Name);
                            CreateCell(sheet, 2, col, border).SetCellValue(data.LimitUp);
                            CreateCell(sheet, 3, col, border).SetCellValue(data.LimitDown);
                            CreateCell(sheet, row, col, data.Result == "Failed" ? red : border).SetCellValue(data.Value);
                        }
                        using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
                        {
                            workbook.Write(fs);
                        }
                    }
                    else
                    {
                        using (FileStream fs = File.OpenRead(filePath))
                        {
                            IWorkbook workbook = new HSSFWorkbook(fs);
                            ICellStyle border = workbook.CreateCellStyle();
                            border.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
                            border.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
                            border.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
                            border.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
                            border.BottomBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                            border.TopBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                            border.LeftBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                            border.RightBorderColor = NPOI.HSSF.Util.HSSFColor.Black.Index;
                            ICellStyle red = workbook.CreateCellStyle();
                            red.CloneStyleFrom(border);
                            red.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Red.Index;
                            red.FillPattern = FillPattern.SolidForeground;
                            ICellStyle green = workbook.CreateCellStyle();
                            green.CloneStyleFrom(border);
                            green.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.BrightGreen.Index;
                            green.FillPattern = FillPattern.SolidForeground;
                            ISheet sheet = workbook.GetSheet("Sheet1");
                            Dictionary<string, int> columnMap = new Dictionary<string, int>();
                            var titleRow = sheet.GetRow(1);
                            for (int i = 0; i < titleRow.LastCellNum; i++)
                            {
                                if (!string.IsNullOrEmpty(titleRow.GetCell(i).StringCellValue))
                                {
                                    columnMap[titleRow.GetCell(i).StringCellValue] = i;
                                }
                            }
                            int row = sheet.LastRowNum + 1;
                            CreateCell(sheet, row, 0, border).SetCellValue(testTime.ToString("*HH:mm:ss*"));
                            CreateCell(sheet, row, 1, border).SetCellValue(UserInfo.User.Name);
                            CreateCell(sheet, row, 2, border).SetCellValue(ParamSetting.StationName);
                            CreateCell(sheet, row, 3, border).SetCellValue(socket.Barcode);
                            CreateCell(sheet, row, 4, status == "Passed" ? green : red).SetCellValue(ConvertStatus(status));
                            foreach (var data in socket.Datasource)
                            {
                                int col = titleRow.LastCellNum;
                                if (columnMap.ContainsKey(data.Name))
                                {
                                    col = columnMap[data.Name];
                                }
                                else
                                {
                                    columnMap[data.Name] = col;
                                }
                                CreateCell(sheet, 1, col, border).SetCellValue(data.Name);
                                CreateCell(sheet, 2, col, border).SetCellValue(data.LimitUp);
                                CreateCell(sheet, 3, col, border).SetCellValue(data.LimitDown);
                                CreateCell(sheet, row, col, data.Result == "Failed" ? red : border).SetCellValue(data.Value);
                            }
                            using (FileStream nfs = new FileStream(filePath, FileMode.OpenOrCreate))
                            {
                                workbook.Write(nfs);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    log.Error("SOUND Export Excel Data Error.", ex);
                }
            }
        }
        #endregion
        public void UpdatePositionProcessValue(int index, int value)
        {
@@ -1287,9 +1550,11 @@
                sqls.Add($@"INSERT INTO serialData (sn,model,station,step,position,limitDown,limitUp,unit,value,status,finalStatus,consumeTime,totalConsumeTime,time) VALUES
('{snr}','{model}','','{data.Name}','{index}','{data.LimitDown}','{data.LimitUp}','{data.Unit}','{data.Value}','{data.Result}','{socket.Status}','{data.ConsumeTime}','{socket.ElapseTime}','{time}')");
            }
            sqls.Add($@"INSERT INTO testResult (sn,model,station,result,time,position,status) VALUES
('{snr}','{model}','','{socket.Status}','{time}','{index}','{status}')");
            sqliteHelper.ExecuteBatchSql(sqls);
            var ssql = $@"INSERT INTO testResult (sn,model,station,result,time,position,status) VALUES
('{snr}','{model}','','{socket.Status}','{time}','{index}','{status}')";
            sqliteHelper.ExecuteSql(ssql);
        }
        private void HandleUIMessage(int eventCode, int index, string strData)
@@ -1314,17 +1579,26 @@
                    break;
                case 10006://发送产品条码
                    SetPositionBarcode(index, strData);
                    Dispatcher.Invoke(() =>
                    {
                        SetPositionBarcode(index, strData);
                    });
                    break;
                case 10007://开始测试计时
                    tsSockets[index].ElapseTime = "Start Timing";
                    BtnStopSeq.IsEnabled = false;
                    Dispatcher.Invoke(() =>
                    {
                        tsSockets[index].ElapseTime = "Start Timing";
                        BtnStopSeq.IsEnabled = false;
                    });
                    break;
                case 10008://停止测试计时
                    tsSockets[index].ElapseTime = "Stop Timing";
                    BtnStopSeq.IsEnabled = true;
                    Dispatcher.Invoke(() =>
                    {
                        tsSockets[index].ElapseTime = "Stop Timing";
                        BtnStopSeq.IsEnabled = true;
                    });
                    break;
                case 10009:
@@ -1365,7 +1639,10 @@
                    break;
                case 10017://更新测试计时,针对部分计时有PLC完成的项目
                    this.tsSockets[index].ElapseTime = strData;
                    Dispatcher.Invoke(() =>
                    {
                        this.tsSockets[index].ElapseTime = strData;
                    });
                    break;
                case (int)UIMessageCodes.UIMsg_EndExecution://执行结束
                    //IsTesting = false;
@@ -1373,9 +1650,32 @@
                    break;
                case 10030://显示测试进度
                    int ProcessVal = Convert.ToInt32(strData);
                    this.tsSockets[index].SetTestProcess(ProcessVal);
                    Dispatcher.Invoke(() =>
                    {
                        this.tsSockets[index].SetTestProcess(ProcessVal);
                    });
                    break;
                case 10031://导出CSV
                    if (ParamSetting.CustomerCode == "SOUND")
                    {
                        string[] datas = strData.Split(';');
                        this.tsSockets[index].WriteToExcel(datas[0]);
                        if (datas.Length >= 2)
                        {
                            SavePositionDB(index, datas[1]);
                        }
                        else
                        {
                            Dispatcher.Invoke(() =>
                            {
                                var sss = this.tsSockets[index].Status;
                                SavePositionDB(index, sss);
                            });
                        }
                        return;
                    }
                    if (string.IsNullOrEmpty(strData))
                    {
                        SaveTemplateCSV(index);
@@ -1391,8 +1691,11 @@
                        }
                        else
                        {
                            var sss = this.tsSockets[index].Status;
                            SavePositionDB(index, sss);
                            Dispatcher.Invoke(() =>
                            {
                                var sss = this.tsSockets[index].Status;
                                SavePositionDB(index, sss);
                            });
                        }
                    }
                    catch (Exception ex)
@@ -1426,6 +1729,22 @@
                        points[i - 1] = new int[2] { int.Parse(strs[0]), int.Parse(strs[1]) };
                    }
                    this.tsSockets[index].DrawScreenPoints(int.Parse(arr_10035[0]), points);
                    break;
                case 100350:
                    string[] arr_100350 = strData.Split(';');
                    int[][] points_0 = new int[arr_100350.Length - 1][];
                    for (int i = 1; i < arr_100350.Length; i++)
                    {
                        if (string.IsNullOrEmpty(arr_100350[i]))
                        {
                            continue;
                        }
                        string[] strs = arr_100350[i].Split(',');
                        points_0[i - 1] = new int[2] { int.Parse(strs[0]), int.Parse(strs[1]) };
                    }
                    this.tsSockets[index].DrawNewScreenPoints(int.Parse(arr_100350[0]), points_0);
                    break;
                case 10036:
                    this.tsSockets[index].ClearScreenPoint(int.Parse(strData));
@@ -1472,8 +1791,8 @@
                            {
                                log?.Info("开始自动换型");
                                SelectedProduct = target;
                                engine?.Globals.SetValString($"CacheSNR[{index}]", 1, snr);
                                engine?.CommitGlobalsToDisk(false);
                                testExecutionWrapper?.SetStationGlobalValue($"CacheSNR[{index}]", 1, snr);
                                testExecutionWrapper?.CommitToDisk(false);
                                StartClick();
                            }
                            catch (Exception ex)
@@ -1489,43 +1808,7 @@
        private void ShowAlarm(AlarmText alarm)
        {
            AlarmShowHelper.ShowAlarm(alarm);
            mSMQHelper.Send("Alarm", $"ATE测试 {alarm.AlarmInfo}");
        }
        /// <summary>
        /// TestStand用户交互事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AxApplicationMgr_UIMessageEvent(object sender, _ApplicationMgrEvents_UIMessageEventEvent e)
        {
            try
            {
                int eventCode = Convert.ToInt32(e.uiMsg.Event);
                int index = Convert.ToInt16(e.uiMsg.NumericData);//SocketIndex
                string strData = e.uiMsg.StringData;
                try
                {
                    log.Debug($"Event: {eventCode}, NumericData: {index}, StringData: {strData}");
                }
                catch
                {
                }
                if (eventCode == 10001)
                {
                    sequenceContext = (SequenceContext)e.uiMsg.ActiveXData;
                    tsSockets[index].SequenceFileContext = (SequenceContext)e.uiMsg.ActiveXData;
                    return;
                }
                HandleUIMessage(eventCode, index, strData);
            }
            catch (Exception ex)
            {
                log.Warn("Ts Event Error.", ex);
            }
            mSMQHelper.Send("Alarm", "PC", $"ATE测试 {alarm.AlarmInfo}", 2);
        }
        private void UpdateTestCounter()
@@ -1548,7 +1831,7 @@
                return;
            }
            string name = product.Name;
            List<ParameterData> parameters = ProductManager.GetParametersByProductName(name);
            List<HMI.Core.Entity.ParameterData> parameters = ProductManager.GetParametersByProductName(name);
            testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value?.ToString();
            if (AutoExecuteSeq)
            {
@@ -1586,6 +1869,7 @@
        public override void DoClose(object sender, EventArgs e)
        {
            testExecutionWrapper?.Dispose();
            base.DoClose(sender, e);
        }
@@ -1598,32 +1882,13 @@
            {
                lock (this)
                {
                    if (this.testSeq == null)
                    {
                        return true;
                    }
                    //execution.Abort();
                    int count = 10;
                    while (count > 0)
                    {
                        try
                        {
                            if (this.testSeq == null)
                                return true;
                            if (this.engine?.ReleaseSequenceFileEx(this.testSeq, 4) == true)
                            {
                                Marshal.ReleaseComObject(this.testSeq);
                                this.testSeq = null;
                                // GC.Collect();
                            }
                            if (this.engine?.ReleaseSequenceFileEx(this.modeSeq, 4) == true)
                            {
                                Marshal.ReleaseComObject(this.modeSeq);
                                this.modeSeq = null;
                            }
                            this.engine?.UnloadAllModules();
                            return this.testSeq == null && this.modeSeq == null;
                            return testExecutionWrapper.ReleaseSeqFile();
                        }
                        catch (Exception ex)
                        {