using Application.Domain.Entities; using Infrastructure.Data; using Infrastructure.Extensions; using IoT.Shared.Application.Models; using IoT.Shared.Services; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace IoTNode.DeviceServices.FBee { public class FBeeService : BaseDeviceService { private readonly ConcurrentDictionary Clients = new ConcurrentDictionary(); private readonly ILogger _logger; public FBeeService(IServiceProvider applicationServices, IWebHostEnvironment env, ILogger logger) : base(applicationServices, env) { this._logger = logger; } public override Task StartAsync(CancellationToken cancellationToken) { Task.Run(async () => { while (!cancellationToken.IsCancellationRequested) { foreach (var item in this.Clients) { try { this._logger.LogInformation($"check gateway:{item.Key}"); this.X9d(item.Key); } catch (Exception ex) { ex.PrintStack(); } } await Task.Delay(1000 * 60); } }); return base.StartAsync(cancellationToken); } public void UpdateButtons(string number, string buttons) { var deviceId = this.GetIoTDeviceId(number); if(deviceId.HasValue) { this.UpdateIoTData(deviceId.Value, DataKeys.Buttons, buttons.FromJson>()); } } public void IRMath(string number, byte type) { var deviceId = this.GetIoTDeviceId(number); if (deviceId.HasValue) { var version = this.GetIoTDataValue(deviceId.Value, DataKeys.IrVersion); if(!string.IsNullOrEmpty(version)) { var values = number.Split('-'); this.XA70081(values[0], values[1], version, type); } } } public override void Execute() { 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) { this._logger.LogDebug($"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); this._logger.LogDebug(result); if (!list.Any(o => o == result)) { list.Add(result); } } Thread.Sleep(1); } } catch (Exception ex) { ex.PrintStack(); } }, tokenSource.Token); Thread.Sleep(1000); tokenSource.Cancel(); } using var scope = _applicationServices.CreateScope(); var iotNodeClient = scope.ServiceProvider.GetService(); var productNumber = "fbee:gateway"; var product = this.UpdateProduct("FBee网关", productNumber, "/Gateway/", "gateway"); var deviceNodeRepo = scope.ServiceProvider.GetService>(); var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault(); var deviceRepo = 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 writeList = GetSetting("fbee.writelist"); if (!string.IsNullOrEmpty(writeList)) { var snList = writeList.Split(','); if (!snList.Contains(sn)) { this._logger.LogDebug($"skip {sn}:{ip} because it isn't contains by writelist"); continue; } } var device = deviceRepo.Table().FirstOrDefault(o => o.Number == sn); if (device == null) { device = new IoTDevice { Number = sn, Name = "网关", DisplayName = "网关", Icon = "gateway", IoTProductId = product.Id, IoTGatewayId = node.Id, }; deviceRepo.Add(device); } device.Ip = ip; deviceRepo.SaveChanges(); } var gateways = deviceRepo.ReadOnlyTable().Where(o => o.IoTProduct.Name == "FBee网关").ToList(); foreach (var gateway in gateways) { try { if (Clients.Any(o => o.Key == gateway.Number)) { Clients.TryGetValue(gateway.Number, out TcpClientWrapper client); if (client.Ip != gateway.Ip) { client.Ip = gateway.Ip; this.Connect(client); } else { if (!client.Client.Connected) { this.Connect(client); } } } else { var client = new TcpClientWrapper { Sn = gateway.Number, Ip = gateway.Ip, Client = new TcpClient() }; Clients.TryAdd(gateway.Number, client); this.Connect(client); } this.X81(gateway.Number); } catch (Exception ex) { ex.PrintStack(); } } foreach (var item in Clients) { if (!gateways.Any(o => o.Number == item.Key)) { Clients.Remove(item.Key, out TcpClientWrapper value); value.Client.Dispose(); } } } public override Task StopAsync(CancellationToken cancellationToken) { try { foreach (var item in Clients) { if (item.Value.Client != null) { item.Value.Client.Dispose(); } } } catch (Exception ex) { ex.PrintStack(); } finally { this._logger.LogDebug($"{this.GetType().Name} Service Stopd"); } return Task.CompletedTask; } /// /// 0x81获取当前连接所有设备 /// /// public void X81(string sn) { this._logger.LogDebug($"refresh {sn}"); this.Write(sn, RequestType.x81); } /// /// 接收当前连接的设备信息返回值 /// /// /// private void X01(string sn, byte[] data) { try { using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var iotNodeClient = scope.ServiceProvider.GetService(); var profileId = ms.ReadInt(); var deviceId = ms.ReadInt(); if (deviceId <= 0) { return; } var switchState = ms.ReadByte(); var nameLength = ms.ReadByte(); var cname = ""; if (nameLength > 0) { cname = ms.ReadASIIString(nameLength); } var isOnline = ms.ReadByte(); var ieee = ms.ReadHexString(8); var snLength = ms.ReadByte(); if (snLength > 0) { var rawSn = ms.ReadHexString(snLength); } var zoneType = ms.ReadInt(); var deviceType = DeviceId.List.FirstOrDefault(o => o.RawDeviceId == deviceId); if (deviceId == 0x000a) { } if (deviceType != null && zoneType != 0x0000) { var deviceIcon = deviceType.Icon; var deviceName = deviceType.Name; if (deviceId == 0x402) { if (zoneType == 0x0028) { deviceName = "烟雾报警器"; deviceIcon = "smoke"; } else if (zoneType == 0x000d) { deviceName = "人体感应器"; deviceIcon = "infrared"; } else { deviceName = null; } } else if (deviceId == 0x002) { if (zoneType == 0x0001) { deviceName = "一路开关"; deviceIcon = "switch1"; } else if (zoneType == 0x0002) { deviceName = "二路开关"; deviceIcon = "switch2"; } else if (zoneType == 0x0003) { deviceName = "三路开关"; deviceIcon = "switch3"; } else { deviceName = null; } } if (string.IsNullOrEmpty(deviceName)) { return; } var productNumber = $"fbee:{deviceType.RawDeviceId:x4}:{zoneType:x2}"; var number = deviceType.RawDeviceId; var productRepo = scope.ServiceProvider.GetService>(); var product = this.UpdateProduct(deviceName, productNumber, this.GetPathByDeviceName(deviceName), deviceIcon); var deviceNodeRepo = scope.ServiceProvider.GetService>(); var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault(); var deviceRepo = scope.ServiceProvider.GetService>(); var deviceNumber = $"{sn}-{ieee}"; var device = deviceRepo.Table().FirstOrDefault(o=>o.Number== deviceNumber); var online = isOnline != 0x00; if (device == null) { device = new IoTDevice { Number = $"{sn}-{ieee}", Name = deviceName, DisplayName = deviceName, Icon = deviceIcon, Gateway = sn, IoTProductId = product.Id, IoTGatewayId = node.Id }; deviceRepo.Add(device); device.IsOnline = online; deviceRepo.SaveChanges(); if (device.Name == "红外转发器") { var buttons = new List();// { new ButtonModel { Name = "测试", Value = "603", Order = 0 } }.ToJson(true); this.UpdateIoTData(device.Id,DataKeys.Buttons, buttons); } } else { device.IsOnline = online; deviceRepo.SaveChanges(); } this.UpdateIoTData(device.Id,DataKeys.DeviceId, deviceId); this.UpdateIoTData(device.Id,DataKeys.Address, address); this.UpdateIoTData(device.Id,DataKeys.EndPoint, endpoint); if (new int[] { 0x0002, 0x0009, 0x0081, 0x0202, 0x0220, 0x0051 }.Contains(deviceId)) { if (deviceId == 0x202) { } this.UpdateIoTData(device.Id,DataKeys.PowerState, switchState); } var battery = ms.ReadByte(); this.UpdateIoTData(device.Id,DataKeys.Battery, battery); var epCount = ms.ReadByte(); this.UpdateIoTData(device.Id,DataKeys.EndPointCount, battery); if (device.Name == "光强检测器") { ms.Read(2); var light = BitConverter.ToInt16(ms.Read(2)); var desc = this.GetDescription(DataKeys.Light.GetName(), light); this.UpdateIoTData(device.Id,DataKeys.Light, light, desc); } else if (device.Name == "温湿度传感器") { var temperature = BitConverter.ToInt16(ms.Read(2)) / 100f; this.UpdateIoTData(device.Id,DataKeys.Temperature, temperature, this.GetDescription(DataKeys.Temperature.GetName(),temperature)); var humidity = BitConverter.ToInt16(ms.Read(2)) / 100f; this.UpdateIoTData(device.Id,DataKeys.Humidity, humidity, this.GetDescription(DataKeys.Humidity.GetName(), humidity)); } else if (device.Name == "烟雾报警器" || device.Name == "人体感应器") { var state = BitConverter.ToInt16(ms.Read(2)); this.UpdateIoTData(device.Id,DataKeys.Warning, state); } else if (device.Name == "门锁") { var state = ms.ReadByte(); //this.UpdateData(deviceRepo, device, device.CreateData(Keys.State, "开", DeviceDataType.String, "状态", timestamp: timestamp)); } //var historyData = ms.ReadHexString((int)(ms.Length - ms.Position)); //device.AddorUpdateData(device.CreateData(Keys.Data, historyData, DeviceDataType.String, Keys.Data, hidden: true)); this.UpdateIoTData(device.Id,DataKeys.ZoneType, zoneType); //deviceRepo.SaveChanges(); //var deviceDto = device.To(); //this.SendToServer(Methods.EditDevice, deviceDto); this.UpdateStatus(device); } else { this._logger.LogDebug($"unknown device id or zone type :{deviceId} {zoneType}"); } } catch (Exception ex) { ex.PrintStack(); } } private string GetPathByDeviceName(string deviceName) { if (deviceName == "插座") { return "/Socket/"; } else if (deviceName == "智能插座") { return "/Socket/"; } else if (deviceName == "一路开关") { return "/Switch/"; } else if (deviceName == "二路开关") { return "/Switch2/"; } else if (deviceName == "三路开关") { return "/Switch3/"; } else if (deviceName == "窗帘电机") { return "/Curtain/"; } else if (deviceName == "调色灯") { return "/ColorLight/"; } else if (deviceName == "色暖灯") { return "/WarmLight/"; } else if (deviceName == "红外转发器") { return "/Ir/"; } else if (deviceName == "门锁") { return "/Door/"; } else { return null; } } private void UpdateStatus(IoTDevice device) { try { var deviceId = Convert.ToInt32(this.GetIoTDataValue(device.Id,DataKeys.DeviceId)); var sn = device.Gateway; var ieee = device.Number; if (new int[] { 0x0002, 0x0009, 0x0081, 0x0202, 0x0220, 0x0051 }.Contains(deviceId)) { //this.X85(sn, ieee);//获取开关状态 } if (deviceId == 0x0210 || deviceId == 0x0220) { this.X86(sn, ieee);//获取亮度 this.X87(sn, ieee);//获取色调 this.X88(sn, ieee);//获取饱和度 this.XA9(sn, ieee);//色温 } else if (deviceId == 0x0163) { this.XA70080(sn, ieee);//读版本号 } else if (deviceId == 0x0051) { this.X8D0702(sn, ieee);//读取电量 } } catch (Exception ex) { ex.PrintStack(); } } /// /// 0x95删除指定设备 /// /// /// public void X95(string sn, string ieee) { this._logger.LogDebug($"delete {ieee} from {sn}"); var list = new List(); list.AddRange(ieee.HexToBytes()); list.Add(0x01); this.Write(sn, RequestType.x95, ieee, list, 0); } //0x94更改指定设备名 /// /// 0x82设置指定设备的开关状态 /// /// /// /// public void X82(string sn, string ieee, byte status, byte? ep = null) { var list = new List { status }; this.Write(sn, RequestType.x82, ieee, list, ep: ep); } public void X82OpenDoor(string sn, string ieee, string password, byte status, byte? ep = null) { if (string.IsNullOrEmpty(password)) { password = "123456"; } if (password.Length == 6) { var passwords = password.Select(o => Convert.ToByte(Convert.ToInt32(o.ToString()))).ToArray(); var keys = new byte[] { 0x46, 0x45, 0x49, 0x42, 0x49, 0x47 }; var list = new List { status,0x06//,0x01^0x46,0x02^0x45,0x03^0x49,0x04^0x42,0x05^0x49,0x06^0x47 }; for (int i = 0; i < passwords.Length; i++) { list.Add((byte)(passwords[i] ^ keys[i])); } Console.WriteLine(BitConverter.ToString(list.ToArray())); var list2 = new List { status,0x06,0x01^0x46,0x02^0x45,0x03^0x49,0x04^0x42,0x05^0x49,0x06^0x47 }; Console.WriteLine(BitConverter.ToString(list2.ToArray())); this.Write(sn, RequestType.x82, ieee, list, ep: ep); } } /// /// 0x85-0x07接收指定设备的开关状态 /// /// /// private void X07(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = this.GetDeviceByAddress(deviceRepo, sn, address); if (device != null) { var switchState = ms.ReadInt(); if (device.Name.Contains("二路开关") || device.Name.Contains("三路开关")) { if (endpoint == 0x10) { this.UpdateIoTData(device.Id, DataKeys.L1State, switchState); } else if (endpoint == 0x11) { this.UpdateIoTData(device.Id, DataKeys.L2State, switchState); } else if (endpoint == 0x12) { this.UpdateIoTData(device.Id, DataKeys.L3State, switchState); } } else { var key = device.Name.Contains("电机") ? DataKeys.CurtainState : DataKeys.PowerState; this.UpdateIoTData(device.Id, key, switchState); } } else { this._logger.LogDebug($"device {address} hasn't save in database"); } } /// /// 0x83设置指定设备的亮度 /// /// /// /// 0-255 public void X83(string sn, string ieee, [Range(0, 255)] byte brightness) { var list = new List { brightness }; list.AddRange(new byte[] { 0x00, 0x00 }); this.Write(sn, RequestType.x83, ieee, list); } /// /// 0x08接收指定设备的亮度状态返回值 /// /// /// private void X08(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = this.GetDeviceByAddress(deviceRepo, sn, address); if (device != null) { this.UpdateIoTData(device.Id,DataKeys.Brightness, ms.ReadByte()); } else { this._logger.LogDebug($"device {address} hasn't save in database"); } } /// /// 0x84设置指定设备的颜色,即色调和饱和度 /// public void X84(string sn, string ieee, [Range(0, 255)] byte hue, [Range(0, 255)] byte saturation) { var list = new List { hue, saturation }; list.AddRange(new byte[] { 0x00, 0x00 }); this.Write(sn, RequestType.x84, ieee, list); } /// /// 0x09接收指定设备的色调返回值 /// /// /// private void X09(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = this.GetDeviceByAddress(deviceRepo, sn, address); if (device != null) { this.UpdateIoTData(device.Id,DataKeys.Hue, ms.ReadByte()); } else { this._logger.LogDebug($"device {address} hasn't save in database"); } } /// /// 0x09接收指定设备的饱和度返回值 /// /// /// private void X0A(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = this.GetDeviceByAddress(deviceRepo, sn, address); if (device != null) { this.UpdateIoTData(device.Id,DataKeys.Saturation, ms.ReadByte()); } else { this._logger.LogDebug($"device {address} hasn't save in database"); } } /// /// 0x85-0x07查询开关状态 /// public void X85(string sn, string ieee) { var list = new List(); this.Write(sn, RequestType.x85, ieee, list); } /// /// 0x86-0x08查询亮度状态 /// public void X86(string sn, string ieee) { var list = new List(); this.Write(sn, RequestType.x86, ieee, list); } /// /// 0x87-0x09查询色度状态 /// public void X87(string sn, string ieee) { var list = new List(); this.Write(sn, RequestType.x87, ieee, list); } /// /// 0x88-0x0a查询饱和度状态 /// public void X88(string sn, string ieee) { var list = new List(); this.Write(sn, RequestType.x88, ieee, list); } /// /// 0x8d-0x70发送zcl指令,读取簇id为0702的电量、电功率 /// public void X8D0702(string sn, string ieee) { this.Write(sn, RequestType.x8d, ieee, new List() { 0x02, 0x07, 0x01, 0x03, 0x00 }, 3);//0301电量乘数 this.Write(sn, RequestType.x8d, ieee, new List() { 0x02, 0x07, 0x02, 0x03, 0x00 }, 3);//0302电量除数 this.Write(sn, RequestType.x8d, ieee, new List() { 0x04, 0x0b, 0x04, 0x06, 0x00 }, 3);//0604功率乘数 this.Write(sn, RequestType.x8d, ieee, new List() { 0x04, 0x0b, 0x05, 0x06, 0x00 }, 3);//0605功率除数 this.Write(sn, RequestType.xa7, ieee, new List() { 0x02, 0x07, 0x00, 0x00, 0x00 }, 2);//0000电量 this.Write(sn, RequestType.xa7, ieee, new List() { 0x02, 0x07, 0x0b, 0x05, 0x00 }, 2);//050b功率 } // //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); } /// /// 0x15接收网关信息 /// /// /// private void X15(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var version = ms.ReadASIIString(5); var snid = ms.ReadHexString(3); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetRequiredService>(); var device = deviceRepo.Table().FirstOrDefault(o => o.Number == sn); if (device == null) { this._logger.LogDebug($"gateway {sn} hasn't save in database"); } else { this.UpdateIoTData(device.Id,DataKeys.GatewayVersion, version); this.UpdateIoTData(device.Id,DataKeys.DeviceCount, ms.ReadByte()); var userName = ms.ReadASIIString(20); var password = ms.ReadASIIString(20); device.UserName = userName; device.Password = password; device.IsOnline = true; deviceRepo.SaveChanges(); ms.ReadByte(); ms.ReadByte(); ms.ReadByte(); ms.ReadByte(); var hex = ms.ReadHexString(5); var compileVersion = ms.ReadHexString(5); } } //0x9e //0x9f //0xa0 //0xa1 //0xa2 //0xa3 //0xa4 //0xa5 /// /// 0xa8设置指定设备的色温 /// public void XA8(string sn, string ieee, [Range(2700, 6500)] ushort color) { var list = new List(); list.AddRange(BitConverter.GetBytes(color)); list.AddRange(new byte[] { 0x00, 0x00 }); this.Write(sn, RequestType.xa8, ieee, list); } /// /// 0x27接收指定设备的色温返回值 /// /// /// private void X27(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = this.GetDeviceByAddress(deviceRepo, sn, address); if (device != null) { this.UpdateIoTData(device.Id,DataKeys.ColorTemperature, ms.ReadInt()); } else { this._logger.LogDebug($"device {address} hasn't save in database"); } } /// /// 0080查询红外版本号 /// /// /// public void XA70080(string sn, string ieee) { var list = new List { 0x03,//控制标志 0xff,//包长 0x00//包序号 }; list.AddRange(new byte[] { 0x55, 0x55 }); list.Add(0x02); list.AddRange(new byte[] { 0x80, 0x00 }); list.Add(0x82); list[1] = (byte)(list.Count() - 3); this.Write(sn, RequestType.xa7, ieee, list, 2); } /// /// 0081开始匹配遥控器 /// /// /// public void XA70081(string sn, string ieee, string version, [Range(1, 5)] byte type) { var list = new List { 0x03,//控制标志 0xff,//包长 0x00//包序号 }; list.AddRange(new byte[] { 0x55, 0x55 }); list.Add(0x09); list.AddRange(version.HexToBytes()); list.AddRange(new byte[] { 0x81, 0x00 }); list.Add(type); list[1] = (byte)(list.Count() - 3 + 2); list.Add((byte)list.Skip(list.Count - 10).Take(10).Select(o => (int)o).Sum()); //list.Add(0x1a); this.Write(sn, RequestType.xa7, ieee, list, 2); } public void XA70082(string sn, string ieee, byte type, ushort code) { var list = new List { 0x03,//控制标志 0xff,//包长 0x00//包序号 }; list.AddRange(new byte[] { 0x55, 0x55 }); list.Add(0x0b); using (var scope = _applicationServices.CreateScope()) { var deviceRepo = scope.ServiceProvider.GetService>(); var number = $"{sn}-{ieee}"; var deviceId=this.GetIoTDeviceId(number); if(deviceId.HasValue) { var version = this.GetIoTDataValue(deviceId.Value, DataKeys.IrVersion); if(!string.IsNullOrEmpty(version)) { list.AddRange(version.HexToBytes()); } } } list.AddRange(new byte[] { 0x82, 0x00 }); list.Add(type); list.AddRange(BitConverter.GetBytes(code)); list[1] = (byte)(list.Count() - 3 + 1); list.Add((byte)list.Skip(5).Take(12).Select(o => (int)o).Sum()); this.Write(sn, RequestType.xa7, ieee, list, 2); } public void XA70083(string sn, string ieee, byte type, ushort code) { if (type == 1) { if (code <= 602) { throw new Exception($"code {code} must > 602 when type is {type} "); } } else { if (code <= 43) { throw new Exception($"code {code} must > 43 when type is {type} "); } } var list = new List { 0x03,//控制标志 0xff,//包长 0x00//包序号 }; list.AddRange(new byte[] { 0x55, 0x55 }); list.Add(0x0b); using (var scope = _applicationServices.CreateScope()) { var repo = scope.ServiceProvider.GetService>(); var number = $"{sn}-{ieee}"; var deviceId = this.GetIoTDeviceId(number); if(deviceId.HasValue) { var version = this.GetIoTDataValue(deviceId.Value,DataKeys.IrVersion); list.AddRange(version.HexToBytes()); } else { //no device return; } } list.AddRange(new byte[] { 0x83, 0x00 }); list.Add(type); list.AddRange(BitConverter.GetBytes(code)); list[1] = (byte)(list.Count() - 3 + 2); list.Add((byte)list.Skip(5).Take(12).Select(o => (int)o).Sum()); //list.Add(0x1a); this.Write(sn, RequestType.xa7, ieee, list, 2); } /// /// 0xa9-0x27查询色温状态 /// public void XA9(string sn, string ieee) { var list = new List(); this.Write(sn, RequestType.xa9, ieee, list); } public void X50(string sn, byte[] data) { try { using var ms = new MemoryStream(data); if (ms.ReadByte() != 0x50) { return; } var length = ms.ReadByte(); ms.Seek(10, SeekOrigin.Current); var success = ms.ReadByte(); if (success == 0x01) { ms.Seek(1, SeekOrigin.Current); var controlType = ms.ReadByte(); if (controlType == 0x95) { ms.Seek(2, SeekOrigin.Current); var address = ms.ReadHexStringDesc(2); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var key = DataKeys.Address.GetName(); var device = deviceRepo.Table().FirstOrDefault(o => o.Number.StartsWith(sn) && o.Data.Any(d => d.Key == key && d.Value == address)); if (device != null) { deviceRepo.Delete(device); deviceRepo.SaveChanges(); } } } } catch (Exception ex) { ex.PrintStack(); this._logger.LogError(ex, ex.Message); } } //0xac //0xaf //0xb0 /// /// 0x70设备上报信息 /// /// /// private void X70(string sn, byte[] data) { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); using var ms = new MemoryStream(data); var responseType = ms.ReadByte(); var dataLength = ms.ReadByte(); var address = ms.ReadHexStringDesc(2); var endpoint = ms.ReadByte(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var device = this.GetDeviceByAddress(deviceRepo, sn, address); var clusterId = (ClusterId)ms.ReadInt(); if (device != null) { var fbeeDeviceId = Convert.ToInt32(this.GetIoTDataValue(device.Id,DataKeys.DeviceId)); 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(FBeeDataType), propDataTypeValue)) { var propDataType = (FBeeDataType)propDataTypeValue; if (propDataType == FBeeDataType.bitstring || propDataType == FBeeDataType.characterstring) { propDataLength = ms.ReadByte(); } else if (propDataType == FBeeDataType.longbitstring || propDataType == FBeeDataType.longcharacterstring) { propDataLength = ms.ReadInt(); } else if (propDataType == FBeeDataType.sequence || propDataType == FBeeDataType.set || propDataType == FBeeDataType.bag) { propDataLength = ms.ReadInt(); } else if (propDataType == FBeeDataType.unknown) { propDataLength = 0; } else { try { propDataLength = Convert.ToInt32(Regex.Match(propDataType.GetName(), @"(\d+)$").Groups[1].Value); } catch (Exception ex) { ex.PrintStack(); this._logger.LogError(ex.ToString()); propDataLength = 1; } } } else { propDataLength = 1; } var propData = ms.Read(propDataLength); props.Add(propId, propData); } if (clusterId == ClusterId.off) { var status = props.First().Value[0]; if (status == 0x00)//离网 { this.DeleteDevice(device.Id); } else if (status == 0x03)//入网 { } } else if (clusterId == ClusterId.light) { var light = BitConverter.ToInt16(props[0x0000]); var desc = this.GetDescription(DataKeys.Light.GetName(), light); this.UpdateIoTData(device.Id,DataKeys.Light, light, desc); } else if (clusterId == ClusterId.alarm) { var state = BitConverter.ToInt16(props[0x0080]); this.UpdateIoTData(device.Id,DataKeys.Warning, state); //var data2 = device.CreateData(Keys.UnderVoltage, new BitArray(props[0x0080])[3], DeviceDataType.Int, "低电量"); //device.AddorUpdateData(data2); //var dto2 = data2.To(); //dto2.DeviceNumber = device.Number; //dataList.Add(dto2); } else if (clusterId == ClusterId.pm25) { var pm2d5 = BitConverter.ToInt16(props[0x0000]); var pm1d0 = BitConverter.ToInt16(props[0x0002]); var pm10 = BitConverter.ToInt16(props[0x0001]); this.UpdateIoTData(device.Id,DataKeys.PM25, pm2d5, this.GetDescription(DataKeys.PM25.GetName(), pm2d5)); this.UpdateIoTData(device.Id,DataKeys.PM100, pm1d0, this.GetDescription(DataKeys.PM100.GetName(), pm1d0)); this.UpdateIoTData(device.Id,DataKeys.PM10, pm10); } else if (clusterId == ClusterId.temperature) { var temperature = BitConverter.ToInt16(props[0x0000]) / 100f; var desc = this.GetDescription(DataKeys.Temperature.GetName(), temperature); this.UpdateIoTData(device.Id,DataKeys.Temperature, temperature, desc); } else if (clusterId == ClusterId.humidity) { var humidity = BitConverter.ToInt16(props[0x0000]) / 100f; var desc = this.GetDescription(DataKeys.Humidity.GetName(), humidity); this.UpdateIoTData(device.Id,DataKeys.Humidity, humidity, desc); } else if (clusterId == ClusterId.voltage) { this.UpdateIoTData(device.Id,DataKeys.Voltage, props[0x21][0] / 2f); } else if (clusterId == ClusterId.socket) { if (props.Keys.Contains(0x0000)) { var tempBytes = new List(); tempBytes.AddRange(props[0x00]); tempBytes.Add(0x00); tempBytes.Add(0x00); var electricity = BitConverter.ToInt64(tempBytes.ToArray()) / 1000f; this.UpdateIoTData(device.Id,DataKeys.Electricity, electricity); } } else if (clusterId == ClusterId.doorlock) { if (props.ContainsKey(0x0000)) { this.UpdateIoTData(device.Id,DataKeys.PowerState, props[0x0000][0]); } else if (props.First().Value[0] == 0x20) { var state2 = string.Empty; if (props.First().Value[1] == 0x00) { state2 = "密码"; } else if (props.First().Value[1] == 0x02) { state2 = "指纹"; } else if (props.First().Value[1] == 0x03) { state2 = "刷卡"; } else if (props.First().Value[1] == 0x04) { state2 = "远程"; } else if (props.First().Value[1] == 0x05) { state2 = "多重验证"; } if (props.First().Value[2] == 0x01) { state2 += "关锁"; } else if (props.First().Value[2] == 0x02) { state2 += "开锁"; } else if (props.First().Value[2] == 0x03) { state2 += "非法操作"; } else if (props.First().Value[2] == 0x05) { state2 += "非法门卡"; } if (!string.IsNullOrEmpty(state2)) { this.UpdateIoTData(device.Id,DataKeys.Status, $"{state2}"); } } else { var state = string.Empty; if (props.First().Value[0] == 0x01) { state = "开锁"; } else if (props.First().Value[0] == 0x00) { state = "关锁"; } if (!string.IsNullOrEmpty(state)) { var success = string.Empty; if (props.First().Value[0] == 0x00) { success = "成功"; } else if (props.First().Value[0] == 0x01) { success = "失败"; } else if (props.First().Value[0] == 0x02) { success = "不允许"; } if (!string.IsNullOrEmpty(success)) { this.UpdateIoTData(device.Id,DataKeys.Status, $"{state}{success}"); } } } } if (fbeeDeviceId == 0x0163) { if (props.ContainsKey(0x400a)) { var irdata = props[0x400a]; if (irdata.Length == 10) { var irVersion = BitConverter.ToString((props[0x400a].Skip(3).Take(6).ToArray())).Replace("-", "").ToLower(); if (!string.IsNullOrEmpty(irVersion)) { this.UpdateIoTData(device.Id,DataKeys.IrVersion, irVersion); } } else { var irtype = BitConverter.ToInt16(irdata.Skip(9).Take(2).ToArray()); if (irtype == 0x0082) { var irDeviceType = irdata.Skip(11).First(); if (irDeviceType == 0x01) { var keyCode = BitConverter.ToInt16(irdata.Skip(12).Take(2).ToArray()); this.UpdateIoTData(device.Id,DataKeys.KeyPress, keyCode); } } } } } else if (fbeeDeviceId == 0x0051) { foreach (var item in props) { if (item.Key == 0x0400) { Console.WriteLine(item.Value.ToInt()); } if (item.Key == 0x0000) { var tempBytes = new List(); tempBytes.AddRange(item.Value); tempBytes.Add(0x00); tempBytes.Add(0x00); if (tempBytes.Count == 8) { var multiplier =this.GetIoTDataValue(device.Id,DataKeys.ElectricityMultiplier); var divisor = this.GetIoTDataValue(device.Id, DataKeys.ElectricityDivisor); if (!string.IsNullOrEmpty(multiplier) && !string.IsNullOrEmpty(divisor)) { var electricity = BitConverter.ToInt64(tempBytes.ToArray()); var value = electricity * Convert.ToInt16(multiplier) / Convert.ToSingle(divisor); this.UpdateIoTData(device.Id,DataKeys.Electricity, value); } } else { this._logger.LogError($"data length must be 8 but now is {tempBytes.Count}"); } } else if (item.Key == 0x050b) { var multiplier = this.GetIoTDataValue(device.Id, DataKeys.PowerMultiplier); var divisor = this.GetIoTDataValue(device.Id, DataKeys.PowerDivisor); if (!string.IsNullOrEmpty(multiplier) && !string.IsNullOrEmpty(divisor)) { var power = BitConverter.ToUInt16(item.Value.ToArray()); var value = power * Convert.ToInt16(multiplier) / Convert.ToSingle(divisor); this.UpdateIoTData(device.Id,DataKeys.Power, value); } } else if (item.Key == 0x0400)//0x050b { var multiplier = this.GetIoTDataValue(device.Id, DataKeys.PowerMultiplier); var divisor = this.GetIoTDataValue(device.Id, DataKeys.PowerDivisor); if (!string.IsNullOrEmpty(multiplier) && !string.IsNullOrEmpty(divisor)) { var power = BitConverter.ToUInt16(item.Value.ToArray()); var value = power * Convert.ToInt16(multiplier) / Convert.ToSingle(divisor); this.UpdateIoTData(device.Id,DataKeys.Power, value); } } else if (item.Key == 0x0301) { var value = BitConverter.ToUInt16(item.Value.ToArray()); this.UpdateIoTData(device.Id,DataKeys.ElectricityMultiplier, value); } else if (item.Key == 0x0302) { var value = BitConverter.ToUInt16(item.Value.ToArray()); this.UpdateIoTData(device.Id,DataKeys.ElectricityDivisor, value); } else if (item.Key == 0x0604) { var value = BitConverter.ToUInt16(item.Value.ToArray()); this.UpdateIoTData(device.Id,DataKeys.PowerMultiplier, value); } else if (item.Key == 0x0605) { var value = BitConverter.ToUInt16(item.Value.ToArray()); this.UpdateIoTData(device.Id,DataKeys.PowerDivisor, value); } } //if (clusterId == ClusterId.SummationDivisor) //{ // this.X8D0702(sn, device.Number); //} } } } private string GetDescription(string key, short value) { if (key == DataKeys.Light.GetName()) { return value < 300 ? "低" : (value > 500 ? "高" : "适中"); } else if (key == DataKeys.PM25.GetName()) { return value <= 35 ? "优" : (value < 75 ? "良" : "污染"); } else if (key == DataKeys.PM100.GetName()) { return value <= 50 ? "优" : (value < 75 ? "100" : "污染"); } return null; } private string GetDescription(string key, float value) { if (key == DataKeys.Temperature.GetName()) { return value < 16 ? "低" : (value > 28 ? "高" : "适中"); } else if (key == DataKeys.Humidity.GetName()) { return value < 30 ? "低" : (value > 80 ? "高" : "适中"); } return null; } //0xb1 //0xb2 //0xc2 //0xc3 //0xc4 //0xc5 //0x75 private void Connect(TcpClientWrapper client) { try { if (client != null) { client.Client.Dispose(); client.Client = new TcpClient(); } this._logger.LogDebug($"connect {client.Ip} for {client.Sn}"); client.Client.Connect(client.Ip, 8001); new Thread(() => { var stream = client.Client.GetStream(); while (client.Client.Connected) { try { if (stream.DataAvailable) { var buffer = new byte[512]; var length = stream.Read(buffer); var data = buffer.ToList().Take(length).ToArray(); this._logger.LogDebug($"response from {client.Sn}:{BitConverter.ToString(data)}"); this.Handle(client.Sn, data); } Thread.Sleep(1); } catch (Exception ex) { ex.PrintStack($"read stream {client.Sn} error"); break; } } }).Start(); this.X9d(client.Sn); } catch (Exception ex) { ex.PrintStack(); } } private void Handle(string sn, byte[] data) { try { var length = 2 + data[1]; if (data.Length > length) { Handle(sn, data.Skip(length).ToArray()); } this.HandleInternal(sn, data.Take(length).ToArray()); } catch (Exception ex) { ex.PrintStack(); } } private void HandleInternal(string sn, byte[] data) { this._logger.LogDebug($"read from {sn}:{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.x08) { this.X08(sn, data); } else if (responseType == ResponseType.x09) { this.X09(sn, data); } else if (responseType == ResponseType.x0a) { this.X0A(sn, data); } else if (responseType == ResponseType.x27) { this.X27(sn, data); } else if (responseType == ResponseType.x50) { this.X50(sn, data); } else if (responseType == ResponseType.x70) { this.X70(sn, data); } else if (responseType == ResponseType.x72) { this.X70(sn, data); } else if (responseType == ResponseType.x75) { //网关应答指令 } else { this._logger.LogDebug($"{responseType} hasn't handle"); } } private void Write(string sn, byte commandType, string ieee = null, List command = null, int format = 1, byte? ep = null) { if (ieee != null && ieee.Contains('-')) { ieee = ieee.Split('-')[1]; } this.Clients.TryGetValue(sn, out TcpClientWrapper client); var data = new List(); data.AddRange(sn.HexToBytes().Reverse());//sn号 data.Add(RequestType.xfe);//控制标志 data.Add(commandType);//控制类型 if (command != null) { byte addressType = 0x02; var payload = new List(); using var scope = _applicationServices.CreateScope(); var deviceRepo = scope.ServiceProvider.GetService>(); var deviceNumber = $"{sn}-{ieee}"; var deviceId = this.GetIoTDeviceId(deviceNumber); var address = this.GetIoTDataValue(deviceId.Value, DataKeys.Address).HexToBytes().Reverse(); var endPoint = Convert.ToInt32(this.GetIoTDataValue(deviceId.Value,DataKeys.EndPoint)); if (format == 0) { payload.Add((byte)(command.Count() + 1 + 2));//数据总长 payload.Add(addressType);//地址模式 payload.AddRange(address); payload.AddRange(command); } else if (format == 1) { payload.Add((byte)(command.Count() + 1 + 2 + 6 + 1 + 2));//数据总长 payload.Add(addressType);//地址模式 payload.AddRange(address); payload.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); payload.Add(ep ?? (byte)endPoint); payload.AddRange(new byte[] { 0x00, 0x00 }); payload.AddRange(command); } else if (format == 2) { payload.Add((byte)(command.Count() + 2 + 1));//数据总长 payload.AddRange(address); payload.Add(ep ?? (byte)endPoint); payload.AddRange(command); } else if (format == 3) { payload.Add((byte)(command.Count() + 1 + 2 + 1));//数据总长 payload.Add(addressType);//地址模式 payload.AddRange(address); payload.Add(ep ?? (byte)endPoint); payload.AddRange(command); } data.AddRange(payload); } var MessageLength = BitConverter.GetBytes((ushort)(data.Count() + 2));//消息总长 if (BitConverter.IsLittleEndian) { MessageLength.Reverse(); } var message = new List(); message.AddRange(MessageLength); message.AddRange(data); var bytes = message.ToArray(); this.Send(client, bytes); } private void Send(TcpClientWrapper client, byte[] bytes) { this._logger.LogDebug($"write:{BitConverter.ToString(bytes).Replace("-", " ")}"); try { client?.Client.GetStream().Write(bytes); } catch (Exception ex) { ex.PrintStack(); if (!client.Client.Connected) { this.Connect(client); } } } private IoTDevice GetDeviceByAddress(IRepository deviceRepo, string sn, string address) { return deviceRepo.ReadOnlyTable() .Where(o => o.Number.StartsWith(sn) && o.Data.Any(d => d.Key == DataKeys.Address.GetName() && d.Value == address)) .FirstOrDefault(); } } }