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 { /// /// ToolBar.xaml 的交互逻辑 /// [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(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(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); } } /// /// 转义 CSV 字段中的特殊字符(逗号、双引号、换行符等) /// /// 原始字段值 /// 转义后的字段值 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(); } } }