1 /*
2 * Copyright (c) 2024 Glenn Andrews
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/smf.h>
9 #include <stdbool.h>
10 #include "smf_calculator_thread.h"
11 #include <zephyr/logging/log.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include "main.h"
15 #include <stdio.h>
16
17 LOG_MODULE_REGISTER(smf_thread, LOG_LEVEL_DBG);
18
19 K_MSGQ_DEFINE(event_msgq, sizeof(struct calculator_event), 8, 1);
20
21 /* In theory can still overflow. A double can be hundreds of characters long */
22 #define RESULT_STRING_LENGTH 64
23
24 enum display_mode {
25 DISPLAY_OPERAND_1,
26 DISPLAY_OPERAND_2,
27 DISPLAY_RESULT,
28 DISPLAY_ERROR,
29 };
30
31 struct operand {
32 char string[CALCULATOR_STRING_LENGTH];
33 int index;
34 };
35
36 /* User defined object */
37 struct s_object {
38 /* This must be first */
39 struct smf_ctx ctx;
40
41 /* Other state specific data add here */
42 struct calculator_event event;
43
44 struct operand operand_1;
45 struct operand operand_2;
46 char operator_btn;
47 struct operand result;
48 } s_obj;
49
50 static enum display_mode current_display_mode = DISPLAY_OPERAND_1;
51
set_display_mode(enum display_mode mode)52 static void set_display_mode(enum display_mode mode)
53 {
54 current_display_mode = mode;
55 }
56
setup_operand(struct operand * op)57 static void setup_operand(struct operand *op)
58 {
59 /* indexes start at 1 because position 0 is the sign */
60 op->index = 1;
61 op->string[0] = ' '; /* space for sign */
62 op->string[1] = '0'; /* A 0 indicator to be overwritten */
63 op->string[2] = 0x00;
64 }
65
insert(struct operand * op,char digit)66 static int insert(struct operand *op, char digit)
67 {
68 if (op->index >= (CALCULATOR_STRING_LENGTH - 1)) {
69 /* Can't store an extra operand */
70 return -ENOBUFS;
71 }
72 op->string[op->index++] = digit;
73 op->string[op->index] = 0x00;
74
75 return 0;
76 }
77
negate(struct operand * op)78 static void negate(struct operand *op)
79 {
80 if (op->string[0] == ' ') {
81 op->string[0] = '-';
82 } else {
83 op->string[0] = ' ';
84 }
85 }
86
87 /**
88 * @brief copies one operand struct to another
89 * Assumes src operand is well-formed
90 */
copy_operand(struct operand * dest,struct operand * src)91 static void copy_operand(struct operand *dest, struct operand *src)
92 {
93 strncpy(dest->string, src->string, CALCULATOR_STRING_LENGTH);
94 dest->index = src->index;
95 }
96
calculate_result(struct s_object * s)97 static int calculate_result(struct s_object *s)
98 {
99 double operand_1 = strtod(s->operand_1.string, NULL);
100 double operand_2 = strtod(s->operand_2.string, NULL);
101 double result;
102 char result_string[RESULT_STRING_LENGTH];
103
104 switch (s->operator_btn) {
105 case '+':
106 result = operand_1 + operand_2;
107 break;
108 case '-':
109 result = operand_1 - operand_2;
110 break;
111 case '*':
112 result = operand_1 * operand_2;
113 break;
114 case '/':
115 if (operand_2 != 0.0) {
116 result = operand_1 / operand_2;
117 } else {
118 /* division by zero */
119 return -1;
120 }
121 break;
122 default:
123 /* unknown operator */
124 return -1;
125 }
126
127 snprintf(result_string, RESULT_STRING_LENGTH, "% f", result);
128
129 /* Strip off trailing zeroes from result */
130 for (int i = strlen(result_string) - 1; i >= 0; i--) {
131 if (result_string[i] != '0') {
132 /* was it .000? */
133 if (result_string[i] == '.') {
134 result_string[i] = 0x00;
135 } else {
136 result_string[i + 1] = 0x00;
137 }
138 break;
139 }
140 }
141
142 /* copy to the result operand */
143 strncpy(s->result.string, result_string, CALCULATOR_STRING_LENGTH - 1);
144 s->result.string[CALCULATOR_STRING_LENGTH - 1] = 0x00;
145 s->result.index = strlen(s->result.string);
146
147 return 0;
148 }
149
150 /* copy result into operand_1 and clear operand_2 to allow chaining calculations */
chain_calculations(struct s_object * s)151 static void chain_calculations(struct s_object *s)
152 {
153 copy_operand(&s->operand_1, &s->result);
154 setup_operand(&s->operand_2);
155 }
156
157 /* Declaration of possible states */
158 enum demo_states {
159 STATE_ON,
160 STATE_READY,
161 STATE_RESULT,
162 STATE_BEGIN,
163 STATE_NEGATED_1,
164 STATE_OPERAND_1,
165 STATE_ZERO_1,
166 STATE_INT_1,
167 STATE_FRAC_1,
168 STATE_NEGATED_2,
169 STATE_OPERAND_2,
170 STATE_ZERO_2,
171 STATE_INT_2,
172 STATE_FRAC_2,
173 STATE_OP_ENTERED,
174 STATE_OP_CHAINED,
175 STATE_OP_NORMAL,
176 STATE_ERROR,
177 };
178
179 /* Forward declaration of state table */
180 static const struct smf_state calculator_states[];
181
on_entry(void * obj)182 static void on_entry(void *obj)
183 {
184 struct s_object *s = (struct s_object *)obj;
185
186 LOG_DBG("");
187
188 setup_operand(&s->operand_1);
189 setup_operand(&s->operand_2);
190 setup_operand(&s->result);
191 s->operator_btn = 0x00;
192 }
193
on_run(void * obj)194 static void on_run(void *obj)
195 {
196 struct s_object *s = (struct s_object *)obj;
197
198 LOG_DBG("");
199
200 switch (s->event.event_id) {
201 case CANCEL_BUTTON:
202 smf_set_state(&s->ctx, &calculator_states[STATE_ON]);
203 break;
204 default:
205 /* Let parent state handle it: shuts up compiler warning */
206 break;
207 }
208 }
209
ready_run(void * obj)210 static void ready_run(void *obj)
211 {
212 struct s_object *s = (struct s_object *)obj;
213
214 LOG_DBG("");
215
216 switch (s->event.event_id) {
217 case DECIMAL_POINT:
218 insert(&s->operand_1, s->event.operand);
219 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_1]);
220 break;
221 case DIGIT_1_9:
222 insert(&s->operand_1, s->event.operand);
223 smf_set_state(&s->ctx, &calculator_states[STATE_INT_1]);
224 break;
225 case DIGIT_0:
226 /* Don't insert the leading zero */
227 smf_set_state(&s->ctx, &calculator_states[STATE_ZERO_1]);
228 break;
229 case OPERATOR:
230 s->operator_btn = s->event.operand;
231 smf_set_state(&s->ctx, &calculator_states[STATE_OP_CHAINED]);
232 break;
233 default:
234 /* Let parent state handle it: shuts up compiler warning */
235 break;
236 }
237 }
result_entry(void * obj)238 static void result_entry(void *obj)
239 {
240 LOG_DBG("");
241 set_display_mode(DISPLAY_RESULT);
242 }
243
result_run(void * obj)244 static void result_run(void *obj)
245 {
246 LOG_DBG("");
247 /* Ready state handles the exits from this state */
248 }
249
begin_entry(void * obj)250 static void begin_entry(void *obj)
251 {
252 LOG_DBG("");
253 set_display_mode(DISPLAY_OPERAND_1);
254 }
255
begin_run(void * obj)256 static void begin_run(void *obj)
257 {
258 struct s_object *s = (struct s_object *)obj;
259
260 LOG_DBG("");
261
262 switch (s->event.event_id) {
263 case OPERATOR:
264 /* We only care about negative */
265 if (s->event.operand == '-') {
266 smf_set_state(&s->ctx, &calculator_states[STATE_NEGATED_1]);
267 }
268 break;
269 default:
270 /* Let parent state handle it: shuts up compiler warning */
271 break;
272 }
273 }
274
negated_1_entry(void * obj)275 static void negated_1_entry(void *obj)
276 {
277 struct s_object *s = (struct s_object *)obj;
278
279 LOG_DBG("");
280
281 negate(&s->operand_1);
282 }
283
negated_1_run(void * obj)284 static void negated_1_run(void *obj)
285 {
286 struct s_object *s = (struct s_object *)obj;
287
288 LOG_DBG("");
289
290 switch (s->event.event_id) {
291 case DECIMAL_POINT:
292 insert(&s->operand_1, s->event.operand);
293 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_1]);
294 break;
295 case DIGIT_1_9:
296 insert(&s->operand_1, s->event.operand);
297 smf_set_state(&s->ctx, &calculator_states[STATE_INT_1]);
298 break;
299 case DIGIT_0:
300 /* Don't need to insert leading zeroes */
301 smf_set_state(&s->ctx, &calculator_states[STATE_ZERO_1]);
302 break;
303 case OPERATOR:
304 /* We only care about ignoring the negative key */
305 if (s->event.operand == '-') {
306 smf_set_handled(&s->ctx);
307 }
308 break;
309 case CANCEL_ENTRY:
310 setup_operand(&s->operand_1);
311 smf_set_state(&s->ctx, &calculator_states[STATE_BEGIN]);
312 break;
313 default:
314 /* Let parent state handle it: shuts up compiler warning */
315 break;
316 }
317 }
318
operand_1_entry(void * obj)319 static void operand_1_entry(void *obj)
320 {
321 LOG_DBG("");
322 set_display_mode(DISPLAY_OPERAND_1);
323 }
324
operand_1_run(void * obj)325 static void operand_1_run(void *obj)
326 {
327 struct s_object *s = (struct s_object *)obj;
328
329 LOG_DBG("");
330
331 switch (s->event.event_id) {
332 case OPERATOR:
333 s->operator_btn = s->event.operand;
334 smf_set_state(&s->ctx, &calculator_states[STATE_OP_ENTERED]);
335 break;
336 case CANCEL_ENTRY:
337 setup_operand(&s->operand_1);
338 smf_set_state(&s->ctx, &calculator_states[STATE_READY]);
339 break;
340 default:
341 /* Let parent state handle it: shuts up compiler warning */
342 break;
343 }
344 }
345
zero_1_run(void * obj)346 static void zero_1_run(void *obj)
347 {
348 struct s_object *s = (struct s_object *)obj;
349
350 LOG_DBG("");
351
352 switch (s->event.event_id) {
353 case DIGIT_0:
354 /* We ignore leading zeroes */
355 smf_set_handled(&s->ctx);
356 break;
357 case DIGIT_1_9:
358 insert(&s->operand_1, s->event.operand);
359 smf_set_state(&s->ctx, &calculator_states[STATE_INT_1]);
360 break;
361 case DECIMAL_POINT:
362 insert(&s->operand_1, s->event.operand);
363 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_1]);
364 break;
365 default:
366 /* Let parent state handle it: shuts up compiler warning */
367 break;
368 }
369 }
370
int_1_run(void * obj)371 static void int_1_run(void *obj)
372 {
373 struct s_object *s = (struct s_object *)obj;
374
375 LOG_DBG("");
376
377 switch (s->event.event_id) {
378 case DIGIT_0:
379 case DIGIT_1_9:
380 insert(&s->operand_1, s->event.operand);
381 smf_set_handled(&s->ctx);
382 break;
383 case DECIMAL_POINT:
384 insert(&s->operand_1, s->event.operand);
385 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_1]);
386 break;
387 default:
388 /* Let parent state handle it: shuts up compiler warning */
389 break;
390 }
391 }
392
frac_1_run(void * obj)393 static void frac_1_run(void *obj)
394 {
395 struct s_object *s = (struct s_object *)obj;
396
397 LOG_DBG("");
398
399 switch (s->event.event_id) {
400 case DIGIT_0:
401 case DIGIT_1_9:
402 insert(&s->operand_1, s->event.operand);
403 smf_set_handled(&s->ctx);
404 break;
405 case DECIMAL_POINT:
406 /* Ignore further decimal points */
407 smf_set_handled(&s->ctx);
408 break;
409 default:
410 /* Let parent state handle it: shuts up compiler warning */
411 break;
412 }
413 }
414
negated_2_entry(void * obj)415 static void negated_2_entry(void *obj)
416 {
417 struct s_object *s = (struct s_object *)obj;
418
419 LOG_DBG("");
420
421 negate(&s->operand_2);
422 }
423
negated_2_run(void * obj)424 static void negated_2_run(void *obj)
425 {
426 struct s_object *s = (struct s_object *)obj;
427
428 LOG_DBG("");
429
430 switch (s->event.event_id) {
431 case DECIMAL_POINT:
432 insert(&s->operand_2, s->event.operand);
433 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_2]);
434 break;
435 case DIGIT_1_9:
436 insert(&s->operand_2, s->event.operand);
437 smf_set_state(&s->ctx, &calculator_states[STATE_INT_2]);
438 break;
439 case DIGIT_0:
440 /* Don't insert the leading zero */
441 smf_set_state(&s->ctx, &calculator_states[STATE_ZERO_2]);
442 break;
443 case OPERATOR:
444 /* We only care about ignoring the negative key */
445 if (s->event.operand == '-') {
446 smf_set_handled(&s->ctx);
447 }
448 break;
449 case CANCEL_ENTRY:
450 setup_operand(&s->operand_2);
451 smf_set_state(&s->ctx, &calculator_states[STATE_OP_ENTERED]);
452 break;
453 default:
454 /* Let parent state handle it: shuts up compiler warning */
455 break;
456 }
457 }
458
operand_2_entry(void * obj)459 static void operand_2_entry(void *obj)
460 {
461 LOG_DBG("");
462 set_display_mode(DISPLAY_OPERAND_2);
463 }
464
operand_2_run(void * obj)465 static void operand_2_run(void *obj)
466 {
467 struct s_object *s = (struct s_object *)obj;
468
469 LOG_DBG("");
470
471 switch (s->event.event_id) {
472 case CANCEL_ENTRY:
473 setup_operand(&s->operand_2);
474 smf_set_state(&s->ctx, &calculator_states[STATE_OP_ENTERED]);
475 break;
476 case OPERATOR:
477 if (calculate_result(s) == 0) {
478 /* move result into operand_1 to allow chaining */
479 chain_calculations(s);
480 /* Copy in new operand */
481 s->operator_btn = s->event.operand;
482 smf_set_state(&s->ctx, &calculator_states[STATE_OP_CHAINED]);
483 } else {
484 smf_set_state(&s->ctx, &calculator_states[STATE_ERROR]);
485 }
486 break;
487 case EQUALS:
488 if (calculate_result(s) == 0) {
489 /* move result into operand_1 to allow chaining */
490 chain_calculations(s);
491 smf_set_state(&s->ctx, &calculator_states[STATE_RESULT]);
492 } else {
493 smf_set_state(&s->ctx, &calculator_states[STATE_ERROR]);
494 }
495 break;
496 default:
497 /* Let parent state handle it: shuts up compiler warning */
498 break;
499 }
500 }
501
zero_2_run(void * obj)502 static void zero_2_run(void *obj)
503 {
504 struct s_object *s = (struct s_object *)obj;
505
506 LOG_DBG("");
507
508 switch (s->event.event_id) {
509 case DIGIT_0:
510 /* We ignore leading zeroes */
511 smf_set_handled(&s->ctx);
512 break;
513 case DIGIT_1_9:
514 insert(&s->operand_2, s->event.operand);
515 smf_set_state(&s->ctx, &calculator_states[STATE_INT_2]);
516 break;
517 case DECIMAL_POINT:
518 insert(&s->operand_2, s->event.operand);
519 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_2]);
520 break;
521 default:
522 /* Let parent state handle it: shuts up compiler warning */
523 break;
524 }
525 }
526
int_2_run(void * obj)527 static void int_2_run(void *obj)
528 {
529 struct s_object *s = (struct s_object *)obj;
530
531 LOG_DBG("");
532
533 switch (s->event.event_id) {
534 case DIGIT_0:
535 case DIGIT_1_9:
536 insert(&s->operand_2, s->event.operand);
537 smf_set_handled(&s->ctx);
538 break;
539 case DECIMAL_POINT:
540 insert(&s->operand_2, s->event.operand);
541 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_2]);
542 break;
543 default:
544 /* Let parent state handle it: shuts up compiler warning */
545 break;
546 }
547 }
548
frac_2_run(void * obj)549 static void frac_2_run(void *obj)
550 {
551 struct s_object *s = (struct s_object *)obj;
552
553 LOG_DBG("");
554
555 switch (s->event.event_id) {
556 case DIGIT_0:
557 case DIGIT_1_9:
558 insert(&s->operand_2, s->event.operand);
559 smf_set_handled(&s->ctx);
560 break;
561 case DECIMAL_POINT:
562 /* Ignore further decimal points */
563 smf_set_handled(&s->ctx);
564 break;
565 default:
566 /* Let parent state handle it: shuts up compiler warning */
567 break;
568 }
569 }
570
op_entered_run(void * obj)571 static void op_entered_run(void *obj)
572 {
573 struct s_object *s = (struct s_object *)obj;
574
575 LOG_DBG("");
576
577 switch (s->event.event_id) {
578 case DIGIT_0:
579 /* Don't insert the leading zero */
580 smf_set_state(&s->ctx, &calculator_states[STATE_ZERO_2]);
581 break;
582 case DIGIT_1_9:
583 insert(&s->operand_2, s->event.operand);
584 smf_set_state(&s->ctx, &calculator_states[STATE_INT_2]);
585 break;
586 case DECIMAL_POINT:
587 insert(&s->operand_2, s->event.operand);
588 smf_set_state(&s->ctx, &calculator_states[STATE_FRAC_2]);
589 break;
590 case OPERATOR:
591 /* We only care about negative */
592 if (s->event.operand == '-') {
593 smf_set_state(&s->ctx, &calculator_states[STATE_NEGATED_2]);
594 }
595 break;
596 default:
597 /* Let parent state handle it: shuts up compiler warning */
598 break;
599 }
600 }
601
op_chained_entry(void * obj)602 static void op_chained_entry(void *obj)
603 {
604 LOG_DBG("");
605 set_display_mode(DISPLAY_OPERAND_1);
606 }
607
op_normal_entry(void * obj)608 static void op_normal_entry(void *obj)
609 {
610 LOG_DBG("");
611 set_display_mode(DISPLAY_OPERAND_2);
612 }
613
error_entry(void * obj)614 static void error_entry(void *obj)
615 {
616 LOG_DBG("");
617 set_display_mode(DISPLAY_ERROR);
618 }
619
620 /* State storage: handler functions, parent states and initial transition states */
621 /* clang-format off */
622 static const struct smf_state calculator_states[] = {
623 [STATE_ON] = SMF_CREATE_STATE(on_entry, on_run, NULL,
624 NULL, &calculator_states[STATE_READY]),
625 [STATE_READY] = SMF_CREATE_STATE(NULL, ready_run, NULL,
626 &calculator_states[STATE_ON],
627 &calculator_states[STATE_BEGIN]),
628 [STATE_RESULT] = SMF_CREATE_STATE(result_entry, result_run,
629 NULL, &calculator_states[STATE_READY], NULL),
630 [STATE_BEGIN] = SMF_CREATE_STATE(begin_entry, begin_run, NULL,
631 &calculator_states[STATE_READY], NULL),
632 [STATE_NEGATED_1] = SMF_CREATE_STATE(negated_1_entry, negated_1_run, NULL,
633 &calculator_states[STATE_ON], NULL),
634 [STATE_OPERAND_1] = SMF_CREATE_STATE(operand_1_entry, operand_1_run, NULL,
635 &calculator_states[STATE_ON], NULL),
636 [STATE_ZERO_1] = SMF_CREATE_STATE(NULL, zero_1_run, NULL,
637 &calculator_states[STATE_OPERAND_1], NULL),
638 [STATE_INT_1] = SMF_CREATE_STATE(NULL, int_1_run, NULL,
639 &calculator_states[STATE_OPERAND_1], NULL),
640 [STATE_FRAC_1] = SMF_CREATE_STATE(NULL, frac_1_run, NULL,
641 &calculator_states[STATE_OPERAND_1], NULL),
642 [STATE_NEGATED_2] = SMF_CREATE_STATE(negated_2_entry, negated_2_run, NULL,
643 &calculator_states[STATE_ON], NULL),
644 [STATE_OPERAND_2] = SMF_CREATE_STATE(operand_2_entry, operand_2_run, NULL,
645 &calculator_states[STATE_ON], NULL),
646 [STATE_ZERO_2] = SMF_CREATE_STATE(NULL, zero_2_run, NULL,
647 &calculator_states[STATE_OPERAND_2], NULL),
648 [STATE_INT_2] = SMF_CREATE_STATE(NULL, int_2_run, NULL,
649 &calculator_states[STATE_OPERAND_2], NULL),
650 [STATE_FRAC_2] = SMF_CREATE_STATE(NULL, frac_2_run, NULL,
651 &calculator_states[STATE_OPERAND_2], NULL),
652 [STATE_OP_ENTERED] = SMF_CREATE_STATE(NULL, op_entered_run, NULL,
653 &calculator_states[STATE_ON],
654 &calculator_states[STATE_OP_NORMAL]),
655 [STATE_OP_CHAINED] = SMF_CREATE_STATE(op_chained_entry, NULL, NULL,
656 &calculator_states[STATE_OP_ENTERED], NULL),
657 [STATE_OP_NORMAL] = SMF_CREATE_STATE(op_normal_entry, NULL, NULL,
658 &calculator_states[STATE_OP_ENTERED], NULL),
659 [STATE_ERROR] = SMF_CREATE_STATE(error_entry, NULL, NULL,
660 &calculator_states[STATE_ON], NULL),
661 };
662 /* clang-format on */
663
post_calculator_event(struct calculator_event * event,k_timeout_t timeout)664 int post_calculator_event(struct calculator_event *event, k_timeout_t timeout)
665 {
666 return k_msgq_put(&event_msgq, event, timeout);
667 }
668
output_display(void)669 static void output_display(void)
670 {
671 char *output;
672
673 switch (current_display_mode) {
674 case DISPLAY_OPERAND_1:
675 output = s_obj.operand_1.string;
676 break;
677 case DISPLAY_OPERAND_2:
678 output = s_obj.operand_2.string;
679 break;
680 case DISPLAY_RESULT:
681 output = s_obj.result.string;
682 break;
683 case DISPLAY_ERROR:
684 output = "ERROR";
685 break;
686 default:
687 output = "";
688 }
689 update_display(output);
690 }
691
smf_calculator_thread(void * arg1,void * arg2,void * arg3)692 static void smf_calculator_thread(void *arg1, void *arg2, void *arg3)
693 {
694 smf_set_initial(SMF_CTX(&s_obj), &calculator_states[STATE_ON]);
695 while (1) {
696 int rc;
697
698 rc = k_msgq_get(&event_msgq, &s_obj.event, K_FOREVER);
699 if (rc != 0) {
700 continue;
701 }
702 /* run state machine with given message */
703
704 LOG_INF("Received %c from GUI", s_obj.event.operand);
705 int ret = smf_run_state(SMF_CTX(&s_obj));
706
707 if (ret) {
708 /* State machine was terminated if a non-zero value is returned */
709 break;
710 }
711
712 output_display();
713 LOG_INF("op1=%s, op=%c op2=%s res=%s", s_obj.operand_1.string, s_obj.operator_btn,
714 s_obj.operand_2.string, s_obj.result.string);
715 }
716 }
717
718 K_THREAD_DEFINE(smf_calculator, SMF_THREAD_STACK_SIZE, smf_calculator_thread, NULL, NULL, NULL,
719 SMF_THREAD_PRIORITY, 0, 0);
720