302 lines
10 KiB
C#
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;
|
|
}
|
|
}
|
|
} |