added errormessages (in method MCI_TxRequest_RxResponse, for better NFC error analysis) CRC check included. If the meter CRC doesn't match the calculated CRC an error is returned. removed method MCI_Raw_Read/write (same as MCI_Read/Write, which autoselects the correct password) Refactoring more detailed errorcodes(no more unidentified, separated host (>=20) and Meter messages (0..8))
160 lines
4.6 KiB
C#
160 lines
4.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
//*****************************************************************************
|
|
// Copyright 2020 Sensus GmbH Ludwigshafen. All rights reserved.
|
|
// Author: Venkat, Rajeshwar
|
|
//*****************************************************************************
|
|
|
|
namespace Sensus.iPerl.NfcHandler
|
|
{
|
|
public static class Tools
|
|
{
|
|
public static bool ByteArrayCompare(byte[] a1, byte[] a2)
|
|
{
|
|
// thanks to https://stackoverflow.com/questions/43289/comparing-two-byte-arrays-in-net
|
|
if (a1.Length != a2.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < a1.Length; i++)
|
|
if (a1[i] != a2[i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
public static string SwapHex(string sHex)
|
|
{
|
|
string sResult = "";
|
|
int iPos = sHex.Length - 2;
|
|
while (iPos >= 0)
|
|
{
|
|
sResult += sHex.Substring(iPos, 2);
|
|
sHex = sHex.Remove(iPos, 2);
|
|
iPos = sHex.Length - 2;
|
|
}
|
|
return sResult;
|
|
}
|
|
public static string DecimalToHexString(string value, int size = 1)
|
|
{
|
|
try
|
|
{
|
|
long Lval = long.Parse(value);
|
|
if (size == 1) return Lval.ToString("X2");
|
|
if (size == 2) return Lval.ToString("X4");
|
|
if (size == 4) return Lval.ToString("X8");
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (size == 1) return "00";
|
|
if (size == 2) return "0000";
|
|
if (size == 4) return "00000000";
|
|
}
|
|
return "00";
|
|
}
|
|
public static string ByteToHexString(byte data)
|
|
{
|
|
List<byte> val = new List<byte>();
|
|
val.Add(data);
|
|
byte[] arr = val.ToArray();
|
|
return BytesToHex(arr);
|
|
}
|
|
public static string BytesToHex(byte[] data)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
foreach (byte b in data)
|
|
sb.Append(b.ToString("X2"));
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
public static byte[] HexStringToByteArray(String hexString)
|
|
{
|
|
int numberChars = hexString.Length;
|
|
byte[] bytes = new byte[numberChars / 2];
|
|
for (int i = 0; i < numberChars; i += 2)
|
|
{
|
|
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
|
|
}
|
|
return bytes;
|
|
}
|
|
public enum InitialCrcValue
|
|
{
|
|
Zeros,
|
|
NonZero1 = 0xffff,
|
|
NonZero2 = 0x1D0F
|
|
}
|
|
|
|
public class Crc16Ccitt
|
|
{
|
|
private const ushort poly = 0x1021;
|
|
ushort[] table = new ushort[256];
|
|
ushort initialValue = 0;
|
|
|
|
public ushort ComputeChecksum(byte[] bytes)
|
|
{
|
|
ushort crc = this.initialValue;
|
|
for (int i = 0; i < bytes.Length; ++i)
|
|
{
|
|
crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
|
|
}
|
|
return crc;
|
|
}
|
|
public byte[] ComputeChecksumBytes(byte[] bytes)
|
|
{
|
|
ushort crc = ComputeChecksum(bytes);
|
|
return BitConverter.GetBytes(crc);
|
|
}
|
|
public Crc16Ccitt(InitialCrcValue initialValue)
|
|
{
|
|
this.initialValue = (ushort) initialValue;
|
|
ushort temp, a;
|
|
for (int i = 0; i < table.Length; ++i)
|
|
{
|
|
temp = 0;
|
|
a = (ushort) (i << 8);
|
|
for (int j = 0; j < 8; ++j)
|
|
{
|
|
if (((temp ^ a) & 0x8000) != 0)
|
|
{
|
|
temp = (ushort) ((temp << 1) ^ poly);
|
|
}
|
|
else
|
|
{
|
|
temp <<= 1;
|
|
}
|
|
a <<= 1;
|
|
}
|
|
table[i] = temp;
|
|
}
|
|
}
|
|
public byte[] calcCRCfromMessage(byte[] message)
|
|
{
|
|
// CRC calculation goes from MessageID to Password
|
|
// For Read this means MsgID (1) + Offset (2) + PayloadLength (1) + Password (2) = 6
|
|
// for write the length of the payload comes on top.
|
|
// STX(1), length(1), CRC(2) and ETX(1) are excluded, in total 5 bytes.
|
|
//
|
|
// the method works both for sending messages (where the CRC and ETX are not in message)
|
|
// and for receiving messages (where the CRC and ETX are included at the end)
|
|
byte[] crc_msg = new byte[message[1]]; // message length is 2nd byte
|
|
Array.Copy(message, 2, crc_msg, 0, crc_msg.Length);
|
|
return ComputeChecksumBytes(crc_msg);
|
|
}
|
|
public string commentCRC(byte[] crc_meter, byte[] crc_computed)
|
|
{
|
|
bool crc_do_match = Tools.ByteArrayCompare(crc_meter, crc_computed);
|
|
string crcComment = crc_do_match ? "ok." : "CRC DOESN'T MATCH!!";
|
|
return "CRC Meter: " + Tools.BytesToHex(crc_meter) + (crc_do_match ? " == " : " != ")
|
|
+ "CRC Computed: " + Tools.BytesToHex(crc_computed) + ". " + crcComment;
|
|
}
|
|
public bool crc_do_match(byte[] a, byte[] b)
|
|
{
|
|
return ByteArrayCompare(a, b);
|
|
}
|
|
|
|
}
|
|
}
|
|
} |