Files
laatzen/Common/Hardware/WaterMeter/eRegister/Common.Hardware.WaterMeter.eRegister/EregisterExtensions.cs
T

302 lines
10 KiB
C#

namespace Common.Hardware.WaterMeter.eRegister
{
using Common.Hardware.Ports;
using Common.Hardware.WaterMeter.eRegister.Features;
using Common.Hardware.WaterMeter.eRegister.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class EregisterExtensions
{
public static IEnumerable<KeyValuePair<string, byte[]>> BuildPams(this EregisterFeatures eregister)
{
var pamList = new SortedDictionary<bool, SortedDictionary<AuthLevel, ICollection<byte[]>>>();
var pamAttribute = default(PamAttribute);
var cmd = default(byte);
var bytes = default(byte[]);
var value = default(object);
var singleCmd = default(bool);
var authLevel = default(AuthLevel);
foreach (var eregisterProperty in typeof(EregisterFeatures).GetProperties())
{
pamAttribute = eregisterProperty.GetCustomAttribute<PamAttribute>();
if (pamAttribute is null)
{
continue;
}
value = eregisterProperty.GetValue(eregister);
if (value is null)
{
continue;
}
singleCmd = pamAttribute.IsSingleCmd;
if (!pamList.ContainsKey(singleCmd))
{
pamList[singleCmd] = new SortedDictionary<AuthLevel, ICollection<byte[]>>();
}
authLevel = pamAttribute.AuthLevel;
if (!pamList[singleCmd].ContainsKey(authLevel))
{
pamList[singleCmd][authLevel] = new List<byte[]>();
}
if (eregisterProperty.Name == nameof(eregister.Debug))
{
var debugValue = default(object);
var debugBytes = new List<byte[]>();
foreach (var debugProperty in typeof(EregisterDebugFeatures).GetProperties())
{
debugValue = debugProperty.GetValue(value);
if (debugValue is null)
{
continue;
}
if (debugValue is Pam pam)
{
bytes = pam.ToByteArray();
debugBytes.Add(bytes);
}
}
foreach (var _bytes in debugBytes.OrderByDescending(x => x[2]))
{
pamList[singleCmd][authLevel].Add(_bytes);
}
}
else if (value is Pam pam)
{
bytes = pam.ToByteArray();
cmd = bytes[0];
pamList[singleCmd][authLevel].Add(bytes);
}
}
return pamList
.SelectMany(isSingle => isSingle.Key == false
// Multi pams
? isSingle.Value
.Select(auth => new
{
Name = $"{string.Join(", ", auth.Value.OrderBy(x => x[0]).Select(x => $"{x[0]}"))}",
Cmd = auth.Value.OrderBy(x => x[0]).SelectMany(x => x).ToList(),
AuthLvl = auth.Key.AuthBytes()
})
// Single pams
: isSingle.Value
.SelectMany(auth => auth.Value
.OrderByDescending(x => x[0])
.Select(x => new
{
Name = $"{x[0]}{(x[0] == 31 ? $"-{x[2]}" : "")}",
Cmd = x.ToList(),
AuthLvl = auth.Key.AuthBytes()
})))
.Select(x =>
{
x.Cmd.AddRange(x.AuthLvl);
return new KeyValuePair<string, byte[]>(x.Name, x.Cmd.ToArray());
});
}
public static IEnumerable<KeyValuePair<string, byte[]>> BuildPamsFull(this EregisterFeatures eregister)
{
var pamList = new List<KeyValuePair<string, byte[]>>();
var debugList = new List<KeyValuePair<string, byte[]>>();
var pamNames = new Dictionary<byte, string>();
var pamAttribute = default(PamAttribute);
var cmd = default(byte);
var bytes = default(byte[]);
var value = default(object);
foreach (var field in typeof(Pam).GetFields(BindingFlags.Public | BindingFlags.Static))
{
value = field.GetValue(null);
if (value is byte cmdByte)
{
pamNames[cmdByte] = field.Name;
}
}
foreach (var eregisterProperty in typeof(EregisterFeatures).GetProperties())
{
pamAttribute = eregisterProperty.GetCustomAttribute<PamAttribute>();
if (pamAttribute is null)
{
continue;
}
value = eregisterProperty.GetValue(eregister);
if (value is null)
{
continue;
}
if (eregisterProperty.Name == nameof(eregister.Debug))
{
var debugValue = default(object);
foreach (var debugProperty in typeof(EregisterDebugFeatures).GetProperties())
{
debugValue = debugProperty.GetValue(value);
if (debugValue is null)
{
continue;
}
if (debugValue is Pam pam)
{
pam.Append(new ProvidePin { AuthLevel = pamAttribute.AuthLevel });
bytes = pam.ToByteArray();
cmd = bytes[0];
if (pamNames.TryGetValue(cmd, out var name))
{
debugList.Add(new KeyValuePair<string, byte[]>($"{cmd} - {debugProperty.Name}", bytes));
}
}
}
}
else if (value is Pam pam)
{
pam.Append(new ProvidePin { AuthLevel = pamAttribute.AuthLevel });
bytes = pam.ToByteArray();
cmd = bytes[0];
if (pamNames.TryGetValue(cmd, out var name))
{
pamList.Add(new KeyValuePair<string, byte[]>($"{cmd} - {eregisterProperty.Name}", bytes));
}
}
}
var sortedPams = pamList
.OrderBy(x => x.Value[0])
.ToList();
var sortedDebugs = debugList
.OrderByDescending(x => x.Value[2])
.ToList();
sortedPams.AddRange(sortedDebugs);
return sortedPams;
}
public static byte[] AuthBytes(this AuthLevel authLevel)
{
var bytes = new byte[] { };
if (authLevel != AuthLevel.O)
{
var _bytes = ((long)authLevel).GetBytesLE();
Array.Resize(ref bytes, 6);
Array.Copy(_bytes, 0, bytes, 0, 6);
}
return bytes;
}
public static byte[] DecryptResponse(this byte[] encryptedData, byte[] encryptionKey)
{
if (encryptionKey?.Length != 16)
{
return encryptedData;
}
var length = encryptedData?.Length ?? 0;
if (length == 0)
{
return encryptedData;
}
var decryptedData = new byte[length];
Array.Copy(encryptedData, 0, decryptedData, 0, length);
var token = decryptedData[1];
for (var x = 6; x < length; x++)
{
token += (byte)(decryptedData[x - (token & 0x3) - 1] >> ((token >> 6) & 0x1));
decryptedData[x] ^= encryptionKey[(token >> 2) & 0x0F];
if (token >= 0x80)
{
decryptedData[x] = (byte)~decryptedData[x];
}
}
return decryptedData;
}
public static byte[] EncryptRequest(this byte[] plainData, byte[] encryptionKey, uint address)
{
if (encryptionKey?.Length != 16 || address <= 0)
{
return plainData;
}
var dataLength = plainData.Length;
var telegramLength = dataLength + 2;
var encryptedData = new byte[telegramLength];
Array.Copy(plainData, 0, encryptedData, 0, dataLength);
var telegramForCRCLength = telegramLength + 4;
var telegramForCRC = new byte[telegramForCRCLength];
telegramForCRC[0] = (byte)(address >> 24);
telegramForCRC[1] = (byte)(address >> 16);
telegramForCRC[2] = (byte)(address >> 8);
telegramForCRC[3] = (byte)(address >> 0);
Array.Copy(encryptedData, 0, telegramForCRC, 4, telegramLength);
var telegramCRC = telegramForCRC.CRCCCITT(1, telegramForCRCLength - 2);
encryptedData[telegramLength - 2] = (byte)(telegramCRC >> 8);
encryptedData[telegramLength - 1] = (byte)(telegramCRC >> 0);
var reference = address;
var token = (byte)0xFF;
for (var i = 0; i < telegramLength; i++)
{
token += (byte)(((reference >> ((token & 0x3) << 3)) & 0xFF) >> ((token >> 6) & 1));
reference <<= 8;
reference += encryptedData[i];
encryptedData[i] ^= encryptionKey[(token >> 2) & 0x0F];
if (token >= 0x80)
{
encryptedData[i] = (byte)~encryptedData[i];
}
}
return encryptedData;
}
}
}