Regilo
A simple C++ library for controlling the Neato XV robot and the Hokuyo scanner.
controller.hpp
1 /*
2  * Regilo
3  * Copyright (C) 2015-2016 Branislav HolĂ˝ <branoholy@gmail.com>
4  *
5  * This file is part of Regilo.
6  *
7  * Regilo is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Regilo is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Regilo. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef REGILO_CONTROLLER_HPP
23 #define REGILO_CONTROLLER_HPP
24 
25 #include <sstream>
26 
27 #include <boost/asio/io_service.hpp>
28 #include <boost/asio/read_until.hpp>
29 #include <boost/asio/streambuf.hpp>
30 #include <boost/asio/write.hpp>
31 
32 #include "log.hpp"
33 #include "utils.hpp"
34 
35 namespace regilo {
36 
37 namespace ba = boost::asio;
38 
43 {
44 public:
48  virtual ~IController() = default;
49 
54  virtual void connect(const std::string& endpoint) = 0;
55 
60  virtual bool isConnected() const = 0;
61 
66  virtual std::string getEndpoint() const = 0;
67 
72  virtual std::shared_ptr<ILog> getLog() = 0;
73 
78  virtual std::shared_ptr<const ILog> getLog() const = 0;
79 
84  virtual void setLog(std::shared_ptr<ILog> log) = 0;
85 
91  virtual std::string sendCommand(const std::string& command) = 0;
92 };
93 
97 template<typename StreamT>
98 class StreamController : public virtual IController
99 {
100 private:
101  ba::streambuf istreamBuffer;
102  std::istream istream;
103 
104  ba::streambuf ostreamBuffer;
105  std::ostream ostream;
106 
107 protected:
108  std::istringstream deviceOutput;
109  std::ostringstream deviceInput;
110 
111  ba::io_service ioService;
112  StreamT stream;
113 
114  std::shared_ptr<Log> log;
115 
119  template<typename Response = void, typename std::enable_if<std::is_void<Response>::value>::type* = nullptr>
120  void sendCommand();
121 
126  template<typename Response, typename std::enable_if<!std::is_void<Response>::value>::type* = nullptr>
127  Response sendCommand();
128 
129 public:
130  typedef StreamT Stream;
131 
132  std::string REQUEST_END = "\n";
133  std::string RESPONSE_END = "\n";
134 
135  bool readResponse = true;
136  bool readCommand = true;
137 
142 
147  StreamController(const std::string& logPath);
148 
153  StreamController(std::iostream& logStream);
154 
158  virtual ~StreamController();
159 
160  virtual inline bool isConnected() const override { return stream.is_open(); }
161 
162  virtual inline std::shared_ptr<ILog> getLog() override { return log; }
163  virtual inline std::shared_ptr<const ILog> getLog() const override { return log; }
164 
165  virtual void setLog(std::shared_ptr<ILog> log) override;
166 
167  virtual std::string sendCommand(const std::string& command) final override;
168 
174  template<typename Response = void, typename Command>
175  Response sendCommand(const Command& command);
176 
183  template<typename Response = void, typename Command, typename... Args>
184  Response sendCommand(const Command& command, const Args& ... params);
185 
192  template<typename Response = void, typename... Args>
193  Response sendFormattedCommand(const std::string& commandFormat, Args... params);
194 
201  template<typename... Args>
202  std::string createFormattedCommand(const std::string& commandFormat, Args... params) const;
203 };
204 
205 template<typename StreamT>
207  istream(&istreamBuffer),
208  ostream(&ostreamBuffer),
209  stream(ioService)
210 {
211 }
212 
213 template<typename StreamT>
215 {
216  if(!logPath.empty())
217  {
218  log.reset(new Log(logPath));
219  }
220 }
221 
222 template<typename StreamT>
224 {
225  this->log.reset(new Log(logStream));
226 }
227 
228 template<typename StreamT>
230 {
231  if(stream.is_open()) stream.close();
232 }
233 
234 template<typename StreamT>
235 void StreamController<StreamT>::setLog(std::shared_ptr<ILog> log)
236 {
237  std::shared_ptr<Log> logPointer = std::dynamic_pointer_cast<Log>(log);
238  this->log.swap(logPointer);
239 }
240 
241 template<typename StreamT>
242 std::string StreamController<StreamT>::sendCommand(const std::string& command)
243 {
244  sendCommand<>(command);
245 
246  std::string response;
247  std::getline(deviceOutput, response, '\0');
248 
249  return response;
250 }
251 
252 template<typename StreamT>
253 template<typename Response, typename Command>
254 Response StreamController<StreamT>::sendCommand(const Command& command)
255 {
256  deviceInput << command;
257  return sendCommand<Response>();
258 }
259 
260 template<typename StreamT>
261 template<typename Response, typename Command, typename... Args>
262 Response StreamController<StreamT>::sendCommand(const Command& command, const Args& ... params)
263 {
264  deviceInput << command << ' ';
265  return sendCommand<Response>(params...);
266 }
267 
268 template<typename StreamT>
269 template<typename Response, typename... Args>
270 Response StreamController<StreamT>::sendFormattedCommand(const std::string& commandFormat, Args... params)
271 {
272  return sendCommand<Response>(createFormattedCommand(commandFormat, params...));
273 }
274 
275 template<typename StreamT>
276 template<typename Response, typename std::enable_if<std::is_void<Response>::value>::type*>
278 {
280 
281  std::string input = deviceInput.str();
282  ostream << input;
283 
284  ba::write(stream, ostreamBuffer);
285  ostream.flush();
286 
287  deviceInput.clear();
288  deviceInput.str("");
289 
290  std::string output;
291  if(readResponse)
292  {
293  ba::read_until(stream, istreamBuffer, RESPONSE_END);
294 
295  if(readCommand)
296  {
297  std::string cmdInput;
298  getLine(istream, cmdInput, REQUEST_END);
299  cmdInput = cmdInput.substr(0, cmdInput.length() - REQUEST_END.length());
300  }
301 
302  getLine(istream, output, RESPONSE_END);
303  deviceOutput.clear();
304  deviceOutput.str(output);
305  }
306 
307  if(log != nullptr) log->write(input, output);
308 }
309 
310 template<typename StreamT>
311 template<typename Response, typename std::enable_if<!std::is_void<Response>::value>::type*>
313 {
314  sendCommand();
315 
316  Response output;
317  deviceOutput >> output;
318 
319  return output;
320 }
321 
322 template<typename StreamT>
323 template<typename... Args>
324 std::string StreamController<StreamT>::createFormattedCommand(const std::string& command, Args... params) const
325 {
326  std::size_t size = std::snprintf(nullptr, 0, command.c_str(), params...) + 1;
327  char *buffer = new char[size];
328  std::snprintf(buffer, size, command.c_str(), params...);
329 
330  std::string result(buffer, buffer + size - 1);
331  delete[] buffer;
332 
333  return result;
334 }
335 
336 }
337 
338 #endif // REGILO_CONTROLLER_HPP
void sendCommand()
Send a command from the device input to the device.
Definition: controller.hpp:277
virtual void setLog(std::shared_ptr< ILog > log) override
Set a Log (it can be shared between more controllers).
Definition: controller.hpp:235
StreamT stream
A stream (TCP, socket, etc.) that is used for read/write operations.
Definition: controller.hpp:112
Definition: controller.hpp:35
std::shared_ptr< Log > log
A log that is connected to the controller.
Definition: controller.hpp:114
bool readResponse
If true the sendCommand method reads a response.
Definition: controller.hpp:135
virtual ~StreamController()
Default destructor.
Definition: controller.hpp:229
std::istringstream deviceOutput
A buffer for the device output.
Definition: controller.hpp:108
virtual std::string getEndpoint() const =0
Get the endpoint of device.
The Log class is a basic log with a simple read/write functionality.
Definition: log.hpp:108
StreamController()
Default constructor.
Definition: controller.hpp:206
The IController interface is used for all controller classes.
Definition: controller.hpp:42
std::string REQUEST_END
A string that the request ends with.
Definition: controller.hpp:132
virtual std::shared_ptr< ILog > getLog()=0
Get the current Log.
virtual std::string sendCommand(const std::string &command)=0
Send a command to the device.
std::string RESPONSE_END
A string that the response ends with.
Definition: controller.hpp:133
virtual bool isConnected() const override
Test if the controller is connected.
Definition: controller.hpp:160
bool readCommand
If true the input command is read from the response at first.
Definition: controller.hpp:136
StreamT Stream
The stream type for this Controller.
Definition: controller.hpp:130
virtual std::shared_ptr< ILog > getLog() override
Get the current Log.
Definition: controller.hpp:162
Response sendFormattedCommand(const std::string &commandFormat, Args...params)
Create a command with the specified parameters (printf formatting is used) and send it to the device...
Definition: controller.hpp:270
std::string createFormattedCommand(const std::string &commandFormat, Args...params) const
Create a command with the specified parameters (printf formatting is used).
Definition: controller.hpp:324
virtual void connect(const std::string &endpoint)=0
Connect the controller to a device.
ba::io_service ioService
The Boost IO service.
Definition: controller.hpp:111
virtual std::shared_ptr< const ILog > getLog() const override
Get the current Log (a const variant).
Definition: controller.hpp:163
std::ostringstream deviceInput
A buffer for the device input.
Definition: controller.hpp:109
virtual ~IController()=default
Default destructor.
The StreamController class is used to communicate with a device.
Definition: controller.hpp:98
virtual void setLog(std::shared_ptr< ILog > log)=0
Set a Log (it can be shared between more controllers).
virtual bool isConnected() const =0
Test if the controller is connected.