From b55d074fdb955444d8cb3fff61f88e72eb2e4eb1 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sat, 21 Nov 2020 17:35:21 -0330 Subject: [PATCH 01/12] =?UTF-8?q?=EF=BB=BFSetup=20initial=20cmake=20and=20?= =?UTF-8?q?easylogging=20for=20FlightComputer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Subsurface/FlightComputer/CMakeLists.txt | 5 ++++- Subsurface/Node/Vendors/placeholder.cpp | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 Subsurface/Node/Vendors/placeholder.cpp diff --git a/Subsurface/FlightComputer/CMakeLists.txt b/Subsurface/FlightComputer/CMakeLists.txt index 175bd9ca..7228d677 100644 --- a/Subsurface/FlightComputer/CMakeLists.txt +++ b/Subsurface/FlightComputer/CMakeLists.txt @@ -3,16 +3,19 @@ project(FlightComputer) set(CMAKE_CXX_STANDARD 17) +include_directories(vendors/asio-1.18.0/include) include_directories(vendors/EasyLogger) include_directories(include) include_directories(src) set(HEADERS include/FlightComputer.h + include/catch.hpp ) set(SOURCES - src/FlightComputer.cpp + src/test.pb.cc + src/test.pb.h vendors/EasyLogger/easylogging++.cc ) diff --git a/Subsurface/Node/Vendors/placeholder.cpp b/Subsurface/Node/Vendors/placeholder.cpp deleted file mode 100644 index 1aa4e703..00000000 --- a/Subsurface/Node/Vendors/placeholder.cpp +++ /dev/null @@ -1 +0,0 @@ -// Intentionally left blank From 985c3ffe4eb39d06750b57012401adaddfa9b3e5 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Tue, 23 Feb 2021 10:03:42 -0330 Subject: [PATCH 02/12] Create message class to represent serialized data, and create TCP and UDP clients The templated message class is to be passed the protobuf class, so that it can be properly serialized and deserialized within the class. The class stores the data as a vector of chars. NetClient contains templated TCP and UDP clients, where the template argument is again the protobuf message type --- Subsurface/FlightComputer/CMakeLists.txt | 2 + Subsurface/FlightComputer/include/Message.h | 72 ++++++ Subsurface/FlightComputer/include/NetClient.h | 222 ++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 Subsurface/FlightComputer/include/Message.h create mode 100644 Subsurface/FlightComputer/include/NetClient.h diff --git a/Subsurface/FlightComputer/CMakeLists.txt b/Subsurface/FlightComputer/CMakeLists.txt index 7228d677..4de2c18d 100644 --- a/Subsurface/FlightComputer/CMakeLists.txt +++ b/Subsurface/FlightComputer/CMakeLists.txt @@ -10,6 +10,8 @@ include_directories(src) set(HEADERS include/FlightComputer.h + include/Message.h + include/NetClient.h include/catch.hpp ) diff --git a/Subsurface/FlightComputer/include/Message.h b/Subsurface/FlightComputer/include/Message.h new file mode 100644 index 00000000..ff186f1a --- /dev/null +++ b/Subsurface/FlightComputer/include/Message.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +// Template parameter must be a protobuf message + +template +class Message +{ +public: + static constexpr int headerLength = sizeof(uint32_t); + static constexpr int maxBodyLength = 1024; + + Message() + : m_bodyLength(0) + { + m_data.resize(256); + } + + Message(const Protobuf& message) + { + m_data.resize(256); + serialize(message); + } + + Message(const Message&) = default; + + std::size_t length() const { return headerLength + m_bodyLength; } + + const char* data() const { return m_data.data(); } + char* data() { return m_data.data(); } + + std::size_t bodyLength() const { return m_bodyLength; } + + const char* body() const { return m_data.data() + headerLength; } + char* body() { return m_data.data() + headerLength; } + + bool decodeHeader() + { + std::memcpy(&m_bodyLength, m_data.data(), headerLength); + if (m_bodyLength > maxBodyLength) + { + m_bodyLength = 0; + return false; + } + return true; + } + + bool serialize(const Protobuf& message) + { + std::string serialized; + bool success = message.SerializeToString(&serialized); + m_bodyLength = serialized.length(); + + std::memcpy(m_data.data(), &m_bodyLength, sizeof(m_bodyLength)); + + m_data.resize(headerLength + m_bodyLength); + std::memcpy(m_data.data() + headerLength, serialized.data(), serialized.length()); + + return success; + } + + bool deserialize(Protobuf& message) const + { + return message.ParseFromString(std::string(body())); + } + +private: + std::vector m_data; + std::size_t m_bodyLength; +}; diff --git a/Subsurface/FlightComputer/include/NetClient.h b/Subsurface/FlightComputer/include/NetClient.h new file mode 100644 index 00000000..b9d2e1c6 --- /dev/null +++ b/Subsurface/FlightComputer/include/NetClient.h @@ -0,0 +1,222 @@ +#pragma once +#include + +#include "Message.h" + +#include + +using asio::ip::tcp; +using asio::ip::udp; + +template +class TcpClient +{ +public: + TcpClient(asio::io_context& ioContext, const tcp::resolver::results_type& endpoints) + : m_ioContext(ioContext), m_socket(ioContext) + { + connect(endpoints); + } + + void write(const Message& message) + { + asio::post(m_ioContext, + [this, message]() + { + bool writeInProgress = !m_writeMessages.empty(); + m_writeMessages.push_back(message); + if (!writeInProgress) + { + internalWrite(); + } + }); + } + + bool connected() const { return m_isConnected; } + + void close() + { + asio::post(m_ioContext, [this]() {m_socket.close(); }); + m_isConnected = false; + } + + const Message& getReadMessage() { return m_readMessage; } + +private: + void connect(const tcp::resolver::results_type& endpoints) + { + asio::async_connect(m_socket, endpoints, + [this, &endpoints](std::error_code ec, Protocol::endpoint) + { + if (!ec) + { + m_isConnected = true; + readHeader(); + } + else + { + std::cout << "Could Not Connect!" << std::endl; + connect(endpoints); + } + }); + } + + void readHeader() + { + asio::async_read(m_socket, + asio::buffer(m_readMessage.data(), Message::headerLength), + [this](std::error_code ec, std::size_t) + { + if (!ec && m_readMessage.decodeHeader()) + { + readBody(); + } + else if (ec) + { + std::cout << ec << std::endl; + } + else + { + readHeader(); + } + }); + } + + void readBody() + { + asio::async_read(m_socket, + asio::buffer(m_readMessage.body(), m_readMessage.bodyLength()), + [this](std::error_code ec, std::size_t) + { + if (!ec) + { + readHeader(); + } + else + { + m_socket.close(); + } + }); + } + + void internalWrite() + { + asio::async_write(m_socket, + asio::buffer(m_writeMessages.front().data(), m_writeMessages.front().length()), + [this](std::error_code ec, std::size_t) + { + if (!ec) + { + m_writeMessages.pop_front(); + if (!m_writeMessages.empty()) + { + internalWrite(); + } + } + else + { + m_socket.close(); + } + }); + } + + asio::io_context& m_ioContext; + tcp::socket m_socket; + Message m_readMessage; + std::deque> m_writeMessages; + bool m_isConnected = false; +}; + + +template +class UdpClient +{ +public: + UdpClient(asio::io_context& ioContext, const udp::resolver::results_type endpoints) + : m_ioContext(ioContext), m_socket(ioContext, udp::endpoint(udp::v4(), 0)), m_endpoint(endpoints.begin()->endpoint()) + { + connect(); + } + + void write(const Message& message) + { + asio::post(m_ioContext, + [this, message]() + { + bool writeInProgress = !m_writeMessages.empty(); + m_writeMessages.push_back(message); + if (!writeInProgress) + { + internalWrite(); + } + }); + } + + void close() + { + asio::post(m_ioContext, [this]() {m_socket.close(); }); + } + + const Message& getReadMessage() { return m_readMessage; } + +private: + void connect() + { + read(); + } + + void read() + { + m_socket.async_receive_from( + asio::buffer(m_readMessage.data(), Message::maxBodyLength), + m_senderEndpoint, + [this](std::error_code ec, std::size_t) + { + if (!ec) + { + std::cout << m_readMessage.data() << std::endl; + Person person; + if (m_readMessage.deserialize(person)) + { + std::cout << "Successfully parsed data!" << std::endl; + std::cout << "Email: " << person.email() << std::endl; + std::cout << "Name: " << person.name() << std::endl; + } + read(); + } + else + { + std::cout << ec << std::endl; + read(); + } + }); + } + + void internalWrite() + { + m_socket.async_send_to( + asio::buffer(m_writeMessages.front().data(), m_writeMessages.front().length()), + m_endpoint, + [this](std::error_code ec, std::size_t) + { + if (!ec) + { + m_writeMessages.pop_front(); + if (!m_writeMessages.empty()) + { + internalWrite(); + } + } + else + { + m_socket.close(); + } + }); + } + asio::io_context& m_ioContext; + udp::socket m_socket; + Message m_readMessage; + std::deque> m_writeMessages; + const udp::endpoint m_endpoint; + udp::endpoint m_senderEndpoint; +}; From 56a9396976a4e96efb22cdd0228c239acc668215 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Tue, 23 Feb 2021 10:22:26 -0330 Subject: [PATCH 03/12] Use FlightComputer.cpp for UDP client echo with python script --- .../FlightComputer/include/FlightComputer.h | 4 ++- .../FlightComputer/src/FlightComputer.cpp | 36 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Subsurface/FlightComputer/include/FlightComputer.h b/Subsurface/FlightComputer/include/FlightComputer.h index bde888c1..9506bdc7 100644 --- a/Subsurface/FlightComputer/include/FlightComputer.h +++ b/Subsurface/FlightComputer/include/FlightComputer.h @@ -1,4 +1,6 @@ #ifndef FLIGHTCOMPUTER_H -#define FLIGHTCOMPUTER_H + +#define FLIGHTCOMPUTER_H +#include #endif diff --git a/Subsurface/FlightComputer/src/FlightComputer.cpp b/Subsurface/FlightComputer/src/FlightComputer.cpp index 27490689..f090efb1 100644 --- a/Subsurface/FlightComputer/src/FlightComputer.cpp +++ b/Subsurface/FlightComputer/src/FlightComputer.cpp @@ -1,11 +1,41 @@ -#include "easylogging++.h" #include "FlightComputer.h" +#include "Message.h" +#include "NetClient.h" +#include "test.pb.h" + +#include "easylogging++.h" +#include +#include INITIALIZE_EASYLOGGINGPP +using asio::ip::tcp; +using asio::ip::udp; + int main(int argc, char* argv[]) { - LOG(INFO) << "Hello World"; + asio::io_context context; + + udp::resolver resolver(context); + auto endpoints = resolver.resolve(udp::v4(), "127.0.0.1", "10000"); + UdpClient client(context, endpoints); + + std::thread t1([&context]() { context.run(); }); + while (true) + { + static int count = 0; + Person person; + person.set_name("Daniel Burke"); + person.set_email("danielseanburke@gmail.com"); + + Message message(person); + client.write(message); + std::cout << client.getReadMessage().body() << std::endl; + std::this_thread::sleep_for(std::chrono::duration(std::chrono::milliseconds(1000))); + } + + client.close(); + t1.join(); - return 0; + return 0; } From 7feaeba16e8ac12dd4736c2df060397787b770e5 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 4 Apr 2021 14:54:58 -0230 Subject: [PATCH 04/12] Big ol' cmake overhaul for protobuf --- Subsurface/FlightComputer/CMakeLists.txt | 28 +++++++++++++++++-- .../FlightComputer/include/FlightComputer.h | 1 - Subsurface/FlightComputer/include/Message.h | 2 ++ Subsurface/FlightComputer/include/NetClient.h | 6 +++- .../FlightComputer/src/FlightComputer.cpp | 5 ++-- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Subsurface/FlightComputer/CMakeLists.txt b/Subsurface/FlightComputer/CMakeLists.txt index 4de2c18d..834340ac 100644 --- a/Subsurface/FlightComputer/CMakeLists.txt +++ b/Subsurface/FlightComputer/CMakeLists.txt @@ -3,6 +3,16 @@ project(FlightComputer) set(CMAKE_CXX_STANDARD 17) +if (WIN32) + # Get Protobuf Library + find_package(Protobuf CONFIG REQUIRED) +else () + set(Protobuf_INCLUDE_DIR /usr/local/include) + set(Protobuf_LIBRARIES /usr/local/lib/libprotobuf.a) +endif (WIN32) + +include_directories(${Protobuf_INCLUDE_DIR}) + include_directories(vendors/asio-1.18.0/include) include_directories(vendors/EasyLogger) include_directories(include) @@ -12,19 +22,31 @@ set(HEADERS include/FlightComputer.h include/Message.h include/NetClient.h - include/catch.hpp + include/Paradigm.pb.h ) set(SOURCES - src/test.pb.cc - src/test.pb.h + src/Paradigm.pb.cc + src/FlightComputer.cpp vendors/EasyLogger/easylogging++.cc ) +find_package(Threads REQUIRED) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) if (WIN32) add_executable(FlightComputer ${SOURCES} ${HEADERS}) + target_compile_definitions(FlightComputer PUBLIC _WIN32_WINNT=0x0601) + target_compile_definitions(FlightComputer PUBLIC ELPP_WINSOCK2) else () add_executable(FlightComputer ${SOURCES}) endif (WIN32) + +target_include_directories(FlightComputer PRIVATE ${CMAKE_BINARY_DIR}) + +if (WIN32) + target_link_libraries(FlightComputer protobuf::libprotobuf) +else () + target_link_Libraries(FlightComputer ${Protobuf_LIBRARIES} Threads::Threads) +endif (WIN32) diff --git a/Subsurface/FlightComputer/include/FlightComputer.h b/Subsurface/FlightComputer/include/FlightComputer.h index 9506bdc7..2cce09f0 100644 --- a/Subsurface/FlightComputer/include/FlightComputer.h +++ b/Subsurface/FlightComputer/include/FlightComputer.h @@ -2,5 +2,4 @@ #define FLIGHTCOMPUTER_H -#include #endif diff --git a/Subsurface/FlightComputer/include/Message.h b/Subsurface/FlightComputer/include/Message.h index ff186f1a..15b820ca 100644 --- a/Subsurface/FlightComputer/include/Message.h +++ b/Subsurface/FlightComputer/include/Message.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include // Template parameter must be a protobuf message diff --git a/Subsurface/FlightComputer/include/NetClient.h b/Subsurface/FlightComputer/include/NetClient.h index b9d2e1c6..ebd05269 100644 --- a/Subsurface/FlightComputer/include/NetClient.h +++ b/Subsurface/FlightComputer/include/NetClient.h @@ -1,9 +1,12 @@ #pragma once +#define ASIO_STANDALONE #include #include "Message.h" +#include "Paradigm.pb.h" #include +#include using asio::ip::tcp; using asio::ip::udp; @@ -46,7 +49,7 @@ class TcpClient void connect(const tcp::resolver::results_type& endpoints) { asio::async_connect(m_socket, endpoints, - [this, &endpoints](std::error_code ec, Protocol::endpoint) + [this, &endpoints](std::error_code ec, tcp::endpoint) { if (!ec) { @@ -128,6 +131,7 @@ class TcpClient }; +// TODO change to serialize raw protobuf instead of message abstraction template class UdpClient { diff --git a/Subsurface/FlightComputer/src/FlightComputer.cpp b/Subsurface/FlightComputer/src/FlightComputer.cpp index f090efb1..ae30d557 100644 --- a/Subsurface/FlightComputer/src/FlightComputer.cpp +++ b/Subsurface/FlightComputer/src/FlightComputer.cpp @@ -1,7 +1,7 @@ #include "FlightComputer.h" #include "Message.h" #include "NetClient.h" -#include "test.pb.h" +#include "Paradigm.pb.h" #include "easylogging++.h" #include @@ -27,10 +27,11 @@ int main(int argc, char* argv[]) Person person; person.set_name("Daniel Burke"); person.set_email("danielseanburke@gmail.com"); + auto phone = person.add_phones(); Message message(person); client.write(message); - std::cout << client.getReadMessage().body() << std::endl; + LOG(INFO) << client.getReadMessage().body(); std::this_thread::sleep_for(std::chrono::duration(std::chrono::milliseconds(1000))); } From e69ea01fd3e032fcbb9a854bd397c28befb9bb39 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 4 Apr 2021 14:55:57 -0230 Subject: [PATCH 05/12] Fix unit testing cmake and update unit tests themselves --- .../FlightComputer/Tests/CMakeLists.txt | 27 ++++++++----- .../Tests/FlightComputerTest.cpp | 40 ++++++++++++++++--- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/Subsurface/FlightComputer/Tests/CMakeLists.txt b/Subsurface/FlightComputer/Tests/CMakeLists.txt index 142a520f..b9abffec 100644 --- a/Subsurface/FlightComputer/Tests/CMakeLists.txt +++ b/Subsurface/FlightComputer/Tests/CMakeLists.txt @@ -9,27 +9,32 @@ include_directories(../vendors/proto/include) include_directories(../include) include_directories(../src) -set(Proto_Root ${CMAKE_SOURCE_DIR}/../vendors/proto) +if (WIN32) + # Get Protobuf Library + find_package(Protobuf CONFIG REQUIRED) +else () + set(Protobuf_INCLUDE_DIR /usr/local/include) + set(Protobuf_LIBRARIES /usr/local/lib/libprotobuf.a) +endif (WIN32) + +file(GLOB_RECURSE PROTO_SOURCES ../src/*.pb.cc) +file(GLOB_RECURSE HEADERS ../include/*.h ../include/*.hpp ../include/*.pb.h) set(SOURCES - FlightComputerTest.cpp + ../vendors/EasyLogger/easylogging++.cc ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -add_executable(FlightComputerTest ${SOURCES}) +add_executable(FlightComputerTest ${SOURCES} ${PROTO_SOURCES} ${HEADERS} FlightComputerTest.cpp catch.hpp) target_compile_definitions(FlightComputerTest PUBLIC _WIN32_WINNT=0x0601) target_compile_definitions(FlightComputerTest PUBLIC ELPP_WINSOCK2) +target_include_directories(FlightComputerTest PRIVATE ${CMAKE_BINARY_DIR}) + if (WIN32) - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set_property(TARGET FlightComputerTest PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") - target_link_libraries(FlightComputerTest ${Proto_Root}/windows/Debug/libprotobuf-lited.lib) - else () - set_property(TARGET FlightComputerTest PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") - target_link_libraries(FlightComputerTest ${Proto_Root}/windows/Release/libprotobuf-lite.lib) - endif () + target_link_libraries(FlightComputerTest protobuf::libprotobuf) else () - target_link_libraries(FlightComputerTest ${Proto_Root}/linux/libprotobuf-lite.a) + target_link_libraries(FlightComputerTest ${Protobuf_LIBRARIES} Threads::Threads) endif (WIN32) diff --git a/Subsurface/FlightComputer/Tests/FlightComputerTest.cpp b/Subsurface/FlightComputer/Tests/FlightComputerTest.cpp index 687a729b..9326ba6d 100644 --- a/Subsurface/FlightComputer/Tests/FlightComputerTest.cpp +++ b/Subsurface/FlightComputer/Tests/FlightComputerTest.cpp @@ -1,10 +1,40 @@ #define CATCH_CONFIG_MAIN #include "catch.hpp" -TEST_CASE("This is a test unit test") +#include "Message.h" +#include "Paradigm.pb.h" + +#include "easylogging++.h" +#include +#include + +INITIALIZE_EASYLOGGINGPP + +TEST_CASE("Check that sizes are correct in message struct") { - SECTION("This is a test section") - { - REQUIRE(true == true); - } + Person person; + person.set_name("Daniel Burke"); + person.set_email("danielseanburke@gmail.com"); + auto serialized = person.SerializeAsString(); + char size[5]; + sprintf(size, "%4d", static_cast(serialized.length())); + + Message message(person); + + SECTION("Check sizes are equal") + { + REQUIRE(serialized.size() == message.bodyLength()); + REQUIRE(serialized.size() + sizeof(uint32_t) == message.length()); + } + + SECTION("Check contents are equal") + { + REQUIRE(*(serialized.data()) == *(message.body())); + } + + SECTION("Check that protobuf is deserialized correctly") + { + Person person1; + REQUIRE(message.deserialize(person1) == true); + } } From a4c31f57b2d257137a3a4c4e7eee7e55f56cbd55 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 4 Apr 2021 14:56:14 -0230 Subject: [PATCH 06/12] Update README to add new protobuf build instructions --- README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d7c39c53..a29d1b48 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,25 @@ Prerequisites: - Windows - Visual Studio 2019 cl.exe should work fine - Note: Ensure that C++ Cmake tools for Windows is installed along with Desktop development with C++ +Compiling Protobuf: + - Unfortunately compiling protobuf and getting it to link properly can be a bit of a pain, so these instructions are a bit more manual for now + - On Windows: + - The easiest way to install is to use the vcpkg package manager for C++ libraries to install protobuf and the protoc compiler. The instructions [here](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md) under the header "C++ Installation - Windows" will show you how to install the packages under vcpkg, and [here](https://github.com/microsoft/vcpkg#quick-start-windows) shows you how to install vcpkg itself. + - Note that when cmake is invoked for the FlightComputer project, the cmake toolchain file will have to be set so that protobuf is found + - As well, the protoc compiler binary will have to be added to the system PATH, which can be done by adding the path to the folder where vcpkg has installed protoc to the PATH variable, as shown [here](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) + - On Linux/MacOS: + - The Unix build instructions [here](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md) can be used to build from source, just clone the repo somewhere locally and then follow the instructions. If all goes well, protobuf libraries and includes should be added to the proper directories under /usr/local, and protoc should be invokable from the terminal. + Setup: -1. In a command prompt (Windows)/terminal (MacOS/Linux), navigate to `comp1-software/Subsurface/FlightComputer` -2. Make a build directory, and enter it -3. Building for Raspberry pi: -Invoke cmake using `cmake -DCMAKE_TOOLCHAIN_FILE=../arm-linux-gnueabihf-gcc.cmake ..`, this should automatically find the cross compiler (assuming it has been added to your PATH), and generate a Makefile. -Building for everything else: -Invoke cmake using `cmake ..`, this should generate a Visual Studio Solution (Windows), or a Makefile (MacOS/Linux) -4. On Windows: Open the .sln file in Visual Studio, and in the solution explorer set the FlightComputer project as the startup project (right click, "set as startup project"), then build and run. +1. In a command prompt (Windows)/terminal (MacOS/Linux), navigate to `comp1-software/Protobuf` +2. On Windows, run `updateProtos.bat`, and on MacOS/Linux run `updateProtos.sh`. This will compiler the sample `Paradigm.proto` and copy the source files to the correct directories. +3. Navigate to `comp1-software/Subsurface/FlightComputer` +4. Make a build directory, and enter it +5. Building for Raspberry pi: + - Invoke cmake using `cmake -DCMAKE_TOOLCHAIN_FILE=../arm-linux-gnueabihf-gcc.cmake ..`, this should automatically find the cross compiler (assuming it has been added to your PATH), and generate a Makefile. + - Building for Windows: Invoke cmake using `cmake -DCMAKE_TOOLCHAIN_FILE= ..`, this will ensure that the protobuf libraries are picked up correctly. + - Building for MacOS/Linux: Invoke cmake using `cmake ..`, this should generate a Makefile (MacOS/Linux) +6. On Windows: Open the .sln file in Visual Studio, and in the solution explorer set the FlightComputer project as the startup project (right click, "set as startup project"), then build and run. On Linux/MacOS/Arm64 Linux: Run `make`, and this should build the project, and then run the executable with `./FlightComputer` From 2aff027cb199caa8ca073d9a5b55dba1d4330ba7 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 2 May 2021 15:09:28 -0230 Subject: [PATCH 07/12] Fix wording in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a29d1b48..c143f68b 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Prerequisites: Compiling Protobuf: - Unfortunately compiling protobuf and getting it to link properly can be a bit of a pain, so these instructions are a bit more manual for now - On Windows: - - The easiest way to install is to use the vcpkg package manager for C++ libraries to install protobuf and the protoc compiler. The instructions [here](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md) under the header "C++ Installation - Windows" will show you how to install the packages under vcpkg, and [here](https://github.com/microsoft/vcpkg#quick-start-windows) shows you how to install vcpkg itself. + - The easiest way to install is to use the vcpkg package manager for C++ libraries to install protobuf and the protoc compiler. The instructions [here](https://github.com/microsoft/vcpkg#quick-start-windows) shows you how to install vcpkg, and the instructions [here](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md) under the header "C++ Installation - Windows" will show you how to install the packages under vcpkg. - Note that when cmake is invoked for the FlightComputer project, the cmake toolchain file will have to be set so that protobuf is found - As well, the protoc compiler binary will have to be added to the system PATH, which can be done by adding the path to the folder where vcpkg has installed protoc to the PATH variable, as shown [here](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) - On Linux/MacOS: From 575ec32ceae28b86656721cd3f9f91b4895ffe00 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 9 May 2021 12:04:07 -0230 Subject: [PATCH 08/12] Fix bug when decoding received message The null terminator that protobuf was looking for when decoding the message was missing --- Subsurface/FlightComputer/include/Message.h | 2 +- Subsurface/FlightComputer/include/NetClient.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Subsurface/FlightComputer/include/Message.h b/Subsurface/FlightComputer/include/Message.h index 15b820ca..126f8307 100644 --- a/Subsurface/FlightComputer/include/Message.h +++ b/Subsurface/FlightComputer/include/Message.h @@ -65,7 +65,7 @@ class Message bool deserialize(Protobuf& message) const { - return message.ParseFromString(std::string(body())); + return message.ParseFromString(std::string(body(), body() + bodyLength())); } private: diff --git a/Subsurface/FlightComputer/include/NetClient.h b/Subsurface/FlightComputer/include/NetClient.h index ebd05269..7b0db30c 100644 --- a/Subsurface/FlightComputer/include/NetClient.h +++ b/Subsurface/FlightComputer/include/NetClient.h @@ -179,6 +179,7 @@ class UdpClient if (!ec) { std::cout << m_readMessage.data() << std::endl; + m_readMessage.decodeHeader(); Person person; if (m_readMessage.deserialize(person)) { From ee583af9161e6b734d5ee7087c7d444b2305a805 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 9 May 2021 14:27:59 -0230 Subject: [PATCH 09/12] Add tcp and udp server tools tcp_server and udp_server can be used to test protobuf, these servers wait for a protobuf packet to be received, and then they deserialize and ensure that the packet is valid, then echo it back to the source. --- .../tools/net_servers/README.md | 10 +++++ .../tools/net_servers/requirements.txt | 2 + .../tools/net_servers/tcp_server.py | 44 +++++++++++++++++++ .../tools/net_servers/udp_server.py | 31 +++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 Subsurface/FlightComputer/tools/net_servers/README.md create mode 100644 Subsurface/FlightComputer/tools/net_servers/requirements.txt create mode 100644 Subsurface/FlightComputer/tools/net_servers/tcp_server.py create mode 100644 Subsurface/FlightComputer/tools/net_servers/udp_server.py diff --git a/Subsurface/FlightComputer/tools/net_servers/README.md b/Subsurface/FlightComputer/tools/net_servers/README.md new file mode 100644 index 00000000..ba613471 --- /dev/null +++ b/Subsurface/FlightComputer/tools/net_servers/README.md @@ -0,0 +1,10 @@ +# Net Test Servers for Protobuf +This folder contains a TCP and UDP server for testing the functionality of Protobuf. + +## Setup and Utilization +1. Ensure you have a python install on your machine. Invoke the command `python -m venv .env`. This will create a virtual environment where protobuf can be installed +2. Activate the virtual environment with the following command: + - Windows: `.\.env\Scripts\activate` + - MacOS/Linux: `source /.env/bin/activate` +3. Run the UpdateProtos.bat/.sh script in the root of the repository. This will add the compiled protobuf python module required to run the scripts to this directory +4. Run either tcp_server.py or udp_server.py with the virtual environment active. This will receive a basic protobuf Person message, attempt to deserialize it, and then reserialize and echo it back. diff --git a/Subsurface/FlightComputer/tools/net_servers/requirements.txt b/Subsurface/FlightComputer/tools/net_servers/requirements.txt new file mode 100644 index 00000000..be13979c --- /dev/null +++ b/Subsurface/FlightComputer/tools/net_servers/requirements.txt @@ -0,0 +1,2 @@ +protobuf==3.14.0 +six==1.15.0 diff --git a/Subsurface/FlightComputer/tools/net_servers/tcp_server.py b/Subsurface/FlightComputer/tools/net_servers/tcp_server.py new file mode 100644 index 00000000..e28d0c38 --- /dev/null +++ b/Subsurface/FlightComputer/tools/net_servers/tcp_server.py @@ -0,0 +1,44 @@ +import socket +import time +import Paradigm_pb2 + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + receivedInfo = Paradigm_pb2.Person() + + server_address = ('127.0.0.1', 10000) + sock.bind(server_address) + + sock.listen(1) + + while True: + print('waiting for a connection!') + connection, client_address = sock.accept() + try: + print('connection_from {}'.format(client_address)) + + while True: + data = connection.recv(4) + messageLength = int.from_bytes(data, 'little') + messageData = connection.recv(messageLength) + print('received {} bytes'.format(messageLength)) + + if messageData: + if receivedInfo.ParseFromString(messageData): + print('parsed data!!!') + print('Name: {}'.format(receivedInfo.name)) + print('Email: {}'.format(receivedInfo.email)) + print('echoeing back') + returnMessage = messageLength.to_bytes(4, 'little') + receivedInfo.SerializeToString() + connection.send(returnMessage) + else: + print('invalid data!') + break + else: + print('no more data from {}'.format(client_address)) + break + finally: + connection.close() + +if __name__ == '__main__': + main() diff --git a/Subsurface/FlightComputer/tools/net_servers/udp_server.py b/Subsurface/FlightComputer/tools/net_servers/udp_server.py new file mode 100644 index 00000000..bd1f6aec --- /dev/null +++ b/Subsurface/FlightComputer/tools/net_servers/udp_server.py @@ -0,0 +1,31 @@ +import socket +import time +import Paradigm_pb2 + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + receivedInfo = Paradigm_pb2.Person() + + server_address = ('localhost', 10000) + sock.bind(server_address) + + while True: + data, sender_info = sock.recvfrom(256) + print(data[:4]) + messageLength = int.from_bytes(data[:4], 'little') + print('received {} bytes'.format(messageLength)) + messageData = data[4:4 + messageLength] + + if receivedInfo.ParseFromString(messageData): + print('parsed data!!!') + print('Name: {}'.format(receivedInfo.name)) + print('Email: {}'.format(receivedInfo.email)) + print('echoeing back') + returnMessage = messageLength.to_bytes(4, 'little') + receivedInfo.SerializeToString() + sock.sendto(returnMessage, sender_info) + else: + print('invalid data!') + break + +if __name__ == '__main__': + main() From 4717cc400b9c4bb0d3f68b02c1e9fd3735214472 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 9 May 2021 14:30:18 -0230 Subject: [PATCH 10/12] Copy compiled protobuf python module to net_server Copies Paradigm_pb2.py to the net_servers directory for use by the tcp_server and udp_server tools. --- Protobuf/updateProtos.bat | 3 +++ Protobuf/updateProtos.sh | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Protobuf/updateProtos.bat b/Protobuf/updateProtos.bat index b5e529a3..04b5dbc9 100644 --- a/Protobuf/updateProtos.bat +++ b/Protobuf/updateProtos.bat @@ -30,6 +30,7 @@ SET flightComputerSrc="..\Subsurface\FlightComputer\src" SET PDS="..\OnLand\PDS" SET FLASK="..\OnLand\ControlLaptop" +SET TESTTOOLS="..\Subsurface\FlightComputer\tools\net_servers" REM Force copy the files, replacing existing ones REM /Y - Suppress confirm prompt @@ -40,5 +41,7 @@ COPY /Y Paradigm\cpp\*.pb.cc %flightComputerSrc% COPY /Y Paradigm\py\* %PDS% COPY /Y Paradigm\py\* %FLASK% +COPY /Y Paradigm\py\* %TESTTOOLS% + echo Proto Update Complete diff --git a/Protobuf/updateProtos.sh b/Protobuf/updateProtos.sh index 73df6975..ada6241e 100755 --- a/Protobuf/updateProtos.sh +++ b/Protobuf/updateProtos.sh @@ -27,6 +27,7 @@ flightComputerSrc="../Subsurface/FlightComputer/src" PDS="../OnLand/PDS" FLASK="../OnLand/ControlLaptop" +TESTTOOLS="..\Subsurface\FlightComputer\tools\net_servers" # Force copy the files, replacing existing ones cp -f Paradigm/cpp/*.pb.h $flightComputer @@ -34,5 +35,6 @@ cp -f Paradigm/cpp/*.pb.cc $flightComputerSrc cp -f Paradigm/py/* $PDS cp -f Paradigm/py/* $FLASK +cp -f Paradigm/py/* $TESTTOOLS echo "Proto Update Complete" From a31514f4e1eaecc632bffbf75aff3cac0cc6eccc Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 9 May 2021 14:31:26 -0230 Subject: [PATCH 11/12] Add code for testing TCP client Runs an identical test case to the existing UDP client code, and utilizes the tcp_server python tool to echo a Person Protobuf message back and forth. --- Subsurface/FlightComputer/include/NetClient.h | 9 +++++++ .../FlightComputer/src/FlightComputer.cpp | 25 +++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Subsurface/FlightComputer/include/NetClient.h b/Subsurface/FlightComputer/include/NetClient.h index 7b0db30c..172f430f 100644 --- a/Subsurface/FlightComputer/include/NetClient.h +++ b/Subsurface/FlightComputer/include/NetClient.h @@ -93,6 +93,15 @@ class TcpClient { if (!ec) { + std::cout << m_readMessage.data() << std::endl; + m_readMessage.decodeHeader(); + Person person; + if (m_readMessage.deserialize(person)) + { + std::cout << "Successfully parsed data!" << std::endl; + std::cout << "Email: " << person.email() << std::endl; + std::cout << "Name: " << person.name() << std::endl; + } readHeader(); } else diff --git a/Subsurface/FlightComputer/src/FlightComputer.cpp b/Subsurface/FlightComputer/src/FlightComputer.cpp index ae30d557..520b8f6d 100644 --- a/Subsurface/FlightComputer/src/FlightComputer.cpp +++ b/Subsurface/FlightComputer/src/FlightComputer.cpp @@ -16,7 +16,8 @@ int main(int argc, char* argv[]) { asio::io_context context; - udp::resolver resolver(context); + // UDP Test + /*udp::resolver resolver(context); auto endpoints = resolver.resolve(udp::v4(), "127.0.0.1", "10000"); UdpClient client(context, endpoints); @@ -33,8 +34,28 @@ int main(int argc, char* argv[]) client.write(message); LOG(INFO) << client.getReadMessage().body(); std::this_thread::sleep_for(std::chrono::duration(std::chrono::milliseconds(1000))); - } + }*/ + + // TCP Test + tcp::resolver resolver(context); + auto endpoints = resolver.resolve(tcp::v4(), "127.0.0.1", "10000"); + TcpClient client(context, endpoints); + + std::thread t1([&context]() { context.run(); }); + while (true) + { + static int count = 0; + Person person; + person.set_name("Daniel Burke"); + person.set_email("danielseanburke@gmail.com"); + auto phone = person.add_phones(); + Message message(person); + client.write(message); + LOG(INFO) << client.getReadMessage().body(); + std::this_thread::sleep_for(std::chrono::duration(std::chrono::milliseconds(1000))); + } + client.close(); t1.join(); From 226aa6c6fbaefb5244292fcfc6ae4f31781942d2 Mon Sep 17 00:00:00 2001 From: Daniel Burke Date: Sun, 16 May 2021 16:26:37 -0230 Subject: [PATCH 12/12] Add step for installing packages with requirements.txt --- Subsurface/FlightComputer/tools/net_servers/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Subsurface/FlightComputer/tools/net_servers/README.md b/Subsurface/FlightComputer/tools/net_servers/README.md index ba613471..ac1be346 100644 --- a/Subsurface/FlightComputer/tools/net_servers/README.md +++ b/Subsurface/FlightComputer/tools/net_servers/README.md @@ -2,9 +2,10 @@ This folder contains a TCP and UDP server for testing the functionality of Protobuf. ## Setup and Utilization -1. Ensure you have a python install on your machine. Invoke the command `python -m venv .env`. This will create a virtual environment where protobuf can be installed +1. Ensure you have a python install on your machine. Invoke the command `python -m venv .venv`. This will create a virtual environment where protobuf can be installed 2. Activate the virtual environment with the following command: - - Windows: `.\.env\Scripts\activate` - - MacOS/Linux: `source /.env/bin/activate` -3. Run the UpdateProtos.bat/.sh script in the root of the repository. This will add the compiled protobuf python module required to run the scripts to this directory -4. Run either tcp_server.py or udp_server.py with the virtual environment active. This will receive a basic protobuf Person message, attempt to deserialize it, and then reserialize and echo it back. + - Windows: `.\.venv\Scripts\activate` + - MacOS/Linux: `source .venv/bin/activate` +3. Run the command `python -m pip install -r requirements.txt`, this will install the protobuf python package into the virtual environment. +4. Run the UpdateProtos.bat/.sh script in the root of the repository. This will add the compiled protobuf python module required to run the scripts to this directory +5. Run either tcp_server.py or udp_server.py with the virtual environment active. This will receive a basic protobuf Person message, attempt to deserialize it, and then reserialize and echo it back.