speech
All Data Structures Functions Modules Pages
main.cpp
1 /*
2  * Copyright (C) 2018 iCub Facility - Istituto Italiano di Tecnologia
3  * Author: Ilaria Carlini Laura Cavaliere Vadim Tikhanoff
4  * email: ilaria.carlini@iit.it laura.cavaliere@iit.it vadim.tikhanoff@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 
18 #include <vector>
19 #include <iostream>
20 #include <deque>
21 #include <cstdio>
22 #include <cmath>
23 
24 #include <fstream>
25 #include <iterator>
26 #include <string>
27 #include <map>
28 
29 #include <yarp/os/BufferedPort.h>
30 #include <yarp/os/ResourceFinder.h>
31 #include <yarp/os/RFModule.h>
32 #include <yarp/os/Network.h>
33 #include <yarp/os/Time.h>
34 #include <yarp/os/Log.h>
35 #include <yarp/os/LogStream.h>
36 #include <yarp/os/Semaphore.h>
37 #include <yarp/sig/SoundFile.h>
38 #include <yarp/dev/PolyDriver.h>
39 
40 #include <grpc++/grpc++.h>
41 #include "google/cloud/language/v1/language_service.grpc.pb.h"
42 #include "google/cloud/texttospeech/v1/cloud_tts.grpc.pb.h"
43 #include "google/cloud/dialogflow/cx/v3beta1/agent.grpc.pb.h"
44 #include "google/cloud/dialogflow/cx/v3beta1/audio_config.grpc.pb.h"
45 //#include "google/cloud/dialogflow/cx/v3beta1/context.grpc.pb.h"
46 #include "google/cloud/dialogflow/cx/v3beta1/entity_type.grpc.pb.h"
47 #include "google/cloud/dialogflow/cx/v3beta1/intent.grpc.pb.h"
48 #include "google/cloud/dialogflow/cx/v3beta1/session_entity_type.grpc.pb.h"
49 #include "google/cloud/dialogflow/cx/v3beta1/session.grpc.pb.h"
50 #include "google/cloud/dialogflow/cx/v3beta1/webhook.grpc.pb.h"
51 
52 #include <chrono>
53 #include <ctime>
54 #include <algorithm>
55 
56 #include "googleDialog_IDL.h"
57 
58 using namespace google::cloud::language::v1;
59 using namespace google::cloud::texttospeech::v1;
60 using namespace google::cloud::dialogflow::cx::v3beta1;
61 bool is_changed;
62 
63 static const std::map<grpc::StatusCode, std::string> status_code_to_string {
64  {grpc::OK, "ok"},
65  {grpc::CANCELLED, "cancelled"},
66  {grpc::UNKNOWN, "unknown"},
67  {grpc::INVALID_ARGUMENT, "invalid_argument"},
68  {grpc::DEADLINE_EXCEEDED, "deadline_exceeded"},
69  {grpc::NOT_FOUND, "not_found"},
70  {grpc::ALREADY_EXISTS, "already_exists"},
71  {grpc::PERMISSION_DENIED, "permission_denied"},
72  {grpc::UNAUTHENTICATED, "unauthenticated"},
73  {grpc::RESOURCE_EXHAUSTED , "resource_exhausted"},
74  {grpc::FAILED_PRECONDITION, "failed_precondition"},
75  {grpc::ABORTED, "aborted"},
76  {grpc::OUT_OF_RANGE, "out_of_range"},
77  {grpc::UNIMPLEMENTED, "unimplemented"},
78  {grpc::INTERNAL, "internal"},
79  {grpc::UNAVAILABLE, "unavailable"},
80  {grpc::DATA_LOSS, "data_loss"},
81  {grpc::DO_NOT_USE, "do_not_use"}
82 };
83 /********************************************************/
84 class Processing : public yarp::os::BufferedPort<yarp::os::Bottle>
85 {
86  std::string moduleName;
87  std::string session_id;
88  std::string agent_name;
89  std::string language_code;
90  std::string &state;
91  bool &input_is_empty, reset;
92  std::int64_t &processing_time;
93  yarp::os::RpcServer handlerPort;
94  yarp::os::BufferedPort<yarp::os::Bottle> targetPort;
95 
96 public:
97  /********************************************************/
98 
99  Processing( const std::string &moduleName, const std::string agent_name, const std::string language_code, std::string &state, bool &input_is_empty, std::int64_t &processing_time) : state(state), input_is_empty(input_is_empty), processing_time(processing_time)
100  {
101  this->moduleName = moduleName;
102  this->session_id = getRandSession();
103  this->agent_name = agent_name;
104  this->language_code = language_code;
105  reset = false;
106  yInfo()<< "State: " << state;
107  yInfo()<< "input_is_empty: " << input_is_empty;
108  }
109 
110  /********************************************************/
111  ~Processing()
112  {
113 
114  };
115  /********************************************************/
116  std::string getRandSession() {
117  std::string session;
118  srand(time(NULL));
119  for(int i = 0; i<16; i++) {
120  session += std::to_string(rand() % 10);
121  }
122  return session;
123  }
124  /********************************************************/
125  bool open()
126  {
127  this->useCallback();
128  yarp::os::BufferedPort<yarp::os::Bottle >::open( "/" + moduleName + "/text:i" );
129  targetPort.open("/"+ moduleName + "/result:o");
130 
131  return true;
132  }
133 
134  /********************************************************/
135  void close()
136  {
137  yarp::os::BufferedPort<yarp::os::Bottle >::close();
138  targetPort.close();
139  }
140 
141  /********************************************************/
142  void onRead( yarp::os::Bottle &bot)
143  {
144  yarp::os::Bottle &outTargets = targetPort.prepare();
145 
146  outTargets.clear();
147  outTargets = queryGoogleDialog(bot);
148  yDebug() << "bottle" << outTargets.toString();
149  if(outTargets.size()>0){
150  targetPort.write();
151  }
152  yInfo() << "State: " << state;
153  yDebug() << "done querying google";
154  }
155 
156 /********************************************************/
157  yarp::os::Bottle queryGoogleDialog(yarp::os::Bottle& bottle)
158  {
159  std::string text = bottle.toString();
160  yarp::os::Bottle result;
161  google::cloud::dialogflow::cx::v3beta1::TextInput text_input;
162  google::cloud::dialogflow::cx::v3beta1::QueryInput query_input;
163 
164  //std::string language_code = "en-English";
165  text_input.set_text(text.c_str());
166  query_input.set_allocated_text(&text_input);
167  query_input.set_language_code(language_code);
168 
169  grpc::Status status;
170  grpc::ClientContext context;
171 
172  DetectIntentRequest request;
173  DetectIntentResponse response;
174 
175  request.set_session(agent_name+"/environments/draft/sessions/"+session_id);
176  request.set_allocated_query_input(&query_input);
177  std::string user_express = request.query_input().text().text();
178  yInfo() << "End-user expression:" << user_express;
179  if(request.query_input().text().text().size()>0){
180  input_is_empty=false;
181  auto creds = grpc::GoogleDefaultCredentials();
182  auto channel = grpc::CreateChannel("dialogflow.googleapis.com", creds);
183  std::unique_ptr<Sessions::Stub> dialog (Sessions::NewStub(channel));
184  checkState("Busy");
185  yarp::os::Time::delay(0.2);
186  const std::chrono::time_point<std::chrono::steady_clock> start = std::chrono::steady_clock::now();
187 
188  grpc::Status dialog_status = dialog->DetectIntent(&context, request, &response);
189  std::string status_string = status_code_to_string.at(dialog_status.error_code());
190 
191  result.clear();
192  if ( dialog_status.ok() ) {
193  const auto end = std::chrono::steady_clock::now();
194  processing_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
195  yDebug() << "Request processing time: " << processing_time << "µs";
196 
197  yInfo() << "Status returned OK";
198  yInfo() << "\n------Response------\n";
199  if (response.query_result().response_messages().size() > 0 && response.query_result().response_messages().Get(0).text().text().size() > 0)
200  {
201  result.addString(response.query_result().response_messages().Get(0).text().text().Get(0).c_str());
202  yDebug() << "result bottle" << result.toString();
203  checkState("Done");
204  }
205  else if (reset)
206  {
207  checkState("Reset");
208  reset=false;
209  }
210  else
211  {
212  checkState("Empty");
213  }
214  }
215  else if ( !dialog_status.ok() ) {
216  yError() << "Status Returned Cancelled";
217  checkState("Failure_" + status_string);
218  yInfo() << dialog_status.error_message();
219  }
220  }
221  else if (request.query_input().text().text().size()==0){
222  input_is_empty=true;
223  yError() << "Input is empty";
224  }
225  request.release_query_input();
226  query_input.release_text();
227  return result;
228  }
229 
230  /********************************************************/
231  bool setLanguageCode(const std::string &languageCode)
232  {
233  language_code = languageCode;
234  return true;
235  }
236 
237  /********************************************************/
238  std::string getLanguageCode()
239  {
240  return language_code;
241  }
242 
243  /********************************************************/
244  bool start_acquisition()
245  {
246  return true;
247  }
248 
249  /********************************************************/
250  bool stop_acquisition()
251  {
252  return true;
253  }
254 
255  /********************************************************/
256  bool checkState(std::string new_state)
257  {
258  if(new_state!=state){
259  is_changed=true;
260  state=new_state;
261  }
262  else{
263  is_changed=false;
264  }
265  return is_changed;
266  }
267 
268  /********************************************************/
269  void resetDialog(){
270  reset = true;
271  yarp::os::Bottle bot;
272  bot.clear();
273  bot.addString("Exit");
274  queryGoogleDialog(bot);
275  }
276 };
277 
278 /********************************************************/
279 class Module : public yarp::os::RFModule, public googleDialog_IDL
280 {
281  yarp::os::ResourceFinder *rf;
282  yarp::os::RpcServer rpcPort;
283  std::string state;
284  std::int64_t processing_time;
285  bool input_is_empty;
286  yarp::os::BufferedPort<yarp::os::Bottle> statePort;
287 
288  Processing *processing;
289  friend class processing;
290 
291  bool closing;
292  std::vector<std::string> allLanguageCodes;
293 
294  /********************************************************/
295  bool attach(yarp::os::RpcServer &source)
296  {
297  return this->yarp().attachAsServer(source);
298  }
299 
300 public:
301 
302  /********************************************************/
303  bool configure(yarp::os::ResourceFinder &rf)
304  {
305  this->rf=&rf;
306  this->state="Ready";
307  this->input_is_empty=false;
308  this->processing_time=0;
309  std::string moduleName = rf.check("name", yarp::os::Value("googleDialog"), "module name (string)").asString();
310  std::string agent_name = rf.check("agent", yarp::os::Value("1"), "name of the agent").asString();
311  std::string language_code = rf.check("language", yarp::os::Value("en-US"), "language of the dialogflow").asString();
312 
313  if (rf.check("languageCodes", "Getting language codes"))
314  {
315  yarp::os::Bottle &grp=rf.findGroup("languageCodes");
316  int sz=grp.size()-1;
317 
318  for (int i=0; i<sz; i++)
319  allLanguageCodes.push_back(grp.get(1+i).asString());
320  }
321 
322  yDebug() << "this is the project" << rf.check("project");
323  yDebug() << "Module name" << moduleName;
324  yDebug() << "agent name" << agent_name;
325  yDebug() << "language of the dialog" << language_code;
326  setName(moduleName.c_str());
327 
328  rpcPort.open(("/"+getName("/rpc")).c_str());
329  statePort.open("/"+ moduleName + "/state:o");
330 
331  closing = false;
332 
333  processing = new Processing( moduleName, agent_name, language_code, state, input_is_empty, processing_time);
334 
335  /* now start the thread to do the work */
336  processing->open();
337 
338  attach(rpcPort);
339 
340  return true;
341  }
342 
343  /********************************************************/
344  bool setLanguage(const std::string& languageCode)
345  {
346  bool returnVal = false;
347 
348  std::string language;
349 
350  for (int i = 0; i < allLanguageCodes.size(); i++)
351  {
352  if (languageCode == allLanguageCodes[i])
353  {
354  language = languageCode;
355  processing->setLanguageCode(languageCode);
356  returnVal = true;
357  break;
358  }
359  }
360 
361  return returnVal;
362  }
363 
364  /********************************************************/
365  std::string getLanguageCode()
366  {
367  return processing->getLanguageCode();
368  }
369 
370  /**********************************************************/
371  bool close()
372  {
373  statePort.close();
374  processing->close();
375  delete processing;
376  return true;
377  }
378 
379  /********************************************************/
380  double getPeriod()
381  {
382  return 0.1;
383  }
384 
385  /********************************************************/
386  bool quit()
387  {
388  closing=true;
389  return true;
390  }
391 
392  /********************************************************/
393  bool updateModule()
394  {
395  if(is_changed){
396  is_changed=false;
397  yarp::os::Bottle &outTargets = statePort.prepare();
398  outTargets.clear();
399  outTargets.addString(state);
400  yDebug() << "outTarget:" << outTargets.toString().c_str();
401  statePort.write();
402  }
403  return !closing;
404  }
405 
406  /********************************************************/
407  std::string getState()
408  {
409  return state;
410  }
411 
412  /********************************************************/
413  std::int64_t getProcessingTime()
414  {
415  return processing_time;
416  }
417 
418  /********************************************************/
419  bool resetDialog()
420  {
421  processing->resetDialog();
422  return true;
423  }
424 };
425 
426 /********************************************************/
427 int main(int argc, char *argv[])
428 {
429  yarp::os::Network::init();
430 
431  yarp::os::Network yarp;
432  if (!yarp.checkNetwork())
433  {
434  yError("YARP server not available!");
435  return 1;
436  }
437 
438  Module module;
439  yarp::os::ResourceFinder rf;
440 
441  rf.setVerbose( true );
442  rf.setDefaultContext( "googleDialog" );
443  rf.setDefaultConfigFile( "config.ini" );
444  rf.setDefault("name","googleDialog");
445  rf.configure(argc,argv);
446 
447  return module.runModule(rf);
448 }