├── VERSION ├── libcluon ├── thirdparty │ ├── argh │ │ ├── README │ │ └── LICENSE │ ├── Mustache │ │ └── README │ ├── cluon │ │ ├── any │ │ │ ├── README │ │ │ └── LICENSE │ │ └── stringtoolbox.hpp │ └── cpp-peglib │ │ ├── README │ │ └── LICENSE ├── tools │ ├── cluon-filter.cpp │ ├── cluon-LCMtoJSON.cpp │ ├── cluon-OD4toJSON.cpp │ ├── cluon-OD4toStdout.cpp │ ├── cluon-msc.cpp │ ├── cluon-replay.cpp │ ├── cluon-livefeed.cpp │ ├── cluon-rec2csv.cpp │ ├── cluon-OD4toStdout.hpp │ ├── libcluon.cpp │ ├── cluon-OD4toJSON.hpp │ ├── cluon-LCMtoJSON.hpp │ └── cluon-msc.hpp ├── include │ └── cluon │ │ ├── IPv4Tools.hpp │ │ ├── ProtoConstants.hpp │ │ ├── UDPPacketSizeConstraints.hpp │ │ ├── JSONConstants.hpp │ │ ├── TerminateHandler.hpp │ │ ├── MsgPackConstants.hpp │ │ ├── MetaMessageToCPPTransformator.hpp │ │ ├── MetaMessageToProtoTransformator.hpp │ │ ├── LCMToGenericMessage.hpp │ │ ├── TCPServer.hpp │ │ ├── cluon.hpp │ │ ├── Time.hpp │ │ ├── MessageParser.hpp │ │ ├── UDPSender.hpp │ │ ├── PortableEndian.hpp │ │ ├── ToMsgPackVisitor.hpp │ │ ├── FromLCMVisitor.hpp │ │ ├── ToLCMVisitor.hpp │ │ ├── ToODVDVisitor.hpp │ │ ├── NotifyingPipeline.hpp │ │ ├── EnvelopeConverter.hpp │ │ ├── ToJSONVisitor.hpp │ │ ├── FromJSONVisitor.hpp │ │ ├── ToCSVVisitor.hpp │ │ ├── FromMsgPackVisitor.hpp │ │ ├── UDPReceiver.hpp │ │ ├── Envelope.hpp │ │ ├── ToProtoVisitor.hpp │ │ └── SharedMemory.hpp ├── testsuites │ ├── TestUDPPacketSizeConstraints.cpp │ ├── TestProtoConstants.cpp │ ├── TestIPv4Tools.cpp │ ├── Testcluon.cpp │ ├── TestNotifyingPipeline.cpp │ ├── TestTime.cpp │ ├── TestcluonDataStructures.cpp │ ├── TestUDPSender.cpp │ ├── Testcluon_OD4toStdout.cpp │ └── TestUDPReceiver.cpp ├── src │ ├── cluon.cpp │ ├── TerminateHandler.cpp │ ├── IPv4Tools.cpp │ ├── MetaMessage.cpp │ ├── LCMToGenericMessage.cpp │ ├── ToODVDVisitor.cpp │ ├── MetaMessageToProtoTransformator.cpp │ └── OD4Session.cpp ├── examples │ ├── cluon-UDPSender.cpp │ ├── cluon-UDPReceiver.cpp │ ├── cluon-TCPServer.cpp │ └── cluon-TCPConnection.cpp └── resources │ ├── cluonDataStructures.odvd │ └── cluonTestDataStructures.odvd ├── .gitignore ├── .github └── workflows │ └── build-docker-image.yml ├── .codecov.yml ├── python ├── sendEnvelopes.py ├── MyExampleMessageSpec.odvd ├── receiveEnvelopes.py ├── Makefile └── OD4Session.py ├── Dockerfile ├── docs ├── cluon-UDPSender.md └── cluon-UDPReceiver.md ├── appveyor.yml ├── CONTRIBUTING.md ├── .clang-format ├── CODE_OF_CONDUCT.md └── Makefile /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.149 2 | -------------------------------------------------------------------------------- /libcluon/thirdparty/argh/README: -------------------------------------------------------------------------------- 1 | The sources are available at https://github.com/adishavit/argh. 2 | -------------------------------------------------------------------------------- /libcluon/thirdparty/Mustache/README: -------------------------------------------------------------------------------- 1 | The sources are available at https://github.com/kainjow/Mustache. 2 | -------------------------------------------------------------------------------- /libcluon/thirdparty/cluon/any/README: -------------------------------------------------------------------------------- 1 | The sources are available at https://github.com/thelink2012/any. 2 | -------------------------------------------------------------------------------- /libcluon/thirdparty/cpp-peglib/README: -------------------------------------------------------------------------------- 1 | The sources are available at https://github.com/yhirose/cpp-peglib. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | api 2 | builds/libcluon* 3 | builds/non-docker* 4 | compile_commands.json 5 | build_dir 6 | debian 7 | tmp* 8 | b 9 | libcluon.js 10 | *swp 11 | -------------------------------------------------------------------------------- /.github/workflows/build-docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Build the Docker image 15 | run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) 16 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-filter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon-filter.hpp" 10 | 11 | #include 12 | 13 | int32_t main(int32_t argc, char **argv) { 14 | return cluon_filter(argc, argv); 15 | } 16 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-LCMtoJSON.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon-LCMtoJSON.hpp" 10 | 11 | #include 12 | 13 | int32_t main(int32_t argc, char **argv) { 14 | return cluon_LCMtoJSON(argc, argv); 15 | } 16 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-OD4toJSON.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon-OD4toJSON.hpp" 10 | 11 | #include 12 | 13 | int32_t main(int32_t argc, char **argv) { 14 | return cluon_OD4toJSON(argc, argv); 15 | } 16 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-OD4toStdout.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon-OD4toStdout.hpp" 10 | 11 | #include 12 | 13 | int32_t main(int32_t argc, char **argv) { 14 | return cluon_OD4toStdout(argc, argv); 15 | } 16 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-msc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | // This test for a compiler definition is necessary to preserve single-file, header-only compability. 10 | #ifndef HAVE_CLUON_MSC 11 | #include "cluon-msc.hpp" 12 | #endif 13 | 14 | #include 15 | 16 | int32_t main(int32_t argc, char **argv) { 17 | return cluon_msc(argc, argv); 18 | } 19 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-replay.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | // This test for a compiler definition is necessary to preserve single-file, header-only compability. 10 | #ifndef HAVE_CLUON_REPLAY 11 | #include "cluon-replay.hpp" 12 | #endif 13 | 14 | #include 15 | 16 | int32_t main(int32_t argc, char **argv) { 17 | return cluon_replay(argc, argv); 18 | } 19 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-livefeed.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | // This test for a compiler definition is necessary to preserve single-file, header-only compability. 10 | #ifndef HAVE_CLUON_LIVEFEED 11 | #include "cluon-livefeed.hpp" 12 | #endif 13 | 14 | #include 15 | 16 | int32_t main(int32_t argc, char **argv) { 17 | return cluon_livefeed(argc, argv); 18 | } 19 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-rec2csv.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | // This test for a compiler definition is necessary to preserve single-file, header-only compability. 10 | #ifndef HAVE_CLUON_REC2CSV 11 | #include "cluon-rec2csv.hpp" 12 | #endif 13 | 14 | #include 15 | 16 | int32_t main(int32_t argc, char **argv) { 17 | return cluon_rec2csv(argc, argv); 18 | } 19 | -------------------------------------------------------------------------------- /libcluon/include/cluon/IPv4Tools.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_IPV4TOOLS_HPP 10 | #define CLUON_IPV4TOOLS_HPP 11 | 12 | #include 13 | 14 | namespace cluon { 15 | 16 | /** 17 | * @return IPv4-formatted string for the given hostname or the empty string. 18 | */ 19 | std::string getIPv4FromHostname(const std::string &hostname) noexcept; 20 | 21 | } // namespace cluon 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: no 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "80...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | notify: 16 | slack: 17 | default: # -> see "sections" below 18 | url: "https://hooks.slack.com/services/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" 19 | 20 | parsers: 21 | gcov: 22 | branch_detection: 23 | conditional: yes 24 | loop: yes 25 | method: no 26 | macro: no 27 | 28 | comment: 29 | layout: "reach, diff, flags, files, footer" 30 | behavior: default 31 | require_changes: no 32 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ProtoConstants.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_PROTOCONSTANTS_HPP 10 | #define CLUON_PROTOCONSTANTS_HPP 11 | 12 | #include 13 | 14 | // clang-format off 15 | namespace cluon { 16 | enum class ProtoConstants : uint8_t { 17 | VARINT = 0, 18 | EIGHT_BYTES = 1, 19 | LENGTH_DELIMITED = 2, 20 | FOUR_BYTES = 5, }; 21 | } 22 | // clang-format on 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /libcluon/include/cluon/UDPPacketSizeConstraints.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_UDPPACKETSIZECONSTRAINTS_H 10 | #define CLUON_UDPPACKETSIZECONSTRAINTS_H 11 | 12 | #include 13 | 14 | // clang-format off 15 | namespace cluon { 16 | enum class UDPPacketSizeConstraints : uint16_t { 17 | SIZE_IPv4_HEADER = 20, 18 | SIZE_UDP_HEADER = 8, 19 | MAX_SIZE_UDP_PACKET = 0xFFFF, }; 20 | } 21 | // clang-format on 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestUDPPacketSizeConstraints.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/UDPPacketSizeConstraints.hpp" 12 | 13 | TEST_CASE("Test constants.") { 14 | REQUIRE(20 == static_cast(cluon::UDPPacketSizeConstraints::SIZE_IPv4_HEADER)); 15 | REQUIRE(8 == static_cast(cluon::UDPPacketSizeConstraints::SIZE_UDP_HEADER)); 16 | REQUIRE(0xFFFF == static_cast(cluon::UDPPacketSizeConstraints::MAX_SIZE_UDP_PACKET)); 17 | } 18 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestProtoConstants.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/ProtoConstants.hpp" 12 | 13 | TEST_CASE("Test constants.") { 14 | REQUIRE(0 == static_cast(cluon::ProtoConstants::VARINT)); 15 | REQUIRE(1 == static_cast(cluon::ProtoConstants::EIGHT_BYTES)); 16 | REQUIRE(2 == static_cast(cluon::ProtoConstants::LENGTH_DELIMITED)); 17 | REQUIRE(5 == static_cast(cluon::ProtoConstants::FOUR_BYTES)); 18 | } 19 | -------------------------------------------------------------------------------- /libcluon/include/cluon/JSONConstants.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_JSONCONSTANTS_HPP 10 | #define CLUON_JSONCONSTANTS_HPP 11 | 12 | #include 13 | 14 | // clang-format off 15 | namespace cluon { 16 | enum class JSONConstants : uint16_t { 17 | IS_FALSE = 0, 18 | IS_TRUE = 1, 19 | NUMBER = 2, 20 | STRING = 3, 21 | OBJECT = 4, // Indicating nested types. 22 | UNDEFINED = 99, 23 | }; 24 | } 25 | // clang-format on 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /python/sendEnvelopes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | # Copyright (C) 2018 Christian Berger 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | # This is the Python version of libcluon's OD4Session: 10 | import OD4Session 11 | 12 | # This is our example message specification. 13 | import MyExampleMessageSpec_pb2 14 | 15 | # "Main" part. 16 | session = OD4Session.OD4Session(cid=253) # Connect to running OD4Session at CID 253. 17 | session.connect() 18 | 19 | # Create test message. 20 | testMessage = MyExampleMessageSpec_pb2.my_TestMessage() 21 | testMessage.attribute11 = "Hello Python World!" 22 | 23 | session.send(30005, testMessage.SerializeToString()); 24 | 25 | session.run() 26 | -------------------------------------------------------------------------------- /python/MyExampleMessageSpec.odvd: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Christian Berger 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | message my.TestMessage [id = 30005] { 8 | uint8 attribute1 [ default = 1, id = 1 ]; 9 | int8 attribute2 [ default = -1, id = 2 ]; 10 | uint16 attribute3 [ default = 100, id = 3 ]; 11 | int16 attribute4 [ default = -100, id = 4 ]; 12 | uint32 attribute5 [ default = 10000, id = 5 ]; 13 | int32 attribute6 [ default = -10000, id = 6 ]; 14 | uint64 attribute7 [ default = 12345, id = 7 ]; 15 | int64 attribute8 [ default = -12345, id = 8 ]; 16 | float attribute9 [ default = -1.2345, id = 9 ]; 17 | double attribute10 [ default = -10.2345, id = 10 ]; 18 | string attribute11 [ default = "Hello World!", id = 11 ]; 19 | } 20 | -------------------------------------------------------------------------------- /libcluon/src/cluon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "argh/argh.h" 10 | 11 | #include "cluon/cluon.hpp" 12 | 13 | namespace cluon { 14 | 15 | std::map getCommandlineArguments(int32_t argc, char **argv) noexcept { 16 | argh::parser commandline{argc, argv}; 17 | std::map retVal; 18 | 19 | for (auto &positionalArgument : commandline.pos_args()) { retVal[positionalArgument] = ""; } 20 | 21 | for (auto &flag : commandline.flags()) { retVal[flag] = "1"; } 22 | 23 | for (auto ¶meter : commandline.params()) { retVal[parameter.first] = parameter.second; } 24 | 25 | return retVal; 26 | } 27 | 28 | } // namespace cluon 29 | -------------------------------------------------------------------------------- /python/receiveEnvelopes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | # Copyright (C) 2018 Christian Berger 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | # This is the Python version of libcluon's OD4Session: 10 | import OD4Session 11 | 12 | # This is our example message specification. 13 | import MyExampleMessageSpec_pb2 14 | 15 | # Callback for a message of interest. 16 | def onMessage(msg, timeStamps): 17 | print "sent: " + str(timeStamps[0]) + ", received: " + str(timeStamps[1]) + ", sample time stamps: " + str(timeStamps[2]) 18 | print msg 19 | 20 | # "Main" part. 21 | session = OD4Session.OD4Session(cid=253) # Connect to running OD4Session at CID 253. 22 | session.registerMessageCallback(30005, onMessage, MyExampleMessageSpec_pb2.my_TestMessage) 23 | session.connect() 24 | session.run() 25 | -------------------------------------------------------------------------------- /python/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Christian Berger 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | all: run 8 | 9 | cluonDataStructures.proto: ../libcluon/resources/cluonDataStructures.odvd 10 | cluon-msc --proto --out=cluonDataStructures.proto ../libcluon/resources/cluonDataStructures.odvd 11 | 12 | cluonDataStructures_pb2.py: cluonDataStructures.proto 13 | protoc --python_out=. cluonDataStructures.proto 14 | 15 | MyExampleMessageSpec.proto: MyExampleMessageSpec.odvd 16 | cluon-msc --proto --out=MyExampleMessageSpec.proto MyExampleMessageSpec.odvd 17 | 18 | MyExampleMessageSpec_pb2.py: MyExampleMessageSpec.proto 19 | protoc --python_out=. MyExampleMessageSpec.proto 20 | 21 | run: cluonDataStructures_pb2.py MyExampleMessageSpec_pb2.py 22 | python receiveEnvelopes.py & 23 | sleep 2 && python sendEnvelopes.py 24 | 25 | clean: 26 | rm -f *_pb2.py *pyc *.proto 27 | -------------------------------------------------------------------------------- /libcluon/thirdparty/cpp-peglib/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 yhirose 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /libcluon/examples/cluon-UDPSender.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/UDPSender.hpp" 10 | 11 | #include 12 | #include 13 | 14 | int main(int argc, char **argv) { 15 | int retVal{0}; 16 | const std::string PROGRAM(argv[0]); //NOLINT 17 | if (3 != argc) { 18 | std::cerr << PROGRAM << " demonstrates how to use libcluon to send data via UDP." << std::endl; 19 | std::cerr << "Usage: " << PROGRAM << " IPv4-address port" << std::endl; 20 | std::cerr << "Example: " << PROGRAM << " 127.0.0.1 1234" << std::endl; 21 | retVal = 1; 22 | } else { 23 | const std::string ADDRESS(argv[1]); //NOLINT 24 | const std::string PORT(argv[2]); //NOLINT 25 | cluon::UDPSender sender(ADDRESS, static_cast(std::stoi(PORT))); 26 | 27 | while (std::cin.good()) { 28 | std::string data; 29 | std::getline(std::cin, data); 30 | 31 | sender.send(std::move(data)); 32 | } 33 | } 34 | return retVal; 35 | } 36 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestIPv4Tools.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/cluon.hpp" // Necessary for the correcting linker settings on Win32. 12 | #include "cluon/IPv4Tools.hpp" 13 | 14 | TEST_CASE("Test hostname resolution localhost to 127.0.0.1.") { 15 | std::string resolvedHostname = cluon::getIPv4FromHostname("localhost"); 16 | REQUIRE(resolvedHostname == "127.0.0.1"); 17 | } 18 | 19 | TEST_CASE("Test hostname resolution localhos to empty string.") { 20 | std::string resolvedHostname = cluon::getIPv4FromHostname("localhos"); 21 | REQUIRE(resolvedHostname == ""); 22 | } 23 | 24 | TEST_CASE("Test hostname resolution 127.0.0.1 to 127.0.0.1.") { 25 | std::string resolvedHostname = cluon::getIPv4FromHostname("127.0.0.1"); 26 | REQUIRE(resolvedHostname == "127.0.0.1"); 27 | } 28 | 29 | TEST_CASE("Test hostname resolution 127.0.1 to 127.0.0.1.") { 30 | std::string resolvedHostname = cluon::getIPv4FromHostname("127.0.1"); 31 | #ifdef WIN32 32 | REQUIRE(resolvedHostname == ""); 33 | #else 34 | REQUIRE(resolvedHostname == "127.0.0.1"); 35 | #endif 36 | } 37 | -------------------------------------------------------------------------------- /libcluon/resources/cluonDataStructures.odvd: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017-2018 Christian Berger 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | message cluon.data.TimeStamp [id = 12] { 8 | int32 seconds [id = 1]; 9 | int32 microseconds [id = 2]; 10 | } 11 | 12 | message cluon.data.Envelope [id = 1] { 13 | int32 dataType [id = 1]; 14 | bytes serializedData [id = 2]; 15 | cluon.data.TimeStamp sent [id = 3]; 16 | cluon.data.TimeStamp received [id = 4]; 17 | cluon.data.TimeStamp sampleTimeStamp [id = 5]; 18 | uint32 senderStamp [id = 6]; 19 | } 20 | 21 | message cluon.data.PlayerCommand [id = 9] { 22 | uint8 command [id = 1]; // 0 = nothing, 1 = play, 2 = pause, 3 = seekTo, 4 = step 23 | float seekTo [id = 2]; 24 | } 25 | 26 | message cluon.data.PlayerStatus [id = 10] { 27 | uint8 state [id = 1]; // 0 = unknown, 1 = loading file, 2 = playback 28 | uint32 numberOfEntries [id = 2]; 29 | uint32 currentEntryForPlayback [id = 3]; 30 | } 31 | 32 | message cluon.data.RecorderCommand [id = 11] { 33 | uint8 command [id = 1]; // 0 = nothing, 1 = record, 2 = stop 34 | } 35 | 36 | -------------------------------------------------------------------------------- /libcluon/thirdparty/cluon/any/LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /libcluon/testsuites/Testcluon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/cluon.hpp" 12 | 13 | #include 14 | 15 | TEST_CASE("Test empty commandline parsing.") { 16 | constexpr int32_t argc = 1; 17 | const char *argv[] = {static_cast("myBinary")}; 18 | auto retVal = cluon::getCommandlineArguments(argc, const_cast(argv)); 19 | 20 | REQUIRE(1 == retVal.size()); 21 | REQUIRE(retVal["myBinary"] == ""); 22 | } 23 | 24 | TEST_CASE("Test non-empty commandline parsing.") { 25 | constexpr int32_t argc = 4; 26 | const char *argv[] = {static_cast("myBinary"), 27 | static_cast("--cid=100"), 28 | static_cast("--freq=10"), 29 | static_cast("--verbose")}; 30 | auto retVal = cluon::getCommandlineArguments(argc, const_cast(argv)); 31 | REQUIRE(4 == retVal.size()); 32 | 33 | REQUIRE(retVal["myBinary"] == ""); 34 | REQUIRE(retVal["cid"] == "100"); 35 | REQUIRE(retVal["freq"] == "10"); 36 | REQUIRE(retVal["verbose"] == "1"); 37 | } 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Christian Berger 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | # Part to build libcluon. 17 | FROM alpine:3.15 as builder 18 | MAINTAINER Christian Berger "christian.berger@gu.se" 19 | RUN apk update && \ 20 | apk --no-cache add \ 21 | cmake \ 22 | g++ \ 23 | linux-headers \ 24 | make 25 | ADD . /opt/sources 26 | WORKDIR /opt/sources 27 | RUN mkdir build && \ 28 | cd build && \ 29 | cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/tmp ../libcluon && \ 30 | make && make test && make install 31 | 32 | # Part to deploy libcluon. 33 | FROM alpine:3.15 as deploy 34 | MAINTAINER Christian Berger "christian.berger@gu.se" 35 | RUN apk update && \ 36 | apk --no-cache add \ 37 | libstdc++ 38 | COPY --from=builder /tmp/bin/* /usr/bin/ 39 | -------------------------------------------------------------------------------- /docs/cluon-UDPSender.md: -------------------------------------------------------------------------------- 1 | # How to send bytes via a UDP socket 2 | 3 | To send data using a UDP socket, simply include the header `#include `. 4 | 5 | Next, create an instance of class `cluon::UDPSender` as follows: `cluon::UDPSender sender("127.0.0.1", 1234);`. The first parameter is of type `std::string` expecting a numerical IPv4 address and the second parameter specifies the UDP port to which the data shall be sent to. 6 | 7 | To finally send data, simply call the method `send` supplying the data to be sent: `sender.send(std::move("Hello World!")`. Please note that the data is supplied using the _move_-semantics. The method `send` returns a `std::pair` where the first element returns the size of the successfully sent bytes and the second element contains the error code in case the transmission of the data failed. 8 | 9 | ```c++ 10 | cluon::UDPSender sender("127.0.0.1", 1234); 11 | 12 | std::pair retVal = sender.send(std::move("Hello World!")); 13 | 14 | std::cout << "Send " << retVal.first << " bytes, error code = " << retVal.second << std::endl; 15 | ``` 16 | 17 | The code needs to be compiled using at least C++11 and linked with `libcluon` and `pthread`; please refer to the example with the GCC compiler below: 18 | 19 | ``` 20 | g++ -std=c++11 -o UDPSenderTest UDPSenderTest.cpp -lcluon -pthread 21 | ``` 22 | 23 | A complete example is available [here](https://github.com/chrberger/libcluon/blob/master/libcluon/examples/cluon-UDPSender.cpp). 24 | -------------------------------------------------------------------------------- /libcluon/include/cluon/TerminateHandler.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TERMINATEHANDLER_HPP 10 | #define CLUON_TERMINATEHANDLER_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | #include 15 | #include 16 | 17 | namespace cluon { 18 | 19 | class LIBCLUON_API TerminateHandler { 20 | private: 21 | TerminateHandler(const TerminateHandler &) = delete; 22 | TerminateHandler(TerminateHandler &&) = delete; 23 | TerminateHandler &operator=(const TerminateHandler &) = delete; 24 | TerminateHandler &operator=(TerminateHandler &&) = delete; 25 | 26 | public: 27 | /** 28 | * Define singleton behavior using static initializer (cf. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf, Sec. 6.7.4). 29 | * @return singleton for an instance of this class. 30 | */ 31 | static TerminateHandler &instance() noexcept { 32 | static TerminateHandler instance; 33 | return instance; 34 | } 35 | 36 | ~TerminateHandler() = default; 37 | 38 | public: 39 | std::atomic isTerminated{false}; 40 | 41 | private: 42 | TerminateHandler() noexcept; 43 | 44 | #ifndef WIN32 45 | struct sigaction m_signalHandler {}; 46 | #endif 47 | }; 48 | } // namespace cluon 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /libcluon/thirdparty/argh/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Adi Shavit 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of nor the names of its contributors may be used to 13 | endorse or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /libcluon/include/cluon/MsgPackConstants.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_MSGPACKCONSTANTS_HPP 10 | #define CLUON_MSGPACKCONSTANTS_HPP 11 | 12 | #include 13 | 14 | // clang-format off 15 | namespace cluon { 16 | enum class MsgPackConstants : uint16_t { 17 | IS_FALSE = 0xC2, 18 | IS_TRUE = 0xC3, 19 | FLOAT = 0xCA, 20 | DOUBLE = 0xCB, 21 | UINT8 = 0xCC, 22 | UINT16 = 0xCD, 23 | UINT32 = 0xCE, 24 | UINT64 = 0xCF, 25 | NEGFIXINT = 0xE0, 26 | INT8 = 0xD0, 27 | INT16 = 0xD1, 28 | INT32 = 0xD2, 29 | INT64 = 0xD3, 30 | FIXSTR = 0xA0, 31 | FIXSTR_END = 0xBF, 32 | STR8 = 0xD9, 33 | STR16 = 0xDA, 34 | STR32 = 0xDB, 35 | FIXMAP = 0x80, 36 | FIXMAP_END = 0x8F, 37 | MAP16 = 0xDE, 38 | MAP32 = 0xDF, 39 | UNKNOWN_FORMAT = 0xFF00, 40 | BOOL_FORMAT = 0xFF01, 41 | UINT_FORMAT = 0xFF02, 42 | INT_FORMAT = 0xFF03, 43 | FLOAT_FORMAT = 0xFF04, 44 | STR_FORMAT = 0xFF05, 45 | MAP_FORMAT = 0xFF06, // Indicating also nested types. 46 | }; 47 | } 48 | // clang-format on 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestNotifyingPipeline.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/NotifyingPipeline.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | TEST_CASE("Creating a NotifyingPipeline and stop immediately.") { 20 | cluon::NotifyingPipeline pipeline(nullptr); 21 | REQUIRE(pipeline.isRunning()); 22 | } 23 | 24 | TEST_CASE("Creating a NotifyingPipeline, add one entry, and get notified.") { 25 | std::atomic hasDataReceived{false}; 26 | std::string data; 27 | 28 | try { 29 | cluon::NotifyingPipeline pipeline([&hasDataReceived, &data](std::string &&entry) { 30 | hasDataReceived.store(true); 31 | data = entry; 32 | }); 33 | REQUIRE(pipeline.isRunning()); 34 | REQUIRE(!hasDataReceived); 35 | REQUIRE(data.empty()); 36 | 37 | std::string dataToSend("Hello World"); 38 | pipeline.add(std::move(dataToSend)); 39 | 40 | REQUIRE(!hasDataReceived); 41 | pipeline.notifyAll(); 42 | 43 | // Yield the UDP receiver so that the embedded thread has time to process the data. 44 | using namespace std::literals::chrono_literals; // NOLINT 45 | do { std::this_thread::sleep_for(1ms); } while (!hasDataReceived.load()); 46 | 47 | REQUIRE("Hello World" == data); 48 | } catch (...) { REQUIRE(false); } // LCOV_EXCL_LINE 49 | } 50 | -------------------------------------------------------------------------------- /libcluon/include/cluon/MetaMessageToCPPTransformator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_METAMESSAGETOCPPTRANSFORMATOR_HPP 10 | #define CLUON_METAMESSAGETOCPPTRANSFORMATOR_HPP 11 | 12 | #include "Mustache/mustache.hpp" 13 | #include "cluon/MetaMessage.hpp" 14 | #include "cluon/cluon.hpp" 15 | 16 | #include 17 | 18 | namespace cluon { 19 | /** 20 | This class transforms a given MetaMessage to a C++ header and source content. 21 | 22 | Transformation is using https://github.com/kainjow/Mustache. 23 | */ 24 | class LIBCLUON_API MetaMessageToCPPTransformator { 25 | private: 26 | MetaMessageToCPPTransformator(MetaMessageToCPPTransformator &&) = delete; 27 | MetaMessageToCPPTransformator &operator=(const MetaMessageToCPPTransformator &) = delete; 28 | MetaMessageToCPPTransformator &operator=(MetaMessageToCPPTransformator &&) = delete; 29 | 30 | public: 31 | MetaMessageToCPPTransformator() = default; 32 | MetaMessageToCPPTransformator(const MetaMessageToCPPTransformator &) = default; 33 | 34 | /** 35 | * The method is called from MetaMessage to visit itself using this transformator. 36 | * 37 | * @param mm MetaMessage to visit. 38 | */ 39 | void visit(const MetaMessage &mm) noexcept; 40 | 41 | /** 42 | * @return Content of the C++ header. 43 | */ 44 | std::string content() noexcept; 45 | 46 | private: 47 | kainjow::mustache::data m_dataToBeRendered{}; 48 | kainjow::mustache::data m_fields{kainjow::mustache::data::type::list}; 49 | }; 50 | } // namespace cluon 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /libcluon/include/cluon/MetaMessageToProtoTransformator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_METAMESSAGETOPROTOTRANSFORMATOR_HPP 10 | #define CLUON_METAMESSAGETOPROTOTRANSFORMATOR_HPP 11 | 12 | #include "Mustache/mustache.hpp" 13 | #include "cluon/MetaMessage.hpp" 14 | #include "cluon/cluon.hpp" 15 | 16 | #include 17 | 18 | namespace cluon { 19 | /** 20 | This class transforms a given MetaMessage to a .proto file. 21 | 22 | Transformation is using https://github.com/kainjow/Mustache. 23 | */ 24 | class LIBCLUON_API MetaMessageToProtoTransformator { 25 | private: 26 | MetaMessageToProtoTransformator(MetaMessageToProtoTransformator &&) = delete; 27 | MetaMessageToProtoTransformator &operator=(const MetaMessageToProtoTransformator &) = delete; 28 | MetaMessageToProtoTransformator &operator=(MetaMessageToProtoTransformator &&) = delete; 29 | 30 | public: 31 | MetaMessageToProtoTransformator() = default; 32 | MetaMessageToProtoTransformator(const MetaMessageToProtoTransformator &) = default; 33 | 34 | /** 35 | * The method is called from MetaMessage to visit itself using this transformator. 36 | * 37 | * @param mm MetaMessage to visit. 38 | */ 39 | void visit(const MetaMessage &mm) noexcept; 40 | 41 | /** 42 | * @return Content of the .proto file. 43 | */ 44 | std::string content(bool withProtoHeader) noexcept; 45 | 46 | private: 47 | kainjow::mustache::data m_dataToBeRendered; 48 | kainjow::mustache::data m_fields{kainjow::mustache::data::type::list}; 49 | }; 50 | } // namespace cluon 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestTime.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/Time.hpp" 12 | #include "cluon/cluonDataStructures.hpp" 13 | #include 14 | #include 15 | 16 | TEST_CASE("Test Time.") { 17 | cluon::data::TimeStamp before{cluon::time::now()}; 18 | 19 | using namespace std::literals::chrono_literals; // NOLINT 20 | std::this_thread::sleep_for(1s); 21 | 22 | cluon::data::TimeStamp after{cluon::time::now()}; 23 | 24 | REQUIRE(cluon::time::toMicroseconds(after) > cluon::time::toMicroseconds(before)); 25 | } 26 | 27 | TEST_CASE("Test delta.") { 28 | cluon::data::TimeStamp before; 29 | before.seconds(0).microseconds(100); 30 | 31 | cluon::data::TimeStamp after; 32 | after.seconds(0).microseconds(300); 33 | 34 | REQUIRE(200 == cluon::time::deltaInMicroseconds(after, before)); 35 | } 36 | 37 | TEST_CASE("Test from microseconds.") { 38 | int64_t timePoint{static_cast(2 * 1000 * 1000) + 13}; 39 | cluon::data::TimeStamp result = cluon::time::fromMicroseconds(timePoint); 40 | 41 | REQUIRE(2 == result.seconds()); 42 | REQUIRE(13 == result.microseconds()); 43 | } 44 | 45 | TEST_CASE("Test convert Time.") { 46 | cluon::data::TimeStamp before{cluon::time::now()}; 47 | 48 | using namespace std::literals::chrono_literals; // NOLINT 49 | std::this_thread::sleep_for(1s); 50 | 51 | std::chrono::system_clock::time_point after = std::chrono::system_clock::now(); 52 | 53 | cluon::data::TimeStamp after2 = cluon::time::convert(after); 54 | 55 | REQUIRE(cluon::time::toMicroseconds(after2) > cluon::time::toMicroseconds(before)); 56 | } 57 | -------------------------------------------------------------------------------- /libcluon/src/TerminateHandler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/TerminateHandler.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cluon { 16 | 17 | inline void cluon_handleExit() { 18 | TerminateHandler::instance().isTerminated.store(true); 19 | } 20 | 21 | inline void cluon_handleSignal(int32_t /*signal*/) { // LCOV_EXCL_LINE 22 | TerminateHandler::instance().isTerminated.store(true); // LCOV_EXCL_LINE 23 | } 24 | 25 | TerminateHandler::TerminateHandler() noexcept { 26 | if (0 != std::atexit(cluon_handleExit)) { 27 | std::cerr << "[cluon::TerminateHandler] Failed to register cluon_exitHandler()." << std::endl; // LCOV_EXCL_LINE 28 | } 29 | 30 | #ifdef WIN32 31 | if (SIG_ERR == ::signal(SIGINT, &cluon_handleSignal)) { 32 | std::cerr << "[cluon::TerminateHandler] Failed to register signal SIGINT." << std::endl; 33 | } 34 | if (SIG_ERR == ::signal(SIGTERM, &cluon_handleSignal)) { 35 | std::cerr << "[cluon::TerminateHandler] Failed to register signal SIGTERM." << std::endl; 36 | } 37 | #else 38 | std::memset(&m_signalHandler, 0, sizeof(m_signalHandler)); 39 | m_signalHandler.sa_handler = &cluon_handleSignal; 40 | 41 | if (::sigaction(SIGINT, &m_signalHandler, NULL) < 0) { 42 | std::cerr << "[cluon::TerminateHandler] Failed to register signal SIGINT." << std::endl; // LCOV_EXCL_LINE 43 | } 44 | if (::sigaction(SIGTERM, &m_signalHandler, NULL) < 0) { 45 | std::cerr << "[cluon::TerminateHandler] Failed to register signal SIGTERM." << std::endl; // LCOV_EXCL_LINE 46 | } 47 | #endif 48 | } 49 | 50 | } // namespace cluon 51 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-OD4toStdout.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_OD4TOSTDOUT_HPP 10 | #define CLUON_OD4TOSTDOUT_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | #include "cluon/Envelope.hpp" 14 | #include "cluon/OD4Session.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | inline int32_t cluon_OD4toStdout(int32_t argc, char **argv) { 22 | int retVal{1}; 23 | const std::string PROGRAM{argv[0]}; // NOLINT 24 | auto commandlineArguments = cluon::getCommandlineArguments(argc, argv); 25 | if (0 == commandlineArguments.count("cid")) { 26 | std::cerr << PROGRAM 27 | << " dumps Envelopes received from an OD4Session to stdout." << std::endl; 28 | std::cerr << "Usage: " << PROGRAM << " --cid=" << std::endl; 29 | std::cerr << "Example: " << PROGRAM << " --cid=111" << std::endl; 30 | } 31 | else { 32 | // Interface to a running OpenDaVINCI session (ignoring any incoming Envelopes). 33 | cluon::OD4Session od4Session(static_cast(std::stoi(commandlineArguments["cid"])), 34 | [](cluon::data::Envelope &&envelope) noexcept { 35 | std::cout << cluon::serializeEnvelope(std::move(envelope)); 36 | std::cout.flush(); 37 | }); 38 | 39 | if (od4Session.isRunning()) { 40 | using namespace std::literals::chrono_literals; // NOLINT 41 | while (od4Session.isRunning()) { 42 | std::this_thread::sleep_for(1s); 43 | } 44 | retVal = 0; 45 | } 46 | } 47 | return retVal; 48 | } 49 | 50 | #endif 51 | 52 | -------------------------------------------------------------------------------- /libcluon/examples/cluon-UDPReceiver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifdef WIN32 10 | // Disable deprecated API warnings. 11 | #pragma warning(disable : 4996) 12 | #endif 13 | 14 | #include "cluon/UDPReceiver.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | int main(int argc, char **argv) { 24 | int retVal{1}; 25 | const std::string PROGRAM(argv[0]); // NOLINT 26 | if (3 != argc) { 27 | std::cerr << PROGRAM 28 | << " demonstrates how to use libcluon to receive data via UDP (running for 60s)." << std::endl; 29 | std::cerr << "Usage: " << PROGRAM << " IPv4-address port" << std::endl; 30 | std::cerr << "Example: " << PROGRAM << " 127.0.0.1 1234" << std::endl; 31 | } else { 32 | const std::string ADDRESS(argv[1]); // NOLINT 33 | const std::string PORT(argv[2]); // NOLINT 34 | 35 | cluon::UDPReceiver receiver( 36 | ADDRESS, 37 | static_cast(std::stoi(PORT)), 38 | [](std::string && data, std::string && sender, std::chrono::system_clock::time_point && ts) noexcept { 39 | const auto timestamp(std::chrono::system_clock::to_time_t(ts)); 40 | std::cout << "Received " << data.size() << " bytes from " << sender << " at " 41 | << timestamp << "s" << ", containing '" << data 42 | << "'." << std::endl; 43 | }); 44 | 45 | if (receiver.isRunning()) { 46 | using namespace std::literals::chrono_literals; // NOLINT 47 | std::this_thread::sleep_for(60s); 48 | retVal = 0; 49 | } 50 | } 51 | return retVal; 52 | } 53 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: v{build} 2 | image: 3 | - Visual Studio 2017 4 | skip_tags: true 5 | build: 6 | verbosity: quiet 7 | 8 | 9 | branches: 10 | # blacklist 11 | except: 12 | - gh-pages 13 | 14 | environment: 15 | matrix: 16 | - APPVEYOR_YML_DISABLE_PS_LINUX: true 17 | CMAKE_GENERATOR: "Visual Studio 15 2017 Win64" 18 | CMAKE_CONFIG: Debug 19 | 20 | 21 | 22 | build_script: 23 | - cmd: cmake -E make_directory Win64 24 | - cmd: cd Win64 25 | - cmd: cmake "-G%CMAKE_GENERATOR%" -D CMAKE_BUILD_TYPE=%CMAKE_CONFIG% -D CMAKE_INSTALL_PREFIX=./install ../libcluon 26 | - cmd: cmake --build . 27 | - cmd: cd .. 28 | - ps: $release = Get-Content C:\projects\libcluon\VERSION 29 | 30 | 31 | test_script: 32 | - cmd: cd Win64 33 | - cmd: ctest -VV --output-on-failure -C "%CMAKE_CONFIG%" 34 | 35 | 36 | # scripts to run after tests 37 | after_test: 38 | - cmd: set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH% 39 | - cmd: cmake --build . --target install 40 | - ps: Get-Content C:\projects\libcluon\buildtools\innosetup\setup.iss.template | Foreach-Object {$_ -replace "RELEASE", $release} | Set-Content C:\projects\libcluon\buildtools\innosetup\setup.iss 41 | - cmd: ISCC.exe C:\projects\libcluon\buildtools\innosetup\setup.iss 42 | 43 | 44 | artifacts: 45 | - path: 'buildtools\innosetup\libcluon-*.exe' 46 | 47 | 48 | deploy: 49 | - provider: BinTray 50 | on: 51 | branch: master 52 | username: chrberger 53 | api_key: 54 | secure: UMVlea7yObUN1uFkjHKSaqozIOYedVU0FAr4DDHlQoK4JXE9OQC1RCHJw7IS9S2z 55 | subject: chrberger 56 | repo: libcluon 57 | package: libcluon-win64-debug 58 | version: master2 59 | publish: true 60 | override: true 61 | explode: true 62 | 63 | 64 | notifications: 65 | - provider: Slack 66 | auth_token: 67 | secure: Tt0Bj1T4fHiCygiv2GpvUumDR+XmqV14ginGuIOfDzYg6DutaZ4BLdgTMs0vsP79ojx4OWLO7DxejmkEVearHe7EtCrrGZivtRUOH8gEkTc= 68 | channel: '#appveyor' 69 | on_build_success: true 70 | on_build_failure: true 71 | on_build_status_changed: true 72 | 73 | -------------------------------------------------------------------------------- /libcluon/include/cluon/LCMToGenericMessage.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_LCMTOGENERICMESSAGE_HPP 10 | #define CLUON_LCMTOGENERICMESSAGE_HPP 11 | 12 | #include "cluon/GenericMessage.hpp" 13 | #include "cluon/MetaMessage.hpp" 14 | #include "cluon/cluon.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cluon { 22 | /** 23 | This class transforms a given LCM message into a GenericMessage. 24 | */ 25 | class LIBCLUON_API LCMToGenericMessage { 26 | private: 27 | LCMToGenericMessage(const LCMToGenericMessage &) = delete; 28 | LCMToGenericMessage(LCMToGenericMessage &&) = delete; 29 | LCMToGenericMessage &operator=(const LCMToGenericMessage &) = delete; 30 | LCMToGenericMessage &operator=(LCMToGenericMessage &&) = delete; 31 | 32 | public: 33 | LCMToGenericMessage() = default; 34 | 35 | /** 36 | * This method sets the message specification to be used for 37 | * interpreting a given LCM-encoded message. 38 | * 39 | * @param ms Message specification following the ODVD format. 40 | * @return -1 in case of invalid message specification; otherwise, number 41 | * of successfully parsed messages from given message specification. 42 | */ 43 | int32_t setMessageSpecification(const std::string &ms) noexcept; 44 | 45 | /** 46 | * This method transforms the given LCM payload into a GenericMessage. 47 | * 48 | * @param data LCM Payload. 49 | * @return GenericMessage representation using the given message specification. 50 | */ 51 | cluon::GenericMessage getGenericMessage(const std::string &data) noexcept; 52 | 53 | private: 54 | std::vector m_listOfMetaMessages{}; 55 | std::map m_scopeOfMetaMessages{}; 56 | }; 57 | } // namespace cluon 58 | #endif 59 | -------------------------------------------------------------------------------- /libcluon/examples/cluon-TCPServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifdef WIN32 10 | // Disable deprecated API warnings. 11 | #pragma warning(disable : 4996) 12 | #endif 13 | 14 | #include "cluon/TCPConnection.hpp" 15 | #include "cluon/TCPServer.hpp" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | int main(int argc, char **argv) { 25 | int retVal{1}; 26 | const std::string PROGRAM(argv[0]); // NOLINT 27 | if (2 != argc) { 28 | std::cerr << PROGRAM 29 | << " demonstrates how to use libcluon to send & receive data as a TCP server (running for 60s)." << std::endl; 30 | std::cerr << "Usage: " << PROGRAM << " port" << std::endl; 31 | std::cerr << "Example: " << PROGRAM << " 1234" << std::endl; 32 | } else { 33 | const std::string PORT(argv[1]); // NOLINT 34 | std::vector > connections; 35 | cluon::TCPServer srv(static_cast(std::stoi(PORT)), [&connections](std::string &&from, std::shared_ptr connection){ 36 | std::cout << "Got connection from " << from << std::endl; 37 | connection->setOnNewData([](std::string &&data, std::chrono::system_clock::time_point &&){ 38 | std::cout << "Data: '" << data << "'" << std::endl; 39 | }); 40 | connection->setOnConnectionLost([](){ 41 | std::cout << "Connection lost." << std::endl; 42 | }); 43 | connections.push_back(connection); 44 | }); 45 | 46 | int counter{0}; 47 | while (srv.isRunning() && (60 > counter++)) { 48 | using namespace std::literals::chrono_literals; // NOLINT 49 | std::this_thread::sleep_for(1s); 50 | } 51 | retVal = 0; 52 | } 53 | return retVal; 54 | } 55 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestcluonDataStructures.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/cluon.hpp" 12 | #include "cluon/cluonDataStructures.hpp" 13 | 14 | TEST_CASE("Test cluon::data::TimeStamp.") { 15 | cluon::data::TimeStamp ts; 16 | REQUIRE("TimeStamp" == cluon::data::TimeStamp::ShortName()); 17 | REQUIRE("cluon.data.TimeStamp" == cluon::data::TimeStamp::LongName()); 18 | REQUIRE(0 == ts.seconds()); 19 | REQUIRE(0 == ts.microseconds()); 20 | REQUIRE(12 == cluon::data::TimeStamp::ID()); 21 | } 22 | 23 | TEST_CASE("Test cluon::data::Envelope.") { 24 | cluon::data::Envelope env; 25 | REQUIRE("Envelope" == cluon::data::Envelope::ShortName()); 26 | REQUIRE("cluon.data.Envelope" == cluon::data::Envelope::LongName()); 27 | 28 | REQUIRE(0 == env.dataType()); 29 | REQUIRE(env.serializedData().empty()); 30 | REQUIRE(0 == env.sent().seconds()); 31 | REQUIRE(0 == env.sent().microseconds()); 32 | REQUIRE(0 == env.received().seconds()); 33 | REQUIRE(0 == env.received().microseconds()); 34 | REQUIRE(0 == env.sampleTimeStamp().seconds()); 35 | REQUIRE(0 == env.sampleTimeStamp().microseconds()); 36 | } 37 | 38 | TEST_CASE("Test cluon::data::PlayerCommand.") { 39 | cluon::data::PlayerCommand pc; 40 | REQUIRE("PlayerCommand" == cluon::data::PlayerCommand::ShortName()); 41 | REQUIRE("cluon.data.PlayerCommand" == cluon::data::PlayerCommand::LongName()); 42 | 43 | REQUIRE(0 == pc.command()); 44 | REQUIRE(0.0f == Approx(pc.seekTo())); 45 | } 46 | 47 | TEST_CASE("Test cluon::data::PlayerStatus.") { 48 | cluon::data::PlayerStatus ps; 49 | REQUIRE("PlayerStatus" == cluon::data::PlayerStatus::ShortName()); 50 | REQUIRE("cluon.data.PlayerStatus" == cluon::data::PlayerStatus::LongName()); 51 | 52 | REQUIRE(0 == ps.state()); 53 | REQUIRE(0 == ps.numberOfEntries()); 54 | REQUIRE(0 == ps.currentEntryForPlayback()); 55 | } 56 | -------------------------------------------------------------------------------- /libcluon/examples/cluon-TCPConnection.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifdef WIN32 10 | // Disable deprecated API warnings. 11 | #pragma warning(disable : 4996) 12 | #endif 13 | 14 | #include "cluon/TCPConnection.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | int main(int argc, char **argv) { 24 | int retVal{1}; 25 | const std::string PROGRAM(argv[0]); // NOLINT 26 | if (3 != argc) { 27 | std::cerr << PROGRAM 28 | << " demonstrates how to use libcluon to send & receive data using a TCP connection (running for 60s)." << std::endl; 29 | std::cerr << "Usage: " << PROGRAM << " IPv4-address port" << std::endl; 30 | std::cerr << "Example: " << PROGRAM << " 127.0.0.1 1234" << std::endl; 31 | } else { 32 | const std::string ADDRESS(argv[1]); // NOLINT 33 | const std::string PORT(argv[2]); // NOLINT 34 | 35 | cluon::TCPConnection connection( 36 | ADDRESS, 37 | static_cast(std::stoi(PORT)), 38 | [](std::string && data, std::chrono::system_clock::time_point && ts) noexcept { 39 | const auto timestamp(std::chrono::system_clock::to_time_t(ts)); 40 | std::cout << "Received " << data.size() << " bytes at " 41 | << timestamp << "s" << ", containing '" << data 42 | << "'." << std::endl; 43 | }, 44 | [](){ std::cout << "Connection lost." << std::endl; }); 45 | 46 | if (connection.isRunning()) { 47 | using namespace std::literals::chrono_literals; // NOLINT 48 | std::this_thread::sleep_for(5s); 49 | connection.send("Sending demo data!"); 50 | 51 | std::this_thread::sleep_for(60s); 52 | retVal = 0; 53 | } 54 | } 55 | return retVal; 56 | } 57 | -------------------------------------------------------------------------------- /docs/cluon-UDPReceiver.md: -------------------------------------------------------------------------------- 1 | # How to receive bytes via a UDP socket 2 | 3 | To receive data using a UDP socket, simply include the header `#include `. 4 | 5 | Next, create an instance of class `cluon::UDPReceiver` as follows: `cluon::UDPReceiver receiver("127.0.0.1", 1234, delegate);`. 6 | The first parameter is of type `std::string` expecting a numerical IPv4 address, the second parameter 7 | specifies the UDP port from which data shall be received from, and the last parameter is of type 8 | `std::function` that is called whenever new bytes are available to be processed. 9 | 10 | The complete signature for the delegate function is 11 | `std::function`: 12 | The first parameter contains the bytes that have been received, the second parameter 13 | containes the human-readable representation of the sender (X.Y.Z.W:ABCD), and the last 14 | parameter is the time stamp when the data has been received. An example using a C++ lambda 15 | expression would look as follows: 16 | 17 | ```c++ 18 | cluon::UDPReceiver receiver("127.0.0.1", 1234, 19 | [](std::string &&data, std::string &&sender, std::chrono::system_clock::time_point &&ts) noexcept { 20 | const auto timestamp(std::chrono::system_clock::to_time_t(ts)); 21 | std::cout << "Received " << data.size() << " bytes from " << sender << " at " 22 | << std::put_time(std::localtime(×tamp), "%Y-%m-%d %X") << ", containing '" << data 23 | << "'." << std::endl; 24 | }); 25 | ``` 26 | 27 | After creating an instance of class `cluon::UDPReceiver`, it is immediately activated and 28 | concurrently waiting for data. To check whether the instance was created successfully and 29 | running, the method `isRunning()` can be called. 30 | 31 | The code needs to be compiled using at least C++11 and linked with `libcluon` and `pthread`; 32 | please refer to the example with the GCC compiler below: 33 | 34 | ``` 35 | g++ -std=c++11 -o UDPReceiverTest UDPReceiverTest.cpp -lcluon -pthread 36 | ``` 37 | 38 | A complete example is available [here](https://github.com/chrberger/libcluon/blob/master/libcluon/examples/cluon-UDPReceiver.cpp). 39 | -------------------------------------------------------------------------------- /libcluon/include/cluon/TCPServer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TCPSERVER_HPP 10 | #define CLUON_TCPSERVER_HPP 11 | 12 | #include "cluon/TCPConnection.hpp" 13 | #include "cluon/cluon.hpp" 14 | 15 | // clang-format off 16 | #ifdef WIN32 17 | #include // for WSAStartUp 18 | #include // for SOCKET 19 | #else 20 | #include 21 | #endif 22 | // clang-format on 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace cluon { 32 | 33 | class LIBCLUON_API TCPServer { 34 | private: 35 | TCPServer(const TCPServer &) = delete; 36 | TCPServer(TCPServer &&) = delete; 37 | TCPServer &operator=(const TCPServer &) = delete; 38 | TCPServer &operator=(TCPServer &&) = delete; 39 | 40 | public: 41 | /** 42 | * Constructor to create a TCP server. 43 | * 44 | * @param port Port to receive UDP packets from. 45 | * @param newConnectionDelegate Functional to handle incoming TCP connections. 46 | */ 47 | TCPServer(uint16_t port, std::function connection)> newConnectionDelegate) noexcept; 48 | 49 | ~TCPServer() noexcept; 50 | 51 | /** 52 | * @return true if the TCPServer could successfully be created and is able to receive data. 53 | */ 54 | bool isRunning() const noexcept; 55 | 56 | private: 57 | /** 58 | * This method closes the socket. 59 | * 60 | * @param errorCode Error code that caused this closing. 61 | */ 62 | void closeSocket(int errorCode) noexcept; 63 | void readFromSocket() noexcept; 64 | 65 | private: 66 | mutable std::mutex m_socketMutex{}; 67 | int32_t m_socket{-1}; 68 | 69 | std::atomic m_readFromSocketThreadRunning{false}; 70 | std::thread m_readFromSocketThread{}; 71 | 72 | std::mutex m_newConnectionDelegateMutex{}; 73 | std::function connection)> m_newConnectionDelegate{}; 74 | }; 75 | } // namespace cluon 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /libcluon/tools/libcluon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | // clang-format off 10 | #ifdef __EMSCRIPTEN__ 11 | #include 12 | #include 13 | using namespace emscripten; 14 | #endif 15 | // clang-format off 16 | 17 | #include "cluon/EnvelopeConverter.hpp" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | static std::mutex envConverterMutex; 24 | static cluon::EnvelopeConverter envConverter; 25 | 26 | int setMessageSpecification(const std::string &s); 27 | std::string decodeEnvelopeToJSON(const std::string &s); 28 | std::string encodeEnvelopeFromJSONWithoutTimeStamps(const std::string &s, int32_t messageIdentifier, uint32_t senderStamp); 29 | 30 | int setMessageSpecification(const std::string &s) { 31 | std::lock_guard lck(envConverterMutex); 32 | return envConverter.setMessageSpecification(s); 33 | } 34 | 35 | std::string decodeEnvelopeToJSON(const std::string &s) { 36 | std::lock_guard lck(envConverterMutex); 37 | return envConverter.getJSONFromProtoEncodedEnvelope(s); 38 | } 39 | 40 | std::string encodeEnvelopeFromJSONWithoutTimeStamps(const std::string &s, int32_t messageIdentifier, uint32_t senderStamp) { 41 | std::lock_guard lck(envConverterMutex); 42 | return envConverter.getProtoEncodedEnvelopeFromJSONWithoutTimeStamps(s, messageIdentifier, senderStamp); 43 | } 44 | 45 | std::string encodeEnvelopeFromJSONWithSampleTimeStamp(const std::string &s, int32_t messageIdentifier, uint32_t senderStamp) { 46 | std::lock_guard lck(envConverterMutex); 47 | return envConverter.getProtoEncodedEnvelopeFromJSON(s, messageIdentifier, senderStamp); 48 | } 49 | 50 | int main(int argc, char **argv) { 51 | (void)argc; 52 | (void)argv; 53 | return 0; 54 | } 55 | 56 | #ifdef __EMSCRIPTEN__ 57 | EMSCRIPTEN_BINDINGS(libcluon) { 58 | function("setMessageSpecification", &setMessageSpecification); 59 | function("decodeEnvelopeToJSON", &decodeEnvelopeToJSON); 60 | function("encodeEnvelopeFromJSONWithoutTimeStamps", &encodeEnvelopeFromJSONWithoutTimeStamps); 61 | function("encodeEnvelopeFromJSONWithSampleTimeStamp", &encodeEnvelopeFromJSONWithSampleTimeStamp); 62 | } 63 | #endif 64 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | How to contribute to the development of libcluon 2 | ================================================ 3 | 4 | Thank you for taking a moment to contribute to read these continuously evolving instructions how to contribute to libcluon. 5 | To maintain a high quality of our code base, we are very strict in terms of reviewing and accepting pull requests (PR). 6 | 7 | ## Getting Started 8 | - Make sure you have a GitHub account. 9 | - Make sure you use an up-to-date C++14 compiler; we are usually developing with GNU GCC. 10 | - Register a report about your issue but check beforehand [our issue list](https://github.com/chrberger/libcluon/issues) for similar problems. 11 | 12 | ## Making Changes 13 | - Create a named branch where you want to base your work. 14 | - We are using the YYYYQN.Z.topic scheme with small letters, where YYYY is the year, QN is the current quarter, Z is one of `feature` or `fix`, and Z is the topic. Example: 2017Q4.feature.awesome-feature. This scheme allows us to quickly list and sort branches. 15 | - Make sure that your topic branched off from master. 16 | - Make changes and commits. 17 | - We are only reviewing PRs when you have added sufficient test cases and maintain a good test coverage. We are using [![CodeCov](https://codecov.io/gh/chrberger/libcluon)](https://codecov.io/gh/chrberger/libcluon) for visualizing code coverage. 18 | - Make sure you're sticking with our code style. You can run [`clang-format`](http://clang.llvm.org/docs/ClangFormat.html) manually or using our Docker development image. 19 | - We highly encourage the use of our Docker development image: 20 | - Creating the Docker development image: `make createDockerBuildImage` 21 | - Cleaning the build folder: `make clean` 22 | - Running a build: `make compile && make test` 23 | - Re-formatting the code according to our coding guidelines: `make reformat-code` 24 | - Running our static-code-analysis (currently including: [`scan-build`](https://clang-analyzer.llvm.org/scan-build.html), [`clang-tidy`](http://clang.llvm.org/extra/clang-tidy/), [`flawfinder`](https://www.dwheeler.com/flawfinder/), [`oclint`](https://github.com/oclint/oclint), [`vera++`](https://bitbucket.org/verateam/vera/wiki/Home), [`cppcheck`](http://cppcheck.sourceforge.net/), and [`Flint++`](https://github.com/L2Program/FlintPlusPlus)): `make static-code-analysis` 25 | - Any PRs that are not following these recommendations will not be considered for potential inclusion! 26 | 27 | ## Coding Guidelines 28 | - Keep the highest possible warning level. 29 | - We treat warnings as errors. 30 | - Keep C++14-only. 31 | - Keep dependency-free. 32 | - Keep platform-independent (i.e., prefer C++ standard library instead of Windows/POSIX dependent API). 33 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-OD4toJSON.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_OD4TOJSON_HPP 10 | #define CLUON_OD4TOJSON_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | #include "cluon/OD4Session.hpp" 14 | #include "cluon/EnvelopeConverter.hpp" 15 | #include "cluon/Time.hpp" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | inline int32_t cluon_OD4toJSON(int32_t argc, char **argv) { 25 | int retVal{1}; 26 | const std::string PROGRAM{argv[0]}; // NOLINT 27 | auto commandlineArguments = cluon::getCommandlineArguments(argc, argv); 28 | if (0 == commandlineArguments.count("cid")) { 29 | std::cerr << PROGRAM 30 | << " dumps Containers received from an OpenDaVINCI v4 session to stdout in JSON format using an optionally supplied ODVD message specification file." << std::endl; 31 | std::cerr << "Usage: " << PROGRAM << " [--odvd=] --cid=" << std::endl; 32 | std::cerr << "Examples: " << PROGRAM << " --cid=111" << std::endl; 33 | std::cerr << " " << PROGRAM << " --odvd=MyMessages.odvd --cid=111" << std::endl; 34 | } else { 35 | std::string odvdFile{commandlineArguments["odvd"]}; 36 | cluon::EnvelopeConverter envConverter; 37 | if (!odvdFile.empty()) { 38 | std::fstream fin{odvdFile, std::ios::in}; 39 | if (fin.good()) { 40 | const std::string s{static_cast(std::stringstream() << fin.rdbuf()).str()}; // NOLINT 41 | std::clog << "Parsed " << envConverter.setMessageSpecification(s) << " message(s)." << std::endl; 42 | } 43 | } 44 | 45 | cluon::OD4Session od4Session(static_cast(std::stoi(commandlineArguments["cid"])), 46 | [&e2J = envConverter](cluon::data::Envelope &&envelope) noexcept { 47 | envelope.received(cluon::time::now()); 48 | std::cout << e2J.getJSONFromEnvelope(envelope) << std::endl; 49 | std::cout.flush(); 50 | }); 51 | 52 | if (od4Session.isRunning()) { 53 | using namespace std::literals::chrono_literals; // NOLINT 54 | while (od4Session.isRunning()) { 55 | std::this_thread::sleep_for(1s); 56 | } 57 | retVal = 0; 58 | } 59 | } 60 | return retVal; 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /libcluon/src/IPv4Tools.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/IPv4Tools.hpp" 10 | 11 | // clang-format off 12 | #ifdef WIN32 13 | #include // for WSAStartUp 14 | #include 15 | #include 16 | #else 17 | #include 18 | #include 19 | 20 | #if defined(BSD) 21 | #include 22 | #include 23 | #endif 24 | #endif 25 | // clang-format on 26 | 27 | #include 28 | 29 | namespace cluon { 30 | 31 | std::string getIPv4FromHostname(const std::string &hostname) noexcept { 32 | #ifdef WIN32 33 | // Load Winsock 2.2 DLL. 34 | WSADATA wsaData; 35 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { 36 | std::cerr << "[cluon::getIPv4FromHostname] Error while calling WSAStartUp: " << WSAGetLastError() << std::endl; 37 | } 38 | #endif 39 | std::string result{""}; 40 | if (!hostname.empty()) { 41 | struct addrinfo hint; 42 | { 43 | std::memset(&hint, 1, sizeof(struct addrinfo)); 44 | hint.ai_flags = AI_CANONNAME; 45 | hint.ai_family = AF_INET; 46 | hint.ai_socktype = 0; 47 | hint.ai_protocol = 0; 48 | hint.ai_addrlen = 0; 49 | hint.ai_canonname = nullptr; 50 | hint.ai_addr = nullptr; 51 | hint.ai_next = nullptr; 52 | } 53 | 54 | struct addrinfo *listOfHosts{nullptr}; 55 | if (0 == getaddrinfo(hostname.c_str(), nullptr, &hint, &listOfHosts)) { 56 | for(struct addrinfo *e = listOfHosts; nullptr != listOfHosts; listOfHosts = listOfHosts->ai_next) { 57 | if (nullptr != e) { 58 | if (AF_INET == e->ai_family) { 59 | struct sockaddr_in *sinp = reinterpret_cast(e->ai_addr); 60 | char buf[INET_ADDRSTRLEN]; 61 | const char *addr = inet_ntop(AF_INET, &sinp->sin_addr, buf, INET_ADDRSTRLEN); 62 | if ( (nullptr != addr) && (result.empty()) ) { 63 | result = std::string(addr); 64 | break; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | if (nullptr != listOfHosts) { 72 | freeaddrinfo(listOfHosts); 73 | } 74 | } 75 | #ifdef WIN32 76 | WSACleanup(); 77 | #endif 78 | return result; 79 | } 80 | 81 | } // namespace cluon 82 | -------------------------------------------------------------------------------- /libcluon/include/cluon/cluon.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_CLUON_HPP 10 | #define CLUON_CLUON_HPP 11 | 12 | // clang-format off 13 | #ifdef WIN32 14 | #ifdef _WIN64 15 | #define ssize_t __int64 16 | #else 17 | #define ssize_t long 18 | #endif 19 | 20 | // Disable warning "'<': signed/unsigned mismatch". 21 | #pragma warning(disable : 4018) 22 | // Disable warning "Unary minus operator applied to unsigned type, result still unsigned". 23 | #pragma warning(disable : 4146) 24 | // Disable warning "Possible loss of precision". 25 | #pragma warning(disable : 4244) 26 | // Disable warning "Conversion from 'size_t' to 'type', possible loss of data". 27 | #pragma warning(disable : 4267) 28 | // Disable warning "'static_cast': truncation of constant value". 29 | #pragma warning(disable : 4309) 30 | // Disable warning "'operator ""s': literal suffix identifiers that do not start with an underscore are reserved". 31 | #pragma warning(disable : 4455) 32 | // Disable deprecated API warnings. 33 | #pragma warning(disable : 4996) 34 | 35 | // Link against ws2_32.lib for networking. 36 | #pragma comment(lib, "ws2_32.lib") 37 | // Link against iphlpapi.lib for address resolving. 38 | #pragma comment(lib, "iphlpapi.lib") 39 | 40 | // Avoid include definitions from Winsock v1. 41 | #define WIN32_LEAN_AND_MEAN 42 | 43 | // Export symbols. 44 | #ifdef LIBCLUON_SHARED 45 | #ifdef LIBCLUON_EXPORTS 46 | #define LIBCLUON_API __declspec(dllexport) 47 | #else 48 | #define LIBCLUON_API __declspec(dllimport) 49 | #endif 50 | #else 51 | // If linking statically: 52 | #define LIBCLUON_API 53 | #endif 54 | #else 55 | // Undefine define for non-Win32 systems: 56 | #define LIBCLUON_API 57 | #endif 58 | // clang-format on 59 | 60 | #include "cluon/PortableEndian.hpp" 61 | 62 | #include 63 | #include 64 | 65 | namespace cluon { 66 | 67 | /** 68 | This class can be used to define hash keys. 69 | */ 70 | class UseUInt32ValueAsHashKey { 71 | public: 72 | inline std::size_t operator()(const uint32_t v) const noexcept { 73 | return static_cast(v); 74 | } 75 | }; 76 | 77 | /** 78 | * @return Map for command line parameters passed as --key=value into key->values. 79 | */ 80 | std::map getCommandlineArguments(int32_t argc, char **argv) noexcept; 81 | 82 | } // namespace cluon 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /libcluon/include/cluon/Time.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TIME_HPP 10 | #define CLUON_TIME_HPP 11 | 12 | #include "cluon/cluonDataStructures.hpp" 13 | 14 | #include 15 | 16 | namespace cluon { 17 | namespace time { 18 | 19 | /** 20 | * @param tp to be converted to TimeStamp. 21 | * @return TimeStamp converted from microseconds. 22 | */ 23 | inline cluon::data::TimeStamp fromMicroseconds(int64_t tp) noexcept { 24 | cluon::data::TimeStamp ts; 25 | ts.seconds(static_cast(tp / static_cast(1000 * 1000))).microseconds(static_cast(tp % static_cast(1000 * 1000))); 26 | return ts; 27 | } 28 | 29 | /** 30 | * @param tp to be converted to microseconds. 31 | * @return TimeStamp converted to microseconds. 32 | */ 33 | inline int64_t toMicroseconds(const cluon::data::TimeStamp &tp) noexcept { 34 | return static_cast(tp.seconds()) * static_cast(1000 * 1000) + static_cast(tp.microseconds()); 35 | } 36 | 37 | /** 38 | * @param AFTER First time stamp. 39 | * @param BEFORE Second time stamp. 40 | * @return Delta (AFTER - BEFORE) between two TimeStamps in microseconds. 41 | */ 42 | inline int64_t deltaInMicroseconds(const cluon::data::TimeStamp &AFTER, const cluon::data::TimeStamp &BEFORE) noexcept { 43 | return toMicroseconds(AFTER) - toMicroseconds(BEFORE); 44 | } 45 | 46 | /** 47 | * @param tp to be converted to microseconds. 48 | * @return TimeStamp of converted chrono::time_point. 49 | */ 50 | inline cluon::data::TimeStamp convert(const std::chrono::system_clock::time_point &tp) noexcept { 51 | cluon::data::TimeStamp timeStamp; 52 | 53 | // Transform chrono time representation to same behavior as gettimeofday. 54 | typedef std::chrono::duration seconds_type; 55 | typedef std::chrono::duration microseconds_type; 56 | 57 | auto duration = tp.time_since_epoch(); 58 | seconds_type s = std::chrono::duration_cast(duration); 59 | microseconds_type us = std::chrono::duration_cast(duration); 60 | microseconds_type partial_us = us - std::chrono::duration_cast(s); 61 | 62 | timeStamp.seconds(s.count()).microseconds(static_cast(partial_us.count())); 63 | 64 | return timeStamp; 65 | } 66 | 67 | /** 68 | * @return TimeStamp of now from std::chrono::system_clock. 69 | */ 70 | inline cluon::data::TimeStamp now() noexcept { 71 | return convert(std::chrono::system_clock::now()); 72 | } 73 | 74 | } // namespace time 75 | } // namespace cluon 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /libcluon/include/cluon/MessageParser.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_MESSAGEPARSER_HPP 10 | #define CLUON_MESSAGEPARSER_HPP 11 | 12 | #include "cluon/MetaMessage.hpp" 13 | #include "cluon/cluon.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace cluon { 21 | /** 22 | This class provides a parser for message specifications in .odvd format. The 23 | format is inspired by Google Protobuf (https://developers.google.com/protocol-buffers/) 24 | but simplified to enforce backwards and forwards compatibility next to 25 | numerical message identifiers. 26 | 27 | This message specification format is also used by OpenDaVINCI (http://code.opendavinci.org). 28 | 29 | The parser is based on https://github.com/yhirose/cpp-peglib. 30 | 31 | An example for a .odvd compliant message is demonstrated in the following: 32 | 33 | \code{.cpp} 34 | const char *spec = R"( 35 | message myMessage.SubName [id = 1] { 36 | uint8 field1 [id = 1]; 37 | uint32 field2 [id = 2]; 38 | int64 field3 [id = 3]; 39 | string field4 [id = 4]; 40 | } 41 | )"; 42 | 43 | cluon::MessageParser mp; 44 | auto retVal = mp.parse(std::string(spec)); 45 | if (retVal.second == cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR) { 46 | auto listOfMessages = retVal.first; 47 | for (auto message : listOfMessages) { 48 | message.accept([](const cluon::MetaMessage &mm){ std::cout << "Message name = " << mm.messageName() << 49 | std::endl; }); 50 | } 51 | } 52 | \endcode 53 | */ 54 | class LIBCLUON_API MessageParser { 55 | public: 56 | enum MessageParserErrorCodes : uint8_t { NO_MESSAGEPARSER_ERROR = 0, SYNTAX_ERROR = 1, DUPLICATE_IDENTIFIERS = 2 }; 57 | 58 | private: 59 | MessageParser(const MessageParser &) = delete; 60 | MessageParser(MessageParser &&) = delete; 61 | MessageParser &operator=(const MessageParser &) = delete; 62 | MessageParser &operator=(MessageParser &&) = delete; 63 | 64 | public: 65 | MessageParser() = default; 66 | 67 | /** 68 | * This method tries to parse the given message specification. 69 | * 70 | * @param input Message specification. 71 | * @return Pair: List of cluon::MetaMessages describing the specified messages and error code: 72 | * NO_MESSAGEPARSER_ERROR: The given specification could be parsed successfully (list moght be non-empty). 73 | * SYNTAX_ERROR: The given specification could not be parsed successfully (list is empty). 74 | * DUPLICATE_IDENTIFIERS: The given specification contains ambiguous names or identifiers (list is empty). 75 | */ 76 | std::pair, MessageParserErrorCodes> parse(const std::string &input); 77 | }; 78 | } // namespace cluon 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | # BasedOnStyle: LLVM 3 | AccessModifierOffset: -1 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: true 12 | AllowShortCaseLabelsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: Inline 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: false 21 | BinPackParameters: false 22 | BraceWrapping: 23 | AfterClass: true 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | BeforeCatch: true 32 | BeforeElse: true 33 | IndentBraces: false 34 | BreakBeforeBinaryOperators: All 35 | BreakBeforeBraces: Attach 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializersBeforeComma: true 38 | BreakAfterJavaFieldAnnotations: false 39 | BreakStringLiterals: true 40 | ColumnLimit: 160 41 | CommentPragmas: '^ IWYU pragma:' 42 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 43 | ConstructorInitializerIndentWidth: 4 44 | ContinuationIndentWidth: 4 45 | Cpp11BracedListStyle: true 46 | DerivePointerAlignment: false 47 | DisableFormat: false 48 | ExperimentalAutoDetectBinPacking: false 49 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 50 | IncludeCategories: 51 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 52 | Priority: 2 53 | - Regex: '^(<|"(gtest|isl|json)/)' 54 | Priority: 3 55 | - Regex: '.*' 56 | Priority: 1 57 | IncludeIsMainRegex: '$' 58 | IndentCaseLabels: true 59 | IndentWidth: 4 60 | IndentWrappedFunctionNames: false 61 | JavaScriptQuotes: Leave 62 | JavaScriptWrapImports: true 63 | KeepEmptyLinesAtTheStartOfBlocks: true 64 | MacroBlockBegin: '' 65 | MacroBlockEnd: '' 66 | MaxEmptyLinesToKeep: 1 67 | NamespaceIndentation: None 68 | ObjCBlockIndentWidth: 2 69 | ObjCSpaceAfterProperty: false 70 | ObjCSpaceBeforeProtocolList: true 71 | PenaltyBreakBeforeFirstCallParameter: 19 72 | PenaltyBreakComment: 300 73 | PenaltyBreakFirstLessLess: 120 74 | PenaltyBreakString: 1000 75 | PenaltyExcessCharacter: 1000000 76 | PenaltyReturnTypeOnItsOwnLine: 60 77 | PointerAlignment: Right 78 | ReflowComments: true 79 | SortIncludes: true 80 | SpaceAfterCStyleCast: false 81 | SpaceAfterTemplateKeyword: true 82 | SpaceBeforeAssignmentOperators: true 83 | SpaceBeforeParens: ControlStatements 84 | SpaceInEmptyParentheses: false 85 | SpacesBeforeTrailingComments: 1 86 | SpacesInAngles: false 87 | SpacesInContainerLiterals: true 88 | SpacesInCStyleCastParentheses: false 89 | SpacesInParentheses: false 90 | SpacesInSquareBrackets: false 91 | Standard: Cpp11 92 | TabWidth: 4 93 | UseTab: Never 94 | 95 | -------------------------------------------------------------------------------- /libcluon/include/cluon/UDPSender.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_UDPSENDER_HPP 10 | #define CLUON_UDPSENDER_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | // clang-format off 15 | #ifdef WIN32 16 | #include // for WSAStartUp 17 | #include // for SOCKET 18 | #else 19 | #include 20 | #endif 21 | // clang-format on 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace cluon { 29 | /** 30 | To send data using a UDP socket, simply include the header 31 | `#include `. 32 | 33 | Next, create an instance of class `cluon::UDPSender` as follows: 34 | `cluon::UDPSender sender("127.0.0.1", 1234);`. The first parameter is of type 35 | `std::string` expecting a numerical IPv4 address and the second parameter 36 | specifies the UDP port to which the data shall be sent to. 37 | 38 | To finally send data, simply call the method `send` supplying the data to be 39 | sent: `sender.send(std::move("Hello World!")`. Please note that the data is 40 | supplied using the _move_-semantics. The method `send` returns a 41 | `std::pair` where the first element returns the size of the 42 | successfully sent bytes and the second element contains the error code in case 43 | the transmission of the data failed. 44 | 45 | \code{.cpp} 46 | cluon::UDPSender sender("127.0.0.1", 1234); 47 | 48 | std::pair retVal = sender.send(std::move("Hello World!")); 49 | 50 | std::cout << "Send " << retVal.first << " bytes, error code = " << retVal.second << std::endl; 51 | \endcode 52 | 53 | A complete example is available 54 | [here](https://github.com/chrberger/libcluon/blob/master/libcluon/examples/cluon-UDPSender.cpp). 55 | */ 56 | class LIBCLUON_API UDPSender { 57 | private: 58 | UDPSender(const UDPSender &) = delete; 59 | UDPSender(UDPSender &&) = delete; 60 | UDPSender &operator=(const UDPSender &) = delete; 61 | UDPSender &operator=(UDPSender &&) = delete; 62 | 63 | public: 64 | /** 65 | * Constructor. 66 | * 67 | * @param sendToAddress Numerical IPv4 address to send a UDP packet to. 68 | * @param sendToPort Port to send a UDP packet to. 69 | */ 70 | UDPSender(const std::string &sendToAddress, uint16_t sendToPort) noexcept; 71 | ~UDPSender() noexcept; 72 | 73 | /** 74 | * Send a given string. 75 | * 76 | * @param data Data to send. 77 | * @return Pair: Number of bytes sent and errno. 78 | */ 79 | std::pair send(std::string &&data) const noexcept; 80 | 81 | public: 82 | /** 83 | * @return Port that this UDP sender will use for sending or 0 if no information available. 84 | */ 85 | uint16_t getSendFromPort() const noexcept; 86 | 87 | private: 88 | mutable std::mutex m_socketMutex{}; 89 | int32_t m_socket{-1}; 90 | uint16_t m_portToSentFrom{0}; 91 | struct sockaddr_in m_sendToAddress {}; 92 | }; 93 | } // namespace cluon 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /libcluon/src/MetaMessage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/MetaMessage.hpp" 10 | 11 | namespace cluon { 12 | 13 | MetaMessage::MetaField::MetaFieldDataTypes MetaMessage::MetaField::fieldDataType() const noexcept { 14 | return m_fieldDataType; 15 | } 16 | 17 | MetaMessage::MetaField &MetaMessage::MetaField::fieldDataType(const MetaMessage::MetaField::MetaFieldDataTypes &v) noexcept { 18 | m_fieldDataType = v; 19 | return *this; 20 | } 21 | 22 | std::string MetaMessage::MetaField::fieldDataTypeName() const noexcept { 23 | return m_fieldDataTypeName; 24 | } 25 | 26 | MetaMessage::MetaField &MetaMessage::MetaField::fieldDataTypeName(const std::string &v) noexcept { 27 | m_fieldDataTypeName = v; 28 | return *this; 29 | } 30 | 31 | std::string MetaMessage::MetaField::fieldName() const noexcept { 32 | return m_fieldName; 33 | } 34 | 35 | MetaMessage::MetaField &MetaMessage::MetaField::fieldName(const std::string &v) noexcept { 36 | m_fieldName = v; 37 | return *this; 38 | } 39 | 40 | uint32_t MetaMessage::MetaField::fieldIdentifier() const noexcept { 41 | return m_fieldIdentifier; 42 | } 43 | 44 | MetaMessage::MetaField &MetaMessage::MetaField::fieldIdentifier(uint32_t v) noexcept { 45 | m_fieldIdentifier = v; 46 | return *this; 47 | } 48 | 49 | std::string MetaMessage::MetaField::defaultInitializationValue() const noexcept { 50 | return m_defaultInitializationValue; 51 | } 52 | 53 | MetaMessage::MetaField &MetaMessage::MetaField::defaultInitializationValue(const std::string &v) noexcept { 54 | m_defaultInitializationValue = v; 55 | return *this; 56 | } 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | 60 | MetaMessage::MetaMessage() noexcept {} 61 | 62 | std::string MetaMessage::packageName() const noexcept { 63 | return m_packageName; 64 | } 65 | 66 | std::string MetaMessage::messageName() const noexcept { 67 | return m_messageName; 68 | } 69 | 70 | MetaMessage &MetaMessage::packageName(const std::string &v) noexcept { 71 | m_packageName = v; 72 | return *this; 73 | } 74 | 75 | MetaMessage &MetaMessage::messageName(const std::string &v) noexcept { 76 | m_messageName = v; 77 | return *this; 78 | } 79 | 80 | int32_t MetaMessage::messageIdentifier() const noexcept { 81 | return m_messageIdentifier; 82 | } 83 | 84 | MetaMessage &MetaMessage::messageIdentifier(int32_t v) noexcept { 85 | m_messageIdentifier = v; 86 | return *this; 87 | } 88 | 89 | MetaMessage &MetaMessage::add(MetaMessage::MetaField &&mf) noexcept { 90 | m_listOfMetaFields.emplace_back(std::move(mf)); 91 | return *this; 92 | } 93 | 94 | const std::vector &MetaMessage::listOfMetaFields() const noexcept { 95 | return m_listOfMetaFields; 96 | } 97 | 98 | void MetaMessage::accept(const std::function &visit) { 99 | visit(*this); 100 | } 101 | } // namespace cluon 102 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-LCMtoJSON.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_LCMTOJSON_HPP 10 | #define CLUON_LCMTOJSON_HPP 11 | 12 | #include "cluon/UDPReceiver.hpp" 13 | #include "cluon/LCMToGenericMessage.hpp" 14 | #include "cluon/ToJSONVisitor.hpp" 15 | 16 | #include "argh/argh.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | inline int32_t cluon_LCMtoJSON(int32_t argc, char **argv) { 28 | int32_t retVal{1}; 29 | const std::string PROGRAM{argv[0]}; // NOLINT 30 | argh::parser commandline{argc, argv}; 31 | std::string LCM{commandline.pos_args().back()}; 32 | if (std::string::npos != LCM.find(PROGRAM)) { 33 | std::cerr << PROGRAM 34 | << " dumps LCM payload received from an LCM session to stdout in JSON format using an optionally supplied ODVD message specification file." << std::endl; 35 | std::cerr << "Usage: " << PROGRAM << " [--odvd=] a.b.c.d:port" << std::endl; 36 | std::cerr << "Examples: " << PROGRAM << " 239.255.76.67:7667" << std::endl; 37 | std::cerr << " " << PROGRAM << " --odvd=MyMessages.odvd 239.255.76.67:7667" << std::endl; 38 | } else { 39 | std::string tmp{LCM}; 40 | std::replace(tmp.begin(), tmp.end(), ':', ' '); 41 | std::istringstream sstr{tmp}; 42 | std::vector tokens{std::istream_iterator(sstr), std::istream_iterator()}; 43 | 44 | const std::string ADDRESS{tokens.at(0)}; 45 | const uint16_t PORT{static_cast(std::stoi(tokens.at(1)))}; 46 | 47 | std::string odvdFile; 48 | commandline({"--odvd"}) >> odvdFile; 49 | 50 | cluon::LCMToGenericMessage lcm2GM; 51 | if (!odvdFile.empty()) { 52 | std::fstream fin{odvdFile, std::ios::in}; 53 | if (fin.good()) { 54 | const std::string s{static_cast(std::stringstream() << fin.rdbuf()).str()}; // NOLINT 55 | std::clog << "Parsed " << lcm2GM.setMessageSpecification(s) << " message(s)." << std::endl; 56 | } 57 | } 58 | 59 | cluon::UDPReceiver receiver( 60 | ADDRESS, PORT, 61 | [&l2GM = lcm2GM](std::string && data, std::string &&, std::chrono::system_clock::time_point &&) noexcept { 62 | cluon::GenericMessage gm = l2GM.getGenericMessage(data); 63 | cluon::ToJSONVisitor j; 64 | gm.accept(j); 65 | std::cout << j.json() << std::endl; 66 | std::cout.flush(); 67 | }); 68 | 69 | if (receiver.isRunning()) { 70 | using namespace std::literals::chrono_literals; // NOLINT 71 | while (receiver.isRunning()) { 72 | std::this_thread::sleep_for(1s); 73 | } 74 | retVal = 0; 75 | } 76 | } 77 | return retVal; 78 | } 79 | 80 | #endif 81 | 82 | -------------------------------------------------------------------------------- /libcluon/thirdparty/cluon/stringtoolbox.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018-2020 Christian Berger 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef STRINGTOOLBOX_HPP 26 | #define STRINGTOOLBOX_HPP 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | namespace stringtoolbox { 33 | 34 | /** 35 | * @return std::string without trailing whitespace characters. 36 | */ 37 | inline std::string &rtrim(std::string &str) noexcept { 38 | str.erase(str.find_last_not_of(" \t") + 1); 39 | return str; 40 | } 41 | 42 | /** 43 | * @return std::tring without leading whitespace characters. 44 | */ 45 | inline std::string <rim(std::string &str) noexcept { 46 | str.erase(0, str.find_first_not_of(" \t")); 47 | return str; 48 | } 49 | 50 | /** 51 | * @return std:string without leading and trailing whitespace characters. 52 | */ 53 | inline std::string &trim(std::string &str) noexcept { 54 | return ltrim(rtrim(str)); 55 | } 56 | 57 | /** 58 | * @return std:string where all occurrences of characters FROM are replaced with TO. 59 | */ 60 | inline std::string replaceAll(const std::string &str, 61 | const char &FROM, 62 | const char &TO) noexcept { 63 | std::string retVal{str}; 64 | std::replace(retVal.begin(), retVal.end(), FROM, TO); 65 | return retVal; 66 | } 67 | 68 | /** 69 | * @return std::vector where the given string is split along delimiter. 70 | */ 71 | inline std::vector split(const std::string &str, 72 | const char &delimiter) noexcept { 73 | std::vector retVal{}; 74 | std::string::size_type prev{0}; 75 | for (std::string::size_type i{str.find_first_of(delimiter, prev)}; 76 | i != std::string::npos; 77 | prev = i + 1, i = str.find_first_of(delimiter, prev)) { 78 | if (i != prev) { 79 | retVal.emplace_back(str.substr(prev, i - prev)); 80 | } 81 | else { 82 | retVal.emplace_back(""); 83 | } 84 | } 85 | if (prev < str.size()) { 86 | retVal.emplace_back(str.substr(prev, str.size() - prev)); 87 | } 88 | else if (prev > 0) { 89 | retVal.emplace_back(""); 90 | } 91 | return retVal; 92 | } 93 | 94 | } // namespace stringtoolbox 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at libcluon@christianberger.net. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | -------------------------------------------------------------------------------- /libcluon/include/cluon/PortableEndian.hpp: -------------------------------------------------------------------------------- 1 | // "License": Public Domain 2 | // I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. 3 | // In case there are jurisdictions that don't support putting things in the public domain you can also consider it to 4 | // be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it 5 | // an example on how to get the endian conversion functions on different platforms. 6 | 7 | // Updated for FreeBSD 10.1+, DragonFly 4.2+, NetBSD 6.1.5+, fixes for Win32, 8 | // and support for emscripten; Christian Berger. 9 | 10 | #ifndef CLUON_PORTABLEENDIAN_HPP 11 | #define CLUON_PORTABLEENDIAN_HPP 12 | 13 | // clang-format off 14 | #if defined(__linux__) || defined(__CYGWIN__) 15 | #include 16 | #elif defined(__APPLE__) 17 | #include 18 | #define htobe16(x) OSSwapHostToBigInt16(x) 19 | #define htole16(x) OSSwapHostToLittleInt16(x) 20 | #define be16toh(x) OSSwapBigToHostInt16(x) 21 | #define le16toh(x) OSSwapLittleToHostInt16(x) 22 | 23 | #define htobe32(x) OSSwapHostToBigInt32(x) 24 | #define htole32(x) OSSwapHostToLittleInt32(x) 25 | #define be32toh(x) OSSwapBigToHostInt32(x) 26 | #define le32toh(x) OSSwapLittleToHostInt32(x) 27 | 28 | #define htobe64(x) OSSwapHostToBigInt64(x) 29 | #define htole64(x) OSSwapHostToLittleInt64(x) 30 | #define be64toh(x) OSSwapBigToHostInt64(x) 31 | #define le64toh(x) OSSwapLittleToHostInt64(x) 32 | #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) 33 | #include 34 | #elif (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) 35 | #if BYTE_ORDER == LITTLE_ENDIAN 36 | // Add missing definitions for MinGW. 37 | #ifndef htonll 38 | #define htonll(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32))) 39 | #endif 40 | #ifndef ntohll 41 | #define ntohll(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32))) 42 | #endif 43 | 44 | #define htobe16(x) htons(x) 45 | #define htole16(x) (x) 46 | #define be16toh(x) ntohs(x) 47 | #define le16toh(x) (x) 48 | 49 | #define htobe32(x) htonl(x) 50 | #define htole32(x) (x) 51 | #define be32toh(x) ntohl(x) 52 | #define le32toh(x) (x) 53 | 54 | #define htobe64(x) htonll(x) 55 | #define htole64(x) (x) 56 | #define be64toh(x) ntohll(x) 57 | #define le64toh(x) (x) 58 | #elif BYTE_ORDER == BIG_ENDIAN 59 | /* that would be xbox 360 */ 60 | #define htobe16(x) (x) 61 | #define htole16(x) __builtin_bswap16(x) 62 | #define be16toh(x) (x) 63 | #define le16toh(x) __builtin_bswap16(x) 64 | 65 | #define htobe32(x) (x) 66 | #define htole32(x) __builtin_bswap32(x) 67 | #define be32toh(x) (x) 68 | #define le32toh(x) __builtin_bswap32(x) 69 | 70 | #define htobe64(x) (x) 71 | #define htole64(x) __builtin_bswap64(x) 72 | #define be64toh(x) (x) 73 | #define le64toh(x) __builtin_bswap64(x) 74 | #else 75 | #error byte order not supported 76 | #endif 77 | #else 78 | #ifdef __EMSCRIPTEN__ 79 | #include 80 | #else 81 | #warning platform not supported 82 | #endif 83 | #endif 84 | // clang-format on 85 | #endif 86 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestUDPSender.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/UDPSender.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // Defining a test fixture to be reused among the test cases. 18 | class TestFixture_UDPSender { 19 | public: 20 | TestFixture_UDPSender() 21 | : m_us("127.0.0.1", 5678) {} 22 | 23 | protected: 24 | cluon::UDPSender m_us; 25 | }; 26 | 27 | TEST_CASE_METHOD(TestFixture_UDPSender, "Creating UDPSender.") {} 28 | 29 | TEST_CASE_METHOD(TestFixture_UDPSender, "Send test data.") { 30 | std::string TEST_DATA{"Hello World"}; 31 | const auto TEST_DATA_SIZE = TEST_DATA.size(); 32 | auto retVal2 = m_us.send(std::move(TEST_DATA)); 33 | REQUIRE(TEST_DATA_SIZE == static_cast(retVal2.first)); 34 | REQUIRE(0 == retVal2.second); 35 | } 36 | 37 | TEST_CASE_METHOD(TestFixture_UDPSender, "Send empty data.") { 38 | std::string TEST_DATA; 39 | auto retVal2 = m_us.send(std::move(TEST_DATA)); 40 | REQUIRE(0 == retVal2.first); 41 | REQUIRE(0 == retVal2.second); 42 | } 43 | 44 | TEST_CASE("Trying to send data with faulty sender.") { 45 | cluon::UDPSender us3{"127.0.0.256", 5677}; 46 | std::string TEST_DATA{"Hello World"}; 47 | auto retVal3 = us3.send(std::move(TEST_DATA)); 48 | REQUIRE(-1 == retVal3.first); 49 | #ifdef WIN32 50 | constexpr int32_t EXPECTED_VALUE = 9; 51 | #else 52 | constexpr int32_t EXPECTED_VALUE = EBADF; 53 | #endif 54 | REQUIRE(EXPECTED_VALUE == retVal3.second); 55 | } 56 | 57 | TEST_CASE_METHOD(TestFixture_UDPSender, "Trying to send too big data.") { 58 | std::string TEST_DATA(0xFFFF - 1, 'A'); 59 | auto retVal4 = m_us.send(std::move(TEST_DATA)); 60 | REQUIRE(-1 == retVal4.first); 61 | REQUIRE(E2BIG == retVal4.second); 62 | } 63 | 64 | TEST_CASE("Trying to send data with empty sendToAddress.") { 65 | cluon::UDPSender us5{"", 1}; 66 | std::string TEST_DATA{"Hello World"}; 67 | auto retVal5 = us5.send(std::move(TEST_DATA)); 68 | REQUIRE(-1 == retVal5.first); 69 | #ifdef WIN32 70 | constexpr int32_t EXPECTED_VALUE = 9; 71 | #else 72 | constexpr int32_t EXPECTED_VALUE = EBADF; 73 | #endif 74 | REQUIRE(EXPECTED_VALUE == retVal5.second); 75 | } 76 | 77 | TEST_CASE("Trying to send data with empty sendToPort.") { 78 | cluon::UDPSender us6{"127.0.0.1", 0}; 79 | std::string TEST_DATA{"Hello World"}; 80 | auto retVal6 = us6.send(std::move(TEST_DATA)); 81 | REQUIRE(-1 == retVal6.first); 82 | #ifdef WIN32 83 | constexpr int32_t EXPECTED_VALUE = 9; 84 | #else 85 | constexpr int32_t EXPECTED_VALUE = EBADF; 86 | #endif 87 | REQUIRE(EXPECTED_VALUE == retVal6.second); 88 | } 89 | 90 | TEST_CASE("Trying to send data with incomplete sendToAddress.") { 91 | cluon::UDPSender us6{"localhos", 1}; 92 | std::string TEST_DATA{"Hello World"}; 93 | auto retVal6 = us6.send(std::move(TEST_DATA)); 94 | REQUIRE(-1 == retVal6.first); 95 | #ifdef WIN32 96 | constexpr int32_t EXPECTED_VALUE = 9; 97 | #else 98 | constexpr int32_t EXPECTED_VALUE = EBADF; 99 | #endif 100 | REQUIRE(EXPECTED_VALUE == retVal6.second); 101 | } 102 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ToMsgPackVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TOMSGPACKVISITOR_HPP 10 | #define CLUON_TOMSGPACKVISITOR_HPP 11 | 12 | #include "cluon/MsgPackConstants.hpp" 13 | #include "cluon/cluon.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace cluon { 20 | /** 21 | This class encodes a given message in MsgPack format. 22 | */ 23 | class LIBCLUON_API ToMsgPackVisitor { 24 | private: 25 | ToMsgPackVisitor(const ToMsgPackVisitor &) = delete; 26 | ToMsgPackVisitor(ToMsgPackVisitor &&) = delete; 27 | ToMsgPackVisitor &operator=(const ToMsgPackVisitor &) = delete; 28 | ToMsgPackVisitor &operator=(ToMsgPackVisitor &&) = delete; 29 | 30 | public: 31 | ToMsgPackVisitor() = default; 32 | ~ToMsgPackVisitor() = default; 33 | 34 | /** 35 | * @return Encoded data in MsgPack format. 36 | */ 37 | std::string encodedData() const noexcept; 38 | 39 | public: 40 | // The following methods are provided to allow an instance of this class to 41 | // be used as visitor for an instance with the method signature void accept(T&); 42 | 43 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 44 | void postVisit() noexcept; 45 | 46 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 47 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 48 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 49 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 50 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 51 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 52 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 53 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 54 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 55 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 56 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 57 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 58 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 59 | 60 | template 61 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 62 | (void)id; 63 | (void)typeName; 64 | 65 | encode(m_buffer, name); 66 | { 67 | cluon::ToMsgPackVisitor nestedMsgPackEncoder; 68 | value.accept(nestedMsgPackEncoder); 69 | const std::string tmp{nestedMsgPackEncoder.encodedData()}; 70 | const uint32_t LENGTH{static_cast(tmp.size())}; 71 | m_buffer.write(tmp.c_str(), static_cast(LENGTH)); 72 | } 73 | m_numberOfFields++; 74 | } 75 | 76 | private: 77 | void encode(std::ostream &o, const std::string &s); 78 | void encodeUint(std::ostream &o, uint64_t v); 79 | void encodeInt(std::ostream &o, int64_t v); 80 | 81 | private: 82 | uint32_t m_numberOfFields{0}; 83 | std::stringstream m_buffer{""}; 84 | }; 85 | } // namespace cluon 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /libcluon/include/cluon/FromLCMVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_FROMLCMVISITOR_HPP 10 | #define CLUON_FROMLCMVISITOR_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace cluon { 21 | /** 22 | This class decodes a given message from LCM format. 23 | */ 24 | class LIBCLUON_API FromLCMVisitor { 25 | private: 26 | FromLCMVisitor(std::stringstream &in) noexcept; 27 | FromLCMVisitor(const FromLCMVisitor &) = delete; 28 | FromLCMVisitor(FromLCMVisitor &&) = delete; 29 | FromLCMVisitor &operator=(const FromLCMVisitor &) = delete; 30 | FromLCMVisitor &operator=(FromLCMVisitor &&) = delete; 31 | 32 | public: 33 | FromLCMVisitor() noexcept; 34 | ~FromLCMVisitor() = default; 35 | 36 | public: 37 | /** 38 | * This method decodes a given istream into LCM. 39 | * 40 | * @param in istream to decode. 41 | */ 42 | void decodeFrom(std::istream &in) noexcept; 43 | 44 | public: 45 | // The following methods are provided to allow an instance of this class to 46 | // be used as visitor for an instance with the method signature void accept(T&); 47 | 48 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 49 | void postVisit() noexcept; 50 | 51 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 52 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 53 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 54 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 55 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 56 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 57 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 58 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 59 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 60 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 61 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 62 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 63 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 64 | 65 | template 66 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 67 | (void)id; 68 | (void)typeName; 69 | // No hash for the type but for name and dimension. 70 | calculateHash(name); 71 | calculateHash(0); 72 | 73 | cluon::FromLCMVisitor nestedLCMDecoder(m_buffer); 74 | value.accept(nestedLCMDecoder); 75 | 76 | m_hashes.push_back(nestedLCMDecoder.hash()); 77 | } 78 | 79 | private: 80 | int64_t hash() const noexcept; 81 | void calculateHash(char c) noexcept; 82 | void calculateHash(const std::string &s) noexcept; 83 | 84 | private: 85 | int64_t m_calculatedHash{0x12345678}; 86 | int64_t m_expectedHash{0}; 87 | std::stringstream m_internalBuffer{""}; 88 | std::stringstream &m_buffer; 89 | std::vector m_hashes{}; 90 | }; 91 | } // namespace cluon 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /libcluon/tools/cluon-msc.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_MSC_HPP 10 | #define CLUON_MSC_HPP 11 | 12 | #include "cluon/MessageParser.hpp" 13 | #include "cluon/MetaMessage.hpp" 14 | #include "cluon/MetaMessageToCPPTransformator.hpp" 15 | #include "cluon/MetaMessageToProtoTransformator.hpp" 16 | 17 | #include "argh/argh.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | inline int32_t cluon_msc(int32_t argc, char **argv) { 25 | const std::string PROGRAM{argv[0]}; // NOLINT 26 | argh::parser commandline(argc, argv); 27 | 28 | std::string inputFilename = commandline.pos_args().back(); 29 | if (std::string::npos != inputFilename.find(PROGRAM)) { 30 | std::cerr << PROGRAM 31 | << " transforms a given message specification file in .odvd format into C++." << std::endl; 32 | std::cerr << "Usage: " << PROGRAM << " [--cpp] [--proto] [--out=] " << std::endl; 33 | std::cerr << " " << PROGRAM << " --cpp: Generate C++14-compliant, self-contained header file." << std::endl; 34 | std::cerr << " " << PROGRAM << " --proto: Generate Proto version2-compliant file." << std::endl; 35 | std::cerr << std::endl; 36 | std::cerr << "Example: " << PROGRAM << " --cpp --out=/tmp/myOutput.hpp myFile.odvd" << std::endl; 37 | return 1; 38 | } 39 | 40 | std::string outputFilename = commandline("--out").str(); 41 | 42 | const bool generateCPP = commandline[{"--cpp"}]; 43 | const bool generateProto = commandline[{"--proto"}]; 44 | 45 | int retVal = 1; 46 | std::ifstream inputFile(inputFilename, std::ios::in); 47 | if (inputFile.good()) { 48 | bool addHeaderForFirstProtoFile = true; 49 | std::string input(static_cast(std::stringstream() << inputFile.rdbuf()).str()); // NOLINT 50 | 51 | cluon::MessageParser mp; 52 | auto result = mp.parse(input); 53 | retVal = result.second; 54 | 55 | // Delete the content of a potentially existing file. 56 | if (!outputFilename.empty()) { 57 | std::ofstream outputFile(outputFilename, std::ios::out | std::ios::trunc); 58 | outputFile.close(); 59 | } 60 | for (auto e : result.first) { 61 | std::string content; 62 | if (generateCPP) { 63 | cluon::MetaMessageToCPPTransformator transformation; 64 | e.accept([&trans = transformation](const cluon::MetaMessage &_mm){ trans.visit(_mm); }); 65 | content = transformation.content(); 66 | } 67 | if (generateProto) { 68 | cluon::MetaMessageToProtoTransformator transformation; 69 | e.accept([&trans = transformation](const cluon::MetaMessage &_mm){ trans.visit(_mm); }); 70 | content = transformation.content(addHeaderForFirstProtoFile); 71 | addHeaderForFirstProtoFile = false; 72 | } 73 | 74 | if (!outputFilename.empty()) { 75 | std::ofstream outputFile(outputFilename, std::ios::out | std::ios::app); 76 | outputFile << content << std::endl; 77 | outputFile.close(); 78 | } 79 | else { // LCOV_EXCL_LINE 80 | std::cout << content << std::endl; // LCOV_EXCL_LINE 81 | } 82 | } 83 | } 84 | else { 85 | std::cerr << "[" << PROGRAM << "] Could not find '" << inputFilename << "'." << std::endl; 86 | } 87 | 88 | return retVal; 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ToLCMVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TOLCMVISITOR_HPP 10 | #define CLUON_TOLCMVISITOR_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace cluon { 20 | /** 21 | This class encodes a given message in LCM format. 22 | */ 23 | class LIBCLUON_API ToLCMVisitor { 24 | private: 25 | ToLCMVisitor(const ToLCMVisitor &) = delete; 26 | ToLCMVisitor(ToLCMVisitor &&) = delete; 27 | ToLCMVisitor &operator=(const ToLCMVisitor &) = delete; 28 | ToLCMVisitor &operator=(ToLCMVisitor &&) = delete; 29 | 30 | public: 31 | ToLCMVisitor() = default; 32 | ~ToLCMVisitor() = default; 33 | 34 | /** 35 | * @param withHash True if the hash value from the fields shall be included. 36 | * @return Encoded data in LCM format. 37 | */ 38 | std::string encodedData(bool withHash = true) const noexcept; 39 | 40 | public: 41 | // The following methods are provided to allow an instance of this class to 42 | // be used as visitor for an instance with the method signature void accept(T&); 43 | 44 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 45 | void postVisit() noexcept; 46 | 47 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 48 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 49 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 50 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 51 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 52 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 53 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 54 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 55 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 56 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 57 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 58 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 59 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 60 | 61 | template 62 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 63 | (void)id; 64 | (void)typeName; 65 | calculateHash(name); 66 | calculateHash(0); 67 | 68 | // No hash for the type but for name and dimension. 69 | cluon::ToLCMVisitor nestedLCMEncoder; 70 | value.accept(nestedLCMEncoder); 71 | 72 | constexpr bool WITH_HASH{false}; 73 | const std::string s = nestedLCMEncoder.encodedData(WITH_HASH); 74 | m_buffer.write(s.c_str(), static_cast(s.size())); 75 | 76 | // Save this complex field's hash for later to compute final hash. 77 | m_hashes.push_back(nestedLCMEncoder.hash()); 78 | } 79 | 80 | private: 81 | int64_t hash() const noexcept; 82 | void calculateHash(char c) noexcept; 83 | void calculateHash(const std::string &s) noexcept; 84 | 85 | private: 86 | int64_t m_hash{0x12345678}; 87 | std::stringstream m_buffer{""}; 88 | std::vector m_hashes{}; 89 | }; 90 | } // namespace cluon 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /libcluon/resources/cluonTestDataStructures.odvd: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017-2018 Christian Berger 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | message testdata.MyTestMessage0 [id = 30000] { 8 | bool attribute1 [default = true, id = 1]; 9 | char attribute2 [default = 'c', id = 2]; 10 | } 11 | 12 | message testdata.MyTestMessage1 [id = 30001] { 13 | bool attribute1 [default = true, id = 1]; 14 | char attribute2 [default = 'c', id = 2]; 15 | int8 attribute3 [default = -1, id = 3]; 16 | uint8 attribute4 [default = 2, id = 4]; 17 | int16 attribute5 [default = -3, id = 5]; 18 | uint16 attribute6 [default = 4, id = 6]; 19 | int32 attribute7 [default = -5, id = 7]; 20 | uint32 attribute8 [default = 6, id = 8]; 21 | int64 attribute9 [default = -7, id = 9]; 22 | uint64 attribute10 [default = 8, id = 10]; 23 | float attribute11 [default = -9.5, id = 11]; 24 | double attribute12 [default = 10.6, id = 12]; 25 | string attribute13 [default = "Hello World", id = 13]; 26 | bytes attribute14 [default = "Hello Galaxy", id = 14]; 27 | } 28 | 29 | message testdata.MyTestMessage2 [id = 30002] { 30 | uint8 attribute1 [ default = 123, id = 1 ]; 31 | } 32 | 33 | message testdata.MyTestMessage3 [id = 30003] { 34 | uint8 attribute1 [ default = 124, id = 1 ]; 35 | int8 attribute2 [ default = -124, id = 2 ]; 36 | } 37 | 38 | message testdata.MyTestMessage4 [id = 30004] { 39 | string attribute1 [ id = 2 ]; 40 | } 41 | 42 | message testdata.MyTestMessage5 [id = 30005] { 43 | uint8 attribute1 [ default = 1, id = 1 ]; 44 | int8 attribute2 [ default = -1, id = 2 ]; 45 | uint16 attribute3 [ default = 100, id = 3 ]; 46 | int16 attribute4 [ default = -100, id = 4 ]; 47 | uint32 attribute5 [ default = 10000, id = 5 ]; 48 | int32 attribute6 [ default = -10000, id = 6 ]; 49 | uint64 attribute7 [ default = 12345, id = 7 ]; 50 | int64 attribute8 [ default = -12345, id = 8 ]; 51 | float attribute9 [ default = -1.2345, id = 9 ]; 52 | double attribute10 [ default = -10.2345, id = 10 ]; 53 | string attribute11 [ default = "Hello World!", id = 11 ]; 54 | } 55 | 56 | message testdata.MyTestMessage6 [id = 30006] { 57 | testdata.MyTestMessage2 attribute1 [ id = 3 ]; 58 | } 59 | 60 | message testdata.MyTestMessage7 [id = 30007] { 61 | testdata.MyTestMessage2 attribute1 [ id = 1 ]; 62 | uint32 attribute2 [ default = 12345, id = 2 ]; 63 | testdata.MyTestMessage2 attribute3 [ id = 3 ]; 64 | } 65 | 66 | message testdata.MyTestMessage8 [id = 30008] { 67 | uint64 attribute1 [ default = 123, id = 1 ]; 68 | } 69 | 70 | message testdata.MyTestMessage9 [id = 30009] { 71 | float attribute1 [ default = -1.2345, id = 1 ]; 72 | double attribute2 [ default = -10.2345, id = 2 ]; 73 | } 74 | 75 | message testdata.MyTestMessage10 [id = 30010] { 76 | int64 attribute1 [ default = 123, id = 1 ]; 77 | } 78 | 79 | message testdata.MyTestMessage11 [id = 30011] {} 80 | 81 | // Message with non-consecutive IDs 82 | message testdata.MyTestMessage12 [id = 30012] { 83 | bool attribute1 [default = true, id = 1]; 84 | char attribute2 [default = 'c', id = 2]; 85 | int8 attribute3 [default = -1, id = 3]; 86 | uint8 attribute4 [default = 2, id = 4]; 87 | int16 attribute5 [default = -3, id = 5]; 88 | uint16 attribute6 [default = 4, id = 6]; 89 | int32 attribute9 [default = -5, id = 9]; 90 | uint32 attribute8 [default = 6, id = 8]; 91 | int64 attribute10 [default = -7, id = 10]; 92 | uint64 attribute11 [default = 8, id = 11]; 93 | float attribute12 [default = -9.5, id = 12]; 94 | double attribute13 [default = 10.6, id = 13]; 95 | string attribute14 [default = "Hello World", id = 14]; 96 | bytes attribute15 [default = "Hello Galaxy", id = 15]; 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ToODVDVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TOODVDVISITOR_HPP 10 | #define CLUON_TOODVDVISITOR_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace cluon { 21 | /** 22 | This class provides a visitor to transform a message into its corresponding 23 | message specification in ODVD format: 24 | 25 | \code{.cpp} 26 | MyMessage msg; 27 | // Set some values in msg. 28 | 29 | cluon::ToODVDVisitor odvd; 30 | msg.accept(odvd); 31 | 32 | const std::string generatedMessageSpecification{odvd.messageSpecification()}; 33 | std::cout << generatedMessageSpecification << std::endl; 34 | 35 | cluon::MessageParser mp; 36 | auto retVal = mp.parse(generatedMessageSpecification); 37 | std::cout << (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == retVal.second); 38 | \endcode 39 | */ 40 | class LIBCLUON_API ToODVDVisitor { 41 | private: 42 | ToODVDVisitor(const ToODVDVisitor &) = delete; 43 | ToODVDVisitor(ToODVDVisitor &&) = delete; 44 | ToODVDVisitor &operator=(const ToODVDVisitor &) = delete; 45 | ToODVDVisitor &operator=(ToODVDVisitor &&) = delete; 46 | 47 | public: 48 | ToODVDVisitor() = default; 49 | 50 | /** 51 | * @return Message specification data. 52 | */ 53 | std::string messageSpecification() const noexcept; 54 | 55 | public: 56 | // The following methods are provided to allow an instance of this class to 57 | // be used as visitor for an instance with the method signature void accept(T&); 58 | 59 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 60 | void postVisit() noexcept; 61 | 62 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 63 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 64 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 65 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 66 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 67 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 68 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 69 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 70 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 71 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 72 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 73 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 74 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 75 | 76 | template 77 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 78 | try { 79 | std::string tmp{std::regex_replace(typeName, std::regex("::"), ".")}; // NOLINT 80 | 81 | ToODVDVisitor odvdVisitor; 82 | value.accept(odvdVisitor); 83 | m_forwardDeclarations.emplace(m_forwardDeclarations.begin(), odvdVisitor.messageSpecification()); 84 | 85 | m_buffer << " " << tmp << ' ' << name << " [ id = " << id << " ];" << '\n'; 86 | } catch (std::regex_error &) { // LCOV_EXCL_LINE 87 | } 88 | } 89 | 90 | private: 91 | std::vector m_forwardDeclarations{}; 92 | std::stringstream m_buffer{}; 93 | }; 94 | 95 | } // namespace cluon 96 | #endif 97 | -------------------------------------------------------------------------------- /libcluon/include/cluon/NotifyingPipeline.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_NOTIFYINGPIPELINE_HPP 10 | #define CLUON_NOTIFYINGPIPELINE_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cluon { 22 | 23 | template 24 | class LIBCLUON_API NotifyingPipeline { 25 | private: 26 | NotifyingPipeline(const NotifyingPipeline &) = delete; 27 | NotifyingPipeline(NotifyingPipeline &&) = delete; 28 | NotifyingPipeline &operator=(const NotifyingPipeline &) = delete; 29 | NotifyingPipeline &operator=(NotifyingPipeline &&) = delete; 30 | 31 | public: 32 | NotifyingPipeline(std::function delegate) 33 | : m_delegate(delegate) { 34 | m_pipelineThread = std::thread(&NotifyingPipeline::processPipeline, this); 35 | 36 | // Let the operating system spawn the thread. 37 | using namespace std::literals::chrono_literals; // NOLINT 38 | do { std::this_thread::sleep_for(1ms); } while (!m_pipelineThreadRunning.load()); 39 | } 40 | 41 | ~NotifyingPipeline() { 42 | m_pipelineThreadRunning.store(false); 43 | 44 | // Wake any waiting threads. 45 | m_pipelineCondition.notify_all(); 46 | 47 | // Joining the thread could fail. 48 | try { 49 | if (m_pipelineThread.joinable()) { 50 | m_pipelineThread.join(); 51 | } 52 | } catch (...) {} // LCOV_EXCL_LINE 53 | } 54 | 55 | public: 56 | inline void add(T &&entry) noexcept { 57 | std::unique_lock lck(m_pipelineMutex); 58 | m_pipeline.emplace_back(entry); 59 | } 60 | 61 | inline void notifyAll() noexcept { m_pipelineCondition.notify_all(); } 62 | 63 | inline bool isRunning() noexcept { return m_pipelineThreadRunning.load(); } 64 | 65 | private: 66 | inline void processPipeline() noexcept { 67 | // Indicate to caller that we are ready. 68 | m_pipelineThreadRunning.store(true); 69 | 70 | while (m_pipelineThreadRunning.load()) { 71 | std::unique_lock lck(m_pipelineMutex); 72 | // Wait until the thread should stop or data is available. 73 | m_pipelineCondition.wait(lck, [this] { return (!this->m_pipelineThreadRunning.load() || !this->m_pipeline.empty()); }); 74 | 75 | // The condition will automatically lock the mutex after waking up. 76 | // As we are locking per entry, we need to unlock the mutex first. 77 | lck.unlock(); 78 | 79 | uint32_t entries{0}; 80 | { 81 | lck.lock(); 82 | entries = static_cast(m_pipeline.size()); 83 | lck.unlock(); 84 | } 85 | for (uint32_t i{0}; i < entries; i++) { 86 | T entry; 87 | { 88 | lck.lock(); 89 | entry = m_pipeline.front(); 90 | lck.unlock(); 91 | } 92 | 93 | if (nullptr != m_delegate) { 94 | m_delegate(std::move(entry)); 95 | } 96 | 97 | { 98 | lck.lock(); 99 | m_pipeline.pop_front(); 100 | lck.unlock(); 101 | } 102 | } 103 | } 104 | } 105 | 106 | private: 107 | std::function m_delegate; 108 | 109 | std::atomic m_pipelineThreadRunning{false}; 110 | std::thread m_pipelineThread{}; 111 | std::mutex m_pipelineMutex{}; 112 | std::condition_variable m_pipelineCondition{}; 113 | 114 | std::deque m_pipeline{}; 115 | }; 116 | } // namespace cluon 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /libcluon/include/cluon/EnvelopeConverter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_ENVELOPECONVERTER_HPP 10 | #define CLUON_ENVELOPECONVERTER_HPP 11 | 12 | #include "cluon/MetaMessage.hpp" 13 | #include "cluon/cluon.hpp" 14 | #include "cluon/cluonDataStructures.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cluon { 22 | /** 23 | This class provides various conversion functions to and from Envelope data structures. 24 | */ 25 | class LIBCLUON_API EnvelopeConverter { 26 | private: 27 | EnvelopeConverter(const EnvelopeConverter &) = delete; 28 | EnvelopeConverter(EnvelopeConverter &&) = delete; 29 | EnvelopeConverter &operator=(const EnvelopeConverter &) = delete; 30 | EnvelopeConverter &operator=(EnvelopeConverter &&) = delete; 31 | 32 | public: 33 | EnvelopeConverter() = default; 34 | 35 | /** 36 | * This method sets the message specification to be used for 37 | * interpreting a given Proto-encoded Envelope. 38 | * 39 | * @param ms Message specification following the ODVD format. 40 | * @return -1 in case of invalid message specification; otherwise, number 41 | * of successfully parsed messages from given message specification. 42 | */ 43 | int32_t setMessageSpecification(const std::string &ms) noexcept; 44 | 45 | /** 46 | * This method transforms the given Proto-encoded Envelope to JSON. The 47 | * Proto-encoded envelope might be preceded with a 5-bytes OD4-header (optional). 48 | * 49 | * @param protoEncodedEnvelope Proto-encoded Envelope. 50 | * @return JSON representation from given Proto-encoded Envelope using the 51 | * given message specification. 52 | */ 53 | std::string getJSONFromProtoEncodedEnvelope(const std::string &protoEncodedEnvelope) noexcept; 54 | 55 | /** 56 | * This method transforms the given Envelope to JSON. 57 | * 58 | * @param envelope Envelope. 59 | * @return JSON representation from given Envelope using the given message specification. 60 | */ 61 | std::string getJSONFromEnvelope(cluon::data::Envelope &envelope) noexcept; 62 | 63 | /** 64 | * This method transforms a given JSON representation into a Proto-encoded Envelope 65 | * including the prepended OD4-header. 66 | * 67 | * @param json representation according to the given message specification. 68 | * @param messageIdentifier The given JSON representation shall be interpreted 69 | * as the specified message. 70 | * @param senderStamp to be used in the Envelope. 71 | * @return Proto-encoded Envelope including OD4-header or empty string. 72 | */ 73 | std::string getProtoEncodedEnvelopeFromJSONWithoutTimeStamps(const std::string &json, int32_t messageIdentifier, uint32_t senderStamp) noexcept; 74 | 75 | /** 76 | * This method transforms a given JSON representation into a Proto-encoded Envelope 77 | * including the prepended OD4-header and setting cluon::time::now() as sampleTimeStamp. 78 | * 79 | * @param json representation according to the given message specification. 80 | * @param messageIdentifier The given JSON representation shall be interpreted 81 | * as the specified message. 82 | * @param senderStamp to be used in the Envelope. 83 | * @return Proto-encoded Envelope including OD4-header or empty string. 84 | */ 85 | std::string getProtoEncodedEnvelopeFromJSON(const std::string &json, int32_t messageIdentifier, uint32_t senderStamp) noexcept; 86 | 87 | private: 88 | // clang-format off 89 | std::string getProtoEncodedEnvelopeFromJSON(const std::string &json, int32_t messageIdentifier, uint32_t senderStamp, cluon::data::TimeStamp sampleTimeStamp) noexcept; 90 | // clang-format on 91 | 92 | private: 93 | std::vector m_listOfMetaMessages{}; 94 | std::map m_scopeOfMetaMessages{}; 95 | }; 96 | } // namespace cluon 97 | #endif 98 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ToJSONVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TOJSONVISITOR_HPP 10 | #define CLUON_TOJSONVISITOR_HPP 11 | 12 | #include "cluon/any/any.hpp" 13 | #include "cluon/cluon.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace cluon { 21 | /** 22 | This class provides a visitor to transform a message into JSON: 23 | 24 | \code{.cpp} 25 | MyMessage msg; 26 | // Set some values in msg. 27 | 28 | cluon::ToJSONVisitor j; 29 | msg.accept(j); 30 | 31 | std::cout << j.json() << std::endl; 32 | \endcode 33 | */ 34 | class LIBCLUON_API ToJSONVisitor { 35 | private: 36 | ToJSONVisitor(const ToJSONVisitor &) = delete; 37 | ToJSONVisitor(ToJSONVisitor &&) = delete; 38 | ToJSONVisitor &operator=(const ToJSONVisitor &) = delete; 39 | ToJSONVisitor &operator=(ToJSONVisitor &&) = delete; 40 | 41 | public: 42 | /** 43 | * Constructor. 44 | * 45 | * @param withOuterCurlyBraces Include the outer curly braces. 46 | * @param mask Map describing which fields to render. If empty, all 47 | * fields will be emitted; individual field identifiers 48 | * can be masked setting them to false. 49 | */ 50 | ToJSONVisitor(bool withOuterCurlyBraces = true, const std::map &mask = {}) noexcept; 51 | 52 | /** 53 | * @return JSON-encoded data. 54 | */ 55 | std::string json() const noexcept; 56 | 57 | public: 58 | // The following methods are provided to allow an instance of this class to 59 | // be used as visitor for an instance with the method signature void accept(T&); 60 | 61 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 62 | void postVisit() noexcept; 63 | 64 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 65 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 66 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 67 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 68 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 69 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 70 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 71 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 72 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 73 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 74 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 75 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 76 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 77 | 78 | template 79 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 80 | (void)typeName; 81 | if ((0 == m_mask.count(id)) || m_mask[id]) { 82 | try { 83 | ToJSONVisitor jsonVisitor; 84 | value.accept(jsonVisitor); 85 | m_buffer << '\"' << name << '\"' << ':' << jsonVisitor.json() << ',' << '\n'; 86 | } catch (const linb::bad_any_cast &) { // LCOV_EXCL_LINE 87 | } 88 | } 89 | } 90 | 91 | public: 92 | /** 93 | * This method returns the base64-encoded representation for the given input. 94 | * 95 | * @param input to encode as base64 96 | * @return base64 encoded input. 97 | */ 98 | static std::string encodeBase64(const std::string &input) noexcept; 99 | 100 | private: 101 | bool m_withOuterCurlyBraces{true}; 102 | std::map m_mask; 103 | std::stringstream m_buffer{}; 104 | }; 105 | 106 | } // namespace cluon 107 | #endif 108 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Christian Berger 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | ################################################################################ 8 | # What Docker images shall be used? 9 | REPOSITORY=chrberger 10 | ARCH=amd64 11 | IMAGE=ubuntu-$(ARCH) 12 | BASE_IMAGE=$(REPOSITORY)/$(IMAGE) 13 | BASE_IMAGE_VERSION=latest 14 | BASE_IMAGE_DEV_SUFFIX=-dev 15 | BASE_IMAGE_DEV=$(REPOSITORY)/$(IMAGE)$(BASE_IMAGE_DEV_SUFFIX) 16 | BASE_IMAGE_DEV_VERSION=$(BASE_IMAGE_VERSION) 17 | 18 | # Use Alpine to build: 19 | # make ARCH=amd64 IMAGE=alpine-amd64 DOCKERFILE_FOR_BUILDING=Dockerfile.Alpine-3.7 20 | 21 | ################################################################################ 22 | # Product names. 23 | PRODUCT=libcluon-on- 24 | SOURCE_FOLDER=libcluon 25 | 26 | ################################################################################ 27 | # Tools. 28 | DOCKER=$(shell which docker) 29 | CMAKE=$(shell which cmake) 30 | 31 | ################################################################################ 32 | # Directories. 33 | PATH_CCACHE=$(HOME)/.ccache 34 | PATH_SOURCES_FOLDER=$(shell pwd) 35 | PATH_BUILDTOOLS_FOLDER=$(PATH_SOURCES_FOLDER)/buildtools/docker 36 | PATH_BUILDS_FOLDER=$(PATH_SOURCES_FOLDER)/builds 37 | PATH_FOR_BUILDING=$(PATH_BUILDS_FOLDER)/$(PRODUCT)$(shell echo $(BASE_IMAGE) | cut -f2 -d"/")-$(GIT_CURRENT_BRANCH) 38 | PATH_FOR_INSTALL=$(PATH_BUILDS_FOLDER)/$(PRODUCT)$(shell echo $(BASE_IMAGE) | cut -f2 -d"/")-$(GIT_CURRENT_BRANCH)/opt 39 | DOCKERFILE_FOR_BUILDING=$(PATH_BUILDTOOLS_FOLDER)/Dockerfile.Ubuntu-18.04 40 | 41 | ################################################################################ 42 | # Determine Git-related setting. 43 | GIT_CURRENT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) 44 | 45 | ################################################################################ 46 | define runOnCode 47 | $(CMAKE) -E make_directory $(PATH_FOR_BUILDING) 48 | $(CMAKE) -E make_directory $(PATH_FOR_INSTALL) 49 | $(DOCKER) run --rm -ti -v /etc/group:/etc/group:ro \ 50 | --net=host \ 51 | --cap-add SYS_PTRACE \ 52 | -e USER_FOR_BUILDING=$$USER \ 53 | -v /etc/passwd:/etc/passwd:ro \ 54 | -v /etc/shadow:/etc/shadow:ro \ 55 | -v $(PATH_SOURCES_FOLDER):/opt/sources \ 56 | -v $(PATH_BUILDTOOLS_FOLDER)/build.sh:/opt/build.sh:ro \ 57 | -v $(PATH_FOR_BUILDING):/opt/build \ 58 | -v $(PATH_FOR_INSTALL):/opt/install \ 59 | -v $(PATH_CCACHE):/opt/ccache \ 60 | -w /opt/build \ 61 | $(BASE_IMAGE_DEV):$(BASE_IMAGE_DEV_VERSION) \ 62 | /opt/build.sh $1 ./$(SOURCE_FOLDER)/ 63 | endef 64 | 65 | .PHONY: all clean createDockerBuildImage build compile test install memory-leaks code-coverage show-coverage reformat-code static-code-analysis performance deploy deployToAlpine deployToAlpine-armhf deployToAlpine-aarch64 deployToJavaScript deployToLaunchpad deployToHeaderOnly docs 66 | 67 | ################################################################################ 68 | all: build 69 | 70 | clean: 71 | $(CMAKE) -E remove_directory $(PATH_FOR_BUILDING) 72 | 73 | createDockerBuildImage: 74 | cd $(PATH_BUILDTOOLS_FOLDER) && $(DOCKER) build --no-cache -t $(BASE_IMAGE_DEV) -f $(DOCKERFILE_FOR_BUILDING) . 75 | 76 | build: compile test install 77 | 78 | compile: 79 | $(call runOnCode,compile) 80 | 81 | test: 82 | $(call runOnCode,test) 83 | 84 | install: 85 | $(call runOnCode,install) 86 | 87 | memory-leaks: 88 | $(call runOnCode,memory-leaks) 89 | 90 | code-coverage: 91 | $(call runOnCode,code-coverage) 92 | 93 | show-coverage: 94 | $(call runOnCode,show-coverage) 95 | 96 | reformat-code: 97 | $(call runOnCode,reformat-code) 98 | 99 | static-code-analysis: 100 | $(call runOnCode,static-code-analysis) 101 | 102 | performance: 103 | $(call runOnCode,performance) 104 | 105 | deployToAlpine: 106 | ./buildtools/deploy/deployToAlpine.sh 107 | 108 | #deployToJavaScript: 109 | # ./buildtools/deploy/deployToJavaScript.sh 110 | 111 | deployToLaunchpad: 112 | ./buildtools/deploy/deployToLaunchpad.sh 113 | 114 | deployToHeaderOnly: 115 | ./buildtools/deploy/deployToHeaderOnly.sh 116 | 117 | #deploy: deployToLaunchpad docs deployToHeaderOnly deployToJavaScript deployToAlpine 118 | deploy: deployToLaunchpad docs deployToHeaderOnly 119 | 120 | docs: 121 | ./buildtools/deploy/generateDocumentation.sh 122 | 123 | -------------------------------------------------------------------------------- /libcluon/src/LCMToGenericMessage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/LCMToGenericMessage.hpp" 10 | #include "cluon/FromLCMVisitor.hpp" 11 | #include "cluon/MessageParser.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // clang-format off 18 | #ifdef WIN32 19 | #undef be32toh 20 | #define be32toh(x) __ntohl(x) 21 | 22 | #include 23 | #include 24 | uint32_t __ntohl(const uint32_t v) { 25 | uint8_t d[4] = {}; 26 | std::memmove(&d, &v, sizeof(d)); 27 | 28 | return ((uint32_t) d[3] << 0) 29 | | ((uint32_t) d[2] << 8) 30 | | ((uint32_t) d[1] << 16) 31 | | ((uint32_t) d[0] << 24); 32 | } 33 | #endif 34 | // clang-format on 35 | 36 | namespace cluon { 37 | 38 | int32_t LCMToGenericMessage::setMessageSpecification(const std::string &ms) noexcept { 39 | int32_t retVal{-1}; 40 | 41 | m_listOfMetaMessages.clear(); 42 | m_scopeOfMetaMessages.clear(); 43 | 44 | cluon::MessageParser mp; 45 | auto parsingResult = mp.parse(ms); 46 | if (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == parsingResult.second) { 47 | m_listOfMetaMessages = parsingResult.first; 48 | for (const auto &mm : m_listOfMetaMessages) { m_scopeOfMetaMessages[mm.messageName()] = mm; } 49 | retVal = static_cast(m_listOfMetaMessages.size()); 50 | } 51 | return retVal; 52 | } 53 | 54 | cluon::GenericMessage LCMToGenericMessage::getGenericMessage(const std::string &data) noexcept { 55 | cluon::GenericMessage gm; 56 | 57 | if (!m_listOfMetaMessages.empty()) { 58 | constexpr uint8_t LCM_HEADER_SIZE{4 /*magic number*/ + 4 /*sequence number*/ + 1 /*'\0' after channel name*/}; 59 | if (LCM_HEADER_SIZE < data.size()) { 60 | // First, read magic number. 61 | constexpr uint32_t MAGIC_NUMBER_LCM2{0x4c433032}; 62 | uint32_t offset{0}; 63 | uint32_t magicNumber{0}; 64 | { 65 | std::stringstream sstr{std::string(&data[offset], 4)}; 66 | // clang-format off 67 | sstr.read(reinterpret_cast(&magicNumber), sizeof(uint32_t)); /* Flawfinder: ignore */ // NOLINT 68 | // clang-format on 69 | magicNumber = be32toh(magicNumber); 70 | } 71 | if (MAGIC_NUMBER_LCM2 == magicNumber) { 72 | offset += 4; 73 | 74 | // Next, read sequence number in case of fragmented data. 75 | uint32_t sequenceNumber{0}; 76 | { 77 | std::stringstream sstr{std::string(&data[offset], 4)}; 78 | // clang-format off 79 | sstr.read(reinterpret_cast(&sequenceNumber), sizeof(uint32_t)); /* Flawfinder: ignore */ // NOLINT 80 | // clang-format on 81 | sequenceNumber = be32toh(sequenceNumber); 82 | } 83 | // Only support for non-fragmented messages. 84 | if (0 == sequenceNumber) { 85 | offset += 4; 86 | 87 | const std::string::size_type START_POSITION = offset; 88 | std::string::size_type pos = data.find('\0', START_POSITION); // Extract channel name. 89 | if (std::string::npos != pos) { 90 | const std::string CHANNEL_NAME(data.substr(START_POSITION, (pos - START_POSITION))); 91 | 92 | // Next, find the MetaMessage corresponding to the channel name 93 | // and create a Message therefrom based on the decoded LCM data. 94 | if ((0 < m_scopeOfMetaMessages.count(CHANNEL_NAME)) && (std::string::npos != (pos + 1))) { 95 | // data[offset+i] marks now the beginning of the payload to be decoded. 96 | std::stringstream sstr{data.substr(pos + 1)}; 97 | 98 | cluon::FromLCMVisitor fromLCM; 99 | fromLCM.decodeFrom(sstr); 100 | 101 | gm.createFrom(m_scopeOfMetaMessages[CHANNEL_NAME], m_listOfMetaMessages); 102 | gm.accept(fromLCM); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | return gm; 111 | } 112 | 113 | } // namespace cluon 114 | -------------------------------------------------------------------------------- /libcluon/src/ToODVDVisitor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/ToODVDVisitor.hpp" 10 | 11 | #include 12 | 13 | namespace cluon { 14 | 15 | std::string ToODVDVisitor::messageSpecification() const noexcept { 16 | std::stringstream tmp; 17 | for (const auto &e : m_forwardDeclarations) { tmp << e; } 18 | tmp << m_buffer.str(); 19 | 20 | const std::string retVal{tmp.str()}; 21 | return retVal; 22 | } 23 | 24 | void ToODVDVisitor::preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept { 25 | (void)shortName; 26 | m_buffer << "message " << longName << " [ id = " << id << " ] {" << '\n'; 27 | } 28 | 29 | void ToODVDVisitor::postVisit() noexcept { 30 | m_buffer << '}' << '\n'; 31 | } 32 | 33 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept { 34 | (void)typeName; 35 | (void)v; 36 | m_buffer << " " 37 | << "bool" 38 | << " " << name << " [ default = false, id = " << id << " ];" << '\n'; 39 | } 40 | 41 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept { 42 | (void)typeName; 43 | (void)v; 44 | m_buffer << " " 45 | << "char" 46 | << " " << name << " [ default = '0', id = " << id << " ];" << '\n'; 47 | } 48 | 49 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept { 50 | (void)typeName; 51 | (void)v; 52 | m_buffer << " " 53 | << "int8" 54 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 55 | } 56 | 57 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept { 58 | (void)typeName; 59 | (void)v; 60 | m_buffer << " " 61 | << "uint8" 62 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 63 | } 64 | 65 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept { 66 | (void)typeName; 67 | (void)v; 68 | m_buffer << " " 69 | << "int16" 70 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 71 | } 72 | 73 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept { 74 | (void)typeName; 75 | (void)v; 76 | m_buffer << " " 77 | << "uint16" 78 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 79 | } 80 | 81 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept { 82 | (void)typeName; 83 | (void)v; 84 | m_buffer << " " 85 | << "int32" 86 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 87 | } 88 | 89 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept { 90 | (void)typeName; 91 | (void)v; 92 | m_buffer << " " 93 | << "uint32" 94 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 95 | } 96 | 97 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept { 98 | (void)typeName; 99 | (void)v; 100 | m_buffer << " " 101 | << "int64" 102 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 103 | } 104 | 105 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept { 106 | (void)typeName; 107 | (void)v; 108 | m_buffer << " " 109 | << "uint64" 110 | << " " << name << " [ default = 0, id = " << id << " ];" << '\n'; 111 | } 112 | 113 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept { 114 | (void)typeName; 115 | (void)v; 116 | m_buffer << " " 117 | << "float" 118 | << " " << name << " [ default = 0.0, id = " << id << " ];" << '\n'; 119 | } 120 | 121 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept { 122 | (void)typeName; 123 | (void)v; 124 | m_buffer << " " 125 | << "double" 126 | << " " << name << " [ default = 0.0, id = " << id << " ];" << '\n'; 127 | } 128 | 129 | void ToODVDVisitor::visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept { 130 | (void)typeName; 131 | (void)v; 132 | m_buffer << " " 133 | << "string" 134 | << " " << name << " [ default = \"\", id = " << id << " ];" << '\n'; 135 | } 136 | 137 | } // namespace cluon 138 | -------------------------------------------------------------------------------- /libcluon/testsuites/Testcluon_OD4toStdout.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon-OD4toStdout.hpp" 12 | #include "cluon/Player.hpp" 13 | #include "cluon/TerminateHandler.hpp" 14 | #include "cluon/cluonTestDataStructures.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // clang-format off 24 | #ifdef WIN32 25 | #define UNLINK _unlink 26 | #else 27 | #include 28 | #define UNLINK unlink 29 | #endif 30 | // clang-format on 31 | 32 | class RedirectCOUT { 33 | public: 34 | RedirectCOUT(std::streambuf *rdbuf) 35 | : m_rdbuf(std::cout.rdbuf(rdbuf)) { 36 | std::ios::sync_with_stdio(true); 37 | } 38 | 39 | ~RedirectCOUT() { std::cout.rdbuf(m_rdbuf); } 40 | 41 | private: 42 | std::streambuf *m_rdbuf; 43 | }; 44 | 45 | TEST_CASE("Test empty commandline parameters.") { 46 | int32_t argc = 1; 47 | const char *argv[] = {static_cast("cluon-OD4toStdout")}; 48 | REQUIRE(1 == cluon_OD4toStdout(argc, const_cast(argv))); 49 | } 50 | 51 | TEST_CASE("Test wrong --cid.") { 52 | // Test only on x86_64 platforms. 53 | #if defined(__amd64__) && defined(__linux__) 54 | std::stringstream capturedCout; 55 | RedirectCOUT redirect(capturedCout.rdbuf()); 56 | 57 | constexpr int32_t argc = 2; 58 | const char *argv[] = {static_cast("cluon-OD4toStdout"), static_cast("--cid=345")}; 59 | REQUIRE(1 == cluon_OD4toStdout(argc, const_cast(argv))); 60 | #endif 61 | } 62 | 63 | TEST_CASE("Test starting cluon-OD4toStdout in thread and send one message.") { 64 | // Test only on x86_64 platforms. 65 | #if defined(__amd64__) && defined(__linux__) 66 | // Reset TerminateHandler. 67 | cluon::TerminateHandler::instance().isTerminated.store(false); 68 | 69 | UNLINK("GHI.rec"); 70 | 71 | std::stringstream capturedCout; 72 | RedirectCOUT redirect(capturedCout.rdbuf()); 73 | 74 | std::thread runOD4toStdout([]() { 75 | constexpr int32_t argc = 2; 76 | const char *argv[] = {static_cast("cluon-OD4toStdout"), static_cast("--cid=71")}; 77 | REQUIRE(0 == cluon_OD4toStdout(argc, const_cast(argv))); 78 | }); 79 | 80 | // Wait before sending. 81 | using namespace std::literals::chrono_literals; 82 | std::this_thread::sleep_for(100ms); 83 | 84 | testdata::MyTestMessage5 msg; 85 | msg.attribute1(3) 86 | .attribute2(-3) 87 | .attribute3(103) 88 | .attribute4(-103) 89 | .attribute5(10003) 90 | .attribute6(-10003) 91 | .attribute7(54321) 92 | .attribute8(-74321) 93 | .attribute9(-5.4321f) 94 | .attribute10(-50.4321) 95 | .attribute11("Hello cluon World!"); 96 | 97 | cluon::OD4Session od4(71); 98 | 99 | using namespace std::literals::chrono_literals; 100 | do { std::this_thread::sleep_for(1ms); } while (!od4.isRunning()); 101 | 102 | REQUIRE(od4.isRunning()); 103 | 104 | od4.send(msg); 105 | 106 | // Wait before stopping. 107 | using namespace std::literals::chrono_literals; 108 | std::this_thread::sleep_for(500ms); 109 | capturedCout.flush(); 110 | 111 | const std::string tmp = capturedCout.str(); 112 | 113 | // Write received data into file and replay with Player. 114 | { 115 | std::fstream fout("GHI.rec", std::ios::out | std::ios::binary | std::ios::trunc); 116 | fout << tmp; 117 | fout.flush(); 118 | fout.close(); 119 | 120 | constexpr bool AUTO_REWIND{false}; 121 | constexpr bool THREADING{false}; 122 | cluon::Player player("GHI.rec", AUTO_REWIND, THREADING); 123 | 124 | REQUIRE(player.hasMoreData()); 125 | REQUIRE(1 == player.totalNumberOfEnvelopesInRecFile()); 126 | 127 | auto entry = player.getNextEnvelopeToBeReplayed(); 128 | REQUIRE(entry.first); 129 | 130 | cluon::data::Envelope env = entry.second; 131 | REQUIRE(testdata::MyTestMessage5::ID() == env.dataType()); 132 | 133 | testdata::MyTestMessage5 tmp2 = cluon::extractMessage(std::move(env)); 134 | REQUIRE(3 == tmp2.attribute1()); 135 | REQUIRE(-3 == tmp2.attribute2()); 136 | REQUIRE(103 == tmp2.attribute3()); 137 | REQUIRE(-103 == tmp2.attribute4()); 138 | REQUIRE(10003 == tmp2.attribute5()); 139 | REQUIRE(-10003 == tmp2.attribute6()); 140 | REQUIRE(54321 == tmp2.attribute7()); 141 | REQUIRE(-74321 == tmp2.attribute8()); 142 | REQUIRE(-5.4321f == Approx(tmp2.attribute9())); 143 | REQUIRE(-50.4321 == Approx(tmp2.attribute10())); 144 | REQUIRE("Hello cluon World!" == tmp2.attribute11()); 145 | } 146 | 147 | cluon::TerminateHandler::instance().isTerminated.store(true); 148 | 149 | runOD4toStdout.join(); 150 | UNLINK("GHI.rec"); 151 | #endif 152 | } 153 | -------------------------------------------------------------------------------- /libcluon/include/cluon/FromJSONVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_FROMJSONVISITOR_HPP 10 | #define CLUON_FROMJSONVISITOR_HPP 11 | 12 | #include "cluon/JSONConstants.hpp" 13 | #include "cluon/any/any.hpp" 14 | #include "cluon/cluon.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cluon { 22 | /** 23 | This class decodes a given message from JSON format. 24 | */ 25 | class LIBCLUON_API FromJSONVisitor { 26 | /** 27 | * This class represents a key/value in a JSON list of key/values. 28 | */ 29 | class JSONKeyValue { 30 | private: 31 | JSONKeyValue &operator=(JSONKeyValue &&) = delete; 32 | 33 | public: 34 | JSONKeyValue() = default; 35 | JSONKeyValue(const JSONKeyValue &) = default; 36 | JSONKeyValue(JSONKeyValue &&) = default; 37 | JSONKeyValue &operator=(const JSONKeyValue &) = default; 38 | ~JSONKeyValue() = default; 39 | 40 | public: 41 | std::string m_key{""}; 42 | JSONConstants m_type{JSONConstants::UNDEFINED}; 43 | linb::any m_value; 44 | }; 45 | 46 | private: 47 | FromJSONVisitor(const FromJSONVisitor &) = delete; 48 | FromJSONVisitor(FromJSONVisitor &&) = delete; 49 | FromJSONVisitor &operator=(FromJSONVisitor &&) = delete; 50 | FromJSONVisitor &operator=(const FromJSONVisitor &other) = delete; 51 | 52 | /** 53 | * Internal constructor to pass reference to preset key/values. 54 | * 55 | * @param preset Pre-filled key/value map to handle nested fields. 56 | */ 57 | FromJSONVisitor(std::map &preset) noexcept; 58 | 59 | public: 60 | FromJSONVisitor() noexcept; 61 | ~FromJSONVisitor() = default; 62 | 63 | public: 64 | /** 65 | * This method decodes a given istream into an internal key/value representation. 66 | * 67 | * @param in istream to decode. 68 | */ 69 | void decodeFrom(std::istream &in) noexcept; 70 | 71 | public: 72 | // The following methods are provided to allow an instance of this class to 73 | // be used as visitor for an instance with the method signature void accept(T&); 74 | 75 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 76 | void postVisit() noexcept; 77 | 78 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 79 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 80 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 81 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 82 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 83 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 84 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 85 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 86 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 87 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 88 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 89 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 90 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 91 | 92 | template 93 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 94 | (void)id; 95 | (void)typeName; 96 | 97 | if (0 < m_keyValues.count(name)) { 98 | try { 99 | std::map v 100 | = linb::any_cast>(m_keyValues[name].m_value); 101 | cluon::FromJSONVisitor nestedJSONDecoder(v); 102 | value.accept(nestedJSONDecoder); 103 | } catch (const linb::bad_any_cast &) { // LCOV_EXCL_LINE 104 | } 105 | } 106 | } 107 | 108 | public: 109 | /** 110 | * This method returns the base64-decoded representation for the given input. 111 | * 112 | * @param input to decode from base64 113 | * @return Decoded input. 114 | */ 115 | static std::string decodeBase64(const std::string &input) noexcept; 116 | 117 | private: 118 | std::map readKeyValues(std::string &input) noexcept; 119 | 120 | private: 121 | std::map m_data{}; 122 | std::map &m_keyValues; 123 | }; 124 | } // namespace cluon 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /libcluon/src/MetaMessageToProtoTransformator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/MetaMessageToProtoTransformator.hpp" 10 | #include "cluon/MetaMessage.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "Mustache/mustache.hpp" 18 | 19 | namespace cluon { 20 | 21 | const char *proto2FileTemplate = R"( 22 | // 23 | // THIS IS AN AUTO-GENERATED FILE. DO NOT MODIFY AS CHANGES MIGHT BE OVERWRITTEN! 24 | // 25 | 26 | {{%PROTO2_HEADER%}} 27 | 28 | // Message identifier: {{%MESSAGE_IDENTIFIER%}}. 29 | message {{%MESSAGE%}} { 30 | {{#%FIELDS%}} 31 | optional {{%TYPE%}} {{%NAME%}} = {{%FIELD_IDENTIFIER%}}; 32 | {{/%FIELDS%}} 33 | } 34 | )"; 35 | 36 | std::string MetaMessageToProtoTransformator::content(bool withProtoHeader) noexcept { 37 | if (withProtoHeader) { 38 | m_dataToBeRendered.set("%PROTO2_HEADER%", R"( 39 | // This line is only needed when using Google Protobuf 3. 40 | syntax = "proto2"; 41 | )"); 42 | } 43 | m_dataToBeRendered.set("%FIELDS%", m_fields); 44 | 45 | kainjow::mustache::mustache tmpl{proto2FileTemplate}; 46 | // Reset Mustache's default string-escaper. 47 | tmpl.set_custom_escape([](const std::string &s) { return s; }); 48 | std::stringstream sstr; 49 | sstr << tmpl.render(m_dataToBeRendered); 50 | const std::string str(sstr.str()); 51 | return str; 52 | } 53 | 54 | void MetaMessageToProtoTransformator::visit(const MetaMessage &mm) noexcept { 55 | kainjow::mustache::data dataToBeRendered; 56 | kainjow::mustache::data fields{kainjow::mustache::data::type::list}; 57 | 58 | std::map typeToTypeStringMap = { 59 | {MetaMessage::MetaField::BOOL_T, "bool"}, 60 | {MetaMessage::MetaField::CHAR_T, "sint32"}, 61 | {MetaMessage::MetaField::UINT8_T, "uint32"}, 62 | {MetaMessage::MetaField::INT8_T, "sint32"}, 63 | {MetaMessage::MetaField::UINT16_T, "uint32"}, 64 | {MetaMessage::MetaField::INT16_T, "sint32"}, 65 | {MetaMessage::MetaField::UINT32_T, "uint32"}, 66 | {MetaMessage::MetaField::INT32_T, "sint32"}, 67 | {MetaMessage::MetaField::UINT64_T, "uint64"}, 68 | {MetaMessage::MetaField::INT64_T, "sint64"}, 69 | {MetaMessage::MetaField::FLOAT_T, "float"}, 70 | {MetaMessage::MetaField::DOUBLE_T, "double"}, 71 | {MetaMessage::MetaField::STRING_T, "string"}, 72 | {MetaMessage::MetaField::BYTES_T, "bytes"}, 73 | }; 74 | 75 | std::string namespacePrefix; 76 | std::string messageName{mm.messageName()}; 77 | const auto pos = mm.messageName().find_last_of('.'); 78 | if (std::string::npos != pos) { 79 | namespacePrefix = mm.messageName().substr(0, pos); 80 | messageName = mm.messageName().substr(pos + 1); 81 | } 82 | 83 | std::string packageNameWithUnderscores{mm.packageName()}; 84 | std::replace(packageNameWithUnderscores.begin(), packageNameWithUnderscores.end(), '.', '_'); 85 | const std::string completePackageNameWithNamespacePrefix 86 | = packageNameWithUnderscores + (!packageNameWithUnderscores.empty() && !namespacePrefix.empty() ? "." : "") + namespacePrefix; 87 | 88 | std::string completePackageNameWithNamespacePrefixWithUnderscores{completePackageNameWithNamespacePrefix}; 89 | std::replace(completePackageNameWithNamespacePrefixWithUnderscores.begin(), completePackageNameWithNamespacePrefixWithUnderscores.end(), '.', '_'); 90 | const std::string completeMessageNameWithUnderscores 91 | = completePackageNameWithNamespacePrefixWithUnderscores + (!completePackageNameWithNamespacePrefixWithUnderscores.empty() ? +"_" : "") + messageName; 92 | 93 | dataToBeRendered.set("%MESSAGE_IDENTIFIER%", std::to_string(mm.messageIdentifier())); 94 | dataToBeRendered.set("%MESSAGE%", completeMessageNameWithUnderscores); 95 | 96 | for (const auto &f : mm.listOfMetaFields()) { 97 | std::string fieldName{f.fieldName()}; 98 | std::replace(fieldName.begin(), fieldName.end(), '.', '_'); 99 | 100 | kainjow::mustache::data fieldEntry; 101 | fieldEntry.set("%NAME%", fieldName); 102 | if (MetaMessage::MetaField::MESSAGE_T != f.fieldDataType()) { 103 | fieldEntry.set("%TYPE%", typeToTypeStringMap[f.fieldDataType()]); 104 | } else { 105 | std::string dataTypeNameWithUnderscores{f.fieldDataTypeName()}; 106 | std::replace(dataTypeNameWithUnderscores.begin(), dataTypeNameWithUnderscores.end(), '.', '_'); 107 | std::string tmp{packageNameWithUnderscores + (!packageNameWithUnderscores.empty() ? "_" : "")}; 108 | tmp += dataTypeNameWithUnderscores; 109 | fieldEntry.set("%TYPE%", tmp); 110 | } 111 | fieldEntry.set("%FIELD_IDENTIFIER%", std::to_string(f.fieldIdentifier())); 112 | fields.push_back(fieldEntry); 113 | } 114 | 115 | m_dataToBeRendered = std::move(dataToBeRendered); 116 | m_fields = std::move(fields); 117 | } 118 | } // namespace cluon 119 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ToCSVVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TOCSVVISITOR_HPP 10 | #define CLUON_TOCSVVISITOR_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace cluon { 20 | /** 21 | This class provides a visitor to transform a message into CSV with 22 | user-specified delimiters and optional column headers: 23 | 24 | \code{.cpp} 25 | MyMessage msg; 26 | // Set some values in msg. 27 | 28 | cluon::ToCSVVisitor csv{',', true}; 29 | msg.accept(csv); 30 | 31 | std::cout << csv.csv() << std::endl; 32 | \endcode 33 | 34 | Subsequent use of this visitor will append the data (please keep in mind to not 35 | change the visited messages in between as the generated CSV data will be messed 36 | up otherwise). 37 | */ 38 | class LIBCLUON_API ToCSVVisitor { 39 | private: 40 | ToCSVVisitor(const ToCSVVisitor &) = delete; 41 | ToCSVVisitor(ToCSVVisitor &&) = delete; 42 | ToCSVVisitor &operator=(const ToCSVVisitor &) = delete; 43 | ToCSVVisitor &operator=(ToCSVVisitor &&) = delete; 44 | 45 | public: 46 | /** 47 | * Constructor. 48 | * 49 | * @param delimiter Delimiter character. 50 | * @param withHeader If true, the first line in the output contains the 51 | * column headers. 52 | * @param mask Map describing which fields to render. If empty, all 53 | * fields will be emitted; individual field identifiers 54 | * can be masked setting them to false. 55 | */ 56 | ToCSVVisitor(char delimiter = ';', bool withHeader = true, const std::map &mask = {}) noexcept; 57 | 58 | protected: 59 | /** 60 | * Constructor for internal use. 61 | * 62 | * @param prefix Prefix to prepend per column header. 63 | * @param delimiter Delimiter character. 64 | * @param withHeader If true, the first line in the output contains the 65 | * column headers. 66 | * @param isNested If true, the returned CSV values do not have a trailing new line. 67 | */ 68 | ToCSVVisitor(const std::string &prefix, char delimiter, bool withHeader, bool isNested) noexcept; 69 | 70 | public: 71 | /** 72 | * @return CSV-encoded data. 73 | */ 74 | std::string csv() const noexcept; 75 | 76 | /** 77 | * This method clears the containing CSV data. 78 | */ 79 | void clear() noexcept; 80 | 81 | public: 82 | // The following methods are provided to allow an instance of this class to 83 | // be used as visitor for an instance with the method signature void accept(T&); 84 | 85 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 86 | void postVisit() noexcept; 87 | 88 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 89 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 90 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 91 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 92 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 93 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 94 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 95 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 96 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 97 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 98 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 99 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 100 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 101 | 102 | template 103 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 104 | (void)id; 105 | (void)typeName; 106 | if ((0 == m_mask.count(id)) || m_mask[id]) { 107 | constexpr bool IS_NESTED{true}; 108 | ToCSVVisitor csvVisitor(name, m_delimiter, m_withHeader, IS_NESTED); 109 | value.accept(csvVisitor); 110 | 111 | if (m_fillHeader) { 112 | m_bufferHeader << csvVisitor.m_bufferHeader.str(); 113 | } 114 | m_bufferValues << csvVisitor.m_bufferValues.str(); 115 | } 116 | } 117 | 118 | private: 119 | std::map m_mask{}; 120 | std::string m_prefix{}; 121 | char m_delimiter{';'}; 122 | bool m_withHeader{true}; 123 | bool m_isNested{false}; 124 | bool m_fillHeader{true}; 125 | std::stringstream m_bufferHeader{}; 126 | std::stringstream m_bufferValues{}; 127 | }; 128 | 129 | } // namespace cluon 130 | #endif 131 | -------------------------------------------------------------------------------- /libcluon/include/cluon/FromMsgPackVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_FROMMSGPACKVISITOR_HPP 10 | #define CLUON_FROMMSGPACKVISITOR_HPP 11 | 12 | #include "cluon/MsgPackConstants.hpp" 13 | #include "cluon/any/any.hpp" 14 | #include "cluon/cluon.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cluon { 22 | /** 23 | This class decodes a given message from MsgPack format. 24 | */ 25 | class LIBCLUON_API FromMsgPackVisitor { 26 | /** 27 | * This class represents a key/value in a MsgPack payload stream of key/values. 28 | */ 29 | class MsgPackKeyValue { 30 | private: 31 | MsgPackKeyValue &operator=(MsgPackKeyValue &&) = delete; 32 | 33 | public: 34 | MsgPackKeyValue() = default; 35 | MsgPackKeyValue(const MsgPackKeyValue &) = default; 36 | MsgPackKeyValue(MsgPackKeyValue &&) = default; 37 | MsgPackKeyValue &operator=(const MsgPackKeyValue &) = default; 38 | ~MsgPackKeyValue() = default; 39 | 40 | public: 41 | std::string m_key{""}; 42 | MsgPackConstants m_formatFamily{MsgPackConstants::BOOL_FORMAT}; 43 | linb::any m_value; 44 | }; 45 | 46 | private: 47 | FromMsgPackVisitor(const FromMsgPackVisitor &) = delete; 48 | FromMsgPackVisitor(FromMsgPackVisitor &&) = delete; 49 | FromMsgPackVisitor &operator=(FromMsgPackVisitor &&) = delete; 50 | FromMsgPackVisitor &operator=(const FromMsgPackVisitor &other) = delete; 51 | 52 | /** 53 | * Internal constructor to pass reference to preset key/values. 54 | * 55 | * @param preset Pre-filled key/value map to handle nested fields. 56 | */ 57 | FromMsgPackVisitor(std::map &preset) noexcept; 58 | 59 | public: 60 | FromMsgPackVisitor() noexcept; 61 | ~FromMsgPackVisitor() = default; 62 | 63 | public: 64 | /** 65 | * This method decodes a given istream into an internal key/value representation. 66 | * 67 | * @param in istream to decode. 68 | */ 69 | void decodeFrom(std::istream &in) noexcept; 70 | 71 | public: 72 | // The following methods are provided to allow an instance of this class to 73 | // be used as visitor for an instance with the method signature void accept(T&); 74 | 75 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 76 | void postVisit() noexcept; 77 | 78 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 79 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 80 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 81 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 82 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 83 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 84 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 85 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 86 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 87 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 88 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 89 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 90 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 91 | 92 | template 93 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 94 | (void)id; 95 | (void)typeName; 96 | 97 | if (0 < m_keyValues.count(name)) { 98 | try { 99 | std::map v 100 | = linb::any_cast>(m_keyValues[name].m_value); 101 | cluon::FromMsgPackVisitor nestedMsgPackDecoder(v); 102 | value.accept(nestedMsgPackDecoder); 103 | } catch (const linb::bad_any_cast &) { // LCOV_EXCL_LINE 104 | } 105 | } 106 | } 107 | 108 | private: 109 | MsgPackConstants getFormatFamily(uint8_t T) noexcept; 110 | std::map readKeyValues(std::istream &in) noexcept; 111 | uint64_t readUint(std::istream &in) noexcept; 112 | int64_t readInt(std::istream &in) noexcept; 113 | std::string readString(std::istream &in) noexcept; 114 | 115 | private: 116 | std::map m_data{}; 117 | std::map &m_keyValues; 118 | }; 119 | } // namespace cluon 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /libcluon/include/cluon/UDPReceiver.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_UDPRECEIVER_HPP 10 | #define CLUON_UDPRECEIVER_HPP 11 | 12 | #include "cluon/NotifyingPipeline.hpp" 13 | #include "cluon/cluon.hpp" 14 | 15 | // clang-format off 16 | #ifdef WIN32 17 | #include // for WSAStartUp 18 | #include // for SOCKET 19 | #else 20 | #include 21 | #endif 22 | // clang-format on 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace cluon { 37 | /** 38 | To receive data from a UDP socket, simply include the header 39 | `#include `. 40 | 41 | Next, create an instance of class `cluon::UDPReceiver` as follows: 42 | `cluon::UDPReceiver receiver("127.0.0.1", 1234, delegate);`. 43 | The first parameter is of type `std::string` expecting a numerical IPv4 address, 44 | the second parameter specifies the UDP port, from which data shall be received 45 | from, and the last parameter is of type `std::function` that is called whenever 46 | new bytes are available to be processed. 47 | 48 | The complete signature for the delegate function is 49 | `std::function`: 50 | The first parameter contains the bytes that have been received, the second 51 | parameter containes the human-readable representation of the sender 52 | (X.Y.Z.W:ABCD), and the last parameter is the time stamp when the data has been 53 | received. An example using a C++ lambda expression would look as follows: 54 | 55 | \code{.cpp} 56 | cluon::UDPReceiver receiver("127.0.0.1", 1234, 57 | [](std::string &&data, std::string &&sender, std::chrono::system_clock::time_point &&ts) noexcept { 58 | const auto timestamp(std::chrono::system_clock::to_time_t(ts)); 59 | std::cout << "Received " << data.size() << " bytes" 60 | << " from " << sender 61 | << " at " << std::put_time(std::localtime(×tamp), "%Y-%m-%d %X") 62 | << ", containing '" << data << "'." << std::endl; 63 | }); 64 | \endcode 65 | 66 | After creating an instance of class `cluon::UDPReceiver`, it is immediately 67 | activated and concurrently waiting for data in a separate thread. To check 68 | whether the instance was created successfully and running, the method 69 | `isRunning()` should be called. 70 | 71 | A complete example is available 72 | [here](https://github.com/chrberger/libcluon/blob/master/libcluon/examples/cluon-UDPReceiver.cpp). 73 | */ 74 | class LIBCLUON_API UDPReceiver { 75 | private: 76 | UDPReceiver(const UDPReceiver &) = delete; 77 | UDPReceiver(UDPReceiver &&) = delete; 78 | UDPReceiver &operator=(const UDPReceiver &) = delete; 79 | UDPReceiver &operator=(UDPReceiver &&) = delete; 80 | 81 | public: 82 | /** 83 | * Constructor. 84 | * 85 | * @param receiveFromAddress Numerical IPv4 address to receive UDP packets from. 86 | * @param receiveFromPort Port to receive UDP packets from. 87 | * @param delegate Functional (noexcept) to handle received bytes; parameters are received data, sender, timestamp. 88 | * @param localSendFromPort Port that an application is using to send data. This port (> 0) is ignored when data is received. 89 | */ 90 | UDPReceiver(const std::string &receiveFromAddress, 91 | uint16_t receiveFromPort, 92 | std::function delegate, 93 | uint16_t localSendFromPort = 0) noexcept; 94 | ~UDPReceiver() noexcept; 95 | 96 | /** 97 | * @return true if the UDPReceiver could successfully be created and is able to receive data. 98 | */ 99 | bool isRunning() const noexcept; 100 | 101 | private: 102 | /** 103 | * This method closes the socket. 104 | * 105 | * @param errorCode Error code that caused this closing. 106 | */ 107 | void closeSocket(int errorCode) noexcept; 108 | 109 | void readFromSocket() noexcept; 110 | 111 | private: 112 | int32_t m_socket{-1}; 113 | bool m_isBlockingSocket{true}; 114 | std::set m_listOfLocalIPAddresses{}; 115 | uint16_t m_localSendFromPort; 116 | struct sockaddr_in m_receiveFromAddress {}; 117 | struct ip_mreq m_mreq {}; 118 | bool m_isMulticast{false}; 119 | 120 | std::atomic m_readFromSocketThreadRunning{false}; 121 | std::thread m_readFromSocketThread{}; 122 | 123 | private: 124 | std::function m_delegate{}; 125 | 126 | private: 127 | class PipelineEntry { 128 | public: 129 | std::string m_data; 130 | std::string m_from; 131 | std::chrono::system_clock::time_point m_sampleTime; 132 | }; 133 | 134 | std::shared_ptr> m_pipeline{}; 135 | }; 136 | } // namespace cluon 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /libcluon/include/cluon/Envelope.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_ENVELOPE_HPP 10 | #define CLUON_ENVELOPE_HPP 11 | 12 | #include "cluon/FromProtoVisitor.hpp" 13 | #include "cluon/ToProtoVisitor.hpp" 14 | #include "cluon/cluonDataStructures.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace cluon { 24 | 25 | /** 26 | * This method transforms a given Envelope to a string representation to be 27 | * sent to an OpenDaVINCI session. 28 | * 29 | * @param envelope Envelope with payload to be sent. 30 | * @return String representation of the Envelope to be sent to OpenDaVINCI v4. 31 | */ 32 | inline std::string serializeEnvelope(cluon::data::Envelope &&envelope) noexcept { 33 | std::string dataToSend; 34 | { 35 | std::stringstream sstr; 36 | 37 | cluon::ToProtoVisitor protoEncoder; 38 | envelope.accept(protoEncoder); 39 | 40 | const std::string tmp{protoEncoder.encodedData()}; 41 | uint32_t length{static_cast(tmp.size())}; 42 | length <<= 8; 43 | length = htole32(length); 44 | 45 | // Add OD4 header. 46 | constexpr unsigned char OD4_HEADER_BYTE0 = 0x0D; 47 | constexpr unsigned char OD4_HEADER_BYTE1 = 0xA4; 48 | sstr.put(static_cast(OD4_HEADER_BYTE0)); 49 | auto posByte1 = sstr.tellp(); 50 | sstr.write(reinterpret_cast(&length), static_cast(sizeof(uint32_t))); 51 | auto posByte5 = sstr.tellp(); 52 | sstr.seekp(posByte1); 53 | sstr.put(static_cast(OD4_HEADER_BYTE1)); 54 | sstr.seekp(posByte5); 55 | 56 | // Write payload. 57 | sstr.write(tmp.data(), static_cast(tmp.size())); 58 | 59 | dataToSend = sstr.str(); 60 | } 61 | return dataToSend; 62 | } 63 | 64 | /** 65 | * This method extracts an Envelope from the given istream that holds bytes in 66 | * format: 67 | * 68 | * 0x0D 0xA4 LEN0 LEN1 LEN2 Proto-encoded cluon::data::Envelope 69 | * 70 | * 0xA4 LEN0 LEN1 LEN2 are little Endian. 71 | * 72 | * @param in Stream to read from. 73 | * @return cluon::data::Envelope. 74 | */ 75 | inline std::pair extractEnvelope(std::istream &in) noexcept { 76 | bool retVal{false}; 77 | cluon::data::Envelope env; 78 | if (in.good()) { 79 | constexpr uint8_t OD4_HEADER_SIZE{5}; 80 | std::vector buffer; 81 | buffer.reserve(OD4_HEADER_SIZE); 82 | #ifdef WIN32 // LCOV_EXCL_LINE 83 | buffer.clear(); // LCOV_EXCL_LINE 84 | retVal = true; // LCOV_EXCL_LINE 85 | for (uint8_t i{0}; i < OD4_HEADER_SIZE; i++) { // LCOV_EXCL_LINE 86 | char c; // LCOV_EXCL_LINE 87 | in.get(c); // LCOV_EXCL_LINE 88 | retVal &= in.good(); // LCOV_EXCL_LINE 89 | buffer.push_back(c); // LCOV_EXCL_LINE 90 | } 91 | if (retVal) { // LCOV_EXCL_LINE 92 | #else // LCOV_EXCL_LINE 93 | in.read(&buffer[0], OD4_HEADER_SIZE); 94 | if (OD4_HEADER_SIZE == in.gcount()) { 95 | #endif 96 | if ((0x0D == static_cast(buffer[0])) && (0xA4 == static_cast(buffer[1]))) { 97 | const uint32_t LENGTH{le32toh(*reinterpret_cast(&buffer[1])) >> 8}; 98 | buffer.reserve(LENGTH); 99 | #ifdef WIN32 // LCOV_EXCL_LINE 100 | buffer.clear(); // LCOV_EXCL_LINE 101 | for (uint32_t i{0}; i < LENGTH; i++) { // LCOV_EXCL_LINE 102 | char c; // LCOV_EXCL_LINE 103 | in.get(c); // LCOV_EXCL_LINE 104 | retVal &= in.good(); // LCOV_EXCL_LINE 105 | buffer.push_back(c); // LCOV_EXCL_LINE 106 | } 107 | #else // LCOV_EXCL_LINE 108 | in.read(&buffer[0], static_cast(LENGTH)); 109 | retVal = static_cast(LENGTH) == in.gcount(); 110 | #endif 111 | if (retVal) { 112 | std::stringstream sstr(std::string(buffer.begin(), buffer.begin() + static_cast(LENGTH))); 113 | cluon::FromProtoVisitor protoDecoder; 114 | protoDecoder.decodeFrom(sstr, env); 115 | } 116 | } 117 | } 118 | } 119 | return std::make_pair(retVal, env); 120 | } 121 | 122 | /** 123 | * @return Extract a given Envelope's payload into the desired type. 124 | */ 125 | template 126 | inline T extractMessage(cluon::data::Envelope &&envelope) noexcept { 127 | cluon::FromProtoVisitor decoder; 128 | 129 | std::stringstream sstr(envelope.serializedData()); 130 | decoder.decodeFrom(sstr); 131 | 132 | T msg; 133 | msg.accept(decoder); 134 | 135 | return msg; 136 | } 137 | 138 | } // namespace cluon 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /libcluon/include/cluon/ToProtoVisitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_TOPROTOVISITOR_HPP 10 | #define CLUON_TOPROTOVISITOR_HPP 11 | 12 | #include "cluon/ProtoConstants.hpp" 13 | #include "cluon/cluon.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace cluon { 20 | /** 21 | This class encodes a given message in Proto format. 22 | */ 23 | class LIBCLUON_API ToProtoVisitor { 24 | private: 25 | ToProtoVisitor(const ToProtoVisitor &) = delete; 26 | ToProtoVisitor(ToProtoVisitor &&) = delete; 27 | ToProtoVisitor &operator=(const ToProtoVisitor &) = delete; 28 | ToProtoVisitor &operator=(ToProtoVisitor &&) = delete; 29 | 30 | public: 31 | ToProtoVisitor() = default; 32 | ~ToProtoVisitor() = default; 33 | 34 | /** 35 | * @return Encoded data in Proto format. 36 | */ 37 | std::string encodedData() const noexcept; 38 | 39 | public: 40 | // The following methods are provided to allow an instance of this class to 41 | // be used as visitor for an instance with the method signature void accept(T&); 42 | 43 | void preVisit(int32_t id, const std::string &shortName, const std::string &longName) noexcept; 44 | void postVisit() noexcept; 45 | 46 | void visit(uint32_t id, std::string &&typeName, std::string &&name, bool &v) noexcept; 47 | void visit(uint32_t id, std::string &&typeName, std::string &&name, char &v) noexcept; 48 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int8_t &v) noexcept; 49 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint8_t &v) noexcept; 50 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int16_t &v) noexcept; 51 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint16_t &v) noexcept; 52 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int32_t &v) noexcept; 53 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint32_t &v) noexcept; 54 | void visit(uint32_t id, std::string &&typeName, std::string &&name, int64_t &v) noexcept; 55 | void visit(uint32_t id, std::string &&typeName, std::string &&name, uint64_t &v) noexcept; 56 | void visit(uint32_t id, std::string &&typeName, std::string &&name, float &v) noexcept; 57 | void visit(uint32_t id, std::string &&typeName, std::string &&name, double &v) noexcept; 58 | void visit(uint32_t id, std::string &&typeName, std::string &&name, std::string &v) noexcept; 59 | 60 | template 61 | void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { 62 | (void)typeName; 63 | (void)name; 64 | 65 | toVarInt(m_buffer, std::move(encodeKey(id, static_cast(ProtoConstants::LENGTH_DELIMITED)))); 66 | cluon::ToProtoVisitor nestedProtoEncoder; 67 | value.accept(nestedProtoEncoder); 68 | encode(m_buffer, std::move(nestedProtoEncoder.encodedData())); 69 | } 70 | 71 | private: 72 | std::size_t encode(std::ostream &o, bool &v) noexcept; 73 | std::size_t encode(std::ostream &o, int8_t &v) noexcept; 74 | std::size_t encode(std::ostream &o, uint8_t &v) noexcept; 75 | std::size_t encode(std::ostream &o, int16_t &v) noexcept; 76 | std::size_t encode(std::ostream &o, uint16_t &v) noexcept; 77 | std::size_t encode(std::ostream &o, int32_t &v) noexcept; 78 | std::size_t encode(std::ostream &o, uint32_t &v) noexcept; 79 | std::size_t encode(std::ostream &o, int64_t &v) noexcept; 80 | std::size_t encode(std::ostream &o, uint64_t &v) noexcept; 81 | std::size_t encode(std::ostream &o, float &v) noexcept; 82 | std::size_t encode(std::ostream &o, double &v) noexcept; 83 | std::size_t encode(std::ostream &o, const std::string &v) noexcept; 84 | 85 | private: 86 | uint8_t toZigZag8(int8_t v) noexcept; 87 | uint16_t toZigZag16(int16_t v) noexcept; 88 | uint32_t toZigZag32(int32_t v) noexcept; 89 | uint64_t toZigZag64(int64_t v) noexcept; 90 | 91 | /** 92 | * This method encodes a given value in VarInt. 93 | * 94 | * @param out std::ostream to encode to. 95 | * @param v Value to encode. 96 | * @return Bytes written. 97 | */ 98 | std::size_t toVarInt(std::ostream &out, uint64_t v) noexcept; 99 | 100 | /** 101 | * This method creates a key/value pair encoded in Proto format. 102 | * 103 | * @param fieldIdentifier Field identifier. 104 | * @param v Value to encode. 105 | * @return Bytes written. 106 | */ 107 | template 108 | std::size_t toKeyValue(uint32_t fieldIdentifier, T &v) noexcept { 109 | std::size_t size{0}; 110 | uint64_t key = encodeKey(fieldIdentifier, static_cast(ProtoConstants::VARINT)); 111 | size += toVarInt(m_buffer, key); 112 | size += encode(m_buffer, v); 113 | return size; 114 | } 115 | 116 | /** 117 | * This method creates the key for the key/value pair in Protobuf format. 118 | * 119 | * @param fieldIdentifier Field identifier. 120 | * @param protoType Protobuf type identifier. 121 | * @return Protobuf fieldIdentifier/key pair. 122 | */ 123 | uint64_t encodeKey(uint32_t fieldIdentifier, uint8_t protoType) noexcept; 124 | 125 | private: 126 | std::stringstream m_buffer{""}; 127 | }; 128 | } // namespace cluon 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /python/OD4Session.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017-2018 Christian Berger, Julian Scholle 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | import time 9 | import signal 10 | import socket 11 | import struct 12 | import thread 13 | 14 | import cluonDataStructures_pb2 15 | 16 | # This class provides an OD4Session listener to receive live messages from a running OD4Session. 17 | class OD4Session: 18 | @staticmethod 19 | def run(): 20 | signal.pause() 21 | 22 | 23 | def __init__(self, cid, port=12175): 24 | assert cid <= 255 25 | self.MULTICAST_PORT = port 26 | self.MULTICAST_GROUP = "225.0.0." + str(cid) 27 | self.isRunning = False 28 | self.isConnected = False 29 | self.callbacks = dict() 30 | self.sock = None 31 | 32 | 33 | def connect(self): 34 | assert not self.isConnected 35 | # Create UDP multicast socket. 36 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 37 | self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 38 | self.sock.bind(('', self.MULTICAST_PORT)) 39 | req = struct.pack("4sl", socket.inet_aton(self.MULTICAST_GROUP), socket.INADDR_ANY) 40 | self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, req) 41 | self.isConnected = True 42 | if not self.isRunning: 43 | thread.start_new_thread(self.__runner, ()) 44 | self.isRunning = True 45 | 46 | 47 | def send(self, messageID, rawStringFromMessageToSend): 48 | now = time.time() 49 | 50 | sentTimeStamp = cluonDataStructures_pb2.cluon_data_TimeStamp() 51 | sentTimeStamp.seconds = int(now) 52 | sentTimeStamp.microseconds = int((now - int(now))*1000*1000) 53 | 54 | envelope = cluonDataStructures_pb2.cluon_data_Envelope() 55 | envelope.dataType = messageID 56 | envelope.sent.seconds = sentTimeStamp.seconds 57 | envelope.sent.microseconds = sentTimeStamp.microseconds 58 | envelope.sampleTimeStamp.seconds = sentTimeStamp.seconds 59 | envelope.sampleTimeStamp.microseconds = sentTimeStamp.microseconds 60 | envelope.serializedData = rawStringFromMessageToSend 61 | 62 | serializedEnvelope = envelope.SerializeToString() 63 | size = len(serializedEnvelope); 64 | 65 | # Add Envelope header. 66 | a = struct.pack("= expectedBytes: 103 | envelope = cluonDataStructures_pb2.cluon_data_Envelope() 104 | envelope.ParseFromString(buf) 105 | self.__process(envelope) 106 | # Start over and read next container. 107 | consumedEnvelopeHeader = False 108 | buf = buf[expectedBytes:] 109 | expectedBytes = 0 110 | 111 | if not consumedEnvelopeHeader: 112 | if len(buf) >= LENGTH_ENVELOPE_HEADER: 113 | consumedEnvelopeHeader = True 114 | byte0 = buf[0] 115 | byte1 = buf[1] 116 | 117 | # Check for Envelope header. 118 | if ord(byte0) == int('0x0D',16) and ord(byte1) == int('0xA4',16): 119 | v = struct.unpack('> 8 # The second byte belongs to the header of an Envelope. 121 | buf = buf[5:] # Remove header. 122 | else: 123 | print "Failed to consume header from Envelope." 124 | 125 | # Receive data from UDP socket. 126 | data = self.sock.recv(2048) 127 | if len(data) != 2048: 128 | # Avoid corrupt packets. 129 | buf = buf + data 130 | 131 | -------------------------------------------------------------------------------- /libcluon/src/OD4Session.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "cluon/OD4Session.hpp" 10 | #include "cluon/Envelope.hpp" 11 | #include "cluon/FromProtoVisitor.hpp" 12 | #include "cluon/TerminateHandler.hpp" 13 | #include "cluon/Time.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace cluon { 20 | 21 | OD4Session::OD4Session(uint16_t CID, std::function delegate) noexcept 22 | : m_receiver{nullptr} 23 | , m_sender{"225.0.0." + std::to_string(CID), 12175} 24 | , m_delegate(std::move(delegate)) 25 | , m_mapOfDataTriggeredDelegatesMutex{} 26 | , m_mapOfDataTriggeredDelegates{} { 27 | m_receiver = std::make_unique( 28 | "225.0.0." + std::to_string(CID), 29 | 12175, 30 | [this](std::string &&data, std::string &&from, std::chrono::system_clock::time_point &&timepoint) { 31 | this->callback(std::move(data), std::move(from), std::move(timepoint)); 32 | }, 33 | m_sender.getSendFromPort() /* passing our local send from port to the UDPReceiver to filter out our own bytes */); 34 | } 35 | 36 | void OD4Session::timeTrigger(float freq, std::function delegate) noexcept { 37 | if (nullptr != delegate) { 38 | bool delegateIsRunning{true}; 39 | const int64_t TIME_SLICE_IN_MILLISECONDS{static_cast(1000 / ((freq > 0) ? freq : 1.0f))}; 40 | do { 41 | cluon::data::TimeStamp before{cluon::time::now()}; 42 | try { 43 | delegateIsRunning = delegate(); 44 | } catch (...) { 45 | delegateIsRunning = false; // delegate threw exception. 46 | } 47 | cluon::data::TimeStamp after{cluon::time::now()}; 48 | 49 | const int64_t beforeInMicroseconds{cluon::time::toMicroseconds(before)}; 50 | const int64_t afterInMicroseconds{cluon::time::toMicroseconds(after)}; 51 | 52 | const int64_t timeSpent{(afterInMicroseconds > beforeInMicroseconds) ? (afterInMicroseconds - beforeInMicroseconds) / 1000 : 0}; 53 | const int64_t timeToSleepInMilliseconds{TIME_SLICE_IN_MILLISECONDS - timeSpent}; 54 | 55 | // Sleep the remaining time. 56 | if ((timeToSleepInMilliseconds > 0) && (timeToSleepInMilliseconds <= TIME_SLICE_IN_MILLISECONDS)) { 57 | std::this_thread::sleep_for(std::chrono::duration(timeToSleepInMilliseconds)); 58 | } else { 59 | std::cerr << "[cluon::OD4Session]: time-triggered delegate violated allocated time slice." << std::endl; 60 | } 61 | } while (delegateIsRunning && !TerminateHandler::instance().isTerminated.load()); 62 | } 63 | } 64 | 65 | bool OD4Session::dataTrigger(int32_t messageIdentifier, std::function delegate) noexcept { 66 | bool retVal{false}; 67 | if (nullptr == m_delegate) { 68 | try { 69 | std::lock_guard lck{m_mapOfDataTriggeredDelegatesMutex}; 70 | if ((nullptr == delegate) && (m_mapOfDataTriggeredDelegates.count(messageIdentifier) > 0)) { 71 | auto element = m_mapOfDataTriggeredDelegates.find(messageIdentifier); 72 | if (element != m_mapOfDataTriggeredDelegates.end()) { 73 | m_mapOfDataTriggeredDelegates.erase(element); 74 | } 75 | } else { 76 | m_mapOfDataTriggeredDelegates[messageIdentifier] = delegate; 77 | } 78 | retVal = true; 79 | } catch (...) {} // LCOV_EXCL_LINE 80 | } 81 | return retVal; 82 | } 83 | 84 | void OD4Session::callback(std::string &&data, std::string && /*from*/, std::chrono::system_clock::time_point &&timepoint) noexcept { 85 | size_t numberOfDataTriggeredDelegates{0}; 86 | { 87 | try { 88 | std::lock_guard lck{m_mapOfDataTriggeredDelegatesMutex}; 89 | numberOfDataTriggeredDelegates = m_mapOfDataTriggeredDelegates.size(); 90 | } catch (...) {} // LCOV_EXCL_LINE 91 | } 92 | // Only unpack the envelope when it needs to be post-processed. 93 | if ((nullptr != m_delegate) || (0 < numberOfDataTriggeredDelegates)) { 94 | std::stringstream sstr(data); 95 | auto retVal = extractEnvelope(sstr); 96 | 97 | if (retVal.first) { 98 | cluon::data::Envelope env{retVal.second}; 99 | env.received(cluon::time::convert(timepoint)); 100 | 101 | // "Catch all"-delegate. 102 | if (nullptr != m_delegate) { 103 | m_delegate(std::move(env)); 104 | } else { 105 | try { 106 | // Data triggered-delegates. 107 | std::lock_guard lck{m_mapOfDataTriggeredDelegatesMutex}; 108 | if (m_mapOfDataTriggeredDelegates.count(env.dataType()) > 0) { 109 | m_mapOfDataTriggeredDelegates[env.dataType()](std::move(env)); 110 | } 111 | } catch (...) {} // LCOV_EXCL_LINE 112 | } 113 | } 114 | } 115 | } 116 | 117 | void OD4Session::send(cluon::data::Envelope &&envelope) noexcept { 118 | sendInternal(cluon::serializeEnvelope(std::move(envelope))); 119 | } 120 | 121 | void OD4Session::sendInternal(std::string &&dataToSend) noexcept { 122 | m_sender.send(std::move(dataToSend)); 123 | } 124 | 125 | bool OD4Session::isRunning() noexcept { 126 | return m_receiver->isRunning(); 127 | } 128 | 129 | } // namespace cluon 130 | -------------------------------------------------------------------------------- /libcluon/include/cluon/SharedMemory.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef CLUON_SHAREDMEMORY_HPP 10 | #define CLUON_SHAREDMEMORY_HPP 11 | 12 | #include "cluon/cluon.hpp" 13 | #include "cluon/cluonDataStructures.hpp" 14 | 15 | // clang-format off 16 | #ifdef WIN32 17 | #include 18 | #else 19 | #include 20 | #include 21 | #endif 22 | // clang-format on 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace cluon { 31 | 32 | class LIBCLUON_API SharedMemory { 33 | private: 34 | SharedMemory(const SharedMemory &) = delete; 35 | SharedMemory(SharedMemory &&) = delete; 36 | SharedMemory &operator=(const SharedMemory &) = delete; 37 | SharedMemory &operator=(SharedMemory &&) = delete; 38 | 39 | public: 40 | /** 41 | * Constructor. 42 | * 43 | * @param name Name of the shared memory area; must start with / and must not 44 | * be longer than NAME_MAX (255) on POSIX or PATH_MAX on WIN32. If the name 45 | * is missing a leading '/' or is longer than 255, it will be adjusted accordingly. 46 | * @param size of the shared memory area to create; if size is 0, the class tries to attach to an existing area. 47 | */ 48 | SharedMemory(const std::string &name, uint32_t size = 0) noexcept; 49 | ~SharedMemory() noexcept; 50 | 51 | /** 52 | * @return true when this shared memory area is locked. 53 | */ 54 | bool isLocked() const noexcept; 55 | 56 | /** 57 | * This method locks the shared memory area. 58 | */ 59 | void lock() noexcept; 60 | 61 | /** 62 | * This method unlocks the shared memory area. 63 | */ 64 | void unlock() noexcept; 65 | 66 | /** 67 | * This method waits for being notified from the shared condition. 68 | */ 69 | void wait() noexcept; 70 | 71 | /** 72 | * This method notifies all threads waiting on the shared condition. 73 | */ 74 | void notifyAll() noexcept; 75 | 76 | /** 77 | * This method sets the time stamp that can be used to 78 | * express the sample time stamp of the data in residing 79 | * in the shared memory. 80 | * 81 | * This method is only allowed when the shared memory is locked. 82 | * 83 | * @param ts TimeStamp. 84 | * @return true if the timestamp could set; false if the shared memory was not locked. 85 | */ 86 | bool setTimeStamp(const cluon::data::TimeStamp &ts) noexcept; 87 | 88 | /** 89 | * This method returns the sample time stamp. 90 | * 91 | * This method is only allowed when the shared memory is locked. 92 | * 93 | * @return (true, sample time stamp) or (false, 0) in case if the shared memory was not locked. 94 | */ 95 | std::pair getTimeStamp() noexcept; 96 | 97 | public: 98 | /** 99 | * @return True if the shared memory area is existing and usable. 100 | */ 101 | bool valid() noexcept; 102 | 103 | /** 104 | * @return Pointer to the raw shared memory or nullptr in case of invalid shared memory. 105 | */ 106 | char *data() noexcept; 107 | 108 | /** 109 | * @return The size of the shared memory area. 110 | */ 111 | uint32_t size() const noexcept; 112 | 113 | /** 114 | * @return Name the shared memory area. 115 | */ 116 | const std::string name() const noexcept; 117 | 118 | #ifdef WIN32 119 | private: 120 | void initWIN32() noexcept; 121 | void deinitWIN32() noexcept; 122 | void lockWIN32() noexcept; 123 | void unlockWIN32() noexcept; 124 | void waitWIN32() noexcept; 125 | void notifyAllWIN32() noexcept; 126 | #else 127 | private: 128 | void initPOSIX() noexcept; 129 | void deinitPOSIX() noexcept; 130 | void lockPOSIX() noexcept; 131 | void unlockPOSIX() noexcept; 132 | void waitPOSIX() noexcept; 133 | void notifyAllPOSIX() noexcept; 134 | bool validPOSIX() noexcept; 135 | 136 | void initSysV() noexcept; 137 | void deinitSysV() noexcept; 138 | void lockSysV() noexcept; 139 | void unlockSysV() noexcept; 140 | void waitSysV() noexcept; 141 | void notifyAllSysV() noexcept; 142 | bool validSysV() noexcept; 143 | #endif 144 | 145 | private: 146 | std::string m_name{""}; 147 | std::string m_nameForTimeStamping{""}; 148 | uint32_t m_size{0}; 149 | char *m_sharedMemory{nullptr}; 150 | char *m_userAccessibleSharedMemory{nullptr}; 151 | bool m_hasOnlyAttachedToSharedMemory{false}; 152 | 153 | std::atomic m_broken{false}; 154 | std::atomic m_isLocked{false}; 155 | 156 | #ifdef WIN32 157 | HANDLE __conditionEvent{nullptr}; 158 | HANDLE __mutex{nullptr}; 159 | HANDLE __sharedMemory{nullptr}; 160 | #else 161 | int32_t m_fdForTimeStamping{-1}; 162 | 163 | bool m_usePOSIX{true}; 164 | 165 | // Member fields for POSIX-based shared memory. 166 | #if !defined(__NetBSD__) && !defined(__OpenBSD__) 167 | int32_t m_fd{-1}; 168 | struct SharedMemoryHeader { 169 | uint32_t __size; 170 | pthread_mutex_t __mutex; 171 | pthread_cond_t __condition; 172 | }; 173 | SharedMemoryHeader *m_sharedMemoryHeader{nullptr}; 174 | #endif 175 | 176 | // Member fields for SysV-based shared memory. 177 | key_t m_shmKeySysV{0}; 178 | key_t m_mutexKeySysV{0}; 179 | key_t m_conditionKeySysV{0}; 180 | 181 | int m_sharedMemoryIDSysV{-1}; 182 | int m_mutexIDSysV{-1}; 183 | int m_conditionIDSysV{-1}; 184 | #endif 185 | }; 186 | } // namespace cluon 187 | 188 | #endif 189 | -------------------------------------------------------------------------------- /libcluon/testsuites/TestUDPReceiver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2018 Christian Berger 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include "catch.hpp" 10 | 11 | #include "cluon/UDPReceiver.hpp" 12 | #include "cluon/UDPSender.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | TEST_CASE("Creating UDPReceiver and stop immediately.") { 24 | cluon::UDPReceiver ur1{"127.0.0.1", 1234, nullptr}; 25 | REQUIRE(ur1.isRunning()); 26 | } 27 | 28 | TEST_CASE("Trying to send data with empty sendToAddress.") { 29 | cluon::UDPReceiver failingUR1{"", 1235, nullptr}; 30 | REQUIRE(!failingUR1.isRunning()); 31 | } 32 | 33 | TEST_CASE("Trying to receive with wrong sendToPort.") { 34 | cluon::UDPReceiver failingUR2{"127.0.0.1", 0, nullptr}; 35 | REQUIRE(!failingUR2.isRunning()); 36 | } 37 | 38 | TEST_CASE("Trying to send data with incomplete sendToAddress.") { 39 | cluon::UDPReceiver failingUR3{"127.0.0", 1236, nullptr}; 40 | REQUIRE(!failingUR3.isRunning()); 41 | } 42 | 43 | TEST_CASE("Trying to send data with wrong sendToAddress.") { 44 | cluon::UDPReceiver failingUR3_2{"127.0.0.256", 1233, nullptr}; 45 | REQUIRE(!failingUR3_2.isRunning()); 46 | } 47 | 48 | TEST_CASE("Creating UDPReceiver and receive data.") { 49 | auto before = std::chrono::system_clock::now(); 50 | 51 | // Setup data structures to receive data from UDPReceiver. 52 | std::atomic hasDataReceived{false}; 53 | std::string data; 54 | std::string sender; 55 | std::chrono::system_clock::time_point timestamp; 56 | 57 | REQUIRE(data.empty()); 58 | REQUIRE(sender.empty()); 59 | REQUIRE(!hasDataReceived); 60 | 61 | cluon::UDPReceiver ur3( 62 | "127.0.0.1", 63 | 1237, 64 | [&hasDataReceived, &data, &sender, ×tamp ](std::string && d, std::string && s, std::chrono::system_clock::time_point && ts) noexcept { 65 | data = std::move(d); 66 | sender = std::move(s); 67 | timestamp = ts; 68 | hasDataReceived.store(true); 69 | std::time_t tp = std::chrono::system_clock::to_time_t(timestamp); 70 | std::cout << "Received '" << data << "' at '" << tp << "" 71 | << "'" << std::endl; 72 | }); 73 | 74 | REQUIRE(ur3.isRunning()); 75 | 76 | cluon::UDPSender us2{"127.0.0.1", 1237}; 77 | std::string TEST_DATA{"Hello World"}; 78 | const auto TEST_DATA_SIZE{TEST_DATA.size()}; 79 | auto retVal2 = us2.send(std::move(TEST_DATA)); 80 | REQUIRE(TEST_DATA_SIZE == static_cast(retVal2.first)); 81 | REQUIRE(0 == retVal2.second); 82 | 83 | // Yield the UDP receiver so that the embedded thread has time to process the data. 84 | using namespace std::literals::chrono_literals; // NOLINT 85 | do { std::this_thread::sleep_for(1ms); } while (!hasDataReceived.load()); 86 | 87 | REQUIRE(hasDataReceived); 88 | REQUIRE("Hello World" == data); 89 | REQUIRE(sender.substr(0, sender.find(':')) == "127.0.0.1"); 90 | 91 | auto after = std::chrono::system_clock::now(); 92 | 93 | // Test if the timestamping works correctly (only on 64bit systems). 94 | if (8 == sizeof(void *)) { 95 | REQUIRE(before < timestamp); 96 | REQUIRE(timestamp < after); 97 | } 98 | } 99 | 100 | TEST_CASE("Testing multicast with 226.x.y.z address.") { 101 | // Setup data structures to receive data from UDPReceiver. 102 | std::atomic hasDataReceived{false}; 103 | std::string data; 104 | 105 | REQUIRE(data.empty()); 106 | REQUIRE(!hasDataReceived); 107 | 108 | cluon::UDPReceiver ur4( 109 | "226.0.0.226", 1238, [&hasDataReceived, &data ](std::string && d, std::string &&, std::chrono::system_clock::time_point &&) noexcept { 110 | data = std::move(d); 111 | hasDataReceived.store(true); 112 | }); 113 | REQUIRE(ur4.isRunning()); 114 | 115 | // Setup UDPSender. 116 | cluon::UDPSender us4{"226.0.0.226", 1238}; 117 | std::string TEST_DATA{"Hello Multicast Group"}; 118 | const auto TEST_DATA_SIZE{TEST_DATA.size()}; 119 | auto retVal4 = us4.send(std::move(TEST_DATA)); 120 | REQUIRE(TEST_DATA_SIZE == static_cast(retVal4.first)); 121 | REQUIRE(0 == retVal4.second); 122 | 123 | // Yield the UDP receiver so that the embedded thread has time to process the data. 124 | do { std::this_thread::yield(); } while (!hasDataReceived.load()); 125 | 126 | REQUIRE(hasDataReceived); 127 | REQUIRE("Hello Multicast Group" == data); 128 | } 129 | 130 | TEST_CASE("Testing multicast with faulty 224.0.0.1 address.") { 131 | // Setup data structures to receive data from UDPReceiver. 132 | std::atomic hasDataReceived{false}; 133 | 134 | REQUIRE(!hasDataReceived); 135 | 136 | // Setup UDPReceiver. 137 | cluon::UDPReceiver ur5{"224.0.0.1", 1239, nullptr}; 138 | REQUIRE(!ur5.isRunning()); 139 | REQUIRE(!hasDataReceived); 140 | } 141 | 142 | #ifndef WIN32 143 | #ifndef __APPLE__ 144 | TEST_CASE("Testing broadcast with 255.255.255.255 address.") { 145 | // Setup data structures to receive data from UDPReceiver. 146 | std::atomic hasDataReceived{false}; 147 | std::string data; 148 | 149 | REQUIRE(data.empty()); 150 | REQUIRE(!hasDataReceived); 151 | 152 | cluon::UDPReceiver ur6( 153 | "255.255.255.255", 1239, [&hasDataReceived, &data ](std::string && d, std::string &&, std::chrono::system_clock::time_point &&) noexcept { 154 | data = std::move(d); 155 | hasDataReceived.store(true); 156 | }); 157 | REQUIRE(ur6.isRunning()); 158 | 159 | // Setup UDPSender. 160 | cluon::UDPSender us6{"255.255.255.255", 1239}; 161 | std::string TEST_DATA{"Hello Broadcast"}; 162 | const auto TEST_DATA_SIZE{TEST_DATA.size()}; 163 | auto retVal6 = us6.send(std::move(TEST_DATA)); 164 | REQUIRE(TEST_DATA_SIZE == static_cast(retVal6.first)); 165 | REQUIRE(0 == retVal6.second); 166 | 167 | // Yield the UDP receiver so that the embedded thread has time to process the data. 168 | do { std::this_thread::yield(); } while (!hasDataReceived.load()); 169 | 170 | REQUIRE(hasDataReceived); 171 | REQUIRE("Hello Broadcast" == data); 172 | } 173 | #endif 174 | #endif 175 | --------------------------------------------------------------------------------