diff --git a/app/http/computermanager.cpp b/app/http/computermanager.cpp new file mode 100644 index 00000000..801f5b36 --- /dev/null +++ b/app/http/computermanager.cpp @@ -0,0 +1,186 @@ +#include "computermanager.h" +#include "nvhttp.h" + +#include + +ComputerManager::ComputerManager() + : m_Polling(false) +{ + +} + +void ComputerManager::startPolling() +{ + m_Polling = true; + + +} + +class PcMonitorThread : public QThread +{ + Q_OBJECT + +#define TRIES_BEFORE_OFFLINING 2 +#define POLLS_PER_APPLIST_FETCH 10 + + PcMonitorThread(NvComputer* computer, IdentityManager im) + : m_Im(im), + m_Computer(computer) + { + + } + +#define DECLARE_UPDATE_COMPUTER_FIELD(Type) \ + bool UpdateComputerField(Type& oldValue, Type newValue) \ + { \ + if (oldValue == newValue) { \ + return false; \ + } \ + \ + oldValue = newValue; \ + return true; \ + } + + DECLARE_UPDATE_COMPUTER_FIELD(QString) + DECLARE_UPDATE_COMPUTER_FIELD(int) + DECLARE_UPDATE_COMPUTER_FIELD(QByteArray) + DECLARE_UPDATE_COMPUTER_FIELD(NvComputer::ComputerState) + DECLARE_UPDATE_COMPUTER_FIELD(NvComputer::PairState) + + bool TryPollComputer(QString& address, bool& changed) + { + NvHTTP http(address, m_Im); + + QString serverInfo; + try { + serverInfo = http.getServerInfo(); + } catch (...) { + return false; + } + + changed = false; + + QString newName = http.getXmlString(serverInfo, "hostname"); + if (newName.isNull()) { + newName = "UNKNOWN"; + } + changed |= UpdateComputerField(m_Computer->name, newName); + changed |= UpdateComputerField(m_Computer->uuid, http.getXmlString(serverInfo, "uniqueid")); + + QString newMacString = http.getXmlString(serverInfo, "mac"); + QByteArray newMac; + if (newMacString != "00:00:00:00:00:00") { + QStringList macOctets = newMacString.split(':'); + for (QString macOctet : macOctets) { + newMac.append((char) macOctet.toInt(nullptr, 16)); + } + changed |= UpdateComputerField(m_Computer->macAddress, newMac); + } + changed |= UpdateComputerField(m_Computer->localAddress, http.getXmlString(serverInfo, "LocalIP")); + changed |= UpdateComputerField(m_Computer->remoteAddress, http.getXmlString(serverInfo, "ExternalIP")); + changed |= UpdateComputerField(m_Computer->pairState, http.getXmlString(serverInfo, "PairStatus") == "1" ? + NvComputer::PS_PAIRED : NvComputer::PS_NOT_PAIRED); + changed |= UpdateComputerField(m_Computer->currentGameId, http.getCurrentGame(serverInfo)); + changed |= UpdateComputerField(m_Computer->activeAddress, address); + changed |= UpdateComputerField(m_Computer->state, NvComputer::CS_ONLINE); + + return true; + } + + bool UpdateAppList(bool& changed) + { + changed = false; + return false; + } + + void run() override + { + // Always fetch the applist the first time + int pollsSinceLastAppListFetch = POLLS_PER_APPLIST_FETCH; + while (!isInterruptionRequested()) { + QVector uniqueAddressList; + + // Start with addresses correctly ordered + uniqueAddressList.append(m_Computer->activeAddress); + uniqueAddressList.append(m_Computer->localAddress); + uniqueAddressList.append(m_Computer->remoteAddress); + uniqueAddressList.append(m_Computer->manualAddress); + + // Prune duplicates (always giving precedence to the first) + for (int i = 0; i < uniqueAddressList.count(); i++) { + if (uniqueAddressList[i].isEmpty() || uniqueAddressList[i].isNull()) { + uniqueAddressList.remove(i); + i--; + continue; + } + for (int j = i + 1; j < uniqueAddressList.count(); j++) { + if (uniqueAddressList[i] == uniqueAddressList[j]) { + // Always remove the later occurrence + uniqueAddressList.remove(j); + j--; + } + } + } + + // We must have at least 1 address for this host + Q_ASSERT(uniqueAddressList.count() != 0); + + + bool stateChanged = false; + for (int i = 0; i < TRIES_BEFORE_OFFLINING; i++) { + for (auto& address : uniqueAddressList) { + if (isInterruptionRequested()) { + return; + } + + if (TryPollComputer(address, stateChanged)) { + break; + } + } + + // No need to continue retrying if we're online + if (m_Computer->state == NvComputer::CS_ONLINE) { + break; + } + } + + // Check if we failed after all retry attempts + if (m_Computer->state != NvComputer::CS_ONLINE) { + if (m_Computer->state != NvComputer::CS_OFFLINE) { + m_Computer->state = NvComputer::CS_OFFLINE; + stateChanged = true; + } + } + + if (stateChanged) { + // Tell anyone listening that we've changed state + emit computerStateChanged(m_Computer); + } + + // Grab the applist if it's empty or it's been long enough that we need to refresh + pollsSinceLastAppListFetch++; + if (m_Computer->state == NvComputer::CS_ONLINE && + (m_Computer->appList.isEmpty() || pollsSinceLastAppListFetch >= POLLS_PER_APPLIST_FETCH)) { + stateChanged = false; + + if (UpdateAppList(stateChanged)) { + pollsSinceLastAppListFetch = 0; + } + + if (stateChanged) { + emit computerStateChanged(m_Computer); + } + } + + // Wait a bit to poll again + QThread::sleep(3); + } + } + +signals: + void computerStateChanged(NvComputer* computer); + +private: + IdentityManager m_Im; + NvComputer* m_Computer; +}; diff --git a/app/http/computermanager.h b/app/http/computermanager.h new file mode 100644 index 00000000..fc9b4b26 --- /dev/null +++ b/app/http/computermanager.h @@ -0,0 +1,56 @@ +#pragma once +#include "nvhttp.h" + + +class ComputerManager +{ +public: + ComputerManager(); + + void startPolling(); + +private: + bool m_Polling; +}; + +class NvApp +{ +public: + int id; + QString name; + bool hdrSupported; +}; + +class NvComputer +{ +public: + enum PairState + { + PS_UNKNOWN, + PS_PAIRED, + PS_NOT_PAIRED + }; + + enum ComputerState + { + CS_UNKNOWN, + CS_ONLINE, + CS_OFFLINE + }; + + // Ephemeral traits + ComputerState state; + PairState pairState; + QString activeAddress; + int currentGameId; + + // Persisted traits + QString localAddress; + QString remoteAddress; + QString manualAddress; + QByteArray macAddress; + QString name; + QString uuid; + int serverCodecModeSupport; + QVector appList; +}; diff --git a/app/http/nvhttp.cpp b/app/http/nvhttp.cpp index e32e9759..91aac65f 100644 --- a/app/http/nvhttp.cpp +++ b/app/http/nvhttp.cpp @@ -23,45 +23,6 @@ NvHTTP::NvHTTP(QString address, IdentityManager im) : m_BaseUrlHttps.setPort(47984); } -NvComputer -NvHTTP::getComputerInfo() -{ - NvComputer computer; - QString serverInfo = getServerInfo(); - - computer.m_Name = getXmlString(serverInfo, "hostname"); - if (computer.m_Name == nullptr) - { - computer.m_Name = "UNKNOWN"; - } - - computer.m_Uuid = getXmlString(serverInfo, "uniqueid"); - computer.m_MacAddress = getXmlString(serverInfo, "mac"); - - // If there's no LocalIP field, use the address we hit the server on - computer.m_LocalAddress = getXmlString(serverInfo, "LocalIP"); - if (computer.m_LocalAddress == nullptr) - { - computer.m_LocalAddress = m_Address; - } - - // If there's no ExternalIP field, use the address we hit the server on - computer.m_RemoteAddress = getXmlString(serverInfo, "ExternalIP"); - if (computer.m_RemoteAddress == nullptr) - { - computer.m_RemoteAddress = m_Address; - } - - computer.m_PairState = getXmlString(serverInfo, "PairStatus") == "1" ? - NvComputer::PS_PAIRED : NvComputer::PS_NOT_PAIRED; - - computer.m_RunningGameId = getCurrentGame(serverInfo); - - computer.m_State = NvComputer::CS_ONLINE; - - return computer; -} - QVector NvHTTP::getServerVersionQuad(QString serverInfo) { diff --git a/app/http/nvhttp.h b/app/http/nvhttp.h index 402c72a7..cbf8adb9 100644 --- a/app/http/nvhttp.h +++ b/app/http/nvhttp.h @@ -37,43 +37,11 @@ private: QString m_StatusMessage; }; -class NvComputer -{ -public: - NvComputer() {} - - enum PairState - { - PS_UNKNOWN, - PS_PAIRED, - PS_NOT_PAIRED - }; - - enum ComputerState - { - CS_UNKNOWN, - CS_ONLINE, - CS_OFFLINE - }; - - QString m_Name; - QString m_Uuid; - QString m_MacAddress; - QString m_LocalAddress; - QString m_RemoteAddress; - int m_RunningGameId; - PairState m_PairState; - ComputerState m_State; -}; - class NvHTTP { public: NvHTTP(QString address, IdentityManager im); - NvComputer - getComputerInfo(); - int getCurrentGame(QString serverInfo);