Full Code of endurodave/C_StateMachine for AI

master 942462cb211b cached
25 files
97.7 KB
24.7k tokens
56 symbols
1 requests
Download .txt
Repository: endurodave/C_StateMachine
Branch: master
Commit: 942462cb211b
Files: 25
Total size: 97.7 KB

Directory structure:
gitextract_21gexqg1/

├── .github/
│   └── workflows/
│       ├── cmake_clang.yml
│       ├── cmake_ubuntu.yml
│       └── cmake_windows.yml
├── .gitignore
├── CMakeLists.txt
├── CentrifugeTest.c
├── CentrifugeTest.h
├── DataTypes.h
├── Fault.cpp
├── Fault.h
├── LICENSE
├── LockGuard.cpp
├── LockGuard.h
├── Motor.c
├── Motor.h
├── README.md
├── StateMachine.c
├── StateMachine.h
├── fb_allocator.c
├── fb_allocator.h
├── main.c
├── sm_allocator.c
├── sm_allocator.h
├── x_allocator.c
└── x_allocator.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/cmake_clang.yml
================================================
name: Clang

on:
  push:
    branches:
      - master  # Trigger on push to 'master' branch
  pull_request:
    branches:
      - master  # Trigger on pull request targeting 'master' branch

jobs:
  build:
    runs-on: ubuntu-latest  # Use Ubuntu environment for the build

    steps:
    - name: Checkout code
      uses: actions/checkout@v2  # Checkout the repository code

    - name: Configure CMake with Clang
      run: cmake -S . -B Build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++  # Configure CMake with Clang as the compiler

    - name: Build
      run: cmake --build Build  # Build the project using CMake

    - name: Run C_StateMachineApp
      run: ./Build/C_StateMachineApp  # Run the built executable


================================================
FILE: .github/workflows/cmake_ubuntu.yml
================================================
name: Ubuntu

on:
  push:
    branches:
      - master  # Trigger on push to 'master' branch
  pull_request:
    branches:
      - master  # Trigger on pull request targeting 'master' branch

jobs:
  build:
    runs-on: ubuntu-latest  # Use Ubuntu environment for the build

    steps:
    - name: Checkout code
      uses: actions/checkout@v2  # Checkout the repository code

    - name: Configure CMake
      run: cmake -S . -B Build  # Configure CMake to generate build files in 'Build' directory

    - name: Build
      run: cmake --build Build  # Build the project using CMake

    - name: Run C_StateMachineApp
      run: ./Build/C_StateMachineApp  # Run the built executable


================================================
FILE: .github/workflows/cmake_windows.yml
================================================
name: Windows

on:
  push:
    branches:
      - master  # Trigger on push to 'main' branch
  pull_request:
    branches:
      - master  # Trigger on pull request targeting 'main' branch

jobs:
  build:
    runs-on: windows-latest  # Use Windows environment for the build

    steps:
    - name: Checkout code
      uses: actions/checkout@v2  # Checkout the repository code

    - name: Set up Visual Studio
      uses: microsoft/setup-msbuild@v1.1  # Set up Visual Studio environment (MSBuild)

    - name: Configure CMake
      run: cmake -S . -B Build -G "Visual Studio 17 2022" -A Win32 # Configure CMake for Visual Studio

    - name: Build
      run: cmake --build Build --config Release  # Build the project using CMake with Release configuration

    - name: Run C_StateMachineApp
      run: .\Build\Release\C_StateMachineApp.exe  # Run the built executable (adjust path for MSBuild)


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
Build
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Typescript v1 declaration files
typings/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush
.cr/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

================================================
FILE: CMakeLists.txt
================================================
# Example CMake command line to create project build files:
#
# *** Windows ***
# cmake -G "Visual Studio 17 2022" -A Win32 -B Build -S .
#
# *** Linux ***
# cmake -G "Unix Makefiles" -B Build -S .

# Specify the minimum CMake version required
cmake_minimum_required(VERSION 3.10)

# Project name and language (C or C++)
project(C_StateMachine VERSION 1.0 LANGUAGES C CXX)

# Collect all source files in the current directory
file(GLOB SOURCES
    "${CMAKE_SOURCE_DIR}/*.cpp"
    "${CMAKE_SOURCE_DIR}/*.c"
    "${CMAKE_SOURCE_DIR}/*.h"
)

# Add an executable target
add_executable(C_StateMachineApp ${SOURCES})





================================================
FILE: CentrifugeTest.c
================================================
#include "CentrifugeTest.h"
#include "StateMachine.h"
#include <stdio.h>

// CentrifugeTest object structure
typedef struct
{
    INT speed;
    BOOL pollActive;
} CentrifugeTest;

// Define private instance of motor state machine
CentrifugeTest centrifugeTestObj;
SM_DEFINE(CentrifugeTestSM, &centrifugeTestObj)

// State enumeration order must match the order of state
// method entries in the state map
enum States
{
    ST_IDLE,
    ST_COMPLETED,
    ST_FAILED,
    ST_START_TEST,
    ST_ACCELERATION,
    ST_WAIT_FOR_ACCELERATION,
    ST_DECELERATION,
    ST_WAIT_FOR_DECELERATION,
    ST_MAX_STATES
};

// State machine state functions
STATE_DECLARE(Idle, NoEventData)
ENTRY_DECLARE(Idle, NoEventData)
STATE_DECLARE(Completed, NoEventData)
STATE_DECLARE(Failed, NoEventData)
STATE_DECLARE(StartTest, NoEventData)
GUARD_DECLARE(StartTest, NoEventData)
STATE_DECLARE(Acceleration, NoEventData)
STATE_DECLARE(WaitForAcceleration, NoEventData)
EXIT_DECLARE(WaitForAcceleration)
STATE_DECLARE(Deceleration, NoEventData)
STATE_DECLARE(WaitForDeceleration, NoEventData)
EXIT_DECLARE(WaitForDeceleration)

// State map to define state function order
BEGIN_STATE_MAP_EX(CentrifugeTest)
    STATE_MAP_ENTRY_ALL_EX(ST_Idle, 0, EN_Idle, 0)
    STATE_MAP_ENTRY_EX(ST_Completed)
    STATE_MAP_ENTRY_EX(ST_Failed)
    STATE_MAP_ENTRY_ALL_EX(ST_StartTest, GD_StartTest, 0, 0)
    STATE_MAP_ENTRY_EX(ST_Acceleration)
    STATE_MAP_ENTRY_ALL_EX(ST_WaitForAcceleration, 0, 0, EX_WaitForAcceleration)
    STATE_MAP_ENTRY_EX(ST_Deceleration)
    STATE_MAP_ENTRY_ALL_EX(ST_WaitForDeceleration, 0, 0, EX_WaitForDeceleration)
END_STATE_MAP_EX(CentrifugeTest)

EVENT_DEFINE(CFG_Start, NoEventData)
{
    BEGIN_TRANSITION_MAP                                // - Current State -
        TRANSITION_MAP_ENTRY(ST_START_TEST)             // ST_IDLE
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)             // ST_COMPLETED
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)             // ST_FAILED
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)             // ST_START_TEST
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)             // ST_ACCELERATION
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)             // ST_WAIT_FOR_ACCELERATION
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)             // ST_DECELERATION
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)             // ST_WAIT_FOR_DECELERATION
    END_TRANSITION_MAP(CentrifugeTest, pEventData)
}

EVENT_DEFINE(CFG_Cancel, NoEventData)
{
    BEGIN_TRANSITION_MAP                                // - Current State -
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)             // ST_IDLE
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)             // ST_COMPLETED
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)             // ST_FAILED
        TRANSITION_MAP_ENTRY(ST_FAILED)                 // ST_START_TEST
        TRANSITION_MAP_ENTRY(ST_FAILED)                 // ST_ACCELERATION
        TRANSITION_MAP_ENTRY(ST_FAILED)                 // ST_WAIT_FOR_ACCELERATION
        TRANSITION_MAP_ENTRY(ST_FAILED)                 // ST_DECELERATION
        TRANSITION_MAP_ENTRY(ST_FAILED)                 // ST_WAIT_FOR_DECELERATION
    END_TRANSITION_MAP(CentrifugeTest, pEventData)
}

EVENT_DEFINE(CFG_Poll, NoEventData)
{
    BEGIN_TRANSITION_MAP                                    // - Current State -
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)                 // ST_IDLE
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)                 // ST_COMPLETED
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)                 // ST_FAILED
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)                 // ST_START_TEST
        TRANSITION_MAP_ENTRY(ST_WAIT_FOR_ACCELERATION)      // ST_ACCELERATION
        TRANSITION_MAP_ENTRY(ST_WAIT_FOR_ACCELERATION)      // ST_WAIT_FOR_ACCELERATION
        TRANSITION_MAP_ENTRY(ST_WAIT_FOR_DECELERATION)      // ST_DECELERATION
        TRANSITION_MAP_ENTRY(ST_WAIT_FOR_DECELERATION)      // ST_WAIT_FOR_DECELERATION
    END_TRANSITION_MAP(CentrifugeTest, pEventData)
}

static void StartPoll(void)
{
    centrifugeTestObj.pollActive = TRUE;
}

static void StopPoll(void)
{
    centrifugeTestObj.pollActive = FALSE;
}

BOOL CFG_IsPollActive(void) 
{ 
    return centrifugeTestObj.pollActive;
}

STATE_DEFINE(Idle, NoEventData)
{
    printf("%s ST_Idle\n", self->name);
}

ENTRY_DEFINE(Idle, NoEventData)
{
    printf("%s EN_Idle\n", self->name);
    centrifugeTestObj.speed = 0;
    StopPoll();
}

STATE_DEFINE(Completed, NoEventData)
{
    printf("%s ST_Completed\n", self->name);
    SM_InternalEvent(ST_IDLE, NULL);
}

STATE_DEFINE(Failed, NoEventData)
{
    printf("%s ST_Failed\n", self->name);
    SM_InternalEvent(ST_IDLE, NULL);
}

// Start the centrifuge test state.
STATE_DEFINE(StartTest, NoEventData)
{
    printf("%s ST_StartTest\n", self->name);
    SM_InternalEvent(ST_ACCELERATION, NULL);
}

// Guard condition to determine whether StartTest state is executed.
GUARD_DEFINE(StartTest, NoEventData)
{
    printf("%s GD_StartTest\n", self->name);
    if (centrifugeTestObj.speed == 0)
        return TRUE;    // Centrifuge stopped. OK to start test.
    else
        return FALSE;   // Centrifuge spinning. Can't start test.
}

// Start accelerating the centrifuge.
STATE_DEFINE(Acceleration, NoEventData)
{
    printf("%s ST_Acceleration\n", self->name);

    // Start polling while waiting for centrifuge to ramp up to speed
    StartPoll();
}

// Wait in this state until target centrifuge speed is reached.
STATE_DEFINE(WaitForAcceleration, NoEventData)
{
    printf("%s ST_WaitForAcceleration : Speed is %d\n", self->name, centrifugeTestObj.speed);
    if (++centrifugeTestObj.speed >= 5)
        SM_InternalEvent(ST_DECELERATION, NULL);
}

// Exit action when WaitForAcceleration state exits.
EXIT_DEFINE(WaitForAcceleration)
{
    printf("%s EX_WaitForAcceleration\n", self->name);

    // Acceleration over, stop polling
    StopPoll();
}

// Start decelerating the centrifuge.
STATE_DEFINE(Deceleration, NoEventData)
{
    printf("%s ST_Deceleration\n", self->name);

    // Start polling while waiting for centrifuge to ramp down to 0
    StartPoll();
}

// Wait in this state until centrifuge speed is 0.
STATE_DEFINE(WaitForDeceleration, NoEventData)
{
    printf("%s ST_WaitForDeceleration : Speed is %d\n", self->name, centrifugeTestObj.speed);
    if (centrifugeTestObj.speed-- == 0)
        SM_InternalEvent(ST_COMPLETED, NULL);
}

// Exit action when WaitForDeceleration state exits.
EXIT_DEFINE(WaitForDeceleration)
{
    printf("%s EX_WaitForDeceleration\n", self->name);

    // Deceleration over, stop polling
    StopPoll();
}




================================================
FILE: CentrifugeTest.h
================================================
#ifndef _CENTRIFUGE_TEST_H
#define _CENTRIFUGE_TEST_H

#include "DataTypes.h"
#include "StateMachine.h"

// Declare the private instance of CentrifugeTest state machine
SM_DECLARE(CentrifugeTestSM)

// State machine event functions
EVENT_DECLARE(CFG_Start, NoEventData)
EVENT_DECLARE(CFG_Cancel, NoEventData)
EVENT_DECLARE(CFG_Poll, NoEventData)

BOOL CFG_IsPollActive();

#endif // _CENTRIFUGE_TEST_H


================================================
FILE: DataTypes.h
================================================
#ifndef _DATA_TYPES_H
#define _DATA_TYPES_H

#if WIN32
	#include "windows.h"
#else
	typedef signed char INT8;
	typedef unsigned char UINT8;
	typedef signed short INT16;
	typedef unsigned short UINT16;
	typedef unsigned int UINT32;
	typedef int INT32;
	typedef char CHAR;
	typedef short SHORT;
	typedef long LONG;
	typedef int INT;
	typedef unsigned int UINT;
	typedef unsigned long DWORD;
	typedef unsigned char BYTE;
	typedef unsigned short WORD;
	typedef float FLOAT;
	typedef double DOUBLE;
	typedef int BOOL;

	#ifndef NULL
	#ifdef __cplusplus
	#define NULL    0
	#else
	#define NULL    ((void *)0)
	#endif
	#endif

	#ifndef FALSE
	#define FALSE               0
	#endif

	#ifndef TRUE
	#define TRUE                1
	#endif
#endif

#endif


================================================
FILE: Fault.cpp
================================================
#include "Fault.h"
#include <assert.h>
#if WIN32
	#include "windows.h"
#endif

//----------------------------------------------------------------------------
// FaultHandler
//----------------------------------------------------------------------------
void FaultHandler(const char* file, unsigned short line)
{
#if WIN32
	// If you hit this line, it means one of the ASSERT macros failed.
    DebugBreak();
#endif

	assert(0);
}

================================================
FILE: Fault.h
================================================
#ifndef _FAULT_H
#define _FAULT_H

#ifdef __cplusplus
extern "C" {
#endif

#pragma warning(disable : 4005)  // Disable warning C4005

// Used for compile-time checking for array sizes. On Windows VC++, you get 
// an "error C2118: negative subscript" error. On GCC: size of "unnamed array is negative"
#ifndef C_ASSERT
//#define C_ASSERT(expr)  {char uname[(expr)?1:-1];uname[0]=0;}		// Original macro
#define C_ASSERT(expr)  ((void)sizeof(char[(expr) ? 1 : -1]))		// New macro to fix GCC warning
#endif

#define ASSERT() \
	FaultHandler(__FILE__, (unsigned short) __LINE__)

#define ASSERT_TRUE(condition) \
	do {if (!(condition)) FaultHandler(__FILE__, (unsigned short) __LINE__);} while (0)

	/// Handles all software assertions in the system.
	/// @param[in] file - the file name that the software assertion occurred on
	/// @param[in] line - the line number that the software assertion occurred on
	void FaultHandler(const char* file, unsigned short line);

#ifdef __cplusplus
}
#endif

#endif 


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 David Lafreniere

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.


================================================
FILE: LockGuard.cpp
================================================
#include "LockGuard.h"
#include "Fault.h"
#include <mutex>

// A lock is a mutex
#define LOCK std::mutex

//------------------------------------------------------------------------------
// LK_Create
//------------------------------------------------------------------------------
LOCK_HANDLE LK_Create(void)
{
    LOCK* lock = new LOCK;
    return lock;
}

//------------------------------------------------------------------------------
// LK_Destroy
//------------------------------------------------------------------------------
void LK_Destroy(LOCK_HANDLE hLock)
{
    ASSERT_TRUE(hLock);
    LOCK* lock = (LOCK*)(hLock);
    delete lock;
}

//------------------------------------------------------------------------------
// LK_Lock
//------------------------------------------------------------------------------
void LK_Lock(LOCK_HANDLE hLock)
{
    ASSERT_TRUE(hLock);
    LOCK* lock = (LOCK*)(hLock);
	lock->lock();
}

//------------------------------------------------------------------------------
// LK_Unlock
//------------------------------------------------------------------------------
void LK_Unlock(LOCK_HANDLE hLock)
{
    ASSERT_TRUE(hLock);
    LOCK* lock = (LOCK*)(hLock);
    lock->unlock();
}



================================================
FILE: LockGuard.h
================================================
#ifndef _LOCK_GUARD_H
#define _LOCK_GUARD_H

#include "DataTypes.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef void* LOCK_HANDLE;

#define LK_CREATE()     LK_Create()
#define LK_DESTROY(h)   LK_Destroy(h)
#define LK_LOCK(h)      LK_Lock(h)
#define LK_UNLOCK(h)    LK_Unlock(h)

LOCK_HANDLE LK_Create(void);
void LK_Destroy(LOCK_HANDLE hLock);
void LK_Lock(LOCK_HANDLE hLock);
void LK_Unlock(LOCK_HANDLE hLock);

#ifdef __cplusplus
}
#endif

#endif 


================================================
FILE: Motor.c
================================================
#include "Motor.h"
#include "StateMachine.h"
#include <stdio.h>

// State enumeration order must match the order of state
// method entries in the state map
enum States
{
    ST_IDLE,
    ST_STOP,
    ST_START,
    ST_CHANGE_SPEED,
    ST_MAX_STATES
};

// State machine state functions
STATE_DECLARE(Idle, NoEventData)
STATE_DECLARE(Stop, NoEventData)
STATE_DECLARE(Start, MotorData)
STATE_DECLARE(ChangeSpeed, MotorData)

// State map to define state function order
BEGIN_STATE_MAP(Motor)
    STATE_MAP_ENTRY(ST_Idle)
    STATE_MAP_ENTRY(ST_Stop)
    STATE_MAP_ENTRY(ST_Start)
    STATE_MAP_ENTRY(ST_ChangeSpeed)
END_STATE_MAP(Motor)

// Set motor speed external event
EVENT_DEFINE(MTR_SetSpeed, MotorData)
{
    // Given the SetSpeed event, transition to a new state based upon 
    // the current state of the state machine
    BEGIN_TRANSITION_MAP                        // - Current State -
        TRANSITION_MAP_ENTRY(ST_START)          // ST_Idle       
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)     // ST_Stop       
        TRANSITION_MAP_ENTRY(ST_CHANGE_SPEED)   // ST_Start      
        TRANSITION_MAP_ENTRY(ST_CHANGE_SPEED)   // ST_ChangeSpeed
    END_TRANSITION_MAP(Motor, pEventData)
}

// Halt motor external event
EVENT_DEFINE(MTR_Halt, NoEventData)
{
    // Given the Halt event, transition to a new state based upon 
    // the current state of the state machine
    BEGIN_TRANSITION_MAP                        // - Current State -
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)     // ST_Idle
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)     // ST_Stop
        TRANSITION_MAP_ENTRY(ST_STOP)           // ST_Start
        TRANSITION_MAP_ENTRY(ST_STOP)           // ST_ChangeSpeed
    END_TRANSITION_MAP(Motor, pEventData)
}

// State machine sits here when motor is not running
STATE_DEFINE(Idle, NoEventData)
{
    printf("%s ST_Idle\n", self->name);
}

// Stop the motor 
STATE_DEFINE(Stop, NoEventData)
{
    // Get pointer to the instance data and update currentSpeed
    Motor* pInstance = SM_GetInstance(Motor);
    pInstance->currentSpeed = 0;

    // Perform the stop motor processing here
    printf("%s ST_Stop: %d\n", self->name, pInstance->currentSpeed);

    // Transition to ST_Idle via an internal event
    SM_InternalEvent(ST_IDLE, NULL);
}

// Start the motor going
STATE_DEFINE(Start, MotorData)
{
    ASSERT_TRUE(pEventData);

    // Get pointer to the instance data and update currentSpeed
    Motor* pInstance = SM_GetInstance(Motor);
    pInstance->currentSpeed = pEventData->speed;

    // Set initial motor speed processing here
    printf("%s ST_Start: %d\n", self->name, pInstance->currentSpeed);
}

// Changes the motor speed once the motor is moving
STATE_DEFINE(ChangeSpeed, MotorData)
{
    ASSERT_TRUE(pEventData);

    // Get pointer to the instance data and update currentSpeed
    Motor* pInstance = SM_GetInstance(Motor);
    pInstance->currentSpeed = pEventData->speed;

    // Perform the change motor speed here
    printf("%s ST_ChangeSpeed: %d\n", self->name, pInstance->currentSpeed);
}

// Get current speed
GET_DEFINE(MTR_GetSpeed, INT)
{
    Motor* pInstance = SM_GetInstance(Motor);
    return pInstance->currentSpeed;
}



================================================
FILE: Motor.h
================================================
#ifndef _MOTOR_H
#define _MOTOR_H

#include "DataTypes.h"
#include "StateMachine.h"

// Motor object structure
typedef struct
{
    INT currentSpeed;
} Motor;

// Event data structure
typedef struct
{
    INT speed;
} MotorData;

// State machine event functions
EVENT_DECLARE(MTR_SetSpeed, MotorData)
EVENT_DECLARE(MTR_Halt, NoEventData)

// Public accessor
GET_DECLARE(MTR_GetSpeed, INT);

#endif // _MOTOR_H


================================================
FILE: README.md
================================================
![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)
[![conan Ubuntu](https://github.com/endurodave/C_StateMachine/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/C_StateMachine/actions/workflows/cmake_ubuntu.yml)
[![conan Ubuntu](https://github.com/endurodave/C_StateMachine/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/C_StateMachine/actions/workflows/cmake_clang.yml)
[![conan Windows](https://github.com/endurodave/C_StateMachine/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/C_StateMachine/actions/workflows/cmake_windows.yml)

# State Machine Design in C

A compact C finite state machine (FSM) implementation that's easy to use on embedded and PC-based systems.

# Table of Contents

- [State Machine Design in C](#state-machine-design-in-c)
- [Table of Contents](#table-of-contents)
- [Preface](#preface)
  - [Related repositories](#related-repositories)
- [Getting Started](#getting-started)
- [Introduction](#introduction)
  - [Background](#background)
- [Why use a state machine?](#why-use-a-state-machine)
- [State machine design](#state-machine-design)
  - [Internal and external events](#internal-and-external-events)
  - [Event data](#event-data)
  - [State transitions](#state-transitions)
- [StateMachine module](#statemachine-module)
- [Motor example](#motor-example)
  - [External events](#external-events)
  - [State enumerations](#state-enumerations)
  - [State functions](#state-functions)
  - [State map](#state-map)
  - [State machine objects](#state-machine-objects)
  - [Transition map](#transition-map)
  - [New state machine steps](#new-state-machine-steps)
- [State engine](#state-engine)
- [Generating events](#generating-events)
- [No heap usage](#no-heap-usage)
- [CentrifugeTest example](#centrifugetest-example)
- [Multithread safety](#multithread-safety)
- [Conclusion](#conclusion)
- [References](#references)

# Preface

Originally published on CodeProject at: <a href="https://www.codeproject.com/Articles/1275479/State-Machine-Design-in-C"><strong>State Machine Design in C</strong></a>

Based on original design published in C\C++ Users Journal (Dr. Dobb's) at: <a href="http://www.drdobbs.com/cpp/state-machine-design-in-c/184401236"><strong>State Machine Design in C++</strong></a>

## Related repositories

Related repositories containing alternative state machine implementations.

| Project | Description |
  | :--- | :--- |
| [**Active-Object State Machine in C++**](https://github.com/endurodave/active-fsm) | A compact active-object C++ finite state machine providing RAII-safe asynchronous dispatch and pub/sub signals. |
  | [**C Language State Machine with Threads**](https://github.com/endurodave/C_StateMachineWithThreads) | A C language state machine with OS threads. |
  | [**State Machine Design in C++**](https://github.com/endurodave/StateMachine) | A C++ finite state machine (FSM) implementation. |

# Getting Started

[CMake](https://cmake.org/) is used to create the project build files on any Windows or Linux machine. The state machine source code works on any C language compiler on any platform.

1. Clone the repository.
2. From the repository root, run the following CMake command:   
   `cmake -B Build .`
3. Build and run the project within the `Build` directory. 

# Introduction

<p>In 2000, I wrote an article entitled &quot;<em>State Machine Design in C++</em>&quot; for C/C++ Users Journal (R.I.P.). Interestingly, that old article is still available and (at the time of writing this article) the #1 hit on Google when searching for C++ state machine. The article was written over 15 years ago, but I continue to use the basic idea on numerous projects. It&#39;s compact, easy to understand and, in most cases, has just enough features to accomplish what I need.</p>

<p>Sometimes C is the right tool for the job. This article provides an alternate C language state machine implementation based on the ideas presented within the article &ldquo;<em>State Machine Design in C++</em>&rdquo;. The design is suitable for any platform, embedded or PC, with any C compiler. This state machine has the following features:</p>

<ul>
	<li><strong>C language</strong> &ndash; state machine written in C</li>
	<li><strong>Compact </strong>&ndash; consumes a minimum amount of resources.</li>
	<li><strong>Objects</strong>&nbsp;&ndash; support&nbsp;multiple instantiations of a&nbsp;single state machine type.</li>
	<li><strong>Transition tables</strong> &ndash; transition tables precisely control state transition behavior.</li>
	<li><strong>Events </strong>&ndash; every event is a simple function with any argument types.</li>
	<li><strong>State action</strong> &ndash; every state action is a separate function with a single, unique event data argument if desired.</li>
	<li><strong>Guards/entry/exit actions</strong>&nbsp;&ndash; optionally a state machine can use guard conditions and separate entry/exit action functions for each state.</li>
	<li><strong>Macros </strong>&ndash; optional multiline macro support simplifies usage by automating the code &quot;machinery&quot;.</li>
	<li><strong>Error checking</strong> &ndash; compile time and runtime checks catch mistakes early.</li>
	<li><strong>Thread-safe</strong> &ndash; adding software locks to make the code thread-safe is easy.</li>
</ul>

<p>The article is not a tutorial on the best design decomposition practices for software state machines. I&#39;ll be focusing on state machine code and simple examples with just enough complexity to facilitate understanding the features and usage.</p>

## Background

<p>A common design technique in the repertoire of most programmers is the venerable finite state machine (FSM). Designers use this programming construct to break complex problems into manageable states and state transitions. There are innumerable ways to implement a state machine.</p>

<p>A switch statement provides one of the easiest to implement and most common version of a state machine. Here, each case within the switch statement becomes a state, implemented something like:</p>

<pre lang="c++">
switch (currentState) {
   case ST_IDLE:
       // do something in the idle state
       break;

    case ST_STOP:
       // do something in the stop state
       break;

    // etc...
}</pre>

<p>This method is certainly appropriate for solving many different design problems. When employed on an event driven, multithreaded project, however, state machines of this form can be quite limiting.</p>

<p>The first problem revolves around controlling what state transitions are valid and which ones are invalid. There is no way to enforce the state transition rules. Any transition is allowed at any time, which is not particularly desirable. For most designs, only a few transition patterns are valid. Ideally, the software design should enforce these predefined state sequences and prevent the unwanted transitions. Another problem arises when trying to send data to a specific state. Since the entire state machine is located within a single function, sending additional data to any given state proves difficult. And lastly these designs are rarely suitable for use in a multithreaded system. The designer must ensure the state machine is called from a single thread of control.</p>

# Why use a state machine?

<p>Implementing code using a state machine is an extremely handy design technique for solving complex engineering problems. State machines break down the design into a series of steps, or what are called states in state-machine lingo. Each state performs some narrowly defined task. Events, on the other hand, are the stimuli, which cause the state machine to move, or transition, between states.</p>

<p>To take a simple example, which I will use throughout this article, let&#39;s say we are designing motor-control software. We want to start and stop the motor, as well as change the motor&#39;s speed. Simple enough. The motor control events to be exposed to the client software will be as follows:</p>

<ol>
	<li><strong>Set Speed</strong> &ndash; sets the motor going at a specific speed.</li>
	<li><strong>Halt</strong> &ndash; stops the motor.</li>
</ol>

<p>These events provide the ability to start the motor at whatever speed desired, which also implies changing the speed of an already moving motor. Or we can stop the motor altogether. To the motor-control module, these two events, or functions, are considered external events. To a client using our code, however, these are just plain functions.</p>

<p>These events are not state machine states. The steps required to handle these two events are different. In this case the states are:</p>

<ol>
	<li><strong>Idle </strong>&mdash; the motor is not spinning but is at rest.</li>
</ol>

<ul>
	<li>Do nothing.</li>
</ul>

<ol start="2">
	<li><strong>Start </strong>&mdash; starts the motor from a dead stop.</li>
</ol>

<ul>
	<li>Turn on motor power.</li>
	<li>Set motor speed.</li>
</ul>

<ol start="3">
	<li><strong>Change Speed </strong>&mdash; adjust the speed of an already moving motor.</li>
</ol>

<ul>
	<li>Change motor speed.</li>
</ul>

<ol start="4">
	<li><strong>Stop </strong>&mdash; stop a moving motor.</li>
</ol>

<ul>
	<li>Turn off motor power.</li>
	<li>Go to the Idle state.</li>
</ul>

<p>As can be seen, breaking the motor control into discreet states, as opposed to having one monolithic function, we can more easily manage the rules of how to operate the motor.</p>

<p>Every state machine has the concept of a &quot;current state.&quot; This is the state the state machine currently occupies. At any given moment in time, the state machine can be in only a single state. Every instance of a particular state machine instance can set the initial state when defined. That initial state, however, does not execute during object creation. Only an event sent to the state machine causes a state function to execute.</p>

<p>To graphically illustrate the states and events, we use a state diagram. Figure 1 below shows the state transitions for the motor control module. A box denotes a state and a connecting arrow indicates the event transitions. Arrows with the event name listed are external events, whereas unadorned lines are considered internal events. (I cover the differences between internal and external events later in the article.)</p>

<p><img alt="" src="Motor.png" /></p>

<div class="Caption">Figure 1: Motor state diagram</div>

<p>As you can see, when an event comes in the state transition that occurs depends on state machine&#39;s current state. When a SetSpeed event comes in, for instance, and the motor is in the Idle state, it transitions to the Start state. However, that same SetSpeed event generated while the current state is Start transitions the motor to the ChangeSpeed state. You can also see that not all state transitions are valid. For instance, the motor can&#39;t transition from ChangeSpeed to Idle without first going through the Stop state.</p>

<p>In short, using a state machine captures and enforces complex interactions, which might otherwise be difficult to convey and implement.</p>

# State machine design

## Internal and external events

<p>As I mentioned earlier, an event is the stimulus that causes a state machine to transition between states. For instance, a button press could be an event. Events can be broken out into two categories: external and internal. The external event, at its most basic level, is a function call into a state-machine module. These functions are public and are called from the outside or from code external to the state-machine object. Any thread or task within a system can generate an external event. If the external event function call causes a state transition to occur, the state will execute synchronously within the caller&#39;s thread of control. An internal event, on the other hand, is self-generated by the state machine itself during state execution.</p>

<p>A typical scenario consists of an external event being generated, which, again, boils down to a function call into the module&#39;s public interface. Based upon the event being generated and the state machine&#39;s current state, a lookup is performed to determine if a transition is required. If so, the state machine transitions to the new state and the code for that state executes. At the end of the state function, a check is performed to determine whether an internal event was generated. If so, another transition is performed and the new state gets a chance to execute. This process continues until the state machine is no longer generating internal events, at which time the original external event function call returns. The external event and all internal events, if any, execute within the caller&#39;s thread of control.</p>

<p>Once the external event starts the state machine executing, it cannot be interrupted by another external event until the external event and all internal events have completed execution if locks are used. This run to completion model provides a multithread-safe environment for the state transitions. Semaphores or mutexes can be used in the state machine engine to block other threads that might be trying to be simultaneously access the same state machine instance. See source code function <code>_SM_ExternalEvent()</code> comments for where the locks go.</p>

## Event data

<p>When an event is generated, it can optionally attach event data to be used by the state function during execution. Event data is a single <code>const</code> or non-<code>const </code>pointer to any built-in or user-defined data type.</p>

<p>Once the state has completed execution, the event data is considered used up and must be deleted. Therefore, any event data sent to a state machine must be dynamically created via <code>SM_XAlloc()</code>. &nbsp;The state machine engine automatically frees allocated event data using <code>SM_XFree()</code>.</p>

## State transitions

<p>When an external event is generated, a lookup is performed to determine the state transition course of action. There are three possible outcomes to an event: new state, event ignored, or cannot happen. A new state causes a transition to a new state where it is allowed to execute. Transitions to the existing state are also possible, which means the current state is re-executed. For an ignored event, no state executes. However, the event data, if any, is deleted. The last possibility, cannot happen, is reserved for situations where the event is not valid given the current state of the state machine. If this occurs, the software faults.</p>

<p>In this implementation, internal events are not required to perform a validating transition lookup. The state transition is assumed to be valid. You could check for both valid internal and external event transitions, but in practice, this just takes more storage space and generates busywork for very little benefit. The real need for validating transitions lies in the asynchronous, external events where a client can cause an event to occur at an inappropriate time. Once the state machine is executing, it cannot be interrupted. It is under the control of the private implementation, thereby making transition checks unnecessary. This gives the designer the freedom to change states, via internal events, without the burden of updating transition tables.</p>

# StateMachine module

<p>The state machine source code is contained within the <strong>StateMachine.c</strong> and <strong>StateMachine.h</strong> files. The code below shows the partial header. The <strong><code>StateMachine</code> </strong>header contains various preprocessor multiline macros to ease implementation of a state machine.</p>

<pre lang="c++">
enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN = 0xFF };

typedef void NoEventData;

// State machine constant data
typedef struct
{
    const CHAR* name;
    const BYTE maxStates;
    const struct SM_StateStruct* stateMap;
    const struct SM_StateStructEx* stateMapEx;
} SM_StateMachineConst;

// State machine instance data
typedef struct 
{
    const CHAR* name;
    void* pInstance;
    BYTE newState;
    BYTE currentState;
    BOOL eventGenerated;
    void* pEventData;
} SM_StateMachine;

// Generic state function signatures
typedef void (*SM_StateFunc)(SM_StateMachine* self, void* pEventData);
typedef BOOL (*SM_GuardFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_EntryFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_ExitFunc)(SM_StateMachine* self);

typedef struct SM_StateStruct
{
    SM_StateFunc pStateFunc;
} SM_StateStruct;

typedef struct SM_StateStructEx
{
    SM_StateFunc pStateFunc;
    SM_GuardFunc pGuardFunc;
    SM_EntryFunc pEntryFunc;
    SM_ExitFunc pExitFunc;
} SM_StateStructEx;

// Public functions
#define SM_Event(_smName_, _eventFunc_, _eventData_) \
    _eventFunc_(&amp;_smName_##Obj, _eventData_)

// Protected functions
#define SM_InternalEvent(_newState_, _eventData_) \
    _SM_InternalEvent(self, _newState_, _eventData_)
#define SM_GetInstance(_instance_) \
    (_instance_*)(self-&gt;pInstance);

// Private functions
void _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst* selfConst, BYTE newState, void* pEventData);
void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEventData);
void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* selfConst);
void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst* selfConst);

#define SM_DECLARE(_smName_) \
    extern SM_StateMachine _smName_##Obj; 

#define SM_DEFINE(_smName_, _instance_) \
    SM_StateMachine _smName_##Obj = { #_smName_, _instance_, \
        0, 0, 0, 0 }; 

#define EVENT_DECLARE(_eventFunc_, _eventData_) \
    void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData);

#define EVENT_DEFINE(_eventFunc_, _eventData_) \
    void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData)

#define STATE_DECLARE(_stateFunc_, _eventData_) \
    static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData);

#define STATE_DEFINE(_stateFunc_, _eventData_) \
    static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData)
</pre>

<p>The <code>SM_Event()</code> macro is used to generate external events whereas <code>SM_InternalEvent()</code> generates an internal event during state function execution. <code>SM_GetInstance()</code> obtains a pointer to the current state machine object.</p>

<p><code>SM_DECLARE </code>and <code>SM_DEFINE</code> are used to create a state machine instance. <code>EVENT_DECLARE</code> and <code>EVENT_DEFINE</code> create external event functions. And finally, <code>STATE_DECLARE</code> and <code>STATE_DEFINE</code> create state functions.</p>

# Motor example

<p><code>Motor </code>implements our hypothetical motor-control state machine, where clients can start the motor, at a specific speed, and stop the motor. The <code>Motor</code> header interface is shown below:</p>

<pre lang="c++">
#include &quot;StateMachine.h&quot;

// Motor object structure
typedef struct
{
    INT currentSpeed;
} Motor;

// Event data structure
typedef struct
{
    INT speed;
} MotorData;

// State machine event functions
EVENT_DECLARE(MTR_SetSpeed, MotorData)
EVENT_DECLARE(MTR_Halt, NoEventData)</pre>

<p>The <code>Motor</code> source file uses macros to simplify usage by hiding the required state machine machinery.</p>

<pre lang="c++">
// State enumeration order must match the order of state
// method entries in the state map
enum States
{
    ST_IDLE,
    ST_STOP,
    ST_START,
    ST_CHANGE_SPEED,
    ST_MAX_STATES
};

// State machine state functions
STATE_DECLARE(Idle, NoEventData)
STATE_DECLARE(Stop, NoEventData)
STATE_DECLARE(Start, MotorData)
STATE_DECLARE(ChangeSpeed, MotorData)

// State map to define state function order
BEGIN_STATE_MAP(Motor)
    STATE_MAP_ENTRY(ST_Idle)
    STATE_MAP_ENTRY(ST_Stop)
    STATE_MAP_ENTRY(ST_Start)
    STATE_MAP_ENTRY(ST_ChangeSpeed)
END_STATE_MAP(Motor)

// Set motor speed external event
EVENT_DEFINE(MTR_SetSpeed, MotorData)
{
    // Given the SetSpeed event, transition to a new state based upon 
    // the current state of the state machine
    BEGIN_TRANSITION_MAP                        // - Current State -
        TRANSITION_MAP_ENTRY(ST_START)          // ST_Idle       
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)     // ST_Stop       
        TRANSITION_MAP_ENTRY(ST_CHANGE_SPEED)   // ST_Start      
        TRANSITION_MAP_ENTRY(ST_CHANGE_SPEED)   // ST_ChangeSpeed
    END_TRANSITION_MAP(Motor, pEventData)
}

// Halt motor external event
EVENT_DEFINE(MTR_Halt, NoEventData)
{
    // Given the Halt event, transition to a new state based upon 
    // the current state of the state machine
    BEGIN_TRANSITION_MAP                        // - Current State -
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)     // ST_Idle
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)     // ST_Stop
        TRANSITION_MAP_ENTRY(ST_STOP)           // ST_Start
        TRANSITION_MAP_ENTRY(ST_STOP)           // ST_ChangeSpeed
    END_TRANSITION_MAP(Motor, pEventData)
}</pre>

## External events

<p><code>MTR_SetSpeed </code>and <code>MTR_Halt</code> are considered external events into the <code>Motor</code> state machine. <code>MTR_SetSpeed </code>takes a pointer to <code>MotorData</code> event data, containing the motor speed. This data structure will be freed using <code>SM_XFree()</code> upon completion of the state processing, so it is imperative that it be created using <code>SM_XAlloc()</code> before the function call is made.</p>

## State enumerations

<p>Each state function must have an enumeration associated with it. These enumerations are used to store the current state of the state machine. In <code>Motor</code>, <code>States</code> provides these enumerations, which are used later for indexing into the transition map and state map lookup tables.</p>

## State functions

<p>State functions implement each state &mdash; one state function per state-machine state. <code>STATE_DECLARE </code>is used to declare the state function interface and <code>STATE_DEFINE </code>defines the implementation.</p>

<pre lang="c++">
// State machine sits here when motor is not running
STATE_DEFINE(Idle, NoEventData)
{
    printf(&quot;%s ST_Idle\n&quot;, self-&gt;name);
}

// Stop the motor 
STATE_DEFINE(Stop, NoEventData)
{
    // Get pointer to the instance data and update currentSpeed
    Motor* pInstance = SM_GetInstance(Motor);
    pInstance-&gt;currentSpeed = 0;

    // Perform the stop motor processing here
    printf(&quot;%s ST_Stop: %d\n&quot;, self-&gt;name, pInstance-&gt;currentSpeed);

    // Transition to ST_Idle via an internal event
    SM_InternalEvent(ST_IDLE, NULL);
}

// Start the motor going
STATE_DEFINE(Start, MotorData)
{
    ASSERT_TRUE(pEventData);

    // Get pointer to the instance data and update currentSpeed
    Motor* pInstance = SM_GetInstance(Motor);
    pInstance-&gt;currentSpeed = pEventData-&gt;speed;

    // Set initial motor speed processing here
    printf(&quot;%s ST_Start: %d\n&quot;, self-&gt;name, pInstance-&gt;currentSpeed);
}

// Changes the motor speed once the motor is moving
STATE_DEFINE(ChangeSpeed, MotorData)
{
    ASSERT_TRUE(pEventData);

    // Get pointer to the instance data and update currentSpeed
    Motor* pInstance = SM_GetInstance(Motor);
    pInstance-&gt;currentSpeed = pEventData-&gt;speed;

    // Perform the change motor speed here
    printf(&quot;%s ST_ChangeSpeed: %d\n&quot;, self-&gt;name, pInstance-&gt;currentSpeed);
}
</pre>

<p><code>STATE_DECLARE</code> and <code>STATE_DEFINE</code> use two arguments. The first argument is the state function name. The second argument is the event data type. If no event data is required, use <code>NoEventData</code>. Macros are also available for creating guard, exit and entry actions which are explained later in the article.</p>

<p>The <code>SM_GetInstance()</code> macro obtains an instance to the state machine object. The argument to the macro is the state machine name.</p>

<p>In this implementation, all state machine functions must adhere to these signatures, which are as follows:</p>

<pre lang="c++">
// Generic state function signatures
typedef void (*SM_StateFunc)(SM_StateMachine* self, void* pEventData);
typedef BOOL (*SM_GuardFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_EntryFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_ExitFunc)(SM_StateMachine* self);
</pre>

<p>Each <code>SM_StateFunc </code>accepts a pointer to a <code>SM_StateMachine</code> object and event data. If <code>NoEventData </code>is used, the <code>pEventData </code>argument will be <code>NULL</code>. Otherwise, the <code>pEventData</code> argument is of the type specified in <code>STATE_DEFINE</code>.</p>

<p>In <code>Motor</code>&rsquo;s <code>Start</code> state function, the <code>STATE_DEFINE(Start, MotorData)&nbsp;</code>macro expands to:</p>

<pre lang="c++">
void ST_Start(SM_StateMachine* self, MotorData* pEventData)</pre>

<p>Notice that every state function has <code>self </code>and&nbsp;<code>pEventData </code>arguments. <code>self </code>is a pointer to the state machine object and <code>pEventData </code>is the event data. Also note that the macro prepends &ldquo;ST_&rdquo; to the state name to create the function <code>ST_Start()</code>.</p>

<p>Similarly, the <code>Stop </code>state function <code>STATE_DEFINE(Stop, NoEventData)</code> is expands to:</p>

<pre lang="c++">
void ST_Stop(SM_StateMachine* self, void* pEventData)</pre>

<p><code>Stop </code>doesn&#39;t accept event data so the <code>pEventData </code>argument is <code>void*</code>.&nbsp;</p>

<p>Three characters are added to each state/guard/entry/exit function automatically within the macros. For instance, if declaring a function using <code>STATE_DEFINE(Idle, NoEventData)</code> the actual state function name is called <code>ST_Idle()</code>.</p>

<ol>
	<li>ST_ - state function prepend characters</li>
	<li>GD_ - guard function prepend characters</li>
	<li>EN_ - entry function prepend characters</li>
	<li>EX_ - exit function prepend characters</li>
</ol>

<p><code>SM_GuardFunc </code>and <code>SM_Entry </code>function <code>typedef</code>&rsquo;s also accept event data. <code>SM_ExitFunc </code>is unique in that no event data is allowed.</p>

## State map

<p>The state-machine engine knows which state function to call by using the state map. The state map maps the <code>currentState</code> variable to a specific state function. For instance, if <code>currentState </code>is 2, then the third state-map function pointer entry will be called (counting from zero). The state map table is created using these three macros:</p>

<div>
<pre>
BEGIN_STATE_MAP
STATE_MAP_ENTRY
END_STATE_MAP</pre>
</div>

<p><code>BEGIN_STATE_MAP </code>starts the state map sequence. Each <code>STATE_MAP_ENTRY </code>has a state function name argument. <code>END_STATE_MAP </code>terminates the map. The state map for <code>Motor </code>is shown below.</p>

<pre lang="c++">
BEGIN_STATE_MAP(Motor)
    STATE_MAP_ENTRY(ST_Idle)
    STATE_MAP_ENTRY(ST_Stop)
    STATE_MAP_ENTRY(ST_Start)
    STATE_MAP_ENTRY(ST_ChangeSpeed)
END_STATE_MAP
</pre>

<p>Alternatively, guard/entry/exit features require utilizing the <code>_EX</code> (extended) version of the macros.</p>

<pre lang="c++">
BEGIN_STATE_MAP_EX
STATE_MAP_ENTRY_EX or STATE_MAP_ENTRY_ALL_EX 
END_STATE_MAP_EX</pre>

<p>The <code>STATE_MAP_ENTRY_ALL_EX </code>macro has four arguments for the state action, guard condition, entry action and exit action in that order. The state action is mandatory but the other actions are optional. If a state doesn&#39;t have an action, then use 0 for the argument. If a state doesn&#39;t have any guard/entry/exit options, the <code>STATE_MAP_ENTRY_EX </code>macro defaults all unused options to 0. The macro snippet below is for an advanced example presented later in the article.</p>

<pre lang="c++">
// State map to define state function order
BEGIN_STATE_MAP_EX(CentrifugeTest)
    STATE_MAP_ENTRY_ALL_EX(ST_Idle, 0, EN_Idle, 0)
    STATE_MAP_ENTRY_EX(ST_Completed)
    STATE_MAP_ENTRY_EX(ST_Failed)
    STATE_MAP_ENTRY_ALL_EX(ST_StartTest, GD_StartTest, 0, 0)
    STATE_MAP_ENTRY_EX(ST_Acceleration)
    STATE_MAP_ENTRY_ALL_EX(ST_WaitForAcceleration, 0, 0, EX_WaitForAcceleration)
    STATE_MAP_ENTRY_EX(ST_Deceleration)
    STATE_MAP_ENTRY_ALL_EX(ST_WaitForDeceleration, 0, 0, EX_WaitForDeceleration)
END_STATE_MAP_EX(CentrifugeTest)</pre>

<p>Don&rsquo;t forget to add the prepended characters (ST_, GD_, EN_ or EX_) for each function.</p>

## State machine objects

<p>In C++, objects are integral to the language. Using C, you have to work a bit harder to accomplish similar behavior. This C language state machine supports multiple state machine objects (or instances) instead of having&nbsp;a single, static state machine implementation.</p>

<p>The <code>SM_StateMachine </code>data structure stores state machine instance data; one object per state machine instance. The&nbsp;<code>SM_StateMachineConst </code>data structure stores constant data; one constant object per state machine type.</p>

<p>The state machine is defined using <code>SM_DEFINE </code>macro. The first argument is the state machine name. The second argument is a pointer to a user defined state machine structure, or <code>NULL </code>if no user object.</p>

<pre lang="c++">
#define SM_DEFINE(_smName_, _instance_) \
    SM_StateMachine _smName_##Obj = { #_smName_, _instance_, \
        0, 0, 0, 0 };</pre>

<p>In this example, the state machine name is <code>Motor</code> and two objects and two state machines are created.</p>

<pre lang="c++">
// Define motor objects
static Motor motorObj1;
static Motor motorObj2;

// Define two public Motor state machine instances
SM_DEFINE(Motor1SM, &amp;motorObj1)
SM_DEFINE(Motor2SM, &amp;motorObj2)</pre>

<p>Each motor object handles state execution independent of the other. The <code>Motor </code>structure is used to store state machine instance-specific data. Within a state function, use <code>SM_GetInstance()</code>&nbsp;to obtain&nbsp;a pointer to the <code>Motor </code>object at runtime.</p>

<pre lang="c++">
// Get pointer to the instance data and update currentSpeed
Motor* pInstance = SM_GetInstance(Motor);
pInstance-&gt;currentSpeed = pEventData-&gt;speed;
</pre>

## Transition map

<p>The last detail to attend to are the state transition rules. How does the state machine know what transitions should occur? The answer is the transition map. A transition map is lookup table that maps the <code>currentState </code>variable to a state enum constant. Every external event function has a transition map table created with three macros:</p>

<pre lang="c++">
BEGIN_TRANSITION_MAP
TRANSITION_MAP_ENTRY
END_TRANSITION_MAP
</pre>

<p>The <code>MTR_Halt </code>event function in <code>Motor </code>defines the transition map as:</p>

<pre lang="c++">
// Halt motor external event
EVENT_DEFINE(MTR_Halt, NoEventData)
{
    // Given the Halt event, transition to a new state based upon 
    // the current state of the state machine
    BEGIN_TRANSITION_MAP                        // - Current State -
        TRANSITION_MAP_ENTRY(EVENT_IGNORED)     // ST_Idle
        TRANSITION_MAP_ENTRY(CANNOT_HAPPEN)     // ST_Stop
        TRANSITION_MAP_ENTRY(ST_STOP)           // ST_Start
        TRANSITION_MAP_ENTRY(ST_STOP)           // ST_ChangeSpeed
    END_TRANSITION_MAP(Motor, pEventData)
}
</pre>

<p><code>BEGIN_TRANSITION_MAP</code> starts the map. Each <code>TRANSITION_MAP_ENTRY</code> that follows indicates what the state machine should do based upon the current state. The number of entries in each transition map table must match the number of state functions exactly. In our example, we have four state functions, so we need four transition map entries. The location of each entry matches the order of state functions defined within the state map. Thus, the first entry within the <code>MTR_Halt</code> function indicates an <code>EVENT_IGNORED </code>as shown below.</p>

<pre lang="c++">
TRANSITION_MAP_ENTRY (EVENT_IGNORED)&nbsp;&nbsp;&nbsp; <i>// ST_Idle</i></pre>

<p>This is interpreted as &quot;If a Halt event occurs while the current state is state Idle, just ignore the event.&quot;</p>

<p>Similarly, the third entry in the map is:</p>

<div>
<pre>
TRANSITION_MAP_ENTRY (ST_STOP)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <em>// ST_Start</em></pre>
</div>

<p>This indicates &quot;If a Halt event occurs while current is state Start, then transition to state Stop.&quot;</p>

<p><code>END_TRANSITION_MAP </code>terminates the map. The first argument to this macro is the state machine name. The second argument is the event data.</p>

<p>The <code>C_ASSERT()</code> macro is used within <code>END_TRANSITION_MAP</code>. If there is a mismatch between the number of state machine states and the number of transition map entries, a compile time error is generated.</p>

## New state machine steps

<p>Creating a new state machine requires a few basic high-level steps:</p>

<ol>
	<li>Create a <code>States </code>enumeration with one entry per state function.</li>
	<li>Define state functions.</li>
	<li>Define event functions.</li>
	<li>Create one state map lookup table using the <code>STATE_MAP</code> macros.</li>
	<li>Create one transition map lookup table for each external event function using the <code>TRANSITION_MAP </code>macros.</li>
</ol>

# State engine

<p>The state engine executes the state functions based upon events generated. The transition map is an array of <code>SM_StateStruct</code> instances indexed by the <code>currentState </code>variable. When the <code>_SM_StateEngine()</code> function executes, it looks up the correct state function within the <code>SM_StateStruct </code>array. After the state function has a chance to execute, it frees the event data, if any, before checking to see if any internal events were generated via <code>SM_InternalEvent()</code>.</p>

<pre lang="c++">
// The state engine executes the state machine states
void _SM_StateEngine(SM_StateMachine* self, SM_StateMachineConst* selfConst)
{
&nbsp; &nbsp; void* pDataTemp = NULL;

&nbsp; &nbsp; ASSERT_TRUE(self);
&nbsp; &nbsp; ASSERT_TRUE(selfConst);

&nbsp; &nbsp; // While events are being generated keep executing states
&nbsp; &nbsp; while (self-&gt;eventGenerated)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; // Error check that the new state is valid before proceeding
&nbsp; &nbsp; &nbsp; &nbsp; ASSERT_TRUE(self-&gt;newState &lt; selfConst-&gt;maxStates);

&nbsp; &nbsp; &nbsp; &nbsp; // Get the pointers from the state map
&nbsp; &nbsp; &nbsp; &nbsp; SM_StateFunc state = selfConst-&gt;stateMap[self-&gt;newState].pStateFunc;

&nbsp; &nbsp; &nbsp; &nbsp; // Copy of event data pointer
&nbsp; &nbsp; &nbsp; &nbsp; pDataTemp = self-&gt;pEventData;

&nbsp; &nbsp; &nbsp; &nbsp; // Event data used up, reset the pointer
&nbsp; &nbsp; &nbsp; &nbsp; self-&gt;pEventData = NULL;

&nbsp; &nbsp; &nbsp; &nbsp; // Event used up, reset the flag
&nbsp; &nbsp; &nbsp; &nbsp; self-&gt;eventGenerated = FALSE;

&nbsp; &nbsp; &nbsp; &nbsp; // Switch to the new current state
&nbsp; &nbsp; &nbsp; &nbsp; self-&gt;currentState = self-&gt;newState;

&nbsp; &nbsp; &nbsp; &nbsp; // Execute the state action passing in event data
&nbsp; &nbsp; &nbsp; &nbsp; ASSERT_TRUE(state != NULL);
&nbsp; &nbsp; &nbsp; &nbsp; state(self, pDataTemp);

&nbsp; &nbsp; &nbsp; &nbsp; // If event data was used, then delete it
&nbsp; &nbsp; &nbsp; &nbsp; if (pDataTemp)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SM_XFree(pDataTemp);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pDataTemp = NULL;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}
</pre>

<p>The state engine logic for guard, entry, state, and exit actions is expressed by the following sequence. The <code>_SM_StateEngine()</code> engine implements only #1 and #5 below. The extended <code>_SM_StateEngineEx()</code> engine uses the entire logic sequence.</p>

<ol>
	<li>Evaluate the state transition table. If <code>EVENT_IGNORED</code>, the event is ignored and the transition is not performed. If <code>CANNOT_HAPPEN</code>, the software faults. Otherwise, continue with next step.</li>
	<li>If a guard condition is defined execute the guard condition function. If the guard condition returns <code>FALSE</code>, the state transition is ignored and the state function is not called. If the guard returns <code>TRUE</code>, or if no guard condition exists, the state function will be executed.</li>
	<li>If transitioning to a new state and an exit action is defined for the current state, call the current state exit action function.</li>
	<li>If transitioning to a new state and an entry action is defined for the new state, call the new state entry action function.</li>
	<li>Call the state action function for the new state. The new state is now the current state.</li>
</ol>

# Generating events

<p>At this point, we have a working state machine. Let&#39;s see how to generate events to it. An external event is generated by dynamically creating the event data structure using <code>SM_XAlloc()</code>, assigning the structure member variables, and calling the external event function using the <code>SM_Event()</code> macro. The following code fragment shows how a synchronous call is made.</p>

<pre lang="c++">
MotorData* data;
 
// Create event data
data = SM_XAlloc(sizeof(MotorData));
data-&gt;speed = 100;

// Call MTR_SetSpeed event function to start motor
SM_Event(Motor1SM, MTR_SetSpeed, data);
</pre>

<p>The <code>SM_Event()</code> first argument is the state machine name. The second argument is the event function to invoke. The third argument is the event data, or <code>NULL </code>if no data.</p>

<p>To generate an internal event from within a state function, call <code>SM_InternalEvent()</code>. If the destination doesn&#39;t accept event data, then the last argument is <code>NULL</code>. Otherwise, create the event data using <code>SM_XAlloc()</code>.</p>

<pre lang="c++">
SM_InternalEvent(ST_IDLE, NULL);</pre>

<p>In the example above, once the state function completes execution the state machine will transition to the <code>ST_Idle</code> state. If, on the other hand, event data needs to be sent to the destination state, then the data structure needs to be created on the heap and passed in as an argument.</p>

<pre lang="c++">
MotorData* data;    
data = SM_XAlloc(sizeof(MotorData));
data-&gt;speed = 100;
SM_InternalEvent(ST_CHANGE_SPEED, data);
</pre>

# No heap usage

<p>All state machine event data must be dynamically created. However, on some systems using the heap is undesirable. The included <code>x_allocator</code> module is a fixed block memory allocator that eliminates heap usage. Define <code>USE_SM_ALLOCATOR </code>within <strong>StateMachine.c</strong> to use the fixed block allocator. See the <strong>References</strong> section below for&nbsp;<code>x_allocator</code> information.</p>

# CentrifugeTest example

<p>The <code>CentrifugeTest </code>example shows how an extended state machine is created using guard, entry and exit actions. The state diagram is shown below.</p>

<p><img alt="" src="CentrifugeTest.png" style="height: 736px; width: 500px" /></p>

<div class="Caption">Figure 2: CentrifugeTest state diagram</div>

<p>A <code>CentrifgeTest </code>object and state machine is created. The only difference here is that the state machine is a singleton, meaning the object is private and only one instance of <code>CentrifugeTest </code>can be created. This is unlike the <code>Motor </code>state machine where multiple instances are allowed.</p>

<pre lang="c++">
// CentrifugeTest object structure
typedef struct
{
    INT speed;
    BOOL pollActive;
} CentrifugeTest;

// Define private instance of motor state machine
CentrifugeTest centrifugeTestObj;
SM_DEFINE(CentrifugeTestSM, &amp;centrifugeTestObj)
</pre>

<p>The extended state machine uses <code>ENTRY_DECLARE</code>, <code>GUARD_DECLARE</code> and <code>EXIT_DECLARE </code>macros.</p>

<pre lang="c++">
// State enumeration order must match the order of state
// method entries in the state map
enum States
{
    ST_IDLE,
    ST_COMPLETED,
    ST_FAILED,
    ST_START_TEST,
    ST_ACCELERATION,
    ST_WAIT_FOR_ACCELERATION,
    ST_DECELERATION,
    ST_WAIT_FOR_DECELERATION,
    ST_MAX_STATES
};

// State machine state functions
STATE_DECLARE(Idle, NoEventData)
ENTRY_DECLARE(Idle, NoEventData)
STATE_DECLARE(Completed, NoEventData)
STATE_DECLARE(Failed, NoEventData)
STATE_DECLARE(StartTest, NoEventData)
GUARD_DECLARE(StartTest, NoEventData)
STATE_DECLARE(Acceleration, NoEventData)
STATE_DECLARE(WaitForAcceleration, NoEventData)
EXIT_DECLARE(WaitForAcceleration)
STATE_DECLARE(Deceleration, NoEventData)
STATE_DECLARE(WaitForDeceleration, NoEventData)
EXIT_DECLARE(WaitForDeceleration)

// State map to define state function order
BEGIN_STATE_MAP_EX(CentrifugeTest)
    STATE_MAP_ENTRY_ALL_EX(ST_Idle, 0, EN_Idle, 0)
    STATE_MAP_ENTRY_EX(ST_Completed)
    STATE_MAP_ENTRY_EX(ST_Failed)
    STATE_MAP_ENTRY_ALL_EX(ST_StartTest, GD_StartTest, 0, 0)
    STATE_MAP_ENTRY_EX(ST_Acceleration)
    STATE_MAP_ENTRY_ALL_EX(ST_WaitForAcceleration, 0, 0, EX_WaitForAcceleration)
    STATE_MAP_ENTRY_EX(ST_Deceleration)
    STATE_MAP_ENTRY_ALL_EX(ST_WaitForDeceleration, 0, 0, EX_WaitForDeceleration)
END_STATE_MAP_EX(CentrifugeTest)
</pre>

<p>Notice the <code>_EX</code> extended state map macros so the guard/entry/exit features are supported. Each guard/entry/exit <code>DECLARE </code>macro must be matched with the <code>DEFINE</code>. For instance, a guard condition for the <code>StartTest </code>state function is declared as:</p>

<pre lang="c++">
GUARD_DECLARE(StartTest, NoEventData)</pre>

<p>The guard condition function returns <code>TRUE </code>if the state function is to be executed or <code>FALSE </code>otherwise.</p>

<pre lang="c++">
// Guard condition to determine whether StartTest state is executed.
GUARD_DEFINE(StartTest, NoEventData)
{
    printf(&quot;%s GD_StartTest\n&quot;, self-&gt;name);
    if (centrifugeTestObj.speed == 0)
        return TRUE;    // Centrifuge stopped. OK to start test.
    else
        return FALSE;   // Centrifuge spinning. Can&#39;t start test.
}
</pre>

# Multithread safety    

<p>To prevent preemption by another thread when the state machine is in the process of execution, the <code>StateMachine </code>module can use locks within the <code>_SM_ExternalEvent()</code>&nbsp;function. Before the external event is allowed to execute, a semaphore can be locked. When the external event and all internal events have been processed, the software lock is released, allowing another external event to enter the state machine instance.</p>

<p>Comments indicate where the lock and unlock should be placed if the application is multithreaded&nbsp;<em>and</em> mutiple threads are able to access a single state machine instance. Note that each <code>StateMachine </code>object should have its own instance of a software lock. This prevents a single instance from locking and preventing all other <code>StateMachine </code>objects from executing. Software locks are only required if a <code>StateMachine </code>instance is called by multiple threads of control. If not, then locks are not required.</p>

<ul>
</ul>

# Conclusion

<p>Implementing a state machine using this method as opposed to the old switch statement style may seem like extra effort. However, the payoff is in a more robust design that is capable of being employed uniformly over an entire multithreaded system. Having each state in its own function provides easier reading than a single huge <code>switch</code> statement, and allows unique event data to be sent to each state. In addition, validating state transitions prevents client misuse by eliminating the side effects caused by unwanted state transitions.</p>

<p>This C language version is a close translation of the&nbsp;C++ implementation I&rsquo;ve used for many years on different projects. Consider the C++ implementation within the <strong>References </strong>section if using C++.</p>

# References

<ul>
    <li><a href="https://github.com/endurodave/C_StateMachineWithThreads">C Language State Machine with Threads</a> - by David Lafreniere</li>
	<li><a href="https://github.com/endurodave/C_Allocator">A Fixed Block Allocator in C</a> - by David Lafreniere</li>
    <li><a href="https://github.com/endurodave/StateMachine">State Machine Design in C++</a> - by David Lafreniere</li>
</ul>


================================================
FILE: StateMachine.c
================================================
#include "Fault.h"
#include "StateMachine.h"

// @see https://github.com/endurodave/C_StateMachine

// Generates an external event. Called once per external event 
// to start the state machine executing
void _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst* selfConst, BYTE newState, void* pEventData)
{
    // If we are supposed to ignore this event
    if (newState == EVENT_IGNORED) 
    {
        // Just delete the event data, if any
        if (pEventData)
            SM_XFree(pEventData);
    }
    else 
    {
        // TODO - capture software lock here for thread-safety if necessary

        // Generate the event 
        _SM_InternalEvent(self, newState, pEventData);

        // Execute state machine based on type of state map defined
        if (selfConst->stateMap)
            _SM_StateEngine(self, selfConst);
        else
            _SM_StateEngineEx(self, selfConst);

        // TODO - release software lock here 
    }
}

// Generates an internal event. Called from within a state 
// function to transition to a new state
void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEventData)
{
    ASSERT_TRUE(self);

    self->pEventData = pEventData;
    self->eventGenerated = TRUE;
    self->newState = newState;
}

// The state engine executes the state machine states
void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* selfConst)
{
    void* pDataTemp = NULL;

    ASSERT_TRUE(self);
    ASSERT_TRUE(selfConst);

    // While events are being generated keep executing states
    while (self->eventGenerated)
    {
        // Error check that the new state is valid before proceeding
        ASSERT_TRUE(self->newState < selfConst->maxStates);

        // Get the pointers from the state map
        SM_StateFunc state = selfConst->stateMap[self->newState].pStateFunc;

        // Copy of event data pointer
        pDataTemp = self->pEventData;

        // Event data used up, reset the pointer
        self->pEventData = NULL;

        // Event used up, reset the flag
        self->eventGenerated = FALSE;

        // Switch to the new current state
        self->currentState = self->newState;

        // Execute the state action passing in event data
        ASSERT_TRUE(state != NULL);
        state(self, pDataTemp);

        // If event data was used, then delete it
        if (pDataTemp)
        {
            SM_XFree(pDataTemp);
            pDataTemp = NULL;
        }
    }
}

// The state engine executes the extended state machine states
void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst* selfConst)
{
    BOOL guardResult = TRUE;
    void* pDataTemp = NULL;

    ASSERT_TRUE(self);
    ASSERT_TRUE(selfConst);

    // While events are being generated keep executing states
    while (self->eventGenerated)
    {
        // Error check that the new state is valid before proceeding
        ASSERT_TRUE(self->newState < selfConst->maxStates);

        // Get the pointers from the extended state map
        SM_StateFunc state = selfConst->stateMapEx[self->newState].pStateFunc;
        SM_GuardFunc guard = selfConst->stateMapEx[self->newState].pGuardFunc;
        SM_EntryFunc entry = selfConst->stateMapEx[self->newState].pEntryFunc;
        SM_ExitFunc exit = selfConst->stateMapEx[self->currentState].pExitFunc;

        // Copy of event data pointer
        pDataTemp = self->pEventData;

        // Event data used up, reset the pointer
        self->pEventData = NULL;

        // Event used up, reset the flag
        self->eventGenerated = FALSE;

        // Execute the guard condition
        if (guard != NULL)
            guardResult = guard(self, pDataTemp);

        // If the guard condition succeeds
        if (guardResult == TRUE)
        {
            // Transitioning to a new state?
            if (self->newState != self->currentState)
            {
                // Execute the state exit action on current state before switching to new state
                if (exit != NULL)
                    exit(self);

                // Execute the state entry action on the new state
                if (entry != NULL)
                    entry(self, pDataTemp);

                // Ensure exit/entry actions didn't call SM_InternalEvent by accident 
                ASSERT_TRUE(self->eventGenerated == FALSE);
            }

            // Switch to the new current state
            self->currentState = self->newState;

            // Execute the state action passing in event data
            ASSERT_TRUE(state != NULL);
            state(self, pDataTemp);
        }

        // If event data was used, then delete it
        if (pDataTemp)
        {
            SM_XFree(pDataTemp);
            pDataTemp = NULL;
        }
    }
}


================================================
FILE: StateMachine.h
================================================
// https://github.com/endurodave/C_StateMachine
//
// The StateMachine module is a C language implementation of a finite state 
// machine (FSM).
//
// All event data must be created dynamically using SM_XAlloc. Use a fixed 
// block allocator or the heap as desired. 
//
// The standard version (non-EX) supports state and event functions. The 
// extended version (EX) supports the additional guard, entry and exit state
// machine features. 
//
// Macros are used to assist in creating the state machine machinery. 

#ifndef _STATE_MACHINE_H
#define _STATE_MACHINE_H

#include "DataTypes.h"
#include "Fault.h"

#ifdef __cplusplus
extern "C" {
#endif

// Define USE_SM_ALLOCATOR to use the fixed block allocator instead of heap
#define USE_SM_ALLOCATOR
#ifdef USE_SM_ALLOCATOR
    #include "sm_allocator.h"
    #define SM_XAlloc(size)    SMALLOC_Alloc(size)
    #define SM_XFree(ptr)      SMALLOC_Free(ptr)
#else
    #include <stdlib.h>
    #define SM_XAlloc(size)    malloc(size)
    #define SM_XFree(ptr)      free(ptr)
#endif

enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN = 0xFF };

typedef void NoEventData;

// State machine constant data
typedef struct
{
    const CHAR* name;
    const BYTE maxStates;
    const struct SM_StateStruct* stateMap;
    const struct SM_StateStructEx* stateMapEx;
} SM_StateMachineConst;

// State machine instance data
typedef struct 
{
    const CHAR* name;
    void* pInstance;
    BYTE newState;
    BYTE currentState;
    BOOL eventGenerated;
    void* pEventData;
} SM_StateMachine;

// Generic state function signatures
typedef void (*SM_StateFunc)(SM_StateMachine* self, void* pEventData);
typedef BOOL (*SM_GuardFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_EntryFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_ExitFunc)(SM_StateMachine* self);

typedef struct SM_StateStruct
{
    SM_StateFunc pStateFunc;
} SM_StateStruct;

typedef struct SM_StateStructEx
{
    SM_StateFunc pStateFunc;
    SM_GuardFunc pGuardFunc;
    SM_EntryFunc pEntryFunc;
    SM_ExitFunc pExitFunc;
} SM_StateStructEx;

// Public functions
#define SM_Event(_smName_, _eventFunc_, _eventData_) \
    _eventFunc_(&_smName_##Obj, _eventData_)
#define SM_Get(_smName_, _getFunc_) \
    _getFunc_(&_smName_##Obj)

// Protected functions
#define SM_InternalEvent(_newState_, _eventData_) \
    _SM_InternalEvent(self, _newState_, _eventData_)
#define SM_GetInstance(_instance_) \
    (_instance_*)(self->pInstance);

// Private functions
void _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst* selfConst, BYTE newState, void* pEventData);
void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEventData);
void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* selfConst);
void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst* selfConst);

#define SM_DECLARE(_smName_) \
    extern SM_StateMachine _smName_##Obj; 

#define SM_DEFINE(_smName_, _instance_) \
    SM_StateMachine _smName_##Obj = { #_smName_, _instance_, \
        0, 0, 0, 0 }; 

#define EVENT_DECLARE(_eventFunc_, _eventData_) \
    void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData);

#define EVENT_DEFINE(_eventFunc_, _eventData_) \
    void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData)

#define GET_DECLARE(_getFunc_, _getData_) \
    _getData_ _getFunc_(SM_StateMachine* self);

#define GET_DEFINE(_getFunc_, _getData_) \
    _getData_ _getFunc_(SM_StateMachine* self)

#define STATE_DECLARE(_stateFunc_, _eventData_) \
    static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData);

#define STATE_DEFINE(_stateFunc_, _eventData_) \
    static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData)

#define GUARD_DECLARE(_guardFunc_, _eventData_) \
    static BOOL GD_##_guardFunc_(SM_StateMachine* self, _eventData_* pEventData);

#define GUARD_DEFINE(_guardFunc_, _eventData_) \
    static BOOL GD_##_guardFunc_(SM_StateMachine* self, _eventData_* pEventData)

#define ENTRY_DECLARE(_entryFunc_, _eventData_) \
    static void EN_##_entryFunc_(SM_StateMachine* self, _eventData_* pEventData);

#define ENTRY_DEFINE(_entryFunc_, _eventData_) \
    static void EN_##_entryFunc_(SM_StateMachine* self, _eventData_* pEventData)

#define EXIT_DECLARE(_exitFunc_) \
    static void EX_##_exitFunc_(SM_StateMachine* self);

#define EXIT_DEFINE(_exitFunc_) \
    static void EX_##_exitFunc_(SM_StateMachine* self)

#define BEGIN_STATE_MAP(_smName_) \
    static const SM_StateStruct _smName_##StateMap[] = { 

#define STATE_MAP_ENTRY(_stateFunc_) \
    { (SM_StateFunc)_stateFunc_ },

#define END_STATE_MAP(_smName_) \
    }; \
    static const SM_StateMachineConst _smName_##Const = { #_smName_, \
        (sizeof(_smName_##StateMap)/sizeof(_smName_##StateMap[0])), \
        _smName_##StateMap, NULL };

#define BEGIN_STATE_MAP_EX(_smName_) \
    static const SM_StateStructEx _smName_##StateMap[] = { 

#define STATE_MAP_ENTRY_EX(_stateFunc_) \
    { (SM_StateFunc)_stateFunc_, NULL, NULL, NULL },

#define STATE_MAP_ENTRY_ALL_EX(_stateFunc_, _guardFunc_, _entryFunc_, _exitFunc_) \
    { (SM_StateFunc)_stateFunc_, (SM_GuardFunc)_guardFunc_, (SM_EntryFunc)_entryFunc_, (SM_ExitFunc)_exitFunc_ },

#define END_STATE_MAP_EX(_smName_) \
    }; \
    static const SM_StateMachineConst _smName_##Const = { #_smName_, \
        (sizeof(_smName_##StateMap)/sizeof(_smName_##StateMap[0])), \
        NULL, _smName_##StateMap };

#define BEGIN_TRANSITION_MAP \
    static const BYTE TRANSITIONS[] = { \

#define TRANSITION_MAP_ENTRY(_entry_) \
    _entry_,

#define END_TRANSITION_MAP(_smName_, _eventData_) \
    }; \
    _SM_ExternalEvent(self, &_smName_##Const, TRANSITIONS[self->currentState], _eventData_); \
    C_ASSERT((sizeof(TRANSITIONS)/sizeof(BYTE)) == (sizeof(_smName_##StateMap)/sizeof(_smName_##StateMap[0])));

#ifdef __cplusplus
}
#endif
 
#endif // _STATE_MACHINE_H


================================================
FILE: fb_allocator.c
================================================
#include "fb_allocator.h"
#include "DataTypes.h"
#include "Fault.h"
#include <string.h>

// Define USE_LOCK to use the default lock implementation
#define USE_LOCKS
#ifdef USE_LOCKS
    #include "LockGuard.h"
    static LOCK_HANDLE _hLock;
#else
    #pragma message("WARNING: Define software lock.")
    typedef int LOCK_HANDLE;
    static LOCK_HANDLE _hLock;

    #define LK_CREATE()     (1)
    #define LK_DESTROY(h)  
    #define LK_LOCK(h)    
    #define LK_UNLOCK(h)  
#endif

// Get a pointer to the client's area within a memory block
#define GET_CLIENT_PTR(_block_ptr_) \
    (_block_ptr_ ? ((void*)((char*)_block_ptr_)) : NULL)

// Get a pointer to the block using a client pointer
#define GET_BLOCK_PTR(_client_ptr_) \
    (_client_ptr_ ? ((void*)((char*)_client_ptr_)) : NULL)

static void* ALLOC_NewBlock(ALLOC_Allocator* alloc);
static void ALLOC_Push(ALLOC_Allocator* alloc, void* pBlock);
static void* ALLOC_Pop(ALLOC_Allocator* alloc);

//----------------------------------------------------------------------------
// ALLOC_NewBlock
//----------------------------------------------------------------------------
static void* ALLOC_NewBlock(ALLOC_Allocator* self)
{
    ALLOC_Block* pBlock = NULL;

    LK_LOCK(_hLock);

    // If we have not exceeded the pool maximum
    if (self->poolIndex < self->maxBlocks)
    {
        // Get pointer to a new fixed memory block within the pool
        pBlock = (void*)(self->pPool + (self->poolIndex++ * self->blockSize));
    }

    LK_UNLOCK(_hLock);

    if (!pBlock)
    {
        // Out of fixed block memory
        ASSERT();
    }

    return pBlock;
} 

//----------------------------------------------------------------------------
// ALLOC_Push
//----------------------------------------------------------------------------
static void ALLOC_Push(ALLOC_Allocator* self, void* pBlock)
{
    if (!pBlock)
        return;

    // Get a pointer to the client's location within the block
    ALLOC_Block* pClient = (ALLOC_Block*)GET_CLIENT_PTR(pBlock);

    LK_LOCK(_hLock);

    // Point client block's next pointer to head
    pClient->pNext = self->pHead;

    // The client block is now the new head
    self->pHead = pClient;

    LK_UNLOCK(_hLock); 
}

//----------------------------------------------------------------------------
// ALLOC_Pop
//----------------------------------------------------------------------------
static void* ALLOC_Pop(ALLOC_Allocator* self)
{
    ALLOC_Block* pBlock = NULL;

    LK_LOCK(_hLock);

    // Is the free-list empty?
    if (self->pHead)
    {
        // Remove the head block
        pBlock = self->pHead;

        // Set the head to the next block
        self->pHead = self->pHead->pNext;
    }

    LK_UNLOCK(_hLock); 
    return GET_BLOCK_PTR(pBlock);
} 

//----------------------------------------------------------------------------
// ALLOC_Init
//----------------------------------------------------------------------------
void ALLOC_Init()
{
    _hLock = LK_CREATE();
} 

//----------------------------------------------------------------------------
// ALLOC_Term
//----------------------------------------------------------------------------
void ALLOC_Term()
{
    LK_DESTROY(_hLock);
}

//----------------------------------------------------------------------------
// ALLOC_Alloc
//----------------------------------------------------------------------------
void* ALLOC_Alloc(ALLOC_HANDLE hAlloc, size_t size)
{
    ALLOC_Allocator* self = NULL;
    void* pBlock = NULL;

    ASSERT_TRUE(hAlloc);

    // Convert handle to an ALLOC_Allocator instance
    self = (ALLOC_Allocator*)hAlloc;

    // Ensure requested size fits within memory block 
    ASSERT_TRUE(size <= self->blockSize);

    // Get a block from the free-list
    pBlock = ALLOC_Pop(self);

    // If the free-list empty?
    if (!pBlock)
    {
        // Get a new block from the pool
        pBlock = ALLOC_NewBlock(self);
    }

    if (pBlock)
    {
        // Keep track of usage statistics
        self->allocations++;
        self->blocksInUse++;
        if (self->blocksInUse > self->maxBlocksInUse)
        {
            self->maxBlocksInUse = self->blocksInUse;
        }
    }

    return GET_CLIENT_PTR(pBlock);
} 

//----------------------------------------------------------------------------
// ALLOC_Calloc
//----------------------------------------------------------------------------
void* ALLOC_Calloc(ALLOC_HANDLE hAlloc, size_t num, size_t size)
{
    void* pMem = NULL;
    size_t n = 0;

    ASSERT_TRUE(hAlloc);

    // Compute the total size of the block
    n = num * size;

    // Allocate the memory
    pMem = ALLOC_Alloc(hAlloc, n);

    if (pMem != NULL)
    {
        // Initialize memory to 0 per calloc behavior 
        memset(pMem, 0, n);
    }

    return pMem;
}

//----------------------------------------------------------------------------
// ALLOC_Free
//----------------------------------------------------------------------------
void ALLOC_Free(ALLOC_HANDLE hAlloc, void* pBlock)
{
    ALLOC_Allocator* self = NULL;

    if (!pBlock)
        return;

    ASSERT_TRUE(hAlloc);

    // Cast handle to an allocator instance
    self = (ALLOC_Allocator*)hAlloc;

    // Get a pointer to the block
    pBlock = GET_BLOCK_PTR(pBlock);

    // Push the block onto a stack (i.e. the free-list)
    ALLOC_Push(self, pBlock);

    // Keep track of usage statistics
    self->deallocations++;
    self->blocksInUse--;
} 





================================================
FILE: fb_allocator.h
================================================
// https://github.com/endurodave/C_Allocator
//
// The fb_allocator is a fixed block memory allocator that handles a 
// single block size. 
//
// Create an allocator instance using the ALLOC_DEFINE macro. Call 
// ALLOC_Init() one time at startup. ALLOC_Alloc() allocates a fixed 
// memory block. ALLOC_Free() frees the block. 
//
// #include "fb_allocator.h"
// ALLOC_DEFINE(myAllocator, 32, 5)
//
// void main() 
// {
//      void* block;
//      ALLOC_Init();
//      block = ALLOC_Alloc(myAllocator, 32);
//      ALLOC_Free(myAllocator, block);
// }

#ifndef _FB_ALLOCATOR_H
#define _FB_ALLOCATOR_H

#include <stdlib.h>
#include "DataTypes.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef void* ALLOC_HANDLE;

typedef struct 
{
    void* pNext;
} ALLOC_Block;

// Use ALLOC_DEFINE to declare an ALLOC_Allocator object
typedef struct
{
    const char* name;
    const char* pPool;
    const size_t objectSize;
    const size_t blockSize;
    const UINT32 maxBlocks;
    ALLOC_Block* pHead;
    UINT16 poolIndex;
    UINT16 blocksInUse;
    UINT16 maxBlocksInUse;
    UINT16 allocations;
    UINT16 deallocations;
} ALLOC_Allocator;

// Align fixed blocks on X-byte boundary based on CPU architecture.
// Set value to 1, 2, 4 or 8.
#define ALLOC_MEM_ALIGN   (1)

// Get the maximum between a or b
#define ALLOC_MAX(a,b) (((a)>(b))?(a):(b))

// Round _numToRound_ to the next higher _multiple_
#define ALLOC_ROUND_UP(_numToRound_, _multiple_) \
    (((_numToRound_ + _multiple_ - 1) / _multiple_) * _multiple_)

// Ensure the memory block size is: (a) is aligned on desired boundary and (b) at
// least the size of a ALLOC_Allocator*. 
#define ALLOC_BLOCK_SIZE(_size_) \
    (ALLOC_MAX((ALLOC_ROUND_UP(_size_, ALLOC_MEM_ALIGN)), sizeof(ALLOC_Allocator*)))

// Defines block memory, allocator instance and a handle. On the example below, 
// the ALLOC_Allocator instance is myAllocatorObj and the handle is myAllocator.
// _name_ - the allocator name
// _size_ - fixed memory block size in bytes
// _objects_ - number of fixed memory blocks 
// e.g. ALLOC_DEFINE(myAllocator, 32, 10)
#define ALLOC_DEFINE(_name_, _size_, _objects_) \
    static char _name_##Memory[ALLOC_BLOCK_SIZE(_size_) * (_objects_)] = { 0 }; \
    static ALLOC_Allocator _name_##Obj = { #_name_, _name_##Memory, _size_, \
        ALLOC_BLOCK_SIZE(_size_), _objects_, NULL, 0, 0, 0, 0, 0 }; \
    static ALLOC_HANDLE _name_ = &_name_##Obj;

void ALLOC_Init(void);
void ALLOC_Term(void);
void* ALLOC_Alloc(ALLOC_HANDLE hAlloc, size_t size);
void* ALLOC_Calloc(ALLOC_HANDLE hAlloc, size_t num, size_t size);
void ALLOC_Free(ALLOC_HANDLE hAlloc, void* pBlock);

#ifdef __cplusplus
}
#endif

#endif  // _FB_ALLOCATOR_H






================================================
FILE: main.c
================================================
#include "fb_allocator.h"
#include "StateMachine.h"
#include "Motor.h"
#include "CentrifugeTest.h"

// @see https://github.com/endurodave/C_StateMachine
// 
// Other related repos:
// @see https://github.com/endurodave/C_StateMachineWithThreads
// @see https://github.com/endurodave/C_Allocator

// Define motor objects
static Motor motorObj1;
static Motor motorObj2;

// Define two public Motor state machine instances
SM_DEFINE(Motor1SM, &motorObj1)
SM_DEFINE(Motor2SM, &motorObj2)

int main(void)
{
    ALLOC_Init();

    MotorData* data;

    // Create event data
    data = SM_XAlloc(sizeof(MotorData));
    data->speed = 100;

    // Call MTR_SetSpeed event function to start motor
    SM_Event(Motor1SM, MTR_SetSpeed, data);

    // Call MTR_SetSpeed event function to change motor speed
    data = SM_XAlloc(sizeof(MotorData));
    data->speed = 200;
    SM_Event(Motor1SM, MTR_SetSpeed, data);

    // Get current speed from Motor1SM
    INT currentSpeed = SM_Get(Motor1SM, MTR_GetSpeed);

    // Stop motor again will be ignored
    SM_Event(Motor1SM, MTR_Halt, NULL);

    // Motor2SM example
    data = SM_XAlloc(sizeof(MotorData));
    data->speed = 300;
    SM_Event(Motor2SM, MTR_SetSpeed, data);
    SM_Event(Motor2SM, MTR_Halt, NULL);

    // CentrifugeTestSM example
    SM_Event(CentrifugeTestSM, CFG_Cancel, NULL);
    SM_Event(CentrifugeTestSM, CFG_Start, NULL);
    while (CFG_IsPollActive())
        SM_Event(CentrifugeTestSM, CFG_Poll, NULL);

    ALLOC_Term();

    return 0;
}



================================================
FILE: sm_allocator.c
================================================
// SMALLOC allocates either a 32 or 128 byte block depending 
// on the requested size. 

#include "sm_allocator.h"
#include "x_allocator.h"

// Maximum number of blocks for each size
#define MAX_32_BLOCKS   10
#define MAX_128_BLOCKS	5

// Define size of each block including meta data overhead
#define BLOCK_32_SIZE     32 + XALLOC_BLOCK_META_DATA_SIZE
#define BLOCK_128_SIZE    128 + XALLOC_BLOCK_META_DATA_SIZE

// Define individual fb_allocators
ALLOC_DEFINE(smDataAllocator32, BLOCK_32_SIZE, MAX_32_BLOCKS)
ALLOC_DEFINE(smDataAllocator128, BLOCK_128_SIZE, MAX_128_BLOCKS)

// An array of allocators sorted by smallest block first
static ALLOC_Allocator* allocators[] = {
    &smDataAllocator32Obj,
    &smDataAllocator128Obj
};

#define MAX_ALLOCATORS   (sizeof(allocators) / sizeof(allocators[0]))

static XAllocData self = { allocators, MAX_ALLOCATORS };

//----------------------------------------------------------------------------
// SMALLOC_Alloc
//----------------------------------------------------------------------------
void* SMALLOC_Alloc(size_t size)
{
    return XALLOC_Alloc(&self, size);
}

//----------------------------------------------------------------------------
// SMALLOC_Free
//----------------------------------------------------------------------------
void SMALLOC_Free(void* ptr)
{
    XALLOC_Free(ptr);
}

//----------------------------------------------------------------------------
// SMALLOC_Realloc
//----------------------------------------------------------------------------
void* SMALLOC_Realloc(void *ptr, size_t new_size)
{
    return XALLOC_Realloc(&self, ptr, new_size);
}

//----------------------------------------------------------------------------
// SMALLOC_Calloc
//----------------------------------------------------------------------------
void* SMALLOC_Calloc(size_t num, size_t size)
{
    return XALLOC_Calloc(&self, num, size);
}



================================================
FILE: sm_allocator.h
================================================
#ifndef _SM_ALLOCATOR_H
#define _SM_ALLOCATOR_H

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

void* SMALLOC_Alloc(size_t size);
void SMALLOC_Free(void* ptr);
void* SMALLOC_Realloc(void *ptr, size_t new_size);
void* SMALLOC_Calloc(size_t num, size_t size);

#ifdef __cplusplus
}
#endif

#endif // _SM_ALLOCATOR_H


================================================
FILE: x_allocator.c
================================================
#include "x_allocator.h"
#include "fb_allocator.h"
#include "DataTypes.h"
#include "Fault.h"
#include <string.h>

static void* XALLOC_PutAllocatorPtrInBlock(void* block, ALLOC_Allocator* allocator);
static ALLOC_Allocator* XALLOC_GetAllocatorPtrFromBlock(void* block);
static ALLOC_Allocator* XALLOC_GetAllocator(XAllocData* self, size_t size);

//----------------------------------------------------------------------------
// XALLOC_PutAllocatorPtrInBlock
//----------------------------------------------------------------------------
static void* XALLOC_PutAllocatorPtrInBlock(void* block, ALLOC_Allocator* allocator)
{
    ALLOC_Allocator** pAllocatorInBlock;

    ASSERT_TRUE(block);
    ASSERT_TRUE(allocator);

    // Cast raw block memory to ALLOC_Allocator**
    pAllocatorInBlock = (ALLOC_Allocator**)(block);

    // Store the allocator pointer in the memory block 
    *pAllocatorInBlock = allocator;

    // Advance the pointer past the ALLOC_Allocator* and return a
    // pointer to the client's memory region
    return ++pAllocatorInBlock;
}

//----------------------------------------------------------------------------
// XALLOC_GetAllocatorPtrFromBlock
//----------------------------------------------------------------------------
static ALLOC_Allocator* XALLOC_GetAllocatorPtrFromBlock(void* block)
{
    ALLOC_Allocator** pAllocatorInBlock;

    ASSERT_TRUE(block);

    // Cast raw memory block to ALLOC_Allocator**
    pAllocatorInBlock = (ALLOC_Allocator**)(block);

    // Backup one ALLOC_Allocator* position to get the stored allocator instance
    pAllocatorInBlock--;

    // Return the allocator instance stored within the memory block
    return *pAllocatorInBlock;
}

//----------------------------------------------------------------------------
// XALLOC_GetBlockPtr
//----------------------------------------------------------------------------
static void* XALLOC_GetBlockPtr(void* block)
{
    ALLOC_Allocator** pAllocatorInBlock;

    ASSERT_TRUE(block);

    // Cast the client memory to ALLOC_Allocator* 
    pAllocatorInBlock = (ALLOC_Allocator**)(block);

    // Back up one ALLOC_Allocator* position and return raw memory block pointer
    return --pAllocatorInBlock;
}

//----------------------------------------------------------------------------
// XALLOC_GetAllocator
//----------------------------------------------------------------------------
static ALLOC_Allocator* XALLOC_GetAllocator(XAllocData* self, size_t size)
{
    UINT16 i = 0;
    ALLOC_Allocator* pAllocator = NULL;

    ASSERT_TRUE(self);

    // Each block stores additional meta data (i.e. an ALLOC_Allocator pointer). 
    // Add overhead for the additional memory required.
    size += XALLOC_BLOCK_META_DATA_SIZE;

    // Iterate over all allocators 
    for (i=0; i<self->maxAllocators; i++)
    {
        // Can the allocator instance handle the requested size?
        if (self->allocators[i] && self->allocators[i]->blockSize >= size)
        {
            // Return allocator instance to handle memory request
            pAllocator = self->allocators[i];
            break;
        }
    }

    return pAllocator;
} 

//----------------------------------------------------------------------------
// XALLOC_Alloc
//----------------------------------------------------------------------------
void* XALLOC_Alloc(XAllocData* self, size_t size)
{
    ALLOC_Allocator* pAllocator;
    void* pBlockMemory = NULL;
    void* pClientMemory = NULL;

    ASSERT_TRUE(self);

    // Get an allocator instance to handle the memory request
    pAllocator = XALLOC_GetAllocator(self, size);

    // An allocator found to handle memory request?
    if (pAllocator)
    {
        // Get a fixed memory block from the allocator instance
        pBlockMemory = ALLOC_Alloc(pAllocator, size + XALLOC_BLOCK_META_DATA_SIZE);
        if (pBlockMemory)
        {
            // Set the block ALLOC_Allocator* ptr within the raw memory block region
            pClientMemory = XALLOC_PutAllocatorPtrInBlock(pBlockMemory, pAllocator);
        }
    }
    else
    {
        // Too large a memory block requested
        ASSERT();
    }

    return pClientMemory;
} 

//----------------------------------------------------------------------------
// XALLOC_Free
//----------------------------------------------------------------------------
void XALLOC_Free(void* ptr)
{
    ALLOC_Allocator* pAllocator = NULL;
    void* pBlock = NULL;

    if (!ptr)
        return;

    // Extract the original allocator instance from the caller's block pointer
    pAllocator = XALLOC_GetAllocatorPtrFromBlock(ptr);
    if (pAllocator)
    {
        // Convert the client pointer into the original raw block pointer
        pBlock = XALLOC_GetBlockPtr(ptr);

        // Deallocate the fixed memory block
        ALLOC_Free(pAllocator, pBlock);
    }
} 

//----------------------------------------------------------------------------
// XALLOC_Realloc
//----------------------------------------------------------------------------
void* XALLOC_Realloc(XAllocData* self, void *ptr, size_t new_size)
{
    void* pNewMem = NULL;
    ALLOC_Allocator* pOldAllocator = NULL;
    size_t oldSize = 0;

    ASSERT_TRUE(self);

    if (!ptr)
        pNewMem = XALLOC_Alloc(self, new_size);
    else if (0 == new_size)
        XALLOC_Free(ptr);
    else
    {
        // Create a new memory block
        pNewMem = XALLOC_Alloc(self, new_size);
        if (pNewMem != 0)
        {
            // Get the original allocator instance from the old memory block
            pOldAllocator = XALLOC_GetAllocatorPtrFromBlock(ptr);
            oldSize = pOldAllocator->blockSize - XALLOC_BLOCK_META_DATA_SIZE;

            // Copy the bytes from the old memory block into the new (as much as will fit)
            memcpy(pNewMem, ptr, (oldSize < new_size) ? oldSize : new_size);

            // Free the old memory block
            XALLOC_Free(ptr);
        }
    }

    // Return the client pointer to the new memory block
    return pNewMem;
} 

//----------------------------------------------------------------------------
// XALLOC_Calloc
//----------------------------------------------------------------------------
void* XALLOC_Calloc(XAllocData* self, size_t num, size_t size)
{
    void* pMem = NULL;
    size_t n;

    ASSERT_TRUE(self);

    // Compute the total block size
    n = num * size;

    // Allocate the memory
    pMem = XALLOC_Alloc(self, n);

    if (pMem)
    {
        // Initialize memory to 0
        memset(pMem, 0, n);
    }

    return pMem;
} 





================================================
FILE: x_allocator.h
================================================
// @see https://github.com/endurodave/C_Allocator
//
// The x_allocator is a fixed block memory allocator that handles multiple 
// block sizes by using two or more fb_allocator objects. Typically users 
// create a thin wrapper module for each x_allocator managed memory blocks. 
//
// For example, create a XAllocData instance and wrapper functions 
// in my_allocator.c:
//
// #define MAX_32_BLOCKS   10
// #define MAX_128_BLOCKS   5
// #define MAX_512_BLOCKS   2
//
// #define BLOCK_32_SIZE           32 + XALLOC_BLOCK_META_DATA_SIZE
// #define BLOCK_128_SIZE          128 + XALLOC_BLOCK_META_DATA_SIZE
// #define BLOCK_512_SIZE          512 + XALLOC_BLOCK_META_DATA_SIZE
//
// // Define each fb_allocator instance
// ALLOC_DEFINE(myAllocator32, BLOCK_32_SIZE, MAX_32_BLOCKS)
// ALLOC_DEFINE(myAllocator128, BLOCK_128_SIZE, MAX_128_BLOCKS)
// ALLOC_DEFINE(myAllocator512, BLOCK_512_SIZE, MAX_512_BLOCKS)
//
// // An array of allocators sorted by smallest to largest block 
// static ALLOC_Allocator* allocators[] = {
//    &myAllocator32Obj,
//    &myAllocator128Obj,
//    &myAllocator512Obj
// };
//
// #define MAX_ALLOCATORS   (sizeof(allocators) / sizeof(allocators[0]))
//
// static XAllocData self = { allocators, MAX_ALLOCATORS };
//
// // Thin allocator wrapper function implementations call XALLOC
// void* MYALLOC_Alloc(size_t size) { return XALLOC_Alloc(&self, size); }
// void MYALLOC_Free(void* ptr) { XALLOC_Free(ptr); }
// void* MYALLOC_Realloc(void *ptr, size_t new_size) { return XALLOC_Realloc(&self, ptr, new_size); }
// void* MYALLOC_Calloc(size_t num, size_t size) { return XALLOC_Calloc(&self, num, size); }
//
// Expose the allocator functions in my_allocator.h:
//
// void* MYALLOC_Alloc(size_t size);
// void MYALLOC_Free(void* ptr);
// void* MYALLOC_Realloc(void *ptr, size_t new_size);
// void* MYALLOC_Calloc(size_t num, size_t size);

#ifndef _X_ALLOCATOR_H
#define _X_ALLOCATOR_H

#include "fb_allocator.h"
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// Overhead bytes added to each XALLOC memory block
#define XALLOC_BLOCK_META_DATA_SIZE  sizeof(ALLOC_Allocator*)

typedef struct
{
    // Array of allocator instances sorted from smallest to largest block
    ALLOC_Allocator* const *allocators;

    // Number of allocator instances stored within the allocators array
    const UINT16 maxAllocators;
} XAllocData;

void* XALLOC_Alloc(XAllocData* self, size_t size);
void XALLOC_Free(void* ptr);
void* XALLOC_Realloc(XAllocData* self, void *ptr, size_t new_size);
void* XALLOC_Calloc(XAllocData* self, size_t num, size_t size);

#ifdef __cplusplus
}
#endif

#endif // _X_ALLOCATOR_H
Download .txt
gitextract_21gexqg1/

├── .github/
│   └── workflows/
│       ├── cmake_clang.yml
│       ├── cmake_ubuntu.yml
│       └── cmake_windows.yml
├── .gitignore
├── CMakeLists.txt
├── CentrifugeTest.c
├── CentrifugeTest.h
├── DataTypes.h
├── Fault.cpp
├── Fault.h
├── LICENSE
├── LockGuard.cpp
├── LockGuard.h
├── Motor.c
├── Motor.h
├── README.md
├── StateMachine.c
├── StateMachine.h
├── fb_allocator.c
├── fb_allocator.h
├── main.c
├── sm_allocator.c
├── sm_allocator.h
├── x_allocator.c
└── x_allocator.h
Download .txt
SYMBOL INDEX (56 symbols across 14 files)

FILE: CentrifugeTest.c
  type CentrifugeTest (line 6) | typedef struct
  type States (line 18) | enum States
  function BEGIN_TRANSITION_MAP (line 87) | BEGIN_TRANSITION_MAP                                    // - Current Sta...
  function StopPoll (line 104) | static void StopPoll(void)
  function BOOL (line 109) | BOOL CFG_IsPollActive(void)
  function EXIT_DEFINE (line 173) | EXIT_DEFINE(WaitForAcceleration)
  function EXIT_DEFINE (line 199) | EXIT_DEFINE(WaitForDeceleration)

FILE: DataTypes.h
  type INT8 (line 7) | typedef signed char INT8;
  type UINT8 (line 8) | typedef unsigned char UINT8;
  type INT16 (line 9) | typedef signed short INT16;
  type UINT16 (line 10) | typedef unsigned short UINT16;
  type UINT32 (line 11) | typedef unsigned int UINT32;
  type INT32 (line 12) | typedef int INT32;
  type CHAR (line 13) | typedef char CHAR;
  type SHORT (line 14) | typedef short SHORT;
  type LONG (line 15) | typedef long LONG;
  type INT (line 16) | typedef int INT;
  type UINT (line 17) | typedef unsigned int UINT;
  type DWORD (line 18) | typedef unsigned long DWORD;
  type BYTE (line 19) | typedef unsigned char BYTE;
  type WORD (line 20) | typedef unsigned short WORD;
  type FLOAT (line 21) | typedef float FLOAT;
  type DOUBLE (line 22) | typedef double DOUBLE;
  type BOOL (line 23) | typedef int BOOL;

FILE: Fault.cpp
  function FaultHandler (line 10) | void FaultHandler(const char* file, unsigned short line)

FILE: LockGuard.cpp
  function LOCK_HANDLE (line 11) | LOCK_HANDLE LK_Create(void)
  function LK_Destroy (line 20) | void LK_Destroy(LOCK_HANDLE hLock)
  function LK_Lock (line 30) | void LK_Lock(LOCK_HANDLE hLock)
  function LK_Unlock (line 40) | void LK_Unlock(LOCK_HANDLE hLock)

FILE: Motor.c
  type States (line 7) | enum States
  function BEGIN_TRANSITION_MAP (line 48) | BEGIN_TRANSITION_MAP                        // - Current State -

FILE: Motor.h
  type Motor (line 8) | typedef struct
  type MotorData (line 14) | typedef struct

FILE: StateMachine.c
  function _SM_ExternalEvent (line 8) | void _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst...
  function _SM_InternalEvent (line 36) | void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEven...
  function _SM_StateEngine (line 46) | void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* ...
  function _SM_StateEngineEx (line 88) | void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst...

FILE: StateMachine.h
  type NoEventData (line 39) | typedef void NoEventData;
  type SM_StateMachineConst (line 42) | typedef struct
  type SM_StateMachine (line 51) | typedef struct
  type BOOL (line 63) | typedef BOOL (*SM_GuardFunc)(SM_StateMachine* self, void* pEventData);
  type SM_StateStruct (line 67) | typedef struct SM_StateStruct
  type SM_StateStructEx (line 72) | typedef struct SM_StateStructEx

FILE: fb_allocator.c
  type LOCK_HANDLE (line 13) | typedef int LOCK_HANDLE;
  function ALLOC_Push (line 64) | static void ALLOC_Push(ALLOC_Allocator* self, void* pBlock)
  function ALLOC_Init (line 109) | void ALLOC_Init()
  function ALLOC_Term (line 117) | void ALLOC_Term()
  function ALLOC_Free (line 190) | void ALLOC_Free(ALLOC_HANDLE hAlloc, void* pBlock)

FILE: fb_allocator.h
  type ALLOC_Block (line 33) | typedef struct
  type ALLOC_Allocator (line 39) | typedef struct

FILE: main.c
  function main (line 20) | int main(void)

FILE: sm_allocator.c
  function SMALLOC_Free (line 40) | void SMALLOC_Free(void* ptr)

FILE: x_allocator.c
  function ALLOC_Allocator (line 35) | static ALLOC_Allocator* XALLOC_GetAllocatorPtrFromBlock(void* block)
  function ALLOC_Allocator (line 70) | static ALLOC_Allocator* XALLOC_GetAllocator(XAllocData* self, size_t size)
  function XALLOC_Free (line 133) | void XALLOC_Free(void* ptr)

FILE: x_allocator.h
  type XAllocData (line 60) | typedef struct
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (104K chars).
[
  {
    "path": ".github/workflows/cmake_clang.yml",
    "chars": 729,
    "preview": "name: Clang\n\non:\n  push:\n    branches:\n      - master  # Trigger on push to 'master' branch\n  pull_request:\n    branches"
  },
  {
    "path": ".github/workflows/cmake_ubuntu.yml",
    "chars": 683,
    "preview": "name: Ubuntu\n\non:\n  push:\n    branches:\n      - master  # Trigger on push to 'master' branch\n  pull_request:\n    branche"
  },
  {
    "path": ".github/workflows/cmake_windows.yml",
    "chars": 893,
    "preview": "name: Windows\n\non:\n  push:\n    branches:\n      - master  # Trigger on push to 'main' branch\n  pull_request:\n    branches"
  },
  {
    "path": ".gitignore",
    "chars": 5075,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 614,
    "preview": "# Example CMake command line to create project build files:\n#\n# *** Windows ***\n# cmake -G \"Visual Studio 17 2022\" -A Wi"
  },
  {
    "path": "CentrifugeTest.c",
    "chars": 6579,
    "preview": "#include \"CentrifugeTest.h\"\n#include \"StateMachine.h\"\n#include <stdio.h>\n\n// CentrifugeTest object structure\ntypedef str"
  },
  {
    "path": "CentrifugeTest.h",
    "chars": 402,
    "preview": "#ifndef _CENTRIFUGE_TEST_H\n#define _CENTRIFUGE_TEST_H\n\n#include \"DataTypes.h\"\n#include \"StateMachine.h\"\n\n// Declare the "
  },
  {
    "path": "DataTypes.h",
    "chars": 743,
    "preview": "#ifndef _DATA_TYPES_H\n#define _DATA_TYPES_H\n\n#if WIN32\n\t#include \"windows.h\"\n#else\n\ttypedef signed char INT8;\n\ttypedef u"
  },
  {
    "path": "Fault.cpp",
    "chars": 429,
    "preview": "#include \"Fault.h\"\n#include <assert.h>\n#if WIN32\n\t#include \"windows.h\"\n#endif\n\n//---------------------------------------"
  },
  {
    "path": "Fault.h",
    "chars": 1000,
    "preview": "#ifndef _FAULT_H\n#define _FAULT_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#pragma warning(disable : 4005)  // Disable w"
  },
  {
    "path": "LICENSE",
    "chars": 1073,
    "preview": "MIT License\n\nCopyright (c) 2022 David Lafreniere\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "LockGuard.cpp",
    "chars": 1221,
    "preview": "#include \"LockGuard.h\"\n#include \"Fault.h\"\n#include <mutex>\n\n// A lock is a mutex\n#define LOCK std::mutex\n\n//------------"
  },
  {
    "path": "LockGuard.h",
    "chars": 455,
    "preview": "#ifndef _LOCK_GUARD_H\n#define _LOCK_GUARD_H\n\n#include \"DataTypes.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef voi"
  },
  {
    "path": "Motor.c",
    "chars": 3187,
    "preview": "#include \"Motor.h\"\n#include \"StateMachine.h\"\n#include <stdio.h>\n\n// State enumeration order must match the order of stat"
  },
  {
    "path": "Motor.h",
    "chars": 411,
    "preview": "#ifndef _MOTOR_H\n#define _MOTOR_H\n\n#include \"DataTypes.h\"\n#include \"StateMachine.h\"\n\n// Motor object structure\ntypedef s"
  },
  {
    "path": "README.md",
    "chars": 44742,
    "preview": "![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)\n[![conan Ubuntu](https://"
  },
  {
    "path": "StateMachine.c",
    "chars": 4775,
    "preview": "#include \"Fault.h\"\n#include \"StateMachine.h\"\n\n// @see https://github.com/endurodave/C_StateMachine\n\n// Generates an exte"
  },
  {
    "path": "StateMachine.h",
    "chars": 5925,
    "preview": "// https://github.com/endurodave/C_StateMachine\n//\n// The StateMachine module is a C language implementation of a finite"
  },
  {
    "path": "fb_allocator.c",
    "chars": 5450,
    "preview": "#include \"fb_allocator.h\"\n#include \"DataTypes.h\"\n#include \"Fault.h\"\n#include <string.h>\n\n// Define USE_LOCK to use the d"
  },
  {
    "path": "fb_allocator.h",
    "chars": 2695,
    "preview": "// https://github.com/endurodave/C_Allocator\n//\n// The fb_allocator is a fixed block memory allocator that handles a \n//"
  },
  {
    "path": "main.c",
    "chars": 1564,
    "preview": "#include \"fb_allocator.h\"\r\n#include \"StateMachine.h\"\r\n#include \"Motor.h\"\r\n#include \"CentrifugeTest.h\"\r\n\r\n// @see https:/"
  },
  {
    "path": "sm_allocator.c",
    "chars": 1896,
    "preview": "// SMALLOC allocates either a 32 or 128 byte block depending \n// on the requested size. \n\n#include \"sm_allocator.h\"\n#inc"
  },
  {
    "path": "sm_allocator.h",
    "chars": 328,
    "preview": "#ifndef _SM_ALLOCATOR_H\n#define _SM_ALLOCATOR_H\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid* SMAL"
  },
  {
    "path": "x_allocator.c",
    "chars": 6551,
    "preview": "#include \"x_allocator.h\"\n#include \"fb_allocator.h\"\n#include \"DataTypes.h\"\n#include \"Fault.h\"\n#include <string.h>\n\nstatic"
  },
  {
    "path": "x_allocator.h",
    "chars": 2635,
    "preview": "// @see https://github.com/endurodave/C_Allocator\n//\n// The x_allocator is a fixed block memory allocator that handles m"
  }
]

About this extraction

This page contains the full source code of the endurodave/C_StateMachine GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (97.7 KB), approximately 24.7k tokens, and a symbol index with 56 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!