<commit_before>//=====================================================================//
/*!	@file
	@brief	R8C PLUSE OUT/LCD メイン @n
			for ST7567 SPI (128 x 32) @n
			LCD: Aitendo M-G0812P7567 @n
			ENCODER: A: P10, B: P11 Com: Vss
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <cstring>
#include "main.hpp"
#include "system.hpp"
#include "clock.hpp"
#include "common/command.hpp"
#include "common/format.hpp"
#include "common/monograph.hpp"
#include "port_def.hpp"

#include "bitmap/font32.h"

static const uint8_t* nmbs_[] = {
	nmb_0, nmb_1, nmb_2, nmb_3, nmb_4,
	nmb_5, nmb_6, nmb_7, nmb_8, nmb_9,
	txt_hz, txt_k
};

static uint8_t enc_lvl_ = 0;
static volatile int8_t enc_cnt_ = 0;

class encoder {
public:
	void operator() () {
		uint8_t lvl = device::P1();  ///< 状態の取得
		uint8_t pos = ~enc_lvl_ &  lvl;  ///< 立ち上がりエッジ検出
		uint8_t neg =  enc_lvl_ & ~lvl;  ///< 立ち下がりエッジ検出
		enc_lvl_ = lvl;  ///< 状態のセーブ

		if(pos & device::P1.B0.b()) {
			if(lvl & device::P1.B1.b()) {
				--enc_cnt_;
			} else {
				++enc_cnt_;
			}
		}
		if(neg & device::P1.B0.b()) {
			if(lvl & device::P1.B1.b()) {
				++enc_cnt_;
			} else {
				--enc_cnt_;
			}
		}
		if(pos & device::P1.B1.b()) {
			if(lvl & device::P1.B0.b()) {
				++enc_cnt_;
			} else {
				--enc_cnt_;
			}
		}
		if(neg & device::P1.B1.b()) {
			if(lvl & device::P1.B0.b()) {
				--enc_cnt_;
			} else {
				++enc_cnt_;
			}
		}
	}
};

static device::trb_io<encoder> timer_b_;
static uart0 uart0_;
static utils::command<64> command_;
static spi_base spi_base_;
static spi_ctrl spi_ctrl_;
static lcd lcd_;
static graphics::monograph bitmap_;
static timer_j timer_j_;

extern "C" {
	void sci_putch(char ch) {
		uart0_.putch(ch);
	}

	char sci_getch(void) {
		return uart0_.getch();
	}

	uint16_t sci_length() {
		return uart0_.length();
	}

	void sci_puts(const char* str) {
		uart0_.puts(str);
	}
}

extern "C" {
	const void* variable_vectors_[] __attribute__ ((section (".vvec"))) = {
		reinterpret_cast<void*>(brk_inst_),		nullptr,	// (0)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (1) flash_ready
		reinterpret_cast<void*>(null_task_),	nullptr,	// (2)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (3)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (4) コンパレーターB1
		reinterpret_cast<void*>(null_task_),	nullptr,	// (5) コンパレーターB3
		reinterpret_cast<void*>(null_task_),	nullptr,	// (6)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (7) タイマＲＣ

		reinterpret_cast<void*>(null_task_),	nullptr,	// (8)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (9)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (10)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (11)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (12)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (13) キー入力
		reinterpret_cast<void*>(null_task_),	nullptr,	// (14) A/D 変換
		reinterpret_cast<void*>(null_task_),	nullptr,	// (15)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (16)
		reinterpret_cast<void*>(uart0_.isend),	nullptr,	// (17) UART0 送信
		reinterpret_cast<void*>(uart0_.irecv),	nullptr,	// (18) UART0 受信
		reinterpret_cast<void*>(null_task_),	nullptr,	// (19)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (20)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (21) /INT2
		reinterpret_cast<void*>(timer_j_.itask_out),nullptr,	// (22) タイマＲＪ２
		reinterpret_cast<void*>(null_task_),	nullptr,	// (23) 周期タイマ

		reinterpret_cast<void*>(timer_b_.itask),nullptr,	// (24) タイマＲＢ２
		reinterpret_cast<void*>(null_task_),	nullptr,	// (25) /INT1
		reinterpret_cast<void*>(null_task_),	nullptr,	// (26) /INT3
		reinterpret_cast<void*>(null_task_),	nullptr,	// (27)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (28)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (29) /INT0
		reinterpret_cast<void*>(null_task_),	nullptr,	// (30)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (31)
	};
}

__attribute__ ((section (".exttext")))
int main(int argc, char *argv[])
{
	using namespace device;

// クロック関係レジスタ・プロテクト解除
	PRCR.PRC0 = 1;

// 高速オンチップオシレーターへ切り替え(20MHz)
// ※ F_CLK を設定する事（Makefile内）
	OCOCR.HOCOE = 1;
	utils::delay::micro_second(1);	// >=30uS(125KHz)
	SCKCR.HSCKSEL = 1;
	CKSTPR.SCKSEL = 1;

	PRCR.PRC0 = 0;

	// タイマーＢ初期化
	{
		uint8_t ir_level = 2;
		timer_b_.start_timer(240, ir_level);
	}

	// UART の設定 (P1_4: TXD0[out], P1_5: RXD0[in])
	// ※シリアルライターでは、RXD 端子は、P1_6 となっているので注意！
	{
		utils::PORT_MAP(utils::port_map::P14::TXD0);
		utils::PORT_MAP(utils::port_map::P15::RXD0);
		uint8_t ir_level = 1;
		uart0_.start(19200, ir_level);
	}

	// エンコーダー入力の設定 P10: (Phi_A), P11: (Phi_B), Vss: (COM)
	{
		utils::PORT_MAP(utils::port_map::P10::PORT);
		utils::PORT_MAP(utils::port_map::P11::PORT);
		device::PD1.B0 = 0;
		device::PD1.B1 = 0;
		device::PUR1.B0 = 1;	///< プルアップ
		device::PUR1.B1 = 1;	///< プルアップ
	}

	// spi_base, spi_ctrl ポートの初期化
	{
		spi_ctrl_.init();
		spi_base_.init();
	}

	// LCD を開始
	{
		lcd_.start();
		bitmap_.init();
		bitmap_.clear(0);
//		bitmap_.frame(0, 0, 128, 32, 1);
	}

	uint32_t count = 20;

	// TRJ のパルス出力設定
	{
		utils::PORT_MAP(utils::port_map::P17::TRJIO);
		uint8_t ir_level = 1;
		if(!timer_j_.pluse_out(count, ir_level)) {
			sci_puts("TRJ out of range.\n");
		}
	}

	sci_puts("Start R8C PLUSE OUT/LCD\n");
	command_.set_prompt("# ");

	uint8_t cnt = 0;
	uint32_t value = 0;
	while(1) {
		timer_b_.sync();

		// エンコーダー値の増減
		int32_t d = 0;
		if(enc_cnt_ >= 4) {
			enc_cnt_ = 0;
			d = 1;
		} else if(enc_cnt_ <= -4) {
			enc_cnt_ = 0;
			d = -1;
		}
		if(d) {
			if(count < 100) {
				d *= 1;
			} else if(count < 1000) { // 1KHz
				d *= 10; // 10Hz step
			} else if(count < 10000) { // 10KHz
				d *= 100; // 100Hz step
			} else if(count < 100000) { // 100KHz
				d *= 1000; // 1KHz step
			} else if(count < 1000000) { // 1MHz
				d *= 10000; // 10KHz step
			} else {
				d *= 100000; // 100KHz step
			}
			count += static_cast<uint32_t>(d);
			if(count < 20) count = 20;
			else if(count > 10000000) count = 10000000;
		}

		if(value != count) {
			value = count;

			timer_j_.set_cycle(count);

			if(count > 99999) {
				utils::format("%dKHz\n") % (count / 1000);
			} else {
				utils::format("%dHz\n") % count;
			}
		}

		// 1/15 sec
		if((cnt & 15) == 0) {
			bitmap_.clear(0);
			uint32_t n = count;
			bool khz = false;
			if(n > 99999) {
				n /= 1000;
				khz = true;
			}
			bitmap_.draw_mobj(20 * 4, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 3, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 2, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 1, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 0, 0, nmbs_[n % 10]);
			if(khz) {
				bitmap_.draw_mobj(20 * 5, 0, nmbs_[11]);
				bitmap_.draw_mobj(20 * 5 + 11, 0, nmbs_[10]);
			} else {
				bitmap_.draw_mobj(20 * 5, 0, nmbs_[10]);
			}
			lcd_.copy(bitmap_.fb());
		}

		++cnt;
	}
}
<commit_msg>Update pluse input<commit_after>//=====================================================================//
/*!	@file
	@brief	R8C PLUSE OUT/LCD メイン @n
			for ST7567 SPI (128 x 32) @n
			LCD: Aitendo M-G0812P7567 @n
			ENCODER: A: P10, B: P11 Com: Vss
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <cstring>
#include "main.hpp"
#include "system.hpp"
#include "clock.hpp"
#include "common/command.hpp"
#include "common/format.hpp"
#include "common/monograph.hpp"
#include "port_def.hpp"

#include "bitmap/font32.h"

static const uint8_t* nmbs_[] = {
	nmb_0, nmb_1, nmb_2, nmb_3, nmb_4,
	nmb_5, nmb_6, nmb_7, nmb_8, nmb_9,
	txt_hz, txt_k
};

static uint8_t enc_lvl_ = 0;
static volatile int8_t enc_cnt_ = 0;

class encoder {
public:
	void operator() () {
		uint8_t lvl = device::P1();  ///< 状態の取得
		uint8_t pos = ~enc_lvl_ &  lvl;  ///< 立ち上がりエッジ検出
		uint8_t neg =  enc_lvl_ & ~lvl;  ///< 立ち下がりエッジ検出
		enc_lvl_ = lvl;  ///< 状態のセーブ

		if(pos & device::P1.B0.b()) {
			if(lvl & device::P1.B1.b()) {
				--enc_cnt_;
			} else {
				++enc_cnt_;
			}
		}
		if(neg & device::P1.B0.b()) {
			if(lvl & device::P1.B1.b()) {
				++enc_cnt_;
			} else {
				--enc_cnt_;
			}
		}
		if(pos & device::P1.B1.b()) {
			if(lvl & device::P1.B0.b()) {
				++enc_cnt_;
			} else {
				--enc_cnt_;
			}
		}
		if(neg & device::P1.B1.b()) {
			if(lvl & device::P1.B0.b()) {
				--enc_cnt_;
			} else {
				++enc_cnt_;
			}
		}
	}
};

static device::trb_io<encoder> timer_b_;
static uart0 uart0_;
static utils::command<64> command_;
static spi_base spi_base_;
static spi_ctrl spi_ctrl_;
static lcd lcd_;
static graphics::monograph bitmap_;
static timer_j timer_j_;

extern "C" {
	void sci_putch(char ch) {
		uart0_.putch(ch);
	}

	char sci_getch(void) {
		return uart0_.getch();
	}

	uint16_t sci_length() {
		return uart0_.length();
	}

	void sci_puts(const char* str) {
		uart0_.puts(str);
	}
}

extern "C" {
	const void* variable_vectors_[] __attribute__ ((section (".vvec"))) = {
		reinterpret_cast<void*>(brk_inst_),		nullptr,	// (0)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (1) flash_ready
		reinterpret_cast<void*>(null_task_),	nullptr,	// (2)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (3)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (4) コンパレーターB1
		reinterpret_cast<void*>(null_task_),	nullptr,	// (5) コンパレーターB3
		reinterpret_cast<void*>(null_task_),	nullptr,	// (6)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (7) タイマＲＣ

		reinterpret_cast<void*>(null_task_),	nullptr,	// (8)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (9)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (10)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (11)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (12)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (13) キー入力
		reinterpret_cast<void*>(null_task_),	nullptr,	// (14) A/D 変換
		reinterpret_cast<void*>(null_task_),	nullptr,	// (15)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (16)
		reinterpret_cast<void*>(uart0_.isend),	nullptr,	// (17) UART0 送信
		reinterpret_cast<void*>(uart0_.irecv),	nullptr,	// (18) UART0 受信
		reinterpret_cast<void*>(null_task_),	nullptr,	// (19)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (20)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (21) /INT2
		reinterpret_cast<void*>(timer_j_.iout),	nullptr,	// (22) タイマＲＪ２
		reinterpret_cast<void*>(null_task_),	nullptr,	// (23) 周期タイマ

		reinterpret_cast<void*>(timer_b_.itask),nullptr,	// (24) タイマＲＢ２
		reinterpret_cast<void*>(null_task_),	nullptr,	// (25) /INT1
		reinterpret_cast<void*>(null_task_),	nullptr,	// (26) /INT3
		reinterpret_cast<void*>(null_task_),	nullptr,	// (27)

		reinterpret_cast<void*>(null_task_),	nullptr,	// (28)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (29) /INT0
		reinterpret_cast<void*>(null_task_),	nullptr,	// (30)
		reinterpret_cast<void*>(null_task_),	nullptr,	// (31)
	};
}

__attribute__ ((section (".exttext")))
int main(int argc, char *argv[])
{
	using namespace device;

// クロック関係レジスタ・プロテクト解除
	PRCR.PRC0 = 1;

// 高速オンチップオシレーターへ切り替え(20MHz)
// ※ F_CLK を設定する事（Makefile内）
	OCOCR.HOCOE = 1;
	utils::delay::micro_second(1);	// >=30uS(125KHz)
	SCKCR.HSCKSEL = 1;
	CKSTPR.SCKSEL = 1;

	PRCR.PRC0 = 0;

	// タイマーＢ初期化
	{
		uint8_t ir_level = 2;
		timer_b_.start_timer(240, ir_level);
	}

	// UART の設定 (P1_4: TXD0[out], P1_5: RXD0[in])
	// ※シリアルライターでは、RXD 端子は、P1_6 となっているので注意！
	{
		utils::PORT_MAP(utils::port_map::P14::TXD0);
		utils::PORT_MAP(utils::port_map::P15::RXD0);
		uint8_t ir_level = 1;
		uart0_.start(19200, ir_level);
	}

	// エンコーダー入力の設定 P10: (Phi_A), P11: (Phi_B), Vss: (COM)
	{
		utils::PORT_MAP(utils::port_map::P10::PORT);
		utils::PORT_MAP(utils::port_map::P11::PORT);
		device::PD1.B0 = 0;
		device::PD1.B1 = 0;
		device::PUR1.B0 = 1;	///< プルアップ
		device::PUR1.B1 = 1;	///< プルアップ
	}

	// spi_base, spi_ctrl ポートの初期化
	{
		spi_ctrl_.init();
		spi_base_.init();
	}

	// LCD を開始
	{
		lcd_.start();
		bitmap_.init();
		bitmap_.clear(0);
//		bitmap_.frame(0, 0, 128, 32, 1);
	}

	uint32_t count = 20;

	// TRJ のパルス出力設定
	{
		utils::PORT_MAP(utils::port_map::P17::TRJIO);
		uint8_t ir_level = 1;
		if(!timer_j_.pluse_out(count, ir_level)) {
			sci_puts("TRJ out of range.\n");
		}
	}

	sci_puts("Start R8C PLUSE OUT/LCD\n");
	command_.set_prompt("# ");

	uint8_t cnt = 0;
	uint32_t value = 0;
	while(1) {
		timer_b_.sync();

		// エンコーダー値の増減
		int32_t d = 0;
		if(enc_cnt_ >= 4) {
			enc_cnt_ = 0;
			d = 1;
		} else if(enc_cnt_ <= -4) {
			enc_cnt_ = 0;
			d = -1;
		}
		if(d) {
			if(count < 100) {
				d *= 1;
			} else if(count < 1000) { // 1KHz
				d *= 10; // 10Hz step
			} else if(count < 10000) { // 10KHz
				d *= 100; // 100Hz step
			} else if(count < 100000) { // 100KHz
				d *= 1000; // 1KHz step
			} else if(count < 1000000) { // 1MHz
				d *= 10000; // 10KHz step
			} else {
				d *= 100000; // 100KHz step
			}
			count += static_cast<uint32_t>(d);
			if(count < 20) count = 20;
			else if(count > 10000000) count = 10000000;
		}

		if(value != count) {
			value = count;

			timer_j_.set_cycle(count);

			if(count > 99999) {
				utils::format("%dKHz\n") % (count / 1000);
			} else {
				utils::format("%dHz\n") % count;
			}
		}

		// 1/15 sec
		if((cnt & 15) == 0) {
			bitmap_.clear(0);
			uint32_t n = count;
			bool khz = false;
			if(n > 99999) {
				n /= 1000;
				khz = true;
			}
			bitmap_.draw_mobj(20 * 4, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 3, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 2, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 1, 0, nmbs_[n % 10]);
			n /= 10;
			bitmap_.draw_mobj(20 * 0, 0, nmbs_[n % 10]);
			if(khz) {
				bitmap_.draw_mobj(20 * 5, 0, nmbs_[11]);
				bitmap_.draw_mobj(20 * 5 + 11, 0, nmbs_[10]);
			} else {
				bitmap_.draw_mobj(20 * 5, 0, nmbs_[10]);
			}
			lcd_.copy(bitmap_.fb());
		}

		++cnt;
	}
}
<|endoftext|>


<commit_before>//	Copyright 2017 Adam Smith
//	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 <type_traits>
#include <cstdint>
#include <string>

#ifndef ASMITH_REFLECTION_CLASS_HPP
#define ASMITH_REFLECTION_CLASS_HPP

namespace asmith {

	class reflection_variable;
	class reflection_function;
	class reflection_constructor;
	class reflection_destructor;

	class reflection_class {
	public:
		virtual ~reflection_class() {}

		virtual const char* get_name() const = 0;
		virtual size_t get_size() const = 0;
		virtual size_t get_variable_count() const = 0;
		virtual const reflection_variable& get_variable(size_t) const = 0;
		virtual size_t get_function_count() const = 0;
		virtual const reflection_function& get_function(size_t) const = 0;
		virtual size_t get_constructor_count() const = 0;
		virtual const reflection_constructor& get_constructor(size_t) const = 0;
		virtual const reflection_destructor& get_destructor() const = 0;
		virtual size_t get_parent_count() const = 0;
		virtual const reflection_class& get_parent_class(size_t) const = 0;
	};

	template<class C, class T>
	using reflection_variable_ptr = T C::*;
}
#endif<commit_msg>Partial layout for auto_reflection_class<commit_after>//	Copyright 2017 Adam Smith
//	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 <type_traits>
#include <cstdint>
#include <string>
#include <memory>
#include <vector>

#ifndef ASMITH_REFLECTION_CLASS_HPP
#define ASMITH_REFLECTION_CLASS_HPP

namespace asmith {

	class reflection_variable;
	class reflection_function;
	class reflection_constructor;
	class reflection_destructor;

	class reflection_class {
	public:
		virtual ~reflection_class() {}

		virtual const char* get_name() const = 0;
		virtual size_t get_size() const = 0;
		virtual size_t get_variable_count() const = 0;
		virtual const reflection_variable& get_variable(size_t) const = 0;
		virtual size_t get_function_count() const = 0;
		virtual const reflection_function& get_function(size_t) const = 0;
		virtual size_t get_constructor_count() const = 0;
		virtual const reflection_constructor& get_constructor(size_t) const = 0;
		virtual const reflection_destructor& get_destructor() const = 0;
		virtual size_t get_parent_count() const = 0;
		virtual const reflection_class& get_parent_class(size_t) const = 0;
	};

	class auto_reflection_class : public reflection_class {
	private:
		std::vector<std::shared_ptr<reflection_class>> mParents;
		std::vector<std::shared_ptr<reflection_constructor>> mConstructors;
		std::vector<std::shared_ptr<reflection_variable>> mVariables;
		std::vector<std::shared_ptr<reflection_function>> mFunctions;
		std::shared_ptr<reflection_destructor> mDestructor;
		const std::string mName;
		const size_t mSize;
	public:
		auto_reflection_class(const std::string& aName, const size_t aSize) :
			mName(aName),
			mSize(aSize)
		{}

		//! \todo Add parent_classes, constructors, variables and destructor

		template<class CLASS, class RETURN, class... PARAMS>
		auto_reflection_class& function(const std::string& aName, RETURN(CLASS::*aPtr)(PARAMS...), const size_t aModifiers) {
			mFunctions.push_back(std::shared_ptr<reflection_function>(
				new typedef auto_reflection_function<CLASS, RETURN, PARAMS...>(aName, aPtr, aModifiers);
			));
			return *this;
		}

		// Inherited from reflection_class
		const char* get_name() const override {
			mName.c_str();
		}

		size_t get_size() const override {
			return mSize;
		}

		size_t get_variable_count() const override {
			return mVariables.size();
		}

		const reflection_variable& get_variable(size_t aIndex) const override {
			return *mVariables[aIndex];
		}

		size_t get_function_count() const override {
			return mFunctions.size();
		}

		const reflection_function& get_function(size_t aIndex) const override {
			return *mFunctions[aIndex];
		}

		size_t get_constructor_count() const override {
			return mConstructors.size();
		}

		const reflection_constructor& get_constructor(size_t aIndex) const override {
			return *mConstructors[aIndex];
		}

		const reflection_destructor& get_destructor() const override {
			return *mDestructor;
		}

		size_t get_parent_count() const override {
			return mParents.size();
		}

		const reflection_class& get_parent_class(size_t aIndex) const override {
			return *mParents[aIndex];
		}
	};
}
#endif<|endoftext|>


<commit_before>#include "fenetre.h"
#include "ui_fenetre.h"
#include "fileManagement.h"
//#include <QMessageBox>

Fenetre::Fenetre(QWidget *parent) : QMainWindow(parent), ui(new Ui::Fenetre)
{
    ui->setupUi(this);

    //on connecte les Qapplication
    connect(ui->actionNouveau,SIGNAL(triggered()),this,SLOT(nouveauFichier()));
    connect(ui->actionOuvrir,SIGNAL(triggered()),this,SLOT(ouvrirFichier()));
    connect(ui->actionSauvegarder,SIGNAL(triggered()),this,SLOT(sauvegarderFichier()));
    connect(ui->actionQuitter,SIGNAL(triggered()),qApp,SLOT(quit()));

    index = loadIndexFromFile("runes.index"); //on charge les runes

    //on charge les icones des runes;
    vectorPixRune.push_back(new QPixmap("marque_ico.png"));
    vectorPixRune.push_back(new QPixmap("sceau_ico.png"));
    vectorPixRune.push_back(new QPixmap("glyphe_ico.png"));
    vectorPixRune.push_back(new QPixmap("quint_ico.png"));

    for (auto &a : index)
    {
        QHBoxLayout* layout = new QHBoxLayout; //layout de l'élément

        QLabel* ico = new QLabel; //icone de rune
        ico->setPixmap(vectorPixRune[static_cast<unsigned>(a.getType())]->scaledToHeight(50,Qt::SmoothTransformation)); //on resize

        QLabel* nom = new QLabel(a.getColoredName()); //on met le nom coloré de la rune
        nom->setWordWrap(true);
        nom->setMaximumWidth(200);

        QLabel* intEffet = new QLabel(a.getColoredEffect());

        layout->addWidget(ico);
        layout->addWidget(nom);
        layout->addWidget(intEffet);

        QListWidgetItem* item = new QListWidgetItem();

        ui->DRunelist->addItem(item); //temporaire

        QWidget* wi = new QWidget;
        wi->setLayout(layout);
        item->setSizeHint(wi->sizeHint());

        ui->DRunelist->setItemWidget(item,wi);
    }

    connect(ui->DRunelist,SIGNAL(clicked(QModelIndex)),this,SLOT(ajouteBonneList(QModelIndex))); //on connecte pour que quand on clique on ajoute la rune.

    //on connecte pour qu'on pouisse supprimer les runes des petites listes
    connect(ui->MarquesList,SIGNAL(clicked(QModelIndex)),this,SLOT(supprimerMarque(QModelIndex)));
    connect(ui->SceauxList,SIGNAL(clicked(QModelIndex)),this,SLOT(supprimerSceau(QModelIndex)));
    connect(ui->GlyphesList,SIGNAL(clicked(QModelIndex)),this,SLOT(supprimerGlyphe(QModelIndex)));
    connect(ui->QuintList,SIGNAL(clicked(QModelIndex)),this,SLOT(supprimerQuint(QModelIndex)));
}

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

void Fenetre::supprimerMarque(QModelIndex ind)
{
    page.remove(RuneType::Marque, ind.row()); //on supprime la bonne rune
    ui->MarquesList->removeItemWidget(ui->MarquesList->currentItem()); //on delete l'item
    ui->MarquesList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::supprimerSceau(QModelIndex ind)
{
    page.remove(RuneType::Sceau, ind.row()); //on supprime la bonne rune
    ui->SceauxList->removeItemWidget(ui->SceauxList->currentItem()); //on delete l'item
    ui->SceauxList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::supprimerGlyphe(QModelIndex ind)
{
    page.remove(RuneType::Glyphe, ind.row()); //on supprime la bonne rune
    ui->GlyphesList->removeItemWidget(ui->GlyphesList->currentItem()); //on delete l'item
    ui->GlyphesList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::supprimerQuint(QModelIndex ind)
{
    page.remove(RuneType::Quint, ind.row()); //on supprime la bonne rune
    ui->QuintList->removeItemWidget(ui->QuintList->currentItem()); //on delete l'item
    ui->QuintList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::ajouteBonneList(QModelIndex ind)
{
    bool success = page.ajouterRune(index[ind.row()]); //On ajoute le bon index à la runepage. théoriquement c'est bon.
    if (success) //on a jouté la rune
    {
        if (index[ind.row()].getType() == RuneType::Marque) //on a cliqué sur une marque
        {
            addToList(ui->MarquesList, index[ind.row()]); //on ajoute à la liste des marques
        }
        else if (index[ind.row()].getType() == RuneType::Sceau) //on a cliqué sur un sceau
        {
            addToList(ui->SceauxList, index[ind.row()]); //on ajoute à la liste des sceaux
        }
        else if (index[ind.row()].getType() == RuneType::Glyphe) //on a cliqué sur un glyphe
        {
            addToList(ui->GlyphesList, index[ind.row()]); //on ajoute à la liste des glyphes
        }
        else if (index[ind.row()].getType() == RuneType::Quint) //on a cliqué sur une quintessence
        {
            addToList(ui->QuintList, index[ind.row()]); //on ajoute à la liste des quints
        }
    }
    updateStats();
}

void Fenetre::addToList(QListWidget* list, Rune const& rune)
{
    QHBoxLayout* layout = new QHBoxLayout; //layout de l'élément

    QLabel* nom = new QLabel(rune.getColoredName(7)); //on met le nom coloré de la rune
    nom->setWordWrap(true);
    nom->setMaximumWidth(125);

    QLabel* intEffet = new QLabel(rune.getColoredEffect(8));

    layout->addWidget(nom);
    layout->addWidget(intEffet);

    QListWidgetItem* item = new QListWidgetItem();

    list->addItem(item); //temporaire

    QWidget* wi = new QWidget;
    wi->setLayout(layout);
    item->setSizeHint(wi->sizeHint());

    list->setItemWidget(item,wi);
}

void Fenetre::nouveauFichier()
{
    //on vide les listes
    ui->MarquesList->clear();
    ui->SceauxList->clear();
    ui->GlyphesList->clear();
    ui->QuintList->clear();

    //on vide la page
    page.clear();

    updateStats();
}

void Fenetre::ouvrirFichier()
{
    //rien pour le moment
}

void Fenetre::sauvegarderFichier()
{
    //rien non plus
}

void Fenetre::updateStats()
{
    if (ui->MarquesList->count() == 0 && ui->SceauxList->count() == 0 && ui->GlyphesList->count() == 0 && ui->QuintList->count() == 0) //les listes sont toutes vides
    {
        ui->StatLabel->setText(""); //on vide le texte
    }
    else
    {
        QString newLabel{""};
        QString tmp{""};
        std::vector<Effet> allEffect = page.getAllEffect(); //on récupère tous les effets de la page !
        for (auto &a : allEffect) //on parcoure tous les effets
        {
            tmp = (a.second > 0) ? "+ " : "- "; //on prend le signe du bonus
            //+/- XX STAT (avec de la coloration et STAT en italique)
            newLabel += "
" + tmp + QString::number(abs(a.second)) + " " + a.first.c_str() + "
";
        }
        newLabel += ""; //on finit la mise en page
        ui->StatLabel->setText(newLabel);
    }
}
<commit_msg>C'est bon on peut cliquer partout sur le QListItemWidget sans problèmes ! Note à moi même : pour les QListView, pressed >>>>> clicked !<commit_after>#include "fenetre.h"
#include "ui_fenetre.h"
#include "fileManagement.h"
#include <QMessageBox>

Fenetre::Fenetre(QWidget *parent) : QMainWindow(parent), ui(new Ui::Fenetre)
{
    ui->setupUi(this);

    //on connecte les Qapplication
    connect(ui->actionNouveau,SIGNAL(triggered()),this,SLOT(nouveauFichier()));
    connect(ui->actionOuvrir,SIGNAL(triggered()),this,SLOT(ouvrirFichier()));
    connect(ui->actionSauvegarder,SIGNAL(triggered()),this,SLOT(sauvegarderFichier()));
    connect(ui->actionQuitter,SIGNAL(triggered()),qApp,SLOT(quit()));

    index = loadIndexFromFile("runes.index"); //on charge les runes

    //on charge les icones des runes;
    vectorPixRune.push_back(new QPixmap("marque_ico.png"));
    vectorPixRune.push_back(new QPixmap("sceau_ico.png"));
    vectorPixRune.push_back(new QPixmap("glyphe_ico.png"));
    vectorPixRune.push_back(new QPixmap("quint_ico.png"));

    for (auto &a : index)
    {
        QHBoxLayout* layout = new QHBoxLayout; //layout de l'élément

        QLabel* ico = new QLabel; //icone de rune
        ico->setPixmap(vectorPixRune[static_cast<unsigned>(a.getType())]->scaledToHeight(50,Qt::SmoothTransformation)); //on resize

        QLabel* nom = new QLabel(a.getColoredName()); //on met le nom coloré de la rune
        nom->setWordWrap(true);
        nom->setMaximumWidth(200);

        QLabel* intEffet = new QLabel(a.getColoredEffect());

        layout->addWidget(ico);
        layout->addWidget(nom);
        layout->addWidget(intEffet);

        QListWidgetItem* item = new QListWidgetItem();

        ui->DRunelist->addItem(item); //temporaire

        QWidget* wi = new QWidget;
        wi->setLayout(layout);
        item->setSizeHint(wi->sizeHint());

        ui->DRunelist->setItemWidget(item,wi);
    }

    connect(ui->DRunelist,SIGNAL(pressed(QModelIndex)),this,SLOT(ajouteBonneList(QModelIndex))); //on connecte pour que quand on clique on ajoute la rune.

    //on connecte pour qu'on pouisse supprimer les runes des petites listes
    connect(ui->MarquesList,SIGNAL(pressed(QModelIndex)),this,SLOT(supprimerMarque(QModelIndex)));
    connect(ui->SceauxList,SIGNAL(pressed(QModelIndex)),this,SLOT(supprimerSceau(QModelIndex)));
    connect(ui->GlyphesList,SIGNAL(pressed(QModelIndex)),this,SLOT(supprimerGlyphe(QModelIndex)));
    connect(ui->QuintList,SIGNAL(pressed(QModelIndex)),this,SLOT(supprimerQuint(QModelIndex)));
}

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

void Fenetre::supprimerMarque(QModelIndex ind)
{
    page.remove(RuneType::Marque, ind.row()); //on supprime la bonne rune
    ui->MarquesList->removeItemWidget(ui->MarquesList->currentItem()); //on delete l'item
    ui->MarquesList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::supprimerSceau(QModelIndex ind)
{
    page.remove(RuneType::Sceau, ind.row()); //on supprime la bonne rune
    ui->SceauxList->removeItemWidget(ui->SceauxList->currentItem()); //on delete l'item
    ui->SceauxList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::supprimerGlyphe(QModelIndex ind)
{
    page.remove(RuneType::Glyphe, ind.row()); //on supprime la bonne rune
    ui->GlyphesList->removeItemWidget(ui->GlyphesList->currentItem()); //on delete l'item
    ui->GlyphesList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::supprimerQuint(QModelIndex ind)
{
    page.remove(RuneType::Quint, ind.row()); //on supprime la bonne rune
    ui->QuintList->removeItemWidget(ui->QuintList->currentItem()); //on delete l'item
    ui->QuintList->takeItem(ind.row()); //on enlève les espaces
    updateStats();
}

void Fenetre::ajouteBonneList(QModelIndex ind)
{
    bool success = page.ajouterRune(index[ind.row()]); //On ajoute le bon index à la runepage. théoriquement c'est bon.
    if (success) //on a jouté la rune
    {
        if (index[ind.row()].getType() == RuneType::Marque) //on a cliqué sur une marque
        {
            addToList(ui->MarquesList, index[ind.row()]); //on ajoute à la liste des marques
        }
        else if (index[ind.row()].getType() == RuneType::Sceau) //on a cliqué sur un sceau
        {
            addToList(ui->SceauxList, index[ind.row()]); //on ajoute à la liste des sceaux
        }
        else if (index[ind.row()].getType() == RuneType::Glyphe) //on a cliqué sur un glyphe
        {
            addToList(ui->GlyphesList, index[ind.row()]); //on ajoute à la liste des glyphes
        }
        else if (index[ind.row()].getType() == RuneType::Quint) //on a cliqué sur une quintessence
        {
            addToList(ui->QuintList, index[ind.row()]); //on ajoute à la liste des quints
        }
    }
    updateStats();
}

void Fenetre::addToList(QListWidget* list, Rune const& rune)
{
    QHBoxLayout* layout = new QHBoxLayout; //layout de l'élément

    QLabel* nom = new QLabel(rune.getColoredName(7)); //on met le nom coloré de la rune
    nom->setWordWrap(true);
    nom->setMaximumWidth(125);

    QLabel* intEffet = new QLabel(rune.getColoredEffect(8));

    layout->addWidget(nom);
    layout->addWidget(intEffet);

    QListWidgetItem* item = new QListWidgetItem();

    list->addItem(item); //temporaire

    QWidget* wi = new QWidget;
    wi->setLayout(layout);
    item->setSizeHint(wi->sizeHint());

    list->setItemWidget(item,wi);
}

void Fenetre::nouveauFichier()
{
    //on vide les listes
    ui->MarquesList->clear();
    ui->SceauxList->clear();
    ui->GlyphesList->clear();
    ui->QuintList->clear();

    //on vide la page
    page.clear();

    updateStats();
}

void Fenetre::ouvrirFichier()
{
    //rien pour le moment
}

void Fenetre::sauvegarderFichier()
{
    //rien non plus
}

void Fenetre::updateStats()
{
    if (ui->MarquesList->count() == 0 && ui->SceauxList->count() == 0 && ui->GlyphesList->count() == 0 && ui->QuintList->count() == 0) //les listes sont toutes vides
    {
        ui->StatLabel->setText(""); //on vide le texte
    }
    else
    {
        QString newLabel{""};
        QString tmp{""};
        std::vector<Effet> allEffect = page.getAllEffect(); //on récupère tous les effets de la page !
        for (auto &a : allEffect) //on parcoure tous les effets
        {
            tmp = (a.second > 0) ? "+ " : "- "; //on prend le signe du bonus
            //+/- XX STAT (avec de la coloration et STAT en italique)
            newLabel += "
" + tmp + QString::number(abs(a.second)) + " " + a.first.c_str() + "
";
        }
        newLabel += ""; //on finit la mise en page
        ui->StatLabel->setText(newLabel);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nl_bond.h"

#include <cassert>
#include <glog/logging.h>
#include <linux/if_bridge.h>
#include <netlink/route/link.h>
#include <netlink/route/link/bonding.h>

#include "cnetlink.h"
#include "nl_output.h"
#include "sai.h"

namespace basebox {

nl_bond::nl_bond(cnetlink *nl) : swi(nullptr), nl(nl) {}

void nl_bond::clear() noexcept {
  lag_members.clear();
  ifi2lag.clear();
}

uint32_t nl_bond::get_lag_id(rtnl_link *bond) {
  assert(bond);

  return get_lag_id(rtnl_link_get_ifindex(bond));
}

uint32_t nl_bond::get_lag_id(int ifindex) {
  auto it = ifi2lag.find(ifindex);
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": lag_id not found for if=" << ifindex;
    return 0;
  }

  return it->second;
}

std::set<uint32_t> nl_bond::get_members(rtnl_link *bond) {
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  return mem_it->second;
}

std::set<uint32_t> nl_bond::get_members_by_port_id(uint32_t port_id) {
  auto mem_it = lag_members.find(port_id);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for port_id="
	         << port_id;
    return {};
  }

  return mem_it->second;
}

int nl_bond::update_lag(rtnl_link *old_link, rtnl_link *new_link) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  VLOG(1) << __FUNCTION__ << ": updating bond interface ";
  int rv = 0;

  uint8_t o_mode, n_mode;
  uint32_t lag_id = nl->get_port_id(new_link);

  if (lag_id == 0) {
    rv = add_lag(new_link);
    if (rv < 0)
      return rv;

    lag_id = rv;
  }

  rv = rtnl_link_bond_get_mode(old_link, &o_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  rv = rtnl_link_bond_get_mode(new_link, &n_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  if (o_mode != n_mode) {
    VLOG(1) << __FUNCTION__ << ": bond mode updated "
            << static_cast<uint32_t>(n_mode);
    rv = swi->lag_set_mode(lag_id, n_mode);

    if (rv < 0) {
      VLOG(1) << __FUNCTION__ << ": failed to set active state for "
              << OBJ_CAST(new_link);
    }

    return 0;
  }

  add_l3_address(new_link);
#endif

  return 0;
}

int nl_bond::add_lag(rtnl_link *bond) {
  uint32_t lag_id = 0;
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint8_t mode;

  rtnl_link_bond_get_mode(bond, &mode);

  assert(bond);
  rv = swi->lag_create(&lag_id, rtnl_link_get_name(bond), mode);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed to create lag for "
               << OBJ_CAST(bond);
    return rv;
  }

  rv = lag_id;

  auto rv_emp =
      ifi2lag.emplace(std::make_pair(rtnl_link_get_ifindex(bond), lag_id));

  if (!rv_emp.second) {
    VLOG(1) << __FUNCTION__
            << ": lag exists with lag_id=" << rv_emp.first->second
            << " for bond " << OBJ_CAST(bond);
    rv = rv_emp.first->second;

    if (lag_id != rv_emp.first->second)
      swi->lag_remove(lag_id);
  }

#endif

  return rv;
}

int nl_bond::remove_lag(rtnl_link *bond) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  int rv = 0;
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));

  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return -ENODEV;
  }

  rv = swi->lag_remove(it->second);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__
               << ": failed to remove lag with lag_id=" << it->second
               << " for bond " << OBJ_CAST(bond);
    return rv;
  }

  ifi2lag.erase(it);
#endif

  return 0;
}

int nl_bond::add_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint32_t lag_id;
  uint8_t state = 0;

  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": no lag_id found creating new for "
            << OBJ_CAST(bond);

    rv = add_lag(bond);
    if (rv < 0)
      return rv;

    lag_id = rv;
  } else {
    lag_id = it->second;
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignoring port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) { // No ports in lag
    std::set<uint32_t> members;
    members.insert(port_id);
    auto lm_rv = lag_members.emplace(lag_id, members);
  } else {
    mem_it->second.insert(port_id);
  }

  rv = rtnl_link_bond_slave_get_state(link, &state);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get slave state for "
            << OBJ_CAST(link);
  }

  rv = swi->lag_add_member(lag_id, port_id);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed add member " << port_id;
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(lag_id, port_id, state == 0);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed set active member " << port_id;
    return -EINVAL;
  }

  if (rtnl_link_get_master(bond)) {
    // check bridge attachement
    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    if (br_link) {
      VLOG(2) << __FUNCTION__
              << ": bond was already bridge slave: " << OBJ_CAST(br_link);
      nl->link_created(br_link);

      auto new_state = rtnl_link_bridge_get_port_state(br_link);
      std::string state;

      switch (new_state) {
      case BR_STATE_FORWARDING:
        state = "forward";
        break;
      case BR_STATE_BLOCKING:
        state = "block";
        break;
      case BR_STATE_DISABLED:
        state = "disable";
        break;
      case BR_STATE_LISTENING:
        state = "listen";
        break;
      case BR_STATE_LEARNING:
        state = "learn";
        break;
      default:
        VLOG(1) << __FUNCTION__ << ": stp state change not supported";
        return rv;
      }

      swi->ofdpa_stg_state_port_set(port_id, state);
    }

    rv = nl->set_bridge_port_vlan_tpid(br_link);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  }

  // XXX FIXME check for vlan interfaces
  // Adding an IP address here will ensure that every slave that is
  // added will retry to add the address. It will not be written to the
  // ASIC, but repeated messages will be seen
  // This should be done in ::add_lag, but for currently unknown reasons
  // this fails when the lag has no members yet. So keep it here for now.
  add_l3_address(bond);
#endif

  return rv;
}

int nl_bond::remove_lag_member(rtnl_link *link) {
  assert(link);

  int master_id = rtnl_link_get_master(link);
  auto master = nl->get_link(master_id, AF_UNSPEC);

  return remove_lag_member(master, link);
}

int nl_bond::remove_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(FATAL) << __FUNCTION__ << ": no lag_id found for " << OBJ_CAST(bond);
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid lag port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto lm_rv = lag_members.find(it->second);
  if (lm_rv == lag_members.end()) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid attached port "
            << OBJ_CAST(link);
    return -EINVAL;
  }

  rv = swi->lag_remove_member(it->second, port_id);
  lag_members.erase(lm_rv);

  if (nl->is_bridge_interface(bond)) {
    swi->ofdpa_stg_state_port_set(port_id, "forward");

    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    rv = nl->unset_bridge_port_vlan_tpid(br_link);

    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  }

  if (lm_rv->second.empty())
    remove_l3_address(bond);

  if (nl->is_bridge_interface(bond))
    swi->ofdpa_stg_state_port_set(port_id, "forward");
#endif

  return rv;
}

int nl_bond::update_lag_member(rtnl_link *old_slave, rtnl_link *new_slave) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(new_slave);

  int rv;
  uint8_t new_state;
  uint8_t old_state;

  int n_master_id = rtnl_link_get_master(new_slave);
  int o_master_id = rtnl_link_get_master(old_slave);
  auto new_master = nl->get_link(n_master_id, AF_UNSPEC);
  auto old_master = nl->get_link(o_master_id, AF_UNSPEC);
  auto port_id = nl->get_port_id(new_slave);

  if (old_master != new_master || new_master == 0) {
    return -EINVAL;
  }

  rv = rtnl_link_bond_slave_get_state(new_slave, &new_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }
  rtnl_link_bond_slave_get_state(old_slave, &old_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(nl->get_port_id(new_master), port_id,
                                  new_state == 0);
#endif
  return 0;
}

int nl_bond::add_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    LOG(INFO) << __FUNCTION__ << ": adding address=" << OBJ_CAST(i);

    rv = nl->add_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to add l3 address " << OBJ_CAST(i)
                 << " to " << OBJ_CAST(link);
  }
  LOG(INFO) << __FUNCTION__ << ": added l3 addresses to bond "
            << OBJ_CAST(link);

#endif
  return rv;
}

int nl_bond::remove_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    rv = nl->del_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to remove l3 address from "
                 << OBJ_CAST(link);
  }

#endif
  return rv;
}

} // namespace basebox
<commit_msg>nl_bond: update configured vlans when changing bond members<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nl_bond.h"

#include <cassert>
#include <glog/logging.h>
#include <linux/if_bridge.h>
#include <netlink/route/link.h>
#include <netlink/route/link/bonding.h>

#include "cnetlink.h"
#include "nl_output.h"
#include "sai.h"

namespace basebox {

nl_bond::nl_bond(cnetlink *nl) : swi(nullptr), nl(nl) {}

void nl_bond::clear() noexcept {
  lag_members.clear();
  ifi2lag.clear();
}

uint32_t nl_bond::get_lag_id(rtnl_link *bond) {
  assert(bond);

  return get_lag_id(rtnl_link_get_ifindex(bond));
}

uint32_t nl_bond::get_lag_id(int ifindex) {
  auto it = ifi2lag.find(ifindex);
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": lag_id not found for if=" << ifindex;
    return 0;
  }

  return it->second;
}

std::set<uint32_t> nl_bond::get_members(rtnl_link *bond) {
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  return mem_it->second;
}

std::set<uint32_t> nl_bond::get_members_by_port_id(uint32_t port_id) {
  auto mem_it = lag_members.find(port_id);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for port_id="
	         << port_id;
    return {};
  }

  return mem_it->second;
}

int nl_bond::update_lag(rtnl_link *old_link, rtnl_link *new_link) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  VLOG(1) << __FUNCTION__ << ": updating bond interface ";
  int rv = 0;

  uint8_t o_mode, n_mode;
  uint32_t lag_id = nl->get_port_id(new_link);

  if (lag_id == 0) {
    rv = add_lag(new_link);
    if (rv < 0)
      return rv;

    lag_id = rv;
  }

  rv = rtnl_link_bond_get_mode(old_link, &o_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  rv = rtnl_link_bond_get_mode(new_link, &n_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  if (o_mode != n_mode) {
    VLOG(1) << __FUNCTION__ << ": bond mode updated "
            << static_cast<uint32_t>(n_mode);
    rv = swi->lag_set_mode(lag_id, n_mode);

    if (rv < 0) {
      VLOG(1) << __FUNCTION__ << ": failed to set active state for "
              << OBJ_CAST(new_link);
    }

    return 0;
  }

  add_l3_address(new_link);
#endif

  return 0;
}

int nl_bond::add_lag(rtnl_link *bond) {
  uint32_t lag_id = 0;
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint8_t mode;

  rtnl_link_bond_get_mode(bond, &mode);

  assert(bond);
  rv = swi->lag_create(&lag_id, rtnl_link_get_name(bond), mode);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed to create lag for "
               << OBJ_CAST(bond);
    return rv;
  }

  rv = lag_id;

  auto rv_emp =
      ifi2lag.emplace(std::make_pair(rtnl_link_get_ifindex(bond), lag_id));

  if (!rv_emp.second) {
    VLOG(1) << __FUNCTION__
            << ": lag exists with lag_id=" << rv_emp.first->second
            << " for bond " << OBJ_CAST(bond);
    rv = rv_emp.first->second;

    if (lag_id != rv_emp.first->second)
      swi->lag_remove(lag_id);
  }

#endif

  return rv;
}

int nl_bond::remove_lag(rtnl_link *bond) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  int rv = 0;
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));

  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return -ENODEV;
  }

  rv = swi->lag_remove(it->second);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__
               << ": failed to remove lag with lag_id=" << it->second
               << " for bond " << OBJ_CAST(bond);
    return rv;
  }

  ifi2lag.erase(it);
#endif

  return 0;
}

int nl_bond::add_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint32_t lag_id;
  uint8_t state = 0;

  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": no lag_id found creating new for "
            << OBJ_CAST(bond);

    rv = add_lag(bond);
    if (rv < 0)
      return rv;

    lag_id = rv;
  } else {
    lag_id = it->second;
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignoring port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) { // No ports in lag
    std::set<uint32_t> members;
    members.insert(port_id);
    auto lm_rv = lag_members.emplace(lag_id, members);
  } else {
    mem_it->second.insert(port_id);
  }

  rv = rtnl_link_bond_slave_get_state(link, &state);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get slave state for "
            << OBJ_CAST(link);
  }

  rv = swi->lag_add_member(lag_id, port_id);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed add member " << port_id;
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(lag_id, port_id, state == 0);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed set active member " << port_id;
    return -EINVAL;
  }

  if (rtnl_link_get_master(bond)) {
    // check bridge attachement
    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    if (br_link) {
      VLOG(2) << __FUNCTION__
              << ": bond was already bridge slave: " << OBJ_CAST(br_link);
      nl->link_created(br_link);

      auto new_state = rtnl_link_bridge_get_port_state(br_link);
      std::string state;

      switch (new_state) {
      case BR_STATE_FORWARDING:
        state = "forward";
        break;
      case BR_STATE_BLOCKING:
        state = "block";
        break;
      case BR_STATE_DISABLED:
        state = "disable";
        break;
      case BR_STATE_LISTENING:
        state = "listen";
        break;
      case BR_STATE_LEARNING:
        state = "learn";
        break;
      default:
        VLOG(1) << __FUNCTION__ << ": stp state change not supported";
        return rv;
      }

      swi->ofdpa_stg_state_port_set(port_id, state);
    }

    rv = nl->set_bridge_port_vlan_tpid(br_link);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  } else {
    std::deque<uint16_t> vlans;

    nl->get_vlans(rtnl_link_get_ifindex(bond), &vlans);
    for (auto vid : vlans) {
      swi->ingress_port_vlan_add(port_id, vid, false);
      swi->egress_port_vlan_add(port_id, vid, false);
    }
  }

  // XXX FIXME check for vlan interfaces
  // Adding an IP address here will ensure that every slave that is
  // added will retry to add the address. It will not be written to the
  // ASIC, but repeated messages will be seen
  // This should be done in ::add_lag, but for currently unknown reasons
  // this fails when the lag has no members yet. So keep it here for now.
  add_l3_address(bond);
#endif

  return rv;
}

int nl_bond::remove_lag_member(rtnl_link *link) {
  assert(link);

  int master_id = rtnl_link_get_master(link);
  auto master = nl->get_link(master_id, AF_UNSPEC);

  return remove_lag_member(master, link);
}

int nl_bond::remove_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(FATAL) << __FUNCTION__ << ": no lag_id found for " << OBJ_CAST(bond);
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid lag port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto lm_rv = lag_members.find(it->second);
  if (lm_rv == lag_members.end()) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid attached port "
            << OBJ_CAST(link);
    return -EINVAL;
  }

  rv = swi->lag_remove_member(it->second, port_id);
  lag_members.erase(lm_rv);

  if (nl->is_bridge_interface(bond)) {
    swi->ofdpa_stg_state_port_set(port_id, "forward");

    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    rv = nl->unset_bridge_port_vlan_tpid(br_link);

    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  } else {
    std::deque<uint16_t> vlans;

    nl->get_vlans(rtnl_link_get_ifindex(bond), &vlans);

  if (lm_rv->second.empty())
    remove_l3_address(bond);

  if (nl->is_bridge_interface(bond))
    swi->ofdpa_stg_state_port_set(port_id, "forward");

    for (auto vid : vlans) {
      swi->ingress_port_vlan_remove(port_id, vid, false);
      swi->egress_port_vlan_remove(port_id, vid);
    }
  }
#endif

  return rv;
}

int nl_bond::update_lag_member(rtnl_link *old_slave, rtnl_link *new_slave) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(new_slave);

  int rv;
  uint8_t new_state;
  uint8_t old_state;

  int n_master_id = rtnl_link_get_master(new_slave);
  int o_master_id = rtnl_link_get_master(old_slave);
  auto new_master = nl->get_link(n_master_id, AF_UNSPEC);
  auto old_master = nl->get_link(o_master_id, AF_UNSPEC);
  auto port_id = nl->get_port_id(new_slave);

  if (old_master != new_master || new_master == 0) {
    return -EINVAL;
  }

  rv = rtnl_link_bond_slave_get_state(new_slave, &new_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }
  rtnl_link_bond_slave_get_state(old_slave, &old_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(nl->get_port_id(new_master), port_id,
                                  new_state == 0);
#endif
  return 0;
}

int nl_bond::add_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    LOG(INFO) << __FUNCTION__ << ": adding address=" << OBJ_CAST(i);

    rv = nl->add_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to add l3 address " << OBJ_CAST(i)
                 << " to " << OBJ_CAST(link);
  }
  LOG(INFO) << __FUNCTION__ << ": added l3 addresses to bond "
            << OBJ_CAST(link);

#endif
  return rv;
}

int nl_bond::remove_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    rv = nl->del_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to remove l3 address from "
                 << OBJ_CAST(link);
  }

#endif
  return rv;
}

} // namespace basebox
<|endoftext|>


<commit_before>//============================================================================
// Name        : ToolKitTest.cpp
// Author      : xzl
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include "Util/logger.h"
#include "Util/util.h"
#include "Util/RingBuffer.h"
#include "Thread/threadgroup.h"
#include <list>

using namespace std;
using namespace ZL::Util;
using namespace ZL::Thread;

bool g_bExitRead = false;
bool g_bExitWrite = false;
RingBuffer<string>::Ptr g_ringBuf(new RingBuffer<string>(48));

void onReadEvent(const string &str){
	//读事件模式性
	DebugL << str;
}
void onDetachEvent(){
	WarnL;
}
void doRead(int threadNum){
	//主动读模式采用轮训机制 效率比较差，可以加入条件变量机制改造
	auto reader = g_ringBuf->attach();
	while(!g_bExitRead){
		auto ptr = reader->read();
		if(ptr){
			InfoL << "thread " << threadNum << ":" << *ptr;
		}else{
			InfoL << "thread " << threadNum << ": read nullptr!";
			usleep(100 * 1000);
		}
	}
}
void doWrite(){
	int i = 0;
	while(!g_bExitWrite){
		g_ringBuf->write(to_string(++i));
		usleep(100 * 1000);
	}

}
int main() {
	Logger::Instance().add(std::make_shared<ConsoleChannel>("stdout", LTrace));
	//Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());

	//添加一个读取器
	auto ringReader = g_ringBuf->attach();
	ringReader->setReadCB([](const string &pkt){
		onReadEvent(pkt);
	});
	ringReader->setDetachCB([](){
		onDetachEvent();
	});

	//主动读取线程
	thread_group group;
	for(int i = 0 ;i < 4 ; ++i){
		group.create_thread([i](){
			doRead(i);
		});
	}

	//写线程
	group.create_thread([](){
		doWrite();
	});

	sleep(1);
	//写线程退出
	g_bExitWrite = true;
	sleep(1);
	//释放环形缓冲
	g_ringBuf.reset();

	sleep(1);
	g_bExitRead = true;
	group.join_all();

	Logger::Destory();
	return 0;
}

<commit_msg>fixed<commit_after>//============================================================================
// Name        : ToolKitTest.cpp
// Author      : xzl
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include "Util/logger.h"
#include "Util/util.h"
#include "Util/RingBuffer.h"
#include "Thread/threadgroup.h"
#include <list>

using namespace std;
using namespace ZL::Util;
using namespace ZL::Thread;

bool g_bExitRead = false;
bool g_bExitWrite = false;
RingBuffer<string>::Ptr g_ringBuf(new RingBuffer<string>());

void onReadEvent(const string &str){
	//读事件模式性
	DebugL << str;
}
void onDetachEvent(){
	WarnL;
}

void doWrite(){
	int i = 0;
	while(!g_bExitWrite){
		g_ringBuf->write(to_string(++i),true);
		usleep(100 * 1000);
	}

}
int main() {
	Logger::Instance().add(std::make_shared<ConsoleChannel>("stdout", LTrace));
	//Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());

	//添加一个读取器
	auto ringReader = g_ringBuf->attach();
	ringReader->setReadCB([](const string &pkt){
		onReadEvent(pkt);
	});
	ringReader->setDetachCB([](){
		onDetachEvent();
	});

	thread_group group;
	//写线程
	group.create_thread([](){
		doWrite();
	});

	sleep(1);
	//写线程退出
	g_bExitWrite = true;
	sleep(1);
	//释放环形缓冲
	g_ringBuf.reset();

	sleep(1);
	g_bExitRead = true;
	group.join_all();

	Logger::Destory();
	return 0;
}

<|endoftext|>


<commit_before>#pragma once

#include <nlohmann/json.hpp>

namespace dai {

/// NodeIo informations such as name, type, ...
struct NodeIoInfo {
    enum class Type { MSender, SSender, MReceiver, SReceiver };

    std::string group;
    std::string name;
    Type type = Type::SReceiver;
    bool blocking = true;
    int queueSize = 8;

    struct Options {
        bool waitForMessage = false;
    } options;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NodeIoInfo::Options, waitForMessage);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NodeIoInfo, group, name, type, blocking, queueSize, options);

}  // namespace dai
<commit_msg>Removed 'options' from NodeIoInfo<commit_after>#pragma once

#include <nlohmann/json.hpp>

namespace dai {

/// NodeIo informations such as name, type, ...
struct NodeIoInfo {
    enum class Type { MSender, SSender, MReceiver, SReceiver };

    std::string group;
    std::string name;
    Type type = Type::SReceiver;
    bool blocking = true;
    int queueSize = 8;
    bool waitForMessage = false;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NodeIoInfo, group, name, type, blocking, queueSize, waitForMessage);

}  // namespace dai
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <sstream>
#include <Eigen/Dense>
#include "gtest/gtest.h"
#include "bicycle.h"

/*
 * A and B expected state space matrices generated using dtk.bicycle:
 *
 * from dtk.bicycle import *
 * np.set_printoptions(precision=16)
 * A, B = benchmark_state_space(*benchmark_matrices(), v=1, g=9.80665)
 */

namespace {
    Eigen::Matrix<double, 4, 4> A;
    Eigen::Matrix<double, 4, 2> B(
        (Eigen::Matrix<double, 4, 2>() <<
            0.                ,  0.                ,
            0.                ,  0.                ,
            0.0159349789179135, -0.1240920254115741,
           -0.1240920254115741,  4.3238401808042282).finished()
        );

    std::string output_matrices(Eigen::MatrixXd expected, Eigen::MatrixXd actual) {
        std::stringstream ss;
        ss << "expected:\n" << expected << "\nactual:\n" << actual << std::endl;
        return ss.str();
    }
} // namespace

TEST(StateSpace, ContinuousV1) {
    model::Bicycle bicycle("benchmark_matrices.txt", 1.0);

    A <<  0.                ,   0.                ,   1.                , 0.                ,
          0.                ,   0.                ,   0.                , 1.                ,
          9.4865338000460664,  -1.4625257433243051,  -0.1055224498056882, -0.330515398992312 ,
         11.7154748079957685,  28.9264833312917631,   3.6768052333214327, -3.0848655274330694;

    EXPECT_TRUE(bicycle.A().isApprox(A)) << output_matrices(bicycle.A(), A);
    EXPECT_TRUE(bicycle.B().isApprox(B)) << output_matrices(bicycle.B(), B);
}

TEST(StateSpace, ContinuousV3) {
    model::Bicycle bicycle("benchmark_matrices.txt", 3.0);

    A <<  0.                ,   0.                ,   1.                , 0.                ,
          0.                ,   0.                ,   0.                , 1.                ,
          9.4865338000460664,  -8.5921076477970253,  -0.3165673494170646, -0.9915461969769359,
         11.7154748079957685,  13.1527626512942426,  11.0304156999642977, -9.2545965822992091;

    EXPECT_TRUE(bicycle.A().isApprox(A)) << output_matrices(bicycle.A(), A);
    EXPECT_TRUE(bicycle.B().isApprox(B)) << output_matrices(bicycle.B(), B);
}

TEST(StateSpace, ContinuousV5) {
    model::Bicycle bicycle("benchmark_matrices.txt", 5.0);

    A <<  0.                ,   0.                ,   1.                , 0.                ,
          0.                ,   0.                ,   0.                , 1.                ,
          9.4865338000460664, -22.8512714567424666,  -0.5276122490284411, -1.6525769949615603,
         11.7154748079957685, -18.3946787087007344,  18.384026166607164 , -15.4243276371653479;

    EXPECT_TRUE(bicycle.A().isApprox(A)) << output_matrices(bicycle.A(), A);
    EXPECT_TRUE(bicycle.B().isApprox(B)) << output_matrices(bicycle.B(), B);
}
<commit_msg>Test computation of discrete state space matrices<commit_after>#include <iostream>
#include <string>
#include <sstream>
#include <Eigen/Dense>
#include "gtest/gtest.h"
#include "bicycle.h"

/*
 * A and B expected state space matrices generated using dtk.bicycle:
 *
 * from dtk.bicycle import *
 * np.set_printoptions(precision=16)
 * A, B = benchmark_state_space(*benchmark_matrices(), v=1, g=9.80665)
 *
 * Ad, Bd generated using scipy.signal
 *
 * import scipy.signal as sig
 * sig.cont2discrete((A, B, np.eye(4), np.zeros((4, 2))), 1/200)
 */

namespace {
    const double dt = 1.0/200;
    Eigen::Matrix<double, 4, 4> A;
    Eigen::Matrix<double, 4, 2> B(
        (Eigen::Matrix<double, 4, 2>() <<
            0.                ,  0.                ,
            0.                ,  0.                ,
            0.0159349789179135, -0.1240920254115741,
           -0.1240920254115741,  4.3238401808042282).finished()
        );
    Eigen::Matrix<double, 4, 4> Ad;
    Eigen::Matrix<double, 4, 2> Bd;

    std::string output_matrices(Eigen::MatrixXd expected, Eigen::MatrixXd actual) {
        std::stringstream ss;
        ss << "expected:\n" << expected << "\nactual:\n" << actual << std::endl;
        return ss.str();
    }
} // namespace

TEST(StateSpace, ContinuousV1) {
    model::Bicycle bicycle("benchmark_matrices.txt", 1.0);

    A <<  0.                ,   0.                ,   1.                , 0.                ,
          0.                ,   0.                ,   0.                , 1.                ,
          9.4865338000460664,  -1.4625257433243051,  -0.1055224498056882, -0.330515398992312 ,
         11.7154748079957685,  28.9264833312917631,   3.6768052333214327, -3.0848655274330694;

    EXPECT_TRUE(bicycle.A().isApprox(A)) << output_matrices(bicycle.A(), A);
    EXPECT_TRUE(bicycle.B().isApprox(B)) << output_matrices(bicycle.B(), B);
}

TEST(StateSpace, ContinuousV3) {
    model::Bicycle bicycle("benchmark_matrices.txt", 3.0);

    A <<  0.                ,   0.                ,   1.                , 0.                ,
          0.                ,   0.                ,   0.                , 1.                ,
          9.4865338000460664,  -8.5921076477970253,  -0.3165673494170646, -0.9915461969769359,
         11.7154748079957685,  13.1527626512942426,  11.0304156999642977, -9.2545965822992091;

    EXPECT_TRUE(bicycle.A().isApprox(A)) << output_matrices(bicycle.A(), A);
    EXPECT_TRUE(bicycle.B().isApprox(B)) << output_matrices(bicycle.B(), B);
}

TEST(StateSpace, ContinuousV5) {
    model::Bicycle bicycle("benchmark_matrices.txt", 5.0);

    A <<  0.                ,   0.                ,   1.                , 0.                ,
          0.                ,   0.                ,   0.                , 1.                ,
          9.4865338000460664, -22.8512714567424666,  -0.5276122490284411, -1.6525769949615603,
         11.7154748079957685, -18.3946787087007344,  18.384026166607164 , -15.4243276371653479;

    EXPECT_TRUE(bicycle.A().isApprox(A)) << output_matrices(bicycle.A(), A);
    EXPECT_TRUE(bicycle.B().isApprox(B)) << output_matrices(bicycle.B(), B);
}

TEST(StateSpace, DiscreteV1) {
    model::Bicycle bicycle("benchmark_matrices.txt", 1.0, dt);

    Ad << 1.0001184820643081e+00,  -1.8478167519170527e-05, 4.9988533321204658e-03,  -4.1402267568149167e-06,
          1.4642849817488363e-04,   1.0003596378458957e+00, 4.5963276543359894e-05,   4.9622093457528903e-03,
          4.7373286374364838e-02,  -7.4307138855974368e-03, 9.9957576800707704e-01,  -1.6579041282911602e-03,
          5.8570670758658606e-02,   1.4347204345110903e-01, 1.8386655631933688e-02,   9.8503669772459101e-01;
    Bd << 2.0001145816138571e-07,  -1.5807242572795020e-06,
         -1.5420741274461165e-06,   5.3764780115010109e-05,
          8.0170391584997460e-05,  -6.3821951352698188e-04,
         -6.1503818438800187e-04,   2.1450096478647790e-02;

    EXPECT_TRUE(bicycle.Ad().isApprox(Ad)) << output_matrices(bicycle.Ad(), Ad);
    EXPECT_TRUE(bicycle.Bd().isApprox(Bd)) << output_matrices(bicycle.Bd(), Bd);
}

TEST(StateSpace, DiscreteV3) {
    model::Bicycle bicycle("benchmark_matrices.txt", 3.0, dt);

    Ad << 1.0001182770323798e+00,  -1.0761577382854053e-04, 4.9960146578785398e-03,  -1.2376061665969063e-05,
          1.4636823146804625e-04,   1.0001599497146791e+00, 1.3595045476592029e-04,   4.8861238841920599e-03,
          4.7249870478820497e-02,  -4.3089075152114520e-02, 9.9840018880958248e-01,  -4.9468596498928336e-03,
          5.8532959858267050e-02,   6.3097926791482337e-02, 5.3999308360513393e-02,   9.5480604315894413e-01;
    Bd << 2.0164212892573775e-07,  -1.6395468174733748e-06,
         -1.5238824775089880e-06,   5.3195920730699685e-05,
          8.1147158805629875e-05,  -6.7347769059348866e-04,
         -6.0416264157068470e-04,   2.1109948411569327e-02;

    EXPECT_TRUE(bicycle.Ad().isApprox(Ad)) << output_matrices(bicycle.Ad(), Ad);
    EXPECT_TRUE(bicycle.Bd().isApprox(Bd)) << output_matrices(bicycle.Bd(), Bd);
}

TEST(StateSpace, DiscreteV5) {
    model::Bicycle bicycle("benchmark_matrices.txt", 5.0, dt);

    Ad << 1.0001184820643081e+00,  -1.8478167519170527e-05, 4.9988533321204658e-03,  -4.1402267568149167e-06,
          1.4642849817488363e-04,   1.0003596378458957e+00, 4.5963276543359894e-05,   4.9622093457528903e-03,
          4.7373286374364838e-02,  -7.4307138855974368e-03, 9.9957576800707704e-01,  -1.6579041282911602e-03,
          5.8570670758658606e-02,   1.4347204345110903e-01, 1.8386655631933688e-02,   9.8503669772459101e-01;
    Bd << 2.0001145816138571e-07,  -1.5807242572795020e-06,
         -1.5420741274461165e-06,   5.3764780115010109e-05,
          8.0170391584997460e-05,  -6.3821951352698188e-04,
         -6.1503818438800187e-04,   2.1450096478647790e-02;
    Ad << 1.0001180700462438e+00,  -2.8474586368268200e-04, 4.9929766799901975e-03,  -2.0583494132583435e-05,
          1.4630038234223096e-04,   9.9976730145466564e-01, 2.2402776466154753e-04,   4.8110697443882302e-03,
          4.7124896630597990e-02,  -1.1371723873036946e-01, 9.9710530689603383e-01,  -8.2185377039953947e-03,
          5.8489213351501479e-02,  -9.3617401457300686e-02, 8.8474932659789590e-02,   9.2518956230185589e-01;
    Bd << 2.0326445533610386e-07,  -1.6981861891088082e-06,
         -1.5058897428593093e-06,   5.2632958211780904e-05,
          8.2117225610236940e-05,  -7.0858832804455301e-04,
         -5.9344551127057076e-04,   2.0774496614372077e-02;

    EXPECT_TRUE(bicycle.Ad().isApprox(Ad)) << output_matrices(bicycle.Ad(), Ad);
    EXPECT_TRUE(bicycle.Bd().isApprox(Bd)) << output_matrices(bicycle.Bd(), Bd);
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED

#include <string>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_id.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/config.hpp"

namespace libtorrent
{

	struct http_connection;
	class entry;
	class http_parser;
	class connection_queue;
	class session_settings;

	class TORRENT_EXPORT http_tracker_connection
		: public tracker_connection
	{
	friend class tracker_manager;
	public:

		http_tracker_connection(
			io_service& ios
			, connection_queue& cc
			, tracker_manager& man
			, tracker_request const& req
			, address bind_infc
			, boost::weak_ptr<request_callback> c
			, session_settings const& stn
			, proxy_settings const& ps
			, std::string const& password = "");

		void close();

	private:

		boost::intrusive_ptr<http_tracker_connection> self()
		{ return boost::intrusive_ptr<http_tracker_connection>(this); }

		void on_response(asio::error_code const& ec, http_parser const& parser
			, char const* data, int size);

		virtual void on_timeout() {}

		void parse(int status_code, const entry& e);
		bool extract_peer_info(const entry& e, peer_entry& ret);

		tracker_manager& m_man;
		boost::shared_ptr<http_connection> m_tracker_connection;
	};

}

#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED

<commit_msg>silence msvc warning<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED

#include <string>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_id.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/config.hpp"

namespace libtorrent
{

	struct http_connection;
	class entry;
	class http_parser;
	class connection_queue;
	struct session_settings;

	class TORRENT_EXPORT http_tracker_connection
		: public tracker_connection
	{
	friend class tracker_manager;
	public:

		http_tracker_connection(
			io_service& ios
			, connection_queue& cc
			, tracker_manager& man
			, tracker_request const& req
			, address bind_infc
			, boost::weak_ptr<request_callback> c
			, session_settings const& stn
			, proxy_settings const& ps
			, std::string const& password = "");

		void close();

	private:

		boost::intrusive_ptr<http_tracker_connection> self()
		{ return boost::intrusive_ptr<http_tracker_connection>(this); }

		void on_response(asio::error_code const& ec, http_parser const& parser
			, char const* data, int size);

		virtual void on_timeout() {}

		void parse(int status_code, const entry& e);
		bool extract_peer_info(const entry& e, peer_entry& ret);

		tracker_manager& m_man;
		boost::shared_ptr<http_connection> m_tracker_connection;
	};

}

#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED

<|endoftext|>


<commit_before>/** @addtogroup Simulation
 *  @{
 *  \copyright TU Dresden ZIH. All rights reserved.
 *  \authors Martin Flehmig, Marc Hartung, Marcus Walther
 *  \date Nov 2015
 */

#ifndef INCLUDE_SIMULATION_OPENMPSIMULATION_HPP_
#define INCLUDE_SIMULATION_OPENMPSIMULATION_HPP_

#include <fmi/AbstractFmu.hpp>
#include "Stdafx.hpp"
#include "solver/AbstractSolver.hpp"
#include "simulation/SerialSimulation.hpp"

namespace Simulation
{
    /**
     * This class represents an OpenMP parallel simulation for shared memory systems.
     * Several solvers and their associated FMUs can be handled.
     */
    class OpenMPSimulation : public SerialSimulation
    {
     public:
        /**
         * Create OpenMP simulation from given solvers.
         * @param solvers
         */
        //OpenMPSimulation(vector<Solver::AbstractSolverSPtr> solvers, const string_type & scheduleKind = "auto");
        OpenMPSimulation(const Initialization::SimulationPlan & in, const vector<std::shared_ptr<Solver::ISolver>> & solver);
        /**
         * Destructor. Destroys OpenMP simulation object and frees allocates resources.
         */
        virtual ~OpenMPSimulation();

        /**
         * As long as the simulation end time is not reached, this method calls
         * the solve methods (time integration) for all solvers in parallel.
         * \remark: OpenMP parallel for loop is used.
         */
        virtual void simulate();

        /**
         * Returns if a simulation is a MPI, OpenMP or Serial simulation
         * @return string_type One of: {"serial", "openmp", "mpi"}
         */
        virtual string_type getSimulationType() const;

     private:
        /**
         * It is possible to specify the OpenMP loop schedule algorithm.
         * Possible values are: static, dynamic, guided, auto, runtime.
         * Default: auto
         *
         * Todo: Add the possibility to control schedule value of OpenMP loops through config file and/or command line.
         */
        string_type _scheduleKind;
    };

} /* namespace Simulation */

#endif /* INCLUDE_SIMULATION_OPENMPSMULATION_HPP_ */
/**
 * @}
 */
<commit_msg>Add empty line for better reading<commit_after>/** @addtogroup Simulation
 *  @{
 *  \copyright TU Dresden ZIH. All rights reserved.
 *  \authors Martin Flehmig, Marc Hartung, Marcus Walther
 *  \date Nov 2015
 */

#ifndef INCLUDE_SIMULATION_OPENMPSIMULATION_HPP_
#define INCLUDE_SIMULATION_OPENMPSIMULATION_HPP_

#include <fmi/AbstractFmu.hpp>
#include "Stdafx.hpp"
#include "solver/AbstractSolver.hpp"
#include "simulation/SerialSimulation.hpp"

namespace Simulation
{
    /**
     * This class represents an OpenMP parallel simulation for shared memory systems.
     * Several solvers and their associated FMUs can be handled.
     */
    class OpenMPSimulation : public SerialSimulation
    {
     public:
        /**
         * Create OpenMP simulation from given solvers.
         * @param solvers
         */
        //OpenMPSimulation(vector<Solver::AbstractSolverSPtr> solvers, const string_type & scheduleKind = "auto");
        OpenMPSimulation(const Initialization::SimulationPlan & in, const vector<std::shared_ptr<Solver::ISolver>> & solver);

        /**
         * Destructor. Destroys OpenMP simulation object and frees allocates resources.
         */
        virtual ~OpenMPSimulation();

        /**
         * As long as the simulation end time is not reached, this method calls
         * the solve methods (time integration) for all solvers in parallel.
         * \remark: OpenMP parallel for loop is used.
         */
        virtual void simulate();

        /**
         * Returns if a simulation is a MPI, OpenMP or Serial simulation
         * @return string_type One of: {"serial", "openmp", "mpi"}
         */
        virtual string_type getSimulationType() const;

     private:
        /**
         * It is possible to specify the OpenMP loop schedule algorithm.
         * Possible values are: static, dynamic, guided, auto, runtime.
         * Default: auto
         *
         * Todo: Add the possibility to control schedule value of OpenMP loops through config file and/or command line.
         */
        string_type _scheduleKind;
    };

} /* namespace Simulation */

#endif /* INCLUDE_SIMULATION_OPENMPSMULATION_HPP_ */
/**
 * @}
 */
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#ifndef VAST_COMMAND_HPP
#define VAST_COMMAND_HPP

#include <functional>
#include <memory>
#include <string>
#include <string_view>

#include <caf/actor_system_config.hpp>
#include <caf/fwd.hpp>
#include <caf/message.hpp>

#include <caf/detail/unordered_flat_map.hpp>

#include "vast/error.hpp"
#include "vast/data.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/data.hpp"

#include "vast/detail/steady_map.hpp"
#include "vast/detail/string.hpp"

namespace vast {

/// A top-level command.
class command {
public:
  // -- member types -----------------------------------------------------------

  /// An option of a command.
  struct option {
    template <class T = bool>
    static std::pair<std::string, option>
    make(const std::string& tag, std::string desc, T x = {}) {
      auto s = detail::split_to_str(tag, ",");
      std::string shortcut;
      if (s.size() >= 2)
        shortcut = s[1][0];
      return {s[0], {std::move(shortcut), std::move(desc),
                    data{std::forward<T>(x)}}};
    }

    std::string shortcut;
    std::string description;
    data value;
  };

  /// Owning pointer to a command.
  using unique_ptr = std::unique_ptr<command>;

  /// Group of configuration parameters.
  using opt_group = caf::actor_system_config::opt_group;

  /// Maps names of config parameters to their value.
  using opt_map = std::map<std::string, caf::config_value>;

  /// Returns a CLI option as name/value pair.
  using get_opt = std::function<std::pair<std::string, caf::config_value>()>;

  /// Wraps the result of proceed.
  enum proceed_result {
    proceed_ok,
    stop_successful,
    stop_with_error
  };

  // -- constructors, destructors, and assignment operators --------------------

  command();

  command(command* parent, std::string_view name);

  virtual ~command();

  /// Runs the command and blocks until execution completes.
  /// @returns An exit code suitable for returning from main.
  int run(caf::actor_system& sys, caf::message args);

  /// Runs the command and blocks until execution completes.
  /// @returns An exit code suitable for returning from main.
  int run(caf::actor_system& sys, opt_map& options, caf::message args);

  /// Prints usage to `std::cerr`.
  void usage();

  /// Defines a sub-command.
  /// @param name The name of the command.
  /// @param desc The description of the command.
  command& cmd(const std::string& name, std::string desc = "");

  /// Parses command line arguments and dispatches the contained command to the
  /// registered sub-command.
  /// @param args The command line arguments.
  void dispatch(const std::vector<std::string>& args) const;

  /// Retrieves an option value.
  /// @param x The name of the option.
  /// @returns The value for *x* or `nullptr` if `x` is not a valid option.
  const data* get(const std::string& x) const;

  std::string description;
  detail::steady_map<std::string, option> options;

  /// Returns the full name for this command.
  std::string full_name();

  /// Queries whether this command has no parent.
  bool is_root() const noexcept;

  inline const std::string_view& name() const noexcept {
    return name_;
  }

  template <class T, class... Ts>
  T* add(std::string_view name, Ts&&... xs) {
    auto ptr = std::make_unique<T>(this, name, std::forward<Ts>(xs)...);
    auto result = ptr.get();
    if (!nested_.emplace(name, std::move(ptr)).second) {
      throw std::invalid_argument("name already exists");
    }
    return result;
  }

  template <class T>
  caf::optional<T> get(const opt_map& xs, const std::string& name) {
    // Map T to the clostest type in config_value.
    using cfg_type =
      typename std::conditional<
        std::is_integral<T>::value && !std::is_same<bool, T>::value,
        int64_t,
        typename std::conditional<
          std::is_floating_point<T>::value,
          double,
          T
          >::type
        >::type;
    auto i = xs.find(name);
    if (i == xs.end())
      return caf::none;
    auto result = caf::get_if<cfg_type>(&i->second);
    if (!result)
      return caf::none;
    return static_cast<T>(*result);
  }

  template <class T>
  T get_or(const opt_map& xs, const std::string& name, T fallback) {
    auto result = get<T>(xs, name);
    if (!result)
      return fallback;
    return *result;
  }

protected:
  /// Checks whether a command is ready to proceed, i.e., whether the
  /// configuration allows for calling `run_impl` or `run` on a nested command.
  virtual proceed_result proceed(caf::actor_system& sys, opt_map& options,
                                 caf::message args);

  virtual int run_impl(caf::actor_system& sys, opt_map& options,
                       caf::message args);

  template <class T>
  void add_opt(std::string name, std::string descr, T& ref) {
    opts_.emplace_back(name, std::move(descr), ref);
    // Extract the long name from the full name (format: "long,l").
    auto pos = name.find_first_of(',');
    if (pos < name.size())
      name.resize(pos);
    get_opts_.emplace_back([name = std::move(name), &ref] {
      // Map T to the clostest type in config_value.
      using cfg_type =
        typename std::conditional<
          std::is_integral<T>::value && !std::is_same<bool, T>::value,
          int64_t,
          typename std::conditional<
            std::is_floating_point<T>::value,
            double,
            T
            >::type
          >::type;
      cfg_type copy = ref;
      return std::make_pair(name, caf::config_value{std::move(copy)});
    });
  }

private:
  /// Separates arguments into the arguments for the current command, the name
  /// of the subcommand, and the arguments for the subcommand.
  std::tuple<caf::message, std::string, caf::message>
  separate_args(const caf::message& args);

  caf::detail::unordered_flat_map<std::string_view, unique_ptr> nested_;
  command* parent_;
  std::string_view name_;
  std::vector<caf::message::cli_arg> opts_;
  std::vector<get_opt> get_opts_;
};

} // namespace vast

#endif
<commit_msg>Add command::root convenience function<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#ifndef VAST_COMMAND_HPP
#define VAST_COMMAND_HPP

#include <functional>
#include <memory>
#include <string>
#include <string_view>

#include <caf/actor_system_config.hpp>
#include <caf/fwd.hpp>
#include <caf/message.hpp>

#include <caf/detail/unordered_flat_map.hpp>

#include "vast/error.hpp"
#include "vast/data.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/data.hpp"

#include "vast/detail/steady_map.hpp"
#include "vast/detail/string.hpp"

namespace vast {

/// A top-level command.
class command {
public:
  // -- member types -----------------------------------------------------------

  /// An option of a command.
  struct option {
    template <class T = bool>
    static std::pair<std::string, option>
    make(const std::string& tag, std::string desc, T x = {}) {
      auto s = detail::split_to_str(tag, ",");
      std::string shortcut;
      if (s.size() >= 2)
        shortcut = s[1][0];
      return {s[0], {std::move(shortcut), std::move(desc),
                    data{std::forward<T>(x)}}};
    }

    std::string shortcut;
    std::string description;
    data value;
  };

  /// Owning pointer to a command.
  using unique_ptr = std::unique_ptr<command>;

  /// Group of configuration parameters.
  using opt_group = caf::actor_system_config::opt_group;

  /// Maps names of config parameters to their value.
  using opt_map = std::map<std::string, caf::config_value>;

  /// Returns a CLI option as name/value pair.
  using get_opt = std::function<std::pair<std::string, caf::config_value>()>;

  /// Wraps the result of proceed.
  enum proceed_result {
    proceed_ok,
    stop_successful,
    stop_with_error
  };

  // -- constructors, destructors, and assignment operators --------------------

  command();

  command(command* parent, std::string_view name);

  virtual ~command();

  /// Runs the command and blocks until execution completes.
  /// @returns An exit code suitable for returning from main.
  int run(caf::actor_system& sys, caf::message args);

  /// Runs the command and blocks until execution completes.
  /// @returns An exit code suitable for returning from main.
  int run(caf::actor_system& sys, opt_map& options, caf::message args);

  /// Prints usage to `std::cerr`.
  void usage();

  /// Defines a sub-command.
  /// @param name The name of the command.
  /// @param desc The description of the command.
  command& cmd(const std::string& name, std::string desc = "");

  /// Parses command line arguments and dispatches the contained command to the
  /// registered sub-command.
  /// @param args The command line arguments.
  void dispatch(const std::vector<std::string>& args) const;

  /// Retrieves an option value.
  /// @param x The name of the option.
  /// @returns The value for *x* or `nullptr` if `x` is not a valid option.
  const data* get(const std::string& x) const;

  std::string description;
  detail::steady_map<std::string, option> options;

  /// Returns the full name for this command.
  std::string full_name();

  /// Queries whether this command has no parent.
  bool is_root() const noexcept;

  /// Queries whether this command has no parent.
  command& root() noexcept {
    return is_root() ? *this : parent_->root();
  }

  inline const std::string_view& name() const noexcept {
    return name_;
  }

  template <class T, class... Ts>
  T* add(std::string_view name, Ts&&... xs) {
    auto ptr = std::make_unique<T>(this, name, std::forward<Ts>(xs)...);
    auto result = ptr.get();
    if (!nested_.emplace(name, std::move(ptr)).second) {
      throw std::invalid_argument("name already exists");
    }
    return result;
  }

  template <class T>
  caf::optional<T> get(const opt_map& xs, const std::string& name) {
    // Map T to the clostest type in config_value.
    using cfg_type =
      typename std::conditional<
        std::is_integral<T>::value && !std::is_same<bool, T>::value,
        int64_t,
        typename std::conditional<
          std::is_floating_point<T>::value,
          double,
          T
          >::type
        >::type;
    auto i = xs.find(name);
    if (i == xs.end())
      return caf::none;
    auto result = caf::get_if<cfg_type>(&i->second);
    if (!result)
      return caf::none;
    return static_cast<T>(*result);
  }

  template <class T>
  T get_or(const opt_map& xs, const std::string& name, T fallback) {
    auto result = get<T>(xs, name);
    if (!result)
      return fallback;
    return *result;
  }

protected:
  /// Checks whether a command is ready to proceed, i.e., whether the
  /// configuration allows for calling `run_impl` or `run` on a nested command.
  virtual proceed_result proceed(caf::actor_system& sys, opt_map& options,
                                 caf::message args);

  virtual int run_impl(caf::actor_system& sys, opt_map& options,
                       caf::message args);

  template <class T>
  void add_opt(std::string name, std::string descr, T& ref) {
    opts_.emplace_back(name, std::move(descr), ref);
    // Extract the long name from the full name (format: "long,l").
    auto pos = name.find_first_of(',');
    if (pos < name.size())
      name.resize(pos);
    get_opts_.emplace_back([name = std::move(name), &ref] {
      // Map T to the clostest type in config_value.
      using cfg_type =
        typename std::conditional<
          std::is_integral<T>::value && !std::is_same<bool, T>::value,
          int64_t,
          typename std::conditional<
            std::is_floating_point<T>::value,
            double,
            T
            >::type
          >::type;
      cfg_type copy = ref;
      return std::make_pair(name, caf::config_value{std::move(copy)});
    });
  }

private:
  /// Separates arguments into the arguments for the current command, the name
  /// of the subcommand, and the arguments for the subcommand.
  std::tuple<caf::message, std::string, caf::message>
  separate_args(const caf::message& args);

  caf::detail::unordered_flat_map<std::string_view, unique_ptr> nested_;
  command* parent_;
  std::string_view name_;
  std::vector<caf::message::cli_arg> opts_;
  std::vector<get_opt> get_opts_;
};

} // namespace vast

#endif
<|endoftext|>