#include "include/recorder.h" namespace germanairlinesva { namespace gaconnector { namespace recorder { Recorder::Recorder(int simVersion, std::function toLog) : toLog(std::move(toLog)) { // Configuration this->configuration = std::make_shared(); this->toLog("Configuration loaded"); // Database #ifdef XP char hash[2 * MD5LEN + 1] = ""; if (utilities::generateMD5(XPLANE_CUSTOM_SCENERY, hash, this->toLog) == 0) { this->database = std::make_unique(simVersion, hash, this->configuration, this->toLog); } #endif // WebSocket this->connector = std::make_unique(WEBSOCKET_ADDRESS, this->configuration->getUser(), this->toLog); this->toLog("WebSocket started"); // For Testing #ifdef XP this->test(); #endif // Thread for sending data to websocket this->serverThread = std::thread(&Recorder::serverWorker, this); this->recordingThread = std::thread(&Recorder::recordingWorker, this); this->toLog("Workers started"); } Recorder::~Recorder() { wantsExit = true; serverThread.join(); recordingThread.join(); } void Recorder::setData(websocket::data &data) { const std::lock_guard lock(mutex); std::memcpy(&this->toSend, &data, sizeof(websocket::data)); } void Recorder::handleMessages() { const std::lock_guard lock(mutex); if (!messageQueue().empty()) { auto op = std::move(messageQueue().front()); messageQueue().pop(); op(); } } std::shared_ptr Recorder::getConfiguration() const { return this->configuration; } void Recorder::loadDatabase(int simVersion) { if (simVersion == MSFS_VERSION) { return; } #if defined(IBM) && not defined(XP) this->toLog("Loading database for " + SimConnect::resolveVersion(simVersion)); PWSTR folder; HRESULT result = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, NULL, &folder); if (SUCCEEDED(result)) { size_t origsize = wcslen(folder) + 1; size_t convertedChars = 0; char *nstring = new char[origsize * 2]; wcstombs_s(&convertedChars, nstring, origsize * 2, folder, _TRUNCATE); std::string path(nstring); path.append(SimConnect::resolveScenery(simVersion)); this->toLog(path); delete[] nstring; CoTaskMemFree(folder); char hash[2 * MD5LEN + 1] = ""; if (utilities::generateMD5(path.c_str(), hash, toLog) == 0) { database = std::make_unique(simVersion, hash, this->configuration, this->toLog); } this->test(); } #endif } void Recorder::serverWorker() { utilities::setThreadName("GAServerWorker"); while (!wantsExit) { struct websocket::data copy; { const std::lock_guard lock(mutex); std::memcpy(©, &toSend, sizeof(websocket::data)); } connector->sendData(copy); std::this_thread::sleep_for(std::chrono::milliseconds(250)); } toLog("Server thread stopped"); } void Recorder::recordingWorker() { utilities::setThreadName("GARecordingWorker"); file::recording::Recording path; file::recording::RecordingEntry lastPath; std::uint32_t segment = 0; while (!wantsExit) { struct websocket::data copy; { const std::lock_guard lock(mutex); std::memcpy(©, &toSend, sizeof(websocket::data)); } file::recording::RecordingEntry currentPath( segment, static_cast(copy.alt), static_cast(copy.gs), {copy.lat, copy.lon}); if (strcmp(copy.path, "") != 0 && !copy.pause && lastPath != currentPath) { path.addEntry(currentPath); lastPath = currentPath; } segment++; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } path.toFile("flight.rec"); toLog("Recording thread stopped"); } void Recorder::test() const { #ifndef MSFS this->toLog("Readback test of sim database using EDDF"); auto ap = (*this->database)["EDDF"]; for (const auto &it : ap.first) { this->toLog(" " + it.to_string()); } for (const auto &it : ap.second) { this->toLog(" " + it.to_string()); } this->toLog("Readback test of sim database using XXXX"); auto ap2 = (*database)["XXXX"]; ap2.first.size() == 0 ? this->toLog(" SUCCESS") : this->toLog(" ERROR"); #endif } } // namespace recorder } // namespace gaconnector } // namespace germanairlinesva