├── 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 |
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 |
--------------------------------------------------------------------------------