refs #241 FS9 implementation of the ISimulator interface

Covers most important interface methods. Still TODO:
- getAirportsInRange
- Time sync
- Updating COM units from context
This commit is contained in:
Roland Winklmeier
2014-07-26 19:06:21 +02:00
parent c78db8be58
commit 9020c65681
2 changed files with 491 additions and 0 deletions

View File

@@ -0,0 +1,325 @@
/* Copyright (C) 2014
* swift project community / contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
#include "fs9.h"
#include "blacksimplugin_freefunctions.h"
#include "simulator_fs9.h"
#include "fs9_host.h"
#include "fs9_client.h"
#include "multiplayer_packets.h"
#include "multiplayer_packet_parser.h"
#include "blacksim/simulatorinfo.h"
#include "blackmisc/project.h"
#include <QTimer>
#include <algorithm>
using namespace BlackMisc::Aviation;
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Geo;
using namespace BlackSim;
using namespace BlackSimPlugin::Fs9;
namespace BlackSimPlugin
{
namespace Fs9
{
BlackCore::ISimulator *CSimulatorFs9Factory::create(QObject *parent)
{
registerMetadata();
return new Fs9::CSimulatorFs9(parent);
}
BlackSim::CSimulatorInfo CSimulatorFs9Factory::getSimulatorInfo() const
{
return CSimulatorInfo::FS9();
}
CSimulatorFs9::CSimulatorFs9(QObject *parent) :
ISimulator(parent),
m_fs9Host(new CFs9Host),
m_hostThread(this),
m_simulatorInfo(CSimulatorInfo::FS9()),
m_fsuipc(new FsCommon::CFsuipc())
{
// We move the host thread already in the constructor
m_fs9Host->moveToThread(&m_hostThread);
connect(&m_hostThread, &QThread::started, m_fs9Host, &CFs9Host::init);
connect(m_fs9Host, &CFs9Host::customPacketReceived, this, &CSimulatorFs9::processFs9Message);
connect(m_fs9Host, &CFs9Host::statusChanged, this, &CSimulatorFs9::changeHostStatus);
connect(&m_hostThread, &QThread::finished, m_fs9Host, &CFs9Host::deleteLater);
connect(&m_hostThread, &QThread::finished, &m_hostThread, &QThread::deleteLater);
m_hostThread.start();
}
CSimulatorFs9::~CSimulatorFs9()
{
m_hostThread.quit();
m_hostThread.wait(1000);
}
bool CSimulatorFs9::isConnected() const
{
return m_fs9Host->isConnected();
}
bool CSimulatorFs9::connectTo()
{
m_fsuipc->connect(); // connect FSUIPC too
// FIXME: This does start hosting only. Add lobby connection here.
return true;
}
void CSimulatorFs9::asyncConnectTo()
{
// Since we are running the host in its own thread, it is async anyway
connectTo();
}
bool CSimulatorFs9::disconnectFrom()
{
disconnectAllClients();
// We tell the host to terminate and stop the thread afterwards
QMetaObject::invokeMethod(m_fs9Host, "stopHosting");
emit statusChanged(ISimulator::Disconnected);
m_fsuipc->disconnect();
return false;
}
bool CSimulatorFs9::canConnect()
{
return true;
}
void CSimulatorFs9::addRemoteAircraft(const CCallsign &callsign, const QString & /* type */, const CAircraftSituation & /*initialSituation*/ )
{
// Create a new client thread, set update frequency to 25 ms and start it
QThread *clientThread = new QThread(this);
CFs9Client *client = new CFs9Client(callsign.toQString(), CTime(25, CTimeUnit::ms()));
client->setPlayerUserId(m_fs9Host->getPlayerUserId());
client->moveToThread(clientThread);
connect(clientThread, &QThread::started, client, &CFs9Client::init);
connect(client, &CFs9Client::clientTimedOut, this, &CSimulatorFs9::removeAircraft);
m_fs9ClientThreads.insert(client, clientThread);
m_hashFs9Clients.insert(callsign, client);
clientThread->start();
}
void CSimulatorFs9::addAircraftSituation(const CCallsign &callsign, const CAircraftSituation &situation)
{
// If this is a new guy, add him to the session
if (!m_hashFs9Clients.contains(callsign))
{
// Only add a maximum number of 20 clients.
// FIXME: We need a smart method to get the 20 nearest aircrafts. If someone logs in
// nearby we need to kick out the one with max distance.
if (m_hashFs9Clients.size() < 20)
addRemoteAircraft(callsign, "Boeing 737-400 Paint1", situation);
}
// otherwise just add the new position
CFs9Client *client = m_hashFs9Clients.value(callsign);
if (!client)
return;
client->addAircraftSituation(situation);
}
void CSimulatorFs9::removeRemoteAircraft(const CCallsign &callsign)
{
if(!m_hashFs9Clients.contains(callsign))
return;
CFs9Client *fs9Client = m_hashFs9Clients.value(callsign);
Q_ASSERT(m_fs9ClientThreads.contains(fs9Client));
QThread *fs9ClientThread = m_fs9ClientThreads.value(fs9Client);
QMetaObject::invokeMethod(fs9Client, "disconnectFrom");
m_fs9ClientThreads.remove(fs9Client);
m_hashFs9Clients.remove(callsign);
fs9ClientThread->wait(100);
/*fs9ClientThread->deleteLater();
fs9Client->deleteLater();*/
}
bool CSimulatorFs9::updateOwnSimulatorCockpit(const CAircraft &ownAircraft)
{
CComSystem newCom1 = ownAircraft.getCom1System();
CComSystem newCom2 = ownAircraft.getCom2System();
CTransponder newTransponder = ownAircraft.getTransponder();
bool changed = false;
if (newCom1 != this->m_ownAircraft.getCom1System())
{
if (newCom1.getFrequencyActive() != this->m_ownAircraft.getCom1System().getFrequencyActive())
{
}
if (newCom1.getFrequencyStandby() != this->m_ownAircraft.getCom1System().getFrequencyStandby())
{
}
this->m_ownAircraft.setCom1System(newCom1);
changed = true;
}
if (newCom2 != this->m_ownAircraft.getCom2System())
{
if (newCom2.getFrequencyActive() != this->m_ownAircraft.getCom2System().getFrequencyActive())
{
}
if (newCom2.getFrequencyStandby() != this->m_ownAircraft.getCom2System().getFrequencyStandby())
{
}
this->m_ownAircraft.setCom2System(newCom2);
changed = true;
}
if (newTransponder != this->m_ownAircraft.getTransponder())
{
if (newTransponder.getTransponderCode() != this->m_ownAircraft.getTransponder().getTransponderCode())
{
changed = true;
}
this->m_ownAircraft.setTransponder(newTransponder);
}
// bye
return changed;
}
CSimulatorInfo CSimulatorFs9::getSimulatorInfo() const
{
return this->m_simulatorInfo;
}
void CSimulatorFs9::displayStatusMessage(const BlackMisc::CStatusMessage &message) const
{
QMetaObject::invokeMethod(m_fs9Host, "sendTextMessage", Q_ARG(QString, message.toQString()));
}
CAirportList CSimulatorFs9::getAirportsInRange() const
{
return this->m_airportsInRange;
}
void CSimulatorFs9::setTimeSynchronization(bool enable, BlackMisc::PhysicalQuantities::CTime offset)
{
this->m_syncTime = enable;
this->m_syncTimeOffset = offset;
}
void CSimulatorFs9::timerEvent(QTimerEvent * /* event */)
{
ps_dispatch();
}
void CSimulatorFs9::ps_dispatch()
{
if (m_fsuipc) m_fsuipc->process();
updateOwnAircraftFromSim(m_fsuipc->getOwnAircraft());
}
void CSimulatorFs9::processFs9Message(const QByteArray &message)
{
CFs9Sdk::MULTIPLAYER_PACKET_ID messageType = MultiPlayerPacketParser::readType(message);
switch (messageType)
{
case CFs9Sdk::MULTIPLAYER_PACKET_ID_CHANGE_PLAYER_PLANE:
{
MPChangePlayerPlane mpChangePlayerPlane;
MultiPlayerPacketParser::readMessage(message, mpChangePlayerPlane);
changeOwnAircraftModel(mpChangePlayerPlane.aircraft_name);
break;
}
case CFs9Sdk::MULTIPLAYER_PACKET_ID_POSITION_VELOCITY:
{
MPPositionVelocity mpPositionVelocity;
MultiPlayerPacketParser::readMessage(message, mpPositionVelocity);
m_ownAircraft.setSituation(aircraftSituationfromFS9(mpPositionVelocity));
break;
}
case CFs9Sdk::MPCHAT_PACKET_ID_CHAT_TEXT_SEND:
{
MPChatText mpChatText;
MultiPlayerPacketParser::readMessage(message, mpChatText);
break;
}
default:
break;
}
}
void CSimulatorFs9::changeOwnAircraftModel(const QString &modelname)
{
m_aircraftModel.setQueriedModelString(modelname);
emit aircraftModelChanged(m_aircraftModel);
}
void CSimulatorFs9::changeHostStatus(CFs9Host::HostStatus status)
{
switch (status)
{
case CFs9Host::Hosting:
{
m_isHosting = true;
startTimer(50);
emit statusChanged(Connected);
break;
}
case CFs9Host::Terminated:
{
qDebug() << "Quitting thread";
m_hostThread.quit();
m_isHosting = false;
emit statusChanged(Disconnected);
break;
}
default:
break;
}
}
void CSimulatorFs9::removeAircraft(const QString &callsign)
{
removeRemoteAircraft(callsign);
}
void CSimulatorFs9::updateOwnAircraftFromSim(const CAircraft &ownAircraft)
{
m_ownAircraft.setCom1System(ownAircraft.getCom1System());
m_ownAircraft.setCom2System(ownAircraft.getCom2System());
m_ownAircraft.setTransponder(ownAircraft.getTransponder());
}
void CSimulatorFs9::disconnectAllClients()
{
// Stop all FS9 client threads
for (auto fs9Client : m_hashFs9Clients.keys())
{
removeRemoteAircraft(fs9Client);
}
}
}
}

View File

@@ -0,0 +1,166 @@
/* Copyright (C) 2014
* swift project community / contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
#ifndef BLACKSIMPLUGIN_SIMULATOR_FS9_H
#define BLACKSIMPLUGIN_SIMULATOR_FS9_H
#include "fs9_host.h"
#include "fs9_client.h"
#include "../fscommon/fsuipc.h"
#include "blackcore/simulator.h"
#include "blackcore/interpolator_linear.h"
#include "blackmisc/avaircraft.h"
#include "blackmisc/nwaircraftmodel.h"
#include "blacksim/simulatorinfo.h"
#include <QObject>
#include <QtPlugin>
#include <QList>
#include <QThread>
#include <QHash>
//! \file
namespace BlackSimPlugin
{
namespace Fs9
{
//! Factory implementation to create CSimulatorFs9 instances
class Q_DECL_EXPORT CSimulatorFs9Factory : public QObject, public BlackCore::ISimulatorFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "net.vatsim.PilotClient.BlackCore.SimulatorInterface")
Q_INTERFACES(BlackCore::ISimulatorFactory)
public:
//! \copydoc BlackCore::ISimulatorFactory::create()
virtual BlackCore::ISimulator *create(QObject *parent) override;
//! Simulator info
virtual BlackSim::CSimulatorInfo getSimulatorInfo() const override;
};
//! \brief FSX Simulator Implementation
class CSimulatorFs9 : public BlackCore::ISimulator
{
Q_OBJECT
public:
//! \brief Constructor
CSimulatorFs9(QObject *parent = nullptr);
virtual ~CSimulatorFs9();
//! \copydoc ISimulator::isConnected()
virtual bool isConnected() const override;
//! \copydoc ISimulator::canConnect()
virtual bool canConnect() override;
public slots:
//! \copydoc ISimulator::connectTo()
virtual bool connectTo() override;
//! \copydoc ISimulator::connectTo()
virtual void asyncConnectTo() override;
//! \copydoc ISimulator::disconnectFrom()
virtual bool disconnectFrom() override;
//! \copydoc ISimulator::getOwnAircraft()
virtual BlackMisc::Aviation::CAircraft getOwnAircraft() const override { return m_ownAircraft; }
//! \copydoc ISimulator::addRemoteAircraft()
virtual void addRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign, const QString &type, const BlackMisc::Aviation::CAircraftSituation &initialSituation) override;
//! \copydoc ISimulator::addAircraftSituation()
virtual void addAircraftSituation(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &initialSituation) override;
//! \copydoc ISimulator::removeRemoteAircraft()
virtual void removeRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) override;
//! \copydoc ISimulator::updateOwnSimulatorCockpit()
virtual bool updateOwnSimulatorCockpit(const BlackMisc::Aviation::CAircraft &ownAircraft) override;
//! \copydoc ISimulator::getSimulatorInfo()
virtual BlackSim::CSimulatorInfo getSimulatorInfo() const override;
//! \copydoc ISimulator::displayStatusMessage()
virtual void displayStatusMessage(const BlackMisc::CStatusMessage &message) const override;
//! \copydoc ISimulator::getAircraftModel()
virtual BlackMisc::Network::CAircraftModel getAircraftModel() const override { return m_aircraftModel; }
//! Airports in range
virtual BlackMisc::Aviation::CAirportList getAirportsInRange() const override;
//! Set time synchronization between simulator and user's computer time
//! \remarks not all drivers implement this, e.g. if it is an intrinsic simulator feature
virtual void setTimeSynchronization(bool enable, BlackMisc::PhysicalQuantities::CTime offset) override;
//! Is time synchronization on?
virtual bool isTimeSynchronized() const override { return m_syncTime; }
//! Time synchronization offset
virtual BlackMisc::PhysicalQuantities::CTime getTimeSynchronizationOffset() const override { return m_syncTimeOffset; }
//! Simulator paused?
virtual bool isSimPaused() const override { return m_simPaused; }
protected:
//! Timer event
virtual void timerEvent(QTimerEvent *event);
private slots:
//! Dispatch SimConnect messages
void ps_dispatch();
//! Process incoming FS9 message
void processFs9Message(const QByteArray &message);
//! Change own aircraft model string
void changeOwnAircraftModel(const QString &modelname);
//! Change DirectPlay host status
void changeHostStatus(CFs9Host::HostStatus status);
//! Remove client by callsign QString
void removeAircraft(const QString &callsign);
private:
//! Called when data about our own aircraft are received
void updateOwnAircraftFromSim(const BlackMisc::Aviation::CAircraft &ownAircraft);
void disconnectAllClients();
// DirectPlay object handling
CFs9Host *m_fs9Host = nullptr;
QThread m_hostThread;
bool m_isHosting = false; //!< Is sim connected
bool m_syncTime = false; //!< Time synchronized?
int m_syncDeferredCounter = 0; //!< Set when synchronized, used to wait some time
bool m_simPaused = false; //!< Simulator paused?
QHash<BlackMisc::Aviation::CCallsign, CFs9Client *> m_hashFs9Clients;
QHash<CFs9Client *, QThread *> m_fs9ClientThreads;
BlackSim::CSimulatorInfo m_simulatorInfo;
BlackMisc::Aviation::CAircraft m_ownAircraft; //!< Object representing our own aircraft from simulator
BlackMisc::Aviation::CAirportList m_airportsInRange;
BlackMisc::Network::CAircraftModel m_aircraftModel;
BlackMisc::PhysicalQuantities::CTime m_syncTimeOffset;
QScopedPointer<FsCommon::CFsuipc> m_fsuipc;
};
}
} // namespace BlackCore
#endif // guard