Skip to content
Snippets Groups Projects
Commit 9acfb35b authored by Tobias's avatar Tobias
Browse files

Added Reflection Methods to call Reflection Methods,

Override ChangeOutput and ClearOutput in FrameControlComponent to configure externalCC accordingly,
Call ReflectionMethods on Outputs to avoid Type Exception default OrderOutput instances,
Call OCCUPIER related methods with FRAME name
parent bdcd6e09
No related branches found
No related tags found
No related merge requests found
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
......@@ -18,7 +19,14 @@ namespace ControlComponents.Core
// TODO get IControlComponent from dict and check type
public T GetComponent<T>(string id) where T : IControlComponent
{
return (T)this.Values.First(c => c.ComponentName == id && c is T);
try
{
return (T)this.Values.First(c => c.ComponentName == id);
}
catch (InvalidOperationException e)
{
throw new InvalidOperationException($"Cannot cast {id} to {typeof(T)}", e);
}
}
public int CountComponents<T>()
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ControlComponents.Core
......@@ -10,7 +10,7 @@ namespace ControlComponents.Core
public static TReturn ReadProperty<TReturn>(string targetRole, string propertyName, IControlComponent instance)
{
// TODO extend this to use all known IControlComponent properties to make the code faster
if(propertyName == nameof(IControlComponent.EXST))
if (propertyName == nameof(IControlComponent.EXST))
{
return (TReturn)(object)instance.EXST;
}
......@@ -20,33 +20,70 @@ namespace ControlComponents.Core
public static void CallMethod(string targetRole, string methodName, IControlComponent instance)
{
instance.GetType().GetMethod(methodName).Invoke(instance, new object[]{});
instance.GetType().GetMethod(methodName).Invoke(instance, new object[] { });
}
public static TReturn CallMethod<TReturn>(string targetRole, string methodName, IControlComponent instance)
{
// TODO extend this to use all known IControlComponent methods to make the code faster
if(methodName == nameof(IControlComponent.IsFree))
if (methodName == nameof(IControlComponent.IsFree))
{
return (TReturn)(object)instance.IsFree();
}
return (TReturn) instance.GetType().GetMethod(methodName).Invoke(instance, new object[]{});
else
{
return (TReturn)instance.GetType().GetMethod(methodName).Invoke(instance, new object[] { });
}
}
public static void CallMethod<TParam>(string targetRole, string methodName, TParam param, IControlComponent instance)
{
instance.GetType().GetMethod(methodName).Invoke(instance, new object[]{param});
instance.GetType().GetMethod(methodName).Invoke(instance, new object[] { param });
}
public static void CallMethod<TParam1, TParam2>(string targetRole, string methodName, TParam1 param1, TParam2 param2, IControlComponent instance)
{
Type type = instance.GetType();
var methods = type.GetMethods().Where(m => m.Name == methodName);
MethodInfo method = methods.First(m => !m.IsGenericMethod);
method.Invoke(instance, new object[] { param1, param2 });
}
public static TReturn CallMethod<TParam, TReturn>(string targetRole, string methodName, TParam param, IControlComponent instance)
{
return (TReturn) instance.GetType().GetMethod(methodName).Invoke(instance, new object[]{param});
Type type = instance.GetType();
MethodInfo method = type.GetMethod(methodName);
return (TReturn)method.Invoke(instance, new object[] { param });
}
public static TReturn CallMethod<TParam1, TParam2, TReturn>(string targetRole, string methodName, TParam1 param1, TParam2 param2, IControlComponent instance)
{
return (TReturn) instance.GetType().GetMethod(methodName).Invoke(instance, new object[]{param1, param2});
return (TReturn)instance.GetType().GetMethod(methodName).Invoke(instance, new object[] { param1, param2 });
}
public static void CallMethodGeneric<TParam1, TParam2, TParam3>(string targetRole, string methodName, TParam1 param1, TParam2 param2, TParam3 param3, IControlComponent instance)
{
Type type = instance.GetType();
MethodInfo method = type.GetMethods().Where(m => m.Name == methodName && m.GetParameters().Length == 3).First();
MethodInfo generic = method.MakeGenericMethod(typeof(TParam3));
generic.Invoke(instance, new object[] { param1, param2, param3 });
}
public static TReturn CallMethodGeneric<TParam1, TParam2, TParam3, TReturn>(string targetRole, string methodName, TParam1 param1, TParam2 param2, TParam3 param3, IControlComponent instance)
{
Type type = instance.GetType();
MethodInfo method = type.GetMethods().Where(m => m.Name == methodName && m.GetParameters().Length == 3 && m.ReturnType != typeof(void)).First();
MethodInfo generic = method.MakeGenericMethod(typeof(TParam3), typeof(TReturn));
return (TReturn)generic.Invoke(instance, new object[] { param1, param2, param3 });
}
public static TReturn CallMethodGeneric<TParam1, TParam2, TReturn>(string targetRole, string methodName, TParam1 param1, TParam2 param2, IControlComponent instance)
{
var type = instance.GetType();
var method = type.GetMethods().Where(m => m.Name == methodName && m.GetParameters().Length == 2 && m.ReturnType != typeof(void)).First();
var generic = method.MakeGenericMethod(typeof(TReturn));
var result = generic.Invoke(instance, new object[] { param1, param2 });
return (TReturn)result;
}
public static void Subscribe<T>(string targetRole, string eventName, T eventHandler, IControlComponent instance)
......
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
......@@ -7,16 +8,53 @@ namespace ControlComponents.Core
{
// This component includes the external deployed operation mode
protected readonly T externalControlComponent;
protected readonly IEnumerable<string> OccupationMethods;
protected FrameControlComponent(string name, T cc, ICollection<IOperationMode> opModes, ICollection<IOrderOutput> orderOutputs, ICollection<string> neededRoles)
: base(name, opModes, orderOutputs, neededRoles)
{
this.externalControlComponent = cc;
OccupationMethods = new List<string>(){
nameof(Reset),
nameof(Start),
nameof(Suspend),
nameof(Unsuspend),
nameof(Stop),
nameof(Hold),
nameof(Unhold),
nameof(Abort),
nameof(Clear),
nameof(Occupy),
nameof(Free),
nameof(Prio)
};
// TODO ChangeOrderOuput at cc for each output that is set already
}
// private void DeconfigureOutputsAtExternalCC()
// {
// foreach (var role in _externalCC.Roles)
// {
// _externalCC.ClearOutput(role);
// }
// }
// private void ConfigureOutputsAtExternalCC()
// {
// foreach (var role in _externalCC.Roles)
// {
// // TODO this can be used for READ interfaces to access target component directly
// // _externalCC.ChangeOutput(role, this.outputs[role].ComponentName);
// if (outputs[role].IsSet)
// _externalCC.ChangeOutput(role, this.execution.ComponentName);
// }
// }
// TODO factory method is bad if class should be inherited from
public static FrameControlComponent<T> Create(string name, T cc, IControlComponentProvider provider)
{
// TODO create ExternalOpmodeOutput to inject it into opmode
// TODO ?? create ExternalOpmodeOutput to inject it into opmode
var output = new OrderOutput("ExternalOperationMode", name, provider, cc);
Collection<IOperationMode> opmodes = CreateConfigOperationModes(cc, output);
Collection<IOrderOutput> outputs = CreateOrderOutputs(name, cc, provider);
......@@ -45,10 +83,32 @@ namespace ControlComponents.Core
return opmodes;
}
public override bool ChangeOutput(string role, string id)
{
bool success = orderOutputs[role].ChangeComponent(id);
// After setting the output of the FrameControlComponent, set the correct output of the ExternalControlComponent
if (externalControlComponent.Roles.Contains(role))
success &= externalControlComponent.ChangeOutput(role, ComponentName);
return success;
}
public override void ClearOutput(string role)
{
// Before clearing the output of the FrameControlComponent, clear the correct output of the ExternalControlComponent
if (externalControlComponent.Roles.Contains(role))
externalControlComponent.ClearOutput(role);
orderOutputs[role].ClearComponent();
}
public override TReturn ReadProperty<TReturn>(string targetRole, string propertyName)
{
if (this.orderOutputs.ContainsKey(targetRole))
return ControlComponentReflection.ReadProperty<TReturn>(targetRole, propertyName, this.orderOutputs[targetRole]);
{
return ControlComponentReflection.CallMethodGeneric<string, string, TReturn>(targetRole, nameof(ReadProperty), targetRole, propertyName, this.orderOutputs[targetRole]);
}
else
return ControlComponentReflection.ReadProperty<TReturn>(targetRole, propertyName, this);
}
......@@ -56,7 +116,7 @@ namespace ControlComponents.Core
public override void CallMethod(string targetRole, string methodName)
{
if (this.orderOutputs.ContainsKey(targetRole))
ControlComponentReflection.CallMethod(targetRole, methodName, this.orderOutputs[targetRole]);
ControlComponentReflection.CallMethod<string, string>(targetRole, nameof(CallMethod), targetRole, methodName, this.orderOutputs[targetRole]);
else
ControlComponentReflection.CallMethod(targetRole, methodName, this);
}
......@@ -64,7 +124,11 @@ namespace ControlComponents.Core
public override void CallMethod<TParam>(string targetRole, string methodName, TParam param)
{
if (this.orderOutputs.ContainsKey(targetRole))
ControlComponentReflection.CallMethod<TParam>(targetRole, methodName, param, this.orderOutputs[targetRole]);
// Methods that occupy outputs need to use the FrameControlComponent Name and not the ExternalControlComponent Name to occupy
if (OccupationMethods.Contains(methodName))
ControlComponentReflection.CallMethodGeneric<string, string, string>(targetRole, nameof(CallMethod), targetRole, methodName, ComponentName, this.orderOutputs[targetRole]);
else
ControlComponentReflection.CallMethodGeneric<string, string, TParam>(targetRole, nameof(CallMethod), targetRole, methodName, param, this.orderOutputs[targetRole]);
else
ControlComponentReflection.CallMethod<TParam>(targetRole, methodName, param, this);
}
......@@ -72,7 +136,7 @@ namespace ControlComponents.Core
public override TReturn CallMethod<TReturn>(string targetRole, string methodName)
{
if (this.orderOutputs.ContainsKey(targetRole))
return ControlComponentReflection.CallMethod<TReturn>(targetRole, methodName, this.orderOutputs[targetRole]);
return ControlComponentReflection.CallMethodGeneric<string, string, TReturn>(targetRole, nameof(CallMethod), targetRole, methodName, this.orderOutputs[targetRole]);
else
return ControlComponentReflection.CallMethod<TReturn>(targetRole, methodName, this);
}
......@@ -80,7 +144,13 @@ namespace ControlComponents.Core
public override TReturn CallMethod<TParam, TReturn>(string targetRole, string methodName, TParam param)
{
if (this.orderOutputs.ContainsKey(targetRole))
return ControlComponentReflection.CallMethod<TParam, TReturn>(targetRole, methodName, param, this.orderOutputs[targetRole]);
{
// Outputs of FrameControlComponent are occupied by FrameComponent not ExternalControlComponent => Change to correct OCCUPIER name
if (methodName == nameof(IsUsableBy))
return ControlComponentReflection.CallMethodGeneric<string, string, string, TReturn>(targetRole, nameof(CallMethod), targetRole, methodName, ComponentName, this.orderOutputs[targetRole]);
else
return ControlComponentReflection.CallMethodGeneric<string, string, TParam, TReturn>(targetRole, nameof(CallMethod), targetRole, methodName, param, this.orderOutputs[targetRole]);
}
else
return ControlComponentReflection.CallMethod<TParam, TReturn>(targetRole, methodName, param, this);
}
......
......@@ -20,7 +20,7 @@ namespace ControlComponents.Core.Tests
string FailingOpModeStart = "FAILING-Start";
string FailingOpModeExecute = "FAILING-Execute";
ControlComponent ese;
ExtendedControlComponent ese;
ControlComponent externalCC;
ExtendedOrderOutput externalCCOutput;
FrameControlComponent<IControlComponent> sut;
......@@ -45,13 +45,15 @@ namespace ControlComponents.Core.Tests
provider = new Mock<IControlComponentProvider>();
// ESE
ese = new ControlComponent(ESE);
ese = new ExtendedControlComponent(ESE);
provider.Setup(p => p.GetComponent<IControlComponent>(ese.ComponentName)).Returns(ese);
ese.AddOperationMode(new OperationMode(OPMODE));
provider.Setup(p => p.GetComponent<IControlComponent>(ese.ComponentName)).Returns(ese);
// EXTERNAL CC
externalCC = new ControlComponent(CC);
externalCC = new ExtendedControlComponent(CC);
provider.Setup(p => p.GetComponent<IControlComponent>(externalCC.ComponentName)).Returns(externalCC);
// external controlcomponent has different opmodes to test with
externalCC.AddOperationMode(new FailingOperationModeReset(FailingOpModeReset));
......@@ -63,13 +65,12 @@ namespace ControlComponents.Core.Tests
externalCCOutput = new ExtendedOrderOutput(ROLE, externalCC.ComponentName, provider.Object);
externalCC.AddOrderOutput(externalCCOutput);
provider.Setup(p => p.GetComponent<IControlComponent>(externalCC.ComponentName)).Returns(externalCC);
// FRAME CC
sut = FrameControlComponent<IControlComponent>.Create(FRAMECC, externalCC, provider.Object);
sut.ChangeOutput(ROLE, ese.ComponentName);
provider.Setup(p => p.GetComponent<IControlComponent>(sut.ComponentName)).Returns(sut);
sut.ChangeOutput(ROLE, ese.ComponentName);
}
[TearDown]
......@@ -86,8 +87,6 @@ namespace ControlComponents.Core.Tests
}
sut.OpModeName.Should().Be("NONE");
externalCC.OpModeName.Should().Be("NONE");
externalCCOutput.IsSet.Should().BeFalse();
}
[Test]
......@@ -97,6 +96,60 @@ namespace ControlComponents.Core.Tests
}
[Test]
public void TestReflectionMethods()
{
sut.ReadProperty<string>(ROLE, nameof(IExtendedControlComponent.TestString)).Should().Be("TestString");
sut.CallMethod(ROLE, nameof(IExtendedControlComponent.TestMethod));
ese.TestString.Should().Be("ChangedTestString");
sut.CallMethod<string>(ROLE, nameof(IExtendedControlComponent.ChangedTestString), "NewTestString");
ese.TestString.Should().Be("NewTestString");
sut.CallMethod<string,string>(ROLE, nameof(IExtendedControlComponent.ChangedAndReturnTestString), "NextTestString").Should().Be("NextTestString");
sut.CallMethod<string>(ROLE, nameof(IExtendedControlComponent.GetTestString)).Should().Be("NextTestString");
}
[Test]
public void When_IsUsableBy_WithReflection_Then_ReturnTrue()
{
ese.Occupy(sut.ComponentName);
sut.CallMethod<string, bool>(ROLE, nameof(sut.IsUsableBy), CC).Should().BeTrue();
}
[Test]
public void When_Occupy_WithReflection_Then_OccupiedBySUT()
{
sut.CallMethod<string>(ROLE, nameof(sut.Occupy), CC);
ese.OCCUPIER.Should().Be(FRAMECC);
}
[Test]
public async Task When_Reset_WithReflection_Then_EseIsResetting()
{
Task running = sut.CallMethod<string,Task>(ROLE, nameof(sut.SelectOperationMode), OPMODE);
sut.CallMethod<string>(ROLE, nameof(sut.Reset), CC);
ese.EXST.Should().Be(ExecutionState.RESETTING);
ese.OCCUPIER.Should().Be(sut.ComponentName);
sut.CallMethod<string>(ROLE, nameof(sut.Stop), CC);
await sut.CallMethod<Task>(ROLE, nameof(sut.DeselectOperationMode));
await running;
}
[Test]
public void Given_OutputIsNotSet_When_Create_Then_OutputNotSetAtExternal()
{
externalCCOutput.IsSet.Should().BeTrue();
sut.ClearOutput(ROLE);
externalCCOutput.IsSet.Should().BeFalse();
}
[Test]
[Ignore("Cases where an output can not be set exist")]
// TODO how can a missing output be simulated?
public async Task Given_MissingOutput_When_SelectOperationMode_Then_Abort()
{
sut.ClearOutput(ROLE);
......@@ -106,14 +159,11 @@ namespace ControlComponents.Core.Tests
}
[Test]
public void Given_Output_When_SelectOperationMode_Then_FrameComponentAtOutputConfigured()
public void When_Created_Then_CorrectHierarchyConfiguration()
{
// Output is not "filled" before opmode is started
Assert.Throws(typeof(NullReferenceException), () => externalCCOutput.ComponentName.ToString());
running = sut.SelectOperationMode(NormalOpMode);
// output can access ese component name
externalCCOutput.ComponentName.Should().Be(ese.ComponentName);
// output is configured with sut component and not with the ese component
externalCCOutput.GetControlComponent.ComponentName.Should().Be(sut.ComponentName);
}
......@@ -137,6 +187,36 @@ namespace ControlComponents.Core.Tests
externalCC.EXST.Should().Be(ExecutionState.COMPLETED);
}
[Test]
public async Task Given_ExternalCC_When_NormalSuspendRun_Then_CorrectStateFlow()
{
running = sut.SelectOperationMode(NormalOpMode);
sut.OpModeName.Should().Be(NormalOpMode);
externalCC.OpModeName.Should().Be(NormalOpMode);
await sut.ResetAndWaitForIdle(SENDER);
sut.EXST.Should().Be(ExecutionState.IDLE);
externalCC.EXST.Should().Be(ExecutionState.IDLE);
await sut.StartAndWaitForExecute(SENDER);
sut.EXST.Should().Be(ExecutionState.EXECUTE);
externalCC.EXST.Should().Be(ExecutionState.EXECUTE);
await sut.SuspendAndWaitForSuspended(SENDER);
// await externalCC.SuspendAndWaitForSuspended(sut.ComponentName);
sut.EXST.Should().Be(ExecutionState.SUSPENDED);
externalCC.EXST.Should().Be(ExecutionState.SUSPENDED);
await sut.UnsuspendAndWaitForExecute(SENDER);
// await externalCC.UnsuspendAndWaitForExecute(sut.ComponentName);
sut.EXST.Should().Be(ExecutionState.EXECUTE);
externalCC.EXST.Should().Be(ExecutionState.EXECUTE);
await sut.WaitForCompleted();
sut.EXST.Should().Be(ExecutionState.COMPLETED);
externalCC.EXST.Should().Be(ExecutionState.COMPLETED);
}
[Test]
public async Task Given_ExternalOpMode_When_ResetFails_Then_Aborted()
{
......@@ -229,10 +309,46 @@ namespace ControlComponents.Core.Tests
interface IExtendedOrderOutput : IExtendedControlComponent
{
}
interface IExtendedControlComponent : IControlComponent
{
string TestString { get; }
void TestMethod();
void ChangedTestString(string newString);
string ChangedAndReturnTestString(string newString);
string GetTestString();
}
interface IExtendedControlComponent : IControlComponent { }
internal class ExtendedControlComponent : ControlComponents.Core.ControlComponent, IExtendedControlComponent
{
private string testString = "TestString";
public ExtendedControlComponent(string name) : base(name)
{
}
public string TestString => testString;
public void TestMethod()
{
testString = "ChangedTestString";
}
public void ChangedTestString(string newString)
{
testString = newString;
}
public string ChangedAndReturnTestString(string newString)
{
testString = newString;
return testString;
}
public string GetTestString()
{
return TestString;
}
}
internal class ExtendedOrderOutput : OrderOutput, IExtendedOrderOutput
{
......@@ -243,6 +359,25 @@ namespace ControlComponents.Core.Tests
{
}
public void TestMethod()
{
throw new NotImplementedException();
}
public void ChangedTestString(string newString)
{
throw new NotImplementedException();
}
public string ChangedAndReturnTestString(string newString)
{
throw new NotImplementedException();
}
public string GetTestString()
{
throw new NotImplementedException();
}
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment