Skip to content
Snippets Groups Projects
Commit 94afc968 authored by Tobias's avatar Tobias
Browse files

Added FrameControlComponent and updated ConfigureOperationMode

parent 6827343c
No related branches found
No related tags found
No related merge requests found
......@@ -17,37 +17,55 @@ namespace ControlComponents.Core
protected override void Selected()
{
// Check if all necessary outputs are available
if(!_externalCC.Roles.All(role => this.outputs.Keys.Contains(role) && this.outputs[role].IsSet))
if (AllNeededOutputsAreSet())
{
ConfigureOutputsAtExternalCC();
// By selecting the ConfigOperationMode, simply the same OperationMode is selected on the external cc
externalOperationMode = _externalCC.SelectOperationMode(OpModeName);
_externalCC.ExecutionStateChanged += ExecutionStateChanged;
}
else
{
base.execution.SetState(ExecutionState.ABORTING);
}
// configure all outputs to be outputs at external opmode as well
}
protected override async Task Deselected()
{
_externalCC.ExecutionStateChanged -= ExecutionStateChanged;
// No need to call DeselectOperationMode, because all outputs are deselected if this opmode is deselected
await externalOperationMode;
}
private bool AllNeededOutputsAreSet()
{
return _externalCC.Roles.All(role => this.outputs.Keys.Contains(role) && this.outputs[role].IsSet);
}
private void ConfigureOutputsAtExternalCC()
{
foreach (var role in _externalCC.Roles)
{
_externalCC.ChangeOutput(role, this.outputs[role].ComponentName);
}
// By selecting the ConfigOperationMode, simply the same OperationMode is selected on the external cc
externalOperationMode = _externalCC.SelectOperationMode(OpModeName);
}
protected override async Task Deselected()
private void ExecutionStateChanged(object sender, ExecutionStateEventArgs e)
{
await _externalCC.DeselectOperationMode();
await externalOperationMode;
base.execution.SetState(e.ExecutionState);
}
protected override async Task Resetting(CancellationToken token)
{
await _externalCC.ResetAndWaitForIdle(base.execution.ComponentName);
_externalCC.Reset(base.execution.ComponentName);
await MirrorState(token);
await base.Resetting(token);
}
protected override async Task Starting(CancellationToken token)
{
await _externalCC.StartAndWaitForExecute(base.execution.ComponentName);
_externalCC.Start(base.execution.ComponentName);
await MirrorState(token);
await base.Starting(token);
}
......@@ -65,24 +83,27 @@ namespace ControlComponents.Core
protected override async Task Stopping(CancellationToken token)
{
await _externalCC.StopAndWaitForStopped(base.execution.ComponentName);
_externalCC.Stop(base.execution.ComponentName);
await MirrorState(token);
await base.Stopping(token);
}
protected override async Task Clearing(CancellationToken token)
{
_externalCC.Clear(base.execution.ComponentName);
await MirrorState(token);
await base.Clearing(token);
}
protected override async Task Aborting(CancellationToken token)
{
await MirrorState(token);
await base.Aborting(token);
}
private async Task MirrorState(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// TODO might be too slow if network is involved => subscription
if(_externalCC.EXST != base.execution.EXST)
{
base.execution.SetState(_externalCC.EXST);
}
else
{
await Task.Delay(1);
}
}
await Task.Delay(Timeout.Infinite, token).ContinueWith(task => { });
}
}
}
\ No newline at end of file
namespace ControlComponents.Core
{
public class FrameControlComponent : ControlComponent
{
// This component includes the external deployed operation mode
private readonly IControlComponent externalControlComponent;
public FrameControlComponent(IControlComponent cc, IControlComponentProvider provider, string name = "FrameControlComponent") : base(name)
{
this.externalControlComponent = cc;
// (TODO) create ExternalOpmodeOutput to inject it into opmode
var output = new OrderOutput("ExternalOperationMode", ComponentName, provider, cc);
AddOrderOutput(output);
foreach (var operationModeName in cc.OpModes)
{
AddOperationMode(new ConfigOperationMode(operationModeName, output));
}
}
}
}
\ No newline at end of file
......@@ -20,6 +20,20 @@ namespace ControlComponents.Core
cc.ExecutionStateChanged -= waiter.EventHandler;
}
public static async Task WaitForAborted(this IControlComponent cc, int delay = 1000)
{
StateWaiter waiter = new StateWaiter();
cc.ExecutionStateChanged += waiter.EventHandler;
if (cc.EXST != ExecutionState.ABORTED)
{
await waiter.Aborted(delay);
}
cc.ExecutionStateChanged -= waiter.EventHandler;
}
public static async Task ResetAndWaitForIdle(this IControlComponent cc, string occupier)
{
if (cc.EXST == ExecutionState.STOPPED || cc.EXST == ExecutionState.COMPLETED || cc.EXST == ExecutionState.RESETTING)
......
......@@ -118,7 +118,6 @@ namespace ControlComponents.Core
}
finally
{
// TOBI TODO test that output opmodes are deselected
// TODO what should happen with not STOPPED outputs, that are ABORTED for example?
foreach (var output in outputs.Values.Where(o => o.IsSet && o.OpModeName != "NONE" && o.EXST == ExecutionState.STOPPED))
{
......
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Moq;
using NLog;
using NUnit.Framework;
namespace ControlComponents.Core.Tests
{
public class ConfigOperationModeTests
{
OrderOutput orderOutput;
string CC = "CC";
string SENDER = "SENDER";
string OpModeName = "CONFIGURE";
[OneTimeSetUp]
public void OneTimeSetUp(){
var config = new NLog.Config.LoggingConfiguration();
// Targets where to log to: Console
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
// Rules for mapping loggers to targets
config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole);
// Apply config
NLog.LogManager.Configuration = config;
}
[SetUp]
public void Setup()
{
Mock<IControlComponentProvider> provider = new Mock<IControlComponentProvider>();
var OpModes = new Collection<IOperationMode>() { new OperationMode("OpModeOne"), new OperationMode("OpModeTwo") };
orderOutput = new OrderOutput("First", "Output", provider.Object, new ControlComponent(CC, OpModes, new Collection<IOrderOutput>(), new Collection<string>()));
}
[Test]
public async Task Given_ExternalCC_When_NormalRun_Then_CorrectStateFlow()
{
ControlComponent externalCC = new ControlComponent("CC1");
OperationMode externalOpmode = new OperationModeAsync(OpModeName);
externalCC.AddOperationMode(externalOpmode);
ConfigOperationMode configureOpmode = new ConfigOperationMode(OpModeName, externalCC);
ControlComponent configureCC = new ControlComponent("CC2");
configureCC.AddOperationMode(configureOpmode);
Task running = configureCC.SelectOperationMode(OpModeName);
Assert.AreEqual(OpModeName, configureCC.OpModeName);
Assert.AreEqual(OpModeName, externalCC.OpModeName);
await configureCC.ResetAndWaitForIdle(SENDER);
Assert.AreEqual(ExecutionState.IDLE, configureCC.EXST);
Assert.AreEqual(ExecutionState.IDLE, externalCC.EXST);
await configureCC.StartAndWaitForExecute(SENDER);
Assert.AreEqual(ExecutionState.EXECUTE, configureCC.EXST);
Assert.AreEqual(ExecutionState.EXECUTE, externalCC.EXST);
await configureCC.WaitForCompleted();
Assert.AreEqual(ExecutionState.COMPLETED, configureCC.EXST);
Assert.AreEqual(ExecutionState.COMPLETED, externalCC.EXST);
await configureCC.StopAndWaitForStopped(SENDER);
Assert.AreEqual(ExecutionState.STOPPED, configureCC.EXST);
Assert.AreEqual(ExecutionState.STOPPED, externalCC.EXST);
await configureCC.DeselectOperationMode();
await running;
Assert.AreEqual("NONE", configureCC.OpModeName);
Assert.AreEqual("NONE", externalCC.OpModeName);
}
}
}
\ No newline at end of file
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NLog;
using NUnit.Framework;
namespace ControlComponents.Core.Tests
{
public class FrameControlComponentTests
{
string CC = "CC";
string SENDER = "SENDER";
string NormalOpMode = "CONFIGURE";
string FailingOpModeReset = "FAILING-Reset";
string FailingOpModeStart = "FAILING-Start";
string FailingOpModeExecute = "FAILING-Execute";
ControlComponent externalCC;
FrameControlComponent sut;
Task running;
[OneTimeSetUp]
public void OneTimeSetUp(){
var config = new NLog.Config.LoggingConfiguration();
// Targets where to log to: Console
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
// Rules for mapping loggers to targets
config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole);
// Apply config
NLog.LogManager.Configuration = config;
}
[SetUp]
public void Setup()
{
Mock<IControlComponentProvider> provider = new Mock<IControlComponentProvider>();
// external controlcomponent has different opmodes to test with
externalCC = new ControlComponent(CC);
externalCC.AddOperationMode(new FailingOperationModeReset(FailingOpModeReset));
externalCC.AddOperationMode(new FailingOperationModeStart(FailingOpModeStart));
externalCC.AddOperationMode(new FailingOperationModeExecute(FailingOpModeExecute));
externalCC.AddOperationMode(new OperationModeAsync(NormalOpMode));
sut = new FrameControlComponent(externalCC, provider.Object);
}
[TearDown]
public async Task TearDown()
{
await sut.StopAndWaitForStopped(SENDER);
sut.EXST.Should().Be(ExecutionState.STOPPED);
externalCC.EXST.Should().Be(ExecutionState.STOPPED);
await sut.DeselectOperationMode();
await running;
sut.OpModeName.Should().Be("NONE");
externalCC.OpModeName.Should().Be("NONE");
}
[Test]
public async Task Given_ExternalCC_When_NormalRun_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.WaitForCompleted();
sut.EXST.Should().Be(ExecutionState.COMPLETED);
externalCC.EXST.Should().Be(ExecutionState.COMPLETED);
}
[Test]
public async Task Given_ExternalOpMode_When_ResetFails_Then_Aborted()
{
running = sut.SelectOperationMode(FailingOpModeReset);
sut.Reset(SENDER);
await sut.WaitForAborted();
externalCC.EXST.Should().Be(ExecutionState.ABORTED);
sut.EXST.Should().Be(ExecutionState.ABORTED);
}
[Test]
public async Task Given_ExternalOpMode_When_StartFails_Then_Aborted()
{
running = sut.SelectOperationMode(FailingOpModeStart);
await sut.ResetAndWaitForIdle(SENDER);
sut.EXST.Should().Be(ExecutionState.IDLE);
externalCC.EXST.Should().Be(ExecutionState.IDLE);
sut.Start(SENDER);
await sut.WaitForAborted();
externalCC.EXST.Should().Be(ExecutionState.ABORTED);
sut.EXST.Should().Be(ExecutionState.ABORTED);
}
[Test]
public async Task Given_ExternalOpMode_When_ExecuteFails_Then_Aborted()
{
running = sut.SelectOperationMode(FailingOpModeExecute);
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.WaitForAborted();
externalCC.EXST.Should().Be(ExecutionState.ABORTED);
sut.EXST.Should().Be(ExecutionState.ABORTED);
}
}
}
\ No newline at end of file
using System.Threading;
using System.Threading.Tasks;
namespace ControlComponents.Core.Tests
{
internal class FailingOperationModeReset : OperationModeBase
{
public FailingOperationModeReset(string name) : base(name)
{
}
protected override Task Resetting(CancellationToken token)
{
base.execution.SetState(ExecutionState.ABORTING);
return base.Resetting(token);
}
}
internal class FailingOperationModeStart : OperationModeBase
{
public FailingOperationModeStart(string name) : base(name)
{
}
protected override Task Starting(CancellationToken token)
{
base.execution.SetState(ExecutionState.ABORTING);
return base.Starting(token);
}
}
internal class FailingOperationModeExecute : OperationModeBase
{
public FailingOperationModeExecute(string name) : base(name)
{
}
protected override Task Execute(CancellationToken token)
{
base.execution.SetState(ExecutionState.ABORTING);
return base.Execute(token);
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace ControlComponents.Core.Tests
......
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