Skip to content

Commit

Permalink
Merge pull request #28 from kiishor/feature/hsm_demo_toaster_oven
Browse files Browse the repository at this point in the history
Feature/hsm demo toaster oven
  • Loading branch information
kiishor committed Feb 9, 2020
2 parents 086d809 + 6ad1c64 commit 740154c
Show file tree
Hide file tree
Showing 11 changed files with 863 additions and 0 deletions.
1 change: 1 addition & 0 deletions demo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ project("demo")

add_subdirectory(simple_state_machine)
add_subdirectory(simple_state_machine_enhanced)
add_subdirectory(toaster_oven)
49 changes: 49 additions & 0 deletions demo/toaster_oven/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project("toaster_oven")

# Setup path for source dir
set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(TARGET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../src)

set(TARGET_FILES
${TARGET_DIR}/hsm.c
)

set (DEMO_FILES
${SRC_DIR}/main.c
${SRC_DIR}/toaster_oven.c
)

set (HEADER_FILES
${SRC_DIR}/toaster_oven.h
${TARGET_DIR}/hsm.h
)
SOURCE_GROUP("Src" FILES ${DEMO_FILES} ${TARGET_FILES} ${HEADER_FILES})

include_directories(
${SRC_DIR}
${TARGET_DIR}
)

set(C_VERSION 99)
if ("c_std_11" IN_LIST CMAKE_C_COMPILE_FEATURES)
set(C_VERSION 11)
endif()

set(CMAKE_C_STANDARD ${C_VERSION})
set(CMAKE_C_STANDARD_REQUIRED ON)
message("Your compiler supports : c${C_VERSION}")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")

add_executable(toaster_oven ${DEMO_FILES} ${TARGET_FILES} ${HEADER_FILES})

if ( CMAKE_C_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
target_compile_options( toaster_oven PRIVATE -Wall -Wextra -Wunreachable-code -Wpedantic)
target_compile_options( toaster_oven PRIVATE -Werror )
endif()

# Setup compiler include path
target_include_directories(toaster_oven PRIVATE ${CMAKE_CURRENT_BINARY_DIR})


54 changes: 54 additions & 0 deletions demo/toaster_oven/build/codeblocks/toaster_oven.cbp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="toaster_oven" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin/Debug/toaster_oven" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Debug/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin/Release/toaster_oven" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Release/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-Wall" />
<Add option="-std=c17" />
<Add directory="../../../../src" />
<Add directory="../../build" />
</Compiler>
<Unit filename="../../../../src/hsm.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="../../../../src/hsm.h" />
<Unit filename="../../CMakeLists.txt" />
<Unit filename="../../readme.md" />
<Unit filename="../../src/main.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="../../src/toaster_oven.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="../../src/toaster_oven.h" />
<Extensions>
<lib_finder disable_auto="1" />
</Extensions>
</Project>
</CodeBlocks_project_file>
31 changes: 31 additions & 0 deletions demo/toaster_oven/build/codeblocks/toaster_oven.depend
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# depslib dependency file v1.0
1576508940 source:c:\project\uml-state-machine-in-c\src\hsm.c
<stdint.h>
<stdbool.h>
<stdio.h>
"hsm.h"

1577012586 c:\project\uml-state-machine-in-c\src\hsm.h
"hsm_config.h"

1581235125 c:\project\uml-state-machine-in-c\demo\toaster_oven\build\hsm_config.h

1581258355 source:c:\project\uml-state-machine-in-c\demo\toaster_oven\src\main.c
<stdint.h>
<stdio.h>
<pthread.h>
<unistd.h>
<semaphore.h>
<stdbool.h>
"hsm.h"
"toaster_oven.h"

1581236460 c:\project\uml-state-machine-in-c\demo\toaster_oven\src\toaster_oven.h

1581260451 source:c:\project\uml-state-machine-in-c\demo\toaster_oven\src\toaster_oven.c
<stdint.h>
<stdio.h>
<stdbool.h>
"hsm.h"
"toaster_oven.h"

40 changes: 40 additions & 0 deletions demo/toaster_oven/build/codeblocks/toaster_oven.layout
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_layout_file>
<FileVersion major="1" minor="0" />
<ActiveTarget name="Debug" />
<File name="..\..\CMakeLists.txt" open="1" top="0" tabpos="7" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="847" topLine="6" />
</Cursor>
</File>
<File name="..\..\..\..\src\hsm.c" open="1" top="0" tabpos="6" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="8467" topLine="204" />
</Cursor>
</File>
<File name="..\..\readme.md" open="1" top="0" tabpos="4" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="1767" topLine="15" />
</Cursor>
</File>
<File name="..\..\src\toaster_oven.h" open="1" top="0" tabpos="3" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="1871" topLine="84" />
</Cursor>
</File>
<File name="..\..\..\..\src\hsm.h" open="1" top="0" tabpos="5" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="2067" topLine="50" />
</Cursor>
</File>
<File name="..\..\src\toaster_oven.c" open="1" top="1" tabpos="2" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="5759" topLine="220" />
</Cursor>
</File>
<File name="..\..\src\main.c" open="1" top="0" tabpos="1" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
<Cursor>
<Cursor1 position="2017" topLine="63" />
</Cursor>
</File>
</CodeBlocks_layout_file>
25 changes: 25 additions & 0 deletions demo/toaster_oven/build/hsm_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* \file
* \brief hierarchical state machine
* \author Nandkishor Biradar
* \date 17 December 2018
* Copyright (c) 2018-2019 Nandkishor Biradar
* https://github.com/kiishor
* Distributed under the MIT License, (See accompanying
* file LICENSE or copy at https://mit-license.org/)
*/

#ifndef HSM_CONFIG_H
#define HSM_CONFIG_H

// configuration file for hierarchical state machine.

// Enable the state machine logging
// 0: disable the state machine logger
// 1: enable the state machine logger
#define STATE_MACHINE_LOGGER 1

#endif // HSM_CONFIG_H
1 change: 1 addition & 0 deletions demo/toaster_oven/docs/Toaster_oven.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
151 changes: 151 additions & 0 deletions demo/toaster_oven/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
Toaster Oven state machine
==========================

## State diagram
This example demonstrates a hierarchical state machine of a toaster oven as shown below.

![Toaster oven: Hierarchical state machine](docs/Toaster_oven.svg)

## States
### 1. Door Close state
This is a composite state and contains two substates.
- **Entry action**: Turn off the oven lamp.

#### a. Off state
This state represents that oven is off. Supported events are events are,
- **Start** : This event turn on the heater and heating timer.
It also triggers the state transition to On state.
- **Door_Open** : This event turns off the heater and pauses the heating timer.
It also triggers the state transition to Door open state.

#### b. On state
This state represents oven is on (i.e. heater is on and timer is running).
- **Entry action**: Turn on the heater.
- **Exit action** : Turn off the heater.
- **Stop** : This event turns off the heater and heating timer.
It also triggers the state transition to off state.
- **TimeOut** : This event turns off the heater and triggers the state transition to off state.
- **Door_Open** : This event pauses the triggers the state transition to Door open state.

### 2. Door Open state
This state supports **Door_Close** event.
- **Entry action**: Turn on the oven lamp.
- **Door_Close** : This event triggers the state transition based on the resume timer.
If resume time is greater than zero then it traverses to on state else it to off state.

## Supported events
1. Start
2. Stop
3. Door_Open
4. Door_Close
5. Timeout

## How to use framework

toaster_oven.c and toaster_oven.h files contain an implementation of oven state machine.

### List of supported events

Define the list of supported events as an enumeration in the header file of your state machine `toaster_oven.h`.
event value must be non-zero. Hence initialize the first enum value to 1.
```C
//! List of oven events
typedef enum
{
EN_START = 1,
EN_STOP,
EN_DOOR_OPEN,
EN_DOOR_CLOSE,
EN_TIMEOUT,
}oven_event_t;
```

### State machine

Create a structure derived from the `state_machine_t` that contains all the required variables for your state machine.

```C
//! Oven state machine
typedef struct
{
state_machine_t Machine; //!< Abstract state machine
uint32_t Set_Time; //!< Set time of a oven
uint32_t Resume_Time; //!< Remaining time when the oven is paused
uint32_t Timer; //!< Oven timer
bool Lamp; //!< Oven lamp
bool Heater; //!< Oven heater
}oven_t;
```
Make sure that `state_machine_t` must be the first element in the derived structure.

### state

state_t in the hierarchical state machine contains an extra three members compared to finite state machine.
```C
const state_t* const Parent; //!< Parent state of the current state.
const state_t* const Node; //!< Child states of the current state.
uint32_t Level; //!< Hierarchy level from the top state.
```

This demo uses macro to define the states. This macro make code more readable and maintainable
and also reduces manual hardcoding of data.

```C
#define ADD_ROOT(NAME, HANDLER, ENTRY, EXIT, CHILD)
```
Use this macro to add root state to the state machine. `DOOR_CLOSE_STATE` is defined using this macro.
- This state doesn't contain parent state.
- Level is zero
- It is composite state and contains substates.
---

```C
#define ADD_ROOT_LEAF(NAME, HANDLER, ENTRY, EXIT)
```
- This state doesn't contain parent state. `ADD_ROOT_LEAF` is defined using this macro.
- Level is zero
- It is a simple state and doesn't contains substates.
---

```C
#define ADD_LEAF(NAME, HANDLER, ENTRY, EXIT, PARENT, LEVEL)
```
Use this macro to add leaf state to the composite state. `OFF_STATE` and `ON_STATE` are defined using this macro.
- It is a simple state and doesn't contains substates.
---

```C
#define ALL_OVEN_STATES \
ADD_ROOT_LEAF(DOOR_OPEN_STATE, door_open_handler, door_open_entry_handler, NULL) \
ADD_ROOT(DOOR_CLOSE_STATE, NULL, door_close_entry_handler, NULL, Door_Close_State) \

#define ALL_DOOR_CLOSED_STATES(PARENT, LEVEL) \
ADD_LEAF(OFF_STATE, off_handler, off_entry_handler, NULL, PARENT, LEVEL) \
ADD_LEAF(ON_STATE, on_handler, on_entry_handler, on_exit_handler, PARENT, LEVEL)
```
These macro's are used for creating the enumeration of `oven_state_t` and `door_close_state_t`.
Also to initialize the state_t structures `Oven_State` and `Door_Close_State`.
each state has function pointers to its event handler, entry and exit actions.
The event handler must be initialized for each state.
the entry and exit actions are optional, if not required then can be initialized as NULL.
### event handler, entry and exit actions
Define event handler, entry and exit action's based on following function signature
```C
typedef state_machine_result_t (*state_handler) (state_machine_t* const state);
```

If handler supports the passed event, then it consumes the event and returns the status as `EVENT_HANDLED`.

Use `switch_state` to switch state when the source state and target state have same parent state.
e.g. switch from `OFF_STATE` to `ON_STATE` and vice versa.

Use `traverse_state` for traversing from source state to target state when both have different parent state.
e.g. switch from `DOOR_OPEN_STATE` to `OFF_STATE` / `ON_STATE` and vice versa.

If handler doesn't support the passed event then return result as `EVENT_UN_HANDLED`.
If the handler consumes the event and generates the new event then return the status as `TRIGGERED_TO_SELF`.

0 comments on commit 740154c

Please sign in to comment.