<commit_before>#include "KEngine/App.hpp"

#include "KEngine/Common/macros.hpp"
#include "KEngine/Common/HeartBeatGenerator.hpp"
#include "KEngine/Common/ScopeFunc.hpp"
#include "KEngine/Common/StopWatch.hpp"
#include "KEngine/Common/String.hpp"
#include "KEngine/Core/EventManager.hpp"
#include "KEngine/Core/FrameRateCounter.hpp"

#include "KEngine/Events/AppExitRequestedEvent.hpp"
#include "KEngine/Events/EventLoopFrameEvent.hpp"
#include "KEngine/Events/LogicLoopFrameEvent.hpp"
#include "KEngine/Events/GraphicsLoopFrameEvent.hpp"
#include "KEngine/Events/GraphicsLoopSetupFailureEvent.hpp"
#include "KEngine/Events/SDL2/SDL2Event.hpp"
#include "KEngine/Events/SFML/SfmlEvent.hpp"
#include "KEngine/Events/SFML/SfmlEventTranslator.hpp"

#include "KEngine/Log/Log.hpp"

#include "KEngine/Graphics/WindowFactory.hpp"

#include <SDL.h>
#include <SFML/Graphics/RenderWindow.hpp>

#include <atomic>
#include <cstdio>
#include <memory>

namespace
{

    static const ke::Time LOGIC_UPDATE_FIXED_TIMESPAN = ke::Time::milliseconds(20); // 50fps.

    static const auto EVENT_THREAD_SLEEP_DURATION = std::chrono::microseconds(100);
    static const auto LOGIC_THREAD_SLEEP_DURATION = std::chrono::milliseconds(2);
    static const auto RENDER_THREAD_SLEEP_DURATION = std::chrono::microseconds(10);

    static const ke::Time LOGIC_THREAD_TARGET_FRAMETIME = ke::Time::milliseconds(10);

    static std::atomic<double> logicLoopFrameTimeMs = { 0.0f };
    static std::atomic<double> renderLoopFrameTimeMs = { 0.0f };

    static std::atomic<float> eventLoopFps    = { 0.0f };
    static std::atomic<float> logicLoopFps    = { 0.0f };
    static std::atomic<float> graphicsLoopFps = { 0.0f };

    static std::atomic<size_t> graphicsCommandCount = { 0 };
    static std::atomic<size_t> graphicsDrawCallCount = { 0 };

    static std::atomic<size_t> eventManagerEventCount = { 0 };

    static char windowTitleTextBuffer[256] = {};

}

namespace ke
{
    ke::App * App::sGlobalAppInstance = nullptr;

    App::App()
    {
        assert(!App::sGlobalAppInstance);
        App::sGlobalAppInstance = this;
    }

    App::~App()
    {
        assert(App::sGlobalAppInstance);
        App::sGlobalAppInstance = nullptr;
    }

    int App::exec()
    {
        this->onBeforeInitialisation();

        this->initExec();
        KE_MAKE_SCOPEFUNC([this]() {
            this->cleanUpExec();
        });

        this->onPostInitialisation();

#if defined(USE_SDL)
        auto sdlInitResult = SDL_Init(SDL_INIT_VIDEO);
        if (sdlInitResult < 0)
        {
            Log::instance()->critical("SDL could not initialize! SDL_Error: {}", SDL_GetError());
            return sdlInitResult;
        }
        ke::Log::instance()->info("SDL initialisation successful.");
#endif

        Log::instance()->info("Creating application main window ...");
        this->mainWindow = ke::WindowFactory::newWindow();
        if (nullptr == mainWindow)
        {
#if defined(USE_SDL)
            Log::instance()->critical("SDL window could not be created. Error: {}", SDL_GetError());
#elif defined(USE_SFML)
            Log::instance()->critical("SFML window could not be created.");
#endif
            return ke::ExitCodes::FAILURE_WINDOW_CREATION;
        }
        this->mainWindow->setThreadCurrent(false);// disable window on this thread so can be made thread current on render thread.

        this->renderSystem = std::make_unique<ke::RenderSystem>();
        this->renderSystem->setWindow(this->mainWindow);

        // enter all the engine loops.
        this->enterLogicLoop();
        this->enterGraphicsLoop();
        this->enterEventLoop();

        this->onBeforeShutdown();

        ke::Log::instance()->info("Destroying main window ...");
        this->mainWindow.reset();

#if defined(USE_SDL)
        ke::Log::instance()->info("Shutting down SDL ...");
        SDL_Quit();
#endif

        this->onPostShutdown();

        return ke::ExitCodes::SUCCESS;
    }

    void App::enterEventLoop()
    {
        ke::Log::instance()->info("Entering KEngine event loop ...");

        this->isEventLoopRunning = true;
        ke::StopWatch stopwatch;
        ke::HeartBeatGenerator heartBeat(ke::Time::milliseconds(5000));
        ke::FrameRateCounter fpsCounter(1000);

        while(this->isEventLoopRunning)
        {
            const auto frameTime = stopwatch.getElapsed();
            stopwatch.restart();

            fpsCounter.push(frameTime);
            ::eventLoopFps.store((float)fpsCounter.getAverageFps(), std::memory_order_relaxed);

            ke::EventManager::enqueue(ke::makeEvent<EventLoopFrameEvent>(frameTime));

#if defined(USE_SDL)
            SDL_Event event;
            while (SDL_PollEvent(&event) != 0)
            {
                ke::EventManager::queue(ke::makeEvent<SDL2Event>(event));
                if (event.type == SDL_QUIT || event.type == SDL_WINDOWEVENT_CLOSE)
                {
                    Log::instance()->info("Normal exit requested.");
                    ke::EventManager::queue(ke::makeEvent<AppExitRequestedEvent>());
                }
            }
#elif defined(USE_SFML)
            sf::Event event;
            auto sfWindow = static_cast<sf::RenderWindow*>(this->mainWindow->get());
            assert(sfWindow);
            while (sfWindow->pollEvent(event))
            {
                ke::EventManager::enqueue(ke::makeEvent<ke::SfmlEvent>(event));
                switch (event.type)
                {
                case sf::Event::EventType::MouseButtonPressed:
                case sf::Event::EventType::MouseButtonReleased:
                case sf::Event::EventType::KeyPressed:
                case sf::Event::EventType::KeyReleased:
                case sf::Event::EventType::TextEntered:
                case sf::Event::EventType::Resized:
                {
                    auto keEvent = SfmlEventTranslator::translate(event, sfWindow);
                    if (keEvent)
                    {
                        ke::EventManager::enqueue(std::move(keEvent));
                    }
                    break;
                }

                case sf::Event::EventType::Closed:
                    Log::instance()->info("Normal exit requested.");
                    ke::EventManager::enqueue(ke::makeEvent<AppExitRequestedEvent>());
                    break;

                default: break;
                }
            }
#endif

            // set title with stats.
            const auto memO = std::memory_order_relaxed;
            using LLU = unsigned long long;
            std::snprintf(::windowTitleTextBuffer, sizeof(::windowTitleTextBuffer),
                "KEngine - FPS(%4.1f, %4.1f[%4.1f/ms], %4.1f[%4.1f/ms]), GraphicsCommands(%llu), DrawCalls(%llu), Events(%llu).",
                ::eventLoopFps.load(memO),
                ::logicLoopFps.load(memO), ::logicLoopFrameTimeMs.load(memO),
                ::graphicsLoopFps.load(memO), ::renderLoopFrameTimeMs.load(memO),
                (LLU)::graphicsCommandCount.load(memO), (LLU)::graphicsDrawCallCount.load(memO),
                (LLU)::eventManagerEventCount.load(memO));
            this->mainWindow->setTitle(::windowTitleTextBuffer);

            if (heartBeat)
            {
                ke::Log::instance()->info("Event loop heart beat");
            }

            std::this_thread::sleep_for(EVENT_THREAD_SLEEP_DURATION);
        }

        ke::Log::instance()->info("KEngine event loop exited.");

        // join render loop thread
        if (this->graphicsLoopThread.joinable())
        {
            ke::Log::instance()->info("KEngine graphics loop thread joining ...");
            this->graphicsLoopThread.join();
            ke::Log::instance()->info("KEngine graphics loop thread joined");
        }
        else
        {
            ke::Log::instance()->warn("KEngine logic loop thread not joinable.");
        }

        // join logic loop thread.
        if (this->logicLoopThread.joinable())
        {
            ke::Log::instance()->info("KEngine logic loop thread joining ...");
            this->logicLoopThread.join();
            ke::Log::instance()->info("KEngine logic loop thread joined");
        }
        else
        {
            ke::Log::instance()->warn("KEngine logic loop thread not joinable.");
        }

        this->mainWindow->setThreadCurrent(true);
        this->mainWindow.reset();
    }

    void App::enterLogicLoop()
    {
        this->logicLoopThread = std::thread([this]()
        {

            ke::EventManager::registerListener<ke::AppExitRequestedEvent>(this, &ke::App::handleAppExitRequest);
            ke::EventManager::registerListener<ke::GraphicsLoopSetupFailureEvent>(this, &ke::App::handleGraphicsLoopSetupFailure);

            ke::Log::instance()->info("Entering KEngine logic loop ...");

            ke::StopWatch stopwatch;
            ke::Time cumulativeLoopTime;
            ke::HeartBeatGenerator heartBeat(ke::Time::milliseconds(5000));
            ke::FrameRateCounter fpsCounter(1000);
            this->isLogicLoopRunning = true;

            while(this->isLogicLoopRunning)
            {
                const auto frameTime = stopwatch.getElapsed();
                stopwatch.restart();
                cumulativeLoopTime += frameTime;

                fpsCounter.push(frameTime);
                ::logicLoopFps.store((float)fpsCounter.getAverageFps(), std::memory_order_relaxed);
                ::logicLoopFrameTimeMs.store(frameTime.asNanoseconds()/1000000.0, std::memory_order_relaxed);
                ::eventManagerEventCount.store(ke::EventManager::instance()->getEventCount(), std::memory_order_relaxed);

//                if (frameTime < ::LOGIC_THREAD_TARGET_FRAMETIME)
//                {
//                    const auto difference = ::LOGIC_THREAD_TARGET_FRAMETIME - frameTime;
//                    std::this_thread::sleep_for(std::chrono::nanoseconds(difference.asNanoseconds()));
//                }
//                std::this_thread::sleep_for(std::chrono::milliseconds(::LOGIC_THREAD_TARGET_FRAMETIME.asMilliseconds()));

                //
                // update logic
                //
                while (cumulativeLoopTime >= ::LOGIC_UPDATE_FIXED_TIMESPAN)
                {
                    cumulativeLoopTime -= ::LOGIC_UPDATE_FIXED_TIMESPAN;

                    ke::EventManager::enqueue(ke::makeEvent<LogicLoopFrameEvent>(::LOGIC_UPDATE_FIXED_TIMESPAN));
                    ke::EventManager::update();

                    //
                    this->resourceManager->update();

                    ke::EventManager::update();

                    // update logic.
                    this->appLogic->update(::LOGIC_UPDATE_FIXED_TIMESPAN);
                }

                //
                // prepare and dispatch render commands
                //
                this->renderSystem->prepareCommands(this->appLogic->getCurrentHumanView()->getScene());
                auto cmdCount = this->renderSystem->dispatchCommands();
                ::graphicsCommandCount.store(cmdCount, std::memory_order_relaxed);

                //
                // debug/diagnostic stuff
                //
                if (heartBeat)
                {
                    ke::Log::instance()->info("Logic loop heart beat");
                }

                if (frameTime < ke::Time::milliseconds(1))
                    std::this_thread::sleep_for(LOGIC_THREAD_SLEEP_DURATION);
            }

            // we only flag event loop for termination here as some logics may still require the event loop to be alive.
            this->isEventLoopRunning = false;

            ke::Log::instance()->info("KEngine logic loop exited.");

            //
            // clean up.
            //
            this->getLogic()->getSystemManager()->clear();
        });
    }

    void App::enterGraphicsLoop()
    {
        this->graphicsLoopThread = std::thread([this]() {

            if (!this->mainWindow->setThreadCurrent(true))
            {
#if defined(USE_SDL)
                Log::instance()->critical("Failure enabling SDL2 window on thread {}. Cannot start graphics loop. SDL2 error: {}",
                    std::hash<std::thread::id>()(std::this_thread::get_id()),
                    SDL_GetError());
#elif defined(USE_SFML)
                Log::instance()->critical("Failure enabling SFML window on thread {}. Cannot start graphics loop.",
                    std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
                ke::EventManager::dispatchNow(ke::makeEvent<ke::GraphicsLoopSetupFailureEvent>());
                return;
            }

            if (!this->renderSystem->initialise())
            {
                Log::instance()->critical("Failuring initialising render system on thread {}.",
                    std::hash<std::thread::id>()(std::this_thread::get_id()));
                return;
            }

            mainWindow->display();

            ke::Log::instance()->info("Entering KEngine render loop ...");

            ke::StopWatch stopwatch;
            ke::HeartBeatGenerator heartBeat(ke::Time::milliseconds(5000));
            ke::FrameRateCounter fpsCounter(500);
            this->isGraphicsLoopRunning = true;

            while (this->isGraphicsLoopRunning)
            {
                const auto frameTime = stopwatch.getElapsed();
                stopwatch.restart();

                fpsCounter.push(frameTime);
                ::graphicsLoopFps.store((float)fpsCounter.getAverageFps(), std::memory_order_relaxed);
                ::renderLoopFrameTimeMs.store(frameTime.asMicroseconds()/1000.0, std::memory_order_relaxed);

                ke::EventManager::enqueue(ke::makeEvent<GraphicsLoopFrameEvent>(frameTime));

                if (heartBeat)
                {
                    ke::Log::instance()->info("Render loop heart beat");
                }

                this->renderSystem->updateOnRenderLoop(frameTime);

                // process the oldest render command list from the render commands queue
                // e.g. culling, ordering, etc...
                // if queue is empty then interpolate before render.
                this->renderSystem->processCommands(frameTime);
                const auto drawCallCount = this->renderSystem->render();
                ::graphicsDrawCallCount.store(drawCallCount > 0 ? drawCallCount : ::graphicsDrawCallCount.load(), std::memory_order_relaxed);

                if (frameTime < ke::Time::milliseconds(1))
                    std::this_thread::sleep_for(::RENDER_THREAD_SLEEP_DURATION);
            }

            this->renderSystem->shutdown();
            this->mainWindow->setThreadCurrent(false);

            ke::Log::instance()->info("KEngine render loop exited.");

        });
    }

    void App::setLogic(ke::AppLogicUptr && p_appLogic)
    {
        this->appLogic = std::move(p_appLogic);
    }

    void App::initExec()
    {
        spdlog::set_async_mode(1048576); // magic number from spdlog repo.

        ke::Log::instance()->info("Creating logic and views ...");
        this->createLogicAndViews();

        assert(this->getLogic());
        assert(this->getLogic()->getCurrentHumanView());

        ke::Log::instance()->info("Creating resource manager ...");
        this->resourceManager = std::make_unique<ResourceManager>();
    }

    void App::cleanUpExec()
    {
        spdlog::drop_all();
    }

    void App::handleAppExitRequest(ke::EventSptr)
    {
        if (isNormalExitRequested)
        {
            ke::Log::instance()->info("Already handling app exit request.");
            return;
        }
        isNormalExitRequested = true;
        ke::Log::instance()->info("Handling app exit request.");

        //this->isEventLoopRunning = false;
        this->isLogicLoopRunning = false;
        this->isGraphicsLoopRunning = false;
    }

    void App::handleGraphicsLoopSetupFailure(ke::EventSptr)
    {
        ke::Log::instance()->critical("Graphics loop setup failure encountered. Shuting down ...");
        this->isLogicLoopRunning = false;
        this->isGraphicsLoopRunning = false;
        this->isEventLoopRunning = false;
    }

}
<commit_msg>change log message during app init<commit_after>#include "KEngine/App.hpp"

#include "KEngine/Common/macros.hpp"
#include "KEngine/Common/HeartBeatGenerator.hpp"
#include "KEngine/Common/ScopeFunc.hpp"
#include "KEngine/Common/StopWatch.hpp"
#include "KEngine/Common/String.hpp"
#include "KEngine/Core/EventManager.hpp"
#include "KEngine/Core/FrameRateCounter.hpp"

#include "KEngine/Events/AppExitRequestedEvent.hpp"
#include "KEngine/Events/EventLoopFrameEvent.hpp"
#include "KEngine/Events/LogicLoopFrameEvent.hpp"
#include "KEngine/Events/GraphicsLoopFrameEvent.hpp"
#include "KEngine/Events/GraphicsLoopSetupFailureEvent.hpp"
#include "KEngine/Events/SDL2/SDL2Event.hpp"
#include "KEngine/Events/SFML/SfmlEvent.hpp"
#include "KEngine/Events/SFML/SfmlEventTranslator.hpp"

#include "KEngine/Log/Log.hpp"

#include "KEngine/Graphics/WindowFactory.hpp"

#include <SDL.h>
#include <SFML/Graphics/RenderWindow.hpp>

#include <atomic>
#include <cstdio>
#include <memory>

namespace
{

    static const ke::Time LOGIC_UPDATE_FIXED_TIMESPAN = ke::Time::milliseconds(20); // 50fps.

    static const auto EVENT_THREAD_SLEEP_DURATION = std::chrono::microseconds(100);
    static const auto LOGIC_THREAD_SLEEP_DURATION = std::chrono::milliseconds(2);
    static const auto RENDER_THREAD_SLEEP_DURATION = std::chrono::microseconds(10);

    static const ke::Time LOGIC_THREAD_TARGET_FRAMETIME = ke::Time::milliseconds(10);

    static std::atomic<double> logicLoopFrameTimeMs = { 0.0f };
    static std::atomic<double> renderLoopFrameTimeMs = { 0.0f };

    static std::atomic<float> eventLoopFps    = { 0.0f };
    static std::atomic<float> logicLoopFps    = { 0.0f };
    static std::atomic<float> graphicsLoopFps = { 0.0f };

    static std::atomic<size_t> graphicsCommandCount = { 0 };
    static std::atomic<size_t> graphicsDrawCallCount = { 0 };

    static std::atomic<size_t> eventManagerEventCount = { 0 };

    static char windowTitleTextBuffer[256] = {};

}

namespace ke
{
    ke::App * App::sGlobalAppInstance = nullptr;

    App::App()
    {
        assert(!App::sGlobalAppInstance);
        App::sGlobalAppInstance = this;
    }

    App::~App()
    {
        assert(App::sGlobalAppInstance);
        App::sGlobalAppInstance = nullptr;
    }

    int App::exec()
    {
        this->onBeforeInitialisation();

        this->initExec();
        KE_MAKE_SCOPEFUNC([this]() {
            this->cleanUpExec();
        });

        this->onPostInitialisation();

#if defined(USE_SDL)
        auto sdlInitResult = SDL_Init(SDL_INIT_VIDEO);
        if (sdlInitResult < 0)
        {
            Log::instance()->critical("SDL could not initialize! SDL_Error: {}", SDL_GetError());
            return sdlInitResult;
        }
        ke::Log::instance()->info("SDL initialisation successful.");
#endif

        Log::instance()->info("Creating application main window ...");
        this->mainWindow = ke::WindowFactory::newWindow();
        if (nullptr == mainWindow)
        {
#if defined(USE_SDL)
            Log::instance()->critical("SDL window could not be created. Error: {}", SDL_GetError());
#elif defined(USE_SFML)
            Log::instance()->critical("SFML window could not be created.");
#endif
            return ke::ExitCodes::FAILURE_WINDOW_CREATION;
        }
        this->mainWindow->setThreadCurrent(false);// disable window on this thread so can be made thread current on render thread.

        this->renderSystem = std::make_unique<ke::RenderSystem>();
        this->renderSystem->setWindow(this->mainWindow);

        // enter all the engine loops.
        this->enterLogicLoop();
        this->enterGraphicsLoop();
        this->enterEventLoop();

        this->onBeforeShutdown();

        ke::Log::instance()->info("Destroying main window ...");
        this->mainWindow.reset();

#if defined(USE_SDL)
        ke::Log::instance()->info("Shutting down SDL ...");
        SDL_Quit();
#endif

        this->onPostShutdown();

        return ke::ExitCodes::SUCCESS;
    }

    void App::enterEventLoop()
    {
        ke::Log::instance()->info("Entering KEngine event loop ...");

        this->isEventLoopRunning = true;
        ke::StopWatch stopwatch;
        ke::HeartBeatGenerator heartBeat(ke::Time::milliseconds(5000));
        ke::FrameRateCounter fpsCounter(1000);

        while(this->isEventLoopRunning)
        {
            const auto frameTime = stopwatch.getElapsed();
            stopwatch.restart();

            fpsCounter.push(frameTime);
            ::eventLoopFps.store((float)fpsCounter.getAverageFps(), std::memory_order_relaxed);

            ke::EventManager::enqueue(ke::makeEvent<EventLoopFrameEvent>(frameTime));

#if defined(USE_SDL)
            SDL_Event event;
            while (SDL_PollEvent(&event) != 0)
            {
                ke::EventManager::queue(ke::makeEvent<SDL2Event>(event));
                if (event.type == SDL_QUIT || event.type == SDL_WINDOWEVENT_CLOSE)
                {
                    Log::instance()->info("Normal exit requested.");
                    ke::EventManager::queue(ke::makeEvent<AppExitRequestedEvent>());
                }
            }
#elif defined(USE_SFML)
            sf::Event event;
            auto sfWindow = static_cast<sf::RenderWindow*>(this->mainWindow->get());
            assert(sfWindow);
            while (sfWindow->pollEvent(event))
            {
                ke::EventManager::enqueue(ke::makeEvent<ke::SfmlEvent>(event));
                switch (event.type)
                {
                case sf::Event::EventType::MouseButtonPressed:
                case sf::Event::EventType::MouseButtonReleased:
                case sf::Event::EventType::KeyPressed:
                case sf::Event::EventType::KeyReleased:
                case sf::Event::EventType::TextEntered:
                case sf::Event::EventType::Resized:
                {
                    auto keEvent = SfmlEventTranslator::translate(event, sfWindow);
                    if (keEvent)
                    {
                        ke::EventManager::enqueue(std::move(keEvent));
                    }
                    break;
                }

                case sf::Event::EventType::Closed:
                    Log::instance()->info("Normal exit requested.");
                    ke::EventManager::enqueue(ke::makeEvent<AppExitRequestedEvent>());
                    break;

                default: break;
                }
            }
#endif

            // set title with stats.
            const auto memO = std::memory_order_relaxed;
            using LLU = unsigned long long;
            std::snprintf(::windowTitleTextBuffer, sizeof(::windowTitleTextBuffer),
                "KEngine - FPS(%4.1f, %4.1f[%4.1f/ms], %4.1f[%4.1f/ms]), GraphicsCommands(%llu), DrawCalls(%llu), Events(%llu).",
                ::eventLoopFps.load(memO),
                ::logicLoopFps.load(memO), ::logicLoopFrameTimeMs.load(memO),
                ::graphicsLoopFps.load(memO), ::renderLoopFrameTimeMs.load(memO),
                (LLU)::graphicsCommandCount.load(memO), (LLU)::graphicsDrawCallCount.load(memO),
                (LLU)::eventManagerEventCount.load(memO));
            this->mainWindow->setTitle(::windowTitleTextBuffer);

            if (heartBeat)
            {
                ke::Log::instance()->info("Event loop heart beat");
            }

            std::this_thread::sleep_for(EVENT_THREAD_SLEEP_DURATION);
        }

        ke::Log::instance()->info("KEngine event loop exited.");

        // join render loop thread
        if (this->graphicsLoopThread.joinable())
        {
            ke::Log::instance()->info("KEngine graphics loop thread joining ...");
            this->graphicsLoopThread.join();
            ke::Log::instance()->info("KEngine graphics loop thread joined");
        }
        else
        {
            ke::Log::instance()->warn("KEngine logic loop thread not joinable.");
        }

        // join logic loop thread.
        if (this->logicLoopThread.joinable())
        {
            ke::Log::instance()->info("KEngine logic loop thread joining ...");
            this->logicLoopThread.join();
            ke::Log::instance()->info("KEngine logic loop thread joined");
        }
        else
        {
            ke::Log::instance()->warn("KEngine logic loop thread not joinable.");
        }

        this->mainWindow->setThreadCurrent(true);
        this->mainWindow.reset();
    }

    void App::enterLogicLoop()
    {
        this->logicLoopThread = std::thread([this]()
        {

            ke::EventManager::registerListener<ke::AppExitRequestedEvent>(this, &ke::App::handleAppExitRequest);
            ke::EventManager::registerListener<ke::GraphicsLoopSetupFailureEvent>(this, &ke::App::handleGraphicsLoopSetupFailure);

            ke::Log::instance()->info("Entering KEngine logic loop ...");

            ke::StopWatch stopwatch;
            ke::Time cumulativeLoopTime;
            ke::HeartBeatGenerator heartBeat(ke::Time::milliseconds(5000));
            ke::FrameRateCounter fpsCounter(1000);
            this->isLogicLoopRunning = true;

            while(this->isLogicLoopRunning)
            {
                const auto frameTime = stopwatch.getElapsed();
                stopwatch.restart();
                cumulativeLoopTime += frameTime;

                fpsCounter.push(frameTime);
                ::logicLoopFps.store((float)fpsCounter.getAverageFps(), std::memory_order_relaxed);
                ::logicLoopFrameTimeMs.store(frameTime.asNanoseconds()/1000000.0, std::memory_order_relaxed);
                ::eventManagerEventCount.store(ke::EventManager::instance()->getEventCount(), std::memory_order_relaxed);

//                if (frameTime < ::LOGIC_THREAD_TARGET_FRAMETIME)
//                {
//                    const auto difference = ::LOGIC_THREAD_TARGET_FRAMETIME - frameTime;
//                    std::this_thread::sleep_for(std::chrono::nanoseconds(difference.asNanoseconds()));
//                }
//                std::this_thread::sleep_for(std::chrono::milliseconds(::LOGIC_THREAD_TARGET_FRAMETIME.asMilliseconds()));

                //
                // update logic
                //
                while (cumulativeLoopTime >= ::LOGIC_UPDATE_FIXED_TIMESPAN)
                {
                    cumulativeLoopTime -= ::LOGIC_UPDATE_FIXED_TIMESPAN;

                    ke::EventManager::enqueue(ke::makeEvent<LogicLoopFrameEvent>(::LOGIC_UPDATE_FIXED_TIMESPAN));
                    ke::EventManager::update();

                    //
                    this->resourceManager->update();

                    ke::EventManager::update();

                    // update logic.
                    this->appLogic->update(::LOGIC_UPDATE_FIXED_TIMESPAN);
                }

                //
                // prepare and dispatch render commands
                //
                this->renderSystem->prepareCommands(this->appLogic->getCurrentHumanView()->getScene());
                auto cmdCount = this->renderSystem->dispatchCommands();
                ::graphicsCommandCount.store(cmdCount, std::memory_order_relaxed);

                //
                // debug/diagnostic stuff
                //
                if (heartBeat)
                {
                    ke::Log::instance()->info("Logic loop heart beat");
                }

                if (frameTime < ke::Time::milliseconds(1))
                    std::this_thread::sleep_for(LOGIC_THREAD_SLEEP_DURATION);
            }

            // we only flag event loop for termination here as some logics may still require the event loop to be alive.
            this->isEventLoopRunning = false;

            ke::Log::instance()->info("KEngine logic loop exited.");

            //
            // clean up.
            //
            this->getLogic()->getSystemManager()->clear();
        });
    }

    void App::enterGraphicsLoop()
    {
        this->graphicsLoopThread = std::thread([this]() {

            if (!this->mainWindow->setThreadCurrent(true))
            {
#if defined(USE_SDL)
                Log::instance()->critical("Failure enabling SDL2 window on thread {}. Cannot start graphics loop. SDL2 error: {}",
                    std::hash<std::thread::id>()(std::this_thread::get_id()),
                    SDL_GetError());
#elif defined(USE_SFML)
                Log::instance()->critical("Failure enabling SFML window on thread {}. Cannot start graphics loop.",
                    std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
                ke::EventManager::dispatchNow(ke::makeEvent<ke::GraphicsLoopSetupFailureEvent>());
                return;
            }

            if (!this->renderSystem->initialise())
            {
                Log::instance()->critical("Failuring initialising render system on thread {}.",
                    std::hash<std::thread::id>()(std::this_thread::get_id()));
                return;
            }

            mainWindow->display();

            ke::Log::instance()->info("Entering KEngine render loop ...");

            ke::StopWatch stopwatch;
            ke::HeartBeatGenerator heartBeat(ke::Time::milliseconds(5000));
            ke::FrameRateCounter fpsCounter(500);
            this->isGraphicsLoopRunning = true;

            while (this->isGraphicsLoopRunning)
            {
                const auto frameTime = stopwatch.getElapsed();
                stopwatch.restart();

                fpsCounter.push(frameTime);
                ::graphicsLoopFps.store((float)fpsCounter.getAverageFps(), std::memory_order_relaxed);
                ::renderLoopFrameTimeMs.store(frameTime.asMicroseconds()/1000.0, std::memory_order_relaxed);

                ke::EventManager::enqueue(ke::makeEvent<GraphicsLoopFrameEvent>(frameTime));

                if (heartBeat)
                {
                    ke::Log::instance()->info("Render loop heart beat");
                }

                this->renderSystem->updateOnRenderLoop(frameTime);

                // process the oldest render command list from the render commands queue
                // e.g. culling, ordering, etc...
                // if queue is empty then interpolate before render.
                this->renderSystem->processCommands(frameTime);
                const auto drawCallCount = this->renderSystem->render();
                ::graphicsDrawCallCount.store(drawCallCount > 0 ? drawCallCount : ::graphicsDrawCallCount.load(), std::memory_order_relaxed);

                if (frameTime < ke::Time::milliseconds(1))
                    std::this_thread::sleep_for(::RENDER_THREAD_SLEEP_DURATION);
            }

            this->renderSystem->shutdown();
            this->mainWindow->setThreadCurrent(false);

            ke::Log::instance()->info("KEngine render loop exited.");

        });
    }

    void App::setLogic(ke::AppLogicUptr && p_appLogic)
    {
        this->appLogic = std::move(p_appLogic);
    }

    void App::initExec()
    {
        spdlog::set_async_mode(1048576); // magic number from spdlog repo.

        ke::Log::instance()->info("Creating logic and views ...");
        this->createLogicAndViews();
        ke::Log::instance()->info("Creating logic and views ... DONE");

        assert(this->getLogic());
        assert(this->getLogic()->getCurrentHumanView());

        ke::Log::instance()->info("Creating resource manager ...");
        this->resourceManager = std::make_unique<ResourceManager>();
        ke::Log::instance()->info("Creating resource manager ... DONE");
    }

    void App::cleanUpExec()
    {
        spdlog::drop_all();
    }

    void App::handleAppExitRequest(ke::EventSptr)
    {
        if (isNormalExitRequested)
        {
            ke::Log::instance()->info("Already handling app exit request.");
            return;
        }
        isNormalExitRequested = true;
        ke::Log::instance()->info("Handling app exit request.");

        //this->isEventLoopRunning = false;
        this->isLogicLoopRunning = false;
        this->isGraphicsLoopRunning = false;
    }

    void App::handleGraphicsLoopSetupFailure(ke::EventSptr)
    {
        ke::Log::instance()->critical("Graphics loop setup failure encountered. Shuting down ...");
        this->isLogicLoopRunning = false;
        this->isGraphicsLoopRunning = false;
        this->isEventLoopRunning = false;
    }

}
<|endoftext|>


<commit_before>/**
 * @file bitstream.cpp
 * @author Robin Dietrich <me (at) invokr (dot) org>
 * @version 1.3
 *
 * @par License
 *    Alice Replay Parser
 *    Copyright 2014 Robin Dietrich
 *
 *    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 <alice/bitstream.hpp>

namespace dota {
    const uint64_t bitstream::masks[64] = {
        0x0,             0x1,              0x3,              0x7,
        0xf,             0x1f,             0x3f,             0x7f,
        0xff,            0x1ff,            0x3ff,            0x7ff,
        0xfff,           0x1fff,           0x3fff,           0x7fff,
        0xffff,          0x1ffff,          0x3ffff,          0x7ffff,
        0xfffff,         0x1fffff,         0x3fffff,         0x7fffff,
        0xffffff,        0x1ffffff,        0x3ffffff,        0x7ffffff,
        0xfffffff,       0x1fffffff,       0x3fffffff,       0x7fffffff,
        0xffffffff,      0x1ffffffff,      0x3ffffffff,      0x7ffffffff,
        0xfffffffff,     0x1fffffffff,     0x3fffffffff,     0x7fffffffff,
        0xffffffffff,    0x1ffffffffff,    0x3ffffffffff,    0x7ffffffffff,
        0xfffffffffff,   0x1fffffffffff,   0x3fffffffffff,   0x7fffffffffff,
        0xffffffffffff,  0x1ffffffffffff,  0x3ffffffffffff,  0x7ffffffffffff,
        0xfffffffffffff, 0x1fffffffffffff, 0x3fffffffffffff, 0x7fffffffffffff
    };

    const uint64_t bitstream::shift[64] = {
        0x0,  0x1,  0x2,  0x3,
        0x4,  0x5,  0x6,  0x7,
        0x8,  0x9,  0xa,  0xb,
        0xc,  0xd,  0xe,  0xf,
        0x10, 0x11, 0x12, 0x13,
        0x14, 0x15, 0x16, 0x17,
        0x18, 0x19, 0x1a, 0x1b,
        0x1c, 0x1d, 0x1e, 0x1f,
        0x20, 0x21, 0x22, 0x23,
        0x24, 0x25, 0x26, 0x27,
        0x28, 0x29, 0x2a, 0x2b,
        0x2c, 0x2d, 0x2e, 0x2f,
        0x30, 0x31, 0x32, 0x33,
        0x34, 0x35, 0x36, 0x37,
        0x38, 0x39, 0x3a, 0x3b,
        0x3c, 0x3d, 0x3e, 0x3f
    };

    uint32_t bitstream::read(const bitstream::size_type n) {
        // make sure the data read fits the return type
        if (n > size-pos) // cant read more than what is left over
            BOOST_THROW_EXCEPTION(bitstreamOverflow()
                << (EArgT<1, size_type>::info(n))
                << (EArgT<2, size_type>::info(size))
            );

        if (n > 32)
            BOOST_THROW_EXCEPTION(bitstreamOverflow()
                << (EArgT<1, size_type>::info(n))
                << (EArgT<2, size_type>::info(32))
            );

        const uint32_t bitSize = sizeof(word_t)*8;        // size of chunk in bits
        const uint32_t start   = pos / bitSize;           // current active chunk
        const uint32_t end     = (pos + n - 1) / bitSize; // next chunk if data needs to be wrapped
        const uint32_t s       = (pos % bitSize);         // shift amount
        uint32_t ret;                               // return value

        if (start == end) {
            ret = (data[start] >> shift[s]) & masks[n];
        } else { // wrap around
            ret = ((data[start] >> shift[s]) | (data[end] << (bitSize - shift[s]))) & masks[n];
        }

        pos += n;
        return ret;
    }

    uint32_t bitstream::nReadVarUInt32() {
        uint32_t readCount = 0;
        uint32_t value = 0;

        uint32_t tmpBuf;
        do {
            if (readCount == VARINT32_MAX) // return when maximum number of iterations is reached
                return value;

            tmpBuf = read(8);
            value |= (tmpBuf & 0x7F) << (7*readCount);
            ++readCount;
        } while (tmpBuf & 0x80);

        return value;
    }

    uint64_t bitstream::nReadVarUInt64() {
        uint32_t readCount = 0;
        uint64_t value = 0;

        uint32_t tmpBuf;
        do {
            if (readCount == VARINT64_MAX)
                return value;

            tmpBuf = read(8);
            value |= static_cast<uint64_t>(tmpBuf & 0x7F) << (7*readCount);
            ++readCount;
        } while (tmpBuf & 0x80);

        return value;
    }

    float bitstream::nReadCoord() {
        uint32_t intval   = read(1); // integer part
        uint32_t fractval = read(1); // fraction part

        if ( intval || fractval ) {
            float ret  = 0.0f; // return value
            const bool sign = read(1); // signed / unsigned flag

            if (intval)
                intval = read( COORD_INTEGER_BITS ) + 1;

            if (fractval)
                // shift from [0..MAX-1] to (1..MAX)
                fractval = read( COORD_FRACTION_BITS );

            ret = intval + (static_cast<float>(fractval) * COORD_RESOLUTION);

            if (sign)
                return -ret;
            else
                return ret;
        }

        D_( std::cout << "[bitstream] Returning default coord " << D_FILE << " " << __LINE__ << std::endl;, 1 )

        return 0.0f;
    }
    float bitstream::nReadCoordMp(const bool integral, const bool lowPrecision) {
        // Read flags depending on type
        const uint32_t flags = integral ? read(3) : read(2);
        const uint32_t flag_inbound = 1;
        const uint32_t flag_intval  = 2;
        const uint32_t flag_sign    = 4;

        if (integral) {
            if (flags & flag_intval) {
                const uint32_t toRead = (flags & flag_inbound) ? COORD_INTEGER_BITS_MP+1 : COORD_INTEGER_BITS+1;
                const uint32_t bits = read(toRead);

                // shift from [0..MAX-1] to [1..MAX], fist part of bits is the sign type
                const int intval = (bits >> 1) + 1;
                return (bits & 1) ? -intval : intval;
            }

            D_( std::cout << "[bitstream] Returning default mp-coord " << D_FILE << " " << __LINE__ << std::endl;, 1 )

            return 0.0f;
        }

        // multiplication table
        static const float mult[4] = {
             1.f / (1 << COORD_FRACTION_BITS),
            -1.f / (1 << COORD_FRACTION_BITS),
             1.f / (1 << COORD_FRACTION_BITS_MP_LOWPRECISION),
            -1.f / (1 << COORD_FRACTION_BITS_MP_LOWPRECISION)
        };

        // multiplication val
        const float multiply = mult[ (flags & flag_sign ? 1 : 0) + lowPrecision*2];

        // bits to read depending on type
        static const unsigned char bits[8] = {
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS_MP,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS_MP
        };

        uint32_t val = read( bits[ (flags & (flag_inbound|flag_intval)) + lowPrecision*4] );

        if (flags & flag_intval) {
            // Remap the integer part from [0,N] to [1,N+1] and paste
            //  it in front of the fractional parts.

            const uint32_t fracMp  = val >> COORD_INTEGER_BITS_MP;
            uint32_t frac    = val >> COORD_INTEGER_BITS;

            const uint32_t maskMp  = ((1<<COORD_INTEGER_BITS_MP)-1);
            uint32_t mask    = ((1<<COORD_INTEGER_BITS)-1);

            uint32_t selectNotMp = (flags & flag_inbound) - 1;

            frac -= fracMp;
            frac &= selectNotMp;
            frac += fracMp;

            mask -= maskMp;
            mask &= selectNotMp;
            mask += maskMp;

            const uint32_t intpart      = (val & mask) + 1;
            const uint32_t intbitsLow   = intpart << COORD_FRACTION_BITS_MP_LOWPRECISION;
            uint32_t intbits      = intpart << COORD_FRACTION_BITS;
            const uint32_t selectNotLow = static_cast<uint32_t>(lowPrecision) - 1;

            intbits -= intbitsLow;
            intbits &= selectNotLow;
            intbits += intbitsLow;

            val = frac | intbits;
        }

        return static_cast<int32_t>(val) * multiply;
    }

    void bitstream::nSkipCoordMp(const bool integral, const bool lowPrecision) {
        // Read flags depending on type
        const uint32_t flags = integral ? read(3) : read(2);
        const uint32_t flag_inbound = 1;
        const uint32_t flag_intval  = 2;

        if (integral && (flags & flag_intval)) {
            seekForward( (flags & flag_inbound) ? COORD_INTEGER_BITS_MP+1 : COORD_INTEGER_BITS+1 );
            return;
        }

        // bits to skip depending on type
        static const unsigned char bits[8] = {
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS_MP,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS_MP
        };

        seekForward( bits[ (flags & (flag_inbound|flag_intval)) + lowPrecision*4] );
    }

    float bitstream::nReadCellCoord(size_type n, const bool integral, const bool lowPrecision) {
        uint32_t val = read(n);

        if (integral) {
            if (val & 0x80)
                val += 4.2949673e9;

            return (float)val;
        } else {
            uint32_t fraction = lowPrecision ? read(CELL_COORD_FRACTION_BITS_LOWPRECISION) : read(CELL_COORD_FRACTION_BITS);
            float f = val + (lowPrecision ? 0.125 : 0.03125) * fraction;

            return f;
        }
    }

    void bitstream::nReadString(char *buffer, const bitstream::size_type n) {
        for (std::size_t i = 0; i < n; ++i) {
            buffer[i] = static_cast<char>(read(8));

            if (buffer[i] == '\0')
                return;
        }

        buffer[n - 1] = '\0';
    }

    void bitstream::readBits(char *buffer, const bitstream::size_type n) {
        size_type remaining = n;
        size_type i = 0;

        while (remaining >= 8) {
            buffer[i++] = read(8);
            remaining -= 8;
        }

        if (remaining > 0) {
            buffer[i++] = read(remaining);
        }
    }
}<commit_msg>Formatting<commit_after>/**
 * @file bitstream.cpp
 * @author Robin Dietrich <me (at) invokr (dot) org>
 * @version 1.3
 *
 * @par License
 *    Alice Replay Parser
 *    Copyright 2014 Robin Dietrich
 *
 *    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 <alice/bitstream.hpp>

namespace dota {
    const uint64_t bitstream::masks[64] = {
        0x0,             0x1,              0x3,              0x7,
        0xf,             0x1f,             0x3f,             0x7f,
        0xff,            0x1ff,            0x3ff,            0x7ff,
        0xfff,           0x1fff,           0x3fff,           0x7fff,
        0xffff,          0x1ffff,          0x3ffff,          0x7ffff,
        0xfffff,         0x1fffff,         0x3fffff,         0x7fffff,
        0xffffff,        0x1ffffff,        0x3ffffff,        0x7ffffff,
        0xfffffff,       0x1fffffff,       0x3fffffff,       0x7fffffff,
        0xffffffff,      0x1ffffffff,      0x3ffffffff,      0x7ffffffff,
        0xfffffffff,     0x1fffffffff,     0x3fffffffff,     0x7fffffffff,
        0xffffffffff,    0x1ffffffffff,    0x3ffffffffff,    0x7ffffffffff,
        0xfffffffffff,   0x1fffffffffff,   0x3fffffffffff,   0x7fffffffffff,
        0xffffffffffff,  0x1ffffffffffff,  0x3ffffffffffff,  0x7ffffffffffff,
        0xfffffffffffff, 0x1fffffffffffff, 0x3fffffffffffff, 0x7fffffffffffff
    };

    const uint64_t bitstream::shift[64] = {
        0x0,  0x1,  0x2,  0x3,
        0x4,  0x5,  0x6,  0x7,
        0x8,  0x9,  0xa,  0xb,
        0xc,  0xd,  0xe,  0xf,
        0x10, 0x11, 0x12, 0x13,
        0x14, 0x15, 0x16, 0x17,
        0x18, 0x19, 0x1a, 0x1b,
        0x1c, 0x1d, 0x1e, 0x1f,
        0x20, 0x21, 0x22, 0x23,
        0x24, 0x25, 0x26, 0x27,
        0x28, 0x29, 0x2a, 0x2b,
        0x2c, 0x2d, 0x2e, 0x2f,
        0x30, 0x31, 0x32, 0x33,
        0x34, 0x35, 0x36, 0x37,
        0x38, 0x39, 0x3a, 0x3b,
        0x3c, 0x3d, 0x3e, 0x3f
    };

    uint32_t bitstream::read(const bitstream::size_type n) {
        // make sure the data read fits the return type
        if (n > size-pos) // cant read more than what is left over
            BOOST_THROW_EXCEPTION(bitstreamOverflow()
                << (EArgT<1, size_type>::info(n))
                << (EArgT<2, size_type>::info(size))
            );

        if (n > 32)
            BOOST_THROW_EXCEPTION(bitstreamOverflow()
                << (EArgT<1, size_type>::info(n))
                << (EArgT<2, size_type>::info(32))
            );

        const uint32_t bitSize = sizeof(word_t)*8;        // size of chunk in bits
        const uint32_t start   = pos / bitSize;           // current active chunk
        const uint32_t end     = (pos + n - 1) / bitSize; // next chunk if data needs to be wrapped
        const uint32_t s       = (pos % bitSize);         // shift amount
        uint32_t ret;                                     // return value

        if (start == end) {
            ret = (data[start] >> shift[s]) & masks[n];
        } else { // wrap around
            ret = ((data[start] >> shift[s]) | (data[end] << (bitSize - shift[s]))) & masks[n];
        }

        pos += n;
        return ret;
    }

    uint32_t bitstream::nReadVarUInt32() {
        uint32_t readCount = 0;
        uint32_t value = 0;

        uint32_t tmpBuf;
        do {
            if (readCount == VARINT32_MAX) // return when maximum number of iterations is reached
                return value;

            tmpBuf = read(8);
            value |= (tmpBuf & 0x7F) << (7*readCount);
            ++readCount;
        } while (tmpBuf & 0x80);

        return value;
    }

    uint64_t bitstream::nReadVarUInt64() {
        uint32_t readCount = 0;
        uint64_t value = 0;

        uint32_t tmpBuf;
        do {
            if (readCount == VARINT64_MAX)
                return value;

            tmpBuf = read(8);
            value |= static_cast<uint64_t>(tmpBuf & 0x7F) << (7*readCount);
            ++readCount;
        } while (tmpBuf & 0x80);

        return value;
    }

    float bitstream::nReadCoord() {
        uint32_t intval   = read(1); // integer part
        uint32_t fractval = read(1); // fraction part

        if ( intval || fractval ) {
            float ret  = 0.0f; // return value
            const bool sign = read(1); // signed / unsigned flag

            if (intval)
                intval = read( COORD_INTEGER_BITS ) + 1;

            if (fractval)
                // shift from [0..MAX-1] to (1..MAX)
                fractval = read( COORD_FRACTION_BITS );

            ret = intval + (static_cast<float>(fractval) * COORD_RESOLUTION);

            if (sign)
                return -ret;
            else
                return ret;
        }

        D_( std::cout << "[bitstream] Returning default coord " << D_FILE << " " << __LINE__ << std::endl;, 1 )

        return 0.0f;
    }
    float bitstream::nReadCoordMp(const bool integral, const bool lowPrecision) {
        // Read flags depending on type
        const uint32_t flags = integral ? read(3) : read(2);
        const uint32_t flag_inbound = 1;
        const uint32_t flag_intval  = 2;
        const uint32_t flag_sign    = 4;

        if (integral) {
            if (flags & flag_intval) {
                const uint32_t toRead = (flags & flag_inbound) ? COORD_INTEGER_BITS_MP+1 : COORD_INTEGER_BITS+1;
                const uint32_t bits = read(toRead);

                // shift from [0..MAX-1] to [1..MAX], fist part of bits is the sign type
                const int intval = (bits >> 1) + 1;
                return (bits & 1) ? -intval : intval;
            }

            D_( std::cout << "[bitstream] Returning default mp-coord " << D_FILE << " " << __LINE__ << std::endl;, 1 )

            return 0.0f;
        }

        // multiplication table
        static const float mult[4] = {
             1.f / (1 << COORD_FRACTION_BITS),
            -1.f / (1 << COORD_FRACTION_BITS),
             1.f / (1 << COORD_FRACTION_BITS_MP_LOWPRECISION),
            -1.f / (1 << COORD_FRACTION_BITS_MP_LOWPRECISION)
        };

        // multiplication val
        const float multiply = mult[ (flags & flag_sign ? 1 : 0) + lowPrecision*2];

        // bits to read depending on type
        static const unsigned char bits[8] = {
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS_MP,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS_MP
        };

        uint32_t val = read( bits[ (flags & (flag_inbound|flag_intval)) + lowPrecision*4] );

        if (flags & flag_intval) {
            // Remap the integer part from [0,N] to [1,N+1] and paste
            //  it in front of the fractional parts.

            const uint32_t fracMp  = val >> COORD_INTEGER_BITS_MP;
            uint32_t frac          = val >> COORD_INTEGER_BITS;

            const uint32_t maskMp  = ((1<<COORD_INTEGER_BITS_MP)-1);
            uint32_t mask          = ((1<<COORD_INTEGER_BITS)-1);

            uint32_t selectNotMp = (flags & flag_inbound) - 1;

            frac -= fracMp;
            frac &= selectNotMp;
            frac += fracMp;

            mask -= maskMp;
            mask &= selectNotMp;
            mask += maskMp;

            const uint32_t intpart      = (val & mask) + 1;
            const uint32_t intbitsLow   = intpart << COORD_FRACTION_BITS_MP_LOWPRECISION;
            uint32_t intbits            = intpart << COORD_FRACTION_BITS;
            const uint32_t selectNotLow = static_cast<uint32_t>(lowPrecision) - 1;

            intbits -= intbitsLow;
            intbits &= selectNotLow;
            intbits += intbitsLow;

            val = frac | intbits;
        }

        return static_cast<int32_t>(val) * multiply;
    }

    void bitstream::nSkipCoordMp(const bool integral, const bool lowPrecision) {
        // Read flags depending on type
        const uint32_t flags = integral ? read(3) : read(2);
        const uint32_t flag_inbound = 1;
        const uint32_t flag_intval  = 2;

        if (integral && (flags & flag_intval)) {
            seekForward( (flags & flag_inbound) ? COORD_INTEGER_BITS_MP+1 : COORD_INTEGER_BITS+1 );
            return;
        }

        // bits to skip depending on type
        static const unsigned char bits[8] = {
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS + COORD_INTEGER_BITS_MP,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS,
            COORD_FRACTION_BITS_MP_LOWPRECISION + COORD_INTEGER_BITS_MP
        };

        seekForward( bits[ (flags & (flag_inbound|flag_intval)) + lowPrecision*4] );
    }

    float bitstream::nReadCellCoord(size_type n, const bool integral, const bool lowPrecision) {
        uint32_t val = read(n);

        if (integral) {
            if (val & 0x80)
                val += 4.2949673e9;

            return (float)val;
        } else {
            uint32_t fraction = lowPrecision ? read(CELL_COORD_FRACTION_BITS_LOWPRECISION) : read(CELL_COORD_FRACTION_BITS);
            float f = val + (lowPrecision ? 0.125 : 0.03125) * fraction;

            return f;
        }
    }

    void bitstream::nReadString(char *buffer, const bitstream::size_type n) {
        for (std::size_t i = 0; i < n; ++i) {
            buffer[i] = static_cast<char>(read(8));

            if (buffer[i] == '\0')
                return;
        }

        buffer[n - 1] = '\0';
    }

    void bitstream::readBits(char *buffer, const bitstream::size_type n) {
        size_type remaining = n;
        size_type i = 0;

        while (remaining >= 8) {
            buffer[i++] = read(8);
            remaining -= 8;
        }

        if (remaining > 0) {
            buffer[i++] = read(remaining);
        }
    }
}<|endoftext|>


<commit_before>/* Copyright (c) 2013,  Regents of the Columbia University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* Authors: Heming Cui (heming@cs.columbia.edu), Junfeng Yang (junfeng@cs.columbia.edu) -*- Mode: C++ -*- */
// Refactored from Heming's Memoizer code

#include "pthread.h"
#include "tern/runtime/scheduler.h"
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "tern/options.h"

using namespace std;
using namespace tern;

Serializer::~Serializer()
{
  if (options::log_sync)
    fclose(logger);
  if (options::light_log_sync)
    fclose(loggerLight);
}

Serializer::Serializer():
  TidMap(pthread_self()), turnCount(0)
{
  if (options::log_sync) {
    mkdir(options::output_dir.c_str(), 0777);
    std::string logPath = options::output_dir + "/serializer.log";
    logger = fopen(logPath.c_str(), "w");
    assert(logger);
  }
  if (options::light_log_sync) {
    std::string dir = "/dev/shm/" + options::output_dir;
    mkdir(dir.c_str(), 0777);
    char buf[1024] = {0};
    snprintf(buf, 1024, "%s/serializer-light-pid-%d.log", dir.c_str(), getpid());
    loggerLight = fopen(buf, "w");
    assert(loggerLight);
  }
}

unsigned Serializer::incTurnCount(const char *callerName, unsigned delta) {
  unsigned newCnt = turnCount + 1 + delta;
  turnCount = newCnt;
  if (options::log_sync)
    fprintf(logger, "%d %u %s\n", (int) self(), turnCount, callerName);
  if (options::light_log_sync && self() != IdleThreadTid) {
    fprintf(loggerLight, "%d %u %s      pid %d\n", self(), turnCount, callerName, getpid());
    fflush(loggerLight);
  }
  return turnCount;
}

unsigned Serializer::getTurnCount(void) {
  return turnCount - 1;
}

void Serializer::reopenLogs() {
  // Recreate the child process's light log file, do not mess up with parerent's.
  // Do not care about the log_sync, because the log file serializer.log is the same anyway.
  if (options::light_log_sync) {
    fclose(loggerLight);
    std::string dir = "/dev/shm/" + options::output_dir;
    mkdir(dir.c_str(), 0777);
    char buf[1024] = {0};
    snprintf(buf, 1024, "%s/serializer-light-pid-%d.log", dir.c_str(), getpid());
    loggerLight = fopen(buf, "w");
    assert(loggerLight);
  }
}

<commit_msg>added schedule logging for timebubble<commit_after>/* Copyright (c) 2013,  Regents of the Columbia University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* Authors: Heming Cui (heming@cs.columbia.edu), Junfeng Yang (junfeng@cs.columbia.edu) -*- Mode: C++ -*- */
// Refactored from Heming's Memoizer code

#include "pthread.h"
#include "tern/runtime/scheduler.h"
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "tern/options.h"

using namespace std;
using namespace tern;

Serializer::~Serializer()
{
  if (options::log_sync)
    fclose(logger);
  if (options::light_log_sync)
    fclose(loggerLight);
}

Serializer::Serializer():
  TidMap(pthread_self()), turnCount(0)
{
  if (options::log_sync) {
    mkdir(options::output_dir.c_str(), 0777);
    std::string logPath = options::output_dir + "/serializer.log";
    logger = fopen(logPath.c_str(), "w");
    assert(logger);
  }
  if (options::light_log_sync) {
    std::string dir = "/dev/shm/" + options::output_dir;
    mkdir(dir.c_str(), 0777);
    char buf[1024] = {0};
    snprintf(buf, 1024, "%s/serializer-light-pid-%d.log", dir.c_str(), getpid());
    loggerLight = fopen(buf, "w");
    assert(loggerLight);
  }
}

unsigned Serializer::incTurnCount(const char *callerName, unsigned delta) {
  unsigned newCnt = turnCount + 1 + delta;
  turnCount = newCnt;
  if (options::log_sync)
    fprintf(logger, "%d %u %s\n", (int) self(), turnCount, callerName);
  if (options::light_log_sync && self() != IdleThreadTid) {
    fprintf(loggerLight, "%d %u %s\n", self(), turnCount, callerName);
    fflush(loggerLight);
  }
  return turnCount;
}

unsigned Serializer::getTurnCount(void) {
  return turnCount - 1;
}

void Serializer::reopenLogs() {
  // Recreate the child process's light log file, do not mess up with parerent's.
  // Do not care about the log_sync, because the log file serializer.log is the same anyway.
  if (options::light_log_sync) {
    fclose(loggerLight);
    std::string dir = "/dev/shm/" + options::output_dir;
    mkdir(dir.c_str(), 0777);
    char buf[1024] = {0};
    snprintf(buf, 1024, "%s/serializer-light-pid-%d.log", dir.c_str(), getpid());
    loggerLight = fopen(buf, "w");
    assert(loggerLight);
  }
}

<|endoftext|>


<commit_before>// coding: utf-8
// ----------------------------------------------------------------------------
/* Copyright (c) 2013, Roboterclub Aachen e.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Roboterclub Aachen e.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ROBOTERCLUB AACHEN E.V. ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ROBOTERCLUB AACHEN E.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
// ----------------------------------------------------------------------------

#ifndef XPCC_PERIPHERAL__I2C_MASTER_HPP
#define XPCC_PERIPHERAL__I2C_MASTER_HPP

#include "../peripheral.hpp"

/**
 * @ingroup 	peripheral
 * @defgroup	i2c		Inter-Integrated Circuit (I2C)
 */

namespace xpcc
{
	/// @ingroup	i2c
	struct I2c
	{
		static constexpr uint8_t WRITE = 0x00;	///< Add the Write bit to the slave addres
		static constexpr uint8_t READ = 0x01;	///< Add the Read bit to the slave address

		/// This tells the `I2cDelegate` why it was detached
		/// @see I2cDelegate
		enum class DetachCause : uint8_t
		{
			NormalStop,		///< All operations finished normally
			ErrorCondition,	///< A bus error occured and the bus was reset
			SoftwareReset	///< The master in initializing itself
		};

		///@{
		/// I2C Operations that the `I2cDelegate` can give the master on callback
		/// @see I2cDelegate

		/// Operations after a start or restart condition.
		enum class Operation
		{
			Stop = 0,		///< Generate a Stop Condition
			Restart = 1,	///< Generate a Restart
			Write = 2,		///< Write data to the slave
			Read = 3,		///< Read data from the slave
		};

		/// Further operations after write operation.
		enum class OperationAfterWrite
		{
			Stop = 0,
			Restart = 1,
			Write = 2,
		};

		/// Further operations after read operation.
		enum class OperationAfterRead
		{
			Stop = 0,
			Restart = 1,
		};
		///@}

		/// State of a Delegate Adapter
		enum class AdapterState
		{
			Idle,	///< No error occured, detached normally
			Busy,	///< The adapter is busy with data transfer
			Error	///< An error occured, check the masters `getErrorCode()`
		};
	};

	/**
	 * Interface of a I2C master.
	 *
	 * Performing transfers in background allows the use of DMA etc. and
	 * enables the program to do other things while the operation is
	 * going on.
	 *
	 * @author	Georgi Grinshpun
	 * @author	Niklas Hauser
	 * @ingroup	peripheral
	 * @ingroup i2c
	 */
	class I2cMaster : public ::xpcc::Peripheral, public I2c
	{
	public:
		/// Errors that can happen during master operation
		enum class Error : uint8_t
		{
			NoError,			///< No Error occurred
			AddressNack,		///< Address was transmitted and NACK received
			DataNack,			///< Data was transmitted and NACK received
			ArbitrationLost,	///< Arbitration was lost during writing or reading
			BusCondition,		///< Misplaced Start or Stop condition
			Unknown				///< Unknown error condition
		};

		/// Baudrate of the I2C bus. Most slaves only work in Standard or Fast mode.
		enum class DataRate : uint32_t
		{
			Standard = 100000,	///< Standard datarate of 100kHz
			Fast = 400000,		///< Fast datarate of 400kHz
			High = 1700000		///< High datarate of 1.7Mhz
		};

#ifdef __DOXYGEN__
	public:
		/**
		 * Requests bus control and starts the transfer.
		 *
		 * @param	delegate
		 *		object that inherits from the I2cDelegate class.
		 * @return	Caller gains control if `true`. Call has no effect if `false`.
		 */
		static bool
		start(I2cDelegate *delegate);

		/**
		 * Requests bus control and starts the transfer, blocks until delegate is detached.
		 *
		 * @param	delegate
		 *		object that inherits from the I2cDelegate class.
		 * @return	Caller gains control if `true`. Call has no effect if `false`.
		 */
		static bool
		startSync(I2cDelegate *delegate);

		/**
		 * Perform a software reset of the driver in case of an error.
		 *
		 * This method calls the stopped Delegate method and then detaches
		 * the delegate.
		 */
		static void
		reset(DetachCause cause=DetachCause::SoftwareReset);

		/**
		 * Check the error state of the driver.
		 *
		 * Since the error states are hardware and implementation specific,
		 * this is only the recommended interface and does not need to be implemented.
		 */
		static Error
		getErrorState();
#endif
	};

	/**
	 * Abstract class for delegation
	 *
	 * For true asynchronous operation, the communication driver should
	 * inherit from this class, allowing multistage driver design and
	 * performance gain by premature data evaluation.
	 * Be aware the methods may or may not be called during the I2C
	 * interrupt even before returning from initiating function.
	 * This Delegate will stay attached to the `I2cMaster` during whole operation.
	 *
	 * @see I2cMaster
	 *
	 * @author	Georgi Grinshpun
	 * @author	Niklas Hauser
	 * @ingroup	i2c
	 */
	class I2cDelegate : public I2c
	{
	public:
		/// Contains the information required to begin an I2C transfer
		struct Starting {
			uint8_t address;	///< the slave address excluding read/write bit
			Operation next;		///< operation following the successful addressing of the slave
		};

		/// Contains the information required to begin a write operation
		struct Writing {
			OperationAfterWrite next;	///< operation following the successful writing to the slave
			const uint8_t *buffer;		///< buffer containing the data to write to the slave
			std::size_t size;			///< number of bytes to be written
		};

		/// Contains the information required to begin a read operation
		struct Reading {
			OperationAfterRead next;	///< operation following the reading
			uint8_t *buffer;			///< buffer to be filled with the bytes received from the slave
			std::size_t size;			///< number of bytes to be read
		};

	public:
		/*
		 * @brief	This method is called when the I2cMaster is not currently
		 *			in use by another delegate and can be attached.
		 *
		 * @return	`true` if the I2cMaster should attach this delegate,
		 * 			`false` if it should not attach it.
		 */
		virtual bool
		attaching() = 0;

		/*
		 * @brief	This is called when the I2cMaster is ready to (re-)start
		 * 			an operation.
		 *
		 * @return	the `Starting` struct containing slave address and the next operation
		 */
		virtual Starting
		starting() = 0;

		/**
		 * @brief This is called before the I2cMaster begins a write operation.
		 *
		 * @return	the `Writing` struct containg the write buffer and size and next operation
		 */
		virtual Writing
		writing() = 0;

		/**
		 * @brief	This is called before the I2cMaster begins a read operation.
		 *
		 * @return	the `Reading` struct containg the read buffer and size and next operation
		 */
		virtual Reading
		reading() = 0;

		/**
		 * @brief	This is called when the I2cMaster stops the operation and detached the delegate.
		 *
		 * @param	cause	specifies whether the detachment was expected (`NormalStop`),
		 * 					or a error occurred (`ErrorCondition`), which can, but does not need
		 * 					to be reacted upon.
		 */
		virtual void
		stopped(DetachCause cause) = 0;
	};
}

#endif // XPCC_PERIPHERAL__I2C_MASTER_HPP
<commit_msg>Adapt I2C interface to new coding style.<commit_after>// coding: utf-8
// ----------------------------------------------------------------------------
/* Copyright (c) 2013, Roboterclub Aachen e.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Roboterclub Aachen e.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ROBOTERCLUB AACHEN E.V. ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ROBOTERCLUB AACHEN E.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
// ----------------------------------------------------------------------------

#ifndef XPCC_PERIPHERAL__I2C_MASTER_HPP
#define XPCC_PERIPHERAL__I2C_MASTER_HPP

#include "../peripheral.hpp"

/**
 * @ingroup 	peripheral
 * @defgroup	i2c		Inter-Integrated Circuit (I2C)
 */

namespace xpcc
{
	/// @ingroup	i2c
	struct I2c
	{
		static constexpr uint8_t WRITE = 0x00;	///< Add the Write bit to the slave addres
		static constexpr uint8_t READ  = 0x01;	///< Add the Read bit to the slave address

		/// This tells the `I2cDelegate` why it was detached
		/// @see I2cDelegate
		enum class
		DetachCause : uint8_t
		{
			NormalStop,		///< All operations finished normally
			ErrorCondition,	///< A bus error occured and the bus was reset
			SoftwareReset	///< The master is initializing itself
		};

		///@{
		/// I2C Operations that the `I2cDelegate` can give the master on callback
		/// @see I2cDelegate

		/// Operations after a start or restart condition.
		enum class
		Operation
		{
			Stop = 0,		///< Generate a Stop Condition
			Restart = 1,	///< Generate a Restart
			Write = 2,		///< Write data to the slave
			Read = 3,		///< Read data from the slave
		};

		/// Further operations after write operation.
		enum class
		OperationAfterWrite
		{
			Stop = 0,
			Restart = 1,
			Write = 2,
		};

		/// Further operations after read operation.
		enum class
		OperationAfterRead
		{
			Stop = 0,
			Restart = 1,
		};
		///@}

		/// State of a Delegate Adapter
		enum class
		AdapterState
		{
			Idle,	///< No error occured, detached normally
			Busy,	///< The adapter is busy with data transfer
			Error	///< An error occured, check the masters `getErrorCode()`
		};
	};

	/**
	 * Interface of a I2C master.
	 *
	 * Performing transfers in background allows the use of DMA etc. and
	 * enables the program to do other things while the operation is
	 * going on.
	 *
	 * @author	Georgi Grinshpun
	 * @author	Niklas Hauser
	 * @ingroup	peripheral
	 * @ingroup i2c
	 */
	class I2cMaster : public ::xpcc::Peripheral, public I2c
	{
	public:
		/// Errors that can happen during master operation
		enum class
		Error : uint8_t
		{
			NoError,			///< No Error occurred
			AddressNack,		///< Address was transmitted and NACK received
			DataNack,			///< Data was transmitted and NACK received
			ArbitrationLost,	///< Arbitration was lost during writing or reading
			BusCondition,		///< Misplaced Start or Stop condition
			Unknown				///< Unknown error condition
		};

		/// Baudrate of the I2C bus. Most slaves only work in Standard or Fast mode.
		enum class
		DataRate : uint32_t
		{
			Standard = 100000,	///< Standard datarate of 100kHz
			Fast = 400000,		///< Fast datarate of 400kHz
			High = 1700000		///< High datarate of 1.7Mhz
		};

#ifdef __DOXYGEN__
	public:
		/**
		 * Requests bus control and starts the transfer.
		 *
		 * @param	delegate
		 *		object that inherits from the I2cDelegate class.
		 * @return	Caller gains control if `true`. Call has no effect if `false`.
		 */
		static bool
		start(I2cDelegate *delegate);

		/**
		 * Requests bus control and starts the transfer, blocks until delegate is detached.
		 *
		 * @param	delegate
		 *		object that inherits from the I2cDelegate class.
		 * @return	Caller gains control if `true`. Call has no effect if `false`.
		 */
		static bool
		startBlocking(I2cDelegate *delegate);

		/**
		 * Perform a software reset of the driver in case of an error.
		 *
		 * This method calls the stopped Delegate method and then detaches
		 * the delegate.
		 */
		static void
		reset(DetachCause cause = DetachCause::SoftwareReset);

		/**
		 * Check the error state of the driver.
		 *
		 * Since the error states are hardware and implementation specific,
		 * this is only the recommended interface and does not need to be implemented.
		 */
		static Error
		getErrorState();
#endif
	};

	/**
	 * Abstract class for delegation.
	 *
	 * For true asynchronous operation, the communication driver should
	 * inherit from this class, allowing multistage driver design and
	 * performance gain by premature data evaluation.
	 * Be aware the methods may or may not be called during the I2C
	 * interrupt even before returning from initiating function.
	 * This Delegate will stay attached to the `I2cMaster` during whole operation.
	 *
	 * @see I2cMaster
	 *
	 * @author	Georgi Grinshpun
	 * @author	Niklas Hauser
	 * @ingroup	i2c
	 */
	class I2cDelegate : public I2c
	{
	public:
		/// Contains the information required to begin an I2C transfer
		struct Starting {
			uint8_t address;	///< the slave address excluding read/write bit
			Operation next;		///< operation following the successful addressing of the slave
		};

		/// Contains the information required to begin a write operation
		struct Writing {
			OperationAfterWrite next;	///< operation following the successful writing to the slave
			const uint8_t *buffer;		///< buffer containing the data to write to the slave
			std::size_t size;			///< number of bytes to be written
		};

		/// Contains the information required to begin a read operation
		struct Reading {
			OperationAfterRead next;	///< operation following the reading
			uint8_t *buffer;			///< buffer to be filled with the bytes received from the slave
			std::size_t size;			///< number of bytes to be read
		};

	public:
		/**
		 * This method is called when the I2cMaster is not currently
		 * in use by another delegate and can be attached.
		 *
		 * @return	`true` if the I2cMaster should attach this delegate,
		 * 			`false` if it should not attach it.
		 */
		virtual bool
		attaching() = 0;

		/**
		 * This is called when the I2cMaster is ready to (re-)start an operation.
		 *
		 * @return	the `Starting` struct containing slave address and the next operation
		 */
		virtual Starting
		starting() = 0;

		/**
		 * This is called before the I2cMaster begins a write operation.
		 *
		 * @return	the `Writing` struct containg the write buffer and size and next operation
		 */
		virtual Writing
		writing() = 0;

		/**
		 * This is called before the I2cMaster begins a read operation.
		 *
		 * @return	the `Reading` struct containg the read buffer and size and next operation
		 */
		virtual Reading
		reading() = 0;

		/**
		 * This is called when the I2cMaster stops the operation and detached the delegate.
		 *
		 * @param	cause	specifies whether the detachment was expected (`NormalStop`),
		 * 					or a error occurred (`ErrorCondition`), which can, but does not need
		 * 					to be reacted upon.
		 */
		virtual void
		stopped(DetachCause cause) = 0;
	};

}

#endif // XPCC_PERIPHERAL__I2C_MASTER_HPP
<|endoftext|>