diff --git a/.gitignore b/.gitignore index 2f4074d..7aaac2b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ *.log examples/vs2015/bin/ examples/vs2015/build/ -.vs/ \ No newline at end of file +.vs/ +*.gch +build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5846814 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "examples/cmake/CppSockets"] + path = examples/cmake/CppSockets + url = https://github.com/simondlevy/CppSockets.git diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0130191 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "g++-11 - Build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "lldb", + // "preLaunchTask": "C/C++: g++-11 build active file" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5bedcc4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "functional": "cpp", + "iostream": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..238b42d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++-11 build active file", + "command": "/usr/local/bin/g++-11", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..24a3712 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) +project(PSN) + +add_subdirectory(examples/cmake) + +add_library(psnlib INTERFACE) +target_include_directories(psnlib INTERFACE ./include) diff --git a/README.md b/README.md index 1cd359b..bbeed67 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,41 @@ This package contains the documentation and a C++ implementation of the protocol The implementation only uses ".hpp" files. If you want to use this implementation in your project, you need to include the file "psn_lib.hpp" in one of your ".cpp" file. -The implementation is cross-platform so it should compile under Windows, Linux and Mac OS X. In revenge, the example projects will only work under a Windows operating system as they use a Windows specific implementation of a simple udp_socket class. If you want to run these demo on another platform, you will need to write your own udp_socket class. +The implementation is cross-platform so it should compile under Windows, Linux and Mac OS X. For any question about the protocol, please write at **info@posistage.net** + +## CMake compilation +You can compile the library and the examples using CMake (for Windows, MacOS and Linux) +- `git submodule update --init --recursive` (fetches the CppSockets library from https://github.com/simondlevy/CppSockets - only required for the examples to work; otherwise bring your own socket library) +- `mkdir build` +- `cd build` +- `cmake ..` +- `cmake --build .` + +Now you should be example to run examples as follows: +- `./examples/cmake/send_example` : encode and send PSN messages using UDP ("PSN Server") +- `./examples/cmake/receive_example` : receive PSN messages via UDP and decode ("PSN Client") + +## Installing this library using CMake +To install this library in your own CMake-based project, you could use a `CMakeList.txt` similar to the following, assuming you copied or cloned psn-cpp to a directory named `./libs/psn-cpp` relative to the project root: +``` +cmake_minimum_required(VERSION 3.19) + +project(my_project) + +# --- Optional: add CppSockets library if you need it +add_library(cppsockets INTERFACE) +set_property(TARGET cppsockets PROPERTY CXX_STANDARD 11) +target_include_directories(cppsockets INTERFACE ./libs/CppSockets) +# ---- + +add_executable(my_executable src/my_executable.cpp) +set_property(TARGET my_executable PROPERTY CXX_STANDARD 11) + +add_subdirectory(./libs/psn-cpp) + +target_include_directories(my_executable PUBLIC ./libs/tether/base_agent/cpp/src) + +target_link_libraries(my_executable PUBLIC psnlib cppsockets) +``` diff --git a/examples/cmake/.gitignore b/examples/cmake/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/examples/cmake/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/examples/cmake/CMakeLists.txt b/examples/cmake/CMakeLists.txt new file mode 100644 index 0000000..9b7e4ee --- /dev/null +++ b/examples/cmake/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.19) +project (psn_cpp_examples) + +add_executable(send_example psn_server.cpp) +add_executable(receive_example psn_client.cpp) + +set_property(TARGET send_example PROPERTY CXX_STANDARD 11) +set_property(TARGET receive_example PROPERTY CXX_STANDARD 11) + +target_include_directories(send_example PUBLIC ../../include) +target_include_directories(send_example PUBLIC ./CppSockets) + +target_include_directories(receive_example PUBLIC ../../include) +target_include_directories(receive_example PUBLIC ./CppSockets) \ No newline at end of file diff --git a/examples/cmake/CppSockets b/examples/cmake/CppSockets new file mode 160000 index 0000000..19d7c99 --- /dev/null +++ b/examples/cmake/CppSockets @@ -0,0 +1 @@ +Subproject commit 19d7c9993563924ec5fe3b5742f0f4b37ef160d0 diff --git a/examples/cmake/psn_client.cpp b/examples/cmake/psn_client.cpp new file mode 100644 index 0000000..1c5bd50 --- /dev/null +++ b/examples/cmake/psn_client.cpp @@ -0,0 +1,161 @@ +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 VYV Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +**/ +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include +#include "UdpServerSocket.hpp" + +#include + +#include +#include +#include +#include + +const short PORT = 8888; +static const short BUFLEN = 1024; +static const uint32_t TIMEOUT_MSEC = 1000; + +int main( void ) +{ + char buf[BUFLEN]; + // wsa_session session ; + + +//==================================================== +// Init client + + UdpServerSocket server(PORT, TIMEOUT_MSEC); + // udp_socket socket_client ; + // socket_client.bind( ::psn::DEFAULT_UDP_PORT ) ; + // socket_client.join_multicast_group( ::psn::DEFAULT_UDP_MULTICAST_ADDR ) ; + + ::psn::psn_decoder psn_decoder ; + uint8_t last_frame_id = 0 ; + int skip_cout = 0 ; + + //==================================================== + // Main loop + while ( 1 ) + { + // Sleep( 1 ) ; + // sleep(1); + std::this_thread::sleep_for(std::chrono::milliseconds(1) ); + + + + printf("Waiting for data..."); + fflush(stdout); + + memset(buf, 0, BUFLEN); + + *buf = 0; + + server.receiveData(buf, BUFLEN); + + if (*buf) { + printf("Data: %s\n", buf); + // server.sendData(buf, strlen(buf)); + + // std::cout << "As string: " << msg << " with size " << msg.size() << std::endl; + + psn_decoder.decode( buf , BUFLEN ) ; + + // if ( psn_decoder.get_data().header.frame_id != last_frame_id ) + // { + last_frame_id = psn_decoder.get_data().header.frame_id ; + + const ::psn::tracker_map & recv_trackers = psn_decoder.get_data().trackers ; + + // if ( skip_cout++ % 20 == 0 ) + // { + ::std::cout << "--------------------PSN CLIENT-----------------" << ::std::endl ; + ::std::cout << "System Name: " << psn_decoder.get_info().system_name << ::std::endl ; + ::std::cout << "Frame Id: " << (int)last_frame_id << ::std::endl ; + ::std::cout << "Frame Timestamp: " << psn_decoder.get_data().header.timestamp_usec << ::std::endl ; + ::std::cout << "Tracker count: " << recv_trackers.size() << ::std::endl ; + + for ( auto it = recv_trackers.begin() ; it != recv_trackers.end() ; ++it ) + { + const ::psn::tracker & tracker = it->second ; + + ::std::cout << "Tracker - id: " << tracker.get_id() << " | name: " << tracker.get_name() << ::std::endl ; + + if ( tracker.is_pos_set() ) + ::std::cout << " pos: " << tracker.get_pos().x << ", " << + tracker.get_pos().y << ", " << + tracker.get_pos().z << std::endl ; + + if ( tracker.is_speed_set() ) + ::std::cout << " speed: " << tracker.get_speed().x << ", " << + tracker.get_speed().y << ", " << + tracker.get_speed().z << std::endl ; + + if ( tracker.is_ori_set() ) + ::std::cout << " ori: " << tracker.get_ori().x << ", " << + tracker.get_ori().y << ", " << + tracker.get_ori().z << std::endl ; + + if ( tracker.is_status_set() ) + ::std::cout << " status: " << tracker.get_status() << std::endl ; + + if ( tracker.is_accel_set() ) + ::std::cout << " accel: " << tracker.get_accel().x << ", " << + tracker.get_accel().y << ", " << + tracker.get_accel().z << std::endl ; + + if ( tracker.is_target_pos_set() ) + ::std::cout << " target pos: " << tracker.get_target_pos().x << ", " << + tracker.get_target_pos().y << ", " << + tracker.get_target_pos().z << std::endl ; + + if ( tracker.is_timestamp_set() ) + ::std::cout << " timestamp: " << tracker.get_timestamp() << std::endl ; + } + + ::std::cout << "-----------------------------------------------" << ::std::endl ; + //} // skip + + } + + else { + printf("\n"); + // sleep(1); + std::this_thread::sleep_for(std::chrono::milliseconds(1) ); + } + + + + // if ( socket_client.receive_message( msg , ::psn::MAX_UDP_PACKET_SIZE ) ) + // { + + + // } + } + + return 0; +} + + diff --git a/examples/cmake/psn_server.cpp b/examples/cmake/psn_server.cpp new file mode 100644 index 0000000..fe8cbe8 --- /dev/null +++ b/examples/cmake/psn_server.cpp @@ -0,0 +1,164 @@ +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 VYV Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +**/ +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include "psn_lib.hpp" +#include "UdpClientSocket.hpp" + +#include +#include +#include +#include +#include +#include +#include + +const char * HOST = "127.0.0.1"; +const short PORT = 8888; + +int main( void ) +{ + // wsa_session session ; + + //==================================================== + // Init server + // udp_socket socket_server ; + // socket_server.enable_send_message_multicast() ; + + UdpClientSocket client(HOST, PORT); + + ::psn::psn_encoder psn_encoder( "Test PSN Server" ) ; + + ::psn::tracker_map trackers ; + int i = 0 ; + trackers[ i ] = ::psn::tracker( i++ , "Sun" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Mercury" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Venus" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Earth" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Mars" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Jupiter" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Saturn" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Uranus" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Neptune" ) ; + trackers[ i ] = ::psn::tracker( i++ , "Pluto" ) ; + + // Planets orbit in days + float orbits[ 10 ] = { 1.0f , 88.0f , 224.7f , 365.2f , 687.0f , 4332.0f , 10760.0f , 30700.0f , 60200.0f , 90600.0f } ; + // Planets distance from sun in million km x 100 + float dist_from_sun[ 10 ] = { 0.0f , 0.58f , 1.08f , 1.50f , 2.28f , 7.78f , 14.29f , 28.71f , 45.04f , 59.13f } ; + + uint64_t timestamp = 0 ; + + //==================================================== + // Main loop + while ( true ) + { + // Update trackers + for ( int i = 1 ; i < 10 ; ++i ) // do not update the sun + { + float a = 1.0f / orbits[ i ] ; + float b = dist_from_sun[ i ] ; + float x = (float)timestamp ; + float cb = ::std::cosf( a * x ) * b ; + float sb = ::std::sinf( a * x ) * b ; + trackers[ i ].set_pos( ::psn::float3( sb , 0 , cb ) ) ; + trackers[ i ].set_speed( ::psn::float3( a * cb , 0 , -a * sb ) ) ; + trackers[ i ].set_ori( ::psn::float3( 0 , x / 1000.0f , 0 ) ) ; + trackers[ i ].set_accel( ::psn::float3( ::psn::float3( -a * a * sb , 0 , -a * a * cb ) ) ) ; + trackers[ i ].set_target_pos( ::psn::float3( 3 , 14 , 16 ) ) ; + trackers[ i ].set_status( i / 10.0f ) ; + trackers[ i ].set_timestamp( timestamp ) ; + } + + // Send data + if ( timestamp % 16 == 0 ) // transmit data at 60 Hz approx. + { + std::cout << "timestamp = " << timestamp << std::endl; + + ::std::list< ::std::string > data_packets = psn_encoder.encode_data( trackers , timestamp ) ; + + ::std::cout << "--------------------PSN SERVER-----------------" << ::std::endl ; + ::std::cout << "Sending PSN_DATA_PACKET : " + << "Frame Id = " << (int)psn_encoder.get_last_data_frame_id() << std::endl + << "Tracker count = " << (int)trackers.size() + << " , Packet Count = " << data_packets.size() << ::std::endl ; + + for ( auto it = data_packets.begin() ; it != data_packets.end() ; ++it ) + { + // Uncomment these lines if you want to simulate a packet drop now and then + /*static uint64_t packet_drop = 0 ; + if ( packet_drop++ % 100 != 0 )*/ + + + // socket_server.send_message( ::psn::DEFAULT_UDP_MULTICAST_ADDR , ::psn::DEFAULT_UDP_PORT , *it ) ; + + std::cout << "Send:" << it->c_str() << " of length " << it->length() << std::endl; + + // void* buf; + // std::string s (it->c_str()); + // std::cout << "Converted? " << s << std::endl; + // buf = static_cast(&s); + // client.sendData(buf, it->length()); + + + char buf[it->length()]; + for (int i = 0; i < it->length(); i++) { + // std::cout << "#" << i << " : " << (uint)it->c_str()[i] << std::endl; + buf[i] = it->c_str()[i]; + } + + + client.sendData(buf, it->length()); + + } + + ::std::cout << "-----------------------------------------------" << ::std::endl ; + } + + // Send Info + if ( timestamp % 1000 == 0 ) // transmit info at 1 Hz approx. + { + ::std::list< ::std::string > info_packets = psn_encoder.encode_info( trackers , timestamp ) ; + + ::std::cout << "--------------------PSN SERVER-----------------" << ::std::endl ; + ::std::cout << "Sending PSN_INFO_PACKET : " + << "Frame Id = " << (int)psn_encoder.get_last_info_frame_id() + << " , Packet Count = " << info_packets.size() << ::std::endl ; + + for ( auto it = info_packets.begin() ; it != info_packets.end() ; ++it ) + // TODO: send info packet using client.sendData + // socket_server.send_message( ::psn::DEFAULT_UDP_MULTICAST_ADDR , ::psn::DEFAULT_UDP_PORT , *it ) ; + + + ::std::cout << "-----------------------------------------------" << ::std::endl ; + } + + // sleep( 1 ) ; + std::this_thread::sleep_for(std::chrono::milliseconds(1) ); + timestamp++ ; + } + + return 0; +} \ No newline at end of file