serial_sensor::serial_sensor(size_t size, baud_rate baud)
:
#if defined(__unix__)
_device("/dev/ttyS0"),
#elif defined(_WIN32)
_device("\\\\.\\COM1"),
#endif
_baud(baud), _cs(cs8), _stop_bits(two), _parity(none), _rate(baud_to_rate(baud)) { }
void serial_sensor::open(direction mode)
{
#if defined(__unix__)
// Open the serial port.
if ((_fd = ::open(_device.c_str(),
(mode == input ? O_RDONLY :
mode == output ? O_WRONLY :
O_RDWR) | O_NOCTTY)) == -1) {
throw system_error("open()");
}
// Get the previous port state to restore latter.
if (tcgetattr(_fd, &_previous) == -1) {
::close(_fd);
_fd = -1;
throw system_error("tcgetattr()");
}
#elif defined(_WIN32)
// Open the serial port.
if ((_hfile = CreateFileA(_device.c_str(),
(mode == input ? GENERIC_READ :
mode == output ? GENERIC_WRITE :
GENERIC_READ | GENERIC_WRITE),
0, 0, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
throw win32_error("CreateFile()");
}
// Get the previous port state to restore latter.
memset(&_dcb, 0, sizeof(_dcb));
_dcb.DCBlength = sizeof(_dcb);
if (!GetCommState(_hfile, &_dcb)) {
CloseHandle(_hfile);
_hfile = 0;
throw win32_error("GetCommState()");
}
// Get the previous port timeouts to restore latter.
if (!GetCommTimeouts(_hfile, &_cto)) {
CloseHandle(_hfile);
_hfile = 0;
throw win32_error("GetCommTimeouts()");
}
// Set communications timeouts.
COMMTIMEOUTS cto = _cto; // Initialize to previous timeouts.
cto.ReadIntervalTimeout = 10;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;
cto.WriteTotalTimeoutConstant = 0;
cto.WriteTotalTimeoutMultiplier = 0;
// Set the serial port timeouts.
if (!SetCommTimeouts(_hfile, &cto)) {
throw win32_error("SetCommTimeouts()");
}
#endif
}
void serial_sensor::set(baud_rate baud)
{
#if defined(__unix__)
termios settings = _previous; // Initialize to current settings.
cfmakeraw(&settings); // As if stty raw.
settings.c_cflag &= ~CSIZE; // Clear character size mask.
settings.c_cflag |= _cs; // Set character size.
settings.c_cflag &= ~CSTOPB; // Clear stop bits mask.
settings.c_cflag |= _stop_bits; // Set stop bit size.
settings.c_cflag &= ~(PARENB|PARODD); // Clear parity mask.
settings.c_cflag |= _parity; // Set parity.
// Set baud rate only if not b_default.
if (baud == b_default) baud = _baud;
if (baud != b_default) {
_baud = baud, _rate = baud_to_rate(baud);
cfsetispeed(&settings, _baud);
cfsetospeed(&settings, _baud);
}
// Set the serial port configuration.
if (tcsetattr(_fd, TCSANOW, &settings) == -1) {
throw system_error("tcsetattr()");
}
#elif defined(_WIN32)
DCB dcb = _dcb; // Initialize to previous settings.
dcb.ByteSize = _cs; // Set character size.
dcb.StopBits = _stop_bits; // Set stop bit size.
dcb.Parity = _parity; // Set parity.
// Set baud rate only if not b_default.
if (baud == b_default) baud = _baud;
if (baud != b_default) {
_baud = baud, _rate = baud_to_rate(baud);
dcb.BaudRate = _baud;
}
// Set the serial port configuration.
if (!SetCommState(_hfile, &dcb)) {
throw win32_error("SetCommState()");
}
#endif
}
void serial_sensor::write(const char* p, std::size_t size) const
{
#if defined(__unix__)
while (size > 0) {
ssize_t n = ::write(_fd, p, size);
if (n == -1) throw system_error("write()");
p += n;
size -= n;
}
#elif defined(_WIN32)
while (size > 0) {
DWORD written;
if (!WriteFile(_hfile, p, DWORD(size), &written, 0)) {
throw win32_error("WriteFile()");
}
p += written;
size -= written;
}
#endif
}
std::size_t serial_sensor::read(precision_time to)
{
#if defined(__unix__)
// Prepare arguments for select().
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(_fd, &readfds);
timeval timeout = to;
// Wait for the serial port to be ready for reading.
int n = select(_fd + 1, &readfds, 0, 0, &timeout);
if (n == -1) throw system_error("select()");
// Read data if available.
if (n != 0) {
n = ::read(_fd, _buffer.obeginp(), _buffer.ossize());
if (n == -1) throw system_error("read()");
return size_t(n);
}
else {
return 0;
}
#elif defined(_WIN32)
// Wait for the serial port to be ready for reading.
if (!SetCommMask(_hfile, EV_RXCHAR)) {
throw win32_error("SetCommMask()");
}
DWORD EvtMask = 0;
if (!WaitCommEvent(_hfile, &EvtMask, 0)) {
throw win32_error("WaitCommEvent()");
}
if (!SetCommMask(_hfile, 0)) {
throw win32_error("SetCommMask()");
}
// Read data if available.
DWORD n;
if (!ReadFile(_hfile, _buffer.obeginp(), DWORD(_buffer.ossize()), &n, 0)) {
throw win32_error("ReadFile()");
}
return size_t(n);
#endif
}