Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serial connection support for Windows #375

Merged
merged 9 commits into from
May 2, 2018
Merged
3 changes: 3 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ add_library(dronecore ${LIBRARY_TYPE}
tcp_connection.cpp
timeout_handler.cpp
udp_connection.cpp
log.cpp
cli_arg.cpp
)

target_link_libraries(dronecore
Expand Down Expand Up @@ -90,5 +92,6 @@ list(APPEND UNIT_TEST_SOURCES
${CMAKE_SOURCE_DIR}/core/call_every_handler_test.cpp
${CMAKE_SOURCE_DIR}/core/curl_test.cpp
${CMAKE_SOURCE_DIR}/core/any_test.cpp
${CMAKE_SOURCE_DIR}/core/cli_arg_test.cpp
)
set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE)
161 changes: 161 additions & 0 deletions core/cli_arg.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#include "global_include.h"
#include "cli_arg.h"
#include "log.h"
#include <vector>
#include <cctype>
#include <climits>

namespace dronecore {

void CliArg::reset()
{
_protocol = Protocol::NONE;
_path.clear();
_baudrate = 0;
_port = 0;
}

bool CliArg::parse(const std::string &uri)
{
reset();

std::string rest(uri);
if (!find_protocol(rest)) {
return false;
}

if (!find_path(rest)) {
return false;
}

if (_protocol == Protocol::SERIAL) {
if (!find_baudrate(rest)) {
return false;
}
} else {
if (!find_port(rest)) {
return false;
}
}

return true;
}

bool CliArg::find_protocol(std::string &rest)
{
const std::string udp = "udp";
const std::string tcp = "tcp";
const std::string serial = "serial";
const std::string delimiter = "://";

if (rest.find(udp + delimiter) == 0) {
_protocol = Protocol::UDP;
rest.erase(0, udp.length() + delimiter.length());
return true;
} else if (rest.find(tcp + delimiter) == 0) {
_protocol = Protocol::TCP;
rest.erase(0, tcp.length() + delimiter.length());
return true;
} else if (rest.find(serial + delimiter) == 0) {
_protocol = Protocol::SERIAL;
rest.erase(0, serial.length() + delimiter.length());
return true;
} else {
LogWarn() << "Unknown protocol";
return false;
}
}

bool CliArg::find_path(std::string &rest)
{
if (rest.length() == 0) {
if (_protocol == Protocol::UDP || _protocol == Protocol::TCP) {
// We have to use the default path
return true;
} else {
LogWarn() << "Path for serial device required.";
return false;
}
}

const std::string delimiter = ":";
size_t pos = rest.find(delimiter);
if (pos != rest.npos) {
_path = rest.substr(0, pos);
rest.erase(0, pos + delimiter.length());
} else {
_path = rest;
rest = "";
}

if (_protocol == Protocol::SERIAL) {
if (_path.find("/") == 0) {
// A Linux/macOS path starting with '/' is ok.
return true;
} else if (_path.find("COM") == 0) {
// On Windows a path starting with 'COM' is ok but needs to be followed by digits.
if (_path.length() == 3) {
LogWarn() << "COM port number missing";
return false;
}
for (const auto &digit : _path.substr(3, _path.length() - 3)) {
if (!std::isdigit(digit)) {
LogWarn() << "COM port number invalid.";
_path = "";
return false;
}
}
} else {
LogWarn() << "Invalid serial path";
_path = "";
return false;
}
}

return true;
}

bool CliArg::find_port(std::string &rest)
{
if (rest.length() == 0) {
_port = 0;
return true;
}

for (const auto &digit : rest) {
if (!std::isdigit(digit)) {
LogWarn() << "Non-numeric char found in port";
return false;
}
}
_port = std::stoi(rest);
if (_port < 0) {
LogWarn() << "Port can't be negative.";
_port = 0;
return false;
} else if (_port > UINT16_MAX) {
LogWarn() << "Port number to big.";
_port = 0;
return false;
}
return true;
}

bool CliArg::find_baudrate(std::string &rest)
{
if (rest.length() == 0) {
_port = 0;
return true;
}

for (const auto &digit : rest) {
if (!std::isdigit(digit)) {
LogWarn() << "Non-numeric char found in baudrate";
return false;
}
}
_baudrate = std::stoi(rest);
return true;
}

} // namespace dronecore
52 changes: 52 additions & 0 deletions core/cli_arg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include <string>

namespace dronecore {

class CliArg
{
public:
enum class Protocol {
NONE,
UDP,
TCP,
SERIAL
};

bool parse(const std::string &uri);

Protocol get_protocol()
{
return _protocol;
}

int get_port() const
{
return _port;
}

int get_baudrate() const
{
return _baudrate;
}

std::string get_path() const
{
return _path;
}

private:
void reset();
bool find_protocol(std::string &rest);
bool find_path(std::string &rest);
bool find_port(std::string &rest);
bool find_baudrate(std::string &rest);

Protocol _protocol {Protocol::NONE};
std::string _path {};
int _port {0};
int _baudrate {0};
};

} // namespace dronecore
157 changes: 157 additions & 0 deletions core/cli_arg_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include <string>
#include "cli_arg.h"
#include <gtest/gtest.h>

using namespace dronecore;

TEST(CliArg, UDPConnections)
{
CliArg ca;
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::NONE);

ca.parse("udp://127.0.0.1");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "127.0.0.1");
EXPECT_EQ(0, ca.get_port());

ca.parse("udp://");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "");
EXPECT_EQ(0, ca.get_port());

ca.parse("udp://:7");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "");
EXPECT_EQ(7, ca.get_port());

ca.parse("udp://0.0.0.0");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "0.0.0.0");
EXPECT_EQ(0, ca.get_port());

ca.parse("udp://0.0.0.0:7");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "0.0.0.0");
EXPECT_EQ(7, ca.get_port());

ca.parse("udp://localhost:99");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "localhost");
EXPECT_EQ(99, ca.get_port());

ca.parse("udp://example.com");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "example.com");
EXPECT_EQ(0, ca.get_port());

ca.parse("udp://something.local:42");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::UDP);
EXPECT_STREQ(ca.get_path().c_str(), "something.local");
EXPECT_EQ(42, ca.get_port());

// All the wrong combinations.
EXPECT_FALSE(ca.parse(""));
EXPECT_FALSE(ca.parse("udp:/localhost:99"));
EXPECT_FALSE(ca.parse("/udp://localhost:99"));
EXPECT_FALSE(ca.parse("udp:/localhost:99"));
EXPECT_FALSE(ca.parse("udp:localhost:99"));
EXPECT_FALSE(ca.parse("udp//localhost:99"));
EXPECT_FALSE(ca.parse("ud://localhost:99"));
EXPECT_FALSE(ca.parse("udp://0.0.0.0:100000")); // highest is 65535
EXPECT_FALSE(ca.parse("udp://0.0.0.0:-5"));
}

TEST(CliArg, TCPConnections)
{
CliArg ca;
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::NONE);

EXPECT_TRUE(ca.parse("tcp://127.0.0.1"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "127.0.0.1");
EXPECT_EQ(0, ca.get_port());

ca.parse("tcp://");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "");
EXPECT_EQ(0, ca.get_port());

ca.parse("tcp://:8");
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "");
EXPECT_EQ(8, ca.get_port());

EXPECT_TRUE(ca.parse("tcp://127.0.0.1:7"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "127.0.0.1");
EXPECT_EQ(7, ca.get_port());

EXPECT_TRUE(ca.parse("tcp://localhost:99"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "localhost");
EXPECT_EQ(99, ca.get_port());

EXPECT_TRUE(ca.parse("tcp://example.com:1234"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "example.com");
EXPECT_EQ(1234, ca.get_port());

EXPECT_TRUE(ca.parse("tcp://something.local:42"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::TCP);
EXPECT_STREQ(ca.get_path().c_str(), "something.local");
EXPECT_EQ(42, ca.get_port());

// All the wrong combinations.
EXPECT_FALSE(ca.parse(""));
EXPECT_FALSE(ca.parse("tcp:/localhost:99"));
EXPECT_FALSE(ca.parse("tcp:/localhost:99"));
EXPECT_FALSE(ca.parse("/tcp://localhost:99"));
EXPECT_FALSE(ca.parse("tcp:localhost:99"));
EXPECT_FALSE(ca.parse("tcp//localhost:99"));
EXPECT_FALSE(ca.parse("tc://localhost:99"));
EXPECT_FALSE(ca.parse("tcp://127.0.0.1:100000")); // highest is 65535
EXPECT_FALSE(ca.parse("tcp://127.0.0.1:-5"));
}

TEST(CliArg, SerialConnections)
{
CliArg ca;
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::NONE);

EXPECT_TRUE(ca.parse("serial:///dev/ttyS0"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::SERIAL);
EXPECT_STREQ(ca.get_path().c_str(), "/dev/ttyS0");
EXPECT_EQ(0, ca.get_baudrate());

EXPECT_TRUE(ca.parse("serial://COM13:57600"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::SERIAL);
EXPECT_STREQ(ca.get_path().c_str(), "COM13");
EXPECT_EQ(57600, ca.get_baudrate());

EXPECT_TRUE(ca.parse("serial:///dev/tty.usbmodem1:115200"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::SERIAL);
EXPECT_STREQ(ca.get_path().c_str(), "/dev/tty.usbmodem1");
EXPECT_EQ(115200, ca.get_baudrate());

EXPECT_TRUE(ca.parse("serial://COM3"));
EXPECT_EQ(ca.get_protocol(), CliArg::Protocol::SERIAL);
EXPECT_STREQ(ca.get_path().c_str(), "COM3");
EXPECT_EQ(0, ca.get_baudrate());

// All the wrong combinations.
EXPECT_FALSE(ca.parse(""));
EXPECT_FALSE(ca.parse("serial///dev/ttyS0"));
EXPECT_FALSE(ca.parse("/serial:///dev/ttyS0"));
EXPECT_FALSE(ca.parse("serial:/dev/ttyS0"));
EXPECT_FALSE(ca.parse("serial://dev/ttyS0"));
EXPECT_FALSE(ca.parse("serial://"));
EXPECT_FALSE(ca.parse("serial://COM"));
EXPECT_FALSE(ca.parse("serial://COM:57600"));
EXPECT_FALSE(ca.parse("seri://COM3"));
EXPECT_FALSE(ca.parse("seri://COM3:57600"));
EXPECT_FALSE(ca.parse("serial://"));
EXPECT_FALSE(ca.parse("serial://SOM3"));
EXPECT_FALSE(ca.parse("serial://SOM3:57600"));
EXPECT_FALSE(ca.parse("serial://COM3:-1"));
}

Loading