icub-client
allostaticController.cpp
Go to the documentation of this file.
1 #include "allostaticController.h"
2 
4 {
5  yDebug() << "Interrupt rpc port";
6  rpc_in_port.interrupt();
7 
8  yDebug() << "Interrupt port to homeo rpc";
9  to_homeo_rpc.interrupt();
10  to_behavior_rpc.interrupt();
11  for (auto& outputm_port : outputm_ports)
12  {
13  outputm_port->interrupt();
14  }
15 
16  for(auto& outputM_port : outputM_ports)
17  {
18  outputM_port->interrupt();
19  }
20 
21  yDebug() << "Interrupt AllostaticDrive ports";
22  for(auto& allostaticDrive : allostaticDrives) {
23  allostaticDrive.second.interrupt_ports();
24  }
25 
26  return true;
27 
28 }
29 
31 {
32  yDebug() << "Closing rpc port";
33  rpc_in_port.interrupt();
34  rpc_in_port.close();
35 
36  yDebug() << "Closing port to homeo rpc";
37  to_homeo_rpc.interrupt();
38  to_homeo_rpc.close();
39  to_behavior_rpc.interrupt();
40  to_behavior_rpc.close();
41 
42  for (auto& outputm_port : outputm_ports)
43  {
44  outputm_port->interrupt();
45  outputm_port->close();
46  delete outputm_port;
47  }
48 
49  for(auto& outputM_port : outputM_ports)
50  {
51  outputM_port->interrupt();
52  outputM_port->close();
53  delete outputM_port;
54  }
55 
56  yDebug() << "Closing AllostaticDrive ports";
57  for(auto& allostaticDrive : allostaticDrives) {
58  allostaticDrive.second.close_ports();
59  }
60 
61  return true;
62 }
63 
64 bool AllostaticController::openPorts(string driveName)
65 {
66  bool allGood = true;
67 
68  outputm_ports.push_back(new BufferedPort<Bottle>);
69  outputM_ports.push_back(new BufferedPort<Bottle>);
70 
71  string portName = "/" + moduleName + "/" + driveName;
72 
73  // first, min ports
74  string pn = portName + "/min:i";
75 
76  if (!outputm_ports.back()->open(pn))
77  {
78  yError() << getName() << ": Unable to open port " << pn;
79  allGood = false;
80  }
81  string targetPortName = "/" + homeo_name + "/" + driveName + "/min:o";
82  yarp::os::Time::delay(0.1);
83  while(!Network::connect(targetPortName,pn)) {
84  yInfo() <<"Setting up homeostatic connections... "<< targetPortName << " " << pn ;
85  yarp::os::Time::delay(0.5);
86  }
87 
88  // now, max ports
89  pn = portName + "/max:i";
90  yInfo() << "Configuring port " << pn << " ...";
91  yarp::os::Time::delay(0.1);
92  if (!outputM_ports.back()->open(pn))
93  {
94  yError() << getName() << ": Unable to open port " << pn;
95  allGood = false;
96  }
97  yarp::os::Time::delay(0.1);
98  targetPortName = "/" + homeo_name + "/" + driveName + "/max:o";
99  while(!Network::connect(targetPortName, pn))
100  {
101  yDebug()<<"Setting up homeostatic connections... "<< targetPortName << " " << pn;
102  yarp::os::Time::delay(0.5);
103  }
104 
105  yarp::os::Time::delay(0.1);
106  pn = "/" + moduleName + "/toBehaviorManager:o";
107  targetPortName = "/BehaviorManager/trigger:i";
108  to_behavior_rpc.open(pn);
109  while(!Network::connect(pn, targetPortName))
110  {
111  yDebug()<<"Setting up BehaviorManager connections... "<< pn << " " << targetPortName;
112  yarp::os::Time::delay(0.5);
113  }
114  return allGood;
115 }
116 
117 bool AllostaticController::configure(yarp::os::ResourceFinder &rf)
118 {
119 
120  moduleName = rf.check("name",Value("AllostaticController")).asString();
121  setName(moduleName.c_str());
122 
123  yDebug()<<moduleName<<": finding configuration files...";
124  period = rf.check("period",Value(0.5)).asDouble();
125 
126  configureAllostatic(rf);
127 
128  rpc_in_port.open("/" + moduleName + "/rpc");
129  attach(rpc_in_port);
130 
131  yInfo()<<"Configuration done.";
132 
133  return true;
134 }
135 
136 void AllostaticController::configureAllostatic(yarp::os::ResourceFinder &rf)
137 {
138  //The homeostatic module should be running in parallel, independent from this, so the objective of
139  //this config would be to have a proper list and connect to each port
140 
141  homeo_name = "homeostasis";
142  string homeo_rpc_name = "/" + homeo_name + "/rpc";
143  string to_h_rpc_name="/"+moduleName+"/toHomeoRPC:o";
144  to_homeo_rpc.open(to_h_rpc_name);
145 
146  while(!Network::connect(to_h_rpc_name,homeo_rpc_name))
147  {
148  yDebug()<<"Trying to connect to homeostasis...";
149  yDebug() << "from " << to_h_rpc_name << " to " << homeo_rpc_name;
150  yarp::os::Time::delay(0.2);
151  }
152 
153  yInfo() << "Initializing drives...";
154  Bottle grpAllostatic = rf.findGroup("ALLOSTATIC");
155 
156  drivesList = *grpAllostatic.find("drives").asList();
157  Bottle cmd;
158 
159  double priority;
160  for (unsigned int d = 0; d<drivesList.size(); d++)
161  {
162  cmd.clear();
163  string driveName = drivesList.get(d).asString();
164  yInfo() << ("Initializing drive " + driveName);
165 
166  cmd.addString("add");
167  cmd.addString("conf");
168  Bottle drv;
169  drv.clear();
170  Bottle aux;
171  aux.clear();
172  aux.addString("name");
173  aux.addString(driveName);
174  drv.addList()=aux;
175  aux.clear();
176  drv.addList()=grpAllostatic;
177  cmd.addList()=drv;
178  Bottle rply;
179  rply.clear();
180  rply.get(0).asString();
181  to_homeo_rpc.write(cmd,rply);
182 
183  AllostaticDrive alloDrive;
184  alloDrive.name = driveName;
185 
186  Value cmds = grpAllostatic.check((driveName + "-sensation-on"), Value("None"));
187  alloDrive.sensationOnCmd = *cmds.asList();
188  cmds = grpAllostatic.check((driveName + "-sensation-off"), Value("None"));
189  alloDrive.sensationOffCmd = *cmds.asList();
190 
191  cmds = grpAllostatic.check((driveName + "-before-trigger"), Value("None"));
192  if (!(cmds.isString() && cmds.asString() == "None")) {
193  alloDrive.beforeTriggerCmd = *cmds.asList();
194  }
195 
196  cmds = grpAllostatic.check((driveName + "-after-trigger"), Value("None"));
197  if (!(cmds.isString() && cmds.asString() == "None")) {
198  alloDrive.afterTriggerCmd = *cmds.asList();
199  }
200 
201  alloDrive.homeoPort = &to_homeo_rpc;
202 
203  alloDrive.inputSensationPort = new BufferedPort<Bottle>;
204  string portName = "/" + moduleName + "/" + driveName + "/sensation:i";
205  alloDrive.inputSensationPort->open(portName);
206 
207  openPorts(driveName);
208 
209  while(!Network::connect("/opcSensation/toHomeo:o", "/homeostasis/fromSensations:i")) {
210  yDebug()<<"Connecting " << "/opcSensation/toHomeo:o" << " to " << "/homeostasis/fromSensations:i";
211  yarp::os::Time::delay(0.5);
212  }
213 
214 
215  // set drive priorities. Default to 1.
216  priority = grpAllostatic.check((driveName + "-priority"), Value(1.)).asDouble();
217  drivePriorities.push_back(priority);
218 
219  //Under effects
220  string under_port_name = grpAllostatic.check((driveName + "-under-behavior-port"), Value("None")).asString();
221  string under_cmd_name = grpAllostatic.check((driveName + "-under-behavior"), Value("None")).asString();
222 
223  bool active = false;
224 
225  if (under_port_name != "None" && under_cmd_name != "None")
226  {
227  active = true;
228  string out_port_name = "/" + moduleName + "/" + driveName + "/under_action:o";
229  alloDrive.behaviorUnderPort = new Port();
230  alloDrive.behaviorUnderPort->open(out_port_name);
231  yDebug() << "trying to connect to" << under_port_name;
232  while(!Network::connect(out_port_name,under_port_name))
233  {
234 
235  yarp::os::Time::delay(0.5);
236  }
237  alloDrive.behaviorUnderCmd = Bottle(under_cmd_name);
238  }else{
239  yInfo() << "No port name for" << driveName << "under-behavior-port";
240  }
241 
242  //Over effects
243  string over_port_name = grpAllostatic.check((driveName + "-over-behavior-port"), Value("None")).asString();
244  string over_cmd_name = grpAllostatic.check((driveName + "-over-behavior"), Value("None")).asString();
245  if (over_port_name != "None" && over_cmd_name != "None")
246  {
247  active=true;
248  string out_port_name = "/" + moduleName + "/" + driveName + "/over_action:o";
249  alloDrive.behaviorOverPort = new Port();
250  alloDrive.behaviorOverPort->open(out_port_name);
251  yDebug() << "trying to connect to" << over_port_name;
252  while(!Network::connect(out_port_name, over_port_name))
253  {
254  yarp::os::Time::delay(0.5);
255  }
256  alloDrive.behaviorOverCmd = Bottle(over_cmd_name);
257  } else {
258  yInfo() << "No port name for" << driveName << "over-behavior-port";
259  }
260 
261  alloDrive.active = active;
262  allostaticDrives[driveName] = alloDrive;
263  }
264 
265  if (! Normalize(drivePriorities))
266  yDebug() << "Error: Drive priorities sum up to 0.";
267 
268 
269  yInfo() << "done.";
270 
271 }
272 
273 bool AllostaticController::Normalize(vector<double>& vec) {
274  double sum = 0.;
275  for (unsigned int i = 0; i<vec.size(); i++)
276  sum += vec[i];
277  if (sum == 0.)
278  return false;
279  for (unsigned int i = 0; i<vec.size(); i++)
280  vec[i] /= sum;
281  return true;
282 }
283 
285 {
286  double sensationValue;
287  for(auto& drive : allostaticDrives) {
288  sensationValue = drive.second.inputSensationPort->read()->get(0).asDouble();
289  Bottle cmd, reply;
290  reply.clear();
291  cmd.addString("par");
292  cmd.addString(drive.second.name);
293  cmd.addString("decaymult");
294  cmd.addDouble(sensationValue);
295  to_homeo_rpc.write(cmd, reply);
296  if (sensationValue) {
297  yDebug() << "Sensation ON";
298  drive.second.update(SENSATION_ON);
299  } else {
300  yDebug() << "Sensation OFF";
301  drive.second.update(SENSATION_OFF);
302  }
303  }
304 
306 
307  return true;
308 }
309 
310 // Return a DriveOutCZ structure indicating the name and level (UNDER or OVER) of the drive to by humour according to priorities and homeostatis levels
311 // name is "None" if no drive to be solved
313 
314  DriveOutCZ result;
315  bool inCZ;
316  int numOutCz = 0;
317  double random, cum;
318  vector<double> outOfCzPriorities(drivePriorities);
319  vector<string> names;
320  vector<double> min_diff;
321  vector<double> max_diff;
322 
323  for (unsigned int i=0;i<drivesList.size();i++) {
324  names.push_back(drivesList.get(i).asString());
325  min_diff.push_back(outputm_ports[i]->read()->get(0).asDouble());
326  max_diff.push_back(outputM_ports[i]->read()->get(0).asDouble());
327  inCZ = min_diff.back() <= 0 && max_diff.back() <= 0;
328  if (inCZ) {
329  outOfCzPriorities[i] = 0.;
330  }
331  else {
332  numOutCz ++;
333  }
334  }
335  if (! numOutCz) {
336  result.name = "None";
337  result.level = UNDEFINED;
338  return result;
339  }
340  if ( ! Normalize(outOfCzPriorities)) {
341  result.name = "None";
342  result.level = UNDEFINED;
343  return result;
344  }
345  random = Rand::scalar();
346  cum = outOfCzPriorities[0];
347  int idx = 0;
348  while (cum < random) {
349  cum += outOfCzPriorities[idx + 1];
350  idx++;
351  }
352  result.name = names[idx];
353  if (min_diff[idx] > 0)
354  result.level = UNDER;
355  else if (max_diff[idx] > 0)
356  result.level = OVER;
357  else
358  result.level = UNDEFINED;
359  return result;
360 }
361 
362 
364 {
365  DriveOutCZ activeDrive = chooseDrive();
366 
367  yInfo() << "Drive " + activeDrive.name + " chosen";
368 
369  if (activeDrive.name == "None") {
370  yInfo() << "No drive out of CZ." ;
371  return true;
372  }
373  else
374  {
375  yInfo() << "Drive " + activeDrive.name + " out of CZ." ;
376  }
377 
378  if (allostaticDrives[activeDrive.name].active) {
379  yInfo() << "Trigerring " + activeDrive.name;
380  allostaticDrives[activeDrive.name].triggerBehavior(activeDrive.level);
381  }
382  else {
383  yInfo() << "Drive " + activeDrive.name + " is not active";
384  }
385 
386  return true;
387 }
388 
389 
390 bool AllostaticController::respond(const Bottle& cmd, Bottle& reply)
391 {
392  yInfo() << "RPC received in allostaticController";
393  yDebug() << cmd.toString();
394 
395  reply.clear();
396 
397  if (cmd.get(0).asString() == "help" )
398  { string help = "\n";
399  help += " [manual on/off] : Turns on/off manual mode (for manual control of drives) \n";
400  reply.addString(help);
401  }
402  else if (cmd.get(0).asString() == "manual") {
403  if (cmd.get(1).asString() == "on") {
404  for(auto& allostaticDrive : allostaticDrives) {
405  allostaticDrive.second.manualMode = true;
406  }
407  yInfo() << "Manual mode turns on";
408  reply.addString("ack");
409  } else if (cmd.get(1).asString() == "off") {
410  for(auto& allostaticDrive : allostaticDrives) {
411  allostaticDrive.second.manualMode = false;
412  }
413  yInfo() << "Manual mode turns off";
414  reply.addString("ack");
415  }
416  Bottle bmCmd, bmReply;
417  bmCmd.addString("manual");
418  bmCmd.addString(cmd.get(1).asString()); // on or off
419  to_behavior_rpc.write(bmCmd, bmReply);
420  } else {
421  reply.addString("nack");
422  reply.addString("Unknown rpc command");
423  }
424  return true;
425 }
426 
428  if (behaviorUnderPort!=nullptr) {
429  behaviorUnderPort->interrupt();
430  }
431  if (behaviorOverPort!=nullptr) {
432  behaviorOverPort->interrupt();
433  }
434  if (inputSensationPort!=nullptr) {
435  inputSensationPort->interrupt();
436  }
437  return true;
438 }
439 
441  if (behaviorUnderPort!=nullptr) {
442  behaviorUnderPort->interrupt();
443  behaviorUnderPort->close();
444  delete behaviorUnderPort;
445  behaviorUnderPort=nullptr;
446  }
447  if (behaviorOverPort!=nullptr) {
448  behaviorOverPort->interrupt();
449  behaviorOverPort->close();
450  delete behaviorOverPort;
451  behaviorOverPort=nullptr;
452  }
453  if(inputSensationPort!=nullptr) {
454  inputSensationPort->interrupt();
455  inputSensationPort->close();
456  delete inputSensationPort;
457  inputSensationPort=nullptr;
458  }
459  return true;
460 }
461 
463 {
464  Bottle cmds;
465  switch (mode) {
466  case SENSATION_ON:
467  cmds = sensationOnCmd;
468  break;
469  case SENSATION_OFF:
470  cmds = sensationOffCmd;
471  break;
472  default:
473  yDebug() << "Update mode not implemented";
474  yDebug() << to_string(mode);
475  break;
476  }
477  Bottle rplies;
478  for (unsigned int i=0; i<cmds.size(); i++){
479  Bottle rply;
480  Bottle cmd = *cmds.get(i).asList();
481  if (! manualMode) {
482  homeoPort->write(cmd,rply);
483  }
484  rplies.addList() = rply;
485  }
486  return rplies;
487 }
488 
490 {
491  Bottle cmd, rply, rplies;
492  // before trigger command
493  if (! beforeTriggerCmd.isNull() && !manualMode) {
494  cmd.clear();
495  rply.clear();
496  rplies.clear();
497  for (unsigned int i=0; i<beforeTriggerCmd.size(); i++){
498  rply.clear();
499  Bottle cmd = *beforeTriggerCmd.get(i).asList();
500  yDebug() << cmd.toString();
501  homeoPort->write(cmd,rply);
502  rplies.addList() = rply;
503  }
504  }
505 
506  Port* port = nullptr;
507  switch (mode) {
508  case UNDER:
509  cmd = behaviorUnderCmd;
510  port = behaviorUnderPort;
511  break;
512  case OVER:
513  cmd = behaviorOverCmd;
514  port = behaviorOverPort;
515  break;
516  default:
517  yWarning() << "Trigger mode not implemented";
518  yWarning() << to_string(mode);
519  return;
520  }
521 
522  yInfo() << "Drive " + name + " to be triggered via " << port->getName();
523  port->write(cmd, rply);
524 
525  // after trigger command
526  if ( ! afterTriggerCmd.isNull() && ! manualMode) {
527  cmd.clear();
528  rply.clear();
529  rplies.clear();
530  for (unsigned int i=0; i<afterTriggerCmd.size(); i++){
531  rply.clear();
532  Bottle cmd = *afterTriggerCmd.get(i).asList();
533  yDebug() << cmd.toString();
534  homeoPort->write(cmd,rply);
535  rplies.addList() = rply;
536  yDebug() << "triggerBehavior completed.";
537  }
538  }
539 }
Bottle update(DriveUpdateMode mode)
bool configure(yarp::os::ResourceFinder &rf)
void triggerBehavior(OutCZ mode)
triggers a behavior if needs are out of threshold
DriveUpdateMode
Port * behaviorOverPort
Port to be used when drive hits upper threshold of homeostatic level.
BufferedPort< Bottle > * inputSensationPort
Port from sensationManager.
bool Normalize(vector< double > &vec)
Normalize normalize drive priorities.
Bottle behaviorOverCmd
Command to be send to behaviorOverPort when drive hits upper threshold of homeostatic level...
bool interrupt_ports()
Interrupt ports of the drive.
bool close_ports()
Close ports of the drive.
bool respond(const Bottle &cmd, Bottle &reply)
Port * behaviorUnderPort
Port to be used when drive falls below homeostatic level.
bool updateAllostatic()
updateAllostatic Update the drives accordingly to the stimuli
DriveOutCZ chooseDrive()
chooseDrive Choose a drive out of CZ, according to drive priorities
Bottle behaviorUnderCmd
Command to be send to behaviorUnderPort when drive falls below homeostatic level. ...
Port * homeoPort
Port to homeostasis module.
string name
Name of the drive.