Hy,
J'écris mon premier programme Qt et j'ai maintenant des problèmes avec:
QObject :: killTimer: les timers ne peuvent pas être arrêtés depuis un autre thread
QObject :: startTimer: les timers ne peuvent pas être démarrés à partir d'un autre thread
Mon programme communiquera avec un bus CANOpen pour que j'utilise le Canfestival Stack . Le Canfestival fonctionnera avec les méthodes de rappel. Pour détecter le dépassement de délai en communication, je configure une fonction de minuterie (en quelque sorte comme un chien de garde). Mon paquet de minuterie se compose d'un module "tmr", d'un module "TimerForFWUpgrade" et d'un module "SingleTimer". Le module "tmr" était à l'origine programmé en C, donc les méthodes statiques "TimerForFWUpgrade" l'interfèrent. Le module "tmr" fera partie d'un package de mise à jour du micrologiciel programmé en C.
La minuterie fonctionnera comme suit. Avant d'envoyer un message, j'appellerai la méthode TMR_Set. Ensuite, dans ma boucle de programme inactive avec TMR_IsElapsed, nous vérifions s'il y a un dépassement de temps par minuterie. Si TMR_IsElapsed, je vais faire le traitement des erreurs. Lorsque vous voyez la méthode TMR_Set sera appelée en continu et redémarrez QTimer encore et encore.
Les erreurs mentionnées ci-dessus apparaissent si je lance mon programme. Pouvez-vous me dire si mon concept pourrait fonctionner? Pourquoi cette erreur apparaît-elle? Dois-je utiliser des threads supplémentaires (QThread) par rapport au thread principal?
Je vous remercie
Mat
Exécuter et boucle inactive:
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
....
}
Module tmr (interface avec le programme C): _
extern "C"
{
void TMR_Set(UINT8 tmrnbr, UINT32 time)
{
TimerForFWUpgrade::set(tmrnbr, time);
}
INT8 TMR_IsElapsed(UINT8 tmrnbr)
{
return TimerForFWUpgrade::isElapsed(tmrnbr);
}
}
Module 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;
}
}
Module 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;
}
Vous avez ce problème parce que les minuteries du tableau statique sont créées dans Thread X
, mais démarrées et arrêtées dans Thread Y
. Ceci n'est pas autorisé, car Qt s'appuie sur l'affinité des threads pour les temporisations.
Vous pouvez créer, commencer à arrêter dans le même thread ou utiliser signal et slots pour déclencher des opérations start
et stop
pour des minuteries. La solution du signal et de la fente pose quelques problèmes car vous avez des objets n
QTimer (indice: comment démarrer le chronomètre à la position i
?)
Ce que vous pouvez faire à la place est de créer et d’initialiser le temporisateur à la position tmrnbr
dans
TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
singleTimer[tmrnbr] = new SingleTimer(0);
singleTimer[tmrnbr]->set(time);
}
qui est exécuté par le même fil.
De plus, vous n'avez pas besoin d'une classe SingleTimer
. Vous utilisez Qt5 et vous avez déjà tout ce dont vous avez besoin à votre disposition:
SingleTimer::isElapsed
est vraiment QTimer::remainingTime() == 0
;SingleTimer::set
est vraiment QTimer::setSingleShot(true); QTimer::start(time);
SingleTimer::slot_setElapsed
devient inutile SingleTimer::SingleTimer
devient inutile et vous n'avez plus besoin d'une classe SingleTimer
Utilisez QTimer
à cette fin et utilisez SIGNALS
et SLOT
pour démarrer et arrêter le ou les temporisateurs à partir de différents threads. Vous pouvez émettre le signal de n'importe quel thread et l'attraper dans le thread qui a créé le timer pour agir dessus.
Puisque vous dites que vous êtes nouveau sur Qt, je vous suggère de suivre quelques tutoriels avant de continuer, afin que vous sachiez ce que Qt a à offrir et que vous n'essayiez pas de réinventer la roue. :)
VoidRealms est un bon point de départ.
J'ai éliminé les erreurs après avoir changé le concept de ma minuterie. Je n'utiliserais plus mon module SingleTimer. Avant QTimer, je ne laisserai pas le temps mort et peut-être à cause de cela, j'ai des problèmes. Maintenant, j'ai une minuterie cyclique qui expire toutes les 100 ms dans la fonction slot et je compte ensuite les événements. En dessous de mon code de travail:
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;
}
}
}
}