fipa_acl  1.4
statemachine.cpp
Go to the documentation of this file.
1 /*
2 big TODO(for me - not relevant right now but for the general case): line 203(behaviour in case a conversation is successfully cancelled);
3 
4 note for future tweak: make it possible that once a machine has been built, if we try to start it with a message and it fails,
5  make it possible to be able to attempt to start it again during runtime(the same machine - currently it needs rebuilding)
6 
7 note for future: the "in reply to" policy needs to be defined somehow (not necessarily important for this stage); we need to define
8  in which contexts the "in reply to" field must be filled in in order to correctly validate a message;
9 
10  current policy is: if the field is not filled in then it means it should not be(thus considerred valid)
11 
12  another intuitive policy might be(not yet sure whether it stands for the general case): check all the messages
13  from preceding state and if there if there is no message TO the current sender with a "in reply to" field filled in,
14  then it should not be filled in
15 */
16 #include "statemachine.h"
17 #include "state.h"
18 #include "transition.h"
19 
20 #include <iostream>
21 #include <algorithm>
22 #include <set>
23 #include <stdexcept>
24 #include <base-logging/Logging.hpp>
25 
26 namespace fipa {
27 namespace acl {
28 
30 {
31  // Testing whether there are any undefined state referred to
32  // Selection of unique state ids
33  std::set<StateId> referencedStates;
34  std::map<StateId, State>::const_iterator statesIt = mStates.begin();
35 
36  for(; statesIt != mStates.end(); ++statesIt)
37  {
38  const State& state = statesIt->second;
39  std::vector<Transition> transitions = state.getTransitions();
40  std::vector<Transition>::const_iterator transitionsIt = transitions.begin();
41 
42  for(; transitionsIt != transitions.end(); ++transitionsIt)
43  {
44  referencedStates.insert(transitionsIt->getTargetStateId());
45  referencedStates.insert(transitionsIt->getSourceStateId());
46  }
47  }
48 
49  std::set<StateId>::const_iterator referencedStatesIt = referencedStates.begin();
50  for(; referencedStatesIt != referencedStates.end(); ++referencedStatesIt)
51  {
52  statesIt = mStates.find(*referencedStatesIt);
53  if(statesIt == mStates.end())
54  {
55  std::string msg = "Unknown state '" + *referencedStatesIt + "' referred to in statemachine";
56  LOG_ERROR("%s", msg.c_str());
57  throw std::runtime_error(msg);
58  }
59  }
60 }
61 
63 {
64  mCurrentStateId = mInitialStateId;
65 }
66 
67 void StateMachine::addState(const State& state)
68 {
69  std::map<StateId, State>::const_iterator it = mStates.find(state.getId());
70  if(it != mStates.end())
71  {
72  std::string msg = "State '" + state.getId() + "' already defined";
73  LOG_ERROR("%s", msg.c_str());
74  throw std::runtime_error(msg);
75  } else {
76  mStates[state.getId()] = state;
77  }
78 }
79 
81 {
82  std::map<StateId, State>::iterator it = mStates.begin();
83  for(; it != mStates.end(); ++it)
84  {
85  std::vector<Transition> transitions = it->second.getTransitions();
86  std::vector<Transition>::const_iterator transitionIt = transitions.begin();
87  for(; transitionIt != transitions.end(); ++transitionIt)
88  {
89  mRoleMapping.addRole(transitionIt->getSenderRole());
90  mRoleMapping.addRole(transitionIt->getReceiverRole());
91  }
92  }
93 
94 }
95 
96 void StateMachine::generateDefaultTransitions()
97 {
98  std::map<StateId, State>::iterator it = mStates.begin();
99  for(; it != mStates.end(); ++it)
100  {
101  it->second.generateDefaultTransitions();
102  }
103 }
104 
105 void StateMachine::generateDefaultStates()
106 {
107  try {
108  // adding the default states
109  default_state::NotUnderstood notUnderstood;
110  addState(*dynamic_cast<State*>(&notUnderstood));
111 
112  default_state::ConversationCancelling cancelling;
113  addState(*dynamic_cast<State*>(&cancelling));
114 
115  default_state::ConversationCancelSuccess cancelSuccess;
116  addState(*dynamic_cast<State*>(&cancelSuccess));
117 
118  default_state::ConversationCancelFailure cancelFailure;
119  addState(*dynamic_cast<State*>(&cancelFailure));
120 
121  default_state::GeneralFailure generalFailure;
122  addState(*dynamic_cast<State*>(&generalFailure));
123 
124  } catch (const std::runtime_error& e)
125  {
126  LOG_FATAL("%s", e.what());
127  // The states should be unqiue
128  assert(false);
129  }
130 }
131 
133 {
134  if(mCurrentStateId.empty())
135  {
136  throw std::runtime_error("Statemachine has not been properly initialized: current state not set");
137  } else {
138  std::map<StateId, State>::iterator it = mStates.find(mCurrentStateId);
139  if(it == mStates.end())
140  {
141  throw std::runtime_error("Statemachine has not been properly initialized: current state not found");
142  }
143  return it->second;
144  }
145 }
146 
147 // XXX exact copy of the above but with const_iterator
149 {
150  if(mCurrentStateId.empty())
151  {
152  throw std::runtime_error("Statemachine has not been properly initialized: current state not set");
153  } else {
154  std::map<StateId, State>::const_iterator it = mStates.find(mCurrentStateId);
155  if(it == mStates.end())
156  {
157  throw std::runtime_error("Statemachine has not been properly initialized: current state not found");
158  }
159  return it->second;
160  }
161 }
162 
164 {
165  mRoleMapping.setSelf(self);
166 }
167 
168 void StateMachine::updateRoleMapping(const ACLMessage& msg, const Transition& transition)
169 {
170  mRoleMapping.addExpectedAgent(transition.getSenderRole(), msg.getSender());
171  AgentIDList receivers = msg.getAllReceivers();
172  AgentIDList::const_iterator it = receivers.begin();
173  for(; it != receivers.end(); ++it)
174  {
175  mRoleMapping.addExpectedAgent(transition.getReceiverRole(), *it);
176  }
177 }
178 
179 void StateMachine::consumeSubStateMachineMessage(const ACLMessage& msg, const StateMachine& stateMachine, int numberOfSubConversations)
180 {
181  LOG_DEBUG("StateMachine consumeSubStateMachineMessage");
182 
183  State& currentState = getCurrentStateModifiably();
184  currentState.consumeSubStateMachineMessage(msg, stateMachine, mRoleMapping, numberOfSubConversations);
185 }
186 
188 {
189  LOG_DEBUG("StateMachine consumeMessage");
190 
191  try
192  {
193  const State& currentState = getCurrentState();
194  const Transition& transition = currentState.getTransition(msg, mMessageArchive, mRoleMapping);
195  updateRoleMapping(msg, transition);
196  mMessageArchive.addMessage(msg);
197 
198  // Perform transition
199  mCurrentStateId = transition.getTargetStateId();
200  }
201  catch(const std::exception& e)
202  {
203  LOG_DEBUG("StateMachine consumeMessage trying substatemachine proxied transition");
204  // Retry with substatemachineproxied transition
205  State& currentState = getCurrentStateModifiably();
206  const Transition& transition = currentState.getSubstateMachineProxiedTransition(msg, mMessageArchive, mRoleMapping);
207  updateRoleMapping(msg, transition);
208  mMessageArchive.addMessage(msg);
209 
210  // Perform transition
211  mCurrentStateId = transition.getTargetStateId();
212  }
213 }
214 
216 {
217  const State& currentState = getCurrentState();
218  // Use isFinished instead of isFinal
219  return currentState.isFinished();
220 }
221 
223 {
224  return mCurrentStateId == State::GENERAL_FAILURE_STATE || mCurrentStateId == State::CONVERSATION_CANCEL_FAILURE;
225 }
226 
228 {
229  return mCurrentStateId == State::CONVERSATION_CANCEL_SUCCESS;
230 }
231 
232 std::string StateMachine::toString() const
233 {
234  std::stringstream statemachine;
235  statemachine << "Statemachine: (" << mProtocol << ")\n";
236  std::map<StateId, State>::const_iterator it = mStates.begin();
237  for(; it != mStates.end(); ++it)
238  {
239  statemachine << it->second.toString();
240  }
241  return statemachine.str();
242 }
243 
244 // EmbeddedStateMachine
246 {
247  std::stringstream str;
248  str << "embedded state machine: protocol: '" << name
249  << "', from: '" << from
250  << "', proxied_to: '" << proxiedTo << "'\n";
251 
252  return str.str();
253 }
254 
255 std::string StateMachine::toXML() const
256 {
257  std::stringstream ss;
258  ss << "<scxml version=\"1.0\" initial=\"1\">" << std::endl;
259  std::map<StateId, State>::const_iterator it = mStates.begin();
260  for(; it != mStates.end(); ++it)
261  {
262  ss << it->second.toXML();
263  }
264  ss << "</scxml>" << std::endl;
265 
266  return ss.str();
267 }
268 
269 std::string EmbeddedStateMachine::toXML() const
270 {
271  std::stringstream str;
272  str << "<subprotocol name=\"" << name << "\" "
273  << "from=\"" << from << "\" "
274  << "proxied_to=\"" << proxiedTo << "\"";
275 
276  return str.str();
277 }
278 
279 } // end of acl
280 } // end of fipa
void addMessage(const ACLMessage &msg)
void consumeMessage(const ACLMessage &msg)
describes the structure and operation of a transition of the state machine
bool inFailureState() const
std::string toXML() const
implements a state of the StateMachine class
Definition: state.h:41
bool isFinished() const
method that returns whether the state is a finished state or not. In the case of a state with embedde...
Definition: state.cpp:314
void setSelf(const AgentID &id)
Definition: role.cpp:42
static const StateId GENERAL_FAILURE_STATE
Definition: state.h:108
describes the structure and operation of a state machine
static const StateId CONVERSATION_CANCEL_FAILURE
Definition: state.h:107
bool inFinalState() const
const Transition & getTransition(const ACLMessage &msg, const MessageArchive &archive, const RoleMapping &roleMapping) const
Check whether the received message triggers a transition.
Definition: state.cpp:253
void consumeSubStateMachineMessage(const ACLMessage &msg, const fipa::acl::StateMachine &stateMachine, const fipa::acl::RoleMapping &roleMapping, int numberOfSubConversations)
Definition: state.cpp:147
std::string getId() const
getter method for the uid field of the class
Definition: state.h:181
Role getReceiverRole() const
Definition: transition.h:121
std::vector< AgentID > AgentIDList
Definition: agent_id.h:20
void setSelf(const AgentID &self)
Role getSenderRole() const
Definition: transition.h:116
const std::vector< Transition > & getTransitions() const
Definition: state.h:187
void updateRoleMapping(const ACLMessage &msg, const Transition &transition)
AgentID getSender() const
Definition: acl_message.h:311
void addRole(const Role &role)
Definition: role.cpp:78
State & getCurrentStateModifiably()
describes the structure and operation of a state, as part of a state machine
std::string toString() const
This class provides a representation of a message conforming to the FIPA specification SC00061...
Definition: acl_message.h:55
void addState(const State &state)
const State & getCurrentState() const
void consumeSubStateMachineMessage(const ACLMessage &msg, const fipa::acl::StateMachine &stateMachine, int numberOfSubConversations)
void addExpectedAgent(const Role &role, const AgentID &agent)
Definition: role.cpp:83
static const StateId CONVERSATION_CANCEL_SUCCESS
Definition: state.h:106
std::string toString() const
Implements the general AgentID functionality, which is present throughout the fipa specifications(FIP...
Definition: agent_id.h:37
StateId getTargetStateId() const
Definition: transition.h:138
AgentIDList getAllReceivers() const
Definition: acl_message.h:166
const Transition & getSubstateMachineProxiedTransition(const ACLMessage &msg, const MessageArchive &archive, const RoleMapping &roleMapping)
Check whether the received message triggers a substatemachine proxied transition. This method is not ...
Definition: state.cpp:280