using HMI.Base; using HMI.Core; using HMI.Core.Entity; using HMI.Core.Manager; using HMI.Core.Util; using HMI.UI; using HMI.UI.Util; using HMI.Util; using log4net; using NationalInstruments.TestStand.Interop.API; using NationalInstruments.TestStand.Interop.UI.Ax; using Panuon.WPF.UI; using ScottPlot; using ScottPlot.Plottables; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Forms.DataVisualization.Charting; using System.Xml.Linq; namespace Plugin.TsExecution { /// /// UserControl1.xaml 的交互逻辑 /// public partial class TsExecution : PluginControl { #region TestStand事件 /// /// TS 引擎 /// private Engine engine = null; private string testSeqFile = string.Empty; /// /// 测试序列 /// private SequenceFile testSeq = null; /// /// 测试模型序列 /// private SequenceFile modeSeq = null; public AxApplicationMgr AxApplicationMgr; private Execution execution = null; private Chart chart; #endregion public static ILog logger { get; private set; } private List tsSockets = new List(); /// /// 样件模式状态 /// private bool sampleStatus; public bool SampleStatus { get { return sampleStatus; } set { Set(ref sampleStatus, value); SampleTest(value); } } private string _currentUserCode; public string CurrentUserCode { get { return _currentUserCode; } set { Set(ref _currentUserCode, value); } } private bool isLoadOK = false; private Timer GCTimer; private int PassAmount = 0; private int FailAmount = 0; /// /// Socket数量 /// private int socketNum = 1; private ProductData selectedProduct; public ProductData SelectedProduct { get { return selectedProduct; } set { Set(ref selectedProduct, value); SetTestSeqFile(value); } } public override void SetLogger(ILog logger) { base.SetLogger(logger); TsExecution.logger = logger; } private ObservableCollection products; public ObservableCollection Products { get { return products; } set { Set(ref products, value); } } private Pie pie; private string CountIniPath; private bool alarmVisiable; public bool AlarmVisiable { get => alarmVisiable; set { Set(ref alarmVisiable, value); } } private bool _enableMes; public bool EnableMes { get => _enableMes; set { if (WriteConfig("EnableMes", value)) { Set(ref _enableMes, value); } } } private bool _enableCanLog; public bool EnableCanLog { get => _enableCanLog; set { if (WriteConfig("EnableCanLog", value)) { Set(ref _enableCanLog, value); } } } private bool _enableDebugMode; public bool EnableDebugMode { get => _enableDebugMode; set { if (WriteConfig("EnableDebugMode", value)) { Set(ref _enableDebugMode, value); } } } private bool _enableNGPause; public bool EnableNGPause { get => _enableNGPause; set { if (WriteConfig("EnableNGPause", value)) { Set(ref _enableNGPause, value); } } } private bool _enableSimpleMode; public bool EnableSimpleMode { get => _enableSimpleMode; set { if (WriteConfig("EnableSimpleMode", value)) { Set(ref _enableSimpleMode, value); foreach (var cell in tsSockets) cell.SetSimpleUI(value); } } } private bool _ui_showInfoDock; public bool UI_ShowInfoDock { get => _ui_showInfoDock; set { if (WriteConfig("UI_ShowInfoDock", value)) { Set(ref _ui_showInfoDock, value); detailPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed; } } } public TsExecution() { DataContext = this; GCTimer = new Timer(); InitializeComponent(); InitChart(); InitTsEngine(); //初始化TestStand引擎 this.InitTestStandEngine(); } public override void SetBaseFolder(string folder) { CountIniPath = Path.Combine(folder, "Count.ini"); base.SetBaseFolder(folder); } public override void InitFinish() { this.ReadCount(out int pass, out int fail); this.DisplayCount(pass, fail); InitProducts(); base.InitFinish(); } /// /// 初始化Winform图表 /// private void InitChart() { pieChart.Plot.Axes.Frameless(); pieChart.Plot.HideGrid(); pieChart.Plot.DataBackground.Color = Colors.Transparent; var legend = pieChart.Plot.ShowLegend(Alignment.UpperRight); legend.Alignment = Alignment.UpperRight; legend.Padding = new PixelPadding(0); legend.FontSize = (float)WidthSizeConverter.CountFontSize(20, 300, 300); pieChart.UserInputProcessor.Disable(); pieChart.Plot.Font.Automatic(); List slices = new List { new PieSlice() { Value = 5, FillColor = Colors.Green, LabelFontSize = (float)WidthSizeConverter.CountFontSize(20,300,300), Label = "Pass",LegendText="Pass" }, new PieSlice() { Value = 2, FillColor = Colors.Red, LabelFontSize = (float)WidthSizeConverter.CountFontSize(20,300,300), Label = "Fail",LegendText="Fail" } }; pie = pieChart.Plot.Add.Pie(slices); } private void InitProducts() { Products = ProductManager.GetProducts>(); if (Products != null && Products.Count > 0) { List parameters = ParameterManager.GetCommonParameter>(); string tmpNum = parameters?.FirstOrDefault(p => p.Name == "SocketNum")?.Value?.ToString(); if (!string.IsNullOrEmpty(tmpNum)) { this.socketNum = Convert.ToInt32(tmpNum); } string lastModel = engine?.Globals.GetValString("StationState.ProductModel", 1); if (!string.IsNullOrEmpty(lastModel)) { var product = Products.FirstOrDefault(p => p.Name == lastModel); if (product != null) SelectedProduct = product; goto Write; } goto Write; Write: SelectedProduct = Products[0]; engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name); RefershUI(); } SampleStatus = false; } private bool _isTesting = false; public bool IsTesting { get { return _isTesting; } set { Set(ref _isTesting, value); Dispatcher.Invoke(() => { BtnStartSeq.IsEnabled = !value; BtnStopSeq.IsEnabled = value; CbProduct.IsEnabled = !value; }); } } /// /// 初始化TestStand引擎 /// private void InitTsEngine() { AxApplicationMgr = new AxApplicationMgr(); AxApplicationMgr.ExitApplication += new EventHandler(this.AxApplicationMgr_ExitApplication); AxApplicationMgr.UIMessageEvent += new _ApplicationMgrEvents_UIMessageEventEventHandler(this.AxApplicationMgr_UIMessageEvent); AxApplicationMgr.ExecutionClosed += new _ApplicationMgrEvents_ExecutionClosedEventHandler(this.AxApplicationMgr_ExecutionClosed); ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).BeginInit(); host.Child = AxApplicationMgr; ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).EndInit(); } private void PluginControl_Loaded(object sender, RoutedEventArgs e) { new MaskWindow((WindowX)Window.GetWindow(this), (win) => { try { Dispatcher.Invoke(() => { try { UserInfo user = UserInfo.User; CurrentUserCode = user?.Code; } catch (Exception ex) { log.Warn("Get Login User Failed.", ex); } ReadConfig(); }); isLoadOK = true; StartSequence(); } catch (Exception ex) { 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(() => { if (isLoadOK) Loaded -= PluginControl_Loaded; }); //Dispatcher.Invoke(() => //{ // try // { // ReadConfig(); // //连接OPChandle // //this.OpcHandle.Connect2("127.0.0.1", 6543, 18081, out string ErrMsg, null); // isLoadOK = true; // //启动测试进程 // StartSequence(); // } // catch (Exception ex) // { // log.Error("TsExecution Loaded Failed.", ex); // MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}"); // } //}); } ).Info(LangUtil.Get("App.UsercontrolIsLoading")); } private void RefershUI() { ParameterData p = ParameterManager.GetCommonParameterByName("ProbeWarn"); int ProbeWarn = 0; if (p != null) { try { ProbeWarn = Convert.ToInt32(p.Value); } catch { ProbeWarn = 0; } } panel.Children.Clear(); //重新生成子面板 for (int i = 0; i < this.socketNum; i++) { SingleControl control = new SingleControl(i, CountIniPath, ProbeWarn); panel.Children.Add(control); tsSockets.Add(control); } if (tsSockets.Count == 1) { panel.Columns = 1; } else { panel.Columns = 2; } } /// /// 初始化TestStand引擎 /// 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); } } /// /// 读取Count配置文件的pass和fail数 /// /// 合格数 /// 不合格数 private void ReadCount(out int pass, out int fail) { pass = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Pass", "0", CountIniPath)); fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "0", CountIniPath)); } /// /// 读取配置文件里的几个属性 /// /// /// private void ReadConfig() { ParameterData value; try { value = ParameterManager.GetCommonParameterByName("EnableMes"); _enableMes = bool.Parse(value.Value); Set("EnableMes"); } catch { _enableMes = false; Set("EnableMes"); } try { value = ParameterManager.GetCommonParameterByName("EnableNGPause"); _enableNGPause = bool.Parse(value.Value); Set("EnableNGPause"); } catch { _enableNGPause = false; Set("EnableNGPause"); } try { value = ParameterManager.GetCommonParameterByName("EnableCanLog"); _enableCanLog = bool.Parse(value.Value); Set("EnableCanLog"); } catch { _enableCanLog = false; Set("EnableCanLog"); } try { value = ParameterManager.GetCommonParameterByName("EnableDebugMode"); _enableDebugMode = bool.Parse(value.Value); Set("EnableDebugMode"); } catch { _enableDebugMode = false; Set("EnableDebugMode"); } try { value = ParameterManager.GetCommonParameterByName("EnableSimpleMode"); EnableSimpleMode = bool.Parse(value.Value); } catch { EnableSimpleMode = false; } try { value = ParameterManager.GetCommonParameterByName("UI_ShowInfoDock"); UI_ShowInfoDock = bool.Parse(value.Value); } catch { UI_ShowInfoDock = false; } //Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableMes", "true", CountIniPath)); //EnableCanLog = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableCanLog", "true", CountIniPath)); //EnableDebugMode = Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableDebugMode", "true", CountIniPath)); } //private void WriteToTsWhenInit() //{ // foreach (var ts in tsSockets) // { // ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableMes", 1, EnableMes); // ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableNGPause", 1, EnableNGPause); // ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableCanLog", 1, EnableCanLog); // ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.EnableDebugMode", 1, EnableDebugMode); // } //} private bool WriteConfig(string name, bool value) { //IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath); try { if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg)) { foreach (var ts in tsSockets) { ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.{name}", 1, value); } return true; } else { throw new Exception(errMsg); } } catch (Exception ex) { MsgBoxHelper2.Error(ex.Message); return false; } } private void WriteCount(double pass, double fail) { IniFileHelper.WriteIniData("Count", "Pass", pass.ToString(), CountIniPath); IniFileHelper.WriteIniData("Count", "Fail", fail.ToString(), CountIniPath); } /// /// 刷新计数 /// /// 合格数 /// 不合格数 private void DisplayCount(double pass, double fail) { double total = pass + fail; double passRate; if (total == 0) { passRate = 0.00; } else { passRate = (double)pass / (double)total * 100.00; } this.TbPass.Text = pass.ToString(); this.TbFail.Text = fail.ToString(); this.TbTotal.Text = total.ToString(); this.TbPassRate.Text = string.Format("{0:0.00}", passRate) + "%"; pie.Slices[0].Value = pass; pie.Slices[1].Value = fail; pieChart.Refresh(); WriteCount(pass, fail); } /// /// 启动样件测试 /// /// /// private void SampleTest(bool flag) { try { if (this.tsSockets.Count == 0) return; if (this.tsSockets[0].SequenceFileContext == null) return; bool val = this.tsSockets[0].SequenceFileContext.StationGlobals.Exists("StationState.Sample", 0); if (val) { this.tsSockets[0].SequenceFileContext.StationGlobals.SetValBoolean("StationState.Sample", 0, flag); } } catch (Exception ex) { throw new Exception(LangUtil.Get("Ts.SampleTestFail"), ex); } } /// /// 启动测试序列 /// private void StartSequence() { if (!isLoadOK) return; //MaskWindow mask = new MaskWindow(); try { this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq"); this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 0, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType); this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null); IsTesting = true; } catch (Exception ex) { Dispatcher.Invoke(() => { log.Error("Start Sequence Failed.", ex); MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.StartSequenceFail")}: {ex.Message}"); }); } finally { } } public void StopSequenceClick() { new MaskWindow((WindowX)Window.GetWindow(this), (win) => { try { bool stoped = StopSequence(); if (stoped) { IsTesting = false; } } finally { Dispatcher.Invoke(() => { win.Close(); }); } //Dispatcher.Invoke(() => //{ //}); } ).Info(LangUtil.Get("App.SequenceIsStopping")); } /// /// 停止测试序列 /// private bool StopSequence() { try { if (this.tsSockets.Count > 0) { for (int i = 0; i < tsSockets.Count; i++) { var context = this.tsSockets[i].SequenceFileContext; //bool val = context.StationGlobals.Exists("StationState.StopTest", 0); //if (val) //{ // context.StationGlobals.SetValBoolean("StationState.StopTest", 0, true); // bool isStop = this.execution.WaitForEndEx(2000, true, null, null); //} context?.StationGlobals.SetValBoolean("StationState.StopTest", 1, true); } } if (this.execution == null) return true; return this.execution.WaitForEndEx(2000, true, null, null); } catch (Exception ex) { log.Error("Stop Sequence Failed.", ex); MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.StopSequenceFail")}: {ex.Message}"); return false; } } private void Button_Click(object sender, RoutedEventArgs e) { Button btn = sender as Button; switch (btn.Name) { case "BtnStartSeq": StartClick(); break; case "BtnStopSeq": StopSequenceClick(); break; case "BtnClearNum": ClearNum(); break; case "BtnSample": SampleStatus = !SampleStatus; break; default: break; } } private void StartClick() { new MaskWindow(Window.GetWindow(this), (win) => { try { StartSequence(); } finally { win.Dispatcher.Invoke(() => { win.Close(); }); } }).Info(LangUtil.Get("Ts.SequenceIsStarting")); } /// /// 退出应用 /// /// /// private void AxApplicationMgr_ExitApplication(object sender, EventArgs e) { //Environment.ExitCode = this.AxApplicationMgr.ExitCode; //this.Close(); } /// /// 进程结束事件 /// /// /// private void AxApplicationMgr_ExecutionClosed(object sender, _ApplicationMgrEvents_ExecutionClosedEvent e) { try { if (this.testSeq == null) return; this.engine.ReleaseSequenceFileEx(this.testSeq, 0); this.engine.ReleaseSequenceFileEx(this.modeSeq, 0); this.engine.UnloadAllModules(); this.testSeq = null; this.modeSeq = null; } catch (Exception ex) { log.Error("ReleaseSequenceFileEx Failed.", ex); MsgBoxHelper.Error(Window.GetWindow(this), $"{LangUtil.Get("Ts.ReleaseSequenceFileFail")}: {ex.Message}"); } } public void UpdatePositionStatus(int index, string status) { this.tsSockets[index].UpdateStatus(status); this.UpdateTestCounter(); } public void UpdatePositionProcessValue(int index, int value) { this.tsSockets[index].SetTestProcess(value); } public void PausePosition(int index) { this.tsSockets[index].ShowPausePanel(); } public void AddTestData(int index, string data) { string[] TestItems = data.Split(','); tsSockets[index].AddTestData(TestItems[0], TestItems[1], TestItems[2], TestItems[3], TestItems[4], TestItems[5], TestItems[6]); } public void SetPositionBarcode(int index, string barcode) { this.tsSockets[index].Barcode = barcode; } /// /// TestStand用户交互事件 /// /// /// private void AxApplicationMgr_UIMessageEvent(object sender, _ApplicationMgrEvents_UIMessageEventEvent e) { try { int EventCase = Convert.ToInt32(e.uiMsg.Event); int Index = Convert.ToInt16(e.uiMsg.NumericData);//SocketIndex string strData = e.uiMsg.StringData; try { log.Info($"Event: {EventCase}, NumericData: {Index}, StringData: {strData}"); } catch { } switch (EventCase) { case 10001://传递SocketContext tsSockets[Index].SequenceFileContext = (SequenceContext)e.uiMsg.ActiveXData; break; case 10002://更新测试状态 string status = e.uiMsg.StringData; UpdatePositionStatus(Index, status); break; case 10003://发送测试数据 AddTestData(Index, e.uiMsg.StringData); break; case 10004://发送测试过程信息 this.tsSockets[Index].CurrentStep = e.uiMsg.StringData; break; case 10005://初始化测试数据,清空测试数据 this.tsSockets[Index].IniTestData(); break; case 10006://发送产品条码 SetPositionBarcode(Index, e.uiMsg.StringData); break; case 10007://开始测试计时 tsSockets[Index].ElapseTime = "Start Timing"; BtnStopSeq.IsEnabled = false; break; case 10008://停止测试计时 tsSockets[Index].ElapseTime = "Stop Timing"; BtnStopSeq.IsEnabled = true; break; case 10009: // break; case 10010: //PropertyObj.SetValBoolean("Locals.MD5_Used", 0, true); //PropertyObj.SetValVariant("FileGlobals.MD5_Value", 0, MD5_DATA); //PropertyObj.SetValVariant("StationGlobals.MD5", 0, MD5_DATA); break; case 10011: // write model break; case 10012: // read model break; case 10013: // read continue break; case 10014: break; case 10015: break; case 10016://显示3D拼接图片 break; case 10017://更新测试计时,针对部分计时有PLC完成的项目 this.tsSockets[Index].ElapseTime = e.uiMsg.StringData; break; case (int)UIMessageCodes.UIMsg_EndExecution://执行结束 IsTesting = false; // this.ReleaseSequenceFile(); break; case 10030://显示测试进度 int ProcessVal = Convert.ToInt32(e.uiMsg.StringData); this.tsSockets[Index].SetTestProcess(ProcessVal); break; case 10031://导出CSV this.tsSockets[Index].WriteToCSV(e.uiMsg.StringData); break; case 10032://按下标暂停测试 this.tsSockets[Index].ShowPausePanel(); break; default: break; } } catch (Exception ex) { log.Warn("Ts Event Error.", ex); } } private void UpdateTestCounter() { double PassNum = 0; double FailNum = 0; foreach (SingleControl socket in this.tsSockets) { PassNum += socket.PassNum; FailNum += socket.FailNum; } this.DisplayCount(PassNum, FailNum); } private void SetTestSeqFile(ProductData product) { if (product == null) { testSeqFile = null; return; } string name = product.Name; List parameters = ProductManager.GetParametersByProductName(name); testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value?.ToString(); StartSequence(); } private void ClearNum() { if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), LangUtil.Get("Ts.MakeSureClearNum")) == MessageBoxResult.OK) { PassAmount = 0; FailAmount = 0; //this.OpcHandle.DoFctWriteSingle("Count_Clear", "1", out string ErrMsg); foreach (SingleControl socket in tsSockets) { socket.ResetCounter(); } DisplayCount(PassAmount, FailAmount); } } public override void OnClose(object sender, CancelEventArgs e) { try { StopSequence(); // this.engine?.TerminateAll(); // this.engine?.ReleaseSequenceFileEx(this.testSeq, 0); // this.engine?.ReleaseSequenceFileEx(this.modeSeq, 0); // this.engine?.UnloadAllModules(); // this.engine?.ShutDown(true); //this.testSeq = null; //this.modeSeq = null; base.OnClose(sender, e); } catch (Exception ex) { log.Error("OnClose Error.", ex); } } private void PluginControl_SizeChanged(object sender, SizeChangedEventArgs e) { } } }