using Application.Domain.Entities; using Application.Models; using Infrastructure.Data; using Infrastructure.Extensions; using Infrastructure.Web.SignalR; using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using System.Web; namespace Application.Services { public class NodeService : INodeService, IDisposable { private IServiceProvider applicationServices; private readonly IConfiguration _cfg; private CancellationTokenSource _tokenSource; public HubConnection Connection; private string _notifyHost; public NodeService(IServiceProvider applicationServices, IConfiguration configuration) { this.applicationServices = applicationServices; this._cfg = configuration; this._tokenSource = new CancellationTokenSource(); } public bool IsCallback { get; set; } public bool EditNode(EditNodeModel model) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == model.Id); entity.Name = model.Name; if (repo.SaveChanges() > 0) { this.SendToPage(scope, $"page", "UpdateNode", entity.ToJson()); this.UpdateServer(nameof(INodeService.EditNode), model); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool DeleteNode(Guid id) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == id); repo.Delete(entity); if (repo.SaveChanges() > 0) { this.SendToPage(scope, $"page", "DeleteNode", entity.ToJson()); this.UpdateServer(nameof(INodeService.DeleteNode), id); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool EditDevice(EditDeviceModel model) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == model.Id); entity.DisplayName = model.DisplayName; if (repo.SaveChanges() > 0) { var device = repo.ReadOnlyTable() .Include(o => o.Node) .Include(o => o.Data) .Include(o => o.Apis).ThenInclude(o => o.Parameters) .FirstOrDefault(o => o.Id == entity.Id); this.SendToPage(scope, $"node-{device.Node.Number}", "UpdateDevice", device.ToJson()); this.UpdateServer(nameof(INodeService.EditDevice), model); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool DeleteDevice(Guid id) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == id); if (entity != null) { var nodeRepo = scope.ServiceProvider.GetService>(); var number = nodeRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == entity.NodeId).Number; repo.Delete(entity); if (repo.SaveChanges() > 0) { this.SendToPage(scope, $"node-{number}", "DeleteDevice", entity.Number); this.UpdateServer(nameof(INodeService.DeleteDevice), id); } } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool CreateSence(EditSenceModel model) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = new Sence().From(model); repo.Add(entity); if (repo.SaveChanges() > 0) { var nodeRepo = scope.ServiceProvider.GetService>(); var number = nodeRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == entity.NodeId).Number; this.SendToPage(scope, $"page", "UpdateSence", entity.ToJson()); this.UpdateServer(nameof(INodeService.CreateSence), model); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool EditSence(EditSenceModel model) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == model.Id); entity.From(model); if (repo.SaveChanges() > 0) { var nodeRepo = scope.ServiceProvider.GetService>(); var number = nodeRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == entity.NodeId).Number; this.SendToPage(scope, $"page", "UpdateSence", entity.ToJson()); this.UpdateServer(nameof(INodeService.EditSence), model); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool DeleteSence(Guid id) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == id); if (entity != null) { var nodeRepo = scope.ServiceProvider.GetService>(); var number = nodeRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == entity.NodeId).Number; repo.Delete(entity); if (repo.SaveChanges() > 0) { this.SendToPage(scope, $"page", "DeleteSence", id.ToString()); this.UpdateServer(nameof(INodeService.DeleteSence), id); } } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool CreateCommand(EditCommandModel model) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = new Command().From(model); entity.QueryString = model.GetQueryString(); repo.Add(entity); if (repo.SaveChanges() > 0) { this.UpdateServer(nameof(INodeService.CreateCommand), model); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool EditCommand(EditCommandModel model) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == model.Id); entity.From(model); entity.QueryString = model.GetQueryString(); if (repo.SaveChanges() > 0) { this.UpdateServer(nameof(INodeService.EditCommand), model); } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public bool DeleteCommand(Guid id) { try { using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var entity = repo.Table().FirstOrDefault(o => o.Id == id); if (entity != null) { repo.Delete(entity); if (repo.SaveChanges() > 0) { this.UpdateServer(nameof(INodeService.DeleteCommand), id); } } return true; } } catch (Exception ex) { ex.PrintStack(); return false; } } public ApiResponse Exec(Guid id, string cmd, string query) { try { using (var scope = applicationServices.CreateScope()) { var deviceRepo = scope.ServiceProvider.GetService>(); var device = deviceRepo.ReadOnlyTable().Include(o => o.Apis).Include(o => o.Data).FirstOrDefault(o => o.Id == id); if (device.Name == "主机" && cmd == "21boot") { this.BootStrapPCByMacAddress(device.Number); return ApiResponse.AsyncSuccess(); } var url = $"{device.BaseUrl}/{cmd}{query}"; url = url.SetParam("id", device.Number); if (device.Name == "红外转发器") { var api = device.Apis.FirstOrDefault(o => o.Command == cmd); var keySets = device.Data.FirstOrDefault(o => o.Key == "hidden")?.Value ?? ""; var array = JsonConvert.DeserializeObject>(keySets) ?? new List(); if (cmd == "26add" || cmd == "25update" || cmd == "24delete") { var values = HttpUtility.ParseQueryString(query); if (cmd == "26add") { var code = values.Get("newcode"); var name = values.Get("name"); if (!array.Any(o => o.code == code)) { array.Add(new IrDataModel { code = code, name = name, irType = "00", status = "0", keyset = code }); } } else if (cmd == "25update") { var code = values.Get("code"); var name = values.Get("name"); var item = array.FirstOrDefault(o => o.code == code); if (item != null) { item.name = name; } } else if (cmd == "24delete") { } url = url.SetParam("hidden", array.ToJson()); } } return ExecUrl(scope, url); } } catch (Exception ex) { ex.PrintStack(); return ApiResponse.Error(ex); } } public ApiResponse Sence(Guid id) { try { using (var scope = applicationServices.CreateScope()) { var httpClientFactory = scope.ServiceProvider.GetService(); var senceRepo = scope.ServiceProvider.GetService>(); var sence = senceRepo.ReadOnlyTable() .Include(o => o.Commands) .ThenInclude(o => o.Api) .ThenInclude(o => o.Device) .FirstOrDefault(o => o.Id == id); foreach (var item in sence.Commands) { var device = item.Api.Device; var url = $"{device.BaseUrl}/{item.Api.Command}?id={device.Number}"; if (!string.IsNullOrEmpty(item.QueryString)) { url += $"&{item.QueryString}"; } Console.WriteLine($"start:{url}"); var task = httpClientFactory.CreateClient().GetAsync(url); task.Wait(); using (var response = task.Result) { using (var content = response.Content) { var result = content.ReadAsStringAsync().Result; Console.WriteLine($"end:{url}:{result}"); } } } } return ApiResponse.AsyncSuccess(); } catch (Exception ex) { ex.PrintStack(); return ApiResponse.Error(ex); } } public ApiResponse ExecAll(List id, string cmd, string query) { try { using (var scope = applicationServices.CreateScope()) { foreach (var deviceId in id) { this.Exec(deviceId, cmd, query); } } return ApiResponse.AsyncSuccess(); } catch (Exception ex) { ex.PrintStack(); return ApiResponse.Error(ex); } } private ApiResponse ExecUrl(IServiceScope scope, string url) { Console.WriteLine($"start:{url}"); var httpClientFactory = scope.ServiceProvider.GetService(); var task = httpClientFactory.CreateClient().GetAsync(url); task.Wait(); using (var response = task.Result) { using (var content = response.Content) { var value = content.ReadAsStringAsync().Result; Console.WriteLine($"end:{url}:{value}"); var result = value.FromJson(); return result; } } } private void BootStrapPCByMacAddress(string mac) { var message = ""; for (int i = 0; i < 12; i++) { message += "f"; } for (int i = 0; i < 16; i++) { message += mac; } var data = message.HexToBytes(); using (var client = new UdpClient(new IPEndPoint(Helper.Instance.GetLocalIP(), 0))) { client.Send(data, data.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 9)); } Console.WriteLine("boot"); } private void SendToPage(IServiceScope scope, string group, string method, string data) { try { var hc = scope.ServiceProvider.GetService>(); hc.Clients.Group(group).SendAsync(method, data); } catch (Exception ex) { ex.PrintStack(); } } private void UpdateServer(string method, object model) { if (!this.IsCallback) { try { using (var scope = applicationServices.CreateScope()) { var configuration = scope.ServiceProvider.GetService(); if (configuration.GetValue("notify:enabled")) { this.Connection.SendAsync("UpdateCallBack", method, model.ToJson()); } } } catch (Exception ex) { ex.PrintStack(); } } } public void Notify() { try { using (var scope = applicationServices.CreateScope()) { var configuration = scope.ServiceProvider.GetService(); if (configuration.GetValue("notify:enabled")) { Console.WriteLine("PushSenceAndDevice"); var schoolUrl = $"http://{configuration["notify:host"]}/Notify/Update"; var nodeRepo = scope.ServiceProvider.GetService>(); var node = nodeRepo.ReadOnlyTable().Include(o => o.Sences).Include(o => o.Devices).FirstOrDefault(); var data = node.ToJson(); var httpClientFactory = scope.ServiceProvider.GetService(); var task = httpClientFactory.CreateClient().PostAsync(schoolUrl, new StringContent(data)); task.Wait(); using (var response = task.Result) { using (var content = response.Content) { var result = content.ReadAsStringAsync().Result; Console.WriteLine($"server response:{schoolUrl}:{result}"); } } } } } catch (Exception ex) { ex.PrintStack(); } } public void Start() { Task.Run(async () => { while (!_tokenSource.IsCancellationRequested) { this.Connect(); await Task.Delay(this._cfg.GetValue("timer.seconds", 60) * 1000); } }); } public void Connect() { if (this._cfg.GetValue("notify:enabled")) { try { if (Connection == null) { Console.WriteLine("connection is null"); InitConnection(); } if (Connection.State == HubConnectionState.Disconnected) { Console.WriteLine("start connect"); Connection.StartAsync().Wait(); this.Notify(); } else { if (this._notifyHost != this._cfg["notify:host"]) { this.ReConnect(null); } else { Console.WriteLine($"connection is connected"); } } } catch (Exception ex) { ex.PrintStack(); } } } public void Close() { if (this.Connection != null) { if (this.Connection.State == HubConnectionState.Connected) { this.Connection.StopAsync(); } this.Connection.DisposeAsync(); this.Connection = null; } } private Task ReConnect(Exception arg) { this.Close(); this.Connect(); return Task.CompletedTask; } private void InitConnection() { this._notifyHost = this._cfg["notify:host"]; var url = ""; using (var scope = applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var nodeNumber = repo.ReadOnlyTable().FirstOrDefault().Number; url = $"http://{this._notifyHost}/hub?group={nodeNumber}"; } this.Connection = new HubConnectionBuilder().WithUrl(url).Build(); this.Connection.Closed += ReConnect; this.Connection.On(nameof(Exec), (string connectionId, Guid id, string cmd, string query) => { var response = this.Exec(id, cmd, query); this.Connection.SendAsync("Send", connectionId, "ApiResult", response.ToJson()); }); this.Connection.On(nameof(ExecAll), (string connectionId, List id, string cmd, string query) => { var response = this.ExecAll(id, cmd, query); this.Connection.SendAsync("Send", connectionId, "ApiResult", response.ToJson()); }); this.Connection.On(nameof(Sence), (string connectionId, Guid id) => { var response = this.Sence(id); this.Connection.SendAsync("Send", connectionId, "ApiResult", response.ToJson()); }); //server call node update this.Connection.On(nameof(INodeService.EditNode), (EditNodeModel model) => { this.EditNode(model); }); this.Connection.On(nameof(INodeService.DeleteNode), (Guid model) => { this.DeleteNode(model); }); this.Connection.On(nameof(INodeService.CreateSence), (EditSenceModel model) => { this.CreateSence(model); }); this.Connection.On(nameof(INodeService.EditSence), (EditSenceModel model) => { this.EditSence(model); }); this.Connection.On(nameof(INodeService.DeleteSence), (Guid model) => { this.DeleteSence(model); }); this.Connection.On(nameof(INodeService.EditDevice), (EditDeviceModel model) => { this.EditDevice(model); }); this.Connection.On(nameof(INodeService.DeleteDevice), (Guid model) => { this.DeleteDevice(model); }); this.Connection.On(nameof(INodeService.CreateCommand), (EditCommandModel model) => { this.CreateCommand(model); }); this.Connection.On(nameof(INodeService.EditCommand), (EditCommandModel model) => { this.EditCommand(model); }); this.Connection.On(nameof(INodeService.DeleteCommand), (Guid model) => { this.DeleteCommand(model); }); } public void Dispose() { this._tokenSource.Cancel(); this.Close(); } } }