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

1453 lines
58 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 Application.Models;
using Infrastructure.Data;
using Infrastructure.Extensions;
using IoT.Shared.Services;
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.Diagnostics;
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, ILogger<FBeeService> logger)
: base(applicationServices)
{
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)
{
using var scope = _applicationServices.CreateScope();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
var device = deviceRepo.Table().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
if (device != null)
{
var data = device.Data.FirstOrDefault(o => o.Key == Keys.Buttons);
if (data != null)
{
data.Value = buttons.FromJson<List<ButtonModel>>().ToJson(true);
deviceRepo.SaveChanges();
}
}
}
public void IRMath(string number, byte type)
{
using var scope = _applicationServices.CreateScope();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
var device = deviceRepo.Table().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
if (device != null)
{
var values = number.Split('-');
var version = device.Data.FirstOrDefault(o => o.Key == Keys.Version)?.Value;
if (version != null)
{
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 categoryRepo = scope.ServiceProvider.GetService<IRepository<Category>>();
var productNumber = "fbee:gateway";
var product = this.UpdateProduct("FBee网关", productNumber, "/Gateway/", "00", "gateway");
var deviceNodeRepo = scope.ServiceProvider.GetService<IRepository<Node>>();
var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
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 Device
{
Number = sn,
Name = "网关",
DisplayName = "网关",
Icon = "gateway",
ProductId = product.Id,
NodeId = node.Id,
};
deviceRepo.Add(device);
}
device.Ip = ip;
deviceRepo.SaveChanges();
}
var gateways = deviceRepo.ReadOnlyTable().Where(o => o.Product.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;
}
#region private
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.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<Device>>();
var device = this.GetDevice(deviceRepo, sn, ieee);
var address = device.GetDataValue(Keys.Address).HexToBytes().Reverse();
var endPoint = Convert.ToInt32(device.GetDataValue(Keys.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);
}
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);
}
}
}
#endregion private
#region api
/// <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
{
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 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);
var deviceIcon = deviceType.Icon;
if (deviceType != null)
{
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 categoryRepo = scope.ServiceProvider.GetService<IRepository<Category>>();
var category = categoryRepo.ReadOnlyTable().FirstOrDefault(o => o.Number == deviceType.CategoryNumber);
var productRepo = scope.ServiceProvider.GetService<IRepository<Product>>();
var product = this.UpdateProduct(deviceName, productNumber, this.GetPathByDeviceName(deviceName), deviceType.CategoryNumber, deviceIcon);
var deviceNodeRepo = scope.ServiceProvider.GetService<IRepository<Node>>();
var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault();
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
var device = this.GetDevice(deviceRepo, sn, ieee);
var online = isOnline != 0x00;
if (device == null)
{
device = new Device
{
Number = $"{sn}-{ieee}",
Name = deviceName,
DisplayName = deviceName,
Icon = deviceIcon,
Gateway = sn,
ProductId = product.Id,
NodeId = 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.UpdateData(deviceRepo, device, device.CreateData(Keys.Buttons, buttons, DeviceDataType.String, "指令", timestamp: timestamp));
}
}
else
{
device.IsOnline = online;
deviceRepo.SaveChanges();
}
this.UpdateData(deviceRepo, device, device.CreateData(Keys.DeviceId, deviceId, DeviceDataType.Int, Keys.DeviceId, hidden: true, timestamp: timestamp));
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Address, address, DeviceDataType.String, Keys.Address, hidden: true, timestamp: timestamp));
this.UpdateData(deviceRepo, device, device.CreateData(Keys.EndPoint, endpoint, DeviceDataType.Int, Keys.EndPoint, hidden: true, timestamp: timestamp));
if (new int[] { 0x0002, 0x0009, 0x0081, 0x0202, 0x0220, 0x0051 }.Contains(deviceId))
{
if (deviceId == 0x202)
{
}
this.UpdateData(deviceRepo, device, device.CreateData(Keys.State, switchState == 0 ? "关" : (switchState == 1 ? "开" : "停"), DeviceDataType.String, "状态", timestamp: timestamp));
}
var battery = ms.ReadByte();
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Battery, battery, DeviceDataType.Int, Keys.Battery, hidden: true, timestamp: timestamp));
var epCount = ms.ReadByte();
this.UpdateData(deviceRepo, device, device.CreateData(Keys.EndPointCount, battery, DeviceDataType.Int, Keys.EndPointCount, hidden: true, timestamp: timestamp));
if (device.Name == "光强检测器")
{
ms.Read(2);
var light = BitConverter.ToInt16(ms.Read(2));
var desc = this.GetDescription(Keys.Light, light);
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Light, light, DeviceDataType.Int, "光照度", "lux", desc, timestamp: timestamp));
}
else if (device.Name == "温湿度传感器")
{
var temperature = BitConverter.ToInt16(ms.Read(2)) / 100f;
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Temperature, temperature, DeviceDataType.Float, "温度", "℃", this.GetDescription(Keys.Temperature, temperature), timestamp: timestamp));
var humidity = BitConverter.ToInt16(ms.Read(2)) / 100f;
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Humidity, humidity, DeviceDataType.Float, "湿度", "RH%", this.GetDescription(Keys.Humidity, humidity), timestamp: timestamp));
}
else if (device.Name == "烟雾报警器" || device.Name == "人体感应器")
{
var state = BitConverter.ToInt16(ms.Read(2));
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Warning, state == 1 ? "警报" : "正常", 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.UpdateData(deviceRepo, device, device.CreateData(Keys.ZoneType, zoneType, DeviceDataType.String, Keys.ZoneType, hidden: true, timestamp: timestamp));
//deviceRepo.SaveChanges();
//var deviceDto = device.To<EditDeviceModel>();
//this.SendToServer(Methods.EditDevice, deviceDto);
if (deviceId == 0x00a)
{
}
this.UpdateStatus(device);
}
else
{
this._logger.LogDebug($"unknown device id :{deviceId}");
}
}
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(Device device)
{
try
{
var deviceId = Convert.ToInt32(device.GetDataValue(Keys.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.X8D07020000(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, byte status = 1, byte? ep = null)
{
var list = new List<byte>
{
status,0x06,0x01,0x02,0x03,0x04,0x05,0x06
};
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<Device>>();
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
if (device != null)
{
Data deviceData = null;
var switchState = ms.ReadInt();
if (device.Name.Contains("二路开关") || device.Name.Contains("三路开关"))
{
if (endpoint == 0x10)
{
deviceData = device.CreateData(Keys.L1State, switchState == 0 ? "关" : "开", DeviceDataType.String, "L1状态", timestamp: timestamp);
}
else if (endpoint == 0x11)
{
deviceData = device.CreateData(Keys.L2State, switchState == 0 ? "关" : "开", DeviceDataType.String, "L2状态", timestamp: timestamp);
}
else if (endpoint == 0x12)
{
deviceData = device.CreateData(Keys.L3State, switchState == 0 ? "关" : "开", DeviceDataType.String, "L3状态", timestamp: timestamp);
}
}
else
{
deviceData = device.CreateData(Keys.State, switchState == 0 ? "关" : (switchState == 1 ? "开" : "停"), DeviceDataType.String, "状态", timestamp: timestamp);
}
if (deviceData != null)
{
this.UpdateData(deviceRepo, device, deviceData);
}
}
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<Device>>();
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
if (device != null)
{
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Brightness, ms.ReadByte(), DeviceDataType.Int, "亮度", timestamp: timestamp));
}
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<Device>>();
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
if (device != null)
{
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Hue, ms.ReadByte(), DeviceDataType.Int, "色调", timestamp: timestamp));
}
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<Device>>();
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
if (device != null)
{
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Saturation, ms.ReadByte(), DeviceDataType.Int, "饱和度", timestamp: timestamp));
}
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属性为0000的电量值
/// </summary>
public void X8D07020000(string sn, string ieee)
{
this.Write(sn, RequestType.xa7, ieee, new List<byte>() { 0x02, 0x07, 0x00, 0x00, 0x00 }, 2);
this.Write(sn, RequestType.xa7, ieee, new List<byte>() { 0x02, 0x07, 0x0b, 0x05, 0x00 }, 2);
}
//
//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.GetService<IRepository<Device>>();
var device = deviceRepo.Table()
.Include(o => o.Data)
.FirstOrDefault(o => o.Number == sn);
if (device == null)
{
this._logger.LogDebug($"gateway {sn} hasn't save in database");
}
else
{
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Version, version, DeviceDataType.String, "版本", timestamp: timestamp));
this.UpdateData(deviceRepo, device, device.CreateData(Keys.DeviceCount, ms.ReadByte(), DeviceDataType.Int, "设备数量", timestamp: timestamp));
var userName = ms.ReadASIIString(20);
var password = ms.ReadASIIString(20);
device.UserName = userName;
device.Password = password;
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<Device>>();
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
if (device != null)
{
this.UpdateData(deviceRepo, device, device.CreateData(Keys.ColorTemperature, ms.ReadInt(), DeviceDataType.Int, "色温", timestamp: timestamp));
}
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<Device>>();
var number = $"{sn}-{ieee}";
var device = deviceRepo.Table().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
var version = device.GetDataValue(Keys.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<Device>>();
var number = $"{sn}-{ieee}";
var device = repo.ReadOnlyTable().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
var version = device.GetDataValue(Keys.Version);
list.AddRange(version.HexToBytes());
}
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);
}
//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<Device>>();
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
var clusterId = (ClusterId)ms.ReadInt();
if (device != null)
{
var deviceId = Convert.ToInt32(device.GetDataValue(Keys.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)
{
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)//离网
{
DeleteDevice(deviceRepo, device);
}
else if (status == 0x03)//入网
{
}
}
else if (clusterId == ClusterId.light)
{
var light = BitConverter.ToInt16(props[0x0000]);
var desc = this.GetDescription(Keys.Light, light);
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Light, light, DeviceDataType.Int, "光照度", "lux", desc, timestamp: timestamp));
}
else if (clusterId == ClusterId.alarm)
{
var state = BitConverter.ToInt16(props[0x0080]);
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Warning, state == 1 ? "警报" : "正常", DeviceDataType.String, "状态", timestamp: timestamp));
//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.UpdateData(deviceRepo, device, device.CreateData(Keys.PM25, pm2d5, DeviceDataType.Int, "PM2.5", "μg/m³", this.GetDescription(Keys.PM25, pm2d5), timestamp: timestamp));
this.UpdateData(deviceRepo, device, device.CreateData(Keys.PM100, pm1d0, DeviceDataType.Int, "PM10", "μg/m³", this.GetDescription(Keys.PM100, pm1d0), timestamp: timestamp));
this.UpdateData(deviceRepo, device, device.CreateData(Keys.PM10, pm10, DeviceDataType.Int, "PM1.0", "μg/m³"));
}
else if (clusterId == ClusterId.temperature)
{
var temperature = BitConverter.ToInt16(props[0x0000]) / 100f;
var desc = this.GetDescription(Keys.Temperature, temperature);
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Temperature, temperature, DeviceDataType.Float, "温度", "℃", desc, timestamp: timestamp));
}
else if (clusterId == ClusterId.humidity)
{
var humidity = BitConverter.ToInt16(props[0x0000]) / 100f;
var desc = this.GetDescription(Keys.Humidity, humidity);
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Humidity, humidity, DeviceDataType.Float, "湿度", "RH%", desc, timestamp: timestamp));
}
else if (clusterId == ClusterId.voltage)
{
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Voltage, props[0x21][0] / 2f, DeviceDataType.Float, "电量", hidden: true, timestamp: timestamp));
}
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.UpdateData(deviceRepo, device, device.CreateData(Keys.Electricity, electricity, DeviceDataType.Float, "电量", "kWh", timestamp: timestamp));
}
}
else if (clusterId == ClusterId.doorlock)
{
}
if (deviceId == 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.UpdateData(deviceRepo, device, device.CreateData(Keys.Version, irVersion, DeviceDataType.String, "红外版本", timestamp: timestamp));
}
}
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.UpdateData(deviceRepo, device, device.CreateData(Keys.KeyPress, keyCode, DeviceDataType.Int, "按键", timestamp: timestamp));
}
}
}
}
}
else if (deviceId == 0x0051)
{
foreach (var item in props)
{
if (item.Key == 0x0000)
{
var tempBytes = new List<byte>();
tempBytes.AddRange(item.Value);
tempBytes.Add(0x00);
tempBytes.Add(0x00);
if (tempBytes.Count == 8)
{
var electricity = BitConverter.ToInt64(tempBytes.ToArray()) / 10000f;
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Electricity, electricity.ToString("f2"), DeviceDataType.Float, "电量", "kWh", timestamp: timestamp));
}
else
{
this._logger.LogError($"data length must be 8 but now is {tempBytes.Count}");
}
}
else if (item.Key == 0x050b)
{
var power = BitConverter.ToUInt16(item.Value.ToArray());
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Power, power, DeviceDataType.Float, "功率", "W", timestamp: timestamp));
}
}
if (clusterId == ClusterId.SummationDivisor)
{
this.X8D07020000(sn, device.Number);
}
}
}
}
private static void DeleteDevice(IRepository<Device> deviceRepo, Device device)
{
try
{
var model = device.To<EditDeviceModel>();
deviceRepo.Delete(device);
deviceRepo.SaveChanges();
}
catch (Exception ex)
{
ex.PrintStack();
}
}
private string GetDescription(string key, short value)
{
if (key == Keys.Light)
{
return value < 300 ? "低" : (value > 500 ? "高" : "适中");
}
else if (key == Keys.PM25)
{
return value <= 35 ? "优" : (value < 75 ? "良" : "污染");
}
else if (key == Keys.PM100)
{
return value <= 50 ? "优" : (value < 75 ? "100" : "污染");
}
return null;
}
private string GetDescription(string key, float value)
{
if (key == Keys.Temperature)
{
return value < 16 ? "低" : (value > 28 ? "高" : "适中");
}
else if (key == Keys.Humidity)
{
return value < 30 ? "低" : (value > 80 ? "高" : "适中");
}
return null;
}
//0xb1
//0xb2
//0xc2
//0xc3
//0xc4
//0xc5
//0x75
#endregion api
private Device GetDevice(IRepository<Device> deviceRepo, string sn, string ieee)
{
var number = $"{sn}-{ieee}";
return deviceRepo.Table()
.Include(o => o.Data)
.FirstOrDefault(o => o.Number == number);
}
private Device GetDeviceByAddress(IRepository<Device> deviceRepo, string sn, string address)
{
return deviceRepo.Table()
.Include(o => o.Data)
.FirstOrDefault(o => o.Number.StartsWith(sn) && o.Data.Any(d => d.Key == Keys.Address && d.Value == address));
}
}
}