From 310785bffdd645442d4aebc6307c5ca9dd0dc57e Mon Sep 17 00:00:00 2001 From: KingfuChan-MacPD Date: Sat, 29 Jul 2023 14:14:08 +0800 Subject: [PATCH 1/7] initialize tx/rx functions --- RDFPlugin/CRDFPlugin.cpp | 70 +++++++++++++++++++++++++++++++++------- RDFPlugin/CRDFPlugin.h | 9 ++++-- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/RDFPlugin/CRDFPlugin.cpp b/RDFPlugin/CRDFPlugin.cpp index 6ef552c..c0f4ff4 100644 --- a/RDFPlugin/CRDFPlugin.cpp +++ b/RDFPlugin/CRDFPlugin.cpp @@ -6,6 +6,8 @@ using namespace std; #define VECTORAUDIO_PARAM_VERSION "/*" #define VECTORAUDIO_PARAM_TRANSMIT "/transmitting" +#define VECTORAUDIO_PARAM_TX "/tx" +#define VECTORAUDIO_PARAM_RX "/rx" #define SETTING_VECTORAUDIO_ADDRESS "VectorAudioAddress" #define SETTING_VECTORAUDIO_TIMEOUT "VectorAudioTimeout" @@ -63,21 +65,26 @@ CRDFPlugin::CRDFPlugin() DisplayEuroScopeMessage(string("Version " + MY_PLUGIN_VERSION + " loaded")); // detach thread for VectorAudio - VectorAudioTransmission = new thread(&CRDFPlugin::VectorAudioHTTPLoop, this); - VectorAudioTransmission->detach(); + threadVectorAudioMain = new thread(&CRDFPlugin::VectorAudioMainLoop, this); + threadVectorAudioMain->detach(); + threadVectorAudioTXRX = new thread(&CRDFPlugin::VectorAudioTXRXLoop, this); + threadVectorAudioTXRX->detach(); } CRDFPlugin::~CRDFPlugin() { // close detached thread - threadRunning = false; - threadClosed.wait(false); + threadMainRunning = false; + threadTXRXRunning = false; if (this->hiddenWindow != NULL) { DestroyWindow(this->hiddenWindow); } UnregisterClass("RDFHiddenWindowClass", NULL); + + threadMainClosed.wait(false); + threadTXRXClosed.wait(false); } void CRDFPlugin::ProcessAFVMessage(std::string message) @@ -360,21 +367,21 @@ void CRDFPlugin::AddOffset(CPosition& position, double heading, double distance) position.m_Longitude = GEOM_DEG_FROM_RAD(lambda2); } -void CRDFPlugin::VectorAudioHTTPLoop(void) +void CRDFPlugin::VectorAudioMainLoop(void) { - threadRunning = true; - threadClosed = false; + threadMainRunning = true; + threadMainClosed = false; bool getTransmit = false; while (true) { for (int sleepRemain = getTransmit ? pollInterval : retryInterval * 1000; sleepRemain > 0;) { - if (threadRunning) { + if (threadMainRunning) { int sleepThis = min(sleepRemain, pollInterval); this_thread::sleep_for(chrono::milliseconds(sleepThis)); sleepRemain -= sleepThis; } else { - threadClosed = true; - threadClosed.notify_all(); + threadMainClosed = true; + threadMainClosed.notify_all(); return; } } @@ -407,7 +414,7 @@ void CRDFPlugin::VectorAudioHTTPLoop(void) ProcessMessageQueue(); continue; } - DisplayEuroScopeDebugMessage("HTTP error: " + httplib::to_string(res.error())); + DisplayEuroScopeDebugMessage("HTTP error on MAIN: " + httplib::to_string(res.error())); } else { DisplayEuroScopeDebugMessage("Not connected"); @@ -419,6 +426,47 @@ void CRDFPlugin::VectorAudioHTTPLoop(void) } } +void CRDFPlugin::VectorAudioTXRXLoop(void) +{ + threadTXRXRunning = true; + threadTXRXClosed = false; + while (true) { + for (int sleepRemain = retryInterval * 1000; sleepRemain > 0;) { + if (threadTXRXRunning) { + int sleepThis = min(sleepRemain, pollInterval); + this_thread::sleep_for(chrono::milliseconds(sleepThis)); + sleepRemain -= sleepThis; + } + else { + threadTXRXClosed = true; + threadTXRXClosed.notify_all(); + return; + } + } + + httplib::Client cli("http://" + addressVectorAudio); + cli.set_connection_timeout(0, connectionTimeout * 1000); + if (auto res = cli.Get(VECTORAUDIO_PARAM_TX)) { + if (res->status == 200) { + // TODO: set TX + DisplayEuroScopeDebugMessage(string("VectorAudio message on TX: ") + res->body); + } + else { + DisplayEuroScopeDebugMessage("HTTP error on TX: " + httplib::to_string(res.error())); + } + } + if (auto res = cli.Get(VECTORAUDIO_PARAM_RX)) { + if (res->status == 200) { + // TODO: set RX + DisplayEuroScopeDebugMessage(string("VectorAudio message on RX: ") + res->body); + } + else { + DisplayEuroScopeDebugMessage("HTTP error on RX: " + httplib::to_string(res.error())); + } + } + } +} + CRadarScreen* CRDFPlugin::OnRadarScreenCreated(const char* sDisplayName, bool NeedRadarContent, bool GeoReferenced, diff --git a/RDFPlugin/CRDFPlugin.h b/RDFPlugin/CRDFPlugin.h index 0139d07..035dc0d 100644 --- a/RDFPlugin/CRDFPlugin.h +++ b/RDFPlugin/CRDFPlugin.h @@ -45,10 +45,13 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn normal_distribution<> disDistance; string addressVectorAudio; - thread* VectorAudioTransmission; int connectionTimeout, pollInterval, retryInterval; - atomic_bool threadRunning, threadClosed; // for thread control - void VectorAudioHTTPLoop(void); + // thread controls + thread* threadVectorAudioMain, * threadVectorAudioTXRX; + atomic_bool threadMainRunning, threadMainClosed, + threadTXRXRunning, threadTXRXClosed; + void VectorAudioMainLoop(void); + void VectorAudioTXRXLoop(void); HWND hiddenWindow = NULL; From 9780007e96774a232d503885411271ea2c8915f9 Mon Sep 17 00:00:00 2001 From: Kingfu-PD Date: Sat, 5 Aug 2023 11:12:39 +0800 Subject: [PATCH 2/7] bridge realization --- RDFPlugin/CRDFPlugin.cpp | 82 +++++++++++++++++++++++++++++++++++---- RDFPlugin/CRDFPlugin.h | 2 + RDFPlugin/RDFPlugin.aps | Bin 2664 -> 2112 bytes 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/RDFPlugin/CRDFPlugin.cpp b/RDFPlugin/CRDFPlugin.cpp index c0f4ff4..12d3bec 100644 --- a/RDFPlugin/CRDFPlugin.cpp +++ b/RDFPlugin/CRDFPlugin.cpp @@ -29,6 +29,10 @@ const double EarthRadius = 3438.0; // nautical miles, referred to internal CEuro constexpr double GEOM_RAD_FROM_DEG(double deg) { return deg * pi / 180.0; }; constexpr double GEOM_DEG_FROM_RAD(double rad) { return rad / pi * 180.0; }; +inline bool FrequencyCompare(int freq1, int freq2) { // return true if same frequency, frequency *= 1000 + return abs(freq1 - freq2) <= 10; +} + CRDFPlugin::CRDFPlugin() : EuroScopePlugIn::CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PLUGIN_NAME.c_str(), @@ -325,11 +329,6 @@ void CRDFPlugin::ProcessMessageQueue(void) double distance = abs(disDistance(rdGenerator)) / 3.0 * offset; double bearing = disBearing(rdGenerator); AddOffset(posnew, bearing, distance); -#ifdef _DEBUG - double _dis = pos.DistanceTo(posnew); - double _dir = pos.DirectionTo(posnew); - DisplayEuroScopeDebugMessage("d: " + to_string(_dis) + "/" + to_string(distance) + " a: " + to_string(_dir) + "/" + to_string(bearing)); -#endif // _DEBUG } activeTransmittingPilots[callsign] = { posnew, radius }; } @@ -346,6 +345,71 @@ void CRDFPlugin::ProcessMessageQueue(void) } } +int CRDFPlugin::UpdateChannels(string line, bool mode_tx) +{ + // parse message and returns number of total toggles + map channelFreq; + istringstream ssLine(line); + string strChnl; + while (getline(ssLine, strChnl, ',')) { + size_t colon = strChnl.find(':'); + string channel = strChnl.substr(0, colon); + try { + // frequencies * 1000 => int + int frequency = round(stod(strChnl.substr(colon + 1)) * 1000.0); + channelFreq.insert({ channel, frequency }); + } + catch (...) { + DisplayEuroScopeDebugMessage("Error when parsing frequencies: " + strChnl); + continue; + } + } + + int count = 0; + for (auto chnl = GroundToArChannelSelectFirst(); chnl.IsValid(); chnl = GroundToArChannelSelectNext(chnl)) { + if (chnl.GetIsPrimary() || chnl.GetIsAtis()) { // make sure primary and ATIS are not affected + continue; + } + string chName = chnl.GetName(); + int chFreq = round(chnl.GetFrequency() * 1000.0); + auto it = channelFreq.find(chName); + if (it != channelFreq.end() && FrequencyCompare(it->second, chFreq)) { // allows 0.010 of deviation + goto _toggle_on; + } + else if (it == channelFreq.end()) { + auto itc = channelFreq.begin(); + for (; itc != channelFreq.end() && !FrequencyCompare(itc->second, chFreq); itc++); // locate a matching freq + if (itc != channelFreq.end()) { + size_t posi = itc->first.find('_'); + size_t posc = chName.find('_'); + if (itc->first.substr(0, posi) == chName.substr(0, posc)) { + goto _toggle_on; + } + } + } + // toggle off + if (mode_tx && chnl.GetIsTextTransmitOn()) { + chnl.ToggleTextTransmit(); + count++; + } + else if (!mode_tx && chnl.GetIsTextReceiveOn()) { + chnl.ToggleTextReceive(); + count++; + } + continue; + _toggle_on: + if (mode_tx && !chnl.GetIsTextTransmitOn()) { + chnl.ToggleTextTransmit(); + count++; + } + else if (!mode_tx && !chnl.GetIsTextReceiveOn()) { + chnl.ToggleTextReceive(); + count++; + } + } + return count; +} + void CRDFPlugin::AddOffset(CPosition& position, double heading, double distance) { // from ES internal void CEuroScopeCoord :: Move ( double heading, double distance ) @@ -446,10 +510,13 @@ void CRDFPlugin::VectorAudioTXRXLoop(void) httplib::Client cli("http://" + addressVectorAudio); cli.set_connection_timeout(0, connectionTimeout * 1000); + int count = 0; if (auto res = cli.Get(VECTORAUDIO_PARAM_TX)) { if (res->status == 200) { - // TODO: set TX DisplayEuroScopeDebugMessage(string("VectorAudio message on TX: ") + res->body); + count += UpdateChannels(res->body, true); + DisplayEuroScopeDebugMessage("TX toggles:" + to_string(count)); + } else { DisplayEuroScopeDebugMessage("HTTP error on TX: " + httplib::to_string(res.error())); @@ -457,8 +524,9 @@ void CRDFPlugin::VectorAudioTXRXLoop(void) } if (auto res = cli.Get(VECTORAUDIO_PARAM_RX)) { if (res->status == 200) { - // TODO: set RX DisplayEuroScopeDebugMessage(string("VectorAudio message on RX: ") + res->body); + count += UpdateChannels(res->body, false); + DisplayEuroScopeDebugMessage("TX+RX toggles:" + to_string(count)); } else { DisplayEuroScopeDebugMessage("HTTP error on RX: " + httplib::to_string(res.error())); diff --git a/RDFPlugin/CRDFPlugin.h b/RDFPlugin/CRDFPlugin.h index 035dc0d..88639ce 100644 --- a/RDFPlugin/CRDFPlugin.h +++ b/RDFPlugin/CRDFPlugin.h @@ -79,6 +79,8 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn void LoadSettings(void); void ProcessMessageQueue(void); + int UpdateChannels(string line, bool mode_tx); + inline void DisplayEuroScopeDebugMessage(string msg) { #ifdef _DEBUG DisplayUserMessage("RDF-DEBUG", "", msg.c_str(), true, true, true, false, false); diff --git a/RDFPlugin/RDFPlugin.aps b/RDFPlugin/RDFPlugin.aps index e359fbdc73db06a64e631d06dc9e294c62a8cdbb..3b89eae624fb5a09511fc8889a538846d2940131 100644 GIT binary patch delta 348 zcmaDMazJ2$0;A1DMP=p-Yz&k6m?T((QVa5nC+5pA7EJ!jDi0)O*`gUQOs-^8W?VCQ zE}Qt~8*DciCtI){nb;%6%)-Gmaph}00Y(M}6$S3vrML3NRmv<6I5mb^`M$}3Tz6pd6R!J>cAb#CIwXg z|NrD2j7}UN22i!nV+M*|0Ex;>e#R=q_7KSZ!94jNhnyD3mkg}fe96WFlmP>f%T3rO&tjAV0F4+} A>Hq)$ delta 867 zcmb7?O>YuW6o%gm!=M-=8Wc68fVgb2rBFW>CNNCt1e|Gx>6cDI8UmD9pnT)EP%u9~>;2WZlb!EzV9p7TEVJ#-jjg+V-_ zAD;u12liU8*6rC(>(!m@w%zOWyVaWAt?hMscCp!R)ccCi&HS?XR(Jc2rjxl*vRegC z0Ne)u;-BHC-f!%PUzTqKWQkN~@fUKAr{&ql{{u}EaWoQNhzUBdf(_*89F6Xf0?Y@` zkFN;$lF=?|*h2?B*eIfjHX5j-4+T5eMw@4S8Ghh9+=qjiM9%#|dIX!6rEXf9u9VjD zDi$Ww)MUB{EG&^{y*F(Isekqe%ag`CkNyjC6@C zN>*3Y5>mpZ<+C}%w6N$VrfQj6NV|zv&<#^X#?7|WGM3y>H*i-(SBX_J$|~-;`ifdw z#eE)-;taecMQDE{MQNXvVj-%P?3J>WwKN5DPLVw5{lJEU=TX{TJz+lvzY^>dkF#+6 z77-b}cLy!%1id+)L8r4topIKi4ExI1kbjoL7lm;41_l08PRJ*u96NKge?;M1Dt6rT zkHU|6jOR)W#sLw=p6S8J36A$~&vd6nmtr(ZV@@(m(qxw|P`>Nq5B(|6YjH&QOaC>` TsT;oYUXh1XNkrx|{+qu6ks+~; From b9560a6cf489c2b52868e6a230efadb283940767 Mon Sep 17 00:00:00 2001 From: Kingfu-PD Date: Mon, 14 Aug 2023 00:58:18 +0800 Subject: [PATCH 3/7] reservation for afv-bridge --- RDFPlugin/CRDFPlugin.cpp | 45 ++++++++++++++++++++++++++++++++------ RDFPlugin/CRDFPlugin.h | 20 ++++++++++++++--- RDFPlugin/HiddenWindow.cpp | 23 +++++++++++++++++-- RDFPlugin/HiddenWindow.h | 4 +++- 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/RDFPlugin/CRDFPlugin.cpp b/RDFPlugin/CRDFPlugin.cpp index 12d3bec..02658fd 100644 --- a/RDFPlugin/CRDFPlugin.cpp +++ b/RDFPlugin/CRDFPlugin.cpp @@ -40,9 +40,9 @@ CRDFPlugin::CRDFPlugin() MY_PLUGIN_DEVELOPER.c_str(), MY_PLUGIN_COPYRIGHT.c_str()) { - RegisterClass(&this->windowClass); - - this->hiddenWindow = CreateWindow( + // RDF window + RegisterClass(&this->windowClassRDF); + this->hiddenWindowRDF = CreateWindow( "RDFHiddenWindowClass", "RDFHiddenWindow", NULL, @@ -55,9 +55,27 @@ CRDFPlugin::CRDFPlugin() GetModuleHandle(NULL), reinterpret_cast(this) ); + if (GetLastError() != S_OK) { + DisplayEuroScopeMessage("Unable to open communications for RDF"); + } + // AFV bridge window + RegisterClass(&this->windowClassAFV); + this->hiddenWindowAFV = CreateWindow( + "AfvBridgeHiddenWindowClass", + "AfvBridgeHiddenWindow", + NULL, + 0, + 0, + 0, + 0, + NULL, + NULL, + GetModuleHandle(NULL), + reinterpret_cast(this) + ); if (GetLastError() != S_OK) { - DisplayEuroScopeMessage("Unable to open communications for RDF plugin"); + DisplayEuroScopeMessage("Unable to open communications for AFV bridge"); } LoadSettings(); @@ -82,16 +100,21 @@ CRDFPlugin::~CRDFPlugin() threadMainRunning = false; threadTXRXRunning = false; - if (this->hiddenWindow != NULL) { - DestroyWindow(this->hiddenWindow); + if (this->hiddenWindowRDF != NULL) { + DestroyWindow(this->hiddenWindowRDF); } UnregisterClass("RDFHiddenWindowClass", NULL); + if (this->hiddenWindowAFV != NULL) { + DestroyWindow(this->hiddenWindowAFV); + } + UnregisterClass("AfvBridgeHiddenWindowClass", NULL); + threadMainClosed.wait(false); threadTXRXClosed.wait(false); } -void CRDFPlugin::ProcessAFVMessage(std::string message) +void CRDFPlugin::ProcessRDFMessage(std::string message) { { std::lock_guard lock(this->messageLock); @@ -112,6 +135,14 @@ void CRDFPlugin::ProcessAFVMessage(std::string message) ProcessMessageQueue(); } +void CRDFPlugin::ProcessAFVMessage(string message) +{ + // functions as AFV bridge + if (message.size()) { + DisplayEuroScopeDebugMessage(string("AFV message: ") + message); + } +} + void CRDFPlugin::GetRGB(COLORREF& color, const char* settingValue) { unsigned int r, g, b; diff --git a/RDFPlugin/CRDFPlugin.h b/RDFPlugin/CRDFPlugin.h index 88639ce..b91ed57 100644 --- a/RDFPlugin/CRDFPlugin.h +++ b/RDFPlugin/CRDFPlugin.h @@ -53,16 +53,17 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn void VectorAudioMainLoop(void); void VectorAudioTXRXLoop(void); - HWND hiddenWindow = NULL; + HWND hiddenWindowRDF = NULL; + HWND hiddenWindowAFV = NULL; mutex messageLock; // Lock for the message queue // Internal message quque queue> messages; // Class for our window - WNDCLASS windowClass = { + WNDCLASS windowClassRDF = { NULL, - HiddenWindow, + HiddenWindowRDF, NULL, NULL, GetModuleHandle(NULL), @@ -72,6 +73,18 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn NULL, "RDFHiddenWindowClass" }; + WNDCLASS windowClassAFV = { + NULL, + HiddenWindowAFV, + NULL, + NULL, + GetModuleHandle(NULL), + NULL, + NULL, + NULL, + NULL, + "AfvBridgeHiddenWindowClass" + }; bool drawController; @@ -94,6 +107,7 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn public: CRDFPlugin(); virtual ~CRDFPlugin(); + void ProcessRDFMessage(string message); void ProcessAFVMessage(string message); virtual CRadarScreen* OnRadarScreenCreated(const char* sDisplayName, bool NeedRadarContent, bool GeoReferenced, bool CanBeSaved, bool CanBeCreated); virtual bool OnCompileCommand(const char* sCommandLine); diff --git a/RDFPlugin/HiddenWindow.cpp b/RDFPlugin/HiddenWindow.cpp index 0b540bd..2e77657 100644 --- a/RDFPlugin/HiddenWindow.cpp +++ b/RDFPlugin/HiddenWindow.cpp @@ -5,7 +5,27 @@ CRDFPlugin* rdfPlugin; -LRESULT CALLBACK HiddenWindow(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK HiddenWindowRDF(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CREATE: { + rdfPlugin = reinterpret_cast(reinterpret_cast(lParam)->lpCreateParams); + return TRUE; + } + case WM_COPYDATA: { + COPYDATASTRUCT* data = reinterpret_cast(lParam); + + if (data != nullptr && data->dwData == 666 && data->lpData != nullptr && rdfPlugin != nullptr) { + rdfPlugin->ProcessRDFMessage(reinterpret_cast(data->lpData)); + } + return TRUE; + } + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK HiddenWindowAFV(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: { @@ -26,4 +46,3 @@ LRESULT CALLBACK HiddenWindow(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } - \ No newline at end of file diff --git a/RDFPlugin/HiddenWindow.h b/RDFPlugin/HiddenWindow.h index 061d260..5caef9f 100644 --- a/RDFPlugin/HiddenWindow.h +++ b/RDFPlugin/HiddenWindow.h @@ -2,4 +2,6 @@ #include "stdafx.h" -LRESULT CALLBACK HiddenWindow(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); \ No newline at end of file +LRESULT CALLBACK HiddenWindowRDF(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +LRESULT CALLBACK HiddenWindowAFV(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); \ No newline at end of file From b92d1db2ba1a8bd5b995b4700b06cc44d42cd675 Mon Sep 17 00:00:00 2001 From: Kingfu-PD Date: Sat, 26 Aug 2023 14:55:22 +0800 Subject: [PATCH 4/7] fix duplicate toggle in VectorAudio --- RDFPlugin/CRDFPlugin.cpp | 76 +++++++++++++++++++++----------------- RDFPlugin/CRDFPlugin.h | 9 +++-- RDFPlugin/HiddenWindow.cpp | 4 +- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/RDFPlugin/CRDFPlugin.cpp b/RDFPlugin/CRDFPlugin.cpp index 02658fd..c0bd30c 100644 --- a/RDFPlugin/CRDFPlugin.cpp +++ b/RDFPlugin/CRDFPlugin.cpp @@ -29,6 +29,9 @@ const double EarthRadius = 3438.0; // nautical miles, referred to internal CEuro constexpr double GEOM_RAD_FROM_DEG(double deg) { return deg * pi / 180.0; }; constexpr double GEOM_DEG_FROM_RAD(double rad) { return rad / pi * 180.0; }; +inline int FrequencyConvert(double freq) { // frequency * 1000 => int + return round(freq * 1000.0); +} inline bool FrequencyCompare(int freq1, int freq2) { // return true if same frequency, frequency *= 1000 return abs(freq1 - freq2) <= 10; } @@ -114,7 +117,7 @@ CRDFPlugin::~CRDFPlugin() threadTXRXClosed.wait(false); } -void CRDFPlugin::ProcessRDFMessage(std::string message) +void CRDFPlugin::HiddenWndProcessRDFMessage(std::string message) { { std::lock_guard lock(this->messageLock); @@ -132,15 +135,17 @@ void CRDFPlugin::ProcessRDFMessage(std::string message) this->messages.push(set()); } } - ProcessMessageQueue(); + ProcessRDFQueue(); } -void CRDFPlugin::ProcessAFVMessage(string message) +void CRDFPlugin::HiddenWndProcessAFVMessage(string message) { // functions as AFV bridge - if (message.size()) { - DisplayEuroScopeDebugMessage(string("AFV message: ") + message); - } + if (!message.size()) return; + DisplayEuroScopeDebugMessage(string("AFV message: ") + message); + // TODO: set TX/RX + // format: xxx.xxx:True:False + xxx.xx0:True:False + } void CRDFPlugin::GetRGB(COLORREF& color, const char* settingValue) @@ -311,7 +316,7 @@ void CRDFPlugin::LoadSettings(void) } -void CRDFPlugin::ProcessMessageQueue(void) +void CRDFPlugin::ProcessRDFQueue(void) { std::lock_guard lock(this->messageLock); // Process all incoming messages @@ -376,19 +381,21 @@ void CRDFPlugin::ProcessMessageQueue(void) } } -int CRDFPlugin::UpdateChannels(string line, bool mode_tx) +void CRDFPlugin::UpdateVectorAudioChannels(string line, bool mode_tx) { // parse message and returns number of total toggles map channelFreq; istringstream ssLine(line); string strChnl; + int freqMe = FrequencyConvert(ControllerMyself().GetPrimaryFrequency()); while (getline(ssLine, strChnl, ',')) { size_t colon = strChnl.find(':'); string channel = strChnl.substr(0, colon); try { - // frequencies * 1000 => int - int frequency = round(stod(strChnl.substr(colon + 1)) * 1000.0); - channelFreq.insert({ channel, frequency }); + int frequency = FrequencyConvert(stod(strChnl.substr(colon + 1))); + if (!FrequencyCompare(freqMe, frequency)) { + channelFreq.insert({ channel, frequency }); + } } catch (...) { DisplayEuroScopeDebugMessage("Error when parsing frequencies: " + strChnl); @@ -396,15 +403,15 @@ int CRDFPlugin::UpdateChannels(string line, bool mode_tx) } } - int count = 0; for (auto chnl = GroundToArChannelSelectFirst(); chnl.IsValid(); chnl = GroundToArChannelSelectNext(chnl)) { if (chnl.GetIsPrimary() || chnl.GetIsAtis()) { // make sure primary and ATIS are not affected continue; } string chName = chnl.GetName(); - int chFreq = round(chnl.GetFrequency() * 1000.0); + int chFreq = FrequencyConvert(chnl.GetFrequency()); auto it = channelFreq.find(chName); if (it != channelFreq.end() && FrequencyCompare(it->second, chFreq)) { // allows 0.010 of deviation + channelFreq.erase(it); goto _toggle_on; } else if (it == channelFreq.end()) { @@ -414,31 +421,38 @@ int CRDFPlugin::UpdateChannels(string line, bool mode_tx) size_t posi = itc->first.find('_'); size_t posc = chName.find('_'); if (itc->first.substr(0, posi) == chName.substr(0, posc)) { + channelFreq.erase(itc); goto _toggle_on; } } } // toggle off - if (mode_tx && chnl.GetIsTextTransmitOn()) { - chnl.ToggleTextTransmit(); - count++; + if (mode_tx) { + ToggleChannels(chnl, 0, -1); } - else if (!mode_tx && chnl.GetIsTextReceiveOn()) { - chnl.ToggleTextReceive(); - count++; + else { + ToggleChannels(chnl, -1, 0); } continue; _toggle_on: - if (mode_tx && !chnl.GetIsTextTransmitOn()) { - chnl.ToggleTextTransmit(); - count++; + if (mode_tx) { + ToggleChannels(chnl, 1, -1); } - else if (!mode_tx && !chnl.GetIsTextReceiveOn()) { - chnl.ToggleTextReceive(); - count++; + else { + ToggleChannels(chnl, -1, 1); } } - return count; +} + +void CRDFPlugin::ToggleChannels(CGrountToAirChannel Channel, int tx, int rx) +{ + // pass tx/rx = -1 to skip + if (tx >= 0 && tx != (int)Channel.GetIsTextTransmitOn()) { + Channel.ToggleTextTransmit(); + } + if (rx >= 0 && rx != (int)Channel.GetIsTextReceiveOn()) { + Channel.ToggleTextReceive(); + } } void CRDFPlugin::AddOffset(CPosition& position, double heading, double distance) @@ -506,7 +520,7 @@ void CRDFPlugin::VectorAudioMainLoop(void) this->messages.push(strings); } } - ProcessMessageQueue(); + ProcessRDFQueue(); continue; } DisplayEuroScopeDebugMessage("HTTP error on MAIN: " + httplib::to_string(res.error())); @@ -541,13 +555,10 @@ void CRDFPlugin::VectorAudioTXRXLoop(void) httplib::Client cli("http://" + addressVectorAudio); cli.set_connection_timeout(0, connectionTimeout * 1000); - int count = 0; if (auto res = cli.Get(VECTORAUDIO_PARAM_TX)) { if (res->status == 200) { DisplayEuroScopeDebugMessage(string("VectorAudio message on TX: ") + res->body); - count += UpdateChannels(res->body, true); - DisplayEuroScopeDebugMessage("TX toggles:" + to_string(count)); - + UpdateVectorAudioChannels(res->body, true); } else { DisplayEuroScopeDebugMessage("HTTP error on TX: " + httplib::to_string(res.error())); @@ -556,8 +567,7 @@ void CRDFPlugin::VectorAudioTXRXLoop(void) if (auto res = cli.Get(VECTORAUDIO_PARAM_RX)) { if (res->status == 200) { DisplayEuroScopeDebugMessage(string("VectorAudio message on RX: ") + res->body); - count += UpdateChannels(res->body, false); - DisplayEuroScopeDebugMessage("TX+RX toggles:" + to_string(count)); + UpdateVectorAudioChannels(res->body, false); } else { DisplayEuroScopeDebugMessage("HTTP error on RX: " + httplib::to_string(res.error())); diff --git a/RDFPlugin/CRDFPlugin.h b/RDFPlugin/CRDFPlugin.h index b91ed57..5a0ca55 100644 --- a/RDFPlugin/CRDFPlugin.h +++ b/RDFPlugin/CRDFPlugin.h @@ -90,9 +90,10 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn void GetRGB(COLORREF& color, const char* settingValue); void LoadSettings(void); - void ProcessMessageQueue(void); + void ProcessRDFQueue(void); - int UpdateChannels(string line, bool mode_tx); + void UpdateVectorAudioChannels(string line, bool mode_tx); + void ToggleChannels(CGrountToAirChannel Channel, int tx = -1, int rx = -1); inline void DisplayEuroScopeDebugMessage(string msg) { #ifdef _DEBUG @@ -107,8 +108,8 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn public: CRDFPlugin(); virtual ~CRDFPlugin(); - void ProcessRDFMessage(string message); - void ProcessAFVMessage(string message); + void HiddenWndProcessRDFMessage(string message); + void HiddenWndProcessAFVMessage(string message); virtual CRadarScreen* OnRadarScreenCreated(const char* sDisplayName, bool NeedRadarContent, bool GeoReferenced, bool CanBeSaved, bool CanBeCreated); virtual bool OnCompileCommand(const char* sCommandLine); diff --git a/RDFPlugin/HiddenWindow.cpp b/RDFPlugin/HiddenWindow.cpp index 2e77657..787ef76 100644 --- a/RDFPlugin/HiddenWindow.cpp +++ b/RDFPlugin/HiddenWindow.cpp @@ -16,7 +16,7 @@ LRESULT CALLBACK HiddenWindowRDF(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar COPYDATASTRUCT* data = reinterpret_cast(lParam); if (data != nullptr && data->dwData == 666 && data->lpData != nullptr && rdfPlugin != nullptr) { - rdfPlugin->ProcessRDFMessage(reinterpret_cast(data->lpData)); + rdfPlugin->HiddenWndProcessRDFMessage(reinterpret_cast(data->lpData)); } return TRUE; } @@ -36,7 +36,7 @@ LRESULT CALLBACK HiddenWindowAFV(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar COPYDATASTRUCT* data = reinterpret_cast(lParam); if (data != nullptr && data->dwData == 666 && data->lpData != nullptr && rdfPlugin != nullptr) { - rdfPlugin->ProcessAFVMessage(reinterpret_cast(data->lpData)); + rdfPlugin->HiddenWndProcessAFVMessage(reinterpret_cast(data->lpData)); } return TRUE; } From da31d5c0bafab33ca6fd594b5b0aabb3648aa286 Mon Sep 17 00:00:00 2001 From: Kingfu-PD Date: Sun, 3 Sep 2023 19:21:14 +0800 Subject: [PATCH 5/7] realize afv-bridge --- RDFPlugin/CRDFPlugin.cpp | 73 ++++++++++++++++++++++++++++++++++++++-- RDFPlugin/CRDFPlugin.h | 1 + 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/RDFPlugin/CRDFPlugin.cpp b/RDFPlugin/CRDFPlugin.cpp index c0bd30c..0261c11 100644 --- a/RDFPlugin/CRDFPlugin.cpp +++ b/RDFPlugin/CRDFPlugin.cpp @@ -143,9 +143,71 @@ void CRDFPlugin::HiddenWndProcessAFVMessage(string message) // functions as AFV bridge if (!message.size()) return; DisplayEuroScopeDebugMessage(string("AFV message: ") + message); - // TODO: set TX/RX // format: xxx.xxx:True:False + xxx.xx0:True:False + // parse message + queue strings; + istringstream f(message); + string s; + while (getline(f, s, ':')) { + strings.push(s); + } + if (strings.size() != 3) return; // in case of incomplete message + int msgFrequency; + bool transmitX, receiveX; + try { + msgFrequency = FrequencyConvert(stod(strings.front())); + strings.pop(); + receiveX = strings.front() == "True"; + strings.pop(); + transmitX = strings.front() == "True"; + strings.pop(); + } + catch (...) { + DisplayEuroScopeDebugMessage("Error when parsing AFV message: " + message); + return; + } + + // abort if frequency is prim + if (FrequencyCompare(msgFrequency, FrequencyConvert(ControllerMyself().GetPrimaryFrequency()))) + return; + + // match frequency to callsign + string msgCallsign = ControllerMyself().GetCallsign(); + for (auto c = ControllerSelectFirst(); c.IsValid(); c = ControllerSelectNext(c)) { + if (FrequencyCompare(FrequencyConvert(c.GetPrimaryFrequency()), msgFrequency)) { + msgCallsign = c.GetCallsign(); + break; + } + } + + // find channel and toggle + for (auto c = GroundToArChannelSelectFirst(); c.IsValid(); c = GroundToArChannelSelectNext(c)) { + int chFreq = FrequencyConvert(c.GetFrequency()); + if (c.GetIsPrimary() || c.GetIsAtis() || !FrequencyCompare(chFreq, msgFrequency)) + continue; + string chName = c.GetName(); + if (msgCallsign == chName) { + ToggleChannels(c, transmitX, receiveX); + return; + } + else { + size_t posc = msgCallsign.find('_'); + if (chName.starts_with(msgCallsign.substr(0, posc))) { + ToggleChannels(c, transmitX, receiveX); + return; + } + } + } + + // no possible match, toggle the first same frequency + for (auto c = GroundToArChannelSelectFirst(); c.IsValid(); c = GroundToArChannelSelectNext(c)) { + if (!c.GetIsPrimary() && !c.GetIsAtis() && + FrequencyCompare(FrequencyConvert(c.GetFrequency()), msgFrequency)) { + ToggleChannels(c, transmitX, receiveX); + return; + } + } } void CRDFPlugin::GetRGB(COLORREF& color, const char* settingValue) @@ -419,8 +481,7 @@ void CRDFPlugin::UpdateVectorAudioChannels(string line, bool mode_tx) for (; itc != channelFreq.end() && !FrequencyCompare(itc->second, chFreq); itc++); // locate a matching freq if (itc != channelFreq.end()) { size_t posi = itc->first.find('_'); - size_t posc = chName.find('_'); - if (itc->first.substr(0, posi) == chName.substr(0, posc)) { + if (chName.starts_with(itc->first.substr(0, posi))) { channelFreq.erase(itc); goto _toggle_on; } @@ -449,9 +510,15 @@ void CRDFPlugin::ToggleChannels(CGrountToAirChannel Channel, int tx, int rx) // pass tx/rx = -1 to skip if (tx >= 0 && tx != (int)Channel.GetIsTextTransmitOn()) { Channel.ToggleTextTransmit(); + DisplayEuroScopeDebugMessage( + string("TX toggle: ") + Channel.GetName() + " frequency: " + to_string(Channel.GetFrequency()) + ); } if (rx >= 0 && rx != (int)Channel.GetIsTextReceiveOn()) { Channel.ToggleTextReceive(); + DisplayEuroScopeDebugMessage( + string("RX toggle: ") + Channel.GetName() + " frequency: " + to_string(Channel.GetFrequency()) + ); } } diff --git a/RDFPlugin/CRDFPlugin.h b/RDFPlugin/CRDFPlugin.h index 5a0ca55..86908a1 100644 --- a/RDFPlugin/CRDFPlugin.h +++ b/RDFPlugin/CRDFPlugin.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From a20587d2f3502a55e6a01f5aee574e7c976752b2 Mon Sep 17 00:00:00 2001 From: Kingfu-PD Date: Sat, 9 Sep 2023 23:41:09 +0800 Subject: [PATCH 6/7] classify messages --- RDFPlugin/CRDFPlugin.cpp | 117 +++++++++++++++++++++++---------------- RDFPlugin/CRDFPlugin.h | 8 ++- README.md | 28 +++++++--- 3 files changed, 93 insertions(+), 60 deletions(-) diff --git a/RDFPlugin/CRDFPlugin.cpp b/RDFPlugin/CRDFPlugin.cpp index 0261c11..7d70541 100644 --- a/RDFPlugin/CRDFPlugin.cpp +++ b/RDFPlugin/CRDFPlugin.cpp @@ -59,7 +59,7 @@ CRDFPlugin::CRDFPlugin() reinterpret_cast(this) ); if (GetLastError() != S_OK) { - DisplayEuroScopeMessage("Unable to open communications for RDF"); + DisplayWarnMessage("Unable to open communications for RDF"); } // AFV bridge window @@ -78,7 +78,7 @@ CRDFPlugin::CRDFPlugin() reinterpret_cast(this) ); if (GetLastError() != S_OK) { - DisplayEuroScopeMessage("Unable to open communications for AFV bridge"); + DisplayWarnMessage("Unable to open communications for AFV bridge"); } LoadSettings(); @@ -87,7 +87,7 @@ CRDFPlugin::CRDFPlugin() this->disBearing = uniform_real_distribution<>(0.0, 360.0); this->disDistance = normal_distribution<>(0, 1.0); - DisplayEuroScopeMessage(string("Version " + MY_PLUGIN_VERSION + " loaded")); + DisplayInfoMessage(string("Version " + MY_PLUGIN_VERSION + " loaded")); // detach thread for VectorAudio threadVectorAudioMain = new thread(&CRDFPlugin::VectorAudioMainLoop, this); @@ -122,7 +122,7 @@ void CRDFPlugin::HiddenWndProcessRDFMessage(std::string message) { std::lock_guard lock(this->messageLock); if (message.size()) { - DisplayEuroScopeDebugMessage(string("AFV message: ") + message); + DisplayDebugMessage(string("AFV message: ") + message); set strings; istringstream f(message); string s; @@ -142,7 +142,7 @@ void CRDFPlugin::HiddenWndProcessAFVMessage(string message) { // functions as AFV bridge if (!message.size()) return; - DisplayEuroScopeDebugMessage(string("AFV message: ") + message); + DisplayDebugMessage(string("AFV message: ") + message); // format: xxx.xxx:True:False + xxx.xx0:True:False // parse message @@ -164,7 +164,7 @@ void CRDFPlugin::HiddenWndProcessAFVMessage(string message) strings.pop(); } catch (...) { - DisplayEuroScopeDebugMessage("Error when parsing AFV message: " + message); + DisplayDebugMessage("Error when parsing AFV message: " + message); return; } @@ -215,7 +215,7 @@ void CRDFPlugin::GetRGB(COLORREF& color, const char* settingValue) unsigned int r, g, b; sscanf_s(settingValue, "%u:%u:%u", &r, &g, &b); if (r <= 255 && g <= 255 && b <= 255) { - DisplayEuroScopeDebugMessage(string("R: ") + to_string(r) + string(" G: ") + to_string(g) + string(" B: ") + to_string(b)); + DisplayDebugMessage(string("R: ") + to_string(r) + string(" G: ") + to_string(g) + string(" B: ") + to_string(b)); color = RGB(r, g, b); } } @@ -250,7 +250,7 @@ void CRDFPlugin::LoadSettings(void) if (cstrAddrVA != NULL) { addressVectorAudio = cstrAddrVA; - DisplayEuroScopeDebugMessage(string("Address: ") + addressVectorAudio); + DisplayDebugMessage(string("Address: ") + addressVectorAudio); } const char* cstrTimeout = GetDataFromSettings(SETTING_VECTORAUDIO_TIMEOUT); @@ -259,7 +259,7 @@ void CRDFPlugin::LoadSettings(void) int parsedTimeout = atoi(cstrTimeout); if (parsedTimeout >= 100 && parsedTimeout <= 1000) { connectionTimeout = parsedTimeout; - DisplayEuroScopeDebugMessage(string("Timeout: ") + to_string(connectionTimeout)); + DisplayDebugMessage(string("Timeout: ") + to_string(connectionTimeout)); } } @@ -269,7 +269,7 @@ void CRDFPlugin::LoadSettings(void) int parsedInterval = atoi(cstrPollInterval); if (parsedInterval >= 100) { pollInterval = parsedInterval; - DisplayEuroScopeDebugMessage(string("Poll interval: ") + to_string(pollInterval)); + DisplayDebugMessage(string("Poll interval: ") + to_string(pollInterval)); } } @@ -279,7 +279,7 @@ void CRDFPlugin::LoadSettings(void) int parsedInterval = atoi(cstrRetryInterval); if (parsedInterval >= 1) { retryInterval = parsedInterval; - DisplayEuroScopeDebugMessage(string("Retry interval: ") + to_string(retryInterval)); + DisplayDebugMessage(string("Retry interval: ") + to_string(retryInterval)); } } @@ -301,7 +301,7 @@ void CRDFPlugin::LoadSettings(void) int parsedRadius = atoi(cstrRadius); if (parsedRadius > 0) { circleRadius = parsedRadius; - DisplayEuroScopeDebugMessage(string("Radius: ") + to_string(circleRadius)); + DisplayDebugMessage(string("Radius: ") + to_string(circleRadius)); } } @@ -309,7 +309,7 @@ void CRDFPlugin::LoadSettings(void) if (cstrThreshold != NULL) { circleThreshold = atoi(cstrThreshold); - DisplayEuroScopeDebugMessage(string("Threshold: ") + to_string(circleThreshold)); + DisplayDebugMessage(string("Threshold: ") + to_string(circleThreshold)); } const char* cstrPrecision = GetDataFromSettings(SETTING_PRECISION); @@ -318,7 +318,7 @@ void CRDFPlugin::LoadSettings(void) int parsedPrecision = atoi(cstrPrecision); if (parsedPrecision >= 0) { circlePrecision = parsedPrecision; - DisplayEuroScopeDebugMessage(string("Precision: ") + to_string(circlePrecision)); + DisplayDebugMessage(string("Precision: ") + to_string(circlePrecision)); } } @@ -327,7 +327,7 @@ void CRDFPlugin::LoadSettings(void) { int parsedAlt = atoi(cstrLowAlt); lowAltitude = parsedAlt; - DisplayEuroScopeDebugMessage(string("Low Altitude: ") + to_string(lowAltitude)); + DisplayDebugMessage(string("Low Altitude: ") + to_string(lowAltitude)); } const char* cstrHighAlt = GetDataFromSettings(SETTING_HIGH_ALTITUDE); @@ -336,7 +336,7 @@ void CRDFPlugin::LoadSettings(void) int parsedAlt = atoi(cstrHighAlt); if (parsedAlt > 0) { highAltitude = parsedAlt; - DisplayEuroScopeDebugMessage(string("High Altitude: ") + to_string(highAltitude)); + DisplayDebugMessage(string("High Altitude: ") + to_string(highAltitude)); } } @@ -346,7 +346,7 @@ void CRDFPlugin::LoadSettings(void) int parsedPrecision = atoi(cstrLowPrecision); if (parsedPrecision >= 0) { lowPrecision = parsedPrecision; - DisplayEuroScopeDebugMessage(string("Low Precision: ") + to_string(lowPrecision)); + DisplayDebugMessage(string("Low Precision: ") + to_string(lowPrecision)); } } @@ -356,7 +356,7 @@ void CRDFPlugin::LoadSettings(void) int parsedPrecision = atoi(cstrHighPrecision); if (parsedPrecision >= 0) { highPrecision = parsedPrecision; - DisplayEuroScopeDebugMessage(string("High Precision: ") + to_string(highPrecision)); + DisplayDebugMessage(string("High Precision: ") + to_string(highPrecision)); } } @@ -364,16 +364,16 @@ void CRDFPlugin::LoadSettings(void) if (cstrController != NULL) { drawController = (bool)atoi(cstrController); - DisplayEuroScopeDebugMessage(string("Draw controllers and observers: ") + to_string(drawController)); + DisplayDebugMessage(string("Draw controllers and observers: ") + to_string(drawController)); } } catch (std::runtime_error const& e) { - DisplayEuroScopeMessage(string("Error: ") + e.what()); + DisplayWarnMessage(string("Error: ") + e.what()); } catch (...) { - DisplayEuroScopeMessage(string("Unexpected error: ") + to_string(GetLastError())); + DisplayWarnMessage(string("Unexpected error: ") + to_string(GetLastError())); } } @@ -460,7 +460,7 @@ void CRDFPlugin::UpdateVectorAudioChannels(string line, bool mode_tx) } } catch (...) { - DisplayEuroScopeDebugMessage("Error when parsing frequencies: " + strChnl); + DisplayDebugMessage("Error when parsing frequencies: " + strChnl); continue; } } @@ -510,13 +510,13 @@ void CRDFPlugin::ToggleChannels(CGrountToAirChannel Channel, int tx, int rx) // pass tx/rx = -1 to skip if (tx >= 0 && tx != (int)Channel.GetIsTextTransmitOn()) { Channel.ToggleTextTransmit(); - DisplayEuroScopeDebugMessage( + DisplayDebugMessage( string("TX toggle: ") + Channel.GetName() + " frequency: " + to_string(Channel.GetFrequency()) ); } if (rx >= 0 && rx != (int)Channel.GetIsTextReceiveOn()) { Channel.ToggleTextReceive(); - DisplayEuroScopeDebugMessage( + DisplayDebugMessage( string("RX toggle: ") + Channel.GetName() + " frequency: " + to_string(Channel.GetFrequency()) ); } @@ -566,9 +566,9 @@ void CRDFPlugin::VectorAudioMainLoop(void) cli.set_connection_timeout(0, connectionTimeout * 1000); if (auto res = cli.Get(getTransmit ? VECTORAUDIO_PARAM_TRANSMIT : VECTORAUDIO_PARAM_VERSION)) { if (res->status == 200) { - DisplayEuroScopeDebugMessage(string("VectorAudio message: ") + res->body); + DisplayDebugMessage(string("VectorAudio message: ") + res->body); if (!getTransmit) { - DisplayEuroScopeMessage("Connected to " + res->body); + DisplayInfoMessage("Connected to " + res->body); getTransmit = true; continue; } @@ -590,13 +590,13 @@ void CRDFPlugin::VectorAudioMainLoop(void) ProcessRDFQueue(); continue; } - DisplayEuroScopeDebugMessage("HTTP error on MAIN: " + httplib::to_string(res.error())); + DisplayDebugMessage("HTTP error on MAIN: " + httplib::to_string(res.error())); } else { - DisplayEuroScopeDebugMessage("Not connected"); + DisplayDebugMessage("Not connected"); } if (getTransmit) { - DisplayEuroScopeMessage("VectorAudio disconnected"); + DisplayWarnMessage("VectorAudio disconnected"); } getTransmit = false; } @@ -606,6 +606,7 @@ void CRDFPlugin::VectorAudioTXRXLoop(void) { threadTXRXRunning = true; threadTXRXClosed = false; + bool suppressEmpty = false; while (true) { for (int sleepRemain = retryInterval * 1000; sleepRemain > 0;) { if (threadTXRXRunning) { @@ -622,24 +623,42 @@ void CRDFPlugin::VectorAudioTXRXLoop(void) httplib::Client cli("http://" + addressVectorAudio); cli.set_connection_timeout(0, connectionTimeout * 1000); + bool isActive = false; // false when no active station, for warning if (auto res = cli.Get(VECTORAUDIO_PARAM_TX)) { if (res->status == 200) { - DisplayEuroScopeDebugMessage(string("VectorAudio message on TX: ") + res->body); + DisplayDebugMessage(string("VectorAudio message on TX: ") + res->body); + isActive = isActive || res->body.size(); UpdateVectorAudioChannels(res->body, true); } else { - DisplayEuroScopeDebugMessage("HTTP error on TX: " + httplib::to_string(res.error())); + DisplayDebugMessage("HTTP error on TX: " + httplib::to_string(res.error())); + suppressEmpty = true; } } + else { + suppressEmpty = true; + } if (auto res = cli.Get(VECTORAUDIO_PARAM_RX)) { if (res->status == 200) { - DisplayEuroScopeDebugMessage(string("VectorAudio message on RX: ") + res->body); + DisplayDebugMessage(string("VectorAudio message on RX: ") + res->body); + isActive = isActive || res->body.size(); UpdateVectorAudioChannels(res->body, false); } else { - DisplayEuroScopeDebugMessage("HTTP error on RX: " + httplib::to_string(res.error())); + DisplayDebugMessage("HTTP error on RX: " + httplib::to_string(res.error())); + suppressEmpty = true; } } + else { + suppressEmpty = true; + } + if (!isActive && !suppressEmpty) { + DisplayWarnMessage("No active stations in VecterAudio! Please check configuration."); + suppressEmpty = true; + } + else if (isActive) { + suppressEmpty = false; + } } } @@ -649,7 +668,7 @@ CRadarScreen* CRDFPlugin::OnRadarScreenCreated(const char* sDisplayName, bool CanBeSaved, bool CanBeCreated) { - DisplayEuroScopeMessage(string("Radio Direction Finder plugin activated on ") + sDisplayName); + DisplayInfoMessage(string("Radio Direction Finder plugin activated on ") + sDisplayName); return new CRDFScreen(this); } @@ -671,7 +690,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) char bufferAddr[128] = { 0 }; if (sscanf_s(cmd.c_str(), ".RDF ADDRESS %s", bufferAddr, sizeof(bufferAddr)) == 1) { addressVectorAudio = string(bufferAddr); - DisplayEuroScopeMessage(string("Address: ") + addressVectorAudio); + DisplayInfoMessage(string("Address: ") + addressVectorAudio); SaveDataToSettings(SETTING_VECTORAUDIO_ADDRESS, "VectorAudio address", addressVectorAudio.c_str()); return true; } @@ -680,7 +699,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF TIMEOUT %d", &bufferTimeout) == 1) { if (bufferTimeout >= 100 && bufferTimeout <= 1000) { connectionTimeout = bufferTimeout; - DisplayEuroScopeMessage(string("Timeout: ") + to_string(connectionTimeout)); + DisplayInfoMessage(string("Timeout: ") + to_string(connectionTimeout)); SaveDataToSettings(SETTING_VECTORAUDIO_TIMEOUT, "VectorAudio timeout", to_string(connectionTimeout).c_str()); return true; } @@ -690,7 +709,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF POLL %d", &bufferPollInterval) == 1) { if (bufferPollInterval >= 100) { pollInterval = bufferPollInterval; - DisplayEuroScopeMessage(string("Poll interval: ") + to_string(bufferPollInterval)); + DisplayInfoMessage(string("Poll interval: ") + to_string(bufferPollInterval)); SaveDataToSettings(SETTING_VECTORAUDIO_POLL_INTERVAL, "VectorAudio poll interval", to_string(pollInterval).c_str()); return true; } @@ -700,7 +719,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF RETRY %d", &bufferRetryInterval) == 1) { if (bufferRetryInterval >= 1) { retryInterval = bufferRetryInterval; - DisplayEuroScopeMessage(string("Retry interval: ") + to_string(retryInterval)); + DisplayInfoMessage(string("Retry interval: ") + to_string(retryInterval)); SaveDataToSettings(SETTING_VECTORAUDIO_RETRY_INTERVAL, "VectorAudio retry interval", to_string(retryInterval).c_str()); return true; } @@ -712,7 +731,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) GetRGB(rdfRGB, bufferRGB); if (rdfRGB != prevRGB) { SaveDataToSettings(SETTING_RGB, "RGB", bufferRGB); - DisplayEuroScopeMessage((string("RGB: ") + bufferRGB).c_str()); + DisplayInfoMessage((string("RGB: ") + bufferRGB).c_str()); return true; } } @@ -721,7 +740,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) GetRGB(rdfConcurrentTransmissionRGB, bufferRGB); if (rdfConcurrentTransmissionRGB != prevRGB) { SaveDataToSettings(SETTING_CONCURRENT_RGB, "Concurrent RGB", bufferRGB); - DisplayEuroScopeMessage((string("Concurrent RGB: ") + bufferRGB).c_str()); + DisplayInfoMessage((string("Concurrent RGB: ") + bufferRGB).c_str()); return true; } } @@ -730,14 +749,14 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF RADIUS %d", &bufferRadius) == 1) { if (bufferRadius > 0) { circleRadius = bufferRadius; - DisplayEuroScopeMessage(string("Radius: ") + to_string(circleRadius)); + DisplayInfoMessage(string("Radius: ") + to_string(circleRadius)); SaveDataToSettings(SETTING_CIRCLE_RADIUS, "Radius", to_string(circleRadius).c_str()); return true; } } if (sscanf_s(cmd.c_str(), ".RDF THRESHOLD %d", &circleThreshold) == 1) { - DisplayEuroScopeMessage(string("Threshold: ") + to_string(circleThreshold)); + DisplayInfoMessage(string("Threshold: ") + to_string(circleThreshold)); SaveDataToSettings(SETTING_THRESHOLD, "Threshold", to_string(circleThreshold).c_str()); return true; } @@ -746,20 +765,20 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF PRECISION %d", &bufferPrecision) == 1) { if (bufferPrecision >= 0) { circlePrecision = bufferPrecision; - DisplayEuroScopeMessage(string("Precision: ") + to_string(circlePrecision)); + DisplayInfoMessage(string("Precision: ") + to_string(circlePrecision)); SaveDataToSettings(SETTING_PRECISION, "Precision", to_string(circlePrecision).c_str()); return true; } } if (sscanf_s(cmd.c_str(), ".RDF ALTITUDE L%d", &lowAltitude) == 1) { - DisplayEuroScopeMessage(string("Altitude (low): ") + to_string(lowAltitude)); + DisplayInfoMessage(string("Altitude (low): ") + to_string(lowAltitude)); SaveDataToSettings(SETTING_LOW_ALTITUDE, "Altitude (low)", to_string(lowAltitude).c_str()); return true; } if (sscanf_s(cmd.c_str(), ".RDF ALTITUDE H%d", &highAltitude) == 1) { - DisplayEuroScopeMessage(string("Altitude (high): ") + to_string(highAltitude)); + DisplayInfoMessage(string("Altitude (high): ") + to_string(highAltitude)); SaveDataToSettings(SETTING_HIGH_ALTITUDE, "Altitude (high)", to_string(highAltitude).c_str()); return true; } @@ -767,7 +786,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF PRECISION L%d", &bufferPrecision) == 1) { if (bufferPrecision >= 0) { lowPrecision = bufferPrecision; - DisplayEuroScopeMessage(string("Precision (low): ") + to_string(lowPrecision)); + DisplayInfoMessage(string("Precision (low): ") + to_string(lowPrecision)); SaveDataToSettings(SETTING_LOW_PRECISION, "Precision (low)", to_string(lowPrecision).c_str()); return true; } @@ -776,7 +795,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) if (sscanf_s(cmd.c_str(), ".RDF PRECISION H%d", &bufferPrecision) == 1) { if (bufferPrecision >= 0) { highPrecision = bufferPrecision; - DisplayEuroScopeMessage(string("Precision (high): ") + to_string(highPrecision)); + DisplayInfoMessage(string("Precision (high): ") + to_string(highPrecision)); SaveDataToSettings(SETTING_HIGH_PRECISION, "Precision (high)", to_string(highPrecision).c_str()); return true; } @@ -785,7 +804,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) int bufferCtrl; if (sscanf_s(cmd.c_str(), ".RDF CONTROLLER %d", &bufferCtrl) == 1) { drawController = bufferCtrl; - DisplayEuroScopeMessage(string("Draw controllers: ") + to_string(drawController)); + DisplayInfoMessage(string("Draw controllers: ") + to_string(drawController)); SaveDataToSettings(SETTING_DRAW_CONTROLLERS, "Draw controllers", to_string(bufferCtrl).c_str()); return true; } @@ -793,7 +812,7 @@ bool CRDFPlugin::OnCompileCommand(const char* sCommandLine) } catch (const std::exception& e) { - DisplayEuroScopeDebugMessage(e.what()); + DisplayWarnMessage(e.what()); } return false; } diff --git a/RDFPlugin/CRDFPlugin.h b/RDFPlugin/CRDFPlugin.h index 86908a1..ca3a230 100644 --- a/RDFPlugin/CRDFPlugin.h +++ b/RDFPlugin/CRDFPlugin.h @@ -96,16 +96,20 @@ class CRDFPlugin : public EuroScopePlugIn::CPlugIn void UpdateVectorAudioChannels(string line, bool mode_tx); void ToggleChannels(CGrountToAirChannel Channel, int tx = -1, int rx = -1); - inline void DisplayEuroScopeDebugMessage(string msg) { + inline void DisplayDebugMessage(string msg) { #ifdef _DEBUG DisplayUserMessage("RDF-DEBUG", "", msg.c_str(), true, true, true, false, false); #endif // _DEBUG } - inline void DisplayEuroScopeMessage(string msg) { + inline void DisplayInfoMessage(string msg) { DisplayUserMessage("Message", "RDF Plugin", msg.c_str(), false, false, false, false, false); } + inline void DisplayWarnMessage(string msg) { + DisplayUserMessage("Message", "RDF Plugin", msg.c_str(), true, true, true, false, false); + } + public: CRDFPlugin(); virtual ~CRDFPlugin(); diff --git a/README.md b/README.md index 2470fef..e1f4651 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,23 @@ # RDF -## Support VectorAudio/0.5.0+ +## Support *VectorAudio*/0.5.0+ -[VectorAudio](https://github.com/pierr3/VectorAudio) is an Audio-For-VATSIM ATC client for macOS and Linux. It provides better audio quality when EuroScope is running in a Windows virtual machine. This improved RDF plugin utilizes VectorAudio's SDK and sends HTTP GET request every second to get transmitting pilots *and controllers*. +[*VectorAudio*](https://github.com/pierr3/*) is an officially recognized multi-platform Audio-For-VATSIM ATC client for Windows, macOS and Linux. This improved RDF plugin utilizes *VectorAudio*'s SDK and sends HTTP GET requests to get transmitting stations and RX/TX status. ## More Customizations + Random radio direction offsets to simulate measuring errors in real life. -+ Customizable circle radius in nautical miles (instead of pixels) meanwhile radius follows precision. ++ Simulate precision via variable circle radius in nautical miles. + Hide radio-direction-finders for low altitude aircrafts. + (Old feature) RGB settings for circle or line, and different color for concurrent transmission. +## Integrate afv-bridge + +Do the same work in [*afv-euroscope-bridge*](https://github.com/AndyTWF/afv-euroscope-bridge). Supports both *VectorAudio* and *Audio for VATSIM standalone client*. When a new radio station is set up for RX/TX, RDF detects its status and toggles respective channels in EuroScope. + ++ For *Audio for VATSIM standalone client*, toggles are made instantly. ++ For *VectorAudio*, the status are updated in an interval of **VectorAudioRetryInterval**, by default 5 seconds. See below. + ## Configurations There are two ways to modify the plugin settings - by settings file and by commandline functions. @@ -59,12 +66,12 @@ END ``` + **VectorAudioAddress** should include address and port only. E.g. 127.0.0.1:49080 or localhost:49080, etc. -+ **VectorAudioTimeout** is in milliseconds. For VectorAudio HTTP requests. -+ **VectorAudioPollInterval** is in milliseconds. For VectorAudio normal refresh. -+ **VectorAudioRetryInterval** is in seconds. If the plugin disconnets from VectorAudio, it will attempt to re-establish connection every 5 seconds by default. ++ **VectorAudioTimeout** is in milliseconds. For *VectorAudio* HTTP requests. ++ **VectorAudioPollInterval** is in milliseconds. For *VectorAudio* normal refresh. ++ **VectorAudioRetryInterval** is in seconds. If the plugin disconnets from *VectorAudio*, it will attempt to re-establish connection every 5 seconds by default. This value is also used to update RX/TX channels. + **RGB, ConcurrentTransmissionRGB**, see [Previous README](#installation-and-previous-readme) below. + **Radius, Threshold, Precision, LowAltitude, HighAltitude, LowPrecision, HighPrecision** see [Random Offset Schematic](#random-offset-schematic) below. -+ **DrawControllers** is compatible with both VectorAudio and AFV. Other transimitting controllers will be circled as well but without offset. 0 means OFF and other numeric value means ON. ++ **DrawControllers** is compatible with both *VectorAudio* and *Audio for VATSIM standalone client*. Other transimitting controllers will be circled as well but without offset. 0 means OFF and other numeric value means ON. When EuroScope is running, you can reload settings in *Settings File Setup* and then enter ***".RDF RELOAD"*** (case-insensitive) in command line. @@ -89,14 +96,17 @@ When EuroScope is running, you can reload settings in *Settings File Setup* and ## Known Issues -+ It is possible to crash EuroScope when using TopSky at the same time under certain TopSky settings due to conflicting API method to communicate with AFC standalone client. Goto *TopSkySettings.txt* and add *RDF_Mode=-1* to prevent such cases. ++ EuroScope may crash when using TopSky at the same time with certain TopSky settings due to conflicting API method to communicate with Audio for VATSIM standalone client. Goto *TopSkySettings.txt* and add *RDF_Mode=-1* to prevent such cases. ++ Do not simultaneously load this plugin along with the older version of RDF, or with the original *afv-euroscope-bridge* plugin, which may cause unexpected behavior. ++ *Audio for VATSIM standalone client* doesn't provide callsign for RX/TX, so this plugin has to guess the corresponding callsign and it don't guarantee 100% correct toggles. But it shouldn't affect text receive and transmit function. + When using professional correlation mode (S or C) in EuroScope, it's possible some aircraft won't be radio-direction-found because the plugin doesn't know the callsign for an uncorrelated radar target. + For dual pilot situation where the transmitting pilot logs in as observer, this plugin will try to drop the last character of the observer callsign and find again if this dropped character is between A-Z. This feature may cause inaccurate radio-direction. ## Credits + [pierr3/VectorAudio](https://github.com/pierr3/VectorAudio): initiative. -+ [chembergj/RDF](https://github.com/chembergj/RDF): basic drawings and AFV message handling. ++ [chembergj/RDF](https://github.com/chembergj/RDF): basic drawings. ++ [AndyTWF/afv-euroscope-bridge](https://github.com/AndyTWF/afv-euroscope-bridge): *Audio for VATSIM standalone client* message handling. + [LeoChen98](https://github.com/LeoChen98), [websterzh](https://github.com/websterzh): idea of using HTTP requests. + [yhirose/cpp-httplib](https://github.com/yhirose/cpp-httplib): HTTP library. + [vaccfr/CoFrance](https://github.com/vaccfr/CoFrance): method to use async HTTP requests (deprecated since v1.3.2). From 1238c4771c07061177bd2b47330322da52e1f8e3 Mon Sep 17 00:00:00 2001 From: Kingfu-PD Date: Sat, 9 Sep 2023 23:56:49 +0800 Subject: [PATCH 7/7] update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e1f4651..e8a42a8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Support *VectorAudio*/0.5.0+ -[*VectorAudio*](https://github.com/pierr3/*) is an officially recognized multi-platform Audio-For-VATSIM ATC client for Windows, macOS and Linux. This improved RDF plugin utilizes *VectorAudio*'s SDK and sends HTTP GET requests to get transmitting stations and RX/TX status. +[*VectorAudio*](https://github.com/pierr3/VectorAudio) is an officially recognized multi-platform Audio-For-VATSIM ATC client for Windows, macOS and Linux. This improved RDF plugin utilizes *VectorAudio*'s SDK and sends HTTP GET requests to get transmitting stations and RX/TX status. ## More Customizations @@ -13,10 +13,10 @@ ## Integrate afv-bridge -Do the same work in [*afv-euroscope-bridge*](https://github.com/AndyTWF/afv-euroscope-bridge). Supports both *VectorAudio* and *Audio for VATSIM standalone client*. When a new radio station is set up for RX/TX, RDF detects its status and toggles respective channels in EuroScope. +Do the same work as [*afv-euroscope-bridge*](https://github.com/AndyTWF/afv-euroscope-bridge). Supports both *VectorAudio* and *Audio for VATSIM standalone client*. When a new radio station is set up for RX/TX, RDF detects its status and toggles respective channels in EuroScope. + For *Audio for VATSIM standalone client*, toggles are made instantly. -+ For *VectorAudio*, the status are updated in an interval of **VectorAudioRetryInterval**, by default 5 seconds. See below. ++ For *VectorAudio*, the status are updated at an interval of **VectorAudioRetryInterval**, by default 5 seconds. See below. ## Configurations @@ -96,7 +96,7 @@ When EuroScope is running, you can reload settings in *Settings File Setup* and ## Known Issues -+ EuroScope may crash when using TopSky at the same time with certain TopSky settings due to conflicting API method to communicate with Audio for VATSIM standalone client. Goto *TopSkySettings.txt* and add *RDF_Mode=-1* to prevent such cases. ++ EuroScope may crash when using TopSky at the same time with certain TopSky settings due to conflicting API method to communicate with *Audio for VATSIM standalone client*. Goto *TopSkySettings.txt* and add *RDF_Mode=-1* to prevent such cases. + Do not simultaneously load this plugin along with the older version of RDF, or with the original *afv-euroscope-bridge* plugin, which may cause unexpected behavior. + *Audio for VATSIM standalone client* doesn't provide callsign for RX/TX, so this plugin has to guess the corresponding callsign and it don't guarantee 100% correct toggles. But it shouldn't affect text receive and transmit function. + When using professional correlation mode (S or C) in EuroScope, it's possible some aircraft won't be radio-direction-found because the plugin doesn't know the callsign for an uncorrelated radar target.