text:
// __BEGIN_LICENSE__
//  Copyright (c) 2009-2013, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NGT platform is 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.
// __END_LICENSE__

#include <vw/Image.h>
#include <vw/Camera.h>
#include <vw/FileIO.h>
#include <vw/Cartography.h>
#include <vw/Plate/PlateFile.h>
#include <vw/Plate/TileManipulation.h>
#include <vw/Plate/PlateCarreePlateManager.h>
#include <vw/Plate/ToastPlateManager.h>
#include <vw/Plate/PlateView.h>
using namespace vw;
using namespace vw::platefile;

#include <asp/Sessions/StereoSessionFactory.h>
#include <asp/Core/Common.h>
#include <asp/Core/Macros.h>
#include <asp/IsisIO/DiskImageResourceIsis.h>

#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <boost/filesystem/convenience.hpp>
namespace fs = boost::filesystem;

using namespace std;

// --- Standard Terminal Args ---
struct Options : public asp::BaseOptions {
  Options() : output_id(-1) {}

  // Input
  string input_url;
  string camera_image, camera_model; // For ISIS, these are the same
  int32 dem_id, level;

  // Settings
  string datum, session, output_mode, mask_image;

  // Output
  string output_url;
  int output_id; // transaction id
};

void handle_arguments(int argc, char *argv[], Options& opt) {
  po::options_description general_options("Orthoprojects imagery into a plate file");
  general_options.add_options()
    ("dem-id", po::value(&opt.dem_id)->default_value(-1), "DEM's transaction ID in input platefile")
    ("level,l", po::value(&opt.level)->default_value(-1), "Level at which to find input DEM.")
    ("output-id", po::value(&opt.output_id),
     "Output transaction ID to use. If not provided, one will be solved for.")
    ("mode,m", po::value(&opt.output_mode)->default_value("equi"), "Output mode [toast, equi, polar]")
    ("mask", po::value(&opt.mask_image), "Input single channel image with same aspect ratio as camera image whose pixel with maximum value define valid pixels from projection.")
    ("datum,d", po::value(&opt.datum)->default_value("Earth"), "Datum to use [Earth, Moon, Mars].")
    ("session-type,t", po::value(&opt.session),
     "Select the stereo session type to use for processing. [Options are pinhole, isis, ...]");
  general_options.add( asp::BaseOptionsDescription(opt) );

  po::options_description hidden_options("");
  hidden_options.add_options()
    ("input-url", po::value(&opt.input_url), "")
    ("camera-image", po::value(&opt.camera_image), "")
    ("camera-model", po::value(&opt.camera_model), "")
    ("output-url", po::value(&opt.output_url), "");

  po::positional_options_description p;
  p.add("input-url",1);
  p.add("camera-image",1);
  p.add("camera-model",1);
  p.add("output-url",1);

  std::string usage("<input-plate> <camera-image> <camera-model> <output-plate> [options]");
  bool allow_unregistered = false;
  std::vector<std::string> unregistered;
  po::variables_map vm =
    asp::check_command_line( argc, argv, opt, general_options, general_options,
                             hidden_options, p, usage,
                             allow_unregistered, unregistered );

  if ( opt.input_url.empty() ||
       opt.camera_image.empty() || opt.camera_model.empty() )
    vw_throw( ArgumentErr() << "Missing input files!\n\n" << usage << general_options);

  // Detecting session type
  boost::to_lower(opt.session);
  if ( opt.session.empty() ) {
    if ( boost::iends_with(opt.camera_model,".cahvor") ||
         boost::iends_with(opt.camera_model,".cmod") ||
         boost::iends_with(opt.camera_model,".cahv") ||
         boost::iends_with(opt.camera_model,".pin") ||
         boost::iends_with(opt.camera_model,".tsai") ) {
      vw_out() << "\t--> Detected pinhole camera files.  Executing pinhole stereo pipeline.\n";
      opt.session = "pinhole";
    }

    else if ( boost::iends_with(opt.camera_image, ".cub") ) {
      vw_out() << "\t--> Detected ISIS cube files.  Executing ISIS stereo pipeline.\n";
      opt.session = "isis";
    }

    else {
      vw_throw( ArgumentErr() << "\n\n******************************************************************\n"
                << "Could not determine stereo session type.   Please set it explicitly\n"
                << "using the -t switch.  Options include: [pinhole isis].\n"
                << "******************************************************************\n\n" );
    }
  }

  // Handling ISIS's model within image
  if ( opt.output_url.empty() ) {
    if ( opt.session == "isis" ) {
      opt.output_url = opt.camera_model;
      opt.camera_model = "";
    } else {
      vw_throw( ArgumentErr() << usage << general_options );
    }
  }

  // Final clean up and saving user from themselves
  boost::to_lower(opt.datum);
}

// --- Internal Operations ---
template <class PixelT>
void do_projection(boost::shared_ptr<PlateFile> input_plate,
                   boost::shared_ptr<PlateFile> output_plate,
                   boost::shared_ptr<camera::CameraModel> camera_model,
                   Options& opt) {
  typedef PixelGrayA<float> InputT;

  boost::scoped_ptr<PlateManager<InputT> > input_pm( PlateManager<InputT >::make(opt.output_mode, input_plate ) );
  cartography::GeoReference input_georef = input_pm->georeference(opt.level);

  // Finding and setting our Datum
  if ( opt.datum == "earth" )
    input_georef.set_well_known_geogcs("WGS84");
  else if ( opt.datum == "mars" )
    input_georef.set_well_known_geogcs("D_MARS");
  else if ( opt.datum == "moon" )
    input_georef.set_well_known_geogcs("D_MOON");
  else
    vw_throw( ArgumentErr() << "Unknown datum, \"" << opt.datum << "\".\n" );

  // Loading up input image
  DiskImageView<PixelT> texture_disk_image(opt.camera_image);
  ImageViewRef<PixelT> texture_image = texture_disk_image;

  // Camera Center's
  Vector3 camera_center = camera_model->camera_center(Vector2());
  Vector3 camera_lla = cartography::xyz_to_lon_lat_radius( camera_center );
  vw_out() << "\tLoaded Camera Location: " << camera_lla << " [deg deg meters]\n";

#if defined(ASP_HAVE_PKG_ISISIO) && ASP_HAVE_PKG_ISISIO == 1
  // Normalize between 0->1.
  if ( opt.session == "isis" ) {
    DiskImageResourceIsis isis_rsrc( opt.camera_image );
    texture_image =
      normalize_retain_alpha(asp::remove_isis_special_pixels(texture_disk_image),
                             isis_rsrc.valid_minimum(),isis_rsrc.valid_maximum(),
                             ChannelRange<PixelT>::min(), ChannelRange<PixelT>::max() );
  }
#endif

  if ( !opt.mask_image.empty() ) {
    DiskImageView<uint8> mask(opt.mask_image);
    if ( bounding_box(mask) != bounding_box(texture_image ) ) {
      vw_out(WarningMessage) << "Mask image does not have same size as input. Attempting to scale to correct size.\n";
      VW_ASSERT( fabs( float(mask.cols())/float(mask.rows()) - float(texture_image.cols())/float(texture_image.rows()) ) < 1e-2,
                 ArgumentErr() << "Mask image does not have same aspect ratio as camera image. Unable to use as mask!" );
      texture_image = mask_to_alpha(copy_mask(alpha_to_mask(texture_image),
                                              create_mask(resize(DiskImageView<uint8>(mask),
                                                                 texture_image.cols(), texture_image.rows(),
                                                                 ZeroEdgeExtension(), NearestPixelInterpolation()),0)));
    } else {
      texture_image = mask_to_alpha(copy_mask(alpha_to_mask(texture_image),create_mask(DiskImageView<uint8>(mask),0)));
    }
  }

  // Performing rough approximation of which tiles this camera touches
  BBox2 image_alt = camera_bbox( input_georef, camera_model,
                                 texture_image.cols(), texture_image.rows() );

  BBox2i dem_square; // Is in pixels
  dem_square.grow( input_georef.lonlat_to_pixel(image_alt.min()) );
  dem_square.grow( input_georef.lonlat_to_pixel(image_alt.max()) );
  dem_square.max() += Vector2i(1,1);

  // Expand rough draft DEM square to the 256 boundaries
  dem_square.min() = floor(Vector2f(dem_square.min())/256.)*256;
  dem_square.max() = ceil(Vector2f(dem_square.max())/256.)*256;

  // Render a low res version of the DEM and use that to get a better approximation
  PlateView<InputT > input_view( opt.input_url );
  input_view.set_level( opt.level > 4 ? opt.level - 3 : opt.level );
  image_alt = camera_bbox(copy(crop(input_view, opt.level > 4 ? dem_square / 8 : dem_square)),
                          crop(resample(input_georef, opt.level > 4 ? 1./8. : 1),
                               opt.level > 4 ? dem_square / 8 : dem_square),
                          camera_model, texture_image.cols(), texture_image.rows());

  dem_square = BBox2i();
  dem_square.grow( input_georef.lonlat_to_pixel(image_alt.min()) );
  dem_square.grow( input_georef.lonlat_to_pixel(image_alt.max()) );
  dem_square.max() += Vector2i(1,1);

  // Expand rough draft DEM square to the 256 boundaries
  dem_square.min() = floor(Vector2f(dem_square.min())/256.)*256;
  dem_square.max() = ceil(Vector2f(dem_square.max())/256.)*256;

  // Building plateview to extract a single DEM
  input_view.set_level( opt.level );
  ImageViewRef<InputT > input_view_ref =
    crop(edge_extend(input_view, PeriodicEdgeExtension()),dem_square);

  cartography::GeoReference dem_georef = crop(input_georef, dem_square);

  vw_out() << "\t--> Orthoprojecting into DEM square with transform:\n"
           << "\t    " << dem_georef << "\n";
  ImageViewRef<PixelT> projection =
    orthoproject(input_view_ref, dem_georef,
                 texture_image, camera_model,
                 BicubicInterpolation(),
                 ZeroEdgeExtension());

  // Push to output plate file
  boost::scoped_ptr<PlateManager<PixelT> > output_pm( PlateManager<PixelT>::make( opt.output_mode, output_plate ) );
  output_pm->insert(projection, opt.camera_image,
                    opt.output_id, dem_georef, false, false,
                    TerminalProgressCallback( "asp.plateorthoproject",
                                              "\t   Processing") );
}

void do_run( Options& opt ) {

  // Extracting camera model from stereo session
  typedef boost::scoped_ptr<asp::StereoSession> SessionPtr;
  SessionPtr session( asp::StereoSession::create(opt.session) );
  session->initialize(opt, opt.camera_image, opt.camera_image,
                      opt.camera_model, opt.camera_model,
                      "", "", "", "", "" );
  boost::shared_ptr<camera::CameraModel> camera_model;
  camera_model = session->camera_model(opt.camera_image,
                                       opt.camera_model);

  // Opening input and output plate files
  if ( opt.output_url.empty() )
    opt.output_url = fs::change_extension( opt.camera_image, "_DRG.plate" ).string();
  PixelFormatEnum pixel_format;
  ChannelTypeEnum channel_type;
  try {
    DiskImageResource *rsrc = DiskImageResource::open(opt.camera_image);
    pixel_format = rsrc->pixel_format();
    channel_type = rsrc->channel_type();

    // Plate files should always have an alpha channel.
    if (pixel_format == VW_PIXEL_GRAY)
      pixel_format = VW_PIXEL_GRAYA;
    if (pixel_format == VW_PIXEL_RGB)
      pixel_format = VW_PIXEL_RGBA;

    delete rsrc;
  } catch (vw::Exception const& e) {
    vw_throw( ArgumentErr() << "An error occured while finding pixel type: " << e.what() << "\n" );
  }

  string tile_filetype;
  if (channel_type == VW_CHANNEL_FLOAT32 ||
      channel_type == VW_CHANNEL_INT16 )
    tile_filetype = "tif";
  else
    tile_filetype = "png";

  typedef boost::shared_ptr<PlateFile> PlatePtr;
  PlatePtr input_plate( new PlateFile(opt.input_url) );
  PlatePtr output_plate( new PlateFile(opt.output_url,
                                       opt.output_mode, "",
                                       256, tile_filetype,
                                       pixel_format, channel_type) );
  switch( pixel_format ) {
  case VW_PIXEL_GRAYA:
    switch( channel_type ) {
    case VW_CHANNEL_UINT8:
    case VW_CHANNEL_UINT16:
      do_projection<PixelGrayA<uint8> >( input_plate, output_plate,
                                         camera_model, opt );
      break;
    case VW_CHANNEL_INT16:
      do_projection<PixelGrayA<int16> >( input_plate, output_plate,
                                         camera_model, opt );
      break;
    case VW_CHANNEL_FLOAT32:
      do_projection<PixelGrayA<float32> >( input_plate, output_plate,
                                           camera_model, opt );
      break;
    default:
      vw_throw(InputErr() << "Unsupported channel type.\n");
    }
    break;
  case VW_PIXEL_RGBA:
    switch( channel_type ) {
    case VW_CHANNEL_UINT8:
      do_projection<PixelRGBA<uint8> >( input_plate, output_plate,
                                        camera_model, opt );
      break;
    default:
      vw_throw(InputErr() << "Unsupported channel type.\n");
    }
  default:
    vw_throw(InputErr() << "Unsupported pixel type.\n");
    break;
  }
}

// --- Interface to Terminal ----
int main( int argc, char *argv[] ) {

  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    // Farm out task from here
    do_run( opt );

  } ASP_STANDARD_CATCHES;

  return 0;
}

max_stars_repo_name: AndrewAnnex/StereoPipeline

max_issues_repo_name: AndrewAnnex/StereoPipeline

max_forks_repo_name: AndrewAnnex/StereoPipeline


text:
// Copyright (c) 2017 The Bitcoin developers
// Copyright (c) 2017-2019 The Bitcoin Unlimited developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <limits>
#include <string>

#include "chainparams.h"
#include "forks_csv.h"
#include "test/test_bitcoin.h"

#include <boost/test/unit_test.hpp>

namespace utf = boost::unit_test;

BOOST_FIXTURE_TEST_SUITE(forkscsv_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(forkscsv_read_and_dumpforks_test)
{
    CChainParams& params = ModifiableParams();

    // Set up the testdummy bits for this test
    VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_TESTDUMMY].name = "testdummy";
    VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_TESTDUMMY].gbt_force = false;

    // read of emptiness should be ok for now.
    // In a better implementation, overall file validation should warn the user
    // if no data lines were found in the file.
    std::istringstream is_0("");
    BOOST_CHECK(ReadForksCsv("main", is_0, params.GetModifiableConsensus()));

    std::istringstream is_1(
            "# deployment info for network 'main':\n"
            "main,28,testdummy,1199145601,1230767999,2016,1916,0,0,false\n");
    BOOST_CHECK(ReadForksCsv("main", is_1, params.GetModifiableConsensus()));

    std::istringstream is_2(
            "# deployment info for network 'main':\n"
            "main,28,testdummy,1199145601,1230767999,2016,1916,0,0,false\n");
    BOOST_CHECK(ReadForksCsv("main", is_2, params.GetModifiableConsensus()));

    // dump forks testing
    BOOST_CHECK(NetworkDeploymentInfoCSV(CBaseChainParams::MAIN) == std::string(
            "# deployment info for network 'main':\n"
            "main,28,testdummy,1199145601,1230767999,2016,1916,0,0,false\n"));

    BOOST_CHECK(NetworkDeploymentInfoCSV(CBaseChainParams::UNL) == std::string(
            "# deployment info for network 'nol':\n"));

    BOOST_CHECK(NetworkDeploymentInfoCSV(CBaseChainParams::TESTNET) == std::string(
            "# deployment info for network 'test':\n"
            "test,28,testdummy,1199145601,1230767999,2016,1512,0,0,false\n"));

    BOOST_CHECK(NetworkDeploymentInfoCSV(CBaseChainParams::REGTEST) == std::string(
            "# deployment info for network 'regtest':\n"
            "regtest,28,testdummy,0,999999999999,144,108,0,0,false\n"));

    BOOST_CHECK_THROW( NetworkDeploymentInfoCSV("_foo_"), std::runtime_error );
}

/**
 * Test validation of CSV input fields
 */
BOOST_AUTO_TEST_CASE(forkscsv_validation_test)
{
    // bit number
    for (int num = 0; num < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; num++)
    {
        BOOST_CHECK(ValidateBit(num));
    }
    BOOST_CHECK(!ValidateBit(Consensus::MAX_VERSION_BITS_DEPLOYMENTS + 1));
    BOOST_CHECK(!ValidateBit(-1));

    // network name
    BOOST_CHECK(ValidateNetwork(CBaseChainParams::MAIN));
    BOOST_CHECK(ValidateNetwork(CBaseChainParams::UNL));
    BOOST_CHECK(ValidateNetwork(CBaseChainParams::TESTNET));
    BOOST_CHECK(ValidateNetwork(CBaseChainParams::REGTEST));
    BOOST_CHECK(!ValidateNetwork("nonexistent_net"));
    BOOST_CHECK(!ValidateNetwork(""));

    // gbt_force
    BOOST_CHECK(ValidateGBTForce("true"));
    BOOST_CHECK(ValidateGBTForce("false"));
    BOOST_CHECK(ValidateGBTForce("True"));
    BOOST_CHECK(ValidateGBTForce("False"));
    BOOST_CHECK(ValidateGBTForce("TRUE"));
    BOOST_CHECK(ValidateGBTForce("FALSE"));
    BOOST_CHECK(!ValidateGBTForce("oui"));
    BOOST_CHECK(!ValidateGBTForce("non"));
    BOOST_CHECK(!ValidateGBTForce(""));

    // fork name
    BOOST_CHECK(ValidateForkName("a_fork"));
    BOOST_CHECK(ValidateForkName("a fork"));
    BOOST_CHECK(!ValidateForkName(""));

    // window size
    BOOST_CHECK(!ValidateWindowSize(0));
    BOOST_CHECK(!ValidateWindowSize(1));
    BOOST_CHECK(!ValidateWindowSize(-1));
    BOOST_CHECK(ValidateWindowSize(2));
    BOOST_CHECK(ValidateWindowSize(3));
    BOOST_CHECK(ValidateWindowSize(100));
    BOOST_CHECK(ValidateWindowSize(10000));
    BOOST_CHECK(ValidateWindowSize(std::numeric_limits<int>::max()));
#pragma GCC diagnostic ignored "-Woverflow"
    BOOST_CHECK(!ValidateWindowSize(1 + std::numeric_limits<int>::max()));
#pragma GCC diagnostic pop

    // threshold size (2nd param is window)
    BOOST_CHECK(!ValidateThreshold(1,1));   // 1 is not valid window size
    BOOST_CHECK(!ValidateThreshold(0,1));   // 1 is not valid window size, 0 is not valid threshold
    BOOST_CHECK(!ValidateThreshold(0,2));   // 0 is not valid threshold
    BOOST_CHECK(ValidateThreshold(1,2));
    BOOST_CHECK(!ValidateThreshold(-1,2));
    BOOST_CHECK(!ValidateThreshold(2,1));
    BOOST_CHECK(ValidateThreshold(2,2));
    BOOST_CHECK(ValidateThreshold(1,100));
    BOOST_CHECK(ValidateThreshold(50,100));
    BOOST_CHECK(ValidateThreshold(99,100));
    BOOST_CHECK(ValidateThreshold(100,100));
    BOOST_CHECK(!ValidateThreshold(101,100));
    BOOST_CHECK(ValidateThreshold(1916,2016));
    BOOST_CHECK(ValidateThreshold(2016,2016));
    BOOST_CHECK(ValidateThreshold(2016,2016));
    BOOST_CHECK(!ValidateThreshold(2017,2016));
    BOOST_CHECK(ValidateThreshold(1, std::numeric_limits<int>::max()));

    // starttime / timeout
    BOOST_CHECK(!ValidateTimes(0, 0));     // starttime must be strictly less than timeout
    BOOST_CHECK(!ValidateTimes(-1, 100));  // starttime can not be less than zero
    BOOST_CHECK(!ValidateTimes(1000, 999));// starttime must be strictly less than timeout
    BOOST_CHECK(ValidateTimes(0, 1));      // starttime must be strictly less than timeout
    BOOST_CHECK(ValidateTimes(100, 1001)); // starttime must be strictly less than timeout
    BOOST_CHECK(ValidateTimes(0, 1001));   // starttime must be strictly less than timeout
    BOOST_CHECK(ValidateTimes(0, std::numeric_limits<int64_t>::max()));
    BOOST_CHECK(ValidateTimes(std::numeric_limits<int64_t>::max() - 1,
                              std::numeric_limits<int64_t>::max()));

    // minlockedblocks
    BOOST_CHECK(ValidateMinLockedBlocks(0));   // zero is ok
    BOOST_CHECK(ValidateMinLockedBlocks(1));
    BOOST_CHECK(ValidateMinLockedBlocks(100));
    BOOST_CHECK(!ValidateMinLockedBlocks(-1));
    BOOST_CHECK(ValidateMinLockedBlocks(std::numeric_limits<int>::max()));
#pragma GCC diagnostic ignored "-Woverflow"
    BOOST_CHECK(!ValidateMinLockedBlocks(1 + std::numeric_limits<int>::max()));
#pragma GCC diagnostic pop

    // minlockedtime
    BOOST_CHECK(ValidateMinLockedTime(0));   // zero is ok
    BOOST_CHECK(ValidateMinLockedTime(1));
    BOOST_CHECK(ValidateMinLockedTime(100));
    BOOST_CHECK(!ValidateMinLockedTime(-1));
    BOOST_CHECK(ValidateMinLockedTime(std::numeric_limits<int64_t>::max()));
#pragma GCC diagnostic ignored "-Woverflow"
    BOOST_CHECK(!ValidateMinLockedTime(1 + std::numeric_limits<int64_t>::max()));
#pragma GCC diagnostic pop
}

BOOST_AUTO_TEST_SUITE_END()

ext: cpp

max_stars_repo_name: dagurval/bitcoinunlimited

max_issues_repo_name: dagurval/bitcoinunlimited

max_forks_repo_name: dagurval/bitcoinunlimited


text: #include <boost/log/expressions/formatters/named_scope.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/insert_range.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/tuple.hpp>

#include <string>
namespace hana = boost::hana;
using namespace hana::literals;
using namespace std::literals;

int main() {
    auto xs = hana::make_tuple("Hello"s, "world!"s);
    BOOST_HANA_RUNTIME_CHECK(
        hana::insert_range(xs, 1_c, hana::make_tuple(1, 2, 3)) == hana::make_tuple("Hello"s, 1, 2, 3, "world!"s)
    );
}


text:
// ======================================================================
/*!
 * \brief Implementation of class Reactor
 */
// ======================================================================

#include "Reactor.h"
#include "ConfigTools.h"
#include "Convenience.h"
#include "DynamicPlugin.h"
#include "FmiApiKey.h"
#include "Names.h"
#include "Options.h"
#include "SmartMet.h"
#include "SmartMetEngine.h"

// sleep?t=n queries only in debug mode
#ifndef NDEBUG
#include "Convenience.h"
#endif

#include <boost/algorithm/string/case_conv.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/locale.hpp>
#include <boost/make_shared.hpp>
#include <boost/process/child.hpp>
#include <boost/timer/timer.hpp>
#include <macgyver/AnsiEscapeCodes.h>
#include <macgyver/Exception.h>
#include <macgyver/StringConversion.h>

#include <algorithm>
#include <dlfcn.h>
#include <functional>
#include <iostream>
#include <mysql.h>
#include <stdexcept>
#include <string>

extern "C"
{
#include <unistd.h>
}

namespace fs = boost::filesystem;

namespace
{
std::atomic_bool gIsShuttingDown{false};

void absolutize_path(std::string& file_name)
{
  if (!file_name.empty() && fs::exists(file_name))
  {
    fs::path path(file_name);
    if (!path.is_absolute())
    {
      path = fs::canonical(path);
      file_name = path.string();
    }
  }
}

int is_file_readable(std::string& file_name)
{
  int handle;
  // Open appears to be the only practical solution
  // Use POSIX syscalls here for most efficient solution
  if ((handle = open(file_name.c_str(), O_RDONLY)) >= 0)
  {
    // Open is not enough: directory might be opened but is not readable
    // On NFS: open(2) man page says reading might fail even if open succeeds
    const std::size_t nbytes = 1;
    char buf[nbytes];
    auto rdtest = read(handle, static_cast<void*>(buf), nbytes);
    close(handle);
    if (rdtest < 0)  // 0 bytes might be returned, if file has no data
      return errno;
    return 0;
  }
  return errno;
}
}  // namespace

namespace SmartMet
{
namespace Spine
{
// ----------------------------------------------------------------------
/*!
 * \brief Destructor
 */
// ----------------------------------------------------------------------

Reactor::~Reactor()
{
  // Debug output
  std::cout << "SmartMet Server stopping..." << std::endl;

  // Manual cleanup
  itsInitTasks->stop();
  itsInitTasks.reset();
  itsHandlers.clear();
  itsPlugins.clear();
  itsEngines.clear();
}

// ----------------------------------------------------------------------
/*!
 * \brief Construct from given options
 */
// ----------------------------------------------------------------------

Reactor::Reactor(Options& options) : itsOptions(options), itsInitTasks(new Fmi::AsyncTaskGroup)
{
  try
  {
    // Startup message
    if (!itsOptions.quiet)
    {
      std::cout << ANSI_ITALIC_ON << "+ SmartMet Server "
                << "(compiled on " __DATE__ " " __TIME__ ")" << std::endl
                << "  Copyright (c) Finnish Meteorological Institute" << ANSI_ITALIC_OFF
                << std::endl
                << std::endl;
    }

    if (itsOptions.verbose)
      itsOptions.report();

    // Initial limit for simultaneous active requests
    itsActiveRequestsLimit = itsOptions.throttle.start_limit;

    // This has to be done before threading starts
    mysql_library_init(0, nullptr, nullptr);

    // Initialize the locale before engines are loaded

    boost::locale::generator gen;
    std::locale::global(gen(itsOptions.locale));
    std::cout.imbue(std::locale());

    itsInitTasks->stop_on_error(true);

    itsInitTasks->on_task_error(
        [this](const std::string& name)
        {
          if (!isShuttingDown())
          {
            Fmi::Exception::Trace(BCP, "Operation failed").printError();
            std::cout << __FILE__ << ":" << __LINE__ << ": init task " << name << " failed"
                      << std::endl;
          }
        });
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

void Reactor::init()
{
  try
  {
    // Load engines - all or just the requested ones

    const auto& config = itsOptions.itsConfig;

    if (!config.exists("engines"))
    {
      std::cerr << "Warning: engines setting missing from the server configuration file - are you "
                   "sure you do not need any?"
                << std::endl;
    }
    else
    {
      auto libs = findLibraries("engine");
      itsEngineCount = libs.size();

      for (const auto& libfile : libs)
        loadEngine(libfile, itsOptions.verbose);
    }

    // Load plugins

    if (!config.exists("plugins"))
    {
      throw Fmi::Exception(BCP, "plugins setting missing from the server configuration file");
    }

    auto libs = findLibraries("plugin");

    itsPluginCount = libs.size();

    // Then load them in parallel, keeping track of how many have been loaded
    for (const auto& libfile : libs)
      loadPlugin(libfile, itsOptions.verbose);

    try
    {
      itsInitTasks->wait();
    }
    catch (...)
    {
      std::cout << "Initialization failed" << std::endl;
      exit(1);  // NOLINT not thread safe
    }
    // Set ContentEngine default logging. Do this after plugins are loaded so handlers are
    // recognized

    setLogging(itsOptions.defaultlogging);

    itsInitializing = false;
  }
  catch (...)
  {
    // Inform engines and plugings polling the status that we're going down
    gIsShuttingDown = true;
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Find enabled libraries or plugins from the configuration
 */
// ----------------------------------------------------------------------

std::vector<std::string> Reactor::findLibraries(const std::string& theName) const
{
  const auto& name = theName;
  const auto names = theName + "s";

  const auto moduledir = itsOptions.directory + "/" + names;

  const auto& config = itsOptions.itsConfig;
  const auto& modules = config.lookup(names);

  if (!modules.isGroup())
    throw Fmi::Exception(BCP, names + "-setting must be a group of settings");

  // Collect all enabled modules

  std::vector<std::string> libs;

  for (int i = 0; i < modules.getLength(); i++)
  {
    auto& settings = modules[i];

    if (!settings.isGroup())
      throw Fmi::Exception(BCP, name + " settings must be groups");

    if (settings.getName() == nullptr)
      throw Fmi::Exception(BCP, name + " settings must have names");

    std::string module_name = settings.getName();
    std::string libfile = moduledir + "/" + module_name + ".so";
    lookupPathSetting(config, libfile, names + "." + module_name + ".libfile");

    bool disabled = false;
    lookupHostSetting(itsOptions.itsConfig, disabled, names + "." + module_name + ".disabled");

    if (!disabled)
      libs.push_back(libfile);
    else if (itsOptions.verbose)
    {
      std::cout << Spine::log_time_str() << ANSI_FG_YELLOW << "\t  + [Ignoring " << name << " '"
                << module_name << "' since disabled flag is true]" << ANSI_FG_DEFAULT << std::endl;
    }
  }
  return libs;
}

// ----------------------------------------------------------------------
/*!
 * \brief Required API version
 */
// ----------------------------------------------------------------------

int Reactor::getRequiredAPIVersion() const
{
  return APIVersion;
}

// ----------------------------------------------------------------------
/*!
 * \brief Method to register new public URI/CallBackFunction association.
 *
 * Handler added using this method is visible through getURIMap() method
 */
// ----------------------------------------------------------------------

bool Reactor::addContentHandler(SmartMetPlugin* thePlugin,
                                const std::string& theDir,
                                ContentHandler theCallBackFunction)
{
  return addContentHandlerImpl(false, thePlugin, theDir, theCallBackFunction);
}

// ----------------------------------------------------------------------
/*!
 * \brief Method to register new private URI/CallBackFunction association.
 *
 * Handler added using this method is not visible through getURIMap() method
 */
// ----------------------------------------------------------------------

bool Reactor::addPrivateContentHandler(SmartMetPlugin* thePlugin,
                                       const std::string& theDir,
                                       ContentHandler theCallBackFunction)
{
  return addContentHandlerImpl(true, thePlugin, theDir, theCallBackFunction);
}

// ----------------------------------------------------------------------
/*!
 * \brief Implementation of registeration of new private URI/CallBackFunction association.
 *
 */
// ----------------------------------------------------------------------

bool Reactor::addContentHandlerImpl(bool isPrivate,
                                    SmartMetPlugin* thePlugin,
                                    const std::string& theUri,
                                    ContentHandler theHandler)
{
  try
  {
    if (isShuttingDown())
      return true;

    WriteLock lock(itsContentMutex);
    WriteLock lock2(itsLoggingMutex);
    std::string pluginName = thePlugin->getPluginName();

    auto itsFilter = itsIPFilters.find(Fmi::ascii_tolower_copy(pluginName));
    boost::shared_ptr<IPFilter::IPFilter> filter;
    if (itsFilter != itsIPFilters.end())
    {
      filter = itsFilter->second;
    }

    HandlerPtr theView(new HandlerView(theHandler,
                                       filter,
                                       thePlugin,
                                       theUri,
                                       itsLoggingEnabled,
                                       isPrivate,
                                       itsOptions.accesslogdir));

    std::cout << Spine::log_time_str() << ANSI_BOLD_ON << ANSI_FG_GREEN << " Registered "
              << (isPrivate ? "private " : "") << "URI " << theUri << " for plugin "
              << thePlugin->getPluginName() << ANSI_BOLD_OFF << ANSI_FG_DEFAULT << std::endl;

    // Set the handler and filter
    return itsHandlers.insert(Handlers::value_type(theUri, theView)).second;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!").addParameter("URI", theUri);
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Set the handler for unrecognized requests
 */
// ----------------------------------------------------------------------

bool Reactor::setNoMatchHandler(ContentHandler theHandler)
{
  try
  {
    WriteLock lock(itsContentMutex);

    // Catch everything that is specifically not added elsewhere.
    if (theHandler != nullptr)
    {
      // Set the data members
      HandlerPtr theView(new HandlerView(theHandler));
      itsCatchNoMatchHandler = theView;
      itsCatchNoMatch = true;
    }
    else
    {
      // If function pointer was 0, disable catching hook.
      itsCatchNoMatch = false;
    }

    return itsCatchNoMatch;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Removes all handlers that use specified plugin.
 */
// ----------------------------------------------------------------------
std::size_t Reactor::removeContentHandlers(SmartMetPlugin* thePlugin)
{
  std::size_t count = 0;
  WriteLock lock(itsContentMutex);
  for (auto it = itsHandlers.begin(); it != itsHandlers.end();)
  {
    auto curr = it++;
    if (curr->second and curr->second->usesPlugin(thePlugin))
    {
      const std::string uri = curr->second->getResource();
      const std::string name = curr->second->getPluginName();
      itsHandlers.erase(curr);
      count++;
      WriteLock lock2(itsLoggingMutex);
      std::cout << Spine::log_time_str() << ANSI_BOLD_ON << ANSI_FG_GREEN << " Removed URI " << uri
                << " handled by plugin " << name << ANSI_BOLD_OFF << ANSI_FG_DEFAULT << std::endl;
    }
  }
  return count;
}

// ----------------------------------------------------------------------
/*!
 * \brief Obtain the Handler view corresponding to the given request
 */
// ----------------------------------------------------------------------

boost::optional<HandlerView&> Reactor::getHandlerView(const HTTP::Request& theRequest)
{
  try
  {
    ReadLock lock(itsContentMutex);

    // Try to find a content handler
    auto it = itsHandlers.find(theRequest.getResource());
    if (it == itsHandlers.end())
    {
      // No specific match found, decide what we should do
      if (itsCatchNoMatch)
      {
        // Return with true, as this was catched by external handler
        return boost::optional<HandlerView&>(*itsCatchNoMatchHandler);
      }

      // No match found -- return with failure
      return boost::optional<HandlerView&>();
    }

    return boost::optional<HandlerView&>(*(it->second));
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Getting logging mode
 */
// ----------------------------------------------------------------------

bool Reactor::getLogging() const
{
  return itsLoggingEnabled;
}

// ----------------------------------------------------------------------
/*!
 * \brief Getting logging mode
 */
// ----------------------------------------------------------------------

bool Reactor::lazyLinking() const
{
  return itsOptions.lazylinking;
}
// ----------------------------------------------------------------------
/*!
 * \brief Get copy of the log
 */
// ----------------------------------------------------------------------

AccessLogStruct Reactor::getLoggedRequests() const
{
  try
  {
    if (itsLoggingEnabled)
    {
      LoggedRequests requests;
      ReadLock lock(itsContentMutex);
      for (const auto& handler : itsHandlers)
      {
        requests.insert(std::make_pair(handler.first, handler.second->getLoggedRequests()));
      }
      return std::make_tuple(true, requests, itsLogLastCleaned);
    }

    return std::make_tuple(false, LoggedRequests(), boost::posix_time::ptime());
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Return true if the load is high
 */
// ----------------------------------------------------------------------

bool Reactor::isLoadHigh() const
{
  return itsHighLoadFlag;
}

// ----------------------------------------------------------------------
/*!
 * \brief Get a copy of active requests
 */
// ----------------------------------------------------------------------

ActiveRequests::Requests Reactor::getActiveRequests() const
{
  return itsActiveRequests.requests();
}

// ----------------------------------------------------------------------
/*!
 * \brief Print the given request list
 */
// ----------------------------------------------------------------------

void print_requests(const Spine::ActiveRequests::Requests& requests)
{
  // Based on Admin::Plugin::requestActiveRequests

  auto now = boost::posix_time::microsec_clock::universal_time();

  std::cout << "Printing active requests due to high load:\n"
            << "Number\tID\tTime\tDuration\tIP\tAPIKEY\t\tURI\n";

  std::size_t row = 0;
  for (const auto& id_info : requests)
  {
    const auto id = id_info.first;
    const auto& time = id_info.second.time;
    const auto& req = id_info.second.request;

    auto duration = now - time;

    const bool check_access_token = false;
    auto apikey = FmiApiKey::getFmiApiKey(req, check_access_token);

    // clang-format off
    std::cout << row++ << "\t"
              << Fmi::to_string(id) << "\t"
              << Fmi::to_iso_extended_string(time.time_of_day()) << "\t"
              << Fmi::to_string(duration.total_milliseconds() / 1000.0) << "\t"
              << req.getClientIP() << "\t"
              << (apikey ? *apikey : "-\t") << "\t"
              << req.getURI() << "\n";
    // clang-format on
  }
  std::cout << std::flush;
}

// ----------------------------------------------------------------------
/*!
 * \brief Add a new active request
 */
// ----------------------------------------------------------------------

std::size_t Reactor::insertActiveRequest(const HTTP::Request& theRequest)
{
  auto key = itsActiveRequests.insert(theRequest);

  auto n = itsActiveRequests.size();

  // Run alert script if needed

  if (n >= itsOptions.throttle.alert_limit && !itsOptions.throttle.alert_script.empty())
  {
    if (itsActiveRequestsLimit < itsOptions.throttle.limit)
    {
      // Do nothing when ramping up
    }
    else if (itsRunningAlertScript)
    {
      // This turns out to be a bit too verbose
      // if (itsOptions.verbose)
      // std::cerr << Spine::log_time_str() << " Alert script already running" << std::endl;
    }
    else
    {
      itsRunningAlertScript = true;

      // First print the active requests since the alert script may be slow
      if (itsOptions.verbose)
        print_requests(getActiveRequests());

      if (!boost::filesystem::exists(itsOptions.throttle.alert_script))
      {
        std::cerr << Spine::log_time_str() << " Configured alert script  "
                  << itsOptions.throttle.alert_script << " does not exist" << std::endl;
      }
      else
      {
        // Run the alert script in a separate thread not to delay the user response too much
        if (itsOptions.verbose)
          std::cerr << Spine::log_time_str() << " Running alert script "
                    << itsOptions.throttle.alert_script << std::endl;

        std::thread thr(
            [this]
            {
              try
              {
                boost::process::child cld(itsOptions.throttle.alert_script);
                cld.wait();
              }
              catch (...)
              {
                std::cerr << Spine::log_time_str() << " Running alert script "
                          << itsOptions.throttle.alert_script << " failed!" << std::endl;
              }
              itsRunningAlertScript = false;
            });
        thr.detach();
      }
    }
  }

  // Check if we should report high load

  if (n < itsActiveRequestsLimit)
  {
    itsHighLoadFlag = false;
    return key;
  }

  // Load is now high

  itsActiveRequestsCounter = 0;  // now new finished active requests yet

  itsHighLoadFlag = true;

  if (itsOptions.verbose)
    std::cerr << Spine::log_time_str() << " " << itsActiveRequests.size()
              << " active requests, limit is " << itsActiveRequestsLimit << "/"
              << itsOptions.throttle.limit << std::endl;

  // Reduce the limit back down unless already smaller due to being just started
  if (itsActiveRequestsLimit > itsOptions.throttle.restart_limit)
  {
    itsActiveRequestsLimit = itsOptions.throttle.restart_limit;
    if (itsOptions.verbose)
      std::cerr << Spine::log_time_str() << " dropping active requests limit to "
                << itsOptions.throttle.restart_limit << "/" << itsOptions.throttle.limit
                << std::endl;
  }

  return key;
}

// ----------------------------------------------------------------------
/*!
 * \brief Remove an old active request
 */
// ----------------------------------------------------------------------

void Reactor::removeActiveRequest(std::size_t theKey, HTTP::Status theStatusCode)
{
  itsActiveRequests.remove(theKey);

  if (itsActiveRequests.size() < itsActiveRequestsLimit)
    itsHighLoadFlag = false;

  // Update current limit for simultaneous requests only if the request was a success
  //
  // We also ignore the 204 No content response, which the backend uses to indicate
  // the etagged result is still valid, since generating no content successfully
  // does not mean the server is not having load problems for example due to i/o issues.

  if (theStatusCode != HTTP::ok)
    return;

  if (++itsActiveRequestsCounter % itsOptions.throttle.increase_rate == 0)
  {
    if (itsActiveRequestsLimit < itsOptions.throttle.limit)
    {
      auto new_limit = ++itsActiveRequestsLimit;

      if (itsOptions.verbose)
        std::cerr << Spine::log_time_str() << " increased active requests limit to " << new_limit
                  << "/" << itsOptions.throttle.limit << std::endl;
    }
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Start a new active request to a backend
 */
// ----------------------------------------------------------------------

void Reactor::startBackendRequest(const std::string& theHost, int thePort)
{
  itsActiveBackends.start(theHost, thePort);
}

// ----------------------------------------------------------------------
/*!
 * \brief Stop a request to a backend
 */
// ----------------------------------------------------------------------

void Reactor::stopBackendRequest(const std::string& theHost, int thePort)
{
  itsActiveBackends.stop(theHost, thePort);
}

// ----------------------------------------------------------------------
/*!
 * \brief Reset request count to a backend
 */
// ----------------------------------------------------------------------

void Reactor::resetBackendRequest(const std::string& theHost, int thePort)
{
  itsActiveBackends.reset(theHost, thePort);
}

// ----------------------------------------------------------------------
/*!
 * \brief Get the counts for active backend requests
 */
// ----------------------------------------------------------------------

ActiveBackends::Status Reactor::getBackendRequestStatus() const
{
  return itsActiveBackends.status();
}

// ----------------------------------------------------------------------
/*!
 * \brief Get registered URIs
 */
// ----------------------------------------------------------------------

URIMap Reactor::getURIMap() const
{
  try
  {
    ReadLock lock(itsContentMutex);
    URIMap theMap;

    for (const auto& handlerPair : itsHandlers)
    {
      // Getting plugin names during shutdown may throw due to a call to a pure virtual method.
      // This mitigates the problem, but does not solve it. The shutdown flag should be
      // locked for the duration of this loop.
      if (isShuttingDown())
        return {};

      if (not handlerPair.second->isPrivate())
      {
        theMap.insert(std::make_pair(handlerPair.first, handlerPair.second->getPluginName()));
      }
    }

    return theMap;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Clean the log of old entries
 */
// ----------------------------------------------------------------------

/* [[noreturn]] */ void Reactor::cleanLog()
{
  try
  {
    // This function must be called as an argument to the cleaner thread

    auto maxAge = boost::posix_time::hours(24);  // Here we give the maximum log time span, 24 hours

    while (!isShuttingDown())
    {
      // Sleep for some time
      boost::this_thread::sleep(boost::posix_time::seconds(5));

      auto firstValidTime = boost::posix_time::second_clock::local_time() - maxAge;
      for (auto& handlerPair : itsHandlers)
      {
        if (isShuttingDown())
          return;

        handlerPair.second->cleanLog(firstValidTime);
        handlerPair.second->flushLog();
      }

      if (itsLogLastCleaned < firstValidTime)
      {
        itsLogLastCleaned = firstValidTime;
      }
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "cleanLog operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Set request logging activity on or off. Only call after the handlers are registered
 */
// ----------------------------------------------------------------------

void Reactor::setLogging(bool loggingEnabled)
{
  try
  {
    WriteLock lock(itsLoggingMutex);

    // Check for no change in status
    if (itsLoggingEnabled == loggingEnabled)
      return;

    itsLoggingEnabled = loggingEnabled;

    if (itsLoggingEnabled)
    {
      // See if cleaner thread is running for some reason
      if (itsLogCleanerThread.get() != nullptr && itsLogCleanerThread->joinable())
      {
        // Kill any remaining thread
        itsLogCleanerThread->interrupt();
        itsLogCleanerThread->join();
      }

      // Launch log cleaner thread
      itsLogCleanerThread.reset(new boost::thread(boost::bind(&Reactor::cleanLog, this)));

      // Set log cleanup time
      itsLogLastCleaned = boost::posix_time::second_clock::local_time();
    }
    else
    {
      // Status set to false, make the transition true->false
      // Erase log, stop cleaning thread
      itsLogCleanerThread->interrupt();
      itsLogCleanerThread->join();
    }

    // Set logging status for ALL plugins
    for (auto& handlerPair : itsHandlers)
    {
      handlerPair.second->setLogging(itsLoggingEnabled);
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief List the names of the loaded plugins
 */
// ----------------------------------------------------------------------

void Reactor::listPlugins() const
{
  try
  {
    // List all plugins

    std::cout << std::endl << "List of plugins:" << std::endl;

    for (const auto& plugin : itsPlugins)
    {
      std::cout << "  " << plugin->filename() << '\t' << plugin->pluginname() << '\t'
                << plugin->apiversion() << std::endl;
    }

    // Number of plugins
    std::cout << itsPlugins.size() << " plugin(s) loaded in memory." << std::endl << std::endl;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Load a new plugin
 */
// ----------------------------------------------------------------------

bool Reactor::loadPlugin(const std::string& theFilename, bool /* verbose */)
{
  try
  {
    std::string pluginname = Names::plugin_name(theFilename);

    std::string configfile;
    lookupConfigSetting(itsOptions.itsConfig, configfile, "plugins." + pluginname);

    if (!configfile.empty())
    {
      absolutize_path(configfile);

      if (is_file_readable(configfile) != 0)
        throw Fmi::Exception(BCP,
                             "plugin " + pluginname + " config " + configfile + " is unreadable: " +
                                 std::strerror(errno));  // NOLINT not thread safe
    }

    // Find the ip filters
    std::vector<std::string> filterTokens;
    lookupHostStringSettings(
        itsOptions.itsConfig, filterTokens, "plugins." + pluginname + ".ip_filters");

    if (not filterTokens.empty())
    {
      boost::shared_ptr<IPFilter::IPFilter> theFilter;

      try
      {
        theFilter.reset(new IPFilter::IPFilter(filterTokens));
        std::cout << "IP Filter registered for plugin: " << pluginname << std::endl;
      }
      catch (std::runtime_error& err)
      {
        // No IP filter for this plugin
        std::cout << "No IP filter for plugin: " << pluginname << ". Reason: " << err.what()
                  << std::endl;
      }

      auto inserted = itsIPFilters.insert(std::make_pair(pluginname, theFilter));
      if (!inserted.second)
      {
        // Plugin name is not unique
        std::cout << "No IP filter for plugin: " << pluginname << ". Reason: plugin name not unique"
                  << std::endl;
      }
    }

    boost::shared_ptr<DynamicPlugin> plugin(new DynamicPlugin(theFilename, configfile, *this));

    if (plugin.get() != nullptr)
    {
      // Start to initialize the plugin

      itsInitTasks->add("Load plugin[" + theFilename + "]",
                        [this, plugin, pluginname]()
                        { initializePlugin(plugin.get(), pluginname); });

      itsPlugins.push_back(plugin);
      return true;
    }

    return false;  // Should we throw??
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!").addParameter("Filename", theFilename);
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Method to create a new instance of loaded class
 */
// ----------------------------------------------------------------------

void* Reactor::newInstance(const std::string& theClassName, void* user_data)
{
  try
  {
    // Search for the class creator

    auto it = itsEngines.find(theClassName);
    if (it == itsEngines.end())
    {
      std::cerr << ANSI_FG_RED << "Unable to create a new instance of engine class '"
                << theClassName << "'" << std::endl
                << "No such class was found loaded in the EngineHood" << ANSI_FG_DEFAULT
                << std::endl;
      return nullptr;
    }

    // config names are all lower case
    std::string name = Fmi::ascii_tolower_copy(theClassName);
    std::string configfile = itsEngineConfigs.find(name)->second;

    // Construct the new engine instance
    void* engineInstance = it->second(configfile.c_str(), user_data);

    auto* theEngine = reinterpret_cast<SmartMetEngine*>(engineInstance);

    // Fire the initialization thread
    itsInitTasks->add("New engine instance[" + theClassName + "]",
                      [this, theEngine, theClassName]()
                      { initializeEngine(theEngine, theClassName); });

    return engineInstance;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!").addParameter("Class", theClassName);
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Method to create a new instance of loaded class
 */
// ----------------------------------------------------------------------

SmartMetEngine* Reactor::getSingleton(const std::string& theClassName, void* /* user_data */)
{
  try
  {
    if (isShuttingDown())
    {
      // Notice that this exception most likely terminates a plugin's initialization
      // phase when the plugin requests an engine. This exception is usually
      // caught in the plugin's initPlugin() method.

      throw Fmi::Exception(BCP, "Shutdown active!").disableStackTrace();
    }

    // Search for the singleton in
    auto it = itsSingletons.find(theClassName);

    if (it == itsSingletons.end())
    {
      // Log error and return with zero
      std::cout << ANSI_FG_RED << "No engine '" << theClassName << "' was found loaded in memory."
                << ANSI_FG_DEFAULT << std::endl;

      return nullptr;
    }

    // Found it, return the already-created instance of class.
    auto* engine = it->second;

    // Engines must be wait() - ed before use, do it here so plugins don't have worry about it

    engine->wait();

    return isShuttingDown() ? nullptr : engine;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!").addParameter("ClassName", theClassName);
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Dynamic Engine loader
 */
// ----------------------------------------------------------------------

bool Reactor::loadEngine(const std::string& theFilename, bool /* verbose */)
{
  try
  {
    // Open the dynamic library specified by private data
    // member module_filename

    std::string enginename = Names::engine_name(theFilename);

    std::string configfile;
    lookupConfigSetting(itsOptions.itsConfig, configfile, "engines." + enginename);

    if (configfile != "")
    {
      absolutize_path(configfile);
      if (is_file_readable(configfile) != 0)
        throw Fmi::Exception(BCP,
                             "engine " + enginename + " config " + configfile + " is unreadable: " +
                                 std::strerror(errno));  // NOLINT not thread safe
    }

    itsEngineConfigs.insert(ConfigList::value_type(enginename, configfile));

    void* itsHandle = dlopen(theFilename.c_str(), RTLD_NOW | RTLD_GLOBAL);
    if (itsHandle == nullptr)
    {
      // Error occurred while opening the dynamic library
      throw Fmi::Exception(BCP,
                           "Unable to load dynamic engine class library: " +
                               std::string(dlerror()));  // NOLINT not thread safe
    }

    // Load the symbols (pointers to functions in dynamic library)

    auto itsNamePointer = reinterpret_cast<EngineNamePointer>(dlsym(itsHandle, "engine_name"));

    auto itsCreatorPointer =
        reinterpret_cast<EngineInstanceCreator>(dlsym(itsHandle, "engine_class_creator"));

    // Check that pointers to function were loaded succesfully
    if (itsNamePointer == nullptr || itsCreatorPointer == nullptr)
    {
      throw Fmi::Exception(BCP,
                           "Cannot resolve dynamic library symbols: " +
                               std::string(dlerror()));  // NOLINT not thread safe
    }

    // Create a permanent string out of engines human readable name

    if (!itsEngines
             .insert(EngineList::value_type((*itsNamePointer)(),
                                            static_cast<EngineInstanceCreator>(itsCreatorPointer)))
             .second)
      return false;

    // Begin constructing the engine
    auto* singleton = newInstance(itsNamePointer(), nullptr);

    // Check whether the preliminary creation succeeded
    if (singleton == nullptr)
    {
      // Log error and return with zero
      std::cout << ANSI_FG_RED << "No engine '" << itsNamePointer()
                << "' was found loaded in memory." << ANSI_FG_DEFAULT << std::endl;

      return false;
    }

    auto* engine = reinterpret_cast<SmartMetEngine*>(singleton);
    itsSingletons.insert(SingletonList::value_type(itsNamePointer(), engine));

    return true;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!").addParameter("Filename", theFilename);
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Initialize an engine
 */
// ----------------------------------------------------------------------

void Reactor::initializeEngine(SmartMetEngine* theEngine, const std::string& theName)
{
  boost::timer::cpu_timer timer;
  theEngine->construct(theName, this);
  timer.stop();

  auto now_initialized = itsInitializedEngineCount.fetch_add(1) + 1;

  std::string report =
      (std::string(ANSI_FG_GREEN) + "Engine [" + theName +
       "] initialized in %t sec CPU, %w sec real (" + std::to_string(now_initialized) + "/" +
       std::to_string(itsEngineCount) + ")" + ANSI_FG_DEFAULT);

  std::cout << Spine::log_time_str() << " " << timer.format(2, report) << std::endl;

  if (now_initialized == itsEngineCount)
    std::cout << log_time_str()
              << std::string(ANSI_FG_GREEN) + std::string(" *** All ") +
                     std::to_string(itsEngineCount) + " engines initialized" + ANSI_FG_DEFAULT
              << std::endl;
}

// ----------------------------------------------------------------------
/*!
 * \brief Initialize a plugin
 */
// ----------------------------------------------------------------------

void Reactor::initializePlugin(DynamicPlugin* thePlugin, const std::string& theName)
{
  boost::timer::cpu_timer timer;
  thePlugin->initializePlugin();
  timer.stop();

  auto now_initialized = itsInitializedPluginCount.fetch_add(1) + 1;

  std::string report =
      (std::string(ANSI_FG_GREEN) + "Plugin [" + theName +
       "] initialized in %t sec CPU, %w sec real (" + std::to_string(now_initialized) + "/" +
       std::to_string(itsPluginCount) + ")" + ANSI_FG_DEFAULT);

  std::cout << Spine::log_time_str() << " " << timer.format(2, report) << std::endl;

  if (now_initialized == itsPluginCount)
    std::cout << log_time_str()
              << std::string(ANSI_FG_GREEN) + std::string(" *** All ") +
                     std::to_string(itsPluginCount) + " plugins initialized" + ANSI_FG_DEFAULT
              << std::endl;
}

// ----------------------------------------------------------------------
/*!
 * \brief List the names of the loaded engines
 */
// ----------------------------------------------------------------------

void Reactor::listEngines() const
{
  try
  {
    std::cout << std::endl << "List of engines:" << std::endl;

    for (const auto& engine : itsEngines)
    {
      std::cout << "  " << engine.first << std::endl;
    }

    // Number of engines
    std::cout << itsEngines.size() << " engines(s) loaded in memory." << std::endl << std::endl;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

bool Reactor::addClientConnectionStartedHook(const std::string& hookName,
                                             ClientConnectionStartedHook theHook)
{
  try
  {
    WriteLock lock(itsHookMutex);
    return itsClientConnectionStartedHooks.insert(std::make_pair(hookName, theHook)).second;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

bool Reactor::addBackendConnectionFinishedHook(const std::string& hookName,
                                               BackendConnectionFinishedHook theHook)
{
  try
  {
    WriteLock lock(itsHookMutex);
    return itsBackendConnectionFinishedHooks.insert(std::make_pair(hookName, theHook)).second;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

bool Reactor::addClientConnectionFinishedHook(const std::string& hookName,
                                              ClientConnectionFinishedHook theHook)
{
  try
  {
    WriteLock lock(itsHookMutex);
    return itsClientConnectionFinishedHooks.insert(std::make_pair(hookName, theHook)).second;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

void Reactor::callClientConnectionStartedHooks(const std::string& theClientIP)
{
  try
  {
    ReadLock lock(itsHookMutex);

    for (auto& pair : itsClientConnectionStartedHooks)
    {
      pair.second(theClientIP);
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

void Reactor::callBackendConnectionFinishedHooks(
    const std::string& theHostName,
    int thePort,
    Spine::HTTP::ContentStreamer::StreamerStatus theStatus)
{
  try
  {
    ReadLock lock(itsHookMutex);

    for (auto& pair : itsBackendConnectionFinishedHooks)
    {
      pair.second(theHostName, thePort, theStatus);
    }
  }
  catch (...)
  {
    // This is called by the Connection destructor, hence we must not throw
    Fmi::Exception ex(BCP, "Connections destructing, not calling hooks anymore", nullptr);
    ex.printError();
    // Fall through to the destructor
  }
}

void Reactor::callClientConnectionFinishedHooks(const std::string& theClientIP,
                                                const boost::system::error_code& theError)
{
  try
  {
    ReadLock lock(itsHookMutex);
    for (auto& pair : itsClientConnectionFinishedHooks)
    {
      pair.second(theClientIP, theError);
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

SmartMetEngine* Reactor::getEnginePtr(const std::string& theClassName, void* user_data)
{
  auto* ptr = getSingleton(theClassName, user_data);
  if (ptr != nullptr)
    return ptr;

  if (isShuttingDown())
  {
    throw Fmi::Exception::Trace(
        BCP, "Shutdown in progress - engine " + theClassName + " is not available")
        .disableStackTrace();
  }

  throw Fmi::Exception::Trace(BCP, "No " + theClassName + " engine available");
}

bool Reactor::isShuttingDown()
{
  return gIsShuttingDown;
}

bool Reactor::isInitializing() const
{
  return itsInitializing;
}

void Reactor::shutdown()
{
  try
  {
    // We are no more interested about init task errors when shutdown has been requested
    itsInitTasks->stop_on_error(false);

    gIsShuttingDown = true;

    Fmi::AsyncTaskGroup shutdownTasks;

    // Requesting all plugins to shutdown. Notice that now the plugins know
    // how many requests they have received and how many responses they have sent.
    // In the other words, the plugins wait until the number of responses equals to
    // the number of the requests. This was implemented on the top level, because
    // this way we can relatively safely shutdown all plugins even if the do not
    // implement their own shutdown() method.

    std::cout << ANSI_FG_RED << ANSI_BOLD_ON << "\nShutdown plugins" << ANSI_BOLD_OFF
              << ANSI_FG_DEFAULT << std::endl;

    for (auto& plugin : itsPlugins)
    {
      std::cout << ANSI_FG_RED << "* Plugin [" << plugin->pluginname() << "] shutting down\n"
                << ANSI_FG_DEFAULT;
      shutdownTasks.add("Plugin [" + plugin->pluginname() + "] shutdown",
                        [&plugin]()
                        {
                          plugin->shutdownPlugin();
                          plugin.reset();
                        });
    }

    shutdownTasks.wait();
    std::cout << ANSI_FG_RED << "* Plugin shutdown completed" << ANSI_FG_DEFAULT << std::endl;

    // STEP 4: Requesting all engines to shutdown.

    std::cout << ANSI_FG_RED << ANSI_BOLD_ON << "\nShutdown engines" << ANSI_BOLD_OFF
              << ANSI_FG_DEFAULT << std::endl;

    for (auto& singleton : itsSingletons)
    {
      std::ostringstream tmp1;
      tmp1 << ANSI_FG_RED << "* Engine [" << singleton.first << "] shutting down" << ANSI_FG_DEFAULT
           << '\n';
      std::cout << tmp1.str() << std::flush;
      auto* engine = singleton.second;
      shutdownTasks.add("Engine [" + singleton.first + "] shutdown",
                        [&engine, singleton]()
                        {
                          engine->shutdownEngine();
                          std::ostringstream tmp2;
                          tmp2 << ANSI_FG_MAGENTA << "* Engine [" << singleton.first
                               << "] shutdown complete" << ANSI_FG_DEFAULT << '\n';
                          std::cout << tmp2.str() << std::flush;
                        });
    }

    shutdownTasks.wait();

    // STEP 5: Deleting engines. We should not delete engines before they are all shutted down
    //         because they might use other engines (for example, obsengine => geoengine).

    std::cout << ANSI_FG_RED << ANSI_BOLD_ON << "\nDeleting engines" << ANSI_BOLD_OFF
              << ANSI_FG_DEFAULT << std::endl;

    for (auto& singleton : itsSingletons)
    {
      std::cout << ANSI_FG_RED << "* Deleting engine [" << singleton.first << "]\n"
                << ANSI_FG_DEFAULT;
      auto* engine = singleton.second;
      boost::this_thread::disable_interruption do_not_disturb;
      delete engine;
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Shutdown operation failed!");
  }
}

Fmi::Cache::CacheStatistics Reactor::getCacheStats() const
{
  Fmi::Cache::CacheStatistics ret;

  // Engines

  for (auto engine_item : itsSingletons)
  {
    auto* engine = engine_item.second;
    if (engine && engine->ready())
    {
      Fmi::Cache::CacheStatistics engine_stats = engine->getCacheStats();
      if (!engine_stats.empty())
        ret.insert(engine_stats.begin(), engine_stats.end());
    }
  }

  // Plugins
  for (auto plugin_item : itsPlugins)
  {
    auto* smartmet_plugin = plugin_item->getPlugin();
    if (smartmet_plugin && !smartmet_plugin->isInitActive())
    {
      Fmi::Cache::CacheStatistics plugin_stats = smartmet_plugin->getCacheStats();
      ret.insert(plugin_stats.begin(), plugin_stats.end());
    }
  }

  return ret;
}

}  // namespace Spine
}  // namespace SmartMet

max_stars_repo_name: fmidev/smartmet-library-spine

max_issues_repo_name: fmidev/smartmet-library-spine

max_forks_repo_name: fmidev/smartmet-library-spine


text:
// Copyright 2019-present MongoDB 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 <iostream>

#include <boost/log/trivial.hpp>

#include <yaml-cpp/yaml.h>

#include <mongocxx/exception/server_error_code.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/pool.hpp>

#include <loki/ScopeGuard.h>

#include <gennylib/ExecutionStrategy.hpp>
#include <gennylib/MongoException.hpp>
#include <gennylib/PhaseLoop.hpp>
#include <gennylib/context.hpp>
#include <testlib/ActorHelper.hpp>

#include <testlib/helpers.hpp>

namespace Catchers = Catch::Matchers;

namespace genny::test {
struct StrategyActor : public Actor {
public:
    struct PhaseState {
        PhaseState(const PhaseContext& context)
            : options{ExecutionStrategy::getOptionsFrom(context, "ExecutionStrategy")},
              throwCount{context.get<int, false>("ThrowCount").value_or(0)},
              shouldThrow{context.get<bool, false>("ShouldThrow").value_or(false)} {}

        ExecutionStrategy::RunOptions options;
        signed long long throwCount;
        bool shouldThrow;
    };

    using Exception = genny::MongoException;

    static constexpr auto kErrorMessage = "Testing ExecutionStrategy catching";

public:
    StrategyActor(ActorContext& context)
        : Actor(context),
          strategy{context.operation("sipmle", StrategyActor::id())},
          _loop{context} {}

    void run() override {
        for (auto&& config : _loop) {
            if (config->shouldThrow) {
                REQUIRE_THROWS_AS(runOnce(*config, config.phaseNumber()), Exception);
            } else {
                REQUIRE_NOTHROW(runOnce(*config, config.phaseNumber()));
            }
        }
    }

    void runOnce(PhaseState& state, PhaseNumber phase) {
        auto guard = Loki::MakeGuard([&]() {
            auto attempts = strategy.lastResult().numAttempts;
            BOOST_LOG_TRIVIAL(info) << "Phase " << phase << ": tried " << attempts << " times";
            allRuns += attempts;

            if (!strategy.lastResult().wasSuccessful) {
                BOOST_LOG_TRIVIAL(info) << "Phase " << phase << ": failed";
                ++failedRuns;
            }
        });

        strategy.run(
            [&](metrics::OperationContext&) {
                if (state.throwCount > 0) {
                    --state.throwCount;
                    throw Exception(kErrorMessage);
                }

                ++goodRuns;
            },
            state.options);
    }

    static std::string_view defaultName() {
        return "Strategy";
    }

public:
    ExecutionStrategy strategy;
    size_t allRuns = 0;
    size_t failedRuns = 0;
    size_t goodRuns = 0;

private:
    PhaseLoop<PhaseState> _loop;
};

const StrategyActor* extractActor(const std::unique_ptr<Actor>& baseActor) {

    auto actor = dynamic_cast<genny::test::StrategyActor*>(baseActor.get());
    REQUIRE(actor != nullptr);
    return actor;
}
}  // namespace genny::test

TEST_CASE("ExecutionStrategy") {
    mongocxx::instance::current();

    SECTION("Test a clean function") {
        YAML::Node config = YAML::Load(R"(
SchemaVersion: 2018-07-01
Actors:
- Name: Simple
  Type: Strategy
)");

        // Make three phases
        constexpr size_t kPhaseCount = 3;
        auto yamlSimpleActor = config["Actors"][0];
        for (size_t i = 0; i < kPhaseCount; ++i) {
            yamlSimpleActor["Phases"][i]["Phase"] = i;
        }

        auto producer = std::make_shared<genny::DefaultActorProducer<genny::test::StrategyActor>>();
        genny::ActorHelper elf(config, 1, {{"Strategy", producer}});

        auto verifyFun = [&](const genny::WorkloadContext& context) {
            auto actor = genny::test::extractActor(context.actors()[0]);

            REQUIRE(actor->allRuns == kPhaseCount);
            REQUIRE(actor->goodRuns == kPhaseCount);
            REQUIRE(actor->failedRuns == 0);
            REQUIRE(actor->strategy.lastResult().wasSuccessful);
        };

        // Give it a run
        elf.runDefaultAndVerify(verifyFun);

        // Give it another run
        elf.runDefaultAndVerify(verifyFun);

        // Once more, for spirit
        elf.runDefaultAndVerify(verifyFun);
    }

    SECTION("Test default exception catching") {
        YAML::Node config = YAML::Load(R"(
SchemaVersion: 2018-07-01
Actors:
- Name: Simple
  Type: Strategy
)");

        // Make three phases
        constexpr size_t kPhaseCount = 3;
        constexpr size_t kExpectedFailures = 2;
        auto yamlSimpleActor = config["Actors"][0];
        for (size_t i = 0; i < kPhaseCount; ++i) {
            yamlSimpleActor["Phases"][i]["Phase"] = i;
            yamlSimpleActor["Phases"][i]["ThrowCount"] = i;
        }

        auto producer = std::make_shared<genny::DefaultActorProducer<genny::test::StrategyActor>>();
        genny::ActorHelper elf(config, 1, {{"Strategy", producer}});

        auto verifyFun = [&](const genny::WorkloadContext& context) {
            auto actor = genny::test::extractActor(context.actors()[0]);

            // We are willing to throw twice on Phase 2, but we are not retrying, so we our fails
            // match our catches
            REQUIRE(actor->allRuns == kPhaseCount);
            REQUIRE(actor->failedRuns == kExpectedFailures);
            REQUIRE(actor->goodRuns == (kPhaseCount - kExpectedFailures));
            REQUIRE(!actor->strategy.lastResult().wasSuccessful);
        };

        elf.runDefaultAndVerify(verifyFun);
    }

    SECTION("Test default exception rethrow") {
        YAML::Node config = YAML::Load(R"(
SchemaVersion: 2018-07-01
Actors:
- Name: Simple
  Type: Strategy
  Phases:
    - Phase: 0
      ThrowCount: 1
      ShouldThrow: True
      ExecutionStrategy:
        ThrowOnFailure: True
        Retries: 0
)");

        auto producer = std::make_shared<genny::DefaultActorProducer<genny::test::StrategyActor>>();
        genny::ActorHelper elf(config, 1, {{"Strategy", producer}});

        auto verifyFun = [&](const genny::WorkloadContext& context) {
            auto actor = genny::test::extractActor(context.actors()[0]);

            REQUIRE(actor->allRuns == 1);
            REQUIRE(actor->failedRuns == 1);
            REQUIRE(actor->goodRuns == 0);
            REQUIRE(!actor->strategy.lastResult().wasSuccessful);
        };

        elf.runDefaultAndVerify(verifyFun);
    }

    SECTION("Test retries and failure reset") {
        YAML::Node config = YAML::Load(R"(
SchemaVersion: 2018-07-01
Actors:
- Name: Simple
  Type: Strategy
)");

        // Make three phases
        constexpr size_t kPhaseCount = 3;
        constexpr size_t kExpectedFailures = 2;
        auto yamlSimpleActor = config["Actors"][0];
        for (size_t i = 0; i < kPhaseCount; ++i) {
            yamlSimpleActor["Phases"][i]["Phase"] = i;
        }

        size_t run = 0;
        size_t good = 0;
        size_t failed = 0;

        // Do not throw but be very willing to catch
        yamlSimpleActor["Phases"][0]["ThrowCount"] = 0;
        yamlSimpleActor["Phases"][0]["ExecutionStrategy"]["Retries"] = 10;
        ++run;
        ++good;

        // Throw one more time than we can catch
        constexpr size_t kOverthrows = 4;
        yamlSimpleActor["Phases"][1]["ThrowCount"] = kOverthrows;
        yamlSimpleActor["Phases"][1]["ExecutionStrategy"]["Retries"] = kOverthrows - 1;
        run += kOverthrows;
        ++failed;

        // Throw exactly as many times as we can catch
        constexpr size_t kMatchedThrows = 2;
        yamlSimpleActor["Phases"][2]["ThrowCount"] = kMatchedThrows;
        yamlSimpleActor["Phases"][2]["ExecutionStrategy"]["Retries"] = kMatchedThrows;
        run += kMatchedThrows + 1;
        ++good;

        auto producer = std::make_shared<genny::DefaultActorProducer<genny::test::StrategyActor>>();
        genny::ActorHelper elf(config, 1, {{"Strategy", producer}});

        auto verifyFun = [&](const genny::WorkloadContext& context) {
            auto actor = genny::test::extractActor(context.actors()[0]);

            REQUIRE(actor->allRuns == run);
            REQUIRE(actor->failedRuns == failed);
            REQUIRE(actor->goodRuns == good);
            REQUIRE(actor->strategy.lastResult().wasSuccessful);
        };

        elf.runDefaultAndVerify(verifyFun);
    }
}

max_stars_repo_name: vrachev/genny

max_issues_repo_name: vrachev/genny

max_forks_repo_name: vrachev/genny


text:
#include "daemon_manager.hpp"
#include <boost/program_options.hpp>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sqlite3.h>
//using namespace boost::program_options;

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

//    options_description desc{"Options"};

    int flags, opt;
    int nsecs, tfnd;

    nsecs = 0;
    tfnd = 0;
    flags = 0;
    while ((opt = getopt(argc, argv, "o:")) != -1) {
        switch (opt) {
        case 'o':
            nsecs = atoi(optarg);
            tfnd = 1;
            break;
        default: /* '?' */
            fprintf(stderr, "Usage: %s [-o option]\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    if(nsecs == 1) {
    	DaemonManager *manager = new DaemonManager(start);
    } else if(nsecs == 2) {
        DaemonManager *manager = new DaemonManager(stop);
    }

    return 0;
}

max_stars_repo_name: mikefeneley/chaos-cloud

max_issues_repo_name: mikefeneley/chaos-cloud

max_forks_repo_name: mikefeneley/chaos-cloud


text:
// Copyright 2015-2020 Tier IV, Inc. All rights reserved.
//
// 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 <quaternion_operation/quaternion_operation.h>

#include <boost/algorithm/clamp.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <traffic_simulator/behavior/pedestrian/follow_lane_action.hpp>
#include <vector>

namespace entity_behavior
{
namespace pedestrian
{
FollowLaneAction::FollowLaneAction(const std::string & name, const BT::NodeConfiguration & config)
: entity_behavior::PedestrianActionNode(name, config)
{
}

void FollowLaneAction::getBlackBoardValues() { PedestrianActionNode::getBlackBoardValues(); }

BT::NodeStatus FollowLaneAction::tick()
{
  getBlackBoardValues();
  if (request != "none" && request != "follow_lane") {
    return BT::NodeStatus::FAILURE;
  }
  if (!entity_status.lanelet_pose_valid) {
    setOutput("updated_status", stopAtEndOfRoad());
    return BT::NodeStatus::RUNNING;
  }
  auto following_lanelets =
    hdmap_utils->getFollowingLanelets(entity_status.lanelet_pose.lanelet_id);
  if (!target_speed) {
    target_speed = hdmap_utils->getSpeedLimit(following_lanelets);
  }
  setOutput("updated_status", calculateEntityStatusUpdated(target_speed.get()));
  return BT::NodeStatus::RUNNING;
}
}  // namespace pedestrian
}  // namespace entity_behavior


text:
// SISTEMAS //
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <math.h>
#include <algorithm>
#include <ctime>
#include <string.h>
#include <sys/stat.h>

#define LOGNAME_FORMAT "%d-%b-%y_%H:%M:%S"
#define LOGNAME_SIZE 20
//////////////

/// ROS //////
#include <ros/ros.h>
#include <ar_track_alvar_msgs/AlvarMarkers.h>
#include <tf/transform_datatypes.h>
#include <geometry_msgs/Quaternion.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/TwistStamped.h>
#include <tf/transform_listener.h>
//////////////

//#include <vaant.h> //ANTES
#include "/home/laic/catkin_ws/src/vant/include/vant/vaant.h"    //RODRIGO ALTEROU ISSO AQUI
//#include <vant/vision.h>  //ANTES
#include "/home/laic/catkin_ws/src/vant/include/vant/vision.h"  //RODRIGO ALTEROU ISSO AQUI

// #### MATRICIAL ### //
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/LU>

// ========================================================================================================================================= //
// Codigo que busca pelo Marcador.
// Passar a altura maxima de busca pelo marcador como parametro
// Melhorar a rota de busca
// ========================================================================================================================================= //

# define BIGGER_MARKER 100 // Definindo o maior ID presente
# define BANNER 1          // Definindo qual banner utilizado

using namespace std;

void timer(int timeTolerance) // Tolerance time to start action. Parameters: Time tolerance in seconds.
{
    ros::Rate tol_rate(1);

    ROS_WARN("Tolerance Timer has been started. Tolerance Time set: %d", timeTolerance+1);

    int t = 1;
    while(ros::ok() && t< timeTolerance+1){
        ros::spinOnce();
        cout << t << "s already passed (total of "<< timeTolerance+1 << ")"<<endl;
        tol_rate.sleep();
        t++;
    }
}

double defineGoToZ(vaant quad,double maxHigh) // maxHigh that the marker is visible
{
    double goToThisZ;
    ros::spinOnce();

    if((quad.poseMavros.pose.position.z >= maxHigh) || (quad.poseMavros.pose.position.z < 0))
         goToThisZ = -(quad.poseMavros.pose.position.z - maxHigh);

    else if(quad.poseMavros.pose.position.z < maxHigh)
         goToThisZ = -(quad.poseMavros.pose.position.z - maxHigh);

    return goToThisZ;
}

// SetAlexandre
//void defineOrientation(tf::Vector3 route[], double a, double z, double dir)
//{
//    Eigen::MatrixXd rot(4,4),
//            p(4,8),
//            pRot(4,8);

//    //cout << "Antes p" << endl;

//    p << 0, a, 0, -2*a,    0, 2*a, 0, -a,
//         0, 0, a,    0, -2*a,   0, a,  0,
//         z, 0, 0,    0,    0,   0, 0,  0,
//         1, 1, 1,    1,    1,   1, 1,  1;

//    //cout << p << endl;

//    rot << cos(dir), -1*sin(dir), 0, 0,  // Conferir se e essa matriz mesmo
//           sin(dir),    cos(dir), 0, 0,
//           0,           0,        1, 0,
//           0,           0,        0, 1;

//    //cout << rot << endl;

//    pRot = rot * p;

//    //cout << endl << pRot << endl;

//    // Defining the route of seeking the marker
//    for (int i = 0; i < 8; i++)
//    {
//        //cout << pRot(0, i) << endl;
//        route[i] = {(double)pRot(0,i), (double)pRot(1,i), (double)pRot(2,i)};
//    }
//    //cout << route << endl;
//}

int main(int argc, char **argv)
{
    system("rosnode kill /lastWaypoint ");

    ros::init(argc, argv, "seekMarker");
    ros::NodeHandle n;
    ros::Rate loop_rate(10);

    tf::TransformListener listener;
    tf::StampedTransform TH_CENTER_I;

    tf::Vector3 squareRoute[8];

    vision v(n, BIGGER_MARKER, MULTI, BANNER);
    vaant quad(n);

    double a = 1, firsta,                // displacement in meters
           tolConvergence = 0.2, // In meters
           goToThisZ = 6,
           maxHigh = 6,
           posOld[2],
           multiplier = 2,
           interaction = 3;

    int aux = 0,counter = 1;

    if(argc == 1){
        cout <<" Default Values. {Maximum High, Tolerance Convergence, Square Displacement, Multiplier, Interaction, Timer of tolerance of not seeing} {6, 0.2, 1, 2x, 4x, 3s}  " << endl;
    }

    else{
        maxHigh = atof(argv[1]);
        tolConvergence = atof(argv[2]);
        a = atof(argv[3]);
        cout <<" \n Maximum High Set "<< maxHigh << endl;
        cout <<" \n Tolerance Convergence "<< tolConvergence << endl;
        cout <<" \n Square Displacement "<< a << endl;
        cout <<" \n Multiplier "<< multiplier << endl;
        cout <<" \n Interactions "<< interaction << endl;
        cout <<" \n Timer of Tolerance "<< interaction << endl;
        sleep(1);
    }

   firsta = a;

   while(ros::ok() && !quad.poseMavros.pose.position.z){ // Melhorar aqui
       ros::spinOnce();
       loop_rate.sleep();
   }

   goToThisZ = defineGoToZ(quad,maxHigh);

   //////////////////////////////////////// Defining the route of seeking the marker ////////////////////////////////////////
   squareRoute[0] = {0,0, goToThisZ};
   squareRoute[1] = {a, 0 ,0};
   squareRoute[2] = {0, a ,0};
   squareRoute[3] = {-2*a, 0 ,0};
   squareRoute[4] = {0, -2*a ,0};
   squareRoute[5] = {2*a, 0 ,0};
   squareRoute[6] = {0, a ,0};
   squareRoute[7] = {-a, 0 ,0};
   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    while(ros::ok()){

        ros::spinOnce();
        loop_rate.sleep();

        if (v.getPoseAlvar().id == 0 ) // There is no visible marker
        {
            timer(2); // Give a second chance after a time

            if (v.getPoseAlvar().id == 0 ){ // There is no visible marker after the time tolerance, so start the seeking.

                //defineOrientation(squareRoute, a, goToThisZ, dir);
                cout << " " << TH_CENTER_I.getOrigin().getX()<<" , " << TH_CENTER_I.getOrigin().getY() <<endl;

                // Rotina de busca de rota quadrada.
                cout <<" Seeking the Marker " << endl;
                cout<<"passou 1"<<endl;
                quad.goToPosition(squareRoute[aux], tolConvergence,v);
                aux ++;
                cout<<"passou 2"<<endl;
                if(aux == 8){

                    if (counter == interaction){
                        quad.changeMode(vaant::MODE::AUTO_RTL);
                    }
                    cout<<"passou 3"<<endl;
                    counter++;
                    aux = 0;

                    goToThisZ = defineGoToZ(quad,maxHigh);

                    a = multiplier*a;

                    //////////////////////////////////////// Defining the route of seeking the marker ////////////////////////////////////////
                    squareRoute[0] = {0,0, goToThisZ};
                    squareRoute[1] = {a, 0 ,0};
                    squareRoute[2] = {0, a ,0};
                    squareRoute[3] = {-2*a, 0 ,0};
                    squareRoute[4] = {0, -2*a ,0};
                    squareRoute[5] = {2*a, 0 ,0};
                    squareRoute[6] = {0, a ,0};
                    squareRoute[7] = {-a, 0 ,0};
                    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            }

        }

        }
        if (v.getPoseAlvar().id == 1 ) // There is visible marker, so let the landing algorithm act.
        {
            aux = 0;
            a = firsta;
            ROS_WARN("There is visible marker. Not seeking");

            // Implementando a rotina de memoria. Coleta a ultima posicao registrada do marcador e coloca como proimo ponto da rota de procura, com a altura desejada.

            try{
                listener.lookupTransform("/vant","/center",ros::Time(0),TH_CENTER_I); //obtem matriz de transf. homogena do centro do marcador em relacao a camera
            }
            catch (tf::TransformException &ex) {
                ROS_ERROR("%s",ex.what());
            }

            goToThisZ = defineGoToZ(quad,maxHigh);
            posOld[0] = squareRoute[0][0];
            posOld[1] = squareRoute[0][1];
            squareRoute[0] = { (double) TH_CENTER_I.getOrigin().getX(), (double) TH_CENTER_I.getOrigin().getY(), goToThisZ};
            cout << "Ultima incidencia o marcador (REF VANT)" << TH_CENTER_I.getOrigin().getX()<<" , " << TH_CENTER_I.getOrigin().getY() <<endl;

            // Fim da rotina de memoria

        }

    }

    system("rosnode kill /logGenerator ");
    return 0;

}