mirror of
https://github.com/pine64/bl_iot_sdk.git
synced 2024-09-15 15:14:03 +00:00
213 lines
7.2 KiB
C
213 lines
7.2 KiB
C
|
|
/*
|
|
* Copyright (c) 2020 Bouffalolab.
|
|
*
|
|
* This file is part of
|
|
* *** Bouffalolab Software Dev Kit ***
|
|
* (see www.bouffalolab.com).
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of Bouffalo Lab nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
* Copyright (c) 2013 Andreas Misje
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include "stateMachine.h"
|
|
|
|
static void goToErrorState( struct stateMachine *stateMachine,
|
|
struct event *const event );
|
|
static struct transition *getTransition( struct stateMachine *stateMachine,
|
|
const struct state *state, struct event *const event );
|
|
|
|
void stateM_init( struct stateMachine *fsm,
|
|
const struct state *initialState, const struct state *errorState )
|
|
{
|
|
if ( !fsm )
|
|
return;
|
|
|
|
fsm->currentState = initialState;
|
|
fsm->previousState = NULL;
|
|
fsm->errorState = errorState;
|
|
}
|
|
|
|
int stateM_handleEvent( struct stateMachine *fsm,
|
|
struct event *event )
|
|
{
|
|
if ( !fsm || !event )
|
|
return stateM_errArg;
|
|
|
|
if ( !fsm->currentState )
|
|
{
|
|
goToErrorState( fsm, event );
|
|
return stateM_errorStateReached;
|
|
}
|
|
|
|
if ( !fsm->currentState->numTransitions )
|
|
return stateM_noStateChange;
|
|
|
|
const struct state *nextState = fsm->currentState;
|
|
do {
|
|
struct transition *transition = getTransition( fsm, nextState, event );
|
|
|
|
/* If there were no transitions for the given event for the current
|
|
* state, check if there are any transitions for any of the parent
|
|
* states (if any): */
|
|
if ( !transition )
|
|
{
|
|
nextState = nextState->parentState;
|
|
continue;
|
|
}
|
|
|
|
/* A transition must have a next state defined. If the user has not
|
|
* defined the next state, go to error state: */
|
|
if ( !transition->nextState )
|
|
{
|
|
goToErrorState( fsm, event );
|
|
return stateM_errorStateReached;
|
|
}
|
|
|
|
nextState = transition->nextState;
|
|
|
|
/* If the new state is a parent state, enter its entry state (if it has
|
|
* one). Step down through the whole family tree until a state without
|
|
* an entry state is found: */
|
|
while ( nextState->entryState )
|
|
nextState = nextState->entryState;
|
|
|
|
/* Run exit action only if the current state is left (only if it does
|
|
* not return to itself): */
|
|
if ( nextState != fsm->currentState && fsm->currentState->exitAction )
|
|
fsm->currentState->exitAction( fsm->currentState->data, event );
|
|
|
|
/* Run transition action (if any): */
|
|
if ( transition->action )
|
|
transition->action( fsm->currentState->data, event, nextState->
|
|
data );
|
|
|
|
/* Call the new state's entry action if it has any (only if state does
|
|
* not return to itself): */
|
|
if ( nextState != fsm->currentState && nextState->entryAction )
|
|
nextState->entryAction( nextState->data, event );
|
|
|
|
fsm->previousState = fsm->currentState;
|
|
fsm->currentState = nextState;
|
|
|
|
/* If the state returned to itself: */
|
|
if ( fsm->currentState == fsm->previousState )
|
|
return stateM_stateLoopSelf;
|
|
|
|
if ( fsm->currentState == fsm->errorState )
|
|
return stateM_errorStateReached;
|
|
|
|
/* If the new state is a final state, notify user that the state
|
|
* machine has stopped: */
|
|
if ( !fsm->currentState->numTransitions )
|
|
return stateM_finalStateReached;
|
|
|
|
return stateM_stateChanged;
|
|
} while ( nextState );
|
|
|
|
return stateM_noStateChange;
|
|
}
|
|
|
|
const struct state *stateM_currentState( struct stateMachine *fsm )
|
|
{
|
|
if ( !fsm )
|
|
return NULL;
|
|
|
|
return fsm->currentState;
|
|
}
|
|
|
|
const struct state *stateM_previousState( struct stateMachine *fsm )
|
|
{
|
|
if ( !fsm )
|
|
return NULL;
|
|
|
|
return fsm->previousState;
|
|
}
|
|
|
|
|
|
static void goToErrorState( struct stateMachine *fsm,
|
|
struct event *const event )
|
|
{
|
|
fsm->previousState = fsm->currentState;
|
|
fsm->currentState = fsm->errorState;
|
|
|
|
if ( fsm->currentState && fsm->currentState->entryAction )
|
|
fsm->currentState->entryAction( fsm->currentState->data, event );
|
|
}
|
|
|
|
static struct transition *getTransition( struct stateMachine *fsm,
|
|
const struct state *state, struct event *const event )
|
|
{
|
|
size_t i;
|
|
|
|
for ( i = 0; i < state->numTransitions; ++i )
|
|
{
|
|
struct transition *t = &state->transitions[ i ];
|
|
|
|
/* A transition for the given event has been found: */
|
|
if ( t->eventType == event->type )
|
|
{
|
|
if ( !t->guard )
|
|
return t;
|
|
/* If transition is guarded, ensure that the condition is held: */
|
|
else if ( t->guard( t->condition, event ) )
|
|
return t;
|
|
}
|
|
}
|
|
|
|
/* No transitions found for given event for given state: */
|
|
return NULL;
|
|
}
|
|
|
|
bool stateM_stopped( struct stateMachine *stateMachine )
|
|
{
|
|
if ( !stateMachine )
|
|
return true;
|
|
|
|
return stateMachine->currentState->numTransitions == 0;
|
|
}
|