fipa_acl  1.4
conversation.cpp
Go to the documentation of this file.
1 #include "conversation.h"
2 #include <uuid/uuid.h>
3 #include <base-logging/Logging.hpp>
4 #include <boost/regex.hpp>
5 
6 namespace fipa {
7 namespace acl {
8 
9 namespace conversation {
10 
12  : msg()
13  , type(UNKNOWN)
14  , timestamp()
15 {}
16 
18  : msg(_msg)
19  , type(_type)
20  , timestamp(base::Time::now())
21 {}
22 
23 } // end namespace conversation
24 
25 fipa::acl::StateMachineFactory Conversation::msStateMachineFactory;
26 
28 {}
29 
31 {
32  boost::unique_lock<boost::mutex> lock(mEventsMutex);
33  return !mEvents.empty();
34 }
35 
36 void ConversationObserver::update(const conversation::Event& event)
37 {
38  boost::unique_lock<boost::mutex> lock(mEventsMutex);
39  mEvents.push_back(event);
40  mCondition.notify_all();
41 }
42 
44 {
45  boost::mutex mutex;
46  {
47  boost::unique_lock<boost::mutex> lock(mutex);
48  while(!hasEvents())
49  {
50  mCondition.wait(lock);
51  }
52  }
53 
54  conversation::Event event;
55  assert(getNextEvent(event));
56 
57  return event;
58 }
59 
61 {
62  boost::unique_lock<boost::mutex> lock(mEventsMutex);
63  if(mEvents.empty())
64  {
65  return false;
66  }
67 
68  event = mEvents.front();
69  mEvents.erase(mEvents.begin());
70 
71  LOG_INFO("Retrieve event: type: '%s', msg content: '%s'", conversation::EventTypeTxt[event.type].c_str(), event.msg.getContent().c_str());
72  return true;
73 }
74 
75 std::vector<fipa::acl::ConversationID> ConversationObserver::getConversationIdsOfObservables() const
76 {
77  std::vector<fipa::acl::ConversationID> conversationIds;
78 
79  boost::unique_lock<boost::mutex> lock(mObservablesMutex);
80  std::vector<ConversationObservablePtr>::const_iterator it = mObservables.begin();
81  for(; it != mObservables.end(); ++it)
82  {
83  conversationIds.push_back((*it)->getConversationId());
84  }
85 
86  return conversationIds;
87 }
88 
89 void ConversationObserver::registerObservable(const ConversationObservablePtr& observable)
90 {
91  boost::unique_lock<boost::mutex> lock(mObservablesMutex);
92 
93  std::vector<ConversationObservablePtr>::const_iterator it = std::find(mObservables.begin(), mObservables.end(), observable);
94  if(it == mObservables.end())
95  {
96  mObservables.push_back(observable);
97  }
98 }
99 
100 Conversation::Conversation(const std::string& owner, const fipa::acl::ConversationID& conversationId)
101  : ConversationObservable(conversationId)
102  , mOwner(owner)
103  , mNumberOfSubConversations(0)
104 {
105  if(mConversationId.empty())
106  {
108  }
109  LOG_DEBUG("Conversation created with id: %s\n", conversationId.c_str());
110 }
111 
112 Conversation::Conversation(const std::string& owner, const fipa::acl::ACLMessage& initiator)
114  , mOwner(owner)
115  , mNumberOfSubConversations(0)
116 {
117  update(initiator);
118  LOG_DEBUG("Conversation created with id: %s\n", initiator.getConversationID().c_str());
119  assert(!mConversationId.empty());
120 }
121 
123  : ConversationObservable(other)
124  , mOwner(other.mOwner)
125  , mProtocol(other.mProtocol)
126  , mContentLanguage(other.mContentLanguage)
127  , mNumberOfSubConversations(other.mNumberOfSubConversations)
128  , mMessages(other.mMessages)
129  , mStateMachine(other.mStateMachine)
130 {
131 }
132 
134 {
135 }
136 
138 {
139  return mContentLanguage;
140 }
141 
143 {
144  boost::unique_lock<boost::mutex> lock(mMutex);
145 
146  LOG_INFO("Update conversation: id '%s'", msg.getConversationID().c_str());
147  LOG_INFO("Update message: performative: '%s' content: '%s'", msg.getPerformative().c_str(), msg.getContent().c_str());
148 
149 
150  bool newConversation = false;
151  try {
152  // If protocol is unset we don't have a valid message
153  // Thus at this point the conversation is not yet initialized
154  if(mProtocol.empty())
155  {
156  std::string protocol = msg.getProtocol();
157  try {
158  if(!protocol.empty())
159  {
160  mStateMachine = msStateMachineFactory.getStateMachine(protocol);
161  mStateMachine.setSelf( fipa::acl::AgentID(mOwner) );
162 
163  mProtocol = protocol;
164  mContentLanguage = msg.getLanguage();
166 
167  } else {
168  LOG_ERROR("Protocol not set");
169  throw std::runtime_error("Protocol not set");
170  }
171  } catch(const std::runtime_error& e)
172  {
173  LOG_FATAL("Conversation could not retrieve statemachine for protocol '%s'. Check if protocol specification was loaded -- '%s'", protocol.c_str(), e.what());
174  throw;
175  }
176 
177  newConversation = true;
178 
179  } else if( mProtocol != msg.getProtocol())
180  {
181  // This probably means, it's a subProtocol message
182  try {
183  mStateMachine.consumeSubStateMachineMessage(msg, msStateMachineFactory.getStateMachine(msg.getProtocol()), mNumberOfSubConversations);
184  } catch(const std::runtime_error& e)
185  {
186  std::string errorMsg = "Conversation: unexpected message with performative '" + msg.getPerformative() + "' for the protocol '" + msg.getProtocol() + "' ";
187  errorMsg += " current state: '" + mStateMachine.getCurrentStateId() + "',";
188  errorMsg += " role mapping -- " + mStateMachine.getRoleMapping().toString();
189  errorMsg += " -- " + std::string(e.what()) + "\n";
190  throw conversation::ProtocolException(errorMsg);
191  }
192 
193  notifyAll(msg, false);
194  return;
195  } else if( msg.getProtocol().empty())
196  {
197  LOG_WARN("Conversation: received message has no protocol being set. Current conversation using '%s'", mProtocol.c_str());
198  }
199 
200  if( msg.getLanguage().empty())
201  {
202  LOG_INFO("Conversation: received message has not language being set. Current conversation using '%s'", mContentLanguage.c_str());
203  } else if(mContentLanguage != msg.getLanguage())
204  {
205  LOG_INFO("Conversation: message with different content language being inserted: current '%s' - to be inserted '%s'", mContentLanguage.c_str(), msg.getLanguage().c_str());
206  }
207 
208  // update the message state machine
209  try {
210  mStateMachine.consumeMessage(msg);
211  } catch(const std::runtime_error& e)
212  {
213  std::string errorMsg = "Conversation: unexpected message with performative '" + msg.getPerformative() + "' for the protocol '" + msg.getProtocol() + "' ";
214  errorMsg += " current state: '" + mStateMachine.getCurrentStateId() + "',";
215  errorMsg += " role mapping -- " + mStateMachine.getRoleMapping().toString();
216  errorMsg += " -- " + std::string(e.what()) + "\n";
217  throw conversation::ProtocolException(errorMsg);
218  }
219 
220  notifyAll(msg, newConversation);
221 
222  } catch(...)
223  {
224  // Notify only to handle internally
225  throw;
226  }
227 }
228 
229 void Conversation::notifyAll(const fipa::acl::ACLMessage& msg, bool newConversation)
230 {
231  mMessages.push_back(msg);
232 
233  if(mStateMachine.inFailureState())
234  {
236  } else if(hasEnded()) {
238  } else if(newConversation) {
240  } else {
242  }
243 }
244 
246 {
247  boost::unique_lock<boost::mutex> lock(mMutex);
248  return mMessages.back();
249 }
250 
252 {
253  try
254  {
255  if(!mStateMachine.inFinalState())
256  {
257  LOG_DEBUG("Conversation did not end");
258  return false;
259 
260  }
261  LOG_DEBUG("Conversation ended");
262  return true;
263  }
264  catch(const std::runtime_error& e)
265  {
266  // This very probably means the state machine has not been initialized properly,
267  // as there was no message yet to know the protocol. Therefore, technically
268  // the conversation did not end!
269  LOG_WARN_S << "Runtime error when testing if conversation ended. Therefore not ended. Message: " << e.what();
270  return false;
271  }
272 }
273 
275 {
276  boost::unique_lock<boost::mutex> lock(mMutex);
277  return !mMessages.empty();
278 }
279 
280 std::string Conversation::getOwner() const
281 {
282  return mOwner;
283 }
284 
286 {
287  uuid_t uuid;
288  uuid_generate(uuid);
289 
290  char conversationId[37];
291  uuid_unparse(uuid, conversationId);
292  return std::string(conversationId) + "--" + topic + "--" + base::Time::now().toString();
293 }
294 
295 std::string Conversation::toString(const std::vector<fipa::acl::ACLMessage>& messages)
296 {
297  std::stringstream ss;
298  std::vector<fipa::acl::ACLMessage>::const_iterator cit = messages.begin();
299  for(; cit != messages.end(); ++cit)
300  {
301  ss << cit->toString() << std::endl;
302  }
303  return ss.str();
304 }
305 
306 std::string Conversation::toString() const
307 {
308  std::stringstream ss;
309  ss << "Conversation: " << std::endl;
310  if(!mMessages.empty())
311  {
312  ss << " id: " << mMessages.front().getConversationID();
313  }
314  ss << std::endl;
315  ss << " owner: " << mOwner << std::endl;
316  ss << " protocol: " << mProtocol << std::endl;
317  ss << " language: " << mContentLanguage << std::endl;
318  ss << " # subconversations: " << mNumberOfSubConversations << std::endl;
319  ss << " # messages " << mMessages.size() << std::endl;
320  ss << "BEGIN " << std::endl;
321  ss << toString(mMessages) << std::endl;
322  ss << "END" << std::endl;
323 
324  return ss.str();
325 }
326 
328  : mStatus(conversation::UNINITIALIZED)
329 {}
330 
333  , mStatus(other.mStatus)
334  , mObservers(other.mObservers)
335 {}
336 
338  : mConversationId(conversationId)
339  , mStatus(conversation::UNINITIALIZED)
340 {}
341 
343 {
344  return mConversationId;
345 }
346 
348 {
349  boost::unique_lock<boost::mutex> lock(mObserverMutex);
350  return !mObservers.empty();
351 }
352 
354 {
355  boost::unique_lock<boost::mutex> lock(mObserverMutex);
356  ConversationObserverList::const_iterator it = std::find(mObservers.begin(), mObservers.end(), observer);
357  if(it == mObservers.end())
358  {
359  mObservers.push_back(observer);
360  observer->registerObservable(ConversationObservablePtr(this, NullDeleter()));
361  }
362 }
363 
365 {
366  boost::unique_lock<boost::mutex> lock(mObserverMutex);
367  ConversationObserverList::iterator it = std::find(mObservers.begin(), mObservers.end(), observer);
368  if(it != mObservers.end())
369  {
370  mObservers.erase(it);
371  }
372 }
373 
375 {
376  boost::unique_lock<boost::mutex> lock(mObserverMutex);
377  LOG_INFO("notify: message event: '%s', message content: '%s'", conversation::EventTypeTxt[eventType].c_str(), msg.getContent().c_str());
378  ConversationObserverList::iterator it = mObservers.begin();
379  for(; it != mObservers.end(); ++it)
380  {
381  (*it)->update( conversation::Event(msg, eventType) );
382  }
383 
384  switch(eventType)
385  {
387  assert(mStatus == conversation::UNINITIALIZED);
388  mStatus = conversation::RUNNING;
389  break;
391  mStatus = conversation::ENDED;
392  break;
394  mStatus = conversation::RUNNING;
395  break;
397  mStatus = conversation::FAILED;
398  break;
399  default:
400  break;
401  }
402 
403 }
404 
406 {
407  return mStatus;
408 }
409 
411 {
412  mObservers.clear();
413 }
414 
416 {
417  boost::unique_lock<boost::mutex> lock(mObserverMutex);
418  return mObservers;
419 }
420 
421 } // end namespace acl
422 } // end namespace fipa
std::string ConversationID
Definition: acl_message.h:28
std::string getLanguage() const
Definition: acl_message.h:268
void consumeMessage(const ACLMessage &msg)
std::vector< fipa::acl::ConversationID > getConversationIdsOfObservables() const
boost::shared_ptr< ConversationObservable > ConversationObservablePtr
Definition: conversation.h:21
std::string getConversationID() const
Definition: acl_message.h:227
StateId getCurrentStateId() const
Definition: statemachine.h:129
static fipa::acl::ConversationID generateConversationID(const std::string &topic="")
fipa::acl::ConversationID getConversationId() const
static StateMachine getStateMachine(const std::string &protocol)
std::vector< ConversationObserverPtr > ConversationObserverList
Definition: conversation.h:22
boost::shared_ptr< ConversationObserver > ConversationObserverPtr
Definition: conversation.h:18
std::string getContent() const
Definition: acl_message.h:294
bool getNextEvent(conversation::Event &msg)
void notify(const fipa::acl::ACLMessage &msg, conversation::EventType eventType)
std::string getOwner() const
void setSelf(const AgentID &self)
conversation::Status getStatus() const
const RoleMapping & getRoleMapping() const
Definition: statemachine.h:109
std::string toString() const
std::string toString() const
Definition: role.cpp:165
std::string getPerformative() const
Definition: acl_message.h:139
void addObserver(const ConversationObserverPtr &observer)
This class provides a representation of a message conforming to the FIPA specification SC00061...
Definition: acl_message.h:55
Conversation(const std::string &owner, const fipa::acl::ConversationID &conversationId=fipa::acl::ConversationID())
fipa::acl::ConversationID mConversationId
Definition: conversation.h:231
conversation::Event waitForNextEvent()
void consumeSubStateMachineMessage(const ACLMessage &msg, const fipa::acl::StateMachine &stateMachine, int numberOfSubConversations)
void removeObserver(const ConversationObserverPtr &observer)
ConversationObserverList getObservers() const
fipa::acl::ContentLanguage getContentLanguage() const
void update(const fipa::acl::ACLMessage &msg)
fipa::acl::ACLMessage getLastMessage() const
std::string getProtocol() const
Definition: acl_message.h:238
Implements the general AgentID functionality, which is present throughout the fipa specifications(FIP...
Definition: agent_id.h:37
Foundation of Physical Intelligent Agents.
Definition: conversation.cpp:6
fipa::acl::ACLMessage msg
Definition: conversation.h:51