libcreate/src/serial.cpp
Jacob Perron 2b9591f0f7
Replace boost features with C++11 equivalents (#58)
* Replace boost features with C++11 equivalents

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Include <cmath> in util.h

Needed for std::abs

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2020-11-01 18:05:17 -08:00

201 lines
5 KiB
C++

#include <chrono>
#include <functional>
#include <iostream>
#include "create/serial.h"
#include "create/types.h"
namespace create {
Serial::Serial(std::shared_ptr<Data> d) :
signals(io, SIGINT, SIGTERM),
port(io),
dataReady(false),
isReading(false),
data(d),
corruptPackets(0),
totalPackets(0) {
}
Serial::~Serial() {
disconnect();
}
void Serial::signalHandler(const boost::system::error_code& error, int signal_number) {
if (!error) {
if (connected()) {
// Ensure not in Safe/Full modes
sendOpcode(OC_START);
// Stop OI
sendOpcode(OC_STOP);
exit(signal_number);
}
}
}
bool Serial::connect(const std::string& portName, const int& baud, std::function<void()> cb) {
using namespace boost::asio;
port.open(portName);
port.set_option(serial_port::baud_rate(baud));
port.set_option(serial_port::character_size(8));
port.set_option(serial_port::parity(serial_port::parity::none));
port.set_option(serial_port::stop_bits(serial_port::stop_bits::one));
port.set_option(serial_port::flow_control(serial_port::flow_control::none));
signals.async_wait(std::bind(&Serial::signalHandler, this, std::placeholders::_1, std::placeholders::_2));
usleep(1000000);
if (port.is_open()) {
callback = cb;
bool startReadSuccess = startReading();
if (!startReadSuccess) {
port.close();
}
return startReadSuccess;
}
return false;
}
void Serial::disconnect() {
if (isReading) {
stopReading();
}
if (connected()) {
// Ensure not in Safe/Full modes
sendOpcode(OC_START);
// Stop OI
sendOpcode(OC_STOP);
port.close();
}
}
bool Serial::startReading() {
if (!connected()) return false;
if (!data) {
CERR("[create::Serial] ", "data pointer not initialized.");
return false;
}
// Only allow once
if (isReading) return true;
// Start OI
sendOpcode(OC_START);
if (!startSensorStream()) return false;
io.reset();
// Start continuously reading one byte at a time
boost::asio::async_read(port,
boost::asio::buffer(&byteRead, 1),
std::bind(&Serial::onData,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
ioThread = std::thread(std::bind(
static_cast<std::size_t(boost::asio::io_service::*)(void)>(
&boost::asio::io_service::run), &io));
// Wait for first complete read to finish
std::unique_lock<std::mutex> lock(dataReadyMut);
int attempts = 1;
int maxAttempts = 10;
while (!dataReady) {
if (dataReadyCond.wait_for(lock, std::chrono::milliseconds(500)) == std::cv_status::timeout) {
if (attempts >= maxAttempts) {
CERR("[create::Serial] ", "failed to receive data from Create. Check if robot is powered!");
io.stop();
ioThread.join();
return false;
}
attempts++;
// Request data again
sendOpcode(OC_START);
startSensorStream();
}
}
isReading = true;
return true;
}
void Serial::stopReading() {
if (isReading) {
io.stop();
ioThread.join();
isReading = false;
{
std::lock_guard<std::mutex> lock(dataReadyMut);
dataReady = false;
}
}
}
void Serial::notifyDataReady() {
// Validate all packets
data->validateAll();
// Notify first data packets ready
{
std::lock_guard<std::mutex> lock(dataReadyMut);
if (!dataReady) {
dataReady = true;
dataReadyCond.notify_one();
}
}
// Callback to notify data is ready
if (callback)
callback();
}
void Serial::onData(const boost::system::error_code& e, const std::size_t& size) {
if (e) {
CERR("[create::Serial] ", "serial error - " << e.message());
return;
}
// Should have read exactly one byte
if (size == 1) {
processByte(byteRead);
} // end if (size == 1)
// Read the next byte
boost::asio::async_read(port,
boost::asio::buffer(&byteRead, 1),
std::bind(&Serial::onData,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
}
bool Serial::send(const uint8_t* bytes, unsigned int numBytes) {
if (!connected()) {
CERR("[create::Serial] ", "send failed, not connected.");
return false;
}
// TODO: catch boost exceptions
boost::asio::write(port, boost::asio::buffer(bytes, numBytes));
return true;
}
bool Serial::sendOpcode(const Opcode& code) {
uint8_t oc = (uint8_t) code;
return send(&oc, 1);
}
uint64_t Serial::getNumCorruptPackets() const {
return corruptPackets;
}
uint64_t Serial::getTotalPackets() const {
return totalPackets;
}
} // namespace create