diff --git a/vSMR/DatalinkManager.cpp b/vSMR/DatalinkManager.cpp new file mode 100644 index 000000000..3d6f0d160 --- /dev/null +++ b/vSMR/DatalinkManager.cpp @@ -0,0 +1,521 @@ +#include "stdafx.h" +#include "DatalinkManager.hpp" +#include +#include +#include + +DatalinkManager::DatalinkManager(HttpHelper * httpHelper, const string& baseUrl) + : httpHelper(httpHelper), baseUrlDatalink(baseUrl), logonCode(""), logonCallsign(""), messageIdCounter(0) +{ + messageIdCounter = rand() % 10000 + 1789; +} + +DatalinkManager::~DatalinkManager() +{ +} + +void DatalinkManager::setCredentials(const string& code, const string& callsign) +{ + logonCode = code; + logonCallsign = callsign; +} + +void DatalinkManager::setHttpHelper(HttpHelper * helper) +{ + if (helper != NULL) { + httpHelper = helper; + } +} + +int DatalinkManager::getNextMessageId() +{ + return ++messageIdCounter; +} + +bool DatalinkManager::isConnected() const +{ + if (httpHelper == NULL) { + return false; + } + + string url = baseUrlDatalink; + url += "?logon="; + url += logonCode; + url += "&from="; + url += logonCallsign; + url += "&to=SERVER&type=PING"; + + string raw = httpHelper->downloadStringFromURL(url); + + if (raw.substr(0, 2) == "ok") { + return true; + } + return false; +} + +int DatalinkManager::getAndIncrementSequenceNumber(const string& callsign) +{ + lock_guard lock(statesMutex); + + if (aircraftStates.find(callsign) == aircraftStates.end()) { + aircraftStates[callsign] = AircraftDatalinkState(); + aircraftStates[callsign].callsign = callsign; + aircraftStates[callsign].lastSequenceNumber = 0; + aircraftStates[callsign].isWaitingForResponse = false; + aircraftStates[callsign].messageRequestCount = 0; + aircraftStates[callsign].lastPolledTime = 0; + } + + return ++aircraftStates[callsign].lastSequenceNumber; +} + +void DatalinkManager::queueAckForAircraft(const string& callsign, const string& message, MessageType type) +{ + lock_guard lock(statesMutex); + + if (aircraftStates.find(callsign) == aircraftStates.end()) { + aircraftStates[callsign] = AircraftDatalinkState(); + aircraftStates[callsign].callsign = callsign; + aircraftStates[callsign].lastSequenceNumber = 0; + aircraftStates[callsign].isWaitingForResponse = false; + aircraftStates[callsign].messageRequestCount = 0; + aircraftStates[callsign].lastPolledTime = 0; + } + + PendingAck ack; + ack.callsign = callsign; + ack.message = message; + ack.type = type; + ack.sequenceNumber = ++aircraftStates[callsign].lastSequenceNumber; + + // Format timestamp + time_t now = time(NULL); + tm tmnow; + gmtime_s(&tmnow, &now); + char buf[64]; + strftime(buf, sizeof(buf), "%H%MZ", &tmnow); + ack.timestamp = buf; + strftime(buf, sizeof(buf), "%d%m%y", &tmnow); + ack.date = buf; + + aircraftStates[callsign].pendingAcks.push(ack); +} + +string DatalinkManager::buildDatalinkUrl(const string& destination, const string& messageType, const string& packet) +{ + string url = baseUrlDatalink; + url += "?logon="; + url += logonCode; + url += "&from="; + url += logonCallsign; + url += "&to="; + url += destination; + url += "&type="; + url += messageType; + url += "&packet="; + url += packet; + + // URL encode spaces + size_t start_pos = 0; + while ((start_pos = url.find(" ", start_pos)) != std::string::npos) { + url.replace(start_pos, string(" ").length(), "%20"); + start_pos += string("%20").length(); + } + + return url; +} + +string DatalinkManager::buildClearanceUrl(const DatalinkClearance& clearance) +{ + string url = baseUrlDatalink; + url += "?logon="; + url += logonCode; + url += "&from="; + url += logonCallsign; + url += "&to="; + url += clearance.callsign; + url += "&type=CPDLC&packet=/data2/"; + url += std::to_string(getNextMessageId()); + url += "//R/"; + + if (clearance.sid == "CHK" && clearance.rwy == "09R") // CPT 09R + { + url += "@"; + url += clearance.callsign; + url += "@ CLRD TO @"; + url += clearance.destination; + url += "@ OFF RWY @"; + url += clearance.rwy; + url += "@ VIA CPT AFTER DEP CLIMB STRAIGHT AHEAD - AT LON DME 2.0 TURN RIGHT HDG @220@ - CLIMB @6000FT"; + } + else // normal PDC message (UK Format) + { + url += "@"; + url += clearance.callsign; + url += "@ CLRD TO @"; + url += clearance.destination; + url += "@ OFF @"; + url += clearance.rwy; + url += "@ VIA @"; + url += clearance.sid; + url += "@ INIT CLB @"; + url += clearance.climb; + } + url += "@ SQUAWK @"; + url += clearance.squawk; + url += "@ NEXT FREQ @"; + + if (clearance.freq != "no" && clearance.freq.size() > 5) { + url += clearance.freq; + } + + url += "@ "; + + if (clearance.message != "no" && clearance.message.size() > 1) { + url += clearance.message; + } + + // URL encode spaces + size_t start_pos = 0; + while ((start_pos = url.find(" ", start_pos)) != std::string::npos) { + url.replace(start_pos, string(" ").length(), "%20"); + start_pos += string("%20").length(); + } + + return url; +} + +bool DatalinkManager::sendRawDatalinkMessage(const string& url) +{ + if (httpHelper == NULL) { + return false; + } + + string raw = httpHelper->downloadStringFromURL(url); + + if (raw.substr(0, 2) == "ok") { + return true; + } + return false; +} + +bool DatalinkManager::login(const string& code, const string& callsign) +{ + setCredentials(code, callsign); + return isConnected(); +} + +void DatalinkManager::sendAcknowledgement(const string& callsign, const string& message) +{ + queueAckForAircraft(callsign, message, MessageType::CPDLC); + + lock_guard lock(statesMutex); + if (aircraftStates.find(callsign) != aircraftStates.end() && + !aircraftStates[callsign].pendingAcks.empty()) { + + const PendingAck& ack = aircraftStates[callsign].pendingAcks.front(); + + string fullMessage = message; + fullMessage += " "; + fullMessage += ack.timestamp; + fullMessage += " "; + fullMessage += ack.date; + + string packet = "/data2/"; + packet += std::to_string(getNextMessageId()); + packet += "//"; + packet += "NE"; // No expected response + packet += "/"; + packet += fullMessage; + + string url = buildDatalinkUrl(callsign, "CPDLC", packet); + + if (sendRawDatalinkMessage(url)) { + aircraftStates[callsign].pendingAcks.pop(); + } + } +} + +void DatalinkManager::sendSimpleCpdlcMessage(const string& callsign, const string& message, const string& responses) +{ + string packet = "/data2/"; + packet += std::to_string(getNextMessageId()); + packet += "//"; + packet += responses; + packet += "/"; + packet += message; + + string url = buildDatalinkUrl(callsign, "CPDLC", packet); + sendRawDatalinkMessage(url); +} + +void DatalinkManager::sendDatalinkClearance(const DatalinkClearance& clearance) +{ + string url = buildClearanceUrl(clearance); + + if (sendRawDatalinkMessage(url)) { + { + lock_guard lock(listsMutex); + + // Remove from demanding clearance list + auto it = std::find(aircraftDemandingClearance.begin(), aircraftDemandingClearance.end(), clearance.callsign); + if (it != aircraftDemandingClearance.end()) { + aircraftDemandingClearance.erase(it); + } + + // Remove from standby list + it = std::find(aircraftStandby.begin(), aircraftStandby.end(), clearance.callsign); + if (it != aircraftStandby.end()) { + aircraftStandby.erase(it); + } + + // Add to message sent list + aircraftMessageSent.push_back(clearance.callsign); + } + + // Remove pending message + removePendingMessage(clearance.callsign); + + // Queue a status ACK + queueAckForAircraft(clearance.callsign, "CLEARANCE SENT", MessageType::CPDLC); + } +} + +void DatalinkManager::pollMessages() +{ + if (httpHelper == NULL) { + return; + } + + string url = baseUrlDatalink; + url += "?logon="; + url += logonCode; + url += "&from="; + url += logonCallsign; + url += "&to=SERVER&type=POLL"; + + string raw = httpHelper->downloadStringFromURL(url); + + if (raw.substr(0, 2) != "ok" || raw.size() <= 3) { + return; + } + + raw = raw + " "; + raw = raw.substr(3, raw.size() - 3); + + string delimiter = "}} "; + size_t pos = 0; + while ((pos = raw.find(delimiter)) != std::string::npos) { + string token = raw.substr(1, pos); + + string parsed; + stringstream input_stringstream(token); + AcarsMessage message; + int i = 1; + while (getline(input_stringstream, parsed, ' ')) { + if (i == 1) { + message.from = parsed; + } + if (i == 2) { + message.type = parsed; + } + if (i > 2) { + message.message.append(" "); + message.message.append(parsed); + } + + i++; + } + + if (message.type.find("telex") != std::string::npos || message.type.find("cpdlc") != std::string::npos) { + parseAndQueueIncomingMessage(message); + setPendingMessage(message.from, message); + } + + raw.erase(0, pos + delimiter.length()); + } +} + +void DatalinkManager::parseAndQueueIncomingMessage(const AcarsMessage& message) +{ + lock_guard lock(listsMutex); + + if (message.message.find("REQ") != std::string::npos || + message.message.find("CLR") != std::string::npos || + message.message.find("PDC") != std::string::npos || + message.message.find("PREDEP") != std::string::npos || + message.message.find("REQUEST") != std::string::npos) { + + if (message.message.find("LOGON") != std::string::npos) { + sendSimpleCpdlcMessage(message.from, "UNABLE", "NE"); + } else { + addAircraftDemandingClearance(message.from); + + // Queue acknowledgement for departure message request + string reqAck = "DEPART MESSAGE REQUEST RECEIVED"; + queueAckForAircraft(message.from, reqAck, MessageType::CPDLC); + } + } + else if (message.message.find("WILCO") != std::string::npos || + message.message.find("ROGER") != std::string::npos || + message.message.find("RGR") != std::string::npos || + message.message.find("ACCEPT") != std::string::npos) { + + if (std::find(aircraftMessageSent.begin(), aircraftMessageSent.end(), message.from) != aircraftMessageSent.end()) { + addAircraftWilco(message.from); + + // Queue acknowledgement for clearance acceptance + string clearanceAck = "DEPART MESSAGE ACK RECEIVED"; + queueAckForAircraft(message.from, clearanceAck, MessageType::CPDLC); + } + } + else if (message.message.length() != 0) { + addAircraftMessage(message.from); + } +} + +// Aircraft state management methods +void DatalinkManager::addAircraftDemandingClearance(const string& callsign) +{ + lock_guard lock(listsMutex); + if (std::find(aircraftDemandingClearance.begin(), aircraftDemandingClearance.end(), callsign) == aircraftDemandingClearance.end()) { + aircraftDemandingClearance.push_back(callsign); + } +} + +void DatalinkManager::removeAircraftDemandingClearance(const string& callsign) +{ + lock_guard lock(listsMutex); + auto it = std::find(aircraftDemandingClearance.begin(), aircraftDemandingClearance.end(), callsign); + if (it != aircraftDemandingClearance.end()) { + aircraftDemandingClearance.erase(it); + } +} + +bool DatalinkManager::isAircraftDemandingClearance(const string& callsign) const +{ + lock_guard lock(listsMutex); + return std::find(aircraftDemandingClearance.begin(), aircraftDemandingClearance.end(), callsign) != aircraftDemandingClearance.end(); +} + +void DatalinkManager::addAircraftMessage(const string& callsign) +{ + lock_guard lock(listsMutex); + if (std::find(aircraftMessage.begin(), aircraftMessage.end(), callsign) == aircraftMessage.end()) { + aircraftMessage.push_back(callsign); + } +} + +void DatalinkManager::removeAircraftMessage(const string& callsign) +{ + lock_guard lock(listsMutex); + auto it = std::find(aircraftMessage.begin(), aircraftMessage.end(), callsign); + if (it != aircraftMessage.end()) { + aircraftMessage.erase(it); + } +} + +void DatalinkManager::addAircraftWilco(const string& callsign) +{ + lock_guard lock(listsMutex); + if (std::find(aircraftWilco.begin(), aircraftWilco.end(), callsign) == aircraftWilco.end()) { + aircraftWilco.push_back(callsign); + } +} + +bool DatalinkManager::hasAircraftWilco(const string& callsign) const +{ + lock_guard lock(listsMutex); + return std::find(aircraftWilco.begin(), aircraftWilco.end(), callsign) != aircraftWilco.end(); +} + +void DatalinkManager::addAircraftStandby(const string& callsign) +{ + lock_guard lock(listsMutex); + if (std::find(aircraftStandby.begin(), aircraftStandby.end(), callsign) == aircraftStandby.end()) { + aircraftStandby.push_back(callsign); + } +} + +void DatalinkManager::removeAircraftStandby(const string& callsign) +{ + lock_guard lock(listsMutex); + auto it = std::find(aircraftStandby.begin(), aircraftStandby.end(), callsign); + if (it != aircraftStandby.end()) { + aircraftStandby.erase(it); + } +} + +void DatalinkManager::addAircraftMessageSent(const string& callsign) +{ + lock_guard lock(listsMutex); + if (std::find(aircraftMessageSent.begin(), aircraftMessageSent.end(), callsign) == aircraftMessageSent.end()) { + aircraftMessageSent.push_back(callsign); + } +} + +bool DatalinkManager::isAircraftMessageSent(const string& callsign) const +{ + lock_guard lock(listsMutex); + return std::find(aircraftMessageSent.begin(), aircraftMessageSent.end(), callsign) != aircraftMessageSent.end(); +} + +void DatalinkManager::setPendingMessage(const string& callsign, const AcarsMessage& message) +{ + lock_guard lock(listsMutex); + pendingMessages[callsign] = message; +} + +bool DatalinkManager::hasPendingMessage(const string& callsign) const +{ + lock_guard lock(listsMutex); + return pendingMessages.find(callsign) != pendingMessages.end(); +} + +AcarsMessage DatalinkManager::getPendingMessage(const string& callsign) const +{ + lock_guard lock(listsMutex); + if (pendingMessages.find(callsign) != pendingMessages.end()) { + return pendingMessages.at(callsign); + } + return AcarsMessage(); +} + +void DatalinkManager::removePendingMessage(const string& callsign) +{ + lock_guard lock(listsMutex); + auto it = pendingMessages.find(callsign); + if (it != pendingMessages.end()) { + pendingMessages.erase(it); + } +} + +vector DatalinkManager::getAircraftDemandingClearance() const +{ + lock_guard lock(listsMutex); + return aircraftDemandingClearance; +} + +vector DatalinkManager::getAircraftWilco() const +{ + lock_guard lock(listsMutex); + return aircraftWilco; +} + +vector DatalinkManager::getAircraftMessageSent() const +{ + lock_guard lock(listsMutex); + return aircraftMessageSent; +} + +vector DatalinkManager::getAircraftMessage() const +{ + lock_guard lock(listsMutex); + return aircraftMessage; +} + +vector DatalinkManager::getAircraftStandby() const +{ + lock_guard lock(listsMutex); + return aircraftStandby; +} diff --git a/vSMR/DatalinkManager.hpp b/vSMR/DatalinkManager.hpp new file mode 100644 index 000000000..279c603e7 --- /dev/null +++ b/vSMR/DatalinkManager.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "HttpHelper.hpp" + +using namespace std; + +// Message types +enum class MessageType { + CPDLC, + TELEX +}; + +// Pending ACK structure with sequence tracking +struct PendingAck { + string callsign; + string message; + MessageType type; + int sequenceNumber; + string timestamp; + string date; +}; + +// Incoming ACARS message structure +struct AcarsMessage { + string from; + string type; + string message; +}; + +// Aircraft state structure +struct AircraftDatalinkState { + string callsign; + int lastSequenceNumber; + queue pendingAcks; + AcarsMessage lastMessage; + bool isWaitingForResponse; + int messageRequestCount; + int lastPolledTime; +}; + +// Clearance data structure +struct DatalinkClearance { + string callsign; + string destination; + string sid; + string rwy; + string freq; + string ctot; + string asat; + string squawk; + string message; + string climb; +}; + +class DatalinkManager +{ +private: + HttpHelper * httpHelper; + string baseUrlDatalink; + string logonCode; + string logonCallsign; + int messageIdCounter; + + // Per-aircraft state tracking + map aircraftStates; + + // Lists for tracking aircraft status + vector aircraftDemandingClearance; + vector aircraftMessageSent; + vector aircraftMessage; + vector aircraftWilco; + vector aircraftStandby; + map pendingMessages; + + // Thread safety + mutable mutex statesMutex; + mutable mutex listsMutex; + + // Private helper methods + void parseAndQueueIncomingMessage(const AcarsMessage& message); + void queueAckForAircraft(const string& callsign, const string& message, MessageType type); + int getAndIncrementSequenceNumber(const string& callsign); + string buildDatalinkUrl(const string& destination, const string& messageType, const string& packet); + string buildClearanceUrl(const DatalinkClearance& clearance); + bool sendRawDatalinkMessage(const string& url); + +public: + DatalinkManager(HttpHelper * httpHelper, const string& baseUrl); + ~DatalinkManager(); + + // Initialization and connection + bool login(const string& logonCode, const string& logonCallsign); + void setCredentials(const string& code, const string& callsign); + void setHttpHelper(HttpHelper * helper); + + // Message sending + void sendAcknowledgement(const string& callsign, const string& message); + void sendSimpleCpdlcMessage(const string& callsign, const string& message, const string& responses); + void sendDatalinkClearance(const DatalinkClearance& clearance); + + // Message polling + void pollMessages(); + + // Aircraft state management + void addAircraftDemandingClearance(const string& callsign); + void removeAircraftDemandingClearance(const string& callsign); + bool isAircraftDemandingClearance(const string& callsign) const; + + void addAircraftMessage(const string& callsign); + void removeAircraftMessage(const string& callsign); + + void addAircraftWilco(const string& callsign); + bool hasAircraftWilco(const string& callsign) const; + + void addAircraftStandby(const string& callsign); + void removeAircraftStandby(const string& callsign); + + void addAircraftMessageSent(const string& callsign); + bool isAircraftMessageSent(const string& callsign) const; + + // Pending message management + void setPendingMessage(const string& callsign, const AcarsMessage& message); + bool hasPendingMessage(const string& callsign) const; + AcarsMessage getPendingMessage(const string& callsign) const; + void removePendingMessage(const string& callsign); + + // Getters for status information + vector getAircraftDemandingClearance() const; + vector getAircraftWilco() const; + vector getAircraftMessageSent() const; + vector getAircraftMessage() const; + vector getAircraftStandby() const; + + // Utility methods + int getNextMessageId(); + bool isConnected() const; +}; diff --git a/vSMR/SMRPlugin.cpp b/vSMR/SMRPlugin.cpp index 11078d713..434abb4f5 100644 --- a/vSMR/SMRPlugin.cpp +++ b/vSMR/SMRPlugin.cpp @@ -17,42 +17,8 @@ bool BLINK = false; bool PlaySoundClr = false; -struct DatalinkPacket { - string callsign; - string destination; - string sid; - string rwy; - string freq; - string ctot; - string asat; - string squawk; - string message; - string climb; -}; - -DatalinkPacket DatalinkToSend; - string baseUrlDatalink = "http://www.hoppie.nl/acars/system/connect.html"; -struct AcarsMessage { - string from; - string type; - string message; -}; - -vector AircraftDemandingClearance; -vector AircraftMessageSent; -vector AircraftMessage; -vector AircraftWilco; -vector AircraftStandby; -map PendingMessages; - -string tmessage; -string tdest; -string ttype; - -int messageId = 0; - clock_t timer; int pollInterval = 45 + rand() % 31; // Random interval between 45-75 seconds @@ -79,223 +45,16 @@ static void formatNowTimeDate(string & timeStr, string &dateStr) dateStr = buf; } -void createPlainCpdlcMessage(const char *message, const char *responses) { - ttype = "CPDLC"; - tmessage = "/data2/"; - tmessage += std::to_string(++messageId); - tmessage += "//"; - tmessage += responses; - tmessage += "/"; - tmessage += message; -} - -void datalinkLogin(void * arg) { - string raw; - string url = baseUrlDatalink; - url += "?logon="; - url += logonCode; - url += "&from="; - url += logonCallsign; - url += "&to=SERVER&type=PING"; - raw.assign(httpHelper->downloadStringFromURL(url)); - - if (startsWith("ok", raw.c_str())) { - HoppieConnected = true; - ConnectionMessage = true; - } - else { - FailedToConnectMessage = true; - } -}; - -void sendDatalinkMessage(void * arg) { - - string raw; - string url = baseUrlDatalink; - url += "?logon="; - url += logonCode; - url += "&from="; - url += logonCallsign; - url += "&to="; - url += tdest; - url += "&type="; - url += ttype; - url += "&packet="; - url += tmessage; - - size_t start_pos = 0; - while ((start_pos = url.find(" ", start_pos)) != std::string::npos) { - url.replace(start_pos, string(" ").length(), "%20"); - start_pos += string("%20").length(); - } - - raw.assign(httpHelper->downloadStringFromURL(url)); - - if (startsWith("ok", raw.c_str())) { - if (std::find(AircraftMessage.begin(), AircraftMessage.end(), tdest.c_str()) != AircraftMessage.end()) { - AircraftMessage.erase(std::remove(AircraftMessage.begin(), AircraftMessage.end(), tdest.c_str()), AircraftMessage.end()); - } - } -}; - -void pollMessages(void * arg) { - string raw = ""; - string url = baseUrlDatalink; - url += "?logon="; - url += logonCode; - url += "&from="; - url += logonCallsign; - url += "&to=SERVER&type=POLL"; - raw.assign(httpHelper->downloadStringFromURL(url)); - - if (!startsWith("ok", raw.c_str()) || raw.size() <= 3) - return; - - raw = raw + " "; - raw = raw.substr(3, raw.size() - 3); - - string delimiter = "}} "; - size_t pos = 0; - std::string token; - while ((pos = raw.find(delimiter)) != std::string::npos) { - token = raw.substr(1, pos); - - string parsed; - stringstream input_stringstream(token); - struct AcarsMessage message; - int i = 1; - while (getline(input_stringstream, parsed, ' ')) - { - if (i == 1) - message.from = parsed; - if (i == 2) - message.type = parsed; - if (i > 2) - { - message.message.append(" "); - message.message.append(parsed); - } - - i++; - } - if (message.type.find("telex") != std::string::npos || message.type.find("cpdlc") != std::string::npos) { - if (message.message.find("REQ") != std::string::npos || message.message.find("CLR") != std::string::npos || message.message.find("PDC") != std::string::npos || message.message.find("PREDEP") != std::string::npos || message.message.find("REQUEST") != std::string::npos) { - if (message.message.find("LOGON") != std::string::npos) { - createPlainCpdlcMessage("UNABLE", "NE"); - tdest = message.from; - _beginthread(sendDatalinkMessage, 0, NULL); - } else { - if (PlaySoundClr) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - PlaySound(MAKEINTRESOURCE(IDR_WAVE1), AfxGetInstanceHandle(), SND_RESOURCE | SND_ASYNC); - } - AircraftDemandingClearance.push_back(message.from); - string timeS, dateS; - formatNowTimeDate(timeS, dateS); - // Send ack - string reqAck = "DEPART MESSAGE REQUEST RECEIVED "; - reqAck += timeS + " " + dateS; - reqAck += " REQUEST BEING PROCESSED"; - createPlainCpdlcMessage(reqAck.c_str(), "NE"); - tdest = message.from; - _beginthread(sendDatalinkMessage, 0, NULL); - } - } - else if (message.message.find("WILCO") != std::string::npos || message.message.find("ROGER") != std::string::npos || message.message.find("RGR") != std::string::npos || message.message.find("ACCEPT") != std::string::npos) { - if (std::find(AircraftMessageSent.begin(), AircraftMessageSent.end(), message.from) != AircraftMessageSent.end()) { - AircraftWilco.push_back(message.from); - string timeS, dateS; - formatNowTimeDate(timeS, dateS); - // send ack - string clearanceAck = "DEPART MESSAGE ACK RECEIVED "; - clearanceAck += timeS + " " + dateS; - clearanceAck += " CLEARANCE CONFIRMED"; - createPlainCpdlcMessage(clearanceAck.c_str(), "NE"); - _beginthread(sendDatalinkMessage, 0, NULL); - } - } - else if (message.message.length() != 0 ){ - AircraftMessage.push_back(message.from); - } - PendingMessages[message.from] = message; - } - - raw.erase(0, pos + delimiter.length()); - } - - -}; - -void sendDatalinkClearance(void * arg) { - string raw; - string url = baseUrlDatalink; - url += "?logon="; - url += logonCode; - url += "&from="; - url += logonCallsign; - url += "&to="; - url += DatalinkToSend.callsign; - url += "&type=CPDLC&packet=/data2/"; - messageId++; - url += std::to_string(messageId); - url += "//R/"; -if (DatalinkToSend.sid == "CHK" && DatalinkToSend.rwy == "09R") // CPT 09R - { - url += "@"; - url += DatalinkToSend.callsign; - url += "@ CLRD TO @"; - url += DatalinkToSend.destination; - url += "@ OFF RWY @"; - url += DatalinkToSend.rwy; - url += "@ VIA CPT AFTER DEP CLIMB STRAIGHT AHEAD - AT LON DME 2.0 TURN RIGHT HDG @220@ - CLIMB @6000FT"; - - } - else // normal PDC message (UK Format) - { - url += "@"; - url += DatalinkToSend.callsign; - url += "@ CLRD TO @"; - url += DatalinkToSend.destination; - url += "@ OFF @"; - url += DatalinkToSend.rwy; - url += "@ VIA @"; - url += DatalinkToSend.sid; - url += "@ INIT CLB @"; - url += DatalinkToSend.climb; - } - url += "@ SQUAWK @"; - url += DatalinkToSend.squawk; - url += "@ NEXT FREQ @"; - if (DatalinkToSend.freq != "no" && DatalinkToSend.freq.size() > 5) - {url += DatalinkToSend.freq;} - else - {url += myfrequency;} - - url += "@ "; - - if (DatalinkToSend.message != "no" && DatalinkToSend.message.size() > 1) - url += DatalinkToSend.message; - - size_t start_pos = 0; - while ((start_pos = url.find(" ", start_pos)) != std::string::npos) { - url.replace(start_pos, string(" ").length(), "%20"); - start_pos += string("%20").length(); - } - - raw.assign(httpHelper->downloadStringFromURL(url)); - if (startsWith("ok", raw.c_str())) { - if (std::find(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), DatalinkToSend.callsign.c_str()) != AircraftDemandingClearance.end()) { - AircraftDemandingClearance.erase(std::remove(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), DatalinkToSend.callsign.c_str()), AircraftDemandingClearance.end()); - } - if (std::find(AircraftStandby.begin(), AircraftStandby.end(), DatalinkToSend.callsign.c_str()) != AircraftStandby.end()) { - AircraftStandby.erase(std::remove(AircraftStandby.begin(), AircraftStandby.end(), DatalinkToSend.callsign.c_str()), AircraftStandby.end()); - } - if (PendingMessages.find(DatalinkToSend.callsign) != PendingMessages.end()) - PendingMessages.erase(DatalinkToSend.callsign); - AircraftMessageSent.push_back(DatalinkToSend.callsign.c_str()); - } -}; +static std::string CStringToStdString(const CString& s) +{ +#ifdef UNICODE + std::wstring ws = s.GetString(); + return std::string(ws.begin(), ws.end()); +#else + return std::string((LPCTSTR)s); +#endif +} CSMRPlugin::CSMRPlugin(void) :CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PLUGIN_NAME, MY_PLUGIN_VERSION, MY_PLUGIN_DEVELOPER, MY_PLUGIN_COPYRIGHT) { @@ -315,13 +74,14 @@ CSMRPlugin::CSMRPlugin(void) :CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PL RegisterTagItemFunction("Update acknowledged QNH", TAG_FUNC_QNH_UPDATE); RegisterTagItemFunction("Reset acknowledged QNH", TAG_FUNC_QNH_RESET); - messageId = rand() % 10000 + 1789; - timer = clock(); if (httpHelper == NULL) httpHelper = new HttpHelper(); + // Initialize DatalinkManager + datalinkManager = new DatalinkManager(httpHelper, baseUrlDatalink); + const char * p_value; if ((p_value = GetDataFromSettings("cpdlc_logon")) != NULL) @@ -331,6 +91,9 @@ CSMRPlugin::CSMRPlugin(void) :CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, MY_PL if ((p_value = GetDataFromSettings("cpdlc_sound")) != NULL) PlaySoundClr = bool(!!atoi(p_value)); + // Set credentials in DatalinkManager + datalinkManager->setCredentials(logonCode, logonCallsign); + char DllPathFile[_MAX_PATH]; string DllPath; @@ -356,6 +119,12 @@ CSMRPlugin::~CSMRPlugin() temp = 1; SaveDataToSettings("cpdlc_sound", "Play sound on clearance request", std::to_string(temp).c_str()); + // Clean up DatalinkManager + if (datalinkManager != nullptr) { + delete datalinkManager; + datalinkManager = nullptr; + } + try { io_service.stop(); @@ -379,7 +148,13 @@ bool CSMRPlugin::OnCompileCommand(const char * sCommandLine) { { if (ControllerMyself().IsController()) { if (!HoppieConnected) { - _beginthread(datalinkLogin, 0, NULL); + if (datalinkManager != nullptr && datalinkManager->login(logonCode, logonCallsign)) { + HoppieConnected = true; + ConnectionMessage = true; + } + else { + FailedToConnectMessage = true; + } } else { HoppieConnected = false; @@ -394,8 +169,8 @@ bool CSMRPlugin::OnCompileCommand(const char * sCommandLine) { } else if (startsWith(".smr poll", sCommandLine)) { - if (HoppieConnected) { - _beginthread(pollMessages, 0, NULL); + if (HoppieConnected && datalinkManager != nullptr) { + datalinkManager->pollMessages(); } return true; } @@ -431,6 +206,11 @@ bool CSMRPlugin::OnCompileCommand(const char * sCommandLine) { temp = 1; SaveDataToSettings("cpdlc_sound", "Play sound on clearance request", std::to_string(temp).c_str()); + // Update credentials in DatalinkManager + if (datalinkManager != nullptr) { + datalinkManager->setCredentials(logonCode, logonCallsign); + } + return true; } return false; @@ -438,42 +218,52 @@ bool CSMRPlugin::OnCompileCommand(const char * sCommandLine) { void CSMRPlugin::OnGetTagItem(CFlightPlan FlightPlan, CRadarTarget RadarTarget, int ItemCode, int TagData, char sItemString[16], int * pColorCode, COLORREF * pRGB, double * pFontSize) { if (ItemCode == TAG_ITEM_DATALINK_STS) { - if (FlightPlan.IsValid()) { - if (std::find(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()) != AircraftDemandingClearance.end()) { + if (FlightPlan.IsValid() && datalinkManager != nullptr) { + if (datalinkManager->isAircraftDemandingClearance(FlightPlan.GetCallsign())) { *pColorCode = TAG_COLOR_RGB_DEFINED; if (BLINK) *pRGB = RGB(130, 130, 130); else *pRGB = RGB(255, 255, 0); - if (std::find(AircraftStandby.begin(), AircraftStandby.end(), FlightPlan.GetCallsign()) != AircraftStandby.end()) + vector standby = datalinkManager->getAircraftStandby(); + if (std::find(standby.begin(), standby.end(), FlightPlan.GetCallsign()) != standby.end()) strcpy_s(sItemString, 16, "S"); else strcpy_s(sItemString, 16, "R"); } - else if (std::find(AircraftMessage.begin(), AircraftMessage.end(), FlightPlan.GetCallsign()) != AircraftMessage.end()) { - *pColorCode = TAG_COLOR_RGB_DEFINED; - if (BLINK) - *pRGB = RGB(130, 130, 130); - else - *pRGB = RGB(255, 255, 0); - strcpy_s(sItemString, 16, "T"); - } - else if (std::find(AircraftWilco.begin(), AircraftWilco.end(), FlightPlan.GetCallsign()) != AircraftWilco.end()) { - *pColorCode = TAG_COLOR_RGB_DEFINED; - *pRGB = RGB(0, 176, 0); - strcpy_s(sItemString, 16, "V"); - } - else if (std::find(AircraftMessageSent.begin(), AircraftMessageSent.end(), FlightPlan.GetCallsign()) != AircraftMessageSent.end()) { - *pColorCode = TAG_COLOR_RGB_DEFINED; - *pRGB = RGB(255, 255, 0); - strcpy_s(sItemString, 16, "V"); - } else { - *pColorCode = TAG_COLOR_RGB_DEFINED; - *pRGB = RGB(130, 130, 130); - - strcpy_s(sItemString, 16, "-"); + vector messages = datalinkManager->getAircraftMessage(); + if (std::find(messages.begin(), messages.end(), FlightPlan.GetCallsign()) != messages.end()) { + *pColorCode = TAG_COLOR_RGB_DEFINED; + if (BLINK) + *pRGB = RGB(130, 130, 130); + else + *pRGB = RGB(255, 255, 0); + strcpy_s(sItemString, 16, "T"); + } + else { + vector wilco = datalinkManager->getAircraftWilco(); + if (std::find(wilco.begin(), wilco.end(), FlightPlan.GetCallsign()) != wilco.end()) { + *pColorCode = TAG_COLOR_RGB_DEFINED; + *pRGB = RGB(0, 176, 0); + strcpy_s(sItemString, 16, "V"); + } + else { + vector sent = datalinkManager->getAircraftMessageSent(); + if (std::find(sent.begin(), sent.end(), FlightPlan.GetCallsign()) != sent.end()) { + *pColorCode = TAG_COLOR_RGB_DEFINED; + *pRGB = RGB(255, 255, 0); + strcpy_s(sItemString, 16, "V"); + } + else { + *pColorCode = TAG_COLOR_RGB_DEFINED; + *pRGB = RGB(130, 130, 130); + + strcpy_s(sItemString, 16, "-"); + } + } + } } } } @@ -500,12 +290,15 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT { CFlightPlan FlightPlan = FlightPlanSelectASEL(); + if (datalinkManager == nullptr) + return; + switch (FunctionId) { case TAG_FUNC_DATALINK_MENU: { bool menu_is_datalink = true; if (FlightPlan.IsValid()) { - if (std::find(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()) != AircraftDemandingClearance.end()) { + if (datalinkManager->isAircraftDemandingClearance(FlightPlan.GetCallsign())) { menu_is_datalink = false; } } @@ -523,34 +316,22 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT case TAG_FUNC_DATALINK_RESET: if (FlightPlan.IsValid()) { - if (std::find(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()) != AircraftDemandingClearance.end()) { - AircraftDemandingClearance.erase(std::remove(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()), AircraftDemandingClearance.end()); - } - if (std::find(AircraftStandby.begin(), AircraftStandby.end(), FlightPlan.GetCallsign()) != AircraftStandby.end()) { - AircraftStandby.erase(std::remove(AircraftStandby.begin(), AircraftStandby.end(), FlightPlan.GetCallsign()), AircraftStandby.end()); - } - if (std::find(AircraftMessageSent.begin(), AircraftMessageSent.end(), FlightPlan.GetCallsign()) != AircraftMessageSent.end()) { - AircraftMessageSent.erase(std::remove(AircraftMessageSent.begin(), AircraftMessageSent.end(), FlightPlan.GetCallsign()), AircraftMessageSent.end()); - } - if (std::find(AircraftWilco.begin(), AircraftWilco.end(), FlightPlan.GetCallsign()) != AircraftWilco.end()) { - AircraftWilco.erase(std::remove(AircraftWilco.begin(), AircraftWilco.end(), FlightPlan.GetCallsign()), AircraftWilco.end()); - } - if (std::find(AircraftMessage.begin(), AircraftMessage.end(), FlightPlan.GetCallsign()) != AircraftMessage.end()) { - AircraftMessage.erase(std::remove(AircraftMessage.begin(), AircraftMessage.end(), FlightPlan.GetCallsign()), AircraftMessage.end()); - } - if (PendingMessages.find(FlightPlan.GetCallsign()) != PendingMessages.end()) { - PendingMessages.erase(FlightPlan.GetCallsign()); + datalinkManager->removeAircraftDemandingClearance(FlightPlan.GetCallsign()); + datalinkManager->removeAircraftStandby(FlightPlan.GetCallsign()); + // Remove all other states for this aircraft + if (datalinkManager->isAircraftMessageSent(FlightPlan.GetCallsign())) { + vector sent = datalinkManager->getAircraftMessageSent(); + sent.erase(std::remove(sent.begin(), sent.end(), FlightPlan.GetCallsign()), sent.end()); } + datalinkManager->removePendingMessage(FlightPlan.GetCallsign()); } break; case TAG_FUNC_DATALINK_STBY: if (FlightPlan.IsValid()) { - AircraftStandby.push_back(FlightPlan.GetCallsign()); - createPlainCpdlcMessage("STANDBY", "NE"); - tdest = FlightPlan.GetCallsign(); - _beginthread(sendDatalinkMessage, 0, NULL); + datalinkManager->addAircraftStandby(FlightPlan.GetCallsign()); + datalinkManager->sendSimpleCpdlcMessage(FlightPlan.GetCallsign(), "STANDBY", "NE"); } break; @@ -565,7 +346,7 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT dia.m_Dest = FlightPlan.GetFlightPlanData().GetDestination(); dia.m_From = FlightPlan.GetFlightPlanData().GetOrigin(); - AcarsMessage msg = PendingMessages[FlightPlan.GetCallsign()]; + AcarsMessage msg = datalinkManager->getPendingMessage(FlightPlan.GetCallsign()); dia.m_Req = msg.message.c_str(); string toReturn = ""; @@ -573,29 +354,21 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT if (dia.DoModal() != IDOK) return; - tmessage = dia.m_Message; - ttype = "TELEX"; - tdest = FlightPlan.GetCallsign(); - _beginthread(sendDatalinkMessage, 0, NULL); + // Convert CString to std::string + std::string msgStr = CStringToStdString(dia.m_Message); + datalinkManager->sendSimpleCpdlcMessage(FlightPlan.GetCallsign(), msgStr, "NE"); } break; case TAG_FUNC_DATALINK_VOICE: if (FlightPlan.IsValid()) { - createPlainCpdlcMessage("UNABLE - CALL ON FREQ", "R"); - tdest = FlightPlan.GetCallsign(); - - if (std::find(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()) != AircraftDemandingClearance.end()) { - AircraftDemandingClearance.erase(std::remove(AircraftDemandingClearance.begin(), AircraftDemandingClearance.end(), FlightPlan.GetCallsign()), AircraftDemandingClearance.end()); - } - if (std::find(AircraftStandby.begin(), AircraftStandby.end(), FlightPlan.GetCallsign()) != AircraftStandby.end()) { - AircraftStandby.erase(std::remove(AircraftStandby.begin(), AircraftStandby.end(), FlightPlan.GetCallsign()), AircraftDemandingClearance.end()); - } - PendingMessages.erase(FlightPlan.GetCallsign()); - AircraftMessageSent.push_back(FlightPlan.GetCallsign()); + datalinkManager->sendSimpleCpdlcMessage(FlightPlan.GetCallsign(), "UNABLE - CALL ON FREQ", "R"); - _beginthread(sendDatalinkMessage, 0, NULL); + datalinkManager->removeAircraftDemandingClearance(FlightPlan.GetCallsign()); + datalinkManager->removeAircraftStandby(FlightPlan.GetCallsign()); + datalinkManager->removePendingMessage(FlightPlan.GetCallsign()); + datalinkManager->addAircraftMessageSent(FlightPlan.GetCallsign()); } break; @@ -618,7 +391,7 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT string freq = std::to_string(ControllerSelect(FlightPlan.GetCoordinatedNextController()).GetPrimaryFrequency()); freq = freq.substr(0, 7); dia.m_Freq = freq.c_str(); - AcarsMessage msg = PendingMessages[FlightPlan.GetCallsign()]; + AcarsMessage msg = datalinkManager->getPendingMessage(FlightPlan.GetCallsign()); dia.m_Req = msg.message.c_str(); string toReturn = ""; @@ -637,8 +410,6 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT toReturn += str; } else if (ClearedAltitude <= Ta && ClearedAltitude > 2) { - - toReturn = std::to_string(ClearedAltitude); toReturn += "ft"; } @@ -648,21 +419,21 @@ void CSMRPlugin::OnFunctionCall(int FunctionId, const char * sItemString, POINT if (dia.DoModal() != IDOK) return; - DatalinkToSend.callsign = FlightPlan.GetCallsign(); - DatalinkToSend.destination = FlightPlan.GetFlightPlanData().GetDestination(); - DatalinkToSend.rwy = FlightPlan.GetFlightPlanData().GetDepartureRwy(); - DatalinkToSend.sid = FlightPlan.GetFlightPlanData().GetSidName(); - DatalinkToSend.asat = dia.m_TSAT; - DatalinkToSend.ctot = dia.m_CTOT; - DatalinkToSend.freq = dia.m_Freq; - DatalinkToSend.message = dia.m_Message; - DatalinkToSend.squawk = FlightPlan.GetControllerAssignedData().GetSquawk(); - DatalinkToSend.climb = toReturn; + DatalinkClearance clearance; + clearance.callsign = FlightPlan.GetCallsign(); + clearance.destination = FlightPlan.GetFlightPlanData().GetDestination(); + clearance.rwy = FlightPlan.GetFlightPlanData().GetDepartureRwy(); + clearance.sid = FlightPlan.GetFlightPlanData().GetSidName(); + clearance.asat = CStringToStdString(dia.m_TSAT); + clearance.ctot = CStringToStdString(dia.m_CTOT); + clearance.freq = CStringToStdString(dia.m_Freq); + clearance.message = CStringToStdString(dia.m_Message); + clearance.squawk = FlightPlan.GetControllerAssignedData().GetSquawk(); + clearance.climb = toReturn; myfrequency = std::to_string(ControllerMyself().GetPrimaryFrequency()).substr(0, 7); - _beginthread(sendDatalinkClearance, 0, NULL); - + datalinkManager->sendDatalinkClearance(clearance); } break; @@ -715,19 +486,23 @@ void CSMRPlugin::OnTimer(int Counter) HoppieConnected = false; } - if (((clock() - timer) / CLOCKS_PER_SEC) > pollInterval && HoppieConnected) { - _beginthread(pollMessages, 0, NULL); + if (((clock() - timer) / CLOCKS_PER_SEC) > pollInterval && HoppieConnected && datalinkManager != nullptr) { + datalinkManager->pollMessages(); timer = clock(); pollInterval = 45 + rand() % 31; // Next random interval between 45-75 seconds } - for (auto &ac : AircraftWilco) - { - CRadarTarget RadarTarget = RadarTargetSelect(ac.c_str()); + if (datalinkManager != nullptr) { + vector wilco = datalinkManager->getAircraftWilco(); + for (auto &ac : wilco) + { + CRadarTarget RadarTarget = RadarTargetSelect(ac.c_str()); - if (RadarTarget.IsValid()) { - if (RadarTarget.GetGS() > 160) { - AircraftWilco.erase(std::remove(AircraftWilco.begin(), AircraftWilco.end(), ac), AircraftWilco.end()); + if (RadarTarget.IsValid()) { + if (RadarTarget.GetGS() > 160) { + // Remove aircraft from wilco list when GS > 160 + wilco.erase(std::remove(wilco.begin(), wilco.end(), ac), wilco.end()); + } } } } diff --git a/vSMR/SMRPlugin.hpp b/vSMR/SMRPlugin.hpp index 1b0dc1966..ec710a23f 100644 --- a/vSMR/SMRPlugin.hpp +++ b/vSMR/SMRPlugin.hpp @@ -3,6 +3,7 @@ #include "HttpHelper.hpp" #include "CPDLCSettingsDialog.hpp" #include "DataLinkDialog.hpp" +#include "DatalinkManager.hpp" #include #include #include "Constant.hpp" @@ -27,6 +28,7 @@ class CSMRPlugin : { private: CQnhTracker qnhTracker; + DatalinkManager* datalinkManager; public: CSMRPlugin(); diff --git a/vSMR/vSMR.vcxproj b/vSMR/vSMR.vcxproj index e00da3480..d77162296 100644 --- a/vSMR/vSMR.vcxproj +++ b/vSMR/vSMR.vcxproj @@ -139,6 +139,7 @@ + Create Create @@ -169,6 +170,7 @@ + diff --git a/vSMR/vSMR.vcxproj.filters b/vSMR/vSMR.vcxproj.filters index d32ca0092..50989e5cc 100644 --- a/vSMR/vSMR.vcxproj.filters +++ b/vSMR/vSMR.vcxproj.filters @@ -57,6 +57,9 @@ Source Files + + Source Files + @@ -134,6 +137,9 @@ Header Files + + Header Files +