### Start HSM Execution Source: https://github.com/tinverse/tsm/blob/main/README.md Initializes and starts the periodic state machine. ```cpp int main() { TrafficLightHsm hsm; hsm.start(); // ... } ``` -------------------------------- ### Build and Test tsm Project with CMake Source: https://github.com/tinverse/tsm/blob/main/README.md Provides command-line instructions for cloning the tsm repository, configuring the build using CMake with the Ninja generator, installing the project, and running the tests. Assumes necessary build tools like CMake, Ninja, and Graphviz are installed. ```console git clone https://github.com/tinverse/tsm.git cd tsm; mkdir build; cd build # cmake .. && make #if you want to use make cmake -GNinja -DCMAKE_INSTALL_PREFIX=${HOME}/usr .. && ninja install # run tests ./bin/tsm_test ``` -------------------------------- ### Define Concurrent State Machine with Multiple Policies Source: https://github.com/tinverse/tsm/blob/main/README.md Illustrates creating a concurrent state machine using `make_concurrent_hsm_t`. This example shows how to combine different execution policies, such as `ClockedHsm` and `ThreadedExecutionPolicy`, for concurrent state management. ```cpp template using ThreadedClockedHsm = ThreadedExecutionPolicy>; // Both ParkAveLights and FifthAveLights can be `tick`-ed. ie. they own their own clocks and operate // concurrently. using type = make_concurrent_hsm_t; ``` -------------------------------- ### Execute State Machine Asynchronously Source: https://github.com/tinverse/tsm/blob/main/README.md Use ThreadedHsm to run the state machine in a dedicated thread, requiring explicit start and stop calls. ```cpp using SwitchSM = ThreadedHsm; int main() { SwitchSM s; s.start(); // Starts the state machine/event processing thread. s.sendEvent(s.toggle); // ... s.stop(); // Shuts down the event processing thread. } ``` -------------------------------- ### Implement Asynchronous Threaded Execution Source: https://context7.com/tinverse/tsm/llms.txt Utilizes ThreadedHsm for background event processing, requiring explicit start and stop calls to manage the worker thread. ```cpp #include #include #include struct SwitchContext { struct Toggle {}; struct Off {}; struct On {}; using transitions = std::tuple< tsm::detail::Transition, tsm::detail::Transition >; }; // Create an asynchronous state machine using ThreadedSwitchSM = tsm::ThreadedHsm; int main() { ThreadedSwitchSM sm; // Start the event processing thread sm.start(); // Send events - they are processed automatically in the background sm.send_event(SwitchContext::Toggle{}); // Allow time for event processing std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Verify state change assert(std::holds_alternative(sm.current_state_)); // Send another event sm.send_event(SwitchContext::Toggle{}); std::this_thread::sleep_for(std::chrono::milliseconds(10)); assert(std::holds_alternative(sm.current_state_)); // Stop the event processing thread sm.stop(); return 0; } ``` -------------------------------- ### Build and test with Nix Source: https://github.com/tinverse/tsm/blob/main/README.md Standard workflow for building and running tests using Nix and Ninja. ```console nix-shell cd build ninja tsm_test ``` -------------------------------- ### Run build script with Nix Source: https://github.com/tinverse/tsm/blob/main/README.md Executes the build process within a Nix shell environment. ```console nix-shell --run ./build.sh ``` -------------------------------- ### Build TSM and Projects Source: https://context7.com/tinverse/tsm/llms.txt Commands for building TSM from source using CMake/Ninja or using Nix for environment management. ```bash # Build TSM from source git clone https://github.com/tinverse/tsm.git cd tsm && mkdir build && cd build cmake -GNinja -DCMAKE_INSTALL_PREFIX=${HOME}/usr .. ninja install # Build your project cd /path/to/your/project mkdir build && cd build cmake -Dtsm_DIR=${HOME}/usr/lib/cmake/tsm .. make # Or using Nix (recommended) nix-shell --run ./build.sh ``` -------------------------------- ### Run and Verify Concurrent State Machines Source: https://context7.com/tinverse/tsm/llms.txt Initializes a concurrent Broadway traffic light system, accesses individual traffic light HSMs, and simulates time ticks to verify state transitions. ```cpp int main() { using BroadwayHsm = CityStreet::Broadway::type; BroadwayHsm hsm; // Access individual HSMs from the tuple auto& park_ave = std::get<0>(hsm.hsms_); auto& fifth_ave = std::get<1>(hsm.hsms_); // Both start in G1 state assert(std::holds_alternative(park_ave.current_state_)); assert(std::holds_alternative(fifth_ave.current_state_)); // Tick all concurrent HSMs simultaneously for (int i = 0; i < 30; i++) { hsm.tick(); // Sends tick to all HSMs } // Both transition to Y1 assert(std::holds_alternative(park_ave.current_state_)); assert(std::holds_alternative(fifth_ave.current_state_)); return 0; } ``` -------------------------------- ### Implement Transitions with Guards and Actions in C++ Source: https://context7.com/tinverse/tsm/llms.txt Defines a state machine context with guard functions to validate transitions and action functions to execute side effects. Uses tsm::detail::Transition to bind these to specific state changes. ```cpp #include struct SwitchWithActionsContext { struct Off {}; struct On {}; struct Toggle {}; // Member variables to track state int on_count_{0}; bool enabled_{true}; // Guard function - returns true if transition is allowed bool canTurnOn() { return enabled_; } // Action function - called during transition void onTurnOn() { on_count_++; } int on_count() const { return on_count_; } // Transition with action and guard: Transition using transitions = std::tuple< tsm::detail::Transition, tsm::detail::Transition >; }; using SwitchWithActions = tsm::detail::make_hsm_t; int main() { SwitchWithActions hsm; // Verify initial state assert(std::holds_alternative(hsm.current_state_)); // Handle event directly (synchronous) hsm.handle(SwitchWithActionsContext::Toggle{}); // Action was called, counter incremented assert(hsm.on_count() == 1); assert(std::holds_alternative(hsm.current_state_)); // Toggle back to Off hsm.handle(SwitchWithActionsContext::Toggle{}); assert(std::holds_alternative(hsm.current_state_)); return 0; } ``` -------------------------------- ### Instantiate and Interact with a Traffic Light HSM Source: https://github.com/tinverse/tsm/blob/main/README.md Demonstrates instantiating a threaded hierarchical state machine for a traffic light and sending an event to trigger an emergency mode transition. Asserts the current state after the event. ```cpp using LightHsm = ThreadedHsm; REQUIRE(std::holds_alternative(hsm.current_state_)); hsm.send_event(TrafficLight::TrafficLightHsmContext::EmergencySwitchOn()); REQUIRE(std::holds_alternative(current_hsm->current_state_)); ``` -------------------------------- ### Create Concurrent Traffic Light System Source: https://context7.com/tinverse/tsm/llms.txt Sets up a concurrent state machine system for a city street, managing two independent traffic light systems (Park Avenue and Fifth Avenue) using threaded execution for each. ```cpp namespace CityStreet { struct Broadway { // Two independent traffic light systems using ParkAveLights = TrafficLight::LightContext; using FifthAveLights = TrafficLight::LightContext; // Wrap each HSM with ThreadedExecutionPolicy around ClockedHsm template using ThreadedClockedHsm = tsm::detail::ThreadedExecutionPolicy< tsm::detail::ClockedHsm>; // Create concurrent HSM with independent clocks using type = tsm::detail::make_concurrent_hsm_t< ThreadedClockedHsm, ParkAveLights, FifthAveLights>; }; } // namespace CityStreet ``` -------------------------------- ### Traffic Light Context with Entry and Guard Functions Source: https://github.com/tinverse/tsm/blob/main/README.md A more complete implementation of the LightContext for a traffic light, defining entry and guard functions for each state (G1, Y1, G2, Y2). G2's guard includes logic for the walk signal. ```cpp namespace TrafficLightAG { struct LightContext { struct G1 { void entry(LightContext&, ClockTickEvent& t) { t.ticks_ = 0; } bool guard(LightContext&, ClockTickEvent& t) { return t.ticks_ >= 30; } }; struct Y1 { bool guard(LightContext&, ClockTickEvent& t) { return t.ticks_ >= 5; } }; struct G2 { void entry(LightContext&, ClockTickEvent& t) { t.ticks_ = 0; } bool guard(LightContext& l, ClockTickEvent& t) { return t.ticks_ >= 60 || (l.walk_pressed_ && t.ticks_ >= 30); } }; struct Y2 { void entry(LightContext&, ClockTickEvent& t) { t.ticks_ = 0; } bool guard(LightContext&, ClockTickEvent& t) { return t.ticks_ >= 5; } boid action(LightContext& l, ClockTickEvent&) { l.walk_pressed_ = false; } }; using transitions = std::tuple, ClockedTransition, ClockedTransition, ``` -------------------------------- ### Test ThreadedExecutionPolicy Implementation Source: https://github.com/tinverse/tsm/blob/main/Documentation.dox Illustrates applying the ThreadedExecutionPolicy to a hierarchical state machine and verifying state transitions. ```cpp TEST_CASE("Test ThreadedExecutionPolicy") { // apply policy using TrafficLightHsm = ThreadedExecutionPolicy; using LightHsm = make_hsm_t; using EmergencyOverrideHsm = make_hsm_t; TrafficLightHsm hsm; hsm.start(); hsm.send_event(TrafficLight::TrafficLightHsmContext::EmergencySwitchOn()); std::this_thread::sleep_for(std::chrono::milliseconds(5)); REQUIRE(std::holds_alternative(hsm.current_state_)); auto current_hsm = std::get(hsm.current_state_); REQUIRE(std::holds_alternative( current_hsm->current_state_)); } ``` -------------------------------- ### Model TCP Socket States Source: https://context7.com/tinverse/tsm/llms.txt Demonstrates a single-threaded state machine for TCP socket lifecycle management. Uses a transition table to define valid state changes. ```cpp #include #include namespace tcp_socket { struct Socket { // Events struct sock_open {}; struct bind {}; struct listen {}; struct connect {}; struct accept {}; struct close {}; // States struct Closed {}; struct Ready {}; struct Bound {}; struct Open {}; struct Listening {}; // State transition table using transitions = std::tuple< tsm::detail::Transition, tsm::detail::Transition, tsm::detail::Transition, tsm::detail::Transition, tsm::detail::Transition, tsm::detail::Transition, tsm::detail::Transition, tsm::detail::Transition // Self-transition >; // Socket configuration data std::string ip; int port; int backlog; }; } // namespace tcp_socket int main() { using SocketHsm = tsm::SingleThreadedHsm; SocketHsm sm; // Configure socket sm.ip = "127.0.0.1"; sm.port = 8080; sm.backlog = 10; // Initial state: Closed assert(std::holds_alternative(sm.current_state_)); // Server workflow: Closed -> Ready -> Bound -> Listening sm.send_event(tcp_socket::sock_open{}); sm.step(); assert(std::holds_alternative(sm.current_state_)); sm.send_event(tcp_socket::bind{}); sm.step(); assert(std::holds_alternative(sm.current_state_)); sm.send_event(tcp_socket::listen{}); sm.step(); assert(std::holds_alternative(sm.current_state_)); // Close the socket sm.send_event(tcp_socket::close{}); sm.step(); assert(std::holds_alternative(sm.current_state_)); return 0; } ``` -------------------------------- ### Configure Periodic HSM Source: https://github.com/tinverse/tsm/blob/main/README.md Transforms the context into a periodic state machine using the tsm library. ```cpp #include using TrafficLightHsm = tsm::PeriodicHsm; ``` -------------------------------- ### Define a Basic Single-Threaded State Machine Source: https://context7.com/tinverse/tsm/llms.txt Defines a state machine context with states, events, and a transition table, then executes it using the SingleThreadedHsm policy. ```cpp #include // Define a simple on/off switch state machine struct SwitchContext { // Events - empty structs that trigger transitions struct Toggle {}; // States - empty structs representing state machine states struct Off {}; struct On {}; // Transition table: tuple of Transition using transitions = std::tuple< tsm::detail::Transition, tsm::detail::Transition >; }; // Create a single-threaded state machine using SwitchSM = tsm::SingleThreadedHsm; int main() { SwitchSM sm; // Initial state is the 'from' state of the first transition (Off) // Check current state using std::holds_alternative assert(std::holds_alternative(sm.current_state_)); // Send event and process it sm.send_event(SwitchContext::Toggle{}); sm.step(); // Process the event // Now in On state assert(std::holds_alternative(sm.current_state_)); // Toggle back to Off sm.send_event(SwitchContext::Toggle{}); sm.step(); assert(std::holds_alternative(sm.current_state_)); return 0; } ``` -------------------------------- ### Implement Real-time Periodic HSM Source: https://context7.com/tinverse/tsm/llms.txt Uses RealtimePeriodicHsm for high-precision timer-driven state machines. Requires a defined execution policy and clock source. ```cpp #include #include namespace TrafficLight { struct LightContext { struct G1 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 30) { t.ticks_ = 0; return true; } return false; } }; struct Y1 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; struct G2 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 60) { t.ticks_ = 0; return true; } return false; } }; struct Y2 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; bool walk_pressed_{}; using transitions = std::tuple< tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition >; }; // Parent context for hierarchical HSM struct TrafficLightHsmContext { struct EmergencySwitchOn {}; struct EmergencySwitchOff {}; using transitions = std::tuple< tsm::detail::Transition >; }; } // namespace TrafficLight // Custom real-time periodic policy (1ms tick interval) template using MyRealtimePeriodic1KhzPolicy = tsm::detail::RealtimePeriodicExecutionPolicy< Context, tsm::detail::ThreadedExecutionPolicy, tsm::detail::PeriodicSleepTimer>; int main() { // Real-time periodic HSM with automatic tick events using TrafficLightHsm = tsm::RealtimePeriodicHsm; using LightHsm = tsm::detail::make_hsm_t; TrafficLightHsm hsm; // Start real-time periodic execution hsm.start(); // Get current nested state machine auto current_hsm = std::get(hsm.current_state_); // Wait for automatic transition from G1 to Y1 (30 ticks at 1ms each = ~30ms) while (!std::holds_alternative( current_hsm->current_state_)) { // Busy wait - in production, use proper synchronization } // Verify transition occurred assert(std::holds_alternative( current_hsm->current_state_)); // Stop the real-time execution hsm.stop(); return 0; } ``` -------------------------------- ### Define State Machine Logic with TSM Source: https://context7.com/tinverse/tsm/llms.txt Implements a traffic light state machine using entry methods for initialization and guard methods for transition conditions. Requires the tsm.h header and a defined context structure. ```cpp #include namespace TrafficLightAG { struct LightContext { struct G1 { // Entry called when entering this state void entry(LightContext&, tsm::detail::ClockTickEvent& t) { t.ticks_ = 0; // Reset counter on entry } // Guard determines if transition is allowed bool guard(LightContext&, tsm::detail::ClockTickEvent& t) { return t.ticks_ >= 30; } }; struct Y1 { bool guard(LightContext&, tsm::detail::ClockTickEvent& t) { return t.ticks_ >= 5; } }; struct G2 { void entry(LightContext&, tsm::detail::ClockTickEvent& t) { t.ticks_ = 0; } bool guard(LightContext& ctx, tsm::detail::ClockTickEvent& t) { return t.ticks_ >= 60 || (ctx.walk_pressed_ && t.ticks_ >= 30); } }; struct Y2 { void entry(LightContext&, tsm::detail::ClockTickEvent& t) { t.ticks_ = 0; } bool guard(LightContext&, tsm::detail::ClockTickEvent& t) { return t.ticks_ >= 5; } // Action called during transition out of this state void action(LightContext& ctx, tsm::detail::ClockTickEvent&) { ctx.walk_pressed_ = false; // Reset walk button } }; using transitions = std::tuple< tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition >; bool walk_pressed_{false}; }; } // namespace TrafficLightAG int main() { using LightHsm = tsm::detail::ClockedHsm; LightHsm hsm; // Full cycle through all states for (int i = 0; i < 30; i++) hsm.tick(); // G1 -> Y1 for (int i = 0; i < 5; i++) hsm.tick(); // Y1 -> G2 for (int i = 0; i < 60; i++) hsm.tick(); // G2 -> Y2 for (int i = 0; i < 5; i++) hsm.tick(); // Y2 -> G1 assert(std::holds_alternative(hsm.current_state_)); return 0; } ``` -------------------------------- ### Implement Hierarchical State Machines in C++ Source: https://context7.com/tinverse/tsm/llms.txt Defines nested state machine contexts and transitions between them. Requires the tsm.h header and standard library support for tuples and variants. ```cpp #include namespace TrafficLight { // Normal light context (child HSM) struct LightContext { struct G1 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 30) { t.ticks_ = 0; return true; } return false; } }; struct Y1 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; struct G2 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 60) { t.ticks_ = 0; return true; } return false; } }; struct Y2 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; bool walk_pressed_{}; using transitions = std::tuple< tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition >; }; // Emergency override context (child HSM) - faster cycling struct EmergencyOverrideContext { struct BaseHandle { bool handle(EmergencyOverrideContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; struct G1 : BaseHandle {}; struct Y1 : BaseHandle {}; struct G2 : BaseHandle {}; struct Y2 : BaseHandle {}; bool walk_pressed_{}; using transitions = std::tuple< tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition >; }; // Parent HSM with nested state machines as states struct TrafficLightHsmContext { // Events for switching between normal and emergency modes struct EmergencySwitchOn {}; struct EmergencySwitchOff {}; // Transitions between child state machines using transitions = std::tuple< tsm::detail::Transition, tsm::detail::Transition >; }; } // namespace TrafficLight int main() { using LightHsm = tsm::detail::make_hsm_t; using EmergencyOverrideHsm = tsm::detail::make_hsm_t; using TrafficLightHsm = tsm::detail::ClockedHsm< tsm::detail::make_hsm_t>; TrafficLightHsm hsm; // Initial state is the LightContext HSM assert(std::holds_alternative(hsm.current_state_)); // Switch to emergency mode hsm.handle(TrafficLight::TrafficLightHsmContext::EmergencySwitchOn{}); assert(std::holds_alternative(hsm.current_state_)); // Get the current nested HSM auto emergency_hsm = std::get(hsm.current_state_); assert(std::holds_alternative( emergency_hsm->current_state_)); // Tick through emergency states (5 ticks each) for (int i = 0; i < 5; i++) hsm.tick(); assert(std::holds_alternative( emergency_hsm->current_state_)); // Switch back to normal mode hsm.handle(TrafficLight::TrafficLightHsmContext::EmergencySwitchOff{}); assert(std::holds_alternative(hsm.current_state_)); return 0; } ``` -------------------------------- ### Define Traffic Light States and Transitions Source: https://github.com/tinverse/tsm/blob/main/README.md Defines the states (G1, Y1, G2, Y2) and the basic clocked transitions for a traffic light system within a LightContext struct. Initializes a walk_pressed_ flag. ```cpp namespace TrafficLight { struct LightContext { // States struct G1 { }; struct Y1 { }; struct G2 { }; struct Y2 { }; bool walk_pressed_{}; using transitions = std::tuple, ClockedTransition, ClockedTransition>; }; ``` -------------------------------- ### Create Single-Threaded HSM Source: https://github.com/tinverse/tsm/blob/main/Documentation.dox Instantiates a state machine that executes in the context of the parent thread. The client thread sending events drives event processing. ```cpp SingleThreadedHsm sm; ``` -------------------------------- ### Configure CMake for tsm integration Source: https://github.com/tinverse/tsm/blob/main/examples/hello_tsm/CMakeLists.txt Use this CMakeLists.txt file to locate the tsm package and link it to your executable. Ensure tsm_DIR is set to the directory containing tsmConfig.cmake. ```cmake cmake_minimum_required(VERSION 3.10) project(hello_tsm VERSION 0.1.0) set(CMAKE_CXX_STANDARD 14) # READ THIS: # Set tsm_DIR to a folder that contains tsmConfig.cmake # For e.g. # git clone https://github.com/tinverse/tsm tsm # cd tsm && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/tmp .. # make install # mkdir build-hello_tsm && cd build-hello_tsm # cmake -Dtsm_DIR=/tmp/lib/cmake/tsm ../../examples/hello_tsm # make # This should build this example. You can use this as a guide to include tsm # in your projects. if (NOT tsm_DIR) message(WARNING "tsm_DIR not defined") endif(NOT tsm_DIR) find_package(tsm REQUIRED CONFIG) add_executable(hello_tsm main.cpp ) find_package (Threads) target_link_libraries(hello_tsm PRIVATE tsm::tsm Threads::Threads) ``` -------------------------------- ### G1 State with Combined Handle Method Source: https://github.com/tinverse/tsm/blob/main/README.md An alternative implementation for the G1 state using a single handle method that combines exit actions and transition logic. It checks if 30 seconds have passed and resets ticks if transitioning. ```cpp struct G1 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 30) { // exit action t.ticks_ = 0; return true; } return false; } }; ``` -------------------------------- ### G1 State with Exit and Guard for Timer Reset Source: https://github.com/tinverse/tsm/blob/main/README.md Implements the G1 state with an exit function to reset the timer ticks and a guard function to check if the timer has reached 30 seconds before allowing a transition. ```cpp struct G1 { void exit(LightContext&, ClockTickEvent& t) { t.ticks_ = 0; } auto guard(LightContext& l, ClockTickEvent& t) -> bool { if (t.ticks_ >= 30) { return true; } return false; }; ``` -------------------------------- ### Create Threaded HSM Source: https://github.com/tinverse/tsm/blob/main/Documentation.dox Instantiates a state machine that processes incoming events in its own dedicated thread. Clients can 'fire and forget' events. ```cpp ThreadedExecutionPolicy sm; ``` -------------------------------- ### CMake Project Configuration Source: https://github.com/tinverse/tsm/blob/main/test/CMakeLists.txt Configures the build environment for the TSM test project, including dependency management, compiler flags, and test discovery. ```cmake find_package(Catch2 3 REQUIRED) option(TSM_USE_VALGRIND "Use valgrind by default" ON) find_program(VALGRIND_PATH valgrind) if(NOT VALGRIND_PATH) option(TSM_USE_VALGRIND "Valgrind not found" OFF) endif(NOT VALGRIND_PATH) set (TEST_PROJECT tsm_test) add_executable(${TEST_PROJECT} test_hsm.cpp ) if(MSVC) target_compile_options(${TEST_PROJECT} PRIVATE /W4 /WX) else(MSVC) target_compile_options(${TEST_PROJECT} PRIVATE -Wall -Wextra -pedantic -Werror) endif(MSVC) target_link_libraries(${TEST_PROJECT} PRIVATE Catch2::Catch2WithMain Threads::Threads tsm::tsm) include(CTest) include(Catch) # configure unit tests via CTest if (TSM_USE_VALGRIND) add_test(NAME ValgrindRunTests COMMAND valgrind --leak-check=full --error-exitcode=1 $) set_tests_properties(ValgrindRunTests PROPERTIES PASS_REGULAR_EXPRESSION "no leaks are possible") add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $ --list-tests --verbosity high) set_tests_properties(ValgrindListTests PROPERTIES PASS_REGULAR_EXPRESSION "no leaks are possible") add_test(NAME ValgrindListTags COMMAND valgrind --leak-check=full --error-exitcode=1 $ --list-tags) set_tests_properties(ValgrindListTags PROPERTIES PASS_REGULAR_EXPRESSION "no leaks are possible") endif() catch_discover_tests(${TEST_PROJECT}) # test coverage include(coverage) set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*) install(TARGETS ${TEST_PROJECT} RUNTIME DESTINATION test) ``` -------------------------------- ### Define Real-time Periodic 1kHz Execution Policy Source: https://github.com/tinverse/tsm/blob/main/README.md Defines a custom real-time execution policy for a state machine that operates on a 1kHz periodic timer. It utilizes `RealtimePeriodicExecutionPolicy` and specifies the timer type as `std::chrono::steady_clock` with millisecond precision. ```cpp template using MyRealtimePeriodic1KhzPolicy = RealtimePeriodicExecutionPolicy>; using MyRealtimePeriodic1KhzHsm = MyRealtimePeriodic1KhzPolicy; MyRealtimePeriodic1KhzHsm sm; ``` -------------------------------- ### Execute State Machine Synchronously Source: https://github.com/tinverse/tsm/blob/main/README.md Use SingleThreadedHsm to process events manually via the step() method. ```cpp using SwitchSM = SingleThreadedHsm; int main() { SwitchSM s; s.sendEvent(Toggle{}); s.step(); // ... } ``` -------------------------------- ### Define a Finite State Machine Context Source: https://github.com/tinverse/tsm/blob/main/README.md Define a context struct containing events, states, and a transitions tuple to specify the state machine logic. ```cpp struct SwitchContext { // Events struct Toggle {}; // States struct Off {}; struct On {}; using transitions = std::tuple, Transition>; }; ``` -------------------------------- ### Define Traffic Light HSM Contexts and Transitions Source: https://github.com/tinverse/tsm/blob/main/Documentation.dox Defines the state structures, transition logic, and hierarchical relationships for a traffic light system. ```cpp namespace TrafficLight { struct LightContext { struct G1 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 30) { // exit action t.ticks_ = 0; return true; } return false; } }; struct Y1 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 5) { // exit action t.ticks_ = 0; return true; } return false; } void entry(LightContext& t) { t.walk_pressed_ = false; } }; struct G2 { bool handle(LightContext& l, ClockTickEvent& t) { if (t.ticks_ >= 60 || (l.walk_pressed_ && t.ticks_ >= 30)) { // exit action t.ticks_ = 0; return true; } return false; } }; struct Y2 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 5) { // exit action t.ticks_ = 0; return true; } return false; } }; bool walk_pressed_{}; using transitions = std::tuple, ClockedTransition, ClockedTransition, ClockedTransition>; }; // Emergency override trait. G1 and G2 will transition every five ticks. If // walk_pressed_ is true, the light will transition to Y2 after 30 ticks. This // will be part of a hierarchical state machine struct EmergencyOverrideContext { struct BaseHandle { bool handle(EmergencyOverrideContext&, ClockTickEvent& t) { if (t.ticks_ >= 5) { // exit action t.ticks_ = 0; return true; } return false; } }; struct G1 : BaseHandle {}; struct Y1 : BaseHandle {}; struct G2 : BaseHandle {}; struct Y2 : BaseHandle {}; bool walk_pressed_{}; using transitions = std::tuple, ClockedTransition, ClockedTransition, ClockedTransition>; }; // Parent HSM - TrafficLight struct TrafficLightHsmContext { // Events struct EmergencySwitchOn {}; struct EmergencySwitchOff {}; using transitions = std::tuple< Transition, Transition>; }; } ``` -------------------------------- ### Define Traffic Light State Machine Context Source: https://context7.com/tinverse/tsm/llms.txt Defines the context and transitions for a single traffic light state machine, including timed transitions between states like Green (G1, G2) and Yellow (Y1, Y2). ```cpp #include namespace TrafficLight { struct LightContext { struct G1 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 30) { t.ticks_ = 0; return true; } return false; } }; struct Y1 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; struct G2 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 60) { t.ticks_ = 0; return true; } return false; } }; struct Y2 { bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; bool walk_pressed_{}; using transitions = std::tuple< tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition >; }; } // namespace TrafficLight ``` -------------------------------- ### Define Traffic Light Context Source: https://github.com/tinverse/tsm/blob/main/README.md Defines the state machine structure including states, transition logic, and the transition tuple. ```cpp namespace TrafficLight { struct LightContext { struct G1 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 30) { // exit action t.ticks_ = 0; return true; } return false; } }; struct Y1 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 5) { // exit action t.ticks_ = 0; return true; } return false; } void entry(LightContext& t) { t.walk_pressed_ = false; } }; struct G2 { bool handle(LightContext& l, ClockTickEvent& t) { if (t.ticks_ >= 60 || (l.walk_pressed_ && t.ticks_ >= 30)) { // exit action t.ticks_ = 0; return true; } return false; } }; struct Y2 { bool handle(LightContext&, ClockTickEvent& t) { if (t.ticks_ >= 5) { // exit action t.ticks_ = 0; return true; } return false; } }; bool walk_pressed_{}; using transitions = std::tuple, ClockedTransition, ClockedTransition, ClockedTransition>; }; } // namespace TrafficLight ``` -------------------------------- ### Define Orthogonal State Machine for City Streets Source: https://github.com/tinverse/tsm/blob/main/README.md Defines an orthogonal state machine for city street traffic lights, where 'Broadway' contains independent traffic light contexts for 'Park Ave' and '5th Ave'. This allows concurrent operation of different traffic light systems. ```cpp namespace CityStreet { struct Broadway { // Traffic on Park Ave using ParkAveLights = TrafficLight::LightContext; // Traffic on 5th Ave - specialize if needed using FifthAveLights = TrafficLight::LightContext; using type = make_orthogonal_hsm_t; }; } ``` -------------------------------- ### Send Event to HSM Source: https://github.com/tinverse/tsm/blob/main/Documentation.dox Sends an event to the state machine using the `sendEvent` method provided by the execution policy. ```cpp sm.sendEvent(sm.doorOpen); ``` -------------------------------- ### Define Traffic Light HSM Context with Emergency Transitions Source: https://github.com/tinverse/tsm/blob/main/README.md Defines the main context for a Traffic Light Hierarchical State Machine (HSM), including events to switch to and from emergency mode. It specifies transitions between the normal LightContext and the EmergencyOverrideContext. ```cpp struct TrafficLightHsmContext { // Events struct EmergencySwitchOn {}; struct EmergencySwitchOff {}; using transitions = std::tuple< Transition, Transition>; }; ``` -------------------------------- ### Integrate TSM in CMake Source: https://context7.com/tinverse/tsm/llms.txt Use find_package to locate TSM and link against the tsm::tsm target. Requires C++17 or higher for std::variant support. ```cmake # CMakeLists.txt cmake_minimum_required(VERSION 3.7) project(my_project) # Set C++17 or C++20 standard (required for std::variant) set(CMAKE_CXX_STANDARD 20) # Find tsm package (set tsm_DIR to point to tsmConfig.cmake location) # e.g., -Dtsm_DIR=${HOME}/usr/lib/cmake/tsm find_package(tsm REQUIRED) # Create your executable add_executable(my_app main.cpp) # Link against tsm (header-only, provides include paths) target_link_libraries(my_app PRIVATE tsm::tsm) ``` -------------------------------- ### Create Clocked or Periodic State Machines in C++ Source: https://context7.com/tinverse/tsm/llms.txt Utilizes ClockedTransition and ClockedHsm to manage time-based state changes. Each state implements a handle method that evaluates ClockTickEvent to determine when to trigger a transition. ```cpp #include namespace TrafficLight { struct LightContext { // States with handle methods for time-based transitions struct G1 { // Green light 1 - 30 ticks bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 30) { t.ticks_ = 0; // Reset tick counter return true; // Allow transition } return false; // Block transition } }; struct Y1 { // Yellow light 1 - 5 ticks bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; struct G2 { // Green light 2 - 60 ticks (or 30 if walk pressed) bool handle(LightContext& ctx, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 60 || (ctx.walk_pressed_ && t.ticks_ >= 30)) { t.ticks_ = 0; return true; } return false; } }; struct Y2 { // Yellow light 2 - 5 ticks bool handle(LightContext&, tsm::detail::ClockTickEvent& t) { if (t.ticks_ >= 5) { t.ticks_ = 0; return true; } return false; } }; bool walk_pressed_{false}; // ClockedTransition automatically uses ClockTickEvent using transitions = std::tuple< tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition, tsm::detail::ClockedTransition >; }; } // namespace TrafficLight int main() { using LightHsm = tsm::detail::ClockedHsm< tsm::detail::make_hsm_t>; LightHsm hsm; // Initial state is G1 assert(std::holds_alternative(hsm.current_state_)); // Tick 30 times to transition G1 -> Y1 for (int i = 0; i < 30; i++) { hsm.tick(); } assert(std::holds_alternative(hsm.current_state_)); // Tick 5 times to transition Y1 -> G2 for (int i = 0; i < 5; i++) { hsm.tick(); } assert(std::holds_alternative(hsm.current_state_)); // Simulate walk button press - shortens G2 duration hsm.walk_pressed_ = true; for (int i = 0; i < 30; i++) { hsm.tick(); } assert(std::holds_alternative(hsm.current_state_)); return 0; } ```