Skip to content

Commit

Permalink
better loop
Browse files Browse the repository at this point in the history
  • Loading branch information
melgish committed May 7, 2024
1 parent a307a32 commit cc36115
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 36 deletions.
54 changes: 28 additions & 26 deletions DoorNotifier.Tests/WorkerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ public async Task ExecuteAsync_WhenDoorIsClosed_ShouldNotifyOnce()
var once = true;
_mockSensorClient
.Setup(s => s.GetAsync())
.Returns(() => {
if (once) {
once = false;
return Task.FromResult(SensorClient.OPEN);
}
return Task.FromResult(SensorClient.CLOSED);
.Returns(() =>
{
if (once)
{
once = false;
return Task.FromResult(SensorClient.OPEN);
}
return Task.FromResult(SensorClient.CLOSED);
});


Expand All @@ -57,28 +59,28 @@ public async Task ExecuteAsync_WhenDoorIsClosed_ShouldNotifyOnce()
_mockNotifyClient.Verify(n => n.PostAsync(SensorClient.CLOSED), Times.Once);
}

[Fact]
public async Task ExecuteAsync_WhenDoorIsOpened_ShouldNotifyEveryfter()
{
var worker = new WorkerService(
_fakeLogger,
_mockSensorClient.Object,
_mockNotifyClient.Object
);
[Fact]
public async Task ExecuteAsync_WhenDoorIsOpened_ShouldNotifyEveryfter()
{
var worker = new WorkerService(
_fakeLogger,
_mockSensorClient.Object,
_mockNotifyClient.Object
);

// Door starts open, and stays that way.
_mockSensorClient
.Setup(s => s.GetAsync())
.ReturnsAsync(SensorClient.OPEN);
// Door starts open, and stays that way.
_mockSensorClient
.Setup(s => s.GetAsync())
.ReturnsAsync(SensorClient.OPEN);


await worker.StartAsync(CancellationToken.None);
await Task.Delay(TimeSpan.FromSeconds(1));
await worker.StopAsync(CancellationToken.None);
await worker.StartAsync(CancellationToken.None);
await Task.Delay(TimeSpan.FromSeconds(1));
await worker.StopAsync(CancellationToken.None);

// Assert
// 7 is enough to cover 'After'
_mockSensorClient.Verify(s => s.GetAsync(), Times.AtLeast(7));
_mockNotifyClient.Verify(n => n.PostAsync(SensorClient.OPEN), Times.AtLeast(2));
}
// Assert
// 7 is enough to cover 'After'
_mockSensorClient.Verify(s => s.GetAsync(), Times.AtLeast(7));
_mockNotifyClient.Verify(n => n.PostAsync(SensorClient.OPEN), Times.AtLeast(2));
}
}
34 changes: 24 additions & 10 deletions DoorNotifier/WorkerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class WorkerService : BackgroundService
private readonly ILogger<WorkerService> _logger;
private readonly ISensorClient _sensorClient;
private readonly INotifyClient _notifyClient;
private readonly PeriodicTimer _timer;

private bool _wasClosed;
private DateTime _lastChange;
Expand All @@ -21,6 +22,21 @@ INotifyClient notifyClient
_logger = logger;
_sensorClient = sensorClient;
_notifyClient = notifyClient;
_timer = new PeriodicTimer(_sensorClient.Interval);
}

public override void Dispose()
{
_timer.Dispose();
base.Dispose();
}

private async Task SetInitialValuesAsync()
{
// Before going into the loop, load initial values.
var payload = await _sensorClient.GetAsync();
_lastChange = DateTime.UtcNow;
_wasClosed = payload == SensorClient.CLOSED;
}

private async Task CheckDoorStateAsync()
Expand All @@ -32,17 +48,19 @@ private async Task CheckDoorStateAsync()
var isClosed = payload == SensorClient.CLOSED;
var isChange = _wasClosed != isClosed;

if (isChange) {
if (isChange)
{
_lastChange = now;
_wasClosed = isClosed;
// Notify on every change...
_logger.LogDebug(LogEvent.DoorChanged, "Door state has changed to {Description}", payload);
await _notifyClient.PostAsync(payload);
}
}

var elapsed = now - _lastChange;
_logger.LogInformation(LogEvent.DoorState, "Door state {Description} {Elapsed:g}", payload, elapsed);
if (!_wasClosed && elapsed > _notifyClient.After) {
if (!_wasClosed && elapsed > _notifyClient.After)
{
// Notify again when it has been open for a while.
_logger.LogDebug(LogEvent.DoorLeftOpen, "Door was left open");
// Restart the clock to notify again after another hour.
Expand All @@ -56,15 +74,11 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
_logger.LogInformation(LogEvent.PollingStarted, "Polling {Description:g}", _sensorClient.Interval);
try
{
// Before going into the loop, load initial values.
var payload = await _sensorClient.GetAsync();
_lastChange = DateTime.UtcNow;
_wasClosed = payload == SensorClient.CLOSED;

using var timer = new PeriodicTimer(_sensorClient.Interval);
while (await timer.WaitForNextTickAsync(stoppingToken))
await SetInitialValuesAsync();
while (!stoppingToken.IsCancellationRequested)
{
await CheckDoorStateAsync();
await _timer.WaitForNextTickAsync(stoppingToken);
}
}
catch (OperationCanceledException)
Expand Down

0 comments on commit cc36115

Please # to comment.