diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleRampUp.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleRampUp.cs new file mode 100644 index 0000000..0be2091 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleRampUp.cs @@ -0,0 +1,50 @@ +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp; +using SharpBrick.PoweredUp.Functions; + +namespace Example +{ + public class ExampleRampUp : BaseExample + { + public override async Task ExecuteAsync() + { + using (var technicMediumHub = Host.FindByType()) + { + var stopWatch = new Stopwatch(); + + var motor = technicMediumHub.A.GetDevice(); + + // ramp up with linear speed + var rampUp = ServiceProvider.GetService(); + + await technicMediumHub.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Red); + + stopWatch.Start(); + await rampUp.ExecuteAsync(motor, 20, 100, 40, 10_000); + var redPhase = stopWatch.ElapsedMilliseconds; + + await technicMediumHub.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Green); + + await Task.Delay(2_000); + + await technicMediumHub.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Orange); + + // ramp down with linear speed + var rampDown = ServiceProvider.GetService(); + + var beforeOrangePhase = stopWatch.ElapsedMilliseconds; + await rampDown.ExecuteAsync(motor, 100, 0, 100, 20_000); + var orangePhase = stopWatch.ElapsedMilliseconds - beforeOrangePhase; + stopWatch.Stop(); + + await technicMediumHub.SwitchOffAsync(); + + // time delays (parameter) + 100s of BLE messages async/await ops + Log.LogInformation($"Red Phase: {redPhase}ms; Orange Phase: {orangePhase}ms"); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/Program.cs b/examples/SharpBrick.PoweredUp.Examples/Program.cs index 92c0efe..79f9109 100644 --- a/examples/SharpBrick.PoweredUp.Examples/Program.cs +++ b/examples/SharpBrick.PoweredUp.Examples/Program.cs @@ -33,6 +33,7 @@ static async Task Main(string[] args) //example = new Example.ExampleHubPropertyObserving(); //example = new Example.ExampleDiscoverByType(); //example = new Example.ExampleCalibrationSteering(); + example = new Example.ExampleRampUp(); //example = new Example.ExampleTechnicMediumHubGestSensor(); //example = new Example.ExampleRemoteControlButton(); //example = new Example.ExampleRemoteControlRssi(); @@ -40,7 +41,7 @@ static async Task Main(string[] args) //example = new Example.ExampleMarioBarcode(); //example = new Example.ExampleMarioPants(); //example = new Example.ExampleMarioAccelerometer(); - example = new Example.ExampleDuploTrainBase(); + //example = new Example.ExampleDuploTrainBase(); // NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand. await example.InitHostAndDiscoverAsync(enableTrace); diff --git a/src/SharpBrick.PoweredUp/Functions/IterativeChange.cs b/src/SharpBrick.PoweredUp/Functions/IterativeChange.cs new file mode 100644 index 0000000..f1f3dee --- /dev/null +++ b/src/SharpBrick.PoweredUp/Functions/IterativeChange.cs @@ -0,0 +1,72 @@ +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace SharpBrick.PoweredUp.Functions +{ + public abstract class IterativeChange + { + private readonly ILogger> _logger; + private readonly Stopwatch _stopwatch; + + protected abstract T Function(int idx); + protected abstract Task ChangeAsync(T value, CancellationTokenSource cts); + + public IterativeChange(ILogger> logger) + { + _logger = logger; + _stopwatch = new Stopwatch(); + } + + protected async Task IterativeExecuteAsync(int iterations, int delayInMilliseconds, CancellationTokenSource cts = default) + { + _logger.LogInformation($"Start iterative change over {iterations} steps"); + _stopwatch.Start(); + + for (int idx = 0; idx < iterations; idx++) + { + _logger.LogInformation($"+ Iteration: {idx}"); + var startTime = _stopwatch.ElapsedMilliseconds; + if (cts?.IsCancellationRequested ?? false) + { + _logger.LogInformation("+ Cancelled (before Actor)"); + + break; + } + + var value = Function(idx); + + _logger.LogInformation($"+ Value: {value}"); + + await ChangeAsync(value, cts); + + if (cts?.IsCancellationRequested ?? false) + { + _logger.LogInformation("+ Cancelled (after Actor)"); + + break; + } + var endTime = _stopwatch.ElapsedMilliseconds; + + var currentDuration = endTime - startTime; + var waiting = delayInMilliseconds - currentDuration; + + _logger.LogInformation($"+ Waiting {delayInMilliseconds}ms ({currentDuration} already elapsed; {waiting} to go; walltime: {endTime})"); + + if (waiting > 0 && waiting <= int.MaxValue) + { + await Task.Delay((int)waiting); + } + else + { + _logger.LogError($"+ IO Time exceeded Waiting Time. Reducing Step Count or Increasing Delay Time might help."); + } + } + + _stopwatch.Stop(); + + _logger.LogInformation("Finished iterative change"); + } + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Functions/LinearSpeedChange.cs b/src/SharpBrick.PoweredUp/Functions/LinearSpeedChange.cs new file mode 100644 index 0000000..1489a68 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Functions/LinearSpeedChange.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace SharpBrick.PoweredUp.Functions +{ + public class LinearSpeedChange : IterativeChange + { + private readonly ILogger _logger; + private TachoMotor _motor; + private sbyte _startSpeed; + + public byte MaxPower { get; set; } = 100; + + public sbyte IterationStep { get; private set; } + + public LinearSpeedChange(ILogger logger) + : base(logger) + { + _logger = logger; + } + + public async Task ExecuteAsync(TachoMotor motor, sbyte startSpeed, sbyte endSpeed, int steps, int milliseconds, CancellationTokenSource cts = default) + { + _logger.LogInformation($"Start execute {nameof(LinearSpeedChange)} ({startSpeed} - {endSpeed} over {steps} steps and {milliseconds}ms)"); + _startSpeed = startSpeed; + _motor = motor ?? throw new ArgumentNullException(nameof(motor)); + + IterationStep = (sbyte)((endSpeed - startSpeed) / steps); + _logger.LogInformation($"+ IterationStep: {IterationStep}"); + + await IterativeExecuteAsync(steps, milliseconds / steps, cts); + + _logger.LogInformation($"Finished {nameof(LinearSpeedChange)} "); + } + + protected override sbyte Function(int idx) + => (sbyte)(_startSpeed + IterationStep * (idx + 1)); + + protected override Task ChangeAsync(sbyte value, CancellationTokenSource cts) + => _motor.StartSpeedAsync(value, MaxPower); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs index f96bff4..cec1e53 100644 --- a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs +++ b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs @@ -30,6 +30,8 @@ public static IServiceCollection AddPoweredUp(this IServiceCollection self) // functions .AddTransient() .AddTransient() - .AddTransient(); + .AddTransient() + .AddTransient() + ; } } \ No newline at end of file