<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright 2019 ScyllaDB
 */

#include <seastar/core/future-util.hh>
#include <seastar/core/file.hh>
#include <seastar/core/fair_queue.hh>
#include <seastar/core/io_queue.hh>
#include <seastar/core/reactor.hh>
#include <seastar/core/metrics.hh>
#include <seastar/core/linux-aio.hh>
#include <seastar/core/internal/io_desc.hh>
#include <chrono>
#include <mutex>
#include <array>
#include <fmt/format.h>
#include <fmt/ostream.h>

namespace seastar {

using namespace std::chrono_literals;
using namespace internal::linux_abi;

class io_desc_read_write final : public kernel_completion {
    io_queue* _ioq_ptr;
    fair_queue_ticket _fq_ticket;
    promise<size_t> _pr;
private:
    void notify_requests_finished() {
        _ioq_ptr->notify_requests_finished(_fq_ticket);
    }
public:
    io_desc_read_write(io_queue* ioq, unsigned weight, unsigned size)
        : _ioq_ptr(ioq)
        , _fq_ticket(fair_queue_ticket{weight, size})
    {}

    fair_queue_ticket& fq_ticket() {
        return _fq_ticket;
    }

    void set_exception(std::exception_ptr eptr) {
        notify_requests_finished();
        _pr.set_exception(eptr);
        delete this;
    }

    virtual void complete_with(ssize_t ret) override {
        try {
            engine().handle_io_result(ret);
            notify_requests_finished();
            _pr.set_value(ret);
            delete this;
        } catch (...) {
            set_exception(std::current_exception());
        }
    }

    future<size_t> get_future() {
        return _pr.get_future();
    }
};

void
io_queue::notify_requests_finished(fair_queue_ticket& desc) {
    _requests_executing--;
    _completed_accumulator += desc;
}

void
io_queue::process_completions() {
    _fq.notify_requests_finished(std::exchange(_completed_accumulator, {}));
}

fair_queue::config io_queue::make_fair_queue_config(config iocfg) {
    fair_queue::config cfg;
    cfg.max_req_count = iocfg.max_req_count;
    cfg.max_bytes_count = iocfg.max_bytes_count;
    return cfg;
}

io_queue::io_queue(io_queue::config cfg)
    : _priority_classes()
    , _fq(make_fair_queue_config(cfg))
    , _config(std::move(cfg)) {
}

io_queue::~io_queue() {
    // It is illegal to stop the I/O queue with pending requests.
    // Technically we would use a gate to guarantee that. But here, it is not
    // needed since this is expected to be destroyed only after the reactor is destroyed.
    //
    // And that will happen only when there are no more fibers to run. If we ever change
    // that, then this has to change.
    for (auto&& pc_vec : _priority_classes) {
        for (auto&& pc_data : pc_vec) {
            if (pc_data) {
                _fq.unregister_priority_class(pc_data->ptr);
            }
        }
    }
}

std::mutex io_queue::_register_lock;
std::array<uint32_t, io_queue::_max_classes> io_queue::_registered_shares;
// We could very well just add the name to the io_priority_class. However, because that
// structure is passed along all the time - and sometimes we can't help but copy it, better keep
// it lean. The name won't really be used for anything other than monitoring.
std::array<sstring, io_queue::_max_classes> io_queue::_registered_names;

io_priority_class io_queue::register_one_priority_class(sstring name, uint32_t shares) {
    std::lock_guard<std::mutex> lock(_register_lock);
    for (unsigned i = 0; i < _max_classes; ++i) {
        if (!_registered_shares[i]) {
            _registered_shares[i] = shares;
            _registered_names[i] = std::move(name);
        } else if (_registered_names[i] != name) {
            continue;
        } else {
            // found an entry matching the name to be registered,
            // make sure it was registered with the same number shares
            // Note: those may change dynamically later on in the
            // fair queue priority_class_ptr
            assert(_registered_shares[i] == shares);
        }
        return io_priority_class(i);
    }
    throw std::runtime_error("No more room for new I/O priority classes");
}

seastar::metrics::label io_queue_shard("ioshard");

io_queue::priority_class_data::priority_class_data(sstring name, sstring mountpoint, priority_class_ptr ptr, shard_id owner)
    : ptr(ptr)
    , bytes(0)
    , ops(0)
    , nr_queued(0)
    , queue_time(1s)
{
    register_stats(name, mountpoint, owner);
}

void
io_queue::priority_class_data::rename(sstring new_name, sstring mountpoint, shard_id owner) {
    try {
        register_stats(new_name, mountpoint, owner);
    } catch (metrics::double_registration &e) {
        // we need to ignore this exception, since it can happen that
        // a class that was already created with the new name will be
        // renamed again (this will cause a double registration exception
        // to be thrown).
    }

}

void
io_queue::priority_class_data::register_stats(sstring name, sstring mountpoint, shard_id owner) {
    seastar::metrics::metric_groups new_metrics;
    namespace sm = seastar::metrics;
    auto shard = sm::impl::shard();

    auto ioq_group = sm::label("mountpoint");
    auto mountlabel = ioq_group(mountpoint);

    auto class_label_type = sm::label("class");
    auto class_label = class_label_type(name);
    new_metrics.add_group("io_queue", {
            sm::make_derive("total_bytes", bytes, sm::description("Total bytes passed in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            sm::make_derive("total_operations", ops, sm::description("Total bytes passed in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            // Note: The counter below is not the same as reactor's queued-io-requests
            // queued-io-requests shows us how many requests in total exist in this I/O Queue.
            //
            // This counter lives in the priority class, so it will count only queued requests
            // that belong to that class.
            //
            // In other words: the new counter tells you how busy a class is, and the
            // old counter tells you how busy the system is.

            sm::make_queue_length("queue_length", nr_queued, sm::description("Number of requests in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            sm::make_gauge("delay", [this] {
                return queue_time.count();
            }, sm::description("total delay time in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            sm::make_gauge("shares", [this] {
                return this->ptr->shares();
            }, sm::description("current amount of shares"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label})
    });
    _metric_groups = std::exchange(new_metrics, {});
}

io_queue::priority_class_data& io_queue::find_or_create_class(const io_priority_class& pc, shard_id owner) {
    auto id = pc.id();
    bool do_insert = false;
    if ((do_insert = (owner >= _priority_classes.size()))) {
        _priority_classes.resize(owner + 1);
        _priority_classes[owner].resize(id + 1);
    } else if ((do_insert = (id >= _priority_classes[owner].size()))) {
        _priority_classes[owner].resize(id + 1);
    }
    if (do_insert || !_priority_classes[owner][id]) {
        auto shares = _registered_shares.at(id);
        sstring name;
        {
            std::lock_guard<std::mutex> lock(_register_lock);
            name = _registered_names.at(id);
        }

        // A note on naming:
        //
        // We could just add the owner as the instance id and have something like:
        //  io_queue-<class_owner>-<counter>-<class_name>
        //
        // However, when there are more than one shard per I/O queue, it is very useful
        // to know which shards are being served by the same queue. Therefore, a better name
        // scheme is:
        //
        //  io_queue-<queue_owner>-<counter>-<class_name>, shard=<class_owner>
        //  using the shard label to hold the owner number
        //
        // This conveys all the information we need and allows one to easily group all classes from
        // the same I/O queue (by filtering by shard)
        auto pc_ptr = _fq.register_priority_class(shares);
        auto pc_data = make_lw_shared<priority_class_data>(name, mountpoint(), pc_ptr, owner);

        _priority_classes[owner][id] = pc_data;
    }
    return *_priority_classes[owner][id];
}

future<size_t>
io_queue::queue_request(const io_priority_class& pc, size_t len, internal::io_request req) noexcept {
    auto start = std::chrono::steady_clock::now();
    return smp::submit_to(coordinator(), [start, &pc, len, req = std::move(req), owner = this_shard_id(), this] () mutable {
        _queued_requests++;

        // First time will hit here, and then we create the class. It is important
        // that we create the shared pointer in the same shard it will be used at later.
        auto& pclass = find_or_create_class(pc, owner);
        pclass.nr_queued++;
        unsigned weight;
        size_t size;
        if (req.is_write()) {
            weight = _config.disk_req_write_to_read_multiplier;
            size = _config.disk_bytes_write_to_read_multiplier * len;
        } else if (req.is_read()) {
            weight = io_queue::read_request_base_count;
            size = io_queue::read_request_base_count * len;
        } else {
            throw std::runtime_error(fmt::format("Unrecognized request passing through I/O queue {}", req.opname()));
        }
        auto desc = std::make_unique<io_desc_read_write>(this, weight, size);
        auto fq_ticket = desc->fq_ticket();
        auto fut = desc->get_future();
        _fq.queue(pclass.ptr, std::move(fq_ticket), [&pclass, start, req = std::move(req), desc = desc.release(), len, this] () mutable noexcept {
            _queued_requests--;
            _requests_executing++;
            try {
                pclass.nr_queued--;
                pclass.ops++;
                pclass.bytes += len;
                pclass.queue_time = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - start);
                engine().submit_io(desc, std::move(req));
            } catch (...) {
                desc->set_exception(std::current_exception());
            }
        });
        return fut;
    });
}

future<>
io_queue::update_shares_for_class(const io_priority_class pc, size_t new_shares) {
    return smp::submit_to(coordinator(), [this, pc, owner = this_shard_id(), new_shares] {
        auto& pclass = find_or_create_class(pc, owner);
        _fq.update_shares(pclass.ptr, new_shares);
    });
}

void
io_queue::rename_priority_class(io_priority_class pc, sstring new_name) {
    for (unsigned owner = 0; owner < _priority_classes.size(); owner++) {
        if (_priority_classes[owner].size() > pc.id() &&
                _priority_classes[owner][pc.id()]) {
            _priority_classes[owner][pc.id()]->rename(new_name, _config.mountpoint, owner);
        }
    }
}

}
<commit_msg>io-queue: Dont leak desc<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright 2019 ScyllaDB
 */

#include <seastar/core/future-util.hh>
#include <seastar/core/file.hh>
#include <seastar/core/fair_queue.hh>
#include <seastar/core/io_queue.hh>
#include <seastar/core/reactor.hh>
#include <seastar/core/metrics.hh>
#include <seastar/core/linux-aio.hh>
#include <seastar/core/internal/io_desc.hh>
#include <chrono>
#include <mutex>
#include <array>
#include <fmt/format.h>
#include <fmt/ostream.h>

namespace seastar {

using namespace std::chrono_literals;
using namespace internal::linux_abi;

class io_desc_read_write final : public kernel_completion {
    io_queue* _ioq_ptr;
    fair_queue_ticket _fq_ticket;
    promise<size_t> _pr;
private:
    void notify_requests_finished() {
        _ioq_ptr->notify_requests_finished(_fq_ticket);
    }
public:
    io_desc_read_write(io_queue* ioq, unsigned weight, unsigned size)
        : _ioq_ptr(ioq)
        , _fq_ticket(fair_queue_ticket{weight, size})
    {}

    fair_queue_ticket& fq_ticket() {
        return _fq_ticket;
    }

    void set_exception(std::exception_ptr eptr) {
        notify_requests_finished();
        _pr.set_exception(eptr);
        delete this;
    }

    virtual void complete_with(ssize_t ret) override {
        try {
            engine().handle_io_result(ret);
            notify_requests_finished();
            _pr.set_value(ret);
            delete this;
        } catch (...) {
            set_exception(std::current_exception());
        }
    }

    future<size_t> get_future() {
        return _pr.get_future();
    }
};

void
io_queue::notify_requests_finished(fair_queue_ticket& desc) {
    _requests_executing--;
    _completed_accumulator += desc;
}

void
io_queue::process_completions() {
    _fq.notify_requests_finished(std::exchange(_completed_accumulator, {}));
}

fair_queue::config io_queue::make_fair_queue_config(config iocfg) {
    fair_queue::config cfg;
    cfg.max_req_count = iocfg.max_req_count;
    cfg.max_bytes_count = iocfg.max_bytes_count;
    return cfg;
}

io_queue::io_queue(io_queue::config cfg)
    : _priority_classes()
    , _fq(make_fair_queue_config(cfg))
    , _config(std::move(cfg)) {
}

io_queue::~io_queue() {
    // It is illegal to stop the I/O queue with pending requests.
    // Technically we would use a gate to guarantee that. But here, it is not
    // needed since this is expected to be destroyed only after the reactor is destroyed.
    //
    // And that will happen only when there are no more fibers to run. If we ever change
    // that, then this has to change.
    for (auto&& pc_vec : _priority_classes) {
        for (auto&& pc_data : pc_vec) {
            if (pc_data) {
                _fq.unregister_priority_class(pc_data->ptr);
            }
        }
    }
}

std::mutex io_queue::_register_lock;
std::array<uint32_t, io_queue::_max_classes> io_queue::_registered_shares;
// We could very well just add the name to the io_priority_class. However, because that
// structure is passed along all the time - and sometimes we can't help but copy it, better keep
// it lean. The name won't really be used for anything other than monitoring.
std::array<sstring, io_queue::_max_classes> io_queue::_registered_names;

io_priority_class io_queue::register_one_priority_class(sstring name, uint32_t shares) {
    std::lock_guard<std::mutex> lock(_register_lock);
    for (unsigned i = 0; i < _max_classes; ++i) {
        if (!_registered_shares[i]) {
            _registered_shares[i] = shares;
            _registered_names[i] = std::move(name);
        } else if (_registered_names[i] != name) {
            continue;
        } else {
            // found an entry matching the name to be registered,
            // make sure it was registered with the same number shares
            // Note: those may change dynamically later on in the
            // fair queue priority_class_ptr
            assert(_registered_shares[i] == shares);
        }
        return io_priority_class(i);
    }
    throw std::runtime_error("No more room for new I/O priority classes");
}

seastar::metrics::label io_queue_shard("ioshard");

io_queue::priority_class_data::priority_class_data(sstring name, sstring mountpoint, priority_class_ptr ptr, shard_id owner)
    : ptr(ptr)
    , bytes(0)
    , ops(0)
    , nr_queued(0)
    , queue_time(1s)
{
    register_stats(name, mountpoint, owner);
}

void
io_queue::priority_class_data::rename(sstring new_name, sstring mountpoint, shard_id owner) {
    try {
        register_stats(new_name, mountpoint, owner);
    } catch (metrics::double_registration &e) {
        // we need to ignore this exception, since it can happen that
        // a class that was already created with the new name will be
        // renamed again (this will cause a double registration exception
        // to be thrown).
    }

}

void
io_queue::priority_class_data::register_stats(sstring name, sstring mountpoint, shard_id owner) {
    seastar::metrics::metric_groups new_metrics;
    namespace sm = seastar::metrics;
    auto shard = sm::impl::shard();

    auto ioq_group = sm::label("mountpoint");
    auto mountlabel = ioq_group(mountpoint);

    auto class_label_type = sm::label("class");
    auto class_label = class_label_type(name);
    new_metrics.add_group("io_queue", {
            sm::make_derive("total_bytes", bytes, sm::description("Total bytes passed in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            sm::make_derive("total_operations", ops, sm::description("Total bytes passed in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            // Note: The counter below is not the same as reactor's queued-io-requests
            // queued-io-requests shows us how many requests in total exist in this I/O Queue.
            //
            // This counter lives in the priority class, so it will count only queued requests
            // that belong to that class.
            //
            // In other words: the new counter tells you how busy a class is, and the
            // old counter tells you how busy the system is.

            sm::make_queue_length("queue_length", nr_queued, sm::description("Number of requests in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            sm::make_gauge("delay", [this] {
                return queue_time.count();
            }, sm::description("total delay time in the queue"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label}),
            sm::make_gauge("shares", [this] {
                return this->ptr->shares();
            }, sm::description("current amount of shares"), {io_queue_shard(shard), sm::shard_label(owner), mountlabel, class_label})
    });
    _metric_groups = std::exchange(new_metrics, {});
}

io_queue::priority_class_data& io_queue::find_or_create_class(const io_priority_class& pc, shard_id owner) {
    auto id = pc.id();
    bool do_insert = false;
    if ((do_insert = (owner >= _priority_classes.size()))) {
        _priority_classes.resize(owner + 1);
        _priority_classes[owner].resize(id + 1);
    } else if ((do_insert = (id >= _priority_classes[owner].size()))) {
        _priority_classes[owner].resize(id + 1);
    }
    if (do_insert || !_priority_classes[owner][id]) {
        auto shares = _registered_shares.at(id);
        sstring name;
        {
            std::lock_guard<std::mutex> lock(_register_lock);
            name = _registered_names.at(id);
        }

        // A note on naming:
        //
        // We could just add the owner as the instance id and have something like:
        //  io_queue-<class_owner>-<counter>-<class_name>
        //
        // However, when there are more than one shard per I/O queue, it is very useful
        // to know which shards are being served by the same queue. Therefore, a better name
        // scheme is:
        //
        //  io_queue-<queue_owner>-<counter>-<class_name>, shard=<class_owner>
        //  using the shard label to hold the owner number
        //
        // This conveys all the information we need and allows one to easily group all classes from
        // the same I/O queue (by filtering by shard)
        auto pc_ptr = _fq.register_priority_class(shares);
        auto pc_data = make_lw_shared<priority_class_data>(name, mountpoint(), pc_ptr, owner);

        _priority_classes[owner][id] = pc_data;
    }
    return *_priority_classes[owner][id];
}

future<size_t>
io_queue::queue_request(const io_priority_class& pc, size_t len, internal::io_request req) noexcept {
    auto start = std::chrono::steady_clock::now();
    return smp::submit_to(coordinator(), [start, &pc, len, req = std::move(req), owner = this_shard_id(), this] () mutable {
        _queued_requests++;

        // First time will hit here, and then we create the class. It is important
        // that we create the shared pointer in the same shard it will be used at later.
        auto& pclass = find_or_create_class(pc, owner);
        pclass.nr_queued++;
        unsigned weight;
        size_t size;
        if (req.is_write()) {
            weight = _config.disk_req_write_to_read_multiplier;
            size = _config.disk_bytes_write_to_read_multiplier * len;
        } else if (req.is_read()) {
            weight = io_queue::read_request_base_count;
            size = io_queue::read_request_base_count * len;
        } else {
            throw std::runtime_error(fmt::format("Unrecognized request passing through I/O queue {}", req.opname()));
        }
        auto desc = std::make_unique<io_desc_read_write>(this, weight, size);
        auto fq_ticket = desc->fq_ticket();
        auto fut = desc->get_future();
        _fq.queue(pclass.ptr, std::move(fq_ticket), [&pclass, start, req = std::move(req), d = std::move(desc), len, this] () mutable noexcept {
            io_desc_read_write* desc = d.release();
            _queued_requests--;
            _requests_executing++;
            try {
                pclass.nr_queued--;
                pclass.ops++;
                pclass.bytes += len;
                pclass.queue_time = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - start);
                engine().submit_io(desc, std::move(req));
            } catch (...) {
                desc->set_exception(std::current_exception());
            }
        });
        return fut;
    });
}

future<>
io_queue::update_shares_for_class(const io_priority_class pc, size_t new_shares) {
    return smp::submit_to(coordinator(), [this, pc, owner = this_shard_id(), new_shares] {
        auto& pclass = find_or_create_class(pc, owner);
        _fq.update_shares(pclass.ptr, new_shares);
    });
}

void
io_queue::rename_priority_class(io_priority_class pc, sstring new_name) {
    for (unsigned owner = 0; owner < _priority_classes.size(); owner++) {
        if (_priority_classes[owner].size() > pc.id() &&
                _priority_classes[owner][pc.id()]) {
            _priority_classes[owner][pc.id()]->rename(new_name, _config.mountpoint, owner);
        }
    }
}

}
<|endoftext|>


<commit_before>/*******************************************************************************
    Taichi - Physically based Computer Graphics Library

    Copyright (c) 2017 Yuanming Hu <yuanmhu@gmail.com>

    All rights reserved. Use of this source code is governed by
    the MIT license as written in the LICENSE file.
*******************************************************************************/

#include <taichi/util.h>
#include <taichi/system/threading.h>
#include <csignal>
#include <spdlog/spdlog.h>

TC_NAMESPACE_BEGIN

void signal_handler(int signo);

#define TC_REGISTER_SIGNAL_HANDLER(name, handler)                    \
  {                                                                  \
    if (std::signal(name, handler) == SIG_ERR)                       \
      std::printf("Can not register signal handler for" #name "\n"); \
  }

Logger::Logger() {
  console = spdlog::stdout_color_mt("console");
  TC_LOG_SET_PATTERN("[%L %D %X.%e] %v")

  TC_REGISTER_SIGNAL_HANDLER(SIGSEGV, signal_handler);
  TC_REGISTER_SIGNAL_HANDLER(SIGABRT, signal_handler);
  TC_REGISTER_SIGNAL_HANDLER(SIGBUS, signal_handler);
  TC_REGISTER_SIGNAL_HANDLER(SIGFPE, signal_handler);
  spdlog::set_level(spdlog::level::trace);
  TC_TRACE("Taichi core started. Thread ID = {}", PID::get_pid());
}

void Logger::trace(const std::string &s) {
  console->trace(s);
}

void Logger::debug(const std::string &s) {
  console->debug(s);
}

void Logger::info(const std::string &s) {
  console->info(s);
}

void Logger::warn(const std::string &s) {
  console->warn(s);
}
void Logger::error(const std::string &s, bool raise_signal) {
  console->error(s);
  if (raise_signal) {
    std::raise(SIGABRT);
  }
}
void Logger::critical(const std::string &s, bool raise_signal) {
  console->critical(s);
  if (raise_signal) {
    std::raise(SIGABRT);
  }
}
void Logger::flush() {
  console->flush();
}

Logger logger;

void signal_handler(int signo) {
  logger.error(fmt::format("Received signal {} ({})", signo, strsignal(signo)),
               false);
  TC_FLUSH_LOGGER;
  taichi::print_traceback();
  if (taichi::CoreState::get_instance().python_imported) {
    std::string msg =
        fmt::format("Taichi Core Exception: {} ({})", signo, strsignal(signo));
    taichi_raise_assertion_failure_in_python(msg.c_str());
  }
  std::exit(-1);
}

TC_NAMESPACE_END
<commit_msg>trigger gdb when crash<commit_after>/*******************************************************************************
    Taichi - Physically based Computer Graphics Library

    Copyright (c) 2017 Yuanming Hu <yuanmhu@gmail.com>

    All rights reserved. Use of this source code is governed by
    the MIT license as written in the LICENSE file.
*******************************************************************************/

#include <taichi/util.h>
#include <taichi/system/threading.h>
#include <csignal>
#include <spdlog/spdlog.h>

TC_NAMESPACE_BEGIN

void signal_handler(int signo);

#define TC_REGISTER_SIGNAL_HANDLER(name, handler)                    \
  {                                                                  \
    if (std::signal(name, handler) == SIG_ERR)                       \
      std::printf("Can not register signal handler for" #name "\n"); \
  }

Logger::Logger() {
  console = spdlog::stdout_color_mt("console");
  TC_LOG_SET_PATTERN("[%L %D %X.%e] %v")

  TC_REGISTER_SIGNAL_HANDLER(SIGSEGV, signal_handler);
  TC_REGISTER_SIGNAL_HANDLER(SIGABRT, signal_handler);
  TC_REGISTER_SIGNAL_HANDLER(SIGBUS, signal_handler);
  TC_REGISTER_SIGNAL_HANDLER(SIGFPE, signal_handler);
  spdlog::set_level(spdlog::level::trace);
  TC_TRACE("Taichi core started. Thread ID = {}", PID::get_pid());
}

void Logger::trace(const std::string &s) {
  console->trace(s);
}

void Logger::debug(const std::string &s) {
  console->debug(s);
}

void Logger::info(const std::string &s) {
  console->info(s);
}

void Logger::warn(const std::string &s) {
  console->warn(s);
}
void Logger::error(const std::string &s, bool raise_signal) {
  console->error(s);
  if (raise_signal) {
    std::raise(SIGABRT);
  }
}
void Logger::critical(const std::string &s, bool raise_signal) {
  console->critical(s);
  if (raise_signal) {
    std::raise(SIGABRT);
  }
}
void Logger::flush() {
  console->flush();
}

Logger logger;

void signal_handler(int signo) {
  logger.error(fmt::format("Received signal {} ({})", signo, strsignal(signo)),
               false);
  TC_FLUSH_LOGGER;
  taichi::print_traceback();
#ifdef __linux__
  if (false)
    system(fmt::format("sudo gdb -p {}", PID::get_pid()).c_str());
#endif
  if (taichi::CoreState::get_instance().python_imported) {
    std::string msg =
        fmt::format("Taichi Core Exception: {} ({})", signo, strsignal(signo));
    taichi_raise_assertion_failure_in_python(msg.c_str());
  }
  std::exit(-1);
}

TC_NAMESPACE_END
<|endoftext|>


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

  This source file is part of the Avogadro project.

  Copyright 2013 Kitware, Inc.

  Adapted from Avogadro 1.x with the following authors' permission:
  Copyright 2007 Donald Ephraim Curtis
  Copyright 2008 Marcus D. Hanwell

  This source code is released under the New BSD License, (the "License").

  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 "measuretool.h"

#include <avogadro/qtopengl/glwidget.h>

#include <avogadro/rendering/geometrynode.h>
#include <avogadro/rendering/glrenderer.h>
#include <avogadro/rendering/groupnode.h>
#include <avogadro/rendering/scene.h>
#include <avogadro/rendering/textlabel2d.h>
#include <avogadro/rendering/textlabel3d.h>
#include <avogadro/rendering/textproperties.h>

#include <avogadro/core/atom.h>
#include <avogadro/core/elements.h>
#include <avogadro/core/vector.h>
#include <avogadro/qtgui/molecule.h>
#include <avogadro/qtgui/rwmolecule.h>

#include <QtGui/QIcon>
#include <QtGui/QMouseEvent>
#include <QtWidgets/QAction>

#include <QDebug>

#include <cmath>

using Avogadro::Core::Atom;
using Avogadro::Core::Elements;
using Avogadro::Rendering::GeometryNode;
using Avogadro::Rendering::GroupNode;
using Avogadro::Rendering::Identifier;
using Avogadro::Rendering::TextLabel2D;
using Avogadro::Rendering::TextLabel3D;
using Avogadro::Rendering::TextProperties;

namespace Avogadro {
namespace QtPlugins {

MeasureTool::MeasureTool(QObject* parent_)
  : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)),
    m_molecule(nullptr), m_rwMolecule(nullptr), m_renderer(nullptr)
{
  m_activateAction->setText(tr("Measure"));
  m_activateAction->setIcon(QIcon(":/icons/measuretool.png"));
}

MeasureTool::~MeasureTool()
{
}

QWidget* MeasureTool::toolWidget() const
{
  return nullptr;
}

QUndoCommand* MeasureTool::mousePressEvent(QMouseEvent* e)
{
  if (e->button() != Qt::LeftButton || !m_renderer)
    return nullptr;

  Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y());

  // If an atom is clicked, accept the event, but don't add it to the atom list
  // until the button is released (this way the user can cancel the click by
  // moving off the atom, and the click won't get passed to the default tool).
  if (hit.type == Rendering::AtomType)
    e->accept();

  return nullptr;
}

QUndoCommand* MeasureTool::mouseReleaseEvent(QMouseEvent* e)
{
  // If the click is released on an atom, add it to the list
  if (e->button() != Qt::LeftButton || !m_renderer)
    return nullptr;

  Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y());

  // Now add the atom on release.
  if (hit.type == Rendering::AtomType) {
    if (toggleAtom(hit))
      emit drawablesChanged();
    e->accept();
  }

  return nullptr;
}

QUndoCommand* MeasureTool::mouseDoubleClickEvent(QMouseEvent* e)
{
  // Reset the atom list
  if (e->button() == Qt::LeftButton && !m_atoms.isEmpty()) {
    m_atoms.clear();
    emit drawablesChanged();
    e->accept();
  }
  return nullptr;
}

template <typename T>
void MeasureTool::createLabels(T* mol, GeometryNode* geo,
                               QVector<Vector3>& positions)
{
  TextProperties atomLabelProp;
  atomLabelProp.setFontFamily(TextProperties::SansSerif);
  atomLabelProp.setAlign(TextProperties::HCenter, TextProperties::VCenter);

  for (int i = 0; i < m_atoms.size(); ++i) {
    Identifier& ident = m_atoms[i];
    Q_ASSERT(ident.type == Rendering::AtomType);
    Q_ASSERT(ident.molecule != nullptr);

    typename T::AtomType atom = mol->atom(ident.index);
    Q_ASSERT(atom.isValid());
    unsigned char atomicNumber(atom.atomicNumber());
    positions[i] = atom.position3d();

    const unsigned char* color = Elements::color(atomicNumber);
    atomLabelProp.setColorRgb(contrastingColor(Vector3ub(color)).data());

    TextLabel3D* label = new TextLabel3D;
    label->setText(QString("#%1").arg(i + 1).toStdString());
    label->setTextProperties(atomLabelProp);
    label->setAnchor(positions[i].cast<float>());
    label->setRadius(
      static_cast<float>(Elements::radiusCovalent(atomicNumber)));
    geo->addDrawable(label);
  }
}

void MeasureTool::draw(Rendering::GroupNode& node)
{
  if (m_atoms.size() == 0)
    return;

  GeometryNode* geo = new GeometryNode;
  node.addChild(geo);

  // Add labels, extract positions
  QVector<Vector3> positions(m_atoms.size(), Vector3());
  if (m_molecule)
    createLabels(m_molecule, geo, positions);
  else if (m_rwMolecule)
    createLabels(m_rwMolecule, geo, positions);

  // Calculate angles and distances
  Vector3 v1;
  Vector3 v2;
  Vector3 v3;
  Real v1Norm = -1.f;
  Real v2Norm = -1.f;
  Real v3Norm = -1.f;

  switch (m_atoms.size()) {
    case 4:
      v3 = positions[3] - positions[2];
      v3Norm = v3.norm();
    case 3:
      v2 = positions[2] - positions[1];
      v2Norm = v2.norm();
    case 2:
      v1 = positions[1] - positions[0];
      v1Norm = v1.norm();
    default:
      break;
  }

  QString overlayText;
  float angle23 = 361.f;
  float angle12 = 361.f;
  QString dihedralLabel = tr("Dihedral:");
  QString angleLabel = tr("Angle:");
  QString distanceLabel = tr("Distance:");
  // Use the longest label size to determine the field width. Negate it to
  // indicate left-alignment.
  int labelWidth = -std::max(std::max(dihedralLabel.size(), angleLabel.size()),
                             distanceLabel.size());
  switch (m_atoms.size()) {
    case 4:
      overlayText += QString("%1 %L2\n")
                       .arg(tr("Dihedral:"), labelWidth)
                       .arg(dihedralAngle(v1, v2, v3), 10, 'f', 5);
      angle23 =
        static_cast<float>(std::acos((-v2).dot(v3) / (v2Norm * v3Norm))) *
        RAD_TO_DEG_F;
    // fall through
    case 3:
      angle12 =
        static_cast<float>(std::acos((-v1).dot(v2) / (v1Norm * v2Norm))) *
        RAD_TO_DEG_F;
      overlayText +=
        QString("%1 %L2 %L3\n")
          .arg(tr("Angles:"), labelWidth)
          .arg(angle12, 10, 'f', 5)
          .arg(angle23 < 360.f ? QString::number(angle23, 'f', 5) : QString(),
               10);
    // fall through
    case 2:
      overlayText +=
        QString("%1 %L2 %L3 %L4")
          .arg(tr("Distance:"), labelWidth)
          .arg(v1Norm, 10, 'f', 5)
          .arg(v2Norm >= 0.f ? QString::number(v2Norm, 'f', 5) : QString(), 10)
          .arg(v3Norm >= 0.f ? QString::number(v3Norm, 'f', 5) : QString(), 10);
    default:
      break;
  }

  if (overlayText.isEmpty())
    return;

  TextProperties overlayTProp;
  overlayTProp.setFontFamily(TextProperties::Mono);
  overlayTProp.setColorRgb(64, 255, 220);
  overlayTProp.setAlign(TextProperties::HLeft, TextProperties::VBottom);

  TextLabel2D* label = new TextLabel2D;
  label->setText(overlayText.toStdString());
  label->setTextProperties(overlayTProp);
  label->setRenderPass(Rendering::Overlay2DPass);
  label->setAnchor(Vector2i(10, 10));

  geo->addDrawable(label);
}

inline Vector3ub MeasureTool::contrastingColor(const Vector3ub& rgb) const
{
  // If we're far 'enough' (+/-32) away from 128, just invert the component.
  // If we're close to 128, inverting the color will end up too close to the
  // input -- adjust the component before inverting.
  const unsigned char minVal = 32;
  const unsigned char maxVal = 223;
  Vector3ub result;
  for (size_t i = 0; i < 3; ++i) {
    unsigned char input = rgb[i];
    if (input > 160 || input < 96)
      result[i] = static_cast<unsigned char>(255 - input);
    else
      result[i] = static_cast<unsigned char>(255 - (input / 4));

    // Clamp to 32-->223 to prevent pure black/white
    result[i] = std::min(maxVal, std::max(minVal, result[i]));
  }

  return result;
}

float MeasureTool::dihedralAngle(const Vector3& b1, const Vector3& b2,
                                 const Vector3& b3) const
{
  // See http://math.stackexchange.com/questions/47059/
  // how-do-i-calculate-a-dihedral-angle-given-cartesian-coordinates
  // for description of algorithm
  const Vector3 n1 = b1.cross(b2).normalized();
  const Vector3 n2 = b2.cross(b3).normalized();
  const Vector3 m1 = n1.cross(b2.normalized());
  const Real x(n1.dot(n2));
  const Real y(m1.dot(n2));
  return static_cast<float>(std::atan2(y, x)) * RAD_TO_DEG_F;
}

bool MeasureTool::toggleAtom(const Rendering::Identifier& atom)
{
  int ind = m_atoms.indexOf(atom);
  if (ind >= 0) {
    m_atoms.remove(ind);
    return true;
  }

  if (m_atoms.size() >= 4)
    return false;

  m_atoms.push_back(atom);
  return true;
}

} // namespace QtPlugins
} // namespace Avogadro
<commit_msg>Addition of units for measure tool<commit_after>/******************************************************************************

  This source file is part of the Avogadro project.

  Copyright 2013 Kitware, Inc.

  Adapted from Avogadro 1.x with the following authors' permission:
  Copyright 2007 Donald Ephraim Curtis
  Copyright 2008 Marcus D. Hanwell

  This source code is released under the New BSD License, (the "License").

  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 "measuretool.h"

#include <avogadro/qtopengl/glwidget.h>

#include <avogadro/rendering/geometrynode.h>
#include <avogadro/rendering/glrenderer.h>
#include <avogadro/rendering/groupnode.h>
#include <avogadro/rendering/scene.h>
#include <avogadro/rendering/textlabel2d.h>
#include <avogadro/rendering/textlabel3d.h>
#include <avogadro/rendering/textproperties.h>

#include <avogadro/core/atom.h>
#include <avogadro/core/elements.h>
#include <avogadro/core/vector.h>
#include <avogadro/qtgui/molecule.h>
#include <avogadro/qtgui/rwmolecule.h>

#include <QtGui/QIcon>
#include <QtGui/QMouseEvent>
#include <QtWidgets/QAction>

#include <QDebug>

#include <cmath>

using Avogadro::Core::Atom;
using Avogadro::Core::Elements;
using Avogadro::Rendering::GeometryNode;
using Avogadro::Rendering::GroupNode;
using Avogadro::Rendering::Identifier;
using Avogadro::Rendering::TextLabel2D;
using Avogadro::Rendering::TextLabel3D;
using Avogadro::Rendering::TextProperties;

namespace Avogadro {
namespace QtPlugins {

MeasureTool::MeasureTool(QObject* parent_)
  : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)),
    m_molecule(nullptr), m_rwMolecule(nullptr), m_renderer(nullptr)
{
  m_activateAction->setText(tr("Measure"));
  m_activateAction->setIcon(QIcon(":/icons/measuretool.png"));
}

MeasureTool::~MeasureTool()
{
}

QWidget* MeasureTool::toolWidget() const
{
  return nullptr;
}

QUndoCommand* MeasureTool::mousePressEvent(QMouseEvent* e)
{
  if (e->button() != Qt::LeftButton || !m_renderer)
    return nullptr;

  Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y());

  // If an atom is clicked, accept the event, but don't add it to the atom list
  // until the button is released (this way the user can cancel the click by
  // moving off the atom, and the click won't get passed to the default tool).
  if (hit.type == Rendering::AtomType)
    e->accept();

  return nullptr;
}

QUndoCommand* MeasureTool::mouseReleaseEvent(QMouseEvent* e)
{
  // If the click is released on an atom, add it to the list
  if (e->button() != Qt::LeftButton || !m_renderer)
    return nullptr;

  Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y());

  // Now add the atom on release.
  if (hit.type == Rendering::AtomType) {
    if (toggleAtom(hit))
      emit drawablesChanged();
    e->accept();
  }

  return nullptr;
}

QUndoCommand* MeasureTool::mouseDoubleClickEvent(QMouseEvent* e)
{
  // Reset the atom list
  if (e->button() == Qt::LeftButton && !m_atoms.isEmpty()) {
    m_atoms.clear();
    emit drawablesChanged();
    e->accept();
  }
  return nullptr;
}

template <typename T>
void MeasureTool::createLabels(T* mol, GeometryNode* geo,
                               QVector<Vector3>& positions)
{
  TextProperties atomLabelProp;
  atomLabelProp.setFontFamily(TextProperties::SansSerif);
  atomLabelProp.setAlign(TextProperties::HCenter, TextProperties::VCenter);

  for (int i = 0; i < m_atoms.size(); ++i) {
    Identifier& ident = m_atoms[i];
    Q_ASSERT(ident.type == Rendering::AtomType);
    Q_ASSERT(ident.molecule != nullptr);

    typename T::AtomType atom = mol->atom(ident.index);
    Q_ASSERT(atom.isValid());
    unsigned char atomicNumber(atom.atomicNumber());
    positions[i] = atom.position3d();

    const unsigned char* color = Elements::color(atomicNumber);
    atomLabelProp.setColorRgb(contrastingColor(Vector3ub(color)).data());

    TextLabel3D* label = new TextLabel3D;
    label->setText(QString("#%1").arg(i + 1).toStdString());
    label->setTextProperties(atomLabelProp);
    label->setAnchor(positions[i].cast<float>());
    label->setRadius(
      static_cast<float>(Elements::radiusCovalent(atomicNumber)));
    geo->addDrawable(label);
  }
}

void MeasureTool::draw(Rendering::GroupNode& node)
{
  if (m_atoms.size() == 0)
    return;

  GeometryNode* geo = new GeometryNode;
  node.addChild(geo);

  // Add labels, extract positions
  QVector<Vector3> positions(m_atoms.size(), Vector3());
  if (m_molecule)
    createLabels(m_molecule, geo, positions);
  else if (m_rwMolecule)
    createLabels(m_rwMolecule, geo, positions);

  // Calculate angles and distances
  Vector3 v1;
  Vector3 v2;
  Vector3 v3;
  Real v1Norm = -1.f;
  Real v2Norm = -1.f;
  Real v3Norm = -1.f;

  switch (m_atoms.size()) {
    case 4:
      v3 = positions[3] - positions[2];
      v3Norm = v3.norm();
    case 3:
      v2 = positions[2] - positions[1];
      v2Norm = v2.norm();
    case 2:
      v1 = positions[1] - positions[0];
      v1Norm = v1.norm();
    default:
      break;
  }

  QString overlayText;
  float angle23 = 361.f;
  float angle12 = 361.f;
  QString dihedralLabel = tr("Dihedral:");
  QString angleLabel = tr("Angle:");
  QString distanceLabel = tr("Distance:");
  // Use the longest label size to determine the field width. Negate it to
  // indicate left-alignment.
  int labelWidth = -std::max(std::max(dihedralLabel.size(), angleLabel.size()),
                             distanceLabel.size());
  switch (m_atoms.size()) {
    case 4:
      overlayText += QString("%1 %L2\n")
                       .arg(tr("Dihedral:"), labelWidth)
                       .arg(tr("%L1°").arg(dihedralAngle(v1, v2, v3), 10, 'f', 5), 10);
      angle23 =
        static_cast<float>(std::acos((-v2).dot(v3) / (v2Norm * v3Norm))) *
        RAD_TO_DEG_F;
    // fall through
    case 3:
      angle12 =
        static_cast<float>(std::acos((-v1).dot(v2) / (v1Norm * v2Norm))) *
        RAD_TO_DEG_F;
      overlayText +=
        QString("%1 %L2 %L3\n")
          .arg(tr("Angles:"), labelWidth)
          .arg(tr("%L1°").arg(angle12, 10, 'f', 5), 10)
          .arg(angle23 < 360.f ? tr("%L1°").arg(angle23, 10, 'f', 5) : QString(),
               10);
    // fall through
    case 2:
      overlayText +=
        QString("%1 %L2 %L3 %L4")
          .arg(tr("Distance:"), labelWidth)
          .arg(tr("%L1 Å").arg(v1Norm, 10, 'f', 5), 10)
          .arg(v2Norm >= 0.f ? tr("%L1 Å").arg(v2Norm, 10, 'f', 5) : QString(), 10)
          .arg(v3Norm >= 0.f ? tr("%L1 Å").arg(v3Norm, 10, 'f', 5) : QString(), 10);
    default:
      break;
  }

  if (overlayText.isEmpty())
    return;

  TextProperties overlayTProp;
  overlayTProp.setFontFamily(TextProperties::Mono);
  overlayTProp.setColorRgb(64, 255, 220);
  overlayTProp.setAlign(TextProperties::HLeft, TextProperties::VBottom);

  TextLabel2D* label = new TextLabel2D;
  label->setText(overlayText.toStdString());
  label->setTextProperties(overlayTProp);
  label->setRenderPass(Rendering::Overlay2DPass);
  label->setAnchor(Vector2i(10, 10));

  geo->addDrawable(label);
}

inline Vector3ub MeasureTool::contrastingColor(const Vector3ub& rgb) const
{
  // If we're far 'enough' (+/-32) away from 128, just invert the component.
  // If we're close to 128, inverting the color will end up too close to the
  // input -- adjust the component before inverting.
  const unsigned char minVal = 32;
  const unsigned char maxVal = 223;
  Vector3ub result;
  for (size_t i = 0; i < 3; ++i) {
    unsigned char input = rgb[i];
    if (input > 160 || input < 96)
      result[i] = static_cast<unsigned char>(255 - input);
    else
      result[i] = static_cast<unsigned char>(255 - (input / 4));

    // Clamp to 32-->223 to prevent pure black/white
    result[i] = std::min(maxVal, std::max(minVal, result[i]));
  }

  return result;
}

float MeasureTool::dihedralAngle(const Vector3& b1, const Vector3& b2,
                                 const Vector3& b3) const
{
  // See http://math.stackexchange.com/questions/47059/
  // how-do-i-calculate-a-dihedral-angle-given-cartesian-coordinates
  // for description of algorithm
  const Vector3 n1 = b1.cross(b2).normalized();
  const Vector3 n2 = b2.cross(b3).normalized();
  const Vector3 m1 = n1.cross(b2.normalized());
  const Real x(n1.dot(n2));
  const Real y(m1.dot(n2));
  return static_cast<float>(std::atan2(y, x)) * RAD_TO_DEG_F;
}

bool MeasureTool::toggleAtom(const Rendering::Identifier& atom)
{
  int ind = m_atoms.indexOf(atom);
  if (ind >= 0) {
    m_atoms.remove(ind);
    return true;
  }

  if (m_atoms.size() >= 4)
    return false;

  m_atoms.push_back(atom);
  return true;
}

} // namespace QtPlugins
} // namespace Avogadro
<|endoftext|>


<commit_before>/*
 Copyright (c) 2018 Anon authors, see AUTHORS file.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include "aws_http.h"
#include "dns_lookup.h"
#include "http_client.h"
#include <aws/core/http/standard/StandardHttpRequest.h>
#include <aws/core/http/standard/StandardHttpResponse.h>
#include <aws/core/http/Scheme.h>

using namespace Aws::Http;

namespace
{

class http_client : public HttpClient
{
  static std::string normalize(const std::string &path)
  {
    if (path.find("//") == 0)
      return path.substr(1);
    return path;
  }

public:
  http_client(const std::shared_ptr<aws_http_client_factory::epc_map> &maps, const std::shared_ptr<tls_context> &tls)
      : _maps(maps),
        _tls(tls)
  {
  }
  ~http_client() {}

  std::shared_ptr<endpoint_cluster> get_epc(const Aws::String &url) const
  {
    URI uri(url);
    auto key = uri.GetAuthority() + ":" + std::to_string(uri.GetPort());
    fiber_lock l(_maps->_mtx);
    auto &m = _maps->_epc_map;
    auto epc = m.find(key);
    if (epc != m.end())
      return epc->second;

    auto newepc = endpoint_cluster::create(uri.GetAuthority().c_str(), uri.GetPort(),
                                             uri.GetScheme() == Scheme::HTTPS, _tls.get());
    newepc->disable_retries();
    newepc->set_max_io_block_time(120);
    return m[key] = newepc;
  }

  void MakeRequest(const std::shared_ptr<Standard::StandardHttpResponse>& resp,
                                            HttpRequest &request,
                                            URI uri,
                                            Aws::Utils::RateLimits::RateLimiterInterface *readLimiter,
                                            Aws::Utils::RateLimits::RateLimiterInterface *writeLimiter,
                                            int recursion) const
  {
    if (recursion > 4) {
      resp->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR);
      return;
    }

    auto body = request.GetContentBody();
    auto method = request.GetMethod();
    std::ostringstream str;
    str << HttpMethodMapper::GetNameForHttpMethod(method) << " " << normalize(uri.GetPath())
      << uri.GetQueryString() << " HTTP/1.1\r\n";
    auto headers = request.GetHeaders();
    for (auto &h : headers)
      str << h.first << ": " << h.second << "\r\n";
    if (body && !request.HasHeader(CONTENT_LENGTH_HEADER))
    {
      str << "transfer-encoding: identity\r\n";
      str << "content-length: " << request.GetContentLength() << "\r\n";
    }

    str << "\r\n";
    if (body)
      str << body->rdbuf();
    auto message = str.str();

    auto read_body = method != HttpMethod::HTTP_HEAD;
    get_epc(uri.GetURIString())->with_connected_pipe([this, &request, &resp, &message, read_body, readLimiter, writeLimiter, recursion](const pipe_t *pipe) -> bool {
      // anon_log("sending...\n\n" << message << "\n");
      pipe->write(message.c_str(), message.size());
      http_client_response re;
      re.parse(*pipe, read_body, false/*throw_on_server_error*/);
      if ((re.status_code == 301 || re.status_code == 302) && re.headers.contains_header("location")) {
        MakeRequest(resp, request, URI(re.headers.get_header("location").str()), readLimiter, writeLimiter, recursion+1);
      }
      else {
        resp->SetResponseCode(static_cast<HttpResponseCode>(re.status_code));
        for (auto &h : re.headers.headers)
          resp->AddHeader(h.first.str(), h.second.str());
        for (auto &data : re.body)
          resp->GetResponseBody().write(&data[0], data.size());
      }
      return re.should_keep_alive;
    });

  }

  std::shared_ptr<HttpResponse> MakeRequest(HttpRequest &request,
                                            Aws::Utils::RateLimits::RateLimiterInterface *readLimiter,
                                            Aws::Utils::RateLimits::RateLimiterInterface *writeLimiter) const override
  {
    auto resp = std::make_shared<Standard::StandardHttpResponse>(request);
    try
    {
      MakeRequest(resp, request, request.GetUri(), readLimiter, writeLimiter, 0);
    }
#if ANON_LOG_NET_TRAFFIC > 0
    catch (const std::exception &exc)
    {
      anon_log("failure to write request: " << exc.what());
      resp.reset();
    }
#endif
    catch (...)
    {
#if ANON_LOG_NET_TRAFFIC > 0
      anon_log("unknown failure to write request");
#endif
      resp.reset();
    }
    return std::static_pointer_cast<HttpResponse>(resp);
  }

  std::shared_ptr<HttpResponse> MakeRequest(const std::shared_ptr<HttpRequest> &request,
                                            Aws::Utils::RateLimits::RateLimiterInterface *readLimiter,
                                            Aws::Utils::RateLimits::RateLimiterInterface *writeLimiter) const override
  {
    return MakeRequest(*request, readLimiter, writeLimiter);
  }

  std::shared_ptr<aws_http_client_factory::epc_map> _maps;
  std::shared_ptr<tls_context> _tls;
};

} // namespace

/////////////////////////////////////////////////////////////////////////////////////////////////

aws_http_client_factory::aws_http_client_factory()
    : _maps(std::make_shared<epc_map>()),
      _tls(std::make_shared<tls_context>(true /*client*/, nullptr /*verify_cert*/, "/etc/ssl/certs" /*verify_loc*/, nullptr, nullptr, 5))
{
}

std::shared_ptr<HttpClient> aws_http_client_factory::CreateHttpClient(const Aws::Client::ClientConfiguration &clientConfiguration) const
{
  return std::static_pointer_cast<HttpClient>(std::make_shared<http_client>(_maps, _tls));
}

std::shared_ptr<HttpRequest> aws_http_client_factory::CreateHttpRequest(const Aws::String &uri, HttpMethod method, const Aws::IOStreamFactory &streamFactory) const
{
  auto req = std::static_pointer_cast<HttpRequest>(std::make_shared<Standard::StandardHttpRequest>(uri, method));
  req->SetResponseStreamFactory(streamFactory);
  return req;
}

std::shared_ptr<HttpRequest> aws_http_client_factory::CreateHttpRequest(const URI &uri, HttpMethod method, const Aws::IOStreamFactory &streamFactory) const
{
  auto req = std::static_pointer_cast<HttpRequest>(std::make_shared<Standard::StandardHttpRequest>(uri, method));
  req->SetResponseStreamFactory(streamFactory);
  return req;
}
<commit_msg>more logging<commit_after>/*
 Copyright (c) 2018 Anon authors, see AUTHORS file.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include "aws_http.h"
#include "dns_lookup.h"
#include "http_client.h"
#include <aws/core/http/standard/StandardHttpRequest.h>
#include <aws/core/http/standard/StandardHttpResponse.h>
#include <aws/core/http/Scheme.h>

using namespace Aws::Http;

namespace
{

class http_client : public HttpClient
{
  static std::string normalize(const std::string &path)
  {
    if (path.find("//") == 0)
      return path.substr(1);
    return path;
  }

public:
  http_client(const std::shared_ptr<aws_http_client_factory::epc_map> &maps, const std::shared_ptr<tls_context> &tls)
      : _maps(maps),
        _tls(tls)
  {
  }
  ~http_client() {}

  std::shared_ptr<endpoint_cluster> get_epc(const Aws::String &url) const
  {
    URI uri(url);
    auto key = uri.GetAuthority() + ":" + std::to_string(uri.GetPort());
    fiber_lock l(_maps->_mtx);
    auto &m = _maps->_epc_map;
    auto epc = m.find(key);
    if (epc != m.end())
      return epc->second;

    auto newepc = endpoint_cluster::create(uri.GetAuthority().c_str(), uri.GetPort(),
                                             uri.GetScheme() == Scheme::HTTPS, _tls.get());
    newepc->disable_retries();
    newepc->set_max_io_block_time(120);
    return m[key] = newepc;
  }

  void MakeRequest(const std::shared_ptr<Standard::StandardHttpResponse>& resp,
                                            HttpRequest &request,
                                            URI uri,
                                            Aws::Utils::RateLimits::RateLimiterInterface *readLimiter,
                                            Aws::Utils::RateLimits::RateLimiterInterface *writeLimiter,
                                            int recursion) const
  {
    if (recursion > 4) {
      resp->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR);
      return;
    }

    auto body = request.GetContentBody();
    auto method = request.GetMethod();
    std::ostringstream str;
    str << HttpMethodMapper::GetNameForHttpMethod(method) << " " << normalize(uri.GetPath())
      << uri.GetQueryString() << " HTTP/1.1\r\n";
    auto headers = request.GetHeaders();
    for (auto &h : headers)
      str << h.first << ": " << h.second << "\r\n";
    if (body && !request.HasHeader(CONTENT_LENGTH_HEADER))
    {
      str << "transfer-encoding: identity\r\n";
      str << "content-length: " << request.GetContentLength() << "\r\n";
    }

    std::ostringstream strd;
    if (body)
      strd << body->rdbuf();
    anon_log("sending:\n" << str.str() << "\nplus " << strd.str().size() << " bytes of body content");

    str << "\r\n";
    if (body)
      str << body->rdbuf();
    auto message = str.str();

    auto read_body = method != HttpMethod::HTTP_HEAD;
    get_epc(uri.GetURIString())->with_connected_pipe([this, &request, &resp, &message, read_body, readLimiter, writeLimiter, recursion](const pipe_t *pipe) -> bool {
      // anon_log("sending...\n\n" << message << "\n");
      pipe->write(message.c_str(), message.size());
      http_client_response re;
      re.parse(*pipe, read_body, false/*throw_on_server_error*/);
      if ((re.status_code == 301 || re.status_code == 302) && re.headers.contains_header("location")) {
        MakeRequest(resp, request, URI(re.headers.get_header("location").str()), readLimiter, writeLimiter, recursion+1);
      }
      else {
        resp->SetResponseCode(static_cast<HttpResponseCode>(re.status_code));
        for (auto &h : re.headers.headers)
          resp->AddHeader(h.first.str(), h.second.str());
        for (auto &data : re.body)
          resp->GetResponseBody().write(&data[0], data.size());
      }
      return re.should_keep_alive;
    });

  }

  std::shared_ptr<HttpResponse> MakeRequest(HttpRequest &request,
                                            Aws::Utils::RateLimits::RateLimiterInterface *readLimiter,
                                            Aws::Utils::RateLimits::RateLimiterInterface *writeLimiter) const override
  {
    auto resp = std::make_shared<Standard::StandardHttpResponse>(request);
    try
    {
      MakeRequest(resp, request, request.GetUri(), readLimiter, writeLimiter, 0);
    }
#if ANON_LOG_NET_TRAFFIC > 0
    catch (const std::exception &exc)
    {
      anon_log("failure to write request: " << exc.what());
      resp.reset();
    }
#endif
    catch (...)
    {
#if ANON_LOG_NET_TRAFFIC > 0
      anon_log("unknown failure to write request");
#endif
      resp.reset();
    }
    return std::static_pointer_cast<HttpResponse>(resp);
  }

  std::shared_ptr<HttpResponse> MakeRequest(const std::shared_ptr<HttpRequest> &request,
                                            Aws::Utils::RateLimits::RateLimiterInterface *readLimiter,
                                            Aws::Utils::RateLimits::RateLimiterInterface *writeLimiter) const override
  {
    return MakeRequest(*request, readLimiter, writeLimiter);
  }

  std::shared_ptr<aws_http_client_factory::epc_map> _maps;
  std::shared_ptr<tls_context> _tls;
};

} // namespace

/////////////////////////////////////////////////////////////////////////////////////////////////

aws_http_client_factory::aws_http_client_factory()
    : _maps(std::make_shared<epc_map>()),
      _tls(std::make_shared<tls_context>(true /*client*/, nullptr /*verify_cert*/, "/etc/ssl/certs" /*verify_loc*/, nullptr, nullptr, 5))
{
}

std::shared_ptr<HttpClient> aws_http_client_factory::CreateHttpClient(const Aws::Client::ClientConfiguration &clientConfiguration) const
{
  return std::static_pointer_cast<HttpClient>(std::make_shared<http_client>(_maps, _tls));
}

std::shared_ptr<HttpRequest> aws_http_client_factory::CreateHttpRequest(const Aws::String &uri, HttpMethod method, const Aws::IOStreamFactory &streamFactory) const
{
  auto req = std::static_pointer_cast<HttpRequest>(std::make_shared<Standard::StandardHttpRequest>(uri, method));
  req->SetResponseStreamFactory(streamFactory);
  return req;
}

std::shared_ptr<HttpRequest> aws_http_client_factory::CreateHttpRequest(const URI &uri, HttpMethod method, const Aws::IOStreamFactory &streamFactory) const
{
  auto req = std::static_pointer_cast<HttpRequest>(std::make_shared<Standard::StandardHttpRequest>(uri, method));
  req->SetResponseStreamFactory(streamFactory);
  return req;
}
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libdui.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "pt_qapplication.h"

#include <QApplication>
#include <QProcess>

#include <qtest.h>

void Pt_QApplication::processCreation()
{
    executeSelf(QLatin1String("--exit-immediately"));
}

void Pt_QApplication::processCreationAndCtor()
{
    executeSelf(QLatin1String("--exit-after-qapp"));
}

void Pt_QApplication::ctor()
{
    QApplication *a;
    QBENCHMARK {
        int fakeArgc = 1;
        char *fakeArgv[fakeArgc];
        char appName[] = "./pt_qapplication";
        fakeArgv[0] = appName;
        a = new QApplication(fakeArgc, fakeArgv);
    }
    delete a;
}

void Pt_QApplication::ctor2()
{
    QApplication *a;
    QBENCHMARK {
        int fakeArgc = 1;
        char *fakeArgv[fakeArgc];
        char appName[] = "./pt_qapplication2";
        fakeArgv[0] = appName;
        a = new QApplication(fakeArgc, fakeArgv);
    }
    delete a;
}

void Pt_QApplication::executeSelf(const QLatin1String &parameter)
{
    QProcess proc;
    const QString program       = "./pt_qapplication";
    const QStringList arguments = QStringList() << QLatin1String(parameter);

    QBENCHMARK {
        proc.start(program, arguments);
        QVERIFY(proc.waitForStarted());
        QVERIFY(proc.waitForFinished());
        QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
        QCOMPARE(proc.exitCode(), 0);
    }
}

int main(int argc, char **argv)
{
    if (argc == 2 && QLatin1String("--exit-immediately") == QLatin1String(argv[1])) {
        return 0;
    }

    if (argc == 2 && QLatin1String("--exit-after-qapp") == QLatin1String(argv[1])) {
        QApplication app(argc, argv);
        return 0;
    }

    Pt_QApplication test;
    return QTest::qExec(&test, argc, argv);
}

<commit_msg>Changes: Benchamrk test pt_qapplication loop can run only once<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libdui.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "pt_qapplication.h"

#include <QApplication>
#include <QProcess>

#include <qtest.h>

void Pt_QApplication::processCreation()
{
    executeSelf(QLatin1String("--exit-immediately"));
}

void Pt_QApplication::processCreationAndCtor()
{
    executeSelf(QLatin1String("--exit-after-qapp"));
}

void Pt_QApplication::ctor()
{
    QApplication *a(NULL);
    QBENCHMARK_ONCE {
        int fakeArgc = 1;
        char *fakeArgv[fakeArgc];
        char appName[] = "./pt_qapplication";
        fakeArgv[0] = appName;
        a = new QApplication(fakeArgc, fakeArgv);
    }
    delete a;
}

void Pt_QApplication::ctor2()
{
    QApplication *a(NULL);
    QBENCHMARK_ONCE {
        int fakeArgc = 1;
        char *fakeArgv[fakeArgc];
        char appName[] = "./pt_qapplication2";
        fakeArgv[0] = appName;
        a = new QApplication(fakeArgc, fakeArgv);
    }
    delete a;
}

void Pt_QApplication::executeSelf(const QLatin1String &parameter)
{
    QProcess proc;
    const QString program       = "./pt_qapplication";
    const QStringList arguments = QStringList() << QLatin1String(parameter);

    QBENCHMARK {
        proc.start(program, arguments);
        QVERIFY(proc.waitForStarted());
        QVERIFY(proc.waitForFinished());
        QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
        QCOMPARE(proc.exitCode(), 0);
    }
}

int main(int argc, char **argv)
{
    if (argc == 2 && QLatin1String("--exit-immediately") == QLatin1String(argv[1])) {
        return 0;
    }

    if (argc == 2 && QLatin1String("--exit-after-qapp") == QLatin1String(argv[1])) {
        QApplication app(argc, argv);
        return 0;
    }

    Pt_QApplication test;
    return QTest::qExec(&test, argc, argv);
}

<|endoftext|>


<commit_before>/*
 * APSRack.cpp
 *
 *  Created on: Jun 13, 2012
 *      Author: cryan
 */

#include "APSRack.h"

APSRack::APSRack() : numDevices_{0} {
}

APSRack::~APSRack()  {
	//Close the logging file so we don't leave it dangling
	fclose(Output2FILE::Stream());
}

//Initialize the rack by polling for devices and serial numbers
int APSRack::init() {

	//Create the logger
	FILE* pFile = fopen("libaps.log", "a");
	Output2FILE::Stream() = pFile;

	//Enumerate the serial numbers of the devices attached
	enumerate_devices();

	return 0;
}

//Initialize a specific APS unit
int APSRack::initAPS(const int & deviceID, const string & bitFile, const bool & forceReload){
	return APSs_[deviceID].init(bitFile, forceReload);
}

int APSRack::get_num_devices()  {
	int numDevices;
	FT_ListDevices(&numDevices, NULL, FT_LIST_NUMBER_ONLY);
	if (numDevices_ != numDevices) {
		update_device_enumeration();
	}
	return numDevices;
}

string APSRack::get_deviceSerial(const int & deviceID) {

	// Call get_num_devices to update if enumeration if the number of devices has changed.
	// We would like to check serials but if we are already connected we can't.
	get_num_devices();

	// test to make sure ID is valid relative to vector size
	if (static_cast<size_t>(deviceID) > deviceSerials_.size()) {
		return "InvalidID";
	}

	return deviceSerials_[deviceID];
}

//This will reset the APS vector so it really should only be called during initialization
void APSRack::enumerate_devices() {

	//Have to disconnect everything first
	for (auto & aps : APSs_){
		aps.disconnect();
	}
	FTDI::get_device_serials(deviceSerials_);
	numDevices_ = deviceSerials_.size();

	APSs_.clear();
	APSs_.reserve(numDevices_);

	//	Now setup the map between device serials and number and assign the APS units appropriately
	//	Also setup the FPGA checksums
	size_t devicect = 0;
	for (string tmpSerial : deviceSerials_) {
		serial2dev[tmpSerial] = devicect;
		APSs_.emplace_back(devicect, tmpSerial);
		FILE_LOG(logDEBUG) << "Device " << devicect << " has serial number " << tmpSerial;
		devicect++;
	}
}

// This will update enumerate of devices by matching serial numbers
// If a device is missing it will be removed
// New devices are added
// Old devices are left as is (moved in place to new vector)
void APSRack::update_device_enumeration() {

	// get serial numbers from FTDI layer
	//Have to disconnect everything first
	for (auto & aps : APSs_){
		aps.disconnect();
	}
	vector<string> newSerials;
	FTDI::get_device_serials(newSerials);

	// construct new APS_ vector & new serial2dev map
	vector<APS> newAPS_;
	map<string, int> newSerial2dev;

	size_t devicect = 0;
	for (string tmpSerial : newSerials) {
		// does the new serial number exist in the old list?
		if ( serial2dev.count(tmpSerial) > 0) {
			// move from old APSs_ vector to new
			newAPS_.push_back(std::move(APSs_[serial2dev[tmpSerial]]));
			FILE_LOG(logDEBUG) << "Old Device " << devicect << " [ " << tmpSerial << " ] moved";
		} else {
			// does not exist so construct it in the new vector
			newAPS_.emplace_back(devicect, tmpSerial);
			FILE_LOG(logDEBUG) << "New Device " << devicect << " has serial number " << tmpSerial;
		}

		newSerial2dev[tmpSerial] = devicect;
		devicect++;
	}

	// update APSRack members with new lists
	numDevices_ = deviceSerials_.size();  // number of devices
	deviceSerials_ = newSerials;		  // device serial vector
	APSs_ = std::move(newAPS_);           // APS vector
	serial2dev = newSerial2dev;           // serial to device map
}

int APSRack::read_bitfile_version(const int & deviceID) const {
	return APSs_[deviceID].read_bitFile_version(ALL_FPGAS);
}

int APSRack::connect(const int & deviceID){
	//Connect to a instrument specified by deviceID
	return APSs_[deviceID].connect();
}

int APSRack::disconnect(const int & deviceID){
	return APSs_[deviceID].disconnect();
}

int APSRack::connect(const string & deviceSerial){
	//Look up the associated ID and call the next connect
	return APSs_[serial2dev[deviceSerial]].connect();
}

int APSRack::disconnect(const string & deviceSerial){
	//Look up the associated ID and call the next connect
	return APSs_[serial2dev[deviceSerial]].disconnect();
}

int APSRack::program_FPGA(const int & deviceID, const string &bitFile, const FPGASELECT & chipSelect, const int & expectedVersion){
	return APSs_[deviceID].program_FPGA(bitFile, chipSelect, expectedVersion);
}

int APSRack::setup_DACs(const int & deviceID) const{
	return APSs_[deviceID].setup_DACs();
}

int APSRack::set_sampleRate(const int & deviceID, const int & freq) {
	return APSs_[deviceID].set_sampleRate(freq);
}

int APSRack::get_sampleRate(const int & deviceID) const{
	return APSs_[deviceID].get_sampleRate();
}

int APSRack::set_run_mode(const int & deviceID, const int & dac, const RUN_MODE & mode){
	return APSs_[deviceID].set_run_mode(dac, mode);
}

int APSRack::set_repeat_mode(const int & deviceID, const int & dac, const bool & mode){
	return APSs_[deviceID].set_repeat_mode(dac, mode);
}

int APSRack::clear_channel_data(const int & deviceID) {
	return APSs_[deviceID].clear_channel_data();
}

int APSRack::run(const int & deviceID) {
	return APSs_[deviceID].run();
}
int APSRack::stop(const int & deviceID) {
	return APSs_[deviceID].stop();
}

int APSRack::load_sequence_file(const int & deviceID, const string & seqFile){
	return APSs_[deviceID].load_sequence_file(seqFile);
}

int APSRack::set_LL_data(const int & deviceID, const int & channelNum, const WordVec & addr, const WordVec & count, const WordVec & trigger1, const WordVec & trigger2, const WordVec & repeat){
	return APSs_[deviceID].set_LLData_IQ(dac2fpga(channelNum), addr, count, trigger1, trigger2, repeat);
}

int APSRack::get_running(const int & deviceID){
	//TODO:
//	return APSs_[deviceID].running_;
	return 0;
}

int APSRack::set_log(FILE * pFile) {
	if (pFile) {
		//Close the current file
		if(Output2FILE::Stream()) fclose(Output2FILE::Stream());
		//Assign the new one
		Output2FILE::Stream() = pFile;
		return 1;
	} else {
		return 0;
	}
}

int APSRack::set_logging_level(const int & logLevel){
	FILELog::ReportingLevel() = TLogLevel(logLevel);
	return 0;
}

int APSRack::set_trigger_source(const int & deviceID, const TRIGGERSOURCE & triggerSource) {
	return APSs_[deviceID].set_trigger_source(triggerSource);
}

TRIGGERSOURCE APSRack::get_trigger_source(const int & deviceID) const{
	return APSs_[deviceID].get_trigger_source();
}

int APSRack::set_trigger_interval(const int & deviceID, const double & interval){
	return APSs_[deviceID].set_trigger_interval(interval);
}

double APSRack::get_trigger_interval(const int & deviceID) const{
	return APSs_[deviceID].get_trigger_interval();
}

int APSRack::set_miniLL_repeat(const int & deviceID, const USHORT & repeat){
	return APSs_[deviceID].set_miniLL_repeat(repeat);
}

int APSRack::set_channel_enabled(const int & deviceID, const int & channelNum, const bool & enable){
	return APSs_[deviceID].set_channel_enabled(channelNum, enable);
}

bool APSRack::get_channel_enabled(const int & deviceID, const int & channelNum) const{
	return APSs_[deviceID].get_channel_enabled(channelNum);
}

int APSRack::set_channel_offset(const int & deviceID, const int & channelNum, const float & offset){
	return APSs_[deviceID].set_channel_offset(channelNum, offset);
}

float APSRack::get_channel_offset(const int & deviceID, const int & channelNum) const{
	return APSs_[deviceID].get_channel_offset(channelNum);
}

int APSRack::set_channel_scale(const int & deviceID, const int & channelNum, const float & scale){
	return APSs_[deviceID].set_channel_scale(channelNum, scale);
}

float APSRack::get_channel_scale(const int & deviceID, const int & channelNum) const{
	return APSs_[deviceID].get_channel_scale(channelNum);
}

int APSRack::save_state_files(){
	// loop through available APS Units and save state
	for(unsigned int apsct = 0; apsct < APSs_.size(); apsct++) {
		string stateFileName = ""; // use default file name
		APSs_[apsct].save_state_file(stateFileName);
	}
	return 0;
}

int APSRack::read_state_files(){
	// loop through available APS Units and load state
	for(unsigned int  apsct = 0; apsct < APSs_.size(); apsct++) {
		string stateFileName = ""; // use default file name
		APSs_[apsct].read_state_file(stateFileName);
	}
	return 0;
}

int APSRack::save_bulk_state_file(string & stateFile){

	if (stateFile.length() == 0) {
		stateFile += "cache_APSRack.h5";
	}

	FILE_LOG(logDEBUG) << "Writing Bulk State File " << stateFile;
	H5::H5File H5StateFile(stateFile, H5F_ACC_TRUNC);
	// loop through available APS Units and save state
	for(unsigned int  apsct = 0; apsct < APSs_.size(); apsct++) {
		string rootStr = "/";
		rootStr += APSs_[apsct].deviceSerial_ ;
		FILE_LOG(logDEBUG) << "Creating Group: " << rootStr;
		H5::Group tmpGroup = H5StateFile.createGroup(rootStr);
		tmpGroup.close();
		APSs_[apsct].write_state_to_hdf5(H5StateFile, rootStr);
	}
	//Close the file
	H5StateFile.close();
	return 0;
}

int APSRack::read_bulk_state_file(string & stateFile){
	if (stateFile.length() == 0) {
		stateFile += "cache_APSRack.h5";
	}
	FILE_LOG(logDEBUG) << "Reading Bulk State File " << stateFile;
	H5::H5File H5StateFile(stateFile, H5F_ACC_RDONLY);

	// loop through available APS Units and load data
	for(unsigned int  apsct = 0; apsct < APSs_.size(); apsct++) {
		string rootStr = "/";
		rootStr += "/" + APSs_[apsct].deviceSerial_;
		APSs_[apsct].read_state_from_hdf5(H5StateFile, rootStr);
	}
	//Close the file
	H5StateFile.close();
	return 0;
}

int APSRack::raw_write(int deviceID, int numBytes, UCHAR* data){
	DWORD bytesWritten;
	FT_Write(APSs_[deviceID].handle_, data, numBytes, &bytesWritten);
	return int(bytesWritten);
}

int APSRack::raw_read(int deviceID, FPGASELECT fpga) {
	DWORD bytesRead, bytesWritten;
	UCHAR dataBuffer[2];
	USHORT transferSize = 1;
	int Command = APS_FPGA_IO;

	//Send the read command byte
	UCHAR commandPacket = 0x80 | Command | (fpga<<2) | transferSize;
    FT_Write(APSs_[deviceID].handle_, &commandPacket, 1, &bytesWritten);

	//Look for the data
	FT_Read(APSs_[deviceID].handle_, dataBuffer, 2, &bytesRead);
	FILE_LOG(logDEBUG2) << "Read " << bytesRead << " bytes with value" << myhex << ((dataBuffer[0] << 8) | dataBuffer[1]);
	return int((dataBuffer[0] << 8) | dataBuffer[1]);
}

int APSRack::read_register(int deviceID, FPGASELECT fpga, int addr){
	return FPGA::read_FPGA(APSs_[deviceID].handle_, addr, fpga);
}
<commit_msg>Fix for multiple re-enumeration if number of devices had changed. Re-enambled serial number check on get_device_serial.<commit_after>/*
 * APSRack.cpp
 *
 *  Created on: Jun 13, 2012
 *      Author: cryan
 */

#include "APSRack.h"

APSRack::APSRack() : numDevices_{0} {
}

APSRack::~APSRack()  {
	//Close the logging file so we don't leave it dangling
	fclose(Output2FILE::Stream());
}

//Initialize the rack by polling for devices and serial numbers
int APSRack::init() {

	//Create the logger
	FILE* pFile = fopen("libaps.log", "a");
	Output2FILE::Stream() = pFile;

	//Enumerate the serial numbers of the devices attached
	enumerate_devices();

	return 0;
}

//Initialize a specific APS unit
int APSRack::initAPS(const int & deviceID, const string & bitFile, const bool & forceReload){
	return APSs_[deviceID].init(bitFile, forceReload);
}

int APSRack::get_num_devices()  {
	int numDevices;
	FT_ListDevices(&numDevices, NULL, FT_LIST_NUMBER_ONLY);
	if (numDevices_ != numDevices) {
		update_device_enumeration();
	}
	return numDevices;
}

string APSRack::get_deviceSerial(const int & deviceID) {

	// get current number of devices
	get_num_devices();

	// Get serials from FTDI layer to check for change in devices
	vector<string> testSerials;
	FTDI::get_device_serials(testSerials);

	// match serials for each device id to make sure mapping of device count to serial
	// number is still correct
	// if serial number is different re-enumerate
	for(unsigned int cnt = 0; cnt < testSerials.size(); cnt++) {
		if (testSerials[cnt].compare(deviceSerials_[cnt]) != 0) {
			FILE_LOG(logDEBUG) << testSerials[cnt] << " does not match " << deviceSerials_[cnt] << " re-enumerating.";
			update_device_enumeration();
			break;
		}
	}

	// test to make sure ID is valid relative to vector size
	if (static_cast<size_t>(deviceID) > deviceSerials_.size()) {
		return "InvalidID";
	}

	return deviceSerials_[deviceID];
}

//This will reset the APS vector so it really should only be called during initialization
void APSRack::enumerate_devices() {

	//Have to disconnect everything first
	for (auto & aps : APSs_){
		aps.disconnect();
	}
	FTDI::get_device_serials(deviceSerials_);
	numDevices_ = deviceSerials_.size();

	APSs_.clear();
	APSs_.reserve(numDevices_);

	//	Now setup the map between device serials and number and assign the APS units appropriately
	//	Also setup the FPGA checksums
	size_t devicect = 0;
	for (string tmpSerial : deviceSerials_) {
		serial2dev[tmpSerial] = devicect;
		APSs_.emplace_back(devicect, tmpSerial);
		FILE_LOG(logDEBUG) << "Device " << devicect << " has serial number " << tmpSerial;
		devicect++;
	}
}

// This will update enumerate of devices by matching serial numbers
// If a device is missing it will be removed
// New devices are added
// Old devices are left as is (moved in place to new vector)
void APSRack::update_device_enumeration() {

	vector<string> newSerials;
	FTDI::get_device_serials(newSerials);

	// construct new APS_ vector & new serial2dev map
	vector<APS> newAPS_;
	map<string, int> newSerial2dev;

	size_t devicect = 0;
	for (string tmpSerial : newSerials) {

		// example test to see if FTDI thinks device is open
		if (FTDI::isOpen(devicect)) {
			FILE_LOG(logDEBUG) << "Device " << devicect << " [ " << tmpSerial << " ] is open";
		}

		// does the new serial number exist in the old list?
		if ( serial2dev.count(tmpSerial) > 0) {
			// move from old APSs_ vector to new
			newAPS_.push_back(std::move(APSs_[serial2dev[tmpSerial]]));
			FILE_LOG(logDEBUG) << "Old Device " << devicect << " [ " << tmpSerial << " ] moved";
		} else {
			// does not exist so construct it in the new vector
			newAPS_.emplace_back(devicect, tmpSerial);
			FILE_LOG(logDEBUG) << "New Device " << devicect << " [ " << tmpSerial << " ]";
		}

		newSerial2dev[tmpSerial] = devicect;
		devicect++;
	}

	// update APSRack members with new lists
	numDevices_ = newSerials.size();  // number of devices
	deviceSerials_ = newSerials;		  // device serial vector
	APSs_ = std::move(newAPS_);           // APS vector
	serial2dev = newSerial2dev;           // serial to device map
}

int APSRack::read_bitfile_version(const int & deviceID) const {
	return APSs_[deviceID].read_bitFile_version(ALL_FPGAS);
}

int APSRack::connect(const int & deviceID){
	//Connect to a instrument specified by deviceID
	return APSs_[deviceID].connect();
}

int APSRack::disconnect(const int & deviceID){
	return APSs_[deviceID].disconnect();
}

int APSRack::connect(const string & deviceSerial){
	//Look up the associated ID and call the next connect
	return APSs_[serial2dev[deviceSerial]].connect();
}

int APSRack::disconnect(const string & deviceSerial){
	//Look up the associated ID and call the next connect
	return APSs_[serial2dev[deviceSerial]].disconnect();
}

int APSRack::program_FPGA(const int & deviceID, const string &bitFile, const FPGASELECT & chipSelect, const int & expectedVersion){
	return APSs_[deviceID].program_FPGA(bitFile, chipSelect, expectedVersion);
}

int APSRack::setup_DACs(const int & deviceID) const{
	return APSs_[deviceID].setup_DACs();
}

int APSRack::set_sampleRate(const int & deviceID, const int & freq) {
	return APSs_[deviceID].set_sampleRate(freq);
}

int APSRack::get_sampleRate(const int & deviceID) const{
	return APSs_[deviceID].get_sampleRate();
}

int APSRack::set_run_mode(const int & deviceID, const int & dac, const RUN_MODE & mode){
	return APSs_[deviceID].set_run_mode(dac, mode);
}

int APSRack::set_repeat_mode(const int & deviceID, const int & dac, const bool & mode){
	return APSs_[deviceID].set_repeat_mode(dac, mode);
}

int APSRack::clear_channel_data(const int & deviceID) {
	return APSs_[deviceID].clear_channel_data();
}

int APSRack::run(const int & deviceID) {
	return APSs_[deviceID].run();
}
int APSRack::stop(const int & deviceID) {
	return APSs_[deviceID].stop();
}

int APSRack::load_sequence_file(const int & deviceID, const string & seqFile){
	return APSs_[deviceID].load_sequence_file(seqFile);
}

int APSRack::set_LL_data(const int & deviceID, const int & channelNum, const WordVec & addr, const WordVec & count, const WordVec & trigger1, const WordVec & trigger2, const WordVec & repeat){
	return APSs_[deviceID].set_LLData_IQ(dac2fpga(channelNum), addr, count, trigger1, trigger2, repeat);
}

int APSRack::get_running(const int & deviceID){
	//TODO:
//	return APSs_[deviceID].running_;
	return 0;
}

int APSRack::set_log(FILE * pFile) {
	if (pFile) {
		//Close the current file
		if(Output2FILE::Stream()) fclose(Output2FILE::Stream());
		//Assign the new one
		Output2FILE::Stream() = pFile;
		return 1;
	} else {
		return 0;
	}
}

int APSRack::set_logging_level(const int & logLevel){
	FILELog::ReportingLevel() = TLogLevel(logLevel);
	return 0;
}

int APSRack::set_trigger_source(const int & deviceID, const TRIGGERSOURCE & triggerSource) {
	return APSs_[deviceID].set_trigger_source(triggerSource);
}

TRIGGERSOURCE APSRack::get_trigger_source(const int & deviceID) const{
	return APSs_[deviceID].get_trigger_source();
}

int APSRack::set_trigger_interval(const int & deviceID, const double & interval){
	return APSs_[deviceID].set_trigger_interval(interval);
}

double APSRack::get_trigger_interval(const int & deviceID) const{
	return APSs_[deviceID].get_trigger_interval();
}

int APSRack::set_miniLL_repeat(const int & deviceID, const USHORT & repeat){
	return APSs_[deviceID].set_miniLL_repeat(repeat);
}

int APSRack::set_channel_enabled(const int & deviceID, const int & channelNum, const bool & enable){
	return APSs_[deviceID].set_channel_enabled(channelNum, enable);
}

bool APSRack::get_channel_enabled(const int & deviceID, const int & channelNum) const{
	return APSs_[deviceID].get_channel_enabled(channelNum);
}

int APSRack::set_channel_offset(const int & deviceID, const int & channelNum, const float & offset){
	return APSs_[deviceID].set_channel_offset(channelNum, offset);
}

float APSRack::get_channel_offset(const int & deviceID, const int & channelNum) const{
	return APSs_[deviceID].get_channel_offset(channelNum);
}

int APSRack::set_channel_scale(const int & deviceID, const int & channelNum, const float & scale){
	return APSs_[deviceID].set_channel_scale(channelNum, scale);
}

float APSRack::get_channel_scale(const int & deviceID, const int & channelNum) const{
	return APSs_[deviceID].get_channel_scale(channelNum);
}

int APSRack::save_state_files(){
	// loop through available APS Units and save state
	for(unsigned int apsct = 0; apsct < APSs_.size(); apsct++) {
		string stateFileName = ""; // use default file name
		APSs_[apsct].save_state_file(stateFileName);
	}
	return 0;
}

int APSRack::read_state_files(){
	// loop through available APS Units and load state
	for(unsigned int  apsct = 0; apsct < APSs_.size(); apsct++) {
		string stateFileName = ""; // use default file name
		APSs_[apsct].read_state_file(stateFileName);
	}
	return 0;
}

int APSRack::save_bulk_state_file(string & stateFile){

	if (stateFile.length() == 0) {
		stateFile += "cache_APSRack.h5";
	}

	FILE_LOG(logDEBUG) << "Writing Bulk State File " << stateFile;
	H5::H5File H5StateFile(stateFile, H5F_ACC_TRUNC);
	// loop through available APS Units and save state
	for(unsigned int  apsct = 0; apsct < APSs_.size(); apsct++) {
		string rootStr = "/";
		rootStr += APSs_[apsct].deviceSerial_ ;
		FILE_LOG(logDEBUG) << "Creating Group: " << rootStr;
		H5::Group tmpGroup = H5StateFile.createGroup(rootStr);
		tmpGroup.close();
		APSs_[apsct].write_state_to_hdf5(H5StateFile, rootStr);
	}
	//Close the file
	H5StateFile.close();
	return 0;
}

int APSRack::read_bulk_state_file(string & stateFile){
	if (stateFile.length() == 0) {
		stateFile += "cache_APSRack.h5";
	}
	FILE_LOG(logDEBUG) << "Reading Bulk State File " << stateFile;
	H5::H5File H5StateFile(stateFile, H5F_ACC_RDONLY);

	// loop through available APS Units and load data
	for(unsigned int  apsct = 0; apsct < APSs_.size(); apsct++) {
		string rootStr = "/";
		rootStr += "/" + APSs_[apsct].deviceSerial_;
		APSs_[apsct].read_state_from_hdf5(H5StateFile, rootStr);
	}
	//Close the file
	H5StateFile.close();
	return 0;
}

int APSRack::raw_write(int deviceID, int numBytes, UCHAR* data){
	DWORD bytesWritten;
	FT_Write(APSs_[deviceID].handle_, data, numBytes, &bytesWritten);
	return int(bytesWritten);
}

int APSRack::raw_read(int deviceID, FPGASELECT fpga) {
	DWORD bytesRead, bytesWritten;
	UCHAR dataBuffer[2];
	USHORT transferSize = 1;
	int Command = APS_FPGA_IO;

	//Send the read command byte
	UCHAR commandPacket = 0x80 | Command | (fpga<<2) | transferSize;
    FT_Write(APSs_[deviceID].handle_, &commandPacket, 1, &bytesWritten);

	//Look for the data
	FT_Read(APSs_[deviceID].handle_, dataBuffer, 2, &bytesRead);
	FILE_LOG(logDEBUG2) << "Read " << bytesRead << " bytes with value" << myhex << ((dataBuffer[0] << 8) | dataBuffer[1]);
	return int((dataBuffer[0] << 8) | dataBuffer[1]);
}

int APSRack::read_register(int deviceID, FPGASELECT fpga, int addr){
	return FPGA::read_FPGA(APSs_[deviceID].handle_, addr, fpga);
}
<|endoftext|>