wake-up-neo.net

Qt-Timer können nicht von einem anderen Thread angehalten werden

Hy,

Ich schreibe mein erstes Qt-Programm und stoße jetzt Probleme mit:

QObject :: killTimer: Timer können nicht von einem anderen Thread angehalten werden

QObject :: startTimer: Timer können nicht von einem anderen Thread aus gestartet werden

Mein Programm kommuniziert mit einem CANOpen-Bus, für den ich den Canfestival Stack verwende. Das Canfestival arbeitet mit Callback-Methoden. Um Timeout in der Kommunikation zu erkennen, stelle ich eine Timer-Funktion ein (irgendwie wie ein Watchdog). Mein Timer-Paket besteht aus einem "tmr" -Modul, einem "TimerForFWUpgrade" -Modul und einem "SingleTimer" -Modul. Das "tmr" -Modul wurde ursprünglich C-programmiert, sodass die statischen "TimerForFWUpgrade" -Methoden eine Schnittstelle bilden. Das "tmr" -Modul ist Teil eines von C programmierten Firmware-Aktualisierungspakets.

Der Timer funktioniert wie folgt. Bevor eine Nachricht gesendet wird, rufe ich die TMR_Set-Methode auf. Eine in meinem Leerlauf befindliche Programmschleife mit TMR_IsElapsed überprüft dann, ob ein Timer-Unterlauf vorliegt. Wenn TMR_IsElapsed, werde ich die Fehlerbehandlung durchführen. Wie Sie sehen, wird die TMR_Set-Methode fortlaufend aufgerufen und der QTimer immer wieder neu gestartet.

Die oben genannten Fehler werden angezeigt, wenn ich mein Programm starte. Können Sie mir sagen, ob mein Konzept funktionieren könnte? Warum werden diese Fehler angezeigt? Muss ich zusätzliche Threads (QThread) zum Hauptthread verwenden? 

Vielen Dank

Matt

Lauf- und Leerlaufschleife:

void run
{
    // start communicate with callbacks where TMR_Set is set continously
    ...

    while(TMR_IsElapsed(TMR_NBR_CFU) != 1);

    // if TMR_IsElapsed check for errorhandling
    ....
}  

Modul tmr (Schnittstelle zum C-Programm):

extern "C"
{
void TMR_Set(UINT8 tmrnbr, UINT32 time)
{
    TimerForFWUpgrade::set(tmrnbr, time);
}

INT8 TMR_IsElapsed(UINT8 tmrnbr)
{
 return TimerForFWUpgrade::isElapsed(tmrnbr);
}
}

Modul TimerForFWUpgrade:

SingleTimer* TimerForFWUpgrade::singleTimer[NR_OF_TIMERS];

TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent)
{

    for(unsigned char i = 0; i < NR_OF_TIMERS; i++)
    {
        singleTimer[i] = new SingleTimer(parent);
    }
}

//static
void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
    if(tmrnbr < NR_OF_TIMERS)
    {
        time *= TimerForFWUpgrade::timeBase;
        singleTimer[tmrnbr]->set(time);
    }

}


//static
char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
    if(true == singleTimer[tmrnbr]->isElapsed())
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

Modul SingleTimer:

SingleTimer::SingleTimer(QObject* parent) : QObject(parent),
                                            pTime(new QTimer(this)),
                                            myElapsed(true)
{
    connect(pTime, SIGNAL(timeout()), this, SLOT(slot_setElapsed()));
    pTime->setTimerType(Qt::PreciseTimer);
    pTime->setSingleShot(true);
}

void SingleTimer::set(unsigned int time)
{
    myElapsed = false;
    pTime->start(time);
}

bool SingleTimer::isElapsed()
{
    QCoreApplication::processEvents();
    return myElapsed;
}

void SingleTimer::slot_setElapsed()
{
    myElapsed = true;
}
11
Matt

Sie haben dieses Problem, weil die Timer im statischen Array in Thread X erstellt, aber in Thread Y gestartet und gestoppt wurden. Dies ist nicht zulässig, da Qt von der Thread-Affinität zu Timeout-Timern abhängig ist.

Sie können entweder erstellen, stop im selben Thread starten oder Signal und Slots verwenden, um start- und stop-Operationen für Timer auszulösen. Die Signal- und Slot-Lösung ist etwas problematisch, weil Sie n QTimer-Objekte haben (Hinweis: wie starten Sie den Timer an Position i?)

Stattdessen können Sie den Timer an Position tmrnbr in erstellen und initialisieren 

TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
     singleTimer[tmrnbr] = new SingleTimer(0);
     singleTimer[tmrnbr]->set(time);
}

die von demselben Thread ausgeführt wird.

Außerdem benötigen Sie keine SingleTimer-Klasse. Sie verwenden Qt5 und haben bereits alles, was Sie brauchen:

  • SingleTimer::isElapsed ist wirklich QTimer::remainingTime() == 0;
  • SingleTimer::set ist wirklich QTimer::setSingleShot(true); QTimer::start(time); 
  • SingleTimer::slot_setElapsed wird unbrauchbar 
  • Somit wird SingleTimer::SingleTimer unbrauchbar und Sie brauchen keine SingleTimer-Klasse mehr
6
UmNyobe

Verwenden Sie QTimer für diesen Zweck und verwenden Sie SIGNALS und SLOT, um den/die Timer von verschiedenen Threads zu starten und zu stoppen. Sie können das Signal von einem beliebigen Thread aus senden und es in dem Thread abfangen, der den Timer erstellt hat, um darauf einzuwirken.

Da Sie sagen, dass Sie neu bei Qt sind, sollten Sie einige Tutorials durchgehen, bevor Sie fortfahren, damit Sie wissen, was Qt zu bieten hat, und nicht versuchen, das Rad neu zu erfinden. :)

VoidRealms ist ein guter Ausgangspunkt.

6
nnb

Ich habe die Fehler beseitigt, nachdem ich mein Timer-Konzept geändert hatte. Ich würde mein SingleTimer-Modul nicht mehr verwenden. Vor dem QTimer lasse ich kein Timeout zu und vielleicht stoße ich auf Probleme. Nun habe ich einen zyklischen QTimer, der alle 100ms mal in Slot-Funktion ausläuft. Ich zähle dann die Events. Unter meinem Arbeitscode:

TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent) : QObject(parent),
                                                        pTime(new QTimer(this))
{
    connect(pTime, SIGNAL(timeout()), this, SLOT(slot_handleTimer()));
    pTime->setTimerType(Qt::PreciseTimer);
    pTime->start(100);
}

void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
    if(tmrnbr < NR_OF_TIMERS)
    {
        if(timeBase != 0)
        {
            myTimeout[tmrnbr] = time / timeBase;
        }
        else
        {
            myTimeout[tmrnbr] = 0;
        }
        myTimer[tmrnbr] = 0;
        myElapsed[tmrnbr] = false;
        myActive[tmrnbr] = true;
    }

}

char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
    QCoreApplication::processEvents();
    if(tmrnbr < NR_OF_TIMERS)
    {
        if(true == myElapsed[tmrnbr])
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return 0; // NOK
    }
}

void TimerForFWUpgrade::slot_handleTimer()
{
    for(UINT8 i = 0; i < NR_OF_TIMERS; i++)
    {
        if(myActive[i] == true)
        {
            myTimer[i]++;
            if(myTimeout[i] < myTimer[i])
            {
                myTimer[i] = 0;
                myElapsed[i] = true;
                myActive[i] = false;
            }
         }
    }
}
0
Matt