refs #246 moved network context logic concerned with monitoring other airspace entities (aircraft, ATC) into a support class to which the context can delegate,

following the separation of concerns principle and making class invariants easier to identify and verify
This commit is contained in:
Mathew Sutcliffe
2014-06-12 15:29:22 +01:00
parent 837964fe20
commit d2bb36a322
6 changed files with 694 additions and 611 deletions

View File

@@ -0,0 +1,518 @@
/* Copyright (C) 2013 VATSIM Community / contributors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "airspace_monitor.h"
#include "blackmisc/project.h"
#include "blackmisc/indexvariantmap.h"
#include "network_vatlib.h" // for createFsipiCustomPacketData
namespace BlackCore
{
using namespace BlackMisc;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Network;
using namespace BlackMisc::Geo;
using namespace BlackMisc::PhysicalQuantities;
CAirspaceMonitor::CAirspaceMonitor(QObject *parent, INetwork *network, CVatsimBookingReader *bookings, CVatsimDataFileReader *dataFile)
: QObject(parent), m_network(network), m_vatsimBookingReader(bookings), m_vatsimDataFileReader(dataFile)
{
this->connect(this->m_network, &INetwork::atcPositionUpdate, this, &CAirspaceMonitor::atcPositionUpdate);
this->connect(this->m_network, &INetwork::atisReplyReceived, this, &CAirspaceMonitor::atisReceived);
this->connect(this->m_network, &INetwork::atisVoiceRoomReplyReceived, this, &CAirspaceMonitor::atisVoiceRoomReceived);
this->connect(this->m_network, &INetwork::atisLogoffTimeReplyReceived, this, &CAirspaceMonitor::atisLogoffTimeReceived);
this->connect(this->m_network, &INetwork::metarReplyReceived, this, &CAirspaceMonitor::metarReceived);
this->connect(this->m_network, &INetwork::flightPlanReplyReceived, this, &CAirspaceMonitor::flightplanReceived);
this->connect(this->m_network, &INetwork::realNameReplyReceived, this, &CAirspaceMonitor::realNameReplyReceived);
this->connect(this->m_network, &INetwork::icaoCodesReplyReceived, this, &CAirspaceMonitor::icaoCodesReceived);
this->connect(this->m_network, &INetwork::pilotDisconnected, this, &CAirspaceMonitor::pilotDisconnected);
this->connect(this->m_network, &INetwork::atcDisconnected, this, &CAirspaceMonitor::atcControllerDisconnected);
this->connect(this->m_network, &INetwork::aircraftPositionUpdate, this, &CAirspaceMonitor::aircraftUpdateReceived);
this->connect(this->m_network, &INetwork::frequencyReplyReceived, this, &CAirspaceMonitor::frequencyReceived);
this->connect(this->m_network, &INetwork::capabilitiesReplyReceived, this, &CAirspaceMonitor::capabilitiesReplyReceived);
this->connect(this->m_network, &INetwork::customPacketReceived, this, &CAirspaceMonitor::customPacketReceived);
this->connect(this->m_network, &INetwork::serverReplyReceived, this, &CAirspaceMonitor::serverReplyReceived);
this->connect(this->m_vatsimBookingReader, &CVatsimBookingReader::dataRead, this, &CAirspaceMonitor::receivedBookings);
}
CFlightPlan CAirspaceMonitor::loadFlightPlanFromNetwork(const CCallsign &callsign)
{
CFlightPlan plan;
// use cache, but not for own callsign (always reload)
if (this->m_flightPlanCache.contains(callsign)) plan = this->m_flightPlanCache[callsign];
if (!plan.wasSentOrLoaded() || plan.timeDiffSentOrLoadedMs() > 30 * 1000)
{
// outdated, or not in cache at all
this->m_network->sendFlightPlanQuery(callsign);
// with this little trick we try to make an asynchronous signal / slot
// based approach a synchronous return value
QTime waitForFlightPlan = QTime::currentTime().addMSecs(1000);
while (QTime::currentTime() < waitForFlightPlan)
{
// process some other events and hope network answer is received already
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
if (m_flightPlanCache.contains(callsign))
{
plan = this->m_flightPlanCache[callsign];
break;
}
}
}
return plan;
}
CUserList CAirspaceMonitor::getUsers() const
{
CUserList users;
foreach(CAtcStation station, this->m_atcStationsOnline)
{
CUser user = station.getController();
users.push_back(user);
}
foreach(CAircraft aircraft, this->m_aircraftsInRange)
{
CUser user = aircraft.getPilot();
users.push_back(user);
}
return users;
}
CUserList CAirspaceMonitor::getUsersForCallsigns(const CCallsignList &callsigns) const
{
CUserList users;
if (callsigns.isEmpty()) return users;
CCallsignList searchList(callsigns);
// myself, which is not in the lists below
CAircraft ownAircraft = this->m_ownAircraft;
if (!ownAircraft.getCallsign().isEmpty() && searchList.contains(ownAircraft.getCallsign()))
{
searchList.remove(ownAircraft.getCallsign());
users.push_back(ownAircraft.getPilot());
}
// do aircrafts first, this will handle most callsigns
foreach(CAircraft aircraft, this->m_aircraftsInRange)
{
if (searchList.isEmpty()) break;
CCallsign callsign = aircraft.getCallsign();
if (searchList.contains(callsign))
{
CUser user = aircraft.getPilot();
users.push_back(user);
searchList.remove(callsign);
}
}
foreach(CAtcStation station, this->m_atcStationsOnline)
{
if (searchList.isEmpty()) break;
CCallsign callsign = station.getCallsign();
if (searchList.contains(callsign))
{
CUser user = station.getController();
users.push_back(user);
searchList.remove(callsign);
}
}
// we might have unresolved callsigns
// these are the ones not in range
foreach(CCallsign callsign, searchList)
{
CUserList usersByCallsign = this->m_vatsimDataFileReader->getUsersForCallsign(callsign);
if (usersByCallsign.isEmpty())
{
CUser user;
user.setCallsign(callsign);
users.push_back(user);
}
else
{
users.push_back(usersByCallsign[0]);
}
}
return users;
}
CClientList CAirspaceMonitor::getOtherClientsForCallsigns(const CCallsignList &callsigns) const
{
CClientList clients;
if (callsigns.isEmpty()) return clients;
foreach(CCallsign callsign, callsigns)
{
clients.push_back(this->m_otherClients.findBy(&CClient::getCallsign, callsign));
}
return clients;
}
BlackMisc::Aviation::CInformationMessage CAirspaceMonitor::getMetar(const BlackMisc::Aviation::CAirportIcao &airportIcaoCode)
{
CInformationMessage metar;
if (airportIcaoCode.isEmpty()) return metar;
if (this->m_metarCache.contains(airportIcaoCode)) metar = this->m_metarCache[airportIcaoCode];
if (metar.isEmpty() || metar.timeDiffReceivedMs() > 10 * 1000)
{
// outdated, or not in cache at all
this->m_network->sendMetarQuery(airportIcaoCode);
// with this little trick we try to make an asynchronous signal / slot
// based approach a synchronous return value
QTime waitForMetar = QTime::currentTime().addMSecs(1000);
while (QTime::currentTime() < waitForMetar)
{
// process some other events and hope network answer is received already
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
if (m_metarCache.contains(airportIcaoCode))
{
metar = this->m_metarCache[airportIcaoCode];
break;
}
}
}
return metar;
}
CAtcStation CAirspaceMonitor::getAtcStationForComUnit(const CComSystem &comSystem)
{
CAtcStation station;
CAtcStationList stations = this->m_atcStationsOnline.findIfComUnitTunedIn25KHz(comSystem);
if (!stations.isEmpty())
{
stations.sortBy(&CAtcStation::getDistanceToPlane);
station = stations.front();
}
return station;
}
void CAirspaceMonitor::requestDataUpdates()
{
if (!this->m_network->isConnected()) return;
foreach(CAircraft aircraft, this->m_aircraftsInRange)
{
this->m_network->sendFrequencyQuery(aircraft.getCallsign());
this->m_network->sendIcaoCodesQuery(aircraft.getCallsign());
}
}
void CAirspaceMonitor::requestAtisUpdates()
{
if (!this->m_network->isConnected()) return;
foreach(CAtcStation station, this->m_atcStationsOnline)
{
this->m_network->sendAtisQuery(station.getCallsign());
}
}
void CAirspaceMonitor::realNameReplyReceived(const CCallsign &callsign, const QString &realname)
{
if (realname.isEmpty()) return;
CIndexVariantMap vm(CAtcStation::IndexControllerRealName, realname);
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
vm = CIndexVariantMap(CAircraft::IndexPilotRealName, realname);
this->m_aircraftsInRange.applyIf(&CAircraft::getCallsign, callsign, vm);
vm = CIndexVariantMap(CClient::IndexRealName, realname);
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
void CAirspaceMonitor::capabilitiesReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, quint32 flags)
{
if (callsign.isEmpty()) return;
CIndexVariantMap capabilities;
capabilities.addValue(CClient::FsdAtisCanBeReceived, (flags & INetwork::AcceptsAtisResponses));
capabilities.addValue(CClient::FsdWithInterimPositions, (flags & INetwork::SupportsInterimPosUpdates));
capabilities.addValue(CClient::FsdWithModelDescription, (flags & INetwork::SupportsModelDescriptions));
CIndexVariantMap vm(CClient::IndexCapabilities, capabilities.toQVariant());
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
void CAirspaceMonitor::customPacketReceived(const CCallsign &callsign, const QString &packet, const QStringList &data)
{
if (callsign.isEmpty() || data.isEmpty()) return;
if (packet.startsWith("FSIPIR", Qt::CaseInsensitive))
{
// Request of other client, I can get the other's model from that
// FsInn response is usually my model
QString model = data.last();
if (model.isEmpty()) return;
CIndexVariantMap vm(CClient::IndexQueriedModelString, QVariant(model));
if (!this->m_otherClients.contains(&CClient::getCallsign, callsign))
{
// with custom packets it can happen,
//the packet is received before any other packet
this->m_otherClients.push_back(CClient(callsign));
}
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
this->sendFsipiCustomPacket(callsign); // response
}
}
void CAirspaceMonitor::serverReplyReceived(const CCallsign &callsign, const QString &server)
{
if (callsign.isEmpty() || server.isEmpty()) return;
CIndexVariantMap vm(CClient::IndexServer, QVariant(server));
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
void CAirspaceMonitor::metarReceived(const QString &metarMessage)
{
if (metarMessage.length() < 10) return; // invalid
const QString icaoCode = metarMessage.left(4).toUpper();
const QString icaoCodeTower = icaoCode + "_TWR";
CCallsign callsignTower(icaoCodeTower);
CInformationMessage metar(CInformationMessage::METAR, metarMessage);
// add METAR to existing stations
CIndexVariantMap vm(CAtcStation::IndexMetar, metar.toQVariant());
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsignTower, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsignTower, vm);
this->m_metarCache.insert(icaoCode, metar);
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsignTower)) emit this->changedAtcStationsOnline();
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsignTower)) emit this->changedAtcStationsBooked();
}
void CAirspaceMonitor::flightplanReceived(const CCallsign &callsign, const CFlightPlan &flightPlan)
{
CFlightPlan plan(flightPlan);
plan.setWhenLastSentOrLoaded(QDateTime::currentDateTimeUtc());
this->m_flightPlanCache.insert(callsign, plan);
}
void CAirspaceMonitor::sendFsipiCustomPacket(const CCallsign &recipientCallsign) const
{
QStringList data = this->createFsipiCustomPacketData();
this->m_network->sendCustomPacket(recipientCallsign.asString(), "FSIPI", data);
}
void CAirspaceMonitor::sendFsipirCustomPacket(const CCallsign &recipientCallsign) const
{
QStringList data = this->createFsipiCustomPacketData();
this->m_network->sendCustomPacket(recipientCallsign.asString(), "FSIPIR", data);
}
QStringList CAirspaceMonitor::createFsipiCustomPacketData() const
{
CAircraft me = this->m_ownAircraft;
CAircraftIcao icao = me.getIcaoInfo();
QString modelString;
// FIXME (MS) simulator or ownaircraft context should send an ownAircraftModelChanged signal, so we wouldn't need to interrogate the simulator context here.
//if (this->getIContextSimulator())
//{
// if (this->getIContextSimulator()->isConnected()) modelString = this->getIContextSimulator()->getOwnAircraftModel().getQueriedModelString();
//}
if (modelString.isEmpty()) modelString = CProject::systemNameAndVersion();
QStringList data = CNetworkVatlib::createFsipiCustomPacketData(
"0", icao.getAirlineDesignator(), icao.getAircraftDesignator(),
"", "", "", "",
icao.getAircraftCombinedType(), modelString);
return data;
}
void CAirspaceMonitor::receivedBookings(const CAtcStationList &bookedStations)
{
this->m_atcStationsBooked.clear();
foreach(CAtcStation bookedStation, bookedStations)
{
// complete by VATSIM data file data
this->m_vatsimDataFileReader->getAtcStations().updateFromVatsimDataFileStation(bookedStation);
// exchange booking and online data
this->m_atcStationsOnline.mergeWithBooking(bookedStation);
// into list
this->m_atcStationsBooked.push_back(bookedStation);
}
}
void CAirspaceMonitor::atcPositionUpdate(const CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range)
{
CAtcStationList stationsWithCallsign = this->m_atcStationsOnline.findByCallsign(callsign);
if (stationsWithCallsign.isEmpty())
{
// new station
CAtcStation station;
station.setCallsign(callsign);
station.setRange(range);
station.setFrequency(frequency);
station.setPosition(position);
station.setOnline(true);
station.calculcateDistanceToPlane(this->m_ownAircraft.getPosition());
this->m_vatsimDataFileReader->getAtcStations().updateFromVatsimDataFileStation(station); // prefill
this->m_atcStationsOnline.push_back(station);
if (this->m_network->isConnected())
{
emit this->m_network->sendRealNameQuery(callsign);
emit this->m_network->sendAtisQuery(callsign); // request ATIS and voice rooms
emit this->m_network->sendServerQuery(callsign);
}
emit this->changedAtcStationsOnline();
// Remark: this->changedAtcStationOnlineConnectionStatus(station, true);
// will be sent in psFsdAtisVoiceRoomReceived
}
else
{
// update
CIndexVariantMap values;
values.addValue(CAtcStation::IndexFrequency, frequency);
values.addValue(CAtcStation::IndexPosition, position);
values.addValue(CAtcStation::IndexRange, range);
this->m_atcStationsOnline.applyIf(BlackMisc::Predicates::MemberEqual<CAtcStation>(&CAtcStation::getCallsign, callsign), values);
emit this->changedAtcStationsOnline();
}
}
void CAirspaceMonitor::atcControllerDisconnected(const CCallsign &callsign)
{
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign))
{
CAtcStation removeStation = this->m_atcStationsOnline.findByCallsign(callsign).front();
this->m_atcStationsOnline.removeIf(&CAtcStation::getCallsign, callsign);
emit this->changedAtcStationsOnline();
emit this->changedAtcStationOnlineConnectionStatus(removeStation, false);
}
// booked
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, CIndexVariantMap(CAtcStation::IndexIsOnline, QVariant(false)));
}
void CAirspaceMonitor::atisReceived(const CCallsign &callsign, const CInformationMessage &atisMessage)
{
if (callsign.isEmpty()) return;
CIndexVariantMap vm(CAtcStation::IndexAtis, atisMessage.toQVariant());
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
}
void CAirspaceMonitor::atisVoiceRoomReceived(const CCallsign &callsign, const QString &url)
{
QString trimmedUrl = url.trimmed();
CIndexVariantMap vm(CAtcStation::IndexVoiceRoomUrl, trimmedUrl);
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsign))
{
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
emit this->changedAtcStationsBooked();
}
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign))
{
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
CAtcStation station = this->m_atcStationsOnline.findFirstByCallsign(callsign);
emit this->changedAtcStationsBooked();
emit this->changedAtcStationOnlineConnectionStatus(station, true);
}
vm = CIndexVariantMap(CClient::IndexVoiceCapabilities, CVoiceCapabilities(CVoiceCapabilities::Voice).toQVariant());
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
void CAirspaceMonitor::atisLogoffTimeReceived(const CCallsign &callsign, const QString &zuluTime)
{
if (zuluTime.length() == 4)
{
// Logic to set logoff time
bool ok;
int h = zuluTime.left(2).toInt(&ok);
if (!ok) return;
int m = zuluTime.right(2).toInt(&ok);
if (!ok) return;
QDateTime logoffDateTime = QDateTime::currentDateTimeUtc();
logoffDateTime.setTime(QTime(h, m));
CIndexVariantMap vm(CAtcStation::IndexBookedUntil, logoffDateTime);
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
}
}
void CAirspaceMonitor::icaoCodesReceived(const CCallsign &callsign, const CAircraftIcao &icaoData)
{
// update
CIndexVariantMap vm(CAircraft::IndexIcao, icaoData.toQVariant());
if (!icaoData.hasAircraftDesignator())
{
// empty so far, try to fetch from data file
qDebug() << "Empty ICAO info for " << callsign << icaoData;
CAircraftIcao icaoDataDataFile = this->m_vatsimDataFileReader->getIcaoInfo(callsign);
if (!icaoDataDataFile.hasAircraftDesignator()) return; // give up!
vm = CIndexVariantMap(CAircraft::IndexIcao, icaoData.toQVariant());
}
this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual<CAircraft>(&CAircraft::getCallsign, callsign), vm);
emit this->changedAircraftsInRange();
}
void CAirspaceMonitor::aircraftUpdateReceived(const CCallsign &callsign, const CAircraftSituation &situation, const CTransponder &transponder)
{
CAircraftList list = this->m_aircraftsInRange.findByCallsign(callsign);
if (list.isEmpty())
{
// new aircraft
CAircraft aircraft;
aircraft.setCallsign(callsign);
aircraft.setSituation(situation);
aircraft.setTransponder(transponder);
aircraft.setCalculcatedDistanceToPosition(this->m_ownAircraft.getPosition()); // distance from myself
this->m_vatsimDataFileReader->updateWithVatsimDataFileData(aircraft);
this->m_aircraftsInRange.push_back(aircraft);
// and new client, there is a chance it has been created by
// custom packet first
if (!this->m_otherClients.contains(&CClient::getCallsign, callsign))
{
CClient c(callsign);
this->m_otherClients.push_back(c); // initial, will be filled by data later
}
if (this->m_network->isConnected())
{
// only if still connected
this->m_network->sendFrequencyQuery(callsign);
this->m_network->sendRealNameQuery(callsign);
this->m_network->sendIcaoCodesQuery(callsign);
this->m_network->sendCapabilitiesQuery(callsign);
this->m_network->sendServerQuery(callsign);
this->sendFsipirCustomPacket(callsign); // own aircraft model
}
}
else
{
// update
CLength distance = this->m_ownAircraft.calculcateDistanceToPosition(situation.getPosition());
distance.switchUnit(CLengthUnit::NM());
CIndexVariantMap vm;
vm.addValue(CAircraft::IndexTransponder, transponder);
vm.addValue(CAircraft::IndexSituation, situation);
vm.addValue(CAircraft::IndexDistance, distance);
this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual<CAircraft>(&CAircraft::getCallsign, callsign), vm);
}
emit this->changedAircraftsInRange();
emit changedAircraftSituation(callsign, situation);
}
void CAirspaceMonitor::pilotDisconnected(const CCallsign &callsign)
{
this->m_aircraftsInRange.removeIf(&CAircraft::getCallsign, callsign);
this->m_otherClients.removeIf(&CClient::getCallsign, callsign);
}
void CAirspaceMonitor::frequencyReceived(const CCallsign &callsign, const CFrequency &frequency)
{
// update
CIndexVariantMap vm(CAircraft::IndexFrequencyCom1, frequency.toQVariant());
this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual<CAircraft>(&CAircraft::getCallsign, callsign), vm);
emit this->changedAircraftsInRange();
}
} // namespace

View File

@@ -0,0 +1,146 @@
/* Copyright (C) 2013 VATSIM Community / contributors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BLACKCORE_AIRSPACE_MONITOR_H
#define BLACKCORE_AIRSPACE_MONITOR_H
#include "blackmisc/avatcstationlist.h"
#include "blackmisc/avaircraftlist.h"
#include "blackmisc/nwclientlist.h"
#include "blackmisc/avflightplan.h"
#include "blackmisc/nwuserlist.h"
#include "blackmisc/avcallsignlist.h"
#include "network.h"
#include "vatsimbookingreader.h"
#include "vatsimdatafilereader.h"
//! \file
namespace BlackCore
{
/*!
* Keeps track of other entities in the airspace: aircraft, ATC stations, etc.
*/
class CAirspaceMonitor : public QObject
{
Q_OBJECT
public:
//! Constructor
CAirspaceMonitor(QObject *parent, INetwork *network, CVatsimBookingReader *bookings, CVatsimDataFileReader *dataFile);
//! Returns the list of users we know about
BlackMisc::Network::CUserList getUsers() const;
//! Returns a list of the users corresponding to the given callsigns
BlackMisc::Network::CUserList getUsersForCallsigns(const BlackMisc::Aviation::CCallsignList &callsigns) const;
//! Returns the flightplan for the given callsign
BlackMisc::Aviation::CFlightPlan loadFlightPlanFromNetwork(const BlackMisc::Aviation::CCallsign &callsign);
//! Returns this list of other clients we know about
BlackMisc::Network::CClientList getOtherClients() const { return m_otherClients; }
//! Returns a list of other clients corresponding to the given callsigns
BlackMisc::Network::CClientList getOtherClientsForCallsigns(const BlackMisc::Aviation::CCallsignList &callsigns) const;
//! Returns a METAR for the given airport, if available
BlackMisc::Aviation::CInformationMessage getMetar(const BlackMisc::Aviation::CAirportIcao &airportIcaoCode);
//! Returns the current online ATC stations
BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const { return m_atcStationsOnline; }
//! Returns the current booked ATC stations
BlackMisc::Aviation::CAtcStationList getAtcStationsBooked() const { return m_atcStationsBooked; }
//! Returns the current aircraft in range
BlackMisc::Aviation::CAircraftList getAircraftInRange() const { return m_aircraftsInRange; }
//! Returns the closest ATC station operating on the given frequency, if any
BlackMisc::Aviation::CAtcStation getAtcStationForComUnit(const BlackMisc::Aviation::CComSystem &comSystem);
//! Request to update other clients' data from the network
void requestDataUpdates();
//! Request to update ATC stations' ATIS data from the network
void requestAtisUpdates();
signals:
//! Online ATC stations were changed
void changedAtcStationsOnline();
//! Booked ATC stations were changed
void changedAtcStationsBooked();
//! Connection status of an ATC station was changed
void changedAtcStationOnlineConnectionStatus(const BlackMisc::Aviation::CAtcStation &station, bool isConnected);
//! Aircrafts were changed
void changedAircraftsInRange();
//! An aircraft's situation was changed
void changedAircraftSituation(const BlackMisc::Aviation::CCallsign, const BlackMisc::Aviation::CAircraftSituation &situation);
//! Sent a status message
void statusMessage(const BlackMisc::CStatusMessage &msg);
public slots:
//! Own aircraft updated
void setOwnAircraft(const BlackMisc::Aviation::CAircraft &ownAircraft) { m_ownAircraft = ownAircraft; }
public:
//! Clear the contents
void clear()
{
m_atcStationsOnline.clear();
m_atcStationsBooked.clear();
m_aircraftsInRange.clear();
m_otherClients.clear();
m_metarCache.clear();
m_flightPlanCache.clear();
}
private:
BlackMisc::Aviation::CAtcStationList m_atcStationsOnline;
BlackMisc::Aviation::CAtcStationList m_atcStationsBooked;
BlackMisc::Aviation::CAircraftList m_aircraftsInRange;
BlackMisc::Network::CClientList m_otherClients;
QMap<BlackMisc::Aviation::CAirportIcao, BlackMisc::Aviation::CInformationMessage> m_metarCache;
QMap<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CFlightPlan> m_flightPlanCache;
BlackMisc::Aviation::CAircraft m_ownAircraft;
INetwork *m_network;
CVatsimBookingReader *m_vatsimBookingReader;
CVatsimDataFileReader *m_vatsimDataFileReader;
// FIXME (MS) should be in INetwork
void sendFsipiCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;
void sendFsipirCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;
QStringList createFsipiCustomPacketData() const;
private slots:
void realNameReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &realname);
void capabilitiesReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, quint32 flags);
void customPacketReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &packet, const QStringList &data);
void serverReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &server);
void metarReceived(const QString &metarMessage);
void flightplanReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CFlightPlan &flightPlan);
void receivedBookings(const BlackMisc::Aviation::CAtcStationList &bookedStations);
void atcPositionUpdate(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range);
void atcControllerDisconnected(const BlackMisc::Aviation::CCallsign &callsign);
void atisReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CInformationMessage &atisMessage);
void atisVoiceRoomReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &url);
void atisLogoffTimeReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &zuluTime);
void icaoCodesReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftIcao &icaoData);
void aircraftUpdateReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);
void pilotDisconnected(const BlackMisc::Aviation::CCallsign &callsign);
void frequencyReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency);
};
} // namespace
#endif // guard

View File

@@ -1,113 +0,0 @@
/* Copyright (C) 2013 VATSIM Community / authors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "context_network_impl.h"
#include "context_runtime.h"
#include "vatsimbookingreader.h"
#include "vatsimdatafilereader.h"
#include "blackmisc/predicates.h"
using namespace BlackMisc;
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Network;
using namespace BlackMisc::Geo;
namespace BlackCore
{
/*
* Aircraft info received
*/
void CContextNetwork::psFsdIcaoCodesReceived(const CCallsign &callsign, const CAircraftIcao &icaoData)
{
// update
CIndexVariantMap vm(CAircraft::IndexIcao, icaoData.toQVariant());
if (!icaoData.hasAircraftDesignator())
{
// empty so far, try to fetch from data file
qDebug() << "Empty ICAO info for " << callsign << icaoData;
CAircraftIcao icaoDataDataFile = this->m_vatsimDataFileReader->getIcaoInfo(callsign);
if (!icaoDataDataFile.hasAircraftDesignator()) return; // give up!
vm = CIndexVariantMap(CAircraft::IndexIcao, icaoData.toQVariant());
}
this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual<CAircraft>(&CAircraft::getCallsign, callsign), vm);
emit this->changedAircraftsInRange();
}
/*
* Aircraft update received
*/
void CContextNetwork::psFsdAircraftUpdateReceived(const CCallsign &callsign, const CAircraftSituation &situation, const CTransponder &transponder)
{
CAircraftList list = this->m_aircraftsInRange.findByCallsign(callsign);
if (list.isEmpty())
{
// new aircraft
CAircraft aircraft;
aircraft.setCallsign(callsign);
aircraft.setSituation(situation);
aircraft.setTransponder(transponder);
aircraft.setCalculcatedDistanceToPosition(this->ownAircraft().getPosition()); // distance from myself
this->m_vatsimDataFileReader->updateWithVatsimDataFileData(aircraft);
this->m_aircraftsInRange.push_back(aircraft);
// and new client, there is a chance it has been created by
// custom packet first
if (!this->m_otherClients.contains(&CClient::getCallsign, callsign))
{
CClient c(callsign);
this->m_otherClients.push_back(c); // initial, will be filled by data later
}
if (this->isConnected())
{
// only if still connected
this->m_network->sendFrequencyQuery(callsign);
this->m_network->sendRealNameQuery(callsign);
this->m_network->sendIcaoCodesQuery(callsign);
this->m_network->sendCapabilitiesQuery(callsign);
this->m_network->sendServerQuery(callsign);
this->sendFsipirCustomPacket(callsign); // own aircraft model
}
}
else
{
// update
CLength distance = this->ownAircraft().calculcateDistanceToPosition(situation.getPosition());
distance.switchUnit(CLengthUnit::NM());
CIndexVariantMap vm;
vm.addValue(CAircraft::IndexTransponder, transponder);
vm.addValue(CAircraft::IndexSituation, situation);
vm.addValue(CAircraft::IndexDistance, distance);
this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual<CAircraft>(&CAircraft::getCallsign, callsign), vm);
}
emit this->changedAircraftsInRange();
emit changedAircraftSituation(callsign, situation);
}
/*
* Pilot disconnected
*/
void CContextNetwork::psFsdPilotDisconnected(const CCallsign &callsign)
{
this->m_aircraftsInRange.removeIf(&CAircraft::getCallsign, callsign);
this->m_otherClients.removeIf(&CClient::getCallsign, callsign);
}
/*
* Frequency received
*/
void CContextNetwork::psFsdFrequencyReceived(const CCallsign &callsign, const CFrequency &frequency)
{
// update
CIndexVariantMap vm(CAircraft::IndexFrequencyCom1, frequency.toQVariant());
this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual<CAircraft>(&CAircraft::getCallsign, callsign), vm);
emit this->changedAircraftsInRange();
}
} // namespace

View File

@@ -43,19 +43,8 @@ namespace BlackCore
/* /*
* Update bookings * Update bookings
*/ */
void CContextNetwork::psReceivedBookings(const CAtcStationList &bookedStations) void CContextNetwork::psReceivedBookings(const CAtcStationList &)
{ {
this->m_atcStationsBooked.clear();
foreach(CAtcStation bookedStation, bookedStations)
{
// complete by VATSIM data file data
this->m_vatsimDataFileReader->getAtcStations().updateFromVatsimDataFileStation(bookedStation);
// exchange booking and online data
this->m_atcStationsOnline.mergeWithBooking(bookedStation);
// into list
this->m_atcStationsBooked.push_back(bookedStation);
}
// TODO (MS) no test for if (this->getIContextApplication()) here? // TODO (MS) no test for if (this->getIContextApplication()) here?
this->getIContextApplication()->sendStatusMessage(CStatusMessage::getInfoMessage("Read bookings from network", CStatusMessage::TypeTrafficNetwork)); this->getIContextApplication()->sendStatusMessage(CStatusMessage::getInfoMessage("Read bookings from network", CStatusMessage::TypeTrafficNetwork));
} }
@@ -67,14 +56,9 @@ namespace BlackCore
{ {
Q_ASSERT(this->m_network); Q_ASSERT(this->m_network);
if (!this->isConnected()) return; if (!this->isConnected()) return;
this->requestAtisUpdates();
// other updates this->requestAtisUpdates();
foreach(CAircraft aircraft, this->m_aircraftsInRange) this->m_airspace->requestDataUpdates();
{
this->m_network->sendFrequencyQuery(aircraft.getCallsign());
this->m_network->sendIcaoCodesQuery(aircraft.getCallsign());
}
} }
/* /*
@@ -84,10 +68,8 @@ namespace BlackCore
{ {
Q_ASSERT(this->m_network); Q_ASSERT(this->m_network);
if (!this->isConnected()) return; if (!this->isConnected()) return;
foreach(CAtcStation station, this->m_atcStationsOnline)
{ this->m_airspace->requestAtisUpdates();
this->m_network->sendAtisQuery(station.getCallsign());
}
} }
/* /*
@@ -96,29 +78,7 @@ namespace BlackCore
BlackMisc::Aviation::CInformationMessage CContextNetwork::getMetar(const BlackMisc::Aviation::CAirportIcao &airportIcaoCode) BlackMisc::Aviation::CInformationMessage CContextNetwork::getMetar(const BlackMisc::Aviation::CAirportIcao &airportIcaoCode)
{ {
this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO, airportIcaoCode.toQString()); this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO, airportIcaoCode.toQString());
CInformationMessage metar; return m_airspace->getMetar(airportIcaoCode);
if (airportIcaoCode.isEmpty()) return metar;
if (this->m_metarCache.contains(airportIcaoCode)) metar = this->m_metarCache[airportIcaoCode];
if (metar.isEmpty() || metar.timeDiffReceivedMs() > 10 * 1000)
{
// outdated, or not in cache at all
this->m_network->sendMetarQuery(airportIcaoCode);
// with this little trick we try to make an asynchronous signal / slot
// based approach a synchronous return value
QTime waitForMetar = QTime::currentTime().addMSecs(1000);
while (QTime::currentTime() < waitForMetar)
{
// process some other events and hope network answer is received already
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
if (m_metarCache.contains(airportIcaoCode))
{
metar = this->m_metarCache[airportIcaoCode];
break;
}
}
}
return metar;
} }
/* /*
@@ -126,22 +86,8 @@ namespace BlackCore
*/ */
CAtcStationList CContextNetwork::getSelectedAtcStations() const CAtcStationList CContextNetwork::getSelectedAtcStations() const
{ {
CAtcStation com1Station; CAtcStation com1Station = this->m_airspace->getAtcStationForComUnit(this->ownAircraft().getCom1System());
CAtcStation com2Station; CAtcStation com2Station = this->m_airspace->getAtcStationForComUnit(this->ownAircraft().getCom2System());
CAtcStationList stationsCom1 = this->m_atcStationsOnline.findIfComUnitTunedIn25KHz(this->ownAircraft().getCom1System());
CAtcStationList stationsCom2 = this->m_atcStationsOnline.findIfComUnitTunedIn25KHz(this->ownAircraft().getCom2System());
if (!stationsCom1.isEmpty())
{
stationsCom1.sortBy(&CAtcStation::getDistanceToPlane);
com1Station = stationsCom1.front();
}
if (!stationsCom2.isEmpty())
{
stationsCom2.sortBy(&CAtcStation::getDistanceToPlane);
com2Station = stationsCom2.front();
}
CAtcStationList selectedStations; CAtcStationList selectedStations;
selectedStations.push_back(com1Station); selectedStations.push_back(com1Station);
@@ -169,122 +115,4 @@ namespace BlackCore
return rooms; return rooms;
} }
/*
* ATC Position update
*/
void CContextNetwork::psFsdAtcPositionUpdate(const CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range)
{
CAtcStationList stationsWithCallsign = this->m_atcStationsOnline.findByCallsign(callsign);
if (stationsWithCallsign.isEmpty())
{
// new station
CAtcStation station;
station.setCallsign(callsign);
station.setRange(range);
station.setFrequency(frequency);
station.setPosition(position);
station.setOnline(true);
station.calculcateDistanceToPlane(this->ownAircraft().getPosition());
this->m_vatsimDataFileReader->getAtcStations().updateFromVatsimDataFileStation(station); // prefill
this->m_atcStationsOnline.push_back(station);
if (this->isConnected())
{
emit this->m_network->sendRealNameQuery(callsign);
emit this->m_network->sendAtisQuery(callsign); // request ATIS and voice rooms
emit this->m_network->sendServerQuery(callsign);
}
emit this->changedAtcStationsOnline();
// Remark: this->changedAtcStationOnlineConnectionStatus(station, true);
// will be sent in psFsdAtisVoiceRoomReceived
}
else
{
// update
CIndexVariantMap values;
values.addValue(CAtcStation::IndexFrequency, frequency);
values.addValue(CAtcStation::IndexPosition, position);
values.addValue(CAtcStation::IndexRange, range);
this->m_atcStationsOnline.applyIf(BlackMisc::Predicates::MemberEqual<CAtcStation>(&CAtcStation::getCallsign, callsign), values);
emit this->changedAtcStationsOnline();
}
}
/*
* ATC Controller disconnected
*/
void CContextNetwork::psFsdAtcControllerDisconnected(const CCallsign &callsign)
{
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign))
{
CAtcStation removeStation = this->m_atcStationsOnline.findByCallsign(callsign).front();
this->m_atcStationsOnline.removeIf(&CAtcStation::getCallsign, callsign);
emit this->changedAtcStationsOnline();
emit this->changedAtcStationOnlineConnectionStatus(removeStation, false);
}
// booked
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, CIndexVariantMap(CAtcStation::IndexIsOnline, QVariant(false)));
}
/*
* ATIS received
*/
void CContextNetwork::psFsdAtisReceived(const CCallsign &callsign, const CInformationMessage &atisMessage)
{
if (callsign.isEmpty()) return;
CIndexVariantMap vm(CAtcStation::IndexAtis, atisMessage.toQVariant());
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
}
/*
* ATIS (voice room part) received
*/
void CContextNetwork::psFsdAtisVoiceRoomReceived(const CCallsign &callsign, const QString &url)
{
QString trimmedUrl = url.trimmed();
CIndexVariantMap vm(CAtcStation::IndexVoiceRoomUrl, trimmedUrl);
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsign))
{
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
emit this->changedAtcStationsBooked();
}
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign))
{
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
CAtcStation station = this->m_atcStationsOnline.findFirstByCallsign(callsign);
emit this->changedAtcStationsBooked();
emit this->changedAtcStationOnlineConnectionStatus(station, true);
}
vm = CIndexVariantMap(CClient::IndexVoiceCapabilities, CVoiceCapabilities(CVoiceCapabilities::Voice).toQVariant());
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
/*
* ATIS (logoff time part) received
*/
void CContextNetwork::psFsdAtisLogoffTimeReceived(const CCallsign &callsign, const QString &zuluTime)
{
if (zuluTime.length() == 4)
{
// Logic to set logoff time
bool ok;
int h = zuluTime.left(2).toInt(&ok);
if (!ok) return;
int m = zuluTime.right(2).toInt(&ok);
if (!ok) return;
QDateTime logoffDateTime = QDateTime::currentDateTimeUtc();
logoffDateTime.setTime(QTime(h, m));
CIndexVariantMap vm(CAtcStation::IndexBookedUntil, logoffDateTime);
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsign)) emit this->changedAtcStationsBooked();
}
}
} // namespace } // namespace

View File

@@ -24,6 +24,7 @@ using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Aviation; using namespace BlackMisc::Aviation;
using namespace BlackMisc::Network; using namespace BlackMisc::Network;
using namespace BlackMisc::Geo; using namespace BlackMisc::Geo;
using namespace BlackMisc::Audio;
namespace BlackCore namespace BlackCore
{ {
@@ -32,7 +33,7 @@ namespace BlackCore
* Init this context * Init this context
*/ */
CContextNetwork::CContextNetwork(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : CContextNetwork::CContextNetwork(CRuntimeConfig::ContextMode mode, CRuntime *runtime) :
IContextNetwork(mode, runtime), m_network(nullptr), m_vatsimBookingReader(nullptr), m_vatsimDataFileReader(nullptr), m_dataUpdateTimer(nullptr) IContextNetwork(mode, runtime), m_network(nullptr), m_airspace(nullptr), m_vatsimBookingReader(nullptr), m_vatsimDataFileReader(nullptr), m_dataUpdateTimer(nullptr)
{ {
Q_ASSERT(this->getRuntime()); Q_ASSERT(this->getRuntime());
Q_ASSERT(this->getRuntime()->getIContextSettings()); Q_ASSERT(this->getRuntime()->getIContextSettings());
@@ -40,22 +41,7 @@ namespace BlackCore
// 1. Init by "network driver" // 1. Init by "network driver"
this->m_network = new CNetworkVatlib(this); this->m_network = new CNetworkVatlib(this);
this->connect(this->m_network, &INetwork::connectionStatusChanged, this, &CContextNetwork::psFsdConnectionStatusChanged); this->connect(this->m_network, &INetwork::connectionStatusChanged, this, &CContextNetwork::psFsdConnectionStatusChanged);
this->connect(this->m_network, &INetwork::atcPositionUpdate, this, &CContextNetwork::psFsdAtcPositionUpdate);
this->connect(this->m_network, &INetwork::atisReplyReceived, this, &CContextNetwork::psFsdAtisReceived);
this->connect(this->m_network, &INetwork::atisVoiceRoomReplyReceived, this, &CContextNetwork::psFsdAtisVoiceRoomReceived);
this->connect(this->m_network, &INetwork::atisLogoffTimeReplyReceived, this, &CContextNetwork::psFsdAtisLogoffTimeReceived);
this->connect(this->m_network, &INetwork::metarReplyReceived, this, &CContextNetwork::psFsdMetarReceived);
this->connect(this->m_network, &INetwork::flightPlanReplyReceived, this, &CContextNetwork::psFsdFlightplanReceived);
this->connect(this->m_network, &INetwork::realNameReplyReceived, this, &CContextNetwork::psFsdRealNameReplyReceived);
this->connect(this->m_network, &INetwork::icaoCodesReplyReceived, this, &CContextNetwork::psFsdIcaoCodesReceived);
this->connect(this->m_network, &INetwork::pilotDisconnected, this, &CContextNetwork::psFsdPilotDisconnected);
this->connect(this->m_network, &INetwork::atcDisconnected, this, &CContextNetwork::psFsdAtcControllerDisconnected);
this->connect(this->m_network, &INetwork::aircraftPositionUpdate, this, &CContextNetwork::psFsdAircraftUpdateReceived);
this->connect(this->m_network, &INetwork::frequencyReplyReceived, this, &CContextNetwork::psFsdFrequencyReceived);
this->connect(this->m_network, &INetwork::textMessagesReceived, this, &CContextNetwork::psFsdTextMessageReceived); this->connect(this->m_network, &INetwork::textMessagesReceived, this, &CContextNetwork::psFsdTextMessageReceived);
this->connect(this->m_network, &INetwork::capabilitiesReplyReceived, this, &CContextNetwork::psFsdCapabilitiesReplyReceived);
this->connect(this->m_network, &INetwork::customPacketReceived, this, &CContextNetwork::psFsdCustomPacketReceived);
this->connect(this->m_network, &INetwork::serverReplyReceived, this, &CContextNetwork::psFsdServerReplyReceived);
// 2. VATSIM bookings // 2. VATSIM bookings
this->m_vatsimBookingReader = new CVatsimBookingReader(this->getRuntime()->getIContextSettings()->getNetworkSettings().getBookingServiceUrl(), this); this->m_vatsimBookingReader = new CVatsimBookingReader(this->getRuntime()->getIContextSettings()->getNetworkSettings().getBookingServiceUrl(), this);
@@ -75,6 +61,15 @@ namespace BlackCore
this->connect(this->m_dataUpdateTimer, &QTimer::timeout, this, &CContextNetwork::requestDataUpdates); this->connect(this->m_dataUpdateTimer, &QTimer::timeout, this, &CContextNetwork::requestDataUpdates);
this->m_dataUpdateTimer->start(30 * 1000); this->m_dataUpdateTimer->start(30 * 1000);
// 5. Airspace contents
this->m_airspace = new CAirspaceMonitor(this, this->m_network, this->m_vatsimBookingReader, this->m_vatsimDataFileReader);
this->connect(this->m_airspace, &CAirspaceMonitor::changedAtcStationsOnline, this, &CContextNetwork::changedAtcStationsOnline);
this->connect(this->m_airspace, &CAirspaceMonitor::changedAtcStationsBooked, this, &CContextNetwork::changedAtcStationsBooked);
this->connect(this->m_airspace, &CAirspaceMonitor::changedAtcStationOnlineConnectionStatus, this, &CContextNetwork::changedAtcStationOnlineConnectionStatus);
this->connect(this->m_airspace, &CAirspaceMonitor::changedAircraftsInRange, this, &CContextNetwork::changedAircraftsInRange);
this->connect(this->m_airspace, &CAirspaceMonitor::changedAircraftSituation, this, &CContextNetwork::changedAircraftSituation);
this->connect(this->getIContextOwnAircraft(), &IContextOwnAircraft::changedAircraft, this->m_airspace, &CAirspaceMonitor::setOwnAircraft);
// FIXME (MS) conditional increases the number of scenarios which must be considered and continuously tested // FIXME (MS) conditional increases the number of scenarios which must be considered and continuously tested
if (this->getIContextApplication()) if (this->getIContextApplication())
{ {
@@ -144,11 +139,7 @@ namespace BlackCore
if (this->m_network->isConnected()) if (this->m_network->isConnected())
{ {
this->m_network->terminateConnection(); this->m_network->terminateConnection();
this->m_aircraftsInRange.clear(); this->m_airspace->clear();
this->m_atcStationsBooked.clear();
this->m_atcStationsOnline.clear();
this->m_otherClients.clear();
this->m_metarCache.clear();
msgs.push_back(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityInfo, "Connection terminating")); msgs.push_back(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityInfo, "Connection terminating"));
} }
else else
@@ -189,30 +180,7 @@ namespace BlackCore
CFlightPlan CContextNetwork::loadFlightPlanFromNetwork(const BlackMisc::Aviation::CCallsign &callsign) const CFlightPlan CContextNetwork::loadFlightPlanFromNetwork(const BlackMisc::Aviation::CCallsign &callsign) const
{ {
this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO); this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO);
CFlightPlan plan; return this->m_airspace->loadFlightPlanFromNetwork(callsign);
// use cache, but not for own callsign (always reload)
if (this->m_flightPlanCache.contains(callsign)) plan = this->m_flightPlanCache[callsign];
if (!plan.wasSentOrLoaded() || plan.timeDiffSentOrLoadedMs() > 30 * 1000)
{
// outdated, or not in cache at all
this->m_network->sendFlightPlanQuery(callsign);
// with this little trick we try to make an asynchronous signal / slot
// based approach a synchronous return value
QTime waitForFlightPlan = QTime::currentTime().addMSecs(1000);
while (QTime::currentTime() < waitForFlightPlan)
{
// process some other events and hope network answer is received already
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
if (m_flightPlanCache.contains(callsign))
{
plan = this->m_flightPlanCache[callsign];
break;
}
}
}
return plan;
} }
/* /*
@@ -220,18 +188,7 @@ namespace BlackCore
*/ */
CUserList CContextNetwork::getUsers() const CUserList CContextNetwork::getUsers() const
{ {
CUserList users; return this->m_airspace->getUsers();
foreach(CAtcStation station, this->m_atcStationsOnline)
{
CUser user = station.getController();
users.push_back(user);
}
foreach(CAircraft aircraft, this->m_aircraftsInRange)
{
CUser user = aircraft.getPilot();
users.push_back(user);
}
return users;
} }
/* /*
@@ -241,58 +198,7 @@ namespace BlackCore
{ {
CUserList users; CUserList users;
if (callsigns.isEmpty()) return users; if (callsigns.isEmpty()) return users;
CCallsignList searchList(callsigns); return this->m_airspace->getUsersForCallsigns(callsigns);
// myself, which is not in the lists below
CAircraft ownAircraft = this->ownAircraft();
if (!ownAircraft.getCallsign().isEmpty() && searchList.contains(ownAircraft.getCallsign()))
{
searchList.remove(ownAircraft.getCallsign());
users.push_back(ownAircraft.getPilot());
}
// do aircrafts first, this will handle most callsigns
foreach(CAircraft aircraft, this->m_aircraftsInRange)
{
if (searchList.isEmpty()) break;
CCallsign callsign = aircraft.getCallsign();
if (searchList.contains(callsign))
{
CUser user = aircraft.getPilot();
users.push_back(user);
searchList.remove(callsign);
}
}
foreach(CAtcStation station, this->m_atcStationsOnline)
{
if (searchList.isEmpty()) break;
CCallsign callsign = station.getCallsign();
if (searchList.contains(callsign))
{
CUser user = station.getController();
users.push_back(user);
searchList.remove(callsign);
}
}
// we might have unresolved callsigns
// these are the ones not in range
foreach(CCallsign callsign, searchList)
{
CUserList usersByCallsign = this->m_vatsimDataFileReader->getUsersForCallsign(callsign);
if (usersByCallsign.isEmpty())
{
CUser user;
user.setCallsign(callsign);
users.push_back(user);
}
else
{
users.push_back(usersByCallsign[0]);
}
}
return users;
} }
/* /*
@@ -312,7 +218,7 @@ namespace BlackCore
*/ */
CClientList CContextNetwork::getOtherClients() const CClientList CContextNetwork::getOtherClients() const
{ {
return this->m_otherClients; return this->m_airspace->getOtherClients();
} }
/* /*
@@ -320,13 +226,7 @@ namespace BlackCore
*/ */
CClientList CContextNetwork::getOtherClientsForCallsigns(const CCallsignList &callsigns) const CClientList CContextNetwork::getOtherClientsForCallsigns(const CCallsignList &callsigns) const
{ {
CClientList clients; return this->m_airspace->getOtherClientsForCallsigns(callsigns);
if (callsigns.isEmpty()) return clients;
foreach(CCallsign callsign, callsigns)
{
clients.push_back(this->m_otherClients.findBy(&CClient::getCallsign, callsign));
}
return clients;
} }
/* /*
@@ -360,24 +260,6 @@ namespace BlackCore
emit this->connectionStatusChanged(from, to, message); emit this->connectionStatusChanged(from, to, message);
} }
/*
* Name query
*/
void CContextNetwork::psFsdRealNameReplyReceived(const CCallsign &callsign, const QString &realname)
{
this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO, { callsign.toQString(), realname });
if (realname.isEmpty()) return;
CIndexVariantMap vm(CAtcStation::IndexControllerRealName, realname);
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm);
vm = CIndexVariantMap(CAircraft::IndexPilotRealName, realname);
this->m_aircraftsInRange.applyIf(&CAircraft::getCallsign, callsign, vm);
vm = CIndexVariantMap(CClient::IndexRealName, realname);
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
/* /*
* Data file (VATSIM) has been read * Data file (VATSIM) has been read
*/ */
@@ -397,115 +279,6 @@ namespace BlackCore
this->textMessagesReceived(messages); // relay this->textMessagesReceived(messages); // relay
} }
/*
* Capabilities
*/
void CContextNetwork::psFsdCapabilitiesReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, quint32 flags)
{
if (callsign.isEmpty()) return;
CIndexVariantMap capabilities;
capabilities.addValue(CClient::FsdAtisCanBeReceived, (flags & CNetworkVatlib::AcceptsAtisResponses));
capabilities.addValue(CClient::FsdWithInterimPositions, (flags & CNetworkVatlib::SupportsInterimPosUpdates));
capabilities.addValue(CClient::FsdWithModelDescription, (flags & CNetworkVatlib::SupportsModelDescriptions));
CIndexVariantMap vm(CClient::IndexCapabilities, capabilities.toQVariant());
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
/*
* Custom packets
*/
void CContextNetwork::psFsdCustomPacketReceived(const CCallsign &callsign, const QString &packet, const QStringList &data)
{
if (callsign.isEmpty() || data.isEmpty()) return;
if (packet.startsWith("FSIPIR", Qt::CaseInsensitive))
{
// Request of other client, I can get the other's model from that
// FsInn response is usually my model
QString model = data.last();
if (model.isEmpty()) return;
CIndexVariantMap vm(CClient::IndexQueriedModelString, QVariant(model));
if (!this->m_otherClients.contains(&CClient::getCallsign, callsign))
{
// with custom packets it can happen,
//the packet is received before any other packet
this->m_otherClients.push_back(CClient(callsign));
}
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
this->sendFsipiCustomPacket(callsign); // response
}
}
/*
* Host
*/
void CContextNetwork::psFsdServerReplyReceived(const CCallsign &callsign, const QString &server)
{
if (callsign.isEmpty() || server.isEmpty()) return;
CIndexVariantMap vm(CClient::IndexServer, QVariant(server));
this->m_otherClients.applyIf(&CClient::getCallsign, callsign, vm);
}
/*
* Metar received
*/
void CContextNetwork::psFsdMetarReceived(const QString &metarMessage)
{
if (metarMessage.length() < 10) return; // invalid
const QString icaoCode = metarMessage.left(4).toUpper();
const QString icaoCodeTower = icaoCode + "_TWR";
CCallsign callsignTower(icaoCodeTower);
CInformationMessage metar(CInformationMessage::METAR, metarMessage);
// add METAR to existing stations
CIndexVariantMap vm(CAtcStation::IndexMetar, metar.toQVariant());
this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsignTower, vm);
this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsignTower, vm);
this->m_metarCache.insert(icaoCode, metar);
if (this->m_atcStationsOnline.contains(&CAtcStation::getCallsign, callsignTower)) emit this->changedAtcStationsOnline();
if (this->m_atcStationsBooked.contains(&CAtcStation::getCallsign, callsignTower)) emit this->changedAtcStationsBooked();
}
/*
* Flight plan received
*/
void CContextNetwork::psFsdFlightplanReceived(const CCallsign &callsign, const CFlightPlan &flightPlan)
{
CFlightPlan plan(flightPlan);
plan.setWhenLastSentOrLoaded(QDateTime::currentDateTimeUtc());
this->m_flightPlanCache.insert(callsign, plan);
}
void CContextNetwork::sendFsipiCustomPacket(const CCallsign &recipientCallsign) const
{
QStringList data = this->createFsipiCustomPacketData();
this->m_network->sendCustomPacket(recipientCallsign.asString(), "FSIPI", data);
}
void CContextNetwork::sendFsipirCustomPacket(const CCallsign &recipientCallsign) const
{
QStringList data = this->createFsipiCustomPacketData();
this->m_network->sendCustomPacket(recipientCallsign.asString(), "FSIPIR", data);
}
QStringList CContextNetwork::createFsipiCustomPacketData() const
{
CAircraft me = this->ownAircraft();
CAircraftIcao icao = me.getIcaoInfo();
QString modelString;
// FIXME (MS) simulator context should send model string to own aircraft context, so we wouldn't need to interrogate the simulator context here.
if (this->getIContextSimulator())
{
if (this->getIContextSimulator()->isConnected()) modelString = this->getIContextSimulator()->getOwnAircraftModel().getQueriedModelString();
}
if (modelString.isEmpty()) modelString = CProject::systemNameAndVersion();
QStringList data = CNetworkVatlib::createFsipiCustomPacketData(
"0", icao.getAirlineDesignator(), icao.getAircraftDesignator(),
"", "", "", "",
icao.getAircraftCombinedType(), modelString);
return data;
}
const CAircraft &CContextNetwork::ownAircraft() const const CAircraft &CContextNetwork::ownAircraft() const
{ {
Q_ASSERT(this->getRuntime()); Q_ASSERT(this->getRuntime());

View File

@@ -11,6 +11,7 @@
#include "blackcore/context_runtime.h" #include "blackcore/context_runtime.h"
#include "blackcore/dbus_server.h" #include "blackcore/dbus_server.h"
#include "blackcore/network.h" #include "blackcore/network.h"
#include "blackcore/airspace_monitor.h"
#include "blackmisc/avatcstationlist.h" #include "blackmisc/avatcstationlist.h"
#include "blackmisc/setnetwork.h" #include "blackmisc/setnetwork.h"
#include "blackmisc/nwclientlist.h" #include "blackmisc/nwclientlist.h"
@@ -45,21 +46,21 @@ namespace BlackCore
virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const override virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const override
{ {
this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO); this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO);
return m_atcStationsOnline; return this->m_airspace->getAtcStationsOnline();
} }
//! \copydoc IContextNetwork::getAtcStationsBooked() //! \copydoc IContextNetwork::getAtcStationsBooked()
virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsBooked() const override virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsBooked() const override
{ {
this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO); this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO);
return m_atcStationsBooked; return this->m_airspace->getAtcStationsBooked();
} }
//! \copydoc IContextNetwork::getAircraftsInRange() //! \copydoc IContextNetwork::getAircraftsInRange()
virtual const BlackMisc::Aviation::CAircraftList getAircraftsInRange() const override virtual const BlackMisc::Aviation::CAircraftList getAircraftsInRange() const override
{ {
this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO); this->getRuntime()->logSlot(c_logContext, Q_FUNC_INFO);
return m_aircraftsInRange; return this->m_airspace->getAircraftInRange();
} }
//! \copydoc IContextNetwork::connectToNetwork() //! \copydoc IContextNetwork::connectToNetwork()
@@ -125,13 +126,8 @@ namespace BlackCore
private: private:
static const auto c_logContext = CRuntime::LogForNetwork; static const auto c_logContext = CRuntime::LogForNetwork;
BlackMisc::Aviation::CAtcStationList m_atcStationsOnline; CAirspaceMonitor *m_airspace;
BlackMisc::Aviation::CAtcStationList m_atcStationsBooked;
BlackMisc::Aviation::CAircraftList m_aircraftsInRange;
BlackMisc::Network::CClientList m_otherClients;
BlackCore::INetwork *m_network; BlackCore::INetwork *m_network;
QMap<BlackMisc::Aviation::CAirportIcao, BlackMisc::Aviation::CInformationMessage> m_metarCache /*!< Keep METARs for a while */;
QMap<BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CFlightPlan> m_flightPlanCache /*!< Keep flight plans for a while */;
// for reading XML and VATSIM data files // for reading XML and VATSIM data files
CVatsimBookingReader *m_vatsimBookingReader; CVatsimBookingReader *m_vatsimBookingReader;
@@ -146,15 +142,6 @@ namespace BlackCore
return this->getRuntime()->getIContextSettings()->getNetworkSettings(); return this->getRuntime()->getIContextSettings()->getNetworkSettings();
} }
//! Send FsInn custom packet
void sendFsipiCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;
//! Send FsInn custom packet
void sendFsipirCustomPacket(const BlackMisc::Aviation::CCallsign &recipientCallsign) const;
//! Custom packet data based on own aircraft / model
QStringList createFsipiCustomPacketData() const;
//! Own aircraft //! Own aircraft
const BlackMisc::Aviation::CAircraft &ownAircraft() const; const BlackMisc::Aviation::CAircraft &ownAircraft() const;
@@ -175,64 +162,8 @@ namespace BlackCore
*/ */
void psFsdConnectionStatusChanged(INetwork::ConnectionStatus from, INetwork::ConnectionStatus to, const QString &message); void psFsdConnectionStatusChanged(INetwork::ConnectionStatus from, INetwork::ConnectionStatus to, const QString &message);
//! ATC position update
void psFsdAtcPositionUpdate(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range);
/*!
* \brief Controller disconnected
* \param callsign callsign of controller
*/
void psFsdAtcControllerDisconnected(const BlackMisc::Aviation::CCallsign &callsign);
//! ATIS received
void psFsdAtisReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CInformationMessage &atisMessage);
/*!
* \brief ATIS received (voice room part)
* \param callsign station callsign
* \param url voice room's URL
*/
void psFsdAtisVoiceRoomReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &url);
/*!
* \brief ATIS received (logoff time part)
* \param callsign station callsign
* \param zuluTime UTC time, when controller will logoff
*/
void psFsdAtisLogoffTimeReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &zuluTime);
//! METAR received
void psFsdMetarReceived(const QString &metarMessage);
//! Flight plan received
void psFsdFlightplanReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CFlightPlan &flightplan);
//! Realname recevied
void psFsdRealNameReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &realname);
//! Plane ICAO codes received
void psFsdIcaoCodesReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftIcao &icaoData);
//! Aircraft position update received
void psFsdAircraftUpdateReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder);
//! Pilot disconnected
void psFsdPilotDisconnected(const BlackMisc::Aviation::CCallsign &callsign);
//! Frequency received
void psFsdFrequencyReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency);
//! Radio text messages received //! Radio text messages received
void psFsdTextMessageReceived(const BlackMisc::Network::CTextMessageList &messages); void psFsdTextMessageReceived(const BlackMisc::Network::CTextMessageList &messages);
//! Capabilities received
void psFsdCapabilitiesReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, quint32 flags);
//! Custom packet
void psFsdCustomPacketReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &package, const QStringList &data);
//! Server reply received
void psFsdServerReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &server);
}; };
} }