From fd5a5f4918b4afa5b805999fdfea86739dcc52c2 Mon Sep 17 00:00:00 2001 From: wanggang <76527413@qq.com> Date: Thu, 18 Mar 2021 17:26:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9B=E5=88=9B=E9=9B=86=E6=8A=84=E5=99=A8?= =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Former-commit-id: 562cd7dd5ac3113417bf1edbf0d88ea6d309bb45 Former-commit-id: 6b8f5343619959bee6ff24e2c5042bc17ab58d94 --- .../DeviceServices/LiChuang/ClientWraper.cs | 608 ++++++++++++++++++ .../LiChuang/LCDAUE800DService.cs | 603 +---------------- projects/IoTNode/Startup.cs | 6 +- 3 files changed, 615 insertions(+), 602 deletions(-) create mode 100644 projects/IoTNode/DeviceServices/LiChuang/ClientWraper.cs diff --git a/projects/IoTNode/DeviceServices/LiChuang/ClientWraper.cs b/projects/IoTNode/DeviceServices/LiChuang/ClientWraper.cs new file mode 100644 index 00000000..fc215c2f --- /dev/null +++ b/projects/IoTNode/DeviceServices/LiChuang/ClientWraper.cs @@ -0,0 +1,608 @@ +using Application.Domain.Entities; +using Infrastructure.Data; +using Infrastructure.Extensions; +using IoT.Shared.Application.Models; +using IoT.Shared.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace IoTNode.DeviceServices.LiChuang +{ + public class ClientWraper + { + private static ushort[] crcTable = new ushort[]{ + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 + }; + + private static Dictionary Keys = new Dictionary { + { "1","有功电度"}, + { "2","有功功率"}, + { "3","无功电度"}, + { "4","无功功率"}, + { "5","A相电压"}, + { "6","B相电压"}, + { "7","C相电压"}, + { "8","A相电流"}, + { "9","B相电流"}, + { "10","C相电流"}, + { "11","功率因数"}, + { "12","A相功率"}, + { "13","B相功率"}, + { "14","C相功率"}, + { "15","水燃气当前流量"}, + { "16","结算日热量"}, + { "17","当前热量"}, + { "18","热功率"}, + { "19","瞬时流量"}, + { "20","当前累计流量"}, + { "21","供水温度"}, + { "22","回水温度"}, + { "23","累计时间"}, + { "24","累计时间"}, + { "25","自定义2"}, + { "26","自定义3"} + }; + + public readonly IServiceProvider _applicationServices; + private readonly BaseDeviceService _service; + private List _frame = new List(); + private int _frameLength = 0; + private TcpClient _client; + private string _mac; + + public ClientWraper(IServiceProvider applicationServices, BaseDeviceService service, TcpClient client) + { + this._applicationServices = applicationServices; + this._service = service; + this._client = client; + } + + public void Run() + { + var stream = _client.GetStream(); + byte[] buffer = new byte[10240]; + int bufferLength; + while (_client.Connected) + { + if (stream.DataAvailable) + { + try + { + bufferLength = stream.Read(buffer); + var rawData = buffer.Take(bufferLength).ToArray(); + Debug.WriteLine($"from {_client.Client.RemoteEndPoint}:{rawData.Length}"); + UnitHandle(stream, rawData); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + else + { + try + { + stream.Write(buffer, 0, 0); + } + catch + { + break; + } + } + } + } + + private void UnitHandle(NetworkStream stream, byte[] data) + { + var start = BitConverter.ToString(data.Take(4).ToArray()).Replace("-", "").ToLower(); + var length = data.Length; + if (start == "aa55aa55") + { + var unitLength = 4 + 4 + BitConverter.ToInt32(data.Skip(4).Take(4).ToArray()) + 2 + 4; + if (length == unitLength) + { + Debug.WriteLine("单个包"); + Handle(stream, data); + } + else if (length > unitLength) + { + Debug.WriteLine("多个包"); + Handle(stream, data.Take(unitLength).ToArray()); + UnitHandle(stream, data.Skip(unitLength).ToArray()); + } + else if (length < unitLength) + { + Debug.WriteLine("长包开头"); + _frame.Clear(); + _frame.AddRange(data); + _frameLength = unitLength; + } + } + else + { + if (_frame.Count + length == _frameLength) + { + Debug.WriteLine("长包结尾"); + _frame.AddRange(data); + Handle(stream, _frame.ToArray()); + _frame.Clear(); + } + else if (_frame.Count + length > _frameLength) + { + Debug.WriteLine("长包结尾+后续包"); + _frame.AddRange(data.Take(length - _frame.Count)); + Handle(stream, _frame.ToArray()); + Handle(stream, data.Skip(length - _frame.Count).ToArray()); + } + else if (_frame.Count + length < _frameLength) + { + Debug.WriteLine("长包中间"); + _frame.AddRange(data); + } + } + } + + private void Handle(NetworkStream stream, byte[] rawData) + { + var xml = GetXml(rawData); + Debug.WriteLine("收到客户端的消息"); + Debug.WriteLine(FormatXml(xml)); + var type = GetTypeFromXml(xml); + Debug.WriteLine($"type:{type}"); + if (type == "request") + { + HandleRequest(stream, xml); + } + else if (type == "md5") + { + HandleMd5(stream, xml); + } + else if (type == "device") + { + HandleDevice(stream, xml); + } + else if (type == "notify") + { + HandleNotify(stream, xml); + } + else if (type == "report") + { + HandleReport(stream, xml); + } + else if (type == "continuous") + { + HandleReport(stream, xml); + } + } + + private void HandleRequest(NetworkStream stream, string xml) + { + var command = "sequence"; + var operation = "operation"; + var doc = new XmlDocument(); + doc.PreserveWhitespace = true; + doc.LoadXml(xml); + doc.SelectSingleNode("/root/common/type").InnerText = command; + var idValidate = doc.SelectSingleNode("/root/id_validate"); + idValidate.Attributes[operation].Value = command; + var sequence = doc.CreateElement(command); + sequence.InnerText = "01234567"; + idValidate.AppendChild(sequence); + Reply(stream, doc.OuterXml); + } + + private void HandleMd5(NetworkStream stream, string xml) + { + var command = "md5"; + var operation = "operation"; + var doc = new XmlDocument(); + doc.PreserveWhitespace = true; + doc.LoadXml(xml); + var value1 = doc.SelectSingleNode($"/root/id_validate/{command}").InnerText; + using var md5 = MD5.Create(); + var value2 = BitConverter.ToString(md5.ComputeHash(Encoding.ASCII.GetBytes("01234567IJKLMNOPQRSTUVWX"))).Replace("-", "").ToLower(); + doc.SelectSingleNode("/root/common/type").InnerText = command; + var idValidate = doc.SelectSingleNode("/root/id_validate"); + idValidate.Attributes[operation].Value = "result"; + var result = doc.CreateElement("result"); + result.InnerText = value1 == value2 ? "pass" : "fail"; + idValidate.ReplaceChild(result, doc.SelectSingleNode($"/root/id_validate/{command}")); + Reply(stream, doc.OuterXml); + } + + private void HandleDevice(NetworkStream stream, string xml) + { + var command = "device_ack"; + var operation = "operation"; + var doc = new XmlDocument(); + doc.PreserveWhitespace = true; + doc.LoadXml(xml); + + //building_id //build_no + //var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim(); + //var buildArea = GetBuildName(building_id); + //var buildType = GetBuildType(building_id); + //var buildName = doc.SelectSingleNode("//build_name").InnerText.Trim(); + //var period = doc.SelectSingleNode("//period").InnerText.Trim(); + var mac = doc.SelectSingleNode("//mac").InnerText.Trim().Replace(":", ""); + var ip = doc.SelectSingleNode("//ip").InnerText.Trim().Replace(":", ""); + var period = doc.SelectSingleNode("//period").InnerText.Trim(); + var factory = doc.SelectSingleNode("//factory").InnerText.Trim(); + var hardware = doc.SelectSingleNode("//hardware").InnerText.Trim(); + var software = doc.SelectSingleNode("//software").InnerText.Trim(); + Debug.WriteLine($"factory:{factory},hardware:{hardware},software:{software},mac:{mac}"); + //Console.WriteLine($"楼宇建筑编码:{building_id},行政区域:{buildArea},建筑类型:{buildType},建筑名称:{buildName}"); + + //add for iotnode start + try + { + this._mac = mac; + var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + var product = this._service.UpdateProduct("LC集抄器", hardware, "", "collector"); + using var scope = _applicationServices.CreateScope(); + var iotNodeClient = scope.ServiceProvider.GetService(); + var deviceNodeRepo = scope.ServiceProvider.GetService>(); + var iotNode = deviceNodeRepo.ReadOnlyTable().FirstOrDefault(); + var deviceRepo = scope.ServiceProvider.GetService>(); + var device = deviceRepo.Table().FirstOrDefault(o => o.Number == mac); + if (device == null) + { + device = new IoTDevice + { + Name = "LC集抄器", + Number = mac, + DisplayName = "LC集抄器", + Icon = "collector", + Ip = ip, + IoTGatewayId = iotNode.Id, + IoTProductId = product.Id, + IsOnline = true, + }; + deviceRepo.Add(device); + deviceRepo.SaveChanges(); + this._service.UpdateIoTData(device.Id, DataKeys.Hardware, hardware); + this._service.UpdateIoTData(device.Id, DataKeys.Period, period); + } + } + catch (Exception ex) + { + ex.PrintStack(); + } + + //add for iotnode end + + doc.SelectSingleNode("/root/common/type").InnerText = command; + var node = doc.SelectSingleNode("/root/device"); + node.RemoveAll(); + var deviceOperation = doc.CreateAttribute(operation); + deviceOperation.Value = command; + node.Attributes.Append(deviceOperation); + var deviceAck = doc.CreateElement(command); + deviceAck.InnerText = "pass"; + node.AppendChild(deviceAck); + + Reply(stream, doc.OuterXml); + } + + //private static string GetBuildName(string building_id) + //{ + // try + // { + // var doc = new XmlDocument(); + // var file = Path.Combine(AppContext.BaseDirectory, "SDLC", "ZoneCode.xml"); + // doc.LoadXml(File.ReadAllText(file)); + // var value = doc.SelectSingleNode($"//ConfigData[Value={building_id.Substring(0, 6)}]/Value[2]").InnerText; + // return value; + // } + // catch (Exception ex) + // { + // Console.WriteLine(ex.ToString()); + // return building_id; + // } + //} + + //private static string GetBuildType(string building_id) + //{ + // try + // { + // var doc = new XmlDocument(); + // var file = Path.Combine(AppContext.BaseDirectory, "SDLC", "BuildingSet.xml"); + // doc.LoadXml(File.ReadAllText(file)); + // var value = doc.SelectSingleNode($"//ConfigData[Value={building_id.Skip(6).Take(1)}]/Value[1]").InnerText; + // return value; + // } + // catch (Exception ex) + // { + // Console.WriteLine(ex.ToString()); + // return building_id; + // } + //} + private void HandleNotify(NetworkStream stream, string xml) + { + var command = "time"; + var operation = "operation"; + var doc = new XmlDocument(); + doc.PreserveWhitespace = true; + doc.LoadXml(xml); + + doc.SelectSingleNode("/root/common/type").InnerText = command; + var node = doc.SelectSingleNode("/root/heart_beat"); + node.RemoveAll(); + var deviceOperation = doc.CreateAttribute(operation); + deviceOperation.Value = command; + node.Attributes.Append(deviceOperation); + + var deviceAck = doc.CreateElement(command); + deviceAck.InnerText = DateTime.Now.ToString("yyyyMMddHHmmss"); + node.AppendChild(deviceAck); + Reply(stream, doc.OuterXml); + } + + //private void HandleContinuous(NetworkStream stream, string xml) + //{ + // //续传数据处理 + // var doc = new XmlDocument(); + // doc.PreserveWhitespace = true; + // doc.LoadXml(xml); + // var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim(); + // var gateway_id = doc.SelectSingleNode("//gateway_id").InnerText.Trim(); + + // //续传数据响应 + // var doc2 = new XmlDocument(); + // var xml2 = @$"{building_id}{gateway_id}report_ackpass"; + // doc2.LoadXml(xml2); + // Reply(stream, doc2.OuterXml); + //} + private void HandleReport(NetworkStream stream, string xml) + { + //上传数据处理 + var doc = new XmlDocument(); + doc.PreserveWhitespace = true; + doc.LoadXml(xml); + + var type = doc.SelectSingleNode("//type").InnerText.Trim(); + var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim(); + var gateway_id = doc.SelectSingleNode("//gateway_id").InnerText.Trim(); + var time = doc.SelectSingleNode("//time").InnerText.Trim(); + + try + { + var meters = doc.SelectNodes("//meter"); + foreach (XmlNode meter in meters) + { + var deviceNo = meter.SelectSingleNode("//functionex"); + var equipidex = deviceNo.Attributes["equipidex"].Value.Trim(); + var tpex = deviceNo.Attributes["tpex"].Value.Trim(); + var functions = meter.SelectNodes("//function"); + foreach (XmlNode function in functions) + { + var key = function.Attributes["id"].Value.Trim(); + var name = Keys[key]; + var value = function.InnerText.Trim(); + Debug.WriteLine($"type:{type},time:{time},equipidex:{equipidex},item:{name},value:{value}"); + //add for iotnode start + if (!string.IsNullOrEmpty(this._mac)) + { + var timestamp = (type == "report" ? DateTimeOffset.Now : DateTime.ParseExact(time, "yyyyMMddHHmmss", CultureInfo.InvariantCulture)).ToUnixTimeMilliseconds(); + using var scope = _applicationServices.CreateScope(); + var deviceRepo = scope.ServiceProvider.GetService>(); + var dataRepo = scope.ServiceProvider.GetService>(); + var deviceId = this._service.GetIoTDeviceId(this._mac); + if (deviceId.HasValue) + { + var dataKey = GetKey(key); + var unit = this.GetUnit(name)?.ToString(); + var dataName = $"{dataKey.GetDisplayName():unit}"; + this._service.UpdateIoTData(deviceId.Value, dataKey, value, name: dataName, unit: unit); + } + } + //add for iotnode end + } + } + } + catch (Exception ex) + { + ex.PrintStack(); + } + //上传数据响应 + var doc2 = new XmlDocument(); + var xml2 = @$"{building_id}{gateway_id}report_ackpass"; + doc2.LoadXml(xml2); + Reply(stream, doc2.OuterXml); + } + + private DataKeys GetKey(string equipidex) + { + var value = $"Device{equipidex}"; + return Enum.Parse(value); + } + + private object GetUnit(string name) + { + if (name.Contains("有功功率")) + { + return "W"; + } + if (name.Contains("无功功率")) + { + return "Var"; + } + if (name.Contains("电压")) + { + return "V"; + } + if (name.Contains("电流")) + { + return "A"; + } + if (name.Contains("电能") || name.Contains("有功电度")) + { + return "千瓦时"; + } + if (name.Contains("无功电度")) + { + return "Kvarh"; + } + if (name.Contains("流量")) + { + return "立方米"; + } + return null; + } + + private object GetDeviceType(string value) + { + throw new NotImplementedException(); + } + + private void Reply(NetworkStream stream, string xml) + { + Debug.WriteLine("服务端响应:"); + Debug.WriteLine(FormatXml(xml)); + Debug.WriteLine("服务端响应:"); + using var ms = new MemoryStream(); + ms.Write(new byte[] { 0xaa, 0x55, 0xaa, 0x55 });//header + var data = new byte[] { 0x00, 0x00, 0x00, 0x00 }.Concat(AesEncrypt(Encoding.UTF8.GetBytes(xml))).ToArray(); + ms.Write(BitConverter.GetBytes(data.Length));//length + ms.Write(data);//data + ms.Write(BitConverter.GetBytes((ushort)XmlGetCrc16(data)));//crc + ms.Write(new byte[] { 0x68, 0x68, 0x16, 0x16 }); + var response = ms.ToArray(); + stream.Write(response); + stream.Flush(); + } + + private static string GetTypeFromXml(string xml) + { + var doc = new XmlDocument(); + doc.LoadXml(xml); + return doc.SelectSingleNode("/root/common/type").InnerText; + } + + private string GetXml(byte[] rawData) + { + var dataLength = BitConverter.ToUInt32(rawData.Skip(4).Take(4).ToArray()); + var data = rawData.Skip(8).Take((int)dataLength).ToArray(); + var xmlData = data.Skip(4).ToArray(); + var crc = BitConverter.ToUInt16(rawData.Skip(8 + (int)dataLength).Take(2).ToArray()); + var crc2 = XmlGetCrc16(data); + if (crc == crc2) + { + var result = Encoding.UTF8.GetString(AesDecrypt(xmlData)); + result = result.Trim(); + if (!result.EndsWith(">")) + { + Debug.WriteLine("xml format error"); + result += ">"; + } + return result; + } + throw new Exception("crc error"); + } + + private static byte[] HexStringToByteArray(string hex) + { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + private static uint XmlGetCrc16(byte[] data) + { + uint crc = 0, by; + for (int i = 0; i < data.Length; i++) + { + by = (crc >> 8) & 0xff; + crc = (crc & 0xffff) << 8; + crc = (crc ^ crcTable[(data[i] ^ by) & 0xff]) & 0xffff; + } + return crc; + } + + private static byte[] AesEncrypt(byte[] plainText) + { + using var aes = CreateAes(); + using var encryptor = aes.CreateEncryptor(); + return encryptor.TransformFinalBlock(plainText, 0, plainText.Length); + } + + private static byte[] AesDecrypt(byte[] cipherText) + { + using var aes = CreateAes(); + using var decryptor = aes.CreateDecryptor(); + return decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length); + } + + private static Aes CreateAes() + { + var key = HexStringToByteArray("0102030405060708090a0b0c0d0e0f10"); + var iv = HexStringToByteArray("0102030405060708090a0b0c0d0e0f10"); + var aesAlg = Aes.Create(); + aesAlg.Mode = CipherMode.CBC; + aesAlg.Padding = PaddingMode.Zeros; + aesAlg.BlockSize = 128; + aesAlg.Key = key; + aesAlg.IV = iv; + return aesAlg; + } + + private string FormatXml(string xml) + { + try + { + var doc = new XmlDocument(); + doc.LoadXml(xml); + var xdoc = XDocument.Parse(doc.OuterXml); + return xdoc.ToString(); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + return xml; + } + } + } +} \ No newline at end of file diff --git a/projects/IoTNode/DeviceServices/LiChuang/LCDAUE800DService.cs b/projects/IoTNode/DeviceServices/LiChuang/LCDAUE800DService.cs index 1b1d061c..6c63c42c 100644 --- a/projects/IoTNode/DeviceServices/LiChuang/LCDAUE800DService.cs +++ b/projects/IoTNode/DeviceServices/LiChuang/LCDAUE800DService.cs @@ -1,111 +1,22 @@ -using Application.Domain.Entities; -using Infrastructure.Data; -using Infrastructure.Extensions; -using IoT.Shared.Application.Models; -using IoT.Shared.Services; +using Infrastructure.Extensions; using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; using System.Net; using System.Net.Sockets; -using System.Security.Cryptography; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; namespace IoTNode.DeviceServices.LiChuang { public class LCDAUE800DService : BaseDeviceService { - private static ushort[] crcTable = new ushort[]{ - 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, - 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, - 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, - 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, - 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, - 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, - 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, - 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, - 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, - 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, - 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, - 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, - 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, - 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, - 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, - 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, - 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, - 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, - 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, - 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, - 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, - 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, - 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, - 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, - 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, - 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, - 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, - 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, - 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, - 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, - 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, - 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 - }; - - private static Dictionary Keys = new Dictionary { - { "1","有功电度"}, - { "2","有功功率"}, - { "3","无功电度"}, - { "4","无功功率"}, - { "5","A相电压"}, - { "6","B相电压"}, - { "7","C相电压"}, - { "8","A相电流"}, - { "9","B相电流"}, - { "10","C相电流"}, - { "11","功率因数"}, - { "12","A相功率"}, - { "13","B相功率"}, - { "14","C相功率"}, - { "15","水燃气当前流量"}, - { "16","结算日热量"}, - { "17","当前热量"}, - { "18","热功率"}, - { "19","瞬时流量"}, - { "20","当前累计流量"}, - { "21","供水温度"}, - { "22","回水温度"}, - { "23","累计时间"}, - { "24","累计时间"}, - { "25","自定义2"}, - { "26","自定义3"} - }; - - private List _frame = new List(); - - private int _frameLength = 0; - - private TcpClient _client; - private TcpListener _listener; private CancellationToken _cancellationToken; - private string _mac; - private readonly ILogger _logger; - - public LCDAUE800DService(IServiceProvider applicationServices, IWebHostEnvironment env, ILogger logger) + public LCDAUE800DService(IServiceProvider applicationServices, IWebHostEnvironment env) : base(applicationServices, env) { - this._logger = logger; } public override Task StartAsync(CancellationToken cancellationToken) @@ -130,10 +41,10 @@ namespace IoTNode.DeviceServices.LiChuang { while (!this._cancellationToken.IsCancellationRequested) { - this._client = _listener.AcceptTcpClient(); + var client = _listener.AcceptTcpClient(); Task.Run(() => { - this.Run(); + new ClientWraper(_applicationServices, this, client).Run(); }); } } @@ -156,511 +67,5 @@ namespace IoTNode.DeviceServices.LiChuang this._env.Debug("lc end"); return base.StopAsync(cancellationToken); } - - public void Run() - { - var stream = _client.GetStream(); - byte[] buffer = new byte[10240]; - int bufferLength; - while (_client.Connected) - { - if (stream.DataAvailable) - { - try - { - bufferLength = stream.Read(buffer); - var rawData = buffer.Take(bufferLength).ToArray(); - this._env.Debug($"from {_client.Client.RemoteEndPoint}:{rawData.Length}"); - UnitHandle(stream, rawData); - } - catch (Exception ex) - { - this._env.Debug(ex); - } - } - else - { - try - { - stream.Write(buffer, 0, 0); - } - catch - { - break; - } - } - } - } - - private void UnitHandle(NetworkStream stream, byte[] data) - { - var start = BitConverter.ToString(data.Take(4).ToArray()).Replace("-", "").ToLower(); - var length = data.Length; - if (start == "aa55aa55") - { - var unitLength = 4 + 4 + BitConverter.ToInt32(data.Skip(4).Take(4).ToArray()) + 2 + 4; - if (length == unitLength) - { - this._env.Debug("单个包"); - Handle(stream, data); - } - else if (length > unitLength) - { - this._env.Debug("多个包"); - Handle(stream, data.Take(unitLength).ToArray()); - UnitHandle(stream, data.Skip(unitLength).ToArray()); - } - else if (length < unitLength) - { - this._env.Debug("长包开头"); - _frame.Clear(); - _frame.AddRange(data); - _frameLength = unitLength; - } - } - else - { - if (_frame.Count + length == _frameLength) - { - this._env.Debug("长包结尾"); - _frame.AddRange(data); - Handle(stream, _frame.ToArray()); - _frame.Clear(); - } - else if (_frame.Count + length > _frameLength) - { - this._env.Debug("长包结尾+后续包"); - _frame.AddRange(data.Take(length - _frame.Count)); - Handle(stream, _frame.ToArray()); - Handle(stream, data.Skip(length - _frame.Count).ToArray()); - } - else if (_frame.Count + length < _frameLength) - { - this._env.Debug("长包中间"); - _frame.AddRange(data); - } - } - } - - private void Handle(NetworkStream stream, byte[] rawData) - { - var xml = GetXml(rawData); - this._env.Debug("收到客户端的消息"); - this._env.Debug(() => FormatXml(xml)); - var type = GetTypeFromXml(xml); - this._env.Debug($"type:{type}"); - if (type == "request") - { - HandleRequest(stream, xml); - } - else if (type == "md5") - { - HandleMd5(stream, xml); - } - else if (type == "device") - { - HandleDevice(stream, xml); - } - else if (type == "notify") - { - HandleNotify(stream, xml); - } - else if (type == "report") - { - HandleReport(stream, xml); - } - else if (type == "continuous") - { - HandleReport(stream, xml); - } - } - - private void HandleRequest(NetworkStream stream, string xml) - { - var command = "sequence"; - var operation = "operation"; - var doc = new XmlDocument(); - doc.PreserveWhitespace = true; - doc.LoadXml(xml); - doc.SelectSingleNode("/root/common/type").InnerText = command; - var idValidate = doc.SelectSingleNode("/root/id_validate"); - idValidate.Attributes[operation].Value = command; - var sequence = doc.CreateElement(command); - sequence.InnerText = "01234567"; - idValidate.AppendChild(sequence); - Reply(stream, doc.OuterXml); - } - - private void HandleMd5(NetworkStream stream, string xml) - { - var command = "md5"; - var operation = "operation"; - var doc = new XmlDocument(); - doc.PreserveWhitespace = true; - doc.LoadXml(xml); - var value1 = doc.SelectSingleNode($"/root/id_validate/{command}").InnerText; - using var md5 = MD5.Create(); - var value2 = BitConverter.ToString(md5.ComputeHash(Encoding.ASCII.GetBytes("01234567IJKLMNOPQRSTUVWX"))).Replace("-", "").ToLower(); - doc.SelectSingleNode("/root/common/type").InnerText = command; - var idValidate = doc.SelectSingleNode("/root/id_validate"); - idValidate.Attributes[operation].Value = "result"; - var result = doc.CreateElement("result"); - result.InnerText = value1 == value2 ? "pass" : "fail"; - idValidate.ReplaceChild(result, doc.SelectSingleNode($"/root/id_validate/{command}")); - Reply(stream, doc.OuterXml); - } - - private void HandleDevice(NetworkStream stream, string xml) - { - var command = "device_ack"; - var operation = "operation"; - var doc = new XmlDocument(); - doc.PreserveWhitespace = true; - doc.LoadXml(xml); - - //building_id //build_no - //var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim(); - //var buildArea = GetBuildName(building_id); - //var buildType = GetBuildType(building_id); - //var buildName = doc.SelectSingleNode("//build_name").InnerText.Trim(); - //var period = doc.SelectSingleNode("//period").InnerText.Trim(); - var mac = doc.SelectSingleNode("//mac").InnerText.Trim().Replace(":", ""); - var ip = doc.SelectSingleNode("//ip").InnerText.Trim().Replace(":", ""); - var period = doc.SelectSingleNode("//period").InnerText.Trim(); - var factory = doc.SelectSingleNode("//factory").InnerText.Trim(); - var hardware = doc.SelectSingleNode("//hardware").InnerText.Trim(); - var software = doc.SelectSingleNode("//software").InnerText.Trim(); - this._env.Debug($"factory:{factory},hardware:{hardware},software:{software},mac:{mac}"); - //Console.WriteLine($"楼宇建筑编码:{building_id},行政区域:{buildArea},建筑类型:{buildType},建筑名称:{buildName}"); - - //add for iotnode start - try - { - this._mac = mac; - var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - var product = this.UpdateProduct("LC集抄器", hardware, "", "collector"); - using var scope = _applicationServices.CreateScope(); - var iotNodeClient = scope.ServiceProvider.GetService(); - var deviceNodeRepo = scope.ServiceProvider.GetService>(); - var iotNode = deviceNodeRepo.ReadOnlyTable().FirstOrDefault(); - var deviceRepo = scope.ServiceProvider.GetService>(); - var device = deviceRepo.Table().FirstOrDefault(o => o.Number == mac); - if (device == null) - { - device = new IoTDevice - { - Name = "LC集抄器", - Number = mac, - DisplayName = "LC集抄器", - Icon = "collector", - Ip = ip, - IoTGatewayId = iotNode.Id, - IoTProductId = product.Id, - IsOnline = true, - }; - deviceRepo.Add(device); - deviceRepo.SaveChanges(); - this.UpdateIoTData(device.Id, DataKeys.Hardware, hardware); - this.UpdateIoTData(device.Id, DataKeys.Period, period); - } - } - catch (Exception ex) - { - ex.PrintStack(); - } - - //add for iotnode end - - doc.SelectSingleNode("/root/common/type").InnerText = command; - var node = doc.SelectSingleNode("/root/device"); - node.RemoveAll(); - var deviceOperation = doc.CreateAttribute(operation); - deviceOperation.Value = command; - node.Attributes.Append(deviceOperation); - var deviceAck = doc.CreateElement(command); - deviceAck.InnerText = "pass"; - node.AppendChild(deviceAck); - - Reply(stream, doc.OuterXml); - } - - //private static string GetBuildName(string building_id) - //{ - // try - // { - // var doc = new XmlDocument(); - // var file = Path.Combine(AppContext.BaseDirectory, "SDLC", "ZoneCode.xml"); - // doc.LoadXml(File.ReadAllText(file)); - // var value = doc.SelectSingleNode($"//ConfigData[Value={building_id.Substring(0, 6)}]/Value[2]").InnerText; - // return value; - // } - // catch (Exception ex) - // { - // Console.WriteLine(ex.ToString()); - // return building_id; - // } - //} - - //private static string GetBuildType(string building_id) - //{ - // try - // { - // var doc = new XmlDocument(); - // var file = Path.Combine(AppContext.BaseDirectory, "SDLC", "BuildingSet.xml"); - // doc.LoadXml(File.ReadAllText(file)); - // var value = doc.SelectSingleNode($"//ConfigData[Value={building_id.Skip(6).Take(1)}]/Value[1]").InnerText; - // return value; - // } - // catch (Exception ex) - // { - // Console.WriteLine(ex.ToString()); - // return building_id; - // } - //} - private void HandleNotify(NetworkStream stream, string xml) - { - var command = "time"; - var operation = "operation"; - var doc = new XmlDocument(); - doc.PreserveWhitespace = true; - doc.LoadXml(xml); - - doc.SelectSingleNode("/root/common/type").InnerText = command; - var node = doc.SelectSingleNode("/root/heart_beat"); - node.RemoveAll(); - var deviceOperation = doc.CreateAttribute(operation); - deviceOperation.Value = command; - node.Attributes.Append(deviceOperation); - - var deviceAck = doc.CreateElement(command); - deviceAck.InnerText = DateTime.Now.ToString("yyyyMMddHHmmss"); - node.AppendChild(deviceAck); - Reply(stream, doc.OuterXml); - } - - //private void HandleContinuous(NetworkStream stream, string xml) - //{ - // //续传数据处理 - // var doc = new XmlDocument(); - // doc.PreserveWhitespace = true; - // doc.LoadXml(xml); - // var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim(); - // var gateway_id = doc.SelectSingleNode("//gateway_id").InnerText.Trim(); - - // //续传数据响应 - // var doc2 = new XmlDocument(); - // var xml2 = @$"{building_id}{gateway_id}report_ackpass"; - // doc2.LoadXml(xml2); - // Reply(stream, doc2.OuterXml); - //} - private void HandleReport(NetworkStream stream, string xml) - { - //上传数据处理 - var doc = new XmlDocument(); - doc.PreserveWhitespace = true; - doc.LoadXml(xml); - - var type = doc.SelectSingleNode("//type").InnerText.Trim(); - var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim(); - var gateway_id = doc.SelectSingleNode("//gateway_id").InnerText.Trim(); - var time = doc.SelectSingleNode("//time").InnerText.Trim(); - - try - { - var meters = doc.SelectNodes("//meter"); - foreach (XmlNode meter in meters) - { - var deviceNo = meter.SelectSingleNode("//functionex"); - var equipidex = deviceNo.Attributes["equipidex"].Value.Trim(); - var tpex = deviceNo.Attributes["tpex"].Value.Trim(); - var functions = meter.SelectNodes("//function"); - foreach (XmlNode function in functions) - { - var key = function.Attributes["id"].Value.Trim(); - var name = Keys[key]; - var value = function.InnerText.Trim(); - this._logger.LogDebug($"type:{type},time:{time},equipidex:{equipidex},item:{name},value:{value}"); - //add for iotnode start - if (!string.IsNullOrEmpty(this._mac)) - { - var timestamp = (type == "report" ? DateTimeOffset.Now : DateTime.ParseExact(time, "yyyyMMddHHmmss", CultureInfo.InvariantCulture)).ToUnixTimeMilliseconds(); - using var scope = _applicationServices.CreateScope(); - var deviceRepo = scope.ServiceProvider.GetService>(); - var dataRepo = scope.ServiceProvider.GetService>(); - var deviceId = this.GetIoTDeviceId(this._mac); - if (deviceId.HasValue) - { - var dataKey = GetKey(key); - var unit = this.GetUnit(name)?.ToString(); - var dataName = $"{dataKey.GetDisplayName():unit}"; - this.UpdateIoTData(deviceId.Value, dataKey, value, name: dataName, unit: unit); - } - } - //add for iotnode end - } - } - } - catch (Exception ex) - { - this._logger.LogError(ex, ex.Message); - } - //上传数据响应 - var doc2 = new XmlDocument(); - var xml2 = @$"{building_id}{gateway_id}report_ackpass"; - doc2.LoadXml(xml2); - Reply(stream, doc2.OuterXml); - } - - private DataKeys GetKey(string equipidex) - { - var value = $"Device{equipidex}"; - return Enum.Parse(value); - } - - private object GetUnit(string name) - { - if (name.Contains("有功功率")) - { - return "W"; - } - if (name.Contains("无功功率")) - { - return "Var"; - } - if (name.Contains("电压")) - { - return "V"; - } - if (name.Contains("电流")) - { - return "A"; - } - if (name.Contains("电能") || name.Contains("有功电度")) - { - return "千瓦时"; - } - if (name.Contains("无功电度")) - { - return "Kvarh"; - } - if (name.Contains("流量")) - { - return "立方米"; - } - return null; - } - - private object GetDeviceType(string value) - { - throw new NotImplementedException(); - } - - private void Reply(NetworkStream stream, string xml) - { - this._env.Debug("服务端响应:"); - this._env.Debug(() => FormatXml(xml)); - this._env.Debug("服务端响应:"); - using var ms = new MemoryStream(); - ms.Write(new byte[] { 0xaa, 0x55, 0xaa, 0x55 });//header - var data = new byte[] { 0x00, 0x00, 0x00, 0x00 }.Concat(AesEncrypt(Encoding.UTF8.GetBytes(xml))).ToArray(); - ms.Write(BitConverter.GetBytes(data.Length));//length - ms.Write(data);//data - ms.Write(BitConverter.GetBytes((ushort)XmlGetCrc16(data)));//crc - ms.Write(new byte[] { 0x68, 0x68, 0x16, 0x16 }); - var response = ms.ToArray(); - stream.Write(response); - stream.Flush(); - } - - private static string GetTypeFromXml(string xml) - { - var doc = new XmlDocument(); - doc.LoadXml(xml); - return doc.SelectSingleNode("/root/common/type").InnerText; - } - - private string GetXml(byte[] rawData) - { - var dataLength = BitConverter.ToUInt32(rawData.Skip(4).Take(4).ToArray()); - var data = rawData.Skip(8).Take((int)dataLength).ToArray(); - var xmlData = data.Skip(4).ToArray(); - var crc = BitConverter.ToUInt16(rawData.Skip(8 + (int)dataLength).Take(2).ToArray()); - var crc2 = XmlGetCrc16(data); - if (crc == crc2) - { - var result = Encoding.UTF8.GetString(AesDecrypt(xmlData)); - result = result.Trim(); - if (!result.EndsWith(">")) - { - this._env.Debug("xml format error"); - result += ">"; - } - return result; - } - throw new Exception("crc error"); - } - - private static byte[] HexStringToByteArray(string hex) - { - return Enumerable.Range(0, hex.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) - .ToArray(); - } - - private static uint XmlGetCrc16(byte[] data) - { - uint crc = 0, by; - for (int i = 0; i < data.Length; i++) - { - by = (crc >> 8) & 0xff; - crc = (crc & 0xffff) << 8; - crc = (crc ^ crcTable[(data[i] ^ by) & 0xff]) & 0xffff; - } - return crc; - } - - private static byte[] AesEncrypt(byte[] plainText) - { - using var aes = CreateAes(); - using var encryptor = aes.CreateEncryptor(); - return encryptor.TransformFinalBlock(plainText, 0, plainText.Length); - } - - private static byte[] AesDecrypt(byte[] cipherText) - { - using var aes = CreateAes(); - using var decryptor = aes.CreateDecryptor(); - return decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length); - } - - private static Aes CreateAes() - { - var key = HexStringToByteArray("0102030405060708090a0b0c0d0e0f10"); - var iv = HexStringToByteArray("0102030405060708090a0b0c0d0e0f10"); - var aesAlg = Aes.Create(); - aesAlg.Mode = CipherMode.CBC; - aesAlg.Padding = PaddingMode.Zeros; - aesAlg.BlockSize = 128; - aesAlg.Key = key; - aesAlg.IV = iv; - return aesAlg; - } - - private string FormatXml(string xml) - { - try - { - var doc = new XmlDocument(); - doc.LoadXml(xml); - var xdoc = XDocument.Parse(doc.OuterXml); - return xdoc.ToString(); - } - catch (Exception ex) - { - this._env.Debug(ex); - return xml; - } - } } } \ No newline at end of file diff --git a/projects/IoTNode/Startup.cs b/projects/IoTNode/Startup.cs index 21c05464..3f7aa808 100644 --- a/projects/IoTNode/Startup.cs +++ b/projects/IoTNode/Startup.cs @@ -44,9 +44,9 @@ namespace IoTNode services.AddTransient(); if (Env.IsDevelopment()) { - services.AddHostedService(o => o.GetService()); - services.AddHostedService(o => o.GetService()); - services.AddHostedService(o => o.GetService()); + //services.AddHostedService(o => o.GetService()); + //services.AddHostedService(o => o.GetService()); + //services.AddHostedService(o => o.GetService()); services.AddHostedService(o => o.GetService()); } else