iol
main.cpp
1 /*
2  * Copyright (C) 2011 Department of Robotics Brain and Cognitive Sciences - Istituto Italiano di Tecnologia
3  * Author: Ugo Pattacini
4  * email: ugo.pattacini@iit.it
5  * Permission is granted to copy, distribute, and/or modify this program
6  * under the terms of the GNU General Public License, version 2 or any
7  * later version published by the Free Software Foundation.
8  *
9  * A copy of the license can be found at
10  * http://www.robotcub.org/icub/license/gpl.txt
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details
16 */
17 
83 #include <string>
84 #include <deque>
85 #include <mutex>
86 #include <condition_variable>
87 
88 #include <yarp/os/all.h>
89 
90 using namespace std;
91 using namespace yarp::os;
92 
93 
94 /************************************************************************/
95 class FakeClassifierService: public PortReader
96 {
97  /************************************************************************/
98  bool read(ConnectionReader& connection)
99  {
100  Bottle cmd,reply;
101  cmd.read(connection);
102  reply.addVocab32("ack");
103 
104  if (cmd.size()>=2)
105  {
106  if (cmd.get(0).asVocab32()==Vocab32::encode("classify"))
107  {
108  Bottle *payLoad=cmd.get(1).asList();
109  int maxArea=0;
110 
111  for (int i=0; i<payLoad->size(); i++)
112  {
113  Bottle *item=payLoad->get(i).asList();
114  string tag=item->get(0).asString();
115  Bottle *blob=item->get(1).asList();
116  int tl_x=(int)blob->get(0).asFloat64();
117  int tl_y=(int)blob->get(1).asFloat64();
118  int br_x=(int)blob->get(2).asFloat64();
119  int br_y=(int)blob->get(3).asFloat64();
120 
121  int area=(br_x-tl_x)*(br_y-tl_y);
122  if (area>=maxArea)
123  {
124  reply.clear();
125  Bottle &l1=reply.addList();
126  l1.addString(tag);
127  Bottle &l2=l1.addList().addList();
128  l2.addString("toy");
129  l2.addFloat64(1.0);
130  maxArea=area;
131  }
132  }
133  }
134  }
135 
136  if (ConnectionWriter *client=connection.getWriter())
137  reply.write(*client);
138 
139  return true;
140  }
141 };
142 
143 
144 /************************************************************************/
145 class iolHelperModule: public RFModule
146 {
147  RpcClient opcPort;
148  Port rpcPort;
149  Port fakePort;
150  Port extClassOutPort;
151  BufferedPort<Bottle> extClassInPort;
152 
153  FakeClassifierService fakeService;
154 
155  Bottle blobTags;
156  Bottle reply;
157  mutex mtx_replyEvent;
158  condition_variable cv_replyEvent;
159  bool interrupting;
160 
161  deque<pair<int,string> > objects;
162 
163 public:
164  /************************************************************************/
165  bool configure(ResourceFinder &rf)
166  {
167  string name=rf.find("name").asString();
168  opcPort.open("/"+name+"/opc");
169 
170  rpcPort.open("/"+name+"/rpc");
171  attach(rpcPort);
172 
173  fakePort.open("/"+name+"/fake");
174  fakePort.setReader(fakeService);
175 
176  extClassOutPort.open("/"+name+"/extclass:o");
177  extClassInPort.open("/"+name+"/extclass:i");
178 
179  string context_extclass=rf.find("context_extclass").asString();
180  string memory_extclass=rf.find("memory_extclass").asString();
181 
182  ResourceFinder memory_rf;
183  memory_rf.setDefaultContext(context_extclass);
184  memory_rf.setDefaultConfigFile(memory_extclass.c_str());
185  memory_rf.configure(0,NULL);
186 
187  string dataFile=memory_rf.findFile("from");
188  Property dataProp; dataProp.fromConfigFile(dataFile);
189  Bottle dataBottle; dataBottle.read(dataProp);
190  for (int i=0; i<dataBottle.size(); i++)
191  {
192  if (Bottle *payLoad=dataBottle.get(i).asList()->get(2).asList())
193  {
194  if (payLoad->check("extclass_id") && payLoad->check("name"))
195  {
196  int id=payLoad->find("extclass_id").asInt32();
197  string name=payLoad->find("name").asString();
198  objects.push_back(pair<int,string>(id,name));
199  }
200  }
201  }
202 
203  yInfo("Available objects:");
204  for (size_t i=0; i<objects.size(); i++)
205  yInfo("#%d: %s",objects[i].first,objects[i].second.c_str());
206 
207  interrupting=false;
208  return true;
209  }
210 
211  /************************************************************************/
212  bool interruptModule()
213  {
214  yInfo("interrupting...");
215  interrupting=true;
216  cv_replyEvent.notify_all();
217  return true;
218  }
219 
220  /************************************************************************/
221  bool close()
222  {
223  opcPort.close();
224  rpcPort.close();
225  fakePort.close();
226  extClassOutPort.close();
227  extClassInPort.close();
228  return true;
229  }
230 
231  /************************************************************************/
232  bool getNames(Bottle &names)
233  {
234  names.clear();
235  if (opcPort.getOutputCount()>0)
236  {
237  Bottle opcCmd,opcReply,opcReplyProp;
238  opcCmd.addVocab32("ask");
239  Bottle &content=opcCmd.addList().addList();
240  content.addString("entity");
241  content.addString("==");
242  content.addString("object");
243  opcPort.write(opcCmd,opcReply);
244 
245  if (opcReply.size()>1)
246  {
247  if (opcReply.get(0).asVocab32()==Vocab32::encode("ack"))
248  {
249  if (Bottle *idField=opcReply.get(1).asList())
250  {
251  if (Bottle *idValues=idField->get(1).asList())
252  {
253  // cycle over items
254  for (int i=0; i<idValues->size(); i++)
255  {
256  int id=idValues->get(i).asInt32();
257 
258  // get the relevant properties
259  // [get] (("id" <num>) ("propSet" ("name")))
260  opcCmd.clear();
261  opcCmd.addVocab32("get");
262  Bottle &content=opcCmd.addList();
263  Bottle &list_bid=content.addList();
264  list_bid.addString("id");
265  list_bid.addInt32(id);
266  Bottle &list_propSet=content.addList();
267  list_propSet.addString("propSet");
268  list_propSet.addList().addString("name");
269  opcPort.write(opcCmd,opcReplyProp);
270 
271  // append the name (if any)
272  if (opcReplyProp.get(0).asVocab32()==Vocab32::encode("ack"))
273  if (Bottle *propField=opcReplyProp.get(1).asList())
274  if (propField->check("name"))
275  names.addString(propField->find("name").asString());
276  }
277  }
278  }
279  }
280  }
281 
282  return true;
283  }
284  else
285  return false;
286  }
287 
288  /************************************************************************/
289  int getLocation(const Bottle &body, Bottle &location)
290  {
291  int id=-1;
292  location.clear();
293  if ((opcPort.getOutputCount()>0) && (body.size()>0))
294  {
295  Bottle opcCmd,opcReply,opcReplyProp;
296  opcCmd.addVocab32("ask");
297  Bottle &content=opcCmd.addList();
298  Bottle &cond1=content.addList();
299  cond1.addString("entity");
300  cond1.addString("==");
301  cond1.addString("navloc");
302  content.addString("&&");
303  Bottle &cond2=content.addList();
304  cond2.addString("name");
305  cond2.addString("==");
306  cond2.addString(body.get(0).asString());
307  opcPort.write(opcCmd,opcReply);
308 
309  if (opcReply.size()>1)
310  {
311  if (opcReply.get(0).asVocab32()==Vocab32::encode("ack"))
312  {
313  if (Bottle *idField=opcReply.get(1).asList())
314  {
315  if (Bottle *idValues=idField->get(1).asList())
316  {
317  if (idValues->size()>0)
318  {
319  // consider just the first element
320  id=idValues->get(0).asInt32();
321 
322  // get the relevant properties
323  // [get] (("id" <num>) ("propSet" ("location")))
324  opcCmd.clear();
325  opcCmd.addVocab32("get");
326  Bottle &content=opcCmd.addList();
327  Bottle &list_bid=content.addList();
328  list_bid.addString("id");
329  list_bid.addInt32(id);
330  Bottle &list_propSet=content.addList();
331  list_propSet.addString("propSet");
332  list_propSet.addList().addString("location");
333  opcPort.write(opcCmd,opcReplyProp);
334 
335  // append the name (if any)
336  if (opcReplyProp.get(0).asVocab32()==Vocab32::encode("ack"))
337  if (Bottle *propField=opcReplyProp.get(1).asList())
338  if (propField->check("location"))
339  if (Bottle *loc=propField->find("location").asList())
340  for (int i=0; i<loc->size(); i++)
341  location.addFloat64(loc->get(i).asFloat64());
342  }
343  }
344  }
345  }
346  }
347  }
348 
349  return id;
350  }
351 
352  /************************************************************************/
353  bool setLocation(const int id, const Bottle &body)
354  {
355  if ((opcPort.getOutputCount()>0) && (body.size()>3))
356  {
357  Bottle opcCmd,opcReply;
358  Bottle *pContent=NULL;
359  if (id<0)
360  {
361  opcCmd.addVocab32("add");
362  Bottle &content=opcCmd.addList();
363  Bottle &list_ent=content.addList();
364  list_ent.addString("entity");
365  list_ent.addString("navloc");
366  Bottle &list_name=content.addList();
367  list_name.addString("name");
368  list_name.addString(body.get(0).asString());
369  pContent=&content;
370  }
371  else
372  {
373  opcCmd.addVocab32("set");
374  Bottle &content=opcCmd.addList();
375  Bottle &list_id=content.addList();
376  list_id.addString("id");
377  list_id.addInt32(id);
378  pContent=&content;
379  }
380 
381  Bottle &list_loc=pContent->addList();
382  list_loc.addString("location");
383  Bottle &list_data=list_loc.addList();
384  list_data.addFloat64(body.get(1).asFloat64());
385  list_data.addFloat64(body.get(2).asFloat64());
386  list_data.addFloat64(body.get(3).asFloat64());
387  opcPort.write(opcCmd,opcReply);
388 
389  return (opcReply.get(0).asVocab32()==Vocab32::encode("ack"));
390  }
391  else
392  return false;
393  }
394 
395  /************************************************************************/
396  Bottle mergeList(const Bottle &b1, const Bottle &b2)
397  {
398  Bottle ret=b1;
399  for (int i=0; i<b2.size(); i++)
400  {
401  string name=b2.get(i).asString();
402  bool toBeAppended=true;
403 
404  for (int j=0; j<ret.size(); j++)
405  {
406  if (name==ret.get(j).asString())
407  {
408  toBeAppended=false;
409  break;
410  }
411  }
412 
413  if (toBeAppended)
414  ret.addString(name);
415  }
416 
417  return ret;
418  }
419 
420  /************************************************************************/
421  bool respond(const Bottle &cmd, Bottle &reply)
422  {
423  yInfo("Received request: %s",cmd.toString().c_str());
424  switch (cmd.get(0).asVocab32())
425  {
426  //-----------------
427  case createVocab32('n','a','m','e'):
428  {
429  Bottle names;
430  if (getNames(names))
431  {
432  reply.addString("ack");
433  reply.append(mergeList(cmd.tail(),names));
434  }
435  else
436  reply.addString("nack");
437 
438  return true;
439  }
440 
441  //-----------------
442  case createVocab32('n','a','v','g'):
443  {
444  Bottle location;
445  if (getLocation(cmd.tail(),location)>=0)
446  {
447  reply.addString("ack");
448  reply.append(location);
449  }
450  else
451  reply.addString("nack");
452 
453  return true;
454  }
455 
456  //-----------------
457  case createVocab32('n','a','v','s'):
458  {
459  Bottle location;
460  Bottle body=cmd.tail();
461  int id=getLocation(body,location);
462  if (setLocation(id,body))
463  reply.addString("ack");
464  else
465  reply.addString("nack");
466 
467  return true;
468  }
469 
470  //-----------------
471  case createVocab32('c','l','a','s'):
472  {
473  if (extClassOutPort.getOutputCount()==0)
474  {
475  yWarning("external classifier is not connected => request skipped!");
476  reply.addString("failed");
477  return true;
478  }
479 
480  blobTags.clear();
481  Bottle msg;
482  msg.addString("classify");
483 
484  Bottle *payLoad=cmd.get(1).asList();
485  for (int i=0; i<payLoad->size(); i++)
486  {
487  Bottle *item=payLoad->get(i).asList();
488  string tag=item->get(0).asString();
489  Bottle *blob=item->get(1).asList();
490  int tl_x=(int)blob->get(0).asFloat64();
491  int tl_y=(int)blob->get(1).asFloat64();
492  int br_x=(int)blob->get(2).asFloat64();
493  int br_y=(int)blob->get(3).asFloat64();
494 
495  blobTags.addString(tag);
496  msg.addInt32(tl_x);
497  msg.addInt32(tl_y);
498  msg.addInt32(br_x);
499  msg.addInt32(br_y);
500  }
501 
502  if (blobTags.size()>0)
503  {
504  yInfo("Forwarding request: %s",msg.toString().c_str());
505  yInfo("waiting reply...");
506  extClassOutPort.write(msg);
507  unique_lock<mutex> lck(mtx_replyEvent);
508  cv_replyEvent.wait(lck);
509  if (!interrupting)
510  {
511  yInfo("...sending reply");
512  reply=this->reply;
513  }
514  else
515  {
516  yWarning("reply skipped!");
517  reply.addString("failed");
518  }
519  }
520  else
521  {
522  yWarning("empty request!");
523  reply.addString("failed");
524  }
525 
526  return true;
527  }
528 
529  //-----------------
530  default:
531  return RFModule::respond(cmd,reply);
532  }
533 
534  reply.addString("nack");
535  return false;
536  }
537 
538  /************************************************************************/
539  bool updateModule()
540  {
541  if (Bottle *msg=extClassInPort.read(false))
542  {
543  yInfo("Received reply: %s",msg->toString().c_str());
544 
545  reply.clear();
546  for (int i=0; i<msg->size(); i++)
547  {
548  Bottle &blob=reply.addList();
549  blob.addString(blobTags.get(i).asString());
550  Bottle &items=blob.addList();
551  for (size_t j=0; j<objects.size(); j++)
552  {
553  Bottle &item=items.addList();
554  item.addString(objects[j].second);
555  item.addFloat64(objects[j].first==msg->get(i).asInt32()?1.0:0.0);
556  }
557  }
558 
559  yInfo("Reply to be transmitted: %s",reply.toString().c_str());
560  cv_replyEvent.notify_all();
561  }
562 
563  return true;
564  }
565 
566  /************************************************************************/
567  double getPeriod()
568  {
569  return 0.02;
570  }
571 };
572 
573 
574 /************************************************************************/
575 int main(int argc, char *argv[])
576 {
577  ResourceFinder rf;
578  rf.setDefault("name","iolHelper");
579  rf.setDefault("context_extclass","iolStateMachineHandler");
580  rf.setDefault("memory_extclass","memory_extclass.ini");
581  rf.configure(argc,argv);
582 
583  Network yarp;
584  if (!yarp.checkNetwork())
585  return 1;
586 
587  iolHelperModule module;
588  return module.runModule(rf);
589 }
590 
591