using HMI.Base;
|
using HMI.Core;
|
using HMI.Core.Util;
|
using HMI.ParameterManage.Entity;
|
using HMI.ParameterManage.Util;
|
using HMI.UI;
|
using HMI.UI.Util;
|
using HMI.Util;
|
using NationalInstruments.TestStand.Interop.API;
|
using NationalInstruments.TestStand.Interop.UI.Ax;
|
using Panuon.WPF.UI;
|
using ScottPlot;
|
using ScottPlot.AxisLimitManagers;
|
using ScottPlot.Plottables;
|
using System;
|
using System.Collections.Generic;
|
using System.Collections.ObjectModel;
|
using System.ComponentModel;
|
using System.Drawing;
|
using System.IO;
|
using System.Linq;
|
using System.Timers;
|
using System.Windows;
|
using System.Windows.Controls;
|
using System.Windows.Forms.DataVisualization.Charting;
|
|
namespace Plugin.TsExecution
|
{
|
/// <summary>
|
/// UserControl1.xaml 的交互逻辑
|
/// </summary>
|
public partial class TsExecution : PluginControl
|
{
|
//private OpcInterface.OpcInterface OpcHandle = new OpcInterface.OpcInterface();
|
#region TestStand事件
|
/// <summary>
|
/// TS 引擎
|
/// </summary>
|
private Engine engine = 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 Chart chart;
|
#endregion
|
|
private List<SingleControl> tsSockets = new List<SingleControl>();
|
/// <summary>
|
/// 样件模式状态
|
/// </summary>
|
private bool sampleStatus;
|
public bool SampleStatus
|
{
|
get { return sampleStatus; }
|
set
|
{
|
Set(ref sampleStatus, value);
|
SampleTest(value);
|
}
|
}
|
|
private bool isLoadOK = false;
|
private Timer GCTimer;
|
|
private int PassAmount = 0;
|
private int FailAmount = 0;
|
|
/// <summary>
|
/// Socket数量
|
/// </summary>
|
private int socketNum = 1;
|
|
private ProductData selectedProduct;
|
|
public ProductData SelectedProduct
|
{
|
get { return selectedProduct; }
|
set
|
{
|
Set(ref selectedProduct, value);
|
SetTestSeqFile(value);
|
}
|
}
|
|
private ObservableCollection<ProductData> products;
|
|
public ObservableCollection<ProductData> 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);
|
}
|
}
|
|
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);
|
UpdateChart(pass, fail);
|
InitProducts();
|
base.InitFinish();
|
}
|
|
/// <summary>
|
/// 初始化Winform图表
|
/// </summary>
|
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)CustomFontSizeConverter.CountFontSize(20, 300, 300);
|
pieChart.UserInputProcessor.Disable();
|
pieChart.Plot.Font.Automatic();
|
|
List<PieSlice> slices = new List<PieSlice> {
|
new PieSlice() { Value = 5, FillColor = Colors.Green,
|
LabelFontSize = (float)CustomFontSizeConverter.CountFontSize(20,300,300),
|
Label = "Pass",LegendText="Pass" },
|
new PieSlice() { Value = 2, FillColor = Colors.Red,
|
LabelFontSize = (float)CustomFontSizeConverter.CountFontSize(20,300,300),
|
Label = "Fail",LegendText="Fail" } };
|
|
|
pie = pieChart.Plot.Add.Pie(slices);
|
}
|
|
private void InitProducts()
|
{
|
Products = ParameterManager.GetProducts<ObservableCollection<ProductData>>();
|
if (Products != null && Products.Count > 0)
|
{
|
List<ParameterData> parameters =
|
ParameterManager.GetCommonParameter<List<ParameterData>>();
|
string tmpNum = parameters?.FirstOrDefault(p => p.Name == "SocketNum")?.Value?.ToString();
|
if (!string.IsNullOrEmpty(tmpNum))
|
{
|
this.socketNum = Convert.ToInt32(tmpNum);
|
}
|
SelectedProduct = Products[0];
|
RefershUI();
|
}
|
SampleStatus = false;
|
}
|
|
/// <summary>
|
/// 初始化TestStand引擎
|
/// </summary>
|
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) =>
|
{
|
Dispatcher.Invoke(() =>
|
{
|
try
|
{
|
//连接OPChandle
|
//this.OpcHandle.Connect2("127.0.0.1", 6543, 18081, out string ErrMsg, null);
|
//启动测试进程
|
StartSequence();
|
isLoadOK = true;
|
}
|
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}");
|
}
|
finally
|
{
|
win.Close();
|
}
|
if (isLoadOK) Loaded -= PluginControl_Loaded;
|
});
|
}
|
).Info(LangUtil.Get("App.UsercontrolIsLoading"));
|
}
|
|
private void RefershUI()
|
{
|
panel.Children.Clear();
|
//重新生成子面板
|
for (int i = 0; i < this.socketNum; i++)
|
{
|
SingleControl control = new SingleControl(i, CountIniPath);
|
panel.Children.Add(control);
|
tsSockets.Add(control);
|
}
|
if (tsSockets.Count == 1)
|
{
|
panel.Columns = 1;
|
}
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 读取Count配置文件的pass和fail数
|
/// </summary>
|
/// <param name="pass">合格数</param>
|
/// <param name="fail">不合格数</param>
|
private void ReadCount(out int pass, out int fail)
|
{
|
pass = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Pass", "", CountIniPath));
|
fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "", CountIniPath));
|
}
|
|
private void WriteCount(int pass, int fail)
|
{
|
IniFileHelper.WriteIniData("Count", "Pass", pass.ToString(), CountIniPath);
|
IniFileHelper.WriteIniData("Count", "Fail", fail.ToString(), CountIniPath);
|
}
|
|
/// <summary>
|
/// 刷新计数
|
/// </summary>
|
/// <param name="pass">合格数</param>
|
/// <param name="fail">不合格数</param>
|
private void DisplayCount(int pass, int fail)
|
{
|
int 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) + "%";
|
WriteCount(pass, fail);
|
}
|
|
/// <summary>
|
/// 启动样件测试
|
/// </summary>
|
/// <param name="flag"></param>
|
/// <exception cref="Exception"></exception>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 启动测试序列
|
/// </summary>
|
private void StartSequence()
|
{
|
//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);
|
|
Dispatcher.Invoke(() =>
|
{
|
this.CbProduct.IsEnabled = false;
|
this.BtnStartSeq.IsEnabled = false;
|
this.BtnStopSeq.IsEnabled = 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) =>
|
{
|
Dispatcher.Invoke(() =>
|
{
|
try
|
{
|
StopSequence();
|
}
|
finally
|
{
|
win.Close();
|
}
|
});
|
}
|
).Info(LangUtil.Get("App.SequenceIsStopping"));
|
}
|
|
/// <summary>
|
/// 停止测试序列
|
/// </summary>
|
private void StopSequence()
|
{
|
try
|
{
|
if (this.tsSockets.Count > 0 && this.tsSockets[0]?.SequenceFileContext != null)
|
{
|
bool val = this.tsSockets[0].SequenceFileContext.StationGlobals.Exists("StationState.StopTest", 0);
|
if (val)
|
{
|
tsSockets[0].SequenceFileContext.StationGlobals.SetValBoolean("StationState.StopTest", 0, true);
|
bool isStop = this.execution.WaitForEndEx(2000, true, null, null);
|
}
|
}
|
this.CbProduct.IsEnabled = true;
|
this.BtnStartSeq.IsEnabled = true;
|
this.BtnStopSeq.IsEnabled = false;
|
}
|
catch (Exception ex)
|
{
|
log.Error("Stop Sequence Failed.", ex);
|
MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.StopSequenceFail")}: {ex.Message}");
|
}
|
}
|
|
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) =>
|
{
|
Dispatcher.Invoke(() =>
|
{
|
try
|
{
|
StartSequence();
|
}
|
finally
|
{
|
win.Close();
|
}
|
});
|
}).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();
|
}
|
|
/// <summary>
|
/// 进程结束事件
|
/// </summary>
|
/// <param name="sender"></param>
|
/// <param name="e"></param>
|
private void AxApplicationMgr_ExecutionClosed(object sender, NationalInstruments.TestStand.Interop.UI.Ax._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}");
|
}
|
}
|
|
/// <summary>
|
/// TestStand用户交互事件
|
/// </summary>
|
/// <param name="sender"></param>
|
/// <param name="e"></param>
|
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
|
switch (EventCase)
|
{
|
case 10001://传递SocketContext
|
tsSockets[Index].SequenceFileContext = (SequenceContext)e.uiMsg.ActiveXData;
|
break;
|
|
case 10002://更新测试状态
|
string status = e.uiMsg.StringData;
|
this.tsSockets[Index].UpdateStatus(status);
|
this.UpdateTestCounter();
|
break;
|
|
case 10003://发送测试数据
|
string[] TestItems = e.uiMsg.StringData.Split('\t');
|
tsSockets[Index].AddTestData(TestItems[0], TestItems[4], TestItems[1], TestItems[2], TestItems[3], TestItems[5]);
|
|
break;
|
|
case 10004://发送测试过程信息
|
this.tsSockets[Index].CurrentStep = e.uiMsg.StringData;
|
break;
|
|
case 10005://初始化测试数据,清空测试数据
|
this.tsSockets[Index].IniTestData();
|
break;
|
|
case 10006://发送产品条码
|
this.tsSockets[Index].Barcode = 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://执行结束
|
this.BtnStartSeq.IsEnabled = true;
|
this.BtnStopSeq.IsEnabled = true;
|
// this.ReleaseSequenceFile();
|
break;
|
case 10030://显示测试进度
|
int ProcessVal = Convert.ToInt32(e.uiMsg.StringData);
|
this.tsSockets[Index].SetTestProcess(ProcessVal);
|
break;
|
default:
|
break;
|
}
|
}
|
catch (Exception ex)
|
{
|
log.Warn("Ts Event Error.", ex);
|
}
|
}
|
|
private void UpdateTestCounter()
|
{
|
int PassNum = 0;
|
int FailNum = 0;
|
foreach (SingleControl socket in this.tsSockets)
|
{
|
PassNum += socket.PassNum;
|
FailNum += socket.FailNum;
|
}
|
this.DisplayCount(PassNum, FailNum);
|
UpdateChart(PassNum, FailNum);
|
}
|
|
private void UpdateChart(int pass, int fail)
|
{
|
pie.Slices[0].Value = pass;
|
pie.Slices[1].Value = fail;
|
pieChart.Refresh();
|
}
|
|
private void SetTestSeqFile(ProductData product)
|
{
|
string name = product.Name;
|
List<ParameterData> parameters = ParameterManager.GetParametersByProductName(name);
|
testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value?.ToString();
|
}
|
|
private void ClearNum()
|
{
|
if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), LangUtil.Get("Ts.MakeSureClearNum")) == MessageBoxResult.OK)
|
{
|
PassAmount = 0;
|
FailAmount = 0;
|
DisplayCount(PassAmount, FailAmount);
|
UpdateChart(PassAmount, FailAmount);
|
//this.OpcHandle.DoFctWriteSingle("Count_Clear", "1", out string ErrMsg);
|
foreach (SingleControl socket in tsSockets)
|
{
|
socket.ResetCounter();
|
}
|
}
|
}
|
|
public override void OnClose(object sender, CancelEventArgs e)
|
{
|
StopSequence();
|
this.engine?.ReleaseSequenceFileEx(this.testSeq, 0);
|
this.engine?.ReleaseSequenceFileEx(this.modeSeq, 0);
|
this.engine?.UnloadAllModules();
|
this.testSeq = null;
|
this.modeSeq = null;
|
base.OnClose(sender, e);
|
}
|
|
private void PluginControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
{
|
|
}
|
}
|
}
|