Files
qt6-migration-pipeline/TetraIF/PrtCnv/PrtCnvDbThread.cpp
T
2026-07-14 12:07:36 +02:00

1648 lines
45 KiB
C++

#include "PrtCnvDbThread.hpp"
#include "PrtCnvThreadStarter.hpp"
#include "PrtCnvCentralThread.hpp"
#include "PrtCnvMember.hpp"
#include "PrtCnvRadioThread.hpp"
#include "PrtCnvStoredCentralTelegram.hpp"
#include "PrtCnvStoredRadioTelegram.hpp"
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvHexDumpHelper.hpp>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <ngldb/dbtransaction.h>
#include <ngldb/nsqlquery.h>
#include <QtSql/QSqlError>
#include <iostream>
namespace PrtCnv
{
PrtCnvDbThread::PrtCnvDbThread(const QString &dbDriver,
const QString &dbHostName,
const QString &dbDatabaseName,
const QString &schemaName,
const QString &dbUserName,
const QString &dbPassword,
PrtCnv::PrtCnvThreadStarter *a_parent)
: PrtCnvThread(a_parent, "PrtCnvDbThread")
, m_BattleFieldOpStateMutex(QMutex::Recursive)
, m_MemberListByIdMutex(QMutex::Recursive)
, m_CentralListMutex(QMutex::Recursive)
, m_OldCentralAckCounter(0)
, m_RadioListMutex(QMutex::Recursive)
, m_SqlCmdListMutex(QMutex::Recursive)
, m_BroadcastMapMutex(QMutex::Recursive)
, m_DoClean(false)
, m_DoWaitUntilSqlWritten(false)
, m_DoStorePermanent(false)
, m_ResetCounter(0)
, m_BattleFieldOperatingState(0)
, m_EnvironmentTelegram(nullptr)
, m_DoProceedCleanup(false)
, m_OldCentralCounter(0)
, m_StorePermanentPending(0)
, m_NbrOfAttempts(5)
, m_dbDriver(dbDriver.isEmpty() ? QStringLiteral("QSQLITE") : dbDriver)
, m_dbHostName(dbHostName)
, m_dbDatabaseName(dbDatabaseName)
, m_dbSchemaName(schemaName)
, m_dbUserName(dbUserName)
, m_dbPassword(dbPassword)
{
m_Name = QStringLiteral("Db");
}
PrtCnvDbThread::~PrtCnvDbThread()
{
closeDb();
qDeleteAll(m_BroadcastList);
qDeleteAll(m_RadioList);
qDeleteAll(m_CentralList);
for (const auto &p : m_MemberListById)
delete p.second;
}
static const int s_iCurrentDatabaseSchemeVersion = 1;
void PrtCnvDbThread::reinitDatabase()
{
DBTransaction trans(m_db);
NSqlQuery q(m_db);
if (m_db.driverName() == QStringLiteral("QSQLITE"))
{
q.exec(QStringLiteral("DROP TABLE IF EXISTS %1;").arg(versionTableName()));
q.exec(QStringLiteral("CREATE TABLE %1 ("
"version integer,"
"date text,"
"product text,"
"change text)").arg(versionTableName()));
q.exec(QStringLiteral("INSERT INTO %1 (version, date, product, change) VALUES "
"(1, '2012-11-20', 'TetraInterface', 'Initial version table');").arg(versionTableName()));
q.exec(QStringLiteral("DROP TABLE IF EXISTS %1;").arg(toCentralTableName()));
q.exec(QStringLiteral("CREATE TABLE %1 ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"tel_id INTEGER,"
"counter INTEGER DEFAULT 0,"
"data BLOB);").arg(toCentralTableName()));
q.exec(QStringLiteral("CREATE INDEX counterIndex ON %1(counter);").arg(toCentralTableName()));
q.exec(QStringLiteral("DROP TABLE IF EXISTS %1;").arg(toRadioTableName()));
q.exec(QStringLiteral("CREATE TABLE %1 ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"tel_id INTEGER,"
"counter INTEGER DEFAULT 0,"
"broadcast_index INTEGER DEFAULT 0,"
"reset_counter INTEGER DEFAULT 0,"
"data BLOB);").arg(toRadioTableName()));
q.exec(QStringLiteral("CREATE INDEX bcindex ON %1(reset_counter, broadcast_index);").arg(toRadioTableName()));
q.exec(QStringLiteral("DROP TABLE IF EXISTS %1;").arg(memberTableName()));
q.exec(QStringLiteral("CREATE TABLE %1 ("
"id INTEGER PRIMARY KEY,"
"issi INTEGER,"
"licensed INTEGER DEFAULT 0,"
"claims INTEGER DEFAULT 0,"
"exercise_id INTEGER"
");").arg(memberTableName()));
q.exec(QStringLiteral("DROP TABLE IF EXISTS %1;").arg(statusTableName()));
q.exec(QStringLiteral("CREATE TABLE %1 ("
"id INTEGER PRIMARY KEY,"
"reset_counter INTEGER,"
"ctas_operating_state INTEGER,"
"machine_state INTEGER"
");").arg(statusTableName()));
}
q.exec(QStringLiteral("DELETE FROM %1 WHERE id = 1;").arg(statusTableName()));
q.exec(QStringLiteral("INSERT INTO %1 "
"(id, reset_counter, ctas_operating_state, machine_state) "
"VALUES (1, %2, %3, 0);")
.arg(statusTableName()).arg(int(m_ResetCounter)).arg(int(m_BattleFieldOperatingState)));
q.exec(QStringLiteral("DELETE FROM %1 WHERE id = 0;").arg(memberTableName()));
trans.commit();
}
bool PrtCnvDbThread::createDatabase()
{
closeDb();
m_db = QSqlDatabase::addDatabase(m_dbDriver, QStringLiteral("PrtCnv"));
if (m_dbDriver == QLatin1String("QSQLITE"))
{
QString path = m_Config->getString(getName(), QStringLiteral("DatabaseFileName"), QStringLiteral("telegrams.db"));
if (!QDir::isAbsolutePath(path))
{
path = QCoreApplication::applicationDirPath() + QLatin1String("/../") + path;
QFileInfo fi(path);
QDir().mkpath(fi.absolutePath());
}
m_db.setDatabaseName(path);
INFO_LOG(m_logger) << "Use db-file: " << path;
std::cout << "Use db-file: " << qPrintable(path) << std::endl;
}
else
{
m_db.setDatabaseName(m_dbDatabaseName);
m_db.setHostName(m_dbHostName);
m_db.setUserName(m_dbUserName);
m_db.setPassword(m_dbPassword);
}
DEBUG_LOG(m_logger) << "Connection name: " << m_db.connectionName();
if (!m_db.open())
{
FATAL_LOG(m_logger) << "Error while opening database: " << m_db.lastError().text();
return false;
}
if (m_DoCleanDb)
{
NOTICE_LOG(m_logger) << "Permanent data will be erased...";
reinitDatabase();
}
else
{
NOTICE_LOG(m_logger) << "Check database...";
NSqlQuery q(m_db);
if (m_db.driverName() == QStringLiteral("QSQLITE"))
{
if (!q.execAndFetch(QStringLiteral("SELECT MAX(version) FROM %1;").arg(versionTableName())))
{
ERROR_LOG(m_logger) << "Could not read version table - reinitialize database";
reinitDatabase();
}
else
{
int iVersion = q.value(0).toInt();
if (iVersion != s_iCurrentDatabaseSchemeVersion)
{
ERROR_LOG(m_logger) << "Database version does not match (is: "
<< iVersion << " expected: " << s_iCurrentDatabaseSchemeVersion
<< " - reinitialize database";
reinitDatabase();
}
}
}
if (!q.exec(QStringLiteral("SELECT * from %1 LIMIT 1;").arg(statusTableName())))
{
ERROR_LOG(m_logger) << "Failure while checking DB-table Status";
std::cout << "Failure while checking DB-table Status. Start PrtCnv with --cleandb" << std::endl;
return false;
}
if (!q.exec(QStringLiteral("SELECT * from %1 LIMIT 1;").arg(toCentralTableName())))
{
ERROR_LOG(m_logger) << "Failure while checking DB-table ToCentral";
std::cout << "Failure while checking DB-table ToCentral. Start PrtCnv with --cleandb" << std::endl;
return false;
}
if (!q.exec(QStringLiteral("SELECT * from %1 LIMIT 1;").arg(toRadioTableName())))
{
ERROR_LOG(m_logger) << "Failure while checking DB-table ToRadio";
std::cout << "Failure while checking DB-table ToRadio. Start PrtCnv with --cleandb" << std::endl;
return false;
}
if (!q.exec(QStringLiteral("SELECT issi FROM %1 WHERE id=0;").arg(memberTableName())))
{
ERROR_LOG(m_logger) << "Failure while checking DB-table Member";
std::cout << "Failure while checking DB-table Member. Start PrtCnv with --cleandb" << std::endl;
return false;
}
const int broadcastIssi = m_Config->getInteger(QStringLiteral("Radio"), QStringLiteral("BroadcastISSI"), 5000);
if (q.next())
{
const int dbBroadcastIssi = q.value(0).toInt();
if (broadcastIssi != dbBroadcastIssi)
{
WARNING_LOG(m_logger) << "Broadcast-ISSI mismatch (DB: " << dbBroadcastIssi << "; Configfile: " << broadcastIssi << ")";
q.exec(QStringLiteral("DELETE FROM %1 WHERE id = 0;").arg(memberTableName()));
licenseMember(addMember(0, broadcastIssi));
}
}
else
{
licenseMember(addMember(0, broadcastIssi));
}
INFO_LOG(m_logger) << "Restore permanent data (after crash)...";
m_DoStorePermanent = false;
INFO_LOG(m_logger) << "restoreStatus";
if (!q.execAndFetch(QStringLiteral("SELECT reset_counter, ctas_operating_state, machine_state FROM %1;").arg(statusTableName())))
{
ERROR_LOG(m_logger) << "Error reading Status from DB";
std::cout << "Error reading Status from DB. Start PrtCnv with --cleandb" << std::endl;
return false;
}
m_ResetCounter = q.value(0).toInt();
INFO_LOG(m_logger) << "Restore resetCounter=" << (int)m_ResetCounter;
if (1 == q.value(2).toInt())
{
INFO_LOG(m_logger) << "Crash happenend while cleaning-DB";
m_DoStorePermanent = true;
m_DoProceedCleanup = true;
return true;
}
m_BattleFieldOperatingState = q.value(1).toInt();
INFO_LOG(m_logger) << "Restore battleFieldOperatingState=" << (int)m_BattleFieldOperatingState;
if (!q.exec(QStringLiteral("SELECT id, issi, licensed, claims, exercise_id FROM %1;").arg(memberTableName())))
{
ERROR_LOG(m_logger) << "Error reading Member from DB";
std::cout << "Error reading Member from DB. Start PrtCnv with --cleandb" << std::endl;
return false;
}
while (q.next())
{
int id = q.value(0).toInt();
int issi = q.value(1).toInt();
bool licensed = q.value(2).toBool();
int claims = q.value(3).toInt();
int exerciseId = q.value(4).toInt();
INFO_LOG(m_logger) << "Restore member: id=" << id << " issi=" << issi << " licensed=" << licensed
<< " claims=" << claims << " exerciseId " << exerciseId;
PrtCnvMember *member = addMember(id, issi);
if (member)
{
member->m_ClaimCount = claims;
member->m_loggedInExerciseId = exerciseId;
if (licensed)
{
bool ok = licenseMember(member);
if (ok)
{
emit playerLicencedChanged(member->m_Id, N_CoPrtCnv::E_LicensingKind::Success);
}
}
}
}
if (!q.exec(QStringLiteral("SELECT id, counter, broadcast_index, data "
"FROM %1 ORDER BY reset_counter, broadcast_index, id;").arg(toRadioTableName())))
{
ERROR_LOG(m_logger) << "Error reading from ToRadio table";
std::cout << "Error reading from ToRadio table. Start PrtCnv with --cleandb" << std::endl;
return false;
}
while (q.next())
{
const qint64 id = q.value(0).toLongLong();
const int counter = q.value(1).toInt();
const int bcIndex = q.value(2).toInt();
const QByteArray telData(q.value(3).toByteArray());
auto radioTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(telData));
PrtCnvStoredRadioTelegram *storedTelegram =
new PrtCnvStoredRadioTelegram(radioTelegram);
storedTelegram->m_Id = id;
if (0 == counter)
{
m_RadioList.push_back(storedTelegram);
INFO_LOG(m_logger) << "Restore unsent radiotelegram: id=" << storedTelegram->m_Id
<< " telId=" << storedTelegram->m_RadioTelegram->getTelID()
<< " BC=" << storedTelegram->m_RadioTelegram->getBroadcastIndex()
<< " counter=" << counter;
}
else
{
if (bcIndex == 0)
{
if (radioTelegram->getTelID() == GEF_UMGEBUNGS_MELDUNG2)
m_RadioList.push_back(storedTelegram);
else
delete storedTelegram;
}
else
{
radioTelegram->setBroadcastIndex(bcIndex);
bool dummy;
radioTelegram->mustBeSentAsBroadcast(0xFFFFFFFF, dummy);
radioTelegram->mustBeSentAsBroadcast(0xFFFFFFFF, dummy);
radioTelegram->mustBeSentAsBroadcast(0xFFFFFFFF, dummy);
m_BroadcastList.push_back(storedTelegram);
INFO_LOG(m_logger) << "Restore broadcast-radiotelegram: id="
<< storedTelegram->m_Id << " telId=" << storedTelegram->m_RadioTelegram->getTelID()
<< " BC=" << storedTelegram->m_RadioTelegram->getBroadcastIndex()
<< " counter=" << counter;
}
}
}
q.exec(QStringLiteral("UPDATE %1 SET counter=0;").arg(toCentralTableName()));
if (!q.exec(QStringLiteral("SELECT id, data FROM %1 ORDER BY id;").arg(toCentralTableName())))
{
ERROR_LOG(m_logger) << "Error reading from ToCentral table";
std::cout << "Error reading from ToCentral table. Start PrtCnv with --cleandb" << std::endl;
return false;
}
while (q.next())
{
const qint64 id = q.value(0).toLongLong();
const QByteArray telData(q.value(1).toByteArray());
N_CoPrtCnv::CoPrtCnvRadioTelegram radioTelegram(telData);
std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram>
centralTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(&radioTelegram));
if (centralTelegram)
{
PrtCnvStoredCentralTelegram *storedTelegram = new PrtCnvStoredCentralTelegram(centralTelegram, nullptr);
storedTelegram->m_Id = id;
m_CentralList.push_back(storedTelegram);
INFO_LOG(m_logger) << "Restore unsent or unacked centraltelegram: id=" << storedTelegram->m_Id
<< " telId=" << storedTelegram->m_CentralTelegram->telegramId()
<< " counter=" << storedTelegram->m_WriteCounter
<< " size=" << telData.size();
}
else
ERROR_LOG(m_logger) << "Internal error: conversion of telegram with telId=" << radioTelegram.getTelID() << " from radio to central failed";
}
if (m_db.driverName() == QLatin1String("QSQLITE"))
{
q.exec(QStringLiteral("VACUUM;"));
q.exec(QStringLiteral("PRAGMA journal_mode=WAL;"));
q.exec(QStringLiteral("PRAGMA synchronous=NORMAL;"));
}
}
m_DoStorePermanent = true;
return true;
}
int32_t PrtCnvDbThread::create(NGL::Settings *settings)
{
INFO_LOG(m_logger) << "create";
PrtCnvThread::create(settings);
m_NbrOfAttempts = m_Config->getInteger(QStringLiteral("Central"), QStringLiteral("NbrOfAttempts"), 5);
if (!createDatabase())
{
ERROR_LOG(m_logger) << "Trying to create clean database after error";
m_DoCleanDb = true;
if (!createDatabase())
exit(-2);
}
connect(this, &PrtCnvDbThread::doStorePermanent, this, &PrtCnvDbThread::storePermanent, Qt::QueuedConnection);
connect(this, &PrtCnvDbThread::doClean, this, &PrtCnvDbThread::clean, Qt::QueuedConnection);
PrtCnvCentralThread *centralThread = qobject_cast<PrtCnvCentralThread *>(m_Parent->getOtherThread(PrtCnvApplication::e_Central));
connect(this, &PrtCnvDbThread::permanentStored, centralThread, &PrtCnvCentralThread::permanentStored, Qt::QueuedConnection);
return 0;
}
int32_t PrtCnvDbThread::init()
{
INFO_LOG(m_logger) << "init";
PrtCnvThread::init();
m_CentralAckCounter = 0;
m_OldCentralAckCounter = 0;
m_OldCentralCounter = 0;
m_DoClean = false;
m_DoWaitUntilSqlWritten = false;
return 0;
}
int32_t PrtCnvDbThread::start()
{
INFO_LOG(m_logger) << "start";
PrtCnvThread::start();
return 0;
}
int32_t PrtCnvDbThread::stop()
{
PrtCnvThread::stop();
m_db.close();
m_db = QSqlDatabase();
return 0;
}
void PrtCnvDbThread::storeCentralTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> &a_centralTelegram,
const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram)
{
PrtCnvStoredCentralTelegram *storedTelegram =
new PrtCnvStoredCentralTelegram(a_centralTelegram,
std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(a_radioTelegram));
QMutexLocker l(&m_CentralListMutex);
m_CentralList.push_back(storedTelegram);
sendStorePermanentSignal();
}
N_CoPrtCnv::CoPrtCnvCentralTelegram *PrtCnvDbThread::getNextCentralTelegram(PrtCnvStoredCentralTelegram *&a_handle)
{
QMutexLocker l(&m_CentralListMutex);
std::vector<PrtCnvStoredCentralTelegram *>::const_iterator iter = m_CentralList.begin();
std::vector<PrtCnvStoredCentralTelegram *>::const_iterator end = m_CentralList.end();
for (; iter != end; ++iter)
{
PrtCnvStoredCentralTelegram *storedTelegram = *iter;
if (!storedTelegram || !storedTelegram->m_CentralTelegram)
continue;
if (0 == storedTelegram->m_WriteCounter || storedTelegram->m_DoResend)
{
const auto &telegram = storedTelegram->m_CentralTelegram;
storedTelegram->m_DoResend = false;
a_handle = storedTelegram;
DEBUG_LOG(m_logger) << "Next telegram to send to central: id=" << storedTelegram->m_Id
<< " a_telId=" << telegram->telegramId();
return new N_CoPrtCnv::CoPrtCnvCentralTelegram(*telegram);
}
}
return nullptr;
}
void PrtCnvDbThread::centralTelegramSent(const int32_t a_counter, PrtCnvStoredCentralTelegram *a_handle)
{
QMutexLocker l(&m_CentralListMutex);
a_handle->m_WriteCounter = a_counter;
a_handle->m_WriteCounterChanged = true;
sendStorePermanentSignal();
}
void PrtCnvDbThread::ackCentralTelegrams(int32_t a_counter)
{
m_CentralAckCounter = a_counter;
sendStorePermanentSignal();
}
void PrtCnvDbThread::centralTelegramCouldNotBeSent(PrtCnvStoredCentralTelegram *a_handle)
{
if (a_handle)
{
QMutexLocker l(&m_CentralListMutex);
if (++(a_handle->m_Attempts) >= 5)
{
WARNING_LOG(m_logger) << "Delete unsendable Central-Telegram with id=" << a_handle->m_Id
<< " telId=" << a_handle->m_CentralTelegram->telegramId()
<< " rc=" << a_handle->m_CentralTelegram->counter() << " wc=" << a_handle->m_WriteCounter;
a_handle->m_DoDelete = true;
}
l.unlock();
waitUntilDbWritten();
}
}
void PrtCnvDbThread::storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram,
const int32_t a_readCounter)
{
PrtCnvStoredRadioTelegram *storedTelegram = new PrtCnvStoredRadioTelegram(a_telegram);
storedTelegram->m_ReadCounter = a_readCounter;
{
QMutexLocker l(&m_RadioListMutex);
m_RadioList.push_back(storedTelegram);
}
if (a_telegram)
DEBUG_LOG(m_logger) << "Store Radio-Telegram with telId=" << a_telegram->getTelID()
<< " rc=" << a_readCounter
<< " wc=" << int(storedTelegram->m_WriteCounter);
if (m_DoStorePermanent)
sendStorePermanentSignal();
}
void PrtCnvDbThread::centralCounterReadyForAck(const int32_t a_centralCounter)
{
storeRadioTelegram(nullptr, a_centralCounter);
DEBUG_LOG(m_logger) << "No Radio-Telegram to store, but rc=" << a_centralCounter << " must be acked";
}
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> PrtCnvDbThread::getNextBroadcastTelegram()
{
QMutexLocker l(&m_BroadcastMapMutex);
if (m_BroadcastList.empty())
return nullptr;
PrtCnvStoredRadioTelegram *storedRadioTelegram = m_BroadcastList.front();
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> radioTelegram;
if (storedRadioTelegram->m_Id != 0 && !storedRadioTelegram->m_WriteCounterChanged)
{
radioTelegram = storedRadioTelegram->m_RadioTelegram;
m_BroadcastList.pop_front();
delete storedRadioTelegram;
}
return radioTelegram;
}
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> PrtCnvDbThread::getNextRadioTelegram(PrtCnvStoredRadioTelegram *&a_handle)
{
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> telegram;
uint8_t priority = 255;
QMutexLocker l(&m_RadioListMutex);
for (PrtCnvStoredRadioTelegram *storedTelegram: m_RadioList)
{
const auto &radioTelegram = storedTelegram->m_RadioTelegram;
if (radioTelegram)
{
if (!storedTelegram->m_IsSent)
{
uint8_t newPriority = radioTelegram->getPriority();
if (newPriority < priority)
{
priority = newPriority;
telegram = radioTelegram;
a_handle = storedTelegram;
}
}
}
}
return telegram;
}
void PrtCnvDbThread::radioTelegramSent(const uint32_t a_sendCounter, PrtCnvStoredRadioTelegram *a_handle, const uint32_t a_broadcastIndex)
{
QMutexLocker l(&m_RadioListMutex);
if (a_broadcastIndex)
{
DEBUG_LOG(m_logger) << "request update radio telegramm in db - sc: " << a_sendCounter
<< " telId: " << a_handle->m_RadioTelegram->getTelID()
<< " BC: " << a_broadcastIndex;
}
a_handle->m_WriteCounter = a_sendCounter;
a_handle->m_WriteCounterChanged = true;
a_handle->m_IsSent = true;
if (a_broadcastIndex != 0)
a_handle->m_BroadcastIndex = a_broadcastIndex;
if (a_handle->m_BroadcastIndex != 0)
{
bool dummy;
a_handle->m_RadioTelegram->mustBeSentAsBroadcast(0xFFFFFFFF, dummy);
DEBUG_LOG(m_logger) << "Move Radio-Telegram with telId=" << a_handle->m_RadioTelegram->getTelID()
<< " and BC=" << a_handle->m_RadioTelegram->getBroadcastIndex()
<< " to BC-List of DB";
QMutexLocker l2(&m_BroadcastMapMutex);
m_BroadcastList.push_back(a_handle);
}
else
{
if (a_handle->m_RadioTelegram->isNormalTelegram() && (a_handle->m_RadioTelegram->getTelID() == GEF_UMGEBUNGS_MELDUNG2))
{
if (m_EnvironmentTelegram)
m_EnvironmentTelegram->m_DoDelete = true;
m_EnvironmentTelegram = a_handle;
}
else
a_handle->m_DoDelete = true;
}
sendStorePermanentSignal();
}
void PrtCnvDbThread::deleteTelegramsFromDbById(const QString &table)
{
if (m_telegramsToDelete.empty())
return;
QStringList sl;
sl.reserve(m_telegramsToDelete.size());
for (const int64_t telId : m_telegramsToDelete)
sl += QString::number(telId);
NSqlQuery q(m_db);
q.exec(QStringLiteral("DELETE FROM %1 WHERE id IN (%2);").arg(table, sl.join(QLatin1Char(','))));
m_telegramsToDelete.clear();
}
void PrtCnvDbThread::updateTelegramsBroadcast(const QString &table)
{
if (m_telegramsToUpdateBC.empty())
return;
NSqlQuery q(m_db);
q.prepare(QStringLiteral("UPDATE %1 SET broadcast_index = :broadcast_index WHERE id = :id").arg(table));
for (const auto &p : m_telegramsToUpdateBC)
{
const qint64 telId = p.first;
const qint32 bcIndex = p.second;
q.bindValue(QStringLiteral(":broadcast_index"), bcIndex);
q.bindValue(QStringLiteral(":id"), telId);
q.exec();
DEBUG_LOG(m_logger) << "Update broadcastindex for id: " << telId << " to BC: " << bcIndex;
}
m_telegramsToUpdateBC.clear();
}
void PrtCnvDbThread::updateTelegramsWriteCounter(const QString &table)
{
if (m_telegramsToUpdateCounter.empty())
return;
NSqlQuery q(m_db);
q.prepare(QStringLiteral("UPDATE %1 SET counter = :counter WHERE id = :id").arg(table));
for (const auto &p : m_telegramsToUpdateCounter)
{
const qint64 telId = p.first;
const qint32 writeCounter = p.second;
q.bindValue(QStringLiteral(":counter"), writeCounter);
q.bindValue(QStringLiteral(":id"), telId);
q.exec();
DEBUG_LOG(m_logger) << "Update WriteCounter for id: " << telId << " to wc: " << writeCounter;
}
m_telegramsToUpdateCounter.clear();
}
void PrtCnvDbThread::storeCentralTelegrams()
{
QMutexLocker l(&m_CentralListMutex);
if (m_CentralList.empty())
return;
int iEmptyCount = 0;
for (auto &storedCentralTelegram : m_CentralList)
{
if (!storedCentralTelegram)
{
++iEmptyCount;
continue;
}
const auto centralTelegram = storedCentralTelegram->m_CentralTelegram;
if (0 == storedCentralTelegram->m_Id)
{
m_telegramsToInsert.emplace_back(std::make_pair(storedCentralTelegram, 0));
DEBUG_LOG(m_logger) << "Permanent Central-Telegram with id=" << storedCentralTelegram->m_Id
<< " data=" << centralTelegram->data()
<< " wc=" << storedCentralTelegram->m_WriteCounter;
}
else
{
const int centralAckCnt = m_CentralAckCounter;
if (storedCentralTelegram->m_WriteCounterChanged)
{
m_telegramsToUpdateCounter.emplace_back(std::make_pair(storedCentralTelegram->m_Id,
storedCentralTelegram->m_WriteCounter));
storedCentralTelegram->m_WriteCounterChanged = false;
DEBUG_LOG(m_logger) << "Update Central-Telegram with id=" << storedCentralTelegram->m_Id
<< " data=" << centralTelegram->data()
<< " wc=" << storedCentralTelegram->m_WriteCounter;
}
else if ((storedCentralTelegram->m_WriteCounter > 0 &&
storedCentralTelegram->m_WriteCounter <= centralAckCnt) ||
storedCentralTelegram->m_DoDelete)
{
DEBUG_LOG(m_logger) << "Delete Central-Telegram with id=" << storedCentralTelegram->m_Id
<< " data=" << storedCentralTelegram->m_CentralTelegram->data()
<< " wc=" << storedCentralTelegram->m_WriteCounter;
if (storedCentralTelegram->m_WriteCounter > centralAckCnt)
m_telegramsToDelete.push_back(storedCentralTelegram->m_Id);
delete storedCentralTelegram;
storedCentralTelegram = nullptr;
}
}
}
if (iEmptyCount > 50)
{
const auto isNull = [](const PrtCnvStoredCentralTelegram *t) { return t == nullptr; };
m_CentralList.erase(std::remove_if(m_CentralList.begin(), m_CentralList.end(), isNull),
m_CentralList.end());
}
l.unlock();
if (!m_telegramsToInsert.empty())
{
l.relock();
NSqlQuery q(m_db);
q.prepare(QStringLiteral("INSERT INTO %1 (tel_id, counter, data)"
"VALUES(:tel_id, :counter, :data);").arg(toCentralTableName()));
for (const auto &p : m_telegramsToInsert)
{
PrtCnvStoredCentralTelegram *storedCentralTelegram = p.first;
storedCentralTelegram->m_WriteCounterChanged = false;
q.bindValue(QStringLiteral(":tel_id"), storedCentralTelegram->m_CentralTelegram->telegramId());
q.bindValue(QStringLiteral(":counter"), storedCentralTelegram->m_WriteCounter);
q.bindValue(QStringLiteral(":data"), storedCentralTelegram->m_RadioTelegram->data());
q.exec();
storedCentralTelegram->m_Id = q.lastInsertId().toInt();
}
l.unlock();
m_telegramsToInsert.clear();
}
updateTelegramsWriteCounter(toCentralTableName());
const unsigned int centralAckCnt = static_cast<unsigned int>(m_CentralAckCounter);
if (centralAckCnt > m_OldCentralAckCounter)
{
m_OldCentralAckCounter = centralAckCnt;
DEBUG_LOG(m_logger) << "Central delete all counter <= " << m_OldCentralAckCounter;
NSqlQuery q(m_db);
q.exec(QStringLiteral("DELETE FROM %1 WHERE counter > 0 AND counter <= %2;").arg(toCentralTableName()).arg(m_OldCentralAckCounter));
}
deleteTelegramsFromDbById(toCentralTableName());
}
int32_t PrtCnvDbThread::storeRadioTelegrams()
{
QMutexLocker l(&m_RadioListMutex);
if (m_RadioList.empty())
return 0;
int32_t centralCounter = 0;
NSqlQuery q(m_db);
q.prepare(QStringLiteral("INSERT INTO %1 (tel_id, counter, broadcast_index, reset_counter, data) "
"VALUES (:tel_id, :counter, :broadcast_index, :reset_counter, :data);").arg(toRadioTableName()));
std::list<PrtCnvStoredRadioTelegram *>::iterator radioIter = m_RadioList.begin();
while (radioIter != m_RadioList.end())
{
bool doDeleteThisItem = false;
PrtCnvStoredRadioTelegram *storedRadioTelegram = *radioIter;
const auto &radioTelegram = storedRadioTelegram->m_RadioTelegram;
if (radioTelegram)
{
DEBUG_LOG(m_logger) << "Handling Radio-Telegram with id=" << storedRadioTelegram->m_Id
<< " TelID: " << radioTelegram->getTelID()
<< " rc: " << int(radioTelegram->zaehler())
<< " BC: " << storedRadioTelegram->m_BroadcastIndex
<< " wc: " << int(storedRadioTelegram->m_WriteCounter);
if (0 == storedRadioTelegram->m_Id)
{
if (storedRadioTelegram->m_ReadCounter > centralCounter)
centralCounter = storedRadioTelegram->m_ReadCounter;
storedRadioTelegram->m_WriteCounterChanged = false;
q.bindValue(QStringLiteral(":tel_id"), radioTelegram->getTelID());
q.bindValue(QStringLiteral(":counter"), storedRadioTelegram->m_WriteCounter);
q.bindValue(QStringLiteral(":broadcast_index"), storedRadioTelegram->m_BroadcastIndex);
q.bindValue(QStringLiteral(":reset_counter"), int(m_ResetCounter));
q.bindValue(QStringLiteral(":data"), radioTelegram->data());
q.exec();
storedRadioTelegram->m_Id = q.lastInsertId().toInt();
DEBUG_LOG(m_logger) << "Stored Telegramm in DB, id: " << storedRadioTelegram->m_Id;
if (storedRadioTelegram->m_RadioTelegram->isMovedToBroadcastList())
{
dumpTelegram(m_logger,
radioTelegram->getBroadcastCounter(),
storedRadioTelegram->m_RadioTelegram.get());
DEBUG_LOG(m_logger) << "Removed Telegramm from radioList (place 1), id: " << storedRadioTelegram->m_Id;
doDeleteThisItem = true;
}
}
else
{
if (storedRadioTelegram->m_DoDelete)
{
DEBUG_LOG(m_logger) << "Delete Telegramm from DB, id: " << storedRadioTelegram->m_Id;
m_telegramsToDelete.push_back(storedRadioTelegram->m_Id);
delete storedRadioTelegram;
doDeleteThisItem = true;
}
else if (storedRadioTelegram->m_WriteCounterChanged)
{
m_telegramsToUpdateCounter.emplace_back(std::make_pair(storedRadioTelegram->m_Id,
storedRadioTelegram->m_WriteCounter));
if (storedRadioTelegram->m_BroadcastIndex)
m_telegramsToUpdateBC.emplace_back(std::make_pair(storedRadioTelegram->m_Id,
storedRadioTelegram->m_BroadcastIndex));
storedRadioTelegram->m_WriteCounterChanged = false;
DEBUG_LOG(m_logger) << "Update Write counter in DB, id: " << storedRadioTelegram->m_Id
<< " New wc: " << int(storedRadioTelegram->m_WriteCounter)
<< " new bc: " << storedRadioTelegram->m_BroadcastIndex;
if (storedRadioTelegram->m_RadioTelegram->isMovedToBroadcastList())
{
dumpTelegram(m_logger,
storedRadioTelegram->m_BroadcastIndex,
storedRadioTelegram->m_RadioTelegram.get());
DEBUG_LOG(m_logger) << "Removed Telegramm from radioList (place 2), id: " << storedRadioTelegram->m_Id;
doDeleteThisItem = true;
}
}
}
}
else
{
if (storedRadioTelegram->m_ReadCounter > centralCounter)
centralCounter = storedRadioTelegram->m_ReadCounter;
delete storedRadioTelegram;
doDeleteThisItem = true;
}
if (doDeleteThisItem)
radioIter = m_RadioList.erase(radioIter);
else
++radioIter;
}
l.unlock();
static const QString &table = toRadioTableName();
updateTelegramsWriteCounter(table);
updateTelegramsBroadcast(table);
deleteTelegramsFromDbById(table);
return centralCounter;
}
void PrtCnvDbThread::handleSqlCommandList()
{
std::vector<QString> sqlCommandList;
{
QMutexLocker l(&m_SqlCmdListMutex);
std::swap(m_SqlCommandList, sqlCommandList);
}
if (sqlCommandList.empty())
return;
NSqlQuery q(m_db);
for (const QString &sql : sqlCommandList)
{
DEBUG_LOG(m_logger) << "execute sql=" << sql;
q.exec(sql);
}
}
void PrtCnvDbThread::storePermanent()
{
m_StorePermanentPending = 0;
TRACE_LOG(m_logger) << "storePermanent m_DoClean=" << m_DoClean << " m_DoWaitUntilSqlWritten=" << m_DoWaitUntilSqlWritten;
if (m_DoClean)
{
m_DoWaitUntilSqlWritten = false;
return;
}
DBTransaction trans(m_db);
storeCentralTelegrams();
const int32_t centralCounter = storeRadioTelegrams();
handleSqlCommandList();
trans.commit();
if (0 != centralCounter && centralCounter != m_OldCentralCounter)
{
m_OldCentralCounter = centralCounter;
emit permanentStored(centralCounter);
}
m_DoWaitUntilSqlWritten = false;
}
void PrtCnvDbThread::addSqlCommand(const QString &sql)
{
QMutexLocker l(&m_SqlCmdListMutex);
m_SqlCommandList.push_back(sql);
sendStorePermanentSignal();
}
void PrtCnvDbThread::cleanAndInit()
{
m_DoClean = true;
emit doClean();
while (m_DoClean)
QThread::sleep(1);
m_DoProceedCleanup = false;
}
void PrtCnvDbThread::clean()
{
NSqlQuery q(m_db);
INFO_LOG(m_logger) << "Delete CentralList";
{
QMutexLocker l(&m_CentralListMutex);
for (PrtCnvStoredCentralTelegram *t : m_CentralList)
delete t;
m_CentralList.clear();
q.exec(QStringLiteral("DELETE FROM %1;").arg(toCentralTableName()));
}
m_OldCentralCounter = 0;
INFO_LOG(m_logger) << "Delete RadioList";
m_EnvironmentTelegram = nullptr;
{
QMutexLocker l(&m_RadioListMutex);
for (PrtCnvStoredRadioTelegram *t : m_RadioList)
delete t;
m_RadioList.clear();
q.exec(QStringLiteral("DELETE FROM %1;").arg(toRadioTableName()));
}
INFO_LOG(m_logger) << "Delete BroadcastList";
{
QMutexLocker l(&m_BroadcastMapMutex);
for (PrtCnvStoredRadioTelegram *t : m_BroadcastList)
delete t;
m_BroadcastList.clear();
}
{
QMutexLocker l(&m_SqlCmdListMutex);
m_MemberListByISSI.clear();
{
QMutexLocker l2(&m_MemberListByIdMutex);
for (const auto &p : m_MemberListById)
delete p.second;
m_MemberListById.clear();
}
INFO_LOG(m_logger) << "Delete SqlList";
m_SqlCommandList.clear();
q.exec(QStringLiteral("DELETE FROM %1;").arg(memberTableName()));
}
licenseMember(addMember(0, m_Config->getInteger(QStringLiteral("Radio"), QStringLiteral("BroadcastISSI"), 5000)));
m_DoClean = false;
}
bool PrtCnvDbThread::resendCentralTelegrams(const int32_t a_lastSendCounter)
{
const int centralAckCnt = m_CentralAckCounter;
if (a_lastSendCounter > centralAckCnt)
{
bool warnOnce = true;
QMutexLocker l(&m_CentralListMutex);
int iCount = 0;
std::vector<PrtCnvStoredCentralTelegram *>::const_iterator iter = m_CentralList.begin();
std::vector<PrtCnvStoredCentralTelegram *>::const_iterator end = m_CentralList.end();
for (; iter != end && iCount < 50; ++iter)
{
PrtCnvStoredCentralTelegram *storedTelegram = *iter;
if (!storedTelegram)
continue;
++iCount;
if (storedTelegram->m_WriteCounter > centralAckCnt)
{
if (warnOnce)
{
WARNING_LOG(m_logger) << "Resend central-telegrams from wc=" << a_lastSendCounter
<< " centralAckCounter=" << centralAckCnt;
warnOnce = false;
}
INFO_LOG(m_logger) << "Resend central-telegram with telId="
<< storedTelegram->m_CentralTelegram->telegramId()
<< " wc=" << storedTelegram->m_WriteCounter;
storedTelegram->m_DoResend = true;
}
}
return true;
}
return false;
}
void PrtCnvDbThread::prepareCentralTelegramsForReconnection()
{
INFO_LOG(m_logger) << "m_CentralAckCounter=" << m_CentralAckCounter;
waitUntilDbWritten();
INFO_LOG(m_logger) << "start";
m_CentralAckCounter = 0;
m_OldCentralAckCounter = 0;
m_OldCentralCounter = 0;
QMutexLocker l(&m_CentralListMutex);
for (PrtCnvStoredCentralTelegram *storedTelegram : m_CentralList)
{
if (!storedTelegram)
continue;
if (storedTelegram->m_WriteCounter != 0)
{
INFO_LOG(m_logger) << "***Prepare central-telegram with id=" << storedTelegram->m_Id
<< " telId=" << storedTelegram->m_CentralTelegram->telegramId()
<< " wc=" << storedTelegram->m_WriteCounter << " wcChg="
<< storedTelegram->m_WriteCounterChanged
<< " for resend after reconnection";
storedTelegram->m_WriteCounter = 0;
storedTelegram->m_WriteCounterChanged = false;
storedTelegram->m_DoResend = false;
}
}
INFO_LOG(m_logger) << "Ready for reconnection";
}
void PrtCnvDbThread::waitUntilDbWritten()
{
m_DoWaitUntilSqlWritten = true;
emit doStorePermanent();
while (m_DoWaitUntilSqlWritten)
QThread::usleep(10000);
m_DoWaitUntilSqlWritten = true;
emit doStorePermanent();
while (m_DoWaitUntilSqlWritten)
QThread::usleep(10000);
}
PrtCnv::PrtCnvMember *PrtCnvDbThread::addMember(const int a_identity, const int a_ISSI)
{
PrtCnvMember *newMember = new PrtCnvMember(a_identity, a_ISSI);
{
QMutexLocker lSql(&m_SqlCmdListMutex);
QMutexLocker lId(&m_MemberListByIdMutex);
m_MemberListById.insert(std::make_pair(a_identity, newMember));
m_MemberListByISSI.insert(std::make_pair(a_ISSI, newMember));
}
DEBUG_LOG(m_logger) << "Adding member with eqId " << a_identity
<< " and issi " << a_ISSI
<< ", store in db: " << m_DoStorePermanent;
if (m_DoStorePermanent)
addSqlCommand(QStringLiteral("INSERT INTO %1 (id, issi) VALUES(%2, %3)")
.arg(memberTableName()).arg(newMember->m_Id).arg(newMember->m_ISSI));
return newMember;
}
void PrtCnvDbThread::removeMember(PrtCnv::PrtCnvMember *a_member)
{
{
QMutexLocker lSql(&m_SqlCmdListMutex);
QMutexLocker lId(&m_MemberListByIdMutex);
auto iterId = m_MemberListById.find(a_member->m_Id);
if (iterId != m_MemberListById.end())
m_MemberListById.erase(iterId);
else
ERROR_LOG(m_logger) << "Internal error: did not find the member by ID";
auto iterIssi = m_MemberListByISSI.find(a_member->m_ISSI);
if (iterIssi != m_MemberListByISSI.end())
m_MemberListByISSI.erase(iterIssi);
else
ERROR_LOG(m_logger) << "Internal error: did not find the member by ISSI";
}
DEBUG_LOG(m_logger) << "Removing member with eqId " << a_member->m_Id
<< " and issi " << a_member->m_ISSI
<< ", store in db: " << m_DoStorePermanent;
if (m_DoStorePermanent)
addSqlCommand(QStringLiteral("DELETE FROM %1 WHERE id = %2 OR issi = %3")
.arg(memberTableName()).arg(a_member->m_Id).arg(a_member->m_ISSI));
delete a_member;
}
bool PrtCnvDbThread::licenseMember(PrtCnvMember *a_member, const bool a_doLicense)
{
if (!a_member)
return false;
a_member->m_Licensed = a_doLicense;
if (m_DoStorePermanent)
addSqlCommand(QStringLiteral("UPDATE %1 SET licensed = %2 WHERE id = %3")
.arg(memberTableName()).arg(a_doLicense ? 1 : 0).arg(a_member->m_Id));
return true;
}
void PrtCnvDbThread::updateExerciseIdForMember(PrtCnvMember *a_member, int newExerciseId)
{
if (!a_member)
return;
a_member->m_loggedInExerciseId = newExerciseId;
DEBUG_LOG(m_logger) << "Updating exerciseId member with eqId " << a_member->m_Id
<< " and issi " << a_member->m_ISSI << " to " << newExerciseId
<< ", store in db: " << m_DoStorePermanent;
if (m_DoStorePermanent)
addSqlCommand(QStringLiteral("UPDATE %1 SET exercise_id = %2 WHERE id = %3")
.arg(memberTableName()).arg(newExerciseId).arg(a_member->m_Id));
}
PrtCnv::PrtCnvMember *PrtCnvDbThread::getMemberById(const uint16_t a_id)
{
QMutexLocker l(&m_MemberListByIdMutex);
auto iter = m_MemberListById.find(a_id);
if (iter != m_MemberListById.end())
return iter->second;
return nullptr;
}
PrtCnv::PrtCnvMember *PrtCnvDbThread::getMemberByISSI(const uint32_t a_ISSI) const
{
auto iter = m_MemberListByISSI.find(a_ISSI);
if (iter != m_MemberListByISSI.end())
return iter->second;
return nullptr;
}
uint8_t PrtCnvDbThread::getResetCounter() const
{
return m_ResetCounter;
}
void PrtCnvDbThread::setResetCounter(uint8_t newVal)
{
m_ResetCounter = newVal;
if (m_DoStorePermanent)
{
NSqlQuery q(m_db);
q.exec(QStringLiteral("UPDATE Status SET reset_counter = %1 WHERE id = 1")
.arg(int(newVal)));
}
INFO_LOG(m_logger) << "Update resetCounter=" << (int)m_ResetCounter << " doStorePermanent=" << m_DoStorePermanent;
}
uint8_t PrtCnvDbThread::getBattleFieldOperatingState() const
{
QMutexLocker l(&m_BattleFieldOpStateMutex);
return m_BattleFieldOperatingState;
}
void PrtCnvDbThread::setPause(const bool a_doSet)
{
QMutexLocker l(&m_BattleFieldOpStateMutex);
if (a_doSet)
m_BattleFieldOperatingState = m_BattleFieldOperatingState | 0x80;
else
m_BattleFieldOperatingState = m_BattleFieldOperatingState & 0x7F;
if (m_DoStorePermanent)
addSqlCommand(QStringLiteral("UPDATE %1 SET ctas_operating_state = %2 WHERE id = 1")
.arg(statusTableName()).arg(int(m_BattleFieldOperatingState)));
INFO_LOG(m_logger) << "Update battleFieldOperatingState=" << (int)m_BattleFieldOperatingState << " doStorePermanent=" << m_DoStorePermanent;
}
void PrtCnvDbThread::setPositionSendTime(unsigned int sendTime)
{
QMutexLocker l(&m_BattleFieldOpStateMutex);
if (sendTime > 31)
sendTime = 31;
if (sendTime < 1)
sendTime = 1;
m_BattleFieldOperatingState &= 0x83;
m_BattleFieldOperatingState |= (sendTime << 2);
}
void PrtCnvDbThread::setAirLoad(PrtCnvRadioThread::E_TrafficLoad airLoad)
{
QMutexLocker l(&m_BattleFieldOpStateMutex);
m_BattleFieldOperatingState &= 0xFC;
m_BattleFieldOperatingState |= (static_cast<uint8_t>(airLoad) & 0x03);
}
bool PrtCnvDbThread::isEnvironmentResetCounterOld() const
{
QMutexLocker l(&m_RadioListMutex);
bool isOld = false;
if (m_EnvironmentTelegram)
isOld = getResetCounter() != m_EnvironmentTelegram->m_RadioTelegram->getPayloadDataUInt8(7);
return isOld;
}
void PrtCnvDbThread::setResetCounterOnEnvironment()
{
QMutexLocker l(&m_RadioListMutex);
if (m_EnvironmentTelegram)
m_EnvironmentTelegram->m_RadioTelegram->setPayloadDataWithIndex(7, getResetCounter(), 1);
}
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> PrtCnvDbThread::getEnvironmentTelegram()
{
QMutexLocker l(&m_RadioListMutex);
if (m_EnvironmentTelegram)
return std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(m_EnvironmentTelegram->m_RadioTelegram->data());
return {};
}
std::map<int32_t, int32_t> PrtCnvDbThread::getClaimStatistics() const
{
std::map<int32_t, int32_t> ret;
QMutexLocker l(&m_MemberListByIdMutex);
for (const auto &p : m_MemberListById)
{
if (p.first != 0)
ret.insert(std::make_pair(p.first, p.second->m_ClaimCount));
}
return ret;
}
void PrtCnvDbThread::incClaimCount(int a_identity)
{
QMutexLocker l(&m_MemberListByIdMutex);
auto iter = m_MemberListById.find(a_identity);
if (iter != m_MemberListById.end())
{
iter->second->m_ClaimCount++;
if (m_DoStorePermanent)
addSqlCommand(QStringLiteral("UPDATE %1 SET claims = claims + 1 WHERE id = %2")
.arg(memberTableName()).arg(iter->second->m_Id));
}
else
ERROR_LOG(m_logger) << "Internal error: did not find TNE with id=" << a_identity;
}
void PrtCnvDbThread::setMachineState(const int a_newMachineState)
{
NSqlQuery q(m_db);
q.exec(QStringLiteral("UPDATE %1 SET machine_state = %2 WHERE id = 1")
.arg(statusTableName()).arg(a_newMachineState));
}
bool PrtCnvDbThread::mustProceedCleanup() const
{
return m_DoProceedCleanup;
}
void PrtCnvDbThread::sendStorePermanentSignal()
{
if (m_StorePermanentPending.testAndSetOrdered(0, 1))
emit doStorePermanent();
}
void PrtCnvDbThread::closeDb()
{
if (m_db.isOpen())
{
m_db.close();
m_db = QSqlDatabase();
QSqlDatabase::removeDatabase(QStringLiteral("PrtCnv"));
}
}
}