Use nested namespaces (C++17 feature)

This commit is contained in:
Mat Sutcliffe
2021-09-15 21:44:54 +01:00
parent 3f2e5b0b69
commit 57d32da826
1345 changed files with 146075 additions and 150376 deletions

View File

@@ -25,455 +25,449 @@ using namespace BlackMisc::Aviation;
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Weather;
namespace BlackMisc
namespace BlackMisc::Simulation::Fsx
{
namespace Simulation
CSimConnectUtilities::CSimConnectUtilities() { }
const QString &CSimConnectUtilities::simConnectFilename()
{
namespace Fsx
static const QString fn("SimConnect.cfg");
return fn;
}
const QString &CSimConnectUtilities::getSwiftLocalSimConnectCfgFilename()
{
static const QString n = CFileUtils::appendFilePaths(QCoreApplication::applicationDirPath(), simConnectFilename());
return n;
}
const QString &CSimConnectUtilities::getUserSimConnectCfgFilename()
{
static const QString n = CFileUtils::appendFilePaths(QStandardPaths::locate(QStandardPaths::DocumentsLocation, "", QStandardPaths::LocateDirectory), simConnectFilename());
return n;
}
bool CSimConnectUtilities::hasSwiftLocalSimConnectCfgFile()
{
const QFile f(getSwiftLocalSimConnectCfgFilename());
return f.exists();
}
bool CSimConnectUtilities::hasUserSimConnectCfgFile()
{
const QFile f(getUserSimConnectCfgFilename());
return f.exists();
}
QSharedPointer<QSettings> CSimConnectUtilities::simConnectFileAsSettings(const QString &fileName)
{
QSharedPointer<QSettings> sp;
const QFile file(fileName);
if (!file.exists()) { return sp; }
sp.reset(new QSettings(fileName, QSettings::IniFormat));
return sp;
}
QString CSimConnectUtilities::ipAddress(const QSettings *simConnectSettings)
{
if (!simConnectSettings) { return {}; }
return simConnectSettings->value("SimConnect/Address").toString();
}
int CSimConnectUtilities::ipPort(const QSettings *simConnectSettings)
{
if (!simConnectSettings) { return -1; }
return simConnectSettings->value("SimConnect/Port", QVariant::fromValue(-1)).toInt();
}
bool CSimConnectUtilities::writeSimConnectCfg(const QString &fileName, const QString &ip, int port)
{
const QString sc = CSimConnectUtilities::simConnectCfg(ip, port);
QFile file(fileName);
bool success = false;
if ((success = file.open(QIODevice::WriteOnly | QIODevice::Text)))
{
CSimConnectUtilities::CSimConnectUtilities() { }
QTextStream out(&file);
out << sc;
file.close();
}
return success;
}
const QString &CSimConnectUtilities::simConnectFilename()
QString CSimConnectUtilities::simConnectCfg(const QString &ip, int port)
{
const QString sc = QStringLiteral("[SimConnect]\nProtocol=Ipv4\nAddress=%1\nPort=%2\n"
"MaxReceiveSize=4096\nDisableNagle=0").arg(ip).arg(port);
return sc;
}
QString CSimConnectUtilities::resolveEnumToString(const DWORD id, const char *enumName)
{
const int i = CSimConnectUtilities::staticMetaObject.indexOfEnumerator(enumName);
if (i < 0) { return QStringLiteral("No enumerator for %1").arg(enumName); }
const QMetaEnum m = CSimConnectUtilities::staticMetaObject.enumerator(i);
const char *k = m.valueToKey(static_cast<int>(id));
return (k) ? QLatin1String(k) : QStringLiteral("Id '%1' not found for %2").arg(id).arg(enumName);
}
const QString &CSimConnectUtilities::simConnectIniFilename()
{
static const QString n("SimConnect.ini");
return n;
}
QString CSimConnectUtilities::simConnectExceptionToString(const DWORD id)
{
return CSimConnectUtilities::resolveEnumToString(id, "SIMCONNECT_EXCEPTION");
}
QString CSimConnectUtilities::simConnectSurfaceTypeToString(const DWORD type, bool beautify)
{
QString sf = CSimConnectUtilities::resolveEnumToString(type, "SIMCONNECT_SURFACE");
return beautify ? sf.replace('_', ' ') : sf;
}
QStringList CSimConnectUtilities::getSimConnectIniFileDirectories()
{
const QString docDir = QStandardPaths::locate(QStandardPaths::DocumentsLocation, "", QStandardPaths::LocateDirectory);
if (docDir.isEmpty()) return QStringList();
QDir directory(docDir);
directory.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
const QStringList dirList = directory.entryList();
QStringList simDirs;
for (const QString &dir : dirList)
{
if (dir.contains("Flight Simulator", Qt::CaseInsensitive) || dir.contains("Prepar3D", Qt::CaseInsensitive))
{
static const QString fn("SimConnect.cfg");
return fn;
simDirs.push_back(CFileUtils::appendFilePaths(docDir, dir));
}
}
const QString &CSimConnectUtilities::getSwiftLocalSimConnectCfgFilename()
// gets the latest P3D as first
simDirs.sort();
std::reverse(std::begin(simDirs), std::end(simDirs));
return simDirs;
}
QStringList CSimConnectUtilities::getSimConnectIniFiles()
{
QStringList files;
for (const QString &dir : getSimConnectIniFileDirectories())
{
const QFileInfo f(CFileUtils::appendFilePaths(dir, simConnectIniFilename()));
if (f.exists()) { files.push_back(f.absoluteFilePath()); }
}
return files;
}
QString CSimConnectUtilities::getSimConnectIniFileDirectory(CSimulatorInfo &simulator)
{
static const QString docDir = QStandardPaths::locate(QStandardPaths::DocumentsLocation, "", QStandardPaths::LocateDirectory);
if (docDir.isEmpty()) { return {}; }
if (!simulator.isSingleSimulator() || !simulator.isFsxP3DFamily()) return {};
const QString iniDir = CFileUtils::appendFilePaths(docDir, simulator.isP3D() ? "Prepar3D v4 Files" : "Flight Simulator X Files");
if (getSimConnectIniFileDirectories().isEmpty()) { return iniDir; }
for (const QString &dir : getSimConnectIniFileDirectories())
{
if (simulator.isP3D())
{
static const QString n = CFileUtils::appendFilePaths(QCoreApplication::applicationDirPath(), simConnectFilename());
return n;
if (dir.contains("Prepar3D", Qt::CaseInsensitive)) { return dir; }
}
const QString &CSimConnectUtilities::getUserSimConnectCfgFilename()
else if (simulator.isFSX())
{
static const QString n = CFileUtils::appendFilePaths(QStandardPaths::locate(QStandardPaths::DocumentsLocation, "", QStandardPaths::LocateDirectory), simConnectFilename());
return n;
if (dir.contains("Flight Simulator", Qt::CaseInsensitive)) { return dir; }
}
}
bool CSimConnectUtilities::hasSwiftLocalSimConnectCfgFile()
return iniDir;
}
QString CSimConnectUtilities::simConnectReceiveIdToString(DWORD type)
{
const QString ri = CSimConnectUtilities::resolveEnumToString(type, "SIMCONNECT_RECV_ID");
return ri;
}
int CSimConnectUtilities::lightsToLightStates(const CAircraftLights &lights)
{
int lightMask = 0;
if (lights.isBeaconOn()) { lightMask |= Beacon; }
if (lights.isLandingOn()) { lightMask |= Landing; }
if (lights.isLogoOn()) { lightMask |= Logo; }
if (lights.isNavOn()) { lightMask |= Nav; }
if (lights.isStrobeOn()) { lightMask |= Strobe; }
if (lights.isTaxiOn()) { lightMask |= Taxi; }
return lightMask;
}
QString CSimConnectUtilities::convertToSimConnectMetar(const CGridPoint &gridPoint, bool isFSX, bool useWindLayers, bool useVisibilityLayers, bool useCloudLayers, bool useTempLayers)
{
// STATION ID
Q_ASSERT(!gridPoint.getIdentifier().isEmpty());
QString simconnectMetar = gridPoint.getIdentifier();
// SURFACE WINDS/WINDS ALOFT
const CWindLayerList windLayers = useWindLayers ? gridPoint.getWindLayers().sortedBy(&CWindLayer::getLevel) : CWindLayerList();
simconnectMetar += windsToSimConnectMetar(windLayers, isFSX);
// VISIBILITY
const CVisibilityLayerList visibilityLayers = useVisibilityLayers ? gridPoint.getVisibilityLayers().sortedBy(&CVisibilityLayer::getBase) : CVisibilityLayerList();
simconnectMetar += visibilitiesToSimConnectMetar(visibilityLayers);
// PRESENT CONDITIONS
// todo
// PARTIAL OBSCURATION
// todo
// SKY CONDITIONS
const CCloudLayerList cloudLayers = useCloudLayers ? gridPoint.getCloudLayers().sortedBy(&CCloudLayer::getBase) : CCloudLayerList();
simconnectMetar += cloudsToSimConnectMetar(cloudLayers);
// TEMPERATURE
const CTemperatureLayerList temperatureLayers = useTempLayers ? gridPoint.getTemperatureLayers().sortedBy(&CTemperatureLayer::getLevel) : CTemperatureLayerList();
simconnectMetar += temperaturesToSimConnectMetar(temperatureLayers);
// ALTIMETER
// Format:
// QNNNN
// Q = specifier for altimeter in millibars
// NNNN = altimeter in millibars
if (!gridPoint.getPressureAtMsl().isNull())
{
static const QString arg1s(" Q%1");
const int altimeter = gridPoint.getPressureAtMsl().valueInteger(CPressureUnit::mbar());
simconnectMetar += arg1s.arg(altimeter, 4, 10, QLatin1Char('0'));
}
return simconnectMetar.simplified();
}
void CSimConnectUtilities::registerMetadata()
{
qRegisterMetaType<CSimConnectUtilities::SIMCONNECT_EXCEPTION>();
qRegisterMetaType<CSimConnectUtilities::SIMCONNECT_SURFACE>();
}
QString CSimConnectUtilities::windsToSimConnectMetar(const CWindLayerList &windLayers, bool isFSX)
{
static const QString arg1s("%1");
QString simConnectWinds;
bool surface = true;
for (const CWindLayer &windLayer : windLayers)
{
simConnectWinds += QLatin1Char(' ');
// Format:
// DDDSSSUUU (steady)
// DDDSSSGXXUUU (gusts)
if (windLayer.isDirectionVariable())
{
const QFile f(getSwiftLocalSimConnectCfgFilename());
return f.exists();
// DDD = VRB for variable
simConnectWinds += QLatin1String("VRB");
}
bool CSimConnectUtilities::hasUserSimConnectCfgFile()
else
{
const QFile f(getUserSimConnectCfgFilename());
return f.exists();
}
QSharedPointer<QSettings> CSimConnectUtilities::simConnectFileAsSettings(const QString &fileName)
{
QSharedPointer<QSettings> sp;
const QFile file(fileName);
if (!file.exists()) { return sp; }
sp.reset(new QSettings(fileName, QSettings::IniFormat));
return sp;
}
QString CSimConnectUtilities::ipAddress(const QSettings *simConnectSettings)
{
if (!simConnectSettings) { return {}; }
return simConnectSettings->value("SimConnect/Address").toString();
}
int CSimConnectUtilities::ipPort(const QSettings *simConnectSettings)
{
if (!simConnectSettings) { return -1; }
return simConnectSettings->value("SimConnect/Port", QVariant::fromValue(-1)).toInt();
}
bool CSimConnectUtilities::writeSimConnectCfg(const QString &fileName, const QString &ip, int port)
{
const QString sc = CSimConnectUtilities::simConnectCfg(ip, port);
QFile file(fileName);
bool success = false;
if ((success = file.open(QIODevice::WriteOnly | QIODevice::Text)))
if (!windLayer.getSpeed().isNull() && !windLayer.getDirection().isNull())
{
QTextStream out(&file);
out << sc;
file.close();
const int speedKts = windLayer.getSpeed().valueInteger(CSpeedUnit::kts());
const int directionDeg = windLayer.getDirection().valueInteger(CAngleUnit::deg());
simConnectWinds += arg1s.arg(directionDeg, 3, 10, QLatin1Char('0')) % // DDD = Direction (0-360 degrees)
arg1s.arg(speedKts, 3, 10, QLatin1Char('0')); // SSS = Speed
}
return success;
}
// XX = Gust speed
const int gustSpeedKts = windLayer.getGustSpeed().valueInteger(CSpeedUnit::kts());
if (gustSpeedKts > 0) { simConnectWinds += u'G' % arg1s.arg(gustSpeedKts, 2, 10, QLatin1Char('0')); }
QString CSimConnectUtilities::simConnectCfg(const QString &ip, int port)
// UUU = Speed units
simConnectWinds += QStringLiteral("KT");
if (surface)
{
const QString sc = QStringLiteral("[SimConnect]\nProtocol=Ipv4\nAddress=%1\nPort=%2\n"
"MaxReceiveSize=4096\nDisableNagle=0").arg(ip).arg(port);
return sc;
// Surface extension:
// &DNNNNTS
static const QString surfaceWinds =
"&D" // D = specifier for surface layer
"305" // Surface default depth is 1000 feet or 305m
"NG"; // We don't have turbulence or wind shear information, hence we use the defaults
simConnectWinds += surfaceWinds;
surface = false;
}
QString CSimConnectUtilities::resolveEnumToString(const DWORD id, const char *enumName)
else
{
const int i = CSimConnectUtilities::staticMetaObject.indexOfEnumerator(enumName);
if (i < 0) { return QStringLiteral("No enumerator for %1").arg(enumName); }
const QMetaEnum m = CSimConnectUtilities::staticMetaObject.enumerator(i);
const char *k = m.valueToKey(static_cast<int>(id));
return (k) ? QLatin1String(k) : QStringLiteral("Id '%1' not found for %2").arg(id).arg(enumName);
}
CAltitude altitude = windLayer.getLevel();
const QString &CSimConnectUtilities::simConnectIniFilename()
{
static const QString n("SimConnect.ini");
return n;
}
QString CSimConnectUtilities::simConnectExceptionToString(const DWORD id)
{
return CSimConnectUtilities::resolveEnumToString(id, "SIMCONNECT_EXCEPTION");
}
QString CSimConnectUtilities::simConnectSurfaceTypeToString(const DWORD type, bool beautify)
{
QString sf = CSimConnectUtilities::resolveEnumToString(type, "SIMCONNECT_SURFACE");
return beautify ? sf.replace('_', ' ') : sf;
}
QStringList CSimConnectUtilities::getSimConnectIniFileDirectories()
{
const QString docDir = QStandardPaths::locate(QStandardPaths::DocumentsLocation, "", QStandardPaths::LocateDirectory);
if (docDir.isEmpty()) return QStringList();
QDir directory(docDir);
directory.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
const QStringList dirList = directory.entryList();
QStringList simDirs;
for (const QString &dir : dirList)
// this seems to crash FSX, P3D works
// https://www.fsdeveloper.com/forum/threads/setting-winds-aloft.18862/
if (!altitude.isNull() && !isFSX)
{
if (dir.contains("Flight Simulator", Qt::CaseInsensitive) || dir.contains("Prepar3D", Qt::CaseInsensitive))
{
simDirs.push_back(CFileUtils::appendFilePaths(docDir, dir));
}
altitude.toMeanSeaLevel();
int altitudeValueMeters = altitude.valueInteger(CLengthUnit::m());
// Winds aloft extension:
// &ANNNNTS
simConnectWinds +=
u"&A" % // A = specifier for altitude above mean sea-level (MSL)
arg1s.arg(altitudeValueMeters, 4, 10, QLatin1Char('0')) % // NNNN = depth (height) in meters.
u"NG"; // We don't have turbulence or wind shear information, hence we use the defaults
}
// gets the latest P3D as first
simDirs.sort();
std::reverse(std::begin(simDirs), std::end(simDirs));
return simDirs;
}
}
QStringList CSimConnectUtilities::getSimConnectIniFiles()
return QStringLiteral(" ") % simConnectWinds.simplified();
}
QString CSimConnectUtilities::visibilitiesToSimConnectMetar(const CVisibilityLayerList &visibilityLayers)
{
// There are several format options, we use the meter format:
// NNNND&BXXXX&DYYYY
QString simconnectVisibilities;
for (const auto &visibilityLayer : visibilityLayers)
{
simconnectVisibilities += QLatin1Char(' ');
// NNNN = in meters
auto visibility = visibilityLayer.getVisibility().valueInteger(CLengthUnit::m());
visibility = qMin(9999, visibility);
simconnectVisibilities += QStringLiteral("%1").arg(visibility, 4, 10, QLatin1Char('0'));
// D = directional variation
// We set NDV - no directional variation
simconnectVisibilities += QLatin1String("NDV");
// XXXX = base of visibility layer in meters
const auto base = visibilityLayer.getBase().valueInteger(CLengthUnit::m());
simconnectVisibilities += QStringLiteral("&B%1").arg(base, 4, 10, QLatin1Char('0'));
// YYYY = depth of visibility layer in meters
const auto depth = visibilityLayer.getTop().valueInteger(CLengthUnit::m());
simconnectVisibilities += QStringLiteral("&D%1").arg(depth, 4, 10, QLatin1Char('0'));
}
return simconnectVisibilities;
}
QString CSimConnectUtilities::cloudsToSimConnectMetar(const CCloudLayerList &cloudLayers)
{
// Format:
// CCCNNN&BXXXX&DYYYY
QString simconnectClouds;
static const QString arg1s("%1");
for (const CCloudLayer &cloudLayer : cloudLayers)
{
simconnectClouds += QLatin1Char(' ');
// CCC = Coverage string
switch (cloudLayer.getCoverage())
{
QStringList files;
for (const QString &dir : getSimConnectIniFileDirectories())
{
const QFileInfo f(CFileUtils::appendFilePaths(dir, simConnectIniFilename()));
if (f.exists()) { files.push_back(f.absoluteFilePath()); }
}
return files;
case CCloudLayer::None: simconnectClouds += QLatin1String("CLR"); break;
case CCloudLayer::Few: simconnectClouds += QLatin1String("FEW"); break;
case CCloudLayer::Broken: simconnectClouds += QLatin1String("BKN"); break;
case CCloudLayer::Overcast: simconnectClouds += QLatin1String("OVC"); break;
case CCloudLayer::Scattered:
default:
simconnectClouds += QLatin1String("SCT");
}
QString CSimConnectUtilities::getSimConnectIniFileDirectory(CSimulatorInfo &simulator)
// NNN = coded height
// If NNN is 999 the level is 100,000 feet, otherwise it is 100 x NNN in feet
auto level = cloudLayer.getTop().valueInteger(CLengthUnit::ft()) / 100;
// Ignore clouds higher than 99900 feet
if (level > 999) { continue; }
simconnectClouds += arg1s.arg(level, 3, 10, QLatin1Char('0')) %
u'&';
// TT = Cloud type
switch (cloudLayer.getClouds())
{
static const QString docDir = QStandardPaths::locate(QStandardPaths::DocumentsLocation, "", QStandardPaths::LocateDirectory);
if (docDir.isEmpty()) { return {}; }
if (!simulator.isSingleSimulator() || !simulator.isFsxP3DFamily()) return {};
const QString iniDir = CFileUtils::appendFilePaths(docDir, simulator.isP3D() ? "Prepar3D v4 Files" : "Flight Simulator X Files");
if (getSimConnectIniFileDirectories().isEmpty()) { return iniDir; }
for (const QString &dir : getSimConnectIniFileDirectories())
{
if (simulator.isP3D())
{
if (dir.contains("Prepar3D", Qt::CaseInsensitive)) { return dir; }
}
else if (simulator.isFSX())
{
if (dir.contains("Flight Simulator", Qt::CaseInsensitive)) { return dir; }
}
}
return iniDir;
case CCloudLayer::Cirrus: simconnectClouds += QLatin1String("CI"); break;
case CCloudLayer::Stratus: simconnectClouds += QLatin1String("ST"); break;
case CCloudLayer::Thunderstorm: simconnectClouds += QLatin1String("CB"); break;
case CCloudLayer::Cumulus:
default:
simconnectClouds += QLatin1String("CU");
}
QString CSimConnectUtilities::simConnectReceiveIdToString(DWORD type)
// 000 - Unused.
simconnectClouds += QLatin1String("000");
// F = Top of cloud
// Default to F - flat
simconnectClouds += QLatin1Char('F');
// T = Turbulence
// N - None (default)
simconnectClouds += QLatin1Char('N');
// P = precipitation rate
// http://wiki.sandaysoft.com/a/Rain_measurement#Rain_Rate
auto precipitationRate = cloudLayer.getPrecipitationRate();
// Very light rain: precipitation rate is < 0.25 mm/hour
if (precipitationRate < 0.25) { simconnectClouds += QLatin1Char('V'); }
// Light rain: precipitation rate is between 0.25mm/hour and 1.0mm/hour
else if (precipitationRate >= 0.25 && precipitationRate < 1.0) { simconnectClouds += QLatin1Char('L'); }
// Moderate rain: precipitation rate is between 1.0 mm/hour and 4.0 mm/hour
else if (precipitationRate >= 1.0 && precipitationRate < 4.0) { simconnectClouds += QLatin1Char('M'); }
// Heavy rain: recipitation rate is between 4.0 mm/hour and 16.0 mm/hour
else if (precipitationRate >= 4.0 && precipitationRate < 16.0) { simconnectClouds += QLatin1Char('H'); }
// Very heavy rain: precipitation rate is > 16.0 mm/hour
else if (precipitationRate >= 16.0) { simconnectClouds += QLatin1Char('D'); }
// Q = Type of precipitation
switch (cloudLayer.getPrecipitation())
{
const QString ri = CSimConnectUtilities::resolveEnumToString(type, "SIMCONNECT_RECV_ID");
return ri;
case CCloudLayer::Rain: simconnectClouds += QLatin1Char('R'); break;
case CCloudLayer::Snow: simconnectClouds += QLatin1Char('S'); break;
default: simconnectClouds += QLatin1Char('N');
}
int CSimConnectUtilities::lightsToLightStates(const CAircraftLights &lights)
{
int lightMask = 0;
if (lights.isBeaconOn()) { lightMask |= Beacon; }
if (lights.isLandingOn()) { lightMask |= Landing; }
if (lights.isLogoOn()) { lightMask |= Logo; }
if (lights.isNavOn()) { lightMask |= Nav; }
if (lights.isStrobeOn()) { lightMask |= Strobe; }
if (lights.isTaxiOn()) { lightMask |= Taxi; }
return lightMask;
}
// BBB = Coded base height
// the precipitation ends at this height, set to 0 for it to land on the ground
simconnectClouds += QLatin1String("000");
QString CSimConnectUtilities::convertToSimConnectMetar(const CGridPoint &gridPoint, bool isFSX, bool useWindLayers, bool useVisibilityLayers, bool useCloudLayers, bool useTempLayers)
{
// STATION ID
Q_ASSERT(!gridPoint.getIdentifier().isEmpty());
QString simconnectMetar = gridPoint.getIdentifier();
// I = icing rate
// Set to None for now
simconnectClouds += QLatin1String("N");
}
return simconnectClouds;
}
// SURFACE WINDS/WINDS ALOFT
const CWindLayerList windLayers = useWindLayers ? gridPoint.getWindLayers().sortedBy(&CWindLayer::getLevel) : CWindLayerList();
simconnectMetar += windsToSimConnectMetar(windLayers, isFSX);
QString CSimConnectUtilities::temperaturesToSimConnectMetar(const CTemperatureLayerList &temperatureLayers)
{
// Format:
// TT/DD&ANNNNN
QString simconnectTemperatures;
static const QString arg1s("%1");
// VISIBILITY
const CVisibilityLayerList visibilityLayers = useVisibilityLayers ? gridPoint.getVisibilityLayers().sortedBy(&CVisibilityLayer::getBase) : CVisibilityLayerList();
simconnectMetar += visibilitiesToSimConnectMetar(visibilityLayers);
for (const CTemperatureLayer &temperatureLayer : temperatureLayers)
{
simconnectTemperatures += QLatin1Char(' ');
// PRESENT CONDITIONS
// todo
const int temperature = temperatureLayer.getTemperature().valueInteger(CTemperatureUnit::C());
const int dewPoint = temperatureLayer.getDewPoint().valueInteger(CTemperatureUnit::C());
const int altitude = temperatureLayer.getLevel().valueInteger(CLengthUnit::m());
// PARTIAL OBSCURATION
// todo
simconnectTemperatures += arg1s.arg(temperature, 2, 10, QLatin1Char('0')) % // TT = temperature in Celsius
u'/' %
arg1s.arg(dewPoint, 2, 10, QLatin1Char('0')) % // DD = dewpoint in Celsius
u"&A" %
arg1s.arg(altitude, 5, 10, QLatin1Char('0')); // NNNNN = altitude of the temperatures in meters.
}
return simconnectTemperatures;
}
// SKY CONDITIONS
const CCloudLayerList cloudLayers = useCloudLayers ? gridPoint.getCloudLayers().sortedBy(&CCloudLayer::getBase) : CCloudLayerList();
simconnectMetar += cloudsToSimConnectMetar(cloudLayers);
// TEMPERATURE
const CTemperatureLayerList temperatureLayers = useTempLayers ? gridPoint.getTemperatureLayers().sortedBy(&CTemperatureLayer::getLevel) : CTemperatureLayerList();
simconnectMetar += temperaturesToSimConnectMetar(temperatureLayers);
// ALTIMETER
// Format:
// QNNNN
// Q = specifier for altimeter in millibars
// NNNN = altimeter in millibars
if (!gridPoint.getPressureAtMsl().isNull())
{
static const QString arg1s(" Q%1");
const int altimeter = gridPoint.getPressureAtMsl().valueInteger(CPressureUnit::mbar());
simconnectMetar += arg1s.arg(altimeter, 4, 10, QLatin1Char('0'));
}
return simconnectMetar.simplified();
}
void CSimConnectUtilities::registerMetadata()
{
qRegisterMetaType<CSimConnectUtilities::SIMCONNECT_EXCEPTION>();
qRegisterMetaType<CSimConnectUtilities::SIMCONNECT_SURFACE>();
}
QString CSimConnectUtilities::windsToSimConnectMetar(const CWindLayerList &windLayers, bool isFSX)
{
static const QString arg1s("%1");
QString simConnectWinds;
bool surface = true;
for (const CWindLayer &windLayer : windLayers)
{
simConnectWinds += QLatin1Char(' ');
// Format:
// DDDSSSUUU (steady)
// DDDSSSGXXUUU (gusts)
if (windLayer.isDirectionVariable())
{
// DDD = VRB for variable
simConnectWinds += QLatin1String("VRB");
}
else
{
if (!windLayer.getSpeed().isNull() && !windLayer.getDirection().isNull())
{
const int speedKts = windLayer.getSpeed().valueInteger(CSpeedUnit::kts());
const int directionDeg = windLayer.getDirection().valueInteger(CAngleUnit::deg());
simConnectWinds += arg1s.arg(directionDeg, 3, 10, QLatin1Char('0')) % // DDD = Direction (0-360 degrees)
arg1s.arg(speedKts, 3, 10, QLatin1Char('0')); // SSS = Speed
}
}
// XX = Gust speed
const int gustSpeedKts = windLayer.getGustSpeed().valueInteger(CSpeedUnit::kts());
if (gustSpeedKts > 0) { simConnectWinds += u'G' % arg1s.arg(gustSpeedKts, 2, 10, QLatin1Char('0')); }
// UUU = Speed units
simConnectWinds += QStringLiteral("KT");
if (surface)
{
// Surface extension:
// &DNNNNTS
static const QString surfaceWinds =
"&D" // D = specifier for surface layer
"305" // Surface default depth is 1000 feet or 305m
"NG"; // We don't have turbulence or wind shear information, hence we use the defaults
simConnectWinds += surfaceWinds;
surface = false;
}
else
{
CAltitude altitude = windLayer.getLevel();
// this seems to crash FSX, P3D works
// https://www.fsdeveloper.com/forum/threads/setting-winds-aloft.18862/
if (!altitude.isNull() && !isFSX)
{
altitude.toMeanSeaLevel();
int altitudeValueMeters = altitude.valueInteger(CLengthUnit::m());
// Winds aloft extension:
// &ANNNNTS
simConnectWinds +=
u"&A" % // A = specifier for altitude above mean sea-level (MSL)
arg1s.arg(altitudeValueMeters, 4, 10, QLatin1Char('0')) % // NNNN = depth (height) in meters.
u"NG"; // We don't have turbulence or wind shear information, hence we use the defaults
}
}
}
return QStringLiteral(" ") % simConnectWinds.simplified();
}
QString CSimConnectUtilities::visibilitiesToSimConnectMetar(const CVisibilityLayerList &visibilityLayers)
{
// There are several format options, we use the meter format:
// NNNND&BXXXX&DYYYY
QString simconnectVisibilities;
for (const auto &visibilityLayer : visibilityLayers)
{
simconnectVisibilities += QLatin1Char(' ');
// NNNN = in meters
auto visibility = visibilityLayer.getVisibility().valueInteger(CLengthUnit::m());
visibility = qMin(9999, visibility);
simconnectVisibilities += QStringLiteral("%1").arg(visibility, 4, 10, QLatin1Char('0'));
// D = directional variation
// We set NDV - no directional variation
simconnectVisibilities += QLatin1String("NDV");
// XXXX = base of visibility layer in meters
const auto base = visibilityLayer.getBase().valueInteger(CLengthUnit::m());
simconnectVisibilities += QStringLiteral("&B%1").arg(base, 4, 10, QLatin1Char('0'));
// YYYY = depth of visibility layer in meters
const auto depth = visibilityLayer.getTop().valueInteger(CLengthUnit::m());
simconnectVisibilities += QStringLiteral("&D%1").arg(depth, 4, 10, QLatin1Char('0'));
}
return simconnectVisibilities;
}
QString CSimConnectUtilities::cloudsToSimConnectMetar(const CCloudLayerList &cloudLayers)
{
// Format:
// CCCNNN&BXXXX&DYYYY
QString simconnectClouds;
static const QString arg1s("%1");
for (const CCloudLayer &cloudLayer : cloudLayers)
{
simconnectClouds += QLatin1Char(' ');
// CCC = Coverage string
switch (cloudLayer.getCoverage())
{
case CCloudLayer::None: simconnectClouds += QLatin1String("CLR"); break;
case CCloudLayer::Few: simconnectClouds += QLatin1String("FEW"); break;
case CCloudLayer::Broken: simconnectClouds += QLatin1String("BKN"); break;
case CCloudLayer::Overcast: simconnectClouds += QLatin1String("OVC"); break;
case CCloudLayer::Scattered:
default:
simconnectClouds += QLatin1String("SCT");
}
// NNN = coded height
// If NNN is 999 the level is 100,000 feet, otherwise it is 100 x NNN in feet
auto level = cloudLayer.getTop().valueInteger(CLengthUnit::ft()) / 100;
// Ignore clouds higher than 99900 feet
if (level > 999) { continue; }
simconnectClouds += arg1s.arg(level, 3, 10, QLatin1Char('0')) %
u'&';
// TT = Cloud type
switch (cloudLayer.getClouds())
{
case CCloudLayer::Cirrus: simconnectClouds += QLatin1String("CI"); break;
case CCloudLayer::Stratus: simconnectClouds += QLatin1String("ST"); break;
case CCloudLayer::Thunderstorm: simconnectClouds += QLatin1String("CB"); break;
case CCloudLayer::Cumulus:
default:
simconnectClouds += QLatin1String("CU");
}
// 000 - Unused.
simconnectClouds += QLatin1String("000");
// F = Top of cloud
// Default to F - flat
simconnectClouds += QLatin1Char('F');
// T = Turbulence
// N - None (default)
simconnectClouds += QLatin1Char('N');
// P = precipitation rate
// http://wiki.sandaysoft.com/a/Rain_measurement#Rain_Rate
auto precipitationRate = cloudLayer.getPrecipitationRate();
// Very light rain: precipitation rate is < 0.25 mm/hour
if (precipitationRate < 0.25) { simconnectClouds += QLatin1Char('V'); }
// Light rain: precipitation rate is between 0.25mm/hour and 1.0mm/hour
else if (precipitationRate >= 0.25 && precipitationRate < 1.0) { simconnectClouds += QLatin1Char('L'); }
// Moderate rain: precipitation rate is between 1.0 mm/hour and 4.0 mm/hour
else if (precipitationRate >= 1.0 && precipitationRate < 4.0) { simconnectClouds += QLatin1Char('M'); }
// Heavy rain: recipitation rate is between 4.0 mm/hour and 16.0 mm/hour
else if (precipitationRate >= 4.0 && precipitationRate < 16.0) { simconnectClouds += QLatin1Char('H'); }
// Very heavy rain: precipitation rate is > 16.0 mm/hour
else if (precipitationRate >= 16.0) { simconnectClouds += QLatin1Char('D'); }
// Q = Type of precipitation
switch (cloudLayer.getPrecipitation())
{
case CCloudLayer::Rain: simconnectClouds += QLatin1Char('R'); break;
case CCloudLayer::Snow: simconnectClouds += QLatin1Char('S'); break;
default: simconnectClouds += QLatin1Char('N');
}
// BBB = Coded base height
// the precipitation ends at this height, set to 0 for it to land on the ground
simconnectClouds += QLatin1String("000");
// I = icing rate
// Set to None for now
simconnectClouds += QLatin1String("N");
}
return simconnectClouds;
}
QString CSimConnectUtilities::temperaturesToSimConnectMetar(const CTemperatureLayerList &temperatureLayers)
{
// Format:
// TT/DD&ANNNNN
QString simconnectTemperatures;
static const QString arg1s("%1");
for (const CTemperatureLayer &temperatureLayer : temperatureLayers)
{
simconnectTemperatures += QLatin1Char(' ');
const int temperature = temperatureLayer.getTemperature().valueInteger(CTemperatureUnit::C());
const int dewPoint = temperatureLayer.getDewPoint().valueInteger(CTemperatureUnit::C());
const int altitude = temperatureLayer.getLevel().valueInteger(CLengthUnit::m());
simconnectTemperatures += arg1s.arg(temperature, 2, 10, QLatin1Char('0')) % // TT = temperature in Celsius
u'/' %
arg1s.arg(dewPoint, 2, 10, QLatin1Char('0')) % // DD = dewpoint in Celsius
u"&A" %
arg1s.arg(altitude, 5, 10, QLatin1Char('0')); // NNNNN = altitude of the temperatures in meters.
}
return simconnectTemperatures;
}
CWinDllUtils::DLLInfo CSimConnectUtilities::simConnectDllInfo()
{
const QList<CWinDllUtils::ProcessModule> modules = CWinDllUtils::getModules(-1, "simconnect");
if (modules.isEmpty())
{
CWinDllUtils::DLLInfo info;
info.errorMsg = "No SimConnect.dll loaded";
return info;
}
return CWinDllUtils::getDllInfo(modules.first().executable);
}
} // namespace
} // namespace
CWinDllUtils::DLLInfo CSimConnectUtilities::simConnectDllInfo()
{
const QList<CWinDllUtils::ProcessModule> modules = CWinDllUtils::getModules(-1, "simconnect");
if (modules.isEmpty())
{
CWinDllUtils::DLLInfo info;
info.errorMsg = "No SimConnect.dll loaded";
return info;
}
return CWinDllUtils::getDllInfo(modules.first().executable);
}
} // namespace

View File

@@ -35,237 +35,231 @@
using DWORD = unsigned long; //!< Fake Windows DWORD
#endif
namespace BlackMisc
namespace BlackMisc::Simulation::Fsx
{
namespace Simulation
//! Utilities for SimConnect
//! \remark not using the simconnect.h headers as BlackMisc classes are not driver aware
class BLACKMISC_EXPORT CSimConnectUtilities : public QObject
{
namespace Fsx
Q_OBJECT
public:
//! Filename of the file
static const QString &simConnectFilename();
//! Path to swift local config file (generated by us, in swift dir)
static const QString &getSwiftLocalSimConnectCfgFilename();
//! Path to user's config file
static const QString &getUserSimConnectCfgFilename();
//! Has a swift local config file
static bool hasSwiftLocalSimConnectCfgFile();
//! Has a user config file
static bool hasUserSimConnectCfgFile();
//! The simconnect.cfg as settings (or nullptr settings if no such file)
static QSharedPointer<QSettings> simConnectFileAsSettings(const QString &fileName = getSwiftLocalSimConnectCfgFilename());
//! IP address from settings (of simconnect.cfg), "" if not available
static QString ipAddress(const QSettings *simConnectSettings);
//! IP port from settings (of simconnect.cfg), -1 if not available
static int ipPort(const QSettings *simConnectSettings);
//! Content for FSX simconnect.cfg file
//! \param ip IP address of FSX
//! \param port Port of FSX (e.g. 500)
//! \return content for simconnect.cfg
static QString simConnectCfg(const QString &ip, int port = 500);
//! Create a FSX simconnect.cfg file
//! \param fileName and path
//! \param ip IP address of FSX
//! \param port Port of FSX (e.g. 500)
//! \return success
static bool writeSimConnectCfg(const QString &fileName, const QString &ip, int port = 500);
//! Resolve SimConnect exception (based on Qt metadata).
//! \param id enum element
//! \return enum element's name
static QString simConnectExceptionToString(const DWORD id);
//! Resolve SimConnect surface (based on Qt metadata).
//! \param type enum element
//! \param beautify remove "_"
static QString simConnectSurfaceTypeToString(const DWORD type, bool beautify = true);
//! For all P3D and FSX simulators
//! \remark reevaluating directories every time
static QStringList getSimConnectIniFileDirectories();
//! For all P3D and FSX simulators
static QStringList getSimConnectIniFiles();
//! Directory where SimConnect.ini is located
static QString getSimConnectIniFileDirectory(Simulation::CSimulatorInfo &simulator);
//! SimConnect surfaces.
//! \sa http://msdn.microsoft.com/en-us/library/cc526981.aspx#AircraftFlightInstrumentationData
enum SIMCONNECT_SURFACE
{
//! Utilities for SimConnect
//! \remark not using the simconnect.h headers as BlackMisc classes are not driver aware
class BLACKMISC_EXPORT CSimConnectUtilities : public QObject
{
Q_OBJECT
Concrete,
Grass,
Water,
Grass_bumpy,
Asphalt,
Short_grass,
Long_grass,
Hard_turf,
Snow,
Ice,
Urban,
Forest,
Dirt,
Coral,
Gravel,
Oil_treated,
Steel_mats,
Bituminus,
Brick,
Macadam,
Planks,
Sand,
Shale,
Tarmac,
Wright_flyer_track
};
Q_ENUM(SIMCONNECT_SURFACE)
public:
//! Filename of the file
static const QString &simConnectFilename();
//! SimConnect exceptions.
enum SIMCONNECT_EXCEPTION
{
SIMCONNECT_EXCEPTION_NONE,
SIMCONNECT_EXCEPTION_ERROR,
SIMCONNECT_EXCEPTION_SIZE_MISMATCH,
SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID,
SIMCONNECT_EXCEPTION_UNOPENED,
SIMCONNECT_EXCEPTION_VERSION_MISMATCH,
SIMCONNECT_EXCEPTION_TOO_MANY_GROUPS,
SIMCONNECT_EXCEPTION_NAME_UNRECOGNIZED,
SIMCONNECT_EXCEPTION_TOO_MANY_EVENT_NAMES,
SIMCONNECT_EXCEPTION_EVENT_ID_DUPLICATE,
SIMCONNECT_EXCEPTION_TOO_MANY_MAPS,
SIMCONNECT_EXCEPTION_TOO_MANY_OBJECTS,
SIMCONNECT_EXCEPTION_TOO_MANY_REQUESTS,
SIMCONNECT_EXCEPTION_WEATHER_INVALID_PORT,
SIMCONNECT_EXCEPTION_WEATHER_INVALID_METAR,
SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_GET_OBSERVATION,
SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_CREATE_STATION,
SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_REMOVE_STATION,
SIMCONNECT_EXCEPTION_INVALID_DATA_TYPE,
SIMCONNECT_EXCEPTION_INVALID_DATA_SIZE,
SIMCONNECT_EXCEPTION_DATA_ERROR,
SIMCONNECT_EXCEPTION_INVALID_ARRAY,
SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED,
SIMCONNECT_EXCEPTION_LOAD_FLIGHTPLAN_FAILED,
SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE,
SIMCONNECT_EXCEPTION_ILLEGAL_OPERATION,
SIMCONNECT_EXCEPTION_ALREADY_SUBSCRIBED,
SIMCONNECT_EXCEPTION_INVALID_ENUM,
SIMCONNECT_EXCEPTION_DEFINITION_ERROR,
SIMCONNECT_EXCEPTION_DUPLICATE_ID,
SIMCONNECT_EXCEPTION_DATUM_ID,
SIMCONNECT_EXCEPTION_OUT_OF_BOUNDS,
SIMCONNECT_EXCEPTION_ALREADY_CREATED,
SIMCONNECT_EXCEPTION_OBJECT_OUTSIDE_REALITY_BUBBLE,
SIMCONNECT_EXCEPTION_OBJECT_CONTAINER,
SIMCONNECT_EXCEPTION_OBJECT_AI,
SIMCONNECT_EXCEPTION_OBJECT_ATC,
SIMCONNECT_EXCEPTION_OBJECT_SCHEDULE
};
Q_ENUM(SIMCONNECT_EXCEPTION)
//! Path to swift local config file (generated by us, in swift dir)
static const QString &getSwiftLocalSimConnectCfgFilename();
//! Lights for FSX/P3D "LIGHT ON STATES"
//! \sa http://www.prepar3d.com/SDKv2/LearningCenter/utilities/variables/simulation_variables.html
enum LIGHT_STATES
{
Nav = 0x0001,
Beacon = 0x0002,
Landing = 0x0004,
Taxi = 0x0008,
Strobe = 0x0010,
Panel = 0x0020,
Recognition = 0x0040,
Wing = 0x0080,
Logo = 0x0100,
Cabin = 0x0200
};
//! Path to user's config file
static const QString &getUserSimConnectCfgFilename();
//! Receive IDs for SimConnect
enum SIMCONNECT_RECV_ID
{
SIMCONNECT_RECV_ID_NULL,
SIMCONNECT_RECV_ID_EXCEPTION,
SIMCONNECT_RECV_ID_OPEN,
SIMCONNECT_RECV_ID_QUIT,
SIMCONNECT_RECV_ID_EVENT,
SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE,
SIMCONNECT_RECV_ID_EVENT_FILENAME,
SIMCONNECT_RECV_ID_EVENT_FRAME,
SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE,
SIMCONNECT_RECV_ID_WEATHER_OBSERVATION,
SIMCONNECT_RECV_ID_CLOUD_STATE,
SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID,
SIMCONNECT_RECV_ID_RESERVED_KEY,
SIMCONNECT_RECV_ID_CUSTOM_ACTION,
SIMCONNECT_RECV_ID_SYSTEM_STATE,
SIMCONNECT_RECV_ID_CLIENT_DATA,
SIMCONNECT_RECV_ID_EVENT_WEATHER_MODE,
SIMCONNECT_RECV_ID_AIRPORT_LIST,
SIMCONNECT_RECV_ID_VOR_LIST,
SIMCONNECT_RECV_ID_NDB_LIST,
SIMCONNECT_RECV_ID_WAYPOINT_LIST,
SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_SERVER_STARTED,
SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_CLIENT_STARTED,
SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_SESSION_ENDED,
SIMCONNECT_RECV_ID_EVENT_RACE_END,
SIMCONNECT_RECV_ID_EVENT_RACE_LAP,
};
Q_ENUM(SIMCONNECT_RECV_ID)
//! Has a swift local config file
static bool hasSwiftLocalSimConnectCfgFile();
//! Receive id to string
static QString simConnectReceiveIdToString(DWORD type);
//! Has a user config file
static bool hasUserSimConnectCfgFile();
//! Lights to states
static int lightsToLightStates(const Aviation::CAircraftLights &lights);
//! The simconnect.cfg as settings (or nullptr settings if no such file)
static QSharedPointer<QSettings> simConnectFileAsSettings(const QString &fileName = getSwiftLocalSimConnectCfgFilename());
//! Converts the weather at gridPoint to a SimConnect METAR string
static QString convertToSimConnectMetar(const Weather::CGridPoint &gridPoint, bool isFSX, bool useWindLayers = true, bool useVisibilityLayers = true, bool useCloudLayers = true, bool useTempLayers = true);
//! IP address from settings (of simconnect.cfg), "" if not available
static QString ipAddress(const QSettings *simConnectSettings);
//! Get info about SimConnect DLL
static BlackMisc::CWinDllUtils::DLLInfo simConnectDllInfo();
//! IP port from settings (of simconnect.cfg), -1 if not available
static int ipPort(const QSettings *simConnectSettings);
//! SimConnect.ini file name
static const QString &simConnectIniFilename();
//! Content for FSX simconnect.cfg file
//! \param ip IP address of FSX
//! \param port Port of FSX (e.g. 500)
//! \return content for simconnect.cfg
static QString simConnectCfg(const QString &ip, int port = 500);
//! Register metadata
static void registerMetadata();
//! Create a FSX simconnect.cfg file
//! \param fileName and path
//! \param ip IP address of FSX
//! \param port Port of FSX (e.g. 500)
//! \return success
static bool writeSimConnectCfg(const QString &fileName, const QString &ip, int port = 500);
private:
//!
//! Resolve enum value to its cleartext (based on Qt metadata).
//! \param id enum element
//! \param enumName name of the resolved enum
//! \return enum element's name
static QString resolveEnumToString(const DWORD id, const char *enumName);
//! Resolve SimConnect exception (based on Qt metadata).
//! \param id enum element
//! \return enum element's name
static QString simConnectExceptionToString(const DWORD id);
static QString windsToSimConnectMetar(const BlackMisc::Weather::CWindLayerList &windLayers, bool isFSX);
static QString visibilitiesToSimConnectMetar(const BlackMisc::Weather::CVisibilityLayerList &visibilityLayers);
static QString cloudsToSimConnectMetar(const BlackMisc::Weather::CCloudLayerList &cloudLayers);
static QString temperaturesToSimConnectMetar(const BlackMisc::Weather::CTemperatureLayerList &temperatureLayers);
//! Resolve SimConnect surface (based on Qt metadata).
//! \param type enum element
//! \param beautify remove "_"
static QString simConnectSurfaceTypeToString(const DWORD type, bool beautify = true);
//! For all P3D and FSX simulators
//! \remark reevaluating directories every time
static QStringList getSimConnectIniFileDirectories();
//! For all P3D and FSX simulators
static QStringList getSimConnectIniFiles();
//! Directory where SimConnect.ini is located
static QString getSimConnectIniFileDirectory(Simulation::CSimulatorInfo &simulator);
//! SimConnect surfaces.
//! \sa http://msdn.microsoft.com/en-us/library/cc526981.aspx#AircraftFlightInstrumentationData
enum SIMCONNECT_SURFACE
{
Concrete,
Grass,
Water,
Grass_bumpy,
Asphalt,
Short_grass,
Long_grass,
Hard_turf,
Snow,
Ice,
Urban,
Forest,
Dirt,
Coral,
Gravel,
Oil_treated,
Steel_mats,
Bituminus,
Brick,
Macadam,
Planks,
Sand,
Shale,
Tarmac,
Wright_flyer_track
};
Q_ENUM(SIMCONNECT_SURFACE)
//! SimConnect exceptions.
enum SIMCONNECT_EXCEPTION
{
SIMCONNECT_EXCEPTION_NONE,
SIMCONNECT_EXCEPTION_ERROR,
SIMCONNECT_EXCEPTION_SIZE_MISMATCH,
SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID,
SIMCONNECT_EXCEPTION_UNOPENED,
SIMCONNECT_EXCEPTION_VERSION_MISMATCH,
SIMCONNECT_EXCEPTION_TOO_MANY_GROUPS,
SIMCONNECT_EXCEPTION_NAME_UNRECOGNIZED,
SIMCONNECT_EXCEPTION_TOO_MANY_EVENT_NAMES,
SIMCONNECT_EXCEPTION_EVENT_ID_DUPLICATE,
SIMCONNECT_EXCEPTION_TOO_MANY_MAPS,
SIMCONNECT_EXCEPTION_TOO_MANY_OBJECTS,
SIMCONNECT_EXCEPTION_TOO_MANY_REQUESTS,
SIMCONNECT_EXCEPTION_WEATHER_INVALID_PORT,
SIMCONNECT_EXCEPTION_WEATHER_INVALID_METAR,
SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_GET_OBSERVATION,
SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_CREATE_STATION,
SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_REMOVE_STATION,
SIMCONNECT_EXCEPTION_INVALID_DATA_TYPE,
SIMCONNECT_EXCEPTION_INVALID_DATA_SIZE,
SIMCONNECT_EXCEPTION_DATA_ERROR,
SIMCONNECT_EXCEPTION_INVALID_ARRAY,
SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED,
SIMCONNECT_EXCEPTION_LOAD_FLIGHTPLAN_FAILED,
SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE,
SIMCONNECT_EXCEPTION_ILLEGAL_OPERATION,
SIMCONNECT_EXCEPTION_ALREADY_SUBSCRIBED,
SIMCONNECT_EXCEPTION_INVALID_ENUM,
SIMCONNECT_EXCEPTION_DEFINITION_ERROR,
SIMCONNECT_EXCEPTION_DUPLICATE_ID,
SIMCONNECT_EXCEPTION_DATUM_ID,
SIMCONNECT_EXCEPTION_OUT_OF_BOUNDS,
SIMCONNECT_EXCEPTION_ALREADY_CREATED,
SIMCONNECT_EXCEPTION_OBJECT_OUTSIDE_REALITY_BUBBLE,
SIMCONNECT_EXCEPTION_OBJECT_CONTAINER,
SIMCONNECT_EXCEPTION_OBJECT_AI,
SIMCONNECT_EXCEPTION_OBJECT_ATC,
SIMCONNECT_EXCEPTION_OBJECT_SCHEDULE
};
Q_ENUM(SIMCONNECT_EXCEPTION)
//! Lights for FSX/P3D "LIGHT ON STATES"
//! \sa http://www.prepar3d.com/SDKv2/LearningCenter/utilities/variables/simulation_variables.html
enum LIGHT_STATES
{
Nav = 0x0001,
Beacon = 0x0002,
Landing = 0x0004,
Taxi = 0x0008,
Strobe = 0x0010,
Panel = 0x0020,
Recognition = 0x0040,
Wing = 0x0080,
Logo = 0x0100,
Cabin = 0x0200
};
//! Receive IDs for SimConnect
enum SIMCONNECT_RECV_ID
{
SIMCONNECT_RECV_ID_NULL,
SIMCONNECT_RECV_ID_EXCEPTION,
SIMCONNECT_RECV_ID_OPEN,
SIMCONNECT_RECV_ID_QUIT,
SIMCONNECT_RECV_ID_EVENT,
SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE,
SIMCONNECT_RECV_ID_EVENT_FILENAME,
SIMCONNECT_RECV_ID_EVENT_FRAME,
SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE,
SIMCONNECT_RECV_ID_WEATHER_OBSERVATION,
SIMCONNECT_RECV_ID_CLOUD_STATE,
SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID,
SIMCONNECT_RECV_ID_RESERVED_KEY,
SIMCONNECT_RECV_ID_CUSTOM_ACTION,
SIMCONNECT_RECV_ID_SYSTEM_STATE,
SIMCONNECT_RECV_ID_CLIENT_DATA,
SIMCONNECT_RECV_ID_EVENT_WEATHER_MODE,
SIMCONNECT_RECV_ID_AIRPORT_LIST,
SIMCONNECT_RECV_ID_VOR_LIST,
SIMCONNECT_RECV_ID_NDB_LIST,
SIMCONNECT_RECV_ID_WAYPOINT_LIST,
SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_SERVER_STARTED,
SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_CLIENT_STARTED,
SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_SESSION_ENDED,
SIMCONNECT_RECV_ID_EVENT_RACE_END,
SIMCONNECT_RECV_ID_EVENT_RACE_LAP,
};
Q_ENUM(SIMCONNECT_RECV_ID)
//! Receive id to string
static QString simConnectReceiveIdToString(DWORD type);
//! Lights to states
static int lightsToLightStates(const Aviation::CAircraftLights &lights);
//! Converts the weather at gridPoint to a SimConnect METAR string
static QString convertToSimConnectMetar(const Weather::CGridPoint &gridPoint, bool isFSX, bool useWindLayers = true, bool useVisibilityLayers = true, bool useCloudLayers = true, bool useTempLayers = true);
//! Get info about SimConnect DLL
static BlackMisc::CWinDllUtils::DLLInfo simConnectDllInfo();
//! SimConnect.ini file name
static const QString &simConnectIniFilename();
//! Register metadata
static void registerMetadata();
private:
//!
//! Resolve enum value to its cleartext (based on Qt metadata).
//! \param id enum element
//! \param enumName name of the resolved enum
//! \return enum element's name
static QString resolveEnumToString(const DWORD id, const char *enumName);
static QString windsToSimConnectMetar(const BlackMisc::Weather::CWindLayerList &windLayers, bool isFSX);
static QString visibilitiesToSimConnectMetar(const BlackMisc::Weather::CVisibilityLayerList &visibilityLayers);
static QString cloudsToSimConnectMetar(const BlackMisc::Weather::CCloudLayerList &cloudLayers);
static QString temperaturesToSimConnectMetar(const BlackMisc::Weather::CTemperatureLayerList &temperatureLayers);
//! Hidden constructor
CSimConnectUtilities();
};
} // namespace
} // namespace
//! Hidden constructor
CSimConnectUtilities();
};
} // namespace
Q_DECLARE_METATYPE(BlackMisc::Simulation::Fsx::CSimConnectUtilities::SIMCONNECT_EXCEPTION)