Files
qt5-legatus-legacy/Lts/Plugins/ParticipantManagement/Gui/Saab/saab/saab_export.cpp
T
2026-08-13 18:33:10 +02:00

621 lines
21 KiB
C++
Executable File

/**
* @RDE_HEADER@
* Exportieren von SAAB-Dateien
* $Id$
* $HeadURL$
* $Author$
*/
#include "saab_export.h"
#include "ui_saab_export.h"
#include "saab_xml_strings.h"
#include "saab_map_type.h"
#include "saab_map_party.h"
#include <QFileDialog>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QDateTime>
#include <QUuid>
#include <LtsSession/lts_session.h>
#include <LtsUtils/LtsUtilsSettings/settings.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <ParticipantManagement/ppm_translation.h>
/*
* QUuid::QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3, uchar b4, uchar b5, uchar b6, uchar b7, uchar b8)
* Creates a UUID with the value specified by the parameters, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8.
* General QUuid format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
* The vaues will be visualized in HEX-Code.
* Used QUuid Format: aaaaaaaa-bbbb-xxxx-ccxx-xxxxxxxxxxdd
* a = Participant ID, Parameter: l
* b = Exercise ID, Parameter: w1
* c = Party ID, Parameter: b1
* d = GUID Context, Parameter: b8
* 1 = Exercise
* 2 = Party
* 3 = Participant
* x = Unused, curretly filled with '0'
*
* Exercise format: xxxxxxxx-bbbb-xxxx-xxxx-xxxxxxxxxx01
* Party format: xxxxxxxx-bbbb-xxxx-ccxx-xxxxxxxxxx02
* Participant format: aaaaaaaa-bbbb.xxxx.ccxx-xxxxxxxxxx03
*/
namespace saab
{
static const QString xmlnsValue(QStringLiteral("http://www.saab.se/training/schemas/ExerciseOrbat/1.1.1"));
static const uint l = 0;
static const ushort w1 = 0;
static const ushort w2 = 0x88;
static const uchar b1 = 0;
static const uchar b2 = 0xde;
static const uchar b3 = 0xad;
static const uchar b4 = 0xbe;
static const uchar b5 = 0xef;
static const uchar b6 = 0x33;
static const uchar b7 = 0x44;
static const uchar b8 = 0;
ExportSaab::ExportSaab(LtsSession::LtsSession *session, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const ExerciseManagement::Exercise &exercise, QWidget *parent)
: LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_currentExercise(exercise)
, m_ppmDtc(ppmDtc)
, m_session(session)
, m_configFile(nullptr)
, m_ui(new Ui_SAABExport)
, m_logger(getLogger("ExportSaab"))
{
m_ui->setupUi(this);
connect(m_ui->m_pushbutton_SelectConfig, &QToolButton::clicked,
this, &ExportSaab::onSelectConfigFile);
connect(m_ui->m_pushbutton_SelectFile, &QToolButton::clicked,
this, &ExportSaab::onSelectExportFile);
connect(m_ui->m_pushButton_StartImport, &QPushButton::clicked,
this, &ExportSaab::onStartExport);
QString strExportFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedExportFile"), QString()).toString();
QString strConfigFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFile"), QString()).toString();
m_ui->m_lineedit_ExportFile->setText(strExportFile);
m_ui->m_lineedit_ConfigFile->setText(strConfigFile);
m_ui->m_pushButton_StartImport->setEnabled(strExportFile.size() && strConfigFile.size());
m_parties = m_ppmDtc->partys();
m_types = m_ppmDtc->types();
}
ExportSaab::~ExportSaab()
{
delete m_ui;
}
void ExportSaab::onStartExport()
{
QString strExportFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedExportFile"), QString()).toString();
QString strConfigFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFile"), QString()).toString();
QFile file(strExportFile);
if (m_configFile)
{
m_configFile->close();
delete m_configFile;
m_configFile = nullptr;
}
m_configFile = new QFile(strConfigFile, this);
if (!file.open(QIODevice::WriteOnly))
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Not possible to start export. You do not have the permission to write the selected file."));
ERROR_LOG(m_logger) << "Could not export exercise: \"" << strExportFile << "\" could not be opened for writing";
return;
}
if (!isValidConfigFile())
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Not possible to start export. Config file invalid."));
ERROR_LOG(m_logger) << "Could not export exercise: \"" << strConfigFile << "\" invalid";
return;
}
importConfig();
QXmlStreamWriter writer(&file);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement(XmlStrings::SAAB_EXERCISE_ORBAT());
writer.writeAttribute(XmlStrings::SAAB_XMLNS_XSI(), QStringLiteral("http://www.w3.org/2001/XMLSchema-instance"));
writer.writeAttribute(XmlStrings::SAAB_XMLNS_XSD(), QStringLiteral("http://www.w3.org/2001/XMLSchema"));
writer.writeAttribute(XmlStrings::SAAB_XMLNS(), xmlnsValue);
writeExercise(&writer);
writeOrbat(&writer);
writeFooter(&writer);
writer.writeEndElement();
writer.writeEndDocument();
file.close();
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Export finished"));
close();
}
void ExportSaab::onSelectConfigFile()
{
QString strPathSelectedConfigFolder = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFolder"), QStringLiteral("/shd/CTC/TOOLS/TDS/")).toString();
QString strPathSelectedConfigFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFile"), QStringLiteral("/shd/CTC/TOOLS/TDS/")).toString();
QList<QUrl> urls;
urls << QUrl::fromLocalFile(strPathSelectedConfigFile);
QFileDialog fileDialog(this, tr("Select File"), strPathSelectedConfigFolder, tr("XML files (*.xml)"));
fileDialog.setSidebarUrls(urls);
fileDialog.setOptions(QFileDialog::ReadOnly);
fileDialog.setFileMode(QFileDialog::ExistingFile);
if (fileDialog.exec()) // TODO: use show(), use signals & slots !
{
strPathSelectedConfigFile = fileDialog.selectedFiles().constFirst();
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFolder"), strPathSelectedConfigFolder);
if (!strPathSelectedConfigFile.size())
return;
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFile"), strPathSelectedConfigFile);
m_ui->m_lineedit_ConfigFile->setText(strPathSelectedConfigFile);
m_ui->m_pushButton_StartImport->setEnabled(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedExportFile"), QString()).toString().size());
}
}
void ExportSaab::onSelectExportFile()
{
QString strPathSelectedExportFolder = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedExportFolder"), QStringLiteral("/tmp")).toString();
QString strPathSelectedExportFile = QFileDialog::getSaveFileName(this, tr("Export file"),
strPathSelectedExportFolder,
tr("XML files (*.xml)"));
if (strPathSelectedExportFile.isEmpty())
return;
strPathSelectedExportFolder = strPathSelectedExportFile.mid(0, strPathSelectedExportFile.lastIndexOf(QLatin1Char('/')));
QString suffix = QStringLiteral(".xml");
if (!strPathSelectedExportFile.endsWith(suffix))
strPathSelectedExportFile += suffix;
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("SAAB"), QStringLiteral("pathSelectedExportFolder"), strPathSelectedExportFolder);
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("SAAB"), QStringLiteral("pathSelectedExportFile"), strPathSelectedExportFile);
m_ui->m_lineedit_ExportFile->setText(strPathSelectedExportFile);
m_ui->m_pushButton_StartImport->setEnabled(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("SAAB"), QStringLiteral("pathSelectedConfigFile"), QString()).toString().size());
}
bool ExportSaab::isValidConfigFile()
{
if (!m_configFile)
{
ERROR_LOG(m_logger) << "Not possible to read config: No file selected";
return false;
}
if (!m_configFile->open(QIODevice::ReadOnly))
{
ERROR_LOG(m_logger) << "Not possible to read config: Could not open file \""
<< m_configFile->fileName()
<< "\" for reading.";
return false;
}
using namespace XmlStrings;
QXmlStreamReader reader(m_configFile);
bool hasSymbols = false;
bool hasEchelons = false;
bool hasForces = false;
while (!reader.atEnd() && !(hasSymbols && hasEchelons && hasForces))
{
if (reader.readNextStartElement())
{
if (reader.name() == SAAB_SYMBOLS())
hasSymbols = true;
else if (reader.name() == SAAB_ECHELONS())
hasEchelons = true;
else if (reader.name() == SAAB_FORCES())
hasForces = true;
}
}
m_configFile->close();
if (!hasEchelons)
ERROR_LOG(m_logger) << "Could not find " << SAAB_ECHELONS() << " in file \"" << m_configFile->fileName() << '\"';
if (!hasSymbols)
ERROR_LOG(m_logger) << "Could not find " << SAAB_SYMBOLS() << " in file \"" << m_configFile->fileName() << '\"';
if (!hasForces)
ERROR_LOG(m_logger) << "Could not find " << SAAB_FORCES() << " in file \"" << m_configFile->fileName() << '\"';
return hasEchelons && hasSymbols && hasForces;
}
void ExportSaab::importConfig()
{
if (!m_mapSymbols.isEmpty() || !m_mapEchelons.isEmpty())
{
using namespace LtsWidgets;
Button b = LtsMessageDialog::showMessage(this,
tr("A config is already imported. Overwrite?"),
tr("Overwrite config?"),
Behaviour::Modal,
Button::Yes | Button::No);
if (b != Button::Yes)
return;
}
if (!m_configFile)
{
ERROR_LOG(m_logger) << "Not possible to read config: No file selected";
return;
}
if (!m_configFile->open(QIODevice::ReadOnly))
{
ERROR_LOG(m_logger) << "Not possible to read config: Could not open file \""
<< m_configFile->fileName()
<< "\" for reading.";
return;
}
m_mapSymbols.clear();
m_mapEchelons.clear();
m_mapTypeToSaabSymbol.clear();
m_mapPartyToForce.clear();
m_mapForces.clear();
QXmlStreamReader reader(m_configFile);
using namespace XmlStrings;
while (!reader.atEnd())
{
if (reader.readNextStartElement())
{
if (reader.name() == SAAB_SYMBOL())
{
SaabSymbol newSymbol = readSymbol(&reader);
m_mapSymbols.insert(newSymbol.code, newSymbol);
}
else if (reader.name() == SAAB_ECHELON())
{
SaabEchelon newEchelon = readEchelon(&reader);
m_mapEchelons.insert(newEchelon.code, newEchelon);
}
else if (reader.name() == SAAB_FORCE())
{
SaabForce newForce = readForce(&reader);
m_mapForces.insert(newForce.text, newForce.code);
}
}
}
m_configFile->close();
for (auto it = m_parties.constBegin(); it != m_parties.constEnd(); ++it)
{
if (m_mapForces.isEmpty())
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("All Forces are mapped."));
break;
}
mapPartyToForce(it.value());
}
}
SaabSymbol ExportSaab::readSymbol(QXmlStreamReader *reader) const
{
SaabSymbol result;
using namespace XmlStrings;
while (reader->readNext() != QXmlStreamReader::EndElement && reader->name() != SAAB_SYMBOL())
{
if (reader->name() == SAAB_CODE())
result.code = reader->readElementText();
else if (reader->name() == SAAB_TEXT())
result.text = reader->readElementText();
else if (reader->name() == SAAB_SYMBOL_ID())
result.symbolId = reader->readElementText();
else if (reader->name() == SAAB_MODEL_ID())
result.modelId = reader->readElementText();
else if (reader->name() == SAAB_ECHELON_ID())
result.echelonId = reader->readElementText();
else if (reader->name() == SAAB_PLAYER_TYPE_ID())
result.playerTypeId = reader->readElementText();
else if (reader->name() == SAAB_COLOR())
result.color = reader->readElementText();
}
return result;
}
SaabEchelon ExportSaab::readEchelon(QXmlStreamReader *reader) const
{
SaabEchelon result;
using namespace XmlStrings;
while (reader->readNext() != QXmlStreamReader::EndElement && reader->name() != SAAB_ECHELON())
{
if (reader->name() == SAAB_CODE())
result.code = reader->readElementText();
else if (reader->name() == SAAB_TEXT())
result.text = reader->readElementText();
else if (reader->name() == SAAB_ID())
result.id = reader->readElementText();
}
return result;
}
SaabForce ExportSaab::readForce(QXmlStreamReader *reader) const
{
SaabForce result;
using namespace XmlStrings;
while (reader->readNext() != QXmlStreamReader::EndElement && reader->name() != SAAB_FORCE())
{
if (reader->name() == SAAB_CODE())
result.code = reader->readElementText();
else if (reader->name() == SAAB_TEXT())
result.text = reader->readElementText();
}
return result;
}
void ExportSaab::writeExercise(QXmlStreamWriter *writer) const
{
using namespace XmlStrings;
writer->writeStartElement(SAAB_EXERCISE());
writer->writeAttribute(SAAB_ID(), QUuid(l, m_currentExercise.id(), w2, b1, b2, b3, b4, b5, b6, b7, GuidContext::GuidExercise).toString(QUuid::WithoutBraces));
writer->writeTextElement(SAAB_EXPORT_TIME(), toSaabTime(m_session->partitionTime()));
writer->writeTextElement(SAAB_PLANNED_START_TIME(), toSaabTime(m_currentExercise.beginTime()));
writer->writeTextElement(SAAB_PLANNED_STOP_TIME(), toSaabTime(m_currentExercise.endTime()));
writer->writeTextElement(SAAB_DESIGNATION(), QString::fromStdString(m_currentExercise.name()));
writer->writeTextElement(SAAB_DESCRIPTION(), QString::fromStdString(m_currentExercise.description()));
writer->writeTextElement(SAAB_WIND_SPEED(), QStringLiteral("0"));
writer->writeTextElement(SAAB_WIND_DIRECTION(), QStringLiteral("0"));
writer->writeEmptyElement(SAAB_EVALUATION_PACKAGE_FILE_NAME());
writer->writeTextElement(SAAB_EXERCISE_STATE(), QStringLiteral("0"));
writer->writeEndElement();
}
void ExportSaab::writeOrbat(QXmlStreamWriter *writer)
{
using namespace XmlStrings;
writer->writeStartElement(SAAB_ORBAT());
QMap<int, ParticipantManagement::Participant> participants = m_ppmDtc->participants();
for (auto it = m_mapPartyToForce.constBegin(); it != m_mapPartyToForce.constEnd(); ++it)
{
SaabPlayer *player = toSaabPlayer(m_parties.value(it.key()), participants);
player->writeToXml(writer);
delete player;
player = nullptr;
}
writer->writeEndElement();
}
void ExportSaab::writeFooter(QXmlStreamWriter *writer) const
{
using namespace XmlStrings;
writer->writeEmptyElement(SAAB_PERSONS());
writer->writeEmptyElement(SAAB_VEHICLES());
writer->writeEmptyElement(SAAB_HELICOPTERS());
}
SaabPlayer *ExportSaab::toSaabPlayer(const ParticipantManagement::Party &party, QMap<int, ParticipantManagement::Participant> &remainingParticipants)
{
SaabPlayer *result;
result = new SaabUnit;
result->symbol = m_mapPartyToForce.value(party.id()).symbolCode;
static_cast<SaabUnit *>(result)->unitSize = m_mapPartyToForce.value(party.id()).echelonCode;
result->designation = QString::fromStdString(party.name());
result->trashcan = false;
result->isExternalPlayer = true;
result->force = m_mapPartyToForce.value(party.id()).forceCode;
result->guid = QUuid(l, m_currentExercise.id(), w2, static_cast<uchar>(party.id()), b2, b3, b4, b5, b6, b7, GuidContext::GuidParty).toString(QUuid::WithoutBraces);
QList<int> keys = remainingParticipants.keys();
// add the root units/participants
for (int key : qAsConst(keys))
{
if (!remainingParticipants.contains(key))
continue;
ParticipantManagement::Participant participant = remainingParticipants.value(key);
if (!(participant.partyId() == party.id() && participant.superiorId() == 0))
continue;
SaabPlayer *player = toSaabPlayer(participant, remainingParticipants);
if (!player)
continue;
static_cast<SaabUnit *>(result)->inferiors.append(player);
}
return result;
}
SaabPlayer *ExportSaab::toSaabPlayer(const ParticipantManagement::Participant &participant, QMap<int, ParticipantManagement::Participant> &remainingParticipants)
{
if (participant.isDeleted())
{
DEBUG_LOG(m_logger) << "Participant with id " << participant.id() << " id deleted --> skipping";
return nullptr;
}
if (participant.externalId())
{
DEBUG_LOG(m_logger) << "Particpant with id " << participant.id() << " has external ID set to " << participant.externalId() << " --> not being exported.";
return nullptr;
}
SaabPlayer *result;
ParticipantManagement::Type type = m_types.value(participant.typeId());
if (!m_mapTypeToSaabSymbol.contains(type.id()))
mapType(type);
switch (type.typeClass().underlying())
{
case ParticipantManagement::TypeClass::SupportEntity: // OC Player
result = new SaabOcPlayer;
break;
case ParticipantManagement::TypeClass::Human: // Person Player
result = new SaabPersonPlayer;
break;
case ParticipantManagement::TypeClass::GroundVehicle: // Vehicle Player
case ParticipantManagement::TypeClass::VehicleWithGear:
case ParticipantManagement::TypeClass::WeaponSystem:
result = new SaabVehiclePlayer;
break;
case ParticipantManagement::TypeClass::Unit: // Unit
case ParticipantManagement::TypeClass::SupportUnit:
{
result = new SaabUnit;
DEBUG_LOG(m_logger) << "Reading unit with " << participant.inferiorIds().size() << " inferiors";
for (int32_t id : participant.inferiorIds())
{
SaabPlayer *player = toSaabPlayer(remainingParticipants.value(id), remainingParticipants);
if (player == nullptr)
{
DEBUG_LOG(m_logger) << "Skipping inferior with id " << id;
continue;
}
static_cast<SaabUnit *>(result)->inferiors.append(player);
}
// do not export empty units
if (!static_cast<SaabUnit *>(result)->inferiors.size())
{
DEBUG_LOG(m_logger) << "Unit " << participant.id() << " has no inferiors.";
return nullptr;
}
break;
}
case ParticipantManagement::TypeClass::Vessel: // we do not support that for export atm
case ParticipantManagement::TypeClass::Aircraft:
case ParticipantManagement::TypeClass::Unknown:
default:
DEBUG_LOG(m_logger) << "Type " << participant.typeId() << " is not supported.";
return nullptr;
}
QString symbol = m_mapTypeToSaabSymbol.value(type.id());
if (typeid(*result) == typeid(SaabUnit))
{
QStringList symbolUnitSize = symbol.split(QLatin1Char(';'));
result->symbol = symbolUnitSize.at(0);
static_cast<SaabUnit *>(result)->unitSize = symbolUnitSize.at(1);
}
else
result->symbol = symbol;
result->designation = QString::fromStdString(participant.designator());
result->trashcan = participant.isDeleted();
result->rank = QStringLiteral("OF1");
result->isExternalPlayer = true;
if (participant.equipmentNumber().is_set())
result->simulatorId = participant.equipmentNumber().get();
result->force = m_mapPartyToForce.value(participant.partyId()).forceCode;
result->guid = QUuid(participant.id(), m_currentExercise.id(), w2, static_cast<uchar>(participant.partyId()), b2, b3, b4, b5, b6, b7, GuidContext::GuidParticipant).toString(QUuid::WithoutBraces);
return result;
}
ParticipantManagement::Participant ExportSaab::rootParticipant(const ParticipantManagement::Participant &participant, const QMap<int, ParticipantManagement::Participant> &participants) const
{
if (participant.superiorId())
return rootParticipant(participants.value(participant.superiorId()), participants);
return participant;
}
QString ExportSaab::toSaabTime(qint64 msecs) const
{
return QDateTime::fromMSecsSinceEpoch(msecs).toString(QStringLiteral("yyyy-MM-ddThh:mm:ssZ"));
}
void ExportSaab::mapType(const ParticipantManagement::Type &type)
{
using namespace ParticipantManagement;
MapType dialog(ParticipantManagement::trTypeName(type.name()),
m_mapEchelons,
m_mapSymbols,
type.typeClass() == TypeClass::Unit || type.typeClass() == TypeClass::SupportUnit,
this);
dialog.exec();
QString saabTypeStr = dialog.symbolCode();
if (!dialog.echelonCode().isEmpty())
{
saabTypeStr.append(QLatin1Char(';'));
saabTypeStr.append(dialog.echelonCode());
}
m_mapTypeToSaabSymbol.insert(type.id(), saabTypeStr);
}
void ExportSaab::mapPartyToForce(const ParticipantManagement::Party &party)
{
MapParty dialog(QString::fromStdString(party.name()), m_mapForces, m_mapEchelons, m_mapSymbols, this);
dialog.exec();
if (dialog.isSkipped())
return;
m_mapPartyToForce.insert(party.id(), { m_mapForces.take(dialog.force()), dialog.echelonCode(), dialog.symbolCode() });
}
}