tbf/NfcHandler/NfcDataHandler.cs
2024-07-25 12:14:58 +02:00

551 lines
20 KiB
C#

using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
//*****************************************************************************
// Copyright 2020 Sensus GmbH Ludwigshafen. All rights reserved.
// Author: Venkat, Rajeshwar
//*****************************************************************************
namespace Sensus.iPerl.NfcHandler
{
public sealed class NfcDataHandler
{
private string _ComPort = "";
private readonly byte[] readCommand = { 0x80, 0x08, 0x00, 0x03, 0x00, 0x00 };
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;
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();
return false;
}
return true;
}
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();
return false;
}
return true;
}
return false;
}
public bool MCI_Write_Read(MCI_Protocol.StructName StName, ushort offset, byte payloadlength, byte[] payload, int timeoutMs)
{
LastErrorMessage = string.Empty;
LastData = null;
byte[] response;
byte errorCode = 0;
if (ST25DV_MailboxCommStart())
{
bool ans = _MCI_Protocol.MCI_Write_Read(StName, offset, payloadlength, payload, out response, timeoutMs, out errorCode);
LastErrorCode = errorCode;
LastData = response;
if (!ans)
{
SetLastErrorMesage();
return false;
}
return true;
}
return false;
}
private bool ST25DV_MailboxCommStart()
{
// wake up the ST25DV by setting the GPO pin to output a pulse -> INT on Metro (set highest bit to one -> causes a pulse on GPO)
if (_ST25DV_Device.ST25DV_WriteGPOManageCommand(_ST25DV_Device.GPO_MANAGE_SET_PULSE, false))
{
Thread.Sleep(10); // wait 10ms to give metro some time
byte MB_Ctrl_Reg_Value = 0x00;
for (int i = 0; i < 20; i++) // do 20 retries max (20 times 100ms = 2sec, which should be more than enough)
{
if (_ST25DV_Device.ST25DV_ReadMailboxControlReg(out MB_Ctrl_Reg_Value))
{
if ((MB_Ctrl_Reg_Value & 0x01) == 0x01)
{
return true; //With this we proceed for mailbox communication with ST25
}
else
{
Thread.Sleep(100); // wait 100ms, then try again
LastErrorMessage = "Mailbox was not activated by Firmware (ST25DV_MailboxCommStart), try again";
}
}
else
{
Thread.Sleep(100); // wait 100ms, then try again
LastErrorMessage = "Mailbox state could not be read (ST25DV_MailboxCommStart), try again";
}
}
LastErrorCode = 0x08; //MCI Error: Unidentified
OnMessageEvent(">> REMARK: Check if Metorlogy firmware-version is at least: 5.1.24 <<");
return false;
}
else
{
LastErrorCode = 0x08; //MCI Error: Unidentified
LastErrorMessage = "GPO config writing failed(ST25DV_MailboxCommStart), 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 string NFCSecurity_Read_EndpointId(int timeoutMs)
{
string result = string.Empty;
byte[] payload = readCommand;
byte payloadLength = Convert.ToByte(payload.Length);
ushort offset = 0;
try
{
if ((!MCI_Write_Read(MCI_Protocol.StructName.NfcSecurity, offset, payloadLength, payload, timeoutMs)) || (LastErrorCode != 0x00))
{
return string.Empty;
}
}
catch (Exception ex)
{
OnMessageEvent(ex.Message);
return string.Empty;
}
//Check response header
if (LastData[0] != 0x05 || LastData[1] != 0x00)
{
LastErrorMessage = "Wrong response header";
return string.Empty;
}
//Check length byte
if (LastData[2] == 0x00)
{
LastErrorMessage = "Response length is 0";
return string.Empty;
}
//Check length of message
int lengthMessage = LastData[2] + 3; // two header bytes and one length byte
if (LastData.Length != lengthMessage)
{
LastErrorMessage = "Response length is wrong";
return string.Empty;
}
//Check identifier for information
if (LastData[3] != 0x00 || LastData[4] != 0x00)
{
LastErrorMessage = "Wrong identifier";
return string.Empty;
}
int lengthEndpointId = LastData[2] - 2; //Identifier bytes
byte[] id = new byte[lengthEndpointId];
Array.Copy(LastData, 5, id, 0, lengthEndpointId);
result = Encoding.ASCII.GetString(id);
return result;
}
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()
{
Version dll_version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return "NfcS5 DLL v" + String.Format("{0}.{1}.{2}", dll_version.Major, dll_version.Minor, dll_version.Build); //, dll_version.MajorRevision);
}
#region Message Event
private void OnMessageEvent(string message)
{
NfcMessageEventArgs e = new NfcMessageEventArgs();
e.Message = message;
if (MessageEvent != null)
{
MessageEvent(this, e);
}
e = null;
}
#endregion
}
}