From 8efa47f3da52112381bb7626cdb0dfaf8dea6ad2 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 5 Sep 2018 18:49:37 +0100 Subject: [PATCH 1/7] First draft --- include/etl/lwfsm.h | 154 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 include/etl/lwfsm.h diff --git a/include/etl/lwfsm.h b/include/etl/lwfsm.h new file mode 100644 index 00000000..99f11c8d --- /dev/null +++ b/include/etl/lwfsm.h @@ -0,0 +1,154 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_LWFSM_INCLUDED +#define ETL_LWFSM_INCLUDED + +#include + +#include "etl/nullptr.h" + +namespace etl +{ + //*************************************************************************** + /// Light Weight Finite State Machine + //*************************************************************************** + template + class state_machine + { + public: + + typedef uint32_t state_id_t; + typedef uint32_t event_id_t; + + //************************************************************************* + /// Transition definition + //************************************************************************* + struct transition + { + transition(event_id_t event_, + state_id_t current_state_, + state_id_t next_state_, + void (TObject::*action_)(), + bool (TObject::*condition_)() = nullptr) + : event(event_), + current_state(current_state_), + nextState(next_state_), + action(action_), + condition(condition_) + { + } + + event_id_t event; + state_id_t current_state; + state_id_t nextState; + void (TObject::*action)(); + bool (TObject::*condition)(); + }; + + //************************************************************************* + /// + //************************************************************************* + state_machine(TObject& object_) + : object(object_) + { + } + + //************************************************************************* + /// + //************************************************************************* + state_machine(TObject& object_, state_id_t state_) + : object(object_), + current_state(state_) + { + } + + //************************************************************************* + /// + //************************************************************************* + template + void set_transition_table(transition(&transition_table_)[TRANSITION_TABLE_SIZE]) + { + transition_table = &transition_table_[0]; + transition_table_size = TRANSITION_TABLE_SIZE; + } + + //************************************************************************* + /// + //************************************************************************* + void set_initial_state(state_id_t state) + { + current_state = state; + } + + //************************************************************************* + /// + //************************************************************************* + state_id_t get_state() const + { + return current_state; + } + + //************************************************************************* + /// + //************************************************************************* + void process_event(event_id_t event) + { + bool handled = false; + + // Scan the transition table. + for (uint32_t i = 0; (i < transition_table_size) && (handled == false); ++i) + { + transition& transition = transition_table[i]; + + // Matching event and state? + if ((transition.event == event) && (transition.current_state == current_state)) + { + // Shall we execute the transition? + bool do_transition = (transition.condition == nullptr) || ((object.*transition.condition)()); + + if (do_transition) + { + (object.*transition.action)(); + current_state = transition.next_state; + handled = true; + } + } + } + } + + private: + + state_id_t current_state; ///< The current state id. + TObject& object; ///< The object that supplies codition and action member functions + transition* transition_table; ///< The table of transitions. + uint32_t transition_table_size; ///< The size of the transition table. + }; +} + +#endif \ No newline at end of file From 085a66f93062cc756bfb7f3bb83f70ede6926fc1 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 5 Sep 2018 21:11:16 +0100 Subject: [PATCH 2/7] Renamed --- include/etl/{lwfsm.h => state_machine.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/etl/{lwfsm.h => state_machine.h} (100%) diff --git a/include/etl/lwfsm.h b/include/etl/state_machine.h similarity index 100% rename from include/etl/lwfsm.h rename to include/etl/state_machine.h From 9872acd6fb470951ff5f52ac92bf4bc03fec536a Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 5 Sep 2018 21:12:35 +0100 Subject: [PATCH 3/7] First draft --- include/etl/state_machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etl/state_machine.h b/include/etl/state_machine.h index 99f11c8d..cf9727df 100644 --- a/include/etl/state_machine.h +++ b/include/etl/state_machine.h @@ -5,7 +5,7 @@ Embedded Template Library. https://github.com/ETLCPP/etl https://www.etlcpp.com -Copyright(c) 2017 jwellbelove +Copyright(c) 2018 jwellbelove Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal From 5a3f6b435570ec055218f12459a461ce37c67a07 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 6 Sep 2018 21:33:33 +0100 Subject: [PATCH 4/7] First draft --- include/etl/version.h | 10 +++++----- support/Release notes.txt | 4 ++++ test/vs2017/etl.vcxproj | 1 + test/vs2017/etl.vcxproj.filters | 3 +++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/etl/version.h b/include/etl/version.h index 32094197..d8f02332 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -37,13 +37,13 @@ SOFTWARE. /// Definitions of the ETL version ///\ingroup utilities -#define ETL_VERSION "11.16.5" -#define ETL_VERSION_W L"11.16.5" -#define ETL_VERSION_U16 u"11.16.5" -#define ETL_VERSION_U32 U"11.16.5" +#define ETL_VERSION "11.16.6" +#define ETL_VERSION_W L"11.16.6" +#define ETL_VERSION_U16 u"11.16.6" +#define ETL_VERSION_U32 U"11.16.6" #define ETL_VERSION_MAJOR 11 #define ETL_VERSION_MINOR 16 -#define ETL_VERSION_PATCH 5 +#define ETL_VERSION_PATCH 6 #define ETL_VERSION_VALUE ((ETL_VERSION_MAJOR * 10000) + (ETL_VERSION_MINOR * 100) + ETL_VERSION_PATCH) #endif diff --git a/support/Release notes.txt b/support/Release notes.txt index d3293ecf..a7b2be00 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,7 @@ +=============================================================================== +11.16.6 +Fixed implementations of key_comp and value_comp for maps and sets + =============================================================================== 11.16.5 Added 'ull' suffix to 64bit literals diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 7c97660e..797f2d40 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -371,6 +371,7 @@ + diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index b4225aba..b830a382 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -693,6 +693,9 @@ ETL\Utilities\Atomic + + ETL\Frameworks + From c2c7ae01117e87769b2f8a093741a96c839d4c7f Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 7 Sep 2018 00:33:04 +0100 Subject: [PATCH 5/7] Renamed --- include/etl/state_machine.h | 154 ------------------------------------ 1 file changed, 154 deletions(-) delete mode 100644 include/etl/state_machine.h diff --git a/include/etl/state_machine.h b/include/etl/state_machine.h deleted file mode 100644 index cf9727df..00000000 --- a/include/etl/state_machine.h +++ /dev/null @@ -1,154 +0,0 @@ -/****************************************************************************** -The MIT License(MIT) - -Embedded Template Library. -https://github.com/ETLCPP/etl -https://www.etlcpp.com - -Copyright(c) 2018 jwellbelove - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files(the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions : - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -******************************************************************************/ - -#ifndef ETL_LWFSM_INCLUDED -#define ETL_LWFSM_INCLUDED - -#include - -#include "etl/nullptr.h" - -namespace etl -{ - //*************************************************************************** - /// Light Weight Finite State Machine - //*************************************************************************** - template - class state_machine - { - public: - - typedef uint32_t state_id_t; - typedef uint32_t event_id_t; - - //************************************************************************* - /// Transition definition - //************************************************************************* - struct transition - { - transition(event_id_t event_, - state_id_t current_state_, - state_id_t next_state_, - void (TObject::*action_)(), - bool (TObject::*condition_)() = nullptr) - : event(event_), - current_state(current_state_), - nextState(next_state_), - action(action_), - condition(condition_) - { - } - - event_id_t event; - state_id_t current_state; - state_id_t nextState; - void (TObject::*action)(); - bool (TObject::*condition)(); - }; - - //************************************************************************* - /// - //************************************************************************* - state_machine(TObject& object_) - : object(object_) - { - } - - //************************************************************************* - /// - //************************************************************************* - state_machine(TObject& object_, state_id_t state_) - : object(object_), - current_state(state_) - { - } - - //************************************************************************* - /// - //************************************************************************* - template - void set_transition_table(transition(&transition_table_)[TRANSITION_TABLE_SIZE]) - { - transition_table = &transition_table_[0]; - transition_table_size = TRANSITION_TABLE_SIZE; - } - - //************************************************************************* - /// - //************************************************************************* - void set_initial_state(state_id_t state) - { - current_state = state; - } - - //************************************************************************* - /// - //************************************************************************* - state_id_t get_state() const - { - return current_state; - } - - //************************************************************************* - /// - //************************************************************************* - void process_event(event_id_t event) - { - bool handled = false; - - // Scan the transition table. - for (uint32_t i = 0; (i < transition_table_size) && (handled == false); ++i) - { - transition& transition = transition_table[i]; - - // Matching event and state? - if ((transition.event == event) && (transition.current_state == current_state)) - { - // Shall we execute the transition? - bool do_transition = (transition.condition == nullptr) || ((object.*transition.condition)()); - - if (do_transition) - { - (object.*transition.action)(); - current_state = transition.next_state; - handled = true; - } - } - } - } - - private: - - state_id_t current_state; ///< The current state id. - TObject& object; ///< The object that supplies codition and action member functions - transition* transition_table; ///< The table of transitions. - uint32_t transition_table_size; ///< The size of the transition table. - }; -} - -#endif \ No newline at end of file From 61f0953c9be5b931fcd378765195c303597ba1e1 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 7 Sep 2018 00:33:30 +0100 Subject: [PATCH 6/7] Added entry and exit --- include/etl/sfsm.h | 187 ++++++++++++++++++++++++++++++++ test/vs2017/etl.vcxproj | 2 +- test/vs2017/etl.vcxproj.filters | 2 +- 3 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 include/etl/sfsm.h diff --git a/include/etl/sfsm.h b/include/etl/sfsm.h new file mode 100644 index 00000000..56660f54 --- /dev/null +++ b/include/etl/sfsm.h @@ -0,0 +1,187 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_LWFSM_INCLUDED +#define ETL_LWFSM_INCLUDED + +#include h> + +#include "etl/nullptr.h" + +namespace etl +{ + //*************************************************************************** + /// Simple State Machine + //*************************************************************************** + template + class sfsm + { + public: + + typedef uint32_t state_id_t; + typedef uint32_t event_id_t; + + //************************************************************************* + /// Transition definition + //************************************************************************* + struct transition + { + transition(const event_id_t event_id_, + const state_id_t current_state_id_, + const state_id_t next_state_id_, + void (TObject::* const action_)() = nullptr, + bool (TObject::* const guard_)() = nullptr) + : event_id(event_id_), + current_state(current_state_), + nextState(next_state_), + action(action_), + guard(guard_) + { + } + + const event_id_t event_id; + const state_id_t current_state_id; + const state_id_t next_state_id; + void (TObject::* const action)(); + bool (TObject::* const guard)(); + }; + + //************************************************************************* + /// State definition + //************************************************************************* + struct state + { + state(const state_id_t state_id_, + void (TObject::* const on_enter_)() = nullptr, + void (TObject::* const on_exit_)() = nullptr) + : state_id(state_id_), + on_enter_(on_enter_), + on_exit(on_exit_) + { + } + + state_id_t state_id; + void (TObject::* const on_enter)(); + void (TObject::* const on_exit)(); + }; + + //************************************************************************* + /// + //************************************************************************* + template + sfsm(TObject& object_, + const transition(&transition_table_)[TRANSITION_TABLE_SIZE], + const enter_exit(&state_table_)[STATE_TABLE_SIZE], + const state_id_t state_id_) + : object(object_), + current_state_id(state_id_), + transition_table(&transition_table_[0]), + transition_table_size(TRANSITION_TABLE_SIZE) + state_table(&state_table_[0]), + state_table_size(ENTER_EXIT_TABLE_SIZE) + { + } + + //************************************************************************* + /// + //************************************************************************* + state_id_t get_state() const + { + return current_state; + } + + //************************************************************************* + /// + //************************************************************************* + void process_event(const event_id_t event_id) + { + // Scan the transition table. + transition* transition_table_begin = transition_table; + transition* transition_table_end = transition_table + transition_table_size; + + transition* t = std::find_if(transition_table_begin, + transition_table_end, + is_transition(event_id, current_state_id)); + + // Found an entry? + if (t != transition_table_end) + { + // Shall we execute the transition? + if ((t->guard == nullptr) || ((object->*t->guard)())) + { + // Are we moving to a new state? + const bool to_new_state = (current_state_id != t->next_state_id); + + state* state_table_begin = state_table; + state* state_table_end = state_table + state_table_size; + + // See if we have a state entry. + state* s = std::find_if(state_table_begin, + state_table_end, + is_state(current_state_id)); + + // If the current state has an 'on_exit' and we are changing states, then call it. + if (to_new_state && (s != state_table_end) && (s->on_exit != nullptr)) + { + (object->*(s->on_exit))(); + } + + // Shall we execute the action? + if (t->action != nullptr) + { + (object->*t->action)(); + } + + // See if we have a state entry. + s = std::find_if(state_table_begin, + state_table_end, + is_state(t->next_state_id)); + + // If the new state has an 'on_entry' and we are changing states, then call it. + if (to_new_state && (s != state_table_end) && (s->on_exit != nullptr)) + { + (object->*(s->on_entry))(); + } + + current_state_id = t->next_state_id; + } + } + } + + private: + + state_id_t current_state_id; ///< The current state id. + TObject& object; ///< The object that supplies codition and action member functions + const transition* transition_table; ///< The table of transitions. + const uint32_t transition_table_size; ///< The size of the transition table. + const state* state_table; ///< The table of transitions. + const uint32_t state_table_size; ///< The size of the transition table. + }; +} + +#endif \ No newline at end of file diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 797f2d40..7b00e497 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -371,7 +371,7 @@ - + diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index b830a382..4a284a91 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -693,7 +693,7 @@ ETL\Utilities\Atomic - + ETL\Frameworks From 5aa369451eee52ee4ab417726f5edda64748ad06 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 8 Sep 2018 15:41:54 +0100 Subject: [PATCH 7/7] State Chart Template Unit tests complete --- include/etl/platform.h | 12 +- include/etl/sfsm.h | 187 --------------- include/etl/state_chart.h | 289 ++++++++++++++++++++++++ include/etl/version.h | 12 +- support/Release notes.txt | 4 + test/codeblocks/ETL.cbp | 2 + test/test_state_chart.cpp | 387 ++++++++++++++++++++++++++++++++ test/vs2017/cpp.hint | 8 + test/vs2017/etl.vcxproj | 4 +- test/vs2017/etl.vcxproj.filters | 6 +- 10 files changed, 715 insertions(+), 196 deletions(-) delete mode 100644 include/etl/sfsm.h create mode 100644 include/etl/state_chart.h create mode 100644 test/test_state_chart.cpp create mode 100644 test/vs2017/cpp.hint diff --git a/include/etl/platform.h b/include/etl/platform.h index 0034c1cc..cab27d25 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -69,7 +69,11 @@ SOFTWARE. #endif #if defined(ETL_COMPILER_GCC) - #define GCC_VERSION ((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) + #define ETL_COMPILER_VERSION __GNUC__ + #define ETL_COMPILER_FULL_VERSION ((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) +#elif defined ETL_COMPILER_MICROSOFT + #define ETL_COMPILER_VERSION _MSC_VER + #define ETL_COMPILER_FULL_VERSION _MSC_FULL_VER #endif #if ETL_CPP11_SUPPORTED @@ -84,4 +88,10 @@ SOFTWARE. #define ETL_IF_CONSTEXPR #endif +#if ETL_CPP11_SUPPORTED + #define ETL_DELETE = delete +#else + #define ETL_DELETE +#endif + #endif diff --git a/include/etl/sfsm.h b/include/etl/sfsm.h deleted file mode 100644 index 56660f54..00000000 --- a/include/etl/sfsm.h +++ /dev/null @@ -1,187 +0,0 @@ -/****************************************************************************** -The MIT License(MIT) - -Embedded Template Library. -https://github.com/ETLCPP/etl -https://www.etlcpp.com - -Copyright(c) 2018 jwellbelove - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files(the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions : - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -******************************************************************************/ - -#ifndef ETL_LWFSM_INCLUDED -#define ETL_LWFSM_INCLUDED - -#include h> - -#include "etl/nullptr.h" - -namespace etl -{ - //*************************************************************************** - /// Simple State Machine - //*************************************************************************** - template - class sfsm - { - public: - - typedef uint32_t state_id_t; - typedef uint32_t event_id_t; - - //************************************************************************* - /// Transition definition - //************************************************************************* - struct transition - { - transition(const event_id_t event_id_, - const state_id_t current_state_id_, - const state_id_t next_state_id_, - void (TObject::* const action_)() = nullptr, - bool (TObject::* const guard_)() = nullptr) - : event_id(event_id_), - current_state(current_state_), - nextState(next_state_), - action(action_), - guard(guard_) - { - } - - const event_id_t event_id; - const state_id_t current_state_id; - const state_id_t next_state_id; - void (TObject::* const action)(); - bool (TObject::* const guard)(); - }; - - //************************************************************************* - /// State definition - //************************************************************************* - struct state - { - state(const state_id_t state_id_, - void (TObject::* const on_enter_)() = nullptr, - void (TObject::* const on_exit_)() = nullptr) - : state_id(state_id_), - on_enter_(on_enter_), - on_exit(on_exit_) - { - } - - state_id_t state_id; - void (TObject::* const on_enter)(); - void (TObject::* const on_exit)(); - }; - - //************************************************************************* - /// - //************************************************************************* - template - sfsm(TObject& object_, - const transition(&transition_table_)[TRANSITION_TABLE_SIZE], - const enter_exit(&state_table_)[STATE_TABLE_SIZE], - const state_id_t state_id_) - : object(object_), - current_state_id(state_id_), - transition_table(&transition_table_[0]), - transition_table_size(TRANSITION_TABLE_SIZE) - state_table(&state_table_[0]), - state_table_size(ENTER_EXIT_TABLE_SIZE) - { - } - - //************************************************************************* - /// - //************************************************************************* - state_id_t get_state() const - { - return current_state; - } - - //************************************************************************* - /// - //************************************************************************* - void process_event(const event_id_t event_id) - { - // Scan the transition table. - transition* transition_table_begin = transition_table; - transition* transition_table_end = transition_table + transition_table_size; - - transition* t = std::find_if(transition_table_begin, - transition_table_end, - is_transition(event_id, current_state_id)); - - // Found an entry? - if (t != transition_table_end) - { - // Shall we execute the transition? - if ((t->guard == nullptr) || ((object->*t->guard)())) - { - // Are we moving to a new state? - const bool to_new_state = (current_state_id != t->next_state_id); - - state* state_table_begin = state_table; - state* state_table_end = state_table + state_table_size; - - // See if we have a state entry. - state* s = std::find_if(state_table_begin, - state_table_end, - is_state(current_state_id)); - - // If the current state has an 'on_exit' and we are changing states, then call it. - if (to_new_state && (s != state_table_end) && (s->on_exit != nullptr)) - { - (object->*(s->on_exit))(); - } - - // Shall we execute the action? - if (t->action != nullptr) - { - (object->*t->action)(); - } - - // See if we have a state entry. - s = std::find_if(state_table_begin, - state_table_end, - is_state(t->next_state_id)); - - // If the new state has an 'on_entry' and we are changing states, then call it. - if (to_new_state && (s != state_table_end) && (s->on_exit != nullptr)) - { - (object->*(s->on_entry))(); - } - - current_state_id = t->next_state_id; - } - } - } - - private: - - state_id_t current_state_id; ///< The current state id. - TObject& object; ///< The object that supplies codition and action member functions - const transition* transition_table; ///< The table of transitions. - const uint32_t transition_table_size; ///< The size of the transition table. - const state* state_table; ///< The table of transitions. - const uint32_t state_table_size; ///< The size of the transition table. - }; -} - -#endif \ No newline at end of file diff --git a/include/etl/state_chart.h b/include/etl/state_chart.h new file mode 100644 index 00000000..692c6322 --- /dev/null +++ b/include/etl/state_chart.h @@ -0,0 +1,289 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_STATE_CHART_INCLUDED +#define ETL_STATE_CHART_INCLUDED + +#include + +#include "etl/platform.h" +#include "etl/nullptr.h" +#include "etl/array.h" +#include "etl/array_view.h" + +namespace etl +{ + //*************************************************************************** + /// Simple Finite State Machine Interface + //*************************************************************************** + class istate_chart + { + public: + + typedef uint32_t state_id_t; + typedef uint32_t event_id_t; + + virtual state_id_t get_state_id() const = 0; + virtual void process_event(const event_id_t event_id) = 0; + + virtual ~istate_chart() + { + } + }; + + //*************************************************************************** + /// Simple Finite State Machine + //*************************************************************************** + template + class state_chart : public istate_chart + { + public: + + //************************************************************************* + /// Transition definition + //************************************************************************* + struct transition + { + transition(const event_id_t event_id_, + const state_id_t current_state_id_, + const state_id_t next_state_id_, + void (TObject::* const action_)() = nullptr, + bool (TObject::* const guard_)() = nullptr) + : event_id(event_id_), + current_state_id(current_state_id_), + next_state_id(next_state_id_), + action(action_), + guard(guard_) + { + } + + const event_id_t event_id; + const state_id_t current_state_id; + const state_id_t next_state_id; + void (TObject::* const action)(); + bool (TObject::* const guard)(); + }; + + //************************************************************************* + /// State definition + //************************************************************************* + struct state + { + state(const state_id_t state_id_, + void (TObject::* const on_entry_)() = nullptr, + void (TObject::* const on_exit_)() = nullptr) + : state_id(state_id_), + on_entry(on_entry_), + on_exit(on_exit_) + { + } + + state_id_t state_id; + void (TObject::* const on_entry)(); + void (TObject::* const on_exit)(); + }; + + //************************************************************************* + /// Constructor. + /// \tparam TRANSITION_TABLE_SIZE The transition table size. + /// \param object_ A reference to the implementation object. + /// \param transition_table_ The table of transitions. + /// \param state_id_ The initial state id. + //************************************************************************* + template + state_chart(TObject& object_, + const etl::array& transition_table_, + const state_id_t state_id_) + : object(object_), + current_state_id(state_id_), + transition_table(transition_table_.begin(), transition_table_.end()) + { + } + + //************************************************************************* + /// Sets the state table. + /// \tparam STATE_TABLE_SIZE The state table size. + /// \param state_table_ A reference to the state table. + //************************************************************************* + template + void set_state_table(const etl::array& state_table_) + { + state_table.assign(state_table_.begin(), state_table_.end()); + } + + //************************************************************************* + /// Gets a reference to the implementation object. + /// \return Reference to the implementation object. + //************************************************************************* + TObject& get_object() + { + return object; + } + + //************************************************************************* + /// Gets a const reference to the implementation object. + /// \return Const reference to the implementation object. + //************************************************************************* + const TObject& get_object() const + { + return object; + } + + //************************************************************************* + /// Gets the current state id. + /// \return The current state id. + //************************************************************************* + state_id_t get_state_id() const + { + return current_state_id; + } + + //************************************************************************* + /// Gets the current state id. + /// \return The current state id. + //************************************************************************* + const state* find_state(state_id_t state_id) + { + if (state_table.empty()) + { + return state_table.end(); + } + else + { + return std::find_if(state_table.begin(), + state_table.end(), + is_state(state_id)); + } + } + + //************************************************************************* + /// Processes the specified event. + /// The state machine will action the first item in the transition table + /// that satisfies the conditions for executing the action. + /// \param event_id The id of the event to process. + //************************************************************************* + void process_event(const event_id_t event_id) + { + // Scan the transition table. + const transition* t = std::find_if(transition_table.begin(), + transition_table.end(), + is_transition(event_id, current_state_id)); + + // Found an entry? + if (t != transition_table.end()) + { + // Shall we execute the transition? + if ((t->guard == nullptr) || ((object.*t->guard)())) + { + const bool to_new_state = (current_state_id != t->next_state_id); + + // Execute the state exit if necessary. + if (to_new_state) + { + // See if we have a state item for the current state. + const state* s = find_state(current_state_id); + + // If the current state has an 'on_exit' then call it. + if ((s != state_table.end()) && (s->on_exit != nullptr)) + { + (object.*(s->on_exit))(); + } + } + + // Shall we execute the action? + if (t->action != nullptr) + { + (object.*t->action)(); + } + + // Execute the state entry if necessary. + if (to_new_state) + { + // See if we have a state item for the next state. + const state* s = find_state(t->next_state_id); + + // If the new state has an 'on_entry' then call it. + if ((s != state_table.end()) && (s->on_entry != nullptr)) + { + (object.*(s->on_entry))(); + } + } + + current_state_id = t->next_state_id; + } + } + } + + private: + + //************************************************************************* + struct is_transition + { + is_transition(event_id_t event_id_, state_id_t state_id_) + : event_id(event_id_), + state_id(state_id_) + { + } + + bool operator()(const transition& t) const + { + return (t.event_id == event_id) && (t.current_state_id == state_id); + } + + const event_id_t event_id; + const state_id_t state_id; + }; + + //************************************************************************* + struct is_state + { + is_state(state_id_t state_id_) + : state_id(state_id_) + { + } + + bool operator()(const state& s) const + { + return (s.state_id == state_id); + } + + const state_id_t state_id; + }; + + // Disabled + state_chart(const state_chart&) ETL_DELETE; + state_chart& operator =(const state_chart&) ETL_DELETE; + + TObject& object; ///< The object that supplies guard and action member functions. + state_id_t current_state_id; ///< The current state id. + const etl::array_view transition_table; ///< The table of transitions. + etl::array_view state_table; ///< The table of states. + }; +} + +#endif diff --git a/include/etl/version.h b/include/etl/version.h index d8f02332..c5444289 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -37,13 +37,13 @@ SOFTWARE. /// Definitions of the ETL version ///\ingroup utilities -#define ETL_VERSION "11.16.6" -#define ETL_VERSION_W L"11.16.6" -#define ETL_VERSION_U16 u"11.16.6" -#define ETL_VERSION_U32 U"11.16.6" +#define ETL_VERSION "11.17.0" +#define ETL_VERSION_W L"11.17.0" +#define ETL_VERSION_U16 u"11.17.0" +#define ETL_VERSION_U32 U"11.17.0" #define ETL_VERSION_MAJOR 11 -#define ETL_VERSION_MINOR 16 -#define ETL_VERSION_PATCH 6 +#define ETL_VERSION_MINOR 17 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION_VALUE ((ETL_VERSION_MAJOR * 10000) + (ETL_VERSION_MINOR * 100) + ETL_VERSION_PATCH) #endif diff --git a/support/Release notes.txt b/support/Release notes.txt index a7b2be00..69a38502 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,7 @@ +=============================================================================== +11.17.0 +Added etl::state_chart + =============================================================================== 11.16.6 Fixed implementations of key_comp and value_comp for maps and sets diff --git a/test/codeblocks/ETL.cbp b/test/codeblocks/ETL.cbp index e66e4acd..b4a78c2f 100644 --- a/test/codeblocks/ETL.cbp +++ b/test/codeblocks/ETL.cbp @@ -279,6 +279,7 @@ + @@ -423,6 +424,7 @@ + diff --git a/test/test_state_chart.cpp b/test/test_state_chart.cpp new file mode 100644 index 00000000..a7730ce8 --- /dev/null +++ b/test/test_state_chart.cpp @@ -0,0 +1,387 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "etl/state_chart.h" +#include "etl/enum_type.h" +#include "etl/queue.h" +#include "etl/array.h" + +#include + +namespace +{ + //*************************************************************************** + // Events + struct EventId + { + enum enum_type + { + START, + STOP, + EMERGENCY_STOP, + STOPPED, + SET_SPEED + }; + + ETL_DECLARE_ENUM_TYPE(EventId, etl::istate_chart::event_id_t) + ETL_ENUM_TYPE(START, "Start") + ETL_ENUM_TYPE(STOP, "Stop") + ETL_ENUM_TYPE(EMERGENCY_STOP, "Emergency Stop") + ETL_ENUM_TYPE(STOPPED, "Stopped") + ETL_ENUM_TYPE(SET_SPEED, "Set Speed") + ETL_END_ENUM_TYPE + }; + + //*************************************************************************** + // States + struct StateId + { + enum enum_type + { + IDLE, + RUNNING, + WINDING_DOWN, + NUMBER_OF_STATES + }; + + ETL_DECLARE_ENUM_TYPE(StateId, etl::istate_chart::state_id_t) + ETL_ENUM_TYPE(IDLE, "Idle") + ETL_ENUM_TYPE(RUNNING, "Running") + ETL_ENUM_TYPE(WINDING_DOWN, "Winding Down") + ETL_END_ENUM_TYPE + }; + + //*********************************** + // The motor control FSM. + //*********************************** + class MotorControl : public etl::state_chart + { + public: + + MotorControl() + : state_chart(*this, transitionTable, StateId::IDLE) + { + this->set_state_table(stateTable); + ClearStatistics(); + } + + //*********************************** + void ClearStatistics() + { + startCount = 0; + stopCount = 0; + setSpeedCount = 0; + stoppedCount = 0; + isLampOn = false; + speed = 0; + windingDown = 0; + } + + //*********************************** + void OnStart() + { + ++startCount; + } + + //*********************************** + void OnStop() + { + ++stopCount; + } + + //*********************************** + void OnStopped() + { + ++stoppedCount; + } + + //*********************************** + void OnSetSpeed() + { + ++setSpeedCount; + SetSpeedValue(100); + } + + //*********************************** + void OnEnterIdle() + { + TurnRunningLampOff(); + } + + //*********************************** + void OnEnterRunning() + { + TurnRunningLampOn(); + } + + //*********************************** + void OnEnterWindingDown() + { + ++windingDown; + } + + //*********************************** + void OnExitWindingDown() + { + --windingDown; + } + + //*********************************** + void SetSpeedValue(int speed_) + { + speed = speed_; + } + + //*********************************** + bool Guard() + { + return guard; + } + + //*********************************** + void TurnRunningLampOn() + { + isLampOn = true; + } + + //*********************************** + void TurnRunningLampOff() + { + isLampOn = false; + } + + int startCount; + int stopCount; + int setSpeedCount; + int stoppedCount; + bool isLampOn; + int speed; + int windingDown; + + bool guard; + + static const etl::array transitionTable; + static const etl::array stateTable; + }; + + //*************************************************************************** + const etl::array MotorControl::transitionTable = + { + MotorControl::transition(EventId::START, StateId::IDLE, StateId::RUNNING, &MotorControl::OnStart, &MotorControl::Guard), + MotorControl::transition(EventId::STOP, StateId::RUNNING, StateId::WINDING_DOWN, &MotorControl::OnStop), + MotorControl::transition(EventId::STOPPED, StateId::WINDING_DOWN, StateId::IDLE, &MotorControl::OnStopped), + MotorControl::transition(EventId::EMERGENCY_STOP, StateId::RUNNING, StateId::IDLE, &MotorControl::OnStop), + MotorControl::transition(EventId::SET_SPEED, StateId::RUNNING, StateId::RUNNING, &MotorControl::OnSetSpeed) + }; + + //*************************************************************************** + const etl::array MotorControl::stateTable = + { + MotorControl::state(StateId::IDLE, &MotorControl::OnEnterIdle, nullptr), + MotorControl::state(StateId::RUNNING, &MotorControl::OnEnterRunning, nullptr), + MotorControl::state(StateId::WINDING_DOWN, &MotorControl::OnEnterWindingDown, &MotorControl::OnExitWindingDown) + }; + + MotorControl motorControl; + + SUITE(test_state_chart_class) + { + //************************************************************************* + TEST(test_state_chart) + { + motorControl.ClearStatistics(); + + // Now in Idle state. + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(0, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send unhandled events. + motorControl.process_event(EventId::STOP); + motorControl.process_event(EventId::STOPPED); + + CHECK_EQUAL(StateId::IDLE, motorControl.get_state_id()); + + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(0, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + + // Send Start event. + motorControl.guard = false; + motorControl.process_event(EventId::START); + + // Still in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(0, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send Start event. + motorControl.guard = true; + motorControl.process_event(EventId::START); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send unhandled events. + motorControl.process_event(EventId::START); + motorControl.process_event(EventId::STOPPED); + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send SetSpeed event. + motorControl.process_event(EventId::SET_SPEED); + + // Still in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send Stop event. + motorControl.process_event(EventId::STOP); + + // Now in WindingDown state. + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(1, motorControl.windingDown); + + // Send unhandled events. + motorControl.process_event(EventId::START); + motorControl.process_event(EventId::STOP); + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(1, motorControl.windingDown); + + // Send Stopped event. + motorControl.process_event(EventId::STOPPED); + + // Now in Idle state. + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(1, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + } + + //************************************************************************* + TEST(test_fsm_emergency_stop) + { + motorControl.ClearStatistics(); + + // Now in Idle state. + + // Send Start event. + motorControl.process_event(EventId::START); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send emergency Stop event. + motorControl.process_event(EventId::EMERGENCY_STOP); + + // Now in Idle state. + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + } + }; +} diff --git a/test/vs2017/cpp.hint b/test/vs2017/cpp.hint new file mode 100644 index 00000000..82067cca --- /dev/null +++ b/test/vs2017/cpp.hint @@ -0,0 +1,8 @@ +// Hint files help the Visual Studio IDE interpret Visual C++ identifiers +// such as names of functions and macros. +// For more information see https://go.microsoft.com/fwlink/?linkid=865984 +#define ETL_CONSTEXPR +#define ETL_CONSTEXPR_IF +#define ETL_DELETE + + diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 7b00e497..fd081b11 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -371,7 +371,7 @@ - + @@ -729,6 +729,7 @@ true true + @@ -769,6 +770,7 @@ + diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 4a284a91..32db94a6 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -693,7 +693,7 @@ ETL\Utilities\Atomic - + ETL\Frameworks @@ -1139,6 +1139,9 @@ Source Files + + Source Files + @@ -1177,6 +1180,7 @@ Resource Files\Generators +