772 lines
31 KiB
C#
772 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
using System.IO.Ports;
|
|
using System.Timers;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
//*****************************************************************************
|
|
// Copyright 2020 Sensus GmbH Ludwigshafen. All rights reserved.
|
|
// Author: Venkat, Rajeshwar
|
|
//*****************************************************************************
|
|
|
|
namespace Sensus.iPerl.NfcHandler
|
|
{
|
|
public class MCI_Protocol
|
|
{
|
|
private bool MCI_CycleResponseMsgTimeoutFlag = false;
|
|
|
|
private readonly byte STX = (byte) 0x02;
|
|
private readonly byte ETX = (byte) 0x03;
|
|
private readonly byte RX_ACK_RESPONSE_MSG_ID = (byte) 0xC9;
|
|
private readonly ushort[] MciPasswords = { 0x0000, 0x55AA, 0x1234, 0x97D1 };
|
|
|
|
public event DelNfc_MCI_MessageHandler MessageEvent;
|
|
|
|
public enum StructName
|
|
{
|
|
Calibration,
|
|
Configuration,
|
|
Status,
|
|
Power,
|
|
LCD,
|
|
unused1,
|
|
unused2,
|
|
Diagnostics,
|
|
ProductionInfo,
|
|
AckResponse,
|
|
RadioParams,
|
|
RadioInfo,
|
|
Command,
|
|
EEPROM_Metro,
|
|
ST25_Chip_Registers,
|
|
IndexReset,
|
|
NfcSecurity
|
|
}
|
|
|
|
public static readonly Dictionary<byte, string> StructName_id_to_str = new Dictionary<byte, string>
|
|
{
|
|
{0, "Calibration"},
|
|
{1, "Configuration"},
|
|
{2, "Status"},
|
|
{3, "Power"},
|
|
{4, "LCD"},
|
|
{5, "unused1"},
|
|
{6, "unused2"},
|
|
{7, "Diagnostics"},
|
|
{8, "ProductionInfo"},
|
|
{9, "AckResponse"},
|
|
{10, "RadioParams"},
|
|
{11, "RadioInfo"},
|
|
{12, "Command"},
|
|
{13, "EEPROM_Metro"},
|
|
{14, "ST25_Chip_Registers"},
|
|
{15, "IndexReset"},
|
|
{16, "NfcSecurity"}
|
|
};
|
|
|
|
private string StructToStr(StructName StName)
|
|
{
|
|
return StructName_id_to_str[Convert.ToByte(StName)];
|
|
}
|
|
|
|
private bool IsValidStruct(StructName StName)
|
|
{
|
|
return MCI_Protocol.StructName_id_to_str.ContainsKey(Convert.ToByte(StName));
|
|
}
|
|
|
|
public enum MCI_ErrorCodes
|
|
{
|
|
// these first 0..8 error codes are defined both in
|
|
// radio and metro. They can return these codes.
|
|
// higher codes will not be returned
|
|
Success_No_error = 0,
|
|
MCI_Error_MeterAns_Timeout = 1,
|
|
MCI_Error_MeterAns_Wrong_CRC = 2,
|
|
MCI_Error_MeterAns_Wrong_password = 3,
|
|
MCI_Error_MeterAns_Data_not_available = 4,
|
|
MCI_Error_MeterAns_Invalid_access = 5,
|
|
MCI_Error_MeterAns_Command_not_supported = 6,
|
|
MCI_Error_MeterAns_Command_not_executed = 7,
|
|
MCI_Error_MeterAns_Unidentified = 8,
|
|
// in case higher errors will be defined for the IMI in future
|
|
// we keep some distance with the errorcodes...
|
|
|
|
// host error codes from MCI_Read/Write start from 20:
|
|
MCI_Error_Host_Bad_MCI_ID = 20,
|
|
MCI_Error_Host_WriteReq_denied = 21,
|
|
MCI_Error_Host_WriteReq_bad_device_ans = 22,
|
|
MCI_Error_Host_ReadReq_denied = 23,
|
|
MCI_Error_Host_ReadReq_bad_device_ans = 24,
|
|
MCI_Error_Host_Bad_Payload_Len = 25,
|
|
MCI_Error_Host_MeterCRCDoesNotMatch = 26,
|
|
MCI_Error_Host_ReadReqBadResponseStructure = 27,
|
|
MCI_Error_Host_InvalidStructureID_CannotSendMsg = 28,
|
|
MCI_Error_Host_CommandNotInCmdCodeList = 29,
|
|
|
|
// host error codes from method MCI_TxRequest_RxResponse start from 40
|
|
MCI_Error_Host_MB_CTRL_Reg_Read1 = 40,
|
|
MCI_Error_Host_MB_disabled1 = 41,
|
|
MCI_Error_Host_MB_LEN_Reg_read1 = 42,
|
|
MCI_Error_Host_MB_msg_read1 = 43,
|
|
MCI_Error_Host_req_not_sent = 44,
|
|
MCI_Error_Host_req_not_read_by_iperl = 45,
|
|
MCI_Error_Host_MB_CTRL_Reg_Read2 = 46,
|
|
MCI_Error_Host_MB_disabled2 = 47,
|
|
MCI_Error_Host_MB_LEN_Reg_read2 = 48,
|
|
MCI_Error_Host_MB_msg_read2 = 49,
|
|
MCI_Error_Host_Timeout = 50,
|
|
MCI_Error_Host_msg_write = 51
|
|
}
|
|
|
|
public static string GetMCIErrorMessage(byte ErrorCode)
|
|
{
|
|
// this is called reflection. The variable names will be
|
|
// returned. No need of copys of variable names to string values.
|
|
return ((MCI_ErrorCodes) ErrorCode).ToString();
|
|
}
|
|
|
|
public static readonly Dictionary<byte, string> MCI_CmdCodes = new Dictionary<byte, string>
|
|
{
|
|
{0x00 , "Command: No Operation" },
|
|
{0x01 , "Command: Clear Alarms" },
|
|
{0x04 , "Command: Clear Diag" },
|
|
{0x05 , "Command: Clear Peak flowrate" },
|
|
{0x06 , "Command: Enter Active mode" },
|
|
{0x07 , "Command: Enter Test mode" },
|
|
{0x09 , "Command: Reset Accumulated Volume" },
|
|
{0x0A , "Command: Factory Default" },
|
|
{0x0B , "Command: Seal" },
|
|
{0x0C , "Command: Send radio address to radio FW" },
|
|
{0x0D , "Command: Adjust Field" },
|
|
{0x0E , "Command: LCD All segments ON" },
|
|
{0x0F , "Command: LCD All segments OFF" },
|
|
{0x10 , "Command: LCD Even segments ON" },
|
|
{0x11 , "Command: LCD Odd segments ON" },
|
|
{0x12 , "Command: LNA ON" },
|
|
{0x13 , "Command: LNA OFF" },
|
|
{0x14 , "Command: EPD ON" },
|
|
{0x15 , "Command: EPD OFF" },
|
|
{0x16 , "Command: Enter LPM4" },
|
|
{0x17 , "Command: Select drive coil connectivity" },
|
|
{0x1A , "Command: Enter VRTM" },
|
|
{0x1B , "Custom Package Mode ON" },
|
|
{0x1C , "Custom Package Mode OFF" },
|
|
{0x20 , "Command: Deinstall" },
|
|
{0x21 , "Command: Audit Mode" },
|
|
{0x22 , "Command: Diagnostic Refresh" },
|
|
{0x23 , "Command: NFC Security Reset Access Level" }
|
|
};
|
|
|
|
Dictionary<int, byte[]> MCI_Write_Info = new Dictionary<int, byte[]>();
|
|
Dictionary<int, byte[]> MCI_Read_Info = new Dictionary<int, byte[]>();
|
|
Dictionary<int, byte[]> MCI_ReadResponse_Info = new Dictionary<int, byte[]>();
|
|
|
|
private byte MB_Ctrl_Reg_Value;
|
|
private byte MB_LEN_Dyn_Register;
|
|
|
|
private ST25DV_Device _ST25DV_Device;
|
|
Tools.Crc16Ccitt Crc = new Tools.Crc16Ccitt(Tools.InitialCrcValue.NonZero1);
|
|
|
|
private System.Timers.Timer _ReqResCycleTimeoutTimer = new System.Timers.Timer();
|
|
|
|
public MCI_Protocol(ST25DV_Device ST25DVDevice)
|
|
{
|
|
_ST25DV_Device = ST25DVDevice;
|
|
Init_MCI_Info_MsgId_Password();
|
|
}
|
|
private void Init_MCI_Info_MsgId_Password()
|
|
{
|
|
//Write request
|
|
byte[] value1 = { 0x40, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(0, value1);//Calibration
|
|
|
|
byte [] value2 = { 0x41, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(1, value2); //Configuration
|
|
|
|
byte[] value3 = { 0x42, 0x34, 0x12 };
|
|
MCI_Write_Info.Add(2, value3); //Status
|
|
|
|
byte[] value4 = { 0x43, 0x34, 0x12 };
|
|
MCI_Write_Info.Add(3, value4);//Power
|
|
|
|
byte[] value5 = { 0x44, 0xAA, 0x55 };
|
|
MCI_Write_Info.Add(4, value5); //LCD
|
|
|
|
byte[] value6 = { 0x48, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(8, value6);//ProductionInfo
|
|
|
|
byte[] value7 = { 0x4E, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(10, value7);//RadioParams
|
|
|
|
byte[] value8 = { 0x4E, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(11, value8);//RadioInfo
|
|
|
|
byte[] value9 = { 0x4A, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(12, value9);//Command
|
|
|
|
byte[] valueIndexResetWrite = { 0x52, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(15, valueIndexResetWrite);//Index reset
|
|
|
|
byte[] valueNfcSecuritySignedWrite = { 0x54, 0xD1, 0x97 };
|
|
MCI_Write_Info.Add(16, valueNfcSecuritySignedWrite);//NFC Security signed message - while writing
|
|
|
|
//Read request
|
|
byte[] value10 = { 0x00, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(0, value10);//Calibration
|
|
|
|
byte[] value11 = { 0x01, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(1, value11); //Configuration
|
|
|
|
byte[] value12 = { 0x02, 0x34, 0x12 };
|
|
MCI_Read_Info.Add(2, value12); //Status
|
|
|
|
byte[] value13 = { 0x03, 0x34, 0x12 };
|
|
MCI_Read_Info.Add(3, value13);//Power
|
|
|
|
byte[] value14 = { 0x04, 0xAA, 0x55 };
|
|
MCI_Read_Info.Add(4, value14); //LCD
|
|
|
|
byte[] value15 = { 0x07, 0xAA, 0x55 };
|
|
MCI_Read_Info.Add(7, value15); //Diag
|
|
|
|
byte[] value16 = { 0x08, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(8, value16);//ProductionInfo
|
|
|
|
byte[] value17 = { 0x0E, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(10, value17);//RadioParams
|
|
|
|
byte[] value18 = { 0x0E, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(11, value18);//RadioInfo
|
|
|
|
byte[] valueIndexResetRead = { 0x12, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(15, valueIndexResetRead);//Index reset
|
|
|
|
byte[] valueNfcSecurityUnsignedRead = { 0x14, 0xD1, 0x97 };
|
|
MCI_Read_Info.Add(16, valueNfcSecurityUnsignedRead);//NFC Security non-signed message - while reading
|
|
|
|
//Read response
|
|
byte[] value19 = { 0xC0, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(0, value19);//Calibration
|
|
|
|
byte[] value20 = { 0xC1, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(1, value20); //Configuration
|
|
|
|
byte[] value21 = { 0xC2, 0x34, 0x12 };
|
|
MCI_ReadResponse_Info.Add(2, value21); //Status
|
|
|
|
byte[] value22 = { 0xC3, 0x34, 0x12 };
|
|
MCI_ReadResponse_Info.Add(3, value22);//Power
|
|
|
|
byte[] value23 = { 0xC4, 0xAA, 0x55 };
|
|
MCI_ReadResponse_Info.Add(4, value23); //LCD
|
|
|
|
byte[] value24 = { 0xC7, 0xAA, 0x55 };
|
|
MCI_ReadResponse_Info.Add(7, value24); //Diag
|
|
|
|
byte[] value25 = { 0xC8, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(8, value25);//ProductionInfo
|
|
|
|
byte[] value26 = { 0xC9, 0x00, 0x00 };
|
|
MCI_ReadResponse_Info.Add(9, value26);//Ack response
|
|
|
|
byte[] value27 = { 0xCE, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(10, value27);//RadioParams
|
|
|
|
byte[] value28 = { 0xCE, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(11, value28);//RadioInfo
|
|
|
|
byte[] valueIndexResetReadResponse = { 0xD2, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(15, valueIndexResetReadResponse);//Index reset
|
|
|
|
byte[] valueNfcSecurityUnsignedReadResponse = { 0xD4, 0xD1, 0x97 };
|
|
MCI_ReadResponse_Info.Add(16, valueNfcSecurityUnsignedReadResponse);//NFC Security non-signed message response - while reading
|
|
}
|
|
|
|
public bool MCI_Write(StructName StName, ushort offset, byte payloadlength, byte[] inBuffer, int timeoutMs,
|
|
out byte errorCode)
|
|
{
|
|
List<byte> RequestMessage = new List<byte>();
|
|
byte[] ResponseMessage = null;
|
|
byte[] crcValue = new byte[2];
|
|
|
|
errorCode = (byte) MCI_ErrorCodes.Success_No_error;
|
|
|
|
if (payloadlength != inBuffer.Length)
|
|
{
|
|
// Insufficient Payload length
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_Bad_Payload_Len;
|
|
return false;
|
|
}
|
|
if (StName == StructName.Command)
|
|
{
|
|
if (! MCI_Protocol.MCI_CmdCodes.ContainsKey(inBuffer[0]))
|
|
{
|
|
// Command not supported
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_CommandNotInCmdCodeList;
|
|
return false;
|
|
}
|
|
}
|
|
else // not a command => should be struct
|
|
{
|
|
if (!IsValidStruct(StName))
|
|
{
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_InvalidStructureID_CannotSendMsg;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
byte[] MsgId_Password = MCI_Write_Info[(int)StName];
|
|
|
|
RequestMessage.Add(STX);
|
|
RequestMessage.Add(Convert.ToByte(6 + payloadlength));
|
|
RequestMessage.Add(MsgId_Password[0]);
|
|
RequestMessage.Add((byte)(offset & 0x00FF));
|
|
RequestMessage.Add((byte)((offset & 0xFF00) >> 8));
|
|
RequestMessage.Add(payloadlength);
|
|
|
|
for (int i = 0; i < payloadlength; i++) // copy Buffer -> Request Payload
|
|
{
|
|
RequestMessage.Add(inBuffer[i]);
|
|
}
|
|
|
|
RequestMessage.Add(MsgId_Password[1]);
|
|
RequestMessage.Add(MsgId_Password[2]);
|
|
// calc CRC and add to request
|
|
crcValue = Crc.calcCRCfromMessage(RequestMessage.ToArray());
|
|
RequestMessage.Add(crcValue[0]);
|
|
RequestMessage.Add(crcValue[1]);
|
|
RequestMessage.Add(ETX);
|
|
|
|
OnMessageEvent("Write " + StructToStr(StName) + "Request: " + Tools.BytesToHex(RequestMessage.ToArray()));
|
|
errorCode = MCI_TxRequest_RxResponse(RequestMessage.ToArray(), out ResponseMessage, timeoutMs);
|
|
if (errorCode != (byte)MCI_ErrorCodes.Success_No_error)
|
|
{
|
|
return false;
|
|
}
|
|
//Response message received
|
|
byte[] RxCrcComputed = Crc.calcCRCfromMessage(ResponseMessage);
|
|
byte[] RxCrcMeter = new byte[2];
|
|
// CRC starts at length + STX(1) + lenbyte(1) = RespMsg[1] + 2.
|
|
Array.Copy(ResponseMessage, ResponseMessage[1] + 2, RxCrcMeter, 0, 2);
|
|
if (!Crc.crc_do_match(RxCrcMeter, RxCrcComputed))
|
|
{
|
|
OnMessageEvent(Crc.commentCRC(RxCrcMeter, RxCrcComputed));
|
|
errorCode = (byte) MCI_ErrorCodes.MCI_Error_Host_MeterCRCDoesNotMatch;
|
|
return false;
|
|
}
|
|
if ((ResponseMessage[0] == STX)
|
|
&& (ResponseMessage[1] == 0x07)
|
|
&& (ResponseMessage[2] == RX_ACK_RESPONSE_MSG_ID)
|
|
&& (ResponseMessage[11] == ETX))
|
|
{
|
|
errorCode = ResponseMessage[6]; // meter error. Must be between 0..8.
|
|
// Will be converted to errormessage string from the calling method.
|
|
if (errorCode != 0)
|
|
{
|
|
OnMessageEvent("Write " + StructToStr(StName) + " failed. Request Response: " + Tools.BytesToHex(ResponseMessage));
|
|
return false;
|
|
}
|
|
OnMessageEvent("Write : Success");
|
|
return true;
|
|
}
|
|
errorCode = (byte) MCI_ErrorCodes.MCI_Error_Host_WriteReq_bad_device_ans;
|
|
return false;
|
|
}
|
|
|
|
public bool MCI_Write_Read(StructName StName, ushort offset, byte payloadlength, byte[] inBuffer, out byte[] outBuffer, int timeoutMs,
|
|
out byte errorCode)
|
|
{
|
|
List<byte> RequestMessage = new List<byte>();
|
|
byte[] ResponseMessage = null;
|
|
byte[] crcValue = new byte[2];
|
|
|
|
byte RxMsgLength;
|
|
byte RxMsgId;
|
|
byte[] RxOffset = new byte[2];
|
|
byte RxPayloadlength;
|
|
byte[] RxCrcMeter = new byte[2];
|
|
|
|
outBuffer = null;
|
|
|
|
errorCode = (byte)MCI_ErrorCodes.Success_No_error;
|
|
|
|
if (payloadlength != inBuffer.Length)
|
|
{
|
|
// Insufficient Payload length
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_Bad_Payload_Len;
|
|
return false;
|
|
}
|
|
if (StName == StructName.Command)
|
|
{
|
|
if (!MCI_Protocol.MCI_CmdCodes.ContainsKey(inBuffer[0]))
|
|
{
|
|
// Command not supported
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_CommandNotInCmdCodeList;
|
|
return false;
|
|
}
|
|
}
|
|
else // not a command => should be struct
|
|
{
|
|
if (!IsValidStruct(StName))
|
|
{
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_InvalidStructureID_CannotSendMsg;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
byte[] MsgId_Password = MCI_Write_Info[(int)StName];
|
|
|
|
RequestMessage.Add(STX);
|
|
RequestMessage.Add(Convert.ToByte(6 + payloadlength));
|
|
RequestMessage.Add(MsgId_Password[0]);
|
|
RequestMessage.Add((byte)(offset & 0x00FF));
|
|
RequestMessage.Add((byte)((offset & 0xFF00) >> 8));
|
|
RequestMessage.Add(payloadlength);
|
|
|
|
for (int i = 0; i < payloadlength; i++) // copy Buffer -> Request Payload
|
|
{
|
|
RequestMessage.Add(inBuffer[i]);
|
|
}
|
|
|
|
RequestMessage.Add(MsgId_Password[1]);
|
|
RequestMessage.Add(MsgId_Password[2]);
|
|
// calc CRC and add to request
|
|
crcValue = Crc.calcCRCfromMessage(RequestMessage.ToArray());
|
|
RequestMessage.Add(crcValue[0]);
|
|
RequestMessage.Add(crcValue[1]);
|
|
RequestMessage.Add(ETX);
|
|
|
|
OnMessageEvent("Write " + StructToStr(StName) + "Request: " + Tools.BytesToHex(RequestMessage.ToArray()));
|
|
errorCode = MCI_TxRequest_RxResponse(RequestMessage.ToArray(), out ResponseMessage, timeoutMs);
|
|
if (errorCode != (byte)MCI_ErrorCodes.Success_No_error)
|
|
{
|
|
return false;
|
|
}
|
|
if ((ResponseMessage[0] == STX) && (ResponseMessage[11] == ETX)
|
|
&& (ResponseMessage[1] == 0x07)
|
|
&& (ResponseMessage[2] == RX_ACK_RESPONSE_MSG_ID))
|
|
{
|
|
//Ack message received, not served the read request
|
|
errorCode = ResponseMessage[6]; // forward error message from meter
|
|
OnMessageEvent("Read " + StructToStr(StName) + ": Failed with error "
|
|
+ Tools.ByteToHexString(errorCode));
|
|
return false;
|
|
}
|
|
//Response message received
|
|
RxMsgLength = ResponseMessage[1];
|
|
RxMsgId = ResponseMessage[2];
|
|
// RxOffset[0] = ResponseMessage[3];
|
|
// RxOffset[1] = ResponseMessage[4];
|
|
RxPayloadlength = ResponseMessage[5];
|
|
// RxPassword[0] = ResponseMessage[6 + RxPayloadlength];
|
|
// RxPassword[1] = ResponseMessage[7 + RxPayloadlength];
|
|
RxCrcMeter[0] = ResponseMessage[8 + RxPayloadlength];
|
|
RxCrcMeter[1] = ResponseMessage[9 + RxPayloadlength];
|
|
|
|
|
|
byte[] RxCrcComputed = Crc.calcCRCfromMessage(ResponseMessage);
|
|
// OnMessageEvent(Crc.commentCRC(RxCrcMeter, RxCrcComputed));
|
|
if (!Crc.crc_do_match(RxCrcMeter, RxCrcComputed))
|
|
{
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_MeterCRCDoesNotMatch;
|
|
return false;
|
|
}
|
|
|
|
//TODO, verify received password matches with expected password or not?
|
|
if (!((ResponseMessage[0] == STX) && (ResponseMessage[10 + RxPayloadlength] == ETX)
|
|
&& ((ResponseMessage[2] & 0x3F) == (MsgId_Password[0] & 0x3F))))
|
|
{
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_ReadReqBadResponseStructure;
|
|
return false;
|
|
}
|
|
|
|
outBuffer = new byte[RxPayloadlength];
|
|
for (int i = 0; i < RxPayloadlength; i++)
|
|
{
|
|
outBuffer[i] = (ResponseMessage[6 + i]);
|
|
}
|
|
|
|
if (IsValidStruct(StName))
|
|
{
|
|
OnMessageEvent("Read " + StructToStr(StName) + ": " + Tools.BytesToHex(outBuffer)
|
|
+ " Success");
|
|
}
|
|
else
|
|
{
|
|
OnMessageEvent("Read Failed! Unknown MCI ID: " + Convert.ToByte(StName));
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_Bad_MCI_ID;
|
|
return false;
|
|
}
|
|
errorCode = (byte)0x00; //Success, No error
|
|
return true;
|
|
}
|
|
|
|
public bool MCI_Read(StructName StName, ushort offset, byte payloadlength, int timeoutMs,
|
|
out byte[] outBuffer, out byte errorCode)
|
|
{
|
|
List<byte> RequestMessage = new List<byte>();
|
|
byte[] ResponseMessage = null;
|
|
|
|
byte RxMsgLength;
|
|
byte RxMsgId;
|
|
byte [] RxOffset = new byte[2];
|
|
byte RxPayloadlength;
|
|
byte[] RxCrcMeter = new byte[2];
|
|
byte[] RxPassword = new byte[2];
|
|
|
|
byte[] MsgId_Password = MCI_Read_Info[(byte)StName];
|
|
byte[] crcValue;
|
|
outBuffer = null;
|
|
errorCode = (byte)0x00; //Success, No error
|
|
|
|
if (! IsValidStruct(StName))
|
|
{
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_InvalidStructureID_CannotSendMsg;
|
|
OnMessageEvent("Read request:" + MCI_Protocol.GetMCIErrorMessage(errorCode));
|
|
return false;
|
|
}
|
|
|
|
RequestMessage.Add(STX);
|
|
RequestMessage.Add(0x06);
|
|
RequestMessage.Add(MsgId_Password[0]);
|
|
RequestMessage.Add((byte)(offset & 0x00FF));
|
|
RequestMessage.Add((byte)((offset & 0xFF00) >> 8));
|
|
RequestMessage.Add(payloadlength);
|
|
RequestMessage.Add(MsgId_Password[1]);
|
|
RequestMessage.Add(MsgId_Password[2]);
|
|
|
|
crcValue = Crc.calcCRCfromMessage(RequestMessage.ToArray());
|
|
RequestMessage.AddRange(crcValue);
|
|
RequestMessage.Add(ETX);
|
|
|
|
OnMessageEvent("Read Request: " + Tools.BytesToHex(RequestMessage.ToArray()));
|
|
errorCode = MCI_TxRequest_RxResponse(RequestMessage.ToArray(), out ResponseMessage, timeoutMs);
|
|
if (errorCode != (byte) MCI_ErrorCodes.Success_No_error)
|
|
{
|
|
return false;
|
|
}
|
|
if ((ResponseMessage[0] == STX) && (ResponseMessage[11] == ETX)
|
|
&& (ResponseMessage[1] == 0x07)
|
|
&& (ResponseMessage[2] == RX_ACK_RESPONSE_MSG_ID))
|
|
{
|
|
//Ack message received, not served the read request
|
|
errorCode = ResponseMessage[6]; // forward error message from meter
|
|
OnMessageEvent("Read " + StructToStr(StName) + ": Failed with error "
|
|
+ Tools.ByteToHexString(errorCode));
|
|
return false;
|
|
}
|
|
//Response message received
|
|
RxMsgLength = ResponseMessage[1];
|
|
RxMsgId = ResponseMessage[2];
|
|
// RxOffset[0] = ResponseMessage[3];
|
|
// RxOffset[1] = ResponseMessage[4];
|
|
RxPayloadlength = ResponseMessage[5];
|
|
// RxPassword[0] = ResponseMessage[6 + RxPayloadlength];
|
|
// RxPassword[1] = ResponseMessage[7 + RxPayloadlength];
|
|
RxCrcMeter[0] = ResponseMessage[8 + RxPayloadlength];
|
|
RxCrcMeter[1] = ResponseMessage[9 + RxPayloadlength];
|
|
|
|
|
|
byte[] RxCrcComputed = Crc.calcCRCfromMessage(ResponseMessage);
|
|
// OnMessageEvent(Crc.commentCRC(RxCrcMeter, RxCrcComputed));
|
|
if (!Crc.crc_do_match(RxCrcMeter, RxCrcComputed))
|
|
{
|
|
errorCode = (byte) MCI_ErrorCodes.MCI_Error_Host_MeterCRCDoesNotMatch;
|
|
return false;
|
|
}
|
|
|
|
//TODO, verify received password matches with expected password or not?
|
|
if (!((ResponseMessage[0] == STX) && (ResponseMessage[10 + RxPayloadlength] == ETX)
|
|
&& ((ResponseMessage[2] & 0x3F) == (MsgId_Password[0] & 0x3F))))
|
|
{
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_ReadReqBadResponseStructure;
|
|
return false;
|
|
}
|
|
|
|
outBuffer = new byte[RxPayloadlength];
|
|
for (int i = 0; i < RxPayloadlength; i++)
|
|
{
|
|
outBuffer[i] = (ResponseMessage[6 + i]);
|
|
}
|
|
|
|
|
|
if (IsValidStruct(StName))
|
|
{
|
|
OnMessageEvent("Read " + StructToStr(StName) + ": " + Tools.BytesToHex(outBuffer)
|
|
+ " Success");
|
|
}
|
|
else
|
|
{
|
|
OnMessageEvent("Read Failed! Unknown MCI ID: " + Convert.ToByte(StName));
|
|
errorCode = (byte)MCI_ErrorCodes.MCI_Error_Host_Bad_MCI_ID;
|
|
return false;
|
|
}
|
|
errorCode = (byte)0x00; //Success, No error
|
|
return true;
|
|
}
|
|
|
|
private byte MCI_TxRequest_RxResponse(byte[] InBuffer, out byte[] OutBuffer, int timeout)
|
|
{
|
|
// return message is the errorcode, 0 means no error.
|
|
byte[] responseMsg = null;
|
|
OutBuffer = null;
|
|
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
StartReqResCycleTimeoutTimer(timeout);
|
|
|
|
if (!_ST25DV_Device.ST25DV_ReadMailboxControlReg(out MB_Ctrl_Reg_Value))
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte) MCI_ErrorCodes.MCI_Error_Host_MB_CTRL_Reg_Read1;
|
|
}
|
|
//Mailbox enable/disable check, proceed further only if enabled
|
|
if ((MB_Ctrl_Reg_Value & 0x01) == 0x00)
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
OnMessageEvent(">> REMARK: Check if Metorlogy firmware-version is at least: 5.1.24 <<");
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_MB_disabled1;
|
|
}
|
|
//Checking iPERL previous message is in mailbox or not, if present then read it and return failure
|
|
if ((MB_Ctrl_Reg_Value & 0x02) == 0x02)
|
|
{
|
|
byte returnError;
|
|
OnMessageEvent("NFC DLL Error: Previous message not read, reading now !");
|
|
if (!_ST25DV_Device.ST25DV_ReadMailboxMessageLength(out MB_LEN_Dyn_Register))
|
|
{
|
|
returnError = (byte)MCI_ErrorCodes.MCI_Error_Host_MB_LEN_Reg_read1;
|
|
}
|
|
else
|
|
{
|
|
OnMessageEvent("NFC DLL Error: Previous message length " + MB_LEN_Dyn_Register);
|
|
if (!_ST25DV_Device.ST25DV_ReadMessage(MB_LEN_Dyn_Register, out responseMsg))
|
|
{
|
|
returnError = (byte)MCI_ErrorCodes.MCI_Error_Host_MB_msg_read1;
|
|
}
|
|
else
|
|
{
|
|
OnMessageEvent("NFC DLL Error: Previous message read as " + Tools.BytesToHex(responseMsg));
|
|
OnMessageEvent("NFC DLL Error: Present request not sent, retry again !");
|
|
returnError = (byte)MCI_ErrorCodes.MCI_Error_Host_req_not_sent;
|
|
}
|
|
}
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return returnError;
|
|
}
|
|
|
|
//Checking previous app message is in mailbox or not, if present then return failure !
|
|
if ((MB_Ctrl_Reg_Value & 0x04) == 0x04)
|
|
{
|
|
// Previous sent request is not read by iPERL
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_req_not_read_by_iperl;
|
|
}
|
|
//Mailbox free, so sending request message
|
|
if (_ST25DV_Device.ST25DV_WriteMessage(InBuffer))
|
|
{
|
|
while (true)
|
|
{
|
|
if (!_ST25DV_Device.ST25DV_ReadMailboxControlReg(out MB_Ctrl_Reg_Value))
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_MB_CTRL_Reg_Read2;
|
|
}
|
|
//Mailbox enable/disable check, proceed further only if enabled
|
|
if ((MB_Ctrl_Reg_Value & 0x01) == 0x00)
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_MB_disabled2;
|
|
}
|
|
|
|
//Proceed further only if host replied response message, otherwise wait in this loop
|
|
if ((MB_Ctrl_Reg_Value & 0x02) == 0x02)
|
|
{
|
|
if (!_ST25DV_Device.ST25DV_ReadMailboxMessageLength(out MB_LEN_Dyn_Register))
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_MB_LEN_Reg_read2;
|
|
}
|
|
else
|
|
{
|
|
if (!_ST25DV_Device.ST25DV_ReadMessage(MB_LEN_Dyn_Register, out responseMsg))
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_MB_msg_read2;
|
|
}
|
|
else
|
|
{
|
|
OutBuffer = new byte[responseMsg.Length];
|
|
OutBuffer = responseMsg;
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.Success_No_error;
|
|
}
|
|
}
|
|
}
|
|
//Checking timeout occured or not
|
|
if(MCI_CycleResponseMsgTimeoutFlag)
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_Timeout;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = false;
|
|
stopReqResCycleTimeoutTimer();
|
|
return (byte)MCI_ErrorCodes.MCI_Error_Host_msg_write;
|
|
}
|
|
}
|
|
|
|
#region Message Event
|
|
private void OnMessageEvent(string message)
|
|
{
|
|
NfcMessageEventArgs e = new NfcMessageEventArgs();
|
|
e.Message = message;
|
|
|
|
if (MessageEvent != null)
|
|
{
|
|
MessageEvent(this, e);
|
|
}
|
|
e = null;
|
|
}
|
|
#endregion
|
|
|
|
private void StartReqResCycleTimeoutTimer(int timeoutMilliSec)
|
|
{
|
|
_ReqResCycleTimeoutTimer.Elapsed += new ElapsedEventHandler(ReqResCycleTimeoutEvent);
|
|
_ReqResCycleTimeoutTimer.Interval = timeoutMilliSec;
|
|
_ReqResCycleTimeoutTimer.Enabled = true;
|
|
_ReqResCycleTimeoutTimer.Stop();
|
|
_ReqResCycleTimeoutTimer.Start();
|
|
}
|
|
|
|
private void stopReqResCycleTimeoutTimer()
|
|
{
|
|
_ReqResCycleTimeoutTimer.Stop();
|
|
}
|
|
|
|
#region Timer Event
|
|
private void ReqResCycleTimeoutEvent(object sender, ElapsedEventArgs e)
|
|
{
|
|
MCI_CycleResponseMsgTimeoutFlag = true;
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
}
|