From de19cccb06df6069c058543e60f2051540049a84 Mon Sep 17 00:00:00 2001 From: wanggang <76527413@qq.com> Date: Mon, 11 Nov 2019 19:06:51 +0800 Subject: [PATCH] update Former-commit-id: c8bd677165dbf2783c6f21f91d4955d89cc6cdc6 --- .../Services/IoTCenter/IoTCenterHub.cs | 15 ++-- .../IoTCenter/Controllers/AppController.cs | 47 ++++++++++++ .../Services/IoTCenterEventHandler.cs | 68 ++++++++++++++++++ projects/IoTCenter/Views/Home/Node.cshtml | 14 ++++ projects/IoTCenter/iotcenter.db | Bin 430080 -> 430080 bytes .../Assets/StreamingAssets/wwwroot/js/node.js | 65 +++++++++++++++++ .../StreamingAssets/wwwroot/lib/toastr.meta | 8 +++ .../wwwroot/lib/toastr/toastr.min.css.meta | 7 ++ .../wwwroot/lib/toastr/toastr.min.js.meta | 7 ++ projects/IoTNode/iotnode.db | Bin 462848 -> 462848 bytes 10 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr.meta create mode 100644 projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.css.meta create mode 100644 projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.js.meta diff --git a/projects/IoT.Shared/Services/IoTCenter/IoTCenterHub.cs b/projects/IoT.Shared/Services/IoTCenter/IoTCenterHub.cs index 48e71271..7af0c9ab 100644 --- a/projects/IoT.Shared/Services/IoTCenter/IoTCenterHub.cs +++ b/projects/IoT.Shared/Services/IoTCenter/IoTCenterHub.cs @@ -318,13 +318,13 @@ namespace IoTCenter.Services } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] - private async Task LogToInfluxdbAsync(EditDataModel model, string deviceNumber) + public async Task LogToInfluxdbAsync(Data data) { - if (string.IsNullOrEmpty(model.Value)) + if (string.IsNullOrEmpty(data.Value)) { return; } - if (model.Type != DeviceDataType.Int && model.Type != DeviceDataType.Float) + if (data.Type != DeviceDataType.Int && data.Type != DeviceDataType.Float) { return; } @@ -341,9 +341,10 @@ namespace IoTCenter.Services { Timestamp = DateTime.UtcNow }; - row.Fields.Add("DeviceNumber", deviceNumber); - row.Fields.Add("DeviceName", model.Name); - row.Fields.Add(model.Key, this.GetDataValue(model)); + var device = this._deviceRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == data.DeviceId); + row.Fields.Add("DeviceNumber", device.Number); + row.Fields.Add("DeviceName", device.Name); + row.Fields.Add(data.Key, this.GetDataValue(data)); await client.WriteAsync(dbName, measurementName, new List { row }); } @@ -353,7 +354,7 @@ namespace IoTCenter.Services } } - private object GetDataValue(EditDataModel model) + private object GetDataValue(Data model) { return model.Type switch { diff --git a/projects/IoTCenter/Controllers/AppController.cs b/projects/IoTCenter/Controllers/AppController.cs index 7ada4eaa..84b91f40 100644 --- a/projects/IoTCenter/Controllers/AppController.cs +++ b/projects/IoTCenter/Controllers/AppController.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Linq; using Vibrant.InfluxDB.Client; @@ -175,6 +176,52 @@ namespace IoTCenter.Controllers } } + public IActionResult GetChartData(string number, string time) + { + var device = this._deviceRepo.ReadOnlyTable().Include(o => o.Data).FirstOrDefault(o => o.Number == number); + var url = this._configuration["influxdb:url"]; + var usr = this._configuration["influxdb:usr"]; + var pwd = this._configuration["influxdb:pwd"]; + var dbName = "iot"; + var measurementName = "data"; + var list = new List(); + using (var client = new InfluxClient(new Uri(url), usr, pwd)) + { + var fileds = String.Join(',', device.Data.Where(o => o.Type == DeviceDataType.Int || o.Type == DeviceDataType.Float).Select(o => o.Key)); + var query = $"select {fileds} from {measurementName} where time>now()-{time} and DeviceNumber = '{device.Number}' limit 10000"; + var result = client.ReadAsync(dbName, query).Result; + var rows = result.Results.FirstOrDefault()? + .Series.FirstOrDefault()? + .Rows; + foreach (var data in device.Data.Where(o => o.Type == DeviceDataType.Int || o.Type == DeviceDataType.Float)) + { + list.Add(new + { + label = data.Name, + data = rows != null ? rows.Where(o => o.Fields.ContainsKey(data.Key)).Select(o => o.GetField(data.Key)).ToList() : new List(), + backgroundColor = this.GetColor(data.Key), + fill = false + }); + } + var labels = rows != null ? rows.Select(o => o.Timestamp.Value).Select(o => o.ToString("MM-dd HH:mm")).ToList().Distinct() : new List(); + var model = new + { + datasets = list, + labels = labels + }; + return Json(model); + } + } + + private string GetColor(object key) + { + var randomGen = new Random(); + var names = (KnownColor[])Enum.GetValues(typeof(KnownColor)); + var randomColorName = names[randomGen.Next(names.Length)]; + var randomColor = Color.FromKnownColor(randomColorName); + return randomColor.Name; + } + /************************************************************/ public IActionResult GetDevice(string token, string number) diff --git a/projects/IoTCenter/Services/IoTCenterEventHandler.cs b/projects/IoTCenter/Services/IoTCenterEventHandler.cs index 6ae6cf6b..0af813fa 100644 --- a/projects/IoTCenter/Services/IoTCenterEventHandler.cs +++ b/projects/IoTCenter/Services/IoTCenterEventHandler.cs @@ -1,13 +1,19 @@ using Application.Domain.Entities; using CSScriptLib; using Hangfire; +using Infrastructure.Data; using Infrastructure.Events; using Infrastructure.Extensions; using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Vibrant.InfluxDB.Client; +using Vibrant.InfluxDB.Client.Rows; namespace IoTCenter.Services { @@ -172,12 +178,14 @@ namespace IoTCenter.Services { this.Notify(message); this.TiggerHandle(message); + this.LogData(message.Data); } public void Handle(EntityUpdatedEvent message) { this.Notify(message); this.TiggerHandle(message); + this.LogData(message.Data); } public void Handle(EntityDeletedEvent message) @@ -277,5 +285,65 @@ namespace IoTCenter.Services } }); } + + private void LogData(Data data) + { + using var scope = this._sp.CreateScope(); + var _cfg = scope.ServiceProvider.GetRequiredService(); + var url = _cfg["influxdb:url"]; + var usr = _cfg["influxdb:usr"]; + var pwd = _cfg["influxdb:pwd"]; + var deviceRepo = scope.ServiceProvider.GetRequiredService>(); + var device = deviceRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == data.DeviceId); + Task.Run(async () => + { + try + { + if (string.IsNullOrEmpty(data.Value)) + { + return; + } + if (data.Type != DeviceDataType.Int && data.Type != DeviceDataType.Float) + { + return; + } + try + { + var dbName = "iot"; + var measurementName = "data"; + using var client = new InfluxClient(new Uri(url), usr, pwd); + await client.CreateDatabaseAsync(dbName); + var row = new DynamicInfluxRow + { + Timestamp = DateTime.UtcNow + }; + row.Fields.Add("DeviceNumber", device.Number); + row.Fields.Add("DeviceName", device.Name); + row.Fields.Add(data.Key, this.GetDataValue(data)); + await client.WriteAsync(dbName, measurementName, new List { row }); + } + catch (Exception ex) + { + ex.PrintStack(); + } + } + catch (Exception ex) + { + ex.PrintStack(); + } + }); + } + + private object GetDataValue(Data model) + { + return model.Type switch + { + DeviceDataType.Int => Convert.ToInt32(model.Value), + + DeviceDataType.Float => Convert.ToSingle(model.Value), + + _ => model.Value, + }; + } } } \ No newline at end of file diff --git a/projects/IoTCenter/Views/Home/Node.cshtml b/projects/IoTCenter/Views/Home/Node.cshtml index 019cc88d..30ab3093 100644 --- a/projects/IoTCenter/Views/Home/Node.cshtml +++ b/projects/IoTCenter/Views/Home/Node.cshtml @@ -699,6 +699,20 @@ +
+
+
+
+
+ {{device.DisplayName}} +
+
+
+ +
+
+
+
@section scripts{ diff --git a/projects/IoTCenter/iotcenter.db b/projects/IoTCenter/iotcenter.db index bf88f9f34df2e8fdb5564633a50138777155c8f2..406d81fd06cd82bbbd5d63752b26dd4afbd2b6a3 100644 GIT binary patch delta 157 zcmZozAk_dwEsQNpEzB(}Ev#GEG#uF2POvX!H{Y)3z$VAc#csw>#-Pl0f}vpgdUrN+ z77IN?!|AWw*$lTIa%K}}WHL9Le%pmDpT$7W%woEqD_bt3;r2bQY}||-47qwdp$xL3 z(>J-ZnJ}B{nS!-VKkmk6!DMDOoe?O?UZM|FqAWh$7G$)co`K=?a-i^bX%Dui?f@%5 BDUbjF delta 135 zcmZozAk_dwEsQNpEzB(}Ev#GEG#uF2SF@Y39pA3!z$V8$*+8Id`yppGaYiOHlj*lz z*z#En^^6Ut`?<2^GFoikI{^Q-B&h%Z diff --git a/projects/IoTClient/Assets/StreamingAssets/wwwroot/js/node.js b/projects/IoTClient/Assets/StreamingAssets/wwwroot/js/node.js index 79ff393f..84ac241d 100644 --- a/projects/IoTClient/Assets/StreamingAssets/wwwroot/js/node.js +++ b/projects/IoTClient/Assets/StreamingAssets/wwwroot/js/node.js @@ -84,6 +84,59 @@ function UpdateCamera() { } } } +function UpdateChart(deviceNumber) { + $('canvas.chart').each(function () { + var canvas = this; + var number = canvas.id; + if (!deviceNumber || deviceNumber === number) { + var device = vm.GetDevice(number); + var time = time || '30d'; + var url = iotCenter + '/App/GetChartData?time=' + time + '+&number=' + number; + axios.post(url, { crossDomain: true }) + .then(function (response) { + var data = response.data; + UpdateChartInternal(canvas, device.DisplayName,data); + }) + .catch(function (error) { + console.log(error); + toastr.error(error) + }); + } + }); +} +function UpdateChartInternal(canvas, title, data) { + var chart; + Chart.helpers.each(Chart.instances, function (instance) { + if (instance.chart.canvas.id === canvas.id) { + chart = instance; + } + }) + if (chart) { + chart.data = data; + chart.update(); + } + else { + var ctx = canvas.getContext('2d'); + var options = { + responsive: true, + legend: { + position: 'bottom', + }, + title: { + display: false, + text: title + }, + animation: { + duration: 0 + } + }; + var chart = new Chart(ctx, { + type: 'line', + data: data, + options: options + }); + } +} function playHls(videoElement) { var number = $('#camera').val(); var device = Enumerable.from(vm.model.Devices).where(function (o) { return o.Number === number; }).firstOrDefault(); @@ -332,6 +385,7 @@ function init() { connect(); this.$nextTick(function () { UpdateCamera(); + UpdateChart(); }); }, methods: { @@ -345,6 +399,17 @@ function init() { .where(function (o) { return o.Name == name; }) .toArray(); }, + GetDevice(nameOrNumber) { + return Enumerable.from(this.model.Devices) + .where(function (o) { return o.Name === nameOrNumber || o.Number === nameOrNumber; }) + .firstOrDefault(); + }, + GetChartDevices() { + return Enumerable.from(this.model.Devices) + .where(function (o) { return o.Name === '温湿度传感器' || o.Name === '粉尘检测器' || o.Name === '光强检测器' || o.Name === '智能插座'; }) + .orderBy('o=>o.ProductId') + .toArray(); + }, GetDataValue(number, name) { var device = Enumerable.from(this.model.Devices) .where(function (o) { return o.Number === number; }) diff --git a/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr.meta b/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr.meta new file mode 100644 index 00000000..f6361fbb --- /dev/null +++ b/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ce079c7515beaae4fb0ed787ff8f8ed5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.css.meta b/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.css.meta new file mode 100644 index 00000000..84e21628 --- /dev/null +++ b/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.css.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4cefe37c7668e1e4d9b28297ce380c40 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.js.meta b/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.js.meta new file mode 100644 index 00000000..a3eb21d3 --- /dev/null +++ b/projects/IoTClient/Assets/StreamingAssets/wwwroot/lib/toastr/toastr.min.js.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9c84f344a9bebe543a3721e46d8093d9 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/projects/IoTNode/iotnode.db b/projects/IoTNode/iotnode.db index 108f32c40a1e64b93d661414ca32b533183d0837..395f24e66da4f15ee0d4327df5abd38408c9033a 100644 GIT binary patch delta 259 zcmZozAkzRuEsQNpEzB(}Ev#GE)Z#djlMT#MObimY>&3BkigB8x85p=EmLwWYpQyyF zvi(U3n`Hx|;dZ&NY$|NL#-;`qNvQ^jPz8V3RJIrYX4}mo%(j8OpIw2?k@XsDCd&tw zdKPZx&4L22nYh^H8OlQ$*fuZ}PM^unZoy)qXJ|P6Dm%O3_QlNXQjARIhST@6uotix z=$TneH)dte1KPESm7SN7lOeZJnkSS&R&@GYc6JkHb3IeAzUk}P*e#jN%%;C$V>jV1 yX_Nt~Q5KhE6qe-R2%WCKn@vR^BsI4nwJ5Qqv?$fcP|v_{dLqz(?Q9(Ezt{od<3{}e delta 229 zcmZozAkzRuEsQNpEzB(}Ev#GE)Z#cyjSVa;O$?H@>&3Bkig6m78yL7GmLwWYpQyyF zvi(U3n`Hx|#df)`Y$|NLNoEEXW@!dVPz8V3RJIrYX4}mo%s!P}o^3swBkMKROqLHU z^(@@Xn*{}un71!xW|v}QGBcUJpM|}E#Zb@KaJn%odmhllMXc<+jMEj^*)5q2jHgGl zvzrJo6gSH7gfb|LN-_#da&UxB*Wb;i!XJ{FTaa3mSW;S)YGkBmJpB?-DNA0XG|zN1 YW_Ak}OFbj=>F?Rt4Y#v#u>WEQ07U0LIRF3v