tbf/NfcHandler/NfcDataHandler.cs
Jan Augustin 334d43fbda Manuel Stein:
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))
2024-04-05 13:44:07 +02:00

562 lines
21 KiB
C#

using System;
using System.IO.Ports;
using System.Diagnostics;
//*****************************************************************************
// Copyright 2020 Sensus GmbH Ludwigshafen. All rights reserved.
// Author: Venkat, Rajeshwar
//*****************************************************************************
namespace Sensus.iPerl.NfcHandler
{
public sealed class NfcDataHandler
{
public SERIAL_Driver _SERIAL_Driver;
public SERIAL_Driver _SERIAL_Driver_Head_Config;
public CR95HF_Reader _CR95HF_Reader;
public ST25DV_Device _ST25DV_Device;
public MCI_Protocol _MCI_Protocol;
public NFCHeadConfig _NFCHead_Config;
public byte LastErrorCode { get; set; }
public string LastErrorMessage { get; set; }
public byte[] LastData { get; set; }
public event DelNfcMessageHandler MessageEvent;
private string _ComPort = "";
public NfcDataHandler()
{
_SERIAL_Driver = new SERIAL_Driver();
_SERIAL_Driver_Head_Config = new SERIAL_Driver();
_CR95HF_Reader = new CR95HF_Reader(_SERIAL_Driver);
_ST25DV_Device = new ST25DV_Device(_CR95HF_Reader);
_MCI_Protocol = new MCI_Protocol(_ST25DV_Device);
_NFCHead_Config = new NFCHeadConfig(_SERIAL_Driver_Head_Config);
LastErrorCode = 0;
}
public bool OpenConnection(string comport, int baudrate, int databits, Parity parity, StopBits stopbits)
{
_ComPort = comport;
bool isopen = _SERIAL_Driver.OpenConnection(comport, baudrate, databits, parity, stopbits);
if(!isopen)OnMessageEvent(_SERIAL_Driver.ErrorMessage);
return isopen;
}
public bool isOpen()
{
return _SERIAL_Driver.isOpen();
}
public void Close()
{
_SERIAL_Driver.Close();
_SERIAL_Driver.Dispose();
}
public bool OpenConnectionSTM(string comport)
{
// helper for lazy people who don't know the comport settings or don't have
// parameters of type 'Parity' or 'StopBits' in their language (Python) at hand
// used here: https://slm.sms-esaap.com/iPERL/tetoo/-/tree/dev/tetoo/nfcs5_dll_wrapper
return OpenConnection(comport, 57600, 8, Parity.None, StopBits.Two);
}
public bool NFCHead_OpenConnection(string comport)
{
if (isOpen() && _ComPort == comport)
{
OnMessageEvent(comport + "......... Already in use ");
return false;
}
bool isopen = _SERIAL_Driver_Head_Config.OpenConnection(comport, 9600, 8, Parity.None, StopBits.Two);
OnMessageEvent(_SERIAL_Driver_Head_Config.ErrorMessage);
return isopen;
}
public bool isNFCHeadOpen()
{
return _SERIAL_Driver_Head_Config.isOpen();
}
public void NFCHeadClose()
{
if (_SERIAL_Driver_Head_Config.isOpen())
{
_SERIAL_Driver_Head_Config.Close();
_SERIAL_Driver_Head_Config.Dispose();
}
}
public bool ConnectReader()
{
if (_CR95HF_Reader.CR95HF_Connect())
{
OnMessageEvent("CombiHead.......Connected");
return true;
}
else
{
OnMessageEvent("CombiHead.......Not connected");
return false;
}
}
public bool RFProtocolON()
{
if (_CR95HF_Reader.CR95HF_RFProtocolOn())
{
OnMessageEvent("NFC CR95HF RF Protocol ON........Success");
return true;
}
else
{
OnMessageEvent("NFC CR95HF RF Protocol ON........Failed");
return false;
}
}
public bool RFProtocolOFF()
{
if (_CR95HF_Reader.CR95HF_RFProtocolOff())
{
OnMessageEvent("NFC CR95HF RF Protocol OFF........Success");
return true;
}
else
{
OnMessageEvent("NFC CR95HF RF Protocol OFF........Failed");
return false;
}
}
public bool ConnectDevice()
{
byte[] responseData;
if (_CR95HF_Reader.CR95HF_RFProtocolOn())
{
if (GetSystemInfo())
{
responseData = LastData;
if (responseData[0] == 0x0F)
OnMessageEvent("Device -> ST25DV04K");
else
OnMessageEvent("Device -> ST25DV16K or 64K");
OnMessageEvent("UID -> " + Tools.ByteToHexString(responseData[1]) + " " + Tools.ByteToHexString(responseData[2]) + " " + Tools.ByteToHexString(responseData[3]) + " " + Tools.ByteToHexString(responseData[4]) +
" " + Tools.ByteToHexString(responseData[5]) + " " + Tools.ByteToHexString(responseData[6]) + " " + Tools.ByteToHexString(responseData[7]) + " " + Tools.ByteToHexString(responseData[8]));
//OnMessageEvent("DSFID -> " + Tools.ByteToHexString(responseData[9]));
//OnMessageEvent("AFI -> " + Tools.ByteToHexString(responseData[10]));
//OnMessageEvent("Block size -> " + Tools.ByteToHexString(responseData[11]));
//OnMessageEvent("Memory size -> " + Tools.ByteToHexString(responseData[12]));
//if (responseData[13] == 0x24)
// OnMessageEvent("IC Ref -> ST25DV04K");
//else
// OnMessageEvent("IC Ref -> ST25DV16K or 64K");
OnMessageEvent("iPERL.......Connected");
return true;
}
else
{
OnMessageEvent("iPERL.......Not connected");
}
}
return false;
}
public bool GetSystemInfo()
{
byte[] responseData = null;
try
{
if (_ST25DV_Device.ST25DV_GetSystemInfo(out responseData))
{
LastData = responseData;
return true;
}
}
catch (Exception ex)
{
OnMessageEvent(ex.Message);
}
return false;
}
public bool ST25DV_ReadSingleBlock(byte blockNumber, out byte[] outBuffer)
{
return _ST25DV_Device.ST25DV_ReadSingleBlock(blockNumber, out outBuffer);
}
public bool ST25DV_WriteSingleBlock(byte blockNumber, byte[] inBuffer)
{
return _ST25DV_Device.ST25DV_WriteSingleBlock(blockNumber, inBuffer);
}
public bool ST25DV_ReadMultipleBlocks(byte startingBlockNumber, byte numberOfBlocks, out byte[] OutBuffer)
{
return _ST25DV_Device.ST25DV_ReadMultipleBlocks(startingBlockNumber, numberOfBlocks, out OutBuffer);
}
public bool Echo()
{
if (!_CR95HF_Reader.CR95HF_Echo())
{
OnMessageEvent("Echo..Failed. CombiHead connection...Lost");
return false;
}
return true;
}
public bool ST25DV_ReadConfiguration(byte pointer, out byte value)
{
return _ST25DV_Device.ST25DV_ReadConfiguration(pointer, out value, true);
}
public bool ST25DV_ReadDynamicConfiguration(byte regAddress, out byte OutBuffer)
{
return _ST25DV_Device.ST25DV_ReadDynamicConfiguration(regAddress, out OutBuffer, true);
}
public bool ST25DV_WriteDynamicConfiguration(byte regAddress, byte regValue)
{
return _ST25DV_Device.ST25DV_WriteDynamicConfiguration(regAddress, regValue, true);
}
public bool ST25DV_ReadMailboxMessageLength(out byte MessageLength)
{
return _ST25DV_Device.ST25DV_ReadMailboxMessageLength(out MessageLength, true);
}
// This function-header is needed for calls to the DLL without need for using enums.
// In python 3.11 the module pythonnet (v3.x) does not support casting 'int' to 'enum' aynmore,
// so this workaround is needed.
public bool MCI_Read_pythonwrapper_enum(int STIndex, ushort offset, byte payloadlength, int timeoutMs)
{
return MCI_Read((MCI_Protocol.StructName)STIndex, offset, payloadlength, timeoutMs);
}
public bool MCI_Read(MCI_Protocol.StructName StName, ushort offset, byte payloadlength, int timeoutMs)
{
LastErrorMessage = string.Empty;
byte[] response;
byte errorCode = 0;
if (ST25DV_MailboxCommStart())
{
bool worked = _MCI_Protocol.MCI_Read(StName, offset, payloadlength, timeoutMs, out response, out errorCode);
LastErrorCode = errorCode;
LastData = response;
if (!worked)
{
SetLastErrorMesage();
ST25DV_MailboxCommStop();
return false;
}
return ST25DV_MailboxCommStop();
}
return false;
}
// This function-header is needed for calls to the DLL without need for using enums.
// In python 3.11 the module pythonnet (v3.x) does not support casting 'int' to 'enum' aynmore,
// so this workaround is needed.
public bool MCI_Write_pythonwrapper_enum(int STIndex, ushort offset, byte payloadlength, byte[] payload, int timeoutMs)
{
return MCI_Write((MCI_Protocol.StructName)STIndex, offset, payloadlength, payload, timeoutMs);
}
public bool MCI_Write(MCI_Protocol.StructName StName, ushort offset, byte payloadlength, byte[] payload, int timeoutMs)
{
LastErrorMessage = string.Empty;
LastData = null;
byte errorCode = 0;
if (ST25DV_MailboxCommStart())
{
bool ans = _MCI_Protocol.MCI_Write(StName, offset, payloadlength, payload, timeoutMs, out errorCode);
LastErrorCode = errorCode;
if (!ans)
{
SetLastErrorMesage();
ST25DV_MailboxCommStop();
return false;
}
return ST25DV_MailboxCommStop();
}
return false;
}
public bool MCI_Read(byte msgId, ushort offset, byte payloadlength, int timeoutMs)
{
LastErrorMessage = string.Empty;
byte[] response;
byte errorCode = 0;
if (ST25DV_MailboxCommStart())
{
bool ans = _MCI_Protocol.MCI_Read((MCI_Protocol.StructName) msgId, offset, payloadlength, timeoutMs,
out response, out errorCode);
LastErrorCode = errorCode;
LastData = response;
if (!ans)
{
SetLastErrorMesage();
ST25DV_MailboxCommStop();
return false;
}
return ST25DV_MailboxCommStop();
}
return false;
}
public bool MCI_Write(byte msgId, ushort offset, byte payloadlength, byte[] payload, int timeoutMs)
{
LastErrorMessage = string.Empty;
LastData = null;
byte errorCode = 0;
if (ST25DV_MailboxCommStart())
{
bool ans = _MCI_Protocol.MCI_Write((MCI_Protocol.StructName) msgId, offset, payloadlength, payload,
timeoutMs, out errorCode);
LastErrorCode = errorCode;
if (!ans)
{
SetLastErrorMesage();
ST25DV_MailboxCommStop();
return false;
}
return ST25DV_MailboxCommStop();
}
return false;
}
private bool ST25DV_MailboxCommStart()
{
bool success = false;
byte[] EepromBlockRead = { 0, 0, 0, 0 };
byte[] EepromBlockWrite = { 0, 0, 0, 0 };
//Byte-2 in block-2 represent the EEPROM I2C interface status ->
//0xA5-EEPROM I2C Interface Idle, 0x5A-EEPROM I2C Interface Active
success = _ST25DV_Device.ST25DV_ReadSingleBlock(2, out EepromBlockRead, false);
if (success)
{
if (EepromBlockRead[1] == 0xA5) //0xA5-EEPROM I2C Interface Idle
{
//Byte-1 in block-64 represent the Mailbox RF interface status ->
//0xA5-Mailbox RF Interface Idle, 0x5A-Mailbox RF Interface Active
EepromBlockWrite[0] = 0x5A; //Raising Mailbox RF interface Active flag to avoid collisions with host interface
success = _ST25DV_Device.ST25DV_WriteSingleBlock(64, EepromBlockWrite, false);
if (success)
{
//Sets 0x01 (MB_EN) in the register (MB_CTRL_Dyn) with address 0x0D
if (_ST25DV_Device.ST25DV_WriteDynamicConfiguration(0x0D, 0x01, false))
{
return true; //With this we proceed for mailbox communication with ST25
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "MB_CTRL_Dyn writing failed(ST25DV_MailboxCommStart), try again";
return false;
}
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "Mailbox RF interface status writing failed(ST25DV_MailboxCommStart), try again";
return false;
}
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "EEPROM interface busy(ST25DV_MailboxCommStart), try again";
return false;
}
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "EEPROM interface status reading failed(ST25DV_MailboxCommStart), try again";
return false;
}
}
private bool ST25DV_MailboxCommStop()
{
bool success = false;
byte[] EepromBlockWrite = { 0, 0, 0, 0 };
//Sets 0x00 (MB_EN) in the register (MB_CTRL_Dyn) with address 0x0D
if (_ST25DV_Device.ST25DV_WriteDynamicConfiguration(0x0D, 0x00, false))
{
//Byte-1 in block-64 represent the Mailbox RF interface status ->
//0xA5-Mailbox RF Interface Idle, 0x5A-Mailbox RF Interface Active
EepromBlockWrite[0] = 0xA5; //Raising Mailbox RF interface Idle flag to avoid collisions with host interface
success = _ST25DV_Device.ST25DV_WriteSingleBlock(64, EepromBlockWrite, false);
if (success)
{
return true; //With this we proceed for interpreting the communication happened with ST25
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "Mailbox RF interface status writing failed(ST25DV_MailboxCommStop), try again";
return false;
}
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "MB_CTRL_Dyn writing failed(ST25DV_MailboxCommStop), try again";
return false;
}
}
private void SetLastErrorMesage()
{
try
{
LastErrorMessage = MCI_Protocol.GetMCIErrorMessage(LastErrorCode);
}
catch
{
LastErrorMessage = "MCI Error: The Errorcode '" + LastErrorCode as string +
"' couldn't be converted to string with the enum MCI_ErrorCodes!";
}
}
public bool NFCHeadConfig_PulseLedOutputEnable()
{
return _NFCHead_Config.NFCHeadConfig_PulseLedOutputEnable(); // true - success, false - failed
}
public bool NFCHeadConfig_DataLedOutputEnable()
{
return _NFCHead_Config.NFCHeadConfig_DataLedOutputEnable(); // true - success, false - failed
}
public ushort NFCHeadConfig_ReadOpampOutput()
{
return _NFCHead_Config.NFCHeadConfig_ReadOpampOutput(); // 0 - failed, any other value is opamp output
}
public bool NFCHeadConfig_SetGain(byte gain)
{
return _NFCHead_Config.NFCHeadConfig_SetGain(gain); // true - success, false - failed
}
public byte NFCHeadConfig_ReadGain()
{
return _NFCHead_Config.NFCHeadConfig_ReadGain(); // 0 - failed, any other value is gain
}
public bool NFCHeadConfig_SetInterface(bool value)
{
return _NFCHead_Config.NFCHeadConfig_SetInterface(value); // true - success, false - failed
}
public byte NFCHeadConfig_ReadInterface()
{
return _NFCHead_Config.NFCHeadConfig_ReadInterface(); // 0 - failed, 1 - NFC, 2 - RFID
}
public byte[] NFCHeadConfig_ReadFirmwareVersion()
{
return _NFCHead_Config.NFCHeadConfig_ReadFirmwareVersion(); // 0 - failed, any other value is firmware version
}
public bool NFCSecurity_SendSignedCommand(byte[] payload, int timeoutMs)
{
byte payloadLength = Convert.ToByte(payload.Length);
ushort offset = 0;
try
{
if ( (!MCI_Write(MCI_Protocol.StructName.NfcSecurity, offset, payloadLength, payload, timeoutMs)) || (LastErrorCode != 0x00))
{
return false;
}
}
catch (Exception ex)
{
OnMessageEvent(ex.Message);
}
return true; // true - success, false - failed
}
public byte NFCSecurity_ReadPrivilegeLevel(int timeoutMs)
{
ushort offset = 0;
byte payloadLength = 1;
byte payload = 4;
try
{
if ((!MCI_Read(MCI_Protocol.StructName.NfcSecurity, offset, payloadLength, timeoutMs)) || (LastErrorCode != 0x00))
{
payload = 4;
}
else
{
payload = LastData[0];
}
}
catch (Exception ex)
{
OnMessageEvent(ex.Message);
}
return payload; // 0 - No Security, 1 - Customer Maintenance, 2 - Customer Admin, 3 - Sensus Admin, 4 and above - failed
}
public bool NFCSecurity_ClearPrivilegeLevel(int timeoutMs)
{
ushort offset = 0;
byte payloadLength = 1;
byte [] payload = { 0x23 }; // 0x23 - Command code for 'NFC Security reset access level'
try
{
if ((!MCI_Write(MCI_Protocol.StructName.Command, offset, payloadLength, payload, timeoutMs)) || (LastErrorCode != 0x00))
{
return false;
}
}
catch (Exception ex)
{
OnMessageEvent(ex.Message);
}
return true; // true - success, false - failed
}
public string GetDllVersion()
{
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
string str = "NfcS5 DLL v" + fvi.FileVersion;
return str.Remove(str.Length - 2); // only show x.y.z version, hide 4th number
}
#region Message Event
private void OnMessageEvent(string message)
{
NfcMessageEventArgs e = new NfcMessageEventArgs();
e.Message = message;
if (MessageEvent != null)
{
MessageEvent(this, e);
}
e = null;
}
#endregion
}
}