├── LICENSE.txt ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── docs ├── Debug.md ├── HSM.md ├── Queue.md ├── Test.md ├── mbb.png └── mbb.svg ├── examples ├── Makefile.am ├── debugging.c ├── keyboard.inc ├── monostable.c ├── pelican.c └── periodic.inc ├── mbb ├── Makefile.am ├── debug.c ├── debug.h ├── hsm.c ├── hsm.h ├── queue.h ├── test.h ├── timer_common.h ├── timer_ev.c ├── timer_ev.h ├── timer_periodic.c ├── timer_periodic.h └── types.h ├── tests ├── Makefile.am ├── test_hsm.c └── test_queue.c └── tools ├── Makefile.am ├── mhsm_scaffold └── munt_main /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jan Weil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = mbb examples tools 2 | if HAVE_RUBY 3 | SUBDIRS += tests 4 | endif 5 | nobase_include_HEADERS = mbb/debug.h mbb/hsm.h mbb/queue.h mbb/test.h mbb/timer_common.h mbb/timer_periodic.h mbb/types.h 6 | if HAVE_LIBEV 7 | nobase_include_HEADERS += mbb/timer_ev.h 8 | endif 9 | nobase_doc_DATA = README.md docs/Debug.md docs/HSM.md docs/Queue.md docs/Test.md docs/mbb.png examples/debugging.c examples/monostable.c examples/pelican.c tests/test_hsm.c tests/test_queue.c 10 | EXTRA_DIST = README.md LICENSE.txt docs examples/keyboard.inc examples/periodic.inc tests/test_hsm.c tests/test_queue.c 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | libmbb - Embedded Building Bricks 4 | ================================= 5 | 6 | Synopsis 7 | -------- 8 | 9 | *libmbb* is a [MIT-licensed](LICENSE.txt) C library targeted at embedded 10 | systems. 11 | 12 | The upstream repository is at . 13 | 14 | Features 15 | -------- 16 | 17 | * [Hierarchical state machines (HSMs)](docs/HSM.md), including timers 18 | * [Fixed-cacpacity queues](docs/Queue.md) 19 | * [Debugging macros](docs/Debug.md) 20 | * [Unit tests](docs/Test.md) 21 | 22 | *libmbb* does not allocate memory dynamically. It is up to the developer to 23 | decide how memory is allocated. The HSM module supports both purely 24 | event-driven and non-blocking, real-time suitable processing. 25 | 26 | Tools 27 | ----- 28 | 29 | The tools sub directory contains the following command line tools: 30 | 31 | * `mhsm_scaffold` adds event processing function stubs to source files 32 | * `munt_main` generates main functions for unit tests 33 | 34 | Building 35 | -------- 36 | 37 | *libmbb* uses the autotools for building. If you clone its upstream repository 38 | you will have to call `./autogen.sh` to build the `configure` script. 39 | `autogen.sh` just calls `autoreconf` which depends on `autoconf` and `automake` 40 | being installed. If the `configure` script is built it is the usual game of 41 | 42 | ./configure 43 | make 44 | make install 45 | 46 | The `install` target will install the examples and unit tests along with libmbb 47 | itself. These programs have rather unspecific names like `test_hsm`. Calling 48 | `./configure --program-prefix=mbb_` will install them as `mbb_test_hsm` 49 | instead. Alternatively, you might specify `./configure --prefix=/opt/mbb` to 50 | install everything into `/opt/mbb`. 51 | 52 | Call `make check` to run the unit tests. 53 | 54 | Call `./configure --host=arm-linux` to cross-compile for arm-linux. 55 | 56 | Call `./configure --help` for a general help message. 57 | 58 | Dependencies 59 | ------------ 60 | 61 | * The [libev](http://software.schmorp.de/pkg/libev.html) timers backend and the 62 | examples using it are only compiled if `libev` and its header files are 63 | installed on your system. 64 | * The tools are written in and thus depend on 65 | [Ruby](https://www.ruby-lang.org/). 66 | 67 | Examples 68 | -------- 69 | 70 | * [debugging](examples/debugging.c): debugging macros 71 | * [monostable](examples/monostable.c): multiple HSM instances, libev timers 72 | * [pelican](examples/pelican.c): [Miro Samek's](http://www.state-machine.com/) 73 | [PEdestrian LIght CONtrolled (PELICAN) Crossing 74 | Example](http://www.state-machine.com/resources/AN_PELICAN.pdf), periodic 75 | timers 76 | 77 | Note that the terminal interfaces of some of the examples (`pelican` and 78 | `monostable`) will be interfered with by the `stderr` output of the debugging 79 | macros. You can either add `-DNDEBUG` to `CPPFLAGS` to disable these debugging 80 | macros or redirect `stderr` like this: 81 | 82 | examples/pelican 2> log 83 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | autoreconf --install || exit 1 2 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(Embedded Building Bricks, 0.1, jan.weil@web.de, libmbb) 2 | AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 3 | AC_PROG_CC 4 | AM_PROG_AR 5 | AC_PROG_RANLIB 6 | AC_CHECK_PROG(have_ruby, ruby, yes, no) 7 | if test x$have_ruby != xyes; then 8 | AC_MSG_WARN([Ruby was not found on your system, unit tests will not be compiled.]) 9 | fi 10 | AC_CHECK_LIB(ev, ev_version_major, [have_ev=yes], [have_ev=no]) 11 | AC_HEADER_STDC 12 | AC_HEADER_STDBOOL 13 | AC_SYS_POSIX_TERMIOS 14 | if test x$ac_cv_header_stdbool_h != xyes -o x$ac_cv_header_stdc != xyes; then 15 | AC_MSG_WARN([Your system lacks one of the standard types header.]) 16 | AC_MSG_WARN([Please define MBB_SYSTEM_TYPES_HEADER to make standard types available.]) 17 | AC_MSG_WARN([Have a look at mbb/types.h.]) 18 | fi 19 | AM_CONDITIONAL([HAVE_TERMIOSH], [test x$ac_cv_sys_posix_termios = xyes]) 20 | AM_CONDITIONAL([HAVE_LIBEV], [test x$have_ev = xyes]) 21 | AM_CONDITIONAL([HAVE_RUBY], [test x$have_ruby = xyes]) 22 | AC_CONFIG_FILES([ 23 | Makefile 24 | mbb/Makefile 25 | examples/Makefile 26 | tests/Makefile 27 | tools/Makefile 28 | ]) 29 | AC_OUTPUT 30 | -------------------------------------------------------------------------------- /docs/Debug.md: -------------------------------------------------------------------------------- 1 | libmbb - Debugging Macros 2 | ========================= 3 | 4 | [*libmbb*](..) features a set of debugging macros defined in `mbb/debug.h`. 5 | 6 | #include "mbb/debug.h" 7 | 8 | All debugging macros can be disabled by defining the macro NDEBUG before including 9 | `mbb/debug.h`. 10 | 11 | Have a look at [the example](../examples/debugging.c). 12 | 13 | Print a string adding a newline 14 | ------------------------------- 15 | 16 | MDBG_PRINT_LN("hello, world!"); 17 | 18 | `2015-02-12T12:36:41.000 (examples/debugging.c, 11): hello, world!` 19 | 20 | Print an integer (decimal) 21 | -------------------------- 22 | 23 | int i = 128; 24 | MDBG_PRINT_I(i); 25 | 26 | `2015-02-12T12:36:41.000 (examples/debugging.c, 13): i = 128` 27 | 28 | Print an integer (octal) 29 | -------------------------- 30 | 31 | int i = 128; 32 | MDBG_PRINT_O(i); 33 | 34 | `2015-02-12T12:36:41.000 (examples/debugging.c, 14): i = O200` 35 | 36 | Print an integer (hexadecimal) 37 | -------------------------- 38 | 39 | int i = 128; 40 | MDBG_PRINT_X(i); 41 | 42 | `2015-02-12T12:36:41.000 (examples/debugging.c, 15): i = 0x80` 43 | 44 | Print a character 45 | ----------------- 46 | 47 | char a = 'b'; 48 | MDBG_PRINT_C(a); 49 | 50 | `2015-02-12T12:36:41.000 (examples/debugging.c, 17): a = 'b'` 51 | 52 | Print a float value 53 | ------------------- 54 | 55 | double pi = 3.14; 56 | MDBG_PRINT_F(pi); 57 | 58 | `2015-02-12T12:36:41.000 (examples/debugging.c, 19): pi = 3.140000` 59 | 60 | Print a string 61 | -------------- 62 | 63 | char foo[] = "bar"; 64 | MDBG_PRINT_S(foo); 65 | 66 | `2015-02-12T12:44:06.000 (examples/debugging.c, 23): foo: "bar"` 67 | 68 | Print a memory region 69 | --------------------- 70 | 71 | uint8_t data[] = { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; 72 | MDBG_PRINT_MEM(data, sizeof(data)); 73 | 74 | `2015-02-12T12:36:41.000 (examples/debugging.c, 21): data(0xbf916bd9, 6): 0A 0B 0C 0D 0E 0F ` 75 | 76 | Generic printing macros 77 | ----------------------- 78 | 79 | There are generic `printf`-like macros depending on the number of arguments. 80 | This is because only few preprocessors feature support for variadic macros. 81 | 82 | MDBG_PRINT0(FORMAT) 83 | MDBG_PRINT1(FORMAT, ARG) 84 | MDBG_PRINT2(FORMAT, ARG1, ARG2) 85 | ... 86 | MDBG_PRINT10(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10) 87 | 88 | Example: 89 | 90 | int answer = 42; 91 | MDBG_PRINT1("the answer is %d\n", answer); 92 | 93 | `2015-02-12T12:44:06.000 (examples/debugging.c, 27): the answer is 42` 94 | 95 | Print errno 96 | ----------- 97 | 98 | read(55, NULL, 0); 99 | MDBG_PRINT_ERRNO("read"); 100 | 101 | `2015-02-12T13:13:41.000 (examples/debugging.c, 52): read: Bad file descriptor` 102 | 103 | Assertions 104 | ---------- 105 | 106 | int answer = 42; 107 | MDBG_ASSERT(answer == 41); 108 | 109 | `2015-02-12T13:10:33.000 (examples/debugging.c, 54): assertion failed: answer == 41` 110 | 111 | debugging: examples/debugging.c:54: main: Assertion `answer == 41' failed. 112 | Aborted 113 | 114 | Assert a line of code is never reached 115 | -------------------------------------- 116 | 117 | MDBG_NEVER_REACHED(); 118 | 119 | `2015-02-12T13:13:41.000 (examples/debugging.c, 56): assertion failed: !"reached"` 120 | 121 | debugging: examples/debugging.c:56: main: Assertion `!"reached"' failed. 122 | Aborted 123 | -------------------------------------------------------------------------------- /docs/HSM.md: -------------------------------------------------------------------------------- 1 | libmbb - Hierarchical State Machines 2 | ==================================== 3 | 4 | [*libmbb*](..) features a lightweight module for hierarchical state machines 5 | (HSMs) modeled after the 6 | [UML state machine](http://en.wikipedia.org/wiki/UML_state_machine). 7 | 8 | HSM types, macros, and functions prototypes are defined in `mbb/hsm.h`. 9 | 10 | #include "mbb/hsm.h" 11 | 12 | Features 13 | -------- 14 | 15 | * Composite states 16 | * Run-to-completion processing 17 | * Greedy transition selection 18 | * Deferred events 19 | * DO event 20 | * Timers with system-specific backends 21 | 22 | Synopsis 23 | -------- 24 | 25 | * Define custom events. 26 | * Define the state hierarchy of your HSM. 27 | * Optionally, use [`mhsm_scaffold`](../tools/mhsm_scaffold) to generate stubs 28 | of the event processing functions. 29 | * Define the event processing functions. 30 | * Create as many instances of your HSM as you need along with their contexts. 31 | * Initialise your HSM instances and their contexts. 32 | * Dispatch the `MHSM_EVENT_INITIAL` event to trigger the initial transition. 33 | * Dispatch events as they occur. 34 | 35 | Example 36 | ------- 37 | 38 | #include "mbb/hsm.h" 39 | #include 40 | 41 | enum { 42 | EVENT1 = MHSM_EVENT_CUSTOM, 43 | EVENT2 44 | }; 45 | 46 | MHSM_DEFINE_STATE(top, NULL); 47 | MHSM_DEFINE_STATE(a, &top); 48 | MHSM_DEFINE_STATE(b, &top); 49 | 50 | mhsm_state_t *top_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 51 | { 52 | switch (event.id) { 53 | case EVENT1: 54 | printf("event1\n"); 55 | break; 56 | case EVENT2: 57 | printf("event2\n"); 58 | break; 59 | } 60 | 61 | return ⊤ 62 | } 63 | 64 | mhsm_state_t *a_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 65 | { 66 | switch (event.id) { 67 | case EVENT1: 68 | return &b; 69 | } 70 | 71 | return &a; 72 | } 73 | 74 | mhsm_state_t *b_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 75 | { 76 | switch (event.id) { 77 | case EVENT1: 78 | return &a; 79 | } 80 | 81 | return &b; 82 | } 83 | 84 | int main(void) 85 | { 86 | mhsm_hsm_t hsm; 87 | 88 | mhsm_initialise(&hsm, NULL, &a); 89 | mhsm_dispatch_event(&hsm, MHSM_EVENT_INITIAL); 90 | mhsm_dispatch_event(&hsm, EVENT1); 91 | mhsm_dispatch_event(&hsm, EVENT1); 92 | mhsm_dispatch_event(&hsm, EVENT2); 93 | 94 | return 0; 95 | } 96 | 97 | * [monostable](../examples/monostable.c) is a simple example implementing 98 | monostable multivibrators (as in staircase lightings). 99 | * [pelican](../examples/pelican.c) is an implementation of 100 | [Miro Samek's](http://www.state-machine.com/) 101 | [PEdestrian LIght CONtrolled (PELICAN) Crossing 102 | Example](http://www.state-machine.com/resources/AN_PELICAN.pdf). 103 | 104 | Events 105 | ------ 106 | 107 | typedef struct { 108 | uint32_t id; 109 | int32_t arg; 110 | } mhsm_event_t; 111 | 112 | `mhsm_event_t` represents an event which is identified by its `uint32_t id` and 113 | features an optional `int32_t` argument `arg`. 114 | 115 | There are five pre-defined event ids: 116 | 117 | enum { 118 | MHSM_EVENT_ENTRY, 119 | MHSM_EVENT_INITIAL, 120 | MHSM_EVENT_DO, 121 | MHSM_EVENT_EXIT, 122 | MHSM_EVENT_CUSTOM 123 | }; 124 | 125 | `MHSM_EVENT_ENTRY`, `MHSM_EVENT_INITIAL`, and `MHSM_EVENT_EXIT` are dispatched 126 | automatically if event processing functions trigger a transition. 127 | 128 | The `MHSM_EVENT_DO` event is meant to be dispatched periodically in 129 | non-blocking real-time systems. This can be used to implement concurrency of 130 | multiple tasks if the underlying operating system lacks support for 131 | concurrency. 132 | 133 | Custom events should be defined as follows: 134 | 135 | enum { 136 | MY_EVENT_1 = MHSM_EVENT_CUSTOM, 137 | MY_EVENT_2, 138 | ... 139 | }; 140 | 141 | ### Event Arguments 142 | 143 | Typical examples for event arguments include return codes, error codes, and 144 | indices. If an event is associated with an argument which cannot be represented 145 | by an `int32_t` value you will have to reserve space in the HSM's context 146 | structure (see below). 147 | 148 | States and Event Processing Functions 149 | ------------------------------------- 150 | 151 | typedef struct mhsm_state_s mhsm_state_t; 152 | 153 | `mhsm_state_t` is an anonymous structure representing an HSM state. It is 154 | basically a pointer to an event processing function along with a pointer to its 155 | superstate. 156 | 157 | typedef mhsm_state_t *mhsm_event_processing_fun_t(mhsm_hsm_t *hsm, mhsm_event_t event); 158 | 159 | An event processing function takes a pointer to the HSM and an event and 160 | returns a pointer to the next state. The HSM pointer is needed to retrieve the 161 | HSM's context and in case another event is to be dispatched. 162 | 163 | ### State Transitions 164 | 165 | If the pointer returned by the event processing function differs from the 166 | current state of the HSM a transition is triggered. If more than one of the 167 | active states trigger a transition the most inner state's transition is 168 | performed (UML's greedy transition selection). 169 | 170 | __________________________________________________ 171 | | LCA | 172 | | __________________ _______________________ | 173 | | | STATE_A | | ___________________ | | 174 | | | | | | STATE_B | | | 175 | | | | | | | | | 176 | | | | | | | | | 177 | | ------------------ | ------------------- | | 178 | | ----------------------- | 179 | -------------------------------------------------- 180 | 181 | 182 | A transition from `STATE_A` to `STATE_B` consists of the following steps: 183 | 184 | * Find the least comon ancestor `LCA` of `STATE_A` and `STATE_B` 185 | * Dispatch `MHSM_EVENT_EXIT` events starting at `STATE_A` up to but not 186 | including `LCA` 187 | * Dispatch `MHSM_EVENT_ENTRY` events down to `STATE_B` 188 | * Dispatch the `MHSM_EVENT_INITIAL` event to `STATE_B` 189 | 190 | A composite state's event processing function can catch the 191 | `MHSM_EVENT_INITIAL` event to trigger the initial transition to one of its 192 | substates. 193 | 194 | ### Deferring Events 195 | 196 | If a state cannot process a certain event its event processing function can 197 | defer the event by returning `NULL`. Deferred events are enqueued in an 198 | HSM-specific queue. This queue's capacity is `MHSM_EVENT_QUEUE_LENGTH`, which 199 | is 5 by default. Whether this is enough depends on the processing logic. If a 200 | longer queue is needed `MHSM_EVENT_QUEUE_LENGTH` can be pre-defined before 201 | including `mbb/hsm.h`. 202 | 203 | Deferring an event also prevents any potential transitions triggered in super 204 | states. 205 | 206 | ### Transitions Triggered by `MHSM_EVENT_ENTRY` and `MHSM_EVENT_EXIT` events 207 | 208 | To ensure run-to-completion processing `MHSM_EVENT_ENTRY` and `MHSM_EVENT_EXIT` 209 | events should usually not be allowed to trigger transitions. However, 210 | *libmbb*'s HSM module allows such transitions which may be useful under certain 211 | conditions. 212 | 213 | For example, a `MHSM_EVENT_EXIT` event may trigger an action which is supposed 214 | to write some data to non-volatile memory. If this action fails it may be 215 | necessary to interrupt the current transition and trigger a transition to a 216 | fatal error state instead. 217 | 218 | Use this feature with care as it can easily lead to infinite loops if abused. 219 | 220 | ### Defining States and Event Processing Functions 221 | 222 | MHSM_DEFINE_STATE(STATE, SUPERSTATE); 223 | 224 | The event processing function for `STATE` is called `STATE_fun` by convention. 225 | The macro `MHSM_DEFINE_STATE` is used to declare `STATE_fun`, define `STATE`, 226 | and assign `STATE_fun` and `SUPERSTATE` to `STATE`'s internal pointers. 227 | 228 | `SUPERSTATE` is a pointer to `STATE`'s superstate. For states without a 229 | superstate `SUPERSTATE` must be `NULL`. 230 | 231 | After defining the state hierarchy using `MHSM_DEFINE_STATE` the tool 232 | `mhsm_scaffold` can be used to append event processing function stubs to the 233 | source file: 234 | 235 | tools/mhsm_scaffold myhsm.c 236 | 237 | The tool is rather primitive. It records all appearances of the 238 | `MHSM_DEFINE_STATE` macro and appends an event processing function stub for 239 | each of the states to the end of the file, regardless of any other existing 240 | source code. 241 | 242 | ### Extended State Variables 243 | 244 | Since event processing functions can be called by multiple HSM instances they 245 | should not contain any static variables. If the application logic depends on 246 | extended state variables these should be part of the HSM's context. 247 | 248 | An HSM's context is typically represented by an HSM-specific context structure: 249 | 250 | typedef struct { 251 | ... 252 | } hsm_context_t; 253 | 254 | A pointer to such a context structure is given to the HSM during initialisation 255 | (see below) and can be retrieved by event processing functions using the 256 | function `mhsm_context`: 257 | 258 | void *mhsm_context(mhsm_hsm_t *hsm); 259 | 260 | The `mhsm_scaffold` tool adds a variable pointing to the context structure to 261 | the event processing function stubs if you add the `--context-type` option. 262 | 263 | The generated event processing function stub including a context pointer looks 264 | like this: 265 | 266 | mhsm_state_t *STATE_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 267 | { 268 | hsm_context_t *ctx = (hsm_context_t*) mhsm_context(hsm); 269 | 270 | switch (event.id) { 271 | case MHSM_EVENT_ENTRY: 272 | break; 273 | } 274 | 275 | return &STATE; 276 | } 277 | 278 | ### Auxiliary Functions 279 | 280 | The function `mhsm_is_ancestor` returns true if `ancestor` is an ancestor of 281 | `target`: 282 | 283 | bool mhsm_is_ancestor(mhsm_state_t *ancestor, mhsm_state_t *target); 284 | 285 | HSMs 286 | ---- 287 | 288 | typedef struct mhsm_hsm_s mhsm_hsm_t; 289 | 290 | `mhsm_hsm_t` is an anonymous structure representing an HSM. It stores the 291 | pointer of the HSM's current state, a pointer the HSM's context, and the queue 292 | of deferred events. 293 | 294 | void mhsm_initialise(mhsm_hsm_t *hsm, void *context, mhsm_state_t *initial_state); 295 | 296 | The function `mhsm_initialise` must be called to initialse an HSM. In addition 297 | to a pointer to the HSM, it takes a pointer to HSM-specific context data and a 298 | pointer to the HSM's initial state. 299 | 300 | ### Dispatching Events 301 | 302 | After initialising the HSM events are dispatched using the functions 303 | `mhsm_dispatch_event` and `mhsm_dispatch_event_arg`: 304 | 305 | void mhsm_dispatch_event(mhsm_hsm_t *hsm, uint32_t id); 306 | void mhsm_dispatch_event_arg(mhsm_hsm_t *hsm, uint32_t id, int32_t arg); 307 | 308 | To trigger the initial transition after initialising an HSM you must dispatch 309 | the `MHSM_EVENT_INITIAL` event: 310 | 311 | mhsm_dispatch_event(hsm, MHSM_EVENT_INITIAL); 312 | 313 | ### Recursive Calls of Dispatch Functions and Enqueued Events 314 | 315 | The dispatch functions may be called from within an event processing function. 316 | To assure run-to-completion processing the event is enqueued in the HSM's 317 | internal queue in this case and dispatched after the processing function has 318 | returned. 319 | 320 | Each call of one of the dispatch functions will also dispatch all enqueued 321 | events once after dispatching the given event. 322 | 323 | ### Auxiliary Functions 324 | 325 | A pointer to the HSM's most inner active state can be retrieved using the 326 | `mhsm_current_state` function: 327 | 328 | mhsm_state_t *mhsm_current_state(mhsm_hsm_t *hsm); 329 | 330 | The function `mhsm_is_in` returns true if `state` is an active state (including 331 | composite states): 332 | 333 | bool mhsm_is_in(mhsm_hsm_t *hsm, mhsm_state_t *state); 334 | 335 | Timers 336 | ------ 337 | 338 | Since timers are of substantial importance in embedded computing *libmbb*'s HSM 339 | module features a simple yet powerful interface. 340 | 341 | int mhsm_start_timer(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs); 342 | 343 | An event processing function may call `mhsm_start_timer` to ask its HSM to 344 | dispatch `event_id` after `period_msecs` ms have passed. 345 | 346 | Of course, timers are highly system specific which is why you have to choose an 347 | appropriate backend. 348 | 349 | *All these backends rely on a convention:* 350 | 351 | The first element of the context structure of the HSM given to 352 | `mhsm_start_timer` must be an array of timer structures, one structure per 353 | `event_id`. Additionally, they assume that custom *timer* events are defined 354 | first, i.e., the first timer event corresponds to `MHSM_EVENT_CUSTOM`. The 355 | `MTMR_NROF_TIMERS` macro returns the number of timers given the last timer 356 | event id, provided the assumption is fulfilled. 357 | 358 | ### Example 359 | 360 | enum { 361 | MY_TIMER_EVENT_A = MHSM_EVENT_CUSTOM, 362 | MY_TIMER_EVENT_B, 363 | MY_TIMER_EVENT_C, 364 | MY_OTHER_EVENT_A 365 | ... 366 | }; 367 | 368 | typedef struct { 369 | mtmr_prd_t timers[MTMR_NROF_TIMERS(MY_TIMER_EVENT_C)]; 370 | ... 371 | } hsm_context_t; 372 | 373 | In this example the timers array contains three elements: `timers[0]` 374 | corresponds to `MY_TIMER_EVENT_A`, `timers[1]` to `MY_TIMER_EVENT_B` and 375 | `timers[2]` to `MY_TIMER_EVENT_C`. 376 | 377 | When an event processing function requests a timer the index of the timer 378 | structure is computed based on this convention. Using any of the existing timer 379 | backends without adhering to this convention will cause memory corruption. 380 | 381 | ### `mtmr_prd_t` for Non-Blocking Real-Time Systems 382 | 383 | #include "mbb/timer_periodic.h" 384 | 385 | The timer structure is `mtmr_prd_t`. The array of timers must be initialised 386 | calling 387 | 388 | mtmr_prd_initialise_timers(hsm, MTMR_NROF_TIMERS(MY_TIMER_EVENT_C)); 389 | 390 | after the HSM has been intialised. 391 | 392 | int mtmr_prd_increment_timers(mhsm_hsm_t *hsm, size_t nrof_timers, uint32_t passed_msecs); 393 | 394 | The function `mtmr_prd_increment_timers` must be called periodically indicating 395 | how much time has passed. This is usually done along with dispatching the 396 | `MHSM_EVENT_DO` event. 397 | 398 | [pelican](../examples/pelican.c) is an example using this timer backend. 399 | 400 | ### `mtmr_ev_t` based on `libev` 401 | 402 | #include "mbb/timer_ev.h" 403 | 404 | [libev](http://software.schmorp.de/pkg/libev.html) is a full-featured and 405 | high-performance event loop featuring, amongst others, relative timers. 406 | 407 | The timer structure is `mtmr_ev_t`. The array of timers must be initialised calling 408 | 409 | mtmr_ev_initalise_timers(hsm, MTMR_NROF_TIMERS(MY_TIMER_EVENT_C), ev_loop); 410 | 411 | after the HSM has been initialised. 412 | 413 | [monostable](../examples/monostable.c) is an example using this timer backend. 414 | 415 | ### System-specific Timer Backends 416 | 417 | To implement a system-specific timer backend you will at least have to call 418 | 419 | void mhsm_set_timer_callback(int (*callback)(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs)); 420 | 421 | after the HSM has been intialised. The callback will be called whenever an 422 | event processing function calls `mhsm_start_timer`. 423 | 424 | The existing backends set this callback in their specific initialisation 425 | function. 426 | -------------------------------------------------------------------------------- /docs/Queue.md: -------------------------------------------------------------------------------- 1 | libmbb - Fixed-capacity Queues 2 | ============================== 3 | 4 | [*libmbb*](..) features a simple set of macros to implement type-safe 5 | fixed-capacity queues. 6 | 7 | The macros are defined in `mbb/queue.h`. 8 | 9 | #include "mbb/queue.h" 10 | 11 | Example 12 | ------- 13 | 14 | { 15 | MQUE_DEFINE_STRUCT(int, 5) event_queue; 16 | 17 | MQUE_INITIALISE(&event_queue); 18 | 19 | MQUE_ENQUEUE(&event_queue, 4); 20 | MQUE_ENQUEUE(&event_queue, 3); 21 | MQUE_ENQUEUE(&event_queue, 2); 22 | 23 | while (MQUE_LENGTH(&event_queue)) { 24 | printf("event: %d\n", MQUE_HEAD(&event_queue)); 25 | MQUE_DEQUEUE(&event_queue); 26 | } 27 | } 28 | 29 | Output: 30 | 31 | event: 4 32 | event: 3 33 | event: 2 34 | 35 | Macros 36 | ------ 37 | 38 | For the following macros `Q` refers to a pointer to a queue structure defined 39 | by `MQUE_DEFINE_STRUCT`. Note that these macros cannot be used on pointers 40 | passed to functions, the queue structure must be 'in scope'. 41 | 42 | MQUE_DEFINE_STRUCT(TYPE, CAPACITY) [ = MQUE_INITIALISER]; 43 | 44 | Defines a queue of CAPACITY TYPEs. You can assign `MQUE_INITIALISER` to 45 | initialise the queue without calling `MQUE_INITIALISE`. 46 | 47 | MQUE_INITIALISE(Q); 48 | 49 | Initialise a queue. 50 | 51 | MQUE_CAPACITY(Q) 52 | 53 | Returns the queue's capacity. 54 | 55 | MQUE_LENGTH(Q) 56 | 57 | Returns the queue's length. 58 | 59 | MQUE_IS_FULL(Q) 60 | 61 | Returns true if the queue is full, false otherwise. 62 | 63 | MQUE_IS_EMPTY(Q) 64 | 65 | Returns true if the queue is empty, false otherwise. 66 | 67 | MQUE_ENQUEUE(Q, ELEMENT); 68 | 69 | Enqueue an element. `ELEMENT` is assigned to the enqueued element. Always check 70 | whether the queue is full before calling `MQUE_ENQUEUE`. It will do nothing if 71 | the queue is full. 72 | 73 | MQUE_HEAD(Q) 74 | 75 | Returns the queue's head, which is of type `TYPE`. 76 | 77 | MQUE_DEQUEUE(Q); 78 | 79 | Dequeue the queue's head. `MQUE_DEQUEUE` will do nothing if the queue is empty. 80 | 81 | This is *not* a function returning the head. Use `MQUE_HEAD` instead. Refer to 82 | the example above. 83 | 84 | -------------------------------------------------------------------------------- /docs/Test.md: -------------------------------------------------------------------------------- 1 | libmbb - Unit Tests 2 | =================== 3 | 4 | [*libmbb*](..) features a simple unit test framework inspired by 5 | [MinUnit](http://www.jera.com/techinfo/jtns/jtn002.html). 6 | 7 | Writing unit tests 8 | ------------------ 9 | 10 | A test suite is a single C file containing any number of test cases. It must 11 | 12 | #include "mbb/test.h" 13 | 14 | A test case looks like this: 15 | 16 | #include "mbb/queue.h" 17 | #include "mbb/debug.h" 18 | 19 | char *test_enqueue_dequeue() 20 | { 21 | int i; 22 | 23 | MQUE_DEFINE_STRUCT(int, 5) queue; 24 | 25 | MQUE_INITIALISE(&queue); 26 | 27 | MUNT_ASSERT(MQUE_IS_EMPTY(&queue)); 28 | 29 | for (i = 1; i <= 5; i++) { 30 | MUNT_ASSERT(!MQUE_IS_FULL(&queue)); 31 | MQUE_ENQUEUE(&queue, i); 32 | } 33 | 34 | MUNT_ASSERT(MQUE_IS_FULL(&queue)); 35 | 36 | for (i = 1; i <= 5; i++) { 37 | int head; 38 | 39 | MUNT_ASSERT(!MQUE_IS_EMPTY(&queue)); 40 | 41 | head = MQUE_HEAD(&queue); 42 | MUNT_ASSERT(head == i); 43 | 44 | MQUE_DEQUEUE(&queue); 45 | } 46 | 47 | MUNT_ASSERT(MQUE_IS_EMPTY(&queue)); 48 | 49 | return 0; 50 | } 51 | 52 | The test case function must return a string and its name must begin with 53 | `test_`. It must return 0 if no error occured. The `MUNT_ASSERT(EXPRESSION)` 54 | macro is used for the actual test assertions. It makes the test case function 55 | return a string if `EXPRESSION` is false. 56 | 57 | Have a look at *libmbb*'s own [tests](../tests/) as an example. 58 | 59 | Compiling unit tests 60 | -------------------- 61 | 62 | The `munt_main` tool is used to generate a main function for a test suite, 63 | printing it to `stdout`. Calling 64 | 65 | munt_main test_suite.c > test_suite_main.c 66 | 67 | pipes the automatically generated main function to `test_suite_main.c`. 68 | 69 | This source code can be compiled to an executable which exits returning 70 | `EXIT_SUCCESS` or `EXIT_FAILURE` depending on whether the test suite failed. 71 | 72 | Running unit tests 73 | ------------------ 74 | 75 | The output of the automatically generated test program looks like this: 76 | 77 | tests/test_queue 78 | 79 | Test suite 'test_queue': 80 | 81 | ### test_enqueue_dequeue ### 82 | Assertion passed (013): MQUE_IS_EMPTY(&queue) 83 | Assertion passed (016): !MQUE_IS_FULL(&queue) 84 | Assertion passed (016): !MQUE_IS_FULL(&queue) 85 | Assertion passed (016): !MQUE_IS_FULL(&queue) 86 | Assertion passed (016): !MQUE_IS_FULL(&queue) 87 | Assertion passed (016): !MQUE_IS_FULL(&queue) 88 | Assertion passed (020): MQUE_IS_FULL(&queue) 89 | Assertion passed (025): !MQUE_IS_EMPTY(&queue) 90 | Assertion passed (028): head == i 91 | Assertion passed (025): !MQUE_IS_EMPTY(&queue) 92 | Assertion passed (028): head == i 93 | Assertion passed (025): !MQUE_IS_EMPTY(&queue) 94 | Assertion passed (028): head == i 95 | Assertion passed (025): !MQUE_IS_EMPTY(&queue) 96 | Assertion passed (028): head == i 97 | Assertion passed (025): !MQUE_IS_EMPTY(&queue) 98 | Assertion passed (028): head == i 99 | Assertion passed (033): MQUE_IS_EMPTY(&queue) 100 | ### passed ### 101 | 102 | Test suite 'test_queue' passed (1 test run) 103 | -------------------------------------------------------------------------------- /docs/mbb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawebada/libmbb/6dd52d73a8e3393004f0a44b832d81d72b3f16f0/docs/mbb.png -------------------------------------------------------------------------------- /docs/mbb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 46 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 77 | 84 | 87 | 97 | 107 | 117 | 118 | 119 | 123 | 130 | 133 | 143 | 153 | 154 | 155 | 159 | 166 | 169 | 179 | 189 | 190 | 191 | 195 | 202 | 205 | 215 | 225 | 226 | 227 | 231 | 238 | 241 | 251 | 261 | 262 | 263 | 267 | 274 | 277 | 287 | 297 | 298 | 299 | 303 | 310 | 313 | 323 | 333 | 334 | 335 | 339 | 346 | 349 | 359 | 369 | 379 | 380 | 381 | 385 | 392 | 395 | 405 | 415 | 425 | 426 | 427 | 428 | 429 | -------------------------------------------------------------------------------- /examples/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = debugging 2 | if HAVE_TERMIOSH 3 | bin_PROGRAMS += pelican 4 | if HAVE_LIBEV 5 | bin_PROGRAMS += monostable 6 | endif 7 | endif 8 | AM_CPPFLAGS = -I$(top_srcdir) 9 | LDADD = $(top_builddir)/mbb/libmbb.a 10 | monostable_LDADD = -lev $(LDADD) 11 | -------------------------------------------------------------------------------- /examples/debugging.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "mbb/debug.h" 22 | #include "mbb/types.h" 23 | #include 24 | 25 | int main(void) 26 | { 27 | int i = 128; 28 | double pi = 3.14; 29 | char a = 'b'; 30 | char foo[] = "bar"; 31 | uint8_t data[] = { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; 32 | int answer = 42; 33 | ssize_t ret; 34 | 35 | MDBG_PRINT_LN("hello, world!"); 36 | 37 | MDBG_PRINT_I(i); 38 | MDBG_PRINT_O(i); 39 | MDBG_PRINT_X(i); 40 | 41 | MDBG_PRINT_C(a); 42 | 43 | MDBG_PRINT_F(pi); 44 | 45 | MDBG_PRINT_S(foo); 46 | 47 | MDBG_PRINT_MEM(data, sizeof(data)); 48 | 49 | MDBG_PRINT1("the answer is %d\n", answer); 50 | 51 | /* expected to fail */ 52 | ret = read(55, NULL, 0); 53 | MDBG_ASSERT(ret == -1); 54 | MDBG_PRINT_ERRNO("read"); 55 | 56 | MDBG_ASSERT(answer == 42); 57 | 58 | /* this will abort the program */ 59 | MDBG_NEVER_REACHED(); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /examples/keyboard.inc: -------------------------------------------------------------------------------- 1 | /* vim: set filetype=c: */ 2 | /* * Copyright (C) 2015 Jan Weil 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | static void nonblock(int state) 28 | { 29 | struct termios ttystate; 30 | 31 | tcgetattr(STDIN_FILENO, &ttystate); 32 | 33 | if (state) { 34 | ttystate.c_lflag &= ~ICANON; 35 | ttystate.c_cc[VMIN] = 1; 36 | } else { 37 | ttystate.c_lflag |= ICANON; 38 | } 39 | 40 | tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); 41 | } 42 | 43 | static int kbhit() 44 | { 45 | struct timeval tv; 46 | fd_set fds; 47 | 48 | tv.tv_sec = 0; 49 | tv.tv_usec = 0; 50 | 51 | FD_ZERO(&fds); 52 | FD_SET(STDIN_FILENO, &fds); 53 | 54 | select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); 55 | return FD_ISSET(STDIN_FILENO, &fds); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /examples/monostable.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "mbb/hsm.h" 22 | #include "mbb/timer_ev.h" 23 | #include "mbb/debug.h" 24 | #include 25 | #include 26 | #include 27 | 28 | #define MONO_NROF_SWITCHES 5 29 | #define MONO_TIMEOUT (2 * MTMR_ONE_SEC) 30 | #define MONO_PERIOD (100 * MTMR_ONE_MSEC) 31 | 32 | enum { 33 | /* timeout events first */ 34 | MONO_EVENT_TIMEOUT = MHSM_EVENT_CUSTOM, 35 | MONO_EVENT_TRIGGER 36 | }; 37 | 38 | typedef struct { 39 | /* the state structure must start with an array of timers */ 40 | mtmr_ev_t timer[MTMR_NROF_TIMERS(MONO_EVENT_TIMEOUT)]; 41 | int id; 42 | int counter; 43 | } mono_state_t; 44 | 45 | MHSM_DEFINE_STATE(mono_off, NULL); 46 | MHSM_DEFINE_STATE(mono_on, NULL); 47 | 48 | mhsm_state_t *mono_off_fun(mhsm_hsm_t* hsm, mhsm_event_t event) 49 | { 50 | mono_state_t *state = (mono_state_t*) mhsm_context(hsm); 51 | 52 | switch (event.id) { 53 | case MHSM_EVENT_ENTRY: 54 | MDBG_PRINT2("OFF %02d (%03d)\n", state->id, state->counter); 55 | break; 56 | case MONO_EVENT_TRIGGER: 57 | return &mono_on; 58 | } 59 | 60 | return &mono_off; 61 | } 62 | 63 | mhsm_state_t *mono_on_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 64 | { 65 | mono_state_t *state = (mono_state_t*) mhsm_context(hsm); 66 | 67 | switch (event.id) { 68 | case MHSM_EVENT_ENTRY: 69 | state->counter++; 70 | /* fall-through */ 71 | case MONO_EVENT_TRIGGER: 72 | MDBG_PRINT2("ON %02d (%03d)\n", state->id, state->counter); 73 | mhsm_start_timer(hsm, MONO_EVENT_TIMEOUT, MONO_TIMEOUT); 74 | break; 75 | case MONO_EVENT_TIMEOUT: 76 | return &mono_off; 77 | } 78 | 79 | return &mono_on; 80 | } 81 | 82 | static void print_cb(EV_P_ ev_timer *w, int revents) 83 | { 84 | mhsm_hsm_t *switches = (mhsm_hsm_t*) w->data; 85 | int i; 86 | 87 | printf("\r"); 88 | for (i = 0; i < MONO_NROF_SWITCHES; i++) { 89 | mhsm_hsm_t *hsm = switches + i; 90 | mono_state_t *state = (mono_state_t*) mhsm_context(hsm); 91 | 92 | printf("%d: %s (%03d)\t", state->id, mhsm_is_in(hsm, &mono_on) ? "ON " : "OFF", state->counter); 93 | } 94 | fflush(stdout); 95 | 96 | } 97 | 98 | static void keypress_cb(EV_P_ ev_io *w, int revents) 99 | { 100 | mhsm_hsm_t *switches = (mhsm_hsm_t*) w->data; 101 | char c; 102 | 103 | c = getchar(); 104 | 105 | if (c >= '1' && c < '1' + MONO_NROF_SWITCHES) { 106 | mhsm_dispatch_event(switches + (c - '1'), MONO_EVENT_TRIGGER); 107 | } 108 | 109 | if (c == 'q') ev_break(EV_A_ EVBREAK_ALL); 110 | } 111 | 112 | #include "keyboard.inc" 113 | 114 | int main(void) 115 | { 116 | mhsm_hsm_t switches[MONO_NROF_SWITCHES]; 117 | mono_state_t switch_states[MONO_NROF_SWITCHES]; 118 | ev_io stdin_watcher; 119 | ev_timer print_timeout; 120 | struct ev_loop *loop = EV_DEFAULT; 121 | int i; 122 | 123 | for (i = 0; i < MONO_NROF_SWITCHES; i++) { 124 | mhsm_hsm_t *switch_hsm = switches + i; 125 | mono_state_t *switch_state = switch_states + i; 126 | 127 | mhsm_initialise(switch_hsm, switch_state, &mono_off); 128 | mtmr_ev_initalise_timers(switch_hsm, MTMR_NROF_TIMERS(MONO_EVENT_TIMEOUT), loop); 129 | 130 | switch_state->id = i + 1; 131 | switch_state->counter = 0; 132 | 133 | mhsm_dispatch_event(switch_hsm, MHSM_EVENT_INITIAL); 134 | } 135 | 136 | ev_io_init(&stdin_watcher, keypress_cb, 0, EV_READ); 137 | ev_io_start(loop, &stdin_watcher); 138 | stdin_watcher.data = switches; 139 | 140 | ev_timer_init(&print_timeout, print_cb, 0.1, 0.1); 141 | ev_timer_start(loop, &print_timeout); 142 | print_timeout.data = switches; 143 | 144 | nonblock(1); 145 | printf("press '1' to '%c' to enable lights, 'q' to quit\n", '1' + MONO_NROF_SWITCHES - 1); 146 | ev_run(loop, 0); 147 | printf("\nquitting\n"); 148 | nonblock(0); 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /examples/pelican.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | /* 22 | * PElican LIght CONtrolled Crossing Example 23 | * http://www.state-machine.com/resources/AN_PELICAN.pdf 24 | */ 25 | 26 | #include "mbb/hsm.h" 27 | #include "mbb/timer_periodic.h" 28 | #include "mbb/debug.h" 29 | #include 30 | #include 31 | 32 | #define PELICAN_PERIOD (MTMR_ONE_SEC / 20) 33 | #define PELICAN_TIMEOUT_CARS_GREEN_MIN (8 * MTMR_ONE_SEC) 34 | #define PELICAN_TIMEOUT_CARS_YELLOW (3 * MTMR_ONE_SEC) 35 | #define PELICAN_TIMEOUT_PEDS_WALK (3 * MTMR_ONE_SEC) 36 | #define PELICAN_TIMEOUT_PEDS_FLASH (MTMR_ONE_SEC / 5) 37 | #define PELICAN_PEDS_NROF_FLASHES 10 38 | #define PELICAN_TIMEOUT_OFF_FLASH (MTMR_ONE_SEC / 2) 39 | 40 | enum { 41 | PELICAN_CARS_BLANK, 42 | PELICAN_CARS_RED, 43 | PELICAN_CARS_YELLOW, 44 | PELICAN_CARS_GREEN 45 | }; 46 | 47 | enum { 48 | PELICAN_PEDS_BLANK, 49 | PELICAN_PEDS_DONT_WALK, 50 | PELICAN_PEDS_WALK 51 | }; 52 | 53 | enum { 54 | /* timeout events must be defined first */ 55 | PELICAN_EVENT_TIMEOUT_CARS_GREEN_MIN = MHSM_EVENT_CUSTOM, 56 | PELICAN_EVENT_TIMEOUT_CARS_YELLOW, 57 | PELICAN_EVENT_TIMEOUT_PEDS_WALK, 58 | PELICAN_EVENT_TIMEOUT_PEDS_FLASH, 59 | PELICAN_EVENT_TIMEOUT_OFF_FLASH, 60 | PELICAN_EVENT_PEDS_WAITING, 61 | PELICAN_EVENT_OFF, 62 | PELICAN_EVENT_ON 63 | }; 64 | 65 | MHSM_DEFINE_STATE(operational, NULL); 66 | MHSM_DEFINE_STATE(cars_enabled, &operational); 67 | MHSM_DEFINE_STATE(cars_green, &cars_enabled); 68 | MHSM_DEFINE_STATE(cars_green_no_ped, &cars_green); 69 | MHSM_DEFINE_STATE(cars_green_int, &cars_green); 70 | MHSM_DEFINE_STATE(cars_green_ped_wait, &cars_green); 71 | MHSM_DEFINE_STATE(cars_yellow, &cars_enabled); 72 | MHSM_DEFINE_STATE(peds_enabled, &operational); 73 | MHSM_DEFINE_STATE(peds_walk, &peds_enabled); 74 | MHSM_DEFINE_STATE(peds_flash, &peds_enabled); 75 | MHSM_DEFINE_STATE(offline, NULL); 76 | 77 | typedef struct { 78 | /* 79 | * mtmr functions assume that mhsm_context(hsm) points to an array of 80 | * mtmr_prd_t. So this array must be the first component of the state 81 | * structure. 82 | * If the timeout events are defined first (see above) 83 | * MTMR_NROF_TIMERS(last_timer_event) gives the number of timers. 84 | */ 85 | mtmr_prd_t timers[MTMR_NROF_TIMERS(PELICAN_EVENT_TIMEOUT_OFF_FLASH)]; 86 | int cars_light_state; 87 | int peds_light_state; 88 | int peds_flash_counter; 89 | } pelican_state_t; 90 | 91 | static void print_state(pelican_state_t *state) 92 | { 93 | int i; 94 | 95 | printf("\r"); 96 | for (i = 0; i < 80; i++) printf(" "); 97 | 98 | printf("\r"); 99 | printf("cars: "); 100 | switch (state->cars_light_state) { 101 | case PELICAN_CARS_RED: 102 | printf("red"); 103 | break; 104 | case PELICAN_CARS_YELLOW: 105 | printf("yellow"); 106 | break; 107 | case PELICAN_CARS_GREEN: 108 | printf("green"); 109 | break; 110 | default: 111 | printf(" "); 112 | break; 113 | } 114 | printf("\tpedestrians: "); 115 | switch (state->peds_light_state) { 116 | case PELICAN_PEDS_WALK: 117 | printf("walk"); 118 | break; 119 | case PELICAN_PEDS_DONT_WALK: 120 | printf("don't walk"); 121 | break; 122 | default: 123 | printf(" "); 124 | break; 125 | } 126 | fflush(stdout); 127 | } 128 | 129 | static void set_cars_light(pelican_state_t *state, int light_state) 130 | { 131 | state->cars_light_state = light_state; 132 | print_state(state); 133 | } 134 | 135 | static void set_peds_light(pelican_state_t *state, int light_state) 136 | { 137 | state->peds_light_state = light_state; 138 | print_state(state); 139 | } 140 | 141 | mhsm_state_t *operational_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 142 | { 143 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 144 | 145 | switch (event.id) { 146 | case MHSM_EVENT_ENTRY: 147 | set_cars_light(state, PELICAN_CARS_RED); 148 | set_peds_light(state, PELICAN_PEDS_DONT_WALK); 149 | state->peds_flash_counter = 0; 150 | break; 151 | case MHSM_EVENT_INITIAL: 152 | return &cars_enabled; 153 | case PELICAN_EVENT_OFF: 154 | return &offline; 155 | } 156 | 157 | return &operational; 158 | } 159 | 160 | mhsm_state_t *cars_enabled_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 161 | { 162 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 163 | 164 | switch (event.id) { 165 | case MHSM_EVENT_INITIAL: 166 | return &cars_green; 167 | case MHSM_EVENT_EXIT: 168 | set_cars_light(state, PELICAN_CARS_RED); 169 | break; 170 | } 171 | 172 | return &cars_enabled; 173 | } 174 | 175 | mhsm_state_t *cars_green_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 176 | { 177 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 178 | 179 | switch (event.id) { 180 | case MHSM_EVENT_ENTRY: 181 | set_cars_light(state, PELICAN_CARS_GREEN); 182 | break; 183 | case MHSM_EVENT_INITIAL: 184 | return &cars_green_no_ped; 185 | } 186 | 187 | return &cars_green; 188 | } 189 | 190 | mhsm_state_t *cars_green_no_ped_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 191 | { 192 | switch (event.id) { 193 | case MHSM_EVENT_ENTRY: 194 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_CARS_GREEN_MIN, PELICAN_TIMEOUT_CARS_GREEN_MIN); 195 | break; 196 | case PELICAN_EVENT_TIMEOUT_CARS_GREEN_MIN: 197 | return &cars_green_int; 198 | case PELICAN_EVENT_PEDS_WAITING: 199 | return &cars_green_ped_wait; 200 | } 201 | 202 | return &cars_green_no_ped; 203 | } 204 | 205 | mhsm_state_t *cars_green_int_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 206 | { 207 | switch (event.id) { 208 | case PELICAN_EVENT_PEDS_WAITING: 209 | return &cars_yellow; 210 | } 211 | 212 | return &cars_green_int; 213 | } 214 | 215 | mhsm_state_t *cars_green_ped_wait_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 216 | { 217 | switch (event.id) { 218 | case PELICAN_EVENT_TIMEOUT_CARS_GREEN_MIN: 219 | return &cars_yellow; 220 | } 221 | 222 | return &cars_green_ped_wait; 223 | } 224 | 225 | mhsm_state_t *cars_yellow_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 226 | { 227 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 228 | 229 | switch (event.id) { 230 | case MHSM_EVENT_ENTRY: 231 | set_cars_light(state, PELICAN_CARS_YELLOW); 232 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_CARS_YELLOW, PELICAN_TIMEOUT_CARS_YELLOW); 233 | break; 234 | case PELICAN_EVENT_TIMEOUT_CARS_YELLOW: 235 | return &peds_enabled; 236 | } 237 | 238 | return &cars_yellow; 239 | } 240 | 241 | mhsm_state_t *peds_enabled_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 242 | { 243 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 244 | 245 | switch (event.id) { 246 | case MHSM_EVENT_INITIAL: 247 | return &peds_walk; 248 | case MHSM_EVENT_EXIT: 249 | set_peds_light(state, PELICAN_PEDS_DONT_WALK); 250 | break; 251 | } 252 | 253 | return &peds_enabled; 254 | } 255 | 256 | mhsm_state_t *peds_walk_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 257 | { 258 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 259 | 260 | switch (event.id) { 261 | case MHSM_EVENT_ENTRY: 262 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_PEDS_WALK, PELICAN_TIMEOUT_PEDS_WALK); 263 | set_peds_light(state, PELICAN_PEDS_WALK); 264 | break; 265 | case PELICAN_EVENT_TIMEOUT_PEDS_WALK: 266 | return &peds_flash; 267 | } 268 | 269 | return &peds_walk; 270 | } 271 | 272 | mhsm_state_t *peds_flash_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 273 | { 274 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 275 | 276 | switch (event.id) { 277 | case MHSM_EVENT_ENTRY: 278 | state->peds_flash_counter = PELICAN_PEDS_NROF_FLASHES; 279 | set_peds_light(state, PELICAN_PEDS_BLANK); 280 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_PEDS_FLASH, PELICAN_TIMEOUT_PEDS_FLASH); 281 | break; 282 | case PELICAN_EVENT_TIMEOUT_PEDS_FLASH: 283 | state->peds_flash_counter--; 284 | if (state->peds_flash_counter == 0) 285 | return &cars_enabled; 286 | set_peds_light(state, (state->peds_flash_counter % 2) ? PELICAN_PEDS_WALK : PELICAN_PEDS_BLANK); 287 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_PEDS_FLASH, PELICAN_TIMEOUT_PEDS_FLASH); 288 | break; 289 | } 290 | 291 | return &peds_flash; 292 | } 293 | 294 | mhsm_state_t *offline_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 295 | { 296 | pelican_state_t *state = (pelican_state_t*) mhsm_context(hsm); 297 | 298 | switch (event.id) { 299 | case MHSM_EVENT_ENTRY: 300 | set_cars_light(state, PELICAN_CARS_RED); 301 | set_peds_light(state, PELICAN_PEDS_DONT_WALK); 302 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_OFF_FLASH, PELICAN_TIMEOUT_OFF_FLASH); 303 | break; 304 | case PELICAN_EVENT_TIMEOUT_OFF_FLASH: 305 | set_cars_light(state, state->cars_light_state == PELICAN_CARS_RED ? PELICAN_CARS_BLANK : PELICAN_CARS_RED); 306 | set_peds_light(state, state->peds_light_state == PELICAN_PEDS_DONT_WALK ? PELICAN_PEDS_BLANK : PELICAN_PEDS_DONT_WALK); 307 | mhsm_start_timer(hsm, PELICAN_EVENT_TIMEOUT_OFF_FLASH, PELICAN_TIMEOUT_OFF_FLASH); 308 | break; 309 | case PELICAN_EVENT_ON: 310 | return &operational; 311 | } 312 | 313 | return &offline; 314 | } 315 | 316 | #include "periodic.inc" 317 | #include "keyboard.inc" 318 | 319 | static int process(mhsm_hsm_t *pelican, void *state) 320 | { 321 | if (kbhit()) { 322 | char c = fgetc(stdin); 323 | 324 | switch (c) { 325 | case ' ': 326 | mhsm_dispatch_event(pelican, PELICAN_EVENT_PEDS_WAITING); 327 | break; 328 | case 'o': 329 | mhsm_dispatch_event(pelican, mhsm_is_in(pelican, &operational) ? PELICAN_EVENT_OFF : PELICAN_EVENT_ON); 330 | break; 331 | case 'q': 332 | break; 333 | default: 334 | MDBG_PRINT1("unhandled key press: '%c'\n", c); 335 | break; 336 | } 337 | 338 | if (c == 'q') return -1; 339 | } 340 | 341 | mtmr_prd_increment_timers(pelican, MTMR_NROF_TIMERS(PELICAN_EVENT_TIMEOUT_OFF_FLASH), PELICAN_PERIOD); 342 | 343 | return 0; 344 | } 345 | 346 | int main(void) 347 | { 348 | mhsm_hsm_t pelican; 349 | pelican_state_t pelican_state; 350 | 351 | printf("press [space] to trigger PEDS_WAITING event\n"); 352 | printf("press 'o' to trigger ON/OFF events\n"); 353 | printf("press 'q' to quit\n"); 354 | 355 | mhsm_initialise(&pelican, &pelican_state, &operational); 356 | if (mtmr_prd_initialise_timers(&pelican, MTMR_NROF_TIMERS(PELICAN_EVENT_TIMEOUT_OFF_FLASH)) != 0) { 357 | MDBG_PRINT_LN("failed to initialise timers"); 358 | exit(EXIT_FAILURE); 359 | } 360 | mhsm_dispatch_event(&pelican, MHSM_EVENT_INITIAL); 361 | 362 | nonblock(1); 363 | periodic(PELICAN_PERIOD, process, &pelican, &pelican_state); 364 | nonblock(0); 365 | 366 | printf("\nquitting\n"); 367 | 368 | return 0; 369 | } 370 | -------------------------------------------------------------------------------- /examples/periodic.inc: -------------------------------------------------------------------------------- 1 | /* vim: set filetype=c: */ 2 | /* * Copyright (C) 2015 Jan Weil 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | #include "mbb/hsm.h" 23 | #include 24 | 25 | void periodic(uint32_t period_msecs, int (*callback)(mhsm_hsm_t*, void*), mhsm_hsm_t *hsm, void *state) 26 | { 27 | while (1) { 28 | usleep(period_msecs * 1000); 29 | if (callback(hsm, state) != 0) 30 | break; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mbb/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LIBRARIES = libmbb.a 2 | libmbb_a_SOURCES = debug.c hsm.c timer_periodic.c 3 | if HAVE_LIBEV 4 | libmbb_a_SOURCES += timer_ev.c 5 | endif 6 | libmbb_a_CPPFLAGS = -I.. 7 | -------------------------------------------------------------------------------- /mbb/debug.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | int mdbg_printf(const char *format, ...) 26 | { 27 | va_list ap; 28 | int ret; 29 | 30 | va_start(ap, format); 31 | ret = vfprintf(stderr, format, ap); 32 | va_end(ap); 33 | return ret; 34 | } 35 | 36 | int mdbg_timestamp(char *out, int size) 37 | { 38 | time_t current_time; 39 | struct tm *bdtime; 40 | 41 | if (size < 24) 42 | return -1; 43 | 44 | if (time(¤t_time) == -1) 45 | return -1; 46 | 47 | bdtime = localtime(¤t_time); 48 | if (bdtime == NULL) 49 | return -1; 50 | 51 | if (strftime(out, size, "%Y-%m-%dT%H:%M:%S.000", bdtime) != 23) 52 | return -1; 53 | 54 | out[23] = '\0'; 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /mbb/debug.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_DEBUG_H 22 | #define MBB_DEBUG_H 23 | 24 | #ifndef NDEBUG 25 | 26 | # include 27 | # include 28 | # include 29 | 30 | # ifndef MDBG_PRINTF 31 | int mdbg_printf(const char *format, ...); 32 | # define MDBG_PRINTF mdbg_printf 33 | # endif 34 | 35 | /* 36 | * MDBG_TIMESTAMP should name a function which writes a timestamp in ISO 8601 37 | * format to a string: 2015-02-10T13:22:35.102 38 | */ 39 | # ifndef MDBG_TIMESTAMP 40 | int mdbg_timestamp(char *out, int size); 41 | # define MDBG_TIMESTAMP mdbg_timestamp 42 | # endif 43 | 44 | # define MDBG_PRINT_PREFIX() do { \ 45 | char timestamp[24]; \ 46 | if (MDBG_TIMESTAMP(timestamp, sizeof(timestamp)) != 0) break; \ 47 | MDBG_PRINTF("%s (%s, %d): ", timestamp, __FILE__, __LINE__); \ 48 | } while (0) 49 | 50 | # define MDBG_PRINT0(FORMAT) do { \ 51 | MDBG_PRINT_PREFIX(); \ 52 | MDBG_PRINTF(FORMAT); \ 53 | } while (0) 54 | 55 | # define MDBG_PRINT1(FORMAT, ARG) do { \ 56 | MDBG_PRINT_PREFIX(); \ 57 | MDBG_PRINTF(FORMAT, ARG); \ 58 | } while (0) 59 | 60 | # define MDBG_PRINT2(FORMAT, ARG1, ARG2) do { \ 61 | MDBG_PRINT_PREFIX(); \ 62 | MDBG_PRINTF(FORMAT, ARG1, ARG2); \ 63 | } while (0) 64 | 65 | # define MDBG_PRINT3(FORMAT, ARG1, ARG2, ARG3) do { \ 66 | MDBG_PRINT_PREFIX(); \ 67 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3); \ 68 | } while (0) 69 | 70 | # define MDBG_PRINT4(FORMAT, ARG1, ARG2, ARG3, ARG4) do { \ 71 | MDBG_PRINT_PREFIX(); \ 72 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4); \ 73 | } while (0) 74 | 75 | # define MDBG_PRINT5(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5) do { \ 76 | MDBG_PRINT_PREFIX(); \ 77 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5); \ 78 | } while (0) 79 | 80 | # define MDBG_PRINT6(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) do { \ 81 | MDBG_PRINT_PREFIX(); \ 82 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6); \ 83 | } while (0) 84 | 85 | # define MDBG_PRINT7(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) do { \ 86 | MDBG_PRINT_PREFIX(); \ 87 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7); \ 88 | } while (0) 89 | 90 | # define MDBG_PRINT8(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8) do { \ 91 | MDBG_PRINT_PREFIX(); \ 92 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8); \ 93 | } while (0) 94 | 95 | # define MDBG_PRINT9(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9) do { \ 96 | MDBG_PRINT_PREFIX(); \ 97 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9); \ 98 | } while (0) 99 | 100 | # define MDBG_PRINT10(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10) do { \ 101 | MDBG_PRINT_PREFIX(); \ 102 | MDBG_PRINTF(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10); \ 103 | } while (0) 104 | 105 | # define MDBG_PRINT_LN(LINE) MDBG_PRINT1("%s\n", LINE) 106 | # define MDBG_PRINT_I(INTVAL) MDBG_PRINT1(#INTVAL " = %i\n", (int) (INTVAL)) 107 | # define MDBG_PRINT_O(OCTVAL) MDBG_PRINT1(#OCTVAL " = O%o\n", (unsigned int) (OCTVAL)) 108 | # define MDBG_PRINT_X(HEXVAL) MDBG_PRINT1(#HEXVAL " = 0x%02X\n", (unsigned int) (HEXVAL)) 109 | # define MDBG_PRINT_C(CHARVAL) MDBG_PRINT1(#CHARVAL " = '%c'\n", (int) (CHARVAL)) 110 | # define MDBG_PRINT_F(FLOATVAL) MDBG_PRINT1(#FLOATVAL " = %f\n", (double) (FLOATVAL)) 111 | # define MDBG_PRINT_S(STRVAL) MDBG_PRINT1(#STRVAL ": \"%s\"\n", (STRVAL)) 112 | # define MDBG_PRINT_P(MEMADDR) MDBG_PRINT1(#MEMADDR ": %p\n", (MEMADDR)) 113 | 114 | # define MDBG_PRINT_MEM(PTR, SIZE) do { \ 115 | int i; \ 116 | MDBG_PRINT_PREFIX(); \ 117 | MDBG_PRINTF("%s(%p, %d): ", #PTR, PTR, SIZE); \ 118 | for (i = 0; i < SIZE; i++) \ 119 | MDBG_PRINTF("%02X ", PTR[i]); \ 120 | MDBG_PRINTF("\n"); \ 121 | } while (0) 122 | 123 | # define MDBG_PRINT_ERRNO(MSG) MDBG_PRINT1(MSG ": %s\n", strerror(errno)) 124 | 125 | # define MDBG_ASSERT(EXP) do { \ 126 | if (!(EXP)) { \ 127 | MDBG_PRINT1("assertion failed: %s\n", #EXP); \ 128 | assert(EXP); \ 129 | } \ 130 | } while (0) 131 | 132 | # define MDBG_NEVER_REACHED() MDBG_ASSERT(!"reached") 133 | 134 | #else /* NDEBUG */ 135 | 136 | # define MDBG_PRINT0(FORMAT) do {} while(0) 137 | # define MDBG_PRINT1(FORMAT, ARG) do {} while(0) 138 | # define MDBG_PRINT2(FORMAT, ARG1, ARG2) do {} while(0) 139 | # define MDBG_PRINT3(FORMAT, ARG1, ARG2, ARG3) do {} while(0) 140 | # define MDBG_PRINT4(FORMAT, ARG1, ARG2, ARG3, ARG4) do {} while(0) 141 | # define MDBG_PRINT5(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5) do {} while(0) 142 | # define MDBG_PRINT6(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) do {} while(0) 143 | # define MDBG_PRINT7(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) do {} while(0) 144 | # define MDBG_PRINT8(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8) do {} while(0) 145 | # define MDBG_PRINT9(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9) do {} while(0) 146 | # define MDBG_PRINT10(FORMAT, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10) do {} while(0) 147 | 148 | # define MDBG_PRINT_LN(LINE)do {} while(0) 149 | # define MDBG_PRINT_I(INTVAL) do {} while(0) 150 | # define MDBG_PRINT_O(OCTVAL) do {} while(0) 151 | # define MDBG_PRINT_X(HEXVAL) do {} while(0) 152 | # define MDBG_PRINT_C(CHARVAl)do {} while(0) 153 | # define MDBG_PRINT_F(FLOATVal) do {} while(0) 154 | # define MDBG_PRINT_S(STRVAL) do {} while(0) 155 | # define MDBG_PRINT_P(MEMVAL)do {} while(0) 156 | 157 | # define MDBG_PRINT_MEM(PTR, SIZE)do {} while(0) 158 | # define MDBG_PRINT_ERRNO(MSG)do {} while(0) 159 | # define MDBG_ASSERT(EXP) do {} while(0) 160 | # define MDBG_NEVER_REACHED()do {} while(0) 161 | 162 | #endif /* NDEBUG */ 163 | 164 | #endif /* MBB_DEBUG_H */ 165 | -------------------------------------------------------------------------------- /mbb/hsm.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "hsm.h" 22 | #include "types.h" 23 | #include "queue.h" 24 | #include "debug.h" 25 | 26 | static mhsm_state_t *_find_least_common_ancestor(mhsm_state_t *a, mhsm_state_t *b) 27 | { 28 | if (a == NULL || b == NULL) 29 | return NULL; 30 | 31 | while (a->parent != NULL) { 32 | if (mhsm_is_ancestor(a->parent, b)) 33 | return a->parent; 34 | 35 | a = a->parent; 36 | } 37 | 38 | return NULL; 39 | } 40 | 41 | static mhsm_state_t *_enter_state(mhsm_hsm_t *hsm, mhsm_state_t *from, mhsm_state_t *to); 42 | 43 | static mhsm_state_t *_local_dispatch(mhsm_hsm_t *hsm, mhsm_state_t *state, mhsm_event_t event) 44 | { 45 | MDBG_ASSERT(state != NULL); 46 | if (state == NULL) 47 | return NULL; 48 | 49 | #ifndef NDEBUG 50 | switch (event.id) { 51 | case MHSM_EVENT_INITIAL: 52 | case MHSM_EVENT_ENTRY: 53 | case MHSM_EVENT_DO: 54 | case MHSM_EVENT_EXIT: 55 | break; 56 | default: 57 | MDBG_PRINT3("dispatching event %s (%d) to state %s\n", 58 | event.id == MHSM_EVENT_INITIAL ? "INITIAL" : 59 | event.id == MHSM_EVENT_ENTRY ? "ENTRY" : 60 | event.id == MHSM_EVENT_DO ? "DO" : 61 | event.id == MHSM_EVENT_EXIT ? "EXIT" : 62 | "CUSTOM", 63 | event.id, state->name); 64 | } 65 | #endif 66 | 67 | return state->event_processing_function(hsm, event); 68 | } 69 | 70 | static mhsm_state_t *_transition(mhsm_hsm_t *hsm, mhsm_state_t *from, mhsm_state_t *to) 71 | { 72 | mhsm_state_t *least_common_ancestor, *result; 73 | 74 | MDBG_ASSERT(to != NULL); 75 | if (to == NULL) 76 | /* Run, Forrest, run! */ 77 | return NULL; 78 | 79 | MDBG_PRINT2("transition from %s to %s\n", from->name, to->name); 80 | 81 | /* dispatch exit events */ 82 | if (mhsm_is_ancestor(from, to)) { 83 | least_common_ancestor = from; 84 | } else { 85 | if (mhsm_is_ancestor(to, from)) 86 | least_common_ancestor = to; 87 | else 88 | least_common_ancestor = _find_least_common_ancestor(from, to); 89 | 90 | while (from != least_common_ancestor) { 91 | mhsm_event_t event; 92 | 93 | event.id = MHSM_EVENT_EXIT; 94 | result = _local_dispatch(hsm, from, event); 95 | if (result != from) { 96 | MDBG_PRINT_S(result->name); 97 | return _transition(hsm, from, result); 98 | } 99 | 100 | from = from->parent; 101 | } 102 | } 103 | 104 | if (least_common_ancestor == to) 105 | return least_common_ancestor; 106 | 107 | return _enter_state(hsm, least_common_ancestor, to); 108 | } 109 | 110 | static mhsm_state_t *_enter_state(mhsm_hsm_t *hsm, mhsm_state_t *from, mhsm_state_t *to) 111 | { 112 | mhsm_state_t *current; 113 | 114 | MDBG_ASSERT(mhsm_is_ancestor(from, to)); 115 | if (!mhsm_is_ancestor(from, to)) 116 | return from; 117 | 118 | MDBG_ASSERT(to != NULL); 119 | if (to == NULL) 120 | return from; 121 | 122 | if (to == from) 123 | return to; 124 | 125 | /* link path to target state */ 126 | for (current = to; current->parent != from; current = current->parent) { 127 | current->parent->current_substate = current; 128 | } 129 | 130 | /* dispatch parent entry events */ 131 | while (current != to) { 132 | mhsm_event_t event; 133 | mhsm_state_t *result; 134 | 135 | event.id = MHSM_EVENT_ENTRY; 136 | result = _local_dispatch(hsm, current, event); 137 | if (result != current) { 138 | MDBG_PRINT2("dispatching the entry event to %s triggered a new transition to %s\n", current->name, result->name); 139 | return _transition(hsm, current, result); 140 | } 141 | 142 | current = current->current_substate; 143 | current->parent->current_substate = NULL; 144 | } 145 | 146 | /* initial transition */ 147 | while (1) { 148 | mhsm_event_t event; 149 | mhsm_state_t *target; 150 | 151 | event.id = MHSM_EVENT_ENTRY; 152 | target = _local_dispatch(hsm, to, event); 153 | if (target != to) { 154 | MDBG_PRINT2("transition interrupted by %s, new target: %s\n", to->name, target->name); 155 | return _transition(hsm, to, target); 156 | } 157 | 158 | event.id = MHSM_EVENT_INITIAL; 159 | target = _local_dispatch(hsm, to, event); 160 | if (target == to) 161 | break; 162 | else 163 | MDBG_PRINT2("intial transition to %s in composite state %s\n", target->name, to->name); 164 | 165 | to = target; 166 | } 167 | 168 | return to; 169 | } 170 | 171 | static int _defer_event_arg(mhsm_hsm_t *hsm, uint32_t id, int32_t arg) 172 | { 173 | mhsm_event_t event; 174 | 175 | if (MQUE_IS_FULL(&hsm->deferred_events)) { 176 | MDBG_PRINT_LN("event queue too short"); 177 | return -1; 178 | } 179 | 180 | event.id = id; 181 | event.arg = arg; 182 | 183 | MQUE_ENQUEUE(&hsm->deferred_events, event); 184 | 185 | MDBG_PRINT3("defered event (%d, %d) in %s\n", (int) event.id, (int) event.arg, hsm->current_state->name); 186 | 187 | return 0; 188 | } 189 | 190 | static mhsm_state_t *_dispatch_event(mhsm_hsm_t *hsm, mhsm_state_t *state, mhsm_event_t event) 191 | { 192 | mhsm_state_t *target = state; 193 | mhsm_state_t *current; 194 | mhsm_state_t *result; 195 | 196 | /* catch special INITIAL event */ 197 | if (event.id == MHSM_EVENT_INITIAL) { 198 | return _enter_state(hsm, NULL, state); 199 | } 200 | 201 | /* dispatch event to all active states */ 202 | for (current = state; current != NULL; current = current->parent) { 203 | result = _local_dispatch(hsm, current, event); 204 | 205 | /* greedy transition selection */ 206 | if (result != current && target == state) 207 | target = result; 208 | } 209 | 210 | /* return if the event was deferred */ 211 | if (target == NULL) return NULL; 212 | 213 | if (target != state) 214 | return _transition(hsm, state, target); 215 | 216 | return state; 217 | } 218 | 219 | void mhsm_initialise(mhsm_hsm_t *hsm, void *context, mhsm_state_t *initial_state) 220 | { 221 | MQUE_INITIALISE(&hsm->deferred_events); 222 | hsm->context = context; 223 | hsm->current_state = initial_state; 224 | hsm->in_transition = 0; 225 | hsm->start_timer_callback = NULL; 226 | } 227 | 228 | void mhsm_dispatch_event(mhsm_hsm_t *hsm, uint32_t id) 229 | { 230 | mhsm_dispatch_event_arg(hsm, id, 0); 231 | } 232 | 233 | void mhsm_dispatch_event_arg(mhsm_hsm_t *hsm, uint32_t id, int32_t arg) 234 | { 235 | mhsm_event_t event; 236 | mhsm_state_t *new_state; 237 | int nevents; 238 | int i; 239 | 240 | MDBG_ASSERT(hsm->current_state != NULL); 241 | 242 | if (hsm->in_transition) { 243 | _defer_event_arg(hsm, id, arg); 244 | return; 245 | } 246 | 247 | hsm->in_transition = 1; 248 | 249 | event.id = id; 250 | event.arg = arg; 251 | 252 | new_state = _dispatch_event(hsm, hsm->current_state, event); 253 | if (new_state == NULL) { 254 | MDBG_PRINT2("event %d was defered by %s\n", event.id, hsm->current_state->name); 255 | 256 | /* (re-)enqueue event */ 257 | if (_defer_event_arg(hsm, event.id, event.arg) != 0) 258 | MDBG_PRINT_LN("(re-)enqueing defered event failed"); 259 | 260 | return; 261 | } 262 | 263 | hsm->current_state = new_state; 264 | 265 | nevents = MQUE_LENGTH(&hsm->deferred_events); 266 | for (i = 0; i < nevents; i++) { 267 | MDBG_ASSERT(!MQUE_IS_EMPTY(&hsm->deferred_events)); 268 | if (MQUE_IS_EMPTY(&hsm->deferred_events)) break; 269 | 270 | hsm->current_state = _dispatch_event(hsm, hsm->current_state, MQUE_HEAD(&hsm->deferred_events)); 271 | MDBG_ASSERT(hsm->current_state != NULL); 272 | 273 | MQUE_DEQUEUE(&hsm->deferred_events); 274 | } 275 | 276 | hsm->in_transition = 0; 277 | } 278 | 279 | void *mhsm_context(mhsm_hsm_t *hsm) 280 | { 281 | return hsm->context; 282 | } 283 | 284 | mhsm_state_t *mhsm_current_state(mhsm_hsm_t *hsm) 285 | { 286 | return hsm->current_state; 287 | } 288 | 289 | bool mhsm_is_ancestor(mhsm_state_t *ancestor, mhsm_state_t *target) 290 | { 291 | MDBG_ASSERT(target != NULL); 292 | if (target == NULL) 293 | return 0; 294 | 295 | if (ancestor == NULL) 296 | return 1; 297 | 298 | while (target->parent != NULL) { 299 | if (target->parent == ancestor) 300 | return 1; 301 | 302 | target = target->parent; 303 | } 304 | 305 | return 0; 306 | } 307 | 308 | bool mhsm_is_in(mhsm_hsm_t *hsm, mhsm_state_t *state) 309 | { 310 | return mhsm_current_state(hsm) == state || mhsm_is_ancestor(state, mhsm_current_state(hsm)); 311 | } 312 | 313 | void mhsm_set_timer_callback(mhsm_hsm_t *hsm, int (*callback)(mhsm_hsm_t*, uint32_t, uint32_t)) 314 | { 315 | hsm->start_timer_callback = callback; 316 | } 317 | 318 | int mhsm_start_timer(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs) 319 | { 320 | if (hsm->start_timer_callback == NULL) { 321 | MDBG_PRINT_LN("start_timer_callback uninitialised"); 322 | return -1; 323 | } 324 | 325 | return hsm->start_timer_callback(hsm, event_id, period_msecs); 326 | } 327 | -------------------------------------------------------------------------------- /mbb/hsm.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_HSM_H 22 | #define MBB_HSM_H 23 | 24 | /* Public API */ 25 | #include "types.h" 26 | #include "queue.h" 27 | 28 | /* HSM, state, and event types */ 29 | typedef struct mhsm_hsm_s mhsm_hsm_t; 30 | typedef struct mhsm_state_s mhsm_state_t; 31 | typedef struct { 32 | uint32_t id; 33 | int32_t arg; 34 | } mhsm_event_t; 35 | typedef mhsm_state_t *mhsm_event_processing_fun_t(mhsm_hsm_t *hsm, mhsm_event_t event); 36 | 37 | #ifndef NDEBUG 38 | # define MHSM_DEFINE_STATE(STATE, PARENT) \ 39 | const char STATE##_name[] = #STATE; \ 40 | mhsm_state_t *STATE##_fun(mhsm_hsm_t *hsm, mhsm_event_t event); \ 41 | mhsm_state_t STATE = { STATE##_fun, PARENT, NULL, STATE##_name } 42 | #else /* NDEBUG */ 43 | # define MHSM_DEFINE_STATE(STATE, PARENT) \ 44 | mhsm_state_t *STATE##_fun(mhsm_hsm_t *hsm, mhsm_event_t event); \ 45 | mhsm_state_t STATE = { STATE##_fun, PARENT, NULL, } 46 | #endif 47 | 48 | 49 | /* Common events */ 50 | enum { 51 | MHSM_EVENT_ENTRY, 52 | MHSM_EVENT_INITIAL, 53 | MHSM_EVENT_DO, 54 | MHSM_EVENT_EXIT, 55 | MHSM_EVENT_CUSTOM 56 | }; 57 | 58 | void mhsm_initialise(mhsm_hsm_t *hsm, void *context, mhsm_state_t *initial_state); 59 | void mhsm_dispatch_event(mhsm_hsm_t *hsm, uint32_t id); 60 | void mhsm_dispatch_event_arg(mhsm_hsm_t *hsm, uint32_t id, int32_t arg); 61 | void *mhsm_context(mhsm_hsm_t *hsm); 62 | mhsm_state_t *mhsm_current_state(mhsm_hsm_t *hsm); 63 | bool mhsm_is_ancestor(mhsm_state_t *ancestor, mhsm_state_t *target); 64 | bool mhsm_is_in(mhsm_hsm_t *hsm, mhsm_state_t *state); 65 | void mhsm_set_timer_callback(mhsm_hsm_t *hsm, int (*callback)(mhsm_hsm_t*, uint32_t, uint32_t)); 66 | int mhsm_start_timer(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs); 67 | 68 | #ifndef MHSM_EVENT_QUEUE_LENGTH 69 | # define MHSM_EVENT_QUEUE_LENGTH 5 70 | #endif 71 | 72 | /* Private API */ 73 | #include "queue.h" 74 | 75 | /* HSM struct */ 76 | struct mhsm_hsm_s { 77 | void *context; 78 | mhsm_state_t *current_state; 79 | MQUE_DEFINE_STRUCT(mhsm_event_t, MHSM_EVENT_QUEUE_LENGTH) deferred_events; 80 | bool in_transition; 81 | int (*start_timer_callback)(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs); 82 | }; 83 | 84 | /* state struct */ 85 | struct mhsm_state_s { 86 | /* The state's event processing function */ 87 | mhsm_event_processing_fun_t *event_processing_function; 88 | /* s.parent != NULL => s is a substate */ 89 | mhsm_state_t *parent; 90 | /* s.current_substate != NULL => dispatch entry event to current_substate */ 91 | mhsm_state_t *current_substate; 92 | #ifndef NDEBUG 93 | const char *name; 94 | #endif 95 | }; 96 | 97 | #endif /* MBB_HSM_H */ 98 | -------------------------------------------------------------------------------- /mbb/queue.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_QUEUE_H 22 | #define MBB_QUEUE_H 23 | 24 | #include "types.h" 25 | 26 | /* 27 | * Simple type-safe fixed-capacity queues. 28 | * 29 | * Always check MQUE_LENGTH(), MQUE_IS_EMPTY(), or MQUE_IS_FULL(), 30 | * respectively, before calling MQUE_ENQUEUE() or MQUE_DEQUEUE(). 31 | * MQUE_ENQUEUE() will do nothing if the queue is full. MQUE_DEQUEUE() will do 32 | * nothing if the queue is empty. 33 | * 34 | * Example: 35 | * 36 | * { 37 | * MQUE_DEFINE_STRUCT(int, 5) event_queue; 38 | * 39 | * MQUE_INITIALISE(&event_queue); 40 | * 41 | * MQUE_ENQUEUE(&event_queue, 4); 42 | * MQUE_ENQUEUE(&event_queue, 3); 43 | * MQUE_ENQUEUE(&event_queue, 2); 44 | * 45 | * while (MQUE_LENGTH(&event_queue)) { 46 | * printf("event: %d\n", MQUE_HEAD(&event_queue)); 47 | * MQUE_DEQUEUE(&event_queue); 48 | * } 49 | * } 50 | * 51 | * Output: 52 | * 53 | * event: 4 54 | * event: 3 55 | * event: 2 56 | */ 57 | 58 | #define MQUE_DEFINE_STRUCT(TYPE, CAPACITY) \ 59 | struct { \ 60 | int first; \ 61 | int last; \ 62 | size_t count; \ 63 | TYPE data[CAPACITY]; \ 64 | } 65 | 66 | #define MQUE_CAPACITY(Q) (sizeof((Q)->data) / sizeof((Q)->data[0])) 67 | 68 | #define MQUE_INITIALISER { 0, -1, 0 } 69 | 70 | #define MQUE_INITIALISE(Q) do { \ 71 | (Q)->first = 0; \ 72 | (Q)->last = -1; \ 73 | (Q)->count = 0; \ 74 | } while (0) 75 | 76 | #define MQUE_LENGTH(Q) (Q)->count 77 | 78 | #define MQUE_IS_FULL(Q) (MQUE_LENGTH(Q) == MQUE_CAPACITY(Q)) 79 | 80 | #define MQUE_IS_EMPTY(Q) (MQUE_LENGTH(Q) == 0) 81 | 82 | #define MQUE_ENQUEUE(Q, ELEMENT) do { \ 83 | if (MQUE_IS_FULL(Q)) break; \ 84 | (Q)->last = ((Q)->last + 1) % MQUE_CAPACITY(Q); \ 85 | (Q)->data[(Q)->last] = ELEMENT; \ 86 | (Q)->count += 1; \ 87 | } while (0) 88 | 89 | #define MQUE_HEAD(Q) ((Q)->data[(Q)->first]) 90 | 91 | #define MQUE_DEQUEUE(Q) do { \ 92 | if (MQUE_IS_EMPTY(Q)) break; \ 93 | (Q)->first = ((Q)->first + 1) % MQUE_CAPACITY(Q); \ 94 | (Q)->count -= 1; \ 95 | } while (0) 96 | 97 | #endif /* MBB_QUEUE_H */ 98 | -------------------------------------------------------------------------------- /mbb/test.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_TEST_H 22 | #define MBB_TEST_H 23 | 24 | #include 25 | #include 26 | 27 | #if __linux 28 | /* 29 | * vt100-compatible text colors 30 | * http://linuxgazette.net/issue65/padala.html 31 | */ 32 | void textcolor(int attr, int fg, int bg); 33 | 34 | #define MUNT_RESET 0 35 | #define MUNT_BRIGHT 1 36 | #define MUNT_BLACK 0 37 | #define MUNT_RED 1 38 | #define MUNT_WHITE 7 39 | 40 | #define MUNT_COLOR(statement) do { \ 41 | textcolor(MUNT_BRIGHT, MUNT_RED, MUNT_BLACK); \ 42 | statement; \ 43 | textcolor(MUNT_RESET, MUNT_WHITE, MUNT_BLACK); \ 44 | } while (0) 45 | #else 46 | #define MUNT_COLOR(statement) do { \ 47 | statement; \ 48 | } while (0) 49 | #endif /* __linux */ 50 | 51 | /* 52 | * Minunit-like test 'framework' 53 | * http://www.jera.com/techinfo/jtns/jtn002.html 54 | */ 55 | #define MUNT_ASSERT(TEST) do { \ 56 | static char msgstr[256]; \ 57 | snprintf(msgstr, sizeof(msgstr), "Assertion FAILED (%03d): %s", __LINE__, #TEST); \ 58 | if (!(TEST)) return msgstr; \ 59 | fprintf(stderr, "Assertion passed (%03d): %s\n", __LINE__, #TEST); \ 60 | } while (0) 61 | 62 | int munt_tests_run; 63 | 64 | #define MUNT_RUN_TEST(TEST) do { \ 65 | char *message; \ 66 | fprintf(stderr, "### " #TEST " ###\n"); \ 67 | message = TEST(); \ 68 | munt_tests_run++; \ 69 | if (message) { \ 70 | MUNT_COLOR(fprintf(stderr, "### " #TEST " FAILED: '%s' ###\n\n", message)); \ 71 | return message; \ 72 | } else { \ 73 | fprintf(stderr, "### passed ###\n\n"); \ 74 | } \ 75 | } while (0) 76 | 77 | #endif /* MBB_TEST_H */ 78 | -------------------------------------------------------------------------------- /mbb/timer_common.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_TIMER_COMMON_H 22 | #define MBB_TIMER_COMMON_H 23 | 24 | #include "hsm.h" 25 | 26 | #define MTMR_NROF_TIMERS(LAST_TIMER_EVENT) (LAST_TIMER_EVENT - MHSM_EVENT_CUSTOM + 1) 27 | 28 | #define MTMR_ONE_MSEC (1) 29 | #define MTMR_ONE_SEC (1000 * MTMR_ONE_MSEC) 30 | #define MTMR_ONE_MIN (60 * MTMR_ONE_SEC) 31 | #define MTMR_ONE_HOUR (60 * MTMR_ONE_MIN) 32 | 33 | #endif /* MBB_TIMER_COMMON_H */ 34 | -------------------------------------------------------------------------------- /mbb/timer_ev.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "timer_ev.h" 22 | #include "types.h" 23 | #include 24 | 25 | static void timeout_cb(EV_P_ ev_timer *w, int revents) 26 | { 27 | mtmr_ev_t *timer = (mtmr_ev_t*) w->data; 28 | 29 | ev_timer_stop(EV_A_ w); 30 | mhsm_dispatch_event(timer->hsm, timer->event_id); 31 | } 32 | 33 | static int start_timer(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs) 34 | { 35 | mtmr_ev_t *timers = (mtmr_ev_t*) mhsm_context(hsm); 36 | uint32_t idx = event_id - MHSM_EVENT_CUSTOM; 37 | mtmr_ev_t *timer = timers + idx; 38 | ev_timer *ev_timer = &timer->timer; 39 | ev_tstamp duration = (ev_tstamp) period_msecs / 1000.0; 40 | 41 | if (ev_is_active(ev_timer)) 42 | ev_timer_stop(timer->loop, ev_timer); 43 | ev_timer_init(ev_timer, timeout_cb, duration, 0); 44 | ev_timer_start(timer->loop, ev_timer); 45 | 46 | return 0; 47 | } 48 | 49 | int mtmr_ev_initalise_timers(mhsm_hsm_t *hsm, size_t nrof_timers, struct ev_loop *loop) 50 | { 51 | mtmr_ev_t *timers = (mtmr_ev_t*) mhsm_context(hsm); 52 | int i; 53 | 54 | for (i = 0; i < nrof_timers; i++) { 55 | mtmr_ev_t *timer = timers + i; 56 | ev_timer *ev_timer = &timer->timer; 57 | 58 | timer->loop = loop; 59 | timer->hsm = hsm; 60 | timer->event_id = MHSM_EVENT_CUSTOM + i; 61 | ev_timer->data = timer; 62 | ev_timer_init(ev_timer, timeout_cb, 0, 0); 63 | } 64 | 65 | mhsm_set_timer_callback(hsm, start_timer); 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /mbb/timer_ev.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_TIMER_EV_H 22 | #define MBB_TIMER_EV_H 23 | 24 | #include "timer_common.h" 25 | #include "types.h" 26 | #include "hsm.h" 27 | #include 28 | 29 | typedef struct { 30 | struct ev_loop *loop; 31 | ev_timer timer; 32 | mhsm_hsm_t *hsm; 33 | uint32_t event_id; 34 | } mtmr_ev_t; 35 | 36 | int mtmr_ev_initalise_timers(mhsm_hsm_t *hsm, size_t nrof_timers, struct ev_loop *loop); 37 | 38 | #endif /* MBB_TIMER_EV_H */ 39 | -------------------------------------------------------------------------------- /mbb/timer_periodic.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "timer_periodic.h" 22 | #include "types.h" 23 | #include "hsm.h" 24 | #include "debug.h" 25 | 26 | static int start_timer(mhsm_hsm_t *hsm, uint32_t event_id, uint32_t period_msecs) 27 | { 28 | mtmr_prd_t *timers = (mtmr_prd_t*) mhsm_context(hsm); 29 | uint32_t idx = event_id - MHSM_EVENT_CUSTOM; 30 | 31 | MDBG_PRINT2("activating timer %d with period %d\n", idx, period_msecs); 32 | 33 | timers[idx].period = period_msecs; 34 | timers[idx].value = 0; 35 | timers[idx].active = 1; 36 | 37 | return 0; 38 | } 39 | 40 | int mtmr_prd_initialise_timers(mhsm_hsm_t *hsm, size_t nrof_timers) 41 | { 42 | mtmr_prd_t *timers; 43 | uint32_t i; 44 | 45 | if (hsm == NULL) return -1; 46 | 47 | timers = (mtmr_prd_t*) mhsm_context(hsm); 48 | 49 | for (i = 0; i < nrof_timers; i++) { 50 | mtmr_prd_t *timer = timers + i; 51 | 52 | timer->active = 0; 53 | timer->period = 0; 54 | timer->value = 0; 55 | } 56 | 57 | mhsm_set_timer_callback(hsm, start_timer); 58 | 59 | return 0; 60 | } 61 | 62 | int mtmr_prd_increment_timers(mhsm_hsm_t *hsm, size_t nrof_timers, uint32_t passed_msecs) 63 | { 64 | mtmr_prd_t *timers; 65 | int i; 66 | 67 | if (hsm == NULL) return -1; 68 | 69 | timers = (mtmr_prd_t*) mhsm_context(hsm); 70 | 71 | for (i = 0; i < nrof_timers; i++) { 72 | mtmr_prd_t *timer = timers + i; 73 | 74 | if (timer->active) { 75 | timer->value += passed_msecs; 76 | if (timer->value >= timer->period) { 77 | timer->active = 0; 78 | mhsm_dispatch_event(hsm, MHSM_EVENT_CUSTOM + i); 79 | } 80 | } 81 | } 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /mbb/timer_periodic.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_TIMER_PERIODIC_H 22 | #define MBB_TIMER_PERIODIC_H 23 | 24 | #include "types.h" 25 | #include "hsm.h" 26 | #include "timer_common.h" 27 | 28 | typedef struct { 29 | uint32_t period; 30 | uint32_t value; 31 | bool active; 32 | } mtmr_prd_t; 33 | 34 | int mtmr_prd_initialise_timers(mhsm_hsm_t *hsm, size_t nrof_timers); 35 | int mtmr_prd_increment_timers(mhsm_hsm_t *hsm, size_t nrof_timers, uint32_t passed_msecs); 36 | 37 | #endif /* MBB_TIMER_PERIODIC_H */ 38 | -------------------------------------------------------------------------------- /mbb/types.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MBB_TYPES_H 22 | #define MBB_TYPES_H 23 | 24 | /* 25 | * This file is included to define the following types: 26 | * * fixed-width integer types uint8_t, int8_t, uint16_t, ... 27 | * * bool 28 | * * size_t 29 | * 30 | * If your system does not have , , or , you 31 | * should define MBB_SYSTEM_TYPES_HEADER before including mbb/types.h which is 32 | * included below and expected to provide the type definitions listed above. 33 | */ 34 | 35 | #ifdef MBB_SYSTEM_TYPES_HEADER 36 | # include MBB_SYSTEM_TYPES_HEADER 37 | #else 38 | # include 39 | # include 40 | # include 41 | #endif 42 | 43 | #endif /* MBB_TYPES_H */ 44 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | SUFFIXES = .c _main.c 2 | .c_main.c: 3 | $(top_srcdir)/tools/munt_main $< > $@ 4 | 5 | bin_PROGRAMS = test_hsm test_queue 6 | nodist_test_hsm_SOURCES = test_hsm_main.c 7 | nodist_test_queue_SOURCES = test_queue_main.c 8 | AM_CPPFLAGS = -I$(top_srcdir) 9 | LDADD = $(top_builddir)/mbb/libmbb.a 10 | MOSTLYCLEANFILES = test_hsm_main.c test_queue_main.c 11 | TESTS = $(bin_PROGRAMS) 12 | -------------------------------------------------------------------------------- /tests/test_hsm.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "mbb/test.h" 22 | #include "mbb/hsm.h" 23 | #include "mbb/debug.h" 24 | #include "mbb/queue.h" 25 | 26 | typedef struct { 27 | mhsm_state_t *state; 28 | uint32_t event_id; 29 | } test_event_t; 30 | 31 | MQUE_DEFINE_STRUCT(test_event_t, 30) test_event_queue; 32 | MQUE_DEFINE_STRUCT(test_event_t, 30) expected_event_queue; 33 | 34 | #define TEST_ENQUEUE(STATE, EVENT_ID) do { \ 35 | test_event_t test_event; \ 36 | test_event.state = STATE; \ 37 | test_event.event_id = EVENT_ID; \ 38 | MQUE_ENQUEUE(&test_event_queue, test_event); \ 39 | } while (0) 40 | 41 | #define EXPECTED_ENQUEUE(STATE, EVENT_ID) do { \ 42 | test_event_t expected_event; \ 43 | expected_event.state = STATE; \ 44 | expected_event.event_id = EVENT_ID; \ 45 | MQUE_ENQUEUE(&expected_event_queue, expected_event); \ 46 | } while (0) 47 | 48 | enum { 49 | TEST_TE_EVENT_TRIGGER = MHSM_EVENT_CUSTOM 50 | }; 51 | 52 | MHSM_DEFINE_STATE(test_te_top, NULL); 53 | MHSM_DEFINE_STATE(test_te_a, &test_te_top); 54 | MHSM_DEFINE_STATE(test_te_a1, &test_te_a); 55 | MHSM_DEFINE_STATE(test_te_b, &test_te_top); 56 | MHSM_DEFINE_STATE(test_te_b1, &test_te_b); 57 | 58 | mhsm_state_t *test_te_top_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 59 | { 60 | TEST_ENQUEUE(&test_te_top, event.id); 61 | 62 | return &test_te_top; 63 | } 64 | 65 | mhsm_state_t *test_te_a_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 66 | { 67 | TEST_ENQUEUE(&test_te_a, event.id); 68 | 69 | switch (event.id) { 70 | case MHSM_EVENT_INITIAL: 71 | return &test_te_a1; 72 | } 73 | 74 | return &test_te_a; 75 | } 76 | 77 | mhsm_state_t *test_te_a1_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 78 | { 79 | TEST_ENQUEUE(&test_te_a1, event.id); 80 | 81 | switch (event.id) { 82 | case TEST_TE_EVENT_TRIGGER: 83 | return &test_te_b1; 84 | } 85 | 86 | return &test_te_a1; 87 | } 88 | 89 | mhsm_state_t *test_te_b_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 90 | { 91 | TEST_ENQUEUE(&test_te_b, event.id); 92 | 93 | switch (event.id) { 94 | case MHSM_EVENT_INITIAL: 95 | return &test_te_b1; 96 | } 97 | 98 | return &test_te_b; 99 | } 100 | 101 | mhsm_state_t *test_te_b1_fun(mhsm_hsm_t *hsm, mhsm_event_t event) 102 | { 103 | TEST_ENQUEUE(&test_te_b1, event.id); 104 | 105 | switch (event.id) { 106 | case MHSM_EVENT_ENTRY: 107 | MDBG_PRINT_LN("entry b1"); 108 | break; 109 | } 110 | 111 | return &test_te_b1; 112 | } 113 | 114 | char *test_transition_events() 115 | { 116 | mhsm_hsm_t hsm; 117 | 118 | MQUE_INITIALISE(&expected_event_queue); 119 | MQUE_INITIALISE(&test_event_queue); 120 | 121 | EXPECTED_ENQUEUE(&test_te_top, MHSM_EVENT_ENTRY); 122 | EXPECTED_ENQUEUE(&test_te_a, MHSM_EVENT_ENTRY); 123 | EXPECTED_ENQUEUE(&test_te_a, MHSM_EVENT_INITIAL); 124 | EXPECTED_ENQUEUE(&test_te_a1, MHSM_EVENT_ENTRY); 125 | EXPECTED_ENQUEUE(&test_te_a1, MHSM_EVENT_INITIAL); 126 | EXPECTED_ENQUEUE(&test_te_a1, TEST_TE_EVENT_TRIGGER); 127 | EXPECTED_ENQUEUE(&test_te_a, TEST_TE_EVENT_TRIGGER); 128 | EXPECTED_ENQUEUE(&test_te_top, TEST_TE_EVENT_TRIGGER); 129 | EXPECTED_ENQUEUE(&test_te_a1, MHSM_EVENT_EXIT); 130 | EXPECTED_ENQUEUE(&test_te_a, MHSM_EVENT_EXIT); 131 | EXPECTED_ENQUEUE(&test_te_b, MHSM_EVENT_ENTRY); 132 | EXPECTED_ENQUEUE(&test_te_b1, MHSM_EVENT_ENTRY); 133 | EXPECTED_ENQUEUE(&test_te_b1, MHSM_EVENT_INITIAL); 134 | 135 | mhsm_initialise(&hsm, NULL, &test_te_a); 136 | mhsm_dispatch_event(&hsm, MHSM_EVENT_INITIAL); 137 | 138 | MUNT_ASSERT(mhsm_current_state(&hsm) == &test_te_a1); 139 | 140 | mhsm_dispatch_event(&hsm, TEST_TE_EVENT_TRIGGER); 141 | 142 | MUNT_ASSERT(MQUE_LENGTH(&expected_event_queue) == MQUE_LENGTH(&test_event_queue)); 143 | 144 | while (MQUE_LENGTH(&expected_event_queue)) { 145 | test_event_t expected_event = MQUE_HEAD(&expected_event_queue); 146 | test_event_t test_event = MQUE_HEAD(&test_event_queue); 147 | 148 | MUNT_ASSERT(expected_event.state == test_event.state && expected_event.event_id == test_event.event_id); 149 | MQUE_DEQUEUE(&expected_event_queue); 150 | MQUE_DEQUEUE(&test_event_queue); 151 | } 152 | 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /tests/test_queue.c: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2015 Jan Weil 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "mbb/test.h" 22 | #include "mbb/queue.h" 23 | #include "mbb/debug.h" 24 | 25 | char *test_enqueue_dequeue() 26 | { 27 | int i; 28 | 29 | MQUE_DEFINE_STRUCT(int, 5) queue; 30 | 31 | MQUE_INITIALISE(&queue); 32 | 33 | MUNT_ASSERT(MQUE_IS_EMPTY(&queue)); 34 | 35 | for (i = 1; i <= 5; i++) { 36 | MUNT_ASSERT(!MQUE_IS_FULL(&queue)); 37 | MQUE_ENQUEUE(&queue, i); 38 | } 39 | 40 | MUNT_ASSERT(MQUE_IS_FULL(&queue)); 41 | 42 | for (i = 1; i <= 5; i++) { 43 | int head; 44 | 45 | MUNT_ASSERT(!MQUE_IS_EMPTY(&queue)); 46 | 47 | head = MQUE_HEAD(&queue); 48 | MUNT_ASSERT(head == i); 49 | 50 | MQUE_DEQUEUE(&queue); 51 | } 52 | 53 | MUNT_ASSERT(MQUE_IS_EMPTY(&queue)); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /tools/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_bin_SCRIPTS = mhsm_scaffold munt_main 2 | -------------------------------------------------------------------------------- /tools/mhsm_scaffold: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Copyright (C) 2015 Jan Weil 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | # this software and associated documentation files (the "Software"), to deal in 7 | # the Software without restriction, including without limitation the rights to 8 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | # the Software, and to permit persons to whom the Software is furnished to do so, 10 | # subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | # 22 | 23 | require 'optparse' 24 | 25 | options = {} 26 | options[:context_name] = "ctx" 27 | 28 | OptionParser.new do |opts| 29 | opts.banner = "Usage: #{File.basename($0)} [options] ..." 30 | 31 | opts.on("-t", "--context-type CONTEXT_TYPE", "Add CONTEXT_TYPE pointer to HSM context") do |type| 32 | options[:context_type] = type 33 | end 34 | 35 | opts.on("-n", "--context-name CONTEXT_NAME", "Set the name of the HSM context pointer") do |name| 36 | options[:context_name] = name 37 | end 38 | end.parse! 39 | 40 | ARGV.each do |file| 41 | if not File.exist?(file) 42 | puts "file '#{file}' not found" 43 | next 44 | end 45 | 46 | puts "processing #{file}" 47 | 48 | states = [] 49 | File.readlines(file).each do |line| 50 | if line =~ /^MHSM_DEFINE_STATE\(([a-zA-Z0-9_]+),[^)]*\);.*/ 51 | states.push($1) 52 | end 53 | end 54 | 55 | open(file, 'a') do |f| 56 | f.puts "\n" 57 | for state in states do 58 | f.puts < 1 ? "tests" : "test"); 80 | } 81 | 82 | exit(result == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 83 | } 84 | EOC 85 | --------------------------------------------------------------------------------