//Copyright 2012-2019 Keysight Technologies
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
using System;
using System.Windows;
using System.IO;
using Keysight.OpenTap.Wpf;
using System.Windows.Threading;
using System.Windows.Forms;
using System.Windows.Controls;
using Brushes = System.Windows.Media.Brushes;
using Button = System.Windows.Controls.Button;
// This file shows how to implement a custom dockable panel. The panel can be enabled/disabled under
// the View menu choice in the TAP GUI. The panel can be configured to be either floating or docked.
namespace OpenTap.Plugins.PluginDevelopment
{
[Display("Dockable Panel Example")]
// A custom dockable panel has to implement ITapDockPanel.
public class DockablePanel : ITapDockPanel
{
// Default panel dimensions
public double? DesiredWidth { get { return 200; } }
public double? DesiredHeight { get { return 200; } }
dockResultListener listener;
static TraceSource Log = OpenTap.Log.CreateSource("DockExample");
// In this method the layout of the dockable panel is defined/setup.
// The ITapDockContext enables you to set the TestPlan, attach ResultListeners,
// configure Settings and start execution of a TestPlan.
public FrameworkElement CreateElement(ITapDockContext context)
{
var loadPlanBtn = new Button() { Content = "Load Plan" };
var runPlanBtn = new Button() { Content = "Run Plan" };
var stopPlanBtn = new Button() { Content = "Stop Plan" };
var statusTxt = new TextBlock
{
FontSize = 40,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
// Setup UI panel and add elements
var panel = new StackPanel() { Orientation = System.Windows.Controls.Orientation.Vertical };
panel.Children.Add(loadPlanBtn);
panel.Children.Add(runPlanBtn);
panel.Children.Add(stopPlanBtn);
panel.Children.Add(statusTxt);
TapThread planThread = null;
// Register event-handling methods for each of the buttons
runPlanBtn.Click += (s, e) => planThread = context.Run();
stopPlanBtn.Click += (s, e) => planThread?.Abort();
loadPlanBtn.Click += (s, e) =>
{
var fd = new OpenFileDialog();
fd.CheckFileExists = true;
var r = fd.ShowDialog();
try
{
if (r == DialogResult.OK)
context.Plan = TestPlan.Load(fd.FileName);
}
catch (InvalidOperationException ex)
{
Log.Warning("{0}", ex.Message);
}
};
// Attach Result listener. runPlanBtn and statusTxt is updated according to status
context.ResultListeners.Add(listener = new dockResultListener(runPlanBtn, statusTxt));
return panel;
}
// Result listener used for dockable panel. Result listeners can be used in
// a custom dockable panel.
[System.ComponentModel.Browsable(false)]
class dockResultListener : ResultListener
{
Button btn;
TextBlock txt;
public dockResultListener(Button b, TextBlock txt)
{
btn = b;
this.txt = txt;
OpenTap.Log.RemoveSource(this.Log);
}
public override void OnTestPlanRunCompleted(TestPlanRun planRun, Stream logStream)
{
GuiHelper.GuiInvoke(() =>
{
btn.IsEnabled = true;
txt.Text = planRun.Verdict.ToString();
txt.Foreground = Brushes.Gray;
if (planRun.Verdict == Verdict.Pass)
txt.Foreground = Brushes.Green;
if (planRun.Verdict == Verdict.Fail)
txt.Foreground = Brushes.Red;
});
}
public override void OnTestPlanRunStart(TestPlanRun planRun)
{
GuiHelper.GuiInvoke(() =>
{
btn.IsEnabled = false;
txt.Text = "";
});
}
}
}
// GuiHelper class for updating GUIs. It can be reused for custom UI components.
class GuiHelper
{
static Dispatcher getGuiDispatcher()
{
if (System.Windows.Application.Current != null) return System.Windows.Application.Current.Dispatcher;
return null;
}
///
/// Invoke action in GUI thread. Optionally blocking.
///
///
/// use Invoke or BeginInvoke
public static void GuiInvoke(Action action, Dispatcher dispatch = null, DispatcherPriority priority = DispatcherPriority.Normal)
{
try
{
dispatch = dispatch ?? getGuiDispatcher();
if (dispatch == null)
{
try
{
action();
}
catch (InvalidOperationException)
{
// There is a chance that this might throw an InvalidOperationException ("The calling thread cannot access this object because a different thread owns it.")
// because we are not on the correct thread.
// This can happen when the app is closing.
}
}
else if (dispatch.CheckAccess())
{
action();
}
else
{
dispatch.Invoke(action, priority);
}
}
catch (System.Threading.Tasks.TaskCanceledException)
{
// If the dispatcher is stopped, this can happen.
// Should only happen upon exiting, so we need to check that it is because the dispatcher is shutting down.
if (dispatch != null && (dispatch.HasShutdownStarted || dispatch.HasShutdownFinished))
{
// Do nothing. This is OK.
}
else
{
throw;
}
}
}
}
}