diff --git a/GpioController/Commands/GpioSetCommand.cs b/GpioController/Commands/GpioSetCommand.cs index 2dfd97f..3cb0701 100644 --- a/GpioController/Commands/GpioSetCommand.cs +++ b/GpioController/Commands/GpioSetCommand.cs @@ -25,15 +25,24 @@ protected override void RunOptionalPostCommandLogic(GpioSetRequest request, Gpio { if (request.Options == null) return; + var originalState = request.State; + var sleepTime = request.Options?.Milliseconds ?? 0; for (var timesRepeated = 1; timesRepeated < request.Options?.RepeatTimes * 2; timesRepeated++) { Thread.Sleep(sleepTime); + if (request.CancellationToken.IsCancellationRequested) - break; + { + request.State = originalState; + break; + } + RunOpposite(request); } + + request.State = originalState; } private void RunOpposite(GpioSetRequest request) diff --git a/GpioController/Services/ITokenManagementService.cs b/GpioController/Services/ITokenManagementService.cs index 5906c0a..a48bf67 100644 --- a/GpioController/Services/ITokenManagementService.cs +++ b/GpioController/Services/ITokenManagementService.cs @@ -6,4 +6,5 @@ public interface ITokenManagementService { public void CancelAll(); public CancellationToken CreateToken(GpioSetRequest request); + public void RemoveCompletedTask(GpioSetRequest request); } \ No newline at end of file diff --git a/GpioController/Services/StateService.cs b/GpioController/Services/StateService.cs index 11ac9df..4202844 100644 --- a/GpioController/Services/StateService.cs +++ b/GpioController/Services/StateService.cs @@ -7,8 +7,23 @@ namespace GpioController.Services; -public class StateService(ICommandFactory commandFactory, IGpioService gpioService, ITokenManagementService tokenManagementService) : IStateService +public class StateService : IStateService { + private readonly ICommandFactory commandFactory; + private readonly IGpioService gpioService; + private readonly ITokenManagementService tokenManagementService; + + public Action> ValidateUpdateMultipleStates; + + public StateService(ICommandFactory commandFactory, IGpioService gpioService, ITokenManagementService tokenManagementService) + { + this.commandFactory = commandFactory; + this.gpioService = gpioService; + this.tokenManagementService = tokenManagementService; + + ValidateUpdateMultipleStates = ValidateUpdateRequests; + } + public GpioReadResult GetStateByGpioId(int chipsetId, int gpioId) { gpioService.GetGpioById(chipsetId, gpioId); @@ -22,11 +37,11 @@ public GpioReadResult GetStateByGpioId(int chipsetId, int gpioId) return result; } - + public void UpdateSingleState(GpioSetRequest request) { gpioService.GetGpioById(request.Chipset, request.Gpios.First()); - + request.CancellationToken = tokenManagementService.CreateToken(request); var command = commandFactory.GetCommand(); command.Execute(request); @@ -34,17 +49,16 @@ public void UpdateSingleState(GpioSetRequest request) public void UpdateMultipleStates(IEnumerable updateRequests) { - ValidateUpdateRequests(updateRequests); - + ValidateUpdateMultipleStates(updateRequests); + new Action(() => { foreach (var request in updateRequests) - { + { request.CancellationToken = tokenManagementService.CreateToken(request); var command = commandFactory.GetCommand(); command.Execute(request); - if (request.CancellationToken.IsCancellationRequested) - break; + tokenManagementService.RemoveCompletedTask(request); } }).StartOnBackgroundThread(); } @@ -68,11 +82,11 @@ private void ValidateRequest(IEnumerable gpioIdsToValidate, int chipsetId, private void ValidateIndividualGpio(int chipsetId, int gpioId, string state) { var gpios = gpioService.GetGpios(); - - if(!gpios.Chipset(chipsetId).HasGpio(gpioId)) + + if (!gpios.Chipset(chipsetId).HasGpio(gpioId)) throw new NoGpiosFoundOnChipsetException(gpioId, chipsetId); - - if(!State.CanParse(state)) + + if (!State.CanParse(state)) throw new InvalidStateException(state); } -} \ No newline at end of file +} diff --git a/GpioController/Services/TokenManagementService.cs b/GpioController/Services/TokenManagementService.cs index c3d84c1..61cffb0 100644 --- a/GpioController/Services/TokenManagementService.cs +++ b/GpioController/Services/TokenManagementService.cs @@ -29,6 +29,14 @@ public CancellationToken CreateToken(GpioSetRequest request) return cts.Token; } + public void RemoveCompletedTask(GpioSetRequest request) + { + lock (lockingMechanism) + { + activeTokenSources.RemoveAll(activeTask => activeTask.ActiveRequest == request ); + } + } + public void CancelAll() { lock (lockingMechanism) diff --git a/GpioControllerTests/Services/IntegrationTests.cs b/GpioControllerTests/Services/IntegrationTests.cs new file mode 100644 index 0000000..1a72475 --- /dev/null +++ b/GpioControllerTests/Services/IntegrationTests.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using FakeItEasy; +using FluentAssertions; +using GpioController.Commands; +using GpioController.Commands.Request; +using GpioController.Commands.Results; +using GpioController.Factories; +using GpioController.Parsers; +using GpioController.Services; +using Microsoft.Extensions.Logging; + +namespace GpioControllerTests.Services; + +public class IntegrationTests +{ + [Fact] + public void UpdateMultipleStateionTestShouldShowCorrectPinsTurnOff() + { + + var commandFactory = A.Fake(); + var gpioService = A.Fake(); + var logger = A.Fake>(); + var tokenManagementService = new TokenManagementService(commandFactory, logger); + var stateService = new StateService(commandFactory, gpioService, tokenManagementService); + var parser = A.Fake>(); + var terminalService = A.Fake(); + + var json = """ + [ + {"Gpios":[91,92,81],"Chipset":1,"State":"Low","Options":{"Milliseconds":1000,"RepeatTimes":1}}, + {"Gpios":[91,92,95],"Chipset":1,"State":"Low","Options":{"Milliseconds":1000,"RepeatTimes":1}}, + {"Gpios":[91,92,80],"Chipset":1,"State":"Low","Options":{"Milliseconds":1000,"RepeatTimes":1}} + ] + """; + + var requests = JsonSerializer.Deserialize>(json); + + stateService.ValidateUpdateMultipleStates = (requests) => { }; + + var command = new GpioSetCommand(parser, terminalService); + A.CallTo(() => commandFactory.GetCommand()).ReturnsLazily(() => command); + + stateService.UpdateMultipleStates(requests); + + Thread.Sleep(2500); + + tokenManagementService.CancelAll(); + + var allTerminalCalls = Fake.GetCalls(terminalService) + .Where(call => call.Method.Name == nameof(terminalService.RunCommand)) + .Select(call => call.Arguments[0] as string) + .ToList(); + + allTerminalCalls[0].Should().Be("gpioset 1 91=0;gpioset 1 92=0;gpioset 1 81=0"); + allTerminalCalls[1].Should().Be("gpioset 1 91=1;gpioset 1 92=1;gpioset 1 81=1"); + allTerminalCalls[2].Should().Be("gpioset 1 91=0;gpioset 1 92=0;gpioset 1 95=0"); + allTerminalCalls[3].Should().Be("gpioset 1 91=1;gpioset 1 92=1;gpioset 1 95=1"); + allTerminalCalls[4].Should().Be("gpioset 1 91=0;gpioset 1 92=0;gpioset 1 80=0"); + allTerminalCalls[5].Should().Be("gpioset 1 91=1;gpioset 1 92=1;gpioset 1 80=1"); + } + +} \ No newline at end of file diff --git a/Installation/linux-arm64/pinpanda-api-1.4.deb b/Installation/linux-arm64/pinpanda-api-1.4.deb index a7de0ab..afa3086 100644 Binary files a/Installation/linux-arm64/pinpanda-api-1.4.deb and b/Installation/linux-arm64/pinpanda-api-1.4.deb differ diff --git a/Installation/linux-x64/pinpanda-api-1.4.deb b/Installation/linux-x64/pinpanda-api-1.4.deb index 0b589a0..6f54a90 100644 Binary files a/Installation/linux-x64/pinpanda-api-1.4.deb and b/Installation/linux-x64/pinpanda-api-1.4.deb differ