text:
/**
 *  Copyright 2014-2016 CyberVision, Inc.
 *
 *  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 <boost/test/unit_test.hpp>

#include <thread>
#include <chrono>
#include <memory>

#include "kaa/log/DefaultLogUploadStrategy.hpp"
#include "kaa/common/exception/KaaException.hpp"
#include "kaa/context/SimpleExecutorContext.hpp"
#include "kaa/KaaClientContext.hpp"
#include "kaa/KaaClientProperties.hpp"
#include "kaa/logging/DefaultLogger.hpp"

#include "headers/MockKaaClientStateStorage.hpp"
#include "headers/context/MockExecutorContext.hpp"

#include "headers/log/MockLogFailoverCommand.hpp"
#include "headers/log/MockLogStorageStatus.hpp"

namespace kaa {

static KaaClientProperties properties;
static DefaultLogger tmp_logger(properties.getClientId());
static IKaaClientStateStoragePtr tmp_state(new MockKaaClientStateStorage);
static MockExecutorContext tmpExecContext;
static KaaClientContext clientContext(properties, tmp_logger, tmpExecContext, tmp_state);

BOOST_AUTO_TEST_SUITE(DefaultLogUploadStrategyTestSuite)

BOOST_AUTO_TEST_CASE(GetSetUplaodTimeoutTest)
{
    MockLogFailoverCommand failoverCommand;
    DefaultLogUploadStrategy strategy(clientContext);

    BOOST_CHECK_EQUAL(strategy.getTimeout(), DefaultLogUploadStrategy::DEFAULT_UPLOAD_TIMEOUT);

    std::size_t uploadTimeout = std::rand();
    strategy.setUploadTimeout(uploadTimeout);

    BOOST_CHECK_EQUAL(strategy.getTimeout(), uploadTimeout);
}

BOOST_AUTO_TEST_CASE(GetSetMaxParallelUploads)
{
    MockLogFailoverCommand failoverCommand;
    DefaultLogUploadStrategy strategy(clientContext);

    BOOST_CHECK_EQUAL(strategy.getMaxParallelUploads(), DefaultLogUploadStrategy::DEFAULT_MAX_PARALLEL_UPLOADS);

    std::size_t count = std::rand();
    strategy.setMaxParallelUploads(count);

    BOOST_CHECK_EQUAL(strategy.getMaxParallelUploads(), count);
}

BOOST_AUTO_TEST_CASE(UploadByOccupiedSizeTest)
{
    const std::size_t THRESHOLD_SIZE = 35;
    const std::size_t THRESHOLD_COUNT = 100500;

    MockLogStorageStatus logStorageStatus;
    logStorageStatus.consumedVolume_ = THRESHOLD_SIZE - 1;
    logStorageStatus.recordsCount_ = THRESHOLD_COUNT - 1;

    MockLogFailoverCommand failoverCommand;
    DefaultLogUploadStrategy strategy(clientContext);;

    strategy.setVolumeThreshold(THRESHOLD_SIZE);
    strategy.setCountThreshold(THRESHOLD_COUNT);

    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::NOOP);

    logStorageStatus.consumedVolume_ = THRESHOLD_SIZE;
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::UPLOAD);

    logStorageStatus.consumedVolume_ = THRESHOLD_SIZE + 1;
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::UPLOAD);
}

BOOST_AUTO_TEST_CASE(UploadByRecordCountTest)
{
    const std::size_t THRESHOLD_SIZE = 35;
    const std::size_t THRESHOLD_COUNT = 100500;

    MockLogStorageStatus logStorageStatus;
    logStorageStatus.consumedVolume_ = THRESHOLD_SIZE - 1;
    logStorageStatus.recordsCount_ = THRESHOLD_COUNT - 1;

    MockLogFailoverCommand failoverCommand;
    DefaultLogUploadStrategy strategy(clientContext);;

    strategy.setVolumeThreshold(THRESHOLD_SIZE);
    strategy.setCountThreshold(THRESHOLD_COUNT);

    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::NOOP);

    logStorageStatus.recordsCount_ = THRESHOLD_COUNT;
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::UPLOAD);

    logStorageStatus.recordsCount_ = THRESHOLD_COUNT + 1;
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::UPLOAD);
}

BOOST_AUTO_TEST_CASE(OnFailureTest)
{
    MockLogFailoverCommand failoverCommand;
    DefaultLogUploadStrategy strategy(clientContext);;

    const size_t RETRY_PERIOD = 2;
    const std::size_t THRESHOLD_SIZE = 35;
    const std::size_t THRESHOLD_COUNT = 100500;

    MockLogStorageStatus logStorageStatus;
    logStorageStatus.consumedVolume_ = THRESHOLD_SIZE;
    logStorageStatus.recordsCount_ = THRESHOLD_COUNT;

    strategy.setRetryPeriod(RETRY_PERIOD);
    strategy.setVolumeThreshold(THRESHOLD_SIZE);
    strategy.setCountThreshold(THRESHOLD_COUNT);

    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::UPLOAD);

    strategy.onFailure(failoverCommand, LogDeliveryErrorCode::REMOTE_CONNECTION_ERROR);

    BOOST_CHECK(failoverCommand.onRetryLogUploadWithDelay_ == 1);
    BOOST_CHECK(failoverCommand.onRetryLogUpload_ == 0);
    BOOST_CHECK(failoverCommand.onSwitchAccessPoint_ == 0);
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::NOOP);

    std::this_thread::sleep_for(std::chrono::seconds(RETRY_PERIOD / 2));
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::NOOP);

    std::this_thread::sleep_for(std::chrono::seconds(RETRY_PERIOD / 2));
    BOOST_CHECK(strategy.isUploadNeeded(logStorageStatus) == LogUploadStrategyDecision::UPLOAD);
}

BOOST_AUTO_TEST_CASE(OnTimeoutTest)
{
    MockLogFailoverCommand failoverCommand;
    DefaultLogUploadStrategy strategy(clientContext);;

    strategy.onTimeout(failoverCommand);

    BOOST_CHECK(failoverCommand.onSwitchAccessPoint_ == 1);
    BOOST_CHECK(failoverCommand.onRetryLogUploadWithDelay_ == 0);
    BOOST_CHECK(failoverCommand.onRetryLogUpload_ == 0);
}

BOOST_AUTO_TEST_SUITE_END()

}


text:
//=====================================================================//
/*! @file
	@brief  player クラス @n
			Copyright 2017, 2020 Kunihito Hiramatsu
	@author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <boost/lexical_cast.hpp>
#include "player.hpp"
#include "core/glcore.hpp"
#include "widgets/widget_utils.hpp"
#include "widgets/widget_null.hpp"
#include "widgets/widget_button.hpp"
#include "widgets/widget_image.hpp"
#include "snd_io/snd_files.hpp"

namespace app {

	static const char* resume_path_ = { "/player/resume" };
	static const char* volume_path_ = { "/player/volume" };
	static const char* remain_pos_path_  = { "/player/remain/position" };
	static const char* remain_type_path_ = { "/player/remain/type" };
	static const char* remain_file_path_ = { "/player/remain/file" };

	void player::sound_play_(const std::string& file)
	{
		al::sound& sound = director_.at().sound_;

		auto path = utils::get_file_path(file);
		if(!sound.play_stream(path, utils::get_file_name(file))) {
			std::string s = "Can't open input file:  " + path + '/' + file;
			if(error_dialog_) {
				error_dialog_->set_text(s);
				error_dialog_->enable();
			}
		}
	}

	static void set_time_(gui::widget_label* w, time_t t)
	{
		gui::widget_label::param& pa = w->at_local_param();
		time_t tt = t / 3600;
		std::string s;
		if(tt) {
			s  = boost::lexical_cast<std::string>(tt / 10);
			s += boost::lexical_cast<std::string>(tt % 10);
			s += ':';
		}
		tt = t / 60;
		s += boost::lexical_cast<std::string>(tt / 10);
		s += boost::lexical_cast<std::string>(tt % 10);
		s += ':';
		tt = t % 60;
		s += boost::lexical_cast<std::string>(tt / 10);
		s += boost::lexical_cast<std::string>(tt % 10);
		pa.text_param_.set_text(s);
	}

	gui::widget_label* player::create_text_pad_(const vtx::spos& size,
		const std::string& text, const std::string& font, bool proportional)
	{
		using namespace gui;
		widget_director& wd = director_.at().widget_director_;

		widget::param wp(vtx::irect(vtx::ipos(0), size), 0);
		widget_label::param wp_(text);
		wp_.color_param_ = widget_director::default_slider_color_;
		wp_.color_param_select_ = widget_director::default_slider_color_;
		if(!font.empty()) {
			wp_.text_param_.font_ = font;
		}
		wp_.text_param_.proportional_ = proportional;
		wp_.text_param_.placement_.hpt = vtx::placement::holizontal::CENTER;
		if(font.empty()) wp_.text_param_.placement_.vpt = vtx::placement::vertical::CENTER;
		else wp_.text_param_.placement_.vpt = vtx::placement::vertical::BOTTOM;

		wp_.text_param_.placement_.vpt = vtx::placement::vertical::CENTER;

		wp_.plate_param_.resizeble_ = true;
		wp_.shift_param_.every_ = true;
		wp_.shift_param_.org_wait_frame_ = 60 * 4;
		return wd.add_widget<widget_label>(wp, wp_);
	}

	bool player::setup_apic_(const sound::tag_t::apic_t& apic, const std::string& file)
	{
		if(apic.len_ == 0) {
			return false;
		}

		img::img_files imgs;
		utils::file_io fin;
		if(!fin.open(file, "rb")) {
			return false;
		}
		fin.seek(utils::file_io::SEEK::SET, apic.ofs_);
		bool ret = imgs.load(fin, apic.ext_);
		if(ret) {
			img::shared_img sig = imgs.get_image();
			const img::i_img* im = sig.get();
			jacket_ = mobj_.install(im);
		}
		fin.close();
		return ret;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  初期化
	*/
	//-----------------------------------------------------------------//
	void player::initialize()
	{
		auto& core = gl::core::get_instance();

		mobj_.initialize();

		auto& fonts = core.at_fonts();
		auto cf = fonts.get_font_type();
		auto fp = core.get_current_path();
		fp += "/res/seg12.ttf";
		if(!fonts.install_font_type(fp, "led")) {
			std::cerr << "Can't install player's TTF font: '" << fp << "'" << std::endl;
		}
		fonts.set_spaceing(6);
		fonts.set_font_type(cf);

		// サウンド・デコーダーの拡張子設定
		auto& sound = director_.at().sound_;
		tag_serial_ = sound.get_tag_stream().serial_;

		using namespace gui;
		widget_director& wd = director_.at().widget_director_;

		{	// ファイラーリソースの生成
			widget::param wp(vtx::irect(10, 10, 500, 350));
			widget_filer::param wp_(core.get_current_path(), sound.get_file_exts());
			filer_ = wd.add_widget<widget_filer>(wp, wp_);
			filer_->enable(false);
			filer_->at_local_param().select_file_func_ = [this](const std::string& file) {
			   	sound_play_(file);
			};
		}

		// ファイラー起動ボタン（ファイル選択）
		const std::string& curp = core.get_current_path();
		file_btn_  = gui::create_image<widget_button>(wd, curp + "/res/select.png");
		file_btn_->at_local_param().select_func_ = [this](int id) {
			bool f = filer_->get_state(gui::widget::state::ENABLE);
			filer_->enable(!f);
			if(!f) {
				auto& sound = director_.at().sound_;
				filer_->focus_file(sound.get_file_stream());
				files_step_ = 0;
				files_.clear();
			}
		};

		play_btn_  = gui::create_image<widget_button>(wd, curp + "/res/play.png");

		pause_btn_ = gui::create_image<widget_button>(wd, curp + "/res/pause.png");

		rew_btn_   = gui::create_image<widget_button>(wd, curp + "/res/rew.png");
		rew_btn_->at_local_param().select_func_ = [this](int id) {
			al::sound& sound = director_.at().sound_;
			// 開始５秒以降なら、曲の先頭に～
			if(sound.get_time_stream() < seek_change_time_) {
				sound.prior_stream();
			} else {
				sound.replay_stream();
			}
		};

		ff_btn_    = gui::create_image<widget_button>(wd, curp + "/res/ff.png");
		ff_btn_->at_local_param().select_func_ = [this](int id) {
			al::sound& sound = director_.at().sound_;
			sound.next_stream();
		};

		short lw = 40;
		total_time_  = create_text_pad_(vtx::spos(16 * 3, lw), "00:00", "led", false);
		remain_time_ = create_text_pad_(vtx::spos(16 * 3, lw), "00:00", "led", false);
		{
			widget::param wp(vtx::irect(10, 10, 500, 16), 0);
			widget_slider::param wp_;
			if(wd.at_img_files().load(curp + "/res/seek_handle.png")) {
				wp_.hand_image_ = wd.at_img_files().get_image().get();
			}
			wp_.plate_param_.resizeble_ = true;
			wp_.hand_ctrl_ = true;
			seek_handle_ = wd.add_widget<widget_slider>(wp, wp_);
		}

		album_pad_  = create_text_pad_(vtx::spos(16 * 3, lw), "");
		title_pad_  = create_text_pad_(vtx::spos(16 * 3, lw), "");
		artist_pad_ = create_text_pad_(vtx::spos(16 * 3, lw), "");
		other_pad_  = create_text_pad_(vtx::spos(16 * 3, lw), "");

		{
			widget::param wp(vtx::irect(20, 500, 200, 20), 0);
			widget_slider::param wp_;
			if(wd.at_img_files().load(curp + "/res/slider_handle.png")) {
				wp_.hand_image_ = wd.at_img_files().get_image().get();
			}
   			volume_ = wd.add_widget<widget_slider>(wp, wp_);
			volume_->at_local_param().select_func_ = [this](float pos) {
				// ストリームのゲイン(volume)を設定
				al::sound& sound = director_.at().sound_;
				sound.set_gain_stream(pos);
			};
		}
		{
			widget::param wp(vtx::irect(0, 0, 0, 0), 0);
			widget_image::param wp_;
			if(wd.at_img_files().load(curp + "/res/piano.png")) {
				wp_.image_ = wd.at_img_files().get_image().get();
			}
   			vol_min_img_ = wd.add_widget<widget_image>(wp, wp_);
			if(wd.at_img_files().load(curp + "/res/forte.png")) {
				wp_.image_ = wd.at_img_files().get_image().get();
			}
   			vol_max_img_ = wd.add_widget<widget_image>(wp, wp_);
		}
		{
			widget::param wp(vtx::irect(0, 0, 120, 30), 0);
			widget_check::param wp_("Resume");
			resume_play_ = wd.add_widget<widget_check>(wp, wp_);
		}

		// エラー用ダイアログリソースの生成
		{
			const auto& scs = core.get_rect().size;
			short w = 450;
			short h = 150;
			widget::param wp(vtx::irect((scs.x - w) / 2, (scs.y - h) / 2, w, h));
			widget_dialog::param wp_;
   			wp_.style_ = widget_dialog::style::OK;
			error_dialog_ = wd.add_widget<widget_dialog>(wp, wp_);
			const utils::strings& ss = wd.get_error_report();
			if(ss.empty()) {
				error_dialog_->enable(false);
			} else {
				std::string s = utils::strings_to_string(ss, true);
				error_dialog_->set_text(s);
				std::cout << s << std::endl;
			}
		}

		// プリファレンスの取得
		auto& pre = director_.at().preference_;
		pre.get_boolean(resume_path_, resume_play_->at_local_param().check_);
		if(resume_play_->at_local_param().check_) {
			int pos = 0;
			pre.get_integer(remain_pos_path_, pos);
			std::string file;
			pre.get_text(remain_file_path_, file);
			auto path = utils::get_file_path(file);
			int type;
			pre.get_integer(remain_type_path_, type);
			al::sound::stream_state::type t = static_cast<al::sound::stream_state::type>(type);
			if(!file.empty() && !path.empty()) {
				if(t == al::sound::stream_state::PAUSE) {
					sound.play_stream(path, utils::get_file_name(file));
					sound.pause_stream();
					sound.seek_stream(static_cast<size_t>(pos));
				} else if(t == al::sound::stream_state::PLAY) {
					sound.play_stream(path, utils::get_file_name(file));
					sound.seek_stream(static_cast<size_t>(pos));
				}
			}
		}
		{
			float pos;
			pre.get_real(volume_path_, pos);
			volume_->at_slider_param().position_ = pos;
			sound.set_gain_stream(pos);
		}
		if(filer_) {
			filer_->load(pre);
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  アップデート
	*/
	//-----------------------------------------------------------------//
	void player::update()
	{
		gl::core& core = gl::core::get_instance();

		const vtx::spos& size = core.get_rect().size;

		// ウィジェットのコア、更新
		gui::widget_director& wd = director_.at().widget_director_;

		// 画面のサイズに合わせた動的な位置補正
		short btw = 64;
		short btofs = btw / 2;
		short btspc = 10;
		short cenx = size.x / 2;
		short ofsy = size.y - btw - btspc;
		file_btn_-> at_rect().org.set(btspc, ofsy);
		rew_btn_->  at_rect().org.set(cenx - btofs - btspc - btw, ofsy);
		play_btn_-> at_rect().org.set(cenx - btofs, ofsy);
		pause_btn_->at_rect().org.set(cenx - btofs, ofsy);
		ff_btn_->   at_rect().org.set(cenx - btofs + btspc + btw, ofsy);

		short padh = album_pad_->get_rect().size.y;
		short pady = size.y - btspc - btw - btspc - padh;
		short padspc = 10;
		short padsz = (size.x - padspc * 3) / 2;
		album_pad_->at_rect().size.x = padsz;
		album_pad_->at_rect().org.x  = padspc;
		album_pad_->at_rect().org.y  = pady;
		title_pad_->at_rect().size.x = padsz;
		title_pad_->at_rect().org.x  = size.x - padsz - padspc;
		title_pad_->at_rect().org.y  = pady;
		pady -= 10 + padh;
		other_pad_->at_rect().size.x = padsz;
		other_pad_->at_rect().org.x  = padspc;
		other_pad_->at_rect().org.y  = pady;
		artist_pad_->at_rect().size.x = padsz;
		artist_pad_->at_rect().org.x  = size.x - padsz - padspc;
		artist_pad_->at_rect().org.y  = pady;
		pady -= 10 + padh;
		padsz = 120;
		remain_time_->at_rect().size.x = padsz;
		remain_time_->at_rect().org.x = padspc;
		remain_time_->at_rect().org.y = pady;
		total_time_->at_rect().size.x = padsz;
		total_time_->at_rect().org.x  = size.x - padsz - padspc;
		total_time_->at_rect().org.y  = pady;
		seek_handle_->at_rect().org.x   = padsz + padspc * 2;
		short ofs = total_time_->at_rect().size.y - seek_handle_->at_rect().size.y;
		seek_handle_->at_rect().org.y   = pady + ofs / 2;
		seek_handle_->at_rect().size.x  = size.x - padsz * 2 - padspc * 4;

		short vcen = size.x - (size.x / 2 + btw / 2 + btspc + btw);
		volume_->at_rect().org.x = size.x - vcen / 2 - volume_->get_rect().size.x / 2;
		short btofsy = size.y - btspc - btw + btw / 2;
		volume_->at_rect().org.y = btofsy - volume_->get_rect().size.y / 2;
		vol_min_img_->at_rect().org.x = volume_->get_rect().org.x - 10 -
			vol_min_img_->at_rect().size.x;
		vol_min_img_->at_rect().org.y = btofsy - vol_min_img_->get_rect().size.y / 2;
		vol_max_img_->at_rect().org.x = volume_->get_rect().end().x + 10;
		vol_max_img_->at_rect().org.y = btofsy - vol_max_img_->get_rect().size.y / 2;

		resume_play_->at_rect().org.x = size.x / 4
			- (resume_play_->get_rect().size.x / 2);
		resume_play_->at_rect().org.y = btofsy - resume_play_->get_rect().size.y / 2;

		// ボタンの状態を設定
		wd.enable(play_btn_, false);
		wd.enable(pause_btn_, false);
		auto& sound = director_.at().sound_;
		std::string state;
		if(sound.get_state_stream() == al::sound::stream_state::STALL) {
			wd.enable(play_btn_);
			play_btn_->set_state(gui::widget::state::STALL);
			rew_btn_->set_state(gui::widget::state::STALL);
			ff_btn_->set_state(gui::widget::state::STALL);
			seek_handle_->set_state(gui::widget::state::STALL);
			state = " (stalled)";
		} else if(sound.get_state_stream() == al::sound::stream_state::PLAY) {
			play_btn_->set_state(gui::widget::state::STALL, false);
			rew_btn_->set_state(gui::widget::state::STALL, false);
			ff_btn_->set_state(gui::widget::state::STALL, false);
			seek_handle_->set_state(gui::widget::state::STALL, false);
			wd.enable(pause_btn_);
			if(pause_btn_->get_selected()) {
				sound.pause_stream();
			}
			state = " (playing)";
		} else if(sound.get_state_stream() == al::sound::stream_state::PAUSE) {
			wd.enable(play_btn_);
			play_btn_->set_state(gui::widget::state::STALL, false);
			rew_btn_->set_state(gui::widget::state::STALL, false);
			ff_btn_->set_state(gui::widget::state::STALL, false);
			seek_handle_->set_state(gui::widget::state::STALL, false);
			if(play_btn_->get_selected()) {
				sound.pause_stream(false);
			}
			state = " (pause)";
		} else {	// to stop.
			wd.enable(play_btn_);
			play_btn_->set_state(gui::widget::state::STALL);
			state = " (stoped)";
		}
		core.set_title(sound.get_file_stream() + state);

		// 時間表示
		remain_t_ = sound.get_time_stream();
		total_t_ = sound.get_end_time_stream();
		if(sound.get_state_stream() == al::sound::stream_state::STALL) {
			total_t_ = 0;
			remain_t_ = 0;
		}
		set_time_(total_time_, total_t_);
		if(seek_handle_->get_select()) {
			const gui::widget::slider_param& sp = seek_handle_->get_slider_param();
			remain_t_ = sp.position_ * sound.get_end_time_stream();
		}
		set_time_(remain_time_, remain_t_);

		// シークハンドルの操作と応答
		float stream_length = static_cast<float>(sound.get_length_stream());
		if(seek_handle_->get_select_in()) {
			const gui::widget::slider_param& sp = seek_handle_->get_slider_param();
			seek_pos_ = sp.position_ * stream_length;
		}
		if(seek_handle_->get_select_out()) {
			const gui::widget::slider_param& sp = seek_handle_->get_slider_param();
			size_t pos = sp.position_ * stream_length;
			sound.seek_stream(pos);
		} else if(seek_handle_->get_select()) {
			const gui::widget::slider_param& sp = seek_handle_->get_slider_param();
			uint32_t new_pos = sp.position_ * stream_length;
			if(seek_pos_ != new_pos) {
				seek_pos_ = new_pos;
				sound.seek_stream(seek_pos_);
			}
		} else {
			gui::widget::slider_param& sp = seek_handle_->at_slider_param();
			if(total_t_) {
				float pos = static_cast<float>(sound.get_position_stream());
				sp.position_ = pos / static_cast<float>(sound.get_length_stream());
			} else {
				sp.position_ = 0.0f;
			}
		}

		// 曲の情報を取得して表示
		const auto& tag = sound.get_tag_stream();
		if(tag_serial_ != tag.serial_) {
			gui::set_widget_text(album_pad_, tag.get_album());
			gui::set_widget_text(title_pad_, tag.get_title());
			std::string s;
			if(!tag.get_year().empty()) {
				s += "Y:" + tag.get_year();
			}
			if(!tag.get_disc().empty()) {
				if(!s.empty()) s += ' ';
				s += "D:" + tag.get_disc();
			}
			if(!tag.get_track().empty()) {
				if(!s.empty()) s += ' ';
				s += "T:" + tag.get_track();
			}
			if(!tag.get_date().empty()) {
				if(!s.empty()) s += " : ";
				s += tag.get_date();
			}
			gui::set_widget_text(other_pad_, s);
			s = tag.get_artist();
			if(!tag.get_writer().empty()) {
				s += " / " + tag.get_writer();
			}
			gui::set_widget_text(artist_pad_, s);

			// ジャケット画像の取得とテクスチャーへの登録
			mobj_.destroy();
			mobj_.initialize();
			if(!setup_apic_(tag.get_apic(), sound.get_file_stream())) {
				const std::string& curp = core.get_current_path();
				if(wd.at_img_files().load(curp + "/res/NoImage.png")) {
					jacket_ = mobj_.install(wd.at_img_files().get_image().get());
				}
			}

			// 演奏ファイル名を取得して、ファイラーのフォーカスを設定
			{
				filer_->focus_file(sound.get_file_stream());
			}
			tag_serial_ = tag.serial_;
		}

		// ファイラーが有効で、マウス操作が無い状態が５秒続いたら、
		// ※スクロール・ダイアルの操作
		// 演奏ファイルパスへフォーカスする
   		if(filer_->get_state(gui::widget::state::ENABLE)) {
			const vtx::spos& msp = core.get_device().get_locator().get_cursor();
			const vtx::spos& scr = core.get_device().get_locator().get_scroll();
			if(msp == mouse_pos_ && scr == mouse_scr_) {
				++filer_count_;
				if(filer_count_ >= (60 * 5)) {
					filer_->focus_file(sound.get_file_stream());
				}
			} else {
				filer_count_ = 0;
				mouse_pos_ = msp;
				mouse_scr_ = scr;
			}

			// ファイルのタグ情報をファイラーのエイリアスに設定
			if(filer_->get_file_state()) {
				files_.clear();
				files_step_ = 0;
			} else {
				if(files_.empty()) {
					if(!sound.state_tag_info()) {
						files_ = filer_->get_file_list();
						tag_info_serial_ = sound.get_tag_info().serial_;
						tag_wait_ = false;
					}
				} else if(files_step_ < files_.size()) {
					const auto& t = sound.get_tag_info();
					if(t.serial_ == tag_info_serial_) {
						if(!tag_wait_) {
							const std::string fn = files_[files_step_];
							sound.request_tag_info(fn);
							tag_wait_ = true;
						}
					} else {
						tag_wait_ = false;
						tag_info_serial_ = t.serial_;
						using namespace std;
						const string p = utils::get_file_name(files_[files_step_]);
						// 75% 一致しない場合
						if(utils::compare(p, t.get_title()) < 0.75f) {
							if(t.get_title().empty()) {
								filer_->set_alias(p, p);
							} if(!t.get_track().empty()) {
								string n;
								auto pos = t.get_track().find('/');
								if(pos != string::npos) {
									n = t.get_track().substr(0, pos);
								} else {
									n = t.get_track();
								}
								if(n.size() == 1) {
									if(n[0] == '0') n.clear();
									else if(n[0] >= '0' && n[0] <= '9') {
										n = '0' + n;
									}
								}
								if(!n.empty()) n += ' ';
								filer_->set_alias(p, n + t.get_title());
							} else {
								filer_->set_alias(p, t.get_title());
							}
						}
						++files_step_;
					}
				}
			}
		}

		// Drag & Drop されたファイルを再生
		int id = core.get_recv_files_id();
		if(drop_file_id_ != id) {
			drop_file_id_ = id;
			const utils::strings& ss = core.get_recv_files_path();
			if(!ss.empty()) {
				std::string file = ss[0];
				if(ss.size() > 1) {
					if(file > ss.back()) file = ss.back();
				}
				sound_play_(file);
			}
		}

		wd.update();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  レンダリング
	*/
	//-----------------------------------------------------------------//
	void player::render()
	{
		auto& core = gl::core::get_instance();
		const auto& siz = core.get_rect().size;

		if(jacket_) {
			mobj_.setup_matrix(siz.x, siz.y);
			float refs = static_cast<float>(siz.x) * 0.5f;
			float scale = refs / static_cast<float>(mobj_.get_size(jacket_).x);
			glScalef(scale, scale, scale);
			float sci = 1.0f / scale;
			float ofsx = (siz.x * 0.5f) / 2 * sci;
			float ofsy = 10.0f * sci;
			mobj_.draw(jacket_, gl::mobj::attribute::normal, vtx::spos(ofsx, ofsy));
			mobj_.restore_matrix();
		}

		director_.at().widget_director_.render();

		director_.at().widget_director_.service();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  廃棄
	*/
	//-----------------------------------------------------------------//
	void player::destroy()
	{
		auto& pre = director_.at().preference_;

		// remain time
		auto& sound = director_.at().sound_;
		int po = 0;
		std::string fn;
		if(sound.get_state_stream() == al::sound::stream_state::PLAY ||
			sound.get_state_stream() == al::sound::stream_state::PAUSE) {
			po = static_cast<int>(sound.get_position_stream());
			fn = sound.get_file_stream();
		}
		pre.put_integer(remain_type_path_, sound.get_state_stream());
		pre.put_integer(remain_pos_path_, po);
		pre.put_text(remain_file_path_, fn);

		// Resume check box
		pre.put_boolean(resume_path_, resume_play_->get_local_param().check_);
		// volume slider
		pre.put_real(volume_path_, volume_->get_local_param().slider_param_.position_);

		if(filer_) {
			filer_->save(pre);
		}
	}
}

ext: cpp


text:
/**
 * @file test_invalid.cpp
 * @brief This file defines the test cases for the invalid checks.
 *
 * @copyright Copyright (c) 2022
 *
 */

#include <boost/test/unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <memory>
#include <string>
#include <vector>

#include "proseco_planning/action/action.h"
#include "proseco_planning/action/actionClass.h"
#include "proseco_planning/agent/agent.h"
#include "proseco_planning/agent/vehicle.h"
#include "proseco_planning/collision_checker/collisionChecker.h"
#include "proseco_planning/collision_checker/collisionCheckerCircleApproximation.h"
#include "proseco_planning/config/configuration.h"
#include "proseco_planning/config/defaultConfiguration.h"
#include "proseco_planning/config/scenarioOptions.h"
#include "proseco_planning/node.h"
#include "proseco_planning/trajectory/trajectorygenerator.h"

using namespace proseco_planning;

struct ThreeLaneFixture {
  ThreeLaneFixture() {
    auto road     = config::Road(false, 3, 3.5, 0);
    auto scenario = config::Scenario("simple", road, config::agents, config::obstacles);
    Config::create(scenario, config::optionsSimple);
  }

  ~ThreeLaneFixture() { Config::get()->reset(); }
};

struct DefaultFixture {
  DefaultFixture() { Config::create(config::scenarioSimple, config::optionsSimple); }

  ~DefaultFixture() { Config::get()->reset(); }
};

BOOST_AUTO_TEST_SUITE(invalidState)

BOOST_FIXTURE_TEST_CASE(rightBoundary, ThreeLaneFixture) {
  auto doNothing = std::make_shared<Action>(Action(ActionClass::DO_NOTHING, 0, 0));
  std::vector<Agent> agents;
  for (const auto& agent : sOpt().agents) {
    agents.emplace_back(agent);
  }

  auto collisionChecker = CollisionChecker::createCollisionChecker("circleApproximation");
  // create TrajectoryGenerator
  auto trajectoryGeneratorP = TrajectoryGenerator::createTrajectoryGenerator("jerkOptimal");

  auto rootNode = std::make_unique<Node>(agents);

  // default vehicle dimension: LxW: 4x2
  // Without orientation change
  rootNode->m_agents[0].m_vehicle.m_positionY = 0.99;

  rootNode->m_agents[1].m_vehicle.m_positionY = 1.5;
  rootNode->m_agents[1].m_vehicle.m_heading   = -3.14 / 2.0;

  rootNode->m_agents[2].m_vehicle.m_positionY = -5.0;

  // Set action
  rootNode->m_agents[0].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[1].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[2].setAction(doNothing, *trajectoryGeneratorP);

  rootNode->checkValidity();

  // for visualizing the actions
  {
    // Create temporary derived class pointer to set members for debugging
    auto temp        = static_cast<CollisionCheckerCircleApproximation*>(collisionChecker.get());
    temp->m_exporter = true;
    temp->m_fileName = "debugCircleApproximation";
  }
  collisionChecker->collision(rootNode->m_agents[0].m_vehicle, rootNode->m_agents[0].m_trajectory,
                              rootNode->m_agents[1].m_vehicle, rootNode->m_agents[1].m_trajectory);

  BOOST_CHECK(rootNode->m_agents[0].m_invalid);
  BOOST_CHECK(rootNode->m_agents[1].m_invalid);
  BOOST_CHECK(rootNode->m_agents[2].m_invalid);
}

BOOST_FIXTURE_TEST_CASE(leftBoundary, ThreeLaneFixture) {
  auto changeLeft  = std::make_shared<Action>(Action(2.0, sOpt().road.lane_width));
  auto changeRight = std::make_shared<Action>(Action(2.0, -sOpt().road.lane_width));

  std::vector<Agent> agents;
  for (const auto& agent : sOpt().agents) {
    agents.emplace_back(agent);
  }
  // create TrajectoryGenerator
  auto trajectoryGeneratorP = TrajectoryGenerator::createTrajectoryGenerator("jerkOptimal");

  auto rootNode = std::make_unique<Node>(agents);

  // default vehicle dimension: LxW: 4x2
  // Without orientation change
  rootNode->m_agents[0].m_vehicle.setLane(1);
  rootNode->m_agents[0].m_vehicle.m_velocityX = 10;

  rootNode->m_agents[1].m_vehicle.setLane(1);
  rootNode->m_agents[1].m_vehicle.m_positionX = 20;
  rootNode->m_agents[1].m_vehicle.m_heading   = 0;
  rootNode->m_agents[1].m_vehicle.m_velocityX = 10;

  rootNode->m_agents[2].m_vehicle.setLane(1);
  rootNode->m_agents[2].m_vehicle.m_velocityX = 10;

  // Set action
  rootNode->m_agents[0].setAction(changeLeft, *trajectoryGeneratorP);
  rootNode->m_agents[1].setAction(changeLeft, *trajectoryGeneratorP);
  rootNode->m_agents[2].setAction(changeRight, *trajectoryGeneratorP);

  rootNode->checkValidity();
  BOOST_CHECK(!rootNode->m_agents[0].m_invalid);
  BOOST_CHECK(!rootNode->m_agents[1].m_invalid);
  BOOST_CHECK(!rootNode->m_agents[2].m_invalid);
}

BOOST_FIXTURE_TEST_CASE(laneChange, DefaultFixture) {
  auto doNothing = std::make_shared<Action>(Action(ActionClass::DO_NOTHING, 0, 0));
  std::vector<Agent> agents;
  for (const auto& agent : sOpt().agents) {
    agents.emplace_back(agent);
  }
  // create TrajectoryGenerator
  auto trajectoryGeneratorP = TrajectoryGenerator::createTrajectoryGenerator("jerkOptimal");

  auto rootNode = std::make_unique<Node>(agents);

  // default vehicle dimension: LxW: 4x2
  // Without orientation change
  rootNode->m_agents[0].m_vehicle.m_positionY = 7.01;

  rootNode->m_agents[1].m_vehicle.m_positionY = 5.5;
  rootNode->m_agents[1].m_vehicle.m_heading   = 3.14 / 2.0;

  rootNode->m_agents[2].m_vehicle.m_positionY = 12.0;

  // Set action
  rootNode->m_agents[0].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[1].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[2].setAction(doNothing, *trajectoryGeneratorP);

  rootNode->checkValidity();

  BOOST_CHECK(rootNode->m_agents[0].m_invalid);
  BOOST_CHECK(rootNode->m_agents[1].m_invalid);
  BOOST_CHECK(rootNode->m_agents[2].m_invalid);
}

BOOST_FIXTURE_TEST_CASE(debugging_3veh, DefaultFixture) {
  auto doNothing = std::make_shared<Action>(Action(ActionClass::DO_NOTHING, 0, 0));
  std::vector<Agent> agents;
  for (const auto& agent : sOpt().agents) {
    agents.emplace_back(agent);
  }
  // create TrajectoryGenerator
  auto trajectoryGeneratorP = TrajectoryGenerator::createTrajectoryGenerator("jerkOptimal");

  auto rootNode = std::make_unique<Node>(agents);

  // default vehicle dimension: LxW: 4x2
  // Without orientation change
  rootNode->m_agents[0].m_vehicle.m_positionY = 7.01;

  rootNode->m_agents[1].m_vehicle.m_positionY = 5.5;
  rootNode->m_agents[1].m_vehicle.m_heading   = 3.14 / 2.0;

  rootNode->m_agents[2].m_vehicle.m_positionY = 12.0;

  // Set action
  rootNode->m_agents[0].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[1].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[2].setAction(doNothing, *trajectoryGeneratorP);

  rootNode->checkValidity();

  BOOST_CHECK(rootNode->m_agents[0].m_invalid);
  BOOST_CHECK(rootNode->m_agents[1].m_invalid);
  BOOST_CHECK(rootNode->m_agents[2].m_invalid);
}

BOOST_FIXTURE_TEST_CASE(debugging, DefaultFixture) {
  auto doNothing = std::make_shared<Action>(Action(ActionClass::DO_NOTHING, 0, 0));
  std::vector<Agent> agents;
  for (const auto& agent : sOpt().agents) {
    agents.emplace_back(agent);
  }
  // create TrajectoryGenerator
  auto trajectoryGeneratorP = TrajectoryGenerator::createTrajectoryGenerator("jerkOptimal");

  auto rootNode = std::make_unique<Node>(agents);

  // default vehicle dimension: LxW: 4x2
  // Without orientation change
  rootNode->m_agents[0].m_vehicle.m_positionX     = 42.828947;
  rootNode->m_agents[0].m_vehicle.m_positionY     = 1.972868;
  rootNode->m_agents[0].m_vehicle.m_velocityX     = -0.0575;
  rootNode->m_agents[0].m_vehicle.m_velocityY     = -0.001995;
  rootNode->m_agents[0].m_vehicle.m_accelerationX = -1.44011;
  rootNode->m_agents[0].m_vehicle.m_accelerationY = -0.015022;
  rootNode->m_agents[0].m_vehicle.m_heading       = 0.0;

  auto action1 = std::make_shared<Action>(-0.071, 0.89);

  // Set action
  rootNode->m_agents[0].setAction(action1, *trajectoryGeneratorP);
  rootNode->m_agents[1].setAction(doNothing, *trajectoryGeneratorP);
  rootNode->m_agents[2].setAction(doNothing, *trajectoryGeneratorP);

  rootNode->checkValidity();

  BOOST_CHECK(rootNode->m_agents[0].m_invalid);
}

BOOST_AUTO_TEST_SUITE_END()


text:
// test/html/simple_html.cpp

#include <boost/test/unit_test.hpp>

#include <wordring/html/simple_html.hpp>

#include <wordring/compatibility.hpp>
#include <wordring/tree/tree.hpp>
#include <wordring/tree/tree_iterator.hpp>

#include <iterator>
#include <string>

namespace
{
	auto get_document(wordring::html::u8simple_tree& t)
	{
		auto it1 = wordring::tree_iterator<decltype(t.begin())>(t.begin());
		auto it2 = wordring::tree_iterator<decltype(t.begin())>();

		while (it1 != it2)
		{
			if (it1->is_document()) return it1.base();
			++it1;
		}

		return t.end();
	}
}

BOOST_AUTO_TEST_SUITE(simple_html_test)

BOOST_AUTO_TEST_CASE(simple_html_make_document_1)
{
	using namespace wordring::html;

	std::u8string const in = u8"あいうえお";

	std::u8string const s = u8R"*(あいうえお)*";

	auto tree = make_document<u8simple_tree>(in.begin(), in.end());
	auto doc = get_document(tree);

	std::u8string out;
	to_string(doc, std::back_inserter(out));

	BOOST_CHECK(s == out);
}

BOOST_AUTO_TEST_CASE(simple_html_make_document_2)
{
	using namespace wordring::html;

	std::u8string const in = "<meta charset=\"shift-jis\">\x82\xA0\x82\xA2\x82\xA4\x82\xA6\x82\xA8";

	std::u8string const s = u8R"*(<meta charset="shift-jis">あいうえお)*";

	auto tree = make_document<u8simple_tree>(in.begin(), in.end());
	auto doc = get_document(tree);

	std::u8string out;
	to_string(doc, std::back_inserter(out));

	BOOST_CHECK(s == out);
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: wordring/wordring

max_issues_repo_name: wordring/libwordring

max_forks_repo_name: wordring/libwordring


text:
// Copyright (c) 2020 Graphcore Ltd. All rights reserved.
#include <popart/adaptive.hpp>
#include <popart/error.hpp>
#include <popart/graph.hpp>
#include <popart/ir.hpp>
#include <popart/op/adaptivecombo.hpp>
#include <popart/sessionoptions.hpp>
#include <popart/tensor.hpp>
#include <popart/tensordata.hpp>
#include <popart/tensornames.hpp>
#include <popart/tensors.hpp>
#include <popart/util.hpp>

#include <boost/functional/hash.hpp>

namespace popart {

Adaptive
Adaptive::fromDefaultMap(const std::map<std::string, OptimizerValue> &m,
                         AdaptiveMode adaptiveMode_,
                         WeightDecayMode decayMode_,
                         DataType accumType_,
                         DataType accl1Type_,
                         DataType accl2Type_,
                         DataType accl3Type_) {
  return Adaptive(getComplete(m),
                  adaptiveMode_,
                  decayMode_,
                  accumType_,
                  accl1Type_,
                  accl2Type_,
                  accl3Type_,
                  1011);
}

namespace {
const std::vector<std::string> &getSpecificNames() {
  const static std::vector<std::string> names{
      "learningRate", "weightDecay", "alpha", "momentum", "eps"};
  return names;
}
} // namespace

Adaptive::Adaptive(const std::map<std::string, std::pair<float, bool>> &m,
                   AdaptiveMode adaptiveMode_,
                   WeightDecayMode decayMode_,
                   DataType accumType_,
                   DataType accl1Type_,
                   DataType accl2Type_,
                   DataType accl3Type_,
                   bool rmspropTFVariant)
    : Adaptive(getComplete(getOptMap(m)),
               adaptiveMode_,
               decayMode_,
               accumType_,
               accl1Type_,
               accl2Type_,
               accl3Type_,
               31415,
               rmspropTFVariant) {}

void Adaptive::insertSpecific(
    const TensorId &id,
    const std::map<std::string, std::pair<float, bool>> &m0) {

  const auto &names = getSpecificNames();

  std::map<std::string, OptimizerValue> complete;

  // Note that these take the default values already set in the maps, not the
  // "unset" values. So if a user has set default momentum to 0.7, and there is
  // no momentum key in m0, then this tensor "id" will have momentum 0.7.
  complete.insert({"learningRate", lrs.getDefault()});
  complete.insert({"weightDecay", wds.getDefault()});
  complete.insert({"alpha", as.getDefault()});
  complete.insert({"momentum", ms.getDefault()});
  complete.insert({"eps", epsvs.getDefault()});
  for (auto key_val : m0) {
    if (std::find(names.cbegin(), names.cend(), key_val.first) ==
        names.cend()) {
      std::ostringstream oss;
      oss << "Invalid key " << key_val.first
          << " in Adaptive::insertSpecific. Permitted keys are ( ";
      for (auto x : names) {
        oss << x << ' ';
      }
      oss << ')';
      throw error(oss.str());
    }
    complete[key_val.first] = key_val.second;
  }

  insertSpecific(id,
                 complete.at("learningRate"),
                 complete.at("weightDecay"),
                 complete.at("alpha"),
                 complete.at("momentum"),
                 complete.at("eps"));
}

bool Adaptive::hasSpecific(const Tensor &w) const {

  // confirm that all the atomic scalars have a specific value for "w"
  const auto &id = w.id;
  int counter    = 0;
  counter += lrs.hasSpecific(id);
  counter += wds.hasSpecific(id);
  counter += as.hasSpecific(id);
  counter += ms.hasSpecific(id);
  counter += epsvs.hasSpecific(id);

  if (counter != 0 && counter != getSpecificNames().size()) {
    throw error(
        "Inconsistency in Adaptive::hasSpecific : there should either be a "
        "specific value for ALL (5) or NO (0) atomic scalar values, "
        "not {} of them. ",
        counter);
  }

  return counter > 0;
}

bool Adaptive::hasSpecific() const {
  auto specifics = {lrs.hasSpecific(),
                    wds.hasSpecific(),
                    as.hasSpecific(),
                    ms.hasSpecific(),
                    epsvs.hasSpecific()};
  return std::any_of(
      specifics.begin(), specifics.end(), [](bool s) { return s; });
}

void Adaptive::insertSpecific(const TensorId &id,
                              OptimizerValue lr,
                              OptimizerValue wd,
                              OptimizerValue a,
                              OptimizerValue m,
                              OptimizerValue eps) {

  lrs.insertSpecific(id, lr);
  wds.insertSpecific(id, wd);
  as.insertSpecific(id, a);
  ms.insertSpecific(id, m);
  epsvs.insertSpecific(id, eps);

  runValueChecks(lr, wd, a, m, eps);
}

TensorId Adaptive::getInverseLossScalingTensorId(const Tensor &weight) const {
  return getInputIds(weight).at(AdaptiveComboOp::getGsInIndex());
}

void Adaptive::runValueChecks(OptimizerValue lr,
                              OptimizerValue wd,
                              OptimizerValue a,
                              OptimizerValue m,
                              OptimizerValue eps) const {
  if (lr.val() < 0) {
    throw error(
        "Negative learning rate ({}) in Adaptive, bailing as this might be "
        "a user error.",
        lr.val());
  } else if (lr.val() == 0.0f && lr.isConst()) {
    throw error(
        "Constant, zero learning rate in Adaptive, bailing as this might be "
        "a user error.");
  }

  if (wd.val() < 0) {
    throw error(
        "Negative weight decay ({}) in Adaptive, bailing as this might be a "
        "user error",
        wd.val());
  }

  if (a.val() < 0) {
    throw error("Negative alpha ({}) in Adaptive, bailing as this might be a "
                "user error",
                a.val());
  }

  if (m.val() < 0) {
    throw error(
        "Negative momentum ({}) in Adaptive, bailing as this might be a "
        "user error",
        a.val());
  }

  if (eps.val() <= 0) {
    throw error("Non-positive eps ({}) in Adaptive is not supported",
                eps.val());
  }
}

Adaptive::Adaptive(const std::map<std::string, OptimizerValue> &cmap,
                   AdaptiveMode mode_,
                   WeightDecayMode decayMode_,
                   DataType accumType_,
                   DataType accl1Type_,
                   DataType accl2Type_,
                   DataType accl3Type_,
                   int,
                   bool rmspropTFVariant)
    : Adaptive(cmap.at("defaultLearningRate"),
               cmap.at("defaultWeightDecay"),
               cmap.at("defaultAlpha"),
               cmap.at("defaultMomentum"),
               cmap.at("defaultEps"),
               cmap.at("lossScaling"),
               mode_,
               decayMode_,
               accumType_,
               accl1Type_,
               accl2Type_,
               accl3Type_,
               rmspropTFVariant) {}

Adaptive::Adaptive(OptimizerValue lr,
                   OptimizerValue wd,
                   OptimizerValue a,
                   OptimizerValue m,
                   OptimizerValue eps,
                   OptimizerValue lossScaling,
                   AdaptiveMode mode_,
                   WeightDecayMode decayMode_,
                   DataType accumType_,
                   DataType accl1Type_,
                   DataType accl2Type_,
                   DataType accl3Type_,
                   bool rmspropTFVariant_)
    : Optimizer(lossScaling, {}), lrs(lr), wds(wd), as(a), ms(m), epsvs(eps),
      mode(mode_), decayMode(decayMode_), accumType(accumType_),
      accl1Type(accl1Type_), accl2Type(accl1Type_), accl3Type(accl3Type_),
      rmspropTFVariant(rmspropTFVariant_) {
  if (rmspropTFVariant_ && mode != AdaptiveMode::RMSProp &&
      mode != AdaptiveMode::CenteredRMSProp) {
    throw error("The rmspropTFVariant parameter is only valid with the RMSProp "
                "and CenteredRMSProp optimizers.");
  }
  runValueChecks(lr, wd, a, m, eps);
}

std::map<std::string, OptimizerValue>
Adaptive::getComplete(const std::map<std::string, OptimizerValue> &m) {

  std::vector<std::string> argNames{"defaultLearningRate",
                                    "defaultWeightDecay",
                                    "defaultAlpha",
                                    "defaultMomentum",
                                    "defaultEps",
                                    "lossScaling"};

  std::map<std::string, OptimizerValue> complete{};

  complete.insert({"defaultLearningRate", getUnsetLearningRate()});
  complete.insert({"defaultWeightDecay", getUnsetWeightDecay()});
  complete.insert({"defaultAlpha", getUnsetAlpha()});
  complete.insert({"defaultMomentum", getUnsetMomentum()});
  complete.insert({"defaultEps", getUnsetEps()});
  complete.insert({"lossScaling", getUnsetLossScaling()});

  for (auto key_val : m) {
    auto key = key_val.first;
    auto val = key_val.second;
    if (std::find(argNames.cbegin(), argNames.cend(), key) == argNames.cend()) {
      std::ostringstream oss;
      oss << "Invalid Adaptive key, " << key << ", the allowed keys are ( ";
      for (auto x : argNames) {
        oss << x << ' ';
      }
      oss << ')';
      throw error(oss.str());
    }
    complete[key] = val;
  }

  return complete;
}

std::unique_ptr<Op> Adaptive::createOp(const Tensor &w, Graph &graph) const {

  auto opSettings = Op::Settings(graph, "");

  for (Op *op : w.consumers.getOps()) {
    for (auto &outlineAttribute : op->settings.extraOutlineAttributes) {
      opSettings.extraOutlineAttributes.insert(outlineAttribute);
    }
  }

  OptimizerReductionType reductionType{OptimizerReductionType::None};

  if (getReplicatedGraphCount() > 1) {
    if (gradientAccumulationEnabled()) {
      reductionType = OptimizerReductionType::AccumReduce;
    } else {
      // Disable acclReduce in favor of gradReduce when not using gradient
      // accumulation
      reductionType = OptimizerReductionType::GradReduce;
    }
  }

  return std::make_unique<AdaptiveComboOp>(
      lrhelper.getFromWeightId(w.id, *this),
      wdhelper.getFromWeightId(w.id, *this),
      ahelper.getFromWeightId(w.id, *this),
      mhelper.getFromWeightId(w.id, *this),
      epshelper.getFromWeightId(w.id, *this),
      lshelper.getFromWeightId(w.id, *this),
      gshelper.getFromWeightId(w.id, *this),
      mode,
      decayMode,
      gradientAccumulationEnabled(),
      reductionType,
      accumType == DataType::UNDEFINED ? w.info.getDataTypeInfo()->type()
                                       : accumType,
      accl1Type == DataType::UNDEFINED ? w.info.getDataTypeInfo()->type()
                                       : accl1Type,
      accl2Type == DataType::UNDEFINED ? w.info.getDataTypeInfo()->type()
                                       : accl2Type,
      accl3Type == DataType::UNDEFINED ? w.info.getDataTypeInfo()->type()
                                       : accl3Type,
      rmspropTFVariant,
      opSettings);
}

std::vector<TensorId> Adaptive::getInputIds(const Tensor &w) const {
  const TensorId &varId = w.id;
  std::vector<TensorId> inputs(9, "");

  // variable
  inputs[VarUpdateOp::getVarToUpdateInIndex()] = varId;

  // gradient
  inputs[VarUpdateWithUpdaterOp::getUpdaterInIndex()] = getGradId(varId);

  // lrhelper
  inputs[AdaptiveComboOp::getLrInIndex()] =
      lrhelper.getScalarIdIfNonConst(w, *this);

  // wdhelper
  inputs[AdaptiveComboOp::getWdInIndex()] =
      wdhelper.getScalarIdIfNonConst(w, *this);

  // alpha
  inputs[AdaptiveComboOp::getAlphaInIndex()] =
      ahelper.getScalarIdIfNonConst(w, *this);

  // momentum
  inputs[AdaptiveComboOp::getMomentumInIndex()] =
      mhelper.getScalarIdIfNonConst(w, *this);

  // eps
  inputs[AdaptiveComboOp::getEpsInIndex()] =
      epshelper.getScalarIdIfNonConst(w, *this);

  // loss scaling
  inputs[AdaptiveComboOp::getLsInIndex()] =
      lshelper.getScalarIdIfNonConst(w, *this);

  // gradient scaling
  inputs[AdaptiveComboOp::getGsInIndex()] =
      gshelper.getScalarIdIfNonConst(w, *this);

  return inputs;
}

std::vector<std::tuple<TensorId, TensorInfo>>
Adaptive::getOptimizerInputs(const Tensor &weight) const {

  std::vector<TensorId> ids;

  ids.push_back(lrhelper.getScalarIdIfNonConst(weight, *this));
  ids.push_back(wdhelper.getScalarIdIfNonConst(weight, *this));
  ids.push_back(ahelper.getScalarIdIfNonConst(weight, *this));
  ids.push_back(mhelper.getScalarIdIfNonConst(weight, *this));
  ids.push_back(epshelper.getScalarIdIfNonConst(weight, *this));
  ids.push_back(lshelper.getScalarIdIfNonConst(weight, *this));
  ids.push_back(gshelper.getScalarIdIfNonConst(weight, *this));

  std::vector<std::tuple<TensorId, TensorInfo>> optInputs;
  for (const auto &id : ids) {
    if (!id.empty()) {
      auto tuppy = std::make_tuple(id, TensorInfo(DataType::FLOAT, {}));
      optInputs.push_back(tuppy);
    }
  }

  return optInputs;
}

void Adaptive::setTensorData(Tensor &optTensor) const {
  const auto &info   = optTensor.info;
  float storedValue  = getStoredValue(optTensor.id);
  auto convertedData = convertFloatToDataType(info.dataType(), storedValue);

  logging::ir::trace(
      "Setting TensorData for {} to {}", optTensor.str(), storedValue);
  optTensor.setTensorData(info, convertedData.data());
}

void Adaptive::resetTensorData(Tensor &optTensor) const {
  const auto &info   = optTensor.info;
  float storedValue  = getStoredValue(optTensor.id);
  auto convertedData = convertFloatToDataType(info.dataType(), storedValue);
  logging::ir::trace(
      "Resetting TensorData for {} to {}", optTensor.str(), storedValue);
  optTensor.tensorData()->resetData(info, convertedData.data());
}

float Adaptive::getStoredValue(const TensorId &optId) const {

  if (optId.find(reservedLossScalingPrefix()) != std::string::npos) {
    return lossScaling().val();
  }

  if (ahelper.idMatch(optId)) {
    return ahelper.getFromScalarId(optId, *this).val();
  }

  if (mhelper.idMatch(optId)) {
    return mhelper.getFromScalarId(optId, *this).val();
  }

  if (wdhelper.idMatch(optId)) {
    return wdhelper.getFromScalarId(optId, *this).val();
  }

  if (epshelper.idMatch(optId)) {
    return epshelper.getFromScalarId(optId, *this).val();
  }

  if (lrhelper.idMatch(optId)) {
    return lrhelper.getFromScalarId(optId, *this).val();
  }

  if (lshelper.idMatch(optId)) {
    return lshelper.getFromScalarId(optId, *this).val();
  }

  if (gshelper.idMatch(optId)) {
    return gshelper.getFromScalarId(optId, *this).val();
  }

  throw runtime_error("In getStoredValue for {}, it doesn't match any existing "
                      "optimizer prefix",
                      optId);
}

void Adaptive::validReplacement(const Optimizer &other) const {
  Optimizer::validReplacement(other);

  auto asAdaptive = dynamic_cast<const Adaptive *>(&other);
  if (!asAdaptive) {
    throw internal_error(
        "other has same `type' as this Adaptive, but cannot be "
        "dynamically cast to Adaptive. Has there been a redesign of the "
        "optimizer classes? if so this needs a rethink");
  }

  if (asAdaptive->mode != mode) {
    throw optimizer_replacement_error("Adaptive modes do not match");
  }

  if (asAdaptive->decayMode != decayMode) {
    throw optimizer_replacement_error("Weight decay modes do not match");
  }

  checkReplacementValue(lossScaling(), other.lossScaling(), "loss scaling");
  checkReplacementValue(lrs, asAdaptive->lrs, "learning rates");
  checkReplacementValue(wds, asAdaptive->wds, "weight decays");
  checkReplacementValue(as, asAdaptive->as, "alphas");
  checkReplacementValue(ms, asAdaptive->ms, "momentums");
  checkReplacementValue(epsvs, asAdaptive->epsvs, "eps");
}

std::unique_ptr<Optimizer> Adaptive::clone() const {
  return std::make_unique<Adaptive>(*this);
}

size_t Adaptive::hash() const {
  std::size_t seed = 0;
  boost::hash_combine(seed, Optimizer::hash());
  boost::hash_combine(seed, lrs);
  boost::hash_combine(seed, wds);
  boost::hash_combine(seed, as);
  boost::hash_combine(seed, ms);
  boost::hash_combine(seed, epsvs);

  boost::hash_combine(seed, static_cast<int>(type()));
  boost::hash_combine(seed, static_cast<int>(mode));
  boost::hash_combine(seed, static_cast<int>(accumType));
  boost::hash_combine(seed, static_cast<int>(accl1Type));
  boost::hash_combine(seed, static_cast<int>(accl2Type));
  boost::hash_combine(seed, static_cast<int>(accl3Type));
  boost::hash_combine(seed, static_cast<int>(decayMode));

  bool hasVelocityTensor =
      !ms.getDefault().isConst() || ms.getDefault().val() != 0.0f;
  boost::hash_combine(seed, hasVelocityTensor);
  return seed;
}

} // namespace popart


text:
////////////////////////////////////////////////////////////////////////////////
// Copyright 2022 FZI Research Center for Information Technology
//
// 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.

//-----------------------------------------------------------------------------
/*!\file    cartesian_controller.hpp
 *
 * \author  Stefan Scherzinger <scherzin@fzi.de>
 * \date    2021/08/18
 *
 */
//-----------------------------------------------------------------------------

#include "arne_skill_pipeline/State.h"
#include "geometry_msgs/PoseStamped.h"
#include "ros/time.h"
#include <arne_robot_control/cartesian_controller.h>
#include <cartesian_compliance_controller/cartesian_compliance_controller.h>
#include <kdl/frames.hpp>

#include <Eigen/Dense>

namespace arne_robot_control
{
  template <class ControlPolicy>
  bool CartesianController<ControlPolicy>::init(hardware_interface::PositionJointInterface* hw, ros::NodeHandle& nh)
  {
    ControlPolicy::init(hw, nh);

    // Unregister CartesianMotionController's target callback.
    // We provide our own callbacks for direct control and macro replay.
    if constexpr (std::is_same<ControlPolicy,
        cartesian_motion_controller::CartesianMotionController<hardware_interface::PositionJointInterface>>::value)
    {
      ControlPolicy::m_target_frame_subscr.shutdown();
    }
    else if constexpr (std::is_same<ControlPolicy,
        cartesian_compliance_controller::CartesianComplianceController<hardware_interface::PositionJointInterface>>::value)
    {
      ControlPolicy::MotionBase::m_target_frame_subscr.shutdown();
    }

    // Connect dynamic reconfigure and overwrite the default values with values
    // on the parameter server. This is done automatically if parameters with
    // the according names exist.
    callback_type_ =
      std::bind(&CartesianController::dynamicReconfigureCallback, this, std::placeholders::_1, std::placeholders::_2);

    m_reconfig_server = std::make_shared<dynamic_reconfigure::Server<ControlConfig> >(ros::NodeHandle(nh, "teaching"));
    m_reconfig_server->setCallback(callback_type_);

    // Motion control
    m_motion_control_subscriber = nh.subscribe("motion_control_input", 3, &CartesianController::motionControlCallback, this);

    // Gripper control
    std::string gripper = nh.param("gripper", std::string(""));
    if (!gripper.empty())
    {
      m_gripper_control_subscriber = nh.subscribe("gripper_control_input", 3, &CartesianController::gripperControlCallback, this);
      m_gripper_handle = hw->getHandle(gripper);
      m_use_gripper = true;;
    }
    else
    {
      m_use_gripper = false;;
    }

    // Feedback and replay
    m_replay_subscriber = nh.subscribe("replay_input", 3, &CartesianController::replayCallback, this);
    m_current_target_publisher = nh.advertise<geometry_msgs::PoseStamped>("current_target", 3);
    m_state_publisher = nh.advertise<arne_skill_pipeline::State>("state_output", 3);

    return true;
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::starting(const ros::Time& time)
  {
    ControlPolicy::starting(time);
    if (m_use_gripper)
    {
      m_gripper_state = m_gripper_handle.getPosition();
    }
    else
    {
      m_gripper_state = -1.0;
    }
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::update(const ros::Time& time, const ros::Duration& period)
  {
    // State feedback for skill recording
    arne_skill_pipeline::State state;
    state.header.frame_id = ControlPolicy::m_robot_base_link;
    state.header.stamp = ros::Time::now();
    state.gripper.data = m_gripper_state;
    state.pose.position.x = ControlPolicy::m_current_frame.p.x();
    state.pose.position.y = ControlPolicy::m_current_frame.p.y();
    state.pose.position.z = ControlPolicy::m_current_frame.p.z();
    ControlPolicy::m_current_frame.M.GetQuaternion(
        state.pose.orientation.x, state.pose.orientation.y,
        state.pose.orientation.z, state.pose.orientation.w);
    m_state_publisher.publish(state);

    // Integrate gripper control into abstract state in [0, 1].
    if (m_use_gripper)
    {
      m_gripper_state = m_gripper_state + m_gripper_control * period.toSec();
      m_gripper_state = std::max(0.0, std::min(1.0, m_gripper_state.load()));
      m_gripper_handle.setCommand(m_gripper_state);
    }

    // Quaternion velocity from angular velocity:
    // https://math.stackexchange.com/questions/1792826
    Eigen::Quaterniond q;
    ControlPolicy::m_target_frame.M.GetQuaternion(q.x(), q.y(), q.z(), q.w());
    Eigen::Quaterniond w(0, m_motion_control.angular.x, m_motion_control.angular.y, m_motion_control.angular.z);

    if (m_local_coordinates)
    {
      // Frames are given w.r.t. the robot base frame, so we need a
      // transformation before integrating in end-effector relative
      // coordinates.
      auto tmp = ControlPolicy::m_current_frame.Inverse() * ControlPolicy::m_target_frame.p;
      tmp += KDL::Vector(m_motion_control.linear.x, m_motion_control.linear.y, m_motion_control.linear.z) * period.toSec();

      // Transform back
      tmp = ControlPolicy::m_current_frame * tmp;
      ControlPolicy::m_target_frame.p = KDL::Vector(tmp.x(), tmp.y(), tmp.z());

      q.coeffs() += 0.5 * (q * w).coeffs() * period.toSec();
    }
    else
    {
      // Base frame relative time integration
      ControlPolicy::m_target_frame.p += KDL::Vector(m_motion_control.linear.x, m_motion_control.linear.y, m_motion_control.linear.z) * period.toSec();

      q.coeffs() += 0.5 * (w * q).coeffs() * period.toSec();
    }
    q.normalize();
    ControlPolicy::m_target_frame.M = KDL::Rotation::Quaternion(q.x(), q.y(), q.z(), q.w());

    // Make sure we don't stray too far from our current pose in motion control.
    // Compliance control needs those offsets for its computation of restoring
    // spring forces.
    if constexpr (std::is_same<ControlPolicy,
        cartesian_motion_controller::CartesianMotionController<hardware_interface::PositionJointInterface>>::value)
    {
      limitTargetOffset(ControlPolicy::m_target_frame);
    }

    // Give visual feedback on the current target
    geometry_msgs::PoseStamped current_target;
    current_target.header.stamp = ros::Time::now();
    current_target.header.frame_id = ControlPolicy::m_robot_base_link;
    current_target.pose.position.x = ControlPolicy::m_target_frame.p.x();
    current_target.pose.position.y = ControlPolicy::m_target_frame.p.y();
    current_target.pose.position.z = ControlPolicy::m_target_frame.p.z();
    current_target.pose.orientation.x = q.x();
    current_target.pose.orientation.y = q.y();
    current_target.pose.orientation.z = q.z();
    current_target.pose.orientation.w = q.w();
    m_current_target_publisher.publish(current_target);

    // Process m_target_frame as usual
    ControlPolicy::update(time, period);
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::dynamicReconfigureCallback(ControlConfig& config, uint32_t level)
  {
    m_local_coordinates = config.local_coordinates;
    m_max_lin_offset = config.max_lin_offset;
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::motionControlCallback(const geometry_msgs::Twist& input)
  {
    m_motion_control = input;
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::gripperControlCallback(const std_msgs::Float64& input)
  {
    m_gripper_control = input.data;
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::replayCallback(const arne_skill_pipeline::State& state)
  {
    m_gripper_state = state.gripper.data;
    geometry_msgs::PoseStamped p;

    // State feedback during recording is always given in robot base frame.
    // Generalizing skills from this recording does not change this, so it's
    // safe to always set this frame here for control.
    p.header.frame_id = ControlPolicy::m_robot_base_link;
    p.pose = state.pose;
    ControlPolicy::targetFrameCallback(p);
  }

  template <class ControlPolicy>
  void CartesianController<ControlPolicy>::limitTargetOffset(KDL::Frame& target)
  {
    auto offset = target.p - ControlPolicy::m_current_frame.p;
    if (offset.Normalize() > m_max_lin_offset)
    {
      target.p = ControlPolicy::m_current_frame.p + offset * m_max_lin_offset;
    }
  }
}

ext: hpp

max_stars_repo_name: fzi-forschungszentrum-informatik/ArNe

max_issues_repo_name: fzi-forschungszentrum-informatik/ArNe

max_forks_repo_name: fzi-forschungszentrum-informatik/ArNe


text:
#define _WIN32_WINNT 0x0601
#include <string>
#include <thread>
#include <iostream>
#include <queue>
#include <list>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

using namespace boost::asio;

class Completion_Handler {
public :
	virtual int handler() = 0;
};
class Async_Operation {
public :
	Async_Operation(io_service& ios, Completion_Handler& handler) : ios_(ios), handler_(handler) {}
	virtual void execute() = 0;
private :
	Completion_Handler& handler_;
	io_service& ios_;
};

#pragma pack(1)

struct Header {
	unsigned long marker;
	unsigned long data_type;
	unsigned long data_size;
};
struct Data {
	//unsigned long marker;
	//unsigned long data_type;
	std::shared_ptr<unsigned char> buffer;
};
struct Packet {
	Header header;
	Data data;
	void encode() {
		header.marker = 0x01020304;
		//data.marker = 0x04030201;
	}
	bool is_valid() const {
		return header_valid();
	}
private :
	bool header_valid() const {
		unsigned long validation = 0x01020304;
		if (::memcmp(&header.marker, &validation, sizeof(unsigned long)) == 0) {
			return true;
		}
		return false;
	}
	//bool data_valid() const {
	//	unsigned long validation = 0x04030201;
	//	if (::memcmp(&data.marker, &validation, sizeof(unsigned long)) == 0) {
	//		return true;
	//	}
	//	return false;
	//}
};
struct Session {
	std::shared_ptr<ip::tcp::socket> socket;
	Packet packet;
	Packet packet_for_sending;
};
class Client {
public :
	Client(io_service& ios) : ios_(ios) {
	}
	void connect(const std::string& remote_ip_address, const unsigned short& remote_port_num) {
		// requirement for connection operation.
		// io_service : member variable.
		// server endpoint : for local socket to connect
		// local socket : having server endpoint, must be alive for connect operation have completed. saved in Session.
		ip::tcp::endpoint server_ep(ip::address::from_string(remote_ip_address), remote_port_num);
		std::shared_ptr<ip::tcp::socket> socket(std::make_shared<ip::tcp::socket>(ios_));

		session_ = std::make_shared<Session>();
		session_->socket = socket;

		std::cout << "try to connect " << server_ep.address().to_string() << ":" << server_ep.port() << std::endl;

		// request connect operation asynchronously.
		socket->async_connect(server_ep,
			boost::bind(&Client::connect_handler, this, boost::placeholders::_1, session_));
	}
	void close() {
		session_->socket->shutdown(ip::tcp::socket::shutdown_both);
		session_->socket->close();
	}
	// enqueue message in messageQueue
	void send(const std::string& message) {
		// make packet from message.
		Packet packet;
		packet.encode();
		packet.header.data_size = message.length();
		packet.data.buffer = std::shared_ptr<unsigned char>(new unsigned char[message.length()], std::default_delete<unsigned char[]>());
		::memcpy(packet.data.buffer.get(), message.c_str(), message.length());

		// enqueue message in messageQueue.
		messageQueue.push(packet);

		send_internal(session_);
	}
protected :
	void send_internal(std::shared_ptr<Session>& session) {
		if (messageQueue.empty()) { return; }

		session->packet_for_sending = messageQueue.front();

		send_header(session);
	}
	void send_header(std::shared_ptr<Session>& session) {
		session->socket->async_send(buffer(&session->packet_for_sending.header, sizeof(Header)),
			boost::bind(&Client::send_header_handler, this, boost::placeholders::_1, boost::placeholders::_2, session));
	}
	void send_data(std::shared_ptr<Session>& session) {
		session->socket->async_send(buffer(session->packet_for_sending.data.buffer.get(), session->packet_for_sending.header.data_size),
			boost::bind(&Client::send_data_handler, this, boost::placeholders::_1, boost::placeholders::_2, session));
	}
	void receive_internal(std::shared_ptr<Session>& session) {
		receive_header(session);
	}
	void receive_header(std::shared_ptr<Session>& session) {
		// asynchronously receive packet header.
		session->socket->async_receive(buffer((char*)&session->packet.header, sizeof(Header)),
			boost::bind(&Client::receive_header_handler, this, boost::placeholders::_1, boost::placeholders::_2, session));
	}
	void receive_data(std::shared_ptr<Session>& session) {
		// prepare packet data buffer.
		session->packet.data.buffer = std::shared_ptr<unsigned char>(new unsigned char[session->packet.header.data_size], std::default_delete<unsigned char[]>());

		// asynchronously receive packet data.
		session->socket->async_receive(buffer((char*)session->packet.data.buffer.get(), session->packet.header.data_size),
			boost::bind(&Client::receive_data_handler, this, boost::placeholders::_1, boost::placeholders::_2, session));
	}
	// completion handler for connect request.
	// 1. check whether error occur or not.
	// 2. notify result of connect request.
	// 3. start next operation : receive & send
	void connect_handler(const boost::system::error_code& e, std::shared_ptr<Session>& session) {
		// check whether error occur or not.
		if (e.value() != 0) {
			std::cerr << "CONNECT ERROR[" << e.value() << "] " << e.message() << std::endl;
			return;
		}

		// notify result of connect request
		const ip::tcp::endpoint& remote = session->socket->remote_endpoint();
		const ip::tcp::endpoint& local = session->socket->local_endpoint();
		std::cout << "successfully connected." << std::endl;
		std::cout << "local[" << local.address().to_string() << ":" << local.port() << "]----"
			<< "remote[" << remote.address().to_string() << ":" << remote.port() << "]" << std::endl;

		// start next operation : receive & send.
		receive_internal(session);

		send_internal(session);
	}
	void receive_header_handler(const boost::system::error_code& e, std::size_t read_size, std::shared_ptr<Session>& session) {
		if (e.value() != 0) {
			std::cerr << "RECEIVE HEADER ERROR[" << e.value() << "] " << e.message() << std::endl;
			return;
		}

		// notify
		std::cout << read_size << "bytes header received." << std::endl;

		// receive data
		receive_data(session);
	}
	void receive_data_handler(const boost::system::error_code& e, std::size_t read_size, std::shared_ptr<Session>& session) {
		if (e.value() != 0) {
			std::cerr << "RECEIVE DATA ERROR[" << e.value() << "] " << e.message() << std::endl;
			return;
		}

		// notify
		const ip::tcp::endpoint& remote = session->socket->remote_endpoint();
		std::cout << read_size << "bytes data received." << std::endl;
		std::string msg(session->packet.data.buffer.get(), session->packet.data.buffer.get() + session->packet.header.data_size);
		std::cout << remote.address().to_string() << ":" << remote.port() << "> " << msg << std::endl;

		// receive another packet.
		receive_internal(session);
	}
	void send_header_handler(const boost::system::error_code& e, std::size_t send_size, std::shared_ptr<Session>& session) {
		if (e.value() != 0) {
			std::cerr << "SEND HEADER ERROR[" << e.value() << "] " << e.message() << std::endl;
			return;
		}

		// notify
		std::cout << "HEADER SENT" << std::endl;

		// send data
		send_data(session);
	}
	void send_data_handler(const boost::system::error_code& e, std::size_t send_size, std::shared_ptr<Session>& session) {
		if (e.value() != 0) {
			std::cerr << "SEND DATA ERROR[" << e.value() << "] " << e.message() << std::endl;
			return;
		}

		// notify
		std::cout << "DATA SENT" << std::endl;

		// dequeue message.
		messageQueue.pop();

		// send another message.
		send_internal(session);
	}
protected :
	io_service& ios_;
	std::shared_ptr<Session> session_;
	std::queue<Packet> messageQueue;
};
int main(void) {

	io_service ios;

	std::string server_ip_address("127.0.0.1");
	unsigned short server_port(33333);

	Client client(ios);

	client.connect(server_ip_address, server_port);

	std::thread t([&]() {
		std::string message;
		do {
			std::cout << "> ";
			std::getline(std::cin, message);

			client.send(message);

		} while (message != "quit");

		client.close();
	});

	ios.run();

	t.join();

	return 0;
}


text:
// ======================================================================
/*!
 * \brief Data associated with a single model
 */
// ======================================================================

#include "Model.h"
#include "ValidPoints.h"
#include <boost/filesystem/operations.hpp>
#include <macgyver/Exception.h>
#include <macgyver/Hash.h>
#include <newbase/NFmiFastQueryInfo.h>
#include <newbase/NFmiGeoTools.h>
#include <newbase/NFmiQueryData.h>
#include <spine/Convenience.h>

namespace SmartMet
{
namespace Engine
{
namespace Querydata
{
// ----------------------------------------------------------------------
/*!
 * \brief Construct a model
 *
 * Note that we could have ordered itsOriginTime after itsQueryData
 * in the object and then initialized the time in the constructor
 * list. This method is less risky against accidental reorders
 * in the header file.
 */
// ----------------------------------------------------------------------

Model::Model(const boost::filesystem::path& filename,
             const std::string& validpointscachedir,
             Producer producer,
             std::string levelname,
             bool climatology,
             bool full,
             bool relativeuv,
             unsigned int update_interval,
             unsigned int minimum_expiration_time,
             bool mmap)
    : itsPath(filename),
      itsProducer(std::move(producer)),
      itsLevelName(std::move(levelname)),
      itsUpdateInterval(update_interval),
      itsMinimumExpirationTime(minimum_expiration_time),
      itsClimatology(climatology),
      itsFullGrid(full),
      itsRelativeUV(relativeuv),
      itsValidTimeList(new ValidTimeList()),
      itsQueryData(new NFmiQueryData(filename.string(), mmap))
{
  try
  {
    if (!itsQueryData)
      throw Fmi::Exception(
          BCP, "Failed to initialize NFmiQueryData object from '" + filename.string() + "'!");

    itsOriginTime = itsQueryData->OriginTime();
    itsLoadTime = boost::posix_time::second_clock::universal_time();

    // May throw if file is gone
    itsModificationTime =
        boost::posix_time::from_time_t(boost::filesystem::last_write_time(filename));

    // Unique hash value for this model

    itsHashValue = 0;
    Fmi::hash_combine(itsHashValue, Fmi::hash_value(itsPath.string()));
    Fmi::hash_combine(itsHashValue, Fmi::hash_value(itsModificationTime));

    // querydata.conf changes may alter essential model properties
    Fmi::hash_combine(itsHashValue, Fmi::hash_value(itsClimatology));
    Fmi::hash_combine(itsHashValue, Fmi::hash_value(itsFullGrid));
    Fmi::hash_combine(itsHashValue, Fmi::hash_value(itsRelativeUV));

    // We need an info object to intialize some data members

    boost::shared_ptr<NFmiFastQueryInfo> qinfo =
        boost::make_shared<NFmiFastQueryInfo>(itsQueryData.get());

    // Might as well pool it for subsequent use

    itsQueryInfoPool.push_front(qinfo);

    // This may be slow for huge data with missing values,
    // hence we configure separately whether this initialization
    // needs to be done or not. findvalidpoint acts accordingly.

    if (!itsFullGrid)
      itsValidPoints = boost::make_shared<ValidPoints>(*qinfo, validpointscachedir, itsHashValue);

    // Requesting the valid times repeatedly is slow if we have to do
    // a time conversion to ptime every time - hence we optimize

    auto& vt = *itsValidTimeList;
    for (qinfo->ResetTime(); qinfo->NextTime();)
    {
      const NFmiMetTime& t = qinfo->ValidTime();
      vt.push_back(t);
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Construct a model from a filtered one
 */
// ----------------------------------------------------------------------

Model::Model(const Model& theModel, boost::shared_ptr<NFmiQueryData> theData, std::size_t theHash)
    : itsHashValue(theHash)  // decided externally on purpose
      ,
      itsOriginTime(theModel.itsOriginTime),
      itsPath(theModel.itsPath),
      itsModificationTime(theModel.itsModificationTime),
      itsProducer(theModel.itsProducer),
      itsLevelName(theModel.itsLevelName),
      itsUpdateInterval(theModel.itsUpdateInterval),
      itsMinimumExpirationTime(theModel.itsMinimumExpirationTime),
      itsClimatology(theModel.itsClimatology),
      itsFullGrid(theModel.itsFullGrid),
      itsRelativeUV(theModel.itsRelativeUV),
      itsValidPoints(theModel.itsValidPoints),
      itsValidTimeList(theModel.itsValidTimeList),
      itsQueryData(std::move(theData))
{
}

// ----------------------------------------------------------------------
/*!
 * \brief Construct a model without querydata file
 *
 * Note: The hash is given from the outside on purpose
 */
// ----------------------------------------------------------------------

Model::Model(boost::shared_ptr<NFmiQueryData> theData, std::size_t theHash)
    : itsHashValue(theHash),
      itsUpdateInterval(0),
      itsMinimumExpirationTime(999999),
      itsFullGrid(true),
      itsValidTimeList(new ValidTimeList()),
      itsQueryData(std::move(theData))
{
  try
  {
    // We need an info object to intialize some data members

    boost::shared_ptr<NFmiFastQueryInfo> qinfo =
        boost::make_shared<NFmiFastQueryInfo>(itsQueryData.get());

    // Might as well pool it for subsequent use

    itsQueryInfoPool.push_front(qinfo);

    // Requesting the valid times repeatedly is slow if we have to do
    // a time conversion to ptime every time - hence we optimize

    auto& vt = *itsValidTimeList;
    for (qinfo->ResetTime(); qinfo->NextTime();)
    {
      const NFmiMetTime& t = qinfo->ValidTime();
      vt.push_back(t);
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Origin time accessor
 */
// ----------------------------------------------------------------------

const boost::posix_time::ptime& Model::originTime() const
{
  return itsOriginTime;
}

// ----------------------------------------------------------------------
/*!
 * \brief Load time accessor
 */
// ----------------------------------------------------------------------

const boost::posix_time::ptime& Model::loadTime() const
{
  return itsLoadTime;
}

// ----------------------------------------------------------------------
/*!
 * \brief Modification time accessor
 */
// ----------------------------------------------------------------------

const boost::posix_time::ptime& Model::modificationTime() const
{
  return itsModificationTime;
}

// ----------------------------------------------------------------------
/*!
 * \brief Estimated expiration time for products generated from this data
 */
// ----------------------------------------------------------------------

boost::posix_time::ptime Model::expirationTime() const
{
  // Expected time for the next model
  auto t1 = itsModificationTime + boost::posix_time::seconds(itsUpdateInterval);

  // Minimum expiration time from wall clock
  auto t2 = boost::posix_time::second_clock::universal_time() +
            boost::posix_time::seconds(itsMinimumExpirationTime);

  // Choose the later one. t1 dominates until the next model is overdue, in
  // which case we start waiting for it in smaller minimum expiration time
  // intervals. If the next model is early, too bad. Someone is bound to
  // make a fresh load of the data though, in which case the backend
  // will generate a new product and the frontend cache will be updated.

  return std::max(t1, t2);
}

// ----------------------------------------------------------------------
/*!
 * \brief Path accessor
 */
// ----------------------------------------------------------------------

const boost::filesystem::path& Model::path() const
{
  return itsPath;
}
// ----------------------------------------------------------------------
/*!
 * \brief Producer accessor
 */
// ----------------------------------------------------------------------

const Producer& Model::producer() const
{
  return itsProducer;
}

// ----------------------------------------------------------------------
/*!
 * \brief Level type accessor
 */
// ----------------------------------------------------------------------

const std::string& Model::levelName() const
{
  return itsLevelName;
}

// ----------------------------------------------------------------------
/*!
 * \brief Climatology accessor
 */
// ----------------------------------------------------------------------

bool Model::isClimatology() const
{
  return itsClimatology;
}

// ----------------------------------------------------------------------
/*!
 * \brief Return true if grid is full
 */
// ----------------------------------------------------------------------

bool Model::isFullGrid() const
{
  return itsFullGrid;
}

// ----------------------------------------------------------------------
/*!
 * \brief Return true if wind U/V components are relative to the grid
 */
// ----------------------------------------------------------------------

bool Model::isRelativeUV() const
{
  return itsRelativeUV;
}

// ----------------------------------------------------------------------
/*!
 * \brief Find closest valid coordinate point within given radius (km)
 *
 * Returns (kFloatMissing,kFloatMissing) on failure
 *
 * TODO: REWRITE TO USE A NEARTREE OF VALID POINTS!!
 */
// ----------------------------------------------------------------------

NFmiPoint Model::validPoint(const NFmiPoint& latlon, double maxdist) const
{
  try
  {
    // First establish the nearest point
    NFmiFastQueryInfo qi(itsQueryData.get());

    if (!qi.NearestPoint(latlon) || !qi.IsGrid())
      return {kFloatMissing, kFloatMissing};

    // If the model covers all grid points, we're done

    if (itsFullGrid)
    {
      NFmiPoint p = qi.LatLon();
      double distance = NFmiGeoTools::GeoDistance(latlon.X(), latlon.Y(), p.X(), p.Y());
      if (distance <= 1000 * maxdist)
        return p;
      return {kFloatMissing, kFloatMissing};
    }

    // The model does not cover the entire grid, but for example
    // only land or sea areas. We must search the nearest valid
    // model point.

    // Start an expanding search loop

    NFmiPoint bestpoint;
    bool ok = false;
    double bestdistance = maxdist * 1000;

    for (unsigned int y = 1;; y++)
    {
      int j = (2 * (y % 2) - 1) * (y >> 1);  // 0,-1,1,-2,2,-3,3... NOLINT(hicpp-signed-bitwise)

      NFmiPoint p = qi.PeekLocationLatLon(0, j);
      double distance = NFmiGeoTools::GeoDistance(latlon.X(), latlon.Y(), p.X(), p.Y());

      if (distance > bestdistance)
        break;

      for (unsigned int x = 1;; x++)
      {
        int i = (2 * (x % 2) - 1) * (x >> 1);  // 0,-1,1,-2,2,-3,3... NOLINT(hicpp-signed-bitwise)

        p = qi.PeekLocationLatLon(i, j);

        distance = NFmiGeoTools::GeoDistance(latlon.X(), latlon.Y(), p.X(), p.Y());

        if (distance > bestdistance)
          break;

        if (itsValidPoints->isvalid(qi.PeekLocationIndex(i, j)))
        {
          ok = true;
          bestpoint = p;
          bestdistance = distance;
        }
      }
    }

    // Check if we found any points within the search radius

    if (ok)
      return bestpoint;
    return {kFloatMissing, kFloatMissing};
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Return valid times available in the model
 */
// ----------------------------------------------------------------------

boost::shared_ptr<ValidTimeList> Model::validTimes() const
{
  return itsValidTimeList;
}

// ----------------------------------------------------------------------
/*!
 * \brief Return a Q for the data
 */
// ----------------------------------------------------------------------

SharedInfo Model::info() const
{
  try
  {
    Spine::WriteLock lock(itsQueryInfoPoolMutex);
    if (itsQueryInfoPool.empty())
    {
      auto qinfo = boost::make_shared<NFmiFastQueryInfo>(itsQueryData.get());
      qinfo->First();  // reset for first use
      return qinfo;
    }

    auto qinfo = itsQueryInfoPool.front();
    itsQueryInfoPool.pop_front();
    qinfo->First();  // reset after prior use
    return qinfo;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

void Model::release(boost::shared_ptr<NFmiFastQueryInfo> theInfo) const
{
  try
  {
    Spine::WriteLock lock(itsQueryInfoPoolMutex);
    itsQueryInfoPool.emplace_front(theInfo);
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 *\ brief Return the hash value for the grid in the querydata
 */
// ----------------------------------------------------------------------

std::size_t Model::gridHashValue() const
{
  return itsQueryData->GridHashValue();
}

// ----------------------------------------------------------------------
/*!
 * \brief Uncache related data
 */
// ----------------------------------------------------------------------

void Model::uncache() const
{
  if (itsValidPoints)
    itsValidPoints->uncache();
}

// ----------------------------------------------------------------------
/*!
 * \brief Return an unique hash for the object
 */
// ----------------------------------------------------------------------

std::size_t hash_value(const Model& theModel)
{
  return theModel.itsHashValue;
}

// ----------------------------------------------------------------------
/*!
 * \brief Set the LatLonCache for the querydata from external cache
 */
// ----------------------------------------------------------------------

#ifndef WGS84
void Model::setLatLonCache(boost::shared_ptr<std::vector<NFmiPoint>> theCache)
{
  itsQueryData->SetLatLonCache(theCache);
}
#endif

// ----------------------------------------------------------------------
/*!
 * \brief Make querydata latlon cache and return it
 */
// ----------------------------------------------------------------------

#ifndef WGS84
boost::shared_ptr<std::vector<NFmiPoint>> Model::makeLatLonCache()
{
  return itsQueryData->LatLonCache();
}
#endif

}  // namespace Querydata
}  // namespace Engine
}  // namespace SmartMet

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

max_stars_repo_name: fmidev/smartmet-engine-querydata

max_issues_repo_name: fmidev/smartmet-engine-querydata

max_forks_repo_name: fmidev/smartmet-engine-querydata


text:
/**
 *	\file
 */

#pragma once

#include <ares.h>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/system/error_code.hpp>
#include <mpark/variant.hpp>
#include <utility>
#include <vector>

namespace asio_cares {

/**
 *	Encapsulates a libcares channel and the state
 *	required for it to interoperate with Boost.Asio.
 */
class channel {
public:
	channel () = delete;
	channel (const channel &) = delete;
	channel (channel &&) = delete;
	channel & operator = (const channel &) = delete;
	channel & operator = (channel &&) = delete;
	/**
	 *	Creates a new channel object by calling
	 *	`ares_init`.
	 *
	 *	\param [in] ios
	 *		The `io_service` which shall be used for
	 *		asynchronous operations. This reference must
	 *		remain valid for the lifetime of the object.
	 */
	explicit channel (boost::asio::io_service & ios);
	/**
	 *	Creates a new channel object by calling
	 *	`ares_init_options`.
	 *
	 *	\param [in] options
	 *		An `ares_options` object giving the options
	 *		to pass as the second argument to `ares_init_options`.
	 *	\param [in] optmask
	 *		An integer giving the mask to pass as the
	 *		third argument to `ares_init_options`.
	 *	\param [in] ios
	 *		The `io_service` which shall be used for
	 *		asynchronous operations. This reference must
	 *		remain valid for the lifetime of the object.
	 */
	channel (const ares_options & options, int optmask, boost::asio::io_service & ios);
	/**
	 *	Cleans up a channel object.
	 */
	~channel () noexcept;
	/**
	 *	Retrieves the managed `io_service`.
	 *
	 *	\return
	 *		A reference to an `io_service` object.
	 */
	boost::asio::io_service & get_io_service () noexcept;
	/**
	 *	All operations on a channel are conceptually
	 *	operations on the underlying `ares_channel`
	 *	which are not thread safe, accordingly a
	 *	`boost::asio::strand` is used to ensure the
	 *	`ares_channel` is only accessed by one thread
	 *	of execution at a time. This method retrieves
	 *	that `boost::asio::strand` object.
	 *
	 *	\return
	 *		A reference to a `strand`.
	 */
	boost::asio::strand & get_strand () noexcept;
	/**
	 *	Operations on a channel have timeouts. This
	 *	method retrieves the `boost::asio::deadline_timer`
	 *	used to track those timeouts.
	 *
	 *	\return
	 *		A reference to a `deadline_timer`.
	 */
	boost::asio::deadline_timer & get_timer () noexcept;
private:
	using socket_type = mpark::variant<boost::asio::ip::tcp::socket, boost::asio::ip::udp::socket>;
	template <typename Function>
	static constexpr auto is_nothrow_invocable = noexcept(std::declval<Function>()(std::declval<boost::asio::ip::tcp::socket &>())) &&
	                                             noexcept(std::declval<Function>()(std::declval<boost::asio::ip::udp::socket &>()));
public:
	/**
	 *	The type used to represent a socket when
	 *	it is acquired.
	 *
	 *	In addition to making a certain socket
	 *	accessible alse releases the socket back
	 *	to the associated \ref channel once its
	 *	lifetime ends.
	 */
	class socket_guard {
	public:
		socket_guard () = delete;
		socket_guard (const socket_guard &) = delete;
		socket_guard & operator = (const socket_guard &) = delete;
		socket_guard & operator = (socket_guard &&) = delete;
		socket_guard (socket_type &, channel &) noexcept;
		socket_guard (socket_guard &&) noexcept;
		~socket_guard () noexcept;
		/**
		 *	Provides access to the underlying Boost.Asio
		 *	socket object by invoking a provided function
		 *	object with the socket as its sole argument.
		 *
		 *	Since libcares sockets may be TCP or UDP
		 *	the provided function object must accept
		 *	either `boost::asio::ip::tcp::socket` or
		 *	`boost::asio::ip::udp::socket`.
		 *
		 *	\tparam Function
		 *		The type of function object.
		 *
		 *	\param [in] function
		 *		The function object.
		 */
		template <typename Function>
		void unwrap (Function && function) noexcept(is_nothrow_invocable<Function>) {
			assert(socket_);
			mpark::visit(function, *socket_);
		}
	private:
		socket_type * socket_;
		channel *     channel_;
	};
	/**
	 *	Acquires a socket.
	 *
	 *	Acquiring a socket which has already been acquired
	 *	and not yet released results in undefined behavior.
	 *
	 *	Once a socket has been acquired it cannot be closed
	 *	by libcares until it is released. If libcares attempts
	 *	to close it while it is acquired the closure shall be
	 *	deferred until it is released.
	 *
	 *	\param [in] socket
	 *		The libcares handle for the socket to acquire.
	 *
	 *	\return
	 *		A \ref socket_guard which shall release the
	 *		socket when it goes out of scope and through
	 *		which the socket may be accessed.
	 */
	socket_guard acquire_socket (ares_socket_t socket) noexcept;
	/**
	 *	Retrieves the managed `ares_channel` object.
	 *
	 *	\return
	 *		An `ares_channel` object.
	 */
	operator ares_channel () noexcept;
	/**
	 *	Invokes a certain function object for each
	 *	socket currently in use by the channel.
	 *
	 *	Due to the fact libcares uses both TCP and
	 *	UDP sockets the provided function object must
	 *	be invocable with both `boost::asio::ip::tcp::socket`
	 *	and `boost::asio::ip::udp::socket` objects.
	 *
	 *	\tparam Function
	 *		The type of function object to invoke.
	 *
	 *	\param [in] function
	 *		The function object to invoke.
	 */
	template <typename Function>
	void for_each_socket (Function && function) noexcept(is_nothrow_invocable<Function>) {
		for (auto && state : sockets_) mpark::visit(function, state.socket);
	}
private:
	class socket_state {
	public:
		socket_state () = delete;
		socket_state (const socket_state &) = delete;
		socket_state (socket_state &&) = default;
		socket_state & operator = (const socket_state &) = delete;
		socket_state & operator = (socket_state &&) = default;
		explicit socket_state (socket_type);
		socket_type socket;
		bool        acquired;
		bool        closed;
	};
	void release_socket (int) noexcept;
	using sockets_collection_type = std::vector<socket_state>;
	template <typename T>
	sockets_collection_type::iterator insertion_point (const T &) noexcept;
	template <typename T>
	sockets_collection_type::iterator find (const T &) noexcept;
	boost::asio::ip::tcp::socket tcp_socket (bool, boost::system::error_code &) noexcept;
	boost::asio::ip::udp::socket udp_socket (bool, boost::system::error_code &) noexcept;
	socket_type socket (bool, bool, boost::system::error_code &) noexcept;
	static ares_socket_t socket (int, int, int, void *) noexcept;
	static int close (ares_socket_t, void *) noexcept;
	void init () noexcept;
	ares_socket_functions       funcs_;
	ares_channel                channel_;
	boost::asio::strand         strand_;
	sockets_collection_type     sockets_;
	boost::asio::deadline_timer timer_;
};

}

max_stars_repo_name: RobertLeahy/ASIO-C-ARES

max_issues_repo_name: RobertLeahy/ASIO-C-ARES

max_forks_repo_name: RobertLeahy/ASIO-C-ARES