Add active device count for status API (#169)

* Register SimulationRunner as a singleton type
* Allow DeviceStatusActor report active status
* Add active device count to status API
This commit is contained in:
Hugh Xiong 2018-02-28 14:07:54 -08:00 коммит произвёл Devis Lucato
Родитель a6b63536fe
Коммит d7b1916d60
8 изменённых файлов: 407 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,84 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Simulation;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceState;
using Moq;
using SimulationAgent.Test.helpers;
using System;
using System.Collections.Generic;
using Xunit;
using Xunit.Abstractions;
namespace SimulationAgent.Test.DeviceState
{
public class DeviceStateActorTest
{
private readonly Mock<ILogger> logger;
private readonly Mock<IScriptInterpreter> scriptInterpreter;
private readonly Mock<UpdateDeviceState> updateDeviceStateLogic;
private readonly DeviceStateActor target;
public DeviceStateActorTest(ITestOutputHelper log)
{
this.logger = new Mock<ILogger>();
this.scriptInterpreter = new Mock<IScriptInterpreter>();
this.updateDeviceStateLogic = new Mock<UpdateDeviceState>(
this.scriptInterpreter.Object,
this.logger.Object);
this.target = new DeviceStateActor(
this.logger.Object,
this.updateDeviceStateLogic.Object);
}
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ReportInactiveStatusBeforeRun()
{
// Arrange
SetupDeviceStateActor();
// Act
var result = this.target.IsDeviceActive;
// Assert
Assert.False(result);
}
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ReportActiveStatusAfterRun()
{
// Arrange
SetupDeviceStateActor();
// Act
this.target.Run();
var result = this.target.IsDeviceActive;
// Assert
Assert.True(result);
}
private void SetupDeviceStateActor()
{
string DEVICE_ID = "01";
int postion = 1;
int total = 10;
var deviceModel = new DeviceModel { Id = DEVICE_ID };
var deviceState = new Dictionary<string, object>
{
{ DEVICE_ID, new Object { } }
};
this.scriptInterpreter
.Setup(x => x.Invoke(
It.IsAny<Script>(),
It.IsAny<Dictionary<string, object>>(),
It.IsAny<Dictionary<string, object>>()))
.Returns(deviceState);
this.target.Setup(DEVICE_ID, deviceModel, postion, total);
}
}
}

Просмотреть файл

@ -12,4 +12,8 @@
<PackageReference Include="xunit.runner.console" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Services\Services.csproj" />
<ProjectReference Include="..\SimulationAgent\SimulationAgent.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,179 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Concurrency;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceConnection;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceState;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceTelemetry;
using Moq;
using SimulationAgent.Test.helpers;
using Xunit;
using Xunit.Abstractions;
using static Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models.Simulation;
using SimulationModel = Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models.Simulation;
using SimulationRunner = Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.SimulationRunner;
namespace SimulationAgent.Test
{
public class SimulationRunnerTest
{
private readonly Mock<IRateLimitingConfig> ratingConfig;
private readonly Mock<ILogger> logger;
private readonly Mock<IDeviceModels> deviceModels;
private readonly Mock<IDeviceModelsGeneration> deviceModelsOverriding;
private readonly Mock<IDevices> devices;
private readonly Mock<ISimulations> simulations;
private readonly Mock<IFactory> factory;
private readonly Mock<IDictionary<string, IDeviceStateActor>> deviceStateActors;
private readonly Mock<IDeviceStateActor> deviceStateActor;
private readonly Mock<IDeviceConnectionActor> deviceConnectionActor;
private readonly Mock<IDeviceTelemetryActor> deviceTelemetryActor;
private readonly Mock<UpdateDeviceState> updateDeviceStateLogic;
private readonly SimulationRunner target;
public SimulationRunnerTest(ITestOutputHelper log)
{
this.ratingConfig = new Mock<IRateLimitingConfig>();
this.logger = new Mock<ILogger>();
this.deviceModels = new Mock<IDeviceModels>();
this.deviceModelsOverriding = new Mock<IDeviceModelsGeneration>();
this.devices = new Mock<IDevices>();
this.simulations = new Mock<ISimulations>();
this.factory = new Mock<IFactory>();
this.deviceStateActors = new Mock<IDictionary<string, IDeviceStateActor>>();
this.deviceStateActor = new Mock<IDeviceStateActor>();
this.deviceConnectionActor = new Mock<IDeviceConnectionActor>();
this.deviceTelemetryActor = new Mock<IDeviceTelemetryActor>();
this.updateDeviceStateLogic = new Mock<UpdateDeviceState>();
this.target = new SimulationRunner(
this.ratingConfig.Object,
this.logger.Object,
this.deviceModels.Object,
this.deviceModelsOverriding.Object,
this.devices.Object,
this.simulations.Object,
this.factory.Object);
}
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void TheActiveDevicesCountIsZeroAtStart()
{
// Act
var result = this.target.GetActiveDevicesCount();
// Assert
Assert.Equal(0, result);
}
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public void ItReturnsTheNumberOfActiveDevices()
{
// Arrange
const int ACTIVE_DEVICES_COUNT = 7;
var models = new List<DeviceModelRef>
{
new DeviceModelRef { Id = "01", Count = ACTIVE_DEVICES_COUNT }
};
var simulation = new SimulationModel
{
Id = "1",
Created = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(10)),
Modified = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(10)),
ETag = "ETag0",
Enabled = false,
Version = 1,
DeviceModels = models
};
SetupSimulationReadyToStart();
// Act
this.target.Start(simulation);
var result = this.target.GetActiveDevicesCount();
// Assert
Assert.Equal(ACTIVE_DEVICES_COUNT, result);
}
private void SetupSimulationReadyToStart()
{
this.SetupSimulations();
this.SetUpDeviceModelsOverriding();
this.SetupDevices();
this.SetupDeviceStateActor();
this.SetupDeviceConnectionActor();
this.SetupDeviceTelemetryActor();
this.SetupActiveDeviceStatus();
}
private void SetupActiveDeviceStatus()
{
this.deviceStateActor
.Setup(x => x.IsDeviceActive)
.Returns(true);
}
private void SetupDeviceTelemetryActor()
{
this.factory
.Setup(x => x.Resolve<IDeviceTelemetryActor>())
.Returns(this.deviceTelemetryActor.Object);
}
private void SetupDeviceConnectionActor()
{
this.factory
.Setup(x => x.Resolve<IDeviceConnectionActor>())
.Returns(this.deviceConnectionActor.Object);
}
private void SetupDeviceStateActor()
{
this.factory
.Setup(x => x.Resolve<IDeviceStateActor>())
.Returns(this.deviceStateActor.Object);
}
private void SetupDevices()
{
this.devices
.Setup(x => x.GenerateId(
It.IsAny<string>(),
It.IsAny<int>()))
.Returns("Simulate-01");
}
private void SetUpDeviceModelsOverriding()
{
var deviceModel = new DeviceModel { Id = "01" };
this.deviceModelsOverriding
.Setup(x => x.Generate(
It.IsAny<DeviceModel>(),
It.IsAny<DeviceModelOverride>()))
.Returns(deviceModel);
}
private void SetupSimulations()
{
var deviceIds = new List<string> { "01", "02" };
this.simulations
.Setup(x => x.GetDeviceIds(It.IsAny<Simulation>()))
.Returns(deviceIds);
}
}
}

Просмотреть файл

@ -12,6 +12,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceSt
public interface IDeviceStateActor
{
Dictionary<string, object> DeviceState { get; }
bool IsDeviceActive { get; }
void Setup(string deviceId, DeviceModel deviceModel, int position, int totalDevices);
void Run();
}
@ -32,6 +33,22 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceSt
/// </summary>
public Dictionary<string, object> DeviceState { get; set; }
/// <summary>
/// The device is considered active when the state is being updated.
///
/// By design, rather than talking about "connected devices", we use
/// the term "active devices" which is more generic. So when we show
/// the number of active devices, we can include devices which are not
/// connected yet but being simulated.
/// </summary>
public bool IsDeviceActive
{
get
{
return this.status == ActorStatus.Updating;
}
}
private readonly ILogger log;
private readonly UpdateDeviceState updateDeviceStateLogic;
private string deviceId;

Просмотреть файл

@ -21,6 +21,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
{
void Start(Services.Models.Simulation simulation);
void Stop();
int GetActiveDevicesCount();
}
public class SimulationRunner : ISimulationRunner
@ -230,6 +231,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
}
}
// Method to return the count of active devices
public int GetActiveDevicesCount() => this.deviceStateActors.Count(a => a.Value.IsDeviceActive);
private DeviceModel GetDeviceModel(string id, Services.Models.Simulation.DeviceModelOverride overrides)
{
var modelDef = new DeviceModel();

Просмотреть файл

@ -0,0 +1,97 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.StorageAdapter;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent;
using Moq;
using WebService.Test.helpers;
using Xunit;
using Xunit.Abstractions;
using SimulationModel = Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models.Simulation;
using StatusController = Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controllers.StatusController;
namespace WebService.Test.v1.Controllers
{
public class StatusControllerTest
{
private const string SIMULATION_ID = "1";
private readonly Mock<IPreprovisionedIotHub> preprovisionedIotHub;
private readonly Mock<IStorageAdapterClient> storage;
private readonly Mock<ISimulations> simulations;
private readonly Mock<ILogger> logger;
private readonly Mock<IServicesConfig> servicesConfig;
private readonly Mock<IDeploymentConfig> deploymentConfig;
private readonly Mock<IIotHubConnectionStringManager> connectionStringManager;
private readonly Mock<ISimulationRunner> simulationRunner;
private readonly StatusController target;
public StatusControllerTest(ITestOutputHelper log)
{
this.preprovisionedIotHub = new Mock<IPreprovisionedIotHub>();
this.storage = new Mock<IStorageAdapterClient>();
this.simulations = new Mock<ISimulations>();
this.logger = new Mock<ILogger>();
this.servicesConfig = new Mock<IServicesConfig>();
this.deploymentConfig = new Mock<IDeploymentConfig>();
this.connectionStringManager = new Mock<IIotHubConnectionStringManager>();
this.simulationRunner = new Mock<ISimulationRunner>();
this.target = new StatusController(
this.preprovisionedIotHub.Object,
this.storage.Object,
this.simulations.Object,
this.logger.Object,
this.servicesConfig.Object,
this.deploymentConfig.Object,
this.connectionStringManager.Object,
this.simulationRunner.Object);
}
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
public async Task ItReturnsTheNumberOfActiveDevices()
{
// Arrange
const int ACTIVE_DEVICES_COUNT = 5;
this.SetupSimulationForRunner();
this.simulationRunner
.Setup(x => x.GetActiveDevicesCount())
.Returns(ACTIVE_DEVICES_COUNT);
// Act
var result = await this.target.Get();
// Assert
Assert.Equal(ACTIVE_DEVICES_COUNT.ToString(), result.Properties["ActiveDevicesCount"]);
}
private void SetupSimulationForRunner()
{
var simulation = new SimulationModel
{
Id = SIMULATION_ID,
Created = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(10)),
Modified = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(10)),
ETag = "ETag0",
Enabled = true,
Version = 1
};
var simulations = new List<SimulationModel>
{
simulation
};
this.simulations
.Setup(x => x.GetListAsync())
.ReturnsAsync(simulations);
}
}
}

Просмотреть файл

@ -91,6 +91,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService
builder.RegisterType<Services.Devices>().As<IDevices>().SingleInstance();
builder.RegisterType<RateLimiting>().As<IRateLimiting>().SingleInstance();
// The simulation runner contains the service counters, which are read and
// written by multiple parts of the application, so we need to make sure
// there is only one instance storing that information.
builder.RegisterType<SimulationRunner>().As<ISimulationRunner>().SingleInstance();
// Registrations required by Autofac, these classes implement the same interface
builder.RegisterType<Connect>().As<Connect>();
builder.RegisterType<DeviceTwinTag>().As<DeviceTwinTag>();

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.StorageAdapter;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Filters;
using Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Models;
@ -27,6 +28,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
private const string PREPROVISIONED_IOTHUB_KEY = "PreprovisionedIoTHub";
private const string PREPROVISIONED_IOTHUB_INUSE_KEY = "PreprovisionedIoTHubInUse";
private const string PREPROVISIONED_IOTHUB_METRICS_KEY = "PreprovisionedIoTHubMetricsUrl";
private const string ACTIVE_DEVICES_COUNT_KEY = "ActiveDevicesCount";
private readonly IPreprovisionedIotHub preprovisionedIotHub;
private readonly IStorageAdapterClient storage;
@ -35,6 +37,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
private readonly IServicesConfig servicesConfig;
private readonly IDeploymentConfig deploymentConfig;
private readonly IIotHubConnectionStringManager connectionStringManager;
private readonly ISimulationRunner simulationRunner;
public StatusController(
IPreprovisionedIotHub preprovisionedIotHub,
@ -43,7 +46,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
ILogger logger,
IServicesConfig servicesConfig,
IDeploymentConfig deploymentConfig,
IIotHubConnectionStringManager connectionStringManager)
IIotHubConnectionStringManager connectionStringManager,
ISimulationRunner simulationRunner)
{
this.preprovisionedIotHub = preprovisionedIotHub;
this.storage = storage;
@ -52,6 +56,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
this.servicesConfig = servicesConfig;
this.deploymentConfig = deploymentConfig;
this.connectionStringManager = connectionStringManager;
this.simulationRunner = simulationRunner;
}
[HttpGet]
@ -97,6 +102,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
}
}
// Active devices status
string activeDevicesCount = this.GetActiveDevicesCount(isRunning).ToString();
result.Properties.Add(ACTIVE_DEVICES_COUNT_KEY, activeDevicesCount);
// Prepare status message and response
if (!statusIsOk)
{
@ -219,5 +228,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
$"/resourceGroups/{this.deploymentConfig.AzureResourceGroup}" +
$"/providers/Microsoft.Devices/IotHubs/{this.deploymentConfig.AzureIothubName}/Metrics";
}
private int GetActiveDevicesCount(bool isRunning)
{
if (!isRunning) return 0;
return this.simulationRunner.GetActiveDevicesCount();
}
}
}