From d8f57576d4dd73aa18329ad66f53f62a851f9cb3 Mon Sep 17 00:00:00 2001 From: "Daniel J. Hofmann" Date: Sat, 11 Feb 2017 19:57:34 +0100 Subject: [PATCH] Adds Protobuf + ZeroMQ Networking Example See #12, #13. References: - http://api.zeromq.org/2-1:zmq-cpp --- .travis.yml | 14 +--- libsweep/examples/CMakeLists.txt | 14 ++++ libsweep/examples/README.md | 25 +++++-- libsweep/examples/net.cc | 111 +++++++++++++++++++++++++++++++ libsweep/examples/net.proto | 9 +++ 5 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 libsweep/examples/net.cc create mode 100644 libsweep/examples/net.proto diff --git a/.travis.yml b/.travis.yml index d3695ed..53e3d85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,31 +11,23 @@ matrix: addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-6', 'cmake', 'python3', 'nodejs', 'npm', 'libsfml-dev'] + packages: ['g++-6', 'cmake', 'python3', 'nodejs', 'npm', 'libsfml-dev', 'libprotobuf-dev', 'protobuf-compiler', 'libzmq-dev'] env: CCOMPILER='gcc-6' CXXCOMPILER='g++-6' - os: linux addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'cmake', 'python3', 'nodejs', 'npm', 'libsfml-dev'] + packages: ['g++-5', 'cmake', 'python3', 'nodejs', 'npm', 'libsfml-dev', 'libprotobuf-dev', 'protobuf-compiler', 'libzmq-dev'] env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' - os: linux addons: apt: sources: ['llvm-toolchain-precise-3.8'] - packages: ['clang-3.8', 'cmake', 'python3', 'nodejs', 'npm', 'libsfml-dev'] + packages: ['clang-3.8', 'cmake', 'python3', 'nodejs', 'npm', 'libsfml-dev', 'libprotobuf-dev', 'protobuf-compiler', 'libzmq-dev'] env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' - # Waiting on Travis whitelisting 3.9, see https://github.com/travis-ci/apt-source-whitelist/issues/300 - #- os: linux - # addons: - # apt: - # sources: ['llvm-toolchain-precise-3.9'] - # packages: ['clang-3.9'] - # env: CCOMPILER='clang-3.9' CXXCOMPILER='clang++-3.9' - before_install: - export CC=${CCOMPILER} CXX=${CXXCOMPILER} diff --git a/libsweep/examples/CMakeLists.txt b/libsweep/examples/CMakeLists.txt index 0a9899d..60ea4c9 100644 --- a/libsweep/examples/CMakeLists.txt +++ b/libsweep/examples/CMakeLists.txt @@ -28,3 +28,17 @@ if (LIBSFML_FOUND) else() message(STATUS "SFML2 required for real-time viewer (libsfml-dev)") endif() + + +# Optional Protobuf + ZeroMQ networking example +find_package(Protobuf 2.4.1) +pkg_check_modules(LIBZMQ libzmq) + +if (Protobuf_FOUND AND LIBZMQ_FOUND) + protobuf_generate_cpp(ProtoSources ProtoHeaders net.proto) + add_executable(example-net net.cc ${ProtoSources} ${ProtoHeaders}) + target_link_libraries(example-net PRIVATE ${LIBSWEEP_LIBRARY} ${Protobuf_LITE_LIBRARIES} ${LIBZMQ_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_include_directories(example-net SYSTEM PRIVATE ${LIBSWEEP_INCLUDE_DIR} ${Protobuf_INCLUDE_DIRS} ${LIBZMQ_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) +else() + message(STATUS "Protobuf2 and ZeroMQ required for networking example (libprotobuf-dev protobuf-compiler libzmq-dev)") +endif() diff --git a/libsweep/examples/README.md b/libsweep/examples/README.md index 56afd85..b272a7b 100644 --- a/libsweep/examples/README.md +++ b/libsweep/examples/README.md @@ -17,14 +17,29 @@ To build: cmake --build . ``` -**Note:** the viewer requires SFML2 to be installed. +**Note:** +- The viewer requires SFML2 to be installed. +- The pub-sub networking example requires Protobuf and ZeroMQ to be installed. ```bash - # run the examples - ./example-c - ./example-c++ - ./example-viewer +./example-c +./example-c++ +``` + +Real-time viewer: + +```bash +./example-viewer +``` + +Pub-Sub networking example. +Start a publisher sending out full 360 degree scans via the network (localhost). +Then start some subscribers connecting to the publisher. + +```bash +./example-net publisher +./example-net subscriber ``` ### License diff --git a/libsweep/examples/net.cc b/libsweep/examples/net.cc new file mode 100644 index 0000000..e227e6e --- /dev/null +++ b/libsweep/examples/net.cc @@ -0,0 +1,111 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "net.pb.h" + +static void usage() { + std::cout << "Usage: example-net [ publisher | subscriber ]\n"; + std::exit(EXIT_SUCCESS); +} + +void subscriber() { + zmq::context_t ctx{/*io_threads=*/1}; + zmq::socket_t sub{ctx, ZMQ_SUB}; + + sub.connect("tcp://127.0.0.1:5555"); + sub.setsockopt(ZMQ_SUBSCRIBE, "", 0); + + std::cout << "Subscribing." << std::endl; + + for (;;) { + zmq::message_t msg; + + if (!sub.recv(&msg)) + continue; + + sweep::proto::scan in; + in.ParseFromArray(msg.data(), msg.size()); + + const auto n = in.angle_size(); + + for (auto i = 0; i < n; ++i) { + std::cout << "Angle: " << in.angle(i) // + << " Distance: " << in.distance(i) // + << " Signal strength: " << in.signal_strength(i) // + << std::endl; + } + } +} + +void publisher() try { + zmq::context_t ctx{/*io_threads=*/1}; + zmq::socket_t pub{ctx, ZMQ_PUB}; + + pub.bind("tcp://127.0.0.1:5555"); + + sweep::sweep device; + device.start_scanning(); + + std::cout << "Publishing. Each dot is a full 360 degree scan." << std::endl; + + for (;;) { + const sweep::scan scan = device.get_scan(); + + sweep::proto::scan out; + + for (const sweep::sample& sample : scan.samples) { + out.add_angle(sample.angle); + out.add_distance(sample.distance); + out.add_signal_strength(sample.signal_strength); + } + + auto encoded = out.SerializeAsString(); + + zmq::message_t msg{encoded.size()}; + std::memcpy(msg.data(), encoded.data(), encoded.size()); + + const auto ok = pub.send(msg); + + if (ok) + std::cout << "." << std::flush; + } + + device.stop_scanning(); + +} catch (const sweep::device_error& e) { + std::cerr << "Error: " << e.what() << '\n'; +} + +int main(int argc, char** argv) { + std::vector args{argv, argv + argc}; + + if (args.size() != 2) + usage(); + + const auto isPublisher = args[1] == "publisher"; + const auto isSubscriber = args[1] == "subscriber"; + + if (!isPublisher && !isSubscriber) + usage(); + + GOOGLE_PROTOBUF_VERIFY_VERSION; + + struct AtExit { + ~AtExit() { ::google::protobuf::ShutdownProtobufLibrary(); } + } sentry; + + if (isPublisher) + publisher(); + + if (isSubscriber) + subscriber(); +} diff --git a/libsweep/examples/net.proto b/libsweep/examples/net.proto new file mode 100644 index 0000000..58f4180 --- /dev/null +++ b/libsweep/examples/net.proto @@ -0,0 +1,9 @@ +package sweep.proto; + +option optimize_for = LITE_RUNTIME; + +message scan { + repeated int32 angle = 1 [packed=true]; + repeated int32 distance = 2 [packed=true]; + repeated int32 signal_strength = 3 [packed=true]; +}