Something went wrong. Try again.
Behaviour Tree for memory constrained devices. icpmoles.tngl.io/orchard
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176/* * SPDX-License-Identifier: MIT * * C++ example: the example tree driving a simulated board through orchard.hpp, * with the leaves as member functions of a class. * * orchard_cpp_demo [tree.json] load the tree from a file * orchard_cpp_demo --code use the same tree, built by the compiler * * Instead of reading the keyboard, it plays a fixed timeline of button presses * on a simulated clock, so a run takes no time and always prints the same * thing. Each line is a button event or a change of the LEDs. */
#include <array>#include <cstdint>#include <cstdio>#include <cstdlib>#include <cstring>#include <stdexcept>
#include <orchard/orchard.hpp>#include <orchard/orchard_report.h>
namespace {
constexpr uint32_t tick_ms = 10;
/* Simulated time. The clock passed to the tree is a plain function, so it reads a global */orchard_time_t sim_now_ms = 0;
orchard_time_t sim_clock() { return sim_now_ms; }
class Board {public: /* * The leaves. arg comes from the tree file, not from the compiler, so * at() checks it: a bad index throws, and orchard::member_leaf turns * that into FAILURE. */ orchard_status button(int32_t arg, uint32_t *) { return buttons_.at(index(arg)) ? ORCHARD_SUCCESS : ORCHARD_FAILURE; }
orchard_status led_on(int32_t arg, uint32_t *) { return set_led(arg, true); }
orchard_status led_off(int32_t arg, uint32_t *) { return set_led(arg, false); }
orchard_status led_toggle(int32_t arg, uint32_t *) { return set_led(arg, !leds_.at(index(arg))); }
void press(std::size_t id, bool pressed) { buttons_.at(id) = pressed; }
/* Print the LEDs if they changed since the last call */ void show_leds(orchard_time_t now) { if (leds_ == shown_) { return; } shown_ = leds_; std::printf("%6lu ms leds ", static_cast<unsigned long>(now)); for (bool on : leds_) { std::putchar(on ? '*' : '.'); } std::putchar('\n'); }
private: static std::size_t index(int32_t arg) { if (arg < 0) { throw std::out_of_range("negative index"); } return static_cast<std::size_t>(arg); }
orchard_status set_led(int32_t arg, bool on) { leds_.at(index(arg)) = on; return ORCHARD_SUCCESS; }
std::array<bool, 3> buttons_{}; std::array<bool, 5> leds_{}; std::array<bool, 5> shown_{{true, true, true, true, true}}; /* force a first print */};
/* The Board is the tree's user pointer; member_leaf calls the method on it */constexpr orchard_leaf_def leaves[] = { {"button", orchard::member_leaf<Board, &Board::button>}, {"led_on", orchard::member_leaf<Board, &Board::led_on>}, {"led_off", orchard::member_leaf<Board, &Board::led_off>}, {"led_toggle", orchard::member_leaf<Board, &Board::led_toggle>},};
/* * The tree of tree.json, for --code. The compiler checks it and lays it out: * a misspelled leaf name or a negative wait is a build error, and loading it * cannot fail. */using namespace orchard;constexpr auto example_tree = compile( leaves, reactive_fallback( /* Hold button 0: LED 1 blinks fast */ reactive_sequence(condition("button", 0), sequence(action("led_on", 1), wait(100), action("led_off", 1), wait(100))), /* Hold button 1 (but not button 2): LEDs 2 and 3 on */ reactive_sequence(condition("button", 1), invert(condition("button", 2)), action("led_on", 2), action("led_on", 3)), /* Idle: clear the outputs, heartbeat on LED 0 */ sequence(action("led_off", 1), action("led_off", 2), action("led_off", 3), action("led_on", 0), wait(500), action("led_off", 0), wait(500))));
struct Event { uint32_t at_ms; std::size_t button; bool pressed;};
/* Each branch of the tree in turn: blink, LEDs 2 and 3, blocked by button 2, idle */const Event timeline[] = { {1200, 0, true}, {1700, 0, false}, {2200, 1, true}, {2600, 2, true}, {2900, 1, false}, {2900, 2, false},};constexpr uint32_t end_ms = 3500;
Board board;Tree<64> tree(leaves, &board, sim_clock);char file_buf[4096];
} // namespace
int main(int argc, char **argv){ const char *path = (argc > 1) ? argv[1] : "examples/posix/tree.json";
orchard::set_exception_handler( [](const char *what) { std::fprintf(stderr, "leaf failed: %s\n", what); });
if (std::strcmp(path, "--code") == 0) { tree.load(example_tree); path = "the tree built by the compiler"; } else { orchard_json_error err; int rc = tree.load_json_file(path, file_buf, &err);
if (rc != 0) { std::fprintf(stderr, "%s:%u:%u: %s (%d)\n", path, err.line, err.col, err.msg, rc); return EXIT_FAILURE; } } std::printf("loaded %s: %zu/%zu nodes\n", path, tree.node_count(), tree.capacity());
const Event *next = timeline; const Event *const last = timeline + sizeof(timeline) / sizeof(timeline[0]);
for (sim_now_ms = 0; sim_now_ms <= end_ms; sim_now_ms += tick_ms) { for (; next != last && next->at_ms <= sim_now_ms; ++next) { board.press(next->button, next->pressed); std::printf("%6lu ms button %zu %s\n", static_cast<unsigned long>(sim_now_ms), next->button, next->pressed ? "pressed" : "released"); } tree.tick(); board.show_leds(sim_now_ms); }
std::printf("\ntree after the last tick:\n"); orchard_print(tree.get(), [](void *, const char *line) { std::puts(line); }, nullptr); return EXIT_SUCCESS;}