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)
{
}
}
}