mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-09 05:28:09 +08:00
Use nested namespaces (C++17 feature)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user