Former-commit-id: c7300f0d32800220e9e6ec3685334bc419ba3d5c
TangShanKaiPing
wanggang 6 years ago
parent 19daada856
commit 266a65385b

@ -1,11 +1,11 @@
<div class="panel panel-default">
<div class="card">
<div class="form-horizontal">
<div class="box-body">
<div class="card-body">
@foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !pm.HideSurroundingHtml))
{
var uihit = prop.DataTypeName ?? prop.TemplateHint;
<div class="form-group">
@Html.Label(prop.PropertyName, prop.GetDisplayName() + "", new { @class = "col-sm-2 control-label" })
<div class="form-group row">
@Html.Label(prop.PropertyName, prop.GetDisplayName() + "", new { @class = "col-sm-2 col-form-label" })
<div class="col-sm-8">
<div class="form-control form-control-display">
@Html.Display(prop.PropertyName, prop.DataTypeName ?? prop.TemplateHint)
@ -14,22 +14,20 @@
</div>
}
</div>
<div class="box-footer">
<div class="form-group">
<div class="card-footer">
<div class="form-group row">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-6">
@if (!DisableBackUrl)
{
var BackUrl = Url.Action("Index", null);
<a class="btn btn-default" href="@BackUrl">
<i class="fa fa-reply"></i>
返回
</a>
}
@if (ViewData["HasEditPermission"] != null)
{
<a href="@Url.Action("Edit",values:new { id=Model.Id})" class="btn btn-primary">
<i class="fa fa-floppy-o"></i>
编辑
</a>
}

@ -0,0 +1,27 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Application.Models
{
[Display(Name = "历史数据")]
public class DisplayDataHistoryModel
{
[Display(Name = "设备编号")]
public string DeviceNumber { get; set; }
[Display(Name = "设备名称")]
public string DeviceName { get; set; }
[Display(Name = "数据名称")]
public string Name { get; set; }
[Display(Name = "数据值")]
public string Value { get; set; }
[Display(Name = "单位")]
public string Unit { get; set; }
[Display(Name = "日期")]
public DateTime? Date { get; set; }
}
}

@ -0,0 +1,18 @@
using Application.Domain.Entities;
using Infrastructure.Application;
using System;
using System.ComponentModel.DataAnnotations;
namespace Application.Models
{
public class SearchDataHistoryModel : PagedListModel<DisplayDataHistoryModel>
{
[Display(Name = "开始")]
[DataType(DataType.Date)]
public DateTime Start { get; set; } = DateTime.Now.Date.AddMonths(-1);
[Display(Name = "结束")]
[DataType(DataType.Date)]
public DateTime End { get; set; } = DateTime.Now.Date;
}
}

@ -5,8 +5,11 @@ using Infrastructure.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Linq;
using Vibrant.InfluxDB.Client;
using Vibrant.InfluxDB.Client.Rows;
namespace IoT.Shared.Areas.Admin.Controlls
{
@ -14,11 +17,13 @@ namespace IoT.Shared.Areas.Admin.Controlls
[Area(nameof(Admin))]
public class DataController : SharedController<Data, SearchDataModel, EditDataModel, EditDataModel>
{
private readonly IConfiguration _configuration;
private readonly IRepository<Node> _nodeRepo;
private readonly AjaxController _ajax;
public DataController(IRepository<Node> nodeRepo, IRepository<Data> repo, AjaxController ajax, IServiceProvider sp) : base(repo, sp)
public DataController(IConfiguration configuration, IRepository<Node> nodeRepo, IRepository<Data> repo, AjaxController ajax, IServiceProvider sp) : base(repo, sp)
{
this._configuration = configuration;
this._nodeRepo = nodeRepo;
this._ajax = ajax;
}
@ -58,5 +63,49 @@ namespace IoT.Shared.Areas.Admin.Controlls
{
return this._nodeRepo.ReadOnlyTable().Where(o => o.Id == model.NodeId).Select(o => o.Number).FirstOrDefault();
}
public IActionResult DataHistory(Guid id, SearchDataHistoryModel model)
{
var data = this._repo.ReadOnlyTable()
.Include(o => o.Device).ThenInclude(o => o.Node)
.Include(o => o.Device).ThenInclude(o => o.Product)
.FirstOrDefault(o => o.Id == id);
var url = this._configuration["influxdb:url"];
var usr = this._configuration["influxdb:usr"];
var pwd = this._configuration["influxdb:pwd"];
var dbName = "iot";
var measurementName = "data";
using (var client = new InfluxClient(new Uri(url), usr, pwd))
{
var parameters = new
{
start = model.Start,
end = model.End.AddDays(1)
};
var query = $"from {measurementName} where time>=$start and time<$end and DeviceNumber = '{data.Device.Number}'";
var dataQuery = $"select {data.Key} {query} limit {model.PageSize} offset {(model.PageIndex - 1) * model.PageSize}";
var result = client.ReadAsync<DynamicInfluxRow>(dbName, dataQuery, parameters: parameters).Result;
var rows = result.Results.FirstOrDefault()?
.Series.FirstOrDefault()?
.Rows;
model.List.AddRange(rows.Select(o => new DisplayDataHistoryModel
{
DeviceName = data.Device.DisplayName,
DeviceNumber = data.Device.Number,
Name = data.Name,
Unit = data.Unit,
Value = o.GetField(data.Key).ToString(),
Date = o.Timestamp
}));
var countQuery = $"select count({data.Key}) {query}";
var countResult = client.ReadAsync<DynamicInfluxRow>(dbName, countQuery, parameters: parameters).Result;
model.TotalCount = Convert.ToInt32(countResult.Results.FirstOrDefault().Series.FirstOrDefault().Rows.FirstOrDefault().GetField("count"));
ViewBag.HtmlTitle = data.Name;
return View(model);
}
}
}
}

@ -0,0 +1,40 @@
@model SearchDataHistoryModel
@{
var start = (Model.PageIndex - 1) * Model.PageSize;
}
@Html.EditorForModel("Search")
<div class="card">
<div class="card-body">
<table class="table table-striped projects">
<tbody>
<tr>
<th>行号</th>
<th>设备编号</th>
<th>设备名称</th>
<th>数据名称</th>
<th>数据值</th>
<th>单位</th>
<th>时间</th>
</tr>
@foreach (var item in Model.List)
{
<tr>
<td>@(++start)</td>
<td>@item.DeviceNumber</td>
<td>@item.DeviceName</td>
<td>@item.Name</td>
<td>@item.Value</td>
<td>@item.Unit</td>
<td>@item.Date</td>
</tr>
}
</tbody>
</table>
</div>
@if (Model.PageCount() > 1)
{
<div class="card-footer clearfix">
@(await Html.PartialAsync("_Paged"))
</div>
}
</div>

@ -0,0 +1,12 @@
@model EditDataModel
@{
HtmlTitle = "查看" + ViewContext.ViewData.ModelMetadata.ModelType.GetDisplayName();
}
@if (Model.Type == DeviceDataType.Int || Model.Type == DeviceDataType.Float)
{
<div class="row col-md-12">
<a class="btn btn-info btn-sm" href="@Url.Action("DataHistory","Data",new { id=Model.Id})">查看历史数据</a>
</div>
}
<br />
@Html.DisplayForModel()

@ -133,6 +133,9 @@ namespace IoTCenter.Controllers
var userName = User.Identity.IsAuthenticated ? User.Identity.Name : this._jwtHelper.GetPayload(token)["UserName"].ToString();
var model = this._deviceRepo.ReadOnlyTable()
.Include(o => o.Data)
.Include(o => o.Product)
.ThenInclude(o => o.Apis)
.ThenInclude(o => o.Parameters)
.Include(o => o.Commands)
.FirstOrDefault(o => o.Number == number);
return Json(model);
@ -178,6 +181,23 @@ namespace IoTCenter.Controllers
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
public IActionResult ExecCommand(string token, string connectionId, Guid id)
{
try
{
var userName = User.Identity.IsAuthenticated ? User.Identity.Name : this._jwtHelper.GetPayload(token)["UserName"].ToString();
var command = this._commandRepo.ReadOnlyTable().Include(o => o.Device).ThenInclude(o => o.Node).FirstOrDefault(o => o.Id == id);
this._hub.ServerToClient(Methods.ExecCommand, command.Id, command.Device.Node.Number, connectionId);
return Json(ApiResponse.AsyncSuccess());
}
catch (Exception ex)
{
ex.PrintStack();
return Json(ApiResponse.Error(ex));
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
public IActionResult ExecScene(string token, string connectionId, Guid id)
{
@ -308,22 +328,6 @@ namespace IoTCenter.Controllers
/************************************************************/
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
public IActionResult ExecCommand(string connectionId, Guid id)
{
try
{
var command = this._commandRepo.ReadOnlyTable().Include(o => o.Device).ThenInclude(o => o.Node).FirstOrDefault(o => o.Id == id);
this._hub.ServerToClient(Methods.ExecCommand, command.Id, command.Device.Node.Number, connectionId);
return Json(ApiResponse.AsyncSuccess());
}
catch (Exception ex)
{
ex.PrintStack();
return Json(ApiResponse.Error(ex));
}
}
public IActionResult Data(Guid id, string time = "10m")
{
var device = this._deviceRepo.ReadOnlyTable().Include(o => o.Node).Include(o => o.Data).FirstOrDefault(o => o.Id == id);

@ -8,8 +8,6 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.1.4" />
<PackageReference Include="Vibrant.InfluxDB.Client" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />

@ -1,7 +1,6 @@
@{
HideBread = true;
HtmlTitle = "设备详情";
}
<br />
<div id="template">
<style>
h3 img {
@ -143,7 +142,7 @@
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title"><img src="/images/batch.png" />{{model.DisplayName}}</h3>
<h3 class="card-title"><img :src="iotCenter+model.Product.Image" />{{model.DisplayName}}</h3>
<div class="card-tools">命令</div>
</div>
<div class="card-body" v-if="model.Commands.length>0">
@ -157,10 +156,70 @@
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title"><img src="/images/batch.png" />属性</h3>
<h3 class="card-title">属性</h3>
</div>
<div class="card-body" v-if="model.Commands.length>0">
<button class="btn btn-success" v-for="command in GetCommands()" v-on:click="CallCommand(command.Id)">{{command.Name}}</button>
<div class="card-body" v-if="model.Data.length>0">
<div class="row" v-for="data in GetDatas()">
<div class="col-md-3">{{data.Name}}</div>
<div class="col-md-9">{{data.Value}}{{data.Unit}}{{data.Description}}</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-if="HasChart()">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">趋势</h3>
</div>
<div class="card-body">
<canvas class="chart" :id="model.Number"></canvas>
</div>
</div>
</div>
</div>
<div class="row" v-if="model.Product.Apis.length>0">
<!--接口-->
<div class="col-md-12">
<div class="card">
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">接口</h3>
</div>
<div class="card-body">
<template v-if="model.Name==='摄像头'">
</template>
<template v-else-if="model.Name==='调色灯'">
</template>
<template v-else-if="model.Name==='红外转发器'">
</template>
<template>
<div class="row">
<div class="col-md-12">
<button class="btn btn-success" v-for="api in Enumerable.from(model.Product.Apis).orderBy('o=>o.Name')" v-if="api.Parameters.length<=1">{{api.Name}}</button>
</div>
</div>
<form class="form-horizontal" v-for="api in Enumerable.from(model.Product.Apis).orderBy('o=>o.Name')" v-if="api.Parameters.length>1">
<input type="hidden" name="Number" :value="model.Number" />
<input type="hidden" name="Method" :value="api.Method" />
<div class="form-group" v-for="item in api.Parameters" v-if="item.Name!=='number'">
<label :for="item.Name">{{item.Description}}:</label>
<template v-if="item.Type==='string'">
<input type="text" class="form-control" :name="item.Name" :value="GetDataValueByKey(item.Name)" />
</template>
<template v-else>
<input type="text" class="form-control" :name="item.Name" :value="GetDataValueByKey(item.Name)" />
</template>
</div>
<div class="row">
<div class="col-sm-12">
<button class="btn btn-success">{{api.Name}}</button>
</div>
</div>
</form>
</template>
</div>
</div>
</div>

@ -24,9 +24,9 @@
<button class="btn btn-sm btn-info uncheck" v-on:click="SelectDevice($event)">反选</button>
</div>
<div class="card-tools">
<button v-on:click="CallApiAll('On')">开</button>
<button v-on:click="CallApiAll('Stop')" v-if="model.Name.indexOf('窗帘电机')>=0">停</button>
<button v-on:click="CallApiAll('Off')">关</button>
<button class="btn btn-success" v-on:click="CallApiAll('On')">开</button>
<button class="btn btn-success" v-on:click="CallApiAll('Stop')" v-if="model.Name.indexOf('窗帘电机')>=0">停</button>
<button class="btn btn-success" v-on:click="CallApiAll('Off')">关</button>
</div>
</div>
</div>

@ -88,8 +88,8 @@ function UpdateChart(deviceNumber) {
$('canvas.chart').each(function () {
var canvas = this;
var number = canvas.id;
if (!deviceNumber || deviceNumber === number) {
var device = vm.GetDevice(number);
if (vm.HasChart()) {
var device = vm.model;
var time = time || '30d';
var url = iotCenter + '/App/GetChartData?time=' + time + '+&number=' + number;
axios.post(url, { crossDomain: true })
@ -335,7 +335,7 @@ function onMessage(method, json,to, from) {
}
else {
if(method == 'DataEntityInserted' || method == 'DataEntityUpdated') {
var device = Enumerable.from(vm.model.Devices).firstOrDefault(function (o) { return o.Id === item.DeviceId; })
var device = vm.model.Id===item.DeviceId? vm.model:null;
if (device) {
updateItem(device.Data, item);
toastr.info(device.DisplayName + '更新');
@ -345,11 +345,11 @@ function onMessage(method, json,to, from) {
}
}
else if (method == 'CommandEntityInserted' || method == 'CommandEntityUpdated') {
updateItem(vm.model.Scenes, item);
updateItem(model.Commands, item);
toastr.info(item.Name + '更新');
}
else if (method == 'CommandEntityDeleted') {
deleteItem(vm.model.Scenes, item);
deleteItem(model.Commands, item);
toastr.info(item.Name+'删除');
}
}
@ -392,6 +392,13 @@ function init() {
});
},
methods: {
HasChart() {
var o = this.model;
return o.Name === '温湿度传感器' || o.Name === 'PM2.5感应器' || o.Name === '光强检测器' || o.Name === '智能插座';
},
GetDatas() {
return Enumerable.from(this.model.Data).orderBy('o=>o.Key').toArray();
},
HasDevices(name) {
return Enumerable
.from(this.model.Devices)
@ -408,22 +415,17 @@ function init() {
.firstOrDefault();
},
GetChartDevices() {
return Enumerable.from(this.model.Devices)
return Enumerable.from(this.model.Data)
.where(function (o) { return o.Name === '温湿度传感器' || o.Name === 'PM2.5感应器' || 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; })
GetDataValueByKey(key) {
var data = Enumerable.from(this.model.Data)
.where(function (o) { return o.Key.toLowerCase() === key; })
.firstOrDefault();
if (device != null) {
var data = Enumerable.from(device.Data)
.where(function (o) { return o.Name == name })
.firstOrDefault();
if (data != null) {
return data.Value;
}
if (data != null) {
return data.Value;
}
return null;
},
@ -455,6 +457,9 @@ function init() {
CallScene(id) {
ajax('/App/ExecScene', { token: token, connectionId: connectionId, id: id }, 'post');
},
CallCommand(id) {
ajax('/App/ExecCommand', { token: token, connectionId: connectionId, id: id }, 'post');
},
AjaxSubmit(event, deviceNumber, dataName) {
var device = Enumerable.from(vm.model.Devices)
.where(function (o) { return o.Number === deviceNumber; })

@ -1,14 +1,65 @@
using IoTNode.DeviceServices.FBee;
using Application.Models;
using Infrastructure.Extensions;
using IoTNode.DeviceServices.FBee;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System;
using System.ComponentModel.DataAnnotations;
namespace IoTNode.Controllers
{
[SwaggerTag("调色灯")]
public class ColorLightController : WarmLightController
public class ColorLightController : SwitchController
{
private readonly FBeeService _deviceService;
public ColorLightController(IServiceProvider applicationServices, FBeeService deviceService) : base(applicationServices, deviceService)
{
this._deviceService = deviceService;
}
[HttpGet, Route("/[controller]/[action]"), SwaggerOperation("")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
public ApiResponse SetBrightness([SwaggerParameter("设备编号")]string number, [SwaggerParameter("亮度")][Range(0, 255)]int brightness)
{
if (number is null)
{
throw new ArgumentNullException(nameof(number));
}
try
{
var values = number.Split('-');
this._deviceService.X83(values[0], values[1], (byte)brightness);
}
catch (Exception ex)
{
ex.PrintStack();
return ApiResponse.Error(ex.Message);
}
return ApiResponse.AsyncSuccess();
}
[HttpGet, Route("/[controller]/[action]"), SwaggerOperation("")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
public ApiResponse SetColor([SwaggerParameter("设备编号")]string number, [SwaggerParameter("色调")][Range(0, 255)]int hue, [SwaggerParameter("饱和度")][Range(0, 255)]int saturation)
{
if (number is null)
{
throw new ArgumentNullException(nameof(number));
}
try
{
var values = number.Split('-');
this._deviceService.X84(values[0], values[1], (byte)hue, (byte)saturation);
}
catch (Exception ex)
{
ex.PrintStack();
return ApiResponse.Error(ex.Message);
}
return ApiResponse.AsyncSuccess();
}
}
}
Loading…
Cancel
Save