<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2019                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <modules/base/rendering/renderablesphere.h>

#include <modules/base/basemodule.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/powerscaledsphere.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/opengl/programobject.h>

namespace {
    constexpr const char* ProgramName = "Sphere";

    constexpr const std::array<const char*, 5> UniformNames = {
        "opacity", "modelViewProjection", "modelViewRotation", "colorTexture",
        "mirrorTexture"
    };

    enum class Orientation : int {
        Outside = 0,
        Inside,
        Both
    };

    constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
        "Texture",
        "Texture",
        "This value specifies an image that is loaded from disk and is used as a texture "
        "that is applied to this sphere. This image is expected to be an equirectangular "
        "projection."
    };

    constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = {
        "MirrorTexture",
        "Mirror Texture",
        "Mirror the texture along the x-axis."
    };

    constexpr openspace::properties::Property::PropertyInfo OrientationInfo = {
        "Orientation",
        "Orientation",
        "Specifies whether the texture is applied to the inside of the sphere, the "
        "outside of the sphere, or both."
    };

    constexpr openspace::properties::Property::PropertyInfo UseAdditiveBlendingInfo = {
        "UseAdditiveBlending",
        "Use Additive Blending",
        "Render the object using additive blending."
    };

    constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
        "Segments",
        "Number of Segments",
        "This value specifies the number of segments that the sphere is separated in."
    };

    constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
        "Size",
        "Size (in meters)",
        "This value specifies the radius of the sphere in meters."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = {
        "FadeOutThreshold",
        "Fade-Out Threshold",
        "This value determines percentage of the sphere is visible before starting "
        "fading-out it."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = {
        "FadeInThreshold",
        "Fade-In Threshold",
        "Distance from center of MilkyWay from where the astronomical object starts to "
        "fade in."
    };

    constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = {
        "DisableFadeInOut",
        "Disable Fade-In/Fade-Out effects",
        "Enables/Disables the Fade-In/Out effects."
    };

    constexpr openspace::properties::Property::PropertyInfo BackgroundInfo = {
        "Background",
        "Sets the current sphere rendering as a background rendering type",
        "Enables/Disables background rendering."
    };
} // namespace

namespace openspace {

documentation::Documentation RenderableSphere::Documentation() {
    using namespace documentation;
    return {
        "RenderableSphere",
        "base_renderable_sphere",
        {
            {
                SizeInfo.identifier,
                new DoubleVerifier,
                Optional::No,
                SizeInfo.description
            },
            {
                SegmentsInfo.identifier,
                new IntVerifier,
                Optional::No,
                SegmentsInfo.description
            },
            {
                TextureInfo.identifier,
                new StringVerifier,
                Optional::No,
                TextureInfo.description
            },
            {
                OrientationInfo.identifier,
                new StringInListVerifier({ "Inside", "Outside", "Both" }),
                Optional::Yes,
                OrientationInfo.description
            },
            {
                UseAdditiveBlendingInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                UseAdditiveBlendingInfo.description
            },
            {
                MirrorTextureInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                MirrorTextureInfo.description
            },
            {
                FadeOutThresholdInfo.identifier,
                new DoubleInRangeVerifier(0.0, 1.0),
                Optional::Yes,
                FadeOutThresholdInfo.description
            },
            {
                FadeInThresholdInfo.identifier,
                new DoubleVerifier,
                Optional::Yes,
                FadeInThresholdInfo.description
            },
            {
                DisableFadeInOutInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                DisableFadeInOutInfo.description
            },
            {
                BackgroundInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                BackgroundInfo.description
            },
        }
    };
}

RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _texturePath(TextureInfo)
    , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown)
    , _size(SizeInfo, 1.f, 0.f, 1e35f)
    , _segments(SegmentsInfo, 8, 4, 1000)
    , _mirrorTexture(MirrorTextureInfo, false)
    , _useAdditiveBlending(UseAdditiveBlendingInfo, false)
    , _disableFadeInDistance(DisableFadeInOutInfo, true)
    , _backgroundRendering(BackgroundInfo, false)
    , _fadeInThreshold(FadeInThresholdInfo, -1.f, 0.f, 1.f)
    , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, 0.f, 1.f)
{
    documentation::testSpecificationAndThrow(
        Documentation(),
        dictionary,
        "RenderableSphere"
    );

    addProperty(_opacity);
    registerUpdateRenderBinFromOpacity();

    _size = static_cast<float>(dictionary.value<double>(SizeInfo.identifier));
    _segments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
    _texturePath = absPath(dictionary.value<std::string>(TextureInfo.identifier));

    _orientation.addOptions({
        { static_cast<int>(Orientation::Outside), "Outside" },
        { static_cast<int>(Orientation::Inside), "Inside" },
        { static_cast<int>(Orientation::Both), "Both" }
    });

    if (dictionary.hasKey(OrientationInfo.identifier)) {
        const std::string& v = dictionary.value<std::string>(OrientationInfo.identifier);
        if (v == "Inside") {
            _orientation = static_cast<int>(Orientation::Inside);
        }
        else if (v == "Outside") {
            _orientation = static_cast<int>(Orientation::Outside);
        }
        else if (v == "Both") {
            _orientation = static_cast<int>(Orientation::Both);
        }
        else {
            throw ghoul::MissingCaseException();
        }
    }
    else {
        _orientation = static_cast<int>(Orientation::Outside);
    }
    addProperty(_orientation);

    addProperty(_size);
    _size.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_segments);
    _segments.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_texturePath);
    _texturePath.onChange([this]() { loadTexture(); });

    addProperty(_mirrorTexture);
    addProperty(_useAdditiveBlending);

    if (dictionary.hasKey(MirrorTextureInfo.identifier)) {
       _mirrorTexture = dictionary.value<bool>(MirrorTextureInfo.identifier);
    }
    if (dictionary.hasKey(UseAdditiveBlendingInfo.identifier)) {
        _useAdditiveBlending = dictionary.value<bool>(UseAdditiveBlendingInfo.identifier);

        if (_useAdditiveBlending) {
            setRenderBin(Renderable::RenderBin::Transparent);
        }
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier)) {
        _fadeOutThreshold = static_cast<float>(
            dictionary.value<double>(FadeOutThresholdInfo.identifier)
        );
        addProperty(_fadeOutThreshold);
    }

    if (dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _fadeInThreshold = static_cast<float>(
            dictionary.value<double>(FadeInThresholdInfo.identifier)
        );
        addProperty(_fadeInThreshold);
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier) ||
        dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _disableFadeInDistance.set(false);
        addProperty(_disableFadeInDistance);
    }

    if (dictionary.hasKey(BackgroundInfo.identifier)) {
        _backgroundRendering = dictionary.value<bool>(BackgroundInfo.identifier);

        if (_backgroundRendering) {
            setRenderBin(Renderable::RenderBin::Background);
        }
    }
}

bool RenderableSphere::isReady() const {
    return _shader && _texture;
}

void RenderableSphere::initializeGL() {
    _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
    _sphere->initialize();

    _shader = BaseModule::ProgramObjectManager.request(
        ProgramName,
        []() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
            return global::renderEngine.buildRenderProgram(
                ProgramName,
                absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"),
                absPath("${MODULE_BASE}/shaders/sphere_fs.glsl")
            );
        }
    );

    ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);

    loadTexture();
}

void RenderableSphere::deinitializeGL() {
    _texture = nullptr;

    BaseModule::ProgramObjectManager.release(
        ProgramName,
        [](ghoul::opengl::ProgramObject* p) {
            global::renderEngine.removeRenderProgram(p);
        }
    );
    _shader = nullptr;
}

void RenderableSphere::render(const RenderData& data, RendererTasks&) {
    Orientation orientation = static_cast<Orientation>(_orientation.value());

    glm::dmat4 modelTransform =
        glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
        glm::dmat4(data.modelTransform.rotation) *
        glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));

    glm::dmat3 modelRotation =
        glm::dmat3(data.modelTransform.rotation);

    // Activate shader
    using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
    _shader->activate();
    _shader->setIgnoreUniformLocationError(IgnoreError::Yes);

    glm::mat4 modelViewProjection = data.camera.projectionMatrix() *
                             glm::mat4(data.camera.combinedViewMatrix() * modelTransform);
    _shader->setUniform(_uniformCache.modelViewProjection, modelViewProjection);

    glm::mat3 modelViewRotation = glm::mat3(
        glm::dmat3(data.camera.viewRotationMatrix()) * modelRotation
    );
    _shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation);

    float adjustedTransparency = _opacity;

    if (_fadeInThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeInThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= fadeFactor;
        }
        else if (logDistCamera <= startLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    if (_fadeOutThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeOutThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= (1.f - fadeFactor);
        }
        else if (logDistCamera >= stopLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    // Performance wise
    if (adjustedTransparency < 0.01f) {
        return;
    }

    _shader->setUniform(_uniformCache.opacity, adjustedTransparency);
    _shader->setUniform(_uniformCache._mirrorTexture, _mirrorTexture.value());

    ghoul::opengl::TextureUnit unit;
    unit.activate();
    _texture->bind();
    _shader->setUniform(_uniformCache.colorTexture, unit);

    // Setting these states should not be necessary,
    // since they are the default state in OpenSpace.
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    if (orientation == Orientation::Inside) {
        glCullFace(GL_FRONT);
    } else if (orientation == Orientation::Both) {
        glDisable(GL_CULL_FACE);
    }

    bool usingFramebufferRenderer = global::renderEngine.rendererImplementation() ==
                                    RenderEngine::RendererImplementation::Framebuffer;

    bool usingABufferRenderer = global::renderEngine.rendererImplementation() ==
                                RenderEngine::RendererImplementation::ABuffer;

    if (usingABufferRenderer && _useAdditiveBlending) {
        _shader->setUniform("additiveBlending", true);
    }

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glDepthMask(false);
    }

    _sphere->render();

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDepthMask(true);
    }

    _shader->setIgnoreUniformLocationError(IgnoreError::No);
    _shader->deactivate();

    if (orientation == Orientation::Inside) {
        glCullFace(GL_BACK);
    } else if (orientation == Orientation::Both) {
        glEnable(GL_CULL_FACE);
    }
}

void RenderableSphere::update(const UpdateData&) {
    if (_shader->isDirty()) {
        _shader->rebuildFromFile();
        ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
    }

    if (_sphereIsDirty) {
        _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
        _sphere->initialize();
        _sphereIsDirty = false;
    }
}

void RenderableSphere::loadTexture() {
    if (!_texturePath.value().empty()) {
        std::unique_ptr<ghoul::opengl::Texture> texture =
            ghoul::io::TextureReader::ref().loadTexture(_texturePath);

        if (texture) {
            LDEBUGC(
                "RenderableSphere",
                fmt::format("Loaded texture from '{}'", absPath(_texturePath))
            );
            texture->uploadTexture();
            texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
            _texture = std::move(texture);
        }
    }
}

} // namespace openspace
<commit_msg>Correctly set render bin for RenderableSphere to prevent flickering of atmosphere<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2019                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <modules/base/rendering/renderablesphere.h>

#include <modules/base/basemodule.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/powerscaledsphere.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/opengl/programobject.h>

namespace {
    constexpr const char* ProgramName = "Sphere";

    constexpr const std::array<const char*, 5> UniformNames = {
        "opacity", "modelViewProjection", "modelViewRotation", "colorTexture",
        "mirrorTexture"
    };

    enum class Orientation : int {
        Outside = 0,
        Inside,
        Both
    };

    constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
        "Texture",
        "Texture",
        "This value specifies an image that is loaded from disk and is used as a texture "
        "that is applied to this sphere. This image is expected to be an equirectangular "
        "projection."
    };

    constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = {
        "MirrorTexture",
        "Mirror Texture",
        "Mirror the texture along the x-axis."
    };

    constexpr openspace::properties::Property::PropertyInfo OrientationInfo = {
        "Orientation",
        "Orientation",
        "Specifies whether the texture is applied to the inside of the sphere, the "
        "outside of the sphere, or both."
    };

    constexpr openspace::properties::Property::PropertyInfo UseAdditiveBlendingInfo = {
        "UseAdditiveBlending",
        "Use Additive Blending",
        "Render the object using additive blending."
    };

    constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
        "Segments",
        "Number of Segments",
        "This value specifies the number of segments that the sphere is separated in."
    };

    constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
        "Size",
        "Size (in meters)",
        "This value specifies the radius of the sphere in meters."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = {
        "FadeOutThreshold",
        "Fade-Out Threshold",
        "This value determines percentage of the sphere is visible before starting "
        "fading-out it."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = {
        "FadeInThreshold",
        "Fade-In Threshold",
        "Distance from center of MilkyWay from where the astronomical object starts to "
        "fade in."
    };

    constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = {
        "DisableFadeInOut",
        "Disable Fade-In/Fade-Out effects",
        "Enables/Disables the Fade-In/Out effects."
    };

    constexpr openspace::properties::Property::PropertyInfo BackgroundInfo = {
        "Background",
        "Sets the current sphere rendering as a background rendering type",
        "Enables/Disables background rendering."
    };
} // namespace

namespace openspace {

documentation::Documentation RenderableSphere::Documentation() {
    using namespace documentation;
    return {
        "RenderableSphere",
        "base_renderable_sphere",
        {
            {
                SizeInfo.identifier,
                new DoubleVerifier,
                Optional::No,
                SizeInfo.description
            },
            {
                SegmentsInfo.identifier,
                new IntVerifier,
                Optional::No,
                SegmentsInfo.description
            },
            {
                TextureInfo.identifier,
                new StringVerifier,
                Optional::No,
                TextureInfo.description
            },
            {
                OrientationInfo.identifier,
                new StringInListVerifier({ "Inside", "Outside", "Both" }),
                Optional::Yes,
                OrientationInfo.description
            },
            {
                UseAdditiveBlendingInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                UseAdditiveBlendingInfo.description
            },
            {
                MirrorTextureInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                MirrorTextureInfo.description
            },
            {
                FadeOutThresholdInfo.identifier,
                new DoubleInRangeVerifier(0.0, 1.0),
                Optional::Yes,
                FadeOutThresholdInfo.description
            },
            {
                FadeInThresholdInfo.identifier,
                new DoubleVerifier,
                Optional::Yes,
                FadeInThresholdInfo.description
            },
            {
                DisableFadeInOutInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                DisableFadeInOutInfo.description
            },
            {
                BackgroundInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                BackgroundInfo.description
            },
        }
    };
}

RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _texturePath(TextureInfo)
    , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown)
    , _size(SizeInfo, 1.f, 0.f, 1e35f)
    , _segments(SegmentsInfo, 8, 4, 1000)
    , _mirrorTexture(MirrorTextureInfo, false)
    , _useAdditiveBlending(UseAdditiveBlendingInfo, false)
    , _disableFadeInDistance(DisableFadeInOutInfo, true)
    , _backgroundRendering(BackgroundInfo, false)
    , _fadeInThreshold(FadeInThresholdInfo, -1.f, 0.f, 1.f)
    , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, 0.f, 1.f)
{
    documentation::testSpecificationAndThrow(
        Documentation(),
        dictionary,
        "RenderableSphere"
    );

    addProperty(_opacity);
    registerUpdateRenderBinFromOpacity();

    _size = static_cast<float>(dictionary.value<double>(SizeInfo.identifier));
    _segments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
    _texturePath = absPath(dictionary.value<std::string>(TextureInfo.identifier));

    _orientation.addOptions({
        { static_cast<int>(Orientation::Outside), "Outside" },
        { static_cast<int>(Orientation::Inside), "Inside" },
        { static_cast<int>(Orientation::Both), "Both" }
    });

    if (dictionary.hasKey(OrientationInfo.identifier)) {
        const std::string& v = dictionary.value<std::string>(OrientationInfo.identifier);
        if (v == "Inside") {
            _orientation = static_cast<int>(Orientation::Inside);
        }
        else if (v == "Outside") {
            _orientation = static_cast<int>(Orientation::Outside);
        }
        else if (v == "Both") {
            _orientation = static_cast<int>(Orientation::Both);
        }
        else {
            throw ghoul::MissingCaseException();
        }
    }
    else {
        _orientation = static_cast<int>(Orientation::Outside);
    }
    addProperty(_orientation);

    addProperty(_size);
    _size.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_segments);
    _segments.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_texturePath);
    _texturePath.onChange([this]() { loadTexture(); });

    addProperty(_mirrorTexture);
    addProperty(_useAdditiveBlending);

    if (dictionary.hasKey(MirrorTextureInfo.identifier)) {
       _mirrorTexture = dictionary.value<bool>(MirrorTextureInfo.identifier);
    }
    if (dictionary.hasKey(UseAdditiveBlendingInfo.identifier)) {
        _useAdditiveBlending = dictionary.value<bool>(UseAdditiveBlendingInfo.identifier);

        if (_useAdditiveBlending) {
            setRenderBin(Renderable::RenderBin::Transparent);
        }
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier)) {
        _fadeOutThreshold = static_cast<float>(
            dictionary.value<double>(FadeOutThresholdInfo.identifier)
        );
        addProperty(_fadeOutThreshold);
    }

    if (dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _fadeInThreshold = static_cast<float>(
            dictionary.value<double>(FadeInThresholdInfo.identifier)
        );
        addProperty(_fadeInThreshold);
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier) ||
        dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _disableFadeInDistance.set(false);
        addProperty(_disableFadeInDistance);
    }

    if (dictionary.hasKey(BackgroundInfo.identifier)) {
        _backgroundRendering = dictionary.value<bool>(BackgroundInfo.identifier);

        if (_backgroundRendering) {
            setRenderBin(Renderable::RenderBin::Background);
        }
    }

    setRenderBinFromOpacity();
}

bool RenderableSphere::isReady() const {
    return _shader && _texture;
}

void RenderableSphere::initializeGL() {
    _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
    _sphere->initialize();

    _shader = BaseModule::ProgramObjectManager.request(
        ProgramName,
        []() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
            return global::renderEngine.buildRenderProgram(
                ProgramName,
                absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"),
                absPath("${MODULE_BASE}/shaders/sphere_fs.glsl")
            );
        }
    );

    ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);

    loadTexture();
}

void RenderableSphere::deinitializeGL() {
    _texture = nullptr;

    BaseModule::ProgramObjectManager.release(
        ProgramName,
        [](ghoul::opengl::ProgramObject* p) {
            global::renderEngine.removeRenderProgram(p);
        }
    );
    _shader = nullptr;
}

void RenderableSphere::render(const RenderData& data, RendererTasks&) {
    Orientation orientation = static_cast<Orientation>(_orientation.value());

    glm::dmat4 modelTransform =
        glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
        glm::dmat4(data.modelTransform.rotation) *
        glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));

    glm::dmat3 modelRotation =
        glm::dmat3(data.modelTransform.rotation);

    // Activate shader
    using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
    _shader->activate();
    _shader->setIgnoreUniformLocationError(IgnoreError::Yes);

    glm::mat4 modelViewProjection = data.camera.projectionMatrix() *
                             glm::mat4(data.camera.combinedViewMatrix() * modelTransform);
    _shader->setUniform(_uniformCache.modelViewProjection, modelViewProjection);

    glm::mat3 modelViewRotation = glm::mat3(
        glm::dmat3(data.camera.viewRotationMatrix()) * modelRotation
    );
    _shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation);

    float adjustedTransparency = _opacity;

    if (_fadeInThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeInThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= fadeFactor;
        }
        else if (logDistCamera <= startLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    if (_fadeOutThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeOutThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= (1.f - fadeFactor);
        }
        else if (logDistCamera >= stopLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    // Performance wise
    if (adjustedTransparency < 0.01f) {
        return;
    }

    _shader->setUniform(_uniformCache.opacity, adjustedTransparency);
    _shader->setUniform(_uniformCache._mirrorTexture, _mirrorTexture.value());

    ghoul::opengl::TextureUnit unit;
    unit.activate();
    _texture->bind();
    _shader->setUniform(_uniformCache.colorTexture, unit);

    // Setting these states should not be necessary,
    // since they are the default state in OpenSpace.
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    if (orientation == Orientation::Inside) {
        glCullFace(GL_FRONT);
    } else if (orientation == Orientation::Both) {
        glDisable(GL_CULL_FACE);
    }

    bool usingFramebufferRenderer = global::renderEngine.rendererImplementation() ==
                                    RenderEngine::RendererImplementation::Framebuffer;

    bool usingABufferRenderer = global::renderEngine.rendererImplementation() ==
                                RenderEngine::RendererImplementation::ABuffer;

    if (usingABufferRenderer && _useAdditiveBlending) {
        _shader->setUniform("additiveBlending", true);
    }

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glDepthMask(false);
    }

    _sphere->render();

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDepthMask(true);
    }

    _shader->setIgnoreUniformLocationError(IgnoreError::No);
    _shader->deactivate();

    if (orientation == Orientation::Inside) {
        glCullFace(GL_BACK);
    } else if (orientation == Orientation::Both) {
        glEnable(GL_CULL_FACE);
    }
}

void RenderableSphere::update(const UpdateData&) {
    if (_shader->isDirty()) {
        _shader->rebuildFromFile();
        ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
    }

    if (_sphereIsDirty) {
        _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
        _sphere->initialize();
        _sphereIsDirty = false;
    }
}

void RenderableSphere::loadTexture() {
    if (!_texturePath.value().empty()) {
        std::unique_ptr<ghoul::opengl::Texture> texture =
            ghoul::io::TextureReader::ref().loadTexture(_texturePath);

        if (texture) {
            LDEBUGC(
                "RenderableSphere",
                fmt::format("Loaded texture from '{}'", absPath(_texturePath))
            );
            texture->uploadTexture();
            texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
            _texture = std::move(texture);
        }
    }
}

} // namespace openspace
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/dreamview/backend/hmi/hmi_worker.h"

#include <vector>

#include "gflags/gflags.h"
#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/kv_db/kv_db.h"
#include "modules/common/util/file.h"
#include "modules/common/util/map_util.h"
#include "modules/common/util/string_tokenizer.h"
#include "modules/common/util/string_util.h"
#include "modules/control/proto/pad_msg.pb.h"
#include "modules/data/util/info_collector.h"
#include "modules/dreamview/backend/common/dreamview_gflags.h"
#include "modules/dreamview/backend/hmi/vehicle_manager.h"

DEFINE_string(map_data_path, "/apollo/modules/map/data", "Path to map data.");

DEFINE_string(vehicle_data_path, "/apollo/modules/calibration/data",
              "Path to vehicle data.");

DEFINE_string(global_flagfile,
              "/apollo/modules/common/data/global_flagfile.txt",
              "Global flagfile shared by all modules.");

namespace apollo {
namespace dreamview {
namespace {

using apollo::canbus::Chassis;
using apollo::common::adapter::AdapterManager;
using apollo::common::util::ContainsKey;
using apollo::common::util::FindOrNull;
using apollo::common::util::StringTokenizer;
using apollo::control::DrivingAction;
using google::protobuf::Map;

// Convert a string to be title-like. E.g.: "hello_world" -> "Hello World".
std::string TitleCase(const std::string &origin,
                      const std::string &delimiter = "_") {
  std::vector<std::string> parts = StringTokenizer::Split(origin, delimiter);
  for (auto &part : parts) {
    if (!part.empty()) {
      // Upper case the first char.
      part[0] = toupper(part[0]);
    }
  }

  return apollo::common::util::PrintIter(parts);
}

// List subdirs and return a dict of {subdir_title: subdir_path}.
Map<std::string, std::string> ListDirAsDict(const std::string &dir) {
  Map<std::string, std::string> result;
  const auto subdirs = apollo::common::util::ListSubDirectories(dir);
  for (const auto &subdir : subdirs) {
    const auto subdir_title = TitleCase(subdir);
    const auto subdir_path = apollo::common::util::StrCat(dir, "/", subdir);
    result.insert({subdir_title, subdir_path});
  }
  return result;
}

// Run a supported command of some component, return the ret code.
int RunComponentCommand(
    const google::protobuf::Map<std::string, Component> &components,
    const std::string &component_name, const std::string &command_name) {
  const auto *component = FindOrNull(components, component_name);
  if (component == nullptr) {
    AERROR << "Cannot find component " << component_name;
    return -1;
  }
  const auto *cmd = FindOrNull(component->supported_commands(), command_name);
  if (cmd == nullptr) {
    AERROR << "Cannot find command " << component_name << "." << command_name;
    return -1;
  }
  AINFO << "Execute system command: " << *cmd;
  const int ret = std::system(cmd->c_str());

  AERROR_IF(ret != 0) << "Command returns " << ret << ": " << *cmd;
  return ret;
}

}  // namespace

HMIWorker::HMIWorker() {
  // Init HMIConfig.
  CHECK(common::util::GetProtoFromFile(FLAGS_hmi_config_filename, &config_))
      << "Unable to parse HMI config file " << FLAGS_hmi_config_filename;
  config_.set_docker_image(apollo::data::InfoCollector::GetDockerImage());

  // If the module path doesn't exist, remove it from list.
  auto *modules = config_.mutable_modules();
  for (auto iter = modules->begin(); iter != modules->end();) {
    const auto &conf = iter->second;
    if (conf.has_path() && !common::util::PathExists(conf.path())) {
      iter = modules->erase(iter);
    } else {
      ++iter;
    }
  }

  // Get available maps and vehicles by listing data directory.
  *config_.mutable_available_maps() = ListDirAsDict(FLAGS_map_data_path);
  *config_.mutable_available_vehicles() =
      ListDirAsDict(FLAGS_vehicle_data_path);
  ADEBUG << "Loaded HMI config: " << config_.DebugString();

  // Init HMIStatus.
  // If the default mode is unavailable, select the first one.
  const auto &modes = config_.modes();
  if (!ContainsKey(modes, status_.current_mode())) {
    CHECK(!modes.empty());
    status_.set_current_mode(modes.begin()->first);
  }
}

bool HMIWorker::Trigger(const HMIAction action) {
  AINFO << "HMIAction " << HMIAction_Name(action) << " was triggered!";
  // TODO(xiaoxq): Do the action.
  return false;
}

int HMIWorker::RunModuleCommand(const std::string &module,
                                const std::string &command) {
  return RunComponentCommand(config_.modules(), module, command);
}

int HMIWorker::RunHardwareCommand(const std::string &hardware,
                                  const std::string &command) {
  return RunComponentCommand(config_.hardware(), hardware, command);
}

int HMIWorker::RunToolCommand(const std::string &tool,
                              const std::string &command) {
  return RunComponentCommand(config_.tools(), tool, command);
}

void HMIWorker::SubmitDriveEvent(const uint64_t event_time_ms,
                                 const std::string &event_msg) {
  apollo::common::DriveEvent drive_event;
  AdapterManager::FillDriveEventHeader("HMI", &drive_event);
  drive_event.mutable_header()->set_timestamp_sec(event_time_ms / 1000.0);
  drive_event.set_event(event_msg);
  AdapterManager::PublishDriveEvent(drive_event);
}

bool HMIWorker::ChangeToDrivingMode(const Chassis::DrivingMode mode) {
  // Always reset to MANUAL mode before changing to other mode.
  if (mode != Chassis::COMPLETE_MANUAL) {
    if (!ChangeToDrivingMode(Chassis::COMPLETE_MANUAL)) {
      AERROR << "Failed to reset to MANUAL mode before changing to "
             << Chassis::DrivingMode_Name(mode);
      return false;
    }
  }

  control::PadMessage pad;
  switch (mode) {
    case Chassis::COMPLETE_MANUAL:
      pad.set_action(DrivingAction::RESET);
      break;
    case Chassis::COMPLETE_AUTO_DRIVE:
      pad.set_action(DrivingAction::START);
      break;
    default:
      AFATAL << "Unknown action to change driving mode to "
             << Chassis::DrivingMode_Name(mode);
  }

  constexpr int kMaxTries = 3;
  constexpr auto kTryInterval = std::chrono::milliseconds(500);
  auto *chassis = CHECK_NOTNULL(AdapterManager::GetChassis());
  for (int i = 0; i < kMaxTries; ++i) {
    // Send driving action periodically until entering target driving mode.
    AdapterManager::FillPadHeader("HMI", &pad);
    AdapterManager::PublishPad(pad);

    std::this_thread::sleep_for(kTryInterval);
    chassis->Observe();
    if (chassis->Empty()) {
      AERROR << "No Chassis message received!";
    } else if (chassis->GetLatestObserved().driving_mode() == mode) {
      return true;
    }
  }
  AERROR << "Failed to change driving mode to "
         << Chassis::DrivingMode_Name(mode);
  return false;
}

void HMIWorker::RunModeCommand(const std::string &command_name) {
  const Mode &mode_conf = config_.modes().at(status_.current_mode());
  if (command_name == "start" || command_name == "stop") {
    // Run the command on all live modules.
    for (const auto &module : mode_conf.live_modules()) {
      RunModuleCommand(module, command_name);
    }
  }
}

void HMIWorker::ChangeToMap(const std::string &map_name,
                            MapService *map_service) {
  if (status_.current_map() == map_name) {
    return;
  }
  const auto *map_dir = FindOrNull(config_.available_maps(), map_name);
  if (map_dir == nullptr) {
    AERROR << "Unknown map " << map_name;
    return;
  }
  status_.set_current_map(map_name);
  apollo::common::KVDB::Put("apollo:dreamview:map", map_name);

  FLAGS_map_dir = *map_dir;
  // Append new map_dir flag to global flagfile.
  std::ofstream fout(FLAGS_global_flagfile, std::ios_base::app);
  CHECK(fout) << "Fail to open " << FLAGS_global_flagfile;
  fout << "\n--map_dir=" << *map_dir << std::endl;
  // Also reload simulation map.
  CHECK(map_service->ReloadMap(true)) << "Failed to load map from " << *map_dir;
  RunModeCommand("stop");
}

void HMIWorker::ChangeToVehicle(const std::string &vehicle_name) {
  if (status_.current_vehicle() == vehicle_name) {
    return;
  }
  const auto *vehicle = FindOrNull(config_.available_vehicles(), vehicle_name);
  if (vehicle == nullptr) {
    AERROR << "Unknown vehicle " << vehicle_name;
    return;
  }
  status_.set_current_vehicle(vehicle_name);
  apollo::common::KVDB::Put("apollo:dreamview:vehicle", vehicle_name);

  CHECK(VehicleManager::instance()->UseVehicle(*vehicle));
  RunModeCommand("stop");
}

void HMIWorker::ChangeToMode(const std::string &mode_name) {
  const std::string &old_mode = status_.current_mode();
  if (old_mode == mode_name) {
    return;
  }
  if (!ContainsKey(config_.modes(), mode_name)) {
    AERROR << "Unknown mode " << mode_name;
    return;
  }
  const auto &old_modules = config_.modes().at(old_mode).live_modules();
  status_.set_current_mode(mode_name);
  apollo::common::KVDB::Put("apollo:dreamview:mode", mode_name);

  // Now stop all old modules.
  for (const auto &module : old_modules) {
    RunModuleCommand(module, "stop");
  }
}

void HMIWorker::UpdateSystemStatus(const monitor::SystemStatus &system_status) {
  *status_.mutable_system_status() = system_status;
}

}  // namespace dreamview
}  // namespace apollo
<commit_msg>Dreamview: Set and keep use_navigation_mode.<commit_after>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/dreamview/backend/hmi/hmi_worker.h"

#include <vector>

#include "gflags/gflags.h"
#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/kv_db/kv_db.h"
#include "modules/common/util/file.h"
#include "modules/common/util/map_util.h"
#include "modules/common/util/string_tokenizer.h"
#include "modules/common/util/string_util.h"
#include "modules/control/proto/pad_msg.pb.h"
#include "modules/data/util/info_collector.h"
#include "modules/dreamview/backend/common/dreamview_gflags.h"
#include "modules/dreamview/backend/hmi/vehicle_manager.h"

DEFINE_string(map_data_path, "/apollo/modules/map/data", "Path to map data.");

DEFINE_string(vehicle_data_path, "/apollo/modules/calibration/data",
              "Path to vehicle data.");

namespace apollo {
namespace dreamview {
namespace {

using apollo::canbus::Chassis;
using apollo::common::adapter::AdapterManager;
using apollo::common::util::ContainsKey;
using apollo::common::util::FindOrNull;
using apollo::common::util::StringTokenizer;
using apollo::control::DrivingAction;
using google::protobuf::Map;

constexpr char kNavigationModeName[] = "Navigation";

// Convert a string to be title-like. E.g.: "hello_world" -> "Hello World".
std::string TitleCase(const std::string &origin,
                      const std::string &delimiter = "_") {
  std::vector<std::string> parts = StringTokenizer::Split(origin, delimiter);
  for (auto &part : parts) {
    if (!part.empty()) {
      // Upper case the first char.
      part[0] = toupper(part[0]);
    }
  }

  return apollo::common::util::PrintIter(parts);
}

// List subdirs and return a dict of {subdir_title: subdir_path}.
Map<std::string, std::string> ListDirAsDict(const std::string &dir) {
  Map<std::string, std::string> result;
  const auto subdirs = apollo::common::util::ListSubDirectories(dir);
  for (const auto &subdir : subdirs) {
    const auto subdir_title = TitleCase(subdir);
    const auto subdir_path = apollo::common::util::StrCat(dir, "/", subdir);
    result.insert({subdir_title, subdir_path});
  }
  return result;
}

// Run a supported command of some component, return the ret code.
int RunComponentCommand(const Map<std::string, Component> &components,
                        const std::string &component_name,
                        const std::string &command_name) {
  const auto *component = FindOrNull(components, component_name);
  if (component == nullptr) {
    AERROR << "Cannot find component " << component_name;
    return -1;
  }
  const auto *cmd = FindOrNull(component->supported_commands(), command_name);
  if (cmd == nullptr) {
    AERROR << "Cannot find command " << component_name << "." << command_name;
    return -1;
  }
  AINFO << "Execute system command: " << *cmd;
  const int ret = std::system(cmd->c_str());

  AERROR_IF(ret != 0) << "Command returns " << ret << ": " << *cmd;
  return ret;
}

template <class FlagType, class ValueType>
void SetGlobalFlag(const std::string &flag_name, const ValueType &value,
                   FlagType* flag) {
  static constexpr char kGlobalFlagfile[] =
      "/apollo/modules/common/data/global_flagfile.txt";
  if (*flag != value) {
    *flag = value;
    // Overwrite global flagfile.
    std::ofstream fout(kGlobalFlagfile, std::ios_base::app);
    CHECK(fout) << "Fail to open global flagfile " << kGlobalFlagfile;
    fout << "\n--" << flag_name << "=" << value << std::endl;
  }
}

}  // namespace

HMIWorker::HMIWorker() {
  // Init HMIConfig.
  CHECK(common::util::GetProtoFromFile(FLAGS_hmi_config_filename, &config_))
      << "Unable to parse HMI config file " << FLAGS_hmi_config_filename;
  config_.set_docker_image(apollo::data::InfoCollector::GetDockerImage());

  // If the module path doesn't exist, remove it from list.
  auto *modules = config_.mutable_modules();
  for (auto iter = modules->begin(); iter != modules->end();) {
    const auto &conf = iter->second;
    if (conf.has_path() && !common::util::PathExists(conf.path())) {
      iter = modules->erase(iter);
    } else {
      ++iter;
    }
  }

  // Get available maps and vehicles by listing data directory.
  *config_.mutable_available_maps() = ListDirAsDict(FLAGS_map_data_path);
  *config_.mutable_available_vehicles() =
      ListDirAsDict(FLAGS_vehicle_data_path);
  ADEBUG << "Loaded HMI config: " << config_.DebugString();

  // Init HMIStatus.
  const auto &modes = config_.modes();
  if (FLAGS_use_navigation_mode && ContainsKey(modes, kNavigationModeName)) {
    // If the FLAG_use_navigation_mode is set, set it in HMIStatus.
    status_.set_current_mode(kNavigationModeName);
  } else if (!ContainsKey(modes, status_.current_mode())) {
    CHECK(!modes.empty()) << "No available modes to run vehicle.";
    // If the default mode is unavailable, select the first one.
    status_.set_current_mode(modes.begin()->first);
  }

  // If the FLAGS_map_dir is set, set it in HMIStatus.
  if (!FLAGS_map_dir.empty()) {
    for (const auto &entry : config_.available_maps()) {
      // entry is (map_name, map_path)
      if (entry.second == FLAGS_map_dir) {
        status_.set_current_map(entry.first);
        break;
      }
    }
  }
}

bool HMIWorker::Trigger(const HMIAction action) {
  AINFO << "HMIAction " << HMIAction_Name(action) << " was triggered!";
  // TODO(xiaoxq): Do the action.
  return false;
}

int HMIWorker::RunModuleCommand(const std::string &module,
                                const std::string &command) {
  return RunComponentCommand(config_.modules(), module, command);
}

int HMIWorker::RunHardwareCommand(const std::string &hardware,
                                  const std::string &command) {
  return RunComponentCommand(config_.hardware(), hardware, command);
}

int HMIWorker::RunToolCommand(const std::string &tool,
                              const std::string &command) {
  return RunComponentCommand(config_.tools(), tool, command);
}

void HMIWorker::SubmitDriveEvent(const uint64_t event_time_ms,
                                 const std::string &event_msg) {
  apollo::common::DriveEvent drive_event;
  AdapterManager::FillDriveEventHeader("HMI", &drive_event);
  drive_event.mutable_header()->set_timestamp_sec(event_time_ms / 1000.0);
  drive_event.set_event(event_msg);
  AdapterManager::PublishDriveEvent(drive_event);
}

bool HMIWorker::ChangeToDrivingMode(const Chassis::DrivingMode mode) {
  // Always reset to MANUAL mode before changing to other mode.
  if (mode != Chassis::COMPLETE_MANUAL) {
    if (!ChangeToDrivingMode(Chassis::COMPLETE_MANUAL)) {
      AERROR << "Failed to reset to MANUAL mode before changing to "
             << Chassis::DrivingMode_Name(mode);
      return false;
    }
  }

  control::PadMessage pad;
  switch (mode) {
    case Chassis::COMPLETE_MANUAL:
      pad.set_action(DrivingAction::RESET);
      break;
    case Chassis::COMPLETE_AUTO_DRIVE:
      pad.set_action(DrivingAction::START);
      break;
    default:
      AFATAL << "Unknown action to change driving mode to "
             << Chassis::DrivingMode_Name(mode);
  }

  constexpr int kMaxTries = 3;
  constexpr auto kTryInterval = std::chrono::milliseconds(500);
  auto *chassis = CHECK_NOTNULL(AdapterManager::GetChassis());
  for (int i = 0; i < kMaxTries; ++i) {
    // Send driving action periodically until entering target driving mode.
    AdapterManager::FillPadHeader("HMI", &pad);
    AdapterManager::PublishPad(pad);

    std::this_thread::sleep_for(kTryInterval);
    chassis->Observe();
    if (chassis->Empty()) {
      AERROR << "No Chassis message received!";
    } else if (chassis->GetLatestObserved().driving_mode() == mode) {
      return true;
    }
  }
  AERROR << "Failed to change driving mode to "
         << Chassis::DrivingMode_Name(mode);
  return false;
}

void HMIWorker::RunModeCommand(const std::string &command_name) {
  const Mode &mode_conf = config_.modes().at(status_.current_mode());
  if (command_name == "start" || command_name == "stop") {
    // Run the command on all live modules.
    for (const auto &module : mode_conf.live_modules()) {
      RunModuleCommand(module, command_name);
    }
  }
}

void HMIWorker::ChangeToMap(const std::string &map_name,
                            MapService *map_service) {
  if (status_.current_map() == map_name) {
    return;
  }
  const auto *map_dir = FindOrNull(config_.available_maps(), map_name);
  if (map_dir == nullptr) {
    AERROR << "Unknown map " << map_name;
    return;
  }
  status_.set_current_map(map_name);
  apollo::common::KVDB::Put("apollo:dreamview:map", map_name);

  SetGlobalFlag("map_dir", *map_dir, &FLAGS_map_dir);
  // Also reload simulation map.
  CHECK(map_service->ReloadMap(true)) << "Failed to load map from " << *map_dir;
  RunModeCommand("stop");
}

void HMIWorker::ChangeToVehicle(const std::string &vehicle_name) {
  if (status_.current_vehicle() == vehicle_name) {
    return;
  }
  const auto *vehicle = FindOrNull(config_.available_vehicles(), vehicle_name);
  if (vehicle == nullptr) {
    AERROR << "Unknown vehicle " << vehicle_name;
    return;
  }
  status_.set_current_vehicle(vehicle_name);
  apollo::common::KVDB::Put("apollo:dreamview:vehicle", vehicle_name);

  CHECK(VehicleManager::instance()->UseVehicle(*vehicle));
  RunModeCommand("stop");
}

void HMIWorker::ChangeToMode(const std::string &mode_name) {
  const std::string &old_mode = status_.current_mode();
  if (old_mode == mode_name) {
    return;
  }
  if (!ContainsKey(config_.modes(), mode_name)) {
    AERROR << "Unknown mode " << mode_name;
    return;
  }
  const auto &old_modules = config_.modes().at(old_mode).live_modules();
  status_.set_current_mode(mode_name);
  apollo::common::KVDB::Put("apollo:dreamview:mode", mode_name);

  const bool use_navigation_mode = (mode_name == kNavigationModeName);
  SetGlobalFlag("use_navigation_mode", use_navigation_mode,
                &FLAGS_use_navigation_mode);
  // Now stop all old modules.
  for (const auto &module : old_modules) {
    RunModuleCommand(module, "stop");
  }
}

void HMIWorker::UpdateSystemStatus(const monitor::SystemStatus &system_status) {
  *status_.mutable_system_status() = system_status;
}

}  // namespace dreamview
}  // namespace apollo
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/dreamview/backend/hmi/hmi_worker.h"

#include <vector>

#include "gflags/gflags.h"
#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/kv_db/kv_db.h"
#include "modules/common/util/file.h"
#include "modules/common/util/map_util.h"
#include "modules/common/util/string_tokenizer.h"
#include "modules/common/util/string_util.h"
#include "modules/control/proto/pad_msg.pb.h"
#include "modules/data/util/info_collector.h"
#include "modules/dreamview/backend/common/dreamview_gflags.h"
#include "modules/dreamview/backend/hmi/vehicle_manager.h"

DEFINE_string(map_data_path, "/apollo/modules/map/data", "Path to map data.");

DEFINE_string(vehicle_data_path, "/apollo/modules/calibration/data",
              "Path to vehicle data.");

namespace apollo {
namespace dreamview {
namespace {

using apollo::canbus::Chassis;
using apollo::common::adapter::AdapterManager;
using apollo::common::util::ContainsKey;
using apollo::common::util::FindOrNull;
using apollo::common::util::StringTokenizer;
using apollo::control::DrivingAction;
using google::protobuf::Map;
using RLock = boost::shared_lock<boost::shared_mutex>;
using WLock = boost::unique_lock<boost::shared_mutex>;

constexpr char kNavigationModeName[] = "Navigation";

// Convert a string to be title-like. E.g.: "hello_world" -> "Hello World".
std::string TitleCase(const std::string &origin,
                      const std::string &delimiter = "_") {
  std::vector<std::string> parts = StringTokenizer::Split(origin, delimiter);
  for (auto &part : parts) {
    if (!part.empty()) {
      // Upper case the first char.
      part[0] = toupper(part[0]);
    }
  }

  return apollo::common::util::PrintIter(parts);
}

// List subdirs and return a dict of {subdir_title: subdir_path}.
Map<std::string, std::string> ListDirAsDict(const std::string &dir) {
  Map<std::string, std::string> result;
  const auto subdirs = apollo::common::util::ListSubDirectories(dir);
  for (const auto &subdir : subdirs) {
    const auto subdir_title = TitleCase(subdir);
    const auto subdir_path = apollo::common::util::StrCat(dir, "/", subdir);
    result.insert({subdir_title, subdir_path});
  }
  return result;
}

// Run a supported command of some component, return the ret code.
int RunComponentCommand(const Map<std::string, Component> &components,
                        const std::string &component_name,
                        const std::string &command_name) {
  const auto *component = FindOrNull(components, component_name);
  if (component == nullptr) {
    AERROR << "Cannot find component " << component_name;
    return -1;
  }
  const auto *cmd = FindOrNull(component->supported_commands(), command_name);
  if (cmd == nullptr) {
    AERROR << "Cannot find command " << component_name << "." << command_name;
    return -1;
  }
  AINFO << "Execute system command: " << *cmd;
  const int ret = std::system(cmd->c_str());

  AERROR_IF(ret != 0) << "Command returns " << ret << ": " << *cmd;
  return ret;
}

template <class FlagType, class ValueType>
void SetGlobalFlag(const std::string &flag_name, const ValueType &value,
                   FlagType *flag) {
  static constexpr char kGlobalFlagfile[] =
      "/apollo/modules/common/data/global_flagfile.txt";
  if (*flag != value) {
    *flag = value;
    // Overwrite global flagfile.
    std::ofstream fout(kGlobalFlagfile, std::ios_base::app);
    CHECK(fout) << "Fail to open global flagfile " << kGlobalFlagfile;
    fout << "\n--" << flag_name << "=" << value << std::endl;
  }
}

}  // namespace

HMIWorker::HMIWorker() {
  // Init HMIConfig.
  CHECK(common::util::GetProtoFromFile(FLAGS_hmi_config_filename, &config_))
      << "Unable to parse HMI config file " << FLAGS_hmi_config_filename;
  config_.set_docker_image(apollo::data::InfoCollector::GetDockerImage());

  // If the module path doesn't exist, remove it from list.
  auto *modules = config_.mutable_modules();
  for (auto iter = modules->begin(); iter != modules->end();) {
    const auto &conf = iter->second;
    if (conf.has_path() && !common::util::PathExists(conf.path())) {
      iter = modules->erase(iter);
    } else {
      ++iter;
    }
  }

  // Get available maps and vehicles by listing data directory.
  *config_.mutable_available_maps() = ListDirAsDict(FLAGS_map_data_path);
  *config_.mutable_available_vehicles() =
      ListDirAsDict(FLAGS_vehicle_data_path);
  config_.set_utm_zone_id(FLAGS_local_utm_zone_id);
  ADEBUG << "Loaded HMI config: " << config_.DebugString();

  // Init HMIStatus.
  const auto &modes = config_.modes();
  if (FLAGS_use_navigation_mode && ContainsKey(modes, kNavigationModeName)) {
    // If the FLAG_use_navigation_mode is set, set it in HMIStatus.
    status_.set_current_mode(kNavigationModeName);
  } else if (!ContainsKey(modes, status_.current_mode())) {
    CHECK(!modes.empty()) << "No available modes to run vehicle.";
    // If the default mode is unavailable, select the first one.
    status_.set_current_mode(modes.begin()->first);
  }

  // If the FLAGS_map_dir is set, set it in HMIStatus.
  if (!FLAGS_map_dir.empty()) {
    for (const auto &entry : config_.available_maps()) {
      // entry is (map_name, map_path)
      if (entry.second == FLAGS_map_dir) {
        status_.set_current_map(entry.first);
        break;
      }
    }
  }
}

bool HMIWorker::Trigger(const HMIAction action) {
  AINFO << "HMIAction " << HMIAction_Name(action) << " was triggered!";
  switch (action) {
    case HMIAction::SETUP:
      RunModeCommand("start");
      break;
    case HMIAction::AUTO_MODE:
      return ChangeToDrivingMode(Chassis::COMPLETE_AUTO_DRIVE);
    case HMIAction::DISENGAGE:
      return ChangeToDrivingMode(Chassis::COMPLETE_MANUAL);
    default:
      AERROR << "HMIAction not implemented, yet!";
      return false;
  }
  return true;
}

int HMIWorker::RunModuleCommand(const std::string &module,
                                const std::string &command) {
  return RunComponentCommand(config_.modules(), module, command);
}

int HMIWorker::RunHardwareCommand(const std::string &hardware,
                                  const std::string &command) {
  return RunComponentCommand(config_.hardware(), hardware, command);
}

int HMIWorker::RunToolCommand(const std::string &tool,
                              const std::string &command) {
  return RunComponentCommand(config_.tools(), tool, command);
}

void HMIWorker::SubmitDriveEvent(const uint64_t event_time_ms,
                                 const std::string &event_msg) {
  apollo::common::DriveEvent drive_event;
  AdapterManager::FillDriveEventHeader("HMI", &drive_event);
  drive_event.mutable_header()->set_timestamp_sec(event_time_ms / 1000.0);
  drive_event.set_event(event_msg);
  AdapterManager::PublishDriveEvent(drive_event);
}

bool HMIWorker::ChangeToDrivingMode(const Chassis::DrivingMode mode) {
  // Always reset to MANUAL mode before changing to other mode.
  if (mode != Chassis::COMPLETE_MANUAL) {
    if (!ChangeToDrivingMode(Chassis::COMPLETE_MANUAL)) {
      AERROR << "Failed to reset to MANUAL mode before changing to "
             << Chassis::DrivingMode_Name(mode);
      return false;
    }
  }

  control::PadMessage pad;
  switch (mode) {
    case Chassis::COMPLETE_MANUAL:
      pad.set_action(DrivingAction::RESET);
      break;
    case Chassis::COMPLETE_AUTO_DRIVE:
      pad.set_action(DrivingAction::START);
      break;
    default:
      AFATAL << "Unknown action to change driving mode to "
             << Chassis::DrivingMode_Name(mode);
  }

  constexpr int kMaxTries = 3;
  constexpr auto kTryInterval = std::chrono::milliseconds(500);
  auto *chassis = CHECK_NOTNULL(AdapterManager::GetChassis());
  for (int i = 0; i < kMaxTries; ++i) {
    // Send driving action periodically until entering target driving mode.
    AdapterManager::FillPadHeader("HMI", &pad);
    AdapterManager::PublishPad(pad);

    std::this_thread::sleep_for(kTryInterval);
    chassis->Observe();
    if (chassis->Empty()) {
      AERROR << "No Chassis message received!";
    } else if (chassis->GetLatestObserved().driving_mode() == mode) {
      return true;
    }
  }
  AERROR << "Failed to change driving mode to "
         << Chassis::DrivingMode_Name(mode);
  return false;
}

void HMIWorker::RunModeCommand(const std::string &command_name) {
  std::string current_mode;
  {
    RLock rlock(status_mutex_);
    current_mode = status_.current_mode();
  }
  const Mode &mode_conf = config_.modes().at(current_mode);
  if (command_name == "start" || command_name == "stop") {
    // Run the command on all live modules.
    for (const auto &module : mode_conf.live_modules()) {
      RunModuleCommand(module, command_name);
    }
  }
}

void HMIWorker::ChangeToMap(const std::string &map_name,
                            MapService *map_service) {
  const auto *map_dir = FindOrNull(config_.available_maps(), map_name);
  if (map_dir == nullptr) {
    AERROR << "Unknown map " << map_name;
    return;
  }

  {
    // Update current_map status.
    WLock wlock(status_mutex_);
    if (status_.current_map() == map_name) {
      return;
    }
    status_.set_current_map(map_name);
  }
  apollo::common::KVDB::Put("apollo:dreamview:map", map_name);

  SetGlobalFlag("map_dir", *map_dir, &FLAGS_map_dir);
  // Also reload simulation map.
  CHECK(map_service->ReloadMap(true)) << "Failed to load map from " << *map_dir;
  RunModeCommand("stop");
}

void HMIWorker::ChangeToVehicle(const std::string &vehicle_name) {
  const auto *vehicle = FindOrNull(config_.available_vehicles(), vehicle_name);
  if (vehicle == nullptr) {
    AERROR << "Unknown vehicle " << vehicle_name;
    return;
  }

  {
    // Update current_vehicle status.
    WLock wlock(status_mutex_);
    if (status_.current_vehicle() == vehicle_name) {
      return;
    }
    status_.set_current_vehicle(vehicle_name);
  }
  apollo::common::KVDB::Put("apollo:dreamview:vehicle", vehicle_name);

  CHECK(VehicleManager::instance()->UseVehicle(*vehicle));
  RunModeCommand("stop");
}

void HMIWorker::ChangeToMode(const std::string &mode_name) {
  if (!ContainsKey(config_.modes(), mode_name)) {
    AERROR << "Unknown mode " << mode_name;
    return;
  }

  std::string old_mode;
  {
    // Update current_mode status.
    WLock wlock(status_mutex_);
    old_mode = status_.current_mode();
    if (old_mode == mode_name) {
      return;
    }
    status_.set_current_mode(mode_name);
  }
  apollo::common::KVDB::Put("apollo:dreamview:mode", mode_name);

  const auto &old_modules = config_.modes().at(old_mode).live_modules();
  const bool use_navigation_mode = (mode_name == kNavigationModeName);
  SetGlobalFlag("use_navigation_mode", use_navigation_mode,
                &FLAGS_use_navigation_mode);
  // Now stop all old modules.
  for (const auto &module : old_modules) {
    RunModuleCommand(module, "stop");
  }
}

void HMIWorker::UpdateSystemStatus(const monitor::SystemStatus &system_status) {
  WLock wlock(status_mutex_);
  *status_.mutable_system_status() = system_status;
}

}  // namespace dreamview
}  // namespace apollo
<commit_msg>Data: Fix missing map if it's set by default.<commit_after>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/dreamview/backend/hmi/hmi_worker.h"

#include <vector>

#include "gflags/gflags.h"
#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/kv_db/kv_db.h"
#include "modules/common/util/file.h"
#include "modules/common/util/map_util.h"
#include "modules/common/util/string_tokenizer.h"
#include "modules/common/util/string_util.h"
#include "modules/control/proto/pad_msg.pb.h"
#include "modules/data/util/info_collector.h"
#include "modules/dreamview/backend/common/dreamview_gflags.h"
#include "modules/dreamview/backend/hmi/vehicle_manager.h"

DEFINE_string(map_data_path, "/apollo/modules/map/data", "Path to map data.");

DEFINE_string(vehicle_data_path, "/apollo/modules/calibration/data",
              "Path to vehicle data.");

namespace apollo {
namespace dreamview {
namespace {

using apollo::canbus::Chassis;
using apollo::common::adapter::AdapterManager;
using apollo::common::util::ContainsKey;
using apollo::common::util::FindOrNull;
using apollo::common::util::StringTokenizer;
using apollo::control::DrivingAction;
using google::protobuf::Map;
using RLock = boost::shared_lock<boost::shared_mutex>;
using WLock = boost::unique_lock<boost::shared_mutex>;

constexpr char kNavigationModeName[] = "Navigation";

// Convert a string to be title-like. E.g.: "hello_world" -> "Hello World".
std::string TitleCase(const std::string &origin,
                      const std::string &delimiter = "_") {
  std::vector<std::string> parts = StringTokenizer::Split(origin, delimiter);
  for (auto &part : parts) {
    if (!part.empty()) {
      // Upper case the first char.
      part[0] = toupper(part[0]);
    }
  }

  return apollo::common::util::PrintIter(parts);
}

// List subdirs and return a dict of {subdir_title: subdir_path}.
Map<std::string, std::string> ListDirAsDict(const std::string &dir) {
  Map<std::string, std::string> result;
  const auto subdirs = apollo::common::util::ListSubDirectories(dir);
  for (const auto &subdir : subdirs) {
    const auto subdir_title = TitleCase(subdir);
    const auto subdir_path = apollo::common::util::StrCat(dir, "/", subdir);
    result.insert({subdir_title, subdir_path});
  }
  return result;
}

// Run a supported command of some component, return the ret code.
int RunComponentCommand(const Map<std::string, Component> &components,
                        const std::string &component_name,
                        const std::string &command_name) {
  const auto *component = FindOrNull(components, component_name);
  if (component == nullptr) {
    AERROR << "Cannot find component " << component_name;
    return -1;
  }
  const auto *cmd = FindOrNull(component->supported_commands(), command_name);
  if (cmd == nullptr) {
    AERROR << "Cannot find command " << component_name << "." << command_name;
    return -1;
  }
  AINFO << "Execute system command: " << *cmd;
  const int ret = std::system(cmd->c_str());

  AERROR_IF(ret != 0) << "Command returns " << ret << ": " << *cmd;
  return ret;
}

template <class FlagType, class ValueType>
void SetGlobalFlag(const std::string &flag_name, const ValueType &value,
                   FlagType *flag) {
  static constexpr char kGlobalFlagfile[] =
      "/apollo/modules/common/data/global_flagfile.txt";
  if (*flag != value) {
    *flag = value;
    // Overwrite global flagfile.
    std::ofstream fout(kGlobalFlagfile, std::ios_base::app);
    CHECK(fout) << "Fail to open global flagfile " << kGlobalFlagfile;
    fout << "\n--" << flag_name << "=" << value << std::endl;
  }
}

}  // namespace

HMIWorker::HMIWorker() {
  // Init HMIConfig.
  CHECK(common::util::GetProtoFromFile(FLAGS_hmi_config_filename, &config_))
      << "Unable to parse HMI config file " << FLAGS_hmi_config_filename;
  config_.set_docker_image(apollo::data::InfoCollector::GetDockerImage());

  // If the module path doesn't exist, remove it from list.
  auto *modules = config_.mutable_modules();
  for (auto iter = modules->begin(); iter != modules->end();) {
    const auto &conf = iter->second;
    if (conf.has_path() && !common::util::PathExists(conf.path())) {
      iter = modules->erase(iter);
    } else {
      ++iter;
    }
  }

  // Get available maps and vehicles by listing data directory.
  *config_.mutable_available_maps() = ListDirAsDict(FLAGS_map_data_path);
  *config_.mutable_available_vehicles() =
      ListDirAsDict(FLAGS_vehicle_data_path);
  config_.set_utm_zone_id(FLAGS_local_utm_zone_id);
  ADEBUG << "Loaded HMI config: " << config_.DebugString();

  // Init HMIStatus.
  const auto &modes = config_.modes();
  if (FLAGS_use_navigation_mode && ContainsKey(modes, kNavigationModeName)) {
    // If the FLAG_use_navigation_mode is set, set it in HMIStatus.
    status_.set_current_mode(kNavigationModeName);
  } else if (!ContainsKey(modes, status_.current_mode())) {
    CHECK(!modes.empty()) << "No available modes to run vehicle.";
    // If the default mode is unavailable, select the first one.
    status_.set_current_mode(modes.begin()->first);
  }

  // If the FLAGS_map_dir is set, set it in HMIStatus.
  if (!FLAGS_map_dir.empty()) {
    for (const auto &entry : config_.available_maps()) {
      // entry is (map_name, map_path)
      if (entry.second == FLAGS_map_dir) {
        status_.set_current_map(entry.first);
        apollo::common::KVDB::Put("apollo:dreamview:map", entry.first);
        break;
      }
    }
  }
}

bool HMIWorker::Trigger(const HMIAction action) {
  AINFO << "HMIAction " << HMIAction_Name(action) << " was triggered!";
  switch (action) {
    case HMIAction::SETUP:
      RunModeCommand("start");
      break;
    case HMIAction::AUTO_MODE:
      return ChangeToDrivingMode(Chassis::COMPLETE_AUTO_DRIVE);
    case HMIAction::DISENGAGE:
      return ChangeToDrivingMode(Chassis::COMPLETE_MANUAL);
    default:
      AERROR << "HMIAction not implemented, yet!";
      return false;
  }
  return true;
}

int HMIWorker::RunModuleCommand(const std::string &module,
                                const std::string &command) {
  return RunComponentCommand(config_.modules(), module, command);
}

int HMIWorker::RunHardwareCommand(const std::string &hardware,
                                  const std::string &command) {
  return RunComponentCommand(config_.hardware(), hardware, command);
}

int HMIWorker::RunToolCommand(const std::string &tool,
                              const std::string &command) {
  return RunComponentCommand(config_.tools(), tool, command);
}

void HMIWorker::SubmitDriveEvent(const uint64_t event_time_ms,
                                 const std::string &event_msg) {
  apollo::common::DriveEvent drive_event;
  AdapterManager::FillDriveEventHeader("HMI", &drive_event);
  drive_event.mutable_header()->set_timestamp_sec(event_time_ms / 1000.0);
  drive_event.set_event(event_msg);
  AdapterManager::PublishDriveEvent(drive_event);
}

bool HMIWorker::ChangeToDrivingMode(const Chassis::DrivingMode mode) {
  // Always reset to MANUAL mode before changing to other mode.
  if (mode != Chassis::COMPLETE_MANUAL) {
    if (!ChangeToDrivingMode(Chassis::COMPLETE_MANUAL)) {
      AERROR << "Failed to reset to MANUAL mode before changing to "
             << Chassis::DrivingMode_Name(mode);
      return false;
    }
  }

  control::PadMessage pad;
  switch (mode) {
    case Chassis::COMPLETE_MANUAL:
      pad.set_action(DrivingAction::RESET);
      break;
    case Chassis::COMPLETE_AUTO_DRIVE:
      pad.set_action(DrivingAction::START);
      break;
    default:
      AFATAL << "Unknown action to change driving mode to "
             << Chassis::DrivingMode_Name(mode);
  }

  constexpr int kMaxTries = 3;
  constexpr auto kTryInterval = std::chrono::milliseconds(500);
  auto *chassis = CHECK_NOTNULL(AdapterManager::GetChassis());
  for (int i = 0; i < kMaxTries; ++i) {
    // Send driving action periodically until entering target driving mode.
    AdapterManager::FillPadHeader("HMI", &pad);
    AdapterManager::PublishPad(pad);

    std::this_thread::sleep_for(kTryInterval);
    chassis->Observe();
    if (chassis->Empty()) {
      AERROR << "No Chassis message received!";
    } else if (chassis->GetLatestObserved().driving_mode() == mode) {
      return true;
    }
  }
  AERROR << "Failed to change driving mode to "
         << Chassis::DrivingMode_Name(mode);
  return false;
}

void HMIWorker::RunModeCommand(const std::string &command_name) {
  std::string current_mode;
  {
    RLock rlock(status_mutex_);
    current_mode = status_.current_mode();
  }
  const Mode &mode_conf = config_.modes().at(current_mode);
  if (command_name == "start" || command_name == "stop") {
    // Run the command on all live modules.
    for (const auto &module : mode_conf.live_modules()) {
      RunModuleCommand(module, command_name);
    }
  }
}

void HMIWorker::ChangeToMap(const std::string &map_name,
                            MapService *map_service) {
  const auto *map_dir = FindOrNull(config_.available_maps(), map_name);
  if (map_dir == nullptr) {
    AERROR << "Unknown map " << map_name;
    return;
  }

  {
    // Update current_map status.
    WLock wlock(status_mutex_);
    if (status_.current_map() == map_name) {
      return;
    }
    status_.set_current_map(map_name);
  }
  apollo::common::KVDB::Put("apollo:dreamview:map", map_name);

  SetGlobalFlag("map_dir", *map_dir, &FLAGS_map_dir);
  // Also reload simulation map.
  CHECK(map_service->ReloadMap(true)) << "Failed to load map from " << *map_dir;
  RunModeCommand("stop");
}

void HMIWorker::ChangeToVehicle(const std::string &vehicle_name) {
  const auto *vehicle = FindOrNull(config_.available_vehicles(), vehicle_name);
  if (vehicle == nullptr) {
    AERROR << "Unknown vehicle " << vehicle_name;
    return;
  }

  {
    // Update current_vehicle status.
    WLock wlock(status_mutex_);
    if (status_.current_vehicle() == vehicle_name) {
      return;
    }
    status_.set_current_vehicle(vehicle_name);
  }
  apollo::common::KVDB::Put("apollo:dreamview:vehicle", vehicle_name);

  CHECK(VehicleManager::instance()->UseVehicle(*vehicle));
  RunModeCommand("stop");
}

void HMIWorker::ChangeToMode(const std::string &mode_name) {
  if (!ContainsKey(config_.modes(), mode_name)) {
    AERROR << "Unknown mode " << mode_name;
    return;
  }

  std::string old_mode;
  {
    // Update current_mode status.
    WLock wlock(status_mutex_);
    old_mode = status_.current_mode();
    if (old_mode == mode_name) {
      return;
    }
    status_.set_current_mode(mode_name);
  }
  apollo::common::KVDB::Put("apollo:dreamview:mode", mode_name);

  const auto &old_modules = config_.modes().at(old_mode).live_modules();
  const bool use_navigation_mode = (mode_name == kNavigationModeName);
  SetGlobalFlag("use_navigation_mode", use_navigation_mode,
                &FLAGS_use_navigation_mode);
  // Now stop all old modules.
  for (const auto &module : old_modules) {
    RunModuleCommand(module, "stop");
  }
}

void HMIWorker::UpdateSystemStatus(const monitor::SystemStatus &system_status) {
  WLock wlock(status_mutex_);
  *status_.mutable_system_status() = system_status;
}

}  // namespace dreamview
}  // namespace apollo
<|endoftext|>


<commit_before>/*****************************************************************************
 * EPGRuler.coo: EPGRuler
 ****************************************************************************
 * Copyright © 2009-2010 VideoLAN
 * $Id$
 *
 * Authors: Ludovic Fauvet <etix@l0cal.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "EPGRuler.hpp"

#include <QPainter>
#include <QFont>
#include <QPaintEvent>
#include <QtDebug>
#include <QDateTime>

EPGRuler::EPGRuler( QWidget* parent )
    : QWidget( parent )
{
    setContentsMargins( 0, 0, 0, 0 );
    setMinimumHeight( 30 );
    setMaximumHeight( 30 );
    m_offset = 0;
}

void EPGRuler::setScale( double scale )
{
    m_scale = scale;
    update();
}

void EPGRuler::setStartTime( const QDateTime& startTime )
{
    m_startTime = startTime;
    update();
}

void EPGRuler::setDuration( int duration )
{
    m_duration = duration;
    update();
}

void EPGRuler::setOffset( int offset )
{
    m_offset = offset;
    update();
}

void EPGRuler::paintEvent( QPaintEvent *event )
{
    Q_UNUSED( event );

    QPainter p( this );

    int secondsOffset = m_offset / m_scale;

    QDateTime localStartTime;
    localStartTime = m_startTime.addSecs( secondsOffset );

    QDateTime diff( localStartTime );
    diff.setTime( QTime( localStartTime.time().hour(), 0, 0, 0 ) );

    int secondsToHour = localStartTime.secsTo( diff );

    QDateTime current( localStartTime.addSecs( secondsToHour ) );

    int spacing = ( m_scale * 60 ) * 60;
    int posx = secondsToHour * m_scale;

    // Count the number of items to draw
    int itemsToDraw = ( width() / spacing ) + 1;

    for ( ; itemsToDraw >= 0; --itemsToDraw )
    {
        p.setFont( QFont( "Verdana", 8 ) );
        p.drawLine( posx, 15, posx, 30 );
        p.drawText( posx + 1, 12, 50, 15, Qt::AlignLeft, current.toString( "hh'h'" ) );
        posx += spacing;
        current = current.addSecs( 60 * 60 );
    }
}
<commit_msg>qt4: don't force font and size in epg-widget<commit_after>/*****************************************************************************
 * EPGRuler.coo: EPGRuler
 ****************************************************************************
 * Copyright © 2009-2010 VideoLAN
 * $Id$
 *
 * Authors: Ludovic Fauvet <etix@l0cal.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "EPGRuler.hpp"

#include <QPainter>
#include <QFont>
#include <QPaintEvent>
#include <QtDebug>
#include <QDateTime>

EPGRuler::EPGRuler( QWidget* parent )
    : QWidget( parent )
{
    setContentsMargins( 0, 0, 0, 0 );
    setMinimumHeight( 30 );
    setMaximumHeight( 30 );
    m_offset = 0;
}

void EPGRuler::setScale( double scale )
{
    m_scale = scale;
    update();
}

void EPGRuler::setStartTime( const QDateTime& startTime )
{
    m_startTime = startTime;
    update();
}

void EPGRuler::setDuration( int duration )
{
    m_duration = duration;
    update();
}

void EPGRuler::setOffset( int offset )
{
    m_offset = offset;
    update();
}

void EPGRuler::paintEvent( QPaintEvent *event )
{
    Q_UNUSED( event );

    QPainter p( this );

    int secondsOffset = m_offset / m_scale;

    QDateTime localStartTime;
    localStartTime = m_startTime.addSecs( secondsOffset );

    QDateTime diff( localStartTime );
    diff.setTime( QTime( localStartTime.time().hour(), 0, 0, 0 ) );

    int secondsToHour = localStartTime.secsTo( diff );

    QDateTime current( localStartTime.addSecs( secondsToHour ) );

    int spacing = ( m_scale * 60 ) * 60;
    int posx = secondsToHour * m_scale;

    // Count the number of items to draw
    int itemsToDraw = ( width() / spacing ) + 1;

    for ( ; itemsToDraw >= 0; --itemsToDraw )
    {
        p.drawLine( posx, 15, posx, 30 );
        p.drawText( posx + 1, 12, 50, 15, Qt::AlignLeft, current.toString( "hh'h'" ) );
        posx += spacing;
        current = current.addSecs( 60 * 60 );
    }
}
<|endoftext|>


<commit_before>/*
 * server_test.cpp
 *
 *  Created on: Jan 16, 2013
 *      Author: mfleder
 */

#include <stdio.h>
#include <lcm/lcm-cpp.hpp>
#include <boost/shared_ptr.hpp>
#include "affordance/AffordanceServer.h"
#include "affordance/AffordanceUpWrapper.h"
#include "affordance/AffordanceState.h"
#include <iostream>

using namespace boost;
using namespace std;
using namespace affordance;

/**populates the server w/ a bunch of affordances*/
void runPopulate(const shared_ptr<lcm::LCM> lcm)
{
    AffordanceUpWrapper wrapper(lcm);

    const int mapId = 0;
    int uniqueObjId = 0;

    //sphere
    AffordanceState sphere(AffordanceState::SPHERE, uniqueObjId++, mapId,
                           KDL::Frame(KDL::Vector(-0.5, -0.5, 0.0)),
                           Eigen::Vector3f(1.0, 0.0, 1.0));   //color
    sphere._params[AffordanceState::RADIUS_NAME] = 0.125;
    wrapper.addNewlyFittedAffordance(sphere);

    //box
    AffordanceState box(AffordanceState::BOX, uniqueObjId++, mapId,
                        KDL::Frame(KDL::Vector(0.0, 0.0, 0.0)),
                        Eigen::Vector3f(0.75, 0.75, 0.0));
    box._params[AffordanceState::LENGTH_NAME] = 100;
    box._params[AffordanceState::WIDTH_NAME]  = 100;
    box._params[AffordanceState::HEIGHT_NAME] = .00001;
    wrapper.addNewlyFittedAffordance(box);

    //cylinder
    AffordanceState cylinder(AffordanceState::CYLINDER, uniqueObjId++, mapId,
                             KDL::Frame(KDL::Vector(0.0, 1.0, 0.0)),
                             Eigen::Vector3f(0.0, 1.0, 1.0)); //color
    cylinder._params[AffordanceState::RADIUS_NAME] = 0.25;
    cylinder._params[AffordanceState::LENGTH_NAME] = 0.25;
    wrapper.addNewlyFittedAffordance(cylinder);

    //=============
    //add a bunch of affordances w/ just names + ids
    //	wrapper.addNewlyFittedAffordance(AffordanceState("steering_cyl",      uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("box",       uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("ladder",      uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("cylinder",       uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("sphere",  uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("box", 		uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("ladder_cyl", 	uniqueObjId++, mapId));

    int j = 0;
    boost::posix_time::seconds sleepTime(1); //update every 1 seconds

    while (true)
    {
        //cout << "\n\n===" << j << endl;
        cout << wrapper << endl;
        boost::this_thread::sleep(sleepTime); //======sleep

        //======modify server
        if (j++ == 10)
        {
            //make a modification
        }
    }
}

int main(int argc, char **argv)
{
    shared_ptr<lcm::LCM> theLcm(new lcm::LCM());

    if (!theLcm->good())
    {
        cerr << "Cannot create lcm object" << endl;
        return -1;
    }

    //create the server
    AffordanceServer s(theLcm);

    //thread will add to the affordance server
    //and possibly make changes over time
    boost::thread populateThread = boost::thread(runPopulate, theLcm);

    //lcm loop
    cout << "\nstarting lcm loop" << endl;

    while (0 == theLcm->handle())
    {
        ;
    }

    return 0;
}
<commit_msg>updated for new affordanceState constructor<commit_after>/*
 * server_test.cpp
 *
 *  Created on: Jan 16, 2013
 *      Author: mfleder
 */

#include <stdio.h>
#include <lcm/lcm-cpp.hpp>
#include <boost/shared_ptr.hpp>
#include "affordance/AffordanceServer.h"
#include "affordance/AffordanceUpWrapper.h"
#include "affordance/AffordanceState.h"
#include <iostream>

using namespace boost;
using namespace std;
using namespace affordance;

/**populates the server w/ a bunch of affordances*/
void runPopulate(const shared_ptr<lcm::LCM> lcm)
{
    AffordanceUpWrapper wrapper(lcm);

    const int mapId = 0;
    int uniqueObjId = 0;

    //sphere
    AffordanceState sphere(uniqueObjId++, mapId,
                           KDL::Frame(KDL::Vector(-0.5, -0.5, 0.0)),
                           Eigen::Vector3f(1.0, 0.0, 1.0));   //color
    sphere._params[AffordanceState::RADIUS_NAME] = 0.125;
    sphere.setType(AffordanceState::SPHERE);
    wrapper.addNewlyFittedAffordance(sphere);

    //box
    AffordanceState box(uniqueObjId++, mapId,
                        KDL::Frame(KDL::Vector(0.0, 0.0, 0.0)),
                        Eigen::Vector3f(0.75, 0.75, 0.0));
    box._params[AffordanceState::LENGTH_NAME] = 100;
    box._params[AffordanceState::WIDTH_NAME]  = 100;
    box._params[AffordanceState::HEIGHT_NAME] = .00001;
    box.setType(AffordanceState::BOX);
    wrapper.addNewlyFittedAffordance(box);

    //cylinder
    AffordanceState cylinder(uniqueObjId++, mapId,
                             KDL::Frame(KDL::Vector(0.0, 1.0, 0.0)),
                             Eigen::Vector3f(0.0, 1.0, 1.0)); //color
    cylinder._params[AffordanceState::RADIUS_NAME] = 0.25;
    cylinder._params[AffordanceState::LENGTH_NAME] = 0.25;
    cylinder.setType(AffordanceState::CYLINDER);
    wrapper.addNewlyFittedAffordance(cylinder);

    //=============
    //add a bunch of affordances w/ just names + ids
    //	wrapper.addNewlyFittedAffordance(AffordanceState("steering_cyl",      uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("box",       uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("ladder",      uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("cylinder",       uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("sphere",  uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("box", 		uniqueObjId++, mapId));
    //	wrapper.addNewlyFittedAffordance(AffordanceState("ladder_cyl", 	uniqueObjId++, mapId));

    int j = 0;
    boost::posix_time::seconds sleepTime(1); //update every 1 seconds

    while (true)
    {
        //cout << "\n\n===" << j << endl;
        cout << wrapper << endl;
        boost::this_thread::sleep(sleepTime); //======sleep

        //======modify server
        if (j++ == 10)
        {
            //make a modification
        }
    }
}

int main(int argc, char **argv)
{
    shared_ptr<lcm::LCM> theLcm(new lcm::LCM());

    if (!theLcm->good())
    {
        cerr << "Cannot create lcm object" << endl;
        return -1;
    }

    //create the server
    AffordanceServer s(theLcm);

    //thread will add to the affordance server
    //and possibly make changes over time
    boost::thread populateThread = boost::thread(runPopulate, theLcm);

    //lcm loop
    cout << "\nstarting lcm loop" << endl;

    while (0 == theLcm->handle())
    {
        ;
    }

    return 0;
}
<|endoftext|>