-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
v1.3.0 (#43)
major refactor to improve performance and remove technical debt
Showing
51 changed files
with
3,452 additions
and
3,180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
BasedOnStyle: Google | ||
IndentWidth: 4 | ||
ColumnLimit: 120 |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
name: Build Plugin Dev Release | ||
|
||
on: | ||
push: | ||
branches: | ||
- "develop" | ||
|
||
env: | ||
DLL_NAME: vACDM.dll | ||
BUILD_CONFIGURATION: Release | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
VERSION: "0" | ||
DEV_RELEASE: "-1" | ||
|
||
jobs: | ||
formatting-check: | ||
uses: ./.github/workflows/clang-format.yml | ||
|
||
build: | ||
needs: formatting-check | ||
runs-on: windows-latest | ||
name: "Build plugin" | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: "3.x" | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install PyGithub | ||
- name: Run version handler | ||
env: | ||
REPOSITORY: ${{ github.repository }} | ||
REF: ${{ github.ref }} | ||
run: | | ||
python .github/workflows/version_handler.py | ||
- name: Determine DEV_RELEASE value | ||
id: find_latest_dev_release | ||
env: | ||
REPOSITORY: ${{ github.repository }} | ||
run: python .github/workflows/determine_dev_release.py | ||
|
||
- name: Install MSVC toolset | ||
run: choco install visualstudio2019buildtools --package-parameters "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64" | ||
|
||
- name: Set up MSVC environment | ||
shell: pwsh | ||
run: | | ||
Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" | ||
Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools" | ||
- name: Configure CMake | ||
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_CONFIGURATION}} -A Win32 -DDEV_BUILD=ON -DDEV_RELEASE_NUMBER="${{ env.DEV_RELEASE }}" | ||
|
||
- name: Build DLL | ||
run: cmake --build build --config ${{env.BUILD_CONFIGURATION}} | ||
|
||
- name: Create GitHub Release | ||
id: create_release | ||
uses: actions/create-release@v1 | ||
with: | ||
tag_name: v${{ env.VERSION }} | ||
release_name: v${{ env.VERSION }} | ||
draft: true # to allow amendment of release notes | ||
prerelease: true | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Upload release asset | ||
id: upload-release-asset | ||
uses: actions/upload-release-asset@v1 | ||
with: | ||
upload_url: ${{ steps.create_release.outputs.upload_url }} | ||
asset_path: build/${{env.BUILD_CONFIGURATION}}/${{env.DLL_NAME}} | ||
asset_name: ${{env.DLL_NAME}} | ||
asset_content_type: application/octet-stream | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Upload config file (vacdm.txt) | ||
id: upload-release-asset-txt | ||
uses: actions/upload-release-asset@v1 | ||
with: | ||
upload_url: ${{ steps.create_release.outputs.upload_url }} | ||
asset_path: src/config/vacdm.txt | ||
asset_name: vacdm.txt | ||
asset_content_type: text/plain | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
name: Build Plugin Release | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
env: | ||
DLL_NAME: vACDM.dll | ||
BUILD_CONFIGURATION: Release | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
VERSION: "0" | ||
|
||
jobs: | ||
formatting-check: | ||
uses: ./.github/workflows/clang-format.yml | ||
|
||
build: | ||
needs: formatting-check | ||
runs-on: windows-latest | ||
name: "Build plugin" | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Install MSVC toolset | ||
run: choco install visualstudio2019buildtools --package-parameters "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64" | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: "3.x" | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install PyGithub | ||
- name: Run version handler | ||
env: | ||
REPOSITORY: ${{ github.repository }} | ||
REF: ${{ github.ref }} | ||
run: | | ||
python .github/workflows/version_handler.py | ||
- name: Set up MSVC environment | ||
shell: pwsh | ||
run: | | ||
Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" | ||
Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools" | ||
- name: Configure CMake | ||
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_CONFIGURATION}} -A Win32 | ||
|
||
- name: Build DLL | ||
run: cmake --build build --config ${{env.BUILD_CONFIGURATION}} | ||
|
||
- name: Create GitHub Release | ||
id: create_release | ||
uses: actions/create-release@v1 | ||
with: | ||
tag_name: v${{ env.VERSION }} | ||
release_name: v${{ env.VERSION }} | ||
draft: true # to allow amendment of release notes | ||
prerelease: false | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Upload release asset | ||
id: upload-release-asset | ||
uses: actions/upload-release-asset@v1 | ||
with: | ||
upload_url: ${{ steps.create_release.outputs.upload_url }} | ||
asset_path: build/${{env.BUILD_CONFIGURATION}}/${{env.DLL_NAME}} | ||
asset_name: ${{env.DLL_NAME}} | ||
asset_content_type: application/octet-stream | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Upload config file (vacdm.txt) | ||
id: upload-release-asset-txt | ||
uses: actions/upload-release-asset@v1 | ||
with: | ||
upload_url: ${{ steps.create_release.outputs.upload_url }} | ||
asset_path: src/config/vacdm.txt | ||
asset_name: vacdm.txt | ||
asset_content_type: text/plain | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
name: clang-format Check | ||
on: [workflow_call, pull_request] | ||
jobs: | ||
formatting-check: | ||
name: Formatting Check | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Run clang-format style check for C/C++ programs. | ||
uses: jidicula/[email protected] | ||
with: | ||
clang-format-version: "13" | ||
check-path: "src" | ||
fallback-style: "Google" | ||
exclude-regex: "(sqlite3)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
""" Finds the project version defined in CMakeLists.txt""" | ||
|
||
import sys | ||
import re | ||
|
||
|
||
def extract_version_from_cmakelists(): | ||
# Define the pattern to search for in the file | ||
pattern = r'PROJECT\(.*?VERSION\s+"(\d+\.\d+\.\d+)"' | ||
|
||
# Read the contents of CMakeLists.txt | ||
with open("CMakeLists.txt", "r") as file: | ||
contents = file.read() | ||
|
||
# Search for the pattern | ||
match = re.search(pattern, contents) | ||
|
||
# If a match is found, extract the version | ||
if match: | ||
print("Found version number in CMakeLists.txt: ", match.group(1)) | ||
return match.group(1) | ||
|
||
# exit if the version could not be found | ||
print("Could not find version in CMakeLists.txt") | ||
sys.exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import sys | ||
import re | ||
from github import Github | ||
|
||
|
||
def parse_semantic_version(version): | ||
# Parse the semantic version and extract major and minor versions | ||
match = re.match(r"v?(\d+)\.(\d+)\.(\d+)", version) | ||
if match: | ||
print(match.groups()) | ||
major, minor, patch = match.groups() | ||
return int(major), int(minor), int(patch) | ||
else: | ||
return None | ||
|
||
|
||
def find_highest_dev_release(repo, major, minor): | ||
# Initialize a GitHub instance | ||
g = Github() | ||
|
||
# Get the repository object | ||
repository = g.get_repo(repo) | ||
|
||
# Fetch all releases | ||
releases = repository.get_releases() | ||
|
||
# Filter pre-releases with matching major and minor versions in their titles | ||
pre_releases = [ | ||
release | ||
for release in releases | ||
if release.prerelease and release.title.startswith(f"v{major}.{minor}") | ||
] | ||
|
||
# Extract DEV_RELEASE numbers from titles | ||
dev_releases = [int(release.title.split(".")[-1]) for release in pre_releases] | ||
|
||
# Find the highest DEV_RELEASE number | ||
highest_dev_release = max(dev_releases, default=0) | ||
|
||
return highest_dev_release | ||
|
||
|
||
def determine_dev_release(version, repository): | ||
if version == "0": | ||
result = 0 | ||
else: | ||
# Parse the semantic version to extract major and minor versions | ||
major, minor, _ = parse_semantic_version(version) | ||
if major is not None and minor is not None: | ||
result = find_highest_dev_release(repository, major, minor) | ||
else: | ||
print("Invalid semantic version format") | ||
sys.exit(1) | ||
|
||
print("Determined dev release version as ", result) | ||
|
||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
""" """ | ||
|
||
import sys | ||
import os | ||
import re | ||
|
||
from cmake_project_version import extract_version_from_cmakelists | ||
from determine_dev_release import determine_dev_release | ||
|
||
REPOSITORY_NAME = os.environ.get("REPOSITORY") | ||
REF = os.environ.get("REF") | ||
|
||
# determine the branch that triggered the workflow | ||
match = re.match(r"refs/heads/(.*)", REF) | ||
if not match: | ||
sys.exit(1) | ||
|
||
branch_name = match.group(1) | ||
print("Determined branch name: ", branch_name) | ||
|
||
version = extract_version_from_cmakelists() | ||
|
||
is_dev_branch = branch_name == "develop" | ||
dev_release = None | ||
if is_dev_branch: | ||
last_dev_release = determine_dev_release(version, REPOSITORY_NAME) | ||
dev_release = str(last_dev_release + 1) | ||
version += "-dev." + dev_release | ||
|
||
# Write the version and dev release to GitHub environment variable | ||
with open(os.getenv("GITHUB_ENV"), "a") as env_file: | ||
env_file.write(f"VERSION={version}\n") | ||
if is_dev_branch and dev_release: | ||
env_file.write(f"DEV_RELEASE={dev_release}\n") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,85 @@ | ||
# Author: | ||
# Sven Czarnian <devel@svcz.de> | ||
# License: | ||
# GPLv3 | ||
# Brief: | ||
# Creates the vACMD EuroScope plugin | ||
|
||
CMAKE_MINIMUM_REQUIRED(VERSION 3.14) | ||
|
||
# define the project | ||
PROJECT(vACDM LANGUAGES C CXX VERSION "1.0.0") | ||
SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) | ||
SET(CMAKE_CXX_STANDARD 20) | ||
SET(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
SET(CMAKE_CXX_EXTENSIONS OFF) | ||
SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) | ||
IF (MSVC) | ||
IF (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") | ||
STRING(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") | ||
ELSE () | ||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") | ||
ENDIF () | ||
IF (NOT CMAKE_CXX_FLAGS MATCHES "/MP") | ||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") | ||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") | ||
ENDIF () | ||
|
||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /sdl /permissive- /DNOMINMAX") | ||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /sdl /permissive- /DNOMINMAX") | ||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /MANIFESTUAC:NO /ignore:4099") | ||
ADD_DEFINITIONS(/D_USRDLL) | ||
ENDIF () | ||
|
||
CONFIGURE_FILE( | ||
${CMAKE_SOURCE_DIR}/Version.h.in | ||
${CMAKE_BINARY_DIR}/Version.h | ||
) | ||
|
||
# add the binary, include and installation directories to the search paths | ||
INCLUDE_DIRECTORIES( | ||
${CMAKE_BINARY_DIR} | ||
${CMAKE_SOURCE_DIR}/external/include | ||
${CMAKE_SOURCE_DIR} | ||
) | ||
|
||
ADD_DEFINITIONS( | ||
-D_CRT_SECURE_NO_WARNINGS | ||
-DSQLITE_THREADSAFE=0 | ||
-DSQLITE_DEFAULT_FILE_FORMAT=4 | ||
-DSQLITE_DEFAULT_SYNCHRONOUS=0 | ||
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=0 | ||
-DSQLITE_WIN32_MALLOC | ||
-DSQLITE_THREADSAFE=0 | ||
) | ||
|
||
SET(SOURCE_FILES | ||
config/FileFormat.cpp | ||
config/FileFormat.h | ||
com/Airport.cpp | ||
com/Airport.h | ||
com/PerformanceLock.h | ||
com/Server.cpp | ||
com/Server.h | ||
logging/Logger.cpp | ||
logging/Logger.h | ||
logging/Performance.h | ||
logging/sqlite3.c | ||
logging/sqlite3.h | ||
logging/sqlite3ext.h | ||
types/Flight.h | ||
types/SystemConfig.h | ||
ui/color.cpp | ||
ui/color.h | ||
main.cpp | ||
vACDM.cpp | ||
vACDM.h | ||
Version.h | ||
) | ||
|
||
ADD_LIBRARY(vACDM SHARED ${SOURCE_FILES}) | ||
TARGET_LINK_LIBRARIES(vACDM ${CMAKE_SOURCE_DIR}/external/lib/EuroScopePlugInDLL.lib crypt32.lib ws2_32.lib Shlwapi.lib) | ||
TARGET_LINK_LIBRARIES(vACDM debug ${CMAKE_SOURCE_DIR}/external/lib/jsoncpp_d.lib) | ||
TARGET_LINK_LIBRARIES(vACDM debug ${CMAKE_SOURCE_DIR}/external/lib/libcurl-d.lib) | ||
TARGET_LINK_LIBRARIES(vACDM debug ${CMAKE_SOURCE_DIR}/external/lib/Geographic_d.lib) | ||
TARGET_LINK_LIBRARIES(vACDM optimized ${CMAKE_SOURCE_DIR}/external/lib/jsoncpp.lib) | ||
TARGET_LINK_LIBRARIES(vACDM optimized ${CMAKE_SOURCE_DIR}/external/lib/libcurl.lib) | ||
TARGET_LINK_LIBRARIES(vACDM optimized ${CMAKE_SOURCE_DIR}/external/lib/Geographic.lib) | ||
CMAKE_MINIMUM_REQUIRED(VERSION 3.14) | ||
|
||
PROJECT(vACDM VERSION "1.3.0") | ||
SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) | ||
SET(CMAKE_CXX_STANDARD 20) | ||
SET(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
SET(CMAKE_CXX_EXTENSIONS OFF) | ||
SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) | ||
IF (MSVC) | ||
IF (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") | ||
STRING(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") | ||
ELSE () | ||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") | ||
ENDIF () | ||
IF (NOT CMAKE_CXX_FLAGS MATCHES "/MP") | ||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") | ||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") | ||
ENDIF () | ||
|
||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /sdl /permissive- /DNOMINMAX") | ||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /sdl /permissive- /DNOMINMAX") | ||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /MANIFESTUAC:NO /ignore:4099") | ||
ADD_DEFINITIONS(/D_USRDLL) | ||
ENDIF () | ||
|
||
if(DEV_BUILD) | ||
ADD_DEFINITIONS(-DDEV_BUILD) | ||
endif() | ||
|
||
if(CMAKE_BUILD_TYPE STREQUAL "Debug") | ||
ADD_DEFINITIONS(-DDEBUG_BUILD=1) # enables log output to console window | ||
ADD_DEFINITIONS(-DDEV_BUILD) | ||
endif() | ||
|
||
CONFIGURE_FILE( | ||
${CMAKE_SOURCE_DIR}/src/Version.h.in | ||
${CMAKE_BINARY_DIR}/Version.h | ||
) | ||
|
||
INCLUDE_DIRECTORIES( | ||
${CMAKE_BINARY_DIR} | ||
${CMAKE_SOURCE_DIR}/src/ | ||
${CMAKE_SOURCE_DIR}/external/include | ||
${CMAKE_SOURCE_DIR} | ||
) | ||
|
||
ADD_DEFINITIONS( | ||
-D_CRT_SECURE_NO_WARNINGS | ||
-DSQLITE_THREADSAFE=0 | ||
-DSQLITE_DEFAULT_FILE_FORMAT=4 | ||
-DSQLITE_DEFAULT_SYNCHRONOUS=0 | ||
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=0 | ||
-DSQLITE_WIN32_MALLOC | ||
-DSQLITE_THREADSAFE=0 | ||
) | ||
|
||
SET(SOURCE_FILES | ||
src/config/ConfigParser.cpp | ||
src/config/ConfigParser.h | ||
src/core/DataManager.cpp | ||
src/core/DataManager.h | ||
src/core/Server.cpp | ||
src/core/Server.h | ||
src/log/Logger.cpp | ||
src/log/Logger.h | ||
src/log/sqlite3.c | ||
src/log/sqlite3.h | ||
src/log/sqlite3ext.h | ||
src/vACDM.cpp | ||
src/vACDM.h | ||
src/main.cpp | ||
src/Version.h | ||
) | ||
|
||
ADD_LIBRARY(vACDM SHARED ${SOURCE_FILES}) | ||
TARGET_LINK_LIBRARIES(vACDM ${CMAKE_SOURCE_DIR}/external/lib/EuroScopePlugInDLL.lib crypt32.lib ws2_32.lib Shlwapi.lib) | ||
TARGET_LINK_LIBRARIES(vACDM debug ${CMAKE_SOURCE_DIR}/external/lib/jsoncpp_d.lib) | ||
TARGET_LINK_LIBRARIES(vACDM debug ${CMAKE_SOURCE_DIR}/external/lib/libcurl-d.lib) | ||
TARGET_LINK_LIBRARIES(vACDM debug ${CMAKE_SOURCE_DIR}/external/lib/Geographic_d.lib) | ||
TARGET_LINK_LIBRARIES(vACDM optimized ${CMAKE_SOURCE_DIR}/external/lib/jsoncpp.lib) | ||
TARGET_LINK_LIBRARIES(vACDM optimized ${CMAKE_SOURCE_DIR}/external/lib/libcurl.lib) | ||
TARGET_LINK_LIBRARIES(vACDM optimized ${CMAKE_SOURCE_DIR}/external/lib/Geographic.lib) | ||
|
||
# move config file to output dir, allows loading of DLL from output dir | ||
configure_file(${CMAKE_SOURCE_DIR}/src/config/vacdm.txt ${CMAKE_BINARY_DIR}/vacdm.txt COPY) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,17 @@ | ||
#pragma once | ||
|
||
namespace { | ||
const char *PLUGIN_NAME{ "vACDM" }; | ||
const char *PLUGIN_VERSION{ "@CMAKE_PROJECT_VERSION@" }; | ||
const char *PLUGIN_AUTHOR{ "-" }; | ||
const char *PLUGIN_LICENSE{ "GPLv3" }; | ||
|
||
static constexpr std::uint8_t PLUGIN_VERSION_MAJOR = @CMAKE_PROJECT_VERSION_MAJOR@; | ||
static constexpr std::uint8_t PLUGIN_VERSION_MINOR = @CMAKE_PROJECT_VERSION_MINOR@; | ||
static constexpr std::uint8_t PLUGIN_VERSION_PATCH = @CMAKE_PROJECT_VERSION_PATCH@; | ||
#pragma once | ||
// clang-format off | ||
// prevents C2018 during compilation | ||
namespace { | ||
const char *PLUGIN_NAME{ "vACDM" }; | ||
#if DEV_BUILD | ||
const char *PLUGIN_VERSION{ "@CMAKE_PROJECT_VERSION@-DEV.@DEV_RELEASE_NUMBER@"}; | ||
#else | ||
const char *PLUGIN_VERSION{ "@CMAKE_PROJECT_VERSION@" }; | ||
#endif | ||
const char *PLUGIN_AUTHOR{ "vACDM Team" }; | ||
const char *PLUGIN_LICENSE{ "GPLv3" }; | ||
|
||
static constexpr std::uint8_t PLUGIN_VERSION_MAJOR = @CMAKE_PROJECT_VERSION_MAJOR@; | ||
static constexpr std::uint8_t PLUGIN_VERSION_MINOR = @CMAKE_PROJECT_VERSION_MINOR@; | ||
static constexpr std::uint8_t PLUGIN_VERSION_PATCH = @CMAKE_PROJECT_VERSION_PATCH@; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
#include "ConfigParser.h" | ||
|
||
#include <array> | ||
#include <fstream> | ||
#include <limits> | ||
#include <vector> | ||
|
||
#include "core/DataManager.h" | ||
#include "utils/String.h" | ||
|
||
using namespace vacdm; | ||
|
||
ConfigParser::ConfigParser() : m_errorLine(std::numeric_limits<std::uint32_t>::max()), m_errorMessage() {} | ||
|
||
bool ConfigParser::errorFound() const { return std::numeric_limits<std::uint32_t>::max() != this->m_errorLine; } | ||
|
||
std::uint32_t ConfigParser::errorLine() const { return this->m_errorLine; } | ||
|
||
const std::string &ConfigParser::errorMessage() const { return this->m_errorMessage; } | ||
|
||
bool ConfigParser::parseColor(const std::string &block, COLORREF &color, std::uint32_t line) { | ||
std::vector<std::string> colorValues = utils::String::splitString(block, ","); | ||
|
||
if (3 != colorValues.size()) { | ||
this->m_errorLine = line; | ||
this->m_errorMessage = "Invalid color config"; | ||
return false; | ||
} | ||
|
||
std::array<std::uint8_t, 3> colors; | ||
for (std::size_t i = 0; i < 3; ++i) colors[i] = static_cast<std::uint8_t>(std::atoi(colorValues[i].c_str())); | ||
|
||
color = RGB(colors[0], colors[1], colors[2]); | ||
|
||
return true; | ||
} | ||
|
||
bool ConfigParser::parse(const std::string &filename, PluginConfig &config) { | ||
config.valid = true; | ||
|
||
std::ifstream stream(filename); | ||
if (stream.is_open() == false) { | ||
this->m_errorMessage = "Unable to open the configuration file"; | ||
this->m_errorLine = 0; | ||
return false; | ||
} | ||
|
||
std::string line; | ||
std::uint32_t lineOffset = 0; | ||
while (std::getline(stream, line)) { | ||
std::string value; | ||
|
||
lineOffset += 1; | ||
|
||
/* skip empty lines */ | ||
if (line.length() == 0) continue; | ||
|
||
/* trim the line and skip comments*/ | ||
std::string trimmed = utils::String::trim(line); | ||
if (trimmed.find_first_of('#', 0) == 0) continue; | ||
|
||
// get values of line | ||
std::vector<std::string> values = utils::String::splitString(trimmed, "="); | ||
if (values.size() != 2) { | ||
this->m_errorLine = lineOffset; | ||
this->m_errorMessage = "Invalid configuration entry"; | ||
return false; | ||
} else { | ||
value = values[1]; | ||
} | ||
|
||
/* end on invalid lines */ | ||
if (0 == value.length()) { | ||
this->m_errorLine = lineOffset; | ||
this->m_errorMessage = "Invalid entry"; | ||
return false; | ||
} | ||
|
||
bool parsed = false; | ||
if ("SERVER_url" == values[0]) { | ||
config.serverUrl = values[1]; | ||
parsed = true; | ||
} else if ("UPDATE_RATE_SECONDS" == values[0]) { | ||
try { | ||
const int updateCycleSeconds = std::stoi(values[1]); | ||
if (updateCycleSeconds < core::minUpdateCycleSeconds || | ||
updateCycleSeconds > core::maxUpdateCycleSeconds) { | ||
this->m_errorLine = lineOffset; | ||
this->m_errorMessage = "Value must be number between " + | ||
std::to_string(core::minUpdateCycleSeconds) + " and " + | ||
std::to_string(core::maxUpdateCycleSeconds); | ||
} else { | ||
config.updateCycleSeconds = updateCycleSeconds; | ||
parsed = true; | ||
} | ||
} catch (const std::exception &e) { | ||
this->m_errorMessage = e.what(); | ||
this->m_errorLine = lineOffset; | ||
} | ||
|
||
} else if ("COLOR_lightgreen" == values[0]) { | ||
parsed = this->parseColor(values[1], config.lightgreen, lineOffset); | ||
} else if ("COLOR_lightblue" == values[0]) { | ||
parsed = this->parseColor(values[1], config.lightblue, lineOffset); | ||
} else if ("COLOR_green" == values[0]) { | ||
parsed = this->parseColor(values[1], config.green, lineOffset); | ||
} else if ("COLOR_blue" == values[0]) { | ||
parsed = this->parseColor(values[1], config.blue, lineOffset); | ||
} else if ("COLOR_lightyellow" == values[0]) { | ||
parsed = this->parseColor(values[1], config.lightyellow, lineOffset); | ||
} else if ("COLOR_yellow" == values[0]) { | ||
parsed = this->parseColor(values[1], config.yellow, lineOffset); | ||
} else if ("COLOR_orange" == values[0]) { | ||
parsed = this->parseColor(values[1], config.orange, lineOffset); | ||
} else if ("COLOR_red" == values[0]) { | ||
parsed = this->parseColor(values[1], config.red, lineOffset); | ||
} else if ("COLOR_grey" == values[0]) { | ||
parsed = this->parseColor(values[1], config.grey, lineOffset); | ||
} else if ("COLOR_white" == values[0]) { | ||
parsed = this->parseColor(values[1], config.white, lineOffset); | ||
} else if ("COLOR_debug" == values[0]) { | ||
parsed = this->parseColor(values[1], config.debug, lineOffset); | ||
} else { | ||
this->m_errorLine = lineOffset; | ||
this->m_errorMessage = "Unknown file entry: " + value[0]; | ||
return false; | ||
} | ||
|
||
if (false == parsed) { | ||
return false; | ||
} | ||
} | ||
|
||
config.valid = true; | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <string> | ||
|
||
#include "config/PluginConfig.h" | ||
|
||
namespace vacdm { | ||
/// @brief parses the config file .txt to type PluginConfig. Also provides information about errors. | ||
class ConfigParser { | ||
private: | ||
std::uint32_t m_errorLine; /* Defines the line number the error has occurred */ | ||
std::string m_errorMessage; /* The error message to print */ | ||
bool parseColor(const std::string &block, COLORREF &color, std::uint32_t line); | ||
|
||
public: | ||
ConfigParser(); | ||
|
||
bool errorFound() const; | ||
std::uint32_t errorLine() const; | ||
const std::string &errorMessage() const; | ||
|
||
bool parse(const std::string &filename, PluginConfig &config); | ||
}; | ||
} // namespace vacdm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
|
||
#pragma warning(push, 0) | ||
#include "EuroScopePlugIn.h" | ||
#pragma warning(pop) | ||
|
||
namespace vacdm { | ||
struct PluginConfig { | ||
bool valid = true; | ||
std::string serverUrl = "https://app.vacdm.net"; | ||
int updateCycleSeconds = 5; | ||
COLORREF lightgreen = RGB(127, 252, 73); | ||
COLORREF lightblue = RGB(53, 218, 235); | ||
COLORREF green = RGB(0, 181, 27); | ||
COLORREF blue = RGB(0, 0, 255); | ||
COLORREF lightyellow = RGB(255, 255, 191); | ||
COLORREF yellow = RGB(255, 255, 0); | ||
COLORREF orange = RGB(255, 153, 0); | ||
COLORREF red = RGB(255, 0, 0); | ||
COLORREF grey = RGB(153, 153, 153); | ||
COLORREF white = RGB(255, 255, 255); | ||
COLORREF debug = RGB(255, 0, 255); | ||
}; | ||
} // namespace vacdm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
SERVER_url=https://app.vacdm.net | ||
UPDATE_RATE_SECONDS=5 | ||
COLOR_lightgreen=127,252,73 | ||
COLOR_lightblue=53,218,235 | ||
COLOR_green=0,181,27 | ||
COLOR_blue=0,0,255 | ||
COLOR_lightyellow=255,255,191 | ||
COLOR_yellow=255,255,0 | ||
COLOR_orange=255,153,0 | ||
COLOR_red=255,0,0 | ||
COLOR_grey=153,153,153 | ||
COLOR_white=255,255,255 | ||
COLOR_debug=255,0,255 |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#pragma once | ||
|
||
#include <list> | ||
#include <map> | ||
#include <mutex> | ||
#include <string> | ||
#include <thread> | ||
|
||
#pragma warning(push, 0) | ||
#include "EuroScopePlugIn.h" | ||
#pragma warning(pop) | ||
|
||
#include <json/json.h> | ||
|
||
#include "types/Pilot.h" | ||
|
||
using namespace vacdm; | ||
|
||
namespace vacdm::core { | ||
|
||
constexpr int maxUpdateCycleSeconds = 10; | ||
constexpr int minUpdateCycleSeconds = 1; | ||
class DataManager { | ||
private: | ||
DataManager(); | ||
|
||
std::thread m_worker; | ||
bool m_pause; | ||
bool m_stop; | ||
|
||
void run(); | ||
int updateCycleSeconds = 5; | ||
|
||
public: | ||
~DataManager(); | ||
DataManager(const DataManager &) = delete; | ||
DataManager(DataManager &&) = delete; | ||
|
||
DataManager &operator=(const DataManager &) = delete; | ||
DataManager &operator=(DataManager &&) = delete; | ||
static DataManager &instance(); | ||
|
||
std::string setUpdateCycleSeconds(const int newUpdateCycleSeconds); | ||
|
||
enum class MessageType { | ||
None, | ||
Post, | ||
Patch, | ||
UpdateEXOT, | ||
UpdateTOBT, | ||
UpdateTOBTConfirmed, | ||
UpdateASAT, | ||
UpdateASRT, | ||
UpdateAOBT, | ||
UpdateAORT, | ||
ResetTOBT, | ||
ResetASAT, | ||
ResetASRT, | ||
ResetTOBTConfirmed, | ||
ResetAORT, | ||
ResetAOBT, | ||
ResetPilot | ||
}; | ||
|
||
private: | ||
std::mutex m_pilotLock; | ||
std::map<std::string, std::array<types::Pilot, 3>> m_pilots; | ||
std::mutex m_airportLock; | ||
std::list<std::string> m_activeAirports; | ||
|
||
struct EuroscopeFlightplanUpdate { | ||
std::chrono::utc_clock::time_point timeIssued; | ||
EuroScopePlugIn::CFlightPlan data; | ||
}; | ||
|
||
std::mutex m_euroscopeUpdatesLock; | ||
std::list<EuroscopeFlightplanUpdate> m_euroscopeFlightplanUpdates; | ||
|
||
/// @brief consolidates all flightplan updates by throwing out old updates and keeping the most current ones | ||
/// @param list of flightplans to consolidate | ||
void consolidateFlightplanUpdates(std::list<EuroscopeFlightplanUpdate> &list); | ||
/// @brief updates the pilots with the saved EuroScope flightplan updates | ||
/// @param pilots to update | ||
void processEuroScopeUpdates(std::map<std::string, std::array<types::Pilot, 3U>> &pilots); | ||
/// @brief gathers all information from EuroScope::CFlightPlan and converts it to type Pilot | ||
types::Pilot CFlightPlanToPilot(const EuroScopePlugIn::CFlightPlan flightplan); | ||
/// @brief updates the local data with the data from the backend | ||
/// @param pilots to update | ||
void consolidateWithBackend(std::map<std::string, std::array<types::Pilot, 3U>> &pilots); | ||
/// @brief consolidates EuroScope and backend data | ||
/// @param pilot | ||
void consolidateData(std::array<types::Pilot, 3> &pilot); | ||
|
||
MessageType deltaEuroscopeToBackend(const std::array<types::Pilot, 3> &data, Json::Value &message); | ||
|
||
struct AsynchronousMessage { | ||
const MessageType type; | ||
const std::string callsign; | ||
const std::chrono::utc_clock::time_point value; | ||
}; | ||
|
||
std::mutex m_asyncMessagesLock; | ||
std::list<struct AsynchronousMessage> m_asynchronousMessages; | ||
void processAsynchronousMessages(std::map<std::string, std::array<types::Pilot, 3U>> &pilots); | ||
|
||
public: | ||
void setActiveAirports(const std::list<std::string> activeAirports); | ||
void queueFlightplanUpdate(EuroScopePlugIn::CFlightPlan flightplan); | ||
void handleTagFunction(MessageType message, const std::string callsign, | ||
const std::chrono::utc_clock::time_point value); | ||
|
||
bool checkPilotExists(const std::string &callsign); | ||
const types::Pilot getPilot(const std::string &callsign); | ||
void pause(); | ||
void resume(); | ||
}; | ||
} // namespace vacdm::core |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#pragma once | ||
|
||
#define CURL_STATICLIB 1 | ||
#include <curl/curl.h> | ||
#include <json/json.h> | ||
|
||
#include <list> | ||
#include <mutex> | ||
#include <string> | ||
|
||
#include "types/Pilot.h" | ||
|
||
namespace vacdm::com { | ||
class Server { | ||
public: | ||
typedef struct ServerConfiguration_t { | ||
std::string name = ""; | ||
bool allowMasterInSweatbox = false; | ||
bool allowMasterAsObserver = false; | ||
} ServerConfiguration; | ||
|
||
private: | ||
Server(); | ||
struct Communication { | ||
std::mutex lock; | ||
CURL* socket; | ||
|
||
Communication() : lock(), socket(curl_easy_init()) {} | ||
}; | ||
|
||
std::string m_authToken; | ||
Communication m_getRequest; | ||
Communication m_postRequest; | ||
Communication m_patchRequest; | ||
Communication m_deleteRequest; | ||
|
||
bool m_apiIsChecked; | ||
bool m_apiIsValid; | ||
std::string m_baseUrl; | ||
bool m_clientIsMaster; | ||
std::string m_errorCode; | ||
ServerConfiguration m_serverConfiguration; | ||
|
||
public: | ||
~Server(); | ||
Server(const Server&) = delete; | ||
Server(Server&&) = delete; | ||
|
||
Server& operator=(const Server&) = delete; | ||
Server& operator=(Server&&) = delete; | ||
|
||
static Server& instance(); | ||
|
||
void changeServerAddress(const std::string& url); | ||
bool checkWebApi(); | ||
ServerConfiguration_t getServerConfig(); | ||
std::list<types::Pilot> getPilots(const std::list<std::string> airports); | ||
void postPilot(types::Pilot); | ||
void patchPilot(const Json::Value& root); | ||
|
||
/// @brief Sends a post message to the specififed endpoint url with the root as content | ||
/// @param endpointUrl endpoint url to send the request to | ||
/// @param root message content | ||
void sendPostMessage(const std::string& endpointUrl, const Json::Value& root); | ||
|
||
/// @brief Sends a patch message to the specified endpoint url with the root as content | ||
/// @param endpointUrl endpoint url to send the request to | ||
/// @param root message content | ||
void sendPatchMessage(const std::string& endpointUrl, const Json::Value& root); | ||
void sendDeleteMessage(const std::string& endpointUrl); | ||
|
||
void updateExot(const std::string& pilot, const std::chrono::utc_clock::time_point& exot); | ||
void updateTobt(const types::Pilot& pilot, const std::chrono::utc_clock::time_point& tobt, bool manualTobt); | ||
void updateAsat(const std::string& callsign, const std::chrono::utc_clock::time_point& asat); | ||
void updateAsrt(const std::string& callsign, const std::chrono::utc_clock::time_point& asrt); | ||
void updateAobt(const std::string& callsign, const std::chrono::utc_clock::time_point& aobt); | ||
void updateAort(const std::string& callsign, const std::chrono::utc_clock::time_point& aort); | ||
|
||
void resetTobt(const std::string& callsign, const std::chrono::utc_clock::time_point& tobt, | ||
const std::string& tobtState); | ||
void deletePilot(const std::string& callsign); | ||
|
||
const std::string& errorMessage() const; | ||
void setMaster(bool master); | ||
bool getMaster(); | ||
}; | ||
} // namespace vacdm::com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
#pragma once | ||
|
||
#pragma warning(push, 0) | ||
#include "EuroScopePlugIn.h" | ||
#pragma warning(pop) | ||
|
||
#include "core/DataManager.h" | ||
#include "core/Server.h" | ||
#include "types/Pilot.h" | ||
#include "utils/Date.h" | ||
#include "utils/Number.h" | ||
|
||
using namespace vacdm; | ||
using namespace vacdm::core; | ||
using namespace vacdm::com; | ||
|
||
namespace vacdm::tagfunctions { | ||
|
||
enum itemFunction { | ||
EXOT_MODIFY = 1, | ||
EXOT_NEW_VALUE, | ||
TOBT_NOW, | ||
TOBT_MANUAL, | ||
TOBT_MANUAL_EDIT, | ||
TOBT_MENU, | ||
ASAT_NOW, | ||
ASAT_NOW_AND_STARTUP, | ||
STARTUP_REQUEST, | ||
TOBT_CONFIRM, | ||
OFFBLOCK_REQUEST, | ||
AOBT_NOW_AND_STATE, | ||
RESET_TOBT, | ||
RESET_ASAT, | ||
RESET_ASRT, | ||
RESET_TOBT_CONFIRM, | ||
RESET_AORT, | ||
RESET_AOBT_AND_STATE, | ||
RESET_MENU, | ||
RESET_PILOT, | ||
}; | ||
|
||
void RegisterTagItemFuntions(vACDM *plugin) { | ||
plugin->RegisterTagItemFunction("Modify EXOT", EXOT_MODIFY); | ||
plugin->RegisterTagItemFunction("TOBT now", TOBT_NOW); | ||
plugin->RegisterTagItemFunction("Set TOBT", TOBT_MANUAL); | ||
plugin->RegisterTagItemFunction("TOBT confirm", TOBT_CONFIRM); | ||
plugin->RegisterTagItemFunction("Tobt menu", TOBT_MENU); | ||
plugin->RegisterTagItemFunction("ASAT now", ASAT_NOW); | ||
plugin->RegisterTagItemFunction("ASAT now and startup state", ASAT_NOW_AND_STARTUP); | ||
plugin->RegisterTagItemFunction("Startup Request", STARTUP_REQUEST); | ||
plugin->RegisterTagItemFunction("Request Offblock", OFFBLOCK_REQUEST); | ||
plugin->RegisterTagItemFunction("Set AOBT and Groundstate", AOBT_NOW_AND_STATE); | ||
// Reset Functions | ||
plugin->RegisterTagItemFunction("Reset TOBT", RESET_TOBT); | ||
plugin->RegisterTagItemFunction("Reset ASAT", RESET_ASAT); | ||
plugin->RegisterTagItemFunction("Reset confirmed TOBT", RESET_TOBT_CONFIRM); | ||
plugin->RegisterTagItemFunction("Reset Offblock Request", RESET_AORT); | ||
plugin->RegisterTagItemFunction("Reset AOBT", RESET_AOBT_AND_STATE); | ||
plugin->RegisterTagItemFunction("Reset Menu", RESET_MENU); | ||
plugin->RegisterTagItemFunction("Reset pilot", RESET_PILOT); | ||
} | ||
|
||
void handleTagFunction(vACDM *plugin, int functionId, const char *itemString, POINT pt, RECT area) { | ||
std::ignore = pt; | ||
|
||
// do not handle functions if client is not master | ||
if (false == Server::instance().getMaster()) return; | ||
|
||
auto flightplan = plugin->FlightPlanSelectASEL(); | ||
std::string callsign(flightplan.GetCallsign()); | ||
|
||
if (false == DataManager::instance().checkPilotExists(callsign)) return; | ||
|
||
auto pilot = DataManager::instance().getPilot(callsign); | ||
|
||
switch (static_cast<itemFunction>(functionId)) { | ||
case EXOT_MODIFY: | ||
plugin->OpenPopupEdit(area, static_cast<int>(itemFunction::EXOT_NEW_VALUE), itemString); | ||
break; | ||
case EXOT_NEW_VALUE: | ||
if (true == isNumber(itemString)) { | ||
const auto exot = std::chrono::utc_clock::time_point(std::chrono::minutes(std::atoi(itemString))); | ||
if (exot != pilot.exot) | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateEXOT, pilot.callsign, | ||
exot); | ||
} | ||
break; | ||
case TOBT_NOW: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateTOBT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
break; | ||
case TOBT_MANUAL: | ||
plugin->OpenPopupEdit(area, TOBT_MANUAL_EDIT, ""); | ||
break; | ||
case TOBT_MANUAL_EDIT: { | ||
std::string clock(itemString); | ||
if (clock.length() == 4 && isNumber(clock)) { | ||
const auto hours = std::atoi(clock.substr(0, 2).c_str()); | ||
const auto minutes = std::atoi(clock.substr(2, 4).c_str()); | ||
if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60) | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateTOBTConfirmed, | ||
pilot.callsign, | ||
utils::Date::convertStringToTimePoint(clock)); | ||
else | ||
plugin->DisplayMessage("Invalid time format. Expected: HHMM (24 hours)"); | ||
} else if (clock.length() != 0) { | ||
plugin->DisplayMessage("Invalid time format. Expected: HHMM (24 hours)"); | ||
} | ||
break; | ||
} | ||
case ASAT_NOW: { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateASAT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
// if ASRT has not been set yet -> set ASRT | ||
if (pilot.asrt == types::defaultTime) { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateASRT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
} | ||
break; | ||
} | ||
case ASAT_NOW_AND_STARTUP: { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateASAT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
|
||
// if ASRT has not been set yet -> set ASRT | ||
if (pilot.asrt == types::defaultTime) { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateASRT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
} | ||
|
||
plugin->SetGroundState(flightplan, "ST-UP"); | ||
|
||
break; | ||
} | ||
case STARTUP_REQUEST: { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateASRT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
break; | ||
} | ||
case AOBT_NOW_AND_STATE: { | ||
// set ASRT if ASRT has not been set yet | ||
if (pilot.asrt == types::defaultTime) { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateAORT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
} | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateAOBT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
|
||
// set status depending on if the aircraft is positioned at a taxi-out position | ||
if (pilot.taxizoneIsTaxiout) { | ||
plugin->SetGroundState(flightplan, "TAXI"); | ||
} else { | ||
plugin->SetGroundState(flightplan, "PUSH"); | ||
} | ||
break; | ||
} | ||
case TOBT_CONFIRM: { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateTOBTConfirmed, pilot.callsign, | ||
pilot.tobt); | ||
break; | ||
} | ||
case OFFBLOCK_REQUEST: { | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::UpdateAORT, pilot.callsign, | ||
std::chrono::utc_clock::now()); | ||
break; | ||
} | ||
case TOBT_MENU: { | ||
plugin->OpenPopupList(area, "TOBT menu", 1); | ||
plugin->AddPopupListElement("TOBT now", NULL, TOBT_NOW, false, 2, false, false); | ||
plugin->AddPopupListElement("TOBT edit", NULL, TOBT_MANUAL, false, 2, false, false); | ||
plugin->AddPopupListElement("TOBT confirm", NULL, TOBT_CONFIRM, false, 2, false, false); | ||
break; | ||
} | ||
case RESET_TOBT: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetTOBT, pilot.callsign, | ||
types::defaultTime); | ||
break; | ||
case RESET_ASAT: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetASAT, pilot.callsign, | ||
types::defaultTime); | ||
plugin->SetGroundState(flightplan, "NSTS"); | ||
break; | ||
case RESET_ASRT: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetASRT, pilot.callsign, | ||
types::defaultTime); | ||
break; | ||
case RESET_TOBT_CONFIRM: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetTOBTConfirmed, pilot.callsign, | ||
types::defaultTime); | ||
break; | ||
case RESET_AORT: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetAORT, pilot.callsign, | ||
types::defaultTime); | ||
break; | ||
case RESET_AOBT_AND_STATE: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetAOBT, pilot.callsign, | ||
types::defaultTime); | ||
plugin->SetGroundState(flightplan, "NSTS"); | ||
break; | ||
case RESET_MENU: | ||
plugin->OpenPopupList(area, "RESET menu", 1); | ||
plugin->AddPopupListElement("Reset TOBT", NULL, RESET_TOBT, false, 2, false, false); | ||
plugin->AddPopupListElement("Reset ASAT", NULL, RESET_ASAT, false, 2, false, false); | ||
plugin->AddPopupListElement("Reset ASRT", NULL, RESET_ASRT, false, 2, false, false); | ||
plugin->AddPopupListElement("Reset confirmed TOBT", NULL, RESET_TOBT_CONFIRM, false, 2, false, false); | ||
plugin->AddPopupListElement("Reset AORT", NULL, RESET_AORT, false, 2, false, false); | ||
plugin->AddPopupListElement("Reset AOBT", NULL, RESET_AOBT_AND_STATE, false, 2, false, false); | ||
plugin->AddPopupListElement("Reset Pilot", NULL, RESET_PILOT, false, 2, false, false); | ||
break; | ||
case RESET_PILOT: | ||
DataManager::instance().handleTagFunction(DataManager::MessageType::ResetPilot, pilot.callsign, | ||
types::defaultTime); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
} // namespace vacdm::tagfunctions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
#pragma once | ||
|
||
#include <wtypes.h> | ||
|
||
#include <chrono> | ||
#include <format> | ||
#include <string> | ||
|
||
#include "TagItemsColor.h" | ||
#include "types/Pilot.h" | ||
|
||
namespace vacdm::tagitems { | ||
enum itemType { | ||
EOBT, | ||
TOBT, | ||
TSAT, | ||
TTOT, | ||
EXOT, | ||
ASAT, | ||
AOBT, | ||
ATOT, | ||
ASRT, | ||
AORT, | ||
CTOT, | ||
ECFMP_MEASURES, | ||
EVENT_BOOKING, | ||
}; | ||
|
||
void RegisterTagItemTypes(vACDM *plugin) { | ||
plugin->RegisterTagItemType("EOBT", itemType::EOBT); | ||
plugin->RegisterTagItemType("TOBT", itemType::TOBT); | ||
plugin->RegisterTagItemType("TSAT", itemType::TSAT); | ||
plugin->RegisterTagItemType("TTOT", itemType::TTOT); | ||
plugin->RegisterTagItemType("EXOT", itemType::EXOT); | ||
plugin->RegisterTagItemType("ASAT", itemType::ASAT); | ||
plugin->RegisterTagItemType("AOBT", itemType::AOBT); | ||
plugin->RegisterTagItemType("ATOT", itemType::ATOT); | ||
plugin->RegisterTagItemType("ASRT", itemType::ASRT); | ||
plugin->RegisterTagItemType("AORT", itemType::AORT); | ||
plugin->RegisterTagItemType("CTOT", itemType::CTOT); | ||
plugin->RegisterTagItemType("Event Booking", itemType::EVENT_BOOKING); | ||
plugin->RegisterTagItemType("ECFMP Measures", itemType::ECFMP_MEASURES); | ||
} | ||
|
||
std::string formatTime(const std::chrono::utc_clock::time_point timepoint) { | ||
if (timepoint.time_since_epoch().count() > 0) | ||
return std::format("{:%H%M}", timepoint); | ||
else | ||
return ""; | ||
} | ||
|
||
void displayTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, EuroScopePlugIn::CRadarTarget RadarTarget, int ItemCode, | ||
int TagData, char sItemString[16], int *pColorCode, COLORREF *pRGB, double *pFontSize) { | ||
std::ignore = RadarTarget; | ||
std::ignore = TagData; | ||
std::ignore = pRGB; | ||
std::ignore = pFontSize; | ||
|
||
*pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; | ||
if (nullptr == FlightPlan.GetFlightPlanData().GetPlanType() || | ||
0 == std::strlen(FlightPlan.GetFlightPlanData().GetPlanType())) | ||
return; | ||
// skip non IFR flights | ||
if (std::string_view("I") != FlightPlan.GetFlightPlanData().GetPlanType()) { | ||
return; | ||
} | ||
std::string callsign = FlightPlan.GetCallsign(); | ||
|
||
if (false == DataManager::instance().checkPilotExists(callsign)) return; | ||
|
||
auto pilot = DataManager::instance().getPilot(callsign); | ||
|
||
std::stringstream outputText; | ||
|
||
switch (static_cast<itemType>(ItemCode)) { | ||
case itemType::EOBT: | ||
outputText << formatTime(pilot.eobt); | ||
*pRGB = Color::colorizeEobt(pilot); | ||
break; | ||
case itemType::TOBT: | ||
outputText << formatTime(pilot.tobt); | ||
*pRGB = Color::colorizeTobt(pilot); | ||
break; | ||
case itemType::TSAT: | ||
outputText << formatTime(pilot.tsat); | ||
*pRGB = Color::colorizeTsat(pilot); | ||
break; | ||
case itemType::TTOT: | ||
outputText << formatTime(pilot.ttot); | ||
*pRGB = Color::colorizeTtot(pilot); | ||
break; | ||
case itemType::EXOT: | ||
if (pilot.exot.time_since_epoch().count() > 0) { | ||
outputText << std::format("{:%M}", pilot.exot); | ||
*pRGB = Color::colorizeExot(pilot); | ||
} | ||
break; | ||
case itemType::ASAT: | ||
outputText << formatTime(pilot.asat); | ||
*pRGB = Color::colorizeAsat(pilot); | ||
break; | ||
case itemType::AOBT: | ||
outputText << formatTime(pilot.aobt); | ||
*pRGB = Color::colorizeAobt(pilot); | ||
break; | ||
case itemType::ATOT: | ||
outputText << formatTime(pilot.atot); | ||
*pRGB = Color::colorizeAtot(pilot); | ||
break; | ||
case itemType::ASRT: | ||
outputText << formatTime(pilot.asrt); | ||
*pRGB = Color::colorizeAsrt(pilot); | ||
break; | ||
case itemType::AORT: | ||
outputText << formatTime(pilot.aort); | ||
*pRGB = Color::colorizeAort(pilot); | ||
break; | ||
case itemType::CTOT: | ||
outputText << formatTime(pilot.ctot); | ||
*pRGB = Color::colorizeCtot(pilot); | ||
break; | ||
case itemType::ECFMP_MEASURES: | ||
outputText << ""; | ||
*pRGB = Color::colorizeEcfmpMeasure(pilot); | ||
break; | ||
case itemType::EVENT_BOOKING: | ||
outputText << (pilot.hasBooking ? "B" : ""); | ||
*pRGB = Color::colorizeEventBooking(pilot); | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
std::strcpy(sItemString, outputText.str().c_str()); | ||
} | ||
} // namespace vacdm::tagitems |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
#pragma once | ||
|
||
#include "config/PluginConfig.h" | ||
#include "types/Pilot.h" | ||
|
||
using namespace vacdm; | ||
|
||
namespace vacdm::tagitems { | ||
class Color { | ||
public: | ||
Color() = delete; | ||
Color(const Color &) = delete; | ||
Color(Color &&) = delete; | ||
Color &operator=(const Color &) = delete; | ||
Color &operator=(Color &&) = delete; | ||
|
||
static inline PluginConfig pluginConfig; | ||
static types::Pilot pilotT; | ||
static void updatePluginConfig(PluginConfig newPluginConfig) { pluginConfig = newPluginConfig; } | ||
|
||
static COLORREF colorizeEobt(const types::Pilot &pilot) { return colorizeEobtAndTobt(pilot); } | ||
|
||
static COLORREF colorizeTobt(const types::Pilot &pilot) { return colorizeEobtAndTobt(pilot); } | ||
|
||
static COLORREF colorizeTsat(const types::Pilot &pilot) { | ||
if (pilot.asat != types::defaultTime || pilot.tsat == types::defaultTime) { | ||
return pluginConfig.grey; | ||
} | ||
const auto timeSinceTsat = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.tsat).count(); | ||
if (timeSinceTsat <= 5 * 60 && timeSinceTsat >= -5 * 60) { | ||
// CTOT exists | ||
if (pilot.ctot.time_since_epoch().count() > 0) { | ||
return pluginConfig.blue; | ||
} | ||
return pluginConfig.green; | ||
} | ||
// TSAT earlier than 5+ min | ||
if (timeSinceTsat < -5 * 60) { | ||
// CTOT exists | ||
if (pilot.ctot.time_since_epoch().count() > 0) { | ||
return pluginConfig.lightblue; | ||
} | ||
return pluginConfig.lightgreen; | ||
} | ||
// TSAT passed by 5+ min | ||
if (timeSinceTsat > 5 * 60) { | ||
// CTOT exists | ||
if (pilot.ctot.time_since_epoch().count() > 0) { | ||
return pluginConfig.red; | ||
} | ||
return pluginConfig.orange; | ||
} | ||
return pluginConfig.debug; | ||
} | ||
|
||
static COLORREF colorizeTtot(const types::Pilot &pilot) { | ||
if (pilot.ttot == types::defaultTime) { | ||
return pluginConfig.grey; | ||
} | ||
|
||
auto now = std::chrono::utc_clock::now(); | ||
|
||
// Round up to the next 10, 20, 30, 40, 50, or 00 minute interval | ||
auto timeSinceEpoch = pilot.ttot.time_since_epoch(); | ||
auto minutesSinceEpoch = std::chrono::duration_cast<std::chrono::minutes>(timeSinceEpoch); | ||
std::chrono::time_point<std::chrono::utc_clock> rounded; | ||
|
||
// Compute the number of minutes remaining to the next highest ten | ||
auto remainingMinutes = 10 - minutesSinceEpoch.count() % 10; | ||
|
||
// If the time point is already at a multiple of ten minutes, no rounding is needed | ||
if (remainingMinutes == 10) { | ||
rounded = std::chrono::time_point_cast<std::chrono::minutes>(pilot.ttot); | ||
} else { | ||
// Add the remaining minutes to the time point | ||
auto roundedUpMinutes = minutesSinceEpoch + std::chrono::minutes(remainingMinutes); | ||
|
||
// Convert back to a time_point object and return | ||
rounded = std::chrono::time_point_cast<std::chrono::minutes>( | ||
std::chrono::utc_clock::time_point(roundedUpMinutes)); | ||
rounded += std::chrono::seconds(30); | ||
} | ||
|
||
// Check if the current time has passed the ttot time point | ||
if (pilot.atot.time_since_epoch().count() > 0) { | ||
// ATOT exists | ||
return pluginConfig.grey; | ||
} | ||
if (now < rounded) { | ||
// time before TTOT and during TTOT block | ||
return pluginConfig.green; | ||
} else if (now >= rounded) { | ||
// time past TTOT / TTOT block | ||
return pluginConfig.orange; | ||
} | ||
return pluginConfig.debug; | ||
} | ||
|
||
static COLORREF colorizeExot(const types::Pilot &pilot) { | ||
std::ignore = pilot; | ||
return EuroScopePlugIn::TAG_COLOR_DEFAULT; | ||
} | ||
|
||
static COLORREF colorizeAsat(const types::Pilot &pilot) { | ||
if (pilot.asat == types::defaultTime) { | ||
return pluginConfig.grey; | ||
} | ||
|
||
if (pilot.aobt.time_since_epoch().count() > 0) { | ||
return pluginConfig.grey; | ||
} | ||
|
||
const auto timeSinceAsat = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.asat).count(); | ||
const auto timeSinceTsat = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.tsat).count(); | ||
if (pilot.taxizoneIsTaxiout == false) { | ||
if (/* Datalink clearance == true &&*/ timeSinceTsat >= -5 * 60 && timeSinceTsat <= 5 * 60) { | ||
return pluginConfig.green; | ||
} | ||
if (timeSinceAsat < 5 * 60) { | ||
return pluginConfig.green; | ||
} | ||
} | ||
if (pilot.taxizoneIsTaxiout == true) { | ||
if (timeSinceTsat >= -5 * 60 && timeSinceTsat <= 10 * 60 /* && Datalink clearance == true*/) { | ||
return pluginConfig.green; | ||
} | ||
if (timeSinceAsat < 10 * 60) { | ||
return pluginConfig.green; | ||
} | ||
} | ||
return pluginConfig.orange; | ||
} | ||
|
||
static COLORREF colorizeAobt(const types::Pilot &pilot) { | ||
std::ignore = pilot; | ||
return pluginConfig.grey; | ||
} | ||
|
||
static COLORREF colorizeAtot(const types::Pilot &pilot) { | ||
std::ignore = pilot; | ||
return pluginConfig.grey; | ||
} | ||
|
||
static COLORREF colorizeAsrt(const types::Pilot &pilot) { | ||
if (pilot.asat.time_since_epoch().count() > 0) { | ||
return pluginConfig.grey; | ||
} | ||
const auto timeSinceAsrt = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.asrt).count(); | ||
if (timeSinceAsrt <= 5 * 60 && timeSinceAsrt >= 0) { | ||
return pluginConfig.green; | ||
} | ||
if (timeSinceAsrt > 5 * 60 && timeSinceAsrt <= 10 * 60) { | ||
return pluginConfig.yellow; | ||
} | ||
if (timeSinceAsrt > 10 * 60 && timeSinceAsrt <= 15 * 60) { | ||
return pluginConfig.orange; | ||
} | ||
if (timeSinceAsrt > 15 * 60) { | ||
return pluginConfig.red; | ||
} | ||
|
||
return pluginConfig.debug; | ||
} | ||
|
||
static COLORREF colorizeAort(const types::Pilot &pilot) { | ||
if (pilot.aort == types::defaultTime) { | ||
return pluginConfig.grey; | ||
} | ||
if (pilot.aobt.time_since_epoch().count() > 0) { | ||
return pluginConfig.grey; | ||
} | ||
const auto timeSinceAort = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.aort).count(); | ||
|
||
if (timeSinceAort <= 5 * 60 && timeSinceAort >= 0) { | ||
return pluginConfig.green; | ||
} | ||
if (timeSinceAort > 5 * 60 && timeSinceAort <= 10 * 60) { | ||
return pluginConfig.yellow; | ||
} | ||
if (timeSinceAort > 10 * 60 && timeSinceAort <= 15 * 60) { | ||
return pluginConfig.orange; | ||
} | ||
if (timeSinceAort > 15 * 60) { | ||
return pluginConfig.red; | ||
} | ||
|
||
return pluginConfig.debug; | ||
} | ||
|
||
static COLORREF colorizeCtot(const types::Pilot &pilot) { return colorizeCtotandCtottimer(pilot); } | ||
|
||
static COLORREF colorizeCtotTimer(const types::Pilot &pilot) { return colorizeCtotandCtottimer(pilot); } | ||
|
||
static COLORREF colorizeAsatTimer(const types::Pilot &pilot) { | ||
// aort set | ||
if (pilot.aort.time_since_epoch().count() > 0) { | ||
return pluginConfig.grey; | ||
} | ||
const auto timeSinceAobt = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.aobt).count(); | ||
if (timeSinceAobt >= 0) { | ||
// hide Timer | ||
} | ||
const auto timeSinceAsat = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.asat).count(); | ||
const auto timeSinceTsat = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.tsat).count(); | ||
// Pushback required | ||
if (pilot.taxizoneIsTaxiout != false) { | ||
/* | ||
if (hasdatalinkclearance == true && timesincetsat >=5*60 && timesincetsat <=5*60) | ||
{ | ||
return pluginConfig.green | ||
} */ | ||
if (timeSinceAsat < 5 * 60) { | ||
return pluginConfig.green; | ||
} | ||
} | ||
if (pilot.taxizoneIsTaxiout == true) { | ||
if (timeSinceTsat >= -5 * 60 && timeSinceTsat <= 10 * 60) { | ||
return pluginConfig.green; | ||
} | ||
if (timeSinceAsat <= 10 * 60) { | ||
return pluginConfig.green; | ||
} | ||
} | ||
return pluginConfig.orange; | ||
} | ||
|
||
// other: | ||
|
||
static COLORREF colorizeEcfmpMeasure(const types::Pilot &pilot) { | ||
return pilot.measures.empty() ? pluginConfig.grey : pluginConfig.green; | ||
} | ||
|
||
static COLORREF colorizeEventBooking(const types::Pilot &pilot) { | ||
return pilot.hasBooking ? pluginConfig.green : pluginConfig.grey; | ||
} | ||
|
||
private: | ||
static COLORREF colorizeEobtAndTobt(const types::Pilot &pilot) { | ||
const auto now = std::chrono::utc_clock::now(); | ||
const auto timeSinceTobt = std::chrono::duration_cast<std::chrono::seconds>(now - pilot.tobt).count(); | ||
const auto timeSinceTsat = std::chrono::duration_cast<std::chrono::seconds>(now - pilot.tsat).count(); | ||
const auto diffTsatTobt = std::chrono::duration_cast<std::chrono::seconds>(pilot.tsat - pilot.tobt).count(); | ||
|
||
if (pilot.tsat == types::defaultTime) { | ||
return pluginConfig.grey; | ||
} | ||
// ASAT exists | ||
if (pilot.asat.time_since_epoch().count() > 0) { | ||
return pluginConfig.grey; | ||
} | ||
// TOBT in past && TSAT expired, i.e. 5min past TSAT || TOBT >= +1h || TSAT does not exist && TOBT in past | ||
// -> TOBT in past && (TSAT expired || TSAT does not exist) || TOBT >= now + 1h | ||
if (timeSinceTobt > 0 && (timeSinceTsat >= 5 * 60 || pilot.tsat == types::defaultTime) || | ||
pilot.tobt >= now + std::chrono::hours(1)) // last statement could cause problems | ||
{ | ||
return pluginConfig.orange; | ||
} | ||
// Diff TOBT TSAT >= 5min && unconfirmed | ||
if (diffTsatTobt >= 5 * 60 && (pilot.tobt_state == "GUESS" || pilot.tobt_state == "FLIGHTPLAN")) { | ||
return pluginConfig.lightyellow; | ||
} | ||
// Diff TOBT TSAT >= 5min && confirmed | ||
if (diffTsatTobt >= 5 * 60 && pilot.tobt_state == "CONFIRMED") { | ||
return pluginConfig.yellow; | ||
} | ||
// Diff TOBT TSAT < 5min | ||
if (diffTsatTobt < 5 * 60 && pilot.tobt_state == "CONFIRMED") { | ||
return pluginConfig.green; | ||
} | ||
// tobt is not confirmed | ||
if (pilot.tobt_state != "CONFIRMED") { | ||
return pluginConfig.lightgreen; | ||
} | ||
return pluginConfig.debug; | ||
} | ||
|
||
static COLORREF colorizeCtotandCtottimer(const types::Pilot &pilot) { | ||
if (pilot.ctot == types::defaultTime) { | ||
return pluginConfig.grey; | ||
} | ||
|
||
const auto timetoctot = | ||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now() - pilot.ctot).count(); | ||
if (timetoctot >= 5 * 60) { | ||
return pluginConfig.lightgreen; | ||
} | ||
if (timetoctot <= 5 * 60 && timetoctot >= -10 * 60) { | ||
return pluginConfig.green; | ||
} | ||
if (timetoctot < -10 * 60) { | ||
return pluginConfig.orange; | ||
} | ||
|
||
return pluginConfig.grey; | ||
} | ||
}; | ||
} // namespace vacdm::tagitems |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
#include "Logger.h" | ||
|
||
#ifdef DEBUG_BUILD | ||
#include <Windows.h> | ||
|
||
#include <iostream> | ||
#endif | ||
|
||
#include <algorithm> | ||
#include <chrono> | ||
#include <numeric> | ||
|
||
#include "utils/String.h" | ||
|
||
using namespace std::chrono_literals; | ||
using namespace vacdm::logging; | ||
|
||
static const char __loggingTable[] = | ||
"CREATE TABLE messages( \ | ||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, \ | ||
sender TEXT, \ | ||
level INT, \ | ||
message TEXT \ | ||
);"; | ||
static const std::string __insertMessage = "INSERT INTO messages VALUES (CURRENT_TIMESTAMP, @1, @2, @3)"; | ||
|
||
Logger::Logger() { | ||
stream << std::format("{0:%Y%m%d%H%M%S}", std::chrono::utc_clock::now()) << ".vacdm"; | ||
#ifdef DEBUG_BUILD | ||
AllocConsole(); | ||
#pragma warning(push) | ||
#pragma warning(disable : 6031) | ||
freopen("CONOUT$", "w", stdout); | ||
freopen("CONOUT$", "w", stderr); | ||
#pragma warning(pop) | ||
this->enableLogging(); | ||
#endif | ||
this->m_logWriter = std::thread(&Logger::run, this); | ||
} | ||
|
||
Logger::~Logger() { | ||
this->m_stop = true; | ||
this->m_logWriter.join(); | ||
|
||
if (nullptr != this->m_database) sqlite3_close_v2(this->m_database); | ||
} | ||
|
||
void Logger::run() { | ||
while (true) { | ||
if (m_stop) return; | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
|
||
// obtain a copy of the logs, clear the log list to minimize lock time | ||
this->m_logLock.lock(); | ||
auto logs = m_asynchronousLogs; | ||
m_asynchronousLogs.clear(); | ||
this->m_logLock.unlock(); | ||
|
||
auto it = logs.begin(); | ||
while (it != logs.end()) { | ||
auto logsetting = std::find_if(logSettings.begin(), logSettings.end(), | ||
[it](const LogSetting &setting) { return setting.sender == it->sender; }); | ||
|
||
if (logsetting != logSettings.end() && it->loglevel >= logsetting->minimumLevel && | ||
false == this->m_LogAll) { | ||
#ifdef DEBUG_BUILD | ||
std::cout << logsetting->name << ": " << it->message << "\n"; | ||
#endif | ||
|
||
sqlite3_stmt *stmt; | ||
|
||
sqlite3_prepare_v2(this->m_database, __insertMessage.c_str(), __insertMessage.length(), &stmt, nullptr); | ||
sqlite3_bind_text(stmt, 1, logsetting->name.c_str(), -1, SQLITE_TRANSIENT); | ||
sqlite3_bind_int(stmt, 2, static_cast<int>(it->loglevel)); | ||
sqlite3_bind_text(stmt, 3, it->message.c_str(), -1, SQLITE_TRANSIENT); | ||
|
||
sqlite3_step(stmt); | ||
sqlite3_clear_bindings(stmt); | ||
sqlite3_reset(stmt); | ||
} | ||
it = logs.erase(it); | ||
} | ||
} | ||
} | ||
|
||
void Logger::log(const LogSender &sender, const std::string &message, const LogLevel loglevel) { | ||
std::lock_guard guard(this->m_logLock); | ||
if (true == this->loggingEnabled) m_asynchronousLogs.push_back({sender, message, loglevel}); | ||
} | ||
|
||
std::string Logger::handleLogCommand(std::string command) { | ||
auto elements = vacdm::utils::String::splitString(command, " "); | ||
|
||
std::string usageString = "Usage: .vacdm LOG ON/OFF/DEBUG"; | ||
if (elements.size() != 3) return usageString; | ||
|
||
if ("ON" == elements[2]) { | ||
this->enableLogging(); | ||
return "Enabled logging"; | ||
} else if ("OFF" == elements[2]) { | ||
this->disableLogging(); | ||
return "Disabled logging"; | ||
} else if ("DEBUG" == elements[2]) { | ||
std::lock_guard guard(this->m_logLock); | ||
if (false == this->m_LogAll) { | ||
this->m_LogAll = true; | ||
return "Set all log levels to DEBUG"; | ||
} else { | ||
this->m_LogAll = false; | ||
return "Reset log levels, using previous settings"; | ||
} | ||
} | ||
|
||
return usageString; | ||
} | ||
|
||
std::string Logger::handleLogLevelCommand(std::string command) { | ||
const auto elements = vacdm::utils::String::splitString(command, " "); | ||
if (elements.size() != 4) { | ||
return "Usage: .vacdm LOGLEVEL sender loglevel"; | ||
} | ||
|
||
std::string sender = elements[2]; | ||
std::string newLevel = elements[3]; | ||
|
||
std::lock_guard guard(this->m_logLock); | ||
auto logsetting = std::find_if(logSettings.begin(), logSettings.end(), [sender](const LogSetting &setting) { | ||
std::string uppercaseName = setting.name; | ||
#pragma warning(push) | ||
#pragma warning(disable : 4244) | ||
std::transform(uppercaseName.begin(), uppercaseName.end(), uppercaseName.begin(), ::toupper); | ||
#pragma warning(pop) | ||
return uppercaseName == sender; | ||
}); | ||
|
||
// sender not found | ||
if (logsetting == logSettings.end()) { | ||
return "Sender " + sender + " not found. Available senders are " + | ||
std::accumulate(std::next(logSettings.begin()), logSettings.end(), logSettings.front().name, | ||
[](std::string acc, const LogSetting &setting) { return acc + " " + setting.name; }); | ||
} | ||
|
||
// Modify logsetting by reference | ||
auto &logSettingRef = *logsetting; | ||
|
||
#pragma warning(push) | ||
#pragma warning(disable : 4244) | ||
std::transform(newLevel.begin(), newLevel.end(), newLevel.begin(), ::toupper); | ||
#pragma warning(pop) | ||
|
||
if (newLevel == "DEBUG") { | ||
logSettingRef.minimumLevel = LogLevel::Debug; | ||
} else if (newLevel == "INFO") { | ||
logSettingRef.minimumLevel = LogLevel::Info; | ||
} else if (newLevel == "WARNING") { | ||
logSettingRef.minimumLevel = LogLevel::Warning; | ||
} else if (newLevel == "ERROR") { | ||
logSettingRef.minimumLevel = LogLevel::Error; | ||
} else if (newLevel == "CRITICAL") { | ||
logSettingRef.minimumLevel = LogLevel::Critical; | ||
} else if (newLevel == "SYSTEM") { | ||
logSettingRef.minimumLevel = LogLevel::System; | ||
} else if (newLevel == "DISABLED") { | ||
logSettingRef.minimumLevel = LogLevel::Disabled; | ||
} else { | ||
return "Invalid log level: " + newLevel; | ||
} | ||
|
||
// check if at least one sender is set to log | ||
bool enableLogging = false; | ||
for (auto logSetting : logSettings) { | ||
if (logsetting->minimumLevel != LogLevel::Disabled) { | ||
enableLogging = true; | ||
break; | ||
} | ||
} | ||
this->loggingEnabled = enableLogging; | ||
|
||
return "Changed sender " + sender + " to " + newLevel; | ||
} | ||
|
||
void Logger::enableLogging() { | ||
if (false == this->logFileCreated) createLogFile(); | ||
|
||
std::lock_guard guard(this->m_logLock); | ||
this->loggingEnabled = true; | ||
} | ||
|
||
void Logger::disableLogging() { | ||
std::lock_guard guard(this->m_logLock); | ||
this->loggingEnabled = false; | ||
} | ||
|
||
void Logger::createLogFile() { | ||
sqlite3_open(stream.str().c_str(), &this->m_database); | ||
sqlite3_exec(this->m_database, __loggingTable, nullptr, nullptr, nullptr); | ||
sqlite3_exec(this->m_database, "PRAGMA journal_mode = MEMORY", nullptr, nullptr, nullptr); | ||
logFileCreated = true; | ||
} | ||
|
||
Logger &Logger::instance() { | ||
static Logger __instance; | ||
return __instance; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#pragma once | ||
|
||
#include <list> | ||
#include <mutex> | ||
#include <sstream> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include "sqlite3.h" | ||
|
||
namespace vacdm::logging { | ||
class Logger { | ||
public: | ||
enum LogSender { | ||
vACDM, | ||
DataManager, | ||
Server, | ||
ConfigParser, | ||
Utils, | ||
}; | ||
|
||
enum LogLevel { | ||
Debug, | ||
Info, | ||
Warning, | ||
Error, | ||
Critical, | ||
System, | ||
Disabled, | ||
}; | ||
|
||
struct LogSetting { | ||
LogSender sender; | ||
std::string name; | ||
LogLevel minimumLevel; | ||
}; | ||
|
||
struct AsynchronousLog { | ||
LogSender sender; | ||
std::string message; | ||
LogLevel loglevel; | ||
}; | ||
|
||
private: | ||
Logger(); | ||
#ifdef DEBUG_BUILD | ||
std::vector<LogSetting> logSettings = { | ||
{vACDM, "vACDM", Debug}, {DataManager, "DataManager", Info}, | ||
{Server, "Server", Debug}, {ConfigParser, "ConfigParser", Debug}, | ||
{Utils, "Utils", Debug}, | ||
}; | ||
#else | ||
/// @brief set the log level for each sender separately | ||
std::vector<LogSetting> logSettings = { | ||
{vACDM, "vACDM", Disabled}, {DataManager, "DataManager", Disabled}, | ||
{Server, "Server", Disabled}, {ConfigParser, "ConfigParser", Disabled}, | ||
{Utils, "Utils", Disabled}, | ||
}; | ||
#endif | ||
bool m_LogAll = false; | ||
|
||
std::mutex m_logLock; | ||
std::list<struct AsynchronousLog> m_asynchronousLogs; | ||
std::thread m_logWriter; | ||
bool m_stop = false; | ||
void run(); | ||
|
||
void enableLogging(); | ||
void disableLogging(); | ||
bool loggingEnabled = false; | ||
|
||
sqlite3 *m_database; | ||
std::stringstream stream; | ||
bool logFileCreated = false; | ||
void createLogFile(); | ||
|
||
public: | ||
~Logger(); | ||
/// @brief queues a log message to be processed asynchronously | ||
/// @param sender the sender (e.g. class) | ||
/// @param message the message to be displayed | ||
/// @param loglevel the severity, must be greater than m_minimumLogLevel to be logged | ||
void log(const LogSender &sender, const std::string &message, const LogLevel loglevel); | ||
std::string handleLogCommand(std::string command); | ||
std::string handleLogLevelCommand(std::string command); | ||
static Logger &instance(); | ||
}; | ||
} // namespace vacdm::logging |
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,15 @@ | ||
#include <memory> | ||
#pragma warning(push, 0) | ||
#include <EuroScopePlugIn.h> | ||
#pragma warning(pop) | ||
|
||
#include "vACDM.h" | ||
|
||
std::unique_ptr<EuroScopePlugIn::CPlugIn> Plugin; | ||
|
||
void __declspec (dllexport) EuroScopePlugInInit(EuroScopePlugIn::CPlugIn **ppPlugInInstance) | ||
{ | ||
Plugin.reset(new vacdm::vACDM()); | ||
*ppPlugInInstance = Plugin.get(); | ||
} | ||
|
||
void __declspec (dllexport) EuroScopePlugInExit(void) | ||
{ | ||
} | ||
#include <memory> | ||
#pragma warning(push, 0) | ||
#include <EuroScopePlugIn.h> | ||
#pragma warning(pop) | ||
|
||
#include "vACDM.h" | ||
|
||
std::unique_ptr<EuroScopePlugIn::CPlugIn> Plugin; | ||
|
||
void __declspec(dllexport) EuroScopePlugInInit(EuroScopePlugIn::CPlugIn **ppPlugInInstance) { | ||
Plugin.reset(new vacdm::vACDM()); | ||
*ppPlugInInstance = Plugin.get(); | ||
} | ||
|
||
void __declspec(dllexport) EuroScopePlugInExit(void) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#pragma once | ||
|
||
#include <array> | ||
#include <chrono> | ||
#include <string> | ||
|
||
namespace vacdm::types { | ||
// defines the types returned by the ECFMP API | ||
// see https://ecfmp.vatsim.net/docs/v1 for documentation | ||
|
||
typedef struct EcfmpMeasure_t { | ||
std::string ident; | ||
std::int64_t value = -1; | ||
std::vector<std::string> mandatoryRoute; | ||
} EcfmpMeasure; | ||
|
||
typedef struct EcfmpFilter_t { | ||
std::string type; | ||
std::vector<std::string> value; | ||
std::vector<std::int64_t> flightlevels; | ||
std::vector<std::string> waypoints; | ||
} EcfmpFilter; | ||
|
||
typedef struct EcfmpFlowMeasure_t { | ||
std::int64_t number; | ||
std::string ident; | ||
std::int64_t event_id; | ||
std::string reason; | ||
std::chrono::utc_clock::time_point starttime; | ||
std::chrono::utc_clock::time_point endtime; | ||
std::chrono::utc_clock::time_point withdrawn_at; | ||
std::vector<int> notified_fir_regions; | ||
std::vector<EcfmpMeasure> measures; | ||
std::vector<EcfmpFilter> filters; | ||
} EcfmpFlowMeasure; | ||
} // namespace vacdm::types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,54 @@ | ||
#pragma once | ||
|
||
#include <chrono> | ||
#include <string> | ||
|
||
namespace vacdm { | ||
namespace types { | ||
|
||
static constexpr std::chrono::utc_clock::time_point defaultTime = std::chrono::utc_clock::time_point(std::chrono::milliseconds(-1)); | ||
|
||
typedef struct Measure { | ||
std::string ident; // measure id | ||
int value = -1; // measure value in seconds, i.e. 5 | ||
} Measure; | ||
|
||
typedef struct Flight { | ||
std::chrono::utc_clock::time_point lastUpdate; | ||
std::string callsign; | ||
bool inactive = false; | ||
|
||
// position/* | ||
double latitude = 0.0; | ||
double longitude = 0.0; | ||
bool taxizoneIsTaxiout = false; | ||
|
||
// flightplan/* | ||
std::string origin; | ||
std::string destination; | ||
std::string rule; | ||
|
||
// vacdm/* | ||
std::chrono::utc_clock::time_point eobt = defaultTime; | ||
std::chrono::utc_clock::time_point tobt = defaultTime; | ||
std::chrono::utc_clock::time_point ctot = defaultTime; | ||
std::chrono::utc_clock::time_point ttot = defaultTime; | ||
std::chrono::utc_clock::time_point tsat = defaultTime; | ||
std::chrono::utc_clock::time_point exot = defaultTime; | ||
std::chrono::utc_clock::time_point asat = defaultTime; | ||
std::chrono::utc_clock::time_point aobt = defaultTime; | ||
std::chrono::utc_clock::time_point atot = defaultTime; | ||
std::chrono::utc_clock::time_point asrt = defaultTime; | ||
std::chrono::utc_clock::time_point aort = defaultTime; | ||
std::string tobt_state = ""; | ||
|
||
// booking/* | ||
bool hasBooking = false; | ||
|
||
// clearance/* | ||
std::string runway; | ||
std::string sid; | ||
std::string assignedSquawk; | ||
std::string currentSquawk; | ||
std::string initialClimb; | ||
|
||
bool departed = false; | ||
|
||
// ecfmp measures/* | ||
std::vector<Measure> measures; | ||
} Flight_t; | ||
|
||
} | ||
} | ||
#pragma once | ||
|
||
#include <chrono> | ||
#include <string> | ||
|
||
#include "Ecfmp.h" | ||
|
||
namespace vacdm::types { | ||
static constexpr std::chrono::utc_clock::time_point defaultTime = | ||
std::chrono::utc_clock::time_point(std::chrono::milliseconds(-1)); | ||
|
||
typedef struct Pilot_t { | ||
std::string callsign; | ||
std::chrono::utc_clock::time_point lastUpdate; | ||
|
||
bool inactive = false; | ||
|
||
// position data | ||
|
||
double latitude = 0.0; | ||
double longitude = 0.0; | ||
bool taxizoneIsTaxiout = false; | ||
|
||
// flightplan & clearance data | ||
|
||
std::string origin; | ||
std::string destination; | ||
std::string runway; | ||
std::string sid; | ||
|
||
// ACDM procedure data | ||
|
||
std::chrono::utc_clock::time_point eobt = defaultTime; | ||
std::chrono::utc_clock::time_point tobt = defaultTime; | ||
std::string tobt_state; | ||
std::chrono::utc_clock::time_point ctot = defaultTime; | ||
std::chrono::utc_clock::time_point ttot = defaultTime; | ||
std::chrono::utc_clock::time_point tsat = defaultTime; | ||
std::chrono::utc_clock::time_point exot = defaultTime; | ||
std::chrono::utc_clock::time_point asat = defaultTime; | ||
std::chrono::utc_clock::time_point aobt = defaultTime; | ||
std::chrono::utc_clock::time_point atot = defaultTime; | ||
std::chrono::utc_clock::time_point asrt = defaultTime; | ||
std::chrono::utc_clock::time_point aort = defaultTime; | ||
|
||
// ECFMP Measures | ||
|
||
std::vector<EcfmpMeasure> measures; | ||
|
||
// event booking data | ||
|
||
bool hasBooking = false; | ||
} Pilot; | ||
} // namespace vacdm::types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#pragma once | ||
|
||
#include <chrono> | ||
#include <string> | ||
|
||
#pragma warning(push, 0) | ||
#include "EuroScopePlugIn.h" | ||
#pragma warning(pop) | ||
|
||
#include "log/Logger.h" | ||
|
||
namespace vacdm::utils { | ||
|
||
class Date { | ||
public: | ||
Date() = delete; | ||
Date(const Date &) = delete; | ||
Date(Date &&) = delete; | ||
Date &operator=(const Date &) = delete; | ||
Date &operator=(Date &&) = delete; | ||
|
||
/// @brief Converts std::chrono::utc_clock::time_point to an ISO-formatted string. | ||
/// | ||
/// This function takes a std::chrono::utc_clock::time_point and converts it to an | ||
/// ISO-formatted string. The resulting string follows the format "%Y-%m-%dT%H:%M:%S.Z". | ||
/// The function ensures proper formatting, including the addition of "Z" to indicate UTC. | ||
/// | ||
/// If the provided time_point is non-negative (i.e., not before the epoch), the | ||
/// function formats it into a string and appends "Z" to indicate UTC. If the time_point | ||
/// is negative, the function returns a default timestamp representing "1969-12-31T23:59:59.999Z". | ||
/// | ||
/// @param timepoint std::chrono::utc_clock::time_point to be converted. | ||
/// @return ISO-formatted string representing the converted timestamp. | ||
static std::string timestampToIsoString(const std::chrono::utc_clock::time_point &timepoint) { | ||
if (timepoint.time_since_epoch().count() >= 0) { | ||
std::stringstream stream; | ||
stream << std::format("{0:%FT%T}", timepoint); | ||
auto timestamp = stream.str(); | ||
timestamp = timestamp.substr(0, timestamp.length() - 4) + "Z"; | ||
return timestamp; | ||
} else { | ||
return "1969-12-31T23:59:59.999Z"; | ||
} | ||
} | ||
|
||
/// @brief Converts an ISO-formatted string to std::chrono::utc_clock::time_point. | ||
/// | ||
/// This function takes an ISO-formatted string representing a timestamp and converts | ||
/// it to a std::chrono::utc_clock::time_point. The input string is expected to be in | ||
/// the format "%Y-%m-%dT%H:%M:%S". | ||
/// | ||
/// The function uses a std::stringstream to parse the input string and create a | ||
/// std::chrono::utc_clock::time_point. The resulting time_point is then returned. | ||
/// | ||
/// @param timestamp ISO-formatted string representing the timestamp. | ||
/// @return std::chrono::utc_clock::time_point representing the converted timestamp. | ||
static std::chrono::utc_clock::time_point isoStringToTimestamp(const std::string ×tamp) { | ||
std::chrono::utc_clock::time_point retval; | ||
std::stringstream stream; | ||
|
||
stream << timestamp.substr(0, timestamp.length() - 1); | ||
std::chrono::from_stream(stream, "%FT%T", retval); | ||
|
||
return retval; | ||
} | ||
|
||
/// @brief Converts a EuroScope departure time string to a UTC time_point. | ||
/// This function takes a EuroScope flight plan and extracts the estimated departure time string. | ||
/// Using a different util function it then convert the string to a utc time_point | ||
/// | ||
/// @param flightplan EuroScope flight plan to extract information from. | ||
/// @return std::chrono::utc_clock::time_point representing the converted departure time. | ||
/// | ||
static std::chrono::utc_clock::time_point convertEuroscopeDepartureTime( | ||
const EuroScopePlugIn::CFlightPlan flightplan) { | ||
const std::string callsign = flightplan.GetCallsign(); | ||
const std::string eobt = flightplan.GetFlightPlanData().GetEstimatedDepartureTime(); | ||
|
||
return convertStringToTimePoint(eobt); | ||
} | ||
/// @brief Converts a 4-character HHMM string to a UTC time_point. | ||
/// This function takes a 4-character string representing time in HHMM format. | ||
/// If the string is not valid (empty or exceeds 4 characters), the function returns the | ||
/// current UTC time. Otherwise, it constructs a formatted string combining the current | ||
/// date (year, month, day) with the given time, ensuring proper zero-padding. | ||
/// The formatted string is then parsed to obtain a std::chrono::utc_clock::time_point. | ||
/// | ||
/// @param hhmmString The 4-character string representing time in HHMM format. | ||
/// @return std::chrono::utc_clock::time_point representing the converted time. | ||
/// | ||
static std::chrono::utc_clock::time_point convertStringToTimePoint(const std::string &hhmmString) { | ||
const auto now = std::chrono::utc_clock::now(); | ||
if (hhmmString.length() == 0 || hhmmString.length() > 4) { | ||
return now; | ||
} | ||
|
||
std::stringstream stream; | ||
stream << std::format("{0:%Y%m%d}", now); | ||
std::size_t requiredLeadingZeros = 4 - hhmmString.length(); | ||
while (requiredLeadingZeros != 0) { | ||
requiredLeadingZeros -= 1; | ||
stream << "0"; | ||
} | ||
stream << hhmmString; | ||
|
||
std::chrono::utc_clock::time_point time; | ||
std::stringstream input(stream.str()); | ||
std::chrono::from_stream(stream, "%Y%m%d%H%M", time); | ||
|
||
return time; | ||
} | ||
}; | ||
} // namespace vacdm::utils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
|
||
namespace vacdm { | ||
static __inline bool isNumber(const std::string& s) { | ||
return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); | ||
} | ||
} // namespace vacdm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* @brief Defines and implements functions to handle strings | ||
* @file helper/String.h | ||
* @author Sven Czarnian <devel@svcz.de> | ||
* @copyright Copyright 2020-2021 Sven Czarnian | ||
* @license This project is published under the GNU General Public License v3 | ||
* (GPLv3) | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <string> | ||
#include <vector> | ||
|
||
namespace vacdm::utils { | ||
/** | ||
* @brief Implements and defines convenience functions for the string handling | ||
* @ingroup helper | ||
*/ | ||
class String { | ||
private: | ||
template <typename Separator> | ||
static auto splitAux(const std::string &value, Separator &&separator) -> std::vector<std::string> { | ||
std::vector<std::string> result; | ||
std::string::size_type p = 0; | ||
std::string::size_type q; | ||
while ((q = separator(value, p)) != std::string::npos) { | ||
result.emplace_back(value, p, q - p); | ||
p = q + 1; | ||
} | ||
result.emplace_back(value, p); | ||
return result; | ||
} | ||
|
||
public: | ||
String() = delete; | ||
String(const String &) = delete; | ||
String(String &&) = delete; | ||
String &operator=(const String &) = delete; | ||
String &operator=(String &&) = delete; | ||
|
||
/** | ||
* @brief Replaces all markers by replace in message | ||
* @param[in,out] message The message which needs to be modified | ||
* @param[in] marker The wildcard which needs to be found in message and which | ||
* needs to be replaced | ||
* @param[in] replace The replacement of marker in message | ||
* @return | ||
*/ | ||
static __inline void stringReplace(std::string &message, const std::string &marker, const std::string &replace) { | ||
std::size_t pos = message.find(marker, 0); | ||
while (std::string::npos != pos) { | ||
auto it = message.cbegin() + pos; | ||
message.replace(it, it + marker.length(), replace); | ||
pos = message.find(marker, pos + marker.length()); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Splits value into chunks and the separator is defined in separators | ||
* @param[in] value The string which needs to be splitted up | ||
* @param[in] separators The separators which split up the value | ||
* @return The list of splitted chunks | ||
*/ | ||
static auto splitString(const std::string &value, const std::string &separators) -> std::vector<std::string> { | ||
return String::splitAux(value, [&](const std::string &v, std::string::size_type p) noexcept { | ||
return v.find_first_of(separators, p); | ||
}); | ||
} | ||
|
||
/** | ||
* @brief Removes leading and trailing whitespaces | ||
* @param[in] value The trimmable string | ||
* @param[in] spaces The characters that need to be removed | ||
* @return The trimmed version of value | ||
*/ | ||
static auto trim(const std::string &value, const std::string &spaces = " \t") -> std::string { | ||
const auto begin = value.find_first_not_of(spaces, 0); | ||
if (std::string::npos == begin) return ""; | ||
|
||
const auto end = value.find_last_not_of(spaces); | ||
const auto range = end - begin + 1; | ||
|
||
return value.substr(begin, range); | ||
} | ||
|
||
/** | ||
* @brief finds the ICAO in a EuroScope airport name | ||
* @details There is no consistent naming defined for EuroScope airports | ||
* This means that an airport name may include only the ICAO (e.g. "EDDK") or additionally the name aswell like | ||
* "EDDK Cologne-Bonn" in no paticular order. This functions finds the ICAO in this string based on two conditions. | ||
* 1. The airport ICAO is 4-letters long | ||
* 2. The airport ICAO is full uppercase | ||
* @param[in] input the string to find the ICAO in | ||
* @return the ICAO or "" if none was found | ||
*/ | ||
static auto findIcao(std::string input) -> std::string { | ||
if (input.size() < 4) return ""; | ||
|
||
// split the input into separate words | ||
const auto words = splitString(input, " "); | ||
|
||
for (auto word : words) { | ||
if (word.size() == 4 && std::all_of(word.begin(), word.end(), [](char c) { return std::isupper(c); })) { | ||
return word; | ||
} | ||
} | ||
|
||
return ""; // Return an empty string if no valid ICAO code is found | ||
} | ||
}; | ||
} // namespace vacdm::utils |
Oops, something went wrong.