diff --git a/externals b/externals index ef0f5499a..685c8fffb 160000 --- a/externals +++ b/externals @@ -1 +1 @@ -Subproject commit ef0f5499a58655a47015ec659c7585476c1a5644 +Subproject commit 685c8fffbcaedbafd4a3186291c753a02f496f08 diff --git a/src/plugins/simulator/fs9/simulatorfs9.cpp b/src/plugins/simulator/fs9/simulatorfs9.cpp index f40e1016c..b5682c0ec 100644 --- a/src/plugins/simulator/fs9/simulatorfs9.cpp +++ b/src/plugins/simulator/fs9/simulatorfs9.cpp @@ -32,6 +32,7 @@ using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Geo; using namespace BlackMisc::Simulation; using namespace BlackMisc::Simulation::FsCommon; +using namespace BlackMisc::Weather; using namespace BlackSimPlugin::Fs9; using namespace BlackSimPlugin::FsCommon; @@ -133,6 +134,11 @@ namespace BlackSimPlugin m_fsuipc->connect(); // connect FSUIPC too } m_dispatchTimerId = startTimer(50); + + // Pull weather data from core. + // Since we don't get weather data from core yet, use hard coded weather. + injectWeatherGrid(CWeatherGrid::getCavokGrid()); + return true; } @@ -361,6 +367,11 @@ namespace BlackSimPlugin } } + void CSimulatorFs9::injectWeatherGrid(const Weather::CWeatherGrid &weatherGrid) + { + m_fsuipc->write(weatherGrid); + } + CSimulatorFs9Listener::CSimulatorFs9Listener(const CSimulatorPluginInfo &info, const QSharedPointer &fs9Host, const QSharedPointer &lobbyClient) : diff --git a/src/plugins/simulator/fs9/simulatorfs9.h b/src/plugins/simulator/fs9/simulatorfs9.h index a768ab748..7802c1144 100644 --- a/src/plugins/simulator/fs9/simulatorfs9.h +++ b/src/plugins/simulator/fs9/simulatorfs9.h @@ -105,6 +105,9 @@ namespace BlackSimPlugin void disconnectAllClients(); + //! Inject weather grid to simulator + void injectWeatherGrid(const BlackMisc::Weather::CWeatherGrid &weatherGrid); + QHash> m_hashFs9Clients; QMetaObject::Connection m_connectionHostMessages; int m_dispatchTimerId = -1; diff --git a/src/plugins/simulator/fscommon/fsuipc.cpp b/src/plugins/simulator/fscommon/fsuipc.cpp index 6e55a3b83..7afcbb99d 100644 --- a/src/plugins/simulator/fscommon/fsuipc.cpp +++ b/src/plugins/simulator/fscommon/fsuipc.cpp @@ -15,6 +15,7 @@ #include // bug in FSUIPC_User.h, windows.h not included, so we have to import it first #include "FSUIPC/FSUIPC_User.h" +#include "FSUIPC/NewWeather.h" #include "blackmisc/simulation/fscommon/bcdconversions.h" #include "blackmisc/logmessage.h" @@ -29,13 +30,17 @@ using namespace BlackMisc::Network; using namespace BlackMisc::Geo; using namespace BlackMisc::Simulation; using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Weather; namespace BlackSimPlugin { namespace FsCommon { - CFsuipc::CFsuipc() { } + CFsuipc::CFsuipc() + { + startTimer(100); + } CFsuipc::~CFsuipc() { @@ -89,6 +94,121 @@ namespace BlackSimPlugin return false; } + bool CFsuipc::write(const BlackMisc::Weather::CWeatherGrid &weatherGrid) + { + if (!this->isConnected()) { return false; } + + clearAllWeather(); + + CGridPoint gridPoint = weatherGrid.front(); + + NewWeather nw; + // Clear new weather + nw.uCommand = NW_SET; + nw.uFlags = 0; + nw.ulSignature = 0; + nw.uDynamics = 0; + for (std::size_t i = 0; i < sizeof(nw.uSpare) / sizeof(nw.uSpare[0]); i++) { nw.uSpare[i] = 0; } + + nw.dLatitude = 0.0; + nw.dLongitude = 0.0; + nw.nElevation = 0; + nw.ulTimeStamp = 0; + nw.nTempCtr = 0; + nw.nWindsCtr = 0; + nw.nCloudsCtr = 0; + nw.nElevation = 0; // metres * 65536; + nw.nUpperVisCtr = 0; + + // todo: Take station from weather grid + memcpy(nw.chICAO, "GLOB", 4); + + const CVisibilityLayerList visibilityLayers = gridPoint.getVisibilityLayers(); + for (const auto &visibilityLayer : visibilityLayers) + { + NewVis vis; + vis.LowerAlt = visibilityLayer.getBase().value(CLengthUnit::m()); + vis.UpperAlt = visibilityLayer.getCeiling().value(CLengthUnit::m()); + vis.Range = visibilityLayer.getVisibility().value(CLengthUnit::mi()) * 100; + nw.Vis = vis; + } + + const CTemperatureLayerList temperatureLayers = gridPoint.getTemperatureLayers(); + for (const auto &temperatureLayer : temperatureLayers) + { + NewTemp temp; + temp.Alt = temperatureLayer.getLevel().value(CLengthUnit::m()); + temp.Day = temperatureLayer.getTemperature().value(CTemperatureUnit::C()); + temp.DayNightVar = 3; + temp.DewPoint = temperatureLayer.getDewPoint().value(CTemperatureUnit::C()); + nw.Temp[nw.nTempCtr++] = temp; + } + + const CCloudLayerList cloudLayers = gridPoint.getCloudLayers(); + for (const auto &cloudLayer : cloudLayers) + { + NewCloud cloud; + cloud.Coverage = static_cast(cloudLayer.getCoverage()); + + switch (cloudLayer.getCoverage()) + { + case CCloudLayer::None: cloud.Coverage = 0; break; + case CCloudLayer::Few: cloud.Coverage = 2; break; + case CCloudLayer::Scattered: cloud.Coverage = 4; break; + case CCloudLayer::Broken: cloud.Coverage = 6; break; + case CCloudLayer::Overcast: cloud.Coverage = 8; break; + default: cloud.Coverage = 0; + } + + cloud.Deviation = 0; + cloud.Icing = 0; + cloud.LowerAlt = cloudLayer.getBase().value(CLengthUnit::m()); + cloud.PrecipBase = 0; + cloud.PrecipRate = static_cast(cloudLayer.getPrecipitationRate()); + cloud.PrecipType = static_cast(cloudLayer.getPrecipitation()); + cloud.TopShape = 0; + cloud.Turbulence = 0; + + switch (cloudLayer.getClouds()) + { + case CCloudLayer::NoClouds: cloud.Type = 0; break; + case CCloudLayer::Cirrus: cloud.Type = 1; break; + case CCloudLayer::Stratus: cloud.Type = 8; break; + case CCloudLayer::Cumulus: cloud.Type = 9; break; + case CCloudLayer::Thunderstorm: cloud.Type = 10; break; + default: cloud.Type = 0; + } + + cloud.UpperAlt = cloudLayer.getCeiling().value(CLengthUnit::m()); + nw.Cloud[nw.nCloudsCtr++] = cloud; + } + + const CWindLayerList windLayers = gridPoint.getWindLayers(); + for (const auto &windLayer : windLayers) + { + NewWind wind; + wind.Direction = windLayer.getDirection().value(CAngleUnit::deg()) * 65536 / 360.0; + wind.GapAbove = 0; + wind.Gust = windLayer.getGustSpeed().value(CSpeedUnit::kts()); + wind.Shear = 0; + wind.Speed = windLayer.getSpeed().value(CSpeedUnit::kts()); + wind.SpeedFract = 0; + wind.Turbulence = 0; + wind.UpperAlt = windLayer.getLevel().value(CLengthUnit::m()); + wind.Variance = 0; + nw.Wind[nw.nWindsCtr++] = wind; + } + + NewPress press; + press.Drift = 0; + press.Pressure = 15827; // 16 x mb + nw.Press = press; + + QByteArray weatherData(reinterpret_cast(&nw), sizeof(NewWeather)); + m_weatherMessageQueue.append(FsuipcWeatherMessage(0xC800, weatherData, 5)); + return true; + } + bool CFsuipc::read(CSimulatedAircraft &aircraft, bool cockpit, bool situation, bool aircraftParts) { DWORD dwResult; @@ -265,6 +385,68 @@ namespace BlackSimPlugin return read; } + void CFsuipc::timerEvent(QTimerEvent *event) + { + Q_UNUSED(event); + processWeatherMessages(); + } + + CFsuipc::FsuipcWeatherMessage::FsuipcWeatherMessage(unsigned int offset, const QByteArray &data, int leftTrials) : + m_offset(offset), m_messageData(data), m_leftTrials(leftTrials) + { } + + + void CFsuipc::clearAllWeather() + { + if (!this->isConnected()) { return; } + + // clear all weather + NewWeather nw; + + // Clear new weather + nw.uCommand = NW_CLEAR; + nw.uFlags = 0; + nw.ulSignature = 0; + nw.uDynamics = 0; + for (std::size_t i = 0; i < sizeof(nw.uSpare) / sizeof(nw.uSpare[0]); i++) { nw.uSpare[i] = 0; } + + nw.dLatitude = 0.; + nw.dLongitude = 0.; + nw.nElevation = 0; + nw.ulTimeStamp = 0; + nw.nTempCtr = 0; + nw.nWindsCtr = 0; + nw.nCloudsCtr = 0; + QByteArray clearWeather(reinterpret_cast(&nw), sizeof(NewWeather)); + m_weatherMessageQueue.append(FsuipcWeatherMessage(0xC800, clearWeather, 1)); + } + + void CFsuipc::processWeatherMessages() + { + if (m_weatherMessageQueue.empty()) { return; } + FsuipcWeatherMessage &weatherMessage = m_weatherMessageQueue.first(); + + DWORD dwResult; + weatherMessage.m_leftTrials--; + FSUIPC_Write(weatherMessage.m_offset, weatherMessage.m_messageData.size(), reinterpret_cast(weatherMessage.m_messageData.data()), &dwResult); + + unsigned int timeStamp = 0; + FSUIPC_Read(0xC824, sizeof(timeStamp), &timeStamp, &dwResult); + FSUIPC_Process(&dwResult); + if (timeStamp > m_lastTimestamp) + { + m_weatherMessageQueue.removeFirst(); + m_lastTimestamp = timeStamp; + return; + } + + if (weatherMessage.m_leftTrials == 0) + { + CLogMessage(this).debug() << "Number of trials reached for weather message. Dropping it."; + m_weatherMessageQueue.removeFirst(); + } + } + double CFsuipc::intToFractional(double fractional) { double f = fractional / 10.0; diff --git a/src/plugins/simulator/fscommon/fsuipc.h b/src/plugins/simulator/fscommon/fsuipc.h index 350bc1356..ad8731f2a 100644 --- a/src/plugins/simulator/fscommon/fsuipc.h +++ b/src/plugins/simulator/fscommon/fsuipc.h @@ -13,6 +13,7 @@ #define BLACKSIMPLUGIN_FSUIPC_H #include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/weather/weathergrid.h" #include namespace BlackSimPlugin @@ -20,8 +21,10 @@ namespace BlackSimPlugin namespace FsCommon { //! Class representing a FSUIPC "interface" - class CFsuipc + class CFsuipc : public QObject { + Q_OBJECT + public: //! Constructor @@ -45,6 +48,9 @@ namespace BlackSimPlugin //! Write variables bool write(const BlackMisc::Simulation::CSimulatedAircraft &aircraft); + //! Write weather grid to simulator + bool write(const BlackMisc::Weather::CWeatherGrid &weatherGrid); + //! Read data from FSUIPC //! \param aircraft object to be updated //! \param cockpit update cockpit data @@ -97,11 +103,30 @@ namespace BlackSimPlugin //! Log message category static QString getMessageCategory() { return "swift.fscommon.fsuipc"; } + protected: + //! \copydoc QObject::timerEvent + void timerEvent(QTimerEvent *event); + private: + struct FsuipcWeatherMessage + { + FsuipcWeatherMessage() = default; + FsuipcWeatherMessage(unsigned int offset, const QByteArray &data, int leftTrials); + int m_offset = 0; + QByteArray m_messageData; + int m_leftTrials = 0; + }; + + void clearAllWeather(); + void processWeatherMessages(); + bool m_connected = false; QString m_lastErrorMessage; QString m_fsuipcVersion; + QVector m_weatherMessageQueue; + unsigned int m_lastTimestamp = 0; + //! Integer representing fractional static double intToFractional(double fractional); diff --git a/src/plugins/simulator/fsx/simulatorfsx.cpp b/src/plugins/simulator/fsx/simulatorfsx.cpp index faa3fe968..ebd84f706 100644 --- a/src/plugins/simulator/fsx/simulatorfsx.cpp +++ b/src/plugins/simulator/fsx/simulatorfsx.cpp @@ -33,6 +33,7 @@ using namespace BlackMisc::Simulation; using namespace BlackMisc::Simulation; using namespace BlackMisc::Simulation::FsCommon; using namespace BlackMisc::Simulation::Fsx; +using namespace BlackMisc::Weather; using namespace BlackCore; namespace BlackSimPlugin @@ -50,7 +51,7 @@ namespace BlackSimPlugin Q_ASSERT(remoteAircraftProvider); this->m_simulatorSetup = CFsxSimulatorSetup::getInitialSetup(); - m_useFsuipc = false; // do not use FSUIPC at the moment with FSX + m_useFsuipc = true; // Temporarily enabled until Simconnect Weather is implemented. this->m_interpolator = new CInterpolatorLinear(remoteAircraftProvider, this); m_modelMatcher.setDefaultModel(CAircraftModel( "Boeing 737-800 Paint1", @@ -95,6 +96,10 @@ namespace BlackSimPlugin m_simconnectTimerId = startTimer(10); m_simConnected = true; emitSimulatorCombinedStatus(); + + // Pull weather data from core. + // Since we don't get weather data from core yet, use hard coded weather. + injectWeatherGrid(CWeatherGrid::getCavokGrid()); return true; } @@ -824,6 +829,11 @@ namespace BlackSimPlugin } } + void CSimulatorFsx::injectWeatherGrid(const Weather::CWeatherGrid &weatherGrid) + { + m_fsuipc->write(weatherGrid); + } + CSimulatorFsxListener::CSimulatorFsxListener(const CSimulatorPluginInfo &info) : ISimulatorListener(info), m_timer(new QTimer(this)) diff --git a/src/plugins/simulator/fsx/simulatorfsx.h b/src/plugins/simulator/fsx/simulatorfsx.h index 8bb90a5c2..53e14242e 100644 --- a/src/plugins/simulator/fsx/simulatorfsx.h +++ b/src/plugins/simulator/fsx/simulatorfsx.h @@ -179,6 +179,9 @@ namespace BlackSimPlugin //! Sync time with user's computer void synchronizeTime(const BlackMisc::PhysicalQuantities::CTime &zuluTimeSim, const BlackMisc::PhysicalQuantities::CTime &localTimeSim); + //! Inject weather grid to simulator + void injectWeatherGrid(const BlackMisc::Weather::CWeatherGrid &weatherGrid); + static const int SkipUpdateCyclesForCockpit = 10; //!< skip x cycles before updating cockpit again bool m_simConnected = false; //!< Is simulator connected? bool m_simSimulating = false; //!< Simulator running?