Files
qt5-legatus-legacy/TetraIF/PrtCnv/PrtCnvDbThread.hpp
T
2026-08-13 18:33:10 +02:00

560 lines
16 KiB
C++

// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvDbThread
// **************************************************************************
#pragma once
#include <QtCore/QAtomicInt>
#include <QtCore/QMutex>
#include <QtCore/QString>
#include <QtSql/QSqlDatabase>
#include "PrtCnvThread.hpp"
#include "PrtCnvRadioThread.hpp"
#include <memory>
#include <unordered_map>
#include <vector>
namespace N_CoPrtCnv
{
class CoPrtCnvCentralTelegram;
class CoPrtCnvRadioTelegram;
}
namespace PrtCnv
{
class PrtCnvThreadStarter;
class PrtCnvStoredCentralTelegram;
class PrtCnvStoredRadioTelegram;
class PrtCnvMember;
/**
* Handles all data which must be stored permanent:
* - central-telegrams
* - radio-telegrams
* - radio-members (TNE)
* - reset-counter
* - states
*
* @author wia
* @version 1.0
* @created 28-Jul-2009 16:08:53
*/
class PrtCnvDbThread : public PrtCnvThread
{
Q_OBJECT
public:
/**
* Constructor with thread-starter as argument
*
* @param a_parent
*/
PrtCnvDbThread(const QString &dbDriver,
const QString &dbHostName,
const QString &dbDatabaseName,
const QString &schemaName,
const QString &dbUserName,
const QString &dbPassword,
PrtCnvThreadStarter* a_parent);
~PrtCnvDbThread();
/**
* starts this thread and its childs
*/
int32_t start() override;
/**
* stops this thread and its childs
*/
int32_t stop() override;
/**
* creates this thread and its childs
*/
int32_t create(NGL::Settings *settings) override;
/**
* initializes this thread and its childs
*/
int32_t init() override;
/**
* Indicates the Db, which telegrams were successful received by central. This
* will delete this telegrams from Db.
*
* @param a_counter
*/
void ackCentralTelegrams(int32_t a_counter);
/**
* returns the next telegram to send to central.
* returns NULL if no telegram found.
* ATTENTION: The caller is responsible to deallocate the returned
* centralTelegram!
*
* @param a_handle
*/
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextCentralTelegram(PrtCnvStoredCentralTelegram *& a_handle);
/**
* Marks the central-telegram as to be sent
*
* @param a_counter The counter the telegram has while sent
* @param a_handle The telegram-handle gotten from getNextCentralTelegram()
*/
void centralTelegramSent(const int32_t a_counter, PrtCnvStoredCentralTelegram *a_handle);
/**
* Updates the counter for unsuccessful sending the telegram. If a maximal value
* of this counter is reached, the central-telegram will be deleted.
* a_handle could be deleted after calling this function!
* This function could block some milliseconds, because it must wait until all
* changes are written to DB.
*
* @param a_handle The telegram-handle gotten from getNextCentralTelegram()
*/
void centralTelegramCouldNotBeSent(PrtCnvStoredCentralTelegram *a_handle);
/**
* Marks the radio-telegram as to be sent
*
* @param a_sendCounter
* @param a_handle
* @param a_broadcastIndex
*/
void radioTelegramSent(const uint32_t a_sendCounter, PrtCnvStoredRadioTelegram *a_handle, const uint32_t a_broadcastIndex);
/**
* returns the next telegram to send to radio, respecting the priority.
* returns NULL if no radio-telegram found.
*
* @param a_handle
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextRadioTelegram(PrtCnvStoredRadioTelegram *&a_handle);
/**
* returns the next broadcast-telegram from list.
* Deletes the telegram from this list.
* returns NULL if list empty.
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextBroadcastTelegram();
/**
* stores the telegram to be sent to central in working-list and also in Db.
* Because storing data in XDR_Wrapper_it-format is complicated (has pointer to
* data), we store them in the radio-telegram-format).
* Attention: Be aware that centralTelegram and radioTelegram contain the same
* data.
* The DbThread takes control over centralTelegram and radioTelegram and deletes
* them when no longer needed.
*
* @param a_centralTelegram
* @param a_radioTelegram Must be the same telegram like the central-telegram
* (used for storing the telegram in Db)
*/
void storeCentralTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> &a_centralTelegram,
const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
/**
* Stores the telegram to be sent to radio in working-list and also in Db.
* The DbThread takes control over a_telegram and deletes it when no longer needed.
*
*
* @param a_telegram
* @param a_readCounter
*/
void storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram, const int32_t a_readCounter);
/**
* This function causes acking a central-telegram, which must not be stored
* permanent in Db, because it will be handled immediately (i.e.
* AnforderungStatistikBroadcastNachforderungen).
*
* @param a_centralCounter The read-counter of the central-telegram
*/
void centralCounterReadyForAck(const int32_t a_centralCounter);
/**
* Adds an sql-command in the sql-command-list. On next iteration of
* storePermanent() all command in list will be executed.
*
* @param a_sqlCommand
*/
void addSqlCommand(const QString &sql);
/**
* erases and initializes the database
*/
void cleanAndInit();
/**
* Checks whether central-telegrams have to be resent. If yes, all actions will be
* done, that getNextCentralTelegram() delivers the telegram(s) to be resent.
* Returns true if there must telegrams be resent.
*
* @param a_lastSendCounter The counter of the telegram we expect a ack-
* telegram has been received
*/
bool resendCentralTelegrams(const int32_t a_lastSendCounter);
/**
* Prepares the telegrams not yet acked from central for reconnection. This will
* be done by setting the write-counter to 0.
*/
void prepareCentralTelegramsForReconnection();
/**
* Blocks until all SQL-commands were written to DB.
*/
void waitUntilDbWritten();
/**
* returns NULL if no member found
*
* @param a_ISSI
*/
PrtCnvMember* getMemberByISSI(const uint32_t a_ISSI) const;
/**
* returns NULL if member not found
*
* @param a_id
*/
PrtCnvMember* getMemberById(const uint16_t a_id);
/**
* Licenses the member
*
* @param a_member
* @param a_doLicense
*/
bool licenseMember(PrtCnvMember *a_member, const bool a_doLicense = true);
/**
* Updates the current exercise for this member
*/
void updateExerciseIdForMember(PrtCnvMember *a_member, int newExerciseId);
/**
* adds a member in the 2 maps, sorted by id and ISSI
* returns the new member
*
* @param a_identity
* @param a_ISSI
*/
PrtCnvMember* addMember(const int a_identity, const int a_ISSI);
/**
* removes the member in the 2 maps, sorted by id and ISSI
*
* @param a_member
*/
void removeMember(PrtCnvMember* a_member);
/**
* Returns the actual reset-counter
*/
uint8_t getResetCounter() const;
/**
* Sets the actual reset-counter
*
* @param newVal
*/
void setResetCounter(uint8_t newVal);
/**
* The actual 'Betriebszustaende Gefechtsfeld' which will be sent to TNEs
* periodically
*/
uint8_t getBattleFieldOperatingState() const;
/**
* Sets or unsets pause, depending on a_doSet.
* (Bit 0 in UMGEBUNGS_MELDUNG(2))
* @param a_doSet
*/
void setPause(const bool a_doSet = true);
/**
* Set the inhibit time for position telegrams (1 .. 31 Seconds)
* (Bit 1-5 in UMGEBUNGS_MELDUNG(2))
* @param a_doSet
*/
void setPositionSendTime(unsigned int sendTime);
/**
* Set the current air load to be sent with the Umgebungsmeldung
* (Bit 6-7 in UMGEBUNGS_MELDUNG(2))
* @param airLoad The current air-load received from Tetra
*/
void setAirLoad(PrtCnvRadioThread::E_TrafficLoad airLoad);
/**
* Returns true if the resetCounter itself is different than the reset-counter of
* the environment-telegram
*/
bool isEnvironmentResetCounterOld() const;
/**
* Returns the actual Umgebungsmeldung
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getEnvironmentTelegram();
/**
* Sets the resetcounter of the environment-telegram to the actual reset-counter.
*/
void setResetCounterOnEnvironment();
/**
* Increments the counter for nachforderungen of the TNE with id a_identity.
*
* @param a_identity The identity of the TNE, which asked for nachforderung
*/
void incClaimCount(int a_identity);
/**
* Fills the map with nachforderungsstatistic.
* Returns the number of elements in map.
*
* @param a_statisticMap this map will be filled with pairs of
* ausstattungsnummer and amount of nachforderungen
*/
std::map<int32_t, int32_t> getClaimStatistics() const;
/**
* Sets the actual machine-state permanent, used to proceed cleaning db after a
* crash.
*
* @param a_newMachineState The new machine-state. 0 => working; 1 => cleaning
* DB
*/
void setMachineState(const int a_newMachineState);
/**
* Returns true if protocol-converter was crashed while cleanup DB.
*/
bool mustProceedCleanup() const;
protected:
void closeDb();
/**
* List of telegrams to be sent to central
*/
std::vector<PrtCnvStoredCentralTelegram*> m_CentralList;
/**
* Synchronizes access to m_BattleFieldOperatingState
*/
mutable QMutex m_BattleFieldOpStateMutex;
/**
* Synchronizes access to m_BattleFieldOperatingState
*/
mutable QMutex m_MemberListByIdMutex;
/**
* Synchronizes access to m_CentralList
*/
mutable QMutex m_CentralListMutex;
/**
* The permanent DB
*/
QSqlDatabase m_db;
/**
* The last counter received from central as ack. Used for deleting no longer used
* central-telegrams and for resending of central-telegrams.
*/
QAtomicInt m_CentralAckCounter;
/**
* Used for optimizing deletion of no longer needed central-telegrams.
*/
unsigned int m_OldCentralAckCounter;
/**
* List of telegrams to be sent to radio
*/
std::list<PrtCnvStoredRadioTelegram*> m_RadioList;
/**
* Synchronizes access to m_RadioList
*/
mutable QMutex m_RadioListMutex;
/**
* Holds the sql-command, which have to be executed in next physical storing cycle
*/
std::vector<QString> m_SqlCommandList;
/**
* Synchronizes access to m_SqlCommandList
*/
mutable QMutex m_SqlCmdListMutex;
/**
* Synchronizes access to m_BroadcastList
*/
mutable QMutex m_BroadcastMapMutex;
/**
* List of broadcast-telegrams. The broadcast-manager will get them periodically
* out of this list.
*/
std::list<PrtCnvStoredRadioTelegram*> m_BroadcastList;
/**
* Used to wait until cleaning is finished and to suppress storing data within
* database while cleaning
*/
bool m_DoClean;
/**
* Used for waiting data written to DB
*/
bool m_DoWaitUntilSqlWritten;
/**
* Holds all logged in members ordered by Id.
*/
std::unordered_map<int,PrtCnvMember*> m_MemberListById;
/**
* Holds all logged in members ordered by ISSI
*/
std::unordered_map<int,PrtCnvMember*> m_MemberListByISSI;
/**
* Used to prevent data storing within DB while restoring after crash
*/
bool m_DoStorePermanent;
/**
* the actual reset-counter
*/
uint8_t m_ResetCounter;
/**
* The actual 'Betriebszustaende Gefechtsfeld' which will be sent to TNEs
* periodically
*/
uint8_t m_BattleFieldOperatingState;
/**
* The actual telegram "Umgebungsmeldung"
*/
PrtCnvStoredRadioTelegram* m_EnvironmentTelegram;
/**
* True if cleanup has to be proceed after crash.
*/
bool m_DoProceedCleanup;
/**
* Used to optimize send the signal permanentStored
*/
int32_t m_OldCentralCounter;
/**
* For ensure that not too much storePermanent-signals will be queued
*/
QBasicAtomicInt m_StorePermanentPending;
/**
* nbr of unsuccessful send-attempts, before telegram will be deleted without
* sending it
*/
uint32_t m_NbrOfAttempts;
/**
* creates or restores the permanent database
*/
bool createDatabase();
/**
* Sends a signal storePermanent if not already such a signal is pending
*/
void sendStorePermanentSignal();
/**
* Helper for storePermanent()
*/
void storeCentralTelegrams();
/**
* Helper for storePermanent()
*/
int32_t storeRadioTelegrams();
/**
* Helper for storePermanent()
*/
void handleSqlCommandList();
/**
* Delete all telegrams with the ids in telegramsToDelete
* from a given table
*/
void deleteTelegramsFromDbById(const QString &table);
/**
* Update the write counter for the telegrams in
* telegramsToUpdateCounter for the given table
*/
void updateTelegramsWriteCounter(const QString &table);
/**
* Update the broadcast counter for the telegrams in
* telegramsToUpdateCounter for the given table
*/
void updateTelegramsBroadcast(const QString &table);
/**
* Reinitialize the database
*/
void reinitDatabase();
QString toCentralTableName() const { return tableName(QStringLiteral("prtcnv_to_central")); }
QString toRadioTableName() const { return tableName(QStringLiteral("prtcnv_to_radio")); }
QString memberTableName() const { return tableName(QStringLiteral("prtcnv_member")); }
QString statusTableName() const { return tableName(QStringLiteral("prtcnv_status")); }
QString versionTableName() const { return tableName(QStringLiteral("prtcnv_version")); }
QString tableName(const QString &table) const { return m_dbSchemaName.isEmpty() ? table : m_dbSchemaName + QLatin1Char('.') + table; }
protected slots:
/**
* Stores the radio-telegrams, central-telegrams in Db and executes the sql-
* command-list.
*/
void storePermanent();
/**
* cleans up this thread and its childs
*/
void clean();
public:
signals:
/**
* Signal to call storePermanent()
*/
void doStorePermanent();
/**
* Signal to inform central-thread about stored telegrams
*
* @param a_centralCounter
*/
void permanentStored(int32_t a_centralCounter);
/**
* a signal to erase the permanent database
*/
void doClean();
void playerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
private:
// local caches to memory (re)allocation all the time
// note: std::vector.clear() does not release memory and therefore
// a reallocation in the next turn is not neccessary
std::vector<int64_t> m_telegramsToDelete;
std::vector<std::pair<int64_t, int32_t> > m_telegramsToUpdateCounter;
std::vector<std::pair<int64_t, int32_t> > m_telegramsToUpdateBC;
std::vector<std::pair<PrtCnvStoredCentralTelegram*, int64_t> > m_telegramsToInsert;
QString m_dbDriver;
QString m_dbHostName;
QString m_dbDatabaseName;
QString m_dbSchemaName;
QString m_dbUserName;
QString m_dbPassword;
};
}