using Application.Domain.Entities; using Application.Models; using Infrastructure.Data; using Infrastructure.Extensions; using Infrastructure.Jwt; using Infrastructure.Web; using IoTCenter.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using Vibrant.InfluxDB.Client; using Vibrant.InfluxDB.Client.Rows; namespace IoTCenter.Controllers { [Device] public class AppController : Controller { private readonly IHostEnvironment _env; private readonly IConfiguration _configuration; private readonly IJwtHelper _jwtHelper; private readonly IRepository _categoryRepo; private readonly IRepository _productRepo; private readonly IRepository _nodeRepo; private readonly IRepository _sceneRepo; private readonly IRepository _sceneCommandRepo; private readonly IRepository _commandRepo; private readonly IRepository _deviceRepo; private readonly IRepository _liveRecordRepo; private readonly IHubContext _hub; public AppController( IHostEnvironment env, IConfiguration configuration, IJwtHelper jwtHelper, IRepository categoryRepo, IRepository productRepo, IRepository nodeRepo, IRepository sceneRepo, IRepository sceneCommandRepo, IRepository commandRepo, IRepository deviceRepo, IRepository liveRecordRepo, IHubContext hub) { this._env = env; this._configuration = configuration; this._jwtHelper = jwtHelper; this._categoryRepo = categoryRepo; this._productRepo = productRepo; this._nodeRepo = nodeRepo; this._sceneRepo = sceneRepo; this._sceneCommandRepo = sceneCommandRepo; this._commandRepo = commandRepo; this._deviceRepo = deviceRepo; this._liveRecordRepo = liveRecordRepo; this._hub = hub; } public IActionResult GetProducts(string token) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var model = new { Products = this._productRepo.ReadOnlyTable().Select(o => new { o.Id, o.Number, o.Name, o.Image, DeviceCount = o.Devices.Count() }).ToList(), Scenes = this._sceneRepo.ReadOnlyTable().Where(o => o.NodeId == null).ToList() }; return Json(model); } public IActionResult GetNodes(string token) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var model = this._nodeRepo.ReadOnlyTable() .Include(o => o.Scenes) .OrderBy(o => o.DisplayOrder) .ThenBy(o => o.Name) .Select(o => new { o.Id, o.Number, o.Name, o.Image, o.DisplayOrder, o.Scenes, DeviceCount = o.Devices.Count }) .ToList(); return Json(model); } public IActionResult GetNode(string token, string number) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var model = this._nodeRepo.ReadOnlyTable() .Include(o => o.Scenes) .Include(o => o.Devices) .ThenInclude(o => o.Data) .Include(o => o.Devices) .ThenInclude(o => o.Commands) .FirstOrDefault(o => o.Number == number); return Json(model); } public IActionResult GetProduct(string token, string number) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var model = this._productRepo.ReadOnlyTable() .Include(o => o.Devices) .ThenInclude(o => o.Data) .Include(o => o.Devices) .ThenInclude(o => o.Node) .FirstOrDefault(o => o.Number == number); return Json(model); } public IActionResult GetDevice(string token, string number) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } 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); } public IActionResult GetTemplate(string token, string number) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var node = _nodeRepo.ReadOnlyTable().FirstOrDefault(o => o.Number == number); var template = string.IsNullOrEmpty(token) ? node.Template : node.MobileTemplate; if (!string.IsNullOrEmpty(template)) { return Content(System.IO.File.ReadAllText(Path.Combine(this._env.ContentRootPath, "wwwroot", template))); } else { return Content(""); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] public IActionResult ExecApi(string token, string connectionId, string number, string method) { try { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } CallApi(connectionId, number, method); return Json(ApiResponse.AsyncSuccess()); } catch (Exception ex) { ex.PrintStack(); return Json(ApiResponse.Error(ex)); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] public IActionResult ExecApiAll(string token, string connectionId, string method, string query, List numbers) { try { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } foreach (var number in numbers) { this.CallApi(connectionId, number, method); } return Json(ApiResponse.AsyncSuccess()); } catch (Exception ex) { ex.PrintStack(); return Json(ApiResponse.Error(ex)); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] public IActionResult ExecCommand(string token, string connectionId, Guid id) { try { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } 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) { try { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var scene = this._sceneRepo.ReadOnlyTable().Include(o => o.Node).FirstOrDefault(o => o.Id == id); if (scene.NodeId != null) { this._hub.ServerToClient(Methods.ExecSceneRequest, id, scene.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 ExecGlobalScene(string token, string connectionId, Guid id) { try { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } var commands = this._sceneCommandRepo.ReadOnlyTable() .Include(o => o.Command).ThenInclude(o => o.Device).ThenInclude(o => o.Node) .Where(o => o.SceneId == id) .Select(o => o.Command) .ToList(); foreach (var command in commands) { try { this._hub.ServerToClient(Methods.ExecCommand, command.Id, command.Device.Node.Number, connectionId); } catch (Exception ex) { ex.PrintStack(); } } return Json(ApiResponse.AsyncSuccess()); } catch (Exception ex) { ex.PrintStack(); return Json(ApiResponse.Error(ex)); } } public IActionResult GetChartData(string token, string number, string time) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } 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(); var labels = new List(); var hours = Convert.ToInt32(DateTime.Now.ToString("%z")); using (var client = new InfluxClient(new Uri(url), usr, pwd)) { var keys = device.Data.Where(o => o.Type == DeviceDataType.Int || o.Type == DeviceDataType.Float).Select(o => o.Key); var fileds = String.Join(',', keys); if (!string.IsNullOrEmpty(fileds)) { 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.Select(o => o.Fields.ContainsKey(data.Key) ? o.GetField(data.Key) : null).ToList() : new List(), backgroundColor = this.GetColor(data.Key), fill = false }); } if (rows != null) { var format = time.StartsWith("1") ? "H:mm" : (time.StartsWith("7") ? "ddd" : "MMM-d"); labels = rows.Select(o => o.Timestamp.Value).Select(o => o.AddHours(hours).ToString(format, new CultureInfo("zh-CN"))).ToList(); } } var model = new { datasets = list, labels = labels }; return Json(model); } } /************************************************************/ private void CallApi(string connectionId, string number, string method) { var device = this._deviceRepo.ReadOnlyTable().Include(o => o.Node).FirstOrDefault(o => o.Number == number); if (device != null) { var query2 = string.Empty; var parameters = Request.Method.ToUpper() == "GET" ? Request.Query.ToArray() : Request.Form.ToArray(); foreach (var item in parameters.Where(o => o.Key.ToLower() != "connectionid" && o.Key.ToLower() != "method")) { query2 = query2.SetParam(item.Key, item.Value); } foreach (var item in new string[] { "token", "connectionid", "method", "numbers[]" }) { query2 = query2.RemoveParam(item); } query2 = query2.SetParam("number", number); var message = $"{method}{query2}"; var group = device.Node.Number; this._hub.ServerToClient(Methods.ExecApiRequest, message, group, connectionId); } } private string GetColor(string 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; if (key == "Humidity") { return Color.FromKnownColor(KnownColor.Green).Name; } else if (key == "Electricity") { return Color.FromKnownColor(KnownColor.Red).Name; } else if (key == "Light") { return Color.FromKnownColor(KnownColor.Orange).Name; } return Color.FromKnownColor(KnownColor.DarkBlue).Name; } private string GetUserName(string token) { try { return User.Identity.IsAuthenticated ? User.Identity.Name : this._jwtHelper.GetPayload(token)["UserName"].ToString(); } catch (Exception ex) { ex.PrintStack(); return null; } } /************************************************************/ public IActionResult Power(string token, string connectionId, string method, string[] numbers) { var userName = this.GetUserName(token); if (string.IsNullOrEmpty(userName)) { return Forbid(); } this.Power(connectionId, numbers, method, o => o.Name.Contains("开关") || o.Name.Contains("插座") || o.Name.Contains("灯")); return Json(ApiResponse.AsyncSuccess()); } public IActionResult AllSwitchOn(string connectionId, string[] nodes) { this.Power(connectionId, nodes, "On", o => o.Name.Contains("开关")); return Json(ApiResponse.AsyncSuccess()); } public IActionResult AllSocketOn(string connectionId, string[] nodes) { this.Power(connectionId, nodes, "On", o => o.Name.Contains("插座")); return Json(ApiResponse.AsyncSuccess()); } public IActionResult AllSocketOff(string connectionId, string[] nodes) { this.Power(connectionId, nodes, "Off", o => o.Name.Contains("插座")); return Json(ApiResponse.AsyncSuccess()); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] private void Power(string connectionId, string[] nodes, string command, Func func) { var devices = this._deviceRepo.ReadOnlyTable() .Include(o => o.Node) .Include(o => o.Product.Apis) .Where(o => nodes.Contains(o.Node.Number)) .Where(func) .ToList(); foreach (var device in devices) { try { var api = device.Product.Apis.FirstOrDefault(o => o.Command == command); var message = $"{api.Path}{api.Command}?number={device.Number}"; this._hub.ServerToClient(Methods.ExecApiRequest, message, device.Node.Number, connectionId); } catch (Exception ex) { ex.PrintStack(); } } } #region srs callback public IActionResult OnPublish() { try { using (var reader = new StreamReader(Request.Body)) { var json = reader.ReadToEndAsync().Result; var result = JsonConvert.DeserializeObject>(json); if (result["action"] == "on_publish") { var streamId = result["stream"]; var deviceNumber = streamId.StartsWith("main") ? streamId.Substring(4) : streamId.Substring(3); var device = this._deviceRepo.ReadOnlyTable().Include(o => o.Node).Include(o => o.Data).FirstOrDefault(o => o.Number == deviceNumber); if (device != null) { var data = device.Data.FirstOrDefault(o => o.Name == "录制"); if (data != null) { this.CallApi(null, device.Node.Number, $"/Onvif/{(data.Value == "是" ? "Start" : "Stop")}Record"); } } } } } catch (Exception ex) { ex.PrintStack(); } return Content(string.Empty); } /// /// 录制完毕事件响应 ///{"action":"on_dvr","client_id":107,"ip":"192.168.3.124","vhost":"__defaultVhost__","app":"live","stream":"main0a5d5deee43011b483884cbd8fd2b3dd","param":"","cwd":"/root/publish/apps/srs","file":"./objs/nginx/html/live.main0a5d5deee43011b483884cbd8fd2b3dd.1574832559180.mp4"} /// /// public IActionResult OnDvr() { using (var reader = new StreamReader(Request.Body)) { var json = reader.ReadToEndAsync().Result; var result = JsonConvert.DeserializeObject>(json); if (result["action"] == "on_dvr") { var streamId = result["stream"]; var deviceNumber = streamId.StartsWith("main") ? streamId.Substring(4) : streamId.Substring(3); var device = this._deviceRepo.ReadOnlyTable().FirstOrDefault(o => o.Number == deviceNumber); if (device != null) { this._liveRecordRepo.Add(new LiveRecord { DeviceNumber = deviceNumber, DeviceName = device?.DisplayName, Name = $"回放{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}", Value = $"/video{result["file"].Substring(result["file"].LastIndexOf('/'))}" }); this._liveRecordRepo.SaveChanges(); } } } return Content(string.Empty); } #endregion srs callback } }