野外求生夫妻档爱奇艺:Visual Modeling of Complex Reactive Systems with Harel UML StateCharts

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 23:01:59

Introduction

A reactive system is characterized by being, to a large extent,event-driven, continuously having to react to external and internalstimuli. Examples include telephones, automobiles, communicationnetworks, computer Operating Systems, and missiles. The problem isrooted in the difficulty of describing reactive behavior in ways thatare clear and realistic, and at the same time formal and rigorous,sufficiently so to be amenable to detailed computerized simulation[David Harel].

Harel StateCharts are gaining widespread usage since a variant hasbecome part of the Unified Modeling Language. The diagram type allowsthe modeling of superstates, concurrent states, and activities as partof a state.

Problems with Conventional FSM

Classic Mealy-Moore state machine modeling techniques require thecreation of distinct nodes for every valid combination of parametersthat define the state. This can lead to a very large number of nodes andtransitions between nodes for all but the simplest of systems. Thiscomplexity reduces the readability of the state diagram.

Another of the limitations of modeling a computer system as aconventional state machine is the lack of support for concurrentconstructs. Traditional state machine modeling is based on sequentialtransitions from one state to the next. Concurrent systems cannot bemodeled in this manner as various aspects of the system may be indifferent states.

It is seen that the limitations inherent in state machine modelsinclude the inherent complexity which occurs as the number of statesincreases, and also the modeling of concurrent systems. This articlepresents a commercial-grade cross-platform Harel UML StateChartOpen-Source application framework named StateWizard for concurrent,distributed, and real-time reactive system development with simplicity,efficiency, and scalability. The following sections describe the ways tosolve complex, reactive system problems with the following HarelStateChart features:

  • Hierarchical state machines
  • Supports large scale state machines with hundreds of states through separating state tree definitions to several C/C++ files
  • State history information and history transitions
  • Guarded transitions on event handling
  • Conditional pseudo-states
  • Join pseudo-states
  • Orthogonal states
  • Built-in state timers

How to Reduce the Size of the Representation?

The solution is hierarchical state machines. In conventional statemachine design, all states are considered at the same level. The designdoes not capture the commonality that exists among states. In real life,many states handle most messages in a similar fashion and differ onlyin the handling of a few key messages. Even when the actual handlingdiffers, there is still some commonality. Hierarchical state machinedesign captures the commonality by organizing the states as a hierarchy.The states at the higher level in the hierarchy perform the commonmessage handling, while the lower level states inherit the commonalityfrom the higher level ones and perform the state specific functions.

The state machine state hierarchy is based on a parent-childrelationship, which is depicted by the arrangement of the tree branches.For example, in the following figure: State Hierarchy in Tree Form, thenode Player in the state tree is a root state which has two children: PowerDown and PowerUp. Meanwhile, the node PowerUp is also a parent of three children, and these children are referred as sibling states among them.

This hierarchical organization means that when a transition from state PowerDown to PowerUp is triggered, the State Machine de-activates the state PowerDown and its children (if any) and activates PowerUp and one or more of its children (if any).

Player

  • PowerDown (Init)
  • PowerUp
    • Playing (Init)
    • Pause
    • Record
Figure: State Hierarchy in Tree Form

The same hierarchy may be represented in a nested chart form. In thisformat, the hierarchy among states are shown by nesting child stateswithin their parent state.

Figure: State Hierarchy in Chart Form

Using the StateWizard application framework API set, the Player state machine is defined as below:

Collapse
/* The definition of the Player composite root state. */#define SME_CURR_DEFAULT_PARENT PlayerSME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)SME_ON_INIT_STATE(SME_NULL_ACTION, PowerDown)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(PowerDown, PowerDownEntry, PowerDownExit)SME_ON_EVENT(EXT_EVENT_ID_POWER, OnPowerDownEXT_EVENT_ID_POWER, PowerUp)SME_END_STATE_DEFSME_BEGIN_SUB_STATE_DEF_P(PowerUp)SME_ON_EVENT(EXT_EVENT_ID_POWER,OnPowerUpEXT_EVENT_ID_POWER,PowerDown)SME_END_STATE_DEFSME_END_COMP_STATE_DEF(Player)
Collapse
#define SME_CURR_DEFAULT_PARENT PowerUpSME_BEGIN_COMP_STATE_DEF(PowerUp, Player, PowerUpEntry, PowerUpExit)SME_ON_INIT_STATE(OnPowerUpInitChild, Playing)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Playing, PlayingEntry, PlayingExit)SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPlayingEXT_EVENT_ID_PAUSE_RESUME,Pause)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Pause, PauseEntry, PauseExit)SME_ON_EVENT(EXT_EVENT_ID_START_RECORD,OnPauseEXT_EVENT_ID_START_RECORD,Record)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Record,RecordEntry,RecordExit)SME_ON_EVENT(EXT_EVENT_ID_STOP_RECORD,OnRecordEXT_EVENT_ID_STOP_RECORD,Pause)SME_END_STATE_DEFSME_END_COMP_STATE_DEF(PowerUp)

Root State is the uppermost state, bearing the application name. As you add states, the state tree grows downwards from the root state.

For example, the Player composite state is the root state of the Player state machine. It is defined as below. The PlayerEntry and PlayerExit are the function pointers to the actions on the root state entry and exit, respectively.

Collapse
SME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)

Parent State is a state that branches into one or more child states. A parent can have several children, but a child has only one parent.

For example, the PowerUp composite state's parent state is the state Player. Define the PowerUp state as below:

Collapse
SME_BEGIN_COMP_STATE_DEF(PowerUp, Player, PowerUpEntry, PowerUpExit)

Initial Child State identifies the initial state forstate machines that have substates. This child must occur if and onlyif the machine has one or more states or parallel children.

For example, an initial child state, Playing, of the PowerUp composite state is defined as below. The OnPowerUpInitChild is a function pointer to the initial child state entry action.

Collapse
SME_BEGIN_COMP_STATE_DEF(PowerUp, Player, PowerUpEntry, PowerUpExit)SME_ON_INIT_STATE(OnPowerUpInitChild, Playing)SME_END_STATE_DEF

Sibling States are the child states with a common parent.

How to Scale State Machines?

In complex large-scale reactive systems, a state machine object couldbe composed of more than 100 states, it is not a good way to place allstate definitions in a source file.

The definitions of different composite states could be spread in source files.

For example, the Player composite state could be defined in Player.c. There is a sub-state named PowerUp which is a composite state too. The SME_BEGIN_SUB_STATE_DEF_P(PowerUp) block is declared in the Player composite state definition body. The common attributes and behaviours of the PowerUp composite state are defined in the SME_BEGIN_SUB_STATE_DEF_P(PowerUp) block, for example state transition from PowerUp to PowerDown, no matter what the current active state is (PowerUp, Pause) or (PowerUp, Playing).

Collapse
// File: Player.c#define SME_CURR_DEFAULT_PARENT PlayerSME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)SME_ON_INIT_STATE(SME_NULL_ACTION, PowerDown)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(PowerDown, PowerDownEntry, PowerDownExit)SME_ON_EVENT_WITH_GUARD(EXT_EVENT_ID_POWER, Guard1_func,OnPowerDownEXT_EVENT_ID_POWER, Join1)SME_END_STATE_DEFSME_BEGIN_SUB_STATE_DEF_P(PowerUp)SME_ON_EVENT(EXT_EVENT_ID_POWER,OnPowerUpEXT_EVENT_ID_POWER,PowerDown)SME_END_STATE_DEFSME_END_COMP_STATE_DEF(Player)

And, the detail PowerUp composite state with its children could be defined in Player2.c.

Collapse
// File: Player2.c#define SME_CURR_DEFAULT_PARENT PowerUpSME_BEGIN_COMP_STATE_DEF(PowerUp, Player, PowerUpEntry, PowerUpExit)SME_ON_INIT_STATE(OnPowerUpInitChild, Playing)SME_ON_STATE_TIMEOUT_INTERNAL_TRAN(3000, PowerUpTimeOut)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Playing, PlayingEntry, PlayingExit)SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPlayingEXT_EVENT_ID_PAUSE_RESUME,Pause)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Pause, PauseEntry, PauseExit)SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPauseEXT_EVENT_ID_PAUSE_RESUME,Playing)SME_END_STATE_DEFSME_END_COMP_STATE_DEF(PowerUp)

How to Activate a State Machine Instance?

A state machine application is an instance of a state machine, or aninstance of a region which is an orthogonal part of either a compositestate or a state machine. Applications can have one of two modes: activeor inactive. Active applications are the ones running on the statemachine at a given time, whereas inactive applications are not. In otherwords, only active applications can handle events. A state machineengine is responsible for managing these applications and dispatchingevents to specific applications.

The following macro defines an application instance, Player1, based on the Player state machine. SmeActivateObj() activates the Player1 instance. The second parameter is the parent application of the application to be activated, with NULL standing for no parent application.

Collapse
SME_OBJ_DEF(Player1, Player)SmeActivateObj(&Player1,NULL);

How to Conveniently Describe State Concurrency?

The solution is to allow Orthogonal states to operate concurrently.

Composite states have one or more regions for substates. A region is simply a container for substates.

A composite state is a state that consists ofsub-states. A composite state can be decomposed using AND-relationshipsinto two or more concurrent regions which are containers for sub-states,or using OR-relationships into mutually exclusive disjoint sub-states.

Orthogonal state: If a composite state can be decomposed using AND-relationships into two or more concurrent regions, it is called Orthogonal State.

The following macros defines an Orthogonal state named OrthoState as a child of the Player parent state. The state entry/exit actions are OrthoStateEntry and OrthoStateExit, respectively. The orthogonal state is composed of three regions:

  • PlayerReg1 as an instance of the Player state machine, running at the same thread as its parent with 0 priority.
  • PlayerReg2 as an instance of the Player state machine, running at a separate thread with 0 priority.
  • PlayerReg3 as an instance of the Player state machine, running at a separate thread with 0 priority.
Collapse
SME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)SME_ON_INIT_STATE(SME_NULL_ACTION, PowerDown)SME_END_STATE_DEFSME_BEGIN_ORTHO_SUB_STATE_DEF_P(OrthoState)SME_ON_EVENT(EXT_EVENT_ID_POWER, OnPowerDownEXT_EVENT_ID_POWER, Join1)SME_END_STATE_DEFSME_BEGIN_ORTHO_COMP_STATE_DEF(OrthoState, Player, OrthoStateEntry, OrthoStateExit)SME_REGION_DEF(PlayerReg1,Player,SME_RUN_MODE_PARENT_THREAD,0)SME_REGION_DEF(PlayerReg2,Player,SME_RUN_MODE_SEPARATE_THREAD,0)SME_REGION_DEF(PlayerReg3,Player,SME_RUN_MODE_SEPARATE_THREAD,0)SME_END_ORTHO_STATE_DEF

With the StateWizard, you may define a transition from/to an Orthogonal state, using SME_BEGIN_ORTHO_SUB_STATE_DEF().However, you cannot define an explicit entry to one of the child statesof a region. And, all regions work in the form of applications whichrun concurrently. On an Orthogonal state entry, all regions willactivate automatically. And on state exit, all regions will de-activateautomatically. If several regions run at their separate threads, theOrthogonal state will post SME_EVENT_EXIT_LOOP events to these regions and wait for these threads to exit.

How to Model State Built-in Timer for a Real-time System?

Timers need to be modeled in real-time systems. The engine supportstwo kinds of timers, state-built-in timers and regular timers.

State-built-in timers work tightly with the state machine. They aremanaged by the engine. On a state entry, automatically start thebuilt-in timer, if it is available. On a state exit, stop it. Ontimeout, they trigger SME_EVENT_STATE_TIMER events. The engine will take actions based on the state machine definition using SME_ON_STATE_TIMEOUT.

Regular timers are explicit timers. Developers have to start or stop them using API calls. On timeout, they trigger SME_EVENT_TIMER events. The engine provides two kinds of handling modes, callback function call or event handling, which are defined as below:

Collapse
enum {SME_TIMER_TYPE_CALLBACK, SME_TIMER_TYPE_EVENT};

An explicit callback function should be defined for a callbackfunction mode timer. This mode is independent from state machinedefinitions. For a timer event mode, SME_ON_EVENT(SME_EVENT_TIMER, handler, new state) should be defined to handle a timeout event.

The following sample defines a 3000-ms timer in the Player state and a 9000-ms timer in the Playing state. On timeout, a PowerUpTimeOut action will be invoked. On the Playing state entry, start it. On timeout, transit to the Pause state automatically with the PlayingTimeOut action execution.

Collapse
#define SME_CURR_DEFAULT_PARENT PowerUpSME_BEGIN_COMP_STATE_DEF(PowerUp, Player, PowerUpEntry, PowerUpExit)SME_ON_INIT_STATE(OnPowerUpInitChild, Playing)SME_ON_STATE_TIMEOUT_INTERNAL_TRAN(3000, PowerUpTimeOut)SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Playing, PlayingEntry, PlayingExit)SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPlayingEXT_EVENT_ID_PAUSE_RESUME,Pause)SME_ON_INTERNAL_TRAN_WITH_GUARD(SME_EVENT_TIMER,GuardTimer2_func,OnTimer2Proc) /* Regular timer event */SME_ON_STATE_TIMEOUT(9000, PlayingTimeOut, Pause)/* Go to Pause state if 9 sec is timeout. */SME_END_STATE_DEFSME_BEGIN_LEAF_STATE_DEF_P(Pause, PauseEntry, PauseExit)SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPauseEXT_EVENT_ID_PAUSE_RESUME,Playing)SME_END_STATE_DEFSME_END_COMP_STATE_DEF(PowerUp)

How to Model Pseudo States?

A PseudoState is an abstraction of different typesof nodes in the state machine graph which represent transient points intransition paths from one state to another (e.g., branch and forkpoints). Pseudo states are used to construct complex transitions fromsimple transitions. For example, by combining a transition entering afork pseudo state with a set of transitions exiting the fork pseudostate, we get a complex transition that leads to a set of target states.

Conditional (Fork) PseudoStates are a notational shorthand for multiple exiting transitions all triggered by the same event but each having different guards.

Join PseudoState is a state with several incoming transitions and a single outgoing one.

Using the StateWizard application framework API set, a Conditional PseudoState Cond1 and a Join PseudoState Join1 are defined as below. The Cond1 pseudo state has three forks: COND1, COND2, and COND_ELSE. The exit destination state is dependent on the return value of the function Cond1_func. The action on entry to the Join1 pseudo state is the JoinAct function.

Collapse
#define SME_CURR_DEFAULT_PARENT PlayerSME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)SME_ON_INIT_STATE(SME_NULL_ACTION, PowerDown)SME_END_STATE_DEF....SME_BEGIN_COND_STATE_DEF_P(Cond1, Cond1_func)SME_ON_EVENT(COND_EV_COND1, CondAct1, Playing)SME_ON_EVENT(COND_EV_COND2, CondAct2, Pause)SME_ON_EVENT(SME_EVENT_COND_ELSE, CondActElse, PowerUp)SME_END_STATE_DEFSME_BEGIN_JOIN_STATE_DEF_P(Join1)SME_ON_JOIN_TRAN(JoinAct, Cond1)SME_END_STATE_DEF...

StateWizard Event Handling Workflow

Event Handling Loop

The SmeRun() function is the state machine engine eventhandling loop function. If there is no event in the queue, this functionwaits for external event. If any event is triggered, it will dispatchit to the appropriate applications, and keep state machines takingappropriate transitions and actions.

If an external event is triggered, it will be transformed to aninternal event via an external event waiting function which is an OSvirtual layer function.

If an external event destroy function is hooked, SmeRun() will call this function to destroy external event when an external event is consumed.

Collapse
void SmeRun(void){SME_EVENT_T ExtEvent;SME_EVENT_T *pEvent=NULL;SME_OBJ_T *pObj;SME_THREAD_CONTEXT_PT pThreadContext=NULL;if (g_pfnGetThreadContext)pThreadContext = (*g_pfnGetThreadContext)();if (!pThreadContext) return;if (!g_pfnGetExtEvent) return;pObj = pThreadContext->pActObjHdr;while (TRUE){/* Check the internal event pool firstly. */pEvent = GetEventFromQueue();if (pEvent == NULL){/* Wait for an external event. */if (FALSE == (*g_pfnGetExtEvent)(&ExtEvent))return; // Exit the thread.pEvent = &ExtEvent;pEvent->nOrigin = SME_EVENT_ORIGIN_EXTERNAL;/* Call hook function on an external event coming. */if (pThreadContext->fnOnEventComeHook)(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_EXTERNAL, pEvent);}else{/* Call hook function on an internal event coming. */if (pThreadContext->fnOnEventComeHook)(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_INTERNAL, pEvent);}do {DispatchEventToApps(pThreadContext, pEvent);/* Free internal event. Free external event later. */if (pEvent != &ExtEvent)SmeDeleteEvent(pEvent);/* Get an event from event queue if available. */pEvent = GetEventFromQueue();if (pEvent != NULL){/* Call hook function on aninternal event coming. */if (pThreadContext->fnOnEventComeHook)(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_INTERNAL, pEvent);}else{/* The internal event queue is empty. */break;}} while (TRUE); /* Get all events from the internal event pool. *//* Free external event if necessary. */if (g_pfnDelExtEvent){(*g_pfnDelExtEvent)(&ExtEvent);// Engine should delete this event, // because translation of external event // will create an internal event.SmeDeleteEvent(&ExtEvent);}} /* Wait for an external event. */}

The SmeSetExtEventOprProc() function installs platformrelated external event operating functions. They work as the StateWizardOS Virtual Layer. The event handling loop function, SmeRun() calls the fnGetExtEvent function to get an external event and free it after handling it via fnDelExtEvent

Collapse
void SmeSetExtEventOprProc(SME_GET_EXT_EVENT_PROC_T fnGetExtEvent,SME_DEL_EXT_EVENT_PROC_T fnDelExtEvent,SME_POST_THREAD_EXT_INT_EVENT_PROC_T fnPostThreadExtIntEvent,SME_POST_THREAD_EXT_PTR_EVENT_PROC_T fnPostThreadExtPtrEvent,SME_INIT_THREAD_EXT_MSG_BUF_PROC_T fnInitThreadExtMsgBuf,SME_INIT_THREAD_EXT_MSG_BUF_PROC_T fnFreeThreadExtMsgBuf){g_pfnGetExtEvent = fnGetExtEvent;g_pfnDelExtEvent = fnDelExtEvent;g_pfnPostThreadExtIntEvent = fnPostThreadExtIntEvent;g_pfnPostThreadExtPtrEvent = fnPostThreadExtPtrEvent;g_pfnInitThreadExtMsgBuf = fnInitThreadExtMsgBuf;g_pfnFreeThreadExtMsgBuf = fnFreeThreadExtMsgBuf;}

External Event Management as OS Virtual Layer

The SmeSetExtEventOprProc() function installs platformrelated external event operating functions. They work as the StateWizardOS Virtual Layer. The event handling loop function, SmeRun() calls the fnGetExtEvent function to get an external event and free it after handling it via fnDelExtEvent.

Collapse
BOOL XGetExtEvent(SME_EVENT_T* pEvent){X_EXT_MSG_T NativeMsg;int ret=0;SME_THREAD_CONTEXT_T* p = XGetThreadContext();X_EXT_MSG_POOL_T *pMsgPool;if (NULL==pEvent || NULL==p || NULL==p->pExtEventPool)return FALSE;pMsgPool = (X_EXT_MSG_POOL_T*)(p->pExtEventPool);memset(&NativeMsg,0,sizeof(NativeMsg));while (TRUE){ret = XWaitForEvent(&(pMsgPool->EventToThread),&(pMsgPool->MutexForPool),(XIS_CODITION_OK_T)XIsMsgAvailable, NULL,(XTHREAD_SAFE_ACTION_T)XGetMsgFromBuf,&NativeMsg);if (NativeMsg.nMsgID == SME_EVENT_EXIT_LOOP){return FALSE; //Request Exit}#ifdef SME_WIN32#else// Built-in call back timer on Linuxelse if (SME_EVENT_TIMER == NativeMsg.nMsgID&& SME_TIMER_TYPE_CALLBACK == NativeMsg.Data.Int.nParam1){// Invoke the call back function.SME_TIMER_PROC_T pfnCallback =(SME_TIMER_PROC_T)(NativeMsg.Data.Int.nParam2);(*pfnCallback)(NativeMsg.pDestObj, NativeMsg.nSequenceNum);}#endifelse {// Translate the native message to SME event.memset(pEvent,0,sizeof(SME_EVENT_T));pEvent->nEventID = NativeMsg.nMsgID;pEvent->pDestObj = NativeMsg.pDestObj;pEvent->nSequenceNum = NativeMsg.nSequenceNum;pEvent->nDataFormat = NativeMsg.nDataFormat;pEvent->nCategory = NativeMsg.nCategory;pEvent->bIsConsumed = FALSE;memcpy(&(pEvent->Data),&(NativeMsg.Data),sizeof(union SME_EVENT_DATA_T));}//printf("External message received. \n");return TRUE;}; // while (TRUE)}BOOL XDelExtEvent(SME_EVENT_T *pEvent){if (0==pEvent)return FALSE;if (pEvent->nDataFormat == SME_EVENT_DATA_FORMAT_PTR){if (pEvent->Data.Ptr.pData){#if SME_CPPdelete pEvent->Data.Ptr.pData;#elsefree(pEvent->Data.Ptr.pData);#endifpEvent->Data.Ptr.pData=NULL;}}return TRUE;}

Application and Sample

Assume there are two state machine instances Player1 and Player2 which run at the two threads whose thread contexts are AppThreadContext1 and AppThreadContext2.

Collapse
CPlayer Player1;CPlayer Player2;SME_THREAD_CONTEXT_T g_AppThreadContext1;SME_THREAD_CONTEXT_T g_AppThreadContext2;

There is a control panel which allows the user to post events to these two state machine instances.

Collapse
int main(int argc, char* argv[]){XTHREADHANDLE ThreadHandle = 0;int ret;printf("Cross-Platform State Timer Sample: \n");// Install thread local storage data functions.XTlsAlloc();SmeSetTlsProc(XSetThreadContext, XGetThreadContext);////////////////////////////////////////////////////////////////// Engine initialization.SmeInitEngine(&g_AppThreadContext1); // Initialize at the thread-1XInitMsgBuf();// Install event handler functions.SmeSetExtEventOprProc(XGetExtEvent, XDelExtEvent,XPostThreadExtIntEvent, XPostThreadExtPtrEvent, XInitMsgBuf, XFreeMsgBuf);// Create a thread to trigger external events.ret = XCreateThread(ConsoleProc, NULL, &ThreadHandle);// Create the Player2 application threadret = XCreateThread(AppThread2Proc, NULL, &ThreadHandle);SmeActivateObj(&Player1,NULL);SmeRun();printf("Exit from SmeRun() at thread-1 \n");XFreeMsgBuf();XFreeThreadContext(&g_AppThreadContext1); /* At last, free thread localstorage resource. */}

The second application thread function. The Player2 state machine instance runs at this thread.

Collapse
#ifdef WIN32unsigned __stdcall AppThread2Proc(void *Param)#elsevoid* AppThread2Proc(void *Param)#endif{SmeInitEngine(&g_AppThreadContext2); // Initialize at the thread-2// Save the thread context pointer to the TLS.// XSetThreadContext(&g_AppThreadContext2);XInitMsgBuf();// Install event handler functions.SmeSetExtEventOprProc(XGetExtEvent, XDelExtEvent,XPostThreadExtIntEvent, XPostThreadExtPtrEvent, XInitMsgBuf, XFreeMsgBuf);SmeActivateObj(&Player2,NULL);SmeRun();printf("Exit from SmeRun() at thread-2 \n");XFreeMsgBuf();XFreeThreadContext(&g_AppThreadContext2); /* At last, free thread localstorage resource. */return 0;}

A control panel to trigger external events.

Collapse
#ifdef WIN32unsigned __stdcall ConsoleProc(void *Param)#elsevoid* ConsoleProc(void *Param)#endif{// On Linux platform, call the XInitTimer function at the // time-out event trigger thread.// On time-out, the external event trigger thread posts // SME_EVENT_TIMER to the state machine application thread,// and then invokes the callback function // installed by the XSetTimer function.XInitTimer();printf("Enter the console procedure thread.\n");ConsoleProcUsage();while(TRUE){int nParam1 =0;if(g_bQuit) break;//OSRelated::Sleep(ISVW_LOOP_INTERVAL);nParam1 = fgetc(stdin);switch(nParam1){case EOF:return 0;//Cancel ConsoleProc in Daemoncase 'x':case 'X'://QuitXPostThreadExtIntEvent(&g_AppThreadContext1,SME_EVENT_EXIT_LOOP, 0, 0, NULL,0,SME_EVENT_CAT_OTHER);printf("Exiting thread-1 ... Please wait. \n");XPostThreadExtIntEvent(&g_AppThreadContext2,SME_EVENT_EXIT_LOOP, 0, 0, NULL,0,SME_EVENT_CAT_OTHER);printf("Exiting thread-2 ... Please wait. \n");return 0;break;. . .default:break;}}return 0;}

Further Information

You may get further information and download the UML StateChart Open Source framework and IDE tool here.

History

  • 25th May, 2009: First edition of this article
  • 23rd June, 2009: Article updated
  • 6th September, 2009: Sample and source code updated - fixed some bugs based on feedbacks from users

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)