<commit_before>/*-------------------------------------------------------------------------
 *
 *   FILE
 *	robusttransaction.cxx
 *
 *   DESCRIPTION
 *      implementation of the pqxx::robusttransaction class.
 *   pqxx::robusttransaction is a slower but safer transaction class
 *
 * Copyright (c) 2002-2005, Jeroen T. Vermeulen <jtv@xs4all.nl>
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 *
 *-------------------------------------------------------------------------
 */
#include "pqxx/compiler.h"

#include <stdexcept>

#include "pqxx/connection_base"
#include "pqxx/result"
#include "pqxx/robusttransaction"

using namespace PGSTD;
using namespace pqxx::internal;

// TODO: Use two-phase transaction if backend supports it

#define SQL_COMMIT_WORK 	"COMMIT"
#define SQL_ROLLBACK_WORK 	"ROLLBACK"

pqxx::basic_robusttransaction::basic_robusttransaction(connection_base &C,
	const string &IsolationLevel,
	const string &TName) :
  dbtransaction(C,
      IsolationLevel,
      TName,
      "robusttransaction<"+IsolationLevel+">"),
  m_ID(oid_none),
  m_LogTable(),
  m_backendpid(-1)
{
  m_LogTable = string("PQXXLOG_") + conn().username();
}

pqxx::basic_robusttransaction::~basic_robusttransaction()
{
}

void pqxx::basic_robusttransaction::do_begin()
{
  start_backend_transaction();

  try
  {
    CreateTransactionRecord();
  }
  catch (const exception &)
  {
    // The problem here *may* be that the log table doesn't exist yet.  Create
    // one, start a new transaction, and try again.
    try { DirectExec(SQL_ROLLBACK_WORK); } catch (const exception &e) {}
    CreateLogTable();
    start_backend_transaction();
    m_backendpid = conn().backendpid();
    CreateTransactionRecord();
  }
}

void pqxx::basic_robusttransaction::do_commit()
{
  const IDType ID = m_ID;

  if (ID == oid_none)
    throw logic_error("libpqxx internal error: transaction "
		      "'" + name() + "' "
		     "has no ID");

  // Check constraints before sending the COMMIT to the database to reduce the
  // work being done inside our in-doubt window.  It also implicitly provides
  // one last check that our connection is really working before sending the
  // commit command.
  //
  // This may not an entirely clear-cut 100% obvious unambiguous Good Thing.  If
  // we lose our connection during the in-doubt window, it may be better if the
  // transaction didn't actually go though, and having constraints checked first
  // theoretically makes it more likely that it will once we get to that stage.
  // However, it seems reasonable to assume that most transactions will satisfy
  // their constraints.  Besides, the goal of robusttransaction is to weed out
  // indeterminacy, rather than to improve the odds of desirable behaviour when
  // all bets are off anyway.
  DirectExec("SET CONSTRAINTS ALL IMMEDIATE");

  // Here comes the critical part.  If we lose our connection here, we'll be
  // left clueless as to whether the backend got the message and is trying to
  // commit the transaction (let alone whether it will succeed if so).  This
  // case requires some special handling that makes robusttransaction what it
  // is.
  try
  {
    DirectExec(SQL_COMMIT_WORK);
  }
  catch (const exception &e)
  {
    // TODO: Can we limit this handler to broken_connection exceptions?
    m_ID = oid_none;
    if (!conn().is_open())
    {
      // We've lost the connection while committing.  We'll have to go back to
      // the backend and check our transaction log to see what happened.
      process_notice(e.what() + string("\n"));

      // See if transaction record ID exists; if yes, our transaction was
      // committed before the connection went down.  If not, the transaction
      // was aborted.
      bool Exists;
      try
      {
        Exists = CheckTransactionRecord(ID);
      }
      catch (const exception &f)
      {
	// Couldn't check for transaction record.  We're still in doubt as to
	// whether the transaction was performed.
        const string Msg = "WARNING: "
		    "Connection lost while committing transaction "
		    "'" + name() + "' (oid " + to_string(ID) + "). "
		    "Please check for this record in the "
		    "'" + m_LogTable + "' table.  "
		    "If the record exists, the transaction was executed. "
		    "If not, then it wasn't.\n";

        process_notice(Msg);
	process_notice("Could not verify existence of transaction record "
	    "because of the following error:\n");
	process_notice(string(f.what()) + "\n");

        throw in_doubt_error(Msg);
      }

      // Transaction record is gone, so all we have is a "normal" transaction
      // failure.
      if (!Exists) throw;
    }
    else
    {
      // Commit failed--probably due to a constraint violation or something
      // similar.  But we're still connected, so no worries from a consistency
      // point of view.

      // Try to delete transaction record ID, if it still exists (although it
      // really shouldn't)
      DeleteTransactionRecord(ID);
      throw;
    }
  }

  m_ID = oid_none;
  DeleteTransactionRecord(ID);
}

void pqxx::basic_robusttransaction::do_abort()
{
  m_ID = oid_none;

  // Rollback transaction.  Our transaction record will be dropped as a side
  // effect, which is what we want since "it never happened."
  DirectExec(SQL_ROLLBACK_WORK);
}

// Create transaction log table if it didn't already exist
void pqxx::basic_robusttransaction::CreateLogTable()
{
  // Create log table in case it doesn't already exist.  This code must only be
  // executed before the backend transaction has properly started.
  const string CrTab = "CREATE TABLE " + m_LogTable +
	               "("
	               "name VARCHAR(256), "
	               "date TIMESTAMP"
	               ")";

  try { DirectExec(CrTab.c_str(), 1); } catch (const exception &) { }
}

void pqxx::basic_robusttransaction::CreateTransactionRecord()
{
  const string Insert = "INSERT INTO " + m_LogTable + " "
	                "(name, date) "
			"VALUES "
	                "(" +
			(name().empty() ? "null" : "'"+sqlesc(name())+"'") +
			", "
	                "CURRENT_TIMESTAMP"
	                ")";

  m_ID = DirectExec(Insert.c_str()).inserted_oid();

  if (m_ID == oid_none)
    throw runtime_error("Could not create transaction log record");
}

void pqxx::basic_robusttransaction::DeleteTransactionRecord(IDType ID) throw ()
{
  if (ID == oid_none) return;

  try
  {
    // Try very, very hard to delete record.  Specify an absurd retry count to
    // ensure that the server gets a chance to restart before we give up.
    const string Del = "DELETE FROM " + m_LogTable + " "
	               "WHERE oid=" + to_string(ID);

    DirectExec(Del.c_str(), 20);

    // Now that we've arrived here, we're almost sure that record is quite dead.
    ID = oid_none;
  }
  catch (const exception &)
  {
  }

  if (ID != oid_none) try
  {
    process_notice("WARNING: "
	           "Failed to delete obsolete transaction record with oid " +
		   to_string(ID) + " ('" + name() + "'). "
		   "Please delete it manually.  Thank you.\n");
  }
  catch (const exception &)
  {
  }
}

// Attempt to establish whether transaction record with given ID still exists
bool pqxx::basic_robusttransaction::CheckTransactionRecord(IDType ID)
{
  /* First, wait for the old backend (with the lost connection) to die.
   *
   * On 2004-09-18, Tom Lane wrote:
   *
   * I don't see any reason for guesswork.  Remember the PID of the backend
   * you were connected to.  On reconnect, look in pg_stat_activity to see
   * if that backend is still alive; if so, sleep till it's not.  Then check
   * to see if your transaction committed or not.  No need for anything so
   * dangerous as a timeout.
   */
  /* Actually, looking if the query has finished is only possible if the
   * stats_command_string has been set in postgresql.conf and we're running
   * as the postgres superuser.  If we get a string saying this option has not
   * been enabled, we must wait as if the query were still executing--and hope
   * that the backend dies.
   */
  // TODO: Tom says we need stats_start_collector, not stats_command_string!?
  bool hold = true;
  for (int c=20; hold && c; internal::sleep_seconds(5), --c)
  {
    const result R(DirectExec(("SELECT current_query "
	  "FROM pq_stat_activity "
	  "WHERE procpid=" + to_string(m_backendpid)).c_str()));
    hold = (!R.empty() &&
      !R[0][0].as(string()).empty() &&
      (R[0][0].as(string()) != "<IDLE>"));
  }

  if (hold)
    throw runtime_error("Old backend process stays alive too long to wait for");

  // Now look for our transaction record
  const string Find = "SELECT oid FROM " + m_LogTable + " "
	              "WHERE oid=" + to_string(ID);

  return !DirectExec(Find.c_str(), 20).empty();
}

<commit_msg>Updated comments (outline new improvement); removed trailing whitespace<commit_after>/*-------------------------------------------------------------------------
 *
 *   FILE
 *	robusttransaction.cxx
 *
 *   DESCRIPTION
 *      implementation of the pqxx::robusttransaction class.
 *   pqxx::robusttransaction is a slower but safer transaction class
 *
 * Copyright (c) 2002-2005, Jeroen T. Vermeulen <jtv@xs4all.nl>
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 *
 *-------------------------------------------------------------------------
 */
#include "pqxx/compiler.h"

#include <stdexcept>

#include "pqxx/connection_base"
#include "pqxx/result"
#include "pqxx/robusttransaction"

using namespace PGSTD;
using namespace pqxx::internal;

// TODO: Use two-phase transaction if backend supports it

#define SQL_COMMIT_WORK 	"COMMIT"
#define SQL_ROLLBACK_WORK 	"ROLLBACK"

pqxx::basic_robusttransaction::basic_robusttransaction(connection_base &C,
	const string &IsolationLevel,
	const string &TName) :
  dbtransaction(C,
      IsolationLevel,
      TName,
      "robusttransaction<"+IsolationLevel+">"),
  m_ID(oid_none),
  m_LogTable(),
  m_backendpid(-1)
{
  m_LogTable = string("PQXXLOG_") + conn().username();
}

pqxx::basic_robusttransaction::~basic_robusttransaction()
{
}

void pqxx::basic_robusttransaction::do_begin()
{
  start_backend_transaction();

  try
  {
    CreateTransactionRecord();
  }
  catch (const exception &)
  {
    // The problem here *may* be that the log table doesn't exist yet.  Create
    // one, start a new transaction, and try again.
    try { DirectExec(SQL_ROLLBACK_WORK); } catch (const exception &e) {}
    CreateLogTable();
    start_backend_transaction();
    m_backendpid = conn().backendpid();
    CreateTransactionRecord();
  }
}

void pqxx::basic_robusttransaction::do_commit()
{
  const IDType ID = m_ID;

  if (ID == oid_none)
    throw logic_error("libpqxx internal error: transaction "
		      "'" + name() + "' "
		     "has no ID");

  // Check constraints before sending the COMMIT to the database to reduce the
  // work being done inside our in-doubt window.  It also implicitly provides
  // one last check that our connection is really working before sending the
  // commit command.
  //
  // This may not be an entirely clear-cut 100% obvious unambiguous Good Thing.
  // If we lose our connection during the in-doubt window, it may be better if
  // the transaction didn't actually go though, and having constraints checked
  // first theoretically makes it more likely that it will once we get to that
  // stage.
  //
  // However, it seems reasonable to assume that most transactions will satisfy
  // their constraints.  Besides, the goal of robusttransaction is to weed out
  // indeterminacy, rather than to improve the odds of desirable behaviour when
  // all bets are off anyway.
  DirectExec("SET CONSTRAINTS ALL IMMEDIATE");

  // Here comes the critical part.  If we lose our connection here, we'll be
  // left clueless as to whether the backend got the message and is trying to
  // commit the transaction (let alone whether it will succeed if so).  This
  // case requires some special handling that makes robusttransaction what it
  // is.
  try
  {
    DirectExec(SQL_COMMIT_WORK);
  }
  catch (const exception &e)
  {
    // TODO: Can we limit this handler to broken_connection exceptions?
    m_ID = oid_none;
    if (!conn().is_open())
    {
      // We've lost the connection while committing.  We'll have to go back to
      // the backend and check our transaction log to see what happened.
      process_notice(e.what() + string("\n"));

      // See if transaction record ID exists; if yes, our transaction was
      // committed before the connection went down.  If not, the transaction
      // was aborted.
      bool Exists;
      try
      {
        Exists = CheckTransactionRecord(ID);
      }
      catch (const exception &f)
      {
	// Couldn't check for transaction record.  We're still in doubt as to
	// whether the transaction was performed.
        const string Msg = "WARNING: "
		    "Connection lost while committing transaction "
		    "'" + name() + "' (oid " + to_string(ID) + "). "
		    "Please check for this record in the "
		    "'" + m_LogTable + "' table.  "
		    "If the record exists, the transaction was executed. "
		    "If not, then it wasn't.\n";

        process_notice(Msg);
	process_notice("Could not verify existence of transaction record "
	    "because of the following error:\n");
	process_notice(string(f.what()) + "\n");

        throw in_doubt_error(Msg);
      }

      // Transaction record is gone, so all we have is a "normal" transaction
      // failure.
      if (!Exists) throw;
    }
    else
    {
      // Commit failed--probably due to a constraint violation or something
      // similar.  But we're still connected, so no worries from a consistency
      // point of view.

      // Try to delete transaction record ID, if it still exists (although it
      // really shouldn't)
      DeleteTransactionRecord(ID);
      throw;
    }
  }

  m_ID = oid_none;
  DeleteTransactionRecord(ID);
}

void pqxx::basic_robusttransaction::do_abort()
{
  m_ID = oid_none;

  // Rollback transaction.  Our transaction record will be dropped as a side
  // effect, which is what we want since "it never happened."
  DirectExec(SQL_ROLLBACK_WORK);
}

// Create transaction log table if it didn't already exist
void pqxx::basic_robusttransaction::CreateLogTable()
{
  // Create log table in case it doesn't already exist.  This code must only be
  // executed before the backend transaction has properly started.
  const string CrTab = "CREATE TABLE " + m_LogTable +
	               "("
	               "name VARCHAR(256), "
	               "date TIMESTAMP"
	               ")";

  try { DirectExec(CrTab.c_str(), 1); } catch (const exception &) { }
}

void pqxx::basic_robusttransaction::CreateTransactionRecord()
{
  // TODO: Might as well include real transaction ID and backend PID
  const string Insert = "INSERT INTO " + m_LogTable + " "
	                "(name, date) "
			"VALUES "
	                "(" +
			(name().empty() ? "null" : "'"+sqlesc(name())+"'") +
			", "
	                "CURRENT_TIMESTAMP"
	                ")";

  m_ID = DirectExec(Insert.c_str()).inserted_oid();

  if (m_ID == oid_none)
    throw runtime_error("Could not create transaction log record");
}

void pqxx::basic_robusttransaction::DeleteTransactionRecord(IDType ID) throw ()
{
  if (ID == oid_none) return;

  try
  {
    // Try very, very hard to delete record.  Specify an absurd retry count to
    // ensure that the server gets a chance to restart before we give up.
    const string Del = "DELETE FROM " + m_LogTable + " "
	               "WHERE oid=" + to_string(ID);

    DirectExec(Del.c_str(), 20);

    // Now that we've arrived here, we're almost sure that record is quite dead.
    ID = oid_none;
  }
  catch (const exception &)
  {
  }

  if (ID != oid_none) try
  {
    process_notice("WARNING: "
	           "Failed to delete obsolete transaction record with oid " +
		   to_string(ID) + " ('" + name() + "'). "
		   "Please delete it manually.  Thank you.\n");
  }
  catch (const exception &)
  {
  }
}

// Attempt to establish whether transaction record with given ID still exists
bool pqxx::basic_robusttransaction::CheckTransactionRecord(IDType ID)
{
  /* First, wait for the old backend (with the lost connection) to die.
   *
   * On 2004-09-18, Tom Lane wrote:
   *
   * I don't see any reason for guesswork.  Remember the PID of the backend
   * you were connected to.  On reconnect, look in pg_stat_activity to see
   * if that backend is still alive; if so, sleep till it's not.  Then check
   * to see if your transaction committed or not.  No need for anything so
   * dangerous as a timeout.
   */
  /* Actually, looking if the query has finished is only possible if the
   * stats_command_string has been set in postgresql.conf and we're running
   * as the postgres superuser.  If we get a string saying this option has not
   * been enabled, we must wait as if the query were still executing--and hope
   * that the backend dies.
   */
  // TODO: Tom says we need stats_start_collector, not stats_command_string!?
  // TODO: This should work: "SELECT * FROM pg_locks WHERE pid=" + m_backendpid
  bool hold = true;
  for (int c=20; hold && c; internal::sleep_seconds(5), --c)
  {
    const result R(DirectExec(("SELECT current_query "
	  "FROM pq_stat_activity "
	  "WHERE procpid=" + to_string(m_backendpid)).c_str()));
    hold = (!R.empty() &&
      !R[0][0].as(string()).empty() &&
      (R[0][0].as(string()) != "<IDLE>"));
  }

  if (hold)
    throw runtime_error("Old backend process stays alive too long to wait for");

  // Now look for our transaction record
  const string Find = "SELECT oid FROM " + m_LogTable + " "
	              "WHERE oid=" + to_string(ID);

  return !DirectExec(Find.c_str(), 20).empty();
}

<|endoftext|>


<commit_before>/*******************************************************************************
    Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
    The use of this software is governed by the LICENSE file.
*******************************************************************************/

#include <taichi/system/threading.h>
#include <thread>
#include <condition_variable>

TC_NAMESPACE_BEGIN

class ThreadPool {
public:
  std::vector<std::thread> threads;
  std::condition_variable cv;
  std::mutex mutex;
  bool running;
  bool finished;

  void task() {
    while (true) {
      std::unique_lock<std::mutex> lock(mutex);
      cv.wait(lock, [this]{ return running;});
      if (finished) {
        break;
      }
      TC_TAG;
    }
  }

  ThreadPool(int num_threads) {
    running = false;
    finished = false;
    threads.resize((std::size_t)num_threads);
    for (int i = 0; i < num_threads; i++) {
      threads[i] = std::thread([this] {
        this->task();
      });
    }
  }

  void start() {
    {
      std::lock_guard<std::mutex> lg(mutex);
      running = true;
    }
    cv.notify_all();
  }

  ~ThreadPool() {
    {
      std::lock_guard<std::mutex> lg(mutex);
      finished = true;
    }
    cv.notify_all();
    for (int i = 0; i < threads.size(); i++) {
      threads[i].join();
    }
  }
};

bool test_threading() {
  auto tp = ThreadPool(4);
  tp.start();
  return true;
}

TC_NAMESPACE_END
<commit_msg>parallel task pooling<commit_after>/*******************************************************************************
    Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
    The use of this software is governed by the LICENSE file.
*******************************************************************************/

#include <taichi/system/threading.h>
#include <thread>
#include <condition_variable>

TC_NAMESPACE_BEGIN

class ThreadPool {
public:
  std::vector<std::thread> threads;
  std::condition_variable cv;
  std::mutex mutex;
  std::atomic<int> task_head;
  int task_tail;
  bool running;
  bool finished;

  void do_task(int i) {
    double ret = 0.0;
    for (int t = 0; t < 100000000; t++) {
      ret += t * 1e-20;
    }
    TC_P(i + ret);
  }

  void target() {
    while (true) {
      int task_id;
      {
        std::unique_lock<std::mutex> lock(mutex);
        cv.wait(lock, [this]{ return running;});
        task_id = task_head++;
        if (task_id > task_tail) {
          break;
        }
      }
      do_task(task_id);
    }
  }

  ThreadPool(int num_threads) {
    running = false;
    finished = false;
    threads.resize((std::size_t)num_threads);
    for (int i = 0; i < num_threads; i++) {
      threads[i] = std::thread([this] {
        this->target();
      });
    }
  }

  void start(int tail) {
    {
      std::lock_guard<std::mutex> lg(mutex);
      running = true;
      task_head = 0;
      task_tail = tail;
    }
    cv.notify_all();
  }

  ~ThreadPool() {
    {
      std::lock_guard<std::mutex> lg(mutex);
      finished = true;
    }
    cv.notify_all();
    for (int i = 0; i < threads.size(); i++) {
      threads[i].join();
    }
  }
};

bool test_threading() {
  auto tp = ThreadPool(10);
  tp.start(1000);
  return true;
}

TC_NAMESPACE_END
<|endoftext|>


<commit_before>#pragma once

#include <algorithm>
#include <utility>
#include <vector>

#include "Runtime/IOStreams.hpp"
#include "Runtime/Character/CPASAnimState.hpp"

namespace urde {

class CRandom16;
class CPASAnimParmData;
class CPASDatabase {
  std::vector<CPASAnimState> x0_states;
  s32 x10_defaultState;
  void AddAnimState(CPASAnimState&& state);
  void SetDefaultState(s32 state) { x10_defaultState = state; }

public:
  explicit CPASDatabase(CInputStream& in);

  std::pair<float, s32> FindBestAnimation(const CPASAnimParmData&, s32) const;
  std::pair<float, s32> FindBestAnimation(const CPASAnimParmData&, CRandom16&, s32) const;
  s32 GetDefaultState() const { return x10_defaultState; }
  s32 GetNumAnimStates() const { return x0_states.size(); }
  const CPASAnimState* GetAnimState(s32 id) const {
    for (const CPASAnimState& state : x0_states)
      if (id == state.GetStateId())
        return &state;

    return nullptr;
  }
  const CPASAnimState* GetAnimStateByIndex(s32 index) const {
    if (index < 0 || index >= x0_states.size())
      return nullptr;

    return &x0_states.at(index);
  }

  bool HasState(s32 id) const {
    const auto& st = std::find_if(x0_states.begin(), x0_states.end(),
                                  [&id](const CPASAnimState& other) -> bool { return other.GetStateId() == id; });
    return st != x0_states.end();
  }
};

} // namespace urde
<commit_msg>CPASDatabase: Add names to parameters in prototypes<commit_after>#pragma once

#include <algorithm>
#include <utility>
#include <vector>

#include "Runtime/IOStreams.hpp"
#include "Runtime/Character/CPASAnimState.hpp"

namespace urde {

class CRandom16;
class CPASAnimParmData;
class CPASDatabase {
  std::vector<CPASAnimState> x0_states;
  s32 x10_defaultState;
  void AddAnimState(CPASAnimState&& state);
  void SetDefaultState(s32 state) { x10_defaultState = state; }

public:
  explicit CPASDatabase(CInputStream& in);

  std::pair<float, s32> FindBestAnimation(const CPASAnimParmData& data, s32 ignoreAnim) const;
  std::pair<float, s32> FindBestAnimation(const CPASAnimParmData& data, CRandom16& rand, s32 ignoreAnim) const;
  s32 GetDefaultState() const { return x10_defaultState; }
  s32 GetNumAnimStates() const { return x0_states.size(); }
  const CPASAnimState* GetAnimState(s32 id) const {
    for (const CPASAnimState& state : x0_states)
      if (id == state.GetStateId())
        return &state;

    return nullptr;
  }
  const CPASAnimState* GetAnimStateByIndex(s32 index) const {
    if (index < 0 || index >= x0_states.size())
      return nullptr;

    return &x0_states.at(index);
  }

  bool HasState(s32 id) const {
    const auto& st = std::find_if(x0_states.begin(), x0_states.end(),
                                  [&id](const CPASAnimState& other) -> bool { return other.GetStateId() == id; });
    return st != x0_states.end();
  }
};

} // namespace urde
<|endoftext|>


<commit_before>#include <jaco_interaction/jaco_interactive_manipulation.h>

using namespace std;

JacoInteractiveManipulation::JacoInteractiveManipulation() :
    acGripper("jaco_arm/manipulation/gripper", true), acLift("jaco_arm/manipulation/lift", true), acHome(
        "jaco_arm/home_arm", true)
{
  joints.resize(6);

  //messages
  cartesianCmd = n.advertise<wpi_jaco_msgs::CartesianCommand>("jaco_arm/cartesian_cmd", 1);
  jointStateSubscriber = n.subscribe("jaco_arm/joint_states", 1, &JacoInteractiveManipulation::updateJoints, this);

  //services
  eraseTrajectoriesClient = n.serviceClient<std_srvs::Empty>("jaco_arm/erase_trajectories");
  jacoFkClient = n.serviceClient<wpi_jaco_msgs::JacoFK>("jaco_arm/kinematics/fk");
  qeClient = n.serviceClient<wpi_jaco_msgs::QuaternionToEuler>("jaco_conversions/quaternion_to_euler");

  //actionlib
  ROS_INFO("Waiting for grasp, pickup, and home arm action servers...");
  acGripper.waitForServer();
  acLift.waitForServer();
  acHome.waitForServer();
  ROS_INFO("Finished waiting for action servers");

  lockPose = false;

  imServer.reset(
      new interactive_markers::InteractiveMarkerServer("jaco_interactive_manipulation", "jaco_markers", false));

  ros::Duration(0.1).sleep();

  makeHandMarker();

  imServer->applyChanges();
}

void JacoInteractiveManipulation::updateJoints(const sensor_msgs::JointState::ConstPtr& msg)
{
  for (unsigned int i = 0; i < 6; i++)
  {
    joints.at(i) = msg->position.at(i);
  }
}

void JacoInteractiveManipulation::makeHandMarker()
{
  visualization_msgs::InteractiveMarker iMarker;
  iMarker.header.frame_id = "jaco_link_base";

  //initialize position to the jaco arm's current position
  wpi_jaco_msgs::JacoFK fkSrv;
  for (unsigned int i = 0; i < 6; i++)
  {
    fkSrv.request.joints.push_back(joints.at(i));
  }
  if (jacoFkClient.call(fkSrv))
  {
    iMarker.pose = fkSrv.response.handPose.pose;
  }
  else
  {
    iMarker.pose.position.x = 0.0;
    iMarker.pose.position.y = 0.0;
    iMarker.pose.position.z = 0.0;
    iMarker.pose.orientation.x = 0.0;
    iMarker.pose.orientation.y = 0.0;
    iMarker.pose.orientation.z = 0.0;
    iMarker.pose.orientation.w = 1.0;
  }
  iMarker.scale = .2;

  iMarker.name = "jaco_hand_marker";
  iMarker.description = "JACO Hand Control";

  //make a sphere control to represent the end effector position
  visualization_msgs::Marker sphereMarker;
  sphereMarker.type = visualization_msgs::Marker::SPHERE;
  sphereMarker.scale.x = iMarker.scale * 1;
  sphereMarker.scale.y = iMarker.scale * 1;
  sphereMarker.scale.z = iMarker.scale * 1;
  sphereMarker.color.r = .5;
  sphereMarker.color.g = .5;
  sphereMarker.color.b = .5;
  sphereMarker.color.a = 0.0;
  visualization_msgs::InteractiveMarkerControl sphereControl;
  sphereControl.markers.push_back(sphereMarker);
  sphereControl.interaction_mode = visualization_msgs::InteractiveMarkerControl::BUTTON;
  sphereControl.name = "jaco_hand_origin_marker";
  iMarker.controls.push_back(sphereControl);

  //add 6-DOF controls
  visualization_msgs::InteractiveMarkerControl control;

  control.orientation.w = 1;
  control.orientation.x = 1;
  control.orientation.y = 0;
  control.orientation.z = 0;
  control.name = "rotate_x";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::ROTATE_AXIS;
  iMarker.controls.push_back(control);
  control.name = "move_x";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
  iMarker.controls.push_back(control);

  control.orientation.w = 1;
  control.orientation.x = 0;
  control.orientation.y = 1;
  control.orientation.z = 0;
  control.name = "rotate_y";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::ROTATE_AXIS;
  iMarker.controls.push_back(control);
  control.name = "move_y";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
  iMarker.controls.push_back(control);

  control.orientation.w = 1;
  control.orientation.x = 0;
  control.orientation.y = 0;
  control.orientation.z = 1;
  control.name = "rotate_z";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::ROTATE_AXIS;
  iMarker.controls.push_back(control);
  control.name = "move_z";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
  iMarker.controls.push_back(control);

  //menu
  interactive_markers::MenuHandler::EntryHandle fingersSubMenuHandle = menuHandler.insert("Gripper");
  menuHandler.insert(fingersSubMenuHandle, "Close",
                     boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert(fingersSubMenuHandle, "Open",
                     boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert("Pickup", boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert("Home", boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert("Retract", boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));

  visualization_msgs::InteractiveMarkerControl menuControl;
  menuControl.interaction_mode = visualization_msgs::InteractiveMarkerControl::MENU;
  menuControl.name = "jaco_hand_menu";
  iMarker.controls.push_back(menuControl);

  imServer->insert(iMarker);
  imServer->setCallback(iMarker.name, boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));

  menuHandler.apply(*imServer, iMarker.name);
}

void JacoInteractiveManipulation::processHandMarkerFeedback(
    const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback)
{
  switch (feedback->event_type)
  {
    //Send a stop command so that when the marker is released the arm stops moving
    case visualization_msgs::InteractiveMarkerFeedback::BUTTON_CLICK:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0)
      {
        lockPose = true;
        sendStopCommand();
      }
      break;

      //Menu actions
    case visualization_msgs::InteractiveMarkerFeedback::MENU_SELECT:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0)
      {
        if (feedback->menu_entry_id == 2)	//grasp requested
        {
          rail_manipulation_msgs::GripperGoal gripperGoal;
          gripperGoal.close = true;
          acGripper.sendGoal(gripperGoal);
        }
        else if (feedback->menu_entry_id == 3)	//release requested
        {
          rail_manipulation_msgs::GripperGoal gripperGoal;
          gripperGoal.close = false;
          acGripper.sendGoal(gripperGoal);
        }
        else if (feedback->menu_entry_id == 4)	//pickup requested
        {
          rail_manipulation_msgs::LiftGoal liftGoal;
          acLift.sendGoal(liftGoal);
        }
        else if (feedback->menu_entry_id == 5)  //home requested
        {
          acGripper.cancelAllGoals();
          acLift.cancelAllGoals();
          wpi_jaco_msgs::HomeArmGoal homeGoal;
          homeGoal.retract = false;
          acHome.sendGoal(homeGoal);
          acHome.waitForResult(ros::Duration(10.0));
        }
        else if (feedback->menu_entry_id == 6)
        {
          acGripper.cancelAllGoals();
          acLift.cancelAllGoals();
          wpi_jaco_msgs::HomeArmGoal homeGoal;
          homeGoal.retract = true;
          homeGoal.retractPosition.position = true;
          homeGoal.retractPosition.armCommand = true;
          homeGoal.retractPosition.fingerCommand = false;
          homeGoal.retractPosition.repeat = false;
          homeGoal.retractPosition.joints.resize(6);
          homeGoal.retractPosition.joints[0] = -2.57;
          homeGoal.retractPosition.joints[1] = 1.39;
          homeGoal.retractPosition.joints[2] = .377;
          homeGoal.retractPosition.joints[3] = -.084;
          homeGoal.retractPosition.joints[4] = .515;
          homeGoal.retractPosition.joints[5] = -1.745;
          acHome.sendGoal(homeGoal);
          acHome.waitForResult(ros::Duration(15.0));
        }
      }
      break;

      //Send movement commands to the arm to follow the pose marker
    case visualization_msgs::InteractiveMarkerFeedback::POSE_UPDATE:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0
          && feedback->control_name.compare("jaco_hand_origin_marker") != 0)
      {
        if (!lockPose)
        {
          acGripper.cancelAllGoals();
          acLift.cancelAllGoals();

          //convert pose for compatibility with JACO API
          wpi_jaco_msgs::QuaternionToEuler qeSrv;
          qeSrv.request.orientation = feedback->pose.orientation;
          if (qeClient.call(qeSrv))
          {
            wpi_jaco_msgs::CartesianCommand cmd;
            cmd.position = true;
            cmd.armCommand = true;
            cmd.fingerCommand = false;
            cmd.repeat = false;
            cmd.arm.linear.x = feedback->pose.position.x;
            cmd.arm.linear.y = feedback->pose.position.y;
            cmd.arm.linear.z = feedback->pose.position.z;
            cmd.arm.angular.x = qeSrv.response.roll;
            cmd.arm.angular.y = qeSrv.response.pitch;
            cmd.arm.angular.z = qeSrv.response.yaw;

            cartesianCmd.publish(cmd);
          }
          else
            ROS_INFO("Quaternion to Euler conversion service failed, could not send pose update");
        }
      }
      break;

      //Mouse down events
    case visualization_msgs::InteractiveMarkerFeedback::MOUSE_DOWN:
      lockPose = false;
      break;

      //As with mouse clicked, send a stop command when the mouse is released on the marker
    case visualization_msgs::InteractiveMarkerFeedback::MOUSE_UP:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0)
      {
        lockPose = true;
        sendStopCommand();
      }
      break;
  }

  //Update interactive marker server
  imServer->applyChanges();
}

void JacoInteractiveManipulation::sendStopCommand()
{
  wpi_jaco_msgs::CartesianCommand cmd;
  cmd.position = false;
  cmd.armCommand = true;
  cmd.fingerCommand = false;
  cmd.repeat = true;
  cmd.arm.linear.x = 0.0;
  cmd.arm.linear.y = 0.0;
  cmd.arm.linear.z = 0.0;
  cmd.arm.angular.x = 0.0;
  cmd.arm.angular.y = 0.0;
  cmd.arm.angular.z = 0.0;
  cartesianCmd.publish(cmd);

  std_srvs::Empty srv;
  if (!eraseTrajectoriesClient.call(srv))
  {
    ROS_INFO("Could not call erase trajectories service...");
  }
}

void JacoInteractiveManipulation::updateMarkerPosition()
{
  wpi_jaco_msgs::JacoFK fkSrv;
  for (unsigned int i = 0; i < 6; i++)
  {
    fkSrv.request.joints.push_back(joints.at(i));
  }

  if (jacoFkClient.call(fkSrv))
  {
    imServer->setPose("jaco_hand_marker", fkSrv.response.handPose.pose);
    imServer->applyChanges();
  }
  else
  {
    ROS_INFO("Failed to call forward kinematics service");
  }
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "jaco_interactive_manipulation");

  JacoInteractiveManipulation jim;

  ros::Rate loop_rate(30);
  while (ros::ok())
  {
    jim.updateMarkerPosition();
    ros::spinOnce();
    loop_rate.sleep();
  }
}

<commit_msg>Fixed retract position from interactive marker call so that the arm doesn't bump itself<commit_after>#include <jaco_interaction/jaco_interactive_manipulation.h>

using namespace std;

JacoInteractiveManipulation::JacoInteractiveManipulation() :
    acGripper("jaco_arm/manipulation/gripper", true), acLift("jaco_arm/manipulation/lift", true), acHome(
        "jaco_arm/home_arm", true)
{
  joints.resize(6);

  //messages
  cartesianCmd = n.advertise<wpi_jaco_msgs::CartesianCommand>("jaco_arm/cartesian_cmd", 1);
  jointStateSubscriber = n.subscribe("jaco_arm/joint_states", 1, &JacoInteractiveManipulation::updateJoints, this);

  //services
  eraseTrajectoriesClient = n.serviceClient<std_srvs::Empty>("jaco_arm/erase_trajectories");
  jacoFkClient = n.serviceClient<wpi_jaco_msgs::JacoFK>("jaco_arm/kinematics/fk");
  qeClient = n.serviceClient<wpi_jaco_msgs::QuaternionToEuler>("jaco_conversions/quaternion_to_euler");

  //actionlib
  ROS_INFO("Waiting for grasp, pickup, and home arm action servers...");
  acGripper.waitForServer();
  acLift.waitForServer();
  acHome.waitForServer();
  ROS_INFO("Finished waiting for action servers");

  lockPose = false;

  imServer.reset(
      new interactive_markers::InteractiveMarkerServer("jaco_interactive_manipulation", "jaco_markers", false));

  ros::Duration(0.1).sleep();

  makeHandMarker();

  imServer->applyChanges();
}

void JacoInteractiveManipulation::updateJoints(const sensor_msgs::JointState::ConstPtr& msg)
{
  for (unsigned int i = 0; i < 6; i++)
  {
    joints.at(i) = msg->position.at(i);
  }
}

void JacoInteractiveManipulation::makeHandMarker()
{
  visualization_msgs::InteractiveMarker iMarker;
  iMarker.header.frame_id = "jaco_link_base";

  //initialize position to the jaco arm's current position
  wpi_jaco_msgs::JacoFK fkSrv;
  for (unsigned int i = 0; i < 6; i++)
  {
    fkSrv.request.joints.push_back(joints.at(i));
  }
  if (jacoFkClient.call(fkSrv))
  {
    iMarker.pose = fkSrv.response.handPose.pose;
  }
  else
  {
    iMarker.pose.position.x = 0.0;
    iMarker.pose.position.y = 0.0;
    iMarker.pose.position.z = 0.0;
    iMarker.pose.orientation.x = 0.0;
    iMarker.pose.orientation.y = 0.0;
    iMarker.pose.orientation.z = 0.0;
    iMarker.pose.orientation.w = 1.0;
  }
  iMarker.scale = .2;

  iMarker.name = "jaco_hand_marker";
  iMarker.description = "JACO Hand Control";

  //make a sphere control to represent the end effector position
  visualization_msgs::Marker sphereMarker;
  sphereMarker.type = visualization_msgs::Marker::SPHERE;
  sphereMarker.scale.x = iMarker.scale * 1;
  sphereMarker.scale.y = iMarker.scale * 1;
  sphereMarker.scale.z = iMarker.scale * 1;
  sphereMarker.color.r = .5;
  sphereMarker.color.g = .5;
  sphereMarker.color.b = .5;
  sphereMarker.color.a = 0.0;
  visualization_msgs::InteractiveMarkerControl sphereControl;
  sphereControl.markers.push_back(sphereMarker);
  sphereControl.interaction_mode = visualization_msgs::InteractiveMarkerControl::BUTTON;
  sphereControl.name = "jaco_hand_origin_marker";
  iMarker.controls.push_back(sphereControl);

  //add 6-DOF controls
  visualization_msgs::InteractiveMarkerControl control;

  control.orientation.w = 1;
  control.orientation.x = 1;
  control.orientation.y = 0;
  control.orientation.z = 0;
  control.name = "rotate_x";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::ROTATE_AXIS;
  iMarker.controls.push_back(control);
  control.name = "move_x";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
  iMarker.controls.push_back(control);

  control.orientation.w = 1;
  control.orientation.x = 0;
  control.orientation.y = 1;
  control.orientation.z = 0;
  control.name = "rotate_y";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::ROTATE_AXIS;
  iMarker.controls.push_back(control);
  control.name = "move_y";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
  iMarker.controls.push_back(control);

  control.orientation.w = 1;
  control.orientation.x = 0;
  control.orientation.y = 0;
  control.orientation.z = 1;
  control.name = "rotate_z";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::ROTATE_AXIS;
  iMarker.controls.push_back(control);
  control.name = "move_z";
  control.interaction_mode = visualization_msgs::InteractiveMarkerControl::MOVE_AXIS;
  iMarker.controls.push_back(control);

  //menu
  interactive_markers::MenuHandler::EntryHandle fingersSubMenuHandle = menuHandler.insert("Gripper");
  menuHandler.insert(fingersSubMenuHandle, "Close",
                     boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert(fingersSubMenuHandle, "Open",
                     boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert("Pickup", boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert("Home", boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));
  menuHandler.insert("Retract", boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));

  visualization_msgs::InteractiveMarkerControl menuControl;
  menuControl.interaction_mode = visualization_msgs::InteractiveMarkerControl::MENU;
  menuControl.name = "jaco_hand_menu";
  iMarker.controls.push_back(menuControl);

  imServer->insert(iMarker);
  imServer->setCallback(iMarker.name, boost::bind(&JacoInteractiveManipulation::processHandMarkerFeedback, this, _1));

  menuHandler.apply(*imServer, iMarker.name);
}

void JacoInteractiveManipulation::processHandMarkerFeedback(
    const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback)
{
  switch (feedback->event_type)
  {
    //Send a stop command so that when the marker is released the arm stops moving
    case visualization_msgs::InteractiveMarkerFeedback::BUTTON_CLICK:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0)
      {
        lockPose = true;
        sendStopCommand();
      }
      break;

      //Menu actions
    case visualization_msgs::InteractiveMarkerFeedback::MENU_SELECT:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0)
      {
        if (feedback->menu_entry_id == 2)	//grasp requested
        {
          rail_manipulation_msgs::GripperGoal gripperGoal;
          gripperGoal.close = true;
          acGripper.sendGoal(gripperGoal);
        }
        else if (feedback->menu_entry_id == 3)	//release requested
        {
          rail_manipulation_msgs::GripperGoal gripperGoal;
          gripperGoal.close = false;
          acGripper.sendGoal(gripperGoal);
        }
        else if (feedback->menu_entry_id == 4)	//pickup requested
        {
          rail_manipulation_msgs::LiftGoal liftGoal;
          acLift.sendGoal(liftGoal);
        }
        else if (feedback->menu_entry_id == 5)  //home requested
        {
          acGripper.cancelAllGoals();
          acLift.cancelAllGoals();
          wpi_jaco_msgs::HomeArmGoal homeGoal;
          homeGoal.retract = false;
          acHome.sendGoal(homeGoal);
          acHome.waitForResult(ros::Duration(10.0));
        }
        else if (feedback->menu_entry_id == 6)
        {
          acGripper.cancelAllGoals();
          acLift.cancelAllGoals();
          wpi_jaco_msgs::HomeArmGoal homeGoal;
          homeGoal.retract = true;
          homeGoal.retractPosition.position = true;
          homeGoal.retractPosition.armCommand = true;
          homeGoal.retractPosition.fingerCommand = false;
          homeGoal.retractPosition.repeat = false;
          homeGoal.retractPosition.joints.resize(6);
          homeGoal.retractPosition.joints[0] = -2.57;
          homeGoal.retractPosition.joints[1] = 1.39;
          homeGoal.retractPosition.joints[2] = .527;
          homeGoal.retractPosition.joints[3] = -.084;
          homeGoal.retractPosition.joints[4] = .515;
          homeGoal.retractPosition.joints[5] = -1.745;
          acHome.sendGoal(homeGoal);
          acHome.waitForResult(ros::Duration(15.0));
        }
      }
      break;

      //Send movement commands to the arm to follow the pose marker
    case visualization_msgs::InteractiveMarkerFeedback::POSE_UPDATE:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0
          && feedback->control_name.compare("jaco_hand_origin_marker") != 0)
      {
        if (!lockPose)
        {
          acGripper.cancelAllGoals();
          acLift.cancelAllGoals();

          //convert pose for compatibility with JACO API
          wpi_jaco_msgs::QuaternionToEuler qeSrv;
          qeSrv.request.orientation = feedback->pose.orientation;
          if (qeClient.call(qeSrv))
          {
            wpi_jaco_msgs::CartesianCommand cmd;
            cmd.position = true;
            cmd.armCommand = true;
            cmd.fingerCommand = false;
            cmd.repeat = false;
            cmd.arm.linear.x = feedback->pose.position.x;
            cmd.arm.linear.y = feedback->pose.position.y;
            cmd.arm.linear.z = feedback->pose.position.z;
            cmd.arm.angular.x = qeSrv.response.roll;
            cmd.arm.angular.y = qeSrv.response.pitch;
            cmd.arm.angular.z = qeSrv.response.yaw;

            cartesianCmd.publish(cmd);
          }
          else
            ROS_INFO("Quaternion to Euler conversion service failed, could not send pose update");
        }
      }
      break;

      //Mouse down events
    case visualization_msgs::InteractiveMarkerFeedback::MOUSE_DOWN:
      lockPose = false;
      break;

      //As with mouse clicked, send a stop command when the mouse is released on the marker
    case visualization_msgs::InteractiveMarkerFeedback::MOUSE_UP:
      if (feedback->marker_name.compare("jaco_hand_marker") == 0)
      {
        lockPose = true;
        sendStopCommand();
      }
      break;
  }

  //Update interactive marker server
  imServer->applyChanges();
}

void JacoInteractiveManipulation::sendStopCommand()
{
  wpi_jaco_msgs::CartesianCommand cmd;
  cmd.position = false;
  cmd.armCommand = true;
  cmd.fingerCommand = false;
  cmd.repeat = true;
  cmd.arm.linear.x = 0.0;
  cmd.arm.linear.y = 0.0;
  cmd.arm.linear.z = 0.0;
  cmd.arm.angular.x = 0.0;
  cmd.arm.angular.y = 0.0;
  cmd.arm.angular.z = 0.0;
  cartesianCmd.publish(cmd);

  std_srvs::Empty srv;
  if (!eraseTrajectoriesClient.call(srv))
  {
    ROS_INFO("Could not call erase trajectories service...");
  }
}

void JacoInteractiveManipulation::updateMarkerPosition()
{
  wpi_jaco_msgs::JacoFK fkSrv;
  for (unsigned int i = 0; i < 6; i++)
  {
    fkSrv.request.joints.push_back(joints.at(i));
  }

  if (jacoFkClient.call(fkSrv))
  {
    imServer->setPose("jaco_hand_marker", fkSrv.response.handPose.pose);
    imServer->applyChanges();
  }
  else
  {
    ROS_INFO("Failed to call forward kinematics service");
  }
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "jaco_interactive_manipulation");

  JacoInteractiveManipulation jim;

  ros::Rate loop_rate(30);
  while (ros::ok())
  {
    jim.updateMarkerPosition();
    ros::spinOnce();
    loop_rate.sleep();
  }
}

<|endoftext|>


<commit_before>#include "App.h"

#include "../examples/helpers/AsyncFileReader.h"
#include "../examples/helpers/AsyncFileStreamer.h"

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

    // do websockets here

    uWS::App().get("/hello", [](auto *res, auto *req) {
        res->end("Hello HTTP!");
    }).ws("/*", [](auto *ws, auto *req) {
        std::cout << "WebSocket conntected to URL: " << req->getUrl() << std::endl;
    }, [](auto *ws, std::string_view message) {
        ws->send(message);
    }).listen(3000, [](auto *token) {
        if (token) {
            std::cout << "Listening on port " << 3000 << std::endl;
        }
    }).run();

    return 0;

    AsyncFileStreamer *asyncFileStreamer = new AsyncFileStreamer("/home/alexhultman/v0.15/public");

    uWS::/*SSL*/App(/*{
        .key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem",
        .cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem",
        .dh_params_file_name = "/home/alexhultman/dhparams.pem",
        .passphrase = "1234"
    }*/)/*.get("/*", [](auto *res, auto *req) {

        res->end("GET /WILDCARD");

    })*/.get("/:param1/:param2", [](auto *res, auto *req) {

        res->write("GET /:param1/:param2 = ");
        res->write(req->getParameter(0));
        res->write(" and ");
        res->end(req->getParameter(1));

    }).post("/hello", [asyncFileStreamer](auto *res, auto *req) {

        // depending on the file type we want to also add mime!
        //asyncFileStreamer->streamFile(res, req->getUrl());

        res->end("POST /hello");

    }).get("/hello", [](auto *res, auto *req) {

        res->end("GET /hello");

    }).unhandled([](auto *res, auto *req) {

        res->writeStatus("404 Not Found");
        res->writeHeader("Content-Type", "text/html; charset=utf-8");
        res->end("
404 Not Found
µWebSockets v0.15");

    }).listen(3000, [](auto *token) {
        if (token) {
            std::cout << "Listening on port " << 3000 << std::endl;
        }
    }).run();

}
<commit_msg>Take opCode in main.cpp<commit_after>#include "App.h"

#include "../examples/helpers/AsyncFileReader.h"
#include "../examples/helpers/AsyncFileStreamer.h"

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

    // do websockets here

    /*
    uWS::App().ws("/*", {
        .open = []() {

        },
        .message = []() {

        },
        .drain = []() {

        },
        .close = []() {

        }
    }).listen().run();*/

    uWS::App().get("/hello", [](auto *res, auto *req) {
        res->end("Hello HTTP!");
    }).ws("/*", [](auto *ws, auto *req) {
        std::cout << "WebSocket conntected to URL: " << req->getUrl() << std::endl;
    }, [](auto *ws, std::string_view message, uWS::OpCode opCode) {
        ws->send(message, opCode);
    }).listen(3000, [](auto *token) {
        if (token) {
            std::cout << "Listening on port " << 3000 << std::endl;
        }
    }).run();

    return 0;

    AsyncFileStreamer *asyncFileStreamer = new AsyncFileStreamer("/home/alexhultman/v0.15/public");

    uWS::/*SSL*/App(/*{
        .key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem",
        .cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem",
        .dh_params_file_name = "/home/alexhultman/dhparams.pem",
        .passphrase = "1234"
    }*/)/*.get("/*", [](auto *res, auto *req) {

        res->end("GET /WILDCARD");

    })*/.get("/:param1/:param2", [](auto *res, auto *req) {

        res->write("GET /:param1/:param2 = ");
        res->write(req->getParameter(0));
        res->write(" and ");
        res->end(req->getParameter(1));

    }).post("/hello", [asyncFileStreamer](auto *res, auto *req) {

        // depending on the file type we want to also add mime!
        //asyncFileStreamer->streamFile(res, req->getUrl());

        res->end("POST /hello");

    }).get("/hello", [](auto *res, auto *req) {

        res->end("GET /hello");

    }).unhandled([](auto *res, auto *req) {

        res->writeStatus("404 Not Found");
        res->writeHeader("Content-Type", "text/html; charset=utf-8");
        res->end("
404 Not Found
µWebSockets v0.15");

    }).listen(3000, [](auto *token) {
        if (token) {
            std::cout << "Listening on port " << 3000 << std::endl;
        }
    }).run();

}
<|endoftext|>


<commit_before>
// hack, see http://stackoverflow.com/questions/12523122/what-is-glibcxx-use-nanosleep-all-about
#define _GLIBCXX_USE_NANOSLEEP 1
#include <chrono>
#include <thread>
#include <linux/i2c-dev.h>
//#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <tinyxml.h>
#include <iostream>
#include <sstream>
#include <map>

#include "Nodes/MotorHat.h"

namespace Arro {
class NodeStepperMotor: public INodeDefinition {
    public:

        //MICROSTEPS = 16
        // a sinusoidal curve NOT LINEAR!
        //MICROSTEP_CURVE = [0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255]

        /**
         * Constructor
         *
         * \param d The Process node instance.
         * \param name Name of this node.
         * \param params List of parameters passed to this node.
         */
        NodeStepperMotor(INodeContext* d, const std::string& name, Arro::StringMap& params, TiXmlElement*);
        virtual ~NodeStepperMotor() {};

        // Copy and assignment is not supported.
        NodeStepperMotor(const NodeStepperMotor&) = delete;
        NodeStepperMotor& operator=(const NodeStepperMotor& other) = delete;

        /**
         * Handle a message that is sent to this node.
         *
         * \param msg Message sent to this node.
         * \param padName name of pad that message was sent to.
         */
        void handleMessage(const MessageBuf& msg, const std::string& padName);

        /**
         * Make the node execute a processing cycle.
         */
        void runCycle();

        void run(MotorHAT::dir command);

        void setSpeed(int speed);

        void setPin(int pin, int value);

        int oneStep(int dir, int style);

        void step(int steps, int direction, int stepstyle);

        Trace m_trace;
        INodeContext* m_elemBlock;

        //
        int m_PWMA;
        int m_AIN2;
        int m_AIN1;
        int m_PWMB;
        int m_BIN2;
        int m_BIN1;

        int m_direction;
        int m_revsteps;
        float m_sec_per_step;
        int m_currentstep;

        int m_Ch;
        StringMap m_params;

        bool m_running;
    };
}

using namespace std;
using namespace Arro;
using namespace arro;
static RegisterMe<NodeStepperMotor> registerMe("StepperMotor");

static const int MICROSTEPS = 8;
static const int MICROSTEP_CURVE[] = {0, 50, 98, 142, 180, 212, 236, 250, 255};

NodeStepperMotor::NodeStepperMotor(INodeContext* d, const string& /*name*/, Arro::StringMap& /* params */, TiXmlElement*):
    m_trace("NodeStepperMotor", true),
    m_elemBlock(d),
    m_Ch(0),
    m_running(true) {

    m_Ch = stod(d->getParameter("Motor"));
    m_Ch--; // It's 0 based, but named M1, M2 on PCB.

    m_trace.println(string("Init motor ") + std::to_string(m_Ch));

    //m_MC = controller
    m_revsteps = 200;  // For now, maybe parameter?
    m_sec_per_step = 0.1;
    m_currentstep = 0;
    m_direction = MotorHAT::BRAKE;

    if (m_Ch == 0) {
        m_PWMA = 8;
        m_AIN2 = 9;
        m_AIN1 = 10;
        m_PWMB = 13;
        m_BIN2 = 12;
        m_BIN1 = 11;
    }
    else if(m_Ch == 1) {
        m_PWMA = 2;
        m_AIN2 = 3;
        m_AIN1 = 4;
        m_PWMB = 7;
        m_BIN2 = 6;
        m_BIN1 = 5;
    }
    else {
        throw std::runtime_error("MotorHAT Stepper Motor must be between 1 and 2 inclusive");
    }

}

int NodeStepperMotor::oneStep(int dir, int style) {
    int pwm_a = 255;
    int pwm_b = 255;

    // first determine what sort of stepping procedure we're up to
    if (style == MotorHAT::SINGLE) {
        if ((m_currentstep/(MICROSTEPS/2)) % 2) {
            // we're at an odd step, weird
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS/2;
            }
            else {
                m_currentstep -= MICROSTEPS/2;
            }
        }
        else {
            // go to next even step
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS;
            }
            else {
                m_currentstep -= MICROSTEPS;
            }
        }
    }

    if (style == MotorHAT::DOUBLE) {
        if (not (m_currentstep/(MICROSTEPS/2) % 2)) {
            // we're at an even step, weird
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS/2;
            }
            else {
                m_currentstep -= MICROSTEPS/2;
            }
        }
        else {
            // go to next odd step
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS;
            }
            else {
                m_currentstep -= MICROSTEPS;
            }
        }
    }
    if (style == MotorHAT::INTERLEAVE) {
        if (dir == MotorHAT::FORWARD) {
            m_currentstep += MICROSTEPS/2;
        }
        else {
            m_currentstep -= MICROSTEPS/2;
        }
    }

    if (style == MotorHAT::MICROSTEP) {
        if (dir == MotorHAT::FORWARD) {
            m_currentstep += 1;
        }
        else {
            m_currentstep -= 1;

            // go to next 'step' and wrap around
            m_currentstep += MICROSTEPS * 4;
            m_currentstep %= MICROSTEPS * 4;
        }

        pwm_a = pwm_b = 0;
        if ((m_currentstep >= 0) && (m_currentstep < MICROSTEPS)) {
            pwm_a = MICROSTEP_CURVE[MICROSTEPS - m_currentstep];
            pwm_b = MICROSTEP_CURVE[m_currentstep];
        }
        else if ((m_currentstep >= MICROSTEPS) && (m_currentstep < MICROSTEPS * 2)) {
            pwm_a = MICROSTEP_CURVE[m_currentstep - MICROSTEPS];
            pwm_b = MICROSTEP_CURVE[MICROSTEPS*2 - m_currentstep];
        }
        else if ((m_currentstep >= MICROSTEPS*2) && (m_currentstep < MICROSTEPS*3)) {
            pwm_a = MICROSTEP_CURVE[MICROSTEPS*3 - m_currentstep];
            pwm_b = MICROSTEP_CURVE[m_currentstep - MICROSTEPS*2];
        }
        else if ((m_currentstep >= MICROSTEPS*3) && (m_currentstep < MICROSTEPS*4)) {
            pwm_a = MICROSTEP_CURVE[m_currentstep - MICROSTEPS*3];
            pwm_b = MICROSTEP_CURVE[MICROSTEPS*4 - m_currentstep];
        }
    }

    // go to next 'step' and wrap around
    m_currentstep += MICROSTEPS * 4;
    m_currentstep %= MICROSTEPS * 4;

    // only really used for microstepping, otherwise always on!
    MotorHAT::getInstance()->setPWM(m_PWMA, 0, pwm_a*16);
    MotorHAT::getInstance()->setPWM(m_PWMB, 0, pwm_b*16);

    // set up coil energizing!
    vector<int> coils{0, 0, 0, 0};

    if (style == MotorHAT::MICROSTEP) {
        if ((m_currentstep >= 0) && (m_currentstep < MICROSTEPS)) {
            coils = {1, 1, 0, 0};
        }
        else if ((m_currentstep >= MICROSTEPS) && (m_currentstep < MICROSTEPS*2)) {
            coils = {0, 1, 1, 0};
        }
        else if ((m_currentstep >= MICROSTEPS*2) && (m_currentstep < MICROSTEPS*3)) {
            coils = {0, 0, 1, 1};
        }
        else if ((m_currentstep >= MICROSTEPS*3) && (m_currentstep < MICROSTEPS*4)) {
            coils = {1, 0, 0, 1};
        }
    }
    else {
        int step2coils[][4] = {
            {1, 0, 0, 0},
            {1, 1, 0, 0},
            {0, 1, 0, 0},
            {0, 1, 1, 0},
            {0, 0, 1, 0},
            {0, 0, 1, 1},
            {0, 0, 0, 1},
            {1, 0, 0, 1} };
        for(int i = 0; i < 4; i++) {
            coils[i] = step2coils[m_currentstep/(MICROSTEPS/2)][i];
        }
    }

    //print "coils state = " + str(coils)
    MotorHAT::getInstance()->setPin(m_AIN2, coils[0]);
    MotorHAT::getInstance()->setPin(m_BIN1, coils[1]);
    MotorHAT::getInstance()->setPin(m_AIN1, coils[2]);
    MotorHAT::getInstance()->setPin(m_BIN2, coils[3]);

    return m_currentstep;
}

void NodeStepperMotor::step(int steps, int direction, int stepstyle) {
    int s_per_s = m_sec_per_step;
    int lateststep = 0;

    if (stepstyle == MotorHAT::INTERLEAVE) {
        s_per_s = s_per_s / 2.0;
    }
    if (stepstyle == MotorHAT::MICROSTEP) {
        s_per_s /= MICROSTEPS;
        steps *= MICROSTEPS;
    }

    //m_trace("{} sec per step".format(s_per_s));

    for (int s = 0; s < steps; s++) {
        lateststep = oneStep(direction, stepstyle);
        //time.sleep(s_per_s);
        std::chrono::milliseconds timespan(s_per_s);
        std::this_thread::sleep_for(timespan);
    }

    if (stepstyle == MotorHAT::MICROSTEP) {
        // this is an edge case, if we are in between full steps, lets just keep going
        // so we end on a full step
        while ((lateststep != 0) and (lateststep != MICROSTEPS)) {
            lateststep = oneStep(direction, stepstyle);
            //time.sleep(s_per_s);
            std::chrono::milliseconds timespan(s_per_s);
            std::this_thread::sleep_for(timespan);
        }
    }
}

void
NodeStepperMotor::setSpeed(int rpm) {
    m_trace.println(std::string("NodeStepperMotor::setSpeed ") + to_string(rpm));
    m_sec_per_step = 60.0 / (m_revsteps * rpm);
}

void
NodeStepperMotor::handleMessage(const MessageBuf& m, const std::string& padName) {
    m_trace.println("NodeStepperMotor::handleMessage");
    if(m_running && padName == "speed") {
        auto msg = new Value();
        msg->ParseFromString(m->c_str());

        m_trace.println("Speed " + padName + " value " + std::to_string(((Value*)msg)->value()));

        assert(msg->GetTypeName() == "arro.Value");

        setSpeed(((Value*)msg)->value());

    } else if(m_running && padName == "direction") {
        auto msg = new Value();
        msg->ParseFromString(m->c_str());

        m_trace.println(std::string("Dir (1, 2, 4) ") + padName + " value " + std::to_string(((Value*)msg)->value()));

        int dir = ((Value*)msg)->value();
        if(dir >= 0 && dir <= 4) {
            m_direction = dir;
        }

        assert(msg->GetTypeName() == "arro.Value");

    } else if(m_running && padName == "steps") {
        auto msg = new Value();
        msg->ParseFromString(m->c_str());

        m_trace.println(std::string("Steps ") + padName + " value " + std::to_string(((Value*)msg)->value()));

        int steps = ((Value*)msg)->value();
        if(steps >= 0) {
            step(steps, m_direction, MotorHAT::SINGLE);
        }

        assert(msg->GetTypeName() == "arro.Value");

    } else if(padName == "_action") {
        auto msg = new Action();
        msg->ParseFromString(m->c_str());

        string a = msg->action();

        if(a == "_terminated") {
            m_trace.println("Received _terminated");
            m_running = false;
            // Switch off the motor
            // FIXME: reset?
        }

        assert(msg->GetTypeName() == "arro.Action");

    } else {
        m_trace.println(string("Message received from ") + padName);
    }
}

void
NodeStepperMotor::runCycle() {
    m_trace.println("NodeStepperMotor::runCycle");

}
<commit_msg>Added traces<commit_after>
// hack, see http://stackoverflow.com/questions/12523122/what-is-glibcxx-use-nanosleep-all-about
#define _GLIBCXX_USE_NANOSLEEP 1
#include <chrono>
#include <thread>
#include <linux/i2c-dev.h>
//#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <tinyxml.h>
#include <iostream>
#include <sstream>
#include <map>

#include "Nodes/MotorHat.h"

namespace Arro {
class NodeStepperMotor: public INodeDefinition {
    public:

        //MICROSTEPS = 16
        // a sinusoidal curve NOT LINEAR!
        //MICROSTEP_CURVE = [0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255]

        /**
         * Constructor
         *
         * \param d The Process node instance.
         * \param name Name of this node.
         * \param params List of parameters passed to this node.
         */
        NodeStepperMotor(INodeContext* d, const std::string& name, Arro::StringMap& params, TiXmlElement*);
        virtual ~NodeStepperMotor() {};

        // Copy and assignment is not supported.
        NodeStepperMotor(const NodeStepperMotor&) = delete;
        NodeStepperMotor& operator=(const NodeStepperMotor& other) = delete;

        /**
         * Handle a message that is sent to this node.
         *
         * \param msg Message sent to this node.
         * \param padName name of pad that message was sent to.
         */
        void handleMessage(const MessageBuf& msg, const std::string& padName);

        /**
         * Make the node execute a processing cycle.
         */
        void runCycle();

        void run(MotorHAT::dir command);

        void setSpeed(int speed);

        void setPin(int pin, int value);

        int oneStep(int dir, int style);

        void step(int steps, int direction, int stepstyle);

        Trace m_trace;
        INodeContext* m_elemBlock;

        //
        int m_PWMA;
        int m_AIN2;
        int m_AIN1;
        int m_PWMB;
        int m_BIN2;
        int m_BIN1;

        int m_direction;
        int m_revsteps;
        float m_sec_per_step;
        int m_currentstep;

        int m_Ch;
        StringMap m_params;

        bool m_running;
    };
}

using namespace std;
using namespace Arro;
using namespace arro;
static RegisterMe<NodeStepperMotor> registerMe("StepperMotor");

static const int MICROSTEPS = 8;
static const int MICROSTEP_CURVE[] = {0, 50, 98, 142, 180, 212, 236, 250, 255};

NodeStepperMotor::NodeStepperMotor(INodeContext* d, const string& /*name*/, Arro::StringMap& /* params */, TiXmlElement*):
    m_trace("NodeStepperMotor", true),
    m_elemBlock(d),
    m_Ch(0),
    m_running(true) {

    m_Ch = stod(d->getParameter("Motor"));
    m_Ch--; // It's 0 based, but named M1, M2 on PCB.

    m_trace.println(string("Init motor ") + std::to_string(m_Ch));

    //m_MC = controller
    m_revsteps = 200;  // For now, maybe parameter?
    m_sec_per_step = 0.1;
    m_currentstep = 0;
    m_direction = MotorHAT::BRAKE;

    if (m_Ch == 0) {
        m_PWMA = 8;
        m_AIN2 = 9;
        m_AIN1 = 10;
        m_PWMB = 13;
        m_BIN2 = 12;
        m_BIN1 = 11;
    }
    else if(m_Ch == 1) {
        m_PWMA = 2;
        m_AIN2 = 3;
        m_AIN1 = 4;
        m_PWMB = 7;
        m_BIN2 = 6;
        m_BIN1 = 5;
    }
    else {
        throw std::runtime_error("MotorHAT Stepper Motor must be between 1 and 2 inclusive");
    }

}

int NodeStepperMotor::oneStep(int dir, int style) {
    int pwm_a = 255;
    int pwm_b = 255;

    // first determine what sort of stepping procedure we're up to
    if (style == MotorHAT::SINGLE) {
        if ((m_currentstep/(MICROSTEPS/2)) % 2) {
            // we're at an odd step, weird
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS/2;
            }
            else {
                m_currentstep -= MICROSTEPS/2;
            }
        }
        else {
            // go to next even step
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS;
            }
            else {
                m_currentstep -= MICROSTEPS;
            }
        }
    }

    if (style == MotorHAT::DOUBLE) {
        if (not (m_currentstep/(MICROSTEPS/2) % 2)) {
            // we're at an even step, weird
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS/2;
            }
            else {
                m_currentstep -= MICROSTEPS/2;
            }
        }
        else {
            // go to next odd step
            if (dir == MotorHAT::FORWARD) {
                m_currentstep += MICROSTEPS;
            }
            else {
                m_currentstep -= MICROSTEPS;
            }
        }
    }
    if (style == MotorHAT::INTERLEAVE) {
        if (dir == MotorHAT::FORWARD) {
            m_currentstep += MICROSTEPS/2;
        }
        else {
            m_currentstep -= MICROSTEPS/2;
        }
    }

    if (style == MotorHAT::MICROSTEP) {
        if (dir == MotorHAT::FORWARD) {
            m_currentstep += 1;
        }
        else {
            m_currentstep -= 1;

            // go to next 'step' and wrap around
            m_currentstep += MICROSTEPS * 4;
            m_currentstep %= MICROSTEPS * 4;
        }

        pwm_a = pwm_b = 0;
        if ((m_currentstep >= 0) && (m_currentstep < MICROSTEPS)) {
            pwm_a = MICROSTEP_CURVE[MICROSTEPS - m_currentstep];
            pwm_b = MICROSTEP_CURVE[m_currentstep];
        }
        else if ((m_currentstep >= MICROSTEPS) && (m_currentstep < MICROSTEPS * 2)) {
            pwm_a = MICROSTEP_CURVE[m_currentstep - MICROSTEPS];
            pwm_b = MICROSTEP_CURVE[MICROSTEPS*2 - m_currentstep];
        }
        else if ((m_currentstep >= MICROSTEPS*2) && (m_currentstep < MICROSTEPS*3)) {
            pwm_a = MICROSTEP_CURVE[MICROSTEPS*3 - m_currentstep];
            pwm_b = MICROSTEP_CURVE[m_currentstep - MICROSTEPS*2];
        }
        else if ((m_currentstep >= MICROSTEPS*3) && (m_currentstep < MICROSTEPS*4)) {
            pwm_a = MICROSTEP_CURVE[m_currentstep - MICROSTEPS*3];
            pwm_b = MICROSTEP_CURVE[MICROSTEPS*4 - m_currentstep];
        }
    }

    // go to next 'step' and wrap around
    m_currentstep += MICROSTEPS * 4;
    m_currentstep %= MICROSTEPS * 4;

    // only really used for microstepping, otherwise always on!
    MotorHAT::getInstance()->setPWM(m_PWMA, 0, pwm_a*16);
    MotorHAT::getInstance()->setPWM(m_PWMB, 0, pwm_b*16);

    // set up coil energizing!
    vector<int> coils{0, 0, 0, 0};

    if (style == MotorHAT::MICROSTEP) {
        if ((m_currentstep >= 0) && (m_currentstep < MICROSTEPS)) {
            coils = {1, 1, 0, 0};
        }
        else if ((m_currentstep >= MICROSTEPS) && (m_currentstep < MICROSTEPS*2)) {
            coils = {0, 1, 1, 0};
        }
        else if ((m_currentstep >= MICROSTEPS*2) && (m_currentstep < MICROSTEPS*3)) {
            coils = {0, 0, 1, 1};
        }
        else if ((m_currentstep >= MICROSTEPS*3) && (m_currentstep < MICROSTEPS*4)) {
            coils = {1, 0, 0, 1};
        }
    }
    else {
        int step2coils[][4] = {
            {1, 0, 0, 0},
            {1, 1, 0, 0},
            {0, 1, 0, 0},
            {0, 1, 1, 0},
            {0, 0, 1, 0},
            {0, 0, 1, 1},
            {0, 0, 0, 1},
            {1, 0, 0, 1} };
        for(int i = 0; i < 4; i++) {
            coils[i] = step2coils[m_currentstep/(MICROSTEPS/2)][i];
        }
    }

    //print "coils state = " + str(coils)
    MotorHAT::getInstance()->setPin(m_AIN2, coils[0]);
    MotorHAT::getInstance()->setPin(m_BIN1, coils[1]);
    MotorHAT::getInstance()->setPin(m_AIN1, coils[2]);
    MotorHAT::getInstance()->setPin(m_BIN2, coils[3]);

    return m_currentstep;
}

void NodeStepperMotor::step(int steps, int direction, int stepstyle) {
    int s_per_s = m_sec_per_step;
    int lateststep = 0;

    if (stepstyle == MotorHAT::INTERLEAVE) {
        s_per_s = s_per_s / 2.0;
    }
    if (stepstyle == MotorHAT::MICROSTEP) {
        s_per_s /= MICROSTEPS;
        steps *= MICROSTEPS;
    }

    m_trace.println("seconds per step " + std::to_string(s_per_s) + " steps " + std::to_string(steps) + " direction " + std::to_string(direction) + " stepstyle " + std::to_string(stepstyle));

    for (int s = 0; s < steps; s++) {
        lateststep = oneStep(direction, stepstyle);
        //time.sleep(s_per_s);
        std::chrono::milliseconds timespan(s_per_s);
        std::this_thread::sleep_for(timespan);
    }

    if (stepstyle == MotorHAT::MICROSTEP) {
        // this is an edge case, if we are in between full steps, lets just keep going
        // so we end on a full step
        while ((lateststep != 0) and (lateststep != MICROSTEPS)) {
            lateststep = oneStep(direction, stepstyle);
            //time.sleep(s_per_s);
            std::chrono::milliseconds timespan(s_per_s);
            std::this_thread::sleep_for(timespan);
        }
    }
}

void
NodeStepperMotor::setSpeed(int rpm) {
    m_trace.println(std::string("NodeStepperMotor::setSpeed ") + to_string(rpm));
    m_sec_per_step = 60.0 / (m_revsteps * rpm);
    m_trace.println("m_sec_per_step " + std::to_string(m_sec_per_step));
}

void
NodeStepperMotor::handleMessage(const MessageBuf& m, const std::string& padName) {
    m_trace.println("NodeStepperMotor::handleMessage");
    if(m_running && padName == "speed") {
        auto msg = new Value();
        msg->ParseFromString(m->c_str());

        m_trace.println("Speed " + padName + " value " + std::to_string(((Value*)msg)->value()));

        assert(msg->GetTypeName() == "arro.Value");

        setSpeed(((Value*)msg)->value());

    } else if(m_running && padName == "direction") {
        auto msg = new Value();
        msg->ParseFromString(m->c_str());

        m_trace.println(std::string("Dir (1, 2, 4) ") + padName + " value " + std::to_string(((Value*)msg)->value()));

        int dir = ((Value*)msg)->value();
        if(dir >= 0 && dir <= 4) {
            m_direction = dir;
        }

        assert(msg->GetTypeName() == "arro.Value");

    } else if(m_running && padName == "steps") {
        auto msg = new Value();
        msg->ParseFromString(m->c_str());

        m_trace.println(std::string("Steps ") + padName + " value " + std::to_string(((Value*)msg)->value()));

        int steps = ((Value*)msg)->value();
        if(steps >= 0) {
            step(steps, m_direction, MotorHAT::SINGLE);
        }

        assert(msg->GetTypeName() == "arro.Value");

    } else if(padName == "_action") {
        auto msg = new Action();
        msg->ParseFromString(m->c_str());

        string a = msg->action();

        if(a == "_terminated") {
            m_trace.println("Received _terminated");
            m_running = false;
            // Switch off the motor
            // FIXME: reset?
        }

        assert(msg->GetTypeName() == "arro.Action");

    } else {
        m_trace.println(string("Message received from ") + padName);
    }
}

void
NodeStepperMotor::runCycle() {
    m_trace.println("NodeStepperMotor::runCycle");

}
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkDataPixelRef.h"
#include "SkData.h"

SkDataPixelRef::SkDataPixelRef(SkData* data) : fData(data) {
    fData->ref();
    this->setPreLocked(const_cast<void*>(fData->data()), NULL);
}

SkDataPixelRef::~SkDataPixelRef() {
    fData->unref();
}

void* SkDataPixelRef::onLockPixels(SkColorTable** ct) {
    *ct = NULL;
    return const_cast<void*>(fData->data());
}

void SkDataPixelRef::onUnlockPixels() {
    // nothing to do
}

void SkDataPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

//    fData->flatten(buffer);
}

SkDataPixelRef::SkDataPixelRef(SkFlattenableReadBuffer& buffer)
        : INHERITED(buffer, NULL) {

//    fData = buffer.readData();
            this->setPreLocked(const_cast<void*>(fData->data()), NULL);
}

SK_DEFINE_FLATTENABLE_REGISTRAR(SkDataPixelRef)
<commit_msg>add flattening<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkDataPixelRef.h"
#include "SkData.h"

SkDataPixelRef::SkDataPixelRef(SkData* data) : fData(data) {
    fData->ref();
    this->setPreLocked(const_cast<void*>(fData->data()), NULL);
}

SkDataPixelRef::~SkDataPixelRef() {
    fData->unref();
}

void* SkDataPixelRef::onLockPixels(SkColorTable** ct) {
    *ct = NULL;
    return const_cast<void*>(fData->data());
}

void SkDataPixelRef::onUnlockPixels() {
    // nothing to do
}

void SkDataPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);
    buffer.writeFlattenable(fData);
}

SkDataPixelRef::SkDataPixelRef(SkFlattenableReadBuffer& buffer)
        : INHERITED(buffer, NULL) {
    fData = (SkData*)buffer.readFlattenable();
    this->setPreLocked(const_cast<void*>(fData->data()), NULL);
}

SK_DEFINE_FLATTENABLE_REGISTRAR(SkDataPixelRef)
<|endoftext|>


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

#include "HTTPRequest.h"

#include <iostream>
#include <string>

#include "utf.h"

#include "url/URI.h"
#include "http/HTTPCache.h"
#include "http/HTTPConnection.h"

#include "css/Box.h"

namespace org { namespace w3c { namespace dom { namespace bootstrap {

const unsigned short HttpRequest::UNSENT;
const unsigned short HttpRequest::OPENED;
const unsigned short HttpRequest::HEADERS_RECEIVED;
const unsigned short HttpRequest::LOADING;
const unsigned short HttpRequest::COMPLETE;
const unsigned short HttpRequest::DONE;

std::string HttpRequest::aboutPath;
std::string HttpRequest::cachePath("/tmp");

int HttpRequest::getContentDescriptor()
{
    return fdContent;  // TODO: call dup internally?
}

std::FILE* HttpRequest::openFile()
{
    if (fdContent == -1)
        return 0;
    std::FILE* file = fdopen(dup(fdContent), "rb");
    if (!file)
        return 0;
    rewind(file);
    return file;
}

std::fstream& HttpRequest::getContent()
{
    if (content.is_open())
        return content;

    char filename[PATH_MAX];
    if (PATH_MAX <= cachePath.length() + 9)
        return content;
    strcpy(filename, cachePath.c_str());
    strcat(filename, "/esXXXXXX");
    fdContent = mkstemp(filename);
    if (fdContent == -1)
        return content;
    content.open(filename, std::ios_base::trunc | std::ios_base::in | std::ios_base::out | std::ios::binary);
    remove(filename);

    return content;
}

void HttpRequest::setHandler(boost::function<void (void)> f)
{
    handler = f;
}

void HttpRequest::clearHandler()
{
    handler.clear();
    for (auto i = callbackList.begin(); i != callbackList.end(); ++i) {
        if (*i) {
            (*i)();
            *i = 0;
        }
    }
}

unsigned HttpRequest::addCallback(boost::function<void (void)> f, unsigned id)
{
    if (f) {
        if (readyState == DONE) {
            f();
            return static_cast<unsigned>(-1);
        }
        if (id < callbackList.size()) {
            callbackList[id] = f;
            return id;
        }
        callbackList.push_back(f);
        return callbackList.size() - 1;
    }
    return static_cast<unsigned>(-1);
}

void HttpRequest::clearCallback(unsigned id)
{
    if (id < callbackList.size())
        callbackList[id] = 0;
}

bool HttpRequest::redirect(const HttpResponseMessage& res)
{
    int method = request.getMethodCode();
    if (method != HttpRequestMessage::GET && method != HttpRequestMessage::HEAD)
        return false;
    if (!res.shouldRedirect())
        return false;
    std::string location = res.getResponseHeader("Location");
    if (!request.redirect(utfconv(location)))
        return false;

    // Redirect to location
    if (content.is_open())
        content.close();
    if (0 <= fdContent) {
        close(fdContent);
        fdContent = -1;
    }
    cache = 0;
    readyState = OPENED;
    return true;
}

// Return true to put this request in the completed list.
bool HttpRequest::complete(bool error)
{
    errorFlag = error;
    if (cache)
        cache->notify(this, error);
    if (!error) {
        if (redirect(response)) {
            response.clear();
            send();
            return false;
        }
    } else
        response.setStatus(404);
    readyState = (handler || !callbackList.empty()) ? COMPLETE : DONE;
    return readyState == COMPLETE;
}

void HttpRequest::notify()
{
    readyState = DONE;
    if (handler) {
        handler();
        handler.clear();
    }
    for (auto i = callbackList.begin(); i != callbackList.end(); ++i) {
        if (*i) {
            (*i)();
            *i = 0;
        }
    }
}

bool HttpRequest::notify(bool error)
{
    if (complete(error))
        notify();
    return errorFlag;
}

void HttpRequest::open(const std::u16string& method, const std::u16string& urlString)
{
    URL url(base, urlString);
    request.open(utfconv(method), url);
    readyState = OPENED;
}

void HttpRequest::setRequestHeader(const std::u16string& header, const std::u16string& value)
{
    request.setHeader(utfconv(header), utfconv(value));
}

bool HttpRequest::constructResponseFromCache(bool sync)
{
    assert(cache);
    readyState = DONE;
    errorFlag = false;

    response.update(cache->getResponseMessage());
    response.updateStatus(cache->getResponseMessage());

    // TODO: deal with partial...
    int fd = cache->getContentDescriptor();
    if (0 <= fd) {
        fdContent = dup(fd);
        lseek(fdContent, 0, SEEK_SET);
    }

    cache = 0;
    if (sync)
        notify();
    else
        HttpConnectionManager::getInstance().complete(this, errorFlag);
    return errorFlag;
}

namespace {

bool decodeBase64(std::fstream& content, const std::string& data)
{
    static const char* const table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char buf[4];
    char out[3];
    const char* p = data.c_str();
    int i = 0;
    int count = 3;
    while (char c = *p++) {
        if (c == '=') {
            buf[i++] = 0;
            if (--count <= 0)
                return false;
        } else if (const char* found = strchr(table, c))
            buf[i++] = found - table;
        else if (isspace(c))
            continue;
        else
            return false;
        if (i == 4) {
            out[0] = ((buf[0] << 2) & 0xfc) | ((buf[1] >> 4) & 0x03);
            out[1] = ((buf[1] << 4) & 0xf0) | ((buf[2] >> 2) & 0x0f);
            out[2] = ((buf[2] << 6) & 0xc0) | (buf[3] & 0x3f);
            content.write(out, count);
            i = 0;
            count = 3;
        }
    }
    return i == 0;
}

}  // namespace

bool HttpRequest::constructResponseFromData()
{
    URI dataURI(request.getURL());
    const std::string& data(dataURI);
    size_t end = data.find(',');
    if (end == std::string::npos) {
        notify(true);
        return errorFlag;
    }
    bool base64(false);
    if (7 <= end && data.compare(end - 7, 7, ";base64") == 0) {
        end -= 7;
        base64 = true;
    }
    response.parseMediaType(data.c_str() + 5, data.c_str() + end);
    std::fstream& content = getContent();
    if (!content.is_open()) {
        notify(true);
        return errorFlag;
    }
    if (!base64) {
        end += 1;
        std::string decoded(URI::percentDecode(URI::percentDecode(data, end, data.length() - end)));
        content << decoded;
    } else {
        end += 8;
        std::string decoded(URI::percentDecode(URI::percentDecode(data, end, data.length() - end)));
        errorFlag = !decodeBase64(content, decoded);
    }
    content.flush();
    notify(errorFlag);
    return errorFlag;
}

bool HttpRequest::send()
{
    if (3 <= getLogLevel())
        std::cerr << "HttpRequest::send(): " << request.getURL() << '\n';

    if (request.getURL().isEmpty())
        return notify(false);

    if (request.getURL().testProtocol(u"file")) {
        if (request.getMethodCode() != HttpRequestMessage::GET)
            return notify(true);
        std::u16string host = request.getURL().getHostname();
        if (!host.empty() && host != u"localhost")  // TODO: maybe allow local host IP addresses?
            return notify(true);
        std::string path = utfconv(request.getURL().getPathname());
        fdContent = ::open(path.c_str(), O_RDONLY);
        return notify(fdContent == -1);
    }

    if (request.getURL().testProtocol(u"about")) {
        if (aboutPath.empty() || request.getMethodCode() != HttpRequestMessage::GET)
            return notify(true);
        std::string path = utfconv(request.getURL().getPathname());
        if (path.empty())
            path = aboutPath + "/about/index.html";
        else
            path = aboutPath + "/about/" + path;
        fdContent = ::open(path.c_str(), O_RDONLY);
        return notify(fdContent == -1);
    }

    if (request.getURL().testProtocol(u"data"))
        return constructResponseFromData();

    cache = HttpCacheManager::getInstance().send(this);
    if (!cache || cache->isBusy())
        return false;
    return constructResponseFromCache(true);
}

void HttpRequest::abort()
{
    if (readyState == UNSENT)
        return;

    // TODO: implement more details.
    clearHandler();
    HttpConnectionManager& manager = HttpConnectionManager::getInstance();
    manager.abort(this);
    readyState = UNSENT;
    errorFlag = false;
    request.clear();
    response.clear();
    if (content.is_open())
        content.close();
    if (0 <= fdContent) {
        close(fdContent);
        fdContent = -1;
    }
    cache = 0;
}

unsigned short HttpRequest::getStatus() const
{
    return response.getStatus();
}

const std::string& HttpRequest::getStatusText() const
{
    return response.getStatusText();
}

const std::string HttpRequest::getResponseHeader(std::u16string header) const
{
    return response.getResponseHeader(utfconv(header));
}

const std::string& HttpRequest::getAllResponseHeaders() const
{
    return response.getAllResponseHeaders();
}

HttpRequest::HttpRequest(const std::u16string& base) :
    base(base),
    readyState(UNSENT),
    errorFlag(false),
    fdContent(-1),
    cache(0),
    handler(0),
    boxImage(0)
{
}

HttpRequest::~HttpRequest()
{
    abort();
    delete boxImage;
}

}}}}  // org::w3c::dom::bootstrap
<commit_msg>(HttpRequest::complete, HttpRequest::notify) : Fix to call HttpCache::notify() from the main thread<commit_after>/*
 * Copyright 2011, 2012 Esrille Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "HTTPRequest.h"

#include <iostream>
#include <string>

#include "utf.h"

#include "url/URI.h"
#include "http/HTTPCache.h"
#include "http/HTTPConnection.h"

#include "css/Box.h"

namespace org { namespace w3c { namespace dom { namespace bootstrap {

const unsigned short HttpRequest::UNSENT;
const unsigned short HttpRequest::OPENED;
const unsigned short HttpRequest::HEADERS_RECEIVED;
const unsigned short HttpRequest::LOADING;
const unsigned short HttpRequest::COMPLETE;
const unsigned short HttpRequest::DONE;

std::string HttpRequest::aboutPath;
std::string HttpRequest::cachePath("/tmp");

int HttpRequest::getContentDescriptor()
{
    return fdContent;  // TODO: call dup internally?
}

std::FILE* HttpRequest::openFile()
{
    if (fdContent == -1)
        return 0;
    std::FILE* file = fdopen(dup(fdContent), "rb");
    if (!file)
        return 0;
    rewind(file);
    return file;
}

std::fstream& HttpRequest::getContent()
{
    if (content.is_open())
        return content;

    char filename[PATH_MAX];
    if (PATH_MAX <= cachePath.length() + 9)
        return content;
    strcpy(filename, cachePath.c_str());
    strcat(filename, "/esXXXXXX");
    fdContent = mkstemp(filename);
    if (fdContent == -1)
        return content;
    content.open(filename, std::ios_base::trunc | std::ios_base::in | std::ios_base::out | std::ios::binary);
    remove(filename);

    return content;
}

void HttpRequest::setHandler(boost::function<void (void)> f)
{
    handler = f;
}

void HttpRequest::clearHandler()
{
    handler.clear();
    for (auto i = callbackList.begin(); i != callbackList.end(); ++i) {
        if (*i) {
            (*i)();
            *i = 0;
        }
    }
}

unsigned HttpRequest::addCallback(boost::function<void (void)> f, unsigned id)
{
    if (f) {
        if (readyState == DONE) {
            f();
            return static_cast<unsigned>(-1);
        }
        if (id < callbackList.size()) {
            callbackList[id] = f;
            return id;
        }
        callbackList.push_back(f);
        return callbackList.size() - 1;
    }
    return static_cast<unsigned>(-1);
}

void HttpRequest::clearCallback(unsigned id)
{
    if (id < callbackList.size())
        callbackList[id] = 0;
}

bool HttpRequest::redirect(const HttpResponseMessage& res)
{
    int method = request.getMethodCode();
    if (method != HttpRequestMessage::GET && method != HttpRequestMessage::HEAD)
        return false;
    if (!res.shouldRedirect())
        return false;
    std::string location = res.getResponseHeader("Location");
    if (!request.redirect(utfconv(location)))
        return false;

    // Redirect to location
    if (content.is_open())
        content.close();
    if (0 <= fdContent) {
        close(fdContent);
        fdContent = -1;
    }
    cache = 0;
    readyState = OPENED;
    return true;
}

// Return true to put this request in the completed list.
bool HttpRequest::complete(bool error)
{
    errorFlag = error;
    if (error)
        response.setStatus(404);
    readyState = (cache || handler || !callbackList.empty()) ? COMPLETE : DONE;
    return readyState == COMPLETE;
}

void HttpRequest::notify()
{
    if (cache)
        cache->notify(this, errorFlag);
    if (!errorFlag && redirect(response)) {
        response.clear();
        send();
        return;
    }

    readyState = DONE;
    if (handler) {
        handler();
        handler.clear();
    }
    for (auto i = callbackList.begin(); i != callbackList.end(); ++i) {
        if (*i) {
            (*i)();
            *i = 0;
        }
    }
}

bool HttpRequest::notify(bool error)
{
    if (complete(error))
        notify();
    return errorFlag;
}

void HttpRequest::open(const std::u16string& method, const std::u16string& urlString)
{
    URL url(base, urlString);
    request.open(utfconv(method), url);
    readyState = OPENED;
}

void HttpRequest::setRequestHeader(const std::u16string& header, const std::u16string& value)
{
    request.setHeader(utfconv(header), utfconv(value));
}

bool HttpRequest::constructResponseFromCache(bool sync)
{
    assert(cache);
    readyState = DONE;
    errorFlag = false;

    response.update(cache->getResponseMessage());
    response.updateStatus(cache->getResponseMessage());

    // TODO: deal with partial...
    int fd = cache->getContentDescriptor();
    if (0 <= fd) {
        fdContent = dup(fd);
        lseek(fdContent, 0, SEEK_SET);
    }

    cache = 0;
    if (sync)
        notify();
    else
        HttpConnectionManager::getInstance().complete(this, errorFlag);
    return errorFlag;
}

namespace {

bool decodeBase64(std::fstream& content, const std::string& data)
{
    static const char* const table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char buf[4];
    char out[3];
    const char* p = data.c_str();
    int i = 0;
    int count = 3;
    while (char c = *p++) {
        if (c == '=') {
            buf[i++] = 0;
            if (--count <= 0)
                return false;
        } else if (const char* found = strchr(table, c))
            buf[i++] = found - table;
        else if (isspace(c))
            continue;
        else
            return false;
        if (i == 4) {
            out[0] = ((buf[0] << 2) & 0xfc) | ((buf[1] >> 4) & 0x03);
            out[1] = ((buf[1] << 4) & 0xf0) | ((buf[2] >> 2) & 0x0f);
            out[2] = ((buf[2] << 6) & 0xc0) | (buf[3] & 0x3f);
            content.write(out, count);
            i = 0;
            count = 3;
        }
    }
    return i == 0;
}

}  // namespace

bool HttpRequest::constructResponseFromData()
{
    URI dataURI(request.getURL());
    const std::string& data(dataURI);
    size_t end = data.find(',');
    if (end == std::string::npos) {
        notify(true);
        return errorFlag;
    }
    bool base64(false);
    if (7 <= end && data.compare(end - 7, 7, ";base64") == 0) {
        end -= 7;
        base64 = true;
    }
    response.parseMediaType(data.c_str() + 5, data.c_str() + end);
    std::fstream& content = getContent();
    if (!content.is_open()) {
        notify(true);
        return errorFlag;
    }
    if (!base64) {
        end += 1;
        std::string decoded(URI::percentDecode(URI::percentDecode(data, end, data.length() - end)));
        content << decoded;
    } else {
        end += 8;
        std::string decoded(URI::percentDecode(URI::percentDecode(data, end, data.length() - end)));
        errorFlag = !decodeBase64(content, decoded);
    }
    content.flush();
    notify(errorFlag);
    return errorFlag;
}

bool HttpRequest::send()
{
    if (3 <= getLogLevel())
        std::cerr << "HttpRequest::send(): " << request.getURL() << '\n';

    if (request.getURL().isEmpty())
        return notify(false);

    if (request.getURL().testProtocol(u"file")) {
        if (request.getMethodCode() != HttpRequestMessage::GET)
            return notify(true);
        std::u16string host = request.getURL().getHostname();
        if (!host.empty() && host != u"localhost")  // TODO: maybe allow local host IP addresses?
            return notify(true);
        std::string path = utfconv(request.getURL().getPathname());
        fdContent = ::open(path.c_str(), O_RDONLY);
        return notify(fdContent == -1);
    }

    if (request.getURL().testProtocol(u"about")) {
        if (aboutPath.empty() || request.getMethodCode() != HttpRequestMessage::GET)
            return notify(true);
        std::string path = utfconv(request.getURL().getPathname());
        if (path.empty())
            path = aboutPath + "/about/index.html";
        else
            path = aboutPath + "/about/" + path;
        fdContent = ::open(path.c_str(), O_RDONLY);
        return notify(fdContent == -1);
    }

    if (request.getURL().testProtocol(u"data"))
        return constructResponseFromData();

    cache = HttpCacheManager::getInstance().send(this);
    if (!cache || cache->isBusy())
        return false;
    return constructResponseFromCache(true);
}

void HttpRequest::abort()
{
    if (readyState == UNSENT)
        return;

    // TODO: implement more details.
    clearHandler();
    HttpConnectionManager& manager = HttpConnectionManager::getInstance();
    manager.abort(this);
    readyState = UNSENT;
    errorFlag = false;
    request.clear();
    response.clear();
    if (content.is_open())
        content.close();
    if (0 <= fdContent) {
        close(fdContent);
        fdContent = -1;
    }
    cache = 0;
}

unsigned short HttpRequest::getStatus() const
{
    return response.getStatus();
}

const std::string& HttpRequest::getStatusText() const
{
    return response.getStatusText();
}

const std::string HttpRequest::getResponseHeader(std::u16string header) const
{
    return response.getResponseHeader(utfconv(header));
}

const std::string& HttpRequest::getAllResponseHeaders() const
{
    return response.getAllResponseHeaders();
}

HttpRequest::HttpRequest(const std::u16string& base) :
    base(base),
    readyState(UNSENT),
    errorFlag(false),
    fdContent(-1),
    cache(0),
    handler(0),
    boxImage(0)
{
}

HttpRequest::~HttpRequest()
{
    abort();
    delete boxImage;
}

}}}}  // org::w3c::dom::bootstrap
<|endoftext|>