Merge branch 'feature/state-chart-to-have-optional-data-parameter' into development

This commit is contained in:
John Wellbelove 2021-02-28 11:06:32 +00:00
commit bec129a192
4 changed files with 813 additions and 3 deletions

View File

@ -35,6 +35,7 @@ SOFTWARE.
#include "etl/nullptr.h"
#include "etl/array.h"
#include "etl/array_view.h"
#include "etl/type_traits.h"
namespace etl
{
@ -74,11 +75,332 @@ namespace etl
state_id_t current_state_id; ///< The current state id.
};
//***************************************************************************
/// Simple Finite State Machine
/// Data parameter for events.
//***************************************************************************
template <typename TObject, typename TDataParameter = void>
class state_chart : public istate_chart
{
public:
typedef TDataParameter data_parameter_type;
typedef typename etl::types<data_parameter_type>::type data_type;
//*************************************************************************
/// Transition definition
//*************************************************************************
struct transition
{
ETL_CONSTEXPR transition(const state_id_t current_state_id_,
const event_id_t event_id_,
const state_id_t next_state_id_,
void (TObject::* const action_)(data_parameter_type) = ETL_NULLPTR,
bool (TObject::* const guard_)() = ETL_NULLPTR)
: from_any_state(false),
current_state_id(current_state_id_),
event_id(event_id_),
next_state_id(next_state_id_),
action(action_),
guard(guard_)
{
}
ETL_CONSTEXPR transition(const event_id_t event_id_,
const state_id_t next_state_id_,
void (TObject::* const action_)(data_parameter_type) = ETL_NULLPTR,
bool (TObject::* const guard_)() = ETL_NULLPTR)
: from_any_state(true),
current_state_id(0),
event_id(event_id_),
next_state_id(next_state_id_),
action(action_),
guard(guard_)
{
}
const bool from_any_state;
const state_id_t current_state_id;
const event_id_t event_id;
const state_id_t next_state_id;
void (TObject::* const action)(data_parameter_type);
bool (TObject::* const guard)();
};
//*************************************************************************
/// State definition
//*************************************************************************
struct state
{
ETL_CONSTEXPR state(const state_id_t state_id_,
void (TObject::* const on_entry_)() = ETL_NULLPTR,
void (TObject::* const on_exit_)() = ETL_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.
/// \param object_ A reference to the implementation object.
/// \param transition_table_begin_ The start of the table of transitions.
/// \param transition_table_end_ The end of the table of transitions.
/// \param state_id_ The initial state id.
//*************************************************************************
ETL_CONSTEXPR state_chart(TObject& object_,
const transition* transition_table_begin_,
const transition* transition_table_end_,
const state_id_t state_id_)
: istate_chart(state_id_),
object(object_),
transition_table(transition_table_begin_, transition_table_end_),
started(false)
{
}
//*************************************************************************
/// Constructor.
/// \param object_ A reference to the implementation object.
/// \param transition_table_begin_ The start of the table of transitions.
/// \param transition_table_end_ The end of the table of transitions.
/// \param state_table_begin_ The start of the state table.
/// \param state_table_end_ The end of the state table.
/// \param state_id_ The initial state id.
//*************************************************************************
ETL_CONSTEXPR state_chart(TObject& object_,
const transition* transition_table_begin_,
const transition* transition_table_end_,
const state* state_table_begin_,
const state* state_table_end_,
const state_id_t state_id_)
: istate_chart(state_id_),
object(object_),
transition_table(transition_table_begin_, transition_table_end_),
state_table(state_table_begin_, state_table_end_),
started(false)
{
}
//*************************************************************************
/// Sets the transition table.
/// \param state_table_begin_ The start of the state table.
/// \param state_table_end_ The end of the state table.
//*************************************************************************
void set_transition_table(const transition* transition_table_begin_,
const transition* transition_table_end_)
{
transition_table.assign(transition_table_begin_, transition_table_end_);
}
//*************************************************************************
/// Sets the state table.
/// \param state_table_begin_ The start of the state table.
/// \param state_table_end_ The end of the state table.
//*************************************************************************
void set_state_table(const state* state_table_begin_,
const state* state_table_end_)
{
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.
//*************************************************************************
const state* find_state(state_id_t state_id)
{
if (state_table.empty())
{
return state_table.end();
}
else
{
return etl::find_if(state_table.begin(),
state_table.end(),
is_state(state_id));
}
}
//*************************************************************************
///
//*************************************************************************
virtual void start(const bool on_entry_initial = true) ETL_OVERRIDE
{
if (!started)
{
if (on_entry_initial)
{
// See if we have a state item for the initial state.
const state* s = find_state(current_state_id);
// If the initial state has an 'on_entry' then call it.
if ((s != state_table.end()) && (s->on_entry != ETL_NULLPTR))
{
(object.*(s->on_entry))();
}
}
started = true;
}
}
//*************************************************************************
/// Processes the specified event.
/// The state machine will action the <b>first</b> item in the transition table
/// that satisfies the conditions for executing the action.
/// \param event_id The id of the event to process.
//*************************************************************************
virtual void process_event(const event_id_t event_id) ETL_OVERRIDE
{
process_event(event_id, data_type());
}
//*************************************************************************
/// Processes the specified event.
/// The state machine will action the <b>first</b> item in the transition table
/// that satisfies the conditions for executing the action.
/// \param event_id The id of the event to process.
/// \param data The data to pass to the action.
//*************************************************************************
void process_event(const event_id_t event_id, data_parameter_type data)
{
if (started)
{
const transition* t = transition_table.begin();
// Keep looping until we execute a transition or reach the end of the table.
while (t != transition_table.end())
{
// Scan the transition table from the latest position.
t = etl::find_if(t,
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 == ETL_NULLPTR) || ((object.*t->guard)()))
{
// Shall we execute the action?
if (t->action != ETL_NULLPTR)
{
(object.*t->action)(data);
}
// Changing state?
if (current_state_id != t->next_state_id)
{
const state* s;
// See if we have a state item for the current 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 != ETL_NULLPTR))
{
(object.*(s->on_exit))();
}
current_state_id = t->next_state_id;
// See if we have a state item for the new state.
s = find_state(current_state_id);
// If the new state has an 'on_entry' then call it.
if ((s != state_table.end()) && (s->on_entry != ETL_NULLPTR))
{
(object.*(s->on_entry))();
}
}
t = transition_table.end();
}
else
{
// Start the search from the next item in the table.
++t;
}
}
}
}
}
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.from_any_state || (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.
const etl::array_view<const transition> transition_table; ///< The table of transitions.
etl::array_view<const state> state_table; ///< The table of states.
bool started; ///< Set if the state chart has been started.
};
//***************************************************************************
/// Simple Finite State Machine
//***************************************************************************
template <typename TObject>
class state_chart : public istate_chart
class state_chart<TObject, void> : public istate_chart
{
public:
@ -243,7 +565,7 @@ namespace etl
//*************************************************************************
///
//*************************************************************************
virtual void start(const bool on_entry_initial = true)
virtual void start(const bool on_entry_initial = true) ETL_OVERRIDE
{
if (!started)
{
@ -269,7 +591,7 @@ namespace etl
/// that satisfies the conditions for executing the action.
/// \param event_id The id of the event to process.
//*************************************************************************
virtual void process_event(const event_id_t event_id)
virtual void process_event(const event_id_t event_id) ETL_OVERRIDE
{
if (started)
{

View File

@ -0,0 +1,484 @@
/******************************************************************************
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++/UnitTest++.h"
#include "etl/state_chart.h"
#include "etl/enum_type.h"
#include "etl/queue.h"
#include "etl/array.h"
#include <iostream>
namespace
{
//***************************************************************************
// Events
struct EventId
{
enum enum_type
{
START,
STOP,
EMERGENCY_STOP,
STOPPED,
SET_SPEED,
ABORT
};
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_ENUM_TYPE(ABORT, "Abort")
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<MotorControl, const int&>
{
public:
MotorControl()
: state_chart<MotorControl, const int&>(*this, transitionTable.begin(), transitionTable.end(), StateId::IDLE)
{
this->set_state_table(stateTable.begin(), stateTable.end());
ClearStatistics();
}
//***********************************
void ClearStatistics()
{
startCount = 0;
stopCount = 0;
setSpeedCount = 0;
stoppedCount = 0;
isLampOn = false;
speed = 0;
windingDown = 0;
entered_idle = false;
null = 0;
data = 0;
}
//***********************************
void OnStart(data_parameter_type d)
{
data = d;
++startCount;
}
//***********************************
void OnStop(data_parameter_type d)
{
data = d;
++stopCount;
}
//***********************************
void OnStopped(data_parameter_type d)
{
data = d;
++stoppedCount;
}
//***********************************
void OnSetSpeed(data_parameter_type d)
{
data = d;
++setSpeedCount;
SetSpeedValue(100);
}
//***********************************
void OnEnterIdle()
{
TurnRunningLampOff();
entered_idle = true;
}
//***********************************
void OnEnterRunning()
{
TurnRunningLampOn();
}
//***********************************
void OnEnterWindingDown()
{
++windingDown;
}
//***********************************
void OnExitWindingDown()
{
--windingDown;
}
//***********************************
void SetSpeedValue(int speed_)
{
speed = speed_;
}
//***********************************
bool Guard()
{
return guard;
}
//***********************************
bool NotGuard()
{
return !guard;
}
//***********************************
void TurnRunningLampOn()
{
isLampOn = true;
}
//***********************************
void TurnRunningLampOff()
{
isLampOn = false;
}
//***********************************
void Null(data_parameter_type d)
{
data = d;
++null;
}
int startCount;
int stopCount;
int setSpeedCount;
int stoppedCount;
bool isLampOn;
int speed;
int windingDown;
bool entered_idle;
int null;
int data;
bool guard;
static const etl::array<MotorControl::transition, 7> transitionTable;
static const etl::array<MotorControl::state, 3> stateTable;
};
//***************************************************************************
constexpr etl::array<MotorControl::transition, 7> MotorControl::transitionTable =
{
MotorControl::transition(StateId::IDLE, EventId::START, StateId::RUNNING, &MotorControl::OnStart, &MotorControl::Guard),
MotorControl::transition(StateId::IDLE, EventId::START, StateId::IDLE, &MotorControl::Null, &MotorControl::NotGuard),
MotorControl::transition(StateId::RUNNING, EventId::STOP, StateId::WINDING_DOWN, &MotorControl::OnStop),
MotorControl::transition(StateId::RUNNING, EventId::EMERGENCY_STOP, StateId::IDLE, &MotorControl::OnStop),
MotorControl::transition(StateId::RUNNING, EventId::SET_SPEED, StateId::RUNNING, &MotorControl::OnSetSpeed),
MotorControl::transition(StateId::WINDING_DOWN, EventId::STOPPED, StateId::IDLE, &MotorControl::OnStopped),
MotorControl::transition( EventId::ABORT, StateId::IDLE)
};
//***************************************************************************
constexpr etl::array<MotorControl::state, 3> 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();
// 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);
CHECK_EQUAL(false, motorControl.entered_idle);
// Send Start event (state chart not started).
motorControl.guard = true;
motorControl.process_event(EventId::START, 1);
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(0, motorControl.data);
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);
CHECK_EQUAL(false, motorControl.entered_idle);
// Start the state chart
motorControl.guard = true;
motorControl.start();
CHECK_EQUAL(true, motorControl.entered_idle);
// Send unhandled events.
motorControl.process_event(EventId::STOP, 2);
motorControl.process_event(EventId::STOPPED, 3);
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(0, motorControl.data);
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 = false;
motorControl.process_event(EventId::START, 4);
// Still in Idle state.
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(4, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
// Send Start event.
motorControl.guard = true;
motorControl.process_event(EventId::START, 5);
// Now in Running state.
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
CHECK_EQUAL(5, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
// Send unhandled events.
motorControl.process_event(EventId::START, 6);
motorControl.process_event(EventId::STOPPED, 7);
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
CHECK_EQUAL(5, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
// Send SetSpeed event.
motorControl.process_event(EventId::SET_SPEED, 8);
// Still in Running state.
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
CHECK_EQUAL(8, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
// Send Stop event.
motorControl.process_event(EventId::STOP, 9);
// Now in WindingDown state.
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id()));
CHECK_EQUAL(9, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
// Send unhandled events.
motorControl.process_event(EventId::START, 10);
motorControl.process_event(EventId::STOP, 11);
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id()));
CHECK_EQUAL(9, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
// Send Stopped event.
motorControl.process_event(EventId::STOPPED, 12);
// Now in Idle state.
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(12, motorControl.data);
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);
CHECK_EQUAL(1, motorControl.null);
}
//*************************************************************************
TEST(test_fsm_emergency_stop)
{
motorControl.ClearStatistics();
// Now in Idle state.
// Send Start event.
motorControl.process_event(EventId::START, 1);
// Now in Running state.
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
CHECK_EQUAL(1, motorControl.data);
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, 2);
// Now in Idle state.
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(2, motorControl.data);
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);
}
//*************************************************************************
TEST(test_fsm_abort)
{
motorControl.ClearStatistics();
// Now in Idle state.
// Send Start event.
motorControl.process_event(EventId::START, 1);
// Now in Running state.
motorControl.process_event(EventId::ABORT, 2);
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
// Send Start event.
motorControl.process_event(EventId::START, 3);
// Now in Running state.
// Send Stop event.
motorControl.process_event(EventId::STOP, 4);
// Now in WindingDown state.
motorControl.process_event(EventId::ABORT, 5);
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
}
};
}

View File

@ -1950,6 +1950,7 @@
<ClCompile Include="..\test_state_chart.cpp" />
<ClCompile Include="..\test_smallest.cpp" />
<ClCompile Include="..\test_stack.cpp" />
<ClCompile Include="..\test_state_chart_with_data_parameter.cpp" />
<ClCompile Include="..\test_string_utilities_std.cpp" />
<ClCompile Include="..\test_string_char.cpp" />
<ClCompile Include="..\test_string_char_external_buffer.cpp" />

View File

@ -1496,6 +1496,9 @@
<ClCompile Include="..\test_queue_lockable_small.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\test_state_chart_with_data_parameter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\library.properties">