YARP
Yet Another Robot Platform
VirtualAnalogWrapper.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * All rights reserved.
4  *
5  * This software may be modified and distributed under the terms of the
6  * BSD-3-Clause license. See the accompanying LICENSE file for details.
7  */
8 
9 #include "VirtualAnalogWrapper.h"
10 // #include <iostream>
11 #include <yarp/os/LogComponent.h>
12 #include <yarp/os/LogStream.h>
13 
14 using namespace std;
15 using namespace yarp::os;
16 using namespace yarp::dev;
17 
18 
19 namespace {
20 YARP_LOG_COMPONENT(VIRTUALANALOGSERVER, "yarp.device.virtualAnalogServer")
21 constexpr int MAX_ENTRIES = 255;
22 }
23 
24 
26 {
27  detach();
28 }
29 
31 {
32  detach();
33 }
34 
35 bool AnalogSubDevice::configure(int map0, int map1, const std::string &key)
36 {
37  mIsConfigured=false;
38 
39  if (map1<map0)
40  {
41  yCError(VIRTUALANALOGSERVER) << "Check configuration file top<base.";
42  return false;
43  }
44 
45  mMap0=map0;
46  mMap1=map1;
47 
48  mKey=key;
49 
50  mTorques.resize(mMap1-mMap0+1);
51 
52  mIsConfigured=true;
53 
54  return true;
55 }
56 
57 bool AnalogSubDevice::attach(yarp::dev::PolyDriver *device, const std::string &key)
58 {
59  if (key!=mKey)
60  {
61  yCError(VIRTUALANALOGSERVER) << "Wrong device" << key.c_str();
62  return false;
63  }
64 
65  //configure first
66  if (!mIsConfigured)
67  {
68  yCError(VIRTUALANALOGSERVER) << "'configure' should be called before you can attach any device";
69  return false;
70  }
71 
72  if (!device)
73  {
74  yCError(VIRTUALANALOGSERVER) << "Invalid device (null pointer)";
75  return false;
76  }
77 
78  mpDevice=device;
79 
80  if (mpDevice->isValid())
81  {
82  mpDevice->view(mpSensor);
83  }
84  else
85  {
86  yCError(VIRTUALANALOGSERVER) << "Invalid device " << key << " (isValid() returned false)";
87  return false;
88  }
89 
90  if (mpSensor)
91  {
92  mIsAttached=true;
93  return true;
94  }
95 
96  return false;
97 }
98 
100 {
101  mMap0=mMap1=-1;
102 
103  mpDevice=nullptr;
104  mpSensor=nullptr;
105 
106  mIsConfigured=false;
107  mIsAttached=false;
108 }
109 
111 {
112  yCDebug(VIRTUALANALOGSERVER) << config.toString().c_str();
113 
114  mIsVerbose = (config.check("verbose","if present, give detailed output"));
115 
116  if (mIsVerbose) yCDebug(VIRTUALANALOGSERVER) << "Running with verbose output\n";
117 
118  //thus thread period is useful for output port... this input port has callback so maybe can skip it (?)
119  //thread_period = prop.check("threadrate", 20, "thread rate in ms. for streaming encoder data").asInt32();
120 
121  yCDebug(VIRTUALANALOGSERVER) << "Using VirtualAnalogServer\n";
122 
123  if (!config.check("networks", "list of networks merged by this wrapper"))
124  {
125  yCError(VIRTUALANALOGSERVER) << "Missing networks parameters";
126  return false;
127  }
128 
129  Bottle *networks=config.find("networks").asList();
130  mNSubdevs=networks->size();
131  mSubdevices.resize(mNSubdevs);
132 
133  mChan2Board.resize(MAX_ENTRIES);
134  mChan2BAddr.resize(MAX_ENTRIES);
135  for (int i = 0; i < MAX_ENTRIES; i++)
136  {
137  mChan2Board[i]=-1;
138  mChan2BAddr[i]=-1;
139  }
140 
141  int totalJ=0;
142 
143  for (size_t k=0; k<networks->size(); ++k)
144  {
145  auto parameters = config.findGroup(networks->get(k).asString());
146  int map0, map1, map2, map3;
147 
148  if (parameters.size() == 2)
149  {
150  auto* bot = parameters.get(1).asList();
151  Bottle tmpBot;
152  if (bot == nullptr)
153  {
154  // try to read data as a string in the last resort
155  tmpBot.fromString(parameters.get(1).asString());
156  if (tmpBot.size() != 4)
157  {
158  yCError(VIRTUALANALOGSERVER) << "Error: check network parameters in part description"
159  << "--> I was expecting" << networks->get(k).asString() << "followed by four integers between parentheses"
160  << "Got: " << parameters.toString();
161  return false;
162  }
163 
164  bot = &tmpBot;
165  }
166 
167  map0 = bot->get(0).asInt32();
168  map1 = bot->get(1).asInt32();
169  map2 = bot->get(2).asInt32();
170  map3 = bot->get(3).asInt32();
171  }
172  else if (parameters.size() == 5)
173  {
174  yCError(VIRTUALANALOGSERVER) << "Parameter networks use deprecated syntax";
175  map0 = parameters.get(1).asInt32();
176  map1 = parameters.get(2).asInt32();
177  map2 = parameters.get(3).asInt32();
178  map3 = parameters.get(4).asInt32();
179  }
180  else
181  {
182  yCError(VIRTUALANALOGSERVER) << "Error: check network parameters in part description"
183  << "--> I was expecting" << networks->get(k).asString() << "followed by four integers between parentheses"
184  << "Got: " << parameters.toString();
185  return false;
186  }
187 
188  if (map0 >= MAX_ENTRIES || map1 >= MAX_ENTRIES || map2>= MAX_ENTRIES || map3>= MAX_ENTRIES ||
189  map0 <0 || map1 <0 || map2<0 || map3<0)
190  {
191  yCError(VIRTUALANALOGSERVER) << "Invalid map entries in networks section, failed initial check";
192  return false;
193  }
194 
195  for (int j=map0; j<=map1; ++j)
196  {
197  mChan2Board[j]=k;
198  mChan2BAddr[j]=j-map0+map2;
199  }
200 
201  if (!mSubdevices[k].configure(map2,map3,networks->get(k).asString()))
202  {
203  yCError(VIRTUALANALOGSERVER) << "Configure of subdevice ret false";
204  return false;
205  }
206 
207  totalJ+=map1-map0+1;
208  }
209 
210  // Verify minimum set of parameters required
211  if(!config.check("robotName") ) // ?? qui dentro, da dove lo pesco ??
212  {
213  yCError(VIRTUALANALOGSERVER) << "Missing robotName, check your configuration file!";
214  return false;
215  }
216 
217  if (config.check("deviceId"))
218  {
219  yCError(VIRTUALANALOGSERVER) << "The parameter 'deviceId' has been deprecated, please use parameter 'name' instead. \n"
220  << "e.g. In the VFT wrapper configuration files of your robot, replace '<param name=""deviceId""> left_arm </param>' \n"
221  << "with '/icub/joint_vsens/left_arm:i' ";
222  return false;
223  }
224 
225  std::string port_name = config.check("name",Value("controlboard"),"Virtual analog wrapper port name, e.g. /icub/joint_vsens/left_arm:i").asString();
226  std::string robot_name = config.find("robotName").asString();
227 
228  if (!mPortInputTorques.open(port_name))
229  {
230  yCError(VIRTUALANALOGSERVER) << "Can't open port " << port_name.c_str();
231  return false;
232  }
233 
234  return true;
235 }
236 
238 {
239  mPortInputTorques.interrupt();
240  mPortInputTorques.close();
241  Thread::stop();
242  return true;
243 }
244 
246 {
247  mMutex.lock();
248 
249  for (int p=0; p<polylist.size(); ++p)
250  {
251  std::string key=polylist[p]->key;
252 
253  // find appropriate entry in list of subdevices and attach
254  for (auto& mSubdevice : mSubdevices)
255  {
256  if (mSubdevice.getKey() == key)
257  {
258  if (!mSubdevice.attach(polylist[p]->poly,key))
259  {
260  mMutex.unlock();
261  return false;
262  }
263  }
264  }
265  }
266 
267  //check if all devices are attached to the driver
268  for (auto& mSubdevice : mSubdevices)
269  {
270  if (!mSubdevice.isAttached())
271  {
272  mMutex.unlock();
273  return false;
274  }
275  }
276 
277  mMutex.unlock();
278 
279  Thread::start();
280 
281  return true;
282 }
283 
285 {
286  mMutex.lock();
287 
288  for(int k=0; k<mNSubdevs; ++k)
289  {
290  mSubdevices[k].detach();
291  }
292 
293  mMutex.unlock();
294 
295 // close();
296 
297  return true;
298 }
299 
301 {
302  if (first_check) return true;
303 
304  for (int i=0; i<elems; i++)
305  {
306  if (mChan2Board[i]==-1 || mChan2BAddr[i]==-1)
307  {
308  yCError(VIRTUALANALOGSERVER) << "Invalid map entries in networks section, failed runtime check"
309  << " i: " << i << "mChan2Board[i] is " << mChan2Board[i] << " chan2add is " << mChan2BAddr[i];
310  return false;
311  }
312  }
313 
314  yCTrace(VIRTUALANALOGSERVER) << "perform_first_check() successfully completed";
315  first_check = true;
316  return true;
317 }
318 
320 {
321  yarp::os::Bottle *pTorques;
322  bool sendLastValueBeforeTimeout = false;
323  while (!Thread::isStopping())
324  {
325  pTorques=mPortInputTorques.read(false);
326  double timeNow=Time::now();
327 
328  if (pTorques)
329  {
330  sendLastValueBeforeTimeout = false;
331  mMutex.lock();
332 
333  lastRecv=Time::now();
334  switch (pTorques->get(0).asInt32())
335  {
336  case 1: //arm torque message
337  if (perform_first_check(6)==false) break;
338  mSubdevices[mChan2Board[0]].setTorque(mChan2BAddr[0],pTorques->get(1).asFloat64()); //shoulder 1 pitch (0)
339  mSubdevices[mChan2Board[1]].setTorque(mChan2BAddr[1],pTorques->get(2).asFloat64()); //shoulder 2 roll (1)
340  mSubdevices[mChan2Board[2]].setTorque(mChan2BAddr[2],pTorques->get(3).asFloat64()); //shoulder 3 yaw (2)
341  mSubdevices[mChan2Board[3]].setTorque(mChan2BAddr[3],pTorques->get(4).asFloat64()); //elbow (3)
342  mSubdevices[mChan2Board[4]].setTorque(mChan2BAddr[4],pTorques->get(5).asFloat64()); //wrist pronosupination (4)
343  mSubdevices[mChan2Board[5]].setTorque(mChan2BAddr[5],0.0);
344  break;
345 
346  case 2: //legs torque message
347  if (perform_first_check(6)==false) break;
348  mSubdevices[mChan2Board[0]].setTorque(mChan2BAddr[0],pTorques->get(1).asFloat64()); //hip pitch
349  mSubdevices[mChan2Board[1]].setTorque(mChan2BAddr[1],pTorques->get(2).asFloat64()); //hip roll
350  mSubdevices[mChan2Board[2]].setTorque(mChan2BAddr[2],pTorques->get(3).asFloat64()); //hip yaw
351  mSubdevices[mChan2Board[3]].setTorque(mChan2BAddr[3],pTorques->get(4).asFloat64()); //knee
352  mSubdevices[mChan2Board[4]].setTorque(mChan2BAddr[4],pTorques->get(5).asFloat64()); //ankle pitch
353  mSubdevices[mChan2Board[5]].setTorque(mChan2BAddr[5],pTorques->get(6).asFloat64()); //ankle roll
354  break;
355 
356  case 3: //wrist torque message
357  if (perform_first_check(6)==false) break;
358  mSubdevices[mChan2Board[0]].setTorque(mChan2BAddr[0],pTorques->get(6).asFloat64()); //wrist yaw (6)
359  mSubdevices[mChan2Board[1]].setTorque(mChan2BAddr[1],pTorques->get(7).asFloat64()); //wrist pitch (7)
360  mSubdevices[mChan2Board[2]].setTorque(mChan2BAddr[2],0.0);
361  mSubdevices[mChan2Board[3]].setTorque(mChan2BAddr[3],0.0);
362  mSubdevices[mChan2Board[4]].setTorque(mChan2BAddr[4],0.0);
363  mSubdevices[mChan2Board[5]].setTorque(mChan2BAddr[5],0.0);
364  break;
365 
366  case 4: // torso
367  if (perform_first_check(3)==false) break;
368  mSubdevices[mChan2Board[0]].setTorque(mChan2BAddr[0],pTorques->get(1).asFloat64()); //torso yaw (respect gravity)
369  mSubdevices[mChan2Board[1]].setTorque(mChan2BAddr[1],pTorques->get(2).asFloat64()); //torso roll (lateral movement)
370  mSubdevices[mChan2Board[2]].setTorque(mChan2BAddr[2],pTorques->get(3).asFloat64()); //torso pitch (front-back movement)
371 // mSubdevices[mChan2Board[3]].setTorque(mChan2BAddr[3],0.0);
372 // mSubdevices[mChan2Board[4]].setTorque(mChan2BAddr[4],0.0);
373 // mSubdevices[mChan2Board[5]].setTorque(mChan2BAddr[5],0.0);
374  break;
375 
376  default:
377  yCError(VIRTUALANALOGSERVER) << "Got unexpected " << pTorques->get(0).asInt32() << " message on virtualAnalogServer.";
378  }
379 
380  for (int d=0; d<mNSubdevs; ++d)
381  {
382  mSubdevices[d].flushTorques();
383  }
384 
385  mMutex.unlock();
386  }
387  else
388  {
389  // sending rate from wholeBody is 10ms, if nothing is got now, wait that much time
390  yarp::os::Time::delay(0.001);
391  }
392 
393  if(first_check)
394  {
395  if ((lastRecv+0.080 < timeNow) && (!sendLastValueBeforeTimeout))
396  {
397  /* If 80ms have passed since the last received message, reset values to zero (just once).
398  * Sending time will be 1ms due to the delay above (else case).
399  */
400  for (int d=0; d<mNSubdevs; ++d)
401  {
402  mSubdevices[d].resetTorque();
403  mSubdevices[d].flushTorques();
404 
405  }
406  // Virtual Sensor status is not handled now because server DO NOT implement IVirtual AnalogSensor Interface.
407  // status=IAnalogSensor::AS_TIMEOUT;
408  yCError(VIRTUALANALOGSERVER) << "Timeout!! No new value received for more than " << timeNow - lastRecv << " secs.";
409  sendLastValueBeforeTimeout = true;
410  }
411  }
412  }
413 }
bool configure(int map0, int map1, const std::string &key)
bool attach(yarp::dev::PolyDriver *driver, const std::string &key)
bool close() override
Close the DeviceDriver.
bool attachAll(const yarp::dev::PolyDriverList &p) override
Attach to a list of objects.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
bool detachAll() override
Detach the object (you must have first called attach).
void run() override
Main body of the new thread.
bool perform_first_check(int elems)
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:77
A container for a device driver.
Definition: PolyDriver.h:27
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:207
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:254
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:243
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
A base class for nested structures that can be searched.
Definition: Searchable.h:69
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
virtual Bottle & findGroup(const std::string &key) const =0
Gets a list corresponding to a given keyword.
A single value (typically within a Bottle).
Definition: Value.h:47
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:225
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:207
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:243
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
#define yCError(component,...)
Definition: LogComponent.h:157
#define yCTrace(component,...)
Definition: LogComponent.h:88
#define yCDebug(component,...)
Definition: LogComponent.h:112
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
An interface for the device drivers.
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:114
An interface to the operating system, including Port based communication.