using Application.Domain.Entities; using Application.Models; using Infrastructure.Data; using Infrastructure.Extensions; using Infrastructure.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FBeeService { public class DeviceService : IDisposable { private readonly IHostingEnvironment _env; private readonly IConfiguration _configuration; private readonly IHttpClientFactory _httpClientFactory; private readonly IServiceProvider _applicationServices; private CancellationTokenSource _tokenSource; private static readonly object lockObject = new Object(); private readonly TcpClient _tcpClient; private string _tcpIp; private int _tcpPort; private byte[] _address; private string _addressValue; private readonly Dictionary _types = new Dictionary(); private readonly ConcurrentDictionary Clients = new ConcurrentDictionary(); public DeviceService(IHostingEnvironment env, IServiceProvider applicationServices, IConfiguration configuration, IHttpClientFactory httpClientFactory) { this._env = env; this._applicationServices = applicationServices; this._configuration = configuration; this._httpClientFactory = httpClientFactory; this._tokenSource = new CancellationTokenSource(); this._tcpClient = new TcpClient(); this._tcpIp = this._configuration["tcp.ip"]; this._tcpPort = this._configuration.GetValue("tcp.port"); //this.Connect(); } public void Start() { Task.Run(async () => { while (!_tokenSource.IsCancellationRequested) { try { Console.WriteLine("timer ..."); Notify(); } catch (Exception ex) { ex.PrintStack(); } await Task.Delay(this._configuration.GetValue("timer.seconds") * 1000); } }); } public void Notify() { Console.WriteLine("notify start ..."); try { this.CheckConnection(); //this.SearchAddress(); } catch (Exception ex) { ex.PrintStack(); } Console.WriteLine("notify end ..."); } public void SmartPlugOn(string id) { this.DeviceOnOff(id, 1); } public void SmartPlugOff(string id) { this.DeviceOnOff(id, 0); } private void DeviceOnOff(string ieee, byte status) { using (var scope = _applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var device = repo.ReadOnlyTable().FirstOrDefault(o => o.Online != 0 && o.IEEE == ieee); if (device != null) { var sn = device.Sn; var address = device.Address; var list = new List() { 0x16, 0x00 }; list.AddRange(sn.HexToBytes().Reverse()); list.Add(0xfe); list.Add(0x82); list.Add(0x0d); list.Add(0x02); list.AddRange(address.HexToBytes()); list.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); list.Add(0x01); list.AddRange(new byte[] { 0x00, 0x00 }); list.Add(status); this.Clients[sn].Client.GetStream().Write(list.ToArray()); } } } public void Switch2AllOn(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x0f, 0x00, 0x00, 0x00, 0x03, 0x01, 0x07 }); this.SearchSwitchStatus(this.GetNumber(id), 0x02); } public void Switch2AllOff(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x0f, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x02); } public void Switch2L1On(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0xff, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x02); } public void Switch2L1Off(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0x00, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x02); } public void Switch2L2On(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x01, 0xff, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x02); } public void Switch2L2Off(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x01, 0x00, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x02); } public void SocketOn(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0xff, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x10); } public void SocketOff(string id) { this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0x00, 0x00 }); this.SearchSwitchStatus(this.GetNumber(id), 0x10); } private byte GetNumber(string id) { return Convert.ToByte(id.Split("-")[1]); } private void CheckConnection() { var ips = NetworkInterface.GetAllNetworkInterfaces() .Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback) .Select(nic => nic.GetIPProperties().UnicastAddresses) .SelectMany(o => o.ToList()) .Select(o => o.Address) .Where(o => o.AddressFamily == AddressFamily.InterNetwork) .ToList(); var list = new ConcurrentBag(); foreach (var ip in ips) { Console.WriteLine($"search:{ip}"); var tokenSource = new CancellationTokenSource(); using (var client = new UdpClient(new IPEndPoint(ip, 0))) { var message = Encoding.ASCII.GetBytes("GETIP\r\n"); client.Send(message, message.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 9090)); Task.Run(() => { try { var ep = new IPEndPoint(IPAddress.Any, 3702); while (!tokenSource.IsCancellationRequested) { if (client.Available > 0) { var buffer = client.Receive(ref ep); var result = Encoding.ASCII.GetString(buffer); Console.WriteLine(result); if (!list.Any(o => o == result)) { list.Add(result); } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } }, tokenSource.Token); Thread.Sleep(1000); tokenSource.Cancel(); } } using (var scope = _applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); foreach (var result in list) { var sn = Regex.Match(result, @"SN:([^\s]*)").Groups[1].Value; var ip = Regex.Match(result, @"IP:([^\s]*)").Groups[1].Value; var gateway = repo.Table().FirstOrDefault(o => o.Sn == sn); if (gateway == null) { gateway = new Gateway { Sn = sn, Ip = ip, Enable = true }; repo.Add(gateway); repo.SaveChanges(); } else { if (gateway.Ip != ip) { gateway.Ip = ip; repo.Add(gateway); } } } var gateways = repo.ReadOnlyTable().ToList(); foreach (var gateway in gateways) { if (gateway.Enable) { if (Clients.Any(o => o.Key == gateway.Sn)) { Clients.TryGetValue(gateway.Sn, out TcpClientWrapper client); if (client.Ip != gateway.Ip) { if (client.Client.Connected) { client.Client.Close(); } this.Connect(gateway.Sn, client); } else { if (!client.Client.Connected) { this.Connect(gateway.Sn, client); } } } else { var client = new TcpClientWrapper { Ip = gateway.Ip, Client = new TcpClient() }; Clients.TryAdd(gateway.Sn, client); this.Connect(gateway.Sn, client); } this.Refresh(gateway.Sn); } else { if (Clients.Any(o => o.Key == gateway.Sn)) { Clients.TryRemove(gateway.Sn, out TcpClientWrapper client); if (client.Client.Connected) { client.Client.Close(); } } } } } } private void Connect(string sn, TcpClientWrapper client) { try { client.Client.Connect(client.Ip, 8001); new Thread(() => { var stream = client.Client.GetStream(); while (client.Client.Connected) { if (stream.CanRead) { try { var buffer = new byte[512]; var length = stream.Read(buffer); var data = buffer.ToList().Take(length).ToArray(); Console.WriteLine($"response:{BitConverter.ToString(data)}"); var test = new byte[] { 0x16,0x00, 0x34, 0xF5, 0x41, 0x11, 0xFE, 0x82, 0x0D, 0x02, 0x42 ,0x20 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x01 ,0x00 ,0x00 ,0x00 }; this.Handle(sn, data); } catch (Exception ex) { ex.PrintStack(); } } } }).Start(); } catch (Exception ex) { ex.PrintStack(); } } private void Refresh(string sn) { Console.WriteLine($"refresh {sn}"); this.Clients.TryGetValue(sn, out TcpClientWrapper client); var list = new List() { 0x08, 0x00 }; list.AddRange(sn.HexToBytes().Reverse()); list.Add(0xfe); list.Add(0x81); this.Write(client.Client, list.ToArray()); } private void SearchAddress() { var list = new List() { 0x0c }; var command = Command(list.ToArray()); this.Write(command); } private void Handle(string sn, byte[] data) { var length = 2 + data[1]; if (data.Length > length) { Handle(sn, data.Skip(length).ToArray()); } else { this.HandleInternal(sn, data.Take(length).ToArray()); } } private void HandleInternal(string sn, byte[] data) { Console.WriteLine($"read:{BitConverter.ToString(data).Replace("-", " ")}"); using (var ms = new MemoryStream(data)) { var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexString(2); var endpoint = ms.ReadByte(); using (var scope = _applicationServices.CreateScope()) { var deviceRepo = scope.ServiceProvider.GetService>(); var device = deviceRepo.Table().FirstOrDefault(o => o.Address == address); if (device == null) { Console.WriteLine($"{address} hasn't save in database"); } if (responseType == MessageType.DeviceResponse)//获取设备返回值 { var profileId = ms.ReadInt(); var deviceId = ms.ReadInt(); var deviceType = DeviceId.List.FirstOrDefault(o => o.RawDeviceId == deviceId); if (deviceType == null) { Console.WriteLine($"{deviceId} hasn't config in database"); } else { if (device == null) { device = new FBeeDevice { Sn = sn, Name = deviceType.Name, Icon = deviceType.Icon, CategoryNumber = deviceType.RawDeviceId, CategoryName = deviceType.Category }; deviceRepo.Add(device); } device.Update(data); } } else if (responseType == MessageType.DeviceNotify || responseType == MessageType.DeviceNotiryExt)//设备上报 { var clusterId = ms.ReadInt(); if (device != null) { var reportCount = ms.ReadByte(); var props = new Dictionary(); for (int i = 0; i < reportCount; i++) { var propId = ms.ReadInt(); var propDataTypeValue = ms.ReadByte(); int propDataLength; if (Enum.IsDefined(typeof(DataType), propDataTypeValue)) { var propDataType = (DataType)propDataTypeValue; if (propDataType == DataType.bitstring || propDataType == DataType.characterstring) { propDataLength = ms.ReadByte(); } else if (propDataType == DataType.longbitstring || propDataType == DataType.longcharacterstring) { propDataLength = ms.ReadInt(); } else if (propDataType == DataType.sequence || propDataType == DataType.set || propDataType == DataType.bag) { propDataLength = ms.ReadInt(); } else if (propDataType == DataType.unknown) { propDataLength = 0; } else { propDataLength = Convert.ToInt32(Regex.Match(propDataType.GetName(), @"\d+").Groups[1].Value); } } else { propDataLength = 1; } var propData = ms.Read(propDataLength); props.Add(propId, propData); } } } else if (responseType == MessageType.DevicePowerResponse) { var powerStatus = ms.ReadByte(); if (device != null) { device.Power = powerStatus; } } deviceRepo.SaveChanges(); } } } private void SearchNodeStatus() { var list = new List() { 0x15 }; list.AddRange(this._address); list.Add(0x00); var command = Command(list.ToArray()); this.Write(command); } private void SearchNodeType(int i) { var list = new List() { 0x16 }; list.AddRange(this._address); list.Add(0x00); var modbusdata = new List { (byte)i,0x03,0x00,0x0f,0x00,0x01 }.ToArray(); var modbus = new List(); modbus.AddRange(modbusdata); var crc = Crc16.ComputeChecksum(modbusdata); modbus.Add(crc[1]); modbus.Add(crc[0]); list.AddRange(modbus); var command = Data(list.ToArray()); this.Write(command); } private void SearchSwitchStatus(int i, byte cmd) { var list = new List() { 0x16 }; list.AddRange(this._address); list.Add(0x00); var modbusdata = new List { (byte)i, 0x01,0x00,0x00,0x00,cmd }.ToArray(); var modbus = new List(); modbus.AddRange(modbusdata); var crc = Crc16.ComputeChecksum(modbusdata); modbus.Add(crc[1]); modbus.Add(crc[0]); list.AddRange(modbus); var command = Data(list.ToArray()); this.Write(command); } private byte[] Command(byte[] data) { var command = new List { 0xfe, 0xa5, 0x00 }; command.Add((byte)data.Length); command.AddRange(data); command.Add((byte)command.Skip(2).Select(o => Convert.ToInt32(o)).Sum()); return command.ToArray(); } private byte[] Data(byte[] data, bool crc16 = false) { var command = new List { 0xfe, 0xa5, 0x01 }; command.Add((byte)data.Length); command.AddRange(data); if (crc16) { var crc = Crc16.ComputeChecksum(data); command.Add(crc[1]); command.Add(crc[0]); } else { command.Add((byte)command.Skip(2).Select(o => Convert.ToInt32(o)).Sum()); } return command.ToArray(); } private void Command16(byte[] command) { var list = new List() { 0x16 }; list.AddRange(this._address); list.Add(0x00); var modbusdata = command; var modbus = new List(); modbus.AddRange(modbusdata); var crc = Crc16.ComputeChecksum(modbusdata); modbus.Add(crc[1]); modbus.Add(crc[0]); list.AddRange(modbus); var data = Data(list.ToArray()); this.Write(data); } private void Write(byte[] command) { Thread.Sleep(500); Console.WriteLine($"write:{BitConverter.ToString(command).Replace("-", " ")}"); this._tcpClient.GetStream().Write(command); } private void Write(TcpClient client, byte[] command) { if (client.Connected) { Console.WriteLine($"write:{BitConverter.ToString(command).Replace("-", " ")}"); client.GetStream().Write(command); } else { Console.WriteLine($"can not write:{BitConverter.ToString(command).Replace("-", " ")}"); } } private NotifyModel CreateModel(string name, string number, string path, string icon) { var host = string.IsNullOrEmpty(this._configuration["server.ip"]) ? "localhost" : this._configuration["server.ip"]; var port = Convert.ToInt32(Regex.Match(this._configuration["server.urls"], @"(?<=:)\d+").Value); return new NotifyModel { CategoryName = "电器", CategoryNumber = "20", Name = name, Number = number, Icon = icon, IsOnline = true, BaseUrl = $"http://{host}:{port}/{path}", ApiPath = "/api" }; } private void NotifyServer(NotifyModel model) { var url = $"http://{this._configuration["node.url"]}/Notify"; Console.WriteLine(url); var hc = this._httpClientFactory.CreateClient(); var task = this._httpClientFactory.CreateClient().PostAsync(url, new FormUrlEncodedContent(model.ToList())); task.Wait(); using (var response = task.Result) { using (var content = response.Content) { var value = content.ReadAsStringAsync().Result; Console.WriteLine($"end:{url}:{value}"); } } } public void Dispose() { Console.WriteLine("LiChuangService dispose..."); if (this._tcpClient.Connected) { this._tcpClient.Close(); } this._tokenSource.Cancel(); } } }