<commit_before>/*
 * Copyright (C) 2020-present ScyllaDB
 *
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <functional>
#include <optional>
#include <variant>
#include <seastar/core/smp.hh>
#include <seastar/core/file.hh>
#include "sstables/shared_sstable.hh"
#include "sstables/sstable_set.hh"
#include "utils/UUID.hh"
#include "dht/i_partitioner.hh"
#include "compaction_weight_registration.hh"

namespace sstables {

enum class compaction_type {
    Compaction = 0,
    Cleanup = 1,
    Validation = 2,
    Scrub = 3,
    Index_build = 4,
    Reshard = 5,
    Upgrade = 6,
    Reshape = 7,
};

std::ostream& operator<<(std::ostream& os, compaction_type type);

struct compaction_completion_desc {
    // Old, existing SSTables that should be deleted and removed from the SSTable set.
    std::vector<shared_sstable> old_sstables;
    // New, fresh SSTables that should be added to SSTable set, replacing the old ones.
    std::vector<shared_sstable> new_sstables;
    // Set of compacted partition ranges that should be invalidated in the cache.
    dht::partition_range_vector ranges_for_cache_invalidation;
};

// creates a new SSTable for a given shard
using compaction_sstable_creator_fn = std::function<shared_sstable(shard_id shard)>;
// Replaces old sstable(s) by new one(s) which contain all non-expired data.
using compaction_sstable_replacer_fn = std::function<void(compaction_completion_desc)>;

class compaction_options {
public:
    struct regular {
    };
    struct cleanup {
        std::reference_wrapper<database> db;
    };
    struct upgrade {
        std::reference_wrapper<database> db;
    };
    struct scrub {
        enum class mode {
            abort, // abort scrub on the first sign of corruption
            skip, // skip corrupt data, including range of rows and/or partitions that are out-of-order
            segregate, // segregate out-of-order data into streams that all contain data with correct order
            validate, // validate data, printing all errors found (sstables are only read, not rewritten)
        };
        mode operation_mode = mode::abort;
    };
    struct reshard {
    };
    struct reshape {
    };
private:
    using options_variant = std::variant<regular, cleanup, upgrade, scrub, reshard, reshape>;

private:
    options_variant _options;

private:
    explicit compaction_options(options_variant options) : _options(std::move(options)) {
    }

public:
    static compaction_options make_reshape() {
        return compaction_options(reshape{});
    }

    static compaction_options make_reshard() {
        return compaction_options(reshard{});
    }

    static compaction_options make_regular() {
        return compaction_options(regular{});
    }

    static compaction_options make_cleanup(database& db) {
        return compaction_options(cleanup{db});
    }

    static compaction_options make_upgrade(database& db) {
        return compaction_options(upgrade{db});
    }

    static compaction_options make_scrub(scrub::mode mode) {
        return compaction_options(scrub{mode});
    }

    template <typename... Visitor>
    auto visit(Visitor&&... visitor) const {
        return std::visit(std::forward<Visitor>(visitor)..., _options);
    }

    const options_variant& options() const { return _options; }

    compaction_type type() const;
};

std::string_view to_string(compaction_options::scrub::mode);
std::ostream& operator<<(std::ostream& os, compaction_options::scrub::mode scrub_mode);

struct compaction_descriptor {
    // List of sstables to be compacted.
    std::vector<sstables::shared_sstable> sstables;
    // This is a snapshot of the table's sstable set, used only for the purpose of expiring tombstones.
    // If this sstable set cannot be provided, expiration will be disabled to prevent data from being resurrected.
    std::optional<sstables::sstable_set> all_sstables_snapshot;
    // Level of sstable(s) created by compaction procedure.
    int level;
    // Threshold size for sstable(s) to be created.
    uint64_t max_sstable_bytes;
    // Run identifier of output sstables.
    utils::UUID run_identifier;
    // Calls compaction manager's task for this compaction to release reference to exhausted sstables.
    std::function<void(const std::vector<shared_sstable>& exhausted_sstables)> release_exhausted;
    // The options passed down to the compaction code.
    // This also selects the kind of compaction to do.
    compaction_options options = compaction_options::make_regular();

    compaction_sstable_creator_fn creator;
    compaction_sstable_replacer_fn replacer;

    ::io_priority_class io_priority = default_priority_class();

    compaction_descriptor() = default;

    static constexpr int default_level = 0;
    static constexpr uint64_t default_max_sstable_bytes = std::numeric_limits<uint64_t>::max();

    explicit compaction_descriptor(std::vector<sstables::shared_sstable> sstables,
                                   std::optional<sstables::sstable_set> all_sstables_snapshot,
                                   ::io_priority_class io_priority,
                                   int level = default_level,
                                   uint64_t max_sstable_bytes = default_max_sstable_bytes,
                                   utils::UUID run_identifier = utils::make_random_uuid(),
                                   compaction_options options = compaction_options::make_regular())
        : sstables(std::move(sstables))
        , all_sstables_snapshot(std::move(all_sstables_snapshot))
        , level(level)
        , max_sstable_bytes(max_sstable_bytes)
        , run_identifier(run_identifier)
        , options(options)
        , io_priority(io_priority)
    {}
};

}
<commit_msg>compaction/compaction_descriptor: add comment to Validation compaction type<commit_after>/*
 * Copyright (C) 2020-present ScyllaDB
 *
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <functional>
#include <optional>
#include <variant>
#include <seastar/core/smp.hh>
#include <seastar/core/file.hh>
#include "sstables/shared_sstable.hh"
#include "sstables/sstable_set.hh"
#include "utils/UUID.hh"
#include "dht/i_partitioner.hh"
#include "compaction_weight_registration.hh"

namespace sstables {

enum class compaction_type {
    Compaction = 0,
    Cleanup = 1,
    Validation = 2, // Origin uses this for a compaction that is used exclusively for repair
    Scrub = 3,
    Index_build = 4,
    Reshard = 5,
    Upgrade = 6,
    Reshape = 7,
};

std::ostream& operator<<(std::ostream& os, compaction_type type);

struct compaction_completion_desc {
    // Old, existing SSTables that should be deleted and removed from the SSTable set.
    std::vector<shared_sstable> old_sstables;
    // New, fresh SSTables that should be added to SSTable set, replacing the old ones.
    std::vector<shared_sstable> new_sstables;
    // Set of compacted partition ranges that should be invalidated in the cache.
    dht::partition_range_vector ranges_for_cache_invalidation;
};

// creates a new SSTable for a given shard
using compaction_sstable_creator_fn = std::function<shared_sstable(shard_id shard)>;
// Replaces old sstable(s) by new one(s) which contain all non-expired data.
using compaction_sstable_replacer_fn = std::function<void(compaction_completion_desc)>;

class compaction_options {
public:
    struct regular {
    };
    struct cleanup {
        std::reference_wrapper<database> db;
    };
    struct upgrade {
        std::reference_wrapper<database> db;
    };
    struct scrub {
        enum class mode {
            abort, // abort scrub on the first sign of corruption
            skip, // skip corrupt data, including range of rows and/or partitions that are out-of-order
            segregate, // segregate out-of-order data into streams that all contain data with correct order
            validate, // validate data, printing all errors found (sstables are only read, not rewritten)
        };
        mode operation_mode = mode::abort;
    };
    struct reshard {
    };
    struct reshape {
    };
private:
    using options_variant = std::variant<regular, cleanup, upgrade, scrub, reshard, reshape>;

private:
    options_variant _options;

private:
    explicit compaction_options(options_variant options) : _options(std::move(options)) {
    }

public:
    static compaction_options make_reshape() {
        return compaction_options(reshape{});
    }

    static compaction_options make_reshard() {
        return compaction_options(reshard{});
    }

    static compaction_options make_regular() {
        return compaction_options(regular{});
    }

    static compaction_options make_cleanup(database& db) {
        return compaction_options(cleanup{db});
    }

    static compaction_options make_upgrade(database& db) {
        return compaction_options(upgrade{db});
    }

    static compaction_options make_scrub(scrub::mode mode) {
        return compaction_options(scrub{mode});
    }

    template <typename... Visitor>
    auto visit(Visitor&&... visitor) const {
        return std::visit(std::forward<Visitor>(visitor)..., _options);
    }

    const options_variant& options() const { return _options; }

    compaction_type type() const;
};

std::string_view to_string(compaction_options::scrub::mode);
std::ostream& operator<<(std::ostream& os, compaction_options::scrub::mode scrub_mode);

struct compaction_descriptor {
    // List of sstables to be compacted.
    std::vector<sstables::shared_sstable> sstables;
    // This is a snapshot of the table's sstable set, used only for the purpose of expiring tombstones.
    // If this sstable set cannot be provided, expiration will be disabled to prevent data from being resurrected.
    std::optional<sstables::sstable_set> all_sstables_snapshot;
    // Level of sstable(s) created by compaction procedure.
    int level;
    // Threshold size for sstable(s) to be created.
    uint64_t max_sstable_bytes;
    // Run identifier of output sstables.
    utils::UUID run_identifier;
    // Calls compaction manager's task for this compaction to release reference to exhausted sstables.
    std::function<void(const std::vector<shared_sstable>& exhausted_sstables)> release_exhausted;
    // The options passed down to the compaction code.
    // This also selects the kind of compaction to do.
    compaction_options options = compaction_options::make_regular();

    compaction_sstable_creator_fn creator;
    compaction_sstable_replacer_fn replacer;

    ::io_priority_class io_priority = default_priority_class();

    compaction_descriptor() = default;

    static constexpr int default_level = 0;
    static constexpr uint64_t default_max_sstable_bytes = std::numeric_limits<uint64_t>::max();

    explicit compaction_descriptor(std::vector<sstables::shared_sstable> sstables,
                                   std::optional<sstables::sstable_set> all_sstables_snapshot,
                                   ::io_priority_class io_priority,
                                   int level = default_level,
                                   uint64_t max_sstable_bytes = default_max_sstable_bytes,
                                   utils::UUID run_identifier = utils::make_random_uuid(),
                                   compaction_options options = compaction_options::make_regular())
        : sstables(std::move(sstables))
        , all_sstables_snapshot(std::move(all_sstables_snapshot))
        , level(level)
        , max_sstable_bytes(max_sstable_bytes)
        , run_identifier(run_identifier)
        , options(options)
        , io_priority(io_priority)
    {}
};

}
<|endoftext|>


<commit_before>/*
 * Copyright 2021 The Imaging Source Europe GmbH
 *
 * 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 "aboutdialog.h"
#include "ui_aboutdialog.h"

#include <QProcess>
#include <QLabel>
#include <QPushButton>
#include <QFormLayout>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>

namespace {

struct meta_entry
{
    QString name;
    QLabel* value;
};

class MetaWidget : public QWidget
{
public:

    MetaWidget(QWidget*parent=nullptr)
        : QWidget(parent)
    {
        setLayout(new QFormLayout());

        // Add default string, so that user always knows what is going on
        dynamic_cast<QFormLayout*>(layout())->addRow("No GstMeta available.", new QLabel());
    };

    ~MetaWidget()
    {}

    void update(GstStructure& struc)
    {
        // GstMeta will always have more than 1 entry
        // this is a reliable identifier for us receiving the
        // first update and having to remove the default string
        if (dynamic_cast<QFormLayout*>(layout())->rowCount() == 1)
        {
            dynamic_cast<QFormLayout*>(layout())->removeRow(0);
        }

        auto meta_cb = [] (GQuark field_id,
                           const GValue* value,
                           gpointer user_data) -> gboolean
        {

            auto fill_label = [] (QLabel* label, const GValue* gvalue)
            {
                if (G_VALUE_TYPE(gvalue) == G_TYPE_BOOLEAN)
                {
                    gboolean val = g_value_get_boolean(gvalue);
                    if (val)
                    {
                        label->setText("true");
                    }
                    else
                    {
                        label->setText("false");
                    }
                }
                else if (G_VALUE_TYPE(gvalue) == G_TYPE_DOUBLE)
                {
                    double val = g_value_get_double(gvalue);
                    label->setText(QString::number(val));

                }
                else if (G_VALUE_TYPE(gvalue) == G_TYPE_UINT64)
                {
                    guint64 val = g_value_get_uint64(gvalue);
                    label->setText(QString::number(val));
                }
                else
                {
                    qWarning("value type not implemented for TcamMeta\n");
                }
            };

            MetaWidget* self  = (MetaWidget*)user_data;

            QString name = g_quark_to_string(field_id);

            auto iter = std::find_if(self->m_entries.begin(), self->m_entries.end(),
                                     [&name] (const meta_entry& entry)
                                     {
                                         if (entry.name == name)
                                         {
                                             return true;
                                         }
                                         return false;
                                     });

            if (iter == self->m_entries.end())
            {
                meta_entry e = {name, new QLabel()};
                e.value->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
                self->m_entries.push_back(e);

                dynamic_cast<QFormLayout*>(self->layout())->addRow(name, e.value);
                fill_label(e.value, value);
            }
            else
            {
                fill_label(iter->value, value);
            }

            return TRUE;
        };

        gst_structure_foreach(&struc, meta_cb, this);

    }

private:

    std::vector<meta_entry> m_entries;
};

} // namespace

AboutDialog::AboutDialog(const QString& pipeline_str, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AboutDialog),
    m_pipeline_str(pipeline_str)
{
    ui->setupUi(this);

    ui->tab_versions->layout()->setAlignment(Qt::AlignTop);

    fill_stream();
    fill_versions();
    fill_state();
}

AboutDialog::~AboutDialog()
{
    delete ui;
}

void AboutDialog::fill_stream()
{
    auto layout = dynamic_cast<QFormLayout*>(ui->tab_stream->layout());

    auto tmp = m_pipeline_str;

    tmp.replace("!", "
!");

    auto pipe_label = new QLabel(tmp);
    pipe_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);

    layout->addRow("Pipeline: ", pipe_label);

    if (!p_label_device_caps)
    {
        p_label_device_caps = new QLabel();
        p_label_device_caps->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    }

    layout->addRow("Device caps: ", p_label_device_caps);

    p_meta = new MetaWidget();

    layout->addRow("GstMeta:", p_meta);

}

void AboutDialog::fill_versions()
{
    QProcess process_tis_version;
    process_tis_version.start("tcam-ctrl", {"--version"});
    process_tis_version.waitForFinished(-1); // will wait forever until finished

    QString stdout_tis = process_tis_version.readAllStandardOutput();
    QString stderr = process_tis_version.readAllStandardError();

    QProcess process_tis_packages;
    process_tis_packages.start("tcam-ctrl", {"--packages"});
    process_tis_packages.waitForFinished(-1); // will wait forever until finished

    QString stdout_packages = process_tis_packages.readAllStandardOutput();

    // we work with a rich text label
    // this means a 'normal' string
    // does not suffice. Instead use html tags.
    stdout_tis.replace("\n", "
");
    stdout_packages.replace("\n", "
");
    stdout_packages.replace("\t", " ");

    QString s = "
Tiscamera:
"
        + stdout_tis
        + "
Packages:
"
        + stdout_packages;

    ui->label_versions->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    ui->label_versions->setText(s);
}

void AboutDialog::fill_state()
{
    auto apply_button = ui->buttonBox_2->button(QDialogButtonBox::Apply);
    auto reset_button = ui->buttonBox_2->button(QDialogButtonBox::Reset);
    auto save_button = ui->buttonBox_2->button(QDialogButtonBox::Save);
    auto open_button = ui->buttonBox_2->button(QDialogButtonBox::Open);

    connect(apply_button, &QPushButton::clicked, this, &AboutDialog::write_state);
    connect(reset_button, &QPushButton::clicked, this, &AboutDialog::update_state);
    connect(save_button, &QPushButton::clicked, this, &AboutDialog::save_state);
    connect(open_button, &QPushButton::clicked, this, &AboutDialog::open_state);
}

void AboutDialog::set_device_caps(const QString& dev_caps)
{
    p_label_device_caps->setText(dev_caps);
}

void AboutDialog::set_tcambin(GstElement* bin)
{
    p_tcambin = bin;

    update_state();
}

void AboutDialog::update_meta(GstStructure* meta)
{
    MetaWidget* m = (MetaWidget*)p_meta;

    m->update(*meta);

    // has to free here
    gst_structure_free(meta);
}

void AboutDialog::update_state()
{
    if (!p_tcambin)
    {
        ui->state_field->setPlainText("");
        ui->state_field->setEnabled(false);
        return;
    }

    GValue state = G_VALUE_INIT;
    g_value_init(&state, G_TYPE_STRING);
    g_object_get_property(G_OBJECT(p_tcambin), "tcam-properties-json", &state);

    ui->state_field->setPlainText(g_value_get_string(&state));

    g_value_unset(&state);

    ui->state_field->setEnabled(true);
}

void AboutDialog::write_state()
{
    auto str = ui->state_field->toPlainText();

    g_object_set(p_tcambin, "tcam-properties-json", str.toStdString().c_str(), nullptr);

}

void AboutDialog::open_state()
{
    auto filename = QFileDialog::getOpenFileName(this,
                                                 "Open JSON State",
                                                 QString(),
                                                 ("*.json"));

    if (filename.isEmpty())
    {
        return;
    }

    QFile f(filename);

    f.open(QIODevice::ReadOnly);
    ui->state_field->setPlainText(f.readAll().toStdString().c_str());

    f.close();
}

void AboutDialog::save_state()
{
    auto filename = QFileDialog::getSaveFileName(this,
                                                 "Save JSON State",
                                                 QString(),
                                                 ("*.json"));

    if (filename.isEmpty())
    {
        return;
    }

    QFile f(filename);

    f.open(QIODevice::WriteOnly);
    f.write(ui->state_field->toPlainText().toUtf8());

    f.close();
}
<commit_msg>tcam-capture: Change title of json save dialog<commit_after>/*
 * Copyright 2021 The Imaging Source Europe GmbH
 *
 * 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 "aboutdialog.h"
#include "ui_aboutdialog.h"

#include <QProcess>
#include <QLabel>
#include <QPushButton>
#include <QFormLayout>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>

namespace {

struct meta_entry
{
    QString name;
    QLabel* value;
};

class MetaWidget : public QWidget
{
public:

    MetaWidget(QWidget*parent=nullptr)
        : QWidget(parent)
    {
        setLayout(new QFormLayout());

        // Add default string, so that user always knows what is going on
        dynamic_cast<QFormLayout*>(layout())->addRow("No GstMeta available.", new QLabel());
    };

    ~MetaWidget()
    {}

    void update(GstStructure& struc)
    {
        // GstMeta will always have more than 1 entry
        // this is a reliable identifier for us receiving the
        // first update and having to remove the default string
        if (dynamic_cast<QFormLayout*>(layout())->rowCount() == 1)
        {
            dynamic_cast<QFormLayout*>(layout())->removeRow(0);
        }

        auto meta_cb = [] (GQuark field_id,
                           const GValue* value,
                           gpointer user_data) -> gboolean
        {

            auto fill_label = [] (QLabel* label, const GValue* gvalue)
            {
                if (G_VALUE_TYPE(gvalue) == G_TYPE_BOOLEAN)
                {
                    gboolean val = g_value_get_boolean(gvalue);
                    if (val)
                    {
                        label->setText("true");
                    }
                    else
                    {
                        label->setText("false");
                    }
                }
                else if (G_VALUE_TYPE(gvalue) == G_TYPE_DOUBLE)
                {
                    double val = g_value_get_double(gvalue);
                    label->setText(QString::number(val));

                }
                else if (G_VALUE_TYPE(gvalue) == G_TYPE_UINT64)
                {
                    guint64 val = g_value_get_uint64(gvalue);
                    label->setText(QString::number(val));
                }
                else
                {
                    qWarning("value type not implemented for TcamMeta\n");
                }
            };

            MetaWidget* self  = (MetaWidget*)user_data;

            QString name = g_quark_to_string(field_id);

            auto iter = std::find_if(self->m_entries.begin(), self->m_entries.end(),
                                     [&name] (const meta_entry& entry)
                                     {
                                         if (entry.name == name)
                                         {
                                             return true;
                                         }
                                         return false;
                                     });

            if (iter == self->m_entries.end())
            {
                meta_entry e = {name, new QLabel()};
                e.value->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
                self->m_entries.push_back(e);

                dynamic_cast<QFormLayout*>(self->layout())->addRow(name, e.value);
                fill_label(e.value, value);
            }
            else
            {
                fill_label(iter->value, value);
            }

            return TRUE;
        };

        gst_structure_foreach(&struc, meta_cb, this);

    }

private:

    std::vector<meta_entry> m_entries;
};

} // namespace

AboutDialog::AboutDialog(const QString& pipeline_str, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AboutDialog),
    m_pipeline_str(pipeline_str)
{
    ui->setupUi(this);

    ui->tab_versions->layout()->setAlignment(Qt::AlignTop);

    fill_stream();
    fill_versions();
    fill_state();
}

AboutDialog::~AboutDialog()
{
    delete ui;
}

void AboutDialog::fill_stream()
{
    auto layout = dynamic_cast<QFormLayout*>(ui->tab_stream->layout());

    auto tmp = m_pipeline_str;

    tmp.replace("!", "
!");

    auto pipe_label = new QLabel(tmp);
    pipe_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);

    layout->addRow("Pipeline: ", pipe_label);

    if (!p_label_device_caps)
    {
        p_label_device_caps = new QLabel();
        p_label_device_caps->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    }

    layout->addRow("Device caps: ", p_label_device_caps);

    p_meta = new MetaWidget();

    layout->addRow("GstMeta:", p_meta);

}

void AboutDialog::fill_versions()
{
    QProcess process_tis_version;
    process_tis_version.start("tcam-ctrl", {"--version"});
    process_tis_version.waitForFinished(-1); // will wait forever until finished

    QString stdout_tis = process_tis_version.readAllStandardOutput();
    QString stderr = process_tis_version.readAllStandardError();

    QProcess process_tis_packages;
    process_tis_packages.start("tcam-ctrl", {"--packages"});
    process_tis_packages.waitForFinished(-1); // will wait forever until finished

    QString stdout_packages = process_tis_packages.readAllStandardOutput();

    // we work with a rich text label
    // this means a 'normal' string
    // does not suffice. Instead use html tags.
    stdout_tis.replace("\n", "
");
    stdout_packages.replace("\n", "
");
    stdout_packages.replace("\t", " ");

    QString s = "
Tiscamera:
"
        + stdout_tis
        + "
Packages:
"
        + stdout_packages;

    ui->label_versions->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    ui->label_versions->setText(s);
}

void AboutDialog::fill_state()
{
    auto apply_button = ui->buttonBox_2->button(QDialogButtonBox::Apply);
    auto reset_button = ui->buttonBox_2->button(QDialogButtonBox::Reset);
    auto save_button = ui->buttonBox_2->button(QDialogButtonBox::Save);
    auto open_button = ui->buttonBox_2->button(QDialogButtonBox::Open);

    connect(apply_button, &QPushButton::clicked, this, &AboutDialog::write_state);
    connect(reset_button, &QPushButton::clicked, this, &AboutDialog::update_state);
    connect(save_button, &QPushButton::clicked, this, &AboutDialog::save_state);
    connect(open_button, &QPushButton::clicked, this, &AboutDialog::open_state);
}

void AboutDialog::set_device_caps(const QString& dev_caps)
{
    p_label_device_caps->setText(dev_caps);
}

void AboutDialog::set_tcambin(GstElement* bin)
{
    p_tcambin = bin;

    update_state();
}

void AboutDialog::update_meta(GstStructure* meta)
{
    MetaWidget* m = (MetaWidget*)p_meta;

    m->update(*meta);

    // has to free here
    gst_structure_free(meta);
}

void AboutDialog::update_state()
{
    if (!p_tcambin)
    {
        ui->state_field->setPlainText("");
        ui->state_field->setEnabled(false);
        return;
    }

    GValue state = G_VALUE_INIT;
    g_value_init(&state, G_TYPE_STRING);
    g_object_get_property(G_OBJECT(p_tcambin), "tcam-properties-json", &state);

    ui->state_field->setPlainText(g_value_get_string(&state));

    g_value_unset(&state);

    ui->state_field->setEnabled(true);
}

void AboutDialog::write_state()
{
    auto str = ui->state_field->toPlainText();

    g_object_set(p_tcambin, "tcam-properties-json", str.toStdString().c_str(), nullptr);

}

void AboutDialog::open_state()
{
    auto filename = QFileDialog::getOpenFileName(this,
                                                 "Open JSON State",
                                                 QString(),
                                                 ("*.json"));

    if (filename.isEmpty())
    {
        return;
    }

    QFile f(filename);

    f.open(QIODevice::ReadOnly);
    ui->state_field->setPlainText(f.readAll().toStdString().c_str());

    f.close();
}

void AboutDialog::save_state()
{
    auto filename = QFileDialog::getSaveFileName(this,
                                                 "Save Device State",
                                                 QString(),
                                                 ("*.json"));

    if (filename.isEmpty())
    {
        return;
    }

    QFile f(filename);

    f.open(QIODevice::WriteOnly);
    f.write(ui->state_field->toPlainText().toUtf8());

    f.close();
}
<|endoftext|>


<commit_before>/* Copyright (c) 2009 - http://ruinwesen.com/ */

#include "Encoders.hh"
#include "MidiTools.h"
#include "Midi.h"

#include "GUI.h"

/* handlers */

/**
 * \addtogroup GUI
 *
 * @{
 *
 * \addtogroup gui_encoders Encoder classes
 *
 * @{
 *
 * \file
 * Encoder classes
 **/
class Encoder;

/**
 * Handle a change in a CCEncoder by sending out the CC, using the
 * channel and cc out of the CCEncoder object.
 **/
void CCEncoderHandle(Encoder *enc) {
  CCEncoder *ccEnc = (CCEncoder *)enc;
  uint8_t channel = ccEnc->getChannel();
  uint8_t cc = ccEnc->getCC();
  uint8_t value = ccEnc->getValue();

  MidiUart.sendCC(channel, cc, value);
}

/**
 * Handle a change in a VarRangeEncoder by setting the variable pointed to by enc->var.
 **/
void VarRangeEncoderHandle(Encoder *enc) {
  VarRangeEncoder *rEnc = (VarRangeEncoder *)enc;
  if (rEnc->var != NULL) {
    *(rEnc->var) = rEnc->getValue();
  }
}

#ifndef HOST_MIDIDUINO
#include <MidiClock.h>

/**
 * Handle an encoder change by setting the MidiClock tempo to the encoder value.
 **/
void TempoEncoderHandle(Encoder *enc) {
  MidiClock.setTempo(enc->getValue());
}
#endif

Encoder::Encoder(const char *_name, encoder_handle_t _handler)
  : old(0),
    cur(0),
    redisplay(false),
    handler(_handler),
    fastmode(true),
    fastmodestep(5),
    pressmode(false),
    locked(false)
{
  setName(_name);
}

void Encoder::checkHandle() {
  if (cur != old) {
    if (!locked) {
      if (handler != NULL)
        handler(this);
    }
  }

  old = cur;
}

void Encoder::setName(const char *_name) {
  if (_name != NULL)
    m_strncpy_fill(name, _name, 4);
  name[3] = '\0';
}

void Encoder::setValue(int value, bool handle) {
  if (handle) {
    cur = value;
    checkHandle();
  } else {
    old = cur = value;
  }
  redisplay = true;
}

void Encoder::lock() {
  old_lock = old;
  locked = true;
}

void Encoder::unlock() {
  locked = false;
  old = old_lock;
  //  checkHandle();
}

void Encoder::displayAt(int i) {
  GUI.put_value(i, getValue());
  redisplay = false;
}

bool Encoder::hasChanged() {
  return old != cur && !locked;
}

void Encoder::clear() {
  old = 0;
  cur = 0;
}

int Encoder::update(encoder_t *enc) {
  cur = cur + enc->normal + (pressmode ? 0 : (fastmode ? fastmodestep * enc->button : enc->button));
  return cur;
}

/* EnumEncoder */
void EnumEncoder::displayAt(int i) {
  GUI.put_string_at(i * 4, enumStrings[getValue()]);
  redisplay = false;
}

void PEnumEncoder::displayAt(int i) {
  //  GUI.put_p_string_at_fill(i * 4, (PGM_P)(pgm_read_word(enumStrings[getValue()])));
  GUI.put_p_string_at(i * 4, (PGM_P)(enumStrings[getValue()]));
  redisplay = false;
}

/* RangeEncoder */

int RangeEncoder::update(encoder_t *enc) {
  int inc = enc->normal + (pressmode ? 0 : (fastmode ? fastmodestep * enc->button : enc->button));

  cur = limit_value(cur, inc, min, max);
  return cur;
}

/* CharEncoder */
CharEncoder::CharEncoder()  : RangeEncoder(0, 37) {
}

char CharEncoder::getChar() {
  uint8_t val = getValue();
  if (val == 0) {
    return ' ';
  }
  if (val < 27)
    return val - 1+ 'A';
  else
    return (val - 27) + '0';
}

void CharEncoder::setChar(char c) {
  if (c >= 'A' && c <= 'Z') {
    setValue(c - 'A' + 1);
  } else if (c >= '0' && c <= '9') {
    setValue(c - '0' + 26 + 1);
  } else {
    setValue(0);
  }
}

/* notePitchEncoder */
NotePitchEncoder::NotePitchEncoder(char *_name) : RangeEncoder(0, 127, _name) {
  setName(_name);
}

void NotePitchEncoder::displayAt(int i) {
  char name[5];
  getNotePitch(getValue(), name);
  GUI.put_string_at(i * 4, name);
}

void MidiTrackEncoder::displayAt(int i) {
  GUI.put_value(i, getValue() + 1);
}

/* auto name CC encoder */
void AutoNameCCEncoder::initCCEncoder(uint8_t _channel, uint8_t _cc) {
  CCEncoder::initCCEncoder(_channel, _cc);
  setCCName();
  GUI.redisplay();

}

<commit_msg>Fix initialization order<commit_after>/* Copyright (c) 2009 - http://ruinwesen.com/ */

#include "Encoders.hh"
#include "MidiTools.h"
#include "Midi.h"

#include "GUI.h"

/* handlers */

/**
 * \addtogroup GUI
 *
 * @{
 *
 * \addtogroup gui_encoders Encoder classes
 *
 * @{
 *
 * \file
 * Encoder classes
 **/
class Encoder;

/**
 * Handle a change in a CCEncoder by sending out the CC, using the
 * channel and cc out of the CCEncoder object.
 **/
void CCEncoderHandle(Encoder *enc) {
  CCEncoder *ccEnc = (CCEncoder *)enc;
  uint8_t channel = ccEnc->getChannel();
  uint8_t cc = ccEnc->getCC();
  uint8_t value = ccEnc->getValue();

  MidiUart.sendCC(channel, cc, value);
}

/**
 * Handle a change in a VarRangeEncoder by setting the variable pointed to by enc->var.
 **/
void VarRangeEncoderHandle(Encoder *enc) {
  VarRangeEncoder *rEnc = (VarRangeEncoder *)enc;
  if (rEnc->var != NULL) {
    *(rEnc->var) = rEnc->getValue();
  }
}

#ifndef HOST_MIDIDUINO
#include <MidiClock.h>

/**
 * Handle an encoder change by setting the MidiClock tempo to the encoder value.
 **/
void TempoEncoderHandle(Encoder *enc) {
  MidiClock.setTempo(enc->getValue());
}
#endif

Encoder::Encoder(const char *_name, encoder_handle_t _handler)
  : old(0),
    cur(0),
    fastmode(true),
    fastmodestep(5),
    pressmode(false),
    locked(false),
    handler(_handler),
    redisplay(false)
{
  setName(_name);
}

void Encoder::checkHandle() {
  if (cur != old) {
    if (!locked) {
      if (handler != NULL)
        handler(this);
    }
  }

  old = cur;
}

void Encoder::setName(const char *_name) {
  if (_name != NULL)
    m_strncpy_fill(name, _name, 4);
  name[3] = '\0';
}

void Encoder::setValue(int value, bool handle) {
  if (handle) {
    cur = value;
    checkHandle();
  } else {
    old = cur = value;
  }
  redisplay = true;
}

void Encoder::lock() {
  old_lock = old;
  locked = true;
}

void Encoder::unlock() {
  locked = false;
  old = old_lock;
  //  checkHandle();
}

void Encoder::displayAt(int i) {
  GUI.put_value(i, getValue());
  redisplay = false;
}

bool Encoder::hasChanged() {
  return old != cur && !locked;
}

void Encoder::clear() {
  old = 0;
  cur = 0;
}

int Encoder::update(encoder_t *enc) {
  cur = cur + enc->normal + (pressmode ? 0 : (fastmode ? fastmodestep * enc->button : enc->button));
  return cur;
}

/* EnumEncoder */
void EnumEncoder::displayAt(int i) {
  GUI.put_string_at(i * 4, enumStrings[getValue()]);
  redisplay = false;
}

void PEnumEncoder::displayAt(int i) {
  //  GUI.put_p_string_at_fill(i * 4, (PGM_P)(pgm_read_word(enumStrings[getValue()])));
  GUI.put_p_string_at(i * 4, (PGM_P)(enumStrings[getValue()]));
  redisplay = false;
}

/* RangeEncoder */

int RangeEncoder::update(encoder_t *enc) {
  int inc = enc->normal + (pressmode ? 0 : (fastmode ? fastmodestep * enc->button : enc->button));

  cur = limit_value(cur, inc, min, max);
  return cur;
}

/* CharEncoder */
CharEncoder::CharEncoder()  : RangeEncoder(0, 37) {
}

char CharEncoder::getChar() {
  uint8_t val = getValue();
  if (val == 0) {
    return ' ';
  }
  if (val < 27)
    return val - 1+ 'A';
  else
    return (val - 27) + '0';
}

void CharEncoder::setChar(char c) {
  if (c >= 'A' && c <= 'Z') {
    setValue(c - 'A' + 1);
  } else if (c >= '0' && c <= '9') {
    setValue(c - '0' + 26 + 1);
  } else {
    setValue(0);
  }
}

/* notePitchEncoder */
NotePitchEncoder::NotePitchEncoder(char *_name) : RangeEncoder(0, 127, _name) {
  setName(_name);
}

void NotePitchEncoder::displayAt(int i) {
  char name[5];
  getNotePitch(getValue(), name);
  GUI.put_string_at(i * 4, name);
}

void MidiTrackEncoder::displayAt(int i) {
  GUI.put_value(i, getValue() + 1);
}

/* auto name CC encoder */
void AutoNameCCEncoder::initCCEncoder(uint8_t _channel, uint8_t _cc) {
  CCEncoder::initCCEncoder(_channel, _cc);
  setCCName();
  GUI.redisplay();

}

<|endoftext|>


<commit_before>/**
 * @file visibility
 */

#include "visibility.h"
#include "forecast_time.h"
#include "level.h"
#include "logger_factory.h"
#include "plugin_factory.h"
#include "util.h"
#include <boost/lexical_cast.hpp>
#include <cmath>

#include "fetcher.h"
#include "hitool.h"
#include "neons.h"

using namespace std;
using namespace himan;
using namespace himan::plugin;

const double defaultVis = 40000;

// Lumi- tai räntäsateen intensiteetin "tehostus" [mm/h]
// Tällä siis tarkoitus saada mallin heikotkin lumisadeintensiteetit huonontamaan näkyvyyttä
const double pseudoRR = 0.13;

// Raja-arvo merkittävälle sumupilven määrälle [0..1]
const double stLimit = 0.55;

// Sumupilven max korkeus [m], 305m = 1000ft
const double stMaxH = 305.;

const himan::params PFParams({himan::param("PRECFORM2-N"), himan::param("PRECFORM-N")});
const himan::params RHParam({himan::param("RH-PRCNT"), himan::param("RH-0TO1")});
const himan::param RRParam(himan::param("RRR-KGM2"));
const himan::params NParam({himan::param("N-PRCNT"), himan::param("N-0TO1")});

// ..and their levels
const himan::level NLevel(himan::kHeight, 0, "HEIGHT");
const himan::level RHLevel(himan::kHeight, 2, "HEIGHT");

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF);
double VisibilityInMist(double stN, double stH, double RR, double RH);

visibility::visibility()
{
	itsClearTextFormula = "<algorithm>";
	itsLogger = logger_factory::Instance()->GetLog("visibility");
}

void visibility::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({param("VV2-M", 407)});

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void visibility::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	auto myThreadedLogger =
	    logger_factory::Instance()->GetLog("visibilityThread #" + boost::lexical_cast<string>(threadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger->Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                       static_cast<string>(forecastLevel));

	info_t RHInfo = Fetch(forecastTime, RHLevel, RHParam, forecastType, false);
	info_t PFInfo = Fetch(forecastTime, NLevel, PFParams, forecastType, false);
	info_t RRInfo = Fetch(forecastTime, NLevel, RRParam, forecastType, false);

	if (!RRInfo || !RHInfo || !PFInfo)
	{
		myThreadedLogger->Warning("Skipping step " + boost::lexical_cast<string>(forecastTime.Step()) + ", level " +
		                          static_cast<string>(forecastLevel));
		return;
	}

	double RHScale = 1;

	if (itsConfiguration->SourceProducer().Id() == 1 || itsConfiguration->SourceProducer().Id() == 199 ||
	    itsConfiguration->SourceProducer().Id() == 4)
	{
		RHScale = 100;
	}

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());

	// Alle 304m (1000ft) sumupilven (max) määrä [0..1]
	auto stN = h->VerticalMaximum(NParam, 0, stMaxH);

	// Stratus <15m (~0-50ft)
	auto st15 = h->VerticalMaximum(NParam, 0, 15);

	// Stratus 15-45m (~50-150ft)
	auto st45 = h->VerticalMaximum(NParam, 15, 45);

	// Sumupilven korkeus [m]
	auto stH = h->VerticalHeightGreaterThan(NParam, 0, stMaxH, stLimit);

	// Jos sumupilveä ohut kerros (vain) ~alimmalla mallipinnalla, jätetään alin kerros huomioimatta
	// (ehkä mieluummin ylempää keskiarvo, jottei tällöin mahdollinen ylempi st-kerros huononna näkyvyyttä liikaa?)
	auto stHup = h->VerticalHeightGreaterThan(NParam, 15, stMaxH, stLimit);
	auto stNup = h->VerticalMaximum(NParam, 15, stMaxH);

	assert(stH.size() == stHup.size());
	assert(st15.size() == stH.size());

	for (size_t i = 0; i < stH.size(); i++)
	{
		if (st15[i] > stLimit && st45[i] < stLimit)
		{
			stN[i] = stNup[i];
			stH[i] = stHup[i];
		}
	}

	string deviceType = "CPU";

	auto& target = VEC(myTargetInfo);

	for (auto&& tup : zip_range(target, VEC(PFInfo), VEC(RRInfo), VEC(RHInfo), stN, stH))
	{
		double& result = tup.get<0>();
		double PF = tup.get<1>();
		double RR = tup.get<2>();
		double RH = tup.get<3>();
		double stratN = tup.get<4>();
		double stratH = tup.get<5>();

		if (RR == kFloatMissing || RH == kFloatMissing)
		{
			continue;
		}

		assert(stratN <= 1.0);
		assert(RR < 50);

		stratN *= 100;  // Note! Cloudiness is scaled to percents

		RH *= RHScale;

		assert(RH < 102.);

		double visPre = defaultVis;

		if (RR > 0)
		{
			visPre = VisibilityInRain(stratN, stratH, RR, RH, PF);
		}

		double visMist = VisibilityInMist(stratN, stratH, RR, RH);

		// Choose lower visibility to be the end result

		result = fmin(visMist, visPre);
	}

	myThreadedLogger->Info("[" + deviceType + "] Missing values: " +
	                       boost::lexical_cast<string>(myTargetInfo->Data().MissingCount()) + "/" +
	                       boost::lexical_cast<string>(myTargetInfo->Data().Size()));
}

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF)
{
	double visPre = defaultVis;

	// Näkyvyyden utuisuuskerroin sateessa 2m suhteellisen kosteuden perusteella [0,85...8,5, kun 100%<rh<10%]
	// (jos rh<85%, näkyvyyttä parannetaan)

	const double RHpre = 85. / RH;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilven (alle 1000ft) määrän perusteella [1...0,85, kun
	// 50<N<100%]
	// Alle 50% sumupilven määrä ei siis huononna näkyvyyttä sateessa

	const double stNpre = (stN >= 50) ? log(50) / log(stN) : 1;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilvikorkeuden perusteella [0,68...1, kun stH=12...152m]
	// Yli 152m (500ft) korkeudella oleva sumupilvi ei siis huononna näkyvyyttä sateessa

	const double stHpre = (stH != kFloatMissing && stH < 152) ? pow((stH / 152), 0.15) : 1;

	assert(PF != kFloatMissing);

	switch (PF)
	{
		case 0:
		case 4:
			// Tihku (tai jäätävä tihku)
			// Nakyvyys intensiteetin perusteella
			visPre = 1. / RR * 500;
			break;

		case 1:
		case 5:
			// Vesisade (tai jäätävä vesisade)
			// Nakyvyys intensiteetin perusteella
			// (kaava antaa ehkä turhan huonoja <4000m näkyvyyksiä reippaassa RR>4 vesisateessa)
			visPre = 1 / RR * 6000 + 2000;
			break;

		case 3:
			// Snow
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 1400;
			break;

		case 2:
			// Sleet
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 2000;
			break;
		default:
			throw runtime_error("New unhandled precipitation form value: " + to_string(PF));
	}

	// Mahdollinen lisähuononnus utuisuuden perusteella
	visPre = visPre * RHpre * stNpre * stHpre;

	return fmin(visPre, defaultVis);
}

double VisibilityInMist(double stN, double stH, double RR, double RH)
{
	double visMist = defaultVis;

	// Näkyvyyden utuisuuskerroin udussa/sumussa sumupilven määrän perusteella [7,5...0,68, kun stN = 0...100%]
	const double stNmist = 75. / (stN + 10);

	// Nakyvyyden utuisuuskerroin udussa/sumussa sumupilvikorkeuden perusteella [ 0,47...1, kun par500 = 50...999ft]
	const double stHmist = (stH != kFloatMissing && stH < 305) ? pow((stH / 0.3048 / 1000), 0.25) : 1;

	// Nakyvyys udussa/sumussa, lasketaan myos heikossa sateessa (tarkoituksena tasoittaa suuria
	// nakyvyysgradientteja sateen reunalla)
	// (ehka syyta rajata vain tilanteisiin, jossa sateen perusteella saatu nakyvyys oli viela >8000?)

	if ((RR < 0.5 || RR == kFloatMissing) && RH > 80)
	{
		// Näkyvyys udussa/sumussa
		// Yksinkertaistetty kaava, eli lasketaan samalla tavalla riippumatta siitä, onko pakkasta vai ei
		// (pakkaset eri tavalla voisi olla parempi tapa)

		// Alkuarvo suht. kosteuden perusteella [700-31400m, kun 100<=RH<80]
		visMist = pow((101 - RH), 1.25) * 700;
		// Lisamuokkaus sumupilven maaran ja korkeuden perusteella
		visMist = visMist * stNmist * stHmist;
	}

	return fmin(visMist, defaultVis);
}
<commit_msg>STU-5685: change max to average to mimic smarttool<commit_after>/**
 * @file visibility
 */

#include "visibility.h"
#include "forecast_time.h"
#include "level.h"
#include "logger_factory.h"
#include "plugin_factory.h"
#include "util.h"
#include <boost/lexical_cast.hpp>
#include <cmath>

#include "fetcher.h"
#include "hitool.h"
#include "neons.h"

using namespace std;
using namespace himan;
using namespace himan::plugin;

const double defaultVis = 40000;

// Lumi- tai räntäsateen intensiteetin "tehostus" [mm/h]
// Tällä siis tarkoitus saada mallin heikotkin lumisadeintensiteetit huonontamaan näkyvyyttä
const double pseudoRR = 0.13;

// Raja-arvo merkittävälle sumupilven määrälle [0..1]
const double stLimit = 0.55;

// Sumupilven max korkeus [m], 305m = 1000ft
const double stMaxH = 305.;

const himan::params PFParams({himan::param("PRECFORM2-N"), himan::param("PRECFORM-N")});
const himan::params RHParam({himan::param("RH-PRCNT"), himan::param("RH-0TO1")});
const himan::param RRParam(himan::param("RRR-KGM2"));
const himan::params NParam({himan::param("N-PRCNT"), himan::param("N-0TO1")});

// ..and their levels
const himan::level NLevel(himan::kHeight, 0, "HEIGHT");
const himan::level RHLevel(himan::kHeight, 2, "HEIGHT");

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF);
double VisibilityInMist(double stN, double stH, double RR, double RH);

visibility::visibility()
{
	itsClearTextFormula = "<algorithm>";
	itsLogger = logger_factory::Instance()->GetLog("visibility");
}

void visibility::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({param("VV2-M", 407)});

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void visibility::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	auto myThreadedLogger =
	    logger_factory::Instance()->GetLog("visibilityThread #" + boost::lexical_cast<string>(threadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger->Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                       static_cast<string>(forecastLevel));

	info_t RHInfo = Fetch(forecastTime, RHLevel, RHParam, forecastType, false);
	info_t PFInfo = Fetch(forecastTime, NLevel, PFParams, forecastType, false);
	info_t RRInfo = Fetch(forecastTime, NLevel, RRParam, forecastType, false);

	if (!RRInfo || !RHInfo || !PFInfo)
	{
		myThreadedLogger->Warning("Skipping step " + boost::lexical_cast<string>(forecastTime.Step()) + ", level " +
		                          static_cast<string>(forecastLevel));
		return;
	}

	double RHScale = 1;

	if (itsConfiguration->SourceProducer().Id() == 1 || itsConfiguration->SourceProducer().Id() == 199 ||
	    itsConfiguration->SourceProducer().Id() == 4)
	{
		RHScale = 100;
	}

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());

	// Alle 304m (1000ft) sumupilven (max) määrä [0..1]
	auto stN = h->VerticalMaximum(NParam, 0, stMaxH);

	// Stratus <15m (~0-50ft)
	auto st15 = h->VerticalAverage(NParam, 0, 15);

	// Stratus 15-45m (~50-150ft)
	auto st45 = h->VerticalAverage(NParam, 15, 45);

	// Sumupilven korkeus [m]
	auto stH = h->VerticalHeightGreaterThan(NParam, 0, stMaxH, stLimit);

	// Jos sumupilveä ohut kerros (vain) ~alimmalla mallipinnalla, jätetään alin kerros huomioimatta
	// (ehkä mieluummin ylempää keskiarvo, jottei tällöin mahdollinen ylempi st-kerros huononna näkyvyyttä liikaa?)
	auto stHup = h->VerticalHeightGreaterThan(NParam, 15, stMaxH, stLimit);
	auto stNup = h->VerticalMaximum(NParam, 15, stMaxH);

	assert(stH.size() == stHup.size());
	assert(st15.size() == stH.size());

	for (size_t i = 0; i < stH.size(); i++)
	{
		if (st15[i] > stLimit && st45[i] < stLimit)
		{
			stN[i] = stNup[i];
			stH[i] = stHup[i];
		}
	}

	string deviceType = "CPU";

	auto& target = VEC(myTargetInfo);

	for (auto&& tup : zip_range(target, VEC(PFInfo), VEC(RRInfo), VEC(RHInfo), stN, stH))
	{
		double& result = tup.get<0>();
		double PF = tup.get<1>();
		double RR = tup.get<2>();
		double RH = tup.get<3>();
		double stratN = tup.get<4>();
		double stratH = tup.get<5>();

		if (RR == kFloatMissing || RH == kFloatMissing)
		{
			continue;
		}

		assert(stratN <= 1.0);
		assert(RR < 50);

		stratN *= 100;  // Note! Cloudiness is scaled to percents

		RH *= RHScale;

		assert(RH < 102.);

		double visPre = defaultVis;

		if (RR > 0)
		{
			visPre = VisibilityInRain(stratN, stratH, RR, RH, PF);
		}

		double visMist = VisibilityInMist(stratN, stratH, RR, RH);

		// Choose lower visibility to be the end result

		result = fmin(visMist, visPre);
	}

	myThreadedLogger->Info("[" + deviceType + "] Missing values: " +
	                       boost::lexical_cast<string>(myTargetInfo->Data().MissingCount()) + "/" +
	                       boost::lexical_cast<string>(myTargetInfo->Data().Size()));
}

double VisibilityInRain(double stN, double stH, double RR, double RH, int PF)
{
	double visPre = defaultVis;

	// Näkyvyyden utuisuuskerroin sateessa 2m suhteellisen kosteuden perusteella [0,85...8,5, kun 100%<rh<10%]
	// (jos rh<85%, näkyvyyttä parannetaan)

	const double RHpre = 85. / RH;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilven (alle 1000ft) määrän perusteella [1...0,85, kun
	// 50<N<100%]
	// Alle 50% sumupilven määrä ei siis huononna näkyvyyttä sateessa

	const double stNpre = (stN >= 50) ? log(50) / log(stN) : 1;

	// Näkyvyyden utuisuuskerroin sateessa matalan sumupilvikorkeuden perusteella [0,68...1, kun stH=12...152m]
	// Yli 152m (500ft) korkeudella oleva sumupilvi ei siis huononna näkyvyyttä sateessa

	const double stHpre = (stH != kFloatMissing && stH < 152) ? pow((stH / 152), 0.15) : 1;

	assert(PF != kFloatMissing);

	switch (PF)
	{
		case 0:
		case 4:
			// Tihku (tai jäätävä tihku)
			// Nakyvyys intensiteetin perusteella
			visPre = 1. / RR * 500;
			break;

		case 1:
		case 5:
			// Vesisade (tai jäätävä vesisade)
			// Nakyvyys intensiteetin perusteella
			// (kaava antaa ehkä turhan huonoja <4000m näkyvyyksiä reippaassa RR>4 vesisateessa)
			visPre = 1 / RR * 6000 + 2000;
			break;

		case 3:
			// Snow
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 1400;
			break;

		case 2:
			// Sleet
			// Näkyvyys intensiteetin perusteella
			visPre = 1 / (RR + pseudoRR) * 2000;
			break;
		default:
			throw runtime_error("New unhandled precipitation form value: " + to_string(PF));
	}

	// Mahdollinen lisähuononnus utuisuuden perusteella
	visPre = visPre * RHpre * stNpre * stHpre;

	return fmin(visPre, defaultVis);
}

double VisibilityInMist(double stN, double stH, double RR, double RH)
{
	double visMist = defaultVis;

	// Näkyvyyden utuisuuskerroin udussa/sumussa sumupilven määrän perusteella [7,5...0,68, kun stN = 0...100%]
	const double stNmist = 75. / (stN + 10);

	// Nakyvyyden utuisuuskerroin udussa/sumussa sumupilvikorkeuden perusteella [ 0,47...1, kun par500 = 50...999ft]
	const double stHmist = (stH != kFloatMissing && stH < 305) ? pow((stH / 0.3048 / 1000), 0.25) : 1;

	// Nakyvyys udussa/sumussa, lasketaan myos heikossa sateessa (tarkoituksena tasoittaa suuria
	// nakyvyysgradientteja sateen reunalla)
	// (ehka syyta rajata vain tilanteisiin, jossa sateen perusteella saatu nakyvyys oli viela >8000?)

	if ((RR < 0.5 || RR == kFloatMissing) && RH > 80)
	{
		// Näkyvyys udussa/sumussa
		// Yksinkertaistetty kaava, eli lasketaan samalla tavalla riippumatta siitä, onko pakkasta vai ei
		// (pakkaset eri tavalla voisi olla parempi tapa)

		// Alkuarvo suht. kosteuden perusteella [700-31400m, kun 100<=RH<80]
		visMist = pow((101 - RH), 1.25) * 700;
		// Lisamuokkaus sumupilven maaran ja korkeuden perusteella
		visMist = visMist * stNmist * stHmist;
	}

	return fmin(visMist, defaultVis);
}
<|endoftext|>


<commit_before>#pragma once
/*
 * Copyright (c) 2012, 2013 Aldebaran Robotics. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the COPYING file.
 */

#ifndef _QI_APPLICATION_HPP_
# define _QI_APPLICATION_HPP_

# include <functional>
# include <vector>
# include <qi/api.hpp>

namespace qi {

  /**
   * \includename{qi/application.hpp}
   * \brief Class handling startup and teardown of an application.
   *
   * \verbatim
   * The :cpp:class:`qi::Application` class is designed to ease
   * startup and teardown of an executable.
   *
   * All executables using qi classes should create an instance of
   * :cpp:class:`qi::Application` on the stack of the main() function.
   * \endverbatim
   */
  class QI_API Application
  {
  public:
    /**
     * \brief Application constructor. Must be the first thing called by main().
     * \param argc Argument counter of the program.
     * \param argv Arguments of the program (given to main).
     * \param name The name of the program. It will be returned by name().
     * \param path The full path to the program if you wish to override it. It
     * will be returned by program() but not realProgram().
     * \throw std::logic_error When the constructor is called twice.
     */
    Application(int& argc, char** &argv, const std::string& name = "", const std::string& path = "");
    /**
     * \brief Application constructor. Must be the first thing called by main().
     * \param name Name of the application.
     * \param argc Argument counter of the program.
     * \param argv Arguments of the program (given to main).
     * \throw std::logic_error When the constructor is called twice.
     * \deprecated Use Application(int&, char**&, const std::string&, const
     * std::string&)
     */
    QI_API_DEPRECATED Application(const std::string &name, int& argc, char** &argv);
    /**
     * \brief Application destructor. It executes atExit() callbacks.
     * \see qi:Application::atExit
     * \see QI_AT_EXIT
     */
    ~Application();

    /**
     * \brief Wait until the end of the program.
     *
     * \verbatim
     * Wait until one of those conditions becomes true:
     * - stop() is called.
     * - TERM or QUIT signal is received.
     * - the Application instance is destroyed, which means main() is exiting.
     *
     * Run can be called by multiple threads simultaneously.
     * \endverbatim
     */
    static void run();
    /**
     * \brief Stop the application. Call all atStop handlers.
     */
    static void stop();

    /**
     * \brief Get arguments of the program as an std::vector of std::string.
     * \return List of arguments of the program.
     */
    static const std::vector<std::string>& arguments();
    /**
     * \brief Get argument counter of the program.
     * \return Argument counter of the program if Application was initialized,
     *         -1 otherwise.
     */
    static int argc();
    /**
     * \brief Get string arguments of the program (including program name).
     * \return Arguments of the program if Application was initialized, 0 otherwise.
     */
    static const char** argv();
    /**
     * \brief Set application name.
     * \param name The application's name.
     */
    static void setName(const std::string &name);
    /**
     * \brief Get application name.
     * \return A string with the application name,
     *         empty string if setName isn't call.
     */
    static std::string name();
    /**
     * \brief Set arguments of the program with argc as argument counter and argv as
     *        argument values.
     * \param argc Argument counter of the program.
     * \param argv Arguments of the program (given to main).
     */
    static void setArguments(int argc, char** argv);
    /**
     * \brief Set arguments ot the program as an std::vector of std::string.
     * \param arguments Sets arguments with a vector of strings.
     */
    static void setArguments(const std::vector<std::string>& arguments);

    /**
     * \brief Load a module into the current process.
     * \param name The module path and name. If no extension is used, the
     *        correct extension for a library on the current platform is used.
     * \param flags Extra flags to pass to the dlopen function.
     * \return A handle, to be used by qi::os::dlsym() or unloadModule().
     *
     * \verbatim
     * The module can execute code when loaded by using :cpp:macro:`QI_AT_ENTER`.
     * \endverbatim
     */
    static void* loadModule(const std::string& name, int flags=-1);
    /**
     * \brief Unload a module from the current process.
     * \param handle Handle on the loaded module.
     */
    static void unloadModule(void* handle);
    /**
     * \brief Check whether the Application instance is terminated or not.
     * \return True if it is stop, false otherwise.
     */
    static bool terminated();
    /**
     * \brief Check whether the Application instance was initialized or not.
     * \return True if it was initialized, false otherwise.
     */
    static bool initialized();

    /**
     * \brief Return the current program full path according to argv[0]
     * \return full path to the current running program, symbolic links are not
     * resolved.
     * \see realProgram
     */
    static const char* program();

    /**
     * \brief Return the current program full path.
     * \return full path to the current running program, symbolic links are
     * resolved.
     *
     * When using this function from a Python application for example on
     * gentoo, it will return something like /usr/bin/python2.7 (because python
     * is a symlink to python-wrapper which will exec() /usr/bin/python2.7). On
     * the other hand, program() will return the full path to the script run by
     * the Python interpreter.
     *
     * \verbatim
     * Computed using specific OS API:
     *
     * - Apple  : _NSGetExecutablePath
     * - Linux  : reading "/proc/self/exe"
     * - Windows: GetModuleFileName
     *
     * If the former API fail it will try to guess the value from argv[0].
     * For this method to work :cpp:func:`qi::Application(int&, char**&)` should
     * have been called in your main().
     * \endverbatim
     */
    static const char* realProgram();

    /**
     * \brief Return the SDK path given through --qi-sdk-prefix or QI_SDK_PREFIX
     *
     * Used internally, you should not need this.
     */
    static const char* _suggestedSdkPath();

    /**
     * \brief Return the SDK path given through QI_ADDITIONAL_SDK_PREFIXES
     *
     * Used internally, you should not need this.
     */
    static const std::vector<std::string>& _suggestedSdkPaths();

    /**
     * \brief Register a function to be executed at Application creation.
     * \param func Callback function at Application creation.
     * \return True if registering succeeded, false otherwise.
     */
    static bool atEnter(std::function<void()> func);

    /**
     * \brief Register a function to be executed at Application destruction.
     * \param func Callback function called at Application destruction.
     * \return True if registering succeeded, false otherwise.
     */
    static bool atExit(std::function<void()> func);
    /**
     * \brief Register a function to be executed when stop() is called.
     * The functions are executed sequentially before run() returns.
     * \param func Callback function called when stop() is called.
     * \return True if registering succeeded, false otherwise.
     */
    static bool atStop(std::function<void()> func);

    /**
     * \brief Register a function to be executed when a signal occurs.
     * \param func Callback function called on signal.
     * \param signal Signal number.
     * \return True if registering succeeded, false otherwise.
     *
     * The handler is executed in a thread, not from within the signal handler,
     * so there is no restriction on what can be done by your handler function,
     * except that it should return reasonably quickly.
     */
    static bool atSignal(std::function<void(int)> func, int signal);
  };
}

/**
 * \brief calls qi::Application::atEnter(func) at static initialization time.
 * \param func The handler that must be called at enter.
 */
#define QI_AT_ENTER(func)                                               \
  static bool QI_UNIQ_DEF(_qi_atenter) = ::qi::Application::atEnter(func);

/**
 * \def QI_AT_EXIT(func)
 * \brief calls qi::Application::atExit(func) at static initialization time.
 * \param func The handler that must be called at exit.
 */
#define QI_AT_EXIT(func)                                                \
  static bool QI_UNIQ_DEF(_qi_atexit) = ::qi::Application::atExit(func);

//THIS IS INTERNAL
//API is not maintained for this function
//The user need to include <boost/program_options.hpp> and <boost/bind.hpp>
//Use like this:
//namespace {
//  _QI_COMMAND_LINE_OPTIONS(
//    "Name of category",
//    (option1)
//    (option2)
//  )
//}
#define _QI_COMMAND_LINE_OPTIONS(desc, opts)                            \
  static void QI_UNIQ_DEF(_qi_opt_func)() {                             \
    namespace po = boost::program_options;                              \
    po::variables_map vm;                                               \
    po::command_line_parser p(::qi::Application::arguments());          \
    po::options_description options(desc);                              \
    {                                                                   \
      using namespace boost::program_options;                           \
      options.add_options() opts;                                       \
    }                                                                   \
    po::parsed_options res = p.options(options)                         \
      .allow_unregistered()                                             \
      .run();                                                           \
    po::store(res, vm);                                                 \
    /* Invoke notify callbacks*/                                        \
    po::notify(vm);                                                     \
    {                                                                   \
      po::options_description descTmp;                                  \
      descTmp.add_options()                                             \
        ("help,h", "");                                                 \
      po::variables_map vmTmp;                                          \
      po::store(po::command_line_parser(qi::Application::arguments())   \
                .options(descTmp).allow_unregistered().run(), vmTmp);   \
      if (vmTmp.count("help"))                                          \
        std::cout << options << std::endl;                              \
    }                                                                   \
    std::vector<std::string> args                                       \
      = po::collect_unrecognized(res.options, po::include_positional);  \
    /* Set arguments to what was not used */                            \
    ::qi::Application::setArguments(args);                              \
  }                                                                     \
  QI_AT_ENTER(boost::bind(&(QI_UNIQ_DEF(_qi_opt_func))))

#endif  // _QI_APPLICATION_HPP_
<commit_msg>Fix compilation error with default string assignation<commit_after>#pragma once
/*
 * Copyright (c) 2012, 2013 Aldebaran Robotics. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the COPYING file.
 */

#ifndef _QI_APPLICATION_HPP_
# define _QI_APPLICATION_HPP_

# include <functional>
# include <vector>
# include <string>
# include <qi/api.hpp>

namespace qi {

  /**
   * \includename{qi/application.hpp}
   * \brief Class handling startup and teardown of an application.
   *
   * \verbatim
   * The :cpp:class:`qi::Application` class is designed to ease
   * startup and teardown of an executable.
   *
   * All executables using qi classes should create an instance of
   * :cpp:class:`qi::Application` on the stack of the main() function.
   * \endverbatim
   */
  class QI_API Application
  {
  public:
    /**
     * \brief Application constructor. Must be the first thing called by main().
     * \param argc Argument counter of the program.
     * \param argv Arguments of the program (given to main).
     * \param name The name of the program. It will be returned by name().
     * \param path The full path to the program if you wish to override it. It
     * will be returned by program() but not realProgram().
     * \throw std::logic_error When the constructor is called twice.
     */
    Application(int& argc, char** &argv, const std::string& name = "", const std::string& path = "");
    /**
     * \brief Application constructor. Must be the first thing called by main().
     * \param name Name of the application.
     * \param argc Argument counter of the program.
     * \param argv Arguments of the program (given to main).
     * \throw std::logic_error When the constructor is called twice.
     * \deprecated Use Application(int&, char**&, const std::string&, const
     * std::string&)
     */
    QI_API_DEPRECATED Application(const std::string &name, int& argc, char** &argv);
    /**
     * \brief Application destructor. It executes atExit() callbacks.
     * \see qi:Application::atExit
     * \see QI_AT_EXIT
     */
    ~Application();

    /**
     * \brief Wait until the end of the program.
     *
     * \verbatim
     * Wait until one of those conditions becomes true:
     * - stop() is called.
     * - TERM or QUIT signal is received.
     * - the Application instance is destroyed, which means main() is exiting.
     *
     * Run can be called by multiple threads simultaneously.
     * \endverbatim
     */
    static void run();
    /**
     * \brief Stop the application. Call all atStop handlers.
     */
    static void stop();

    /**
     * \brief Get arguments of the program as an std::vector of std::string.
     * \return List of arguments of the program.
     */
    static const std::vector<std::string>& arguments();
    /**
     * \brief Get argument counter of the program.
     * \return Argument counter of the program if Application was initialized,
     *         -1 otherwise.
     */
    static int argc();
    /**
     * \brief Get string arguments of the program (including program name).
     * \return Arguments of the program if Application was initialized, 0 otherwise.
     */
    static const char** argv();
    /**
     * \brief Set application name.
     * \param name The application's name.
     */
    static void setName(const std::string &name);
    /**
     * \brief Get application name.
     * \return A string with the application name,
     *         empty string if setName isn't call.
     */
    static std::string name();
    /**
     * \brief Set arguments of the program with argc as argument counter and argv as
     *        argument values.
     * \param argc Argument counter of the program.
     * \param argv Arguments of the program (given to main).
     */
    static void setArguments(int argc, char** argv);
    /**
     * \brief Set arguments ot the program as an std::vector of std::string.
     * \param arguments Sets arguments with a vector of strings.
     */
    static void setArguments(const std::vector<std::string>& arguments);

    /**
     * \brief Load a module into the current process.
     * \param name The module path and name. If no extension is used, the
     *        correct extension for a library on the current platform is used.
     * \param flags Extra flags to pass to the dlopen function.
     * \return A handle, to be used by qi::os::dlsym() or unloadModule().
     *
     * \verbatim
     * The module can execute code when loaded by using :cpp:macro:`QI_AT_ENTER`.
     * \endverbatim
     */
    static void* loadModule(const std::string& name, int flags=-1);
    /**
     * \brief Unload a module from the current process.
     * \param handle Handle on the loaded module.
     */
    static void unloadModule(void* handle);
    /**
     * \brief Check whether the Application instance is terminated or not.
     * \return True if it is stop, false otherwise.
     */
    static bool terminated();
    /**
     * \brief Check whether the Application instance was initialized or not.
     * \return True if it was initialized, false otherwise.
     */
    static bool initialized();

    /**
     * \brief Return the current program full path according to argv[0]
     * \return full path to the current running program, symbolic links are not
     * resolved.
     * \see realProgram
     */
    static const char* program();

    /**
     * \brief Return the current program full path.
     * \return full path to the current running program, symbolic links are
     * resolved.
     *
     * When using this function from a Python application for example on
     * gentoo, it will return something like /usr/bin/python2.7 (because python
     * is a symlink to python-wrapper which will exec() /usr/bin/python2.7). On
     * the other hand, program() will return the full path to the script run by
     * the Python interpreter.
     *
     * \verbatim
     * Computed using specific OS API:
     *
     * - Apple  : _NSGetExecutablePath
     * - Linux  : reading "/proc/self/exe"
     * - Windows: GetModuleFileName
     *
     * If the former API fail it will try to guess the value from argv[0].
     * For this method to work :cpp:func:`qi::Application(int&, char**&)` should
     * have been called in your main().
     * \endverbatim
     */
    static const char* realProgram();

    /**
     * \brief Return the SDK path given through --qi-sdk-prefix or QI_SDK_PREFIX
     *
     * Used internally, you should not need this.
     */
    static const char* _suggestedSdkPath();

    /**
     * \brief Return the SDK path given through QI_ADDITIONAL_SDK_PREFIXES
     *
     * Used internally, you should not need this.
     */
    static const std::vector<std::string>& _suggestedSdkPaths();

    /**
     * \brief Register a function to be executed at Application creation.
     * \param func Callback function at Application creation.
     * \return True if registering succeeded, false otherwise.
     */
    static bool atEnter(std::function<void()> func);

    /**
     * \brief Register a function to be executed at Application destruction.
     * \param func Callback function called at Application destruction.
     * \return True if registering succeeded, false otherwise.
     */
    static bool atExit(std::function<void()> func);
    /**
     * \brief Register a function to be executed when stop() is called.
     * The functions are executed sequentially before run() returns.
     * \param func Callback function called when stop() is called.
     * \return True if registering succeeded, false otherwise.
     */
    static bool atStop(std::function<void()> func);

    /**
     * \brief Register a function to be executed when a signal occurs.
     * \param func Callback function called on signal.
     * \param signal Signal number.
     * \return True if registering succeeded, false otherwise.
     *
     * The handler is executed in a thread, not from within the signal handler,
     * so there is no restriction on what can be done by your handler function,
     * except that it should return reasonably quickly.
     */
    static bool atSignal(std::function<void(int)> func, int signal);
  };
}

/**
 * \brief calls qi::Application::atEnter(func) at static initialization time.
 * \param func The handler that must be called at enter.
 */
#define QI_AT_ENTER(func)                                               \
  static bool QI_UNIQ_DEF(_qi_atenter) = ::qi::Application::atEnter(func);

/**
 * \def QI_AT_EXIT(func)
 * \brief calls qi::Application::atExit(func) at static initialization time.
 * \param func The handler that must be called at exit.
 */
#define QI_AT_EXIT(func)                                                \
  static bool QI_UNIQ_DEF(_qi_atexit) = ::qi::Application::atExit(func);

//THIS IS INTERNAL
//API is not maintained for this function
//The user need to include <boost/program_options.hpp> and <boost/bind.hpp>
//Use like this:
//namespace {
//  _QI_COMMAND_LINE_OPTIONS(
//    "Name of category",
//    (option1)
//    (option2)
//  )
//}
#define _QI_COMMAND_LINE_OPTIONS(desc, opts)                            \
  static void QI_UNIQ_DEF(_qi_opt_func)() {                             \
    namespace po = boost::program_options;                              \
    po::variables_map vm;                                               \
    po::command_line_parser p(::qi::Application::arguments());          \
    po::options_description options(desc);                              \
    {                                                                   \
      using namespace boost::program_options;                           \
      options.add_options() opts;                                       \
    }                                                                   \
    po::parsed_options res = p.options(options)                         \
      .allow_unregistered()                                             \
      .run();                                                           \
    po::store(res, vm);                                                 \
    /* Invoke notify callbacks*/                                        \
    po::notify(vm);                                                     \
    {                                                                   \
      po::options_description descTmp;                                  \
      descTmp.add_options()                                             \
        ("help,h", "");                                                 \
      po::variables_map vmTmp;                                          \
      po::store(po::command_line_parser(qi::Application::arguments())   \
                .options(descTmp).allow_unregistered().run(), vmTmp);   \
      if (vmTmp.count("help"))                                          \
        std::cout << options << std::endl;                              \
    }                                                                   \
    std::vector<std::string> args                                       \
      = po::collect_unrecognized(res.options, po::include_positional);  \
    /* Set arguments to what was not used */                            \
    ::qi::Application::setArguments(args);                              \
  }                                                                     \
  QI_AT_ENTER(boost::bind(&(QI_UNIQ_DEF(_qi_opt_func))))

#endif  // _QI_APPLICATION_HPP_
<|endoftext|>


<commit_before>#include "catch.hpp"

#include "util/url.h"

using namespace Tangram;

TEST_CASE("Parse components of a correctly formatted URL", "[Url]") {

    // Tests conformance to https://tools.ietf.org/html/rfc1808#section-2.1

    Url url("https://tile.mapzen.com/mapzen/vector/v1/all/0/0/0.mvt;param=val?api_key=mapsRcool#yolo");

    CHECK(!url.isEmpty());
    CHECK(url.isAbsolute());
    CHECK(!url.hasDataScheme());
    CHECK(!url.hasBase64Data());
    CHECK(!url.hasFileScheme());
    CHECK(url.hasHttpScheme());
    CHECK(url.hasScheme());
    CHECK(url.scheme() == "https");
    CHECK(url.hasNetLocation());
    CHECK(url.netLocation() == "vector.mapzen.com");
    CHECK(url.hasPath());
    CHECK(url.path() == "/osm/all/0/0/0.mvt");
    CHECK(url.hasParameters());
    CHECK(url.parameters() == "param=val");
    CHECK(url.hasQuery());
    CHECK(url.query() == "api_key=mapsRcool");
    CHECK(url.hasFragment());
    CHECK(url.fragment() == "yolo");
    CHECK(!url.hasMediaType());
    CHECK(!url.hasData());

}

TEST_CASE("Parse components of a correctly formatted data URI", "[Url]") {

    // Tests conformance to https://tools.ietf.org/html/rfc2397#section-3

    Url url("data:text/html;charset=utf-8;base64,YmFzZTY0");

    CHECK(!url.isEmpty());
    CHECK(url.isAbsolute());
    CHECK(url.hasDataScheme());
    CHECK(url.hasBase64Data());
    CHECK(!url.hasFileScheme());
    CHECK(!url.hasHttpScheme());
    CHECK(url.hasScheme());
    CHECK(url.scheme() == "data");
    CHECK(!url.hasNetLocation());
    CHECK(!url.hasParameters());
    CHECK(!url.hasQuery());
    CHECK(!url.hasFragment());
    CHECK(url.hasMediaType());
    CHECK(url.mediaType() == "text/html;charset=utf-8");
    CHECK(url.hasData());
    CHECK(url.data() == "YmFzZTY0");

}

TEST_CASE("Parse an empty URL", "[Url]") {

    Url url("");

    CHECK(url.isEmpty());
    CHECK(!url.isAbsolute());
    CHECK(!url.hasDataScheme());
    CHECK(!url.hasBase64Data());
    CHECK(!url.hasScheme());
    CHECK(!url.hasNetLocation());
    CHECK(!url.hasPath());
    CHECK(!url.hasParameters());
    CHECK(!url.hasQuery());
    CHECK(!url.hasFragment());
    CHECK(!url.hasMediaType());
    CHECK(!url.hasData());

}

TEST_CASE("Remove dot segments from a path", "[Url]") {

    // Tests conformance to https://tools.ietf.org/html/rfc3986#section-5.2.4

    CHECK(Url::removeDotSegmentsFromString("") == "");
    CHECK(Url::removeDotSegmentsFromString("a/b/c") == "a/b/c");
    CHECK(Url::removeDotSegmentsFromString("a/b=?.;5/c") == "a/b=?.;5/c");
    CHECK(Url::removeDotSegmentsFromString("/a/b/c/./../../g") == "/a/g");
    CHECK(Url::removeDotSegmentsFromString("../a/b") == "a/b");
    CHECK(Url::removeDotSegmentsFromString("./") == "");
    CHECK(Url::removeDotSegmentsFromString("..") == "");
    CHECK(Url::removeDotSegmentsFromString("a/b/../../..") == "");
    CHECK(Url::removeDotSegmentsFromString("a/b/../c/../d/./e/..") == "a/d");
    CHECK(Url::removeDotSegmentsFromString("a//b//c") == "a//b//c");
    CHECK(Url::removeDotSegmentsFromString("a./b../..c/.d") == "a./b../..c/.d");

}

TEST_CASE("Produce a 'standardized' URL", "[Url]") {

    CHECK(Url("http://example.com/path/oops/not/here/../../../file.txt;p?q#f").standardized().string() == "http://example.com/path/file.txt;p?q#f");
    CHECK(Url("http://example.com/../../no/going/back/file.txt;p?q#f").standardized().string() == "http://example.com/no/going/back/file.txt;p?q#f");
    CHECK(Url("data:text/html;charset=utf-8,LoremIpsum").standardized().string() == "data:text/html;charset=utf-8,LoremIpsum");

}

TEST_CASE("Maintain URL components when 'standardized'", "[URL]") {

    Url url(Url("http://mapzen.com/nothing/to/see/here/../../../../index.html;p?q#f").standardized());

    CHECK(!url.isEmpty());
    CHECK(url.isAbsolute());
    CHECK(!url.hasDataScheme());
    CHECK(!url.hasBase64Data());
    CHECK(!url.hasFileScheme());
    CHECK(url.hasHttpScheme());
    CHECK(url.hasScheme());
    CHECK(url.scheme() == "http");
    CHECK(url.hasNetLocation());
    CHECK(url.netLocation() == "mapzen.com");
    CHECK(url.hasPath());
    CHECK(url.path() == "/index.html");
    CHECK(url.hasParameters());
    CHECK(url.parameters() == "p");
    CHECK(url.hasQuery());
    CHECK(url.query() == "q");
    CHECK(url.hasFragment());
    CHECK(url.fragment() == "f");
    CHECK(!url.hasMediaType());
    CHECK(!url.hasData());

}

TEST_CASE("Resolve a URL against an absolute base URL", "[Url]") {

    // https://tools.ietf.org/html/rfc3986#section-5.4.1

    Url base("http://a/b/c/d;p?q");

    CHECK(Url("g:h").resolved(base).string() == "g:h");
    CHECK(Url("g").resolved(base).string() == "http://a/b/c/g");
    CHECK(Url("./g").resolved(base).string() == "http://a/b/c/g");
    CHECK(Url("g/").resolved(base).string() == "http://a/b/c/g/");
    CHECK(Url("/g").resolved(base).string() == "http://a/g");
    CHECK(Url("?y").resolved(base).string() == "http://a/b/c/d;p?y");
    CHECK(Url("g?y").resolved(base).string() == "http://a/b/c/g?y");
    CHECK(Url("#s").resolved(base).string() == "http://a/b/c/d;p?q#s");
    CHECK(Url("g#s").resolved(base).string() == "http://a/b/c/g#s");
    CHECK(Url("g?y#s").resolved(base).string() == "http://a/b/c/g?y#s");
    CHECK(Url(";x").resolved(base).string() == "http://a/b/c/d;x"); // See [1] below.
    CHECK(Url("g;x").resolved(base).string() == "http://a/b/c/g;x");
    CHECK(Url("g;x?y#s").resolved(base).string() == "http://a/b/c/g;x?y#s");
    CHECK(Url("").resolved(base).string() == "http://a/b/c/d;p?q");

}

TEST_CASE("Resolve a URL against a relative base URL", "[Url]") {

    Url base("a/b/c/d;p?q");

    CHECK(Url("g:h").resolved(base).string() == "g:h");
    CHECK(Url("g").resolved(base).string() == "a/b/c/g");
    CHECK(Url("./g").resolved(base).string() == "a/b/c/g");
    CHECK(Url("g/").resolved(base).string() == "a/b/c/g/");
    CHECK(Url("/g").resolved(base).string() == "/g");
    CHECK(Url("?y").resolved(base).string() == "a/b/c/d;p?y");
    CHECK(Url("g?y").resolved(base).string() == "a/b/c/g?y");
    CHECK(Url("#s").resolved(base).string() == "a/b/c/d;p?q#s");
    CHECK(Url("g#s").resolved(base).string() == "a/b/c/g#s");
    CHECK(Url("g?y#s").resolved(base).string() == "a/b/c/g?y#s");
    CHECK(Url(";x").resolved(base).string() == "a/b/c/d;x"); // See [1] below.
    CHECK(Url("g;x").resolved(base).string() == "a/b/c/g;x");
    CHECK(Url("g;x?y#s").resolved(base).string() == "a/b/c/g;x?y#s");
    CHECK(Url("").resolved(base).string() == "a/b/c/d;p?q");

}

TEST_CASE("Resolve a relative URL against an empty base URL", "[Url]") {

    Url base("");

    CHECK(Url("g:h").resolved(base).string() == "g:h");
    CHECK(Url("g").resolved(base).string() == "g");
    CHECK(Url("./g").resolved(base).string() == "g");
    CHECK(Url("g/").resolved(base).string() == "g/");
    CHECK(Url("/g").resolved(base).string() == "/g");
    CHECK(Url("?y").resolved(base).string() == "?y");
    CHECK(Url("g?y").resolved(base).string() == "g?y");
    CHECK(Url("#s").resolved(base).string() == "#s");
    CHECK(Url("g#s").resolved(base).string() == "g#s");
    CHECK(Url("g?y#s").resolved(base).string() == "g?y#s");
    CHECK(Url(";x").resolved(base).string() == ";x");
    CHECK(Url("g;x").resolved(base).string() == "g;x");
    CHECK(Url("g;x?y#s").resolved(base).string() == "g;x?y#s");
    CHECK(Url("").resolved(base).string() == "");

}

TEST_CASE("Resolve an abnormal relative URL against an absolute base URL", "[Url]") {

    // https://tools.ietf.org/html/rfc3986#section-5.4.2

    Url base("http://a/b/c/d;p?q");

    CHECK(Url("../../../g").resolved(base).string() == "http://a/g");
    CHECK(Url("../../../../g").resolved(base).string() == "http://a/g");
    CHECK(Url("/./g").resolved(base).string() == "http://a/g");
    CHECK(Url("/../g").resolved(base).string() == "http://a/g");
    CHECK(Url("g.").resolved(base).string() == "http://a/b/c/g.");
    CHECK(Url(".g").resolved(base).string() == "http://a/b/c/.g");
    CHECK(Url("g..").resolved(base).string() == "http://a/b/c/g..");
    CHECK(Url("..g").resolved(base).string() == "http://a/b/c/..g");
    CHECK(Url("./../g").resolved(base).string() == "http://a/b/g");
    CHECK(Url("./g/.").resolved(base).string() == "http://a/b/c/g/");
    CHECK(Url("g/./h").resolved(base).string() == "http://a/b/c/g/h");
    CHECK(Url("g/../h").resolved(base).string() == "http://a/b/c/h");
    CHECK(Url("g;x=1/./y").resolved(base).string() == "http://a/b/c/g;x=1/./y"); // See [1] below.
    CHECK(Url("g;x=1/../y").resolved(base).string() == "http://a/b/c/g;x=1/../y"); // See [1] below.

}

// [1]:
// Some of the examples for path resolution given in RFC 3986 don't produce the same result
// in our implementation because the interpretation of the parameters string in RFC 3986 is
// in conflict with RFC 1808, which many URL utilities adhere to (e.g. NSURL). In RFC 1808
// the 'path' component stops at a ';', but in RFC 3986 it goes up to a '?'.
<commit_msg>Fix URL test inputs<commit_after>#include "catch.hpp"

#include "util/url.h"

using namespace Tangram;

TEST_CASE("Parse components of a correctly formatted URL", "[Url]") {

    // Tests conformance to https://tools.ietf.org/html/rfc1808#section-2.1

    Url url("https://some.domain:9000/path/to/file.html;param=val?api_key=mapsRcool#yolo");

    CHECK(!url.isEmpty());
    CHECK(url.isAbsolute());
    CHECK(!url.hasDataScheme());
    CHECK(!url.hasBase64Data());
    CHECK(!url.hasFileScheme());
    CHECK(url.hasHttpScheme());
    CHECK(url.hasScheme());
    CHECK(url.scheme() == "https");
    CHECK(url.hasNetLocation());
    CHECK(url.netLocation() == "some.domain:9000");
    CHECK(url.hasPath());
    CHECK(url.path() == "/path/to/file.html");
    CHECK(url.hasParameters());
    CHECK(url.parameters() == "param=val");
    CHECK(url.hasQuery());
    CHECK(url.query() == "api_key=mapsRcool");
    CHECK(url.hasFragment());
    CHECK(url.fragment() == "yolo");
    CHECK(!url.hasMediaType());
    CHECK(!url.hasData());

}

TEST_CASE("Parse components of a correctly formatted data URI", "[Url]") {

    // Tests conformance to https://tools.ietf.org/html/rfc2397#section-3

    Url url("data:text/html;charset=utf-8;base64,YmFzZTY0");

    CHECK(!url.isEmpty());
    CHECK(url.isAbsolute());
    CHECK(url.hasDataScheme());
    CHECK(url.hasBase64Data());
    CHECK(!url.hasFileScheme());
    CHECK(!url.hasHttpScheme());
    CHECK(url.hasScheme());
    CHECK(url.scheme() == "data");
    CHECK(!url.hasNetLocation());
    CHECK(!url.hasParameters());
    CHECK(!url.hasQuery());
    CHECK(!url.hasFragment());
    CHECK(url.hasMediaType());
    CHECK(url.mediaType() == "text/html;charset=utf-8");
    CHECK(url.hasData());
    CHECK(url.data() == "YmFzZTY0");

}

TEST_CASE("Parse an empty URL", "[Url]") {

    Url url("");

    CHECK(url.isEmpty());
    CHECK(!url.isAbsolute());
    CHECK(!url.hasDataScheme());
    CHECK(!url.hasBase64Data());
    CHECK(!url.hasScheme());
    CHECK(!url.hasNetLocation());
    CHECK(!url.hasPath());
    CHECK(!url.hasParameters());
    CHECK(!url.hasQuery());
    CHECK(!url.hasFragment());
    CHECK(!url.hasMediaType());
    CHECK(!url.hasData());

}

TEST_CASE("Remove dot segments from a path", "[Url]") {

    // Tests conformance to https://tools.ietf.org/html/rfc3986#section-5.2.4

    CHECK(Url::removeDotSegmentsFromString("") == "");
    CHECK(Url::removeDotSegmentsFromString("a/b/c") == "a/b/c");
    CHECK(Url::removeDotSegmentsFromString("a/b=?.;5/c") == "a/b=?.;5/c");
    CHECK(Url::removeDotSegmentsFromString("/a/b/c/./../../g") == "/a/g");
    CHECK(Url::removeDotSegmentsFromString("../a/b") == "a/b");
    CHECK(Url::removeDotSegmentsFromString("./") == "");
    CHECK(Url::removeDotSegmentsFromString("..") == "");
    CHECK(Url::removeDotSegmentsFromString("a/b/../../..") == "");
    CHECK(Url::removeDotSegmentsFromString("a/b/../c/../d/./e/..") == "a/d");
    CHECK(Url::removeDotSegmentsFromString("a//b//c") == "a//b//c");
    CHECK(Url::removeDotSegmentsFromString("a./b../..c/.d") == "a./b../..c/.d");

}

TEST_CASE("Produce a 'standardized' URL", "[Url]") {

    CHECK(Url("http://example.com/path/oops/not/here/../../../file.txt;p?q#f").standardized().string() == "http://example.com/path/file.txt;p?q#f");
    CHECK(Url("http://example.com/../../no/going/back/file.txt;p?q#f").standardized().string() == "http://example.com/no/going/back/file.txt;p?q#f");
    CHECK(Url("data:text/html;charset=utf-8,LoremIpsum").standardized().string() == "data:text/html;charset=utf-8,LoremIpsum");

}

TEST_CASE("Maintain URL components when 'standardized'", "[URL]") {

    Url url(Url("http://mapzen.com/nothing/to/see/here/../../../../index.html;p?q#f").standardized());

    CHECK(!url.isEmpty());
    CHECK(url.isAbsolute());
    CHECK(!url.hasDataScheme());
    CHECK(!url.hasBase64Data());
    CHECK(!url.hasFileScheme());
    CHECK(url.hasHttpScheme());
    CHECK(url.hasScheme());
    CHECK(url.scheme() == "http");
    CHECK(url.hasNetLocation());
    CHECK(url.netLocation() == "mapzen.com");
    CHECK(url.hasPath());
    CHECK(url.path() == "/index.html");
    CHECK(url.hasParameters());
    CHECK(url.parameters() == "p");
    CHECK(url.hasQuery());
    CHECK(url.query() == "q");
    CHECK(url.hasFragment());
    CHECK(url.fragment() == "f");
    CHECK(!url.hasMediaType());
    CHECK(!url.hasData());

}

TEST_CASE("Resolve a URL against an absolute base URL", "[Url]") {

    // https://tools.ietf.org/html/rfc3986#section-5.4.1

    Url base("http://a/b/c/d;p?q");

    CHECK(Url("g:h").resolved(base).string() == "g:h");
    CHECK(Url("g").resolved(base).string() == "http://a/b/c/g");
    CHECK(Url("./g").resolved(base).string() == "http://a/b/c/g");
    CHECK(Url("g/").resolved(base).string() == "http://a/b/c/g/");
    CHECK(Url("/g").resolved(base).string() == "http://a/g");
    CHECK(Url("?y").resolved(base).string() == "http://a/b/c/d;p?y");
    CHECK(Url("g?y").resolved(base).string() == "http://a/b/c/g?y");
    CHECK(Url("#s").resolved(base).string() == "http://a/b/c/d;p?q#s");
    CHECK(Url("g#s").resolved(base).string() == "http://a/b/c/g#s");
    CHECK(Url("g?y#s").resolved(base).string() == "http://a/b/c/g?y#s");
    CHECK(Url(";x").resolved(base).string() == "http://a/b/c/d;x"); // See [1] below.
    CHECK(Url("g;x").resolved(base).string() == "http://a/b/c/g;x");
    CHECK(Url("g;x?y#s").resolved(base).string() == "http://a/b/c/g;x?y#s");
    CHECK(Url("").resolved(base).string() == "http://a/b/c/d;p?q");

}

TEST_CASE("Resolve a URL against a relative base URL", "[Url]") {

    Url base("a/b/c/d;p?q");

    CHECK(Url("g:h").resolved(base).string() == "g:h");
    CHECK(Url("g").resolved(base).string() == "a/b/c/g");
    CHECK(Url("./g").resolved(base).string() == "a/b/c/g");
    CHECK(Url("g/").resolved(base).string() == "a/b/c/g/");
    CHECK(Url("/g").resolved(base).string() == "/g");
    CHECK(Url("?y").resolved(base).string() == "a/b/c/d;p?y");
    CHECK(Url("g?y").resolved(base).string() == "a/b/c/g?y");
    CHECK(Url("#s").resolved(base).string() == "a/b/c/d;p?q#s");
    CHECK(Url("g#s").resolved(base).string() == "a/b/c/g#s");
    CHECK(Url("g?y#s").resolved(base).string() == "a/b/c/g?y#s");
    CHECK(Url(";x").resolved(base).string() == "a/b/c/d;x"); // See [1] below.
    CHECK(Url("g;x").resolved(base).string() == "a/b/c/g;x");
    CHECK(Url("g;x?y#s").resolved(base).string() == "a/b/c/g;x?y#s");
    CHECK(Url("").resolved(base).string() == "a/b/c/d;p?q");

}

TEST_CASE("Resolve a relative URL against an empty base URL", "[Url]") {

    Url base("");

    CHECK(Url("g:h").resolved(base).string() == "g:h");
    CHECK(Url("g").resolved(base).string() == "g");
    CHECK(Url("./g").resolved(base).string() == "g");
    CHECK(Url("g/").resolved(base).string() == "g/");
    CHECK(Url("/g").resolved(base).string() == "/g");
    CHECK(Url("?y").resolved(base).string() == "?y");
    CHECK(Url("g?y").resolved(base).string() == "g?y");
    CHECK(Url("#s").resolved(base).string() == "#s");
    CHECK(Url("g#s").resolved(base).string() == "g#s");
    CHECK(Url("g?y#s").resolved(base).string() == "g?y#s");
    CHECK(Url(";x").resolved(base).string() == ";x");
    CHECK(Url("g;x").resolved(base).string() == "g;x");
    CHECK(Url("g;x?y#s").resolved(base).string() == "g;x?y#s");
    CHECK(Url("").resolved(base).string() == "");

}

TEST_CASE("Resolve an abnormal relative URL against an absolute base URL", "[Url]") {

    // https://tools.ietf.org/html/rfc3986#section-5.4.2

    Url base("http://a/b/c/d;p?q");

    CHECK(Url("../../../g").resolved(base).string() == "http://a/g");
    CHECK(Url("../../../../g").resolved(base).string() == "http://a/g");
    CHECK(Url("/./g").resolved(base).string() == "http://a/g");
    CHECK(Url("/../g").resolved(base).string() == "http://a/g");
    CHECK(Url("g.").resolved(base).string() == "http://a/b/c/g.");
    CHECK(Url(".g").resolved(base).string() == "http://a/b/c/.g");
    CHECK(Url("g..").resolved(base).string() == "http://a/b/c/g..");
    CHECK(Url("..g").resolved(base).string() == "http://a/b/c/..g");
    CHECK(Url("./../g").resolved(base).string() == "http://a/b/g");
    CHECK(Url("./g/.").resolved(base).string() == "http://a/b/c/g/");
    CHECK(Url("g/./h").resolved(base).string() == "http://a/b/c/g/h");
    CHECK(Url("g/../h").resolved(base).string() == "http://a/b/c/h");
    CHECK(Url("g;x=1/./y").resolved(base).string() == "http://a/b/c/g;x=1/./y"); // See [1] below.
    CHECK(Url("g;x=1/../y").resolved(base).string() == "http://a/b/c/g;x=1/../y"); // See [1] below.

}

// [1]:
// Some of the examples for path resolution given in RFC 3986 don't produce the same result
// in our implementation because the interpretation of the parameters string in RFC 3986 is
// in conflict with RFC 1808, which many URL utilities adhere to (e.g. NSURL). In RFC 1808
// the 'path' component stops at a ';', but in RFC 3986 it goes up to a '?'.
<|endoftext|>


<commit_before>#include <stdio.h>
#include <QApplication>
#include <QFile>
#include <QLayout>
#include <QDebug>
#include <QRegExp>
#include <QTextDocument>
#include <QTextOption>
#include <rwindow.h>
#include "pageview.h"

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  app.setFont(QFont("Liberation Serif", 12));

  if (argc != 2) {
    fprintf(stderr, "Usage: runcible-open-ext-txt <file>");
    return 1;
  }

  QString filename(argv[1]);
  QFile file(filename);
  qDebug() << "Loading" << filename;
  QString text;
  if (file.open(QIODevice::ReadOnly)) {
    qDebug() << "Opened";
    text = file.readAll();
    qDebug() << "Loaded";
  }

  text.replace(QRegExp("([^\\r\\n])(\\r)?\\n([^\\n\\r])"), "\\1  \\3");

  RWindow window;
  PageView display;
  window.layout()->addWidget(&display);
  qDebug() << "Added display.";

  QObject::connect(&display, SIGNAL(pageCountChanged(int)), &window, SLOT(showTimeline(int)));
  QObject::connect(&display, SIGNAL(pageChanged(int)), &window, SLOT(updateTimeline(int)));

  QTextDocument doc;
  doc.setDefaultTextOption(QTextOption(Qt::AlignJustify));
  doc.setPlainText(text);
  qDebug() << "Created doc.";
  display.setDocument(&doc);
  qDebug() << "Set doc.";

  QObject::connect(&window, SIGNAL(back()), &app, SLOT(quit()));

  window.showMessage(filename);
  window.showMaximized();

  return app.exec();
}
<commit_msg>Added rudimentary HTML support in text renderer.  Binary must be symlinked to indicate format support.<commit_after>#include <stdio.h>
#include <QApplication>
#include <QFile>
#include <QFileInfo>
#include <QLayout>
#include <QDebug>
#include <QRegExp>
#include <QTextDocument>
#include <QTextOption>
#include <rwindow.h>
#include "pageview.h"

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  app.setFont(QFont("Liberation Serif", 12));

  if (argc != 2) {
    fprintf(stderr, "Usage: runcible-open-ext-txt <file>");
    return 1;
  }

  QString filename(argv[1]);
  QFile file(filename);
  QString suffix(QFileInfo(file).suffix());

  qDebug() << "Loading" << filename;
  QString text;
  if (file.open(QIODevice::ReadOnly)) {
    qDebug() << "Opened";
    text = file.readAll();
    qDebug() << "Loaded";
  }

  RWindow window;
  PageView display;
  window.layout()->addWidget(&display);
  qDebug() << "Added display.";

  QObject::connect(&display, SIGNAL(pageCountChanged(int)), &window, SLOT(showTimeline(int)));
  QObject::connect(&display, SIGNAL(pageChanged(int)), &window, SLOT(updateTimeline(int)));

  QTextDocument doc;
  doc.setDefaultTextOption(QTextOption(Qt::AlignJustify));
  if (suffix == "html") {
    doc.setHtml(text);
  } else {
    text.replace(QRegExp("([^\\r\\n])(\\r)?\\n([^\\n\\r])"), "\\1  \\3");
    doc.setPlainText(text);
  }
  qDebug() << "Created doc.";
  display.setDocument(&doc);
  qDebug() << "Set doc.";

  QObject::connect(&window, SIGNAL(back()), &app, SLOT(quit()));

  window.showMessage(filename);
  window.showMaximized();

  return app.exec();
}
<|endoftext|>