forked from drewnoakes/joystick
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserialstream.cpp
215 lines (190 loc) · 6.49 KB
/
serialstream.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
* Author: Terraneo Federico
* Distributed under the Boost Software License, Version 1.0.
*
* v1.01: Fixed a bug regarding reading after a timeout.
*
* v1.00: First release.
*/
#include "serialstream.h"
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using namespace std;
using namespace boost;
using namespace boost::asio;
/**
* Possible outcome of a read. Set by callbacks, read from main code
*/
enum ReadResult
{
resultInProgress,
resultSuccess,
resultError,
resultTimeout
};
//
// class SerialDeviceImpl
//
/**
* Contains all data of the SerialDevice class
*/
class SerialDeviceImpl : private boost::noncopyable
{
public:
/**
* Construct a SerialDeviceImpl from a SerialOptions class
* \param options serial port options
*/
SerialDeviceImpl(const SerialOptions& options);
io_service io; ///< Io service object
serial_port port; ///< Serial port object
deadline_timer timer; ///< Timer for timeout
posix_time::time_duration timeout; ///< Read/write timeout
enum ReadResult result; ///< Used by read with timeout
streamsize bytesTransferred; ///< Used by async read callback
char *readBuffer; ///< Used to hold read data
streamsize readBufferSize; ///< Size of read data buffer
};
SerialDeviceImpl::SerialDeviceImpl(const SerialOptions& options)
: io(), port(io), timer(io), timeout(options.getTimeout()),
result(resultError), bytesTransferred(0), readBuffer(0),
readBufferSize(0)
{
try {
//For this code to work, there should always be a timeout, so the
//request for no timeout is translated into a very long timeout
if(timeout==posix_time::seconds(0)) timeout=posix_time::hours(100000);
port.open(options.getDevice());//Port must be open before setting option
port.set_option(serial_port_base::baud_rate(options.getBaudrate()));
switch(options.getParity())
{
case SerialOptions::odd:
port.set_option(serial_port_base::parity(
serial_port_base::parity::odd));
break;
case SerialOptions::even:
port.set_option(serial_port_base::parity(
serial_port_base::parity::even));
break;
default:
port.set_option(serial_port_base::parity(
serial_port_base::parity::none));
break;
}
port.set_option(serial_port_base::character_size(options.getCsize()));
switch(options.getFlowControl())
{
case SerialOptions::hardware:
port.set_option(serial_port_base::flow_control(
serial_port_base::flow_control::hardware));
break;
case SerialOptions::software:
port.set_option(serial_port_base::flow_control(
serial_port_base::flow_control::software));
break;
default:
port.set_option(serial_port_base::flow_control(
serial_port_base::flow_control::none));
break;
}
switch(options.getStopBits())
{
case SerialOptions::onepointfive:
port.set_option(serial_port_base::stop_bits(
serial_port_base::stop_bits::onepointfive));
break;
case SerialOptions::two:
port.set_option(serial_port_base::stop_bits(
serial_port_base::stop_bits::two));
break;
default:
port.set_option(serial_port_base::stop_bits(
serial_port_base::stop_bits::one));
break;
}
} catch(std::exception& e)
{
throw ios::failure(e.what());
}
}
//
// class SerialDevice
//
SerialDevice::SerialDevice(const SerialOptions& options)
: pImpl(new SerialDeviceImpl(options)) {}
streamsize SerialDevice::read(char *s, streamsize n)
{
pImpl->result=resultInProgress;
pImpl->bytesTransferred=0;
pImpl->readBuffer=s;
pImpl->readBufferSize=n;
pImpl->timer.expires_from_now(pImpl->timeout);
pImpl->timer.async_wait(boost::bind(&SerialDevice::timeoutExpired,this,
boost::asio::placeholders::error));
pImpl->port.async_read_some(buffer(s,n),boost::bind(&SerialDevice::readCompleted,
this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
for(;;)
{
pImpl->io.run_one();
switch(pImpl->result)
{
case resultSuccess:
pImpl->timer.cancel();
return pImpl->bytesTransferred;
case resultTimeout:
pImpl->port.cancel();
throw(TimeoutException("Timeout expired"));
case resultError:
pImpl->port.cancel();
pImpl->timer.cancel();
throw(ios_base::failure("Error while reading"));
default:
//if resultInProgress remain in the loop
break;
}
}
}
streamsize SerialDevice::write(const char *s, streamsize n)
{
try {
asio::write(pImpl->port,asio::buffer(s,n));
} catch(std::exception& e)
{
throw(ios_base::failure(e.what()));
}
return n;
}
void SerialDevice::timeoutExpired(const boost::system::error_code& error)
{
if(!error && pImpl->result==resultInProgress) pImpl->result=resultTimeout;
}
void SerialDevice::readCompleted(const boost::system::error_code& error,
const size_t bytesTransferred)
{
if(!error)
{
pImpl->result=resultSuccess;
pImpl->bytesTransferred=bytesTransferred;
return;
}
//In case a asynchronous operation is cancelled due to a timeout,
//each OS seems to have its way to react.
#ifdef _WIN32
if(error.value()==995) return; //Windows spits out error 995
#elif defined(__APPLE__)
if(error.value()==45)
{
//Bug on OS X, it might be necessary to repeat the setup
//http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
pImpl->port.async_read_some(
asio::buffer(pImpl->readBuffer,pImpl->readBufferSize),
boost::bind(&SerialDevice::readCompleted,this,boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
return;
}
#else //Linux
if(error.value()==125) return; //Linux outputs error 125
#endif
pImpl->result=resultError;
}