From 46f242adae134aa6e7f7bedba13be663af9bf6c8 Mon Sep 17 00:00:00 2001 From: Vasiya Krishnan Date: Fri, 18 Dec 2020 14:02:06 -0800 Subject: [PATCH] windfarm --- README.md | 2 +- .../polybase/DemonstratePolybase.sql | 139 - .../sample-scripts/polybase/README.md | 75 - .../Wind Turbine Demo/.gitattributes | 10 + .../Wind Turbine Demo/.gitignore | 447 + .../Wind Turbine Demo/README.md | 475 + .../deployment/arm-template.json | 383 + .../edge/SensorSolution/.env | 4 + .../edge/SensorSolution/.vscode/launch.json | 43 + .../edge/SensorSolution/.vscode/settings.json | 6 + .../deployment.debug.template.json | 122 + .../modules/SensorModule/.dockerignore | 2 + .../modules/SensorModule/.gitignore | 34 + .../modules/SensorModule/Constants.cs | 56 + .../SensorModule/DataStore/DatabaseContext.cs | 29 + .../SensorModule/DataStructures/RingBuffer.cs | 360 + .../modules/SensorModule/Dockerfile.amd64 | 17 + .../SensorModule/Dockerfile.amd64.debug | 24 + .../modules/SensorModule/Dockerfile.arm32v7 | 17 + .../SensorModule/Dockerfile.arm32v7.debug | 24 + .../SensorModule/Dockerfile.windows-amd64 | 13 + .../SensorModule/Helpers/GeneratorHelper.cs | 316 + .../Models/Interfaces/IWindTurbineRecord.cs | 27 + .../modules/SensorModule/Models/OnnxModel.cs | 15 + .../Models/RealtimeSensorRecord.cs | 16 + .../Models/RealtimeWindTurbineRecord.cs | 35 + .../modules/SensorModule/Models/Sensor.cs | 11 + .../Models/TrainingWindTurbineRecord.cs | 30 + .../modules/SensorModule/Program.cs | 197 + .../modules/SensorModule/SensorModule.csproj | 35 + .../Services/DataExporterService.cs | 144 + .../Services/DataGeneratorService.cs | 215 + .../SensorModule/Services/DataStoreService.cs | 117 + .../Interfaces/IDataExporterService.cs | 10 + .../Interfaces/IDataGeneratorService.cs | 15 + .../Services/Interfaces/IDataStoreService.cs | 18 + .../modules/SensorModule/module.json | 20 + .../edge/sensor-solution.code-workspace | 8 + .../Wind Turbine Demo/images/Github repo.png | Bin 0 -> 55461 bytes .../images/azure-active-directory-option.png | Bin 0 -> 28729 bytes .../Wind Turbine Demo/images/copy.png | Bin 0 -> 1560 bytes .../images/iotedge-vm-dns-name.png | Bin 0 -> 41456 bytes .../images/machine-learning-resource.png | Bin 0 -> 155568 bytes .../ml/data/TrainingDataset.parquet | Bin 0 -> 2811445 bytes .../Wind Turbine Demo/ml/scripts/train.py | 33 + .../Wind Turbine Demo/ml/scripts/utils.py | 91 + .../Wind Turbine Demo/ml/utils.py | 26 + .../ml/wind-turbine-scikit.ipynb | 616 + .../webappsrc/SqlDbEdgeDemoWeb/Rules.ruleset | 23 + .../SqlDbEdgeDemo.Web/.gitignore | 232 + .../SqlDbEdgeDemo.Web/ClientApp/.gitignore | 23 + .../SqlDbEdgeDemo.Web/ClientApp/README.md | 68 + .../ClientApp/package-lock.json | 14318 ++++++++++++++++ .../SqlDbEdgeDemo.Web/ClientApp/package.json | 35 + .../ClientApp/public/favicon.ico | Bin 0 -> 17174 bytes .../ClientApp/public/index.html | 43 + .../ClientApp/public/logo192.png | Bin 0 -> 8581 bytes .../ClientApp/public/logo512.png | Bin 0 -> 22920 bytes .../ClientApp/public/manifest.json | 25 + .../ClientApp/public/robots.txt | 2 + .../SqlDbEdgeDemo.Web/ClientApp/src/App.js | 56 + .../ClientApp/src/api/README.md | 1 + .../ClientApp/src/api/sampleData.js | 4 + .../ClientApp/src/assets/README.md | 3 + .../src/assets/graph-oil-temperature.svg | 82 + .../src/assets/graph-power-generated.svg | 74 + .../ClientApp/src/assets/graph-vibration.svg | 76 + .../src/assets/graph-wind-direction-after.svg | 76 + .../src/assets/graph-wind-direction-farm.svg | 75 + .../assets/graph-wind-direction-turbine.svg | 75 + .../src/assets/graph-wind-speed-after.svg | 94 + .../src/assets/graph-wind-speed-farm.svg | 93 + .../src/assets/graph-wind-speed-turbine.svg | 93 + .../ClientApp/src/assets/grid.svg | 1 + .../ClientApp/src/assets/icon-alerts.svg | 4 + .../ClientApp/src/assets/icon-close.svg | 1 + .../ClientApp/src/assets/icon-dashboard.svg | 3 + .../ClientApp/src/assets/icon-maintenance.svg | 3 + .../ClientApp/src/assets/icon-models.svg | 3 + .../ClientApp/src/assets/icon-refresh.svg | 3 + .../ClientApp/src/assets/icon-reports.svg | 3 + .../ClientApp/src/assets/icon-settings.svg | 3 + .../ClientApp/src/assets/icon-tick.svg | 1 + .../ClientApp/src/assets/line-issue.svg | 1 + .../src/assets/line-oil-temperature.svg | 1 + .../src/assets/line-power-generated.svg | 1 + .../src/assets/line-security-alerts.svg | 1 + .../ClientApp/src/assets/line-vibration.svg | 1 + .../src/assets/line-wind-direction.svg | 1 + .../ClientApp/src/assets/line-wind-speed.svg | 1 + .../ClientApp/src/assets/logo.svg | 10 + .../ClientApp/src/assets/turbine.svg | 1 + .../src/components/Layout/Layout.scss | 63 + .../ClientApp/src/components/Layout/index.js | 51 + .../ClientApp/src/components/README.md | 1 + .../ClientApp/src/components/Toast/Toast.scss | 24 + .../ClientApp/src/components/Toast/index.js | 16 + .../SqlDbEdgeDemo.Web/ClientApp/src/index.js | 8 + .../ClientApp/src/pages/Alerts/Alerts.scss | 264 + .../ClientApp/src/pages/Alerts/index.js | 211 + .../ClientApp/src/pages/Home/Home.scss | 53 + .../ClientApp/src/pages/Home/README.md | 8 + .../Home/components/PulseRIng/PulseRing.scss | 10 + .../pages/Home/components/PulseRIng/index.js | 44 + .../src/pages/Home/components/README.md | 1 + .../Home/components/Turbine/Turbine.scss | 33 + .../pages/Home/components/Turbine/index.js | 52 + .../ClientApp/src/pages/Home/index.js | 202 + .../ClientApp/src/pages/README.md | 1 + .../ClientApp/src/styles/global.scss | 84 + .../Controllers/DeviceController.cs | 27 + .../SqlDbEdgeDemo.Web/Models/DeviceModel.cs | 12 + .../Models/IoTHubModuleTwinProperties.cs | 12 + .../Options/IoTHubOptions.cs | 12 + .../SqlDbEdgeDemo.Web/Pages/Error.cshtml | 26 + .../SqlDbEdgeDemo.Web/Pages/Error.cshtml.cs | 23 + .../Pages/_ViewImports.cshtml | 3 + .../SqlDbEdgeDemo.Web/Program.cs | 24 + .../Interfaces/IIoTHubModuleTwinService.cs | 13 + .../Services/IoTHubModuleTwinService.cs | 49 + .../SqlDbEdgeDemo.Web.csproj | 50 + .../SqlDbEdgeDemo.Web/Startup.cs | 75 + .../appsettings.Development.json | 9 + .../SqlDbEdgeDemo.Web/appsettings.json | 11 + .../SqlDbEdgeDemoWeb/SqlDbEdgeDemo.sln | 34 + samples/demos/azure-sql-edge-demos/readme.md | 2 + .../setup-controller-new.sh | 4 +- .../Asymmetric Key Encryption Algorithm.xml | 352 + .../Backup and Data File Location.xml | 251 + .../Data and Log File Location.xml | 405 + .../sample-policies/Database Auto Close.xml | 330 + .../sample-policies/Database Auto Shrink.xml | 400 + .../sample-policies/Database Collation.xml | 252 + .../sample-policies/Database Page Status.xml | 306 + .../Database Page Verification.xml | 250 + .../sample-policies/Guest Permissions.xml | 404 + .../Last Successful Backup Date.xml | 273 + .../Public Not Granted Server Permissions.xml | 217 + .../epm-framework/sample-policies/README.md | 3 + .../Read-only Database Recovery Model.xml | 436 + ...QL Server 32-bit Affinity Mask Overlap.xml | 268 + ...QL Server 64-bit Affinity Mask Overlap.xml | 316 + .../SQL Server Affinity Mask.xml | 220 + .../SQL Server Blocked Process Threshold.xml | 277 + .../SQL Server Default Trace.xml | 259 + .../SQL Server Dynamic Locks.xml | 258 + ...ty Mask For Non-enterprise SQL Servers.xml | 269 + .../SQL Server Lightweight Pooling.xml | 217 + .../sample-policies/SQL Server Login Mode.xml | 216 + .../SQL Server Max Degree of Parallelism.xml | 216 + ... Threads for SQL Server 2005 and above.xml | 258 + .../SQL Server Network Packet Size.xml | 216 + .../SQL Server Password Expiration.xml | 345 + .../SQL Server Password Policy.xml | 345 + ...tric Key Encryption for User Databases.xml | 397 + .../Symmetric Key for System Databases.xml | 379 + .../Symmetric Key for master Database.xml | 362 + .../sample-policies/Trustworthy Database.xml | 335 + ...Cluster Disk Resource Corruption Error.xml | 247 + ... Event Log Device Driver Control Error.xml | 248 + ...ndows Event Log Device Not Ready Error.xml | 248 + ...ows Event Log Failed I_O Request Error.xml | 248 + .../Windows Event Log I_O Delay Warning.xml | 244 + ...I_O Error During Hard Page Fault Error.xml | 248 + .../Windows Event Log Read Retry Error.xml | 244 + ...t Log Storage System I_O Timeout Error.xml | 248 + ...Windows Event Log System Failure Error.xml | 244 + .../sql-big-data-cluster/deployment/README.md | 3 + .../ubuntu-single-node-vm-ad/setup-bdc-ad.sh | 2 - .../ubuntu-single-node-vm/setup-bdc.sh | 2 - .../push-bdc-images-to-custom-private-repo.py | 2 - .../deployment/openshift/bdc-scc.yaml | 39 + .../deployment/private-aks/README.md | 2 +- .../private-aks/scripts/deploy-bdc.sh | 6 +- .../config-install/installpackage_Spark.ipynb | 2 +- .../Create External Data Source.sql | 2 +- .../Drop External Data Source.sql | 4 +- .../Database/Create Database.sql | 21 +- .../Database/Drop Database.sql | 6 +- .../Alter External Data Source DW.sql | 6 +- .../Create External Data Source DW.sql | 18 +- .../Create External Data Source.sql | 2 +- .../Drop External Data Source.sql | 10 +- .../Create External File Format.sql | 6 +- .../Drop External File Format.sql | 6 +- .../Create Scalar Function DW (New Menu).sql | 6 +- .../Function/Drop Function.sql | 6 +- .../SQL Azure Database/Index/Create Index.sql | 6 +- .../SQL Azure Database/Index/Drop Index.sql | 6 +- .../SQL Azure Database/Login/Create Login.sql | 6 +- .../SQL Azure Database/Login/Drop Login.sql | 6 +- .../SQL Azure Database/Role/Create Role.sql | 6 +- .../Sql/SQL Azure Database/Role/Drop Role.sql | 6 +- .../Schema/Create Schema.sql | 6 +- .../SQL Azure Database/Schema/Drop Schema.sql | 6 +- .../Statistics/Create Statistics.sql | 6 +- .../Statistics/Drop Statistics.sql | 6 +- .../Create Stored Procedure DW (New Menu).sql | 6 +- .../Drop Stored Procedure.sql | 6 +- .../Table/Create External Table DW.sql | 6 +- .../Table/Create Graph Edge Table.sql | 18 +- .../Table/Create SQL DW Partitioned Table.sql | 6 +- .../Table/Create SQL DW Table.sql | 6 +- .../Table/Create System-Versioned Table.sql | 2 +- .../Table/Drop External Table.sql | 6 +- .../SQL Azure Database/Table/Drop Table.sql | 6 +- .../SQL Azure Database/User/Create User.sql | 6 +- .../Sql/SQL Azure Database/User/Drop User.sql | 6 +- .../SQL Azure Database/View/Create View.sql | 6 +- .../Sql/SQL Azure Database/View/Drop View.sql | 6 +- .../Sql/Table/Create Graph Edge Table.sql | 14 +- ...emory Optimized System-Versioned Table.sql | 2 +- .../Table/Create System-Versioned Table.sql | 2 +- .../Sql/Table/drop constraint.sql | 5 + .../features/unicode/DataType_WesternMyth.sql | 19 +- .../notebooks/DataType_WesternMyth.ipynb | 566 +- .../azure-arc-enabled-sql-server/README.md | 30 + .../migrate-to-azure-arc-data.ps1 | 50 + samples/manage/azure-hybrid-benefit/README.md | 61 + .../ahb-usage-in-subscription.ps1 | 112 + .../sql-license-usage.ps1 | 263 + .../SqlAssessmentClient/.gitignore | 350 + .../SqlAssessmentClient/.vscode/launch.json | 27 + .../SqlAssessmentClient/.vscode/tasks.json | 42 + .../AssessmentClient.SMO.csproj | 14 + .../AssessmentClient.SMO/Program.cs | 75 + .../SqlAssessmentClient/AssessmentClient.sln | 25 + .../SqlAssessmentClient/README.md | 224 + .../csharp/Readme.md | 15 + .../AzureSqlColumnstoreSample/Program.cs | 136 + .../AzureSqlEFSample/EFSampleContext.cs | 22 + .../Unix-based/AzureSqlEFSample/Program.cs | 113 + .../Unix-based/AzureSqlEFSample/Task.cs | 18 + .../Unix-based/AzureSqlEFSample/User.cs | 22 + .../Unix-based/AzureSqlSample/Program.cs | 135 + .../AzureSqlSample/Program_KeyVault.cs | 160 + .../csharp/Unix-based/Readme.md | 11 + .../AzureSqlColumnstoreSample/Program.cs | 139 + .../AzureSqlEFSample/EFSampleContext.cs | 16 + .../Windows/AzureSqlEFSample/Program.cs | 110 + .../csharp/Windows/AzureSqlEFSample/Task.cs | 18 + .../csharp/Windows/AzureSqlEFSample/User.cs | 23 + .../csharp/Windows/AzureSqlSample/Program.cs | 135 + .../AzureSqlSample/Program_KeyVault.cs | 160 + .../csharp/Windows/Readme.md | 11 + .../go/Readme.md | 13 + .../go/columnstore.go | 63 + .../go/connect.go | 59 + .../AzureSqlGettingStartedSamples/go/crud.go | 190 + .../AzureSqlGettingStartedSamples/go/orm.go | 103 + .../java/Readme.md | 15 + .../AzureSqlColumnstoreSample/App.java | 117 + .../AzureSqlColumnstoreSample/pom.xml | 50 + .../AzureSqlHibernateSample/App.java | 169 + .../AzureSqlHibernateSample/Task.java | 77 + .../AzureSqlHibernateSample/User.java | 70 + .../AzureSqlHibernateSample/pom.xml | 66 + .../java/Unix-based/AzureSqlSample/App.java | 111 + .../AzureSqlSample/App_KeyVault.java | 126 + .../java/Unix-based/AzureSqlSample/pom.xml | 99 + .../java/Unix-based/Readme.md | 11 + .../AzureSqlColumnstoreSample/App.java | 117 + .../Windows/AzureSqlColumnstoreSample/pom.xml | 50 + .../Windows/AzureSqlHibernateSample/App.java | 169 + .../Windows/AzureSqlHibernateSample/Task.java | 77 + .../Windows/AzureSqlHibernateSample/User.java | 69 + .../Windows/AzureSqlHibernateSample/pom.xml | 66 + .../java/Windows/AzureSqlSample/App.java | 111 + .../Windows/AzureSqlSample/App_KeyVault.java | 126 + .../java/Windows/AzureSqlSample/pom.xml | 99 + .../java/Windows/Readme.md | 11 + .../nodejs/Readme.md | 15 + .../AzureSqlColumnstoreSample/columnstore.js | 99 + .../Unix-based/AzureSqlSample/connect.js | 32 + .../nodejs/Unix-based/AzureSqlSample/crud.js | 150 + .../AzureSqlSample/crud_KeyVault.js | 193 + .../Unix-based/AzureSqlSequelizeSample/orm.js | 147 + .../nodejs/Unix-based/Readme.md | 11 + .../AzureSqlColumnstoreSample/columnstore.js | 101 + .../nodejs/Windows/AzureSqlSample/connect.js | 31 + .../nodejs/Windows/AzureSqlSample/crud.js | 147 + .../Windows/AzureSqlSample/crud_KeyVault.js | 192 + .../Windows/AzureSqlSequelizeSample/orm.js | 147 + .../nodejs/Windows/Readme.md | 11 + .../php/Readme.md | 15 + .../AzureSqlColumnstoreSample/columnstore.php | 41 + .../php/Unix-based/AzureSqlSample/connect.php | 12 + .../php/Unix-based/AzureSqlSample/crud.php | 74 + .../php/Unix-based/Readme.md | 11 + .../AzureSqlColumnstoreSample/columnstore.php | 42 + .../php/Windows/AzureSqlSample/connect.php | 12 + .../php/Windows/AzureSqlSample/crud.php | 74 + .../php/Windows/Readme.md | 11 + .../python/Readme.md | 15 + .../AzureSqlColumnstoreSample/columnstore.py | 29 + .../python/Unix-based/AzureSqlSample/crud.py | 37 + .../AzureSqlSample/crud_KeyVault.py | 50 + .../python/Unix-based/Readme.md | 11 + .../AzureSqlColumnstoreSample/columnstore.py | 29 + .../python/Windows/AzureSqlSample/crud.py | 37 + .../Windows/AzureSqlSample/crud_KeyVault.py | 50 + .../python/Windows/Readme.md | 11 + .../AzureSqlColumnstoreSample/columnstore.rb | 53 + .../ruby/AzureSqlSample/activerecordcrud.rb | 56 + .../ruby/AzureSqlSample/connect.rb | 9 + .../ruby/AzureSqlSample/crud.rb | 45 + .../ruby/Readme.md | 12 + samples/tutorials/README.md | 3 +- samples/tutorials/c#/README.md | 8 + samples/tutorials/f#/README.md | 8 + samples/tutorials/go/README.md | 8 + samples/tutorials/java/README.md | 8 + samples/tutorials/node.js/README.md | 8 + samples/tutorials/php/README.md | 8 + samples/tutorials/python/README.md | 8 + samples/tutorials/ruby/README.md | 9 + 316 files changed, 39624 insertions(+), 766 deletions(-) delete mode 100644 samples/databases/wide-world-importers/sample-scripts/polybase/DemonstratePolybase.sql delete mode 100644 samples/databases/wide-world-importers/sample-scripts/polybase/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitattributes create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitignore create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/deployment/arm-template.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.env create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/launch.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/settings.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/deployment.debug.template.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.dockerignore create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.gitignore create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Constants.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStore/DatabaseContext.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStructures/RingBuffer.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64 create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64.debug create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7 create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7.debug create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.windows-amd64 create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Helpers/GeneratorHelper.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Interfaces/IWindTurbineRecord.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/OnnxModel.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeSensorRecord.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeWindTurbineRecord.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Sensor.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/TrainingWindTurbineRecord.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Program.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/SensorModule.csproj create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataExporterService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataGeneratorService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataStoreService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataExporterService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataGeneratorService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataStoreService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/module.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/sensor-solution.code-workspace create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/Github repo.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/azure-active-directory-option.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/copy.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/iotedge-vm-dns-name.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/machine-learning-resource.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/ml/data/TrainingDataset.parquet create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/ml/scripts/train.py create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/ml/scripts/utils.py create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/ml/utils.py create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/ml/wind-turbine-scikit.ipynb create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/Rules.ruleset create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/.gitignore create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/.gitignore create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/package-lock.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/package.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/public/favicon.ico create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/public/index.html create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/public/logo192.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/public/logo512.png create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/public/manifest.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/public/robots.txt create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/App.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/api/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/api/sampleData.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-oil-temperature.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-power-generated.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-vibration.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-wind-direction-after.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-wind-direction-farm.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-wind-direction-turbine.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-wind-speed-after.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-wind-speed-farm.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/graph-wind-speed-turbine.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/grid.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-alerts.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-close.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-dashboard.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-maintenance.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-models.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-refresh.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-reports.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-settings.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/icon-tick.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-issue.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-oil-temperature.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-power-generated.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-security-alerts.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-vibration.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-wind-direction.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/line-wind-speed.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/logo.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/assets/turbine.svg create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/components/Layout/Layout.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/components/Layout/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/components/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/components/Toast/Toast.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/components/Toast/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Alerts/Alerts.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Alerts/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/Home.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/components/PulseRIng/PulseRing.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/components/PulseRIng/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/components/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/components/Turbine/Turbine.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/components/Turbine/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/Home/index.js create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/pages/README.md create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/src/styles/global.scss create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Controllers/DeviceController.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Models/DeviceModel.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Models/IoTHubModuleTwinProperties.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Options/IoTHubOptions.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Pages/Error.cshtml create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Pages/Error.cshtml.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Pages/_ViewImports.cshtml create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Program.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Services/Interfaces/IIoTHubModuleTwinService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Services/IoTHubModuleTwinService.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/SqlDbEdgeDemo.Web.csproj create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/Startup.cs create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/appsettings.Development.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/appsettings.json create mode 100644 samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.sln create mode 100644 samples/demos/azure-sql-edge-demos/readme.md create mode 100644 samples/features/epm-framework/sample-policies/Asymmetric Key Encryption Algorithm.xml create mode 100644 samples/features/epm-framework/sample-policies/Backup and Data File Location.xml create mode 100644 samples/features/epm-framework/sample-policies/Data and Log File Location.xml create mode 100644 samples/features/epm-framework/sample-policies/Database Auto Close.xml create mode 100644 samples/features/epm-framework/sample-policies/Database Auto Shrink.xml create mode 100644 samples/features/epm-framework/sample-policies/Database Collation.xml create mode 100644 samples/features/epm-framework/sample-policies/Database Page Status.xml create mode 100644 samples/features/epm-framework/sample-policies/Database Page Verification.xml create mode 100644 samples/features/epm-framework/sample-policies/Guest Permissions.xml create mode 100644 samples/features/epm-framework/sample-policies/Last Successful Backup Date.xml create mode 100644 samples/features/epm-framework/sample-policies/Public Not Granted Server Permissions.xml create mode 100644 samples/features/epm-framework/sample-policies/README.md create mode 100644 samples/features/epm-framework/sample-policies/Read-only Database Recovery Model.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server 32-bit Affinity Mask Overlap.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server 64-bit Affinity Mask Overlap.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Affinity Mask.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Blocked Process Threshold.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Default Trace.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Dynamic Locks.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server I_O Affinity Mask For Non-enterprise SQL Servers.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Lightweight Pooling.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Login Mode.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Max Degree of Parallelism.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Max Worker Threads for SQL Server 2005 and above.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Network Packet Size.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Password Expiration.xml create mode 100644 samples/features/epm-framework/sample-policies/SQL Server Password Policy.xml create mode 100644 samples/features/epm-framework/sample-policies/Symmetric Key Encryption for User Databases.xml create mode 100644 samples/features/epm-framework/sample-policies/Symmetric Key for System Databases.xml create mode 100644 samples/features/epm-framework/sample-policies/Symmetric Key for master Database.xml create mode 100644 samples/features/epm-framework/sample-policies/Trustworthy Database.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log Cluster Disk Resource Corruption Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log Device Driver Control Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log Device Not Ready Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log Failed I_O Request Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log I_O Delay Warning.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log I_O Error During Hard Page Fault Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log Read Retry Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log Storage System I_O Timeout Error.xml create mode 100644 samples/features/epm-framework/sample-policies/Windows Event Log System Failure Error.xml create mode 100644 samples/features/sql-big-data-cluster/deployment/openshift/bdc-scc.yaml create mode 100644 samples/manage/azure-arc-enabled-sql-server/README.md create mode 100644 samples/manage/azure-arc-enabled-sql-server/migrate-to-azure-arc-data.ps1 create mode 100644 samples/manage/azure-hybrid-benefit/README.md create mode 100644 samples/manage/azure-hybrid-benefit/ahb-usage-in-subscription.ps1 create mode 100644 samples/manage/azure-hybrid-benefit/sql-license-usage.ps1 create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/.gitignore create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/.vscode/launch.json create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/.vscode/tasks.json create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/AssessmentClient.SMO/AssessmentClient.SMO.csproj create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/AssessmentClient.SMO/Program.cs create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/AssessmentClient.sln create mode 100644 samples/manage/sql-assessment-api/SqlAssessmentClient/README.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlColumnstoreSample/Program.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlEFSample/EFSampleContext.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlEFSample/Program.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlEFSample/Task.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlEFSample/User.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlSample/Program.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/AzureSqlSample/Program_KeyVault.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Unix-based/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlColumnstoreSample/Program.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlEFSample/EFSampleContext.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlEFSample/Program.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlEFSample/Task.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlEFSample/User.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlSample/Program.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/AzureSqlSample/Program_KeyVault.cs create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/csharp/Windows/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/go/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/go/columnstore.go create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/go/connect.go create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/go/crud.go create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/go/orm.go create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlColumnstoreSample/App.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlColumnstoreSample/pom.xml create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/App.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/Task.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/User.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/pom.xml create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/App.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/App_KeyVault.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/pom.xml create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlColumnstoreSample/App.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlColumnstoreSample/pom.xml create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlHibernateSample/App.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlHibernateSample/Task.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlHibernateSample/User.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlHibernateSample/pom.xml create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlSample/App.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlSample/App_KeyVault.java create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/AzureSqlSample/pom.xml create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/java/Windows/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Unix-based/AzureSqlColumnstoreSample/columnstore.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Unix-based/AzureSqlSample/connect.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Unix-based/AzureSqlSample/crud.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Unix-based/AzureSqlSample/crud_KeyVault.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Unix-based/AzureSqlSequelizeSample/orm.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Unix-based/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Windows/AzureSqlColumnstoreSample/columnstore.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Windows/AzureSqlSample/connect.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Windows/AzureSqlSample/crud.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Windows/AzureSqlSample/crud_KeyVault.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Windows/AzureSqlSequelizeSample/orm.js create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/nodejs/Windows/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Unix-based/AzureSqlColumnstoreSample/columnstore.php create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Unix-based/AzureSqlSample/connect.php create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Unix-based/AzureSqlSample/crud.php create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Unix-based/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Windows/AzureSqlColumnstoreSample/columnstore.php create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Windows/AzureSqlSample/connect.php create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Windows/AzureSqlSample/crud.php create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/php/Windows/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Unix-based/AzureSqlColumnstoreSample/columnstore.py create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Unix-based/AzureSqlSample/crud.py create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Unix-based/AzureSqlSample/crud_KeyVault.py create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Unix-based/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Windows/AzureSqlColumnstoreSample/columnstore.py create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Windows/AzureSqlSample/crud.py create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Windows/AzureSqlSample/crud_KeyVault.py create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/python/Windows/Readme.md create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/ruby/AzureSqlColumnstoreSample/columnstore.rb create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/ruby/AzureSqlSample/activerecordcrud.rb create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/ruby/AzureSqlSample/connect.rb create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/ruby/AzureSqlSample/crud.rb create mode 100644 samples/tutorials/AzureSqlGettingStartedSamples/ruby/Readme.md diff --git a/README.md b/README.md index 9480a17f..a33290fd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](./media/solutions-microsoft-logo-small.png) +![](./media/solutions-microsoft-logo-small.png) # Azure Data SQL Samples Repository This GitHub repository contains code samples that demonstrate how to use Microsoft's Azure Data products including SQL Server, Azure SQL Database, Azure Synapse, and Azure SQL Edge. Each sample includes a README file that explains how to run and use the sample. diff --git a/samples/databases/wide-world-importers/sample-scripts/polybase/DemonstratePolybase.sql b/samples/databases/wide-world-importers/sample-scripts/polybase/DemonstratePolybase.sql deleted file mode 100644 index 2675074f..00000000 --- a/samples/databases/wide-world-importers/sample-scripts/polybase/DemonstratePolybase.sql +++ /dev/null @@ -1,139 +0,0 @@ --- Demonstrate WorldWideImporters Polybase connections --- Requires PolyBase to be installed. - -USE WideWorldImportersDW; -GO - --- WideWorldImporters have customers in a variety of cities but feel they are likely missing --- other important cities. They have decided to try to find other cities have a growth rate of more --- than 20% over the last 3 years, and where they do not have existing customers. --- They have obtained census data (a CSV file) and have loaded it into an Azure storage account. --- They want to combine that data with other data in their main OLTP database to work out where --- they should try to find new customers. - --- First, let's apply Polybase connectivity and set up an external table to point to the data --- in the Azure storage account. - -EXEC [Application].Configuration_ApplyPolybase; -GO - --- In Object Explorer, refresh the WideWorldImporters database, then expand the Tables node. --- Note that SQL Server 2016 added a new entry here for External Tables. Expand that node. --- Expand the dbo.CityPopulationStatistics table, expand the list of columns and note the --- values that are contained. Let's look at the data: - -SELECT CityID, StateProvinceCode, CityName, YearNumber, LatestRecordedPopulation FROM dbo.CityPopulationStatistics; -GO - --- How did that work? First the procedure created an external data source like this: -/* - -CREATE EXTERNAL DATA SOURCE AzureStorage -WITH -( - TYPE=HADOOP, LOCATION = 'wasbs://data@sqldwdatasets.blob.core.windows.net' -); - -*/ --- This shows how to connect to AzureStorage. Next the procedure created an --- external file format to describe the layout of the CSV file: -/* - -CREATE EXTERNAL FILE FORMAT CommaDelimitedTextFileFormat -WITH -( - FORMAT_TYPE = DELIMITEDTEXT, - FORMAT_OPTIONS - ( - FIELD_TERMINATOR = ',' - ) -); - -*/ --- Finally the external table was defined like this: -/* - -CREATE EXTERNAL TABLE dbo.CityPopulationStatistics -( - CityID int NOT NULL, - StateProvinceCode nvarchar(5) NOT NULL, - CityName nvarchar(50) NOT NULL, - YearNumber int NOT NULL, - LatestRecordedPopulation bigint NULL -) -WITH -( - LOCATION = '/', - DATA_SOURCE = AzureStorage, - FILE_FORMAT = CommaDelimitedTextFileFormat, - REJECT_TYPE = VALUE, - REJECT_VALUE = 4 -- skipping 1 header row per file -); - -*/ --- From that point onwards, the external table can be used like a local table. Let's run that --- query that they wanted to use to find out which cities they should be finding new customers --- in. We'll start building the query by grouping the cities from the external table --- and finding those with more than a 20% growth rate for the period: - -WITH PotentialCities -AS -( - SELECT cps.CityName, - cps.StateProvinceCode, - MAX(cps.LatestRecordedPopulation) AS PopulationIn2016, - (MAX(cps.LatestRecordedPopulation) - MIN(cps.LatestRecordedPopulation)) * 100.0 - / MIN(cps.LatestRecordedPopulation) AS GrowthRate - FROM dbo.CityPopulationStatistics AS cps - WHERE cps.LatestRecordedPopulation IS NOT NULL - AND cps.LatestRecordedPopulation <> 0 - GROUP BY cps.CityName, cps.StateProvinceCode -) -SELECT CityName, StateProvinceCode, PopulationIn2016, GrowthRate -FROM PotentialCities -WHERE GrowthRate > 2.0; -GO - --- Now let's combine that with our local city and sales data to exclude those where we already --- have customers. We'll find the 100 most interesting cities based upon population. - -WITH PotentialCities -AS -( - SELECT cps.CityName, - cps.StateProvinceCode, - MAX(cps.LatestRecordedPopulation) AS PopulationIn2016, - (MAX(cps.LatestRecordedPopulation) - MIN(cps.LatestRecordedPopulation)) * 100.0 - / MIN(cps.LatestRecordedPopulation) AS GrowthRate - FROM dbo.CityPopulationStatistics AS cps - WHERE cps.LatestRecordedPopulation IS NOT NULL - AND cps.LatestRecordedPopulation <> 0 - GROUP BY cps.CityName, cps.StateProvinceCode -), -InterestingCities -AS -( - SELECT DISTINCT pc.CityName, - pc.StateProvinceCode, - pc.PopulationIn2016, - FLOOR(pc.GrowthRate) AS GrowthRate - FROM PotentialCities AS pc - INNER JOIN Dimension.City AS c - ON pc.CityName = c.City - WHERE GrowthRate > 2.0 - AND NOT EXISTS (SELECT 1 FROM Fact.Sale AS s WHERE s.[City Key] = c.[City Key]) -) -SELECT TOP(100) CityName, StateProvinceCode, PopulationIn2016, GrowthRate -FROM InterestingCities -ORDER BY PopulationIn2016 DESC; -GO - --- Clean up if required -/* -DROP EXTERNAL TABLE dbo.CityPopulationStatistics; -GO -DROP EXTERNAL FILE FORMAT CommaDelimitedTextFileFormat; -GO -DROP EXTERNAL DATA SOURCE AzureStorage; -GO -*/ diff --git a/samples/databases/wide-world-importers/sample-scripts/polybase/README.md b/samples/databases/wide-world-importers/sample-scripts/polybase/README.md deleted file mode 100644 index ed632cce..00000000 --- a/samples/databases/wide-world-importers/sample-scripts/polybase/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Sample Querying of External Data Source in WideWorldImportersDW - -This script demonstrates the use of PolyBase to query an external data source. - -Demographics data is available in Azure blob storage. This data is joined with sales data recorded in the local database to determine which would be good candidates for future expansion of the business. - -### Contents - -[About this sample](#about-this-sample)
-[Before you begin](#before-you-begin)
-[Running the sample](#run-this-sample)
-[Sample details](#sample-details)
-[Disclaimers](#disclaimers)
-[Related links](#related-links)
- - - - -## About this sample - - -1. **Applies to:** SQL Server 2016 (or higher), Azure SQL Database -1. **Key features:** PolyBase -1. **Workload:** Analytics -1. **Programming Language:** T-SQL -1. **Authors:** Greg Low, Jos de Bruijn -1. **Update history:** 26 May 2016 - initial revision - - - -## Before you begin - -To run this sample, you need the following prerequisites. - -**Software prerequisites:** - - -1. SQL Server 2016 (or higher) with PolyBase, connected to the internet. -2. SQL Server Management Studio -3. The WideWorldImportersDW database (Full version). - - - -## Running the sample - -1. Execute the sample script. - -2. Inspect external tables in the database. - -3. Review query results. - -## Sample details - -The sample script performs a configuration and runs three queries: - -1. An external table `dbo.CitePopulationStatistics` is created in the database, pointing to a data set in Azure blob storage. - -2. The data in Azure storage is queried through Transact-SQL, showing all the data in the data source. - -3. Cities with a significant growth rate (>= 20%) are identified. - -4. Top cities for potential expansion are identified based on external data as well as sales data in the local database. - - - -## Disclaimers -The code included in this sample is not intended to be used for production purposes. - - - -## Related Links - -For more information, see these articles: -- [Get started with PolyBase](https://msdn.microsoft.com/library/mt163689.aspx) -- [PolyBase: Gaining insights from HDFS and relational data in SQL Server 2016 (video)](https://channel9.msdn.com/Events/DataDriven/SQLServer2016/PolyBase) diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitattributes b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitattributes new file mode 100644 index 00000000..8c405ec3 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitattributes @@ -0,0 +1,10 @@ +* text eol=lf +*.gif -text +*.jpg -text +*.png -text +*.dll -text +*.exe -text +*.nupkg -text +ml/data/*.parquet binary +*.zip binary +*.dacpac binary \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitignore b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitignore new file mode 100644 index 00000000..31aa3f7b --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/.gitignore @@ -0,0 +1,447 @@ +.DS_Store +temp/ + +# Created by https://www.gitignore.io/api/node,visualstudio,visualstudiocode +# Edit at https://www.gitignore.io/?templates=node,visualstudio,visualstudiocode + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# End of https://www.gitignore.io/api/node,visualstudio,visualstudiocode \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/README.md b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/README.md new file mode 100644 index 00000000..14c811b7 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/README.md @@ -0,0 +1,475 @@ +# SQL DB Edge Demo + +## Overview + +The SQL DB Edge demo is based on a Contoso Renewable Energy, a wind turbine farm that leverages SQL DB Edge for data processing onboard the generator. + +The demo will walk you through resolving an alert being raised due to wind turbulence being detected at the device. You will train a model and deploy it to SQL DB Edge that will correct the detected wind wake and ultimately optimize power output. + +We will also look at some of the security features available with SQL DB Edge. + +## Wind Turbine Data Explanation for the Wake Detection model + +The data stored in the database table represents the following: + + +* **RecordId:** _Unique identifier for the entry._ +* **TurbineId:** _Unique identifier for the turbine in scope._ +* **GearboxOilLevel:** _Oil level recorded for the turbine gear box at the time of the reading._ +* **GearboxOilTemp:** _Oil temperature recorded for the turbine gear box at the time of the reading._ +* **GeneratorActivePower:** _Active Power recorded by the turbine generator._ +* **GeneratorSpeed:** _Speed recorded by the turbine generator._ +* **GeneratorTemp:** _Temperature recorded by the turbine generator._ +* **GeneratorTorque:** _Torque recorded by the turbine generator._ +* **GridFrequency:** _Frequency recorded in the grid for the specific wind turbine._ +* **GridVoltage:** _Voltage recorded in the grid for the specific wind turbine._ +* **HydraulicOilPressure:** _Current pressure of the hydraulic oil for the wind turbine._ +* **NacelleAngle:** _Angle of the nacelle at the time of the reading (the housing that contains all the generating components)._ +* **PitchAngle:** _Pitch angle of the blades against the oncoming air stream to obtain the optimal amount of energy._ +* **Vibration:** _Vibration of the wind turbine at the time of the reading._ +* **WindSpeedAverage:** _Average wind speed calculated from the last X records._ +* **Precipitation:** _Flag to represent if rain was present at the time of the reading._ +* **WindTempAverage:** _Average wind temperature calculated from the last X records._ +* **OverallWindDirection:** _Overall wind direction recorded at the time of the reading._ +* **TurbineWindDirection:** _Turbine wind direction recorded at the time of the reading._ +* **TurbineSpeedAverage:** _Average turbine speed calculated from the last X records._ +* **WindSpeedStdDev:** _Standard Deviation of the last X WindSpeedAverage records._ +* **TurbineSpeedStdDev:** _Standard Deviation of the last X TurbineSpeedAverage records._ + +The above dataset definition contains trends that will enable us to detect the existence of wake in a wind turbine. There are two main conditions that influence the presence of wind wake: + +1. Overall wind farm and turbine wind direction are both between 40° - 45° degrees. +1. TurbineSpeedStdDev and WindSpeedStdDev have been too far apart for greater than a minute. + +The wind turbine will experience wake when the turbine wind direction is between 40° - 45° degrees and the values of TurbineSpeedStdDev and WindSpeedStdDev are not similar. For example: +* Wake Present: + * TurbineWindDirection = 43.5° + * TurbineSpeedStdDev = 8.231 + * WindSpeedStdDev = 0.23 +* Wake Not Present: + * TurbineWindDirection = 23.5° + * TurbineSpeedStdDev = 0.921 + * WindSpeedStdDev = 0.213 + + + +## Clone the SQL DB Edge Demo Repository +Git will be used to copy all the files for the demo to your local computer. + +1. Install Git from [here](https://git-scm.com/download) +1. Open a command prompt and navigate to a folder where the repo should be downloaded
+1. Issue the command `https://github.com/SQLSourabh/DemoFiles.git` + +## Azure Resource Deployment + +An Azure Resource Manager (ARM) template will be used to deploy all the required resources in the solution. Click on the link below to start the deployment. + +[![homepage](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fzarmada.blob.core.windows.net%2Farm-deployments-public%2Farm-template-dbedge.json "Deploy template") + +TODO ^^ Need to update the ARM location + +### Deployment of resources + +Follow the steps to deploy the required Azure resources: + +**BASICS** + + - **Subscription**: Select the Subscription. + - **Resource group**: Click on 'Create new' and provide a unique name for the Resource Group + - **Location**: Select the Region where to deploy the resources. Keep in mind that all resources will be deployed to this region so make sure it supports all of the required services. The template has been confirmed to work in West US 2. + +1. Read and accept the `TERMS AND CONDITIONS` by checking the box. +1. Click the `Purchase` button and wait for the deployment to finish. + +## Post Deployment Configuration + +Some resources require some extra configuration. + +#### Upload SQL DACPAC + +The Edge Module will require access the DACPAC package in order to setup the database. + +1. In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. +1. Select the **Storage account** resource from the list. +1. Click the **Containers** option in the left menu under **Blob service**. +1. Click the **dacpac** container. +1. Click the **Upload** button. +1. Click the **Select a file** input and select the file under the project folder: `sql/turbine-sensor-db-dacpac.zip`. +1. Click the **Upload** button. +1. Once the file is uploaded, click on it. +1. Click **Generate SAS** tab. +1. Update the **Expiry** year to 2050. +1. Click **Generate SAS token and URL** +1. Copy the value in **Blob SAS URL** and save it for later in the setup. + + +##### SQL Security Setup Information + +As security settings were deployed as part of the DACPAC package, below is a **review** of the security setup within the database. + +1. Create users without a login for simpler testing: + ```sql + /* Create users using the logins created */ + CREATE USER OperatorUser WITHOUT LOGIN; + CREATE USER DataScientistUser WITHOUT LOGIN; + CREATE USER SecurityUser WITHOUT LOGIN; + CREATE USER TurbineUser WITHOUT LOGIN; + ``` + +1. Assigned permissions for each user: + ```sql + /* Grant permissions to users */ + GRANT SELECT ON RealtimeSensorRecord TO OperatorUser; + GRANT SELECT ON RealtimeSensorRecord TO DataScientistUser; + GRANT SELECT ON RealtimeSensorRecord TO SecurityUser; + GRANT SELECT, INSERT ON RealtimeSensorRecord TO TurbineUser; + ``` + > **Note**: All users can SELECT, however the TurbineUser can also INSERT to the table. + +1. For privacy reasons, mask the last 4 digits of the SensorId for the Data Scientist user: + ```sql + /*Mask the last four digits of the serial number (Sensor ID) for the Data Scientist User*/ + ALTER TABLE RealtimeSensorRecord + ALTER COLUMN SensorId varchar(50) MASKED WITH (FUNCTION = 'partial(34,"XXXX",0)'); + DENY UNMASK TO DataScientistUser; + GO + ``` + +1. Add a policy using a filter predicate and a function to manage access to data events: + * We updated the SensorType column as it is required in our function then created a new schema to store it. + + ```sql + /** + * Operator: Can see all events + * Data Scientist: Can see everything BUT Hatch Sensor events + * Security: Can ONLY see Hatch Sensor events + */ + ALTER TABLE RealtimeSensorRecord + ALTER COLUMN SensorType sysname + GO + + CREATE SCHEMA Security; + GO + ``` + * Add the function that will ensure each query is authorized based on Sensor Type/User. + + + ```sql + /** + * Operator: Can see all events + * Data Scientist: Can see everything BUT Hatch Sensor events + * Security: Can ONLY see Hatch Sensor events + */ + CREATE FUNCTION Security.fn_securitypredicate(@SensorType AS sysname) + RETURNS TABLE + WITH SCHEMABINDING + AS + RETURN SELECT 1 AS fn_securitypredicate_result + WHERE + USER_NAME() = 'OperatorUser' OR USER_NAME() = 'dbo' OR + (USER_NAME() = 'DataScientistUser' AND @SensorType <> 'HatchSensor') OR + (USER_NAME() = 'SecurityUser' AND @SensorType = 'HatchSensor'); + ``` + * Add a filter to to use the function. + + + ```sql + CREATE SECURITY POLICY SensorsDataFilter + ADD FILTER PREDICATE Security.fn_securitypredicate(SensorType) + ON dbo.RealtimeSensorRecord + WITH (STATE = ON); + ``` + + + +#### Notebook Setup + +In this section, we will setup our notebook with the required files for the generation of the wind adapt model. + +##### Upload training data file: + +1. In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. +1. Select the **Storage account** resource. +1. Click the **Containers** option in the left menu under **Blob service**. +1. Click the **azureml-blobstore-GUID** container. +1. Click the **Upload** button in the top. +1. Click the **Select a file** input and select the `ml\data\TrainingDataset.parquet` from your repo. +1. Click the **Upload** button and wait for the upload to finish. + +##### Notebook files upload +1. Select **Azure Active Directory** option from the main navigation in the Azure Portal: + + ![Azure Active Directory Option](./images/azure-active-directory-option.png) +1. Copy the **Tenant Id** value from the overview as you will need this value later. +1. Go back to the **Resource Group** you created earlier. +1. Select the **Machine Learning** resource. +1. Take note of the following values to be used later in the deployment + * Resource Group + * Workspace Name + * Subscription ID + ![Machine Learning Resource](./images/machine-learning-resource.png) +1. Click the **Launch now** button to open the Machine Learning workspace. +1. Click the **Notebooks** option in the left menu under **Author**. +1. Click the **Create new folder** button at the top of the navigation panel. +1. Enter the name `scripts` for as the folder name and click the **Create** button. +1. Click the **Upload files** button at the top of the navigation panel. +1. Select the 2 files inside the `ml\scripts` folder: + * ml\scripts\train.py + * ml\scripts\utils.py +1. Select the newly created `scripts` folder from the target directory list. +1. Click the **Upload** button and wait for the upload to finish. +1. Click the **Upload files** button again. +1. Select the following 2 files inside the `ml` folder: + * ml\utils.py + * ml\wind-turbine-scikit.ipynb + > **Note**: The `utils.py` is a different file from the previous step. + +1. Select your username folder from the target directory list. +1. Click the **Upload** button. + +##### Notebook configuration + +We need configure values within the notebook before being able to execute it: + +1. Click the `wind-turbine-scikit.ipynb` in the **My files** navigation: +1. Click the **New Compute** button. +1. Enter the name `compute-{your-initials}`. +1. Select **CPU (Central Processing Unit)** from the **Virtual machine type** dropdown. +1. Select the virtual machine size **Standard_D12_v2**. +1. Click the **Create** button and wait for the compute to be created. + > **Note**: This process can take several minutes; wait until status of **compute** is `Running`. +1. Click the **Edit** dropdown and select the **Edit in Jupyter** option. + > **Note**: If required, login with your Azure credentials. +1. Replace the values within the **Setup Azure ML** cell with the values you obtained in the **Notebook files upload** section: + ``` + interactive_auth = InteractiveLoginAuthentication(tenant_id="") + # Get instance of the Workspace and write it to config file + ws = Workspace( + subscription_id = '', + resource_group = '', + workspace_name = '', + auth = interactive_auth) + ``` +1. Click **File** > **Save and Checkpoint** from the menu. +1. Select the **Install requirements** cell and click **Run** from the menu, wait for the script to execute before continuing. +1. Select the **Setup Azure ML** cell and click **Run** from the menu. + > **IMPORTANT**: Observe the output to **authenticate** via the URL provided (https://microsoft.com/devicelogin). + +1. From here, **Run** the remaining cells sequentially until you have executed the notebook. + > **IMPORTANT**: Remember to wait for each cell to execute before continuing. +1. Go back to the azure resource group and click the **Storage Account** resource. +1. Click the **Containers** option in the left menu. +1. Click the container in the list with a name like: `azureml-blobstore-{guid}`. +1. A new file with the name `windturbinewake.model.onnx` will be in the container. +1. Click the `windturbinewake.model.onnx` file +1. Click the **Generate SAS** tab option. +1. Change the **Expiry** Year to 2050. +1. Click the **Generate SAS token and URL** button and wait for the SAS to be generated. +1. Copy the **Blob SAS URL** value for later in the demo usage section. + + > **IMPORTANT**: As this process does take some time, once you have saved your model to blob storage, you will not be required to execute this every time you run through the demo. Showing the notebook flow may be adequate for demo purposes. You will just need the blob SAS for the **SQL DB Edge Demo Usage** section later in the document. + + +#### Device Setup + +In this section, we will set up an Edge device within our IoT Hub instance. + +##### Create a new Edge device + +1. In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. +1. Select the **IoT Hub** resource. +1. Click on **IoT Edge** from the left navigation. +1. Click **+ Add an IoT Edge Device**. +1. Enter a **Device ID** and leave all other fields as default. +1. Click **Save**. +1. Once the device has been created, select the device and copy the **Primary Connection String** for later in this setup. + +##### Setup Edge device as a VM + +1. Click on the link below to start the deploy to Azure: + + [![homepage](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fiotedge-vm-deploy%2Fmaster%2FedgeDeploy.json "Deploy device") + +1. On the newly launched window, fill in the available form fields: + + * **Subscription**: Your subscription. + * **Resource group**: Select the resource group you created earlier. + * **DNS Label Prefix**: Your initials and birth year. + * **Admin Username**: Enter `microsoft` as default. + * **Device Connection String**: The device connection string that you got from previous section. + * **VM Size**: The size of the virtual machine to be deployed. + * **Ubuntu OS Version**: The version of the Ubuntu OS to be installed on the base virtual machine. + * **Location**: The geographic region to deploy the virtual machine into, this value defaults to the location of the selected Resource Group. + * **Authentication Type**: Choose the **password** option. + * **Admin Password or Key**: Enter `M1cr0s0ft2020`. + +1. Accept the **Terms and Conditions**. +1. Select **Purchase** to begin the deployment. + +##### SSH into the VM - Optional +1. Once the deployment is complete, go back to the **Resource Group** you created earlier. +1. Select the **Virtual Machine** resource. + > **Note**: Take note of the machine name, this should be in the format vm-0000000000000. Also, take note of the associated DNS Name, which should be in the format `..cloudapp.azure.com`. + The DNS Name can be obtained from the Overview section of the newly deployed virtual machine within the Azure portal. + ![VM DNS Name](./images/iotedge-vm-dns-name.png) + + +1. If you want to SSH into this VM after setup, use the associated DNS Name with the command: `ssh @`. You can use the password you created in the previous step. + > **IMPORTANT**: There is an optional section at the end of this document showing some example commands. + + +##### Setup Visual Studio Code Development Environment + +1. Install [Visual Studio Code](https://code.visualstudio.com/Download) (VS Code). +1. Install [Docker Community Edition (CE)](https://docs.docker.com/install/#supported-platforms). Don't sign in to Docker Desktop after Docker CE is installed. +1. Install the following extensions for VS Code: + * [Azure Machine Learning](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-ai) ([Azure Account](https://marketplace.visualstudio.com/items?itemName=ms-vscode.azure-account) will be automatically installed) + * [Azure IoT Hub Toolkit](https://marketplace.visualstudio.com/items?itemName=vsciot-vscode.azure-iot-toolkit) + * [Azure IoT Edge](https://marketplace.visualstudio.com/items?itemName=vsciot-vscode.azure-iot-edge) + * [Docker Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) +1. Restart VS Code. +1. Select **[View > Command Palette…]** to open the command palette box, then enter **[Python: Select Interpreter]** command in the command palette box to select your Python interpreter. +1. Enter **[Azure: Sign In]** command in the command palette box to sign in Azure account and select your subscription. + +##### Build and deploy container image to device + +1. Launch Visual Studio Code, and select File > Open Workspace... command to open the `edge\sensor-solution.code-workspace`. +1. Update the .env file with the values for your container registry. + - In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. + - Select the **Container Registry** resource. + - Select **Access Keys** from the left navigation. + - Update the following in `edge/SensorSolution/.env` with the following values from **Access Keys** within the Container Registry: + + CONTAINER_REGISTRY_NAME=`` (Ensure this is the login server and NOT the Registry Name) + + CONTAINER_REGISTRY_USER_NAME=`` + + CONTAINER_REGISTRY_PASSWORD=`` + + SQL_PACKAGE=`` (the one you obtained earlier in the setup) + + - Save the file. +1. Sign in to your Azure Container Registry by entering the following command in the Visual Studio Code integrated terminal (replace , , and with your container registry values set in the .env file IN THE PREVIOUS STEP). + + `docker login -u -p ` + + > **IMPORTANT**: Ensure you have `amd64` selected as the architecture in the bottom navigation bar of VS Code. + +1. Right-click on `edge/SensorSolution/deployment.debug.template.json` and select the **Build and Push IoT Edge Solution** command to generate a new `deployment.debug.amd64.json` file in the config folder, build a module image, and push the image to the specified ACR repository. + > **IMPORTANT:** If you have amended code in your module, you will need to increment the version number in `module.json` so the new version will get deployed to the device in the next steps. + + > **Note**: Some red warnings "/usr/bin/find: '/proc/XXX': No such file or directory" and "debconf: delaying package configuration, since apt-utils is not installed" displayed during the building process can be ignored. + +1. Ensure you have the correct Iot Hub selected in VS Code. + - In the Azure IoT Hub extension, click **Select IoT Hub** from the hamburger menu. (Alternatively, select `Azure IoT Hub: Select IoT Hub` from the **Command Palette**) + - Select your **Subscription**. + - Select the **IoT Hub** you created earlier in the setup. +1. Right-click `config\deployment.debug.amd64.json` and select **Create Deployment for a Single Device**. +1. Select the device you created earlier. +1. Wait for deployment to be completed. + +#### Web App Settings + +Follow the next steps to setup the required module twin connection string property. + +1. In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. +1. Select the **IoT Hub** resource. +1. Click the **IoT Edge** option in the left menu under **Automatic Device Management**. +1. Click the **device** you created earlier. +1. Click the **SensorModule** from the modules list. +1. Copy the **Connection string (primary key)** value and save for the next step. +1. Go back to your **Resource Group**. +1. Select the **App Service** resource. +1. Click the **Configuration** option in the left menu. +1. Under the **Application settings** find the `IoTHub:ModuleConnectionString` and click it. +1. Paste the module connection string that you got before to the `value` input field. +1. Click the **OK** button. +1. Click the **Save** button on the top to apply the change. + +## SQL DB Edge Demo Usage + +Open the Web App. + +1. In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. +1. Select the **App Service** resource. +1. Click **Browse** to go the application on a desktop machine. + +Investigate Turbine Issue +1. Click **view** on the alert. A query is ran against the SQL DB Edge instance. +1. Notice the Operator can't see the Security Alert due to as the permissions we set earlier. +1. You can notice a drop in the **Power Generated** chart. +1. Click the **Environmental** button. +1. You can notice the **Wind Speed and Direction** at the turbine is a lot more turbulent than the rest of the Wind Farm. This could indicate wind wake. + +Now we need to run our notebook in order to generate the Onnx model that we will use to resolve the alert. + +>**Important**: As mentioned earlier in the document, you can choose to run through executing the notebook cells in the `Notebook Setup` section again to obtain the model. Or you can use your Blob URL you created during the initial setup. + +Now we have our wind adapt model, lets update the module to correct the turbine. + +1. Go back to the azure resource group and click the **IoT Hub** resource. +1. Click the **Iot Edge** option in the left menu. +1. Click the created device from previous steps. +1. Click the **SensorModule** from the modules list. +1. Click the **Module Identity Twin** option in the top menu. +1. Find the `properties` section in the json. +1. Find the `desired` section in the json. +1. Find the `OnnxModelUrl` property and update the value with the model Blob URL from the previous section. +1. Click the **Save** button. +1. Go back to the **Web App**. +1. You will notice a notification indicating the alert has been resolved. +1. Click on the **Resfresh** button. +1. Notice the turbine **Wind Speed and Direction** has stabilized. +1. If you go back to the dashboard view. You will notice Unit 34 no longer has an alert. + + +#### Restart the demo + +This steps allows you to restart the demo. + +1. Go back to the azure resource group and click the **IoT Hub** resource. +1. Click the **Iot Edge** option in the left menu. +1. Click the created device from previous steps. +1. Click the **SensorModule** from the modules list. +1. Click the **Module Identity Twin** option in the top menu. +1. Find the `properties` section in the json. +1. Find the `desired` section in the json. +1. Find the `OnnxModelUrl` property set the value as empty. +> **Note**: Since the `Alert` property value was already in `start` we don't need to updated it but the module will set the reported property with this value. + +1. Click the **Save** button. +1. Go back to the **Web App** and refresh. +1. After a short time the alert will appear again. + +# Optional Steps + +This section describe steps that allow us to see extra features of the resources as a reference only. + +## Device VM access + +Here we will see how to run commands into to the device virtual machine from the terminal using SSH connection. + +1. In the [Azure portal](https://portal.azure.com/) select the **Resource Group** you created earlier. +2. Select the **Virtual machine** resource. +3. Copy the **DNS name** to use it for the connection. +4. In a terminal run the following command replacing the **DNS name**:` ssh microsoft@` + > **Note**: The above command is assuming that you use the default Admin username when deploying the VM. + +5. Enter the password to connect. + > **Note**: Default password is: `M1cr0s0ft2020` + +6. Run the following command to see the list of modules running: `sudo iotedge list` + > **Note**: You can should be able to see the `AzureSQLDatabaseEdge` and `SensorModule` we deployed earlier. + +7. Run the following command to see the logs of the **Sensor Module**: `sudo iotedge logs SensorModule` + * Following we will connect with the edge sql server to run a simple query by doing: + * Get the list of containers running with docker: `sudo docker container list`. + * Get the container ID of the `AzureSQLDatabaseEdge` docker image running. + * Connect to the container using the id that you got and the command: `sudo docker exec -it CONTAINERID /bin/sh`. + * Connect to the container using: `/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'Microsoft2020$'`. + * Connect to the database using: `USE [turbine-sensor-db]` and then `go`. + * Query the number of records in the table using: `select count(*) from RealtimeSensorRecord` and `go`. + +# Troubleshooting +### Error when deploying ARM Template +We've seen issues with different subscription types: MSDN, AIRS, etc... not being able to deploy certain resources to certain regions. We've found that deploying to West US 2 works consistently. If you have a deployment error, try deploying to West US 2. The resources inherit their deployment region from the Resource Group location. diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/deployment/arm-template.json b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/deployment/arm-template.json new file mode 100644 index 00000000..fe3b99e1 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/deployment/arm-template.json @@ -0,0 +1,383 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + }, + "variables": { + "prefix": "sqldbedge", + "uniquePrefix": "[concat(variables('prefix'), substring(uniqueString(resourceGroup().id), 0, 6))]", + "registry": { + "name": "[concat(variables('uniquePrefix'), 'registry')]", + "id": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', concat(variables('uniquePrefix'), 'registry'))]", + "vault": { + "name": "[concat(variables('uniquePrefix'), 'vault')]", + "id": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', concat(variables('uniquePrefix'), 'vault'))]" + } + }, + "storage": { + "name": "[concat(variables('uniquePrefix'), 'storage')]", + "id": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', concat(variables('uniquePrefix'), 'storage'))]" + }, + "ml": { + "name": "[concat(variables('uniquePrefix'), 'ml')]", + "id": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', concat(variables('uniquePrefix'), 'ml'))]" + }, + "insights": { + "component": { + "name": "[concat(variables('uniquePrefix'), 'insightscomp')]", + "id": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', concat(variables('uniquePrefix'), 'insightscomp'))]" + } + }, + "sqldbedgehub": { + "name": "[concat(variables('uniquePrefix'), 'sqldbedgehub')]", + "id": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', concat(variables('uniquePrefix'), 'sqldbedgehub'))]" + }, + "website": { + "name": "[concat(variables('uniquePrefix'), 'app')]", + "serverfarms": { + "name": "[concat(variables('uniquePrefix'), 'serverfarms')]" + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2019-12-01-preview", + "name": "[variables('registry').name]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Basic", + "tier": "Basic" + }, + "properties": { + "adminUserEnabled": true, + "policies": { + "quarantinePolicy": { + "status": "disabled" + }, + "trustPolicy": { + "type": "Notary", + "status": "disabled" + }, + "retentionPolicy": { + "days": 7, + "status": "disabled" + } + }, + "encryption": { + "status": "disabled" + }, + "dataEndpointEnabled": false + } + }, + { + "type": "microsoft.insights/components", + "apiVersion": "2015-05-01", + "name": "[variables('insights').component.name]", + "location": "[resourceGroup().location]", + "kind": "web", + "properties": { + "Application_Type": "web" + } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-06-01", + "name": "[variables('storage').name]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "properties": { + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [], + "ipRules": [], + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": false, + "encryption": { + "services": { + "file": { + "keyType": "Account", + "enabled": true + }, + "blob": { + "keyType": "Account", + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2019-06-01", + "name": "[concat(variables('storage').name, '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('storage').name)]" + ], + "sku": { + "name": "Standard_RAGRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + }, + "deleteRetentionPolicy": { + "enabled": false + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2019-06-01", + "name": "[concat(variables('storage').name, '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('storage').name)]" + ], + "sku": { + "name": "Standard_RAGRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(variables('storage').name, '/default/dacpac')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storage').name, 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('storage').name)]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2018-02-14", + "name": "[variables('registry').vault.name]", + "location": "[resourceGroup().location]", + "properties": { + "tenantId": "[subscription().tenantId]", + "enabledForDeployment": true, + "enabledForDiskEncryption": true, + "enabledForTemplateDeployment": true, + "sku": { + "name": "standard", + "family": "A" + }, + "networkAcls": { + "defaultAction": "Allow", + "bypass": "AzureServices" + }, + "accessPolicies": [] + } + }, + { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2018-11-19", + "name": "[variables('ml').name]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('storage').name)]", + "[resourceId('Microsoft.ContainerRegistry/registries', variables('registry').name)]", + "[resourceId('Microsoft.KeyVault/vaults', variables('registry').vault.name)]", + "[resourceId('microsoft.insights/components', variables('insights').component.name)]" + ], + "sku": { + "name": "Basic", + "tier": "Basic" + }, + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "storageAccount": "[resourceId('Microsoft.Storage/storageAccounts', variables('storage').name)]", + "containerRegistry": "[resourceId('Microsoft.ContainerRegistry/registries', variables('registry').name)]", + "keyVault": "[resourceId('Microsoft.KeyVault/vaults', variables('registry').vault.name)]", + "applicationInsights": "[resourceId('microsoft.insights/components', variables('insights').component.name)]", + "discoveryUrl": "[concat('https://', resourceGroup().location, '.experiments.azureml.net/discovery')]" + } + }, + { + "type": "Microsoft.Devices/IotHubs", + "apiVersion": "2019-11-04", + "name": "[variables('sqldbedgehub').name]", + "location": "[resourceGroup().location]", + "sku": { + "name": "S1", + "tier": "Standard", + "capacity": 1 + }, + "properties": { + "ipFilterRules": [], + "eventHubEndpoints": { + "events": { + "retentionTimeInDays": 1, + "partitionCount": 4 + } + }, + "routing": { + "endpoints": { + "serviceBusQueues": [], + "serviceBusTopics": [], + "eventHubs": [], + "storageContainers": [] + }, + "routes": [], + "fallbackRoute": { + "name": "$fallback", + "source": "DeviceMessages", + "condition": "true", + "endpointNames": [ + "events" + ], + "isEnabled": true + } + }, + "storageEndpoints": {}, + "messagingEndpoints": { + "fileNotifications": { + "lockDurationAsIso8601": "PT1M", + "ttlAsIso8601": "PT1H", + "maxDeliveryCount": 10 + } + }, + "enableFileUploadNotifications": false, + "cloudToDevice": { + "maxDeliveryCount": 10, + "defaultTtlAsIso8601": "PT1H", + "feedback": { + "lockDurationAsIso8601": "PT1M", + "ttlAsIso8601": "PT1H", + "maxDeliveryCount": 10 + } + }, + "features": "None" + } + }, + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2018-02-01", + "name": "[variables('website').serverfarms.name]", + "location": "[resourceGroup().location]", + "dependsOn": [ + ], + "sku": { + "name": "D1", + "tier": "Shared", + "size": "D1", + "family": "D", + "capacity": 0 + }, + "kind": "app", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": false, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "apiVersion": "2018-02-01", + "name": "[variables('website').name]", + "type": "Microsoft.Web/sites", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('website').serverfarms.name)]" + ], + "tags": null, + "properties": { + "name": "[variables('website').name]", + "enabled": true, + "hostNameSslStates": [{ + "name": "[concat(variables('website').name, '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(variables('website').name, '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('website').serverfarms.name)]", + "siteConfig": { + "appSettings": [{ + "name": "ApplicationInsightsAgent_EXTENSION_VERSION", + "value": "~2" + }, + { + "name": "XDT_MicrosoftApplicationInsights_Mode", + "value": "default" + }, + { + "name": "DiagnosticServices_EXTENSION_VERSION", + "value": "disabled" + }, + { + "name": "APPINSIGHTS_PROFILERFEATURE_VERSION", + "value": "disabled" + }, + { + "name": "APPINSIGHTS_SNAPSHOTFEATURE_VERSION", + "value": "disabled" + }, + { + "name": "InstrumentationEngine_EXTENSION_VERSION", + "value": "disabled" + }, + { + "name": "SnapshotDebugger_EXTENSION_VERSION", + "value": "disabled" + }, + { + "name": "XDT_MicrosoftApplicationInsights_BaseExtensions", + "value": "disabled" + }, + { + "name": "IoTHub:ModuleConnectionString", + "value": "" + } + ], + "metadata": [{ + "name": "CURRENT_STACK", + "value": "dotnetcore" + }] + }, + "clientAffinityEnabled": true + }, + "resources": [{ + "name": "MSDeploy", + "type": "extensions", + "location": "[resourceGroup().location]", + "apiVersion": "2015-08-01", + "dependsOn": [ + "[concat('Microsoft.Web/sites/', variables('website').name)]" + ], + "tags": { + "displayName": "webDeploy" + }, + "properties": { + "packageUri": "https://zarmada.blob.core.windows.net/sqldbedge-shared/sqldbedgedemo.zip?sp=r&st=2020-04-21T03:07:20Z&se=2050-04-21T11:07:20Z&spr=https&sv=2019-02-02&sr=b&sig=veWrTqH9RqjDeceBszIBtg3ueQlyBxmLd4ZPVrpTXpg%3D", + "dbType": "None", + "connectionString": "" + } + }] + } + ] +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.env b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.env new file mode 100644 index 00000000..0c5c3d16 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.env @@ -0,0 +1,4 @@ +CONTAINER_REGISTRY_NAME= +CONTAINER_REGISTRY_USER_NAME= +CONTAINER_REGISTRY_PASSWORD= +SQL_PACKAGE= \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/launch.json b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/launch.json new file mode 100644 index 00000000..c082e012 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/launch.json @@ -0,0 +1,43 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "SensorModule Remote Debug (.NET Core)", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickRemoteProcess}", + "pipeTransport": { + "pipeProgram": "docker", + "pipeArgs": [ + "exec", + "-i", + "SensorModule", + "sh", + "-c" + ], + "debuggerPath": "~/vsdbg/vsdbg", + "pipeCwd": "${workspaceFolder}", + "quoteArgs": true + }, + "sourceFileMap": { + "/app": "${workspaceFolder}/modules/SensorModule" + }, + "justMyCode": true + }, + { + "name": "SensorModule Local Debug (.NET Core)", + "type": "coreclr", + "request": "launch", + "program": "${workspaceRoot}/modules/SensorModule/bin/Debug/netcoreapp2.1/SensorModule.dll", + "args": [], + "cwd": "${workspaceRoot}/modules/SensorModule", + "internalConsoleOptions": "openOnSessionStart", + "stopAtEntry": false, + "console": "internalConsole", + "env": { + "EdgeHubConnectionString": "${config:azure-iot-edge.EdgeHubConnectionString}", + "EdgeModuleCACertificateFile": "${config:azure-iot-edge.EdgeModuleCACertificateFile}" + } + } + ] +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/settings.json b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/settings.json new file mode 100644 index 00000000..e13a6e88 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "azure-iot-edge.defaultPlatform": { + "platform": "amd64", + "alias": null + } +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/deployment.debug.template.json b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/deployment.debug.template.json new file mode 100644 index 00000000..8dd3f2e6 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/deployment.debug.template.json @@ -0,0 +1,122 @@ +{ + "$schema-template": "2.0.0", + "modulesContent": { + "$edgeAgent": { + "properties.desired": { + "schemaVersion": "1.0", + "runtime": { + "type": "docker", + "settings": { + "minDockerVersion": "v1.25", + "loggingOptions": "", + "registryCredentials": { + "sqldbedgecr": { + "username": "$CONTAINER_REGISTRY_USER_NAME", + "password": "$CONTAINER_REGISTRY_PASSWORD", + "address": "$CONTAINER_REGISTRY_NAME" + } + } + } + }, + "systemModules": { + "edgeAgent": { + "type": "docker", + "settings": { + "image": "mcr.microsoft.com/azureiotedge-agent:1.0", + "createOptions": {} + } + }, + "edgeHub": { + "type": "docker", + "status": "running", + "restartPolicy": "always", + "settings": { + "image": "mcr.microsoft.com/azureiotedge-hub:1.0", + "createOptions": { + "HostConfig": { + "PortBindings": { + "5671/tcp": [ + { + "HostPort": "5671" + } + ], + "8883/tcp": [ + { + "HostPort": "8883" + } + ], + "443/tcp": [ + { + "HostPort": "443" + } + ] + } + } + } + } + } + }, + "modules": { + "SensorModule": { + "version": "1.0", + "type": "docker", + "status": "running", + "restartPolicy": "always", + "settings": { + "image": "${MODULES.SensorModule.debug}", + "createOptions": {} + } + }, + "AzureSQLDatabaseEdge": { + "settings": { + "image": "marketplace.azurecr.io/microsoftsqledge-preview/azure-sql-database-edge", + "createOptions": "{\"HostConfig\":{\"CapAdd\":[\"SYS_PTRACE\"],\"Binds\":[\"sqlvolume:/sqlvolume\"],\"PortBindings\":{\"1433/tcp\":[{\"HostPort\":\"1433\"}]},\"Mounts\":[{\"Type\":\"volume\",\"Source\":\"sqlvolume\",\"Target\":\"/var/opt/mssql\"}]},\"User\":\"0:0\",\"Env\":[\"MSSQL_AGENT_ENABLED=TRUE\",\"ClientTransportType=AMQP_TCP_Only\",\"MSSQL_PID=Developer\"]}" + }, + "type": "docker", + "version": "1.0", + "env": { + "ACCEPT_EULA": { + "value": "Y" + }, + "SA_PASSWORD": { + "value": "Microsoft2020$" + }, + "MSSQL_LCID": { + "value": "1033" + }, + "MSSQL_COLLATION": { + "value": "SQL_Latin1_General_CP1_CI_AS" + } + }, + "status": "running", + "restartPolicy": "always" + } + } + } + }, + "$edgeHub": { + "properties.desired": { + "schemaVersion": "1.0", + "routes": { + "SensorModuleToIoTHub": "FROM /messages/modules/SensorModule/outputs/* INTO $upstream" + }, + "storeAndForwardConfiguration": { + "timeToLiveSecs": 7200 + } + } + }, + "SensorModule": { + "properties.desired": { + "SqlConnnectionString": "Server=tcp:AzureSQLDatabaseEdge,1433;Initial Catalog=turbine-sensor-db;Persist Security Info=False;User ID=sa;Password=Microsoft2020$;MultipleActiveResultSets=False;Connection Timeout=30;", + "PushTimeInterval": 5000, + "Alert": "start", + "OnnxModelUrl": "" + } + }, + "AzureSQLDatabaseEdge": { + "properties.desired": { + "SqlPackage": "$SQL_PACKAGE" + } + } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.dockerignore b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.dockerignore new file mode 100644 index 00000000..a43e8424 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.dockerignore @@ -0,0 +1,2 @@ +[b|B]in +[O|o]bj \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.gitignore b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.gitignore new file mode 100644 index 00000000..1074c051 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/.gitignore @@ -0,0 +1,34 @@ +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc +.vs + +[Bb]in/ +[Oo]bj/ \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Constants.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Constants.cs new file mode 100644 index 00000000..a9c372bb --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Constants.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using SensorModule.Models; + +namespace SensorModule +{ + public static class Constants + { + public static class Sensors + { + public const string WindSpeedStdDev = "WindSpeedStdDev"; + public const string TurbineSpeedStdDev = "TurbineSpeedStdDev"; + public const string OverallWindDirection = "OverallWindDirection"; + public const string TurbineWindDirection = "TurbineWindDirection"; + public const string WindSpeedAverage = "WindSpeedAverage"; + public const string WindTempAverage = "WindTempAverage"; + public const string GearboxOilLevel = "GearboxOilLevel"; + public const string GearboxOilTemp = "GearboxOilTemp"; + public const string GeneratorActivePower = "GeneratorActivePower"; + public const string GeneratorSpeed = "GeneratorSpeed"; + public const string GeneratorTemp = "GeneratorTemp"; + public const string GeneratorTorque = "GeneratorTorque"; + public const string GridFrequency = "GridFrequency"; + public const string GridVoltage = "GridVoltage"; + public const string HydraulicOilPressure = "HydraulicOilPressure"; + public const string NacelleAngle = "NacelleAngle"; + public const string PitchAngle = "PitchAngle"; + public const string Vibration = "Vibration"; + public const string TurbineSpeedAverage = "TurbineSpeedAverage"; + public const string HatchSensor = "HatchSensor"; + } + + public static List SensorsList = new List{ + new Sensor {Id = Guid.NewGuid(), Type = Sensors.WindSpeedStdDev}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.TurbineSpeedStdDev}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.OverallWindDirection}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.TurbineWindDirection}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.WindSpeedAverage}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.WindTempAverage}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GearboxOilLevel}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GearboxOilTemp}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GeneratorActivePower}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GeneratorSpeed}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GeneratorTemp}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GeneratorTorque}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GridFrequency}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.GridVoltage}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.HydraulicOilPressure}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.NacelleAngle}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.PitchAngle}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.Vibration}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.TurbineSpeedAverage}, + new Sensor {Id = Guid.NewGuid(), Type = Sensors.HatchSensor} + }; + } +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStore/DatabaseContext.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStore/DatabaseContext.cs new file mode 100644 index 00000000..4348c34d --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStore/DatabaseContext.cs @@ -0,0 +1,29 @@ +using SensorModule; +using SensorModule.Models; +using Microsoft.EntityFrameworkCore; + +namespace SensorModule.DataStore +{ + + public class DatabaseContext : DbContext + { + private string _sqlConnectionString = string.Empty; + + public DatabaseContext(string sqlConnectionString) + { + this._sqlConnectionString = sqlConnectionString; + } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(_sqlConnectionString); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().ToTable("models"); + } + + public DbSet RealtimeWindTurbineRecord { get; set; } + public DbSet RealtimeSensorRecord { get; set; } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStructures/RingBuffer.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStructures/RingBuffer.cs new file mode 100644 index 00000000..b959fc0a --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/DataStructures/RingBuffer.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SensorModule.DataStructures +{ + /// + /// + /// Ring buffer. + /// + /// When writing to a full buffer: + /// PushBack -> removes this[0] / Front() + /// PushFront -> removes this[Size-1] / Back() + /// + /// this implementation is inspired by + /// http://www.boost.org/doc/libs/1_53_0/libs/Ring_buffer/doc/Ring_buffer.html + /// because I liked their interface. + /// + public class RingBuffer : IEnumerable + { + private readonly T[] _buffer; + + /// + /// The _start. Index of the first element in buffer. + /// + private int _start; + + /// + /// The _end. Index after the last element in the buffer. + /// + private int _end; + + /// + /// The _size. Buffer size. + /// + private int _size; + + public RingBuffer(int capacity) + : this(capacity, new T[] { }) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// Buffer capacity. Must be positive. + /// + /// + /// Items to fill buffer with. Items length must be less than capacity. + /// Suggestion: use Skip(x).Take(y).ToArray() to build this argument from + /// any enumerable. + /// + public RingBuffer(int capacity, T[] items) + { + if (capacity < 1) + { + throw new ArgumentException( + "Ring buffer cannot have negative or zero capacity.", nameof(capacity)); + } + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + if (items.Length > capacity) + { + throw new ArgumentException( + "Too many items to fit Ring buffer", nameof(items)); + } + + _buffer = new T[capacity]; + + Array.Copy(items, _buffer, items.Length); + _size = items.Length; + + _start = 0; + _end = _size == capacity ? 0 : _size; + } + + /// + /// Maximum capacity of the buffer. Elements pushed into the buffer after + /// maximum capacity is reached (IsFull = true), will remove an element. + /// + public int Capacity { get { return _buffer.Length; } } + + public bool IsFull + { + get + { + return Size == Capacity; + } + } + + public bool IsEmpty + { + get + { + return Size == 0; + } + } + + /// + /// Current buffer size (the number of elements that the buffer has). + /// + public int Size { get { return _size; } } + + /// + /// Element at the front of the buffer - this[0]. + /// + /// The value of the element of type T at the front of the buffer. + public T Front() + { + ThrowIfEmpty(); + return _buffer[_start]; + } + + /// + /// Element at the back of the buffer - this[Size - 1]. + /// + /// The value of the element of type T at the back of the buffer. + public T Back() + { + ThrowIfEmpty(); + return _buffer[(_end != 0 ? _end : Capacity) - 1]; + } + + public T this[int index] + { + get + { + if (IsEmpty) + { + throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer is empty", index)); + } + if (index >= _size) + { + throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}", index, _size)); + } + int actualIndex = InternalIndex(index); + return _buffer[actualIndex]; + } + set + { + if (IsEmpty) + { + throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer is empty", index)); + } + if (index >= _size) + { + throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}", index, _size)); + } + int actualIndex = InternalIndex(index); + _buffer[actualIndex] = value; + } + } + + /// + /// Pushes a new element to the back of the buffer. Back()/this[Size-1] + /// will now return this element. + /// + /// When the buffer is full, the element at Front()/this[0] will be + /// popped to allow for this new element to fit. + /// + /// Item to push to the back of the buffer + public void PushBack(T item) + { + if (IsFull) + { + _buffer[_end] = item; + Increment(ref _end); + _start = _end; + } + else + { + _buffer[_end] = item; + Increment(ref _end); + ++_size; + } + } + + /// + /// Pushes a new element to the front of the buffer. Front()/this[0] + /// will now return this element. + /// + /// When the buffer is full, the element at Back()/this[Size-1] will be + /// popped to allow for this new element to fit. + /// + /// Item to push to the front of the buffer + public void PushFront(T item) + { + if (IsFull) + { + Decrement(ref _start); + _end = _start; + _buffer[_start] = item; + } + else + { + Decrement(ref _start); + _buffer[_start] = item; + ++_size; + } + } + + /// + /// Removes the element at the back of the buffer. Decreasing the + /// Buffer size by 1. + /// + public void PopBack() + { + ThrowIfEmpty("Cannot take elements from an empty buffer."); + Decrement(ref _end); + _buffer[_end] = default(T); + --_size; + } + + /// + /// Removes the element at the front of the buffer. Decreasing the + /// Buffer size by 1. + /// + public void PopFront() + { + ThrowIfEmpty("Cannot take elements from an empty buffer."); + _buffer[_start] = default(T); + Increment(ref _start); + --_size; + } + + /// + /// Copies the buffer contents to an array, according to the logical + /// contents of the buffer (i.e. independent of the internal + /// order/contents) + /// + /// A new array with a copy of the buffer contents. + public T[] ToArray() + { + T[] newArray = new T[Size]; + int newArrayOffset = 0; + var segments = new ArraySegment[2] { ArrayOne(), ArrayTwo() }; + foreach (ArraySegment segment in segments) + { + Array.Copy(segment.Array, segment.Offset, newArray, newArrayOffset, segment.Count); + newArrayOffset += segment.Count; + } + return newArray; + } + + #region IEnumerable implementation + public IEnumerator GetEnumerator() + { + var segments = new ArraySegment[2] { ArrayOne(), ArrayTwo() }; + foreach (ArraySegment segment in segments) + { + for (int i = 0; i < segment.Count; i++) + { + yield return segment.Array[segment.Offset + i]; + } + } + } + #endregion + #region IEnumerable implementation + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)GetEnumerator(); + } + #endregion + + private void ThrowIfEmpty(string message = "Cannot access an empty buffer.") + { + if (IsEmpty) + { + throw new InvalidOperationException(message); + } + } + + /// + /// Increments the provided index variable by one, wrapping + /// around if necessary. + /// + /// + private void Increment(ref int index) + { + if (++index == Capacity) + { + index = 0; + } + } + + /// + /// Decrements the provided index variable by one, wrapping + /// around if necessary. + /// + /// + private void Decrement(ref int index) + { + if (index == 0) + { + index = Capacity; + } + index--; + } + + /// + /// Converts the index in the argument to an index in _buffer + /// + /// + /// The transformed index. + /// + /// + /// External index. + /// + private int InternalIndex(int index) + { + return _start + (index < (Capacity - _start) ? index : index - Capacity); + } + + // doing ArrayOne and ArrayTwo methods returning ArraySegment as seen here: + // http://www.boost.org/doc/libs/1_37_0/libs/Ring_buffer/doc/Ring_buffer.html#classboost_1_1Ring__buffer_1957cccdcb0c4ef7d80a34a990065818d + // http://www.boost.org/doc/libs/1_37_0/libs/Ring_buffer/doc/Ring_buffer.html#classboost_1_1Ring__buffer_1f5081a54afbc2dfc1a7fb20329df7d5b + // should help a lot with the code. + + #region Array items easy access. + // The array is composed by at most two non-contiguous segments, + // the next two methods allow easy access to those. + + private ArraySegment ArrayOne() + { + if (IsEmpty) + { + return new ArraySegment(new T[0]); + } + else if (_start < _end) + { + return new ArraySegment(_buffer, _start, _end - _start); + } + else + { + return new ArraySegment(_buffer, _start, _buffer.Length - _start); + } + } + + private ArraySegment ArrayTwo() + { + if (IsEmpty) + { + return new ArraySegment(new T[0]); + } + else if (_start < _end) + { + return new ArraySegment(_buffer, _end, 0); + } + else + { + return new ArraySegment(_buffer, 0, _end); + } + } + #endregion + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64 b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64 new file mode 100644 index 00000000..494ea45d --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64 @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env +WORKDIR /app + +COPY *.csproj ./ +RUN dotnet restore + +COPY . ./ +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim +WORKDIR /app +COPY --from=build-env /app/out ./ + +RUN useradd -ms /bin/bash moduleuser +USER moduleuser + +ENTRYPOINT ["dotnet", "SensorModule.dll"] \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64.debug b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64.debug new file mode 100644 index 00000000..19079e1d --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.amd64.debug @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base + +RUN apt-get update && \ + apt-get install -y --no-install-recommends unzip procps && \ + rm -rf /var/lib/apt/lists/* + +RUN useradd -ms /bin/bash moduleuser +USER moduleuser +RUN curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/vsdbg + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env +WORKDIR /app + +COPY *.csproj ./ +RUN dotnet restore + +COPY . ./ +RUN dotnet publish -c Debug -o out + +FROM base +WORKDIR /app +COPY --from=build-env /app/out ./ + +ENTRYPOINT ["dotnet", "SensorModule.dll"] \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7 b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7 new file mode 100644 index 00000000..e258a5e4 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7 @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env +WORKDIR /app + +COPY *.csproj ./ +RUN dotnet restore + +COPY . ./ +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim-arm32v7 +WORKDIR /app +COPY --from=build-env /app/out ./ + +RUN useradd -ms /bin/bash moduleuser +USER moduleuser + +ENTRYPOINT ["dotnet", "SensorModule.dll"] \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7.debug b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7.debug new file mode 100644 index 00000000..5f6562c7 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.arm32v7.debug @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim-arm32v7 AS base + +RUN apt-get update && \ + apt-get install -y --no-install-recommends unzip procps && \ + rm -rf /var/lib/apt/lists/* + +RUN useradd -ms /bin/bash moduleuser +USER moduleuser +RUN curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/vsdbg + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env +WORKDIR /app + +COPY *.csproj ./ +RUN dotnet restore + +COPY . ./ +RUN dotnet publish -c Debug -o out + +FROM base +WORKDIR /app +COPY --from=build-env /app/out ./ + +ENTRYPOINT ["dotnet", "SensorModule.dll"] \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.windows-amd64 b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.windows-amd64 new file mode 100644 index 00000000..f491fed3 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Dockerfile.windows-amd64 @@ -0,0 +1,13 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env +WORKDIR /app + +COPY *.csproj ./ +RUN dotnet restore + +COPY . ./ +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-nanoserver-1909 +WORKDIR /app +COPY --from=build-env /app/out ./ +ENTRYPOINT ["dotnet", "SensorModule.dll"] \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Helpers/GeneratorHelper.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Helpers/GeneratorHelper.cs new file mode 100644 index 00000000..f2d3ea05 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Helpers/GeneratorHelper.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SensorModule.Helpers +{ + public class GeneratorHelper + { + private Random _random = new Random(); + + public double GetGearBoxOilLevel() + { + int trendSign = _random.Next(-1, 1); + var offset = GetRandomNumber(0, 5); + offset = trendSign < 0 ? offset * -1 : offset; + return 40 + offset; + } + + public double GetGearBoxTemp() + { + var offset = GetRandomNumber(0, 3); + int trendSign = _random.Next(-2, 1); + var toReturn = 53.0 + offset; + + if (toReturn > 40) + { + offset = trendSign < 0 ? offset * -1 : offset; + } + else if (toReturn > 61) + { + offset *= -1; + } + + toReturn += offset; + + return toReturn > 61 ? GetRandomNumber(60, 61) : toReturn; + } + + public double GetGeneratorActivePower(double windSpeed) + { + Dictionary Values = new Dictionary() + { + { 0, -1.165047329 }, + { 1, -1.561275004 }, + { 2, -1.523817212 }, + { 3, 0.258328264 }, + { 4, 39.48032308 }, + { 5, 129.0223307 }, + { 6, 296.3115464 }, + { 7, 533.6013549 }, + { 8, 784.2038201 }, + { 9, 1033.222044 }, + { 10, 1282.494906 }, + { 11, 1522.07693 }, + { 12, 1710.51213 }, + { 13, 1847.754821 }, + { 14, 1909.292275 }, + { 15, 1929.077495 }, + }; + + var ws = Convert.ToInt32(Math.Round(windSpeed)); + ws = ws > 15 ? 15 : ws; + var apAvg = Values[ws]; + int trendSign = _random.Next(-2, 1); + + double offset; + if (ws <= 3) + { + offset = GetRandomNumber(-0.04, 0.01); + } + else if (ws <= 7) + { + offset = GetRandomNumber(0.01, 0.15); + } + else if (ws <= 12) + { + offset = GetRandomNumber(0.01, 0.63); + } + else + { + offset = GetRandomNumber(0.01, 0.94); + } + + offset = trendSign < 0 ? offset * -1 : offset; + var toReturn = (apAvg * offset) + apAvg; + return toReturn > 2100 ? 2100 : toReturn; + } + + public double GetGeneratorSpeed(double windSpeed) + { + Dictionary Values = new Dictionary() + { + { 0, 29.5339 }, + { 1, 93.4983 }, + { 2, 123.626 }, + { 3, 270.0063 }, + { 4, 850.3199 }, + { 5, 1059.4094 }, + { 6, 1317.4786 }, + { 7, 1548.3753 }, + { 8, 1635.4728 }, + { 9, 1660.1312 }, + { 10, 1685.2159 }, + { 11, 1709.3631 }, + { 12, 1722.729 }, + { 13, 1743.2982 }, + { 14, 1739.6748 }, + { 15, 1737.1417 } + }; + + var ws = Convert.ToInt32(Math.Round(windSpeed)); + ws = ws > 15 ? 15 : ws; + var gsAvg = Values[ws]; + double rate; + int trendSign = _random.Next(-3, 1); + + if (ws <= 5) + { + rate = GetRandomNumber(0, 0.7); + } + else if (ws <= 8) + { + rate = GetRandomNumber(0.001, 0.055); + } + else if (ws <= 12) + { + rate = GetRandomNumber(0.001, 0.04362); + trendSign = _random.Next(-1, 2); + } + else + { + rate = GetRandomNumber(0.001, 0.374); + trendSign = _random.Next(-1, 3); + } + rate = trendSign < 0 ? rate * -1 : rate; + var toReturn = (gsAvg * rate) + gsAvg; + return toReturn > 1800.100 + 5 ? 1800.100 : toReturn; + } + + public double GetGeneratorStatorTemp(double generatorSpeed) + { + double[] avgTempValues = { 15.0, 45.5, 55.3, 72.5 }; + var gs = Convert.ToInt32(Math.Round(generatorSpeed)); + var offset = GetRandomNumber(0, 10); + int trendSign = _random.Next(-2, 2); + offset = trendSign < 0 ? offset * -1 : offset; + double toReturn; + if (gs < 450) + { + toReturn = avgTempValues[0] + offset; + } + else if (gs < 900) + { + toReturn = avgTempValues[1] + offset; + } + else if (gs < 1350) + { + toReturn = avgTempValues[2] + offset; + } + else + { + offset = GetRandomNumber(0, 5); + toReturn = avgTempValues[3] + offset; + } + + return toReturn > 83 ? GetRandomNumber(82, 84) : toReturn; + } + + public double GetGeneratorTorque(double generatorSpeed, double activePower) + { + var toReturn = activePower == 0 || generatorSpeed == 0 ? 0 : (activePower / generatorSpeed) * (30 / 3.1416); + return toReturn > 10 ? GetRandomNumber(9, 10) : toReturn; + } + + public double GetGridFrequency(double activePower) + { + if (activePower == 0) + { + return 0; + } + else + { + var offset = GetRandomNumber(0, 0.5); + int trendSign = _random.Next(-1, 1); + offset = trendSign < 0 ? offset * -1 : offset; + + return 50 + offset; + } + } + + public double GetVoltage(double activePower) + { + var offset = GetRandomNumber(0, 20); + int trendSign = _random.Next(-1, 4); + offset = trendSign < 0 ? GetRandomNumber(0, 5) * -1 : offset; + return activePower != 0 ? 690 + offset : 0; + } + + public double GetHydraulicOilPressure() + { + var offset = GetRandomNumber(0, 10); + int trendSign = _random.Next(-2, 1); + double toReturn = 250 + offset; + + if (toReturn > 249) + { + offset = trendSign < 0 ? offset * -1 : offset; + } + else if (toReturn > 375) + { + offset *= -1; + } + + toReturn += offset; + + return toReturn > 380 ? GetRandomNumber(379, 380) : toReturn; + } + + public double GetNacelleAngle(double windDirection) + { + var offset = GetRandomNumber(0, 10); + int trendSign = _random.Next(-2, 2); + offset = trendSign < 0 ? offset * -1 : offset; + var toReturn = windDirection + offset; + return toReturn > 360 ? GetRandomNumber(358, 360) : toReturn; + } + + public double GetPitchAngle(double windSpeed) + { + Dictionary Values = new Dictionary() + { + { 0, new PitchAngleReference { Ratio = 0.98, Avg = 54.24524239 } }, + { 1, new PitchAngleReference {Ratio = 1.014, Avg = 51.67482604 } }, + { 2, new PitchAngleReference {Ratio = 1.4, Avg = 50.15667612 } }, + { 3, new PitchAngleReference {Ratio = 3.75, Avg = 44.47110192 } }, + { 4, new PitchAngleReference {Ratio = 10.9, Avg = 10.16831867 } }, + { 5, new PitchAngleReference {Ratio = 8, Avg = 3.753638458 } }, + { 6, new PitchAngleReference {Ratio = 8.6, Avg = 3.143487107 } }, + { 7, new PitchAngleReference {Ratio = 8.1, Avg = 3.851029642 } }, + { 8, new PitchAngleReference {Ratio = 7, Avg = 5.51622539 } }, + { 9, new PitchAngleReference {Ratio = 12.3, Avg = 5.607653521 } }, + { 10, new PitchAngleReference {Ratio = 11.3, Avg = 4.906807873 } }, + { 11, new PitchAngleReference {Ratio = 12.5, Avg = 4.556013361 } }, + { 12, new PitchAngleReference {Ratio = 7.6, Avg = 5.461135239 } }, + { 13, new PitchAngleReference {Ratio = 4, Avg = 6.617167409 } }, + { 14, new PitchAngleReference {Ratio = 6.5, Avg = 9.174521933 } }, + { 15, new PitchAngleReference {Ratio = 7.1, Avg = 11.5855706 } }, + }; + + var ws = Convert.ToInt32(Math.Round(windSpeed)); + ws = ws > 15 ? 15 : ws; + var paAvg = Values[ws].Avg; + var rate = GetRandomNumber(0.001, Values[ws].Ratio); + + int trendSign = _random.Next(-2, 2); + + rate = trendSign < 0 ? rate * -1 : rate; + var toReturn = (paAvg * rate) + paAvg; + + // This is needed as the avg for speeds greater than 5 are to low and the min value is to close to 0. + if (ws >= 5 && toReturn < 0) + { + toReturn = GetRandomNumber(-13, 1); + } + + return toReturn > 262.6 + 5 ? 262.6 : toReturn; + } + + public double GetVibration(double windSpeed) + { + if (windSpeed < 5) + { + return GetRandomNumber(0, 50); + } + else + { + return windSpeed * 13; + } + } + + public double GetRandomNumber(double minimum, double maximum) + { + return _random.NextDouble() * (maximum - minimum) + minimum; + } + + public bool GetRandomBool(int truePercentage = 50) + { + return _random.NextDouble() < truePercentage / 100.0; + } + + public double CalculateStandardDeviation(IEnumerable values) + { + double standardDeviation = 0; + + if (values.Any()) + { + // Compute the average. + double avg = values.Average(); + + // Perform the Sum of (value-avg)_2_2. + double sum = values.Sum(d => Math.Pow(d - avg, 2)); + + // Put it all together. + standardDeviation = Math.Sqrt((sum) / (values.Count() - 1)); + } + + return double.IsNaN(standardDeviation) ? 0 : standardDeviation; + } + } + + class PitchAngleReference + { + public double Ratio { get; set; } + public double Avg { get; set; } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Interfaces/IWindTurbineRecord.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Interfaces/IWindTurbineRecord.cs new file mode 100644 index 00000000..8fefe5a4 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Interfaces/IWindTurbineRecord.cs @@ -0,0 +1,27 @@ +namespace SensorModule.Models.Interfaces +{ + public interface IWindTurbineRecord + { + int TurbineId { get; set; } + double WindSpeedStdDev { get; set; } + double TurbineSpeedStdDev { get; set; } + double OverallWindDirection { get; set; } + double TurbineWindDirection { get; set; } + double WindSpeedAverage { get; set; } + double WindTempAverage { get; set; } + bool Precipitation { get; set; } + double GearboxOilLevel { get; set; } + double GearboxOilTemp { get; set; } + double GeneratorActivePower { get; set; } + double GeneratorSpeed { get; set; } + double GeneratorTemp { get; set; } + double GeneratorTorque { get; set; } + double GridFrequency { get; set; } + double GridVoltage { get; set; } + double HydraulicOilPressure { get; set; } + double NacelleAngle { get; set; } + double PitchAngle { get; set; } + double Vibration { get; set; } + double TurbineSpeedAverage { get; set; } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/OnnxModel.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/OnnxModel.cs new file mode 100644 index 00000000..2aaf00e2 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/OnnxModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace SensorModule.Models +{ + public class OnnxModel + { + [Key] + public int Id { get; set; } + public string Description { get; set; } + public byte[] Data { get; set; } + } +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeSensorRecord.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeSensorRecord.cs new file mode 100644 index 00000000..03c920e8 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeSensorRecord.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace SensorModule.Models +{ + public class RealtimeSensorRecord + { + [Key] + public Guid RecordId { get; set; } + public Guid TurbineId { get; set; } + public Guid SensorId { get; set; } + public string SensorType { get; set; } + public double SensorValue { get; set; } + public DateTime Timestamp { get; set; } + } +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeWindTurbineRecord.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeWindTurbineRecord.cs new file mode 100644 index 00000000..4a2c971b --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/RealtimeWindTurbineRecord.cs @@ -0,0 +1,35 @@ +using SensorModule.Models.Interfaces; +using System; +using System.ComponentModel.DataAnnotations; + +namespace SensorModule.Models +{ + public class RealtimeWindTurbineRecord : IWindTurbineRecord + { + [Key] + public Guid RecordId { get; set; } + public int TurbineId { get; set; } + public Guid TurbineGuid { get; set; } + public double WindSpeedStdDev { get; set; } + public double TurbineSpeedStdDev { get; set; } + public double OverallWindDirection { get; set; } + public double TurbineWindDirection { get; set; } + public double WindSpeedAverage { get; set; } + public double WindTempAverage { get; set; } + public bool Precipitation { get; set; } + public double GearboxOilLevel { get; set; } + public double GearboxOilTemp { get; set; } + public double GeneratorActivePower { get; set; } + public double GeneratorSpeed { get; set; } + public double GeneratorTemp { get; set; } + public double GeneratorTorque { get; set; } + public double GridFrequency { get; set; } + public double GridVoltage { get; set; } + public double HydraulicOilPressure { get; set; } + public double NacelleAngle { get; set; } + public double PitchAngle { get; set; } + public double Vibration { get; set; } + public double TurbineSpeedAverage { get; set; } + public DateTime Timestamp { get; set; } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Sensor.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Sensor.cs new file mode 100644 index 00000000..c396c5c1 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/Sensor.cs @@ -0,0 +1,11 @@ +using System; + +namespace SensorModule.Models +{ + public class Sensor + { + public Guid Id { get; set; } = Guid.NewGuid(); + + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/TrainingWindTurbineRecord.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/TrainingWindTurbineRecord.cs new file mode 100644 index 00000000..7f67fef9 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Models/TrainingWindTurbineRecord.cs @@ -0,0 +1,30 @@ +using SensorModule.Models.Interfaces; + +namespace SensorModule.Models +{ + public class TrainingWindTurbineRecord : IWindTurbineRecord + { + public int TurbineId { get; set; } + public double WindSpeedStdDev { get; set; } + public double TurbineSpeedStdDev { get; set; } + public double OverallWindDirection { get; set; } + public double TurbineWindDirection { get; set; } + public double WindSpeedAverage { get; set; } + public double WindTempAverage { get; set; } + public bool Precipitation { get; set; } + public double GearboxOilLevel { get; set; } + public double GearboxOilTemp { get; set; } + public double GeneratorActivePower { get; set; } + public double GeneratorSpeed { get; set; } + public double GeneratorTemp { get; set; } + public double GeneratorTorque { get; set; } + public double GridFrequency { get; set; } + public double GridVoltage { get; set; } + public double HydraulicOilPressure { get; set; } + public double NacelleAngle { get; set; } + public double PitchAngle { get; set; } + public double Vibration { get; set; } + public double TurbineSpeedAverage { get; set; } + public bool AlterBlades { get; set; } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Program.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Program.cs new file mode 100644 index 00000000..0eb737e2 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Program.cs @@ -0,0 +1,197 @@ +namespace SensorModule +{ + using System; + using System.IO; + using System.Runtime.InteropServices; + using System.Runtime.Loader; + using System.Security.Cryptography.X509Certificates; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Devices.Client; + using Microsoft.Azure.Devices.Client.Transport.Mqtt; + using Microsoft.Azure.Devices.Shared; + using Newtonsoft.Json; + using SensorModule.Services; + using SensorModule.Services.Interfaces; + + class Program + { + private static IDataGeneratorService _datageneratorservice; + private static IDataStoreService _datastoreservice; + private static ModuleClient _ioTHubModuleClient; + static int PushTimeInterval { get; set; } = 5000; + static bool StopWake { get; set; } = false; + static bool BlockedDataGeneration { get; set; } = false; + + static async Task Main(string[] args) + { + _datageneratorservice = new DataGeneratorService(); + _datastoreservice = new DataStoreService(); + + await Init(); + + try + { + Console.WriteLine("Truncating sensors table before start pushing data"); + _datastoreservice.TruncateRealtimeSensorRecordTable(); + } + catch(Exception e) + { + Console.WriteLine("Error truncating table: " + e.Message); + } + + Console.WriteLine($"Starting writing realtime records with {PushTimeInterval} millisecond interval to database..."); + + while (true) + { + if (!BlockedDataGeneration) + { + var records = _datageneratorservice.GenerateRealTimeSensorRecords(StopWake); + Console.WriteLine($"[{DateTime.Now}] Writing records to db."); + _datastoreservice.WriteSensorRecordsToDB(records); + } + await Task.Delay(PushTimeInterval); + } + } + + /// + /// Handles cleanup operations when app is cancelled or unloads + /// + public static Task WhenCancelled(CancellationToken cancellationToken) + { + var tcs = new TaskCompletionSource(); + cancellationToken.Register(s => ((TaskCompletionSource)s).SetResult(true), tcs); + return tcs.Task; + } + + /// + /// Initializes the ModuleClient and sets up the callback to receive + /// messages containing temperature information + /// + static async Task Init() + { + _ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(); + _ioTHubModuleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertyChanged, null).Wait(); + await SetAlertStatus("start"); + Console.WriteLine("IoT Hub module client initialized."); + + // Read from the module twin's desired properties + var moduleTwin = await _ioTHubModuleClient.GetTwinAsync(); + await OnDesiredPropertyChanged(moduleTwin.Properties.Desired, _ioTHubModuleClient); + } + + static async Task SetAlertStatus(string status) + { + Console.WriteLine($"Sending alert as reported property with value `{status}`."); + TwinCollection reportedProperties = new TwinCollection + { + ["alert"] = status + }; + + await _ioTHubModuleClient.UpdateReportedPropertiesAsync(reportedProperties); + } + + static async Task OnDesiredPropertyChanged(TwinCollection desiredProperties, object userContext) + { + try + { + Console.WriteLine("Desired property change:"); + Console.WriteLine(JsonConvert.SerializeObject(desiredProperties)); + + if (desiredProperties["SqlConnnectionString"]!=null) + { + var connectionString = desiredProperties["SqlConnnectionString"]; + Console.WriteLine($"Updating SqlConnectionString: {connectionString}"); + _datastoreservice.SetSqlConnectionString($"{connectionString}"); + } + + if (desiredProperties["PushTimeInterval"]!=null) + { + var pushTimeInterval = desiredProperties["PushTimeInterval"]; + Console.WriteLine($"Updating PushTimeInterval to: {pushTimeInterval}"); + PushTimeInterval = desiredProperties["PushTimeInterval"]; + } + + if (desiredProperties["Alert"]!=null) + { + var alertStatus = desiredProperties["Alert"]; + Console.WriteLine($"Will update alert from property to: {alertStatus}"); + await SetAlertStatus($"{alertStatus}"); + } + + if (desiredProperties["OnnxModelUrl"]!=null) + { + var onnxModelUrl = $"{desiredProperties["OnnxModelUrl"]}"; + if (!string.IsNullOrEmpty(onnxModelUrl)) + { + // Blocking data generation while we run the model + BlockedDataGeneration = true; + Console.WriteLine($"Updating OnnxModelUrl: {onnxModelUrl}"); + // Re-Create the Model Table in DB to keep only one record of the model + _datastoreservice.DropAndCreateModelTable(); + // Insert the model into the created table + _datastoreservice.InsertModelFromUrl($"{onnxModelUrl}"); + // Get model prediction of top rows + var modelResult = _datastoreservice.GetModelResult(); + Console.WriteLine($"Model prediction got a value of: {modelResult}"); + // Stabilize the data if result is > 0 + StopWake = modelResult > 0; + if (StopWake) + { + Console.WriteLine($"Stabilizing the data."); + // Clear the buffer to accelarate stabilization of the data + ClearBuffer(); + } + // If result is > 0 we need to stop the alert + var alertStatus = modelResult > 0 ? "stop":"start"; + Console.WriteLine($"Setting Alert to: {alertStatus}"); + // Update the twin module property to get new value in client + await SetAlertStatus(alertStatus); + // Unblock the data generation after running the model + BlockedDataGeneration = false; + } + else + { + BlockedDataGeneration = true; + StopWake = false; + Console.WriteLine($"Is data stabilized? {StopWake}"); + _datastoreservice.TruncateRealtimeSensorRecordTable(); + ClearBuffer(); + BlockedDataGeneration = false; + } + } + else + { + BlockedDataGeneration = true; + StopWake = false; + Console.WriteLine($"Is data stabilized? {StopWake}"); + _datastoreservice.TruncateRealtimeSensorRecordTable(); + ClearBuffer(); + BlockedDataGeneration = false; + } + } + catch (AggregateException ex) + { + foreach (Exception exception in ex.InnerExceptions) + { + Console.WriteLine(); + Console.WriteLine("Error when receiving desired property: {0}", exception); + } + } + catch (Exception ex) + { + Console.WriteLine(); + Console.WriteLine("Error when receiving desired property: {0}", ex); + } + } + + static void ClearBuffer() + { + _datageneratorservice.ClearBuffer(); + // Add new records as a reference for the next record generation + _datageneratorservice.GenerateRealTimeSensorRecords(StopWake); + _datageneratorservice.GenerateRealTimeSensorRecords(StopWake); + } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/SensorModule.csproj b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/SensorModule.csproj new file mode 100644 index 00000000..9f611ee3 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/SensorModule.csproj @@ -0,0 +1,35 @@ + + + Exe + netcoreapp3.1 + latest + + + + True + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataExporterService.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataExporterService.cs new file mode 100644 index 00000000..685b1ce4 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataExporterService.cs @@ -0,0 +1,144 @@ +using SensorModule.Models; +using SensorModule.Services.Interfaces; +using Parquet; +using Parquet.Data; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace SensorModule.Services +{ + public class DataExporterService : IDataExporterService + { + public void SaveParquet(IEnumerable records, string outputFile) + { + //create data columns with schema metadata and the data you need + var turbineId = new DataColumn( + new DataField("TurbineId"), + records.Select(x => x.TurbineId).ToArray()); + + var gearboxOilLevel = new DataColumn( + new DataField("GearboxOilLevel"), + records.Select(x => x.GearboxOilLevel).ToArray()); + + var gearboxOilTemp = new DataColumn( + new DataField("GearboxOilTemp"), + records.Select(x => x.GearboxOilTemp).ToArray()); + + var generatorActivePower = new DataColumn( + new DataField("GeneratorActivePower"), + records.Select(x => x.GeneratorActivePower).ToArray()); + + var generatorSpeed = new DataColumn( + new DataField("GeneratorSpeed"), + records.Select(x => x.GeneratorSpeed).ToArray()); + + var generatorTemp = new DataColumn( + new DataField("GeneratorTemp"), + records.Select(x => x.GeneratorTemp).ToArray()); + + var generatorTorque = new DataColumn( + new DataField("GeneratorTorque"), + records.Select(x => x.GeneratorTorque).ToArray()); + + var gridFrequency = new DataColumn( + new DataField("GridFrequency"), + records.Select(x => x.GridFrequency).ToArray()); + + var gridVoltage = new DataColumn( + new DataField("GridVoltage"), + records.Select(x => x.GridVoltage).ToArray()); + + var hydraulicOilPressure = new DataColumn( + new DataField("HydraulicOilPressure"), + records.Select(x => x.HydraulicOilPressure).ToArray()); + + var nacelleAngle = new DataColumn( + new DataField("NacelleAngle"), + records.Select(x => x.NacelleAngle).ToArray()); + + var overallWindDirection = new DataColumn( + new DataField("OverallWindDirection"), + records.Select(x => x.OverallWindDirection).ToArray()); + + var overalWindSpeedStdDev = new DataColumn( + new DataField("WindSpeedStdDev"), + records.Select(x => x.WindSpeedStdDev).ToArray()); + + var precipitation = new DataColumn( + new DataField("Precipitation"), + records.Select(x => x.Precipitation).ToArray()); + + var turbineWindDirection = new DataColumn( + new DataField("TurbineWindDirection"), + records.Select(x => x.TurbineWindDirection).ToArray()); + + var turbineSpeedStdDev = new DataColumn( + new DataField("TurbineSpeedStdDev"), + records.Select(x => x.TurbineSpeedStdDev).ToArray()); + + var windSpeedAverage = new DataColumn( + new DataField("WindSpeedAverage"), + records.Select(x => x.WindSpeedAverage).ToArray()); + + var windTempAverage = new DataColumn( + new DataField("WindTempAverage"), + records.Select(x => x.WindTempAverage).ToArray()); + + var alterBlades = new DataColumn( + new DataField("AlterBlades"), + records.Select(x => x.AlterBlades).ToArray()); + + var pitchAngle = new DataColumn( + new DataField("PitchAngle"), + records.Select(x => x.PitchAngle).ToArray()); + + var vibration = new DataColumn( + new DataField("Vibration"), + records.Select(x => x.Vibration).ToArray()); + + var turbineSpeedAverage = new DataColumn( + new DataField("TurbineSpeedAverage"), + records.Select(x => x.TurbineSpeedAverage).ToArray()); + + // create file schema + var schema = new Schema(turbineId.Field, gearboxOilLevel.Field, gearboxOilTemp.Field, generatorActivePower.Field, generatorSpeed.Field, generatorTemp.Field, generatorTorque.Field, gridFrequency.Field, gridVoltage.Field, hydraulicOilPressure.Field, + nacelleAngle.Field, overallWindDirection.Field, overalWindSpeedStdDev.Field, precipitation.Field, turbineWindDirection.Field, turbineSpeedStdDev.Field, windSpeedAverage.Field, windTempAverage.Field, pitchAngle.Field, vibration.Field, turbineSpeedAverage.Field, alterBlades.Field); + + var outputPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + + using (Stream fileStream = File.OpenWrite(Path.Combine(outputPath, $"{outputFile}.parquet"))) + { + using (var parquetWriter = new ParquetWriter(schema, fileStream)) + { + // create a new row group in the file + using (ParquetRowGroupWriter groupWriter = parquetWriter.CreateRowGroup()) + { + groupWriter.WriteColumn(turbineId); + groupWriter.WriteColumn(gearboxOilLevel); + groupWriter.WriteColumn(gearboxOilTemp); + groupWriter.WriteColumn(generatorActivePower); + groupWriter.WriteColumn(generatorSpeed); + groupWriter.WriteColumn(generatorTemp); + groupWriter.WriteColumn(generatorTorque); + groupWriter.WriteColumn(gridFrequency); + groupWriter.WriteColumn(gridVoltage); + groupWriter.WriteColumn(hydraulicOilPressure); + groupWriter.WriteColumn(nacelleAngle); + groupWriter.WriteColumn(overallWindDirection); + groupWriter.WriteColumn(overalWindSpeedStdDev); + groupWriter.WriteColumn(precipitation); + groupWriter.WriteColumn(turbineWindDirection); + groupWriter.WriteColumn(turbineSpeedStdDev); + groupWriter.WriteColumn(windSpeedAverage); + groupWriter.WriteColumn(windTempAverage); + groupWriter.WriteColumn(pitchAngle); + groupWriter.WriteColumn(vibration); + groupWriter.WriteColumn(turbineSpeedAverage); + groupWriter.WriteColumn(alterBlades); + } + } + } + } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataGeneratorService.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataGeneratorService.cs new file mode 100644 index 00000000..dfc963ac --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataGeneratorService.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SensorModule.DataStructures; +using SensorModule.Helpers; +using SensorModule.Models; +using SensorModule.Services.Interfaces; + +namespace SensorModule.Services +{ + public class DataGeneratorService : IDataGeneratorService + { + private RingBuffer ringBuffer = new RingBuffer(100); + private GeneratorHelper gen = new GeneratorHelper(); + private readonly Guid TurbineGuid = Guid.NewGuid(); + + private RealtimeWindTurbineRecord GenerateFirstRealtimeRecord() + { + var windSpeed = gen.GetRandomNumber(15.0, 25.0); + var generatorSpeed = gen.GetGeneratorSpeed(windSpeed); + var activePower = gen.GetGeneratorActivePower(windSpeed); + var windDirection = gen.GetRandomNumber(41.0, 44.0); + var toReturn = new RealtimeWindTurbineRecord + { + RecordId = Guid.NewGuid(), + TurbineId = 15443, + TurbineGuid = TurbineGuid, + GearboxOilLevel = gen.GetGearBoxOilLevel(), + GearboxOilTemp = gen.GetGearBoxTemp(), + GeneratorActivePower = gen.GetGeneratorActivePower(windSpeed), + GeneratorSpeed = generatorSpeed, + GeneratorTemp = gen.GetGeneratorStatorTemp(generatorSpeed), + GeneratorTorque = gen.GetGeneratorTorque(generatorSpeed, activePower), + GridFrequency = gen.GetGridFrequency(activePower), + GridVoltage = gen.GetVoltage(activePower), + HydraulicOilPressure = gen.GetHydraulicOilPressure(), + NacelleAngle = gen.GetNacelleAngle(windDirection), + PitchAngle = gen.GetPitchAngle(windSpeed), + Vibration = gen.GetVibration(windSpeed), + WindSpeedAverage = windSpeed, + Precipitation = gen.GetRandomBool(20), + WindTempAverage = gen.GetRandomNumber(-10.0, 30), + OverallWindDirection = windDirection, + TurbineWindDirection = windDirection * gen.GetRandomNumber(0.97, 1.03), + TurbineSpeedAverage = windSpeed * gen.GetRandomNumber(0.97f, 1.03f), + WindSpeedStdDev = 4.885422, + TurbineSpeedStdDev = 4.978619, + Timestamp = DateTime.Now + }; + + ringBuffer.PushFront(toReturn); + return toReturn; + } + + public void ClearBuffer() + { + ringBuffer = new RingBuffer(100); + } + + private RealtimeWindTurbineRecord GenerateRegularRealtimeRecord(bool stopWake) + { + var currentRecords = ringBuffer.ToList(); + double windDirection = currentRecords.Select(x => x.OverallWindDirection).Average() * gen.GetRandomNumber(0.9, 1.1); + var windSpeed = currentRecords.Select(x => x.WindSpeedAverage).Average() * gen.GetRandomNumber(0.9, 1.1); + double turbineSpeed = windSpeed * gen.GetRandomNumber(0.25, 1.75); + if (stopWake) + { + turbineSpeed = windSpeed * gen.GetRandomNumber(0.97, 1.03); + windDirection = gen.GetRandomNumber(40.0, 45.0); + } + + var generatorSpeed = gen.GetGeneratorSpeed(windSpeed); + var activePower = gen.GetGeneratorActivePower(windSpeed); + var toReturn = new RealtimeWindTurbineRecord + { + RecordId = Guid.NewGuid(), + TurbineId = 15443, + TurbineGuid = TurbineGuid, + GearboxOilLevel = currentRecords.Select(x => x.GearboxOilLevel).Average() * gen.GetRandomNumber(0.95, 1.05), + GearboxOilTemp = currentRecords.Select(x => x.GearboxOilTemp).Average() * gen.GetRandomNumber(0.95, 1.05), + GeneratorActivePower = gen.GetGeneratorActivePower(windSpeed), + GeneratorSpeed = generatorSpeed, + GeneratorTemp = gen.GetGeneratorStatorTemp(generatorSpeed), + GeneratorTorque = gen.GetGeneratorTorque(generatorSpeed, activePower), + GridFrequency = gen.GetGridFrequency(activePower), + GridVoltage = gen.GetVoltage(activePower), + HydraulicOilPressure = gen.GetHydraulicOilPressure(), + NacelleAngle = gen.GetNacelleAngle(windDirection), + PitchAngle = gen.GetPitchAngle(windSpeed), + Vibration = gen.GetVibration(windSpeed), + WindSpeedAverage = windSpeed, + Precipitation = gen.GetRandomBool(20), + WindTempAverage = gen.GetRandomNumber(-10.0, 30), + OverallWindDirection = windDirection, + TurbineWindDirection = windDirection * gen.GetRandomNumber(0.97, 1.03), + TurbineSpeedAverage = turbineSpeed, + WindSpeedStdDev = gen.CalculateStandardDeviation(ringBuffer.ToList().Select(x => x.WindSpeedAverage).TakeLast(10)), + TurbineSpeedStdDev = gen.CalculateStandardDeviation(ringBuffer.ToList().Select(x => x.TurbineSpeedAverage).TakeLast(10)), + Timestamp = DateTime.Now + }; + + ringBuffer.PushFront(toReturn); + return toReturn; + } + + public RealtimeWindTurbineRecord GenerateRealTimeRecords(bool stopWake) + { + if (ringBuffer.IsEmpty) + { + return GenerateFirstRealtimeRecord(); + } + else + { + return GenerateRegularRealtimeRecord(stopWake); + } + } + + public IEnumerable GenerateTrainingRecords(int amountOfRecords) + { + var toReturn = new List(); + + for (var i = 0; i < amountOfRecords; i++) + { + var windDirection = gen.GetRandomNumber(0.0, 360.0); + var windSpeed = gen.GetRandomNumber(10.0, 25.0); + var turbineSpeed = windSpeed * gen.GetRandomNumber(0.97, 1.03); + if (((double)i / amountOfRecords) >= 0.8) + { + turbineSpeed = windSpeed * gen.GetRandomNumber(0.25, 1.75); + windDirection = gen.GetRandomNumber(40.0, 45.0); + } + + var generatorSpeed = gen.GetGeneratorSpeed(windSpeed); + var activePower = gen.GetGeneratorActivePower(windSpeed); + toReturn.Add(new TrainingWindTurbineRecord + { + TurbineId = i, + GearboxOilLevel = gen.GetGearBoxOilLevel(), + GearboxOilTemp = gen.GetGearBoxTemp(), + GeneratorActivePower = gen.GetGeneratorActivePower(windSpeed), + GeneratorSpeed = generatorSpeed, + GeneratorTemp = gen.GetGeneratorStatorTemp(generatorSpeed), + GeneratorTorque = gen.GetGeneratorTorque(generatorSpeed, activePower), + GridFrequency = gen.GetGridFrequency(activePower), + GridVoltage = gen.GetVoltage(activePower), + HydraulicOilPressure = gen.GetHydraulicOilPressure(), + NacelleAngle = gen.GetNacelleAngle(windDirection), + PitchAngle = gen.GetPitchAngle(windSpeed), + Vibration = gen.GetVibration(windSpeed), + WindSpeedAverage = windSpeed, + Precipitation = gen.GetRandomBool(20), + WindTempAverage = gen.GetRandomNumber(-10.0, 30), + OverallWindDirection = windDirection, + TurbineWindDirection = windDirection * gen.GetRandomNumber(0.97, 1.03), + TurbineSpeedAverage = turbineSpeed, + WindSpeedStdDev = gen.CalculateStandardDeviation(toReturn.Select(x => x.WindSpeedAverage).TakeLast(10)), + TurbineSpeedStdDev = gen.CalculateStandardDeviation(toReturn.Select(x => x.TurbineSpeedAverage).TakeLast(10)), + AlterBlades = (gen.CalculateStandardDeviation(toReturn.Select(x => x.TurbineSpeedAverage).TakeLast(10)) - gen.CalculateStandardDeviation(toReturn.Select(x => x.WindSpeedAverage).TakeLast(10))) > 1.0 + }); + } + + return toReturn.OrderBy(a => Guid.NewGuid()).ToList(); + } + + public List GenerateRealTimeSensorRecords(bool stopWake) + { + RealtimeWindTurbineRecord windTurbineRecord; + if (ringBuffer.IsEmpty) + { + windTurbineRecord = GenerateFirstRealtimeRecord(); + } + else + { + windTurbineRecord = GenerateRegularRealtimeRecord(stopWake); + } + var records = Constants.SensorsList.Select(sensor => + { + double sensorValue = sensor.Type switch + { + Constants.Sensors.WindSpeedStdDev => windTurbineRecord.WindSpeedStdDev, + Constants.Sensors.TurbineSpeedStdDev => windTurbineRecord.TurbineSpeedStdDev, + Constants.Sensors.OverallWindDirection => windTurbineRecord.OverallWindDirection, + Constants.Sensors.TurbineWindDirection => windTurbineRecord.TurbineWindDirection, + Constants.Sensors.WindSpeedAverage => windTurbineRecord.WindSpeedAverage, + Constants.Sensors.WindTempAverage => windTurbineRecord.WindTempAverage, + Constants.Sensors.GearboxOilLevel => windTurbineRecord.GearboxOilLevel, + Constants.Sensors.GearboxOilTemp => windTurbineRecord.GearboxOilTemp, + Constants.Sensors.GeneratorActivePower => windTurbineRecord.GeneratorActivePower, + Constants.Sensors.GeneratorSpeed => windTurbineRecord.GeneratorSpeed, + Constants.Sensors.GeneratorTemp => windTurbineRecord.GeneratorTemp, + Constants.Sensors.GeneratorTorque => windTurbineRecord.GeneratorTorque, + Constants.Sensors.GridFrequency => windTurbineRecord.GridFrequency, + Constants.Sensors.GridVoltage => windTurbineRecord.GridVoltage, + Constants.Sensors.HydraulicOilPressure => windTurbineRecord.HydraulicOilPressure, + Constants.Sensors.NacelleAngle => windTurbineRecord.NacelleAngle, + Constants.Sensors.PitchAngle => windTurbineRecord.PitchAngle, + Constants.Sensors.Vibration => windTurbineRecord.Vibration, + Constants.Sensors.TurbineSpeedAverage => windTurbineRecord.TurbineSpeedAverage, + Constants.Sensors.HatchSensor => 0, + _ => 0, + }; + return new RealtimeSensorRecord + { + RecordId = Guid.NewGuid(), + TurbineId = windTurbineRecord.TurbineGuid, + SensorId = sensor.Id, + SensorType = sensor.Type, + SensorValue = sensorValue, + Timestamp = windTurbineRecord.Timestamp + }; + }); + return records.ToList(); + } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataStoreService.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataStoreService.cs new file mode 100644 index 00000000..e08176ef --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/DataStoreService.cs @@ -0,0 +1,117 @@ +using SensorModule.DataStore; +using SensorModule.Models; +using SensorModule.Services.Interfaces; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using System; +using System.Data; +using System.Net; +using System.Collections.Generic; + +namespace SensorModule.Services +{ + public class DataStoreService : IDataStoreService + { + private const string STOREDPROCNAME = "ModelResponse"; + private const string RUNMODELSTOREDPROCNAME = "RunModel"; + private const string MODELTABLENAME = "models"; + private const string TRUNCATETABLESTOREDPROCNAME = "TruncateRealtimeWindTurbineRecord"; + private const string TRUNCATEREALTIMESENSORTABLESTOREDPROCNAME = "TruncateRealtimeSensorRecords"; + private string _sqlConnectionString; + + public DataStoreService() + { + } + + public void SetSqlConnectionString(string sqlConnectionString) + { + _sqlConnectionString = sqlConnectionString; + } + + public bool ModelResponse() + { + SqlParameter[] @params = + { + new SqlParameter("@returnVal", SqlDbType.Bit) {Direction = ParameterDirection.Output} + }; + + using (var dbContext = new DatabaseContext(_sqlConnectionString)) + { + dbContext.Database.ExecuteSqlRaw($"exec @returnVal=" + STOREDPROCNAME, @params); + } + + return (bool)@params[0].Value; + } + + public void TruncateRealtimeWindTurbineRecordTable() + { + using (var dbContext = new DatabaseContext(_sqlConnectionString)) + { + dbContext.Database.ExecuteSqlRaw($"exec " + TRUNCATETABLESTOREDPROCNAME); + } + } + + public void WriteToDB(RealtimeWindTurbineRecord record) + { + using (var db = new DatabaseContext(_sqlConnectionString)) + { + record.Timestamp = DateTime.Now; + db.Add(record); + db.SaveChanges(); + } + } + + public void DropAndCreateModelTable() + { + using var dbContext = new DatabaseContext(_sqlConnectionString); + dbContext.Database.ExecuteSqlRaw($"drop table if exists {MODELTABLENAME}"); + dbContext.Database.ExecuteSqlRaw($"create table {MODELTABLENAME} ([id] [int] IDENTITY(1,1) NOT NULL, [data] [varbinary](max) NULL, [description] varchar(1000))"); + } + + public void InsertModelFromUrl(string url) + { + using var webClient = new WebClient(); + byte[] modelBytes = webClient.DownloadData(url); + using var dbContext = new DatabaseContext(_sqlConnectionString); + var query = $"insert into {MODELTABLENAME} ([description], [data]) values ('Onnx Model',?)"; + var model = new OnnxModel { Description = "Onnx Model", Data = modelBytes }; + dbContext.Add(model); + dbContext.SaveChanges(); + } + + public int GetModelResult() + { + SqlParameter[] @params = + { + new SqlParameter("@Result", SqlDbType.Int) {Direction = ParameterDirection.Output} + }; + + using (var dbContext = new DatabaseContext(_sqlConnectionString)) + { + dbContext.Database.ExecuteSqlRaw($"EXEC " + RUNMODELSTOREDPROCNAME + " @Result OUTPUT", @params); + } + + return (int)@params[0].Value; + } + + public void TruncateRealtimeSensorRecordTable() + { + using (var dbContext = new DatabaseContext(_sqlConnectionString)) + { + dbContext.Database.ExecuteSqlRaw($"exec " + TRUNCATEREALTIMESENSORTABLESTOREDPROCNAME); + } + } + + public void WriteSensorRecordsToDB(List records) + { + using (var dbContext = new DatabaseContext(_sqlConnectionString)) + { + records.ForEach(record => + { + dbContext.Add(record); + }); + dbContext.SaveChanges(); + } + } + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataExporterService.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataExporterService.cs new file mode 100644 index 00000000..acab5f36 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataExporterService.cs @@ -0,0 +1,10 @@ +using SensorModule.Models; +using System.Collections.Generic; + +namespace SensorModule.Services.Interfaces +{ + public interface IDataExporterService + { + void SaveParquet(IEnumerable records, string outputFile); + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataGeneratorService.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataGeneratorService.cs new file mode 100644 index 00000000..41f7613a --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataGeneratorService.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using SensorModule.Models; + +namespace SensorModule.Services.Interfaces +{ + public interface IDataGeneratorService + { + IEnumerable GenerateTrainingRecords(int amountOfRecords); + + RealtimeWindTurbineRecord GenerateRealTimeRecords(bool stopWake); + List GenerateRealTimeSensorRecords(bool stopWake); + + void ClearBuffer(); + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataStoreService.cs b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataStoreService.cs new file mode 100644 index 00000000..4ddec645 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/Services/Interfaces/IDataStoreService.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using SensorModule.Models; + +namespace SensorModule.Services.Interfaces +{ + public interface IDataStoreService + { + void WriteToDB(RealtimeWindTurbineRecord record); + bool ModelResponse(); + void TruncateRealtimeWindTurbineRecordTable(); + void SetSqlConnectionString(string sqlConnectionString); + void DropAndCreateModelTable(); + void InsertModelFromUrl(string url); + int GetModelResult(); + void TruncateRealtimeSensorRecordTable(); + void WriteSensorRecordsToDB(List records); + } +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/module.json b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/module.json new file mode 100644 index 00000000..5542c079 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/SensorSolution/modules/SensorModule/module.json @@ -0,0 +1,20 @@ +{ + "$schema-version": "0.0.1", + "description": "", + "image": { + "repository": "$CONTAINER_REGISTRY_NAME/sensormodule", + "tag": { + "version": "0.0.24", + "platforms": { + "amd64": "./Dockerfile.amd64", + "amd64.debug": "./Dockerfile.amd64.debug", + "arm32v7": "./Dockerfile.arm32v7", + "arm32v7.debug": "./Dockerfile.arm32v7.debug", + "windows-amd64": "./Dockerfile.windows-amd64" + } + }, + "buildOptions": [], + "contextPath": "./" + }, + "language": "csharp" +} diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/sensor-solution.code-workspace b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/sensor-solution.code-workspace new file mode 100644 index 00000000..876a1499 --- /dev/null +++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/edge/sensor-solution.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/Github repo.png b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/Github repo.png new file mode 100644 index 0000000000000000000000000000000000000000..5aee7afcebca2620e182cd410aa04b444901b8f8 GIT binary patch literal 55461 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

    Ym$ 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 SfN%D@rLLl_T&ZXs`u_lt2$3`Z literal 0 HcmV?d00001 diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/copy.png b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/images/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..5b3ef2649806f131c1021047360a351965d4e63d GIT binary patch literal 1560 zcmV+z2Iu*SP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1+7U$K~#8N?VRsV zTU8jxpVCAIY{~pFCY^C=GJjFrRtmT!OSbq{c(p{6{Sk}NmqN?XWO1VLPqA#ve51Zq zO8H^SA|#rSP~x_@j1pi&sL%79bM8I&wzt3TDMh)TaQmF^J^7I_%WX)(5o;T z7Enri`*R$(wzfGE=oOYD?O$@0&Y-oH%ZFU!_G7(=hXUV4$uOxH=k5-)Q_H=G?-Ug9 zf&CRTGfN8u{RK=T=eZ5VG=v0wSFB+!<&W4Q{G&WbmNK+~6eV1gc2 zCz@fxRdQFkx?eiKnuqJ3d{OW5qDc&E0dv{Xs!`&2*>Dt6prlcsGn_YY|wOB$k>VTF&K!)QB{8(jmE6>WuX5&#*zx7 zB(rgJth!kfvZD0szWa8P=e)>ce;ZBQkgEFYFcWd4R~YTuKCCc`)bi+)Nd839hdvt{ zkWVF`{zL|y!V0Npy)`ubb2L9alaVV47G~3B^G)eoPWUodzQURh^`}V0yK=k`TToSE z>#!(*JUFdCGpFv`#|t(%SM*Xz~-M}?8W zeZ9THXgo~BbF#wbGt)KY*BYC(t~=&(!k3{|^|1dsR)x(`55v7H?!zR!m&t2M1NvP5 zAP?uT5_DY+mG8B_sxZz|RLEx<(Ah=mVO|x6$y&M*?ZB<8QEh#s;=E~%I4X?pJLo=4 zIj!zX{OI#qg*CcfDm4WoBg2k)cjZ>P-jB{vg<%|u$N2dUd=E>dZo^no-O;xm+c0vK zBY&CG3aP30P<2qG>Z9n$$Dnj-ip(14)9(4XnO|WR4?Ul0mX?&>hsj_}Te!6w=k=+n zJCJZxmUDjpwZQ6Ov%h8eL=^ACOrm-iS3`|ZWjW2NoqC?97P5FBM!bJedzc@y_5-J3 zYTFu)3er?@?!zm;QQYhKjd~M1a1}N({2?S0V^SSF>>jotdRT81dl*|w-43I=FKJN6 z;zJk#GHL29)$^66rk@#U4heq`(8;YgXXSjke0|=mwkca-&HMh;hJJmyO7r`A{AjC( zWvGXtmC@+CCR+gX4~QN%lm4Zq{93hIH-qb%SkFr@Vhide%?^jcW(V(=#Fp+XC4C!wsZ}C6O zG_>Y;A|Vyx@%$EkwZv6nMgCcmoyLPSeS~(H^xNi?0QeWYjP>7^I7U4H0000< KMNUMnLSTXe6%1?#fc=0yXXTLsd6L3i2r)R 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