<commit_before>#include "TensorRTEngine.hpp"
#include "NvInfer.h"
#include "NvUffParser.h"
#include "NvOnnxParser.h"
#include "NvUtils.h"
#include <cuda_runtime_api.h>
#include <FAST/Utility.hpp>
#include <fstream>
#include <FAST/Config.hpp>

#define CUDA_CHECK(status)                             \
    do                                            \
    {                                             \
        auto ret = (status);                      \
        if (ret != 0)                             \
        {                                         \
            std::cout << "Cuda failure: " << ret; \
            abort();                              \
        }                                         \
    } while (0)

namespace fast {

/**
 * Logger object for TensorRT
 */
class Logger : public nvinfer1::ILogger {
    void log(Severity severity, const char* msg) override {
        switch(severity) {
            case Severity::kINFO:
                Reporter::info() << msg << Reporter::end();
                break;
            case Severity::kERROR:
            case Severity::kINTERNAL_ERROR:
                Reporter::error() << msg << Reporter::end();
                break;
            case Severity::kWARNING:
                Reporter::warning() << msg << Reporter::end();
                break;
        }
    }
} gLogger;

// Call destroy when an object is deleted
struct Destroy {
    template <typename T>
    void operator()(T* obj) const {
        if (obj)
            obj->destroy();
    }
};

inline unsigned int elementSize(nvinfer1::DataType t) {
    switch (t)
    {
        case nvinfer1::DataType::kINT32:
            // Fallthrough, same as kFLOAT
        case nvinfer1::DataType::kFLOAT: return 4;
        case nvinfer1::DataType::kHALF: return 2;
        case nvinfer1::DataType::kINT8: return 1;
    }
    assert(0);
    return 0;
}

inline int64_t volume(const nvinfer1::Dims& d) {
    int64_t v = 1;
    for (int64_t i = 0; i < d.nbDims; i++)
        v *= d.d[i];
    return v;
}

static std::vector<std::pair<int64_t, nvinfer1::DataType>>
calculateBindingBufferSizes(const nvinfer1::ICudaEngine& engine, int nbBindings, int batchSize) {
    std::vector<std::pair<int64_t, nvinfer1::DataType>> sizes;
    for (int i = 0; i < nbBindings; ++i)
    {
        nvinfer1::Dims dims = engine.getBindingDimensions(i);
        nvinfer1::DataType dtype = engine.getBindingDataType(i);

        int64_t eltCount = volume(dims) * batchSize;
        sizes.push_back(std::make_pair(eltCount, dtype));
    }

    return sizes;
}

void* safeCudaMalloc(size_t memSize)
{
    void* deviceMem;
    CUDA_CHECK(cudaMalloc(&deviceMem, memSize));
    if (deviceMem == nullptr)
    {
        std::cerr << "Out of memory" << std::endl;
        exit(1);
    }
    return deviceMem;
}

void TensorRTEngine::run() {

    const int nbBindings = m_engine->getNbBindings();

    // Get batch size
    const int batchSize = mInputNodes.begin()->second.data->getShape()[0];
    if(batchSize > m_engine->getMaxBatchSize()) {
        reportWarning() << "Batch is larger than the max batch size given to TensorRT" << reportEnd();
    }

    std::vector<void*> buffers(nbBindings);
    auto buffersSizes = calculateBindingBufferSizes(*m_engine, nbBindings, batchSize);

    // TODO assuming 1 input and output here
    std::map<std::string, int> inputIndexes;
    std::map<std::string, int> outputIndexes;
    for(int i = 0; i < nbBindings; ++i) {
        auto name = m_engine->getBindingName(i);
        if (m_engine->bindingIsInput(i)) {
            inputIndexes[name] = i;
        } else {
            outputIndexes[name] = i;
        }
        // Allocate data
        buffers[i] = safeCudaMalloc(buffersSizes[i].first * elementSize(buffersSizes[i].second));
    }

    // Allocate data for each input and copy data to it
    for(const auto& inputNode : mInputNodes) {
        auto tensor = inputNode.second.data;
        auto access = tensor->getAccess(ACCESS_READ);
        float* tensorData = access->getRawData();
        const int index = inputIndexes.at(inputNode.first);
        CUDA_CHECK(cudaMemcpy(buffers[index], tensorData,
                              buffersSizes[index].first * elementSize(buffersSizes[index].second),
                              cudaMemcpyHostToDevice));
        reportInfo() << "Finished copying input data to TensorRT" << reportEnd();
    }

    // Execute network
    bool success = m_context->executeV2(buffers.data());
    //bool success = m_context->execute(batchSize, &buffers[0]);
    if(!success)
        throw Exception("TensorRT execute inference failed!");
    reportInfo() << "Finished execute TensorRT" << reportEnd();

    // Free input memory buffers
    for(const auto& input : inputIndexes)
        CUDA_CHECK(cudaFree(buffers[input.second]));
    reportInfo() << "Finished freeing input data TensorRT" << reportEnd();

    // Transfer output data back
    for(const auto& output : outputIndexes) {
        const int index = output.second;
        reportInfo() << "Processing output node " << output.first << reportEnd();
        auto outputData = make_uninitialized_unique<float[]>(buffersSizes[index].first);
        CUDA_CHECK(cudaMemcpy(outputData.get(), buffers[index],
                              buffersSizes[index].first * elementSize(buffersSizes[index].second),
                              cudaMemcpyDeviceToHost));

        auto outputTensor = Tensor::New();
        mOutputNodes.at(output.first).data = outputTensor;

        // Get output shape
        nvinfer1::Dims dims = m_engine->getBindingDimensions(index);
        TensorShape shape = mOutputNodes.at(output.first).shape;
        if(shape.getDimensions() == 0)
            throw Exception("Missing shape for output node");
        shape[0] = batchSize;
        // TODO check if output dims are correct:
        //for(int i = 0; i < dims.nbDims; ++i) {
        //}

        outputTensor->create(std::move(outputData), shape);
        reportInfo() << "Finished moving data to FAST tensor, TensorRT" << reportEnd();
        reportInfo() << "Finished transfer of output data TensorRT" << reportEnd();
        CUDA_CHECK(cudaFree(buffers[index]));
        reportInfo() << "Finished freeing output data TensorRT" << reportEnd();
    }
}

static TensorShape getTensorShape(nvinfer1::Dims dims) {
    TensorShape shape;
    for(int j = 0; j < dims.nbDims; ++j) {
        auto size = dims.d[j];
        shape.addDimension(size);
    }
    return shape;
}

void TensorRTEngine::load() {

    const auto filename = getFilename();
    std::size_t hash = std::hash<std::string>{}(filename + std::to_string(m_maxBatchSize)); // Hash the full filename any other parameters
    std::string serializedBinaryFilename = join(Config::getKernelBinaryPath(), filename.substr(filename.rfind('/')) + "_" + std::to_string(hash) + ".bin");
    std::string serializedCacheFilename = join(Config::getKernelBinaryPath(), filename.substr(filename.rfind('/')) + "_" + std::to_string(hash) + ".cache");
    bool loadSerializedFile = false;
    if(fileExists(serializedCacheFilename) && fileExists(serializedBinaryFilename)) {
        // Check if date is modified or not
        std::ifstream file(serializedCacheFilename.c_str());
        if(file.fail())
            throw Exception("Failed to read " + serializedCacheFilename);
        std::string line = "";
        std::getline(file, line);
        trim(line);
        std::string modifiedDate = getModifiedDate(filename);
        trim(modifiedDate);
        if(line == modifiedDate) {
            reportInfo() << "Serialized file " << serializedBinaryFilename << " is up to date." << reportEnd();
            loadSerializedFile = true;
        } else {
            reportInfo() << "Serialized file " << serializedBinaryFilename << " was not up to date." << reportEnd();
        }
    }
    if(!loadSerializedFile) {
        // File is not serialized: Create it
        if(!fileExists(filename))
            throw FileNotFoundException(filename);
        reportInfo() << "Loading file " << filename << " using TensorRT" << reportEnd();
        std::unique_ptr<nvinfer1::IBuilder, decltype(Destroy())> builder(nvinfer1::createInferBuilder(gLogger),
                                                                         Destroy());
        const auto flags = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
        std::unique_ptr<nvinfer1::INetworkDefinition, decltype(Destroy())> network(builder->createNetworkV2(flags), Destroy());

        if(filename.substr(filename.size()-4) == ".uff") {
            // Check that input and output nodes are set
            if(mInputNodes.empty() || mOutputNodes.empty())
                throw Exception("Input and output nodes must be defined before loading Uff files using the TensorRT engine");
            reportInfo() << "Assuming file is in UFF format, parsing..." << reportEnd();
            std::unique_ptr<nvuffparser::IUffParser, decltype(Destroy())> parser(nvuffparser::createUffParser(),
                                                                                Destroy());

            // Setup input nodes
            reportInfo() << "Going through nodes.." << reportEnd();
            for (auto node : mInputNodes) {
                reportInfo() << node.first << reportEnd();
                auto shape = node.second.shape;
                if (shape.getDimensions() == 0)
                    throw Exception("Unknown shape for input node " + node.first +
                                    ". For TensorRT you need to specify the input tensor shape explictly with addInputNode");
                if (shape.getDimensions() == 4)
                    parser->registerInput(node.first.c_str(), nvinfer1::Dims3(shape[1], shape[2], shape[3]),
                                          nvuffparser::UffInputOrder::kNCHW);
                if (shape.getDimensions() == 5)
                    parser->registerInput(node.first.c_str(), nvinfer1::Dims4(shape[1], shape[2], shape[3], shape[4]),
                                          nvuffparser::UffInputOrder::kNCHW);
            }
            reportInfo() << "Input nodes finished" << reportEnd();

            // Setup output nodes
            for (auto node : mOutputNodes) {
                parser->registerOutput(node.first.c_str());
            }
            reportInfo() << "Output nodes finished" << reportEnd();

            if (!parser->parse(filename.c_str(), *network, nvinfer1::DataType::kFLOAT))
                throw Exception("Error parsing UFF file " + filename);

            reportInfo() << "Finished parsing UFF file" << reportEnd();
        } else {
            // Assuming file is ONNX format
            reportInfo() << "Assuming file is in ONNX format, parsing..." << reportEnd();
            std::unique_ptr<nvonnxparser::IParser, decltype(Destroy())> parser(nvonnxparser::createParser(*network, gLogger),
                                                                                Destroy());
            bool parsed = parser->parseFromFile(filename.c_str(), 1);
            if(!parsed)
                throw Exception("Unable to parse ONNX file with TensorRT");
        }

        builder->setMaxBatchSize(m_maxBatchSize);
        //builder->setFp16Mode(builder->platformHasFastFp16());

        std::unique_ptr<nvinfer1::IBuilderConfig, decltype(Destroy())> config(builder->createBuilderConfig(), Destroy());
        config->setMaxWorkspaceSize(m_maxWorkspaceSize);

        m_engine = builder->buildEngineWithConfig(*network, *config);
        if(!m_engine)
            throw Exception("Failed to build CUDA engine for TensorRT");
        reportInfo() << "Finished building CUDA engine for TensorRT" << reportEnd();

        // Serialize the model
        std::unique_ptr<nvinfer1::IHostMemory, decltype(Destroy())> serializedModel(m_engine->serialize(), Destroy());
        // Store model to disk
        std::ofstream ofile(serializedBinaryFilename.c_str(), std::ios::binary);
        ofile.write((char *) serializedModel->data(), serializedModel->size());
        ofile.close();
        std::ofstream ofileCache(serializedCacheFilename.c_str());
        std::string modifiedDate = getModifiedDate(filename);
        ofileCache.write(modifiedDate.c_str(), modifiedDate.size());
        ofileCache.close();
    } else {
        std::unique_ptr<nvinfer1::IRuntime, decltype(Destroy())> runtime(nvinfer1::createInferRuntime(gLogger), Destroy());
        // Read serialized model from disk
        std::ifstream ifile(serializedBinaryFilename.c_str(), std::ios::binary);
        std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(ifile), {});
        ifile.close();
        // Deserialize the model data
        m_engine = runtime->deserializeCudaEngine(buffer.data(), buffer.size(), nullptr);
    }

    // Get input and output nodes from the CUDA engine
    int inputCount = 0;
    int outputCount = 0;
    for(int i = 0; i < m_engine->getNbBindings(); ++i) {
        auto name = m_engine->getBindingName(i);
        auto shape = getTensorShape(m_engine->getBindingDimensions(i));
        NodeType type = NodeType::IMAGE;
        if(shape.getDimensions() < 4)
            type = NodeType::TENSOR;
        if (m_engine->bindingIsInput(i)) {
            reportInfo() << "Found input node " << name << " with shape " << shape.toString() << reportEnd();
            addInputNode(inputCount, name, type, shape);
            ++inputCount;
        } else {
            reportInfo() << "Found output node " << name << " with shape " << shape.toString() << reportEnd();
            addOutputNode(outputCount, name, type, shape);
            ++outputCount;
        }
    }

    m_context = m_engine->createExecutionContext();
    setIsLoaded(true);
}

ImageOrdering TensorRTEngine::getPreferredImageOrdering() const {
    return ImageOrdering::ChannelFirst;
}

TensorRTEngine::~TensorRTEngine() {
    if(m_engine != nullptr)
        m_engine->destroy();
    //if(m_context != nullptr)
    //    m_context->destroy();
    //nvuffparser::shutdownProtobufLibrary(); // This cannot be called twice in the same program. Maybe use atexit instead?
}

TensorRTEngine::TensorRTEngine() {

}

std::string TensorRTEngine::getName() const {
    return "TensorRT";
}

void TensorRTEngine::setMaxBatchSize(int maxBatchSize) {
    if(maxBatchSize <= 0)
        throw Exception("Max batch size must be > 0");
    m_maxBatchSize = maxBatchSize;
}

int TensorRTEngine::getMaxBatchSize() const {
    return m_maxBatchSize;
}

std::string TensorRTEngine::getDefaultFileExtension() const {
    return "uff";
}

}
<commit_msg>Try to fix TensorRTEngine, still not working<commit_after>#include "TensorRTEngine.hpp"
#include "NvInfer.h"
#include "NvUffParser.h"
#include "NvOnnxParser.h"
#include "NvUtils.h"
#include <cuda_runtime_api.h>
#include <FAST/Utility.hpp>
#include <fstream>
#include <FAST/Config.hpp>

#define CUDA_CHECK(status)                             \
    do                                            \
    {                                             \
        auto ret = (status);                      \
        if (ret != 0)                             \
        {                                         \
            std::cout << "Cuda failure: " << ret; \
            abort();                              \
        }                                         \
    } while (0)

namespace fast {

/**
 * Logger object for TensorRT
 */
class Logger : public nvinfer1::ILogger {
    void log(Severity severity, const char* msg) override {
        switch(severity) {
            case Severity::kINFO:
                Reporter::info() << msg << Reporter::end();
                break;
            case Severity::kERROR:
            case Severity::kINTERNAL_ERROR:
                Reporter::error() << msg << Reporter::end();
                break;
            case Severity::kWARNING:
                Reporter::warning() << msg << Reporter::end();
                break;
        }
    }
} gLogger;

// Call destroy when an object is deleted
struct Destroy {
    template <typename T>
    void operator()(T* obj) const {
        if (obj)
            obj->destroy();
    }
};

inline unsigned int elementSize(nvinfer1::DataType t) {
    switch (t)
    {
        case nvinfer1::DataType::kINT32:
            // Fallthrough, same as kFLOAT
        case nvinfer1::DataType::kFLOAT: return 4;
        case nvinfer1::DataType::kHALF: return 2;
        case nvinfer1::DataType::kINT8: return 1;
    }
    assert(0);
    return 0;
}

inline int64_t volume(const nvinfer1::Dims& d) {
    int64_t v = 1;
    for (int64_t i = 0; i < d.nbDims; i++)
        v *= d.d[i];
    return v;
}

static std::vector<std::pair<int64_t, nvinfer1::DataType>>
calculateBindingBufferSizes(const nvinfer1::ICudaEngine& engine, int nbBindings, int batchSize) {
    std::vector<std::pair<int64_t, nvinfer1::DataType>> sizes;
    for (int i = 0; i < nbBindings; ++i)
    {
        nvinfer1::Dims dims = engine.getBindingDimensions(i);
        nvinfer1::DataType dtype = engine.getBindingDataType(i);

        int64_t eltCount = volume(dims) * batchSize;
        sizes.push_back(std::make_pair(eltCount, dtype));
    }

    return sizes;
}

void* safeCudaMalloc(size_t memSize)
{
    void* deviceMem;
    CUDA_CHECK(cudaMalloc(&deviceMem, memSize));
    if (deviceMem == nullptr)
    {
        std::cerr << "Out of memory" << std::endl;
        exit(1);
    }
    return deviceMem;
}

void TensorRTEngine::run() {

    const int nbBindings = m_engine->getNbBindings();

    // Get batch size
    const int batchSize = mInputNodes.begin()->second.data->getShape()[0];
    if(batchSize > m_engine->getMaxBatchSize()) {
        reportWarning() << "Batch is larger than the max batch size given to TensorRT" << reportEnd();
    }

    std::vector<void*> buffers(nbBindings);
    auto buffersSizes = calculateBindingBufferSizes(*m_engine, nbBindings, batchSize);

    // TODO assuming 1 input and output here
    std::map<std::string, int> inputIndexes;
    std::map<std::string, int> outputIndexes;
    for(int i = 0; i < nbBindings; ++i) {
        auto name = m_engine->getBindingName(i);
        if (m_engine->bindingIsInput(i)) {
            inputIndexes[name] = i;
        } else {
            outputIndexes[name] = i;
        }
        // Allocate data
        buffers[i] = safeCudaMalloc(buffersSizes[i].first * elementSize(buffersSizes[i].second));
    }

    // Allocate data for each input and copy data to it
    for(const auto& inputNode : mInputNodes) {
        auto tensor = inputNode.second.data;
        auto access = tensor->getAccess(ACCESS_READ);
        float* tensorData = access->getRawData();
        const int index = inputIndexes.at(inputNode.first);
        CUDA_CHECK(cudaMemcpy(buffers[index], tensorData,
                              buffersSizes[index].first * elementSize(buffersSizes[index].second),
                              cudaMemcpyHostToDevice));
        reportInfo() << "Finished copying input data to TensorRT" << reportEnd();
    }

    // Execute network
    bool success = m_context->executeV2(buffers.data());
    //bool success = m_context->execute(batchSize, &buffers[0]);
    if(!success)
        throw Exception("TensorRT execute inference failed!");
    reportInfo() << "Finished execute TensorRT" << reportEnd();

    // Free input memory buffers
    for(const auto& input : inputIndexes)
        CUDA_CHECK(cudaFree(buffers[input.second]));
    reportInfo() << "Finished freeing input data TensorRT" << reportEnd();

    // Transfer output data back
    for(const auto& output : outputIndexes) {
        const int index = output.second;
        reportInfo() << "Processing output node " << output.first << reportEnd();
        auto outputData = make_uninitialized_unique<float[]>(buffersSizes[index].first);
        CUDA_CHECK(cudaMemcpy(outputData.get(), buffers[index],
                              buffersSizes[index].first * elementSize(buffersSizes[index].second),
                              cudaMemcpyDeviceToHost));

        auto outputTensor = Tensor::New();
        mOutputNodes.at(output.first).data = outputTensor;

        // Get output shape
        nvinfer1::Dims dims = m_engine->getBindingDimensions(index);
        TensorShape shape = mOutputNodes.at(output.first).shape;
        if(shape.getDimensions() == 0)
            throw Exception("Missing shape for output node");
        shape[0] = batchSize;
        // TODO check if output dims are correct:
        //for(int i = 0; i < dims.nbDims; ++i) {
        //}

        outputTensor->create(std::move(outputData), shape);
        reportInfo() << "Finished moving data to FAST tensor, TensorRT" << reportEnd();
        reportInfo() << "Finished transfer of output data TensorRT" << reportEnd();
        CUDA_CHECK(cudaFree(buffers[index]));
        reportInfo() << "Finished freeing output data TensorRT" << reportEnd();
    }
}

static TensorShape getTensorShape(nvinfer1::Dims dims) {
    TensorShape shape;
    for(int j = 0; j < dims.nbDims; ++j) {
        auto size = dims.d[j];
        shape.addDimension(size);
    }
    return shape;
}

void TensorRTEngine::load() {

    const auto filename = getFilename();
    std::size_t hash = std::hash<std::string>{}(filename + std::to_string(m_maxBatchSize)); // Hash the full filename any other parameters
    std::string serializedBinaryFilename = join(Config::getKernelBinaryPath(), filename.substr(filename.rfind('/')) + "_" + std::to_string(hash) + ".bin");
    std::string serializedCacheFilename = join(Config::getKernelBinaryPath(), filename.substr(filename.rfind('/')) + "_" + std::to_string(hash) + ".cache");
    bool loadSerializedFile = false;
    if(fileExists(serializedCacheFilename) && fileExists(serializedBinaryFilename)) {
        // Check if date is modified or not
        std::ifstream file(serializedCacheFilename.c_str());
        if(file.fail())
            throw Exception("Failed to read " + serializedCacheFilename);
        std::string line = "";
        std::getline(file, line);
        trim(line);
        std::string modifiedDate = getModifiedDate(filename);
        trim(modifiedDate);
        if(line == modifiedDate) {
            reportInfo() << "Serialized file " << serializedBinaryFilename << " is up to date." << reportEnd();
            loadSerializedFile = true;
        } else {
            reportInfo() << "Serialized file " << serializedBinaryFilename << " was not up to date." << reportEnd();
        }
    }
    if(!loadSerializedFile) {
        // File is not serialized: Create it
        if(!fileExists(filename))
            throw FileNotFoundException(filename);
        reportInfo() << "Loading file " << filename << " using TensorRT" << reportEnd();
        std::unique_ptr<nvinfer1::IBuilder, decltype(Destroy())> builder(nvinfer1::createInferBuilder(gLogger),
                                                                         Destroy());
        const auto flags = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
        std::unique_ptr<nvinfer1::INetworkDefinition, decltype(Destroy())> network;

        if(filename.substr(filename.size()-4) == ".uff") {
            network = {builder->createNetwork(), Destroy()}; // createNetworkV2 not working with UFF for some reason..
            // Check that input and output nodes are set
            if(mInputNodes.empty() || mOutputNodes.empty())
                throw Exception("Input and output nodes must be defined before loading Uff files using the TensorRT engine");
            reportInfo() << "Assuming file is in UFF format, parsing..." << reportEnd();
            std::unique_ptr<nvuffparser::IUffParser, decltype(Destroy())> parser(nvuffparser::createUffParser(),
                                                                                Destroy());

            // Setup input nodes
            reportInfo() << "Going through nodes.." << reportEnd();
            for (auto node : mInputNodes) {
                reportInfo() << node.first << reportEnd();
                auto shape = node.second.shape;
                if (shape.getDimensions() == 0)
                    throw Exception("Unknown shape for input node " + node.first +
                                    ". For TensorRT you need to specify the input tensor shape explictly with addInputNode");
                if (shape.getDimensions() == 4)
                    parser->registerInput(node.first.c_str(), nvinfer1::Dims3(shape[1], shape[2], shape[3]),
                                          nvuffparser::UffInputOrder::kNCHW);
                if (shape.getDimensions() == 5)
                    throw Exception("More than 4 dimensions input is not supported");
            }
            reportInfo() << "Input nodes finished" << reportEnd();

            // Setup output nodes
            for (auto node : mOutputNodes) {
                parser->registerOutput(node.first.c_str());
            }
            reportInfo() << "Output nodes finished" << reportEnd();

            if (!parser->parse(filename.c_str(), *network, nvinfer1::DataType::kFLOAT))
                throw Exception("Error parsing UFF file " + filename);

            reportInfo() << "Finished parsing UFF file" << reportEnd();
        } else {
            network = {builder->createNetworkV2(flags), Destroy()};
            // Assuming file is ONNX format
            reportInfo() << "Assuming file is in ONNX format, parsing..." << reportEnd();
            std::unique_ptr<nvonnxparser::IParser, decltype(Destroy())> parser(nvonnxparser::createParser(*network, gLogger),
                                                                                Destroy());
            bool parsed = parser->parseFromFile(filename.c_str(), 1);
            if(!parsed)
                throw Exception("Unable to parse ONNX file with TensorRT");
        }

        builder->setMaxBatchSize(m_maxBatchSize);
        //builder->setFp16Mode(builder->platformHasFastFp16());

        std::unique_ptr<nvinfer1::IBuilderConfig, decltype(Destroy())> config(builder->createBuilderConfig(), Destroy());
        config->setMaxWorkspaceSize(m_maxWorkspaceSize);

        m_engine = builder->buildEngineWithConfig(*network, *config);
        if(!m_engine)
            throw Exception("Failed to build CUDA engine for TensorRT");
        reportInfo() << "Finished building CUDA engine for TensorRT" << reportEnd();

        // Serialize the model
        std::unique_ptr<nvinfer1::IHostMemory, decltype(Destroy())> serializedModel(m_engine->serialize(), Destroy());
        // Store model to disk
        std::ofstream ofile(serializedBinaryFilename.c_str(), std::ios::binary);
        ofile.write((char *) serializedModel->data(), serializedModel->size());
        ofile.close();
        std::ofstream ofileCache(serializedCacheFilename.c_str());
        std::string modifiedDate = getModifiedDate(filename);
        ofileCache.write(modifiedDate.c_str(), modifiedDate.size());
        ofileCache.close();
    } else {
        std::unique_ptr<nvinfer1::IRuntime, decltype(Destroy())> runtime(nvinfer1::createInferRuntime(gLogger), Destroy());
        // Read serialized model from disk
        std::ifstream ifile(serializedBinaryFilename.c_str(), std::ios::binary);
        std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(ifile), {});
        ifile.close();
        // Deserialize the model data
        m_engine = runtime->deserializeCudaEngine(buffer.data(), buffer.size(), nullptr);
    }

    // Get input and output nodes from the CUDA engine
    if(filename.substr(filename.size()-4) != ".uff") {
        int inputCount = 0;
        int outputCount = 0;
        for (int i = 0; i < m_engine->getNbBindings(); ++i) {
            auto name = m_engine->getBindingName(i);
            auto shape = getTensorShape(m_engine->getBindingDimensions(i));
            NodeType type = NodeType::IMAGE;
            if (shape.getDimensions() < 4)
                type = NodeType::TENSOR;
            if (m_engine->bindingIsInput(i)) {
                reportInfo() << "Found input node " << name << " with shape " << shape.toString() << reportEnd();
                addInputNode(inputCount, name, type, shape);
                ++inputCount;
            } else {
                reportInfo() << "Found output node " << name << " with shape " << shape.toString() << reportEnd();
                addOutputNode(outputCount, name, type, shape);
                ++outputCount;
            }
        }
    }

    m_context = m_engine->createExecutionContext();
    setIsLoaded(true);
}

ImageOrdering TensorRTEngine::getPreferredImageOrdering() const {
    return ImageOrdering::ChannelFirst;
}

TensorRTEngine::~TensorRTEngine() {
    if(m_engine != nullptr)
        m_engine->destroy();
    //if(m_context != nullptr)
    //    m_context->destroy();
    //nvuffparser::shutdownProtobufLibrary(); // This cannot be called twice in the same program. Maybe use atexit instead?
}

TensorRTEngine::TensorRTEngine() {

}

std::string TensorRTEngine::getName() const {
    return "TensorRT";
}

void TensorRTEngine::setMaxBatchSize(int maxBatchSize) {
    if(maxBatchSize <= 0)
        throw Exception("Max batch size must be > 0");
    m_maxBatchSize = maxBatchSize;
}

int TensorRTEngine::getMaxBatchSize() const {
    return m_maxBatchSize;
}

std::string TensorRTEngine::getDefaultFileExtension() const {
    return "uff";
}

}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "schemas.h"

using namespace cm;
using namespace fnord;

namespace cm {

msg::MessageSchema joinedSessionsSchema() {
  Vector<msg::MessageSchemaField> fields;

  msg::MessageSchemaField queries(
      16,
      "queries",
      msg::FieldType::OBJECT,
      0,
      true,
      false);

  queries.fields.emplace_back(
      18,
      "time",
      msg::FieldType::UINT32,
      0xffffffff,
      false,
      false);

  queries.fields.emplace_back(
      1,
      "page",
      msg::FieldType::UINT32,
      100,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      2,
      "language",
      msg::FieldType::UINT32,
      kMaxLanguage,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      3,
      "query_string",
      msg::FieldType::STRING,
      8192,
      false,
      true);

  queries.fields.emplace_back(
      4,
      "query_string_normalized",
      msg::FieldType::STRING,
      8192,
      false,
      true);

  queries.fields.emplace_back(
      5,
      "num_items",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      6,
      "num_items_clicked",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      7,
      "num_ad_impressions",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      8,
      "num_ad_clicks",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      9,
      "ab_test_group",
      msg::FieldType::UINT32,
      100,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      10,
      "device_type",
      msg::FieldType::UINT32,
      kMaxDeviceType,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      11,
      "page_type",
      msg::FieldType::UINT32,
      kMaxPageType,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      12,
      "category1",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      13,
      "category2",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      14,
      "category3",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::BITPACK);

  msg::MessageSchemaField query_items(
      17,
      "items",
      msg::FieldType::OBJECT,
      0,
      true,
      false);

  query_items.fields.emplace_back(
      19,
      "position",
      msg::FieldType::UINT32,
      64,
      false,
      false,
      msg::EncodingHint::BITPACK);

  query_items.fields.emplace_back(
      15,
      "clicked",
      msg::FieldType::BOOLEAN,
      0,
      false,
      false);

  query_items.fields.emplace_back(
      20,
      "item_id",
      msg::FieldType::STRING,
      1024,
      false,
      false);

  query_items.fields.emplace_back(
      21,
      "shop_id",
      msg::FieldType::UINT32,
      0xffffffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  query_items.fields.emplace_back(
      22,
      "category1",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::BITPACK);

  query_items.fields.emplace_back(
      23,
      "category2",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::BITPACK);

  query_items.fields.emplace_back(
      24,
      "category3",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(query_items);
  fields.emplace_back(queries);

  return msg::MessageSchema("joined_session", fields);
}

}
<commit_msg>encode a bunch of fields als LEB128...<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "schemas.h"

using namespace cm;
using namespace fnord;

namespace cm {

msg::MessageSchema joinedSessionsSchema() {
  Vector<msg::MessageSchemaField> fields;

  msg::MessageSchemaField queries(
      16,
      "queries",
      msg::FieldType::OBJECT,
      0,
      true,
      false);

  queries.fields.emplace_back(
      18,
      "time",
      msg::FieldType::UINT32,
      0xffffffff,
      false,
      false);

  queries.fields.emplace_back(
      1,
      "page",
      msg::FieldType::UINT32,
      100,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      2,
      "language",
      msg::FieldType::UINT32,
      kMaxLanguage,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      3,
      "query_string",
      msg::FieldType::STRING,
      8192,
      false,
      true);

  queries.fields.emplace_back(
      4,
      "query_string_normalized",
      msg::FieldType::STRING,
      8192,
      false,
      true);

  queries.fields.emplace_back(
      5,
      "num_items",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      6,
      "num_items_clicked",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      7,
      "num_ad_impressions",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      8,
      "num_ad_clicks",
      msg::FieldType::UINT32,
      250,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      9,
      "ab_test_group",
      msg::FieldType::UINT32,
      100,
      false,
      true,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      10,
      "device_type",
      msg::FieldType::UINT32,
      kMaxDeviceType,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      11,
      "page_type",
      msg::FieldType::UINT32,
      kMaxPageType,
      false,
      false,
      msg::EncodingHint::BITPACK);

  queries.fields.emplace_back(
      12,
      "category1",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  queries.fields.emplace_back(
      13,
      "category2",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  queries.fields.emplace_back(
      14,
      "category3",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  msg::MessageSchemaField query_items(
      17,
      "items",
      msg::FieldType::OBJECT,
      0,
      true,
      false);

  query_items.fields.emplace_back(
      19,
      "position",
      msg::FieldType::UINT32,
      64,
      false,
      false,
      msg::EncodingHint::BITPACK);

  query_items.fields.emplace_back(
      15,
      "clicked",
      msg::FieldType::BOOLEAN,
      0,
      false,
      false);

  query_items.fields.emplace_back(
      20,
      "item_id",
      msg::FieldType::STRING,
      1024,
      false,
      false);

  query_items.fields.emplace_back(
      21,
      "shop_id",
      msg::FieldType::UINT32,
      0xffffffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  query_items.fields.emplace_back(
      22,
      "category1",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  query_items.fields.emplace_back(
      23,
      "category2",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  query_items.fields.emplace_back(
      24,
      "category3",
      msg::FieldType::UINT32,
      0xffff,
      false,
      true,
      msg::EncodingHint::LEB128);

  queries.fields.emplace_back(query_items);
  fields.emplace_back(queries);

  return msg::MessageSchema("joined_session", fields);
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/logging.h"
#include "base/mime_util.h"
#include "net/base/platform_mime_util.h"

namespace net {

bool PlatformMimeUtil::GetPlatformMimeTypeFromExtension(
    const FilePath::StringType& ext, std::string* result) const {
  FilePath::StringType dummy_path = "foo." + ext;
  std::string out = mime_util::GetFileMimeType(dummy_path);

  // GetFileMimeType likes to return application/octet-stream
  // for everything it doesn't know - ignore that.
  if (out == "application/octet-stream" || !out.length())
    return false;

  // GetFileMimeType returns image/x-ico because that's what's in the XDG
  // mime database. That database is the merger of the Gnome and KDE mime
  // databases. Apparently someone working on KDE in 2001 decided .ico
  // resolves to image/x-ico, whereas the rest of the world uses image/x-icon.
  // FWIW, image/vnd.microsoft.icon is the official IANA assignment.
  if (out == "image/x-ico")
    out = "image/x-icon";

  *result = out;
  return true;
}

bool PlatformMimeUtil::GetPreferredExtensionForMimeType(
    const std::string& mime_type, FilePath::StringType* ext) const {
  // Unlike GetPlatformMimeTypeFromExtension, this method doesn't have a
  // default list that it uses, but for now we are also returning false since
  // this doesn't really matter as much under Linux.
  //
  // If we wanted to do this properly, we would read the mime.cache file which
  // has a section where they assign a glob (*.gif) to a mimetype
  // (image/gif). We look up the "heaviest" glob for a certain mime type and
  // then then try to chop off "*.".

  NOTIMPLEMENTED();
  return false;
}

}  // namespace net
<commit_msg>Add a temporary hack to not resolve the mime type for .pl files to fix a layout test.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/mime_util.h"
#include "net/base/platform_mime_util.h"

namespace net {

bool PlatformMimeUtil::GetPlatformMimeTypeFromExtension(
    const FilePath::StringType& ext, std::string* result) const {
  // TODO(thestig) This is a temporary hack until we can fix this
  // properly in test shell / webkit.
  // We have to play dumb and not return application/x-perl here
  // to make the reload-subframe-object layout test happy.
  if (ext == "pl")
    return false;

  FilePath::StringType dummy_path = "foo." + ext;
  std::string out = mime_util::GetFileMimeType(dummy_path);

  // GetFileMimeType likes to return application/octet-stream
  // for everything it doesn't know - ignore that.
  if (out == "application/octet-stream" || !out.length())
    return false;

  // GetFileMimeType returns image/x-ico because that's what's in the XDG
  // mime database. That database is the merger of the Gnome and KDE mime
  // databases. Apparently someone working on KDE in 2001 decided .ico
  // resolves to image/x-ico, whereas the rest of the world uses image/x-icon.
  // FWIW, image/vnd.microsoft.icon is the official IANA assignment.
  if (out == "image/x-ico")
    out = "image/x-icon";

  *result = out;
  return true;
}

bool PlatformMimeUtil::GetPreferredExtensionForMimeType(
    const std::string& mime_type, FilePath::StringType* ext) const {
  // Unlike GetPlatformMimeTypeFromExtension, this method doesn't have a
  // default list that it uses, but for now we are also returning false since
  // this doesn't really matter as much under Linux.
  //
  // If we wanted to do this properly, we would read the mime.cache file which
  // has a section where they assign a glob (*.gif) to a mimetype
  // (image/gif). We look up the "heaviest" glob for a certain mime type and
  // then then try to chop off "*.".

  return false;
}

}  // namespace net
<|endoftext|>


<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you 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.

*******************************************************************************/

//==============================================================================
// Raw CellML view widget
//==============================================================================

#include "borderedwidget.h"
#include "guiutils.h"
#include "qscintillawidget.h"
#include "rawcellmlviewwidget.h"
#include "viewerwidget.h"

//==============================================================================

#include "ui_rawcellmlviewwidget.h"

//==============================================================================

#include <QDesktopWidget>
#include <QFileInfo>
#include <QSettings>
#include <QSplitter>
#include <QTextStream>

//==============================================================================

#include "Qsci/qscilexerxml.h"

//==============================================================================

namespace OpenCOR {
namespace RawCellMLView {

//==============================================================================

RawCellmlViewWidget::RawCellmlViewWidget(QWidget *pParent) :
    QSplitter(pParent),
    CommonWidget(pParent),
    mGui(new Ui::RawCellmlViewWidget),
    mBorderedEditor(0),
    mBorderedEditors(QMap<QString, Core::BorderedWidget *>()),
    mBorderedViewerHeight(0),
    mBorderedEditorHeight(0)
{
    // Set up the GUI

    mGui->setupUi(this);

    // Keep track of our sizes when moving the splitter

    connect(this, SIGNAL(splitterMoved(int,int)),
            this, SLOT(splitterMoved()));

    // Create our viewer

    mViewer = new Viewer::ViewerWidget(this);
    mBorderedViewer = new Core::BorderedWidget(mViewer,
                                               false, false, true, false);

    // Add the bordered viewer to ourselves

    addWidget(mBorderedViewer);
}

//==============================================================================

RawCellmlViewWidget::~RawCellmlViewWidget()
{
    // Delete the GUI

    delete mGui;
}

//==============================================================================

static const QString SettingsViewerHeight = "ViewerHeight";
static const QString SettingsEditorHeight = "EditorHeight";

//==============================================================================

void RawCellmlViewWidget::loadSettings(QSettings *pSettings)
{
    // Retrieve the viewer's and editor's height
    // Note #1: the viewer's default height is 19% of the desktop's height while
    //          that of the editor is as big as it can be...
    // Note #2: because the editor's default height is much bigger than that of
    //          our raw CellML view widget, the viewer's default height will
    //          effectively be less than 19% of the desktop's height, but that
    //          doesn't matter at all...

    mBorderedViewerHeight = pSettings->value(SettingsViewerHeight,
                                             0.19*qApp->desktop()->screenGeometry().height()).toInt();
    mBorderedEditorHeight = pSettings->value(SettingsEditorHeight,
                                             qApp->desktop()->screenGeometry().height()).toInt();
}

//==============================================================================

void RawCellmlViewWidget::saveSettings(QSettings *pSettings) const
{
    // Keep track of the viewer's and editor's height
    // Note #1: we must also keep track of the editor's height because when
    //          loading our settings (see above), the widget doesn't yet have a
    //          'proper' height, so we couldn't simply assume that the editor's
    //          initial height is this widget's height minus the viewer's
    //          initial height, so...
    // Note #2: we rely on mBorderedViewerHeight and mBorderedEditorHeight
    //          rather than directly calling the height() method of the viewer
    //          and of the editor, respectively since it may happen that the
    //          user exits OpenCOR without ever having switched to the raw
    //          CellML view, in which case we couldn't retrieve the viewer and
    //          editor's height which in turn would result in OpenCOR crashing,
    //          so...

    pSettings->setValue(SettingsViewerHeight, mBorderedViewerHeight);
    pSettings->setValue(SettingsEditorHeight, mBorderedEditorHeight);
}

//==============================================================================

void RawCellmlViewWidget::initialize(const QString &pFileName)
{
    // Retrieve the editor associated with the file name, if any

    mBorderedEditor = mBorderedEditors.value(pFileName);

    if (!mBorderedEditor) {
        // No editor exists for the file name, so create and set up a Scintilla
        // editor with an XML lexer associated with it

        QFile file(pFileName);
        QString fileContents = QString();
        bool fileIsReadOnly = false;

        if (file.open(QIODevice::ReadOnly|QIODevice::Text)) {
            // We could open the file, so retrieve its contents and whether it
            // can be written to

            fileContents = QTextStream(&file).readAll();
            fileIsReadOnly = !(QFileInfo(pFileName).isWritable());

            // We are done with the file, so close it

            file.close();
        }

        mBorderedEditor = new Core::BorderedWidget(new QScintillaSupport::QScintillaWidget(fileContents,
                                                                                fileIsReadOnly,
                                                                                new QsciLexerXML(this),
                                                                                parentWidget()),
                                                   true, false, false, false);

        // Keep track of our bordered editor and add it to ourselves

        mBorderedEditors.insert(pFileName, mBorderedEditor);

        addWidget(mBorderedEditor);
    }

    // Show/hide our bordered editors and adjust our sizes

    QList<int> newSizes = QList<int>() << mBorderedViewerHeight;

    for (int i = 1, iMax = count(); i < iMax; ++i) {
        Core::BorderedWidget *borderedEditor = static_cast<Core::BorderedWidget *>(widget(i));

        if (borderedEditor == mBorderedEditor) {
            // This is the editor we are after, so show it and set its size

            borderedEditor->show();

            newSizes << mBorderedEditorHeight;
        } else {
            // Not the editor we are after, so hide it and set its size
            // Note: theoretically speaking, we could set its size to whatever
            //       value we want since it's anyway hidden...

            borderedEditor->hide();

            newSizes << 0;
        }
    }

    setSizes(newSizes);

    // Set the raw CellML view widget's focus proxy to our 'new' editor and
    // make sure that it immediately gets the focus
    // Note: if we were not to immediately give our 'new' editor the focus,
    //       then the central widget would give the focus to our 'old' editor
    //       (see CentralWidget::updateGui()), so...

    setFocusProxy(mBorderedEditor->widget());

    mBorderedEditor->widget()->setFocus();
}

//==============================================================================

bool RawCellmlViewWidget::isManaged(const QString &pFileName) const
{
    // Return whether the given file name is managed

    return mBorderedEditors.value(pFileName);
}

//==============================================================================

void RawCellmlViewWidget::finalize(const QString &pFileName)
{
    // Remove the bordered editor, should there be one for the given file name

    Core::BorderedWidget *borderedEditor  = mBorderedEditors.value(pFileName);

    if (borderedEditor) {
        // There is a bordered editor for the given file name, so delete it and
        // remove it from our list

        delete borderedEditor;

        mBorderedEditors.remove(pFileName);

        // Reset our memory of the current bordered editor

        mBorderedEditor = 0;
    }
}

//==============================================================================

void RawCellmlViewWidget::fileReloaded(const QString &pFileName)
{
    // The given file has been reloaded, so reload it, should it be managed

    if (isManaged(pFileName)) {
        finalize(pFileName);
        initialize(pFileName);
    }
}

//==============================================================================

void RawCellmlViewWidget::splitterMoved()
{
    // The splitter has moved, so keep track of the viewer and editor's new
    // height

    mBorderedViewerHeight = mBorderedViewer->height();
    mBorderedEditorHeight = mBorderedEditor->height();
}

//==============================================================================

}   // namespace RawCellMLView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Replaced a couple of 'static const QString' to 'static const auto'.<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you 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.

*******************************************************************************/

//==============================================================================
// Raw CellML view widget
//==============================================================================

#include "borderedwidget.h"
#include "guiutils.h"
#include "qscintillawidget.h"
#include "rawcellmlviewwidget.h"
#include "viewerwidget.h"

//==============================================================================

#include "ui_rawcellmlviewwidget.h"

//==============================================================================

#include <QDesktopWidget>
#include <QFileInfo>
#include <QSettings>
#include <QSplitter>
#include <QTextStream>

//==============================================================================

#include "Qsci/qscilexerxml.h"

//==============================================================================

namespace OpenCOR {
namespace RawCellMLView {

//==============================================================================

RawCellmlViewWidget::RawCellmlViewWidget(QWidget *pParent) :
    QSplitter(pParent),
    CommonWidget(pParent),
    mGui(new Ui::RawCellmlViewWidget),
    mBorderedEditor(0),
    mBorderedEditors(QMap<QString, Core::BorderedWidget *>()),
    mBorderedViewerHeight(0),
    mBorderedEditorHeight(0)
{
    // Set up the GUI

    mGui->setupUi(this);

    // Keep track of our sizes when moving the splitter

    connect(this, SIGNAL(splitterMoved(int,int)),
            this, SLOT(splitterMoved()));

    // Create our viewer

    mViewer = new Viewer::ViewerWidget(this);
    mBorderedViewer = new Core::BorderedWidget(mViewer,
                                               false, false, true, false);

    // Add the bordered viewer to ourselves

    addWidget(mBorderedViewer);
}

//==============================================================================

RawCellmlViewWidget::~RawCellmlViewWidget()
{
    // Delete the GUI

    delete mGui;
}

//==============================================================================

static const auto SettingsViewerHeight = QStringLiteral("ViewerHeight");
static const auto SettingsEditorHeight = QStringLiteral("EditorHeight");

//==============================================================================

void RawCellmlViewWidget::loadSettings(QSettings *pSettings)
{
    // Retrieve the viewer's and editor's height
    // Note #1: the viewer's default height is 19% of the desktop's height while
    //          that of the editor is as big as it can be...
    // Note #2: because the editor's default height is much bigger than that of
    //          our raw CellML view widget, the viewer's default height will
    //          effectively be less than 19% of the desktop's height, but that
    //          doesn't matter at all...

    mBorderedViewerHeight = pSettings->value(SettingsViewerHeight,
                                             0.19*qApp->desktop()->screenGeometry().height()).toInt();
    mBorderedEditorHeight = pSettings->value(SettingsEditorHeight,
                                             qApp->desktop()->screenGeometry().height()).toInt();
}

//==============================================================================

void RawCellmlViewWidget::saveSettings(QSettings *pSettings) const
{
    // Keep track of the viewer's and editor's height
    // Note #1: we must also keep track of the editor's height because when
    //          loading our settings (see above), the widget doesn't yet have a
    //          'proper' height, so we couldn't simply assume that the editor's
    //          initial height is this widget's height minus the viewer's
    //          initial height, so...
    // Note #2: we rely on mBorderedViewerHeight and mBorderedEditorHeight
    //          rather than directly calling the height() method of the viewer
    //          and of the editor, respectively since it may happen that the
    //          user exits OpenCOR without ever having switched to the raw
    //          CellML view, in which case we couldn't retrieve the viewer and
    //          editor's height which in turn would result in OpenCOR crashing,
    //          so...

    pSettings->setValue(SettingsViewerHeight, mBorderedViewerHeight);
    pSettings->setValue(SettingsEditorHeight, mBorderedEditorHeight);
}

//==============================================================================

void RawCellmlViewWidget::initialize(const QString &pFileName)
{
    // Retrieve the editor associated with the file name, if any

    mBorderedEditor = mBorderedEditors.value(pFileName);

    if (!mBorderedEditor) {
        // No editor exists for the file name, so create and set up a Scintilla
        // editor with an XML lexer associated with it

        QFile file(pFileName);
        QString fileContents = QString();
        bool fileIsReadOnly = false;

        if (file.open(QIODevice::ReadOnly|QIODevice::Text)) {
            // We could open the file, so retrieve its contents and whether it
            // can be written to

            fileContents = QTextStream(&file).readAll();
            fileIsReadOnly = !(QFileInfo(pFileName).isWritable());

            // We are done with the file, so close it

            file.close();
        }

        mBorderedEditor = new Core::BorderedWidget(new QScintillaSupport::QScintillaWidget(fileContents,
                                                                                fileIsReadOnly,
                                                                                new QsciLexerXML(this),
                                                                                parentWidget()),
                                                   true, false, false, false);

        // Keep track of our bordered editor and add it to ourselves

        mBorderedEditors.insert(pFileName, mBorderedEditor);

        addWidget(mBorderedEditor);
    }

    // Show/hide our bordered editors and adjust our sizes

    QList<int> newSizes = QList<int>() << mBorderedViewerHeight;

    for (int i = 1, iMax = count(); i < iMax; ++i) {
        Core::BorderedWidget *borderedEditor = static_cast<Core::BorderedWidget *>(widget(i));

        if (borderedEditor == mBorderedEditor) {
            // This is the editor we are after, so show it and set its size

            borderedEditor->show();

            newSizes << mBorderedEditorHeight;
        } else {
            // Not the editor we are after, so hide it and set its size
            // Note: theoretically speaking, we could set its size to whatever
            //       value we want since it's anyway hidden...

            borderedEditor->hide();

            newSizes << 0;
        }
    }

    setSizes(newSizes);

    // Set the raw CellML view widget's focus proxy to our 'new' editor and
    // make sure that it immediately gets the focus
    // Note: if we were not to immediately give our 'new' editor the focus,
    //       then the central widget would give the focus to our 'old' editor
    //       (see CentralWidget::updateGui()), so...

    setFocusProxy(mBorderedEditor->widget());

    mBorderedEditor->widget()->setFocus();
}

//==============================================================================

bool RawCellmlViewWidget::isManaged(const QString &pFileName) const
{
    // Return whether the given file name is managed

    return mBorderedEditors.value(pFileName);
}

//==============================================================================

void RawCellmlViewWidget::finalize(const QString &pFileName)
{
    // Remove the bordered editor, should there be one for the given file name

    Core::BorderedWidget *borderedEditor  = mBorderedEditors.value(pFileName);

    if (borderedEditor) {
        // There is a bordered editor for the given file name, so delete it and
        // remove it from our list

        delete borderedEditor;

        mBorderedEditors.remove(pFileName);

        // Reset our memory of the current bordered editor

        mBorderedEditor = 0;
    }
}

//==============================================================================

void RawCellmlViewWidget::fileReloaded(const QString &pFileName)
{
    // The given file has been reloaded, so reload it, should it be managed

    if (isManaged(pFileName)) {
        finalize(pFileName);
        initialize(pFileName);
    }
}

//==============================================================================

void RawCellmlViewWidget::splitterMoved()
{
    // The splitter has moved, so keep track of the viewer and editor's new
    // height

    mBorderedViewerHeight = mBorderedViewer->height();
    mBorderedEditorHeight = mBorderedEditor->height();
}

//==============================================================================

}   // namespace RawCellMLView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>

#include "../Flare.h"
#include "FlareTravel.h"
#include "FlareWorld.h"
#include "FlareGame.h"
#include "FlareGameTools.h"
#include "../UI/Components/FlareNotification.h"

static const double TRAVEL_DURATION_PER_PHASE_KM = 0.4;
static const double TRAVEL_DURATION_PER_ALTITUDE_KM = 6;

#define LOCTEXT_NAMESPACE "FlareTravelInfos"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareTravel::UFlareTravel(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareTravel::Load(const FFlareTravelSave& Data)
{
	Game = Cast<UFlareWorld>(GetOuter())->GetGame();
	TravelData = Data;
	TravelShips.Empty();

	Fleet = Game->GetGameWorld()->FindFleet(TravelData.FleetIdentifier);
	DestinationSector = Game->GetGameWorld()->FindSector(TravelData.DestinationSectorIdentifier);
	OriginSector = Game->GetGameWorld()->FindSector(TravelData.OriginSectorIdentifier);

	for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
	{
		TravelShips.Add(Fleet->GetShips()[ShipIndex]);
	}

	Fleet->SetCurrentTravel(this);
	GenerateTravelDuration();
}

FFlareTravelSave* UFlareTravel::Save()
{
	return &TravelData;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

void UFlareTravel::Simulate()
{
	if (GetRemainingTravelDuration() <= 0)
	{
		EndTravel();
	}
}

void UFlareTravel::EndTravel()
{
	Fleet->SetCurrentSector(DestinationSector);
	DestinationSector->AddFleet(Fleet);

	// Place correctly new ships to avoid collision

	// TODO People migration

	// Price migration
	float ContaminationFactor = 0.01f;
	for(int32 ResourceIndex = 0; ResourceIndex < Game->GetResourceCatalog()->Resources.Num(); ResourceIndex++)
	{
		FFlareResourceDescription* Resource = &Game->GetResourceCatalog()->Resources[ResourceIndex]->Data;

		float OriginPrice = OriginSector->GetPreciseResourcePrice(Resource);
		float DestinationPrice = DestinationSector->GetPreciseResourcePrice(Resource);

		float Mean = (OriginPrice + DestinationPrice) / 2.f;

		float NewOriginPrice = (OriginPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);
		float NewDestinationPrice = (DestinationPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);

		//FLOGV("Travel start from %s. %s price ajusted from %f to %f (Mean: %f)", *OriginSector->GetSectorName().ToString(), *Resource->Name.ToString(), OriginPrice/100., NewOriginPrice/100., Mean/100.);
		//FLOGV("Travel end from %s. %s price ajusted from %f to %f (Mean: %f)", *DestinationSector->GetSectorName().ToString(), *Resource->Name.ToString(), DestinationPrice/100., NewDestinationPrice/100., Mean/100.);

		OriginSector->SetPreciseResourcePrice(Resource, NewOriginPrice);
		DestinationSector->SetPreciseResourcePrice(Resource, NewDestinationPrice);

	}

	Game->GetGameWorld()->DeleteTravel(this);

	// Notify travel ended
	if (Fleet->GetFleetCompany() == Game->GetPC()->GetCompany() && Fleet->GetCurrentTradeRoute() == NULL)
	{
		Game->GetPC()->Notify(LOCTEXT("TravelEnded", "Travel ended"),
			FText::Format(LOCTEXT("TravelEndedFormat", "{0} arrived at {1}"),
				Fleet->GetFleetName(),
				DestinationSector->GetSectorName()),
			FName("travel-end"),
			EFlareNotification::NT_Economy,
			5.0f,
			EFlareMenu::MENU_Sector,
			DestinationSector);
	}
}

int64 UFlareTravel::GetElapsedTime()
{
	return Game->GetGameWorld()->GetDate() - TravelData.DepartureDate;
}

int64 UFlareTravel::GetRemainingTravelDuration()
{
	return TravelDuration - GetElapsedTime();
}

void UFlareTravel::ChangeDestination(UFlareSimulatedSector* NewDestinationSector)
{
	if (!CanChangeDestination())
	{
		return;
	}

	DestinationSector = NewDestinationSector;

	TravelData.DestinationSectorIdentifier = DestinationSector->GetIdentifier();

	// Reset travel duration
	// TODO intelligent travel remaining duration change
	TravelData.DepartureDate = Game->GetGameWorld()->GetDate();
	GenerateTravelDuration();
}

bool UFlareTravel::CanChangeDestination()
{
	// Travel not possible once started
	return GetElapsedTime() <= 0;
}

void UFlareTravel::GenerateTravelDuration()
{
	TravelDuration = ComputeTravelDuration(Game->GetGameWorld(), OriginSector, DestinationSector);
}

int64 UFlareTravel::ComputeTravelDuration(UFlareWorld* World, UFlareSimulatedSector* OriginSector, UFlareSimulatedSector* DestinationSector)
{
	int64 TravelDuration = 0;

	if (OriginSector == DestinationSector)
	{
		return 0;
	}

	double OriginAltitude;
	double DestinationAltitude;
	double OriginPhase;
	double DestinationPhase;
	FName OriginCelestialBodyIdentifier;
	FName DestinationCelestialBodyIdentifier;

	OriginAltitude = OriginSector->GetOrbitParameters()->Altitude;
	OriginCelestialBodyIdentifier = OriginSector->GetOrbitParameters()->CelestialBodyIdentifier;
	OriginPhase = OriginSector->GetOrbitParameters()->Phase;

	DestinationAltitude = DestinationSector->GetOrbitParameters()->Altitude;
	DestinationCelestialBodyIdentifier = DestinationSector->GetOrbitParameters()->CelestialBodyIdentifier;
	DestinationPhase = DestinationSector->GetOrbitParameters()->Phase;

	if (OriginCelestialBodyIdentifier == DestinationCelestialBodyIdentifier && OriginAltitude == DestinationAltitude)
	{
		// Phase change travel
		FFlareCelestialBody* CelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		TravelDuration = ComputePhaseTravelDuration(World, CelestialBody, OriginAltitude, OriginPhase, DestinationPhase) / UFlareGameTools::SECONDS_IN_DAY;
	}
	else
	{
		// Altitude change travel
		FFlareCelestialBody* OriginCelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		FFlareCelestialBody* DestinationCelestialBody = World->GetPlanerarium()->FindCelestialBody(DestinationCelestialBodyIdentifier);

		TravelDuration = ComputeAltitudeTravelDuration(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody, DestinationAltitude) / UFlareGameTools::SECONDS_IN_DAY;
	}

	return FMath::Max((int64) 1, TravelDuration);
}

double UFlareTravel::ComputeSphereOfInfluenceAltitude(UFlareWorld* World, FFlareCelestialBody* CelestialBody)
{
	FFlareCelestialBody* ParentCelestialBody = World->GetPlanerarium()->FindParent(CelestialBody);
	return CelestialBody->OrbitDistance * pow(CelestialBody->Mass / ParentCelestialBody->Mass, 0.4) - CelestialBody->Radius;
}

int64 UFlareTravel::ComputePhaseTravelDuration(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude, double OriginPhase, double DestinationPhase)
{
	double TravelPhase =  FMath::Abs(FMath::UnwindDegrees(DestinationPhase - OriginPhase));

	double OrbitRadius = CelestialBody->Radius + Altitude;
	double OrbitPerimeter = 2 * PI * OrbitRadius;
	double TravelDistance = OrbitPerimeter * TravelPhase / 360;

	return TRAVEL_DURATION_PER_PHASE_KM * TravelDistance;
}

int64 UFlareTravel::ComputeAltitudeTravelDuration(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, double OriginAltitude, FFlareCelestialBody* DestinationCelestialBody, double DestinationAltitude)
{
	double TravelAltitude;

	if (OriginCelestialBody == DestinationCelestialBody)
	{
		TravelAltitude = ComputeAltitudeTravelDistance(World, OriginAltitude, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(DestinationCelestialBody, OriginCelestialBody))
	{
		// Planet to moon
		TravelAltitude = ComputeAltitudeTravelToMoonDistance(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody) +
			ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(OriginCelestialBody, DestinationCelestialBody))
	{
		// Moon to planet
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelToMoonDistance(World, DestinationCelestialBody, DestinationAltitude, OriginCelestialBody);
	}
	else
	{
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelMoonToMoonDistance(World, OriginCelestialBody, DestinationCelestialBody) +
				ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}

	return TRAVEL_DURATION_PER_ALTITUDE_KM * TravelAltitude;
}

double UFlareTravel::ComputeAltitudeTravelDistance(UFlareWorld* World, double OriginAltitude, double DestinationAltitude)
{
	// Altitude change in same celestial body
	return FMath::Abs(DestinationAltitude - OriginAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToSoiDistance(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude)
{
	double MoonSoiAltitude = ComputeSphereOfInfluenceAltitude(World, CelestialBody);
	// Travel from moon SOI to moon target altitude
	return FMath::Abs(Altitude - MoonSoiAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToMoonDistance(UFlareWorld* World, FFlareCelestialBody* ParentCelestialBody, double Altitude, FFlareCelestialBody* MoonCelestialBody)
{
	double MoonAltitude = MoonCelestialBody->OrbitDistance - ParentCelestialBody->Radius;
	// Travel to moon altitude
	return FMath::Abs(MoonAltitude - Altitude);
}

double UFlareTravel::ComputeAltitudeTravelMoonToMoonDistance(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, FFlareCelestialBody* DestinationCelestialBody)
{
	// Moon1 orbit  to moon2 orbit
	return FMath::Abs(DestinationCelestialBody->OrbitDistance - OriginCelestialBody->OrbitDistance);
}

#undef LOCTEXT_NAMESPACE
<commit_msg>Make price contamination work only for traveling resources<commit_after>

#include "../Flare.h"
#include "FlareTravel.h"
#include "FlareWorld.h"
#include "FlareGame.h"
#include "FlareGameTools.h"
#include "../UI/Components/FlareNotification.h"
#include "../Economy/FlareCargoBay.h"

static const double TRAVEL_DURATION_PER_PHASE_KM = 0.4;
static const double TRAVEL_DURATION_PER_ALTITUDE_KM = 6;

#define LOCTEXT_NAMESPACE "FlareTravelInfos"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareTravel::UFlareTravel(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareTravel::Load(const FFlareTravelSave& Data)
{
	Game = Cast<UFlareWorld>(GetOuter())->GetGame();
	TravelData = Data;
	TravelShips.Empty();

	Fleet = Game->GetGameWorld()->FindFleet(TravelData.FleetIdentifier);
	DestinationSector = Game->GetGameWorld()->FindSector(TravelData.DestinationSectorIdentifier);
	OriginSector = Game->GetGameWorld()->FindSector(TravelData.OriginSectorIdentifier);

	for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
	{
		TravelShips.Add(Fleet->GetShips()[ShipIndex]);
	}

	Fleet->SetCurrentTravel(this);
	GenerateTravelDuration();
}

FFlareTravelSave* UFlareTravel::Save()
{
	return &TravelData;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

void UFlareTravel::Simulate()
{
	if (GetRemainingTravelDuration() <= 0)
	{
		EndTravel();
	}
}

void UFlareTravel::EndTravel()
{
	Fleet->SetCurrentSector(DestinationSector);
	DestinationSector->AddFleet(Fleet);

	// Place correctly new ships to avoid collision

	// TODO People migration

	// Price migration
	for(int32 ResourceIndex = 0; ResourceIndex < Game->GetResourceCatalog()->Resources.Num(); ResourceIndex++)
	{
		FFlareResourceDescription* Resource = &Game->GetResourceCatalog()->Resources[ResourceIndex]->Data;

		float ContaminationFactor = 0.00f;

		for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
		{
			UFlareSimulatedSpacecraft* Ship = Fleet->GetShips()[ShipIndex];
			if (Ship->GetCargoBay()->GetResourceQuantity(Resource) > 0)
			{
				ContaminationFactor += Ship->GetCargoBay()->GetResourceQuantity(Resource) / 1000.f;
				//TODO scale bay world stock/flow
			}
		}

		ContaminationFactor = FMath::Min(ContaminationFactor, 1.f);

		float OriginPrice = OriginSector->GetPreciseResourcePrice(Resource);
		float DestinationPrice = DestinationSector->GetPreciseResourcePrice(Resource);

		float Mean = (OriginPrice + DestinationPrice) / 2.f;

		float NewOriginPrice = (OriginPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);
		float NewDestinationPrice = (DestinationPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);

		//FLOGV("Travel start from %s. %s price ajusted from %f to %f (Mean: %f)", *OriginSector->GetSectorName().ToString(), *Resource->Name.ToString(), OriginPrice/100., NewOriginPrice/100., Mean/100.);
		//FLOGV("Travel end from %s. %s price ajusted from %f to %f (Mean: %f)", *DestinationSector->GetSectorName().ToString(), *Resource->Name.ToString(), DestinationPrice/100., NewDestinationPrice/100., Mean/100.);

		OriginSector->SetPreciseResourcePrice(Resource, NewOriginPrice);
		DestinationSector->SetPreciseResourcePrice(Resource, NewDestinationPrice);

	}

	Game->GetGameWorld()->DeleteTravel(this);

	// Notify travel ended
	if (Fleet->GetFleetCompany() == Game->GetPC()->GetCompany() && Fleet->GetCurrentTradeRoute() == NULL)
	{
		Game->GetPC()->Notify(LOCTEXT("TravelEnded", "Travel ended"),
			FText::Format(LOCTEXT("TravelEndedFormat", "{0} arrived at {1}"),
				Fleet->GetFleetName(),
				DestinationSector->GetSectorName()),
			FName("travel-end"),
			EFlareNotification::NT_Economy,
			5.0f,
			EFlareMenu::MENU_Sector,
			DestinationSector);
	}
}

int64 UFlareTravel::GetElapsedTime()
{
	return Game->GetGameWorld()->GetDate() - TravelData.DepartureDate;
}

int64 UFlareTravel::GetRemainingTravelDuration()
{
	return TravelDuration - GetElapsedTime();
}

void UFlareTravel::ChangeDestination(UFlareSimulatedSector* NewDestinationSector)
{
	if (!CanChangeDestination())
	{
		return;
	}

	DestinationSector = NewDestinationSector;

	TravelData.DestinationSectorIdentifier = DestinationSector->GetIdentifier();

	// Reset travel duration
	// TODO intelligent travel remaining duration change
	TravelData.DepartureDate = Game->GetGameWorld()->GetDate();
	GenerateTravelDuration();
}

bool UFlareTravel::CanChangeDestination()
{
	// Travel not possible once started
	return GetElapsedTime() <= 0;
}

void UFlareTravel::GenerateTravelDuration()
{
	TravelDuration = ComputeTravelDuration(Game->GetGameWorld(), OriginSector, DestinationSector);
}

int64 UFlareTravel::ComputeTravelDuration(UFlareWorld* World, UFlareSimulatedSector* OriginSector, UFlareSimulatedSector* DestinationSector)
{
	int64 TravelDuration = 0;

	if (OriginSector == DestinationSector)
	{
		return 0;
	}

	double OriginAltitude;
	double DestinationAltitude;
	double OriginPhase;
	double DestinationPhase;
	FName OriginCelestialBodyIdentifier;
	FName DestinationCelestialBodyIdentifier;

	OriginAltitude = OriginSector->GetOrbitParameters()->Altitude;
	OriginCelestialBodyIdentifier = OriginSector->GetOrbitParameters()->CelestialBodyIdentifier;
	OriginPhase = OriginSector->GetOrbitParameters()->Phase;

	DestinationAltitude = DestinationSector->GetOrbitParameters()->Altitude;
	DestinationCelestialBodyIdentifier = DestinationSector->GetOrbitParameters()->CelestialBodyIdentifier;
	DestinationPhase = DestinationSector->GetOrbitParameters()->Phase;

	if (OriginCelestialBodyIdentifier == DestinationCelestialBodyIdentifier && OriginAltitude == DestinationAltitude)
	{
		// Phase change travel
		FFlareCelestialBody* CelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		TravelDuration = ComputePhaseTravelDuration(World, CelestialBody, OriginAltitude, OriginPhase, DestinationPhase) / UFlareGameTools::SECONDS_IN_DAY;
	}
	else
	{
		// Altitude change travel
		FFlareCelestialBody* OriginCelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		FFlareCelestialBody* DestinationCelestialBody = World->GetPlanerarium()->FindCelestialBody(DestinationCelestialBodyIdentifier);

		TravelDuration = ComputeAltitudeTravelDuration(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody, DestinationAltitude) / UFlareGameTools::SECONDS_IN_DAY;
	}

	return FMath::Max((int64) 1, TravelDuration);
}

double UFlareTravel::ComputeSphereOfInfluenceAltitude(UFlareWorld* World, FFlareCelestialBody* CelestialBody)
{
	FFlareCelestialBody* ParentCelestialBody = World->GetPlanerarium()->FindParent(CelestialBody);
	return CelestialBody->OrbitDistance * pow(CelestialBody->Mass / ParentCelestialBody->Mass, 0.4) - CelestialBody->Radius;
}

int64 UFlareTravel::ComputePhaseTravelDuration(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude, double OriginPhase, double DestinationPhase)
{
	double TravelPhase =  FMath::Abs(FMath::UnwindDegrees(DestinationPhase - OriginPhase));

	double OrbitRadius = CelestialBody->Radius + Altitude;
	double OrbitPerimeter = 2 * PI * OrbitRadius;
	double TravelDistance = OrbitPerimeter * TravelPhase / 360;

	return TRAVEL_DURATION_PER_PHASE_KM * TravelDistance;
}

int64 UFlareTravel::ComputeAltitudeTravelDuration(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, double OriginAltitude, FFlareCelestialBody* DestinationCelestialBody, double DestinationAltitude)
{
	double TravelAltitude;

	if (OriginCelestialBody == DestinationCelestialBody)
	{
		TravelAltitude = ComputeAltitudeTravelDistance(World, OriginAltitude, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(DestinationCelestialBody, OriginCelestialBody))
	{
		// Planet to moon
		TravelAltitude = ComputeAltitudeTravelToMoonDistance(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody) +
			ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(OriginCelestialBody, DestinationCelestialBody))
	{
		// Moon to planet
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelToMoonDistance(World, DestinationCelestialBody, DestinationAltitude, OriginCelestialBody);
	}
	else
	{
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelMoonToMoonDistance(World, OriginCelestialBody, DestinationCelestialBody) +
				ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}

	return TRAVEL_DURATION_PER_ALTITUDE_KM * TravelAltitude;
}

double UFlareTravel::ComputeAltitudeTravelDistance(UFlareWorld* World, double OriginAltitude, double DestinationAltitude)
{
	// Altitude change in same celestial body
	return FMath::Abs(DestinationAltitude - OriginAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToSoiDistance(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude)
{
	double MoonSoiAltitude = ComputeSphereOfInfluenceAltitude(World, CelestialBody);
	// Travel from moon SOI to moon target altitude
	return FMath::Abs(Altitude - MoonSoiAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToMoonDistance(UFlareWorld* World, FFlareCelestialBody* ParentCelestialBody, double Altitude, FFlareCelestialBody* MoonCelestialBody)
{
	double MoonAltitude = MoonCelestialBody->OrbitDistance - ParentCelestialBody->Radius;
	// Travel to moon altitude
	return FMath::Abs(MoonAltitude - Altitude);
}

double UFlareTravel::ComputeAltitudeTravelMoonToMoonDistance(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, FFlareCelestialBody* DestinationCelestialBody)
{
	// Moon1 orbit  to moon2 orbit
	return FMath::Abs(DestinationCelestialBody->OrbitDistance - OriginCelestialBody->OrbitDistance);
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>


<commit_before>// seeedstudio grove hal.cpp for mbed
// 2014-3-5

#include "mbed.h"
#include "Suli.h"

/*
 * IO initialize
 * *pio - IO
 * pin - pin name
 */
void suli_pin_init(IO_T *pio, PIN_T pin)
{
    gpio_init(pio, pin, PIN_INPUT);
}

/*
 * set IO direction
 * - pio: IO device pointer
 * - dir: INPUT or OUTPUT
 */
void suli_pin_dir(IO_T *pio, DIR_T dir)
{
    gpio_dir(pio, dir);
}

/*
 * write to IO
 * - pio: IO device pointer
 * - state: HIGH or LOW
 */
void suli_pin_write(IO_T *pio, int16 state)
{
    gpio_write(pio, state);
}

/*
 * read IO state
 * - pio: IO device pointer
 * return HIGH or LOW
 */
int16 suli_pin_read(IO_T *pio)
{
    return gpio_read(pio);
}

/**
 * Reads a pulse (either HIGH or LOW) on a pin. For example, if value is HIGH,
 * suli_pulse_in() waits for the pin to go HIGH, starts timing,
 * then waits for the pin to go LOW and stops timing. Returns the length of the pulse in microseconds.
 * Gives up and returns 0 if no pulse starts within a specified time out.
 * para -
 * pin: pins which you want to read the pulse.
 * state: type of pulse to read: either HIGH or LOW. (int)
 * timeout (optional): the number of microseconds to wait for the pulse to start; default is one second (unsigned long)
 */
uint16 suli_pulse_in(uint8 pin, uint8 state, uint32 timeout)
{
    // add code here
    return 0;
}

/*
 * Analog Init
 * - aio: gpio device pointer
 * - pin: pin name
 */
void suli_analog_init(ANALOG_T * aio, PIN_T pin)
{
		analogin_init(aio, pin);
}

/*
 * Analog Read
 * As usually, 10bit ADC is enough, to increase the compatibility, will use only 10bit.
 * if if your ADC is 12bit, you need to >>2, or your ADC is 8Bit, you need to <<2
 */
int16 suli_analog_read(ANALOG_T * aio)
{
		return (analogin_read_u16(aio)>>6);
}

/*
 * delay us
 */
void suli_delay_us(uint32 us)
{
    wait_us(us);
}

/*
 * delay ms
 */
void suli_delay_ms(uint32 ms)
{
    wait_ms(ms);
}

/*
 * Returns the number of milliseconds since your board began running the current program.
 * This number will overflow (go back to zero), after approximately 50 days.
 */
uint32 suli_millis()
{
    // add code here
    return us_ticker_read()/1000;
}

/*
 * Returns the number of microseconds since your board began running the current program.
 * This number will overflow (go back to zero), after approximately 70 minutes.
 * Note: there are 1,000 microseconds in a millisecond and 1,000,000 microseconds in a second.
 */
uint32 suli_micros()
{
    // add code here
    return us_ticker_read();
}

/*
 * I2C interface initialize.
 */
void suli_i2c_init(void * i2c_device)
{
    // i2c_device
}

/*
 * write a buff to I2C
 * - i2c_device: i2c device pointer
 * - dev_addr: device address
 * - data: data buff
 * - len: data lenght
 */
uint8 suli_i2c_write(void * i2c_device, uint8 dev_addr, uint8 *data, uint8 len)
{
    ((I2C *)i2c_device)->write(dev_addr, (const char*)data, len);
    return 0;
}

/*
 * read a buff to I2C
 * - i2c_device: i2c device pointer
 * - dev_addr: device address
 * - data: data buff
 * - len: data lenght
 * return
 */
uint8 suli_i2c_read(void * i2c_device, uint8 dev_addr, uint8 *buff, uint8 len)
{
    uint8 err = ((I2C *)i2c_device)->read(dev_addr, (char*)buff, len);
    return err ? 0 : len;
}

/*
 * UART Init
 * - uart_device: uart device pointer
 * - uart_num: for some MCU, there's more than one uart, this is the number of uart
 * - baud： baudrate
 */
void suli_uart_init(void * uart_device, int16 uart_num, uint32 baud)
{
    ((Serial*)uart_device) -> baud(baud);
}

/*
 * Send a Buff to uart
 * - uart_device: uart device pointer
 * - uart_num: uart number
 * - *data: buff to sent
 * - len: data length
 */
void suli_uart_send(void * uart_device, int16 uart_num, uint8 *data, uint16 len)
{
    for(int i=0; i<len; i++)
    {
        ((Serial*)uart_device) ->putc(data[i]);
    }
}

/*
 * seed a byte to uart
 */
void suli_uart_send_byte(void * uart_device, int16 uart_num, uint8 data)
{
    ((Serial*)uart_device) -> putc(data);
}

/*
 * read a byte from uart
 */
uint8 suli_uart_read_byte(void * uart_device, int16 uart_num)
{
    return ((Serial*)uart_device) -> getc();
}

/*
 * if uart get data, return 1-readable, 0-unreadable
 */
uint16 suli_uart_readable(void * uart_device, int16 uart_num)
{
    return ((Serial*)uart_device) -> readable();
}

/*
 * write a float
 * num - number to write
 * decimal - x decimal point
 */
void suli_uart_write_float(void * uart_device, int16 uart_num, float num, uint8 decimal)
{
    char str[5];
    sprintf(str,"%%.%df", decimal);
    ((Serial*)uart_device) -> printf(str, num);
}

/*
 * write an integer
 * num - number to write
 */
void suli_uart_write_int(void * uart_device, int16 uart_num, int32 num)
{
    ((Serial*)uart_device) -> printf("%ld", num);
}
<commit_msg>update suli mbed<commit_after>// seeedstudio grove hal.cpp for mbed
// 2014-3-5

#include "mbed.h"
#include "Suli.h"

/*
 * IO initialize
 * *pio - IO
 * pin - pin name
 */
void suli_pin_init(IO_T *pio, PIN_T pin)
{
    gpio_init(pio, PIN_INPUT);
}

/*
 * set IO direction
 * - pio: IO device pointer
 * - dir: INPUT or OUTPUT
 */
void suli_pin_dir(IO_T *pio, DIR_T dir)
{
    gpio_dir(pio, dir);
}

/*
 * write to IO
 * - pio: IO device pointer
 * - state: HIGH or LOW
 */
void suli_pin_write(IO_T *pio, int16 state)
{
    gpio_write(pio, state);
}

/*
 * read IO state
 * - pio: IO device pointer
 * return HIGH or LOW
 */
int16 suli_pin_read(IO_T *pio)
{
    return gpio_read(pio);
}

/**
 * Reads a pulse (either HIGH or LOW) on a pin. For example, if value is HIGH,
 * suli_pulse_in() waits for the pin to go HIGH, starts timing,
 * then waits for the pin to go LOW and stops timing. Returns the length of the pulse in microseconds.
 * Gives up and returns 0 if no pulse starts within a specified time out.
 * para -
 * pin: pins which you want to read the pulse.
 * state: type of pulse to read: either HIGH or LOW. (int)
 * timeout (optional): the number of microseconds to wait for the pulse to start; default is one second (unsigned long)
 */
uint16 suli_pulse_in(uint8 pin, uint8 state, uint32 timeout)
{
    // add code here
    return 0;
}

/*
 * Analog Init
 * - aio: gpio device pointer
 * - pin: pin name
 */
void suli_analog_init(ANALOG_T * aio, PIN_T pin)
{
		analogin_init(aio, pin);
}

/*
 * Analog Read
 * As usually, 10bit ADC is enough, to increase the compatibility, will use only 10bit.
 * if if your ADC is 12bit, you need to >>2, or your ADC is 8Bit, you need to <<2
 */
int16 suli_analog_read(ANALOG_T * aio)
{
		return (analogin_read_u16(aio)>>6);
}

/*
 * delay us
 */
void suli_delay_us(uint32 us)
{
    wait_us(us);
}

/*
 * delay ms
 */
void suli_delay_ms(uint32 ms)
{
    wait_ms(ms);
}

/*
 * Returns the number of milliseconds since your board began running the current program.
 * This number will overflow (go back to zero), after approximately 50 days.
 */
uint32 suli_millis()
{
    // add code here
    return us_ticker_read()/1000;
}

/*
 * Returns the number of microseconds since your board began running the current program.
 * This number will overflow (go back to zero), after approximately 70 minutes.
 * Note: there are 1,000 microseconds in a millisecond and 1,000,000 microseconds in a second.
 */
uint32 suli_micros()
{
    // add code here
    return us_ticker_read();
}

/*
 * I2C interface initialize.
 */
void suli_i2c_init(void * i2c_device)
{
    // i2c_device
}

/*
 * write a buff to I2C
 * - i2c_device: i2c device pointer
 * - dev_addr: device address
 * - data: data buff
 * - len: data lenght
 */
uint8 suli_i2c_write(void * i2c_device, uint8 dev_addr, uint8 *data, uint8 len)
{
    ((I2C *)i2c_device)->write(dev_addr, (const char*)data, len);
    return 0;
}

/*
 * read a buff to I2C
 * - i2c_device: i2c device pointer
 * - dev_addr: device address
 * - data: data buff
 * - len: data lenght
 * return
 */
uint8 suli_i2c_read(void * i2c_device, uint8 dev_addr, uint8 *buff, uint8 len)
{
    uint8 err = ((I2C *)i2c_device)->read(dev_addr, (char*)buff, len);
    return err ? 0 : len;
}

/*
 * UART Init
 * - uart_device: uart device pointer
 * - uart_num: for some MCU, there's more than one uart, this is the number of uart
 * - baud： baudrate
 */
void suli_uart_init(void * uart_device, int16 uart_num, uint32 baud)
{
    ((Serial*)uart_device) -> baud(baud);
}

/*
 * Send a Buff to uart
 * - uart_device: uart device pointer
 * - uart_num: uart number
 * - *data: buff to sent
 * - len: data length
 */
void suli_uart_send(void * uart_device, int16 uart_num, uint8 *data, uint16 len)
{
    for(int i=0; i<len; i++)
    {
        ((Serial*)uart_device) ->putc(data[i]);
    }
}

/*
 * seed a byte to uart
 */
void suli_uart_send_byte(void * uart_device, int16 uart_num, uint8 data)
{
    ((Serial*)uart_device) -> putc(data);
}

/*
 * read a byte from uart
 */
uint8 suli_uart_read_byte(void * uart_device, int16 uart_num)
{
    return ((Serial*)uart_device) -> getc();
}

/*
 * if uart get data, return 1-readable, 0-unreadable
 */
uint16 suli_uart_readable(void * uart_device, int16 uart_num)
{
    return ((Serial*)uart_device) -> readable();
}

/*
 * write a float
 * num - number to write
 * decimal - x decimal point
 */
void suli_uart_write_float(void * uart_device, int16 uart_num, float num, uint8 decimal)
{
    char str[5];
    sprintf(str,"%%.%df", decimal);
    ((Serial*)uart_device) -> printf(str, num);
}

/*
 * write an integer
 * num - number to write
 */
void suli_uart_write_int(void * uart_device, int16 uart_num, int32 num)
{
    ((Serial*)uart_device) -> printf("%ld", num);
}
<|endoftext|>