📜 ⬆️ ⬇️

Qt Framework: streams, hierarchical finite state machine, work with USB devices = QThread + QStateMachine + libUSB

Almost all software developers sooner or later come to the stage when it is necessary to apply technology to distribute tasks to individual threads. Now it is difficult to imagine development without the use of one or another framework.
Many of them contain the necessary tools to create multi-threaded applications. Not an exception and Qt Framework .

Let's talk about Qt methods of multithreaded development in more detail.

The Qt Framework software community is huge. People eagerly share the skills and techniques of creating multi-threaded applications. There are many wonderful books and articles on elegant and not very tackling the task of working with multiple threads.
It would seem that everything has already been decided. What else can I add?
I will try to describe the work flow on the basis of the functioning of the finite state machine. I admit, did not find materials with a similar solution on the network.
If the article helps you with the idea that, in my opinion, is much more valuable than the written code, I will be very happy.
')
Special thanks to A. N. Borovsky for the book "Qt4.7 +. Practical C ++ programming" .
I recommend for mandatory reading!

I feel so comfortable.


In my codes, I define several macros that may seem redundant:
#define IS_ZERRO(VALUE) (0 == (VALUE)) #define MORE_ZERRO(VALUE) (0 < (VALUE)) #define LESS_ZERRO(VALUE) (0 > (VALUE)) #define NOT_ZERRO(VALUE) (!IS_ZERRO(VALUE)) #define IS_NULL(PTR) (Q_NULLPTR == (PTR)) #define NOT_NULL(PTR) (!IS_NULL(PTR)) #define BETWEEN(VALUE,LOW,HIGH) ( ((VALUE) > (LOW)) && ((VALUE) < (HIGH)) ) #define BETWEEN_L(VALUE,LOW,HIGH) (((VALUE) >= (LOW)) && ((VALUE) < (HIGH))) #define BETWEEN_H(VALUE,LOW,HIGH) (((VALUE) > (LOW)) && ((VALUE) <= (HIGH))) #define BETWEEN_ALL(VALUE,LOW,HIGH) (((VALUE) >= (LOW)) && ((VALUE) <= (HIGH))) #define EQUAL(VALUE,EXPR) ((VALUE) == (EXPR)) #define NOT_EQUAL(VALUE,EXPR) (!EQUAL(VALUE,EXPR)) 



I will not argue about the feasibility of their use. It seems to me that mnemonics do not overload the code and contain, say, a “good tone” for checking pointers.
It's just that it's convenient for me.

Let's start.

Step 1. A bit about the threads "on the fingers."


If you put the stress on the last word correctly, continue. :)
As is known, the Qt thread model consists of two parts:

When implementing objects inherited from the QThread class ( Russian ), we are dealing with the creation of the next auxiliary threads.
Each QThread object contains its own event loop. The loop is started by the protected QThread :: exec () method. A thread is launched for execution by calling the QThread :: start () method. The start method launches the protected QThread :: run () method. In turn, the run () method runs exec () and, accordingly, its own event handler for an object of the QThread class.
The run () method runs in the address space of a separate (let's call it, worker) thread. The address space of the workflow is different from the address space of an object of the QThread class.

How is the workflow implemented?
For this, for example, in UNIX-like systems, the pthread library is used . In the network a lot of material about it.
The QThread :: start () method creates a separate workflow by calling pthread_create () , passing the QThreadPrivate :: start () method as a thread function, and the pointer to the object itself as the last parameter arg .

Interesting code inside the method
 QThreadPrivate::start () { . . . QThreadData *data = QThreadData::get2(thr); . . . } 


Having traced the chain of connections, one can understand that all objects moved by the QObject :: moveToThread method (QObject *) will be placed in this data pool of the QThreadData class of the workflow address space.
Quite a lot of discussions in the Qt developer community are conducted on what is better: move objects with QObject :: moveToThread () or overload the protected QThread :: run () method?
Having redefined the QThread :: run () method, know that all objects created in it will be inaccessible from outside, since created on the run () method stack (although this can be bypassed).
The objects being moved are stored in the data pool and pull all their QMetaObject :: Connection for themselves.

Remember this and everything will be fine.

A simplified diagram of the foregoing can be represented by the picture:



It is a little about expediency of use QThread.


In addition to QThread , Qt provides the developer with a set of higher-level classes.
They are located in the QtConcurrent namespace. Classes of this interface take full control of the flow, optimize performance and distribute the load between threads. You can write down methods in them and not worry about correct completion and cleaning of memory. But there are costs. For example, the QRunnable class is not a descendant of QObject , which means it cannot send or receive signals and cannot process events.
I think they are well suited for simple methods, for example, any mathematical calculations, where there exists a predefined set of input data and a strict processing algorithm.
Often it is necessary to create constant waiting cycles for something or to constantly scan and check various conditions. The developer places them in separate threads so that the main event loop or application GUI does not “slow down”. For example, using libUSB library functions , you can constantly update the list of connected devices. In this case, there may be several possible exit points from the stream as a reaction to external events, signals, or the occurrence of exceptional situations. The main thing is that we must manage the algorithm that is enclosed in the workflow cycle. For this, I think, the use of an object of the class derived from the QThread class is most justified.

Comment.


Quite often in the network there is a certain “trick with ears” in moving the stream object to the work flow, namely
 MyThread:: MyThread () : QThread() { moveToThread(this); } 

Do not do this! At least because the basic concept of the QThread class as a thread management add-in is violated. This is similar to the fact that you went fishing, abandoned the bait, and the bait itself was also thrown into the river ...

When creating flow objects, you probably noticed that the value of a pointer to an object moved by the QObject :: moveToThread () method to the address space of the workflow correctly handles an out-of-flow reference. We can safely use the object according to, so to speak, the moved pointer for QMetaObject :: Connection links.
It would seem that they were moved to another address space, but it turns out that there was no movement: the address of the object did not change according to the pointer value.
Consider one of the differences in process and flow. The OS allocates its own memory pool to each process, but each new thread uses shared memory with the parent thread and the process that created it. This means that everything is normal - the address of the object being moved from stream to stream does not change relative to the main process.
It can be said that a pointer to an object relative to the address spaces of threads is somewhat akin to a “smart” pointer of type std :: auto_ptr , which assigns the object to the owner.
I will cite a lyrical analogy.
 //   (  main())       //     . City/*::QObject*/* city = City(); city->setObjectName(“”); std::out << qPrintable(city) << std::endl; . . . //       ,      , QThread* Girey = new QThread(); city->setParent(Q_NULLPTR); city->moveToThread(Girey); //      . std::out << qPrintable(city) << std::endl; . . . //     city->setParent(Q_NULLPTR); city->moveToThread(RussionImperia); . . . city->setParent(Q_NULLPTR); city->moveToThread(USSR); . . . city->setObjectName(“”); //  .. 

The geographical coordinates of the city ( city ) relative to the Earth ( main () ) do not change, but the infrastructure ( * city ) is variable.

For the time being, we ’ll suspend a cursory description of the QThread class and proceed to the description of a useful trick to correctly terminate the thread when the latter has reached any exit point, including a reaction to possible exceptional situations.

Step 2. Correct completion of the stream.


As you know, streams can exchange information, use common data, analyze the state of this data and send each other various notifications.
One stream may indirectly or directly ask another to temporarily suspend work or, in general, to cease to exist.
I will not describe all possible methods and options for properly separating access to data: there is more than enough material on this subject. Simply, imagine a situation that we do not know in advance when the exit from the stream will occur, and by what condition this will happen. Of course, the exit points from the stream must be activated upon completion of a certain final section of the stream operation.
A situation may arise when the algorithm itself will cause an exception. For example, a pointer to a pointer with a nonexistent value. Often this leads to a complete hang of the stream. Even the QThread :: terminate () method cannot remove it from the execution pool. So there are zombie streams. In the case of processes, the OS itself will try to destroy the process, but this number will not work with threads, because the main thread is responsible for their lives, not the core of the OS.
In a word, we will assume that we do not know in advance the total number and places of exit points from the stream and the termination code.
Some helper is needed here - the “helper” wrapping around the stream object. Such tricks are often found in Qt.

I will give an example of the code and briefly explain:
The stream class FWThread is defined :
 class FWThread : public QThread { } 

Assistant class defined
 class FWThread_helper { QPointer<FWThread> m_ext_thread; Public: FWThread_helper(FWThread* aThread) : m_ext_thread(aThread) { Q_ASSERT(NOT_NULL(m_ext_thread)); } ~FWThread_helper() { if(!m_ext_thread.isNull()) m_ext_thread->deleteLater(); } } 

Define a destructor of class FWThread
 FWThread::~FWThread() { quit (); if(!wait (FW_THREAD_WAIT_TIMEOUT)) { // ,   ... terminate (); if(!wait(FW_THREAD_WAIT_TIMEOUT)) exit(EXIT_FAILURE); } } 

QThread :: run () method overridden
(Thanks ArtemE for noticing the blot)
 void FWThread::run() { m_helper = new FWThread_helper(this); //   m_helper.clear (); } 

What happens, for example, when an exception occurs in the body of the run () method? Whatever happens, the v_helper pointer will always call its own destructor either by m_helper.clear () , or by clearing the run () method call stack when an exception occurs.
The destructor will destroy the FWThread thread object. Upon the completion of the latter, an attempt to exit the stream or your own certain algorithm for closing the stream will occur. For example, all objects passed to the stream can be destroyed using the QObject :: deleteLater () method.

Pay attention to the line
 Q_ASSERT(NOT_NULL(m_ext_thread)); 

in the body of the constructor of the class FWThread_helper . The class was created specifically for a non-empty pointer to a stream. This code informs the developer about a flow control error at the debugging stage.

Try to identify
 void FWThread::run() { FWThread_helper* v_helper = new FWThread_helper(this); QException e; QT_TRY { e.raise(); } QT_CATCH(QException& e) { qDebug() << qPrintable(e.what ()); } m_helper.clear (); } 

I hope everything is clear here.

Step 3. Apply hierarchical finite state machine.


One of the most useful, in my opinion, implementations in the Qt Framework is represented by a section of a hierarchical finite state machine (hereinafter referred to as the KA). KA is implemented in the boost library, but it will be a question of Qt.

You can familiarize yourself with the Qt KA theory on the Qt documentation page or the crossplatform.ru website
So how and, most importantly, why do we connect the spacecraft and the flow?
Looking ahead a little bit, here’s an example of the QThread :: run () method overload code that I use:
 void FWThread::run() { FWThread_helper* v_helper = new FWThread_helper(this); m_helper = v_helper; InternalRun (); if(IsValid ()) { BeforeMachineStart (); ///     @sa Machine (). Machine ()->start (); ///  a   . =>   . BaseClass::exec (); } else { if(!IsError ()) SetError (); exit (FW_ERR_THREAD_INCOMPLETE); } m_helper.clear (); } 

In all classes derived from the FWThread class (:: QThread) , I override only the single self-added method FWThread :: BeforeMachineStart () . No method of working directly with the stream is overloaded anymore!
Each BeforeMachineStart () method of the FWThread descendant class adds QA states and transitions between them. Thus, a change in the algorithm of the QThread :: run () method occurs only in one class implementation file, and the thread itself can serve different behaviors of the finite state machine. Moreover, when stopping the flow, you can prohibit QObject :: deleteLater () for an object of the FWThread class, dynamically change the behavior of the spacecraft and start the same instance of the flow using the FWThread :: start () method again with a completely different behavior model of the spacecraft!

I'll tell you in more detail about the model of a finite state machine used as a mechanism for controlling the workflow.

In the Qt implementation, the state machine is represented by the QStateMachine class ( rus. ), The states are described by the QState ( rus. ), QHistoryState ( rus. ), QFinalState ( rus. ) Classes . Transitions are represented by the QEventTransition ( rus. ) And QSignalTransition ( rus. ) Classes .
All listed classes are based on a set of abstract classes. So QState extends the abstract class QAbstractState ( rus. ). The QStateMachine SC class is a derivative of QState. Transition classes originate from the QAbstractTransition class ( rus. ).
The Qt documentation contains a very detailed description of the spacecraft, classes of states and transitions, a sufficient number of simple examples and tricks in the design and implementation of spacecraft.
There is an excellent and complete translation of the Qt QA description in Russian.
Read it. It is, simply, interesting.

From the examples and recommendations of the Qt documentation for the work on the basis of the spacecraft, I chose a scheme similar to the figure of the example documentation .


By overriding the classes of the automaton, states, and transitions, I for a while refused to use the QSignalTransition class. Any signal can be duplicated as sending an event to a stream event handler.
Imagine a picture when you have an event receiver object hidden as an attribute of a certain class. The object of the latter is also an attribute of a class, etc. In order to broadcast a signal, the developer will not have to forget about reassigning QObject :: connect () connections to the depth of the object until the signal reaches the target. And vice versa, to relay the signals of deeply nested objects as class attributes to the upper level.
It is much easier to define your own message format based on the QEvent class with some additional attributes and use the QStateMachine :: postEvent () or QCoreApplication :: postEvent () method .

Additionally, I use my own class model based on the QObject class.
So, my base class sounds like class FWIterm: public QObject .
By declaring new classes, I inherit them only from the FWItem class. This allows you to enter some flags and signs that are common to all. In addition, the heirs of FWItem can supplement and extend the sets of flags, sets, and other attributes of the types of the FWItem class.
The most important thing is that the base class FWItem contains methods for assigning, checking and references to the state machine:

 class FW_LIBRARY FWItem : public QObject { private: QPointer<FWStateMachine> m_machine; protected: /** * @brief  MachineExists       *   @sa FWStateMachine. * @return  : * @value TRUE     ; * @value FALSE    . */ virtual bool MachineExists () const { return !m_machine.isNull (); } /** * @brief  Machine      *   ()  @sa FWStateMachine. * @return    . * *  @sa FWStateMachine   @sa QStateMachine. * *        ,  *  @sa FWItem,  ,     @sa FWStateMachine. */ virtual FWStateMachine* Machine () const; public: /** * @brief  SetMachine     . * @param aMachine    . */ void SetMachine (FWStateMachine* aMachine); } 


Thus, if there is a spacecraft for the FWItem object , you can control the sending of messages for states and transitions directly:
 if(item-> MachineExists()) Machine()->PostEvent(FWEvent::OEI_Open); 

Where FWEvent :: OEI_Open is a certain, defined message identifier of the FWEvent class.

The state and transition diagram for the automaton for controlling the operation of the flow takes the form

where T (FWEvent :: Event :: Type signal) is a transition from one state to another.

For ease of understanding state transitions , a set of virtual methods FWStateMachine :: AddBranch (<event or event constructor> <initial state> <assignment state> == Q_NULLPTR) has been added to the FWStateMachine class;
For many solutions of problems based on the Qt spacecraft of this scheme and the number of states is quite sufficient. Therefore, I implemented it in the implementation body of the FWMachineState class, creating a protected virtual method Initialisation () .
Consider it:

 void FWStateMachine::Initialisation() { bool v_active = IsActive (); if(v_active) stop (); /// @name     /// @{ if(!StateGroupExists ()) m_state_group.reset (new FWState(this)); FWState* sIdle = new FWState(StateGroup ()); StateGroup ()->setInitialState (sIdle); /// @} /// @name       /// @{ if(!StateHistoryExists ()) //    ,   ,    //   - @sa m_state_group (@sa QHistoryState::DeepHistory), //    //    ,    . m_state_history.reset (new QHistoryState(QHistoryState::DeepHistory,StateGroup ())); /// @} /// @name  z    /// @{ if(!StateFinalExists ()) m_state_final.reset (new QFinalState(this)); /// @} /// @name     /// @{ if(!StateErrorExists ()) m_state_error.reset (new FWState(this)); /// @} /// @name       /// @{ AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Stop, StateGroup (), StateFinal ()); AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Error, StateGroup (), StateError ()); /// @} /// @name       /// @{ FWState* sPause = new FWState(this); AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Pause, StateGroup (), sPause); AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Resume, sPause, StateHistory ()); /// @} /// @name      /// @{ AddBranch(StateError (), StateFinal ()); /// @} /// @name       /// @{ connect( sPause,&FWState:: entered, this, &FWStateMachine:: slot_OnPaused, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( sPause,&FWState:: exited, this, &FWStateMachine:: slot_OnResume, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( StateFinal (),&QFinalState:: entered, this, &FWStateMachine:: slot_OnStop, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( StateError (),&FWErrorState:: entered, this, &FWStateMachine:: slot_OnError, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( StateHistory(),&QHistoryState::defaultStateChanged, this, &FWStateMachine::slot_OnDefaultHistoryChanged, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); /// @} if(NOT_EQUAL(initialState (),StateGroup ())) setInitialState (StateGroup ()); StateGroup ()-> setObjectName("State GROUP"); StateHistory ()-> setObjectName("State HISTORY"); StateError()-> setObjectName("State ERROR"); StateFinal()-> setObjectName("State FINAL"); sPause-> setObjectName("State PAUSE"); sIdle-> setObjectName("State IDLE"); if(v_active) start (); } 



In the heirs of the class FWStateMachine
slots can be overloaded

  . . . protected Q_SLOTS: virtual void slot_OnLoging () { qDebug() << qPrintable(Q_FUNC_INFO); } virtual void slot_OnError () { qDebug() << qPrintable(Q_FUNC_INFO); } virtual void slot_OnPaused () { qDebug() << qPrintable(Q_FUNC_INFO); if(thread ()) thread ()->yieldCurrentThread (); emit sign_Paused(); } virtual void slot_OnResume () { qDebug() << qPrintable(Q_FUNC_INFO); emit sign_Resumed(); } virtual void slot_OnStop () { qDebug() << qPrintable(Q_FUNC_INFO); } virtual void slot_OnDefaultHistoryChanged() { if(StateHistoryExists ()) if(NOT_NULL(StateHistory()->defaultState())) qDebug() << "-H-" << StateHistory()->defaultState()->objectName(); } }; 



Let's go back to the FWThread class and look at the Reconnect () method called in the body of the run () method
FWThread :: Reconnect ()

 void FWThread::Reconnect() { if(IsAutoDeleted ()) //       connect( this, &QThread::finished, this, &QThread::deleteLater, Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection)); if(MachineExists ()) { //         //     connect( Machine (),&FWStateMachine:: finished, this, &QThread:: quit, Qt::ConnectionType(Qt::AutoConnection | Qt::UniqueConnection)); //         //  . connect( this, &QThread:: finished, Machine (),&FWStateMachine:: stop, Qt::ConnectionType(Qt::AutoConnection | Qt::UniqueConnection)); /// @name       @sa FWThread /// @{ connect (Machine (),&FWStateMachine:: started, this, &FWThread:: sign_MachineStarted, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect (Machine (),&FWStateMachine:: stopped, this, &FWThread:: sign_MachineStopped, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect (Machine (),&FWStateMachine:: sign_Paused, this, &FWThread:: sign_MachinePaused, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect (Machine (),&FWStateMachine:: sign_Resumed, this, &FWThread:: sign_MachineResumed, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); /// @} //         //    connect (Machine (),&FWStateMachine:: started, this, &FWThread:: slot_AfterMachineStart, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); } } 


. .

: - FWThread_helper FWThread . , .
FWThread.h

 /// /// \language Russian /// #ifndef FW_THREAD_H #define FW_THREAD_H #include <FWTypes> FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ /** * @brief   FW_THREAD_WAIT_TIMEOUT   *     @sa QThread::wait. * * @see QThread */ extern unsigned long FW_THREAD_WAIT_TIMEOUT; //------------------------------------------------------------------------------ class FWThread_helper; class FWStateMachine; //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWThread : public QThread { friend class FWThread_helper; Q_OBJECT Q_CLASSINFO("brief", "Framework Thread Class with QStateMashine") Q_CLASSINFO("created", "03-JUN-2015") Q_CLASSINFO("modified", "23-JUN-2015") // Q_CLASSINFO("project", "Common Qt-based Framework") // Q_CLASSINFO("info_ru", "http://doc.crossplatform.ru/qt/4.7.x/qthread.html#") // Q_DISABLE_COPY(FWThread) /// @name   . /// @{ ///        typedef QThread BaseClass; /// @} public: enum FWThreadFlag { THF_Empty = 0x00, THF_Ready = 0x01, THF_Working = 0x02, THF_Paused = 0x04, THF_AutoDelete = 0x08, THF_Error = 0x80, }; Q_DECLARE_FLAGS(FWThreadFlags, FWThreadFlag) private: QPointer<FWThread_helper> m_helper; /// /// @brief  m_flags     . /// @see FWThreadFlags. /// QAtomicInt m_flags; /// @name  ()      /// @{ void SetEmpty () { m_flags.fetchAndStoreOrdered (THF_Empty); } void SetReady () { FWThreadFlags v_flags = Flags () | THF_Ready; m_flags.fetchAndStoreOrdered (v_flags); } void UnsetReady () { FWThreadFlags v_flags = Flags () & (~THF_Ready); m_flags.fetchAndStoreOrdered (v_flags); } void SetWorking () { FWThreadFlags v_flags = Flags () | THF_Working; m_flags.fetchAndStoreOrdered (v_flags); } void SetStop () { FWThreadFlags v_flags = Flags () & (~THF_Working); m_flags.fetchAndStoreOrdered (v_flags); } void SetPause () { FWThreadFlags v_flags = Flags () | THF_Paused; m_flags.fetchAndStoreOrdered (v_flags); } void SetResume () { FWThreadFlags v_flags = Flags () & (~THF_Paused); m_flags.fetchAndStoreOrdered (v_flags); } void SetAutoDelete () { FWThreadFlags v_flags = Flags () | THF_AutoDelete; m_flags.fetchAndStoreOrdered (v_flags); Reconnect(); } void UnsetAutoDelete () { FWThreadFlags v_flags = Flags () & (~THF_AutoDelete); m_flags.fetchAndStoreOrdered (v_flags); } void SetError () { FWThreadFlags v_flags = Flags () | THF_Error; m_flags.fetchAndStoreOrdered (v_flags); } void UnsetError () { FWThreadFlags v_flags = Flags () & (~THF_Error); m_flags.fetchAndStoreOrdered (v_flags); } /// @} /** * @brief  InternalRun     *         . */ void InternalRun(); public: /** * @static * @brief  SetThreaWaitTimeout    *     @sa QThread::wait. * @param aValue       @sa QThread::wait. * * @see QThread */ static void SetThreaWaitTimeout( unsigned long aValue) { if(NOT_EQUAL(FW_THREAD_WAIT_TIMEOUT,aValue)) FW_THREAD_WAIT_TIMEOUT = aValue; } /** * @brief   @sa FWThread * @param aAutoDelete       *   . * @param aParent   - . * * @note   @sa aParent  -   , *   @sa aAutoDelete ,     *      . */ explicit FWThread(const bool aAutoDelete = true, QObject* aParent = Q_NULLPTR); ~FWThread(); /** * @brief  AsBaseClass     () *  @sa FWThread      @sa QThread. * @return   @sa QThread    . */ BaseClass* AsBaseClass () { return qobject_cast<BaseClass*>(this); } /// /// @brief  setParent    @sa QObject::setParent. /// @param aParent    "" ///          ///         @sa aAutoDelete ///   TRUE. /// @see FWThread::FWThread /// virtual void setParent(QObject* aParent); /** * @brief  Flags      FWThread. * @return     @sa FWThread::FWThreadFlags */ inline FWThreadFlags Flags () const { return FWThreadFlags(m_flags.loadAcquire ()); } /** * @brief  Machine    ,   * . * @return      . * * @warning        * ,        * . */ FWStateMachine* Machine (); /** * @brief  MachineExists     . * @return  : * @value TRUE    ; * @value FALSE   ,     . */ inline bool MachineExists () { return NOT_NULL(Machine ()); } /** * @brief  IsValid       *        . * @return  : * @value TRUE       ; * @value FALSE       . */ virtual bool IsValid (); /** * @brief  IsReady       . * @return  : * @value TRUE       ; * @value FALSE       . */ inline bool IsReady () const { return Flags ().testFlag (THF_Ready); } /** * @brief  IsAutoDeleted     *         . * @return  : * @value TRUE ,       ; * @value FALSE ,        . * * , * @<code> * void foo () * { * FWTread* v_thread = new FWTread(true); * bool v_with_timeout = <>; * ... * thread.quit (); * * if(v_with_timeout) * { * if(!thread.wait (FW_THREAD_WAIT_TIMEOUT) ) * { * // , * // terminate (); * // wait(1000);, * * //  terminate()  ,     * //   quit() ;) * ... * } * } * else * v_thread->Machine()->stop (); * //    FWTread::quit, FWTread::finished * // FWTread::deleteLater,   v_thread,  * //   @sa FWThread_helper  FWTread::m_helper. * } * @</code> */ inline bool IsAutoDeleted () const { return Flags ().testFlag (THF_AutoDelete); } inline bool IsError () const { return Flags ().testFlag (THF_Error); } inline bool IsWorking () const { return Flags ().testFlag (THF_Working); } inline bool IsPaused () const { return Flags ().testFlag (THF_Paused); } /** * @brief  AttachObject      *  . * @param aObject   . * @return  : * @value TRUE ,    ; * @value FALSE ,     . */ bool AttachObject (QObject* aObject); /// @name    @sa QThread /// @{ /** * @brief    @sa QThread::terminate. *       ,   . */ void terminate(); /** * @brief    @sa QThread::quit. *       ,   . */ void quit(); /// @} protected: /** * @brief  Reconnect     *   . * *             *       .   *      . */ void Reconnect (); /** * @brief  run    @sa QThread::run. *     @sa m_helper    *   @sa InternalRun * *        @sa exec()   *       . *     ,   ,    *     . *         @sa exec(). * *           * . *     @sa BeforeMachineStart. * * @note   @sa BeforeMachineStart   *     . * * @warning        *        @sa AttachObject. * * @see QThreadPrivate::start, QThread::start, QThread::exec, QThread::exit. */ void run() Q_DECL_OVERRIDE; public: /** * @brief  BeforeMachineStart     *          . * * ,         *  . *    , ,     *          * . * * @note        *        .   @sa FWStateMachine *         * .         *  Machine ()->StateGroup ().      *   ,      * .         *  Machine ()->StateIdle (). * *    @sa BeforeMachineStart   FWUsbScanner *  / USB-: * @<code> * * @<code> * * @see FWStateMachine, FWState * @see QStateMachine, QState, QAbstractTransition */ virtual void BeforeMachineStart () {} /** * @brief  BeforeThreadDone     *       . * *      ,     *     ,      . * *  @sa BeforeThreadDone()     . * *        . * * @note  :       *  ,       *   @sa FWThread! * * @note  :       *  ,       *   @sa FWThread! * *          *    @sa FWThread::run. */ virtual void BeforeThreadDone() {} /** * @brief  AfterThreadDone      *      . * *      ,     *     ,      . * *  @sa AfterThreadDone,    @sa BeforeThreadDone(),  *    . * *        . * * @note  :    ,  ,  *  ,       *   @sa FWThread! * *          *    @sa FWThread::run. */ virtual void AfterThreadDone() {} /** * @brief  ThreadDone  ,    *  . * *     ,      *  @sa IsBreak (,    ). *  @sa IsBreak ,  ,    . */ void ThreadDone (); Q_SIGNALS: /** * @brief  sign_MachineChanged      *         (). * @param aPointer      (). */ void sign_MachineChanged(const FWStateMachine* aPointer); /** * @brief  sign_MachineStarted      . * @note    @sa QStateMachine::started; */ void sign_MachineStarted(); /** * @brief  sign_MachineStopped     . * @note    @sa QStateMachine::finished; */ void sign_MachineStopped(); /** * @brief  sign_MachinePaused     . */ void sign_MachinePaused (); /** * @brief  sign_MachineResumed     . */ void sign_MachineResumed (); /** * @brief  sign_ObjectAddress   @sa FWItem::sign_ObjectAddress *  ,     . * @param aMyAddress    ( ) */ void sign_ObjectAddress (QObject* aMyAddress); public Q_SLOTS: /** * @brief  slot_AfterMachineStart     *     . * * ,   ,      *     @sa QThread::exec.  ,  * @sa FWStateMachine::start        *  @sa QThread::exec. * *      QThread::run    * @sa FWStateMachine::start,    @sa QThread::exec. * * @note  :       *  ,       *   @sa FWThread! */ virtual void slot_AfterMachineStart () { if(!IsError ()) SetWorking (); } /** * @brief  slot_Pause    *  @sa FWStateMachine::slot_Pause. */ void slot_Pause (); /** * @brief  slot_Resume    *  @sa FWStateMachine::slot_Resume */ void slot_Resume (); }; //------------------------------------------------------------------------------ FW_END_NAMESPACE Q_DECLARE_OPERATORS_FOR_FLAGS(FW_NAMESPACE::FWThread::FWThreadFlags) #endif // FW_THREAD_H 


FWThread.cpp

 #include "FWThread.h" #include "FWStateMachine.h" #include <FWItem> #include <QException> FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ unsigned long FW_THREAD_WAIT_TIMEOUT = 7000; //------------------------------------------------------------------------------ /** * @brief  FWThread_helper     *      @sa FQThread. * *          *  @sa FQThread.     @sa Qthread::run : * @<code> * FWThread::run() * { * QScopedPointer<FWThread_helper> v_helper(new FWThread_helper(this)); * * } * @</code> * * ""  v_helper    @sa FWThread_helper  *          *   . * *   -  @sa FWThread    * @sa m_ext_thread. * *     @sa m_fsm,   *    @sa FWStateMachine    @sa QStateMachine. * *  @sa ~FWThread_helper     *          * @sa FWThread_helper::m_objects_container. * * @warning  ,   @sa FWThread_helper::m_ext_thread *    @sa FWThread  .  - *     .     . */ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWThread_helper : public QObject { // Q_OBJECT /** * @brief  TSharedObject      @sa QObject *   "" . * * @see QSharedPointer, QObject */ typedef QSharedPointer<QObject> TSharedObject; /** * @brief  TObjectsList    ,  *    @sa m_ext_thread. * * @value TObjectsList::at(<i>)   @sa TSharedObject  *  ,     @sa m_ext_thread. */ typedef QList<TSharedObject> TObjectsList; private: /** * @brief  m_ext_thread     @sa FWThread. */ QPointer<FWThread> m_ext_thread; /** * @brief  m_fsm    , *        * @sa m_ext_thread. */ QPointer<FWStateMachine> m_fsm; /** * @brief  m_objects_container     * @sa TSharedObject   ,    * @sa m_ext_thread. * * @note    e     * @sa FWThread     @sa m_ext_thread. */ TObjectsList m_objects_container; /** * @static * @brief   OnDeleteLater    *     @sa m_objects_container,  *    @sa m_ext_thread. * @param aObject   . */ static void OnDeleteLater(QObject* aObject) { if(NOT_NULL(aObject)) aObject->deleteLater(); } public: /** * @brief  ClearObjects   ,  *    @sa m_ext_thread. */ void ClearObjects () { Q_FOREACH(TSharedObject v_obj, m_objects_container) v_obj.reset(); m_objects_container.clear (); } /** * @brief  LocateObject     . * @param aObject    . * @return  : * @value TRUE     ; * @value FALSE       . */ bool LocateObject (const QObject* aObject) const { Q_FOREACH(TSharedObject v_obj, m_objects_container) if(EQUAL(v_obj.data (),aObject)) return true; return false; } /** * @brief  FWThread_helper  @sa FWThread_helper. * @param aThread     @sa FWThread_helper *   @sa FWThread. * * @warning  @sa aThread    NULL, ..   *    . */ explicit FWThread_helper(FWThread* aThread) : QObject (Q_NULLPTR) , m_ext_thread(aThread) , m_fsm (new FWStateMachine) { Q_ASSERT(NOT_NULL(m_ext_thread)); m_objects_container.clear (); } /** * @brief   @sa FWThread_helper. * *       @sa FWThread::run. *     ,  *    @sa m_ext_thread. * *      @sa FWThread  - *     (,  ,   *   ) */ ~FWThread_helper() { if(NOT_NULL(m_ext_thread)) { m_ext_thread->BeforeThreadDone (); m_ext_thread->ThreadDone (); m_ext_thread->AfterThreadDone (); } ClearObjects (); m_fsm->deleteLater (); } /** * @brief  Machine      *  (). * @return     . * *        ,   *   @sa FWThread. */ FWStateMachine* Machine () const { return m_fsm; } /** * @brief  InsertObject    @sa QObject   * . * @param aObject   . * *       @sa FWItem ,  *       . * * @note         *  @sa FWItem::SF_Active  @sa FWItem::IsActive,  *     - . C @sa FWItem::slot_BreakProcess *    @sa FWStateMachine::finished,    *    @sa FWItem::SF_Active    *     @sa FWThread::quit *  @sa FWThread::terminate. */ void InsertObject (QObject* aObject) { if(NOT_NULL(aObject) && (!LocateObject(aObject)) ) { aObject->setParent (Q_NULLPTR); aObject->moveToThread (m_ext_thread); if( aObject->inherits (FW_CLASS_STR(FWItem).toLatin1 ()) ) { //      QObject::connect (qobject_cast<FWItem*>(aObject), &FWItem:: sign_ObjectAddress, m_ext_thread, &FWThread:: sign_ObjectAddress, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); } m_objects_container.append (TSharedObject(aObject,OnDeleteLater)); } } }; //------------------------------------------------------------------------------ FWThread::FWThread(const bool aAutoDelete, QObject* aParent) : BaseClass (aParent) , m_helper (Q_NULLPTR) { SetEmpty (); if(aAutoDelete && IS_NULL(aParent)) SetAutoDelete (); } //------------------------------------------------------------------------------ FWThread::~FWThread() { quit (); if(!wait (FW_THREAD_WAIT_TIMEOUT)) { // ,   ... terminate (); if(!wait(FW_THREAD_WAIT_TIMEOUT)) exit(EXIT_FAILURE); } } //------------------------------------------------------------------------------ void FWThread::setParent(QObject* aParent) { BaseClass::setParent (aParent); bool is_autodelete = bool(Flags () | THF_AutoDelete); if(IS_NULL(aParent) && (!is_autodelete)) SetAutoDelete (); else if(NOT_NULL(aParent) && (is_autodelete)) UnsetAutoDelete (); if(!IsAutoDeleted ()) disconnect( this, &QThread::finished, this, &QThread::deleteLater); } //------------------------------------------------------------------------------ FWStateMachine* FWThread::Machine() { return m_helper.isNull () ? Q_NULLPTR : m_helper->Machine (); } //------------------------------------------------------------------------------ bool FWThread::IsValid() { return (!IsError()) && (MachineExists () ? Machine ()->IsValid () : false); } //------------------------------------------------------------------------------ bool FWThread::AttachObject(QObject* aObject) { bool v_retval = !m_helper.isNull (); if(v_retval) m_helper->InsertObject (aObject); return v_retval; } //------------------------------------------------------------------------------ void FWThread::terminate() { ThreadDone (); BaseClass::terminate (); } //------------------------------------------------------------------------------ void FWThread::quit() { ThreadDone (); BaseClass::quit (); } //------------------------------------------------------------------------------ void FWThread::Reconnect() { if(IsAutoDeleted ()) //       connect( this, &QThread::finished, this, &QThread::deleteLater, Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection)); if(MachineExists ()) { //         //     connect( Machine (),&FWStateMachine:: finished, this, &QThread:: quit, Qt::ConnectionType(Qt::AutoConnection | Qt::UniqueConnection)); //         //  . connect( this, &QThread:: finished, Machine (),&FWStateMachine:: stop, Qt::ConnectionType(Qt::AutoConnection | Qt::UniqueConnection)); /// @name       @sa FWThread /// @{ connect (Machine (),&FWStateMachine:: started, this, &FWThread:: sign_MachineStarted, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect (Machine (),&FWStateMachine:: stopped, this, &FWThread:: sign_MachineStopped, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect (Machine (),&FWStateMachine:: sign_Paused, this, &FWThread:: sign_MachinePaused, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect (Machine (),&FWStateMachine:: sign_Resumed, this, &FWThread:: sign_MachineResumed, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); /// @} //         //  r  connect (Machine (),&FWStateMachine:: started, this, &FWThread:: slot_AfterMachineStart, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); } } //------------------------------------------------------------------------------ void FWThread::InternalRun() { QException _e; QT_TRY { if(IsValid ()) { ///         Reconnect (); SetReady (); emit sign_MachineChanged(Machine ()); } else _e.raise (); } QT_CATCH(QException &e) { UnsetAutoDelete (); SetError (); qDebug() << qPrintable(e.what ()); } } //------------------------------------------------------------------------------ void FWThread::run() { FWThread_helper* v_helper = new FWThread_helper(this); m_helper = v_helper; InternalRun (); if(IsValid ()) { BeforeMachineStart (); ///     @sa Machine (). Machine ()->start (); ///  a   . =>   . BaseClass::exec (); } else { if(!IsError ()) SetError (); exit (FW_ERR_THREAD_INCOMPLETE); } m_helper.clear (); } //------------------------------------------------------------------------------ void FWThread::ThreadDone() { SetStop (); if(MachineExists ()) { if(Machine ()->IsActive ()) Machine ()->stop (); // QObject::disconnect (Machine ()); } } //------------------------------------------------------------------------------ void FWThread::slot_Pause() { if(MachineExists()) Machine ()->slot_Pause(); } //------------------------------------------------------------------------------ void FWThread::slot_Resume() { if(MachineExists()) Machine ()->slot_Resume(); } //------------------------------------------------------------------------------ FW_END_NAMESPACE 



FWThread ::BeforeMachineStart (). doxygen FWThread.h.

, FWThread::run() !
FWStateMachine::BeforeMachineStart () , , .

- FWThread_helper .
, .
FWThread_helper , , , .

4. .


Everything is simple here. QEvent Qt, «». QEvent::User , ( ) , … .
FWEvent .

 #ifndef FW_EVENT_H #define FW_EVENT_H #include <FWConstants> FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ class FWStateMachine; //------------------------------------------------------------------------------ /** * @brief  FWEvent   ,    *  . * *      ()  *    ( -- )  @sa FWStateMachine. *  -  @sa FWItem     * . *  -  @sa FWStateMachine     *   . *    -      * .  @sa FWEvent::IsSystem    . * *   @sa FWTransition    *  .   @sa FWTransition::testEvent  * ,      @sa FWEvent::Machine . */ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWEvent : public QEvent { Q_GADGET Q_ENUMS(FWEventType) //---------------------------------------------------------------------------- private: /** * @brief  m_id     . */ FWIdentificator m_id; /** * @brief  m_params       *  (  ),   . */ QVariantList m_params; //---------------------------------------------------------------------------- public: /** * @brief  FWEvent::FWEvents     *   . */ enum FWEventType { ET_SYSTEM, ///<    /// @name   @sa FWEvent   /// @{ ET_GLOBAL = QEvent::User + 1, ///<   (  )  ET_INTERNAL = ET_GLOBAL + 1, ///<    //TODO:    ... // ET_SIGNAL = ET_INTERNAL + 1, ///<   "" ET_TASK = ET_INTERNAL + 1, ///<     /// @} ET_MAX = ET_TASK + 1, ///<     ///<    }; //-------------------------------------------------------------------------- /// @name    . /// @{ /** * @brief  FWEvent     . * @param aEvent  ; * @param aID  .      *  @sa EMPTY_ID. * @param aParams    . * * @note     ,   *   -:     *     -.      *  . * * @see QEvent, QEvent::Type, FWIdentificator, QVariantList. */ explicit FWEvent(QEvent::Type aEvent, FWIdentificator aID = EMPTY_ID, const QVariantList& aParams = QVariantList()) : QEvent(aEvent) , m_id (aID) { SetParams (aParams); } /** * @brief  FWEvent     * . * @param aEvent  ,    @sa FWEvent::FWEventType; * @param aID  ; * @param aParams    . */ explicit FWEvent(FWEvent::FWEventType aEvent, FWIdentificator aID, const QVariantList& aParams = QVariantList()) :FWEvent(static_cast<QEvent::Type>(aEvent), aID, aParams) {} /** * @brief  FWEvent     . * @param aEvent C  - . */ explicit FWEvent(const FWEvent& aEvent) : FWEvent (aEvent.type (), aEvent.Identificator (), aEvent.Params ()) {} /// @} //-------------------------------------------------------------------------- /** * @brief  AsBaseClass        *   . * @return        QEvent. */ QEvent* AsBaseClass () { return static_cast<QEvent*>(this); } static bool IsValidType (QEvent::Type aType) { return BETWEEN(int(aType),QEvent::None, ET_MAX); } /** * @brief  IsValid      , *  . * @param aEvent    * @return  : * @value TRUE    ; * @value FALSE     . */ static bool IsValid (QEvent* aEvent) { if(NOT_NULL(aEvent)) return IsValidType(aEvent->type()); return false; } /** * @static * @brief  IsType        *  @sa FWEvent::FWEventType. * @param aEvent  ; * @param aType     . * @return  : * @value TRUE      ; * @value FALSE       ; */ static bool IsType (QEvent* aEvent, FWEvent::FWEventType aType) { bool v_retval = IsValid (aEvent); if(v_retval) { v_retval = BETWEEN (int(aEvent->type ()),QEvent::None,ET_GLOBAL) || EQUAL (aType,ET_SYSTEM); if(!v_retval) v_retval = EQUAL(static_cast<FWEvent::FWEventType>(aEvent->type ()), aType); } return v_retval; } /** * @brief  IsSystem        *  (   @sa QEvent::Type  QEvent::User) * @param aEvent  . * @return  : * @value TRUE  ; * @value FALSE   . */ static bool IsSystem (QEvent* aEvent) { return IsType (aEvent, ET_SYSTEM); } /** * @brief  IsGlobal        * ,   @sa FWEventType::ET_GLOBAL * @param aEvent  . * @return  : * @value TRUE   ; * @value FALSE    . */ static bool IsGlobal (QEvent* aEvent) { return IsType (aEvent, ET_GLOBAL); } /** * @brief  IsGlobal        * ,   @sa FWEventType::ET_INTERNAL * @param aEvent  . * @return  : * @value TRUE  ; * @value FALSE   . */ static bool IsInternal (QEvent* aEvent) { return IsType (aEvent, ET_INTERNAL); } /** * @brief  IsGlobal       *   ,   @sa FWEventType::ET_TASK * @param aEvent  . * @return  : * @value TRUE    ; * @value FALSE     . */ static bool IsTask (QEvent* aEvent) { return IsType (aEvent, ET_TASK); } /** * @brief  Identificator    *   @sa FWEvent::FWEventType * @return   . */ inline FWIdentificator Identificator () const { return m_id; } inline QVariantList Params () const { return m_params; } inline void SetIdentificator (const FWIdentificator aID) { if(NOT_EQUAL(Identificator (), aID) ) m_id = aID; } inline void SetParams (const QVariantList& aParams) { m_params.clear (); m_params.append (aParams); } inline virtual bool IsValid () { return IsValid (AsBaseClass ()) && NOT_EQUAL(Identificator (),EMPTY_ID); } virtual FWEvent& operator = (const FWEvent& aFrom) { AsBaseClass ()->operator = (aFrom); SetIdentificator(aFrom.Identificator ()); SetParams (aFrom.Params ()); return (*this); } inline virtual bool operator == (const FWEvent& aFrom) { return EQUAL(type (), aFrom.type () ) && EQUAL(Identificator (), aFrom.Identificator ()); } inline virtual bool operator == (const QEvent& aFrom) { return EQUAL(type (), aFrom.type ()); } inline virtual bool operator != (const FWEvent& aFrom) { return NOT_EQUAL(*this, aFrom); } inline virtual bool operator != (const QEvent& aFrom) { return NOT_EQUAL(*this, aFrom); } }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWSystemEvent : public FWEvent { Q_GADGET public: explicit FWSystemEvent(FWIdentificator aID, const QVariantList& aParams = QVariantList()) : FWEvent(FWEvent::ET_SYSTEM, aID, aParams) { Q_ASSERT(IsSystem (AsBaseClass ())); } }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWGlobalEvent : public FWEvent { Q_GADGET public: explicit FWGlobalEvent(FWIdentificator aID, const QVariantList& aParams = QVariantList()) : FWEvent(FWEventType::ET_GLOBAL, aID, aParams) {} }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWInternalEvent : public FWEvent { Q_GADGET Q_ENUMS(FWInternalIdentificators) public: enum FWInternalIdentificators { OEI_None = FWIdentificator(0x00), OEI_Start = FWIdentificator(0x01), OEI_Stop = FWIdentificator(0x02), OEI_Pause = FWIdentificator(0x04), OEI_Resume= FWIdentificator(0x08), OEI_Log = FWIdentificator(0x10), OEI_Error = FWIdentificator(0x20), }; explicit FWInternalEvent(FWIdentificator aID = OEI_None, const QVariantList& aParams = QVariantList()) : FWEvent(FWEventType::ET_INTERNAL, aID, aParams) {} bool IsValid () Q_DECL_OVERRIDE { return FWEvent::IsValid () && NOT_EQUAL(Identificator (),OEI_None); } }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWTaskEvent : public FWEvent { Q_GADGET public: enum FWUsbTask { TEI_None = FWIdentificator(0), TEI_Open, TEI_Opened, TEI_Close, TEI_Closed, TEI_NewStatus, TEI_NewFileName, TEI_Write, TEI_Writed, TEI_Read, TEI_Readed, TEI_Log, TEI_Error, }; explicit FWTaskEvent(FWIdentificator aID = EMPTY_ID, const QVariantList& aParams = QVariantList()) : FWEvent(FWEventType::ET_TASK, aID, aParams) {} bool IsValid () Q_DECL_OVERRIDE { return FWEvent::IsValid () && NOT_EQUAL(Identificator (),EMPTY_ID); } }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWUsbEvent : public FWTaskEvent { Q_GADGET private: quint16 m_VID; quint16 m_PID; quint8 m_BusNumber; quint8 m_Address; quint8 m_Port; public: enum FWUsbTask { UEI_None = FWIdentificator(0), UEI_Attached, UEI_Detached, UEI_ContextChanged, UEI_Open, UEI_Opened, UEI_Close, UEI_Closed, UEI_WriteBulk, UEI_WriteBulkComplete, UEI_ReadBulk, UEI_ReadBulkComplete, UEI_Log, UEI_Warning, UEI_Error, }; explicit FWUsbEvent(quint16 aVendorID, quint16 aProductID, quint8 aBusNumber, quint8 aAddress, quint8 aPort, FWIdentificator aID = FWUsbEvent::UEI_None, const QVariantList& aParams = QVariantList()) : FWTaskEvent (FWIdentificator(aID), aParams) , m_VID (aVendorID) , m_PID (aProductID) , m_BusNumber (aBusNumber) , m_Address (aAddress) , m_Port (aPort) {} explicit FWUsbEvent(FWIdentificator aID, const QVariantList& aParams = QVariantList()) : FWUsbEvent(0,0,0,0,0,aID,aParams) {} inline quint16 VID () const { return m_VID; } inline quint16 PID () const { return m_PID; } inline quint8 BusNumner () const { return m_BusNumber; } inline quint8 Address () const { return m_Address; } inline quint8 Port () const { return m_Port; } FWUsbEvent& operator = (const FWUsbEvent& aFrom) { FWEvent::operator = (static_cast<FWEvent>(aFrom)); m_VID = aFrom.VID (); m_PID = aFrom.PID (); m_BusNumber = aFrom.BusNumner (); m_Address = aFrom.Address (); m_Port = aFrom.Port (); return (*this); } bool operator == (const FWUsbEvent& aFrom) { return FWEvent::operator == (static_cast<FWEvent>(aFrom)) && EQUAL(VID (), aFrom.VID ()) && EQUAL(PID (), aFrom.PID ()) && EQUAL(BusNumner (), aFrom.BusNumner ()) && EQUAL(Address (), aFrom.Address ()) && EQUAL(Port (), aFrom.Port ()); } }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// /// @brief FWEventTypeMap /// @see FWEvent, QEvent, QEvent::Type /// typedef QMap<FWEvent::FWEventType, FWEvent*> FWEventTypeMap; typedef QMapIterator<FWEvent::FWEventType, FWEvent*> FWEventTypeMap_I1; typedef FWEventTypeMap::Iterator FWEventTypeMap_I2; typedef FWEventTypeMap::ConstIterator FWEventTypeMap_CI; /// /// @brief FWEventMap /// @see FWEvent, QEvent, QEvent::Type, FWIdentificator /// typedef QMap<FWIdentificator, FWEvent*> FWEventMap; typedef QMapIterator<FWIdentificator, FWEvent*> FWEventMap_I1; typedef FWEventMap::Iterator FWEventMap_I2; typedef FWEventMap::ConstIterator FWEventMap_CI; //------------------------------------------------------------------------------ FW_END_NAMESPACE //------------------------------------------------------------------------------ Q_DECLARE_METATYPE(FW_NAMESPACE::FWEventTypeMap) Q_DECLARE_METATYPE(FW_NAMESPACE::FWEventMap) //------------------------------------------------------------------------------ #endif // FW_EVENT_H 


, , FWStateMashine , FWInternalEvent , () FWTskEvent .

, . . . «Qt4.7+. C++» , , , , .

QAbstractTransition::eventTest(QEvent* aEvent) ,
:

 bool FWTransition::eventTest(QEvent* aEvent) { bool v_retval = FWEvent::IsValid (aEvent); if(v_retval) v_retval = EQUAL(aEvent->type (),EventType ()); if(v_retval) { FWEvent* v_e = static_cast<FWEvent*>(aEvent); v_retval = EQUAL(v_e->type (), EventType ()) && EQUAL(v_e->Identificator (),EventID ()); } return v_retval; } 


, FWTransition .

.




5. . USB- libUSB.


« + ».
USB-.
API libUSBx . , USB- .

libUSB libusb_context . , .

USB- . USB-, . libusb_init(NULL) . . , .

libusb_init() NULL , . libusb_init () , .
libUSB , . «» libUSB , : libusb_open () , .
libusb_exit () , API libUSB , libusb_exit () .

, libusb_get_device_list() .
, , libusb_device , .
USB- :
  1. VID:
  2. PID;
  3. ;
  4. ;
  5. .


VID PID libusb_device_descriptor , libusb_get_bus_number() , libusb_get_device_address() libusb_get_port_number() .
, QEvent , .

 class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWUsbEvent : public FWTaskEvent { Q_GADGET private: quint16 m_VID; quint16 m_PID; quint8 m_BusNumber; quint8 m_Address; quint8 m_Port; public: enum FWUsbTask { UEI_None = FWIdentificator(0), UEI_Attached, UEI_Detached, UEI_ContextChanged, UEI_Open, UEI_Opened, UEI_Close, UEI_Closed, UEI_WriteBulk, UEI_WriteBulkComplete, UEI_ReadBulk, UEI_ReadBulkComplete, UEI_Log, UEI_Warning, UEI_Error, }; explicit FWUsbEvent(quint16 aVendorID, quint16 aProductID, quint8 aBusNumber, quint8 aAddress, quint8 aPort, FWIdentificator aID = FWUsbEvent::UEI_None, const QVariantList& aParams = QVariantList()) : FWTaskEvent (FWIdentificator(aID), aParams) , m_VID (aVendorID) , m_PID (aProductID) , m_BusNumber (aBusNumber) , m_Address (aAddress) , m_Port (aPort) {} explicit FWUsbEvent(FWIdentificator aID, const QVariantList& aParams = QVariantList()) : FWUsbEvent(0,0,0,0,0,aID,aParams) {} inline quint16 VID () const { return m_VID; } inline quint16 PID () const { return m_PID; } inline quint8 BusNumner () const { return m_BusNumber; } inline quint8 Address () const { return m_Address; } inline quint8 Port () const { return m_Port; } FWUsbEvent& operator = (const FWUsbEvent& aFrom) { FWEvent::operator = (static_cast<FWEvent>(aFrom)); m_VID = aFrom.VID (); m_PID = aFrom.PID (); m_BusNumber = aFrom.BusNumner (); m_Address = aFrom.Address (); m_Port = aFrom.Port (); return (*this); } bool operator == (const FWUsbEvent& aFrom) { return FWEvent::operator == (static_cast<FWEvent>(aFrom)) && EQUAL(VID (), aFrom.VID ()) && EQUAL(PID (), aFrom.PID ()) && EQUAL(BusNumner (), aFrom.BusNumner ()) && EQUAL(Address (), aFrom.Address ()) && EQUAL(Port (), aFrom.Port ()); } }; 



FWUscbInstance FWExecuteInstance. FWExecuteInstance FWDocument < — FWItem.
FWExecuteInstance.h

 #ifndef FW_EXECUTE_INSTANCE_H #define FW_EXECUTE_INSTANCE_H #include <FWDocument> FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWExecuteInstance : public FWDocument { Q_OBJECT Q_CLASSINFO("brief", "FW Execute Instance Class") Q_CLASSINFO("created", "12-MAY-2015") Q_CLASSINFO("modified", "12-MAY-2015") // Q_CLASSINFO("project", "Common Qt-based Framework") /// @name   . /// @{ typedef FWDocument BaseClass; /// @} private: /// /// @brief m_timer /// QTimer m_timer; /// /// @brief m_timer_mode /// bool m_timer_mode; public: Q_INVOKABLE explicit FWExecuteInstance(bool aTimerMode = false, QObject* aParent = Q_NULLPTR); bool IsTimerMode() const { return m_timer_mode; } void Open () Q_DECL_OVERRIDE; void Close () Q_DECL_OVERRIDE; inline void StartTimer (int aMSec = 10) { if((!m_timer.isActive ()) && IsTimerMode ()) m_timer.start (aMSec); } inline void StopTimer () { if(m_timer.isActive ()) m_timer.stop (); } inline bool IsTiming () const { return m_timer.isActive () && IsTimerMode (); } void SetTimerMode (bool aTimerMode) { if(EQUAL(m_timer_mode, aTimerMode)) return; bool is_timing = IsTiming (); if(is_timing) StopTimer (); m_timer_mode = aTimerMode; if(is_timing || IsTimerMode ()) StartTimer (); } public slots: virtual void slot_OnExecute () {} void slot_BreakProcess () Q_DECL_OVERRIDE; virtual void slot_ResumeProcess () { Open ();//SetStatus (SF_Active); if(IsTimerMode ()) StartTimer (); } }; //------------------------------------------------------------------------------ FW_END_NAMESPACE #endif // FW_EXECUTE_INSTANCE_H 


FWUscbInstance.h

 #ifndef FW_USB_INSTANCE_H #define FW_USB_INSTANCE_H #include "FWUsbTypes.h" #include "FWExecuteInstance.h" FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ class FWUsbInstance; class FWUsbConnection; //------------------------------------------------------------------------------ /// /// \language Russian /// @brief  FWUsbInstance      ///  libusb /// ///     libusb   libusb . ///   libusb      ///    libusb (    ) ///        /// libusb - , libusb_set_debug ()       /// ,  libusb_exit ()    ,   ///  - . /// ///   libusb_init ()    libusb_exit (). /// class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWUsbInstance : public FWExecuteInstance { Q_OBJECT Q_CLASSINFO("brief", "FW LibUSB Instance Class") Q_CLASSINFO("created", "27-MAR-2015") Q_CLASSINFO("modified", "12-MAY-2015") // Q_CLASSINFO("project", "Common Qt-based Framework") /// @name   . /// @{ typedef FWExecuteInstance BaseClass; /// @} private: /// @name     /// @{ /// /// @brief m_context    libusb /// @see libusb_context, TUsbContext* /// TUsbContext* m_context; /// /// @brief m_log_level    /// @see libusb_log_level TUsbLogLevel /// TUsbLogLevel m_log_level; QList<QVector<int> > m_attached_list; /// @} public: /// @name    . /// @{ Q_INVOKABLE explicit FWUsbInstance(bool aTimerMode = false /* Thread or Manual*/, TUsbLogLevel aLogLevel = LIBUSB_LOG_LEVEL_ERROR /*LIBUSB_LOG_LEVEL_DEBUG*/, QObject* aParent = Q_NULLPTR); /// @} BaseClass* AsBaseClass () Q_DECL_OVERRIDE { return AsClass<BaseClass>(); } bool IsValid () const Q_DECL_OVERRIDE { return BaseClass::IsValid() && ContextExists (); } bool IsActive () const Q_DECL_OVERRIDE { return IsValid() && BaseClass::IsActive(); } virtual void OpenUsbContext (bool aCloseOldContext = false); virtual void CloseUsbContext (); void SetContext (TUsbContext* aContext); virtual bool ContextExists () const { return NOT_ZERRO(UsbContextAsID ()); } TUsbContext* UsbContext () const { return m_context; } void MakeUsbDeviceList(); void Open () Q_DECL_OVERRIDE; void Close () Q_DECL_OVERRIDE; void Reload (); protected: void Initialisation () Q_DECL_OVERRIDE; inline FWIdentificator UsbContextAsID () const { return FWIdentificator(UsbContext()); } void ClearUsbDeviceList (); Q_SIGNALS: void sign_ContextChanged (const QObject*); /// void sign_UsbDeviceAttached (quint8 aBus, quint8 aAddr, quint8 aPort, quint16 aVendorID, quint16 aProductID); /// /// @brief sign_UsbDeviceDeleted /// @param aDevice /// void sign_UsbDeviceDetached(quint8 aBus, quint8 aAddr, quint8 aPort, quint16 aVendorID, quint16 aProductID); // void // sign_DeviceListChanged (const TUsbDeviceList& aList, // const FWUsbInstance* aInstance); // void // sign_UsbDeviceStarted (const FWThreadController* aController); public Q_SLOTS: void slot_OnExecute () Q_DECL_OVERRIDE; void slot_OnPause (); void slot_OnResume (); bool slot_ApplyFilter () Q_DECL_OVERRIDE; void slot_OnLoop (); }; //------------------------------------------------------------------------------ /// /// @brief FWSharedUsbInstance  @a QSharedPointer   ///  @a FWUsbInstance /// typedef QSharedPointer<FWUsbInstance> FWSharedUsbInstance; //------------------------------------------------------------------------------ FW_END_NAMESPACE #endif // FW_USB_INSTANCE_H 


FWUsbInstance::Reload() FWUsbInstance USB-.

 void FWUsbInstance::Reload() { if(!IsActive()) return; libusb_set_debug (m_context, LIBUSB_LOG_LEVEL_NONE); //     QList<QVector<int> > _list_prev = m_attached_list; //     ... MakeUsbDeviceList(); // ...     QList<QVector<int> > _list_now = m_attached_list; //    if(!_list_prev.isEmpty ()) for(register int i = 0; i < _list_prev.size (); ++i) if(_list_now.indexOf (_list_prev.at (i)) < 0) { slot_OnMachineEvent( new FWUsbEvent( quint16(_list_prev.at (i).at (3)),//VID quint16(_list_prev.at (i).at (4)),//PID quint8 (_list_prev.at (i).at (0)),//Bus Number quint8 (_list_prev.at (i).at (1)),//Address quint8 (_list_prev.at (i).at (2)),//Port Number FWUsbEvent::UEI_Detached,));//   } //     if(!_list_now.isEmpty ()) for(register int i = 0; i < _list_now.size (); ++i) if(!_list_prev.contains (_list_now.at (i))) { slot_OnMachineEvent( new FWUsbEvent( quint16(_list_prev.at (i).at (3)),//VID quint16(_list_prev.at (i).at (4)),//PID quint8 (_list_prev.at (i).at (0)),//Bus Number quint8 (_list_prev.at (i).at (1)),//Address quint8 (_list_prev.at (i).at (2)),//Port Number FWUsbEvent::UEI_Attached));//   } libusb_set_debug (m_context, m_log_level); } 



, …
, QObject - FWItem ( QObject), slot_OnMachineEvent()
 void FWItem::slot_OnMachineEvent(FWEvent* aEvent) { if(MachineExists()) Machine()->PostEvent(aEvent); } 


.


.
FWThread::BeforeMachineStart()
- FWThread FWUsbScanner .
FWUsbScanner:: BeforeMachineStart()

 void FWUsbScanner::BeforeMachineStart() { if(!IsValid ()) return; if(m_usb.isNull()) m_usb = new FWUsbInstance(m_timer_mode); m_usb->SetMachine(Machine()); /// @name      USB- /// @{ QObject::connect(m_usb,&FWUsbInstance::sign_UsbDeviceAttached, this, &FWUsbScanner:: sign_UsbDeviceAttached, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); QObject::connect(m_usb,&FWUsbInstance::sign_UsbDeviceDetached, this, &FWUsbScanner:: sign_UsbDeviceDetached, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); QObject::connect(m_usb,&FWUsbInstance::sign_ContextChanged, this, &FWUsbScanner:: sign_ContextChanged, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); /// @} //       if(AttachObject (m_usb.data())) QT_TRY { /// @name      USB- /// @{ QState* sIdle = Machine ()->StateIdle (); //    " " FWState* sOpen = new FWState(Machine ()->StateGroup ()); //    "" FWState* sScan = new FWState(Machine ()->StateGroup ()); sScan->setObjectName("USB Scan"); //   " "    Machine()->AddBranch(sIdle, sOpen); QObject::connect(sOpen, &FWState::entered, m_usb, &FWUsbInstance:: slot_Open, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); //   ""    Machine()->AddBranch(FWTaskEvent::ET_TASK, FWTaskEvent::TEI_Opened, sOpen, sScan); if(!m_usb->IsTimerMode()) { //    :      //    FWState* sLoop = new FWState(Machine ()->StateGroup ()); Machine()->AddBranch(sScan, sLoop); Machine()->AddBranch(FWEvent::ET_TASK,FWTaskEvent::TEI_Read, sLoop, sScan); QObject::connect(sLoop, &FWState::entered, m_usb, &FWUsbInstance:: slot_OnLoop, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); } //     "" QObject::connect(sScan, &FWState::entered, m_usb, &FWUsbInstance:: slot_OnExecute, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); //   ""  ""   QObject::connect(Machine(),&FWStateMachine::sign_Paused, m_usb, &FWUsbInstance:: slot_OnPause, Qt::DirectConnection); //    ""    QObject::connect(Machine(),&FWStateMachine::sign_Resumed, m_usb, &FWUsbInstance:: slot_OnResume, Qt::DirectConnection); /// @} return; //    } QT_CATCH(QException& e) { e.what (); } //     ,    SetAutoDelete(); //    exit() m_usb->deleteLater (); //    m_usb = Q_NULLPTR; exit (EXIT_FAILURE); //     } 


– .
:
FWUsbInstance:: slot_OnExecute() .
 void FWUsbInstance::slot_OnExecute() { // if(IsTimerMode()) // { bool _tm = IsTiming (); if(_tm) StopTimer (); Reload (); if(_tm) StartTimer (10); // } // else // while(IsActive()) // Reload (); } 

– . , FWUsbScanner::BeforeMachineStart() Machine()->AddBranch(sScan, sLoop) , , while() . Reload() .

Consider this in more detail.
The IsActive () method checks the class activity flag.
The slot slot_BreakProcess () of the base class FWItem clears the flag, the
operator
  QObject::connect(Machine(),&FWStateMachine::sign_Paused, m_usb, &FWUsbInstance:: slot_OnPause, Qt::DirectConnection); 

Slots
 void FWUsbInstance::slot_OnPause() { slot_BreakProcess(); } 

AND
 void FWUsbInstance::slot_OnResume() { slot_ResumeProcess(); } 

should manage the suspension and resumption of work flow.
Everything should work out.
But there will be no pause, the cycle will not end.
What's the matter?
, Machine() postEvent() , .. . slot_OnExecute() , , .
, slot_OnExecute() : while() . «» QCoreApplication::processEvents() !

, , .
(sLoop) .
 void FWUsbInstance::slot_OnLoop() { if(MachineExists()) Machine()->PostEvent(FWEvent::ET_TASK,FWTaskEvent::TEI_Read); } 

, «» . , , . , . , – .

, , , .

Total




Notes


.
() . . , .

. «» .

– - . :) , .



FWStateMachine.h

 #ifndef FWSTATEMACHINE_H #define FWSTATEMACHINE_H #ifndef QSTATEMACHINE_DEBUG # define QSTATEMACHINE_DEBUG #endif #include "FWState.h" FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ /// /// @brief  FWStateMachine   " ". /// @see QStateMachine /// class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWStateMachine : public QStateMachine { friend class FWThread; Q_OBJECT Q_CLASSINFO("brief", "Finite-state machine Class") Q_CLASSINFO("created", "03-JUN-2015") Q_CLASSINFO("modified", "03-JUN-2015") // Q_CLASSINFO("project", "Common Qt-based Framework") // Q_CLASSINFO("info_en", "https://en.wikipedia.org/wiki/Finite-state_machine") Q_CLASSINFO("info_ru", "https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82") /// @name   . /// @{ ///        typedef QStateMachine BaseClass; /// @} //---------------------------------------------------------------------------- private: /// /// @brief  m_final_signals      ///    ,     ( /// )    . /// @value FWFinalSignalsMap::key()  .   , ///         ///   ; /// @value FWFinalSignalsMap::value ()    .  ///        , ///  - . /// QScopedPointer<FWState> m_state_group; /// /// @brief  m_history_state      ///     . ///    ""  .    , ///         , ///        ,    ///  (..    , ,   , ///  @sa m_state_main) /// /// @note      ,  ///       ;   ///       /// ,     ()  /// ,      . ///     - ,   ,   ///  ,      ; /// ,  ""     /// . (\a http://doc.crossplatform.ru/qt/4.7.x/statemachine-api.html) /// QScopedPointer<QHistoryState> m_state_history; /// /// @brief  m_final_state     ///         ///     . /// QScopedPointer<QFinalState> m_state_final; /// /// @brief  m_state_error     ///      . /// QScopedPointer<FWState> m_state_error; //---------------------------------------------------------------------------- public: explicit FWStateMachine(QObject* aParent = Q_NULLPTR); explicit FWStateMachine(QState::ChildMode aChildMode, QObject* aParent = Q_NULLPTR); BaseClass* AsBaseClass() { return qobject_cast<BaseClass*>(this); } bool StateGroupExists () const { return !m_state_group.isNull (); } FWState* StateGroup () const { return StateGroupExists () ? m_state_group.data () : Q_NULLPTR; } inline FWState* StateIdle () const { return StateGroupExists () ? qobject_cast<FWState*>(StateGroup()->initialState ()): Q_NULLPTR; } inline bool StateHistoryExists () const { return !m_state_history.isNull (); } inline QHistoryState* StateHistory () const { return StateHistoryExists () ? m_state_history.data () : Q_NULLPTR; } inline bool StateFinalExists () const { return !m_state_final.isNull (); } inline QFinalState* StateFinal () const { return StateFinalExists () ? m_state_final.data () : Q_NULLPTR; } inline bool StateErrorExists () const { return !m_state_error.isNull (); } inline FWState* StateError () const { return StateErrorExists () ? m_state_error.data () : Q_NULLPTR; } /// /// @brief  IsValid       ///    . /// @note       ,      ///     . /// @return      : /// @value TRUE    ; /// @value FALSE     :     ///     c   @sa SubscribeFinalSignal; /// @see SubscribeFinalSignal /// virtual bool IsValid () const { return NOT_NULL(initialState ()); } virtual bool IsActive () const { return isRunning (); } /** * @brief  PostEvent    @sa FWEvent   *   . * @param aEvent     @sa FWEvent; * @param priority   . */ inline void PostEvent(FWEvent* aEvent, EventPriority priority = NormalPriority) { if(NOT_NULL(aEvent)) BaseClass::postEvent (aEvent->AsBaseClass (), priority) ; } /** * @brief  PostEvent    @sa FWEventType    *       . * @param aEventType * @param aID * @param priority */ inline void PostEvent(FWEvent::FWEventType aEventType, FWIdentificator aID, EventPriority priority = NormalPriority) { PostEvent (new FWEvent(aEventType,aID), priority) ; } /** * @brief  postDelayedEvent   *  @sa QStateMachine::postDelayedEvent    *  @sa FWEvent      c  *  . * @param aEvent     @sa FWEvent; * @param aDelay    . * @return ,   ,  -1   *   . */ inline int PostDelayedEvent(FWEvent* aEvent, int aDelay) { if(NOT_NULL(aEvent)) return BaseClass::postDelayedEvent (aEvent->AsBaseClass (), aDelay) ; return -1; } /** * @brief PostDelayedEvent * @param aEventType * @param aID * @param aDelay */ inline void PostDelayedEvent(FWEvent::FWEventType aEventType, FWIdentificator aID, int aDelay) { PostDelayedEvent(new FWEvent(aEventType,aID), aDelay) ; } /** * @brief  Configuration   @sa QStateMachine::configuration *    @sa FWState   . * @return    @sa FWState . */ QSet<FWState*> Configuration() const; /// @name        /// @{ /** * @brief AddBranch * @param aSource * @param aTarget * @return */ virtual bool AddBranch ( QState* aSource, QAbstractState* aTarget ) { if(NOT_NULL(aSource)) return aSource->addTransition(aTarget); return false; } /** * @brief AddBranch * @param aEventType * @param aID * @param aSource * @param aTarget * @return */ virtual bool AddBranch ( QEvent::Type aEventType, FWIdentificator aID, QState* aSource, QAbstractState* aTarget = Q_NULLPTR ); /** * @brief AddBranch * @param aEventType * @param aID * @param aSource * @param aTarget * @return */ virtual bool AddBranch ( FWEvent::FWEventType aEventType, FWIdentificator aID, QState* aSource, QAbstractState* aTarget = Q_NULLPTR ); /** * @brief AddBranch * @param aEvent * @param aSource * @param aTarget * @return */ virtual bool AddBranch ( FWEvent* aEvent, QState* aSource, QAbstractState* aTarget = Q_NULLPTR ); /// @} //---------------------------------------------------------------------------- protected: /// /// @brief  Initialisation /// virtual void Initialisation (); //---------------------------------------------------------------------------- Q_SIGNALS: /// /// @brief  sign_Paused       . /// void sign_Paused (); /// /// @brief  sign_Resumed       . /// @see QStateMachine::started, QStateMachine::stopped /// void sign_Resumed (); //---------------------------------------------------------------------------- public Q_SLOTS: /// /// @brief  slot_Pause       . /// void slot_Pause () { postEvent (new FWInternalEvent(FWInternalEvent::OEI_Pause)); } /// /// @brief  slot_Resume       . /// void slot_Resume () { postEvent (new FWInternalEvent(FWInternalEvent::OEI_Resume)); } //---------------------------------------------------------------------------- protected Q_SLOTS: /// /// @brief slot_OnLoging /// virtual void slot_OnLoging () { qDebug() << qPrintable(Q_FUNC_INFO); } /// /// @brief slot_OnError /// virtual void slot_OnError () { qDebug() << qPrintable(Q_FUNC_INFO); } /// /// @brief slot_OnPaused /// virtual void slot_OnPaused () { qDebug() << qPrintable(Q_FUNC_INFO); if(thread ()) thread ()->yieldCurrentThread (); emit sign_Paused(); } /// /// @brief slot_OnResume /// virtual void slot_OnResume () { qDebug() << qPrintable(Q_FUNC_INFO); emit sign_Resumed(); } /// /// @brief slot_OnStop /// virtual void slot_OnStop () { qDebug() << qPrintable(Q_FUNC_INFO); } /// /// @brief slot_OnDefaultHistoryChanged /// virtual void slot_OnDefaultHistoryChanged() { if(StateHistoryExists ()) if(NOT_NULL(StateHistory()->defaultState())) qDebug() << "-H-" << StateHistory()->defaultState()->objectName(); } }; //------------------------------------------------------------------------------ FW_END_NAMESPACE #endif // FWSTATEMACHINE_H 


FWStateMachine.cpp

 #include "FWStateMachine.h" #include "FWTransition.h" #include <QDebug> FW_USING_NAMESPACE //------------------------------------------------------------------------------ FWStateMachine::FWStateMachine(QObject* aParent) : BaseClass (aParent) , m_state_group (Q_NULLPTR) , m_state_history (Q_NULLPTR) , m_state_final (Q_NULLPTR) , m_state_error (Q_NULLPTR) { Initialisation (); } //------------------------------------------------------------------------------ FWStateMachine::FWStateMachine(QState::ChildMode aChildMode, QObject* aParent) : FWStateMachine(aParent) { BaseClass::setChildMode (aChildMode); } //------------------------------------------------------------------------------ QSet<FWState*> FWStateMachine::Configuration() const { QSet<FWState*> _retval; QSet<QAbstractState*> _as = BaseClass::configuration (); Q_FOREACH(QAbstractState* _s, _as) _retval.operator += (qobject_cast<FWState*>(_s)); return _retval; } bool FWStateMachine::AddBranch(QEvent::Type aEventType, FWIdentificator aID, QState* aSource, QAbstractState* aTarget) { if(NOT_NULL(aSource)) if(aSource->inherits(FW_CLASS_STR(FWState).toLatin1 ())) return qobject_cast<FWState*>(aSource)->addTransition(aEventType, aID, aTarget); return false; } bool FWStateMachine::AddBranch(FWEvent::FWEventType aEventType, FWIdentificator aID, QState* aSource, QAbstractState* aTarget) { if(NOT_NULL(aSource)) { if(aSource->inherits(FW_CLASS_STR(FWState).toLatin1 ())) return qobject_cast<FWState*>(aSource)->addTransition(aEventType, aID, aTarget); else { QEventTransition* t = new QEventTransition( (QObject*)aID, static_cast<QEvent::Type>(aEventType), aSource); if(NOT_NULL(aTarget)) t->setTargetState(aTarget); return true; } } return false; } bool FWStateMachine::AddBranch(FWEvent* aEvent, QState* aSource, QAbstractState* aTarget) { if(NOT_NULL(aSource)) { if(aSource->inherits(FW_CLASS_STR(FWState).toLatin1 ())) return qobject_cast<FWState*>(aSource)->addTransition(aEvent, aTarget); else { QEventTransition* t = new QEventTransition( (QObject*)(aEvent->Identificator()), aEvent->type(), aSource); if(NOT_NULL(aTarget)) t->setTargetState(aTarget); return true; } } return false; } //------------------------------------------------------------------------------ void FWStateMachine::Initialisation() { bool v_active = IsActive (); if(v_active) stop (); /// @name     /// @{ if(!StateGroupExists ()) m_state_group.reset (new FWState(this)); FWState* sIdle = new FWState(StateGroup ()); StateGroup ()->setInitialState (sIdle); /// @} /// @name       /// @{ if(!StateHistoryExists ()) m_state_history.reset (new QHistoryState(QHistoryState::DeepHistory,StateGroup ())); ///    ,   ,    ///   - @sa m_state_group,    ///    ,    . StateHistory ()->setHistoryType (QHistoryState::DeepHistory); /// @} /// @name  z    /// @{ if(!StateFinalExists ()) m_state_final.reset (new QFinalState(this)); StateFinal()->setObjectName("State FINAL"); /// @} /// @name     /// @{ if(!StateErrorExists ()) m_state_error.reset (new FWState(this)); /// @} /// @name       /// @{ AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Stop, StateGroup (), StateFinal ()); AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Error, StateGroup (), StateError ()); /// @} /// @name       /// @{ FWState* sPause = new FWState(this); AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Pause, StateGroup (), sPause); AddBranch(FWEvent::ET_INTERNAL,FWInternalEvent::OEI_Resume, sPause, StateHistory ()); /// @} /// @name      /// @{ AddBranch(StateError (), StateFinal ()); /// @} /// @name       /// @{ connect( sPause,&FWState:: entered, this, &FWStateMachine:: slot_OnPaused, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( sPause,&FWState:: exited, this, &FWStateMachine:: slot_OnResume, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( StateFinal (),&QFinalState:: entered, this, &FWStateMachine:: slot_OnStop, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( StateError (),&FWErrorState:: entered, this, &FWStateMachine:: slot_OnError, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); connect( StateHistory(),&QHistoryState::defaultStateChanged, this, &FWStateMachine::slot_OnDefaultHistoryChanged, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); /// @} if(NOT_EQUAL(initialState (),StateGroup ())) setInitialState (StateGroup ()); StateGroup ()-> setObjectName("State GROUP"); StateHistory ()-> setObjectName("State HISTORY"); StateError()-> setObjectName("State ERROR"); StateFinal()-> setObjectName("State FINAL"); sPause-> setObjectName("State PAUSE"); sIdle-> setObjectName("State IDLE"); if(v_active) start (); } //------------------------------------------------------------------------------ 


FWTransition.h

 #ifndef FW_TRANSITION_H #define FW_TRANSITION_H #include <FWEvent> #include <QSignalTransition> FW_BEGIN_NAMESPACE //------------------------------------------------------------------------------ class FWState; class FWStateMachine; //------------------------------------------------------------------------------ /// /// @brief  FWTransition      @sa FWState ///    @sa FWStateMachine. ///    @sa QEventTransition. /// class #ifdef FW_LIBRARY FW_SHARED_EXPORT #else Q_DECL_EXPORT #endif FWTransition : public QAbstractTransition { Q_OBJECT Q_CLASSINFO("brief", "Exlusive Finite-state Class") Q_CLASSINFO("created", "03-JUN-2015") Q_CLASSINFO("modified", "24-JUN-2015") // Q_CLASSINFO("project", "Common Qt-based Framework") Q_PROPERTY(FWIdentificator eventID READ EventID WRITE SetEventID NOTIFY sign_EventIDChanged) Q_PROPERTY(QEvent::Type eventType READ EventType WRITE SetEventType NOTIFY sign_EventTypeChanged) //---------------------------------------------------------------------------- /// @name   . /// @{ ///        /// @see QEventTransition typedef QAbstractTransition BaseClass; /// @} Q_ENUMS(TransitionState) //---------------------------------------------------------------------------- public: /** * @brief  TransitionState    . */ enum TransitionState { UndefinedState, ///<    SignalState, ///<     EventState, ///<     ErrorState ///<    }; //---------------------------------------------------------------------------- private: /** * @brief  m_event     *  . * * @see FWIdentificator, QEvent, QEvent::Type. */ QEvent::Type m_event_type; /** * @brief  m_event_id c    *  @sa FWEvent.      @sa FWSignalEvent, *       ,   *  @sa FWIdentificator  FWIdentificator(<QObject*>). * * @see FWTransition::Object, FWTransition::ObjectExists. */ FWIdentificator m_event_id; /** * @brief  m_state    * @see TransitionState */ TransitionState m_state; //---------------------------------------------------------------------------- public: /** * @brief  FWTransition    * @sa QAbstractTransition::QAbstractTransition. * @param aSourceState      . */ explicit FWTransition(FWState* aSourceState = Q_NULLPTR); /** * @brief  FWTransition     *    @sa FWEvent::FWEvent * @param aEvent   ,  ; * @param aSourceState      . * * "see FWIdentificator, FWEvent, FWState. */ explicit FWTransition(QEvent* aEvent, FWState* aSourceState = Q_NULLPTR); /** * @brief  FWTransition     *    @sa QEvent::Type. * @param aEventType  ,  ; * @param aID  ,  ; * @param aSourceState      . * * @see QEvent, FWEvent, FWState, QState. */ explicit FWTransition(QEvent::Type aEventType, FWIdentificator aID, FWState* aSourceState = Q_NULLPTR); /** * @brief  FWTransition     *    @sa FWEvent::FWEventType. * @param aEventType  ,  ; * @param aID  ,  ; * @param aSourceState      . * * @see QEvent, FWEvent, FWState, QState. */ explicit FWTransition(FWEvent::FWEventType aEventType, FWIdentificator aID, FWState* aSourceState = Q_NULLPTR) : FWTransition(static_cast<QEvent::Type>(aEventType), aID, aSourceState) {} /** * @brief  FWTransition     *   (   )  * @sa FWEvent::ET_INTERNAL. * @param aID  ,  ; * @param aSourceState      . * * @see QEvent, FWEvent, FWState, QState. */ explicit FWTransition(FWInternalEvent::FWInternalIdentificators aID, FWState* aSourceState = Q_NULLPTR) : FWTransition(static_cast<QEvent::Type>(FWEvent::ET_INTERNAL),aID, aSourceState) {} // explicit // FWTransition(QObject* aSender, // const char* aSignalName, // QObject* aRessiver, // const char* aSlotName, // FWState* aSourceState = Q_NULLPTR); /** * @brief  AsBaseClass       . * @return         . */ inline BaseClass* AsBaseClass () { return qobject_cast<BaseClass*>(this); } /** * @brief  EventType     @sa QEvent::Type. * @return   . */ inline QEvent::Type EventType () const { return m_event_type; } /** * @brief  SetEventType     . * @param aType    . */ inline void SetEventType (const QEvent::Type aType) { if(NOT_EQUAL(EventType (),aType)) { m_event_type = aType; emit sign_EventTypeChanged (EventType ()); } } /** * @brief  Identificator   ,  *   . * @return    @sa FWIdentificator. */ inline FWIdentificator EventID () const { return m_event_id; } /** * @brief  setEventID    @sa m_id  *  . * @param aID       . */ inline void SetEventID (const FWIdentificator aID) { if(NOT_EQUAL(EventID (),aID)) { m_event_id = aID; emit sign_EventIDChanged (EventID ()); } } FWStateMachine* Machine () const; inline bool operator == (const FWTransition& aOther) { return EQUAL(EventType (), aOther.EventType ()) && EQUAL(EventID (), aOther.EventID ()) && EQUAL(sourceState (), aOther.sourceState()) && EQUAL(targetState (), aOther.targetState()); } //---------------------------------------------------------------------------- protected: /** * @brief  SetState      . * @param aState        * @sa TransitionState */ virtual void SetState (TransitionState aState) { if(m_state != aState) m_state = aState; } /** * @brief  FWTransition::eventTest    * @sa QEventTransition::eventTest     *     . * @param aEvent    . * @return   : * @value TRUE  ,  ; * @value FALSE  ,   . * *     - . ,    *   ,      @sa FWEvent. *  :  ,   "" * @sa StateMachine::WrappedEvent  @sa StateMachine::SignalEvent; * * @see QEvent, QEvent::Type. * @see QStateMachine, QStateMachine::WrappedEvent QStateMachine::SignalEvent. * @see FWEvent, FWEvent::Machine */ bool eventTest(QEvent* aEvent) Q_DECL_OVERRIDE; /** * @brief  onTransition * @param aEvent */ void onTransition(QEvent* aEvent) Q_DECL_OVERRIDE; //---------------------------------------------------------------------------- Q_SIGNALS: /** * @brief sign_EventTypeChanged * @param aType */ void sign_EventTypeChanged (const QEvent::Type aType); /** * @brief  sign_eventIDChanged     *     @sa m_id. * @param aID    @sa m_id. * * @see FWIdentificator */ void sign_EventIDChanged (const FWIdentificator aID); /// /// @brief  sign_Transiting     /// @param aSource   -,    /// ; /// @param aTarget    - ; /// @param aTransition     @sa FWTransition,  ///   . /// void sign_Transiting (QState* aSource, QAbstractState* aTarget, QAbstractTransition* aTransition); }; //------------------------------------------------------------------------------ FW_END_NAMESPACE #endif // FWTRANSITION_H 


FWTransition.cpp

 #include "FWTransition.h" #include "FWState.h" #include "FWStateMachine.h" FW_USING_NAMESPACE //------------------------------------------------------------------------------ FWTransition::FWTransition(FWState* aSourceState) : BaseClass (aSourceState) { SetState(UndefinedState); SetEventID (EMPTY_ID); SetEventType (static_cast<QEvent::Type>(QEvent::None)); } //------------------------------------------------------------------------------ FWTransition::FWTransition(QEvent* aEvent, FWState* aSourceState) : FWTransition(aSourceState) { if(NOT_NULL(aEvent)) { if(!FWEvent::IsSystem (aEvent)) { SetState(EventState); SetEventID (static_cast<FWEvent*>(aEvent)->Identificator ()); SetEventType (aEvent->type ()); } } else SetState(ErrorState); } //------------------------------------------------------------------------------ FWTransition::FWTransition(QEvent::Type aEventType, FWIdentificator aID, FWState* aSourceState) : FWTransition(aSourceState) { SetState (EventState); SetEventID (aID); SetEventType(aEventType); } //------------------------------------------------------------------------------ //FWTransition::FWTransition(QObject* aSender, // const char* aSignalName, // QObject* aRessiver, // const char* aSlotName, // FWState* aSourceState) // : FWTransition (aSourceState) //{ // SetState(SignalState); // SetEventID (FWIdentificator (aSender)); // SetEventType (static_cast<QEvent::Type>(FWEvent::ET_GLOBAL)); // if(NOT_NULL(aSender) && NOT_NULL(aRessiver)) // { // int v_offset = *aSignalName == '0'+QSIGNAL_CODE ? 1 : 0; // int v_signal_index = // aSender->metaObject ()->indexOfSignal ( // QMetaObject::normalizedSignature (aSignalName+v_offset)); // v_offset = *aSlotName == '0'+QSLOT_CODE ? 1 : 0; // int v_slot_idx = // aRessiver->metaObject ()->indexOfSlot ( // QMetaObject::normalizedSignature (aSlotName+v_offset)); // if(QMetaObject::connect (aSender, v_signal_index, // aRessiver, v_slot_idx, // Qt::DirectConnection)) // { // aRessiver->setParent (Q_NULLPTR); // aRessiver->moveToThread (thread ()); // } // else // SetState(ErrorState); // } //} //------------------------------------------------------------------------------ FWStateMachine* FWTransition::Machine() const { return qobject_cast<FWStateMachine*>(machine ()); } //------------------------------------------------------------------------------ bool FWTransition::eventTest(QEvent* aEvent) { bool v_retval = FWEvent::IsValid (aEvent); if(v_retval) v_retval = EQUAL(aEvent->type (),EventType ()); if(v_retval) { FWEvent* v_e = static_cast<FWEvent*>(aEvent); v_retval = EQUAL(v_e->type (), EventType ()) && EQUAL(v_e->Identificator (),EventID ()); } return v_retval; } //------------------------------------------------------------------------------ void FWTransition::onTransition(QEvent* aEvent) { aEvent->accept(); // qDebug() << "-T- " + // sourceState ()->objectName() + // " -->> " + // targetState()->objectName(); emit sign_Transiting (sourceState (),targetState (), this->AsBaseClass()); } //------------------------------------------------------------------------------ 



, … - .

Thank.

Source: https://habr.com/ru/post/261955/


All Articles