#include "include/main.h" std::mutex mutex; std::queue> &messageQueue() { static std::queue> _messageQueue; return _messageQueue; } std::thread serverThread; std::thread recordingThread; std::atomic wantsExit; std::shared_ptr configuration; std::unique_ptr database; std::unique_ptr connector; std::unique_ptr simConnect; struct germanairlinesva::gaconnector::websocket::data toSend; germanairlinesva::file::recording::Recording p; // The Window Procedure LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static UINT s_uTaskbarRestart; switch (msg) { case WM_CREATE: { s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); break; } case WM_CLOSE: { DestroyWindow(hWnd); break; } case WM_DESTROY: { end(hWnd); break; } case TRAY_MESSAGE: { switch (lParam) { case WM_RBUTTONUP: case WM_CONTEXTMENU: { createMenu(hWnd); break; } } break; } case SIMCONNECT_MESSAGE: { if (simConnect != nullptr && simConnect->isConnected()) { simConnect->handleMessage(); } break; } default: { if (msg == s_uTaskbarRestart) { addNotifyIcon(hWnd); } break; } } return DefWindowProc(hWnd, msg, wParam, lParam); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; ZeroMemory(&wc, sizeof(WNDCLASS)); HWND hWnd; MSG msg; wantsExit.store(false); // Exit if already running hWnd = FindWindow(WINDOW_CLASS, WINDOW_CLASS); if (hWnd != NULL) { toLog("Exiting duplicate"); return 0; } // Clear log std::ofstream clearLog(BASE_DIRECTORY "log.txt"); clearLog.close(); // Registering the Window Class wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = WINDOW_CLASS; if (!RegisterClass(&wc)) { toLog("Error window register: " + std::to_string(GetLastError())); return 0; } // Creating the Window hWnd = CreateWindow(WINDOW_CLASS, WINDOW_CLASS, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL); if (hWnd == NULL) { toLog("Error window create: " + std::to_string(GetLastError())); return 0; } // Add Notify Icon if (!addNotifyIcon(hWnd)) { toLog("Error notifyIcon: " + std::to_string(GetLastError())); return 0; } // Never show window // ShowWindow(hWnd, SHOW_OPENWINDOW); configuration = std::make_unique(); toLog("Config loaded"); connector = std::make_unique( "wss://ws.hofmannnet.myhome-server.de:8000", configuration->getUser(), toLog); toLog("WebSocket started"); #ifndef MSFS 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("\\Microsoft\\FSX\\scenery.cfg"); toLog(path); delete[] nstring; CoTaskMemFree(folder); char hash[2 * MD5LEN + 1] = ""; if (germanairlinesva::utilities::generateMD5(path.c_str(), hash, toLog) == 0) { database = std::make_unique( FSX_VERSION, hash, configuration, toLog); } toLog("Readback test of sim database using EDDF"); auto ap = (*database)["EDDF"]; for (const auto &it : ap.first) { toLog(" " + it.to_string()); } for (const auto &it : ap.second) { toLog(" " + it.to_string()); } toLog("Readback test of sim database using XXXX"); auto ap2 = (*database)["XXXX"]; ap2.first.size() == 0 ? toLog(" SUCCESS") : toLog(" ERROR"); } #endif toLog("Logbook Test"); germanairlinesva::file::logbook::Logbook logbook; logbook.addEntry("08.09.2022", "F", "1000", "L049", "D-ALFA", "John F. Kennedy International Aiport / EDDF", "A1", "14L", "Gander International Airport / CYQX", "10", "03", "10:00", "10:20", "13:20", "13:30", 210.5, 20.1, 5012.4156, 8.87, 5041.3856, 7.1, 971.14, 2.41, 980.65, -165.23, 1, 1.2012, "2022-09-08_VGA1000", 5.5, 1); logbook.toFile(); // Open SimConnect simConnect = std::make_unique(hWnd, toLog, configuration); // Thread for sending data to websocket serverThread = std::thread(&serverWorker); recordingThread = std::thread(&recordingWorker); toLog("Workers started"); // The Message Loop while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } // Exit toLog("Connector stopped"); return msg.wParam; } #pragma clang diagnostic pop WINBOOL addNotifyIcon(HWND hWnd) { HICON icon; NOTIFYICONDATA niData; ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); icon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(APP_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED); if (icon == NULL) { toLog("error icon " + std::to_string(GetLastError())); } niData.cbSize = sizeof(NOTIFYICONDATA); niData.uVersion = NOTIFYICON_VERSION_4; niData.uID = TRAY_ICON_ID; niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; strcpy(niData.szTip, "GA Connector"); niData.hIcon = icon; niData.hWnd = hWnd; niData.uCallbackMessage = TRAY_MESSAGE; WINBOOL retVal = Shell_NotifyIcon(NIM_ADD, &niData); // DestroyIcon(niData.hIcon); return retVal; } WINBOOL removeNotifyIcon(HWND hWnd) { NOTIFYICONDATA niData; ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); niData.cbSize = sizeof(NOTIFYICONDATA); niData.uID = TRAY_ICON_ID; niData.hWnd = hWnd; return Shell_NotifyIcon(NIM_DELETE, &niData); } WINBOOL createMenu(HWND hWnd) { const int IDM_EXIT = 100; POINT pt; GetCursorPos(&pt); HMENU hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING | MF_GRAYED, NULL, "Version: " BIT " Bit"); if (simConnect->isConnected()) { std::string version("Connected to Sim: "); version.append(simConnect->getVersion()); AppendMenu(hMenu, MF_STRING | MF_GRAYED, NULL, version.c_str()); } AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL); AppendMenu(hMenu, MF_STRING, IDM_EXIT, "Exit"); SetForegroundWindow(hWnd); WINBOOL cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_RETURNCMD, pt.x, pt.y, 0, hWnd, NULL); if (cmd == IDM_EXIT) { PostMessage(hWnd, WM_CLOSE, 0, NULL); } return DestroyMenu(hMenu); } void end(HWND hWnd) { removeNotifyIcon(hWnd); UnregisterClass(WINDOW_CLASS, GetModuleHandle(NULL)); /* End threads */ wantsExit = true; serverThread.join(); recordingThread.join(); p.toFile("flight.rec"); // End SimConnect delete simConnect.release(); PostQuitMessage(0); } void serverWorker() { germanairlinesva::utilities::setThreadName("GAServerWorker"); while (!wantsExit) { simConnect->getStates(); struct germanairlinesva::gaconnector::websocket::data *copy = new germanairlinesva::gaconnector::websocket::data(); { const std::lock_guard lock(mutex); simConnect->getData(copy); } connector->sendData(*copy); std::this_thread::sleep_for(std::chrono::milliseconds(250)); } toLog("Server thread stopped"); } void recordingWorker() { germanairlinesva::utilities::setThreadName("GARecordingWorker"); germanairlinesva::file::recording::RecordingEntry lastPath; std::uint32_t segment = 0; auto ap = (*database)["EDDF"]; auto rwys = ap.second; while (!wantsExit) { struct germanairlinesva::gaconnector::websocket::data *copy = new germanairlinesva::gaconnector::websocket::data(); { const std::lock_guard lock(mutex); simConnect->getData(copy); } germanairlinesva::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) { p.addEntry(currentPath); lastPath = currentPath; for (const auto &it : rwys) { if (it.containsPoint({copy->lat, copy->lon})) { toLog("On Runway: " + it.to_string()); } } } segment++; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } toLog("Recording thread stopped"); } void toLog(const std::string &message) { std::time_t utc = std::time(nullptr); std::tm *time = std::gmtime(&utc); std::ofstream msg(BASE_DIRECTORY "log.txt", std::ofstream::app); msg << std::put_time(time, "%Y-%m-%d %H:%M:%S UTC ") << "German Airlines VA: " << message << std::endl; }