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/labs/LCDAUE800DDebug/LCDAUE800DService.cs

496 lines
20 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 System;
using System.Collections.Generic;
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 ConsoleApp3
{
public class LCDAUE800DService
{
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<string, string> Keys = new Dictionary<string, string> {
{ "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<byte> _frame = new List<byte>();
private int _frameLength = 0;
private TcpClient _client;
private string _mac;
public LCDAUE800DService(TcpClient client)
{
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();
Console.WriteLine($"from {_client.Client.RemoteEndPoint}:{rawData.Length}");
UnitHandle(stream, rawData);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
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)
{
Console.WriteLine("单个包");
Handle(stream, data);
}
else if (length > unitLength)
{
Console.WriteLine("多个包");
Handle(stream, data.Take(unitLength).ToArray());
UnitHandle(stream, data.Skip(unitLength).ToArray());
}
else if (length < unitLength)
{
Console.WriteLine("长包开头");
_frame.Clear();
_frame.AddRange(data);
_frameLength = unitLength;
}
}
else
{
if (_frame.Count + length == _frameLength)
{
Console.WriteLine("长包结尾");
_frame.AddRange(data);
Handle(stream, _frame.ToArray());
_frame.Clear();
}
else if (_frame.Count + length > _frameLength)
{
Console.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)
{
Console.WriteLine("长包中间");
_frame.AddRange(data);
}
}
}
private void Handle(NetworkStream stream, byte[] rawData)
{
var xml = GetXml(rawData);
Console.WriteLine("收到客户端的消息:");
Console.WriteLine(FormatXml(xml));
var type = GetTypeFromXml(xml);
Console.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 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();
Console.WriteLine($"factory:{factory},hardware{hardware},software:{software},mac{mac}");
//Console.WriteLine($"楼宇建筑编码:{building_id},行政区域:{buildArea},建筑类型:{buildType},建筑名称:{buildName}");
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);
//set
//var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim();
//doc.SelectSingleNode("//building_id").InnerText = building_id;
//var gateway_id = doc.SelectSingleNode("//gateway_id").InnerText.Trim();
//var doc2 = new XmlDocument();
//doc2.PreserveWhitespace = true;
//var xml2 = @$"<?xml version=""1.0"" encoding=""UTF-8"" ?><root><common><building_id>{building_id}</building_id><gateway_id>{gateway_id}</gateway_id><type>period</type></common><config operation=""period""><period>1</period></config></root>";
//doc2.LoadXml(xml2);
//Reply(stream, doc2.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 = @$"<?xml version=""1.0"" encoding=""UTF-8"" ?><root><common><building_id>{building_id}</building_id><gateway_id>{gateway_id}</gateway_id><type>report_ack</type></common><report_config operation=""report_ack""><report_ack>pass</report_ack></report_config></root>";
// 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;
var building_id = doc.SelectSingleNode("//building_id").InnerText.Trim();
var gateway_id = doc.SelectSingleNode("//gateway_id").InnerText.Trim();
var time = doc.SelectSingleNode("//time").InnerText;
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 deviceNumber = $"lcdaue800d:{equipidex}";
var functions = meter.SelectNodes("//function");
foreach (XmlNode function in functions)
{
var key = function.Attributes["id"].Value.Trim();
var value = Keys[key];
Console.WriteLine($"type:{type},time:{time},devicd number:{deviceNumber},item:{value},value:{function.InnerText.Trim()}");
}
}
//上传数据响应
var doc2 = new XmlDocument();
var xml2 = @$"<?xml version=""1.0"" encoding=""UTF-8"" ?><root><common><building_id>{building_id}</building_id><gateway_id>{gateway_id}</gateway_id><type>report_ack</type></common><report_config operation=""report_ack""><report_ack>pass</report_ack></report_config></root>";
doc2.LoadXml(xml2);
Reply(stream, doc2.OuterXml);
}
private object GetDeviceType(string value)
{
throw new NotImplementedException();
}
private void Reply(NetworkStream stream, string xml)
{
Console.WriteLine("服务端响应:");
Console.WriteLine(FormatXml(xml));
Console.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 static 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(">"))
{
Console.WriteLine("xml format error");
result += "></root>";
}
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 static string FormatXml(string xml)
{
try
{
var doc = new XmlDocument();
doc.LoadXml(xml);
var xdoc = XDocument.Parse(doc.OuterXml);
return xdoc.ToString();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return xml;
}
}
}
}