diff --git a/scxml.workspace b/scxml.workspace
new file mode 100644
index 0000000..bbcb33b
--- /dev/null
+++ b/scxml.workspace
@@ -0,0 +1,8 @@
+
+
+
+
+
+ src
+
+
diff --git a/scxml_core/include/scxml_core/scxml_sm_interface.h b/scxml_core/include/scxml_core/scxml_sm_interface.h
index 9e8b49e..de408e5 100644
--- a/scxml_core/include/scxml_core/scxml_sm_interface.h
+++ b/scxml_core/include/scxml_core/scxml_sm_interface.h
@@ -3,11 +3,13 @@
#include
#include
#include
+#include
+#include
namespace scxml_core
{
/** @brief Container for states and their associated transitions */
-using StateTransitionMap = std::map>;
+using StateTransitionMap = std::map>>;
/** @brief Creates a map of known states and transition events associated with those states */
StateTransitionMap getStateTransitionMap(const std::string& scxml_file);
@@ -23,11 +25,24 @@ class ScxmlSMInterface
public:
ScxmlSMInterface(const std::string& scxml_file);
+ /**
+ * @brief checks if an event exists
+ */
+ bool eventExists(const QString& event, std::set> events);
+
+ /**
+ * @brief gets the state to which a desired transition occurs
+ * @param search_text - insert transition text you'd like to match
+ * @throws if you don't have that transition, it will return itself as it's neighbor
+ */
+
+ const QString getNeighbor(const QString& state, const QString& search_text);
/**
* @brief Adds a callback to the input state that will be invoked on entry to the state
* @param async - flag for executing the input callback asynchronously
* @throws exception if the state does not exist in the state machine
*/
+
void addOnEntryCallback(const QString& state, const std::function& callback, bool async = false);
/**
diff --git a/scxml_core/src/demo.cpp b/scxml_core/src/demo.cpp
index 7ab4643..102a1c2 100644
--- a/scxml_core/src/demo.cpp
+++ b/scxml_core/src/demo.cpp
@@ -38,45 +38,47 @@ int main(int argc, char** argv)
// Get the active state and available events
QStringList active_states = interface.getSM()->activeStateNames();
const QString& current_state = active_states.at(0);
- QSet available_events = map.at(current_state);
+ std::set> available_events = map.at(current_state);
std::stringstream ss;
ss << "Available events: [ ";
- for (const QString& event : available_events)
+ for (auto& pair : available_events)
{
+ const QString& event = pair.first;
ss << event.toStdString() << " ";
}
ss << "]";
- // Get user input as to which event to execute
- bool done = false;
- while (!done)
- {
- std::cout << ss.str() << std::endl;
+ // // Get user input as to which event to execute
+ // bool done = false;
+ // while (!done)
+ // {
+ // std::cout << ss.str() << std::endl;
- // Get the character input
- auto input = std::cin.get();
- // Throw away the enter input
- std::cin.get();
+ // // Get the character input
+ // auto input = std::cin.get();
+ // // Throw away the enter input
+ // std::cin.get();
- if (std::isdigit(input))
- {
- int idx = static_cast(input) - 48;
- if (idx < available_events.size())
- {
- interface.submitEvent(available_events.toList().at(idx));
- done = true;
- }
- else
- {
- std::cout << "Index " << idx << " was not in range [0, " << available_events.size() - 1 << "]" << std::endl;
- }
- }
- else
- {
- std::cout << "Input must be numeric" << std::endl;
- }
- }
+ // if (std::isdigit(input))
+ // {
+ // int idx = static_cast(input) - 48;
+ // if (idx < available_events.size())
+ // {
+ // interface.submitEvent(available_events.toList().at(idx));
+ // done = true;
+ // }
+ // else
+ // {
+ // std::cout << "Index " << idx << " was not in range [0, " << available_events.size() - 1 << "]" <<
+ // std::endl;
+ // }
+ // }
+ // else
+ // {
+ // std::cout << "Input must be numeric" << std::endl;
+ // }
+ // }
}
}
catch (const std::exception& ex)
diff --git a/scxml_core/src/scxml_sm_interface.cpp b/scxml_core/src/scxml_sm_interface.cpp
index 63ac0f7..aef81b7 100644
--- a/scxml_core/src/scxml_sm_interface.cpp
+++ b/scxml_core/src/scxml_sm_interface.cpp
@@ -10,6 +10,7 @@ static const char* HISTORY_STATE_ELEMENT = "history";
static const char* HISTORY_STATE_ID_ATTRIBUTE = "id";
static const char* TRANSITION_ELEMENT = "transition";
static const char* EVENT_ATTRIBUTE = "event";
+static const char* TARGET_ATTRIBUTE = "target";
/**
* @brief Recursively adds states and transitions to the map
@@ -18,7 +19,7 @@ static const char* EVENT_ATTRIBUTE = "event";
*/
static void getStateTransitionsRecursive(tinyxml2::XMLElement* state,
scxml_core::StateTransitionMap& map,
- QSet inherited_events)
+ std::set> inherited_events)
{
using namespace tinyxml2;
@@ -46,8 +47,14 @@ static void getStateTransitionsRecursive(tinyxml2::XMLElement* state,
throw std::runtime_error("'" + std::string(TRANSITION_ELEMENT) + "' element does not have '" +
std::string(EVENT_ATTRIBUTE) + "' attribute");
+ const char* name = transition->Attribute(TARGET_ATTRIBUTE);
+
+ std::pair list = std::make_pair(QString(event), QString(name));
+
+ inherited_events.insert(list);
+
// Add the event name to the map
- map.at(state_id).insert(event);
+ map[state_id] = inherited_events;
// Get the next transition element
transition = transition->NextSiblingElement(TRANSITION_ELEMENT);
@@ -71,7 +78,7 @@ static void getStateTransitionsRecursive(tinyxml2::XMLElement* state,
std::string(HISTORY_STATE_ID_ATTRIBUTE) + "' attribute");
// Add this state to the map
- map[QString(id)] = QSet{};
+ map[QString(id)] = std::set>{};
// History states do not have transitions or nested states, so no need to recurse into it
history = history->NextSiblingElement(HISTORY_STATE_ELEMENT);
@@ -102,7 +109,7 @@ StateTransitionMap getStateTransitionMap(const std::string& scxml_file)
StateTransitionMap map;
while (state)
{
- getStateTransitionsRecursive(state, map, QSet{});
+ getStateTransitionsRecursive(state, map, std::set>{});
state = state->NextSiblingElement(STATE_ELEMENT);
}
@@ -136,6 +143,43 @@ ScxmlSMInterface::ScxmlSMInterface(const std::string& scxml_file)
}
}
+// use this to determine the next state in the state machine, given the name of the transition you'd like to query
+const QString ScxmlSMInterface::getNeighbor(const QString& state, const QString& search_text)
+{
+ QString next_state;
+
+ for (auto& pair : state_transition_map_.at(state))
+ {
+ if (pair.first == search_text)
+ {
+ return pair.second;
+ }
+ }
+ return next_state;
+}
+
+bool ScxmlSMInterface::eventExists(const QString& event, std::set> events)
+{
+ int i = 0;
+ const int j = events.size();
+ for (auto& pair : events)
+ {
+ if (pair.first == event)
+ {
+ return true;
+ }
+ else
+ {
+ i = i + 1;
+ }
+
+ if (i == j)
+ {
+ return false;
+ }
+ }
+}
+
void ScxmlSMInterface::addOnEntryCallback(const QString& state, const std::function& callback, bool async)
{
if (state_transition_map_.find(state) == state_transition_map_.end())
@@ -165,7 +209,7 @@ bool ScxmlSMInterface::submitEvent(const QString& event, bool force)
// Ensure at least one of the active states has the specified transition
auto it = std::find_if(active_states.begin(), active_states.end(), [this, event](const QString& state) -> bool {
- return this->state_transition_map_.at(state).contains(event);
+ return eventExists(event, state_transition_map_.at(state));
});
if (it == active_states.end())
@@ -185,7 +229,7 @@ bool ScxmlSMInterface::submitEvent(const QString& event, bool force)
{
for (const QString& state : active_states)
{
- if (state_transition_map_.at(state).contains(event))
+ if (eventExists(event, state_transition_map_.at(state)))
{
// Check if the asynchronous callback is finished before submitting the event
if (!future_map_.at(state).isFinished())