Skip to content

Commit e475619

Browse files
authored
Merge pull request #42 from Azure-Samples/fbeltrao/fix-41-intervals-parameter
Accept Intervals and Interval parameters to set timespan between telemetries
2 parents e27c0f5 + 036d288 commit e475619

9 files changed

+152
-24
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ The cloud runner can be customized with the following parameters (as `-Parameter
246246
|DeviceCount|Total amout of devices (Default = 100)|
247247
|ContainerCount|Total amount of container instances to create. The total DeviceCount will be divided among all instances (Default = 1)|
248248
|MessageCount|Total amount of messages to send per device. 0 means no limit, **causing the container to never end. It is your job to stop and delete it!** (Default = 100)|
249-
|Interval|Interval in which each device will send messages in milliseconds (Default = 1000)|
249+
|Intervals|Interval in which each device will send messages in milliseconds (Default = 1000). Provide a comma separated list in case intervals change after each message.|
250250
|Template|Telemetry payload template to be used<br />(Default = '{ \"deviceId\": \"$.DeviceId\", \"temp\": $.Temp, \"Ticks\": $.Ticks, \"Counter\": $.Counter, \"time\": \"$.Time\", \"engine\": \"$.Engine\", \"source\": \"$.MachineName\" }')|
251251
|PayloadDistribution|Allows the generation of payloads based on a distribution<br />Example: "fixSize(10, 12) template(25, default) fix(65, aaaaBBBBBCCC)" generates 10% a fix payload of 10 bytes, 25% a template generated payload and 65% of the time a fix payload from values aaaaBBBBBCCC|
252252
|Header|Header properties template to be used<br />(Default = '')|

src/IotTelemetrySimulator/Constants.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
{
33
public static class Constants
44
{
5-
public const string AppVersion = "1.0";
5+
public const string AppVersion = "1.1";
66

7+
/// <summary>For backward compatibility.</summary>
8+
public const string LegacyIntervalConfigName = "Interval";
9+
public const string IntervalsConfigName = "Intervals";
710
public const string TemplateConfigName = "Template";
811
public const string PayloadDistributionConfigName = "PayloadDistribution";
912

src/IotTelemetrySimulator/RunnerConfiguration.cs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Configuration;
56
using System.Linq;
67
using System.Text;
78
using System.Text.RegularExpressions;
@@ -11,6 +12,8 @@
1112

1213
public class RunnerConfiguration
1314
{
15+
private static int[] defaultInterval = new int[] { 1_000 };
16+
1417
public const string DefaultTemplate = "{\"deviceId\": \"$.DeviceId\", \"time\": \"$.Time\", \"counter\": $.Counter}";
1518
private const string RegexExpression = "(?<type>fixsize|template|fix)(\\()(?<pv>[[0-9a-z,=,\\s]+)";
1619
static readonly Regex TemplateParser = new Regex(RegexExpression, RegexOptions.IgnoreCase | RegexOptions.Singleline);
@@ -33,8 +36,6 @@ public class RunnerConfiguration
3336

3437
public int MessageCount { get; set; } = 100;
3538

36-
public int[] WaitIntervals { get; set; } = new int[] { 1_000 };
37-
3839
public int DuplicateEvery { get; private set; }
3940

4041
public PayloadGenerator PayloadGenerator { get; private set; }
@@ -47,7 +48,9 @@ public class RunnerConfiguration
4748

4849
public byte[] FixPayload { get; set; }
4950

50-
public Dictionary<string, int[]> Intervals { get; set; }
51+
public int[] Intervals { get; set; } = defaultInterval;
52+
53+
public Dictionary<string, int[]> DeviceSpecificIntervals { get; set; }
5154

5255
public void EnsureIsValid()
5356
{
@@ -86,21 +89,21 @@ public void EnsureIsValid()
8689
$"{nameof(this.KafkaConnectionProperties)} should contain at least a value for bootstrap.servers");
8790
}
8891

89-
if (this.WaitIntervals.Any(x => (x <= 0)))
90-
throw new Exception($"Elements in {nameof(this.WaitIntervals)} must be greater than zero");
92+
if (this.Intervals.Any(x => (x <= 0)))
93+
throw new Exception($"Elements in {nameof(this.Intervals)} must be greater than zero");
9194

9295
if (this.DuplicateEvery < 0)
9396
throw new Exception($"{nameof(this.DuplicateEvery)} must be greater than or equal to zero");
9497
}
9598

9699
public int[] GetMessageIntervalForDevice(string deviceId)
97100
{
98-
if (this.Intervals != null && this.Intervals.TryGetValue(deviceId, out var customInterval))
101+
if (this.DeviceSpecificIntervals != null && this.DeviceSpecificIntervals.TryGetValue(deviceId, out var customInterval))
99102
{
100103
return customInterval;
101104
}
102105

103-
return this.WaitIntervals;
106+
return this.Intervals;
104107
}
105108

106109
public static RunnerConfiguration Load(IConfiguration configuration, ILogger logger)
@@ -112,7 +115,6 @@ public static RunnerConfiguration Load(IConfiguration configuration, ILogger log
112115
config.DeviceIndex = configuration.GetValue(nameof(DeviceIndex), config.DeviceIndex);
113116
config.DeviceCount = configuration.GetValue(nameof(DeviceCount), config.DeviceCount);
114117
config.MessageCount = configuration.GetValue(nameof(MessageCount), config.MessageCount);
115-
config.WaitIntervals = configuration.GetValue(nameof(WaitIntervals), config.WaitIntervals);
116118
config.DuplicateEvery = configuration.GetValue(nameof(DuplicateEvery), config.DuplicateEvery);
117119

118120
var kafkaProperties = configuration.GetValue<string>(nameof(KafkaConnectionProperties));
@@ -188,36 +190,79 @@ public static RunnerConfiguration Load(IConfiguration configuration, ILogger log
188190
}
189191
}
190192

191-
config.Intervals = LoadIntervals(configuration);
193+
(config.Intervals, config.DeviceSpecificIntervals) = LoadIntervals(configuration);
192194
config.Header = GetTelemetryTemplate(configuration, nameof(Header), futureVariableNames);
193195
config.PartitionKey = GetTelemetryTemplate(configuration, nameof(PartitionKey), futureVariableNames);
194196

195197
return config;
196198
}
197199

198-
private static Dictionary<string, int[]> LoadIntervals(IConfiguration configuration)
200+
private static (int[], Dictionary<string, int[]>) LoadIntervals(IConfiguration configuration)
199201
{
200-
var section = configuration.GetSection(nameof(RunnerConfiguration.Intervals));
202+
var intervalForAllDevices = defaultInterval;
203+
var section = configuration.GetSection(Constants.IntervalsConfigName);
201204
if (!section.Exists())
202205
{
203-
return null;
206+
section = configuration.GetSection(Constants.LegacyIntervalConfigName);
207+
if (!section.Exists())
208+
{
209+
return (intervalForAllDevices, null);
210+
}
204211
}
205212

206213
var result = new Dictionary<string, int[]>();
207-
208-
foreach (var intervalSection in section.GetChildren())
214+
var sectionChildren = section.GetChildren();
215+
if (sectionChildren.Any())
209216
{
210-
if (intervalSection.Value != null && int.TryParse(intervalSection.Value, out int interval))
217+
foreach (var intervalSection in sectionChildren)
211218
{
212-
result[intervalSection.Key] = new[] { interval };
219+
int[] interval = null;
220+
if (intervalSection.Value != null && int.TryParse(intervalSection.Value, out int singleInterval))
221+
{
222+
interval = new[] { singleInterval };
223+
}
224+
else
225+
{
226+
interval = intervalSection.Get<int[]>();
227+
}
228+
229+
if (interval.Any(x => x < 1))
230+
{
231+
throw new ConfigurationErrorsException($"Interval value must be a positive number.");
232+
}
233+
234+
if (string.IsNullOrEmpty(intervalSection.Key))
235+
{
236+
intervalForAllDevices = interval;
237+
}
238+
else
239+
{
240+
result[intervalSection.Key] = interval;
241+
}
213242
}
214-
else
243+
}
244+
else if (!string.IsNullOrEmpty(section.Value))
245+
{
246+
var settingsInterval = section.Value.Split(new[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries)
247+
.Select(intervalText =>
248+
{
249+
int.TryParse(intervalText, out var interval);
250+
if (interval < 1)
251+
{
252+
throw new ConfigurationErrorsException($"Interval value must be a positive number. Actual: {intervalText}");
253+
}
254+
255+
return interval;
256+
})
257+
.ToArray();
258+
259+
if (settingsInterval.Length > 0)
215260
{
216-
result[intervalSection.Key] = intervalSection.Get<int[]>();
261+
intervalForAllDevices = settingsInterval;
217262
}
218263
}
219264

220-
return result;
265+
return (intervalForAllDevices, result);
221266
}
222267

223268
private static TelemetryTemplate GetTelemetryTemplate(IConfiguration configuration, string headerName, IEnumerable<string> futureVariableNames)

src/IotTelemetrySimulator/SimulationWorker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private async Task RunnerAsync()
7575
Console.WriteLine($"Device 0-last = ({this.devices[0].DeviceID}-{this.devices.Last().DeviceID})");
7676
Console.WriteLine("Device index = " + this.config.DeviceIndex);
7777
Console.WriteLine("Message count = " + this.config.MessageCount);
78-
Console.WriteLine("Interval = " + this.config.WaitIntervals + "ms");
78+
Console.WriteLine("Interval = " + string.Join(",", this.config.Intervals) + "ms");
7979
Console.WriteLine("Template = " + this.config.PayloadGenerator.GetDescription());
8080
Console.WriteLine("Header = " + this.config.Header?.GetTemplateDefinition());
8181
Console.WriteLine("========================================================================================================================");

test/IotTelemetrySimulator.Test/IotTelemetrySimulator.Test.csproj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@
3535
<None Update="test_files\test5-config-payloads-as-json.json">
3636
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
3737
</None>
38+
<None Update="test_files\test8-config-payloads-with-invalid-variable-intervals.json">
39+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
40+
</None>
41+
<None Update="test_files\test7-config-payloads-with-invalid-variable-intervals.json">
42+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
43+
</None>
3844
<None Update="test_files\test7-config-payloads-with-variable-intervals-and-tags.jsonc">
3945
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
4046
</None>
41-
<None Update="test_files\test6-config-payloads-with-variable-intervals.jsonc">
47+
<None Update="test_files\test6-config-payloads-with-variable-intervals.json">
4248
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
4349
</None>
4450
<None Update="test_files\test4-config-multiple-internals-per-device.json">

test/IotTelemetrySimulator.Test/RunnerConfigurationTest.cs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ namespace IotTelemetrySimulator.Test
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Configuration;
56
using System.Text;
67
using Microsoft.Extensions.Configuration;
78
using Microsoft.Extensions.Logging.Abstractions;
@@ -224,7 +225,7 @@ public void When_Loading_From_File_With_Custom_Intervals_Loads_Correctly()
224225
public void When_Loading_From_File_With_Variable_Intervals_Loads_Correctly()
225226
{
226227
var configuration = new ConfigurationBuilder()
227-
.AddJsonFile("./test_files/test6-config-payloads-with-variable-intervals.jsonc", false, false)
228+
.AddJsonFile("./test_files/test6-config-payloads-with-variable-intervals.json", false, false)
228229
.Build();
229230

230231
var target = RunnerConfiguration.Load(configuration, NullLogger.Instance);
@@ -236,5 +237,64 @@ public void When_Loading_From_File_With_Variable_Intervals_Loads_Correctly()
236237
Assert.Equal(30_000, intervalsDevice2[1]);
237238
Assert.Equal(15_000, intervalsDevice2[2]);
238239
}
240+
241+
[Theory]
242+
[InlineData("1", new[] { 1 })]
243+
[InlineData("5000", new[] { 5_000 })]
244+
[InlineData("5000,", new[] { 5_000 })]
245+
[InlineData("5000;", new[] { 5_000 })]
246+
[InlineData("1000,5000", new[] { 1_000, 5_000 })]
247+
[InlineData("1000, 5000", new[] { 1_000, 5_000 })]
248+
[InlineData("1000;5000", new[] { 1_000, 5_000 })]
249+
[InlineData("1000; 5000", new[] { 1_000, 5_000 })]
250+
public void When_Intervals_Is_Set_Should_Load(string rawValue, int[] expectedInterval)
251+
{
252+
var configurationNames = new[] { Constants.IntervalsConfigName, Constants.LegacyIntervalConfigName };
253+
254+
foreach (var configurationName in configurationNames)
255+
{
256+
var configuration = new ConfigurationBuilder()
257+
.AddInMemoryCollection(new Dictionary<string, string>
258+
{
259+
{ configurationName, rawValue }
260+
})
261+
.Build();
262+
263+
var target = RunnerConfiguration.Load(configuration, NullLogger.Instance);
264+
var intervals = target.GetMessageIntervalForDevice("sim000001");
265+
266+
Assert.Equal(intervals, expectedInterval);
267+
}
268+
}
269+
270+
[Theory]
271+
[InlineData("aaaa")]
272+
[InlineData("1,aaaa")]
273+
[InlineData("0")]
274+
[InlineData("-1")]
275+
[InlineData("1,-1")]
276+
public void When_Interval_Is_Not_Positive_Number_Should_Throw(string intervalValue)
277+
{
278+
var configuration = new ConfigurationBuilder()
279+
.AddInMemoryCollection(new Dictionary<string, string>
280+
{
281+
{ Constants.IntervalsConfigName, intervalValue }
282+
})
283+
.Build();
284+
285+
Assert.Throws<ConfigurationErrorsException>(() => RunnerConfiguration.Load(configuration, NullLogger.Instance));
286+
}
287+
288+
[Theory]
289+
[InlineData("./test_files/test7-config-payloads-with-invalid-variable-intervals.json")]
290+
[InlineData("./test_files/test8-config-payloads-with-invalid-variable-intervals.json")]
291+
public void When_DeviceSpecific_Interval_Is_Not_Positive_Number_Should_Throw(string srcConfigFile)
292+
{
293+
var configuration = new ConfigurationBuilder()
294+
.AddJsonFile(srcConfigFile, false, false)
295+
.Build();
296+
297+
Assert.Throws<ConfigurationErrorsException>(() => RunnerConfiguration.Load(configuration, NullLogger.Instance));
298+
}
239299
}
240300
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"Intervals": {
3+
"sim000001": [ 1, -10 ],
4+
"sim000002": [ 20000, 30000, 15000 ]
5+
},
6+
"DeviceCount": 2
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"Intervals": {
3+
"sim000001": 1,
4+
"sim000002": [ 20000, 30000, -15000 ]
5+
},
6+
"DeviceCount": 2
7+
}

0 commit comments

Comments
 (0)