You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iot/projects/IoTNode/DeviceServices/FBee/FBeeService.cs

1628 lines
64 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<string, TcpClientWrapper> Clients = new ConcurrentDictionary<string, TcpClientWrapper>();
private readonly ILogger<FBeeService> _logger;
public FBeeService(IServiceProvider applicationServices, IWebHostEnvironment env, ILogger<FBeeService> 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<List<ButtonModel>>());
}
}
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<string>();
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<IoTNodeClient>();
var productNumber = "fbee:gateway";
var product = this.UpdateProduct("FBee网关", productNumber, "/Gateway/", "gateway");
var deviceNodeRepo = scope.ServiceProvider.GetService<IRepository<IoTGateway>>();
var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<IoTDevice>>();
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;
}
/// <summary>
/// 0x81获取当前连接所有设备
/// </summary>
/// <param name="sn"></param>
public void X81(string sn)
{
this._logger.LogDebug($"refresh {sn}");
this.Write(sn, RequestType.x81);
}
/// <summary>
/// 接收当前连接的设备信息返回值
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IoTNodeClient>();
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<IRepository<IoTProduct>>();
var product = this.UpdateProduct(deviceName, productNumber, this.GetPathByDeviceName(deviceName), deviceIcon);
var deviceNodeRepo = scope.ServiceProvider.GetService<IRepository<IoTGateway>>();
var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<IoTDevice>>();
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<ButtonModel>();// { 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<EditDeviceModel>();
//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();
}
}
/// <summary>
/// 0x95删除指定设备
/// </summary>
/// <param name="sn"></param>
/// <param name="ieee"></param>
public void X95(string sn, string ieee)
{
this._logger.LogDebug($"delete {ieee} from {sn}");
var list = new List<byte>();
list.AddRange(ieee.HexToBytes());
list.Add(0x01);
this.Write(sn, RequestType.x95, ieee, list, 0);
}
//0x94更改指定设备名
/// <summary>
/// 0x82设置指定设备的开关状态
/// </summary>
/// <param name="sn"></param>
/// <param name="ieee"></param>
/// <param name="status"></param>
public void X82(string sn, string ieee, byte status, byte? ep = null)
{
var list = new List<byte>
{
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<byte>
{
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<byte>
{
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);
}
}
/// <summary>
/// 0x85-0x07接收指定设备的开关状态
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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");
}
}
/// <summary>
/// 0x83设置指定设备的亮度
/// </summary>
/// <param name="sn"></param>
/// <param name="ieee"></param>
/// <param name="value">0-255</param>
public void X83(string sn, string ieee, [Range(0, 255)] byte brightness)
{
var list = new List<byte>
{
brightness
};
list.AddRange(new byte[] { 0x00, 0x00 });
this.Write(sn, RequestType.x83, ieee, list);
}
/// <summary>
/// 0x08接收指定设备的亮度状态返回值
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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");
}
}
/// <summary>
/// 0x84设置指定设备的颜色即色调和饱和度
/// </summary>
public void X84(string sn, string ieee, [Range(0, 255)] byte hue, [Range(0, 255)] byte saturation)
{
var list = new List<byte>
{
hue,
saturation
};
list.AddRange(new byte[] { 0x00, 0x00 });
this.Write(sn, RequestType.x84, ieee, list);
}
/// <summary>
/// 0x09接收指定设备的色调返回值
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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");
}
}
/// <summary>
/// 0x09接收指定设备的饱和度返回值
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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");
}
}
/// <summary>
/// 0x85-0x07查询开关状态
/// </summary>
public void X85(string sn, string ieee)
{
var list = new List<byte>();
this.Write(sn, RequestType.x85, ieee, list);
}
/// <summary>
/// 0x86-0x08查询亮度状态
/// </summary>
public void X86(string sn, string ieee)
{
var list = new List<byte>();
this.Write(sn, RequestType.x86, ieee, list);
}
/// <summary>
/// 0x87-0x09查询色度状态
/// </summary>
public void X87(string sn, string ieee)
{
var list = new List<byte>();
this.Write(sn, RequestType.x87, ieee, list);
}
/// <summary>
/// 0x88-0x0a查询饱和度状态
/// </summary>
public void X88(string sn, string ieee)
{
var list = new List<byte>();
this.Write(sn, RequestType.x88, ieee, list);
}
/// <summary>
/// 0x8d-0x70发送zcl指令读取簇id为0702的电量、电功率
/// </summary>
public void X8D0702(string sn, string ieee)
{
this.Write(sn, RequestType.x8d, ieee, new List<byte>() { 0x02, 0x07, 0x01, 0x03, 0x00 }, 3);//0301电量乘数
this.Write(sn, RequestType.x8d, ieee, new List<byte>() { 0x02, 0x07, 0x02, 0x03, 0x00 }, 3);//0302电量除数
this.Write(sn, RequestType.x8d, ieee, new List<byte>() { 0x04, 0x0b, 0x04, 0x06, 0x00 }, 3);//0604功率乘数
this.Write(sn, RequestType.x8d, ieee, new List<byte>() { 0x04, 0x0b, 0x05, 0x06, 0x00 }, 3);//0605功率除数
this.Write(sn, RequestType.xa7, ieee, new List<byte>() { 0x02, 0x07, 0x00, 0x00, 0x00 }, 2);//0000电量
this.Write(sn, RequestType.xa7, ieee, new List<byte>() { 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);
}
/// <summary>
/// 0x15接收网关信息
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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
/// <summary>
/// 0xa8设置指定设备的色温
/// </summary>
public void XA8(string sn, string ieee, [Range(2700, 6500)] ushort color)
{
var list = new List<byte>();
list.AddRange(BitConverter.GetBytes(color));
list.AddRange(new byte[] { 0x00, 0x00 });
this.Write(sn, RequestType.xa8, ieee, list);
}
/// <summary>
/// 0x27接收指定设备的色温返回值
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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");
}
}
/// <summary>
/// 0080查询红外版本号
/// </summary>
/// <param name="sn"></param>
/// <param name="ieee"></param>
public void XA70080(string sn, string ieee)
{
var list = new List<byte>
{
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);
}
/// <summary>
/// 0081开始匹配遥控器
/// </summary>
/// <param name="sn"></param>
/// <param name="ieee"></param>
public void XA70081(string sn, string ieee, string version, [Range(1, 5)] byte type)
{
var list = new List<byte>
{
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<byte>
{
0x03,//控制标志
0xff,//包长
0x00//包序号
};
list.AddRange(new byte[] { 0x55, 0x55 });
list.Add(0x0b);
using (var scope = _applicationServices.CreateScope())
{
var deviceRepo = scope.ServiceProvider.GetService<IRepository<IoTDevice>>();
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<byte>
{
0x03,//控制标志
0xff,//包长
0x00//包序号
};
list.AddRange(new byte[] { 0x55, 0x55 });
list.Add(0x0b);
using (var scope = _applicationServices.CreateScope())
{
var repo = scope.ServiceProvider.GetService<IRepository<IoTDevice>>();
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);
}
/// <summary>
/// 0xa9-0x27查询色温状态
/// </summary>
public void XA9(string sn, string ieee)
{
var list = new List<byte>();
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<IRepository<IoTDevice>>();
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
/// <summary>
/// 0x70设备上报信息
/// </summary>
/// <param name="sn"></param>
/// <param name="data"></param>
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<IRepository<IoTDevice>>();
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<int, byte[]>();
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<EditDataModel>();
//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<byte>();
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<byte>();
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<byte> 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<byte>();
data.AddRange(sn.HexToBytes().Reverse());//sn号
data.Add(RequestType.xfe);//控制标志
data.Add(commandType);//控制类型
if (command != null)
{
byte addressType = 0x02;
var payload = new List<byte>();
using var scope = _applicationServices.CreateScope();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<IoTDevice>>();
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<byte>();
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<IoTDevice> 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();
}
}
}