From aaa0465e5649f0eb2bc4a56330e825e324263023 Mon Sep 17 00:00:00 2001 From: Perry Skountrianos - MSFT Date: Tue, 7 Nov 2017 15:58:43 -0800 Subject: [PATCH] IoT Samrt Grig v2 - 1.4M rows/sec --- .../applications/iot-smart-grid/App.config | 31 +- .../iot-smart-grid/ConsoleClient/Program.cs | 84 ++--- .../DataGenerator/SqlDataGenerator.cs | 207 +++++++----- .../applications/iot-smart-grid/Db/Db.sqlproj | 10 +- .../iot-smart-grid/Db/Storage/mod.sql | 3 +- .../InsertMeterMeasurement.sql | 38 +-- .../InsertMeterMeasurementHistory.sql | 12 + .../Db/dbo/Tables/MeterMeasurement.sql | 17 +- .../Db/dbo/Tables/MeterMeasurementHistory.sql | 4 +- .../udtMeterMeasurement.sql | 2 +- .../Db/dbo/Views/vwMeterMeasurement.sql | 6 +- .../iot-smart-grid/IoT-Smart-Grid.sln | 28 +- samples/applications/iot-smart-grid/README.md | 27 +- .../Screenshots/RowsInserted.png | Bin 0 -> 14925 bytes .../WinFormsClient/FrmMain.Designer.cs | 316 +++++++----------- .../iot-smart-grid/WinFormsClient/FrmMain.cs | 189 +++++++---- .../WinFormsClient/FrmMain.resx | 12 +- .../WinFormsClient/WinFormsClient.csproj | 12 +- .../iot-smart-grid/setup-or-reset-demo.sql | 105 ++++++ 19 files changed, 614 insertions(+), 489 deletions(-) create mode 100644 samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurementHistory.sql create mode 100644 samples/applications/iot-smart-grid/Screenshots/RowsInserted.png create mode 100644 samples/applications/iot-smart-grid/setup-or-reset-demo.sql diff --git a/samples/applications/iot-smart-grid/App.config b/samples/applications/iot-smart-grid/App.config index 04d2d1e4c4..64c0bd3803 100644 --- a/samples/applications/iot-smart-grid/App.config +++ b/samples/applications/iot-smart-grid/App.config @@ -4,21 +4,26 @@ - + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/samples/applications/iot-smart-grid/ConsoleClient/Program.cs b/samples/applications/iot-smart-grid/ConsoleClient/Program.cs index 30040d48be..efbed52e78 100644 --- a/samples/applications/iot-smart-grid/ConsoleClient/Program.cs +++ b/samples/applications/iot-smart-grid/ConsoleClient/Program.cs @@ -36,8 +36,7 @@ This code sample simulates an IoT Smart Grid scenario where multiple IoT power m shock absorber scenario: https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/. Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter. It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table. -In addition to the in-memory features, the sample is leveraging System-Versioned Temporal Tables: https://msdn.microsoft.com/en-us/library/dn935015.aspx for building version history, -Clustered Columnstore Index: https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and +In addition to the in-memory features, the sample is offloading historical values to a Clustered Columnstore Index: https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and Power BI: https://powerbi.microsoft.com/en-us/desktop/ for data visualization. */ namespace ConsoleClient @@ -45,31 +44,31 @@ namespace ConsoleClient class Program { static SqlDataGenerator dataGenerator; - static string connection; + static string[] connection; static string spName; static string logFileName; static string powerBIDesktopPath; - static int tasks; static int meters; static int batchSize; - static int delay; static int commandTimeout; - static int shockFrequency; - static int shockDuration; - static int rpsFrequency; - static int enableShock; - static Timer mainTimer = new Timer(); + static int rpsFrequency; + static int numberOfDataLoadTasks; + static int numberOfOffLoadTasks; + static int deleteBatchSize; + static string deleteSPName; + static int dataLoadCommandDelay; + static int offLoadCommandDelay; + static int delayStart; + static int appRunDuration; + static int numberOfRowsOfloadLimit; static Timer rpsTimer = new Timer(); - static Timer shockTimer = new Timer(); static void Main(string[] args) { Init(); - dataGenerator = new SqlDataGenerator(connection, spName, commandTimeout, meters, tasks, delay, batchSize, ExceptionCallback); - - mainTimer.Elapsed += mainTimer_Tick; + dataGenerator = new SqlDataGenerator(connection, spName, commandTimeout, meters, numberOfDataLoadTasks, dataLoadCommandDelay, batchSize, deleteSPName, numberOfOffLoadTasks, offLoadCommandDelay, deleteBatchSize, numberOfRowsOfloadLimit, ExceptionCallback); rpsTimer.Elapsed += rpsTimer_Tick; - shockTimer.Elapsed += shockTimer_Tick; + string commandString = string.Empty; Console.ForegroundColor = ConsoleColor.White; @@ -129,9 +128,7 @@ static async void Start() { if (!dataGenerator.IsRunning) { - if(enableShock == 1) mainTimer.Start(); rpsTimer.Start(); - await dataGenerator.RunAsync(); } } @@ -144,10 +141,7 @@ static async void Stop() { if (dataGenerator.IsRunning) { - if (enableShock == 1) mainTimer.Stop(); rpsTimer.Stop(); - if (enableShock == 1) shockTimer.Stop(); - await dataGenerator.StopAsync(); dataGenerator.RpsReset(); } @@ -177,47 +171,47 @@ static void Init() { try { + int numberOfSqlConnections = ConfigurationManager.ConnectionStrings.Count; + connection = new string[numberOfSqlConnections]; // Read Config Settings - connection = ConfigurationManager.ConnectionStrings["Db"].ConnectionString; + for (int i = 0; i < numberOfSqlConnections; i++) + { + connection[i] = ConfigurationManager.ConnectionStrings[i].ConnectionString; + } + spName = ConfigurationManager.AppSettings["insertSPName"]; logFileName = ConfigurationManager.AppSettings["logFileName"]; - powerBIDesktopPath = ConfigurationManager.AppSettings["powerBIDesktopPath"]; - tasks = int.Parse(ConfigurationManager.AppSettings["numberOfTasks"]); - meters = int.Parse(ConfigurationManager.AppSettings["numberOfMeters"]); + numberOfDataLoadTasks = int.Parse(ConfigurationManager.AppSettings["numberOfDataLoadTasks"]); + dataLoadCommandDelay = int.Parse(ConfigurationManager.AppSettings["dataLoadCommandDelay"]); batchSize = int.Parse(ConfigurationManager.AppSettings["batchSize"]); - delay = int.Parse(ConfigurationManager.AppSettings["commandDelay"]); + deleteSPName = ConfigurationManager.AppSettings["deleteSPName"]; + numberOfOffLoadTasks = int.Parse(ConfigurationManager.AppSettings["numberOfOffLoadTasks"]); + offLoadCommandDelay = int.Parse(ConfigurationManager.AppSettings["offLoadCommandDelay"]); + deleteBatchSize = int.Parse(ConfigurationManager.AppSettings["deleteBatchSize"]); + meters = int.Parse(ConfigurationManager.AppSettings["numberOfMeters"]); commandTimeout = int.Parse(ConfigurationManager.AppSettings["commandTimeout"]); - shockFrequency = int.Parse(ConfigurationManager.AppSettings["shockFrequency"]); - shockDuration = int.Parse(ConfigurationManager.AppSettings["shockDuration"]); - enableShock = int.Parse(ConfigurationManager.AppSettings["enableShock"]); - + delayStart = int.Parse(ConfigurationManager.AppSettings["delayStart"]); + appRunDuration = int.Parse(ConfigurationManager.AppSettings["appRunDuration"]); rpsFrequency = int.Parse(ConfigurationManager.AppSettings["rpsFrequency"]); + numberOfRowsOfloadLimit = int.Parse(ConfigurationManager.AppSettings["numberOfRowsOfloadLimit"]); + powerBIDesktopPath = ConfigurationManager.AppSettings["powerBIDesktopPath"]; // Initialize Timers - mainTimer.Interval = shockFrequency; - shockTimer.Interval = shockDuration; rpsTimer.Interval = rpsFrequency; if (batchSize <= 0) throw new SqlDataGeneratorException("The Batch Size cannot be less or equal to zero."); - if (tasks <= 0) throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero."); + if (numberOfDataLoadTasks <= 0) throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero."); - if (delay < 0) throw new SqlDataGeneratorException("Delay cannot be less than zero"); + if (dataLoadCommandDelay < 0) throw new SqlDataGeneratorException("Delay cannot be less than zero"); if (meters <= 0) throw new SqlDataGeneratorException("Number Of Meters cannot be less than zero"); - if (meters < batchSize * tasks) throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize)."); + if (meters < batchSize * numberOfDataLoadTasks) throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize)."); + } catch (Exception exception) { HandleException(exception); } } - static void mainTimer_Tick(object sender, ElapsedEventArgs e) - { - if (dataGenerator.IsRunning) - { - dataGenerator.Delay = 0; - shockTimer.Start(); - } - } static void rpsTimer_Tick(object sender, ElapsedEventArgs e) { try @@ -236,11 +230,5 @@ static void rpsTimer_Tick(object sender, ElapsedEventArgs e) } catch (Exception exception) { HandleException(exception); } } - static void shockTimer_Tick(object sender, ElapsedEventArgs e) - { - Random rand = new Random(); - dataGenerator.Delay = rand.Next(1500, 3000); - shockTimer.Stop(); - } } } diff --git a/samples/applications/iot-smart-grid/DataGenerator/SqlDataGenerator.cs b/samples/applications/iot-smart-grid/DataGenerator/SqlDataGenerator.cs index 6759847d77..89c7436906 100644 --- a/samples/applications/iot-smart-grid/DataGenerator/SqlDataGenerator.cs +++ b/samples/applications/iot-smart-grid/DataGenerator/SqlDataGenerator.cs @@ -12,17 +12,14 @@ // places, or events is intended or should be inferred. using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Data.SqlClient; -using System.Data.Sql; -using System.Data; -using System.Diagnostics; -using System.Collections.Concurrent; -using Microsoft.SqlServer.Server; namespace DataGenerator { @@ -37,18 +34,29 @@ public class SqlDataGenerator private Action onException; private ConcurrentDictionary tasks; - private string sqlConnectionString; + private string[] sqlConnectionStrings; private string sqlInsertMeterMeasurementSPName; private int sqlCommandTimeout; private int batchSize; - private int initialNumberOfTasks; + private int numberOfDataLoadTasks; + private int dataLoadCommandDelay; + + private string sqlDeleteMeterMeasurementSPName; + private int numberOfOffLoadTasks; + private int offLoadCommandDelay; + private int deleteBatchSize; + private int numberOfMetersPerTask; private int numberOfBatchesPerTask; - private int delay; + private int numberOfMeters; + private int numberOfSqlConnections; private Stopwatch timer; private int numberOfRowsInserted = 0; + private int numberOfRowsDeleted = 0; + private int numberOfRowsOfloadLimit; + protected ThreadLocal randomValue; private bool running = false; @@ -57,11 +65,11 @@ public class SqlDataGenerator /// Integer public int Delay { - get { return delay; } + get { return dataLoadCommandDelay; } set { - Validate(this.batchSize, this.initialNumberOfTasks, value, this.numberOfMeters); - delay = value; + Validate(this.batchSize, this.numberOfDataLoadTasks, value, this.numberOfMeters); + dataLoadCommandDelay = value; } } @@ -72,7 +80,7 @@ public int BatchSize get { return batchSize; } set { - Validate(value, this.initialNumberOfTasks, this.delay, this.numberOfMeters); + Validate(value, this.numberOfDataLoadTasks, this.dataLoadCommandDelay, this.numberOfMeters); batchSize = value; } } @@ -81,6 +89,8 @@ public int BatchSize /// Double public double Rps => (double)this.numberOfRowsInserted / this.timer.Elapsed.TotalSeconds; + public double Drps => (double)this.numberOfRowsDeleted / this.timer.Elapsed.TotalSeconds; + /// The number of current active tasks. /// Integer public int RunningTasks => this.tasks.Count(); @@ -90,39 +100,54 @@ public int BatchSize public bool IsRunning => this.running; /// Creates a new instance of the SqlDataGenerator Class. - /// The sqlserver connectionString. Example: "Data Source=.;Initial Catalog=DbName;Integrated Security=True" + /// The sqlserver connectionString. Example: "Data Source=.;Initial Catalog=DbName;Integrated Security=True" /// The Insert Meter Measurement sqlserver stored procedure. Example: "InsertMeterMeasurement". Note that the sql stored procedure needs to accept exactly two parameters: @Batch AS (Your User Defined Table Type) and @BatchSize INT /// The sqlserver command timeout. Example: 600 /// The total number of Meters. Example: 1000 - /// The number of concurrent tasks. Example: 5. Note that every task 1.Creates and opens a new sql connection 2.Creates a batch of BatchSize sample data and 3.Executes the sql stored procedure passed in sqlStoredProcedureName endless times until stopped by the user. - /// Delay in Millisecods betweeen Sql Commands. Example. 100 + /// The number of concurrent tasks. Example: 5. Note that every task 1.Creates and opens a new sql connection 2.Creates a batch of BatchSize sample data and 3.Executes the sql stored procedure passed in sqlStoredProcedureName endless times until stopped by the user. + /// Delay in Millisecods betweeen Sql Commands. Example. 100 /// The row count of the batch size to be used by every task. Example: 200 /// The pipe seperated column types of the batch table. Example. identity:1:1|string|datetime|double|int|guid /// Exception call back method with TaskId(int) and exception(Exception). Example: ExceptionCallback public SqlDataGenerator( - string sqlConnectionString, + string[] sqlConnectionStrings, string sqlInsertSPName, - int sqlCommandTimeout, + int sqlCommandTimeout, int numberOfMeters, - int initialNumberOfTasks, - int delayInMilliseconds, - int batchSize, + int numbeOfDataLoadTasks, + int dataLoadCommandDelay, + int batchSize, + string sqlDeleteSPName, + int numberOfOffLoadTasks, + int offLoadCommandDelay, + int deleteBatchSize, + int numberOfRowsOfloadLimit, Action onException) { - this.sqlConnectionString = sqlConnectionString; + this.sqlConnectionStrings = sqlConnectionStrings; + this.numberOfSqlConnections = sqlConnectionStrings.Count(); this.sqlInsertMeterMeasurementSPName = sqlInsertSPName; + this.sqlDeleteMeterMeasurementSPName = sqlDeleteSPName; + this.sqlCommandTimeout = sqlCommandTimeout; this.numberOfMeters = numberOfMeters; this.onException = onException; this.tasks = new ConcurrentDictionary(); this.randomValue = new ThreadLocal(() => new Random(Guid.NewGuid().GetHashCode())); - this.initialNumberOfTasks = initialNumberOfTasks; - this.delay = delayInMilliseconds; + + this.numberOfDataLoadTasks = numbeOfDataLoadTasks * this.numberOfSqlConnections; + this.dataLoadCommandDelay = dataLoadCommandDelay; + + this.numberOfOffLoadTasks = numberOfOffLoadTasks * this.numberOfSqlConnections; + this.offLoadCommandDelay = offLoadCommandDelay; + this.deleteBatchSize = deleteBatchSize; + this.batchSize = batchSize; - - Validate(this.batchSize, this.initialNumberOfTasks, this.delay, this.numberOfMeters); + this.numberOfRowsOfloadLimit = numberOfRowsOfloadLimit; + + Validate(this.batchSize, this.numberOfDataLoadTasks, this.dataLoadCommandDelay, this.numberOfMeters); } /// Creates and Starts all the tasks asynchronously. Note that every task 1.Creates and opens a new sql connection 2.Creates a batch of BatchSize sample data and 3.Executes the sql stored procedure passed in sqlStoredProcedureName endless times until stopped by the user. @@ -134,7 +159,7 @@ public async Task RunAsync() return; } timer = Stopwatch.StartNew(); - await this.RunAsync(this.initialNumberOfTasks); + await this.RunAsync(this.sqlConnectionStrings, this.numberOfDataLoadTasks, this.numberOfOffLoadTasks); } /// Stops all tasks asynchronously. @@ -142,6 +167,8 @@ public async Task RunAsync() public async Task StopAsync() { await this.StopAsync(this.RunningTasks); + this.timer.Stop(); + this.numberOfRowsInserted = 0; } /// Restarts the Rows/Second Counter. This is called internally every time the input is changed. @@ -155,46 +182,24 @@ public void RpsReset() } } - /// Updates the number of tasks that the DataGenerator is using. - /// Task - /// - /// The number of Tasks to start/stop depending of the number of tasks currently running. - public async Task UpdateTasksAsync(int numberOfTasks) - { - int diff = numberOfTasks - this.RunningTasks; - - if (!running || diff == 0) - { - this.initialNumberOfTasks = numberOfTasks; - return; - } - - if (diff < 0) - { - await this.StopAsync(-diff); - } - else - { - await this.RunAsync(diff); - } - } - /// InsertMeterMeasurementAsync(int taskId, CancellationToken token) /// Task /// Every Task creates a new sql connection, creates a new sqlcommand, create a batch of random numbers, and executes indefinetely until stopped by the user. /// The taskId /// The task's CancellationToken - private async Task InsertMeterMeasurementAsync(int taskId, CancellationToken token) + private async Task InsertAsync(int taskId, string sqlConnectionString, CancellationToken token) { + + int batchId = 0; int size = this.BatchSize; - - using (SqlConnection connection = new SqlConnection(this.sqlConnectionString)) + + using (SqlConnection connection = new SqlConnection(sqlConnectionString)) { await connection.OpenAsync(token); - + using (SqlCommand command = new SqlCommand()) - { + { command.Connection = connection; command.CommandType = CommandType.StoredProcedure; command.CommandTimeout = this.sqlCommandTimeout; @@ -202,45 +207,58 @@ private async Task InsertMeterMeasurementAsync(int taskId, CancellationToken tok command.Parameters.Add("@Batch", SqlDbType.Structured); command.Parameters.Add("@BatchSize", SqlDbType.Int).Value = this.BatchSize; - while (!token.IsCancellationRequested) - { - DataTable dataTable = CreateBatch(taskId, batchId++); - command.Parameters[0].Value = dataTable; + DataTable dataTable = CreateBatch(taskId, batchId++); + command.Parameters[0].Value = dataTable; - await command.ExecuteNonQueryAsync(token); - Interlocked.Add(ref this.numberOfRowsInserted, size); - await Task.Delay(this.Delay, token); + using (SqlCommand deleteCommand = new SqlCommand()) + { + deleteCommand.Connection = connection; + deleteCommand.CommandType = CommandType.StoredProcedure; + deleteCommand.CommandTimeout = this.sqlCommandTimeout; + deleteCommand.CommandText = this.sqlDeleteMeterMeasurementSPName; + deleteCommand.Parameters.Add("@MeterID", SqlDbType.Int).Value = taskId; + + while (!token.IsCancellationRequested) + { + await command.ExecuteNonQueryAsync(token); + Interlocked.Add(ref this.numberOfRowsInserted, size); + await Task.Delay(this.Delay, token); + + if (Rps > this.numberOfRowsOfloadLimit) + { + await deleteCommand.ExecuteNonQueryAsync(token); + } + } } } } } - + /// CreateMeterMeasurementBatch(int taskId) /// DataTable /// Task Id private DataTable CreateBatch(int taskId, int batchId) - { + { DataTable table = new DataTable(); table.Columns.Add("RowID", typeof(int)); table.Columns.Add("MeterID", typeof(int)); table.Columns.Add("MeasurementInkWh", typeof(double)); table.Columns.Add("PostalCode", typeof(string)); table.Columns.Add("MeasurementDate", typeof(DateTime)); - + batchId = (batchId % this.numberOfBatchesPerTask); for (int i = 1; i <= this.batchSize; i++) { - int randomPostalCode = randomValue.Value.Next(0, this.postalCodes.Length); - int meterId = taskId * this.numberOfMetersPerTask + batchId * this.BatchSize + i; + int randomPostalCode = randomValue.Value.Next(0, this.postalCodes.Length); + int meterId = taskId; + double value = randomValue.Value.NextDouble(); DateTime date = DateTime.Now; string postalCode = this.postalCodes[randomPostalCode].ToString(); - // Data Shock - Produces 10X values - value = this.Delay == 0 ? value * 10 : value; - - table.Rows.Add(i, meterId, value, postalCode, date); + table.Rows.Add(i, meterId, value, postalCode, date); } + return table; } @@ -265,22 +283,33 @@ private async Task StopAsync(int numberOfTasksToStop) /// RunAsync(int numberOfTasks) /// Task - /// The number of Tasks to start/stop depending of the number of tasks currently running. - private async Task RunAsync(int numberOfTasks) - { - for (int i = 0; i < numberOfTasks; i++) + /// The number of Tasks to start/stop depending of the number of tasks currently running. + private async Task RunAsync(string[] connectionStrings, int numberOfDataLoadTasks, int numberOfOffLoadTasks) + { + int dataLoadTasksPerSqlConnection = numberOfDataLoadTasks / this.numberOfSqlConnections; + int offLoadTasksPerSqlConnection = numberOfOffLoadTasks / this.numberOfSqlConnections; + + string con; + + for (int j = 0; j < this.numberOfSqlConnections; j++) { - CancellationTokenSource tokenSource = new CancellationTokenSource(); - int taskId = i; - Task task = Task.Factory.StartNew( - async () => await this.InsertMeterMeasurementAsync(taskId, tokenSource.Token).ContinueWith(t => CleanupTask(taskId, t)), - tokenSource.Token, - TaskCreationOptions.LongRunning, - TaskScheduler.Default).Unwrap(); - - tasks.TryAdd(taskId, new CancellableTask(taskId, task, tokenSource)); + con = connectionStrings[j]; // set connection string + + // Start Data Load Tasks + for (int i = j * dataLoadTasksPerSqlConnection; i < (j + 1) * dataLoadTasksPerSqlConnection; i++) + { + CancellationTokenSource tokenSource = new CancellationTokenSource(); + int taskId = i; + Task task = Task.Factory.StartNew( + async () => await this.InsertAsync(taskId, con, tokenSource.Token).ContinueWith(t => CleanupTask(taskId, t)), + tokenSource.Token, + TaskCreationOptions.LongRunning, + TaskScheduler.Default).Unwrap(); + + tasks.TryAdd(taskId, new CancellableTask(taskId, task, tokenSource)); + } } - + this.running = true; await Task.WhenAll(this.tasks.Values.Select(t => t.Task)); @@ -302,7 +331,7 @@ private void CleanupTask(int taskId, Task task) } } - /// Validate(int batchSize, int tasks, int delay) + /// Validate(int batchSize, int tasks, int dataLoadCommandDelay) /// The Batch Size /// The number Of Tasks /// Teh Delay @@ -333,7 +362,7 @@ private void Validate(int batchSize, int tasks, int delay, int numberOfMeters) RpsReset(); // Set Number Of Meters Per Tasks - this.numberOfMetersPerTask = this.numberOfMeters / this.initialNumberOfTasks; + this.numberOfMetersPerTask = this.numberOfMeters / this.numberOfDataLoadTasks; this.numberOfBatchesPerTask = this.numberOfMetersPerTask / this.BatchSize; } } diff --git a/samples/applications/iot-smart-grid/Db/Db.sqlproj b/samples/applications/iot-smart-grid/Db/Db.sqlproj index 9f0a65aa41..1eb3b8e421 100644 --- a/samples/applications/iot-smart-grid/Db/Db.sqlproj +++ b/samples/applications/iot-smart-grid/Db/Db.sqlproj @@ -7,7 +7,7 @@ Db 2.0 4.1 - {074bb065-0cc1-438a-a963-ea3fd781ba2a} + {8a2d24db-3efa-4fac-84fc-b2ee1a760221} Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider Database @@ -17,7 +17,7 @@ 1033,CI BySchemaAndSchemaType True - v4.5.2 + v4.6.1 CS Properties False @@ -39,7 +39,7 @@ Off DISABLED False - False + True None True False @@ -143,6 +143,10 @@ On On + + On + On + \ No newline at end of file diff --git a/samples/applications/iot-smart-grid/Db/Storage/mod.sql b/samples/applications/iot-smart-grid/Db/Storage/mod.sql index ff8d8f9391..152606512c 100644 --- a/samples/applications/iot-smart-grid/Db/Storage/mod.sql +++ b/samples/applications/iot-smart-grid/Db/Storage/mod.sql @@ -1,3 +1,2 @@ -ALTER DATABASE [$(DatabaseName)] - ADD FILEGROUP [mod] CONTAINS MEMORY_OPTIMIZED_DATA; +--ALTER DATABASE [$(DatabaseName)] ADD FILEGROUP [mod] CONTAINS MEMORY_OPTIMIZED_DATA; diff --git a/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurement.sql b/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurement.sql index 9f72a5c8ba..36d1566fbf 100644 --- a/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurement.sql +++ b/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurement.sql @@ -1,40 +1,12 @@ - - -CREATE PROCEDURE [dbo].[InsertMeterMeasurement] +CREATE PROCEDURE [dbo].[InsertMeterMeasurement] @Batch AS dbo.udtMeterMeasurement READONLY, @BatchSize INT WITH NATIVE_COMPILATION, SCHEMABINDING AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL=SNAPSHOT, LANGUAGE=N'English') - DECLARE @i INT = 1 - DECLARE @MeterID INT - DECLARE @MeasurementInkWh DECIMAL(9, 4) - DECLARE @PostalCode NVARCHAR(10) - DECLARE @MeasurementDate DATETIME2(7) - - WHILE (@i <= @BatchSize) - BEGIN - - SELECT @MeterID = MeterID, - @MeasurementInkWh = MeasurementInkWh, - @MeasurementDate = MeasurementDate, - @PostalCode = PostalCode - FROM @Batch - WHERE RowID = @i - - UPDATE dbo.MeterMeasurement - SET MeasurementInkWh += @MeasurementInkWh, - MeasurementDate = @MeasurementDate, - PostalCode = @PostalCode - WHERE MeterID = @MeterID - - IF(@@ROWCOUNT = 0) - BEGIN - INSERT INTO dbo.MeterMeasurement (MeterID, MeasurementInkWh, PostalCode, MeasurementDate) - VALUES (@MeterID, @MeasurementInkWh, @PostalCode, @MeasurementDate); - END - SET @i += 1 - END -END + INSERT INTO dbo.MeterMeasurement (MeterID, MeasurementInkWh, PostalCode, MeasurementDate) + SELECT MeterID, MeasurementInkWh, PostalCode, MeasurementDate FROM @Batch + +END; \ No newline at end of file diff --git a/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurementHistory.sql b/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurementHistory.sql new file mode 100644 index 0000000000..cb35bfeef7 --- /dev/null +++ b/samples/applications/iot-smart-grid/Db/dbo/Stored Procedures/InsertMeterMeasurementHistory.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE [dbo].[InsertMeterMeasurementHistory] + @MeterID INT +AS +BEGIN + BEGIN TRAN + INSERT INTO dbo.MeterMeasurementHistory (MeterID, MeasurementInkWh, PostalCode, MeasurementDate) + SELECT TOP 250000 MeterID, MeasurementInkWh, PostalCode, MeasurementDate FROM dbo.MeterMeasurement + WHERE MeterID = @MeterID + + DELETE TOP (250000) FROM dbo.MeterMeasurement WHERE MeterID = @MeterID + COMMIT +END; \ No newline at end of file diff --git a/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurement.sql b/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurement.sql index a2611d6bba..33e264756c 100644 --- a/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurement.sql +++ b/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurement.sql @@ -1,12 +1,11 @@ CREATE TABLE [dbo].[MeterMeasurement] ( - [MeterID] INT NOT NULL, - [MeasurementInkWh] DECIMAL (9, 4) NOT NULL, - [PostalCode] NVARCHAR (10) NOT NULL, - [MeasurementDate] DATETIME2 (7) NOT NULL, - [SysStartTime] DATETIME2 (7) GENERATED ALWAYS AS ROW START NOT NULL, - [SysEndTime] DATETIME2 (7) GENERATED ALWAYS AS ROW END NOT NULL, - PRIMARY KEY NONCLUSTERED ([MeterID] ASC), - PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime]) + [MeasurementID] BIGINT IDENTITY (1, 1) NOT NULL, + [MeterID] INT NOT NULL, + [MeasurementInkWh] DECIMAL (9, 4) NOT NULL, + [PostalCode] NVARCHAR (10) NOT NULL, + [MeasurementDate] DATETIME2 (7) NOT NULL, + PRIMARY KEY NONCLUSTERED HASH ([MeasurementID]) WITH (BUCKET_COUNT = 16777216), + INDEX [ix] NONCLUSTERED HASH ([MeterID]) WITH (BUCKET_COUNT = 1048576) ) -WITH (MEMORY_OPTIMIZED = ON, SYSTEM_VERSIONING = ON (HISTORY_TABLE=[dbo].[MeterMeasurementHistory], DATA_CONSISTENCY_CHECK=ON)); +WITH (DURABILITY = SCHEMA_ONLY, MEMORY_OPTIMIZED = ON); diff --git a/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurementHistory.sql b/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurementHistory.sql index 2f9f5e1d1f..9c6f1966df 100644 --- a/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurementHistory.sql +++ b/samples/applications/iot-smart-grid/Db/dbo/Tables/MeterMeasurementHistory.sql @@ -2,9 +2,7 @@ [MeterID] INT NOT NULL, [MeasurementInkWh] DECIMAL (9, 4) NOT NULL, [PostalCode] NVARCHAR (10) NOT NULL, - [MeasurementDate] DATETIME2 (7) NOT NULL, - [SysStartTime] DATETIME2 (7) NOT NULL, - [SysEndTime] DATETIME2 (7) NOT NULL + [MeasurementDate] DATETIME2 (7) NOT NULL ); diff --git a/samples/applications/iot-smart-grid/Db/dbo/User Defined Types/udtMeterMeasurement.sql b/samples/applications/iot-smart-grid/Db/dbo/User Defined Types/udtMeterMeasurement.sql index 6ef9f96379..19a2515e8c 100644 --- a/samples/applications/iot-smart-grid/Db/dbo/User Defined Types/udtMeterMeasurement.sql +++ b/samples/applications/iot-smart-grid/Db/dbo/User Defined Types/udtMeterMeasurement.sql @@ -4,6 +4,6 @@ [MeasurementInkWh] DECIMAL (9, 4) NOT NULL, [PostalCode] NVARCHAR (10) NOT NULL, [MeasurementDate] DATETIME2 (7) NOT NULL, - INDEX [IX_RowID] NONCLUSTERED HASH ([RowID]) WITH (BUCKET_COUNT = 1024)) + INDEX [IX_RowID] NONCLUSTERED HASH ([RowID]) WITH (BUCKET_COUNT = 131072)) WITH (MEMORY_OPTIMIZED = ON); diff --git a/samples/applications/iot-smart-grid/Db/dbo/Views/vwMeterMeasurement.sql b/samples/applications/iot-smart-grid/Db/dbo/Views/vwMeterMeasurement.sql index 80605f852e..9d1047b50d 100644 --- a/samples/applications/iot-smart-grid/Db/dbo/Views/vwMeterMeasurement.sql +++ b/samples/applications/iot-smart-grid/Db/dbo/Views/vwMeterMeasurement.sql @@ -1,4 +1,4 @@ -CREATE VIEW vwMeterMeasurement +CREATE VIEW [dbo].[vwMeterMeasurement] AS SELECT PostalCode, DATETIMEFROMPARTS( @@ -12,7 +12,7 @@ SELECT PostalCode, ) AS MeasurementDate, count(*) AS MeterCount, AVG(MeasurementInkWh) AS AvgMeasurementInkWh -FROM [dbo].[MeterMeasurement] FOR SYSTEM_TIME ALL WITH (NOLOCK) +FROM [dbo].[MeterMeasurement] WITH (NOLOCK) GROUP BY PostalCode, DATETIMEFROMPARTS( @@ -21,4 +21,4 @@ GROUP BY DAY(MeasurementDate), DATEPART(HOUR,MeasurementDate), DATEPART(MINUTE,MeasurementDate), - DATEPART(ss,MeasurementDate)/1,0) \ No newline at end of file + DATEPART(ss,MeasurementDate)/1,0) \ No newline at end of file diff --git a/samples/applications/iot-smart-grid/IoT-Smart-Grid.sln b/samples/applications/iot-smart-grid/IoT-Smart-Grid.sln index 8032e39c63..c591989195 100644 --- a/samples/applications/iot-smart-grid/IoT-Smart-Grid.sln +++ b/samples/applications/iot-smart-grid/IoT-Smart-Grid.sln @@ -1,22 +1,23 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataGenerator", "DataGenerator\DataGenerator.csproj", "{D871B062-06A7-49E3-8BCD-8465B772FC52}" EndProject -Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Db", "Db\Db.sqlproj", "{074BB065-0CC1-438A-A963-EA3FD781BA2A}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormsClient", "WinFormsClient\WinFormsClient.csproj", "{6C0E1820-A10B-47DA-B806-939CBCD0DD39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleClient", "ConsoleClient\ConsoleClient.csproj", "{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2D761B8A-EE55-4825-B19D-F3B04860D9D7}" ProjectSection(SolutionItems) = preProject App.config = App.config PowerDashboard.pbix = PowerDashboard.pbix + setup-or-reset-demo.sql = setup-or-reset-demo.sql EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleClient", "ConsoleClient\ConsoleClient.csproj", "{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}" +EndProject +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Db", "Db\Db.sqlproj", "{8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,12 +28,6 @@ Global {D871B062-06A7-49E3-8BCD-8465B772FC52}.Debug|Any CPU.Build.0 = Debug|Any CPU {D871B062-06A7-49E3-8BCD-8465B772FC52}.Release|Any CPU.ActiveCfg = Release|Any CPU {D871B062-06A7-49E3-8BCD-8465B772FC52}.Release|Any CPU.Build.0 = Release|Any CPU - {074BB065-0CC1-438A-A963-EA3FD781BA2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {074BB065-0CC1-438A-A963-EA3FD781BA2A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {074BB065-0CC1-438A-A963-EA3FD781BA2A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {074BB065-0CC1-438A-A963-EA3FD781BA2A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {074BB065-0CC1-438A-A963-EA3FD781BA2A}.Release|Any CPU.Build.0 = Release|Any CPU - {074BB065-0CC1-438A-A963-EA3FD781BA2A}.Release|Any CPU.Deploy.0 = Release|Any CPU {6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Debug|Any CPU.Build.0 = Debug|Any CPU {6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -41,8 +36,17 @@ Global {BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Debug|Any CPU.Build.0 = Debug|Any CPU {BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Release|Any CPU.Build.0 = Release|Any CPU + {8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}.Release|Any CPU.Build.0 = Release|Any CPU + {8A2D24DB-3EFA-4FAC-84FC-B2EE1A760221}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {66D3191A-D556-47CB-ABE0-EFCDE79E3149} + EndGlobalSection EndGlobal diff --git a/samples/applications/iot-smart-grid/README.md b/samples/applications/iot-smart-grid/README.md index 9e25316691..a374b6ce3d 100644 --- a/samples/applications/iot-smart-grid/README.md +++ b/samples/applications/iot-smart-grid/README.md @@ -1,6 +1,8 @@ # IoT Smart Grid This code sample demonstrates how a SQL Server 2016 (or higher) memory optimized database could be used to ingest a very high input data rate and ultimately help improve the performance of applications with this scenario. The code simulates an IoT Smart Grid scenario where multiple IoT power meters are constantly sending electricity usage measurements to the database. +![Alt text](Screenshots/RowsInserted.png "Windows Forms Data Generator") + ### Contents [About this sample](#about-this-sample)
@@ -18,7 +20,6 @@ This code sample demonstrates how a SQL Server 2016 (or higher) memory optimized 2. **Key features:** - Memory Optimized Tables and Table valued Parameters (TVPs) - Natively Compiled Stored Procedures - - System-Versioned Temporal Tables - Clustered Columnstore Index (CCI) - Power BI 3. **Workload:** Data Ingestion for IoT @@ -54,20 +55,25 @@ To run this sample, you need the following prerequisites. 5. Modify the **App.config Settings** (located in the **Solution Items** solution folder) - By accepting the default values for the following settings you should be able to see a data generated spike every 35 seconds with a 4 second duration. If you want to produce a continuous high data volume workload you should set the **commandDelay to 0** and adjust the **numberOfTasks** and **batchSize** according to the hardware specifications of your environment. + You might want to adjust the configuration settings according to the hardware specifications of your environment. - **Db**: SQL Server connectionString. Currently it is configured to connect to the local default SQL Server Instance using Integrated Security. - **insertSPName**: The name of the Natively Compiled Stored Procedure that inserted the sample data. (Default Value: InsertMeterMeasurement) - - **numberOfTasks**: The number of Asynchronous Tasks the Data Generator uses. (Default Value: 50) - - **numberOfMeters**: The number of IoT Power Meters to be used. (Default Value: 1000000) - - **batchSize**: The sample data batch size.(Default Value: 1000) - - **commandDelay**: The delay between sql calls. Note that during a data generated spike the app changes this to 0. (Default Value: 1500ms) + - **deleteSPName**: The name of the Stored Procedure that offloads historical data to a columnstore index (Default Value: InsertMeterMeasurementHistory) + - **numberOfDataLoadTasks**: The number of Asynchronous Tasks the Data Generator uses. + - **numberOfOffLoadTasks**: The number of Asynchronous Tasks the Data Generator uses. + - **numberOfMeters**: The number of IoT Power Meters to be used. + - **batchSize**: The sample data batch size. + - **dataLoadCommandDelay**: The delay between sql calls. + - **offLoadCommandDelay**: The delay between offload calls. + - **deleteBatchSize**: Delete Batch size - **enableShock**: Flag that turns on/off the data shock. This should be set to 0 for max high volume workload - - **commandTimeout**: SQL Command Timeout(Default Value: 600) - - **shockFrequency**: How often to generate a data spike. (Default Value: 35000ms) - - **shockDuration**: The duration of the data spike. (Default Value: 4000ms) + - **commandTimeout**: SQL Command Timeout - **rpsFrequency**: The polling frequency for Rows per Second. (Default Value: 2000ms) + - **delayStart**: Delay Graph Interval - **logFileName**: Log File Name. (Default Value: log.txt) + - **appRunDuration**: Run App duration time + - **numberOfRowsOfloadLimit**: Number Of Rows Ofload Limit - **powerBIDesktopPath**: The local path to the PBIDesktop.exe. (Default Value: C:\Program Files\Microsoft Power BI Desktop\bin\PBIDesktop.exe) 6. Publish the Database @@ -97,7 +103,7 @@ To run this sample, you need the following prerequisites. **High Level Description** -This code sample simulates an IoT Smart Grid scenario where multiple IoT power meters are sending electricity usage measurements to a SQL Server memory optimized database. The Data Generator, that can be started either from the Console or the Windows Form client, produces a data generated spike to simulate a [shock absorber scenario] (https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/). Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter. It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table. In addition to the in-memory features, the sample is leveraging [System-Versioned Temporal Tables](https://msdn.microsoft.com/en-us/library/dn935015.aspx) for building version history, [Clustered Columnstore Index](https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and [Power BI](https://powerbi.microsoft.com/en-us/desktop/) for data visualization. +This code sample simulates an IoT Smart Grid scenario where multiple IoT power meters are sending electricity usage measurements to a SQL Server memory optimized database. The Data Generator can be started either from the Console or the Windows Form client. Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter. It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table. In addition to the in-memory features, the sample is leveraging [Clustered Columnstore Index](https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and [Power BI](https://powerbi.microsoft.com/en-us/desktop/) for data visualization. ![Alt text](Screenshots/WinFormsClient.png "Windows Forms Data Generator") ![Alt text](Screenshots/ConsoleClient.png "Console Data Generator") @@ -123,6 +129,5 @@ The code included in this sample is not intended to be a set of best practices o For more information, see these articles: - [In-Memory OLTP (In-Memory Optimization)] (https://msdn.microsoft.com/en-us/library/dn133186.aspx) - [OLTP and database management] (https://www.microsoft.com/en-us/server-cloud/solutions/oltp-database-management.aspx) -- [SQL Server 2016 Temporal Tables] (https://msdn.microsoft.com/en-us/library/dn935015.aspx) - [In-Memory OLTP Common Design Pattern – High Data Input Rate/Shock Absorber] (https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/) - [Power BI Download] (https://powerbi.microsoft.com/en-us/desktop/) diff --git a/samples/applications/iot-smart-grid/Screenshots/RowsInserted.png b/samples/applications/iot-smart-grid/Screenshots/RowsInserted.png new file mode 100644 index 0000000000000000000000000000000000000000..1d836798c3e568c59c01e5d3fd08cd501838a722 GIT binary patch literal 14925 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 literal 0 HcmV?d00001 diff --git a/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.Designer.cs b/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.Designer.cs index e65dff519d..ae5310934f 100644 --- a/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.Designer.cs +++ b/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.Designer.cs @@ -29,32 +29,21 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); - System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); - System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.DataPoint dataPoint1 = new System.Windows.Forms.DataVisualization.Charting.DataPoint(0D, 0D); + System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea3 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); + System.Windows.Forms.DataVisualization.Charting.Legend legend3 = new System.Windows.Forms.DataVisualization.Charting.Legend(); + System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.DataPoint dataPoint3 = new System.Windows.Forms.DataVisualization.Charting.DataPoint(0D, 0D); this.bottomToolStrip = new System.Windows.Forms.ToolStrip(); this.lblTasksTitle = new System.Windows.Forms.ToolStripLabel(); this.lblTasksValue = new System.Windows.Forms.ToolStripLabel(); - this.tss_1 = new System.Windows.Forms.ToolStripSeparator(); - this.lblBatchSizeTitle = new System.Windows.Forms.ToolStripLabel(); - this.lblBatchSizeValue = new System.Windows.Forms.ToolStripLabel(); - this.tss_2 = new System.Windows.Forms.ToolStripSeparator(); - this.lblFrequencyTitle = new System.Windows.Forms.ToolStripLabel(); - this.lblFrequencyValue = new System.Windows.Forms.ToolStripLabel(); - this.tss_3 = new System.Windows.Forms.ToolStripSeparator(); - this.lblMetersTitle = new System.Windows.Forms.ToolStripLabel(); - this.lblMetersValue = new System.Windows.Forms.ToolStripLabel(); - this.tss_5 = new System.Windows.Forms.ToolStripSeparator(); - this.lblRpsTitle = new System.Windows.Forms.ToolStripLabel(); - this.lblRpsValue = new System.Windows.Forms.ToolStripLabel(); this.Start = new System.Windows.Forms.Button(); this.Stop = new System.Windows.Forms.Button(); - this.RpsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.rpsTimer = new System.Windows.Forms.Timer(this.components); - this.mainTimer = new System.Windows.Forms.Timer(this.components); - this.shockTimer = new System.Windows.Forms.Timer(this.components); - this.powerBIReport = new System.Windows.Forms.LinkLabel(); + this.Reset = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.lblRpsValue = new System.Windows.Forms.Label(); + this.stopTimer = new System.Windows.Forms.Timer(this.components); + this.RpsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.bottomToolStrip.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.RpsChart)).BeginInit(); this.SuspendLayout(); @@ -63,120 +52,41 @@ private void InitializeComponent() // this.bottomToolStrip.BackColor = System.Drawing.Color.White; this.bottomToolStrip.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomToolStrip.ImageScalingSize = new System.Drawing.Size(24, 24); this.bottomToolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.lblTasksTitle, - this.lblTasksValue, - this.tss_1, - this.lblBatchSizeTitle, - this.lblBatchSizeValue, - this.tss_2, - this.lblFrequencyTitle, - this.lblFrequencyValue, - this.tss_3, - this.lblMetersTitle, - this.lblMetersValue, - this.tss_5, - this.lblRpsTitle, - this.lblRpsValue}); - this.bottomToolStrip.Location = new System.Drawing.Point(0, 303); + this.lblTasksValue}); + this.bottomToolStrip.Location = new System.Drawing.Point(0, 505); this.bottomToolStrip.Name = "bottomToolStrip"; - this.bottomToolStrip.Size = new System.Drawing.Size(847, 25); + this.bottomToolStrip.Size = new System.Drawing.Size(1336, 25); this.bottomToolStrip.TabIndex = 0; this.bottomToolStrip.Text = "toolStrip1"; // // lblTasksTitle // - this.lblTasksTitle.ForeColor = System.Drawing.Color.Gray; + this.lblTasksTitle.Font = new System.Drawing.Font("Segoe UI", 12F); + this.lblTasksTitle.ForeColor = System.Drawing.Color.DimGray; this.lblTasksTitle.Name = "lblTasksTitle"; - this.lblTasksTitle.Size = new System.Drawing.Size(38, 22); - this.lblTasksTitle.Text = "Tasks:"; + this.lblTasksTitle.Size = new System.Drawing.Size(129, 22); + this.lblTasksTitle.Text = "Number of Tasks:"; + this.lblTasksTitle.Visible = false; // // lblTasksValue // + this.lblTasksValue.Font = new System.Drawing.Font("Segoe UI", 12F); this.lblTasksValue.Name = "lblTasksValue"; - this.lblTasksValue.Size = new System.Drawing.Size(13, 22); + this.lblTasksValue.Size = new System.Drawing.Size(19, 22); this.lblTasksValue.Text = "0"; - // - // tss_1 - // - this.tss_1.Name = "tss_1"; - this.tss_1.Size = new System.Drawing.Size(6, 25); - // - // lblBatchSizeTitle - // - this.lblBatchSizeTitle.ForeColor = System.Drawing.Color.Gray; - this.lblBatchSizeTitle.Name = "lblBatchSizeTitle"; - this.lblBatchSizeTitle.Size = new System.Drawing.Size(63, 22); - this.lblBatchSizeTitle.Text = "Batch Size:"; - // - // lblBatchSizeValue - // - this.lblBatchSizeValue.Name = "lblBatchSizeValue"; - this.lblBatchSizeValue.Size = new System.Drawing.Size(13, 22); - this.lblBatchSizeValue.Text = "0"; - // - // tss_2 - // - this.tss_2.Name = "tss_2"; - this.tss_2.Size = new System.Drawing.Size(6, 25); - // - // lblFrequencyTitle - // - this.lblFrequencyTitle.ForeColor = System.Drawing.Color.Gray; - this.lblFrequencyTitle.Name = "lblFrequencyTitle"; - this.lblFrequencyTitle.Size = new System.Drawing.Size(179, 22); - this.lblFrequencyTitle.Text = "Shock Frequency/Duration (sec):"; - // - // lblFrequencyValue - // - this.lblFrequencyValue.Name = "lblFrequencyValue"; - this.lblFrequencyValue.Size = new System.Drawing.Size(30, 22); - this.lblFrequencyValue.Text = "0 / 0"; - // - // tss_3 - // - this.tss_3.Name = "tss_3"; - this.tss_3.Size = new System.Drawing.Size(6, 25); - // - // lblMetersTitle - // - this.lblMetersTitle.ForeColor = System.Drawing.Color.Gray; - this.lblMetersTitle.Name = "lblMetersTitle"; - this.lblMetersTitle.Size = new System.Drawing.Size(46, 22); - this.lblMetersTitle.Text = "Meters:"; - // - // lblMetersValue - // - this.lblMetersValue.Name = "lblMetersValue"; - this.lblMetersValue.Size = new System.Drawing.Size(13, 22); - this.lblMetersValue.Text = "0"; - // - // tss_5 - // - this.tss_5.Name = "tss_5"; - this.tss_5.Size = new System.Drawing.Size(6, 25); - // - // lblRpsTitle - // - this.lblRpsTitle.ForeColor = System.Drawing.Color.Gray; - this.lblRpsTitle.Name = "lblRpsTitle"; - this.lblRpsTitle.Size = new System.Drawing.Size(29, 22); - this.lblRpsTitle.Text = "Rps:"; - // - // lblRpsValue - // - this.lblRpsValue.ForeColor = System.Drawing.Color.Red; - this.lblRpsValue.Name = "lblRpsValue"; - this.lblRpsValue.Size = new System.Drawing.Size(13, 22); - this.lblRpsValue.Text = "0"; + this.lblTasksValue.Visible = false; // // Start // this.Start.FlatAppearance.BorderColor = System.Drawing.Color.Silver; this.Start.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.Start.Location = new System.Drawing.Point(720, 268); + this.Start.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Start.Location = new System.Drawing.Point(930, 569); this.Start.Name = "Start"; - this.Start.Size = new System.Drawing.Size(105, 40); + this.Start.Size = new System.Drawing.Size(112, 50); this.Start.TabIndex = 2; this.Start.Text = "Start"; this.Start.UseVisualStyleBackColor = true; @@ -187,96 +97,125 @@ private void InitializeComponent() this.Stop.Enabled = false; this.Stop.FlatAppearance.BorderColor = System.Drawing.Color.Silver; this.Stop.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.Stop.Location = new System.Drawing.Point(609, 268); + this.Stop.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Stop.Location = new System.Drawing.Point(1191, 479); this.Stop.Name = "Stop"; - this.Stop.Size = new System.Drawing.Size(105, 40); + this.Stop.Size = new System.Drawing.Size(111, 41); this.Stop.TabIndex = 3; - this.Stop.Text = "Stop"; + this.Stop.Text = "Close"; this.Stop.UseVisualStyleBackColor = true; this.Stop.Click += new System.EventHandler(this.Stop_Click); // - // RpsChart - // - this.RpsChart.BackColor = System.Drawing.Color.Transparent; - this.RpsChart.BorderlineColor = System.Drawing.Color.Black; - chartArea1.AxisX.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Seconds; - chartArea1.AxisX.LabelAutoFitMaxFontSize = 8; - chartArea1.AxisX.LineColor = System.Drawing.Color.DarkGray; - chartArea1.AxisX.MajorGrid.Enabled = false; - chartArea1.AxisX.MajorGrid.Interval = 0D; - chartArea1.AxisX.MajorGrid.IntervalOffset = 0D; - chartArea1.AxisX.MajorGrid.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Auto; - chartArea1.AxisX.MajorTickMark.Enabled = false; - chartArea1.AxisX.Maximum = 100D; - chartArea1.AxisX.Minimum = 0D; - chartArea1.AxisY.LabelAutoFitMaxFontSize = 8; - chartArea1.AxisY.LineColor = System.Drawing.Color.DarkGray; - chartArea1.AxisY.MajorGrid.Enabled = false; - chartArea1.AxisY.Minimum = 0D; - chartArea1.BackColor = System.Drawing.Color.Transparent; - chartArea1.Name = "Chart"; - this.RpsChart.ChartAreas.Add(chartArea1); - legend1.BackColor = System.Drawing.Color.Transparent; - legend1.Enabled = false; - legend1.ForeColor = System.Drawing.Color.Maroon; - legend1.Name = "Legend1"; - this.RpsChart.Legends.Add(legend1); - this.RpsChart.Location = new System.Drawing.Point(0, 0); - this.RpsChart.Name = "RpsChart"; - this.RpsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None; - series1.ChartArea = "Chart"; - series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine; - series1.Color = System.Drawing.Color.Red; - series1.Font = new System.Drawing.Font("Microsoft Sans Serif", 6F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - series1.Legend = "Legend1"; - series1.LegendText = "sadsaDS"; - series1.MarkerBorderWidth = 3; - series1.Name = "RPS"; - series1.Points.Add(dataPoint1); - this.RpsChart.Series.Add(series1); - this.RpsChart.Size = new System.Drawing.Size(847, 262); - this.RpsChart.TabIndex = 102; - this.RpsChart.Text = "Rows / Sec"; - // // rpsTimer // - this.rpsTimer.Interval = 300; + this.rpsTimer.Interval = 500; this.rpsTimer.Tick += new System.EventHandler(this.rpsTimer_Tick); // - // mainTimer + // Reset + // + this.Reset.BackColor = System.Drawing.Color.White; + this.Reset.FlatAppearance.BorderColor = System.Drawing.Color.Silver; + this.Reset.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.Reset.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Reset.Location = new System.Drawing.Point(764, 569); + this.Reset.Name = "Reset"; + this.Reset.Size = new System.Drawing.Size(160, 50); + this.Reset.TabIndex = 103; + this.Reset.Text = "Setup/Reset DB"; + this.Reset.UseVisualStyleBackColor = false; + this.Reset.Click += new System.EventHandler(this.Reset_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 22F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.ForeColor = System.Drawing.Color.DimGray; + this.label1.Location = new System.Drawing.Point(101, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(255, 36); + this.label1.TabIndex = 104; + this.label1.Text = "rows inserted/sec:"; // - this.mainTimer.Tick += new System.EventHandler(this.mainTimer_Tick); + // lblRpsValue // - // shockTimer + this.lblRpsValue.AutoSize = true; + this.lblRpsValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 27.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblRpsValue.ForeColor = System.Drawing.Color.Red; + this.lblRpsValue.Location = new System.Drawing.Point(351, 12); + this.lblRpsValue.Name = "lblRpsValue"; + this.lblRpsValue.Size = new System.Drawing.Size(39, 42); + this.lblRpsValue.TabIndex = 105; + this.lblRpsValue.Text = "0"; // - this.shockTimer.Interval = 5000; - this.shockTimer.Tick += new System.EventHandler(this.shockTimer_Tick); + // stopTimer // - // powerBIReport + this.stopTimer.Interval = 6000; + this.stopTimer.Tick += new System.EventHandler(this.stopTimer_Tick); // - this.powerBIReport.AutoSize = true; - this.powerBIReport.Location = new System.Drawing.Point(47, 260); - this.powerBIReport.Name = "powerBIReport"; - this.powerBIReport.Size = new System.Drawing.Size(85, 13); - this.powerBIReport.TabIndex = 103; - this.powerBIReport.TabStop = true; - this.powerBIReport.Text = "Power BI Report"; - this.powerBIReport.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.powerBIReport_LinkClicked); + // RpsChart + // + this.RpsChart.BackColor = System.Drawing.Color.Transparent; + this.RpsChart.BorderlineColor = System.Drawing.Color.Black; + chartArea3.AxisX.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Seconds; + chartArea3.AxisX.LabelAutoFitMaxFontSize = 8; + chartArea3.AxisX.LineColor = System.Drawing.Color.DimGray; + chartArea3.AxisX.MajorGrid.Enabled = false; + chartArea3.AxisX.MajorGrid.Interval = 0D; + chartArea3.AxisX.MajorGrid.IntervalOffset = 0D; + chartArea3.AxisX.MajorGrid.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Auto; + chartArea3.AxisX.MajorTickMark.Enabled = false; + chartArea3.AxisX.Maximum = 100D; + chartArea3.AxisX.Minimum = 0D; + chartArea3.AxisX.Title = "Seconds"; + chartArea3.AxisX.TitleFont = new System.Drawing.Font("Tahoma", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + chartArea3.AxisY.LabelAutoFitMaxFontSize = 8; + chartArea3.AxisY.LineColor = System.Drawing.Color.DimGray; + chartArea3.AxisY.MajorGrid.Enabled = false; + chartArea3.AxisY.Minimum = 0D; + chartArea3.AxisY.Title = "Number Of Rows"; + chartArea3.AxisY.TitleFont = new System.Drawing.Font("Tahoma", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + chartArea3.BackColor = System.Drawing.Color.Transparent; + chartArea3.Name = "Chart"; + this.RpsChart.ChartAreas.Add(chartArea3); + legend3.BackColor = System.Drawing.Color.Transparent; + legend3.Enabled = false; + legend3.ForeColor = System.Drawing.Color.Maroon; + legend3.Name = "Legend1"; + this.RpsChart.Legends.Add(legend3); + this.RpsChart.Location = new System.Drawing.Point(12, 56); + this.RpsChart.Name = "RpsChart"; + this.RpsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None; + series3.ChartArea = "Chart"; + series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Area; + series3.Color = System.Drawing.Color.DimGray; + series3.Font = new System.Drawing.Font("Microsoft Sans Serif", 6F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + series3.Legend = "Legend1"; + series3.LegendText = "sadsaDS"; + series3.MarkerBorderWidth = 3; + series3.Name = "RPS"; + series3.Points.Add(dataPoint3); + this.RpsChart.Series.Add(series3); + this.RpsChart.Size = new System.Drawing.Size(1324, 419); + this.RpsChart.TabIndex = 106; + this.RpsChart.Text = "Rows / Sec"; // // FrmMain // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(847, 328); - this.Controls.Add(this.powerBIReport); + this.ClientSize = new System.Drawing.Size(1336, 530); this.Controls.Add(this.RpsChart); + this.Controls.Add(this.lblRpsValue); + this.Controls.Add(this.label1); + this.Controls.Add(this.Reset); this.Controls.Add(this.Stop); this.Controls.Add(this.Start); this.Controls.Add(this.bottomToolStrip); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Name = "FrmMain"; - this.Text = "Data Generator Client"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "IoT Smart City - Event Monitoring"; this.bottomToolStrip.ResumeLayout(false); this.bottomToolStrip.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.RpsChart)).EndInit(); @@ -290,25 +229,14 @@ private void InitializeComponent() private System.Windows.Forms.ToolStrip bottomToolStrip; private System.Windows.Forms.ToolStripLabel lblTasksTitle; private System.Windows.Forms.ToolStripLabel lblTasksValue; - private System.Windows.Forms.ToolStripSeparator tss_1; - private System.Windows.Forms.ToolStripLabel lblBatchSizeTitle; - private System.Windows.Forms.ToolStripLabel lblBatchSizeValue; - private System.Windows.Forms.ToolStripSeparator tss_2; - private System.Windows.Forms.ToolStripLabel lblFrequencyTitle; - private System.Windows.Forms.ToolStripLabel lblFrequencyValue; private System.Windows.Forms.Button Start; private System.Windows.Forms.Button Stop; - private System.Windows.Forms.DataVisualization.Charting.Chart RpsChart; - private System.Windows.Forms.ToolStripLabel lblRpsTitle; - private System.Windows.Forms.ToolStripLabel lblRpsValue; private System.Windows.Forms.Timer rpsTimer; - private System.Windows.Forms.Timer mainTimer; - private System.Windows.Forms.ToolStripLabel lblMetersTitle; - private System.Windows.Forms.ToolStripLabel lblMetersValue; - private System.Windows.Forms.ToolStripSeparator tss_5; - private System.Windows.Forms.Timer shockTimer; - private System.Windows.Forms.ToolStripSeparator tss_3; - private System.Windows.Forms.LinkLabel powerBIReport; + private System.Windows.Forms.Button Reset; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblRpsValue; + private System.Windows.Forms.Timer stopTimer; + private System.Windows.Forms.DataVisualization.Charting.Chart RpsChart; } } diff --git a/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.cs b/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.cs index 4b2457d63c..07a9d92fe6 100644 --- a/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.cs +++ b/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.cs @@ -20,6 +20,9 @@ OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. using System.IO; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; +using System.Data; +using System.Data.SqlClient; +using System.Threading.Tasks; /*---------------------------------------------------------------------------------- High Level Scenario: @@ -33,8 +36,7 @@ This code sample simulates an IoT Smart Grid scenario where multiple IoT power m shock absorber scenario: https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/. Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter. It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table. -In addition to the in-memory features, the sample is leveraging System-Versioned Temporal Tables: https://msdn.microsoft.com/en-us/library/dn935015.aspx for building version history, -Clustered Columnstore Index: https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and +In addition to the in-memory features, the sample is offloading historical values to a Clustered Columnstore Index: https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and Power BI: https://powerbi.microsoft.com/en-us/desktop/ for data visualization. */ namespace Client @@ -42,27 +44,34 @@ namespace Client public partial class FrmMain : Form { private SqlDataGenerator dataGenerator; - private string connection; + private string[] connection; private string spName; private string logFileName; - private string powerBIDesktopPath; - private int tasks; + private int numberOfDataLoadTasks; + private int numberOfOffLoadTasks; + private int meters; private int batchSize; - private int delay; + private int deleteBatchSize; + + private string deleteSPName; + private int dataLoadCommandDelay; + private int offLoadCommandDelay; + private int commandTimeout; - private int shockFrequency; - private int shockDuration; private int rpsFrequency; private int rpsChartTime = 0; - private int enableShock; + private int delayStart; + private int appRunDuration; + private int numberOfRowsOfloadLimit; public FrmMain() { InitializeComponent(); Init(); - this.dataGenerator = new SqlDataGenerator(this.connection, this.spName, this.commandTimeout, this.meters, this.tasks, this.delay, this.batchSize, this.ExceptionCallback); + this.dataGenerator = new SqlDataGenerator(this.connection, this.spName, this.commandTimeout, this.meters, this.numberOfDataLoadTasks, this.dataLoadCommandDelay, this.batchSize, this.deleteSPName, this.numberOfOffLoadTasks, this.offLoadCommandDelay, this.deleteBatchSize, this.numberOfRowsOfloadLimit, this.ExceptionCallback); + StartApp(); } private void ExceptionCallback(int taskId, Exception exception) @@ -75,42 +84,68 @@ private void HandleException(Exception exception, int? taskId = null) string ex = taskId?.ToString() + " - " + exception.Message + (exception.InnerException != null ? "\n\nInner Exception\n" + exception.InnerException : ""); MessageBox.Show(ex, "Invalid Input Parameter", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); - using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine("\r\n{0}: {1}", DateTime.Now, ex); } + ////using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine("\r\n{0}: {1}", DateTime.Now, ex); } } - private async void Start_Click(object sender, EventArgs e) + private async void StartApp() { - try + if (!dataGenerator.IsRunning) { - if (enableShock == 1) this.mainTimer.Start(); - this.rpsTimer.Start(); + using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine("\r\n{0}: {1}", DateTime.Now, "Start"); } + + this.stopTimer.Start(); this.Stop.Enabled = true; this.Stop.Update(); this.Start.Enabled = false; this.Start.Update(); + this.Reset.Enabled = false; + this.Reset.Update(); - await this.dataGenerator.RunAsync(); + this.dataGenerator.RunAsync(); + await Task.Delay(this.delayStart); + this.rpsTimer.Start(); + } + } + private async void Start_Click(object sender, EventArgs e) + { + try + { + StartApp(); } catch (Exception exception) { HandleException(exception); } } - private async void Stop_Click(object sender, EventArgs e) + private void StopApp() { - try - { + if (dataGenerator.IsRunning) + { + using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine("\r\n{0}: {1}", DateTime.Now, "Stop - Successful run"); } + this.UpdateChart(-1); - if (enableShock == 1) this.mainTimer.Stop(); this.rpsTimer.Stop(); - if (enableShock == 1) this.shockTimer.Stop(); this.lblRpsValue.Text = "0"; this.lblTasksValue.Text = "0"; this.Stop.Enabled = false; this.Stop.Update(); this.Start.Enabled = true; this.Start.Update(); - - await this.dataGenerator.StopAsync(); + this.Reset.Enabled = true; + this.Reset.Update(); + + this.dataGenerator.StopAsync(); this.dataGenerator.RpsReset(); + + this.WindowState = FormWindowState.Minimized; + //ResetDb(); + } + } + + private void Stop_Click(object sender, EventArgs e) + { + try + { + StopApp(); + Application.Exit(); } catch (Exception exception) { HandleException(exception); } } @@ -139,48 +174,56 @@ private void Init() { try { + int numberOfSqlConnections = ConfigurationManager.ConnectionStrings.Count; + connection = new string[numberOfSqlConnections]; // Read Config Settings - this.connection = ConfigurationManager.ConnectionStrings["Db"].ConnectionString; + for (int i = 0; i < numberOfSqlConnections; i++) + { + connection[i] = ConfigurationManager.ConnectionStrings[i].ConnectionString; + } + this.spName = ConfigurationManager.AppSettings["insertSPName"]; this.logFileName = ConfigurationManager.AppSettings["logFileName"]; - this.powerBIDesktopPath = ConfigurationManager.AppSettings["powerBIDesktopPath"]; - this.tasks = int.Parse(ConfigurationManager.AppSettings["numberOfTasks"]); - this.meters = int.Parse(ConfigurationManager.AppSettings["numberOfMeters"]); + this.numberOfDataLoadTasks = int.Parse(ConfigurationManager.AppSettings["numberOfDataLoadTasks"]); + this.dataLoadCommandDelay = int.Parse(ConfigurationManager.AppSettings["dataLoadCommandDelay"]); this.batchSize = int.Parse(ConfigurationManager.AppSettings["batchSize"]); - this.delay = int.Parse(ConfigurationManager.AppSettings["commandDelay"]); - this.commandTimeout = int.Parse(ConfigurationManager.AppSettings["commandTimeout"]); - this.shockFrequency = int.Parse(ConfigurationManager.AppSettings["shockFrequency"]); - this.shockDuration = int.Parse(ConfigurationManager.AppSettings["shockDuration"]); - this.enableShock = int.Parse(ConfigurationManager.AppSettings["enableShock"]); + this.deleteSPName = ConfigurationManager.AppSettings["deleteSPName"]; + this.numberOfOffLoadTasks = int.Parse(ConfigurationManager.AppSettings["numberOfOffLoadTasks"]); + this.offLoadCommandDelay = int.Parse(ConfigurationManager.AppSettings["offLoadCommandDelay"]); + this.deleteBatchSize = int.Parse(ConfigurationManager.AppSettings["deleteBatchSize"]); + + this.meters = int.Parse(ConfigurationManager.AppSettings["numberOfMeters"]); + + this.commandTimeout = int.Parse(ConfigurationManager.AppSettings["commandTimeout"]); + this.delayStart = int.Parse(ConfigurationManager.AppSettings["delayStart"]); + this.appRunDuration = int.Parse(ConfigurationManager.AppSettings["appRunDuration"]); this.rpsFrequency = int.Parse(ConfigurationManager.AppSettings["rpsFrequency"]); + numberOfRowsOfloadLimit = int.Parse(ConfigurationManager.AppSettings["numberOfRowsOfloadLimit"]); // Initialize Timers - this.mainTimer.Interval = shockFrequency; - this.shockTimer.Interval = shockDuration; this.rpsTimer.Interval = this.rpsFrequency; + this.stopTimer.Interval = this.appRunDuration; // Initialize Labels - this.lblTasksValue.Text = string.Format("{0:#,#}", this.tasks).ToString(); - this.lblFrequencyValue.Text = (this.shockFrequency/1000).ToString() + "/" + (this.shockDuration / 1000).ToString(); - this.lblBatchSizeValue.Text = string.Format("{0:#,#}", this.batchSize).ToString(); - this.lblMetersValue.Text = string.Format("{0:#,#}", this.meters).ToString(); - + this.lblTasksValue.Text = string.Format("{0:#,#}", this.numberOfDataLoadTasks).ToString(); + if (batchSize <= 0) throw new SqlDataGeneratorException("The Batch Size cannot be less or equal to zero."); - if (tasks <= 0) throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero."); + if (numberOfDataLoadTasks <= 0) throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero."); - if (delay < 0) throw new SqlDataGeneratorException("Delay cannot be less than zero"); + if (dataLoadCommandDelay < 0) throw new SqlDataGeneratorException("Delay cannot be less than zero"); if (meters <= 0) throw new SqlDataGeneratorException("Number Of Meters cannot be less than zero"); - if (meters < batchSize * tasks) throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize)."); + if (meters < batchSize * numberOfDataLoadTasks) throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize)."); + } catch (Exception exception) { HandleException(exception); } } private void rpsTimer_Tick(object sender, EventArgs e) - { + { try { this.lblTasksValue.Text = this.dataGenerator.RunningTasks.ToString(); @@ -195,34 +238,64 @@ private void rpsTimer_Tick(object sender, EventArgs e) this.lblRpsValue.Text = string.Format("{0:#,#}", rps).ToString(); UpdateChart(rps); } - } } catch (Exception exception) { HandleException(exception); } } - private void mainTimer_Tick(object sender, EventArgs e) + private void Reset_Click(object sender, EventArgs e) + { + ResetDb(); + } + + private void ResetDb() { - if (this.dataGenerator.IsRunning) + try { - this.dataGenerator.Delay = 0; - this.shockTimer.Start(); + this.Stop.Text = "Stopping..."; + this.Stop.Update(); + + string script = File.ReadAllText(@"setup-or-reset-demo.sql"); + + int numberOfSqlConnections = ConfigurationManager.ConnectionStrings.Count; + for (int i = 0; i < numberOfSqlConnections; i++) + { + using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[i].ConnectionString)) + { + connection.Open(); + + using (SqlCommand command = new SqlCommand()) + { + command.Connection = connection; + command.CommandType = CommandType.Text; + command.CommandTimeout = 1800; + command.CommandText = script; + command.ExecuteNonQuery(); + } + } + } + } + catch (Exception exception) { HandleException(exception); } + finally + { + this.Stop.Text = "Close"; + this.Stop.Update(); } } - private void shockTimer_Tick(object sender, EventArgs e) + private void stopTimer_Tick(object sender, EventArgs e) { - Random rand = new Random(); - this.dataGenerator.Delay = rand.Next(1500,3000); - this.shockTimer.Stop(); + try + { + StopApp(); + Application.Exit(); + } + catch (Exception exception) { HandleException(exception); } } - private void powerBIReport_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - ProcessStartInfo psi = new ProcessStartInfo(); - psi.FileName = this.powerBIDesktopPath; - psi.Arguments = @"Reports\PowerDashboard.pbix"; - Process.Start(psi); + private void RpsChart_Click(object sender, EventArgs e) + { + StartApp(); } } } diff --git a/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.resx b/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.resx index 717154aff0..bbffc64c00 100644 --- a/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.resx +++ b/samples/applications/iot-smart-grid/WinFormsClient/FrmMain.resx @@ -118,15 +118,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 17, 17 + 161, 17 - 159, 17 + 358, 17 - - 258, 17 + + 17, 17 - - 367, 17 + + 81 \ No newline at end of file diff --git a/samples/applications/iot-smart-grid/WinFormsClient/WinFormsClient.csproj b/samples/applications/iot-smart-grid/WinFormsClient/WinFormsClient.csproj index 0043366869..047e63aef4 100644 --- a/samples/applications/iot-smart-grid/WinFormsClient/WinFormsClient.csproj +++ b/samples/applications/iot-smart-grid/WinFormsClient/WinFormsClient.csproj @@ -71,9 +71,7 @@ App.config - - - Reports\PowerDashboard.pbix + Designer PreserveNewest @@ -89,9 +87,15 @@ {d871b062-06a7-49e3-8bcd-8465b772fc52} - DataGenerator + EventMonitoring + + + setup-or-reset-demo.sql + PreserveNewest + +