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.

608 lines
23 KiB

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