This repository has been archived by the owner on Oct 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Integrate and merge replay feature to master (#338)
* Create replay thread (#325) * Create empty replay thread * Remove connection using * Remove references to connections in the replay task * Update name of replay task test * Move const to config file * Add device replay actors (#329) * Create empty replay thread * Remove connection using * Remove references to connections in the replay task * Update name of replay task test * Add device replay actors * Read replay file and stream telemetry (#334) * update * read replay file from storage * replay file settings * update * update * update validation * PR comments * fix test
- Loading branch information
1 parent
942b2d5
commit 8a31fcf
Showing
18 changed files
with
666 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"SchemaVersion": "1.0.0", | ||
"Id": "replay", | ||
"Version": "0.0.1", | ||
"Name": "Replay", | ||
"Description": "Fake device model for csv file replay", | ||
"Protocol": "AMQP", | ||
"ReplayFile": "", | ||
"Simulation": { | ||
"InitialState": { | ||
"online": true | ||
}, | ||
"Interval": "00:00:00", | ||
"Scripts": [ | ||
] | ||
}, | ||
"Properties": { | ||
}, | ||
"Tags": { | ||
}, | ||
"Telemetry": [ | ||
{ | ||
"MessageTemplate": "", | ||
"MessageSchema": { | ||
"Name": "replay-sensors;v1", | ||
"Format": "JSON", | ||
"Fields": { | ||
} | ||
} | ||
} | ||
], | ||
"CloudToDeviceMethods": { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
telemetry, 00:00:00,{"temperature": 51.32, "temperature_unit": "fahrenheit", "humidity": 69.59, "humidity_unit":"RH", "pressure": 440.20, "pressure_unit": "psi"} | ||
telemetry, 00:00:30,{"temperature": 71.32, "temperature_unit": "fahrenheit", "humidity": 79.59, "humidity_unit":"RH", "pressure": 240.20, "pressure_unit": "psi"} | ||
telemetry, 00:01:00,{"temperature": 30.00, "temperature_unit": "fahrenheit", "humidity": 59.59, "humidity_unit":"RH", "pressure": 340.20, "pressure_unit": "psi"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
SimulationAgent.Test/SimulationThreads/DeviceReplayTaskTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using System.Collections.Concurrent; | ||
using System.Threading; | ||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Concurrency; | ||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics; | ||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent; | ||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceReplay; | ||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.SimulationThreads; | ||
using Moq; | ||
using Xunit; | ||
using System.Threading.Tasks; | ||
|
||
namespace SimulationAgent.Test.SimulationThreads | ||
{ | ||
public class DeviceReplayTaskTest | ||
{ | ||
private const int NUM_ACTORS = 9; | ||
private const int MAX_PENDING_TASKS = 5; | ||
|
||
private readonly Mock<IAppConcurrencyConfig> mockAppConcurrencyConfig; | ||
private readonly Mock<ILogger> mockLogger; | ||
private readonly DeviceReplayTask target; | ||
private readonly ConcurrentDictionary<string, Mock<IDeviceReplayActor>> mockDeviceReplayActors; | ||
private readonly ConcurrentDictionary<string, IDeviceReplayActor> mockDeviceReplayActorObjects; | ||
private readonly ConcurrentDictionary<string, Mock<ISimulationManager>> mockSimulationManagers; | ||
private readonly ConcurrentDictionary<string, ISimulationManager> mockSimulationManagerObjects; | ||
|
||
public DeviceReplayTaskTest() | ||
{ | ||
this.mockDeviceReplayActors = new ConcurrentDictionary<string, Mock<IDeviceReplayActor>>(); | ||
this.mockDeviceReplayActorObjects = new ConcurrentDictionary<string, IDeviceReplayActor>(); | ||
this.mockSimulationManagers = new ConcurrentDictionary<string, Mock<ISimulationManager>>(); | ||
this.mockSimulationManagerObjects = new ConcurrentDictionary<string, ISimulationManager>(); | ||
|
||
this.mockAppConcurrencyConfig = new Mock<IAppConcurrencyConfig>(); | ||
this.mockAppConcurrencyConfig.SetupGet(x => x.MaxPendingTasks).Returns(MAX_PENDING_TASKS); | ||
this.mockLogger = new Mock<ILogger>(); | ||
|
||
this.target = new DeviceReplayTask(this.mockAppConcurrencyConfig.Object, this.mockLogger.Object); | ||
} | ||
|
||
[Fact] | ||
public void ItCallsRunAsyncOnAllReplayActors() | ||
{ | ||
// Arrange | ||
var cancellationToken = new CancellationTokenSource(); | ||
|
||
this.BuildMockDeviceReplayActors( | ||
this.mockDeviceReplayActors, | ||
this.mockDeviceReplayActorObjects, | ||
cancellationToken, | ||
NUM_ACTORS); | ||
|
||
// Build a list of SimulationManagers | ||
this.BuildMockSimluationManagers( | ||
this.mockSimulationManagers, | ||
this.mockSimulationManagerObjects, | ||
cancellationToken, | ||
NUM_ACTORS); | ||
|
||
// Act | ||
// Act on the target. The cancellation token will be cancelled through | ||
// a callback that will be triggered when each device-replay actor | ||
// is called. | ||
var targetTask = this.target.RunAsync( | ||
this.mockSimulationManagerObjects, | ||
this.mockDeviceReplayActorObjects, | ||
cancellationToken.Token); | ||
|
||
// Assert | ||
// Verify that each SimulationManager was called at least once | ||
foreach (var actor in this.mockDeviceReplayActors) | ||
actor.Value.Verify(x => x.HasWorkToDo(), Times.Once); | ||
} | ||
|
||
private void BuildMockDeviceReplayActors( | ||
ConcurrentDictionary<string, Mock<IDeviceReplayActor>> mockDictionary, | ||
ConcurrentDictionary<string, IDeviceReplayActor> objectDictionary, | ||
CancellationTokenSource cancellationToken, | ||
int count) | ||
{ | ||
mockDictionary.Clear(); | ||
objectDictionary.Clear(); | ||
|
||
for (int i = 0; i < count; i++) | ||
{ | ||
var deviceName = $"device_{i}"; | ||
var mockDeviceReplayActor = new Mock<IDeviceReplayActor>(); | ||
|
||
// Have each DeviceReplayActor report that it has work to do | ||
mockDeviceReplayActor.Setup(x => x.HasWorkToDo()).Returns(true); | ||
mockDeviceReplayActor.Setup(x => x.RunAsync()).Returns(Task.CompletedTask) | ||
.Callback(() => { cancellationToken.Cancel(); }); | ||
|
||
mockDictionary.TryAdd(deviceName, mockDeviceReplayActor); | ||
objectDictionary.TryAdd(deviceName, mockDeviceReplayActor.Object); | ||
} | ||
} | ||
|
||
/* | ||
* Creating two collections: one for the mocks, and another to store the | ||
* mock objects. If we only created one collection and populated it with | ||
* the mock objects, we wouldn't have a reference to the backing mock for | ||
* each. | ||
*/ | ||
private void BuildMockSimluationManagers( | ||
ConcurrentDictionary<string, Mock<ISimulationManager>> mockSimulationManagers, | ||
ConcurrentDictionary<string, ISimulationManager> mockSimulationManagerObjects, | ||
CancellationTokenSource cancellationToken, | ||
int count) | ||
{ | ||
mockSimulationManagers.Clear(); | ||
mockSimulationManagerObjects.Clear(); | ||
|
||
for (int i = 0; i < count; i++) | ||
{ | ||
var deviceName = $"simulation_{i}"; | ||
var mockSimulationManager = new Mock<ISimulationManager>(); | ||
|
||
// We only want the main loop in the target to run once, so here we'll | ||
// trigger a callback which will cancel the cancellation token that | ||
// the main loop uses. | ||
mockSimulationManager.Setup(x => x.NewConnectionLoop()) | ||
.Callback(() => cancellationToken.Cancel()); | ||
|
||
mockSimulationManagers.TryAdd(deviceName, mockSimulationManager); | ||
mockSimulationManagerObjects.TryAdd(deviceName, mockSimulationManager.Object); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.