Files
qt5-legatus-legacy/Lts/Services/AudioVideoManagement/asset_assignment_manager.cpp
2026-08-13 18:33:10 +02:00

444 lines
13 KiB
C++
Executable File

/**
* @RDE_HEADER@
* InfrastructureBuilder
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-11-29
*/
#include "asset_assignment_manager.h"
#include "exercise.h"
#include "asset.h"
#include "mounting_point.h"
#include <InfrastructureManagement/ism.hpp>
#include <InfrastructureManagement/ism_dtc.h>
#include <QtGlobal>
#include <utility>
#include <functional>
namespace
{
bool isAssigned(const InfrastructureManagement::InstrumentationAsset &asset)
{
return asset.availableInExercise().size() > 0;
}
int assignedExercise(const InfrastructureManagement::InstrumentationAsset &asset)
{
Q_ASSERT(asset.availableInExercise().size() == 1);
return asset.availableInExercise()[0];
}
bool isMountingPoint(const InfrastructureManagement::StructureElement &structureElement)
{
return structureElement.structureElementTypeId() == InfrastructureManagement::StructureElementType::MountingPoint;
}
} // anonymous namespace
namespace AudioVideoManagement
{
AssetAssignmentManager *AssetAssignmentManager::create(InfrastructureManagement::Interface::Dtc *ismDtc)
{
Q_ASSERT(ismDtc);
AssetAssignmentManager *newAssetAssignmentManager = new AssetAssignmentManager;
connect(ismDtc, &InfrastructureManagement::Interface::Dtc::instrumentationAssetChanged, newAssetAssignmentManager, &AssetAssignmentManager::onAssetChanged);
connect(ismDtc, &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, newAssetAssignmentManager, &AssetAssignmentManager::onMainStructureElementChanged);
connect(ismDtc, &InfrastructureManagement::Interface::Dtc::mountingPointAssetChanged, newAssetAssignmentManager, &AssetAssignmentManager::onMountingChanged);
// No need to query the DTC here as it is not yet activated.
// But explicitly check that the DTC (i.e. the contained DataReader) is not enabled yet.
Q_ASSERT(!ismDtc->isInstrumentationAssetEnabled());
Q_ASSERT(!ismDtc->isMainStructureElementEnabled());
Q_ASSERT(!ismDtc->isMountingPointAssetEnabled());
return newAssetAssignmentManager;
}
AssetAssignmentManager::AssetAssignmentManager()
: m_logger(getLogger("AssetAssignmentManager"))
{
// Create a "pool" exercise to hold all elements that are not assigned to any exercise.
std::unique_ptr<Exercise> poolExercise(new Exercise(ExerciseContext::defaultContext()));
m_exercises[ExerciseContext::defaultContext()] = std::move(poolExercise);
}
AssetAssignmentManager::~AssetAssignmentManager() = default;
void AssetAssignmentManager::onAssetChanged(const InfrastructureManagement::InstrumentationAsset &asset, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
return addAsset(asset);
case DataModification::eModified:
return modifyAsset(asset);
case DataModification::eRemoved:
return removeAsset(asset);
}
Q_ASSERT(false);
}
void AssetAssignmentManager::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType)
{
// Is the removal of MainStructureElements a valid UseCase or does that only happen when stopping the services?
// For now, we simply ignore the removal.
if (modType == DataModification::eRemoved)
return;
if (modType == DataModification::eAdded)
{
for (const auto &structureElement: data.elements())
{
if (!isMountingPoint(structureElement))
continue;
std::unique_ptr<MountingPoint> newMountingPoint(new MountingPoint(structureElement));
pool()->add(std::move(newMountingPoint));
}
processPendingMounts();
return;
}
Q_ASSERT(modType == DataModification::eModified);
// What to do on modification?
// What does a modification mean?
// Are the contained StructureElements still the same as before?
// For now we simply assume that the MountingPoints are still the same
// and we just update them.
for (const auto &structureElement: data.elements())
{
if (!isMountingPoint(structureElement))
continue;
MountingPoint *mountingPoint = findMountingPoint(structureElement.id()).second;
Q_ASSERT(mountingPoint);
if (mountingPoint)
mountingPoint->update(structureElement);
}
}
void AssetAssignmentManager::onMountingChanged(const InfrastructureManagement::MountingPointAsset &mount, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
return addMount(mount);
case DataModification::eRemoved:
return removeMount(mount);
case DataModification::eModified:
Q_ASSERT(false);
break;
}
Q_ASSERT(false);
}
void AssetAssignmentManager::addAsset(const InfrastructureManagement::InstrumentationAsset &asset)
{
// Put the new Asset into the pool exercise.
std::unique_ptr<Asset> newAsset(new Asset(asset));
Asset *newAsset_ = newAsset.get();
pool()->add(std::move(newAsset));
processPendingMounts();
if (isAssigned(asset))
{
Exercise *targetExercise = exercise(assignedExercise(asset));
tryAssignAsset(newAsset_, pool(), targetExercise, boost::none);
}
}
/**
* The modification of an Asset possibly only affects the assignment.
*/
void AssetAssignmentManager::modifyAsset(const InfrastructureManagement::InstrumentationAsset &asset)
{
if (hasPendingModifications(asset.id()))
return m_pendingModifications.push_back(asset);
auto found = findAsset(asset.id());
Asset *foundAsset = found.second;
Q_ASSERT(foundAsset);
Exercise *currentExercise = found.first;
Exercise *targetExercise = isAssigned(asset) ? exercise(assignedExercise(asset)) : pool();
if (targetExercise == currentExercise)
{
foundAsset->update(asset);
return; // no change in assignment
}
if (targetExercise == pool())
{
// unassign
unassignAsset(foundAsset, currentExercise, asset);
processPendingUnmounts();
return;
}
// assign or reassign
tryAssignAsset(foundAsset, currentExercise, targetExercise, asset);
}
/**
* If the Asset is assigned, it will be unassigned.
* If it is not unmounted yet, defer the removal.
* The pending removals will be processed after the Unmount.
*/
void AssetAssignmentManager::removeAsset(const InfrastructureManagement::InstrumentationAsset &asset)
{
auto assetAndExercise = findAsset(asset.id());
Asset *foundAsset = assetAndExercise.second;
Exercise *exercise = assetAndExercise.first;
Q_ASSERT(foundAsset);
if (exercise != pool())
{
unassignAsset(foundAsset, exercise, boost::none);
processPendingUnmounts();
}
if (foundAsset->isMounted())
{
m_pendingRemoves.push_back(asset);
return;
}
pool()->take(foundAsset); // implicit delete
}
/**
* Try to assign the Asset to an Exercise.
* Assets can only be assigned if they are mounted.
* If the Asset is not mounted yet (because the info was not yet published),
* the assignment will be deferred. It will be processed after the mount.
* The Asset and MountingPoint must still be in the pool Exercise.
* @param modifiedData only set when called as a result of a modification
*/
void AssetAssignmentManager::tryAssignAsset(Asset *asset, Exercise *currentExercise, Exercise *targetExercise, const boost::optional<InfrastructureManagement::InstrumentationAsset> &modifiedData)
{
Q_ASSERT(asset);
Q_ASSERT(currentExercise);
Q_ASSERT(targetExercise);
Q_ASSERT(isAssigned(modifiedData.get_value_or(asset->data())));
if (!asset->isMounted())
{
m_pendingModifications.push_back(modifiedData.get_value_or(asset->data()));
return;
}
currentExercise->moveMountedTo(asset, targetExercise, modifiedData);
}
/**
* @param modifiedData only set when called as a result of a modification
*/
void AssetAssignmentManager::unassignAsset(Asset *asset, Exercise *exercise, const boost::optional<InfrastructureManagement::InstrumentationAsset> &modifiedData)
{
Q_ASSERT(asset);
Q_ASSERT(exercise != pool());
// Q_ASSERT(!isAssigned(asset->data())); // Assertion does not hold in case the unassignment is a result of a removal.
exercise->moveMountedTo(asset, pool(), modifiedData);
}
/**
* Try to mount the Asset. If the mounting was published before the referenced
* Asset or MountingPoint, the mounting will be deferred.
*/
void AssetAssignmentManager::addMount(const InfrastructureManagement::MountingPointAsset &mount)
{
Asset *asset = pool()->findAsset(mount.assetId());
if (!asset)
{
m_pendingMounts.push_back(mount);
return;
}
MountingPoint *mountingPoint = pool()->findMountingPoint(mount.structureElementId());
if (!mountingPoint)
{
m_pendingMounts.push_back(mount);
return;
}
asset->mount(mountingPoint);
processPendingModifications();
}
void AssetAssignmentManager::removeMount(const InfrastructureManagement::MountingPointAsset &mount)
{
auto exerciseAndAsset = findAsset(mount.assetId());
Asset *asset = exerciseAndAsset.second;
Exercise *exercise = exerciseAndAsset.first;
Q_ASSERT(asset);
if (!asset)
return;
tryUnmount(asset, exercise);
processPendingRemoves();
}
/**
* Try to unmount the Asset.
* Unmonting can only happen in the pool Exercise.
* If the Asset is not yet in the pool Exercise (because the updated Asset
* was not yet published), the unmount will be deferred.
* It will be processed after the Asset was unassigned.
*/
void AssetAssignmentManager::tryUnmount(Asset *asset, Exercise *exercise)
{
Q_ASSERT(asset);
bool inPoolExercise = false;
if (exercise)
inPoolExercise = exercise == pool();
else
inPoolExercise = pool()->findAsset(asset->data().id());
if (!inPoolExercise)
{
m_pendingUnmounts.push_back(asset);
return;
}
asset->unmount();
}
template<class Container, class Function>
void AssetAssignmentManager::processPending(Container &pendingSource, Function f)
{
if (pendingSource.empty())
return;
logPending();
Container pending;
std::swap(pending, pendingSource);
for (const auto &element: pending)
f(element);
logPending();
}
/**
* We want to make sure that assignments are always processed after the corresponding mount.
* On DDS these are not always published in that order.
* Thus we need to internally defer assignments that are published before the mount.
*/
void AssetAssignmentManager::processPendingModifications()
{
using namespace std::placeholders;
processPending(m_pendingModifications, std::bind(&AssetAssignmentManager::modifyAsset, this, _1));
}
/**
* Same as for processPendingAssigns() but this time we internally defer
* unmounts that are published before the unassign.
*/
void AssetAssignmentManager::processPendingUnmounts()
{
using namespace std::placeholders;
processPending(m_pendingUnmounts, std::bind(&AssetAssignmentManager::tryUnmount, this, _1, nullptr));
}
void AssetAssignmentManager::processPendingMounts()
{
using namespace std::placeholders;
processPending(m_pendingMounts, std::bind(&AssetAssignmentManager::addMount, this, _1));
}
void AssetAssignmentManager::processPendingRemoves()
{
using namespace std::placeholders;
processPending(m_pendingRemoves, std::bind(&AssetAssignmentManager::removeAsset, this, _1));
}
bool AssetAssignmentManager::hasPendingModifications(int assetId)
{
auto idMatches = [assetId] (const InfrastructureManagement::InstrumentationAsset &asset) { return asset.id() == assetId; };
auto found = std::find_if(m_pendingModifications.begin(), m_pendingModifications.end(), idMatches);
return found != m_pendingModifications.end();
}
void AssetAssignmentManager::logPending()
{
DEBUG_LOG(m_logger) << "pending Mounts, Modifications (Asset), Unmounts, Removes: "
<< m_pendingMounts.size() << ", "
<< m_pendingModifications.size() << ", "
<< m_pendingUnmounts.size() << ", "
<< m_pendingRemoves.size();
}
bool AssetAssignmentManager::hasPending() const
{
return !(m_pendingMounts.empty() && m_pendingModifications.empty() && m_pendingUnmounts.empty() && m_pendingRemoves.empty());
}
Exercise *AssetAssignmentManager::pool() const
{
auto it = m_exercises.find(ExerciseContext::defaultContext());
return (it == m_exercises.end() ? nullptr : it->second.get());
}
Exercise *AssetAssignmentManager::exercise(int id)
{
ExerciseContext context(id);
auto found = m_exercises.find(context);
if (found != m_exercises.end())
return found->second.get();
std::unique_ptr<Exercise> newExercise(new Exercise(context));
Exercise *newExercise_ = newExercise.get();
m_exercises[context] = std::move(newExercise);
return newExercise_;
}
void AssetAssignmentManager::removeExercise(int id)
{
ExerciseContext context(id);
auto found = m_exercises.find(context);
Q_ASSERT(found != m_exercises.end());
if (found != m_exercises.end())
m_exercises.erase(found); // implicit delete
}
std::pair<Exercise *, Asset *> AssetAssignmentManager::findAsset(int id)
{
for (const auto &exercise: m_exercises)
{
Asset *asset = exercise.second->findAsset(id);
if (asset)
return {exercise.second.get(), asset};
}
return {nullptr, nullptr};
}
std::pair<Exercise *, MountingPoint *> AssetAssignmentManager::findMountingPoint(int id)
{
for (const auto &exercise: m_exercises)
{
MountingPoint *mountingPoint = exercise.second->findMountingPoint(id);
if (mountingPoint)
return {exercise.second.get(), mountingPoint};
}
return {nullptr, nullptr};
}
} // namespace AudioVideoManagement