using AddInPlugin.Util; using OpenTap; using OpenTap.Addin.Annotation; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Xml.Serialization; using UtilLib; namespace AddInPlugin { public enum VariableDirection { In = 0, Out = 1 } public class MethodVariable { [DataGridCol("Ãû³Æ", "*", true)] public string Name { get; set; } [XmlIgnore] public Type Type { get; set; } [XmlIgnore] public object Value { get; set; } [DataGridCol("·½Ïò", "*", true)] public VariableDirection Direction { get; set; } [DataGridCol("ÀàÐÍ", "*", true)] public string TypeName { get; set; } [DataGridCol("Öµ", "*", false)] public string ValueStr { get; set; } public override bool Equals(object obj) { if (obj is MethodVariable ev) { return ev.Name == Name && ev.ValueStr == ValueStr; } return base.Equals(obj); } public override int GetHashCode() { var h1 = Name?.GetHashCode() ?? 0; var h2 = ValueStr?.GetHashCode() ?? 0; return (((h1 + 742759321) * 1593213024) + h2) * 1079741372; } } [Prototype] [Display("DotNetStep", Description: "ÔËÐÐ.Net")] public class DotNetStep : VariableTestStep { #region Settings // ToDo: Add property here for each parameter the end user should be able to change //[Display("Instrument", Order: 0.0, Description: "The instrument that the query is sent to.")] //public DllAction Instrument { get; set; } //// ToDo: Add property here for each parameter the end user should be able to change //[Display("Dut", Order: 0.0, Description: "The instrument that the query is sent to.")] //public MyDUT Dut { get; set; } private string dllPath; [Display("·¾¶", Order: 0.0, Description: "Dll¾ø¶Ô·¾¶.")] [FilePath(FilePathAttribute.BehaviorChoice.Open)] public string DllPath { get => dllPath; set { if (dllPath != value) { dllPath = value; OnPropertyChanged(nameof(DllPath)); LoadDll(); } } } private Assembly assembly; private Type targetType; private ObservableCollection types; [XmlIgnore] public ObservableCollection Types { get => types; set { types = value; OnPropertyChanged(nameof(Types)); } } private string className; [Display("ÀàÃû", Order: 0.0, Description: "ÀàÃû.")] [DynamicSelect("Types")] public string ClassName { get => className; set { if (className != value) { className = value; if (!string.IsNullOrEmpty(value)) { LoadTypes(); } OnPropertyChanged(nameof(ClassName)); } } } private ObservableCollection methods; [XmlIgnore] public ObservableCollection Methods { get => methods; set { methods = value; OnPropertyChanged(nameof(Methods)); } } private string methodName; [Display("·½·¨", Order: 0.0, Description: "·½·¨Ãû.")] //[DynamicSelect(nameof(MethodName), "ClassName", "RefreshMethodName")] [DynamicSelect("Methods")] public string MethodName { get => methodName; set { if (methodName != value) { methodName = value; OnPropertyChanged(nameof(MethodName)); if (!string.IsNullOrEmpty(value)) { RefreshVariables(); } } } } private Dictionary methodCache = new Dictionary(); [XmlIgnore] private bool IsConstructorMethod = false; [XmlIgnore] private bool IsStatic = false; [XmlIgnore] private object instance; #endregion public DotNetStep() { // ToDo: Set default values for properties / settings. Rules.Add(() => !string.IsNullOrWhiteSpace(DllPath), "·¾¶²»ÄÜΪ¿Õ", DllPath); Rules.Add(() => !string.IsNullOrWhiteSpace(ClassName), "ÀàÃû²»ÄÜΪ¿Õ", ClassName); } private void LoadDll() { try { if (string.IsNullOrEmpty(DllPath)) { return; } assembly = Assembly.LoadFrom(DllPath); Types = new ObservableCollection(assembly.GetTypes().Select(e => e.ToString()).ToArray()); } catch (Exception ex) { Log.Error("DotNetStep load dll error. {0}", ex); } } public void LoadTypes() { methodCache.Clear(); try { targetType = assembly?.GetType(ClassName); if (targetType != null) { var creators = targetType?.GetConstructors(); if (creators != null) { for (int i = 0; i < creators.Length; i++) { var creator = creators[i]; var ps = creator.GetParameters(); var displayName = $"{targetType.Name} ({string.Join(", ", ps.Select(p => $"{p.ParameterType.ToString()} {p.Name}").ToArray())})"; methodCache[displayName] = creator; } } var methods = MethodInfoHelper.GetPublicMethodNames(targetType); if (methods != null) { for (int i = 0; i < methods.Count; i++) { var method = methods[i]; var ps = method.GetParameters(); var displayName = $"{method.Name} ({string.Join(", ", ps.Select(p => $"{p.ParameterType.ToString()} {p.Name}").ToArray())})"; methodCache[displayName] = method; } } } Methods = new ObservableCollection(methodCache.Keys.ToArray()); } catch (Exception ex) { Log.Error("DotNetStep LoadTypes error. {0}", ex); } } public void RefreshVariables() { if (MethodVariables == null) { MethodVariables = new ObservableCollection(); } MethodVariables.Clear(); try { if (!methodCache.ContainsKey(MethodName)) { return; } var method = methodCache[MethodName]; var ps = method.GetParameters(); IsStatic = method.IsStatic; IsConstructorMethod = method.IsConstructor; if (IsConstructorMethod) { MethodVariables.Add(new MethodVariable { Name = "(return)", Type = targetType, TypeName = targetType?.ToString(), Direction = VariableDirection.Out, }); } else if (!IsStatic) { var mi = (MethodInfo)method; MethodVariables.Add(new MethodVariable { Name = "(instance)", Type = targetType, TypeName = targetType?.ToString(), Direction = VariableDirection.In, }); MethodVariables.Add(new MethodVariable { Name = "(return)", Type = mi.ReturnType, TypeName = mi.ReturnType?.ToString(), Direction = VariableDirection.Out, }); } else { var mi = (MethodInfo)method; MethodVariables.Add(new MethodVariable { Name = "(return)", Type = mi.ReturnType, TypeName = mi.ReturnType?.ToString(), Direction = VariableDirection.Out, }); } foreach (var p in ps) { MethodVariables.Add(new MethodVariable { Name = p.Name, Type = p.ParameterType, TypeName = p.ParameterType.ToString(), Direction = p.IsOut ? VariableDirection.Out : VariableDirection.In, }); } } catch (Exception ex) { Log.Error("DotNetStep LoadVariables error. {0}", ex); } } public override void PrePlanRun() { // ToDo: Optionally add any setup code this step needs to run before the testplan starts base.PrePlanRun(); } /// /// ת»»²ÎÊý /// private void ResolveVariables() { var method = methodCache[methodName]; if (IsConstructorMethod) { //methodParams = new object[MethodVariables.Count - 1]; for (int i = 1; i < MethodVariables.Count; i++) { MethodVariable mv = MethodVariables[i]; if (mv.Direction == VariableDirection.In) { mv.Value = PlanRun.Resolve(this, mv.ValueStr, mv.Type); // mv.GetValue(PlanRun); } //methodParams[i-1] = mv.GetValue(PlanRun); } } else if (IsStatic) { //methodParams = new object[MethodVariables.Count - 2]; for (int i = 1; i < MethodVariables.Count; i++) { MethodVariable mv = MethodVariables[i]; if (mv.Direction == VariableDirection.In) { mv.Value = PlanRun.Resolve(this, mv.ValueStr, mv.Type); //mv.GetValue(PlanRun); } //methodParams[i] = mv.GetValue(PlanRun); } } else { //methodParams = new object[MethodVariables.Count - 1]; for (int i = 1; i < MethodVariables.Count; i++) { var mv = MethodVariables[i]; if (mv.Direction == VariableDirection.In) { mv.Value = PlanRun.Resolve(this, mv.ValueStr, mv.Type); //mv.GetValue(PlanRun); } //methodParams[i] = mv.GetValue(PlanRun); } } } /// /// ÔËÐк¯Êý /// private void RunMethod() { ResolveVariables(); if (IsConstructorMethod) { var method = (ConstructorInfo)methodCache[methodName]; var methodParams = new object[MethodVariables.Count - 1]; for (int i = 1; i < MethodVariables.Count; i++) { var mv = MethodVariables[i]; methodParams[i - 1] = mv.Value; } instance = method.Invoke(methodParams); MethodVariables[0].Value = instance; } else if (IsStatic) { var method = methodCache[methodName]; var methodParams = new object[MethodVariables.Count - 1]; for (int i = 1; i < MethodVariables.Count; i++) { var mv = MethodVariables[i]; methodParams[i - 1] = mv.Value; } MethodVariables[0].Value = method.Invoke(null, methodParams); for (int i = 0; i < methodParams.Length; i++) { MethodVariables[i + 1].Value = methodParams[i]; } } else { var method = methodCache[methodName]; var methodParams = new object[MethodVariables.Count - 2]; for (int i = 2; i < MethodVariables.Count; i++) { var mv = MethodVariables[i]; methodParams[i - 2] = mv.Value; } instance = PlanRun.Resolve(this, MethodVariables[0].ValueStr); MethodVariables[1].Value = method.Invoke(instance, methodParams); for (int i = 0; i < methodParams.Length; i++) { MethodVariables[i + 2].Value = methodParams[i]; } } } public override void Run() { try { // ToDo: Add test case code here //RunChildSteps(); //If step has child steps. RunMethod(); UpdateVariableToRuntime(); this.Verdict = Verdict.Pass; } catch (Exception ex) { Log.Error("DotNetStep Run error. {0}", ex); this.Verdict = Verdict.Error; } } private void UpdateVariableToRuntime() { //if (IsConstructorMethod) //{ // var methodVariable = MethodVariables[0]; // PlanRun.UpdateRuntimeVariable(methodVariable.ValueStr, instance); //} foreach (var mv in MethodVariables) { if (mv.Direction == VariableDirection.Out) { PlanRun.Update(this, mv.ValueStr, mv.Value); } } } public override void PostPlanRun() { // ToDo: Optionally add any cleanup code this step needs to run after the entire testplan has finished base.PostPlanRun(); } } }