<commit_before>/**
Copyright (c) 2016, Ubiquity Robotics
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 ubiquity_motor 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 HOLDER 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 AiiiiiiiiiiiNY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#include <boost/assign.hpp>
#include <ubiquity_motor/motor_hardware.h>
#include <ubiquity_motor/motor_message.h>

#include <boost/math/special_functions/round.hpp>

//#define SENSOR_DISTANCE 0.002478

// 60 tics per revolution of the motor (pre gearbox)
//17.2328767123
// gear ratio of 4.29411764706:1
#define TICS_PER_RADIAN (41.0058030317/2)
#define SECONDS_PER_VELOCITY_READ 10.0 //read = ticks / (100 ms), so we have scale of 10 for ticks/second
#define CURRENT_FIRMWARE_VERSION 18

MotorHardware::MotorHardware(ros::NodeHandle nh){
	ros::V_string joint_names = boost::assign::list_of("left_wheel_joint")("right_wheel_joint");

	for (unsigned int i = 0; i < joint_names.size(); i++) {
		hardware_interface::JointStateHandle joint_state_handle(joint_names[i],
		    &joints_[i].position, &joints_[i].velocity, &joints_[i].effort);
		joint_state_interface_.registerHandle(joint_state_handle);

		hardware_interface::JointHandle joint_handle(
		joint_state_handle, &joints_[i].velocity_command);
		velocity_joint_interface_.registerHandle(joint_handle);
	}
	registerInterface(&joint_state_interface_);
	registerInterface(&velocity_joint_interface_);

	std::string sPort;
	int sBaud;

	double sLoopRate;

	if (!nh.getParam("ubiquity_motor/serial_port", sPort))
	{
		sPort.assign("/dev/ttyS0");
		nh.setParam("ubiquity_motor/serial_port", sPort);
	}

	if (!nh.getParam("ubiquity_motor/serial_baud", sBaud))
	{
		sBaud = 9600;
		nh.setParam("ubiquity_motor/serial_baud", sBaud);
	}

	if (!nh.getParam("ubiquity_motor/serial_loop_rate", sLoopRate))
	{
		sLoopRate = 100;
		nh.setParam("ubiquity_motor/serial_loop_rate", sLoopRate);
	}

	motor_serial_ = new MotorSerial(sPort,sBaud,sLoopRate);
}

MotorHardware::~MotorHardware(){
	delete motor_serial_;
}

void MotorHardware::readInputs(){
	while(motor_serial_->commandAvailable()){
		MotorMessage mm;
		mm = motor_serial_-> receiveCommand();
		if(mm.getType() == MotorMessage::TYPE_RESPONSE){
			switch(mm.getRegister()){
				case MotorMessage::REG_FIRMWARE_VERSION:
					if (!mm.getData() > CURRENT_FIRMWARE_VERSION) {
						ROS_FATAL("Firmware version %d, expect %d or above",
							mm.getData(), CURRENT_FIRMWARE_VERSION);
					}
					else {
						ROS_INFO("Firmware version %d", mm.getData());
					}
					break;
				case MotorMessage::REG_LEFT_ODOM:
					joints_[0].position += mm.getData()/TICS_PER_RADIAN;
					break;
				case MotorMessage::REG_RIGHT_ODOM:
					joints_[1].position += mm.getData()/TICS_PER_RADIAN;
					break;
				case MotorMessage::REG_LEFT_SPEED_MEASURED:
					joints_[0].velocity = mm.getData()*SECONDS_PER_VELOCITY_READ/TICS_PER_RADIAN;
					break;
				case MotorMessage::REG_RIGHT_SPEED_MEASURED:
					joints_[1].velocity = mm.getData()*SECONDS_PER_VELOCITY_READ/TICS_PER_RADIAN;
					break;
				default:
					ROS_ERROR("Got type: 0x%02x, handling not implemented", mm.getRegister());
			}
		}
	}
}

void MotorHardware::writeSpeeds(){
	std::vector<MotorMessage> commands(6);
	//requestOdometry();
	//requestVelocity();
	//requestVersion();

	MotorMessage left_odom;
	left_odom.setRegister(MotorMessage::REG_LEFT_ODOM);
	left_odom.setType(MotorMessage::TYPE_READ);
	left_odom.setData(0);
	commands.push_back(left_odom);

	MotorMessage right_odom;
	right_odom.setRegister(MotorMessage::REG_RIGHT_ODOM);
	right_odom.setType(MotorMessage::TYPE_READ);
	right_odom.setData(0);
	commands.push_back(right_odom);

	MotorMessage left_vel;
	left_vel.setRegister(MotorMessage::REG_LEFT_SPEED_MEASURED);
	left_vel.setType(MotorMessage::TYPE_READ);
	left_vel.setData(0);
	commands.push_back(left_vel);

	MotorMessage right_vel;
	right_vel.setRegister(MotorMessage::REG_RIGHT_SPEED_MEASURED);
	right_vel.setType(MotorMessage::TYPE_READ);
	right_vel.setData(0);
	commands.push_back(right_vel);

	MotorMessage left;
	left.setRegister(MotorMessage::REG_LEFT_SPEED_SET);
	left.setType(MotorMessage::TYPE_WRITE);
	left.setData(boost::math::lround(joints_[0].velocity_command*TICS_PER_RADIAN/SECONDS_PER_VELOCITY_READ));
	commands.push_back(left);

	MotorMessage right;
	right.setRegister(MotorMessage::REG_RIGHT_SPEED_SET);
	right.setType(MotorMessage::TYPE_WRITE);
	right.setData(boost::math::lround(joints_[1].velocity_command*TICS_PER_RADIAN/SECONDS_PER_VELOCITY_READ));
	commands.push_back(right);

	//Send all commands to serial thread in one go to reduce locking
	motor_serial_->transmitCommands(commands);

	//ROS_ERROR("velocity_command %f rad/s %f rad/s", joints_[0].velocity_command, joints_[1].velocity_command);
	// ROS_ERROR("SPEEDS %d %d", left.getData(), right.getData());
}

void MotorHardware::requestVersion(){
    MotorMessage version;
	version.setRegister(MotorMessage::REG_FIRMWARE_VERSION);
	version.setType(MotorMessage::TYPE_READ);
	version.setData(0);
	motor_serial_->transmitCommand(version);

}

void MotorHardware::requestOdometry(){
	std::vector<MotorMessage> commands;
	_addOdometryRequest(commands);
	motor_serial_->transmitCommands(commands);
}

void MotorHardware::requestVelocity(){
	std::vector<MotorMessage> commands;
	_addVelocityRequest(commands);
	motor_serial_->transmitCommands(commands);
}

void MotorHardware::setPid(int32_t p_set, int32_t i_set, int32_t d_set, int32_t denominator_set){
	p_value = p_set;
	i_value = i_set;
	d_value = d_set;
	denominator_value = denominator_set;
}

void MotorHardware::sendPid() {
	std::vector<MotorMessage> commands(4);

	MotorMessage p;
	p.setRegister(MotorMessage::REG_PARAM_P);
	p.setType(MotorMessage::TYPE_WRITE);
	p.setData(p_value);
	commands.push_back(p);

	MotorMessage i;
	i.setRegister(MotorMessage::REG_PARAM_I);
	i.setType(MotorMessage::TYPE_WRITE);
	i.setData(i_value);
	commands.push_back(i);

	MotorMessage d;
	d.setRegister(MotorMessage::REG_PARAM_D);
	d.setType(MotorMessage::TYPE_WRITE);
	d.setData(d_value);
	commands.push_back(d);

	MotorMessage denominator;
	denominator.setRegister(MotorMessage::REG_PARAM_C);
	denominator.setType(MotorMessage::TYPE_WRITE);
	denominator.setData(denominator_value);
	commands.push_back(denominator);

	motor_serial_->transmitCommands(commands);
}

void MotorHardware::setDebugLeds(bool led_1, bool led_2) {
	std::vector<MotorMessage> commands(2);

	MotorMessage led1;
	led1.setRegister(MotorMessage::REG_LED_1);
	led1.setType(MotorMessage::TYPE_WRITE);
	if(led_1) {
		led1.setData(0x00000001);
	}
	else {
		led1.setData(0x00000000);
	}
	commands.push_back(led1);

	MotorMessage led2;
	led2.setRegister(MotorMessage::REG_LED_2);
	led2.setType(MotorMessage::TYPE_WRITE);
	if(led_2) {
		led2.setData(0x00000001);
	}
	else {
		led2.setData(0x00000000);
	}
	commands.push_back(led2);

	motor_serial_->transmitCommands(commands);
}

void MotorHardware::_addOdometryRequest(std::vector<MotorMessage>& commands) const{
	MotorMessage left_odom;
	left_odom.setRegister(MotorMessage::REG_LEFT_ODOM);
	left_odom.setType(MotorMessage::TYPE_READ);
	left_odom.setData(0);
	commands.push_back(left_odom);

	MotorMessage right_odom;
	right_odom.setRegister(MotorMessage::REG_RIGHT_ODOM);
	right_odom.setType(MotorMessage::TYPE_READ);
	right_odom.setData(0);
	commands.push_back(right_odom);
}

void MotorHardware::_addVelocityRequest(std::vector<MotorMessage>& commands) const{
	MotorMessage left_vel;
	left_vel.setRegister(MotorMessage::REG_LEFT_SPEED_MEASURED);
	left_vel.setType(MotorMessage::TYPE_READ);
	left_vel.setData(0);
	commands.push_back(left_vel);

	MotorMessage right_vel;
	right_vel.setRegister(MotorMessage::REG_RIGHT_SPEED_MEASURED);
	right_vel.setType(MotorMessage::TYPE_READ);
	right_vel.setData(0);
	commands.push_back(right_vel);
}<commit_msg>use add request functions instead of duplicated code<commit_after>/**
Copyright (c) 2016, Ubiquity Robotics
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 ubiquity_motor 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 HOLDER 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 AiiiiiiiiiiiNY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#include <boost/assign.hpp>
#include <ubiquity_motor/motor_hardware.h>
#include <ubiquity_motor/motor_message.h>

#include <boost/math/special_functions/round.hpp>

//#define SENSOR_DISTANCE 0.002478

// 60 tics per revolution of the motor (pre gearbox)
//17.2328767123
// gear ratio of 4.29411764706:1
#define TICS_PER_RADIAN (41.0058030317/2)
#define SECONDS_PER_VELOCITY_READ 10.0 //read = ticks / (100 ms), so we have scale of 10 for ticks/second
#define CURRENT_FIRMWARE_VERSION 18

MotorHardware::MotorHardware(ros::NodeHandle nh){
	ros::V_string joint_names = boost::assign::list_of("left_wheel_joint")("right_wheel_joint");

	for (unsigned int i = 0; i < joint_names.size(); i++) {
		hardware_interface::JointStateHandle joint_state_handle(joint_names[i],
		    &joints_[i].position, &joints_[i].velocity, &joints_[i].effort);
		joint_state_interface_.registerHandle(joint_state_handle);

		hardware_interface::JointHandle joint_handle(
		joint_state_handle, &joints_[i].velocity_command);
		velocity_joint_interface_.registerHandle(joint_handle);
	}
	registerInterface(&joint_state_interface_);
	registerInterface(&velocity_joint_interface_);

	std::string sPort;
	int sBaud;

	double sLoopRate;

	if (!nh.getParam("ubiquity_motor/serial_port", sPort))
	{
		sPort.assign("/dev/ttyS0");
		nh.setParam("ubiquity_motor/serial_port", sPort);
	}

	if (!nh.getParam("ubiquity_motor/serial_baud", sBaud))
	{
		sBaud = 9600;
		nh.setParam("ubiquity_motor/serial_baud", sBaud);
	}

	if (!nh.getParam("ubiquity_motor/serial_loop_rate", sLoopRate))
	{
		sLoopRate = 100;
		nh.setParam("ubiquity_motor/serial_loop_rate", sLoopRate);
	}

	motor_serial_ = new MotorSerial(sPort,sBaud,sLoopRate);
}

MotorHardware::~MotorHardware(){
	delete motor_serial_;
}

void MotorHardware::readInputs(){
	while(motor_serial_->commandAvailable()){
		MotorMessage mm;
		mm = motor_serial_-> receiveCommand();
		if(mm.getType() == MotorMessage::TYPE_RESPONSE){
			switch(mm.getRegister()){
				case MotorMessage::REG_FIRMWARE_VERSION:
					if (!mm.getData() > CURRENT_FIRMWARE_VERSION) {
						ROS_FATAL("Firmware version %d, expect %d or above",
							mm.getData(), CURRENT_FIRMWARE_VERSION);
					}
					else {
						ROS_INFO("Firmware version %d", mm.getData());
					}
					break;
				case MotorMessage::REG_LEFT_ODOM:
					joints_[0].position += mm.getData()/TICS_PER_RADIAN;
					break;
				case MotorMessage::REG_RIGHT_ODOM:
					joints_[1].position += mm.getData()/TICS_PER_RADIAN;
					break;
				case MotorMessage::REG_LEFT_SPEED_MEASURED:
					joints_[0].velocity = mm.getData()*SECONDS_PER_VELOCITY_READ/TICS_PER_RADIAN;
					break;
				case MotorMessage::REG_RIGHT_SPEED_MEASURED:
					joints_[1].velocity = mm.getData()*SECONDS_PER_VELOCITY_READ/TICS_PER_RADIAN;
					break;
				default:
					ROS_ERROR("Got type: 0x%02x, handling not implemented", mm.getRegister());
			}
		}
	}
}

void MotorHardware::writeSpeeds(){
	std::vector<MotorMessage> commands(6);
	//requestOdometry();
	//requestVelocity();
	//requestVersion();

	_addOdometryRequest(commands);
	_addVelocityRequest(commands);

	MotorMessage left;
	left.setRegister(MotorMessage::REG_LEFT_SPEED_SET);
	left.setType(MotorMessage::TYPE_WRITE);
	left.setData(boost::math::lround(joints_[0].velocity_command*TICS_PER_RADIAN/SECONDS_PER_VELOCITY_READ));
	commands.push_back(left);

	MotorMessage right;
	right.setRegister(MotorMessage::REG_RIGHT_SPEED_SET);
	right.setType(MotorMessage::TYPE_WRITE);
	right.setData(boost::math::lround(joints_[1].velocity_command*TICS_PER_RADIAN/SECONDS_PER_VELOCITY_READ));
	commands.push_back(right);

	//Send all commands to serial thread in one go to reduce locking
	motor_serial_->transmitCommands(commands);

	//ROS_ERROR("velocity_command %f rad/s %f rad/s", joints_[0].velocity_command, joints_[1].velocity_command);
	// ROS_ERROR("SPEEDS %d %d", left.getData(), right.getData());
}

void MotorHardware::requestVersion(){
    MotorMessage version;
	version.setRegister(MotorMessage::REG_FIRMWARE_VERSION);
	version.setType(MotorMessage::TYPE_READ);
	version.setData(0);
	motor_serial_->transmitCommand(version);

}

void MotorHardware::requestOdometry(){
	std::vector<MotorMessage> commands;
	_addOdometryRequest(commands);
	motor_serial_->transmitCommands(commands);
}

void MotorHardware::requestVelocity(){
	std::vector<MotorMessage> commands;
	_addVelocityRequest(commands);
	motor_serial_->transmitCommands(commands);
}

void MotorHardware::setPid(int32_t p_set, int32_t i_set, int32_t d_set, int32_t denominator_set){
	p_value = p_set;
	i_value = i_set;
	d_value = d_set;
	denominator_value = denominator_set;
}

void MotorHardware::sendPid() {
	std::vector<MotorMessage> commands(4);

	MotorMessage p;
	p.setRegister(MotorMessage::REG_PARAM_P);
	p.setType(MotorMessage::TYPE_WRITE);
	p.setData(p_value);
	commands.push_back(p);

	MotorMessage i;
	i.setRegister(MotorMessage::REG_PARAM_I);
	i.setType(MotorMessage::TYPE_WRITE);
	i.setData(i_value);
	commands.push_back(i);

	MotorMessage d;
	d.setRegister(MotorMessage::REG_PARAM_D);
	d.setType(MotorMessage::TYPE_WRITE);
	d.setData(d_value);
	commands.push_back(d);

	MotorMessage denominator;
	denominator.setRegister(MotorMessage::REG_PARAM_C);
	denominator.setType(MotorMessage::TYPE_WRITE);
	denominator.setData(denominator_value);
	commands.push_back(denominator);

	motor_serial_->transmitCommands(commands);
}

void MotorHardware::setDebugLeds(bool led_1, bool led_2) {
	std::vector<MotorMessage> commands(2);

	MotorMessage led1;
	led1.setRegister(MotorMessage::REG_LED_1);
	led1.setType(MotorMessage::TYPE_WRITE);
	if(led_1) {
		led1.setData(0x00000001);
	}
	else {
		led1.setData(0x00000000);
	}
	commands.push_back(led1);

	MotorMessage led2;
	led2.setRegister(MotorMessage::REG_LED_2);
	led2.setType(MotorMessage::TYPE_WRITE);
	if(led_2) {
		led2.setData(0x00000001);
	}
	else {
		led2.setData(0x00000000);
	}
	commands.push_back(led2);

	motor_serial_->transmitCommands(commands);
}

void MotorHardware::_addOdometryRequest(std::vector<MotorMessage>& commands) const{
	MotorMessage left_odom;
	left_odom.setRegister(MotorMessage::REG_LEFT_ODOM);
	left_odom.setType(MotorMessage::TYPE_READ);
	left_odom.setData(0);
	commands.push_back(left_odom);

	MotorMessage right_odom;
	right_odom.setRegister(MotorMessage::REG_RIGHT_ODOM);
	right_odom.setType(MotorMessage::TYPE_READ);
	right_odom.setData(0);
	commands.push_back(right_odom);
}

void MotorHardware::_addVelocityRequest(std::vector<MotorMessage>& commands) const{
	MotorMessage left_vel;
	left_vel.setRegister(MotorMessage::REG_LEFT_SPEED_MEASURED);
	left_vel.setType(MotorMessage::TYPE_READ);
	left_vel.setData(0);
	commands.push_back(left_vel);

	MotorMessage right_vel;
	right_vel.setRegister(MotorMessage::REG_RIGHT_SPEED_MEASURED);
	right_vel.setType(MotorMessage::TYPE_READ);
	right_vel.setData(0);
	commands.push_back(right_vel);
}<|endoftext|>


<commit_before>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 Google Inc. 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.

#include <string>
#include <memory>
#include <vector>

#include <google/protobuf/unittest_proto3.pb.h>
#include <google/protobuf/arena.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>

namespace google {
using proto3_unittest::TestAllTypes;

namespace protobuf {
namespace {
// We selectively set/check a few representative fields rather than all fields
// as this test is only expected to cover the basics of lite support.
void SetAllFields(TestAllTypes* m) {
  m->set_optional_int32(100);
  m->set_optional_string("asdf");
  m->set_optional_bytes("jkl;");
  m->mutable_optional_nested_message()->set_bb(42);
  m->mutable_optional_foreign_message()->set_c(43);
  m->set_optional_nested_enum(
      proto3_unittest::TestAllTypes_NestedEnum_BAZ);
  m->set_optional_foreign_enum(
      proto3_unittest::FOREIGN_BAZ);
  m->mutable_optional_lazy_message()->set_bb(45);
  m->add_repeated_int32(100);
  m->add_repeated_string("asdf");
  m->add_repeated_bytes("jkl;");
  m->add_repeated_nested_message()->set_bb(46);
  m->add_repeated_foreign_message()->set_c(47);
  m->add_repeated_nested_enum(
      proto3_unittest::TestAllTypes_NestedEnum_BAZ);
  m->add_repeated_foreign_enum(
      proto3_unittest::FOREIGN_BAZ);
  m->add_repeated_lazy_message()->set_bb(49);

  m->set_oneof_uint32(1);
  m->mutable_oneof_nested_message()->set_bb(50);
  m->set_oneof_string("test");  // only this one remains set
}

void ExpectAllFieldsSet(const TestAllTypes& m) {
  EXPECT_EQ(100, m.optional_int32());
  EXPECT_EQ("asdf", m.optional_string());
  EXPECT_EQ("jkl;", m.optional_bytes());
  EXPECT_EQ(true, m.has_optional_nested_message());
  EXPECT_EQ(42, m.optional_nested_message().bb());
  EXPECT_EQ(true, m.has_optional_foreign_message());
  EXPECT_EQ(43, m.optional_foreign_message().c());
  EXPECT_EQ(proto3_unittest::TestAllTypes_NestedEnum_BAZ,
            m.optional_nested_enum());
  EXPECT_EQ(proto3_unittest::FOREIGN_BAZ,
            m.optional_foreign_enum());
  EXPECT_EQ(true, m.has_optional_lazy_message());
  EXPECT_EQ(45, m.optional_lazy_message().bb());

  EXPECT_EQ(1, m.repeated_int32_size());
  EXPECT_EQ(100, m.repeated_int32(0));
  EXPECT_EQ(1, m.repeated_string_size());
  EXPECT_EQ("asdf", m.repeated_string(0));
  EXPECT_EQ(1, m.repeated_bytes_size());
  EXPECT_EQ("jkl;", m.repeated_bytes(0));
  EXPECT_EQ(1, m.repeated_nested_message_size());
  EXPECT_EQ(46, m.repeated_nested_message(0).bb());
  EXPECT_EQ(1, m.repeated_foreign_message_size());
  EXPECT_EQ(47, m.repeated_foreign_message(0).c());
  EXPECT_EQ(1, m.repeated_nested_enum_size());
  EXPECT_EQ(proto3_unittest::TestAllTypes_NestedEnum_BAZ,
            m.repeated_nested_enum(0));
  EXPECT_EQ(1, m.repeated_foreign_enum_size());
  EXPECT_EQ(proto3_unittest::FOREIGN_BAZ,
            m.repeated_foreign_enum(0));
  EXPECT_EQ(1, m.repeated_lazy_message_size());
  EXPECT_EQ(49, m.repeated_lazy_message(0).bb());

  EXPECT_EQ(proto3_unittest::TestAllTypes::kOneofString,
            m.oneof_field_case());
  EXPECT_EQ("test", m.oneof_string());
}

// In this file we only test some basic functionalities of in proto3 and expect
// the rest is fully tested in proto2 unittests because proto3 shares most code
// with proto2.

TEST(Proto3LiteTest, Parsing) {
  TestAllTypes original;
  SetAllFields(&original);

  TestAllTypes msg;
  msg.ParseFromString(original.SerializeAsString());
  ExpectAllFieldsSet(msg);
}

TEST(Proto3LiteTest, Swap) {
  // Test Swap().
  TestAllTypes msg1;
  TestAllTypes msg2;
  msg1.set_optional_string("123");
  msg2.set_optional_string("3456");
  msg1.Swap(&msg2);
  EXPECT_EQ("3456", msg1.optional_string());
  EXPECT_EQ("123", msg2.optional_string());
  EXPECT_EQ(msg1.ByteSize(), msg2.ByteSize() + 1);
}

}  // namespace
}  // namespace protobuf
}  // namespace google
<commit_msg>Fixed up proto3_lite_unittest.cc<commit_after>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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 Google Inc. 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.

#include <string>
#include <memory>
#include <vector>

#include <google/protobuf/unittest_proto3_lite.pb.h>
#include <google/protobuf/arena.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>

namespace google {
using proto3_lite_unittest::TestAllTypes;

namespace protobuf {
namespace {
// We selectively set/check a few representative fields rather than all fields
// as this test is only expected to cover the basics of lite support.
void SetAllFields(TestAllTypes* m) {
  m->set_optional_int32(100);
  m->set_optional_string("asdf");
  m->set_optional_bytes("jkl;");
  m->mutable_optional_nested_message()->set_bb(42);
  m->mutable_optional_foreign_message()->set_c(43);
  m->set_optional_nested_enum(
      proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ);
  m->set_optional_foreign_enum(
      proto3_lite_unittest::FOREIGN_BAZ);
  m->mutable_optional_lazy_message()->set_bb(45);
  m->add_repeated_int32(100);
  m->add_repeated_string("asdf");
  m->add_repeated_bytes("jkl;");
  m->add_repeated_nested_message()->set_bb(46);
  m->add_repeated_foreign_message()->set_c(47);
  m->add_repeated_nested_enum(
      proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ);
  m->add_repeated_foreign_enum(
      proto3_lite_unittest::FOREIGN_BAZ);
  m->add_repeated_lazy_message()->set_bb(49);

  m->set_oneof_uint32(1);
  m->mutable_oneof_nested_message()->set_bb(50);
  m->set_oneof_string("test");  // only this one remains set
}

void ExpectAllFieldsSet(const TestAllTypes& m) {
  EXPECT_EQ(100, m.optional_int32());
  EXPECT_EQ("asdf", m.optional_string());
  EXPECT_EQ("jkl;", m.optional_bytes());
  EXPECT_EQ(true, m.has_optional_nested_message());
  EXPECT_EQ(42, m.optional_nested_message().bb());
  EXPECT_EQ(true, m.has_optional_foreign_message());
  EXPECT_EQ(43, m.optional_foreign_message().c());
  EXPECT_EQ(proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ,
            m.optional_nested_enum());
  EXPECT_EQ(proto3_lite_unittest::FOREIGN_BAZ,
            m.optional_foreign_enum());
  EXPECT_EQ(true, m.has_optional_lazy_message());
  EXPECT_EQ(45, m.optional_lazy_message().bb());

  EXPECT_EQ(1, m.repeated_int32_size());
  EXPECT_EQ(100, m.repeated_int32(0));
  EXPECT_EQ(1, m.repeated_string_size());
  EXPECT_EQ("asdf", m.repeated_string(0));
  EXPECT_EQ(1, m.repeated_bytes_size());
  EXPECT_EQ("jkl;", m.repeated_bytes(0));
  EXPECT_EQ(1, m.repeated_nested_message_size());
  EXPECT_EQ(46, m.repeated_nested_message(0).bb());
  EXPECT_EQ(1, m.repeated_foreign_message_size());
  EXPECT_EQ(47, m.repeated_foreign_message(0).c());
  EXPECT_EQ(1, m.repeated_nested_enum_size());
  EXPECT_EQ(proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ,
            m.repeated_nested_enum(0));
  EXPECT_EQ(1, m.repeated_foreign_enum_size());
  EXPECT_EQ(proto3_lite_unittest::FOREIGN_BAZ,
            m.repeated_foreign_enum(0));
  EXPECT_EQ(1, m.repeated_lazy_message_size());
  EXPECT_EQ(49, m.repeated_lazy_message(0).bb());

  EXPECT_EQ(proto3_lite_unittest::TestAllTypes::kOneofString,
            m.oneof_field_case());
  EXPECT_EQ("test", m.oneof_string());
}

// In this file we only test some basic functionalities of in proto3 and expect
// the rest is fully tested in proto2 unittests because proto3 shares most code
// with proto2.

TEST(Proto3LiteTest, Parsing) {
  TestAllTypes original;
  SetAllFields(&original);

  TestAllTypes msg;
  msg.ParseFromString(original.SerializeAsString());
  ExpectAllFieldsSet(msg);
}

TEST(Proto3LiteTest, Swap) {
  // Test Swap().
  TestAllTypes msg1;
  TestAllTypes msg2;
  msg1.set_optional_string("123");
  msg2.set_optional_string("3456");
  msg1.Swap(&msg2);
  EXPECT_EQ("3456", msg1.optional_string());
  EXPECT_EQ("123", msg2.optional_string());
  EXPECT_EQ(msg1.ByteSize(), msg2.ByteSize() + 1);
}

}  // namespace
}  // namespace protobuf
}  // namespace google
<|endoftext|>


<commit_before>#include "selected.h"

namespace UnTech {
namespace MetaSprite {

template <class BaseControllerT>
SelectedController<BaseControllerT>::SelectedController(BaseControllerT& controller)
    : _controller(controller)
    , _type(SelectedType::NONE)
{
    _controller.frameController().signal_mapChanged().connect(
        [this](void) {
            if (_type == SelectedType::FRAME) {
                _signal_listChanged.emit();
            }
        });
    _controller.frameObjectController().signal_listChanged().connect(
        [this](void) {
            if (_type == SelectedType::FRAME_OBJECT) {
                _signal_listChanged.emit();
            }
        });
    _controller.actionPointController().signal_listChanged().connect(
        [this](void) {
            if (_type == SelectedType::ACTION_POINT) {
                _signal_listChanged.emit();
            }
        });
    _controller.entityHitboxController().signal_listChanged().connect(
        [this](void) {
            if (_type == SelectedType::ENTITY_HITBOX) {
                _signal_listChanged.emit();
            }
        });

    _controller.settingsController().layers().signal_layersChanged().connect(sigc::mem_fun(
        *this, &SelectedController::selectNone));

    _controller.frameObjectController().signal_selectedChanged().connect(
        [this](void) {
            if (_controller.frameObjectController().hasSelected()) {
                _type = SelectedType::FRAME_OBJECT;
                _signal_selectedChanged.emit();
            }
            else {
                selectFrame();
            }
        });

    _controller.actionPointController().signal_selectedChanged().connect(
        [this](void) {
            if (_controller.actionPointController().hasSelected()) {
                _type = SelectedType::ACTION_POINT;
                _signal_selectedChanged.emit();
            }
            else {
                selectFrame();
            }
        });

    _controller.entityHitboxController().signal_selectedChanged().connect(
        [this](void) {
            if (_controller.entityHitboxController().hasSelected()) {
                _type = SelectedType::ENTITY_HITBOX;
                _signal_selectedChanged.emit();
            }
            else {
                selectFrame();
            }
        });
}

template <class T>
std::pair<SelectedType, size_t> SelectedController<T>::typeAndIndex() const
{
    size_t id = 0;

    switch (_type) {
    case SelectedType::NONE:
    case SelectedType::FRAME:
    case SelectedType::TILE_HITBOX:
        break;

    case SelectedType::FRAME_OBJECT:
        id = _controller.frameObjectController().selectedIndex().value();
        break;

    case SelectedType::ACTION_POINT:
        id = _controller.actionPointController().selectedIndex().value();
        break;

    case SelectedType::ENTITY_HITBOX:
        id = _controller.entityHitboxController().selectedIndex().value();
        break;
    }

    return { _type, id };
}

template <class T>
void SelectedController<T>::selectNone()
{
    _controller.frameController().selectNone();
}

template <class T>
void SelectedController<T>::selectFrame()
{
    if (_controller.frameController().hasSelected()) {
        if (_type != SelectedType::FRAME) {
            _type = SelectedType::FRAME;
            _signal_selectedChanged.emit();
        }
    }
    else {
        selectNone();
    }
}

template <class T>
void SelectedController<T>::selectTileHitbox()
{
    if (_controller.frameController().hasSelected()) {
        if (_type != SelectedType::TILE_HITBOX) {
            _type = SelectedType::TILE_HITBOX;
            _signal_selectedChanged.emit();
        }
    }
    else {
        selectNone();
    }
}

template <class T>
void SelectedController<T>::selectFrameItem(const idstring& frameId, SelectedType type, size_t index)
{
    if (_controller.frameController().selectedId() != frameId) {
        _controller.frameController().selectId(frameId);
    }

    if (_controller.frameController().hasSelected()) {
        switch (type) {
        case SelectedType::NONE:
        case SelectedType::FRAME:
            selectFrame();
            break;

        case SelectedType::TILE_HITBOX:
            break;

        case SelectedType::FRAME_OBJECT:
            _controller.frameObjectController().selectIndex(index);
            break;

        case SelectedType::ACTION_POINT:
            _controller.actionPointController().selectIndex(index);
            break;

        case SelectedType::ENTITY_HITBOX:
            _controller.entityHitboxController().selectIndex(index);
            break;
        }
    }
}

template <class T>
const std::string& SelectedController<T>::typeString() const
{
    const static std::string nullString;
    const static std::string tileHitboxString = "Tile Hitbox";

    switch (_type) {
    case SelectedType::NONE:
        return nullString;

    case SelectedType::FRAME:
        return _controller.frameController().HUMAN_TYPE_NAME;

    case SelectedType::TILE_HITBOX:
        return tileHitboxString;

    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().HUMAN_TYPE_NAME;

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().HUMAN_TYPE_NAME;

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().HUMAN_TYPE_NAME;
    }
}

template <class T>
bool SelectedController<T>::canCreateSelected() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canCreate();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canCreate();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canCreate();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canCloneSelected() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canCloneSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canCloneSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canCloneSelected();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canRemoveSelected() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canRemoveSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canRemoveSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canRemoveSelected();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canMoveSelectedUp() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canMoveSelectedUp();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canMoveSelectedUp();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canMoveSelectedUp();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canMoveSelectedDown() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canMoveSelectedDown();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canMoveSelectedDown();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canMoveSelectedDown();

    default:
        return false;
    }
}

template <class T>
void SelectedController<T>::createNewOfSelectedType()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().create();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().create();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().create();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::cloneSelected()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().cloneSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().cloneSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().cloneSelected();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::removeSelected()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().removeSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().removeSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().removeSelected();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::moveSelectedUp()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().moveSelectedUp();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().moveSelectedUp();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().moveSelectedUp();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::moveSelectedDown()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().moveSelectedDown();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().moveSelectedDown();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().moveSelectedDown();

    default:
        return;
    }
}
}
}
<commit_msg>fix: Deselect TILE_HITBOX if the frame is not solid<commit_after>#include "selected.h"

namespace UnTech {
namespace MetaSprite {

template <class BaseControllerT>
SelectedController<BaseControllerT>::SelectedController(BaseControllerT& controller)
    : _controller(controller)
    , _type(SelectedType::NONE)
{
    _controller.frameController().signal_dataChanged().connect(
        [this](void) {
            // deselect TILE_HITBOX is frame is not solid
            if (_type == SelectedType::TILE_HITBOX) {
                auto& f = _controller.frameController().selected();
                if (f.solid == false) {
                    selectFrame();
                }
            }
        });

    _controller.frameController().signal_mapChanged().connect(
        [this](void) {
            if (_type == SelectedType::FRAME || _type == SelectedType::TILE_HITBOX) {
                _signal_listChanged.emit();
            }
        });
    _controller.frameObjectController().signal_listChanged().connect(
        [this](void) {
            if (_type == SelectedType::FRAME_OBJECT) {
                _signal_listChanged.emit();
            }
        });
    _controller.actionPointController().signal_listChanged().connect(
        [this](void) {
            if (_type == SelectedType::ACTION_POINT) {
                _signal_listChanged.emit();
            }
        });
    _controller.entityHitboxController().signal_listChanged().connect(
        [this](void) {
            if (_type == SelectedType::ENTITY_HITBOX) {
                _signal_listChanged.emit();
            }
        });

    _controller.settingsController().layers().signal_layersChanged().connect(sigc::mem_fun(
        *this, &SelectedController::selectNone));

    _controller.frameObjectController().signal_selectedChanged().connect(
        [this](void) {
            if (_controller.frameObjectController().hasSelected()) {
                _type = SelectedType::FRAME_OBJECT;
                _signal_selectedChanged.emit();
            }
            else {
                selectFrame();
            }
        });

    _controller.actionPointController().signal_selectedChanged().connect(
        [this](void) {
            if (_controller.actionPointController().hasSelected()) {
                _type = SelectedType::ACTION_POINT;
                _signal_selectedChanged.emit();
            }
            else {
                selectFrame();
            }
        });

    _controller.entityHitboxController().signal_selectedChanged().connect(
        [this](void) {
            if (_controller.entityHitboxController().hasSelected()) {
                _type = SelectedType::ENTITY_HITBOX;
                _signal_selectedChanged.emit();
            }
            else {
                selectFrame();
            }
        });
}

template <class T>
std::pair<SelectedType, size_t> SelectedController<T>::typeAndIndex() const
{
    size_t id = 0;

    switch (_type) {
    case SelectedType::NONE:
    case SelectedType::FRAME:
    case SelectedType::TILE_HITBOX:
        break;

    case SelectedType::FRAME_OBJECT:
        id = _controller.frameObjectController().selectedIndex().value();
        break;

    case SelectedType::ACTION_POINT:
        id = _controller.actionPointController().selectedIndex().value();
        break;

    case SelectedType::ENTITY_HITBOX:
        id = _controller.entityHitboxController().selectedIndex().value();
        break;
    }

    return { _type, id };
}

template <class T>
void SelectedController<T>::selectNone()
{
    _controller.frameController().selectNone();
}

template <class T>
void SelectedController<T>::selectFrame()
{
    if (_controller.frameController().hasSelected()) {
        if (_type != SelectedType::FRAME) {
            _type = SelectedType::FRAME;
            _signal_selectedChanged.emit();
        }
    }
    else {
        selectNone();
    }
}

template <class T>
void SelectedController<T>::selectTileHitbox()
{
    if (_controller.frameController().selected().solid) {
        if (_type != SelectedType::TILE_HITBOX) {
            _type = SelectedType::TILE_HITBOX;
            _signal_selectedChanged.emit();
        }
    }
    else {
        selectFrame();
    }
}

template <class T>
void SelectedController<T>::selectFrameItem(const idstring& frameId, SelectedType type, size_t index)
{
    if (_controller.frameController().selectedId() != frameId) {
        _controller.frameController().selectId(frameId);
    }

    if (_controller.frameController().hasSelected()) {
        switch (type) {
        case SelectedType::NONE:
        case SelectedType::FRAME:
            selectFrame();
            break;

        case SelectedType::TILE_HITBOX:
            break;

        case SelectedType::FRAME_OBJECT:
            _controller.frameObjectController().selectIndex(index);
            break;

        case SelectedType::ACTION_POINT:
            _controller.actionPointController().selectIndex(index);
            break;

        case SelectedType::ENTITY_HITBOX:
            _controller.entityHitboxController().selectIndex(index);
            break;
        }
    }
}

template <class T>
const std::string& SelectedController<T>::typeString() const
{
    const static std::string nullString;
    const static std::string tileHitboxString = "Tile Hitbox";

    switch (_type) {
    case SelectedType::NONE:
        return nullString;

    case SelectedType::FRAME:
        return _controller.frameController().HUMAN_TYPE_NAME;

    case SelectedType::TILE_HITBOX:
        return tileHitboxString;

    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().HUMAN_TYPE_NAME;

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().HUMAN_TYPE_NAME;

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().HUMAN_TYPE_NAME;
    }
}

template <class T>
bool SelectedController<T>::canCreateSelected() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canCreate();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canCreate();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canCreate();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canCloneSelected() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canCloneSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canCloneSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canCloneSelected();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canRemoveSelected() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canRemoveSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canRemoveSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canRemoveSelected();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canMoveSelectedUp() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canMoveSelectedUp();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canMoveSelectedUp();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canMoveSelectedUp();

    default:
        return false;
    }
}

template <class T>
bool SelectedController<T>::canMoveSelectedDown() const
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().canMoveSelectedDown();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().canMoveSelectedDown();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().canMoveSelectedDown();

    default:
        return false;
    }
}

template <class T>
void SelectedController<T>::createNewOfSelectedType()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().create();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().create();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().create();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::cloneSelected()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().cloneSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().cloneSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().cloneSelected();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::removeSelected()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().removeSelected();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().removeSelected();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().removeSelected();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::moveSelectedUp()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().moveSelectedUp();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().moveSelectedUp();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().moveSelectedUp();

    default:
        return;
    }
}

template <class T>
void SelectedController<T>::moveSelectedDown()
{
    switch (_type) {
    case SelectedType::FRAME_OBJECT:
        return _controller.frameObjectController().moveSelectedDown();

    case SelectedType::ACTION_POINT:
        return _controller.actionPointController().moveSelectedDown();

    case SelectedType::ENTITY_HITBOX:
        return _controller.entityHitboxController().moveSelectedDown();

    default:
        return;
    }
}
}
}
<|endoftext|>


<commit_before>/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
#include "SpaghettiInfillPathGenerator.h"
#include "../infill.h"
#include "../FffGcodeWriter.h"

namespace cura {

bool SpaghettiInfillPathGenerator::processSpaghettiInfill(const SliceDataStorage& storage, const FffGcodeWriter& fff_gcode_writer, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const int extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
{
    if (extruder_nr != mesh.getSettingAsExtruderNr("infill_extruder_nr"))
    {
        return false;
    }
    bool added_something = false;
    const GCodePathConfig* config = mesh_config.getInfillConfig(layer_nr, 0);
    const EFillMethod pattern = mesh.getSettingAsFillMethod("infill_pattern");
    const unsigned int infill_line_width = config->getLineWidth();
    const int64_t z = layer_nr * mesh.getSettingInMicrons("layer_height");
    const int64_t infill_shift = 0;
    const int64_t outline_offset = 0;
    const double layer_height_mm = (layer_nr == 0)? mesh.getSettingInMillimeters("layer_height_0") : mesh.getSettingInMillimeters("layer_height");

    // For each part on this layer which is used to fill that part and parts below:
    for (const std::pair<Polygons, double>& filling_area : part.spaghetti_infill_volumes)
    {
        Polygons infill_lines;
        Polygons infill_polygons;

        const Polygons& area = filling_area.first; // Area of the top within which to move while extruding (might be empty if the spaghetti_inset was too large)
        const double total_volume = filling_area.second * mesh.getSettingAsRatio("spaghetti_flow") + mesh.getSettingInCubicMillimeters("spaghetti_infill_extra_volume"); // volume to be extruded
        if (total_volume <= 0.0)
        {
            continue;
        }

        // generate zigzag print head paths
        Polygons* perimeter_gaps_output = nullptr;
        const bool connected_zigzags = true;
        const bool use_endpieces = false;
        Infill infill_comp(pattern, area, outline_offset, infill_line_width, infill_line_distance, infill_overlap, infill_angle, z, infill_shift, perimeter_gaps_output, connected_zigzags, use_endpieces);
        infill_comp.generate(infill_polygons, infill_lines, &mesh);

        // add paths to plan with a higher flow ratio in order to extrude the required amount.
        const coord_t total_length = infill_polygons.polygonLength() + infill_lines.polyLineLength();
        if (total_length > 0)
        { // zigzag path generation actually generated paths
            // calculate the normal volume extruded when using the layer height and line width to calculate extrusion
            const double normal_volume = INT2MM(INT2MM(total_length * infill_line_width)) * layer_height_mm;
            assert(normal_volume > 0.0);
            const float flow_ratio = total_volume / normal_volume;
            assert(flow_ratio / mesh.getSettingAsRatio("spaghetti_flow") >= 0.9);
            assert(!std::isnan(flow_ratio) && !std::isinf(flow_ratio));

            if (!infill_polygons.empty() || !infill_lines.empty())
            {
                added_something = true;
                fff_gcode_writer.setExtruder_addPrime(storage, gcode_layer, layer_nr, extruder_nr);
                gcode_layer.addPolygonsByOptimizer(infill_polygons, config, nullptr, EZSeamType::SHORTEST, Point(0, 0), 0, false, flow_ratio);
                if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::CUBICSUBDIV)
                {
                    gcode_layer.addLinesByOptimizer(infill_lines, config, SpaceFillType::Lines, mesh.getSettingInMicrons("infill_wipe_dist"), flow_ratio);
                }
                else
                {
                    gcode_layer.addLinesByOptimizer(infill_lines, config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines, 0, flow_ratio);
                }
            }
        }
        else
        { // zigzag path generation couldn't generate paths, probably because the area was too small
            // generate small path near the middle of the filling area
            // note that we need a path with positive length because that is currently the only way to insert an extrusion in a layer plan
            constexpr int path_length = 10;
            Point middle = AABB(area).getMiddle();
            if (!area.inside(middle))
            {
                PolygonUtils::ensureInsideOrOutside(area, middle, infill_line_width / 2);
            }
            const double normal_volume = INT2MM(INT2MM(path_length * infill_line_width)) * layer_height_mm;
            const float flow_ratio = total_volume / normal_volume;
            gcode_layer.addTravel(middle);
            gcode_layer.addExtrusionMove(middle + Point(0, path_length), config, SpaceFillType::Lines, flow_ratio);
        }
    }
    return added_something;
}

}//namespace cura
<commit_msg>Spread out extra spaghetti infill volume proportionally<commit_after>/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
#include "SpaghettiInfillPathGenerator.h"
#include "../infill.h"
#include "../FffGcodeWriter.h"

namespace cura {

bool SpaghettiInfillPathGenerator::processSpaghettiInfill(const SliceDataStorage& storage, const FffGcodeWriter& fff_gcode_writer, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const int extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
{
    if (extruder_nr != mesh.getSettingAsExtruderNr("infill_extruder_nr"))
    {
        return false;
    }
    bool added_something = false;
    const GCodePathConfig* config = mesh_config.getInfillConfig(layer_nr, 0);
    const EFillMethod pattern = mesh.getSettingAsFillMethod("infill_pattern");
    const unsigned int infill_line_width = config->getLineWidth();
    const int64_t z = layer_nr * mesh.getSettingInMicrons("layer_height");
    const int64_t infill_shift = 0;
    const int64_t outline_offset = 0;
    const double layer_height_mm = (layer_nr == 0)? mesh.getSettingInMillimeters("layer_height_0") : mesh.getSettingInMillimeters("layer_height");
    const double default_spaghetti_infill_extra_volume = mesh.getSettingInCubicMillimeters("spaghetti_infill_extra_volume");

    // For each part on this layer which is used to fill that part and parts below:
    for (const std::pair<Polygons, double>& filling_area : part.spaghetti_infill_volumes)
    {
        Polygons infill_lines;
        Polygons infill_polygons;

        const Polygons& area = filling_area.first; // Area of the top within which to move while extruding (might be empty if the spaghetti_inset was too large)
        // the extra infill volume will be spread out proportionally to each step
        const double extra_infill_volume = filling_area.second / mesh.total_infill_volume_mm3 * default_spaghetti_infill_extra_volume;
        const double total_volume = filling_area.second * mesh.getSettingAsRatio("spaghetti_flow") + extra_infill_volume; // volume to be extruded
        if (total_volume <= 0.0)
        {
            continue;
        }

        // generate zigzag print head paths
        Polygons* perimeter_gaps_output = nullptr;
        const bool connected_zigzags = true;
        const bool use_endpieces = false;
        Infill infill_comp(pattern, area, outline_offset, infill_line_width, infill_line_distance, infill_overlap, infill_angle, z, infill_shift, perimeter_gaps_output, connected_zigzags, use_endpieces);
        infill_comp.generate(infill_polygons, infill_lines, &mesh);

        // add paths to plan with a higher flow ratio in order to extrude the required amount.
        const coord_t total_length = infill_polygons.polygonLength() + infill_lines.polyLineLength();
        if (total_length > 0)
        { // zigzag path generation actually generated paths
            // calculate the normal volume extruded when using the layer height and line width to calculate extrusion
            const double normal_volume = INT2MM(INT2MM(total_length * infill_line_width)) * layer_height_mm;
            assert(normal_volume > 0.0);
            const float flow_ratio = total_volume / normal_volume;
            assert(flow_ratio / mesh.getSettingAsRatio("spaghetti_flow") >= 0.9);
            assert(!std::isnan(flow_ratio) && !std::isinf(flow_ratio));

            if (!infill_polygons.empty() || !infill_lines.empty())
            {
                added_something = true;
                fff_gcode_writer.setExtruder_addPrime(storage, gcode_layer, layer_nr, extruder_nr);
                gcode_layer.addPolygonsByOptimizer(infill_polygons, config, nullptr, EZSeamType::SHORTEST, Point(0, 0), 0, false, flow_ratio);
                if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES || pattern == EFillMethod::CUBIC || pattern == EFillMethod::TETRAHEDRAL || pattern == EFillMethod::CUBICSUBDIV)
                {
                    gcode_layer.addLinesByOptimizer(infill_lines, config, SpaceFillType::Lines, mesh.getSettingInMicrons("infill_wipe_dist"), flow_ratio);
                }
                else
                {
                    gcode_layer.addLinesByOptimizer(infill_lines, config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines, 0, flow_ratio);
                }
            }
        }
        else
        { // zigzag path generation couldn't generate paths, probably because the area was too small
            // generate small path near the middle of the filling area
            // note that we need a path with positive length because that is currently the only way to insert an extrusion in a layer plan
            constexpr int path_length = 10;
            Point middle = AABB(area).getMiddle();
            if (!area.inside(middle))
            {
                PolygonUtils::ensureInsideOrOutside(area, middle, infill_line_width / 2);
            }
            const double normal_volume = INT2MM(INT2MM(path_length * infill_line_width)) * layer_height_mm;
            const float flow_ratio = total_volume / normal_volume;
            gcode_layer.addTravel(middle);
            gcode_layer.addExtrusionMove(middle + Point(0, path_length), config, SpaceFillType::Lines, flow_ratio);
        }
    }
    return added_something;
}

}//namespace cura
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017  Sergey Ilinykh
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 */

#include "irisnetplugin.h"

#include <QNetworkInterface>

namespace XMPP {

class IrisQtName : public NameProvider
{
	Q_OBJECT
	Q_INTERFACES(XMPP::NameProvider)

	int currentId;
	QHash<int,QDnsLookup*> lookups;

public:
	IrisQtName(QObject *parent = 0) :
	    NameProvider(parent),
	    currentId(0)
	{

	}

	bool supportsSingle() const
	{
		return true;
	}

	bool supportsRecordType(int type) const
	{
		// yes the types matched to ones from jdns, so it's fine.
		static QVector<int> types = {
		    QDnsLookup::A, QDnsLookup::AAAA, QDnsLookup::ANY,
		    QDnsLookup::CNAME, QDnsLookup::MX, QDnsLookup::NS,
		    QDnsLookup::PTR, QDnsLookup::SRV, QDnsLookup::TXT};
		return types.contains(type);
	}

	int resolve_start(const QByteArray &name, int qType, bool longLived)
	{
		Q_UNUSED(longLived); // FIXME handle local like in jdns name provider
		QDnsLookup *lookup = new QDnsLookup((QDnsLookup::Type)qType, QString::fromLatin1(name), this);
		connect(lookup, SIGNAL(finished()), this, SLOT(handleLookup()));
		int id = currentId++;
		lookup->setProperty("iid", id);
		lookups.insert(id, lookup);
		lookup->lookup();
		return id;
	}

	void resolve_stop(int id)
	{
		QDnsLookup *lookup = lookups.take(id);
		lookup->abort();
	}

private slots:
	void handleLookup()
	{
		QDnsLookup *lookup = static_cast<QDnsLookup *>(sender());
		int id = lookup->property("iid").toInt();
		lookups.remove(id);
		if (lookup->error() != QDnsLookup::NoError) {
			XMPP::NameResolver::Error e;
			switch (lookup->error()) {
				case QDnsLookup::InvalidReplyError:
					e = XMPP::NameResolver::ErrorTimeout;
					break;
				case QDnsLookup::NotFoundError:
					e = XMPP::NameResolver::ErrorNoName;
					break;
				case QDnsLookup::ResolverError:
				case QDnsLookup::OperationCancelledError:
				case QDnsLookup::InvalidRequestError:
				case QDnsLookup::ServerFailureError:
				case QDnsLookup::ServerRefusedError:
				default:
					e = XMPP::NameResolver::ErrorGeneric;
					break;
			}
			emit resolve_error(id, e);
			return;
		}

		QList<XMPP::NameRecord> results;
		for (auto &qtr: lookup->hostAddressRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setAddress(qtr.value());
			results += ir;
		}
		for (auto &qtr: lookup->mailExchangeRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setMx(qtr.exchange().toLatin1(), qtr.preference());
			results += ir;
		}
		for (auto &qtr: lookup->nameServerRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setNs(qtr.value().toLatin1());
			results += ir;
		}
		for (auto &qtr: lookup->pointerRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setPtr(qtr.value().toLatin1());
			results += ir;
		}
		for (auto &qtr: lookup->canonicalNameRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setCname(qtr.value().toLatin1());
			results += ir;
		}
		for (auto &qtr: lookup->serviceRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setSrv(qtr.target().toLatin1(),qtr.port(),qtr.priority(),qtr.weight());
			results += ir;
		}
		for (auto &qtr: lookup->textRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setTxt(qtr.values());
			results += ir;
		}
		lookup->deleteLater();
		emit resolve_resultsReady(id, results);
	}
};

class IrisQtNameProvider : public IrisNetProvider
{
	Q_OBJECT
	Q_INTERFACES(XMPP::IrisNetProvider)
public:

	NameProvider *createNameProviderInternet()
	{
		return new IrisQtName;
	}
};

IrisNetProvider *irisnet_createQtNameProvider()
{
	return new IrisQtNameProvider;
}

}

#include "netinterface_qtname.moc"
<commit_msg>Fix possible memory leaks and crashes in new QDnsLookup based resolver<commit_after>/*
 * Copyright (C) 2017  Sergey Ilinykh
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 */

#include "irisnetplugin.h"

#include <QNetworkInterface>

namespace XMPP {

class IrisQtName : public NameProvider
{
	Q_OBJECT
	Q_INTERFACES(XMPP::NameProvider)

	int currentId;
	QHash<int,QDnsLookup*> lookups;

public:
	IrisQtName(QObject *parent = 0) :
	    NameProvider(parent),
	    currentId(0)
	{

	}

	~IrisQtName()
	{
		qDeleteAll(lookups);
	}

	bool supportsSingle() const
	{
		return true;
	}

	bool supportsRecordType(int type) const
	{
		// yes the types matched to ones from jdns, so it's fine.
		static QVector<int> types = {
		    QDnsLookup::A, QDnsLookup::AAAA, QDnsLookup::ANY,
		    QDnsLookup::CNAME, QDnsLookup::MX, QDnsLookup::NS,
		    QDnsLookup::PTR, QDnsLookup::SRV, QDnsLookup::TXT};
		return types.contains(type);
	}

	int resolve_start(const QByteArray &name, int qType, bool longLived)
	{
		Q_UNUSED(longLived); // FIXME handle local like in jdns name provider
		QDnsLookup *lookup = new QDnsLookup((QDnsLookup::Type)qType, QString::fromLatin1(name), this);
		connect(lookup, SIGNAL(finished()), this, SLOT(handleLookup()));
		int id = currentId++;
		lookup->setProperty("iid", id);
		lookups.insert(id, lookup);
		QMetaObject::invokeMethod(lookup, "lookup", Qt::QueuedConnection);
		return id;
	}

	void resolve_stop(int id)
	{
		QDnsLookup *lookup = lookups.value(id);
		if (lookup) {
			lookup->abort(); // handleLookup will catch it and delete
		}
	}

private slots:
	void handleLookup()
	{
		QDnsLookup *lookup = static_cast<QDnsLookup *>(sender());
		int id = lookup->property("iid").toInt();
		lookups.remove(id);
		if (lookup->error() != QDnsLookup::NoError) {
			XMPP::NameResolver::Error e;
			switch (lookup->error()) {
				case QDnsLookup::InvalidReplyError:
					e = XMPP::NameResolver::ErrorTimeout;
					break;
				case QDnsLookup::NotFoundError:
					e = XMPP::NameResolver::ErrorNoName;
					break;
				case QDnsLookup::ResolverError:
				case QDnsLookup::OperationCancelledError:
				case QDnsLookup::InvalidRequestError:
				case QDnsLookup::ServerFailureError:
				case QDnsLookup::ServerRefusedError:
				default:
					e = XMPP::NameResolver::ErrorGeneric;
					break;
			}
			if (lookup->error() != QDnsLookup::OperationCancelledError) { // don't report after resolve_stop()
				emit resolve_error(id, e);
			}
			lookup->deleteLater();
			return;
		}

		QList<XMPP::NameRecord> results;
		for (auto &qtr: lookup->hostAddressRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setAddress(qtr.value());
			results += ir;
		}
		for (auto &qtr: lookup->mailExchangeRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setMx(qtr.exchange().toLatin1(), qtr.preference());
			results += ir;
		}
		for (auto &qtr: lookup->nameServerRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setNs(qtr.value().toLatin1());
			results += ir;
		}
		for (auto &qtr: lookup->pointerRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setPtr(qtr.value().toLatin1());
			results += ir;
		}
		for (auto &qtr: lookup->canonicalNameRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setCname(qtr.value().toLatin1());
			results += ir;
		}
		for (auto &qtr: lookup->serviceRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setSrv(qtr.target().toLatin1(),qtr.port(),qtr.priority(),qtr.weight());
			results += ir;
		}
		for (auto &qtr: lookup->textRecords()) {
			XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive());
			ir.setTxt(qtr.values());
			results += ir;
		}
		lookup->deleteLater();
		emit resolve_resultsReady(id, results);
	}
};

class IrisQtNameProvider : public IrisNetProvider
{
	Q_OBJECT
	Q_INTERFACES(XMPP::IrisNetProvider)
public:

	NameProvider *createNameProviderInternet()
	{
		return new IrisQtName;
	}
};

IrisNetProvider *irisnet_createQtNameProvider()
{
	return new IrisQtNameProvider;
}

}

#include "netinterface_qtname.moc"
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: jefftk@google.com (Jeff Kaufman)

#include "ngx_base_fetch.h"
#include "ngx_pagespeed.h"
#include "net/instaweb/http/public/response_headers.h"
#include "net/instaweb/util/public/google_message_handler.h"
#include "net/instaweb/util/public/message_handler.h"

namespace net_instaweb {

NgxBaseFetch::NgxBaseFetch(ngx_http_request_t* r, int pipe_fd)
    : request_(r), done_called_(false), last_buf_sent_(false),
      pipe_fd_(pipe_fd) {
  if (pthread_mutex_init(&mutex_, NULL)) CHECK(0);
  PopulateRequestHeaders();
}

NgxBaseFetch::~NgxBaseFetch() {
  pthread_mutex_destroy(&mutex_);
}

void NgxBaseFetch::Lock() {
  pthread_mutex_lock(&mutex_);
}

void NgxBaseFetch::Unlock() {
  pthread_mutex_unlock(&mutex_);
}

void NgxBaseFetch::PopulateRequestHeaders() {
  CopyHeadersFromTable<RequestHeaders>(&request_->headers_in.headers,
                                       request_headers());
}

void NgxBaseFetch::PopulateResponseHeaders() {
  CopyHeadersFromTable<ResponseHeaders>(&request_->headers_out.headers,
                                        response_headers());

  response_headers()->set_status_code(request_->headers_out.status);

  // Manually copy over the content type because it's not included in
  // request_->headers_out.headers.
  response_headers()->Add(
      HttpAttributes::kContentType,
      ngx_psol::str_to_string_piece(request_->headers_out.content_type));

  // TODO(oschaaf): ComputeCaching should be called in setupforhtml()?
  response_headers()->ComputeCaching();
}

template<class HeadersT>
void NgxBaseFetch::CopyHeadersFromTable(ngx_list_t* headers_from,
                                        HeadersT* headers_to) {
  // http_version is the version number of protocol; 1.1 = 1001. See
  // NGX_HTTP_VERSION_* in ngx_http_request.h
  headers_to->set_major_version(request_->http_version / 1000);
  headers_to->set_minor_version(request_->http_version % 1000);

  // Standard nginx idiom for iterating over a list.  See ngx_list.h
  ngx_uint_t i;
  ngx_list_part_t* part = &headers_from->part;
  ngx_table_elt_t* header = static_cast<ngx_table_elt_t*>(part->elts);

  for (i = 0 ; /* void */; i++) {
    if (i >= part->nelts) {
      if (part->next == NULL) {
        break;
      }

      part = part->next;
      header = static_cast<ngx_table_elt_t*>(part->elts);
      i = 0;
    }

    StringPiece key = ngx_psol::str_to_string_piece(header[i].key);
    StringPiece value = ngx_psol::str_to_string_piece(header[i].value);

    headers_to->Add(key, value);
  }
}

bool NgxBaseFetch::HandleWrite(const StringPiece& sp,
                               MessageHandler* handler) {
  Lock();
  buffer_.append(sp.data(), sp.size());
  Unlock();
  return true;
}

ngx_int_t NgxBaseFetch::CopyBufferToNginx(ngx_chain_t** link_ptr) {
  // TODO(jefftk): if done_called_ && last_buf_sent_, should we just short
  // circuit (return NGX_OK) here?

  int rc = ngx_psol::string_piece_to_buffer_chain(
      request_->pool, buffer_, link_ptr, done_called_ /* send_last_buf */);
  if (rc != NGX_OK) {
    return rc;
  }

  // Done with buffer contents now.
  buffer_.clear();

  if (done_called_) {
    last_buf_sent_ = true;
  }

  return NGX_OK;
}

// There may also be a race condition if this is called between the last Write()
// and Done() such that we're sending an empty buffer with last_buf set, which I
// think nginx will reject.
ngx_int_t NgxBaseFetch::CollectAccumulatedWrites(ngx_chain_t** link_ptr) {
  Lock();
  ngx_int_t rc = CopyBufferToNginx(link_ptr);
  Unlock();

  if (rc == NGX_DECLINED) {
    *link_ptr = NULL;
    return NGX_OK;
  }
  return rc;
}

ngx_int_t NgxBaseFetch::CollectHeaders(ngx_http_headers_out_t* headers_out) {
  // Copy from response_headers() into headers_out.

  Lock();
  const ResponseHeaders* pagespeed_headers = response_headers();
  Unlock();

  headers_out->status = pagespeed_headers->status_code();

  ngx_int_t i;
  for (i = 0 ; i < pagespeed_headers->NumAttributes() ; i++) {
    const GoogleString& name_gs = pagespeed_headers->Name(i);
    const GoogleString& value_gs = pagespeed_headers->Value(i);

    ngx_str_t name, value;
    name.len = name_gs.length();
    name.data = reinterpret_cast<u_char*>(const_cast<char*>(name_gs.data()));
    value.len = value_gs.length();
    value.data = reinterpret_cast<u_char*>(const_cast<char*>(value_gs.data()));

    // TODO(jefftk): If we're setting a cache control header we'd like to
    // prevent any downstream code from changing it.  Specifically, if we're
    // serving a cache-extended resource the url will change if the resource
    // does and so we've given it a long lifetime.  If the site owner has done
    // something like set all css files to a 10-minute cache lifetime, that
    // shouldn't apply to our generated resources.  See Apache code in
    // net/instaweb/apache/header_util:AddResponseHeadersToRequest

    // Make copies of name and value to put into headers_out.

    u_char* value_s = ngx_pstrdup(request_->pool, &value);
    if (value_s == NULL) {
      return NGX_ERROR;
    }

    if (STR_EQ_LITERAL(name, "Content-Type")) {
      // Unlike all the other headers, content_type is just a string.
      headers_out->content_type.data = value_s;
      headers_out->content_type.len = value.len;
      headers_out->content_type_len = value.len;
      // In ngx_http_test_content_type() nginx will allocate and calculate
      // content_type_lowcase if we leave it as null.
      headers_out->content_type_lowcase = NULL;
      continue;
    }

    u_char* name_s = ngx_pstrdup(request_->pool, &name);
    if (name_s == NULL) {
      return NGX_ERROR;
    }

    ngx_table_elt_t* header = static_cast<ngx_table_elt_t*>(
        ngx_list_push(&headers_out->headers));
    if (header == NULL) {
      return NGX_ERROR;
    }

    header->hash = 1;  // Include this header in the output.
    header->key.len = name.len;
    header->key.data = name_s;
    header->value.len = value.len;
    header->value.data = value_s;

    // Populate the shortcuts to commonly used headers.
    if (STR_EQ_LITERAL(name, "Date")) {
      headers_out->date = header;
    } else if (STR_EQ_LITERAL(name, "Etag")) {
      headers_out->etag = header;
    } else if (STR_EQ_LITERAL(name, "Expires")) {
      headers_out->expires = header;
    } else if (STR_EQ_LITERAL(name, "Last-Modified")) {
      headers_out->last_modified = header;
    } else if (STR_EQ_LITERAL(name, "Location")) {
      headers_out->location = header;
    } else if (STR_EQ_LITERAL(name, "Server")) {
      headers_out->server = header;
    }
  }

  return NGX_OK;
}

void NgxBaseFetch::RequestCollection() {
  int rc;
  char c = 'A';  // What byte we write is arbitrary.
  while (true) {
    rc = write(pipe_fd_, &c, 1);
    if (rc == 1) {
      break;
    } else if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
      // TODO(jefftk): is this rare enough that spinning isn't a problem?  Could
      // we get into a case where the pipe fills up and we spin forever?

    } else {
      perror("NgxBaseFetch::RequestCollection");
      break;
    }
  }
}

void NgxBaseFetch::HandleHeadersComplete() {
  RequestCollection();  // Headers available.
}

bool NgxBaseFetch::HandleFlush(MessageHandler* handler) {
  RequestCollection();  // A new part of the response body is available.
  return true;
}

void NgxBaseFetch::HandleDone(bool success) {
  done_called_ = true;
  close(pipe_fd_);  // Indicates to nginx that we're done with the rewrite.
  pipe_fd_ = -1;
}

}  // namespace net_instaweb
<commit_msg>ngx_base_fetch: fix extra chunks and race condition<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: jefftk@google.com (Jeff Kaufman)

#include "ngx_base_fetch.h"
#include "ngx_pagespeed.h"
#include "net/instaweb/http/public/response_headers.h"
#include "net/instaweb/util/public/google_message_handler.h"
#include "net/instaweb/util/public/message_handler.h"

namespace net_instaweb {

NgxBaseFetch::NgxBaseFetch(ngx_http_request_t* r, int pipe_fd)
    : request_(r), done_called_(false), last_buf_sent_(false),
      pipe_fd_(pipe_fd) {
  if (pthread_mutex_init(&mutex_, NULL)) CHECK(0);
  PopulateRequestHeaders();
}

NgxBaseFetch::~NgxBaseFetch() {
  pthread_mutex_destroy(&mutex_);
}

void NgxBaseFetch::Lock() {
  pthread_mutex_lock(&mutex_);
}

void NgxBaseFetch::Unlock() {
  pthread_mutex_unlock(&mutex_);
}

void NgxBaseFetch::PopulateRequestHeaders() {
  CopyHeadersFromTable<RequestHeaders>(&request_->headers_in.headers,
                                       request_headers());
}

void NgxBaseFetch::PopulateResponseHeaders() {
  CopyHeadersFromTable<ResponseHeaders>(&request_->headers_out.headers,
                                        response_headers());

  response_headers()->set_status_code(request_->headers_out.status);

  // Manually copy over the content type because it's not included in
  // request_->headers_out.headers.
  response_headers()->Add(
      HttpAttributes::kContentType,
      ngx_psol::str_to_string_piece(request_->headers_out.content_type));

  // TODO(oschaaf): ComputeCaching should be called in setupforhtml()?
  response_headers()->ComputeCaching();
}

template<class HeadersT>
void NgxBaseFetch::CopyHeadersFromTable(ngx_list_t* headers_from,
                                        HeadersT* headers_to) {
  // http_version is the version number of protocol; 1.1 = 1001. See
  // NGX_HTTP_VERSION_* in ngx_http_request.h
  headers_to->set_major_version(request_->http_version / 1000);
  headers_to->set_minor_version(request_->http_version % 1000);

  // Standard nginx idiom for iterating over a list.  See ngx_list.h
  ngx_uint_t i;
  ngx_list_part_t* part = &headers_from->part;
  ngx_table_elt_t* header = static_cast<ngx_table_elt_t*>(part->elts);

  for (i = 0 ; /* void */; i++) {
    if (i >= part->nelts) {
      if (part->next == NULL) {
        break;
      }

      part = part->next;
      header = static_cast<ngx_table_elt_t*>(part->elts);
      i = 0;
    }

    StringPiece key = ngx_psol::str_to_string_piece(header[i].key);
    StringPiece value = ngx_psol::str_to_string_piece(header[i].value);

    headers_to->Add(key, value);
  }
}

bool NgxBaseFetch::HandleWrite(const StringPiece& sp,
                               MessageHandler* handler) {
  Lock();
  buffer_.append(sp.data(), sp.size());
  Unlock();
  return true;
}

ngx_int_t NgxBaseFetch::CopyBufferToNginx(ngx_chain_t** link_ptr) {
  if (done_called_ && last_buf_sent_) {
    return NGX_DECLINED;
  }

  int rc = ngx_psol::string_piece_to_buffer_chain(
      request_->pool, buffer_, link_ptr, done_called_ /* send_last_buf */);
  if (rc != NGX_OK) {
    return rc;
  }

  // Done with buffer contents now.
  buffer_.clear();

  if (done_called_) {
    last_buf_sent_ = true;
  }

  return NGX_OK;
}

// There may also be a race condition if this is called between the last Write()
// and Done() such that we're sending an empty buffer with last_buf set, which I
// think nginx will reject.
ngx_int_t NgxBaseFetch::CollectAccumulatedWrites(ngx_chain_t** link_ptr) {
  Lock();
  ngx_int_t rc = CopyBufferToNginx(link_ptr);
  Unlock();

  if (rc == NGX_DECLINED) {
    *link_ptr = NULL;
    return NGX_OK;
  }
  return rc;
}

ngx_int_t NgxBaseFetch::CollectHeaders(ngx_http_headers_out_t* headers_out) {
  // Copy from response_headers() into headers_out.

  Lock();
  const ResponseHeaders* pagespeed_headers = response_headers();
  Unlock();

  headers_out->status = pagespeed_headers->status_code();

  ngx_int_t i;
  for (i = 0 ; i < pagespeed_headers->NumAttributes() ; i++) {
    const GoogleString& name_gs = pagespeed_headers->Name(i);
    const GoogleString& value_gs = pagespeed_headers->Value(i);

    ngx_str_t name, value;
    name.len = name_gs.length();
    name.data = reinterpret_cast<u_char*>(const_cast<char*>(name_gs.data()));
    value.len = value_gs.length();
    value.data = reinterpret_cast<u_char*>(const_cast<char*>(value_gs.data()));

    // TODO(jefftk): If we're setting a cache control header we'd like to
    // prevent any downstream code from changing it.  Specifically, if we're
    // serving a cache-extended resource the url will change if the resource
    // does and so we've given it a long lifetime.  If the site owner has done
    // something like set all css files to a 10-minute cache lifetime, that
    // shouldn't apply to our generated resources.  See Apache code in
    // net/instaweb/apache/header_util:AddResponseHeadersToRequest

    // Make copies of name and value to put into headers_out.

    u_char* value_s = ngx_pstrdup(request_->pool, &value);
    if (value_s == NULL) {
      return NGX_ERROR;
    }

    if (STR_EQ_LITERAL(name, "Content-Type")) {
      // Unlike all the other headers, content_type is just a string.
      headers_out->content_type.data = value_s;
      headers_out->content_type.len = value.len;
      headers_out->content_type_len = value.len;
      // In ngx_http_test_content_type() nginx will allocate and calculate
      // content_type_lowcase if we leave it as null.
      headers_out->content_type_lowcase = NULL;
      continue;
    }

    u_char* name_s = ngx_pstrdup(request_->pool, &name);
    if (name_s == NULL) {
      return NGX_ERROR;
    }

    ngx_table_elt_t* header = static_cast<ngx_table_elt_t*>(
        ngx_list_push(&headers_out->headers));
    if (header == NULL) {
      return NGX_ERROR;
    }

    header->hash = 1;  // Include this header in the output.
    header->key.len = name.len;
    header->key.data = name_s;
    header->value.len = value.len;
    header->value.data = value_s;

    // Populate the shortcuts to commonly used headers.
    if (STR_EQ_LITERAL(name, "Date")) {
      headers_out->date = header;
    } else if (STR_EQ_LITERAL(name, "Etag")) {
      headers_out->etag = header;
    } else if (STR_EQ_LITERAL(name, "Expires")) {
      headers_out->expires = header;
    } else if (STR_EQ_LITERAL(name, "Last-Modified")) {
      headers_out->last_modified = header;
    } else if (STR_EQ_LITERAL(name, "Location")) {
      headers_out->location = header;
    } else if (STR_EQ_LITERAL(name, "Server")) {
      headers_out->server = header;
    }
  }

  return NGX_OK;
}

void NgxBaseFetch::RequestCollection() {
  int rc;
  char c = 'A';  // What byte we write is arbitrary.
  while (true) {
    rc = write(pipe_fd_, &c, 1);
    if (rc == 1) {
      break;
    } else if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
      // TODO(jefftk): is this rare enough that spinning isn't a problem?  Could
      // we get into a case where the pipe fills up and we spin forever?

    } else {
      perror("NgxBaseFetch::RequestCollection");
      break;
    }
  }
}

void NgxBaseFetch::HandleHeadersComplete() {
  RequestCollection();  // Headers available.
}

bool NgxBaseFetch::HandleFlush(MessageHandler* handler) {
  RequestCollection();  // A new part of the response body is available.
  return true;
}

void NgxBaseFetch::HandleDone(bool success) {
  // TODO(jefftk): it's possible that instead of locking here we can just modify
  // CopyBufferToNginx to only read done_called_ once.
  Lock();
  done_called_ = true;
  Unlock();

  close(pipe_fd_);  // Indicates to nginx that we're done with the rewrite.
  pipe_fd_ = -1;
}

}  // namespace net_instaweb
<|endoftext|>


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

    Configuration file for nomlib library

  Copyright (c) 2013 Jeffrey Carpenter

******************************************************************************/
#ifndef NOMLIB_CONFIG_HEADERS
#define NOMLIB_CONFIG_HEADERS

#include <iostream>
#include <cassert>

#include "nomlib_types.hpp"
#include "sys/Logger.hpp"

// nomlib version
#define NOMLIB_VERSION_MAJOR 0
#define NOMLIB_VERSION_MINOR 0

// Identification the operating system
#if defined ( _WIN32) || defined ( __WIN32__ )
  #define NOMLIB_SYSTEM_WINDOWS
#elif defined ( linux ) || defined ( __linux )
  #define NOMLIB_SYSTEM_LINUX
#elif defined ( __APPLE__ ) || defined ( MACOSX ) || defined ( macintosh ) || defined ( Macintosh )
  #define NOMLIB_SYSTEM_OSX
#else
  #warning This operating system is not officially supported by nomlib
#endif

// Function names and preferably also its type signature
#if defined ( _MSC_VER ) // MSVC++
  // TODO: Presumably the same as GNU's __PRETTY_FUNCTION__ ?
  //
  // SOURCE: http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx
  #define __func__ __FUNCSIG__
#else // We assume GNU v2+

  // The type signature is nice because this shows if the function calling type
  // is a virtual or not and even what arguments the function has
  #define __func__ __PRETTY_FUNCTION__
#endif

// nomlib debugging

// Standard debug level; logging of warnings & errors
//#define NOMLIB_DEBUG

// Internal development; logging of class object construction and destruction
#define NOMLIB_DEBUG_ALL

// Pretty print C macros
#ifdef NOMLIB_DEBUG_ALL
  // If all debugging is turned on, we show class construction and destruction
  #define NOMLIB_LOG_INFO ( nom::Logger::info ( __func__ ) )
#else // We do not add any overhead
  #define NOMLIB_LOG_INFO
#endif

#ifdef NOMLIB_DEBUG
  // If all debugging is turned on, we show all errors logged
  #define NOMLIB_LOG_ERR(message) ( nom::Logger::err ( __FILE__, __LINE__, message ) )
  #define NOMLIB_ASSERT(expression) ( assert (expression) )
#else // We do not add any overhead
  #define NOMLIB_LOG_ERR(message)
  #define NOMLIB_ASSERT(expression)
#endif

#endif // NOMLIB_CONFIG_HEADERS defined
<commit_msg>Re-enable debugging<commit_after>/******************************************************************************

    Configuration file for nomlib library

  Copyright (c) 2013 Jeffrey Carpenter

******************************************************************************/
#ifndef NOMLIB_CONFIG_HEADERS
#define NOMLIB_CONFIG_HEADERS

#include <iostream>
#include <cassert>

#include "nomlib_types.hpp"
#include "sys/Logger.hpp"

// nomlib version
#define NOMLIB_VERSION_MAJOR 0
#define NOMLIB_VERSION_MINOR 0

// Identification the operating system
#if defined ( _WIN32) || defined ( __WIN32__ )
  #define NOMLIB_SYSTEM_WINDOWS
#elif defined ( linux ) || defined ( __linux )
  #define NOMLIB_SYSTEM_LINUX
#elif defined ( __APPLE__ ) || defined ( MACOSX ) || defined ( macintosh ) || defined ( Macintosh )
  #define NOMLIB_SYSTEM_OSX
#else
  #warning This operating system is not officially supported by nomlib
#endif

// Function names and preferably also its type signature
#if defined ( _MSC_VER ) // MSVC++
  // TODO: Presumably the same as GNU's __PRETTY_FUNCTION__ ?
  //
  // SOURCE: http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx
  #define __func__ __FUNCSIG__
#else // We assume GNU v2+

  // The type signature is nice because this shows if the function calling type
  // is a virtual or not and even what arguments the function has
  #define __func__ __PRETTY_FUNCTION__
#endif

// nomlib debugging

// Standard debug level; logging of warnings & errors
#define NOMLIB_DEBUG

// Internal development; logging of class object construction and destruction
#define NOMLIB_DEBUG_ALL

// Pretty print C macros
#ifdef NOMLIB_DEBUG_ALL
  // If all debugging is turned on, we show class construction and destruction
  #define NOMLIB_LOG_INFO ( nom::Logger::info ( __func__ ) )
#else // We do not add any overhead
  #define NOMLIB_LOG_INFO
#endif

#ifdef NOMLIB_DEBUG
  // If all debugging is turned on, we show all errors logged
  #define NOMLIB_LOG_ERR(message) ( nom::Logger::err ( __FILE__, __LINE__, message ) )
  #define NOMLIB_ASSERT(expression) ( assert (expression) )
#else // We do not add any overhead
  #define NOMLIB_LOG_ERR(message)
  #define NOMLIB_ASSERT(expression)
#endif

#endif // NOMLIB_CONFIG_HEADERS defined
<|endoftext|>