using HMI.Base;
|
using HMI.Core;
|
using HMI.Core.Api;
|
using HMI.Core.Entity;
|
using HMI.Core.Manager;
|
using HMI.Core.Setting;
|
using HMI.Core.Util;
|
using HMI.MQUtil;
|
using HMI.UI.Util;
|
using HMI.Util;
|
using log4net;
|
using NationalInstruments.TestStand.Interop.API;
|
using NationalInstruments.TestStand.Interop.UI.Ax;
|
using Plugin.TsSupport;
|
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.Runtime.InteropServices;
|
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 : PluginMaskControl
|
{
|
#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 SequenceContext sequenceContext = null;
|
|
private Chart chart;
|
#endregion
|
|
public static ILog logger { get; private set; }
|
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 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;
|
|
/// <summary>
|
/// Socket数量
|
/// </summary>
|
private int socketNum = 1;
|
|
private ProductData selectedProduct;
|
|
private MSMQHelper mSMQHelper = new MSMQHelper("HMI_Alarm");
|
|
public ProductData SelectedProduct
|
{
|
get { return selectedProduct; }
|
set
|
{
|
Set(ref selectedProduct, value);
|
CurrentProductModel = value?.Name;
|
if (tsSockets != null)
|
{
|
foreach (SingleControl socket in tsSockets)
|
{
|
socket.RefreshProduct(value);
|
}
|
}
|
SetTestSeqFile(value);
|
}
|
}
|
|
private string currentProductModel;
|
public string CurrentProductModel
|
{
|
get => currentProductModel;
|
set
|
{
|
Set(ref currentProductModel, value);
|
}
|
}
|
|
public override void SetLogger(ILog logger)
|
{
|
base.SetLogger(logger);
|
TsExecution.logger = logger;
|
}
|
|
private ObservableCollection<ProductData> products;
|
|
public ObservableCollection<ProductData> Products
|
{
|
get { return products; }
|
set
|
{
|
Set(ref products, value);
|
}
|
}
|
|
public List<double> FontSizeSource { get; set; } = new List<double> {
|
10,11,12,13,14,15,16,17,18,19,20
|
};
|
|
private Pie pie;
|
private string CountIniPath;
|
private bool IsOptRequirePassword;
|
|
private bool alarmVisiable;
|
public bool AlarmVisiable
|
{
|
get => alarmVisiable;
|
set
|
{
|
Set(ref alarmVisiable, value);
|
}
|
}
|
|
private bool _enableMes;
|
public bool EnableMes
|
{
|
get => _enableMes;
|
set
|
{
|
if (RequirePassword() == true)
|
{
|
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 (RequirePassword() == true)
|
{
|
if (WriteConfig("EnableDebugMode", value))
|
{
|
Set(ref _enableDebugMode, value);
|
}
|
}
|
}
|
}
|
|
private bool _enableNGPause;
|
public bool EnableNGPause
|
{
|
get => _enableNGPause;
|
set
|
{
|
if (RequirePassword() == true)
|
{
|
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;
|
}
|
}
|
}
|
|
private bool autoExecuteSeq;
|
public bool AutoExecuteSeq
|
{
|
get => autoExecuteSeq;
|
set
|
{
|
Set(ref autoExecuteSeq, value);
|
}
|
}
|
|
private string currentWorkorder;
|
public string CurrentWorkorder
|
{
|
get => currentWorkorder;
|
set
|
{
|
if (WriteIni("UI_CurrentWorkorder", value))
|
{
|
Set(ref currentWorkorder, value);
|
engine?.Globals.SetValString("StationState.WorkOrder", 1, value);
|
engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
|
}
|
}
|
}
|
|
private bool _DisableScanner;
|
public bool DisableScanner
|
{
|
get => _DisableScanner;
|
set
|
{
|
if (WriteConfig("DisableScanner", value))
|
{
|
Set(ref _DisableScanner, value);
|
}
|
}
|
}
|
|
private string tsModelPath;
|
private string dataLogPath;
|
private SqliteHelper sqliteHelper;
|
|
private MQConnector mqConnector;
|
private int probeWarn;
|
|
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);
|
}
|
|
private void CheckSqlHelper()
|
{
|
string today = DateTime.Now.ToString("yyyyMMdd");
|
string dbPath = Path.Combine(dataLogPath, $"Data_{today}.db");
|
if (!File.Exists(dbPath))
|
{
|
SqliteHelper.CreateDatabase(dbPath);
|
}
|
if (sqliteHelper == null || sqliteHelper.DataBaseFile != dbPath)
|
{
|
sqliteHelper?.Dispose();
|
sqliteHelper = new SqliteHelper(dbPath);
|
sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS serialData (
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
sn TEXT NOT NULL,
|
model TEXT,
|
station TEXT NOT NULL,
|
step TEXT NOT NULL,
|
position TEXT,
|
limitDown TEXT,
|
limitUp TEXT,
|
unit TEXT,
|
offset TEXT,
|
value TEXT,
|
status TEXT,
|
finalStatus TEXT,
|
consumeTime TEXT,
|
totalConsumeTime TEXT,
|
time DATETIME NOT NULL);");
|
sqliteHelper.ExecuteSql(@"CREATE TABLE IF NOT EXISTS testResult (
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
sn TEXT NOT NULL,
|
model TEXT,
|
station TEXT NOT NULL,
|
position TEXT,
|
status TEXT,
|
result TEXT NOT NULL,
|
time DATETIME NOT NULL);");
|
}
|
}
|
|
public override void InitFinish()
|
{
|
this.ReadCount(out int pass, out int fail);
|
this.DisplayCount(pass, fail);
|
ReadConfig();
|
InitProducts();
|
CheckSqlHelper();
|
base.InitFinish();
|
|
if (ParamSetting.EnableAutoModel)
|
{
|
selectModelPanel.Visibility = Visibility.Collapsed;
|
autoModelPanel.Visibility = Visibility.Visible;
|
CurrentProductModel = string.Empty;
|
}
|
|
try
|
{
|
UserInfo user = UserInfo.User;
|
CurrentUserCode = user?.Code;
|
engine?.Globals.SetValString("StationState.OPID", 1, CurrentUserCode);
|
engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
|
}
|
catch (Exception ex)
|
{
|
log.Warn("Get Login User Failed.", ex);
|
}
|
}
|
|
private void InitMQ()
|
{
|
mqConnector = new MQConnector("UI");
|
mqConnector.BeginReceive(HandleMQUI);
|
}
|
|
void HandleMQUI(string msg)
|
{
|
string[] arr = msg.Split('|');
|
int eventCode = int.Parse(arr[0]);
|
int index = int.Parse(arr[1]);
|
string strData = arr[2];
|
Dispatcher.BeginInvoke(new Action(() =>
|
{
|
HandleUIMessage(eventCode, index, strData);
|
}));
|
}
|
|
/// <summary>
|
/// 初始化Winform图表
|
/// </summary>
|
private void InitChart()
|
{
|
pieChart.Plot.Axes.Frameless();
|
pieChart.Plot.HideGrid();
|
pieChart.Plot.DataBackground.Color = ScottPlot.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<PieSlice> slices = new List<PieSlice> {
|
new PieSlice() { Value = 5, FillColor = ScottPlot.Colors.Green,
|
LabelFontSize = (float)WidthSizeConverter.CountFontSize(20,300,300),
|
Label = "Pass",LegendText="Pass" },
|
new PieSlice() { Value = 2, FillColor = ScottPlot.Colors.Red,
|
LabelFontSize = (float)WidthSizeConverter.CountFontSize(20,300,300),
|
Label = "Fail",LegendText="Fail" } };
|
|
|
pie = pieChart.Plot.Add.Pie(slices);
|
}
|
|
private bool? RequirePassword()
|
{
|
if (!IsOptRequirePassword) return true;
|
var win = new RequirePasswordWindow
|
{
|
Owner = Window.GetWindow(this)
|
};
|
return win.ShowDialog();
|
}
|
|
private void InitProducts()
|
{
|
Products = ProductManager.GetProducts<ObservableCollection<ProductData>>();
|
if (Products != null && Products.Count > 0)
|
{
|
string lastModel = engine?.Globals.GetValString("StationState.ProductModel", 1);
|
var tempProduct = Products[0];
|
if (!string.IsNullOrEmpty(lastModel))
|
{
|
var product = Products.FirstOrDefault(p => p.Name == lastModel);
|
if (product != null)
|
{
|
tempProduct = product;
|
}
|
}
|
SelectedProduct = tempProduct;
|
}
|
RefershUI();
|
if (tsSockets != null)
|
{
|
foreach (SingleControl socket in tsSockets)
|
{
|
socket.RefreshProduct(SelectedProduct);
|
}
|
}
|
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;
|
});
|
}
|
}
|
|
/// <summary>
|
/// 初始化TestStand引擎
|
/// </summary>
|
private void InitTsEngine()
|
{
|
AxApplicationMgr = TestAppManager.GetAxApplicationMgr();
|
|
|
//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();
|
}
|
|
private volatile bool canSwitchModel = false;
|
private volatile Action switchAction = null;
|
|
private void AxApplicationMgr_EndExecution(object sender, _ApplicationMgrEvents_EndExecutionEvent e)
|
{
|
log.Info("AxApplicationMgr_EndExecution");
|
if (ReleaseSequenceFile())
|
{
|
IsTesting = false;
|
log.Info("Release SeqFile Success!");
|
if (canSwitchModel) switchAction?.Invoke();
|
}
|
else
|
{
|
log.Warn("Release SeqFile Fail!");
|
}
|
}
|
|
/// <summary>
|
/// 进程结束事件
|
/// </summary>
|
/// <param name="sender"></param>
|
/// <param name="e"></param>
|
private void AxApplicationMgr_ExecutionClosed(object sender, _ApplicationMgrEvents_ExecutionClosedEvent e)
|
{
|
//ReleaseSequenceFile();
|
}
|
|
private void PluginControl_Loaded(object sender, RoutedEventArgs e)
|
{
|
ShowMask(() =>
|
{
|
try
|
{
|
//Dispatcher.Invoke(() =>
|
//{
|
|
//});
|
isLoadOK = true;
|
if (AutoExecuteSeq) 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;
|
});
|
}, (ex) =>
|
{
|
ToastHelper.Error("Loaded Failed.");
|
log.Error("Loaded Error.", ex);
|
}, null, LangUtil.Get("App.UsercontrolIsLoading"));
|
|
//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);
|
// }
|
// });
|
// isLoadOK = true;
|
// if (AutoExecuteSeq) 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;
|
// });
|
// }
|
// ).Info(LangUtil.Get("App.UsercontrolIsLoading"));
|
}
|
|
private void RefershUI()
|
{
|
panel.Children.Clear();
|
string[] names = ParamSetting.SocketNames.Split(',');
|
|
//重新生成子面板
|
for (int i = 0; i < this.socketNum; i++)
|
{
|
string name = string.Empty;
|
if (i < names.Length)
|
{
|
name = names[i];
|
}
|
|
SingleControl control = new SingleControl(i, CountIniPath, this.probeWarn)
|
{
|
SocketName = name
|
};
|
panel.Children.Add(control);
|
tsSockets.Add(control);
|
|
//createChildInNewThread(i, CountIniPath, ProbeWarn);
|
}
|
if (tsSockets.Count == 1)
|
{
|
panel.Columns = 1;
|
}
|
else if (tsSockets.Count > 0 && (tsSockets.Count % 3) == 0)
|
{
|
panel.Columns = 3;
|
}
|
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", "0", CountIniPath));
|
fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "0", CountIniPath));
|
var fontSize = Convert.ToDouble(IniFileHelper.ReadIniData("Config", "TableFontSize", "12", CountIniPath));
|
this.Resources["tableFontSize"] = 20d;
|
//CbFontSize.SelectedItem = fontSize;
|
}
|
|
private string csvDir;
|
|
/// <summary>
|
/// 读取配置文件里的几个属性
|
/// </summary>
|
/// <param name="pass"></param>
|
/// <param name="fail"></param>
|
private void ReadConfig()
|
{
|
ParameterData value;
|
|
|
bool flag = false;
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("DefaultEnableMes");
|
if (value != null)
|
{
|
flag = bool.Parse(value.Value);
|
}
|
}
|
catch
|
{
|
flag = false;
|
}
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("SocketNum");
|
if (value != null)
|
{
|
this.socketNum = int.Parse(value.Value);
|
}
|
}
|
catch
|
{
|
this.socketNum = 2;
|
}
|
|
if (flag)
|
{
|
EnableMes = true;
|
}
|
else
|
{
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("EnableMes");
|
if (value == null)
|
{
|
CbEnableMes.Visibility = Visibility.Collapsed;
|
}
|
else
|
{
|
_enableMes = bool.Parse(value.Value);
|
Set("EnableMes");
|
}
|
}
|
catch
|
{
|
_enableMes = false;
|
Set("EnableMes");
|
}
|
}
|
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("EnableNGPause");
|
if (value == null)
|
{
|
CbEnableNGPause.Visibility = Visibility.Collapsed;
|
}
|
else
|
{
|
_enableNGPause = bool.Parse(value.Value);
|
Set("EnableNGPause");
|
}
|
}
|
catch
|
{
|
_enableNGPause = false;
|
Set("EnableNGPause");
|
}
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("EnableDebugMode");
|
if (value == null)
|
{
|
CbEnableDebugMode.Visibility = Visibility.Collapsed;
|
}
|
else
|
{
|
_enableDebugMode = bool.Parse(value.Value);
|
Set("EnableDebugMode");
|
}
|
}
|
catch
|
{
|
_enableDebugMode = false;
|
Set("EnableDebugMode");
|
}
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("DisableScanner");
|
if (value == null)
|
{
|
CbDisableScanner.Visibility = Visibility.Collapsed;
|
}
|
else
|
{
|
_DisableScanner = bool.Parse(value.Value);
|
Set("DisableScanner");
|
}
|
}
|
catch
|
{
|
_DisableScanner = false;
|
Set("DisableScanner");
|
}
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("EnableCanLog");
|
_enableCanLog = bool.Parse(value.Value);
|
Set("EnableCanLog");
|
}
|
catch
|
{
|
_enableCanLog = false;
|
Set("EnableCanLog");
|
}
|
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("EnableSimpleMode");
|
if (value == null)
|
{
|
CbEnableSimpleMode.Visibility = Visibility.Collapsed;
|
}
|
else
|
{
|
EnableSimpleMode = bool.Parse(value.Value);
|
}
|
}
|
catch
|
{
|
EnableSimpleMode = false;
|
}
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("UI_ShowInfoDock");
|
if (value == null)
|
{
|
CbUI_ShowInfoDock.Visibility = Visibility.Collapsed;
|
}
|
else
|
{
|
UI_ShowInfoDock = bool.Parse(value.Value);
|
}
|
}
|
catch
|
{
|
UI_ShowInfoDock = false;
|
}
|
|
try
|
{
|
value = ParameterManager.GetCommonParameterByName("AutoExecuteSeq");
|
AutoExecuteSeq = bool.Parse(value.Value);
|
}
|
catch
|
{
|
AutoExecuteSeq = true;
|
}
|
|
try
|
{
|
|
value = ParameterManager.GetCommonParameterByName("TestReportFolder");
|
csvDir = value.Value;
|
if (!Directory.Exists(csvDir))
|
{
|
Directory.CreateDirectory(csvDir);
|
}
|
}
|
catch
|
{
|
csvDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestReport");
|
}
|
|
try
|
{
|
|
value = ParameterManager.GetCommonParameterByName("IsOptRequirePassword");
|
IsOptRequirePassword = bool.Parse(value.Value);
|
}
|
catch
|
{
|
IsOptRequirePassword = false;
|
}
|
|
try
|
{
|
|
value = ParameterManager.GetCommonParameterByName("UI_CurrentWorkorder");
|
CurrentWorkorder = value.Value;
|
}
|
catch
|
{
|
CurrentWorkorder = string.Empty;
|
}
|
|
try
|
{
|
|
value = ParameterManager.GetCommonParameterByName("TS_ShowTitleWorkorder");
|
var boo = bool.Parse(value.Value);
|
if (boo)
|
{
|
workorderPanel.Visibility = Visibility.Visible;
|
}
|
}
|
catch
|
{
|
}
|
|
dataLogPath = ParamSetting.DataLogFolder;
|
|
try
|
{
|
|
value = ParameterManager.GetCommonParameterByName("TsModelPath");
|
tsModelPath = value?.Value ?? @"C:\Users\Public\Documents\National Instruments\TestStand 2020 (64-bit)\Components\Models\ParallelModel.seq";
|
}
|
catch
|
{
|
//@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq"
|
tsModelPath = @"C:\Users\Public\Documents\National Instruments\TestStand 2020 (64-bit)\Components\Models\ParallelModel.seq";
|
}
|
|
try
|
{
|
|
value = ParameterManager.GetCommonParameterByName("ProbeWarn");
|
probeWarn = int.Parse(value?.Value);
|
}
|
catch
|
{
|
probeWarn = 0;
|
}
|
//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 bool WriteConfig(string name, bool value)
|
{
|
try
|
{
|
if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg))
|
{
|
foreach (var ts in tsSockets)
|
{
|
ts?.SequenceFileContext?.StationGlobals.SetValBoolean($"SystemSetting.{name}", 1, value);
|
if (ts?.SequenceFileContext?.Sequence != null)
|
{
|
ts.SequenceFileContext.Sequence.FailureAction =
|
value ? FailureActions.FailureAction_None : FailureActions.FailureAction_GotoCleanup;
|
}
|
}
|
return true;
|
}
|
else
|
{
|
throw new Exception(errMsg);
|
}
|
}
|
catch (Exception ex)
|
{
|
MsgBoxHelper2.Error(ex.Message);
|
return false;
|
}
|
}
|
|
private bool WriteIni(string name, object value)
|
{
|
//IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
|
try
|
{
|
if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg))
|
{
|
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);
|
}
|
|
/// <summary>
|
/// 刷新计数
|
/// </summary>
|
/// <param name="pass">合格数</param>
|
/// <param name="fail">不合格数</param>
|
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);
|
}
|
|
/// <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()
|
{
|
log.Info($"Start ATE Seq isLoadOK:{isLoadOK}");
|
if (!isLoadOK) return;
|
try
|
{
|
engine?.Globals.SetValBoolean("StationState.StopTest", 1, false);
|
}
|
catch (Exception ex)
|
{
|
log.Error("Reset StopTest Failed.", ex);
|
}
|
if (ParamSetting.EnableAutoModel)
|
{
|
try
|
{
|
var path = ParameterManager.GetCommonParameterByName<string>("CommonMainSeq", "");
|
if (string.IsNullOrEmpty(path))
|
{
|
Dispatcher.Invoke(() =>
|
{
|
MsgBoxHelper.Error(this, $"请先设置CommonMainSeq参数");
|
});
|
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);
|
IsTesting = true;
|
}
|
catch (Exception ex)
|
{
|
Dispatcher.Invoke(() =>
|
{
|
log.Error("Start Sequence Failed.", ex);
|
MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.StartSequenceFail")}: {ex.Message}");
|
});
|
}
|
}
|
else
|
{
|
//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);
|
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()
|
{
|
canSwitchModel = false;
|
switchAction = null;
|
ShowMask(() =>
|
{
|
bool stoped = StopSequence();
|
}, null, null, LangUtil.Get("App.SequenceIsStopping"));
|
}
|
|
/// <summary>
|
/// 停止测试序列
|
/// </summary>
|
private bool StopSequence()
|
{
|
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);
|
// }
|
//}
|
engine.Globals.SetValBoolean("StationState.StopTest", 1, true);
|
if (this.execution == null) return 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;
|
return res;
|
}
|
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()
|
{
|
canSwitchModel = true;
|
ShowMask(() =>
|
{
|
StartSequence();
|
}, null, null, LangUtil.Get("Ts.SequenceIsStarting"));
|
//new MaskWindow(Window.GetWindow(this),
|
// (win) =>
|
// {
|
// try
|
// {
|
// StartSequence();
|
// }
|
// catch (Exception ex) {
|
|
// }
|
// finally
|
// {
|
// win.Dispatcher.Invoke(() =>
|
// {
|
// 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();
|
}
|
|
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 strData)
|
{
|
if (strData.Contains(";"))
|
{
|
var strs = strData.Split(';');
|
this.tsSockets[index].Barcode = strs[0];
|
this.tsSockets[index].Model = strs[1];
|
}
|
else
|
{
|
this.tsSockets[index].Barcode = strData;
|
}
|
}
|
|
public void SavePositionCSV(int index, string csvPath)
|
{
|
//var time = DateTime.Now;
|
//string status = this.tsSockets[index].Status == "Passed" ? "PASS" : "FAIL";
|
//string csvPath = Path.Combine(csvDir, SelectedProduct?.Name ?? "未知产品", time.ToString("yyyyMMdd"), status,
|
// $"{this.tsSockets[index].Barcode}-{time.ToString("HHmmss")}.csv");
|
this.tsSockets[index].WriteToCSV(csvPath);
|
}
|
|
public void SaveTemplateCSV(int index)
|
{
|
var time = DateTime.Now;
|
string status = this.tsSockets[index].Status == "Passed" ? "PASS" : "FAIL";
|
string csvPath = Path.Combine(csvDir, CurrentProductModel ?? "未知产品", time.ToString("yyyyMMdd"), status, $"{this.tsSockets[index].Barcode}-{time.ToString("HHmmss")}.csv");
|
this.tsSockets[index].WriteToCSV(csvPath);
|
}
|
|
public void SavePositionDB(int index, string status)
|
{
|
CheckSqlHelper();
|
var socket = this.tsSockets[index];
|
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
string snr = socket.Barcode;
|
string model = CurrentProductModel;
|
|
List<string> sqls = new List<string>();
|
foreach (var data in socket.Datasource)
|
{
|
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);
|
}
|
|
private void HandleUIMessage(int eventCode, int index, string strData)
|
{
|
switch (eventCode)
|
{
|
case 10002://更新测试状态
|
string status = strData;
|
UpdatePositionStatus(index, status);
|
break;
|
|
case 10003://发送测试数据
|
AddTestData(index, strData);
|
break;
|
|
case 10004://发送测试过程信息
|
this.tsSockets[index].CurrentStep = strData;
|
break;
|
|
case 10005://初始化测试数据,清空测试数据
|
this.tsSockets[index].IniTestData();
|
break;
|
|
case 10006://发送产品条码
|
SetPositionBarcode(index, strData);
|
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 = strData;
|
break;
|
case (int)UIMessageCodes.UIMsg_EndExecution://执行结束
|
//IsTesting = false;
|
//this.ReleaseSequenceFile();
|
break;
|
case 10030://显示测试进度
|
int ProcessVal = Convert.ToInt32(strData);
|
this.tsSockets[index].SetTestProcess(ProcessVal);
|
break;
|
case 10031://导出CSV
|
if (string.IsNullOrEmpty(strData))
|
{
|
SaveTemplateCSV(index);
|
return;
|
}
|
try
|
{
|
string[] datas = strData.Split(';');
|
SavePositionCSV(index, datas[0]);
|
if (datas.Length >= 2)
|
{
|
SavePositionDB(index, datas[1]);
|
}
|
else
|
{
|
var sss = this.tsSockets[index].Status;
|
SavePositionDB(index, sss);
|
}
|
}
|
catch (Exception ex)
|
{
|
log.Warn("10031 Error.", ex);
|
SaveTemplateCSV(index);
|
}
|
break;
|
case 10032://按下标暂停测试
|
this.tsSockets[index].ShowPausePanel();
|
break;
|
case 10033:
|
var alarm = new AlarmText
|
{
|
VirtualId = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
|
AlarmInfo = strData
|
};
|
ShowAlarm(alarm);
|
break;
|
case 10034:
|
CurrentProductModel = strData;
|
break;
|
case 10035:
|
string[] arr_10035 = strData.Split(';');
|
|
int[][] points = new int[arr_10035.Length - 1][];
|
|
for (int i = 1; i < arr_10035.Length; i++)
|
{
|
string[] strs = arr_10035[i].Split(',');
|
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 10036:
|
this.tsSockets[index].ClearScreenPoint(int.Parse(strData));
|
break;
|
case 10099:
|
string[] arr = strData.Split(',');
|
if (canSwitchModel) SwitchSequenceByModel(index, arr[0], arr[1]);
|
break;
|
default:
|
break;
|
}
|
}
|
|
private void SwitchSequenceByModel(int index, string model, string snr)
|
{
|
Dispatcher.Invoke(() =>
|
{
|
var target = Products.FirstOrDefault(e => e.Name == model);
|
|
ShowMask(() =>
|
{
|
bool stoped = StopSequence();
|
//if (stoped)
|
//{
|
// IsTesting = false;
|
//}
|
}, null, null, "换型中,请稍后...");
|
|
if (target == null)
|
{
|
MsgBoxHelper2.Warn($"请先在产品管理页面配置型号 [{model}] 的产品信息!");
|
return;
|
}
|
|
switchAction = () =>
|
{
|
switchAction = null;
|
log?.Info($"尝试自动换型: {switchAction}");
|
if (canSwitchModel)
|
{
|
Dispatcher.Invoke(() =>
|
{
|
try
|
{
|
log?.Info("开始自动换型");
|
SelectedProduct = target;
|
engine?.Globals.SetValString($"CacheSNR[{index}]", 1, snr);
|
engine?.CommitGlobalsToDisk(false);
|
StartClick();
|
}
|
catch (Exception ex)
|
{
|
log?.Error("自动换型失败", ex);
|
}
|
});
|
}
|
};
|
});
|
}
|
|
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);
|
}
|
}
|
|
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<ParameterData> parameters = ProductManager.GetParametersByProductName(name);
|
testSeqFile = parameters.FirstOrDefault(p => p.Name == "TestSequence")?.Value?.ToString();
|
if (AutoExecuteSeq)
|
{
|
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();
|
base.OnClose(sender, e);
|
}
|
catch (Exception ex)
|
{
|
log.Error("OnClose Error.", ex);
|
}
|
}
|
|
public override void DoClose(object sender, EventArgs e)
|
{
|
base.DoClose(sender, e);
|
}
|
|
/// <summary>
|
/// 释放所有的序列文件
|
/// </summary>
|
private bool ReleaseSequenceFile()
|
{
|
try
|
{
|
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;
|
}
|
catch (Exception ex)
|
{
|
log.Warn($"ReleaseSequenceFile In {count} Times Fail.", ex);
|
count--;
|
System.Threading.Thread.Sleep(1000);
|
}
|
}
|
log.Warn($"ReleaseSequenceFile Failed.");
|
return false;
|
}
|
}
|
catch (Exception ex)
|
{
|
log.Warn("ReleaseSequenceFile Error.", ex);
|
return false;
|
}
|
}
|
|
private void PluginControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
{
|
|
}
|
|
private void CbFontSize_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
{
|
try
|
{
|
if (e.AddedItems != null && e.AddedItems.Count > 0)
|
{
|
var fontSize = (double)e.AddedItems[0];
|
this.Resources["tableFontSize"] = fontSize;
|
IniFileHelper.WriteIniData("Config", "TableFontSize", fontSize.ToString(), CountIniPath);
|
}
|
}
|
catch
|
{
|
}
|
}
|
|
private void Label_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
{
|
//workOrderEditor.Focus();
|
//workOrderEditor.IsEditing = true;
|
if (RequirePassword() == true)
|
{
|
var win = new WorkOrderWin
|
{
|
WorkOrder = CurrentWorkorder,
|
Owner = Window.GetWindow(this)
|
};
|
if (win.ShowDialog() == true)
|
{
|
CurrentWorkorder = win.WorkOrder;
|
}
|
}
|
}
|
}
|
}
|