PDA

View Full Version : Checking QByteArray for certain values



-Kyr0s-
25th October 2011, 10:56
Hey all!

I've been messing around like mad with my code, and it seems I can't get it right.

Basically, I have a QByteArray that contains the values that I have read from my device. The results are usually like this:

01 02 00 03 0x 0y 0z, where X is the DIO number, Y the Input or Output, and Z whether the DIO is turned on or off.

I have to check these values (Y and Z), to see whether it's on Input (read) or Output (write), and whether it's turned on (0) or off (1), so that when I start the application, the checkboxes are either checked or unchecked dependent on the values that have been read.

Here are the relevant code pieces:

Using setChecked() for the checkboxes once clicked on the connect button, which contains the functions to then check whether the DIOs are on or off.


void EcoDIOManager::connection_established()
{
QString strSuccess = "Successfully connected to ",
device_address = this->ui->txtbxIP->text();

this->enableObjects();
this->ui->txtbxSuccess->setText(strSuccess%device_address);
this->ui->chkDIO0->setChecked(this->read_bytes(0));
this->ui->chkDIO1->setChecked(this->read_bytes(1));
this->ui->chkDIO2->setChecked(this->read_bytes(2));
this->ui->chkDIO3->setChecked(this->read_bytes(3));
}


This is the function for checking the status (writing to the device which then responds with not 5 bytes, but 7 (6th is Input/Output, and 7th is On/Off).



bool EcoDIOManager::read_bytes(quint16 dio_n)
{
/*
* 1st byte: Command number, 1
* 2nd byte: Version, 2
* 3rd byte: Response only, any
* 4th byte: Data length, 1
* 5th byte: DIO channel
*/
QByteArray bytes;

bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes);

client.read(7);

return bytes[6] == '00';
}

I want to return true or false dependent on the status with the return, but I've tried everything I had in mind, but couldn't fix it.

E.g.:



return (bytes[8] == '0' & bytes[9] == '0');

return (bytes.mid(8,2) == '00');


Etc.

Any help would be greatly appreciated. =)

nix
25th October 2011, 11:26
I'm not sure I understand your problem, it doesn't seem realy clear to me.
However, it seems to me that you want look if a byte is 0 and to do that
bytes[8] == '0' is wrong (you are actually testing 0 == 0x30) try
bytes[8] == '\0' instead.

Spitfire
25th October 2011, 11:30
To test for 0x00 and 0x01 (or any other value)
if(!bytes[8] && !bytes[9])will work (only when both are 0x00 you'll get true).

-Kyr0s-
25th October 2011, 11:42
Hey,

Thanks to you both for the swift replies. Much appreciated. :)

Unfortunately neither suggestions worked. Your suggestion, Spitfire, somehow set all checkboxes to true. Not sure why.

Sorry for confusing you, Nix. I'll try to explain better.

Basically, I have a device which can be written to, and read from. Each of these have their own functions (write_bytes and read_bytes).

My intention is to check the state of the LEDs (Digital I/Os) by writing to the device, which then in turn, delivers a response. Unlike the writing, which has 5 bytes, the response has 7. These extra two bytes are the ones that return the state of being Input (which means you can only read) or Output (which means only writing), and whether the LEDs are turned on or off.

So, for instance, LED 4(number 3) are on and on output, the response would be (used .toHex() to see the results in the textbox):


01 02 00 03 03 (LED number) 01 (output) 00 (on)

So, when I start the application, I want it to check what the status of the checkboxes are (on or off) right away, so when the user presses connect, checkbox 4 is checked (using this example) and the rest turned off.

Here's the entire code if needed:



#include "ecodiomanager.h"
#include "ui_ecodiomanager.h"

const char NULLPTR = 0;
const quint16 iNULLPTR = 0;

EcoDIOManager::EcoDIOManager(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::EcoDIOManager)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowMinimizeButtonHint);
setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);

this->ui->pushDisconnect->setShown(false);

this->connect(ui->pushConnect, SIGNAL(clicked()), this, SLOT(pre_transfer()));
this->connect(ui->pushDisconnect, SIGNAL(clicked()), this, SLOT(disconnecting()));
this->connect(&client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connection_failed()));
this->connect(&client, SIGNAL(connected()), this, SLOT(connection_established()));
}

EcoDIOManager::~EcoDIOManager()
{
client.abort();
delete this->ui;
}

void EcoDIOManager::enableObjects()
{
this->ui->txtbxIP->setEnabled(false);
this->ui->txtbxPort->setEnabled(false);
this->ui->txtbxOutput->setEnabled(true);
this->ui->pushConnect->setShown(false);
this->ui->pushDisconnect->setShown(true);
this->ui->lblDIO0->setEnabled(true);
this->ui->lblDIO1->setEnabled(true);
this->ui->lblDIO2->setEnabled(true);
this->ui->lblDIO3->setEnabled(true);
this->ui->chkDIO0->setEnabled(true);
this->ui->chkDIO1->setEnabled(true);
this->ui->chkDIO2->setEnabled(true);
this->ui->chkDIO3->setEnabled(true);
this->ui->comboDIO0->setEnabled(true);
this->ui->comboDIO1->setEnabled(true);
this->ui->comboDIO2->setEnabled(true);
this->ui->comboDIO3->setEnabled(true);
}

void EcoDIOManager::disableObjects()
{
this->ui->txtbxIP->setEnabled(true);
this->ui->txtbxPort->setEnabled(true);
this->ui->pushConnect->setShown(true);
this->ui->pushDisconnect->setShown(false);
this->ui->lblDIO0->setEnabled(false);
this->ui->lblDIO1->setEnabled(false);
this->ui->lblDIO2->setEnabled(false);
this->ui->lblDIO3->setEnabled(false);
this->ui->chkDIO0->setEnabled(false);
this->ui->chkDIO1->setEnabled(false);
this->ui->chkDIO2->setEnabled(false);
this->ui->chkDIO3->setEnabled(false);
this->ui->comboDIO0->setEnabled(false);
this->ui->comboDIO1->setEnabled(false);
this->ui->comboDIO2->setEnabled(false);
this->ui->comboDIO3->setEnabled(false);
this->ui->txtbxOutput->setEnabled(false);
}

void EcoDIOManager::pre_transfer()
{
QString device_address = this->ui->txtbxIP->text();
quint16 device_port = this->ui->txtbxPort->text().toInt();

this->connectTo(device_address, device_port);
}

void EcoDIOManager::connectTo(QString host, quint16 port_n)
{
QHostAddress addr(host);

client.connectToHost(addr, port_n);
}

void EcoDIOManager::connection_established()
{
QString strSuccess = "Successfully connected to ",
device_address = this->ui->txtbxIP->text();

this->enableObjects();
this->ui->txtbxSuccess->setText(strSuccess%device_address);
this->ui->chkDIO0->setChecked(this->read_bytes(0));
this->ui->chkDIO1->setChecked(this->read_bytes(1));
this->ui->chkDIO2->setChecked(this->read_bytes(2));
this->ui->chkDIO3->setChecked(this->read_bytes(3));
}

void EcoDIOManager::connection_failed()
{
QString strFailed = "Failed to connect to ",
device_address = this->ui->txtbxIP->text();

this->ui->txtbxSuccess->setText(strFailed%device_address);
}

void EcoDIOManager::disconnecting()
{
client.abort();

this->ui->txtbxSuccess->clear();
this->ui->txtbxOutput->clear();
this->disableObjects();
}

void EcoDIOManager::write_dio(quint16 dio_n, char in_out, char on_off)
{
send_bytes(dio_n, in_out, on_off);
}

void EcoDIOManager::send_bytes(quint16 dio_n, char in_out, char on_off)
{
/*
* 1st byte: Command Number
* 2nd byte: Version
* 3rd byte: Only for response
* 4th byte: Data length
* 5th byte: DIO channel
* 6th byte: Input/Output (0 = Input, 1 = Output)
* 7th byte: On/Off (0 = On, 1 = Off)
*/
QByteArray bytes;

bytes.push_back(2);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(3);
bytes.push_back(dio_n);
bytes.push_back(in_out);
bytes.push_back(on_off);

client.write(bytes);
}

void EcoDIOManager::read_dio(quint16 dio_n)
{
read_bytes(dio_n);
}

bool EcoDIOManager::read_bytes(quint16 dio_n)
{
/* *******************************
* 1st byte: Command number, 1
* 2nd byte: Version, 2
* 3rd byte: Response only, any
* 4th byte: Data length, 1
* 5th byte: DIO channel
********************************/
QByteArray bytes;

bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes);

client.read(7);

return (!bytes[6] && !bytes[7]);
}

void EcoDIOManager::on_chkDIO0_toggled(bool checked)
{
if(checked && this->ui->comboDIO0->currentText() == "Input") this->read_dio(iNULLPTR);
else if(checked && this->ui->comboDIO0->currentText() == "Output") this->write_dio(iNULLPTR, 1, NULLPTR);
else if((!checked) && this->ui->comboDIO0->currentText() == "Input") this->read_dio(iNULLPTR);
else if((!checked) && this->ui->comboDIO0->currentText() == "Output") this->write_dio(iNULLPTR, 1, 1);
}

void EcoDIOManager::on_chkDIO1_toggled(bool checked)
{
if(checked && this->ui->comboDIO1->currentText() == "Input") this->read_dio(1);
else if(checked && this->ui->comboDIO1->currentText() == "Output") this->write_dio(1, 1, NULLPTR);
else if((!checked) && this->ui->comboDIO1->currentText() == "Input") this->read_dio(1);
else if((!checked) && this->ui->comboDIO1->currentText() == "Output") this->write_dio(1, 1, 1);
}

void EcoDIOManager::on_chkDIO2_toggled(bool checked)
{
if(checked && this->ui->comboDIO2->currentText() == "Input") this->read_dio(2);
else if(checked && this->ui->comboDIO2->currentText() == "Output") this->write_dio(2, 1, NULLPTR);
else if((!checked) && this->ui->comboDIO2->currentText() == "Input") this->read_dio(2);
else if((!checked) && this->ui->comboDIO2->currentText() == "Output") this->write_dio(2, 1, 1);
}

void EcoDIOManager::on_chkDIO3_toggled(bool checked)
{
if(checked && this->ui->comboDIO3->currentText() == "Input") this->read_dio(3);
else if(checked && this->ui->comboDIO3->currentText() == "Output") this->write_dio(3, 1, NULLPTR);
else if((!checked) && this->ui->comboDIO3->currentText() == "Input") this->read_dio(3);
else if((!checked) && this->ui->comboDIO3->currentText() == "Output") this->write_dio(3, 1, 1);
}

void EcoDIOManager::on_pushClear_clicked()
{
this->ui->txtbxOutput->clear();
}

Spitfire
25th October 2011, 11:58
if(bytes[6])
{
// LED off
}
else
{
// LED on
}Also when you read 7 bytes index of last byte is 6 not 7, that's why all checkboxes are set to true.

-Kyr0s-
25th October 2011, 12:07
I see, indeed. Thanks for reminding me. =)

And unfortunately, that didn't work either. I went into debug mode and added bytes[6] to my Watch List, and it appears that it constantly has "<no such value>". The checkboxes, even with index 6, are all set to true still, too. :(

Spitfire
25th October 2011, 12:18
when you're reading some unasigned value you're getting some memory garbage, most likely it will be set to something else than 0x00 that's why it returns true.

When you look at your code you have a 5 bytes you write to the client, then you read 7 bytes, but the read fucntion do not assing return value to anything. shouldn't it be

QByteArray bytes;

bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes);

bytes = client.read(7);?

-Kyr0s-
25th October 2011, 12:23
Thanks for the quick reply once more. :)

Good call. I didn't assign it to the bytes variable at all. I've changed it, but that does not change the case however; all checkboxes are still either checked or unchecked (as in, all of them), and the Watch List shows <no such value> still, regardless of what index I use (tried index 0, 1, 3, 6, etc.) :/

Spitfire
25th October 2011, 12:31
I think you're reading from the device wrong or something of that sort.

Look at what your read() function returns (assign it to new bytearray and print).
Make sure that you're geting what you expect and follow from there.

the if(bytes[x]) will do what you want as long as you check correct and valid byte.

-Kyr0s-
25th October 2011, 13:15
Unless I completely misunderstand the QBA, I receive precisely what I expected. I've done as you adviced, and the following printed out in my output textbox:



01020003000100
01020003000101

I wrote to the DIO (output) first to turn it on, and then read it (input) to see the values in the textbox. The first line is DIO turned on, the second is off.

So, assuming the returned value doesn't include anything beside that in a secretive manner, the reading works as it is supposed to. :S

For debug purposes, I disabled the function calls for the other 3 checkboxes, it is still the same as before.

7035

Spitfire
25th October 2011, 13:48
Try that:
void EcoDIOManager::connection_established()
{
QString strSuccess = "Successfully connected to ",
device_address = this->ui->txtbxIP->text();

this->enableObjects();
this->ui->txtbxSuccess->setText(strSuccess%device_address);
this->ui->chkDIO0->setChecked(this->isOn(0));
this->ui->chkDIO1->setChecked(this->isOn(1));
this->ui->chkDIO2->setChecked(this->isOn(2));
this->ui->chkDIO3->setChecked(this->isOn(3));
}

QByteArray EcoDIOManager::read_bytes(quint16 dio_n)
{
QByteArray bytes;
bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes);

return client.read(7);
}

bool EcoDIOManager::isOn( quint16 dio_n )
{
bool isOn = false;
const char ON = 0x00;
const int switchIndex = 6;

QByteArray readBytes = this.read_bytes(dio_n);

if(readBytes.size() > switchIndex && readBytes[switchIndex] == ON)
isOn = true;

return isOn;
}

-Kyr0s-
25th October 2011, 14:05
Thanks for taking your time to assist me, once again much appreciated.

Seems that it still is refusing to work. I've put a breakpoint in bool EcoDIOManager::isOn(quint16 dio_n), and the following were the results once it returned at the end:



ON: 0 '\0'
dio_n: 0
isOn: false
readBytes: ""
switchIndex: 6
this: @0x28fe48


I guess readBytes isn't supposed to be an empty string?

Spitfire
25th October 2011, 14:31
No it should not :)

To make sure that everything else works just your example values into the array and see if checkbox is set correctly.
then investigate around that place, this array should contain exacly what you expect to see.

-Kyr0s-
31st October 2011, 10:37
Hey again,

I've been pulling my hair out for these past few days, but it seems I just can't find the problem.

readBytes is still an empty string after this bit:



bool EcoDIOManager::isOn(quint16 dio_n)
{
bool isOn = false;
const char ON = 0x00;
const int switchIndex = 6;

QByteArray readBytes = this->read_bytes(dio_n);

if(readBytes.size() > switchIndex && readBytes[switchIndex] == ON)
{
isOn = true;
}
else
{
isOn = false;
}

return isOn;
}


I suspected that the problem lies with the call of the read_bytes(dio_n) function.



QByteArray EcoDIOManager::read_bytes(quint16 dio_n)
{
/* *******************************
* 1st byte: Command number, 1
* 2nd byte: Version, 2
* 3rd byte: Response only, any
* 4th byte: Data length, 1
* 5th byte: DIO channel
********************************/
QByteArray bytes;
bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes);

this->ui->txtbxOutput->append(client.read(7).toHex());

return client.read(7);
}


I've also tried return client.read(7).toHex();.

The function on itself parses the values (the string of bytes) properly in the textbox (I can see the values I need without any issues), yet it cannot be used in EcoDIOManager::isOn().

Any suggestions as to why this might occur at all? It's driving me crazy. I've nearly not had as many problems figuring out how to write as opposed to apply a process to reading. :(

-Kyr0s-
31st October 2011, 13:31
Never mind, false alarm with this post. Wasn't called twice. >_<

Spitfire
31st October 2011, 14:33
What happens if you call client.read(7) twice in a row?
Do you get the same output twice?

Instead of returning the output straight away create temporary variable and return that variable and see what happens.
IMHO it should make no difference.

-Kyr0s-
31st October 2011, 15:36
I've tried what you suggested, but to no avail.



QByteArray bytes;

bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(NULLPTR);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes);
buffer = client.read(7);
this->ui->txtbxOutput->append(buffer.toHex());

return buffer;


Buffer is a QByteArray.

Also, maybe worth noting, is that the textbox won't show any output, until I click one of the checboxes. Oddly enough, when the application starts, it does go through read_bytes() and thus, is supposed to print out the string. It doesn't do that, only after having clicked a checkbox it would, whilst it goes through the same function as start-up.

I don't know why it doesn't immediately parse the output in the textbox, as it's pretty much the very same functions..

-Kyr0s-
14th November 2011, 10:49
Hello,

I've decided to redo the application to see where it goes wrong. It seems everything works fine; it writes and reads the appropriate number of bytes. Reading is supposed to send 5 bytes, and receive 7 in return.

However, once it accesses the isOn() method, these bytes turn into 20.

I suspect it's due to this:



this->ui->chkDIO0->setChecked(this->isOn(0));
this->ui->chkDIO1->setChecked(this->isOn(1));
this->ui->chkDIO2->setChecked(this->isOn(2));
this->ui->chkDIO3->setChecked(this->isOn(3));


Each time it goes through reading bytes, it sends 5 bytes, so 5x4 is 20. I guess that makes sense, but even if it didn't, readBytes in isOn() remains to be an empty string.



QByteArray DIOMoxaBoard::read_bytes(quint16 dio_n)
{
/* *******************************
* 1st byte: Command number, 1
* 2nd byte: Version, 2
* 3rd byte: Response only, any
* 4th byte: Data length, 1
* 5th byte: DIO channel
********************************/

QByteArray bytes;

bytes.push_back(1);
bytes.push_back(2);
bytes.push_back(_NULL);
bytes.push_back(1);
bytes.push_back(dio_n);

client.write(bytes); // successfully writes 5 bytes if it doesn't access isOn()

return client.read(7).toHex(); //successfully reads 7 bytes if it doesn't access isOn()
}

bool DIOMoxaBoard::isOn(quint16 dio_n)
{
bool isOn = false;
const char ON = 0x00;
const int switchIndex = 6;

QByteArray readBytes = this->read_bytes(dio_n); //readBytes remains ""

if(readBytes.size() > switchIndex && readBytes[switchIndex] == ON)
isOn = true;
else
isOn = false;

return isOn;
}


Any idea how to avoid sending 20 bytes due to calling isOn() four times for each checkbox (I've tried pause/sleep already), as well as being able to pass on the buffer values to the new variable readBytes, instead of having it remain as an empty string?


Thanks in advance!

gouyoku
14th November 2011, 11:42
Hi

I am no expert but here are some of my thoughts:
You shouldn't use client.read() right after client.write(). Getting data over the network is asynchronous so you need to give it time to arrive.
If I were to guess, you are using QAbstractSocket as your client. If so, you should use its signal readyRead() to know when to read the answer.
This would explain why your program prints out your string only after you click a checkbox. At first pass of read_bytes() you don't have anything to read() as the answer have not arrived yet.
To set your checkboxes you will need to implement some queue, for example: isOn(0); readyRead()->setchecked()->isOn(1); readyRead()->setChecked()->isOn(2); and so on.

Hope it helps.

Lesiok
14th November 2011, 11:47
In line 21 You are converting binary data to hex (that is to say hexadecimal character notation). So method read_bytes() returns more when 2 x 7 bytes. Next in line 32 You compare this character to binary value ON. This have no sense.