Skip to content
Snippets Groups Projects
SkillManagerWrapper.cpp 12 KiB
Newer Older
#include "SkillManagerWrapper.h"
#include <mutex>

#include "RobotAPI/libraries/skills/core/SkillExecutionRequest.h"

Peter Albrecht's avatar
Peter Albrecht committed
namespace armarx::skills::gui
{
    using StatusMap = std::map<skills::SkillExecutionID, skills::SkillStatusUpdate>;
    using SkillMap =
        std::map<skills::ProviderID, std::map<skills::SkillID, skills::SkillDescription>>;
albrecpe's avatar
albrecpe committed
    StatusMap
    SkillManagerWrapper::fetchExecutionsFromMemory()
        if (!memory)
        {
            // check if null
            return {};
        }
        try
        {
            std::scoped_lock l(mutex_memory);

            // we return this map
            StatusMap statusMap;

            auto currentManagerStatuses = memory->ice_invocationTimeout(10000)->getSkillExecutionStatuses();

            // iterate over raw data and convert to common types
            for (const auto& [k, v] : currentManagerStatuses)
            {
                auto executionId = skills::SkillExecutionID::FromIce(k);
                auto statusUpdate = skills::SkillStatusUpdate::FromIce(v);

                // update maps
                statusMap[executionId] = statusUpdate;
            return statusMap;
        }
        catch (Ice::Exception const& e)
        {
            ARMARX_WARNING
                << "Unhandled Ice exception encountered while updating executions. Exception was: "
                << e;
            emit connectionUpdate("Could not fetch executions", e.what());
        }
        catch (...)
        {
            ARMARX_WARNING << "Unknown exception encountered while updating executions.";
    // check if search strings occur in the skill name in order
    bool
    matches(std::string skillName, std::vector<std::string>& searches)
    {
        size_t index = 0;
        for (std::string& substring : searches)
        {
            size_t occurance = skillName.find(substring, index);
            if (occurance == std::string::npos)


            // we found an occurance
            index = occurance;
        }
        return true;
    }

    SkillManagerWrapper::Snapshot
    SkillManagerWrapper::filterUpdate(Snapshot update)
    {
        // empty search => do not filter
        if (this->currentSkillSearch.empty())
        {
            return update;
        Snapshot filtered;

        // for now we don't change the executions
        filtered.statuses = update.statuses;

        std::vector<std::string> substrings;

            std::vector<std::string> rawSubstrings = simox::alg::split(currentSkillSearch);
            for (auto& string : rawSubstrings)
            {
                substrings.push_back(simox::alg::to_lower(string));
            }
        for (auto& provider_and_descrMap : update.skills)
            using DescriptionMap = std::map<skills::SkillID, skills::SkillDescription>;

            DescriptionMap& descriptionMap = provider_and_descrMap.second;
            skills::ProviderID provider = provider_and_descrMap.first;

            for (auto& skill_and_description : descriptionMap)
                skills::SkillID sid = skill_and_description.first;
                skills::SkillDescription descr = skill_and_description.second;

                if (matches(simox::alg::to_lower(sid.skillName), substrings))
                {
                    // add to map
                    filtered.skills[provider][sid] = descr;
                }

        return filtered;
    SkillMap
    SkillManagerWrapper::fetchSkillsFromMemory()
        }

        try
        {
            std::scoped_lock l(mutex_memory);

            auto managerSkills = memory->ice_invocationTimeout(5000)->getSkillDescriptions();

            for (const auto& [sid, desc] : managerSkills)
            {
                auto description = skills::SkillDescription::FromIce(desc);
                auto skillId = skills::SkillID::FromIce(sid);
                auto providerId = skillId.providerId.value_or(
                    skills::ProviderID{.providerName = "UNKNOWN PROVIDER NAME"});

                ARMARX_CHECK(skillId.isFullySpecified());

                auto& providedSkillsMap = skills[providerId]; // create new if not existing
                providedSkillsMap.insert({skillId, description});
            }
        }
        catch (Ice::Exception const& e)
        {
            ARMARX_WARNING
                << "Unhandled Ice exception encountered while updating skills. Exception was: "
                << e;
            emit connectionUpdate("Could not fetch skills", e.what());
        }
        catch (...)
        {
            ARMARX_WARNING << "Unknown exception encountered while updating skills.";
    SkillManagerWrapper::connectMemory(
        skills::manager::dti::SkillManagerInterfacePrx const& updatedMemory)
    {
        std::scoped_lock l(mutex_memory);
        this->memory = updatedMemory;
    }

    void
    SkillManagerWrapper::disconnectMemory()
        std::scoped_lock l(mutex_memory);
        this->memory = nullptr;
    SkillManagerWrapper::acceptSearchRequest(std::string const& search)
    {
        this->currentSkillSearch = search;
        emit searchAccepted();
    void
    SkillManagerWrapper::stopAllExecutions()
    {
        ARMARX_IMPORTANT << "Stopping all running executions.";

        StatusMap executions;

        // we ALWAYS want the newest information when stopping all!
        // e.g. there is some new skill not known to the GUI which we explicitely want to stop too.
        // the stop-all function is often used in an emergency, so we'll live with the extra call...
        try
        {
            executions = this->fetchExecutionsFromMemory();
        }
        catch (...) // if any error occurs, we use the snapshot as backup. better to miss a skill
            // than to not do anything.
        {
            executions = this->getExecutions();
        }

        for (auto& [executionId, status] : executions)
        {
            // select all running executions...
            if (!status.hasBeenTerminated())
            {
                // ... and kill them.
                this->stopExecution(executionId, 3);
            }
        }
    }

    void
    SkillManagerWrapper::updateFromMemory()
        std::scoped_lock l(mutex_snapshot);

        snapshot.skills = fetchSkillsFromMemory();
        snapshot.statuses = fetchExecutionsFromMemory();

        // notify registered widgets of update
        emit updateAvailable(filterUpdate(snapshot));
    const std::optional<ProviderID>
    SkillManagerWrapper::findFirstProvider(SkillMap const& map, SkillID const& skillId)
    {
        // check if id already contains a provider. If so, this function should not have been called!
        if (skillId.isProviderSpecified())
        {
            ARMARX_WARNING << "The memory snapshot was searched for any provider, when a provider "
                              "was specified.";
            // we continue, but this might result in unexpected behaviour...
        }

        for (auto& [prov, skillMap] : map)
        {
            for (auto& [skill, desc] : skillMap)
            {
                if (skill == skillId)
                {
                    return prov;
                }
            }
        }
        return std::nullopt;
    }

    SkillMap
    SkillManagerWrapper::getSkills()
    {
        std::scoped_lock l(mutex_snapshot);

        Snapshot filtered = filterUpdate(snapshot);

        return filtered.skills;
    }

    StatusMap
    SkillManagerWrapper::getExecutions()
    {
        std::scoped_lock l(mutex_snapshot);
        return snapshot.statuses;
    }

    SkillManagerWrapper::stopExecution(skills::SkillExecutionID const& executionId,
                                       const unsigned int max_retries)
    {
        // memory???
        if (!memory)
        {
            return;
        }

        unsigned int retries = max_retries;
        bool retry = false;

        do
        {
            try
            {
                ARMARX_INFO << "Aborting skill '" << executionId.skillId.skillName << "'...";
                this->memory->ice_invocationTimeout(5000)->abortSkillAsync(executionId.toManagerIce());
            }
            catch (Ice::Exception const& e)
            {
                retry = true;
                ARMARX_ERROR << "Unhandeled Ice exception while aborting skill '"
                             << executionId.skillId.skillName << "'.";
                emit connectionUpdate("Could not abort skill " + executionId.skillId.skillName,
                                      e.what());
            }
            catch (...)
            {
                retry = true;
                ARMARX_ERROR << "Unhandled error while aborting skill '"
                             << executionId.skillId.skillName << "'.";
            }

            if (retry)
            {
                retries -= 1;

                if (retries > 0)
                {
                    ARMARX_WARNING << "There where errors aborting skills.  Retrying...";
                }
                else
                {
                    ARMARX_ERROR << "Couldn't abort all skills after " << max_retries
                                 << " tries.  Giving up.";
                    retry = false;
                }
            }
        } while (retry);
    }

    void
    SkillManagerWrapper::startExecutionWithParams(skills::SkillID& skillId,
                                                  aron::data::DictPtr const params)
    {
        // Memory???
        if (!memory)
        {
            return;
        }

        auto providerId = skillId.providerId;
        if (!providerId.has_value())
        {
            ARMARX_IMPORTANT << "The skill: '" << skillId.skillName
                             << "' has been requested to be executed, but no provider was "
                                "given. Aborting...";
        std::map<skills::SkillID, skills::SkillDescription> skillDescriptions;
        if (this->UPDATE_ON_EXECUTION_REQUEST)
        {
            skillDescriptions = this->fetchSkillsFromMemory().at(providerId.value());
        }
        else
            skillDescriptions = this->getSkills().at(providerId.value());
        if (skillDescriptions.find(skillId) == skillDescriptions.end())
        {
            ARMARX_IMPORTANT << "The Skill: '" << skillId.skillName
                             << "' has been requested to be executed, but no skill description was "
                                "found. Aborting...";
            return;
        }

        char hostname[HOST_NAME_MAX];
        gethostname(hostname, HOST_NAME_MAX);

        skills::SkillExecutionRequest req{
            .skillId = skillId,
            .executorName = "Skills.Manager GUI (hostname: " + std::string(hostname) + ")",
            .parameters = params};

        ARMARX_CHECK(skillId.isFullySpecified()); // sanity check
        ARMARX_IMPORTANT << "Executing skill from GUI: " << skillId << ".";

        try
        {
            std::scoped_lock l(mutex_memory);
            memory->ice_invocationTimeout(5000)->executeSkillAsync(req.toManagerIce());
        }
        catch (Ice::Exception const& e)
        {
            ARMARX_ERROR << "Unhandeled Ice exception while executing skill '" << skillId.skillName
                         << "'. Aborting...";
            emit connectionUpdate("Execution failed of skill " + skillId.skillName, e.what());
        }
        catch (...)
        {
            ARMARX_ERROR << "Unhandled error while executing skill '" << skillId.skillName
                         << "'. Aborting...";
        }
    }


Peter Albrecht's avatar
Peter Albrecht committed
} // namespace armarx::skills::gui