Ticket #5339: serial.cpp

File serial.cpp, 11.9 KB (added by wjwwood@…, 12 years ago)
Line 
1#include "serial.h"
2
3/** Completion Conditions **/
4
5class transfer_at_least_ignore_invalid_argument {
6public:
7 typedef bool result_type;
8
9 explicit transfer_at_least_ignore_invalid_argument(std::size_t minimum) : minimum_(minimum) {}
10
11 template <typename Error>
12 bool operator()(const Error& err, std::size_t bytes_transferred) {
13 if(err) {// There is an Error
14 if(err == boost::asio::error::invalid_argument)
15 std::cout << "Invalid Argument Error" << std::endl;
16 if(err == boost::asio::error::operation_aborted) {
17 return 1;
18 }
19 if(err != boost::asio::error::invalid_argument) {// The Error is not invalid argument
20 return 1; // Stop reading
21 }
22 }
23 if(bytes_transferred >= minimum_) {// We have all the bytes we need
24 return 1; // Stop
25 } else {
26 return 0; // Continue
27 }
28 }
29
30private:
31 std::size_t minimum_;
32};
33
34/** Classes for Handshaking control **/
35
36#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
37# define BOOST_ASIO_OPTION_STORAGE DCB
38#else
39# define BOOST_ASIO_OPTION_STORAGE termios
40#endif
41
42class DTRControl {
43public:
44 explicit DTRControl(bool enable = false) : m_enable(enable) {};
45
46 boost::system::error_code store(BOOST_ASIO_OPTION_STORAGE& storage,
47 boost::system::error_code& ec) const
48 {
49 #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
50 if(m_enable)
51 storage.fDtrControl = DTR_CONTROL_ENABLE;
52 else
53 storage.fDtrControl = DTR_CONTROL_DISABLE;
54 #else
55 ec = boost::asio::error::operation_not_supported;
56 ec = boost::system::error_code();
57 #endif
58 return ec;
59 };
60
61 boost::system::error_code load(const BOOST_ASIO_OPTION_STORAGE& storage,
62 boost::system::error_code& ec)
63 {
64 #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
65 if(storage.fDtrControl == DTR_CONTROL_ENABLE)
66 m_enable = true;
67 else
68 m_enable = true;
69 #else
70 #endif
71 return ec;
72 };
73private:
74 bool m_enable;
75};
76
77class RTSControl {
78public:
79 explicit RTSControl(bool enable = false) : m_enable(enable) {};
80 boost::system::error_code store(BOOST_ASIO_OPTION_STORAGE& storage,
81 boost::system::error_code& ec) const
82 {
83 #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
84 if(m_enable)
85 storage.fRtsControl = RTS_CONTROL_ENABLE;
86 else
87 storage.fRtsControl = RTS_CONTROL_DISABLE;
88 #else
89 ec = boost::asio::error::operation_not_supported;
90 ec = boost::system::error_code();
91 #endif
92 return ec;
93 };
94
95 boost::system::error_code load(const BOOST_ASIO_OPTION_STORAGE& storage,
96 boost::system::error_code& ec)
97 {
98 #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
99 if(storage.fRtsControl == RTS_CONTROL_ENABLE)
100 m_enable = true;
101 else
102 m_enable = true;
103 #else
104 #endif
105 return ec;
106 };
107private:
108 bool m_enable;
109};
110
111/** Serial Class Implementation **/
112
113Serial::Serial() {
114 this->init();
115}
116
117Serial::Serial(std::string port,
118 int baudrate,
119 double timeout,
120 int bytesize,
121 int parity,
122 int stopbits,
123 int flowcontrol)
124{
125 // Call default constructor to initialize variables
126 this->init();
127
128 // Write provided settings
129 this->port = port;
130 this->setBaudrate(baudrate);
131 this->setTimeoutMilliseconds(timeout);
132 this->setBytesize(bytesize);
133 this->setParity(parity);
134 this->setStopbits(stopbits);
135 this->setFlowcontrol(flowcontrol);
136
137 // Open the serial port
138 this->open();
139}
140
141void Serial::init() {
142 // Boost asio variables
143 this->work = new boost::asio::io_service::work(this->io_service);
144 this->serial_port = NULL;
145 this->timeout_timer = new boost::asio::deadline_timer(this->io_service);
146
147 // Serial Port settings
148 this->port = "";
149 this->setBaudrate(DEFAULT_BAUDRATE);
150 this->setTimeoutMilliseconds(DEFAULT_TIMEOUT);
151
152 // Private variables
153 this->bytes_read = 0;
154 this->bytes_to_read = 0;
155 this->reading = false;
156}
157
158Serial::~Serial() {
159 this->close();
160}
161
162void Serial::open() {
163 // Make sure the Serial port is not already open.
164 if(this->serial_port != NULL && this->serial_port->is_open()) {
165 throw(SerialPortAlreadyOpenException(this->port.c_str()));
166 }
167
168 // Try to open the serial port
169 try {
170 this->serial_port = new boost::asio::serial_port(this->io_service, this->port);
171
172 this->serial_port->set_option(*this->baudrate);
173 this->serial_port->set_option(*this->flowcontrol);
174 this->serial_port->set_option(*this->parity);
175 this->serial_port->set_option(*this->stopbits);
176 this->serial_port->set_option(*this->bytesize);
177 } catch(std::exception &e) {
178 throw(SerialPortFailedToOpenException(e.what()));
179 this->serial_port = NULL;
180 }
181}
182
183void Serial::close() {
184 // Cancel the current timeout timer and async reads
185 this->timeout_timer->cancel();
186 this->serial_port->cancel();
187 this->serial_port->close();
188}
189
190int Serial::read(char* buffer, int size) {
191 this->reading = true;
192 boost::asio::async_read(*(this->serial_port), boost::asio::buffer(buffer, size), transfer_at_least_ignore_invalid_argument(size),
193 boost::bind(&Serial::read_complete, this,
194 boost::asio::placeholders::error,
195 boost::asio::placeholders::bytes_transferred));
196 timeout_timer->expires_from_now(*this->timeout);
197 timeout_timer->async_wait(boost::bind(&Serial::timeout_callback, this,
198 boost::asio::placeholders::error));
199
200 while(this->reading)
201 this->io_service.run_one();
202
203 this->bytes_to_read = size;
204
205 return this->bytes_read;
206}
207
208std::string Serial::read(int size) {
209 char serial_buffer[size];
210 int bytes_read_ = this->read(serial_buffer, size);
211 return std::string(serial_buffer, (std::size_t)bytes_read_);
212}
213
214void Serial::read_complete(const boost::system::error_code& error, std::size_t bytes_transferred) {
215 if(!error || error != boost::asio::error::operation_aborted) { // If there was no error OR the error wasn't operation aborted (canceled), Cancel the timer
216 this->timeout_timer->cancel(); // will cause timeout_callback to fire with an error
217 }
218
219 this->bytes_read = bytes_transferred;
220
221 this->reading = false;
222}
223
224void Serial::timeout_callback(const boost::system::error_code& error) {
225 if (!error) {
226 // The timeout wasn't canceled, so cancel the async read
227 this->serial_port->cancel();
228 }
229}
230
231int Serial::write(char data[], int length) {
232 return boost::asio::write(*this->serial_port, boost::asio::buffer(data, length), boost::asio::transfer_all());
233}
234
235int Serial::write(std::string data) {
236 char * cstr;
237 cstr = new char[data.size()+1];
238 std::strcpy(cstr, data.c_str());
239 return this->write(cstr, data.length());
240}
241
242void Serial::setRTS(bool level) {
243 this->serial_port->set_option(RTSControl(level));
244}
245
246void Serial::setDTR(bool level) {
247 this->serial_port->set_option(DTRControl(level));
248}
249
250bool Serial::getCTS() {
251 throw(boost::asio::error::operation_not_supported);
252 return false;
253}
254
255bool Serial::getDSR() {
256 throw(boost::asio::error::operation_not_supported);
257 return false;
258}
259
260void Serial::setTimeoutMilliseconds(long timeout) {
261 if(timeout > 0.0) {
262 this->timeout = new boost::posix_time::milliseconds(timeout);
263 } else {
264 this->timeout = NULL;
265 }
266}
267
268long Serial::getTimeoutMilliseconds() {
269 return this->timeout->total_milliseconds();
270}
271
272void Serial::setBaudrate(int baudrate) {
273 this->baudrate = new boost::asio::serial_port_base::baud_rate(baudrate);
274}
275
276int Serial::getBaudrate() {
277 return this->baudrate->value();
278}
279
280void Serial::setBytesize(int bytesize) {
281 switch(bytesize) {
282 case FIVEBITS:
283 this->bytesize = new boost::asio::serial_port_base::character_size(5);
284 break;
285 case SIXBITS:
286 this->bytesize = new boost::asio::serial_port_base::character_size(6);
287 break;
288 case SEVENBITS:
289 this->bytesize = new boost::asio::serial_port_base::character_size(7);
290 break;
291 case EIGHTBITS:
292 this->bytesize = new boost::asio::serial_port_base::character_size(8);
293 break;
294 default:
295 throw(InvalidBytesizeException(bytesize));
296 break;
297 }
298}
299
300int Serial::getBytesize() {
301 return this->bytesize->value();
302}
303
304void Serial::setParity(int parity) {
305 switch(parity) {
306 case PARITY_NONE:
307 this->parity = new boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none);
308 break;
309 case PARITY_ODD:
310 this->parity = new boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd);
311 break;
312 case PARITY_EVEN:
313 this->parity = new boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::even);
314 break;
315 default:
316 throw(InvalidParityException(parity));
317 break;
318 }
319}
320
321int Serial::getParity() {
322 switch(this->parity->value()) {
323 case boost::asio::serial_port_base::parity::none:
324 return PARITY_NONE;
325 case boost::asio::serial_port_base::parity::odd:
326 return PARITY_ODD;
327 case boost::asio::serial_port_base::parity::even:
328 return PARITY_EVEN;
329 }
330 return -1;
331}
332
333void Serial::setStopbits(int stopbits) {
334 switch(stopbits) {
335 case STOPBITS_ONE:
336 this->stopbits = new boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one);
337 break;
338 case STOPBITS_ONE_POINT_FIVE:
339 this->stopbits = new boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::onepointfive);
340 break;
341 case STOPBITS_TWO:
342 this->stopbits = new boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::two);
343 break;
344 default:
345 throw(InvalidStopbitsException(stopbits));
346 break;
347 }
348}
349
350int Serial::getStopbits() {
351 switch(this->parity->value()) {
352 case boost::asio::serial_port_base::stop_bits::one:
353 return STOPBITS_ONE;
354 case boost::asio::serial_port_base::stop_bits::onepointfive:
355 return STOPBITS_ONE_POINT_FIVE;
356 case boost::asio::serial_port_base::stop_bits::two:
357 return STOPBITS_TWO;
358 }
359 return -1;
360}
361
362void Serial::setFlowcontrol(int flowcontrol) {
363 switch(flowcontrol) {
364 case FLOWCONTROL_NONE:
365 this->flowcontrol = new boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none);
366 break;
367 case FLOWCONTROL_SOFTWARE:
368 this->flowcontrol = new boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::software);
369 break;
370 case FLOWCONTROL_HARDWARE:
371 this->flowcontrol = new boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware);
372 break;
373 default:
374 throw(InvalidFlowcontrolException(flowcontrol));
375 break;
376 }
377}
378
379int Serial::getFlowcontrol() {
380 switch(this->parity->value()) {
381 case boost::asio::serial_port_base::flow_control::none:
382 return FLOWCONTROL_NONE;
383 case boost::asio::serial_port_base::flow_control::software:
384 return FLOWCONTROL_SOFTWARE;
385 case boost::asio::serial_port_base::flow_control::hardware:
386 return FLOWCONTROL_HARDWARE;
387 }
388 return -1;
389}