diff --git a/samples/blacksim/main.cpp b/samples/blacksim/main.cpp index 4750fb649..d20e0843d 100644 --- a/samples/blacksim/main.cpp +++ b/samples/blacksim/main.cpp @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) } else if (i.startsWith("2")) { - BlackSimTest::CSamplesFsx::samples(streamOut); + BlackSimTest::CSamplesFsx::samplesMisc(streamOut); } else if (i.startsWith("3")) { diff --git a/samples/blacksim/samplesfsx.cpp b/samples/blacksim/samplesfsx.cpp index 2931700b9..3fb334c1a 100644 --- a/samples/blacksim/samplesfsx.cpp +++ b/samples/blacksim/samplesfsx.cpp @@ -20,7 +20,7 @@ namespace BlackSimTest /* * Samples */ - int CSamplesFsx::samples(QTextStream &streamOut) + int CSamplesFsx::samplesMisc(QTextStream &streamOut) { BlackSim::registerMetadata(); streamOut << CSimConnectUtilities::simConnectExceptionToString(CSimConnectUtilities::SIMCONNECT_EXCEPTION_ALREADY_SUBSCRIBED) << endl; @@ -29,4 +29,10 @@ namespace BlackSimTest return 0; } + int CSamplesFsx::driverTest(QTextStream &streamOut) + { + Q_UNUSED(streamOut); + return 0; + } + } // namespace diff --git a/samples/blacksim/samplesfsx.h b/samples/blacksim/samplesfsx.h index 00adc2879..6e70aae2c 100644 --- a/samples/blacksim/samplesfsx.h +++ b/samples/blacksim/samplesfsx.h @@ -20,8 +20,12 @@ namespace BlackSimTest class CSamplesFsx { public: - //! Run the samples - static int samples(QTextStream &streamOut); + //! Run the misc. samples + static int samplesMisc(QTextStream &streamOut); + + //! Driver test / SimConnect test + static int driverTest(QTextStream &streamOut); + }; } // namespace diff --git a/src/blackcore/interpolator.cpp b/src/blackcore/interpolator.cpp index 811469f1e..e7ef8033e 100644 --- a/src/blackcore/interpolator.cpp +++ b/src/blackcore/interpolator.cpp @@ -66,7 +66,7 @@ namespace BlackCore return m_situationsByCallsign; } - CAircraftPartsList IInterpolator::getAndRemovePartsBeforeOffset(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus) + CAircraftPartsList IInterpolator::getAndRemovePartsBeforeTime(const CCallsign &callsign, qint64 cutoffTime, BlackCore::IInterpolator::PartsStatus &partsStatus) { static const CAircraftPartsList empty; partsStatus.reset(); @@ -78,6 +78,7 @@ namespace BlackCore } else { + // did we ever have parts for this callsign partsStatus.supportsParts = m_aircraftSupportingParts.contains(callsign); return empty; } @@ -99,6 +100,19 @@ namespace BlackCore return m_situationsByCallsign[callsign]; } + CAircraftPartsList IInterpolator::getPartsForCallsign(const CCallsign &callsign) const + { + QReadLocker l(&m_lockParts); + static const CAircraftPartsList empty; + if (!m_partsByCallsign.contains(callsign)) { return empty; } + return m_partsByCallsign[callsign]; + } + + void IInterpolator::forceSorting(bool sort) + { + this->m_forceSortWhenAddingValues = sort; + } + void IInterpolator::ps_onAddedAircraftSituation(const CAircraftSituation &situation) { QWriteLocker lock(&m_lockSituations); @@ -110,6 +124,10 @@ namespace BlackCore // list from new to old CAircraftSituationList &l = this->m_situationsByCallsign[callsign]; l.push_frontMaxElements(situation, MaxSituationsPerCallsign); + if (this->m_forceSortWhenAddingValues) { l.sortLatestFirst(); } + + // check sort order + Q_ASSERT(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch()); } void IInterpolator::ps_onAddedAircraftParts(const CAircraftParts &parts) @@ -123,9 +141,14 @@ namespace BlackCore // list sorted from new to old CAircraftPartsList &l = this->m_partsByCallsign[callsign]; l.push_frontMaxElements(parts, MaxPartsPerCallsign); + if (this->m_forceSortWhenAddingValues) { l.sortLatestFirst(); } if (m_aircraftSupportingParts.contains(callsign)) { return; } - m_aircraftSupportingParts.push_back(callsign); + m_aircraftSupportingParts.push_back(callsign); // mark as callsign which supports parts + + // check sort order + Q_ASSERT(l.size() < 2 || l[0].getMSecsSinceEpoch() >= l[1].getMSecsSinceEpoch()); + } void IInterpolator::ps_onRemovedAircraft(const CCallsign &callsign) diff --git a/src/blackcore/interpolator.h b/src/blackcore/interpolator.h index 0a3857cd5..794fb5fb7 100644 --- a/src/blackcore/interpolator.h +++ b/src/blackcore/interpolator.h @@ -85,7 +85,7 @@ namespace BlackCore //! Parts before given offset time (aka pending parts) //! \threadsafe - virtual BlackMisc::Aviation::CAircraftPartsList getAndRemovePartsBeforeOffset(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset, PartsStatus &partsStatus); + virtual BlackMisc::Aviation::CAircraftPartsList getAndRemovePartsBeforeTime(const BlackMisc::Aviation::CCallsign &callsign, qint64 timeOffset, PartsStatus &partsStatus); //! Clear all data //! \threadsafe @@ -95,9 +95,16 @@ namespace BlackCore //! \threadsafe BlackMisc::Aviation::CAircraftSituationList getSituationsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const; + //! Parts for given callsign + //! \threadsafe + BlackMisc::Aviation::CAircraftPartsList getPartsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const; + //! Enable debug messages void enableDebugMessages(bool enabled); + //! Force sorting (latest first), not required if order can be guaranteed + void forceSorting(bool sort); + static const qint64 TimeOffsetMs = 6000; //!< offset for interpolation static const int MaxSituationsPerCallsign = 6; //!< How many situations per callsign static const int MaxPartsPerCallsign = 3; //!< How many parts per callsign @@ -120,7 +127,8 @@ namespace BlackCore //! Constructor IInterpolator(BlackMisc::Simulation::IRemoteAircraftProviderReadOnly *provider, const QString &workerName, QObject *parent = nullptr); - bool m_withDebugMsg = false; //!< allows to disable debug messages + bool m_withDebugMsg = false; //!< allows to disable debug messages + bool m_forceSortWhenAddingValues = false; //!< force sorting (latest first) when adding values BlackMisc::Aviation::CCallsignList m_aircraftSupportingParts; //!< aircraft supporting parts // hashs, because not sorted by key but keeping order diff --git a/src/blackcore/interpolator_linear.cpp b/src/blackcore/interpolator_linear.cpp index a507b70bf..d2875a8d5 100644 --- a/src/blackcore/interpolator_linear.cpp +++ b/src/blackcore/interpolator_linear.cpp @@ -30,8 +30,9 @@ namespace BlackCore if (situationsPerCallsign) { - if (!situationsPerCallsign->contains(callsign)) { return empty; } - if ((*situationsPerCallsign)[callsign].isEmpty()) { return empty; } + // lock free, expected that situationsPerCallsign is a copy + if (!situationsPerCallsign->contains(callsign)) { return empty; } + if ((*situationsPerCallsign)[callsign].isEmpty()) { return empty; } splitSituations = (*situationsPerCallsign)[callsign].splitByTime(splitTimeMsSinceEpoch); } else @@ -40,7 +41,7 @@ namespace BlackCore QReadLocker lock(&m_lockSituations); if (!m_situationsByCallsign.contains(callsign)) { return empty; } if (m_situationsByCallsign[callsign].isEmpty()) { return empty; } - splitSituations = m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch); + splitSituations = m_situationsByCallsign[callsign].splitByTime(splitTimeMsSinceEpoch, true); } CAircraftSituationList &situationsNewer = splitSituations[0]; // newer part @@ -78,8 +79,8 @@ namespace BlackCore } else { - oldSituation = situationsOlder.front(); // first oldest - newSituation = situationsNewer.back(); // latest newest + oldSituation = situationsOlder.front(); // first oldest (aka newest oldest) + newSituation = situationsNewer.back(); // latest newest (aka oldest of newer block) Q_ASSERT(oldSituation.getMSecsSinceEpoch() < newSituation.getMSecsSinceEpoch()); } @@ -103,12 +104,12 @@ namespace BlackCore } } - // Interpolate latitude: Lat = (LatB - LatA) * t + LatA const CLatitude oldLat(oldSituation.latitude()); const CLatitude newLat(newSituation.latitude()); const CLongitude oldLng(oldSituation.longitude()); const CLongitude newLng(newSituation.longitude()); + // Interpolate latitude: Lat = (LatB - LatA) * t + LatA currentPosition.setLatitude((newLat - oldLat) * simulationTimeFraction + oldLat); @@ -130,6 +131,8 @@ namespace BlackCore if (newLat == oldLat && newLng == oldLng && oldAlt == newAlt) { + // stop interpolation here + //! \todo Does not work for VTOL aircraft. We need a flag for VTOL aircraft return currentSituation; } diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 9c1a87ffc..e4a9e7654 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -60,7 +60,7 @@ namespace BlackMisc CSequence(std::initializer_list il) : m_pimpl(new Pimpl>(QList(il))) {} /*! - * \brief By QList of type . + * \brief By QList of type T. */ CSequence(const QList &list) : m_pimpl(new Pimpl>(QList(list))) {} diff --git a/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp b/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp index fbe5e862f..f5e07b9b9 100644 --- a/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp +++ b/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.cpp @@ -76,9 +76,17 @@ namespace BlackMisc void CRemoteAircraftProviderDummy::insertNewSituation(const CAircraftSituation &situation) { this->m_situations.push_frontMaxElements(situation, 20); + this->m_situations.sortLatestFirst(); // like in real world, latest should be first emit addedRemoteAircraftSituation(situation); } + void CRemoteAircraftProviderDummy::insertNewAircraftParts(const CAircraftParts &parts) + { + this->m_parts.push_frontMaxElements(parts, 20); + this->m_parts.sortLatestFirst(); // like in real world, latest should be first + emit addedRemoteAircraftParts(parts); + } + void CRemoteAircraftProviderDummy::clear() { m_situations.clear(); diff --git a/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.h b/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.h index 826e468e2..432dfa294 100644 --- a/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.h +++ b/src/blackmisc/simulation/simdirectaccessremoteaircraftdummy.h @@ -65,7 +65,10 @@ namespace BlackMisc //! For testing, add new situation and fire signals void insertNewSituation(const BlackMisc::Aviation::CAircraftSituation &situation); - // clear all data + //! For testing, add new parts and fire signals + void insertNewAircraftParts(const BlackMisc::Aviation::CAircraftParts &parts); + + //! Clear all data void clear(); signals: diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp index 694ec5565..631d83ee8 100644 --- a/src/blackmisc/timestampobjectlist.cpp +++ b/src/blackmisc/timestampobjectlist.cpp @@ -59,10 +59,10 @@ namespace BlackMisc } template - QList ITimestampObjectList::splitByTime(qint64 msSinceEpoch) const + QList ITimestampObjectList::splitByTime(qint64 msSinceEpoch, bool alreadySortedLatestFirst) const { CONTAINER newer(this->container()); - newer.sortLatestFirst(); + if (!alreadySortedLatestFirst) { newer.sortLatestFirst(); } CONTAINER older; for (auto it = newer.begin(); it != newer.end(); ++it) { diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 660adf8b5..f2dfd6869 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -46,7 +46,7 @@ namespace BlackMisc //! Split into 2 containers, [0] >= msSinceEpoch ("newer") [b] < msSinceEpoch ("older") //! \note Sort order: latest elements first - QList splitByTime(qint64 msSinceEpoch) const; + QList splitByTime(qint64 msSinceEpoch, bool alreadySortedLatestFirst = false) const; //! Latest value OBJ latestValue() const; diff --git a/src/plugins/simulator/fsx/simulator_fsx.cpp b/src/plugins/simulator/fsx/simulator_fsx.cpp index 16e6afc6b..96269079a 100644 --- a/src/plugins/simulator/fsx/simulator_fsx.cpp +++ b/src/plugins/simulator/fsx/simulator_fsx.cpp @@ -552,6 +552,9 @@ namespace BlackSimPlugin hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluDay, "ZULU_DAY_SET"); hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluHours, "ZULU_HOURS_SET"); hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluMinutes, "ZULU_MINUTES_SET"); + + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleTaxiLights, "TOGGLE_TAXI_LIGHTS"); + if (hr != S_OK) { CLogMessage(this).error("FSX plugin error: %1") << "SimConnect_MapClientEventToSimEvent failed"; @@ -617,13 +620,14 @@ namespace BlackSimPlugin { const CCallsign callsign(simObj.getCallsign()); IInterpolator::InterpolationStatus interpolatorStatus; - IInterpolator::PartsStatus partsStatus; if (simObj.getObjectId() == 0) { continue; } CAircraftSituation interpolatedSituation = this->m_interpolator->getInterpolatedSituation(callsign, currentTimestamp, interpolatorStatus); - // having the on gground flag in parts forces me to obtain parts here - // which is not hte smartest thing regarding performance - CAircraftPartsList parts = this->m_interpolator->getAndRemovePartsBeforeOffset(callsign, currentTimestamp - IInterpolator::TimeOffsetMs, partsStatus); + // having the onGround flag in parts forces me to obtain parts here + // which is not the smartest thing regarding performance + IInterpolator::PartsStatus partsStatus; + CAircraftPartsList parts = this->m_interpolator->getAndRemovePartsBeforeTime(callsign, currentTimestamp - IInterpolator::TimeOffsetMs, partsStatus); + if (interpolatorStatus.allTrue()) { // update situation @@ -648,87 +652,104 @@ namespace BlackSimPlugin simObj.getObjectId(), 0, 0, sizeof(SIMCONNECT_DATA_INITPOSITION), &position); if (hr != S_OK) { CLogMessage(this).warning("Failed so set position on SimObject %1 callsign: %2") << simObj.getObjectId() << callsign; } + + + + } // interpolation data + + if (interpolatorStatus.interpolationSucceeded) + { + // aircraft parts + // inside interpolator if, as no parts can be sent without position + updateRemoteAircraftParts(simObj, parts, partsStatus, interpolatedSituation, isOnGround); // update and retrieve parts in the same step } + } // all callsigns + qint64 dt = QDateTime::currentMSecsSinceEpoch() - currentTimestamp; + m_statsUpdateAircraftTimeTotal += dt; + m_statsUpdateAircraftCount++; + m_statsUpdateAircraftTimeAvg = m_statsUpdateAircraftTimeTotal / m_statsUpdateAircraftCount; + } - // set parts - DataDefinitionRemoteAircraftParts ddRemoteAircraftParts; - if (partsStatus.supportsParts) + bool CSimulatorFsx::updateRemoteAircraftParts(const CSimConnectObject &simObj, const CAircraftPartsList &parts, IInterpolator::PartsStatus partsStatus, const CAircraftSituation &interpolatedSituation, bool isOnGround) const + { + // set parts + DataDefinitionRemoteAircraftParts ddRemoteAircraftParts; + if (partsStatus.supportsParts) + { + // parts is supported, but do we need to update? + if (parts.isEmpty()) { return false; } + + // we have parts + CAircraftParts newestParts = parts.front(); + ddRemoteAircraftParts.lightStrobe = newestParts.getLights().isStrobeOn() ? 1.0 : 0.0; + ddRemoteAircraftParts.lightLanding = newestParts.getLights().isLandingOn() ? 1.0 : 0.0; + // ddRemoteAircraftParts.lightTaxi = newestParts.getLights().isTaxiOn() ? 1.0 : 0.0; + ddRemoteAircraftParts.lightBeacon = newestParts.getLights().isBeaconOn() ? 1.0 : 0.0; + ddRemoteAircraftParts.lightNav = newestParts.getLights().isNavOn() ? 1.0 : 0.0; + ddRemoteAircraftParts.lightLogo = newestParts.getLights().isLogoOn() ? 1.0 : 0.0; + ddRemoteAircraftParts.flapsLeadingEdgeLeftPercent = newestParts.getFlapsPercent() / 100.0; + ddRemoteAircraftParts.flapsLeadingEdgeRightPercent = newestParts.getFlapsPercent() / 100.0; + ddRemoteAircraftParts.flapsTrailingEdgeLeftPercent = newestParts.getFlapsPercent() / 100.0; + ddRemoteAircraftParts.flapsTrailingEdgeRightPercent = newestParts.getFlapsPercent() / 100.0; + ddRemoteAircraftParts.spoilersHandlePosition = newestParts.isSpoilersOut() ? 1.0 : 0.0; + ddRemoteAircraftParts.gearHandlePosition = newestParts.isGearDown() ? 1 : 0; + ddRemoteAircraftParts.engine1Combustion = newestParts.isEngineOn(1) ? 1 : 0; + ddRemoteAircraftParts.engine2Combustion = newestParts.isEngineOn(2) ? 1 : 0;; + ddRemoteAircraftParts.engine3Combustion = newestParts.isEngineOn(3) ? 1 : 0; + ddRemoteAircraftParts.engine4Combustion = newestParts.isEngineOn(4) ? 1 : 0; + } + else + { + // mode is guessing parts + if (this->m_interpolationRequest % 20 != 0) { return false; } // only update every 20th cycle + ddRemoteAircraftParts.gearHandlePosition = isOnGround ? 1 : 0; + + // when first detected moving, lights on + if (isOnGround) { - // parts is supported, but do we need to update? - if (parts.isEmpty()) { continue; } + // ddRemoteAircraftParts.lightTaxi = 1.0; + ddRemoteAircraftParts.lightBeacon = 1.0; + ddRemoteAircraftParts.lightNav = 1.0; - // we have parts - CAircraftParts newestParts = parts.front(); - ddRemoteAircraftParts.lightStrobe = newestParts.getLights().isStrobeOn() ? 1.0 : 0.0; - ddRemoteAircraftParts.lightLanding = newestParts.getLights().isLandingOn() ? 1.0 : 0.0; - // ddRemoteAircraftParts.lightTaxi = newestParts.getLights().isTaxiOn() ? 1.0 : 0.0; - ddRemoteAircraftParts.lightBeacon = newestParts.getLights().isBeaconOn() ? 1.0 : 0.0; - ddRemoteAircraftParts.lightNav = newestParts.getLights().isNavOn() ? 1.0 : 0.0; - ddRemoteAircraftParts.lightLogo = newestParts.getLights().isLogoOn() ? 1.0 : 0.0; - ddRemoteAircraftParts.flapsLeadingEdgeLeftPercent = newestParts.getFlapsPercent() / 100.0; - ddRemoteAircraftParts.flapsLeadingEdgeRightPercent = newestParts.getFlapsPercent() / 100.0; - ddRemoteAircraftParts.flapsTrailingEdgeLeftPercent = newestParts.getFlapsPercent() / 100.0; - ddRemoteAircraftParts.flapsTrailingEdgeRightPercent = newestParts.getFlapsPercent() / 100.0; - ddRemoteAircraftParts.spoilersHandlePosition = newestParts.isSpoilersOut() ? 1.0 : 0.0; - ddRemoteAircraftParts.gearHandlePosition = newestParts.isGearDown() ? 1 : 0; - ddRemoteAircraftParts.engine1Combustion = newestParts.isEngineOn(1) ? 1 : 0; - ddRemoteAircraftParts.engine2Combustion = newestParts.isEngineOn(2) ? 1 : 0;; - ddRemoteAircraftParts.engine3Combustion = newestParts.isEngineOn(3) ? 1 : 0; - ddRemoteAircraftParts.engine4Combustion = newestParts.isEngineOn(4) ? 1 : 0; - } - else - { - // mode is guessing parts - if (this->m_interpolationRequest % 20 != 0) { continue; } // only update every 20th cycle - if (!interpolatorStatus.allTrue()) { continue; } // no position, no really guess possible - - ddRemoteAircraftParts.gearHandlePosition = isOnGround ? 1 : 0; - - // when first detected moving, lights on - if (isOnGround) + double gskmh = interpolatedSituation.getGroundSpeed().value(CSpeedUnit::km_h()); + if (gskmh > 7.5) { + // mode taxi // ddRemoteAircraftParts.lightTaxi = 1.0; - ddRemoteAircraftParts.lightBeacon = 1.0; - ddRemoteAircraftParts.lightNav = 1.0; - - double gskmh = interpolatedSituation.getGroundSpeed().value(CSpeedUnit::km_h()); - if (gskmh > 7.5) - { - // mode taxi - // ddRemoteAircraftParts.lightTaxi = 1.0; - ddRemoteAircraftParts.lightLanding = 0.0; - } - else if (gskmh > 25) - { - // mode accelaration for takeoff - // ddRemoteAircraftParts.lightTaxi = 0.0; - ddRemoteAircraftParts.lightLanding = 1.0; - } - else - { - // slow movements or parking - // ddRemoteAircraftParts.lightTaxi = 0.0; - ddRemoteAircraftParts.lightLanding = 0.0; - } + ddRemoteAircraftParts.lightLanding = 0.0; + } + else if (gskmh > 25) + { + // mode accelaration for takeoff + // ddRemoteAircraftParts.lightTaxi = 0.0; + ddRemoteAircraftParts.lightLanding = 1.0; } else { + // slow movements or parking // ddRemoteAircraftParts.lightTaxi = 0.0; - ddRemoteAircraftParts.lightBeacon = 1.0; - ddRemoteAircraftParts.lightNav = 1.0; - // landing lights for < 10000ft (normally MSL, here ignored) - ddRemoteAircraftParts.lightLanding = (interpolatedSituation.getAltitude().value(CLengthUnit::ft()) < 10000) ? 1.0 : 0; + ddRemoteAircraftParts.lightLanding = 0.0; } } + else + { + // ddRemoteAircraftParts.lightTaxi = 0.0; + ddRemoteAircraftParts.lightBeacon = 1.0; + ddRemoteAircraftParts.lightNav = 1.0; + // landing lights for < 10000ft (normally MSL, here ignored) + ddRemoteAircraftParts.lightLanding = (interpolatedSituation.getAltitude().value(CLengthUnit::ft()) < 10000) ? 1.0 : 0; + } + } - HRESULT hr = S_OK; - hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, - simObj.getObjectId(), 0, 0, - sizeof(DataDefinitionRemoteAircraftParts), &ddRemoteAircraftParts); - if (hr != S_OK) { CLogMessage(this).warning("Failed so set parts on SimObject %1 callsign: %2") << simObj.getObjectId() << callsign; } + Q_ASSERT(m_hSimConnect); + HRESULT hr = S_OK; + hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, + simObj.getObjectId(), 0, 0, + sizeof(DataDefinitionRemoteAircraftParts), &ddRemoteAircraftParts); - } // all callsigns + if (hr != S_OK) { CLogMessage(this).warning("Failed so set parts on SimObject %1 callsign: %2") << simObj.getObjectId() << simObj.getCallsign(); } + return hr == S_OK; } SIMCONNECT_DATA_INITPOSITION CSimulatorFsx::aircraftSituationToFsxInitPosition(const CAircraftSituation &situation) @@ -746,8 +767,8 @@ namespace BlackSimPlugin void CSimulatorFsx::synchronizeTime(const CTime &zuluTimeSim, const CTime &localTimeSim) { - if (!this->m_simTimeSynced) return; - if (!this->isConnected()) return; + if (!this->m_simTimeSynced) { return; } + if (!this->isConnected()) { return; } if (m_syncDeferredCounter > 0) { --m_syncDeferredCounter; @@ -766,7 +787,7 @@ namespace BlackSimPlugin int targetMins = myTime.hour() * 60 + myTime.minute(); int simMins = zuluTimeSim.valueRounded(CTimeUnit::min()); int diffMins = qAbs(targetMins - simMins); - if (diffMins < 2) return; + if (diffMins < 2) { return; } HRESULT hr = S_OK; hr += SimConnect_TransmitClientEvent(m_hSimConnect, 0, EventSetTimeZuluHours, h, SIMCONNECT_GROUP_PRIORITY_STANDARD, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY); hr += SimConnect_TransmitClientEvent(m_hSimConnect, 0, EventSetTimeZuluMinutes, m, SIMCONNECT_GROUP_PRIORITY_STANDARD, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY); diff --git a/src/plugins/simulator/fsx/simulator_fsx.h b/src/plugins/simulator/fsx/simulator_fsx.h index 32d20500f..7411894d4 100644 --- a/src/plugins/simulator/fsx/simulator_fsx.h +++ b/src/plugins/simulator/fsx/simulator_fsx.h @@ -62,7 +62,8 @@ namespace BlackSimPlugin EventSetTimeZuluYear, EventSetTimeZuluDay, EventSetTimeZuluHours, - EventSetTimeZuluMinutes + EventSetTimeZuluMinutes, + EventToggleTaxiLights }; //! FSX Simulator Implementation @@ -165,9 +166,13 @@ namespace BlackSimPlugin //! Initialize SimConnect data definitions HRESULT initDataDefinitionsWhenConnected(); - //! Update other aircrafts + //! Update remote aircraft void updateRemoteAircraft(); + //! Update remote airacraft parts (send to FSX) + bool updateRemoteAircraftParts(const CSimConnectObject &simObj, const BlackMisc::Aviation::CAircraftPartsList &parts, + BlackCore::IInterpolator::PartsStatus partsStatus, const BlackMisc::Aviation::CAircraftSituation &interpolatedSituation, bool isOnGround) const; + //! Format conversion SIMCONNECT_DATA_INITPOSITION aircraftSituationToFsxInitPosition(const BlackMisc::Aviation::CAircraftSituation &situation); @@ -189,6 +194,11 @@ namespace BlackSimPlugin BlackCore::IInterpolator *m_interpolator = nullptr; //!< interpolator instance QHash m_simConnectObjects; QFutureWatcher m_watcherConnect; + + // statistics + qint64 m_statsUpdateAircraftTimeTotal = 0; + qint64 m_statsUpdateAircraftTimeAvg = 0; + int m_statsUpdateAircraftCount = 0; }; } diff --git a/src/plugins/simulator/fsx/simulator_fsx_simconnectproc.cpp b/src/plugins/simulator/fsx/simulator_fsx_simconnectproc.cpp index c22bcc7db..7425867f1 100644 --- a/src/plugins/simulator/fsx/simulator_fsx_simconnectproc.cpp +++ b/src/plugins/simulator/fsx/simulator_fsx_simconnectproc.cpp @@ -146,13 +146,16 @@ namespace BlackSimPlugin case CSimConnectDefinitions::RequestSimEnvironment: { DataDefinitionSimEnvironment *simEnv = (DataDefinitionSimEnvironment *) &pObjData->dwData; - qint32 zh = simEnv->zuluTimeSeconds / 3600; - qint32 zm = (simEnv->zuluTimeSeconds - (zh * 3600)) / 60; - CTime zulu(zh, zm); - qint32 lh = simEnv->localTimeSeconds / 3600; - qint32 lm = (simEnv->localTimeSeconds - (lh * 3600)) / 60; - CTime local(lh, lm); - simulatorFsx->synchronizeTime(zulu, local); + if (simulatorFsx->isTimeSynchronized()) + { + int zh = simEnv->zuluTimeSeconds / 3600; + int zm = (simEnv->zuluTimeSeconds - (zh * 3600)) / 60; + CTime zulu(zh, zm); + int lh = simEnv->localTimeSeconds / 3600; + int lm = (simEnv->localTimeSeconds - (lh * 3600)) / 60; + CTime local(lh, lm); + simulatorFsx->synchronizeTime(zulu, local); + } break; } default: @@ -171,7 +174,7 @@ namespace BlackSimPlugin if (!pFacilityAirport) break; const QString icao(pFacilityAirport->Icao); if (icao.isEmpty()) { continue; } // airfield without ICAO code - if (!CAirportIcao::isValidIcaoDesignator(icao)) continue; // tiny airfields in SIM + if (!CAirportIcao::isValidIcaoDesignator(icao)) { continue; } // tiny airfields in SIM CCoordinateGeodetic pos(pFacilityAirport->Latitude, pFacilityAirport->Longitude, pFacilityAirport->Altitude); CAirport airport(CAirportIcao(icao), pos); CLength d = airport.calculcateDistanceAndBearingToOwnAircraft(posAircraft); diff --git a/tests/blackcore/testinterpolator.cpp b/tests/blackcore/testinterpolator.cpp index 56dbba227..ce1082b25 100644 --- a/tests/blackcore/testinterpolator.cpp +++ b/tests/blackcore/testinterpolator.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 +/* Copyright (C) 2015 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level @@ -25,8 +25,8 @@ namespace BlackCoreTest { QScopedPointer provider(new CRemoteAircraftProviderDummy()); CInterpolatorLinear interpolator(provider.data()); - - const qint64 ts = QDateTime::currentMSecsSinceEpoch(); + interpolator.forceSorting(true); + const qint64 ts = 1425000000000; // QDateTime::currentMSecsSinceEpoch(); const qint64 deltaT = 5000; // ms CCallsign cs("SWIFT"); for (int i = 0; i < IInterpolator::MaxSituationsPerCallsign; i++) @@ -39,11 +39,18 @@ namespace BlackCoreTest provider->insertNewSituation(s); } + for (int i = 0; i < IInterpolator::MaxPartsPerCallsign; i++) + { + CAircraftParts p(getTestParts(cs, i, ts, deltaT)); + provider->insertNewAircraftParts(p); + } + // make sure signals are processed QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); - // check if all situations have been received + // check if all situations / parts have been received QVERIFY2(interpolator.getSituationsForCallsign(cs).size() == IInterpolator::MaxSituationsPerCallsign, "Missing situations"); + QVERIFY2(interpolator.getPartsForCallsign(cs).size() == IInterpolator::MaxPartsPerCallsign, "Missing parts"); // interpolation IInterpolator::InterpolationStatus status; @@ -51,7 +58,7 @@ namespace BlackCoreTest double lngOld = 360.0; for (qint64 currentTime = ts - 2 * deltaT; currentTime < ts; currentTime += 250) { - // This will use range + // This will use time range // from: ts - 2* deltaT - IInterpolator::TimeOffsetMs // to: ts - IInterpolator::TimeOffsetMs CAircraftSituation currentSituation(interpolator.getInterpolatedSituation @@ -114,8 +121,12 @@ namespace BlackCoreTest for (int t = 0; t < IInterpolator::MaxSituationsPerCallsign; t++) { qint64 currentTime = ts - t * deltaT; - CAircraftSituation s(getTestSituation(cs, i++, currentTime, 0)); + CAircraftSituation s(getTestSituation(cs, i, currentTime, 0)); provider->insertNewSituation(s); + + CAircraftParts p(getTestParts(cs, i, currentTime, 0)); + provider->insertNewAircraftParts(p); + i++; } } @@ -123,6 +134,7 @@ namespace BlackCoreTest CCallsignList callsigns(csKeys); QVERIFY(callsigns.size() == callsignsInProvider); QVERIFY(interpolator.getSituationsForCallsign("SWIFT0").size() == IInterpolator::MaxSituationsPerCallsign); + QVERIFY(interpolator.getPartsForCallsign("SWIFT0").size() == IInterpolator::MaxPartsPerCallsign); // interpolation for time, then for each callsign int doneInterpolations = 0; @@ -171,6 +183,21 @@ namespace BlackCoreTest } timeMs = timer.elapsed(); qDebug() << "All callsigns" << doneInterpolations << "interpolations in" << timeMs << "ms"; + + int fetchedParts = 0; + timer.start(); + for (qint64 currentTime = ts - 2 * deltaT; currentTime < ts; currentTime += 250) + { + for (const CCallsign &callsign : callsigns) + { + IInterpolator::PartsStatus status; + CAircraftPartsList pl = interpolator.getAndRemovePartsBeforeTime(callsign, ts, status); + fetchedParts++; + Q_UNUSED(pl); + } + } + timeMs = timer.elapsed(); + qDebug() << "Per callsign" << fetchedParts << "fetched parts in" << timeMs << "ms"; } CAircraftSituation CTestInterpolator::getTestSituation(const CCallsign &callsign, int number, qint64 ts, qint64 deltaT) @@ -189,4 +216,13 @@ namespace BlackCoreTest return s; } + CAircraftParts CTestInterpolator::getTestParts(const CCallsign &callsign, int number, qint64 ts, qint64 deltaT) + { + CAircraftLights l(true, false, true, false, true, false); + CAircraftEngineList e({ CAircraftEngine(0, true), CAircraftEngine(1, false), CAircraftEngine(2, true) }); + CAircraftParts p(callsign, l, true, 20, true, e, false); + p.setMSecsSinceEpoch(ts - deltaT * number); // values in past + return p; + } + } // namespace diff --git a/tests/blackcore/testinterpolator.h b/tests/blackcore/testinterpolator.h index 1a186a174..8fa6e4626 100644 --- a/tests/blackcore/testinterpolator.h +++ b/tests/blackcore/testinterpolator.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 +/* Copyright (C) 2015 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level @@ -9,10 +9,12 @@ //! \file -#ifndef BLACKCORETEST_TESTAVIATIONBASE_H -#define BLACKCORETEST_TESTAVIATIONBASE_H +#ifndef BLACKCORETEST_TESTINTERPOLATOR_H +#define BLACKCORETEST_TESTINTERPOLATOR_H #include "blackmisc/avaircraftsituation.h" +#include "blackmisc/aviation/aircraftparts.h" + #include namespace BlackCoreTest @@ -34,6 +36,10 @@ namespace BlackCoreTest private: //! Test situation for testing static BlackMisc::Aviation::CAircraftSituation getTestSituation(const BlackMisc::Aviation::CCallsign &callsign, int number, qint64 ts, qint64 deltaT); + + //! Test parts + static BlackMisc::Aviation::CAircraftParts getTestParts(const BlackMisc::Aviation::CCallsign &callsign, int number, qint64 ts, qint64 deltaT); + }; } // namespace diff --git a/tests/blacksim/testblacksimmain.cpp b/tests/blacksim/testblacksimmain.cpp index fbbdd59e5..29fc271e3 100644 --- a/tests/blacksim/testblacksimmain.cpp +++ b/tests/blacksim/testblacksimmain.cpp @@ -1,7 +1,11 @@ -/* Copyright (C) 2013 VATSIM Community / contributors - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ #include "testblacksimmain.h" #include "testsimcommon.h" diff --git a/tests/blacksim/testblacksimmain.h b/tests/blacksim/testblacksimmain.h index 9a9d79115..447703883 100644 --- a/tests/blacksim/testblacksimmain.h +++ b/tests/blacksim/testblacksimmain.h @@ -1,7 +1,13 @@ -/* Copyright (C) 2013 VATSIM Community / contributors - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file #ifndef BLACKSIMTEST_TESTMAIN_H #define BLACKSIMTEST_TESTMAIN_H