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 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._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 ..."); Refresh(); } catch (Exception ex) { ex.PrintStack(); } await Task.Delay(this._configuration.GetValue("timer.seconds") * 1000); } }); } public void Refresh() { Console.WriteLine("notify start ..."); try { this.CheckConnection(); //this.SearchAddress(); } catch (Exception ex) { ex.PrintStack(); } Console.WriteLine("notify end ..."); } #region api /// /// 0x81获取当前连接所有设备 /// /// public void X81(string sn) { Console.WriteLine($"refresh {sn}"); this.Write(sn, RequestType.x81, null); } /// /// 接收当前连接的设备信息 /// /// /// private void X01(string sn, byte[] data) { 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 profileId = ms.ReadInt(); var deviceId = ms.ReadInt(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = deviceRepo.Table().FirstOrDefault(o => o.Sn == sn && o.Address == address); if (device == null) { var deviceType = DeviceId.List.FirstOrDefault(o => o.RawDeviceId == deviceId); device = new FBeeDevice { Sn = sn, Name = deviceType.Name, EName = deviceType.EName, Icon = deviceType.Icon, CategoryNumber = deviceType.RawDeviceId, CategoryName = deviceType.Category }; deviceRepo.Add(device); } device.DataType = responseType; device.DataLength = dataLength; device.Address = address; device.Endpoint = endpoint; device.ProfileId = profileId; device.DeviceId = deviceId; device.Status = ms.ReadByte(); device.NameLength = ms.ReadByte(); device.CName = ms.ReadASIIString(device.NameLength); device.Online = ms.ReadByte(); device.IEEE = ms.ReadHexString(8); device.SNLength = ms.ReadByte(); device.RawSN = ms.ReadHexString(device.SNLength); device.ZoneType = ms.ReadByte(); device.Power = ms.ReadByte(); device.Data = ms.ReadHexString(4); device.Safe = ms.ReadHexString(2); device.RawValue = BitConverter.ToString(data); deviceRepo.SaveChanges(); } } } /// /// 0x95删除指定设备 /// /// /// public void X95(string sn, string ieee) { Console.WriteLine($"delete {ieee} from {sn}"); this.Clients.TryGetValue(sn, out TcpClientWrapper client); using (var scope = _applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var device = repo.ReadOnlyTable().FirstOrDefault(o => o.Online != 0 && o.IEEE == ieee); var address = device.Address; var list = new List(); list.Add(0x0c); list.Add(0x02); list.AddRange(address.HexToBytes()); list.AddRange(ieee.HexToBytes()); list.Add(0x01); this.Write(sn, RequestType.x95, list); } } //0x94更改指定设备名 /// /// 0x82设置指定设备的开关状态 /// /// /// /// public void X82(string sn, string ieee, byte status) { using (var scope = _applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var device = repo.ReadOnlyTable().FirstOrDefault(o => o.Sn == sn && o.IEEE == ieee); if (device != null) { var address = device.Address; var list = new List(); 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.Write(sn, RequestType.x82, list); } } } /// /// 0x07接收指定设备的开关状态 /// /// /// private void X07(string sn, byte[] data) { 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.Sn == sn && o.Address == address); if (device != null) { device.Power = ms.ReadInt(); deviceRepo.SaveChanges(); } else { Console.WriteLine($"device {address} hasn't save in database"); } } } } /// /// 0x83设置指定设备的亮度 /// /// /// /// 0-255 public void X83(string sn, string ieee, byte value) { using (var scope = _applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var device = repo.ReadOnlyTable().FirstOrDefault(o => o.Sn == sn && o.IEEE == ieee); if (device != null) { var address = device.Address; var list = new List(); list.AddRange(sn.HexToBytes().Reverse()); list.Add(RequestType.xfe); list.Add(RequestType.x82); 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(value); list.AddRange(new byte[] { 0x00, 0x00 }); this.Write(sn, RequestType.x82, list); } } } /// /// 0x29接收指定设备的亮度 /// /// /// private void X29(string sn, byte[] data) { using (var ms = new MemoryStream(data)) { var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); } } //0x84 //0x85 //0x86 //0x87 //0x88 //0x8d //0x96 //0x8e //0x8f //0x97 //0x98 //0x90 //0x91 //0x92 //0x93 //0x8a //0x8b //0x8c //0x99 //0x9a //0x9b //0x9c //0x9d获取网关信息 public void X9d(string sn) { this.Write(sn, RequestType.x9d, null); } /// /// 0x15接收网关信息 /// /// /// private void X15(string sn, byte[] data) { using (var ms = new MemoryStream(data)) { var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var version = ms.ReadASIIString(5); var snid = ms.ReadHexString(4); using (var scope = _applicationServices.CreateScope()) { var gatewayRepo = scope.ServiceProvider.GetService>(); var gateway = gatewayRepo.Table().FirstOrDefault(o => o.Sn == sn); if (gateway == null) { Console.WriteLine($"gateway {sn} hasn't save in database"); } else { gateway.Version = version; gateway.UserName = ms.ReadASIIString(20); gateway.Password = ms.ReadASIIString(20); gateway.DeviceCount = ms.ReadByte(); gateway.GroupCount = ms.ReadByte(); gateway.TimerCount = ms.ReadByte(); gateway.SceneCount = ms.ReadByte(); gateway.TaskCount = ms.ReadByte(); var hex = ms.ReadHexString(5); gateway.CompileVersion = ms.ReadHexString(5); } gatewayRepo.SaveChanges(); } } } //0x9e //0x9f //0xa0 //0xa1 //0xa2 //0xa3 //0xa4 //0xa5 //0xa8 //0xa9 //0xab //0xa7 //0xac //0xaf //0xb0 /// /// 0x70设备上报信息 /// /// /// private void X70(string sn, byte[] data) { 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.Sn == sn && o.Address == address); 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); } } } } } //0x72 //0xb1 //0xb2 //0xc1 //0xc2 //0xc3 //0xc4 //0xc5 //0x75 #endregion api 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) { this.Connect(client); } else { if (!client.Client.Connected) { this.Connect(client); } } } else { var client = new TcpClientWrapper { Sn = gateway.Sn, Ip = gateway.Ip, Client = new TcpClient() }; Clients.TryAdd(gateway.Sn, client); this.Connect(client); } this.X81(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(TcpClientWrapper client) { try { if (client != null) { client.Client.Dispose(); client.Client = new TcpClient(); } Console.WriteLine($"connect {client.Ip} for {client.Sn}"); client.Client.Connect(client.Ip, 8001); new Thread(() => { var stream = client.Client.GetStream(); while (client.Client.Connected) { if (stream.DataAvailable) { try { var buffer = new byte[512]; var length = stream.Read(buffer); var data = buffer.ToList().Take(length).ToArray(); Console.WriteLine($"response:{BitConverter.ToString(data)}"); this.Handle(client.Sn, data); } catch (Exception ex) { ex.PrintStack("read stream error"); } } } }).Start(); this.X9d(client.Sn); } catch (Exception ex) { ex.PrintStack(); } } 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("-", " ")}"); var responseType = data[0]; if (responseType == ResponseType.x15) { this.X15(sn, data); } else if (responseType == ResponseType.x01) { this.X01(sn, data); } else if (responseType == ResponseType.x07) { this.X07(sn, data); } else if (responseType == ResponseType.x70) { this.X70(sn, data); } else if (responseType == ResponseType.x72) { //this.X70(sn, data); } else { Console.WriteLine($"{responseType} hasn't handle"); } } private void Write(string sn, byte commandType, List command) { this.Clients.TryGetValue(sn, out TcpClientWrapper client); if (!client.Client.Connected) { this.Connect(client); } if (command == null) { command = new List(); } var data = new List(); data.AddRange(sn.HexToBytes().Reverse()); data.Add(RequestType.xfe); data.Add(commandType); data.AddRange(command); var head = BitConverter.GetBytes(data.Count() + 2).ToList(); if (BitConverter.IsLittleEndian) { head.Reverse(); } head = head.Skip(2).Reverse().ToList(); var list = new List(); list.AddRange(head); list.AddRange(data); var bytes = list.ToArray(); Console.WriteLine($"write:{BitConverter.ToString(bytes).Replace("-", " ")}"); client.Client.GetStream().Write(bytes); } 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("dispose..."); this._tokenSource.Cancel(); foreach (var item in Clients) { if (item.Value.Client != null) { item.Value.Client.Dispose(); } } } } }