//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.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Xml.Linq; namespace OpenTap.Plugins.PluginDevelopment { // // This example shows how to create a custom annotation for an IPAddress type // It will provide a way to convert from/to IPAddress/string, a way of showing parsing errors, // and a way of showing suggested values to the user. // // To create an annotation, two things are needed. The annotation itself and an IAnnotator implementation. // The IAnnotator is used to annotate data with the created annotations. // // Annotation types used here: // IStringValueAnnotation - Provides a way to convert from value to a string. This is used by the GUI to create a textbox. // IErrorAnnotation - Provides a way to display data parsing errors to the user. // ISuggestedValuesAnnotation - Provides a way to suggest values to the user. // Other Annotation types available: // IAvailableValuesAnnotation, IMultiSelect - These are for dropdowns. // IAccessAnnotation - Read-Only and hide functionality. // ICollectionAnnotation - collections of things. (advanced) // IMembersAnnotation - members of objects. (advanced) // IOwnedAnnotation - annotation that has Read/Write functionality. (advanced) // IMethodAnnotation - Functionality that can be invoked e.g using a button. // IValueDescriptionAnnotation, IStringExampleAnnotation - Used for improved tooltips. // /// Annotate an IPAddress public class IPAnnotation : IStringValueAnnotation, IErrorAnnotation, ISuggestedValuesAnnotation { private AnnotationCollection annotations; public IPAnnotation(AnnotationCollection annotations) { this.annotations = annotations; } /// Implementing IStringValueAnnotation.Value. public string Value { get { // convert from the value to a string. var ip = (IPAddress) annotations.Get().Value; if (ip == null) return ""; return ip.ToString(); } set { try { IPAddress ip = IPAddress.Parse(value); annotations.Get().Value = ip; error = null; } catch(Exception e) { error = e.Message; } } } public string error; // Implementing IErrorAnnotation using this property. public IEnumerable Errors { get { return error == null ? Array.Empty() : new string[] { error }; } } /// Implementing ISuggestedValuesAnnotation. public IEnumerable SuggestedValues { get { yield return IPAddress.IPv6Loopback.ToString(); yield return IPAddress.IPv6Any.ToString(); yield return IPAddress.IPv6None.ToString(); yield return IPAddress.Parse("127.0.0.1").ToString(); } } } // To fully support IPaddresses, we also need to be able to serialize/deserialize them. // This plugin class takes care of that. public class IpAdressSerializer : ITapSerializerPlugin { public double Order => 5; public bool Deserialize(XElement node, ITypeData t, Action setter) { if(t == TypeData.FromType(typeof(IPAddress)) == false) return false; IPAddress address; if (IPAddress.TryParse(node.Value, out address)) setter(address); return true; } public bool Serialize(XElement node, object obj, ITypeData expectedType) { if(obj is IPAddress) { node.Value = obj.ToString(); return true; } return false; } } /// For demonstration this step has a configurable IP address. [Display("Ping Step", "A step that pings an IP address and measures the round trip time.", Groups: new[] { "Examples", "Plugin Development", "GUI" })] public class PingStep : TestStep { public IPAddress IPAddress { get; set; } = IPAddress.IPv6Loopback; public override void Run() { var p1 = new System.Net.NetworkInformation.Ping(); // Wait 5 s for a connection var reply = p1.Send(IPAddress); if (reply.Status == (IPStatus.Success)) { Log.Info("Address: {0}", IPAddress); Log.Info("Got reply after: {0} ms", reply.RoundtripTime); UpgradeVerdict(Verdict.Pass); } else { Log.Info("No reply from " + IPAddress); Log.Info("Reply Status: " + reply.Status); UpgradeVerdict(Verdict.Fail); } } [Browsable(true)] public void ListAnnotators() { // it can sometimes be useful to list all plugins of a kind to see the order in which they are used. foreach (var controlProvider in PluginManager.GetPlugins().Select(x => (IAnnotator)Activator.CreateInstance(x)).ToArray().OrderBy(x => x.Priority)) { Log.Info("{0} : {1}", controlProvider.Priority, controlProvider.ToString()); } } } /// IpAnnotator is used to check when IPAnnotation is applicable and then apply it. public class IPAnnotator : IAnnotator { /// /// Priority is used to determine the order in which annotators are being used. /// This can be important when layered annotaions are needed. In this case it should just have a relatively low value. /// public double Priority => 1; public void Annotate(AnnotationCollection annotations) { var member = annotations.Get()?.Member; if (member == null) return; if (member.TypeDescriptor == TypeData.FromType(typeof(IPAddress)) == false) return; // now its known that it is an IPAddress annotations.Add(new IPAnnotation(annotations)); } } }