<commit_before>// Copyright 2019 DeepMind Technologies Ltd. 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 "open_spiel/games/pig.h"

#include <sys/types.h>

#include <utility>

#include "open_spiel/game_parameters.h"
#include "open_spiel/spiel.h"

namespace open_spiel {
namespace pig {

namespace {
// Moves.
enum ActionType { kRoll = 0, kStop = 1 };

// Bin size for the information state vectors: how many score values to put
// into one bin. Higher means more coarser.
constexpr int kBinSize = 1;

// Default parameters.
constexpr int kDefaultDiceOutcomes = 6;
constexpr int kDefaultHorizon = 1000;
constexpr int kDefaultPlayers = 2;
constexpr int kDefaultWinScore = 100;

// Facts about the game
const GameType kGameType{
    /*short_name=*/"pig",
    /*long_name=*/"Pig",
    GameType::Dynamics::kSequential,
    GameType::ChanceMode::kExplicitStochastic,
    GameType::Information::kPerfectInformation,
    GameType::Utility::kZeroSum,
    GameType::RewardModel::kTerminal,
    /*max_num_players=*/10,
    /*min_num_players=*/2,
    /*provides_information_state_string=*/false,
    /*provides_information_state_tensor=*/false,
    /*provides_observation_string=*/true,
    /*provides_observation_tensor=*/true,
    /*parameter_specification=*/
    {
        {"players", GameParameter(kDefaultPlayers)},
        {"horizon", GameParameter(kDefaultHorizon)},
        {"winscore", GameParameter(kDefaultWinScore)},
        {"diceoutcomes", GameParameter(kDefaultDiceOutcomes)},
    }};

static std::shared_ptr<const Game> Factory(const GameParameters& params) {
  return std::shared_ptr<const Game>(new PigGame(params));
}

REGISTER_SPIEL_GAME(kGameType, Factory);
}  // namespace

std::string PigState::ActionToString(Player player, Action move_id) const {
  if (player == kChancePlayerId) {
    return absl::StrCat("chance outcome ", move_id);
  } else if (move_id == kRoll) {
    return "roll";
  } else {
    return "stop";
  }
}

bool PigState::IsTerminal() const {
  if (total_moves_ >= horizon_) {
    return true;
  }

  for (auto p = Player{0}; p < num_players_; p++) {
    if (scores_[p] >= win_score_) {
      return true;
    }
  }
  return false;
}

std::vector<double> PigState::Returns() const {
  if (!IsTerminal()) {
    return std::vector<double>(num_players_, 0.0);
  }

  // For (n>2)-player games, must keep it zero-sum.
  std::vector<double> returns(num_players_, -1.0 / (num_players_ - 1));

  for (auto player = Player{0}; player < num_players_; ++player) {
    if (scores_[player] >= win_score_) {
      returns[player] = 1.0;
      return returns;
    }
  }

  // Nobody has won? (e.g. over horizon length.) Then everyone gets 0.
  return std::vector<double>(num_players_, 0.0);
}

std::string PigState::ObservationString(Player player) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);
  return ToString();
}

std::vector<int> PigGame::ObservationTensorShape() const {
  int num_bins = (win_score_ / kBinSize) + 1;
  return {1 + num_players_, num_bins};
}

void PigState::ObservationTensor(Player player,
                                 std::vector<double>* values) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);

  // One extra bin for when value is >= max.
  // So for win_score_ 100, binSize = 1 -> 0, 1, ..., 99, >= 100.
  int num_bins = (win_score_ / kBinSize) + 1;

  // One-hot encoding: turn total (#bin) followed by p1, p2, ...
  values->resize(num_bins + num_players_ * num_bins);
  std::fill(values->begin(), values->end(), 0.);
  int pos = 0;

  // One-hot encoding:
  //  - turn total (#bins)
  //  - player 0 (#bins)
  //  - player 1 (#bins)
  //      .
  //      .
  //      .

  int bin = turn_total_ / kBinSize;
  if (bin >= num_bins) {
    // When the value is too large, use last bin.
    (*values)[pos + (num_bins - 1)] = 1;
  } else {
    (*values)[pos + bin] = 1;
  }

  pos += num_bins;

  // Find the right bin for each player.
  for (auto p = Player{0}; p < num_players_; p++) {
    bin = scores_[p] / kBinSize;
    if (bin >= num_bins) {
      // When the value is too large, use last bin.
      (*values)[pos + (num_bins - 1)] = 1;
    } else {
      (*values)[pos + bin] = 1;
    }

    pos += num_bins;
  }
}

PigState::PigState(std::shared_ptr<const Game> game, int dice_outcomes,
                   int horizon, int win_score)
    : State(game),
      dice_outcomes_(dice_outcomes),
      horizon_(horizon),
      win_score_(win_score) {
  total_moves_ = 0;
  cur_player_ = 0;
  turn_player_ = 0;
  scores_.resize(game->NumPlayers(), 0);
  turn_total_ = 0;
}

int PigState::CurrentPlayer() const {
  return IsTerminal() ? kTerminalPlayerId : cur_player_;
}

void PigState::DoApplyAction(Action move) {
  // For decision node: 0 means roll, 1 means stop.
  // For chance node: outcome of the dice (1-x).
  if (cur_player_ >= 0 && move == kRoll) {
    // Player roll -> chance node.
    cur_player_ = kChancePlayerId;
    total_moves_++;
  } else if (cur_player_ >= 0 && move == kStop) {
    // Player stops. Take turn total and pass to next player.
    scores_[turn_player_] += turn_total_;
    turn_total_ = 0;
    turn_player_ = NextPlayerRoundRobin(turn_player_, num_players_);
    cur_player_ = turn_player_;
    total_moves_++;
  } else if (IsChanceNode()) {
    // Resolve chance node outcome. If 1, reset turn total and change players;
    // else, add to total and keep going.
    if (move == 1) {
      // Reset turn total and loses turn!
      turn_total_ = 0;
      turn_player_ = NextPlayerRoundRobin(turn_player_, num_players_);
      cur_player_ = turn_player_;
    } else {
      // Add to the turn total.
      turn_total_ += (move + 1);
      cur_player_ = turn_player_;
    }
  } else {
    SpielFatalError(absl::StrCat("Move ", move, " is invalid."));
  }
}

std::vector<Action> PigState::LegalActions() const {
  if (IsChanceNode()) {
    return LegalChanceOutcomes();
  } else if (IsTerminal()) {
    return {};
  } else {
    if (scores_[cur_player_] + turn_total_ >= win_score_) {
      return {kStop};
    } else {
      return {kRoll, kStop};
    }
  }
}

std::vector<std::pair<Action, double>> PigState::ChanceOutcomes() const {
  SPIEL_CHECK_TRUE(IsChanceNode());
  std::vector<std::pair<Action, double>> outcomes;

  // All the chance outcomes come after roll and stop.
  outcomes.reserve(dice_outcomes_);
  for (int i = 0; i < dice_outcomes_; i++) {
    outcomes.push_back(std::make_pair(i + 1, 1.0 / dice_outcomes_));
  }

  return outcomes;
}

std::string PigState::ToString() const {
  return absl::StrCat("Scores: ", absl::StrJoin(scores_, " "),
                      ", Turn total: ", turn_total_,
                      "\nCurrent player: ", turn_player_,
                      (cur_player_ == kChancePlayerId ? " (rolling)\n" : "\n"));
}

std::unique_ptr<State> PigState::Clone() const {
  return std::unique_ptr<State>(new PigState(*this));
}

PigGame::PigGame(const GameParameters& params)
    : Game(kGameType, params),
      dice_outcomes_(ParameterValue<int>("diceoutcomes")),
      horizon_(ParameterValue<int>("horizon")),
      num_players_(ParameterValue<int>("players")),
      win_score_(ParameterValue<int>("winscore")) {}

}  // namespace pig
}  // namespace open_spiel
<commit_msg>fix bug in game pig, keep chance action starts from 0<commit_after>// Copyright 2019 DeepMind Technologies Ltd. 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 "open_spiel/games/pig.h"

#include <sys/types.h>

#include <utility>

#include "open_spiel/game_parameters.h"
#include "open_spiel/spiel.h"

namespace open_spiel {
namespace pig {

namespace {
// Moves.
enum ActionType { kRoll = 0, kStop = 1 };

// Bin size for the information state vectors: how many score values to put
// into one bin. Higher means more coarser.
constexpr int kBinSize = 1;

// Default parameters.
constexpr int kDefaultDiceOutcomes = 6;
constexpr int kDefaultHorizon = 1000;
constexpr int kDefaultPlayers = 2;
constexpr int kDefaultWinScore = 100;

// Facts about the game
const GameType kGameType{
    /*short_name=*/"pig",
    /*long_name=*/"Pig",
    GameType::Dynamics::kSequential,
    GameType::ChanceMode::kExplicitStochastic,
    GameType::Information::kPerfectInformation,
    GameType::Utility::kZeroSum,
    GameType::RewardModel::kTerminal,
    /*max_num_players=*/10,
    /*min_num_players=*/2,
    /*provides_information_state_string=*/false,
    /*provides_information_state_tensor=*/false,
    /*provides_observation_string=*/true,
    /*provides_observation_tensor=*/true,
    /*parameter_specification=*/
    {
        {"players", GameParameter(kDefaultPlayers)},
        {"horizon", GameParameter(kDefaultHorizon)},
        {"winscore", GameParameter(kDefaultWinScore)},
        {"diceoutcomes", GameParameter(kDefaultDiceOutcomes)},
    }};

static std::shared_ptr<const Game> Factory(const GameParameters& params) {
  return std::shared_ptr<const Game>(new PigGame(params));
}

REGISTER_SPIEL_GAME(kGameType, Factory);
}  // namespace

std::string PigState::ActionToString(Player player, Action move_id) const {
  if (player == kChancePlayerId) {
    return absl::StrCat("chance outcome ", move_id);
  } else if (move_id == kRoll) {
    return "roll";
  } else {
    return "stop";
  }
}

bool PigState::IsTerminal() const {
  if (total_moves_ >= horizon_) {
    return true;
  }

  for (auto p = Player{0}; p < num_players_; p++) {
    if (scores_[p] >= win_score_) {
      return true;
    }
  }
  return false;
}

std::vector<double> PigState::Returns() const {
  if (!IsTerminal()) {
    return std::vector<double>(num_players_, 0.0);
  }

  // For (n>2)-player games, must keep it zero-sum.
  std::vector<double> returns(num_players_, -1.0 / (num_players_ - 1));

  for (auto player = Player{0}; player < num_players_; ++player) {
    if (scores_[player] >= win_score_) {
      returns[player] = 1.0;
      return returns;
    }
  }

  // Nobody has won? (e.g. over horizon length.) Then everyone gets 0.
  return std::vector<double>(num_players_, 0.0);
}

std::string PigState::ObservationString(Player player) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);
  return ToString();
}

std::vector<int> PigGame::ObservationTensorShape() const {
  int num_bins = (win_score_ / kBinSize) + 1;
  return {1 + num_players_, num_bins};
}

void PigState::ObservationTensor(Player player,
                                 std::vector<double>* values) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);

  // One extra bin for when value is >= max.
  // So for win_score_ 100, binSize = 1 -> 0, 1, ..., 99, >= 100.
  int num_bins = (win_score_ / kBinSize) + 1;

  // One-hot encoding: turn total (#bin) followed by p1, p2, ...
  values->resize(num_bins + num_players_ * num_bins);
  std::fill(values->begin(), values->end(), 0.);
  int pos = 0;

  // One-hot encoding:
  //  - turn total (#bins)
  //  - player 0 (#bins)
  //  - player 1 (#bins)
  //      .
  //      .
  //      .

  int bin = turn_total_ / kBinSize;
  if (bin >= num_bins) {
    // When the value is too large, use last bin.
    (*values)[pos + (num_bins - 1)] = 1;
  } else {
    (*values)[pos + bin] = 1;
  }

  pos += num_bins;

  // Find the right bin for each player.
  for (auto p = Player{0}; p < num_players_; p++) {
    bin = scores_[p] / kBinSize;
    if (bin >= num_bins) {
      // When the value is too large, use last bin.
      (*values)[pos + (num_bins - 1)] = 1;
    } else {
      (*values)[pos + bin] = 1;
    }

    pos += num_bins;
  }
}

PigState::PigState(std::shared_ptr<const Game> game, int dice_outcomes,
                   int horizon, int win_score)
    : State(game),
      dice_outcomes_(dice_outcomes),
      horizon_(horizon),
      win_score_(win_score) {
  total_moves_ = 0;
  cur_player_ = 0;
  turn_player_ = 0;
  scores_.resize(game->NumPlayers(), 0);
  turn_total_ = 0;
}

int PigState::CurrentPlayer() const {
  return IsTerminal() ? kTerminalPlayerId : cur_player_;
}

void PigState::DoApplyAction(Action move) {
  // For decision node: 0 means roll, 1 means stop.
  // For chance node: outcome of the dice (1-x).
  if (cur_player_ >= 0 && move == kRoll) {
    // Player roll -> chance node.
    cur_player_ = kChancePlayerId;
    total_moves_++;
  } else if (cur_player_ >= 0 && move == kStop) {
    // Player stops. Take turn total and pass to next player.
    scores_[turn_player_] += turn_total_;
    turn_total_ = 0;
    turn_player_ = NextPlayerRoundRobin(turn_player_, num_players_);
    cur_player_ = turn_player_;
    total_moves_++;
  } else if (IsChanceNode()) {
    // Resolve chance node outcome. If 1, reset turn total and change players;
    // else, add to total and keep going.
    if (move == 0) {
      // Reset turn total and loses turn!
      turn_total_ = 0;
      turn_player_ = NextPlayerRoundRobin(turn_player_, num_players_);
      cur_player_ = turn_player_;
    } else {
      // Add to the turn total.
      turn_total_ += (move + 1);
      cur_player_ = turn_player_;
    }
  } else {
    SpielFatalError(absl::StrCat("Move ", move, " is invalid."));
  }
}

std::vector<Action> PigState::LegalActions() const {
  if (IsChanceNode()) {
    return LegalChanceOutcomes();
  } else if (IsTerminal()) {
    return {};
  } else {
    if (scores_[cur_player_] + turn_total_ >= win_score_) {
      return {kStop};
    } else {
      return {kRoll, kStop};
    }
  }
}

std::vector<std::pair<Action, double>> PigState::ChanceOutcomes() const {
  SPIEL_CHECK_TRUE(IsChanceNode());
  std::vector<std::pair<Action, double>> outcomes;

  // All the chance outcomes come after roll and stop.
  outcomes.reserve(dice_outcomes_);
  for (int i = 0; i < dice_outcomes_; i++) {
    outcomes.push_back(std::make_pair(i, 1.0 / dice_outcomes_));
  }

  return outcomes;
}

std::string PigState::ToString() const {
  return absl::StrCat("Scores: ", absl::StrJoin(scores_, " "),
                      ", Turn total: ", turn_total_,
                      "\nCurrent player: ", turn_player_,
                      (cur_player_ == kChancePlayerId ? " (rolling)\n" : "\n"));
}

std::unique_ptr<State> PigState::Clone() const {
  return std::unique_ptr<State>(new PigState(*this));
}

PigGame::PigGame(const GameParameters& params)
    : Game(kGameType, params),
      dice_outcomes_(ParameterValue<int>("diceoutcomes")),
      horizon_(ParameterValue<int>("horizon")),
      num_players_(ParameterValue<int>("players")),
      win_score_(ParameterValue<int>("winscore")) {}

}  // namespace pig
}  // namespace open_spiel
<|endoftext|>


<commit_before>#pragma once

#include <memory>
#include <mutex>
#include <tuple>

#include "balancer.hpp"
#include "connection.hpp"
#include "log.hpp"
#include "pool.hpp"
#include "result.hpp"
#include "request/info.hpp"
#include "settings.hpp"

namespace elasticsearch {

class http_transport_t {
public:
    typedef blackhole::synchronized<blackhole::logger_base_t> logger_type;

    typedef boost::asio::io_service loop_type;
    typedef http_connection_t connection_type;
    typedef pool_t<connection_type> pool_type;
    typedef pool_type::endpoint_type endpoint_type;

private:
    settings_t settings;

    loop_type& loop;
    logger_type& log;

    pool_type pool;
    std::unique_ptr<balancing::strategy<pool_type>> balancer;
public:
    http_transport_t(settings_t settings, loop_type& loop, logger_type& log) :
        settings(settings),
        loop(loop),
        log(log),
        balancer(new balancing::round_robin<pool_type>())
    {}

    void add_nodes(const std::vector<endpoint_type>& endpoints) {
        for (auto it = endpoints.begin(); it != endpoints.end(); ++it) {
            add_node(*it);
        }
    }

    void add_node(const endpoint_type& endpoint) {
        boost::system::error_code ec;
        auto address = endpoint.address().to_string(ec);
        if (ec) {
            LOG(log, "can't add node endpoint '%s': %s", endpoint, ec.message());
            return;
        }

        LOG(log, "adding '%s:%d' to the pool ...", address, endpoint.port());
        bool inserted;
        std::tie(std::ignore, inserted) = pool.insert(
            endpoint, std::make_shared<connection_type>(endpoint, loop, log)
        );

        if (!inserted) {
            LOG(log, "adding endpoint to the pool is rejected - already exists");
            return;
        }
    }

    void sniff() {
        LOG(log, "sniffing nodes info ...");
        perform(actions::nodes_info_t(),
                std::bind(&http_transport_t::on_sniff, this, std::placeholders::_1));
    }

    template<class Action>
    void perform(Action&& action, typename callback<Action>::type callback) {
        BOOST_ASSERT(balancer);

        LOG(log, "performing request ...");
        auto connection = balancer->next(pool);
        if (!connection) {
            //!@todo: Write error to the callback via post.
            return;
        }

        LOG(log, "balancing at %s", connection->endpoint());
        request_watcher_t<Action> watcher(*this, callback);
        connection->perform(std::move(action), watcher);
    }

private:
    void on_sniff(result_t<response::nodes_info_t>&&) {}

    template<class Action>
    struct request_watcher_t {
        typedef Action action_type;
        typedef typename Action::result_type result_type;
        typedef typename callback<action_type>::type callback_type;

        const http_transport_t& transport;
        callback_type callback;

        request_watcher_t(const http_transport_t& transport,
                          callback_type callback) :
            transport(transport),
            callback(callback)
        {}

        void operator()(result_type&& result) {
            if (transport.settings.sniffer.when.error) {
                //!@todo: Resniff if failed.
            }

            callback(std::move(result));
        }
    };
};

} // namespace elasticsearch

<commit_msg>[Elasticsearch] Properly handling sniff response.<commit_after>#pragma once

#include <memory>
#include <mutex>
#include <tuple>

#include "balancer.hpp"
#include "connection.hpp"
#include "log.hpp"
#include "pool.hpp"
#include "result.hpp"
#include "request/info.hpp"
#include "settings.hpp"

namespace elasticsearch {

class http_transport_t {
public:
    typedef blackhole::synchronized<blackhole::logger_base_t> logger_type;

    typedef boost::asio::io_service loop_type;
    typedef http_connection_t connection_type;
    typedef pool_t<connection_type> pool_type;
    typedef pool_type::endpoint_type endpoint_type;

private:
    settings_t settings;

    loop_type& loop;
    logger_type& log;

    pool_type pool;
    std::unique_ptr<balancing::strategy<pool_type>> balancer;
public:
    http_transport_t(settings_t settings, loop_type& loop, logger_type& log) :
        settings(settings),
        loop(loop),
        log(log),
        balancer(new balancing::round_robin<pool_type>())
    {}

    void add_nodes(const std::vector<endpoint_type>& endpoints) {
        for (auto it = endpoints.begin(); it != endpoints.end(); ++it) {
            add_node(*it);
        }
    }

    void add_node(const endpoint_type& endpoint) {
        boost::system::error_code ec;
        auto address = endpoint.address().to_string(ec);
        if (ec) {
            LOG(log, "can't add node endpoint '%s': %s", endpoint, ec.message());
            return;
        }

        LOG(log, "adding '%s:%d' to the pool ...", address, endpoint.port());
        bool inserted;
        std::tie(std::ignore, inserted) = pool.insert(
            endpoint, std::make_shared<connection_type>(endpoint, loop, log)
        );

        if (!inserted) {
            LOG(log, "adding endpoint to the pool is rejected - already exists");
            return;
        }
    }

    void sniff() {
        LOG(log, "sniffing nodes info ...");
        perform(actions::nodes_info_t(),
                std::bind(&http_transport_t::on_sniff, this, std::placeholders::_1));
    }

    template<class Action>
    void perform(Action&& action, typename callback<Action>::type callback) {
        BOOST_ASSERT(balancer);

        LOG(log, "performing request ...");
        auto connection = balancer->next(pool);
        if (!connection) {
            //!@todo: Write error to the callback via post.
            return;
        }

        LOG(log, "balancing at %s", connection->endpoint());
        request_watcher_t<Action> watcher(*this, callback);
        connection->perform(std::move(action), watcher);
    }

private:
    void on_sniff(result_t<response::nodes_info_t>&& result) {
        if (auto* nodes_info = boost::get<response::nodes_info_t>(&result)) {
            LOG(log, "successfully sniffed %d nodes", nodes_info->nodes.size());

            const auto& nodes = nodes_info->nodes;
            for (auto it = nodes.begin(); it != nodes.end(); ++it) {
            }
        }
    }

    template<class Action>
    struct request_watcher_t {
        typedef Action action_type;
        typedef typename Action::result_type result_type;
        typedef typename callback<action_type>::type callback_type;

        http_transport_t& transport;
        callback_type callback;

        request_watcher_t(http_transport_t& transport, callback_type callback) :
            transport(transport),
            callback(callback)
        {}

        void operator()(result_type&& result) const {
            if (boost::get<boost::system::error_code>(&result)) {
                if (transport.settings.sniffer.when.error) {
                    //!@todo: Need to transfer sniff try number with error.
                    //!       If if > max -> stop sniffing.
                    LOG(transport.log, "sniff.when.error is true - resniffing ...");
                    transport.sniff();
                }
            }

            callback(std::move(result));
        }
    };
};

} // namespace elasticsearch

<|endoftext|>


<commit_before>/**
 * @file ct2ctml.cpp
 * Driver for the system call to the python executable that converts
 * cti files to ctml files (see \ref inputfiles).
 */

/*
 * $Revision$
 * $Date$
 */

// Copyright 2001-2005  California Institute of Technology

// turn off warnings under Windows
#ifdef WIN32
#pragma warning(disable:4786)
#pragma warning(disable:4503)
#pragma warning(disable:4996)
#endif

#include "ct_defs.h"
#include "ctexceptions.h"
#include "ctml.h"
#include "global.h"
#include "stringUtils.h"

#include <fstream>
#include <string>
#include <cstdlib>
#include <ctime>

#ifdef DEBUG_PATHS
#undef DEBUG_PATHS
#endif

using namespace Cantera;
using namespace std;

namespace ctml {

  //! return the full path to the Python interpreter.
  /*!
   * Use the environment variable PYTHON_CMD if it is set. If not, return
   * the string 'python'.
   *
   * Note, there are hidden problems here that really direct us to use
   * a full pathname for the location of python. Basically the system
   * call will use the shell /bin/sh, in order to launch python.
   * This default shell may not be the shell that the user is employing.
   * Therefore, the default path to python may be different during
   * a system call than during the default user shell environment.
   * This is quite a headache. The answer is to always set the
   * PYTHON_CMD environmental variable in the user environment to
   * an absolute path to locate the python executable. Then this
   * issue goes away.
   */
  static string pypath() {
    string s = "python";
    const char* py = getenv("PYTHON_CMD");
    if (!py) {
      const char* hm = getenv("HOME");
      string home = stripws(string(hm));
      string cmd = string("source ")+home
	+string("/setup_cantera &> /dev/null");
      system(cmd.c_str());
      py = getenv("PYTHON_CMD");
    }
    if (py) {
      string sp = stripws(string(py));
      if (sp.size() > 0) {
	s = sp;
      }
    }
    //else {
    //    throw CanteraError("ct2ctml",
    //        "set environment variable PYTHON_CMD");
    //}
    return s;
  }

  // Convert a cti file into a ctml file
  /*
   *
   *  @param   file    Pointer to the file
   *  @param   debug   Turn on debug printing
   *
   *  @ingroup inputfiles
   */
  void ct2ctml(const char* const file, const int debug) {

#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = file;
    throw CanteraError("ct2ctml",
		       "python cti to ctml conversion requested for file, " + ppath +
		       ", but not available in this computational environment");
#endif

    time_t aclock;
    time( &aclock );
    int ia = static_cast<int>(aclock);
    string path =  tmpDir()+"/.cttmp"+int2str(ia)+".pyw";
    ofstream f(path.c_str());
    if (!f) {
      throw CanteraError("ct2ctml","cannot open "+path+" for writing.");
    }

    f << "from ctml_writer import *\n"
      << "import sys, os, os.path\n"
      << "file = \"" << file << "\"\n"
      << "base = os.path.basename(file)\n"
      << "root, ext = os.path.splitext(base)\n"
      << "dataset(root)\n"
      << "execfile(file)\n"
      << "write()\n";
    f.close();
    string logfile = tmpDir()+"/ct2ctml.log";
#ifdef WIN32
    string cmd = pypath() + " " + "\"" + path + "\"" + "> " + logfile + " 2>&1";
#else
    string cmd = "sleep " + sleep() + "; " + "\"" + pypath() + "\"" +
      " " + "\"" + path + "\"" + " &> " + logfile;
#endif
#ifdef DEBUG_PATHS
    writelog("ct2ctml: executing the command " + cmd + "\n");
#endif
    if (debug > 0) {
      writelog("ct2ctml: executing the command " + cmd + "\n");
      writelog("ct2ctml: the Python command is: " + pypath() + "\n");
    }

    int ierr = 0;
    try {
      ierr = system(cmd.c_str());
    }
    catch (...) {
      ierr = -10;
      if (debug > 0)
	writelog("ct2ctml: command execution failed.\n");
    }

    /*
     * This next section may seem a bit weird. However, it is in
     * response to an issue that arises when running cantera with
     * cygwin, using cygwin's python intepreter. Basically, the
     * xml file is written to the local directory by the last
     * system command. Then, the xml file is read immediately
     * after by an ifstream() c++ command. Unfortunately, it seems
     * that the directory info is not being synched fast enough so
     * that the ifstream() read fails, even though the file is
     * actually there. Putting in a sleep system call here fixes
     * this problem. Also, having the xml file pre-existing fixes
     * the problem as well. There may be more direct ways to fix
     * this bug; however, I am not aware of them.
     * HKM -> During the solaris port, I found the same thing.
     *        It probably has to do with NFS syncing problems.
     *        3/3/06
     */
#ifndef WIN32
    string sss = sleep();
#ifdef DEBUG_PATHS
    writelog("sleeping for " + sss + " secs+\n");
#endif
    if (debug > 0)
      writelog("sleeping for " + sss + " secs+\n");
    cmd = "sleep " + sss;
    try {
      ierr = system(cmd.c_str());
    }
    catch (...) {
      ierr = -10;
      writelog("ct2ctml: command execution failed.\n");
    }
#endif
    // show the contents of the log file on the screen
    try {
      char ch=0;
      string s = "";
      ifstream ferr("ct2ctml.log");
      if (ferr) {
	while (!ferr.eof()) {
	  ferr.get(ch);
	  s += ch;
	  if (ch == '\n') {
	    writelog(s);
	    s = "";
	  }
	}
	ferr.close();
      }
      else {
	if (debug > 0)
	  writelog("cannot open ct2ctml.log for reading.\n");
      }
    }
    catch (...) {
      ;
    }
    if (ierr != 0) {
      string msg = cmd;
      throw CanteraError("ct2ctml",
			 "could not convert input file to CTML.\n "
			 "Command line was: \n" + msg);
    }

    // if the conversion succeeded and DEBUG_PATHS is not defined,
    // then clean up by deleting the temporary Python file.
#ifndef DEBUG_PATHS
    //#ifdef WIN32
    //cmd = "cmd /C rm " + path;
    if (debug == 0)
      remove(path.c_str());
    else {
      writelog("ct2ctml: retaining temporary file "+path+"\n");
    }
#else
    writelog("ct2ctml: retaining temporary file "+path+"\n");
    //#else
    //cmd = "rm -f \"" + path + "\"";
    //try {
    //    if (ierr == 0)
    //        system(cmd.c_str());
    //}
    //catch (...) { ; }
    //#endif
#endif
  }

  // Read an ctml file from a file and fill up an XML tree
  /*
   *  This is the main routine that reads a ctml file and puts it into
   *  an XML_Node tree
   *
   *  @param node    Root of the tree
   *  @param file    Name of the file
   *  @param debug   Turn on debugging printing
   */
  void get_CTML_Tree(XML_Node* rootPtr, const std::string file, const int debug) {
    string ff, ext = "";

    // find the input file on the Cantera search path
    string inname = findInputFile(file);
#ifdef DEBUG_PATHS
    writelog("Found file: "+inname+"\n");
#endif
    if (debug > 0)
      writelog("Found file: "+inname+"\n");

    if (inname == "") {
      throw CanteraError("get_CTML_Tree", "file "+file+" not found");
    }

    /*
     * Check whether or not the file is XML. If not, it will be first
     * processed with the preprocessor.
     */
    string::size_type idot = inname.rfind('.');
    if (idot != string::npos) {
      ext = inname.substr(idot, inname.size());
    }
    if (ext != ".xml" && ext != ".ctml") {
      ct2ctml(inname.c_str(), debug);
      ff = inname.substr(0,idot) + ".xml";
    }
    else {
      ff = inname;
    }
#ifdef DEBUG_PATHS
    writelog("Attempting to parse xml file " + ff + "\n");
#endif
    if (debug > 0)
      writelog("Attempting to parse xml file " + ff + "\n");
    ifstream fin(ff.c_str());
    if (!fin) {
      throw
	CanteraError("get_CTML_Tree",
		     "XML file " + ff + " not found");
    }
    rootPtr->build(fin);
    fin.close();
  }
}
<commit_msg>Fixed a glaring error concerning the situation where the cti file is not in the current directory. THe xml file is always written to the current directory. And the subsequent reading of the xml file must reflect that. The xml file was trying to be read from the directory where the cti file was located. Now, the xml file will be read from the current directory.<commit_after>/**
 * @file ct2ctml.cpp
 * Driver for the system call to the python executable that converts
 * cti files to ctml files (see \ref inputfiles).
 */

/*
 * $Revision$
 * $Date$
 */

// Copyright 2001-2005  California Institute of Technology

// turn off warnings under Windows
#ifdef WIN32
#pragma warning(disable:4786)
#pragma warning(disable:4503)
#pragma warning(disable:4996)
#endif

#include "ct_defs.h"
#include "ctexceptions.h"
#include "ctml.h"
#include "global.h"
#include "stringUtils.h"

#include <fstream>
#include <string>
#include <cstdlib>
#include <ctime>

#ifdef DEBUG_PATHS
#undef DEBUG_PATHS
#endif

using namespace Cantera;
using namespace std;

namespace ctml {

  //! return the full path to the Python interpreter.
  /*!
   * Use the environment variable PYTHON_CMD if it is set. If not, return
   * the string 'python'.
   *
   * Note, there are hidden problems here that really direct us to use
   * a full pathname for the location of python. Basically the system
   * call will use the shell /bin/sh, in order to launch python.
   * This default shell may not be the shell that the user is employing.
   * Therefore, the default path to python may be different during
   * a system call than during the default user shell environment.
   * This is quite a headache. The answer is to always set the
   * PYTHON_CMD environmental variable in the user environment to
   * an absolute path to locate the python executable. Then this
   * issue goes away.
   */
  static string pypath() {
    string s = "python";
    const char* py = getenv("PYTHON_CMD");
    if (!py) {
      const char* hm = getenv("HOME");
      string home = stripws(string(hm));
      string cmd = string("source ")+home
	+string("/setup_cantera &> /dev/null");
      system(cmd.c_str());
      py = getenv("PYTHON_CMD");
    }
    if (py) {
      string sp = stripws(string(py));
      if (sp.size() > 0) {
	s = sp;
      }
    }
    //else {
    //    throw CanteraError("ct2ctml",
    //        "set environment variable PYTHON_CMD");
    //}
    return s;
  }

  // Convert a cti file into a ctml file
  /*
   *
   *  @param   file    Pointer to the file
   *  @param   debug   Turn on debug printing
   *
   *  @ingroup inputfiles
   */
  void ct2ctml(const char* const file, const int debug) {

#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = file;
    throw CanteraError("ct2ctml",
		       "python cti to ctml conversion requested for file, " + ppath +
		       ", but not available in this computational environment");
#endif

    time_t aclock;
    time( &aclock );
    int ia = static_cast<int>(aclock);
    string path =  tmpDir()+"/.cttmp"+int2str(ia)+".pyw";
    ofstream f(path.c_str());
    if (!f) {
      throw CanteraError("ct2ctml","cannot open "+path+" for writing.");
    }

    f << "from ctml_writer import *\n"
      << "import sys, os, os.path\n"
      << "file = \"" << file << "\"\n"
      << "base = os.path.basename(file)\n"
      << "root, ext = os.path.splitext(base)\n"
      << "dataset(root)\n"
      << "execfile(file)\n"
      << "write()\n";
    f.close();
    string logfile = tmpDir()+"/ct2ctml.log";
#ifdef WIN32
    string cmd = pypath() + " " + "\"" + path + "\"" + "> " + logfile + " 2>&1";
#else
    string cmd = "sleep " + sleep() + "; " + "\"" + pypath() + "\"" +
      " " + "\"" + path + "\"" + " &> " + logfile;
#endif
#ifdef DEBUG_PATHS
    writelog("ct2ctml: executing the command " + cmd + "\n");
#endif
    if (debug > 0) {
      writelog("ct2ctml: executing the command " + cmd + "\n");
      writelog("ct2ctml: the Python command is: " + pypath() + "\n");
    }

    int ierr = 0;
    try {
      ierr = system(cmd.c_str());
    }
    catch (...) {
      ierr = -10;
      if (debug > 0)
	writelog("ct2ctml: command execution failed.\n");
    }

    /*
     * This next section may seem a bit weird. However, it is in
     * response to an issue that arises when running cantera with
     * cygwin, using cygwin's python intepreter. Basically, the
     * xml file is written to the local directory by the last
     * system command. Then, the xml file is read immediately
     * after by an ifstream() c++ command. Unfortunately, it seems
     * that the directory info is not being synched fast enough so
     * that the ifstream() read fails, even though the file is
     * actually there. Putting in a sleep system call here fixes
     * this problem. Also, having the xml file pre-existing fixes
     * the problem as well. There may be more direct ways to fix
     * this bug; however, I am not aware of them.
     * HKM -> During the solaris port, I found the same thing.
     *        It probably has to do with NFS syncing problems.
     *        3/3/06
     */
#ifndef WIN32
    string sss = sleep();
#ifdef DEBUG_PATHS
    writelog("sleeping for " + sss + " secs+\n");
#endif
    if (debug > 0)
      writelog("sleeping for " + sss + " secs+\n");
    cmd = "sleep " + sss;
    try {
      ierr = system(cmd.c_str());
    }
    catch (...) {
      ierr = -10;
      writelog("ct2ctml: command execution failed.\n");
    }
#endif
    // show the contents of the log file on the screen
    try {
      char ch=0;
      string s = "";
      ifstream ferr("ct2ctml.log");
      if (ferr) {
	while (!ferr.eof()) {
	  ferr.get(ch);
	  s += ch;
	  if (ch == '\n') {
	    writelog(s);
	    s = "";
	  }
	}
	ferr.close();
      }
      else {
	if (debug > 0)
	  writelog("cannot open ct2ctml.log for reading.\n");
      }
    }
    catch (...) {
      ;
    }
    if (ierr != 0) {
      string msg = cmd;
      throw CanteraError("ct2ctml",
			 "could not convert input file to CTML.\n "
			 "Command line was: \n" + msg);
    }

    // if the conversion succeeded and DEBUG_PATHS is not defined,
    // then clean up by deleting the temporary Python file.
#ifndef DEBUG_PATHS
    //#ifdef WIN32
    //cmd = "cmd /C rm " + path;
    if (debug == 0)
      remove(path.c_str());
    else {
      writelog("ct2ctml: retaining temporary file "+path+"\n");
    }
#else
    writelog("ct2ctml: retaining temporary file "+path+"\n");
    //#else
    //cmd = "rm -f \"" + path + "\"";
    //try {
    //    if (ierr == 0)
    //        system(cmd.c_str());
    //}
    //catch (...) { ; }
    //#endif
#endif
  }

  // Read an ctml file from a file and fill up an XML tree
  /*
   *  This is the main routine that reads a ctml file and puts it into
   *  an XML_Node tree
   *
   *  @param node    Root of the tree
   *  @param file    Name of the file
   *  @param debug   Turn on debugging printing
   */
  void get_CTML_Tree(XML_Node* rootPtr, const std::string file, const int debug) {
    string ff, ext = "";

    // find the input file on the Cantera search path
    string inname = findInputFile(file);
#ifdef DEBUG_PATHS
    writelog("Found file: "+inname+"\n");
#endif
    if (debug > 0)
      writelog("Found file: "+inname+"\n");

    if (inname == "") {
      throw CanteraError("get_CTML_Tree", "file "+file+" not found");
    }

    /*
     * Check whether or not the file is XML. If not, it will be first
     * processed with the preprocessor.
     */
    string::size_type idot = inname.rfind('.');
    if (idot != string::npos) {
      ext = inname.substr(idot, inname.size());
    }
    if (ext != ".xml" && ext != ".ctml") {
      ct2ctml(inname.c_str(), debug);
      string ffull = inname.substr(0,idot) + ".xml";
      ff = "./" + getBaseName(ffull) + ".xml";
#ifdef DEBUG_PATHS
      writelogf("ffull name = %s\n", ffull.c_str());
      writelogf("ff name = %s\n", ff.c_str());
#endif
    }
    else {
      ff = inname;
    }
#ifdef DEBUG_PATHS
    writelog("Attempting to parse xml file " + ff + "\n");
#else
    if (debug > 0)
      writelog("Attempting to parse xml file " + ff + "\n");
#endif
    ifstream fin(ff.c_str());
    if (!fin) {
      throw
	CanteraError("get_CTML_Tree",
		     "XML file " + ff + " not found");
    }
    rootPtr->build(fin);
    fin.close();
  }
}
<|endoftext|>


<commit_before>/*
 * ty, a collection of GUI and command-line tools to manage Teensy devices
 *
 * Distributed under the MIT license (see LICENSE.txt or http://opensource.org/licenses/MIT)
 * Copyright (c) 2015 Niels Martignène <niels.martignene@gmail.com>
 */

#include <QMutexLocker>
#include <QPlainTextDocumentLayout>
#include <QTextBlock>
#include <QTextCursor>

#include "board.hh"
#include "database.hh"
#include "tyqt.hh"

using namespace std;

Board::Board(ty_board *board, QObject *parent)
    : QObject(parent), board_(ty_board_ref(board))
{
    serial_document_.setDocumentLayout(new QPlainTextDocumentLayout(&serial_document_));
    serial_document_.setUndoRedoEnabled(false);

    // The monitor will move the serial notifier to a dedicated thread
    connect(&serial_notifier_, &DescriptorNotifier::activated, this, &Board::serialReceived,
            Qt::DirectConnection);

    error_timer_.setInterval(SHOW_ERROR_TIMEOUT);
    error_timer_.setSingleShot(true);
    connect(&error_timer_, &QTimer::timeout, this, &Board::statusChanged);

    loadSettings();
}

shared_ptr<Board> Board::createBoard(ty_board *board)
{
    // Work around the private constructor for make_shared()
    struct BoardSharedEnabler : public Board {
        BoardSharedEnabler(ty_board *board)
            : Board(board) {}
    };

    return make_shared<BoardSharedEnabler>(board);
}

Board::~Board()
{
    ty_board_interface_close(serial_iface_);
    ty_board_unref(board_);
}

void Board::loadSettings()
{
    auto tag = db_.get("tag", "").toString();
    int r = ty_board_set_tag(board_, tag.isEmpty() ? nullptr : tag.toLocal8Bit().constData());
    if (r < 0)
        throw bad_alloc();

    reset_after_ = db_.get("resetAfter", true).toBool();
    clear_on_reset_ = db_.get("clearOnReset", false).toBool();
    serial_document_.setMaximumBlockCount(db_.get("scrollBackLimit", 200000).toInt());
    serial_attach_ = db_.get("attachMonitor", true).toBool();

    if (serial_attach_ && serialAvailable()) {
        serial_attach_ = openSerialInterface();
    } else {
        closeSerialInterface();
    }

    emit infoChanged();
    emit settingsChanged();
}

bool Board::matchesTag(const QString &id)
{
    return ty_board_matches_tag(board_, id.toLocal8Bit().constData());
}

ty_board_state Board::state() const
{
    return ty_board_get_state(board_);
}

uint16_t Board::capabilities() const
{
    return ty_board_get_capabilities(board_);
}

const ty_board_model *Board::model() const
{
    return ty_board_get_model(board_);
}

QString Board::modelName() const
{
    auto model = ty_board_get_model(board_);
    if (!model)
        return tr("(unknown)");

    return ty_board_model_get_name(model);
}

QString Board::tag() const
{
    return ty_board_get_tag(board_);
}

QString Board::id() const
{
    return ty_board_get_id(board_);
}

QString Board::location() const
{
    return ty_board_get_location(board_);
}

uint64_t Board::serialNumber() const
{
    return ty_board_get_serial_number(board_);
}

std::vector<BoardInterfaceInfo> Board::interfaces() const
{
    std::vector<BoardInterfaceInfo> vec;

    ty_board_list_interfaces(board_, [](ty_board_interface *iface, void *udata) {
        BoardInterfaceInfo info;
        info.name = ty_board_interface_get_name(iface);
        info.path = ty_board_interface_get_path(iface);
        info.capabilities = ty_board_interface_get_capabilities(iface);
        info.number = ty_board_interface_get_interface_number(iface);
        info.open = ty_board_interface_get_handle(iface);

        auto vec = reinterpret_cast<std::vector<BoardInterfaceInfo> *>(udata);
        vec->push_back(info);

        return 0;
    }, &vec);

    return vec;
}

bool Board::isRunning() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_RUN);
}

bool Board::uploadAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_UPLOAD) || rebootAvailable();
}

bool Board::resetAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_RESET) || rebootAvailable();
}

bool Board::rebootAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_REBOOT);
}

bool Board::serialAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_SERIAL);
}

bool Board::errorOccured() const
{
    return error_timer_.remainingTime() > 0;
}

QString Board::statusIconFileName() const
{
    if (errorOccured())
        return ":/board_error";
    if (running_task_.status() == TY_TASK_STATUS_RUNNING)
        return ":/board_working";
    if (isRunning())
        return serialOpen() ? ":/board_attached" : ":/board_detached";
    if (uploadAvailable())
        return ":/board_bootloader";

    return ":/board_missing";
}

QString Board::statusText() const
{
    if (isRunning())
        return status_firmware_.isEmpty() ? tr("(running)") : status_firmware_;
    if (uploadAvailable())
        return tr("(bootloader)");
    return tr("(missing)");
}

void Board::appendToSerialDocument(const QString &s)
{
    QTextCursor cursor(&serial_document_);
    cursor.movePosition(QTextCursor::End);

    cursor.insertText(s);
}

QStringList Board::makeCapabilityList(uint16_t capabilities)
{
    QStringList list;

    for (unsigned int i = 0; i < TY_BOARD_CAPABILITY_COUNT; i++) {
        if (capabilities & (1 << i))
            list.append(ty_board_capability_get_name(static_cast<ty_board_capability>(i)));
    }

    return list;
}

QString Board::makeCapabilityString(uint16_t capabilities, QString empty_str)
{
    QStringList list = makeCapabilityList(capabilities);

    if (list.isEmpty()) {
        return empty_str;
    } else {
        return list.join(", ");
    }
}

TaskInterface Board::upload(const QString &filename)
{
    shared_ptr<Firmware> fw;
    if (!filename.isEmpty()) {
        fw = Firmware::load(filename);
    } else {
        if (firmware_.isEmpty())
            return watchTask(make_task<FailedTask>(tr("No firmware set for board '%1'").arg(tag())));
        fw = Firmware::load(firmware_);
    }
    if (!fw)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    return upload({fw});
}

TaskInterface Board::upload(const vector<shared_ptr<Firmware>> &fws)
{
    return upload(fws, reset_after_);
}

TaskInterface Board::upload(const vector<shared_ptr<Firmware>> &fws, bool reset_after)
{
    vector<ty_firmware *> fws2;
    ty_task *task;
    int r;

    fws2.reserve(fws.size());
    for (auto &fw: fws)
        fws2.push_back(fw->firmware());

    r = ty_upload(board_, &fws2[0], fws2.size(), reset_after ? 0 : TY_UPLOAD_NORESET, &task);
    if (r < 0)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    auto task2 = make_task<TyTask>(task);
    watchTask(task2);
    connect(&task_watcher_, &TaskWatcher::finished,
            this, [=](bool success, shared_ptr<void> result) {
        if (!success)
            return;

        auto fw = static_cast<ty_firmware *>(result.get());
        setFirmware(ty_firmware_get_filename(fw));
        status_firmware_ = ty_firmware_get_name(fw);
    });

    return task2;
}

TaskInterface Board::reset()
{
    ty_task *task;
    int r;

    r = ty_reset(board_, &task);
    if (r < 0)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    return watchTask(make_task<TyTask>(task));
}

TaskInterface Board::reboot()
{
    ty_task *task;
    int r;

    r = ty_reboot(board_, &task);
    if (r < 0)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    return watchTask(make_task<TyTask>(task));
}

bool Board::sendSerial(const QByteArray &buf)
{
    ssize_t r = ty_board_serial_write(board_, buf.data(), buf.size());
    if (r < 0) {
        emit notifyLog(TY_LOG_ERROR, ty_error_last_message());
        return false;
    }

    return true;
}

void Board::setTag(const QString &tag)
{
    if (tag.isEmpty() && ty_board_get_tag(board_) == ty_board_get_id(board_))
        return;
    if (tag == ty_board_get_tag(board_))
        return;

    int r = ty_board_set_tag(board_, tag.isEmpty() ? nullptr : tag.toLocal8Bit().constData());
    if (r < 0)
        throw bad_alloc();

    db_.put("tag", tag);
    emit infoChanged();
}

void Board::setFirmware(const QString &firmware)
{
    if (firmware == firmware_)
        return;

    firmware_ = firmware;

    emit settingsChanged();
}

void Board::setResetAfter(bool reset_after)
{
    if (reset_after == reset_after_)
        return;

    reset_after_ = reset_after;

    db_.put("resetAfter", reset_after);
    emit settingsChanged();
}

void Board::setClearOnReset(bool clear_on_reset)
{
    if (clear_on_reset == clear_on_reset_)
        return;

    clear_on_reset_ = clear_on_reset;

    db_.put("clearOnReset", clear_on_reset);
    emit settingsChanged();
}

void Board::setScrollBackLimit(unsigned int limit)
{
    if (static_cast<int>(limit) == serial_document_.maximumBlockCount())
        return;

    serial_document_.setMaximumBlockCount(static_cast<int>(limit));

    db_.put("scrollBackLimit", limit);
    emit settingsChanged();
}

void Board::setAttachMonitor(bool attach_monitor)
{
    if (attach_monitor == serial_attach_)
        return;

    if (attach_monitor && serialAvailable()) {
        attach_monitor = openSerialInterface();
    } else {
        closeSerialInterface();
    }

    serial_attach_ = attach_monitor;

    db_.put("attachMonitor", attach_monitor);
    emit settingsChanged();
}

TaskInterface Board::startUpload(const QString &filename)
{
    auto task = upload(filename);
    task.start();
    return task;
}

TaskInterface Board::startUpload(const vector<shared_ptr<Firmware>> &fws)
{
    auto task = upload(fws);
    task.start();
    return task;
}

TaskInterface Board::startUpload(const vector<shared_ptr<Firmware>> &fws, bool reset_after)
{
    auto task = upload(fws, reset_after);
    task.start();
    return task;
}

TaskInterface Board::startReset()
{
    auto task = reset();
    task.start();
    return task;
}

TaskInterface Board::startReboot()
{
    auto task = reboot();
    task.start();
    return task;
}

void Board::notifyLog(ty_log_level level, const QString &msg)
{
    Q_UNUSED(msg);

    if (level == TY_LOG_ERROR) {
        error_timer_.start();
        emit statusChanged();
    }
}

void Board::serialReceived(ty_descriptor desc)
{
    Q_UNUSED(desc);

    QMutexLocker locker(&serial_lock_);
    ty_error_mask(TY_ERROR_MODE);
    ty_error_mask(TY_ERROR_IO);

    bool was_empty = !serial_buf_len_;
    /* On OSX El Capitan (at least), serial device reads are often partial (512 and 1020 bytes
       reads happen pretty often), so try hard to empty the OS buffer. The Qt event loop may not
       give us back control before some time, and we want to avoid buffer overruns. */
    for (unsigned int i = 0; i < 4; i++) {
        if (serial_buf_len_ == sizeof(serial_buf_))
            break;

        int r = ty_board_serial_read(board_, serial_buf_ + serial_buf_len_,
                                      sizeof(serial_buf_) - serial_buf_len_, 0);
        if (r < 0) {
            serial_notifier_.clear();
            break;
        }
        if (!r)
            break;
        serial_buf_len_ += static_cast<size_t>(r);
    }

    ty_error_unmask();
    ty_error_unmask();
    locker.unlock();

    if (was_empty && serial_buf_len_)
        QMetaObject::invokeMethod(this, "updateSerialDocument", Qt::QueuedConnection);
}

void Board::updateSerialDocument()
{
    QMutexLocker locker(&serial_lock_);
    auto str = QString::fromLocal8Bit(serial_buf_, serial_buf_len_);
    serial_buf_len_ = 0;
    locker.unlock();

    // FIXME: behavior with partial characters (UTF-8 or other multibyte encodings)
    appendToSerialDocument(str);
}

void Board::notifyFinished(bool success, std::shared_ptr<void> result)
{
    Q_UNUSED(success);
    Q_UNUSED(result);

    running_task_ = TaskInterface();
    task_watcher_.setTask(nullptr);

    emit statusChanged();
}

void Board::notifyProgress(const QString &action, unsigned int value, unsigned int max)
{
    Q_UNUSED(action);
    Q_UNUSED(value);
    Q_UNUSED(max);

    emit statusChanged();
}

void Board::refreshBoard()
{
    if (ty_board_has_capability(board_, TY_BOARD_CAPABILITY_SERIAL) && serial_attach_) {
        openSerialInterface();
    } else {
        closeSerialInterface();
    }

    if (state() == TY_BOARD_STATE_DROPPED) {
        emit dropped();
        return;
    }

    emit infoChanged();
    emit interfacesChanged();
}

bool Board::openSerialInterface()
{
    if (serial_iface_)
        return true;

    ty_descriptor_set set = {};
    int r;

    r = ty_board_open_interface(board_, TY_BOARD_CAPABILITY_SERIAL, &serial_iface_);
    if (r < 0) {
        notifyLog(TY_LOG_ERROR, ty_error_last_message());
        return false;
    }
    if (!r)
        return false;
    ty_board_interface_get_descriptors(serial_iface_, &set, 1);
    serial_notifier_.setDescriptorSet(&set);

    if (clear_on_reset_)
        serial_document_.clear();

    emit interfacesChanged();
    return true;
}

void Board::closeSerialInterface()
{
    if (!serial_iface_)
        return;

    serial_notifier_.clear();
    ty_board_interface_close(serial_iface_);
    serial_iface_ = nullptr;

    emit interfacesChanged();
}

TaskInterface Board::watchTask(TaskInterface task)
{
    running_task_ = task;

    /* There may be task-specific slots, such as the firmware one from upload(),
       disconnect everyone and restore sane connections. */
    task_watcher_.disconnect();
    connect(&task_watcher_, &TaskWatcher::log, this, &Board::notifyLog);
    connect(&task_watcher_, &TaskWatcher::started, this, &Board::statusChanged);
    connect(&task_watcher_, &TaskWatcher::finished, this, &Board::notifyFinished);
    connect(&task_watcher_, &TaskWatcher::progress, this, &Board::notifyProgress);

    task_watcher_.setTask(&running_task_);

    return running_task_;
}
<commit_msg>Make Board firmware path a persistent setting<commit_after>/*
 * ty, a collection of GUI and command-line tools to manage Teensy devices
 *
 * Distributed under the MIT license (see LICENSE.txt or http://opensource.org/licenses/MIT)
 * Copyright (c) 2015 Niels Martignène <niels.martignene@gmail.com>
 */

#include <QFileInfo>
#include <QMutexLocker>
#include <QPlainTextDocumentLayout>
#include <QTextBlock>
#include <QTextCursor>

#include "board.hh"
#include "database.hh"
#include "tyqt.hh"

using namespace std;

Board::Board(ty_board *board, QObject *parent)
    : QObject(parent), board_(ty_board_ref(board))
{
    serial_document_.setDocumentLayout(new QPlainTextDocumentLayout(&serial_document_));
    serial_document_.setUndoRedoEnabled(false);

    // The monitor will move the serial notifier to a dedicated thread
    connect(&serial_notifier_, &DescriptorNotifier::activated, this, &Board::serialReceived,
            Qt::DirectConnection);

    error_timer_.setInterval(SHOW_ERROR_TIMEOUT);
    error_timer_.setSingleShot(true);
    connect(&error_timer_, &QTimer::timeout, this, &Board::statusChanged);

    loadSettings();
}

shared_ptr<Board> Board::createBoard(ty_board *board)
{
    // Work around the private constructor for make_shared()
    struct BoardSharedEnabler : public Board {
        BoardSharedEnabler(ty_board *board)
            : Board(board) {}
    };

    return make_shared<BoardSharedEnabler>(board);
}

Board::~Board()
{
    ty_board_interface_close(serial_iface_);
    ty_board_unref(board_);
}

void Board::loadSettings()
{
    auto tag = db_.get("tag", "").toString();
    int r = ty_board_set_tag(board_, tag.isEmpty() ? nullptr : tag.toLocal8Bit().constData());
    if (r < 0)
        throw bad_alloc();

    firmware_ = db_.get("firmware", "").toString();
    if (firmware_.isEmpty() || !QFileInfo::exists(firmware_))
        firmware_ = "";
    reset_after_ = db_.get("resetAfter", true).toBool();
    clear_on_reset_ = db_.get("clearOnReset", false).toBool();
    serial_document_.setMaximumBlockCount(db_.get("scrollBackLimit", 200000).toInt());
    serial_attach_ = db_.get("attachMonitor", true).toBool();

    if (serial_attach_ && serialAvailable()) {
        serial_attach_ = openSerialInterface();
    } else {
        closeSerialInterface();
    }

    emit infoChanged();
    emit settingsChanged();
}

bool Board::matchesTag(const QString &id)
{
    return ty_board_matches_tag(board_, id.toLocal8Bit().constData());
}

ty_board_state Board::state() const
{
    return ty_board_get_state(board_);
}

uint16_t Board::capabilities() const
{
    return ty_board_get_capabilities(board_);
}

const ty_board_model *Board::model() const
{
    return ty_board_get_model(board_);
}

QString Board::modelName() const
{
    auto model = ty_board_get_model(board_);
    if (!model)
        return tr("(unknown)");

    return ty_board_model_get_name(model);
}

QString Board::tag() const
{
    return ty_board_get_tag(board_);
}

QString Board::id() const
{
    return ty_board_get_id(board_);
}

QString Board::location() const
{
    return ty_board_get_location(board_);
}

uint64_t Board::serialNumber() const
{
    return ty_board_get_serial_number(board_);
}

std::vector<BoardInterfaceInfo> Board::interfaces() const
{
    std::vector<BoardInterfaceInfo> vec;

    ty_board_list_interfaces(board_, [](ty_board_interface *iface, void *udata) {
        BoardInterfaceInfo info;
        info.name = ty_board_interface_get_name(iface);
        info.path = ty_board_interface_get_path(iface);
        info.capabilities = ty_board_interface_get_capabilities(iface);
        info.number = ty_board_interface_get_interface_number(iface);
        info.open = ty_board_interface_get_handle(iface);

        auto vec = reinterpret_cast<std::vector<BoardInterfaceInfo> *>(udata);
        vec->push_back(info);

        return 0;
    }, &vec);

    return vec;
}

bool Board::isRunning() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_RUN);
}

bool Board::uploadAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_UPLOAD) || rebootAvailable();
}

bool Board::resetAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_RESET) || rebootAvailable();
}

bool Board::rebootAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_REBOOT);
}

bool Board::serialAvailable() const
{
    return ty_board_has_capability(board_, TY_BOARD_CAPABILITY_SERIAL);
}

bool Board::errorOccured() const
{
    return error_timer_.remainingTime() > 0;
}

QString Board::statusIconFileName() const
{
    if (errorOccured())
        return ":/board_error";
    if (running_task_.status() == TY_TASK_STATUS_RUNNING)
        return ":/board_working";
    if (isRunning())
        return serialOpen() ? ":/board_attached" : ":/board_detached";
    if (uploadAvailable())
        return ":/board_bootloader";

    return ":/board_missing";
}

QString Board::statusText() const
{
    if (isRunning())
        return status_firmware_.isEmpty() ? tr("(running)") : status_firmware_;
    if (uploadAvailable())
        return tr("(bootloader)");
    return tr("(missing)");
}

void Board::appendToSerialDocument(const QString &s)
{
    QTextCursor cursor(&serial_document_);
    cursor.movePosition(QTextCursor::End);

    cursor.insertText(s);
}

QStringList Board::makeCapabilityList(uint16_t capabilities)
{
    QStringList list;

    for (unsigned int i = 0; i < TY_BOARD_CAPABILITY_COUNT; i++) {
        if (capabilities & (1 << i))
            list.append(ty_board_capability_get_name(static_cast<ty_board_capability>(i)));
    }

    return list;
}

QString Board::makeCapabilityString(uint16_t capabilities, QString empty_str)
{
    QStringList list = makeCapabilityList(capabilities);

    if (list.isEmpty()) {
        return empty_str;
    } else {
        return list.join(", ");
    }
}

TaskInterface Board::upload(const QString &filename)
{
    shared_ptr<Firmware> fw;
    if (!filename.isEmpty()) {
        fw = Firmware::load(filename);
    } else {
        if (firmware_.isEmpty())
            return watchTask(make_task<FailedTask>(tr("No firmware set for board '%1'").arg(tag())));
        fw = Firmware::load(firmware_);
    }
    if (!fw)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    return upload({fw});
}

TaskInterface Board::upload(const vector<shared_ptr<Firmware>> &fws)
{
    return upload(fws, reset_after_);
}

TaskInterface Board::upload(const vector<shared_ptr<Firmware>> &fws, bool reset_after)
{
    vector<ty_firmware *> fws2;
    ty_task *task;
    int r;

    fws2.reserve(fws.size());
    for (auto &fw: fws)
        fws2.push_back(fw->firmware());

    r = ty_upload(board_, &fws2[0], fws2.size(), reset_after ? 0 : TY_UPLOAD_NORESET, &task);
    if (r < 0)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    auto task2 = make_task<TyTask>(task);
    watchTask(task2);
    connect(&task_watcher_, &TaskWatcher::finished,
            this, [=](bool success, shared_ptr<void> result) {
        if (!success)
            return;

        auto fw = static_cast<ty_firmware *>(result.get());
        setFirmware(ty_firmware_get_filename(fw));
        status_firmware_ = ty_firmware_get_name(fw);
    });

    return task2;
}

TaskInterface Board::reset()
{
    ty_task *task;
    int r;

    r = ty_reset(board_, &task);
    if (r < 0)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    return watchTask(make_task<TyTask>(task));
}

TaskInterface Board::reboot()
{
    ty_task *task;
    int r;

    r = ty_reboot(board_, &task);
    if (r < 0)
        return watchTask(make_task<FailedTask>(ty_error_last_message()));

    return watchTask(make_task<TyTask>(task));
}

bool Board::sendSerial(const QByteArray &buf)
{
    ssize_t r = ty_board_serial_write(board_, buf.data(), buf.size());
    if (r < 0) {
        emit notifyLog(TY_LOG_ERROR, ty_error_last_message());
        return false;
    }

    return true;
}

void Board::setTag(const QString &tag)
{
    if (tag.isEmpty() && ty_board_get_tag(board_) == ty_board_get_id(board_))
        return;
    if (tag == ty_board_get_tag(board_))
        return;

    int r = ty_board_set_tag(board_, tag.isEmpty() ? nullptr : tag.toLocal8Bit().constData());
    if (r < 0)
        throw bad_alloc();

    db_.put("tag", tag);
    emit infoChanged();
}

void Board::setFirmware(const QString &firmware)
{
    if (firmware == firmware_)
        return;

    firmware_ = firmware;

    db_.put("firmware", firmware);
    emit settingsChanged();
}

void Board::setResetAfter(bool reset_after)
{
    if (reset_after == reset_after_)
        return;

    reset_after_ = reset_after;

    db_.put("resetAfter", reset_after);
    emit settingsChanged();
}

void Board::setClearOnReset(bool clear_on_reset)
{
    if (clear_on_reset == clear_on_reset_)
        return;

    clear_on_reset_ = clear_on_reset;

    db_.put("clearOnReset", clear_on_reset);
    emit settingsChanged();
}

void Board::setScrollBackLimit(unsigned int limit)
{
    if (static_cast<int>(limit) == serial_document_.maximumBlockCount())
        return;

    serial_document_.setMaximumBlockCount(static_cast<int>(limit));

    db_.put("scrollBackLimit", limit);
    emit settingsChanged();
}

void Board::setAttachMonitor(bool attach_monitor)
{
    if (attach_monitor == serial_attach_)
        return;

    if (attach_monitor && serialAvailable()) {
        attach_monitor = openSerialInterface();
    } else {
        closeSerialInterface();
    }

    serial_attach_ = attach_monitor;

    db_.put("attachMonitor", attach_monitor);
    emit settingsChanged();
}

TaskInterface Board::startUpload(const QString &filename)
{
    auto task = upload(filename);
    task.start();
    return task;
}

TaskInterface Board::startUpload(const vector<shared_ptr<Firmware>> &fws)
{
    auto task = upload(fws);
    task.start();
    return task;
}

TaskInterface Board::startUpload(const vector<shared_ptr<Firmware>> &fws, bool reset_after)
{
    auto task = upload(fws, reset_after);
    task.start();
    return task;
}

TaskInterface Board::startReset()
{
    auto task = reset();
    task.start();
    return task;
}

TaskInterface Board::startReboot()
{
    auto task = reboot();
    task.start();
    return task;
}

void Board::notifyLog(ty_log_level level, const QString &msg)
{
    Q_UNUSED(msg);

    if (level == TY_LOG_ERROR) {
        error_timer_.start();
        emit statusChanged();
    }
}

void Board::serialReceived(ty_descriptor desc)
{
    Q_UNUSED(desc);

    QMutexLocker locker(&serial_lock_);
    ty_error_mask(TY_ERROR_MODE);
    ty_error_mask(TY_ERROR_IO);

    bool was_empty = !serial_buf_len_;
    /* On OSX El Capitan (at least), serial device reads are often partial (512 and 1020 bytes
       reads happen pretty often), so try hard to empty the OS buffer. The Qt event loop may not
       give us back control before some time, and we want to avoid buffer overruns. */
    for (unsigned int i = 0; i < 4; i++) {
        if (serial_buf_len_ == sizeof(serial_buf_))
            break;

        int r = ty_board_serial_read(board_, serial_buf_ + serial_buf_len_,
                                      sizeof(serial_buf_) - serial_buf_len_, 0);
        if (r < 0) {
            serial_notifier_.clear();
            break;
        }
        if (!r)
            break;
        serial_buf_len_ += static_cast<size_t>(r);
    }

    ty_error_unmask();
    ty_error_unmask();
    locker.unlock();

    if (was_empty && serial_buf_len_)
        QMetaObject::invokeMethod(this, "updateSerialDocument", Qt::QueuedConnection);
}

void Board::updateSerialDocument()
{
    QMutexLocker locker(&serial_lock_);
    auto str = QString::fromLocal8Bit(serial_buf_, serial_buf_len_);
    serial_buf_len_ = 0;
    locker.unlock();

    // FIXME: behavior with partial characters (UTF-8 or other multibyte encodings)
    appendToSerialDocument(str);
}

void Board::notifyFinished(bool success, std::shared_ptr<void> result)
{
    Q_UNUSED(success);
    Q_UNUSED(result);

    running_task_ = TaskInterface();
    task_watcher_.setTask(nullptr);

    emit statusChanged();
}

void Board::notifyProgress(const QString &action, unsigned int value, unsigned int max)
{
    Q_UNUSED(action);
    Q_UNUSED(value);
    Q_UNUSED(max);

    emit statusChanged();
}

void Board::refreshBoard()
{
    if (ty_board_has_capability(board_, TY_BOARD_CAPABILITY_SERIAL) && serial_attach_) {
        openSerialInterface();
    } else {
        closeSerialInterface();
    }

    if (state() == TY_BOARD_STATE_DROPPED) {
        emit dropped();
        return;
    }

    emit infoChanged();
    emit interfacesChanged();
}

bool Board::openSerialInterface()
{
    if (serial_iface_)
        return true;

    ty_descriptor_set set = {};
    int r;

    r = ty_board_open_interface(board_, TY_BOARD_CAPABILITY_SERIAL, &serial_iface_);
    if (r < 0) {
        notifyLog(TY_LOG_ERROR, ty_error_last_message());
        return false;
    }
    if (!r)
        return false;
    ty_board_interface_get_descriptors(serial_iface_, &set, 1);
    serial_notifier_.setDescriptorSet(&set);

    if (clear_on_reset_)
        serial_document_.clear();

    emit interfacesChanged();
    return true;
}

void Board::closeSerialInterface()
{
    if (!serial_iface_)
        return;

    serial_notifier_.clear();
    ty_board_interface_close(serial_iface_);
    serial_iface_ = nullptr;

    emit interfacesChanged();
}

TaskInterface Board::watchTask(TaskInterface task)
{
    running_task_ = task;

    /* There may be task-specific slots, such as the firmware one from upload(),
       disconnect everyone and restore sane connections. */
    task_watcher_.disconnect();
    connect(&task_watcher_, &TaskWatcher::log, this, &Board::notifyLog);
    connect(&task_watcher_, &TaskWatcher::started, this, &Board::statusChanged);
    connect(&task_watcher_, &TaskWatcher::finished, this, &Board::notifyFinished);
    connect(&task_watcher_, &TaskWatcher::progress, this, &Board::notifyProgress);

    task_watcher_.setTask(&running_task_);

    return running_task_;
}
<|endoftext|>


<commit_before>/**
	Copyright (c) 2009 James Wynn (james@jameswynn.com)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	James Wynn james@jameswynn.com
*/

#include <FileWatcher/FileWatcherOSX.h>

#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_KQUEUE

#include <sys/event.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>

namespace FW
{

#define MAX_CHANGE_EVENT_SIZE 2000

	typedef struct kevent KEvent;

	struct EntryStruct
	{
		EntryStruct(const char* filename, time_t mtime = 0)
		: mFilename(filename), mModifiedTime(mtime)
		{
		}
		~EntryStruct()
		{
			delete[] mFilename;
		}
		const char* mFilename;
		time_t mModifiedTime;
	};

	int comparator(const void* ke1, const void* ke2)
	{
		/*KEvent* kevent1 = (KEvent*) ke1;
		KEvent* kevent2 = (KEvent*) ke2;

		EntryStruct* event1 = (EntryStruct*)kevent1->udata;
		EntryStruct* event2 = (EntryStruct*)kevent2->udata;
		return strcmp(event1->mFilename, event2->mFilename);
		*/
		return strcmp(((EntryStruct*)(((KEvent*)(ke1))->udata))->mFilename, ((EntryStruct*)(((KEvent*)(ke2))->udata))->mFilename);
	}

	struct WatchStruct
	{
		WatchID mWatchID;
		String mDirName;
		FileWatchListener* mListener;

		// index 0 is always the directory
		KEvent mChangeList[MAX_CHANGE_EVENT_SIZE];
		size_t mChangeListCount;

		WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener)
		: mWatchID(watchid), mDirName(dirname), mListener(listener)
		{
			mChangeListCount = 0;
			addAll();
		}

		void addFile(const String& name, bool imitEvents = true)
		{
			//fprintf(stderr, "ADDED: %s\n", name.c_str());

			// create entry
			struct stat attrib;
			stat(name.c_str(), &attrib);

			int fd = open(name.c_str(), O_RDONLY);

			if(fd == -1)
				throw FileNotFoundException(name);

			++mChangeListCount;

			char* namecopy = new char[name.length() + 1];
			strncpy(namecopy, name.c_str(), name.length());
			namecopy[name.length()] = 0;
			EntryStruct* entry = new EntryStruct(namecopy, attrib.st_mtime);

			// set the event data at the end of the list
			EV_SET(&mChangeList[mChangeListCount], fd, EVFILT_VNODE,
				   EV_ADD | EV_ENABLE | EV_ONESHOT,
				   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
				   0, (void*)entry);

			// qsort
			qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);

			// handle action
			if(imitEvents)
				handleAction(name, Action::Add);
		}

		void removeFile(const String& name, bool imitEvents = true)
		{
			// bsearch
			KEvent target;
			EntryStruct tempEntry(name.c_str(), 0);
			target.udata = &tempEntry;
			KEvent* ke = (KEvent*)bsearch(&target, &mChangeList, mChangeListCount + 1, sizeof(KEvent), comparator);
			if(!ke)
				throw FileNotFoundException(directory);

			tempEntry.mFilename = 0;

			// delete
			close(ke->ident);
			delete((EntryStruct*)ke->udata);
			memset(ke, 0, sizeof(KEvent));

			// move end to current
			memcpy(ke, &mChangeList[mChangeListCount], sizeof(KEvent));
			memset(&mChangeList[mChangeListCount], 0, sizeof(KEvent));
			--mChangeListCount;

			// qsort
			qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);

			// handle action
			if(imitEvents)
				handleAction(name, Action::Delete);
		}

		// called when the directory is actually changed
		// means a file has been added or removed
		// rescans the watched directory adding/removing files and sending notices
		void rescan()
		{
			// if new file, call addFile
			// if missing file, call removeFile
			// if timestamp modified, call handleAction(filename, ACTION_MODIFIED);
			DIR* dir = opendir(mDirName.c_str());
			if(!dir)
				return;

			struct dirent* dentry;
			KEvent* ke = &mChangeList[1];
			EntryStruct* entry = 0;
			struct stat attrib;

			while((dentry = readdir(dir)) != NULL)
			{
				String fname = mDirName + "/" + dentry->d_name;
				stat(fname.c_str(), &attrib);
				if(!S_ISREG(attrib.st_mode))
					continue;

				if(ke <= &mChangeList[mChangeListCount])
				{
					entry = (EntryStruct*)ke->udata;
					int result = strcmp(entry->mFilename, fname.c_str());
					//fprintf(stderr, "[%s cmp %s]\n", entry->mFilename, fname.c_str());
					if(result == 0)
					{
						stat(entry->mFilename, &attrib);
						time_t timestamp = attrib.st_mtime;

						if(entry->mModifiedTime != timestamp)
						{
							entry->mModifiedTime = timestamp;
							handleAction(entry->mFilename, Action::Modified);
						}
						ke++;
					}
					else if(result < 0)
					{
						// f1 was deleted
						removeFile(entry->mFilename);
						ke++;
					}
					else
					{
						// f2 was created
						addFile(fname);
						ke++;
					}
				}
				else
				{
					// just add
					addFile(fname);
					ke++;
				}
			}//end while

			closedir(dir);
		};

		void handleAction(const String& filename, FileWatcher::Action action)
		{
			mListener->handleFileAction(mWatchID, mDirName, filename, action);
		}

		void addAll()
		{
			// add base dir
			int fd = open(mDirName.c_str(), O_RDONLY);
			EV_SET(&mChangeList[0], fd, EVFILT_VNODE,
				   EV_ADD | EV_ENABLE | EV_ONESHOT,
				   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
				   0, 0);

			//fprintf(stderr, "ADDED: %s\n", mDirName.c_str());

			// scan directory and call addFile(name, false) on each file
			DIR* dir = opendir(mDirName.c_str());
			if(!dir)
				throw FileNotFoundException(directory);

			struct dirent* entry;
			struct stat attrib;
			while((entry = readdir(dir)) != NULL)
			{
				String fname = (mDirName + "/" + String(entry->d_name));
				stat(fname.c_str(), &attrib);
				if(S_ISREG(attrib.st_mode))
					addFile(fname, false);
				//else
				//	fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode);

			}//end while

			closedir(dir);
		}

		void removeAll()
		{
			KEvent* ke = NULL;

			// go through list removing each file and sending an event
			for(int i = 0; i < mChangeListCount; ++i)
			{
				ke = &mChangeList[i];
				//handleAction(name, Action::Delete);
				EntryStruct* entry = (EntryStruct*)ke->udata;

				handleAction(entry->mFilename, Action::Delete);

				// delete
				close(ke->ident);
				delete((EntryStruct*)ke->udata);
			}
		}
	};

	void FileWatcherOSX::update()
	{
		int nev = 0;
		struct kevent event;

		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			WatchStruct* watch = iter->second;

			while((nev = kevent(mDescriptor, (KEvent*)&(watch->mChangeList), watch->mChangeListCount + 1, &event, 1, &mTimeOut)) != 0)
			{
				if(nev == -1)
					perror("kevent");
				else
				{
					EntryStruct* entry = 0;
					if((entry = (EntryStruct*)event.udata) != 0)
					{
						//fprintf(stderr, "File: %s -- ", (char*)entry->mFilename);

						if(event.fflags & NOTE_DELETE)
						{
							//fprintf(stderr, "File deleted\n");
							//watch->handleAction(entry->mFilename, Action::Delete);
							watch->removeFile(entry->mFilename);
						}
						if(event.fflags & NOTE_EXTEND ||
						   event.fflags & NOTE_WRITE ||
						   event.fflags & NOTE_ATTRIB)
						{
							//fprintf(stderr, "modified\n");
							//watch->rescan();
							struct stat attrib;
							stat(entry->mFilename, &attrib);
							entry->mModifiedTime = attrib.st_mtime;
							watch->handleAction(entry->mFilename, Action::Modified);
						}
					}
					else
					{
						//fprintf(stderr, "Dir: %s -- rescanning\n", watch->mDirName.c_str());
						watch->rescan();
					}
				}
			}
		}
	}

	//--------
	FileWatcherOSX::FileWatcherOSX()
	{
		mDescriptor = kqueue();
		mTimeOut.tv_sec = 0;
		mTimeOut.tv_nsec = 0;
	}

	//--------
	FileWatcherOSX::~FileWatcherOSX()
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			delete iter->second;
		}
		mWatches.clear();

		close(mDescriptor);
	}

	//--------
	WatchID FileWatcherOSX::addWatch(const String& directory, FileWatchListener* watcher)
	{
/*		int fd = open(directory.c_str(), O_RDONLY);
		if(fd == -1)
			perror("open");

		EV_SET(&change, fd, EVFILT_VNODE,
			   EV_ADD | EV_ENABLE | EV_ONESHOT,
			   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
			   0, (void*)"testing");
*/

		WatchStruct* watch = new WatchStruct(++mLastWatchID, directory, watcher);
		mWatches.insert(std::make_pair(mLastWatchID, watch));
		return mLastWatchID;
	}

	//--------
	void FileWatcherOSX::removeWatch(const String& directory)
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			if(directory == iter->second->mDirName)
			{
				removeWatch(iter->first);
				return;
			}
		}
	}

	//--------
	void FileWatcherOSX::removeWatch(WatchID watchid)
	{
		WatchMap::iterator iter = mWatches.find(watchid);

		if(iter == mWatches.end())
			return;

		WatchStruct* watch = iter->second;
		mWatches.erase(iter);

		//inotify_rm_watch(mFD, watchid);

		delete watch;
		watch = 0;
	}

	//--------
	void FileWatcherOSX::handleAction(WatchStruct* watch, const String& filename, unsigned long action)
	{
	}

};//namespace FW

#endif//FILEWATCHER_PLATFORM_KQUEUE
<commit_msg>Committed OSX fix from Murat Sari.<commit_after>/**
	Copyright (c) 2009 James Wynn (james@jameswynn.com)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	James Wynn james@jameswynn.com
*/

#include <FileWatcher/FileWatcherOSX.h>

#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_KQUEUE

#include <sys/event.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>

namespace FW
{

#define MAX_CHANGE_EVENT_SIZE 2000

	typedef struct kevent KEvent;

	struct EntryStruct
	{
		EntryStruct(const char* filename, time_t mtime = 0)
		: mFilename(filename), mModifiedTime(mtime)
		{
		}
		~EntryStruct()
		{
			delete[] mFilename;
		}
		const char* mFilename;
		time_t mModifiedTime;
	};

	int comparator(const void* ke1, const void* ke2)
	{
		/*KEvent* kevent1 = (KEvent*) ke1;
		KEvent* kevent2 = (KEvent*) ke2;

		EntryStruct* event1 = (EntryStruct*)kevent1->udata;
		EntryStruct* event2 = (EntryStruct*)kevent2->udata;
		return strcmp(event1->mFilename, event2->mFilename);
		*/
		return strcmp(((EntryStruct*)(((KEvent*)(ke1))->udata))->mFilename, ((EntryStruct*)(((KEvent*)(ke2))->udata))->mFilename);
	}

	struct WatchStruct
	{
		WatchID mWatchID;
		String mDirName;
		FileWatchListener* mListener;

		// index 0 is always the directory
		KEvent mChangeList[MAX_CHANGE_EVENT_SIZE];
		size_t mChangeListCount;

		WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener)
		: mWatchID(watchid), mDirName(dirname), mListener(listener)
		{
			mChangeListCount = 0;
			addAll();
		}

		void addFile(const String& name, bool imitEvents = true)
		{
			//fprintf(stderr, "ADDED: %s\n", name.c_str());

			// create entry
			struct stat attrib;
			stat(name.c_str(), &attrib);

			int fd = open(name.c_str(), O_RDONLY);

			if(fd == -1)
				throw FileNotFoundException(name);

			++mChangeListCount;

			char* namecopy = new char[name.length() + 1];
			strncpy(namecopy, name.c_str(), name.length());
			namecopy[name.length()] = 0;
			EntryStruct* entry = new EntryStruct(namecopy, attrib.st_mtime);

			// set the event data at the end of the list
			EV_SET(&mChangeList[mChangeListCount], fd, EVFILT_VNODE,
				   EV_ADD | EV_ENABLE | EV_ONESHOT,
				   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
				   0, (void*)entry);

			// qsort
			qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);

			// handle action
			if(imitEvents)
				handleAction(name, Actions::Add);
		}

		void removeFile(const String& name, bool imitEvents = true)
		{
			// bsearch
			KEvent target;
			EntryStruct tempEntry(name.c_str(), 0);
			target.udata = &tempEntry;
			KEvent* ke = (KEvent*)bsearch(&target, &mChangeList, mChangeListCount + 1, sizeof(KEvent), comparator);
			if(!ke)
				throw FileNotFoundException(name);

			tempEntry.mFilename = 0;

			// delete
			close(ke->ident);
			delete((EntryStruct*)ke->udata);
			memset(ke, 0, sizeof(KEvent));

			// move end to current
			memcpy(ke, &mChangeList[mChangeListCount], sizeof(KEvent));
			memset(&mChangeList[mChangeListCount], 0, sizeof(KEvent));
			--mChangeListCount;

			// qsort
			qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);

			// handle action
			if(imitEvents)
				handleAction(name, Actions::Delete);
		}

		// called when the directory is actually changed
		// means a file has been added or removed
		// rescans the watched directory adding/removing files and sending notices
		void rescan()
		{
			// if new file, call addFile
			// if missing file, call removeFile
			// if timestamp modified, call handleAction(filename, ACTION_MODIFIED);
			DIR* dir = opendir(mDirName.c_str());
			if(!dir)
				return;

			struct dirent* dentry;
			KEvent* ke = &mChangeList[1];
			EntryStruct* entry = 0;
			struct stat attrib;

			while((dentry = readdir(dir)) != NULL)
			{
				String fname = mDirName + "/" + dentry->d_name;
				stat(fname.c_str(), &attrib);
				if(!S_ISREG(attrib.st_mode))
					continue;

				if(ke <= &mChangeList[mChangeListCount])
				{
					entry = (EntryStruct*)ke->udata;
					int result = strcmp(entry->mFilename, fname.c_str());
					//fprintf(stderr, "[%s cmp %s]\n", entry->mFilename, fname.c_str());
					if(result == 0)
					{
						stat(entry->mFilename, &attrib);
						time_t timestamp = attrib.st_mtime;

						if(entry->mModifiedTime != timestamp)
						{
							entry->mModifiedTime = timestamp;
							handleAction(entry->mFilename, Actions::Modified);
						}
						ke++;
					}
					else if(result < 0)
					{
						// f1 was deleted
						removeFile(entry->mFilename);
						ke++;
					}
					else
					{
						// f2 was created
						addFile(fname);
						ke++;
					}
				}
				else
				{
					// just add
					addFile(fname);
					ke++;
				}
			}//end while

			closedir(dir);
		};

		void handleAction(const String& filename, FW::Action action)
		{
			mListener->handleFileAction(mWatchID, mDirName, filename, action);
		}

		void addAll()
		{
			// add base dir
			int fd = open(mDirName.c_str(), O_RDONLY);
			EV_SET(&mChangeList[0], fd, EVFILT_VNODE,
				   EV_ADD | EV_ENABLE | EV_ONESHOT,
				   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
				   0, 0);

			//fprintf(stderr, "ADDED: %s\n", mDirName.c_str());

			// scan directory and call addFile(name, false) on each file
			DIR* dir = opendir(mDirName.c_str());
			if(!dir)
				throw FileNotFoundException(mDirName);

			struct dirent* entry;
			struct stat attrib;
			while((entry = readdir(dir)) != NULL)
			{
				String fname = (mDirName + "/" + String(entry->d_name));
				stat(fname.c_str(), &attrib);
				if(S_ISREG(attrib.st_mode))
					addFile(fname, false);
				//else
				//	fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode);

			}//end while

			closedir(dir);
		}

		void removeAll()
		{
			KEvent* ke = NULL;

			// go through list removing each file and sending an event
			for(int i = 0; i < mChangeListCount; ++i)
			{
				ke = &mChangeList[i];
				//handleAction(name, Action::Delete);
				EntryStruct* entry = (EntryStruct*)ke->udata;

				handleAction(entry->mFilename, Actions::Delete);

				// delete
				close(ke->ident);
				delete((EntryStruct*)ke->udata);
			}
		}
	};

	void FileWatcherOSX::update()
	{
		int nev = 0;
		struct kevent event;

		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			WatchStruct* watch = iter->second;

			while((nev = kevent(mDescriptor, (KEvent*)&(watch->mChangeList), watch->mChangeListCount + 1, &event, 1, &mTimeOut)) != 0)
			{
				if(nev == -1)
					perror("kevent");
				else
				{
					EntryStruct* entry = 0;
					if((entry = (EntryStruct*)event.udata) != 0)
					{
						//fprintf(stderr, "File: %s -- ", (char*)entry->mFilename);

						if(event.fflags & NOTE_DELETE)
						{
							//fprintf(stderr, "File deleted\n");
							//watch->handleAction(entry->mFilename, Action::Delete);
							watch->removeFile(entry->mFilename);
						}
						if(event.fflags & NOTE_EXTEND ||
						   event.fflags & NOTE_WRITE ||
						   event.fflags & NOTE_ATTRIB)
						{
							//fprintf(stderr, "modified\n");
							//watch->rescan();
							struct stat attrib;
							stat(entry->mFilename, &attrib);
							entry->mModifiedTime = attrib.st_mtime;
							watch->handleAction(entry->mFilename, FW::Actions::Modified);
						}
					}
					else
					{
						//fprintf(stderr, "Dir: %s -- rescanning\n", watch->mDirName.c_str());
						watch->rescan();
					}
				}
			}
		}
	}

	//--------
	FileWatcherOSX::FileWatcherOSX()
	{
		mDescriptor = kqueue();
		mTimeOut.tv_sec = 0;
		mTimeOut.tv_nsec = 0;
	}

	//--------
	FileWatcherOSX::~FileWatcherOSX()
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			delete iter->second;
		}
		mWatches.clear();

		close(mDescriptor);
	}

	//--------
	WatchID FileWatcherOSX::addWatch(const String& directory, FileWatchListener* watcher)
	{
/*		int fd = open(directory.c_str(), O_RDONLY);
		if(fd == -1)
			perror("open");

		EV_SET(&change, fd, EVFILT_VNODE,
			   EV_ADD | EV_ENABLE | EV_ONESHOT,
			   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
			   0, (void*)"testing");
*/

		WatchStruct* watch = new WatchStruct(++mLastWatchID, directory, watcher);
		mWatches.insert(std::make_pair(mLastWatchID, watch));
		return mLastWatchID;
	}

	//--------
	void FileWatcherOSX::removeWatch(const String& directory)
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			if(directory == iter->second->mDirName)
			{
				removeWatch(iter->first);
				return;
			}
		}
	}

	//--------
	void FileWatcherOSX::removeWatch(WatchID watchid)
	{
		WatchMap::iterator iter = mWatches.find(watchid);

		if(iter == mWatches.end())
			return;

		WatchStruct* watch = iter->second;
		mWatches.erase(iter);

		//inotify_rm_watch(mFD, watchid);

		delete watch;
		watch = 0;
	}

	//--------
	void FileWatcherOSX::handleAction(WatchStruct* watch, const String& filename, unsigned long action)
	{
	}

};//namespace FW

#endif//FILEWATCHER_PLATFORM_KQUEUE
<|endoftext|>