<commit_before>#include <iostream>
#include <limits>

#include "tatum_assert.hpp"
#include "TimingConstraints.hpp"

using std::cout;
using std::endl;

namespace tatum {

TimingConstraints::domain_range TimingConstraints::clock_domains() const {
    return tatum::util::make_range(domain_ids_.begin(), domain_ids_.end());
}

const std::string& TimingConstraints::clock_domain_name(const DomainId id) const {
    return domain_names_[id];
}

NodeId TimingConstraints::clock_domain_source_node(const DomainId id) const {
    return domain_sources_[id];
}

bool TimingConstraints::is_virtual_clock(const DomainId id) const {
    //No source node indicates a virtual clock
    return bool(clock_domain_source_node(id));
}

DomainId TimingConstraints::node_clock_domain(const NodeId id) const {
    //This is currenlty a linear search through all clock sources and
    //I/O constraints, could be made more efficient but it is only called
    //rarely (i.e. during pre-traversals)

    //Is it a clock source?
    DomainId source_domain = find_node_source_clock_domain(id);
    if(source_domain) return source_domain;

    //Does it have an input constarint?
    for(auto kv : input_constraints()) {
        auto node_id = kv.first;
        auto domain_id = kv.second.domain;

        //TODO: Assumes a single clock per node
        if(node_id == id) return domain_id;
    }

    //Does it have an output constraint?
    for(auto kv : output_constraints()) {
        auto node_id = kv.first;
        auto domain_id = kv.second.domain;

        //TODO: Assumes a single clock per node
        if(node_id == id) return domain_id;
    }

    //None found
    return DomainId::INVALID();
}

bool TimingConstraints::node_is_clock_source(const NodeId id) const {
    //Returns a DomainId which converts to true if valid
    return bool(find_node_source_clock_domain(id));
}

bool TimingConstraints::node_is_constant_generator(const NodeId id) const {
    return constant_generators_.count(id);
}

DomainId TimingConstraints::find_node_source_clock_domain(const NodeId node_id) const {
    //We don't expect many clocks, so the linear search should be fine
    for(auto domain_id : clock_domains()) {
        if(clock_domain_source_node(domain_id) == node_id) {
            return domain_id;
        }
    }

    return DomainId::INVALID();
}

DomainId TimingConstraints::find_clock_domain(const std::string& name) const {
    //Linear search for name
    // We don't expect a large number of domains
    for(DomainId id : clock_domains()) {
        if(clock_domain_name(id) == name) {
            return id;
        }
    }

    //Not found
    return DomainId::INVALID();
}

bool TimingConstraints::should_analyze(const DomainId src_domain, const DomainId sink_domain) const {
    return setup_constraints_.count(DomainPair(src_domain, sink_domain))
           || hold_constraints_.count(DomainPair(src_domain, sink_domain));
}

float TimingConstraints::hold_constraint(const DomainId src_domain, const DomainId sink_domain) const {
    auto iter = hold_constraints_.find(DomainPair(src_domain, sink_domain));
    if(iter == hold_constraints_.end()) {
        return std::numeric_limits<float>::quiet_NaN();
    }

    return iter->second;
}
float TimingConstraints::setup_constraint(const DomainId src_domain, const DomainId sink_domain) const {
    auto iter = setup_constraints_.find(DomainPair(src_domain, sink_domain));
    if(iter == setup_constraints_.end()) {
        return std::numeric_limits<float>::quiet_NaN();
    }

    return iter->second;
}

float TimingConstraints::input_constraint(const NodeId node_id, const DomainId domain_id) const {

    auto iter = find_io_constraint(node_id, domain_id, input_constraints_);
    if(iter != input_constraints_.end()) {
        return iter->second.constraint;
    }

    return std::numeric_limits<float>::quiet_NaN();
}

float TimingConstraints::output_constraint(const NodeId node_id, const DomainId domain_id) const {

    auto iter = find_io_constraint(node_id, domain_id, output_constraints_);
    if(iter != output_constraints_.end()) {
        return iter->second.constraint;
    }

    return std::numeric_limits<float>::quiet_NaN();
}

TimingConstraints::constant_generator_range TimingConstraints::constant_generators() const {
    return tatum::util::make_range(constant_generators_.begin(), constant_generators_.end());
}

TimingConstraints::clock_constraint_range TimingConstraints::setup_constraints() const {
    return tatum::util::make_range(setup_constraints_.begin(), setup_constraints_.end());
}

TimingConstraints::clock_constraint_range TimingConstraints::hold_constraints() const {
    return tatum::util::make_range(hold_constraints_.begin(), hold_constraints_.end());
}

TimingConstraints::io_constraint_range TimingConstraints::input_constraints() const {
    return tatum::util::make_range(input_constraints_.begin(), input_constraints_.end());
}

TimingConstraints::io_constraint_range TimingConstraints::output_constraints() const {
    return tatum::util::make_range(output_constraints_.begin(), output_constraints_.end());
}

TimingConstraints::io_constraint_range TimingConstraints::input_constraints(const NodeId id) const {
    auto range = input_constraints_.equal_range(id);
    return tatum::util::make_range(range.first, range.second);
}

TimingConstraints::io_constraint_range TimingConstraints::output_constraints(const NodeId id) const {
    auto range = output_constraints_.equal_range(id);
    return tatum::util::make_range(range.first, range.second);
}

DomainId TimingConstraints::create_clock_domain(const std::string name) {
    DomainId id = find_clock_domain(name);
    if(!id) {
        //Create it
        id = DomainId(domain_ids_.size());
        domain_ids_.push_back(id);

        domain_names_.push_back(name);
        domain_sources_.emplace_back(NodeId::INVALID());

        TATUM_ASSERT(clock_domain_name(id) == name);
        TATUM_ASSERT(find_clock_domain(name) == id);
    }

    return id;
}

void TimingConstraints::set_setup_constraint(const DomainId src_domain, const DomainId sink_domain, const float constraint) {
    std::cout << "SRC: " << src_domain << " SINK: " << sink_domain << " Constraint: " << constraint << std::endl;
    auto key = DomainPair(src_domain, sink_domain);
    auto iter = setup_constraints_.insert(std::make_pair(key, constraint));
    TATUM_ASSERT_MSG(iter.second, "Attempted to insert duplicate setup clock constraint");
}

void TimingConstraints::set_hold_constraint(const DomainId src_domain, const DomainId sink_domain, const float constraint) {
    std::cout << "SRC: " << src_domain << " SINK: " << sink_domain << " Constraint: " << constraint << std::endl;
    auto key = DomainPair(src_domain, sink_domain);
    auto iter = hold_constraints_.insert(std::make_pair(key, constraint));
    TATUM_ASSERT_MSG(iter.second, "Attempted to insert duplicate hold clock constraint");
}

void TimingConstraints::set_input_constraint(const NodeId node_id, const DomainId domain_id, const float constraint) {
    auto iter = find_io_constraint(node_id, domain_id, input_constraints_);
    if(iter != input_constraints_.end()) {
        //Found, update
        iter->second.constraint = constraint;
    } else {
        //Not found create it
        input_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
    }
}

void TimingConstraints::set_output_constraint(const NodeId node_id, const DomainId domain_id, const float constraint) {
    auto iter = find_io_constraint(node_id, domain_id, output_constraints_);
    if(iter != output_constraints_.end()) {
        //Found, update
        iter->second.constraint = constraint;
    } else {
        //Not found create it
        output_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
    }
}

void TimingConstraints::set_clock_domain_source(const NodeId node_id, const DomainId domain_id) {
    domain_sources_[domain_id] = node_id;
}

void TimingConstraints::set_constant_generator(const NodeId node_id, bool is_constant_generator) {
    if(is_constant_generator) {
        constant_generators_.insert(node_id);
    } else {
        constant_generators_.erase(node_id);
    }
}

void TimingConstraints::remap_nodes(const tatum::util::linear_map<NodeId,NodeId>& node_map) {

    //Domain Sources
    tatum::util::linear_map<DomainId,NodeId> remapped_domain_sources(domain_sources_.size());
    for(size_t domain_idx = 0; domain_idx < domain_sources_.size(); ++domain_idx) {
        DomainId domain_id(domain_idx);

        NodeId old_node_id = domain_sources_[domain_id];
        if(old_node_id) {
            remapped_domain_sources[domain_id] = node_map[old_node_id];
        }
    }
    domain_sources_ = std::move(remapped_domain_sources);

    //Constant generators
    std::unordered_set<NodeId> remapped_constant_generators;
    for(NodeId node_id : constant_generators_) {
        remapped_constant_generators.insert(node_map[node_id]);
    }
    constant_generators_ = std::move(remapped_constant_generators);

    //Input Constraints
    std::multimap<NodeId,IoConstraint> remapped_input_constraints;
    for(auto kv : input_constraints_) {
        NodeId new_node_id = node_map[kv.first];

        remapped_input_constraints.insert(std::make_pair(new_node_id, kv.second));
    }
    input_constraints_ = std::move(remapped_input_constraints);

    //Output Constraints
    std::multimap<NodeId,IoConstraint> remapped_output_constraints;
    for(auto kv : output_constraints_) {
        NodeId new_node_id = node_map[kv.first];

        remapped_output_constraints.insert(std::make_pair(new_node_id, kv.second));
    }
    output_constraints_ = std::move(remapped_output_constraints);
}

void TimingConstraints::print() const {
    cout << "Setup Clock Constraints" << endl;
    for(auto kv : setup_constraints_) {
        auto key = kv.first;
        float constraint = kv.second;
        cout << "SRC: " << key.src_domain_id;
        cout << " SINK: " << key.sink_domain_id;
        cout << " Constraint: " << constraint;
        cout << endl;
    }
    cout << "Hold Clock Constraints" << endl;
    for(auto kv : hold_constraints_) {
        auto key = kv.first;
        float constraint = kv.second;
        cout << "SRC: " << key.src_domain_id;
        cout << " SINK: " << key.sink_domain_id;
        cout << " Constraint: " << constraint;
        cout << endl;
    }
    cout << "Input Constraints" << endl;
    for(auto kv : input_constraints_) {
        auto node_id = kv.first;
        auto io_constraint = kv.second;
        cout << "Node: " << node_id;
        cout << " Domain: " << io_constraint.domain;
        cout << " Constraint: " << io_constraint.constraint;
        cout << endl;
    }
    cout << "Output Constraints" << endl;
    for(auto kv : output_constraints_) {
        auto node_id = kv.first;
        auto io_constraint = kv.second;
        cout << "Node: " << node_id;
        cout << " Domain: " << io_constraint.domain;
        cout << " Constraint: " << io_constraint.constraint;
        cout << endl;
    }
}

TimingConstraints::io_constraint_iterator TimingConstraints::find_io_constraint(const NodeId node_id, const DomainId domain_id, const std::multimap<NodeId,IoConstraint>& io_constraints) const {
    auto range = io_constraints.equal_range(node_id);
    for(auto iter = range.first; iter != range.second; ++iter) {
        if(iter->second.domain == domain_id) return iter;
    }

    //Not found
    return io_constraints.end();
}

TimingConstraints::mutable_io_constraint_iterator TimingConstraints::find_io_constraint(const NodeId node_id, const DomainId domain_id, std::multimap<NodeId,IoConstraint>& io_constraints) {
    auto range = io_constraints.equal_range(node_id);
    for(auto iter = range.first; iter != range.second; ++iter) {
        if(iter->second.domain == domain_id) return iter;
    }

    //Not found
    return io_constraints.end();
}

} //namepsace
<commit_msg>Fix is_virtual_clock (was inverted)<commit_after>#include <iostream>
#include <limits>

#include "tatum_assert.hpp"
#include "TimingConstraints.hpp"

using std::cout;
using std::endl;

namespace tatum {

TimingConstraints::domain_range TimingConstraints::clock_domains() const {
    return tatum::util::make_range(domain_ids_.begin(), domain_ids_.end());
}

const std::string& TimingConstraints::clock_domain_name(const DomainId id) const {
    return domain_names_[id];
}

NodeId TimingConstraints::clock_domain_source_node(const DomainId id) const {
    return domain_sources_[id];
}

bool TimingConstraints::is_virtual_clock(const DomainId id) const {
    //No source node indicates a virtual clock
    return !bool(clock_domain_source_node(id));
}

DomainId TimingConstraints::node_clock_domain(const NodeId id) const {
    //This is currenlty a linear search through all clock sources and
    //I/O constraints, could be made more efficient but it is only called
    //rarely (i.e. during pre-traversals)

    //Is it a clock source?
    DomainId source_domain = find_node_source_clock_domain(id);
    if(source_domain) return source_domain;

    //Does it have an input constarint?
    for(auto kv : input_constraints()) {
        auto node_id = kv.first;
        auto domain_id = kv.second.domain;

        //TODO: Assumes a single clock per node
        if(node_id == id) return domain_id;
    }

    //Does it have an output constraint?
    for(auto kv : output_constraints()) {
        auto node_id = kv.first;
        auto domain_id = kv.second.domain;

        //TODO: Assumes a single clock per node
        if(node_id == id) return domain_id;
    }

    //None found
    return DomainId::INVALID();
}

bool TimingConstraints::node_is_clock_source(const NodeId id) const {
    //Returns a DomainId which converts to true if valid
    return bool(find_node_source_clock_domain(id));
}

bool TimingConstraints::node_is_constant_generator(const NodeId id) const {
    return constant_generators_.count(id);
}

DomainId TimingConstraints::find_node_source_clock_domain(const NodeId node_id) const {
    //We don't expect many clocks, so the linear search should be fine
    for(auto domain_id : clock_domains()) {
        if(clock_domain_source_node(domain_id) == node_id) {
            return domain_id;
        }
    }

    return DomainId::INVALID();
}

DomainId TimingConstraints::find_clock_domain(const std::string& name) const {
    //Linear search for name
    // We don't expect a large number of domains
    for(DomainId id : clock_domains()) {
        if(clock_domain_name(id) == name) {
            return id;
        }
    }

    //Not found
    return DomainId::INVALID();
}

bool TimingConstraints::should_analyze(const DomainId src_domain, const DomainId sink_domain) const {
    return setup_constraints_.count(DomainPair(src_domain, sink_domain))
           || hold_constraints_.count(DomainPair(src_domain, sink_domain));
}

float TimingConstraints::hold_constraint(const DomainId src_domain, const DomainId sink_domain) const {
    auto iter = hold_constraints_.find(DomainPair(src_domain, sink_domain));
    if(iter == hold_constraints_.end()) {
        return std::numeric_limits<float>::quiet_NaN();
    }

    return iter->second;
}
float TimingConstraints::setup_constraint(const DomainId src_domain, const DomainId sink_domain) const {
    auto iter = setup_constraints_.find(DomainPair(src_domain, sink_domain));
    if(iter == setup_constraints_.end()) {
        return std::numeric_limits<float>::quiet_NaN();
    }

    return iter->second;
}

float TimingConstraints::input_constraint(const NodeId node_id, const DomainId domain_id) const {

    auto iter = find_io_constraint(node_id, domain_id, input_constraints_);
    if(iter != input_constraints_.end()) {
        return iter->second.constraint;
    }

    return std::numeric_limits<float>::quiet_NaN();
}

float TimingConstraints::output_constraint(const NodeId node_id, const DomainId domain_id) const {

    auto iter = find_io_constraint(node_id, domain_id, output_constraints_);
    if(iter != output_constraints_.end()) {
        return iter->second.constraint;
    }

    return std::numeric_limits<float>::quiet_NaN();
}

TimingConstraints::constant_generator_range TimingConstraints::constant_generators() const {
    return tatum::util::make_range(constant_generators_.begin(), constant_generators_.end());
}

TimingConstraints::clock_constraint_range TimingConstraints::setup_constraints() const {
    return tatum::util::make_range(setup_constraints_.begin(), setup_constraints_.end());
}

TimingConstraints::clock_constraint_range TimingConstraints::hold_constraints() const {
    return tatum::util::make_range(hold_constraints_.begin(), hold_constraints_.end());
}

TimingConstraints::io_constraint_range TimingConstraints::input_constraints() const {
    return tatum::util::make_range(input_constraints_.begin(), input_constraints_.end());
}

TimingConstraints::io_constraint_range TimingConstraints::output_constraints() const {
    return tatum::util::make_range(output_constraints_.begin(), output_constraints_.end());
}

TimingConstraints::io_constraint_range TimingConstraints::input_constraints(const NodeId id) const {
    auto range = input_constraints_.equal_range(id);
    return tatum::util::make_range(range.first, range.second);
}

TimingConstraints::io_constraint_range TimingConstraints::output_constraints(const NodeId id) const {
    auto range = output_constraints_.equal_range(id);
    return tatum::util::make_range(range.first, range.second);
}

DomainId TimingConstraints::create_clock_domain(const std::string name) {
    DomainId id = find_clock_domain(name);
    if(!id) {
        //Create it
        id = DomainId(domain_ids_.size());
        domain_ids_.push_back(id);

        domain_names_.push_back(name);
        domain_sources_.emplace_back(NodeId::INVALID());

        TATUM_ASSERT(clock_domain_name(id) == name);
        TATUM_ASSERT(find_clock_domain(name) == id);
    }

    return id;
}

void TimingConstraints::set_setup_constraint(const DomainId src_domain, const DomainId sink_domain, const float constraint) {
    std::cout << "SRC: " << src_domain << " SINK: " << sink_domain << " Constraint: " << constraint << std::endl;
    auto key = DomainPair(src_domain, sink_domain);
    auto iter = setup_constraints_.insert(std::make_pair(key, constraint));
    TATUM_ASSERT_MSG(iter.second, "Attempted to insert duplicate setup clock constraint");
}

void TimingConstraints::set_hold_constraint(const DomainId src_domain, const DomainId sink_domain, const float constraint) {
    std::cout << "SRC: " << src_domain << " SINK: " << sink_domain << " Constraint: " << constraint << std::endl;
    auto key = DomainPair(src_domain, sink_domain);
    auto iter = hold_constraints_.insert(std::make_pair(key, constraint));
    TATUM_ASSERT_MSG(iter.second, "Attempted to insert duplicate hold clock constraint");
}

void TimingConstraints::set_input_constraint(const NodeId node_id, const DomainId domain_id, const float constraint) {
    auto iter = find_io_constraint(node_id, domain_id, input_constraints_);
    if(iter != input_constraints_.end()) {
        //Found, update
        iter->second.constraint = constraint;
    } else {
        //Not found create it
        input_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
    }
}

void TimingConstraints::set_output_constraint(const NodeId node_id, const DomainId domain_id, const float constraint) {
    auto iter = find_io_constraint(node_id, domain_id, output_constraints_);
    if(iter != output_constraints_.end()) {
        //Found, update
        iter->second.constraint = constraint;
    } else {
        //Not found create it
        output_constraints_.insert(std::make_pair(node_id, IoConstraint(domain_id, constraint)));
    }
}

void TimingConstraints::set_clock_domain_source(const NodeId node_id, const DomainId domain_id) {
    domain_sources_[domain_id] = node_id;
}

void TimingConstraints::set_constant_generator(const NodeId node_id, bool is_constant_generator) {
    if(is_constant_generator) {
        constant_generators_.insert(node_id);
    } else {
        constant_generators_.erase(node_id);
    }
}

void TimingConstraints::remap_nodes(const tatum::util::linear_map<NodeId,NodeId>& node_map) {

    //Domain Sources
    tatum::util::linear_map<DomainId,NodeId> remapped_domain_sources(domain_sources_.size());
    for(size_t domain_idx = 0; domain_idx < domain_sources_.size(); ++domain_idx) {
        DomainId domain_id(domain_idx);

        NodeId old_node_id = domain_sources_[domain_id];
        if(old_node_id) {
            remapped_domain_sources[domain_id] = node_map[old_node_id];
        }
    }
    domain_sources_ = std::move(remapped_domain_sources);

    //Constant generators
    std::unordered_set<NodeId> remapped_constant_generators;
    for(NodeId node_id : constant_generators_) {
        remapped_constant_generators.insert(node_map[node_id]);
    }
    constant_generators_ = std::move(remapped_constant_generators);

    //Input Constraints
    std::multimap<NodeId,IoConstraint> remapped_input_constraints;
    for(auto kv : input_constraints_) {
        NodeId new_node_id = node_map[kv.first];

        remapped_input_constraints.insert(std::make_pair(new_node_id, kv.second));
    }
    input_constraints_ = std::move(remapped_input_constraints);

    //Output Constraints
    std::multimap<NodeId,IoConstraint> remapped_output_constraints;
    for(auto kv : output_constraints_) {
        NodeId new_node_id = node_map[kv.first];

        remapped_output_constraints.insert(std::make_pair(new_node_id, kv.second));
    }
    output_constraints_ = std::move(remapped_output_constraints);
}

void TimingConstraints::print() const {
    cout << "Setup Clock Constraints" << endl;
    for(auto kv : setup_constraints_) {
        auto key = kv.first;
        float constraint = kv.second;
        cout << "SRC: " << key.src_domain_id;
        cout << " SINK: " << key.sink_domain_id;
        cout << " Constraint: " << constraint;
        cout << endl;
    }
    cout << "Hold Clock Constraints" << endl;
    for(auto kv : hold_constraints_) {
        auto key = kv.first;
        float constraint = kv.second;
        cout << "SRC: " << key.src_domain_id;
        cout << " SINK: " << key.sink_domain_id;
        cout << " Constraint: " << constraint;
        cout << endl;
    }
    cout << "Input Constraints" << endl;
    for(auto kv : input_constraints_) {
        auto node_id = kv.first;
        auto io_constraint = kv.second;
        cout << "Node: " << node_id;
        cout << " Domain: " << io_constraint.domain;
        cout << " Constraint: " << io_constraint.constraint;
        cout << endl;
    }
    cout << "Output Constraints" << endl;
    for(auto kv : output_constraints_) {
        auto node_id = kv.first;
        auto io_constraint = kv.second;
        cout << "Node: " << node_id;
        cout << " Domain: " << io_constraint.domain;
        cout << " Constraint: " << io_constraint.constraint;
        cout << endl;
    }
}

TimingConstraints::io_constraint_iterator TimingConstraints::find_io_constraint(const NodeId node_id, const DomainId domain_id, const std::multimap<NodeId,IoConstraint>& io_constraints) const {
    auto range = io_constraints.equal_range(node_id);
    for(auto iter = range.first; iter != range.second; ++iter) {
        if(iter->second.domain == domain_id) return iter;
    }

    //Not found
    return io_constraints.end();
}

TimingConstraints::mutable_io_constraint_iterator TimingConstraints::find_io_constraint(const NodeId node_id, const DomainId domain_id, std::multimap<NodeId,IoConstraint>& io_constraints) {
    auto range = io_constraints.equal_range(node_id);
    for(auto iter = range.first; iter != range.second; ++iter) {
        if(iter->second.domain == domain_id) return iter;
    }

    //Not found
    return io_constraints.end();
}

} //namepsace
<|endoftext|>


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

#include <memory>
#include <string>

#include "lifecycle_msgs/msg/state.hpp"
#include "lifecycle_msgs/msg/transition.hpp"
#include "lifecycle_msgs/srv/change_state.hpp"
#include "lifecycle_msgs/srv/get_state.hpp"

#include "rclcpp/rclcpp.hpp"

// which node to handle
static constexpr char const * lifecycle_node = "lc_talker";
/*
 * Every lifecycle node has various services
 * attached to it. By convention, we use its
 * node name followed by a doule underscore
 * and the service name.
 * In this demo, we use get_state and change_state
 * and thus the two service topics are:
 * lc_talker__get_state
 * lc_talker__change_state
 */
static constexpr char const * node_get_state_topic = "lc_talker__get_state";
static constexpr char const * node_change_state_topic = "lc_talker__change_state";

template<typename FutureT, typename WaitTimeT>
std::future_status
wait_for_result(
  FutureT & future,
  WaitTimeT time_to_wait)
{
  auto end = std::chrono::steady_clock::now() + time_to_wait;
  std::chrono::milliseconds wait_period(100);
  std::future_status status;
  do {
    auto now = std::chrono::steady_clock::now();
    auto time_left = end - now;
    if (time_left <= std::chrono::seconds(0)) {break;}
    status = future.wait_for((time_left < wait_period) ? time_left : wait_period);
  } while (rclcpp::ok() && status != std::future_status::ready);
  return status;
}

class LifecycleServiceClient : public rclcpp::node::Node
{
public:
  explicit LifecycleServiceClient(const std::string & node_name)
  : rclcpp::node::Node(node_name)
  {}

  void
  init()
  {
    /*
     * Every lifecycle node spawns automatically a couple
     * of services which allow an external interaction with
     * these nodes.
     * The two main important ones are GetState and ChangeState.
     */
    client_get_state_ = this->create_client<lifecycle_msgs::srv::GetState>(
      node_get_state_topic);
    client_change_state_ = this->create_client<lifecycle_msgs::srv::ChangeState>(
      node_change_state_topic);
  }

  /*
   * In this function, we send a service request
   * asking for the current state of the node
   * lc_talker.
   * If it does return within the given time_out,
   * we return the current state of the node, if
   * not, we return an unknown state.
   */
  unsigned int
  get_state(std::chrono::seconds time_out = 3_s)
  {
    auto request = std::make_shared<lifecycle_msgs::srv::GetState::Request>();

    if (!client_get_state_->wait_for_service(time_out)) {
      fprintf(stderr, "Service %s is not available.\n",
        client_get_state_->get_service_name().c_str());
      return lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN;
    }

    /*
     * We send the service request for asking the current
     * state of the lc_talker node.
     */
    auto future_result = client_get_state_->async_send_request(request);
    /*
     * Let's wait until we have the answer from the node.
     * If the request times out, we return an unknown state.
     */
    auto future_status = wait_for_result(future_result, time_out);

    if (future_status != std::future_status::ready) {
      fprintf(stderr, "[%s] Failed to get current state for node %s. Server timed out.\n",
        get_name(), lifecycle_node);
      return lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN;
    }

    /*
     * We have an succesful answer. So let's print the
     * current state.
     */
    if (future_result.get()) {
      printf("[%s] Node %s has current state %s.\n",
        get_name(), lifecycle_node, future_result.get()->current_state.label.c_str());
      return future_result.get()->current_state.id;
    } else {
      fprintf(stderr, "[%s] Failed to get current state for node %s\n",
        get_name(), lifecycle_node);
      return lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN;
    }
  }

  /*
   * We send a Service request and indicate
   * that we want to invoke transition with
   * the id "transition".
   * By default, these transitions are
   * - configure
   * - activate
   * - cleanup
   * - shutdown
   */
  bool
  change_state(std::uint8_t transition, std::chrono::seconds time_out = 3_s)
  {
    auto request = std::make_shared<lifecycle_msgs::srv::ChangeState::Request>();
    request->transition.id = transition;

    if (!client_change_state_->wait_for_service(time_out)) {
      fprintf(stderr, "Service %s is not available.\n",
        client_change_state_->get_service_name().c_str());
      return false;
    }

    /*
     * We send the request with the transition
     * we want to invoke.
     */
    auto future_result = client_change_state_->async_send_request(request);
    /*
     * Let's wait until we have the answer from the node.
     * If the request times out, we return an unknown state.
     */
    auto future_status = wait_for_result(future_result, time_out);

    if (future_status != std::future_status::ready) {
      fprintf(stderr, "[%s] Failed to change state for node %s. Server timed out.\n",
        get_name(), lifecycle_node);
      return false;
    }

    /*
     * We have an answer, let's print
     * our success.
     */
    if (future_result.get()->success) {
      printf("[%s] Transition %d successfully triggered.\n",
        get_name(), static_cast<int>(transition));
      return true;
    } else {
      fprintf(stderr, "[%s] Failed to trigger transition %u\n",
        get_name(), static_cast<unsigned int>(transition));
      return false;
    }
  }

private:
  std::shared_ptr<rclcpp::client::Client<lifecycle_msgs::srv::GetState>> client_get_state_;
  std::shared_ptr<rclcpp::client::Client<lifecycle_msgs::srv::ChangeState>> client_change_state_;
};

/*
 * This is a little independent
 * script which triggers the
 * default lifecycle of a node.
 * It starts with configure, activate,
 * deactivate, activate, deactivate,
 * cleanup and finally shutdown
 */
void
callee_script(std::shared_ptr<LifecycleServiceClient> lc_client)
{
  auto sleep_time = 10_s;

  // configure
  {
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // activate
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // deactivate
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // activate it again
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // and deactivate it again
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // we cleanup
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_CLEANUP)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // and finally shutdown
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_SHUTDOWN)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }
}

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);

  auto lc_client = std::make_shared<LifecycleServiceClient>("lc_client");
  lc_client->init();

  rclcpp::executors::SingleThreadedExecutor exe;
  exe.add_node(lc_client);

  std::shared_future<void> script = std::async(std::launch::async,
      std::bind(callee_script, lc_client));
  exe.spin_until_future_complete(script);

  return 0;
}
<commit_msg>fix uninitialized variable warning (#92)<commit_after>// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <memory>
#include <string>

#include "lifecycle_msgs/msg/state.hpp"
#include "lifecycle_msgs/msg/transition.hpp"
#include "lifecycle_msgs/srv/change_state.hpp"
#include "lifecycle_msgs/srv/get_state.hpp"

#include "rclcpp/rclcpp.hpp"

// which node to handle
static constexpr char const * lifecycle_node = "lc_talker";
/*
 * Every lifecycle node has various services
 * attached to it. By convention, we use its
 * node name followed by a doule underscore
 * and the service name.
 * In this demo, we use get_state and change_state
 * and thus the two service topics are:
 * lc_talker__get_state
 * lc_talker__change_state
 */
static constexpr char const * node_get_state_topic = "lc_talker__get_state";
static constexpr char const * node_change_state_topic = "lc_talker__change_state";

template<typename FutureT, typename WaitTimeT>
std::future_status
wait_for_result(
  FutureT & future,
  WaitTimeT time_to_wait)
{
  auto end = std::chrono::steady_clock::now() + time_to_wait;
  std::chrono::milliseconds wait_period(100);
  std::future_status status = std::future_status::timeout;
  do {
    auto now = std::chrono::steady_clock::now();
    auto time_left = end - now;
    if (time_left <= std::chrono::seconds(0)) {break;}
    status = future.wait_for((time_left < wait_period) ? time_left : wait_period);
  } while (rclcpp::ok() && status != std::future_status::ready);
  return status;
}

class LifecycleServiceClient : public rclcpp::node::Node
{
public:
  explicit LifecycleServiceClient(const std::string & node_name)
  : rclcpp::node::Node(node_name)
  {}

  void
  init()
  {
    /*
     * Every lifecycle node spawns automatically a couple
     * of services which allow an external interaction with
     * these nodes.
     * The two main important ones are GetState and ChangeState.
     */
    client_get_state_ = this->create_client<lifecycle_msgs::srv::GetState>(
      node_get_state_topic);
    client_change_state_ = this->create_client<lifecycle_msgs::srv::ChangeState>(
      node_change_state_topic);
  }

  /*
   * In this function, we send a service request
   * asking for the current state of the node
   * lc_talker.
   * If it does return within the given time_out,
   * we return the current state of the node, if
   * not, we return an unknown state.
   */
  unsigned int
  get_state(std::chrono::seconds time_out = 3_s)
  {
    auto request = std::make_shared<lifecycle_msgs::srv::GetState::Request>();

    if (!client_get_state_->wait_for_service(time_out)) {
      fprintf(stderr, "Service %s is not available.\n",
        client_get_state_->get_service_name().c_str());
      return lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN;
    }

    /*
     * We send the service request for asking the current
     * state of the lc_talker node.
     */
    auto future_result = client_get_state_->async_send_request(request);
    /*
     * Let's wait until we have the answer from the node.
     * If the request times out, we return an unknown state.
     */
    auto future_status = wait_for_result(future_result, time_out);

    if (future_status != std::future_status::ready) {
      fprintf(stderr, "[%s] Failed to get current state for node %s. Server timed out.\n",
        get_name(), lifecycle_node);
      return lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN;
    }

    /*
     * We have an succesful answer. So let's print the
     * current state.
     */
    if (future_result.get()) {
      printf("[%s] Node %s has current state %s.\n",
        get_name(), lifecycle_node, future_result.get()->current_state.label.c_str());
      return future_result.get()->current_state.id;
    } else {
      fprintf(stderr, "[%s] Failed to get current state for node %s\n",
        get_name(), lifecycle_node);
      return lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN;
    }
  }

  /*
   * We send a Service request and indicate
   * that we want to invoke transition with
   * the id "transition".
   * By default, these transitions are
   * - configure
   * - activate
   * - cleanup
   * - shutdown
   */
  bool
  change_state(std::uint8_t transition, std::chrono::seconds time_out = 3_s)
  {
    auto request = std::make_shared<lifecycle_msgs::srv::ChangeState::Request>();
    request->transition.id = transition;

    if (!client_change_state_->wait_for_service(time_out)) {
      fprintf(stderr, "Service %s is not available.\n",
        client_change_state_->get_service_name().c_str());
      return false;
    }

    /*
     * We send the request with the transition
     * we want to invoke.
     */
    auto future_result = client_change_state_->async_send_request(request);
    /*
     * Let's wait until we have the answer from the node.
     * If the request times out, we return an unknown state.
     */
    auto future_status = wait_for_result(future_result, time_out);

    if (future_status != std::future_status::ready) {
      fprintf(stderr, "[%s] Failed to change state for node %s. Server timed out.\n",
        get_name(), lifecycle_node);
      return false;
    }

    /*
     * We have an answer, let's print
     * our success.
     */
    if (future_result.get()->success) {
      printf("[%s] Transition %d successfully triggered.\n",
        get_name(), static_cast<int>(transition));
      return true;
    } else {
      fprintf(stderr, "[%s] Failed to trigger transition %u\n",
        get_name(), static_cast<unsigned int>(transition));
      return false;
    }
  }

private:
  std::shared_ptr<rclcpp::client::Client<lifecycle_msgs::srv::GetState>> client_get_state_;
  std::shared_ptr<rclcpp::client::Client<lifecycle_msgs::srv::ChangeState>> client_change_state_;
};

/*
 * This is a little independent
 * script which triggers the
 * default lifecycle of a node.
 * It starts with configure, activate,
 * deactivate, activate, deactivate,
 * cleanup and finally shutdown
 */
void
callee_script(std::shared_ptr<LifecycleServiceClient> lc_client)
{
  auto sleep_time = 10_s;

  // configure
  {
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // activate
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // deactivate
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // activate it again
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // and deactivate it again
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // we cleanup
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_CLEANUP)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }

  // and finally shutdown
  {
    std::this_thread::sleep_for(sleep_time);
    if (!lc_client->change_state(lifecycle_msgs::msg::Transition::TRANSITION_SHUTDOWN)) {
      return;
    }
    if (!lc_client->get_state()) {
      return;
    }
  }
}

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);

  auto lc_client = std::make_shared<LifecycleServiceClient>("lc_client");
  lc_client->init();

  rclcpp::executors::SingleThreadedExecutor exe;
  exe.add_node(lc_client);

  std::shared_future<void> script = std::async(std::launch::async,
      std::bind(callee_script, lc_client));
  exe.spin_until_future_complete(script);

  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2005, Peter Sommerlad and IFS Institute for Software at HSR Rapperswil, Switzerland
 * All rights reserved.
 *
 * This library/application is free software; you can redistribute and/or modify it under the terms of
 * the license that is included with this library/application in the file license.txt.
 */

//--- test modules used --------------------------------------------------------
#include "TestSuite.h"

//--- module under test --------------------------------------------------------
#include "Socket.h"

//--- interface include --------------------------------------------------------
#include "ConnectorTest.h"

//--- standard modules used ----------------------------------------------------
#include "StringStream.h"
#include "System.h"
#include "Resolver.h"
#include "PoolAllocator.h"

//--- c-library modules used ---------------------------------------------------
#if defined(WIN32)
#include <io.h>
#endif

//---- ConnectorTest ----------------------------------------------------------------
ConnectorTest::ConnectorTest(TString tname) : TestCase(tname)
{
}

ConnectorTest::~ConnectorTest()
{
}

void ConnectorTest::setUp ()
{
	istream *is = System::OpenStream("ConnectorTest", "any");
	if ( is ) {
		fConfig.Import( *is );
		delete is;
	} else {
		assertEqual( "'read ConnectorTest.any'", "'could not read ConnectorTest.any'" );
	}
} // setUp

void ConnectorTest::tearDown ()
{
} // tearDown

void ConnectorTest::simpleConstructorTest()
{
	String theHost(fConfig["Testhost"]["ip"].AsString());
	long   thePort(fConfig["Testhost"]["port"].AsLong());
	TString msg("Connecting ");
	msg << theHost << ":" << thePort;
	Connector connector(theHost, thePort);

	// assert the internal state of the connector
	assertEqual( theHost, connector.GetAddress() );
	assertEqual( thePort, connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// assert the funtionality of the public api
	Socket *socket = connector.MakeSocket();
	t_assertm( socket != NULL , msg);				// these fail without HTTP Server
	t_assertm( connector.Use() != NULL , msg);
	t_assertm( connector.GetStream() != NULL, msg );
	delete socket;
	socket = 0;

} // simpleConstructorTest

void ConnectorTest::ConnectAndAssert(const char *host, long port, long timeout, bool threadLocal, bool shouldFail)
{
	Connector connector(host, port, timeout, String(), 0L, threadLocal);

	// assert the internal state of the connector

	// you can not assert this anymore since in case of dns round robin load balancing
	// the ip addresses do not have to be the same e.g. www.microsoft.com
//	assertEqual( Resolver::DNS2IPAddress(host), connector.GetAddress() );
	assertEqual( port, connector.fPort );
	assertEqual( timeout, connector.fConnectTimeout );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// assert the funtionality of the public api
	String assertMsg(host);
	assertMsg << ":" << port << " with timeout: " << timeout;
	String realMsg(assertMsg);
	realMsg << (( connector.Use() ) ? " has not failed" : " has failed");
	if ( shouldFail ) {
		assertMsg << " has failed";
		assertEqual(assertMsg, realMsg);
		t_assert( connector.Use() == NULL );
		t_assert( connector.GetStream() == NULL );
	} else {
		assertMsg << " has not failed";
		assertEqual(assertMsg, realMsg);
		t_assert( connector.Use() != NULL );
		t_assert( connector.GetStream() != NULL );
	}
}

void ConnectorTest::timeOutTest()
{
#if !defined(ATRAXOSA) && !defined(WIN32)
	ConnectAndAssert(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 1L, true, false);
#else
	ConnectAndAssert(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 1000L, false);
#endif
	ConnectAndAssert(fConfig["Testhost2"]["name"].AsString(), fConfig["Testhost2"]["port"].AsLong(), 5000L, true, false);
	ConnectAndAssert(fConfig["Testhost3"]["name"].AsString(), fConfig["Testhost3"]["port"].AsLong(), 100L, true, true);
} // timeOutTest

void ConnectorTest::bindingConstructorTest()
{
	ROAnything targetConfig = fConfig["Localhost"];
	const long SRC_PORT = 0L;
	const long TIMEOUT = 0L;
	// does not work on WIN32 without HTTP-server... FIXME
	Connector connector(targetConfig["ip"].AsString(), targetConfig["port"].AsLong(), TIMEOUT, targetConfig["ip"].AsString(), SRC_PORT, true);

	// assert the internal state of the connector
	assertEqual( Resolver::DNS2IPAddress(targetConfig["ip"].AsString()), connector.GetAddress() );
	assertEqual( targetConfig["port"].AsLong(), connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );
	assertEqual( Resolver::DNS2IPAddress(targetConfig["ip"].AsString()), connector.fSrcIPAdress );
	assertEqual( SRC_PORT, connector.fSrcPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// assert the funtionality of the public api
	t_assert( connector.Use() != NULL );
	t_assert( connector.GetStream() != NULL );
	if (SRC_PORT > 0) {
		assertEqual( SRC_PORT, connector.GetBoundPort() );
		// it is not possible to create a second socket object with a fully bound socket
		Socket *socket = connector.MakeSocket();
		t_assert( socket == NULL );
	}
}

void ConnectorTest::faultyConstructorTest()
{
	Connector connector((const char *)NULL, -1);
	assertEqual( "", connector.GetAddress() );
	assertEqual( -1, connector.fPort );

	assertEqual( (long)NULL, (long)connector.MakeSocket() );
	assertEqual( (long)NULL, (long)connector.Use() );
	assertEqual( (long)NULL, (long)connector.GetStream() );

} // faultyConstructorTest

void ConnectorTest::makeSocketTest()
{
	// connect to http server on localhost
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	// assert the internal state
	assertEqual( fConfig["Testhost"]["ip"].AsString(), connector.GetAddress() );
	assertEqual( fConfig["Testhost"]["port"].AsLong(), connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// check basic functionality
	Socket *s1 = connector.MakeSocket();
	Socket *s2 = connector.MakeSocket();

	long sock1 = 0, sock2 = 0;

	if (s1 != 0) {
		sock1 = s1->GetFd();
		t_assert( s1 != NULL );
		if (s2 != 0) {
			t_assert( s1 != s2 );
		} // if
		delete s1;
		assertEqual( -1L, ::closeSocket(sock1));
	}

	if (s2 != 0) {
		sock2 = s2->GetFd();

		t_assert( s2 != NULL );
		delete s2;
		// should result in an error because destructor closed socket
		assertEqual( -1L, ::closeSocket(sock2));
	}
} // makeSocketTest

void ConnectorTest::makeSocketWithReuseTest()
{
	// connect to http server on localhost
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	// assert the internal state
	assertEqual( fConfig["Testhost"]["ip"].AsString(), connector.GetAddress() );
	assertEqual( fConfig["Testhost"]["port"].AsLong(), connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// check basic functionality
	Socket *s1 = connector.MakeSocket(false);
	Socket *s2 = connector.MakeSocket(false);

	long sock1 = 0, sock2 = 0;

	if (s1 != 0) {
		sock1 = s1->GetFd();
		t_assert( s1 != NULL );
		if (s2 != 0) {
			t_assert( s1 != s2 );
		} // if
		delete s1;
		assertEqual( 0L, ::closeSocket(sock1));
	}

	if (s2 != 0) {
		sock2 = s2->GetFd();

		t_assert( s2 != NULL );
		delete s2;
		// should not result in an error because destructor has not closed socket
		assertEqual( 0L, ::closeSocket(sock2));
	}
} // makeSocketTest

void ConnectorTest::useSocketTest()
{
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	Socket *s1 = connector.Use();
	Socket *s2 = connector.Use();
	t_assert( s1 != NULL );			// these fail without HTTP Server
	t_assert( s2 != NULL );
	t_assert( s1 == s2 );

	// don't delete s1 or s2
	// conncector is the owner of the
	// object
//	delete s1; // should result in SEGV
//	delete s2; // should result in SEGV
} // useSocketTest

void ConnectorTest::getStreamTest()
{
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	iostream *s1 = connector.GetStream();
	iostream *s2 = connector.GetStream();
	t_assert( s1 != NULL );			// these fail without HTTP Server
	t_assert( s2 != NULL );
	t_assert( s1 == s2 );

	// don't delete s1 or s2
	// conncector is the owner of the
	// object
//	delete s1; // should result in SEGV
//	delete s2; // should result in SEGV
} // useSocketTest

void ConnectorTest::allocatorConstructorTest()
{
	{
		PoolAllocator pa(1, 8 * 1024, 21);
		TestStorageHooks tsh(&pa);
		Storage::SetHooks(&tsh);
		Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);
		Socket *socket = connector.MakeSocket();

		if ( t_assert( socket != NULL ) && t_assertm(&pa == socket->fAllocator, "allocator should match") ) {
			long socketfd = socket->GetFd();
			t_assert( socketfd > 0 );

			iostream *Ios = socket->GetStream();
			t_assert( Ios != NULL);
		}
		delete socket;
		Storage::SetHooks(0);
	}
	{
		TestStorageHooks tsh(Storage::Global());
		Storage::SetHooks(&tsh);
		Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), false);
		Socket *socket = connector.MakeSocket();

		if ( t_assert( socket != NULL ) && t_assertm(Storage::Global() == socket->fAllocator, "allocator should match") ) {
			long socketfd = socket->GetFd();
			t_assert( socketfd > 0 );

			iostream *Ios = socket->GetStream();
			t_assert( Ios != NULL);
		}
		delete socket;
		Storage::SetHooks(0);
	}
}

Test *ConnectorTest::suite ()
// collect all test cases for the SocketStream
{
	TestSuite *testSuite = new TestSuite;

	testSuite->addTest (NEW_CASE(ConnectorTest, simpleConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, bindingConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, bindingConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, faultyConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, makeSocketTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, makeSocketWithReuseTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, useSocketTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, getStreamTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, timeOutTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, allocatorConstructorTest));

	return testSuite;

} // suite
<commit_msg>added missing parameter<commit_after>/*
 * Copyright (c) 2005, Peter Sommerlad and IFS Institute for Software at HSR Rapperswil, Switzerland
 * All rights reserved.
 *
 * This library/application is free software; you can redistribute and/or modify it under the terms of
 * the license that is included with this library/application in the file license.txt.
 */

//--- test modules used --------------------------------------------------------
#include "TestSuite.h"

//--- module under test --------------------------------------------------------
#include "Socket.h"

//--- interface include --------------------------------------------------------
#include "ConnectorTest.h"

//--- standard modules used ----------------------------------------------------
#include "StringStream.h"
#include "System.h"
#include "Resolver.h"
#include "PoolAllocator.h"

//--- c-library modules used ---------------------------------------------------
#if defined(WIN32)
#include <io.h>
#endif

//---- ConnectorTest ----------------------------------------------------------------
ConnectorTest::ConnectorTest(TString tname) : TestCase(tname)
{
}

ConnectorTest::~ConnectorTest()
{
}

void ConnectorTest::setUp ()
{
	istream *is = System::OpenStream("ConnectorTest", "any");
	if ( is ) {
		fConfig.Import( *is );
		delete is;
	} else {
		assertEqual( "'read ConnectorTest.any'", "'could not read ConnectorTest.any'" );
	}
} // setUp

void ConnectorTest::tearDown ()
{
} // tearDown

void ConnectorTest::simpleConstructorTest()
{
	String theHost(fConfig["Testhost"]["ip"].AsString());
	long   thePort(fConfig["Testhost"]["port"].AsLong());
	TString msg("Connecting ");
	msg << theHost << ":" << thePort;
	Connector connector(theHost, thePort);

	// assert the internal state of the connector
	assertEqual( theHost, connector.GetAddress() );
	assertEqual( thePort, connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// assert the funtionality of the public api
	Socket *socket = connector.MakeSocket();
	t_assertm( socket != NULL , msg);				// these fail without HTTP Server
	t_assertm( connector.Use() != NULL , msg);
	t_assertm( connector.GetStream() != NULL, msg );
	delete socket;
	socket = 0;

} // simpleConstructorTest

void ConnectorTest::ConnectAndAssert(const char *host, long port, long timeout, bool threadLocal, bool shouldFail)
{
	Connector connector(host, port, timeout, String(), 0L, threadLocal);

	// assert the internal state of the connector

	// you can not assert this anymore since in case of dns round robin load balancing
	// the ip addresses do not have to be the same e.g. www.microsoft.com
//	assertEqual( Resolver::DNS2IPAddress(host), connector.GetAddress() );
	assertEqual( port, connector.fPort );
	assertEqual( timeout, connector.fConnectTimeout );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// assert the funtionality of the public api
	String assertMsg(host);
	assertMsg << ":" << port << " with timeout: " << timeout;
	String realMsg(assertMsg);
	realMsg << (( connector.Use() ) ? " has not failed" : " has failed");
	if ( shouldFail ) {
		assertMsg << " has failed";
		assertEqual(assertMsg, realMsg);
		t_assert( connector.Use() == NULL );
		t_assert( connector.GetStream() == NULL );
	} else {
		assertMsg << " has not failed";
		assertEqual(assertMsg, realMsg);
		t_assert( connector.Use() != NULL );
		t_assert( connector.GetStream() != NULL );
	}
}

void ConnectorTest::timeOutTest()
{
#if !defined(ATRAXOSA) && !defined(WIN32)
	ConnectAndAssert(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 1L, true, false);
#else
	ConnectAndAssert(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 1000L, true, false);
#endif
	ConnectAndAssert(fConfig["Testhost2"]["name"].AsString(), fConfig["Testhost2"]["port"].AsLong(), 5000L, true, false);
	ConnectAndAssert(fConfig["Testhost3"]["name"].AsString(), fConfig["Testhost3"]["port"].AsLong(), 100L, true, true);
} // timeOutTest

void ConnectorTest::bindingConstructorTest()
{
	ROAnything targetConfig = fConfig["Localhost"];
	const long SRC_PORT = 0L;
	const long TIMEOUT = 0L;
	// does not work on WIN32 without HTTP-server... FIXME
	Connector connector(targetConfig["ip"].AsString(), targetConfig["port"].AsLong(), TIMEOUT, targetConfig["ip"].AsString(), SRC_PORT, true);

	// assert the internal state of the connector
	assertEqual( Resolver::DNS2IPAddress(targetConfig["ip"].AsString()), connector.GetAddress() );
	assertEqual( targetConfig["port"].AsLong(), connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );
	assertEqual( Resolver::DNS2IPAddress(targetConfig["ip"].AsString()), connector.fSrcIPAdress );
	assertEqual( SRC_PORT, connector.fSrcPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// assert the funtionality of the public api
	t_assert( connector.Use() != NULL );
	t_assert( connector.GetStream() != NULL );
	if (SRC_PORT > 0) {
		assertEqual( SRC_PORT, connector.GetBoundPort() );
		// it is not possible to create a second socket object with a fully bound socket
		Socket *socket = connector.MakeSocket();
		t_assert( socket == NULL );
	}
}

void ConnectorTest::faultyConstructorTest()
{
	Connector connector((const char *)NULL, -1);
	assertEqual( "", connector.GetAddress() );
	assertEqual( -1, connector.fPort );

	assertEqual( (long)NULL, (long)connector.MakeSocket() );
	assertEqual( (long)NULL, (long)connector.Use() );
	assertEqual( (long)NULL, (long)connector.GetStream() );

} // faultyConstructorTest

void ConnectorTest::makeSocketTest()
{
	// connect to http server on localhost
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	// assert the internal state
	assertEqual( fConfig["Testhost"]["ip"].AsString(), connector.GetAddress() );
	assertEqual( fConfig["Testhost"]["port"].AsLong(), connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// check basic functionality
	Socket *s1 = connector.MakeSocket();
	Socket *s2 = connector.MakeSocket();

	long sock1 = 0, sock2 = 0;

	if (s1 != 0) {
		sock1 = s1->GetFd();
		t_assert( s1 != NULL );
		if (s2 != 0) {
			t_assert( s1 != s2 );
		} // if
		delete s1;
		assertEqual( -1L, ::closeSocket(sock1));
	}

	if (s2 != 0) {
		sock2 = s2->GetFd();

		t_assert( s2 != NULL );
		delete s2;
		// should result in an error because destructor closed socket
		assertEqual( -1L, ::closeSocket(sock2));
	}
} // makeSocketTest

void ConnectorTest::makeSocketWithReuseTest()
{
	// connect to http server on localhost
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	// assert the internal state
	assertEqual( fConfig["Testhost"]["ip"].AsString(), connector.GetAddress() );
	assertEqual( fConfig["Testhost"]["port"].AsLong(), connector.fPort );
	assertEqual( (long)NULL, (long)connector.fSocket );

	// check basic functionality
	Socket *s1 = connector.MakeSocket(false);
	Socket *s2 = connector.MakeSocket(false);

	long sock1 = 0, sock2 = 0;

	if (s1 != 0) {
		sock1 = s1->GetFd();
		t_assert( s1 != NULL );
		if (s2 != 0) {
			t_assert( s1 != s2 );
		} // if
		delete s1;
		assertEqual( 0L, ::closeSocket(sock1));
	}

	if (s2 != 0) {
		sock2 = s2->GetFd();

		t_assert( s2 != NULL );
		delete s2;
		// should not result in an error because destructor has not closed socket
		assertEqual( 0L, ::closeSocket(sock2));
	}
} // makeSocketTest

void ConnectorTest::useSocketTest()
{
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	Socket *s1 = connector.Use();
	Socket *s2 = connector.Use();
	t_assert( s1 != NULL );			// these fail without HTTP Server
	t_assert( s2 != NULL );
	t_assert( s1 == s2 );

	// don't delete s1 or s2
	// conncector is the owner of the
	// object
//	delete s1; // should result in SEGV
//	delete s2; // should result in SEGV
} // useSocketTest

void ConnectorTest::getStreamTest()
{
	Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);

	iostream *s1 = connector.GetStream();
	iostream *s2 = connector.GetStream();
	t_assert( s1 != NULL );			// these fail without HTTP Server
	t_assert( s2 != NULL );
	t_assert( s1 == s2 );

	// don't delete s1 or s2
	// conncector is the owner of the
	// object
//	delete s1; // should result in SEGV
//	delete s2; // should result in SEGV
} // useSocketTest

void ConnectorTest::allocatorConstructorTest()
{
	{
		PoolAllocator pa(1, 8 * 1024, 21);
		TestStorageHooks tsh(&pa);
		Storage::SetHooks(&tsh);
		Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), 0L, String(), 0L, true);
		Socket *socket = connector.MakeSocket();

		if ( t_assert( socket != NULL ) && t_assertm(&pa == socket->fAllocator, "allocator should match") ) {
			long socketfd = socket->GetFd();
			t_assert( socketfd > 0 );

			iostream *Ios = socket->GetStream();
			t_assert( Ios != NULL);
		}
		delete socket;
		Storage::SetHooks(0);
	}
	{
		TestStorageHooks tsh(Storage::Global());
		Storage::SetHooks(&tsh);
		Connector connector(fConfig["Testhost"]["ip"].AsString(), fConfig["Testhost"]["port"].AsLong(), false);
		Socket *socket = connector.MakeSocket();

		if ( t_assert( socket != NULL ) && t_assertm(Storage::Global() == socket->fAllocator, "allocator should match") ) {
			long socketfd = socket->GetFd();
			t_assert( socketfd > 0 );

			iostream *Ios = socket->GetStream();
			t_assert( Ios != NULL);
		}
		delete socket;
		Storage::SetHooks(0);
	}
}

Test *ConnectorTest::suite ()
// collect all test cases for the SocketStream
{
	TestSuite *testSuite = new TestSuite;

	testSuite->addTest (NEW_CASE(ConnectorTest, simpleConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, bindingConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, bindingConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, faultyConstructorTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, makeSocketTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, makeSocketWithReuseTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, useSocketTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, getStreamTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, timeOutTest));
	testSuite->addTest (NEW_CASE(ConnectorTest, allocatorConstructorTest));

	return testSuite;

} // suite
<|endoftext|>


<commit_before><commit_msg>fix compile error and warning in MSVC<commit_after><|endoftext|>


<commit_before>#include <bitcoin/network/network.hpp>
#include <bitcoin/util/logger.hpp>

#include <functional>
#include <iostream>

using std::placeholders::_1;
using std::placeholders::_2;
using namespace libbitcoin;

void error_exit(const std::string& message, int status=1)
{
    log_error() << "net: " << message;
    exit(status);
}

std::mutex mutex;
std::condition_variable condition;
size_t inv_count = 0;

void receive_inv(network_ptr net, channel_handle chandle,
    const message::inv& packet)
{
    log_info() << "Received:";
    for (const message::inv_vect& ivv: packet.invs)
    {
        if (ivv.type != message::inv_type::block)
            log_info() << "  --";
        else
            log_info() << "  " << pretty_hex(ivv.hash);
    }
    net->subscribe_inv(chandle, std::bind(&receive_inv, net, chandle, _1));
    std::unique_lock<std::mutex> lock(mutex);
    ++inv_count;
    condition.notify_one();
}

void handle_send_getblock(const std::error_code& ec)
{
    if (ec)
        error_exit(ec.message());
}

message::getblocks create_getblocks_message()
{
    message::getblocks packet;
    hash_digest genesis{0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xd6, 0x68,
                        0x9c, 0x08, 0x5a, 0xe1, 0x65, 0x83, 0x1e, 0x93,
                        0x4f, 0xf7, 0x63, 0xae, 0x46, 0xa2, 0xa6, 0xc1,
                        0x72, 0xb3, 0xf1, 0xb6, 0x0a, 0x8c, 0xe2, 0x6f};
    packet.locator_start_hashes.push_back(genesis);
    packet.hash_stop = hash_digest{0, 0, 0, 0, 0, 0, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0};
    return packet;
}

std::mutex version_switch_mutex;
size_t version_count = 0;

void handle_verack_sent(network_ptr net, channel_handle chandle,
    const std::error_code& ec)
{
    if (ec)
        error_exit(ec.message());
    std::unique_lock<std::mutex> lock(version_switch_mutex);
    ++version_count;
    if (version_count == 3)
        net->send(chandle, create_getblocks_message(),
            std::bind(&handle_send_getblock, _1));
}

void receive_version(network_ptr net, channel_handle chandle,
    const message::version&)
{
    net->send(chandle, message::verack(),
        std::bind(&handle_verack_sent, net, chandle, _1));
    std::unique_lock<std::mutex> lock(version_switch_mutex);
    ++version_count;
    if (version_count == 3)
        net->send(chandle, create_getblocks_message(),
            std::bind(&handle_send_getblock, _1));
}

void receive_verack(network_ptr net, channel_handle chandle,
    const message::verack&)
{
    std::unique_lock<std::mutex> lock(version_switch_mutex);
    ++version_count;
    if (version_count == 3)
        net->send(chandle, create_getblocks_message(),
            std::bind(&handle_send_getblock, _1));
}

void handle_connect(network_ptr net, const std::error_code& ec,
    channel_handle chandle)
{
    if (ec)
        error_exit(ec.message());
    net->subscribe_inv(chandle, std::bind(&receive_inv, net, chandle, _1));
    net->subscribe_version(chandle,
        std::bind(&receive_version, net, chandle, _1));
    net->subscribe_verack(chandle,
        std::bind(&receive_verack, net, chandle, _1));
}

int main()
{
    network_ptr net(new network_impl);
    net->connect("localhost", 8333,
        std::bind(&handle_connect, net, _1, _2));

    std::unique_lock<std::mutex> lock(mutex);
    condition.wait(lock, []{ return inv_count >= 500; });
    return 0;
}

<commit_msg>Use std::atomic instead of std::mutex + size_t for barrier in nettest.<commit_after>#include <bitcoin/network/network.hpp>
#include <bitcoin/util/logger.hpp>

#include <atomic>
#include <functional>
#include <iostream>

using std::placeholders::_1;
using std::placeholders::_2;
using namespace libbitcoin;

void error_exit(const std::string& message, int status=1)
{
    log_error() << "net: " << message;
    exit(status);
}

std::mutex mutex;
std::condition_variable condition;
size_t inv_count = 0;

void receive_inv(network_ptr net, channel_handle chandle,
    const message::inv& packet)
{
    log_info() << "Received:";
    for (const message::inv_vect& ivv: packet.invs)
    {
        if (ivv.type != message::inv_type::block)
            log_info() << "  --";
        else
            log_info() << "  " << pretty_hex(ivv.hash);
    }
    net->subscribe_inv(chandle, std::bind(&receive_inv, net, chandle, _1));
    std::unique_lock<std::mutex> lock(mutex);
    ++inv_count;
    condition.notify_one();
}

void handle_send_getblock(const std::error_code& ec)
{
    if (ec)
        error_exit(ec.message());
}

message::getblocks create_getblocks_message()
{
    message::getblocks packet;
    hash_digest genesis{0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xd6, 0x68,
                        0x9c, 0x08, 0x5a, 0xe1, 0x65, 0x83, 0x1e, 0x93,
                        0x4f, 0xf7, 0x63, 0xae, 0x46, 0xa2, 0xa6, 0xc1,
                        0x72, 0xb3, 0xf1, 0xb6, 0x0a, 0x8c, 0xe2, 0x6f};
    packet.locator_start_hashes.push_back(genesis);
    packet.hash_stop = hash_digest{0, 0, 0, 0, 0, 0, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0,
                                   0, 0, 0, 0, 0, 0, 0, 0};
    return packet;
}

typedef std::atomic<size_t> atomic_counter;
typedef std::shared_ptr<atomic_counter> atomic_counter_ptr;
typedef std::function<void ()> callback_handler;

const size_t clearance_count = 2;

void send_getblocks(network_ptr net, channel_handle chandle)
{
    net->send(chandle, create_getblocks_message(),
        std::bind(&handle_send_getblock, _1));
}

void handle_verack_sent(const std::error_code& ec,
    atomic_counter_ptr counter, callback_handler callback)
{
    if (ec)
        error_exit(ec.message());
    if (++(*counter) == clearance_count)
        callback();
}

void receive_version(channel_handle chandle, const message::version&,
    network_ptr net, atomic_counter_ptr counter, callback_handler callback)
{
    net->send(chandle, message::verack(),
        std::bind(&handle_verack_sent, _1, counter, callback));
}

void receive_verack(const message::verack&,
    atomic_counter_ptr counter, callback_handler callback)
{
    if (++(*counter) == clearance_count)
        callback();
}

void handle_connect(const std::error_code& ec, channel_handle chandle,
    network_ptr net)
{
    if (ec)
        error_exit(ec.message());
    net->subscribe_inv(chandle, std::bind(&receive_inv, net, chandle, _1));

    atomic_counter_ptr counter(new atomic_counter(0));
    callback_handler callback = std::bind(&send_getblocks, net, chandle);
    net->subscribe_version(chandle,
        std::bind(&receive_version, chandle, _1, net, counter, callback));
    net->subscribe_verack(chandle,
        std::bind(&receive_verack, _1, counter, callback));
}

int main()
{
    network_ptr net(new network_impl);
    net->connect("localhost", 8333, std::bind(&handle_connect, _1, _2, net));

    std::unique_lock<std::mutex> lock(mutex);
    condition.wait(lock, []{ return inv_count >= 500; });
    return 0;
}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: fmpage.cxx,v $
 *
 *  $Revision: 1.12 $
 *
 *  last change: $Author: kz $ $Date: 2005-03-18 18:42:54 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#pragma hdrstop

#define ENABLE_BYTESTRING_STREAM_OPERATORS

#ifndef _SVX_FMPAGE_HXX
#include "fmpage.hxx"
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif

#ifndef _SVX_FMMODEL_HXX
#include "fmmodel.hxx"
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMOBJ_HXX
#include "fmobj.hxx"
#endif
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMRESIDS_HRC
#include "fmresids.hrc"
#endif
#endif

#ifndef _SHL_HXX
#include <tools/shl.hxx>
#endif

#ifndef _SVX_DIALMGR_HXX
#include "dialmgr.hxx"
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMPGEIMP_HXX
#include "fmpgeimp.hxx"
#endif
#endif

#ifndef _SVDIO_HXX //autogen
#include "svdio.hxx"
#endif

#ifndef SVX_LIGHT
#ifndef _SFX_OBJSH_HXX //autogen
#include <sfx2/objsh.hxx>
#endif
#endif

#ifndef _SVDITER_HXX //autogen
#include "svditer.hxx"
#endif

#ifndef _SVDVIEW_HXX //autogen
#include <svdview.hxx>
#endif

#ifndef _URLOBJ_HXX //autogen
#include <tools/urlobj.hxx>
#endif

#ifndef _SV_HELP_HXX //autogen
#include <vcl/help.hxx>
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMGLOB_HXX
#include "fmglob.hxx"
#endif
#ifndef _SVX_FMPROP_HRC
#include "fmprop.hrc"
#endif
#ifndef _SVX_FMUNDO_HXX
#include "fmundo.hxx"
#endif
#ifndef _SVX_FMTOOLS_HXX
#include "fmtools.hxx"
#endif
using namespace ::svxform;
#endif

#ifndef _COMPHELPER_PROPERTY_HXX_
#include <comphelper/property.hxx>
#endif

using com::sun::star::uno::Reference;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::container::XChild;

TYPEINIT1(FmFormPage, SdrPage);

//------------------------------------------------------------------
FmFormPage::FmFormPage(FmFormModel& rModel, StarBASIC* _pBasic, FASTBOOL bMasterPage)
           :SdrPage(rModel, bMasterPage)
           ,pBasic(_pBasic)
#ifndef SVX_LIGHT
           ,pImpl(new FmFormPageImpl(this))
#else
           ,pImpl(NULL)
#endif
{
}

//------------------------------------------------------------------
FmFormPage::FmFormPage(const FmFormPage& rPage)
           :SdrPage(rPage)
           ,pBasic(0)
#ifndef SVX_LIGHT
           ,pImpl(new FmFormPageImpl(this, *rPage.GetImpl()))
#else
           ,pImpl(NULL)
#endif
{
    aPageName = rPage.aPageName;
}

//------------------------------------------------------------------
FmFormPage::~FmFormPage()
{
#ifndef SVX_LIGHT
    delete pImpl;
#endif
}

//------------------------------------------------------------------
void FmFormPage::SetModel(SdrModel* pNewModel)
{
    /* #35055# */
    // we want to call the super's "SetModel" method even if the model is the
    // same, in case code somewhere in the system depends on it.  But our code
    // doesn't, so get the old model to do a check.
    SdrModel *pOldModel = GetModel();

    SdrPage::SetModel( pNewModel );

    /* #35055# */
    if ( ( pOldModel != pNewModel ) && pImpl )
    {
        try
        {
            if ( pImpl->m_xForms.is() )
            {
                // we want to keep the current collection, just reset the model
                // with which it's associated.
                Reference< XChild > xAsChild( pImpl->m_xForms, UNO_QUERY );
                if ( xAsChild.is() )
                {
                    FmFormModel* pDrawModel = (FmFormModel*) GetModel();
                    SfxObjectShell* pObjShell = pDrawModel->GetObjectShell();
                    if ( pObjShell )
                        xAsChild->setParent( pObjShell->GetModel() );
                }
            }
        }
        catch( ::com::sun::star::uno::Exception ex )
        {
            OSL_ENSURE( sal_False, "UNO Exception caught resetting model for pImpl (FmFormPageImpl) in FmFormPage::SetModel" );
        }
    }
}

//------------------------------------------------------------------
SdrPage* FmFormPage::Clone() const
{
    return new FmFormPage(*this);
    // hier fehlt noch ein kopieren der Objekte
}

//------------------------------------------------------------------
void FmFormPage::NbcInsertObject(SdrObject* pObj,
                                 sal_uInt32 nPos,
                                 const SdrInsertReason* pReason)
{
    SdrPage::NbcInsertObject(pObj, nPos, pReason);
}

//------------------------------------------------------------------
void FmFormPage::InsertObject(SdrObject* pObj, sal_uInt32 nPos,
                              const SdrInsertReason* pReason)
{
    SdrPage::InsertObject( pObj, nPos, pReason );
#ifndef SVX_LIGHT
    if (GetModel() && (!pReason || pReason->GetReason() != SDRREASON_STREAMING))
        ((FmFormModel*)GetModel())->GetUndoEnv().Inserted(pObj);
#endif
}

#ifndef SVX_LIGHT
//------------------------------------------------------------------
void FmFormPage::InsertFormObjectEnsureEnv(const SdrObject* _pSourceObject, SdrObject* _pClone, sal_Bool _bTryPreserveName, sal_uInt32 _nPos,
    const SdrInsertReason* _pReason)
{
    DBG_ERROR("FmFormPage::InsertFormObjectEnsureEnv : obsolete method used (maybe I should write a changes mail ?) !");
    InsertObject(_pClone, _nPos, _pReason);
    return;
}

//------------------------------------------------------------------
sal_Bool FmFormPage::EnsureFormObjectEnv(const SdrObject* _pObj)
{
    DBG_ERROR("FmFormPage::EnsureFormObjectEnv : obsolete method used (maybe I should write a changes mail ?) !");
    return sal_False;
}
#endif

//------------------------------------------------------------------
const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer > & FmFormPage::GetForms( bool _bForceCreate ) const
{
#ifndef SVX_LIGHT
    return pImpl->getForms( _bForceCreate );
#else
    static ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer >  aRef;
    return aRef;
#endif
}

//------------------------------------------------------------------
sal_Bool FmFormPage::RequestHelp( Window* pWindow, SdrView* pView,
                              const HelpEvent& rEvt )
{
#ifndef SVX_LIGHT
    if( !pView->IsAction() )
    {
        Point aPos = rEvt.GetMousePosPixel();
        aPos = pWindow->ScreenToOutputPixel( aPos );
        aPos = pWindow->PixelToLogic( aPos );

        SdrObject* pObj = NULL;
        SdrPageView* pPV = NULL;
        if( pView->PickObj( aPos, 0, pObj, pPV, SDRSEARCH_DEEP ) )
        {
            // Ein Object getroffen
            if( pObj->ISA(FmFormObj) )
            {
                UniString aHelpText;
                ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xSet(((FmFormObj*)pObj)->GetUnoControlModel(), ::com::sun::star::uno::UNO_QUERY);
                if (xSet.is())
                {
                    if (::comphelper::hasProperty(FM_PROP_HELPTEXT, xSet))
                        aHelpText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_HELPTEXT)).getStr();

                    if (!aHelpText.Len() && ::comphelper::hasProperty(FM_PROP_TARGET_URL, xSet))
                    {
                        ::rtl::OUString aText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_TARGET_URL));
                        INetURLObject aUrl(aText);

                        // testen, ob es ein Protokoll-Typ ist, den ich anzeigen will
                        INetProtocol aProtocol = aUrl.GetProtocol();
                        static const INetProtocol s_aQuickHelpSupported[] =
                            {   INET_PROT_FTP, INET_PROT_HTTP, INET_PROT_FILE, INET_PROT_MAILTO, INET_PROT_NEWS,
                                INET_PROT_HTTPS, INET_PROT_JAVASCRIPT, INET_PROT_IMAP, INET_PROT_POP3,
                                INET_PROT_VIM, INET_PROT_LDAP
                            };
                        for (sal_uInt16 i=0; i<sizeof(s_aQuickHelpSupported)/sizeof(s_aQuickHelpSupported[0]); ++i)
                            if (s_aQuickHelpSupported[i] == aProtocol)
                            {
                                aHelpText = INetURLObject::decode(aUrl.GetURLNoPass(), '%', INetURLObject::DECODE_UNAMBIGUOUS);
                                break;
                            }
                    }
                }
                if ( aHelpText.Len() != 0 )
                {
                    // Hilfe anzeigen
                    Rectangle aItemRect = pObj->GetCurrentBoundRect();
                    aItemRect = pWindow->LogicToPixel( aItemRect );
                    Point aPt = pWindow->OutputToScreenPixel( aItemRect.TopLeft() );
                    aItemRect.Left()   = aPt.X();
                    aItemRect.Top()    = aPt.Y();
                    aPt = pWindow->OutputToScreenPixel( aItemRect.BottomRight() );
                    aItemRect.Right()  = aPt.X();
                    aItemRect.Bottom() = aPt.Y();
                    if( rEvt.GetMode() == HELPMODE_BALLOON )
                        Help::ShowBalloon( pWindow, aItemRect.Center(), aItemRect, aHelpText);
                    else
                        Help::ShowQuickHelp( pWindow, aItemRect, aHelpText );
                }
                return sal_True;
            }
        }
    }
#endif
    return sal_False;
}

//------------------------------------------------------------------
SdrObject* FmFormPage::NbcRemoveObject(sal_uInt32 nObjNum)
{
    return SdrPage::NbcRemoveObject(nObjNum);
}

//------------------------------------------------------------------
SdrObject* FmFormPage::RemoveObject(sal_uInt32 nObjNum)
{
    SdrObject* pObj = SdrPage::RemoveObject(nObjNum);
#ifndef SVX_LIGHT
    if (pObj && GetModel())
        ((FmFormModel*)GetModel())->GetUndoEnv().Removed(pObj);
#endif
    return pObj;
}

//------------------------------------------------------------------
SdrObject* FmFormPage::NbcReplaceObject(SdrObject* pNewObj, sal_uInt32 nObjNum)
{
    return SdrPage::NbcReplaceObject(pNewObj, nObjNum);
}

//------------------------------------------------------------------
SdrObject* FmFormPage::ReplaceObject(SdrObject* pNewObj, sal_uInt32 nObjNum)
{
    return SdrPage::ReplaceObject(pNewObj, nObjNum);
}

//------------------------------------------------------------------
void FmFormPage::SetBasic( StarBASIC* pBas )
{
}

void FmFormPage::RequestBasic()
{
}

XubString FmFormPage::GetLinkData( const XubString& rLinkName )
{
    return XubString();
}

void FmFormPage::SetLinkData( const XubString& rLinkName, const XubString& rLinkData )
{
}

void FmFormPage::UpdateLinkData( const XubString& rLinkName, const XubString& rLinkData )
{
}

SfxJSArray* FmFormPage::GetFormsArray() const
{
    return 0;
}

<commit_msg>INTEGRATION: CWS ooo19126 (1.12.254); FILE MERGED 2005/09/05 14:24:33 rt 1.12.254.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: fmpage.cxx,v $
 *
 *  $Revision: 1.13 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 22:54:29 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#pragma hdrstop

#define ENABLE_BYTESTRING_STREAM_OPERATORS

#ifndef _SVX_FMPAGE_HXX
#include "fmpage.hxx"
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif

#ifndef _SVX_FMMODEL_HXX
#include "fmmodel.hxx"
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMOBJ_HXX
#include "fmobj.hxx"
#endif
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMRESIDS_HRC
#include "fmresids.hrc"
#endif
#endif

#ifndef _SHL_HXX
#include <tools/shl.hxx>
#endif

#ifndef _SVX_DIALMGR_HXX
#include "dialmgr.hxx"
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMPGEIMP_HXX
#include "fmpgeimp.hxx"
#endif
#endif

#ifndef _SVDIO_HXX //autogen
#include "svdio.hxx"
#endif

#ifndef SVX_LIGHT
#ifndef _SFX_OBJSH_HXX //autogen
#include <sfx2/objsh.hxx>
#endif
#endif

#ifndef _SVDITER_HXX //autogen
#include "svditer.hxx"
#endif

#ifndef _SVDVIEW_HXX //autogen
#include <svdview.hxx>
#endif

#ifndef _URLOBJ_HXX //autogen
#include <tools/urlobj.hxx>
#endif

#ifndef _SV_HELP_HXX //autogen
#include <vcl/help.hxx>
#endif

#ifndef SVX_LIGHT
#ifndef _SVX_FMGLOB_HXX
#include "fmglob.hxx"
#endif
#ifndef _SVX_FMPROP_HRC
#include "fmprop.hrc"
#endif
#ifndef _SVX_FMUNDO_HXX
#include "fmundo.hxx"
#endif
#ifndef _SVX_FMTOOLS_HXX
#include "fmtools.hxx"
#endif
using namespace ::svxform;
#endif

#ifndef _COMPHELPER_PROPERTY_HXX_
#include <comphelper/property.hxx>
#endif

using com::sun::star::uno::Reference;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::container::XChild;

TYPEINIT1(FmFormPage, SdrPage);

//------------------------------------------------------------------
FmFormPage::FmFormPage(FmFormModel& rModel, StarBASIC* _pBasic, FASTBOOL bMasterPage)
           :SdrPage(rModel, bMasterPage)
           ,pBasic(_pBasic)
#ifndef SVX_LIGHT
           ,pImpl(new FmFormPageImpl(this))
#else
           ,pImpl(NULL)
#endif
{
}

//------------------------------------------------------------------
FmFormPage::FmFormPage(const FmFormPage& rPage)
           :SdrPage(rPage)
           ,pBasic(0)
#ifndef SVX_LIGHT
           ,pImpl(new FmFormPageImpl(this, *rPage.GetImpl()))
#else
           ,pImpl(NULL)
#endif
{
    aPageName = rPage.aPageName;
}

//------------------------------------------------------------------
FmFormPage::~FmFormPage()
{
#ifndef SVX_LIGHT
    delete pImpl;
#endif
}

//------------------------------------------------------------------
void FmFormPage::SetModel(SdrModel* pNewModel)
{
    /* #35055# */
    // we want to call the super's "SetModel" method even if the model is the
    // same, in case code somewhere in the system depends on it.  But our code
    // doesn't, so get the old model to do a check.
    SdrModel *pOldModel = GetModel();

    SdrPage::SetModel( pNewModel );

    /* #35055# */
    if ( ( pOldModel != pNewModel ) && pImpl )
    {
        try
        {
            if ( pImpl->m_xForms.is() )
            {
                // we want to keep the current collection, just reset the model
                // with which it's associated.
                Reference< XChild > xAsChild( pImpl->m_xForms, UNO_QUERY );
                if ( xAsChild.is() )
                {
                    FmFormModel* pDrawModel = (FmFormModel*) GetModel();
                    SfxObjectShell* pObjShell = pDrawModel->GetObjectShell();
                    if ( pObjShell )
                        xAsChild->setParent( pObjShell->GetModel() );
                }
            }
        }
        catch( ::com::sun::star::uno::Exception ex )
        {
            OSL_ENSURE( sal_False, "UNO Exception caught resetting model for pImpl (FmFormPageImpl) in FmFormPage::SetModel" );
        }
    }
}

//------------------------------------------------------------------
SdrPage* FmFormPage::Clone() const
{
    return new FmFormPage(*this);
    // hier fehlt noch ein kopieren der Objekte
}

//------------------------------------------------------------------
void FmFormPage::NbcInsertObject(SdrObject* pObj,
                                 sal_uInt32 nPos,
                                 const SdrInsertReason* pReason)
{
    SdrPage::NbcInsertObject(pObj, nPos, pReason);
}

//------------------------------------------------------------------
void FmFormPage::InsertObject(SdrObject* pObj, sal_uInt32 nPos,
                              const SdrInsertReason* pReason)
{
    SdrPage::InsertObject( pObj, nPos, pReason );
#ifndef SVX_LIGHT
    if (GetModel() && (!pReason || pReason->GetReason() != SDRREASON_STREAMING))
        ((FmFormModel*)GetModel())->GetUndoEnv().Inserted(pObj);
#endif
}

#ifndef SVX_LIGHT
//------------------------------------------------------------------
void FmFormPage::InsertFormObjectEnsureEnv(const SdrObject* _pSourceObject, SdrObject* _pClone, sal_Bool _bTryPreserveName, sal_uInt32 _nPos,
    const SdrInsertReason* _pReason)
{
    DBG_ERROR("FmFormPage::InsertFormObjectEnsureEnv : obsolete method used (maybe I should write a changes mail ?) !");
    InsertObject(_pClone, _nPos, _pReason);
    return;
}

//------------------------------------------------------------------
sal_Bool FmFormPage::EnsureFormObjectEnv(const SdrObject* _pObj)
{
    DBG_ERROR("FmFormPage::EnsureFormObjectEnv : obsolete method used (maybe I should write a changes mail ?) !");
    return sal_False;
}
#endif

//------------------------------------------------------------------
const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer > & FmFormPage::GetForms( bool _bForceCreate ) const
{
#ifndef SVX_LIGHT
    return pImpl->getForms( _bForceCreate );
#else
    static ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer >  aRef;
    return aRef;
#endif
}

//------------------------------------------------------------------
sal_Bool FmFormPage::RequestHelp( Window* pWindow, SdrView* pView,
                              const HelpEvent& rEvt )
{
#ifndef SVX_LIGHT
    if( !pView->IsAction() )
    {
        Point aPos = rEvt.GetMousePosPixel();
        aPos = pWindow->ScreenToOutputPixel( aPos );
        aPos = pWindow->PixelToLogic( aPos );

        SdrObject* pObj = NULL;
        SdrPageView* pPV = NULL;
        if( pView->PickObj( aPos, 0, pObj, pPV, SDRSEARCH_DEEP ) )
        {
            // Ein Object getroffen
            if( pObj->ISA(FmFormObj) )
            {
                UniString aHelpText;
                ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >  xSet(((FmFormObj*)pObj)->GetUnoControlModel(), ::com::sun::star::uno::UNO_QUERY);
                if (xSet.is())
                {
                    if (::comphelper::hasProperty(FM_PROP_HELPTEXT, xSet))
                        aHelpText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_HELPTEXT)).getStr();

                    if (!aHelpText.Len() && ::comphelper::hasProperty(FM_PROP_TARGET_URL, xSet))
                    {
                        ::rtl::OUString aText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_TARGET_URL));
                        INetURLObject aUrl(aText);

                        // testen, ob es ein Protokoll-Typ ist, den ich anzeigen will
                        INetProtocol aProtocol = aUrl.GetProtocol();
                        static const INetProtocol s_aQuickHelpSupported[] =
                            {   INET_PROT_FTP, INET_PROT_HTTP, INET_PROT_FILE, INET_PROT_MAILTO, INET_PROT_NEWS,
                                INET_PROT_HTTPS, INET_PROT_JAVASCRIPT, INET_PROT_IMAP, INET_PROT_POP3,
                                INET_PROT_VIM, INET_PROT_LDAP
                            };
                        for (sal_uInt16 i=0; i<sizeof(s_aQuickHelpSupported)/sizeof(s_aQuickHelpSupported[0]); ++i)
                            if (s_aQuickHelpSupported[i] == aProtocol)
                            {
                                aHelpText = INetURLObject::decode(aUrl.GetURLNoPass(), '%', INetURLObject::DECODE_UNAMBIGUOUS);
                                break;
                            }
                    }
                }
                if ( aHelpText.Len() != 0 )
                {
                    // Hilfe anzeigen
                    Rectangle aItemRect = pObj->GetCurrentBoundRect();
                    aItemRect = pWindow->LogicToPixel( aItemRect );
                    Point aPt = pWindow->OutputToScreenPixel( aItemRect.TopLeft() );
                    aItemRect.Left()   = aPt.X();
                    aItemRect.Top()    = aPt.Y();
                    aPt = pWindow->OutputToScreenPixel( aItemRect.BottomRight() );
                    aItemRect.Right()  = aPt.X();
                    aItemRect.Bottom() = aPt.Y();
                    if( rEvt.GetMode() == HELPMODE_BALLOON )
                        Help::ShowBalloon( pWindow, aItemRect.Center(), aItemRect, aHelpText);
                    else
                        Help::ShowQuickHelp( pWindow, aItemRect, aHelpText );
                }
                return sal_True;
            }
        }
    }
#endif
    return sal_False;
}

//------------------------------------------------------------------
SdrObject* FmFormPage::NbcRemoveObject(sal_uInt32 nObjNum)
{
    return SdrPage::NbcRemoveObject(nObjNum);
}

//------------------------------------------------------------------
SdrObject* FmFormPage::RemoveObject(sal_uInt32 nObjNum)
{
    SdrObject* pObj = SdrPage::RemoveObject(nObjNum);
#ifndef SVX_LIGHT
    if (pObj && GetModel())
        ((FmFormModel*)GetModel())->GetUndoEnv().Removed(pObj);
#endif
    return pObj;
}

//------------------------------------------------------------------
SdrObject* FmFormPage::NbcReplaceObject(SdrObject* pNewObj, sal_uInt32 nObjNum)
{
    return SdrPage::NbcReplaceObject(pNewObj, nObjNum);
}

//------------------------------------------------------------------
SdrObject* FmFormPage::ReplaceObject(SdrObject* pNewObj, sal_uInt32 nObjNum)
{
    return SdrPage::ReplaceObject(pNewObj, nObjNum);
}

//------------------------------------------------------------------
void FmFormPage::SetBasic( StarBASIC* pBas )
{
}

void FmFormPage::RequestBasic()
{
}

XubString FmFormPage::GetLinkData( const XubString& rLinkName )
{
    return XubString();
}

void FmFormPage::SetLinkData( const XubString& rLinkName, const XubString& rLinkData )
{
}

void FmFormPage::UpdateLinkData( const XubString& rLinkName, const XubString& rLinkData )
{
}

SfxJSArray* FmFormPage::GetFormsArray() const
{
    return 0;
}

<|endoftext|>