<commit_before>#include "openmc/source.h"

#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#define HAS_DYNAMIC_LINKING
#endif

#include <algorithm> // for move

#ifdef HAS_DYNAMIC_LINKING
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
#endif

#include <fmt/core.h>
#include "xtensor/xadapt.hpp"

#include "openmc/bank.h"
#include "openmc/cell.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/material.h"
#include "openmc/message_passing.h"
#include "openmc/mgxs_interface.h"
#include "openmc/nuclide.h"
#include "openmc/capi.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/state_point.h"
#include "openmc/xml_interface.h"

namespace openmc {

//==============================================================================
// Global variables
//==============================================================================

namespace model {

typedef Particle::Bank (*sample_t)(uint64_t  &seed);
sample_t custom_source_function;
void* custom_source_library;

std::vector<SourceDistribution> external_sources;

}

//==============================================================================
// SourceDistribution implementation
//==============================================================================

SourceDistribution::SourceDistribution(UPtrSpace space, UPtrAngle angle, UPtrDist energy)
  : space_{std::move(space)}, angle_{std::move(angle)}, energy_{std::move(energy)} { }

SourceDistribution::SourceDistribution(pugi::xml_node node)
{
  // Check for particle type
  if (check_for_node(node, "particle")) {
    auto temp_str = get_node_value(node, "particle", true, true);
    if (temp_str == "neutron") {
      particle_ = Particle::Type::neutron;
    } else if (temp_str == "photon") {
      particle_ = Particle::Type::photon;
      settings::photon_transport = true;
    } else {
      fatal_error(std::string("Unknown source particle type: ") + temp_str);
    }
  }

  // Check for source strength
  if (check_for_node(node, "strength")) {
    strength_ = std::stod(get_node_value(node, "strength"));
  }

  // Check for external source file
  if (check_for_node(node, "file")) {
    // Copy path of source file
    settings::path_source = get_node_value(node, "file", false, true);

    // Check if source file exists
    if (!file_exists(settings::path_source)) {
      fatal_error(fmt::format("Source file '{}' does not exist.",
        settings::path_source));
    }
  } else if (check_for_node(node, "library")) {
    settings::path_source_library = get_node_value(node, "library", false, true);
    if (!file_exists(settings::path_source_library)) {
      fatal_error(fmt::format("Source library '{}' does not exist.",
        settings::path_source_library));
    }
  } else {

    // Spatial distribution for external source
    if (check_for_node(node, "space")) {
      // Get pointer to spatial distribution
      pugi::xml_node node_space = node.child("space");

      // Check for type of spatial distribution and read
      std::string type;
      if (check_for_node(node_space, "type"))
        type = get_node_value(node_space, "type", true, true);
      if (type == "cartesian") {
        space_ = UPtrSpace{new CartesianIndependent(node_space)};
      } else if (type == "cylindrical") {
        space_ = UPtrSpace{new CylindricalIndependent(node_space)};
      } else if (type == "spherical") {
        space_ = UPtrSpace{new SphericalIndependent(node_space)};
      } else if (type == "box") {
        space_ = UPtrSpace{new SpatialBox(node_space)};
      } else if (type == "fission") {
        space_ = UPtrSpace{new SpatialBox(node_space, true)};
      } else if (type == "point") {
        space_ = UPtrSpace{new SpatialPoint(node_space)};
      } else {
        fatal_error(fmt::format(
          "Invalid spatial distribution for external source: {}", type));
      }

    } else {
      // If no spatial distribution specified, make it a point source
      space_ = UPtrSpace{new SpatialPoint()};
    }

    // Determine external source angular distribution
    if (check_for_node(node, "angle")) {
      // Get pointer to angular distribution
      pugi::xml_node node_angle = node.child("angle");

      // Check for type of angular distribution
      std::string type;
      if (check_for_node(node_angle, "type"))
        type = get_node_value(node_angle, "type", true, true);
      if (type == "isotropic") {
        angle_ = UPtrAngle{new Isotropic()};
      } else if (type == "monodirectional") {
        angle_ = UPtrAngle{new Monodirectional(node_angle)};
      } else if (type == "mu-phi") {
        angle_ = UPtrAngle{new PolarAzimuthal(node_angle)};
      } else {
        fatal_error(fmt::format(
          "Invalid angular distribution for external source: {}", type));
      }

    } else {
      angle_ = UPtrAngle{new Isotropic()};
    }

    // Determine external source energy distribution
    if (check_for_node(node, "energy")) {
      pugi::xml_node node_dist = node.child("energy");
      energy_ = distribution_from_xml(node_dist);
    } else {
      // Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1
      energy_ = UPtrDist{new Watt(0.988e6, 2.249e-6)};
    }
  }
}

Particle::Bank SourceDistribution::sample(uint64_t* seed) const
{
  Particle::Bank site;

  // Set weight to one by default
  site.wgt = 1.0;

  // Repeat sampling source location until a good site has been found
  bool found = false;
  int n_reject = 0;
  static int n_accept = 0;
  while (!found) {
    // Set particle type
    site.particle = particle_;

    // Sample spatial distribution
    site.r = space_->sample(seed);
    double xyz[] {site.r.x, site.r.y, site.r.z};

    // Now search to see if location exists in geometry
    int32_t cell_index, instance;
    int err = openmc_find_cell(xyz, &cell_index, &instance);
    found = (err != OPENMC_E_GEOMETRY);

    // Check if spatial site is in fissionable material
    if (found) {
      auto space_box = dynamic_cast<SpatialBox*>(space_.get());
      if (space_box) {
        if (space_box->only_fissionable()) {
          // Determine material
          const auto& c = model::cells[cell_index];
          auto mat_index = c->material_.size() == 1
            ? c->material_[0] : c->material_[instance];

          if (mat_index == MATERIAL_VOID) {
            found = false;
          } else {
            if (!model::materials[mat_index]->fissionable_) found = false;
          }
        }
      }
    }

    // Check for rejection
    if (!found) {
      ++n_reject;
      if (n_reject >= EXTSRC_REJECT_THRESHOLD &&
          static_cast<double>(n_accept)/n_reject <= EXTSRC_REJECT_FRACTION) {
        fatal_error("More than 95% of external source sites sampled were "
                    "rejected. Please check your external source definition.");
      }
    }
  }

  // Increment number of accepted samples
  ++n_accept;

  // Sample angle
  site.u = angle_->sample(seed);

  // Check for monoenergetic source above maximum particle energy
  auto p = static_cast<int>(particle_);
  auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
  if (energy_ptr) {
    auto energies = xt::adapt(energy_ptr->x());
    if (xt::any(energies > data::energy_max[p])) {
      fatal_error("Source energy above range of energies of at least "
                  "one cross section table");
    } else if (xt::any(energies < data::energy_min[p])) {
      fatal_error("Source energy below range of energies of at least "
                  "one cross section table");
    }
  }

  while (true) {
    // Sample energy spectrum
    site.E = energy_->sample(seed);

    // Resample if energy falls outside minimum or maximum particle energy
    if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) break;
  }

  // Set delayed group
  site.delayed_group = 0;

  return site;
}

//==============================================================================
// Non-member functions
//==============================================================================

void initialize_source()
{
  write_message("Initializing source particles...", 5);

  if (!settings::path_source.empty()) {
    // Read the source from a binary file instead of sampling from some
    // assumed source distribution

    write_message(fmt::format("Reading source file from {}...",
      settings::path_source), 6);

    // Open the binary file
    hid_t file_id = file_open(settings::path_source, 'r', true);

    // Read the file type
    std::string filetype;
    read_attribute(file_id, "filetype", filetype);

    // Check to make sure this is a source file
    if (filetype != "source" && filetype != "statepoint") {
      fatal_error("Specified starting source file not a source file type.");
    }

    // Read in the source bank
    read_source_bank(file_id);

    // Close file
    file_close(file_id);
  } else if (!settings::path_source_library.empty()) {

    write_message(fmt::format("Sampling from library source {}...",
      settings::path_source), 6);

    fill_source_bank_custom_source();

  } else {
    // Generation source sites from specified distribution in user input
    for (int64_t i = 0; i < simulation::work_per_rank; ++i) {
      // initialize random number seed
      int64_t id = simulation::total_gen*settings::n_particles +
        simulation::work_index[mpi::rank] + i + 1;
      uint64_t seed = init_seed(id, STREAM_SOURCE);

      // sample external source distribution
      simulation::source_bank[i] = sample_external_source(&seed);
    }
  }

  // Write out initial source
  if (settings::write_initial_source) {
    write_message("Writing out initial source...", 5);
    std::string filename = settings::path_output + "initial_source.h5";
    hid_t file_id = file_open(filename, 'w', true);
    write_source_bank(file_id);
    file_close(file_id);
  }
}

Particle::Bank sample_external_source(uint64_t* seed)
{
  // Determine total source strength
  double total_strength = 0.0;
  for (auto& s : model::external_sources)
    total_strength += s.strength();

  // Sample from among multiple source distributions
  int i = 0;
  if (model::external_sources.size() > 1) {
    double xi = prn(seed)*total_strength;
    double c = 0.0;
    for (; i < model::external_sources.size(); ++i) {
      c += model::external_sources[i].strength();
      if (xi < c) break;
    }
  }

  // Sample source site from i-th source distribution
  Particle::Bank site {model::external_sources[i].sample(seed)};

  // If running in MG, convert site.E to group
  if (!settings::run_CE) {
    site.E = lower_bound_index(data::mg.rev_energy_bins_.begin(),
      data::mg.rev_energy_bins_.end(), site.E);
    site.E = data::mg.num_energy_groups_ - site.E - 1.;
  }

  return site;
}

void free_memory_source()
{
  model::external_sources.clear();
}

//Load custom source library
void load_custom_source_library()
{
#ifdef HAS_DYNAMIC_LINKING

  // Open the library
  model::source_library = dlopen(settings::path_source_library.c_str(), RTLD_LAZY);
  if (!model::source_library) {
    fatal_error("Couldn't open source library " + settings::path_source_library);
  }

  // reset errors
  dlerror();

  // get the function from the library
  //using sample_t = Particle::Bank (*)(uint64_t* seed);
  model::sample_source = reinterpret_cast<model::sample_t>(dlsym(model::source_library, "sample_source"));

  // check for any dlsym errors
  auto dlsym_error = dlerror();
  if (dlsym_error) {
    dlclose(model::source_library);
    fatal_error(fmt::format("Couldn't open the sample_source symbol: {}", dlsym_error));
  }

#else
  fatal_error("Custom source libraries have not yet been implemented for "
    "non-POSIX systems");
#endif

}

//Release custom source library
void close_custom_source_library()
{
  dlclose(model::source_library);
}

//Sample source particle from custom library
Particle::Bank sample_custom_source_library(uint64_t* seed)
{
  return model::sample_source(*seed);
}

// fill the source bank from the external source
void fill_source_bank_custom_source()
{
  // Load the custom library
  load_custom_source_library();

  // Generation source sites from specified distribution in the
  // library source
  for (int64_t i = 0; i < simulation::work_per_rank; ++i) {
    // initialize random number seed
    int64_t id = (simulation::total_gen + overall_generation()) *
      settings::n_particles + simulation::work_index[mpi::rank] + i + 1;
     uint64_t seed = init_seed(id, STREAM_SOURCE);

    // sample custom library source
    simulation::source_bank[i] = sample_custom_source_library(&seed);
  }

  // release the library
  close_custom_source_library();
}

} // namespace openmc
<commit_msg>Rename custom source library and function and move to anonymous namespace<commit_after>#include "openmc/source.h"

#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#define HAS_DYNAMIC_LINKING
#endif

#include <algorithm> // for move

#ifdef HAS_DYNAMIC_LINKING
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
#endif

#include <fmt/core.h>
#include "xtensor/xadapt.hpp"

#include "openmc/bank.h"
#include "openmc/cell.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/material.h"
#include "openmc/message_passing.h"
#include "openmc/mgxs_interface.h"
#include "openmc/nuclide.h"
#include "openmc/capi.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/state_point.h"
#include "openmc/xml_interface.h"

namespace openmc {

//==============================================================================
// Global variables
//==============================================================================

namespace model {

std::vector<SourceDistribution> external_sources;

}

namespace {

typedef Particle::Bank (*sample_t)(uint64_t  &seed);
sample_t custom_source_function;
void* custom_source_library;

}

//==============================================================================
// SourceDistribution implementation
//==============================================================================

SourceDistribution::SourceDistribution(UPtrSpace space, UPtrAngle angle, UPtrDist energy)
  : space_{std::move(space)}, angle_{std::move(angle)}, energy_{std::move(energy)} { }

SourceDistribution::SourceDistribution(pugi::xml_node node)
{
  // Check for particle type
  if (check_for_node(node, "particle")) {
    auto temp_str = get_node_value(node, "particle", true, true);
    if (temp_str == "neutron") {
      particle_ = Particle::Type::neutron;
    } else if (temp_str == "photon") {
      particle_ = Particle::Type::photon;
      settings::photon_transport = true;
    } else {
      fatal_error(std::string("Unknown source particle type: ") + temp_str);
    }
  }

  // Check for source strength
  if (check_for_node(node, "strength")) {
    strength_ = std::stod(get_node_value(node, "strength"));
  }

  // Check for external source file
  if (check_for_node(node, "file")) {
    // Copy path of source file
    settings::path_source = get_node_value(node, "file", false, true);

    // Check if source file exists
    if (!file_exists(settings::path_source)) {
      fatal_error(fmt::format("Source file '{}' does not exist.",
        settings::path_source));
    }
  } else if (check_for_node(node, "library")) {
    settings::path_source_library = get_node_value(node, "library", false, true);
    if (!file_exists(settings::path_source_library)) {
      fatal_error(fmt::format("Source library '{}' does not exist.",
        settings::path_source_library));
    }
  } else {

    // Spatial distribution for external source
    if (check_for_node(node, "space")) {
      // Get pointer to spatial distribution
      pugi::xml_node node_space = node.child("space");

      // Check for type of spatial distribution and read
      std::string type;
      if (check_for_node(node_space, "type"))
        type = get_node_value(node_space, "type", true, true);
      if (type == "cartesian") {
        space_ = UPtrSpace{new CartesianIndependent(node_space)};
      } else if (type == "cylindrical") {
        space_ = UPtrSpace{new CylindricalIndependent(node_space)};
      } else if (type == "spherical") {
        space_ = UPtrSpace{new SphericalIndependent(node_space)};
      } else if (type == "box") {
        space_ = UPtrSpace{new SpatialBox(node_space)};
      } else if (type == "fission") {
        space_ = UPtrSpace{new SpatialBox(node_space, true)};
      } else if (type == "point") {
        space_ = UPtrSpace{new SpatialPoint(node_space)};
      } else {
        fatal_error(fmt::format(
          "Invalid spatial distribution for external source: {}", type));
      }

    } else {
      // If no spatial distribution specified, make it a point source
      space_ = UPtrSpace{new SpatialPoint()};
    }

    // Determine external source angular distribution
    if (check_for_node(node, "angle")) {
      // Get pointer to angular distribution
      pugi::xml_node node_angle = node.child("angle");

      // Check for type of angular distribution
      std::string type;
      if (check_for_node(node_angle, "type"))
        type = get_node_value(node_angle, "type", true, true);
      if (type == "isotropic") {
        angle_ = UPtrAngle{new Isotropic()};
      } else if (type == "monodirectional") {
        angle_ = UPtrAngle{new Monodirectional(node_angle)};
      } else if (type == "mu-phi") {
        angle_ = UPtrAngle{new PolarAzimuthal(node_angle)};
      } else {
        fatal_error(fmt::format(
          "Invalid angular distribution for external source: {}", type));
      }

    } else {
      angle_ = UPtrAngle{new Isotropic()};
    }

    // Determine external source energy distribution
    if (check_for_node(node, "energy")) {
      pugi::xml_node node_dist = node.child("energy");
      energy_ = distribution_from_xml(node_dist);
    } else {
      // Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1
      energy_ = UPtrDist{new Watt(0.988e6, 2.249e-6)};
    }
  }
}

Particle::Bank SourceDistribution::sample(uint64_t* seed) const
{
  Particle::Bank site;

  // Set weight to one by default
  site.wgt = 1.0;

  // Repeat sampling source location until a good site has been found
  bool found = false;
  int n_reject = 0;
  static int n_accept = 0;
  while (!found) {
    // Set particle type
    site.particle = particle_;

    // Sample spatial distribution
    site.r = space_->sample(seed);
    double xyz[] {site.r.x, site.r.y, site.r.z};

    // Now search to see if location exists in geometry
    int32_t cell_index, instance;
    int err = openmc_find_cell(xyz, &cell_index, &instance);
    found = (err != OPENMC_E_GEOMETRY);

    // Check if spatial site is in fissionable material
    if (found) {
      auto space_box = dynamic_cast<SpatialBox*>(space_.get());
      if (space_box) {
        if (space_box->only_fissionable()) {
          // Determine material
          const auto& c = model::cells[cell_index];
          auto mat_index = c->material_.size() == 1
            ? c->material_[0] : c->material_[instance];

          if (mat_index == MATERIAL_VOID) {
            found = false;
          } else {
            if (!model::materials[mat_index]->fissionable_) found = false;
          }
        }
      }
    }

    // Check for rejection
    if (!found) {
      ++n_reject;
      if (n_reject >= EXTSRC_REJECT_THRESHOLD &&
          static_cast<double>(n_accept)/n_reject <= EXTSRC_REJECT_FRACTION) {
        fatal_error("More than 95% of external source sites sampled were "
                    "rejected. Please check your external source definition.");
      }
    }
  }

  // Increment number of accepted samples
  ++n_accept;

  // Sample angle
  site.u = angle_->sample(seed);

  // Check for monoenergetic source above maximum particle energy
  auto p = static_cast<int>(particle_);
  auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
  if (energy_ptr) {
    auto energies = xt::adapt(energy_ptr->x());
    if (xt::any(energies > data::energy_max[p])) {
      fatal_error("Source energy above range of energies of at least "
                  "one cross section table");
    } else if (xt::any(energies < data::energy_min[p])) {
      fatal_error("Source energy below range of energies of at least "
                  "one cross section table");
    }
  }

  while (true) {
    // Sample energy spectrum
    site.E = energy_->sample(seed);

    // Resample if energy falls outside minimum or maximum particle energy
    if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) break;
  }

  // Set delayed group
  site.delayed_group = 0;

  return site;
}

//==============================================================================
// Non-member functions
//==============================================================================

void initialize_source()
{
  write_message("Initializing source particles...", 5);

  if (!settings::path_source.empty()) {
    // Read the source from a binary file instead of sampling from some
    // assumed source distribution

    write_message(fmt::format("Reading source file from {}...",
      settings::path_source), 6);

    // Open the binary file
    hid_t file_id = file_open(settings::path_source, 'r', true);

    // Read the file type
    std::string filetype;
    read_attribute(file_id, "filetype", filetype);

    // Check to make sure this is a source file
    if (filetype != "source" && filetype != "statepoint") {
      fatal_error("Specified starting source file not a source file type.");
    }

    // Read in the source bank
    read_source_bank(file_id);

    // Close file
    file_close(file_id);
  } else if (!settings::path_source_library.empty()) {

    write_message(fmt::format("Sampling from library source {}...",
      settings::path_source), 6);

    fill_source_bank_custom_source();

  } else {
    // Generation source sites from specified distribution in user input
    for (int64_t i = 0; i < simulation::work_per_rank; ++i) {
      // initialize random number seed
      int64_t id = simulation::total_gen*settings::n_particles +
        simulation::work_index[mpi::rank] + i + 1;
      uint64_t seed = init_seed(id, STREAM_SOURCE);

      // sample external source distribution
      simulation::source_bank[i] = sample_external_source(&seed);
    }
  }

  // Write out initial source
  if (settings::write_initial_source) {
    write_message("Writing out initial source...", 5);
    std::string filename = settings::path_output + "initial_source.h5";
    hid_t file_id = file_open(filename, 'w', true);
    write_source_bank(file_id);
    file_close(file_id);
  }
}

Particle::Bank sample_external_source(uint64_t* seed)
{
  // Determine total source strength
  double total_strength = 0.0;
  for (auto& s : model::external_sources)
    total_strength += s.strength();

  // Sample from among multiple source distributions
  int i = 0;
  if (model::external_sources.size() > 1) {
    double xi = prn(seed)*total_strength;
    double c = 0.0;
    for (; i < model::external_sources.size(); ++i) {
      c += model::external_sources[i].strength();
      if (xi < c) break;
    }
  }

  // Sample source site from i-th source distribution
  Particle::Bank site {model::external_sources[i].sample(seed)};

  // If running in MG, convert site.E to group
  if (!settings::run_CE) {
    site.E = lower_bound_index(data::mg.rev_energy_bins_.begin(),
      data::mg.rev_energy_bins_.end(), site.E);
    site.E = data::mg.num_energy_groups_ - site.E - 1.;
  }

  return site;
}

void free_memory_source()
{
  model::external_sources.clear();
}

//Load custom source library
void load_custom_source_library()
{
#ifdef HAS_DYNAMIC_LINKING

  // Open the library
  custom_source_library = dlopen(settings::path_source_library.c_str(), RTLD_LAZY);
  if (!custom_source_library) {
    fatal_error("Couldn't open source library " + settings::path_source_library);
  }

  // reset errors
  dlerror();

  // get the function from the library
  //using sample_t = Particle::Bank (*)(uint64_t* seed);
  custom_source_function = reinterpret_cast<sample_t>(dlsym(custom_source_library, "sample_source"));

  // check for any dlsym errors
  auto dlsym_error = dlerror();
  if (dlsym_error) {
    dlclose(custom_source_library);
    fatal_error(fmt::format("Couldn't open the sample_source symbol: {}", dlsym_error));
  }

#else
  fatal_error("Custom source libraries have not yet been implemented for "
    "non-POSIX systems");
#endif

}

//Release custom source library
void close_custom_source_library()
{
  dlclose(custom_source_library);
}

//Sample source particle from custom library
Particle::Bank sample_custom_source_library(uint64_t* seed)
{
  return custom_source_function(*seed);
}

// fill the source bank from the external source
void fill_source_bank_custom_source()
{
  // Load the custom library
  load_custom_source_library();

  // Generation source sites from specified distribution in the
  // library source
  for (int64_t i = 0; i < simulation::work_per_rank; ++i) {
    // initialize random number seed
    int64_t id = (simulation::total_gen + overall_generation()) *
      settings::n_particles + simulation::work_index[mpi::rank] + i + 1;
     uint64_t seed = init_seed(id, STREAM_SOURCE);

    // sample custom library source
    simulation::source_bank[i] = sample_custom_source_library(&seed);
  }

  // release the library
  close_custom_source_library();
}

} // namespace openmc
<|endoftext|>


<commit_before><commit_msg>update uoj10680<commit_after><|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_recovery_ffdc_cme.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
// *INDENT-OFF*

///
/// @file   p9_pm_recovery_ffdc_cme.C
/// @brief  Models CME platform for the FFDC collection of PM complex
///
/// *HWP HWP Owner:      Greg Still <stillgs@us.ibm.com>
/// *HWP FW Owner:       Prem S Jha <premjha2@in.ibm.com>
/// *HWP Team:           PM
/// *HWP Level:          2
/// *HWP Consumed by:    Hostboot
//
// *INDENT-OFF*
//--------------------------------------------------------------------------
// Includes
//--------------------------------------------------------------------------

#include <p9_pm_recovery_ffdc_cme.H>
#include <p9_hcd_memmap_cme_sram.H>
#include <p9_ppe_defs.H>
#include <stddef.h>
#include <endian.h>

 namespace p9_stop_recov_ffdc
 {
    PlatCme::PlatCme( const fapi2::Target< fapi2::TARGET_TYPE_PROC_CHIP > i_procChipTgt )
      : PlatPmComplex( i_procChipTgt,
                       FFDC_PPE_IMG_HDR_START,
                       FFDC_CME_TRACE_START,
                       FFDC_CME_DASH_BOARD_START,
                       PLAT_CME )
    { }

    //----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectFfdc( void * i_pHomerBuf )
    {
        FAPI_DBG(">> PlatCme::collectFfdc");
        fapi2::ReturnCode l_retCode = fapi2::FAPI2_RC_SUCCESS;
        auto l_exList =
            getProcChip().getChildren< fapi2::TARGET_TYPE_EX > ( fapi2::TARGET_STATE_PRESENT );
        uint8_t l_quadPos = 0;
        uint8_t l_exPos   = 0;
        uint8_t l_cmePos  = 0;
        uint8_t l_ffdcValdityVect = PPE_FFDC_ALL_VALID;
        uint8_t l_haltState = PPE_HALT_COND_UNKNOWN;
        uint8_t *l_pFfdcLoc = NULL;
        HomerFfdcRegion * l_pHomerFfdc =
                ( HomerFfdcRegion *)( (uint8_t *)i_pHomerBuf + FFDC_REGION_HOMER_BASE_OFFSET );

        for( auto ex : l_exList )
        {
            l_ffdcValdityVect = PPE_FFDC_ALL_VALID;

            FAPI_TRY( FAPI_ATTR_GET( fapi2::ATTR_CHIP_UNIT_POS, ex, l_cmePos ),
                      "FAPI_ATTR_GET Failed To Read EX Position" );

            if( !ex.isFunctional() )
            {
                //Marking CME FFDC region as Invalid
                FAPI_TRY( updateCmeFfdcHeader( l_pFfdcLoc, l_cmePos, l_ffdcValdityVect, l_haltState ),
                          "Failed To Update CME FFDC Header for CME 0x%0d", l_cmePos );
                continue;
            }

            l_exPos     =   l_cmePos % 2;
            l_quadPos   =   l_cmePos >> 1;
            l_pFfdcLoc = &l_pHomerFfdc->iv_quadFfdc[l_quadPos].iv_quadCmeBlock[l_exPos][0];

            FAPI_INF("CME FFDC Quad Pos %d Ex Pos %d ", l_quadPos, l_exPos );

            //In case of error , invalidate FFDC in header.

            // @TODO this is still after reset, which would have already
            // halted the ppe. We need a wa to record this before reset
            // and pass it down, or have a spl r-m-w update per member
            // of the PPE Header?
            // l_retCode = getPpeHaltState (
            //             getCmeBaseAddress (l_cmePos),
            //             l_haltState);

            l_retCode = collectPpeState ( getCmeBaseAddress (l_cmePos),
                                          l_pFfdcLoc );
            if ( l_retCode != fapi2::FAPI2_RC_SUCCESS )
            {
                FAPI_ERR ( "Error collecting CME State, CME Pos 0x08x",
                           l_cmePos );
                l_ffdcValdityVect &= ~PPE_STATE_VALID;
            }
            l_retCode = collectTrace( l_pFfdcLoc, ex );

            if( l_retCode )
            {
                FAPI_ERR("Error in collecting CME Trace CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_TRACE_VALID;
            }

            l_retCode = collectGlobals( l_pFfdcLoc, ex );

            if( l_retCode )
            {
                FAPI_ERR("Error in collecting CME Globals, CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_DASHBOARD_VALID;
            }

            l_retCode = collectImageHeader( l_pFfdcLoc, ex );

            if( l_retCode )
            {
                FAPI_ERR("Error in collecting CME Image header, CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_IMAGE_HEADER_VALID;
            }

            FAPI_TRY( updateCmeFfdcHeader( l_pFfdcLoc, l_cmePos, l_ffdcValdityVect, l_haltState ),
                      "Failed To Update CME FFDC Header for CME 0x%0d", l_cmePos );
        }

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectFfdc");
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectTrace( uint8_t * i_pTraceBuf,
                                             const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt  )
    {
        FAPI_DBG(">> PlatCme::collectTrace" );
        PpeFfdcLayout * l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pTraceBuf );

        uint8_t * l_pTraceLoc = &l_pCmeFfdc->iv_ppeTraces[0];

        FAPI_TRY( PlatPmComplex::collectSramInfo
                    ( i_exTgt,
                      l_pTraceLoc,
                      TRACES,
                      FFDC_PPE_TRACES_SIZE ),
                  "Trace Collection Failed" );

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectTrace" );
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode  PlatCme::collectGlobals( uint8_t * i_pCmeGlobals,
                                                const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt )
    {
        FAPI_DBG(">> PlatCme::collectGlobals" );
        PpeFfdcLayout * l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pCmeGlobals );
        uint8_t * l_pTraceLoc = &l_pCmeFfdc->iv_ppeGlobals[0];

        FAPI_TRY( PlatPmComplex::collectSramInfo
                    ( i_exTgt,
                      l_pTraceLoc,
                      DASH_BOARD_VAR,
                      FFDC_PPE_SCORE_BOARD_SIZE ),
                  "Failed To Collect CME Global Variables" );

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectGlobals" );
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectInternalReg( uint8_t * i_pCmeIntReg,
                                                   const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt )
    {
        return fapi2::FAPI2_RC_SUCCESS;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectImageHeader( uint8_t * i_pCmeImgHdr,
                                                   const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt )
    {
        FAPI_DBG(">> PlatCme::collectImageHeader" );
        PpeFfdcLayout *l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pCmeImgHdr );

        uint8_t * l_pTraceLoc = &l_pCmeFfdc->iv_ppeImageHeader[0];
        FAPI_TRY( PlatPmComplex::collectSramInfo
                  ( i_exTgt,
                    l_pTraceLoc,
                    IMAGE_HEADER,
                    FFDC_PPE_IMG_HDR_SIZE ),
                  "Failed To Collect CME Image Header" );

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectImageHeader" );
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::updateCmeFfdcHeader( uint8_t * i_pHomerBuf, uint8_t i_cmePos,
                                                    uint8_t l_ffdcValdityVect, uint8_t i_haltState )
    {
        FAPI_DBG(">> updateCmeFfdcHeader" );

        PpeFfdcHeader * l_pCmeFfdcHdr =  ( (PpeFfdcHeader *)(( PpeFfdcHdrRegion * ) i_pHomerBuf ));
        l_pCmeFfdcHdr->iv_ppeMagicNumber    =  htobe32(FFDC_CME_MAGIC_NUM);
        l_pCmeFfdcHdr->iv_ppeNumber         =  i_cmePos;
        PlatPmComplex::updatePpeFfdcHeader( l_pCmeFfdcHdr, l_ffdcValdityVect, i_haltState );

        FAPI_DBG("<< updateCmeFfdcHeader" );
        return fapi2::FAPI2_RC_SUCCESS;
    }
    //-----------------------------------------------------------------------

extern "C"
{
    fapi2::ReturnCode p9_pm_recovery_ffdc_cme( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP >& i_procChip, void * i_pFfdcBuf )
    {
        FAPI_IMP(">> p9_pm_recovery_ffdc_cme" );
        PlatCme l_cmeFfdc( i_procChip );
        FAPI_TRY( l_cmeFfdc.collectFfdc( i_pFfdcBuf ),
                  "Failed To Collect CME FFDC" );

        fapi_try_exit:
        FAPI_IMP("<< p9_pm_recovery_ffdc_cme" );
        return fapi2::current_err;
    }

}

 }//namespace p9_stop_recov_ffdc ends
<commit_msg>PM Recovery FFDC: Added support to collect Register data for PPM<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_recovery_ffdc_cme.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
// *INDENT-OFF*

///
/// @file   p9_pm_recovery_ffdc_cme.C
/// @brief  Models CME platform for the FFDC collection of PM complex
///
/// *HWP HWP Owner:      Greg Still <stillgs@us.ibm.com>
/// *HWP FW Owner:       Prem S Jha <premjha2@in.ibm.com>
/// *HWP Team:           PM
/// *HWP Level:          2
/// *HWP Consumed by:    Hostboot
//
// *INDENT-OFF*
//--------------------------------------------------------------------------
// Includes
//--------------------------------------------------------------------------

#include <p9_pm_recovery_ffdc_cme.H>
#include <p9_hcd_memmap_cme_sram.H>
#include <collect_reg_ffdc.H>
#include <p9_ppe_defs.H>
#include <stddef.h>
#include <endian.h>

 namespace p9_stop_recov_ffdc
 {
    PlatCme::PlatCme( const fapi2::Target< fapi2::TARGET_TYPE_PROC_CHIP > i_procChipTgt )
      : PlatPmComplex( i_procChipTgt,
                       FFDC_PPE_IMG_HDR_START,
                       FFDC_CME_TRACE_START,
                       FFDC_CME_DASH_BOARD_START,
                       PLAT_CME )
    { }

    //----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectFfdc( void * i_pHomerBuf )
    {
        FAPI_DBG(">> PlatCme::collectFfdc");
        fapi2::ReturnCode l_retCode = fapi2::FAPI2_RC_SUCCESS;
        auto l_exList =
            getProcChip().getChildren< fapi2::TARGET_TYPE_EX > ( fapi2::TARGET_STATE_PRESENT );
        uint8_t l_quadPos = 0;
        uint8_t l_exPos   = 0;
        uint8_t l_cmePos  = 0;
        uint8_t l_ffdcValdityVect = PPE_FFDC_ALL_VALID;
        uint8_t l_haltState = PPE_HALT_COND_UNKNOWN;
        uint8_t *l_pFfdcLoc = NULL;
        HomerFfdcRegion * l_pHomerFfdc =
                ( HomerFfdcRegion *)( (uint8_t *)i_pHomerBuf + FFDC_REGION_HOMER_BASE_OFFSET );

        for( auto ex : l_exList )
        {
            l_ffdcValdityVect = PPE_FFDC_ALL_VALID;

            FAPI_TRY( FAPI_ATTR_GET( fapi2::ATTR_CHIP_UNIT_POS, ex, l_cmePos ),
                      "FAPI_ATTR_GET Failed To Read EX Position" );

            if( !ex.isFunctional() )
            {
                //Marking CME FFDC region as Invalid
                l_ffdcValdityVect = PPE_FFDC_INVALID;
                FAPI_TRY( updateCmeFfdcHeader( l_pFfdcLoc, l_cmePos, l_ffdcValdityVect, l_haltState ),
                          "Failed To Update CME FFDC Header for CME 0x%0d", l_cmePos );
                continue;
            }

            l_exPos     =   l_cmePos % 2;
            l_quadPos   =   l_cmePos >> 1;
            l_pFfdcLoc = &l_pHomerFfdc->iv_quadFfdc[l_quadPos].iv_quadCmeBlock[l_exPos][0];

            FAPI_INF("CME FFDC Quad Pos %d Ex Pos %d ", l_quadPos, l_exPos );

            //In case of error , invalidate FFDC in header.

            // @TODO this is still after reset, which would have already
            // halted the ppe. We need a wa to record this before reset
            // and pass it down, or have a spl r-m-w update per member
            // of the PPE Header?
            // l_retCode = getPpeHaltState (
            //             getCmeBaseAddress (l_cmePos),
            //             l_haltState);

            l_retCode = collectPpeState ( getCmeBaseAddress (l_cmePos),
                                          l_pFfdcLoc );
            if ( l_retCode != fapi2::FAPI2_RC_SUCCESS )
            {
                FAPI_ERR ( "Error collecting CME State, CME Pos 0x08x",
                           l_cmePos );
                l_ffdcValdityVect &= ~PPE_STATE_VALID;
            }
            l_retCode = collectTrace( l_pFfdcLoc, ex );

            if( l_retCode )
            {
                FAPI_ERR("Error in collecting CME Trace CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_TRACE_VALID;
            }

            l_retCode = collectGlobals( l_pFfdcLoc, ex );

            if( l_retCode )
            {
                FAPI_ERR("Error in collecting CME Globals, CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_DASHBOARD_VALID;

            }

            l_retCode = collectImageHeader( l_pFfdcLoc, ex );

            if( l_retCode )
            {
                FAPI_ERR("Error in collecting CME Image header, CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_IMAGE_HEADER_VALID;
            }

            l_retCode = collectInternalReg( l_pFfdcLoc, ex , l_cmePos);

            if( l_retCode )
            {
               FAPI_ERR("Error in collecting CME Internal Regs, CME Pos 0x%08x", l_cmePos );
                l_ffdcValdityVect &= ~PPE_INT_REG_VALID;
            }

            FAPI_TRY( updateCmeFfdcHeader( l_pFfdcLoc, l_cmePos, l_ffdcValdityVect, l_haltState ),
                      "Failed To Update CME FFDC Header for CME 0x%0d", l_cmePos );

        }

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectFfdc");
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectTrace( uint8_t * i_pTraceBuf,
                                             const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt  )
    {
        FAPI_DBG(">> PlatCme::collectTrace" );
        PpeFfdcLayout * l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pTraceBuf );

        uint8_t * l_pTraceLoc = &l_pCmeFfdc->iv_ppeTraces[0];

        FAPI_TRY( PlatPmComplex::collectSramInfo
                    ( i_exTgt,
                      l_pTraceLoc,
                      TRACES,
                      FFDC_PPE_TRACES_SIZE ),
                  "Trace Collection Failed" );

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectTrace" );
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode  PlatCme::collectGlobals( uint8_t * i_pCmeGlobals,
                                                const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt )
    {
        FAPI_DBG(">> PlatCme::collectGlobals" );
        PpeFfdcLayout * l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pCmeGlobals );
        uint8_t * l_pTraceLoc = &l_pCmeFfdc->iv_ppeGlobals[0];

        FAPI_TRY( PlatPmComplex::collectSramInfo
                    ( i_exTgt,
                      l_pTraceLoc,
                      DASH_BOARD_VAR,
                      FFDC_PPE_SCORE_BOARD_SIZE ),
                  "Failed To Collect CME Global Variables" );

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectGlobals" );
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectInternalReg( uint8_t * i_pCmeIntReg,
                                                   const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt ,
                                                   const uint8_t i_exPos)
    {
        FAPI_DBG(">> PlatCme::collectInternalReg" );

        PpeFfdcLayout * l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pCmeIntReg);
        uint8_t * l_pIntRegs = &l_pCmeFfdc->iv_ppeInternalReg[0];

        FAPI_INF("CME Internal FFDC Pos %d ", i_exPos);

        FAPI_TRY(collectRegisterData<fapi2::TARGET_TYPE_EX> (i_exTgt,
                                     l_pIntRegs,
                                     static_cast<fapi2::HwpFfdcId>(fapi2::CME_INTERNAL_FFDC_REGISTERS)),
                      "Failed to collect register data for CME instance %u",i_exPos);

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectInternalReg" );
        return fapi2::FAPI2_RC_SUCCESS;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::collectImageHeader( uint8_t * i_pCmeImgHdr,
                                                   const fapi2::Target<fapi2::TARGET_TYPE_EX >& i_exTgt )
    {
        FAPI_DBG(">> PlatCme::collectImageHeader" );
        PpeFfdcLayout *l_pCmeFfdc = ( PpeFfdcLayout *) ( i_pCmeImgHdr );

        uint8_t * l_pTraceLoc = &l_pCmeFfdc->iv_ppeImageHeader[0];
        FAPI_TRY( PlatPmComplex::collectSramInfo
                  ( i_exTgt,
                    l_pTraceLoc,
                    IMAGE_HEADER,
                    FFDC_PPE_IMG_HDR_SIZE ),
                  "Failed To Collect CME Image Header" );

        fapi_try_exit:
        FAPI_DBG("<< PlatCme::collectImageHeader" );
        return fapi2::current_err;
    }

    //-----------------------------------------------------------------------

    fapi2::ReturnCode PlatCme::updateCmeFfdcHeader( uint8_t * i_pHomerBuf, uint8_t i_cmePos,
                                                    uint8_t l_ffdcValdityVect, uint8_t i_haltState )
    {
        FAPI_DBG(">> updateCmeFfdcHeader" );

        PpeFfdcHeader * l_pCmeFfdcHdr =  ( (PpeFfdcHeader *)(( PpeFfdcHdrRegion * ) i_pHomerBuf ));
        l_pCmeFfdcHdr->iv_ppeMagicNumber    =  htobe32(FFDC_CME_MAGIC_NUM);
        l_pCmeFfdcHdr->iv_ppeNumber         =  i_cmePos;
        PlatPmComplex::updatePpeFfdcHeader( l_pCmeFfdcHdr, l_ffdcValdityVect, i_haltState );

        FAPI_DBG("<< updateCmeFfdcHeader" );
        return fapi2::FAPI2_RC_SUCCESS;
    }
    //-----------------------------------------------------------------------

extern "C"
{
    fapi2::ReturnCode p9_pm_recovery_ffdc_cme( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP >& i_procChip, void * i_pFfdcBuf )
    {
        FAPI_IMP(">> p9_pm_recovery_ffdc_cme" );
        PlatCme l_cmeFfdc( i_procChip );
        FAPI_TRY( l_cmeFfdc.collectFfdc( i_pFfdcBuf ),
                  "Failed To Collect CME FFDC" );

        fapi_try_exit:
        FAPI_IMP("<< p9_pm_recovery_ffdc_cme" );
        return fapi2::current_err;
    }

}

 }//namespace p9_stop_recov_ffdc ends
<|endoftext|>


<commit_before>// Copyright (c) 2013, Maria Teresa Lazaro Grañon
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright notice, this
//   list of conditions and the following disclaimer in the documentation and/or
//   other materials provided with the distribution.
//
//   Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "g2o/stuff/command_args.h"

#include <string>
#include <sstream>

#include "slam/graph_slam.h"
#include "ros_utils/ros_handler.h"
#include "ros_utils/graph_ros_publisher.h"

#include "ros_map_publisher/graph2occupancy.h"
#include "ros_map_publisher/occupancy_map_server.h"

using namespace g2o;

#include <sys/time.h>
//Log files
int logData;
ofstream timesfile, bytesfile;
double timeval_diff(struct timeval *a, struct timeval *b)
{
  return
    (double)(a->tv_sec + (double)a->tv_usec/1000000) -
    (double)(b->tv_sec + (double)b->tv_usec/1000000);
}

int main(int argc, char **argv)
{

  CommandArgs arg;
  double resolution;
  double maxScore;
  double kernelRadius;
  int  minInliers;
  int windowLoopClosure;
  double inlierThreshold;
  int idRobot;
  int nRobots;
  std::string outputFilename;
  std::string odometryTopic, scanTopic, odomFrame, mapFrame, baseFrame;
  std::vector<double> initialPose;
  initialPose.clear();
  bool publishTransform;

  float localizationAngularUpdate, localizationLinearUpdate;
  float maxRange, usableRange;

  arg.param("resolution",  resolution, 0.025, "resolution of the matching grid");
  arg.param("maxScore",    maxScore, 0.15,     "score of the matcher, the higher the less matches");
  arg.param("kernelRadius", kernelRadius, 0.2,  "radius of the convolution kernel");
  arg.param("minInliers",    minInliers, 7,     "min inliers");
  arg.param("windowLoopClosure",  windowLoopClosure, 10,   "sliding window for loop closures");
  arg.param("inlierThreshold",  inlierThreshold, 2.,   "inlier threshold");
  arg.param("idRobot", idRobot, 0, "robot identifier" );
  arg.param("nRobots", nRobots, 1, "number of robots" );
  arg.param("angularUpdate", localizationAngularUpdate, M_PI_4, "angular rotation interval for updating the graph, in radians");
  arg.param("linearUpdate", localizationLinearUpdate, 0.25, "linear translation interval for updating the graph, in meters");
  arg.param("odometryTopic", odometryTopic, "odom", "odometry ROS topic");
  arg.param("scanTopic", scanTopic, "scan", "scan ROS topic");
  arg.param("odomFrame", odomFrame, "odom", "odom frame");
  arg.param("mapFrame", mapFrame, "map", "map frame");
  arg.param("baseFrame", baseFrame, "/base_link", "base robot frame");
  arg.param("initialPose", initialPose, std::vector<double>(), "Pose of the first vertex in the graph. Usage: -initial_pose 0,0,0");
  arg.param("publishTransform", publishTransform, false, "Publish map transform");
  arg.param("o", outputFilename, "", "file where to save output");
  arg.parseArgs(argc, argv);

  //map parameters
  float mapResolution = 0.05;
  float occupiedThreshold = 0.65;
  float rows = 0;
  float cols = 0;
  float gain = 3.0;
  float squareSize = 0;
  float angle = M_PI_2;
  float freeThreshold = 0.196;

  ros::init(argc, argv, "srslam");

  RosHandler rh(idRobot, nRobots, REAL_EXPERIMENT);
  rh.setOdomTopic(odometryTopic);
  rh.setScanTopic(scanTopic);
  rh.setBaseFrame(baseFrame);
  rh.useOdom(true);
  rh.useLaser(true);
  rh.init();
  rh.run();

  maxRange = rh.getLaserMaxRange();
  usableRange = maxRange;

  //For estimation
  SE2 currEst = rh.getOdom();
  std::cout << "My initial position is: " << currEst.translation().x() << " " << currEst.translation().y() << " " << currEst.rotation().angle() << std::endl;
  SE2 odomPosk_1 = currEst;
  std::cout << "My initial odometry is: " << odomPosk_1.translation().x() << " " << odomPosk_1.translation().y() << " " << odomPosk_1.rotation().angle() << std::endl;

  //Graph building
  GraphSLAM gslam;
  gslam.setIdRobot(idRobot);
  int baseId = 10000;
  gslam.setBaseId(baseId);
  gslam.init(resolution, kernelRadius, windowLoopClosure, maxScore, inlierThreshold, minInliers);

  //Map building
  cv::Mat occupancyMap;
  Eigen::Vector2f mapCenter;

  Graph2occupancy mapCreator(gslam.graph(), &occupancyMap, currEst, mapResolution, occupiedThreshold, rows, cols, maxRange, usableRange, gain, squareSize, angle, freeThreshold);
  OccupancyMapServer mapServer(&occupancyMap, idRobot, SIM_EXPERIMENT, mapFrame, occupiedThreshold, freeThreshold);

  RobotLaser* rlaser = rh.getLaser();

  gslam.setInitialData(currEst, odomPosk_1, rlaser);

  GraphRosPublisher graphPublisher(gslam.graph(), mapFrame, odomFrame);
  if (publishTransform)
    graphPublisher.publishMapTransform(gslam.lastVertex()->estimate(), odomPosk_1);

  char buf[100];
  sprintf(buf, "robot-%i-%s", idRobot, outputFilename.c_str());
  ofstream ofmap(buf);
  gslam.graph()->saveVertex(ofmap, gslam.lastVertex());

  mapCreator.computeMap();

  mapCenter = mapCreator.getMapCenter();
  mapServer.setOffset(mapCenter);
  mapServer.setResolution(mapResolution);
  mapServer.publishMapMetaData();
  mapServer.publishMap();

  ros::Rate loop_rate(10);
  while (ros::ok()){
    ros::spinOnce();

    SE2 odomPosk = rh.getOdom(); //current odometry
    SE2 relodom = odomPosk_1.inverse() * odomPosk;
    currEst *= relodom;

    odomPosk_1 = odomPosk;

    if((distanceSE2(gslam.lastVertex()->estimate(), currEst) > localizationLinearUpdate) ||
       (fabs(gslam.lastVertex()->estimate().rotation().angle()-currEst.rotation().angle()) > localizationAngularUpdate)){
      //Add new data
      RobotLaser* laseri = rh.getLaser();

      gslam.addDataSM(odomPosk, laseri);
      gslam.findConstraints();

      struct timeval t_ini, t_fin;
      double secs;
      gettimeofday(&t_ini, NULL);
      gslam.optimize(5);
      gettimeofday(&t_fin, NULL);

      secs = timeval_diff(&t_fin, &t_ini);
      printf("Optimization took %.16g milliseconds\n", secs * 1000.0);

      currEst = gslam.lastVertex()->estimate();
      char buf[100];
      sprintf(buf, "robot-%i-%s", idRobot, outputFilename.c_str());
      gslam.saveGraph(buf);

      //Publish graph to visualize it on Rviz
      graphPublisher.publishGraph();
      //Publish map transform with corrected estimate
      if (publishTransform)
	graphPublisher.publishMapTransform(gslam.lastVertex()->estimate(), odomPosk_1);

        mapCreator.computeMap();
        mapCenter = mapCreator.getMapCenter();
        mapServer.setOffset(mapCenter);

    }else {
      //Publish map transform with last corrected estimate + odometry drift
      if (publishTransform)
	graphPublisher.publishMapTransform(currEst, odomPosk_1);
    }

    mapServer.publishMapMetaData();
    mapServer.publishMap();

    loop_rate.sleep();
  }

  cerr << "Last Optimization...";
  gslam.optimize(5);
  gslam.saveGraph(buf);
  cerr << "Done" << endl;

  return 0;
}
<commit_msg>updated srslam with params initialPose, publishMap and publishGraph<commit_after>// Copyright (c) 2013, Maria Teresa Lazaro Grañon
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright notice, this
//   list of conditions and the following disclaimer in the documentation and/or
//   other materials provided with the distribution.
//
//   Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "g2o/stuff/command_args.h"

#include <string>
#include <sstream>

#include "slam/graph_slam.h"
#include "ros_utils/ros_handler.h"
#include "ros_utils/graph_ros_publisher.h"

#include "ros_map_publisher/graph2occupancy.h"
#include "ros_map_publisher/occupancy_map_server.h"

using namespace g2o;

#include <sys/time.h>
//Log files
int logData;
ofstream timesfile, bytesfile;
double timeval_diff(struct timeval *a, struct timeval *b)
{
  return
    (double)(a->tv_sec + (double)a->tv_usec/1000000) -
    (double)(b->tv_sec + (double)b->tv_usec/1000000);
}

int main(int argc, char **argv)
{

  CommandArgs arg;
  double resolution;
  double maxScore;
  double kernelRadius;
  int  minInliers;
  int windowLoopClosure;
  double inlierThreshold;
  int idRobot;
  int nRobots;
  std::string outputFilename;
  std::string odometryTopic, scanTopic, odomFrame, mapFrame, baseFrame;
  std::vector<double> initialPose;
  initialPose.clear();
  bool publishMap, publishGraph;

  float localizationAngularUpdate, localizationLinearUpdate;
  float maxRange, usableRange;

  arg.param("resolution",  resolution, 0.025, "resolution of the matching grid");
  arg.param("maxScore",    maxScore, 0.15,     "score of the matcher, the higher the less matches");
  arg.param("kernelRadius", kernelRadius, 0.2,  "radius of the convolution kernel");
  arg.param("minInliers",    minInliers, 7,     "min inliers");
  arg.param("windowLoopClosure",  windowLoopClosure, 10,   "sliding window for loop closures");
  arg.param("inlierThreshold",  inlierThreshold, 2.,   "inlier threshold");
  arg.param("idRobot", idRobot, 0, "robot identifier" );
  arg.param("nRobots", nRobots, 1, "number of robots" );
  arg.param("angularUpdate", localizationAngularUpdate, M_PI_4, "angular rotation interval for updating the graph, in radians");
  arg.param("linearUpdate", localizationLinearUpdate, 0.25, "linear translation interval for updating the graph, in meters");
  arg.param("odometryTopic", odometryTopic, "odom", "odometry ROS topic");
  arg.param("scanTopic", scanTopic, "scan", "scan ROS topic");
  arg.param("odomFrame", odomFrame, "odom", "odom frame");
  arg.param("mapFrame", mapFrame, "map", "map frame");
  arg.param("baseFrame", baseFrame, "/base_link", "base robot frame");
  arg.param("initialPose", initialPose, std::vector<double>(), "Pose of the first vertex in the graph. Usage: -initial_pose 0,0,0");
  arg.param("publishMap", publishMap, false, "Publish map");
  arg.param("publishGraph", publishGraph, false, "Publish graph");
  arg.param("o", outputFilename, "", "file where to save output");
  arg.parseArgs(argc, argv);

  //map parameters
  float mapResolution = 0.05;
  float occupiedThreshold = 0.65;
  float rows = 0;
  float cols = 0;
  float gain = 3.0;
  float squareSize = 0;
  float angle = M_PI_2;
  float freeThreshold = 0.196;

  ros::init(argc, argv, "srslam");

  RosHandler rh(idRobot, nRobots, REAL_EXPERIMENT);
  rh.setOdomTopic(odometryTopic);
  rh.setScanTopic(scanTopic);
  rh.setBaseFrame(baseFrame);
  rh.useOdom(true);
  rh.useLaser(true);
  rh.init();
  rh.run();

  maxRange = rh.getLaserMaxRange();
  usableRange = maxRange;

  //For estimation
  SE2 currEst;
  SE2 odomPosk_1 = rh.getOdom();
  if (initialPose.size()){
    if (initialPose.size()==3){
      currEst = SE2(initialPose[0],initialPose[1],initialPose[2]);
    }else {
      std::cerr << "Error. Provide a valid initial pose (x, y, theta)" << std::endl;
      exit(0);
    }
  }else{
    currEst = odomPosk_1;
  }

  std::cout << "My initial position is: " << currEst.translation().x() << " " << currEst.translation().y() << " " << currEst.rotation().angle() << std::endl;
  std::cout << "My initial odometry is: " << odomPosk_1.translation().x() << " " << odomPosk_1.translation().y() << " " << odomPosk_1.rotation().angle() << std::endl;

  //Graph building
  GraphSLAM gslam;
  gslam.setIdRobot(idRobot);
  int baseId = 10000;
  gslam.setBaseId(baseId);
  gslam.init(resolution, kernelRadius, windowLoopClosure, maxScore, inlierThreshold, minInliers);

  RobotLaser* rlaser = rh.getLaser();

  gslam.setInitialData(currEst, rlaser);

  cv::Mat occupancyMap;
  Eigen::Vector2f mapCenter;

  //Map building
  Graph2occupancy mapCreator(gslam.graph(), &occupancyMap, currEst, mapResolution, occupiedThreshold, rows, cols, maxRange, usableRange, gain, squareSize, angle, freeThreshold);
  OccupancyMapServer mapServer(&occupancyMap, idRobot, SIM_EXPERIMENT, mapFrame, occupiedThreshold, freeThreshold);
  GraphRosPublisher graphPublisher(gslam.graph(), mapFrame, odomFrame);

  if (publishMap){
    mapCreator.computeMap();

    mapCenter = mapCreator.getMapCenter();
    mapServer.setOffset(mapCenter);
    mapServer.setResolution(mapResolution);
    mapServer.publishMapMetaData();
    mapServer.publishMap();
  }

  if (publishGraph)
    graphPublisher.publishGraph();

  if (publishMap || publishGraph)
    graphPublisher.publishMapTransform(gslam.lastVertex()->estimate(), odomPosk_1);

  //Saving g2o file

  char buf[100];
  sprintf(buf, "robot-%i-%s", idRobot, outputFilename.c_str());
  ofstream ofmap(buf);
  gslam.graph()->saveVertex(ofmap, gslam.lastVertex());

  ros::Rate loop_rate(10);
  while (ros::ok()){
    ros::spinOnce();

    SE2 odomPosk = rh.getOdom(); //current odometry
    SE2 relodom = odomPosk_1.inverse() * odomPosk;
    currEst *= relodom;

    odomPosk_1 = odomPosk;

    if((distanceSE2(gslam.lastVertex()->estimate(), currEst) > localizationLinearUpdate) ||
       (fabs(gslam.lastVertex()->estimate().rotation().angle()-currEst.rotation().angle()) > localizationAngularUpdate)){
      //Add new data
      RobotLaser* laseri = rh.getLaser();

      gslam.addDataSM(currEst, laseri);
      gslam.findConstraints();

      struct timeval t_ini, t_fin;
      double secs;
      gettimeofday(&t_ini, NULL);
      gslam.optimize(5);
      gettimeofday(&t_fin, NULL);

      secs = timeval_diff(&t_fin, &t_ini);
      printf("Optimization took %.16g milliseconds\n", secs * 1000.0);

      currEst = gslam.lastVertex()->estimate();
      char buf[100];
      sprintf(buf, "robot-%i-%s", idRobot, outputFilename.c_str());
      gslam.saveGraph(buf);

      if (publishMap || publishGraph)
	graphPublisher.publishMapTransform(gslam.lastVertex()->estimate(), odomPosk_1);

      //Publish graph to visualize it on Rviz
      if (publishGraph)
	graphPublisher.publishGraph();

      if (publishMap){
	//Update map
        mapCreator.computeMap();
        mapCenter = mapCreator.getMapCenter();
        mapServer.setOffset(mapCenter);
      }

    }else {
      //Publish map transform with last corrected estimate + odometry drift
      if (publishMap || publishGraph)
	graphPublisher.publishMapTransform(currEst, odomPosk_1);
    }

    //Publish map
    if (publishMap){
      mapServer.publishMapMetaData();
      mapServer.publishMap();
    }

    loop_rate.sleep();
  }

  cerr << "Last Optimization...";
  gslam.optimize(5);
  gslam.saveGraph(buf);
  cerr << "Done" << endl;

  return 0;
}
<|endoftext|>


<commit_before>/* rsspp - Copyright (C) 2008-2012 Andreas Krennmair <ak@newsbeuter.org>
 * Licensed under the MIT/X Consortium License. See file LICENSE
 * for more information.
 */

#include <config.h>
#include <rsspp.h>
#include <rsspp_internal.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <curl/curl.h>
#include <logger.h>
#include <utils.h>
#include <cstring>
#include <utils.h>
#include <remote_api.h>

using namespace newsbeuter;

static size_t my_write_data(void *buffer, size_t size, size_t nmemb, void *userp) {
	std::string * pbuf = static_cast<std::string *>(userp);
	pbuf->append(static_cast<const char *>(buffer), size * nmemb);
	return size * nmemb;
}

namespace rsspp {

parser::parser(unsigned int timeout, const char * user_agent, const char * proxy, const char * proxy_auth, curl_proxytype proxy_type)
	: to(timeout), ua(user_agent), prx(proxy), prxauth(proxy_auth), prxtype(proxy_type), doc(0), lm(0) {
}

parser::~parser() {
	if (doc)
		xmlFreeDoc(doc);
}

struct header_values {
	time_t lastmodified;
	std::string etag;

	header_values()
		: lastmodified(0)
	{
	}
};

static size_t handle_headers(void * ptr, size_t size, size_t nmemb, void * data) {
	char * header = new char[size*nmemb + 1];
	header_values * values = (header_values *)data;

	memcpy(header, ptr, size*nmemb);
	header[size*nmemb] = '\0';

	if (!strncasecmp("Last-Modified:", header, 14)) {
		time_t r = curl_getdate(header+14, NULL);
		if (r == -1) {
			LOG(LOG_DEBUG, "handle_headers: last-modified %s (curl_getdate FAILED)", header+14);
		} else {
			values->lastmodified = curl_getdate(header+14, NULL);
			LOG(LOG_DEBUG, "handle_headers: got last-modified %s (%d)", header+14, values->lastmodified);
		}
	} else if (!strncasecmp("ETag:",header, 5)) {
		values->etag = std::string(header+5);
		utils::trim(values->etag);
		LOG(LOG_DEBUG, "handle_headers: got etag %s", values->etag.c_str());
	}

	delete[] header;

	return size * nmemb;
}

feed parser::parse_url(const std::string& url, time_t lastmodified, const std::string& etag, newsbeuter::remote_api * api, const std::string& cookie_cache, CURL *ehandle) {
	std::string buf;
	CURLcode ret;

	CURL * easyhandle = ehandle;
	if (!easyhandle) {
		easyhandle = curl_easy_init();
		if (!easyhandle) {
			throw exception(_("couldn't initialize libcurl"));
		}
	}

	if (ua) {
		curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, ua);
	}
	if (api) {
		api->configure_handle(easyhandle);
	}
	curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str());
	curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, my_write_data);
	curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &buf);
	curl_easy_setopt(easyhandle, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(easyhandle, CURLOPT_MAXREDIRS, 10);
	curl_easy_setopt(easyhandle, CURLOPT_FAILONERROR, 1);
	curl_easy_setopt(easyhandle, CURLOPT_ENCODING, "gzip, deflate");
	if (cookie_cache != "") {
		curl_easy_setopt(easyhandle, CURLOPT_COOKIEFILE, cookie_cache.c_str());
		curl_easy_setopt(easyhandle, CURLOPT_COOKIEJAR, cookie_cache.c_str());
	}
	if (to != 0)
		curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, to);

	if (prx)
		curl_easy_setopt(easyhandle, CURLOPT_PROXY, prx);

	if (prxauth) {
		curl_easy_setopt(easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
		curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, prxauth);
	}

	curl_easy_setopt(easyhandle, CURLOPT_PROXYTYPE, prxtype);

	header_values hdrs;
	curl_easy_setopt(easyhandle, CURLOPT_HEADERDATA, &hdrs);
	curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, handle_headers);

	curl_easy_setopt(easyhandle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
	if (lastmodified != 0)
		curl_easy_setopt(easyhandle, CURLOPT_TIMEVALUE, lastmodified);
	else
		curl_easy_setopt(easyhandle, CURLOPT_TIMEVALUE, 0);

	curl_slist * custom_headers = NULL;
	if (etag.length() > 0) {
		custom_headers = curl_slist_append(custom_headers, utils::strprintf("If-None-Match: %s", etag.c_str()).c_str());
		curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, custom_headers);
	}

	ret = curl_easy_perform(easyhandle);

	lm = hdrs.lastmodified;
	et = hdrs.etag;

	if (custom_headers) {
		curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, 0);
		curl_slist_free_all(custom_headers);
	}

	LOG(LOG_DEBUG, "rsspp::parser::parse_url: ret = %d", ret);

	long status;
	curl_easy_getinfo(easyhandle, CURLINFO_HTTP_CONNECTCODE, &status);

	if (status >= 400) {
		LOG(LOG_USERERROR, _("Error: trying to download feed `%s' returned HTTP status code %ld."), url.c_str(), status);
	}

	if (!ehandle)
		curl_easy_cleanup(easyhandle);

	if (ret != 0) {
		LOG(LOG_ERROR, "rsspp::parser::parse_url: curl_easy_perform returned err %d: %s", ret, curl_easy_strerror(ret));
		throw exception(curl_easy_strerror(ret));
	}

	LOG(LOG_INFO, "parser::parse_url: retrieved data for %s: %s", url.c_str(), buf.c_str());

	if (buf.length() > 0) {
		LOG(LOG_DEBUG, "parser::parse_url: handing over data to parse_buffer()");
		return parse_buffer(buf.c_str(), buf.length(), url.c_str());
	}

	return feed();
}

feed parser::parse_buffer(const char * buffer, size_t size, const char * url) {
	doc = xmlReadMemory(buffer, size, url, NULL, XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
	if (doc == NULL) {
		throw exception(_("could not parse buffer"));
	}

	xmlNode* root_element = xmlDocGetRootElement(doc);

	feed f = parse_xmlnode(root_element);

	if (doc->encoding) {
		f.encoding = (const char *)doc->encoding;
	}

	LOG(LOG_INFO, "parser::parse_buffer: encoding = %s", f.encoding.c_str());

	return f;
}

feed parser::parse_file(const std::string& filename) {
	doc = xmlReadFile(filename.c_str(), NULL, XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
	if (doc == NULL) {
		throw exception(_("could not parse file"));
	}

	xmlNode* root_element = xmlDocGetRootElement(doc);

	feed f = parse_xmlnode(root_element);

	if (doc->encoding) {
		f.encoding = (const char *)doc->encoding;
	}

	LOG(LOG_INFO, "parser::parse_file: encoding = %s", f.encoding.c_str());

	return f;
}

feed parser::parse_xmlnode(xmlNode* node) {
	feed f;

	if (node) {
		if (node->name && node->type == XML_ELEMENT_NODE) {
			if (strcmp((const char *)node->name, "rss")==0) {
				const char * version = (const char *)xmlGetProp(node, (const xmlChar *)"version");
				if (!version) {
					xmlFree((void *)version);
					throw exception(_("no RSS version"));
				}
				if (strcmp(version, "0.91")==0)
					f.rss_version = RSS_0_91;
				else if (strcmp(version, "0.92")==0)
					f.rss_version = RSS_0_92;
				else if (strcmp(version, "0.94")==0)
					f.rss_version = RSS_0_94;
				else if (strcmp(version, "2.0")==0 || strcmp(version, "2")==0)
					f.rss_version = RSS_2_0;
				else {
					xmlFree((void *)version);
					throw exception(_("invalid RSS version"));
				}
				xmlFree((void *)version);
			} else if (strcmp((const char *)node->name, "RDF")==0) {
				f.rss_version = RSS_1_0;
			} else if (strcmp((const char *)node->name, "feed")==0) {
				if (node->ns && node->ns->href) {
					if (strcmp((const char *)node->ns->href, ATOM_0_3_URI)==0) {
						f.rss_version = ATOM_0_3;
					} else if (strcmp((const char *)node->ns->href, ATOM_1_0_URI)==0) {
						f.rss_version = ATOM_1_0;
					} else {
						const char * version = (const char *)xmlGetProp(node, (const xmlChar *)"version");
						if (!version) {
							xmlFree((void *)version);
							throw exception(_("invalid Atom version"));
						}
						if (strcmp(version, "0.3")==0) {
							xmlFree((void *)version);
							f.rss_version = ATOM_0_3_NONS;
						} else {
							xmlFree((void *)version);
							throw exception(_("invalid Atom version"));
						}
					}
				} else {
					throw exception(_("no Atom version"));
				}
			}

			std::tr1::shared_ptr<rss_parser> parser = rss_parser_factory::get_object(f, doc);

			try {
				parser->parse_feed(f, node);
			} catch (exception& e) {
				throw;
			}
		}
	} else {
		throw exception(_("XML root node is NULL"));
	}

	return f;
}

void parser::global_init() {
	LIBXML_TEST_VERSION
	curl_global_init(CURL_GLOBAL_ALL);
}

void parser::global_cleanup() {
	xmlCleanupParser();
	curl_global_cleanup();
}

}
<commit_msg>only set If-Modified-Since header if lastmodified != 0.<commit_after>/* rsspp - Copyright (C) 2008-2012 Andreas Krennmair <ak@newsbeuter.org>
 * Licensed under the MIT/X Consortium License. See file LICENSE
 * for more information.
 */

#include <config.h>
#include <rsspp.h>
#include <rsspp_internal.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <curl/curl.h>
#include <logger.h>
#include <utils.h>
#include <cstring>
#include <utils.h>
#include <remote_api.h>

using namespace newsbeuter;

static size_t my_write_data(void *buffer, size_t size, size_t nmemb, void *userp) {
	std::string * pbuf = static_cast<std::string *>(userp);
	pbuf->append(static_cast<const char *>(buffer), size * nmemb);
	return size * nmemb;
}

namespace rsspp {

parser::parser(unsigned int timeout, const char * user_agent, const char * proxy, const char * proxy_auth, curl_proxytype proxy_type)
	: to(timeout), ua(user_agent), prx(proxy), prxauth(proxy_auth), prxtype(proxy_type), doc(0), lm(0) {
}

parser::~parser() {
	if (doc)
		xmlFreeDoc(doc);
}

struct header_values {
	time_t lastmodified;
	std::string etag;

	header_values()
		: lastmodified(0)
	{
	}
};

static size_t handle_headers(void * ptr, size_t size, size_t nmemb, void * data) {
	char * header = new char[size*nmemb + 1];
	header_values * values = (header_values *)data;

	memcpy(header, ptr, size*nmemb);
	header[size*nmemb] = '\0';

	if (!strncasecmp("Last-Modified:", header, 14)) {
		time_t r = curl_getdate(header+14, NULL);
		if (r == -1) {
			LOG(LOG_DEBUG, "handle_headers: last-modified %s (curl_getdate FAILED)", header+14);
		} else {
			values->lastmodified = curl_getdate(header+14, NULL);
			LOG(LOG_DEBUG, "handle_headers: got last-modified %s (%d)", header+14, values->lastmodified);
		}
	} else if (!strncasecmp("ETag:",header, 5)) {
		values->etag = std::string(header+5);
		utils::trim(values->etag);
		LOG(LOG_DEBUG, "handle_headers: got etag %s", values->etag.c_str());
	}

	delete[] header;

	return size * nmemb;
}

feed parser::parse_url(const std::string& url, time_t lastmodified, const std::string& etag, newsbeuter::remote_api * api, const std::string& cookie_cache, CURL *ehandle) {
	std::string buf;
	CURLcode ret;

	CURL * easyhandle = ehandle;
	if (!easyhandle) {
		easyhandle = curl_easy_init();
		if (!easyhandle) {
			throw exception(_("couldn't initialize libcurl"));
		}
	}

	if (ua) {
		curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, ua);
	}
	if (api) {
		api->configure_handle(easyhandle);
	}
	curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str());
	curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, my_write_data);
	curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &buf);
	curl_easy_setopt(easyhandle, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(easyhandle, CURLOPT_MAXREDIRS, 10);
	curl_easy_setopt(easyhandle, CURLOPT_FAILONERROR, 1);
	curl_easy_setopt(easyhandle, CURLOPT_ENCODING, "gzip, deflate");
	if (cookie_cache != "") {
		curl_easy_setopt(easyhandle, CURLOPT_COOKIEFILE, cookie_cache.c_str());
		curl_easy_setopt(easyhandle, CURLOPT_COOKIEJAR, cookie_cache.c_str());
	}
	if (to != 0)
		curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, to);

	if (prx)
		curl_easy_setopt(easyhandle, CURLOPT_PROXY, prx);

	if (prxauth) {
		curl_easy_setopt(easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
		curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, prxauth);
	}

	curl_easy_setopt(easyhandle, CURLOPT_PROXYTYPE, prxtype);

	header_values hdrs;
	curl_easy_setopt(easyhandle, CURLOPT_HEADERDATA, &hdrs);
	curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, handle_headers);

	if (lastmodified != 0) {
		curl_easy_setopt(easyhandle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
		curl_easy_setopt(easyhandle, CURLOPT_TIMEVALUE, lastmodified);
	}

	curl_slist * custom_headers = NULL;
	if (etag.length() > 0) {
		custom_headers = curl_slist_append(custom_headers, utils::strprintf("If-None-Match: %s", etag.c_str()).c_str());
		curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, custom_headers);
	}

	ret = curl_easy_perform(easyhandle);

	lm = hdrs.lastmodified;
	et = hdrs.etag;

	if (custom_headers) {
		curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, 0);
		curl_slist_free_all(custom_headers);
	}

	LOG(LOG_DEBUG, "rsspp::parser::parse_url: ret = %d", ret);

	long status;
	curl_easy_getinfo(easyhandle, CURLINFO_HTTP_CONNECTCODE, &status);

	if (status >= 400) {
		LOG(LOG_USERERROR, _("Error: trying to download feed `%s' returned HTTP status code %ld."), url.c_str(), status);
	}

	if (!ehandle)
		curl_easy_cleanup(easyhandle);

	if (ret != 0) {
		LOG(LOG_ERROR, "rsspp::parser::parse_url: curl_easy_perform returned err %d: %s", ret, curl_easy_strerror(ret));
		throw exception(curl_easy_strerror(ret));
	}

	LOG(LOG_INFO, "parser::parse_url: retrieved data for %s: %s", url.c_str(), buf.c_str());

	if (buf.length() > 0) {
		LOG(LOG_DEBUG, "parser::parse_url: handing over data to parse_buffer()");
		return parse_buffer(buf.c_str(), buf.length(), url.c_str());
	}

	return feed();
}

feed parser::parse_buffer(const char * buffer, size_t size, const char * url) {
	doc = xmlReadMemory(buffer, size, url, NULL, XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
	if (doc == NULL) {
		throw exception(_("could not parse buffer"));
	}

	xmlNode* root_element = xmlDocGetRootElement(doc);

	feed f = parse_xmlnode(root_element);

	if (doc->encoding) {
		f.encoding = (const char *)doc->encoding;
	}

	LOG(LOG_INFO, "parser::parse_buffer: encoding = %s", f.encoding.c_str());

	return f;
}

feed parser::parse_file(const std::string& filename) {
	doc = xmlReadFile(filename.c_str(), NULL, XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
	if (doc == NULL) {
		throw exception(_("could not parse file"));
	}

	xmlNode* root_element = xmlDocGetRootElement(doc);

	feed f = parse_xmlnode(root_element);

	if (doc->encoding) {
		f.encoding = (const char *)doc->encoding;
	}

	LOG(LOG_INFO, "parser::parse_file: encoding = %s", f.encoding.c_str());

	return f;
}

feed parser::parse_xmlnode(xmlNode* node) {
	feed f;

	if (node) {
		if (node->name && node->type == XML_ELEMENT_NODE) {
			if (strcmp((const char *)node->name, "rss")==0) {
				const char * version = (const char *)xmlGetProp(node, (const xmlChar *)"version");
				if (!version) {
					xmlFree((void *)version);
					throw exception(_("no RSS version"));
				}
				if (strcmp(version, "0.91")==0)
					f.rss_version = RSS_0_91;
				else if (strcmp(version, "0.92")==0)
					f.rss_version = RSS_0_92;
				else if (strcmp(version, "0.94")==0)
					f.rss_version = RSS_0_94;
				else if (strcmp(version, "2.0")==0 || strcmp(version, "2")==0)
					f.rss_version = RSS_2_0;
				else {
					xmlFree((void *)version);
					throw exception(_("invalid RSS version"));
				}
				xmlFree((void *)version);
			} else if (strcmp((const char *)node->name, "RDF")==0) {
				f.rss_version = RSS_1_0;
			} else if (strcmp((const char *)node->name, "feed")==0) {
				if (node->ns && node->ns->href) {
					if (strcmp((const char *)node->ns->href, ATOM_0_3_URI)==0) {
						f.rss_version = ATOM_0_3;
					} else if (strcmp((const char *)node->ns->href, ATOM_1_0_URI)==0) {
						f.rss_version = ATOM_1_0;
					} else {
						const char * version = (const char *)xmlGetProp(node, (const xmlChar *)"version");
						if (!version) {
							xmlFree((void *)version);
							throw exception(_("invalid Atom version"));
						}
						if (strcmp(version, "0.3")==0) {
							xmlFree((void *)version);
							f.rss_version = ATOM_0_3_NONS;
						} else {
							xmlFree((void *)version);
							throw exception(_("invalid Atom version"));
						}
					}
				} else {
					throw exception(_("no Atom version"));
				}
			}

			std::tr1::shared_ptr<rss_parser> parser = rss_parser_factory::get_object(f, doc);

			try {
				parser->parse_feed(f, node);
			} catch (exception& e) {
				throw;
			}
		}
	} else {
		throw exception(_("XML root node is NULL"));
	}

	return f;
}

void parser::global_init() {
	LIBXML_TEST_VERSION
	curl_global_init(CURL_GLOBAL_ALL);
}

void parser::global_cleanup() {
	xmlCleanupParser();
	curl_global_cleanup();
}

}
<|endoftext|>