918 lines
32 KiB
C++
Executable File
918 lines
32 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Importieren von MSDL-Dateien
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
// at first to avoid compiler errors on windows (CONST as enum value)
|
|
#include <xsd/MilitaryScenario_1.0.0.hxx>
|
|
#include <xsd/MsdlComplexTypes_1.0.0.hxx>
|
|
|
|
#include "msdl_import.h"
|
|
#include "ui_msdl_import.h"
|
|
#include "msdl_logging.h"
|
|
#include "msdl_import_show.h"
|
|
#include "msdl_mapping_dialog.h"
|
|
|
|
#include <QFileDialog>
|
|
#include <QFile>
|
|
#include <QUuid>
|
|
#include <QTextStream>
|
|
#include <QUrl>
|
|
#include <QAbstractMessageHandler>
|
|
#include <QDomDocument>
|
|
#include <QMimeData>
|
|
#include <QProcess>
|
|
#include <QtXmlPatterns/QXmlSchema>
|
|
#include <QtXmlPatterns/QXmlSchemaValidator>
|
|
|
|
#include <ngltools/ngltools_filesystem.h>
|
|
#include <Common/participant_dis_helper.h>
|
|
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
|
|
#include <LtsSession/lts_session.h>
|
|
#include <LtsUtilsSettings/settings.h>
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <QDir>
|
|
|
|
|
|
namespace msdl
|
|
{
|
|
static QString gStrMSDLWorkingFolderPath()
|
|
{
|
|
return QDir::tempPath() + QLatin1String("/excon_msdl/");
|
|
}
|
|
|
|
static QString gStrMSDLWorkingFile()
|
|
{
|
|
return gStrMSDLWorkingFolderPath() + QStringLiteral("msdl_user_input.xml");
|
|
}
|
|
|
|
|
|
using descriptionType = schemas::modelID::modelIdentificationType::description_type;
|
|
using organizationsType = msdl_1::MilitaryScenarioType::Organizations_type;
|
|
using equipType = organizationsType::Equipment_type;
|
|
using equipItemType = equipType::EquipmentItem_type;
|
|
using equipOptional = organizationsType::Equipment_optional;
|
|
using equipRelationsType = equipItemType::Relations_type;
|
|
using unitsType = organizationsType::Units_type;
|
|
using unitType = unitsType::Unit_type;
|
|
using relationsType = unitType::Relations_type;
|
|
using forceRelationDataType = relationsType::ForceRelation_type::ForceRelationData_type;
|
|
using forceSidesType = msdl_1::MilitaryScenarioType::ForceSides_type;
|
|
using forceSideType = forceSidesType::ForceSide_type;
|
|
using ownerChoiceType = equipRelationsType::HoldingOrganization_type::OwnerChoice_type;
|
|
using ownerDataType = equipRelationsType::HoldingOrganization_type::OwnerData_type;
|
|
using purposeOptional = schemas::modelID::modelIdentificationType::purpose_optional;
|
|
|
|
|
|
ImportMsdl::ImportMsdl(ParticipantManagement::ParticipantEditModel *pModel, const QModelIndex &index, QWidget *parent, LtsSession::LtsSession *pSession)
|
|
: NGLDialog(parent)
|
|
, m_indexRoot(index)
|
|
, m_pModel(pModel)
|
|
, m_requestHelper(new LtsUtils::RequestHelper(this))
|
|
, m_pMsdlImportShow(nullptr)
|
|
, m_pUi(new Ui_MSDLImport)
|
|
, mLogger(msdl::getLogger("ImportMsdl"))
|
|
{
|
|
m_pUi->setupUi(this);
|
|
setWindowTitle(tr("Import Exercise Scenario"));
|
|
|
|
|
|
if (!pSession->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc) || !pSession->getFtc<ParticipantManagement::DataInterface>(m_ppmFtc))
|
|
{
|
|
ERROR_LOG(mLogger) << "Fetching m_ppmDtc or m_ppmFtc failed!";
|
|
setEnabled(false);
|
|
}
|
|
else
|
|
{
|
|
m_mapTypes = m_ppmDtc->types();
|
|
|
|
/*
|
|
* Store all DIS ids of each type in 'm_mapDisStr2TypeId' to allow a mapping between
|
|
* DIS ids and types when importing units.
|
|
*
|
|
* Note: A type may have more than one DIS id.
|
|
* Note: Each type should have an assigned DIS id! Otherwise it will not be available for mapping!
|
|
*/
|
|
for (const ParticipantManagement::Type &type : qAsConst(m_mapTypes))
|
|
{
|
|
for (const auto &disId : type.disEntities())
|
|
{
|
|
QString strMSDL_DISId = ParticipantManagement::DIS::convertDISTypeInteger2MSDLString(disId);
|
|
|
|
if (m_mapMSDL_DIS_2TypeId.find(strMSDL_DISId) != m_mapMSDL_DIS_2TypeId.end())
|
|
{
|
|
WARNING_LOG(mLogger) << "DIS id \"" << strMSDL_DISId << "\" which is assigned to type id \"" << type.id() << "\" is already assigned to type id \"" << m_mapMSDL_DIS_2TypeId[strMSDL_DISId] << "\"";
|
|
continue;
|
|
}
|
|
|
|
m_mapMSDL_DIS_2TypeId[strMSDL_DISId] = type.id();
|
|
}
|
|
}
|
|
}
|
|
|
|
QDir dir(gStrMSDLWorkingFolderPath());
|
|
dir.removeRecursively();
|
|
|
|
if (!dir.mkpath(QStringLiteral(".")))
|
|
{
|
|
ERROR_LOG(mLogger) << "Could not create path \"" << gStrMSDLWorkingFolderPath() << "\" which is required to parse MSDL data";
|
|
setEnabled(false);
|
|
}
|
|
|
|
if (!NGL::copyDirContentsToDir(QStringLiteral("msdl"), gStrMSDLWorkingFolderPath(), true))
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Not possible to start MSDL import. Could not copy validation files."));
|
|
ERROR_LOG(mLogger) << "Could not copy xsd files to " << gStrMSDLWorkingFolderPath();
|
|
return;
|
|
}
|
|
|
|
connect(m_pUi->m_pushbutton_SelectFile, &QPushButton::clicked,
|
|
this, &ImportMsdl::slotOpenImportDialog);
|
|
connect(m_pUi->m_pushButton_StartImport, &QPushButton::clicked,
|
|
this, &ImportMsdl::slotStartImport);
|
|
|
|
QString strImportFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("MSDL"), QStringLiteral("pathSelectedImportFile"), QString()).toString();
|
|
m_pUi->m_lineedit_ImportFile->setText(strImportFile);
|
|
m_pUi->m_pushButton_StartImport->setEnabled(strImportFile.size());
|
|
}
|
|
|
|
|
|
ImportMsdl::~ImportMsdl()
|
|
{
|
|
delete m_pUi;
|
|
}
|
|
|
|
|
|
void ImportMsdl::slotOpenImportDialog()
|
|
{
|
|
QString strPathSelectedImportFolder = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("MSDL"), QStringLiteral("pathSelectedImportFolder"), QStringLiteral("/shd/CTC/TOOLS/TDS/")).toString();
|
|
QString strPathSelectedImportFile = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("MSDL"), QStringLiteral("pathSelectedImportFile"), QStringLiteral("/shd/CTC/TOOLS/TDS/")).toString();
|
|
|
|
QList<QUrl> urls;
|
|
urls << QUrl::fromLocalFile(strPathSelectedImportFile);
|
|
|
|
QFileDialog fileDialog(this, tr("Select File"), strPathSelectedImportFolder, tr("XML files (*.xml)"));
|
|
fileDialog.setSidebarUrls(urls);
|
|
fileDialog.setOptions(QFileDialog::ReadOnly);
|
|
fileDialog.setFileMode(QFileDialog::ExistingFile);
|
|
|
|
if (fileDialog.exec()) // TODO: use show(), use signals & slots !
|
|
{
|
|
strPathSelectedImportFile = fileDialog.selectedFiles().constFirst();
|
|
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("MSDL"), QStringLiteral("pathSelectedImportFolder"), strPathSelectedImportFolder);
|
|
|
|
if (!strPathSelectedImportFile.size())
|
|
return;
|
|
|
|
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("MSDL"), QStringLiteral("pathSelectedImportFile"), strPathSelectedImportFile);
|
|
m_pUi->m_lineedit_ImportFile->setText(strPathSelectedImportFile);
|
|
m_pUi->m_pushButton_StartImport->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
|
|
void ImportMsdl::slotStartImport()
|
|
{
|
|
QString strPathSelectedImportFile = m_pUi->m_lineedit_ImportFile->text();
|
|
createWorkingCopy(strPathSelectedImportFile);
|
|
|
|
if (!schemaValidation())
|
|
return;
|
|
|
|
#ifdef LTS_DEBUG
|
|
delete m_pMsdlImportShow;
|
|
m_pMsdlImportShow = new msdl::ImportMsdlShow(nullptr, gStrMSDLWorkingFile());
|
|
m_pMsdlImportShow->show();
|
|
#endif
|
|
|
|
QFile file(gStrMSDLWorkingFile());
|
|
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
{
|
|
ERROR_LOG(mLogger) << "Could not open msdl working copy: " << gStrMSDLWorkingFile();
|
|
return;
|
|
}
|
|
|
|
QDomDocument doc(QStringLiteral("mydocument"));
|
|
|
|
if (!doc.setContent(&file))
|
|
{
|
|
file.close();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
importExerciseScenario();
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
// There is so much that could be wrong when importing or exporting MSDL files (missing tags, optional tags, ...)
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("MSDL import failed!"));
|
|
ERROR_LOG(mLogger) << "MSDL import failed with an exception, e.what(): " << e.what();
|
|
}
|
|
|
|
close();
|
|
}
|
|
|
|
|
|
void ImportMsdl::createWorkingCopy(const QString &strImportFile)
|
|
{
|
|
// Input File
|
|
QFile file(strImportFile);
|
|
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Selected input file doesn't exist on filesystem. Has it been deleted?"), tr("Error"));
|
|
return;
|
|
}
|
|
|
|
|
|
// Output File
|
|
QFile fileWithNamespace(gStrMSDLWorkingFile());
|
|
|
|
if (!fileWithNamespace.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Cannot create working copy of input file on filesystem:") + QLatin1Char('\n') + gStrMSDLWorkingFile(), tr("Error"));
|
|
return;
|
|
}
|
|
|
|
QTextStream inputTextStream(&file);
|
|
QTextStream outputTextStream(&fileWithNamespace);
|
|
|
|
QString line;
|
|
bool kommentar = false;
|
|
do
|
|
{
|
|
line = inputTextStream.readLine();
|
|
|
|
if (kommentar && !line.contains(QStringLiteral("-->")))
|
|
continue;
|
|
|
|
if (line.contains(QStringLiteral("-->")))
|
|
{
|
|
kommentar = false;
|
|
continue;
|
|
}
|
|
|
|
if (line.contains(QStringLiteral("<!--")))
|
|
{
|
|
kommentar = true;
|
|
continue;
|
|
}
|
|
|
|
if ((!line.contains(QStringLiteral("msdl:"))) &&
|
|
(!line.contains(QStringLiteral("?xml"))) &&
|
|
(!line.contains(QStringLiteral("<!"))) &&
|
|
(!line.contains(QStringLiteral("<modelID"))) &&
|
|
(!line.contains(QStringLiteral("</modelID"))) &&
|
|
(!line.contains(QStringLiteral("<?xml"))))
|
|
{
|
|
line.replace(QStringLiteral("<"), QStringLiteral("<msdl:"));
|
|
line.replace(QStringLiteral("<msdl:/"), QStringLiteral("</msdl:"));
|
|
}
|
|
|
|
if (line.contains(QStringLiteral("<msdl:MilitaryScenario")))
|
|
line = QStringLiteral("<msdl:MilitaryScenario xmlns:msdl=\"urn:sisostds:scenario:military:data:draft:msdl:1\" xmlns:modelID=\"http://www.sisostds.org/schemas/modelID\" xmlns:jc3=\"urn:int:nato:standard:mip:jc3iedm:3.1:oo:2.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:sisostds:scenario:military:data:draft:msdl:1 ./MilitaryScenario_1.0.0.xsd\">");
|
|
|
|
outputTextStream << line << "\n";
|
|
}
|
|
while (!line.isNull());
|
|
}
|
|
|
|
|
|
bool ImportMsdl::schemaValidation()
|
|
{
|
|
QString strPathToSchemaFile = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("MilitaryScenario_1.0.0.xsd"));
|
|
QFile schemaFile(strPathToSchemaFile);
|
|
|
|
if (!schemaFile.open(QIODevice::ReadOnly))
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("MilitaryScenario_1.0.0.xsd doesn't exist on filesystem"), tr("Validation Error"));
|
|
return false;
|
|
}
|
|
|
|
QXmlSchema schema;
|
|
msdl::SchemaMessageHandler smh;
|
|
schema.setMessageHandler(&smh);
|
|
schema.load(&schemaFile, QUrl::fromLocalFile(schemaFile.fileName()));
|
|
|
|
// Erst einmal das Schema (*.xsd) ueberpruefen
|
|
if (!schema.isValid())
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Schema is invalid"));
|
|
return false;
|
|
}
|
|
|
|
QProcess xmllint_cmd;
|
|
xmllint_cmd.setProcessChannelMode(QProcess::MergedChannels);
|
|
xmllint_cmd.start(QStringLiteral("xmllint"), QStringList()
|
|
<< QStringLiteral("--noout")
|
|
<< QStringLiteral("--schema")
|
|
<< schemaFile.fileName()
|
|
<< gStrMSDLWorkingFile());
|
|
|
|
|
|
if (!xmllint_cmd.waitForFinished())
|
|
{
|
|
ERROR_LOG(mLogger) << "Validation Fault cmd";
|
|
return false;
|
|
}
|
|
|
|
#ifdef LTS_DEBUG
|
|
QByteArray validation = xmllint_cmd.readAllStandardOutput();
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, QString::fromLatin1(validation), tr("Info"));
|
|
|
|
if (!validation.contains("validates"))
|
|
return false;
|
|
#endif
|
|
|
|
// Nun das Schema (*.xsd) gegen das zu importierende MilitaryScenario (*.xml) validieren
|
|
QXmlSchemaValidator validator(schema);
|
|
|
|
if (false /*|| validator.validate(QUrl::fromLocalFile(mImportFile))*/)
|
|
{
|
|
#ifdef LTS_DEBUG
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Instance document is invalid"));
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
ParticipantManagement::ParticipantData ImportMsdl::createParticipant(const MSDLParticipant &unit)
|
|
{
|
|
ParticipantManagement::ParticipantData p;
|
|
|
|
// Designator
|
|
p.designator(unit.m_designator.toStdString());
|
|
p.tacticalIdentifier(unit.m_tacticalIdentifier.toStdString());
|
|
|
|
// State and Equipment
|
|
p.equipmentNumber() = 0;
|
|
p.assignState() = ParticipantManagement::ParticipantAssignState::HandoutExcon;
|
|
|
|
/* Symbol respectively sidc
|
|
* We are not using the imported 2525c symbol (that would be: p.symbol(unit.m_symbolIdentifier.toStdString());)
|
|
* Instead, we use the symbol of the mapped type!
|
|
*/
|
|
p.symbol(m_mapTypes[unit.m_mappedTypeId].symbol());
|
|
p.typeId(unit.m_mappedTypeId); // Class type is internally derived from typeid, no need (and possibility) to set it here
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
void ImportMsdl::insertParticipant(const MSDLParticipant &unit, const QModelIndex &index)
|
|
{
|
|
ParticipantManagement::ParticipantData newParticipant = createParticipant(unit);
|
|
createNode(newParticipant, index, unit.m_ownHandle);
|
|
}
|
|
|
|
|
|
/*
|
|
* All parties respectively forces are display in one treewidget. Hence, they represent root nodes which means
|
|
* they are directly appended to the QModelIndex 'm_indexRoot'.
|
|
*/
|
|
void ImportMsdl::insertParty(const MSDLParty &party)
|
|
{
|
|
// Create "Force"-Node -> No symbol, no tact. identifier, no classtype
|
|
ParticipantManagement::ParticipantData newParty;
|
|
newParty.designator(party.m_forceSideName.toStdString());
|
|
createNode(newParty, m_indexRoot, party.m_ownHandle);
|
|
}
|
|
|
|
|
|
void ImportMsdl::createNode(const ParticipantManagement::ParticipantData &participant, const QModelIndex &index, const QString &handle)
|
|
{
|
|
int newRow = m_pModel->onInsertRow(index, participant);
|
|
QModelIndex newIdx = m_pModel->index(newRow, 0, index);
|
|
m_parentIndexCache[handle] = newIdx;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Should be called after each 'insertParticipant' since the newly added participant
|
|
* might be the parent of one of the so far parentless units.
|
|
*/
|
|
void ImportMsdl::checkParentlessUnits()
|
|
{
|
|
// Check all Units which have not been added so far because their parent is missing
|
|
bool parentFound = false;
|
|
do
|
|
{
|
|
parentFound = false;
|
|
|
|
for (auto itParentless = m_parentlessItems.begin(); itParentless != m_parentlessItems.end();)
|
|
{
|
|
const MSDLParticipant &parentlessUnit = (*itParentless);
|
|
auto itParent = m_parentIndexCache.find(parentlessUnit.m_parentHandle);
|
|
|
|
// Parent now available in tree?
|
|
if (itParent != m_parentIndexCache.end())
|
|
{
|
|
parentFound = true;
|
|
insertParticipant(parentlessUnit, (*itParent));
|
|
itParentless = m_parentlessItems.erase(itParentless);
|
|
}
|
|
else
|
|
++itParentless;
|
|
}
|
|
}
|
|
while (parentFound);
|
|
}
|
|
|
|
|
|
void ImportMsdl::importExerciseScenario()
|
|
{
|
|
importPurpose();
|
|
|
|
QVector<MSDLParty> vecParties = importParties();
|
|
|
|
for (auto &party : vecParties)
|
|
insertParty(party);
|
|
|
|
QMap<QString, MSDLParticipant> mapUnits = importParticipants();
|
|
importEquipment(mapUnits);
|
|
QVector<MSDLParticipant> vecUnitsWithValidDISTypes = resolveImportMapping(mapUnits);
|
|
|
|
if (!vecUnitsWithValidDISTypes.size())
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("There are no units or participants which can be imported."), tr("Import Warning"));
|
|
return;
|
|
}
|
|
|
|
buildImportTreeView(vecUnitsWithValidDISTypes);
|
|
|
|
if (m_setTouchedTypes.size())
|
|
{
|
|
LtsWidgets::Button button = LtsWidgets::LtsMessageDialog::showMessage(this, tr("You have applied manual DIS mappings between imported units/participants and available database types.<br/>Do you want to save this mapping to database?<br/><br/> If you choose \"No\" you will have to perform the DIS mapping again when importing the same input file at a later timepoint."), tr("Import Information"),
|
|
LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::No);
|
|
|
|
if (button == LtsWidgets::Button::Yes)
|
|
{
|
|
while (!applyDISMappingChangesToDatabase())
|
|
{
|
|
LtsWidgets::Button button = LtsWidgets::LtsMessageDialog::showMessage(this, tr("Applied DIS mapping changes could not be saved to database.<br/>Do you want to retry?"), tr("Import Warning"),
|
|
LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::No);
|
|
|
|
if (button == LtsWidgets::Button::No)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ImportMsdl::importPurpose()
|
|
{
|
|
try
|
|
{
|
|
std::unique_ptr<msdl_1::MilitaryScenarioType> p(msdl_1::MilitaryScenario(gStrMSDLWorkingFile().toStdString()));
|
|
msdl_1::MilitaryScenarioType::ScenarioID_type scIDType = p->ScenarioID();
|
|
|
|
// Purpose
|
|
QString strPurpose;
|
|
QString strDescription;
|
|
purposeOptional pO = scIDType.purpose();
|
|
|
|
if (!pO)
|
|
{
|
|
#ifdef LTS_DEBUG
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("No purpose in xml-file"), windowTitle());
|
|
#endif
|
|
}
|
|
else
|
|
strPurpose = QString::fromStdString(pO.get());
|
|
|
|
// Description
|
|
descriptionType dT = scIDType.description();
|
|
if (dT.empty())
|
|
{
|
|
#ifdef LTS_DEBUG
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("No description in xml-file"), windowTitle());
|
|
#endif
|
|
}
|
|
else
|
|
strDescription = QString::fromStdString(dT);
|
|
|
|
WARNING_LOG(mLogger) << "importPurpose: ToDo: Unused imported data (purpose: \"" << strPurpose << "\", description: \"" << strDescription << "\")";
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
WARNING_LOG(mLogger) << "importPurpose: exception occurred: " << e.what();
|
|
}
|
|
}
|
|
|
|
|
|
QVector<MSDLParty> ImportMsdl::importParties()
|
|
{
|
|
std::unique_ptr<msdl_1::MilitaryScenarioType> p(msdl_1::MilitaryScenario(gStrMSDLWorkingFile().toStdString()));
|
|
forceSidesType foSides = p->ForceSides();
|
|
forceSidesType::ForceSide_sequence foSiSeq = foSides.ForceSide();
|
|
|
|
QVector<MSDLParty> vecForces;
|
|
vecForces.reserve(foSiSeq.size());
|
|
for (const auto &foSiTy : foSiSeq)
|
|
{
|
|
MSDLParty party; // Destination
|
|
|
|
party.m_ownHandle = QString::fromStdString(foSiTy.ObjectHandle());
|
|
party.m_forceSideName = QString::fromStdString(foSiTy.ForceSideName());
|
|
|
|
vecForces.push_back(std::move(party));
|
|
}
|
|
|
|
return vecForces;
|
|
}
|
|
|
|
|
|
QMap<QString, MSDLParticipant> ImportMsdl::importParticipants()
|
|
{
|
|
QMap<QString, MSDLParticipant> mapParticipants; // Handle to Participant
|
|
|
|
std::unique_ptr<msdl_1::MilitaryScenarioType> p(msdl_1::MilitaryScenario(gStrMSDLWorkingFile().toStdString()));
|
|
|
|
msdl_1::MilitaryScenarioType::Organizations_optional orgOpt = p->Organizations();
|
|
|
|
if (!orgOpt)
|
|
{
|
|
WARNING_LOG(mLogger) << "Optional msdl xml tag \"Organizations\" missing. There are not participants to import!";
|
|
return mapParticipants;
|
|
}
|
|
|
|
organizationsType orgType = orgOpt.get();
|
|
unitsType units = orgType.Units();
|
|
unitsType::Unit_sequence unitSeq = units.Unit();
|
|
|
|
QSet<QString> setParents; // All parents are of type class "Unit"
|
|
for (const auto &uTy : unitSeq)
|
|
{
|
|
try
|
|
{
|
|
MSDLParticipant tUnit; // Destination
|
|
|
|
// Designator
|
|
QString designator(QStringLiteral("Unknown"));
|
|
|
|
unitType::Name_optional nameOpt = uTy.Name();
|
|
if (nameOpt)
|
|
designator = QString::fromStdString(nameOpt.get());
|
|
|
|
tUnit.m_designator = designator.left(9);
|
|
tUnit.m_tacticalIdentifier = designator;
|
|
|
|
// Symbol respectively sidc
|
|
unitType::SymbolIdentifier_type symIdTy = uTy.SymbolIdentifier();
|
|
tUnit.m_symbolIdentifier = QString::fromStdString(symIdTy);
|
|
|
|
// Currently ignoring "hierarchy"
|
|
|
|
// Element 'ForceSideHandle' stellt den Bezug zur Truppe her
|
|
// Element 'CommandingSuperiorHandle' stellt den Bezug zu einer uebergeordneten Einheit her
|
|
relationsType reTyps = uTy.Relations();
|
|
relationsType::ForceRelation_type reTyp = reTyps.ForceRelation();
|
|
relationsType::ForceRelation_type::ForceRelationChoice_type foRelChTy = reTyp.ForceRelationChoice();
|
|
|
|
|
|
unitType::ObjectHandle_type obHaTy = uTy.ObjectHandle();
|
|
tUnit.m_ownHandle = QString::fromStdString(obHaTy);
|
|
|
|
|
|
// Einheit ist direkt unter der Truppe
|
|
if (foRelChTy == relationsType::ForceRelation_type::ForceRelationChoice_type::FORCE_SIDE)
|
|
{
|
|
// obHaTy ist eigener Object-Handle. "0", weil Vorgesetzter die Pooltruppe
|
|
forceRelationDataType::ForceSideHandle_optional foSiHaOp = uTy.Relations().ForceRelation().ForceRelationData().ForceSideHandle();
|
|
tUnit.m_parentForce = QString::fromStdString(foSiHaOp.get());
|
|
tUnit.m_parentHandle = QStringLiteral("0"); // 0 for "FORCE_SIDE" types since forces are always on top without parent
|
|
setParents.insert(tUnit.m_ownHandle);
|
|
DEBUG_LOG(mLogger) << "Party: handle: " << tUnit.m_ownHandle << ", parentHandle: " << tUnit.m_parentHandle << ", parent force: " << tUnit.m_parentForce;
|
|
}
|
|
else if (foRelChTy == relationsType::ForceRelation_type::ForceRelationChoice_type::UNIT)
|
|
{
|
|
// Einheit ist unter einer anderen Einheit
|
|
// Vorgesetzte Einheit ermitteln
|
|
forceRelationDataType::CommandRelation_optional cmdRelOpt = uTy.Relations().ForceRelation().ForceRelationData().CommandRelation();
|
|
forceRelationDataType::CommandRelation_type cmdRelTyp = cmdRelOpt.get();
|
|
|
|
tUnit.m_parentHandle = QString::fromStdString(cmdRelTyp.CommandingSuperiorHandle());
|
|
WARNING_LOG(mLogger) << "Participant: handle: " << tUnit.m_ownHandle.toStdString() << ", parentHandle: " << tUnit.m_parentHandle;
|
|
|
|
setParents.insert(tUnit.m_parentHandle);
|
|
}
|
|
|
|
mapParticipants[tUnit.m_ownHandle] = tUnit;
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
ERROR_LOG(mLogger) << "Exception occured when importing unit, skipping unit! " << e.what();
|
|
}
|
|
}
|
|
|
|
// Each parent is of type class 'Unit' or 'SupportUnit'
|
|
for (auto &e : setParents)
|
|
mapParticipants[e].m_typeClassIsUnit = true;
|
|
|
|
return mapParticipants;
|
|
}
|
|
|
|
|
|
void ImportMsdl::importEquipment(QMap<QString, MSDLParticipant> &mapUnits)
|
|
{
|
|
std::unique_ptr<msdl_1::MilitaryScenarioType> p(msdl_1::MilitaryScenario(gStrMSDLWorkingFile().toStdString()));
|
|
|
|
msdl_1::MilitaryScenarioType::Organizations_optional orgOpt = p->Organizations();
|
|
|
|
if (!orgOpt)
|
|
{
|
|
WARNING_LOG(mLogger) << "Optional msdl xml tag \"Organizations\" missing. There is no equipment to import!";
|
|
return;
|
|
}
|
|
|
|
organizationsType orgType = orgOpt.get();
|
|
equipOptional equipOpt = orgType.Equipment();
|
|
|
|
if (!equipOpt)
|
|
{
|
|
WARNING_LOG(mLogger) << "Optional msdl xml tag \"Equipment\" missing: There is no equipment to import!";
|
|
return;
|
|
}
|
|
|
|
equipType eqTy = equipOpt.get();
|
|
equipType::EquipmentItem_sequence eqItSeq = eqTy.EquipmentItem();
|
|
|
|
for (const auto &eqItType : eqItSeq)
|
|
{
|
|
// Equipment handle
|
|
equipItemType::ObjectHandle_type obHaTy = eqItType.ObjectHandle();
|
|
QString equipmentHandle = QString::fromStdString(obHaTy);
|
|
|
|
// Owner (unit) handle
|
|
equipRelationsType reTyps = eqItType.Relations();
|
|
ownerDataType::UnitOwnerHandle_optional unOwHa = reTyps.HoldingOrganization().OwnerData().UnitOwnerHandle();
|
|
|
|
if (!unOwHa)
|
|
{
|
|
WARNING_LOG(mLogger) << "No owner handle found for equipment handle " << equipmentHandle << ", ignoring equipment";
|
|
continue;
|
|
}
|
|
|
|
QString ownerHandle = QString::fromStdString(unOwHa.get());
|
|
|
|
if (mapUnits.find(ownerHandle) == mapUnits.end())
|
|
{
|
|
WARNING_LOG(mLogger) << "Equipment owner handle " << ownerHandle << " for equipment handle " << equipmentHandle << " does not exist, ignoring equipment";
|
|
continue;
|
|
}
|
|
|
|
equipItemType::EquipmentSymbolModifiers_optional eqSymModifiers = eqItType.EquipmentSymbolModifiers();
|
|
|
|
if (!eqSymModifiers)
|
|
{
|
|
WARNING_LOG(mLogger) << "No EquipmentSymbolModifier found for equipment handle " << equipmentHandle << ", not importing equipment and therefore skipping associated unit";
|
|
continue;
|
|
}
|
|
|
|
// prüfen ob vorhanden bzw. 21 zeichen?
|
|
QString strDisType = QString::fromStdString(eqSymModifiers.get().UniqueDesignation());
|
|
|
|
if (strDisType.size() != 21)
|
|
{
|
|
WARNING_LOG(mLogger) << "IGNORING Equipment handle " << equipmentHandle << ": MSDL DIS string length does not equal 21 characters: " << strDisType << " (ownerHandle: " << ownerHandle << ")";
|
|
continue;
|
|
}
|
|
|
|
WARNING_LOG(mLogger) << "Equipment: Handle " << equipmentHandle << ". DIS string " << strDisType << " ownerHandle: " << ownerHandle;
|
|
mapUnits[ownerHandle].m_DISTypeMSDL = strDisType;
|
|
|
|
// Equipment name
|
|
// equipItemType::Name_optional nameOpt = eqItType.Name();
|
|
// if(nameOpt)
|
|
// QString::fromStdString(nameOpt.get());
|
|
|
|
// Equipment Symbolidentifier
|
|
// equipItemType::SymbolIdentifier_type symIdTy = eqItType.SymbolIdentifier();
|
|
// symbolIdentifier = QString::fromStdString(symIdTy);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ImportMsdl::buildImportTreeView(QVector<MSDLParticipant> &vecUnitsWithValidDISTypes)
|
|
{
|
|
// Sort units by their designator which allows to create a treeview that has ordered elements
|
|
std::sort(vecUnitsWithValidDISTypes.begin(), vecUnitsWithValidDISTypes.end(), [](const MSDLParticipant & unit1, const MSDLParticipant & unit2)
|
|
{
|
|
return QString::localeAwareCompare(unit1.m_designator, unit2.m_designator) < 0;
|
|
});
|
|
|
|
|
|
for (const MSDLParticipant &p : vecUnitsWithValidDISTypes)
|
|
{
|
|
/*
|
|
* At this point we have to distinguish between 'UNIT' and 'FORCE_SIDE'.
|
|
* Force sides do not have a 'm_parentHandle' (they are a root node within the xml-file).
|
|
* Hence, we have to append them to a actual force which is stored in 'm_parentForce'.
|
|
* A 'UNIT' however as always a parent.
|
|
*/
|
|
auto it = (p.m_parentHandle != QStringLiteral("0")) ? m_parentIndexCache.find(p.m_parentHandle) : m_parentIndexCache.find(p.m_parentForce);
|
|
|
|
if (it == m_parentIndexCache.end())
|
|
{
|
|
m_parentlessItems[p.m_ownHandle] = p;
|
|
continue;
|
|
}
|
|
|
|
QModelIndex &parentIdx = (*it);
|
|
insertParticipant(p, parentIdx);
|
|
checkParentlessUnits();
|
|
}
|
|
|
|
for (auto &unit : m_parentlessItems)
|
|
WARNING_LOG(mLogger) << "Parent missing: Could not import unit with handle " << unit.m_ownHandle << ", missing parent: " << unit.m_parentHandle;
|
|
}
|
|
|
|
|
|
bool ImportMsdl::applyDISMappingChangesToDatabase()
|
|
{
|
|
// Apply changes (=user mapped imported unknown DIS ids to database type ids) to database
|
|
for (int32_t typeId : qAsConst(m_setTouchedTypes))
|
|
{
|
|
const ParticipantManagement::Type &touchedType = m_mapTypes.value(typeId);
|
|
|
|
m_requestHelper->addRequest([this, touchedType]()
|
|
{
|
|
return m_ppmFtc->modifyTypeRequest(touchedType, this);
|
|
});
|
|
}
|
|
|
|
return m_requestHelper->startRequests();
|
|
}
|
|
|
|
|
|
void ImportMsdl::mapParticipant2Type(MSDLParticipant &participant, qint32 mappedTypeId)
|
|
{
|
|
participant.m_mappedTypeId = mappedTypeId;
|
|
|
|
/*
|
|
* We can map non existing DIS id (=empty QString) to type ids.
|
|
* However, we cannot extend the type disEntities()-vector within the database since in this case since there is
|
|
* simply no DIS id which could be appended to the vector.
|
|
* Hence, mapping units/participants with no DIS id to a type id is only temporary and needs to be done each time
|
|
* a MSDL file is used for import.
|
|
*/
|
|
if (!participant.hasDISType())
|
|
return;
|
|
|
|
/*
|
|
* It may be possible that several participants have the same unknown DIS id.
|
|
* Prevent adding the same DIS id again and again to the same type.
|
|
*/
|
|
if (!m_mapMSDL_DIS_2TypeId.contains(participant.m_DISTypeMSDL))
|
|
{
|
|
m_mapMSDL_DIS_2TypeId[participant.m_DISTypeMSDL] = mappedTypeId;
|
|
|
|
m_mapTypes[mappedTypeId].disEntities().push_back(ParticipantManagement::DIS::convertMSDLString2Integer(participant.m_DISTypeMSDL));
|
|
m_setTouchedTypes.insert(mappedTypeId);
|
|
}
|
|
}
|
|
|
|
|
|
QVector<MSDLParticipant> ImportMsdl::resolveImportMapping(const QMap<QString, MSDLParticipant> &mapUnits)
|
|
{
|
|
QVector<MSDLParticipant> vecObjectsWithValidDISTypes;
|
|
|
|
/*
|
|
* Identify possible conflicts. Each conflict needs to be solved or ignored by the user.
|
|
*
|
|
* Note: We have to distinguish between actual participants (which will be later leaves in the treemodel) and units such as "Platoon".
|
|
* A MSDLParticipant can be both. If a MSDLUnit has no childs it is a participant otherwise an actual unit.
|
|
*
|
|
* - Participants can only be edited and appended to units.
|
|
* - Units can additionally be extended adding new participants.
|
|
*
|
|
* Hence, if the user needs to manually specify the type only the classes "Unit" and "SupportUnit" should be available for units
|
|
* to create a valid treemodel.
|
|
*/
|
|
QMap<QString, QVector<MSDLParticipant>> mapParticipantsWithUnknownDisType, mapUnitsWithUnknownDisType;
|
|
|
|
for (const MSDLParticipant &unit : mapUnits)
|
|
{
|
|
/*
|
|
* No DIS type: User has to perform a manual mapping
|
|
* DIS type available but there is no type in our database that matches: User has to perform a manual mapping
|
|
*/
|
|
if (!unit.hasDISType() || !m_mapMSDL_DIS_2TypeId.contains(unit.m_DISTypeMSDL))
|
|
{
|
|
if (unit.m_typeClassIsUnit)
|
|
mapUnitsWithUnknownDisType[unit.m_DISTypeMSDL].push_back(unit);
|
|
else
|
|
mapParticipantsWithUnknownDisType[unit.m_DISTypeMSDL].push_back(unit);
|
|
}
|
|
else
|
|
{
|
|
// A match between the imported unit (its DIS id) and one of the database types exists: Perform mapping, everything is fine.
|
|
vecObjectsWithValidDISTypes.push_back(unit);
|
|
vecObjectsWithValidDISTypes.back().m_mappedTypeId = m_mapMSDL_DIS_2TypeId[unit.m_DISTypeMSDL];
|
|
}
|
|
}
|
|
|
|
if (mapUnitsWithUnknownDisType.size())
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("<b>Units</b> exist whose DIS type is unknown.<br/>You have to solve this by manually mapping<br/> the appropriate DIS types to existing <b>unit types</b>."), tr("Import Error"));
|
|
vecObjectsWithValidDISTypes.append(showMappingDialog(mapUnitsWithUnknownDisType, true));
|
|
}
|
|
|
|
|
|
if (mapParticipantsWithUnknownDisType.size())
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("<b>Participants</b> exist whose DIS type is unknown.<br/>You have to solve this by manually mapping<br/> the appropriate DIS types to existing <b>participant types</b>."), tr("Import Error"));
|
|
vecObjectsWithValidDISTypes.append(showMappingDialog(mapParticipantsWithUnknownDisType, false));
|
|
}
|
|
|
|
return vecObjectsWithValidDISTypes;
|
|
}
|
|
|
|
|
|
QVector<MSDLParticipant> ImportMsdl::showMappingDialog(const QMap<QString, QVector<MSDLParticipant>> &mapConflictMap, bool typeClassIsUnit)
|
|
{
|
|
QVector<MSDLParticipant> vecUnitsWithValidSymbols;
|
|
|
|
// Let user solve conflicts
|
|
bool userWantsAutoSolve = false;
|
|
int32_t mappedTypeId = 0;
|
|
|
|
auto it = mapConflictMap.begin();
|
|
for (; it != mapConflictMap.end(); ++it)
|
|
{
|
|
const QString &strDisType = it.key();
|
|
const QVector<MSDLParticipant> &vecData = it.value();
|
|
|
|
QString strReason;
|
|
bool offerDatabaseEntriesWithoutDISType;
|
|
|
|
if (!strDisType.size())
|
|
{
|
|
strReason = QString(tr("No DIS type specified."));
|
|
// Imported unit/participant has no DIS type. Mapping "no DIS type" to database elements without a DIS type does not make sense
|
|
offerDatabaseEntriesWithoutDISType = false;
|
|
}
|
|
else
|
|
{
|
|
strReason = QString(tr("Specified DIS type \"%1\" is unknown.<br/>Affected %2:\nDesignator: %3\nTact. Identifier: %4")).arg(ParticipantManagement::DIS::convertMSDLString2DISString(strDisType), typeClassIsUnit ? QStringLiteral("unit") : QStringLiteral("participant"), vecData[0].m_designator, vecData[0].m_tacticalIdentifier);
|
|
offerDatabaseEntriesWithoutDISType = true;
|
|
}
|
|
|
|
QString strType = typeClassIsUnit ? tr("unit(s)") : tr("participant(s)");
|
|
strReason += tr("<br/>This applies for %1 %2. Please choose an option:").arg(QString::number(vecData.size()), strType);
|
|
|
|
msdl::MappingDialog dialog(this, typeClassIsUnit, strReason, m_ppmDtc, offerDatabaseEntriesWithoutDISType);
|
|
dialog.exec(); // TODO: use show(), use signals & slots !
|
|
|
|
if (dialog.skipAllUnmappedElements())
|
|
break;
|
|
|
|
mappedTypeId = dialog.getMappedTypeId();
|
|
|
|
if (dialog.autoMapAllUnmappedElements())
|
|
{
|
|
userWantsAutoSolve = true;
|
|
break;
|
|
}
|
|
|
|
if (dialog.mappingApplied())
|
|
{
|
|
for (auto participant : vecData)
|
|
{
|
|
mapParticipant2Type(participant, mappedTypeId);
|
|
vecUnitsWithValidSymbols.push_back(participant);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (userWantsAutoSolve)
|
|
{
|
|
// Use selected symbol for all remaining conflicts
|
|
for (; it != mapConflictMap.end(); ++it)
|
|
{
|
|
const QVector<MSDLParticipant> &vecData = it.value();
|
|
|
|
for (auto participant : vecData)
|
|
{
|
|
mapParticipant2Type(participant, mappedTypeId);
|
|
vecUnitsWithValidSymbols.push_back(participant);
|
|
}
|
|
}
|
|
}
|
|
|
|
return vecUnitsWithValidSymbols;
|
|
}
|
|
}
|