mirror of
https://github.com/wjwwood/serial.git
synced 2026-01-22 11:44:53 +08:00
Merging boostless into master
This commit is contained in:
commit
061c184d00
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,11 +7,11 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.zip
|
||||
*.cc
|
||||
*/files/*
|
||||
*/tmp/*
|
||||
*.hwm*
|
||||
*.cfg
|
||||
*.out
|
||||
.svn
|
||||
build
|
||||
bin
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
find_path(serial_INCLUDE_DIRS serial.h /usr/include/serial "$ENV{NAMER_ROOT}")
|
||||
find_path(serial_INCLUDE_DIRS serial/serial.h /usr/include
|
||||
/usr/local/include "$ENV{NAMER_ROOT}")
|
||||
|
||||
find_library(serial_LIBRARIES serial /usr/lib "$ENV{NAMER_ROOT}")
|
||||
find_library(serial_LIBRARIES serial /usr/lib /usr/local/lib
|
||||
"$ENV{NAMER_ROOT}")
|
||||
|
||||
set(serial_FOUND TRUE)
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
||||
ifdef ROS_ROOT
|
||||
include $(shell rospack find mk)/cmake.mk
|
||||
else
|
||||
include serial.mk
|
||||
include serial.makefile
|
||||
endif
|
||||
|
||||
@ -7,7 +7,6 @@ Coming Soon!
|
||||
## Dependencies
|
||||
|
||||
* CMake, for the build system: http://www.cmake.org/
|
||||
* Boost, for threading: http://www.boost.org/
|
||||
* (Optional) ROS
|
||||
|
||||
## Stand Alone Installation
|
||||
@ -49,7 +48,7 @@ Setup workspace (skip if you already have one):
|
||||
rosws init some_ros_workspace
|
||||
cd some_ros_workspace
|
||||
source setup.bash
|
||||
|
||||
|
||||
Add the rosinstall entry for this stack:
|
||||
|
||||
echo "- git: {local-name: serial, uri: 'https://github.com/wjwwood/serial.git', version: 'master'}" >> .rosinstall
|
||||
|
||||
1716
doc/Doxyfile
Normal file
1716
doc/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
147
examples/serial_example.cc
Normal file
147
examples/serial_example.cc
Normal file
@ -0,0 +1,147 @@
|
||||
/***
|
||||
* This example expects the serial port has a loopback on it.
|
||||
*
|
||||
* Alternatively, you could use an Arduino:
|
||||
*
|
||||
* <pre>
|
||||
* setup() {
|
||||
* Serial.begin(<insert your baudrate here>);
|
||||
* }
|
||||
*
|
||||
* loop() {
|
||||
* if (Serial.available()) {
|
||||
* Serial.write(Serial.read());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
|
||||
// OS Specific sleep
|
||||
#ifdef __WIN32__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "serial/serial.h"
|
||||
|
||||
using std::string;
|
||||
using std::exception;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
void my_sleep(unsigned long milliseconds) {
|
||||
#ifdef __WIN32__
|
||||
Sleep(milliseconds); // 100 ms
|
||||
#else
|
||||
usleep(milliseconds*1000); // 100 ms
|
||||
#endif
|
||||
}
|
||||
|
||||
int run(int argc, char **argv)
|
||||
{
|
||||
if(argc < 3) {
|
||||
cerr << "Usage: test_serial <serial port address> ";
|
||||
cerr << "<baudrate> [test string]" << endl;
|
||||
return 0;
|
||||
}
|
||||
// Argument 1 is the serial port
|
||||
string port(argv[1]);
|
||||
|
||||
// Argument 2 is the baudrate
|
||||
unsigned long baud = 0;
|
||||
sscanf(argv[2], "%lu", &baud);
|
||||
|
||||
// port, baudrate, timeout in milliseconds
|
||||
serial::Serial my_serial(port, baud, 1000);
|
||||
|
||||
cout << "Is the serial port open?";
|
||||
if(my_serial.isOpen())
|
||||
cout << " Yes." << endl;
|
||||
else
|
||||
cout << " No." << endl;
|
||||
|
||||
// Get the Test string
|
||||
int count = 0;
|
||||
string test_string;
|
||||
if (argc == 4) {
|
||||
test_string = argv[3];
|
||||
} else {
|
||||
test_string = "Testing.";
|
||||
}
|
||||
|
||||
// Test the timeout, there should be 1 second between prints
|
||||
cout << "Timeout == 1000ms, asking for 1 more byte than written." << endl;
|
||||
while (count < 10) {
|
||||
size_t bytes_wrote = my_serial.write(test_string);
|
||||
|
||||
string result = my_serial.read(test_string.length()+1);
|
||||
|
||||
cout << "Iteration: " << count << ", Bytes written: ";
|
||||
cout << bytes_wrote << ", Bytes read: ";
|
||||
cout << result.length() << ", String read: " << result << endl;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Test the timeout at 250ms
|
||||
my_serial.setTimeout(250);
|
||||
count = 0;
|
||||
cout << "Timeout == 250ms, asking for 1 more byte than written." << endl;
|
||||
while (count < 10) {
|
||||
size_t bytes_wrote = my_serial.write(test_string);
|
||||
|
||||
string result = my_serial.read(test_string.length()+1);
|
||||
|
||||
cout << "Iteration: " << count << ", Bytes written: ";
|
||||
cout << bytes_wrote << ", Bytes read: ";
|
||||
cout << result.length() << ", String read: " << result << endl;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Test the timeout at 250ms, but asking exactly for what was written
|
||||
count = 0;
|
||||
cout << "Timeout == 250ms, asking for exactly what was written." << endl;
|
||||
while (count < 10) {
|
||||
size_t bytes_wrote = my_serial.write(test_string);
|
||||
|
||||
string result = my_serial.read(test_string.length());
|
||||
|
||||
cout << "Iteration: " << count << ", Bytes written: ";
|
||||
cout << bytes_wrote << ", Bytes read: ";
|
||||
cout << result.length() << ", String read: " << result << endl;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Test the timeout at 250ms, but asking for 1 less than what was written
|
||||
count = 0;
|
||||
cout << "Timeout == 250ms, asking for 1 less than was written." << endl;
|
||||
while (count < 10) {
|
||||
size_t bytes_wrote = my_serial.write(test_string);
|
||||
|
||||
string result = my_serial.read(test_string.length()-1);
|
||||
|
||||
cout << "Iteration: " << count << ", Bytes written: ";
|
||||
cout << bytes_wrote << ", Bytes read: ";
|
||||
cout << result.length() << ", String read: " << result << endl;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
return run(argc, argv);
|
||||
} catch (exception &e) {
|
||||
cerr << "Unhandled Exception: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "serial/serial.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc < 2) {
|
||||
std::cerr << "Usage: test_serial <serial port address>" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
std::string port(argv[1]);
|
||||
|
||||
serial::Serial serial(port, 115200, 250);
|
||||
|
||||
std::cout << "Is the serial port open?";
|
||||
if(serial.isOpen())
|
||||
std::cout << " Yes." << std::endl;
|
||||
else
|
||||
std::cout << " No." << std::endl;
|
||||
|
||||
int count = 0;
|
||||
while (count >= 0) {
|
||||
int bytes_wrote = serial.write("Testing.");
|
||||
std::string result = serial.read(8);
|
||||
if(count % 10 == 0)
|
||||
std::cout << ">" << count << ">" << bytes_wrote << ">" << result << std::endl;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
197
include/serial/impl/unix.h
Normal file
197
include/serial/impl/unix.h
Normal file
@ -0,0 +1,197 @@
|
||||
/*!
|
||||
* \file serial/impl/unix.h
|
||||
* \author William Woodall <wjwwood@gmail.com>
|
||||
* \author John Harrison <ash@greaterthaninfinity.com>
|
||||
* \version 0.1
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 William Woodall, John Harrison
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* \section DESCRIPTION
|
||||
*
|
||||
* This provides a unix based pimpl for the Serial class. This implementation is
|
||||
* based off termios.h and uses select for multiplexing the IO ports.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SERIAL_IMPL_UNIX_H
|
||||
#define SERIAL_IMPL_UNIX_H
|
||||
|
||||
#include "serial/serial.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace serial {
|
||||
|
||||
using std::string;
|
||||
using std::invalid_argument;
|
||||
|
||||
using serial::SerialExecption;
|
||||
using serial::IOException;
|
||||
|
||||
class serial::Serial::SerialImpl {
|
||||
public:
|
||||
SerialImpl (const string &port,
|
||||
unsigned long baudrate,
|
||||
long timeout,
|
||||
bytesize_t bytesize,
|
||||
parity_t parity,
|
||||
stopbits_t stopbits,
|
||||
flowcontrol_t flowcontrol);
|
||||
|
||||
virtual ~SerialImpl ();
|
||||
|
||||
void
|
||||
open ();
|
||||
|
||||
void
|
||||
close ();
|
||||
|
||||
bool
|
||||
isOpen () const;
|
||||
|
||||
size_t
|
||||
available ();
|
||||
|
||||
size_t
|
||||
read (unsigned char* buf, size_t size = 1);
|
||||
|
||||
size_t
|
||||
write (const string &data);
|
||||
|
||||
void
|
||||
flush ();
|
||||
|
||||
void
|
||||
flushInput ();
|
||||
|
||||
void
|
||||
flushOutput ();
|
||||
|
||||
void
|
||||
sendBreak(int duration);
|
||||
|
||||
void
|
||||
setBreak(bool level);
|
||||
|
||||
void
|
||||
setRTS(bool level);
|
||||
|
||||
void
|
||||
setDTR(bool level);
|
||||
|
||||
bool
|
||||
getCTS();
|
||||
|
||||
bool
|
||||
getDSR();
|
||||
|
||||
bool
|
||||
getRI();
|
||||
|
||||
bool
|
||||
getCD();
|
||||
|
||||
void
|
||||
setPort (const string &port);
|
||||
|
||||
string
|
||||
getPort () const;
|
||||
|
||||
void
|
||||
setTimeout (long timeout);
|
||||
|
||||
long
|
||||
getTimeout () const;
|
||||
|
||||
void
|
||||
setBaudrate (unsigned long baudrate);
|
||||
|
||||
unsigned long
|
||||
getBaudrate () const;
|
||||
|
||||
void
|
||||
setBytesize (bytesize_t bytesize);
|
||||
|
||||
bytesize_t
|
||||
getBytesize () const;
|
||||
|
||||
void
|
||||
setParity (parity_t parity);
|
||||
|
||||
parity_t
|
||||
getParity () const;
|
||||
|
||||
void
|
||||
setStopbits (stopbits_t stopbits);
|
||||
|
||||
stopbits_t
|
||||
getStopbits () const;
|
||||
|
||||
void
|
||||
setFlowcontrol (flowcontrol_t flowcontrol);
|
||||
|
||||
flowcontrol_t
|
||||
getFlowcontrol () const;
|
||||
|
||||
void
|
||||
readLock();
|
||||
|
||||
void
|
||||
readUnlock();
|
||||
|
||||
void
|
||||
writeLock();
|
||||
|
||||
void
|
||||
writeUnlock();
|
||||
|
||||
protected:
|
||||
void reconfigurePort ();
|
||||
|
||||
private:
|
||||
string port_; // Path to the file descriptor
|
||||
int fd_; // The current file descriptor
|
||||
|
||||
bool is_open_;
|
||||
bool xonxoff_;
|
||||
bool rtscts_;
|
||||
|
||||
long timeout_; // Timeout for read operations
|
||||
unsigned long baudrate_; // Baudrate
|
||||
|
||||
parity_t parity_; // Parity
|
||||
bytesize_t bytesize_; // Size of the bytes
|
||||
stopbits_t stopbits_; // Stop Bits
|
||||
flowcontrol_t flowcontrol_; // Flow Control
|
||||
|
||||
// Mutex used to lock the read functions
|
||||
pthread_mutex_t read_mutex;
|
||||
// Mutex used to lock the write functions
|
||||
pthread_mutex_t write_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIAL_IMPL_UNIX_H
|
||||
177
include/serial/impl/windows.h
Normal file
177
include/serial/impl/windows.h
Normal file
@ -0,0 +1,177 @@
|
||||
/*!
|
||||
* \file serial/impl/windows.h
|
||||
* \author William Woodall <wjwwood@gmail.com>
|
||||
* \author John Harrison <ash@greaterthaninfinity.com>
|
||||
* \version 0.1
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 William Woodall, John Harrison
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* \section DESCRIPTION
|
||||
*
|
||||
* This provides a windows implementation of the Serial class interface.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SERIAL_IMPL_WINDOWS_H
|
||||
#define SERIAL_IMPL_WINDOWS_H
|
||||
|
||||
#include "serial/serial.h"
|
||||
|
||||
namespace serial {
|
||||
|
||||
using std::string;
|
||||
using std::invalid_argument;
|
||||
|
||||
using serial::SerialExecption;
|
||||
using serial::IOException;
|
||||
|
||||
class serial::Serial::SerialImpl {
|
||||
public:
|
||||
SerialImpl (const string &port,
|
||||
unsigned long baudrate,
|
||||
long timeout,
|
||||
bytesize_t bytesize,
|
||||
parity_t parity,
|
||||
stopbits_t stopbits,
|
||||
flowcontrol_t flowcontrol);
|
||||
|
||||
virtual ~SerialImpl ();
|
||||
|
||||
void
|
||||
open ();
|
||||
|
||||
void
|
||||
close ();
|
||||
|
||||
bool
|
||||
isOpen () const;
|
||||
|
||||
size_t
|
||||
available ();
|
||||
|
||||
size_t
|
||||
read (char* buf, size_t size = 1);
|
||||
|
||||
size_t
|
||||
write (const string &data);
|
||||
|
||||
void
|
||||
flush ();
|
||||
|
||||
void
|
||||
flushInput ();
|
||||
|
||||
void
|
||||
flushOutput ();
|
||||
|
||||
void
|
||||
sendBreak(int duration);
|
||||
|
||||
void
|
||||
setBreak(bool level);
|
||||
|
||||
void
|
||||
setRTS(bool level);
|
||||
|
||||
void
|
||||
setDTR(bool level);
|
||||
|
||||
bool
|
||||
getCTS();
|
||||
|
||||
bool
|
||||
getDSR();
|
||||
|
||||
bool
|
||||
getRI();
|
||||
|
||||
bool
|
||||
getCD();
|
||||
|
||||
void
|
||||
setPort (const string &port);
|
||||
|
||||
string
|
||||
getPort () const;
|
||||
|
||||
void
|
||||
setTimeout (long timeout);
|
||||
|
||||
long
|
||||
getTimeout () const;
|
||||
|
||||
void
|
||||
setBaudrate (unsigned long baudrate);
|
||||
|
||||
unsigned long
|
||||
getBaudrate () const;
|
||||
|
||||
void
|
||||
setBytesize (bytesize_t bytesize);
|
||||
|
||||
bytesize_t
|
||||
getBytesize () const;
|
||||
|
||||
void
|
||||
setParity (parity_t parity);
|
||||
|
||||
parity_t
|
||||
getParity () const;
|
||||
|
||||
void
|
||||
setStopbits (stopbits_t stopbits);
|
||||
|
||||
stopbits_t
|
||||
getStopbits () const;
|
||||
|
||||
void
|
||||
setFlowcontrol (flowcontrol_t flowcontrol);
|
||||
|
||||
flowcontrol_t
|
||||
getFlowcontrol () const;
|
||||
|
||||
protected:
|
||||
void reconfigurePort ();
|
||||
|
||||
private:
|
||||
string port_; // Path to the file descriptor
|
||||
int fd_; // The current file descriptor
|
||||
|
||||
bool isOpen_;
|
||||
bool xonxoff_;
|
||||
bool rtscts_;
|
||||
|
||||
long timeout_; // Timeout for read operations
|
||||
unsigned long baudrate_; // Baudrate
|
||||
|
||||
parity_t parity_; // Parity
|
||||
bytesize_t bytesize_; // Size of the bytes
|
||||
stopbits_t stopbits_; // Stop Bits
|
||||
flowcontrol_t flowcontrol_; // Flow Control
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIAL_IMPL_WINDOWS_H
|
||||
@ -1,21 +1,21 @@
|
||||
/**
|
||||
* @file serial.h
|
||||
* @author William Woodall <wjwwood@gmail.com>
|
||||
* @author John Harrison <ash.gti@gmail.com>
|
||||
* @version 0.1
|
||||
/*!
|
||||
* \file serial/serial.h
|
||||
* \author William Woodall <wjwwood@gmail.com>
|
||||
* \author John Harrison <ash.gti@gmail.com>
|
||||
* \version 0.1
|
||||
*
|
||||
* @section LICENSE
|
||||
* \section LICENSE
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 William Woodall
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
@ -24,425 +24,475 @@
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
* \section DESCRIPTION
|
||||
*
|
||||
* This provides a cross platform interface for interacting with Serial Ports.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
// If on Windows undefine the PARITY_* defines that are in winbase.h
|
||||
#ifdef _WIN32
|
||||
#undef PARITY_NONE
|
||||
#undef PARITY_ODD
|
||||
#undef PARITY_EVEN
|
||||
#endif
|
||||
|
||||
// DEFINES
|
||||
#ifndef DEFAULT_BAUDRATE
|
||||
#define DEFAULT_BAUDRATE 9600
|
||||
#endif
|
||||
#ifndef DEFAULT_TIMEOUT
|
||||
#define DEFAULT_TIMEOUT 0
|
||||
#endif
|
||||
#ifndef DEFAULT_BYTESIZE
|
||||
#define DEFAULT_BYTESIZE EIGHTBITS
|
||||
#endif
|
||||
#ifndef DEFAULT_PARITY
|
||||
#define DEFAULT_PARITY PARITY_NONE
|
||||
#endif
|
||||
#ifndef DEFAULT_STOPBITS
|
||||
#define DEFAULT_STOPBITS STOPBITS_ONE
|
||||
#endif
|
||||
#ifndef DEFAULT_FLOWCONTROL
|
||||
#define DEFAULT_FLOWCONTROL FLOWCONTROL_NONE
|
||||
#endif
|
||||
#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )
|
||||
|
||||
namespace serial {
|
||||
|
||||
// Serial Port settings CONSTANTS
|
||||
enum bytesize_t { FIVEBITS = 5, SIXBITS = 6, SEVENBITS = 7, EIGHTBITS = 8 };
|
||||
enum parity_t { PARITY_NONE, PARITY_ODD, PARITY_EVEN };
|
||||
enum stopbits_t { STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO };
|
||||
enum flowcontrol_t { FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE };
|
||||
/*!
|
||||
* Enumeration defines the possible bytesizes for the serial port.
|
||||
*/
|
||||
typedef enum {
|
||||
fivebits = 5,
|
||||
sixbits = 6,
|
||||
sevenbits = 7,
|
||||
eightbits = 8
|
||||
} bytesize_t;
|
||||
|
||||
/*!
|
||||
* Enumeration defines the possible parity types for the serial port.
|
||||
*/
|
||||
typedef enum {
|
||||
parity_none = 0,
|
||||
parity_odd = 1,
|
||||
parity_even = 2
|
||||
} parity_t;
|
||||
|
||||
/*!
|
||||
* Enumeration defines the possible stopbit types for the serial port.
|
||||
*/
|
||||
typedef enum {
|
||||
stopbits_one = 1,
|
||||
stopbits_one_point_five,
|
||||
stopbits_two = 2
|
||||
} stopbits_t;
|
||||
|
||||
/*!
|
||||
* Enumeration defines the possible flowcontrol types for the serial port.
|
||||
*/
|
||||
typedef enum {
|
||||
flowcontrol_none = 0,
|
||||
flowcontrol_software
|
||||
} flowcontrol_t;
|
||||
|
||||
/*!
|
||||
* Class that provides a portable serial port interface.
|
||||
*/
|
||||
class Serial {
|
||||
public:
|
||||
/** Constructor, Creates a Serial object but doesn't open the serial port. */
|
||||
Serial();
|
||||
|
||||
/**
|
||||
* Constructor, creates a SerialPortBoost object and opens the port.
|
||||
*
|
||||
* @param port A std::string containing the address of the serial port,
|
||||
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
|
||||
* on Linux.
|
||||
*
|
||||
* @param baudrate An integer that represents the buadrate
|
||||
*
|
||||
* @param timeout A long that represents the time (in milliseconds) until a
|
||||
* timeout on reads occur. Setting this to zero (0) will cause reading
|
||||
* to be non-blocking, i.e. the available data will be returned immediately,
|
||||
* but it will not block to wait for more. Setting this to a number less than
|
||||
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port will
|
||||
* block until either size bytes have been read or an exception has occured.
|
||||
*
|
||||
* @param bytesize Size of each byte in the serial transmission of data,
|
||||
* default is EIGHTBITS, possible values are: FIVEBITS,
|
||||
* SIXBITS, SEVENBITS, EIGHTBITS
|
||||
*
|
||||
* @param parity Method of parity, default is PARITY_NONE, possible values
|
||||
* are: PARITY_NONE, PARITY_ODD, PARITY_EVEN
|
||||
*
|
||||
* @param stopbits Number of stop bits used, default is STOPBITS_ONE, possible
|
||||
* values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
|
||||
*
|
||||
* @param flowcontrol Type of flowcontrol used, default is FLOWCONTROL_NONE, possible
|
||||
* values are: FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE
|
||||
*
|
||||
* @throw SerialPortAlreadyOpenException
|
||||
* @throw SerialPortFailedToOpenException
|
||||
*/
|
||||
Serial(std::string port,
|
||||
int baudrate = DEFAULT_BAUDRATE,
|
||||
long timeout = DEFAULT_TIMEOUT,
|
||||
bytesize_t bytesize = DEFAULT_BYTESIZE,
|
||||
parity_t parity = DEFAULT_PARITY,
|
||||
stopbits_t stopbits = DEFAULT_STOPBITS,
|
||||
flowcontrol_t flowcontrol = DEFAULT_FLOWCONTROL);
|
||||
|
||||
/** Destructor */
|
||||
~Serial();
|
||||
|
||||
/**
|
||||
* Opens the serial port as long as the portname is set and the port isn't alreay open.
|
||||
*
|
||||
* @throw SerialPortAlreadyOpenException
|
||||
* @throw SerialPortFailedToOpenException
|
||||
*/
|
||||
void open();
|
||||
|
||||
/** Gets the status of the serial port.
|
||||
*
|
||||
* @return A boolean value that represents whether or not the serial port is open.
|
||||
*/
|
||||
bool isOpen();
|
||||
|
||||
/** Closes the serial port and terminates threads. */
|
||||
void close();
|
||||
|
||||
/** Read size bytes from the serial port.
|
||||
* If a timeout is set it may return less characters than requested. With no timeout
|
||||
* it will block until the requested number of bytes have been read.
|
||||
*
|
||||
* @param buffer A char[] of length >= the size parameter to hold incoming data.
|
||||
*
|
||||
* @param size An integer defining how many bytes to be read.
|
||||
*
|
||||
* @return An integer representing the number of bytes read.
|
||||
*/
|
||||
int read(char* buffer, int size = 1);
|
||||
|
||||
/** Read size bytes from the serial port.
|
||||
* If a timeout is set it may return less characters than requested. With no timeout
|
||||
* it will block until the requested number of bytes have been read.
|
||||
*
|
||||
* @param size An integer defining how many bytes to be read.
|
||||
*
|
||||
* @return A std::string containing the data read.
|
||||
*/
|
||||
std::string read(int size = 1);
|
||||
|
||||
std::string read_until(char delim, size_t size = -1);
|
||||
std::string read_until(std::string delim, size_t size = -1);
|
||||
|
||||
/** Write length bytes from buffer to the serial port.
|
||||
*
|
||||
* @param data A char[] with data to be written to the serial port.
|
||||
*
|
||||
* @param length An integer representing the number of bytes to be written.
|
||||
*
|
||||
* @return An integer representing the number of bytes written.
|
||||
*/
|
||||
int write(char data[], int length);
|
||||
|
||||
/** Write a string to the serial port.
|
||||
*
|
||||
* @param data A std::string to be written to the serial port. (must be null terminated)
|
||||
*
|
||||
* @return An integer representing the number of bytes written to the serial port.
|
||||
*/
|
||||
int write(std::string data);
|
||||
|
||||
/** Sets the logic level of the RTS line.
|
||||
*
|
||||
* @param level The logic level to set the RTS to. Defaults to true.
|
||||
*/
|
||||
void setRTS(bool level = true);
|
||||
|
||||
/** Sets the logic level of the DTR line.
|
||||
*
|
||||
* @param level The logic level to set the DTR to. Defaults to true.
|
||||
*/
|
||||
void setDTR(bool level = true);
|
||||
|
||||
/** Gets the status of the CTS line.
|
||||
*
|
||||
* @return A boolean value that represents the current logic level of the CTS line.
|
||||
*/
|
||||
bool getCTS() const;
|
||||
|
||||
/** Gets the status of the DSR line.
|
||||
*
|
||||
* @return A boolean value that represents the current logic level of the DSR line.
|
||||
*/
|
||||
bool getDSR() const;
|
||||
|
||||
/** Sets the serial port identifier.
|
||||
*
|
||||
* @param port A std::string containing the address of the serial port,
|
||||
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
|
||||
* on Linux.
|
||||
*/
|
||||
void setPort(std::string port);
|
||||
|
||||
/** Gets the serial port identifier.
|
||||
*
|
||||
* @return A std::string containing the address of the serial port,
|
||||
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
|
||||
* on Linux.
|
||||
*/
|
||||
std::string getPort() const;
|
||||
|
||||
/** Sets the timeout for reads in seconds.
|
||||
*
|
||||
* @param timeout A long that represents the time (in milliseconds) until a
|
||||
* timeout on reads occur. Setting this to zero (0) will cause reading
|
||||
* to be non-blocking, i.e. the available data will be returned immediately,
|
||||
* but it will not block to wait for more. Setting this to a number less than
|
||||
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port will
|
||||
* block until either size bytes have been read or an exception has occured.
|
||||
*/
|
||||
void setTimeoutMilliseconds(long timeout);
|
||||
|
||||
/** Gets the timeout for reads in seconds.
|
||||
*
|
||||
* @param timeout A long that represents the time (in milliseconds) until a
|
||||
* timeout on reads occur. Setting this to zero (0) will cause reading
|
||||
* to be non-blocking, i.e. the available data will be returned immediately,
|
||||
* but it will not block to wait for more. Setting this to a number less than
|
||||
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port will
|
||||
* block until either size bytes have been read or an exception has occured.
|
||||
*/
|
||||
long getTimeoutMilliseconds() const;
|
||||
|
||||
/** Sets the baudrate for the serial port.
|
||||
*
|
||||
* @param baudrate An integer that sets the baud rate for the serial port.
|
||||
*/
|
||||
void setBaudrate(int baudrate);
|
||||
|
||||
/** Gets the baudrate for the serial port.
|
||||
*
|
||||
* @return An integer that sets the baud rate for the serial port.
|
||||
*/
|
||||
int getBaudrate() const;
|
||||
|
||||
/** Sets the bytesize for the serial port.
|
||||
*
|
||||
* @param bytesize Size of each byte in the serial transmission of data,
|
||||
* default is EIGHTBITS, possible values are: FIVEBITS,
|
||||
* SIXBITS, SEVENBITS, EIGHTBITS
|
||||
*
|
||||
* @throw InvalidBytesizeException
|
||||
*/
|
||||
void setBytesize(bytesize_t bytesize);
|
||||
|
||||
/** Gets the bytesize for the serial port.
|
||||
*
|
||||
* @return Size of each byte in the serial transmission of data,
|
||||
* default is EIGHTBITS, possible values are: FIVEBITS,
|
||||
* SIXBITS, SEVENBITS, EIGHTBITS
|
||||
*
|
||||
* @throw InvalidBytesizeException
|
||||
*/
|
||||
bytesize_t getBytesize() const;
|
||||
|
||||
/** Sets the parity for the serial port.
|
||||
*
|
||||
* @param parity Method of parity, default is PARITY_NONE, possible values
|
||||
* are: PARITY_NONE, PARITY_ODD, PARITY_EVEN
|
||||
*
|
||||
* @throw InvalidParityException
|
||||
*/
|
||||
void setParity(parity_t parity);
|
||||
|
||||
/** Gets the parity for the serial port.
|
||||
*
|
||||
* @return Method of parity, default is PARITY_NONE, possible values
|
||||
* are: PARITY_NONE, PARITY_ODD, PARITY_EVEN
|
||||
*
|
||||
* @throw InvalidParityException
|
||||
*/
|
||||
parity_t getParity() const;
|
||||
|
||||
/** Sets the stopbits for the serial port.
|
||||
*
|
||||
* @param stopbits Number of stop bits used, default is STOPBITS_ONE, possible
|
||||
* values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
|
||||
*
|
||||
* @throw InvalidStopbitsException
|
||||
*/
|
||||
void setStopbits(stopbits_t stopbits);
|
||||
|
||||
/** Gets the stopbits for the serial port.
|
||||
*
|
||||
* @return Number of stop bits used, default is STOPBITS_ONE, possible
|
||||
* values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
|
||||
*
|
||||
* @throw InvalidStopbitsException
|
||||
*/
|
||||
stopbits_t getStopbits() const;
|
||||
|
||||
/** Sets the flow control for the serial port.
|
||||
*
|
||||
* @param flowcontrol Type of flowcontrol used, default is FLOWCONTROL_NONE, possible
|
||||
* values are: FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE
|
||||
*
|
||||
* @throw InvalidFlowcontrolException
|
||||
*/
|
||||
void setFlowcontrol(flowcontrol_t flowcontrol);
|
||||
|
||||
/** Gets the flow control for the serial port.
|
||||
*
|
||||
* @return Type of flowcontrol used, default is FLOWCONTROL_NONE, possible
|
||||
* values are: FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE
|
||||
*
|
||||
* @throw InvalidFlowcontrolException
|
||||
*/
|
||||
flowcontrol_t getFlowcontrol() const;
|
||||
/*!
|
||||
* Constructor, creates a SerialPortBoost object and opens the port.
|
||||
*
|
||||
* \param port A std::string containing the address of the serial port,
|
||||
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
|
||||
* on Linux.
|
||||
*
|
||||
* \param baudrate An integer that represents the buadrate
|
||||
*
|
||||
* \param timeout A long that represents the time (in milliseconds) until a
|
||||
* timeout on reads occur. Setting this to zero (0) will cause reading to
|
||||
* be non-blocking, i.e. the available data will be returned immediately,
|
||||
* but it will not block to wait for more. Setting this to a number less
|
||||
* than zero (-1) will result in infinite blocking behaviour, i.e. the
|
||||
* serial port will block until either size bytes have been read or an
|
||||
* exception has occured.
|
||||
*
|
||||
* \param bytesize Size of each byte in the serial transmission of data,
|
||||
* default is eightbits, possible values are: fivebits, sixbits, sevenbits,
|
||||
* eightbits
|
||||
*
|
||||
* \param parity Method of parity, default is parity_none, possible values
|
||||
* are: parity_none, parity_odd, parity_even
|
||||
*
|
||||
* \param stopbits Number of stop bits used, default is stopbits_one,
|
||||
* possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
|
||||
*
|
||||
* \param flowcontrol Type of flowcontrol used, default is
|
||||
* flowcontrol_none, possible values are: flowcontrol_none,
|
||||
* flowcontrol_software, flowcontrol_hardware
|
||||
*
|
||||
* \param buffer_size The maximum size of the internal buffer, defaults
|
||||
* to 256 bytes (2^8).
|
||||
*
|
||||
* \throw PortNotOpenedException
|
||||
*/
|
||||
Serial (const std::string &port = "",
|
||||
unsigned long baudrate = 9600,
|
||||
long timeout = 0,
|
||||
bytesize_t bytesize = eightbits,
|
||||
parity_t parity = parity_none,
|
||||
stopbits_t stopbits = stopbits_one,
|
||||
flowcontrol_t flowcontrol = flowcontrol_none);
|
||||
|
||||
/*! Destructor */
|
||||
virtual ~Serial ();
|
||||
|
||||
/*!
|
||||
* Opens the serial port as long as the portname is set and the port isn't
|
||||
* alreay open.
|
||||
*
|
||||
* If the port is provided to the constructor then an explicit call to open
|
||||
* is not needed.
|
||||
*
|
||||
* \see Serial::Serial
|
||||
*
|
||||
* \throw std::invalid_argument
|
||||
* \throw serial::SerialExecption
|
||||
* \throw serial::IOException
|
||||
*/
|
||||
void
|
||||
open ();
|
||||
|
||||
/*! Gets the open status of the serial port.
|
||||
*
|
||||
* \return Returns true if the port is open, false otherwise.
|
||||
*/
|
||||
bool
|
||||
isOpen () const;
|
||||
|
||||
/*! Closes the serial port. */
|
||||
void
|
||||
close ();
|
||||
|
||||
/*! Return the number of characters in the buffer. */
|
||||
size_t
|
||||
available();
|
||||
|
||||
/*! Read a given amount of bytes from the serial port.
|
||||
*
|
||||
* If a timeout is set it may return less characters than requested. With
|
||||
* no timeout it will block until the requested number of bytes have been
|
||||
* read or until an exception occurs.
|
||||
*
|
||||
* \param size A size_t defining how many bytes to be read.
|
||||
*
|
||||
* \return A std::string containing the data read.
|
||||
*/
|
||||
size_t
|
||||
read (unsigned char *buffer, size_t size);
|
||||
size_t
|
||||
read (std::vector<unsigned char> &buffer, size_t size = 1);
|
||||
size_t
|
||||
read (std::string &buffer, size_t size = 1);
|
||||
std::string
|
||||
read (size_t size = 1);
|
||||
|
||||
/*! Reads in a line or until a given delimiter has been processed
|
||||
*
|
||||
* Reads from the serial port until a single line has been read.
|
||||
*
|
||||
* \param size A maximum length of a line, defaults to 65536 (2^16)
|
||||
* \param eol A string to match against for the EOL.
|
||||
*
|
||||
* \return A std::string containing the line.
|
||||
*/
|
||||
size_t
|
||||
readline (std::string &buffer,
|
||||
size_t size = 65536,
|
||||
std::string eol = "\n");
|
||||
std::string
|
||||
readline (size_t size = 65536,
|
||||
std::string eol = "\n");
|
||||
|
||||
/*! Reads in multiple lines until the serail port times out.
|
||||
*
|
||||
* This requires a timeout > 0 before it can be run. It will read until a
|
||||
* timeout occurs and return a list of strings.
|
||||
*
|
||||
* \param size A maximum length of combined lines, defaults to 65536 (2^16)
|
||||
*
|
||||
* \param eol A string to match against for the EOL.
|
||||
*
|
||||
* \return A vector<string> containing the lines.
|
||||
*/
|
||||
std::vector<std::string>
|
||||
readlines (size_t size = 65536, std::string eol = "\n");
|
||||
|
||||
/*! Write a string to the serial port.
|
||||
*
|
||||
* \param data A const reference containg the data to be written
|
||||
* to the serial port.
|
||||
*
|
||||
* \return A size_t representing the number of bytes actually written to
|
||||
* the serial port.
|
||||
*/
|
||||
size_t
|
||||
write (const unsigned char *data, size_t size);
|
||||
size_t
|
||||
write (const std::vector<unsigned char> &data);
|
||||
size_t
|
||||
write (const std::string &data);
|
||||
|
||||
/*! Sets the serial port identifier.
|
||||
*
|
||||
* \param port A const std::string reference containing the address of the
|
||||
* serial port, which would be something like 'COM1' on Windows and
|
||||
* '/dev/ttyS0' on Linux.
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
void
|
||||
setPort (const std::string &port);
|
||||
|
||||
/*! Gets the serial port identifier.
|
||||
*
|
||||
* \see Serial::setPort
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
std::string
|
||||
getPort () const;
|
||||
|
||||
/*! Sets the timeout for reads in milliseconds.
|
||||
*
|
||||
* \param timeout A long that represents the time (in milliseconds) until a
|
||||
* timeout on reads occur. Setting this to zero (0) will cause reading to be
|
||||
* non-blocking, i.e. the available data will be returned immediately, but it
|
||||
* will not block to wait for more. Setting this to a number less than
|
||||
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port
|
||||
* will block until either size bytes have been read or an exception has
|
||||
* occured.
|
||||
*/
|
||||
void
|
||||
setTimeout (long timeout);
|
||||
|
||||
/*! Gets the timeout for reads in seconds.
|
||||
*
|
||||
* \see Serial::setTimeout
|
||||
*/
|
||||
long
|
||||
getTimeout () const;
|
||||
|
||||
/*! Sets the baudrate for the serial port.
|
||||
*
|
||||
* Possible baudrates depends on the system but some safe baudrates include:
|
||||
* 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000,
|
||||
* 57600, 115200
|
||||
* Some other baudrates that are supported by some comports:
|
||||
* 128000, 153600, 230400, 256000, 460800, 921600
|
||||
*
|
||||
* \param baudrate An integer that sets the baud rate for the serial port.
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
void
|
||||
setBaudrate (unsigned long baudrate);
|
||||
|
||||
/*! Gets the baudrate for the serial port.
|
||||
*
|
||||
* \return An integer that sets the baud rate for the serial port.
|
||||
*
|
||||
* \see Serial::setBaudrate
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
unsigned long
|
||||
getBaudrate () const;
|
||||
|
||||
/*! Sets the bytesize for the serial port.
|
||||
*
|
||||
* \param bytesize Size of each byte in the serial transmission of data,
|
||||
* default is eightbits, possible values are: fivebits, sixbits, sevenbits,
|
||||
* eightbits
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
void
|
||||
setBytesize (bytesize_t bytesize);
|
||||
|
||||
/*! Gets the bytesize for the serial port.
|
||||
*
|
||||
* \see Serial::setBytesize
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
bytesize_t
|
||||
getBytesize () const;
|
||||
|
||||
/*! Sets the parity for the serial port.
|
||||
*
|
||||
* \param parity Method of parity, default is parity_none, possible values
|
||||
* are: parity_none, parity_odd, parity_even
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
void
|
||||
setParity (parity_t parity);
|
||||
|
||||
/*! Gets the parity for the serial port.
|
||||
*
|
||||
* \see Serial::setParity
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
parity_t
|
||||
getParity () const;
|
||||
|
||||
/*! Sets the stopbits for the serial port.
|
||||
*
|
||||
* \param stopbits Number of stop bits used, default is stopbits_one,
|
||||
* possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
void
|
||||
setStopbits (stopbits_t stopbits);
|
||||
|
||||
/*! Gets the stopbits for the serial port.
|
||||
*
|
||||
* \see Serial::setStopbits
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
stopbits_t
|
||||
getStopbits () const;
|
||||
|
||||
/*! Sets the flow control for the serial port.
|
||||
*
|
||||
* \param flowcontrol Type of flowcontrol used, default is flowcontrol_none,
|
||||
* possible values are: flowcontrol_none, flowcontrol_software,
|
||||
* flowcontrol_hardware
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
void
|
||||
setFlowcontrol (flowcontrol_t flowcontrol);
|
||||
|
||||
/*! Gets the flow control for the serial port.
|
||||
*
|
||||
* \see Serial::setFlowcontrol
|
||||
*
|
||||
* \throw InvalidConfigurationException
|
||||
*/
|
||||
flowcontrol_t
|
||||
getFlowcontrol () const;
|
||||
|
||||
/*! Flush the input and output buffers */
|
||||
void
|
||||
flush ();
|
||||
|
||||
/*! Flush only the input buffer */
|
||||
void
|
||||
flushInput ();
|
||||
|
||||
/*! Flush only the output buffer */
|
||||
void
|
||||
flushOutput ();
|
||||
|
||||
void
|
||||
sendBreak (int duration);
|
||||
|
||||
void
|
||||
setBreak (bool level = true);
|
||||
|
||||
void
|
||||
setRTS (bool level = true);
|
||||
|
||||
void
|
||||
setDTR (bool level = true);
|
||||
|
||||
bool
|
||||
getCTS ();
|
||||
|
||||
bool
|
||||
getDSR ();
|
||||
|
||||
bool
|
||||
getRI ();
|
||||
|
||||
bool
|
||||
getCD ();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Serial);
|
||||
void init();
|
||||
void read_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
|
||||
void timeout_callback(const boost::system::error_code& error);
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
boost::asio::io_service::work work;
|
||||
|
||||
boost::scoped_ptr<boost::asio::serial_port> serial_port;
|
||||
|
||||
boost::asio::deadline_timer timeout_timer;
|
||||
|
||||
std::string port;
|
||||
boost::asio::serial_port_base::baud_rate baudrate;
|
||||
boost::posix_time::time_duration timeout;
|
||||
boost::asio::serial_port_base::character_size bytesize;
|
||||
boost::asio::serial_port_base::parity parity;
|
||||
boost::asio::serial_port_base::stop_bits stopbits;
|
||||
boost::asio::serial_port_base::flow_control flowcontrol;
|
||||
|
||||
int bytes_read;
|
||||
int bytes_to_read;
|
||||
bool reading;
|
||||
bool nonblocking;
|
||||
// Disable copy constructors
|
||||
Serial(const Serial&);
|
||||
void operator=(const Serial&);
|
||||
const Serial& operator=(Serial);
|
||||
|
||||
std::string read_cache_; //!< Cache for doing reads in chunks.
|
||||
|
||||
// Pimpl idiom, d_pointer
|
||||
class SerialImpl;
|
||||
SerialImpl *pimpl_;
|
||||
|
||||
// Scoped Lock Classes
|
||||
class ScopedReadLock;
|
||||
class ScopedWriteLock;
|
||||
|
||||
// Read common function
|
||||
size_t
|
||||
read_ (unsigned char *buffer, size_t size);
|
||||
|
||||
};
|
||||
|
||||
class SerialPortAlreadyOpenException : public std::exception {
|
||||
const char * port;
|
||||
class SerialExecption : public std::exception
|
||||
{
|
||||
const char* e_what_;
|
||||
public:
|
||||
SerialPortAlreadyOpenException(const char * port) {this->port = port;}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "Serial Port already open: " << this->port;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
SerialExecption (const char *description) : e_what_ (description) {}
|
||||
|
||||
virtual const char* what () const throw ()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SerialException " << e_what_ << " failed.";
|
||||
return ss.str ().c_str ();
|
||||
}
|
||||
};
|
||||
|
||||
class SerialPortFailedToOpenException : public std::exception {
|
||||
const char * e_what;
|
||||
class IOException : public std::exception
|
||||
{
|
||||
std::string file_;
|
||||
int line_;
|
||||
const char* e_what_;
|
||||
int errno_;
|
||||
public:
|
||||
SerialPortFailedToOpenException(const char * e_what) {this->e_what = e_what;}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "Serial Port failed to open: " << this->e_what;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
explicit IOException (std::string file, int line, int errnum)
|
||||
: file_(file), line_(line), e_what_ (strerror (errnum)), errno_(errnum) {}
|
||||
explicit IOException (std::string file, int line, const char * description)
|
||||
: file_(file), line_(line), e_what_ (description), errno_(0) {}
|
||||
virtual ~IOException() throw() {}
|
||||
|
||||
int getErrorNumber () { return errno_; }
|
||||
|
||||
virtual const char* what () const throw ()
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (errno_ == 0)
|
||||
ss << "IO Exception: " << e_what_;
|
||||
else
|
||||
ss << "IO Exception (" << errno_ << "): " << e_what_;
|
||||
ss << ", file " << file_ << ", line " << line_ << ".";
|
||||
return ss.str ().c_str ();
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidBytesizeException : public std::exception {
|
||||
int bytesize;
|
||||
class PortNotOpenedException : public std::exception
|
||||
{
|
||||
const char * e_what_;
|
||||
public:
|
||||
InvalidBytesizeException(int bytesize) {this->bytesize = bytesize;}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "Invalid bytesize provided: " << this->bytesize;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
PortNotOpenedException (const char * description) : e_what_ (description) {}
|
||||
|
||||
virtual const char* what () const throw ()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << e_what_ << " called before port was opened.";
|
||||
return ss.str ().c_str ();
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidParityException : public std::exception {
|
||||
int parity;
|
||||
public:
|
||||
InvalidParityException(int parity) {this->parity = parity;}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "Invalid parity provided: " << this->parity;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidStopbitsException : public std::exception {
|
||||
int stopbits;
|
||||
public:
|
||||
InvalidStopbitsException(int stopbits) {this->stopbits = stopbits;}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "Invalid stopbits provided: " << this->stopbits;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidFlowcontrolException : public std::exception {
|
||||
int flowcontrol;
|
||||
public:
|
||||
InvalidFlowcontrolException(int flowcontrol) {this->flowcontrol = flowcontrol;}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "Invalid flowcontrol provided: " << this->flowcontrol;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
class SerialExceptionBase : public std::exception
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
} // namespace serial
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
212
serial.cmake
212
serial.cmake
@ -1,122 +1,150 @@
|
||||
macro(build_serial)
|
||||
## Project Setup
|
||||
cmake_minimum_required(VERSION 2.4.6)
|
||||
|
||||
if(COMMAND cmake_policy)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
endif(COMMAND cmake_policy)
|
||||
## Project Setup
|
||||
cmake_minimum_required(VERSION 2.4.6)
|
||||
|
||||
project(Serial)
|
||||
if(COMMAND cmake_policy)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
endif(COMMAND cmake_policy)
|
||||
|
||||
## Configurations
|
||||
project(Serial)
|
||||
|
||||
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
|
||||
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
|
||||
## Configurations
|
||||
# Enable warnings
|
||||
# Assuming unix means a gcc style compiler, eg. g++ or clang++.
|
||||
IF(UNIX)
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Weffc++ -pedantic -pedantic-errors -Wextra -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wconversion -Wdisabled-optimization -Wfloat-equal -Wformat -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimplicit -Wimport -Winit-self -Winline -Winvalid-pch -Wlong-long -Wmissing-braces -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wstack-protector -Wstrict-aliasing -Wstrict-aliasing=2 -Wswitch -Wswitch-default -Wswitch-enum -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wvariadic-macros -Wvolatile-register-var -Wwrite-strings")
|
||||
ELSEIF(WIN32)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
endif(UNIX)
|
||||
|
||||
# Allow for building shared libs override
|
||||
IF(NOT BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
ENDIF(NOT BUILD_SHARED_LIBS)
|
||||
# Use clang if available
|
||||
IF(EXISTS /usr/bin/clang)
|
||||
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
|
||||
set(SERIAL_BUILD_WARNINGS TRUE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
ENDIF(EXISTS /usr/bin/clang)
|
||||
|
||||
# Set the default path for built executables to the "bin" directory
|
||||
IF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
ENDIF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
|
||||
# set the default path for built libraries to the "lib" directory
|
||||
IF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||
ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
|
||||
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
|
||||
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
|
||||
|
||||
## Configure the build system
|
||||
# Allow for building shared libs override
|
||||
IF(NOT BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
ENDIF(NOT BUILD_SHARED_LIBS)
|
||||
|
||||
# Add the include folder to the include path
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
# Threading libraries added for mutexs
|
||||
FIND_PACKAGE (Threads)
|
||||
|
||||
# Add default source files
|
||||
set(SERIAL_SRCS src/serial.cpp)
|
||||
# Add default header files
|
||||
set(SERIAL_HEADERS include/serial/serial.h)
|
||||
# Set the default path for built executables to the "bin" directory
|
||||
IF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
ENDIF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
|
||||
# set the default path for built libraries to the "lib" directory
|
||||
IF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||
ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
|
||||
|
||||
# Find Boost, if it hasn't already been found
|
||||
IF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
|
||||
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
|
||||
ENDIF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
|
||||
## Configure the build system
|
||||
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
# Add the include folder to the include path
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
set(SERIAL_LINK_LIBS ${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY})
|
||||
# Add default source files
|
||||
set(SERIAL_SRCS src/serial.cc)
|
||||
IF(WIN32)
|
||||
list(APPEND SERIAL_SRCS src/impl/windows.cc)
|
||||
ELSE(WIN32)
|
||||
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
||||
ENDIF(WIN32)
|
||||
# Add default header files
|
||||
set(SERIAL_HEADERS include/serial/serial.h)
|
||||
|
||||
## Build the Serial Library
|
||||
|
||||
# Compile the Library
|
||||
add_library(serial ${SERIAL_SRCS} ${SERIAL_HEADERS})
|
||||
target_link_libraries(serial ${SERIAL_LINK_LIBS})
|
||||
IF( WIN32 )
|
||||
target_link_libraries(serial wsock32)
|
||||
ENDIF( )
|
||||
set(OTHER_LIBS "")
|
||||
if(UNIX)
|
||||
set(OTHER_LIBS util)
|
||||
endif(UNIX)
|
||||
|
||||
## Build the Serial Library
|
||||
|
||||
# Check for OS X and if so disable kqueue support in asio
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_KQUEUE)
|
||||
ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
# Compile the Library
|
||||
add_library(serial ${SERIAL_SRCS})
|
||||
target_link_libraries(serial ${CMAKE_THREAD_LIBS_INIT} ${OTHER_LIBS})
|
||||
|
||||
## Build Examples
|
||||
## Build Examples
|
||||
|
||||
# If asked to
|
||||
IF(SERIAL_BUILD_EXAMPLES)
|
||||
# Compile the Test program
|
||||
add_executable(serial_example examples/serial_example.cpp)
|
||||
# Link the Test program to the Serial library
|
||||
target_link_libraries(serial_example serial)
|
||||
ENDIF(SERIAL_BUILD_EXAMPLES)
|
||||
# If asked to
|
||||
IF(SERIAL_BUILD_EXAMPLES)
|
||||
# Compile the Serial Test program
|
||||
add_executable(serial_example examples/serial_example.cc)
|
||||
# Link the Test program to the Serial library
|
||||
target_link_libraries(serial_example serial)
|
||||
ENDIF(SERIAL_BUILD_EXAMPLES)
|
||||
|
||||
## Build tests
|
||||
## Build tests
|
||||
|
||||
# If asked to
|
||||
IF(SERIAL_BUILD_TESTS)
|
||||
# none yet...
|
||||
ENDIF(SERIAL_BUILD_TESTS)
|
||||
# If asked to
|
||||
IF(SERIAL_BUILD_TESTS)
|
||||
# Find Google Test
|
||||
enable_testing()
|
||||
find_package(GTest REQUIRED)
|
||||
include_directories(${GTEST_INCLUDE_DIRS})
|
||||
|
||||
## Setup install and uninstall
|
||||
# Compile the Serial Test program
|
||||
add_executable(serial_tests tests/serial_tests.cc)
|
||||
# Link the Test program to the serial library
|
||||
target_link_libraries(serial_tests ${GTEST_BOTH_LIBRARIES}
|
||||
serial)
|
||||
|
||||
# Unless asked not to...
|
||||
IF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
|
||||
# Configure make install
|
||||
IF(NOT CMAKE_INSTALL_PREFIX)
|
||||
SET(CMAKE_INSTALL_PREFIX /usr/local)
|
||||
ENDIF(NOT CMAKE_INSTALL_PREFIX)
|
||||
add_test(AllTestsIntest_serial serial_tests)
|
||||
ENDIF(SERIAL_BUILD_TESTS)
|
||||
|
||||
## Setup install and uninstall
|
||||
|
||||
# Unless asked not to...
|
||||
IF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
|
||||
# Configure make install
|
||||
IF(NOT CMAKE_INSTALL_PREFIX)
|
||||
SET(CMAKE_INSTALL_PREFIX /usr/local)
|
||||
ENDIF(NOT CMAKE_INSTALL_PREFIX)
|
||||
|
||||
INSTALL(TARGETS serial
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL(TARGETS serial
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
INSTALL(FILES include/serial/serial.h DESTINATION include/serial)
|
||||
INSTALL(FILES include/serial/serial.h
|
||||
DESTINATION include/serial)
|
||||
|
||||
IF(NOT CMAKE_FIND_INSTALL_PATH)
|
||||
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
|
||||
ENDIF(NOT CMAKE_FIND_INSTALL_PATH)
|
||||
IF(NOT CMAKE_FIND_INSTALL_PATH)
|
||||
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
|
||||
ENDIF(NOT CMAKE_FIND_INSTALL_PATH)
|
||||
|
||||
INSTALL(FILES Findserial.cmake DESTINATION ${CMAKE_FIND_INSTALL_PATH}/Modules/)
|
||||
INSTALL(FILES Findserial.cmake
|
||||
DESTINATION ${CMAKE_FIND_INSTALL_PATH}/Modules/)
|
||||
|
||||
ADD_CUSTOM_TARGET(uninstall @echo uninstall package)
|
||||
ADD_CUSTOM_TARGET(uninstall @echo uninstall package)
|
||||
|
||||
IF (UNIX)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
COMMENT "uninstall package"
|
||||
COMMAND xargs ARGS rm < install_manifest.txt
|
||||
IF (UNIX)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
COMMENT "uninstall package"
|
||||
COMMAND xargs ARGS rm < install_manifest.txt
|
||||
|
||||
TARGET uninstall
|
||||
)
|
||||
ELSE(UNIX)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
COMMENT "uninstall only implemented in unix"
|
||||
TARGET uninstall
|
||||
)
|
||||
ENDIF(UNIX)
|
||||
ENDIF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
|
||||
TARGET uninstall
|
||||
)
|
||||
ELSE(UNIX)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
COMMENT "uninstall only implemented in unix"
|
||||
TARGET uninstall
|
||||
)
|
||||
ENDIF(UNIX)
|
||||
ENDIF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
|
||||
endmacro(build_serial)
|
||||
|
||||
@ -20,6 +20,13 @@ clean:
|
||||
-cd build && make clean
|
||||
rm -rf build bin lib
|
||||
|
||||
.PHONY: doc
|
||||
doc:
|
||||
@doxygen doc/Doxyfile
|
||||
ifeq ($(UNAME),Darwin)
|
||||
@open doc/html/index.html
|
||||
endif
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@mkdir -p build
|
||||
@ -30,4 +37,4 @@ ifneq ($(MAKE),)
|
||||
else
|
||||
cd build && make
|
||||
endif
|
||||
# cd bin && ./serial_tests
|
||||
cd bin && ./serial_tests
|
||||
@ -1,36 +1,42 @@
|
||||
macro(build_serial)
|
||||
cmake_minimum_required(VERSION 2.4.6)
|
||||
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
|
||||
|
||||
# Set the build type. Options are:
|
||||
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
|
||||
# Debug : w/ debug symbols, w/o optimization
|
||||
# Release : w/o debug symbols, w/ optimization
|
||||
# RelWithDebInfo : w/ debug symbols, w/ optimization
|
||||
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
|
||||
set(ROS_BUILD_TYPE RelWithDebInfo)
|
||||
cmake_minimum_required(VERSION 2.4.6)
|
||||
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
|
||||
|
||||
rosbuild_init()
|
||||
# Set the build type. Options are:
|
||||
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
|
||||
# Debug : w/ debug symbols, w/o optimization
|
||||
# Release : w/o debug symbols, w/ optimization
|
||||
# RelWithDebInfo : w/ debug symbols, w/ optimization
|
||||
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
|
||||
set(ROS_BUILD_TYPE Debug)
|
||||
|
||||
#set the default path for built executables to the "bin" directory
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
#set the default path for built libraries to the "lib" directory
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||
rosbuild_init()
|
||||
|
||||
# Check for OS X and if so disable kqueue support in asio
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_KQUEUE)
|
||||
ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
#set the default path for built executables to the "bin" directory
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
#set the default path for built libraries to the "lib" directory
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||
|
||||
# Build the serial library
|
||||
rosbuild_add_library(serial src/serial.cpp include/serial/serial.h)
|
||||
include_directories(include)
|
||||
include_directories(vendor)
|
||||
|
||||
# Add boost dependencies
|
||||
rosbuild_add_boost_directories()
|
||||
rosbuild_link_boost(serial system filesystem thread)
|
||||
set(SERIAL_SRCS src/serial.cc)
|
||||
if(UNIX)
|
||||
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
||||
else(UNIX)
|
||||
list(APPEND SERIAL_SRCS src/impl/winows.cc)
|
||||
endif(UNIX)
|
||||
|
||||
# Build example
|
||||
rosbuild_add_executable(serial_example examples/serial_example.cpp)
|
||||
target_link_libraries(serial_example serial)
|
||||
# Build the serial library
|
||||
rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
|
||||
|
||||
# Build example
|
||||
rosbuild_add_executable(serial_example examples/serial_example.cc)
|
||||
target_link_libraries(serial_example ${PROJECT_NAME})
|
||||
|
||||
# Create unit tests
|
||||
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
|
||||
target_link_libraries(serial_tests ${PROJECT_NAME})
|
||||
|
||||
endmacro(build_serial)
|
||||
|
||||
761
src/impl/unix.cc
Normal file
761
src/impl/unix.cc
Normal file
@ -0,0 +1,761 @@
|
||||
/* Copyright 2012 William Woodall and John Harrison */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <sysexits.h>
|
||||
#include <termios.h>
|
||||
#include <sys/param.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
# include <linux/serial.h>
|
||||
#endif
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#ifdef __MACH__
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
#include "serial/impl/unix.h"
|
||||
|
||||
#ifndef TIOCINQ
|
||||
#ifdef FIONREAD
|
||||
#define TIOCINQ FIONREAD
|
||||
#else
|
||||
#define TIOCINQ 0x541B
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
using std::invalid_argument;
|
||||
using serial::Serial;
|
||||
using serial::SerialExecption;
|
||||
using serial::PortNotOpenedException;
|
||||
using serial::IOException;
|
||||
|
||||
|
||||
Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
|
||||
long timeout, bytesize_t bytesize,
|
||||
parity_t parity, stopbits_t stopbits,
|
||||
flowcontrol_t flowcontrol)
|
||||
: port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (true), rtscts_ (false),
|
||||
timeout_ (timeout), baudrate_ (baudrate), parity_ (parity),
|
||||
bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol)
|
||||
{
|
||||
pthread_mutex_init(&this->read_mutex, NULL);
|
||||
pthread_mutex_init(&this->write_mutex, NULL);
|
||||
if (port_.empty () == false)
|
||||
open ();
|
||||
}
|
||||
|
||||
Serial::SerialImpl::~SerialImpl ()
|
||||
{
|
||||
close();
|
||||
pthread_mutex_destroy(&this->read_mutex);
|
||||
pthread_mutex_destroy(&this->write_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::open ()
|
||||
{
|
||||
if (port_.empty ())
|
||||
{
|
||||
throw invalid_argument ("Empty port is invalid.");
|
||||
}
|
||||
if (is_open_ == true)
|
||||
{
|
||||
throw SerialExecption ("Serial port already open.");
|
||||
}
|
||||
|
||||
fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
|
||||
if (fd_ == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EINTR:
|
||||
// Recurse because this is a recoverable error.
|
||||
open ();
|
||||
return;
|
||||
case ENFILE:
|
||||
case EMFILE:
|
||||
THROW (IOException, "Too many file handles open.");
|
||||
default:
|
||||
THROW (IOException, errno);
|
||||
}
|
||||
}
|
||||
|
||||
reconfigurePort();
|
||||
is_open_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::reconfigurePort ()
|
||||
{
|
||||
if (fd_ == -1)
|
||||
{
|
||||
// Can only operate on a valid file descriptor
|
||||
THROW (IOException, "Invalid file descriptor, is the serial port open?");
|
||||
}
|
||||
|
||||
struct termios options; // The options for the file descriptor
|
||||
|
||||
if (tcgetattr(fd_, &options) == -1)
|
||||
{
|
||||
THROW (IOException, "::tcgetattr");
|
||||
}
|
||||
|
||||
// set up raw mode / no echo / binary
|
||||
options.c_cflag |= (unsigned long) (CLOCAL|CREAD);
|
||||
options.c_lflag &= (unsigned long) ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|
|
||||
ISIG|IEXTEN); //|ECHOPRT
|
||||
|
||||
options.c_oflag &= (unsigned long) ~(OPOST);
|
||||
options.c_iflag &= (unsigned long) ~(INLCR|IGNCR|ICRNL|IGNBRK);
|
||||
#ifdef IUCLC
|
||||
options.c_iflag &= (unsigned long) ~IUCLC;
|
||||
#endif
|
||||
#ifdef PARMRK
|
||||
options.c_iflag &= (unsigned long) ~PARMRK;
|
||||
#endif
|
||||
|
||||
// setup baud rate
|
||||
bool custom_baud = false;
|
||||
speed_t baud;
|
||||
switch (baudrate_)
|
||||
{
|
||||
#ifdef B0
|
||||
case 0: baud = B0; break;
|
||||
#endif
|
||||
#ifdef B50
|
||||
case 50: baud = B50; break;
|
||||
#endif
|
||||
#ifdef B75
|
||||
case 75: baud = B75; break;
|
||||
#endif
|
||||
#ifdef B110
|
||||
case 110: baud = B110; break;
|
||||
#endif
|
||||
#ifdef B134
|
||||
case 134: baud = B134; break;
|
||||
#endif
|
||||
#ifdef B150
|
||||
case 150: baud = B150; break;
|
||||
#endif
|
||||
#ifdef B200
|
||||
case 200: baud = B200; break;
|
||||
#endif
|
||||
#ifdef B300
|
||||
case 300: baud = B300; break;
|
||||
#endif
|
||||
#ifdef B600
|
||||
case 600: baud = B600; break;
|
||||
#endif
|
||||
#ifdef B1200
|
||||
case 1200: baud = B1200; break;
|
||||
#endif
|
||||
#ifdef B1800
|
||||
case 1800: baud = B1800; break;
|
||||
#endif
|
||||
#ifdef B2400
|
||||
case 2400: baud = B2400; break;
|
||||
#endif
|
||||
#ifdef B4800
|
||||
case 4800: baud = B4800; break;
|
||||
#endif
|
||||
#ifdef B7200
|
||||
case 7200: baud = B7200; break;
|
||||
#endif
|
||||
#ifdef B9600
|
||||
case 9600: baud = B9600; break;
|
||||
#endif
|
||||
#ifdef B14400
|
||||
case 14400: baud = B14400; break;
|
||||
#endif
|
||||
#ifdef B19200
|
||||
case 19200: baud = B19200; break;
|
||||
#endif
|
||||
#ifdef B28800
|
||||
case 28800: baud = B28800; break;
|
||||
#endif
|
||||
#ifdef B57600
|
||||
case 57600: baud = B57600; break;
|
||||
#endif
|
||||
#ifdef B76800
|
||||
case 76800: baud = B76800; break;
|
||||
#endif
|
||||
#ifdef B38400
|
||||
case 38400: baud = B38400; break;
|
||||
#endif
|
||||
#ifdef B115200
|
||||
case 115200: baud = B115200; break;
|
||||
#endif
|
||||
#ifdef B128000
|
||||
case 128000: baud = B128000; break;
|
||||
#endif
|
||||
#ifdef B153600
|
||||
case 153600: baud = B153600; break;
|
||||
#endif
|
||||
#ifdef B230400
|
||||
case 230400: baud = B230400; break;
|
||||
#endif
|
||||
#ifdef B256000
|
||||
case 256000: baud = B256000; break;
|
||||
#endif
|
||||
#ifdef B460800
|
||||
case 460800: baud = B460800; break;
|
||||
#endif
|
||||
#ifdef B921600
|
||||
case 921600: baud = B921600; break;
|
||||
#endif
|
||||
default:
|
||||
custom_baud = true;
|
||||
// Mac OS X 10.x Support
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define IOSSIOSPEED _IOW('T', 2, speed_t)
|
||||
int new_baud = static_cast<int> (baudrate_);
|
||||
if (ioctl (fd_, IOSSIOSPEED, &new_baud, 1) < 0)
|
||||
{
|
||||
THROW (IOException, errno);
|
||||
}
|
||||
// Linux Support
|
||||
#elif defined(__linux__)
|
||||
struct serial_struct ser;
|
||||
ioctl (fd_, TIOCGSERIAL, &ser);
|
||||
// set custom divisor
|
||||
ser.custom_divisor = ser.baud_base / baudrate_;
|
||||
// update flags
|
||||
ser.flags &= ~ASYNC_SPD_MASK;
|
||||
ser.flags |= ASYNC_SPD_CUST;
|
||||
|
||||
if (ioctl (fd_, TIOCSSERIAL, ser) < 0)
|
||||
{
|
||||
THROW (IOException, errno);
|
||||
}
|
||||
#else
|
||||
throw invalid_argument ("OS does not currently support custom bauds");
|
||||
#endif
|
||||
}
|
||||
if (custom_baud == false)
|
||||
{
|
||||
#ifdef _BSD_SOURCE
|
||||
::cfsetspeed(&options, baud);
|
||||
#else
|
||||
::cfsetispeed(&options, baud);
|
||||
::cfsetospeed(&options, baud);
|
||||
#endif
|
||||
}
|
||||
|
||||
// setup char len
|
||||
options.c_cflag &= (unsigned long) ~CSIZE;
|
||||
if (bytesize_ == eightbits)
|
||||
options.c_cflag |= CS8;
|
||||
else if (bytesize_ == sevenbits)
|
||||
options.c_cflag |= CS7;
|
||||
else if (bytesize_ == sixbits)
|
||||
options.c_cflag |= CS6;
|
||||
else if (bytesize_ == fivebits)
|
||||
options.c_cflag |= CS5;
|
||||
else
|
||||
throw invalid_argument ("invalid char len");
|
||||
// setup stopbits
|
||||
if (stopbits_ == stopbits_one)
|
||||
options.c_cflag &= (unsigned long) ~(CSTOPB);
|
||||
else if (stopbits_ == stopbits_one_point_five)
|
||||
// ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5
|
||||
options.c_cflag |= (CSTOPB);
|
||||
else if (stopbits_ == stopbits_two)
|
||||
options.c_cflag |= (CSTOPB);
|
||||
else
|
||||
throw invalid_argument ("invalid stop bit");
|
||||
// setup parity
|
||||
options.c_iflag &= (unsigned long) ~(INPCK|ISTRIP);
|
||||
if (parity_ == parity_none)
|
||||
{
|
||||
options.c_cflag &= (unsigned long) ~(PARENB|PARODD);
|
||||
}
|
||||
else if (parity_ == parity_even)
|
||||
{
|
||||
options.c_cflag &= (unsigned long) ~(PARODD);
|
||||
options.c_cflag |= (PARENB);
|
||||
}
|
||||
else if (parity_ == parity_odd)
|
||||
{
|
||||
options.c_cflag |= (PARENB|PARODD);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw invalid_argument ("invalid parity");
|
||||
}
|
||||
// setup flow control
|
||||
// xonxoff
|
||||
#ifdef IXANY
|
||||
if (xonxoff_)
|
||||
options.c_iflag |= (IXON|IXOFF); //|IXANY)
|
||||
else
|
||||
options.c_iflag &= (unsigned long) ~(IXON|IXOFF|IXANY);
|
||||
#else
|
||||
if (xonxoff_)
|
||||
options.c_iflag |= (IXON|IXOFF);
|
||||
else
|
||||
options.c_iflag &= (unsigned long) ~(IXON|IXOFF);
|
||||
#endif
|
||||
// rtscts
|
||||
#ifdef CRTSCTS
|
||||
if (rtscts_)
|
||||
options.c_cflag |= (CRTSCTS);
|
||||
else
|
||||
options.c_cflag &= (unsigned long) ~(CRTSCTS);
|
||||
#elif defined CNEW_RTSCTS
|
||||
if (rtscts_)
|
||||
options.c_cflag |= (CNEW_RTSCTS);
|
||||
else
|
||||
options.c_cflag &= (unsigned long) ~(CNEW_RTSCTS);
|
||||
#else
|
||||
#error "OS Support seems wrong."
|
||||
#endif
|
||||
|
||||
// http://www.unixwiz.net/techtips/termios-vmin-vtime.html
|
||||
// this basically sets the read call up to be a polling read,
|
||||
// but we are using select to ensure there is data available
|
||||
// to read before each call, so we should never needlessly poll
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = 0;
|
||||
|
||||
// activate settings
|
||||
::tcsetattr (fd_, TCSANOW, &options);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::close ()
|
||||
{
|
||||
if (is_open_ == true)
|
||||
{
|
||||
if (fd_ != -1)
|
||||
{
|
||||
::close (fd_); // Ignoring the outcome
|
||||
fd_ = -1;
|
||||
}
|
||||
is_open_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::isOpen () const
|
||||
{
|
||||
return is_open_;
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::SerialImpl::available ()
|
||||
{
|
||||
if (!is_open_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
int result = ioctl (fd_, TIOCINQ, &count);
|
||||
if (result == 0)
|
||||
{
|
||||
return static_cast<size_t> (count);
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW (IOException, errno);
|
||||
}
|
||||
}
|
||||
|
||||
inline void get_time_now(struct timespec &time) {
|
||||
# ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
time.tv_sec = mts.tv_sec;
|
||||
time.tv_nsec = mts.tv_nsec;
|
||||
# else
|
||||
clock_gettime(CLOCK_REALTIME, &time);
|
||||
# endif
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::SerialImpl::read (unsigned char* buf, size_t size)
|
||||
{
|
||||
if (!is_open_)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::read");
|
||||
}
|
||||
fd_set readfds;
|
||||
size_t bytes_read = 0;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = timeout_ / 1000;
|
||||
timeout.tv_usec = static_cast<int> (timeout_ % 1000) * 1000;
|
||||
while (bytes_read < size)
|
||||
{
|
||||
FD_ZERO (&readfds);
|
||||
FD_SET (fd_, &readfds);
|
||||
// On Linux the timeout struct is updated by select to contain the time
|
||||
// left on the timeout to make looping easier, but on other platforms this
|
||||
// does not occur.
|
||||
#if !defined(__linux__)
|
||||
// Begin timing select
|
||||
struct timespec start, end;
|
||||
get_time_now(start);
|
||||
#endif
|
||||
// Do the select
|
||||
int r = select (fd_ + 1, &readfds, NULL, NULL, &timeout);
|
||||
#if !defined(__linux__)
|
||||
// Calculate difference and update the structure
|
||||
get_time_now(end);
|
||||
// Calculate the time select took
|
||||
struct timeval diff;
|
||||
diff.tv_sec = end.tv_sec-start.tv_sec;
|
||||
diff.tv_usec = static_cast<int> ((end.tv_nsec-start.tv_nsec)/1000);
|
||||
// Update the timeout
|
||||
if (timeout.tv_sec <= diff.tv_sec) {
|
||||
timeout.tv_sec = 0;
|
||||
} else {
|
||||
timeout.tv_sec -= diff.tv_sec;
|
||||
}
|
||||
if (timeout.tv_usec <= diff.tv_usec) {
|
||||
timeout.tv_usec = 0;
|
||||
} else {
|
||||
timeout.tv_usec -= diff.tv_usec;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Figure out what happened by looking at select's response 'r'
|
||||
/** Error **/
|
||||
if (r < 0) {
|
||||
// Select was interrupted, try again
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise there was some error
|
||||
THROW (IOException, errno);
|
||||
}
|
||||
/** Timeout **/
|
||||
if (r == 0) {
|
||||
break;
|
||||
}
|
||||
/** Something ready to read **/
|
||||
if (r > 0) {
|
||||
// Make sure our file descriptor is in the ready to read list
|
||||
if (FD_ISSET (fd_, &readfds)) {
|
||||
// This should be non-blocking returning only what is avaialble now
|
||||
// Then returning so that select can block again.
|
||||
ssize_t bytes_read_now =
|
||||
::read (fd_, buf+bytes_read, size-bytes_read);
|
||||
// read should always return some data as select reported it was
|
||||
// ready to read when we get to this point.
|
||||
if (bytes_read_now < 1)
|
||||
{
|
||||
// Disconnected devices, at least on Linux, show the
|
||||
// behavior that they are always ready to read immediately
|
||||
// but reading returns nothing.
|
||||
throw SerialExecption ("device reports readiness to read but "
|
||||
"returned no data (device disconnected?)");
|
||||
}
|
||||
// Update bytes_read
|
||||
bytes_read += static_cast<size_t> (bytes_read_now);
|
||||
// If bytes_read == size then we have read everything we need
|
||||
if (bytes_read == size) {
|
||||
break;
|
||||
}
|
||||
// If bytes_read < size then we have more to read
|
||||
if (bytes_read < size) {
|
||||
continue;
|
||||
}
|
||||
// If bytes_read > size then we have over read, which shouldn't happen
|
||||
if (bytes_read > size) {
|
||||
throw SerialExecption ("read over read, too many bytes where "
|
||||
"read, this shouldn't happen, might be "
|
||||
"a logical error!");
|
||||
}
|
||||
}
|
||||
// This shouldn't happen, if r > 0 our fd has to be in the list!
|
||||
THROW (IOException, "select reports ready to read, but our fd isn't"
|
||||
" in the list, this shouldn't happen!");
|
||||
}
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::SerialImpl::write (const string &data)
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::write");
|
||||
}
|
||||
|
||||
return static_cast<size_t> (::write (fd_, data.c_str (), data.length ()));
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setPort (const string &port)
|
||||
{
|
||||
port_ = port;
|
||||
}
|
||||
|
||||
string
|
||||
Serial::SerialImpl::getPort () const
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setTimeout (long timeout)
|
||||
{
|
||||
timeout_ = timeout;
|
||||
}
|
||||
|
||||
long
|
||||
Serial::SerialImpl::getTimeout () const
|
||||
{
|
||||
return timeout_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setBaudrate (unsigned long baudrate)
|
||||
{
|
||||
baudrate_ = baudrate;
|
||||
if (is_open_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
unsigned long
|
||||
Serial::SerialImpl::getBaudrate () const
|
||||
{
|
||||
return baudrate_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
|
||||
{
|
||||
bytesize_ = bytesize;
|
||||
if (is_open_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::bytesize_t
|
||||
Serial::SerialImpl::getBytesize () const
|
||||
{
|
||||
return bytesize_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setParity (serial::parity_t parity)
|
||||
{
|
||||
parity_ = parity;
|
||||
if (is_open_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::parity_t
|
||||
Serial::SerialImpl::getParity () const
|
||||
{
|
||||
return parity_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
|
||||
{
|
||||
stopbits_ = stopbits;
|
||||
if (is_open_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::stopbits_t
|
||||
Serial::SerialImpl::getStopbits () const
|
||||
{
|
||||
return stopbits_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
|
||||
{
|
||||
flowcontrol_ = flowcontrol;
|
||||
if (is_open_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::flowcontrol_t
|
||||
Serial::SerialImpl::getFlowcontrol () const
|
||||
{
|
||||
return flowcontrol_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::flush ()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::flush");
|
||||
}
|
||||
tcdrain (fd_);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::flushInput ()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::flushInput");
|
||||
}
|
||||
tcflush (fd_, TCIFLUSH);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::flushOutput ()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::flushOutput");
|
||||
}
|
||||
tcflush (fd_, TCOFLUSH);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::sendBreak (int duration)
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::sendBreak");
|
||||
}
|
||||
tcsendbreak (fd_, static_cast<int> (duration/4));
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setBreak (bool level)
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::setBreak");
|
||||
}
|
||||
if (level)
|
||||
{
|
||||
ioctl (fd_, TIOCSBRK);
|
||||
}
|
||||
else {
|
||||
ioctl (fd_, TIOCCBRK);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setRTS (bool level)
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::setRTS");
|
||||
}
|
||||
if (level)
|
||||
{
|
||||
ioctl (fd_, TIOCMBIS, TIOCM_RTS);
|
||||
}
|
||||
else {
|
||||
ioctl (fd_, TIOCMBIC, TIOCM_RTS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setDTR (bool level)
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::setDTR");
|
||||
}
|
||||
if (level)
|
||||
{
|
||||
ioctl (fd_, TIOCMBIS, TIOCM_DTR);
|
||||
}
|
||||
else
|
||||
{
|
||||
ioctl (fd_, TIOCMBIC, TIOCM_DTR);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getCTS ()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getCTS");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_CTS) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getDSR()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getDSR");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_DSR) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getRI()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getRI");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_RI) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getCD()
|
||||
{
|
||||
if (is_open_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getCD");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_CD) != 0;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::readLock() {
|
||||
int result = pthread_mutex_lock(&this->read_mutex);
|
||||
if (result) {
|
||||
THROW (IOException, result);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::readUnlock() {
|
||||
int result = pthread_mutex_unlock(&this->read_mutex);
|
||||
if (result) {
|
||||
THROW (IOException, result);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::writeLock() {
|
||||
int result = pthread_mutex_lock(&this->write_mutex);
|
||||
if (result) {
|
||||
THROW (IOException, result);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::writeUnlock() {
|
||||
int result = pthread_mutex_unlock(&this->write_mutex);
|
||||
if (result) {
|
||||
THROW (IOException, result);
|
||||
}
|
||||
}
|
||||
670
src/impl/windows.cc
Normal file
670
src/impl/windows.cc
Normal file
@ -0,0 +1,670 @@
|
||||
/* Copyright 2012 William Woodall and John Harrison */
|
||||
|
||||
#include "serial/impl/windows.h"
|
||||
|
||||
using std::string;
|
||||
using std::invalid_argument;
|
||||
using serial::Serial;
|
||||
using serial::SerialExecption;
|
||||
using serial::PortNotOpenedException;
|
||||
using serial::IOException;
|
||||
|
||||
|
||||
Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
|
||||
long timeout, bytesize_t bytesize,
|
||||
parity_t parity, stopbits_t stopbits,
|
||||
flowcontrol_t flowcontrol)
|
||||
: port_ (port), fd_ (-1), isOpen_ (false), xonxoff_ (true), rtscts_ (false),
|
||||
timeout_ (timeout), baudrate_ (baudrate), parity_ (parity), bytesize_ (bytesize),
|
||||
stopbits_ (stopbits), flowcontrol_ (flowcontrol)
|
||||
{
|
||||
if (port_.empty () == false)
|
||||
open ();
|
||||
}
|
||||
|
||||
Serial::SerialImpl::~SerialImpl ()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::open ()
|
||||
{
|
||||
if (port_.empty())
|
||||
{
|
||||
throw invalid_argument ("bad port specified");
|
||||
}
|
||||
if (isOpen_ == true)
|
||||
{
|
||||
throw SerialExecption ("port already open");
|
||||
}
|
||||
|
||||
fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
|
||||
if (fd_ == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EINTR:
|
||||
// Recurse because this is a recoverable error.
|
||||
open ();
|
||||
return;
|
||||
case ENFILE:
|
||||
case EMFILE:
|
||||
throw IOException ("to many file handles open");
|
||||
break;
|
||||
default:
|
||||
throw IOException (errno);
|
||||
}
|
||||
}
|
||||
|
||||
reconfigurePort();
|
||||
isOpen_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::reconfigurePort ()
|
||||
{
|
||||
if (fd_ == -1)
|
||||
{
|
||||
// Can only operate on a valid file descriptor
|
||||
throw IOException ("invalid file descriptor");
|
||||
}
|
||||
|
||||
struct termios options; // The options for the file descriptor
|
||||
|
||||
if (tcgetattr(fd_, &options) == -1)
|
||||
{
|
||||
throw IOException ("::tcgetattr");
|
||||
}
|
||||
|
||||
// set up raw mode / no echo / binary
|
||||
options.c_cflag |= (unsigned long) (CLOCAL|CREAD);
|
||||
options.c_lflag &= (unsigned long) ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|
|
||||
ISIG|IEXTEN); //|ECHOPRT
|
||||
|
||||
options.c_oflag &= (unsigned long) ~(OPOST);
|
||||
options.c_iflag &= (unsigned long) ~(INLCR|IGNCR|ICRNL|IGNBRK);
|
||||
#ifdef IUCLC
|
||||
options.c_iflag &= (unsigned long) ~IUCLC;
|
||||
#endif
|
||||
#ifdef PARMRK
|
||||
options.c_iflag &= (unsigned long) ~PARMRK;
|
||||
#endif
|
||||
|
||||
// setup baud rate
|
||||
bool custom_baud = false;
|
||||
speed_t baud;
|
||||
switch (baudrate_)
|
||||
{
|
||||
#ifdef B0
|
||||
case 0: baud = B0; break;
|
||||
#endif
|
||||
#ifdef B50
|
||||
case 50: baud = B50; break;
|
||||
#endif
|
||||
#ifdef B75
|
||||
case 75: baud = B75; break;
|
||||
#endif
|
||||
#ifdef B110
|
||||
case 110: baud = B110; break;
|
||||
#endif
|
||||
#ifdef B134
|
||||
case 134: baud = B134; break;
|
||||
#endif
|
||||
#ifdef B150
|
||||
case 150: baud = B150; break;
|
||||
#endif
|
||||
#ifdef B200
|
||||
case 200: baud = B200; break;
|
||||
#endif
|
||||
#ifdef B300
|
||||
case 300: baud = B300; break;
|
||||
#endif
|
||||
#ifdef B600
|
||||
case 600: baud = B600; break;
|
||||
#endif
|
||||
#ifdef B1200
|
||||
case 1200: baud = B1200; break;
|
||||
#endif
|
||||
#ifdef B1800
|
||||
case 1800: baud = B1800; break;
|
||||
#endif
|
||||
#ifdef B2400
|
||||
case 2400: baud = B2400; break;
|
||||
#endif
|
||||
#ifdef B4800
|
||||
case 4800: baud = B4800; break;
|
||||
#endif
|
||||
#ifdef B7200
|
||||
case 7200: baud = B7200; break;
|
||||
#endif
|
||||
#ifdef B9600
|
||||
case 9600: baud = B9600; break;
|
||||
#endif
|
||||
#ifdef B14400
|
||||
case 14400: baud = B14400; break;
|
||||
#endif
|
||||
#ifdef B19200
|
||||
case 19200: baud = B19200; break;
|
||||
#endif
|
||||
#ifdef B28800
|
||||
case 28800: baud = B28800; break;
|
||||
#endif
|
||||
#ifdef B57600
|
||||
case 57600: baud = B57600; break;
|
||||
#endif
|
||||
#ifdef B76800
|
||||
case 76800: baud = B76800; break;
|
||||
#endif
|
||||
#ifdef B38400
|
||||
case 38400: baud = B38400; break;
|
||||
#endif
|
||||
#ifdef B115200
|
||||
case 115200: baud = B115200; break;
|
||||
#endif
|
||||
#ifdef B128000
|
||||
case 128000: baud = B128000; break;
|
||||
#endif
|
||||
#ifdef B153600
|
||||
case 153600: baud = B153600; break;
|
||||
#endif
|
||||
#ifdef B230400
|
||||
case 230400: baud = B230400; break;
|
||||
#endif
|
||||
#ifdef B256000
|
||||
case 256000: baud = B256000; break;
|
||||
#endif
|
||||
#ifdef B460800
|
||||
case 460800: baud = B460800; break;
|
||||
#endif
|
||||
#ifdef B921600
|
||||
case 921600: baud = B921600; break;
|
||||
#endif
|
||||
default:
|
||||
custom_baud = true;
|
||||
// Mac OS X 10.x Support
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define IOSSIOSPEED _IOW('T', 2, speed_t)
|
||||
int new_baud = static_cast<int> (baudrate_);
|
||||
if (ioctl (fd_, IOSSIOSPEED, &new_baud, 1) < 0)
|
||||
{
|
||||
throw IOException (errno);
|
||||
}
|
||||
// Linux Support
|
||||
#elif defined(__linux__)
|
||||
struct serial_struct ser;
|
||||
ioctl(fd_, TIOCGSERIAL, &ser);
|
||||
// set custom divisor
|
||||
ser.custom_divisor = ser.baud_base / baudrate_;
|
||||
// update flags
|
||||
ser.flags &= ~ASYNC_SPD_MASK;
|
||||
ser.flags |= ASYNC_SPD_CUST;
|
||||
|
||||
if (ioctl(fd_, TIOCSSERIAL, ser) < 0)
|
||||
{
|
||||
throw IOException (errno);
|
||||
}
|
||||
#else
|
||||
throw invalid_argument ("OS does not currently support custom bauds");
|
||||
#endif
|
||||
}
|
||||
if (custom_baud == false)
|
||||
{
|
||||
#ifdef _BSD_SOURCE
|
||||
::cfsetspeed(&options, baud);
|
||||
#else
|
||||
::cfsetispeed(&options, baud);
|
||||
::cfsetospeed(&options, baud);
|
||||
#endif
|
||||
}
|
||||
|
||||
// setup char len
|
||||
options.c_cflag &= (unsigned long) ~CSIZE;
|
||||
if (bytesize_ == EIGHTBITS)
|
||||
options.c_cflag |= CS8;
|
||||
else if (bytesize_ == SEVENBITS)
|
||||
options.c_cflag |= CS7;
|
||||
else if (bytesize_ == SIXBITS)
|
||||
options.c_cflag |= CS6;
|
||||
else if (bytesize_ == FIVEBITS)
|
||||
options.c_cflag |= CS5;
|
||||
else
|
||||
throw invalid_argument ("invalid char len");
|
||||
// setup stopbits
|
||||
if (stopbits_ == STOPBITS_ONE)
|
||||
options.c_cflag &= (unsigned long) ~(CSTOPB);
|
||||
else if (stopbits_ == STOPBITS_ONE_POINT_FIVE)
|
||||
options.c_cflag |= (CSTOPB); // XXX same as TWO.. there is no POSIX support for 1.5
|
||||
else if (stopbits_ == STOPBITS_TWO)
|
||||
options.c_cflag |= (CSTOPB);
|
||||
else
|
||||
throw invalid_argument ("invalid stop bit");
|
||||
// setup parity
|
||||
options.c_iflag &= (unsigned long) ~(INPCK|ISTRIP);
|
||||
if (parity_ == PARITY_NONE)
|
||||
{
|
||||
options.c_cflag &= (unsigned long) ~(PARENB|PARODD);
|
||||
}
|
||||
else if (parity_ == PARITY_EVEN)
|
||||
{
|
||||
options.c_cflag &= (unsigned long) ~(PARODD);
|
||||
options.c_cflag |= (PARENB);
|
||||
}
|
||||
else if (parity_ == PARITY_ODD)
|
||||
{
|
||||
options.c_cflag |= (PARENB|PARODD);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw invalid_argument ("invalid parity");
|
||||
}
|
||||
// setup flow control
|
||||
// xonxoff
|
||||
#ifdef IXANY
|
||||
if (xonxoff_)
|
||||
options.c_iflag |= (IXON|IXOFF); //|IXANY)
|
||||
else
|
||||
options.c_iflag &= (unsigned long) ~(IXON|IXOFF|IXANY);
|
||||
#else
|
||||
if (xonxoff_)
|
||||
options.c_iflag |= (IXON|IXOFF);
|
||||
else
|
||||
options.c_iflag &= (unsigned long) ~(IXON|IXOFF);
|
||||
#endif
|
||||
// rtscts
|
||||
#ifdef CRTSCTS
|
||||
if (rtscts_)
|
||||
options.c_cflag |= (CRTSCTS);
|
||||
else
|
||||
options.c_cflag &= (unsigned long) ~(CRTSCTS);
|
||||
#elif defined CNEW_RTSCTS
|
||||
if (rtscts_)
|
||||
options.c_cflag |= (CNEW_RTSCTS);
|
||||
else
|
||||
options.c_cflag &= (unsigned long) ~(CNEW_RTSCTS);
|
||||
#else
|
||||
#error "OS Support seems wrong."
|
||||
#endif
|
||||
|
||||
options.c_cc[VMIN] = 1; // Minimum of 1 character in the buffer
|
||||
options.c_cc[VTIME] = 0; // timeout on waiting for new data
|
||||
|
||||
// activate settings
|
||||
::tcsetattr (fd_, TCSANOW, &options);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::close ()
|
||||
{
|
||||
if (isOpen_ == true)
|
||||
{
|
||||
if (fd_ != -1)
|
||||
{
|
||||
::close (fd_); // Ignoring the outcome
|
||||
fd_ = -1;
|
||||
}
|
||||
isOpen_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::isOpen () const
|
||||
{
|
||||
return isOpen_;
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::SerialImpl::available ()
|
||||
{
|
||||
if (!isOpen_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
int result = ioctl (fd_, TIOCINQ, &count);
|
||||
if (result == 0)
|
||||
{
|
||||
return static_cast<size_t> (count);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw IOException (errno);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::SerialImpl::read (char* buf, size_t size)
|
||||
{
|
||||
if (!isOpen_)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::read");
|
||||
}
|
||||
fd_set readfds;
|
||||
ssize_t bytes_read = 0;
|
||||
int count = 0;
|
||||
while (true)
|
||||
{
|
||||
count++;
|
||||
if (timeout_ != -1)
|
||||
{
|
||||
FD_ZERO (&readfds);
|
||||
FD_SET (fd_, &readfds);
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = timeout_ / 1000;
|
||||
timeout.tv_usec = static_cast<int> (timeout_ % 1000) * 1000;
|
||||
int r = select (fd_ + 1, &readfds, NULL, NULL, &timeout);
|
||||
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
throw IOException (errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout_ == -1 || FD_ISSET (fd_, &readfds))
|
||||
{
|
||||
bytes_read = ::read (fd_, buf, size);
|
||||
// read should always return some data as select reported it was
|
||||
// ready to read when we get to this point.
|
||||
if (bytes_read < 1)
|
||||
{
|
||||
// Disconnected devices, at least on Linux, show the
|
||||
// behavior that they are always ready to read immediately
|
||||
// but reading returns nothing.
|
||||
throw SerialExecption ("device reports readiness to read but "
|
||||
"returned no data (device disconnected?)");
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return static_cast<size_t> (bytes_read);
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::SerialImpl::write (const string &data)
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::write");
|
||||
}
|
||||
|
||||
fd_set writefds;
|
||||
ssize_t bytes_written = 0;
|
||||
while (true)
|
||||
{
|
||||
if (timeout_ != -1)
|
||||
{
|
||||
FD_ZERO (&writefds);
|
||||
FD_SET (fd_, &writefds);
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = timeout_ / 1000;
|
||||
timeout.tv_usec = static_cast<int> (timeout_ % 1000) * 1000;
|
||||
int r = select (fd_ + 1, NULL, &writefds, NULL, &timeout);
|
||||
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
throw IOException (errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout_ == -1 || FD_ISSET (fd_, &writefds))
|
||||
{
|
||||
bytes_written = ::write (fd_, data.c_str (), data.length ());
|
||||
// read should always return some data as select reported it was
|
||||
// ready to read when we get to this point.
|
||||
if (bytes_written < 1)
|
||||
{
|
||||
// Disconnected devices, at least on Linux, show the
|
||||
// behavior that they are always ready to read immediately
|
||||
// but reading returns nothing.
|
||||
throw SerialExecption ("device reports readiness to read but "
|
||||
"returned no data (device disconnected?)");
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<size_t> (bytes_written);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setPort (const string &port)
|
||||
{
|
||||
port_ = port;
|
||||
}
|
||||
|
||||
string
|
||||
Serial::SerialImpl::getPort () const
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setTimeout (long timeout)
|
||||
{
|
||||
timeout_ = timeout;
|
||||
}
|
||||
|
||||
long
|
||||
Serial::SerialImpl::getTimeout () const
|
||||
{
|
||||
return timeout_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setBaudrate (unsigned long baudrate)
|
||||
{
|
||||
baudrate_ = baudrate;
|
||||
if (isOpen_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
unsigned long
|
||||
Serial::SerialImpl::getBaudrate () const
|
||||
{
|
||||
return baudrate_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
|
||||
{
|
||||
bytesize_ = bytesize;
|
||||
if (isOpen_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::bytesize_t
|
||||
Serial::SerialImpl::getBytesize () const
|
||||
{
|
||||
return bytesize_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setParity (serial::parity_t parity)
|
||||
{
|
||||
parity_ = parity;
|
||||
if (isOpen_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::parity_t
|
||||
Serial::SerialImpl::getParity () const
|
||||
{
|
||||
return parity_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
|
||||
{
|
||||
stopbits_ = stopbits;
|
||||
if (isOpen_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::stopbits_t
|
||||
Serial::SerialImpl::getStopbits () const
|
||||
{
|
||||
return stopbits_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
|
||||
{
|
||||
flowcontrol_ = flowcontrol;
|
||||
if (isOpen_)
|
||||
reconfigurePort ();
|
||||
}
|
||||
|
||||
serial::flowcontrol_t
|
||||
Serial::SerialImpl::getFlowcontrol () const
|
||||
{
|
||||
return flowcontrol_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::flush ()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::flush");
|
||||
}
|
||||
tcdrain (fd_);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::flushInput ()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::flushInput");
|
||||
}
|
||||
tcflush (fd_, TCIFLUSH);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::flushOutput ()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::flushOutput");
|
||||
}
|
||||
tcflush (fd_, TCOFLUSH);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::sendBreak (int duration)
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::sendBreak");
|
||||
}
|
||||
tcsendbreak (fd_, static_cast<int> (duration/4));
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setBreak (bool level)
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::setBreak");
|
||||
}
|
||||
if (level)
|
||||
{
|
||||
ioctl (fd_, TIOCSBRK);
|
||||
}
|
||||
else {
|
||||
ioctl (fd_, TIOCCBRK);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setRTS (bool level)
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::setRTS");
|
||||
}
|
||||
if (level)
|
||||
{
|
||||
ioctl (fd_, TIOCMBIS, TIOCM_RTS);
|
||||
}
|
||||
else {
|
||||
ioctl (fd_, TIOCMBIC, TIOCM_RTS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Serial::SerialImpl::setDTR (bool level)
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::setDTR");
|
||||
}
|
||||
if (level)
|
||||
{
|
||||
ioctl (fd_, TIOCMBIS, TIOCM_DTR);
|
||||
}
|
||||
else
|
||||
{
|
||||
ioctl (fd_, TIOCMBIC, TIOCM_DTR);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getCTS ()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getCTS");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_CTS) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getDSR()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getDSR");
|
||||
}
|
||||
int s = ioctl(fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_DSR) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getRI()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getRI");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_RI) != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::SerialImpl::getCD()
|
||||
{
|
||||
if (isOpen_ == false)
|
||||
{
|
||||
throw PortNotOpenedException ("Serial::getCD");
|
||||
}
|
||||
int s = ioctl (fd_, TIOCMGET, 0);
|
||||
return (s & TIOCM_CD) != 0;
|
||||
}
|
||||
|
||||
362
src/serial.cc
Normal file
362
src/serial.cc
Normal file
@ -0,0 +1,362 @@
|
||||
/* Copyright 2012 William Woodall and John Harrison */
|
||||
#include <alloca.h>
|
||||
|
||||
#include "serial/serial.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "serial/impl/win.h"
|
||||
#else
|
||||
#include "serial/impl/unix.h"
|
||||
#endif
|
||||
|
||||
using std::invalid_argument;
|
||||
using std::min;
|
||||
using std::numeric_limits;
|
||||
using std::vector;
|
||||
using std::size_t;
|
||||
using std::string;
|
||||
|
||||
using serial::Serial;
|
||||
using serial::SerialExecption;
|
||||
using serial::IOException;
|
||||
using serial::bytesize_t;
|
||||
using serial::parity_t;
|
||||
using serial::stopbits_t;
|
||||
using serial::flowcontrol_t;
|
||||
|
||||
class Serial::ScopedReadLock {
|
||||
public:
|
||||
ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) {
|
||||
this->pimpl_->readLock();
|
||||
}
|
||||
~ScopedReadLock() {
|
||||
this->pimpl_->readUnlock();
|
||||
}
|
||||
private:
|
||||
SerialImpl *pimpl_;
|
||||
};
|
||||
|
||||
class Serial::ScopedWriteLock {
|
||||
public:
|
||||
ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) {
|
||||
this->pimpl_->writeLock();
|
||||
}
|
||||
~ScopedWriteLock() {
|
||||
this->pimpl_->writeUnlock();
|
||||
}
|
||||
private:
|
||||
SerialImpl *pimpl_;
|
||||
};
|
||||
|
||||
Serial::Serial (const string &port, unsigned long baudrate, long timeout,
|
||||
bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
|
||||
flowcontrol_t flowcontrol)
|
||||
: read_cache_("")
|
||||
{
|
||||
pimpl_ = new SerialImpl (port, baudrate, timeout, bytesize, parity,
|
||||
stopbits, flowcontrol);
|
||||
}
|
||||
|
||||
Serial::~Serial ()
|
||||
{
|
||||
delete pimpl_;
|
||||
}
|
||||
|
||||
void
|
||||
Serial::open ()
|
||||
{
|
||||
pimpl_->open ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::close ()
|
||||
{
|
||||
pimpl_->close ();
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::isOpen () const
|
||||
{
|
||||
return pimpl_->isOpen ();
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::available ()
|
||||
{
|
||||
return pimpl_->available ();
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::read_ (unsigned char *buffer, size_t size)
|
||||
{
|
||||
return this->pimpl_->read (buffer, size);
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::read (unsigned char *buffer, size_t size)
|
||||
{
|
||||
ScopedReadLock (this->pimpl_);
|
||||
return this->pimpl_->read (buffer, size);
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::read (std::vector<unsigned char> &buffer, size_t size)
|
||||
{
|
||||
ScopedReadLock (this->pimpl_);
|
||||
unsigned char *buffer_ = new unsigned char[size];
|
||||
size_t bytes_read = this->pimpl_->read (buffer_, size);
|
||||
buffer.insert (buffer.end (), buffer_, buffer_+bytes_read);
|
||||
delete[] buffer_;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::read (std::string &buffer, size_t size)
|
||||
{
|
||||
ScopedReadLock (this->pimpl_);
|
||||
unsigned char *buffer_ = new unsigned char[size];
|
||||
size_t bytes_read = this->pimpl_->read (buffer_, size);
|
||||
buffer.append (reinterpret_cast<const char*>(buffer_), bytes_read);
|
||||
delete[] buffer_;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
string
|
||||
Serial::read (size_t size)
|
||||
{
|
||||
std::string buffer;
|
||||
this->read (buffer, size);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::readline (string &buffer, size_t size, string eol)
|
||||
{
|
||||
ScopedReadLock (this->pimpl_);
|
||||
size_t eol_len = eol.length ();
|
||||
unsigned char *buffer_ = static_cast<unsigned char*>
|
||||
(alloca (size * sizeof (unsigned char)));
|
||||
size_t read_so_far = 0;
|
||||
while (true)
|
||||
{
|
||||
size_t bytes_read = this->read_ (buffer_ + read_so_far, 1);
|
||||
read_so_far += bytes_read;
|
||||
if (bytes_read == 0) {
|
||||
break; // Timeout occured on reading 1 byte
|
||||
}
|
||||
if (string (reinterpret_cast<const char*>
|
||||
(buffer_ + read_so_far - eol_len), eol_len) == eol) {
|
||||
break; // EOL found
|
||||
}
|
||||
if (read_so_far == size) {
|
||||
break; // Reached the maximum read length
|
||||
}
|
||||
}
|
||||
buffer.append(reinterpret_cast<const char*> (buffer_), read_so_far);
|
||||
return read_so_far;
|
||||
}
|
||||
|
||||
string
|
||||
Serial::readline (size_t size, string eol)
|
||||
{
|
||||
std::string buffer;
|
||||
this->readline (buffer, size, eol);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
vector<string>
|
||||
Serial::readlines (size_t size, string eol)
|
||||
{
|
||||
ScopedReadLock (this->pimpl_);
|
||||
std::vector<std::string> lines;
|
||||
size_t eol_len = eol.length ();
|
||||
unsigned char *buffer_ = static_cast<unsigned char*>
|
||||
(alloca (size * sizeof (unsigned char)));
|
||||
size_t read_so_far = 0;
|
||||
size_t start_of_line = 0;
|
||||
while (read_so_far < size) {
|
||||
size_t bytes_read = this->read_ (buffer_+read_so_far, 1);
|
||||
read_so_far += bytes_read;
|
||||
if (bytes_read == 0) {
|
||||
if (start_of_line != read_so_far) {
|
||||
lines.push_back (
|
||||
string (reinterpret_cast<const char*> (buffer_ + start_of_line),
|
||||
read_so_far - start_of_line));
|
||||
}
|
||||
break; // Timeout occured on reading 1 byte
|
||||
}
|
||||
if (string (reinterpret_cast<const char*>
|
||||
(buffer_ + read_so_far - eol_len), eol_len) == eol) {
|
||||
// EOL found
|
||||
lines.push_back(
|
||||
string(reinterpret_cast<const char*> (buffer_ + start_of_line),
|
||||
read_so_far - start_of_line));
|
||||
start_of_line = read_so_far;
|
||||
}
|
||||
if (read_so_far == size) {
|
||||
if (start_of_line != read_so_far) {
|
||||
lines.push_back(
|
||||
string(reinterpret_cast<const char*> (buffer_ + start_of_line),
|
||||
read_so_far - start_of_line));
|
||||
}
|
||||
break; // Reached the maximum read length
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
size_t
|
||||
Serial::write (const string &data)
|
||||
{
|
||||
ScopedWriteLock(this->pimpl_);
|
||||
return pimpl_->write (data);
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setPort (const string &port)
|
||||
{
|
||||
ScopedReadLock(this->pimpl_);
|
||||
ScopedWriteLock(this->pimpl_);
|
||||
bool was_open = pimpl_->isOpen ();
|
||||
if (was_open) close();
|
||||
pimpl_->setPort (port);
|
||||
if (was_open) open ();
|
||||
}
|
||||
|
||||
string
|
||||
Serial::getPort () const
|
||||
{
|
||||
return pimpl_->getPort ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setTimeout (long timeout)
|
||||
{
|
||||
pimpl_->setTimeout (timeout);
|
||||
}
|
||||
|
||||
long
|
||||
Serial::getTimeout () const {
|
||||
return pimpl_->getTimeout ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setBaudrate (unsigned long baudrate)
|
||||
{
|
||||
pimpl_->setBaudrate (baudrate);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
Serial::getBaudrate () const
|
||||
{
|
||||
return pimpl_->getBaudrate ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setBytesize (bytesize_t bytesize)
|
||||
{
|
||||
pimpl_->setBytesize (bytesize);
|
||||
}
|
||||
|
||||
bytesize_t
|
||||
Serial::getBytesize () const
|
||||
{
|
||||
return pimpl_->getBytesize ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setParity (parity_t parity)
|
||||
{
|
||||
pimpl_->setParity (parity);
|
||||
}
|
||||
|
||||
parity_t
|
||||
Serial::getParity () const
|
||||
{
|
||||
return pimpl_->getParity ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setStopbits (stopbits_t stopbits)
|
||||
{
|
||||
pimpl_->setStopbits (stopbits);
|
||||
}
|
||||
|
||||
stopbits_t
|
||||
Serial::getStopbits () const
|
||||
{
|
||||
return pimpl_->getStopbits ();
|
||||
}
|
||||
|
||||
void
|
||||
Serial::setFlowcontrol (flowcontrol_t flowcontrol)
|
||||
{
|
||||
pimpl_->setFlowcontrol (flowcontrol);
|
||||
}
|
||||
|
||||
flowcontrol_t
|
||||
Serial::getFlowcontrol () const
|
||||
{
|
||||
return pimpl_->getFlowcontrol ();
|
||||
}
|
||||
|
||||
void Serial::flush ()
|
||||
{
|
||||
ScopedReadLock(this->pimpl_);
|
||||
ScopedWriteLock(this->pimpl_);
|
||||
pimpl_->flush ();
|
||||
read_cache_.clear ();
|
||||
}
|
||||
|
||||
void Serial::flushInput ()
|
||||
{
|
||||
ScopedReadLock(this->pimpl_);
|
||||
pimpl_->flushInput ();
|
||||
}
|
||||
|
||||
void Serial::flushOutput ()
|
||||
{
|
||||
ScopedWriteLock(this->pimpl_);
|
||||
pimpl_->flushOutput ();
|
||||
read_cache_.clear ();
|
||||
}
|
||||
|
||||
void Serial::sendBreak (int duration)
|
||||
{
|
||||
pimpl_->sendBreak (duration);
|
||||
}
|
||||
|
||||
void Serial::setBreak (bool level)
|
||||
{
|
||||
pimpl_->setBreak (level);
|
||||
}
|
||||
|
||||
void Serial::setRTS (bool level)
|
||||
{
|
||||
pimpl_->setRTS (level);
|
||||
}
|
||||
|
||||
void Serial::setDTR (bool level)
|
||||
{
|
||||
pimpl_->setDTR (level);
|
||||
}
|
||||
|
||||
bool Serial::getCTS ()
|
||||
{
|
||||
return pimpl_->getCTS ();
|
||||
}
|
||||
|
||||
bool Serial::getDSR ()
|
||||
{
|
||||
return pimpl_->getDSR ();
|
||||
}
|
||||
|
||||
bool Serial::getRI ()
|
||||
{
|
||||
return pimpl_->getRI ();
|
||||
}
|
||||
|
||||
bool Serial::getCD ()
|
||||
{
|
||||
return pimpl_->getCD ();
|
||||
}
|
||||
466
src/serial.cpp
466
src/serial.cpp
@ -1,466 +0,0 @@
|
||||
#include "serial/serial.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace serial;
|
||||
|
||||
/** Completion Conditions **/
|
||||
|
||||
class transfer_at_least_ignore_invalid_argument {
|
||||
public:
|
||||
typedef bool result_type;
|
||||
|
||||
explicit transfer_at_least_ignore_invalid_argument(std::size_t minimum) : minimum_(minimum) {}
|
||||
|
||||
template <typename Error>
|
||||
bool operator()(const Error& err, std::size_t bytes_transferred) {
|
||||
if(err) {// There is an Error
|
||||
if(err == boost::asio::error::invalid_argument)
|
||||
std::cout << "Invalid Argument Error" << std::endl;
|
||||
if(err == boost::asio::error::operation_aborted) {
|
||||
return 1;
|
||||
}
|
||||
if(err != boost::asio::error::invalid_argument) {// The Error is not invalid argument
|
||||
return 1; // Stop reading
|
||||
}
|
||||
}
|
||||
if(bytes_transferred >= minimum_) {// We have all the bytes we need
|
||||
return 1; // Stop
|
||||
} else {
|
||||
return 0; // Continue
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t minimum_;
|
||||
};
|
||||
|
||||
/** Classes for Handshaking control **/
|
||||
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
# define BOOST_ASIO_OPTION_STORAGE DCB
|
||||
#else
|
||||
# define BOOST_ASIO_OPTION_STORAGE termios
|
||||
#endif
|
||||
|
||||
class DTRControl {
|
||||
public:
|
||||
explicit DTRControl(bool enable = false) : m_enable(enable) {};
|
||||
|
||||
boost::system::error_code store(BOOST_ASIO_OPTION_STORAGE& storage,
|
||||
boost::system::error_code& ec) const
|
||||
{
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
if(m_enable)
|
||||
storage.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
else
|
||||
storage.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
#else
|
||||
ec = boost::asio::error::operation_not_supported;
|
||||
ec = boost::system::error_code();
|
||||
#endif
|
||||
return ec;
|
||||
};
|
||||
|
||||
boost::system::error_code load(const BOOST_ASIO_OPTION_STORAGE& storage,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
if(storage.fDtrControl == DTR_CONTROL_ENABLE)
|
||||
m_enable = true;
|
||||
else
|
||||
m_enable = true;
|
||||
#else
|
||||
#endif
|
||||
return ec;
|
||||
};
|
||||
private:
|
||||
bool m_enable;
|
||||
};
|
||||
|
||||
class RTSControl {
|
||||
public:
|
||||
explicit RTSControl(bool enable = false) : m_enable(enable) {};
|
||||
boost::system::error_code store(BOOST_ASIO_OPTION_STORAGE& storage,
|
||||
boost::system::error_code& ec) const
|
||||
{
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
if(m_enable)
|
||||
storage.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
else
|
||||
storage.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
#else
|
||||
ec = boost::asio::error::operation_not_supported;
|
||||
ec = boost::system::error_code();
|
||||
#endif
|
||||
return ec;
|
||||
};
|
||||
|
||||
boost::system::error_code load(const BOOST_ASIO_OPTION_STORAGE& storage,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
if(storage.fRtsControl == RTS_CONTROL_ENABLE)
|
||||
m_enable = true;
|
||||
else
|
||||
m_enable = true;
|
||||
#else
|
||||
#endif
|
||||
return ec;
|
||||
};
|
||||
private:
|
||||
bool m_enable;
|
||||
};
|
||||
|
||||
/** Serial Class Implementation **/
|
||||
|
||||
Serial::Serial() : io_service(), work(io_service), timeout_timer(io_service) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
Serial::Serial(std::string port,
|
||||
int baudrate,
|
||||
long timeout,
|
||||
bytesize_t bytesize,
|
||||
parity_t parity,
|
||||
stopbits_t stopbits,
|
||||
flowcontrol_t flowcontrol)
|
||||
: io_service(), work(io_service), timeout_timer(io_service)
|
||||
{
|
||||
// Call default constructor to initialize variables
|
||||
this->init();
|
||||
|
||||
// Write provided settings
|
||||
this->port = port;
|
||||
this->setBaudrate(baudrate);
|
||||
this->setTimeoutMilliseconds(timeout);
|
||||
this->setBytesize(bytesize);
|
||||
this->setParity(parity);
|
||||
this->setStopbits(stopbits);
|
||||
this->setFlowcontrol(flowcontrol);
|
||||
|
||||
// Open the serial port
|
||||
this->open();
|
||||
}
|
||||
|
||||
void Serial::init() {
|
||||
// Boost asio variables
|
||||
this->serial_port.reset();
|
||||
|
||||
// Serial Port settings
|
||||
this->port = "";
|
||||
this->setBaudrate(DEFAULT_BAUDRATE);
|
||||
this->setTimeoutMilliseconds(DEFAULT_TIMEOUT);
|
||||
|
||||
// Private variables
|
||||
this->bytes_read = 0;
|
||||
this->bytes_to_read = 0;
|
||||
this->reading = false;
|
||||
this->nonblocking = false;
|
||||
}
|
||||
|
||||
Serial::~Serial() {
|
||||
this->close();
|
||||
}
|
||||
|
||||
void Serial::open() {
|
||||
// Make sure the Serial port is not already open.
|
||||
if(this->serial_port != NULL && this->serial_port->is_open()) {
|
||||
throw(SerialPortAlreadyOpenException(this->port.c_str()));
|
||||
}
|
||||
|
||||
// Try to open the serial port
|
||||
try {
|
||||
this->serial_port.reset(new boost::asio::serial_port(this->io_service, this->port));
|
||||
|
||||
this->serial_port->set_option(this->baudrate);
|
||||
this->serial_port->set_option(this->flowcontrol);
|
||||
this->serial_port->set_option(this->parity);
|
||||
this->serial_port->set_option(this->stopbits);
|
||||
this->serial_port->set_option(this->bytesize);
|
||||
} catch(std::exception &e) {
|
||||
this->serial_port.reset();
|
||||
throw(SerialPortFailedToOpenException(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
bool Serial::isOpen() {
|
||||
if(this->serial_port != NULL)
|
||||
return this->serial_port->is_open();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Serial::close() {
|
||||
// Cancel the current timeout timer and async reads
|
||||
this->timeout_timer.cancel();
|
||||
if(this->serial_port != NULL) {
|
||||
this->serial_port->cancel();
|
||||
this->serial_port->close();
|
||||
this->serial_port.reset();
|
||||
}
|
||||
}
|
||||
|
||||
static const boost::posix_time::time_duration timeout_zero_comparison(boost::posix_time::milliseconds(0));
|
||||
|
||||
int Serial::read(char* buffer, int size) {
|
||||
this->reading = true;
|
||||
if(this->nonblocking) {// Do not wait for data
|
||||
this->serial_port->async_read_some(boost::asio::buffer(buffer, size),
|
||||
boost::bind(&Serial::read_complete, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
} else { // Wait for data until size is read or timeout occurs
|
||||
boost::asio::async_read(*this->serial_port, boost::asio::buffer(buffer, size), transfer_at_least_ignore_invalid_argument(size),
|
||||
boost::bind(&Serial::read_complete, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
if(this->timeout > timeout_zero_comparison) { // Only set a timeout_timer if there is a valid timeout
|
||||
this->timeout_timer.expires_from_now(this->timeout);
|
||||
this->timeout_timer.async_wait(boost::bind(&Serial::timeout_callback, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else if(this->nonblocking) {
|
||||
this->timeout_timer.expires_from_now(boost::posix_time::milliseconds(1));
|
||||
this->timeout_timer.async_wait(boost::bind(&Serial::timeout_callback, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
while(this->reading)
|
||||
this->io_service.run_one();
|
||||
|
||||
this->bytes_to_read = size;
|
||||
|
||||
return this->bytes_read;
|
||||
}
|
||||
|
||||
std::string Serial::read(int size) {
|
||||
char *serial_buffer = new char[size];
|
||||
int bytes_read_ = this->read(serial_buffer, size);
|
||||
std::string return_str(serial_buffer, (std::size_t)bytes_read_);
|
||||
delete[] serial_buffer;
|
||||
return return_str;
|
||||
}
|
||||
|
||||
std::string
|
||||
Serial::read_until(char delim, size_t size) {
|
||||
using namespace std;
|
||||
string r = "";
|
||||
|
||||
while (r.find(delim) == string::npos) {
|
||||
string s = read(1);
|
||||
if (s.length() > 0)
|
||||
r += s;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string
|
||||
Serial::read_until(std::string delim, size_t size) {
|
||||
using namespace std;
|
||||
string r = "";
|
||||
|
||||
while (r.find(delim) == string::npos) {
|
||||
string s = read(1);
|
||||
if (s.length() > 0)
|
||||
r += s;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void Serial::read_complete(const boost::system::error_code& error, std::size_t bytes_transferred) {
|
||||
if(!error || error != boost::asio::error::operation_aborted) { // If there was no error OR the error wasn't operation aborted (canceled), Cancel the timer
|
||||
this->timeout_timer.cancel(); // will cause timeout_callback to fire with an error
|
||||
}
|
||||
|
||||
this->bytes_read = bytes_transferred;
|
||||
|
||||
this->reading = false;
|
||||
}
|
||||
|
||||
void Serial::timeout_callback(const boost::system::error_code& error) {
|
||||
if (!error) {
|
||||
// The timeout wasn't canceled, so cancel the async read
|
||||
this->serial_port->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
int Serial::write(char data[], int length) {
|
||||
return boost::asio::write(*this->serial_port, boost::asio::buffer(data, length), boost::asio::transfer_all());
|
||||
}
|
||||
|
||||
int Serial::write(std::string data) {
|
||||
char *cstr = new char[data.size()+1];
|
||||
std::strcpy(cstr, data.c_str());
|
||||
int bytes_wrote = this->write(cstr, data.length());
|
||||
delete[] cstr;
|
||||
return bytes_wrote;
|
||||
}
|
||||
|
||||
void Serial::setRTS(bool level) {
|
||||
this->serial_port->set_option(RTSControl(level));
|
||||
}
|
||||
|
||||
void Serial::setDTR(bool level) {
|
||||
this->serial_port->set_option(DTRControl(level));
|
||||
}
|
||||
|
||||
bool Serial::getCTS() const {
|
||||
throw(boost::asio::error::operation_not_supported);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Serial::getDSR() const {
|
||||
throw(boost::asio::error::operation_not_supported);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Serial::setPort(std::string port) {
|
||||
this->port = port;
|
||||
}
|
||||
|
||||
std::string Serial::getPort() const {
|
||||
return this->port;
|
||||
}
|
||||
|
||||
void Serial::setTimeoutMilliseconds(long timeout) {
|
||||
// If timeout > 0 then read until size or timeout occurs
|
||||
// If timeout == 0 then read nonblocking, return data available immediately up to size
|
||||
// If timeout < 0 then read blocking, until size is read, period.
|
||||
if(timeout > 0) {
|
||||
this->timeout = boost::posix_time::time_duration(boost::posix_time::milliseconds(timeout));
|
||||
} else {
|
||||
this->timeout = boost::posix_time::time_duration(boost::posix_time::milliseconds(0));
|
||||
}
|
||||
|
||||
if(timeout == 0)
|
||||
this->nonblocking = true;
|
||||
else // Must be negative
|
||||
this->nonblocking = false;
|
||||
}
|
||||
|
||||
long Serial::getTimeoutMilliseconds() const {
|
||||
return this->timeout.total_milliseconds();
|
||||
}
|
||||
|
||||
void Serial::setBaudrate(int baudrate) {
|
||||
this->baudrate = boost::asio::serial_port_base::baud_rate(baudrate);
|
||||
}
|
||||
|
||||
int Serial::getBaudrate() const {
|
||||
return this->baudrate.value();
|
||||
}
|
||||
|
||||
void Serial::setBytesize(bytesize_t bytesize) {
|
||||
switch(bytesize) {
|
||||
case FIVEBITS:
|
||||
this->bytesize = boost::asio::serial_port_base::character_size(5);
|
||||
break;
|
||||
case SIXBITS:
|
||||
this->bytesize = boost::asio::serial_port_base::character_size(6);
|
||||
break;
|
||||
case SEVENBITS:
|
||||
this->bytesize = boost::asio::serial_port_base::character_size(7);
|
||||
break;
|
||||
case EIGHTBITS:
|
||||
this->bytesize = boost::asio::serial_port_base::character_size(8);
|
||||
break;
|
||||
default:
|
||||
throw(InvalidBytesizeException(bytesize));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bytesize_t Serial::getBytesize() const {
|
||||
return bytesize_t(this->bytesize.value());
|
||||
}
|
||||
|
||||
void Serial::setParity(parity_t parity) {
|
||||
switch(parity) {
|
||||
case PARITY_NONE:
|
||||
this->parity = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none);
|
||||
break;
|
||||
case PARITY_ODD:
|
||||
this->parity = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd);
|
||||
break;
|
||||
case PARITY_EVEN:
|
||||
this->parity = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::even);
|
||||
break;
|
||||
default:
|
||||
throw(InvalidParityException(parity));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parity_t Serial::getParity() const {
|
||||
switch(this->parity.value()) {
|
||||
case boost::asio::serial_port_base::parity::none:
|
||||
return parity_t(PARITY_NONE);
|
||||
case boost::asio::serial_port_base::parity::odd:
|
||||
return parity_t(PARITY_ODD);
|
||||
case boost::asio::serial_port_base::parity::even:
|
||||
return parity_t(PARITY_EVEN);
|
||||
default:
|
||||
throw(InvalidParityException(this->parity.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void Serial::setStopbits(stopbits_t stopbits) {
|
||||
switch(stopbits) {
|
||||
case STOPBITS_ONE:
|
||||
this->stopbits = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one);
|
||||
break;
|
||||
case STOPBITS_ONE_POINT_FIVE:
|
||||
this->stopbits = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::onepointfive);
|
||||
break;
|
||||
case STOPBITS_TWO:
|
||||
this->stopbits = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::two);
|
||||
break;
|
||||
default:
|
||||
throw(InvalidStopbitsException(stopbits));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stopbits_t Serial::getStopbits() const {
|
||||
switch(this->stopbits.value()) {
|
||||
case boost::asio::serial_port_base::stop_bits::one:
|
||||
return stopbits_t(STOPBITS_ONE);
|
||||
case boost::asio::serial_port_base::stop_bits::onepointfive:
|
||||
return stopbits_t(STOPBITS_ONE_POINT_FIVE);
|
||||
case boost::asio::serial_port_base::stop_bits::two:
|
||||
return stopbits_t(STOPBITS_TWO);
|
||||
default:
|
||||
throw(InvalidStopbitsException(this->stopbits.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void Serial::setFlowcontrol(flowcontrol_t flowcontrol) {
|
||||
switch(flowcontrol) {
|
||||
case FLOWCONTROL_NONE:
|
||||
this->flowcontrol = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none);
|
||||
break;
|
||||
case FLOWCONTROL_SOFTWARE:
|
||||
this->flowcontrol = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::software);
|
||||
break;
|
||||
case FLOWCONTROL_HARDWARE:
|
||||
this->flowcontrol = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware);
|
||||
break;
|
||||
default:
|
||||
throw(InvalidFlowcontrolException(flowcontrol));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
flowcontrol_t Serial::getFlowcontrol() const {
|
||||
switch(this->flowcontrol.value()) {
|
||||
case boost::asio::serial_port_base::flow_control::none:
|
||||
return flowcontrol_t(FLOWCONTROL_NONE);
|
||||
case boost::asio::serial_port_base::flow_control::software:
|
||||
return flowcontrol_t(FLOWCONTROL_SOFTWARE);
|
||||
case boost::asio::serial_port_base::flow_control::hardware:
|
||||
return flowcontrol_t(FLOWCONTROL_HARDWARE);
|
||||
default:
|
||||
throw(InvalidFlowcontrolException(this->flowcontrol.value()));
|
||||
}
|
||||
}
|
||||
1
tests/proof_of_concepts/mdc2250.cc
Normal file
1
tests/proof_of_concepts/mdc2250.cc
Normal file
@ -0,0 +1 @@
|
||||
#include ""
|
||||
15
tests/proof_of_concepts/python_serial_test.py
Normal file
15
tests/proof_of_concepts/python_serial_test.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import serial, sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "python: Usage_serial_test <port name like: /dev/ttyUSB0>"
|
||||
sys.exit(1)
|
||||
|
||||
sio = serial.Serial(sys.argv[1], 115200)
|
||||
sio.timeout = 250
|
||||
|
||||
while True:
|
||||
sio.write("Testing.")
|
||||
print sio.read(8)
|
||||
|
||||
31
tests/proof_of_concepts/tokenizer.cc
Normal file
31
tests/proof_of_concepts/tokenizer.cc
Normal file
@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
void
|
||||
_delimeter_tokenizer (std::string &data, std::vector<std::string> &tokens,
|
||||
std::string delimeter)
|
||||
{
|
||||
boost::split(tokens, data, boost::is_any_of(delimeter));
|
||||
}
|
||||
|
||||
typedef boost::function<void(std::string&,std::vector<std::string>&)> TokenizerType;
|
||||
|
||||
int main(void) {
|
||||
std::string data = "a\rb\rc\r";
|
||||
std::vector<std::string> tokens;
|
||||
std::string delimeter = "\r";
|
||||
|
||||
TokenizerType f = boost::bind(_delimeter_tokenizer, _1, _2, delimeter);
|
||||
f(data, tokens);
|
||||
|
||||
BOOST_FOREACH(std::string token, tokens)
|
||||
std::cout << token << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
118
tests/serial_tests.cc
Normal file
118
tests/serial_tests.cc
Normal file
@ -0,0 +1,118 @@
|
||||
/* To run these tests you need to change the define below to the serial port
|
||||
* with a loop back device attached.
|
||||
*
|
||||
* Alternatively you could use an Arduino:
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
while (Serial.available() > 0) {
|
||||
Serial.write(Serial.read());
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
// Use FRIEND_TEST... its not as nasty, thats what friends are for
|
||||
// // OMG this is so nasty...
|
||||
// #define private public
|
||||
// #define protected public
|
||||
|
||||
#include "serial/serial.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <pty.h>
|
||||
#else
|
||||
#include <util.h>
|
||||
#endif
|
||||
|
||||
using namespace serial;
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace {
|
||||
|
||||
class SerialTests : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
if (openpty(&master_fd, &slave_fd, name, NULL, NULL) == -1) {
|
||||
perror("openpty");
|
||||
exit(127);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(master_fd > 0);
|
||||
ASSERT_TRUE(slave_fd > 0);
|
||||
ASSERT_TRUE(string(name).length() > 0);
|
||||
|
||||
port1 = new Serial(string(name), 115200, 250);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
port1->close();
|
||||
delete port1;
|
||||
}
|
||||
|
||||
Serial * port1;
|
||||
int master_fd;
|
||||
int slave_fd;
|
||||
char name[100];
|
||||
};
|
||||
|
||||
TEST_F(SerialTests, readWorks) {
|
||||
write(master_fd, "abc\n", 4);
|
||||
string r = port1->read(4);
|
||||
EXPECT_EQ(r, string("abc\n"));
|
||||
}
|
||||
|
||||
TEST_F(SerialTests, writeWorks) {
|
||||
char buf[5] = "";
|
||||
port1->write("abc\n");
|
||||
read(master_fd, buf, 4);
|
||||
EXPECT_EQ(string(buf, 4), string("abc\n"));
|
||||
}
|
||||
|
||||
TEST_F(SerialTests, timeoutWorks) {
|
||||
// Timeout a read, returns an empty string
|
||||
string empty = port1->read();
|
||||
EXPECT_EQ(empty, string(""));
|
||||
|
||||
// Ensure that writing/reading still works after a timeout.
|
||||
write(master_fd, "abc\n", 4);
|
||||
string r = port1->read(4);
|
||||
EXPECT_EQ(r, string("abc\n"));
|
||||
}
|
||||
|
||||
TEST_F(SerialTests, partialRead) {
|
||||
// Write some data, but request more than was written.
|
||||
write(master_fd, "abc\n", 4);
|
||||
|
||||
// Should timeout, but return what was in the buffer.
|
||||
string empty = port1->read(10);
|
||||
EXPECT_EQ(empty, string("abc\n"));
|
||||
|
||||
// Ensure that writing/reading still works after a timeout.
|
||||
write(master_fd, "abc\n", 4);
|
||||
string r = port1->read(4);
|
||||
EXPECT_EQ(r, string("abc\n"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "Unhandled Exception: " << e.what() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user