Simulator database for X-Plane

This commit is contained in:
Kilian Hofmann 2022-01-03 04:56:27 +01:00
parent eca1efdf83
commit 4b893ca726
16 changed files with 1054 additions and 230 deletions

View File

@ -29,7 +29,7 @@ IndentCaseLabels: true
IndentFunctionDeclarationAfterType: true
IndentWidth: 4
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
NamespaceIndentation: All
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
ObjCBlockIndentWidth: 4

47
file/include/config.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_CONFIG_H
#define GERMANAIRLINESVA_GACONNECTOR_CONFIG_H
#include "stringExtensions.h"
#include <map>
#include <fstream>
#include <map>
#include <string>
#include <vector>
namespace config
{
static inline std::map<std::string, std::string>
readConfig(const std::string &file)
{
std::ifstream config(file);
std::map<std::string, std::string> settings;
std::string line;
while (std::getline(config, line)) {
std::vector<std::string> fields = split(line, '=');
if (fields.size() >= 2) {
trim(fields[0]);
trim(fields[1]);
settings[fields[0]] = fields[1];
}
}
config.close();
return settings;
}
static inline void
writeConfig(const std::map<std::string, std::string> &config,
const std::string &file)
{
std::ofstream cfg(file);
for (const std::pair<const std::string, std::string> &entry : config) {
cfg << entry.first << '=' << entry.second << '\n';
}
cfg.close();
}
} // namespace config
#endif

56
file/include/gate.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_GATE_H
#define GERMANAIRLINESVA_GACONNECTOR_GATE_H
#include <cstdint>
#include <cstring>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
/*
* Representation of X-Plane gate
* Heading in degrees (0...360)
*
* Length in bytes: 18 + length of designator
* Designator must be null terminated
*
* UINT8 | CHAR[] | DOUBLE | DOUBLE | UINT8
* ------+------------+--------+--------+------
* LEN | DESIGNATOR | LAT | LON | WIDTH
*/
class Gate
{
private:
std::string designator;
double latitude;
double longitude;
std::vector<std::uint8_t> file;
public:
Gate(const std::string &designator, double latitude, double longitude)
{
this->designator = designator;
this->latitude = latitude;
this->longitude = longitude;
file = std::vector<std::uint8_t>(18 + this->designator.length(), 0);
std::uint8_t *bufPtr = file.data();
memset(bufPtr,
static_cast<std::uint8_t>(this->designator.length()),
sizeof(std::uint8_t));
bufPtr++; // Designator length
memcpy(bufPtr, this->designator.c_str(), this->designator.length());
bufPtr +=
this->designator.length() + 1; // Designator plus null termination
memcpy(bufPtr, &this->latitude, sizeof(this->latitude));
bufPtr += 8; // Latitude
memcpy(bufPtr, &this->longitude, sizeof(this->longitude));
}
std::uint8_t *getBinaryData() { return this->file.data(); }
std::size_t getBinaryLength() { return this->file.size(); }
};
#endif

114
file/include/runway.h Normal file
View File

@ -0,0 +1,114 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_RUNWAY_H
#define GERMANAIRLINESVA_GACONNECTOR_RUNWAY_H
#include <iomanip>
#include <regex>
#include <sstream>
#include <string>
#include <utility>
#include "util.h"
/*
* Representation of one X-Plane runway with supplementary information
* Heading in degrees (0...360)
* Width and length in feet
*
* Length in bytes: 23 + length of designator
* Designator must be null terminated
*
* UINT8 | CHAR[] | DOUBLE | DOUBLE | UINT8 | UINT16 | UINT16
* ------+------------+--------+--------+-------+--------+-------
* LEN | DESIGNATOR | LAT | LON | WIDTH | LENGTH | TRUHDG
*/
class Runway
{
private:
std::string designator;
double latitudeStart;
double longitudeStart;
std::uint8_t width;
std::uint16_t length;
std::uint16_t trueHeading;
std::vector<std::uint8_t> file;
public:
Runway(std::string designator,
double latitudeStart,
double longitudeStart,
double latitudeEnd,
double longitudeEnd,
double width)
{
this->designator = std::move(designator);
this->latitudeStart = latitudeStart;
this->longitudeStart = longitudeStart;
this->width = (std::uint8_t)std::round(util::to_feet(width));
double dist = util::distanceEarth(latitudeStart,
longitudeStart,
latitudeEnd,
longitudeEnd);
this->length = (std::uint16_t)std::round(util::to_feet(dist));
this->trueHeading =
(std::uint16_t)std::round(util::bearing(latitudeStart,
longitudeStart,
latitudeEnd,
longitudeEnd));
file = std::vector<std::uint8_t>(23 + this->designator.length(), 0);
std::uint8_t *bufPtr = file.data();
memset(bufPtr,
static_cast<std::uint8_t>(this->designator.length()),
sizeof(std::uint8_t));
bufPtr++;
memcpy(bufPtr, this->designator.c_str(), this->designator.length());
bufPtr += this->designator.length() + 1;
memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart));
bufPtr += sizeof(this->latitudeStart);
memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart));
bufPtr += sizeof(this->longitudeStart);
memcpy(bufPtr, &this->width, sizeof(this->width));
bufPtr += sizeof(this->width);
memcpy(bufPtr, &this->length, sizeof(this->length));
bufPtr += sizeof(this->length);
memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading));
}
Runway(std::string designator,
double latitudeStart,
double longitudeStart,
std::uint8_t width,
std::uint16_t length,
std::uint16_t trueHeading)
{
this->designator = std::move(designator);
this->latitudeStart = latitudeStart;
this->longitudeStart = longitudeStart;
this->width = width;
this->length = length;
this->trueHeading = trueHeading;
file = std::vector<std::uint8_t>(23 + this->designator.length(), 0);
std::uint8_t *bufPtr = file.data();
memset(bufPtr,
static_cast<std::uint8_t>(this->designator.length()),
sizeof(std::uint8_t));
bufPtr++;
memcpy(bufPtr, this->designator.c_str(), this->designator.length());
bufPtr += this->designator.length() + 1;
memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart));
bufPtr += sizeof(this->latitudeStart);
memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart));
bufPtr += sizeof(this->longitudeStart);
memcpy(bufPtr, &this->width, sizeof(this->width));
bufPtr += sizeof(this->width);
memcpy(bufPtr, &this->length, sizeof(this->length));
bufPtr += sizeof(this->length);
memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading));
}
std::uint8_t *getBinaryData() { return this->file.data(); }
std::size_t getBinaryLength() { return this->file.size(); }
};
#endif

View File

@ -0,0 +1,191 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_SIMULATORDATABASE_H
#define GERMANAIRLINESVA_GACONNECTOR_SIMULATORDATABASE_H
#include "gate.h"
#include "runway.h"
#include "stringExtensions.h"
#define CURRENT_VERSION 1
#include <cstdint>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>
/*
* Header
*
* CHAR[5] | UINT8
* --------+--------
* VGAS | VERSION
*/
/*
* Airport
*
* UINT8 | CHAR[] | UINT16 | GATE[] | UINT8 | RUNWAY[]
* --------+--------+----------+--------+---------+---------
* STRLEN | ICAO | NUMGATES | GATES | NUMRWYS | RUNWAYS
*/
namespace simulatorDatabase
{
static inline void toFile(
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
&airports,
const std::string &file)
{
std::uint8_t null = 0;
std::ofstream out(file, std::fstream::binary);
// File Header
std::uint8_t header[] = {'V', 'G', 'A', 'S', 0, CURRENT_VERSION};
out.write(reinterpret_cast<const char *>(header), 6);
// Num Airports
std::uint16_t numAirports = airports.size();
out.write(reinterpret_cast<const char *>(&numAirports),
sizeof(numAirports));
// Airport
for (const std::pair<const std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
&airport : airports) {
std::string icao = airport.first;
std::vector<Gate> gates = airport.second.first;
std::vector<Runway> runways = airport.second.second;
// ICAO
std::uint8_t icaoLength = icao.length();
out.write(reinterpret_cast<const char *>(&icaoLength),
sizeof(icaoLength));
out.write(icao.c_str(), icaoLength);
out.write(reinterpret_cast<const char *>(&null), sizeof(null));
// Gates
std::uint16_t numGates = gates.size();
out.write(reinterpret_cast<const char *>(&numGates),
sizeof(numGates));
for (Gate &gate : gates) {
out.write(reinterpret_cast<const char *>(gate.getBinaryData()),
(std::streamsize)gate.getBinaryLength());
}
// Runways
std::uint8_t numRunways = runways.size();
out.write(reinterpret_cast<const char *>(&numRunways),
sizeof(numRunways));
for (Runway &runway : runways) {
out.write(
reinterpret_cast<const char *>(runway.getBinaryData()),
(std::streamsize)runway.getBinaryLength());
}
}
out.close();
}
static inline std::map<std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
readVersion1(std::ifstream &in)
{
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
airports;
std::uint16_t numAirports;
in.read(reinterpret_cast<char *>(&numAirports), sizeof(numAirports));
for (int i = 0; i < numAirports; i++) {
// ICAO
std::uint8_t icaoLength;
in.read(reinterpret_cast<char *>(&icaoLength), sizeof(icaoLength));
char *icao =
static_cast<char *>(calloc(icaoLength + 1, sizeof(char)));
in.read(icao, icaoLength + 1);
// Gates
std::uint16_t numGates;
in.read(reinterpret_cast<char *>(&numGates), sizeof(numGates));
for (int j = 0; j < numGates; j++) {
// ICAO
std::uint8_t designatorLength;
in.read(reinterpret_cast<char *>(&designatorLength),
sizeof(designatorLength));
char *designator = static_cast<char *>(
calloc(designatorLength + 1, sizeof(char)));
in.read(designator, designatorLength + 1);
// Latitude
double latitude;
in.read(reinterpret_cast<char *>(&latitude), sizeof(latitude));
// Latitude
double longitude;
in.read(reinterpret_cast<char *>(&longitude),
sizeof(longitude));
airports[icao].first.emplace_back(designator,
latitude,
longitude);
}
// Runways
std::uint8_t numRunways;
in.read(reinterpret_cast<char *>(&numRunways), sizeof(numRunways));
for (int j = 0; j < numRunways; j++) {
// ICAO
std::uint8_t designatorLength;
in.read(reinterpret_cast<char *>(&designatorLength),
sizeof(designatorLength));
char *designator = static_cast<char *>(
calloc(designatorLength + 1, sizeof(char)));
in.read(designator, designatorLength + 1);
// Latitude
double latitude;
in.read(reinterpret_cast<char *>(&latitude), sizeof(latitude));
// Latitude
double longitude;
in.read(reinterpret_cast<char *>(&longitude),
sizeof(longitude));
// Width
std::uint8_t width;
in.read(reinterpret_cast<char *>(&width), sizeof(width));
// Length
std::uint16_t length;
in.read(reinterpret_cast<char *>(&length), sizeof(length));
// True Heading
std::uint16_t trueHeading;
in.read(reinterpret_cast<char *>(&trueHeading),
sizeof(trueHeading));
airports[icao].second.emplace_back(designator,
latitude,
longitude,
width,
length,
trueHeading);
}
}
in.close();
return airports;
}
static inline std::map<std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
fromFile(const std::string &file)
{
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
airports;
std::ifstream in(file);
// File Header
char ident[5];
in.read(ident, 5);
if (strcmp(ident, "VGAS") != 0) {
throw std::invalid_argument("Wrong file");
}
std::uint8_t version;
in.read(reinterpret_cast<char *>(&version), 1);
if (version == 1) {
return readVersion1(in);
}
return airports;
}
} // namespace simulatorDatabase
#endif

View File

@ -0,0 +1,53 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_STRINGEXTENSIONS_H
#define GERMANAIRLINESVA_GACONNECTOR_STRINGEXTENSIONS_H
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
// trim from start (in place)
static inline void ltrim(std::string &s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string &s)
{
s.erase(std::find_if(s.rbegin(),
s.rend(),
[](unsigned char ch) { return !std::isspace(ch); })
.base(),
s.end());
}
static inline std::string rtrim_copy(std::string s)
{
rtrim(s);
return s;
}
// trim from both ends (in place)
static inline void trim(std::string &s)
{
ltrim(s);
rtrim(s);
}
static inline std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> result;
std::stringstream ss(s);
std::string item;
while (getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
#endif

306
file/include/util.h Normal file
View File

@ -0,0 +1,306 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_UTIL_H
#define GERMANAIRLINESVA_GACONNECTOR_UTIL_H
#ifdef IBM
#define WIN32_LEAN_AND_MEAN
#endif
#define BUFSIZE 1024
#define MD5LEN 16
#define EARTH_M 6371000
#ifdef IBM
// clang-format off
#include <wtypes.h>
#include <wincrypt.h>
// clang-format on
#endif
#ifdef APL
#include <CommonCrypto/CommonDigest.h>
#include <sys/mman.h>
#endif
#ifdef LIN
#include <openssl/md5.h>
#include <sys/mman.h>
#endif
#if defined APL || defined LIN
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#endif
#include <cmath>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include <sstream>
namespace util
{
static inline double to_feet(double value) { return value * 3.280839895; }
static inline double to_degrees(double value) { return value * 180 / M_PI; }
static inline double to_radians(double value) { return value * M_PI / 180; }
static inline double normalize(double value)
{
return fmod(value + 360, 360);
}
static inline double bearing(double fromLatitude,
double fromLongitude,
double toLatitude,
double toLongitude)
{
double y = sin(to_radians(toLongitude) - to_radians(fromLongitude)) *
cos(to_radians(toLatitude));
double x = cos(to_radians(fromLatitude)) * sin(to_radians(toLatitude)) -
sin(to_radians(fromLatitude)) * cos(to_radians(toLatitude)) *
cos(to_radians(toLongitude) - to_radians(fromLongitude));
return normalize(to_degrees(atan2(y, x)));
}
static inline double distanceEarth(double fromLatitude,
double fromLongitude,
double toLatitude,
double toLongitude)
{
double lat1r, lon1r, lat2r, lon2r, u, v;
lat1r = to_radians(fromLatitude);
lon1r = to_radians(fromLongitude);
lat2r = to_radians(toLatitude);
lon2r = to_radians(toLongitude);
u = sin((lat2r - lat1r) / 2);
v = sin((lon2r - lon1r) / 2);
return 2.0 * EARTH_M *
asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v));
}
template <typename T>
static inline std::vector<T>
select_T(const std::vector<T> &inVec,
std::function<bool(const T &)> predicate)
{
std::vector<T> result;
copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
return result;
}
#if defined APL || defined LIN
static unsigned long get_size_by_fd(int fd)
{
struct stat buf {
};
if (fstat(fd, &buf) < 0)
return 0;
return buf.st_size;
}
#endif
static void to_hex(const char *hash, char *buffer)
{
for (int i = 0; i < MD5LEN; i++) {
if (buffer != nullptr) {
sprintf(&buffer[2 * i], "%02x", hash[i] & 0xff);
}
}
}
#ifdef IBM
static inline int
generateMD5(const char *filepath,
char *lastHash,
const std::function<void(const std::string)> toLog)
{
BOOL bResult = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HANDLE hFile;
BYTE rgbFile[BUFSIZE] = {0};
DWORD cbRead = 0;
BYTE rgbHash[MD5LEN] = {0};
DWORD cbHash = 0;
// Logic to check usage goes here.
hFile = CreateFile(filepath,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
nullptr);
// Get handle to the crypto provider
if (!CryptAcquireContext(&hProv,
nullptr,
nullptr,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
std::stringstream debug_msg;
debug_msg << "CryptAcquireContext returned with error "
<< GetLastError();
toLog(debug_msg.str());
CloseHandle(hFile);
return 1;
}
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
std::stringstream debug_msg;
debug_msg << "CryptCreateHash returned with error "
<< GetLastError();
toLog(debug_msg.str());
CloseHandle(hFile);
CryptReleaseContext(hProv, 0);
return 1;
}
while (
(bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, nullptr))) {
if (0 == cbRead) {
break;
}
if (!CryptHashData(hHash, rgbFile, cbRead, 0)) {
std::stringstream debug_msg;
debug_msg << "CryptHashData returned with error "
<< GetLastError();
toLog(debug_msg.str());
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
return 1;
}
}
if (!bResult) {
std::stringstream debug_msg;
debug_msg << "ReadFile returned with error " << GetLastError();
toLog(debug_msg.str());
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
return 1;
}
cbHash = MD5LEN;
if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) {
to_hex((char *)rgbHash, lastHash);
} else {
std::stringstream debug_msg;
debug_msg << "CryptGetHashParam returned with error "
<< GetLastError();
toLog(debug_msg.str());
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
return 0;
}
#endif
#ifdef APL
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
static inline int
generateMD5(const char *filepath,
char *lastHash,
const std::function<void(const std::string)> &toLog)
{
int file_descript;
unsigned long file_size;
char *file_buffer;
unsigned char result[MD5LEN];
file_descript = open(filepath, O_RDONLY);
if (file_descript < 0)
return 1;
file_size = get_size_by_fd(file_descript);
file_buffer =
(char *)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0);
CC_MD5_CTX context;
CC_MD5_Init(&context);
CC_MD5_Update(&context, file_buffer, (CC_LONG)file_size);
CC_MD5_Final(result, &context);
munmap(file_buffer, file_size);
close(file_descript);
to_hex((char *)result, lastHash);
return 0;
}
#pragma clang diagnostic pop
#endif
#ifdef LIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
static inline int
generateMD5(const char *filepath,
char *buffer,
const std::function<void(const std::string)> &toLog)
{
int file_descriptor;
unsigned long file_size;
char *file_buffer;
unsigned char result[MD5LEN];
file_descriptor = open(filepath, O_RDONLY);
if (file_descriptor < 0)
return 1;
file_size = get_size_by_fd(file_descriptor);
if (file_size == 0)
return 1;
file_buffer = (char *)
mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descriptor, 0);
MD5((unsigned char *)file_buffer, file_size, result);
munmap(file_buffer, file_size);
close(file_descriptor);
to_hex((char *)result, buffer);
return 0;
}
#pragma clang diagnostic pop
#endif
static inline void setThreadName(const std::string &name)
{
#ifdef APL
//
// Apple reserves 16 bytes for its thread names
// Notice that the Apple version of pthread_setname_np
// does not take a pthread_t argument
//
pthread_setname_np(name.substr(0, 63).c_str());
#endif
#ifdef LIN
//
// Linux only reserves 16 bytes for its thread names
// See prctl and PR_SET_NAME property in
// http://man7.org/linux/man-pages/man2/prctl.2.html
//
pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
#endif
#ifdef IBM
SetThreadName(-1, name.c_str());
#endif
}
} // namespace util
#endif

View File

@ -1,5 +1,5 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_TYPES_H
#define GERMANAIRLINESVA_GACONNECTOR_TYPES_H
#ifndef GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_TYPES_H
#define GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_TYPES_H
#include <cstdint>

View File

@ -10,21 +10,7 @@
#include <json.hpp>
#include "types.h"
#ifdef IBM
// clang-format off
#include <wtypes.h>
#include <wincrypt.h>
// clang-format on
#endif
#ifdef APL
#include <CommonCrypto/CommonDigest.h>
#include <sys/mman.h>
#endif
#ifdef LIN
#include <openssl/md5.h>
#include <sys/mman.h>
#endif
#include "util.h"
#include <cstring>
#include <functional>
@ -51,12 +37,6 @@ public:
ix::WebSocket &ws,
const ix::WebSocketMessagePtr &msg);
void sendData(data d);
int generateMD5(const char *filepath);
};
#if defined APL || defined LIN
unsigned long get_size_by_fd(int fd);
#endif
void to_hex(const char *hash, char *buffer);
#endif

View File

@ -97,197 +97,28 @@ void websocket::onClientMessageCallback(
void websocket::sendData(data d)
{
char *hash = (char *)calloc(2 * MD5LEN + 1, sizeof(char));
if (hash != nullptr) {
if (strcmp(d.path, lastPath) != 0) {
strcpy(lastPath, d.path);
if (generateMD5(d.path)) {
strcpy(lastHash, "NOT SET");
}
}
nlohmann::json json = {
{"altitude", d.alt},
{"vs", d.vs},
{"ias", d.ias},
{"magHdg", d.magHeading},
{"truHdg", d.truHdg},
{"totFuel", d.totFuelKg},
{"fuelFlow", d.ff},
{"hash", lastHash},
};
{
std::lock_guard<std::mutex> lock(wsLock);
if (webSocket != nullptr) {
webSocket->send(json.dump(), false);
}
free(hash);
}
}
}
#ifdef IBM
int websocket::generateMD5(const char *filepath)
{
BOOL bResult = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HANDLE hFile = nullptr;
BYTE rgbFile[BUFSIZE] = {0};
DWORD cbRead = 0;
BYTE rgbHash[MD5LEN] = {0};
DWORD cbHash = 0;
// Logic to check usage goes here.
hFile = CreateFile(filepath,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
nullptr);
// Get handle to the crypto provider
if (!CryptAcquireContext(&hProv,
nullptr,
nullptr,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
std::stringstream debug_msg;
debug_msg << "CryptAcquireContext returned with error "
<< GetLastError();
toLog(debug_msg.str());
CloseHandle(hFile);
return 1;
}
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
std::stringstream debug_msg;
debug_msg << "CryptCreateHash returned with error " << GetLastError();
toLog(debug_msg.str());
CloseHandle(hFile);
CryptReleaseContext(hProv, 0);
return 1;
}
while ((bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, nullptr))) {
if (0 == cbRead) {
break;
}
if (!CryptHashData(hHash, rgbFile, cbRead, 0)) {
std::stringstream debug_msg;
debug_msg << "CryptHashData returned with error " << GetLastError();
toLog(debug_msg.str());
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
return 1;
if (strcmp(d.path, lastPath) != 0) {
strcpy(lastPath, d.path);
if (util::generateMD5(d.path, lastHash, toLog)) {
strcpy(lastHash, "NOT SET");
}
}
if (!bResult) {
std::stringstream debug_msg;
debug_msg << "ReadFile returned with error " << GetLastError();
toLog(debug_msg.str());
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
return 1;
}
cbHash = MD5LEN;
if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) {
to_hex((char *)rgbHash, lastHash);
} else {
std::stringstream debug_msg;
debug_msg << "CryptGetHashParam returned with error " << GetLastError();
toLog(debug_msg.str());
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
return 0;
}
#endif
#ifdef APL
int websocket::generateMD5(const char *filepath)
{
int file_descript;
unsigned long file_size;
char *file_buffer;
unsigned char result[MD5LEN];
file_descript = open(filepath, O_RDONLY);
if (file_descript < 0)
return 1;
file_size = get_size_by_fd(file_descript);
file_buffer =
(char *)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0);
CC_MD5_CTX context;
CC_MD5_Init(&context);
CC_MD5_Update(&context, file_buffer, (CC_LONG)file_size);
CC_MD5_Final(result, &context);
munmap(file_buffer, file_size);
close(file_descript);
to_hex((char *)result, lastHash);
return 0;
}
#endif
#ifdef LIN
int websocket::generateMD5(const char *filepath)
{
int file_descriptor;
unsigned long file_size;
char *file_buffer;
unsigned char result[MD5LEN];
file_descriptor = open(filepath, O_RDONLY);
if (file_descriptor < 0)
return 1;
file_size = get_size_by_fd(file_descriptor);
file_buffer = (char *)
mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descriptor, 0);
MD5((unsigned char *)file_buffer, file_size, result);
munmap(file_buffer, file_size);
close(file_descriptor);
to_hex((char *)result, lastHash);
return 0;
}
#endif
#if defined APL || defined LIN
unsigned long get_size_by_fd(int fd)
{
struct stat buf {
nlohmann::json json = {
{"altitude", d.alt},
{"vs", d.vs},
{"ias", d.ias},
{"magHdg", d.magHeading},
{"truHdg", d.truHdg},
{"totFuel", d.totFuelKg},
{"fuelFlow", d.ff},
{"hash", lastHash},
};
if (fstat(fd, &buf) < 0)
exit(-1);
return buf.st_size;
}
#endif
void to_hex(const char *hash, char *buffer)
{
for (int i = 0; i < MD5LEN; i++) {
if (buffer != nullptr) {
sprintf(&buffer[2 * i], "%02x", hash[i] & 0xff);
{
std::lock_guard<std::mutex> lock(wsLock);
if (webSocket != nullptr) {
webSocket->send(json.dump(), false);
}
}
}
}

View File

@ -7,10 +7,13 @@
file(GLOB ixwebsocket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/ixwebsocket/*.cpp)
file(GLOB websocket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/websocket/*.cpp)
file(GLOB file CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/file/*.cpp)
add_library(germanairlinesva_xplugin SHARED
${ixwebsocket}
${websocket}
${file}
makeRwysXP.cpp
main.cpp
)
@ -19,6 +22,7 @@ target_include_directories(germanairlinesva_xplugin PRIVATE
${CMAKE_SOURCE_DIR}/websocket/include
${CMAKE_SOURCE_DIR}/XPSDK/CHeaders
${CMAKE_SOURCE_DIR}/nlohmann
${CMAKE_SOURCE_DIR}/file/include
)
set_target_properties(germanairlinesva_xplugin PROPERTIES
@ -28,9 +32,11 @@ set_target_properties(germanairlinesva_xplugin PROPERTIES
target_compile_definitions(germanairlinesva_xplugin PRIVATE
XPLM200
XPLM210
_USE_MATH_DEFINES
)
target_compile_options(germanairlinesva_xplugin PRIVATE
-Wall
-Wextra
-pedantic
-fvisibility=hidden
)

View File

@ -1,13 +1,5 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_MAIN_H
#define GERMANAIRLINESVA_GACONNECTOR_MAIN_H
#ifdef IBM
#define WIN32_LEAN_AND_MEAN
#define _USE_MATH_DEFINES
#endif
#define BUFSIZE 1024
#define MD5LEN 16
#ifndef GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAIN_H
#define GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAIN_H
#include "XPLM/XPLMDataAccess.h"
#include "XPLM/XPLMGraphics.h"
@ -16,6 +8,9 @@
#include "XPLM/XPLMProcessing.h"
#include "XPLM/XPLMUtilities.h"
#include "config.h"
#include "makeRwysXP.h"
#include "simulatorDatabase.h"
#include "websocket.h"
#include <atomic>

View File

@ -0,0 +1,30 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAKERWYSXP_H
#define GERMANAIRLINESVA_GACONNECTOR_XPLUGIN_MAKERWYSXP_H
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include "gate.h"
#include "runway.h"
#include "stringExtensions.h"
#include "util.h"
int scan(const char *defaultFile,
const char *sceneryPack,
const char *logFile,
std::map<std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>> &airports);
void makeAirport(
const std::string &kind,
std::ifstream *infile,
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
*airports,
std::ofstream *logfile);
void makeGate15(std::vector<Gate> *gates, std::vector<std::string> fields);
void makeRunway(std::vector<Runway> *runways, std::vector<std::string> fields);
void makeGate1300(std::vector<Gate> *gates, std::vector<std::string> fields);
#endif

View File

@ -9,6 +9,8 @@ std::queue<std::function<void()>> &messageQueue()
std::thread serverThread;
std::atomic<bool> wantsExit;
std::map<std::string, std::string> configuration;
websocket *connector;
/* Datarefs */
@ -54,9 +56,9 @@ PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc)
/* First we must fill in the passed-in buffers to describe our
* plugin to the plugin-system. */
strcpy(outName, "WebSocketTestXPlane");
strcpy(outSig, "de.german-airlines.WebSocketTestXPlane");
strcpy(outDesc, "WebSocketTestXPlane");
strcpy(outName, "GAConnector");
strcpy(outSig, "de.german-airlines.GAConnector");
strcpy(outDesc, "GAConnector");
/* Flight Loop */
XPLMRegisterFlightLoopCallback(flightLoop, -1, nullptr);
@ -111,11 +113,41 @@ PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc)
toLog(e.what());
return 0;
}
toLog("WebSocket started");
configuration =
config::readConfig("Resources/plugins/GAConnector/config.cfg");
toLog("Config loaded");
char hash[2 * MD5LEN + 1] = "";
if (util::generateMD5("Custom Scenery/scenery_packs.ini", hash, toLog) ==
0) {
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
airports;
if (strcmp(configuration["scenery"].c_str(), hash) != 0) {
scan("Resources/default scenery/default apt dat/Earth nav "
"data/apt.dat",
"Custom Scenery/scenery_packs.ini",
"Resources/plugins/GAConnector/log.txt",
airports);
simulatorDatabase::toFile(airports,
"Resources/plugins/GAConnector/sim.bin");
configuration["scenery"] = hash;
config::writeConfig(configuration,
"Resources/plugins/GAConnector/config.cfg");
toLog("Sim Database updated");
} else {
airports = simulatorDatabase::fromFile(
"Resources/plugins/GAConnector/sim.bin");
toLog("Sim Database loaded");
}
}
// Thread for sending data to web socket
serverThread = std::thread(&serverWorker);
toLog("WebSocketTestXPlane initialized and ready");
toLog("Worker started");
return 1;
}
@ -134,11 +166,16 @@ PLUGIN_API void XPluginDisable(void) {}
PLUGIN_API int XPluginEnable(void) { return 1; }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
PLUGIN_API void
XPluginReceiveMessage(XPLMPluginID inFromWho, long inMessage, void *inParam)
{
}
#pragma clang diagnostic pop
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
float flightLoop(float elapsedMe, float elapsedSim, int counter, void *refcon)
{
const std::lock_guard<std::mutex> lock(mutex);
@ -177,9 +214,12 @@ float flightLoop(float elapsedMe, float elapsedSim, int counter, void *refcon)
return -1;
}
#pragma clang diagnostic pop
void serverWorker()
{
util::setThreadName("GAWorker");
while (!wantsExit) {
data copy;
{

177
xplugin/makeRwysXP.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "include/makeRwysXP.h"
int scan(const char *defaultFile,
const char *sceneryPack,
const char *logFile,
std::map<std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>> &airports)
{
std::ifstream base(defaultFile);
if (!base.good()) {
return 1;
}
std::ifstream custom(sceneryPack);
if (!custom.good()) {
base.close();
return 2;
}
std::ofstream logfile(logFile, std::ios::out | std::ios::trunc);
if (!logfile.good()) {
base.close();
custom.close();
return 3;
}
// Default
logfile << "<FILE> " << defaultFile << std::endl;
makeAirport("DEFAULT", &base, &airports, &logfile);
base.close();
std::string line;
size_t pos;
std::vector<std::string> packs;
while (std::getline(custom, line)) {
if ((pos = line.find("SCENERY_PACK")) != std::string::npos) {
std::string path =
rtrim_copy(line.substr(pos + 13)) + "Earth nav data/apt.dat";
packs.push_back(path);
}
}
std::reverse(packs.begin(), packs.end());
for (std::string const &path : packs) {
std::ifstream pack(path);
if (pack.good()) {
logfile << "<FILE> " << path << std::endl;
makeAirport("CUSTOM", &pack, &airports, &logfile);
pack.close();
} else {
pack.close();
logfile << "<STATUS>"
<< "Could not find " << path << std::endl;
}
}
custom.close();
logfile.close();
return 0;
}
void makeAirport(
const std::string &kind,
std::ifstream *infile,
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
*airports,
std::ofstream *logfile)
{
std::string line;
std::string *currentIcao = nullptr;
std::vector<Gate> tmpGates;
std::vector<Runway> tmpRunways;
int apCount = 0;
int validCount = 0;
while (std::getline(*infile, line)) {
std::vector<std::string> fields = split(line, ' ');
fields = util::select_T<std::string>(fields, [](const std::string &s) {
return s.length() > 0;
});
if (fields.empty())
continue;
if (fields[0] == "1") {
// Write to file if ICAO is valid, and we have gates and runways
if (currentIcao != nullptr && !tmpRunways.empty() &&
!tmpGates.empty()) {
(*airports)[*currentIcao] = {tmpGates, tmpRunways};
validCount += 1;
*logfile << "\t<STATUS> " << *currentIcao << " committed"
<< std::endl;
} else if (currentIcao != nullptr) {
*logfile << "\t<STATUS> " << *currentIcao
<< " had no gates or runways" << std::endl;
}
tmpGates = std::vector<Gate>();
tmpRunways = std::vector<Runway>();
currentIcao = new std::string(fields[4]);
apCount += 1;
*logfile << "\t<" << kind << "> " << line << std::endl;
} else if (currentIcao != nullptr && fields[0] == "15") {
makeGate15(&tmpGates, fields);
*logfile << "\t\t<GATE OLD> " << line << std::endl;
} else if (fields[0] == "16" || fields[0] == "17") {
// Write to file if ICAO is valid, and we have gates and runways
if (currentIcao != nullptr && !tmpRunways.empty() &&
!tmpGates.empty()) {
(*airports)[*currentIcao] = {tmpGates, tmpRunways};
validCount += 1;
*logfile << "\t<STATUS> " << *currentIcao << " committed"
<< std::endl;
} else if (currentIcao != nullptr) {
*logfile << "\t<STATUS> " << *currentIcao
<< " had no gates or runways" << std::endl;
}
tmpGates = std::vector<Gate>();
tmpRunways = std::vector<Runway>();
currentIcao = nullptr;
*logfile << "\t<" << kind << " SKIPPED> " << line << std::endl;
} else if (currentIcao != nullptr && fields[0] == "100") {
makeRunway(&tmpRunways, fields);
*logfile << "\t\t<RUNWAY> " << line << std::endl;
} else if (currentIcao != nullptr && fields[0] == "1300") {
makeGate1300(&tmpGates, fields);
*logfile << "\t\t<GATE> " << line << std::endl;
}
}
if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) {
(*airports)[*currentIcao] = {tmpGates, tmpRunways};
validCount += 1;
*logfile << "\t<STATUS> " << *currentIcao << " committed" << std::endl;
}
*logfile << "<STATUS> " << apCount << " airports found, of which "
<< validCount << " are valid" << std::endl;
}
void makeGate15(std::vector<Gate> *gates, std::vector<std::string> fields)
{
std::string gateName;
for (size_t j = 4; j < fields.size() - 1; j++) {
gateName += fields[j] + " ";
}
gateName += fields.back();
gateName = std::regex_replace(gateName, std::regex{","}, "0");
gates->push_back(
Gate{gateName, std::stod(fields[1]), std::stod(fields[2])});
}
void makeRunway(std::vector<Runway> *runways, std::vector<std::string> fields)
{
runways->push_back(Runway{fields[8],
std::stod(fields[9]),
std::stod(fields[10]),
std::stod(fields[18]),
std::stod(fields[19]),
std::stod(fields[1])});
runways->push_back(Runway{fields[17],
std::stod(fields[18]),
std::stod(fields[19]),
std::stod(fields[9]),
std::stod(fields[10]),
std::stod(fields[1])});
}
void makeGate1300(std::vector<Gate> *gates, std::vector<std::string> fields)
{
std::string gateName;
for (size_t j = 6; j < fields.size() - 1; j++) {
gateName += fields[j] + " ";
}
gateName += fields.back();
gateName = std::regex_replace(gateName, std::regex{","}, "0");
gates->push_back(
Gate{gateName, std::stod(fields[1]), std::stod(fields[2])});
}

View File

@ -1,7 +1,6 @@
#include <stdio.h>
#include <cstdio>
#include <windows.h>
#pragma warning(suppress : 26440)
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
@ -11,7 +10,6 @@ BOOL APIENTRY DllMain(HANDLE hModule,
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
default:
break;
}