├── .codedocs
├── .gitignore
├── async_comm-config.cmake
├── package.xml
├── .github
└── workflows
│ └── ci.yml
├── CHANGELOG.rst
├── LICENSE.md
├── CMakeLists.txt
├── include
└── async_comm
│ ├── util
│ └── message_handler_ros.h
│ ├── tcp_client.h
│ ├── message_handler.h
│ ├── serial.h
│ ├── udp.h
│ └── comm.h
├── examples
├── tcp_client_hello_world.cpp
├── udp_hello_world.cpp
├── serial_loopback.cpp
└── serial_protocol.cpp
├── src
├── tcp_client.cpp
├── serial.cpp
├── udp.cpp
└── comm.cpp
├── ros_prerelease_tests.py
├── README.md
└── Doxyfile
/.codedocs:
--------------------------------------------------------------------------------
1 | DOXYFILE = Doxyfile
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | *.user
4 | .vscode/
5 |
6 | /build/
7 | /doc/
8 |
--------------------------------------------------------------------------------
/async_comm-config.cmake:
--------------------------------------------------------------------------------
1 | get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
2 | include(${SELF_DIR}/async_comm-targets.cmake)
3 | get_filename_component(async_comm_INCLUDE_DIRS "${SELF_DIR}/../../include/async_comm" ABSOLUTE)
4 | set(async_comm_LIBRARIES async_comm)
5 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | async_comm
4 | 0.2.1
5 | A C++ library for asynchronous serial communication
6 |
7 | Daniel Koch
8 | BSD
9 |
10 | https://github.com/dpkoch/async_comm
11 | https://github.com/dpkoch/async_comm/issues
12 |
13 | cmake
14 |
15 | boost
16 | boost
17 |
18 | doxygen
19 |
20 |
21 | cmake
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | env:
10 | BUILD_TYPE: Release
11 | BUILD_EXAMPLES: ON
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 |
20 | - name: Install Boost
21 | run: sudo apt-get install -y libboost-dev libboost-system-dev
22 |
23 | - name: Configure CMake
24 | run: |
25 | cmake -B ${{github.workspace}}/build \
26 | -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
27 | -DASYNC_COMM_BUILD_EXAMPLES=${{env.BUILD_EXAMPLES}}
28 |
29 | - name: Build
30 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
31 |
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 | Changelog for package async_comm
3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 |
5 | 0.2.1 (2021-01-21)
6 | ------------------
7 | * Add noetic to ROS prerelease test distribution list
8 | * Fixes for compatibility with ROS2 workspaces:
9 |
10 | * cmake: Change install paths to basic lib/ and include/
11 | * package.xml: Remove unneeded catkin dependency
12 |
13 | * Updated CMake examples in README
14 | * Contributors: Daniel Koch, Maciej Bogusz
15 |
16 | 0.2.0 (2020-03-16)
17 | ------------------
18 | * Fix for UDP/TCP when loopback is only device with an address
19 | * Added listener interface
20 | * Added custom message handler functionality
21 | * TCPClient class implementing an async tcp client
22 | * Contributors: Daniel Koch, James Jackson, Rein Appeldoorn
23 |
24 | 0.1.1 (2019-02-21)
25 | ------------------
26 | * Some cleanup
27 | * Process callbacks on a separate thread
28 | * Made buffer sizes constant, removed faulty mechanism for overriding
29 | * Removed requirement that bulk write length be no greater than write buffer size
30 | * Contributors: Daniel Koch
31 |
32 | 0.1.0 (2018-08-31)
33 | ------------------
34 | * Initial release
35 | * Contributors: Daniel Koch, James Jackson
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018 Daniel Koch.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(async_comm)
3 |
4 | if (NOT DEFINED CMAKE_BUILD_TYPE)
5 | set(CMAKE_BUILD_TYPE Release)
6 | endif (NOT DEFINED CMAKE_BUILD_TYPE)
7 |
8 | find_package(Boost REQUIRED COMPONENTS system)
9 | find_package(Threads)
10 |
11 | add_library(${PROJECT_NAME} SHARED
12 | src/comm.cpp
13 | src/udp.cpp
14 | src/serial.cpp
15 | src/tcp_client.cpp
16 | )
17 | target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
18 | target_include_directories(${PROJECT_NAME} PUBLIC
19 | $
20 | $
21 | )
22 | target_compile_options(${PROJECT_NAME} PRIVATE -std=c++11)
23 | target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
24 |
25 | # examples
26 | option(ASYNC_COMM_BUILD_EXAMPLES "Build examples" OFF)
27 |
28 | if(ASYNC_COMM_BUILD_EXAMPLES)
29 | add_executable(udp_hello_world examples/udp_hello_world.cpp)
30 | target_link_libraries(udp_hello_world ${PROJECT_NAME})
31 |
32 | add_executable(serial_loopback examples/serial_loopback.cpp)
33 | target_link_libraries(serial_loopback ${PROJECT_NAME})
34 |
35 | add_executable(serial_protocol examples/serial_protocol.cpp)
36 | target_link_libraries(serial_protocol ${PROJECT_NAME})
37 |
38 | add_executable(tcp_client_hello_world examples/tcp_client_hello_world.cpp)
39 | target_link_libraries(tcp_client_hello_world ${PROJECT_NAME})
40 | endif(ASYNC_COMM_BUILD_EXAMPLES)
41 |
42 | install(TARGETS ${PROJECT_NAME}
43 | EXPORT ${PROJECT_NAME}-targets
44 | ARCHIVE DESTINATION lib
45 | LIBRARY DESTINATION lib
46 | )
47 | install(DIRECTORY include/${PROJECT_NAME}
48 | DESTINATION include
49 | FILES_MATCHING PATTERN "*.h"
50 | )
51 |
52 | # install CMake package configuration
53 | install(EXPORT ${PROJECT_NAME}-targets DESTINATION lib/${PROJECT_NAME})
54 | install(FILES ${PROJECT_NAME}-config.cmake DESTINATION lib/${PROJECT_NAME})
55 |
56 | # install package.xml for ROS release
57 | install(FILES package.xml DESTINATION share/${PROJECT_NAME})
58 |
--------------------------------------------------------------------------------
/include/async_comm/util/message_handler_ros.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file message_handler_ros.h
35 | * @author Daniel Koch
36 | */
37 |
38 | #ifndef ASYNC_COMM_MESSAGE_HANDLER_ROS_H
39 | #define ASYNC_COMM_MESSAGE_HANDLER_ROS_H
40 |
41 | #include
42 |
43 | #include
44 |
45 | namespace async_comm
46 | {
47 | namespace util
48 | {
49 |
50 | /**
51 | * @class MessageHandlerROS
52 | * @brief Message handler implementation for ROS environments
53 | *
54 | * This is a convenience message handler implementation for ROS-based projects.
55 | * The implementation simply forwards messages to the appropriate rosconsole loggers.
56 | */
57 | class MessageHandlerROS : public MessageHandler
58 | {
59 | public:
60 | inline void debug(const std::string &message) override { ROS_DEBUG("[async_comm]: %s", message.c_str()); }
61 | inline void info(const std::string &message) override { ROS_INFO("[async_comm]: %s", message.c_str()); }
62 | inline void warn(const std::string &message) override { ROS_WARN("[async_comm]: %s", message.c_str()); }
63 | inline void error(const std::string &message) override { ROS_ERROR("[async_comm]: %s", message.c_str()); }
64 | inline void fatal(const std::string &message) override { ROS_FATAL("[async_comm]: %s", message.c_str()); }
65 | };
66 |
67 | } // namespace util
68 | } // namespace async_comm
69 |
70 | #endif // ASYNC_COMM_MESSAGE_HANDLER_ROS_H
--------------------------------------------------------------------------------
/examples/tcp_client_hello_world.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2019 Rein Appeldoorn.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file tcp_client_hello_world.cpp
35 | * @author Rein Appeldoorn
36 | *
37 | * This example opens a TCP client that sends "hello world" messages.
38 | */
39 |
40 | #include
41 |
42 | #include
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 | #include
49 |
50 |
51 | /**
52 | * @brief Callback function for the async_comm library
53 | *
54 | * Prints the received bytes to stdout.
55 | *
56 | * @param buf Received bytes buffer
57 | * @param len Number of bytes received
58 | */
59 | void callback(const uint8_t* buf, size_t len)
60 | {
61 | for (size_t i = 0; i < len; i++)
62 | {
63 | std::cout << buf[i];
64 | }
65 | }
66 |
67 |
68 | int main()
69 | {
70 | // open TCP connection
71 | async_comm::TCPClient tcp_client("localhost", 16140);
72 | tcp_client.register_receive_callback(&callback);
73 |
74 | if (!tcp_client.init())
75 | {
76 | std::cout << "Failed to initialize TCP client" << std::endl;
77 | return 1;
78 | }
79 |
80 | // send message one direction
81 | for (size_t i = 0; i < 10; ++i)
82 | {
83 | std::string msg = "hello world " + std::to_string(i) + "!";
84 | tcp_client.send_bytes((uint8_t*) msg.data(), msg.size());
85 | std::this_thread::sleep_for(std::chrono::milliseconds(500));
86 | }
87 |
88 | // close connection
89 | tcp_client.close();
90 |
91 | return 0;
92 | }
93 |
--------------------------------------------------------------------------------
/include/async_comm/tcp_client.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2019 Rein Appeldoorn.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file tcp_client.h
35 | * @author Rein Appeldoorn
36 | */
37 |
38 | #ifndef ASYNC_COMM_TCP_CLIENT_H
39 | #define ASYNC_COMM_TCP_CLIENT_H
40 |
41 | #include
42 |
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 |
49 | namespace async_comm
50 | {
51 |
52 | /**
53 | * @class TCPClient
54 | * @brief Asynchronous communication class for a TCP client
55 | */
56 | class TCPClient : public Comm
57 | {
58 | public:
59 | /**
60 | * @brief Connect to a TCP socket as a client
61 | * @param host The host where the TCP server is running
62 | * @param port The port on which the TCP server is listening
63 | * @param message_handler Custom message handler, or omit for default handler
64 | */
65 | TCPClient(std::string host = DEFAULT_HOST, uint16_t port = DEFAULT_PORT,
66 | MessageHandler& message_handler = default_message_handler_);
67 | ~TCPClient();
68 |
69 | private:
70 | static constexpr auto DEFAULT_HOST = "localhost";
71 | static constexpr uint16_t DEFAULT_PORT = 16140;
72 |
73 | bool is_open() override;
74 | bool do_init() override;
75 | void do_close() override;
76 | void do_async_read(const boost::asio::mutable_buffers_1 &buffer,
77 | boost::function handler) override;
78 | void do_async_write(const boost::asio::const_buffers_1 &buffer,
79 | boost::function handler) override;
80 |
81 | std::string host_;
82 | uint16_t port_;
83 |
84 | boost::asio::ip::tcp::socket socket_;
85 | boost::asio::ip::tcp::endpoint endpoint_;
86 | };
87 |
88 | } // namespace async_comm
89 |
90 | #endif // ASYNC_COMM_TCP_CLIENT_H
91 |
--------------------------------------------------------------------------------
/include/async_comm/message_handler.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file message_handler.h
35 | * @author Daniel Koch
36 | */
37 |
38 | #ifndef ASYNC_COMM_MESSAGE_HANDLER_H
39 | #define ASYNC_COMM_MESSAGE_HANDLER_H
40 |
41 | #include
42 | #include
43 |
44 | namespace async_comm
45 | {
46 |
47 | /**
48 | * @class MessageHandler
49 | * @brief Abstract base class for message handler
50 | *
51 | * The implementations of this class define how messages are displayed, logged,
52 | * etc. To create custom behavior, derive from this base class and override the
53 | * pure virtual functions.
54 | */
55 | class MessageHandler
56 | {
57 | public:
58 | virtual void debug(const std::string& message) = 0;
59 | virtual void info(const std::string& message) = 0;
60 | virtual void warn(const std::string& message) = 0;
61 | virtual void error(const std::string& message) = 0;
62 | virtual void fatal(const std::string& message) = 0;
63 | };
64 |
65 | /**
66 | * @class DefaultMessageHandler
67 | * @brief Default message handler that outputs to stdout and stderr
68 | */
69 | class DefaultMessageHandler : public MessageHandler
70 | {
71 | public:
72 | inline void debug(const std::string &message) override { std::cout << "[async_comm][DEBUG]: " << message << std::endl; }
73 | inline void info(const std::string &message) override { std::cout << "[async_comm][INFO]: " << message << std::endl; }
74 | inline void warn(const std::string &message) override { std::cerr << "[async_comm][WARN]: " << message << std::endl; }
75 | inline void error(const std::string &message) override { std::cerr << "[async_comm][ERROR]: " << message << std::endl; }
76 | inline void fatal(const std::string &message) override { std::cerr << "[async_comm][FATAL]: " << message << std::endl; }
77 | };
78 |
79 | } // namespace async_comm
80 |
81 | #endif // ASYNC_COMM_MESSAGE_HANDLER_H
--------------------------------------------------------------------------------
/include/async_comm/serial.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file serial.h
35 | * @author Daniel Koch
36 | */
37 |
38 | #ifndef ASYNC_COMM_SERIAL_H
39 | #define ASYNC_COMM_SERIAL_H
40 |
41 | #include
42 |
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 |
49 | namespace async_comm
50 | {
51 |
52 | /**
53 | * @class Serial
54 | * @brief Asynchronous communication class for a serial port
55 | */
56 | class Serial : public Comm
57 | {
58 | public:
59 | /**
60 | * @brief Open a serial port
61 | * @param port The port to open (e.g. "/dev/ttyUSB0")
62 | * @param baud_rate The baud rate for the serial port (e.g. 115200)
63 | * @param message_handler Custom message handler, or omit for default handler
64 | *
65 | */
66 | Serial(std::string port, unsigned int baud_rate, MessageHandler& message_handler = default_message_handler_);
67 | ~Serial();
68 |
69 |
70 | /**
71 | * @brief Set serial port baud rate
72 | * @param baud_rate The baud rate for the serial port (e.g. 115200)
73 | * @return True if successful
74 | */
75 | bool set_baud_rate(unsigned int baud_rate);
76 |
77 | private:
78 | bool is_open() override;
79 | bool do_init() override;
80 | void do_close() override;
81 | void do_async_read(const boost::asio::mutable_buffers_1 &buffer,
82 | boost::function handler) override;
83 | void do_async_write(const boost::asio::const_buffers_1 &buffer,
84 | boost::function handler) override;
85 |
86 | std::string port_;
87 | unsigned int baud_rate_;
88 |
89 | boost::asio::serial_port serial_port_;
90 | };
91 |
92 | } // namespace async_comm
93 |
94 | #endif // ASYNC_COMM_SERIAL_H
95 |
--------------------------------------------------------------------------------
/src/tcp_client.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2019 Rein Appeldoorn.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file tcp_client.cpp
35 | * @author Rein Appeldoorn
36 | */
37 |
38 | #include
39 |
40 | #include
41 |
42 | using boost::asio::ip::tcp;
43 |
44 | namespace async_comm
45 | {
46 |
47 | TCPClient::TCPClient(std::string host, uint16_t port, MessageHandler& message_handler) :
48 | Comm(message_handler),
49 | host_(host),
50 | port_(port),
51 | socket_(io_service_)
52 | {
53 | }
54 |
55 | TCPClient::~TCPClient()
56 | {
57 | do_close();
58 | }
59 |
60 | bool TCPClient::is_open()
61 | {
62 | return socket_.is_open();
63 | }
64 |
65 | bool TCPClient::do_init()
66 | {
67 | try
68 | {
69 | tcp::resolver resolver(io_service_);
70 |
71 | endpoint_ = *resolver.resolve({tcp::v4(), host_, "", boost::asio::ip::resolver_query_base::numeric_service});
72 | endpoint_.port(port_);
73 | socket_.open(tcp::v4());
74 |
75 | socket_.connect(endpoint_);
76 |
77 | socket_.set_option(tcp::socket::reuse_address(true));
78 | socket_.set_option(tcp::socket::send_buffer_size(WRITE_BUFFER_SIZE*1024));
79 | socket_.set_option(tcp::socket::receive_buffer_size(READ_BUFFER_SIZE*1024));
80 | }
81 | catch (boost::system::system_error e)
82 | {
83 | message_handler_.error(e.what());
84 | return false;
85 | }
86 |
87 | return true;
88 | }
89 |
90 | void TCPClient::do_close()
91 | {
92 | socket_.close();
93 | }
94 |
95 | void TCPClient::do_async_read(const boost::asio::mutable_buffers_1 &buffer,
96 | boost::function handler)
97 | {
98 | socket_.async_receive(buffer, handler);
99 | }
100 |
101 | void TCPClient::do_async_write(const boost::asio::const_buffers_1 &buffer,
102 | boost::function handler)
103 | {
104 | socket_.async_send(buffer, handler);
105 | }
106 |
107 | } // namespace async_comm
108 |
--------------------------------------------------------------------------------
/examples/udp_hello_world.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file udp_hello_world.cpp
35 | * @author Daniel Koch
36 | *
37 | * This example opens two UDP objects listening on different ports on the local host, and then uses each to send a
38 | * simple "hello world" message to the other.
39 | */
40 |
41 | #include
42 |
43 | #include
44 | #include
45 | #include
46 |
47 | #include
48 | #include
49 | #include
50 |
51 |
52 | /**
53 | * @brief Callback function for the async_comm library
54 | *
55 | * Prints the received bytes to stdout.
56 | *
57 | * @param buf Received bytes buffer
58 | * @param len Number of bytes received
59 | */
60 | void callback(const uint8_t* buf, size_t len)
61 | {
62 | for (size_t i = 0; i < len; i++)
63 | {
64 | std::cout << buf[i];
65 | }
66 | }
67 |
68 |
69 | int main()
70 | {
71 | // open UDP ports
72 | async_comm::UDP udp1("localhost", 14620, "localhost", 14625);
73 | udp1.register_receive_callback(&callback);
74 |
75 | async_comm::UDP udp2("localhost", 14625, "localhost", 14620);
76 | udp2.register_receive_callback(&callback);
77 |
78 | if (!udp1.init() || !udp2.init())
79 | {
80 | std::cout << "Failed to initialize UDP ports" << std::endl;
81 | return 1;
82 | }
83 |
84 | // send message one direction
85 | char message1[] = "hello world 1!";
86 | udp2.send_bytes((uint8_t*) message1, std::strlen(message1));
87 |
88 | // wait for all bytes to be received
89 | std::this_thread::sleep_for(std::chrono::milliseconds(500));
90 | std::cout << std::endl << std::flush;
91 |
92 | // send message the other direction
93 | char message2[] = "hello world 2!";
94 | udp1.send_bytes((uint8_t*) message2, std::strlen(message2));
95 |
96 | // wait for all bytes to be received
97 | std::this_thread::sleep_for(std::chrono::milliseconds(500));
98 | std::cout << std::endl << std::flush;
99 |
100 | std::cout.flush();
101 |
102 | // close UDP ports
103 | udp1.close();
104 | udp2.close();
105 |
106 | return 0;
107 | }
108 |
--------------------------------------------------------------------------------
/examples/serial_loopback.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file serial_loopback.cpp
35 | * @author Daniel Koch
36 | *
37 | * This example is designed for use with a USB-to-UART adapter with the RX and TX pins connected together (loopback).
38 | * Sends a series of bytes out and prints them to the console as they are received back.
39 | */
40 |
41 | #include
42 |
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 |
49 | #define NUM_BYTES 64
50 |
51 |
52 | /**
53 | * @brief Callback function for the async_comm library
54 | *
55 | * Prints the received bytes to stdout.
56 | *
57 | * @param buf Received bytes buffer
58 | * @param len Number of bytes received
59 | */
60 | void callback(const uint8_t* buf, size_t len)
61 | {
62 | for (size_t i = 0; i < len; i++)
63 | {
64 | std::printf("Received byte: %d\n", buf[i]);
65 | }
66 | }
67 |
68 |
69 | int main(int argc, char** argv)
70 | {
71 | // initialize
72 | char* port;
73 | if (argc < 2)
74 | {
75 | std::printf("USAGE: %s PORT\n", argv[0]);
76 | return 1;
77 | }
78 | else
79 | {
80 | std::printf("Using port %s\n", argv[1]);
81 | port = argv[1];
82 | }
83 |
84 | // open serial port
85 | async_comm::Serial serial(port, 115200);
86 | serial.register_receive_callback(&callback);
87 |
88 | if (!serial.init())
89 | {
90 | std::printf("Failed to initialize serial port\n");
91 | return 2;
92 | }
93 |
94 | uint8_t buffer[NUM_BYTES];
95 |
96 | // test sending bytes one at a time
97 | std::printf("Transmit individual bytes:\n");
98 | for (uint8_t i = 0; i < NUM_BYTES; i++)
99 | {
100 | buffer[i] = i;
101 | serial.send_byte(i);
102 | }
103 |
104 | // wait for all bytes to be received
105 | std::this_thread::sleep_for(std::chrono::milliseconds(500));
106 |
107 | // test sending all the bytes at once
108 | std::printf("Bulk transmit:\n");
109 | serial.send_bytes(buffer, NUM_BYTES);
110 |
111 | // wait for all bytes to be received
112 | std::this_thread::sleep_for(std::chrono::milliseconds(500));
113 |
114 | // close serial port
115 | serial.close();
116 |
117 | return 0;
118 | }
119 |
--------------------------------------------------------------------------------
/src/serial.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file serial.cpp
35 | * @author Daniel Koch
36 | */
37 |
38 | #include
39 |
40 | #include
41 |
42 | using boost::asio::serial_port_base;
43 |
44 | namespace async_comm
45 | {
46 |
47 | Serial::Serial(std::string port, unsigned int baud_rate, MessageHandler& message_handler) :
48 | Comm(message_handler),
49 | port_(port),
50 | baud_rate_(baud_rate),
51 | serial_port_(io_service_)
52 | {
53 | }
54 |
55 | Serial::~Serial()
56 | {
57 | do_close();
58 | }
59 |
60 | bool Serial::set_baud_rate(unsigned int baud_rate)
61 | {
62 | baud_rate_ = baud_rate;
63 | try
64 | {
65 | serial_port_.set_option(serial_port_base::baud_rate(baud_rate_));
66 | }
67 | catch (boost::system::system_error e)
68 | {
69 | message_handler_.error(e.what());
70 | return false;
71 | }
72 |
73 | return true;
74 | }
75 |
76 | bool Serial::is_open()
77 | {
78 | return serial_port_.is_open();
79 | }
80 |
81 | bool Serial::do_init()
82 | {
83 | try
84 | {
85 | serial_port_.open(port_);
86 | serial_port_.set_option(serial_port_base::baud_rate(baud_rate_));
87 | serial_port_.set_option(serial_port_base::character_size(8));
88 | serial_port_.set_option(serial_port_base::parity(serial_port_base::parity::none));
89 | serial_port_.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));
90 | serial_port_.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none));
91 | }
92 | catch (boost::system::system_error e)
93 | {
94 | message_handler_.error(e.what());
95 | return false;
96 | }
97 |
98 | return true;
99 | }
100 |
101 | void Serial::do_close()
102 | {
103 | serial_port_.close();
104 | }
105 |
106 | void Serial::do_async_read(const boost::asio::mutable_buffers_1 &buffer,
107 | boost::function handler)
108 | {
109 | serial_port_.async_read_some(buffer, handler);
110 | }
111 |
112 | void Serial::do_async_write(const boost::asio::const_buffers_1 &buffer,
113 | boost::function handler)
114 | {
115 | serial_port_.async_write_some(buffer, handler);
116 | }
117 |
118 | } // namespace async_comm
119 |
--------------------------------------------------------------------------------
/include/async_comm/udp.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file udp.h
35 | * @author Daniel Koch
36 | */
37 |
38 | #ifndef ASYNC_COMM_UDP_H
39 | #define ASYNC_COMM_UDP_H
40 |
41 | #include
42 |
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 |
49 | namespace async_comm
50 | {
51 |
52 | /**
53 | * @class UDP
54 | * @brief Asynchronous communication class for a UDP socket
55 | */
56 | class UDP : public Comm
57 | {
58 | public:
59 | /**
60 | * @brief Bind a UDP socket
61 | * @param bind_host The bind host where this application is listening (usually "localhost")
62 | * @param bind_port The bind port where this application is listening
63 | * @param remote_host The remote host to communicate with
64 | * @param remote_port The port on the remote host
65 | * @param message_handler Custom message handler, or omit for default handler
66 | */
67 | UDP(std::string bind_host = DEFAULT_BIND_HOST, uint16_t bind_port = DEFAULT_BIND_PORT,
68 | std::string remote_host = DEFAULT_REMOTE_HOST, uint16_t remote_port = DEFAULT_REMOTE_PORT,
69 | MessageHandler& message_handler = default_message_handler_);
70 | ~UDP();
71 |
72 | private:
73 | static constexpr auto DEFAULT_BIND_HOST = "localhost";
74 | static constexpr uint16_t DEFAULT_BIND_PORT = 16140;
75 | static constexpr auto DEFAULT_REMOTE_HOST = "localhost";
76 | static constexpr uint16_t DEFAULT_REMOTE_PORT = 16145;
77 |
78 | bool is_open() override;
79 | bool do_init() override;
80 | void do_close() override;
81 | void do_async_read(const boost::asio::mutable_buffers_1 &buffer,
82 | boost::function handler) override;
83 | void do_async_write(const boost::asio::const_buffers_1 &buffer,
84 | boost::function handler) override;
85 |
86 | std::string bind_host_;
87 | uint16_t bind_port_;
88 |
89 | std::string remote_host_;
90 | uint16_t remote_port_;
91 |
92 | boost::asio::ip::udp::socket socket_;
93 | boost::asio::ip::udp::endpoint bind_endpoint_;
94 | boost::asio::ip::udp::endpoint remote_endpoint_;
95 | };
96 |
97 | } // namespace async_comm
98 |
99 | #endif // ASYNC_COMM_UDP_H
100 |
--------------------------------------------------------------------------------
/src/udp.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file udp.cpp
35 | * @author Daniel Koch
36 | */
37 |
38 | #include
39 |
40 | #include
41 |
42 | using boost::asio::ip::udp;
43 |
44 | namespace async_comm
45 | {
46 |
47 |
48 | UDP::UDP(std::string bind_host, uint16_t bind_port, std::string remote_host, uint16_t remote_port,
49 | MessageHandler& message_handler) :
50 | Comm(message_handler),
51 | bind_host_(bind_host),
52 | bind_port_(bind_port),
53 | remote_host_(remote_host),
54 | remote_port_(remote_port),
55 | socket_(io_service_)
56 | {
57 | }
58 |
59 | UDP::~UDP()
60 | {
61 | do_close();
62 | }
63 |
64 | bool UDP::is_open()
65 | {
66 | return socket_.is_open();
67 | }
68 |
69 | bool UDP::do_init()
70 | {
71 | try
72 | {
73 | udp::resolver resolver(io_service_);
74 |
75 | bind_endpoint_ = *resolver.resolve({udp::v4(), bind_host_, "",
76 | boost::asio::ip::resolver_query_base::numeric_service});
77 | bind_endpoint_.port(bind_port_);
78 |
79 | remote_endpoint_ = *resolver.resolve({udp::v4(), remote_host_, "",
80 | boost::asio::ip::resolver_query_base::numeric_service});
81 | remote_endpoint_.port(remote_port_);
82 |
83 | socket_.open(udp::v4());
84 | socket_.bind(bind_endpoint_);
85 |
86 | socket_.set_option(udp::socket::reuse_address(true));
87 | socket_.set_option(udp::socket::send_buffer_size(WRITE_BUFFER_SIZE*1024));
88 | socket_.set_option(udp::socket::receive_buffer_size(READ_BUFFER_SIZE*1024));
89 | }
90 | catch (boost::system::system_error e)
91 | {
92 | message_handler_.error(e.what());
93 | return false;
94 | }
95 |
96 | return true;
97 | }
98 |
99 | void UDP::do_close()
100 | {
101 | socket_.close();
102 | }
103 |
104 | void UDP::do_async_read(const boost::asio::mutable_buffers_1 &buffer,
105 | boost::function handler)
106 | {
107 | socket_.async_receive_from(buffer, remote_endpoint_, handler);
108 | }
109 |
110 | void UDP::do_async_write(const boost::asio::const_buffers_1 &buffer,
111 | boost::function handler)
112 | {
113 | socket_.async_send_to(buffer, remote_endpoint_, handler);
114 | }
115 |
116 | } // namespace async_comm
117 |
--------------------------------------------------------------------------------
/src/comm.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file comm.cpp
35 | * @author Daniel Koch
36 | */
37 |
38 | #include
39 |
40 | #include
41 | #include
42 |
43 | namespace async_comm
44 | {
45 |
46 | DefaultMessageHandler Comm::default_message_handler_;
47 |
48 | Comm::Comm(MessageHandler& message_handler) :
49 | message_handler_(message_handler),
50 | io_service_(),
51 | new_data_(false),
52 | shutdown_requested_(false),
53 | write_in_progress_(false)
54 | {
55 | }
56 |
57 | Comm::~Comm()
58 | {
59 | }
60 |
61 | bool Comm::init()
62 | {
63 | if (!do_init())
64 | return false;
65 |
66 | callback_thread_ = std::thread(std::bind(&Comm::process_callbacks, this));
67 |
68 | async_read();
69 | io_thread_ = std::thread(boost::bind(&boost::asio::io_service::run, &this->io_service_));
70 |
71 | return true;
72 | }
73 |
74 | void Comm::close()
75 | {
76 | // send shutdown signal to callback thread
77 | {
78 | std::unique_lock lock(callback_mutex_);
79 | shutdown_requested_ = true;
80 | }
81 | condition_variable_.notify_one();
82 |
83 | io_service_.stop();
84 | do_close();
85 |
86 | if (io_thread_.joinable())
87 | {
88 | io_thread_.join();
89 | }
90 |
91 | if (callback_thread_.joinable())
92 | {
93 | callback_thread_.join();
94 | }
95 | }
96 |
97 | void Comm::send_bytes(const uint8_t *src, size_t len)
98 | {
99 | mutex_lock lock(write_mutex_);
100 |
101 | for (size_t pos = 0; pos < len; pos += WRITE_BUFFER_SIZE)
102 | {
103 | size_t num_bytes = (len - pos) > WRITE_BUFFER_SIZE ? WRITE_BUFFER_SIZE : (len - pos);
104 | write_queue_.emplace_back(src + pos, num_bytes);
105 | }
106 |
107 | async_write(true);
108 | }
109 |
110 | void Comm::register_receive_callback(std::function fun)
111 | {
112 | receive_callback_ = fun;
113 | }
114 |
115 | void Comm::register_listener(CommListener &listener)
116 | {
117 | listeners_.push_back(listener);
118 | }
119 |
120 | void Comm::async_read()
121 | {
122 | if (!is_open()) return;
123 |
124 | do_async_read(boost::asio::buffer(read_buffer_, READ_BUFFER_SIZE),
125 | boost::bind(&Comm::async_read_end,
126 | this,
127 | boost::asio::placeholders::error,
128 | boost::asio::placeholders::bytes_transferred));
129 | }
130 |
131 | void Comm::async_read_end(const boost::system::error_code &error, size_t bytes_transferred)
132 | {
133 | if (error)
134 | {
135 | message_handler_.error(error.message());
136 | close();
137 | return;
138 | }
139 |
140 | {
141 | std::unique_lock lock(callback_mutex_);
142 | read_queue_.emplace_back(read_buffer_, bytes_transferred);
143 | new_data_ = true;
144 | }
145 | condition_variable_.notify_one();
146 |
147 | async_read();
148 | }
149 |
150 | void Comm::async_write(bool check_write_state)
151 | {
152 | if (check_write_state && write_in_progress_)
153 | return;
154 |
155 | mutex_lock lock(write_mutex_);
156 | if (write_queue_.empty())
157 | return;
158 |
159 | write_in_progress_ = true;
160 | WriteBuffer& buffer = write_queue_.front();
161 | do_async_write(boost::asio::buffer(buffer.dpos(), buffer.nbytes()),
162 | boost::bind(&Comm::async_write_end,
163 | this,
164 | boost::asio::placeholders::error,
165 | boost::asio::placeholders::bytes_transferred));
166 | }
167 |
168 | void Comm::async_write_end(const boost::system::error_code &error, size_t bytes_transferred)
169 | {
170 | if (error)
171 | {
172 | message_handler_.error(error.message());
173 | close();
174 | return;
175 | }
176 |
177 | mutex_lock lock(write_mutex_);
178 | if (write_queue_.empty())
179 | {
180 | write_in_progress_ = false;
181 | return;
182 | }
183 |
184 | WriteBuffer& buffer = write_queue_.front();
185 | buffer.pos += bytes_transferred;
186 | if (buffer.nbytes() == 0)
187 | {
188 | write_queue_.pop_front();
189 | }
190 |
191 | if (write_queue_.empty())
192 | write_in_progress_ = false;
193 | else
194 | async_write(false);
195 | }
196 |
197 | void Comm::process_callbacks()
198 | {
199 | std::list local_queue;
200 |
201 | while (true)
202 | {
203 | // wait for either new data or a shutdown request
204 | std::unique_lock lock(callback_mutex_);
205 | condition_variable_.wait(lock, [this]{ return new_data_ || shutdown_requested_; });
206 |
207 | // if shutdown requested, end thread execution
208 | if (shutdown_requested_)
209 | {
210 | break;
211 | }
212 |
213 | // move data to local buffer
214 | local_queue.splice(local_queue.end(), read_queue_);
215 |
216 | // release mutex to allow continued asynchronous read operations
217 | new_data_ = false;
218 | lock.unlock();
219 |
220 | // execute callbacks for all new data
221 | while (!local_queue.empty())
222 | {
223 | ReadBuffer buffer = local_queue.front();
224 | if (receive_callback_)
225 | {
226 | receive_callback_(buffer.data, buffer.len);
227 | }
228 | for (std::reference_wrapper listener_ref : listeners_)
229 | {
230 | listener_ref.get().receive_callback(buffer.data, buffer.len);
231 | }
232 | local_queue.pop_front();
233 | }
234 | }
235 | }
236 |
237 | } // namespace async_comm
238 |
--------------------------------------------------------------------------------
/ros_prerelease_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import subprocess
5 | import sys
6 |
7 | import urllib.request
8 | import yaml
9 |
10 | import argparse
11 |
12 | from termcolor import cprint
13 |
14 |
15 | def print_blue(x): return cprint(x, 'blue', attrs=['bold'])
16 | def print_cyan(x): return cprint(x, 'cyan', attrs=['bold'])
17 | def print_green(x): return cprint(x, 'green', attrs=['bold'])
18 | def print_magenta(x): return cprint(x, 'magenta', attrs=['bold'])
19 | def print_red(x): return cprint(x, 'red', attrs=['bold'])
20 | def print_yellow(x): return cprint(x, 'yellow', attrs=['bold'])
21 |
22 |
23 | REPO_URL = 'https://raw.githubusercontent.com/ros-infrastructure/ros_buildfarm_config'
24 | BRANCH = 'production'
25 | INDEX_FILE = 'index.yaml'
26 |
27 |
28 | def run_test(target, branch):
29 | test_name = "%(ros_distro)s_%(os_distro)s_%(os_release)s_%(arch)s" % target
30 |
31 | config = target
32 | config['branch'] = branch
33 | config['test_name'] = test_name
34 |
35 | # move working directory into test folder
36 | os.mkdir(test_name)
37 | os.chdir(test_name)
38 |
39 | # generate prerelease scripts
40 | print("Generating prerelease scripts...")
41 | generate_prerelease_command = """
42 | generate_prerelease_script.py \\
43 | https://raw.githubusercontent.com/ros-infrastructure/ros_buildfarm_config/production/index.yaml \\
44 | %(ros_distro)s default %(os_distro)s %(os_release)s %(arch)s \\
45 | --custom-repo \\
46 | async_com__custom-1:git:https://github.com/dpkoch/async_comm.git:%(branch)s \\
47 | --level 0 \\
48 | --output-dir ./
49 | """ % config
50 |
51 | with open("../%s-generate.log" % (test_name), 'w') as log_file:
52 | process = subprocess.run(
53 | generate_prerelease_command, shell=True, stdout=log_file, stderr=subprocess.STDOUT)
54 | if process.returncode != 0:
55 | print_yellow("Generating prerelease scripts failed!")
56 | os.chdir('..')
57 | print_red("[Failed]")
58 | return False
59 |
60 | # run prerelease test
61 | print("Running prerelease tests...")
62 | with open("../%s-test.log" % (test_name), 'w') as log_file:
63 | process = subprocess.run(
64 | "./prerelease.sh", stdout=log_file, stderr=subprocess.STDOUT)
65 | os.chdir('..')
66 | if process.returncode == 0:
67 | print_green("[Passed]")
68 | return True
69 | else:
70 | print_red("[Failed]")
71 | return False
72 |
73 |
74 | def get_repo_file(repo_path):
75 | url = '/'.join([REPO_URL, BRANCH, repo_path])
76 | with urllib.request.urlopen(url) as response:
77 | return response.read()
78 |
79 |
80 | def get_prerelease_targets(distros):
81 | targets = []
82 | index_yaml = yaml.safe_load(get_repo_file(INDEX_FILE))
83 | for ros_distro in distros:
84 | config_files = index_yaml['distributions'][ros_distro]['release_builds']
85 | for file_path in config_files.values():
86 | config_yaml = yaml.safe_load(get_repo_file(file_path))
87 | for os_distro in config_yaml['targets']:
88 | for os_release in config_yaml['targets'][os_distro]:
89 | for arch in config_yaml['targets'][os_distro][os_release]:
90 | targets.append({'ros_distro': ros_distro,
91 | 'os_distro': os_distro,
92 | 'os_release': os_release,
93 | 'arch': arch})
94 | return targets
95 |
96 |
97 | def filter_prerelease_targets(targets, os_distro, os_release, arch):
98 | def check_attribute(value, keys):
99 | if len(keys) > 0:
100 | return value in keys
101 | else:
102 | return True
103 |
104 | filtered_targets = []
105 | for target in targets:
106 | if check_attribute(target['os_distro'], os_distro) \
107 | and check_attribute(target['os_release'], os_release) \
108 | and check_attribute(target['arch'], arch):
109 | filtered_targets.append(target)
110 |
111 | return filtered_targets
112 |
113 |
114 | def run_prerelease_tests(args):
115 |
116 | print_magenta("Running all tests for the \"%s\" branch" % (args.branch))
117 |
118 | print()
119 | print_magenta("Running tests for the following ROS distros:")
120 | for distro in args.distro:
121 | print(distro)
122 |
123 | print()
124 | print("Retrieving release targets...")
125 | targets = filter_prerelease_targets(get_prerelease_targets(
126 | args.distro), args.os, args.release, args.arch)
127 |
128 | print()
129 | if len(targets) > 0:
130 | print_magenta("The following release targets will be tested:")
131 | for target in targets:
132 | print("%(ros_distro)s %(os_distro)s %(os_release)s %(arch)s" % target)
133 | else:
134 | print_yellow("No valid prerelease targets!")
135 | return False
136 |
137 | # run all tests and aggregate overall result into exit code
138 | failed_tests = 0
139 | for target in targets:
140 | print()
141 | print_blue(
142 | "Testing %(ros_distro)s %(os_distro)s %(os_release)s %(arch)s" % target)
143 | if not run_test(target, args.branch):
144 | failed_tests += 1
145 |
146 | # print and return overall result
147 | print()
148 | if failed_tests > 0:
149 | print_red("Failed %d of %d tests." % (failed_tests, len(targets)))
150 | return False
151 | else:
152 | print_green("Passed %d tests." % (len(targets)))
153 | return True
154 |
155 |
156 | if __name__ == '__main__':
157 |
158 | # process command line arguments
159 | parser = argparse.ArgumentParser(description='Run ROS prerelease tests')
160 | parser.add_argument(
161 | 'branch', help='The branch for which to run the prerelease tests')
162 | parser.add_argument('--distro',
163 | nargs='*',
164 | type=str,
165 | default=['kinetic', 'melodic', 'noetic'],
166 | help='A list of one or more ROS distros for which to run the prerelease tests (default: %(default)s)')
167 | parser.add_argument('--os',
168 | nargs='*',
169 | type=str,
170 | default=[],
171 | help='A list of one or more OS distros (e.g. ubuntu, debian) for which to run the prerelease tests')
172 | parser.add_argument('--release',
173 | nargs='*',
174 | type=str,
175 | default=[],
176 | help='A list of one or more OS releases (e.g. bionic, stretch) for which to run the prerelease tests')
177 | parser.add_argument('--arch',
178 | nargs='*',
179 | type=str,
180 | default=[],
181 | help='A list of one or more architectures (e.g. amd64, armhf) for which to run the prerelease tests')
182 |
183 | args = parser.parse_args()
184 |
185 | # check that working directory is empty
186 | if os.listdir('.'):
187 | print("Non-empty directory; please run in an empty directory. Aborting.")
188 | exit(3)
189 |
190 | exit_code = 0 if run_prerelease_tests(args) else 1
191 | print_cyan("Exit code: %d" % (exit_code))
192 | exit(exit_code)
193 |
--------------------------------------------------------------------------------
/include/async_comm/comm.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file comm.h
35 | * @author Daniel Koch
36 | */
37 |
38 | #ifndef ASYNC_COMM_COMM_H
39 | #define ASYNC_COMM_COMM_H
40 |
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 |
49 | #include
50 | #include
51 |
52 | #include
53 |
54 | namespace async_comm
55 | {
56 |
57 | /**
58 | * @class CommListener
59 | * @brief Abstract base class for getting comm events via a listener interface
60 | */
61 | class CommListener
62 | {
63 | public:
64 | /**
65 | * @brief Callback for data received
66 | *
67 | * @warning The data buffer passed to the callback function will be invalid after the callback function exits. If you
68 | * want to store the data for later processing, you must copy the data to a new buffer rather than storing the
69 | * pointer to the buffer.
70 | *
71 | * @param buf Address of buffer containing received bytes
72 | * @param size Number of bytes in the receive buffer
73 | */
74 | virtual void receive_callback(const uint8_t * buf, size_t size) = 0;
75 | };
76 |
77 | /**
78 | * @class Comm
79 | * @brief Abstract base class for an asynchronous communication port
80 | */
81 | class Comm
82 | {
83 | public:
84 | /**
85 | * @brief Set up asynchronous communication base class
86 | * @param message_handler Custom message handler, or omit for default handler
87 | */
88 | Comm(MessageHandler& message_handler = default_message_handler_);
89 | virtual ~Comm();
90 |
91 | /**
92 | * @brief Initializes and opens the port
93 | * @return True if the port was succesfully initialized
94 | */
95 | bool init();
96 |
97 | /**
98 | * @brief Closes the port
99 | */
100 | void close();
101 |
102 | /**
103 | * @brief Send bytes from a buffer over the port
104 | * @param src Address of the buffer
105 | * @param len Number of bytes to send
106 | */
107 | void send_bytes(const uint8_t * src, size_t len);
108 |
109 | /**
110 | * @brief Send a single byte over the port
111 | * @param data Byte to send
112 | */
113 | inline void send_byte(uint8_t data) { send_bytes(&data, 1); }
114 |
115 | /**
116 | * @brief Register a callback function for when bytes are received on the port
117 | *
118 | * The callback function needs to accept two parameters. The first is of type `const uint8_t*`, and is a constant
119 | * pointer to the data buffer. The second is of type `size_t`, and specifies the number of bytes available in the
120 | * buffer.
121 | *
122 | * @warning The data buffer passed to the callback function will be invalid after the callback function exits. If you
123 | * want to store the data for later processing, you must copy the data to a new buffer rather than storing the
124 | * pointer to the buffer.
125 | *
126 | * @param fun Function to call when bytes are received
127 | */
128 | void register_receive_callback(std::function fun);
129 |
130 | /**
131 | * @brief Register a listener for when bytes are received on the port
132 | *
133 | * The listener must inherit from CommListener and implement the `receive_callback` function. This is another
134 | * mechanism for receiving data from the Comm interface without needing to create function pointers. Multiple
135 | * listeners can be added and all will get the callback
136 | *
137 | * @param listener Reference to listener
138 | */
139 | void register_listener(CommListener &listener);
140 |
141 | protected:
142 |
143 | static constexpr size_t READ_BUFFER_SIZE = 1024;
144 | static constexpr size_t WRITE_BUFFER_SIZE = 1024;
145 |
146 | static DefaultMessageHandler default_message_handler_;
147 |
148 | virtual bool is_open() = 0;
149 | virtual bool do_init() = 0;
150 | virtual void do_close() = 0;
151 | virtual void do_async_read(const boost::asio::mutable_buffers_1 &buffer,
152 | boost::function handler) = 0;
153 | virtual void do_async_write(const boost::asio::const_buffers_1 &buffer,
154 | boost::function handler) = 0;
155 |
156 | MessageHandler& message_handler_;
157 | boost::asio::io_service io_service_;
158 |
159 | private:
160 |
161 | struct ReadBuffer
162 | {
163 | uint8_t data[READ_BUFFER_SIZE];
164 | size_t len;
165 |
166 | ReadBuffer(const uint8_t * buf, size_t len) : len(len)
167 | {
168 | assert(len <= READ_BUFFER_SIZE); // only checks in debug mode
169 | memcpy(data, buf, len);
170 | }
171 | };
172 |
173 | struct WriteBuffer
174 | {
175 | uint8_t data[WRITE_BUFFER_SIZE];
176 | size_t len;
177 | size_t pos;
178 |
179 | WriteBuffer() : len(0), pos(0) {}
180 |
181 | WriteBuffer(const uint8_t * buf, size_t len) : len(len), pos(0)
182 | {
183 | assert(len <= WRITE_BUFFER_SIZE); // only checks in debug mode
184 | memcpy(data, buf, len);
185 | }
186 |
187 | const uint8_t * dpos() const { return data + pos; }
188 |
189 | size_t nbytes() const { return len - pos; }
190 | };
191 |
192 | typedef std::lock_guard mutex_lock;
193 |
194 | void async_read();
195 | void async_read_end(const boost::system::error_code& error, size_t bytes_transferred);
196 |
197 | void async_write(bool check_write_state);
198 | void async_write_end(const boost::system::error_code& error, size_t bytes_transferred);
199 |
200 | void process_callbacks();
201 |
202 | std::thread io_thread_;
203 | std::thread callback_thread_;
204 |
205 | uint8_t read_buffer_[READ_BUFFER_SIZE];
206 | std::list read_queue_;
207 | std::mutex callback_mutex_;
208 | std::condition_variable condition_variable_;
209 | bool new_data_;
210 | bool shutdown_requested_;
211 |
212 | std::list write_queue_;
213 | std::recursive_mutex write_mutex_;
214 | bool write_in_progress_;
215 |
216 | std::function receive_callback_ = nullptr;
217 | std::vector> listeners_;
218 | };
219 |
220 | } // namespace async_comm
221 |
222 | #endif // ASYNC_COMM_COMM_H
223 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Async Comm Library
2 |
3 | 
4 | [](http://build.ros.org/job/Mdev__async_comm__ubuntu_bionic_amd64)
5 | [](https://codedocs.xyz/dpkoch/async_comm/)
6 |
7 | This project provides a C++ library that gives a simple interface for asynchronous serial communications over a serial port or UDP.
8 | It uses the [Boost.Asio](http://www.boost.org/doc/libs/master/doc/html/boost_asio.html) library under the hood, but hides from the user the details of interfacing with the ports or sockets and managing send/receive buffers.
9 |
10 | ## Including in your project
11 |
12 | There are three ways to use the `async_comm` library in your project:
13 |
14 | 1. If you'll be using the library in a ROS package, install from the ROS repositories
15 | 2. Build and install the library on your system, then use CMake's `find_package()` functionality
16 | 3. Include the async_comm as a submodule in your project
17 |
18 | With the second and third options, you will need to ensure that the Boost library is installed before proceeding:
19 |
20 | ```bash
21 | sudo apt -y install libboost-dev libboost-system-dev
22 | ```
23 |
24 | ### ROS install
25 |
26 | The `async_comm` library is released as a third-party, non-catkin package for ROS following the guidelines in [REP 136](http://www.ros.org/reps/rep-0136.html). To use the library in your ROS package, first install the library from the ROS repositories:
27 |
28 | ```bash
29 | sudo apt install ros--async-comm
30 | ```
31 |
32 | Replace `` with your ROS distribution. The library is currently released for kinetic, lunar, and melodic.
33 |
34 | Then, add something like the following lines to your package's CMakeLists.txt:
35 |
36 | ```CMake
37 | # ...
38 |
39 | find_package(async_comm REQUIRED)
40 |
41 | catkin_package(
42 | # ...
43 | DEPENDS async_comm
44 | )
45 |
46 | # ...
47 |
48 | add_executable(my_node src/my_node.cpp)
49 | target_link_libraries(my_node ${async_comm_LIBRARIES})
50 | ```
51 |
52 | Also be sure to list `async_comm` as a dependency in your package.xml:
53 |
54 | ```XML
55 |
56 |
57 | ...
58 | async_comm
59 | ...
60 |
61 | ```
62 |
63 | ### System install
64 |
65 | First, download and install the library:
66 |
67 | ```bash
68 | git clone https://github.com/dpkoch/async_comm.git
69 | cd async_comm
70 | mkdir build && cd build/
71 | cmake .. && make
72 | sudo make install
73 | ```
74 |
75 | Then, do something like this in your project's CMakeLists.txt:
76 |
77 | ```CMake
78 | cmake_minimum_required(VERSION 3.0.2)
79 | project(my_project)
80 |
81 | find_package(async_comm REQUIRED)
82 |
83 | add_executable(my_project src/my_project.cpp)
84 | target_link_libraries(my_project ${async_comm_LIBRARIES})
85 | ```
86 |
87 | ### Including as a submodule
88 |
89 | If you don't want to go with the ROS or system install options, the next easiest way to embed the `async_comm` library in your project is as a [Git submodule](https://git-scm.com/docs/gitsubmodules). The following instructions are for a project using Git for version control and CMake for a build system, but should serve as a starting point for other setups.
90 |
91 | For example, to put `async_comm` in the `lib/async_comm directory`, run the following from the root of your project:
92 |
93 | ```bash
94 | git submodule add https://github.com/dpkoch/async_comm.git lib/async_comm
95 | ```
96 |
97 | Your CMakeLists.txt file would then look something like this:
98 |
99 | ```CMake
100 | cmake_minimum_required(VERSION 3.0.2)
101 | project(my_project)
102 |
103 | add_subdirectory(lib/async_comm)
104 |
105 | add_executable(my_project src/my_project.cpp)
106 | target_link_libraries(my_project async_comm)
107 | ```
108 |
109 | ## Usage
110 |
111 | There are three classes that you'll use directly as a user:
112 |
113 | - `async_comm::Serial`: for communication over a serial port
114 | - `async_comm::UDP`: for communication over a UDP socket
115 | - `async_comm::TCPClient`: for communication over a TCP socket (client)
116 |
117 | All classes have the same interface and inherit from the `async_comm::Comm` base class.
118 | The constructors for each class require the arguments to specify the details of the serial port, UDP or TCP socket.
119 |
120 | The interface consists of the following functions:
121 |
122 | - `bool init()`: initializes and opens the port or socket
123 | - `void register_receive_callback(std::function fun)`: register a user-defined function to handle received bytes; this function will be called by the `Serial`, `UDP` or `TCPClient` object every time a new data is received
124 | - `void send_bytes(const uint8_t * src, size_t len)`: send the specified number of bytes from the specified source buffer
125 | - `void close()`: close the port or socket
126 |
127 | More details can be found in the [code API documentation](https://codedocs.xyz/dpkoch/async_comm/).
128 | Very simple example programs are provided to illustrate the usage as described below.
129 |
130 | One tricky part is registering the member function of a class as the receive callback. This is accomplished using `std::bind`. For example, if I want to register the `receive` function of `MyClass` from within the class, I would use
131 |
132 | ```C++
133 | serial_.register_receive_callback(std::bind(&MyClass::receive, this, std::placeholders::_1, std::placeholders::_2));
134 | ```
135 |
136 | where `serial_` is an instance of `async_comm::Serial`.
137 |
138 | ## Message Handlers
139 |
140 | It is possible to implement custom handlers for the error messages and other messages produced by the library. To create a message handler, simply inherit from the `MessageHandler` abstract base class defined in `include/async_comm/message_handler.h`, and override the pure virtual functions.
141 |
142 | Each of the user-facing classes accepts, as an optional final argument, a reference to a class that derives from `MessageHandler`. To use a custom message handler, simply create an instance of your handler and pass it as that optional argument. When that argument is omitted, the library uses a default message handler that prints to `stdout` and `stderr`.
143 |
144 | A custom message handler can be especially useful, for example, when the library is used as part of a ROS node and you wish to forward the error messages to the rosconsole logging functionality. A convenience class `MessageHandlerROS` has been provided for this purpose. To use this handler, do something like the following:
145 |
146 | ```C++
147 | #include
148 | #include
149 |
150 | #include
151 |
152 | // ...
153 |
154 | async_comm::util::MessageHandlerROS rosconsole_handler;
155 | async_comm::Serial serial("/dev/ttyUSB0", 115200, rosconsole_handler);
156 |
157 | // ...
158 | ```
159 |
160 | ## Examples
161 |
162 | There are three examples provided in the repository. The first two are very simple, while the third is more complete. To build the examples, run CMake with the `-DASYNC_COMM_BUILD_EXAMPLES=ON` flag.
163 |
164 | - `examples/serial_loopback.cpp`: Designed for use with a USB-to-UART adapter with the RX and TX pins connected together (loopback). Sends a series of bytes out and prints them to the console as they are received back.
165 | - `examples/udp_hello_world.cpp`: Opens two UDP objects listening on different ports on the local host, and then uses each to send a simple "hello world" message to the other.
166 | - `examples/serial_protocol.cpp`: Implements a simple serial protocol and parser for a message that includes two integer values, including a cyclic reduncancy check. Tests the protocol and `async_comm` library over a serial loopback.
167 | - `examples/tcp_client_hello_world.cpp`: Opens a TCP client that sends "hello world" messages. Example only runs with a valid running TCP/IP server. Server can be started using [netcat](https://en.wikipedia.org/wiki/Netcat): `nc -l 16140`.
168 |
--------------------------------------------------------------------------------
/examples/serial_protocol.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD-3 License)
3 | *
4 | * Copyright (c) 2018 Daniel Koch.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice, this
11 | * list of conditions and the following disclaimer.
12 | *
13 | * * Redistributions in binary form must reproduce the above copyright notice,
14 | * this list of conditions and the following disclaimer in the documentation
15 | * and/or other materials provided with the distribution.
16 | *
17 | * * Neither the name of the copyright holder nor the names of its
18 | * contributors may be used to endorse or promote products derived from
19 | * this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | /**
34 | * @file serial_protocol.cpp
35 | * @author Daniel Koch
36 | *
37 | * This example implements a simple serial protocol, and tests the async_comm library using that protocol on a serial
38 | * loopback (USB-to-UART converter with the RX and TX pins connected together).
39 | *
40 | * The message defined by the serial protocol has the following format:
41 | *
42 | * | Field | Type | Size (bytes) | Description |
43 | * |------------|----------|--------------|------------------------------------------------------|
44 | * | Start Byte | | 1 | Identifies the beginning of a message, value is 0xA5 |
45 | * | `id` | uint32_t | 4 | Sequential message ID |
46 | * | `v1` | uint32_t | 4 | The first data field |
47 | * | `v2` | uint32_t | 4 | The second data field |
48 | * | CRC | uint8_t | 1 | Cyclic redundancy check (CRC) byte |
49 | *
50 | * The "payload" of the message is the part that contains the actual data, and consists of the `id`, `v1`, and `v2`
51 | * fields.
52 | *
53 | * The parser is implemented as a finite state machine.
54 | */
55 |
56 | #include
57 |
58 | #include
59 | #include
60 |
61 | #include
62 | #include
63 | #include
64 |
65 | // specify the number of messages to send
66 | #define NUM_MSGS 40000
67 |
68 | // define attributes of the serial protocol
69 | #define START_BYTE 0xA5
70 |
71 | #define START_BYTE_LEN 1
72 | #define PAYLOAD_LEN 12
73 | #define CRC_LEN 1
74 | #define PACKET_LEN (START_BYTE_LEN + PAYLOAD_LEN + CRC_LEN)
75 |
76 | // specify relevant serial port options
77 | #define BAUD_RATE 921600
78 | #define NUM_START_BITS 1
79 | #define NUM_STOP_BITS 1
80 |
81 |
82 | /**
83 | * @brief Recursively update the cyclic redundancy check (CRC)
84 | *
85 | * This uses the CRC-8-CCITT polynomial.
86 | *
87 | * Source: http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html#gab27eaaef6d7fd096bd7d57bf3f9ba083
88 | *
89 | * @param inCrc The current CRC value. This should be initialized to 0 before processing first byte.
90 | * @param inData The byte being processed
91 | * @return The new CRC value
92 | */
93 | uint8_t update_crc(uint8_t inCrc, uint8_t inData)
94 | {
95 | uint8_t i;
96 | uint8_t data;
97 |
98 | data = inCrc ^ inData;
99 |
100 | for ( i = 0; i < 8; i++ )
101 | {
102 | if (( data & 0x80 ) != 0 )
103 | {
104 | data <<= 1;
105 | data ^= 0x07;
106 | }
107 | else
108 | {
109 | data <<= 1;
110 | }
111 | }
112 | return data;
113 | }
114 |
115 |
116 | /**
117 | * @brief Pack message contents into a buffer
118 | * @param[out] dst Buffer in which to store the message
119 | * @param[in] id ID field of the message
120 | * @param[in] v1 First data field of the message
121 | * @param[in] v2 Second data field of the message
122 | *
123 | * @post The specified buffer contains the complete message packet, including start byte and CRC byte
124 | */
125 | void pack_message(uint8_t* dst, uint32_t id, uint32_t v1, uint32_t v2)
126 | {
127 | dst[0] = START_BYTE;
128 | memcpy(dst+1, &id, 4);
129 | memcpy(dst+5, &v1, 4);
130 | memcpy(dst+9, &v2, 4);
131 |
132 | uint8_t crc = 0;
133 | for (size_t i = 0; i < PACKET_LEN-1; i++)
134 | {
135 | crc = update_crc(crc, dst[i]);
136 | }
137 | dst[PACKET_LEN-1] = crc;
138 | }
139 |
140 |
141 | /**
142 | * @brief Unpack the contents of a message payload buffer
143 | * @param[in] src The buffer to unpack
144 | * @param[out] id ID field of the message
145 | * @param[out] v1 First data field of the message
146 | * @param[out] v2 Second data field of the message
147 | *
148 | * @pre Buffer contains a valid message payload
149 | * @post Payload contents have been placed into the specified variables
150 | */
151 | void unpack_payload(uint8_t* src, uint32_t *id, uint32_t *v1, uint32_t *v2)
152 | {
153 | memcpy(id, src, 4);
154 | memcpy(v1, src+4, 4);
155 | memcpy(v2, src+8, 4);
156 | }
157 |
158 |
159 | /**
160 | * @brief States for the parser state machine
161 | */
162 | enum ParseState
163 | {
164 | PARSE_STATE_IDLE,
165 | PARSE_STATE_GOT_START_BYTE,
166 | PARSE_STATE_GOT_PAYLOAD
167 | };
168 |
169 | ParseState parse_state = PARSE_STATE_IDLE; //!< Current state of the parser state machine
170 | uint8_t receive_buffer[PAYLOAD_LEN]; //!< Buffer for accumulating received payload
171 |
172 | volatile int receive_count = 0; //!< Keeps track of how many valid messages have been received
173 | bool received[NUM_MSGS]; //!< Keeps track of which messages we've received back
174 |
175 | std::mutex mutex; //!< mutex for synchronization between the main thread and callback thread
176 | std::condition_variable condition_variable; //!< condition variable used to suspend main thread until all messages have been received back
177 | volatile bool all_messages_received = false; //!< flag for whether all messages have been received back
178 |
179 |
180 | /**
181 | * @brief Passes a received byte through the parser state machine
182 | * @param byte The byte to process
183 | */
184 | void parse_byte(uint8_t byte)
185 | {
186 | static size_t payload_count;
187 | static uint8_t crc;
188 |
189 | switch (parse_state)
190 | {
191 | case PARSE_STATE_IDLE:
192 | if (byte == START_BYTE)
193 | {
194 | payload_count = 0;
195 | crc = 0;
196 | crc = update_crc(crc, byte);
197 |
198 | parse_state = PARSE_STATE_GOT_START_BYTE;
199 | }
200 | break;
201 | case PARSE_STATE_GOT_START_BYTE:
202 | receive_buffer[payload_count] = byte;
203 | crc = update_crc(crc, byte);
204 | if (++payload_count >= PAYLOAD_LEN)
205 | {
206 | parse_state = PARSE_STATE_GOT_PAYLOAD;
207 | }
208 | break;
209 | case PARSE_STATE_GOT_PAYLOAD:
210 | if (byte == crc)
211 | {
212 | uint32_t id, v1, v2;
213 | unpack_payload(receive_buffer, &id, &v1, &v2);
214 | received[id] = true;
215 | receive_count++;
216 |
217 | // notify the main thread when all messages have been received
218 | if (receive_count >= NUM_MSGS)
219 | {
220 | {
221 | std::unique_lock lock(mutex);
222 | all_messages_received = true;
223 | }
224 | condition_variable.notify_one();
225 | }
226 | } // otherwise ignore it
227 | parse_state = PARSE_STATE_IDLE;
228 | break;
229 | }
230 | }
231 |
232 |
233 | /**
234 | * @brief Callback function for the async_comm library
235 | *
236 | * Passes the received bytes through the parser state machine.
237 | *
238 | * @param buf Received bytes buffer
239 | * @param len Number of bytes received
240 | */
241 | void callback(const uint8_t* buf, size_t len)
242 | {
243 | for (size_t i = 0; i < len; i++)
244 | {
245 | parse_byte(buf[i]);
246 | }
247 | }
248 |
249 |
250 | int main(int argc, char** argv)
251 | {
252 | // initialize
253 | char* port;
254 | if (argc < 2)
255 | {
256 | std::printf("USAGE: %s PORT\n", argv[0]);
257 | return 1;
258 | }
259 | else
260 | {
261 | std::printf("Using port %s\n", argv[1]);
262 | port = argv[1];
263 | }
264 |
265 | // open serial port
266 | async_comm::Serial serial(port, BAUD_RATE);
267 | serial.register_receive_callback(&callback);
268 |
269 | if (!serial.init())
270 | {
271 | std::printf("Failed to initialize serial port\n");
272 | return 2;
273 | }
274 |
275 | // initialize variable for tracking which messages we've received
276 | memset(received, 0, sizeof(received));
277 |
278 | auto start = std::chrono::high_resolution_clock::now();
279 |
280 | // pack and send the specified number of messages with unique IDs and data
281 | uint8_t buffer[PACKET_LEN];
282 | for (uint32_t i = 0; i < NUM_MSGS; i++)
283 | {
284 | pack_message(buffer, i, i*2, i*4);
285 | serial.send_bytes(buffer, PACKET_LEN);
286 | }
287 | auto finish_write = std::chrono::high_resolution_clock::now();
288 |
289 | // wait to receive all messages
290 | {
291 | std::unique_lock lock(mutex);
292 | condition_variable.wait(lock, []{ return all_messages_received; });
293 | }
294 |
295 | auto finish_read = std::chrono::high_resolution_clock::now();
296 |
297 | // close serial port
298 | serial.close();
299 |
300 | // did we get all the messages back?
301 | int num_received = 0;
302 | for (int i = 0; i < NUM_MSGS; i++)
303 | {
304 | if (received[i])
305 | {
306 | num_received++;
307 | }
308 | else
309 | {
310 | std::printf("Missing message %d\n", i);
311 | }
312 | }
313 |
314 | // evaluate and print performance
315 | std::chrono::duration write_time = finish_write - start;
316 | std::chrono::duration read_time = finish_read - start;
317 |
318 | std::printf("Received %d of %d messages\n", num_received, NUM_MSGS);
319 | std::printf("Elapsed write time: %fms\n", write_time.count());
320 | std::printf("Elapsed read time: %fms\n", read_time.count());
321 |
322 | int num_bytes = NUM_MSGS * PACKET_LEN;
323 | double expected_time = num_bytes * (8 + NUM_START_BITS + NUM_STOP_BITS) / (double) BAUD_RATE;
324 | std::printf("Expected read time: %fms\n", expected_time*1e3);
325 | std::printf("Total: %d bytes\n", num_bytes);
326 |
327 | return 0;
328 | }
329 |
--------------------------------------------------------------------------------
/Doxyfile:
--------------------------------------------------------------------------------
1 | # Doxyfile 1.8.11
2 |
3 | #---------------------------------------------------------------------------
4 | # Project related configuration options
5 | #---------------------------------------------------------------------------
6 | DOXYFILE_ENCODING = UTF-8
7 | PROJECT_NAME = "Async Comm"
8 | PROJECT_NUMBER =
9 | PROJECT_BRIEF = "A library for asynchronous serial communication"
10 | PROJECT_LOGO =
11 | OUTPUT_DIRECTORY = "doc"
12 | CREATE_SUBDIRS = NO
13 | ALLOW_UNICODE_NAMES = NO
14 | OUTPUT_LANGUAGE = English
15 | BRIEF_MEMBER_DESC = YES
16 | REPEAT_BRIEF = YES
17 | ABBREVIATE_BRIEF =
18 | ALWAYS_DETAILED_SEC = NO
19 | INLINE_INHERITED_MEMB = NO
20 | FULL_PATH_NAMES = YES
21 | STRIP_FROM_PATH =
22 | STRIP_FROM_INC_PATH =
23 | SHORT_NAMES = NO
24 | JAVADOC_AUTOBRIEF = NO
25 | QT_AUTOBRIEF = NO
26 | MULTILINE_CPP_IS_BRIEF = NO
27 | INHERIT_DOCS = YES
28 | SEPARATE_MEMBER_PAGES = NO
29 | TAB_SIZE = 2
30 | ALIASES =
31 | TCL_SUBST =
32 | OPTIMIZE_OUTPUT_FOR_C = NO
33 | OPTIMIZE_OUTPUT_JAVA = NO
34 | OPTIMIZE_FOR_FORTRAN = NO
35 | OPTIMIZE_OUTPUT_VHDL = NO
36 | EXTENSION_MAPPING =
37 | MARKDOWN_SUPPORT = YES
38 | AUTOLINK_SUPPORT = YES
39 | BUILTIN_STL_SUPPORT = NO
40 | CPP_CLI_SUPPORT = NO
41 | SIP_SUPPORT = NO
42 | IDL_PROPERTY_SUPPORT = YES
43 | DISTRIBUTE_GROUP_DOC = NO
44 | GROUP_NESTED_COMPOUNDS = NO
45 | SUBGROUPING = YES
46 | INLINE_GROUPED_CLASSES = NO
47 | INLINE_SIMPLE_STRUCTS = NO
48 | TYPEDEF_HIDES_STRUCT = NO
49 | LOOKUP_CACHE_SIZE = 0
50 | #---------------------------------------------------------------------------
51 | # Build related configuration options
52 | #---------------------------------------------------------------------------
53 | EXTRACT_ALL = NO
54 | EXTRACT_PRIVATE = NO
55 | EXTRACT_PACKAGE = NO
56 | EXTRACT_STATIC = NO
57 | EXTRACT_LOCAL_CLASSES = YES
58 | EXTRACT_LOCAL_METHODS = NO
59 | EXTRACT_ANON_NSPACES = NO
60 | HIDE_UNDOC_MEMBERS = NO
61 | HIDE_UNDOC_CLASSES = NO
62 | HIDE_FRIEND_COMPOUNDS = NO
63 | HIDE_IN_BODY_DOCS = NO
64 | INTERNAL_DOCS = NO
65 | CASE_SENSE_NAMES = YES
66 | HIDE_SCOPE_NAMES = NO
67 | HIDE_COMPOUND_REFERENCE= NO
68 | SHOW_INCLUDE_FILES = YES
69 | SHOW_GROUPED_MEMB_INC = NO
70 | FORCE_LOCAL_INCLUDES = NO
71 | INLINE_INFO = YES
72 | SORT_MEMBER_DOCS = YES
73 | SORT_BRIEF_DOCS = NO
74 | SORT_MEMBERS_CTORS_1ST = NO
75 | SORT_GROUP_NAMES = NO
76 | SORT_BY_SCOPE_NAME = NO
77 | STRICT_PROTO_MATCHING = NO
78 | GENERATE_TODOLIST = YES
79 | GENERATE_TESTLIST = YES
80 | GENERATE_BUGLIST = YES
81 | GENERATE_DEPRECATEDLIST= YES
82 | ENABLED_SECTIONS =
83 | MAX_INITIALIZER_LINES = 30
84 | SHOW_USED_FILES = YES
85 | SHOW_FILES = YES
86 | SHOW_NAMESPACES = YES
87 | FILE_VERSION_FILTER =
88 | LAYOUT_FILE =
89 | CITE_BIB_FILES =
90 | #---------------------------------------------------------------------------
91 | # Configuration options related to warning and progress messages
92 | #---------------------------------------------------------------------------
93 | QUIET = NO
94 | WARNINGS = YES
95 | WARN_IF_UNDOCUMENTED = YES
96 | WARN_IF_DOC_ERROR = YES
97 | WARN_NO_PARAMDOC = NO
98 | WARN_AS_ERROR = NO
99 | WARN_FORMAT = "$file:$line: $text"
100 | WARN_LOGFILE =
101 | #---------------------------------------------------------------------------
102 | # Configuration options related to the input files
103 | #---------------------------------------------------------------------------
104 | INPUT = include src examples README.md
105 | INPUT_ENCODING = UTF-8
106 | FILE_PATTERNS =
107 | RECURSIVE = YES
108 | EXCLUDE =
109 | EXCLUDE_SYMLINKS = NO
110 | EXCLUDE_PATTERNS =
111 | EXCLUDE_SYMBOLS =
112 | EXAMPLE_PATH =
113 | EXAMPLE_PATTERNS =
114 | EXAMPLE_RECURSIVE = NO
115 | IMAGE_PATH =
116 | INPUT_FILTER =
117 | FILTER_PATTERNS =
118 | FILTER_SOURCE_FILES = NO
119 | FILTER_SOURCE_PATTERNS =
120 | USE_MDFILE_AS_MAINPAGE = README.md
121 | #---------------------------------------------------------------------------
122 | # Configuration options related to source browsing
123 | #---------------------------------------------------------------------------
124 | SOURCE_BROWSER = YES
125 | INLINE_SOURCES = NO
126 | STRIP_CODE_COMMENTS = YES
127 | REFERENCED_BY_RELATION = NO
128 | REFERENCES_RELATION = NO
129 | REFERENCES_LINK_SOURCE = YES
130 | SOURCE_TOOLTIPS = YES
131 | USE_HTAGS = NO
132 | VERBATIM_HEADERS = YES
133 | CLANG_ASSISTED_PARSING = NO
134 | CLANG_OPTIONS =
135 | #---------------------------------------------------------------------------
136 | # Configuration options related to the alphabetical class index
137 | #---------------------------------------------------------------------------
138 | ALPHABETICAL_INDEX = YES
139 | COLS_IN_ALPHA_INDEX = 5
140 | IGNORE_PREFIX =
141 | #---------------------------------------------------------------------------
142 | # Configuration options related to the HTML output
143 | #---------------------------------------------------------------------------
144 | GENERATE_HTML = YES
145 | HTML_OUTPUT = html
146 | HTML_FILE_EXTENSION = .html
147 | HTML_HEADER =
148 | HTML_FOOTER =
149 | HTML_STYLESHEET =
150 | HTML_EXTRA_STYLESHEET =
151 | HTML_EXTRA_FILES =
152 | HTML_COLORSTYLE_HUE = 220
153 | HTML_COLORSTYLE_SAT = 100
154 | HTML_COLORSTYLE_GAMMA = 80
155 | HTML_TIMESTAMP = NO
156 | HTML_DYNAMIC_SECTIONS = NO
157 | HTML_INDEX_NUM_ENTRIES = 100
158 | GENERATE_DOCSET = NO
159 | DOCSET_FEEDNAME = "Doxygen generated docs"
160 | DOCSET_BUNDLE_ID = org.doxygen.Project
161 | DOCSET_PUBLISHER_ID = org.doxygen.Publisher
162 | DOCSET_PUBLISHER_NAME = Publisher
163 | GENERATE_HTMLHELP = NO
164 | CHM_FILE =
165 | HHC_LOCATION =
166 | GENERATE_CHI = NO
167 | CHM_INDEX_ENCODING =
168 | BINARY_TOC = NO
169 | TOC_EXPAND = NO
170 | GENERATE_QHP = NO
171 | QCH_FILE =
172 | QHP_NAMESPACE = org.doxygen.Project
173 | QHP_VIRTUAL_FOLDER = doc
174 | QHP_CUST_FILTER_NAME =
175 | QHP_CUST_FILTER_ATTRS =
176 | QHP_SECT_FILTER_ATTRS =
177 | QHG_LOCATION =
178 | GENERATE_ECLIPSEHELP = NO
179 | ECLIPSE_DOC_ID = org.doxygen.Project
180 | DISABLE_INDEX = NO
181 | GENERATE_TREEVIEW = NO
182 | ENUM_VALUES_PER_LINE = 4
183 | TREEVIEW_WIDTH = 250
184 | EXT_LINKS_IN_WINDOW = NO
185 | FORMULA_FONTSIZE = 10
186 | FORMULA_TRANSPARENT = YES
187 | USE_MATHJAX = NO
188 | MATHJAX_FORMAT = HTML-CSS
189 | MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
190 | MATHJAX_EXTENSIONS =
191 | MATHJAX_CODEFILE =
192 | SEARCHENGINE = YES
193 | SERVER_BASED_SEARCH = NO
194 | EXTERNAL_SEARCH = NO
195 | SEARCHENGINE_URL =
196 | SEARCHDATA_FILE = searchdata.xml
197 | EXTERNAL_SEARCH_ID =
198 | EXTRA_SEARCH_MAPPINGS =
199 | #---------------------------------------------------------------------------
200 | # Configuration options related to the LaTeX output
201 | #---------------------------------------------------------------------------
202 | GENERATE_LATEX = YES
203 | LATEX_OUTPUT = latex
204 | LATEX_CMD_NAME = latex
205 | MAKEINDEX_CMD_NAME = makeindex
206 | COMPACT_LATEX = NO
207 | PAPER_TYPE = a4
208 | EXTRA_PACKAGES =
209 | LATEX_HEADER =
210 | LATEX_FOOTER =
211 | LATEX_EXTRA_STYLESHEET =
212 | LATEX_EXTRA_FILES =
213 | PDF_HYPERLINKS = YES
214 | USE_PDFLATEX = YES
215 | LATEX_BATCHMODE = NO
216 | LATEX_HIDE_INDICES = NO
217 | LATEX_SOURCE_CODE = NO
218 | LATEX_BIB_STYLE = plain
219 | LATEX_TIMESTAMP = NO
220 | #---------------------------------------------------------------------------
221 | # Configuration options related to the RTF output
222 | #---------------------------------------------------------------------------
223 | GENERATE_RTF = NO
224 | RTF_OUTPUT = rtf
225 | COMPACT_RTF = NO
226 | RTF_HYPERLINKS = NO
227 | RTF_STYLESHEET_FILE =
228 | RTF_EXTENSIONS_FILE =
229 | RTF_SOURCE_CODE = NO
230 | #---------------------------------------------------------------------------
231 | # Configuration options related to the man page output
232 | #---------------------------------------------------------------------------
233 | GENERATE_MAN = NO
234 | MAN_OUTPUT = man
235 | MAN_EXTENSION = .3
236 | MAN_SUBDIR =
237 | MAN_LINKS = NO
238 | #---------------------------------------------------------------------------
239 | # Configuration options related to the XML output
240 | #---------------------------------------------------------------------------
241 | GENERATE_XML = NO
242 | XML_OUTPUT = xml
243 | XML_PROGRAMLISTING = YES
244 | #---------------------------------------------------------------------------
245 | # Configuration options related to the DOCBOOK output
246 | #---------------------------------------------------------------------------
247 | GENERATE_DOCBOOK = NO
248 | DOCBOOK_OUTPUT = docbook
249 | DOCBOOK_PROGRAMLISTING = NO
250 | #---------------------------------------------------------------------------
251 | # Configuration options for the AutoGen Definitions output
252 | #---------------------------------------------------------------------------
253 | GENERATE_AUTOGEN_DEF = NO
254 | #---------------------------------------------------------------------------
255 | # Configuration options related to the Perl module output
256 | #---------------------------------------------------------------------------
257 | GENERATE_PERLMOD = NO
258 | PERLMOD_LATEX = NO
259 | PERLMOD_PRETTY = YES
260 | PERLMOD_MAKEVAR_PREFIX =
261 | #---------------------------------------------------------------------------
262 | # Configuration options related to the preprocessor
263 | #---------------------------------------------------------------------------
264 | ENABLE_PREPROCESSING = YES
265 | MACRO_EXPANSION = NO
266 | EXPAND_ONLY_PREDEF = NO
267 | SEARCH_INCLUDES = YES
268 | INCLUDE_PATH =
269 | INCLUDE_FILE_PATTERNS =
270 | PREDEFINED =
271 | EXPAND_AS_DEFINED =
272 | SKIP_FUNCTION_MACROS = YES
273 | #---------------------------------------------------------------------------
274 | # Configuration options related to external references
275 | #---------------------------------------------------------------------------
276 | TAGFILES =
277 | GENERATE_TAGFILE =
278 | ALLEXTERNALS = NO
279 | EXTERNAL_GROUPS = YES
280 | EXTERNAL_PAGES = YES
281 | PERL_PATH = /usr/bin/perl
282 | #---------------------------------------------------------------------------
283 | # Configuration options related to the dot tool
284 | #---------------------------------------------------------------------------
285 | CLASS_DIAGRAMS = YES
286 | MSCGEN_PATH =
287 | DIA_PATH =
288 | HIDE_UNDOC_RELATIONS = YES
289 | HAVE_DOT = YES
290 | DOT_NUM_THREADS = 0
291 | DOT_FONTNAME = Helvetica
292 | DOT_FONTSIZE = 10
293 | DOT_FONTPATH =
294 | CLASS_GRAPH = YES
295 | COLLABORATION_GRAPH = YES
296 | GROUP_GRAPHS = YES
297 | UML_LOOK = NO
298 | UML_LIMIT_NUM_FIELDS = 10
299 | TEMPLATE_RELATIONS = NO
300 | INCLUDE_GRAPH = YES
301 | INCLUDED_BY_GRAPH = YES
302 | CALL_GRAPH = NO
303 | CALLER_GRAPH = NO
304 | GRAPHICAL_HIERARCHY = YES
305 | DIRECTORY_GRAPH = YES
306 | DOT_IMAGE_FORMAT = png
307 | INTERACTIVE_SVG = NO
308 | DOT_PATH =
309 | DOTFILE_DIRS =
310 | MSCFILE_DIRS =
311 | DIAFILE_DIRS =
312 | PLANTUML_JAR_PATH =
313 | PLANTUML_INCLUDE_PATH =
314 | DOT_GRAPH_MAX_NODES = 50
315 | MAX_DOT_GRAPH_DEPTH = 0
316 | DOT_TRANSPARENT = NO
317 | DOT_MULTI_TARGETS = NO
318 | GENERATE_LEGEND = YES
319 | DOT_CLEANUP = YES
320 |
--------------------------------------------------------------------------------