Why do I need to send a message twice to trigger Indy's OnExecute event? - tcp

I am working on an application that works as a "man in the middle" to analyze a protocol (ISO 8583) sent over TCP/IP.
The main idea is to get the raw binary data and convert it to a string for parsing and decoding the protocol.
For this, I am using the TIdMappedPortTCP component.
I am testing with Hercules.
I am working with:
Windows 11 Home
Embarcadero® C++Builder 10.4 Version 27.0.40680.4203
Delphi and C++ Builder 10.4 Update 2
Indy 10.6.2.0
More context can be found in these questions:
Where can I find a fully working example of a TCP Client and Server for Indy in C++Builder?
Parsing bytes as BCD with Indy C++ Builder
The problem is that I have to send the message twice to trigger the OnExecute event. I think this might be length related but I haven't found the issue. Other than that the program does what is expected from it.
If I use this data in Hercules:
00 04 60 02
equivalent to:
"\x00\x04\x60\x02"
My program processes everything correctly:
Here is the code:
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext)
{
static int index;
TIdBytes ucBuffer;
UnicodeString usTemp1;
UnicodeString usTemp2;
int calculated_length;
// getting the length in Hexa
calculated_length = ReadMessageLength(AContext);
// reads data
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length);
// displays string with calculated length and size of the data
usTemp2 = UnicodeString("calculated length = ");
usTemp2 += IntToStr(calculated_length);
usTemp2 += " ucBuffer.Length = ";
usTemp2 += IntToStr(ucBuffer.Length);
Display->Lines->Add(usTemp2);
// converts the binary data into a a Hex String for visualization
usTemp1 = BytesToHexString(ucBuffer);
// adds an index to distinguish from previous entries.
usTemp2 = IntToStr(index);
usTemp2 += UnicodeString(": ");
usTemp2 += usTemp1;
Display->Lines->Add(usTemp2);
index++;
}
Here is the code for the functions called there. By the way, is there a better way to convert the bytes to a hex string?
// Convert an array of bytes to a hexadecimal string
UnicodeString BytesToHexString(const TBytes& bytes)
{
// Create an empty UnicodeString to store the hexadecimal representation of the bytes
UnicodeString hexString;
// Iterate through each byte in the array
for (int i = 0; i < bytes.Length; i++)
{
// Convert the byte to a hexadecimal string and append it to the result string
hexString += IntToHex(bytes[i], 2);
}
// Return the hexadecimal string
return hexString;
}
// Read the first two bytes of an incoming message and interpret them as the length of the message
int ReadMessageLength(TIdContext *AContext)
{
int calculated_length;
// Use the 'ReadSmallInt' method to read the length of the message from the first two bytes
calculated_length = AContext->Connection->IOHandler->ReadSmallInt();
// converting from hex binary to hex string
UnicodeString bcdLength = UnicodeString().sprintf(L"%04x", calculated_length);
// converting from hex string to int
calculated_length = bcdLength.ToInt();
// decrease length
calculated_length -= 2;
return calculated_length;
}
UPDATE
I have created a class to update the TEditRich control. But the problem persist, I need to send the message twice to be processed and the application freezes when trying to close it. This is my class:
class TAddTextToDisplay : public TIdSync {
private:
UnicodeString textToAdd;
public:
__fastcall TAddTextToDisplay(UnicodeString str) {
// Store the input parameters in member variables.
textToAdd = str;
}
virtual void __fastcall DoSynchronize() {
if (textToAdd != NULL) {
// Use the input parameters here...
Form1->Display->Lines->Add(textToAdd);
}
}
void __fastcall setTextToAdd(UnicodeString str) {
textToAdd = str;
}
};
And this is how my new OnExecute event looks:
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext) {
static int index;
TIdBytes ucBuffer;
UnicodeString usTemp1;
UnicodeString usTemp2;
int calculated_length;
int bytes_remaining;
// getting the length in Hexa
calculated_length = ReadMessageLength(AContext);
if (!AContext->Connection->IOHandler->InputBufferIsEmpty()) {
// reads data
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length);
// displays string with calculated length and size of the data
usTemp2 = UnicodeString("calculated length = ");
usTemp2 += IntToStr(calculated_length);
usTemp2 += " ucBuffer.Length = ";
usTemp2 += IntToStr(ucBuffer.Length);
TAddTextToDisplay *AddTextToDisplay = new TAddTextToDisplay(usTemp2);
AddTextToDisplay->Synchronize();
// converts the binary data into a a Hex String for visualization
usTemp1 = BytesToHexString(ucBuffer);
// adds an index to distinguish from previous entries.
usTemp2 = IntToStr(index);
usTemp2 += UnicodeString(": ");
usTemp2 += usTemp1;
AddTextToDisplay->setTextToAdd(usTemp2);
AddTextToDisplay->Synchronize();
delete AddTextToDisplay;
index++;
}
}

You really should not be reading from the IOHandler directly at all. You are getting your communication out of sync. TIdMappedPortTCP internally reads from the client before firing the OnExecute event, and reads from the target server before firing the OnOutboundData event. In both cases, the bytes received are made available in the TIdMappedPortContext::NetData property, which you are not processing at all.
You need to do all of your parsing using just the NetData only, iterating through its bytes looking for complete messages, and saving incomplete messages for future events to finish.
Try something more like this instead:
#include <IdGlobal.hpp>
#include <IdBuffer.hpp>
bool ReadMessageData(TIdBuffer *Buffer, int &Offset, TIdBytes &Data)
{
// has enough bytes?
if ((Offset + 2) > Buffer->Size)
return false;
// read the length of the message from the first two bytes
UInt16 binLength = Buffer->ExtractToUInt16(Offset);
// converting from hex binary to hex string
String bcdLength = String().sprintf(_D("%04hx"), binLength);
// converting from hex string to int
int calculated_length = bcdLength.ToInt() - 2;
// has enough bytes?
if ((Offset + 2 + calculated_length) > Buffer->Size)
return false;
// reads data
Data.Length = calculated_length;
Buffer->ExtractToBytes(Data, calculated_length, false, Offset + 2);
Offset += (2 + calculated_length);
return true;
}
void __fastcall TForm1::MITMProxyConnect(TIdContext *AContext)
{
AContext->Data = new TIdBuffer;
}
void __fastcall TForm1::MITMProxyDisconnect(TIdContext *AContext)
{
delete static_cast<TIdBuffer*>(AContext->Data);
AContext->Data = NULL;
}
void __fastcall TForm1::MITMProxyExecute(TIdContext *AContext)
{
static int index = 0;
TIdBuffer *Buffer = static_cast<TIdBuffer*>(AContext->Data);
Buffer->Write(static_cast<TIdMappedPortContext*>(AContext)->NetData);
Buffer->CompactHead();
TAddTextToDisplay *AddTextToDisplay = NULL;
TIdBytes ucBuffer;
int offset = 0;
while (ReadMessageData(Buffer, offset, ucBuffer))
{
String sTemp = String().sprintf(_D("%d: ucBuffer.Length = %d ucBuffer = %s"), index, ucBuffer.Length, ToHex(ucBuffer).c_str());
if (AddTextToDisplay)
AddTextToDisplay->setTextToAdd(sTemp);
else
AddTextToDisplay = new TAddTextToDisplay(sTemp);
AddTextToDisplay->Synchronize();
++index;
}
delete AddTextToDisplay;
if (offset > 0)
Buffer->Remove(offset);
}
Otherwise, if you want to do your own socket I/O, then you will have to use TIdTCPServer and TIdTCPClient directly instead of using TIdMappedPortTCP.

Related

Translating messages from device to LIS via ASCII using checksums

I'm looking for some help please.
I'm trying to communicate to a device over TCP / IP using ASCII. The protocol includes a checksum that consists of two ASCII characters that represent a two character hexadecimal number in the range 00 through FF.
I know that the hexadecimal number is generated by performing a modulo-256 10 summation of all previous characters in the frame (that is, over <STX> … <ETX>, inclusive) and then expressing the resulting 8-bit unsigned integer in hexadecimal format.
For example I know that this checksum is 84, but how is that calculated? <STX>ID_DATA<FS><RS>aMOD<GS>LIS<GS><GS><GS><FS>iIID<GS>333<GS><GS><GS><FS><RS><ETX>84<EOT>
And that being said, what would the checksum be for this? <STX>SMP_REQ<FS><RS>aMOD<GS>LIS<GS><GS><GS><FS>iIID<GS>42731<GS><GS><GS><FS>rSEQ<GS>16<GS><GS><GS><FS><RS><ETX>{chksum}<EOT>
Any guidance is greatly appreciated? :)
TIA!
Late is better than never. Here is how to get the checksum:
public const byte STX = 2;
public const byte FS = 28;
public const byte GS = 29;
public const byte RS = 30;
public static byte[] STX_BUFF = { STX };
public static byte[] FS_BUFF = { FS };
public static byte[] GS_BUFF = { GS };
public static byte[] RS_BUFF = { RS };
string checksum = "00";
int byteVal = 0;
int sumOfChars = 0;
string frame = string.Format("{0}SMP_REQ{1}{2}aMOD{3}0500{3}{3}{3}{1}iIID{3}{6}{3}{3}{3}{1}rSEQ{3}{5}{3}{3}{3}{1}{2}{4}", Encoding.ASCII.GetString(STX_BUFF), Encoding.ASCII.GetString(FS_BUFF), Encoding.ASCII.GetString(RS_BUFF), Encoding.ASCII.GetString(GS_BUFF), Encoding.ASCII.GetString(ETX_BUFF), rSEQ, iIID);
// obtiene el checksum
for (int i = 0; i < frame.Length; i++)
{
byteVal = Convert.ToInt32(frame[i]);
sumOfChars += byteVal;
}
checksum = Convert.ToString(sumOfChars % 256, 16).ToUpper();
if (checksum.Length == 1) checksum = "0" + checksum;
frame += string.Format("{0}{1}", checksum, Encoding.ASCII.GetString(EOT_BUFF));

Convert string as hex to hexadecimal

I have a function that takes an uint64_t variable. Normally I would do this:
irsend.sendNEC(result.value);
result.value is an uint64_t as hexadecimal (I think). If I do this:
String((uint32_t) results.value, HEX)
I get this:
FF02FD
If I do:
irsend.sendNEC(0x00FF02FD)
it works perfectly and is what I want.
Instead of grabbing the result.value, I want to write it as a string (because that's what I get from the GET request). How do I make "FF02FD" into 0x00FF02FD?
EDIT:
Maybe this makes it easier to understand:
GET: http://192.168.1.125/code=FF02FD
//Arduino grabs the FF02FD by doing:
for (int i = 0; i < server.args(); i++) {
if (server.argName(i) == "code") {
String code = server.arg(i);
irsend.sendNEC(code);
}
}
This is where I get the error:
no matching function for call to 'IRsend::sendNEC(String&)'
because:
void sendNEC(uint64_t data, uint16_t nbits = NEC_BITS, uint16_t repeat = 0);
Comment writeup:
As already suggested, a string containing a hexadecimal value can be converted to an actual integer value using the C standard library functions such as "string to unsigned long" (strtoul) or "string to unsigned long long" (strtoull). From Arduino-type String one can get the actual const char* to the data using the c_str() member function. All in all, one does a hex-string to integer conversion as
uint64_t StrToHex(const char* str)
{
return (uint64_t) strtoull(str, 0, 16);
}
Which can then in code be called as
for (int i = 0; i < server.args(); i++) {
if (server.argName(i) == "code") {
String code = server.arg(i);
irsend.sendNEC(StrToHex(code.c_str()));
}
}
Appendum: Be carefull about using int or long on different platforms. On a Arduino Uno/Nano with a 8-bit microcontroller, such as the ATMega328P, an int is a int16_t. On the 32-bit ESP8266 CPU, an int is int32_t.

Arduino Dynamic Two-dimensional array

I'm working on an Arduino project where I need to build (and work with) a two-dimensional array at runtime. I've been poking around looking for a solution, but I've had no luck. I found an example of a dynamic one-dimentional array helper here: http://playground.arduino.cc/Code/DynamicArrayHelper, so i've been trying to adopt that code for my use. I created a library using the following code:
My Header file:
#ifndef Dynamic2DArray_h
#define Dynamic2DArray_h
#include "Arduino.h"
class Dynamic2DArray
{
public:
Dynamic2DArray( bool sorted );
//Add an integer pair to the array
bool add( int v1, int v2);
//Clear out (empty) the array
bool clear();
//Get the array item in the specified row, column
int getValue(int row, int col);
//Get the number of rows in the array
int length();
private:
int _rows;
void * _slots;
bool _sorted;
void _sort();
};
#endif
The library's code:
#include "Arduino.h"
#include "Dynamic2DArray.h"
#define ARRAY_COLUMNS 2
int _rows;
void * _slots;
bool _sorted;
Dynamic2DArray::Dynamic2DArray(bool sorted) {
//Set our local value indicating where we're supposed to
//sort or not
_sorted = sorted;
//Initialize the row count so it starts at zero
_rows = 0;
}
bool Dynamic2DArray::add( int v1, int v2) {
//Add the values to the array
//implementation adapted from http://playground.arduino.cc/Code/DynamicArrayHelper
//Allocate memory based on the size of the current array rows plus one (the new row)
int elementSize = sizeof(int) * ARRAY_COLUMNS;
//calculate how much memory the current array is using
int currentBufferSize = elementSize * _rows;
//calculate how much memory the new array will use
int newBufferSize = elementSize * (_rows + 1);
//allocate memory for the new array (which should be bigger than the old one)
void * newArray = malloc ( newBufferSize );
//Does newArray not point to something (a memory address)?
if (newArray == 0) {
//Then malloc failed, so return false
return false;
}
// copy the data from the old array, to the new array
for (int idx = 0; idx < currentBufferSize ; idx++)
{
((byte*)newArray)[idx] = ((byte *)_slots)[idx];
}
// free the original array
if (_slots != NULL)
{
free(_slots);
}
// clear the newly allocated memory space (the new row)
for (int idx = currentBufferSize; idx < newBufferSize; idx++)
{
((byte *)newArray)[idx] = 0;
}
// Store the number of rows the memory is allocated for
_rows = ++_rows;
// set the array to the newly created array
_slots = newArray;
//Free up the memory used by the new array
free(newArray);
//If the array's supposed to be sorted,
//then sort it
if (_sorted) {
_sort();
}
// success
return true;
};
int Dynamic2DArray::length() {
return _rows;
};
bool Dynamic2DArray::clear() {
//Free up the memory allocated to the _slots array
free(_slots);
//And zero out the row count
_rows = 0;
};
int Dynamic2DArray::getValue(int row, int col) {
//do we have a valid row/col?
if ((row < _rows) && (col < ARRAY_COLUMNS)) {
//Return the array value at that row/col
return _slots[row][col];
} else {
//No? Then there's nothing we can do here
return -1;
}
};
//Sorted probably doesn't matter, I can probably ignore this one
void _sort() {
}
The initial assignment of the _slots value is giving me problems, I don't know how to define it so this code builds. The _slots variable is supposed to point to the dynamic array, but I've got it wrong.
When I try to compile the code into my project's code, I get the following:
Arduino: 1.8.0 (Windows 10), Board: "Pro Trinket 3V/12MHz (USB)"
sketch\Dynamic2DArray.cpp: In member function 'int Dynamic2DArray::getValue(int, int)':
sketch\Dynamic2DArray.cpp:83:22: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
return _slots[row][col];
^
Dynamic2DArray.cpp:83: error: 'void*' is not a pointer-to-object type
Can someone please help me fix this code? I've posted the files to https://github.com/johnwargo/Arduino-Dynamic-2D-Array-Lib.
The code you took was for a 1D dynamic array; the modifications for a 2D array are too tricky. Give up these horrors.
I think there is no reason you use dynamic array. You can assume that size max is ROW_MAX * COL_MAX, so you can define a static array int array[ROW_MAX][COL_MAX].
on one hand if you defined a dynamic array, you could free space when you dont use it anymore and take advantage of it for other work. I dont know if this is your case.
on the other hand if you define a static array (on UNO), you have 32kB available on program space, instead of 2kB available on RAM.
Because of the difference 32kB / 2kB, there are very few chances you can get bigger array with dynamic allocation.

How to correctly use sscanf

I have to write an Arduino function to look up a number in the phone book. My code doesn't work because of the condition using sscanf. What am I doing wrong?
//read the phone book and put the good value in the string
void Read_ADMIN_PHONE_NUMBER(){
String ADMIN_PHONE_NUMBER;
delay(1000);
sendATcommand("AT+CPBS=\"SM\"", 500) ; //Select the SIM phonebook
sendATcommand("AT+CPBR=1,99", 100) ; // To read ALL phonebook +CPBR: 1,"690506990",129,"ANDROID"---- exemple de reponse copier du serial monitor
if (1 == sscanf(fonaInBuffer, "+CPBR: %*s", &ADMIN_PHONE_NUMBER)) {
Serial.println(F("*****"));
Serial.println(ADMIN_PHONE_NUMBER);
} else {
Serial.println(F("**bad***"));
}
delay(2000);
}
My problem come from the sscanf.I can't put fonaInBuffer in ADMIN_PHONE_NUMBER
void Read_ADMIN_PHONE_NUMBER(){
String ADMIN_PHONE_NUMBER;
delay(1000);
sendATcommand("AT+CPBS=\"SM\"", 500) ; //Select the SIM phonebook
sendATcommand("AT+CPBR=1,99", 100) ; // To read ALL phonebook like example: +CPBR:1,"690506990",129,"ANDROID"
if (1 == sscanf(fonaInBuffer, "+CPBR: %*s", &ADMIN_PHONE_NUMBER)) {
Serial.println(ADMIN_PHONE_NUMBER);
} else {
Serial.println(F("**bad***"));
}
delay(2000);
The * in %*s means 'do not assign', and such conversion specifications are not counted in the number of successful conversions. Therefore, the sscanf() call will return 0 always (unless the scanned string is empty; then it returns EOF) because there is no active conversion.
Remove the *, or replace it with a suitable number. If the ADMIN_PHONE_NUMBER is a char ADMIN_PHONE_NUMBER[123];, then the number you use should be (at most) 122: %122s — because sscanf() writes a null after the 122 characters if it reads 122 characters in a single 'word'. It is not clear what String ADMIN_PHONE_NUMBER; means — more precise advice cannot be given because of that.
Reducing your code to close to an MCVE (How to create a Minimal, Complete, and Verifiable Example?):
#include <stdio.h>
int main(void)
{
char buffer[] = "+CPBR: 1,\"690506990\",129,\"ANDROID\"";
char number[64];
int n = sscanf(buffer, "+CPBR: %63s", number);
printf("n = %d: number = [%s]\n", n, number);
n = sscanf(buffer, "+CPBR: %*d , \" %63[^\"]", number);
printf("n = %d: number = [%s]\n", n, number);
return 0;
}
Sample output:
n = 1: number = [1,"690506990",129,"ANDROID"]
n = 1: number = [690506990]
Take your pick as to which you want to use. I've been liberal with allowed white space in the sscanf conversion specifications. You can be less liberal if you prefer.

Not able to reconstruct RGB565 image from raw data using QImage

I am trying to reconstruct an image from a file which is in Intel hex 386 format. After parsing the file all the data I am copying to a QByteArray and same array is used to create a QImage Object. But whatever image is which I got after reconstructing is not perfect. I am getting blue color instead of black, edges are not clear etc. The text file which I am parsing is a ram memory dump from STM32F4 controller (arm).The image is stored in RGB565 format.
Code to create the image:
{
QString strFilename;
Hex386Parser oFileParser;
strFilename = QFileDialog::getOpenFileName(this,"Select a file", QDir::homePath());
oFileParser.parseFile(strFilename, oByteArray);
QImage image(320, 240, QImage::Format_RGB16);
for (int y = 0; y < image.height(); y++)
{
memcpy(image.scanLine(y), oByteArray.constData() + y * image.bytesPerLine(),
image.bytesPerLine());
}
qDebug() <<"Size of the byte array is " <<oByteArray.size();
QLabel *label = new QLabel();
label->setPixmap(QPixmap::fromImage(image));
label->show();
}
Code to used to parse the file:
#define QT_NO_CAST_TO_ASCII
void Hex386Parser::parseFile(QString strFilename,QByteArray& ref_ByteArray)
{
QFile oFile(strFilename);
std::stringstream sstr;
QString strLength;
int unLength = 0, unAddress = 0,unDescriptor =0xFFFF,nIndex =0,nlineno=0;
if (oFile.open((QIODevice::ReadOnly | QIODevice::Text)))
{
QTextStream in(&oFile);
while (!in.atEnd())
{
QString line = in.readLine();
nIndex = 0;
nlineno++;
//unsigned char *pCharFrame = (unsigned char *)line.toStdString().c_str();
if (':' != line.at(nIndex))
{
// file corrupted
return;
}
nIndex++;
{
strLength = line.mid(nIndex, 2);
sstr << strLength.toStdString();
sstr << std::hex;
sstr >> unLength; // get length of the record
strLength.clear();
sstr.clear();
}
nIndex += 2;
unAddress = line.mid(nIndex,4).toInt(); // get address bytes
nIndex +=4;
unDescriptor = line.mid(nIndex, 2).toInt(); // get data descriptor
nIndex += 2;
switch(unDescriptor)
{
case data_record:
ref_ByteArray.append((line.mid(nIndex, unLength )));
// add data to bytearray
break;
case end_of_file_record:
break;
case extended_segment_address_record:
break;
case extended_linear_address_record:
break;
case start_linear_address_record:
break;
}
}
oFile.close();
}
}
What am I doing wrong??
The line contains hex string data representations where each byte is coded as two characters.
You want binary bytes. So, 2 * unLength symbols should be read from line. Then, that data string should converted to binary, for example:
{
case data_record:
QByteArray hex = line.mid(nIndex, 2 * unLength ).toLatin1();
QByteArray binary = QByteArray::fromHex(hex);
ref_ByteArray.append(binary);
...
}

Resources