diff --git a/src/blackcore/simulator.cpp b/src/blackcore/simulator.cpp index 88dc42196..98ba379c0 100644 --- a/src/blackcore/simulator.cpp +++ b/src/blackcore/simulator.cpp @@ -7,14 +7,25 @@ * contained in the LICENSE file. */ +#include "blackcore/db/databaseutils.h" #include "blackcore/simulator.h" +#include "blackcore/webdataservices.h" #include "blackcore/application.h" +#include "blackmisc/directoryutils.h" #include "blackmisc/logmessage.h" #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include using namespace BlackConfig; using namespace BlackMisc; @@ -24,9 +35,21 @@ using namespace BlackMisc::Simulation; using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Network; using namespace BlackMisc::Weather; +using namespace BlackCore::Db; namespace BlackCore { + const CLogCategoryList &ISimulator::getLogCategories() + { + static const CLogCategoryList cats({ CLogCategory::driver(), CLogCategory::plugin() }); + return cats; + } + + ISimulator::~ISimulator() + { + this->safeKillTimer(); + } + ISimulator::SimulatorStatus ISimulator::getSimulatorStatus() const { if (!this->isConnected()) { return ISimulator::Disconnected; } @@ -37,6 +60,208 @@ namespace BlackCore return status; } + bool ISimulator::logicallyRemoveRemoteAircraft(const CCallsign &callsign) + { + // if not restriced, directly change + if (!this->getInterpolationSetupGlobal().isRenderingRestricted()) + { + m_statsPhysicallyAddedAircraft++; + this->callPhysicallyRemoveRemoteAircraft(callsign); + return true; + } + + // will be added with next snapshot onRecalculatedRenderedAircraft + return false; + } + + bool ISimulator::logicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft) + { + Q_ASSERT_X(remoteAircraft.hasModelString(), Q_FUNC_INFO, "Missing model string"); + Q_ASSERT_X(remoteAircraft.hasCallsign(), Q_FUNC_INFO, "Missing callsign"); + + const bool renderingRestricted = this->getInterpolationSetupGlobal().isRenderingRestricted(); + if (this->showDebugLogMessage()) { this->debugLogMessage(Q_FUNC_INFO, QString("Restricted: %1 cs: '%2' enabled: %3").arg(boolToYesNo(renderingRestricted), remoteAircraft.getCallsignAsString(), boolToYesNo(remoteAircraft.isEnabled()))); } + if (!remoteAircraft.isEnabled()) { return false; } + + // if not restriced, directly change + if (!renderingRestricted) { this->callPhysicallyAddRemoteAircraft(remoteAircraft); return true; } + + // restricted -> will be added with next snapshot onRecalculatedRenderedAircraft + return false; + } + + void ISimulator::highlightAircraft(const CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const CTime &displayTime) + { + const CCallsign cs(aircraftToHighlight.getCallsign()); + m_highlightedAircraft.removeByCallsign(cs); + if (enableHighlight) + { + const qint64 deltaT = displayTime.valueRounded(CTimeUnit::ms(), 0); + m_highlightEndTimeMsEpoch = QDateTime::currentMSecsSinceEpoch() + deltaT; + m_highlightedAircraft.push_back(aircraftToHighlight); + } + } + + bool ISimulator::followAircraft(const CCallsign &callsign) + { + Q_UNUSED(callsign); + return false; + } + + void ISimulator::setWeatherActivated(bool activated) + { + m_isWeatherActivated = activated; + if (m_isWeatherActivated) + { + const auto selectedWeatherScenario = m_weatherScenarioSettings.get(); + if (!CWeatherScenario::isRealWeatherScenario(selectedWeatherScenario)) + { + m_lastWeatherPosition = {}; + this->injectWeatherGrid(CWeatherGrid::getByScenario(selectedWeatherScenario)); + } + } + } + + void ISimulator::reloadWeatherSettings() + { + if (!m_isWeatherActivated) { return; } + const auto selectedWeatherScenario = m_weatherScenarioSettings.get(); + if (!CWeatherScenario::isRealWeatherScenario(selectedWeatherScenario)) + { + m_lastWeatherPosition = {}; + this->injectWeatherGrid(CWeatherGrid::getByScenario(selectedWeatherScenario)); + } + } + + void ISimulator::clearAllRemoteAircraftData() + { + // rendering related stuff + m_addAgainAircraftWhenRemoved.clear(); + m_callsignsToBeRendered.clear(); + m_clampedLogMsg.clear(); + m_lastSentParts.clear(); + m_lastSentSituations.clear(); + m_updateRemoteAircraftInProgress = false; + + this->clearInterpolationSetupsPerCallsign(); + this->resetHighlighting(); + this->resetAircraftStatistics(); + } + + void ISimulator::debugLogMessage(const QString &msg) + { + if (!this->showDebugLogMessage()) { return; } + if (msg.isEmpty()) { return; } + const CStatusMessage m = CStatusMessage(this).info("%1") << msg; + emit this->driverMessages(m); + } + + void ISimulator::debugLogMessage(const QString &funcInfo, const QString &msg) + { + if (!this->showDebugLogMessage()) { return; } + if (msg.isEmpty()) { return; } + const CStatusMessage m = CStatusMessage(this).info("%1 %2") << msg << funcInfo; + emit this->driverMessages(m); + } + + bool ISimulator::showDebugLogMessage() const + { + const bool show = this->getInterpolationSetupGlobal().showSimulatorDebugMessages(); + return show; + } + + void ISimulator::resetAircraftFromProvider(const CCallsign &callsign) + { + const CSimulatedAircraft aircraft(this->getAircraftInRangeForCallsign(callsign)); + const bool enabled = aircraft.isEnabled(); + if (enabled) + { + // are we already visible? + if (!this->isPhysicallyRenderedAircraft(callsign)) + { + this->callPhysicallyAddRemoteAircraft(aircraft); // enable/disable + } + } + else + { + this->callPhysicallyRemoveRemoteAircraft(callsign); + } + } + + void ISimulator::clearData(const CCallsign &callsign) + { + m_highlightedAircraft.removeByCallsign(callsign); + m_statsPhysicallyRemovedAircraft++; + m_clampedLogMsg.clear(); + m_lastSentParts.remove(callsign); + m_lastSentSituations.remove(callsign); + m_clampedLogMsg.remove(callsign); + this->removeInterpolationSetupPerCallsign(callsign); + } + + void ISimulator::reset() + { + this->clearAllRemoteAircraftData(); + } + + void ISimulator::resetHighlighting() + { + m_highlightedAircraft.clear(); + m_blinkCycle = false; + m_highlightEndTimeMsEpoch = false; + } + + void ISimulator::stopHighlighting() + { + // restore + const CSimulatedAircraftList highlightedAircraft(m_highlightedAircraft); + for (const CSimulatedAircraft &aircraft : highlightedAircraft) + { + // get the current state for this aircraft + // it might has been removed in the meantime + const CCallsign cs(aircraft.getCallsign()); + this->resetAircraftFromProvider(cs); + } + this->resetHighlighting(); + } + + void ISimulator::oneSecondTimerTimeout() + { + this->blinkHighlightedAircraft(); + } + + void ISimulator::safeKillTimer() + { + if (m_timerId < 0) { return; } + this->killTimer(m_timerId); + m_timerId = -1; + } + + void ISimulator::injectWeatherGrid(const CWeatherGrid &weatherGrid) + { + Q_UNUSED(weatherGrid); + } + + void ISimulator::blinkHighlightedAircraft() + { + if (m_highlightedAircraft.isEmpty() || m_highlightEndTimeMsEpoch < 1) { return; } + if (this->isShuttingDown()) { return; } + m_blinkCycle = !m_blinkCycle; + + if (QDateTime::currentMSecsSinceEpoch() > m_highlightEndTimeMsEpoch) + { + this->stopHighlighting(); + return; + } + + // blink mode, toggle aircraft + for (const CSimulatedAircraft &aircraft : as_const(m_highlightedAircraft)) + { + if (m_blinkCycle) { this->callPhysicallyRemoveRemoteAircraft(aircraft.getCallsign(), true); } + else { this->callPhysicallyAddRemoteAircraft(aircraft); } + } + } + CInterpolationAndRenderingSetupPerCallsign ISimulator::getInterpolationSetupConsolidated(const CCallsign &callsign) const { CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupPerCallsignOrDefault(callsign); @@ -62,11 +287,205 @@ namespace BlackCore emit this->receivedRequestedElevation(plane, callsign); } + void ISimulator::resetAircraftStatistics() + { + m_statsUpdateAircraftRuns = 0; + m_statsUpdateAircraftTimeAvgMs = 0; + m_statsUpdateAircraftTimeTotalMs = 0; + m_statsMaxUpdateTimeMs = 0; + m_statsCurrentUpdateTimeMs = 0; + m_statsPhysicallyAddedAircraft = 0; + m_statsPhysicallyRemovedAircraft = 0; + m_statsLastUpdateAircraftRequestedMs = 0; + m_statsUpdateAircraftRequestedDeltaMs = 0; + m_statsUpdateAircraftLimited = 0; + ISimulationEnvironmentProvider::resetSimulationEnvironmentStatistics(); + } + + bool ISimulator::parseCommandLine(const QString &commandLine, const CIdentifier &originator) + { + if (this->isMyIdentifier(originator)) { return false; } + if (this->isShuttingDown()) { return false; } + + if (commandLine.isEmpty()) { return false; } + CSimpleCommandParser parser({ ".plugin", ".drv", ".driver" }); + parser.parse(commandLine); + if (!parser.isKnownCommand()) { return false; } + + // .plugin unload + if (parser.matchesPart(1, "unload")) { this->unload(); return true; } + + // .plugin log interpolator + const QString part1(parser.part(1).toLower().trimmed()); + if (part1.startsWith("logint") && parser.hasPart(2)) + { + const QString part2 = parser.part(2).toLower(); + if (part2 == "off" || part2 == "false") + { + CStatusMessage(this).info("Disabled interpolation logging"); + this->clearInterpolationLogCallsigns(); + return true; + } + if (part2 == "clear" || part2 == "clr") + { + m_interpolationLogger.clearLog(); + CStatusMessage(this).info("Cleared interpolation logging"); + this->clearInterpolationLogCallsigns(); + return true; + } + if (part2.startsWith("max")) + { + if (!parser.hasPart(3)) { return false; } + bool ok; + const int max = parser.part(3).toInt(&ok); + if (!ok) { return false; } + m_interpolationLogger.setMaxSituations(max); + CStatusMessage(this).info("Max.situations logged: %1") << max; + return true; + } + if (part2 == "write" || part2 == "save") + { + // stop logging of other log + this->clearInterpolationLogCallsigns(); + + // write + m_interpolationLogger.writeLogInBackground(); + CLogMessage(this).info("Started writing interpolation log"); + return true; + } + if (part2 == "show") + { + const QDir dir(CInterpolationLogger::getLogDirectory()); + if (CDirectoryUtils::isDirExisting(dir)) + { + const QUrl dirUrl = QUrl::fromLocalFile(dir.absolutePath()); + QDesktopServices::openUrl(dirUrl); // show dir in browser + } + else + { + CLogMessage(this).warning("No interpolation log directory"); + } + return true; + } + + const CCallsign cs(part2.toUpper()); + if (!cs.isValid()) { return false; } + if (this->getAircraftInRangeCallsigns().contains(cs)) + { + CLogMessage(this).info("Will log interpolation for '%1'") << cs.asString(); + this->setLogCallsign(true, cs); + return true; + } + else + { + CLogMessage(this).warning("Cannot log interpolation for '%1', no aircraft in range") << cs.asString(); + return false; + } + } // logint + + if (part1.startsWith("spline") || part1.startsWith("linear")) + { + if (parser.hasPart(2)) + { + const CCallsign cs(parser.part(2)); + const bool changed = this->setInterpolationMode(part1, cs); + CLogMessage(this).info(changed ? "Changed interpolation mode for '%1'" : "Unchanged interpolation mode for '%1'") << cs.asString(); + return true; + } + else + { + CInterpolationAndRenderingSetupGlobal setup = this->getInterpolationSetupGlobal(); + const bool changed = setup.setInterpolatorMode(part1); + if (changed) { this->setInterpolationSetupGlobal(setup); } + CLogMessage(this).info(changed ? "Changed interpolation mode globally" : "Unchanged interpolation mode"); + return true; + } + } // spline/linear + + if (part1.startsWith("pos")) + { + CCallsign cs(parser.part(2).toUpper()); + if (!cs.isValid()) + { + const CCallsignSet csSet = this->getLogCallsigns(); + if (csSet.size() != 1) { return false; } + + // if there is just one we take that one + cs = *csSet.begin(); + } + + this->setLogCallsign(true, cs); + CLogMessage(this).info("Display position for '%1'") << cs.asString(); + this->displayLoggedSituationInSimulator(cs, true); + return true; + } + + if (parser.hasPart(2) && (part1.startsWith("aircraft") || part1.startsWith("ac"))) + { + const QString part2 = parser.part(2).toLower(); + if (parser.hasPart(3) && (part2.startsWith("readd") || part2.startsWith("re-add"))) + { + const QString cs = parser.part(3).toUpper(); + if (cs == "all") + { + this->physicallyRemoveAllRemoteAircraft(); + const CStatusMessageList msgs = this->debugVerifyStateAfterAllAircraftRemoved(); + this->clearAllRemoteAircraftData(); + if (!msgs.isEmpty()) { emit this->driverMessages(msgs); } + const CSimulatedAircraftList aircraft = this->getAircraftInRange(); + for (const CSimulatedAircraft a : aircraft) + { + if (a.isEnabled()) { this->logicallyAddRemoteAircraft(a); } + } + } + else if (CCallsign::isValidAircraftCallsign(cs)) + { + this->logicallyReAddRemoteAircraft(cs); + return true; + } + return false; + } + if (parser.hasPart(3) && (part2.startsWith("rm") || part2.startsWith("remove"))) + { + const QString cs = parser.part(3).toUpper(); + if (CCallsign::isValidAircraftCallsign(cs)) + { + this->logicallyRemoveRemoteAircraft(cs); + } + } + + return false; + } + + if (part1.startsWith("limit")) + { + const int perSecond = parser.toInt(2, -1); + this->limitToUpdatesPerSecond(perSecond); + CLogMessage(this).info("Remote aircraft updates limitations: %1") << this->updateAircraftLimitationInfo(); + return true; + } + + // driver specific cmd line arguments + return this->parseDetails(parser); + } + void ISimulator::registerHelp() { if (CSimpleCommandParser::registered("BlackCore::ISimulator")) { return; } CSimpleCommandParser::registerCommand({".drv", "alias: .driver .plugin"}); CSimpleCommandParser::registerCommand({".drv unload", "unload driver"}); + CSimpleCommandParser::registerCommand({".drv limit number/secs.", "limit updates to number per second (0..off)"}); + CSimpleCommandParser::registerCommand({".drv logint callsign", "log interpolator for callsign"}); + CSimpleCommandParser::registerCommand({".drv logint off", "no log information for interpolator"}); + CSimpleCommandParser::registerCommand({".drv logint write", "write interpolator log to file"}); + CSimpleCommandParser::registerCommand({".drv logint clear", "clear current log"}); + CSimpleCommandParser::registerCommand({".drv logint max number", "max. number of entries logged"}); + CSimpleCommandParser::registerCommand({".drv pos callsign", "show position for callsign"}); + CSimpleCommandParser::registerCommand({".drv spline|linear callsign", "set spline/linear interpolator for one/all callsign(s)"}); + CSimpleCommandParser::registerCommand({".drv aircraft readd callsign", "add again (re-add) a given callsign"}); + CSimpleCommandParser::registerCommand({".drv aircraft readd all", "add again (re-add) all aircraft"}); + CSimpleCommandParser::registerCommand({".drv aircraft rm callsign", "remove a given callsign"}); + if (CBuildConfig::isCompiledWithFsuipcSupport()) { CSimpleCommandParser::registerCommand({".drv fsuipc on|off", "enable/disable FSUIPC (if applicable)"}); @@ -84,6 +503,44 @@ namespace BlackCore return s.join(", "); } + bool ISimulator::isEqualLastSent(const CAircraftSituation &compare) const + { + Q_ASSERT_X(compare.hasCallsign(), Q_FUNC_INFO, "Need callsign"); + if (!m_lastSentSituations.contains(compare.getCallsign())) { return false; } + if (compare.isNull()) { return false; } + return compare.equalPbhVectorAltitude(m_lastSentSituations.value(compare.getCallsign())); + } + + bool ISimulator::isEqualLastSent(const CAircraftParts &compare, const CCallsign &callsign) const + { + if (callsign.isEmpty()) { return false; } + if (!m_lastSentParts.contains(callsign)) { return false; } + return compare.equalValues(m_lastSentParts.value(callsign)); + } + + void ISimulator::rememberLastSent(const CAircraftSituation &sent) + { + Q_ASSERT_X(sent.hasCallsign(), Q_FUNC_INFO, "Need callsign"); + m_lastSentSituations.insert(sent.getCallsign(), sent); + } + + void ISimulator::rememberLastSent(const CAircraftParts &sent, const CCallsign &callsign) + { + Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign"); + m_lastSentParts.insert(callsign, sent); + } + + CAircraftSituationList ISimulator::getLastSentCanLikelySkipNearGroundInterpolation() const + { + const QList situations = m_lastSentSituations.values(); + CAircraftSituationList skipped; + for (const CAircraftSituation &s : situations) + { + if (s.canLikelySkipNearGroundInterpolation()) { skipped.push_back(s); } + } + return skipped; + } + bool ISimulator::isAnyConnectedStatus(SimulatorStatus status) { return (status.testFlag(Connected) || status.testFlag(Simulating) || status.testFlag(Paused)); @@ -104,7 +561,144 @@ namespace BlackCore IInterpolationSetupProvider(), CIdentifiable(this) { + this->setObjectName("Simulator: " + pluginInfo.getIdentifier()); ISimulator::registerHelp(); + + // provider signals, hook up with remote aircraft provider + m_remoteAircraftProviderConnections.append( + CRemoteAircraftAware::provider()->connectRemoteAircraftProviderSignals( + this, // receiver must match object in bind + nullptr, + nullptr, + std::bind(&ISimulator::rapOnRemoteProviderRemovedAircraft, this, std::placeholders::_1), + std::bind(&ISimulator::rapOnRecalculatedRenderedAircraft, this, std::placeholders::_1)) + ); + + // timer + connect(&m_oneSecondTimer, &QTimer::timeout, this, &ISimulator::oneSecondTimerTimeout); + m_oneSecondTimer.setObjectName(this->objectName().append(":m_oneSecondTimer")); + m_oneSecondTimer.start(1000); + + // swift data + if (sApp && sApp->hasWebDataServices()) + { + connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbAllDataRead, this, &ISimulator::onSwiftDbAllDataRead, Qt::QueuedConnection); + connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbAirportsRead, this, &ISimulator::onSwiftDbAirportsRead, Qt::QueuedConnection); + connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbModelMatchingEntitiesRead, this, &ISimulator::onSwiftDbModelMatchingEntitiesRead, Qt::QueuedConnection); + } + + connect(sApp, &CApplication::aboutToShutdown, this, &ISimulator::unload, Qt::QueuedConnection); + + // info + CLogMessage(this).info("Initialized simulator driver: '%1'") << this->getSimulatorInfo().toQString(); + } + + void ISimulator::onRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot) + { + if (!snapshot.isValidSnapshot()) { return;} + + // for unrestricted values all add/remove actions are directly linked + // when changing back from restricted->unrestricted an one time update is required + if (!snapshot.isRestricted() && !snapshot.isRestrictionChanged()) { return; } + + Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Needs to run in object thread"); + Q_ASSERT_X(snapshot.generatingThreadName() != QThread::currentThread()->objectName(), Q_FUNC_INFO, "Expect snapshot from background thread"); + + // restricted snapshot values? + bool changed = false; + if (snapshot.isRenderingEnabled()) + { + const CCallsignSet callsignsInSimulator(this->physicallyRenderedAircraft()); // state in simulator + const CCallsignSet callsignsToBeRemoved(callsignsInSimulator.difference(snapshot.getEnabledAircraftCallsignsByDistance())); + const CCallsignSet callsignsToBeAdded(snapshot.getEnabledAircraftCallsignsByDistance().difference(callsignsInSimulator)); + if (!callsignsToBeRemoved.isEmpty()) + { + const int r = this->physicallyRemoveMultipleRemoteAircraft(callsignsToBeRemoved); + changed = r > 0; + } + + if (!callsignsToBeAdded.isEmpty()) + { + CSimulatedAircraftList aircraftToBeAdded(this->getAircraftInRange().findByCallsigns(callsignsToBeAdded)); // thread safe copy + for (const CSimulatedAircraft &aircraft : aircraftToBeAdded) + { + Q_ASSERT_X(aircraft.isEnabled(), Q_FUNC_INFO, "Disabled aircraft detected as to be added"); + Q_ASSERT_X(aircraft.hasModelString(), Q_FUNC_INFO, "Missing model string"); + this->callPhysicallyAddRemoteAircraft(aircraft); // recalcuate snapshot + changed = true; + } + } + } + else + { + // no rendering at all, we remove everything + const int r = this->physicallyRemoveAllRemoteAircraft(); + changed = r > 0; + } + + // we have handled snapshot + if (changed) + { + emit this->airspaceSnapshotHandled(); + } + } + + int ISimulator::physicallyRemoveMultipleRemoteAircraft(const CCallsignSet &callsigns) + { + if (callsigns.isEmpty()) { return 0; } + this->stopHighlighting(); + int removed = 0; + for (const CCallsign &callsign : callsigns) + { + this->callPhysicallyRemoveRemoteAircraft(callsign); + removed++; + } + return removed; + } + + int ISimulator::physicallyRemoveAllRemoteAircraft() + { + // a default implementation, but normally overridden by the sims + const CCallsignSet callsigns = this->getAircraftInRangeCallsigns(); + const int r = this->physicallyRemoveMultipleRemoteAircraft(callsigns); + this->debugVerifyStateAfterAllAircraftRemoved(); + this->clearAllRemoteAircraftData(); + return r; + } + + CAirportList ISimulator::getWebServiceAirports() const + { + if (this->isShuttingDown()) { return CAirportList(); } + if (!sApp->hasWebDataServices()) { return CAirportList(); } + return sApp->getWebDataServices()->getAirports(); + } + + CAirport ISimulator::getWebServiceAirport(const CAirportIcaoCode &icao) const + { + if (this->isShuttingDown()) { return CAirport(); } + if (!sApp->hasWebDataServices()) { return CAirport(); } + return sApp->getWebDataServices()->getAirports().findFirstByIcao(icao); + } + + int ISimulator::maxAirportsInRange() const + { + // might change in future or become a setting or such + return 20; + } + + void ISimulator::onSwiftDbAllDataRead() + { + // void, can be overridden in specialized drivers + } + + void ISimulator::onSwiftDbModelMatchingEntitiesRead() + { + // void, can be overridden in specialized drivers + } + + void ISimulator::onSwiftDbAirportsRead() + { + // void, can be overridden in specialized drivers } void ISimulator::rememberElevationAndCG(const CCallsign &callsign, const QString &modelString, const Geo::CElevationPlane &elevation, const CLength &cg) @@ -145,7 +739,6 @@ namespace BlackCore }); } - bool ISimulator::setInterpolationSetupGlobal(const CInterpolationAndRenderingSetupGlobal &setup) { if (!IInterpolationSetupProvider::setInterpolationSetupGlobal(setup)) { return false; } @@ -156,6 +749,322 @@ namespace BlackCore return true; } + CAirportList ISimulator::getAirportsInRange() const + { + // default implementation + if (this->isShuttingDown()) { return CAirportList(); } + if (!sApp || !sApp->hasWebDataServices()) { return CAirportList(); } + + const CAirportList airports = sApp->getWebDataServices()->getAirports(); + if (airports.isEmpty()) { return airports; } + const CCoordinateGeodetic ownPosition = this->getOwnAircraftPosition(); + CAirportList airportInRange = airports.findClosest(maxAirportsInRange(), ownPosition); + if (m_autoCalcAirportDistance) { airportInRange.calculcateAndUpdateRelativeDistanceAndBearing(ownPosition); } + return airportInRange; + } + + CAircraftModel ISimulator::reverseLookupModel(const CAircraftModel &model) + { + bool modified = false; + const CAircraftModel reverseModel = CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified); + return reverseModel; + } + + bool ISimulator::isUpdateAircraftLimited(qint64 timestamp) + { + if (!m_limitUpdateAircraft) { return false; } + const bool hasToken = m_limitUpdateAircraftBucket.tryConsume(1, timestamp); + return !hasToken; + } + + bool ISimulator::isUpdateAircraftLimitedWithStats(qint64 startTime) + { + const bool limited = this->isUpdateAircraftLimited(startTime); + this->setStatsRemoteAircraftUpdate(startTime, limited); + return limited; + } + + bool ISimulator::limitToUpdatesPerSecond(int numberPerSecond) + { + if (numberPerSecond < 1) + { + m_limitUpdateAircraft = false; + return false; + } + + int tokens = 0.1 * numberPerSecond; // 100ms + do + { + if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(100); break; } + tokens = 0.25 * numberPerSecond; // 250ms + if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(250); break; } + tokens = 0.5 * numberPerSecond; // 500ms + if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(500); break; } + tokens = numberPerSecond; + m_limitUpdateAircraftBucket.setInterval(1000); + } + while (false); + + m_limitUpdateAircraftBucket.setCapacityAndTokensToRefill(tokens); + m_limitUpdateAircraft = true; + return true; + } + + QString ISimulator::updateAircraftLimitationInfo() const + { + if (!m_limitUpdateAircraft) { return QStringLiteral("not limited"); } + static const QString limInfo("Limited %1 times with %2/secs."); + return limInfo.arg(m_statsUpdateAircraftLimited).arg(m_limitUpdateAircraftBucket.getTokensPerSecond()); + } + + void ISimulator::resetLastSentValues() + { + m_lastSentParts.clear(); + m_lastSentSituations.clear(); + } + + void ISimulator::resetLastSentValues(const CCallsign &callsign) + { + m_lastSentParts.remove(callsign); + m_lastSentSituations.remove(callsign); + } + + void ISimulator::unload() + { + this->disconnectFrom(); // disconnect from simulator + m_remoteAircraftProviderConnections.disconnectAll(); // disconnect signals from provider + } + + bool ISimulator::disconnectFrom() + { + // supposed to be overridden + return true; + } + + bool ISimulator::logicallyReAddRemoteAircraft(const CCallsign &callsign) + { + if (this->isShuttingDown()) { return false; } + if (callsign.isEmpty()) { return false; } + this->stopHighlighting(); + this->logicallyRemoveRemoteAircraft(callsign); + if (!this->isAircraftInRange(callsign)) { return false; } + const QPointer myself(this); + QTimer::singleShot(2500, this, [ = ] + { + if (myself.isNull()) { return; } + if (this->isShuttingDown()) { return; } + if (!this->isAircraftInRange(callsign)) { return; } + const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign); + if (aircraft.isEnabled() && aircraft.hasModelString()) + { + this->logicallyAddRemoteAircraft(aircraft); + } + }); + return true; + } + + CCallsignSet ISimulator::unrenderedEnabledAircraft() const + { + const CSimulatedAircraftList aircraft = this->getAircraftInRange().findByEnabled(true); + if (aircraft.isEmpty()) { return CCallsignSet(); } + CCallsignSet enabledOnes = aircraft.getCallsigns(); + const CCallsignSet renderedOnes = this->physicallyRenderedAircraft(); + enabledOnes.remove(renderedOnes); + return enabledOnes; + } + + CCallsignSet ISimulator::renderedDisabledAircraft() const + { + const CSimulatedAircraftList aircraft = this->getAircraftInRange().findByEnabled(false); + if (aircraft.isEmpty()) { return CCallsignSet(); } + const CCallsignSet disabledOnes = aircraft.getCallsigns(); + const CCallsignSet renderedOnes = this->physicallyRenderedAircraft(); + return renderedOnes.intersection(disabledOnes); + } + + bool ISimulator::changeRemoteAircraftEnabled(const CSimulatedAircraft &aircraft) + { + if (this->isShuttingDown()) { return false; } + return aircraft.isEnabled() ? + this->physicallyAddRemoteAircraft(aircraft) : + this->physicallyRemoveRemoteAircraft(aircraft.getCallsign()); + } + + bool ISimulator::changeRemoteAircraftModel(const CSimulatedAircraft &aircraft) + { + // we expect the new model "in aircraft" + // remove upfront, and then enable / disable again + if (this->isShuttingDown()) { return false; } + const CCallsign callsign = aircraft.getCallsign(); + if (!this->isPhysicallyRenderedAircraft(callsign)) { return false; } + this->physicallyRemoveRemoteAircraft(callsign); + return this->changeRemoteAircraftEnabled(aircraft); + } + + CStatusMessageList ISimulator::debugVerifyStateAfterAllAircraftRemoved() const + { + CStatusMessageList msgs; + if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return msgs; } + if (!m_addAgainAircraftWhenRemoved.isEmpty()) { msgs.push_back(CStatusMessage(this).error("m_addAgainAircraftWhenRemoved not empty: '%1'") << m_addAgainAircraftWhenRemoved.getCallsignStrings(true).join(", ")); } + return msgs; + } + + QString ISimulator::getInvalidSituationLogMessage(const CCallsign &callsign, const CInterpolationStatus &status, const QString &details) const + { + static const QString msg("CS: '%1' Interpolation: '%2'"); + const QString m = msg.arg(callsign.asString(), status.toQString()); + if (details.isEmpty()) { return m; } + + static const QString addDetails(" details: '%1'"); + return m + addDetails.arg(details); + } + + bool ISimulator::clampedLog(const CCallsign &callsign, const CStatusMessage &message) + { + if (message.isEmpty()) { return false; } + constexpr qint64 Timeout = 2000; + const qint64 clampTs = m_clampedLogMsg.value(callsign, -1); + const qint64 ts = QDateTime::currentMSecsSinceEpoch(); + if (clampTs > 0 && ((clampTs + Timeout) > ts)) { return false; } + CLogMessage::preformatted(message); + m_clampedLogMsg[callsign] = ts; + return true; + } + + void ISimulator::removedClampedLog(const CCallsign &callsign) + { + m_clampedLogMsg.remove(callsign); + } + + void ISimulator::setStatsRemoteAircraftUpdate(qint64 startTime, bool limited) + { + const qint64 now = QDateTime::currentMSecsSinceEpoch(); + const qint64 dt = now - startTime; + m_statsCurrentUpdateTimeMs = dt; + m_statsUpdateAircraftTimeTotalMs += dt; + m_statsUpdateAircraftRuns++; + m_statsUpdateAircraftTimeAvgMs = static_cast(m_statsUpdateAircraftTimeTotalMs) / static_cast(m_statsUpdateAircraftRuns); + m_updateRemoteAircraftInProgress = false; + m_statsLastUpdateAircraftRequestedMs = startTime; + + if (m_statsMaxUpdateTimeMs < dt) { m_statsMaxUpdateTimeMs = dt; } + if (m_statsLastUpdateAircraftRequestedMs > 0) { m_statsUpdateAircraftRequestedDeltaMs = startTime - m_statsLastUpdateAircraftRequestedMs; } + if (limited) { m_statsUpdateAircraftLimited++; } + } + + QString ISimulator::latestLoggedDataFormatted(const CCallsign &cs) const + { + const SituationLog s = m_interpolationLogger.getLastSituationLog(cs); + const PartsLog p = m_interpolationLogger.getLastPartsLog(cs); + + static const QString sep("\n------\n"); + QString dm; + if (s.tsCurrent > 0) + { + dm = QStringLiteral("Setup: ") % s.usedSetup.toQString(true) % + QStringLiteral("\n\n") % + QStringLiteral("Situation: ") % s.toQString(false, true, true, true, true, sep); + } + if (p.tsCurrent > 0) { dm += (dm.isEmpty() ? QStringLiteral("") : QStringLiteral("\n\n")) % QStringLiteral("Parts: ") % p.toQString(sep); } + return dm; + } + + void ISimulator::rapOnRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot) + { + if (!this->isConnected()) { return; } + if (this->isShuttingDown()) { return; } + this->onRecalculatedRenderedAircraft(snapshot); + } + + void ISimulator::rapOnRemoteProviderRemovedAircraft(const CCallsign &callsign) + { + Q_UNUSED(callsign); + // currently not used, the calls are handled by context call logicallyRemoveRemoteAircraft + } + + void ISimulator::callPhysicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft) + { + m_statsPhysicallyAddedAircraft++; + this->physicallyAddRemoteAircraft(remoteAircraft); + } + + void ISimulator::callPhysicallyRemoveRemoteAircraft(const CCallsign &remoteCallsign, bool blinking) + { + if (!blinking) { this->clearData(remoteCallsign); } + this->physicallyRemoveRemoteAircraft(remoteCallsign); + } + + void ISimulator::displayLoggedSituationInSimulator(const CCallsign &cs, bool stopLogging, int times) + { + if (cs.isEmpty()) { return; } + if (this->isShuttingDown()) { return; } + const CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupPerCallsignOrDefault(cs); + const bool logsCs = setup.logInterpolation(); + if (!logsCs) { return; } + + stopLogging = stopLogging || !this->isSimulating(); // stop when sim was stopped + stopLogging = stopLogging && logsCs; + if (!stopLogging && times < 1) { return; } + + const bool inRange = this->getAircraftInRangeCallsigns().contains(cs); + if (!stopLogging && !inRange) { return; } + if (stopLogging && (times < 1 || !inRange)) + { + this->setLogCallsign(false, cs); + return; + } + + const QString dm = this->latestLoggedDataFormatted(cs); + if (!dm.isEmpty()) + { + this->displayStatusMessage(CStatusMessage(this).info(dm)); + emit this->requestUiConsoleMessage(dm, true); + } + + const int t = 4500 + (qrand() % 1000); // makes sure not always using the same time difference + const QPointer myself(this); + QTimer::singleShot(t, this, [ = ] + { + if (myself.isNull() || myself->isShuttingDown()) { return; } + this->displayLoggedSituationInSimulator(cs, stopLogging, times - 1); + }); + } + + void ISimulator::reverseLookupAndUpdateOwnAircraftModel(const QString &modelString) + { + CAircraftModel model = getOwnAircraftModel(); + model.setModelString(modelString); + this->reverseLookupAndUpdateOwnAircraftModel(model); + } + + void ISimulator::reverseLookupAndUpdateOwnAircraftModel(const BlackMisc::Simulation::CAircraftModel &model) + { + Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp"); + Q_ASSERT_X(sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing web services"); + + if (!model.hasModelString()) { return; } + if (this->getOwnAircraftModel() != model) + { + if (CDatabaseUtils::hasDbAircraftData()) + { + const CAircraftModel newModel = this->reverseLookupModel(model); + const bool updated = this->updateOwnModel(newModel); // update in provider (normally the context) + if (updated) + { + emit this->ownAircraftModelChanged(this->getOwnAircraftModel()); + } + } + else + { + // we wait for the data + connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbModelMatchingEntitiesRead, this, [ = ] + { + this->reverseLookupAndUpdateOwnAircraftModel(model); + }); + } + } + } + ISimulatorListener::ISimulatorListener(const CSimulatorPluginInfo &info) : QObject(), m_info(info) { diff --git a/src/blackcore/simulator.h b/src/blackcore/simulator.h index a11d220a4..a1f1d37a6 100644 --- a/src/blackcore/simulator.h +++ b/src/blackcore/simulator.h @@ -14,6 +14,7 @@ #include "blackcore/application.h" #include "blackcore/blackcoreexport.h" +#include "blackmisc/simulation/settings/simulatorsettings.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/simulation/simulatorplugininfo.h" @@ -35,6 +36,7 @@ #include "blackmisc/identifier.h" #include "blackmisc/pixmap.h" #include "blackmisc/simplecommandparser.h" +#include "blackmisc/tokenbucket.h" #include "blackconfig/buildconfig.h" #include @@ -77,11 +79,14 @@ namespace BlackCore Q_DECLARE_FLAGS(SimulatorStatus, SimulatorStatusFlag) Q_FLAG(SimulatorStatus) + //! Log categories + static const BlackMisc::CLogCategoryList &getLogCategories(); + //! Render all aircraft if number of aircraft >= MaxAircraftInfinite const int MaxAircraftInfinite = 100; //! Destructor - virtual ~ISimulator() {} + virtual ~ISimulator(); //! Combined status virtual SimulatorStatus getSimulatorStatus() const; @@ -90,7 +95,7 @@ namespace BlackCore virtual bool isTimeSynchronized() const = 0; //! Get the setup (simulator environment) - virtual const BlackMisc::Simulation::CSimulatorInternals &getSimulatorInternals() const = 0; + virtual const BlackMisc::Simulation::CSimulatorInternals &getSimulatorInternals() const { return m_simulatorInternals; } //! Connect to simulator virtual bool connectTo() = 0; @@ -98,31 +103,31 @@ namespace BlackCore //! Disconnect from simulator virtual bool disconnectFrom() = 0; - //! Logically add a new aircraft. Depending on max. aircraft, enabled status etc. - //! it will physically added to the simulator. + //! Logically add a new aircraft. + //! Depending on max. aircraft, enabled status etc. it will physically added to the simulator. //! \sa physicallyAddRemoteAircraft - virtual bool logicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft) = 0; + virtual bool logicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft); - //! Logically remove remote aircraft from simulator. Depending on max. aircraft, enabled status etc. - //! it will physically added to the simulator. - virtual bool logicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) = 0; + //! Logically remove remote aircraft from simulator. + //! Depending on max. aircraft, enabled status etc. it will physically added to the simulator. + virtual bool logicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign); //! Removes and adds again the aircraft //! \sa logicallyRemoveRemoteAircraft //! \sa logicallyAddRemoteAircraft - virtual bool logicallyReAddRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) = 0; + virtual bool logicallyReAddRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign); //! Find the unrendered enabled aircraft - virtual BlackMisc::Aviation::CCallsignSet unrenderedEnabledAircraft() const = 0; + virtual BlackMisc::Aviation::CCallsignSet unrenderedEnabledAircraft() const; //! Find the rendered disabled aircraft - virtual BlackMisc::Aviation::CCallsignSet renderedDisabledAircraft() const = 0; + virtual BlackMisc::Aviation::CCallsignSet renderedDisabledAircraft() const; //! Change remote aircraft per property - virtual bool changeRemoteAircraftModel(const BlackMisc::Simulation::CSimulatedAircraft &aircraft) = 0; + virtual bool changeRemoteAircraftModel(const BlackMisc::Simulation::CSimulatedAircraft &aircraft); //! Aircraft got enabled / disabled - virtual bool changeRemoteAircraftEnabled(const BlackMisc::Simulation::CSimulatedAircraft &aircraft) = 0; + virtual bool changeRemoteAircraftEnabled(const BlackMisc::Simulation::CSimulatedAircraft &aircraft); //! Update own aircraft cockpit (usually from context) virtual bool updateOwnSimulatorCockpit(const BlackMisc::Simulation::CSimulatedAircraft &aircraft, const BlackMisc::CIdentifier &originator) = 0; @@ -137,7 +142,7 @@ namespace BlackCore virtual void displayTextMessage(const BlackMisc::Network::CTextMessage &message) const = 0; //! Airports in range from simulator, or if not available from web service - virtual BlackMisc::Aviation::CAirportList getAirportsInRange() const = 0; + virtual BlackMisc::Aviation::CAirportList getAirportsInRange() const; //! Set time synchronization between simulator and user's computer time //! \remarks not all drivers implement this, e.g. if it is an intrinsic simulator feature @@ -155,20 +160,19 @@ namespace BlackCore virtual BlackMisc::Aviation::CCallsignSet physicallyRenderedAircraft() const = 0; //! Highlight the aircraft for given time (or disable highlight) - virtual void highlightAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const BlackMisc::PhysicalQuantities::CTime &displayTime) = 0; + virtual void highlightAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const BlackMisc::PhysicalQuantities::CTime &displayTime); //! Follow aircraft - virtual bool followAircraft(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_UNUSED(callsign); - return false; - } + virtual bool followAircraft(const BlackMisc::Aviation::CCallsign &callsign); //! Activates or deactivates simulator weather - virtual void setWeatherActivated(bool activated) = 0; + virtual void setWeatherActivated(bool activated); + + //! Reload weather settings + void reloadWeatherSettings(); //! Driver will be unloaded - virtual void unload() = 0; + virtual void unload(); //! Are we connected to the simulator? virtual bool isConnected() const = 0; @@ -180,11 +184,11 @@ namespace BlackCore virtual bool isSimulating() const { return this->isConnected(); } //! Clear all aircraft related data - virtual void clearAllRemoteAircraftData() = 0; + virtual void clearAllRemoteAircraftData(); //! Debug function to check state after all aircraft have been removed //! \remarks only in local developer builds - virtual BlackMisc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const = 0; + virtual BlackMisc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const; //! Is overall (swift) application shutting down //! \threadsafe @@ -206,7 +210,7 @@ namespace BlackCore virtual QString getStatisticsSimulatorSpecific() const { return QString(); } //! Reset the statistics - virtual void resetAircraftStatistics() {} + virtual void resetAircraftStatistics(); //! \copydoc BlackMisc::IProvider::asQObject virtual QObject *asQObject() override { return this; } @@ -214,12 +218,23 @@ namespace BlackCore //! \addtogroup swiftdotcommands //! @{ //!
-        //! .drv    unload                  unload driver                           BlackCore::CSimulatorCommon
-        //! .drv    fsuipc      on|off      enable/disable FSUIPC (if applicable)   BlackSimPlugin::FsCommon::CSimulatorFsCommon
+        //! .drv unload                    unload plugin                           BlackCore::ISimulator
+        //! .drv limit number              limit the number of updates             BlackCore::ISimulator
+        //! .drv logint callsign           log interpolator for callsign           BlackCore::ISimulator
+        //! .drv logint off                no log information for interpolator     BlackCore::ISimulator
+        //! .drv logint write              write interpolator log to file          BlackCore::ISimulator
+        //! .drv logint clear              clear current log                       BlackCore::ISimulator
+        //! .drv pos callsign              shows current position in simulator     BlackCore::ISimulator
+        //! .drv spline|linear callsign    interpolator spline or linear           BlackCore::ISimulator
+        //! .drv aircraft readd callsign   re-add (add again) aircraft             BlackCore::ISimulator
+        //! .drv aircraft readd all        re-add all aircraft                     BlackCore::ISimulator
+        //! .drv aircraft rm callsign      remove aircraft                         BlackCore::ISimulator
+        //! .drv    unload                 unload driver                           BlackCore::ISimulator
+        //! .drv    fsuipc      on|off     enable/disable FSUIPC (if applicable)   BlackSimPlugin::FsCommon::CSimulatorFsCommon
         //! 
//! @} - //! Parse command line - virtual bool parseCommandLine(const QString &commandLine, const BlackMisc::CIdentifier &originator) = 0; + //! Parse command line for simulator drivers, derived classes can add specific parsing by overriding ISimulator::parseDetails + virtual bool parseCommandLine(const QString &commandLine, const BlackMisc::CIdentifier &originator); //! Consolidate setup with other data like from BlackMisc::Simulation::IRemoteAircraftProvider //! \threadsafe @@ -228,6 +243,46 @@ namespace BlackCore //! \copydoc BlackMisc::Simulation::IInterpolationSetupProvider::setInterpolationSetupGlobal virtual bool setInterpolationSetupGlobal(const BlackMisc::Simulation::CInterpolationAndRenderingSetupGlobal &setup) override; + //! Counter added aircraft + int getStatisticsPhysicallyAddedAircraft() const { return m_statsPhysicallyAddedAircraft; } + + //! Counter removed aircraft + int getStatisticsPhysicallyRemovedAircraft() const { return m_statsPhysicallyRemovedAircraft; } + + //! Current update time in ms + double getStatisticsCurrentUpdateTimeMs() const { return m_statsCurrentUpdateTimeMs; } + + //! Average update time in ms + double getStatisticsAverageUpdateTimeMs() const { return m_statsUpdateAircraftTimeAvgMs; } + + //! Total update time in ms + qint64 getStatisticsTotalUpdateTimeMs() const { return m_statsUpdateAircraftTimeTotalMs; } + + //! Max.update time in ms + qint64 getStatisticsMaxUpdateTimeMs() const { return m_statsMaxUpdateTimeMs; } + + //! Number of update runs + int getStatisticsUpdateRuns() const { return m_statsUpdateAircraftRuns; } + + //! Time between two update requests + qint64 getStatisticsAircraftUpdatedRequestedDeltaMs() const { return m_statsUpdateAircraftRequestedDeltaMs; } + + //! Access to logger + const BlackMisc::Simulation::CInterpolationLogger &interpolationLogger() const { return m_interpolationLogger; } + + //! The latest logged data formatted + //! \remark public only for log. displays + QString latestLoggedDataFormatted(const BlackMisc::Aviation::CCallsign &cs) const; + + //! Info about update aircraft limitations + QString updateAircraftLimitationInfo() const; + + //! Reset the last sent values + void resetLastSentValues(); + + //! Reset the last sent values per callsign + void resetLastSentValues(const BlackMisc::Aviation::CCallsign &callsign); + //! Register help static void registerHelp(); @@ -280,6 +335,31 @@ namespace BlackCore BlackMisc::Network::IClientProvider *clientProvider, QObject *parent = nullptr); + //! \name When swift DB data are read + //! @{ + virtual void onSwiftDbAllDataRead(); + virtual void onSwiftDbModelMatchingEntitiesRead(); + virtual void onSwiftDbAirportsRead(); + //! @} + + //! Parsed in derived classes + virtual bool parseDetails(const BlackMisc::CSimpleCommandParser &parser) = 0; + + //! Airports from web services + BlackMisc::Aviation::CAirportList getWebServiceAirports() const; + + //! Airport from web services by ICAO code + BlackMisc::Aviation::CAirport getWebServiceAirport(const BlackMisc::Aviation::CAirportIcaoCode &icao) const; + + //! Max.airports in range + int maxAirportsInRange() const; + + //! \name Connected with remote aircraft provider signals + //! @{ + //! Recalculate the rendered aircraft, this happens when restrictions are applied (max. aircraft, range) + virtual void onRecalculatedRenderedAircraft(const BlackMisc::Simulation::CAirspaceAircraftSnapshot &snapshot); + //! @} + //! Add new remote aircraft physically to the simulator //! \sa changeRemoteAircraftEnabled to hide a remote aircraft virtual bool physicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft) = 0; @@ -288,7 +368,7 @@ namespace BlackCore virtual bool physicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) = 0; //! Remove remote aircraft from simulator - virtual int physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns) = 0; + virtual int physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns); //! Remove all remote aircraft virtual int physicallyRemoveAllRemoteAircraft() = 0; @@ -301,8 +381,153 @@ namespace BlackCore //! \sa simulatorStatusChanged; void emitSimulatorCombinedStatus(SimulatorStatus oldStatus = Unspecified); - //! Emit the signal + //! \copydoc BlackMisc::Simulation::IInterpolationSetupProvider::emitInterpolationSetupChanged virtual void emitInterpolationSetupChanged() override; + + //! Display a debug log message based on BlackMisc::Simulation::CInterpolationAndRenderingSetup + //! remark shows log messages of functions calls + void debugLogMessage(const QString &msg); + + //! Display a debug log message based on BlackMisc::Simulation::CInterpolationAndRenderingSetup + //! remark shows log messages of functions calls + void debugLogMessage(const QString &funcInfo, const QString &msg); + + //! Show log messages? + bool showDebugLogMessage() const; + + //! Restore aircraft from the provider data + void resetAircraftFromProvider(const BlackMisc::Aviation::CCallsign &callsign); + + //! Clear the related data as statistics etc. + virtual void clearData(const BlackMisc::Aviation::CCallsign &callsign); + + //! Full reset of state + //! \remark reset as it was unloaded without unloading + //! \sa ISimulator::clearAllRemoteAircraftData + virtual void reset(); + + //! Reset highlighting + void resetHighlighting(); + + //! Restore all highlighted aircraft + void stopHighlighting(); + + //! Slow timer used to highlight aircraft, can be used for other things too + virtual void oneSecondTimerTimeout(); + + //! Kill timer if id is valid + void safeKillTimer(); + + //! Inject weather grid to simulator + virtual void injectWeatherGrid(const BlackMisc::Weather::CWeatherGrid &weatherGrid); + + //! Blink the highlighted aircraft + void blinkHighlightedAircraft(); + + //! Equal to last sent situation + bool isEqualLastSent(const BlackMisc::Aviation::CAircraftSituation &compare) const; + + //! Equal to last sent situation + bool isEqualLastSent(const BlackMisc::Aviation::CAircraftParts &compare, const BlackMisc::Aviation::CCallsign &callsign) const; + + //! Remember as last sent + void rememberLastSent(const BlackMisc::Aviation::CAircraftSituation &sent); + + //! Remember as last sent + void rememberLastSent(const BlackMisc::Aviation::CAircraftParts &sent, const BlackMisc::Aviation::CCallsign &callsign); + + //! Last sent situations + BlackMisc::Aviation::CAircraftSituationList getLastSentCanLikelySkipNearGroundInterpolation() const; + + //! Limit reached (max number of updates by token bucket if enabled) + bool isUpdateAircraftLimited(qint64 timestamp = -1); + + //! Limited as ISimulator::isUpdateAircraftLimited plus updating statistics + bool isUpdateAircraftLimitedWithStats(qint64 startTime = -1); + + //! Limit to updates per second + bool limitToUpdatesPerSecond(int numberPerSecond); + + //! Set own model + void reverseLookupAndUpdateOwnAircraftModel(const BlackMisc::Simulation::CAircraftModel &model); + + //! Set own model + void reverseLookupAndUpdateOwnAircraftModel(const QString &modelString); + + //! Info about invalid situation + QString getInvalidSituationLogMessage(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Simulation::CInterpolationStatus &status, const QString &details = {}) const; + + //! Can a new log message be generated without generating a "message" overflow + //! \remark works per callsign + //! \remark use this function when there is a risk that a lot of log. messages will be generated in a short time + bool clampedLog(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::CStatusMessage &message); + + //! Mark as justed logged + //! \remark touch, but also return if it can be logged + //! \remark use this function when there is a risk that a lot of log. messages will be generated in a short time + void removedClampedLog(const BlackMisc::Aviation::CCallsign &callsign); + + //! Update stats and flags + void setStatsRemoteAircraftUpdate(qint64 startTime, bool limited = false); + + //! Lookup against DB data + static BlackMisc::Simulation::CAircraftModel reverseLookupModel(const BlackMisc::Simulation::CAircraftModel &model); + + bool m_pausedSimFreezesInterpolation = false; //!< paused simulator will also pause interpolation (so AI aircraft will hold) + bool m_autoCalcAirportDistance = true; //!< automatically calculate airport distance and bearing + bool m_updateRemoteAircraftInProgress = false; //!< currently updating remote aircraft + int m_timerId = -1; //!< dispatch timer id + int m_statsUpdateAircraftRuns = 0; //!< statistics update count + int m_statsUpdateAircraftLimited = 0; //!< skipped because of max.update limitations + double m_statsUpdateAircraftTimeAvgMs = 0; //!< statistics average update time + qint64 m_statsUpdateAircraftTimeTotalMs = 0; //!< statistics total update time + qint64 m_statsCurrentUpdateTimeMs = 0; //!< statistics current update time + qint64 m_statsMaxUpdateTimeMs = 0; //!< statistics max.update time + qint64 m_statsLastUpdateAircraftRequestedMs = 0; //!< when was the last aircraft update requested + qint64 m_statsUpdateAircraftRequestedDeltaMs = 0; //!< delta time between 2 aircraft updates + + BlackMisc::Simulation::CSimulatorInternals m_simulatorInternals; //!< setup object + BlackMisc::Simulation::CInterpolationLogger m_interpolationLogger; //!< log.interpolation + BlackMisc::Aviation::CTimestampPerCallsign m_clampedLogMsg; //!< when logged last for this callsign, can be used so there is no log message overflow + BlackMisc::Aviation::CAircraftSituationPerCallsign m_lastSentSituations; //!< last situation sent to simulator + BlackMisc::Aviation::CAircraftPartsPerCallsign m_lastSentParts; //!< last parts sent to simulator + + // some optional functionality which can be used by the simulators as needed + BlackMisc::Simulation::CSimulatedAircraftList m_addAgainAircraftWhenRemoved; //!< add this model again when removed, normally used to change model + + // limit the update aircraft to a maximum per second + BlackMisc::CTokenBucket m_limitUpdateAircraftBucket { 5, 100, 5 }; //!< means 50 per second + bool m_limitUpdateAircraft = false; //!< limit the update frequency by using BlackMisc::CTokenBucket + + // weather + bool m_isWeatherActivated = false; //!< Is simulator weather activated? + BlackMisc::Geo::CCoordinateGeodetic m_lastWeatherPosition; //!< Own aircraft position at which weather was fetched and injected last + BlackMisc::CSetting m_weatherScenarioSettings { this, &ISimulator::reloadWeatherSettings }; //!< Selected weather scenario + + private: + // remote aircraft provider ("rap") bound + void rapOnRecalculatedRenderedAircraft(const BlackMisc::Simulation::CAirspaceAircraftSnapshot &snapshot); + void rapOnRemoteProviderRemovedAircraft(const BlackMisc::Aviation::CCallsign &callsign); + + // call with counters updated + void callPhysicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft); + void callPhysicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &remoteCallsign, bool blinking = false); + + //! Display a logged situation in simulator + void displayLoggedSituationInSimulator(const BlackMisc::Aviation::CCallsign &cs, bool stopLogging, int times = 40); + + bool m_blinkCycle = false; //!< used for highlighting + qint64 m_highlightEndTimeMsEpoch = 0; //!< end highlighting + int m_timerCounter = 0; //!< allows to calculate n seconds + QTimer m_oneSecondTimer; //!< multi purpose timer with 1 sec. interval + BlackMisc::Aviation::CCallsignSet m_callsignsToBeRendered; //!< callsigns which will be rendered + BlackMisc::CConnectionGuard m_remoteAircraftProviderConnections; //!< connected signal/slots + BlackMisc::Simulation::CSimulatedAircraftList m_highlightedAircraft; //!< all other aircraft are to be ignored + + // statistics values of how often those functions are called + // those are the added counters, overflow will not be an issue here (discussed in T171 review) + int m_statsPhysicallyAddedAircraft = 0; //!< statistics, how many aircraft added + int m_statsPhysicallyRemovedAircraft = 0; //!< statistics, how many aircraft removed }; //! Interface to a simulator listener. diff --git a/src/blackcore/simulatorcommon.cpp b/src/blackcore/simulatorcommon.cpp deleted file mode 100644 index d2ddf85f1..000000000 --- a/src/blackcore/simulatorcommon.cpp +++ /dev/null @@ -1,961 +0,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 "blackcore/simulatorcommon.h" -#include "blackcore/db/databaseutils.h" -#include "blackcore/db/databaseutils.h" -#include "blackcore/webdataservices.h" -#include "blackmisc/aviation/aircraftsituation.h" -#include "blackmisc/aviation/callsign.h" -#include "blackmisc/simulation/aircraftmodellist.h" -#include "blackmisc/simulation/airspaceaircraftsnapshot.h" -#include "blackmisc/simulation/interpolator.h" -#include "blackmisc/simulation/simulatedaircraft.h" -#include "blackmisc/pq/physicalquantity.h" -#include "blackmisc/simplecommandparser.h" -#include "blackmisc/logmessage.h" -#include "blackmisc/statusmessage.h" -#include "blackmisc/threadutils.h" -#include "blackconfig/buildconfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace BlackConfig; -using namespace BlackMisc; -using namespace BlackMisc::Geo; -using namespace BlackMisc::Aviation; -using namespace BlackMisc::Network; -using namespace BlackMisc::Simulation; -using namespace BlackMisc::PhysicalQuantities; -using namespace BlackMisc::Simulation; -using namespace BlackMisc::Weather; -using namespace BlackCore::Db; - -namespace BlackCore -{ - CSimulatorCommon::CSimulatorCommon(const CSimulatorPluginInfo &info, - IOwnAircraftProvider *ownAircraftProvider, - IRemoteAircraftProvider *remoteAircraftProvider, - IWeatherGridProvider *weatherGridProvider, - IClientProvider *clientProvider, - QObject *parent) - : ISimulator(info, ownAircraftProvider, remoteAircraftProvider, weatherGridProvider, clientProvider, parent) - { - this->setObjectName("Simulator: " + info.getIdentifier()); - CSimulatorCommon::registerHelp(); - - // provider signals, hook up with remote aircraft provider - m_remoteAircraftProviderConnections.append( - CRemoteAircraftAware::provider()->connectRemoteAircraftProviderSignals( - this, // receiver must match object in bind - nullptr, - nullptr, - std::bind(&CSimulatorCommon::rapOnRemoteProviderRemovedAircraft, this, std::placeholders::_1), - std::bind(&CSimulatorCommon::rapOnRecalculatedRenderedAircraft, this, std::placeholders::_1)) - ); - - // timer - connect(&m_oneSecondTimer, &QTimer::timeout, this, &CSimulatorCommon::oneSecondTimerTimeout); - m_oneSecondTimer.setObjectName(this->objectName().append(":m_oneSecondTimer")); - m_oneSecondTimer.start(1000); - - // swift data - if (sApp && sApp->hasWebDataServices()) - { - connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbAllDataRead, this, &CSimulatorCommon::onSwiftDbAllDataRead, Qt::QueuedConnection); - connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbAirportsRead, this, &CSimulatorCommon::onSwiftDbAirportsRead, Qt::QueuedConnection); - connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbModelMatchingEntitiesRead, this, &CSimulatorCommon::onSwiftDbModelMatchingEntitiesRead, Qt::QueuedConnection); - } - - connect(sApp, &CApplication::aboutToShutdown, this, &CSimulatorCommon::unload, Qt::QueuedConnection); - - // info - CLogMessage(this).info("Initialized simulator driver: '%1'") << this->getSimulatorInfo().toQString(); - } - - CSimulatorCommon::~CSimulatorCommon() - { - this->safeKillTimer(); - } - - const CLogCategoryList &CSimulatorCommon::getLogCategories() - { - static const CLogCategoryList cats({ CLogCategory::driver(), CLogCategory::plugin() }); - return cats; - } - - bool CSimulatorCommon::logicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft) - { - Q_ASSERT_X(remoteAircraft.hasModelString(), Q_FUNC_INFO, "Missing model string"); - Q_ASSERT_X(remoteAircraft.hasCallsign(), Q_FUNC_INFO, "Missing callsign"); - - const bool renderingRestricted = this->getInterpolationSetupGlobal().isRenderingRestricted(); - if (this->showDebugLogMessage()) { this->debugLogMessage(Q_FUNC_INFO, QString("Restricted: %1 cs: '%2' enabled: %3").arg(boolToYesNo(renderingRestricted), remoteAircraft.getCallsignAsString(), boolToYesNo(remoteAircraft.isEnabled()))); } - if (!remoteAircraft.isEnabled()) { return false; } - - // if not restriced, directly change - if (!renderingRestricted) { this->callPhysicallyAddRemoteAircraft(remoteAircraft); return true; } - - // restricted -> will be added with next snapshot onRecalculatedRenderedAircraft - return false; - } - - bool CSimulatorCommon::logicallyRemoveRemoteAircraft(const CCallsign &callsign) - { - // if not restriced, directly change - if (!this->getInterpolationSetupGlobal().isRenderingRestricted()) - { - m_statsPhysicallyAddedAircraft++; - this->callPhysicallyRemoveRemoteAircraft(callsign); return true; - } - - // will be added with next snapshot onRecalculatedRenderedAircraft - return false; - } - - int CSimulatorCommon::maxAirportsInRange() const - { - // might change in future or become a setting or such - return 20; - } - - void CSimulatorCommon::blinkHighlightedAircraft() - { - if (m_highlightedAircraft.isEmpty() || m_highlightEndTimeMsEpoch < 1) { return; } - if (this->isShuttingDown()) { return; } - m_blinkCycle = !m_blinkCycle; - - if (QDateTime::currentMSecsSinceEpoch() > m_highlightEndTimeMsEpoch) - { - this->stopHighlighting(); - return; - } - - // blink mode, toggle aircraft - for (const CSimulatedAircraft &aircraft : as_const(m_highlightedAircraft)) - { - if (m_blinkCycle) { this->callPhysicallyRemoveRemoteAircraft(aircraft.getCallsign(), true); } - else { this->callPhysicallyAddRemoteAircraft(aircraft); } - } - } - - void CSimulatorCommon::resetAircraftFromProvider(const CCallsign &callsign) - { - const CSimulatedAircraft aircraft(this->getAircraftInRangeForCallsign(callsign)); - const bool enabled = aircraft.isEnabled(); - if (enabled) - { - // are we already visible? - if (!this->isPhysicallyRenderedAircraft(callsign)) - { - this->callPhysicallyAddRemoteAircraft(aircraft); // enable/disable - } - } - else - { - this->callPhysicallyRemoveRemoteAircraft(callsign); - } - } - - void CSimulatorCommon::clearData(const CCallsign &callsign) - { - m_highlightedAircraft.removeByCallsign(callsign); - m_statsPhysicallyRemovedAircraft++; - m_clampedLogMsg.clear(); - m_lastSentParts.remove(callsign); - m_lastSentSituations.remove(callsign); - m_clampedLogMsg.remove(callsign); - this->removeInterpolationSetupPerCallsign(callsign); - } - - void CSimulatorCommon::reloadWeatherSettings() - { - if (!m_isWeatherActivated) { return; } - const auto selectedWeatherScenario = m_weatherScenarioSettings.get(); - if (!CWeatherScenario::isRealWeatherScenario(selectedWeatherScenario)) - { - m_lastWeatherPosition = {}; - this->injectWeatherGrid(CWeatherGrid::getByScenario(selectedWeatherScenario)); - } - } - - void CSimulatorCommon::reverseLookupAndUpdateOwnAircraftModel(const QString &modelString) - { - CAircraftModel model = getOwnAircraftModel(); - model.setModelString(modelString); - this->reverseLookupAndUpdateOwnAircraftModel(model); - } - - bool CSimulatorCommon::parseDetails(const CSimpleCommandParser &parser) - { - Q_UNUSED(parser); - return false; - } - - void CSimulatorCommon::debugLogMessage(const QString &msg) - { - if (!this->showDebugLogMessage()) { return; } - if (msg.isEmpty()) { return; } - const CStatusMessage m = CStatusMessage(this).info("%1") << msg; - emit this->driverMessages(m); - } - - void CSimulatorCommon::debugLogMessage(const QString &funcInfo, const QString &msg) - { - if (!this->showDebugLogMessage()) { return; } - if (msg.isEmpty()) { return; } - const CStatusMessage m = CStatusMessage(this).info("%1 %2") << msg << funcInfo; - emit this->driverMessages(m); - } - - bool CSimulatorCommon::showDebugLogMessage() const - { - const bool show = this->getInterpolationSetupGlobal().showSimulatorDebugMessages(); - return show; - } - - void CSimulatorCommon::reverseLookupAndUpdateOwnAircraftModel(const BlackMisc::Simulation::CAircraftModel &model) - { - Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing sApp"); - Q_ASSERT_X(sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing web services"); - - if (!model.hasModelString()) { return; } - if (this->getOwnAircraftModel() != model) - { - if (CDatabaseUtils::hasDbAircraftData()) - { - const CAircraftModel newModel = this->reverseLookupModel(model); - const bool updated = this->updateOwnModel(newModel); // update in provider (normally the context) - if (updated) - { - emit this->ownAircraftModelChanged(this->getOwnAircraftModel()); - } - } - else - { - // we wait for the data - connect(sApp->getWebDataServices(), &CWebDataServices::swiftDbModelMatchingEntitiesRead, this, [ = ] - { - this->reverseLookupAndUpdateOwnAircraftModel(model); - }); - } - } - } - - CAirportList CSimulatorCommon::getAirportsInRange() const - { - // default implementation - if (this->isShuttingDown()) { return CAirportList(); } - if (!sApp || !sApp->hasWebDataServices()) { return CAirportList(); } - - const CAirportList airports = sApp->getWebDataServices()->getAirports(); - if (airports.isEmpty()) { return airports; } - const CCoordinateGeodetic ownPosition = this->getOwnAircraftPosition(); - CAirportList airportInRange = airports.findClosest(maxAirportsInRange(), ownPosition); - if (m_autoCalcAirportDistance) { airportInRange.calculcateAndUpdateRelativeDistanceAndBearing(ownPosition); } - return airportInRange; - } - - void CSimulatorCommon::setWeatherActivated(bool activated) - { - m_isWeatherActivated = activated; - if (m_isWeatherActivated) - { - const auto selectedWeatherScenario = m_weatherScenarioSettings.get(); - if (!CWeatherScenario::isRealWeatherScenario(selectedWeatherScenario)) - { - m_lastWeatherPosition = {}; - this->injectWeatherGrid(CWeatherGrid::getByScenario(selectedWeatherScenario)); - } - } - } - - CAircraftModel CSimulatorCommon::reverseLookupModel(const CAircraftModel &model) - { - bool modified = false; - const CAircraftModel reverseModel = CDatabaseUtils::consolidateOwnAircraftModelWithDbData(model, false, &modified); - return reverseModel; - } - - bool CSimulatorCommon::isUpdateAircraftLimited(qint64 timestamp) - { - if (!m_limitUpdateAircraft) { return false; } - const bool hasToken = m_limitUpdateAircraftBucket.tryConsume(1, timestamp); - return !hasToken; - } - - bool CSimulatorCommon::isUpdateAircraftLimitedWithStats(qint64 startTime) - { - const bool limited = this->isUpdateAircraftLimited(startTime); - this->setStatsRemoteAircraftUpdate(startTime, limited); - return limited; - } - - bool CSimulatorCommon::limitToUpdatesPerSecond(int numberPerSecond) - { - if (numberPerSecond < 1) - { - m_limitUpdateAircraft = false; - return false; - } - - int tokens = 0.1 * numberPerSecond; // 100ms - do - { - if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(100); break; } - tokens = 0.25 * numberPerSecond; // 250ms - if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(250); break; } - tokens = 0.5 * numberPerSecond; // 500ms - if (tokens >= 3) { m_limitUpdateAircraftBucket.setInterval(500); break; } - tokens = numberPerSecond; - m_limitUpdateAircraftBucket.setInterval(1000); - } - while (false); - - m_limitUpdateAircraftBucket.setCapacityAndTokensToRefill(tokens); - m_limitUpdateAircraft = true; - return true; - } - - QString CSimulatorCommon::updateAircraftLimitationInfo() const - { - if (!m_limitUpdateAircraft) { return QStringLiteral("not limited"); } - static const QString limInfo("Limited %1 times with %2/secs."); - return limInfo.arg(m_statsUpdateAircraftLimited).arg(m_limitUpdateAircraftBucket.getTokensPerSecond()); - } - - void CSimulatorCommon::resetLastSentValues() - { - m_lastSentParts.clear(); - m_lastSentSituations.clear(); - } - - void CSimulatorCommon::resetLastSentValues(const CCallsign &callsign) - { - m_lastSentParts.remove(callsign); - m_lastSentSituations.remove(callsign); - } - - void CSimulatorCommon::onSwiftDbAllDataRead() - { - // void, can be overridden in specialized drivers - } - - void CSimulatorCommon::onSwiftDbModelMatchingEntitiesRead() - { - // void, can be overridden in specialized drivers - } - - void CSimulatorCommon::onSwiftDbAirportsRead() - { - // void, can be overridden in specialized drivers - } - - const CSimulatorInternals &CSimulatorCommon::getSimulatorInternals() const - { - return m_simulatorInternals; - } - - void CSimulatorCommon::unload() - { - this->disconnectFrom(); // disconnect from simulator - m_remoteAircraftProviderConnections.disconnectAll(); // disconnect signals from provider - } - - bool CSimulatorCommon::disconnectFrom() - { - // supposed to be overridden - return true; - } - - bool CSimulatorCommon::logicallyReAddRemoteAircraft(const CCallsign &callsign) - { - if (this->isShuttingDown()) { return false; } - if (callsign.isEmpty()) { return false; } - this->stopHighlighting(); - this->logicallyRemoveRemoteAircraft(callsign); - if (!this->isAircraftInRange(callsign)) { return false; } - const QPointer myself(this); - QTimer::singleShot(2500, this, [ = ] - { - if (myself.isNull()) { return; } - if (this->isShuttingDown()) { return; } - if (!this->isAircraftInRange(callsign)) { return; } - const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign); - if (aircraft.isEnabled() && aircraft.hasModelString()) - { - this->logicallyAddRemoteAircraft(aircraft); - } - }); - return true; - } - - CCallsignSet CSimulatorCommon::unrenderedEnabledAircraft() const - { - const CSimulatedAircraftList aircraft = this->getAircraftInRange().findByEnabled(true); - if (aircraft.isEmpty()) { return CCallsignSet(); } - CCallsignSet enabledOnes = aircraft.getCallsigns(); - const CCallsignSet renderedOnes = this->physicallyRenderedAircraft(); - enabledOnes.remove(renderedOnes); - return enabledOnes; - } - - CCallsignSet CSimulatorCommon::renderedDisabledAircraft() const - { - const CSimulatedAircraftList aircraft = this->getAircraftInRange().findByEnabled(false); - if (aircraft.isEmpty()) { return CCallsignSet(); } - const CCallsignSet disabledOnes = aircraft.getCallsigns(); - const CCallsignSet renderedOnes = this->physicallyRenderedAircraft(); - return renderedOnes.intersection(disabledOnes); - } - - void CSimulatorCommon::highlightAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const BlackMisc::PhysicalQuantities::CTime &displayTime) - { - const CCallsign cs(aircraftToHighlight.getCallsign()); - m_highlightedAircraft.removeByCallsign(cs); - if (enableHighlight) - { - const qint64 deltaT = displayTime.valueRounded(CTimeUnit::ms(), 0); - m_highlightEndTimeMsEpoch = QDateTime::currentMSecsSinceEpoch() + deltaT; - m_highlightedAircraft.push_back(aircraftToHighlight); - } - } - - int CSimulatorCommon::physicallyRemoveMultipleRemoteAircraft(const CCallsignSet &callsigns) - { - if (callsigns.isEmpty()) { return 0; } - this->stopHighlighting(); - int removed = 0; - for (const CCallsign &callsign : callsigns) - { - this->callPhysicallyRemoveRemoteAircraft(callsign); - removed++; - } - return removed; - } - - int CSimulatorCommon::physicallyRemoveAllRemoteAircraft() - { - // a default implementation, but normally overridden by the sims - const CCallsignSet callsigns = this->getAircraftInRangeCallsigns(); - const int r = this->physicallyRemoveMultipleRemoteAircraft(callsigns); - this->debugVerifyStateAfterAllAircraftRemoved(); - this->clearAllRemoteAircraftData(); - return r; - } - - bool CSimulatorCommon::changeRemoteAircraftEnabled(const CSimulatedAircraft &aircraft) - { - if (this->isShuttingDown()) { return false; } - return aircraft.isEnabled() ? - this->physicallyAddRemoteAircraft(aircraft) : - this->physicallyRemoveRemoteAircraft(aircraft.getCallsign()); - } - - bool CSimulatorCommon::changeRemoteAircraftModel(const CSimulatedAircraft &aircraft) - { - // we expect the new model "in aircraft" - // remove upfront, and then enable / disable again - if (this->isShuttingDown()) { return false; } - const CCallsign callsign = aircraft.getCallsign(); - if (!this->isPhysicallyRenderedAircraft(callsign)) { return false; } - this->physicallyRemoveRemoteAircraft(callsign); - return this->changeRemoteAircraftEnabled(aircraft); - } - - bool CSimulatorCommon::parseCommandLine(const QString &commandLine, const CIdentifier &originator) - { - if (this->isMyIdentifier(originator)) { return false; } - if (this->isShuttingDown()) { return false; } - - if (commandLine.isEmpty()) { return false; } - CSimpleCommandParser parser({ ".plugin", ".drv", ".driver" }); - parser.parse(commandLine); - if (!parser.isKnownCommand()) { return false; } - - // .plugin unload - if (parser.matchesPart(1, "unload")) { this->unload(); return true; } - - // .plugin log interpolator - const QString part1(parser.part(1).toLower().trimmed()); - if (part1.startsWith("logint") && parser.hasPart(2)) - { - const QString part2 = parser.part(2).toLower(); - if (part2 == "off" || part2 == "false") - { - CStatusMessage(this).info("Disabled interpolation logging"); - this->clearInterpolationLogCallsigns(); - return true; - } - if (part2 == "clear" || part2 == "clr") - { - m_interpolationLogger.clearLog(); - CStatusMessage(this).info("Cleared interpolation logging"); - this->clearInterpolationLogCallsigns(); - return true; - } - if (part2.startsWith("max")) - { - if (!parser.hasPart(3)) { return false; } - bool ok; - const int max = parser.part(3).toInt(&ok); - if (!ok) { return false; } - m_interpolationLogger.setMaxSituations(max); - CStatusMessage(this).info("Max.situations logged: %1") << max; - return true; - } - if (part2 == "write" || part2 == "save") - { - // stop logging of other log - this->clearInterpolationLogCallsigns(); - - // write - m_interpolationLogger.writeLogInBackground(); - CLogMessage(this).info("Started writing interpolation log"); - return true; - } - if (part2 == "show") - { - const QDir dir(CInterpolationLogger::getLogDirectory()); - if (CDirectoryUtils::isDirExisting(dir)) - { - const QUrl dirUrl = QUrl::fromLocalFile(dir.absolutePath()); - QDesktopServices::openUrl(dirUrl); // show dir in browser - } - else - { - CLogMessage(this).warning("No interpolation log directory"); - } - return true; - } - - const CCallsign cs(part2.toUpper()); - if (!cs.isValid()) { return false; } - if (this->getAircraftInRangeCallsigns().contains(cs)) - { - CLogMessage(this).info("Will log interpolation for '%1'") << cs.asString(); - this->setLogCallsign(true, cs); - return true; - } - else - { - CLogMessage(this).warning("Cannot log interpolation for '%1', no aircraft in range") << cs.asString(); - return false; - } - } // logint - - if (part1.startsWith("spline") || part1.startsWith("linear")) - { - if (parser.hasPart(2)) - { - const CCallsign cs(parser.part(2)); - const bool changed = this->setInterpolationMode(part1, cs); - CLogMessage(this).info(changed ? "Changed interpolation mode for '%1'" : "Unchanged interpolation mode for '%1'") << cs.asString(); - return true; - } - else - { - CInterpolationAndRenderingSetupGlobal setup = this->getInterpolationSetupGlobal(); - const bool changed = setup.setInterpolatorMode(part1); - if (changed) { this->setInterpolationSetupGlobal(setup); } - CLogMessage(this).info(changed ? "Changed interpolation mode globally" : "Unchanged interpolation mode"); - return true; - } - } // spline/linear - - if (part1.startsWith("pos")) - { - CCallsign cs(parser.part(2).toUpper()); - if (!cs.isValid()) - { - const CCallsignSet csSet = this->getLogCallsigns(); - if (csSet.size() != 1) { return false; } - - // if there is just one we take that one - cs = *csSet.begin(); - } - - this->setLogCallsign(true, cs); - CLogMessage(this).info("Display position for '%1'") << cs.asString(); - this->displayLoggedSituationInSimulator(cs, true); - return true; - } - - if (parser.hasPart(2) && (part1.startsWith("aircraft") || part1.startsWith("ac"))) - { - const QString part2 = parser.part(2).toLower(); - if (parser.hasPart(3) && (part2.startsWith("readd") || part2.startsWith("re-add"))) - { - const QString cs = parser.part(3).toUpper(); - if (cs == "all") - { - this->physicallyRemoveAllRemoteAircraft(); - const CStatusMessageList msgs = this->debugVerifyStateAfterAllAircraftRemoved(); - this->clearAllRemoteAircraftData(); - if (!msgs.isEmpty()) { emit this->driverMessages(msgs); } - const CSimulatedAircraftList aircraft = this->getAircraftInRange(); - for (const CSimulatedAircraft a : aircraft) - { - if (a.isEnabled()) { this->logicallyAddRemoteAircraft(a); } - } - } - else if (CCallsign::isValidAircraftCallsign(cs)) - { - this->logicallyReAddRemoteAircraft(cs); - return true; - } - return false; - } - if (parser.hasPart(3) && (part2.startsWith("rm") || part2.startsWith("remove"))) - { - const QString cs = parser.part(3).toUpper(); - if (CCallsign::isValidAircraftCallsign(cs)) - { - this->logicallyRemoveRemoteAircraft(cs); - } - } - - return false; - } - - if (part1.startsWith("limit")) - { - const int perSecond = parser.toInt(2, -1); - this->limitToUpdatesPerSecond(perSecond); - CLogMessage(this).info("Remote aircraft updates limitations: %1") << this->updateAircraftLimitationInfo(); - return true; - } - - // driver specific cmd line arguments - return this->parseDetails(parser); - } - - void CSimulatorCommon::registerHelp() - { - if (CSimpleCommandParser::registered("BlackCore::CSimulatorCommon")) { return; } - CSimpleCommandParser::registerCommand({".drv", "alias: .driver .plugin"}); - CSimpleCommandParser::registerCommand({".drv limit number/secs.", "limit updates to number per second (0..off)"}); - CSimpleCommandParser::registerCommand({".drv logint callsign", "log interpolator for callsign"}); - CSimpleCommandParser::registerCommand({".drv logint off", "no log information for interpolator"}); - CSimpleCommandParser::registerCommand({".drv logint write", "write interpolator log to file"}); - CSimpleCommandParser::registerCommand({".drv logint clear", "clear current log"}); - CSimpleCommandParser::registerCommand({".drv logint max number", "max. number of entries logged"}); - CSimpleCommandParser::registerCommand({".drv pos callsign", "show position for callsign"}); - CSimpleCommandParser::registerCommand({".drv spline|linear callsign", "set spline/linear interpolator for one/all callsign(s)"}); - CSimpleCommandParser::registerCommand({".drv aircraft readd callsign", "add again (re-add) a given callsign"}); - CSimpleCommandParser::registerCommand({".drv aircraft readd all", "add again (re-add) all aircraft"}); - CSimpleCommandParser::registerCommand({".drv aircraft rm callsign", "remove a given callsign"}); - } - - void CSimulatorCommon::resetAircraftStatistics() - { - m_statsUpdateAircraftRuns = 0; - m_statsUpdateAircraftTimeAvgMs = 0; - m_statsUpdateAircraftTimeTotalMs = 0; - m_statsMaxUpdateTimeMs = 0; - m_statsCurrentUpdateTimeMs = 0; - m_statsPhysicallyAddedAircraft = 0; - m_statsPhysicallyRemovedAircraft = 0; - m_statsLastUpdateAircraftRequestedMs = 0; - m_statsUpdateAircraftRequestedDeltaMs = 0; - m_statsUpdateAircraftLimited = 0; - ISimulationEnvironmentProvider::resetSimulationEnvironmentStatistics(); - } - - CStatusMessageList CSimulatorCommon::debugVerifyStateAfterAllAircraftRemoved() const - { - CStatusMessageList msgs; - if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return msgs; } - if (!m_addAgainAircraftWhenRemoved.isEmpty()) { msgs.push_back(CStatusMessage(this).error("m_addAgainAircraftWhenRemoved not empty: '%1'") << m_addAgainAircraftWhenRemoved.getCallsignStrings(true).join(", ")); } - return msgs; - } - - void CSimulatorCommon::oneSecondTimerTimeout() - { - this->blinkHighlightedAircraft(); - } - - void CSimulatorCommon::safeKillTimer() - { - if (m_timerId < 0) { return; } - this->killTimer(m_timerId); - m_timerId = -1; - } - - QString CSimulatorCommon::getInvalidSituationLogMessage(const CCallsign &callsign, const CInterpolationStatus &status, const QString &details) const - { - static const QString msg("CS: '%1' Interpolation: '%2'"); - const QString m = msg.arg(callsign.asString(), status.toQString()); - if (details.isEmpty()) { return m; } - - static const QString addDetails(" details: '%1'"); - return m + addDetails.arg(details); - } - - bool CSimulatorCommon::clampedLog(const CCallsign &callsign, const CStatusMessage &message) - { - if (message.isEmpty()) { return false; } - constexpr qint64 Timeout = 2000; - const qint64 clampTs = m_clampedLogMsg.value(callsign, -1); - const qint64 ts = QDateTime::currentMSecsSinceEpoch(); - if (clampTs > 0 && ((clampTs + Timeout) > ts)) { return false; } - CLogMessage::preformatted(message); - m_clampedLogMsg[callsign] = ts; - return true; - } - - void CSimulatorCommon::removedClampedLog(const CCallsign &callsign) - { - m_clampedLogMsg.remove(callsign); - } - - void CSimulatorCommon::setStatsRemoteAircraftUpdate(qint64 startTime, bool limited) - { - const qint64 now = QDateTime::currentMSecsSinceEpoch(); - const qint64 dt = now - startTime; - m_statsCurrentUpdateTimeMs = dt; - m_statsUpdateAircraftTimeTotalMs += dt; - m_statsUpdateAircraftRuns++; - m_statsUpdateAircraftTimeAvgMs = static_cast(m_statsUpdateAircraftTimeTotalMs) / static_cast(m_statsUpdateAircraftRuns); - m_updateRemoteAircraftInProgress = false; - m_statsLastUpdateAircraftRequestedMs = startTime; - - if (m_statsMaxUpdateTimeMs < dt) { m_statsMaxUpdateTimeMs = dt; } - if (m_statsLastUpdateAircraftRequestedMs > 0) { m_statsUpdateAircraftRequestedDeltaMs = startTime - m_statsLastUpdateAircraftRequestedMs; } - if (limited) { m_statsUpdateAircraftLimited++; } - } - - bool CSimulatorCommon::isEqualLastSent(const CAircraftSituation &compare) const - { - Q_ASSERT_X(compare.hasCallsign(), Q_FUNC_INFO, "Need callsign"); - if (!m_lastSentSituations.contains(compare.getCallsign())) { return false; } - if (compare.isNull()) { return false; } - return compare.equalPbhVectorAltitude(m_lastSentSituations.value(compare.getCallsign())); - } - - bool CSimulatorCommon::isEqualLastSent(const CAircraftParts &compare, const CCallsign &callsign) const - { - if (callsign.isEmpty()) { return false; } - if (!m_lastSentParts.contains(callsign)) { return false; } - return compare.equalValues(m_lastSentParts.value(callsign)); - } - - void CSimulatorCommon::rememberLastSent(const CAircraftSituation &sent) - { - Q_ASSERT_X(sent.hasCallsign(), Q_FUNC_INFO, "Need callsign"); - m_lastSentSituations.insert(sent.getCallsign(), sent); - } - - void CSimulatorCommon::rememberLastSent(const CAircraftParts &sent, const CCallsign &callsign) - { - Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign"); - m_lastSentParts.insert(callsign, sent); - } - - CAircraftSituationList CSimulatorCommon::getLastSentCanLikelySkipNearGroundInterpolation() const - { - const QList situations = m_lastSentSituations.values(); - CAircraftSituationList skipped; - for (const CAircraftSituation &s : situations) - { - if (s.canLikelySkipNearGroundInterpolation()) { skipped.push_back(s); } - } - return skipped; - } - - void CSimulatorCommon::onRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot) - { - if (!snapshot.isValidSnapshot()) { return;} - - // for unrestricted values all add/remove actions are directly linked - // when changing back from restricted->unrestricted an one time update is required - if (!snapshot.isRestricted() && !snapshot.isRestrictionChanged()) { return; } - - Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Needs to run in object thread"); - Q_ASSERT_X(snapshot.generatingThreadName() != QThread::currentThread()->objectName(), Q_FUNC_INFO, "Expect snapshot from background thread"); - - // restricted snapshot values? - bool changed = false; - if (snapshot.isRenderingEnabled()) - { - const CCallsignSet callsignsInSimulator(this->physicallyRenderedAircraft()); // state in simulator - const CCallsignSet callsignsToBeRemoved(callsignsInSimulator.difference(snapshot.getEnabledAircraftCallsignsByDistance())); - const CCallsignSet callsignsToBeAdded(snapshot.getEnabledAircraftCallsignsByDistance().difference(callsignsInSimulator)); - if (!callsignsToBeRemoved.isEmpty()) - { - const int r = this->physicallyRemoveMultipleRemoteAircraft(callsignsToBeRemoved); - changed = r > 0; - } - - if (!callsignsToBeAdded.isEmpty()) - { - CSimulatedAircraftList aircraftToBeAdded(this->getAircraftInRange().findByCallsigns(callsignsToBeAdded)); // thread safe copy - for (const CSimulatedAircraft &aircraft : aircraftToBeAdded) - { - Q_ASSERT_X(aircraft.isEnabled(), Q_FUNC_INFO, "Disabled aircraft detected as to be added"); - Q_ASSERT_X(aircraft.hasModelString(), Q_FUNC_INFO, "Missing model string"); - this->callPhysicallyAddRemoteAircraft(aircraft); // recalcuate snapshot - changed = true; - } - } - } - else - { - // no rendering at all, we remove everything - const int r = this->physicallyRemoveAllRemoteAircraft(); - changed = r > 0; - } - - // we have handled snapshot - if (changed) - { - emit this->airspaceSnapshotHandled(); - } - } - - void CSimulatorCommon::reset() - { - this->clearAllRemoteAircraftData(); - } - - void CSimulatorCommon::resetHighlighting() - { - m_highlightedAircraft.clear(); - m_blinkCycle = false; - m_highlightEndTimeMsEpoch = false; - } - - void CSimulatorCommon::stopHighlighting() - { - // restore - const CSimulatedAircraftList highlightedAircraft(m_highlightedAircraft); - for (const CSimulatedAircraft &aircraft : highlightedAircraft) - { - // get the current state for this aircraft - // it might has been removed in the meantime - const CCallsign cs(aircraft.getCallsign()); - this->resetAircraftFromProvider(cs); - } - this->resetHighlighting(); - } - - void CSimulatorCommon::clearAllRemoteAircraftData() - { - // rendering related stuff - m_addAgainAircraftWhenRemoved.clear(); - m_callsignsToBeRendered.clear(); - m_clampedLogMsg.clear(); - m_lastSentParts.clear(); - m_lastSentSituations.clear(); - m_updateRemoteAircraftInProgress = false; - - this->clearInterpolationSetupsPerCallsign(); - this->resetHighlighting(); - this->resetAircraftStatistics(); - } - - CAirportList CSimulatorCommon::getWebServiceAirports() const - { - if (this->isShuttingDown()) { return CAirportList(); } - if (!sApp->hasWebDataServices()) { return CAirportList(); } - return sApp->getWebDataServices()->getAirports(); - } - - CAirport CSimulatorCommon::getWebServiceAirport(const CAirportIcaoCode &icao) const - { - if (this->isShuttingDown()) { return CAirport(); } - if (!sApp->hasWebDataServices()) { return CAirport(); } - return sApp->getWebDataServices()->getAirports().findFirstByIcao(icao); - } - - void CSimulatorCommon::rapOnRecalculatedRenderedAircraft(const CAirspaceAircraftSnapshot &snapshot) - { - if (!this->isConnected()) { return; } - if (this->isShuttingDown()) { return; } - this->onRecalculatedRenderedAircraft(snapshot); - } - - void CSimulatorCommon::rapOnRemoteProviderRemovedAircraft(const CCallsign &callsign) - { - Q_UNUSED(callsign); - // currently not used, the calls are handled by context call logicallyRemoveRemoteAircraft - } - - void CSimulatorCommon::callPhysicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft) - { - m_statsPhysicallyAddedAircraft++; - this->physicallyAddRemoteAircraft(remoteAircraft); - } - - void CSimulatorCommon::callPhysicallyRemoveRemoteAircraft(const CCallsign &remoteCallsign, bool blinking) - { - if (!blinking) { this->clearData(remoteCallsign); } - this->physicallyRemoveRemoteAircraft(remoteCallsign); - } - - QString CSimulatorCommon::latestLoggedDataFormatted(const CCallsign &cs) const - { - const SituationLog s = m_interpolationLogger.getLastSituationLog(cs); - const PartsLog p = m_interpolationLogger.getLastPartsLog(cs); - - static const QString sep("\n------\n"); - QString dm; - if (s.tsCurrent > 0) - { - dm = QStringLiteral("Setup: ") % s.usedSetup.toQString(true) % - QStringLiteral("\n\n") % - QStringLiteral("Situation: ") % s.toQString(false, true, true, true, true, sep); - } - if (p.tsCurrent > 0) { dm += (dm.isEmpty() ? QStringLiteral("") : QStringLiteral("\n\n")) % QStringLiteral("Parts: ") % p.toQString(sep); } - return dm; - } - - void CSimulatorCommon::displayLoggedSituationInSimulator(const CCallsign &cs, bool stopLogging, int times) - { - if (cs.isEmpty()) { return; } - if (this->isShuttingDown()) { return; } - const CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupPerCallsignOrDefault(cs); - const bool logsCs = setup.logInterpolation(); - if (!logsCs) { return; } - - stopLogging = stopLogging || !this->isSimulating(); // stop when sim was stopped - stopLogging = stopLogging && logsCs; - if (!stopLogging && times < 1) { return; } - - const bool inRange = this->getAircraftInRangeCallsigns().contains(cs); - if (!stopLogging && !inRange) { return; } - if (stopLogging && (times < 1 || !inRange)) - { - this->setLogCallsign(false, cs); - return; - } - - const QString dm = this->latestLoggedDataFormatted(cs); - if (!dm.isEmpty()) - { - this->displayStatusMessage(CStatusMessage(this).info(dm)); - emit this->requestUiConsoleMessage(dm, true); - } - - const int t = 4500 + (qrand() % 1000); // makes sure not always using the same time difference - const QPointer myself(this); - QTimer::singleShot(t, this, [ = ] - { - if (myself.isNull() || myself->isShuttingDown()) { return; } - this->displayLoggedSituationInSimulator(cs, stopLogging, times - 1); - }); - } -} // namespace diff --git a/src/blackcore/simulatorcommon.h b/src/blackcore/simulatorcommon.h deleted file mode 100644 index ca4b6e598..000000000 --- a/src/blackcore/simulatorcommon.h +++ /dev/null @@ -1,345 +0,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 BLACKCORE_SIMULATOR_COMMON_H -#define BLACKCORE_SIMULATOR_COMMON_H - -#include -#include -#include -#include -#include - -#include "blackcore/aircraftmatcher.h" -#include "blackcore/blackcoreexport.h" -#include "blackcore/simulator.h" -#include "blackmisc/simulation/aircraftmodelsetloader.h" -#include "blackmisc/simulation/simulatedaircraftlist.h" -#include "blackmisc/simulation/simulatorinfo.h" -#include "blackmisc/simulation/simulatorplugininfo.h" -#include "blackmisc/simulation/simulatorinternals.h" -#include "blackmisc/simulation/settings/simulatorsettings.h" -#include "blackmisc/simulation/interpolationrenderingsetup.h" -#include "blackmisc/simulation/interpolationlogger.h" -#include "blackmisc/aviation/callsignset.h" -#include "blackmisc/pq/length.h" -#include "blackmisc/pq/time.h" -#include "blackmisc/pq/units.h" -#include "blackmisc/tokenbucket.h" -#include "blackmisc/connectionguard.h" - -namespace BlackMisc -{ - class CSimpleCommandParser; - - namespace Aviation - { - class CAircraftParts; - class CAircraftSituation; - class CCallsign; - } - namespace Simulation - { - class CAirspaceAircraftSnapshot; - class CSimulatedAircraft; - } -} - -namespace BlackCore -{ - //! Common base class with providers, interface and some base functionality - class BLACKCORE_EXPORT CSimulatorCommon : public ISimulator - { - Q_OBJECT - Q_INTERFACES(BlackCore::ISimulator) - Q_INTERFACES(BlackMisc::Simulation::ISimulationEnvironmentProvider) - Q_INTERFACES(BlackMisc::Simulation::IInterpolationSetupProvider) - - public: - //! Log categories - static const BlackMisc::CLogCategoryList &getLogCategories(); - - //! Destructor - virtual ~CSimulatorCommon(); - - // --------- ISimulator implementations ------------ - virtual void highlightAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const BlackMisc::PhysicalQuantities::CTime &displayTime) override; - virtual const BlackMisc::Simulation::CSimulatorInternals &getSimulatorInternals() const override; - virtual BlackMisc::Aviation::CAirportList getAirportsInRange() const override; - virtual void setWeatherActivated(bool activated) override; - virtual void unload() override; - virtual bool disconnectFrom() override; - virtual bool logicallyReAddRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual BlackMisc::Aviation::CCallsignSet unrenderedEnabledAircraft() const override; - virtual BlackMisc::Aviation::CCallsignSet renderedDisabledAircraft() const override; - virtual int physicallyRemoveMultipleRemoteAircraft(const BlackMisc::Aviation::CCallsignSet &callsigns) override; - virtual int physicallyRemoveAllRemoteAircraft() override; - virtual bool changeRemoteAircraftEnabled(const BlackMisc::Simulation::CSimulatedAircraft &aircraft) override; - virtual bool changeRemoteAircraftModel(const BlackMisc::Simulation::CSimulatedAircraft &aircraft) override; - virtual void clearAllRemoteAircraftData() override; - virtual void resetAircraftStatistics() override; - virtual BlackMisc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const override; - - //! \addtogroup swiftdotcommands - //! @{ - //!
-        //! .drv unload                    unload plugin                           BlackCore::CSimulatorCommon
-        //! .drv limit number              limit the number of updates             BlackCore::CSimulatorCommon
-        //! .drv logint callsign           log interpolator for callsign           BlackCore::CSimulatorCommon
-        //! .drv logint off                no log information for interpolator     BlackCore::CSimulatorCommon
-        //! .drv logint write              write interpolator log to file          BlackCore::CSimulatorCommon
-        //! .drv logint clear              clear current log                       BlackCore::CSimulatorCommon
-        //! .drv pos callsign              shows current position in simulator     BlackCore::CSimulatorCommon
-        //! .drv spline|linear callsign    interpolator spline or linear           BlackCore::CSimulatorCommon
-        //! .drv aircraft readd callsign   re-add (add again) aircraft             BlackCore::CSimulatorCommon
-        //! .drv aircraft readd all        re-add all aircraft                     BlackCore::CSimulatorCommon
-        //! .drv aircraft rm callsign      remove aircraft                         BlackCore::CSimulatorCommon
-        //! 
- //! @} - //! \copydoc ISimulator::parseCommandLine - virtual bool parseCommandLine(const QString &commandLine, const BlackMisc::CIdentifier &originator) override; - - //! \name Interface implementations, called from context - //! @{ - virtual bool logicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft) override; - virtual bool logicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) override; - //! @} - - // --------- ISimulator implementations ------------ - - //! Register help - static void registerHelp(); - - //! Counter added aircraft - int getStatisticsPhysicallyAddedAircraft() const { return m_statsPhysicallyAddedAircraft; } - - //! Counter removed aircraft - int getStatisticsPhysicallyRemovedAircraft() const { return m_statsPhysicallyRemovedAircraft; } - - //! Current update time in ms - double getStatisticsCurrentUpdateTimeMs() const { return m_statsCurrentUpdateTimeMs; } - - //! Average update time in ms - double getStatisticsAverageUpdateTimeMs() const { return m_statsUpdateAircraftTimeAvgMs; } - - //! Total update time in ms - qint64 getStatisticsTotalUpdateTimeMs() const { return m_statsUpdateAircraftTimeTotalMs; } - - //! Max.update time in ms - qint64 getStatisticsMaxUpdateTimeMs() const { return m_statsMaxUpdateTimeMs; } - - //! Number of update runs - int getStatisticsUpdateRuns() const { return m_statsUpdateAircraftRuns; } - - //! Time between two update requests - qint64 getStatisticsAircraftUpdatedRequestedDeltaMs() const { return m_statsUpdateAircraftRequestedDeltaMs; } - - //! Access to logger - const BlackMisc::Simulation::CInterpolationLogger &interpolationLogger() const { return m_interpolationLogger; } - - //! The latest logged data formatted - //! \remark public only for log. displays - QString latestLoggedDataFormatted(const BlackMisc::Aviation::CCallsign &cs) const; - - //! Info about update aircraft limitations - QString updateAircraftLimitationInfo() const; - - //! Reset the last sent values - void resetLastSentValues(); - - //! Reset the last sent values per callsign - void resetLastSentValues(const BlackMisc::Aviation::CCallsign &callsign); - - protected: - //! Constructor - CSimulatorCommon(const BlackMisc::Simulation::CSimulatorPluginInfo &info, - BlackMisc::Simulation::IOwnAircraftProvider *ownAircraftProvider, - BlackMisc::Simulation::IRemoteAircraftProvider *remoteAircraftProvider, - BlackMisc::Weather::IWeatherGridProvider *weatherGridProvider, - BlackMisc::Network::IClientProvider *clientProvider, - QObject *parent); - - //! \name When swift DB data are read - //! @{ - virtual void onSwiftDbAllDataRead(); - virtual void onSwiftDbModelMatchingEntitiesRead(); - virtual void onSwiftDbAirportsRead(); - //! @} - - //! \name Connected with remote aircraft provider signals - //! @{ - //! Recalculate the rendered aircraft, this happens when restrictions are applied (max. aircraft, range) - virtual void onRecalculatedRenderedAircraft(const BlackMisc::Simulation::CAirspaceAircraftSnapshot &snapshot); - //! @} - - //! Max.airports in range - int maxAirportsInRange() const; - - //! Full reset of state - //! \remark reset as it was unloaded without unloading - //! \sa ISimulator::clearAllRemoteAircraftData - virtual void reset(); - - //! Reset highlighting - void resetHighlighting(); - - //! Restore all highlighted aircraft - void stopHighlighting(); - - //! Inject weather grid to simulator - virtual void injectWeatherGrid(const BlackMisc::Weather::CWeatherGrid &weatherGrid) { Q_UNUSED(weatherGrid); } - - //! Airports from web services - BlackMisc::Aviation::CAirportList getWebServiceAirports() const; - - //! Airport from web services by ICAO code - BlackMisc::Aviation::CAirport getWebServiceAirport(const BlackMisc::Aviation::CAirportIcaoCode &icao) const; - - //! Blink the highlighted aircraft - void blinkHighlightedAircraft(); - - //! Restore aircraft from the provider data - void resetAircraftFromProvider(const BlackMisc::Aviation::CCallsign &callsign); - - //! Clear the related data as statistics etc. - virtual void clearData(const BlackMisc::Aviation::CCallsign &callsign); - - //! Set own model - void reverseLookupAndUpdateOwnAircraftModel(const BlackMisc::Simulation::CAircraftModel &model); - - //! Set own model - void reverseLookupAndUpdateOwnAircraftModel(const QString &modelString); - - //! Reload weather settings - void reloadWeatherSettings(); - - //! Parse driver specific details for ISimulator::parseCommandLine - virtual bool parseDetails(const BlackMisc::CSimpleCommandParser &parser); - - //! Display a debug log message based on BlackMisc::Simulation::CInterpolationAndRenderingSetup - //! remark shows log messages of functions calls - void debugLogMessage(const QString &msg); - - //! Display a debug log message based on BlackMisc::Simulation::CInterpolationAndRenderingSetup - //! remark shows log messages of functions calls - void debugLogMessage(const QString &funcInfo, const QString &msg); - - //! Show log messages? - bool showDebugLogMessage() const; - - //! Slow timer used to highlight aircraft, can be used for other things too - virtual void oneSecondTimerTimeout(); - - //! Kill timer if id is valid - void safeKillTimer(); - - //! Info about invalid situation - QString getInvalidSituationLogMessage(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Simulation::CInterpolationStatus &status, const QString &details = {}) const; - - //! Can a new log message be generated without generating a "message" overflow - //! \remark works per callsign - //! \remark use this function when there is a risk that a lot of log. messages will be generated in a short time - bool clampedLog(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::CStatusMessage &message); - - //! Mark as justed logged - //! \remark touch, but also return if it can be logged - //! \remark use this function when there is a risk that a lot of log. messages will be generated in a short time - void removedClampedLog(const BlackMisc::Aviation::CCallsign &callsign); - - //! Update stats and flags - void setStatsRemoteAircraftUpdate(qint64 startTime, bool limited = false); - - //! Equal to last sent situation - bool isEqualLastSent(const BlackMisc::Aviation::CAircraftSituation &compare) const; - - //! Equal to last sent situation - bool isEqualLastSent(const BlackMisc::Aviation::CAircraftParts &compare, const BlackMisc::Aviation::CCallsign &callsign) const; - - //! Remember as last sent - void rememberLastSent(const BlackMisc::Aviation::CAircraftSituation &sent); - - //! Remember as last sent - void rememberLastSent(const BlackMisc::Aviation::CAircraftParts &sent, const BlackMisc::Aviation::CCallsign &callsign); - - //! Last sent situations - BlackMisc::Aviation::CAircraftSituationList getLastSentCanLikelySkipNearGroundInterpolation() const; - - //! Lookup against DB data - static BlackMisc::Simulation::CAircraftModel reverseLookupModel(const BlackMisc::Simulation::CAircraftModel &model); - - bool m_pausedSimFreezesInterpolation = false; //!< paused simulator will also pause interpolation (so AI aircraft will hold) - bool m_autoCalcAirportDistance = true; //!< automatically calculate airport distance and bearing - bool m_updateRemoteAircraftInProgress = false; //!< currently updating remote aircraft - int m_timerId = -1; //!< dispatch timer id - int m_statsUpdateAircraftRuns = 0; //!< statistics update count - int m_statsUpdateAircraftLimited = 0; //!< skipped because of max.update limitations - double m_statsUpdateAircraftTimeAvgMs = 0; //!< statistics average update time - qint64 m_statsUpdateAircraftTimeTotalMs = 0; //!< statistics total update time - qint64 m_statsCurrentUpdateTimeMs = 0; //!< statistics current update time - qint64 m_statsMaxUpdateTimeMs = 0; //!< statistics max.update time - qint64 m_statsLastUpdateAircraftRequestedMs = 0; //!< when was the last aircraft update requested - qint64 m_statsUpdateAircraftRequestedDeltaMs = 0; //!< delta time between 2 aircraft updates - - BlackMisc::Simulation::CSimulatorInternals m_simulatorInternals; //!< setup object - BlackMisc::Simulation::CInterpolationLogger m_interpolationLogger; //!< log.interpolation - BlackMisc::Aviation::CTimestampPerCallsign m_clampedLogMsg; //!< when logged last for this callsign, can be used so there is no log message overflow - BlackMisc::Aviation::CAircraftSituationPerCallsign m_lastSentSituations; //!< last situation sent to simulator - BlackMisc::Aviation::CAircraftPartsPerCallsign m_lastSentParts; //!< last parts sent to simulator - - // some optional functionality which can be used by the simulators as needed - BlackMisc::Simulation::CSimulatedAircraftList m_addAgainAircraftWhenRemoved; //!< add this model again when removed, normally used to change model - - // limit the update aircraft to a maximum per second - BlackMisc::CTokenBucket m_limitUpdateAircraftBucket { 5, 100, 5 }; //!< means 50 per second - bool m_limitUpdateAircraft = false; //!< limit the update frequency by using BlackMisc::CTokenBucket - - //! Limit reached (max number of updates by token bucket if enabled) - bool isUpdateAircraftLimited(qint64 timestamp = -1); - - //! Limited as CSimulatorCommon::isUpdateAircraftLimited plus updating statistics - bool isUpdateAircraftLimitedWithStats(qint64 startTime = -1); - - //! Limit to updates per second - bool limitToUpdatesPerSecond(int numberPerSecond); - - // weather - bool m_isWeatherActivated = false; //!< Is simulator weather activated? - BlackMisc::Geo::CCoordinateGeodetic m_lastWeatherPosition; //!< Own aircraft position at which weather was fetched and injected last - BlackMisc::CSetting m_weatherScenarioSettings { this, &CSimulatorCommon::reloadWeatherSettings }; //!< Selected weather scenario - - private: - // remote aircraft provider ("rap") bound - void rapOnRecalculatedRenderedAircraft(const BlackMisc::Simulation::CAirspaceAircraftSnapshot &snapshot); - void rapOnRemoteProviderRemovedAircraft(const BlackMisc::Aviation::CCallsign &callsign); - - // call with counters updated - void callPhysicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft); - void callPhysicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &remoteCallsign, bool blinking = false); - - //! Display a logged situation in simulator - void displayLoggedSituationInSimulator(const BlackMisc::Aviation::CCallsign &cs, bool stopLogging, int times = 40); - - bool m_blinkCycle = false; //!< used for highlighting - qint64 m_highlightEndTimeMsEpoch = 0; //!< end highlighting - int m_timerCounter = 0; //!< allows to calculate n seconds - QTimer m_oneSecondTimer; //!< multi purpose timer with 1 sec. interval - BlackMisc::Simulation::CSimulatedAircraftList m_highlightedAircraft; //!< all other aircraft are to be ignored - BlackMisc::Aviation::CCallsignSet m_callsignsToBeRendered; //!< callsigns which will be rendered - BlackMisc::CConnectionGuard m_remoteAircraftProviderConnections; //!< connected signal/slots - - // statistics values of how often those functions are called - // those are the added counters, overflow will not be an issue here (discussed in T171 review) - int m_statsPhysicallyAddedAircraft = 0; //!< statistics, how many aircraft added - int m_statsPhysicallyRemovedAircraft = 0; //!< statistics, how many aircraft removed - }; -} // namespace - -#endif // guard diff --git a/src/plugins/simulator/emulated/simulatoremulated.cpp b/src/plugins/simulator/emulated/simulatoremulated.cpp index 702d23667..56f837028 100644 --- a/src/plugins/simulator/emulated/simulatoremulated.cpp +++ b/src/plugins/simulator/emulated/simulatoremulated.cpp @@ -89,31 +89,31 @@ namespace BlackSimPlugin bool CSimulatorEmulated::logicallyAddRemoteAircraft(const CSimulatedAircraft &remoteAircraft) { if (canLog()) { m_monitorWidget->appendReceivingCall(Q_FUNC_INFO, remoteAircraft.toQString()); } - return CSimulatorCommon::logicallyAddRemoteAircraft(remoteAircraft); + return ISimulator::logicallyAddRemoteAircraft(remoteAircraft); } bool CSimulatorEmulated::logicallyRemoveRemoteAircraft(const CCallsign &callsign) { if (canLog()) { m_monitorWidget->appendReceivingCall(Q_FUNC_INFO, callsign.toQString()); } - return CSimulatorCommon::logicallyRemoveRemoteAircraft(callsign); + return ISimulator::logicallyRemoveRemoteAircraft(callsign); } int CSimulatorEmulated::physicallyRemoveMultipleRemoteAircraft(const CCallsignSet &callsigns) { if (canLog()) m_monitorWidget->appendReceivingCall(Q_FUNC_INFO, callsigns.toQString()); - return CSimulatorCommon::physicallyRemoveMultipleRemoteAircraft(callsigns); + return ISimulator::physicallyRemoveMultipleRemoteAircraft(callsigns); } bool CSimulatorEmulated::changeRemoteAircraftModel(const CSimulatedAircraft &aircraft) { if (canLog()) { m_monitorWidget->appendReceivingCall(Q_FUNC_INFO, aircraft.toQString()); } - return CSimulatorCommon::changeRemoteAircraftEnabled(aircraft); + return ISimulator::changeRemoteAircraftEnabled(aircraft); } bool CSimulatorEmulated::changeRemoteAircraftEnabled(const CSimulatedAircraft &aircraft) { if (canLog()) { m_monitorWidget->appendReceivingCall(Q_FUNC_INFO, aircraft.toQString()); } - return CSimulatorCommon::changeRemoteAircraftEnabled(aircraft); + return ISimulator::changeRemoteAircraftEnabled(aircraft); } bool CSimulatorEmulated::updateOwnSimulatorCockpit(const CSimulatedAircraft &aircraft, const CIdentifier &originator) @@ -176,7 +176,7 @@ namespace BlackSimPlugin void CSimulatorEmulated::highlightAircraft(const CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const CTime &displayTime) { if (canLog()) { m_monitorWidget->appendReceivingCall(Q_FUNC_INFO, aircraftToHighlight.toQString(), boolToTrueFalse(enableHighlight), displayTime.toQString()); } - CSimulatorCommon::highlightAircraft(aircraftToHighlight, enableHighlight, displayTime); + ISimulator::highlightAircraft(aircraftToHighlight, enableHighlight, displayTime); } bool CSimulatorEmulated::parseCommandLine(const QString &commandLine, const CIdentifier &originator) @@ -308,7 +308,7 @@ namespace BlackSimPlugin int CSimulatorEmulated::physicallyRemoveAllRemoteAircraft() { if (canLog()) { m_monitorWidget->appendReceivingCall(Q_FUNC_INFO); } - return CSimulatorCommon::physicallyRemoveAllRemoteAircraft(); + return ISimulator::physicallyRemoveAllRemoteAircraft(); } bool CSimulatorEmulated::parseDetails(const CSimpleCommandParser &parser) diff --git a/src/plugins/simulator/emulated/simulatoremulated.h b/src/plugins/simulator/emulated/simulatoremulated.h index 48e436794..a648c1bfc 100644 --- a/src/plugins/simulator/emulated/simulatoremulated.h +++ b/src/plugins/simulator/emulated/simulatoremulated.h @@ -69,7 +69,7 @@ namespace BlackSimPlugin virtual bool isPhysicallyRenderedAircraft(const BlackMisc::Aviation::CCallsign &callsign) const override; virtual BlackMisc::Aviation::CCallsignSet physicallyRenderedAircraft() const override; - // functions just logged + // ----- functions just logged ------- virtual void highlightAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraftToHighlight, bool enableHighlight, const BlackMisc::PhysicalQuantities::CTime &displayTime) override; virtual bool logicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraft) override; virtual bool logicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) override; @@ -129,7 +129,7 @@ namespace BlackSimPlugin // just logged virtual int physicallyRemoveAllRemoteAircraft() override; - //! \copydoc BlackCore::CSimulatorCommon::parseDetails + //! \copydoc BlackCore::ISimulator::parseDetails virtual bool parseDetails(const BlackMisc::CSimpleCommandParser &parser) override; private: diff --git a/src/plugins/simulator/fscommon/simulatorfscommon.cpp b/src/plugins/simulator/fscommon/simulatorfscommon.cpp index 79f33e885..67acfb57f 100644 --- a/src/plugins/simulator/fscommon/simulatorfscommon.cpp +++ b/src/plugins/simulator/fscommon/simulatorfscommon.cpp @@ -123,7 +123,7 @@ namespace BlackSimPlugin CAirportList CSimulatorFsCommon::getAirportsInRange() const { if (!m_airportsInRangeFromSimulator.isEmpty()) { return m_airportsInRangeFromSimulator; } - return CSimulatorCommon::getAirportsInRange(); + return ISimulator::getAirportsInRange(); } void CSimulatorFsCommon::onSwiftDbAirportsRead() @@ -133,7 +133,7 @@ namespace BlackSimPlugin { m_airportsInRangeFromSimulator.updateMissingParts(webServiceAirports); } - CSimulatorCommon::onSwiftDbAirportsRead(); + ISimulator::onSwiftDbAirportsRead(); } } // namespace } // namespace diff --git a/src/plugins/simulator/fscommon/simulatorfscommon.h b/src/plugins/simulator/fscommon/simulatorfscommon.h index 21b8522ee..a6620161b 100644 --- a/src/plugins/simulator/fscommon/simulatorfscommon.h +++ b/src/plugins/simulator/fscommon/simulatorfscommon.h @@ -20,8 +20,6 @@ #include #include -#include - namespace BlackSimPlugin { namespace FsCommon diff --git a/src/plugins/simulator/plugincommon/simulatorplugincommon.cpp b/src/plugins/simulator/plugincommon/simulatorplugincommon.cpp index 15cfc413a..e6178cdc3 100644 --- a/src/plugins/simulator/plugincommon/simulatorplugincommon.cpp +++ b/src/plugins/simulator/plugincommon/simulatorplugincommon.cpp @@ -31,7 +31,7 @@ namespace BlackSimPlugin IWeatherGridProvider *weatherGridProvider, IClientProvider *clientProvider, QObject *parent) : - CSimulatorCommon(info, ownAircraftProvider, renderedAircraftProvider, weatherGridProvider, clientProvider, parent) + ISimulator(info, ownAircraftProvider, renderedAircraftProvider, weatherGridProvider, clientProvider, parent) { CSimulatorPluginCommon::registerHelp(); } @@ -75,19 +75,19 @@ namespace BlackSimPlugin this->showInterpolationDisplay(); return true; } - return CSimulatorCommon::parseDetails(parser); + return false; } void CSimulatorPluginCommon::unload() { this->deleteInterpolationDisplay(); - CSimulatorCommon::unload(); + ISimulator::unload(); } bool CSimulatorPluginCommon::disconnectFrom() { this->deleteInterpolationDisplay(); - return CSimulatorCommon::disconnectFrom(); + return ISimulator::disconnectFrom(); } void CSimulatorPluginCommon::registerHelp() diff --git a/src/plugins/simulator/plugincommon/simulatorplugincommon.h b/src/plugins/simulator/plugincommon/simulatorplugincommon.h index 6d4a73f7c..680b91478 100644 --- a/src/plugins/simulator/plugincommon/simulatorplugincommon.h +++ b/src/plugins/simulator/plugincommon/simulatorplugincommon.h @@ -12,7 +12,7 @@ #ifndef BLACKSIMPLUGIN_COMMON_SIMULATORPLUGINCOMMON_H #define BLACKSIMPLUGIN_COMMON_SIMULATORPLUGINCOMMON_H -#include "blackcore/simulatorcommon.h" +#include "blackcore/simulator.h" #include #include @@ -22,7 +22,7 @@ namespace BlackSimPlugin namespace Common { //! Common base class for simulator plugins - class CSimulatorPluginCommon : public BlackCore::CSimulatorCommon + class CSimulatorPluginCommon : public BlackCore::ISimulator { Q_OBJECT Q_INTERFACES(BlackCore::ISimulator) diff --git a/src/plugins/simulator/xplane/simulatorxplane.h b/src/plugins/simulator/xplane/simulatorxplane.h index 5126dbd2b..de5b6d222 100644 --- a/src/plugins/simulator/xplane/simulatorxplane.h +++ b/src/plugins/simulator/xplane/simulatorxplane.h @@ -15,8 +15,6 @@ #include "xplanempaircraft.h" #include "plugins/simulator/xplaneconfig/simulatorxplaneconfig.h" #include "plugins/simulator/plugincommon/simulatorplugincommon.h" -#include "blackcore/simulator.h" -#include "blackcore/simulatorcommon.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/data/modelcaches.h" #include "blackmisc/simulation/settings/simulatorsettings.h"