614 lines
22 KiB
C#
614 lines
22 KiB
C#
using BDU.Logic;
|
||
using BDU.Models;
|
||
using BDU.Tools;
|
||
using BDU.Windows;
|
||
using ATS_DBContext;
|
||
using MaterialDesignThemes.Wpf;
|
||
using Microsoft.Win32;
|
||
using Newtonsoft.Json;
|
||
using PropertyChanged;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Data;
|
||
using System.Windows.Documents;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Imaging;
|
||
using System.Windows.Navigation;
|
||
using System.Windows.Shapes;
|
||
using TSMasterCAN;
|
||
using static BDU.Models.ParameterModel;
|
||
using Path = System.IO.Path;
|
||
|
||
namespace BDU.Views
|
||
{
|
||
/// <summary>
|
||
/// ToolBar.xaml 的交互逻辑
|
||
/// </summary>
|
||
[AddINotifyPropertyChangedInterface]
|
||
public partial class ToolBar : UserControl
|
||
{
|
||
public static ToolBar Instance { get; private set; }
|
||
public string CurSubProgramPath { get; set; }
|
||
public bool SingleStep = false;
|
||
public string RunState { get; set; } = "运行";
|
||
|
||
public PackIconKind RunIcon { get; set; } = PackIconKind.Play;
|
||
|
||
public bool? IsStop { get; set; } = null;
|
||
|
||
|
||
public bool IsTerminate { get; set; } = false;
|
||
|
||
public bool? CanReport => IsStop == false; // 是否可以导出报告
|
||
public bool IsAdmin => MainWindow.Instance.User.Role > 0;
|
||
|
||
public bool IsDebug => MainWindow.Instance.User.UserName == "开发者" && MainWindow.Instance.User.UserAccount == "Developer";
|
||
|
||
private Task? currentExecutionTask;
|
||
|
||
public ToolBar()
|
||
{
|
||
Instance = this;
|
||
InitializeComponent();
|
||
DataContext = this;
|
||
|
||
// 监听主窗口属性变化,特别是 Title 变化
|
||
if (MainWindow.Instance != null)
|
||
{
|
||
MainWindow.Instance.PropertyChanged += (s, e) =>
|
||
{
|
||
if (e.PropertyName == nameof(MainWindow.IsInSubProgramMode) ||
|
||
e.PropertyName == "Title") // 监听 Title 变化
|
||
{
|
||
OnPropertyChanged(nameof(IsInSubProgramMode));
|
||
OnPropertyChanged(nameof(CurrentPathDisplay));
|
||
}
|
||
};
|
||
}
|
||
|
||
this.Loaded += (s, e) => LoadDefaultProgramIfExists();
|
||
}
|
||
private void LoadDefaultProgramIfExists()
|
||
{
|
||
try
|
||
{
|
||
if (File.Exists(SystemConfig.Instance.DefaultSubProgramFilePath))
|
||
{
|
||
string json = File.ReadAllText(SystemConfig.Instance.DefaultSubProgramFilePath);
|
||
var tmp = JsonConvert.DeserializeObject<ProgramModel>(json);
|
||
if (tmp != null)
|
||
{
|
||
MainWindow.Instance.Program = tmp;
|
||
InitParameter(tmp);
|
||
InitDevice(tmp);
|
||
}
|
||
else
|
||
{
|
||
MainWindow.Instance.Program = new();
|
||
}
|
||
MainWindow.Instance.CurrentFilePath = SystemConfig.Instance.DefaultSubProgramFilePath;
|
||
Log.Success($"已打开文件: {SystemConfig.Instance.DefaultSubProgramFilePath}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"默认程序:{SystemConfig.Instance.DefaultSubProgramFilePath} 文件打开失败:{ex.Message}");
|
||
}
|
||
}
|
||
#region 辅助方法
|
||
|
||
private void InitDevice(ProgramModel program)
|
||
{
|
||
if (program.Devices != null && program.Devices.Count > 0)
|
||
{
|
||
foreach (DeviceModel device in program.Devices)
|
||
{
|
||
_ = DeviceConnect.InitAndConnectDevice(program, device);
|
||
}
|
||
}
|
||
foreach (var step in program.StepCollection)
|
||
{
|
||
if (step.SubProgram != null)
|
||
{
|
||
InitDevice(step.SubProgram);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void InitParameter(ProgramModel program)
|
||
{
|
||
if (program.Parameters != null && program.Parameters.Count > 0)
|
||
{
|
||
foreach (var parameter in program.Parameters)
|
||
{
|
||
if (parameter.Type!.BaseType == typeof(Enum))
|
||
{
|
||
parameter.Value = Enum.Parse(parameter.Type, parameter.Value!.ToString()!);
|
||
}
|
||
}
|
||
}
|
||
foreach (var step in program.StepCollection)
|
||
{
|
||
if (step.SubProgram != null)
|
||
{
|
||
InitDevice(step.SubProgram);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SaveProgramToFile(string filePath)
|
||
{
|
||
try
|
||
{
|
||
var tmp = ClearDeviceParameterValue(MainWindow.Instance.Program);
|
||
string json = JsonConvert.SerializeObject(tmp, Formatting.Indented);
|
||
File.WriteAllText(filePath, json);
|
||
Log.Success($"程序已保存: {filePath}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"保存文件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private ProgramModel ClearUnnecessaryParameterValue(ProgramModel program)
|
||
{
|
||
var tmp = new ProgramModel(program);
|
||
foreach (var step in tmp.StepCollection)
|
||
{
|
||
if (step.Method != null)
|
||
{
|
||
foreach (var para in step.Method.Parameters)
|
||
{
|
||
if (para.VariableID != null && para.Category == ParameterCategory.Output)
|
||
{
|
||
para.Value = null;
|
||
tmp.Parameters.First(x => x.ID == para.VariableID)!.Value = null;
|
||
}
|
||
}
|
||
}
|
||
else if (step.SubProgram != null)
|
||
{
|
||
var subTmp = ClearUnnecessaryParameterValue(step.SubProgram);
|
||
step.SubProgram = subTmp;
|
||
}
|
||
}
|
||
return tmp;
|
||
}
|
||
|
||
private ProgramModel ClearDeviceParameterValue(ProgramModel program)
|
||
{
|
||
var tmp = new ProgramModel(program);
|
||
foreach (var device in tmp.Devices)
|
||
{
|
||
tmp.Parameters.Remove(tmp.Parameters.First(x => x.ID == device.ParameterID));
|
||
}
|
||
foreach (var step in tmp.StepCollection)
|
||
{
|
||
if (step.SubProgram != null)
|
||
{
|
||
step.SubProgram = ClearDeviceParameterValue(step.SubProgram);
|
||
}
|
||
}
|
||
return tmp;
|
||
}
|
||
|
||
#endregion
|
||
|
||
private void File_New_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
MainWindow.Instance.Program = new();
|
||
MainWindow.Instance.CurrentFilePath = "";
|
||
}
|
||
private void Set_DefaultProgram_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SystemConfig.Instance.DefaultSubProgramFilePath = CurSubProgramPath;
|
||
SystemConfig.Instance.SaveToFile();
|
||
}
|
||
private void File_Open_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var openFileDialog = new OpenFileDialog
|
||
{
|
||
Filter = "ATS程序文件|*.ats|所有文件|*.*",
|
||
Title = "打开程序文件"
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() == true)
|
||
{
|
||
try
|
||
{
|
||
CurSubProgramPath = openFileDialog.FileName;
|
||
string json = File.ReadAllText(openFileDialog.FileName);
|
||
var tmp = JsonConvert.DeserializeObject<ProgramModel>(json);
|
||
if (tmp != null)
|
||
{
|
||
MainWindow.Instance.Program = tmp;
|
||
MainWindow.Instance.Title = Path.GetFileName(openFileDialog.FileName).Split(".ats")[0];
|
||
InitParameter(tmp);
|
||
InitDevice(tmp);
|
||
}
|
||
else
|
||
{
|
||
MainWindow.Instance.Program = new();
|
||
}
|
||
MainWindow.Instance.SelectedStep = null;
|
||
MainWindow.Instance.CurrentFilePath = openFileDialog.FileName;
|
||
Log.Success($"已打开文件: {openFileDialog.FileName}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"程序文件打开失败:{ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void File_Save_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (string.IsNullOrEmpty(MainWindow.Instance.CurrentFilePath))
|
||
{
|
||
File_SaveAsOther_Click(sender, e);
|
||
}
|
||
else
|
||
{
|
||
SaveProgramToFile(MainWindow.Instance.CurrentFilePath);
|
||
}
|
||
}
|
||
|
||
private void File_SaveAsOther_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var saveFileDialog = new SaveFileDialog
|
||
{
|
||
Filter = "ATS程序文件|*.ats|所有文件|*.*",
|
||
Title = "另存为",
|
||
FileName = "NewProgram.ats"
|
||
};
|
||
|
||
if (saveFileDialog.ShowDialog() == true)
|
||
{
|
||
MainWindow.Instance.CurrentFilePath = saveFileDialog.FileName;
|
||
SaveProgramToFile(MainWindow.Instance.CurrentFilePath);
|
||
}
|
||
}
|
||
|
||
private async void ProgramRun_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (RunState == "运行")
|
||
{
|
||
SingleStep = false;
|
||
Log.Info($"用户 [ {MainWindow.Instance.User.UserName} ] 点击 [ 运行 ] ");
|
||
// 清空报告列表
|
||
ReportModelList.ReportList = new();
|
||
RunState = "暂停";
|
||
RunIcon = PackIconKind.Pause;
|
||
if (IsStop == null)
|
||
{
|
||
IsStop = false;
|
||
currentExecutionTask = StepRunning.ExecuteSteps(MainWindow.Instance.Program, cancellationToken: StepRunning.stepCTS.Token);
|
||
await currentExecutionTask;
|
||
RunState = "运行";
|
||
RunIcon = PackIconKind.Play;
|
||
IsStop = null;
|
||
}
|
||
else if (IsStop == true)
|
||
{
|
||
IsStop = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Log.Info($"用户 [ {MainWindow.Instance.User.UserName} ] 点击 [ 暂停 ] ");
|
||
IsStop = true;
|
||
RunState = "运行";
|
||
RunIcon = PackIconKind.Play;
|
||
}
|
||
}
|
||
private async void SingleStepExecution_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (RunState == "运行")
|
||
{
|
||
SingleStep = true;
|
||
Log.Info($"用户 [ {MainWindow.Instance.User.UserName} ] 点击 [ 单步执行 ] ");
|
||
RunState = "暂停";
|
||
RunIcon = PackIconKind.Pause;
|
||
if (IsStop == null)
|
||
{
|
||
IsStop = false;
|
||
currentExecutionTask = StepRunning.ExecuteSteps(MainWindow.Instance.Program, cancellationToken: StepRunning.stepCTS.Token);
|
||
await currentExecutionTask;
|
||
RunState = "运行";
|
||
RunIcon = PackIconKind.Play;
|
||
IsStop = null;
|
||
}
|
||
else if (IsStop == true)
|
||
{
|
||
IsStop = false;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
private void Terminate_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (IsStop == false)
|
||
{
|
||
Log.Info($"用户 [ {MainWindow.Instance.User.UserName} ] 点击 [ 停止 ] ");
|
||
IsTerminate = true;
|
||
IsStop = null;
|
||
RunState = "运行";
|
||
RunIcon = PackIconKind.Play;
|
||
StepRunning.stepCTS.Cancel();
|
||
}
|
||
}
|
||
|
||
private void Reset_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
Log.Info($"用户 [ {MainWindow.Instance.User.UserName} ] 点击 [ 复位 ] ");
|
||
IsTerminate = false;
|
||
currentExecutionTask = null;
|
||
StepRunning.stepCTS = new();
|
||
StepRunning.ResetAllStepStatus(MainWindow.Instance.Program);
|
||
}
|
||
|
||
//导出按钮
|
||
private void Report_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
Log.Info($"用户 [ {MainWindow.Instance.User.UserName} ] 点击 [ 导出报告 ] ");
|
||
if (ReportModelList.ReportList != null && ReportModelList.ReportList.Count > 0)
|
||
{
|
||
//生成csv报告文件
|
||
GenerateCsvReport();
|
||
}
|
||
else
|
||
{
|
||
Log.Warning("当前无报告可导出");
|
||
MessageBox.Show("当前无报告可导出", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
}
|
||
}
|
||
|
||
|
||
private void ChannelMapping_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var re = CAN.ShowChannelMappingWindow(true);
|
||
if (re != 0)
|
||
{
|
||
var msg = CAN.GetErrorDescription(re);
|
||
Log.Error($"同星通道映射界面打开失败:{msg}");
|
||
}
|
||
}
|
||
|
||
private void TestDataInfo_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
new TestDataInfomationWindow().Show();
|
||
}
|
||
|
||
private void SystemConfig_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
new SystemConfigWindow().Show();
|
||
}
|
||
|
||
Task? DebugAutoRunTask;
|
||
CancellationTokenSource DebugAutoRunTaskCT;
|
||
private void Debug_AutoRun_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (MainWindow.Instance.Program.StepCollection.Count == 0) return;
|
||
if (DebugAutoRunTask == null)
|
||
{
|
||
DebugAutoRunTaskCT = new();
|
||
try
|
||
{
|
||
DebugAutoRunTask = Task.Run(async () =>
|
||
{
|
||
while (true)
|
||
{
|
||
DebugAutoRunTaskCT.Token.ThrowIfCancellationRequested();
|
||
await StepRunning.ExecuteSteps(MainWindow.Instance.Program, cancellationToken: DebugAutoRunTaskCT.Token);
|
||
}
|
||
});
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugAutoRunTaskCT.Cancel();
|
||
DebugAutoRunTask = null;
|
||
}
|
||
}
|
||
|
||
private async void Debug_ClearDataBase_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
MessageBoxResult result = MessageBox.Show($"确定执行删除操作?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||
if (result == MessageBoxResult.Yes)
|
||
{
|
||
using ATS_DB db = new();
|
||
db.TestData.RemoveRange(db.TestData);
|
||
await db.SaveChangesAsync();
|
||
}
|
||
}
|
||
|
||
private void CAN_DatabaseConnect_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
OpenFileDialog openFileDialog = new OpenFileDialog
|
||
{
|
||
Title = "请选择 DBC 文件",
|
||
Filter = "DBC 文件 (*.dbc)|*.dbc",
|
||
//InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
|
||
Multiselect = false
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() == true)
|
||
{
|
||
var re = CAN.LoadDBC(openFileDialog.FileName, [0, 1, 2, 3], out var DataBaseID);
|
||
if (re != 0)
|
||
{
|
||
Log.Error("CAN数据库加载失败:" + CAN.GetErrorDescription(re));
|
||
}
|
||
else
|
||
{
|
||
Log.Success("CAN数据库加载成功");
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool isConnect = true;
|
||
private void Connect_DisConnect_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (isConnect)
|
||
{
|
||
var res = CAN.DisConnect();
|
||
if (res == 0)
|
||
{
|
||
Log.Success($"CAN卡断开连接成功,返回值:{res}");
|
||
isConnect = false;
|
||
}
|
||
else
|
||
{
|
||
Log.Error($"CAN卡断开连接失败,返回值:{res}");
|
||
}
|
||
|
||
|
||
}
|
||
else
|
||
{
|
||
var res = CAN.Connect();
|
||
if (res == 0)
|
||
{
|
||
Log.Success($"CAN卡连接成功,返回值:{res}");
|
||
isConnect = true;
|
||
}
|
||
else
|
||
{
|
||
Log.Error($"CAN卡连接失败,返回值:{res}");
|
||
}
|
||
}
|
||
}
|
||
private void GenerateCsvReport()
|
||
{
|
||
try
|
||
{
|
||
// 生成文件名(包含当前时间戳)
|
||
string fileName = $"Report_{DateTime.Now:yyyyMMdd_HHmmss}.csv";
|
||
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Reports", fileName);
|
||
|
||
// 确保 Reports 目录存在
|
||
string directory = Path.GetDirectoryName(filePath);
|
||
if (!Directory.Exists(directory))
|
||
{
|
||
Directory.CreateDirectory(directory);
|
||
}
|
||
|
||
// 写入 CSV 文件
|
||
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
|
||
{
|
||
// 写入 CSV 头部(新增"所属子程序"列)
|
||
writer.WriteLine("步骤序号,步骤名称,所属子程序,备注,执行人,执行时间,是否通过,结果");
|
||
|
||
var allPASS = true;
|
||
// 写入数据行
|
||
foreach (var report in ReportModelList.ReportList)
|
||
{
|
||
string stepIndex = report.stepModel?.Index.ToString() ?? "";
|
||
string stepName = EscapeCsvField(report.stepModel?.Name ?? "");
|
||
string subProgramPath = EscapeCsvField(report.SubProgramPath); // 新增列
|
||
string stepRemark = EscapeCsvField(report.stepModel?.Description ?? "");
|
||
string user = EscapeCsvField(report.User);
|
||
string executeTime = report.ExcuteTime?.ToString("yyyy-MM-dd HH:mm:ss");
|
||
string isPass = report.IsPass.ToString(); // 根据你的 IsPass 枚举定义,可能需要调整
|
||
if (isPass == "FAIL") allPASS = false;
|
||
string result = EscapeCsvField(report.Result ?? "");
|
||
|
||
writer.WriteLine($"{stepIndex},{stepName},{subProgramPath},{stepRemark},{user},{executeTime},{isPass},{result}");
|
||
}
|
||
// 写入报告总结果
|
||
writer.WriteLine("");
|
||
writer.WriteLine($"测试总结果,,,,,,{(allPASS ? "PASS" : "FAIL")},");
|
||
|
||
}
|
||
|
||
Log.Success($"报告导出成功,路径:{filePath}");
|
||
MessageBox.Show($"报告导出成功!\n文件路径:{filePath}", "导出成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
|
||
// 可选:打开文件所在目录
|
||
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\"");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"导出报告失败:{ex.Message}");
|
||
MessageBox.Show($"导出报告失败:{ex.Message}", "导出失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转义 CSV 字段中的特殊字符(逗号、双引号、换行符等)
|
||
/// </summary>
|
||
/// <param name="field">原始字段值</param>
|
||
/// <returns>转义后的字段值</returns>
|
||
private string EscapeCsvField(string field)
|
||
{
|
||
if (string.IsNullOrEmpty(field))
|
||
return "";
|
||
|
||
// 如果字段包含逗号、双引号或换行符,则用双引号包围,并将双引号转义为两个双引号
|
||
if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r"))
|
||
{
|
||
return "\"" + field.Replace("\"", "\"\"") + "\"";
|
||
}
|
||
|
||
return field;
|
||
}
|
||
|
||
private void CAN_CatchConfig_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
new CANCatchSingalView().Show();
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// 添加 IsInSubProgramMode 依赖属性或普通属性
|
||
// 如果 MainWindow.Instance.IsInSubProgramMode 是普通属性,可以用普通属性
|
||
public bool IsInSubProgramMode => MainWindow.Instance?.IsInSubProgramMode ?? false;
|
||
|
||
|
||
|
||
// 修改 CurrentPathDisplay 属性,直接从 MainWindow 获取 Title
|
||
public string CurrentPathDisplay
|
||
{
|
||
get
|
||
{
|
||
// 直接使用 MainWindow 的 Title,它已经在 MainWindow 中被正确管理
|
||
return MainWindow.Instance?.Title ?? "ATS";
|
||
}
|
||
set
|
||
{
|
||
if (MainWindow.Instance != null)
|
||
{
|
||
value = MainWindow.Instance.Title;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private void NavigateBack_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
MainWindow.Instance?.ExitSubProgramMode();
|
||
}
|
||
|
||
private void DeviceManage_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
new DeviceManageWindow().Show();
|
||
}
|
||
}
|
||
}
|