444 lines
13 KiB
C++
Executable File
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
|
|
|