Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to variable sequences and file based configuration #32

Merged
merged 7 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 62 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ A single AMQP connection can handle approximately 995 devices.
The quickest way to generate telemetry is using Docker with the following command:

```bash
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator:latest
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator
```

**The simulator expects the devices to already exist in Azure IoT Hub**. If you need help creating simulation devices in an Azure IoT Hub use the included project IotSimulatorDeviceProvisioning or the Docker image:

```bash
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=registryReadWrite;SharedAccessKey=your-iothub-key" -e DeviceCount=1000 mcr.microsoft.com/oss/azure-samples/azureiot-simulatordeviceprovisioning:latest
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=registryReadWrite;SharedAccessKey=your-iothub-key" -e DeviceCount=1000 mcr.microsoft.com/oss/azure-samples/azureiot-simulatordeviceprovisioning
```

## Simulator input parameters
Expand All @@ -60,6 +60,8 @@ The amount of devices, their names and telemetry generated can be customized usi
|PartitionKey|optional [partition key](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-features#partitions) template for Event Hubs (see telemetry template and [Advanced options](#advanced-options))|
|Variables|telemetry variables (see telemetry template)|
|DuplicateEveryNEvents|if > 0, send duplicates of the given fraction of messages. See [Advanced options](#advanced-options) (default = 0)|
|File|Defines a json file where templates, variables and device based intervals can be defined. File and environment variable configuration can be used in conjunction.|
|Intervals|Allows customizing the message interval per device|

## Telemetry template

Expand Down Expand Up @@ -99,6 +101,8 @@ Customizable variables can be created with the following properties:
|max|The maximum value generated|
|values|Defines an array of possible values. Example ["on", "off"]|
|customlengthstring|Creates a random string of n bytes. Provide n as parameter|
|sequence|Create a sequence of values as defined in `values` property, producing one after the other. Values can reference other non-sequence variables|


#### Example 1: Telemetry with temperature between 23 and 25 and a counter starting from 100

Expand All @@ -125,13 +129,13 @@ Output:
Running with Docker:

```powershell
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" -e Template="{ \"deviceId\": \"$.DeviceId\", \"temp\": $.Temp, \"Ticks\": $.Ticks, \"Counter\": $.Counter, \"time\": \"$.Time\" }" -e Variables="[{name: \"Temp\", \"random\": true, \"max\": 25, \"min\": 23}, {\"name\":\"Counter\", \"min\":100} ]" mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator:latest
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" -e Template="{ \"deviceId\": \"$.DeviceId\", \"temp\": $.Temp, \"Ticks\": $.Ticks, \"Counter\": $.Counter, \"time\": \"$.Time\" }" -e Variables="[{name: \"Temp\", \"random\": true, \"max\": 25, \"min\": 23}, {\"name\":\"Counter\", \"min\":100} ]" mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator
```

calling from PowerShell:

```powershell
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=your-iothub-key" -e Template="{ \"""deviceId\""": \"""$.DeviceId\""", \"""temp\""": $.Temp, \"""Ticks\""": $.Ticks, \"""Counter\""": $.Counter, \"""time\""": \"""$.Time\""", \"""engine\""": \"""$.Engine\""" }" -e Variables="[{name: \"""Temp\""", \"""random\""": true, \"""max\""": 25, \"""min\""": 23}, {\"""name\""":\"""Counter\""", \"""min\""":100}, {name:\"""Engine\""", values: [\"""on\""", \"""off\"""]}]" -e DeviceCount=1 -e MessageCount=3 mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator:latest
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=your-iothub-key" -e Template="{ \"""deviceId\""": \"""$.DeviceId\""", \"""temp\""": $.Temp, \"""Ticks\""": $.Ticks, \"""Counter\""": $.Counter, \"""time\""": \"""$.Time\""", \"""engine\""": \"""$.Engine\""" }" -e Variables="[{name: \"""Temp\""", \"""random\""": true, \"""max\""": 25, \"""min\""": 23}, {\"""name\""":\"""Counter\""", \"""min\""":100}, {name:\"""Engine\""", values: [\"""on\""", \"""off\"""]}]" -e DeviceCount=1 -e MessageCount=3 mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator
```

#### Example 2: Adding the engine status ("on" or "off") to the telemetry
Expand All @@ -158,7 +162,60 @@ Output:
Running with Docker:

```bash
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" -e Template="{ \"deviceId\": \"$.DeviceId\", \"temp\": $.Temp, \"Ticks\": $.Ticks, \"Counter\": $.Counter, \"time\": \"$.Time\", \"engine\": \"$.Engine\" }" -e Variables="[{name: \"Temp\", \"random\": true, \"max\": 25, \"min\": 23}, {\"name\":\"Counter\", \"min\":100}, {name:\"Engine\", values: [\"on\", \"off\"]}]" mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator:latest
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" -e Template="{ \"deviceId\": \"$.DeviceId\", \"temp\": $.Temp, \"Ticks\": $.Ticks, \"Counter\": $.Counter, \"time\": \"$.Time\", \"engine\": \"$.Engine\" }" -e Variables="[{name: \"Temp\", \"random\": true, \"max\": 25, \"min\": 23}, {\"name\":\"Counter\", \"min\":100}, {name:\"Engine\", values: [\"on\", \"off\"]}]" mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator
```

#### Example 3: Using a configuration file to customize simulation

```bash
docker run -it -e "IotHubConnectionString=HostName=your-iothub-name.azure-devices.net;SharedAccessKeyName=device;SharedAccessKey=your-iothub-key" -e "File=/config_files/test4-config-multiple-internals-per-device.json" -e DeviceCount=3 --mount type=bind,source=$pwd\test\IotTelemetrySimulator.Test\test_files,target=/config_files,readonly mcr.microsoft.com/oss/azure-samples/azureiot-telemetrysimulator
```

Where the file content is:

```plain
{
"Variables": [
{
"name": "DeviceSequenceValue1",
"sequence": true,
"values": [ "$.Counter", "$.Counter", "$.Counter", "$.Counter", "$.Counter", "true", "false", "$.Counter" ]
},
{
"name": "Device1Tags",
"sequence": true,
"values": [ "['ProducedPartCount']", "['ProducedPartCount']", "['ProducedPartCount']", "['ProducedPartCount']", "['ProducedPartCount']", "['Downtime']", "['Downtime']", "['ProducedPartCount']" ]
},
{
"name": "Device1Downtime",
"values": [ "true", "true", "true", "true", "false" ]
},
{
"name": "Counter"
}
],
"Intervals": {
"sim000001": 10000,
"sim000002": 100
},
"Payloads": [
{
"type": "template",
"deviceId": "sim000001",
"template": "{\"device\":\"$.DeviceId\",\"value\":\"$.DeviceSequenceValue1\",\"tags\": $.Device1Tags}"
},
{
"type": "fix",
"deviceId": "sim000002",
"value": "{\"value\":\"myfixvalue\"}"
},
{
"type": "template",
"deviceId": "sim000003",
"template": "{\"device\":\"$.DeviceId\",\"a\":\"b\",\"value\":\"$.DeviceSequenceValue1\"}"
}
]
}
```

## Generating high volume of telemetry
Expand Down
3 changes: 2 additions & 1 deletion src/IotTelemetrySimulator/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ public static class Constants
public const string DeviceIdValueName = "DeviceId";
public const string GuidValueName = "Guid";
public const string MachineNameValueName = "MachineName";
public const string IterationNumberValueName = "IterationNumber";
public static readonly string[] AllSpecialValueNames =
{
TimeValueName, LocalTimeValueName, EpochValueName, TicksValueName,
DeviceIdValueName, GuidValueName, MachineNameValueName,
DeviceIdValueName, GuidValueName, MachineNameValueName, IterationNumberValueName
};
}
}
7 changes: 6 additions & 1 deletion src/IotTelemetrySimulator/FixPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ public class FixPayload : PayloadBase
public byte[] Payload { get; }

public FixPayload(int distribution, byte[] payload)
: base(distribution)
: this(distribution, null, payload)
{
}

public FixPayload(int distribution, string deviceId, byte[] payload)
: base(distribution, deviceId)
{
this.Payload = payload;
}
Expand Down
2 changes: 1 addition & 1 deletion src/IotTelemetrySimulator/IotHubSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class IotHubSender : SenderBase<Message>
const string ApplicationJsonContentType = "application/json";
const string Utf8Encoding = "utf-8";

private DeviceClient deviceClient;
private readonly DeviceClient deviceClient;

public IotHubSender(DeviceClient deviceClient, string deviceId, RunnerConfiguration config)
: base(deviceId, config)
Expand Down
10 changes: 9 additions & 1 deletion src/IotTelemetrySimulator/PayloadBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ public abstract class PayloadBase
{
public int Distribution { get; set; }

public PayloadBase(int distribution)
public string DeviceId { get; set; }

protected PayloadBase(int distribution)
: this(distribution, null)
{
}

protected PayloadBase(int distribution, string deviceId)
{
if (distribution < 1 || distribution > 100)
throw new ArgumentOutOfRangeException(nameof(distribution), "Distribution must be between 1 and 100");

this.Distribution = distribution;
this.DeviceId = deviceId;
}

public abstract (byte[], Dictionary<string, object>) Generate(Dictionary<string, object> variableValues);
Expand Down
10 changes: 9 additions & 1 deletion src/IotTelemetrySimulator/PayloadGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class PayloadGenerator

public PayloadBase[] Payloads { get; }

private readonly Dictionary<string, PayloadBase> payloadsPerDevice;

public PayloadGenerator(IEnumerable<PayloadBase> payloads, IRandomizer randomizer)
{
this.randomizer = randomizer ?? throw new ArgumentNullException(nameof(randomizer));
Expand All @@ -19,13 +21,19 @@ public PayloadGenerator(IEnumerable<PayloadBase> payloads, IRandomizer randomize
}

this.Payloads = payloads.OrderByDescending(x => x.Distribution).ToArray();
this.payloadsPerDevice = this.Payloads
.Where(x => !string.IsNullOrEmpty(x.DeviceId))
.ToDictionary(x => x.DeviceId);
}

public (byte[], Dictionary<string, object>) Generate(Dictionary<string, object> variableValues)
public (byte[], Dictionary<string, object>) Generate(string deviceId, Dictionary<string, object> variableValues)
{
if (this.Payloads.Length == 1)
return this.Payloads[0].Generate(variableValues);

if (deviceId != null && this.payloadsPerDevice.TryGetValue(deviceId, out var payloadForDevice))
return payloadForDevice.Generate(variableValues);

var random = this.randomizer.Next(1, 101);
var currentPercentage = 0;
foreach (var payload in this.Payloads)
Expand Down
20 changes: 15 additions & 5 deletions src/IotTelemetrySimulator/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
namespace IotTelemetrySimulator
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Expand All @@ -22,6 +18,20 @@ public static IHostBuilder CreateHostBuilder(string[] args)
#if DEBUG
.UseEnvironment("Development")
#endif
.ConfigureAppConfiguration(builder =>
{
var tempConfig = builder.Build();
var fileConfig = tempConfig["File"];
if (!string.IsNullOrEmpty(fileConfig))
{
builder.AddJsonFile(fileConfig, optional: false, reloadOnChange: false);
}

if (tempConfig is IDisposable diposableConfig)
{
diposableConfig.Dispose();
}
})
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<IDeviceSimulatorFactory, DefaultDeviceSimulatorFactory>();
Expand Down
Loading