using AddInPlugin; using AddInPlugin.Setting; using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.Win32; using OpenTap; using OpenTap.Addin; using OpenTap.Addin.Listener; using OpenTap.Addin.Util; using OpenTap.Plugins.BasicSteps; using SeqEditor.Util; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; namespace OpenTapEditor; /// /// Interaction logic for SequencePanel.xaml /// [ObservableObject] public partial class ExecutionPanel : UserControl, ITabItem, IStepRunStatus { private TestPlan Plan; private TestPlanRun PlanRun; private TapThread testPlanThread; private string startedSeqName; [ObservableProperty] private ITestStep selectedTestStep; [ObservableProperty] private bool isRunning = false; [ObservableProperty] private bool hasChanged = false; public string TabName { get { return $"Execution-{Plan?.Name}"; } set { } } [ObservableProperty] private RuningMode runingMode = RuningMode.Stop; private ManualResetEvent SingleStepHandle = new ManualResetEvent(true); public event EventHandler RunningFinished; public ExecutionPanel(TestPlan plan, string seqName) { this.Plan = plan; this.startedSeqName = seqName; InitializeComponent(); treeView.Plan = plan; } private void VariableControl_SequenceChanged(object? sender, EventArgs e) { HasChanged = true; } private void ChildTestSteps_ChildStepsChanged(TestStepList senderList, TestStepList.ChildStepsChangedAction Action, ITestStep Object, int Index) { HasChanged = true; } private void ChildTestSteps_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { HasChanged = true; } private void Button_Click(object sender, RoutedEventArgs e) { var btn = (Button)sender; switch (btn.Tag) { case "运行": RunBtn(); break; case "调试": RunTestPlan(RuningMode.RunningInDebugMode); break; case "单步运行": StepRuning(); break; case "停止": StopRunTestPlan(); break; default: break; } } private void RunBtn() { if (IsRunning) { if (RuningMode == RuningMode.RunningInStepMode) { RuningMode = RuningMode.Running; SingleStepHandle.Set(); return; } if (RuningMode == RuningMode.RunningInDebugModeButStep) { RuningMode = RuningMode.RunningInDebugMode; SingleStepHandle.Set(); return; } if (RuningMode == RuningMode.RunningInDebugMode) { SingleStepHandle.Set(); return; } } else { RunTestPlan(RuningMode.Running); } } public void RunTestPlan(RuningMode mode) { try { /// 保存一下StationGlobals变量 try { StationGlobalsManager.WriteToLocal(UIStationGlobalsManager.Instance.Datasource); } catch (Exception ex) { } IsRunning = true; RuningMode = mode; treeView.ComplatedIds.Clear(); treeView.UpdateStepStatus(); RunVars = null; RunVarContext = null; handledPlan.Clear(); handledPlan.Add(Plan); Plan.BreakOffered += Plan_BreakOffered; settingView.CanEdit = false; runVariableControl.Visibility = Visibility.Visible; var uiListener = new UIListener(SeqEditorUI.Instance.logControl, this); testPlanThread = TapThread.Start(() => { try { var s = ResultSettings.Current; ResultSettings.Current.Add(uiListener); if (StationSettings.Current.ReportType == ReportType.TXT) { ResultSettings.Current.Add(new TxtReportListener("Report")); } else { ResultSettings.Current.Add(new HtmlReportListener("Report")); } var res = ResultSettings.Current; var run = Plan.Execute(startedSeqName, res, null, null, null); } catch (Exception ex) { SeqEditorUI.Log.Error("Start TestPlan Error. {0}", ex); } finally { ResultSettings.Current.Remove(uiListener); RunVarContext?.Release(); Dispatcher.BeginInvoke(() => { RunningFinished?.Invoke(this, new EventArgs()); RuningMode = RuningMode.Stop; Plan.BreakOffered -= Plan_BreakOffered; treeView.DebugStepId = Guid.Empty; treeView.RunningStepId = Guid.Empty; settingView.CanEdit = true; IsRunning = false; foreach (var handled in handledPlan) { handled.BreakOffered -= Plan_BreakOffered; } handledPlan.Clear(); }); } }, "UI_Start"); } catch (Exception ex) { SeqEditorUI.Log.Error("Run TestPlan Error. {0}", ex); } } [ObservableProperty] private RunningVarContext runVarContext; [ObservableProperty] private ObservableCollection runVars; private readonly List handledPlan = new List(); private string currentSeqId = null; private void Plan_BreakOffered(object? sender, BreakOfferedEventArgs e) { try { var stepRun = e.TestStepRun; var planRun = stepRun.testPlanRun; var step = stepRun.CurrentStep; var plan = step.Root; var seqId = $"{plan.VisualPath}_{planRun.RunningSeqName}"; TestStepList list = plan.GetStepsByName(planRun.RunningSeqName); Dispatcher.BeginInvoke(() => { treeView.DebugStepId = e.TestStepRun.TestStepId; if (RunVarContext == null) { RunVarContext = new RunningVarContext() { PlanRun = planRun }; RunVarContext.Children = RunningVar.FromTest(RunVarContext, null, false, new ObservableCollection() { new TestVariable(){ Name = "Locals", Type = TestVariableType.Container, Children = list.Variables }, new TestVariable(){ Name = "Parameters", Type = TestVariableType.Container, Children = list.Parameters }, new TestVariable(){ Name = "FileGlobals", Type = TestVariableType.Container, Children = plan.Variables }, new TestVariable(){ Name = "StationGlobals", Type = TestVariableType.Container, Children = UIStationGlobalsManager.Instance.Datasource, }}); RunVars = RunVarContext.Children; } RunVarContext.Children[0].ContextName = list.SequenceName; RunVarContext.Children[1].ContextName = list.SequenceName; RunVarContext.Children[2].ContextName = plan.VisualPath; if (currentSeqId != seqId) { RunVarContext.RefreshPlan(planRun, plan, list); treeView.Datasource = list; currentSeqId = seqId; } }).Wait(); if (step is SequenceCallStep scs) { //if (lastChildPlan != null) //{ // lastChildPlan.BreakOffered -= Plan_BreakOffered; //} if (!handledPlan.Contains(scs.runningPlan)) { scs.runningPlan.BreakOffered += Plan_BreakOffered; handledPlan.Add(scs.runningPlan); } } if (RuningMode == RuningMode.RunningInStepMode || RuningMode == RuningMode.RunningInDebugModeButStep) { SingleStepHandle.Reset(); WaitHandle.WaitAny(new[] { SingleStepHandle, testPlanThread.AbortToken.WaitHandle }); } else if (RuningMode == RuningMode.RunningInDebugMode) { if (treeView.NeedBreak(e.TestStepRun.TestStepId)) { SingleStepHandle.Reset(); WaitHandle.WaitAny(new[] { SingleStepHandle, testPlanThread.AbortToken.WaitHandle }); } } } catch (Exception ex) { SeqEditorUI.Log.Error($"Plan_BreakOffered Error: {ex}"); } } private void StepRuning() { if (IsRunning && RuningMode != RuningMode.Running) { if (RuningMode == RuningMode.RunningInDebugMode) { RuningMode = RuningMode.RunningInDebugModeButStep; } SingleStepHandle.Set(); } else { RunTestPlan(RuningMode.RunningInStepMode); } } public void StopRunTestPlan() { testPlanThread?.Abort(); } public bool TrySaveTestStep(Func check) { if (!string.IsNullOrEmpty(Plan.Path)) { Plan.Save(Plan.Path); HasChanged = false; OnPropertyChanged(nameof(TabName)); return true; } return TrySaveAs(check); } public bool TrySaveAs(Func check) { SaveFileDialog sfd = new SaveFileDialog() { Filter = "Test Plan|*.TapPlan", DefaultExt = ".TapPlan" }; if (sfd.ShowDialog() == true) { if (check.Invoke(sfd.FileName)) { Plan.Save(sfd.FileName); HasChanged = false; OnPropertyChanged(nameof(TabName)); return true; } } return false; } private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { if (e.NewValue != null) { settingView.UpdateSource((ITestStep)e.NewValue); SelectedTestStep = (ITestStep)e.NewValue; } } private void variableControl_SelectedSequenceChanged(object sender, TestStepList e) { treeView.Datasource = e; settingView.Seq = e; } public void ComplatedStepId(Guid id) { Dispatcher.BeginInvoke(() => { treeView.ComplatedIds[id] = true; treeView.UpdateStepStatus(); }); } public void CurrentStepId(Guid id) { Dispatcher.BeginInvoke(() => { treeView.ComplatedIds.TryRemove(id, out _); treeView.UpdateStepStatus(); treeView.RunningStepId = id; }); } public void Close() { RunVarContext = null; RunVars = null; settingView = null; Plan = null; PlanRun = null; treeView.Unload(); } }