From 50335c4a5cf1dea3a5c4ddaa6f71973289158e27 Mon Sep 17 00:00:00 2001 From: Jan Chaloupecky Date: Thu, 14 Mar 2019 14:49:43 +0000 Subject: [PATCH] Added UDP multicast for plugin discovery (#153) * Added Timer * Added UDPSocket::GetAddr * Added MessageHandlers::SendBeacon() * PoC of starting a timer on PluginEnable * C++11 * Added Timer to xcode project * C++11 in cmake * added Timer to cmake * use function pointer in Timer * moved Timer to namespace + wait for thread to join when stopping the timer * Windows: changed uint to unisgned short * Windows: Added Timer.h/cpp to project * GetAddr static * Send xplane and plugin version with BECN * SendBeacon with params * fixed file copyrights * Include functional to fix Linux compile error * review fixes * Send plugin receive port in BECN * review fixes --- xpcPlugin/CMakeLists.txt | 3 ++ xpcPlugin/MessageHandlers.cpp | 27 +++++++++++++++++ xpcPlugin/MessageHandlers.h | 2 ++ xpcPlugin/Timer.cpp | 27 +++++++++++++++++ xpcPlugin/Timer.h | 29 +++++++++++++++++++ xpcPlugin/UDPSocket.cpp | 12 ++++++++ xpcPlugin/UDPSocket.h | 4 ++- xpcPlugin/XPCPlugin.cpp | 28 ++++++++++++++++-- xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj | 10 +++++-- xpcPlugin/xpcPlugin/xpcPlugin.vcxproj | 2 ++ 10 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 xpcPlugin/Timer.cpp create mode 100644 xpcPlugin/Timer.h diff --git a/xpcPlugin/CMakeLists.txt b/xpcPlugin/CMakeLists.txt index 53b2bcf..69f2db6 100644 --- a/xpcPlugin/CMakeLists.txt +++ b/xpcPlugin/CMakeLists.txt @@ -10,6 +10,7 @@ add_definitions(-DXPLM200 -DLIN=1) SET(CMAKE_C_COMPILER gcc) SET(CMAKE_CXX_COMPILER g++) +SET(CMAKE_CXX_STANDARD 11) add_library(xpc64 SHARED XPCPlugin.cpp DataManager.cpp @@ -17,6 +18,7 @@ add_library(xpc64 SHARED XPCPlugin.cpp Log.cpp Message.cpp MessageHandlers.cpp + Timer.cpp UDPSocket.cpp) set_target_properties(xpc64 PROPERTIES PREFIX "" SUFFIX ".xpl") set_target_properties(xpc64 PROPERTIES COMPILE_FLAGS "-m64 -fno-stack-protector" LINK_FLAGS "-shared -rdynamic -nodefaultlibs -undefined_warning -m64 -fno-stack-protector") @@ -27,6 +29,7 @@ add_library(xpc32 SHARED XPCPlugin.cpp Log.cpp Message.cpp MessageHandlers.cpp + Timer.cpp UDPSocket.cpp) set_target_properties(xpc32 PROPERTIES PREFIX "" SUFFIX ".xpl") set_target_properties(xpc32 PROPERTIES COMPILE_FLAGS "-m32 -fno-stack-protector" LINK_FLAGS "-shared -rdynamic -nodefaultlibs -undefined_warning -m32 -fno-stack-protector") diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index 4808c7a..1cb0c18 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -26,6 +26,9 @@ #include #include +#define MULTICAST_GROUP "239.255.1.1" +#define MULITCAST_PORT 49710 + namespace XPC { @@ -35,6 +38,8 @@ namespace XPC std::string MessageHandlers::connectionKey; MessageHandlers::ConnectionInfo MessageHandlers::connection; UDPSocket* MessageHandlers::sock; + + static sockaddr multicast_address = UDPSocket::GetAddr(MULTICAST_GROUP, MULITCAST_PORT); void MessageHandlers::SetSocket(UDPSocket* socket) { @@ -130,6 +135,28 @@ namespace XPC MessageHandlers::HandleUnknown(msg); } } + + void MessageHandlers::SendBeacon(const std::string& pluginVersion, unsigned short pluginReceivePort, int xplaneVersion) { + + unsigned char response[128] = "BECN"; + + std::size_t cur = 5; + + // 2 bytes plugin port + *((uint16_t *)(response + cur)) = pluginReceivePort; + cur += sizeof(uint16_t); + + // 4 bytes xplane version + *((uint32_t*)(response + cur)) = xplaneVersion; + cur += sizeof(uint32_t); + + // plugin version + int len = pluginVersion.length(); + memcpy(response + cur, pluginVersion.c_str(), len); + cur += strlen(pluginVersion.c_str()) + len; + + sock->SendTo(response, cur, &multicast_address); + } void MessageHandlers::HandleConn(const Message& msg) { diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h index 52b652e..38fc12a 100644 --- a/xpcPlugin/MessageHandlers.h +++ b/xpcPlugin/MessageHandlers.h @@ -38,6 +38,8 @@ namespace XPC /// Sets the socket that message handlers use to send responses. static void SetSocket(UDPSocket* socket); + + static void SendBeacon(const std::string& pluginVersion, unsigned short pluginReceivePort, int xplaneVersion); private: // One handler per message type. Message types are descripbed on the diff --git a/xpcPlugin/Timer.cpp b/xpcPlugin/Timer.cpp new file mode 100644 index 0000000..ea56aa2 --- /dev/null +++ b/xpcPlugin/Timer.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. + +#include "Timer.h" +using namespace std; + +namespace XPC { + void Timer::start(const chrono::milliseconds interval, const Callback &callback) { + { + running.test_and_set(); + th = thread([=]() + { + while (running.test_and_set()) { + this_thread::sleep_for(interval); + callback(); + } + }); + } + } + + void Timer::stop() { + running.clear(); + if(th.joinable()) { + th.join(); + } + } +} diff --git a/xpcPlugin/Timer.h b/xpcPlugin/Timer.h new file mode 100644 index 0000000..fc31732 --- /dev/null +++ b/xpcPlugin/Timer.h @@ -0,0 +1,29 @@ +// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. + +#ifndef XPCPLUGIN_TIMER_H_ +#define XPCPLUGIN_TIMER_H_ + +#include +#include +#include +#include +#include + +namespace XPC { + class Timer + { + std::atomic_flag running; + std::thread th; + + public: + + using Callback = std::function; + + void start(const std::chrono::milliseconds, const Callback &callback); + + void stop(); + }; +} + +#endif /* Timer_hpp */ diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index ad662a7..7361339 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -138,6 +138,18 @@ namespace XPC Log::FormatLine(LOG_INFO, tag, "Send succeeded. (remote: %s)", GetHost(remote).c_str()); } } + + sockaddr UDPSocket::GetAddr(std::string address, unsigned short port) + { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(address.c_str()); + addr.sin_port = htons(port); + + sockaddr* sa = reinterpret_cast(&addr); + return *sa; + } std::string UDPSocket::GetHost(sockaddr* sa) { diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h index aa74fa7..67eeeb7 100644 --- a/xpcPlugin/UDPSocket.h +++ b/xpcPlugin/UDPSocket.h @@ -56,12 +56,14 @@ namespace XPC /// \param len The number of bytes to send. /// \param remote The destination socket. void SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote) const; - + /// Gets a string containing the IP address and port contained in the given sockaddr. /// /// \param addr The socket address to parse. /// \returns A string representation of the socket address. static std::string GetHost(sockaddr* addr); + + static sockaddr GetAddr(std::string address, unsigned short port); private: #ifdef _WIN32 SOCKET sock; diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index 0b75667..b679f86 100755 --- a/xpcPlugin/XPCPlugin.cpp +++ b/xpcPlugin/XPCPlugin.cpp @@ -60,13 +60,15 @@ #include "Log.h" #include "MessageHandlers.h" #include "UDPSocket.h" +#include "Timer.h" // XPLM Includes #include "XPLMProcessing.h" +#include "XPLMUtilities.h" // System Includes #include -#include +#include #include #ifdef __APPLE__ #include @@ -75,7 +77,12 @@ #define RECVPORT 49009 // Port that the plugin receives commands on #define OPS_PER_CYCLE 20 // Max Number of operations per cycle +#define XPC_PLUGIN_VERSION "1.3-rc.1" + +using namespace std; + XPC::UDPSocket* sock = NULL; +XPC::Timer* timer = NULL; double start; double lap; @@ -96,7 +103,7 @@ PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) strcpy(outDesc, "X Plane Communications Toolbox\nCopyright (c) 2013-2018 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved."); #if (__APPLE__) - if ( abs(timeConvert) <= 1e-9 ) // is about 0 + if ( timeConvert <= 1e-9 ) // is about 0 { mach_timebase_info_data_t timeBase; (void)mach_timebase_info(&timeBase); @@ -105,7 +112,7 @@ PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) 1000000000.0; } #endif - XPC::Log::Initialize("1.3-rc.1"); + XPC::Log::Initialize(XPC_PLUGIN_VERSION); XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Start"); XPC::DataManager::Initialize(); @@ -133,12 +140,18 @@ PLUGIN_API void XPluginDisable(void) XPC::Drawing::ClearWaypoints(); XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Disabled, sockets closed"); + + timer->stop(); + delete timer; + timer = NULL; } PLUGIN_API int XPluginEnable(void) { // Open sockets sock = new XPC::UDPSocket(RECVPORT); + timer = new XPC::Timer(); + XPC::MessageHandlers::SetSocket(sock); XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Enabled, sockets opened"); @@ -152,6 +165,15 @@ PLUGIN_API int XPluginEnable(void) void* refcon = NULL; // Don't pass anything to the callback directly XPLMRegisterFlightLoopCallback(XPCFlightLoopCallback, interval, refcon); + + int xpVer; + XPLMGetVersions(&xpVer, NULL, NULL); + + timer->start(chrono::milliseconds(1000), [=]{ + XPC::MessageHandlers::SendBeacon(XPC_PLUGIN_VERSION, RECVPORT, xpVer); + }); + + return 1; } diff --git a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj index a7ed423..4a5138b 100755 --- a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj +++ b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3D0F44CE21C6D3E7008A0655 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D0F44CD21C6D3E7008A0655 /* Timer.cpp */; }; BE37D960187C8B0F0033B082 /* XPCPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */; }; BE8361EF18C5591C00E9C923 /* mac.xpl in CopyFiles */ = {isa = PBXBuildFile; fileRef = D607B19909A556E400699BC3 /* mac.xpl */; }; BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */; }; @@ -36,6 +37,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3D0F44CC21C6D3E7008A0655 /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = ""; }; + 3D0F44CD21C6D3E7008A0655 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = ""; }; BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XPCPlugin.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; }; BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataManager.cpp; sourceTree = ""; }; BEABAD2C1AE041A3007BA7DA /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataManager.h; sourceTree = ""; }; @@ -76,6 +79,7 @@ AC4E46B809C2E0B3006B7E1B /* src */ = { isa = PBXGroup; children = ( + 3D0F44CD21C6D3E7008A0655 /* Timer.cpp */, BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */, BEDC620218EDF1A7005DB364 /* xplaneConnect.c */, BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */, @@ -91,6 +95,7 @@ BE953E0B1AEB183400CE4A8C /* inc */ = { isa = PBXGroup; children = ( + 3D0F44CC21C6D3E7008A0655 /* Timer.h */, BEDC620318EDF1A7005DB364 /* xplaneConnect.h */, BEABAD2C1AE041A3007BA7DA /* DataManager.h */, BEABAD301AE041A3007BA7DA /* Drawing.h */, @@ -190,6 +195,7 @@ BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */, BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */, BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */, + 3D0F44CE21C6D3E7008A0655 /* Timer.cpp in Sources */, BE37D960187C8B0F0033B082 /* XPCPlugin.cpp in Sources */, BEABAD3F1AE0498D007BA7DA /* UDPSocket.cpp in Sources */, ); @@ -298,6 +304,7 @@ D607B19C09A556E400699BC3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_ENABLE_MODULES = YES; CLANG_LINK_OBJC_RUNTIME = NO; CLANG_WARN_CXX0X_EXTENSIONS = YES; @@ -337,6 +344,7 @@ D607B19D09A556E400699BC3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_ENABLE_MODULES = YES; CLANG_LINK_OBJC_RUNTIME = NO; CLANG_WARN_CXX0X_EXTENSIONS = YES; @@ -365,9 +373,7 @@ "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/xplaneConnect-asdjuezcjkhojuewbyxhyhabxfwc/Build/Products/Debug", ); MACH_O_TYPE = mh_bundle; - ONLY_ACTIVE_ARCH = NO; - PRODUCT_NAME = mac; SDKROOT = macosx; STRIP_INSTALLED_PRODUCT = YES; diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj index 2f48de6..0660444 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj @@ -206,6 +206,7 @@ + @@ -214,6 +215,7 @@ +