<commit_before>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/test/unit_test.hpp>

#include "Model.hpp"

BOOST_AUTO_TEST_CASE (kill_test) {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            0,
            1
        );
    Abstract::Point coordinates(0, 0);
    model->createNewByCoordinates(
        coordinates,
        DEFAULT_MASS,
        0,
        0,
        0
    );
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::BACTERIUM);
    model->killByCoordinates(coordinates);
    state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    delete model;
}
<commit_msg>Model tests: static function createBaseModel()<commit_after>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/test/unit_test.hpp>

#include "Model.hpp"

static Implementation::Model* createBaseModel() {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            0,
            1
        );
    return model;
}

BOOST_AUTO_TEST_CASE (kill_test) {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            0,
            1
        );
    Abstract::Point coordinates(0, 0);
    model->createNewByCoordinates(
        coordinates,
        DEFAULT_MASS,
        0,
        0,
        0
    );
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::BACTERIUM);
    model->killByCoordinates(coordinates);
    state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    delete model;
}
<|endoftext|>


<commit_before>#include "../include/yal/items.h"

#include <iomanip>
#include <sstream>

#include "../include/yal/entries.h"
#include "../include/yal/utils.h"

namespace
{
    fmt::BasicWriter<char>& appendZeroIfNeeded(fmt::BasicWriter<char>& stream, int value)
    {
        if (value < 10)
        {
            stream << '0';
        }
        stream << value;
        return stream;
    }

    const char* findEnd(const char* begin)
    {
        if (*begin == '}')
        {
            return begin + 1;
        }

        const char* end = begin + 1;
        while (*end && *end != '}')
            ++end;
        return end + 1;
    }

    std::string getFormat(const char* begin, const char* end)
    {
        std::string format = "{";
        format.append(begin, end);
        return format;
    }
}

#define YAL_FORMAT_VALUE(value) \
    const char* end = findEnd(begin); \
    if (begin + 1 != end) \
    { \
        formatter.writer().write(getFormat(begin, end), (value)); \
    } \
    else \
    { \
        formatter.writer() << (value); \
    } \
    begin = end;

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::SeverityItem& item)
{
    using namespace artec::yal;

    YAL_FORMAT_VALUE(toString(item.entry.level))
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::TextItem& item)
{
    using namespace artec::yal;

    YAL_FORMAT_VALUE(item.entry.text)
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::TimezoneItem& item)
{
    //TODO need to improve performance (localtime is very expensive)

    using namespace artec::yal;

    const auto time = ClockType::to_time_t(item.entry.time);
    const auto localtime = std::localtime(&time);

    std::stringstream buf;
    buf << std::put_time(localtime, "%z");
    formatter.writer() << buf.str();

    begin = findEnd(begin);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::TimeItem& item)
{
    using namespace artec::yal;

    const auto time = ClockType::to_time_t(item.entry.time);
    const auto upToSecond = ClockType::from_time_t(time);
    const auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(item.entry.time - upToSecond).count();

    const auto utcTime = toDateTime(item.entry.time);

    appendZeroIfNeeded(formatter.writer(), utcTime.hour) << ':';
    appendZeroIfNeeded(formatter.writer(), utcTime.min) << ':';
    appendZeroIfNeeded(formatter.writer(), utcTime.sec) << '.' << microseconds;

    begin = findEnd(begin);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::DateItem& item)
{
    using namespace artec::yal;

    const auto utcTime = toDateTime(item.entry.time);

    formatter.writer() << utcTime.year << '-';
    appendZeroIfNeeded(formatter.writer(), utcTime.month + 1) << '-';
    appendZeroIfNeeded(formatter.writer(), utcTime.day);

    begin = findEnd(begin);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::ThreadItem& item)
{
    using namespace artec::yal;

    std::stringstream buf;
    buf << item.entry.thread;
    const auto tid = std::stoul(buf.str());

    YAL_FORMAT_VALUE(tid)
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::PlaceInCodeItem& item)
{
    using namespace artec::yal;

    formatter.writer() << item.entry.file << ':' << item.entry.line;

    begin = findEnd(begin);
}

#undef YAL_FORMAT_VALUE
<commit_msg>replace macro with function<commit_after>#include "../include/yal/items.h"

#include <iomanip>
#include <sstream>

#include "../include/yal/entries.h"
#include "../include/yal/utils.h"

namespace
{
    fmt::BasicWriter<char>& appendZeroIfNeeded(fmt::BasicWriter<char>& stream, int value)
    {
        if (value < 10)
        {
            stream << '0';
        }
        stream << value;
        return stream;
    }

    const char* findEnd(const char* begin)
    {
        if (*begin == '}')
        {
            return begin + 1;
        }

        const char* end = begin + 1;
        while (*end && *end != '}')
        {
            ++end;
        }

        return end + 1;
    }

    std::string getFormat(const char* begin, const char* end)
    {
        std::string format = "{";
        format.append(begin, end);
        return format;
    }

    template <class T>
    void formatItem(fmt::BasicFormatter<char>& formatter, const char*& begin, const T& value)
    {
        const char* end = findEnd(begin);
        if (begin + 1 != end)
        {
            formatter.writer().write(getFormat(begin, end), (value));
        }
        else
        {
            formatter.writer() << (value);
        }
        begin = end;
    }
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::SeverityItem& item)
{
    formatItem(formatter, begin, toString(item.entry.level));
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::TextItem& item)
{
    formatItem(formatter, begin, item.entry.text);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::TimezoneItem& item)
{
    //TODO need to improve performance (localtime is very expensive)

    const auto time = artec::yal::ClockType::to_time_t(item.entry.time);
    const auto localtime = std::localtime(&time);

    std::stringstream buf;
    buf << std::put_time(localtime, "%z");
    formatter.writer() << buf.str();

    begin = findEnd(begin);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::TimeItem& item)
{
    using namespace artec::yal;

    const auto time = ClockType::to_time_t(item.entry.time);
    const auto upToSecond = ClockType::from_time_t(time);
    const auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(item.entry.time - upToSecond).count();

    const auto utcTime = toDateTime(item.entry.time);

    appendZeroIfNeeded(formatter.writer(), utcTime.hour) << ':';
    appendZeroIfNeeded(formatter.writer(), utcTime.min) << ':';
    appendZeroIfNeeded(formatter.writer(), utcTime.sec) << '.' << microseconds;

    begin = findEnd(begin);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::DateItem& item)
{
    const auto utcTime = artec::yal::toDateTime(item.entry.time);

    formatter.writer() << utcTime.year << '-';
    appendZeroIfNeeded(formatter.writer(), utcTime.month + 1) << '-';
    appendZeroIfNeeded(formatter.writer(), utcTime.day);

    begin = findEnd(begin);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::ThreadItem& item)
{
    std::stringstream buf;
    buf << item.entry.thread;
    const auto tid = std::stoul(buf.str());

    formatItem(formatter, begin, tid);
}

void fmt::format_arg(fmt::BasicFormatter<char>& formatter, const char*& begin, const artec::yal::PlaceInCodeItem& item)
{
    formatter.writer() << item.entry.file << ':' << item.entry.line;
    begin = findEnd(begin);
}
<|endoftext|>


<commit_before>#include <thread>
#include <set>
#include <signal.h>

#include <cl/cl.h>
#include <ev3/servo.h>

using namespace std;
using namespace ev3dev;

cl::arg<std::string> port(
  ev3dev::OUTPUT_A,
  cl::name("port"),
  cl::desc("Port the motor is attached to."));

cl::arg<float> period(
  4.0f,
  cl::name("period"),
  cl::desc("Sine wave period, in s."));
cl::arg<float> amplitude(
  180.0f,
  cl::name("amplitude"),
  cl::desc("Sine wave amplitude."));
cl::boolean square_wave(
  cl::name("square-wave"),
  cl::desc("Use a square wave instead of a sine wave."));

cl::boolean use_servo(
  cl::name("servo"),
  cl::desc("Use the ev3cv::servo class."));

cl::group ev3cv_group("ev3cv::servo settings");
cl::arg<vector3i> K(
  vector3i(5000, 5000, 200),
  cl::name("K"),
  cl::desc("PID parameters Kp, Ki, Kd."),
  ev3cv_group);

cl::group ev3dev_group("ev3dev::motor settings");
cl::arg<std::string> regulation_mode(
  "off",
  cl::name("regulation-mode"),
  ev3dev_group);
cl::arg<std::string> stop_mode(
  "coast",
  cl::name("stop-mode"),
  ev3dev_group);
cl::arg<int> pulses_per_second_setpoint(
  700,
  cl::name("pulses-per-second-setpoint"),
  ev3dev_group);
cl::arg<int> duty_cycle_setpoint(
  100,
  cl::name("duty-cycle-setpoint"),
  ev3dev_group);

int setpoint_fn(int ms) {
  float sp = sin(ms*2.0f*pi/(period*1000.0f));
  if (square_wave)
    sp = sp < 0.0f ? -1.0f : 1.0f;
  return sp*amplitude;
}

// Test and benchmark estimate_trajectory.
int main(int argc, const char **argv) {
  cl::parse(argv[0], argc - 1, argv + 1);

  typedef chrono::high_resolution_clock clock;
  chrono::milliseconds T(20);

  if (use_servo) {
    // Use ev3cv::servo.
    servo m(*port);
    m.controller().set_K(K->x, K->y, K->z);
    m.run();

    auto t0 = clock::now();
    for (auto t = t0; ; t += T) {
      int ms = chrono::duration_cast<chrono::milliseconds>(t - t0).count();
      m.set_position_setpoint(setpoint_fn(ms));
      this_thread::sleep_until(t);
    }
  } else {
    // To compare against the stock controller
    motor m(*port);
    m.reset();
    m.set_run_mode(motor::run_mode_position);
    m.set_regulation_mode(regulation_mode);
    m.set_pulses_per_second_setpoint(pulses_per_second_setpoint);
    m.set_duty_cycle_setpoint(duty_cycle_setpoint);
    m.set_stop_mode(stop_mode);

    auto t0 = clock::now();
    for (auto t = t0; ; t += T) {
      int ms = chrono::duration_cast<chrono::milliseconds>(t - t0).count();
      m.set_position_setpoint(setpoint_fn(ms));
      m.run();
      this_thread::sleep_until(t);
    }
  }
  return 0;
}

<commit_msg>Use another motor as input to control the servo<commit_after>#include <thread>
#include <set>
#include <signal.h>

#include <cl/cl.h>
#include <ev3/servo.h>

using namespace std;
using namespace ev3dev;

cl::arg<std::string> output_port(
  ev3dev::OUTPUT_A,
  cl::name("output-port"),
  cl::desc("Port the motor is attached to."));

cl::arg<std::string> input_port(
  ev3dev::OUTPUT_D,
  cl::name("input-port"),
  cl::desc("Port the tacho is attached to."));

cl::boolean use_servo(
  cl::name("servo"),
  cl::desc("Use the ev3cv::servo class."));

cl::arg<float> scale(
  1.0f,
  cl::name("scale"),
  cl::desc("Relative scale of the motion between the input and output."));

cl::group ev3cv_group("ev3cv::servo settings");
cl::arg<vector3i> K(
  vector3i(5000, 5000, 200),
  cl::name("K"),
  cl::desc("PID parameters Kp, Ki, Kd."),
  ev3cv_group);

cl::group ev3dev_group("ev3dev::motor settings");
cl::arg<std::string> regulation_mode(
  "off",
  cl::name("regulation-mode"),
  ev3dev_group);
cl::arg<std::string> stop_mode(
  "hold",
  cl::name("stop-mode"),
  ev3dev_group);
cl::arg<int> ramp_up(
  0,
  cl::name("ramp-up"),
  ev3dev_group);
cl::arg<int> ramp_down(
  0,
  cl::name("ramp-down"),
  ev3dev_group);
cl::arg<int> pulses_per_second_setpoint(
  700,
  cl::name("pulses-per-second-setpoint"),
  ev3dev_group);
cl::arg<int> duty_cycle_setpoint(
  100,
  cl::name("duty-cycle-setpoint"),
  ev3dev_group);

// Test and benchmark estimate_trajectory.
int main(int argc, const char **argv) {
  cl::parse(argv[0], argc - 1, argv + 1);

  typedef chrono::high_resolution_clock clock;
  chrono::milliseconds T(20);

  motor in(*input_port);
  in.reset();

  cout << "Turn the motor connected to port " << *input_port << "..." << endl;

  if (use_servo) {
    // Use ev3cv::servo.
    servo m(*output_port);
    m.controller().set_K(K->x, K->y, K->z);
    m.run();

    for (auto t = clock::now(); ; t += T) {
      m.set_position_setpoint(in.position()*scale);
      this_thread::sleep_until(t);
    }
  } else {
    // Compare against the stock controller
    motor m(*output_port);
    m.reset();
    m.set_run_mode(motor::run_mode_position);
    m.set_regulation_mode(regulation_mode);
    m.set_pulses_per_second_setpoint(pulses_per_second_setpoint);
    m.set_duty_cycle_setpoint(duty_cycle_setpoint);
    m.set_stop_mode(stop_mode);
    m.set_ramp_up(ramp_up);
    m.set_ramp_down(ramp_down);

    for (auto t = clock::now(); ; t += T) {
      m.set_position_setpoint(in.position()*scale);
      m.run();
      this_thread::sleep_until(t);
    }
  }
  return 0;
}

<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "include/types.h"
#include "include/rados/librados.hpp"

using namespace librados;

#include <iostream>

#include <stdlib.h>
#include <time.h>

void buf_to_hex(const unsigned char *buf, int len, char *str)
{
  str[0] = '\0';
  for (int i = 0; i < len; i++) {
    sprintf(&str[i*2], "%02x", (int)buf[i]);
  }
}

class C_Watch : public WatchCtx {
public:
  C_Watch() {}
  void notify(uint8_t opcode, uint64_t ver) {
    cout << "C_Watch::notify() opcode=" << (int)opcode << " ver=" << ver << std::endl;
  }
};

void testradospp_milestone(void)
{
  cout << "*** press enter to continue ***" << std::endl;
  getchar();
}

int main(int argc, const char **argv)
{
  Rados rados;
  if (rados.init(NULL) < 0) {
     cerr << "couldn't initialize rados!" << std::endl;
     exit(1);
  }

  if (rados.conf_read_file(NULL)) {
     cerr << "couldn't read configuration file." << std::endl;
     exit(1);
  }

  if (!rados.conf_set("config option that doesn't exist",
                     "some random value")) {
    printf("error: succeeded in setting nonexistent config option\n");
    exit(1);
  }
  if (rados.conf_set("log to stderr", "2")) {
    printf("error: error setting log_to_stderr\n");
    exit(1);
  }
  rados.reopen_log();
  std::string tmp;
  if (rados.conf_get("log to stderr", tmp)) {
    printf("error: failed to read log_to_stderr from config\n");
    exit(1);
  }
  if (tmp[0] != '2') {
    printf("error: new setting for log_to_stderr failed to take effect.\n");
    exit(1);
  }

  if (rados.connect()) {
    printf("error connecting\n");
    exit(1);
  }

  cout << "rados_initialize completed" << std::endl;
  testradospp_milestone();

  time_t tm;
  bufferlist bl, bl2, blf;
  char buf[128];

  time(&tm);
  snprintf(buf, 128, "%s", ctime(&tm));
  bl.append(buf, strlen(buf));
  blf.append(buf, 16);

  const char *oid = "bar";

  IoCtx io_ctx;
  int r = rados.ioctx_create("data", io_ctx);
  cout << "ioctx_create result = " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length(), 0);
  uint64_t objver = io_ctx.get_last_version();
  cout << "io_ctx.write returned " << r << " last_ver=" << objver << std::endl;

  uint64_t stat_size;
  time_t stat_mtime;
  r = io_ctx.stat(oid, &stat_size, &stat_mtime);
  cout << "io_ctx.stat size = " << stat_size << " mtime = " << stat_mtime << std::endl;

  r = io_ctx.stat(oid, NULL, NULL);
  cout << "io_ctx.stat(does_not_exist) = " << r;

  uint64_t handle;
  C_Watch wc;
  r = io_ctx.watch(oid, objver, &handle, &wc);
  cout << "io_ctx.watch returned " << r << std::endl;

  testradospp_milestone();
  io_ctx.set_notify_timeout(7);
  r = io_ctx.notify(oid, objver);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.unwatch(oid, handle);
  cout << "io_ctx.unwatch returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver);
  cout << "io_ctx.notify returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();
  io_ctx.set_assert_version(objver);

  r = io_ctx.write(oid, bl, bl.length() - 1, 0);
  cout << "io_ctx.write returned " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length() - 2, 0);
  cout << "io_ctx.write returned " << r << std::endl;
  r = io_ctx.write(oid, bl, bl.length() - 3, 0);
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.append(oid, bl, bl.length());
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.write_full(oid, blf);
  cout << "rados.write_full returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.trunc(oid, 8);
  cout << "rados.trunc returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.exec(oid, "crypto", "md5", bl, bl2);
  cout << "exec returned " << r <<  " buf size=" << bl2.length() << std::endl;
  const unsigned char *md5 = (const unsigned char *)bl2.c_str();
  char md5_str[bl2.length()*2 + 1];
  buf_to_hex(md5, bl2.length(), md5_str);
  cout << "md5 result=" << md5_str << std::endl;

  r = io_ctx.exec(oid, "crypto", "sha1", bl, bl2);
  cout << "exec returned " << r << std::endl;
  const unsigned char *sha1 = (const unsigned char *)bl2.c_str();
  char sha1_str[bl2.length()*2 + 1];
  buf_to_hex(sha1, bl2.length(), sha1_str);
  cout << "sha1 result=" << sha1_str << std::endl;

  r = io_ctx.exec(oid, "acl", "set", bl, bl2);
  r = io_ctx.exec(oid, "acl", "get", bl, bl2);
  cout << "exec returned " << r << std::endl;
  if (bl2.length() > 0) {
    cout << "attr=" << bl2.c_str() << std::endl;
  }

  int size = io_ctx.read(oid, bl2, 128, 0);
  cout << "read result=" << bl2.c_str() << std::endl;
  cout << "size=" << size << std::endl;

  const char *oid2 = "jjj10.rbd";
  r = io_ctx.exec(oid2, "rbd", "snap_list", bl, bl2);
  cout << "snap_list result=" << r << std::endl;
  r = io_ctx.exec(oid2, "rbd", "snap_add", bl, bl2);
  cout << "snap_add result=" << r << std::endl;

  if (r > 0) {
    char *s = bl2.c_str();
    for (int i=0; i<r; i++, s += strlen(s) + 1)
      cout << s << std::endl;
  }

  cout << "iterating over objects..." << std::endl;
  int num_objs = 0;
  for (ObjectIterator iter = io_ctx.objects_begin();
       iter != io_ctx.objects_end(); ++iter) {
    num_objs++;
    cout << "'" << *iter << "'" << std::endl;
  }
  cout << "iterated over " << num_objs << " objects." << std::endl;
  map<string, bufferlist> attrset;
  io_ctx.getxattrs(oid, attrset);

  map<string, bufferlist>::iterator it;
  for (it = attrset.begin(); it != attrset.end(); ++it) {
    cout << "xattr: " << it->first << std::endl;
  }

  r = io_ctx.remove(oid);
  cout << "remove result=" << r << std::endl;
  rados.shutdown();

  return 0;
}

<commit_msg>testradospp: zero terminate before printing strs<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "include/types.h"
#include "include/rados/librados.hpp"

using namespace librados;

#include <iostream>

#include <stdlib.h>
#include <time.h>

void buf_to_hex(const unsigned char *buf, int len, char *str)
{
  str[0] = '\0';
  for (int i = 0; i < len; i++) {
    sprintf(&str[i*2], "%02x", (int)buf[i]);
  }
}

class C_Watch : public WatchCtx {
public:
  C_Watch() {}
  void notify(uint8_t opcode, uint64_t ver) {
    cout << "C_Watch::notify() opcode=" << (int)opcode << " ver=" << ver << std::endl;
  }
};

void testradospp_milestone(void)
{
  cout << "*** press enter to continue ***" << std::endl;
  getchar();
}

int main(int argc, const char **argv)
{
  Rados rados;
  if (rados.init(NULL) < 0) {
     cerr << "couldn't initialize rados!" << std::endl;
     exit(1);
  }

  if (rados.conf_read_file(NULL)) {
     cerr << "couldn't read configuration file." << std::endl;
     exit(1);
  }

  if (!rados.conf_set("config option that doesn't exist",
                     "some random value")) {
    printf("error: succeeded in setting nonexistent config option\n");
    exit(1);
  }
  if (rados.conf_set("log to stderr", "2")) {
    printf("error: error setting log_to_stderr\n");
    exit(1);
  }
  rados.reopen_log();
  std::string tmp;
  if (rados.conf_get("log to stderr", tmp)) {
    printf("error: failed to read log_to_stderr from config\n");
    exit(1);
  }
  if (tmp[0] != '2') {
    printf("error: new setting for log_to_stderr failed to take effect.\n");
    exit(1);
  }

  if (rados.connect()) {
    printf("error connecting\n");
    exit(1);
  }

  cout << "rados_initialize completed" << std::endl;
  testradospp_milestone();

  time_t tm;
  bufferlist bl, bl2, blf;
  char buf[128];

  time(&tm);
  snprintf(buf, 128, "%s", ctime(&tm));
  bl.append(buf, strlen(buf));
  blf.append(buf, 16);

  const char *oid = "bar";

  IoCtx io_ctx;
  int r = rados.ioctx_create("data", io_ctx);
  cout << "ioctx_create result = " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length(), 0);
  uint64_t objver = io_ctx.get_last_version();
  cout << "io_ctx.write returned " << r << " last_ver=" << objver << std::endl;

  uint64_t stat_size;
  time_t stat_mtime;
  r = io_ctx.stat(oid, &stat_size, &stat_mtime);
  cout << "io_ctx.stat size = " << stat_size << " mtime = " << stat_mtime << std::endl;

  r = io_ctx.stat(oid, NULL, NULL);
  cout << "io_ctx.stat(does_not_exist) = " << r;

  uint64_t handle;
  C_Watch wc;
  r = io_ctx.watch(oid, objver, &handle, &wc);
  cout << "io_ctx.watch returned " << r << std::endl;

  testradospp_milestone();
  io_ctx.set_notify_timeout(7);
  r = io_ctx.notify(oid, objver);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.unwatch(oid, handle);
  cout << "io_ctx.unwatch returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver);
  cout << "io_ctx.notify returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();
  io_ctx.set_assert_version(objver);

  r = io_ctx.write(oid, bl, bl.length() - 1, 0);
  cout << "io_ctx.write returned " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length() - 2, 0);
  cout << "io_ctx.write returned " << r << std::endl;
  r = io_ctx.write(oid, bl, bl.length() - 3, 0);
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.append(oid, bl, bl.length());
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.write_full(oid, blf);
  cout << "rados.write_full returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.trunc(oid, 8);
  cout << "rados.trunc returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.exec(oid, "crypto", "md5", bl, bl2);
  cout << "exec returned " << r <<  " buf size=" << bl2.length() << std::endl;
  const unsigned char *md5 = (const unsigned char *)bl2.c_str();
  char md5_str[bl2.length()*2 + 1];
  buf_to_hex(md5, bl2.length(), md5_str);
  cout << "md5 result=" << md5_str << std::endl;

  r = io_ctx.exec(oid, "crypto", "sha1", bl, bl2);
  cout << "exec returned " << r << std::endl;
  const unsigned char *sha1 = (const unsigned char *)bl2.c_str();
  char sha1_str[bl2.length()*2 + 1];
  buf_to_hex(sha1, bl2.length(), sha1_str);
  cout << "sha1 result=" << sha1_str << std::endl;

  r = io_ctx.exec(oid, "acl", "set", bl, bl2);
  r = io_ctx.exec(oid, "acl", "get", bl, bl2);
  cout << "exec returned " << r << std::endl;
  if (bl2.length() > 0) {
    cout << "attr=" << bl2.c_str() << std::endl;
  }

  int size = io_ctx.read(oid, bl2, 128, 0);
  if (size <= 0) {
    cout << "failed to read oid " << oid << "." << std::endl;
    exit(1);
  }
  if (size > 4096) {
    cout << "read too many bytes from oid " << oid << "." << std::endl;
    exit(1);
  }
  char rbuf[size + 1];
  memcpy(rbuf, bl2.c_str(), size);
  rbuf[size] = '\0';
  cout << "read result='" << rbuf << "'" << std::endl;
  cout << "size=" << size << std::endl;

  const char *oid2 = "jjj10.rbd";
  r = io_ctx.exec(oid2, "rbd", "snap_list", bl, bl2);
  cout << "snap_list result=" << r << std::endl;
  r = io_ctx.exec(oid2, "rbd", "snap_add", bl, bl2);
  cout << "snap_add result=" << r << std::endl;

  if (r > 0) {
    char *s = bl2.c_str();
    for (int i=0; i<r; i++, s += strlen(s) + 1)
      cout << s << std::endl;
  }

  cout << "iterating over objects..." << std::endl;
  int num_objs = 0;
  for (ObjectIterator iter = io_ctx.objects_begin();
       iter != io_ctx.objects_end(); ++iter) {
    num_objs++;
    cout << "'" << *iter << "'" << std::endl;
  }
  cout << "iterated over " << num_objs << " objects." << std::endl;
  map<string, bufferlist> attrset;
  io_ctx.getxattrs(oid, attrset);

  map<string, bufferlist>::iterator it;
  for (it = attrset.begin(); it != attrset.end(); ++it) {
    cout << "xattr: " << it->first << std::endl;
  }

  r = io_ctx.remove(oid);
  cout << "remove result=" << r << std::endl;
  rados.shutdown();

  return 0;
}

<|endoftext|>


<commit_before>//
// Copyright Copyright 2009-2019, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

namespace date {};
using namespace date;

#include <sstream>
#include <chrono>
#include <date/date.h> // This file is to allow std::chrono types to be output to a stream
#include <thread>
#include "connector_test.hpp"

using namespace std;
using namespace std::chrono;

namespace mtconnect {
  namespace test {
    // Registers the fixture into the 'registry'
    CPPUNIT_TEST_SUITE_REGISTRATION(ConnectorTest);

    void ConnectorTest::setUp()
    {
      CPPUNIT_ASSERT(!create_listener(m_server, 0, "127.0.0.1"));
      m_port = m_server->get_listening_port();
      m_connector.reset(new TestConnector("127.0.0.1", m_port));
      m_connector->m_disconnected = true;
    }

    void ConnectorTest::thread()
    {
      m_connector->connect();
    }

    void ConnectorTest::tearDown()
    {
      m_server.reset();
      m_serverSocket.reset();
      stop();
      wait();
      m_connector.reset();
    }

    void ConnectorTest::testConnection()
    {
      CPPUNIT_ASSERT(m_connector->m_disconnected);
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      this_thread::sleep_for(100ms);
      CPPUNIT_ASSERT(m_serverSocket.get());
      CPPUNIT_ASSERT(!m_connector->m_disconnected);
    }

    void ConnectorTest::testDataCapture()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      string command("Hello Connector\n");
      CPPUNIT_ASSERT((size_t) m_serverSocket->write(command.c_str(),
                                                    command.length()) == command.length());
      this_thread::sleep_for(1000ms);

      // \n is stripped from the posted data.
      CPPUNIT_ASSERT_EQUAL(command.substr(0, command.length() - 1), m_connector->m_data);
    }

    void ConnectorTest::testDisconnect()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      this_thread::sleep_for(1000ms);
      CPPUNIT_ASSERT(m_serverSocket.get());
      CPPUNIT_ASSERT(!m_connector->m_disconnected);
      m_serverSocket.reset();
      this_thread::sleep_for(1000ms);
      CPPUNIT_ASSERT(m_connector->m_disconnected);
    }

    void ConnectorTest::testProtocolCommand()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      const auto cmd = "* Hello Connector\n";
      CPPUNIT_ASSERT_EQUAL(strlen(cmd), (size_t) m_serverSocket->write(cmd, strlen(cmd)));
      this_thread::sleep_for(1000ms);

      // \n is stripped from the posted data.
      CPPUNIT_ASSERT(strncmp(cmd, m_connector->m_command.c_str(), strlen(cmd) - 1) == 0);
    }

    void ConnectorTest::testHeartbeat()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      // Receive initial heartbeat request "* PING\n"
      char buf[1024] = {0};
      CPPUNIT_ASSERT_EQUAL(7L, m_serverSocket->read(buf, 1023, 5000));
      buf[7] = '\0';
      CPPUNIT_ASSERT(strcmp(buf, "* PING\n") == 0);

      // Respond to the heartbeat of 1 second
      const auto pong = "* PONG 1000\n";
      CPPUNIT_ASSERT_EQUAL(strlen(pong), (size_t) m_serverSocket->write(pong, strlen(pong)));
      this_thread::sleep_for(1000ms);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{1000}, m_connector->heartbeatFrequency());
    }

    void ConnectorTest::testHeartbeatPong()
    {
      testHeartbeat();

      auto last_heartbeat = system_clock::now();

      // Test to make sure we can send and receive 5 heartbeats
      for (int i = 0; i < 5; i++)
      {
        // Receive initial heartbeat request "* PING\n"

        char buf[1024] = {0};
        CPPUNIT_ASSERT(m_serverSocket->read(buf, 1023, 1100) > 0);
        buf[7] = '\0';
        CPPUNIT_ASSERT(!strcmp(buf, "* PING\n"));

        auto now = system_clock::now();
        CPPUNIT_ASSERT(now - last_heartbeat < 2000ms);
        last_heartbeat = now;

        // Respond to the heartbeat of 1 second
        const auto pong = "* PONG 1000\n";
        CPPUNIT_ASSERT_EQUAL(strlen(pong), (size_t) m_serverSocket->write(pong, strlen(pong)));
        this_thread::sleep_for(10ms);

        CPPUNIT_ASSERT(!m_connector->m_disconnected);
      }
    }

    void ConnectorTest::testHeartbeatTimeout()
    {
      testHeartbeat();
      this_thread::sleep_for(2100ms);

      CPPUNIT_ASSERT(m_connector->m_disconnected);
    }

    void ConnectorTest::testLegacyTimeout()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      char buf[1024] = {0};
      CPPUNIT_ASSERT_EQUAL(7L, m_serverSocket->read(buf, 1023, 5000));
      buf[7] = '\0';
      CPPUNIT_ASSERT(!strcmp(buf, "* PING\n"));

      // Write some data...
      const auto cmd = "* Hello Connector\n";
      CPPUNIT_ASSERT_EQUAL(strlen(cmd), (size_t) m_serverSocket->write(cmd, strlen(cmd)));

      // No pings, but timeout after 5 seconds of silence
      this_thread::sleep_for(11000ms);

      CPPUNIT_ASSERT(m_connector->m_disconnected);
    }

    void ConnectorTest::testParseBuffer()
    {
      // Test data fragmentation
      m_connector->pushData("Hello");
      CPPUNIT_ASSERT_EQUAL((string) "", m_connector->m_data);

      m_connector->pushData(" There\n");
      CPPUNIT_ASSERT_EQUAL((string) "Hello There", m_connector->m_data);
      m_connector->m_data.clear();

      m_connector->pushData("Hello");
      CPPUNIT_ASSERT_EQUAL((string) "", m_connector->m_data);

      m_connector->pushData(" There\nAnd ");
      CPPUNIT_ASSERT_EQUAL((string) "Hello There", m_connector->m_data);

      m_connector->pushData("Again\nXXX");
      CPPUNIT_ASSERT_EQUAL((string) "And Again", m_connector->m_data);
    }

    void ConnectorTest::testParseBufferFraming()
    {
      m_connector->m_list.clear();
      m_connector->pushData("first\nseco");
      m_connector->pushData("nd\nthird\nfourth\nfifth");
      CPPUNIT_ASSERT_EQUAL((size_t) 4, m_connector->m_list.size());
      CPPUNIT_ASSERT_EQUAL((string) "first", m_connector->m_list[0]);
      CPPUNIT_ASSERT_EQUAL((string) "second", m_connector->m_list[1]);
      CPPUNIT_ASSERT_EQUAL((string) "third", m_connector->m_list[2]);
      CPPUNIT_ASSERT_EQUAL((string) "fourth", m_connector->m_list[3]);
    }

    void ConnectorTest::testSendCommand()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      // Receive initial heartbeat request "* PING\n"
      char buf[1024];
      CPPUNIT_ASSERT_EQUAL(7L, m_serverSocket->read(buf, 1023, 1000));
      buf[7] = '\0';
      CPPUNIT_ASSERT(!strcmp(buf, "* PING\n"));

      m_connector->sendCommand("Hello There;");

      CPPUNIT_ASSERT_EQUAL(15L, m_serverSocket->read(buf, 1023, 1000));
      buf[15] = '\0';
      CPPUNIT_ASSERT(!strcmp(buf, "* Hello There;\n"));
    }

    void ConnectorTest::testIPV6Connection()
    {
#if !defined(WIN32) || (NTDDI_VERSION >= NTDDI_VISTA)
      m_connector.reset();

      CPPUNIT_ASSERT(!create_listener(m_server, 0, "::1"));
      m_port = m_server->get_listening_port();
      m_connector.reset(new TestConnector("::1", m_port));
      m_connector->m_disconnected = true;

      CPPUNIT_ASSERT(m_connector->m_disconnected);
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      this_thread::sleep_for(100ms);
      CPPUNIT_ASSERT(m_serverSocket.get());
      CPPUNIT_ASSERT(!m_connector->m_disconnected);
#endif
    }

    void ConnectorTest::testStartHeartbeats()
    {
      CPPUNIT_ASSERT(!m_connector->heartbeats());

      string line = "* PONG ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONK ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONG      ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONG FLAB";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONG       123";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{123}, m_connector->heartbeatFrequency());

      m_connector->resetHeartbeats();

      line = "* PONG       456 ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{456}, m_connector->heartbeatFrequency());

      line = "* PONG 323";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{323}, m_connector->heartbeatFrequency());
    }
  }
}
<commit_msg>Retry recv on timeout.<commit_after>//
// Copyright Copyright 2009-2019, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

namespace date {};
using namespace date;

#include <sstream>
#include <chrono>
#include <date/date.h> // This file is to allow std::chrono types to be output to a stream
#include <thread>
#include "connector_test.hpp"

using namespace std;
using namespace std::chrono;

namespace mtconnect {
  namespace test {
    // Registers the fixture into the 'registry'
    CPPUNIT_TEST_SUITE_REGISTRATION(ConnectorTest);

    void ConnectorTest::setUp()
    {
      CPPUNIT_ASSERT(!create_listener(m_server, 0, "127.0.0.1"));
      m_port = m_server->get_listening_port();
      m_connector.reset(new TestConnector("127.0.0.1", m_port));
      m_connector->m_disconnected = true;
    }

    void ConnectorTest::thread()
    {
      m_connector->connect();
    }

    void ConnectorTest::tearDown()
    {
      m_server.reset();
      m_serverSocket.reset();
      stop();
      wait();
      m_connector.reset();
    }

    void ConnectorTest::testConnection()
    {
      CPPUNIT_ASSERT(m_connector->m_disconnected);
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      this_thread::sleep_for(100ms);
      CPPUNIT_ASSERT(m_serverSocket.get());
      CPPUNIT_ASSERT(!m_connector->m_disconnected);
    }

    void ConnectorTest::testDataCapture()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      string command("Hello Connector\n");
      CPPUNIT_ASSERT((size_t) m_serverSocket->write(command.c_str(),
                                                    command.length()) == command.length());
      this_thread::sleep_for(1000ms);

      // \n is stripped from the posted data.
      CPPUNIT_ASSERT_EQUAL(command.substr(0, command.length() - 1), m_connector->m_data);
    }

    void ConnectorTest::testDisconnect()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      this_thread::sleep_for(1000ms);
      CPPUNIT_ASSERT(m_serverSocket.get());
      CPPUNIT_ASSERT(!m_connector->m_disconnected);
      m_serverSocket.reset();
      this_thread::sleep_for(1000ms);
      CPPUNIT_ASSERT(m_connector->m_disconnected);
    }

    void ConnectorTest::testProtocolCommand()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      const auto cmd = "* Hello Connector\n";
      CPPUNIT_ASSERT_EQUAL(strlen(cmd), (size_t) m_serverSocket->write(cmd, strlen(cmd)));
      this_thread::sleep_for(1000ms);

      // \n is stripped from the posted data.
      CPPUNIT_ASSERT(strncmp(cmd, m_connector->m_command.c_str(), strlen(cmd) - 1) == 0);
    }

    void ConnectorTest::testHeartbeat()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      // Receive initial heartbeat request "* PING\n"
      char buf[1024] = {0};
      CPPUNIT_ASSERT_EQUAL(7L, m_serverSocket->read(buf, 1023, 5000));
      buf[7] = '\0';
      CPPUNIT_ASSERT(strcmp(buf, "* PING\n") == 0);

      // Respond to the heartbeat of 1 second
      const auto pong = "* PONG 1000\n";
      CPPUNIT_ASSERT_EQUAL(strlen(pong), (size_t) m_serverSocket->write(pong, strlen(pong)));
      this_thread::sleep_for(1000ms);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{1000}, m_connector->heartbeatFrequency());
    }

    void ConnectorTest::testHeartbeatPong()
    {
      testHeartbeat();

      auto last_heartbeat = system_clock::now();

      // Test to make sure we can send and receive 5 heartbeats
      for (int i = 0; i < 5; i++)
      {
        // Receive initial heartbeat request "* PING\n"

        char buf[1024] = {0};
        CPPUNIT_ASSERT(m_serverSocket->read(buf, 1023, 1100) > 0);
        buf[7] = '\0';
        CPPUNIT_ASSERT(!strcmp(buf, "* PING\n"));

        auto now = system_clock::now();
        CPPUNIT_ASSERT(now - last_heartbeat < 2000ms);
        last_heartbeat = now;

        // Respond to the heartbeat of 1 second
        const auto pong = "* PONG 1000\n";
        CPPUNIT_ASSERT_EQUAL(strlen(pong), (size_t) m_serverSocket->write(pong, strlen(pong)));
        this_thread::sleep_for(10ms);

        CPPUNIT_ASSERT(!m_connector->m_disconnected);
      }
    }

    void ConnectorTest::testHeartbeatTimeout()
    {
      testHeartbeat();
      this_thread::sleep_for(2100ms);

      CPPUNIT_ASSERT(m_connector->m_disconnected);
    }

    void ConnectorTest::testLegacyTimeout()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      char buf[1024] = {0};
      CPPUNIT_ASSERT_EQUAL(7L, m_serverSocket->read(buf, 1023, 5000));
      buf[7] = '\0';
      CPPUNIT_ASSERT(!strcmp(buf, "* PING\n"));

      // Write some data...
      const auto cmd = "* Hello Connector\n";
      CPPUNIT_ASSERT_EQUAL(strlen(cmd), (size_t) m_serverSocket->write(cmd, strlen(cmd)));

      // No pings, but timeout after 5 seconds of silence
      this_thread::sleep_for(11000ms);

      CPPUNIT_ASSERT(m_connector->m_disconnected);
    }

    void ConnectorTest::testParseBuffer()
    {
      // Test data fragmentation
      m_connector->pushData("Hello");
      CPPUNIT_ASSERT_EQUAL((string) "", m_connector->m_data);

      m_connector->pushData(" There\n");
      CPPUNIT_ASSERT_EQUAL((string) "Hello There", m_connector->m_data);
      m_connector->m_data.clear();

      m_connector->pushData("Hello");
      CPPUNIT_ASSERT_EQUAL((string) "", m_connector->m_data);

      m_connector->pushData(" There\nAnd ");
      CPPUNIT_ASSERT_EQUAL((string) "Hello There", m_connector->m_data);

      m_connector->pushData("Again\nXXX");
      CPPUNIT_ASSERT_EQUAL((string) "And Again", m_connector->m_data);
    }

    void ConnectorTest::testParseBufferFraming()
    {
      m_connector->m_list.clear();
      m_connector->pushData("first\nseco");
      m_connector->pushData("nd\nthird\nfourth\nfifth");
      CPPUNIT_ASSERT_EQUAL((size_t) 4, m_connector->m_list.size());
      CPPUNIT_ASSERT_EQUAL((string) "first", m_connector->m_list[0]);
      CPPUNIT_ASSERT_EQUAL((string) "second", m_connector->m_list[1]);
      CPPUNIT_ASSERT_EQUAL((string) "third", m_connector->m_list[2]);
      CPPUNIT_ASSERT_EQUAL((string) "fourth", m_connector->m_list[3]);
    }

    void ConnectorTest::testSendCommand()
    {
      // Start the accept thread
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      CPPUNIT_ASSERT(m_serverSocket.get());

      // Receive initial heartbeat request "* PING\n"
      char buf[1024];
      CPPUNIT_ASSERT_EQUAL(7L, m_serverSocket->read(buf, 1023, 1000));
      buf[7] = '\0';
      CPPUNIT_ASSERT(!strcmp(buf, "* PING\n"));

      m_connector->sendCommand("Hello There;");

      long len;
      int times = 0;
      do {
        len = m_serverSocket->read(buf, 1023, 1000);
      }
      while (times++ < 5 && (len == TIMEOUT || len == WOULDBLOCK));

      CPPUNIT_ASSERT_EQUAL(15L, len);
      buf[15] = '\0';
      CPPUNIT_ASSERT(!strcmp(buf, "* Hello There;\n"));
    }

    void ConnectorTest::testIPV6Connection()
    {
#if !defined(WIN32) || (NTDDI_VERSION >= NTDDI_VISTA)
      m_connector.reset();

      CPPUNIT_ASSERT(!create_listener(m_server, 0, "::1"));
      m_port = m_server->get_listening_port();
      m_connector.reset(new TestConnector("::1", m_port));
      m_connector->m_disconnected = true;

      CPPUNIT_ASSERT(m_connector->m_disconnected);
      start();

      CPPUNIT_ASSERT_EQUAL(0, m_server->accept(m_serverSocket));
      this_thread::sleep_for(100ms);
      CPPUNIT_ASSERT(m_serverSocket.get());
      CPPUNIT_ASSERT(!m_connector->m_disconnected);
#endif
    }

    void ConnectorTest::testStartHeartbeats()
    {
      CPPUNIT_ASSERT(!m_connector->heartbeats());

      string line = "* PONG ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONK ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONG      ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONG FLAB";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(!m_connector->heartbeats());

      line = "* PONG       123";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{123}, m_connector->heartbeatFrequency());

      m_connector->resetHeartbeats();

      line = "* PONG       456 ";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{456}, m_connector->heartbeatFrequency());

      line = "* PONG 323";
      m_connector->startHeartbeats(line);

      CPPUNIT_ASSERT(m_connector->heartbeats());
      CPPUNIT_ASSERT_EQUAL(std::chrono::milliseconds{323}, m_connector->heartbeatFrequency());
    }
  }
}
<|endoftext|>


<commit_before>#include <iostream>

#include "headings.h"
#include "libaps2.h"
#include "constants.h"

#include <concol.h>

using namespace std;

int get_device_id() {
  cout << "Choose device ID [0]: ";
  string input = "";
  getline(cin, input);

  if (input.length() == 0) {
    return 0;
  }
  int device_id;
  stringstream mystream(input);

  mystream >> device_id;
  return device_id;
}

uint64_t get_mac_input() {
  cout << "New MAC address [ENTER to skip]: ";
  string input = "";
  getline(cin, input);

  if (input.length() == 0) {
    return 0;
  }
  stringstream mystream(input);
  uint64_t mac_addr;
  mystream >> std::hex >> mac_addr;

  cout << "Received " << hexn<12> << mac_addr << endl;
  return mac_addr;
}

string get_ip_input() {
  cout << "New IP address [ENTER to skip]: ";
  string input = "";
  getline(cin, input);
  return input;
}

bool spi_prompt() {
  cout << "Do you want to program the SPI startup sequence? [y/N]: ";
  string input = "";
  getline(cin, input);
  if (input.length() == 0) {
    return false;
  }
  stringstream mystream(input);
  char response;
  mystream >> response;
  switch (response) {
    case 'y':
    case 'Y':
      return true;
      break;
    case 'n':
    case 'N':
    default:
      return false;
  }
}

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

  concol::concolinit();
  cout << concol::RED << "BBN AP2 Flash Test Executable" << concol::RESET << endl;

  int dbgLevel = 8;
  if (argc >= 2) {
    dbgLevel = atoi(argv[1]);
  }

  set_logging_level(dbgLevel);
  set_log("stdout");

  cout << concol::RED << "Enumerating devices" << concol::RESET << endl;

  int numDevices = get_numDevices();

  cout << concol::RED << numDevices << " APS device" << (numDevices > 1 ? "s": "")  << " found" << concol::RESET << endl;

  if (numDevices < 1)
  	return 0;

  cout << concol::RED << "Attempting to get serials" << concol::RESET << endl;

  const char ** serialBuffer = new const char*[numDevices];
  get_deviceSerials(serialBuffer);

  for (int cnt=0; cnt < numDevices; cnt++) {
  	cout << concol::RED << "Device " << cnt << " serial #: " << serialBuffer[cnt] << concol::RESET << endl;
  }

  string deviceSerial;

  if (numDevices == 1) {
    deviceSerial = string(serialBuffer[0]);
  } else {
    deviceSerial = string(serialBuffer[get_device_id()]);
  }

  cout << concol::RED << "Connecting to device serial #: " << deviceSerial << concol::RESET << endl;

  connect_APS(deviceSerial.c_str());

  double uptime = get_uptime(deviceSerial.c_str());

  cout << concol::RED << "Uptime for device " << deviceSerial << " is " << uptime << " seconds" << concol::RESET << endl;

  cout << "Programmed MAC and IP address at 0x00FF0000 are " << endl;
  cout << "MAC addr: " << hexn<12> << get_mac_addr(deviceSerial.c_str()) << endl;
  cout << "IP addr: " << get_ip_addr(deviceSerial.c_str()) << endl;

  // write a new MAC address
  uint64_t mac_addr = get_mac_input();
  if (mac_addr != 0) {
    cout << concol::RED << "Writing new MAC address" << concol::RESET << endl;
    set_mac_addr(deviceSerial.c_str(), mac_addr);
  }

  // write a new IP address
  string ip_addr = get_ip_input();
  if (ip_addr != "") {
    cout << concol::RED << "Writing new IP address" << concol::RESET << endl;
    set_ip_addr(deviceSerial.c_str(), ip_addr.c_str());
  }

  // read SPI setup sequence
  uint32_t setup[32];
  read_flash(deviceSerial.c_str(), 0x0, 32, setup);
  cout << "Programmed setup SPI sequence:" << endl;
  for (size_t ct=0; ct < 32; ct++) {
    cout << hexn<8> << setup[ct] << " ";
    if (ct % 4 == 3) cout << endl;
  }

  // write new SPI setup sequence
  if (spi_prompt()) {
    cout << concol::RED << "Writing SPI startup sequence" << concol::RESET << endl;
    write_SPI_setup(deviceSerial.c_str());
  }
  /*
  cout << concol::RED << "Reading flash addr 0x00FA0000" << concol::RESET << endl;
  uint32_t buffer[4] = {0, 0, 0, 0};
  read_flash(deviceSerial.c_str(), 0x00FA0000, 4, buffer);
  cout << "Received " << hexn<8> << buffer[0] << " " << hexn<8> << buffer[1];
  cout << " " << hexn<8> << buffer[2] << " " << hexn<8> << buffer[3] << endl;

  cout << concol::RED << "Erasing/writing flash addr 0x00FA0000 (128 words)" << concol::RESET << endl;
  vector<uint32_t> testData;
  for (size_t ct=0; ct<128; ct++){
    testData.push_back(0x00FA0000 + static_cast<uint32_t>(ct));
  }
  write_flash(deviceSerial.c_str(), 0x00FA0000, testData.data(), testData.size());

  cout << concol::RED << "Reading flash addr 0x00FA0000" << concol::RESET << endl;
  read_flash(deviceSerial.c_str(), 0x00FA0000, 4, buffer);
  cout << "Received " << hexn<8> << buffer[0] << " " << hexn<8> << buffer[1];
  cout << " " << hexn<8> << buffer[2] << " " << hexn<8> << buffer[3] << endl;

  cout << concol::RED << "Erasing/writing flash addr 0x00FA0000 (2 words)" << concol::RESET << endl;
  buffer[0] = 0xBADD1234;
  buffer[1] = 0x1234F00F;
  write_flash(deviceSerial.c_str(), 0x00FA0000, buffer, 2);

  cout << concol::RED << "Reading flash addr 0x00FA0000" << concol::RESET << endl;
  read_flash(deviceSerial.c_str(), 0x00FA0000, 4, buffer);
  cout << "Received " << hexn<8> << buffer[0] << " " << hexn<8> << buffer[1];
  cout << " " << hexn<8> << buffer[2] << " " << hexn<8> << buffer[3] << endl;
  */

  disconnect_APS(deviceSerial.c_str());

  delete[] serialBuffer;

  cout << concol::RED << "Finished!" << concol::RESET << endl;

  return 0;
}
<commit_msg>Reduce default logging level<commit_after>#include <iostream>

#include "headings.h"
#include "libaps2.h"
#include "constants.h"

#include <concol.h>

using namespace std;

int get_device_id() {
  cout << "Choose device ID [0]: ";
  string input = "";
  getline(cin, input);

  if (input.length() == 0) {
    return 0;
  }
  int device_id;
  stringstream mystream(input);

  mystream >> device_id;
  return device_id;
}

uint64_t get_mac_input() {
  cout << "New MAC address [ENTER to skip]: ";
  string input = "";
  getline(cin, input);

  if (input.length() == 0) {
    return 0;
  }
  stringstream mystream(input);
  uint64_t mac_addr;
  mystream >> std::hex >> mac_addr;

  cout << "Received " << hexn<12> << mac_addr << endl;
  return mac_addr;
}

string get_ip_input() {
  cout << "New IP address [ENTER to skip]: ";
  string input = "";
  getline(cin, input);
  return input;
}

bool spi_prompt() {
  cout << "Do you want to program the SPI startup sequence? [y/N]: ";
  string input = "";
  getline(cin, input);
  if (input.length() == 0) {
    return false;
  }
  stringstream mystream(input);
  char response;
  mystream >> response;
  switch (response) {
    case 'y':
    case 'Y':
      return true;
      break;
    case 'n':
    case 'N':
    default:
      return false;
  }
}

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

  concol::concolinit();
  cout << concol::RED << "BBN AP2 Flash Test Executable" << concol::RESET << endl;

  int dbgLevel = 4;
  if (argc >= 2) {
    dbgLevel = atoi(argv[1]);
  }

  set_logging_level(dbgLevel);
  set_log("stdout");

  cout << concol::RED << "Enumerating devices" << concol::RESET << endl;

  int numDevices = get_numDevices();

  cout << concol::RED << numDevices << " APS device" << (numDevices > 1 ? "s": "")  << " found" << concol::RESET << endl;

  if (numDevices < 1)
  	return 0;

  cout << concol::RED << "Attempting to get serials" << concol::RESET << endl;

  const char ** serialBuffer = new const char*[numDevices];
  get_deviceSerials(serialBuffer);

  for (int cnt=0; cnt < numDevices; cnt++) {
  	cout << concol::RED << "Device " << cnt << " serial #: " << serialBuffer[cnt] << concol::RESET << endl;
  }

  string deviceSerial;

  if (numDevices == 1) {
    deviceSerial = string(serialBuffer[0]);
  } else {
    deviceSerial = string(serialBuffer[get_device_id()]);
  }

  cout << concol::RED << "Connecting to device serial #: " << deviceSerial << concol::RESET << endl;

  connect_APS(deviceSerial.c_str());

  double uptime = get_uptime(deviceSerial.c_str());

  cout << concol::RED << "Uptime for device " << deviceSerial << " is " << uptime << " seconds" << concol::RESET << endl;

  cout << "Programmed MAC and IP address at 0x00FF0000 are " << endl;
  cout << "MAC addr: " << hexn<12> << get_mac_addr(deviceSerial.c_str()) << endl;
  cout << "IP addr: " << get_ip_addr(deviceSerial.c_str()) << endl;

  // write a new MAC address
  uint64_t mac_addr = get_mac_input();
  if (mac_addr != 0) {
    cout << concol::RED << "Writing new MAC address" << concol::RESET << endl;
    set_mac_addr(deviceSerial.c_str(), mac_addr);
  }

  // write a new IP address
  string ip_addr = get_ip_input();
  if (ip_addr != "") {
    cout << concol::RED << "Writing new IP address" << concol::RESET << endl;
    set_ip_addr(deviceSerial.c_str(), ip_addr.c_str());
  }

  // read SPI setup sequence
  uint32_t setup[32];
  read_flash(deviceSerial.c_str(), 0x0, 32, setup);
  cout << "Programmed setup SPI sequence:" << endl;
  for (size_t ct=0; ct < 32; ct++) {
    cout << hexn<8> << setup[ct] << " ";
    if (ct % 4 == 3) cout << endl;
  }

  // write new SPI setup sequence
  if (spi_prompt()) {
    cout << concol::RED << "Writing SPI startup sequence" << concol::RESET << endl;
    write_SPI_setup(deviceSerial.c_str());
  }
  /*
  cout << concol::RED << "Reading flash addr 0x00FA0000" << concol::RESET << endl;
  uint32_t buffer[4] = {0, 0, 0, 0};
  read_flash(deviceSerial.c_str(), 0x00FA0000, 4, buffer);
  cout << "Received " << hexn<8> << buffer[0] << " " << hexn<8> << buffer[1];
  cout << " " << hexn<8> << buffer[2] << " " << hexn<8> << buffer[3] << endl;

  cout << concol::RED << "Erasing/writing flash addr 0x00FA0000 (128 words)" << concol::RESET << endl;
  vector<uint32_t> testData;
  for (size_t ct=0; ct<128; ct++){
    testData.push_back(0x00FA0000 + static_cast<uint32_t>(ct));
  }
  write_flash(deviceSerial.c_str(), 0x00FA0000, testData.data(), testData.size());

  cout << concol::RED << "Reading flash addr 0x00FA0000" << concol::RESET << endl;
  read_flash(deviceSerial.c_str(), 0x00FA0000, 4, buffer);
  cout << "Received " << hexn<8> << buffer[0] << " " << hexn<8> << buffer[1];
  cout << " " << hexn<8> << buffer[2] << " " << hexn<8> << buffer[3] << endl;

  cout << concol::RED << "Erasing/writing flash addr 0x00FA0000 (2 words)" << concol::RESET << endl;
  buffer[0] = 0xBADD1234;
  buffer[1] = 0x1234F00F;
  write_flash(deviceSerial.c_str(), 0x00FA0000, buffer, 2);

  cout << concol::RED << "Reading flash addr 0x00FA0000" << concol::RESET << endl;
  read_flash(deviceSerial.c_str(), 0x00FA0000, 4, buffer);
  cout << "Received " << hexn<8> << buffer[0] << " " << hexn<8> << buffer[1];
  cout << " " << hexn<8> << buffer[2] << " " << hexn<8> << buffer[3] << endl;
  */

  disconnect_APS(deviceSerial.c_str());

  delete[] serialBuffer;

  cout << concol::RED << "Finished!" << concol::RESET << endl;

  return 0;
}
<|endoftext|>


<commit_before>#include "rubymotion.h"
#include "motion-game.h"
#include <dlfcn.h>

/// @class Scene < Node
/// This class represents a scene, an independent screen or stage of the
/// application workflow. A scene is responsible for handling events from the
/// device, providing a physics world for the sprites, and also starting the
/// game loop.
/// An application must have at least one scene, and the +Scene+ class is
/// designed to be subclassed.

VALUE rb_cScene = Qnil;

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
extern "C" {
    void rb_repl_new(VALUE);
}
#endif

enum mc_Scene_EventType {
    ON_BEGIN,
    ON_MOVE,
    ON_END,
    ON_CANCEL
};

class mc_Scene : public cocos2d::LayerColor {
    public:
	cocos2d::Scene *scene;
	VALUE obj;
	SEL update_sel;
    cocos2d::EventListenerTouchOneByOne *touch_listener;

    mc_Scene() {
	obj = Qnil;
	touch_listener = NULL;
#if CC_TARGET_OS_IPHONE || CC_TARGET_OS_APPLETV
	update_sel = rb_selector("update:");
#else
	update_sel = rb_selector("update");
#endif
    }

    static mc_Scene *create(void) {
	auto scene = new mc_Scene();
	scene->initWithColor(cocos2d::Color4B::BLACK);
	return scene;
    }

    virtual void update(float delta) {
	LayerColor::update(delta);
	VALUE arg = DBL2NUM(delta);
	rb_send(obj, update_sel, 1, &arg);
    }

    void setBackgroundColor(cocos2d::Color3B color) {
	setColor(color);
	updateColor();
    }

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
    virtual void onEnter() {
	cocos2d::LayerColor::onEnter();
	rb_repl_new(this->obj);
    }
#endif
};

#define SCENE(obj) _COCOS_WRAP_GET(obj, mc_Scene)

extern "C"
cocos2d::Scene *
rb_any_to_scene(VALUE obj)
{
    if (rb_obj_is_kind_of(obj, rb_cScene)) {
	return SCENE(obj)->scene;
    }
    rb_raise(rb_eArgError, "expected Scene object");
}

static VALUE
scene_alloc(VALUE rcv, SEL sel)
{
    auto layer = mc_Scene::create();

    auto scene = cocos2d::Scene::createWithPhysics();
    scene->addChild(layer);
    layer->scene = scene;

    VALUE obj = rb_cocos2d_object_new(layer, rcv);
    layer->obj = rb_retain(obj);
    return obj;
}

/// @group Constructors

/// @method #initialize
/// The default initializer. Subclasses can construct the scene interface in
/// this method, as well as providing an implementation for {#update}, then
/// run the update loop by calling {#start_update}.
/// @return [Scene] the receiver.

static VALUE
scene_initialize(VALUE rcv, SEL sel)
{
    return rcv;
}

/// @group Update Loop

/// @method #start_update
/// Starts the update loop. The +#update+ method will be called on this object
/// for every frame.
/// @return [self] the receiver.

static VALUE
scene_start_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->scheduleUpdate();
    return rcv;
}

/// @method #stop_update
/// Stops the update loop. The +#update+ method will no longer be called on
/// this object.
/// @return [self] the receiver.

static VALUE
scene_stop_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->unscheduleUpdate();
    return rcv;
}

/// @method #update(delta)
/// The update loop method. Subclasses can provide a custom implementation of
/// this method. The default implementation is empty.
/// @param delta [Float] a value representing the amount of time, in seconds,
///   since the last time this method was called.
/// @return [self] the receiver.

static VALUE
scene_update(VALUE rcv, SEL sel, VALUE delta)
{
    // Do nothing.
    return rcv;
}

/// @group Events

static VALUE
scene_add_listener(VALUE rcv, cocos2d::EventListener *listener)
{
    auto scene = SCENE(rcv);
    scene->getEventDispatcher()->addEventListenerWithSceneGraphPriority(
	    listener, scene);
    return rcv;
}

static VALUE
scene_on_touch_event(VALUE rcv, SEL sel, mc_Scene_EventType type)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto scene = SCENE(rcv);
    if (scene->touch_listener == NULL) {
	scene->touch_listener = cocos2d::EventListenerTouchOneByOne::create();
    }
    else {
	scene->getEventDispatcher()->removeEventListener(scene->touch_listener);
    }
    auto lambda = [block](cocos2d::Touch *touch, cocos2d::Event *event) -> bool {
	VALUE touch_obj = rb_cocos2d_object_new(touch, rb_cTouch);
	return RTEST(rb_block_call(block, 1, &touch_obj));
    };

    switch (type) {
      case ON_BEGIN:
	scene->touch_listener->onTouchBegan = lambda;
	break;
      case ON_MOVE:
	scene->touch_listener->onTouchMoved = lambda;
	break;
      case ON_END:
	scene->touch_listener->onTouchEnded = lambda;
	break;
      case ON_CANCEL:
	scene->touch_listener->onTouchCancelled = lambda;
	break;
    }

    return scene_add_listener(rcv, scene->touch_listener);
}

/// @method #on_touch_begin
/// Starts listening for touch begin events on the receiver.
/// @yield [Events::Touch] the given block will be yield when a touch begin
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_begin(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_BEGIN);
}

/// @method #on_touch_end
/// Starts listening for touch end events on the receiver.
/// This method requires {#on_touch_begin} calling in order to catch the event.
/// @yield [Events::Touch] the given block will be yield when a touch end
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_end(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_END);
}

/// @method #on_touch_move
/// Starts listening for touch move events on the receiver.
/// This method requires {#on_touch_begin} calling in order to catch the event.
/// @yield [Events::Touch] the given block will be yield when a touch move
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_move(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_MOVE);
}

/// @method #on_touch_cancel
/// Starts listening for touch cancel events on the receiver.
/// This method requires {#on_touch_begin} calling in order to catch the event.
/// @yield [Events::Touch] the given block will be yield when a touch cancel
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_cancel(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_CANCEL);
}
/// @method #on_accelerate
/// Starts listening for accelerometer events on the receiver.
/// @yield [Events::Acceleration] the given block will be yield when an
///   accelerometer event is received from the device.
/// @return [self] the receiver.

static VALUE
scene_on_accelerate(VALUE rcv, SEL sel)
{
#if CC_TARGET_OS_APPLETV
    rb_raise(rb_eRuntimeError, "Not supported in tvOS");
#else
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    cocos2d::Device::setAccelerometerEnabled(true);
    auto listener = cocos2d::EventListenerAcceleration::create(
	[block](cocos2d::Acceleration *acc, cocos2d::Event *event) {
	    VALUE acc_obj = rb_cocos2d_object_new(acc, rb_cAcceleration);
	    rb_block_call(block, 1, &acc_obj);
	});

    return scene_add_listener(rcv, listener);
#endif
}

/// @method #on_contact_begin
/// Starts listening for contact begin events from the physics engine.
/// @yield [Events::PhysicsContact] the given block will be yield when a
///   contact event is received from the physics engine.
/// @return [self] the receiver.

static VALUE
scene_on_contact_begin(VALUE rcv, SEL sel)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto listener = cocos2d::EventListenerPhysicsContact::create();
    listener->onContactBegin = [block](cocos2d::PhysicsContact &contact) -> bool {
	return RTEST(rb_block_call(block, 0, NULL));
    };

    return scene_add_listener(rcv, listener);
}

/// @endgroup

/// @property #gravity
/// @return [Point] the gravity of the scene's physics world.

static VALUE
scene_gravity(VALUE rcv, SEL sel)
{
    return rb_ccvec2_to_obj(SCENE(rcv)->scene->getPhysicsWorld()->getGravity());
}

static VALUE
scene_gravity_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setGravity(rb_any_to_ccvec2(arg));
    return rcv;
}

/// @method #debug_physics?
/// @return [Boolean] whether the physics engine should draw debug lines.

static VALUE
scene_debug_physics(VALUE rcv, SEL sel)
{
    return SCENE(rcv)->scene->getPhysicsWorld()->getDebugDrawMask()
	== cocos2d::PhysicsWorld::DEBUGDRAW_NONE ? Qfalse : Qtrue;
}

/// @method #debug_physics=(value)
/// Set to draw the debug line.
/// @param value [Boolean] true if draw debug lines.

static VALUE
scene_debug_physics_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setDebugDrawMask(RTEST(arg)
	    ? cocos2d::PhysicsWorld::DEBUGDRAW_ALL
	    : cocos2d::PhysicsWorld::DEBUGDRAW_NONE);
    return arg;
}

/// @method #background_color=(color)
/// Set background color for scene.
/// @param color [Color] background color for scene.

static VALUE
scene_background_color_set(VALUE rcv, SEL sel, VALUE val)
{
    SCENE(rcv)->setBackgroundColor(rb_any_to_cccolor3(val));
    return val;
}

extern "C"
void
Init_Layer(void)
{
    rb_cScene = rb_define_class_under(rb_mMC, "Scene", rb_cNode);
    // rb_register_cocos2d_object_finalizer(rb_cScene); removed because rb_cScene inherits rb_cNode and it already has finalizer.

    rb_define_singleton_method(rb_cScene, "alloc", scene_alloc, 0);
    rb_define_method(rb_cScene, "initialize", scene_initialize, 0);
    rb_define_method(rb_cScene, "start_update", scene_start_update, 0);
    rb_define_method(rb_cScene, "stop_update", scene_stop_update, 0);
    rb_define_method(rb_cScene, "update", scene_update, 1);
    rb_define_method(rb_cScene, "on_touch_begin", scene_on_touch_begin, 0);
    rb_define_method(rb_cScene, "on_touch_end", scene_on_touch_end, 0);
    rb_define_method(rb_cScene, "on_touch_move", scene_on_touch_move, 0);
    rb_define_method(rb_cScene, "on_touch_cancel", scene_on_touch_cancel, 0);
    rb_define_method(rb_cScene, "on_accelerate", scene_on_accelerate, 0);
    rb_define_method(rb_cScene, "on_contact_begin", scene_on_contact_begin, 0);
    rb_define_method(rb_cScene, "gravity", scene_gravity, 0);
    rb_define_method(rb_cScene, "gravity=", scene_gravity_set, 1);
    rb_define_method(rb_cScene, "debug_physics?", scene_debug_physics, 0);
    rb_define_method(rb_cScene, "debug_physics=", scene_debug_physics_set, 1);
    rb_define_method(rb_cScene, "background_color=", scene_background_color_set, 1);
    rb_define_method(rb_cScene, "color=", scene_background_color_set, 1); // depricated
}
<commit_msg>[#69] add dummy implementation when call Scene#on_touch_{end, move, cancel} without #on_touch_begin<commit_after>#include "rubymotion.h"
#include "motion-game.h"
#include <dlfcn.h>

/// @class Scene < Node
/// This class represents a scene, an independent screen or stage of the
/// application workflow. A scene is responsible for handling events from the
/// device, providing a physics world for the sprites, and also starting the
/// game loop.
/// An application must have at least one scene, and the +Scene+ class is
/// designed to be subclassed.

VALUE rb_cScene = Qnil;

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
extern "C" {
    void rb_repl_new(VALUE);
}
#endif

enum mc_Scene_EventType {
    ON_BEGIN,
    ON_MOVE,
    ON_END,
    ON_CANCEL
};

class mc_Scene : public cocos2d::LayerColor {
    public:
	cocos2d::Scene *scene;
	VALUE obj;
	SEL update_sel;
    cocos2d::EventListenerTouchOneByOne *touch_listener;

    mc_Scene() {
	obj = Qnil;
	touch_listener = NULL;
#if CC_TARGET_OS_IPHONE || CC_TARGET_OS_APPLETV
	update_sel = rb_selector("update:");
#else
	update_sel = rb_selector("update");
#endif
    }

    static mc_Scene *create(void) {
	auto scene = new mc_Scene();
	scene->initWithColor(cocos2d::Color4B::BLACK);
	return scene;
    }

    virtual void update(float delta) {
	LayerColor::update(delta);
	VALUE arg = DBL2NUM(delta);
	rb_send(obj, update_sel, 1, &arg);
    }

    void setBackgroundColor(cocos2d::Color3B color) {
	setColor(color);
	updateColor();
    }

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
    virtual void onEnter() {
	cocos2d::LayerColor::onEnter();
	rb_repl_new(this->obj);
    }
#endif
};

#define SCENE(obj) _COCOS_WRAP_GET(obj, mc_Scene)

extern "C"
cocos2d::Scene *
rb_any_to_scene(VALUE obj)
{
    if (rb_obj_is_kind_of(obj, rb_cScene)) {
	return SCENE(obj)->scene;
    }
    rb_raise(rb_eArgError, "expected Scene object");
}

static VALUE
scene_alloc(VALUE rcv, SEL sel)
{
    auto layer = mc_Scene::create();

    auto scene = cocos2d::Scene::createWithPhysics();
    scene->addChild(layer);
    layer->scene = scene;

    VALUE obj = rb_cocos2d_object_new(layer, rcv);
    layer->obj = rb_retain(obj);
    return obj;
}

/// @group Constructors

/// @method #initialize
/// The default initializer. Subclasses can construct the scene interface in
/// this method, as well as providing an implementation for {#update}, then
/// run the update loop by calling {#start_update}.
/// @return [Scene] the receiver.

static VALUE
scene_initialize(VALUE rcv, SEL sel)
{
    return rcv;
}

/// @group Update Loop

/// @method #start_update
/// Starts the update loop. The +#update+ method will be called on this object
/// for every frame.
/// @return [self] the receiver.

static VALUE
scene_start_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->scheduleUpdate();
    return rcv;
}

/// @method #stop_update
/// Stops the update loop. The +#update+ method will no longer be called on
/// this object.
/// @return [self] the receiver.

static VALUE
scene_stop_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->unscheduleUpdate();
    return rcv;
}

/// @method #update(delta)
/// The update loop method. Subclasses can provide a custom implementation of
/// this method. The default implementation is empty.
/// @param delta [Float] a value representing the amount of time, in seconds,
///   since the last time this method was called.
/// @return [self] the receiver.

static VALUE
scene_update(VALUE rcv, SEL sel, VALUE delta)
{
    // Do nothing.
    return rcv;
}

/// @group Events

static VALUE
scene_add_listener(VALUE rcv, cocos2d::EventListener *listener)
{
    auto scene = SCENE(rcv);
    scene->getEventDispatcher()->addEventListenerWithSceneGraphPriority(
	    listener, scene);
    return rcv;
}

static
bool scene_onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event) {
    return true;
}

static VALUE
scene_on_touch_event(VALUE rcv, SEL sel, mc_Scene_EventType type)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto scene = SCENE(rcv);
    if (scene->touch_listener == NULL) {
	scene->touch_listener = cocos2d::EventListenerTouchOneByOne::create();
    }
    else {
	scene->getEventDispatcher()->removeEventListener(scene->touch_listener);
    }
    auto lambda = [block](cocos2d::Touch *touch, cocos2d::Event *event) -> bool {
	VALUE touch_obj = rb_cocos2d_object_new(touch, rb_cTouch);
	return RTEST(rb_block_call(block, 1, &touch_obj));
    };

    switch (type) {
      case ON_BEGIN:
	scene->touch_listener->onTouchBegan = lambda;
	break;
      case ON_MOVE:
	scene->touch_listener->onTouchMoved = lambda;
	break;
      case ON_END:
	scene->touch_listener->onTouchEnded = lambda;
	break;
      case ON_CANCEL:
	scene->touch_listener->onTouchCancelled = lambda;
	break;
    }
    if (scene->touch_listener->onTouchBegan == NULL) {
	// EventDispatcher will use the return value of 'onTouchBegan' to determine whether to pass following 'move', 'end'
	// message to 'EventListenerTouchOneByOne' or not. So 'onTouchBegan' needs to be set.
	scene->touch_listener->onTouchBegan = scene_onTouchBegan;
    }

    return scene_add_listener(rcv, scene->touch_listener);
}

/// @method #on_touch_begin
/// Starts listening for touch begin events on the receiver.
/// @yield [Events::Touch] the given block will be yield when a touch begin
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_begin(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_BEGIN);
}

/// @method #on_touch_end
/// Starts listening for touch end events on the receiver.
/// This method requires {#on_touch_begin} calling in order to catch the event.
/// @yield [Events::Touch] the given block will be yield when a touch end
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_end(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_END);
}

/// @method #on_touch_move
/// Starts listening for touch move events on the receiver.
/// This method requires {#on_touch_begin} calling in order to catch the event.
/// @yield [Events::Touch] the given block will be yield when a touch move
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_move(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_MOVE);
}

/// @method #on_touch_cancel
/// Starts listening for touch cancel events on the receiver.
/// This method requires {#on_touch_begin} calling in order to catch the event.
/// @yield [Events::Touch] the given block will be yield when a touch cancel
///   event is received.
/// @return [self] the receiver.

static VALUE
scene_on_touch_cancel(VALUE rcv, SEL sel)
{
    return scene_on_touch_event(rcv, sel, mc_Scene_EventType::ON_CANCEL);
}
/// @method #on_accelerate
/// Starts listening for accelerometer events on the receiver.
/// @yield [Events::Acceleration] the given block will be yield when an
///   accelerometer event is received from the device.
/// @return [self] the receiver.

static VALUE
scene_on_accelerate(VALUE rcv, SEL sel)
{
#if CC_TARGET_OS_APPLETV
    rb_raise(rb_eRuntimeError, "Not supported in tvOS");
#else
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    cocos2d::Device::setAccelerometerEnabled(true);
    auto listener = cocos2d::EventListenerAcceleration::create(
	[block](cocos2d::Acceleration *acc, cocos2d::Event *event) {
	    VALUE acc_obj = rb_cocos2d_object_new(acc, rb_cAcceleration);
	    rb_block_call(block, 1, &acc_obj);
	});

    return scene_add_listener(rcv, listener);
#endif
}

/// @method #on_contact_begin
/// Starts listening for contact begin events from the physics engine.
/// @yield [Events::PhysicsContact] the given block will be yield when a
///   contact event is received from the physics engine.
/// @return [self] the receiver.

static VALUE
scene_on_contact_begin(VALUE rcv, SEL sel)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto listener = cocos2d::EventListenerPhysicsContact::create();
    listener->onContactBegin = [block](cocos2d::PhysicsContact &contact) -> bool {
	return RTEST(rb_block_call(block, 0, NULL));
    };

    return scene_add_listener(rcv, listener);
}

/// @endgroup

/// @property #gravity
/// @return [Point] the gravity of the scene's physics world.

static VALUE
scene_gravity(VALUE rcv, SEL sel)
{
    return rb_ccvec2_to_obj(SCENE(rcv)->scene->getPhysicsWorld()->getGravity());
}

static VALUE
scene_gravity_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setGravity(rb_any_to_ccvec2(arg));
    return rcv;
}

/// @method #debug_physics?
/// @return [Boolean] whether the physics engine should draw debug lines.

static VALUE
scene_debug_physics(VALUE rcv, SEL sel)
{
    return SCENE(rcv)->scene->getPhysicsWorld()->getDebugDrawMask()
	== cocos2d::PhysicsWorld::DEBUGDRAW_NONE ? Qfalse : Qtrue;
}

/// @method #debug_physics=(value)
/// Set to draw the debug line.
/// @param value [Boolean] true if draw debug lines.

static VALUE
scene_debug_physics_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setDebugDrawMask(RTEST(arg)
	    ? cocos2d::PhysicsWorld::DEBUGDRAW_ALL
	    : cocos2d::PhysicsWorld::DEBUGDRAW_NONE);
    return arg;
}

/// @method #background_color=(color)
/// Set background color for scene.
/// @param color [Color] background color for scene.

static VALUE
scene_background_color_set(VALUE rcv, SEL sel, VALUE val)
{
    SCENE(rcv)->setBackgroundColor(rb_any_to_cccolor3(val));
    return val;
}

extern "C"
void
Init_Layer(void)
{
    rb_cScene = rb_define_class_under(rb_mMC, "Scene", rb_cNode);
    // rb_register_cocos2d_object_finalizer(rb_cScene); removed because rb_cScene inherits rb_cNode and it already has finalizer.

    rb_define_singleton_method(rb_cScene, "alloc", scene_alloc, 0);
    rb_define_method(rb_cScene, "initialize", scene_initialize, 0);
    rb_define_method(rb_cScene, "start_update", scene_start_update, 0);
    rb_define_method(rb_cScene, "stop_update", scene_stop_update, 0);
    rb_define_method(rb_cScene, "update", scene_update, 1);
    rb_define_method(rb_cScene, "on_touch_begin", scene_on_touch_begin, 0);
    rb_define_method(rb_cScene, "on_touch_end", scene_on_touch_end, 0);
    rb_define_method(rb_cScene, "on_touch_move", scene_on_touch_move, 0);
    rb_define_method(rb_cScene, "on_touch_cancel", scene_on_touch_cancel, 0);
    rb_define_method(rb_cScene, "on_accelerate", scene_on_accelerate, 0);
    rb_define_method(rb_cScene, "on_contact_begin", scene_on_contact_begin, 0);
    rb_define_method(rb_cScene, "gravity", scene_gravity, 0);
    rb_define_method(rb_cScene, "gravity=", scene_gravity_set, 1);
    rb_define_method(rb_cScene, "debug_physics?", scene_debug_physics, 0);
    rb_define_method(rb_cScene, "debug_physics=", scene_debug_physics_set, 1);
    rb_define_method(rb_cScene, "background_color=", scene_background_color_set, 1);
    rb_define_method(rb_cScene, "color=", scene_background_color_set, 1); // depricated
}
<|endoftext|>


<commit_before>/***************************************************************************
                          level.cpp  -  description
                             -------------------
    begin                : mar avr 15 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.      *
 *                                                                         *
 * This software is published under the terms of the Apache Software       *
 * License version 1.1, a copy of which has been included with this        *
 * distribution in the LICENSE.txt file.                                   *
 ***************************************************************************/

#include <log4cxx/level.h>
#include <log4cxx/helpers/stringhelper.h>

using namespace log4cxx;
using namespace log4cxx::helpers;

IMPLEMENT_LOG4CXX_OBJECT_WITH_CUSTOM_CLASS(Level, LevelClass)

const LevelPtr Level::OFF(new Level(Level::OFF_INT, _T("OFF"), 0));
const LevelPtr Level::FATAL(new Level(Level::FATAL_INT, _T("FATAL"), 0));
const LevelPtr Level::ERROR(new Level(Level::ERROR_INT, _T("ERROR"), 3));
const LevelPtr Level::WARN(new Level(Level::WARN_INT, _T("WARN"),  4));
const LevelPtr Level::INFO(new Level(Level::INFO_INT, _T("INFO"),  6));
const LevelPtr Level::DEBUG(new Level(Level::DEBUG_INT, _T("DEBUG"), 7));
const LevelPtr Level::ALL(new Level(Level::ALL_INT, _T("ALL"), 7));

Level::Level(int level, const String& levelStr, int syslogEquivalent)
: level(level), levelStr(levelStr), syslogEquivalent(syslogEquivalent)
{
}

const LevelPtr& Level::toLevel(const String& sArg)
{
    return toLevel(sArg, Level::DEBUG);
}

const LevelPtr& Level::toLevel(int val)
{
    return toLevel(val, Level::DEBUG);
}

const LevelPtr& Level::toLevel(int val, const LevelPtr& defaultLevel)
{
    switch(val)
    {
    case ALL_INT: return ALL;
    case DEBUG_INT: return DEBUG;
    case INFO_INT: return INFO;
    case WARN_INT: return WARN;
    case ERROR_INT: return ERROR;
    case FATAL_INT: return FATAL;
    case OFF_INT: return OFF;
    default: return defaultLevel;
    }
}

const LevelPtr& Level::toLevel(const String& sArg, const LevelPtr& defaultLevel)
{
    if (sArg.empty())
    {
       return defaultLevel;
    }

    String s = StringHelper::toUpperCase(sArg);

    if(s == (_T("ALL"))) return ALL;
    if(s == (_T("DEBUG"))) return DEBUG;
    if(s == (_T("INFO"))) return INFO;
    if(s == (_T("WARN")))  return WARN;
    if(s == (_T("ERROR"))) return ERROR;
    if(s == (_T("FATAL"))) return FATAL;
    if(s == (_T("OFF"))) return OFF;

    return defaultLevel;
}

bool Level::equals(const LevelPtr& level) const
{
	return (this->level == level->level);
}

int Level::getSyslogEquivalent() const
{
	return syslogEquivalent;
}

bool Level::isGreaterOrEqual(const LevelPtr& level) const
{
    return this->level >= level->level;
}

const String& Level::toString() const
{
	return levelStr;
}

int Level::toInt() const
{
	return level;
}

const LevelPtr& Level::getAllLevel()
{
	return ALL;
}

const LevelPtr& Level::getFatalLevel()
{
	return FATAL;
}

const LevelPtr& Level::getErrorLevel()
{
	return ERROR;
}

const LevelPtr& Level::getWarnLevel()
{
	return WARN;
}

const LevelPtr& Level::getInfoLevel()
{
	return INFO;
}

const LevelPtr& Level::getDebugLevel()
{
	return DEBUG;
}

const LevelPtr& Level::getOffLevel()
{
	return OFF;
}

<commit_msg>removed useless methods<commit_after>/***************************************************************************
                          level.cpp  -  description
                             -------------------
    begin                : mar avr 15 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.      *
 *                                                                         *
 * This software is published under the terms of the Apache Software       *
 * License version 1.1, a copy of which has been included with this        *
 * distribution in the LICENSE.txt file.                                   *
 ***************************************************************************/

#include <log4cxx/level.h>
#include <log4cxx/helpers/stringhelper.h>

using namespace log4cxx;
using namespace log4cxx::helpers;

IMPLEMENT_LOG4CXX_OBJECT_WITH_CUSTOM_CLASS(Level, LevelClass)

const LevelPtr Level::OFF(new Level(Level::OFF_INT, _T("OFF"), 0));
const LevelPtr Level::FATAL(new Level(Level::FATAL_INT, _T("FATAL"), 0));
const LevelPtr Level::ERROR(new Level(Level::ERROR_INT, _T("ERROR"), 3));
const LevelPtr Level::WARN(new Level(Level::WARN_INT, _T("WARN"),  4));
const LevelPtr Level::INFO(new Level(Level::INFO_INT, _T("INFO"),  6));
const LevelPtr Level::DEBUG(new Level(Level::DEBUG_INT, _T("DEBUG"), 7));
const LevelPtr Level::ALL(new Level(Level::ALL_INT, _T("ALL"), 7));

Level::Level(int level, const String& levelStr, int syslogEquivalent)
: level(level), levelStr(levelStr), syslogEquivalent(syslogEquivalent)
{
}

const LevelPtr& Level::toLevel(const String& sArg)
{
    return toLevel(sArg, Level::DEBUG);
}

const LevelPtr& Level::toLevel(int val)
{
    return toLevel(val, Level::DEBUG);
}

const LevelPtr& Level::toLevel(int val, const LevelPtr& defaultLevel)
{
    switch(val)
    {
    case ALL_INT: return ALL;
    case DEBUG_INT: return DEBUG;
    case INFO_INT: return INFO;
    case WARN_INT: return WARN;
    case ERROR_INT: return ERROR;
    case FATAL_INT: return FATAL;
    case OFF_INT: return OFF;
    default: return defaultLevel;
    }
}

const LevelPtr& Level::toLevel(const String& sArg, const LevelPtr& defaultLevel)
{
    if (sArg.empty())
    {
       return defaultLevel;
    }

    String s = StringHelper::toUpperCase(sArg);

    if(s == (_T("ALL"))) return ALL;
    if(s == (_T("DEBUG"))) return DEBUG;
    if(s == (_T("INFO"))) return INFO;
    if(s == (_T("WARN")))  return WARN;
    if(s == (_T("ERROR"))) return ERROR;
    if(s == (_T("FATAL"))) return FATAL;
    if(s == (_T("OFF"))) return OFF;

    return defaultLevel;
}

bool Level::equals(const LevelPtr& level) const
{
	return (this->level == level->level);
}

int Level::getSyslogEquivalent() const
{
	return syslogEquivalent;
}

bool Level::isGreaterOrEqual(const LevelPtr& level) const
{
    return this->level >= level->level;
}

const String& Level::toString() const
{
	return levelStr;
}

int Level::toInt() const
{
	return level;
}

<|endoftext|>