<commit_before>//
// Created by k.leyfer on 27.09.2017.
//

#include <sstream>
#include <sys/stat.h>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

#include "MotionFileWriter.h"
#include "../dirty/common/logging.h"

std::string MotionFileWriter::getFileName(nsecs_t when)
{
  std::stringstream fileNameStream;
  fileNameStream << logDirAbsPath << "/" << fileBasename << "_" << when << ".log";
  return fileNameStream.str();
}

void MotionFileWriter::checkFile(nsecs_t when)
{
  if (currentLogFile == NULL)
  {
    struct stat st;
    if (stat(logDirAbsPath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode))
    {
      LOGV("Dir for gesture data not found, create one");
      if (mkdir(logDirAbsPath.c_str(), 0755) == -1)
      {
        LOGV("Unable to create dir: %s!", strerror(errno));
        return;
      }
    }

    currentLogFile = fopen(getFileName(when).c_str(), "a+");
  }
  else if (writtenGestures > maxWrittenGestures)
  {
    LOGV("Gestures limit exceeded, create new file");
    if (fclose(currentLogFile) != 0)
    {
      LOGV("Unable to close file, try next time");
    }
    else
    {
      currentLogFile = fopen(getFileName(when).c_str(), "a+");
      writtenGestures = 0;
    }
  }
}

void MotionFileWriter::writeMotionEvent(nsecs_t when, int action, int32_t changedId,
                                        uint32_t numPointers,
                                        const PointerCoords* coords,
                                        const PointerProperties* properties)
{
  checkFile(when);
  const char* actionStr;
  switch (action)
  {
    case AMOTION_EVENT_ACTION_DOWN:
      actionStr = "Down";
      break;
    case AMOTION_EVENT_ACTION_POINTER_DOWN:
      actionStr = "Pointer down";
      break;
    case AMOTION_EVENT_ACTION_MOVE:
      actionStr = "Move";
      break;
    case AMOTION_EVENT_ACTION_POINTER_UP:
      actionStr = "Pointer up";
      break;
    case AMOTION_EVENT_ACTION_UP:
      actionStr = "Up";
      break;
    default:
      actionStr = "Unknown";
  }

  fprintf(currentLogFile, "{\"ts\": %llu,"
              "\"prefix\": \"%s\","
              "\"pointer_count\": %d,"
              "\"changed_id\": %d,"
              "\"pointers\": [",
          when, actionStr, numPointers, changedId);

  for (int i = 0; i < numPointers; ++i)
  {
    fprintf(currentLogFile, "{\"id\": %d, \"x\": %f, \"y\": %f, \"pressure\": %f}",
            properties[i].id, coords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
            coords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
            coords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));

    if (i != numPointers - 1)
    {
      fputs(",", currentLogFile);
    }
  }

  fputs("]}\n", currentLogFile);
  writtenGestures++;
  fflush(currentLogFile);
}

int MotionFileWriter::runChildProcess(const char* path, const char** args, int* inFd, int* outFd)
{
  int pipeIn[2];
  int pipeOut[2];

  if (pipe(pipeIn) == -1)
  {
    LOGV("Unable to pipe in: %s!", strerror(errno));
    return -1;
  }

  if (pipe(pipeOut) == -1)
  {
    LOGV("Unable to pipe out: %s!", strerror(errno));
    return -1;
  }

  int pid = fork();
  if (pid == -1)
  {
    LOGV("Unable to fork: %s!", strerror(errno));
    return -1;
  }

  if (pid == 0)
  {
    dup2(pipeIn[0], STDIN_FILENO);
    dup2(pipeOut[1], STDOUT_FILENO);
    dup2(pipeOut[1], STDERR_FILENO);

    if (execv(path, (char* const*) args) == -1)
    {
      LOGV("Unable to execute %s: %s!", path, strerror(errno));
    }

    exit(-1);
  }
  else
  {
    *inFd = pipeIn[1];
    *outFd = pipeOut[0];
    return 0;
  }
}

std::string MotionFileWriter::parseFocusedWindowString(const char* focusedWindowString)
{
//  mCurrentFocus=Window{d192c75 u0 StatusBar}
//  or
//  mCurrentFocus=null
//  or
//  mCurrentFocus=Window{42d3cd18 u0 com.cooee.phenix/com.cooee.phenix.Launcher}
//  or
//  mCurrentFocus=Window{42310f20 com.test/com.test.MainActivity paused=false}
  char window[4096];
  memset(window, 0, 4096);
  const char* windowIndex = strstr(focusedWindowString, "Window{");
  if (windowIndex != NULL)
  {
    printf("here: %d %s!\n", __LINE__, focusedWindowString);
    const char* u0Index = NULL;
    if ((u0Index = strstr(focusedWindowString, "u0")) != NULL)
    {
      int scanned = -1;
      printf("here: %d!\n", __LINE__);
      if ((scanned = sscanf(u0Index, "u0 %s", window)) == 1)
      {
        printf("here: %d!\n", __LINE__);
        char* index;
        if ((index = strrchr(window, '}')) != NULL)
        {
          printf("here: %d!\n", __LINE__);
          *index = '\0';
        }

        printf("here: %d!\n", __LINE__);
        return std::string(window);
      }
      else
      {
        printf("sscanf returned %d: %s\n", scanned, strerror(errno));
      }
    }
    else
    {
      printf("here: %d!\n", __LINE__);
      long addr = -1;
      if (sscanf(windowIndex, "Window{%lx %s", &addr, window) == 2)
      {
        printf("here: %d!\n", __LINE__);
        char* index;
        if ((index = strrchr(window, '}')) != NULL)
        {
          printf("here: %d!\n", __LINE__);
          *index = '\0';
        }

        printf("here: %d!\n", __LINE__);
        return std::string(window);
      }
    }
  }

  return std::string("null");
}

std::string MotionFileWriter::findCurrentFocusWindow()
{
  int in = -1, out = -1;
  const char* path = "/system/bin/dumpsys";
  const char* args[] = {"/system/bin/dumpsys", "window", "windows", (char*) NULL};
  int pid = runChildProcess(path, args, &in, &out);
  if (pid == -1)
  {
    LOGV("Unable to launch dumpsys");
    return std::string("");
  }

  char buf[4096];
  char c;
  int readed = 0;

  int status = 0;
  if (waitpid(pid, &status, 0) == -1)
  {
    LOGV("Unable to wait for process: %s!", strerror(errno));
    close(in);
    close(out);
    return std::string("");
  }

  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
  {
    LOGV("Abnormal process exit!");
  }

  while (read(out, &c, 1) == 1)
  {
    buf[readed++] = c;
    if (c == '\n')
    {
      buf[readed] = '\0';
      readed = 0;

      if (strstr(buf, "mCurrentFocus") != NULL)
      {
        close(in);
        close(out);
        return parseFocusedWindowString(buf);
      }
    }
  }

  close(in);
  close(out);
  return std::string("");
}

void MotionFileWriter::writeCurrentFocusWindow(nsecs_t when)
{
  checkFile(when);
  std::string window = findCurrentFocusWindow();
  if (!window.empty())
  {
    fprintf(currentLogFile, "{\"ts\": %llu, \"window\": \"%s\"}\n", when, window.c_str());
    fflush(currentLogFile);
  }
}

MotionFileWriter::MotionFileWriter(std::string logDir) : logDirAbsPath(logDir),
                                                         currentLogFile(NULL),
                                                         writtenGestures(0),
                                                         fileBasename("touch_event_data"),
                                                         maxWrittenGestures(1000)
{}

MotionFileWriter::~MotionFileWriter()
{
  if (currentLogFile != NULL)
  {
    fclose(currentLogFile);
    currentLogFile = NULL;
  }
}
<commit_msg>Close extra pipes.<commit_after>//
// Created by k.leyfer on 27.09.2017.
//

#include <sstream>
#include <sys/stat.h>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

#include "MotionFileWriter.h"
#include "../dirty/common/logging.h"

std::string MotionFileWriter::getFileName(nsecs_t when)
{
  std::stringstream fileNameStream;
  fileNameStream << logDirAbsPath << "/" << fileBasename << "_" << when << ".log";
  return fileNameStream.str();
}

void MotionFileWriter::checkFile(nsecs_t when)
{
  if (currentLogFile == NULL)
  {
    struct stat st;
    if (stat(logDirAbsPath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode))
    {
      LOGV("Dir for gesture data not found, create one");
      if (mkdir(logDirAbsPath.c_str(), 0755) == -1)
      {
        LOGV("Unable to create dir: %s!", strerror(errno));
        return;
      }
    }

    currentLogFile = fopen(getFileName(when).c_str(), "a+");
  }
  else if (writtenGestures > maxWrittenGestures)
  {
    LOGV("Gestures limit exceeded, create new file");
    if (fclose(currentLogFile) != 0)
    {
      LOGV("Unable to close file, try next time");
    }
    else
    {
      currentLogFile = fopen(getFileName(when).c_str(), "a+");
      writtenGestures = 0;
    }
  }
}

void MotionFileWriter::writeMotionEvent(nsecs_t when, int action, int32_t changedId,
                                        uint32_t numPointers,
                                        const PointerCoords* coords,
                                        const PointerProperties* properties)
{
  checkFile(when);
  const char* actionStr;
  switch (action)
  {
    case AMOTION_EVENT_ACTION_DOWN:
      actionStr = "Down";
      break;
    case AMOTION_EVENT_ACTION_POINTER_DOWN:
      actionStr = "Pointer down";
      break;
    case AMOTION_EVENT_ACTION_MOVE:
      actionStr = "Move";
      break;
    case AMOTION_EVENT_ACTION_POINTER_UP:
      actionStr = "Pointer up";
      break;
    case AMOTION_EVENT_ACTION_UP:
      actionStr = "Up";
      break;
    default:
      actionStr = "Unknown";
  }

  fprintf(currentLogFile, "{\"ts\": %llu,"
              "\"prefix\": \"%s\","
              "\"pointer_count\": %d,"
              "\"changed_id\": %d,"
              "\"pointers\": [",
          when, actionStr, numPointers, changedId);

  for (int i = 0; i < numPointers; ++i)
  {
    fprintf(currentLogFile, "{\"id\": %d, \"x\": %f, \"y\": %f, \"pressure\": %f}",
            properties[i].id, coords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
            coords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
            coords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));

    if (i != numPointers - 1)
    {
      fputs(",", currentLogFile);
    }
  }

  fputs("]}\n", currentLogFile);
  writtenGestures++;
  fflush(currentLogFile);
}

int MotionFileWriter::runChildProcess(const char* path, const char** args, int* inFd, int* outFd)
{
  int pipeIn[2];
  int pipeOut[2];

  if (pipe(pipeIn) == -1)
  {
    LOGV("Unable to pipe in: %s!", strerror(errno));
    return -1;
  }

  if (pipe(pipeOut) == -1)
  {
    LOGV("Unable to pipe out: %s!", strerror(errno));
    return -1;
  }

  int pid = fork();
  if (pid == -1)
  {
    LOGV("Unable to fork: %s!", strerror(errno));
    return -1;
  }

  if (pid == 0)
  {
    dup2(pipeIn[0], STDIN_FILENO);
    dup2(pipeOut[1], STDOUT_FILENO);
    dup2(pipeOut[1], STDERR_FILENO);

    if (execv(path, (char* const*) args) == -1)
    {
      LOGV("Unable to execute %s: %s!", path, strerror(errno));
    }

    exit(-1);
  }
  else
  {
    close(pipeOut[1]);
    close(pipeIn[0]);
    *inFd = pipeIn[1];
    *outFd = pipeOut[0];
    return 0;
  }
}

std::string MotionFileWriter::parseFocusedWindowString(const char* focusedWindowString)
{
//  mCurrentFocus=Window{d192c75 u0 StatusBar}
//  or
//  mCurrentFocus=null
//  or
//  mCurrentFocus=Window{42d3cd18 u0 com.cooee.phenix/com.cooee.phenix.Launcher}
//  or
//  mCurrentFocus=Window{42310f20 com.test/com.test.MainActivity paused=false}
  char window[4096];
  memset(window, 0, 4096);
  const char* windowIndex = strstr(focusedWindowString, "Window{");
  if (windowIndex != NULL)
  {
    printf("here: %d %s!\n", __LINE__, focusedWindowString);
    const char* u0Index = NULL;
    if ((u0Index = strstr(focusedWindowString, "u0")) != NULL)
    {
      int scanned = -1;
      printf("here: %d!\n", __LINE__);
      if ((scanned = sscanf(u0Index, "u0 %s", window)) == 1)
      {
        printf("here: %d!\n", __LINE__);
        char* index;
        if ((index = strrchr(window, '}')) != NULL)
        {
          printf("here: %d!\n", __LINE__);
          *index = '\0';
        }

        printf("here: %d!\n", __LINE__);
        return std::string(window);
      }
      else
      {
        printf("sscanf returned %d: %s\n", scanned, strerror(errno));
      }
    }
    else
    {
      printf("here: %d!\n", __LINE__);
      long addr = -1;
      if (sscanf(windowIndex, "Window{%lx %s", &addr, window) == 2)
      {
        printf("here: %d!\n", __LINE__);
        char* index;
        if ((index = strrchr(window, '}')) != NULL)
        {
          printf("here: %d!\n", __LINE__);
          *index = '\0';
        }

        printf("here: %d!\n", __LINE__);
        return std::string(window);
      }
    }
  }

  return std::string("null");
}

std::string MotionFileWriter::findCurrentFocusWindow()
{
  int in = -1, out = -1;
  const char* path = "/system/bin/dumpsys";
  const char* args[] = {"/system/bin/dumpsys", "window", "windows", (char*) NULL};
  int pid = runChildProcess(path, args, &in, &out);
  if (pid == -1)
  {
    LOGV("Unable to launch dumpsys");
    return std::string("");
  }

  char buf[4096];
  char c;
  int readed = 0;

  int status = 0;
  if (waitpid(pid, &status, 0) == -1)
  {
    LOGV("Unable to wait for process: %s!", strerror(errno));
    close(in);
    close(out);
    return std::string("");
  }

  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
  {
    LOGV("Abnormal process exit!");
  }

  while (read(out, &c, 1) == 1)
  {
    buf[readed++] = c;
    if (c == '\n')
    {
      buf[readed] = '\0';
      readed = 0;

      if (strstr(buf, "mCurrentFocus") != NULL)
      {
        close(in);
        close(out);
        return parseFocusedWindowString(buf);
      }
    }
  }

  close(in);
  close(out);
  return std::string("");
}

void MotionFileWriter::writeCurrentFocusWindow(nsecs_t when)
{
  checkFile(when);
  std::string window = findCurrentFocusWindow();
  if (!window.empty())
  {
    fprintf(currentLogFile, "{\"ts\": %llu, \"window\": \"%s\"}\n", when, window.c_str());
    fflush(currentLogFile);
  }
}

MotionFileWriter::MotionFileWriter(std::string logDir) : logDirAbsPath(logDir),
                                                         currentLogFile(NULL),
                                                         writtenGestures(0),
                                                         fileBasename("touch_event_data"),
                                                         maxWrittenGestures(1000)
{}

MotionFileWriter::~MotionFileWriter()
{
  if (currentLogFile != NULL)
  {
    fclose(currentLogFile);
    currentLogFile = NULL;
  }
}
<|endoftext|>


<commit_before>#ifndef _SDD_MEM_CACHE_HH_
#define _SDD_MEM_CACHE_HH_

#include <algorithm> // count_if, for_each, nth_element
#include <cstdint>   // uint32_t
#include <forward_list>
#include <numeric>   // accumulate
#include <tuple>
#include <utility>   // forward
#include <vector>

#include <boost/intrusive/unordered_set.hpp>

#include "sdd/util/packed.hh"

namespace sdd { namespace mem {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Used by cache to know if an operation should be cached or not.
///
/// A filter should always return the same result for the same operation.
template <typename T, typename... Filters>
struct apply_filters;

template <typename T>
struct apply_filters<T>
{
  constexpr
  bool
  operator()(const T&)
  const
  {
    return true;
  }
};

template <typename T, typename Filter, typename... Filters>
struct apply_filters<T, Filter, Filters...>
{
  bool
  operator()(const T& op)
  const
  {
    return Filter()(op) ? apply_filters<T, Filters...>()(op) : false;
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief The statistics of a cache.
///
/// A statistic is made of several rounds: each time a cache is cleaned up, a new round is
/// created. Thus, one can have detailed statistics to see how well the cache performed.
struct cache_statistics
{
  /// @internal
  /// @brief Statistic between two cleanups.
  struct round
  {
    round()
      : hits(0)
      , misses(0)
      , filtered(0)
    {
    }

    /// @brief The number of hits in a round.
    std::size_t hits;

    /// @brief The number of misses in a round.
    std::size_t misses;

    /// @brief The number of filtered entries in a round.
    std::size_t filtered;
  };

  /// @brief The list of all rounds.
  std::forward_list<round> rounds;

  /// @brief Default constructor.
  cache_statistics()
    : rounds(1)
  {
  }

  /// @brief Get the number of rounds.
  std::size_t
  size()
  const noexcept
  {
    return std::count_if(rounds.begin(), rounds.end(), [](const round&){return true;});
  }

  /// @brief Get the number of performed cleanups.
  std::size_t
  cleanups()
  const noexcept
  {
    return size() - 1;
  }

  round
  total()
  const noexcept
  {
    return std::accumulate( rounds.begin(), rounds.end(), round()
                          , [&](const round& acc, const round& r) -> round
                               {
                                 round res;
                                 res.hits = acc.hits + r.hits;
                                 res.misses = acc.misses + r.misses;
                                 res.filtered = acc.filtered + r.filtered;
                                 return res;
                               }
                          );
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief  A generic cache.
/// @tparam Operation is the operation type.
/// @tparam EvaluationError is the exception that the evaluation of an Operation can throw.
/// @tparam Filters is a list of filters that reject some operations.
///
/// It uses the LFU strategy to cleanup old entries.
template <typename Context, typename Operation, typename EvaluationError, typename... Filters>
class cache
{
  // Can't copy a cache.
  cache(const cache&) = delete;
  cache* operator=(const cache&) = delete;

private:

  /// @brief The type of the context of this cache.
  typedef Context context_type;

  /// @brief The type of the result of an operation stored in the cache.
  typedef typename Operation::result_type result_type;

  /// @brief Faster, unsafe mode for Boost.Intrusive.
  typedef boost::intrusive::link_mode<boost::intrusive::normal_link> link_mode;

  /// @brief Associate an operation to its result into the cache.
  ///
  /// The operation acts as a key and the associated result is the value counterpart.
  struct
#if defined __clang__
  LIBSDD_ATTRIBUTE_PACKED
#endif
  cache_entry
    : public boost::intrusive::unordered_set_base_hook<link_mode>
  {
    // Can't copy a cache_entry.
    cache_entry(const cache_entry&) = delete;
    cache_entry& operator=(const cache_entry&) = delete;

    /// @brief The cached operation.
    const Operation operation;

    /// @brief The result of the evaluation of operation.
    const result_type result;

    /// @brief Count the number of times this entry has been accessed.
    ///
    /// Used by the LFU cache cleanup strategy.
    std::uint32_t nb_hits_;

    /// brief The 'in use' bit position in nb_hits_.
    static constexpr std::uint32_t in_use_bit = (1u << 31);

    /// @brief Constructor.
    template <typename... Args>
    cache_entry(Operation&& op, Args&&... args)
      : operation(std::move(op))
      , result(std::forward<Args>(args)...)
      , nb_hits_(in_use_bit) // initially in use
    {
    }

    /// @brief Cache entries are only compared using their operations.
    bool
    operator==(const cache_entry& other)
    const noexcept
    {
      return operation == other.operation;
    }

    /// @brief Get the number of hits, with the 'in use' bit set or not.
    unsigned int
    raw_nb_hits()
    const noexcept
    {
      return nb_hits_;
    }

    /// @brief Set this cache entry to a 'not in use' and 'never used' state.
    void
    reset()
    noexcept
    {
      nb_hits_ = 0;
    }

    /// @brief Increment the number of hits.
    void
    increment_nb_hits()
    noexcept
    {
      ++nb_hits_;
    }

    /// @brief Set this cache entry to a 'not in use' state.
    void
    reset_in_use()
    noexcept
    {
      nb_hits_ &= ~in_use_bit;
    }

    /// @brief Set this cache entry to an 'in use' state.
    bool
    in_use()
    const noexcept
    {
      return nb_hits_ & in_use_bit;
    }
  };

  /// @brief Hash a cache_entry.
  ///
  /// We only use the Operation part of a cache_entry to find it in the cache, in order
  /// to manage the set that stores cache entries as a map.
  struct hash_key
  {
    std::size_t
    operator()(const cache_entry& x)
    const noexcept
    {
      return std::hash<Operation>()(x.operation);
    }
  };

  /// @brief This cache's context.
  context_type& cxt_;

  /// @brief The cache name.
  const std::string name_;

  /// @brief The maximum size this cache is authorized to grow to.
  const std::size_t max_size_;

  /// @brief We use Boost.Intrusive to store the cache entries.
  typedef typename boost::intrusive::make_unordered_set< cache_entry
                                                       , boost::intrusive::hash<hash_key>
                                                       >::type
          set_type;

  /// @brief Needed to use Boost.Intrusive.
  typedef typename set_type::bucket_type bucket_type;

  /// @brief Needed to use Boost.Intrusive.
  typedef typename set_type::bucket_traits bucket_traits;

  /// @brief Boost.Intrusive requires us to manage ourselves its buckets.
  bucket_type* buckets_;

  /// @brief The actual storage of caches entries.
  set_type* set_;

  /// @brief The statistics of this cache.
  cache_statistics stats_;

public:

  /// @brief Construct a cache.
  /// @param context This cache's context.
  /// @param name Give a name to this cache.
  /// @param size tells how many cache entries are keeped in the cache.
  ///
  /// When the maximal size is reached, a cleanup is launched: half of the cache is removed,
  /// using an LFU strategy. This cache will never perform a rehash, therefore it allocates
  /// all the memory it needs at its construction.
  cache(context_type& context, const std::string& name, std::size_t size)
    : cxt_(context)
    , name_(name)
    , max_size_(2/*set_type::suggested_upper_bucket_count(size)*/)
    , buckets_(new bucket_type[max_size_])
    , set_(new set_type(bucket_traits(buckets_, max_size_)))
    , stats_()
  {
  }

  /// @brief Destructor.
  ~cache()
  {
    clear();
    delete set_;
    delete[] buckets_;
  }

  /// @brief Cache lookup.
  result_type
  operator()(Operation&& op)
  {
    // Check if the current operation should not be cached.
    if (not apply_filters<Operation, Filters...>()(op))
    {
      ++stats_.rounds.front().filtered;
      try
      {
        return op(cxt_);
      }
      catch (EvaluationError& e)
      {
        --stats_.rounds.front().filtered;
        e.add_step(std::move(op));
        throw;
      }
    }

    // Lookup for op.
    typename set_type::insert_commit_data commit_data;
    auto insertion = set_->insert_check( op
                                       , std::hash<Operation>()
                                       , [](const Operation& lhs, const cache_entry& rhs)
                                           {return lhs == rhs.operation;}
                                       , commit_data);

    // Check if op has already been computed.
    if (not insertion.second)
    {
      ++stats_.rounds.front().hits;
      insertion.first->increment_nb_hits();
      return insertion.first->result;
    }

    ++stats_.rounds.front().misses;

    // Remove half of the cache if it's full.
    if (set_->size() >= max_size_)
    {
      cleanup();
    }

    try
    {
      cache_entry* entry = new cache_entry(std::move(op), op(cxt_));
      // A cache entry is constructed with the 'in use' bit set.
      entry->reset_in_use();
      set_->insert_commit(*entry, commit_data); // doesn't throw
      return entry->result;
    }
    catch (EvaluationError& e)
    {
      --stats_.rounds.front().misses;
      e.add_step(std::move(op));
      throw;
    }
  }

  /// @brief Remove half of the cache (LFU strategy).
  void
  cleanup()
  {
    typedef typename set_type::iterator iterator;

    stats_.rounds.emplace_front(cache_statistics::round());

    std::vector<iterator> vec;
    vec.reserve(set_->size());

    for (auto cit = set_->begin(); cit != set_->end(); ++cit)
    {
      vec.push_back(cit);
    }

    // We remove an half of the cache.
    const std::size_t cut_size = vec.size() / 2;

    // Find the median of the number of hits.
    std::nth_element( vec.begin(), vec.begin() + cut_size, vec.end()
                    , [](iterator lhs, iterator rhs)
                        {
                          // We sort using the raw nb_hits counter, with the 'in use' possibily bit
                          // set. As this is the higher bit, the value of nb_hits may be large.
                          // Thus, it's very unlikely that cache entries currently in use will be
                          // put below the median.
                          return lhs->raw_nb_hits() < rhs->raw_nb_hits();
                        });

    // Delete all cache entries with a number of entries smaller than the median.
    std::for_each( vec.begin(), vec.begin() + cut_size
                 , [&](iterator cit)
                      {
                        if (not cit->in_use())
                        {
                          const cache_entry* x = &*cit;
                          set_->erase(cit);
                          delete x;
                        }
                      });

    // Reset the number of hits of all remaining cache entries.
    std::for_each(vec.begin() + cut_size, vec.end(), [](iterator it){it->reset();});
  }

  /// @brief Remove all entries of the cache.
  void
  clear()
  noexcept
  {
    set_->clear_and_dispose([](cache_entry* x){delete x;});
  }

  /// @brief Get the number of cached operations.
  std::size_t
  size()
  const noexcept
  {
    return set_->size();
  }

  /// @brief Get the statistics of this cache.
  const cache_statistics&
  statistics()
  const noexcept
  {
    return stats_;
  }

  /// @brief Get this cache's name
  const std::string&
  name()
  const noexcept
  {
    return name_;
  }
};

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::mem

#endif // _SDD_MEM_CACHE_HH_
<commit_msg>Remove constant for test.<commit_after>#ifndef _SDD_MEM_CACHE_HH_
#define _SDD_MEM_CACHE_HH_

#include <algorithm> // count_if, for_each, nth_element
#include <cstdint>   // uint32_t
#include <forward_list>
#include <numeric>   // accumulate
#include <tuple>
#include <utility>   // forward
#include <vector>

#include <boost/intrusive/unordered_set.hpp>

#include "sdd/util/packed.hh"

namespace sdd { namespace mem {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Used by cache to know if an operation should be cached or not.
///
/// A filter should always return the same result for the same operation.
template <typename T, typename... Filters>
struct apply_filters;

template <typename T>
struct apply_filters<T>
{
  constexpr
  bool
  operator()(const T&)
  const
  {
    return true;
  }
};

template <typename T, typename Filter, typename... Filters>
struct apply_filters<T, Filter, Filters...>
{
  bool
  operator()(const T& op)
  const
  {
    return Filter()(op) ? apply_filters<T, Filters...>()(op) : false;
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief The statistics of a cache.
///
/// A statistic is made of several rounds: each time a cache is cleaned up, a new round is
/// created. Thus, one can have detailed statistics to see how well the cache performed.
struct cache_statistics
{
  /// @internal
  /// @brief Statistic between two cleanups.
  struct round
  {
    round()
      : hits(0)
      , misses(0)
      , filtered(0)
    {
    }

    /// @brief The number of hits in a round.
    std::size_t hits;

    /// @brief The number of misses in a round.
    std::size_t misses;

    /// @brief The number of filtered entries in a round.
    std::size_t filtered;
  };

  /// @brief The list of all rounds.
  std::forward_list<round> rounds;

  /// @brief Default constructor.
  cache_statistics()
    : rounds(1)
  {
  }

  /// @brief Get the number of rounds.
  std::size_t
  size()
  const noexcept
  {
    return std::count_if(rounds.begin(), rounds.end(), [](const round&){return true;});
  }

  /// @brief Get the number of performed cleanups.
  std::size_t
  cleanups()
  const noexcept
  {
    return size() - 1;
  }

  round
  total()
  const noexcept
  {
    return std::accumulate( rounds.begin(), rounds.end(), round()
                          , [&](const round& acc, const round& r) -> round
                               {
                                 round res;
                                 res.hits = acc.hits + r.hits;
                                 res.misses = acc.misses + r.misses;
                                 res.filtered = acc.filtered + r.filtered;
                                 return res;
                               }
                          );
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief  A generic cache.
/// @tparam Operation is the operation type.
/// @tparam EvaluationError is the exception that the evaluation of an Operation can throw.
/// @tparam Filters is a list of filters that reject some operations.
///
/// It uses the LFU strategy to cleanup old entries.
template <typename Context, typename Operation, typename EvaluationError, typename... Filters>
class cache
{
  // Can't copy a cache.
  cache(const cache&) = delete;
  cache* operator=(const cache&) = delete;

private:

  /// @brief The type of the context of this cache.
  typedef Context context_type;

  /// @brief The type of the result of an operation stored in the cache.
  typedef typename Operation::result_type result_type;

  /// @brief Faster, unsafe mode for Boost.Intrusive.
  typedef boost::intrusive::link_mode<boost::intrusive::normal_link> link_mode;

  /// @brief Associate an operation to its result into the cache.
  ///
  /// The operation acts as a key and the associated result is the value counterpart.
  struct
#if defined __clang__
  LIBSDD_ATTRIBUTE_PACKED
#endif
  cache_entry
    : public boost::intrusive::unordered_set_base_hook<link_mode>
  {
    // Can't copy a cache_entry.
    cache_entry(const cache_entry&) = delete;
    cache_entry& operator=(const cache_entry&) = delete;

    /// @brief The cached operation.
    const Operation operation;

    /// @brief The result of the evaluation of operation.
    const result_type result;

    /// @brief Count the number of times this entry has been accessed.
    ///
    /// Used by the LFU cache cleanup strategy.
    std::uint32_t nb_hits_;

    /// brief The 'in use' bit position in nb_hits_.
    static constexpr std::uint32_t in_use_bit = (1u << 31);

    /// @brief Constructor.
    template <typename... Args>
    cache_entry(Operation&& op, Args&&... args)
      : operation(std::move(op))
      , result(std::forward<Args>(args)...)
      , nb_hits_(in_use_bit) // initially in use
    {
    }

    /// @brief Cache entries are only compared using their operations.
    bool
    operator==(const cache_entry& other)
    const noexcept
    {
      return operation == other.operation;
    }

    /// @brief Get the number of hits, with the 'in use' bit set or not.
    unsigned int
    raw_nb_hits()
    const noexcept
    {
      return nb_hits_;
    }

    /// @brief Set this cache entry to a 'not in use' and 'never used' state.
    void
    reset()
    noexcept
    {
      nb_hits_ = 0;
    }

    /// @brief Increment the number of hits.
    void
    increment_nb_hits()
    noexcept
    {
      ++nb_hits_;
    }

    /// @brief Set this cache entry to a 'not in use' state.
    void
    reset_in_use()
    noexcept
    {
      nb_hits_ &= ~in_use_bit;
    }

    /// @brief Set this cache entry to an 'in use' state.
    bool
    in_use()
    const noexcept
    {
      return nb_hits_ & in_use_bit;
    }
  };

  /// @brief Hash a cache_entry.
  ///
  /// We only use the Operation part of a cache_entry to find it in the cache, in order
  /// to manage the set that stores cache entries as a map.
  struct hash_key
  {
    std::size_t
    operator()(const cache_entry& x)
    const noexcept
    {
      return std::hash<Operation>()(x.operation);
    }
  };

  /// @brief This cache's context.
  context_type& cxt_;

  /// @brief The cache name.
  const std::string name_;

  /// @brief The maximum size this cache is authorized to grow to.
  const std::size_t max_size_;

  /// @brief We use Boost.Intrusive to store the cache entries.
  typedef typename boost::intrusive::make_unordered_set< cache_entry
                                                       , boost::intrusive::hash<hash_key>
                                                       >::type
          set_type;

  /// @brief Needed to use Boost.Intrusive.
  typedef typename set_type::bucket_type bucket_type;

  /// @brief Needed to use Boost.Intrusive.
  typedef typename set_type::bucket_traits bucket_traits;

  /// @brief Boost.Intrusive requires us to manage ourselves its buckets.
  bucket_type* buckets_;

  /// @brief The actual storage of caches entries.
  set_type* set_;

  /// @brief The statistics of this cache.
  cache_statistics stats_;

public:

  /// @brief Construct a cache.
  /// @param context This cache's context.
  /// @param name Give a name to this cache.
  /// @param size tells how many cache entries are keeped in the cache.
  ///
  /// When the maximal size is reached, a cleanup is launched: half of the cache is removed,
  /// using an LFU strategy. This cache will never perform a rehash, therefore it allocates
  /// all the memory it needs at its construction.
  cache(context_type& context, const std::string& name, std::size_t size)
    : cxt_(context)
    , name_(name)
    , max_size_(set_type::suggested_upper_bucket_count(size))
    , buckets_(new bucket_type[max_size_])
    , set_(new set_type(bucket_traits(buckets_, max_size_)))
    , stats_()
  {
  }

  /// @brief Destructor.
  ~cache()
  {
    clear();
    delete set_;
    delete[] buckets_;
  }

  /// @brief Cache lookup.
  result_type
  operator()(Operation&& op)
  {
    // Check if the current operation should not be cached.
    if (not apply_filters<Operation, Filters...>()(op))
    {
      ++stats_.rounds.front().filtered;
      try
      {
        return op(cxt_);
      }
      catch (EvaluationError& e)
      {
        --stats_.rounds.front().filtered;
        e.add_step(std::move(op));
        throw;
      }
    }

    // Lookup for op.
    typename set_type::insert_commit_data commit_data;
    auto insertion = set_->insert_check( op
                                       , std::hash<Operation>()
                                       , [](const Operation& lhs, const cache_entry& rhs)
                                           {return lhs == rhs.operation;}
                                       , commit_data);

    // Check if op has already been computed.
    if (not insertion.second)
    {
      ++stats_.rounds.front().hits;
      insertion.first->increment_nb_hits();
      return insertion.first->result;
    }

    ++stats_.rounds.front().misses;

    // Remove half of the cache if it's full.
    if (set_->size() >= max_size_)
    {
      cleanup();
    }

    try
    {
      cache_entry* entry = new cache_entry(std::move(op), op(cxt_));
      // A cache entry is constructed with the 'in use' bit set.
      entry->reset_in_use();
      set_->insert_commit(*entry, commit_data); // doesn't throw
      return entry->result;
    }
    catch (EvaluationError& e)
    {
      --stats_.rounds.front().misses;
      e.add_step(std::move(op));
      throw;
    }
  }

  /// @brief Remove half of the cache (LFU strategy).
  void
  cleanup()
  {
    typedef typename set_type::iterator iterator;

    stats_.rounds.emplace_front(cache_statistics::round());

    std::vector<iterator> vec;
    vec.reserve(set_->size());

    for (auto cit = set_->begin(); cit != set_->end(); ++cit)
    {
      vec.push_back(cit);
    }

    // We remove an half of the cache.
    const std::size_t cut_size = vec.size() / 2;

    // Find the median of the number of hits.
    std::nth_element( vec.begin(), vec.begin() + cut_size, vec.end()
                    , [](iterator lhs, iterator rhs)
                        {
                          // We sort using the raw nb_hits counter, with the 'in use' possibily bit
                          // set. As this is the higher bit, the value of nb_hits may be large.
                          // Thus, it's very unlikely that cache entries currently in use will be
                          // put below the median.
                          return lhs->raw_nb_hits() < rhs->raw_nb_hits();
                        });

    // Delete all cache entries with a number of entries smaller than the median.
    std::for_each( vec.begin(), vec.begin() + cut_size
                 , [&](iterator cit)
                      {
                        if (not cit->in_use())
                        {
                          const cache_entry* x = &*cit;
                          set_->erase(cit);
                          delete x;
                        }
                      });

    // Reset the number of hits of all remaining cache entries.
    std::for_each(vec.begin() + cut_size, vec.end(), [](iterator it){it->reset();});
  }

  /// @brief Remove all entries of the cache.
  void
  clear()
  noexcept
  {
    set_->clear_and_dispose([](cache_entry* x){delete x;});
  }

  /// @brief Get the number of cached operations.
  std::size_t
  size()
  const noexcept
  {
    return set_->size();
  }

  /// @brief Get the statistics of this cache.
  const cache_statistics&
  statistics()
  const noexcept
  {
    return stats_;
  }

  /// @brief Get this cache's name
  const std::string&
  name()
  const noexcept
  {
    return name_;
  }
};

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::mem

#endif // _SDD_MEM_CACHE_HH_
<|endoftext|>


<commit_before>/// @file
/// @copyright The code is licensed under the BSD License
///            <http://opensource.org/licenses/BSD-2-Clause>,
///            Copyright (c) 2012-2015 Alexandre Hamez.
/// @author Alexandre Hamez
#pragma once

#include <algorithm> // for_each
#include <iterator>  // iterator_traits
#include <utility>   // declval

#include <boost/optional.hpp>

namespace sdd { namespace hash {

/*------------------------------------------------------------------------------------------------*/

/// @brief Combine the hash value of x with seed.
///
/// Taken from <boost/functional/hash.hpp>. Sligthy modified to use std::hash<T> instead of
/// boost::hash_value().
template <typename T>
inline
void
hash_combine(std::size_t& seed, const T& x)
noexcept(noexcept(std::hash<T>()(x)))
{
  seed ^= std::hash<T>()(x) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

/*------------------------------------------------------------------------------------------------*/

/// @brief Hash a range.
template <typename InputIterator>
inline
void
hash_combine(std::size_t& seed, InputIterator cit, InputIterator cend)
noexcept(noexcept(
  hash_combine(seed, std::declval<typename std::iterator_traits<InputIterator>::value_type>())
))
{
  std::for_each(cit, cend, [&](const auto& v){hash_combine(seed, v);});
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
struct seed
{
  const std::size_t seed_;

  seed(std::size_t s = 0)
  noexcept
    : seed_(s)
  {}

  template <typename T>
  seed(const T& x)
  noexcept(noexcept(std::hash<T>()(x)))
    : seed_(std::hash<T>()(x))
  {}

  template <typename Cont>
  auto
  operator()(const Cont& cont)
  const noexcept
  {
    return cont(seed_);
  }

  operator std::size_t()
  const noexcept
  {
    return seed_;
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename T>
auto val(const T& x)
{
  return [&](std::size_t s) { hash_combine(s, x); return seed(s); };
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename T>
auto val(const boost::optional<T>& x)
noexcept
{
  return [&](std::size_t s) { if (x) hash_combine(s, *x); return seed(s); };
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename InputIterator>
auto
range(InputIterator begin, InputIterator end)
noexcept
{
  return [=](std::size_t s) { hash_combine(s, begin, end); return seed(s); };
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename Container>
auto
range(const Container& c)
noexcept
{
  return range(c.begin(), c.end());
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::hash
<commit_msg>noexcept specification<commit_after>/// @file
/// @copyright The code is licensed under the BSD License
///            <http://opensource.org/licenses/BSD-2-Clause>,
///            Copyright (c) 2012-2015 Alexandre Hamez.
/// @author Alexandre Hamez
#pragma once

#include <algorithm> // for_each
#include <iterator>  // iterator_traits
#include <utility>   // declval

#include <boost/optional.hpp>

namespace sdd { namespace hash {

/*------------------------------------------------------------------------------------------------*/

/// @brief Combine the hash value of x with seed.
///
/// Taken from <boost/functional/hash.hpp>. Sligthy modified to use std::hash<T> instead of
/// boost::hash_value().
template <typename T>
inline
void
hash_combine(std::size_t& seed, const T& x)
noexcept(noexcept(std::hash<T>()(x)))
{
  seed ^= std::hash<T>()(x) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

/*------------------------------------------------------------------------------------------------*/

/// @brief Hash a range.
template <typename InputIterator>
inline
void
hash_combine(std::size_t& seed, InputIterator cit, InputIterator cend)
noexcept(noexcept(
  hash_combine(seed, std::declval<typename std::iterator_traits<InputIterator>::value_type>())
))
{
  std::for_each(cit, cend, [&](const auto& v){hash_combine(seed, v);});
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
struct seed
{
  const std::size_t seed_;

  seed(std::size_t s = 0)
  noexcept
    : seed_(s)
  {}

  template <typename T>
  seed(const T& x)
  noexcept(noexcept(std::hash<T>()(x)))
    : seed_(std::hash<T>()(x))
  {}

  template <typename Cont>
  auto
  operator()(const Cont& cont)
  const noexcept
  {
    return cont(seed_);
  }

  operator std::size_t()
  const noexcept
  {
    return seed_;
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename T>
auto val(const T& x)
noexcept
{
  return [&](std::size_t s) { hash_combine(s, x); return seed(s); };
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename T>
auto val(const boost::optional<T>& x)
noexcept
{
  return [&](std::size_t s) { if (x) hash_combine(s, *x); return seed(s); };
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename InputIterator>
auto
range(InputIterator begin, InputIterator end)
noexcept
{
  return [=](std::size_t s) { hash_combine(s, begin, end); return seed(s); };
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
template <typename Container>
auto
range(const Container& c)
noexcept
{
  return range(c.begin(), c.end());
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::hash
<|endoftext|>


<commit_before>/*
 * catom2D1BlockCode.cpp
 *
 *  Created on: 12 avril 2013
 *      Author: ben
 */

#include <iostream>
#include <sstream>
#include "catom2D1BlockCode.h"
#include "scheduler.h"
#include "events.h"
//MODIF NICO
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace Catoms2D;

Catoms2D1BlockCode::Catoms2D1BlockCode(Catoms2DBlock *host):Catoms2DBlockCode(host) {
	cout << "Catoms2D1BlockCode constructor" << endl;
	scheduler = Catoms2D::getScheduler();
	catom2D = (Catoms2DBlock*)hostBlock;
}

Catoms2D1BlockCode::~Catoms2D1BlockCode() {
	cout << "Catoms2D1BlockCode destructor" << endl;
}

void Catoms2D1BlockCode::startup() {
	stringstream info;

	info << "Starting ";
	scheduler->trace(info.str(),hostBlock->blockId);
}

void Catoms2D1BlockCode::processLocalEvent(EventPtr pev) {
	int i;
	MessagePtr message;
	stringstream info;

	switch (pev->eventType) {
	case EVENT_NI_RECEIVE: {
	  message = (boost::static_pointer_cast<NetworkInterfaceReceiveEvent>(pev))->message;
	  P2PNetworkInterface * recv_interface = message->destinationInterface;
	}
	  break;
	}
}

Catoms2D::Catoms2DBlockCode* Catoms2D1BlockCode::buildNewBlockCode(Catoms2DBlock *host) {
	return(new Catoms2D1BlockCode(host));
}
<commit_msg>Silenced warnings from noneCatoms2D block code<commit_after>/*
 * catom2D1BlockCode.cpp
 *
 *  Created on: 12 avril 2013
 *      Author: ben
 */

#include <iostream>
#include <sstream>
#include "catom2D1BlockCode.h"
#include "scheduler.h"
#include "events.h"
//MODIF NICO
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace Catoms2D;

Catoms2D1BlockCode::Catoms2D1BlockCode(Catoms2DBlock *host):Catoms2DBlockCode(host) {
	cout << "Catoms2D1BlockCode constructor" << endl;
	scheduler = Catoms2D::getScheduler();
	catom2D = (Catoms2DBlock*)hostBlock;
}

Catoms2D1BlockCode::~Catoms2D1BlockCode() {
	cout << "Catoms2D1BlockCode destructor" << endl;
}

void Catoms2D1BlockCode::startup() {
	stringstream info;

	info << "Starting ";
	scheduler->trace(info.str(),hostBlock->blockId);
}

void Catoms2D1BlockCode::processLocalEvent(EventPtr pev) {
	// int i;
	MessagePtr message;
	stringstream info;

	switch (pev->eventType) {
	case EVENT_NI_RECEIVE: {
	  message = (boost::static_pointer_cast<NetworkInterfaceReceiveEvent>(pev))->message;
	  // P2PNetworkInterface * recv_interface = message->destinationInterface;
	}
	  break;
	}
}

Catoms2D::Catoms2DBlockCode* Catoms2D1BlockCode::buildNewBlockCode(Catoms2DBlock *host) {
	return(new Catoms2D1BlockCode(host));
}
<|endoftext|>


<commit_before>#include "serialworker.h"

#include <QSerialPort>
#include <QDebug>
#include <QTimer>
#include <QSettings>

SerialWorker::SerialWorker(CANFrameModel *model, QObject *parent) : QObject(parent)
{
    serial = NULL;
    rx_state = IDLE;
    rx_step = 0;
    buildFrame = new CANFrame;
    canModel = model;
    gotFrames = 0;
    ticker = NULL;
    elapsedTime = NULL;
    framesPerSec = 0;
    capturing = true;
    gotValidated = true;
    isAutoRestart = false;
    targetID = -1;

    readSettings();
}

SerialWorker::~SerialWorker()
{
    if (serial != NULL)
    {
        if (serial->isOpen())
        {
            serial->clear();
            serial->close();

        }
        serial->disconnect(); //disconnect all signals
        delete serial;
    }
    if (ticker != NULL) ticker->stop();
}

void SerialWorker::run()
{
    ticker = new QTimer;
    connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));

    elapsedTime = new QTime;
    elapsedTime->start();

    ticker->setInterval(250); //tick four times per second
    ticker->setSingleShot(false); //keep ticking
    ticker->start();
}

void SerialWorker::readSettings()
{
    QSettings settings;

    if (settings.value("Main/ValidateComm", true).toBool())
    {
        doValidation = true;
    }
    else doValidation = false;
    //doValidation=false;
}

void SerialWorker::setSerialPort(QSerialPortInfo *port)
{
    QSettings settings;

    currentPort = port;

    if (serial != NULL)
    {
        if (serial->isOpen())
        {
            serial->clear();
            serial->close();
        }
        serial->disconnect(); //disconnect all signals
        delete serial;
    }

    serial = new QSerialPort(*port);

    qDebug() << "Serial port name is " << port->portName();
    //serial->setBaudRate(10000000); //more speed! probably does nothing for USB serial
    serial->setDataBits(serial->Data8);
    serial->setFlowControl(serial->HardwareControl); //this is important though
    if (!serial->open(QIODevice::ReadWrite))
    {
        qDebug() << serial->errorString();
    }
    serial->setDataTerminalReady(true); //you do need to set these or the fan gets dirty
    serial->setRequestToSend(true);
    QByteArray output;
    output.append((char)0xE7); //this puts the device into binary comm mode
    output.append((char)0xE7);

    output.append((char)0xF1); //signal we want to issue a command
    output.append((char)0x06); //request canbus stats from the board

    output.append((char)0xF1); //another command to the GVRET
    output.append((char)0x07); //request device information

    output.append((char)0xF1);
    output.append((char)0x08); //setting singlewire mode
    if (settings.value("Main/SingleWireMode", false).toBool())
    {
        output.append((char)0x10); //signal that we do want single wire mode
    }
    else
    {
        output.append((char)0xFF); //signal we don't want single wire mode
    }

    output.append((char)0xF1); //yet another command
    output.append((char)0x09); //comm validation command

    serial->write(output);
    if (doValidation) connected = false;
        else connected = true;
    connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData()));
    if (doValidation) QTimer::singleShot(1000, this, SLOT(connectionTimeout()));
}

void SerialWorker::connectionTimeout()
{
    //one second after trying to connect are we actually connected?
    if (!connected) //no?
    {
        //then emit the the failure signal and see if anyone cares
        qDebug() << "Failed to connect to GVRET at that com port";
        ticker->stop();
        closeSerialPort(); //make sure it's properly closed anyway
        emit connectionFailure();
    }
}

void SerialWorker::readSerialData()
{
    QByteArray data = serial->readAll();
    unsigned char c;
    //qDebug() << (tr("Got data from serial. Len = %0").arg(data.length()));
    for (int i = 0; i < data.length(); i++)
    {
        c = data.at(i);
        procRXChar(c);
    }
}

void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
{
    QByteArray buffer;
    int c;
    int ID;
    CANFrame tempFrame = *frame;
    tempFrame.isReceived = false;

    //qDebug() << "Sending out frame with id " << frame->ID;

    //show our sent frames in the list too. This happens even if we're not connected.
    canModel->addFrame(tempFrame, false);
    gotFrames++;

    if (serial == NULL) return;
    if (!serial->isOpen()) return;
    if (!connected) return;

    ID = frame->ID;
    if (frame->extended) ID |= 1 << 31;

    buffer[0] = (char)0xF1; //start of a command over serial
    buffer[1] = 0; //command ID for sending a CANBUS frame
    buffer[2] = (unsigned char)(ID & 0xFF); //four bytes of ID LSB first
    buffer[3] = (unsigned char)(ID >> 8);
    buffer[4] = (unsigned char)(ID >> 16);
    buffer[5] = (unsigned char)(ID >> 24);
    buffer[6] = (unsigned char)(bus & 1);
    buffer[7] = (unsigned char)frame->len;
    for (c = 0; c < frame->len; c++)
    {
        buffer[8 + c] = frame->data[c];
    }
    buffer[8 + frame->len] = 0;

    //qDebug() << "writing " << buffer.length() << " bytes to serial port";
    serial->write(buffer);
}

//a simple way for another thread to pass us a bunch of frames to send.
//Don't get carried away here. The GVRET firmware only has finite
//buffers and besides, the other end will get buried in traffic.
void SerialWorker::sendFrameBatch(const QList<CANFrame> *frames)
{
    sendBulkMutex.lock();
    for (int i = 0; i < frames->length(); i++) sendFrame(&frames->at(i), frames->at(i).bus);
    sendBulkMutex.unlock();
}

void SerialWorker::updateBaudRates(int Speed1, int Speed2)
{
    QByteArray buffer;
    qDebug() << "Got signal to update bauds. 1: " << Speed1 <<" 2: " << Speed2;
    buffer[0] = (char)0xF1; //start of a command over serial
    buffer[1] = 5; //setup canbus
    buffer[2] = (unsigned char)(Speed1 & 0xFF); //four bytes of ID LSB first
    buffer[3] = (unsigned char)(Speed1 >> 8);
    buffer[4] = (unsigned char)(Speed1 >> 16);
    buffer[5] = (unsigned char)(Speed1 >> 24);
    buffer[6] = (unsigned char)(Speed2 & 0xFF); //four bytes of ID LSB first
    buffer[7] = (unsigned char)(Speed2 >> 8);
    buffer[8] = (unsigned char)(Speed2 >> 16);
    buffer[9] = (unsigned char)(Speed2 >> 24);
    buffer[10] = 0;
    if (serial == NULL) return;
    if (!serial->isOpen()) return;
    serial->write(buffer);
}

void SerialWorker::procRXChar(unsigned char c)
{
    switch (rx_state)
    {
    case IDLE:
        if (c == 0xF1) rx_state = GET_COMMAND;
        break;
    case GET_COMMAND:
        switch (c)
        {
        case 0: //receiving a can frame
            rx_state = BUILD_CAN_FRAME;
            rx_step = 0;
            break;
        case 1: //we don't accept time sync commands from the firmware
            rx_state = IDLE;
            break;
        case 2: //process a return reply for digital input states.
            rx_state = GET_DIG_INPUTS;
            rx_step = 0;
            break;
        case 3: //process a return reply for analog inputs
            rx_state = GET_ANALOG_INPUTS;
            break;
        case 4: //we set digital outputs we don't accept replies so nothing here.
            rx_state = IDLE;
            break;
        case 5: //we set canbus specs we don't accept replies.
            rx_state = IDLE;
            break;
        case 6: //get canbus parameters from GVRET
            rx_state = GET_CANBUS_PARAMS;
            rx_step = 0;
            break;
        case 7: //get device info
            rx_state = GET_DEVICE_INFO;
            rx_step = 0;
            break;
        case 9:
            gotValidated = true;
            //qDebug() << "Got validated";
            rx_state = IDLE;
            break;
        }
        break;
    case BUILD_CAN_FRAME:
        switch (rx_step)
        {
        case 0:
            buildFrame->timestamp = c;
            break;
        case 1:
            buildFrame->timestamp |= (uint)(c << 8);
            break;
        case 2:
            buildFrame->timestamp |= (uint)c << 16;
            break;
        case 3:
            buildFrame->timestamp |= (uint)c << 24;
            break;
        case 4:
            buildFrame->ID = c;
            break;
        case 5:
            buildFrame->ID |= c << 8;
            break;
        case 6:
            buildFrame->ID |= c << 16;
            break;
        case 7:
            buildFrame->ID |= c << 24;
            if ((buildFrame->ID & 1 << 31) == 1 << 31)
            {
                buildFrame->ID &= 0x7FFFFFFF;
                buildFrame->extended = true;
            }
            else buildFrame->extended = false;
            break;
        case 8:
            buildFrame->len = c & 0xF;
            if (buildFrame->len > 8) buildFrame->len = 8;
            buildFrame->bus = (c & 0xF0) >> 4;
            break;
        default:
            if (rx_step < buildFrame->len + 9)
            {
                buildFrame->data[rx_step - 9] = c;
            }
            else
            {
                rx_state = IDLE;
                rx_step = 0;
                //qDebug() << "emit from serial handler to main form id: " << buildFrame->ID;
                if (capturing)
                {
                    buildFrame->isReceived = true;
                    canModel->addFrame(*buildFrame, false);
                    gotFrames++;
                    if (buildFrame->ID == targetID) emit gotTargettedFrame(canModel->rowCount() - 1);
                }
            }
            break;
        }
        rx_step++;
        break;
    case GET_ANALOG_INPUTS: //get 9 bytes - 2 per analog input plus checksum
        switch (rx_step)
        {
        case 0:
            break;
        }
        rx_step++;
        break;
    case GET_DIG_INPUTS: //get two bytes. One for digital in status and one for checksum.
        switch (rx_step)
        {
        case 0:
            break;
        case 1:
            rx_state = IDLE;
            break;
        }
        rx_step++;
        break;
    case GET_CANBUS_PARAMS:
        switch (rx_step)
        {
        case 0:
            can0Enabled = c;
            break;
        case 1:
            can0Baud = c;
            break;
        case 2:
            can0Baud |= c << 8;
            break;
        case 3:
            can0Baud |= c << 16;
            break;
        case 4:
            can0Baud |= c << 24;
            break;
        case 5:
            can1Enabled = c;
            break;
        case 6:
            can1Baud = c;
            break;
        case 7:
            can1Baud |= c << 8;
            break;
        case 8:
            can1Baud |= c << 16;
            break;
        case 9:
            can1Baud |= c << 24;
            rx_state = IDLE;
            qDebug() << "Baud 0 = " << can0Baud;
            qDebug() << "Baud 1 = " << can1Baud;
            if (!can1Enabled) can1Baud = 0;
            if (!can0Enabled) can0Baud = 0;
            connected = true;
            emit connectionSuccess(can0Baud, can1Baud);
            break;
        }
        rx_step++;
        break;
    case GET_DEVICE_INFO:
        switch (rx_step)
        {
        case 0:
            deviceBuildNum = c;
            break;
        case 1:
            deviceBuildNum |= c << 8;
            break;
        case 2:
            break; //don't care about eeprom version
        case 3:
            break; //don't care about file type
        case 4:
            break; //don't care about whether it auto logs or not
        case 5:
            deviceSingleWireMode = c;
            rx_state = IDLE;
            qDebug() << "build num: " << deviceBuildNum;
            qDebug() << "single wire can: " << deviceSingleWireMode;
            emit deviceInfo(deviceBuildNum, deviceSingleWireMode);
            break;
        }
        rx_step++;
        break;
    case TIME_SYNC:
        rx_state = IDLE;
        break;
    case SET_DIG_OUTPUTS:
        rx_state = IDLE;
        break;
    case SETUP_CANBUS:
        rx_state = IDLE;
        break;
    case SET_SINGLEWIRE_MODE:
        rx_state = IDLE;
        break;

    }
}

void SerialWorker::handleTick()
{
    //qDebug() << "Tick!";

    if (connected)
    {
        if (!gotValidated)
        {
            if (serial == NULL) return;
            if (serial->isOpen()) //if it's still false we have a problem...
            {
                qDebug() << "Comm validation failed. ";
                closeSerialPort(); //start by stopping everything.
                QTimer::singleShot(500, this, SLOT(handleReconnect()));
                return;
            }
        }
    }

    framesPerSec += gotFrames * 1000 / elapsedTime->elapsed() - (framesPerSec / 4);
    elapsedTime->restart();
    emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties
    canModel->sendBulkRefresh(gotFrames);
    gotFrames = 0;
    if (doValidation && serial) sendCommValidation();
}

void SerialWorker::handleReconnect()
{
    qDebug() << "Automatically reopening the connection";
    setSerialPort(currentPort); //then go back through the re-init
}

void SerialWorker::sendCommValidation()
{
    QByteArray output;

    gotValidated = false;
    output.append((char)0xF1); //another command to the GVRET
    output.append((char)0x09); //request a reply to get validation
    serial->write(output);
}

//totally shuts down the whole thing
void SerialWorker::closeSerialPort()
{
    if (serial == NULL) return;
    if (serial->isOpen())
    {
        serial->clear();
        serial->close();
    }
    serial->disconnect();
    //do not stop the ticker here. It always stays running now.
    //ticker->stop();
    delete serial;
    serial = NULL;
}

void SerialWorker::stopFrameCapture()
{
    qDebug() << "Stopping frame capture";
    capturing = false;
}

void SerialWorker::startFrameCapture()
{
    qDebug() << "Starting up frame capture";
    capturing = true;
}

void SerialWorker::targetFrameID(int target)
{
    targetID = target;
}
<commit_msg>Slight changes to serialworker to make things run a bit more stable.<commit_after>#include "serialworker.h"

#include <QSerialPort>
#include <QDebug>
#include <QTimer>
#include <QSettings>

SerialWorker::SerialWorker(CANFrameModel *model, QObject *parent) : QObject(parent)
{
    serial = NULL;
    rx_state = IDLE;
    rx_step = 0;
    buildFrame = new CANFrame;
    canModel = model;
    gotFrames = 0;
    ticker = NULL;
    elapsedTime = NULL;
    framesPerSec = 0;
    capturing = true;
    gotValidated = true;
    isAutoRestart = false;
    targetID = -1;

    readSettings();
}

SerialWorker::~SerialWorker()
{
    if (serial != NULL)
    {
        if (serial->isOpen())
        {
            serial->clear();
            serial->close();

        }
        serial->disconnect(); //disconnect all signals
        delete serial;
    }
    if (ticker != NULL) ticker->stop();
}

void SerialWorker::run()
{
    ticker = new QTimer;
    connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));

    elapsedTime = new QTime;
    elapsedTime->start();

    ticker->setInterval(250); //tick four times per second
    ticker->setSingleShot(false); //keep ticking
    ticker->start();
}

void SerialWorker::readSettings()
{
    QSettings settings;

    if (settings.value("Main/ValidateComm", true).toBool())
    {
        doValidation = true;
    }
    else doValidation = false;
    //doValidation=false;
}

void SerialWorker::setSerialPort(QSerialPortInfo *port)
{
    QSettings settings;

    currentPort = port;

    if (serial != NULL)
    {
        if (serial->isOpen())
        {
            serial->clear();
            serial->close();
        }
        serial->disconnect(); //disconnect all signals
        delete serial;
    }

    serial = new QSerialPort(*port);

    qDebug() << "Serial port name is " << port->portName();
    //serial->setBaudRate(10000000); //more speed! probably does nothing for USB serial
    serial->setDataBits(serial->Data8);
    serial->setFlowControl(serial->HardwareControl); //this is important though
    if (!serial->open(QIODevice::ReadWrite))
    {
        qDebug() << serial->errorString();
    }
    serial->setDataTerminalReady(true); //you do need to set these or the fan gets dirty
    serial->setRequestToSend(true);
    QByteArray output;
    output.append((char)0xE7); //this puts the device into binary comm mode
    output.append((char)0xE7);

    output.append((char)0xF1); //signal we want to issue a command
    output.append((char)0x06); //request canbus stats from the board

    output.append((char)0xF1); //another command to the GVRET
    output.append((char)0x07); //request device information

    output.append((char)0xF1);
    output.append((char)0x08); //setting singlewire mode
    if (settings.value("Main/SingleWireMode", false).toBool())
    {
        output.append((char)0x10); //signal that we do want single wire mode
    }
    else
    {
        output.append((char)0xFF); //signal we don't want single wire mode
    }

    output.append((char)0xF1); //yet another command
    output.append((char)0x09); //comm validation command

    serial->write(output);
    if (doValidation) connected = false;
        else connected = true;
    connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData()));
    if (doValidation) QTimer::singleShot(1000, this, SLOT(connectionTimeout()));
}

void SerialWorker::connectionTimeout()
{
    //one second after trying to connect are we actually connected?
    if (!connected) //no?
    {
        //then emit the the failure signal and see if anyone cares
        qDebug() << "Failed to connect to GVRET at that com port";
        //ticker->stop();
        closeSerialPort(); //make sure it's properly closed anyway
        emit connectionFailure();
    }
}

void SerialWorker::readSerialData()
{
    QByteArray data = serial->readAll();
    unsigned char c;
    //qDebug() << (tr("Got data from serial. Len = %0").arg(data.length()));
    for (int i = 0; i < data.length(); i++)
    {
        c = data.at(i);
        procRXChar(c);
    }
}

void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
{
    QByteArray buffer;
    int c;
    int ID;
    CANFrame tempFrame = *frame;
    tempFrame.isReceived = false;

    //qDebug() << "Sending out frame with id " << frame->ID;

    //show our sent frames in the list too. This happens even if we're not connected.
    canModel->addFrame(tempFrame, false);
    gotFrames++;

    if (serial == NULL) return;
    if (!serial->isOpen()) return;
    if (!connected) return;

    ID = frame->ID;
    if (frame->extended) ID |= 1 << 31;

    buffer[0] = (char)0xF1; //start of a command over serial
    buffer[1] = 0; //command ID for sending a CANBUS frame
    buffer[2] = (unsigned char)(ID & 0xFF); //four bytes of ID LSB first
    buffer[3] = (unsigned char)(ID >> 8);
    buffer[4] = (unsigned char)(ID >> 16);
    buffer[5] = (unsigned char)(ID >> 24);
    buffer[6] = (unsigned char)(bus & 1);
    buffer[7] = (unsigned char)frame->len;
    for (c = 0; c < frame->len; c++)
    {
        buffer[8 + c] = frame->data[c];
    }
    buffer[8 + frame->len] = 0;

    //qDebug() << "writing " << buffer.length() << " bytes to serial port";
    serial->write(buffer);
}

//a simple way for another thread to pass us a bunch of frames to send.
//Don't get carried away here. The GVRET firmware only has finite
//buffers and besides, the other end will get buried in traffic.
void SerialWorker::sendFrameBatch(const QList<CANFrame> *frames)
{
    sendBulkMutex.lock();
    for (int i = 0; i < frames->length(); i++) sendFrame(&frames->at(i), frames->at(i).bus);
    sendBulkMutex.unlock();
}

void SerialWorker::updateBaudRates(int Speed1, int Speed2)
{
    QByteArray buffer;
    qDebug() << "Got signal to update bauds. 1: " << Speed1 <<" 2: " << Speed2;
    buffer[0] = (char)0xF1; //start of a command over serial
    buffer[1] = 5; //setup canbus
    buffer[2] = (unsigned char)(Speed1 & 0xFF); //four bytes of ID LSB first
    buffer[3] = (unsigned char)(Speed1 >> 8);
    buffer[4] = (unsigned char)(Speed1 >> 16);
    buffer[5] = (unsigned char)(Speed1 >> 24);
    buffer[6] = (unsigned char)(Speed2 & 0xFF); //four bytes of ID LSB first
    buffer[7] = (unsigned char)(Speed2 >> 8);
    buffer[8] = (unsigned char)(Speed2 >> 16);
    buffer[9] = (unsigned char)(Speed2 >> 24);
    buffer[10] = 0;
    if (serial == NULL) return;
    if (!serial->isOpen()) return;
    serial->write(buffer);
}

void SerialWorker::procRXChar(unsigned char c)
{
    switch (rx_state)
    {
    case IDLE:
        if (c == 0xF1) rx_state = GET_COMMAND;
        break;
    case GET_COMMAND:
        switch (c)
        {
        case 0: //receiving a can frame
            rx_state = BUILD_CAN_FRAME;
            rx_step = 0;
            break;
        case 1: //we don't accept time sync commands from the firmware
            rx_state = IDLE;
            break;
        case 2: //process a return reply for digital input states.
            rx_state = GET_DIG_INPUTS;
            rx_step = 0;
            break;
        case 3: //process a return reply for analog inputs
            rx_state = GET_ANALOG_INPUTS;
            break;
        case 4: //we set digital outputs we don't accept replies so nothing here.
            rx_state = IDLE;
            break;
        case 5: //we set canbus specs we don't accept replies.
            rx_state = IDLE;
            break;
        case 6: //get canbus parameters from GVRET
            rx_state = GET_CANBUS_PARAMS;
            rx_step = 0;
            break;
        case 7: //get device info
            rx_state = GET_DEVICE_INFO;
            rx_step = 0;
            break;
        case 9:
            gotValidated = true;
            //qDebug() << "Got validated";
            rx_state = IDLE;
            break;
        }
        break;
    case BUILD_CAN_FRAME:
        switch (rx_step)
        {
        case 0:
            buildFrame->timestamp = c;
            break;
        case 1:
            buildFrame->timestamp |= (uint)(c << 8);
            break;
        case 2:
            buildFrame->timestamp |= (uint)c << 16;
            break;
        case 3:
            buildFrame->timestamp |= (uint)c << 24;
            break;
        case 4:
            buildFrame->ID = c;
            break;
        case 5:
            buildFrame->ID |= c << 8;
            break;
        case 6:
            buildFrame->ID |= c << 16;
            break;
        case 7:
            buildFrame->ID |= c << 24;
            if ((buildFrame->ID & 1 << 31) == 1 << 31)
            {
                buildFrame->ID &= 0x7FFFFFFF;
                buildFrame->extended = true;
            }
            else buildFrame->extended = false;
            break;
        case 8:
            buildFrame->len = c & 0xF;
            if (buildFrame->len > 8) buildFrame->len = 8;
            buildFrame->bus = (c & 0xF0) >> 4;
            break;
        default:
            if (rx_step < buildFrame->len + 9)
            {
                buildFrame->data[rx_step - 9] = c;
            }
            else
            {
                rx_state = IDLE;
                rx_step = 0;
                //qDebug() << "emit from serial handler to main form id: " << buildFrame->ID;
                if (capturing)
                {
                    buildFrame->isReceived = true;
                    canModel->addFrame(*buildFrame, false);
                    gotFrames++;
                    if (buildFrame->ID == targetID) emit gotTargettedFrame(canModel->rowCount() - 1);
                }
            }
            break;
        }
        rx_step++;
        break;
    case GET_ANALOG_INPUTS: //get 9 bytes - 2 per analog input plus checksum
        switch (rx_step)
        {
        case 0:
            break;
        }
        rx_step++;
        break;
    case GET_DIG_INPUTS: //get two bytes. One for digital in status and one for checksum.
        switch (rx_step)
        {
        case 0:
            break;
        case 1:
            rx_state = IDLE;
            break;
        }
        rx_step++;
        break;
    case GET_CANBUS_PARAMS:
        switch (rx_step)
        {
        case 0:
            can0Enabled = c;
            break;
        case 1:
            can0Baud = c;
            break;
        case 2:
            can0Baud |= c << 8;
            break;
        case 3:
            can0Baud |= c << 16;
            break;
        case 4:
            can0Baud |= c << 24;
            break;
        case 5:
            can1Enabled = c;
            break;
        case 6:
            can1Baud = c;
            break;
        case 7:
            can1Baud |= c << 8;
            break;
        case 8:
            can1Baud |= c << 16;
            break;
        case 9:
            can1Baud |= c << 24;
            rx_state = IDLE;
            qDebug() << "Baud 0 = " << can0Baud;
            qDebug() << "Baud 1 = " << can1Baud;
            if (!can1Enabled) can1Baud = 0;
            if (!can0Enabled) can0Baud = 0;
            connected = true;
            emit connectionSuccess(can0Baud, can1Baud);
            break;
        }
        rx_step++;
        break;
    case GET_DEVICE_INFO:
        switch (rx_step)
        {
        case 0:
            deviceBuildNum = c;
            break;
        case 1:
            deviceBuildNum |= c << 8;
            break;
        case 2:
            break; //don't care about eeprom version
        case 3:
            break; //don't care about file type
        case 4:
            break; //don't care about whether it auto logs or not
        case 5:
            deviceSingleWireMode = c;
            rx_state = IDLE;
            qDebug() << "build num: " << deviceBuildNum;
            qDebug() << "single wire can: " << deviceSingleWireMode;
            emit deviceInfo(deviceBuildNum, deviceSingleWireMode);
            break;
        }
        rx_step++;
        break;
    case TIME_SYNC:
        rx_state = IDLE;
        break;
    case SET_DIG_OUTPUTS:
        rx_state = IDLE;
        break;
    case SETUP_CANBUS:
        rx_state = IDLE;
        break;
    case SET_SINGLEWIRE_MODE:
        rx_state = IDLE;
        break;

    }
}

void SerialWorker::handleTick()
{
    //qDebug() << "Tick!";

    if (connected)
    {
        if (!gotValidated && doValidation)
        {
            if (serial == NULL) return;
            if (serial->isOpen()) //if it's still false we have a problem...
            {
                qDebug() << "Comm validation failed. ";
                closeSerialPort(); //start by stopping everything.
                //Then wait 500ms and restart the connection automatically
                QTimer::singleShot(500, this, SLOT(handleReconnect()));
                return;
            }
        }
    }

    framesPerSec += gotFrames * 1000 / elapsedTime->elapsed() - (framesPerSec / 4);
    elapsedTime->restart();
    emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties
    canModel->sendBulkRefresh(gotFrames);
    gotFrames = 0;
    if (doValidation && serial && serial->isOpen()) sendCommValidation();
}

void SerialWorker::handleReconnect()
{
    qDebug() << "Automatically reopening the connection";
    setSerialPort(currentPort); //then go back through the re-init
}

void SerialWorker::sendCommValidation()
{
    QByteArray output;

    gotValidated = false;
    output.append((char)0xF1); //another command to the GVRET
    output.append((char)0x09); //request a reply to get validation
    //send it twice for good measure.
    output.append((char)0xF1); //another command to the GVRET
    output.append((char)0x09); //request a reply to get validation

    serial->write(output);
}

//totally shuts down the whole thing
void SerialWorker::closeSerialPort()
{
    if (serial == NULL) return;
    if (serial->isOpen())
    {
        serial->clear();
        serial->close();
    }
    serial->disconnect();
    //do not stop the ticker here. It always stays running now.
    //ticker->stop();
    delete serial;
    serial = NULL;
}

void SerialWorker::stopFrameCapture()
{
    qDebug() << "Stopping frame capture";
    capturing = false;
}

void SerialWorker::startFrameCapture()
{
    qDebug() << "Starting up frame capture";
    capturing = true;
}

void SerialWorker::targetFrameID(int target)
{
    targetID = target;
}
<|endoftext|>


<commit_before>#include <v8.h>
#include <node.h>
#include <node_version.h>

#include <sstream>

using namespace v8;
using namespace node;

#if NODE_VERSION_AT_LEAST(0, 9, 4)
    // do nothing
#else
    #if NODE_VERSION_AT_LEAST(0, 8, 0)
        #define LIBUV_HAS_EV
    #else
        #define LIBUV_HAS_EV
        #include <eio.h>
    #endif
#endif

Handle<Value> SetMinParallel(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 1 || !args[0]->IsUint32()) {
        return ThrowException(Exception::TypeError(String::New("Expected number of threads")));
    }

#ifdef LIBUV_HAS_EV
    eio_set_min_parallel(args[0]->Uint32Value());
#else
    std::stringstream s;
    s << args[0]->Uint32Value();
    setenv("UV_THREADPOOL_SIZE",s.str().c_str(),0);
#endif
    return scope.Close(Undefined());
}

Handle<Value> SetMaxParallel(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 1 || !args[0]->IsUint32()) {
        return ThrowException(Exception::TypeError(String::New("Expected number of threads")));
    }
#ifdef LIBUV_HAS_EV
    eio_set_max_parallel(args[0]->Uint32Value());
#else
    std::stringstream s;
    s << args[0]->Uint32Value();
    setenv("UV_THREADPOOL_SIZE",s.str().c_str(),0);
#endif
    return scope.Close(Undefined());
}

Handle<Value> Usage(const Arguments& args) {
    HandleScope scope;

    Local<Object> info = Object::New();
#ifdef LIBUV_HAS_EV
    unsigned int io_reqs = eio_nreqs(),
        io_active = eio_nthreads(),
        io_pending = eio_npending();

    info->Set(String::NewSymbol("requests"), Integer::NewFromUnsigned(io_reqs));
    info->Set(String::NewSymbol("active"), Integer::NewFromUnsigned(io_active));
    info->Set(String::NewSymbol("pending"), Integer::NewFromUnsigned(io_pending));
#else
    info->Set(String::NewSymbol("requests"), Undefined());
    info->Set(String::NewSymbol("active"), Undefined());
    info->Set(String::NewSymbol("pending"), Undefined());
#endif

    return scope.Close(info);
}

extern "C" void init(Handle<Object> target) {
    NODE_SET_METHOD(target, "setMinParallel", SetMinParallel);
    NODE_SET_METHOD(target, "setMaxParallel", SetMaxParallel);
    NODE_SET_METHOD(target, "usage", Usage);
}

NODE_MODULE(eio, init);
<commit_msg>calling setenv does not work (likely too late) so just print warning instead<commit_after>#include <v8.h>
#include <node.h>
#include <node_version.h>

#include <sstream>
#include <iostream>

using namespace v8;
using namespace node;

#if NODE_VERSION_AT_LEAST(0, 9, 4)
    // do nothing
#else
    #if NODE_VERSION_AT_LEAST(0, 8, 0)
        #define LIBUV_HAS_EV
    #else
        #define LIBUV_HAS_EV
        #include <eio.h>
    #endif
#endif

Handle<Value> SetMinParallel(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 1 || !args[0]->IsUint32()) {
        return ThrowException(Exception::TypeError(String::New("Expected number of threads")));
    }

#ifdef LIBUV_HAS_EV
    eio_set_min_parallel(args[0]->Uint32Value());
#else
    std::stringstream s;
    s << "node-eio has no effect with your node version (" << NODE_VERSION << ")"
      << " set UV_THREADPOOL_SIZE at startup instead";
    std::clog << s.str() << "\n";
#endif
    return scope.Close(Undefined());
}

Handle<Value> SetMaxParallel(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 1 || !args[0]->IsUint32()) {
        return ThrowException(Exception::TypeError(String::New("Expected number of threads")));
    }
#ifdef LIBUV_HAS_EV
    eio_set_max_parallel(args[0]->Uint32Value());
#else
    std::stringstream s;
    s << "node-eio has no effect with your node version (" << NODE_VERSION << ")"
      << " set UV_THREADPOOL_SIZE at startup instead";
    std::clog << s.str() << "\n";
#endif
    return scope.Close(Undefined());
}

Handle<Value> Usage(const Arguments& args) {
    HandleScope scope;

    Local<Object> info = Object::New();
#ifdef LIBUV_HAS_EV
    unsigned int io_reqs = eio_nreqs(),
        io_active = eio_nthreads(),
        io_pending = eio_npending();

    info->Set(String::NewSymbol("requests"), Integer::NewFromUnsigned(io_reqs));
    info->Set(String::NewSymbol("active"), Integer::NewFromUnsigned(io_active));
    info->Set(String::NewSymbol("pending"), Integer::NewFromUnsigned(io_pending));
#else
    info->Set(String::NewSymbol("requests"), Undefined());
    info->Set(String::NewSymbol("active"), Undefined());
    info->Set(String::NewSymbol("pending"), Undefined());
#endif

    return scope.Close(info);
}

extern "C" void init(Handle<Object> target) {
    NODE_SET_METHOD(target, "setMinParallel", SetMinParallel);
    NODE_SET_METHOD(target, "setMaxParallel", SetMaxParallel);
    NODE_SET_METHOD(target, "usage", Usage);
}

NODE_MODULE(eio, init);
<|endoftext|>


<commit_before>#include <iostream>

#include "db_url.h"
#include "process.h"
#include "unidirectional_pipe.h"
#include "to_string.h"

using namespace std;
using namespace boost::program_options;

const string this_program_name("ks");
const int to_descriptor_list_start = 8; // arbitrary; when the program starts we expect only 0, 1, and 2 to be in use, and we only need a couple of temporaries.  getdtablesize() is guaranteed to be at least 20, which is not much!

int help(options_description desc) {
    cout << desc << "\n";
    return 1;
}

void be_christmassy() {
	cout << "            #" << endl
	     << "           ##o" << endl
	     << "          o#*##" << endl
	     << "         ####o##" << endl
	     << "        #*#o####o" << endl
	     << "       #####o##*##" << endl
	     << "      ####o###*####" << endl
	     << "           | |" << endl;
}

int main(int argc, char *argv[]) {
	try
	{
		options_description desc("Allowed options");
		DbUrl from, to;
		string via;
		string filters;
		string set_from_variables;
		string set_to_variables;
		int workers;
		int verbose = 0;
		bool snapshot = true;
		bool partial = false;
		bool rollback = false;
		bool alter = false;
		string ignore, only;
		desc.add_options()
			("from",               value<DbUrl>(&from)->required(),        "The URL of the database to copy data from.  Required.\n")
			("to",                 value<DbUrl>(&to)->required(),          "The URL of the database to copy data to.  Required.\n")
			("via",                value<string>(&via),                    "The server to run the 'from' end on (instead of accessing the database server directly).  Optional; useful whenever the network link to the 'from' database server is a bottleneck, which will definitely be the case if it is at another datacentre, and may be the case even on local LANs if you have very fast disks.\n")
			("workers",            value<int>(&workers)->default_value(1), "The number of concurrent workers to use at each end.\n")
			("ignore",             value<string>(&ignore),                 "Comma-separated list of tables to ignore.\n")
			("only",               value<string>(&only),                   "Comma-separated list of tables to process (making all others ignored).\n")
			("filters",	           value<string>(&filters),				   "YAML file to read table/column filtering information from (at the 'from' end).\n")
			("set-from-variables", value<string>(&set_from_variables),     "SET variables to apply at the 'from' end (eg. --set-from-variables=\"sql_log_off=0, time_zone='UTC'\")\n")
			("set-to-variables",   value<string>(&set_to_variables),       "SET variables to apply at the 'to' end (eg. --set-to-variables=\"sql_log_bin=0\")\n")
			("without-snapshot-export",                                    "Don't attempt to export/use a consistent snapshot across multiple workers (snapshots are normally a good thing, but require version 9.2 or later for PostgreSQL, and on MySQL uses FLUSH TABLES WITH READ LOCK which requires the RELOAD privilege and may have an impact on other connections as it blocks till all open transactions commit).  Turning on this option avoids these problems, but you may get an inconsistent copy if transactions commit in between the individual worker transactions starting (changes after that point still won't not be a problem).\n")
			("partial",                                                    "Attempt to commit changes even if some workers hit errors.\n")
			("rollback-after",                                             "Roll back afterwards, for benchmarking.\n")
			("alter",                                                      "Alter the database schema if it currently doesn't match.\n")
			("verbose",                                                    "Log more information as the program works.\n")
			("debug",                                                      "Log debugging information as the program works.\n");
		variables_map vm;

		try {
			store(parse_command_line(argc, argv, desc), vm);
			notify(vm);
		} catch(required_option) {
			return help(desc);
		} catch(validation_error &e) {
			cerr << e.what() << endl;
			return help(desc);
		}
		if (vm.count("help")) return help(desc);
		if (vm.count("verbose")) verbose = 1;
		if (vm.count("debug"))   verbose = 2;
		if (vm.count("without-snapshot-export")) snapshot = false;
		if (vm.count("partial")) partial = true;
		if (vm.count("rollback-after")) rollback = true;
		if (vm.count("alter")) alter = true;

		cout << "Kitchen Sync" << endl;

		string self_binary(argv[0]);
		string from_binary(Process::related_binary_path(self_binary, this_program_name, "ks_" + from.protocol));
		string   to_binary(Process::related_binary_path(self_binary, this_program_name, "ks_" +   to.protocol));
		string  ssh_binary("/usr/bin/ssh");
		string workers_str(to_string(workers));
		string verbose_str(to_string(verbose));
		string startfd_str(to_string(to_descriptor_list_start));

		// unfortunately when we transport program arguments over SSH it flattens them into a string and so empty arguments get lost; we work around by using "-"
		if (from.port    .empty()) from.port     = "-";
		if (from.username.empty()) from.username = "-";
		if (from.password.empty()) from.password = "-";
		if (to  .port    .empty()) to  .port     = "-";
		if (to  .username.empty()) to  .username = "-";
		if (to  .password.empty()) to  .password = "-";
		if (set_from_variables.empty()) set_from_variables = "-";
		if (set_to_variables.empty())   set_to_variables = "-";

		const char *from_args[] = { ssh_binary.c_str(), "-C", "-c", "blowfish", via.c_str(),
									from_binary.c_str(), "from", from.host.c_str(), from.port.c_str(), from.database.c_str(), from.username.c_str(), from.password.c_str(), set_from_variables.c_str(), filters.c_str(), nullptr };
		const char *  to_args[] = {   to_binary.c_str(),   "to",   to.host.c_str(),   to.port.c_str(),   to.database.c_str(),   to.username.c_str(),   to.password.c_str(), set_to_variables.c_str(), ignore.c_str(), only.c_str(), workers_str.c_str(), startfd_str.c_str(), verbose_str.c_str(), snapshot ? "1" : "0", partial ? "1" : "0", rollback ? "1" : "0", alter ? "1" : "0", nullptr };
		const char **applicable_from_args = (via.empty() ? from_args + 5 : from_args);

		vector<pid_t> child_pids;
		for (int worker = 0; worker < workers; ++worker) {
			UnidirectionalPipe stdin_pipe;
			UnidirectionalPipe stdout_pipe;
			child_pids.push_back(Process::fork_and_exec(*applicable_from_args, applicable_from_args, stdin_pipe, stdout_pipe));
			stdout_pipe.dup_read_to(to_descriptor_list_start + worker);
			stdin_pipe.dup_write_to(to_descriptor_list_start + worker + workers);
		}

		child_pids.push_back(Process::fork_and_exec(to_binary, to_args));

		for (int worker = 0; worker < workers; ++worker) {
			::close(to_descriptor_list_start + worker);
			::close(to_descriptor_list_start + worker + workers);
		}

		bool success = true;
		for (pid_t pid : child_pids) {
			success &= Process::wait_for_and_check(pid);
		}

		if (success) {
			cout << "Finished Kitchen Syncing." << endl;
			time_t t = time(NULL);
			if (verbose && localtime(&t)->tm_mon == 11) be_christmassy();
			return 0;
		} else {
			cout << "Kitchen Syncing failed." << endl;
			return 1;
		}
	} catch (const exception &e) {
		cerr << e.what() << endl;
	}
}
<commit_msg>dump the worker arguments if running with --debug<commit_after>#include <iostream>

#include "db_url.h"
#include "process.h"
#include "unidirectional_pipe.h"
#include "to_string.h"

using namespace std;
using namespace boost::program_options;

#define VERY_VERBOSE 2

const string this_program_name("ks");
const int to_descriptor_list_start = 8; // arbitrary; when the program starts we expect only 0, 1, and 2 to be in use, and we only need a couple of temporaries.  getdtablesize() is guaranteed to be at least 20, which is not much!

int help(options_description desc) {
    cout << desc << "\n";
    return 1;
}

void be_christmassy() {
	cout << "            #" << endl
	     << "           ##o" << endl
	     << "          o#*##" << endl
	     << "         ####o##" << endl
	     << "        #*#o####o" << endl
	     << "       #####o##*##" << endl
	     << "      ####o###*####" << endl
	     << "           | |" << endl;
}

int main(int argc, char *argv[]) {
	try
	{
		options_description desc("Allowed options");
		DbUrl from, to;
		string via;
		string filters;
		string set_from_variables;
		string set_to_variables;
		int workers;
		int verbose = 0;
		bool snapshot = true;
		bool partial = false;
		bool rollback = false;
		bool alter = false;
		string ignore, only;
		desc.add_options()
			("from",               value<DbUrl>(&from)->required(),        "The URL of the database to copy data from.  Required.\n")
			("to",                 value<DbUrl>(&to)->required(),          "The URL of the database to copy data to.  Required.\n")
			("via",                value<string>(&via),                    "The server to run the 'from' end on (instead of accessing the database server directly).  Optional; useful whenever the network link to the 'from' database server is a bottleneck, which will definitely be the case if it is at another datacentre, and may be the case even on local LANs if you have very fast disks.\n")
			("workers",            value<int>(&workers)->default_value(1), "The number of concurrent workers to use at each end.\n")
			("ignore",             value<string>(&ignore),                 "Comma-separated list of tables to ignore.\n")
			("only",               value<string>(&only),                   "Comma-separated list of tables to process (making all others ignored).\n")
			("filters",	           value<string>(&filters),				   "YAML file to read table/column filtering information from (at the 'from' end).\n")
			("set-from-variables", value<string>(&set_from_variables),     "SET variables to apply at the 'from' end (eg. --set-from-variables=\"sql_log_off=0, time_zone='UTC'\")\n")
			("set-to-variables",   value<string>(&set_to_variables),       "SET variables to apply at the 'to' end (eg. --set-to-variables=\"sql_log_bin=0\")\n")
			("without-snapshot-export",                                    "Don't attempt to export/use a consistent snapshot across multiple workers (snapshots are normally a good thing, but require version 9.2 or later for PostgreSQL, and on MySQL uses FLUSH TABLES WITH READ LOCK which requires the RELOAD privilege and may have an impact on other connections as it blocks till all open transactions commit).  Turning on this option avoids these problems, but you may get an inconsistent copy if transactions commit in between the individual worker transactions starting (changes after that point still won't not be a problem).\n")
			("partial",                                                    "Attempt to commit changes even if some workers hit errors.\n")
			("rollback-after",                                             "Roll back afterwards, for benchmarking.\n")
			("alter",                                                      "Alter the database schema if it currently doesn't match.\n")
			("verbose",                                                    "Log more information as the program works.\n")
			("debug",                                                      "Log debugging information as the program works.\n");
		variables_map vm;

		try {
			store(parse_command_line(argc, argv, desc), vm);
			notify(vm);
		} catch(required_option) {
			return help(desc);
		} catch(validation_error &e) {
			cerr << e.what() << endl;
			return help(desc);
		}
		if (vm.count("help")) return help(desc);
		if (vm.count("verbose")) verbose = 1;
		if (vm.count("debug"))   verbose = 2;
		if (vm.count("without-snapshot-export")) snapshot = false;
		if (vm.count("partial")) partial = true;
		if (vm.count("rollback-after")) rollback = true;
		if (vm.count("alter")) alter = true;

		cout << "Kitchen Sync" << endl;

		string self_binary(argv[0]);
		string from_binary(Process::related_binary_path(self_binary, this_program_name, "ks_" + from.protocol));
		string   to_binary(Process::related_binary_path(self_binary, this_program_name, "ks_" +   to.protocol));
		string  ssh_binary("/usr/bin/ssh");
		string workers_str(to_string(workers));
		string verbose_str(to_string(verbose));
		string startfd_str(to_string(to_descriptor_list_start));

		// unfortunately when we transport program arguments over SSH it flattens them into a string and so empty arguments get lost; we work around by using "-"
		if (from.port    .empty()) from.port     = "-";
		if (from.username.empty()) from.username = "-";
		if (from.password.empty()) from.password = "-";
		if (to  .port    .empty()) to  .port     = "-";
		if (to  .username.empty()) to  .username = "-";
		if (to  .password.empty()) to  .password = "-";
		if (set_from_variables.empty()) set_from_variables = "-";
		if (set_to_variables.empty())   set_to_variables = "-";

		const char *from_args[] = { ssh_binary.c_str(), "-C", "-c", "blowfish", via.c_str(),
									from_binary.c_str(), "from", from.host.c_str(), from.port.c_str(), from.database.c_str(), from.username.c_str(), from.password.c_str(), set_from_variables.c_str(), filters.c_str(), nullptr };
		const char *  to_args[] = {   to_binary.c_str(),   "to",   to.host.c_str(),   to.port.c_str(),   to.database.c_str(),   to.username.c_str(),   to.password.c_str(), set_to_variables.c_str(), ignore.c_str(), only.c_str(), workers_str.c_str(), startfd_str.c_str(), verbose_str.c_str(), snapshot ? "1" : "0", partial ? "1" : "0", rollback ? "1" : "0", alter ? "1" : "0", nullptr };
		const char **applicable_from_args = (via.empty() ? from_args + 5 : from_args);

		if (verbose >= VERY_VERBOSE) {
			cout << "from command:";
			for (const char **p = from_args; *p; p++) cout << ' ' << (**p ? *p : "''");
			cout << endl;

			cout << "to command:";
			for (const char **p = to_args; *p; p++) cout << ' ' << (**p ? *p : "''");
			cout << endl;
		}

		vector<pid_t> child_pids;
		for (int worker = 0; worker < workers; ++worker) {
			UnidirectionalPipe stdin_pipe;
			UnidirectionalPipe stdout_pipe;
			child_pids.push_back(Process::fork_and_exec(*applicable_from_args, applicable_from_args, stdin_pipe, stdout_pipe));
			stdout_pipe.dup_read_to(to_descriptor_list_start + worker);
			stdin_pipe.dup_write_to(to_descriptor_list_start + worker + workers);
		}

		child_pids.push_back(Process::fork_and_exec(to_binary, to_args));

		for (int worker = 0; worker < workers; ++worker) {
			::close(to_descriptor_list_start + worker);
			::close(to_descriptor_list_start + worker + workers);
		}

		bool success = true;
		for (pid_t pid : child_pids) {
			success &= Process::wait_for_and_check(pid);
		}

		if (success) {
			cout << "Finished Kitchen Syncing." << endl;
			time_t t = time(NULL);
			if (verbose && localtime(&t)->tm_mon == 11) be_christmassy();
			return 0;
		} else {
			cout << "Kitchen Syncing failed." << endl;
			return 1;
		}
	} catch (const exception &e) {
		cerr << e.what() << endl;
	}
}
<|endoftext|>