using Infrastructure.Extensions; using Infrastructure.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Sockets; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace LiChuangService { 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 _types = new Dictionary(); 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("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("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 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 tcpIp = this._configuration["tcp.ip"]; var tcpPort = this._configuration.GetValue("tcp.port"); if (this._tcpIp != tcpIp || this._tcpPort != tcpPort) { this._tcpIp = tcpIp; this._tcpPort = tcpPort; this.Connect(); } else { if (!this._tcpClient.Connected) { this.Connect(); } } } private void Connect() { if (this._tcpClient.Connected) { this._tcpClient.Close(); } try { this._tcpClient.Connect(this._tcpIp, this._tcpPort); new Thread(() => { while (this._tcpClient.Connected) { var stream = this._tcpClient.GetStream(); if (stream.CanRead) { var buffer = new byte[512]; var length = stream.Read(buffer); var data = buffer.ToList().Take(length).ToArray(); try { this.Handle(data); } catch (Exception ex) { ex.PrintStack(); } } } }).Start(); } catch (Exception ex) { ex.PrintStack(); } } private void SearchAddress() { var list = new List() { 0x0c }; var command = Command(list.ToArray()); this.Write(command); } private void Handle(byte[] data) { var list = new List(); for (int i = 0; i < data.Length; i++) { if ((i + 3) < data.Length && data[i] == 0xfe && data[i + 1] == 0xa5) { var length = data[i + 3]; this.HandleInternal(data.Skip(i).Take(5 + length).ToArray()); } } } private void HandleInternal(byte[] data) { Console.WriteLine($"read:{BitConverter.ToString(data).Replace("-", " ")}"); var type = data[2]; var command = data[4]; if (command == 0x0c)//16位地址 { this._address = this._address ?? data.ToList().Skip(5).Take(8).ToArray(); this._addressValue = BitConverter.ToString(this._address).Replace("-", ""); this.SearchNodeStatus(); } else if (command == 0x15)//节点状态 { var status = data.ToList().Skip(14).Take(100).ToArray(); for (int i = 0; i < 100; i++) { if (status[i] == 0x01) { Console.WriteLine($"node {i + 1} online"); this.SearchNodeType(i + 1); } else if (status[i] == 0x02) { Console.WriteLine($"node {i + 1} offline"); } } } else if (command == 0x17)//读取 { var nodeNumber = data[14]; if (data.Length == 22 && data[18] == 0xff)//读类型命令返回值 { var nodeType = data[17]; this._types[nodeNumber] = nodeType; if (nodeType == 0x02) { Console.WriteLine($"node {nodeNumber} is switch 2"); this.SearchSwitchStatus(nodeNumber, 0x02); } else if (nodeType == 0x03) { Console.WriteLine($"node {nodeNumber} is socket 1"); this.SearchSwitchStatus(nodeNumber, 0x10); } else if (nodeType == 0x04) { Console.WriteLine($"node {nodeNumber} is socket 2"); this.SearchSwitchStatus(nodeNumber, 0x10); } else { Console.WriteLine($"unknown type:{nodeType}"); } } if (data[15] == 0x01) { var nodeType = this._types[nodeNumber]; var statusData = data[17]; NotifyModel model = null; if (nodeType == 0x02) { Console.WriteLine($"~~~node {nodeNumber},switch 2 status:{statusData}"); var switchStatus = statusData.ToBitString(); model = CreateModel("二路灯开关", this._addressValue + "-" + nodeNumber, "switch2", "switch"); model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status1", Name = "L1状态", Value = switchStatus[7] == '1' ? "开" : "关", DisplayOrder = 1 }); model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status2", Name = "L2状态", Value = switchStatus[6] == '1' ? "开" : "关", DisplayOrder = 2 }); } else if (nodeType == 0x03) { Console.WriteLine($"~~~node {nodeNumber},socket 1 status:{statusData}"); model = CreateModel("一路插座", this._addressValue + "-" + nodeNumber, "socket1", "socket"); model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status", Name = "状态", Value = statusData == 0x01 ? "开" : "关", DisplayOrder = 1 }); } else if (nodeType == 0x04) { Console.WriteLine($"~~~node {nodeNumber},socket 2 status:{statusData}"); model = CreateModel("二路插座", this._addressValue + "-" + nodeNumber, "socket2", "switch"); model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status", Name = "状态", Value = statusData == 0x01 ? "开" : "关", DisplayOrder = 1 }); } else { Console.WriteLine($"unknown type:{nodeType}"); } if (model != null) { this.NotifyServer(model); } } } } private void SearchNodeStatus() { var list = new List() { 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() { 0x16 }; list.AddRange(this._address); list.Add(0x00); var modbusdata = new List { (byte)i,0x03,0x00,0x0f,0x00,0x01 }.ToArray(); var modbus = new List(); 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() { 0x16 }; list.AddRange(this._address); list.Add(0x00); var modbusdata = new List { (byte)i, 0x01,0x00,0x00,0x00,cmd }.ToArray(); var modbus = new List(); 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 { 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 { 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() { 0x16 }; list.AddRange(this._address); list.Add(0x00); var modbusdata = command; var modbus = new List(); 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 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(); } } }