PDA

View Full Version : Architecture problem using threads, custom events and signals/slots



yellowmat
6th March 2006, 12:31
So it's a bit complicated but I will do my best to explain my problem.

I am developping some classes to read/write data over a serial port. I am using the Win_QextSerialPort class (and its base class) that can be found on sourceforge. The only thing I'll tell about this class is that it uses in quite every function the QMutex lock/unlock mechanism.

So, go on, I ddi developp a class called CSerialDataFrameDecoder which uses the Win_QextSerialPort. Mt class inherits from QThread and reimplements the run() method in which I keep reading the serial port buffer and process its data. The process is quite simple, I am looking for some start and stop sequence and create some CSerialDataFrame (it is a class I did developp too). Once a CSerialDataFrame is created I post a custom event to object owner of the CSerialDataFrameDecoder. At the beginning I wished to use the signals/slots mechanism but it is not possible with threads. To test my classes I developped a simple GUI application which reimplements the customEvent function and just display the content of the CSerialDataFrame contained in my custom event. This test application works fine in debug version but it sometime crashes in release version (access violation) ... this is my first problem, but I decided to focus on that problem later on (also because I don't really know how to find out the origin of this problem) because the debug version works.

After that I decided to developp another class called CSerialPort which inherits from QObject. This class encapsulates the CSerialDataFrameDecoder, stores all CSerialDataFrame into a stack and posts a custom event when it receives a custom event from the CSerialDataFrameDecoder. This class has at least two functions start and stop which respectively starts and stops the CSerialDataFrameDecoder thread. I also developp a GUI test application with a reimplentation of the function customEvent which displays the CSerialDataFrame content using a QListBox object. Here is my second problem because the application blocks, it does not bug or crashes, it just blocks so I must stop and start the CSerialDataFrameDecoder. The application never blocks at the same time and I don't know how to find out the problem.

If someone have a clue to the answer (even the answer itself) ... thanks in advance.

yellowmat
6th March 2006, 13:13
I did a simple change in my code so that the release version is now working with no problem.

In fact I was allocating a block of n bytes, reading a buffer of n bytes and writing a null character at position n. Now I allocated a block of n+1 bytes.

My application still blocks (second problem), so if someone could help me with this it would be great.

Thanks

yellowmat
6th March 2006, 15:27
Using qDebug helped me to see what is happening and understand that my application does not blocks but seems to be lost. Let's my explain, many times I see that the serialDataFrameDecoded function is called but never returned. I say that because I can read the following messages :

qDebug("In CSerialDataFrameDecoder::serialDataFrameDecoded. Before : QApplication::postEvent(objectOwner, ce);");
qDebug("In CSerialDataFrameDecoder::serialDataFrameDecoded. After : QApplication::postEvent(objectOwner, ce);");
When my application seems to be blocked, I see that the message that should be writen after the serialDataFrameDecoded function call is never writen. Why ? That is what I do not understand. So if someone could help me to understand I whould be great. thanks.

Here is the code of the run() reimplementation function of my thread, and also the serialDataFrameDecoded fnuction which trigger the custom event :

void CSerialDataFrameDecoder::run()
{
int bytesToRead = -1;
Q_LONG bytesRead = 0;

int startSequenceIndex = -1;
int stopSequenceIndex = -1;

QString buffer("");
QString frame("");

while( bMustDecode == true )
{
qDebug("In CSerialDataFrameDecoder. After : while( bMustDecode == true )");
// TO DO
// ... read the com port buffer
// ... decode frames
// ... trigger a custom events each time a complete frame is decoded
if( comPort != 0 )
{
qDebug("In CSerialDataFrameDecoder::run(). After : if( comPort != 0 )");
if( comPort->isOpen() )
{
qDebug("In CSerialDataFrameDecoder::run(). After : if( comPort->isOpen() )");
// Get the number of bytes to read
bytesToRead = comPort->bytesWaiting();
qDebug("In CSerialDataFrameDecoder::run(). After : bytesToRead = comPort->bytesWaiting();");

if( bytesToRead != 0 )
{
// Here we can do some stuff
char* buffData = 0;
buffData = new char[bytesToRead+1];
qDebug("In CSerialDataFrameDecoder::run(). After : buffData = new char[bytesToRead+1];");

bytesRead = comPort->readBlock(buffData, bytesToRead);
qDebug("In CSerialDataFrameDecoder::run(). After : bytesRead = comPort->readBlock(buffData, bytesToRead);");
buffData[bytesRead] = '\0';
comPort->flush();
qDebug("In CSerialDataFrameDecoder::run(). After : comPort->flush();");

buffer = buffData;

while( (buffer.length() != 0) && (bMustDecode == true) )
{
qDebug("In CSerialDataFrameDecoder::run(). After : while( (buffer.length() != 0) && (bMustDecode == true) )");

startSequenceIndex = buffer.find(startSequence, 0, false);
stopSequenceIndex = buffer.find(stopSequence, 0, false);
qDebug("In CSerialDataFrameDecoder::run(). startSequenceIndex=%d, stopSequenceIndex=%d", startSequenceIndex, stopSequenceIndex);
// Check previous indexes to determine if the frame is complete or not
// CASE 1 : start and stop indexex are found, stop index is greather than start index
// !!! A complete frame can be extracted !!!

// CASE 2 and CASE 5 : stop index is found but not start index
// !!! An incomplete frame can be extracted, its beginning is missing !!!

// CASE 3 : start index is found but not stop
// !!! An incomplete frame can be extracted, its ending is missing

// CASE 4 : start index neither stop are found
// !!! An incomplete frame can be extracted, its beginning and ending are missing

// CASE 1
if( (startSequenceIndex == 0) && (stopSequenceIndex != -1) && (stopSequenceIndex>startSequenceIndex) )
{
qDebug("In CSerialDataFrameDecoder::run(). Entry : CASE1");
frame = buffer.mid(startSequenceIndex, (stopSequenceIndex-startSequenceIndex)+1);
buffer.remove(0, stopSequenceIndex+stopSequence.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusComplete);
qDebug("In CSerialDataFrameDecoder::run(). Before : this->serialDataFrameDecoded(aFrame);");
this->serialDataFrameDecoded(aFrame);
qDebug("In CSerialDataFrameDecoder::run(). After : this->serialDataFrameDecoded(aFrame);");
}
// CASE 2
else if( (startSequenceIndex == -1) && (stopSequenceIndex != -1) )
{
qDebug("In CSerialDataFrameDecoder::run(). Entry : CASE2");
frame = buffer.left(stopSequenceIndex+1);
buffer.remove(0, stopSequenceIndex+stopSequence.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheEndingPar t);
qDebug("In CSerialDataFrameDecoder::run(). Before : this->serialDataFrameDecoded(aFrame);");
this->serialDataFrameDecoded(aFrame);
qDebug("In CSerialDataFrameDecoder::run(). After : this->serialDataFrameDecoded(aFrame);");
}
// CASE 3
else if( (startSequenceIndex != -1) && (stopSequenceIndex == -1) )
{
qDebug("In CSerialDataFrameDecoder::run(). Entry : CASE3");
frame = buffer.mid(0, buffer.length());
buffer.remove(0, buffer.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheBeginning Part);
qDebug("In CSerialDataFrameDecoder::run(). Before : this->serialDataFrameDecoded(aFrame);");
this->serialDataFrameDecoded(aFrame);
qDebug("In CSerialDataFrameDecoder::run(). After : this->serialDataFrameDecoded(aFrame);");
}
// CASE 4
else if( (startSequenceIndex == -1) && (stopSequenceIndex == -1) )
{
qDebug("In CSerialDataFrameDecoder::run(). Entry : CASE4");
frame = buffer;
buffer.remove(frame);

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheMiddlePar t);
qDebug("In CSerialDataFrameDecoder::run(). Before : this->serialDataFrameDecoded(aFrame);");
this->serialDataFrameDecoded(aFrame);
qDebug("In CSerialDataFrameDecoder::run(). After this->serialDataFrameDecoded(aFrame);");
}
// CASE 5
else if( (startSequenceIndex != -1) && (stopSequenceIndex != -1) && (stopSequenceIndex<startSequenceIndex) )
{
qDebug("In CSerialDataFrameDecoder::run(). Entry : CASE5");
frame = buffer.left(stopSequenceIndex+1);
buffer.remove(0, stopSequenceIndex+stopSequence.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheEndingPar t);
qDebug("In CSerialDataFrameDecoder::run(). Before : this->serialDataFrameDecoded(aFrame);");
this->serialDataFrameDecoded(aFrame);
qDebug("In CSerialDataFrameDecoder::run(). After : this->serialDataFrameDecoded(aFrame);");
}
}
}
}
}
}
}


void CSerialDataFrameDecoder::serialDataFrameDecoded(co nst CSerialDataFrame& frame)
{
// Allocate some memory for the data
CSerialDataFrame* data = 0;
data = new CSerialDataFrame(frame);
// The allocated memory for the data MUST by freed by the receiver

// Allocate some memory for the custom event
QCustomEvent* ce = 0;
ce = new QCustomEvent(1002);
ce->setData(data);

// Send the event
qDebug("In CSerialDataFrameDecoder::serialDataFrameDecoded. Before : QApplication::postEvent(objectOwner, ce);");
QApplication::postEvent(objectOwner, ce);
qDebug("In CSerialDataFrameDecoder::serialDataFrameDecoded. After : QApplication::postEvent(objectOwner, ce);");
// The allocated memory for the custum event is freed by Qt so the receiver neither the sender MUST NOT free it
}

jacek
6th March 2006, 16:32
What member variables does CSerialDataFrame contain? Maybe it uses some Qt classes with implicit sharing?

yellowmat
6th March 2006, 16:40
Here is the declaration of my CSerialDataFrame class

#ifndef _SERIAL_DATA_FRAME_H
#define _SERIAL_DATA_FRAME_H

#include <qstring.h>

class CSerialDataFrame
{
// Constructor / Destructor
public:
CSerialDataFrame(QString data="");
CSerialDataFrame(const CSerialDataFrame&);

const CSerialDataFrame& operator+= (const CSerialDataFrame&);
const CSerialDataFrame& operator= (const CSerialDataFrame&);
bool operator== (const CSerialDataFrame&);


// Functions
void setSerialDataFrameValue(QString);
QString getSerialDataFrameValue() const;

void setSerialDataFrameStatus(int);
int getSerialDataFrameStatus() const;

// Members
public:
static const int SerialDataFrameStatusNotDefined;
static const int SerialDataFrameStatusComplete;
static const int SerialDataFrameStatusIncompleteHasJustTheBeginning Part;
static const int SerialDataFrameStatusIncompleteHasJustTheEndingPar t;
static const int SerialDataFrameStatusIncompleteHasJustTheMiddlePar t;

private:
QString serialDataFrameValue;
int serialDataFrameStatus;
};

#endif

and the implementation

#include "SerialDataFrame.h"

const int CSerialDataFrame::SerialDataFrameStatusNotDefined = -1;
const int CSerialDataFrame::SerialDataFrameStatusComplete = 0;
const int CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart = 1;
const int CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheEndingPart = 2;
const int CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart = 3;


// Constructor / Destructor
CSerialDataFrame::CSerialDataFrame(QString data):
serialDataFrameValue(data),
serialDataFrameStatus(SerialDataFrameStatusNotDefi ned)
{
}


CSerialDataFrame::CSerialDataFrame(const CSerialDataFrame& frame)
{
this->serialDataFrameValue = frame.serialDataFrameValue;
this->serialDataFrameStatus = frame.serialDataFrameStatus;
}


const CSerialDataFrame& CSerialDataFrame::operator+=(const CSerialDataFrame& frame)
{
this->serialDataFrameValue += frame.serialDataFrameValue;

// Beginnig part + middle part = beginning part
if( (this->serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart) && (frame.serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart) )
{
this->serialDataFrameStatus = CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart;
}
// Beginning part + ending part = complete part
else if( (this->serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart) && (frame.serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheEndingPart) )
{
this->serialDataFrameStatus = CSerialDataFrame::SerialDataFrameStatusComplete;
}
// Middle part + middle part = middle part
else if( (this->serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart) && (frame.serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart) )
{
this->serialDataFrameStatus = CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart;
}
// Middle part + ending part = ending part
else if( (this->serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart) && (frame.serialDataFrameStatus == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheEndingPart) )
{
this->serialDataFrameStatus = CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheEndingPart;
}
// Other cases are not defined
else
this->serialDataFrameStatus = CSerialDataFrame::SerialDataFrameStatusNotDefined;

return *this;
}


const CSerialDataFrame& CSerialDataFrame::operator=(const CSerialDataFrame& frame)
{
this->serialDataFrameValue = frame.serialDataFrameValue;
this->serialDataFrameStatus = frame.serialDataFrameStatus;

return *this;
}


bool CSerialDataFrame::operator==(const CSerialDataFrame& frame)
{
if( (this->serialDataFrameValue == frame.serialDataFrameValue) && (this->serialDataFrameStatus == frame.serialDataFrameStatus) )
return true;
else
return false;
}


// Functions
void CSerialDataFrame::setSerialDataFrameValue(QString value)
{
serialDataFrameValue = value;
}


QString CSerialDataFrame::getSerialDataFrameValue() const
{
return serialDataFrameValue;
}


void CSerialDataFrame::setSerialDataFrameStatus(int value)
{
serialDataFrameStatus = value;
}


int CSerialDataFrame::getSerialDataFrameStatus() const
{
return serialDataFrameStatus;
}


All the afternoon I was looking logs delivered by qDebug. What happening is sometime strange. Sometime I can see that my class CSerialDataFrameDecoder is at a particular code line and the next step it is at a previous code line ... and it is not possible :( :confused:

yellowmat
6th March 2006, 16:48
An example of a very strange log :
Sometime qDebug trace tell me that

qDebug("In CSerialDataFrameDecoder::run(). Entry : CASE1");
and the next trace tell me

qDebug("In CSerialDataFrameDecoder::run(). After : while( (buffer.length() != 0) && (bMustDecode == true) )");

I don't understand what is going on and what's wrong with my code ... I thought what I was coding was not that difficult.

jacek
6th March 2006, 16:59
QString serialDataFrameValue;
QString data is implicitly shared.

Try this:
CSerialDataFrame::CSerialDataFrame( const CSerialDataFrame& frame )
{
serialDataFrameValue = QDeepCopy<QString>( frame.serialDataFrameValue );
serialDataFrameStatus = frame.serialDataFrameStatus;
}

const CSerialDataFrame& CSerialDataFrame::operator=(const CSerialDataFrame& frame)
{
serialDataFrameValue = QDeepCopy<QString>( frame.serialDataFrameValue );
serialDataFrameStatus = frame.serialDataFrameStatus;
return *this;
}


Sometime I can see that my class CSerialDataFrameDecoder is at a particular code line and the next step it is at a previous code line ... and it is not possible
I'm not sure if qDebug() is thread safe.

yellowmat
6th March 2006, 17:07
The same problem occurs despite the modifucation you proposed me to implement.

Could I post you the source code of my CSerialDataFrameDecoder and CSerialPort ? There must be something that is not architecturaly clear and clean ?

yellowmat
6th March 2006, 17:09
Sorry, I forget to ask but, why should I use QDeepCopy in my CSerialDataFrame code ? Maybe I should use it somewhere else to because I use QString quite often in my code :o ?

Another question, what should I use instead of qDebug to keep a trace of what (should) going on ?

jacek
6th March 2006, 17:15
why should I use QDeepCopy in my CSerialDataFrame code ?
Because you are passing a implicitly shared object (i.e. QString) between two threads.

http://doc.trolltech.com/3.3/threads.html#9
http://doc.trolltech.com/3.3/shclass.html


Another question, what should I use instead of qDebug to keep a trace of what (should) going on ?
First you check whether you can use qDebug() or not, but you can always try writing messages to a text file.

yellowmat
6th March 2006, 18:27
Sorry Jacek, I know that I should find out by myself but I don't manage to ... so I need some more help please.

I have modified the source code of my CSerialDataFrame in order to make a deep copy when I call the copy constructor, the equal operator but also the setSerialDataFrameValue. Here I am sure that the copy is deep.

I have analysed my code and I don't know what could be wrong but I'm sure there is something wrong ... it is starting to drive me crazy :o

First, the CSerialDataDecoder code looks fine

void CSerialDataFrameDecoder::run()
{
int bytesToRead = -1;
Q_LONG bytesRead = 0;

int startSequenceIndex = -1;
int stopSequenceIndex = -1;

QString buffer("");
QString frame("");

while( bMustDecode == true )
{
msleep(1);
// TO DO
// ... read the com port buffer
// ... decode frames
// ... trigger a custom events each time a complete frame is decoded
if( comPort != 0 )
{
if( comPort->isOpen() )
{
// Get the number of bytes to read
bytesToRead = comPort->bytesWaiting();

if( bytesToRead != 0 )
{
// Here we can do some stuff
char* buffData = 0;
buffData = new char[bytesToRead+1];

bytesRead = comPort->readBlock(buffData, bytesToRead);
buffData[bytesRead] = '\0';
comPort->flush();

buffer = buffData;

while( (buffer.length() != 0) && (bMustDecode == true) )
{
msleep(1);

startSequenceIndex = buffer.find(startSequence, 0, false);
stopSequenceIndex = buffer.find(stopSequence, 0, false);
// Check previous indexes to determine if the frame is complete or not
// CASE 1 : start and stop indexex are found, stop index is greather than start index
// !!! A complete frame can be extracted !!!

// CASE 2 and CASE 5 : stop index is found but not start index
// !!! An incomplete frame can be extracted, its beginning is missing !!!

// CASE 3 : start index is found but not stop
// !!! An incomplete frame can be extracted, its ending is missing

// CASE 4 : start index neither stop are found
// !!! An incomplete frame can be extracted, its beginning and ending are missing

// CASE 1
if( (startSequenceIndex == 0) && (stopSequenceIndex != -1) && (stopSequenceIndex>startSequenceIndex) )
{
frame = buffer.mid(startSequenceIndex, (stopSequenceIndex-startSequenceIndex)+1);
buffer.remove(0, stopSequenceIndex+stopSequence.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusComplete);
this->serialDataFrameDecoded(aFrame);
}
// CASE 2
else if( (startSequenceIndex == -1) && (stopSequenceIndex != -1) )
{
frame = buffer.left(stopSequenceIndex+1);
buffer.remove(0, stopSequenceIndex+stopSequence.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheEndingPar t);
this->serialDataFrameDecoded(aFrame);
}
// CASE 3
else if( (startSequenceIndex != -1) && (stopSequenceIndex == -1) )
{
frame = buffer.mid(0, buffer.length());
buffer.remove(0, buffer.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheBeginning Part);
this->serialDataFrameDecoded(aFrame);
}
// CASE 4
else if( (startSequenceIndex == -1) && (stopSequenceIndex == -1) )
{
frame = buffer;
buffer.remove(frame);

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheMiddlePar t);
this->serialDataFrameDecoded(aFrame);
}
// CASE 5
else if( (startSequenceIndex != -1) && (stopSequenceIndex != -1) && (stopSequenceIndex<startSequenceIndex) )
{
frame = buffer.left(stopSequenceIndex+1);
buffer.remove(0, stopSequenceIndex+stopSequence.length());

CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheEndingPar t);
this->serialDataFrameDecoded(aFrame);
}
}
}
}
}
}
}


void CSerialDataFrameDecoder::serialDataFrameDecoded(co nst CSerialDataFrame& frame)
{
// Allocate some memory for the data
CSerialDataFrame* data = 0;
data = new CSerialDataFrame(frame);
// The allocated memory for the data MUST by freed by the receiver

// Allocate some memory for the custom event
QCustomEvent* ce = 0;
ce = new QCustomEvent(1002);
ce->setData(data);

// Send the event
QApplication::postEvent(objectOwner, ce);
// The allocated memory for the custum event is freed by Qt so the receiver neither the sender MUST NOT free it
}


In my opinion its behaviour is correct and match with the code. It just flush the buffer, reads data from the Win_QextSerialPort and add a null terminating char. Maybe should I make a deep copy of the buffer content and store it into my QString variable buffer ? After that it processes the buffer variable and looks for a start and stop sequence char, extract the data and then update the buffer variable (it removes the extracted data). After extracting the serial data frame, it allocated some memory for a such a frame, make a copy of it and then send this frame using a custom event to the owner of the CSerialDataFrameDecoder which is the CSerialPort object. At this point should I do something else in that code ? (Other deep copy ?, mutex ?).

yellowmat
6th March 2006, 18:28
Hmm, the other suspicious point may be the CSerialPort class which code is below

#include "SerialPort.h"
#include <qapplication.h>
#include <qstring.h>
#include ".\\..\\..\\DataParser\\WidgetSource\\DataParser.h"
#include ".\\qextserialbase.h"
#include ".\\win_qextserialport.h"
#include ".\\..\\..\\SerialDataFrameDecoder\\WidgetSource\\S erialDataFrameDecoder.h"



// Constructor / Destructor
CSerialPort::CSerialPort(QObject* parent/*=0*/, const char* name/*=0*/, const QString& configFileName/*=""*/):
QObject(parent, name),
parser(0),
port(0),
decoder(0),
isDecoding(false),
readSerialDataFrameStartSequence(""),
readSerialDataFrameStopSequence(""),
writeSerialDataFrameStartSequence(""),
writeSerialDataFrameStopSequence("")
{
// Create a serial port object
if( port == 0 )
port = new Win_QextSerialPort(sTemp.ascii(), comSettings);

// Open the port
if( port != 0 )
port->open();

// Flush the com port$
if( port->isOpen() )
port->flush();

// Create a serial data frame decoder
if( decoder == 0 )
decoder = new CSerialDataFrameDecoder(this, port, readSerialDataFrameStartSequence, readSerialDataFrameStopSequence);
}


CSerialPort::~CSerialPort()
{
// Free memory allocated
// ... to the data parser
if( parser != 0 )
{
delete parser;
parser = 0;
}

// ... the serial data frame decoder
if( decoder != 0 )
{
delete decoder;
decoder = 0;
}

// ... to the serial port
if( port != 0 )
{
delete port;
port = 0;
}
}


// Slots
/*
Starts the serial data frames decoding only if all the following conditions are met
There is no decoding running yet
A serial port object has been instanciated and is currently open
*/
void CSerialPort::startDecoding()
{
if( !isDecoding && port->isOpen() )
{
decoder->startDecode();
isDecoding = true;
}
}


/*
Stops the serial data frames decoding only if all the following conditions are met
There is a decoding running
A serial port object has been instanciated and is currently open
*/
void CSerialPort::stopDecoding()
{
if( isDecoding && port->isOpen() )
{
decoder->stopDecode();
isDecoding = false;
}
}


/*
Return a copy of the first serial data frame stored in the fifo.

Removes (pop) the returned serial data frame from the fifo
*/
CSerialDataFrame CSerialPort::read()
{
// Get the first serial data frame in the stack
// Create a copy of it
// Delete it from the stack if it is a complete serial data frame

// Return the serial data frame
return CSerialDataFrame();
}


/*
Write the serial data frame on the current opened serial port.

For the moment this function just write "as it" the serial data frame, it may in a close future process the serial data
frame before sending it on the serial port.
*/
void CSerialPort::write(CSerialDataFrame)
{
}


// Functions
/*
Return the number of serial data frame stored in the fifo
*/
int CSerialPort::count()
{
return serialDataFrames.count();
}


/*
Return the open state of the port
true = the port is open
false = the port is closed
*/
bool CSerialPort::isOpen()
{
return port->isOpen();
}


/*
Intercept the event from CSerialDataFrameDecoder
The only type of custom events we want to deal with is 1002
*/
void CSerialPort::customEvent(QCustomEvent* e)
{
if( e->type() == 1002 )
{
CSerialDataFrame* frame = (CSerialDataFrame*)e->data();

// Here we process the serial data frame defragmentation if necessary
if( frame->getSerialDataFrameStatus() == CSerialDataFrame::SerialDataFrameStatusComplete )
{
// If the serial data frame is complete we just append it to the stack
serialDataFrames.append( CSerialDataFrame(*frame) );
serialDataFrameDecoded(*frame);
}
else if( frame->getSerialDataFrameStatus() == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart )
{
// We can append the current serial data frame in the two following cases
// Case 1 : there is no frame in the stack yet
// Case 2 : the last frame appended in the stack is a complete one
if( (serialDataFrames.count() == 0) || (serialDataFrames.last().getSerialDataFrameStatus( ) == CSerialDataFrame::SerialDataFrameStatusComplete) )
{
serialDataFrames.append( CSerialDataFrame(*frame) );
serialDataFrameDecoded(*frame);
}
}
else if( frame->getSerialDataFrameStatus() == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheMiddlePart )
{
// We can append the current serial data frame to the last appended one in the following case
// Case 1 : the last frame has just the beginning part
if( (serialDataFrames.count() > 0) && (serialDataFrames.last().getSerialDataFrameStatus( ) == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart) )
{
serialDataFrames.last() += *frame;
serialDataFrameDecoded(*frame);
}
}
else if( frame->getSerialDataFrameStatus() == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheEndingPart )
{
// We can append the current serial data frame to the last appended one in the following case
if( (serialDataFrames.count() > 0) && (serialDataFrames.last().getSerialDataFrameStatus( ) == CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheBeginningPart) )
{
serialDataFrames.last() += *frame;
serialDataFrameDecoded(*frame);
}
}
}
}


void CSerialPort::serialDataFrameDecoded(const CSerialDataFrame& frame)
{
// Allocate some memory for the data
CSerialDataFrame* data = 0;
data = new CSerialDataFrame(frame);
// The allocated memory for the data MUST be freed by the receiver

// Allocate some memory for the custom event
QCustomEvent* ce = new QCustomEvent(1002);
ce->setData(data);

// Send the event
QApplication::postEvent(parent(), ce);
// The allocated memory for the custum event is freed by Qt so the receiver neither the sender MUST NOT free it
}

... but it does not a lot of thing, just analyse the frame received, concatenate some if necessary and add them to a QValueList (I know that the QValueList is an implicitly shared class as QString but as far as deep copy are made into CSerialDataFrame, must I apply some deep operations on my QValueList variables too ?).

I really need some help, maybe more than a simple guide line to lead me to the solution. I am over this problem since friday morning and I don't understant everything, except that I need some help :D

jacek
6th March 2006, 20:31
You create Win_QextSerialPort instance in one thread, but then use it in a different one --- are you sure this is correct?

yellowmat
6th March 2006, 21:36
I don't know the "good-way-programming" with thread so I don't know if it is correct to do the way I do. I didn't it was a problem to create an instance in one thread and use it in another ... I didn't knew since it brings me trouble with data sharing and so on :(

jacek
6th March 2006, 23:13
I don't know the "good-way-programming" with thread so I don't know if it is correct to do the way I do. I didn't it was a problem to create an instance in one thread and use it in another ...
It looks like QextSerialPort has some protection against problems with multiple threads, but still I think that you should instantiate it in CSerialDataFrameDecoder::run().

I'm not sure why did you split the decoding in two parts (CSerialDataFrameDecoder::run() and CSerialPort::customEvent()). If you create a separate thread for decoding why don't you let it do the whole job?

Another thing is that in several places you create a temporary frame object just to copy it later:
CSerialDataFrame aFrame;
aFrame.setSerialDataFrameValue(frame);
aFrame.setSerialDataFrameStatus(CSerialDataFrame:: SerialDataFrameStatusIncompleteHasJustTheEndingPar t);
this->serialDataFrameDecoded(aFrame);
Wouldn't it be easier to allocate that CSerialDataFrame on the heap?

CSerialDataFrame *aFrame = new CSerialDataFrame( QDeepCopy<QString>( frame ),
CSerialDataFrame::SerialDataFrameStatusIncompleteH asJustTheEndingPart );
serialDataFrameDecoded( aFrame );

//...

void CSerialDataFrameDecoder::serialDataFrameDecoded( CSerialDataFrame * frame )
{
// Allocate some memory for the custom event
QCustomEvent* ce = new QCustomEvent( 1002, data );

// Send the event
QApplication::postEvent( objectOwner, ce );
// The allocated memory for the custum event is freed by Qt so the receiver neither the sender MUST NOT free it
}

And the last thing --- you should define that "1002" as constant.

yellowmat
7th March 2006, 10:15
Ok.

I'll do all those changes in CSerialDataFrameDecoder and keep you informed.

Thanks for your help, it is precious.

yellowmat
7th March 2006, 16:57
That's it ! :)

Jacek, I followed your advices and my CSerialPort is functionnal.

So I migrate the Win_QextSerialPort instanciation and the process of all CSerialDataFrames into the CSerialDataFrameDecoder. This object send a custom event each time a complete CSerialDataFrame is available. The CSerialPort object receive the event sended by the CSerialDataFrameDecoder and it stores it in a QValueList, waiting some object to ask him using its function read (it pop the data from the QValueList).

Thank you very much for your help.