RS232-Thread Problem
RS232-Thread Problem
Hallo Forum,
ich bastele seid einiger Zeit an einer Klasse, die selbstständig
Daten von einem COM-Port liest und zwischenspeichert. Wenn
neue Daten vorliegen, wird ein Signal erzeugt und der Anwender
der Klasse kann sich die Daten per read() holen.
Das Prinzip habe ich unten mal skizziert.
Die mainForm erzeugt sich ein CComPort-Objekt, das wiederrum
einen Thread erzeugt, der die Daten vom COM-Port liest und
innerhalb des CComPort-Objektes speichert. Der Thread sendet
dann ein Signal an mainForm, das daraufhin die Daten aus dem
CComPort-Objekt lesen kann. Soweit funktioniert auch alles.
Wenn ich die gelesenen Daten innerhalb des Slots comDataReceived() auf der Konsole ausgebe, ist alles in Ordnung.
Sobald ich die Daten innerhalb des Slots aber in einem TextEdit
ausgeben will, stürzt das Programm ab.
Alle Daten sind reine ASCII-Zeichen. Das Programm stürzt auch
dann ab, wenn man statt der gelesenen Daten innerhalb des Slots
einen simplen Teststring im TextEdit ausgibt.
Die Fehlermeldungen beim Crash sehen meistens so aus:
Error: BadPixmap (invalid Pixmap parameter) 4
Major opcode: 56
Minor opcode: 0
Resource id: 0x50038
X Error: BadRequest (invalid request code or no such operation) 1
Major opcode: 182
Minor opcode: 0
Resource id: 0x50038
rs232test: Fatal IO error: client killed
Ich habe das Ganze auch mal ohne Signal/Slot-Mechanismus mit
einem Custom-Event implementiert, führt aber exakt zum gleichen
Verhalten. Im dem vom Thread aktivierten Slot bez. EventHandler
lässt sich kein Text auf dem TextEdit ausgeben.
Woran kann das liegen???
/********************************************/
/* MainForm */
/********************************************/
class mainForm:public QWidget
{
Q_OBJECT
public:
mainForm( QWidget* parent, const char* name, WFlags fl);
public slots:
comDataReceived( long readLen);
private:
CComPort* pComPort;
QTextEdit* pTextEdit;
};
/************************************/
/* Initialisierung */
/************************************/
void mainForm::init()
{
pTextEdit = new QTextEdit( this, "textEdit");
pComPort = new CComPort( this);
connect( pComPort, SIGNAL( comInputSignal( long)), this, SLOT( comDataReceived( long)));
}
/************************************/
/* Neue Daten vom COM-Port sind da */
/************************************/
void mainForm::comDataReceived( long al_readLen)
{
char* pInBuf;
QString tempStr;
pInBuf = new char[1000];
pComPort->read( pInBuf);
tempStr.append( (const char*)pInBuf);
cout << tempStr << endl; // OK !
pTextEdit->append( tempStr); // CRASH !
}
/********************************************/
/* Klasse zur Verwaltung eines COM-Ports */
/********************************************/
class CComPort:public QObject
{
Q_OBJECT
public:
CComPort( QWidget* pParentWidget);
read( char* pInBuf);
signals:
comInputSignal( long);
private:
/************************************/
/* Thread zum Lesen des COM-Ports */
/************************************/
class CReaderThread : public QThread
{
public:
CReaderThread( CComPort* pComPort);
run();
}
};
/************************************/
/* */
/************************************/
void CComPort::read( char* pInBuf)
{
// Gelesene Daten an den Aufrufer liefern
}
/************************************/
/* Thread holt Daten vom COM-Port */
/************************************/
void CComPort::CReaderThread::run()
{
long n;
// Lese den Port
n = Anzahl der gelesenen Bytes
// Speichere die Daten in einem Buffer von CComPort
// Signalisiere, dass neue Daten da sind
emit pComPort->comInputSignal( n);
}
ich bastele seid einiger Zeit an einer Klasse, die selbstständig
Daten von einem COM-Port liest und zwischenspeichert. Wenn
neue Daten vorliegen, wird ein Signal erzeugt und der Anwender
der Klasse kann sich die Daten per read() holen.
Das Prinzip habe ich unten mal skizziert.
Die mainForm erzeugt sich ein CComPort-Objekt, das wiederrum
einen Thread erzeugt, der die Daten vom COM-Port liest und
innerhalb des CComPort-Objektes speichert. Der Thread sendet
dann ein Signal an mainForm, das daraufhin die Daten aus dem
CComPort-Objekt lesen kann. Soweit funktioniert auch alles.
Wenn ich die gelesenen Daten innerhalb des Slots comDataReceived() auf der Konsole ausgebe, ist alles in Ordnung.
Sobald ich die Daten innerhalb des Slots aber in einem TextEdit
ausgeben will, stürzt das Programm ab.
Alle Daten sind reine ASCII-Zeichen. Das Programm stürzt auch
dann ab, wenn man statt der gelesenen Daten innerhalb des Slots
einen simplen Teststring im TextEdit ausgibt.
Die Fehlermeldungen beim Crash sehen meistens so aus:
Error: BadPixmap (invalid Pixmap parameter) 4
Major opcode: 56
Minor opcode: 0
Resource id: 0x50038
X Error: BadRequest (invalid request code or no such operation) 1
Major opcode: 182
Minor opcode: 0
Resource id: 0x50038
rs232test: Fatal IO error: client killed
Ich habe das Ganze auch mal ohne Signal/Slot-Mechanismus mit
einem Custom-Event implementiert, führt aber exakt zum gleichen
Verhalten. Im dem vom Thread aktivierten Slot bez. EventHandler
lässt sich kein Text auf dem TextEdit ausgeben.
Woran kann das liegen???
/********************************************/
/* MainForm */
/********************************************/
class mainForm:public QWidget
{
Q_OBJECT
public:
mainForm( QWidget* parent, const char* name, WFlags fl);
public slots:
comDataReceived( long readLen);
private:
CComPort* pComPort;
QTextEdit* pTextEdit;
};
/************************************/
/* Initialisierung */
/************************************/
void mainForm::init()
{
pTextEdit = new QTextEdit( this, "textEdit");
pComPort = new CComPort( this);
connect( pComPort, SIGNAL( comInputSignal( long)), this, SLOT( comDataReceived( long)));
}
/************************************/
/* Neue Daten vom COM-Port sind da */
/************************************/
void mainForm::comDataReceived( long al_readLen)
{
char* pInBuf;
QString tempStr;
pInBuf = new char[1000];
pComPort->read( pInBuf);
tempStr.append( (const char*)pInBuf);
cout << tempStr << endl; // OK !
pTextEdit->append( tempStr); // CRASH !
}
/********************************************/
/* Klasse zur Verwaltung eines COM-Ports */
/********************************************/
class CComPort:public QObject
{
Q_OBJECT
public:
CComPort( QWidget* pParentWidget);
read( char* pInBuf);
signals:
comInputSignal( long);
private:
/************************************/
/* Thread zum Lesen des COM-Ports */
/************************************/
class CReaderThread : public QThread
{
public:
CReaderThread( CComPort* pComPort);
run();
}
};
/************************************/
/* */
/************************************/
void CComPort::read( char* pInBuf)
{
// Gelesene Daten an den Aufrufer liefern
}
/************************************/
/* Thread holt Daten vom COM-Port */
/************************************/
void CComPort::CReaderThread::run()
{
long n;
// Lese den Port
n = Anzahl der gelesenen Bytes
// Speichere die Daten in einem Buffer von CComPort
// Signalisiere, dass neue Daten da sind
emit pComPort->comInputSignal( n);
}
-
FlorianBecker
- Beiträge: 1213
- Registriert: 2. Dezember 2004 10:54
- Kontaktdaten:
Hallo Florian,
danke für deine Antwort. Habe das Format des TextEdit mal auf
QTextEdit::LogText umgestellt und zu meiner Verblüffung
festgestellt, dass der Absturz zunächst tatsächlich nicht mehr
auftrat. Bei genauerer Betrachtung tritt der Effekt aber leider nur
etwas später ein. So ab ca. 2000...3000 Charakters.
Ich habe den Eindruck, dass die Ursache für den Absturz eher im
Aufrufkontext zu suchen ist.
Wenn ich das richtig verstanden habe, schreibt sendEvent doch
ein Event in die Event-Queue und kommt erst zurück, wenn das
Event vom Event-Handler bearbeitet wurde.
Ich meine, dass der Thread, der den COM-Port liest, also das Event
schreibt und dann wartet, bis der GUI-Thread den Event-Handler
beendet hat und das Event aus der Queue gelöscht ist.
Der Event-Handler wird also im Kontext des GUI-Threads
ausgeführt und deshalb sollte der Pointer auf das QTextEdit
dort auch gültig sein. Vielleicht liegen die Verhältnisse aber auch
anders ??
Habe statt mit sendEvent auch eine Version mit postEvent und
die in meinem ursprünglichen Posting skizzierte Version mit einem
Signal und einem Slot ausprobiert, aber alle Versionen stürzen
mit dem gleichen Fehler ab. Bin etwas ratlos.
Ich arbeite gerade an einer weiteren Version, die ohne Qt
auskommt und stattdessen die pthread-lib direkt verwendet.
Wäre aber sehr schade, wenn's mit Qt nicht liefe, denn dann
müsste ich auf die ganzen Annehmlichkeiten verzichten.
....Kai
danke für deine Antwort. Habe das Format des TextEdit mal auf
QTextEdit::LogText umgestellt und zu meiner Verblüffung
festgestellt, dass der Absturz zunächst tatsächlich nicht mehr
auftrat. Bei genauerer Betrachtung tritt der Effekt aber leider nur
etwas später ein. So ab ca. 2000...3000 Charakters.
Ich habe den Eindruck, dass die Ursache für den Absturz eher im
Aufrufkontext zu suchen ist.
Wenn ich das richtig verstanden habe, schreibt sendEvent doch
ein Event in die Event-Queue und kommt erst zurück, wenn das
Event vom Event-Handler bearbeitet wurde.
Ich meine, dass der Thread, der den COM-Port liest, also das Event
schreibt und dann wartet, bis der GUI-Thread den Event-Handler
beendet hat und das Event aus der Queue gelöscht ist.
Der Event-Handler wird also im Kontext des GUI-Threads
ausgeführt und deshalb sollte der Pointer auf das QTextEdit
dort auch gültig sein. Vielleicht liegen die Verhältnisse aber auch
anders ??
Habe statt mit sendEvent auch eine Version mit postEvent und
die in meinem ursprünglichen Posting skizzierte Version mit einem
Signal und einem Slot ausprobiert, aber alle Versionen stürzen
mit dem gleichen Fehler ab. Bin etwas ratlos.
Ich arbeite gerade an einer weiteren Version, die ohne Qt
auskommt und stattdessen die pthread-lib direkt verwendet.
Wäre aber sehr schade, wenn's mit Qt nicht liefe, denn dann
müsste ich auf die ganzen Annehmlichkeiten verzichten.
....Kai
-
FlorianBecker
- Beiträge: 1213
- Registriert: 2. Dezember 2004 10:54
- Kontaktdaten:
-
Spring-Daniel
- Beiträge: 40
- Registriert: 4. Oktober 2004 16:26
Eine andere Alternative waere auch, komplett auf threads zu verzichten, und evtl. einen QSocketNotifier zu verwenden.
http://doc.trolltech.com/3.3/qsocketnotifier.html
Hab das einmal mit dem Seriellen Port gemacht, hat eigentlich prima funktioniert.
http://doc.trolltech.com/3.3/qsocketnotifier.html
Hab das einmal mit dem Seriellen Port gemacht, hat eigentlich prima funktioniert.
Ja, QSocketNotifier habe ich auch schon verwendet, das funktioniert gut. Der Thread, der den COM-Port bedient, verwendetSpring-Daniel hat geschrieben:Eine andere Alternative waere auch, komplett auf threads zu verzichten, und evtl. einen QSocketNotifier zu verwenden.
http://doc.trolltech.com/3.3/qsocketnotifier.html
Hab das einmal mit dem Seriellen Port gemacht, hat eigentlich prima funktioniert.
das auch, bez. er nimmt direkt den select() call. Das
anschliessende Lesen der Daten kann aber innerhalb des
GUI-Threads zu lange dauern, wenn eine langsame Baudrate am seriellen Port eingestellt ist.
Darum der Thread. Der liest die Daten und schreibt sie in einen
Buffer. Wenn er fertig ist, kann sich die Klasse im GUI-Thread die
Daten in einem Rutsch holen.
War gerade dabei mir die Qt 4.0 Beta zu besorgen. Weiß jemand,
ob man für ein Suse 9.1 die Version:
qt-x11-opensource-4.0.0-b1.tar.gz
oder die Version
qt-x11-preview-4.0.0-tp2.tar.gz
braucht?
-
FlorianBecker
- Beiträge: 1213
- Registriert: 2. Dezember 2004 10:54
- Kontaktdaten:
Ok, danke für den Tip. Probiers gleich mal aus.FlorianBecker hat geschrieben:b ist beta die previews haben bei mir nicht gebaut.
Habe auch die Lösung des Problems von jemandem aus
dem QtForum.org bekommen und dachte ich teile sie mal
allen mit, falls Interesse besteht.
Im Event-Handler muß für die Dauer seiner Ausführung
der Zugriff auf die Qt-Lib gesperrt werden. Sonst stört
der laufende COM-Thread den GUI-Thread.
Warum das so ist, ist mir nicht ganz klar. Ich dachte immer,
dass der Event-Handler im Kontext des GUI-Threads läuft.
So funktionierts jedenfalls:
void mainForm::comDataReceived()
{
qApp->lock ();
// Tu irgendwas mit der Qt-Lib
qApp->unlock();
}