551 lines
20 KiB
C#
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
|
|
}
|
|
}
|