Skip to content

Commit

Permalink
feat: Internal transition are recognized also when no external transi…
Browse files Browse the repository at this point in the history
…tion is present (#185)
  • Loading branch information
erikzenker authored Dec 23, 2022
1 parent 09d66e4 commit 4d17e39
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 48 deletions.
15 changes: 14 additions & 1 deletion include/hsm/details/collect_states.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <boost/hana/at.hpp>
#include <boost/hana/basic_tuple.hpp>
#include <boost/hana/flatten.hpp>
#include <boost/hana/prepend.hpp>
#include <boost/hana/transform.hpp>
#include <boost/hana/type.hpp>

Expand Down Expand Up @@ -51,6 +52,12 @@ constexpr auto extractStateTypeids = [](auto transition) {
};
}

constexpr auto extractParentAndState = [](auto transition) {
return bh::make_basic_tuple(
bh::make_pair(transition.parent(), resolveInitialState(transition)),
bh::make_pair(transition.parent(), transition.target()));
};

template <class State> constexpr auto collect_child_state_typeids_recursive(State parentState)
{
auto transitions = flatten_transition_table(parentState);
Expand All @@ -62,7 +69,6 @@ template <class State> constexpr auto collect_child_state_typeids_recursive(Stat
template <class State> constexpr auto collect_child_states_recursive(State parentState)
{
return bh::flatten(bh::transform(flatten_transition_table(parentState), extractExtendedStates));
;
}

template <class State> constexpr auto collect_state_typeids_recursive(State parentState)
Expand Down Expand Up @@ -90,4 +96,11 @@ template <class State> constexpr auto collect_child_states(State state)
return remove_duplicates(
bh::flatten(bh::transform(make_transition_table2(state), extractStates)));
}

template <class State> constexpr auto collect_state_parentstate_pairs_recursively(State rootState)
{
return remove_duplicates(bh::prepend(
bh::flatten(bh::transform(flatten_transition_table(rootState), extractParentAndState)),
bh::make_pair(rootState, rootState)));
}
}
67 changes: 48 additions & 19 deletions include/hsm/details/flatten_internal_transition_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,29 +76,57 @@ constexpr auto extend_internal_transition(Transition internalTransition, States
}

/**
* Returns the internal transitions for each for each state
* Extends an internal transitions to all provided states
*
* @param internalTranstion Internal transition that should be extended
* @param states tuple of states
*/
template <class Transition, class ParentState, class State>
constexpr auto
extend_internal_transition2(Transition internalTransition, ParentState parent, State state)
{
return bh::make_basic_tuple(details::internal_extended_transition(
parent,
details::transition(
state,
internalTransition.event(),
internalTransition.guard(),
internalTransition.action(),
state)));
}

/**
* Returns the internal transitions for each state
* [[transition1, transition2], [transition3, transition4], []]
*
* @param states a tuple of states
*/
constexpr auto get_internal_transitions = [](auto states) {
constexpr auto get_internal_transitions = [](auto stateAndParentStatePairs) {
return bh::flatten(bh::filter(
bh::transform(
states,
[](auto parentState) {
constexpr auto extend
= bh::capture(parentState)([](auto parentState, auto transition) {
// Folowing lines satisfies older gcc -Werror=unused-but-set-parameter
(void)transition;
if constexpr (has_transition_table(parentState)) {
return extend_internal_transition(
transition, collect_child_states(parentState));
} else {
return bh::make_basic_tuple();
}
});
stateAndParentStatePairs,
[](auto stateAndParentStatePair) {
constexpr auto extend = bh::capture(stateAndParentStatePair)(
[](auto stateAndParentStatePair, auto internalTransition) {
// Folowing lines satisfies older gcc -Werror=unused-but-set-parameter
(void)internalTransition;
if constexpr (has_transition_table(bh::second(stateAndParentStatePair))) {
return extend_internal_transition(
internalTransition,
collect_child_states(bh::second(stateAndParentStatePair)));
} else if constexpr (has_internal_transition_table(
bh::second(stateAndParentStatePair))) {
return extend_internal_transition2(
internalTransition,
bh::first(stateAndParentStatePair),
bh::second(stateAndParentStatePair));
} else {
return bh::make_basic_tuple();
}
});

return bh::transform(get_internal_transition_table(parentState), extend);
return bh::transform(
get_internal_transition_table(bh::second(stateAndParentStatePair)), extend);
}),
isNotEmpty));
};
Expand All @@ -110,9 +138,10 @@ constexpr auto get_internal_transitions = [](auto states) {
*/
template <class State> constexpr auto flatten_internal_transition_table(State rootState)
{
return [](auto states) {
return bh::to<bh::basic_tuple_tag>(bh::flatten(get_internal_transitions(states)));
}(collect_states_recursive(rootState));
return [](auto stateAndParentStatePairs) {
return remove_duplicate_types(bh::to<bh::basic_tuple_tag>(
bh::flatten(get_internal_transitions(stateAndParentStatePairs))));
}(collect_state_parentstate_pairs_recursively(rootState));
}

} // namespace hsm
6 changes: 6 additions & 0 deletions include/hsm/details/flatten_transition_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ template <class State> constexpr auto flatten_sub_transition_table(State state);

}

/**
* This function iterates recursively trough the transition table of a state provided by
* `make_transition_table()` and collects all transitions.
*
* @return a list of ExtendedTransition
*/
template <class State> constexpr auto flatten_transition_table(State state)
{
auto flattenSubTransitionTable = [state](auto transition) {
Expand Down
31 changes: 27 additions & 4 deletions test/integration/internal_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct e8 {
};
struct e9 {
};
struct e10 { };
struct e11 { };

// Guards
const auto fail = [](auto /*event*/, auto /*source*/, auto /*target*/) { return false; };
Expand Down Expand Up @@ -94,15 +96,27 @@ struct SubState {
}
};

struct InternalOnlySubState {
static constexpr auto make_internal_transition_table()
{
// clang-format off
return hsm::transition_table(
+ (hsm::event<e11>)
);
// clang-format on
}
};

struct MainState {
static constexpr auto make_transition_table()
{
// clang-format off
return hsm::transition_table(
* hsm::state<S1> + hsm::event<e1> = hsm::state<S2>
, hsm::state<S1> + hsm::event<e7> = hsm::state<S2>
, hsm::state<S1> + hsm::event<e3> = hsm::state<SubState>
, hsm::state<S2> + hsm::event<e6> = hsm::state<S1>
* hsm::state<S1> + hsm::event<e1> = hsm::state<S2>
, hsm::state<S1> + hsm::event<e7> = hsm::state<S2>
, hsm::state<S1> + hsm::event<e3> = hsm::state<SubState>
, hsm::state<S2> + hsm::event<e6> = hsm::state<S1>
, hsm::state<S1> + hsm::event<e10> = hsm::state<InternalOnlySubState>
);
// clang-format on
}
Expand Down Expand Up @@ -214,3 +228,12 @@ TEST_F(InternalTransitionTests, should_not_transit_to_initial_state)
sm.process_event(e1 {});
ASSERT_TRUE(sm.is(hsm::state<S2>));
}

TEST_F(InternalTransitionTests, should_recognize_substates_with_only_internal_transition_table)
{
ASSERT_TRUE(sm.is(hsm::state<S1>));
sm.process_event(e10 {});
ASSERT_TRUE(sm.is(hsm::state<InternalOnlySubState>));
sm.process_event(e11 {});
ASSERT_TRUE(sm.is(hsm::state<InternalOnlySubState>));
}
4 changes: 2 additions & 2 deletions test/unit/collect_events_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ TEST_F(CollectEventsTests, should_collect_event_typeids_recursive)
{
auto collectedEvents = hsm::collect_event_typeids_recursive(hsm::state_t<S> {});
auto expectedEvents = bh::make_basic_tuple(
bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E4 {}), bh::typeid_(E3 {}));
bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E3 {}), bh::typeid_(E4 {}));

ASSERT_EQ(bh::size(expectedEvents), bh::size(collectedEvents));
ASSERT_TRUE(bh::equal(expectedEvents, collectedEvents));
Expand All @@ -73,7 +73,7 @@ TEST_F(CollectEventsTests, should_collect_events_recursive)
auto collectedEvents
= bh::transform(hsm::collect_events_recursive(hsm::state_t<S> {}), bh::typeid_);
auto expectedEvents = bh::make_basic_tuple(
bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E4 {}), bh::typeid_(E3 {}));
bh::typeid_(E1 {}), bh::typeid_(E2 {}), bh::typeid_(E3 {}), bh::typeid_(E4 {}));

ASSERT_EQ(bh::size(expectedEvents), bh::size(collectedEvents));
ASSERT_TRUE(bh::equal(expectedEvents, collectedEvents));
Expand Down
30 changes: 30 additions & 0 deletions test/unit/collect_states_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <gtest/gtest.h>

#include <boost/hana.hpp>
#include <boost/hana/experimental/printable.hpp>

using namespace ::testing;

Expand Down Expand Up @@ -300,4 +301,33 @@ TEST_F(CollectStatesTests, should_collect_parent_state_typeids)
bh::typeid_);

ASSERT_EQ(expectedParentStates, collectedParentStates);
}

TEST_F(CollectStatesTests, should_collect_parent_state_and_state_typeids)
{
auto collectedParentStatesAndStatesTypesids
= hsm::collect_state_parentstate_pairs_recursively(hsm::state_t<MainState> {});

ASSERT_EQ(bh::size_c<11>, bh::size(collectedParentStatesAndStatesTypesids));

auto expected = bh::make_tuple(
bh::make_pair(hsm::state_t<MainState> {}, hsm::state_t<MainState> {}),
bh::make_pair(hsm::state_t<MainState> {}, hsm::state_t<S1> {}),
bh::make_pair(hsm::state_t<MainState> {}, hsm::state_t<S2> {}),
bh::make_pair(hsm::state_t<MainState> {}, hsm::state_t<SubState> {}),
bh::make_pair(hsm::state_t<SubState> {}, hsm::state_t<S1> {}),
bh::make_pair(hsm::state_t<SubState> {}, hsm::state_t<S2> {}),
bh::make_pair(hsm::state_t<MainState> {}, hsm::history_t<HistorySubState> {}),
bh::make_pair(hsm::state_t<HistorySubState> {}, hsm::state_t<S1> {}),
bh::make_pair(hsm::state_t<HistorySubState> {}, hsm::state_t<S2> {}),
bh::make_pair(hsm::state_t<MainState> {}, hsm::state_t<S3> {}),
bh::make_pair(hsm::state_t<MainState> {}, hsm::state_t<S4> {}));

auto toTypes = [](auto pairs) {
return bh::transform(pairs, [](auto pair) {
return bh::make_pair(bh::typeid_(bh::first(pair)), bh::typeid_(bh::second(pair)));
});
};

ASSERT_EQ(toTypes(expected), toTypes(collectedParentStatesAndStatesTypesids));
}
Loading

0 comments on commit 4d17e39

Please sign in to comment.