fipa_acl  1.4
state.cpp
Go to the documentation of this file.
1 #include "state.h"
2 #include "transition.h"
3 #include "role.h"
4 #include "message_archive.h"
5 #include "statemachine.h"
6 
7 #include <boost/assign/list_of.hpp>
8 #include <boost/regex.hpp>
9 #include <iostream>
10 #include <stdexcept>
11 #include <sstream>
12 #include <base-logging/Logging.hpp>
13 
14 namespace fipa {
15 namespace acl {
16 
17 const StateId State::NOT_UNDERSTOOD = "__internal_state:not_understood__";
18 const StateId State::CONVERSATION_CANCELLING = "__internal_state:conversation_cancelling__";
19 const StateId State::CONVERSATION_CANCEL_SUCCESS = "__internal_state:conversation_cancel_success__";
20 const StateId State::CONVERSATION_CANCEL_FAILURE = "__internal_state:conversation_cancel_failure__";
21 const StateId State::UNDEFINED_ID = "__undefined__";
22 const StateId State::GENERAL_FAILURE_STATE = "__internal_state:general_failure__";
23 
24 std::vector<StateId> State::msDefaultStates = boost::assign::list_of
25  (State::NOT_UNDERSTOOD)
27  (State::CONVERSATION_CANCEL_SUCCESS)
29  (State::UNDEFINED_ID)
31  ;
32 
34  : mId(State::UNDEFINED_ID)
35  , mIsFinal(false)
36 {
37 }
38 
39 State::State(const StateId& uid)
40  : mId(uid)
41  , mIsFinal(false)
42 {
43 }
44 
46 {
47  std::vector<Transition>::const_iterator it;
48  it = std::find(mTransitions.begin(), mTransitions.end(), t);
49  if( it == mTransitions.end())
50  {
51  Transition transition = t;
52  transition.setSourceState(mId);
53  mTransitions.push_back(transition);
54  return transition;
55  } else {
56  return *it;
57  }
58 }
59 
61 {
62  // Not adding any transition for final states without substatemachines
63  if(isFinal())
64  {
65  if(mEmbeddedStateMachines.empty())
66  {
67  return;
68  }
69  }
70  else if(mTransitions.empty())
71  {
72  throw std::runtime_error("Invalid state: non final state without transition defined");
73  }
74 
75  std::vector<Transition> transitions(mTransitions);
76 
77  std::vector<Transition>::const_iterator it = transitions.begin();
78  for (; it != transitions.end();++it)
79  {
80  // we don't generate a not-understood transition for not-understood message...
81  boost::regex peformativeRegex(it->getPerformativeRegExp());
82  if(regex_match(PerformativeTxt[ACLMessage::NOT_UNDERSTOOD], peformativeRegex))
83  {
84  continue;
85  } else {
86  // Add the not understood transition for every matching transition
87  default_transition::NotUnderstood transitionSender = default_transition::NotUnderstood(it->getSenderRole(), it->getReceiverRole(), mId);
88  addTransition(*dynamic_cast<Transition*>(&transitionSender));
89 
90  default_transition::NotUnderstood transitionReceiver = default_transition::NotUnderstood(it->getReceiverRole(), it->getSenderRole(), mId);
91  addTransition(*dynamic_cast<Transition*>(&transitionReceiver));
92  }
93 
94  if(regex_match(PerformativeTxt[ACLMessage::CANCEL], peformativeRegex))
95  {
96  continue;
97  } else {
98  // Add the cancel transition -- bidirectional regarding receiver/sender role
99  default_transition::ConversationCancelling transitionSender = default_transition::ConversationCancelling(it->getSenderRole(), it->getReceiverRole(), mId);
100  addTransition(*dynamic_cast<Transition*>(&transitionSender));
101 
102  default_transition::ConversationCancelling transitionReceiver = default_transition::ConversationCancelling(it->getReceiverRole(), it->getSenderRole(), mId);
103  addTransition(*dynamic_cast<Transition*>(&transitionReceiver));
104  }
105 
106  if(regex_match(PerformativeTxt[ACLMessage::FAILURE], peformativeRegex))
107  {
108  continue;
109  } else {
110  // Add default failure transition -- which only applies for self as receiver
112  addTransition(*dynamic_cast<Transition*>(&transitionReceiver));
113  }
114  }
115 
116  // Also add default states for each substatemachine that proxies
117  std::vector<EmbeddedStateMachine>::const_iterator it0 = mEmbeddedStateMachines.begin();
118  for (; it0 != mEmbeddedStateMachines.end();++it0)
119  {
120  if(!it0->proxiedTo.empty())
121  {
122  // Add the not understood transition for every matching transition
123  default_transition::NotUnderstood transitionSender0 = default_transition::NotUnderstood(it0->fromRole, it0->proxiedToRole, mId);
124  addTransition(*dynamic_cast<Transition*>(&transitionSender0));
125  default_transition::NotUnderstood transitionReceiver0 = default_transition::NotUnderstood(it0->proxiedToRole, it0->fromRole, mId);
126  addTransition(*dynamic_cast<Transition*>(&transitionReceiver0));
127 
128  // Add the cancel transition -- bidirectional regarding receiver/sender role
129  default_transition::ConversationCancelling transitionSender1 = default_transition::ConversationCancelling(it0->fromRole, it0->proxiedToRole, mId);
130  addTransition(*dynamic_cast<Transition*>(&transitionSender1));
131  default_transition::ConversationCancelling transitionReceiver1 = default_transition::ConversationCancelling(it0->proxiedToRole, it0->fromRole, mId);
132  addTransition(*dynamic_cast<Transition*>(&transitionReceiver1));
133 
134  // Add default failure transition -- which only applies for self as receiver
136  addTransition(*dynamic_cast<Transition*>(&transitionReceiver2));
137  }
138  }
139 }
140 
142 {
143  std::vector<StateId>::const_iterator cit = std::find(msDefaultStates.begin(), msDefaultStates.end(), mId);
144  return cit != msDefaultStates.end();
145 }
146 
147 void State::consumeSubStateMachineMessage(const ACLMessage& msg, const fipa::acl::StateMachine& stateMachine, const fipa::acl::RoleMapping& roleMapping, int numberOfSubConversations)
148 {
149  LOG_INFO("State consumeSubStateMachineMessage");
150 
151  // Loop through all not-ended sub state machines
152  for(std::vector<StateMachine>::iterator it0 = mSubStateMachines.begin(); it0 != mSubStateMachines.end(); it0++)
153  {
154  if(it0->inFinalState() || it0->inFailureState())
155  {
156  // StateMachine ended already
157  continue;
158  }
159  // Test if this StateMachine is correct by maintaining a copy of it
160  StateMachine copy = *it0;
161 
162  // Test the update
163  try {
164  LOG_DEBUG("Trying an existing sub state machine");
165  it0->consumeMessage(msg);
166  // It worked
167  return;
168  } catch(const std::runtime_error& e)
169  {
170  LOG_DEBUG("Sub state machine incorrect: ", e.what());
171  // The state machine was obviously not correct, we play back the copy
172  *it0 = copy;
173  }
174  }
175 
176  LOG_DEBUG("Trying to search for a fitting a embedded state machine");
177 
178  EmbeddedStateMachine* embeddedStateMachinePtr = NULL;
179  // We must be in a state that allows subProtocols
180  std::string protocol = msg.getProtocol();
181 
182  // Search for an embedded state machine with the same protocol
183  std::vector<EmbeddedStateMachine>::iterator it;
184  for(it = mEmbeddedStateMachines.begin(); it != mEmbeddedStateMachines.end(); it++)
185  {
186  // Protocol must match Regex
187  boost::regex peformativeRegex(it->name);
188  if(regex_match(protocol, peformativeRegex))
189  {
190  // Check that the sender role is correct
191  // This throws if the mapping does not exist
192  const AgentIDList l = roleMapping.getMapping().at(it->fromRole);
193  AgentIDList::const_iterator lit = std::find(l.begin(), l.end(), msg.getSender());
194  if(lit == l.end())
195  {
196  // Sender does not match
197  continue;
198  }
199 
200  // Check that the number of subconversations allows another one
201  if(mSubStateMachines.size() >= numberOfSubConversations)
202  {
203  continue;
204  }
205 
206  // This is the right ESM
207  embeddedStateMachinePtr = &(*it);
208  break;
209  }
210  }
211 
212  if(!embeddedStateMachinePtr)
213  {
214  // No fitting running or new embedded state machine found
215  LOG_ERROR("State consumeSubStateMachineMessage: No fitting sub state machine found");
216  throw std::runtime_error("State consumeSubStateMachineMessage: No fitting sub state machine found");
217  }
218 
219  LOG_DEBUG("Found a fitting embedded state machine and will try to create a sub state machine now");
220 
221  // Construct a new state machine with mapped sender role
222  if(!protocol.empty())
223  {
224  // Copy the given state machine
225  StateMachine subStateMachine = stateMachine;
226  subStateMachine.setSelf(msg.getSender());
227 
228  // update the message state machine
229  try {
230  LOG_DEBUG("Substate machine initialized, trying to consume message");
231  subStateMachine.consumeMessage(msg);
232  } catch(const std::runtime_error& e)
233  {
234  // Also constructing and using a new one did not work for that message.
235  std::string errorMsg = "Substatemachine creation failed: ";
236  errorMsg += " -- " + std::string(e.what());
237  throw std::runtime_error(errorMsg);
238  }
239 
240  // If that was successful, save the actual protocol and number of subconversations in the embedded state machine
241  LOG_DEBUG("New sub state machine consumed message");
242  mSubStateMachines.push_back(subStateMachine);
243  embeddedStateMachinePtr->actualProtocol = protocol;
244  embeddedStateMachinePtr->numberOfSubConversations = numberOfSubConversations;
245  } else {
246  LOG_ERROR("Protocol not set");
247  throw std::runtime_error("Protocol not set");
248  }
249 
250  LOG_DEBUG("State consumeSubStateMachineMessage successfully created new sub state machine");
251 }
252 
253 const Transition& State::getTransition(const ACLMessage &msg, const MessageArchive& archive, const RoleMapping& roleMapping) const
254 {
255  std::vector<Transition>::const_iterator it = mTransitions.begin();
256  for (; it != mTransitions.end(); ++it)
257  {
258  // TODO: better use the directly corresponding one
259  // but this should be ok for now
260  if(!archive.hasMessages())
261  {
262  // Initiating message, i.e. validation should only apply to performative
263  boost::regex peformativeRegex(it->getPerformativeRegExp());
264  if(regex_match(msg.getPerformative(), peformativeRegex))
265  {
266  return *it;
267  }
268  } else {
269  const ACLMessage& initiatingMsg = archive.getInitiatingMessage();
270  if (it->triggers(msg, initiatingMsg, roleMapping))
271  {
272  return *it;
273  }
274  }
275  }
276 
277  throw std::runtime_error("Message does not trigger any transition in this state");
278 }
279 
280 const Transition& State::getSubstateMachineProxiedTransition(const ACLMessage& msg, const MessageArchive& archive, const RoleMapping& roleMapping)
281 {
282  if(archive.hasMessages())
283  {
284  const ACLMessage& initiatingMsg = archive.getInitiatingMessage();
285  // If state has substatemachine(s) && substatemachine(s) proxied_to not empty && actual_protocol != inform && response not already received:
286  // Genereate Transition on-the-fly, if posssible
287  std::vector<EmbeddedStateMachine>::iterator it0 = mEmbeddedStateMachines.begin();
288  for (; it0 != mEmbeddedStateMachines.end(); ++it0)
289  {
290  LOG_DEBUG("Checking if a transition needs to be generated");
291  // FIXME there can be other protocols that do not expect any responses
292  if(!it0->proxiedTo.empty() && it0->actualProtocol != "inform" && !it0->receivedProxiedReply )
293  {
294  LOG_DEBUG("Generating a transition");
295  // Generate a transition (any performative, not leaving the state)
296  Transition transition (it0->fromRole, it0->proxiedToRole, ".*", getId(), getId());
297  // And see if it triggers
298  if (transition.triggers(msg, initiatingMsg, roleMapping))
299  {
300  // Save that a proxied reply was received
301  it0->receivedProxiedReply = true;
302  mSubstateMachineProxiedTransitions.push_back(transition);
303  LOG_DEBUG("Transition triggered");
304  // We cannot use the local var to return as a reference
305  return mSubstateMachineProxiedTransitions.back();
306  }
307  }
308  }
309  }
310 
311  throw std::runtime_error("Message does not trigger any (incl. proxied) transitions in this state");
312 }
313 
314 bool State::isFinished() const
315 {
316  if(!isFinal())
317  {
318  return false;
319  }
320 
321  // Check all subprotocols
322  std::vector<StateMachine>::const_iterator it;
323  for(it = mSubStateMachines.begin(); it != mSubStateMachines.end(); it++)
324  {
325  if(!it->inFinalState() && !it->inFailureState())
326  {
327  LOG_DEBUG("State not finished (subconversation still running)");
328  return false;
329  }
330  }
331 
332  // When there are embedded state machines, they all must have forwarded a proxied reply, if this was
333  // necessary in the first place
334  std::vector<EmbeddedStateMachine>::const_iterator it0 = mEmbeddedStateMachines.begin();
335  for (; it0 != mEmbeddedStateMachines.end(); ++it0)
336  {
337  // They must also all have started enough sub state machines
338  // Check that enough subprotocols have been started
339  if(it0->numberOfSubConversations != mSubStateMachines.size())
340  {
341  LOG_DEBUG("State not finished (subconversation still running)");
342  return false;
343  }
344 
345  // FIXME there can be other protocols that do not expect any responses
346  if(!it0->proxiedTo.empty() && it0->actualProtocol != "inform" && !it0->receivedProxiedReply )
347  {
348  LOG_DEBUG("State not finished (proxied response missing)");
349  return false;
350  }
351  }
352 
353  return true;
354 }
355 
356 
358 {
359  mEmbeddedStateMachines.push_back(embeddedStateMachine);
360 }
361 
362 std::string State::toString() const
363 {
364  std::stringstream state;
365  state << "state id: '" << mId << "', final: '" << mIsFinal << "'\n";
366  for(std::vector<Transition>::const_iterator it = mTransitions.begin(); it != mTransitions.end(); ++it)
367  {
368  state << "\t" << it->toString() << "\n";
369  }
370 
371  for(std::vector<EmbeddedStateMachine>::const_iterator it = mEmbeddedStateMachines.begin(); it != mEmbeddedStateMachines.end(); ++it)
372  {
373  state << "\t" << it->toString() << "\n";
374  }
375 
376  return state.str();
377 }
378 
379 std::string State::toXML() const
380 {
381  std::stringstream ss;;
382  ss << "<state id=\"" << mId << "\"";
383  if(mIsFinal)
384  {
385  ss << " final=\"yes\" /";
386  }
387  ss << ">" << std::endl;
388 
389  for(std::vector<Transition>::const_iterator it = mTransitions.begin(); it != mTransitions.end(); ++it)
390  {
391  ss << "\t" << it->toXML() << "\n";
392  }
393 
394  for(std::vector<EmbeddedStateMachine>::const_iterator it = mEmbeddedStateMachines.begin(); it != mEmbeddedStateMachines.end(); ++it)
395  {
396  ss << "\t" << it->toXML() << "\n";
397  }
398 
399  return ss.str();
400 }
401 
403  : State(id)
404 {
405  setFinal(true);
406 }
407 
410 {}
411 
412 namespace default_state {
413 
414 ConversationCancelling::ConversationCancelling()
416 {
417  // Add the cancel transitions -> success or failure
419  addTransition(*dynamic_cast<Transition*>(&transitionSuccess));
420 
422  addTransition(*dynamic_cast<Transition*>(&transitionFailure));
423 }
424 
425 }
426 
427 } // end of acl
428 } // end of fipa
void consumeMessage(const ACLMessage &msg)
describes the structure and operation of a transition of the state machine
void addEmbeddedStateMachine(fipa::acl::EmbeddedStateMachine embeddedStateMachine)
Definition: state.cpp:357
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
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
static const StateId CONVERSATION_CANCELLING
Definition: state.h:105
FinalState(const StateId &id)
Definition: state.cpp:402
void setSourceState(const StateId &stateId)
Definition: transition.h:96
const ACLMessage & getInitiatingMessage() 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
std::string StateId
Definition: state.h:35
State()
empty constructor initializes default fields
Definition: state.cpp:33
std::string getPerformative() const
Definition: acl_message.h:139
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
void generateDefaultTransitions()
method that generates implicit generic transitions applicable to all states, that may or may not be s...
Definition: state.cpp:60
std::vector< AgentID > AgentIDList
Definition: agent_id.h:20
bool triggers(const ACLMessage &msg, const ACLMessage &initiatingMsg, const RoleMapping &roleMapping) const
check whether a certain message trigger the execution of the transition
Definition: transition.cpp:42
void setSelf(const AgentID &self)
void setFinal(bool final)
setter method for the final field of the class When changing the final state flag make sure you eithe...
Definition: state.h:97
Mapping between roles and actual agent ids.
Definition: role.h:98
An embedded state machine with mappings.
Definition: statemachine.h:200
std::string toString() const
Definition: state.cpp:362
std::map< ACLMessage::Performative, std::string > PerformativeTxt
Definition: acl_message.cpp:15
AgentID getSender() const
Definition: acl_message.h:311
std::string getProtocol() const
Definition: acl_message.h:238
describes the structure and operation of a state, as part of a state machine
This class provides a representation of a message conforming to the FIPA specification SC00061...
Definition: acl_message.h:55
bool isFinal() const
Definition: state.h:156
std::string toXML() const
Definition: state.cpp:379
static const StateId CONVERSATION_CANCEL_SUCCESS
Definition: state.h:106
static const StateId UNDEFINED_ID
Definition: state.h:103
static const StateId NOT_UNDERSTOOD
Definition: state.h:104
Foundation of Physical Intelligent Agents.
Definition: conversation.cpp:6
const std::map< Role, AgentIDList > & getMapping() const
Definition: role.h:114
Transition addTransition(const Transition &t)
Definition: state.cpp:45
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
bool isDefaultState() const
Test if state belongs to the default state or not.
Definition: state.cpp:141