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