YARP
Yet Another Robot Platform
BatteryWrapper.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "BatteryWrapper.h"
20 #include <sstream>
21 #include <string>
23 #include <yarp/os/LogComponent.h>
24 #include <yarp/os/LogStream.h>
25 #include <time.h>
26 #include <stdlib.h>
27 
28 using namespace yarp::sig;
29 using namespace yarp::dev;
30 using namespace yarp::os;
31 using namespace std;
32 
33 namespace {
34 YARP_LOG_COMPONENT(BATTERYWRAPPER, "yarp.devices.BatteryWrapper")
35 }
36 
38 {
39  m_period = DEFAULT_THREAD_PERIOD;
40  m_ibattery_p = nullptr;
41  m_ownDevices = false;
42  memset(m_log_buffer, 0, 1024);
43 }
44 
46 {
47  threadRelease();
48  m_ibattery_p = nullptr;
49 }
50 
51 bool BatteryWrapper::attachAll(const PolyDriverList &battery2attach)
52 {
53  if (m_ownDevices)
54  {
55  return false;
56  }
57 
58  if (battery2attach.size() != 1)
59  {
60  yCError(BATTERYWRAPPER, "Cannot attach more than one device");
61  return false;
62  }
63 
64  yarp::dev::PolyDriver * Idevice2attach = battery2attach[0]->poly;
65 
66  if (Idevice2attach->isValid())
67  {
68  Idevice2attach->view(m_ibattery_p);
69  }
70 
71  if(nullptr == m_ibattery_p)
72  {
73  yCError(BATTERYWRAPPER, "Subdevice passed to attach method is invalid");
74  return false;
75  }
76  attach(m_ibattery_p);
77  PeriodicThread::setPeriod(m_period);
78  return PeriodicThread::start();
79 }
80 
82 {
83  if (PeriodicThread::isRunning())
84  {
85  PeriodicThread::stop();
86  }
87  m_ibattery_p = nullptr;
88  return true;
89 }
90 
91 void BatteryWrapper::attach(yarp::dev::IBattery *s)
92 {
93  m_ibattery_p=s;
94 }
95 
96 void BatteryWrapper::detach()
97 {
98  m_ibattery_p = nullptr;
99 }
100 
101 bool BatteryWrapper::read(yarp::os::ConnectionReader& connection)
102 {
103  yarp::os::Bottle in;
104  yarp::os::Bottle out;
105  bool ok = in.read(connection);
106  if (!ok) return false;
107 
108  // parse in, prepare out
109  int code = in.get(0).asVocab();
110  bool ret = false;
111  if (code == VOCAB_IBATTERY)
112  {
113  int cmd = in.get(1).asVocab();
114  if (cmd == VOCAB_BATTERY_INFO)
115  {
116  if (m_ibattery_p)
117  {
118  std::string info;
119  m_ibattery_p->getBatteryInfo(info);
120  out.addVocab(VOCAB_IS);
121  out.addVocab(cmd);
122  out.addString(info);
123  ret = true;
124  }
125  }
126  else
127  {
128  yCError(BATTERYWRAPPER, "Invalid vocab received");
129  }
130  }
131  else
132  {
133  yCError(BATTERYWRAPPER, "Invalid vocab received");
134  }
135 
136  if (!ret)
137  {
138  out.clear();
139  out.addVocab(VOCAB_FAILED);
140  }
141 
142  yarp::os::ConnectionWriter *returnToSender = connection.getWriter();
143  if (returnToSender != nullptr)
144  {
145  out.write(*returnToSender);
146  }
147  return true;
148 }
149 
151 {
152  return true;
153 }
154 
156 {
157  Property params;
158  params.fromString(config.toString());
159 
160  if (!config.check("period"))
161  {
162  m_period = 1.0;
163  yCWarning(BATTERYWRAPPER) << "Missing 'period' parameter. Assuming default value 1.0 s";
164  }
165  else
166  {
167  m_period = config.find("period").asFloat32();
168  }
169  yCInfo(BATTERYWRAPPER) << "Using period:" << m_period << "s";
170 
171  if (!config.check("quitPortName"))
172  {
173  m_quitPortName = config.find("quitPortName").asString();
174  }
175 
176  if (!config.check("name"))
177  {
178  yCError(BATTERYWRAPPER) << "Missing 'name' parameter. Check you configuration file; it must be like:";
179  yCError(BATTERYWRAPPER) << "--name: prefix of the ports opened by the device, e.g. /robotName/battery1";
180  yCError(BATTERYWRAPPER) << "/data:o and /rpc:i are automatically appended by the wrapper at the end";
181  return false;
182  }
183  else
184  {
185  m_streamingPortName = config.find("name").asString() + "/data:o";
186  m_rpcPortName = config.find("name").asString() + "/rpc:i";
187  }
188 
189  m_enable_shutdown = config.check("enable_shutdown", Value(0), "enable/disable the automatic shutdown").asBool();
190  m_enable_log = config.check("enable_log", Value(0), "enable/disable log to file").asBool();
191 
192  if(!initialize_YARP(config))
193  {
194  yCError(BATTERYWRAPPER) << m_sensorId << "Error initializing YARP ports";
195  return false;
196  }
197 
198  if (m_enable_log)
199  {
200  yCInfo(BATTERYWRAPPER, "writing to log file batteryLog.txt");
201  m_logFile = fopen("batteryLog.txt", "w");
202  }
203 
204  if (config.check("subdevice"))
205  {
206  PolyDriverList driverlist;
207  Property p;
208  p.fromString(config.toString());
209  p.unput("device");
210  p.unput("subdevice");
211  p.put("device", config.find("subdevice").asString());
212  p.setMonitor(config.getMonitor(), "subdevice"); // pass on any monitoring
213 
214  if (!m_driver.open(p) || !m_driver.isValid())
215  {
216  yCError(BATTERYWRAPPER) << "Failed to open subdevice.. check params";
217  return false;
218  }
219 
220  driverlist.push(&m_driver, "1");
221  if (!attachAll(driverlist))
222  {
223  yCError(BATTERYWRAPPER) << "Failed to open subdevice.. check params";
224  return false;
225  }
226  m_ownDevices = true;
227  }
228 
229  return true;
230 }
231 
232 bool BatteryWrapper::initialize_YARP(yarp::os::Searchable &params)
233 {
234  if (!m_streamingPort.open(m_streamingPortName.c_str()))
235  {
236  yCError(BATTERYWRAPPER) << "Error opening port" << m_streamingPortName;
237  return false;
238  }
239  if (!m_rpcPort.open(m_rpcPortName.c_str()))
240  {
241  yCError(BATTERYWRAPPER) << "Error opening port" << m_rpcPortName;
242  return false;
243  }
244  m_rpcPort.setReader(*this);
245  return true;
246 }
247 
249 {
250 }
251 
253 {
254  if (m_ibattery_p!=nullptr)
255  {
256  m_log_buffer[0] = 0;
257 
258  //acquire data from the wrapped device
259  bool ret_sts, ret_chg, ret_vlt, ret_cur, ret_tmp;
260  {
261  double tmp;
262  ret_chg = m_ibattery_p->getBatteryCharge(tmp);
263  if (ret_chg) m_battery_charge = tmp;
264  }
265  {
266  double tmp;
267  ret_vlt = m_ibattery_p->getBatteryVoltage(tmp);
268  if (ret_vlt) m_battery_voltage = tmp;
269  }
270  {
271  double tmp;
272  ret_cur = m_ibattery_p->getBatteryCurrent(tmp);
273  if (ret_cur) m_battery_current = tmp;
274  }
275  {
276  double tmp;
277  ret_tmp = m_ibattery_p->getBatteryTemperature(tmp);
278  if (ret_tmp) m_battery_temperature = tmp;
279  }
280  {
282  ret_sts = m_ibattery_p->getBatteryStatus(tmp);
283  if (ret_sts) m_battery_status = tmp;
284  }
285 
286  if (ret_sts)
287  {
288  m_lastStateStamp.update();
289  yarp::os::Bottle& b = m_streamingPort.prepare();
290  b.clear();
291  b.addFloat64(m_battery_voltage); //0
292  b.addFloat64(m_battery_current); //1
293  b.addFloat64(m_battery_charge); //2
294  b.addFloat64(m_battery_temperature); //3
295  b.addInt32(m_battery_status); //4
296  m_streamingPort.setEnvelope(m_lastStateStamp);
297  m_streamingPort.write();
298 
299  // if the battery is not charging, checks its status of charge
300  if (m_battery_status >0.4) check_battery_status(m_battery_charge);
301 
302  // save data to file
303  if (m_enable_log)
304  {
305  time_t rawtime;
306  struct tm * timeinfo;
307  time(&rawtime);
308  timeinfo = localtime(&rawtime);
309  char* battery_timestamp = asctime(timeinfo);
310  std::snprintf(m_log_buffer, 1024, "battery status: %+6.1fA % 6.1fV charge:% 6.1f%% time: %s", m_battery_current, m_battery_voltage, m_battery_charge, battery_timestamp);
311  fprintf(m_logFile, "%s", m_log_buffer);
312  }
313  }
314  else
315  {
316  yCError(BATTERYWRAPPER, "BatteryWrapper: %s: Sensor returned error", m_sensorId.c_str());
317  }
318  }
319 }
320 
322 {
323  yCTrace(BATTERYWRAPPER, "BatteryWrapper::Close");
324  if (PeriodicThread::isRunning())
325  {
326  PeriodicThread::stop();
327  }
328 
329  //close the device
330  m_driver.close();
331 
332  m_streamingPort.interrupt();
333  m_streamingPort.close();
334  m_rpcPort.interrupt();
335  m_rpcPort.close();
336 
337  // save data to file
338  if (m_enable_log)
339  {
340  fclose(m_logFile);
341  }
342 
343  PeriodicThread::stop();
344  detachAll();
345  return true;
346 }
347 
348 void BatteryWrapper::notify_message(string msg)
349 {
350 #ifdef WIN32
351  yCWarning(BATTERYWRAPPER, "%s", msg.c_str());
352 #else
353  yCWarning(BATTERYWRAPPER, "%s", msg.c_str());
354  string cmd = "echo " + msg + " | wall";
355  int retval;
356  retval = system(cmd.c_str());
357  yCDebug(BATTERYWRAPPER) << "system executed command" << cmd.c_str() << " with return value:" << retval;
358 #endif
359 }
360 
361 void BatteryWrapper::emergency_shutdown(string msg)
362 {
363 #ifdef WIN32
364  string cmd;
365  cmd = "shutdown /s /t 120 /c " + msg;
366  yCWarning(BATTERYWRAPPER, "%s", msg.c_str());
367  system(cmd.c_str());
368 #else
369  string cmd;
370  int retval;
371  yCWarning(BATTERYWRAPPER, "%s", msg.c_str());
372  cmd = "echo " + msg + " | wall";
373  retval = system(cmd.c_str());
374  yCDebug(BATTERYWRAPPER) << "system executed command" << cmd.c_str() << " with return value:" << retval;
375 
376  cmd = "sudo shutdown -h 2 " + msg;
377  retval = system(cmd.c_str());
378  yCDebug(BATTERYWRAPPER) << "system executed command" << cmd.c_str() << " with return value:" << retval;
379 
380 #ifdef ICUB_SSH_SHUTDOWN
381  cmd = "ssh icub@pc104 sudo shutdown -h 2";
382  retval = system(cmd.c_str());
383  yCDebug(BATTERYWRAPPER) << "system executed command" << cmd.c_str() << " with return value:" << retval;
384 #endif
385 #endif
386 }
387 
388 void BatteryWrapper::check_battery_status(double battery_charge)
389 {
390  static bool notify_15 = true;
391  static bool notify_12 = true;
392  static bool notify_10 = true;
393  static bool notify_0 = true;
394 
395  if (battery_charge > 20)
396  {
397  notify_15 = true;
398  notify_12 = true;
399  notify_10 = true;
400  notify_0 = true;
401  }
402 
403  if (battery_charge < 5)
404  {
405  if (notify_0)
406  {
407  if (m_enable_shutdown)
408  {
409  emergency_shutdown("CRITICAL WARNING: battery charge below critical level 5%. The robot will be stopped and the system will shutdown in 2mins.");
410  if (m_quitPortName != "") { stop_robot(m_quitPortName); }
411  notify_0 = false;
412  }
413  else
414  {
415  notify_message("CRITICAL WARNING: battery charge reached critical level 5%, but the emergency shutodown is currently disabled!");
416  notify_0 = false;
417  }
418  }
419  }
420  else if (battery_charge < 10)
421  {
422  if (notify_10) { notify_message("WARNING: battery charge below 10%"); notify_10 = false; }
423  }
424  else if (battery_charge < 12)
425  {
426  if (notify_12) { notify_message("WARNING: battery charge below 12%"); notify_12 = false; }
427  }
428  else if (battery_charge < 15)
429  {
430  if (notify_15) { notify_message("WARNING: battery charge below 15%"); notify_15 = false; }
431  }
432 }
433 
434 void BatteryWrapper::stop_robot(string quit_port)
435 {
436  //typical quit_port:
437  // "/icub/quit"
438  // "/ikart/quit"
439 
440  Port port_shutdown;
441  port_shutdown.open((m_streamingPortName + "/shutdown:o").c_str());
442  yarp::os::Network::connect((m_streamingPortName + "/shutdown:o").c_str(), quit_port.c_str());
443  Bottle bot;
444  bot.addString("quit");
445  port_shutdown.write(bot);
446  port_shutdown.interrupt();
447  port_shutdown.close();
448 }
#define DEFAULT_THREAD_PERIOD
Definition: AnalogWrapper.h:46
define control board standard interfaces
constexpr yarp::conf::vocab32_t VOCAB_IS
Definition: GenericVocabs.h:17
constexpr yarp::conf::vocab32_t VOCAB_FAILED
Definition: GenericVocabs.h:19
constexpr yarp::conf::vocab32_t VOCAB_IBATTERY
Definition: IBattery.h:17
constexpr yarp::conf::vocab32_t VOCAB_BATTERY_INFO
Definition: IBattery.h:18
bool ret
bool attachAll(const yarp::dev::PolyDriverList &p) override
Attach to a list of objects.
void run() override
Loop function.
bool threadInit() override
Initialization method.
bool detachAll() override
Detach the object (you must have first called attach).
bool close() override
Close the DeviceDriver.
void threadRelease() override
Release method.
bool open(yarp::os::Searchable &params) override
Open the DeviceDriver.
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:77
A generic battery interface.
Definition: IBattery.h:33
virtual bool getBatteryCurrent(double &current)=0
Get the instantaneous current measurement.
virtual bool getBatteryVoltage(double &voltage)=0
Get the instantaneous voltage measurement.
virtual bool getBatteryInfo(std::string &battery_info)=0
get the battery hardware characteristics (e.g.
virtual bool getBatteryStatus(Battery_status &status)=0
get the battery status
virtual bool getBatteryCharge(double &charge)=0
get the battery status of charge
virtual bool getBatteryTemperature(double &temperature)=0
get the battery temperature
void push(PolyDriver *p, const char *k)
A container for a device driver.
Definition: PolyDriver.h:27
bool close() override
Close the DeviceDriver.
Definition: PolyDriver.cpp:176
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:199
bool open(const std::string &txt)
Construct and configure a device by its common name.
Definition: PolyDriver.cpp:143
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:243
void addFloat64(yarp::conf::float64_t x)
Places a 64-bit floating point number in the bottle, at the end of the list.
Definition: Bottle.cpp:161
void addVocab(int x)
Places a vocabulary item in the bottle, at the end of the list.
Definition: Bottle.cpp:167
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:124
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:233
void addInt32(std::int32_t x)
Places a 32-bit integer in the bottle, at the end of the list.
Definition: Bottle.cpp:143
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:173
void close() override
Stop port activity.
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
void interrupt() override
Interrupt any current reads or writes attached to the port.
void write(bool forceStrict=false)
Write the current object being returned by BufferedPort::prepare.
T & prepare()
Access the object which will be transmitted by the next call to yarp::os::BufferedPort::write.
An interface for reading from a network connection.
virtual ConnectionWriter * getWriter()=0
Gets a way to reply to the message, if possible.
An interface for writing to a network connection.
static bool connect(const std::string &src, const std::string &dest, const std::string &carrier="", bool quiet=true)
Request that an output port connect to an input port.
Definition: Network.cpp:685
An abstraction for a periodic thread.
A mini-server for network communication.
Definition: Port.h:50
bool write(const PortWriter &writer, const PortWriter *callback=nullptr) const override
Write an object to the port.
Definition: Port.cpp:430
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:505
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:377
void close() override
Stop port activity.
Definition: Port.cpp:357
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:82
A class for storing options and configuration information.
Definition: Property.h:37
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1046
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:998
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Definition: Property.cpp:1029
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.
void update()
Set the timestamp to the current time, and increment the sequence number (wrapping to 0 if the sequen...
Definition: Stamp.cpp:113
A single value (typically within a Bottle).
Definition: Value.h:47
virtual std::int32_t asVocab() const
Get vocabulary identifier as an integer.
Definition: Value.cpp:231
virtual yarp::conf::float32_t asFloat32() const
Get 32-bit floating point value.
Definition: Value.cpp:219
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
#define yCInfo(component,...)
Definition: LogComponent.h:135
#define yCError(component,...)
Definition: LogComponent.h:157
#define yCTrace(component,...)
Definition: LogComponent.h:88
#define yCWarning(component,...)
Definition: LogComponent.h:146
#define yCDebug(component,...)
Definition: LogComponent.h:112
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
An interface for the device drivers.
An interface to the operating system, including Port based communication.
Signal processing.
Definition: Image.h:25