<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/logging.h>
#include <xzero/ExceptionHandler.h>
#include <xzero/raft/Server.h>
#include <xzero/raft/StateMachine.h>
#include <xzero/raft/Discovery.h>
#include <xzero/raft/Transport.h>
#include <xzero/raft/Storage.h>
#include <xzero/util/BinaryReader.h>
#include <xzero/util/BinaryWriter.h>
#include <xzero/executor/PosixScheduler.h>
#include <xzero/testing.h>
#include <initializer_list>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <limits>

using namespace xzero;
using raft::RaftError;

class TestKeyValueStore : public raft::StateMachine { // {{{
 public:
  TestKeyValueStore();

  bool saveSnapshot(std::unique_ptr<OutputStream>&& output) override;
  bool loadSnapshot(std::unique_ptr<InputStream>&& input) override;
  void applyCommand(const raft::Command& serializedCmd) override;

  int get(int key) const;

  std::function<void(int, int)> onApplyCommand;

 private:
  std::unordered_map<int, int> tuples_;
};

TestKeyValueStore::TestKeyValueStore()
    : onApplyCommand(),
      tuples_() {
}

bool TestKeyValueStore::saveSnapshot(std::unique_ptr<OutputStream>&& output) {
  auto o = [&](const uint8_t* data, size_t len) {
    output->write((const char*) data, len);
  };
  BinaryWriter bw(o);
  for (const auto& pair: tuples_) {
    bw.writeVarUInt(pair.first);
    bw.writeVarUInt(pair.second);
  }
  return true;
}

bool TestKeyValueStore::loadSnapshot(std::unique_ptr<InputStream>&& input) {
  tuples_.clear();

  Buffer buffer;
  for (;;) {
    if (input->read(&buffer, 4096) <= 0) {
      break;
    }
  }

  BinaryReader reader((const uint8_t*) buffer.data(), buffer.size());

  while (reader.pending() > 0) {
    int a = reader.parseVarUInt();
    int b = reader.parseVarUInt();
    if (a < 0 || b < 0) {
      break;
    }
    tuples_[a] = b;
  }
  return true;
}

void TestKeyValueStore::applyCommand(const raft::Command& command) {
  logDebug("TestKeyValueStore", "applyCommand: $0", StringUtil::hexPrint(command.data(), command.size()));
  BinaryReader reader(command.data(), command.size());
  int a = reader.parseVarUInt();
  int b = reader.parseVarUInt();
  logDebug("TestKeyValueStore", "-> applyCommand: $0 = $1", a, b);
  tuples_[a] = b;

  if (onApplyCommand) {
    onApplyCommand(a, b);
  }
}

int TestKeyValueStore::get(int a) const {
  auto i = tuples_.find(a);
  if (i != tuples_.end())
    return i->second;
  else
    return -1;
}
// }}}
class TestServer { // {{{
 public:
  TestServer(raft::Id id,
             const raft::Discovery* discovery,
             Executor* executor);

  int get(int key) const;
  std::error_code set(int key, int value);
  Future<raft::Index> setAsync(int key, int value);

  raft::LocalTransport* transport() noexcept { return &transport_; }
  raft::Server* server() noexcept { return &raftServer_; }
  TestKeyValueStore* fsm() noexcept { return &stateMachine_; }

 private:
  TestKeyValueStore stateMachine_;
  raft::MemoryStore storage_;
  raft::LocalTransport transport_;
  raft::Server raftServer_;
};

TestServer::TestServer(raft::Id id,
                       const raft::Discovery* discovery,
                       Executor* executor)
  : stateMachine_(),
    storage_(executor),
    transport_(id, executor),
    raftServer_(executor, id, &storage_, discovery, &transport_, &stateMachine_) {
}

int TestServer::get(int key) const {
  return stateMachine_.get(key);
}

std::error_code TestServer::set(int key, int value) {
  raft::Command cmd;

  auto o = [&](const uint8_t* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
      cmd.push_back(data[i]);
    }
  };

  BinaryWriter bw(o);
  bw.writeVarUInt(key);
  bw.writeVarUInt(value);

  logDebug("TestServer", "set($0, $1): $2", key, value,
      StringUtil::hexPrint(cmd.data(), cmd.size()));

  return raftServer_.sendCommand(std::move(cmd));
}

Future<raft::Index> TestServer::setAsync(int key, int value) {
  raft::Command cmd;

  auto o = [&](const uint8_t* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
      cmd.push_back(data[i]);
    }
  };

  BinaryWriter bw(o);
  bw.writeVarUInt(key);
  bw.writeVarUInt(value);

  logDebug("TestServer", "setAsync($0, $1): $2", key, value,
      StringUtil::hexPrint(cmd.data(), cmd.size()));

  return raftServer_.sendCommandAsync(std::move(cmd));
}
// }}}
struct TestServerPod { // {{{
  PosixScheduler executor;
  const raft::StaticDiscovery discovery;
  std::vector<std::unique_ptr<TestServer>> servers;

  TestServerPod();

  void enableBreakOnConsensus();
  bool isConsensusReached() const;

  void startWithLeader(raft::Id id);
  void start();

  TestServer* getInstance(raft::Id id);
};

TestServerPod::TestServerPod()
  : executor(std::make_unique<CatchAndLogExceptionHandler>("TestServerPod")),
    discovery{ { 1, "127.0.0.1:4201" },
               { 2, "127.0.0.1:4202" },
               { 3, "127.0.0.1:4203" } },
    servers() {
  // create servers
  for (raft::Id id: discovery.listMembers()) {
    servers.emplace_back(new TestServer(id, &discovery, &executor));
  }

  // register (id,peer) tuples of server peers to this server
  for (auto& s: servers) {
    for (auto& t: servers) {
      s->transport()->setPeer(t->server()->id(), t->server());
    }

    s->server()->onLeaderChanged = [&](raft::Id oldLeaderId) {
      logDebug("TestServerPod", "onLeaderChanged[$0]: $1 ~> $2",
          s->server()->id(), oldLeaderId,
          s->server()->currentLeaderId());
    };
  }
}

void TestServerPod::enableBreakOnConsensus() {
  for (auto& s: servers) {
    s->server()->onStateChanged = [&](raft::Server* s, raft::ServerState oldState) {
      logDebug("TestServerPod", "onStateChanged[$0]: $1 ~> $2", s->id(), oldState, s->state());
      if (isConsensusReached()) {
        executor.breakLoop();
      }
    };
  }
}

bool TestServerPod::isConsensusReached() const {
  size_t leaderCount = 0;
  size_t followerCount = 0;

  for (const auto& s: servers) {
    switch (s->server()->state()) {
      case raft::ServerState::Leader:
        leaderCount++;
        break;
      case raft::ServerState::Follower:
        followerCount++;
        break;
      case raft::ServerState::Candidate:
        break;
    }
  }

  return leaderCount + followerCount == servers.size();
}

void TestServerPod::startWithLeader(raft::Id initialLeaderId) {
  for (auto& s: servers) {
    std::error_code ec = s->server()->startWithLeader(initialLeaderId);
    ASSERT_TRUE(!ec);
  };
}

void TestServerPod::start() {
  for (auto& s: servers) {
    std::error_code ec = s->server()->start();
    ASSERT_TRUE(!ec);
  };
}

TestServer* TestServerPod::getInstance(raft::Id id) {
  for (auto& i: servers) {
    if (i->server()->id() == id) {
      return i.get();
    }
  }

  return nullptr;
}
// }}}

TEST(raft_Server, leaderElection) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.start();
  pod.executor.runLoop();

  // now, leader election must have been taken place
  // 1 leader and 2 followers must exist

  EXPECT_TRUE(pod.isConsensusReached());
}

TEST(raft_Server, startWithLeader) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());
}

TEST(raft_Server, AppendEntries_single_entry) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  std::error_code err = pod.getInstance(1)->set(2, 4);
  ASSERT_FALSE(err);

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 3) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  pod.executor.runLoop();

  ASSERT_EQ(4, pod.getInstance(1)->get(2));
  ASSERT_EQ(4, pod.getInstance(2)->get(2));
  ASSERT_EQ(4, pod.getInstance(3)->get(2));
}

TEST(raft_Server, AppendEntries_batched_entries) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  std::error_code err = pod.getInstance(1)->set(2, 4);
  ASSERT_FALSE(err);

  err = pod.getInstance(1)->set(3, 6);
  ASSERT_FALSE(err);

  err = pod.getInstance(1)->set(4, 7);
  ASSERT_FALSE(err);

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 9) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  pod.executor.runLoop();

  ASSERT_EQ(4, pod.getInstance(1)->get(2));
  ASSERT_EQ(4, pod.getInstance(2)->get(2));
  ASSERT_EQ(4, pod.getInstance(3)->get(2));

  ASSERT_EQ(6, pod.getInstance(1)->get(3));
  ASSERT_EQ(6, pod.getInstance(2)->get(3));
  ASSERT_EQ(6, pod.getInstance(3)->get(3));

  ASSERT_EQ(7, pod.getInstance(1)->get(4));
  ASSERT_EQ(7, pod.getInstance(2)->get(4));
  ASSERT_EQ(7, pod.getInstance(3)->get(4));
}

TEST(raft_Server, AppendEntries_not_leading_err) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  std::error_code err = pod.getInstance(1)->set(2, 4);
  EXPECT_EQ(std::error_code(), err);

  err = pod.getInstance(2)->set(2, 4);
  EXPECT_EQ(std::make_error_code(RaftError::NotLeading), err);

  err = pod.getInstance(3)->set(2, 4);
  EXPECT_EQ(std::make_error_code(RaftError::NotLeading), err);
}

TEST(raft_Server, sendCommandAsync) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 3) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  Future<raft::Index> f = pod.getInstance(1)->setAsync(2, 4);

  pod.executor.runLoop();

  ASSERT_EQ(4, pod.getInstance(1)->get(2));
  ASSERT_EQ(4, pod.getInstance(2)->get(2));
  ASSERT_EQ(4, pod.getInstance(3)->get(2));
}
<commit_msg>[xzero] raft: adds new test for batched asynchronous command sending<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/logging.h>
#include <xzero/ExceptionHandler.h>
#include <xzero/raft/Server.h>
#include <xzero/raft/StateMachine.h>
#include <xzero/raft/Discovery.h>
#include <xzero/raft/Transport.h>
#include <xzero/raft/Storage.h>
#include <xzero/util/BinaryReader.h>
#include <xzero/util/BinaryWriter.h>
#include <xzero/executor/PosixScheduler.h>
#include <xzero/testing.h>
#include <initializer_list>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <limits>

using namespace xzero;
using raft::RaftError;

class TestKeyValueStore : public raft::StateMachine { // {{{
 public:
  TestKeyValueStore();

  bool saveSnapshot(std::unique_ptr<OutputStream>&& output) override;
  bool loadSnapshot(std::unique_ptr<InputStream>&& input) override;
  void applyCommand(const raft::Command& serializedCmd) override;

  int get(int key) const;

  std::function<void(int, int)> onApplyCommand;

 private:
  std::unordered_map<int, int> tuples_;
};

TestKeyValueStore::TestKeyValueStore()
    : onApplyCommand(),
      tuples_() {
}

bool TestKeyValueStore::saveSnapshot(std::unique_ptr<OutputStream>&& output) {
  auto o = [&](const uint8_t* data, size_t len) {
    output->write((const char*) data, len);
  };
  BinaryWriter bw(o);
  for (const auto& pair: tuples_) {
    bw.writeVarUInt(pair.first);
    bw.writeVarUInt(pair.second);
  }
  return true;
}

bool TestKeyValueStore::loadSnapshot(std::unique_ptr<InputStream>&& input) {
  tuples_.clear();

  Buffer buffer;
  for (;;) {
    if (input->read(&buffer, 4096) <= 0) {
      break;
    }
  }

  BinaryReader reader((const uint8_t*) buffer.data(), buffer.size());

  while (reader.pending() > 0) {
    int a = reader.parseVarUInt();
    int b = reader.parseVarUInt();
    if (a < 0 || b < 0) {
      break;
    }
    tuples_[a] = b;
  }
  return true;
}

void TestKeyValueStore::applyCommand(const raft::Command& command) {
  logDebug("TestKeyValueStore", "applyCommand: $0", StringUtil::hexPrint(command.data(), command.size()));
  BinaryReader reader(command.data(), command.size());
  int a = reader.parseVarUInt();
  int b = reader.parseVarUInt();
  logDebug("TestKeyValueStore", "-> applyCommand: $0 = $1", a, b);
  tuples_[a] = b;

  if (onApplyCommand) {
    onApplyCommand(a, b);
  }
}

int TestKeyValueStore::get(int a) const {
  auto i = tuples_.find(a);
  if (i != tuples_.end())
    return i->second;
  else
    return -1;
}
// }}}
class TestServer { // {{{
 public:
  TestServer(raft::Id id,
             const raft::Discovery* discovery,
             Executor* executor);

  int get(int key) const;
  std::error_code set(int key, int value);
  Future<raft::Index> setAsync(int key, int value);

  raft::LocalTransport* transport() noexcept { return &transport_; }
  raft::Server* server() noexcept { return &raftServer_; }
  TestKeyValueStore* fsm() noexcept { return &stateMachine_; }

 private:
  TestKeyValueStore stateMachine_;
  raft::MemoryStore storage_;
  raft::LocalTransport transport_;
  raft::Server raftServer_;
};

TestServer::TestServer(raft::Id id,
                       const raft::Discovery* discovery,
                       Executor* executor)
  : stateMachine_(),
    storage_(executor),
    transport_(id, executor),
    raftServer_(executor, id, &storage_, discovery, &transport_, &stateMachine_) {
}

int TestServer::get(int key) const {
  return stateMachine_.get(key);
}

std::error_code TestServer::set(int key, int value) {
  raft::Command cmd;

  auto o = [&](const uint8_t* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
      cmd.push_back(data[i]);
    }
  };

  BinaryWriter bw(o);
  bw.writeVarUInt(key);
  bw.writeVarUInt(value);

  logDebug("TestServer", "set($0, $1): $2", key, value,
      StringUtil::hexPrint(cmd.data(), cmd.size()));

  return raftServer_.sendCommand(std::move(cmd));
}

Future<raft::Index> TestServer::setAsync(int key, int value) {
  raft::Command cmd;

  auto o = [&](const uint8_t* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
      cmd.push_back(data[i]);
    }
  };

  BinaryWriter bw(o);
  bw.writeVarUInt(key);
  bw.writeVarUInt(value);

  logDebug("TestServer", "setAsync($0, $1): $2", key, value,
      StringUtil::hexPrint(cmd.data(), cmd.size()));

  return raftServer_.sendCommandAsync(std::move(cmd));
}
// }}}
struct TestServerPod { // {{{
  PosixScheduler executor;
  const raft::StaticDiscovery discovery;
  std::vector<std::unique_ptr<TestServer>> servers;

  TestServerPod();

  void enableBreakOnConsensus();
  bool isConsensusReached() const;

  void startWithLeader(raft::Id id);
  void start();

  TestServer* getInstance(raft::Id id);
};

TestServerPod::TestServerPod()
  : executor(std::make_unique<CatchAndLogExceptionHandler>("TestServerPod")),
    discovery{ { 1, "127.0.0.1:4201" },
               { 2, "127.0.0.1:4202" },
               { 3, "127.0.0.1:4203" } },
    servers() {
  // create servers
  for (raft::Id id: discovery.listMembers()) {
    servers.emplace_back(new TestServer(id, &discovery, &executor));
  }

  // register (id,peer) tuples of server peers to this server
  for (auto& s: servers) {
    for (auto& t: servers) {
      s->transport()->setPeer(t->server()->id(), t->server());
    }

    s->server()->onLeaderChanged = [&](raft::Id oldLeaderId) {
      logDebug("TestServerPod", "onLeaderChanged[$0]: $1 ~> $2",
          s->server()->id(), oldLeaderId,
          s->server()->currentLeaderId());
    };
  }
}

void TestServerPod::enableBreakOnConsensus() {
  for (auto& s: servers) {
    s->server()->onStateChanged = [&](raft::Server* s, raft::ServerState oldState) {
      logDebug("TestServerPod", "onStateChanged[$0]: $1 ~> $2", s->id(), oldState, s->state());
      if (isConsensusReached()) {
        executor.breakLoop();
      }
    };
  }
}

bool TestServerPod::isConsensusReached() const {
  size_t leaderCount = 0;
  size_t followerCount = 0;

  for (const auto& s: servers) {
    switch (s->server()->state()) {
      case raft::ServerState::Leader:
        leaderCount++;
        break;
      case raft::ServerState::Follower:
        followerCount++;
        break;
      case raft::ServerState::Candidate:
        break;
    }
  }

  return leaderCount + followerCount == servers.size();
}

void TestServerPod::startWithLeader(raft::Id initialLeaderId) {
  for (auto& s: servers) {
    std::error_code ec = s->server()->startWithLeader(initialLeaderId);
    ASSERT_TRUE(!ec);
  };
}

void TestServerPod::start() {
  for (auto& s: servers) {
    std::error_code ec = s->server()->start();
    ASSERT_TRUE(!ec);
  };
}

TestServer* TestServerPod::getInstance(raft::Id id) {
  for (auto& i: servers) {
    if (i->server()->id() == id) {
      return i.get();
    }
  }

  return nullptr;
}
// }}}

TEST(raft_Server, leaderElection) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.start();
  pod.executor.runLoop();

  // now, leader election must have been taken place
  // 1 leader and 2 followers must exist

  EXPECT_TRUE(pod.isConsensusReached());
}

TEST(raft_Server, startWithLeader) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());
}

TEST(raft_Server, AppendEntries_single_entry) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  std::error_code err = pod.getInstance(1)->set(2, 4);
  ASSERT_FALSE(err);

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 3) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  pod.executor.runLoop();

  ASSERT_EQ(4, pod.getInstance(1)->get(2));
  ASSERT_EQ(4, pod.getInstance(2)->get(2));
  ASSERT_EQ(4, pod.getInstance(3)->get(2));
}

TEST(raft_Server, AppendEntries_batched_entries) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  std::error_code err = pod.getInstance(1)->set(2, 4);
  ASSERT_FALSE(err);

  err = pod.getInstance(1)->set(3, 6);
  ASSERT_FALSE(err);

  err = pod.getInstance(1)->set(4, 7);
  ASSERT_FALSE(err);

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 9) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  pod.executor.runLoop();

  ASSERT_EQ(4, pod.getInstance(1)->get(2));
  ASSERT_EQ(4, pod.getInstance(2)->get(2));
  ASSERT_EQ(4, pod.getInstance(3)->get(2));

  ASSERT_EQ(6, pod.getInstance(1)->get(3));
  ASSERT_EQ(6, pod.getInstance(2)->get(3));
  ASSERT_EQ(6, pod.getInstance(3)->get(3));

  ASSERT_EQ(7, pod.getInstance(1)->get(4));
  ASSERT_EQ(7, pod.getInstance(2)->get(4));
  ASSERT_EQ(7, pod.getInstance(3)->get(4));
}

TEST(raft_Server, AppendEntries_not_leading_err) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  std::error_code err = pod.getInstance(1)->set(2, 4);
  EXPECT_EQ(std::error_code(), err);

  err = pod.getInstance(2)->set(2, 4);
  EXPECT_EQ(std::make_error_code(RaftError::NotLeading), err);

  err = pod.getInstance(3)->set(2, 4);
  EXPECT_EQ(std::make_error_code(RaftError::NotLeading), err);
}

TEST(raft_Server, sendCommandAsync) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 3) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  Future<raft::Index> f = pod.getInstance(1)->setAsync(2, 4);

  pod.executor.runLoop();

  ASSERT_EQ(4, pod.getInstance(1)->get(2));
  ASSERT_EQ(4, pod.getInstance(2)->get(2));
  ASSERT_EQ(4, pod.getInstance(3)->get(2));
  ASSERT_EQ(1, f.get());
}

TEST(raft_Server, sendCommandAsync_batched) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  int applyCount = 0;
  for (raft::Id id = 1; id <= 3; ++id) {
    pod.getInstance(id)->fsm()->onApplyCommand = [&](int key, int value) {
      logf("onApplyCommand for instance $0 = $1", key, value);
      applyCount++;
      if (applyCount == 9) {
        logf("onApplyCommand: breaking loop with applyCount = $0", applyCount);
        pod.executor.breakLoop();
      }
    };
  }

  Future<raft::Index> f = pod.getInstance(1)->setAsync(1, 2);
  Future<raft::Index> g = pod.getInstance(1)->setAsync(3, 4);
  Future<raft::Index> h = pod.getInstance(1)->setAsync(5, 6);

  pod.executor.runLoop();

  ASSERT_EQ(2, pod.getInstance(1)->get(1));
  ASSERT_EQ(2, pod.getInstance(2)->get(1));
  ASSERT_EQ(2, pod.getInstance(3)->get(1));

  ASSERT_EQ(4, pod.getInstance(1)->get(3));
  ASSERT_EQ(4, pod.getInstance(2)->get(3));
  ASSERT_EQ(4, pod.getInstance(3)->get(3));

  ASSERT_EQ(6, pod.getInstance(1)->get(5));
  ASSERT_EQ(6, pod.getInstance(2)->get(5));
  ASSERT_EQ(6, pod.getInstance(3)->get(5));

  ASSERT_EQ(1, f.get());
  ASSERT_EQ(2, g.get());
  ASSERT_EQ(3, h.get());
}
<|endoftext|>


<commit_before>///
/// @file
/// @copyright Copyright (C) 2017, Jonathan Bryan Schmalhofer
///
/// @brief Collection of Unit Tests for the AirSimToRos Node
///

#include <gtest/gtest.h>
#include <ros/time.h>

#include "airsimros_airsim_to_ros/airsim_to_ros.h"

namespace airsimros
{
TEST(AirSimToRosTest, DefaultStatus)
{
    ros::Time::init();

    // ARRANGE
    auto airsim_to_ros = AirSimToRos();

    // ACT
    auto airsim_to_ros_status = airsim_to_ros.GetStatus();

    // ASSERT
	EXPECT_EQ(airsim_to_ros_status, 0);
}

TEST(AirSimToRosTest, SetAndGetStatus)
{
    ros::Time::init();

    // ARRANGE
    auto airsim_to_ros = AirSimToRos();

    // ACT
	airsim_to_ros.SetStatus(42)
    auto airsim_to_ros_status = airsim_to_ros.GetStatus();

    // ASSERT
	EXPECT_EQ(airsim_to_ros_status, 42);
}
}

int main(int argc, char** argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>corrected typo in test of airsim_to_ros<commit_after>///
/// @file
/// @copyright Copyright (C) 2017, Jonathan Bryan Schmalhofer
///
/// @brief Collection of Unit Tests for the AirSimToRos Node
///

#include <gtest/gtest.h>
#include <ros/time.h>

#include "airsimros_airsim_to_ros/airsim_to_ros.h"

namespace airsimros
{
TEST(AirSimToRosTest, DefaultStatus)
{
    ros::Time::init();

    // ARRANGE
    auto airsim_to_ros = AirSimToRos();

    // ACT
    auto airsim_to_ros_status = airsim_to_ros.GetStatus();

    // ASSERT
	EXPECT_EQ(airsim_to_ros_status, 0);
}

TEST(AirSimToRosTest, SetAndGetStatus)
{
    ros::Time::init();

    // ARRANGE
    auto airsim_to_ros = AirSimToRos();

    // ACT
	airsim_to_ros.SetStatus(42);
    auto airsim_to_ros_status = airsim_to_ros.GetStatus();

    // ASSERT
	EXPECT_EQ(airsim_to_ros_status, 42);
}
}

int main(int argc, char** argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <stdlib.h>
#include "packet.h"

#define BUFSIZE 121
#define FILENAME "Testfile"
#define TEST_FILENAME "Testfile2"
#define PORT 10038
#define PAKSIZE 128
#define ACK 0
#define NAK 1

using namespace std;

bool gremlin(Packet * pack, int corruptProb, int lossProb);
bool init(int argc, char** argv);
bool loadFile();
bool sendFile();
bool getFile();
char * recvPkt();
bool isvpack(unsigned char * p);
Packet createPacket(int index);
bool sendPacket();
bool isAck();
void handleAck();
void handleNak(int& x);

int seqNum;
int s;
int probCorrupt;
int probLoss;
string hs;
short int port;
char * file;
unsigned char* window[16]; //packet window
int windowBase; //used to determine position in window of arriving packets
int length;
struct sockaddr_in a;
struct sockaddr_in sa;
socklen_t salen;
string fstr;
bool dropPck;
Packet p;
int delayT;
unsigned char b[BUFSIZE];

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

  if(!init(argc, argv)) return -1;

  if(sendto(s, "GET Testfile", BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
    return false;
  }

  getFile();

  return 0;
}

bool init(int argc, char** argv) {
  windowBase = 0; //initialize windowBase
  s = 0;

  hs = string("131.204.14.") + argv[1]; /* Needs to be updated? Might be a string like "tux175.engr.auburn.edu." */
  port = 10038; /* Can be any port within 10038-10041, inclusive. */

  char* delayTStr = argv[2];
  delayT = boost::lexical_cast<int>(delayTStr);

  /*if(!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }*/

  if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY); //why does this always give us 0?
  a.sin_port = htons(0);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0){
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return false;
  }

  memset((char *)&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  inet_pton(AF_INET, hs.c_str(), &(sa.sin_addr));

  cout << endl;

  cout << "Server address (inet mode): " << inet_ntoa(sa.sin_addr) << endl;
  cout << "Port: " << ntohs(sa.sin_port) << endl;

  cout << endl << endl;

  /*fstr = string(file);

  cout << "File: " << endl << fstr << endl << endl;*/

  seqNum = 0;
  dropPck = false;
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

bool sendFile() {

  for(int x = 0; x <= length / BUFSIZE; x++) {
    p = createPacket(x);
    if(!sendPacket()) continue;

    if(isAck()) {
      handleAck();
    } else {
      handleNak(x);
    }

    memset(b, 0, BUFSIZE);
  }
  return true;
}

Packet createPacket(int index){
    cout << endl;
    cout << "=== TRANSMISSION START" << endl;
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);
    if(index * BUFSIZE + BUFSIZE > length) {
      mstr[length - (index * BUFSIZE)] = '\0';
    }
    return Packet (seqNum, mstr.c_str());
}

bool sendPacket(){
    int pc = probCorrupt; int pl = probLoss;
    if((dropPck = gremlin(&p, pc, pl)) == false){
      if(sendto(s, p.str(), BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
        return false;
      }
      return true;
    } else return false;
}
bool isAck() {
    recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&sa, &salen);

    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {

}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}

bool gremlin(Packet * pack, int corruptProb, int lossProb){
  bool dropPacket = false;
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  if(r <= (lossProb)){
    dropPacket = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  else seqNum = (seqNum) ? false : true;
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  cout << "Message: "  << pack->getDataBuffer() << endl;

  return dropPacket;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[6];
  memcpy(css, &p[1], 6);
  css[5] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[7], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == seqNum) return false;
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){
  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  int rlen;
  int ack;

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&sa, &salen);

	/* Begin Window Loading */
	int tempSeqNum = boost::lexical_cast<int>(packet[0]);
	int properIndex = tempSeqNum - windowBase;

	window[properIndex] = packet;
	cout << "Packet loaded into window" << endl;
	char* tempTest = new char[6];
		memcpy(tempTest, &window[1], 0);
		tempTest[5] = '\0';

	cout << "The Checksum pulled from client window: " << tempTest[0] << endl;

	for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      if(isvpack(packet)) {
        ack = ACK;
		if(boost::lexical_cast<int>(packet[0]) == windowBase) windowBase++; //increment base of window //FIXME
        file << dataPull;
		file.flush();
      } else {
        ack = NAK;
      }
      cout << "Sent response: ";
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;

	  if(packet[6] == '1') usleep(delayT*1000);

      if(sendto(s, (const void*)windowBase, PAKSIZE, 0, (struct sockaddr *)&sa, salen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}
<commit_msg>Okay that worked...kind of<commit_after>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <stdlib.h>
#include "packet.h"

#define BUFSIZE 121
#define FILENAME "Testfile"
#define TEST_FILENAME "Testfile2"
#define PORT 10038
#define PAKSIZE 128
#define ACK 0
#define NAK 1

using namespace std;

bool gremlin(Packet * pack, int corruptProb, int lossProb);
bool init(int argc, char** argv);
bool loadFile();
bool sendFile();
bool getFile();
char * recvPkt();
bool isvpack(unsigned char * p);
Packet createPacket(int index);
bool sendPacket();
bool isAck();
void handleAck();
void handleNak(int& x);

int seqNum;
int s;
int probCorrupt;
int probLoss;
string hs;
short int port;
char * file;
unsigned char* window[16]; //packet window
int windowBase; //used to determine position in window of arriving packets
int length;
struct sockaddr_in a;
struct sockaddr_in sa;
socklen_t salen;
string fstr;
bool dropPck;
Packet p;
int delayT;
unsigned char b[BUFSIZE];

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

  if(!init(argc, argv)) return -1;

  if(sendto(s, "GET Testfile", BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
    return false;
  }

  getFile();

  return 0;
}

bool init(int argc, char** argv) {
  windowBase = 0; //initialize windowBase
  s = 0;

  hs = string("131.204.14.") + argv[1]; /* Needs to be updated? Might be a string like "tux175.engr.auburn.edu." */
  port = 10038; /* Can be any port within 10038-10041, inclusive. */

  char* delayTStr = argv[2];
  delayT = boost::lexical_cast<int>(delayTStr);

  /*if(!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }*/

  if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY); //why does this always give us 0?
  a.sin_port = htons(0);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0){
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return false;
  }

  memset((char *)&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  inet_pton(AF_INET, hs.c_str(), &(sa.sin_addr));

  cout << endl;

  cout << "Server address (inet mode): " << inet_ntoa(sa.sin_addr) << endl;
  cout << "Port: " << ntohs(sa.sin_port) << endl;

  cout << endl << endl;

  /*fstr = string(file);

  cout << "File: " << endl << fstr << endl << endl;*/

  seqNum = 0;
  dropPck = false;
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

bool sendFile() {

  for(int x = 0; x <= length / BUFSIZE; x++) {
    p = createPacket(x);
    if(!sendPacket()) continue;

    if(isAck()) {
      handleAck();
    } else {
      handleNak(x);
    }

    memset(b, 0, BUFSIZE);
  }
  return true;
}

Packet createPacket(int index){
    cout << endl;
    cout << "=== TRANSMISSION START" << endl;
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);
    if(index * BUFSIZE + BUFSIZE > length) {
      mstr[length - (index * BUFSIZE)] = '\0';
    }
    return Packet (seqNum, mstr.c_str());
}

bool sendPacket(){
    int pc = probCorrupt; int pl = probLoss;
    if((dropPck = gremlin(&p, pc, pl)) == false){
      if(sendto(s, p.str(), BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
        return false;
      }
      return true;
    } else return false;
}
bool isAck() {
    recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&sa, &salen);

    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {

}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}

bool gremlin(Packet * pack, int corruptProb, int lossProb){
  bool dropPacket = false;
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  if(r <= (lossProb)){
    dropPacket = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  else seqNum = (seqNum) ? false : true;
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  cout << "Message: "  << pack->getDataBuffer() << endl;

  return dropPacket;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[6];
  memcpy(css, &p[1], 6);
  css[5] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[7], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == seqNum) return false;
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){
  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  int rlen;
  int ack;

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&sa, &salen);

	/* Begin Window Loading */
	int tempSeqNum = boost::lexical_cast<int>(packet[0]);
	int properIndex = tempSeqNum - windowBase;

	window[properIndex] = packet;
	cout << "Packet loaded into window" << endl;
	char* tempTest = new char[6];
		memcpy(tempTest, &window[1], 0);
		tempTest[5] = '\0';

	cout << "The Checksum pulled from client window: " << tempTest[0] << endl;

	for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      if(isvpack(packet)) {
        ack = ACK;
		if(boost::lexical_cast<int>(packet[0]) == windowBase) windowBase++; //increment base of window //FIXME
        file << dataPull;
		file.flush();
      } else {
        ack = NAK;
      }
      cout << "Sent response: ";
      cout << ((ack == ACK) ? "ACK " : "NAK ") << windowBase << endl;

	  if(packet[6] == '1') usleep(delayT*1000);

      if(sendto(s, windowBase, PAKSIZE, 0, (struct sockaddr *)&sa, salen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}
<|endoftext|>


<commit_before>/*
// $Id$
// Fennel is a library of data storage and processing components.
// Copyright (C) 2005-2009 The Eigenbase Project
// Copyright (C) 2005-2009 SQLstream, Inc.
// Copyright (C) 2005-2009 LucidEra, Inc.
// Portions Copyright (C) 1999-2009 John V. Sichi
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version approved by The Eigenbase Project.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "fennel/common/CommonPreamble.h"

#include <ctype.h>
#include <algorithm>

#include <boost/io/ios_state.hpp>
#include <boost/format.hpp>

#ifdef __MSVC__
# include <windows.h>
# include "fennel/common/AtomicCounter.h"
# include "fennel/common/IntrusiveDList.h"
# include "fennel/common/CompoundId.h"
# include "fennel/common/AbortExcn.h"
# include "fennel/common/VoidPtrHash.h"
#else
# include <pthread.h>
#endif

// NOTE jvs 26-June-2005:  I added this to squelch link errors with
// the Boost filesystem library.  Yet another case where I have no
// idea what's really going on.
#ifdef __MSVC__
void *operator new [](unsigned sz) throw (std::bad_alloc)
{
    void *p = malloc(sz ? sz : 1);
    if (!p) {
        throw std::bad_alloc();
    }
    return p;
}

void operator delete [](void *p) throw ()
{
    free(p);
}
#endif

FENNEL_BEGIN_CPPFILE("$Id$");

std::logic_error constructAssertion(
    char const *pFilename,int lineNum,char const *condExpr)
{
    boost::format fmt("Assertion `%1%' failed at line %2% in file %3%");
    return std::logic_error(
        (fmt % condExpr % lineNum % pFilename).str());
}

int getCurrentThreadId()
{
#ifdef __MSVC__
    return static_cast<int>(GetCurrentThreadId());
#elif defined(__APPLE__)
    return reinterpret_cast<int64_t>(pthread_self());
#else
    return static_cast<int>(pthread_self());
#endif
}

void hexDump(std::ostream &o,void const *v,uint cb,uint cbDone)
{
    boost::io::ios_all_saver streamStateSaver(o);

    PConstBuffer b = (PConstBuffer) v;
    uint cbLine = 16, cbThis;
    o.fill('0');
    for (; cb; cb -= cbThis, cbDone += cbThis) {
        cbThis = std::min(cbLine, cb);
        o << std::hex;
        o.width(4);
        o << cbDone << ": ";
        uint i;
        for (i = 0; i < cbThis; i++, b++) {
            o.width(2);
            o << (uint) *b << " ";
        }
        for (i = cbThis; i < cbLine; i++) {
            o << "   ";
        }
        o << "| ";
        for (i = 0, b -= cbThis; i < cbThis; i++, b++) {
            if (isprint(*b)) {
                o << *b;
            } else {
                o << " ";
            }
        }
        o << std::endl;
    }
}

// TODO jvs 27-Feb-2009:  move this somewhere else

// force references to some classes which aren't referenced elsewhere
#ifdef __MSVC__
class UnreferencedCommonStructs
{
    AtomicCounter atomicCounter;
    IntrusiveDListNode dlistNode;
    CompoundId compoundId;
    AbortExcn abortExcn;
    VoidPtrHash voidPtrHash;
};
#endif

FENNEL_END_CPPFILE("$Id$");

// End Memory.cpp
<commit_msg>FENNEL:  delete obsolete code which breaks Win64<commit_after>/*
// $Id$
// Fennel is a library of data storage and processing components.
// Copyright (C) 2005-2009 The Eigenbase Project
// Copyright (C) 2005-2009 SQLstream, Inc.
// Copyright (C) 2005-2009 LucidEra, Inc.
// Portions Copyright (C) 1999-2009 John V. Sichi
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version approved by The Eigenbase Project.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "fennel/common/CommonPreamble.h"

#include <ctype.h>
#include <algorithm>

#include <boost/io/ios_state.hpp>
#include <boost/format.hpp>

#ifdef __MSVC__
# include <windows.h>
# include "fennel/common/AtomicCounter.h"
# include "fennel/common/IntrusiveDList.h"
# include "fennel/common/CompoundId.h"
# include "fennel/common/AbortExcn.h"
# include "fennel/common/VoidPtrHash.h"
#else
# include <pthread.h>
#endif

FENNEL_BEGIN_CPPFILE("$Id$");

std::logic_error constructAssertion(
    char const *pFilename,int lineNum,char const *condExpr)
{
    boost::format fmt("Assertion `%1%' failed at line %2% in file %3%");
    return std::logic_error(
        (fmt % condExpr % lineNum % pFilename).str());
}

int getCurrentThreadId()
{
#ifdef __MSVC__
    return static_cast<int>(GetCurrentThreadId());
#elif defined(__APPLE__)
    return reinterpret_cast<int64_t>(pthread_self());
#else
    return static_cast<int>(pthread_self());
#endif
}

void hexDump(std::ostream &o,void const *v,uint cb,uint cbDone)
{
    boost::io::ios_all_saver streamStateSaver(o);

    PConstBuffer b = (PConstBuffer) v;
    uint cbLine = 16, cbThis;
    o.fill('0');
    for (; cb; cb -= cbThis, cbDone += cbThis) {
        cbThis = std::min(cbLine, cb);
        o << std::hex;
        o.width(4);
        o << cbDone << ": ";
        uint i;
        for (i = 0; i < cbThis; i++, b++) {
            o.width(2);
            o << (uint) *b << " ";
        }
        for (i = cbThis; i < cbLine; i++) {
            o << "   ";
        }
        o << "| ";
        for (i = 0, b -= cbThis; i < cbThis; i++, b++) {
            if (isprint(*b)) {
                o << *b;
            } else {
                o << " ";
            }
        }
        o << std::endl;
    }
}

// TODO jvs 27-Feb-2009:  move this somewhere else

// force references to some classes which aren't referenced elsewhere
#ifdef __MSVC__
class UnreferencedCommonStructs
{
    AtomicCounter atomicCounter;
    IntrusiveDListNode dlistNode;
    CompoundId compoundId;
    AbortExcn abortExcn;
    VoidPtrHash voidPtrHash;
};
#endif

FENNEL_END_CPPFILE("$Id$");

// End Memory.cpp
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// FILE:          CircularBuffer.cpp
// PROJECT:       Micro-Manager
// SUBSYSTEM:     MMCore
//-----------------------------------------------------------------------------
// DESCRIPTION:   Generic implementation of the circular buffer. The buffer
//                allows only one thread to enter at a time by using a mutex lock.
//                This makes the buffer succeptible to race conditions if the
//                calling threads are mutally dependent.
//
// COPYRIGHT:     University of California, San Francisco, 2007,
//
// LICENSE:       This file is distributed under the "Lesser GPL" (LGPL) license.
//                License text is included with the source distribution.
//
//                This file is distributed in the hope that it will be useful,
//                but WITHOUT ANY WARRANTY; without even the implied warranty
//                of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//                IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//                CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//                INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES.
//
// AUTHOR:        Nenad Amodaj, nenad@amodaj.com, 01/05/2007
//
#include "CircularBuffer.h"
#include "CoreUtils.h"
#include "../MMDevice/DeviceUtils.h"
#include "../MMDevice/DeviceThreads.h"
#include "boost/date_time/posix_time/posix_time.hpp"
#include <cstdio>

#ifdef WIN32
#undef min // avoid clash with the system defined macros
#endif

const long long bytesInMB = 1 << 20;
const long adjustThreshold = LONG_MAX / 2;
const unsigned long maxCBSize = 100000;    //a reasonable limit to circular buffer size

CircularBuffer::CircularBuffer(unsigned int memorySizeMB) :
   width_(0),
   height_(0),
   pixDepth_(0),
   imageCounter_(0),
   insertIndex_(0),
   saveIndex_(0),
   memorySizeMB_(memorySizeMB),
   overflow_(false),
   estimatedIntervalMs_(0)
{
}

CircularBuffer::~CircularBuffer() {}

bool CircularBuffer::Initialize(unsigned channels, unsigned slices, unsigned int w, unsigned int h, unsigned int pixDepth)
{
   MMThreadGuard guard(g_bufferLock);
   imageNumbers_.clear();

   bool ret = true;
   try
   {
      if (w == 0 || h==0 || pixDepth == 0 || channels == 0 || slices == 0)
         return false; // does not make sense

      if (w == width_ && height_ == h && pixDepth_ == pixDepth && channels == numChannels_ && slices == numSlices_)
         if (frameArray_.size() > 0)
            return true; // nothing to change

      width_ = w;
      height_ = h;
      pixDepth_ = pixDepth;
      numChannels_ = channels;
      numSlices_ = slices;

      insertIndex_ = 0;
      saveIndex_ = 0;
      overflow_ = false;

      // calculate the size of the entire buffer array once all images get allocated
      // the actual size at the time of the creation is going to be less, because
      // images are not allocated until pixels become available
      unsigned long frameSizeBytes = width_ * height_ * pixDepth_ * numChannels_ * numSlices_;
      unsigned long cbSize = (unsigned long) ((memorySizeMB_ * bytesInMB) / frameSizeBytes);

      if (cbSize == 0)
      {
         frameArray_.resize(0);
         return false; // memory footprint too small
      }

      // set a reasonable limit to circular buffer capacity
      if (cbSize > maxCBSize)
         cbSize = maxCBSize;

      // TODO: verify if we have enough RAM to satisfy this request

      for (unsigned long i=0; i<frameArray_.size(); i++)
         frameArray_[i].Clear();

      // allocate buffers  - could conceivably throw an out-of-memory exception
      frameArray_.resize(cbSize);
      for (unsigned long i=0; i<frameArray_.size(); i++)
      {
         frameArray_[i].Resize(w, h, pixDepth);
         frameArray_[i].Preallocate(numChannels_, numSlices_);
      }
   }

   catch( ... /* std::bad_alloc& ex */)
   {
      frameArray_.resize(0);
      ret = false;
   }
   return ret;
}

unsigned long CircularBuffer::GetSize() const
{
   MMThreadGuard guard(g_bufferLock);
   return (unsigned long)frameArray_.size();
}

unsigned long CircularBuffer::GetFreeSize() const
{
   MMThreadGuard guard(g_bufferLock);
   long freeSize = (long)frameArray_.size() - (insertIndex_ - saveIndex_);
   if (freeSize < 0)
      return 0;
   else
      return (unsigned long)freeSize;
}

unsigned long CircularBuffer::GetRemainingImageCount() const
{
   MMThreadGuard guard(g_bufferLock);
   return (unsigned long)(insertIndex_ - saveIndex_);
}

/**
* Inserts a single image in the buffer.
*/
bool CircularBuffer::InsertImage(const unsigned char* pixArray, unsigned int width, unsigned int height, unsigned int byteDepth, const Metadata* pMd) throw (CMMError)
{
   return InsertMultiChannel(pixArray, 1, width, height, byteDepth, pMd);
}

/**
* Inserts a multi-channel frame in the buffer.
*/
bool CircularBuffer::InsertMultiChannel(const unsigned char* pixArray, unsigned numChannels, unsigned width, unsigned height, unsigned byteDepth, const Metadata* pMd) throw (CMMError)
{
   MMThreadGuard guard(g_insertLock);

   static unsigned long previousTicks = 0;
   bool notOverflowed;
   ImgBuffer* pImg;
   unsigned long singleChannelSize = (unsigned long)width * height * byteDepth;

   {
      MMThreadGuard guard(g_bufferLock);

      if (previousTicks > 0)
         estimatedIntervalMs_ = GetClockTicksMs() - previousTicks;
      else
         estimatedIntervalMs_ = 0;

      // check image dimensions
      if (width != width_ || height_ != height || byteDepth != byteDepth)
         throw CMMError("Incompatible image dimensions in the circular buffer", MMERR_CircularBufferIncompatibleImage);

      notOverflowed = (long)frameArray_.size() - (insertIndex_ - saveIndex_) > 0;
      if (!notOverflowed) {
         // buffer overflow
         overflow_ = true;
         return false;
      }

   }

   for (unsigned i=0; i<numChannels; i++)
   {
      Metadata md;
      {
         MMThreadGuard guard(g_bufferLock);
         // check if the requested (channel, slice) combination exists
         // we assume that all buffers are pre-allocated
         pImg = frameArray_[insertIndex_ % frameArray_.size()].FindImage(i, 0);
         if (!pImg)
            return false;

         if (pMd)
         {
            // TODO: the same metadata is inserted for each channel ???
            // Perhaps we need to add specific tags to each channel
            md = *pMd;
         }

         std::string cameraName = md.GetSingleTag("Camera").GetValue();
         if (imageNumbers_.end() == imageNumbers_.find(cameraName))
         {
            imageNumbers_[cameraName] = 0;
         }

         // insert image number.
         md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageNumbers_[cameraName]));
         ++imageNumbers_[cameraName];
      }

      if (!md.HasTag(MM::g_Keyword_Elapsed_Time_ms))
      {
         // if time tag was not supplied by the camera insert current timestamp
         MM::MMTime timestamp = GetMMTimeNow();
         md.PutImageTag(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString(timestamp.getMsec()));
      }

      md.PutImageTag("Width",width);
      md.PutImageTag("Height",height);
      if (byteDepth == 1)
         md.PutImageTag("PixelType","GRAY8");
      else if (byteDepth == 2)
         md.PutImageTag("PixelType","GRAY16");
      else if (byteDepth == 4)
         md.PutImageTag("PixelType","RGB32");
      else if (byteDepth == 8)
         md.PutImageTag("PixelType","RGB64");
      else
         md.PutImageTag("PixelType","Unknown");

      pImg->SetMetadata(md);
      pImg->SetPixels(pixArray + i*singleChannelSize);
   }

   {
      MMThreadGuard guard(g_bufferLock);

      imageCounter_++;
      insertIndex_++;
      if ((insertIndex_ - (long)frameArray_.size()) > adjustThreshold && (saveIndex_- (long)frameArray_.size()) > adjustThreshold)
      {
         // adjust buffer indices to avoid overflowing integer size
         insertIndex_ -= adjustThreshold;
         saveIndex_ -= adjustThreshold;
      }
   }

   previousTicks = GetClockTicksMs();

   return true;

}

const unsigned char* CircularBuffer::GetTopImage() const
{
   MMThreadGuard guard(g_bufferLock);

   if (frameArray_.size() == 0)
      return 0;

   if (insertIndex_ == 0)
      return frameArray_[0].GetPixels(0, 0);
   else
      return frameArray_[(insertIndex_-1) % frameArray_.size()].GetPixels(0, 0);
}

const ImgBuffer* CircularBuffer::GetTopImageBuffer(unsigned channel, unsigned slice) const
{
   MMThreadGuard guard(g_bufferLock);

   if (frameArray_.size() == 0)
      return 0;

   // TODO: we may return NULL pointer if channel and slice indexes are wrong
   // this will cause problem in the SWIG - Java layer
   if (insertIndex_ == 0)
      return frameArray_[0].FindImage(channel, slice);
   else
      return frameArray_[(insertIndex_-1) % frameArray_.size()].FindImage(channel, slice);
}

/**
* Returns an ImgBuffer to the image inserted n images before the last one
*/
const ImgBuffer* CircularBuffer::GetNthFromTopImageBuffer(unsigned long n) const
{
   MMThreadGuard guard(g_bufferLock);

   if (frameArray_.size() == 0)
      return 0;

   if (n >= frameArray_.size() )
      return 0;

   if ((unsigned long) insertIndex_ <= n)
   {
      return frameArray_[(frameArray_.size() - n + insertIndex_ - 1) % frameArray_.size()].FindImage(0, 0);
   }

   return frameArray_[(insertIndex_-1-n) % frameArray_.size()].FindImage(0, 0);
}

const unsigned char* CircularBuffer::GetNextImage()
{
   MMThreadGuard guard(g_bufferLock);

   if (saveIndex_ < insertIndex_)
   {
      const unsigned char* pBuf = frameArray_[(saveIndex_) % frameArray_.size()].GetPixels(0, 0);
      saveIndex_++;
      return pBuf;
   }
   return 0;
}

const ImgBuffer* CircularBuffer::GetNextImageBuffer(unsigned channel, unsigned slice)
{
   MMThreadGuard guard(g_bufferLock);

   // TODO: we may return NULL pointer if channel and slice indexes are wrong
   // this will cause problem in the SWIG - Java layer
   if (saveIndex_ < insertIndex_)
   {
      const ImgBuffer* pBuf = frameArray_[(saveIndex_) % frameArray_.size()].FindImage(channel, slice);
      saveIndex_++;
      return pBuf;
   }
   return 0;
}

double CircularBuffer::GetAverageIntervalMs() const
{
   MMThreadGuard guard(g_bufferLock);

   return (double)estimatedIntervalMs_;
}

//N.B. an unsigned long millisecond clock tick rolls over in 47 days.
// millisecond clock tick incrementing from the time first requested
unsigned long CircularBuffer::GetClockTicksMs() const
{
   using namespace boost::posix_time;
   using namespace boost::gregorian;
   // use tick from the first time this is call is requested
   static boost::posix_time::ptime sst(boost::date_time::not_a_date_time);
   if (boost::posix_time::ptime(boost::date_time::not_a_date_time) == sst)
   {
      boost::gregorian::date today( day_clock::local_day());
      sst = boost::posix_time::ptime(today);
   }

   boost::posix_time::ptime t = boost::posix_time::microsec_clock::local_time();

   time_duration diff = t - sst;
   return static_cast<unsigned long>(diff.total_milliseconds());

}
<commit_msg>Fix a broken check for buffer size in CircularBuffer.<commit_after>///////////////////////////////////////////////////////////////////////////////
// FILE:          CircularBuffer.cpp
// PROJECT:       Micro-Manager
// SUBSYSTEM:     MMCore
//-----------------------------------------------------------------------------
// DESCRIPTION:   Generic implementation of the circular buffer. The buffer
//                allows only one thread to enter at a time by using a mutex lock.
//                This makes the buffer succeptible to race conditions if the
//                calling threads are mutally dependent.
//
// COPYRIGHT:     University of California, San Francisco, 2007,
//
// LICENSE:       This file is distributed under the "Lesser GPL" (LGPL) license.
//                License text is included with the source distribution.
//
//                This file is distributed in the hope that it will be useful,
//                but WITHOUT ANY WARRANTY; without even the implied warranty
//                of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//                IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//                CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//                INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES.
//
// AUTHOR:        Nenad Amodaj, nenad@amodaj.com, 01/05/2007
//
#include "CircularBuffer.h"
#include "CoreUtils.h"
#include "../MMDevice/DeviceUtils.h"
#include "../MMDevice/DeviceThreads.h"
#include "boost/date_time/posix_time/posix_time.hpp"
#include <cstdio>

#ifdef WIN32
#undef min // avoid clash with the system defined macros
#endif

const long long bytesInMB = 1 << 20;
const long adjustThreshold = LONG_MAX / 2;
const unsigned long maxCBSize = 100000;    //a reasonable limit to circular buffer size

CircularBuffer::CircularBuffer(unsigned int memorySizeMB) :
   width_(0),
   height_(0),
   pixDepth_(0),
   imageCounter_(0),
   insertIndex_(0),
   saveIndex_(0),
   memorySizeMB_(memorySizeMB),
   overflow_(false),
   estimatedIntervalMs_(0)
{
}

CircularBuffer::~CircularBuffer() {}

bool CircularBuffer::Initialize(unsigned channels, unsigned slices, unsigned int w, unsigned int h, unsigned int pixDepth)
{
   MMThreadGuard guard(g_bufferLock);
   imageNumbers_.clear();

   bool ret = true;
   try
   {
      if (w == 0 || h==0 || pixDepth == 0 || channels == 0 || slices == 0)
         return false; // does not make sense

      if (w == width_ && height_ == h && pixDepth_ == pixDepth && channels == numChannels_ && slices == numSlices_)
         if (frameArray_.size() > 0)
            return true; // nothing to change

      width_ = w;
      height_ = h;
      pixDepth_ = pixDepth;
      numChannels_ = channels;
      numSlices_ = slices;

      insertIndex_ = 0;
      saveIndex_ = 0;
      overflow_ = false;

      // calculate the size of the entire buffer array once all images get allocated
      // the actual size at the time of the creation is going to be less, because
      // images are not allocated until pixels become available
      unsigned long frameSizeBytes = width_ * height_ * pixDepth_ * numChannels_ * numSlices_;
      unsigned long cbSize = (unsigned long) ((memorySizeMB_ * bytesInMB) / frameSizeBytes);

      if (cbSize == 0)
      {
         frameArray_.resize(0);
         return false; // memory footprint too small
      }

      // set a reasonable limit to circular buffer capacity
      if (cbSize > maxCBSize)
         cbSize = maxCBSize;

      // TODO: verify if we have enough RAM to satisfy this request

      for (unsigned long i=0; i<frameArray_.size(); i++)
         frameArray_[i].Clear();

      // allocate buffers  - could conceivably throw an out-of-memory exception
      frameArray_.resize(cbSize);
      for (unsigned long i=0; i<frameArray_.size(); i++)
      {
         frameArray_[i].Resize(w, h, pixDepth);
         frameArray_[i].Preallocate(numChannels_, numSlices_);
      }
   }

   catch( ... /* std::bad_alloc& ex */)
   {
      frameArray_.resize(0);
      ret = false;
   }
   return ret;
}

unsigned long CircularBuffer::GetSize() const
{
   MMThreadGuard guard(g_bufferLock);
   return (unsigned long)frameArray_.size();
}

unsigned long CircularBuffer::GetFreeSize() const
{
   MMThreadGuard guard(g_bufferLock);
   long freeSize = (long)frameArray_.size() - (insertIndex_ - saveIndex_);
   if (freeSize < 0)
      return 0;
   else
      return (unsigned long)freeSize;
}

unsigned long CircularBuffer::GetRemainingImageCount() const
{
   MMThreadGuard guard(g_bufferLock);
   return (unsigned long)(insertIndex_ - saveIndex_);
}

/**
* Inserts a single image in the buffer.
*/
bool CircularBuffer::InsertImage(const unsigned char* pixArray, unsigned int width, unsigned int height, unsigned int byteDepth, const Metadata* pMd) throw (CMMError)
{
   return InsertMultiChannel(pixArray, 1, width, height, byteDepth, pMd);
}

/**
* Inserts a multi-channel frame in the buffer.
*/
bool CircularBuffer::InsertMultiChannel(const unsigned char* pixArray, unsigned numChannels, unsigned width, unsigned height, unsigned byteDepth, const Metadata* pMd) throw (CMMError)
{
   MMThreadGuard guard(g_insertLock);

   static unsigned long previousTicks = 0;
   bool notOverflowed;
   ImgBuffer* pImg;
   unsigned long singleChannelSize = (unsigned long)width * height * byteDepth;

   {
      MMThreadGuard guard(g_bufferLock);

      if (previousTicks > 0)
         estimatedIntervalMs_ = GetClockTicksMs() - previousTicks;
      else
         estimatedIntervalMs_ = 0;

      // check image dimensions
      if (width != width_ || height != height_ || byteDepth != pixDepth_)
         throw CMMError("Incompatible image dimensions in the circular buffer", MMERR_CircularBufferIncompatibleImage);

      notOverflowed = (long)frameArray_.size() - (insertIndex_ - saveIndex_) > 0;
      if (!notOverflowed) {
         // buffer overflow
         overflow_ = true;
         return false;
      }

   }

   for (unsigned i=0; i<numChannels; i++)
   {
      Metadata md;
      {
         MMThreadGuard guard(g_bufferLock);
         // check if the requested (channel, slice) combination exists
         // we assume that all buffers are pre-allocated
         pImg = frameArray_[insertIndex_ % frameArray_.size()].FindImage(i, 0);
         if (!pImg)
            return false;

         if (pMd)
         {
            // TODO: the same metadata is inserted for each channel ???
            // Perhaps we need to add specific tags to each channel
            md = *pMd;
         }

         std::string cameraName = md.GetSingleTag("Camera").GetValue();
         if (imageNumbers_.end() == imageNumbers_.find(cameraName))
         {
            imageNumbers_[cameraName] = 0;
         }

         // insert image number.
         md.put(MM::g_Keyword_Metadata_ImageNumber, CDeviceUtils::ConvertToString(imageNumbers_[cameraName]));
         ++imageNumbers_[cameraName];
      }

      if (!md.HasTag(MM::g_Keyword_Elapsed_Time_ms))
      {
         // if time tag was not supplied by the camera insert current timestamp
         MM::MMTime timestamp = GetMMTimeNow();
         md.PutImageTag(MM::g_Keyword_Elapsed_Time_ms, CDeviceUtils::ConvertToString(timestamp.getMsec()));
      }

      md.PutImageTag("Width",width);
      md.PutImageTag("Height",height);
      if (byteDepth == 1)
         md.PutImageTag("PixelType","GRAY8");
      else if (byteDepth == 2)
         md.PutImageTag("PixelType","GRAY16");
      else if (byteDepth == 4)
         md.PutImageTag("PixelType","RGB32");
      else if (byteDepth == 8)
         md.PutImageTag("PixelType","RGB64");
      else
         md.PutImageTag("PixelType","Unknown");

      pImg->SetMetadata(md);
      pImg->SetPixels(pixArray + i*singleChannelSize);
   }

   {
      MMThreadGuard guard(g_bufferLock);

      imageCounter_++;
      insertIndex_++;
      if ((insertIndex_ - (long)frameArray_.size()) > adjustThreshold && (saveIndex_- (long)frameArray_.size()) > adjustThreshold)
      {
         // adjust buffer indices to avoid overflowing integer size
         insertIndex_ -= adjustThreshold;
         saveIndex_ -= adjustThreshold;
      }
   }

   previousTicks = GetClockTicksMs();

   return true;

}

const unsigned char* CircularBuffer::GetTopImage() const
{
   MMThreadGuard guard(g_bufferLock);

   if (frameArray_.size() == 0)
      return 0;

   if (insertIndex_ == 0)
      return frameArray_[0].GetPixels(0, 0);
   else
      return frameArray_[(insertIndex_-1) % frameArray_.size()].GetPixels(0, 0);
}

const ImgBuffer* CircularBuffer::GetTopImageBuffer(unsigned channel, unsigned slice) const
{
   MMThreadGuard guard(g_bufferLock);

   if (frameArray_.size() == 0)
      return 0;

   // TODO: we may return NULL pointer if channel and slice indexes are wrong
   // this will cause problem in the SWIG - Java layer
   if (insertIndex_ == 0)
      return frameArray_[0].FindImage(channel, slice);
   else
      return frameArray_[(insertIndex_-1) % frameArray_.size()].FindImage(channel, slice);
}

/**
* Returns an ImgBuffer to the image inserted n images before the last one
*/
const ImgBuffer* CircularBuffer::GetNthFromTopImageBuffer(unsigned long n) const
{
   MMThreadGuard guard(g_bufferLock);

   if (frameArray_.size() == 0)
      return 0;

   if (n >= frameArray_.size() )
      return 0;

   if ((unsigned long) insertIndex_ <= n)
   {
      return frameArray_[(frameArray_.size() - n + insertIndex_ - 1) % frameArray_.size()].FindImage(0, 0);
   }

   return frameArray_[(insertIndex_-1-n) % frameArray_.size()].FindImage(0, 0);
}

const unsigned char* CircularBuffer::GetNextImage()
{
   MMThreadGuard guard(g_bufferLock);

   if (saveIndex_ < insertIndex_)
   {
      const unsigned char* pBuf = frameArray_[(saveIndex_) % frameArray_.size()].GetPixels(0, 0);
      saveIndex_++;
      return pBuf;
   }
   return 0;
}

const ImgBuffer* CircularBuffer::GetNextImageBuffer(unsigned channel, unsigned slice)
{
   MMThreadGuard guard(g_bufferLock);

   // TODO: we may return NULL pointer if channel and slice indexes are wrong
   // this will cause problem in the SWIG - Java layer
   if (saveIndex_ < insertIndex_)
   {
      const ImgBuffer* pBuf = frameArray_[(saveIndex_) % frameArray_.size()].FindImage(channel, slice);
      saveIndex_++;
      return pBuf;
   }
   return 0;
}

double CircularBuffer::GetAverageIntervalMs() const
{
   MMThreadGuard guard(g_bufferLock);

   return (double)estimatedIntervalMs_;
}

//N.B. an unsigned long millisecond clock tick rolls over in 47 days.
// millisecond clock tick incrementing from the time first requested
unsigned long CircularBuffer::GetClockTicksMs() const
{
   using namespace boost::posix_time;
   using namespace boost::gregorian;
   // use tick from the first time this is call is requested
   static boost::posix_time::ptime sst(boost::date_time::not_a_date_time);
   if (boost::posix_time::ptime(boost::date_time::not_a_date_time) == sst)
   {
      boost::gregorian::date today( day_clock::local_day());
      sst = boost::posix_time::ptime(today);
   }

   boost::posix_time::ptime t = boost::posix_time::microsec_clock::local_time();

   time_duration diff = t - sst;
   return static_cast<unsigned long>(diff.total_milliseconds());

}
<|endoftext|>


<commit_before>
#include "Player.h"

#define PSPEED 200

Player::Player(Projectiles *projs):
psprite(0),
inventory(0),
pdir(D2DEDIR_DOWN)
{

    projectiles = projs;
    inventory =  new Inventory(projs);

    inventory->addPistol();
    inventory->selectPistol();

    psprite = new cgf::Sprite();

    psprite->load("data/img/hero_down.png");
    psprite->setPosition(50,100);

}

Player::~Player(){
    delete psprite;
    delete inventory;
}

void Player::setPosition(float x, float y){
    psprite->setPosition(x,y);
}

cgf::Sprite *Player::getSprite(){
    return psprite;
}

void Player::impulseLeft(){
    psprite->load("data/img/hero_left.png");
    psprite->play();
    psprite->setXspeed(-PSPEED);
    pdir =  D2DEDIR_LEFT;
}

void Player::impulseUp(){
    psprite->load("data/img/hero_up.png");
    psprite->play();
    psprite->setYspeed(-PSPEED);
    pdir = D2DEDIR_UP;
}

void Player::impulseRight(){
    psprite->load("data/img/hero_right.png");
    psprite->play();
    psprite->setXspeed(PSPEED);
    pdir = D2DEDIR_RIGHT;
}

void Player::impulseDown(){
    psprite->load("data/img/hero_down.png");
    psprite->play();
    psprite->setYspeed(PSPEED);
    pdir = D2DEDIR_DOWN;
}

void Player::impulseHalt(){
    psprite->setCurrentFrame(0);
    psprite->setYspeed(0);
    psprite->setXspeed(0);
    psprite->pause();
}

void Player::impulseShoot()
{
    sf::Vector2f ppos = psprite->getPosition();
    inventory->getSelectedWeapon()->fire(ppos.x, ppos.y, pdir);
}

void Player::draw(cgf::Game* game){
    game->getScreen()->draw(*psprite);
}

const sf::Vector2f& Player::getPosition(){
    return psprite->getPosition();
}

Inventory * Player::getInventory(){
    return this->inventory;
}

void Player::impulseSelectPistol(){
    this->inventory->selectPistol();
}

void Player::impulseSelectShotgun(){
    this->inventory->selectShotgun();
}

void Player::impulseSelectRocketLauncher(){
    this->inventory->selectRocketLauncher();
}
<commit_msg>Remove unused animation method calls from player object (we were not able to get the animation stuff to work :( )<commit_after>
#include "Player.h"

#define PSPEED 200

Player::Player(Projectiles *projs):
psprite(0),
inventory(0),
pdir(D2DEDIR_DOWN)
{

    projectiles = projs;
    inventory =  new Inventory(projs);

    inventory->addPistol();
    inventory->selectPistol();

    psprite = new cgf::Sprite();

    psprite->load("data/img/hero_down.png");
    psprite->setPosition(50,100);

}

Player::~Player(){
    delete psprite;
    delete inventory;
}

void Player::setPosition(float x, float y){
    psprite->setPosition(x,y);
}

cgf::Sprite *Player::getSprite(){
    return psprite;
}

void Player::impulseLeft(){
    psprite->load("data/img/hero_left.png");
    psprite->setXspeed(-PSPEED);
    pdir =  D2DEDIR_LEFT;
}

void Player::impulseUp(){
    psprite->load("data/img/hero_up.png");
    psprite->setYspeed(-PSPEED);
    pdir = D2DEDIR_UP;
}

void Player::impulseRight(){
    psprite->load("data/img/hero_right.png");
    psprite->setXspeed(PSPEED);
    pdir = D2DEDIR_RIGHT;
}

void Player::impulseDown(){
    psprite->load("data/img/hero_down.png");
    psprite->setYspeed(PSPEED);
    pdir = D2DEDIR_DOWN;
}

void Player::impulseHalt(){
    psprite->setYspeed(0);
    psprite->setXspeed(0);
}

void Player::impulseShoot()
{
    sf::Vector2f ppos = psprite->getPosition();
    inventory->getSelectedWeapon()->fire(ppos.x, ppos.y, pdir);
}

void Player::draw(cgf::Game* game){
    game->getScreen()->draw(*psprite);
}

const sf::Vector2f& Player::getPosition(){
    return psprite->getPosition();
}

Inventory * Player::getInventory(){
    return this->inventory;
}

void Player::impulseSelectPistol(){
    this->inventory->selectPistol();
}

void Player::impulseSelectShotgun(){
    this->inventory->selectShotgun();
}

void Player::impulseSelectRocketLauncher(){
    this->inventory->selectRocketLauncher();
}
<|endoftext|>


<commit_before>//
//  HUDRenderer.cpp
//  G3MiOSSDK
//
//  Created by Diego Gomez Deck on 12/17/13.
//
//

#include "HUDRenderer.hpp"

#include "HUDWidget.hpp"

#include "MutableMatrix44D.hpp"
#include "GLFeature.hpp"
#include "GLState.hpp"
#include "GL.hpp"
#include "Context.hpp"

HUDRenderer::HUDRenderer(bool readyWhenWidgetsReady) :
_glState(new GLState()),
_readyWhenWidgetsReady(readyWhenWidgetsReady),
_context(NULL)
{
  _widgetsSize = _widgets.size();
}

HUDRenderer::~HUDRenderer() {
  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    delete widget;
  }

  _glState->_release();

#ifdef JAVA_CODE
  super.dispose();
#endif
}

void HUDRenderer::initialize(const G3MContext* context) {
  _context = context;

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    widget->initialize(context);
  }
}

void HUDRenderer::addWidget(HUDWidget* widget) {
  _widgets.push_back(widget);
  _widgetsSize = _widgets.size();

  if (_context != NULL) {
    widget->initialize(_context);
  }
}

RenderState HUDRenderer::getRenderState(const G3MRenderContext* rc) {
  if (_widgetsSize == 0) {
    return RenderState::ready();
  }

  _errors.clear();
  bool busyFlag  = false;
  bool errorFlag = false;

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    if (widget->isEnable()) {
      const RenderState childRenderState = widget->getRenderState(rc);

      const RenderState_Type childRenderStateType = childRenderState._type;

      if (childRenderStateType == RENDER_ERROR) {
        errorFlag = true;

        const std::vector<std::string> childErrors = childRenderState.getErrors();
#ifdef C_CODE
        _errors.insert(_errors.end(),
                       childErrors.begin(),
                       childErrors.end());
#endif
#ifdef JAVA_CODE
        _errors.addAll(childErrors);
#endif
      }
      else if (childRenderStateType == RENDER_BUSY) {
        busyFlag = true;
      }
    }
  }

  if (errorFlag) {
    return RenderState::error(_errors);
  }
  else if (busyFlag && _readyWhenWidgetsReady) {
    return RenderState::busy();
  }
  else {
    return RenderState::ready();
  }
}

bool HUDRenderer::onTouchEvent(const G3MEventContext* ec,
                               const TouchEvent* touchEvent) {
  return false;
}

void HUDRenderer::onResizeViewportEvent(const G3MEventContext* ec,
                                        int width,
                                        int height) {
  const int halfWidth  = width  / 2;
  const int halfHeight = height / 2;
  MutableMatrix44D projectionMatrix = MutableMatrix44D::createOrthographicProjectionMatrix(-halfWidth,  halfWidth,
                                                                                -halfHeight, halfHeight,
                                                                                -halfWidth,  halfWidth);

  ProjectionGLFeature* pr = (ProjectionGLFeature*) _glState->getGLFeature(GLF_PROJECTION);
  if (pr == NULL) {
    _glState->addGLFeature(new ProjectionGLFeature(projectionMatrix.asMatrix44D()),
                           false);
  }
  else {
    pr->setMatrix(projectionMatrix.asMatrix44D());
  }

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    widget->onResizeViewportEvent(ec, width, height);
  }
}

void HUDRenderer::render(const G3MRenderContext* rc,
                         GLState* glState) {

  INativeGL* nativeGL = rc->getGL()->getNative();

  nativeGL->depthMask(false);

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    widget->render(rc, _glState);
  }

  nativeGL->depthMask(true);
}
<commit_msg>slow progress - not yet usable<commit_after>//
//  HUDRenderer.cpp
//  G3MiOSSDK
//
//  Created by Diego Gomez Deck on 12/17/13.
//
//

#include "HUDRenderer.hpp"

#include "HUDWidget.hpp"

#include "MutableMatrix44D.hpp"
#include "GLFeature.hpp"
#include "GLState.hpp"
#include "GL.hpp"
#include "Context.hpp"

HUDRenderer::HUDRenderer(bool readyWhenWidgetsReady) :
_glState(new GLState()),
_readyWhenWidgetsReady(readyWhenWidgetsReady),
_context(NULL)
{
  _widgetsSize = _widgets.size();
}

HUDRenderer::~HUDRenderer() {
  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    delete widget;
  }

  _glState->_release();

#ifdef JAVA_CODE
  super.dispose();
#endif
}

void HUDRenderer::initialize(const G3MContext* context) {
  _context = context;

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    widget->initialize(context);
  }
}

void HUDRenderer::addWidget(HUDWidget* widget) {
  _widgets.push_back(widget);
  _widgetsSize = _widgets.size();

  if (_context != NULL) {
    widget->initialize(_context);
  }
}

RenderState HUDRenderer::getRenderState(const G3MRenderContext* rc) {
  if (_widgetsSize == 0) {
    return RenderState::ready();
  }

  _errors.clear();
  bool busyFlag  = false;
  bool errorFlag = false;

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    if (widget->isEnable()) {
      const RenderState childRenderState = widget->getRenderState(rc);

      const RenderState_Type childRenderStateType = childRenderState._type;

      if (childRenderStateType == RENDER_ERROR) {
        errorFlag = true;

        const std::vector<std::string> childErrors = childRenderState.getErrors();
#ifdef C_CODE
        _errors.insert(_errors.end(),
                       childErrors.begin(),
                       childErrors.end());
#endif
#ifdef JAVA_CODE
        _errors.addAll(childErrors);
#endif
      }
      else if (childRenderStateType == RENDER_BUSY) {
        busyFlag = true;
      }
    }
  }

  if (errorFlag) {
    return RenderState::error(_errors);
  }
  else if (busyFlag && _readyWhenWidgetsReady) {
    return RenderState::busy();
  }
  else {
    return RenderState::ready();
  }
}

bool HUDRenderer::onTouchEvent(const G3MEventContext* ec,
                               const TouchEvent* touchEvent) {
  return false;
}

void HUDRenderer::onResizeViewportEvent(const G3MEventContext* ec,
                                        int width,
                                        int height) {
  const int halfWidth  = width  / 2;
  const int halfHeight = height / 2;
  MutableMatrix44D projectionMatrix = MutableMatrix44D::createOrthographicProjectionMatrix(-halfWidth,  halfWidth,
                                                                                -halfHeight, halfHeight,
                                                                                -halfWidth,  halfWidth);

  ProjectionGLFeature* pr = (ProjectionGLFeature*) _glState->getGLFeature(GLF_PROJECTION);
  if (pr == NULL) {
    _glState->addGLFeature(new ProjectionGLFeature(projectionMatrix.asMatrix44D()),
                           false);
  }
  else {
    pr->setMatrix(projectionMatrix.asMatrix44D());
  }

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    widget->onResizeViewportEvent(ec, width, height);
  }
}

void HUDRenderer::render(const G3MRenderContext* rc,
                         GLState* glState) {
  if (_widgetsSize == 0) {
    return;
  }

  INativeGL* nativeGL = rc->getGL()->getNative();

  nativeGL->depthMask(false);

  for (int i = 0; i < _widgetsSize; i++) {
    HUDWidget* widget = _widgets[i];
    widget->render(rc, _glState);
  }

  nativeGL->depthMask(true);
}
<|endoftext|>