// ************************************************************************** // // (C) Copyright 2009 Swiss SIMTEC AG // // ************************************************************************** // // $HeadURL$ // $LastChangedBy$ // $LastChangedDate$ // $LastChangedRevision$ // // Implementation of the Class PrtCnvDbThread // ************************************************************************** #pragma once #include #include #include #include #include "PrtCnvThread.hpp" #include "PrtCnvRadioThread.hpp" #include #include #include 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 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 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 &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 &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 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 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 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 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 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 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 m_MemberListById; /** * Holds all logged in members ordered by ISSI */ std::unordered_map 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 m_telegramsToDelete; std::vector > m_telegramsToUpdateCounter; std::vector > m_telegramsToUpdateBC; std::vector > m_telegramsToInsert; QString m_dbDriver; QString m_dbHostName; QString m_dbDatabaseName; QString m_dbSchemaName; QString m_dbUserName; QString m_dbPassword; }; }