mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-05-06 02:16:04 +08:00
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:
518
src/blackcore/airspace_monitor.cpp
Normal file
518
src/blackcore/airspace_monitor.cpp
Normal 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
|
||||||
146
src/blackcore/airspace_monitor.h
Normal file
146
src/blackcore/airspace_monitor.h
Normal 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
|
||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user