<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "fnord-msg/MessageBuilder.h"
#include "fnord-msg/MessageObject.h"
#include "fnord-msg/MessageEncoder.h"
#include "fnord-msg/MessageDecoder.h"
#include "fnord-msg/MessagePrinter.h"
#include <fnord-fts/fts.h>
#include <fnord-fts/fts_common.h>
#include "logjoin/LogJoinTarget.h"
#include "common.h"

using namespace fnord;

namespace cm {

LogJoinTarget::LogJoinTarget(
    const msg::MessageSchema& joined_sessions_schema,
    bool dry_run) :
    joined_sessions_schema_(joined_sessions_schema),
    dry_run_(dry_run),
    num_sessions(0),
    cconv_(currencyConversionTable()) {}

void LogJoinTarget::setNormalize(
    Function<fnord::String (Language lang, const fnord::String& query)> normalizeCb) {
  normalize_ = normalizeCb;
}

void LogJoinTarget::setGetField(
    Function<Option<String> (const DocID& docid, const String& feature)> getFieldCb) {
  get_field_ = getFieldCb;
}

Buffer LogJoinTarget::trackedSessionToJoinedSession(TrackedSession& session) {
  if (!get_field_) {
    RAISE(kRuntimeError, "getField has not been initialized");
  }

  if (!normalize_) {
    RAISE(kRuntimeError, "normalize has not been initialized");
  }

  session.joinEvents(cconv_);
  const auto& schema = joined_sessions_schema_;
  msg::MessageObject obj;

  for (const auto& ci : session.cart_items) {
    auto& ci_obj = obj.addChild(schema.id("cart_items"));

    ci_obj.addChild(
        schema.id("cart_items.time"),
        (uint32_t) (ci.time.unixMicros() / kMicrosPerSecond));

    ci_obj.addChild(
        schema.id("cart_items.item_id"),
        ci.item.docID().docid);

    ci_obj.addChild(
        schema.id("cart_items.quantity"),
        ci.quantity);

    ci_obj.addChild(
        schema.id("cart_items.price_cents"),
        ci.price_cents);

    ci_obj.addChild(
        schema.id("cart_items.currency"),
        (uint32_t) currencyFromString(ci.currency));

    ci_obj.addChild(
        schema.id("cart_items.checkout_step"),
        ci.checkout_step);

    // FIXPAUL use getFields...
    auto docid = ci.item.docID();
    auto shopid = get_field_(docid, "shop_id");
    if (shopid.isEmpty()) {
      fnord::logWarning(
          "cm.logjoin",
          "item not found in featureindex: $0",
          docid.docid);
    } else {
      ci_obj.addChild(
          schema.id("cart_items.shop_id"),
          (uint32_t) std::stoull(shopid.get()));
    }

    auto category1 = get_field_(docid, "category1");
    if (!category1.isEmpty()) {
      ci_obj.addChild(
          schema.id("cart_items.category1"),
          (uint32_t) std::stoull(category1.get()));
    }

    auto category2 = get_field_(docid, "category2");
    if (!category2.isEmpty()) {
      ci_obj.addChild(
          schema.id("cart_items.category2"),
          (uint32_t) std::stoull(category2.get()));
    }

    auto category3 = get_field_(docid, "category3");
    if (!category3.isEmpty()) {
      ci_obj.addChild(
          schema.id("cart_items.category3"),
          (uint32_t) std::stoull(category3.get()));
    }
  }

    uint32_t sess_abgrp = 0;
  for (const auto& q : session.queries) {
    auto& qry_obj = obj.addChild(schema.id("search_queries"));

    /* queries.time */
    qry_obj.addChild(
        schema.id("search_queries.time"),
        (uint32_t) (q.time.unixMicros() / kMicrosPerSecond));

    /* queries.language */
    auto lang = cm::extractLanguage(q.attrs);
    qry_obj.addChild(schema.id("search_queries.language"), (uint32_t) lang);

    /* queries.query_string */
    auto qstr = cm::extractQueryString(q.attrs);
    if (!qstr.isEmpty()) {
      auto qstr_norm = normalize_(lang, qstr.get());
      qry_obj.addChild(schema.id("search_queries.query_string"), qstr.get());
      qry_obj.addChild(schema.id("search_queries.query_string_normalized"), qstr_norm);
    }

    /* queries.shopid */
    auto slrid = cm::extractAttr(q.attrs, "slrid");
    if (!slrid.isEmpty()) {
      uint32_t sid = std::stoul(slrid.get());
      qry_obj.addChild(schema.id("search_queries.shop_id"), sid);
    }

    qry_obj.addChild(schema.id("search_queries.num_result_items"), q.nitems);
    qry_obj.addChild(schema.id("search_queries.num_result_items_clicked"), q.nclicks);
    qry_obj.addChild(schema.id("search_queries.num_ad_impressions"), q.nads);
    qry_obj.addChild(schema.id("search_queries.num_ad_clicks"), q.nadclicks);
    qry_obj.addChild(schema.id("search_queries.num_cart_items"), q.num_cart_items);
    qry_obj.addChild(schema.id("search_queries.cart_value_eurcents"), q.cart_value_eurcents);
    qry_obj.addChild(schema.id("search_queries.num_order_items"), q.num_order_items);
    qry_obj.addChild(schema.id("search_queries.gmv_eurcents"), q.gmv_eurcents);

    /* queries.page */
    auto pg_str = cm::extractAttr(q.attrs, "pg");
    if (!pg_str.isEmpty()) {
      uint32_t pg = std::stoul(pg_str.get());
      qry_obj.addChild(schema.id("search_queries.page"), pg);
    }

    /* queries.ab_test_group */
    auto abgrp = cm::extractABTestGroup(q.attrs);
    if (!abgrp.isEmpty()) {
      sess_abgrp = abgrp.get();
      qry_obj.addChild(schema.id("search_queries.ab_test_group"), abgrp.get());
    }

    /* queries.experiments */
    auto qexps = q.joinedExperiments();
    if (qexps.size() > 0) {
      qry_obj.addChild(schema.id("search_queries.experiments"), qexps);
    }

    /* queries.category1 */
    auto qcat1 = cm::extractAttr(q.attrs, "q_cat1");
    if (!qcat1.isEmpty()) {
      uint32_t c = std::stoul(qcat1.get());
      qry_obj.addChild(schema.id("search_queries.category1"), c);
    }

    /* queries.category1 */
    auto qcat2 = cm::extractAttr(q.attrs, "q_cat2");
    if (!qcat2.isEmpty()) {
      uint32_t c = std::stoul(qcat2.get());
      qry_obj.addChild(schema.id("search_queries.category2"), c);
    }

    /* queries.category1 */
    auto qcat3 = cm::extractAttr(q.attrs, "q_cat3");
    if (!qcat3.isEmpty()) {
      uint32_t c = std::stoul(qcat3.get());
      qry_obj.addChild(schema.id("search_queries.category3"), c);
    }

    /* queries.device_type */
    qry_obj.addChild(
        schema.id("search_queries.device_type"),
        (uint32_t) extractDeviceType(q.attrs));

    /* queries.page_type */
    qry_obj.addChild(
        schema.id("search_queries.page_type"),
        (uint32_t) extractPageType(q.attrs));

    for (const auto& item : q.items) {
      auto& item_obj = qry_obj.addChild(
          schema.id("search_queries.result_items"));

      item_obj.addChild(
          schema.id("search_queries.result_items.position"),
          (uint32_t) item.position);

      item_obj.addChild(
          schema.id("search_queries.result_items.item_id"),
          item.item.docID().docid);

      if (item.clicked) {
        item_obj.addChild(
            schema.id("search_queries.result_items.clicked"),
            msg::TRUE);
      } else {
        item_obj.addChild(
            schema.id("search_queries.result_items.clicked"),
            msg::FALSE);
      }

      auto docid = item.item.docID();

      auto shopid = get_field_(docid, "shop_id");
      if (shopid.isEmpty()) {
        fnord::logWarning(
            "cm.logjoin",
            "item not found in featureindex: $0",
            docid.docid);
      } else {
        item_obj.addChild(
            schema.id("search_queries.result_items.shop_id"),
            (uint32_t) std::stoull(shopid.get()));
      }

      auto category1 = get_field_(docid, "category1");
      if (!category1.isEmpty()) {
        item_obj.addChild(
            schema.id("search_queries.result_items.category1"),
            (uint32_t) std::stoull(category1.get()));
      }

      auto category2 = get_field_(docid, "category2");
      if (!category2.isEmpty()) {
        item_obj.addChild(
            schema.id("search_queries.result_items.category2"),
            (uint32_t) std::stoull(category2.get()));
      }

      auto category3 = get_field_(docid, "category3");
      if (!category3.isEmpty()) {
        item_obj.addChild(
            schema.id("search_queries.result_items.category3"),
            (uint32_t) std::stoull(category3.get()));
      }
    }
  }

  for (const auto& iv : session.item_visits) {
    auto& iv_obj = obj.addChild(schema.id("item_visits"));

    iv_obj.addChild(
        schema.id("item_visits.time"),
        (uint32_t) (iv.time.unixMicros() / kMicrosPerSecond));

    iv_obj.addChild(
        schema.id("item_visits.item_id"),
        iv.item.docID().docid);

    auto docid = iv.item.docID();
    auto shopid = get_field_(docid, "shop_id");
    if (shopid.isEmpty()) {
      fnord::logWarning(
          "cm.logjoin",
          "item not found in featureindex: $0",
          docid.docid);
    } else {
      iv_obj.addChild(
          schema.id("item_visits.shop_id"),
          (uint32_t) std::stoull(shopid.get()));
    }

    auto category1 = get_field_(docid, "category1");
    if (!category1.isEmpty()) {
      iv_obj.addChild(
          schema.id("item_visits.category1"),
          (uint32_t) std::stoull(category1.get()));
    }

    auto category2 = get_field_(docid, "category2");
    if (!category2.isEmpty()) {
      iv_obj.addChild(
          schema.id("item_visits.category2"),
          (uint32_t) std::stoull(category2.get()));
    }

    auto category3 = get_field_(docid, "category3");
    if (!category3.isEmpty()) {
      iv_obj.addChild(
          schema.id("item_visits.category3"),
          (uint32_t) std::stoull(category3.get()));
    }
  }

  if (sess_abgrp > 0) {
    obj.addChild(schema.id("ab_test_group"), sess_abgrp);
  }

  auto exps = session.joinedExperiments();
  if (exps.size() > 0) {
    obj.addChild(schema.id("experiments"), exps);
  }

  if (!session.referrer_url.isEmpty()) {
    obj.addChild(schema.id("referrer_url"), session.referrer_url.get());
  }

  if (!session.referrer_campaign.isEmpty()) {
    obj.addChild(schema.id("referrer_campaign"), session.referrer_campaign.get());
  }

  if (!session.referrer_name.isEmpty()) {
    obj.addChild(schema.id("referrer_name"), session.referrer_name.get());
  }

  obj.addChild(schema.id("num_cart_items"), session.num_cart_items);
  obj.addChild(schema.id("cart_value_eurcents"), session.cart_value_eurcents);
  obj.addChild(schema.id("num_order_items"), session.num_order_items);
  obj.addChild(schema.id("gmv_eurcents"), session.gmv_eurcents);

  Buffer msg_buf;
  auto first_seen = session.firstSeenTime();
  auto last_seen = session.lastSeenTime();
  if (first_seen.isEmpty() || last_seen.isEmpty()) {
    RAISE(kRuntimeError, "session: time isn't set");
  }

  obj.addChild(
      schema.id("first_seen_time"),
      first_seen.get().unixMicros() / kMicrosPerSecond);

  obj.addChild(
      schema.id("last_seen_time"),
      last_seen.get().unixMicros() / kMicrosPerSecond);

  msg::MessageEncoder::encode(obj, joined_sessions_schema_, &msg_buf);
  return msg_buf;
}

void LogJoinTarget::onSession(
    mdb::MDBTransaction* txn,
    TrackedSession& session) {

  Buffer msg_buf = trackedSessionToJoinedSession(session);
  if (dry_run_) {
    fnord::logInfo(
        "cm.logjoin",
        "[DRYRUN] not uploading session: ", 1);
        //msg::MessagePrinter::print(obj, joined_sessions_schema_));
  } else {
    auto first_seen = session.firstSeenTime();
    auto time = first_seen.get().unixMicros();
    util::BinaryMessageWriter buf;
    buf.appendUInt64(time);
    buf.appendUInt64(rnd_.random64());
    buf.appendVarUInt(msg_buf.size());
    buf.append(msg_buf.data(), msg_buf.size());

    auto key = StringUtil::format("__uploadq-sessions-$0",  rnd_.hex128());
    txn->update(key.data(), key.size(), buf.data(), buf.size());
  }

  ++num_sessions;
}

} // namespace cm

<commit_msg>fixed indentation<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "fnord-msg/MessageBuilder.h"
#include "fnord-msg/MessageObject.h"
#include "fnord-msg/MessageEncoder.h"
#include "fnord-msg/MessageDecoder.h"
#include "fnord-msg/MessagePrinter.h"
#include <fnord-fts/fts.h>
#include <fnord-fts/fts_common.h>
#include "logjoin/LogJoinTarget.h"
#include "common.h"

using namespace fnord;

namespace cm {

LogJoinTarget::LogJoinTarget(
    const msg::MessageSchema& joined_sessions_schema,
    bool dry_run) :
    joined_sessions_schema_(joined_sessions_schema),
    dry_run_(dry_run),
    num_sessions(0),
    cconv_(currencyConversionTable()) {}

void LogJoinTarget::setNormalize(
    Function<fnord::String (Language lang, const fnord::String& query)> normalizeCb) {
  normalize_ = normalizeCb;
}

void LogJoinTarget::setGetField(
    Function<Option<String> (const DocID& docid, const String& feature)> getFieldCb) {
  get_field_ = getFieldCb;
}

Buffer LogJoinTarget::trackedSessionToJoinedSession(TrackedSession& session) {
  if (!get_field_) {
    RAISE(kRuntimeError, "getField has not been initialized");
  }

  if (!normalize_) {
    RAISE(kRuntimeError, "normalize has not been initialized");
  }

  session.joinEvents(cconv_);
  const auto& schema = joined_sessions_schema_;
  msg::MessageObject obj;

  for (const auto& ci : session.cart_items) {
    auto& ci_obj = obj.addChild(schema.id("cart_items"));

    ci_obj.addChild(
        schema.id("cart_items.time"),
        (uint32_t) (ci.time.unixMicros() / kMicrosPerSecond));

    ci_obj.addChild(
        schema.id("cart_items.item_id"),
        ci.item.docID().docid);

    ci_obj.addChild(
        schema.id("cart_items.quantity"),
        ci.quantity);

    ci_obj.addChild(
        schema.id("cart_items.price_cents"),
        ci.price_cents);

    ci_obj.addChild(
        schema.id("cart_items.currency"),
        (uint32_t) currencyFromString(ci.currency));

    ci_obj.addChild(
        schema.id("cart_items.checkout_step"),
        ci.checkout_step);

    // FIXPAUL use getFields...
    auto docid = ci.item.docID();
    auto shopid = get_field_(docid, "shop_id");
    if (shopid.isEmpty()) {
      fnord::logWarning(
          "cm.logjoin",
          "item not found in featureindex: $0",
          docid.docid);
    } else {
      ci_obj.addChild(
          schema.id("cart_items.shop_id"),
          (uint32_t) std::stoull(shopid.get()));
    }

    auto category1 = get_field_(docid, "category1");
    if (!category1.isEmpty()) {
      ci_obj.addChild(
          schema.id("cart_items.category1"),
          (uint32_t) std::stoull(category1.get()));
    }

    auto category2 = get_field_(docid, "category2");
    if (!category2.isEmpty()) {
      ci_obj.addChild(
          schema.id("cart_items.category2"),
          (uint32_t) std::stoull(category2.get()));
    }

    auto category3 = get_field_(docid, "category3");
    if (!category3.isEmpty()) {
      ci_obj.addChild(
          schema.id("cart_items.category3"),
          (uint32_t) std::stoull(category3.get()));
    }
  }

  uint32_t sess_abgrp = 0;
  for (const auto& q : session.queries) {
    auto& qry_obj = obj.addChild(schema.id("search_queries"));

    /* queries.time */
    qry_obj.addChild(
        schema.id("search_queries.time"),
        (uint32_t) (q.time.unixMicros() / kMicrosPerSecond));

    /* queries.language */
    auto lang = cm::extractLanguage(q.attrs);
    qry_obj.addChild(schema.id("search_queries.language"), (uint32_t) lang);

    /* queries.query_string */
    auto qstr = cm::extractQueryString(q.attrs);
    if (!qstr.isEmpty()) {
      auto qstr_norm = normalize_(lang, qstr.get());
      qry_obj.addChild(schema.id("search_queries.query_string"), qstr.get());
      qry_obj.addChild(schema.id("search_queries.query_string_normalized"), qstr_norm);
    }

    /* queries.shopid */
    auto slrid = cm::extractAttr(q.attrs, "slrid");
    if (!slrid.isEmpty()) {
      uint32_t sid = std::stoul(slrid.get());
      qry_obj.addChild(schema.id("search_queries.shop_id"), sid);
    }

    qry_obj.addChild(schema.id("search_queries.num_result_items"), q.nitems);
    qry_obj.addChild(schema.id("search_queries.num_result_items_clicked"), q.nclicks);
    qry_obj.addChild(schema.id("search_queries.num_ad_impressions"), q.nads);
    qry_obj.addChild(schema.id("search_queries.num_ad_clicks"), q.nadclicks);
    qry_obj.addChild(schema.id("search_queries.num_cart_items"), q.num_cart_items);
    qry_obj.addChild(schema.id("search_queries.cart_value_eurcents"), q.cart_value_eurcents);
    qry_obj.addChild(schema.id("search_queries.num_order_items"), q.num_order_items);
    qry_obj.addChild(schema.id("search_queries.gmv_eurcents"), q.gmv_eurcents);

    /* queries.page */
    auto pg_str = cm::extractAttr(q.attrs, "pg");
    if (!pg_str.isEmpty()) {
      uint32_t pg = std::stoul(pg_str.get());
      qry_obj.addChild(schema.id("search_queries.page"), pg);
    }

    /* queries.ab_test_group */
    auto abgrp = cm::extractABTestGroup(q.attrs);
    if (!abgrp.isEmpty()) {
      sess_abgrp = abgrp.get();
      qry_obj.addChild(schema.id("search_queries.ab_test_group"), abgrp.get());
    }

    /* queries.experiments */
    auto qexps = q.joinedExperiments();
    if (qexps.size() > 0) {
      qry_obj.addChild(schema.id("search_queries.experiments"), qexps);
    }

    /* queries.category1 */
    auto qcat1 = cm::extractAttr(q.attrs, "q_cat1");
    if (!qcat1.isEmpty()) {
      uint32_t c = std::stoul(qcat1.get());
      qry_obj.addChild(schema.id("search_queries.category1"), c);
    }

    /* queries.category1 */
    auto qcat2 = cm::extractAttr(q.attrs, "q_cat2");
    if (!qcat2.isEmpty()) {
      uint32_t c = std::stoul(qcat2.get());
      qry_obj.addChild(schema.id("search_queries.category2"), c);
    }

    /* queries.category1 */
    auto qcat3 = cm::extractAttr(q.attrs, "q_cat3");
    if (!qcat3.isEmpty()) {
      uint32_t c = std::stoul(qcat3.get());
      qry_obj.addChild(schema.id("search_queries.category3"), c);
    }

    /* queries.device_type */
    qry_obj.addChild(
        schema.id("search_queries.device_type"),
        (uint32_t) extractDeviceType(q.attrs));

    /* queries.page_type */
    qry_obj.addChild(
        schema.id("search_queries.page_type"),
        (uint32_t) extractPageType(q.attrs));

    for (const auto& item : q.items) {
      auto& item_obj = qry_obj.addChild(
          schema.id("search_queries.result_items"));

      item_obj.addChild(
          schema.id("search_queries.result_items.position"),
          (uint32_t) item.position);

      item_obj.addChild(
          schema.id("search_queries.result_items.item_id"),
          item.item.docID().docid);

      if (item.clicked) {
        item_obj.addChild(
            schema.id("search_queries.result_items.clicked"),
            msg::TRUE);
      } else {
        item_obj.addChild(
            schema.id("search_queries.result_items.clicked"),
            msg::FALSE);
      }

      auto docid = item.item.docID();

      auto shopid = get_field_(docid, "shop_id");
      if (shopid.isEmpty()) {
        fnord::logWarning(
            "cm.logjoin",
            "item not found in featureindex: $0",
            docid.docid);
      } else {
        item_obj.addChild(
            schema.id("search_queries.result_items.shop_id"),
            (uint32_t) std::stoull(shopid.get()));
      }

      auto category1 = get_field_(docid, "category1");
      if (!category1.isEmpty()) {
        item_obj.addChild(
            schema.id("search_queries.result_items.category1"),
            (uint32_t) std::stoull(category1.get()));
      }

      auto category2 = get_field_(docid, "category2");
      if (!category2.isEmpty()) {
        item_obj.addChild(
            schema.id("search_queries.result_items.category2"),
            (uint32_t) std::stoull(category2.get()));
      }

      auto category3 = get_field_(docid, "category3");
      if (!category3.isEmpty()) {
        item_obj.addChild(
            schema.id("search_queries.result_items.category3"),
            (uint32_t) std::stoull(category3.get()));
      }
    }
  }

  for (const auto& iv : session.item_visits) {
    auto& iv_obj = obj.addChild(schema.id("item_visits"));

    iv_obj.addChild(
        schema.id("item_visits.time"),
        (uint32_t) (iv.time.unixMicros() / kMicrosPerSecond));

    iv_obj.addChild(
        schema.id("item_visits.item_id"),
        iv.item.docID().docid);

    auto docid = iv.item.docID();
    auto shopid = get_field_(docid, "shop_id");
    if (shopid.isEmpty()) {
      fnord::logWarning(
          "cm.logjoin",
          "item not found in featureindex: $0",
          docid.docid);
    } else {
      iv_obj.addChild(
          schema.id("item_visits.shop_id"),
          (uint32_t) std::stoull(shopid.get()));
    }

    auto category1 = get_field_(docid, "category1");
    if (!category1.isEmpty()) {
      iv_obj.addChild(
          schema.id("item_visits.category1"),
          (uint32_t) std::stoull(category1.get()));
    }

    auto category2 = get_field_(docid, "category2");
    if (!category2.isEmpty()) {
      iv_obj.addChild(
          schema.id("item_visits.category2"),
          (uint32_t) std::stoull(category2.get()));
    }

    auto category3 = get_field_(docid, "category3");
    if (!category3.isEmpty()) {
      iv_obj.addChild(
          schema.id("item_visits.category3"),
          (uint32_t) std::stoull(category3.get()));
    }
  }

  if (sess_abgrp > 0) {
    obj.addChild(schema.id("ab_test_group"), sess_abgrp);
  }

  auto exps = session.joinedExperiments();
  if (exps.size() > 0) {
    obj.addChild(schema.id("experiments"), exps);
  }

  if (!session.referrer_url.isEmpty()) {
    obj.addChild(schema.id("referrer_url"), session.referrer_url.get());
  }

  if (!session.referrer_campaign.isEmpty()) {
    obj.addChild(schema.id("referrer_campaign"), session.referrer_campaign.get());
  }

  if (!session.referrer_name.isEmpty()) {
    obj.addChild(schema.id("referrer_name"), session.referrer_name.get());
  }

  obj.addChild(schema.id("num_cart_items"), session.num_cart_items);
  obj.addChild(schema.id("cart_value_eurcents"), session.cart_value_eurcents);
  obj.addChild(schema.id("num_order_items"), session.num_order_items);
  obj.addChild(schema.id("gmv_eurcents"), session.gmv_eurcents);

  Buffer msg_buf;
  auto first_seen = session.firstSeenTime();
  auto last_seen = session.lastSeenTime();
  if (first_seen.isEmpty() || last_seen.isEmpty()) {
    RAISE(kRuntimeError, "session: time isn't set");
  }

  obj.addChild(
      schema.id("first_seen_time"),
      first_seen.get().unixMicros() / kMicrosPerSecond);

  obj.addChild(
      schema.id("last_seen_time"),
      last_seen.get().unixMicros() / kMicrosPerSecond);

  msg::MessageEncoder::encode(obj, joined_sessions_schema_, &msg_buf);
  return msg_buf;
}

void LogJoinTarget::onSession(
    mdb::MDBTransaction* txn,
    TrackedSession& session) {

  Buffer msg_buf = trackedSessionToJoinedSession(session);
  if (dry_run_) {
    fnord::logInfo(
        "cm.logjoin",
        "[DRYRUN] not uploading session: ", 1);
        //msg::MessagePrinter::print(obj, joined_sessions_schema_));
  } else {
    auto first_seen = session.firstSeenTime();
    auto time = first_seen.get().unixMicros();
    util::BinaryMessageWriter buf;
    buf.appendUInt64(time);
    buf.appendUInt64(rnd_.random64());
    buf.appendVarUInt(msg_buf.size());
    buf.append(msg_buf.data(), msg_buf.size());

    auto key = StringUtil::format("__uploadq-sessions-$0",  rnd_.hex128());
    txn->update(key.data(), key.size(), buf.data(), buf.size());
  }

  ++num_sessions;
}

} // namespace cm

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Zubax, zubax.com
 * Distributed under the MIT License, available in the file LICENSE.
 * Author: Pavel Kirienko <pavel.kirienko@zubax.com>
 */

/*
 * Platform-independent I2C master implemented in software.
 * Sources:
 *      https://en.wikipedia.org/wiki/I%C2%B2C
 *      http://www.robot-electronics.co.uk/i2c-tutorial
 */

#pragma once

#include <zubax_chibios/os.hpp>
#include <functional>
#include <cstdint>
#include <array>

namespace os
{
namespace software_i2c
{
/**
 * Generic bit-banging I2C master on bare GPIO.
 * The pins must be configured in open-drain mode, at high level by default.
 * This class automatically emits I2C stop condition and releases the pins when the instance is destroyed.
 *
 * All access must be done from regular threads outside of critical sections.
 *
 * All access is protected with a mutex, so that I2C transactions are atomic, and the class can be used from
 * multiple threads concurrently. If recursive mutexes are enabled, it is also possible to lock the bus
 * for multiple atomic transactions.
 *
 * Usage:
 *     Master master(GPIO_PORT_I2C_SCL, GPIO_PIN_I2C_SCL,
 *                   GPIO_PORT_I2C_SDA, GPIO_PIN_I2C_SDA);
 *     const std::array<std::uint8_t, 3> tx = { 1, 2, 3 };
 *     std::array<std::uint8_t, 5> rx;
 *     auto result = master.exchange(address, tx, rx);
 */
class Master final
{
public:
    enum class Result
    {
        OK,
        Timeout,
        ArbitrationLost,
        NACK
    };

    static constexpr std::uint32_t DefaultClockStretchTimeoutUSec = 10000;

private:
    class I2CPin
    {
        ::ioportid_t const port_;
        const std::uint8_t pin_;

    public:
        I2CPin(::ioportid_t gpio_port, std::uint8_t gpio_pin) :
            port_(gpio_port), pin_(gpio_pin)
        {
        }

        ~I2CPin()
        {
            set();       // Returning to default state
        }

        void set()       { palSetPad(port_, pin_); }
        void clear()     { palClearPad(port_, pin_); }
        bool get() const { return palReadPad(port_, pin_); }
    };

    chibios_rt::Mutex mutex_;
    I2CPin scl_;
    I2CPin sda_;
    bool started_ = false;
    const std::function<void ()> delay_;
    const std::uint32_t clock_stretch_timeout_usec_;

    bool sclWait() const
    {
        const ::systime_t started_at = chVTGetSystemTimeX();
        while (!scl_.get())
        {
            chThdSleep(1);          // Sleeping one sys tick
            if (chVTTimeElapsedSinceX(started_at) > US2ST(clock_stretch_timeout_usec_))
            {
                return false;
            }
        }
        return true;
    }

    Result writeBit(bool bit)
    {
        if (bit)
        {
            sda_.set();
        }
        else
        {
            sda_.clear();
        }
        delay_();
        scl_.set();
        delay_();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        if (bit && !sda_.get())
        {
            return Result::ArbitrationLost;
        }
        scl_.clear();
        return Result::OK;
    }

    Result readBit(bool& out_bit)
    {
        sda_.set();
        delay_();
        scl_.set();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        delay_();
        out_bit = sda_.get();
        scl_.clear();
        return Result::OK;
    }

    /**
     * Generates I2C start on the bus.
     * If the bus has already been started, generates a repeated start sequence.
     */
    Result start()
    {
        sda_.set();
        delay_();
        scl_.set();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        delay_();
        if (!sda_.get())
        {
            return Result::ArbitrationLost;
        }
        sda_.clear();
        delay_();
        scl_.clear();
        delay_();
        started_ = true;
        return Result::OK;
    }

    bool isStarted() const { return started_; }

    /**
     * Stops the bus.
     * If the bus has not been started, the function will generate an assertion failure.
     */
    Result stop()
    {
        assert(started_);
        sda_.clear();
        delay_();
        scl_.set();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        delay_();
        sda_.set();
        delay_();
        if (!sda_.get())
        {
            return Result::ArbitrationLost;
        }
        delay_();
        started_ = false;
        return Result::OK;
    }

    Result writeByte(std::uint8_t byte)
    {
        assert(started_);
        for (std::uint8_t bit = 0; bit < 8; bit++)
        {
            auto res = writeBit((byte & 0x80) != 0);
            if (res != Result::OK)
            {
                return res;
            }
            byte = std::uint8_t(byte << 1U);
        }

        bool nack = false;
        auto res = readBit(nack);
        if (res != Result::OK)
        {
            return res;
        }

        return nack ? Result::NACK : Result::OK;
    }

    Result writeAddress7Bit(std::uint8_t address, bool read)
    {
        assert(address < 128U);
        address = std::uint8_t((address << 1U) | read);
        return writeByte(address);
    }

    Result readByte(std::uint8_t& out_byte, bool ack)
    {
        assert(started_);
        out_byte = 0;
        for (std::uint8_t i = 0; i < 8; i++)
        {
            bool bit = false;
            auto res = readBit(bit);
            if (res != Result::OK)
            {
                return res;
            }
            out_byte = std::uint8_t((out_byte << 1) | (bit ? 1U : 0U));
        }

        return writeBit(!ack);
    }

    // Master is non-copyable! Otherwise that breaks the mutex.
    Master(const Master&)  = delete;
    Master(const Master&&) = delete;
    Master& operator=(const Master&)  = delete;
    Master& operator=(const Master&&) = delete;

public:
    Master(::ioportid_t scl_port, std::uint8_t scl_pin,
           ::ioportid_t sda_port, std::uint8_t sda_pin,
           std::function<void ()> arg_cycle_delay,
           std::uint32_t arg_clock_stretch_timeout_usec = DefaultClockStretchTimeoutUSec) :
        scl_(scl_port, scl_pin),
        sda_(sda_port, sda_pin),
        delay_(arg_cycle_delay),
        clock_stretch_timeout_usec_(arg_clock_stretch_timeout_usec)
    {
        assert(delay_);
    }

    /**
     * Destructor ensures that the bus is correctly stopped, and GPIO pins are correctly set to the high level.
     */
    ~Master()
    {
        if (started_)
        {
            (void)stop();
        }
    }

    /**
     * Modulates a bus reset sequence.
     * The reset sequence allows the master to bring the bus into a known state.
     * Its use is advised by some EEPROM memory chip vendors, for example, like ROHM BR24G128.
     */
    void reset()
    {
        static constexpr std::uint8_t MaxClockCycles = 30;
        static constexpr std::uint8_t AllowStopIfSDAHighAfterThisManyClockCycles = 14;

        ::os::MutexLocker bus_locker(mutex_);

        for (std::uint8_t i = 0; i < MaxClockCycles; i++)
        {
            bool the_bit = false;
            (void) readBit(the_bit);
            if ((i > AllowStopIfSDAHighAfterThisManyClockCycles) && the_bit)
            {
                break;
            }
        }

        delay_();

        started_ = true;
        (void) stop();
    }

    /**
     * This function first writes bytes to the bus, if any, and then reads data back, if requested.
     * It will automatically generate start and stop sequences.
     */
    Result exchange(std::uint8_t address,
                    const void* tx_data, const std::uint16_t tx_size,
                          void* rx_data, const std::uint16_t rx_size)
    {
        ::os::MutexLocker bus_locker(mutex_);

        // This will ensure that the bus is correctly stopped at exit
        struct RAIIStopper
        {
            Master& owner_;
            RAIIStopper(Master& master) : owner_(master) { }
            ~RAIIStopper()
            {
                if (owner_.isStarted())
                {
                    (void)owner_.stop();
                }
            }
        } const volatile stopper(*this);

        // Writing
        if (tx_size > 0)
        {
            auto res = start();
            if (res != Result::OK)
            {
                return res;
            }

            res = writeAddress7Bit(address, false);
            if (res != Result::OK)
            {
                return res;
            }

            const std::uint8_t* p = reinterpret_cast<const std::uint8_t*>(tx_data);

            for (std::uint16_t i = 0; i < tx_size; i++)
            {
                res = writeByte(*p++);
                if (res != Result::OK)
                {
                    return res;
                }
            }
        }

        // Reading
        if (rx_size > 0)
        {
            auto res = start();
            if (res != Result::OK)
            {
                return res;
            }

            res = writeAddress7Bit(address, true);
            if (res != Result::OK)
            {
                return res;
            }

            std::uint8_t* p = reinterpret_cast<std::uint8_t*>(rx_data);
            std::uint16_t left = rx_size;

            while (left --> 0)
            {
                res = readByte(*p++, left > 0);
                if (res != Result::OK)
                {
                    return res;
                }
            }
        }

        return Result::OK;
    }

    /**
     * Safer wrapper over @ref exchange(). Usage:
     *     const std::array<std::uint8_t, 3> tx = { 1, 2, 3 };
     *     std::array<std::uint8_t, 5> rx;
     *     auto result = master.exchange(address, tx, rx);
     */
    template<std::size_t TxSize, std::size_t RxSize>
    Result exchange(std::uint8_t address,
                    const std::array<std::uint8_t, TxSize>& tx,
                          std::array<std::uint8_t, RxSize>& rx)
    {
        return exchange(address, tx.data(), TxSize, rx.data(), RxSize);
    }

#if CH_CFG_USE_MUTEXES_RECURSIVE
    /**
     * This RAII helper allows the user to lock the bus for multiple subsequent atomic transactions.
     * This class is only available if recursive mutexes are enabled.
     */
    class AtomicBusAccessLocker
    {
        ::os::MutexLocker mutex_locker_;
    public:
        explicit AtomicBusAccessLocker(Master& m) : mutex_locker_(m.mutex_) { }
    };
#endif
};

}
}
<commit_msg>I2C: using std::chrono for time specifications<commit_after>/*
 * Copyright (c) 2016 Zubax, zubax.com
 * Distributed under the MIT License, available in the file LICENSE.
 * Author: Pavel Kirienko <pavel.kirienko@zubax.com>
 */

/*
 * Platform-independent I2C master implemented in software.
 * Sources:
 *      https://en.wikipedia.org/wiki/I%C2%B2C
 *      http://www.robot-electronics.co.uk/i2c-tutorial
 */

#pragma once

#include <zubax_chibios/os.hpp>
#include <functional>
#include <cstdint>
#include <chrono>
#include <array>

namespace os
{
namespace software_i2c
{
/**
 * Generic bit-banging I2C master on bare GPIO.
 * The pins must be configured in open-drain mode, at high level by default.
 * This class automatically emits I2C stop condition and releases the pins when the instance is destroyed.
 *
 * All access must be done from regular threads outside of critical sections.
 *
 * All access is protected with a mutex, so that I2C transactions are atomic, and the class can be used from
 * multiple threads concurrently. If recursive mutexes are enabled, it is also possible to lock the bus
 * for multiple atomic transactions.
 *
 * Usage:
 *     Master master(GPIO_PORT_I2C_SCL, GPIO_PIN_I2C_SCL,
 *                   GPIO_PORT_I2C_SDA, GPIO_PIN_I2C_SDA);
 *     const std::array<std::uint8_t, 3> tx = { 1, 2, 3 };
 *     std::array<std::uint8_t, 5> rx;
 *     auto result = master.exchange(address, tx, rx);
 */
class Master final
{
public:
    enum class Result
    {
        OK,
        Timeout,
        ArbitrationLost,
        NACK
    };

private:
    class I2CPin
    {
        ::ioportid_t const port_;
        const std::uint8_t pin_;

    public:
        I2CPin(::ioportid_t gpio_port, std::uint8_t gpio_pin) :
            port_(gpio_port), pin_(gpio_pin)
        {
        }

        ~I2CPin()
        {
            set();       // Returning to default state
        }

        void set()       { palSetPad(port_, pin_); }
        void clear()     { palClearPad(port_, pin_); }
        bool get() const { return palReadPad(port_, pin_); }
    };

    chibios_rt::Mutex mutex_;
    I2CPin scl_;
    I2CPin sda_;
    bool started_ = false;
    const std::function<void ()> delay_;
    const std::uint32_t clock_stretch_timeout_ticks_;

    bool sclWait() const
    {
        const ::systime_t started_at = chVTGetSystemTimeX();
        while (!scl_.get())
        {
            chThdSleep(1);          // Sleeping one sys tick
            // Note that we should use greater comparison rather than greater-or-equal to prevent off-by-one errors
            if (chVTTimeElapsedSinceX(started_at) > clock_stretch_timeout_ticks_)
            {
                return false;
            }
        }
        return true;
    }

    Result writeBit(bool bit)
    {
        if (bit)
        {
            sda_.set();
        }
        else
        {
            sda_.clear();
        }
        delay_();
        scl_.set();
        delay_();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        if (bit && !sda_.get())
        {
            return Result::ArbitrationLost;
        }
        scl_.clear();
        return Result::OK;
    }

    Result readBit(bool& out_bit)
    {
        sda_.set();
        delay_();
        scl_.set();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        delay_();
        out_bit = sda_.get();
        scl_.clear();
        return Result::OK;
    }

    /**
     * Generates I2C start on the bus.
     * If the bus has already been started, generates a repeated start sequence.
     */
    Result start()
    {
        sda_.set();
        delay_();
        scl_.set();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        delay_();
        if (!sda_.get())
        {
            return Result::ArbitrationLost;
        }
        sda_.clear();
        delay_();
        scl_.clear();
        delay_();
        started_ = true;
        return Result::OK;
    }

    bool isStarted() const { return started_; }

    /**
     * Stops the bus.
     * If the bus has not been started, the function will generate an assertion failure.
     */
    Result stop()
    {
        assert(started_);
        sda_.clear();
        delay_();
        scl_.set();
        if (!sclWait())
        {
            return Result::Timeout;
        }
        delay_();
        sda_.set();
        delay_();
        if (!sda_.get())
        {
            return Result::ArbitrationLost;
        }
        delay_();
        started_ = false;
        return Result::OK;
    }

    Result writeByte(std::uint8_t byte)
    {
        assert(started_);
        for (std::uint8_t bit = 0; bit < 8; bit++)
        {
            auto res = writeBit((byte & 0x80) != 0);
            if (res != Result::OK)
            {
                return res;
            }
            byte = std::uint8_t(byte << 1U);
        }

        bool nack = false;
        auto res = readBit(nack);
        if (res != Result::OK)
        {
            return res;
        }

        return nack ? Result::NACK : Result::OK;
    }

    Result writeAddress7Bit(std::uint8_t address, bool read)
    {
        assert(address < 128U);
        address = std::uint8_t((address << 1U) | read);
        return writeByte(address);
    }

    Result readByte(std::uint8_t& out_byte, bool ack)
    {
        assert(started_);
        out_byte = 0;
        for (std::uint8_t i = 0; i < 8; i++)
        {
            bool bit = false;
            auto res = readBit(bit);
            if (res != Result::OK)
            {
                return res;
            }
            out_byte = std::uint8_t((out_byte << 1) | (bit ? 1U : 0U));
        }

        return writeBit(!ack);
    }

    // Master is non-copyable! Otherwise that breaks the mutex.
    Master(const Master&)  = delete;
    Master(const Master&&) = delete;
    Master& operator=(const Master&)  = delete;
    Master& operator=(const Master&&) = delete;

public:
    template <typename Rep, typename Period>
    Master(::ioportid_t scl_port, std::uint8_t scl_pin,
           ::ioportid_t sda_port, std::uint8_t sda_pin,
           const std::function<void ()>& arg_cycle_delay,
           const std::chrono::duration<Rep, Period> arg_clock_stretch_timeout) :
        scl_(scl_port, scl_pin),
        sda_(sda_port, sda_pin),
        delay_(arg_cycle_delay),
        clock_stretch_timeout_ticks_(US2ST(
            std::chrono::duration_cast<std::chrono::microseconds>(arg_clock_stretch_timeout).count()))
    {
        assert(delay_);
        assert(clock_stretch_timeout_ticks_ > 0);
    }

    /**
     * Destructor ensures that the bus is correctly stopped, and GPIO pins are correctly set to the high level.
     */
    ~Master()
    {
        if (started_)
        {
            (void)stop();
        }
    }

    /**
     * Modulates a bus reset sequence.
     * The reset sequence allows the master to bring the bus into a known state.
     * Its use is advised by some EEPROM memory chip vendors, for example, like ROHM BR24G128.
     */
    void reset()
    {
        static constexpr std::uint8_t MaxClockCycles = 30;
        static constexpr std::uint8_t AllowStopIfSDAHighAfterThisManyClockCycles = 14;

        ::os::MutexLocker bus_locker(mutex_);

        for (std::uint8_t i = 0; i < MaxClockCycles; i++)
        {
            bool the_bit = false;
            (void) readBit(the_bit);
            if ((i > AllowStopIfSDAHighAfterThisManyClockCycles) && the_bit)
            {
                break;
            }
        }

        delay_();

        started_ = true;
        (void) stop();
    }

    /**
     * This function first writes bytes to the bus, if any, and then reads data back, if requested.
     * It will automatically generate start and stop sequences.
     */
    Result exchange(std::uint8_t address,
                    const void* tx_data, const std::uint16_t tx_size,
                          void* rx_data, const std::uint16_t rx_size)
    {
        ::os::MutexLocker bus_locker(mutex_);

        // This will ensure that the bus is correctly stopped at exit
        struct RAIIStopper
        {
            Master& owner_;
            RAIIStopper(Master& master) : owner_(master) { }
            ~RAIIStopper()
            {
                if (owner_.isStarted())
                {
                    (void)owner_.stop();
                }
            }
        } const volatile stopper(*this);

        // Writing
        if (tx_size > 0)
        {
            auto res = start();
            if (res != Result::OK)
            {
                return res;
            }

            res = writeAddress7Bit(address, false);
            if (res != Result::OK)
            {
                return res;
            }

            const std::uint8_t* p = reinterpret_cast<const std::uint8_t*>(tx_data);

            for (std::uint16_t i = 0; i < tx_size; i++)
            {
                res = writeByte(*p++);
                if (res != Result::OK)
                {
                    return res;
                }
            }
        }

        // Reading
        if (rx_size > 0)
        {
            auto res = start();
            if (res != Result::OK)
            {
                return res;
            }

            res = writeAddress7Bit(address, true);
            if (res != Result::OK)
            {
                return res;
            }

            std::uint8_t* p = reinterpret_cast<std::uint8_t*>(rx_data);
            std::uint16_t left = rx_size;

            while (left --> 0)
            {
                res = readByte(*p++, left > 0);
                if (res != Result::OK)
                {
                    return res;
                }
            }
        }

        return Result::OK;
    }

    /**
     * Safer wrapper over @ref exchange(). Usage:
     *     const std::array<std::uint8_t, 3> tx = { 1, 2, 3 };
     *     std::array<std::uint8_t, 5> rx;
     *     auto result = master.exchange(address, tx, rx);
     */
    template<std::size_t TxSize, std::size_t RxSize>
    Result exchange(std::uint8_t address,
                    const std::array<std::uint8_t, TxSize>& tx,
                          std::array<std::uint8_t, RxSize>& rx)
    {
        return exchange(address, tx.data(), TxSize, rx.data(), RxSize);
    }

#if CH_CFG_USE_MUTEXES_RECURSIVE
    /**
     * This RAII helper allows the user to lock the bus for multiple subsequent atomic transactions.
     * This class is only available if recursive mutexes are enabled.
     */
    class AtomicBusAccessLocker
    {
        ::os::MutexLocker mutex_locker_;
    public:
        explicit AtomicBusAccessLocker(Master& m) : mutex_locker_(m.mutex_) { }
    };
#endif
};

}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 ARM Limited
 * All rights reserved.
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 *          Nathan Binkert
 */

#include "base/trace.hh"
#include "debug/AddrRanges.hh"
#include "dev/io_device.hh"
#include "sim/system.hh"

PioPort::PioPort(PioDevice *dev)
    : SimpleTimingPort(dev->name() + ".pio", dev), device(dev)
{
}

Tick
PioPort::recvAtomic(PacketPtr pkt)
{
    // @todo: We need to pay for this and not just zero it out
    pkt->firstWordDelay = pkt->lastWordDelay = 0;

    return pkt->isRead() ? device->read(pkt) : device->write(pkt);
}

AddrRangeList
PioPort::getAddrRanges() const
{
    return device->getAddrRanges();
}

PioDevice::PioDevice(const Params *p)
    : MemObject(p), sys(p->system), pioPort(this)
{}

PioDevice::~PioDevice()
{
}

void
PioDevice::init()
{
    if (!pioPort.isConnected())
        panic("Pio port of %s not connected to anything!", name());
    pioPort.sendRangeChange();
}

BaseSlavePort &
PioDevice::getSlavePort(const std::string &if_name, PortID idx)
{
    if (if_name == "pio") {
        return pioPort;
    }
    return MemObject::getSlavePort(if_name, idx);
}

unsigned int
PioDevice::drain(DrainManager *dm)
{
    unsigned int count;
    count = pioPort.drain(dm);
    if (count)
        setDrainState(Drainable::Draining);
    else
        setDrainState(Drainable::Drained);
    return count;
}

BasicPioDevice::BasicPioDevice(const Params *p, Addr size)
    : PioDevice(p), pioAddr(p->pio_addr), pioSize(size),
      pioDelay(p->pio_latency)
{}

AddrRangeList
BasicPioDevice::getAddrRanges() const
{
    assert(pioSize != 0);
    AddrRangeList ranges;
    DPRINTF(AddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
    ranges.push_back(RangeSize(pioAddr, pioSize));
    return ranges;
}
<commit_msg>dev: Add response sanity checks in PioPort<commit_after>/*
 * Copyright (c) 2012 ARM Limited
 * All rights reserved.
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 *          Nathan Binkert
 */

#include "base/trace.hh"
#include "debug/AddrRanges.hh"
#include "dev/io_device.hh"
#include "sim/system.hh"

PioPort::PioPort(PioDevice *dev)
    : SimpleTimingPort(dev->name() + ".pio", dev), device(dev)
{
}

Tick
PioPort::recvAtomic(PacketPtr pkt)
{
    // @todo: We need to pay for this and not just zero it out
    pkt->firstWordDelay = pkt->lastWordDelay = 0;

    const Tick delay(pkt->isRead() ? device->read(pkt) : device->write(pkt));
    assert(pkt->isResponse() || pkt->isError());
    return delay;
}

AddrRangeList
PioPort::getAddrRanges() const
{
    return device->getAddrRanges();
}

PioDevice::PioDevice(const Params *p)
    : MemObject(p), sys(p->system), pioPort(this)
{}

PioDevice::~PioDevice()
{
}

void
PioDevice::init()
{
    if (!pioPort.isConnected())
        panic("Pio port of %s not connected to anything!", name());
    pioPort.sendRangeChange();
}

BaseSlavePort &
PioDevice::getSlavePort(const std::string &if_name, PortID idx)
{
    if (if_name == "pio") {
        return pioPort;
    }
    return MemObject::getSlavePort(if_name, idx);
}

unsigned int
PioDevice::drain(DrainManager *dm)
{
    unsigned int count;
    count = pioPort.drain(dm);
    if (count)
        setDrainState(Drainable::Draining);
    else
        setDrainState(Drainable::Drained);
    return count;
}

BasicPioDevice::BasicPioDevice(const Params *p, Addr size)
    : PioDevice(p), pioAddr(p->pio_addr), pioSize(size),
      pioDelay(p->pio_latency)
{}

AddrRangeList
BasicPioDevice::getAddrRanges() const
{
    assert(pioSize != 0);
    AddrRangeList ranges;
    DPRINTF(AddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
    ranges.push_back(RangeSize(pioAddr, pioSize));
    return ranges;
}
<|endoftext|>


<commit_before>#include "ros/ros.h"
#include "std_msgs/Float32.h"

float offset = 83;
float scalingFactor = 0.297; //feet per tick
float currentDepthRaw = 16.6;
float lastTicks = 83;

ros::Publisher depthPub;

void tickMeasureCallback(const std_msgs::Float32 msg) {
	lastTicks = msg.data;
	currentDepthRaw = (lastTicks-offset) * scalingFactor;
	std_msgs::Float32 outMsg;
	outMsg.data = currentDepthRaw;
	depthPub.publish(outMsg);
}

void calibrationCallback(const std_msgs::Float32 msg) {
	float depth = msg.data;
	if(depth == 0) {
		offset = lastTicks;
	} else {
		scalingFactor = depth / (lastTicks - offset);
	}
}

int main(int argc, char** argv) {
	ros::init(argc, argv, "DepthTranslator");
	ros::NodeHandle nh;
	ros::Subscriber curTicks = nh.subscribe("/Pressure_Data", 1, tickMeasureCallback);
	ros::Subscriber calibrationSub = nh.subscribe("/Calibrate_Depth", 1, calibrationCallback);
	depthPub = nh.advertise<std_msgs::Float32>("/Sub_Depth", 1);
	ros::spin();
}
<commit_msg>modified scaling factor for depth translators<commit_after>#include "ros/ros.h"
#include "std_msgs/Float32.h"

float offset = 83;
float scalingFactor = 0.177; //feet per tick
float currentDepthRaw = 16.6;
float lastTicks = 83;

ros::Publisher depthPub;

void tickMeasureCallback(const std_msgs::Float32 msg) {
	lastTicks = msg.data;
	currentDepthRaw = (lastTicks-offset) * scalingFactor;
	std_msgs::Float32 outMsg;
	outMsg.data = currentDepthRaw;
	depthPub.publish(outMsg);
}

void calibrationCallback(const std_msgs::Float32 msg) {
	float depth = msg.data;
	if(depth == 0) {
		offset = lastTicks;
	} else {
		scalingFactor = depth / (lastTicks - offset);
		printf("scalingFactor = %f\n", scalingFactor);
	}
}

int main(int argc, char** argv) {
	ros::init(argc, argv, "DepthTranslator");
	ros::NodeHandle nh;
	ros::Subscriber curTicks = nh.subscribe("/Pressure_Data", 1, tickMeasureCallback);
	ros::Subscriber calibrationSub = nh.subscribe("/Calibrate_Depth", 1, calibrationCallback);
	depthPub = nh.advertise<std_msgs::Float32>("/Sub_Depth", 1);
	ros::spin();
}
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2011, Willow Garage, Inc.
 *
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  $Id$
 *
 */

#ifndef PCL_FEATURES_IMPL_3DSC_HPP_
#define PCL_FEATURES_IMPL_3DSC_HPP_

#include <cmath>
#include <pcl/features/3dsc.h>
#include <pcl/common/utils.h>
#include <pcl/common/geometry.h>
#include <pcl/common/angles.h>

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> bool
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::initCompute ()
{
  if (!FeatureFromNormals<PointInT, PointNT, PointOutT>::initCompute ())
  {
    PCL_ERROR ("[pcl::%s::initCompute] Init failed.\n", getClassName ().c_str ());
    return (false);
  }

  if (search_radius_< min_radius_)
  {
    PCL_ERROR ("[pcl::%s::initCompute] search_radius_ must be GREATER than min_radius_.\n", getClassName ().c_str ());
    return (false);
  }

  // Update descriptor length
  descriptor_length_ = elevation_bins_ * azimuth_bins_ * radius_bins_;

  // Compute radial, elevation and azimuth divisions
  float azimuth_interval = (float) 360 / azimuth_bins_;
  float elevation_interval = (float) 180 / elevation_bins_;

  // Reallocate divisions and volume lut
  radii_interval_.clear ();
  phi_divisions_.clear ();
  theta_divisions_.clear ();
  volume_lut_.clear ();

  // Fills radii interval based on formula (1) in section 2.1 of Frome's paper
  radii_interval_.resize (radius_bins_ + 1);
  for (size_t j = 0; j < radius_bins_ + 1; j++)
    radii_interval_[j] = exp (log (min_radius_) + (((float) j / (radius_bins_)) * log (search_radius_ / min_radius_)));

  // Fill theta divisions of elevation
  theta_divisions_.resize (elevation_bins_ + 1);
  for (size_t k = 0; k < elevation_bins_ + 1; k++)
    theta_divisions_[k] = k * elevation_interval;

  // Fill phi didvisions of elevation
  phi_divisions_.resize (azimuth_bins_ + 1);
  for (size_t l = 0; l < azimuth_bins_ + 1; l++)
    phi_divisions_[l] = l * azimuth_interval;

  // LookUp Table that contains the volume of all the bins
  // "phi" term of the volume integral
  // "integr_phi" has always the same value so we compute it only one time
  float integr_phi  = pcl::deg2rad (phi_divisions_[1]) - pcl::deg2rad (phi_divisions_[0]);
  // exponential to compute the cube root using pow
  float e = 1.0 / 3.0;
  // Resize volume look up table
  volume_lut_.resize (radius_bins_ * elevation_bins_ * azimuth_bins_);
  // Fill volumes look up table
  for (size_t j = 0; j < radius_bins_; j++)
  {
    // "r" term of the volume integral
    float integr_r = (radii_interval_[j+1] * radii_interval_[j+1] * radii_interval_[j+1] / 3.0) - (radii_interval_[j] * radii_interval_[j] * radii_interval_[j] / 3.0);

    for (size_t k = 0; k < elevation_bins_; k++)
    {
      // "theta" term of the volume integral
      float integr_theta = cos (pcl::deg2rad (theta_divisions_[k])) - cos (pcl::deg2rad (theta_divisions_[k+1]));
      // Volume
      float V = integr_phi * integr_theta * integr_r;
      // Compute cube root of the computed volume commented for performance but left
      // here for clarity
      // float cbrt = pow(V, e);
      // cbrt = 1 / cbrt;

      for (size_t l = 0; l < azimuth_bins_; l++)
      {
        // Store in lut 1/cbrt
        //volume_lut_[ (l*elevation_bins_*radius_bins_) + k*radius_bins_ + j ] = cbrt;
        volume_lut_[(l*elevation_bins_*radius_bins_) + k*radius_bins_ + j] = 1.0 / pow (V, e);
      }
    }
  }
  return (true);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> void
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::computePoint (
    size_t index, const pcl::PointCloud<PointNT> &normals, float rf[9], std::vector<float> &desc)
{
  /// The RF is formed as this x_axis | y_axis | normal
  Eigen::Map<Eigen::Vector3f> x_axis (rf);
  Eigen::Map<Eigen::Vector3f> y_axis (rf + 3);
  Eigen::Map<Eigen::Vector3f> normal (rf + 6);

  /// Find every point within specified search_radius_
  std::vector<int> nn_indices;
  std::vector<float> nn_dists;
  const size_t neighb_cnt = searchForNeighbors ((*indices_)[index], search_radius_, nn_indices, nn_dists);
  float minDist = std::numeric_limits<float>::max();
  int minIndex = -1;
  for (size_t i = 0; i < nn_indices.size (); i++)
  {
	  if (nn_dists[i] < minDist)
	  {
      minDist = nn_dists[i];
      minIndex = nn_indices[i];
	  }
  }

  /// Get origin point
  Vector3fMapConst origin = input_->points[(*indices_)[index]].getVector3fMap ();
  /// Get origin normal
  /// Use pre-computed normals
  normal = normals[minIndex].getNormalVector3fMap ();

  /// Compute and store the RF direction
  x_axis[0] = rnd ();
  x_axis[1] = rnd ();
  x_axis[2] = rnd ();
  if (!pcl::utils::equal (normal[2], 0.0f))
    x_axis[2] = - (normal[0]*x_axis[0] + normal[1]*x_axis[1]) / normal[2];
  else if (!pcl::utils::equal (normal[1], 0.0f))
    x_axis[1] = - (normal[0]*x_axis[0] + normal[2]*x_axis[2]) / normal[1];
  else if (!pcl::utils::equal (normal[0], 0.0f))
    x_axis[0] = - (normal[1]*x_axis[1] + normal[2]*x_axis[2]) / normal[0];

  x_axis.normalize ();
  std::cerr << x_axis << std::endl;

  /// Check if the computed x axis is orthogonal to the normal
  assert (pcl::utils::equal (x_axis[0]*normal[0] + x_axis[1]*normal[1] + x_axis[2]*normal[2], 0.0f, 1E-6f));

  /// Store the 3rd frame vector
  y_axis = normal.cross (x_axis);
  /// For each point within radius
  for (size_t ne = 0; ne < neighb_cnt; ne++)
  {
    if (pcl::utils::equal (nn_dists[ne], 0.0f))
		  continue;
    /// Get neighbours coordinates
    Eigen::Vector3f neighbour = surface_->points[nn_indices[ne]].getVector3fMap ();

    /// ----- Compute current neighbour polar coordinates -----

    /// Get distance between the neighbour and the origin
    float r = sqrt (nn_dists[ne]);

    /// Project point into the tangent plane
    Eigen::Vector3f proj;
    pcl::geometry::project (neighbour, origin, normal, proj);
    proj-= origin;

    /// Normalize to compute the dot product
    proj.normalize ();

    /// Compute the angle between the projection and the x axis in the interval [0,360]
    Eigen::Vector3f cross = x_axis.cross (proj);
    float phi = rad2deg (std::atan2 (cross.norm (), x_axis.dot (proj)));
    phi = cross.dot (normal) < 0.f ? (360.0 - phi) : phi;
    /// Compute the angle between the neighbour and the z axis (normal) in the interval [0, 180]
    Eigen::Vector3f no = neighbour - origin;
    no.normalize ();
    float theta = normal.dot (no);
    theta = pcl::rad2deg (acos (std::min (1.0f, std::max (-1.0f, theta))));

    /// Bin (j, k, l)
    size_t j = 0;
    size_t k = 0;
    size_t l = 0;

    /// Compute the Bin(j, k, l) coordinates of current neighbour
    for (size_t rad = 1; rad < radius_bins_+1; rad++)
    {
      if (r <= radii_interval_[rad])
      {
        j = rad-1;
        break;
      }
    }

    for (size_t ang = 1; ang < elevation_bins_+1; ang++)
    {
      if (theta <= theta_divisions_[ang])
      {
        k = ang-1;
        break;
      }
    }

    for (size_t ang = 1; ang < azimuth_bins_+1; ang++)
    {
      if (phi <= phi_divisions_[ang])
      {
        l = ang-1;
        break;
      }
    }

    /// Local point density = number of points in a sphere of radius "point_density_radius_" around the current neighbour
    std::vector<int> neighbour_indices;
    std::vector<float> neighbour_didtances;
    float point_density = (float) searchForNeighbors (*surface_, nn_indices[ne], point_density_radius_, neighbour_indices, neighbour_didtances);
    /// point_density is always bigger than 0 because FindPointsWithinRadius returns at least the point itself
    float w = (1.0 / point_density) * volume_lut_[(l*elevation_bins_*radius_bins_) +
                                                  (k*radius_bins_) +
                                                  j];

    assert (w >= 0.0);
    if (w == std::numeric_limits<float>::infinity ())
      PCL_ERROR ("Shape Context Error INF!\n");
    if (w != w)
      PCL_ERROR ("Shape Context Error IND!\n");
    /// Accumulate w into correspondant Bin(j,k,l)
    desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] += w;

    assert (desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] >= 0);
  } // end for each neighbour

  // 3DSC does not define a repeatable local RF, we set it to zero to signal it to the user
  memset (rf, 0, sizeof (rf[0]) * 9);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> void
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::shiftAlongAzimuth (
    size_t block_size, std::vector<float>& desc)
{
  assert (desc.size () == descriptor_length_);
  // L rotations for each descriptor
  desc.resize (descriptor_length_ * azimuth_bins_);
  // Create L azimuth rotated descriptors from reference descriptor
  // The descriptor_length_ first ones are the same so start at 1
  for (size_t l = 1; l < azimuth_bins_; l++)
    for (size_t bin = 0; bin < descriptor_length_; bin++)
      desc[(l * descriptor_length_) + bin] = desc[(l*block_size + bin) % descriptor_length_];
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> void
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::computeFeature (PointCloudOut &output)
{
	for (size_t point_index = 0; point_index < indices_->size (); point_index++)
  {
    output[point_index].descriptor.resize (descriptor_length_);
    computePoint (point_index, *normals_, output[point_index].rf, output[point_index].descriptor);
  }
}

#define PCL_INSTANTIATE_ShapeContext3DEstimation(T,NT,OutT) template class PCL_EXPORTS pcl::ShapeContext3DEstimation<T,NT,OutT>;

#endif
<commit_msg>forgot to remove a cerr<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2011, Willow Garage, Inc.
 *
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  $Id$
 *
 */

#ifndef PCL_FEATURES_IMPL_3DSC_HPP_
#define PCL_FEATURES_IMPL_3DSC_HPP_

#include <cmath>
#include <pcl/features/3dsc.h>
#include <pcl/common/utils.h>
#include <pcl/common/geometry.h>
#include <pcl/common/angles.h>

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> bool
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::initCompute ()
{
  if (!FeatureFromNormals<PointInT, PointNT, PointOutT>::initCompute ())
  {
    PCL_ERROR ("[pcl::%s::initCompute] Init failed.\n", getClassName ().c_str ());
    return (false);
  }

  if (search_radius_< min_radius_)
  {
    PCL_ERROR ("[pcl::%s::initCompute] search_radius_ must be GREATER than min_radius_.\n", getClassName ().c_str ());
    return (false);
  }

  // Update descriptor length
  descriptor_length_ = elevation_bins_ * azimuth_bins_ * radius_bins_;

  // Compute radial, elevation and azimuth divisions
  float azimuth_interval = (float) 360 / azimuth_bins_;
  float elevation_interval = (float) 180 / elevation_bins_;

  // Reallocate divisions and volume lut
  radii_interval_.clear ();
  phi_divisions_.clear ();
  theta_divisions_.clear ();
  volume_lut_.clear ();

  // Fills radii interval based on formula (1) in section 2.1 of Frome's paper
  radii_interval_.resize (radius_bins_ + 1);
  for (size_t j = 0; j < radius_bins_ + 1; j++)
    radii_interval_[j] = exp (log (min_radius_) + (((float) j / (radius_bins_)) * log (search_radius_ / min_radius_)));

  // Fill theta divisions of elevation
  theta_divisions_.resize (elevation_bins_ + 1);
  for (size_t k = 0; k < elevation_bins_ + 1; k++)
    theta_divisions_[k] = k * elevation_interval;

  // Fill phi didvisions of elevation
  phi_divisions_.resize (azimuth_bins_ + 1);
  for (size_t l = 0; l < azimuth_bins_ + 1; l++)
    phi_divisions_[l] = l * azimuth_interval;

  // LookUp Table that contains the volume of all the bins
  // "phi" term of the volume integral
  // "integr_phi" has always the same value so we compute it only one time
  float integr_phi  = pcl::deg2rad (phi_divisions_[1]) - pcl::deg2rad (phi_divisions_[0]);
  // exponential to compute the cube root using pow
  float e = 1.0 / 3.0;
  // Resize volume look up table
  volume_lut_.resize (radius_bins_ * elevation_bins_ * azimuth_bins_);
  // Fill volumes look up table
  for (size_t j = 0; j < radius_bins_; j++)
  {
    // "r" term of the volume integral
    float integr_r = (radii_interval_[j+1] * radii_interval_[j+1] * radii_interval_[j+1] / 3.0) - (radii_interval_[j] * radii_interval_[j] * radii_interval_[j] / 3.0);

    for (size_t k = 0; k < elevation_bins_; k++)
    {
      // "theta" term of the volume integral
      float integr_theta = cos (pcl::deg2rad (theta_divisions_[k])) - cos (pcl::deg2rad (theta_divisions_[k+1]));
      // Volume
      float V = integr_phi * integr_theta * integr_r;
      // Compute cube root of the computed volume commented for performance but left
      // here for clarity
      // float cbrt = pow(V, e);
      // cbrt = 1 / cbrt;

      for (size_t l = 0; l < azimuth_bins_; l++)
      {
        // Store in lut 1/cbrt
        //volume_lut_[ (l*elevation_bins_*radius_bins_) + k*radius_bins_ + j ] = cbrt;
        volume_lut_[(l*elevation_bins_*radius_bins_) + k*radius_bins_ + j] = 1.0 / pow (V, e);
      }
    }
  }
  return (true);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> void
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::computePoint (
    size_t index, const pcl::PointCloud<PointNT> &normals, float rf[9], std::vector<float> &desc)
{
  /// The RF is formed as this x_axis | y_axis | normal
  Eigen::Map<Eigen::Vector3f> x_axis (rf);
  Eigen::Map<Eigen::Vector3f> y_axis (rf + 3);
  Eigen::Map<Eigen::Vector3f> normal (rf + 6);

  /// Find every point within specified search_radius_
  std::vector<int> nn_indices;
  std::vector<float> nn_dists;
  const size_t neighb_cnt = searchForNeighbors ((*indices_)[index], search_radius_, nn_indices, nn_dists);
  float minDist = std::numeric_limits<float>::max();
  int minIndex = -1;
  for (size_t i = 0; i < nn_indices.size (); i++)
  {
	  if (nn_dists[i] < minDist)
	  {
      minDist = nn_dists[i];
      minIndex = nn_indices[i];
	  }
  }

  /// Get origin point
  Vector3fMapConst origin = input_->points[(*indices_)[index]].getVector3fMap ();
  /// Get origin normal
  /// Use pre-computed normals
  normal = normals[minIndex].getNormalVector3fMap ();

  /// Compute and store the RF direction
  x_axis[0] = rnd ();
  x_axis[1] = rnd ();
  x_axis[2] = rnd ();
  if (!pcl::utils::equal (normal[2], 0.0f))
    x_axis[2] = - (normal[0]*x_axis[0] + normal[1]*x_axis[1]) / normal[2];
  else if (!pcl::utils::equal (normal[1], 0.0f))
    x_axis[1] = - (normal[0]*x_axis[0] + normal[2]*x_axis[2]) / normal[1];
  else if (!pcl::utils::equal (normal[0], 0.0f))
    x_axis[0] = - (normal[1]*x_axis[1] + normal[2]*x_axis[2]) / normal[0];

  x_axis.normalize ();

  /// Check if the computed x axis is orthogonal to the normal
  assert (pcl::utils::equal (x_axis[0]*normal[0] + x_axis[1]*normal[1] + x_axis[2]*normal[2], 0.0f, 1E-6f));

  /// Store the 3rd frame vector
  y_axis = normal.cross (x_axis);
  /// For each point within radius
  for (size_t ne = 0; ne < neighb_cnt; ne++)
  {
    if (pcl::utils::equal (nn_dists[ne], 0.0f))
		  continue;
    /// Get neighbours coordinates
    Eigen::Vector3f neighbour = surface_->points[nn_indices[ne]].getVector3fMap ();

    /// ----- Compute current neighbour polar coordinates -----

    /// Get distance between the neighbour and the origin
    float r = sqrt (nn_dists[ne]);

    /// Project point into the tangent plane
    Eigen::Vector3f proj;
    pcl::geometry::project (neighbour, origin, normal, proj);
    proj-= origin;

    /// Normalize to compute the dot product
    proj.normalize ();

    /// Compute the angle between the projection and the x axis in the interval [0,360]
    Eigen::Vector3f cross = x_axis.cross (proj);
    float phi = rad2deg (std::atan2 (cross.norm (), x_axis.dot (proj)));
    phi = cross.dot (normal) < 0.f ? (360.0 - phi) : phi;
    /// Compute the angle between the neighbour and the z axis (normal) in the interval [0, 180]
    Eigen::Vector3f no = neighbour - origin;
    no.normalize ();
    float theta = normal.dot (no);
    theta = pcl::rad2deg (acos (std::min (1.0f, std::max (-1.0f, theta))));

    /// Bin (j, k, l)
    size_t j = 0;
    size_t k = 0;
    size_t l = 0;

    /// Compute the Bin(j, k, l) coordinates of current neighbour
    for (size_t rad = 1; rad < radius_bins_+1; rad++)
    {
      if (r <= radii_interval_[rad])
      {
        j = rad-1;
        break;
      }
    }

    for (size_t ang = 1; ang < elevation_bins_+1; ang++)
    {
      if (theta <= theta_divisions_[ang])
      {
        k = ang-1;
        break;
      }
    }

    for (size_t ang = 1; ang < azimuth_bins_+1; ang++)
    {
      if (phi <= phi_divisions_[ang])
      {
        l = ang-1;
        break;
      }
    }

    /// Local point density = number of points in a sphere of radius "point_density_radius_" around the current neighbour
    std::vector<int> neighbour_indices;
    std::vector<float> neighbour_didtances;
    float point_density = (float) searchForNeighbors (*surface_, nn_indices[ne], point_density_radius_, neighbour_indices, neighbour_didtances);
    /// point_density is always bigger than 0 because FindPointsWithinRadius returns at least the point itself
    float w = (1.0 / point_density) * volume_lut_[(l*elevation_bins_*radius_bins_) +
                                                  (k*radius_bins_) +
                                                  j];

    assert (w >= 0.0);
    if (w == std::numeric_limits<float>::infinity ())
      PCL_ERROR ("Shape Context Error INF!\n");
    if (w != w)
      PCL_ERROR ("Shape Context Error IND!\n");
    /// Accumulate w into correspondant Bin(j,k,l)
    desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] += w;

    assert (desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] >= 0);
  } // end for each neighbour

  // 3DSC does not define a repeatable local RF, we set it to zero to signal it to the user
  memset (rf, 0, sizeof (rf[0]) * 9);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> void
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::shiftAlongAzimuth (
    size_t block_size, std::vector<float>& desc)
{
  assert (desc.size () == descriptor_length_);
  // L rotations for each descriptor
  desc.resize (descriptor_length_ * azimuth_bins_);
  // Create L azimuth rotated descriptors from reference descriptor
  // The descriptor_length_ first ones are the same so start at 1
  for (size_t l = 1; l < azimuth_bins_; l++)
    for (size_t bin = 0; bin < descriptor_length_; bin++)
      desc[(l * descriptor_length_) + bin] = desc[(l*block_size + bin) % descriptor_length_];
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointNT, typename PointOutT> void
pcl::ShapeContext3DEstimation<PointInT, PointNT, PointOutT>::computeFeature (PointCloudOut &output)
{
	for (size_t point_index = 0; point_index < indices_->size (); point_index++)
  {
    output[point_index].descriptor.resize (descriptor_length_);
    computePoint (point_index, *normals_, output[point_index].rf, output[point_index].descriptor);
  }
}

#define PCL_INSTANTIATE_ShapeContext3DEstimation(T,NT,OutT) template class PCL_EXPORTS pcl::ShapeContext3DEstimation<T,NT,OutT>;

#endif
<|endoftext|>


<commit_before>#include "ResourceGUI.h"
#include "utils/stringutils.h"
#include "utils/ioutils.h"
#include "IO/XmlWorld.h"

ResourceGUIBackend::ResourceGUIBackend(RobotWorld* world,ResourceManager* library)
  :WorldGUIBackend(world),resources(library)
{
}

inline bool LoadResources(const char* fn,ResourceTree& lib)
{
  size_t origsize = lib.topLevel.size();
  if(!lib.LoadFolder(fn)) {
    lib.TreeFromLibrary();
    fprintf(stderr,"Warning, couldn't load library %s\n",fn);
    return false;
  }
  else {
    printf("Loaded %d items from %s\n",lib.topLevel.size()-origsize,fn);
    return true;
  }
  return false;
}

inline bool LoadResources(TiXmlElement* e,ResourceTree& lib)
{
  size_t origsize = lib.topLevel.size();
  if(!lib.library.Load(e)) {
    fprintf(stderr,"Warning, couldn't load library from XML\n");
    lib.TreeFromLibrary();
    return false;
  }
  else {
    lib.TreeFromLibrary();
    printf("Loaded %d items from XML\n",lib.topLevel.size()-origsize);
    return true;
  }
  return false;
}

inline bool LoadItem(const char* fn,ResourceTree& lib)
{
  ResourceNodePtr r=lib.LoadFile(fn);
  if(r != NULL) {
    printf("Loaded %s as type %s\n",fn,r->Type());
    return true;
  }
  else {
    fprintf(stderr,"Couldn't load resource file %s\n",fn);
    return false;
  }
}

bool ResourceGUIBackend::LoadCommandLine(int argc,const char** argv)
{
  for(int i=1;i<argc;i++) {
    if(argv[i][0] == '-') {
      if(0==strcmp(argv[i],"-l")) {
	LoadResources(argv[i+1],*resources);
	i++;
      }
      else {
	printf("Unknown option %s",argv[i]);
	return 0;
      }
    }
    else {
      const char* ext=FileExtension(argv[i]);
      if(0==strcmp(ext,"xml")) {
	TiXmlDocument doc;
	if(!doc.LoadFile(argv[i])) {
	  printf("Error loading XML file %s\n",argv[i]);
	  return false;
	}
	if(0 == strcmp(doc.RootElement()->Value(),"world")) {
	  XmlWorld xmlWorld;
	  if(!xmlWorld.Load(doc.RootElement(),GetFilePath(argv[i]))) {
	    printf("Error loading world file %s\n",argv[i]);
	    return 0;
	  }
	  if(!xmlWorld.GetWorld(*world)) {
	    printf("Error loading world from %s\n",argv[i]);
	    return 0;
	  }
	}
	else if(0 == strcmp(doc.RootElement()->Value(),"resource_library")) {
	  LoadResources(doc.RootElement(),*resources);
	}
	else {
	  if(!LoadItem(argv[i],*resources))
	    return 0;
	}
      }
      else {
	//try loading into the world
	if(world->LoadElement(argv[i])>= 0) {
	  //loaded successfully
	}
	else {
	  //failed, now try resource library load
	  if(!LoadItem(argv[i],*resources))
	    return 0;
	}
      }
    }
  }
  return true;
}

ResourceNodePtr ResourceGUIBackend::Add(ResourcePtr r)
{
  if(resources == NULL) return NULL;
  ResourceNode* parent = (resources->selected != NULL ? resources->selected->parent : NULL);
  if(r->name.empty()) {
      stringstream ss;
      if(parent == NULL){
	ss<<r->Type()<<"["<<resources->topLevel.size()<<"]";
      }
      else{
	ss<<r->Type()<<"["<<parent->children.size()<<"]";
      }
      r->name = ss.str();
  }
  //last_added = resources->AddChildOfSelected(r);
  if(parent == NULL)
    last_added = resources->Add(r);
  else
    last_added = parent->AddChild(r);
  //SendCommand("new_resource", r->Type()+string(" ")+r->name);
  SendCommand("new_resource", last_added->Identifier());
  return last_added;
}

ResourceNodePtr ResourceGUIBackend::Add(const string& name,const string& type)
{
  if(resources->library.knownTypes.count(type)==0) return NULL;
  ResourcePtr r=resources->library.knownTypes[type][0]->Make();
  r->name = name;
  return Add(r);
}

void ResourceGUIBackend::SaveCur(const string& file)
{
  resources->SaveSelected(file);
}

bool ResourceGUIBackend::LoadNew(const string& file)
{
  return resources->LoadFile(file) != NULL;
}

void ResourceGUIBackend::SaveAll(const string& path)
{
  resources->SaveFolder(path);
}

bool ResourceGUIBackend::LoadAll(const string& path)
{
  if(!resources->LoadFolder(path)) {
    fprintf(stderr,"Error loading resources from %s\n",path.c_str());
    return false;
    }
    else {
    fprintf(stderr,"Loaded all resources from %s\n",path.c_str());
    return true;
  }
  return false;
}

void ResourceGUIBackend::SetLastActive()
{
  if(last_added == NULL) {
    resources->selected = NULL;
  }
  else {
    resources->selected = last_added;
  }
}

void ResourceGUIBackend::SetActive(const string& identifier)
{
  resources->Select(identifier);
}

void ResourceGUIBackend::SetPathTime(double time){
  viewResource.pathTime = time;
}

bool ResourceGUIBackend::OnCommand(const string& cmd,const string& args)
{
  if(cmd == "set_resource") {
    resources->Select(args);
    return true;
  }
  else if(cmd == "get_resource") {
    stringstream ss;
    ss<<resources->selected->Identifier();
    SendCommand("current_resource",ss.str());
    return true;
  }
  else if(cmd == "set_resource_name") {
    if(resources->selected) resources->selected->resource->name = args;
    printf("Updating name to %s\n",args.c_str());
  }
  else if(cmd == "delete_resource") {
    resources->DeleteSelected();
  }
  else if(cmd == "add_resource") {
    string type,name;
    stringstream ss(args);
    if(!SafeInputString(ss,type)) {
      cout<<"Error reading resource type from args \""<<args<<"\""<<endl;
      return true;
    }
    if(!SafeInputString(ss,name)) {
      cout<<"Error reading resource name from args \""<<args<<"\""<<endl;
      return true;
    }
    last_added = Add(name,type);
  }
  else if(cmd == "load_resource") {
    if(LoadNew(args)) {
      SendCommand("new_resource",last_added->Identifier());
    }
  }
  else if(cmd == "save_resource") {
    SaveCur(args);
  }
  else if(cmd == "load_resource_dir") {
    if(LoadAll(args)) {
      SendCommand("refresh_resources","");
    }
  }
  else if(cmd == "save_resource_dir") {
    SaveAll(args);
  }
  else if(cmd == "convert") {
    ResourcePtr r=CurrentResource();
    ResourcePtr res = CastResource(r,args.c_str());
    if(!res) {
      fprintf(stderr,"Conversion failed\n");
      return true;
    }
    res->name = r->name;
    Add(res);
    SetLastActive();
  }
  else if(cmd == "extract") {
    ResourcePtr r=CurrentResource();
    if(!r) return true;
    vector<ResourcePtr> res = ExtractResources(r,args.c_str());
    for(size_t i=0;i<res.size();i++){
      Add(res[i]);
    }
    if(!res.empty())
      SetLastActive();
  }
  else if(cmd == "set_path_time") {
    stringstream ss(args);
    double time;
    ss>>time;
    SetPathTime(time);
  }
  else {
    return WorldGUIBackend::OnCommand(cmd,args);
  }
  return true;
}

ResourcePtr ResourceGUIBackend::CurrentResource()
{
    if(resources && resources->selected)
        return resources->selected->resource;
    else return 0;
}

void ResourceGUIBackend::RenderCurResource()
{
  /*
    ResourcePtr current = CurrentResource();
    if(current)
    viewResource.DrawGL(current);
  */
  //separate open?
  viewResource.SetRobot(world->robots[0].robot);
  if(resources && resources->selected)
    viewResource.DrawGL(resources->selected->resource);
}
<commit_msg>Using new convenience methods for loading worlds<commit_after>#include "ResourceGUI.h"
#include "IO/XmlWorld.h"
#include "utils/stringutils.h"
#include "utils/ioutils.h"

ResourceGUIBackend::ResourceGUIBackend(RobotWorld* world,ResourceManager* library)
  :WorldGUIBackend(world),resources(library)
{
}

inline bool LoadResources(const char* fn,ResourceTree& lib)
{
  size_t origsize = lib.topLevel.size();
  if(!lib.LoadFolder(fn)) {
    lib.TreeFromLibrary();
    fprintf(stderr,"Warning, couldn't load library %s\n",fn);
    return false;
  }
  else {
    printf("Loaded %d items from %s\n",lib.topLevel.size()-origsize,fn);
    return true;
  }
  return false;
}

inline bool LoadResources(TiXmlElement* e,ResourceTree& lib)
{
  size_t origsize = lib.topLevel.size();
  if(!lib.library.Load(e)) {
    fprintf(stderr,"Warning, couldn't load library from XML\n");
    lib.TreeFromLibrary();
    return false;
  }
  else {
    lib.TreeFromLibrary();
    printf("Loaded %d items from XML\n",lib.topLevel.size()-origsize);
    return true;
  }
  return false;
}

inline bool LoadItem(const char* fn,ResourceTree& lib)
{
  ResourceNodePtr r=lib.LoadFile(fn);
  if(r != NULL) {
    printf("Loaded %s as type %s\n",fn,r->Type());
    return true;
  }
  else {
    fprintf(stderr,"Couldn't load resource file %s\n",fn);
    return false;
  }
}

bool ResourceGUIBackend::LoadCommandLine(int argc,const char** argv)
{
  for(int i=1;i<argc;i++) {
    if(argv[i][0] == '-') {
      if(0==strcmp(argv[i],"-l")) {
	LoadResources(argv[i+1],*resources);
	i++;
      }
      else {
	printf("Unknown option %s",argv[i]);
	return 0;
      }
    }
    else {
      const char* ext=FileExtension(argv[i]);
      if(0==strcmp(ext,"xml")) {
	TiXmlDocument doc;
	if(!doc.LoadFile(argv[i])) {
	  printf("Error loading XML file %s\n",argv[i]);
	  return false;
	}
	if(0 == strcmp(doc.RootElement()->Value(),"world")) {
	  XmlWorld xmlWorld;
	  if(!xmlWorld.Load(doc.RootElement(),GetFilePath(argv[i]))) {
	    printf("Error loading world file %s\n",argv[i]);
	    return 0;
	  }
	  if(!xmlWorld.GetWorld(*world)) {
	    printf("Error loading world from %s\n",argv[i]);
	    return 0;
	  }
	}
	else if(0 == strcmp(doc.RootElement()->Value(),"resource_library")) {
	  LoadResources(doc.RootElement(),*resources);
	}
	else {
	  if(!LoadItem(argv[i],*resources))
	    return 0;
	}
      }
      else {
	//try loading into the world
	const char* ext = FileExtension(argv[i]);
	if(world->CanLoadElementExt(ext) && world->LoadElement(argv[i])>= 0) {
	  //loaded successfully
	}
	else {
	  //failed, now try resource library load
	  if(!LoadItem(argv[i],*resources))
	    return 0;
	}
      }
    }
  }
  return true;
}

ResourceNodePtr ResourceGUIBackend::Add(ResourcePtr r)
{
  if(resources == NULL) return NULL;
  ResourceNode* parent = (resources->selected != NULL ? resources->selected->parent : NULL);
  if(r->name.empty()) {
      stringstream ss;
      if(parent == NULL){
	ss<<r->Type()<<"["<<resources->topLevel.size()<<"]";
      }
      else{
	ss<<r->Type()<<"["<<parent->children.size()<<"]";
      }
      r->name = ss.str();
  }
  //last_added = resources->AddChildOfSelected(r);
  if(parent == NULL)
    last_added = resources->Add(r);
  else
    last_added = parent->AddChild(r);
  //SendCommand("new_resource", r->Type()+string(" ")+r->name);
  SendCommand("new_resource", last_added->Identifier());
  return last_added;
}

ResourceNodePtr ResourceGUIBackend::Add(const string& name,const string& type)
{
  if(resources->library.knownTypes.count(type)==0) return NULL;
  ResourcePtr r=resources->library.knownTypes[type][0]->Make();
  r->name = name;
  return Add(r);
}

void ResourceGUIBackend::SaveCur(const string& file)
{
  resources->SaveSelected(file);
}

bool ResourceGUIBackend::LoadNew(const string& file)
{
  return resources->LoadFile(file) != NULL;
}

void ResourceGUIBackend::SaveAll(const string& path)
{
  resources->SaveFolder(path);
}

bool ResourceGUIBackend::LoadAll(const string& path)
{
  if(!resources->LoadFolder(path)) {
    fprintf(stderr,"Error loading resources from %s\n",path.c_str());
    return false;
    }
    else {
    fprintf(stderr,"Loaded all resources from %s\n",path.c_str());
    return true;
  }
  return false;
}

void ResourceGUIBackend::SetLastActive()
{
  if(last_added == NULL) {
    resources->selected = NULL;
  }
  else {
    resources->selected = last_added;
  }
}

void ResourceGUIBackend::SetActive(const string& identifier)
{
  resources->Select(identifier);
}

void ResourceGUIBackend::SetPathTime(double time){
  viewResource.pathTime = time;
}

bool ResourceGUIBackend::OnCommand(const string& cmd,const string& args)
{
  if(cmd == "set_resource") {
    resources->Select(args);
    return true;
  }
  else if(cmd == "get_resource") {
    stringstream ss;
    ss<<resources->selected->Identifier();
    SendCommand("current_resource",ss.str());
    return true;
  }
  else if(cmd == "set_resource_name") {
    if(resources->selected) resources->selected->resource->name = args;
    printf("Updating name to %s\n",args.c_str());
  }
  else if(cmd == "delete_resource") {
    resources->DeleteSelected();
  }
  else if(cmd == "add_resource") {
    string type,name;
    stringstream ss(args);
    if(!SafeInputString(ss,type)) {
      cout<<"Error reading resource type from args \""<<args<<"\""<<endl;
      return true;
    }
    if(!SafeInputString(ss,name)) {
      cout<<"Error reading resource name from args \""<<args<<"\""<<endl;
      return true;
    }
    last_added = Add(name,type);
  }
  else if(cmd == "load_resource") {
    if(LoadNew(args)) {
      SendCommand("new_resource",last_added->Identifier());
    }
  }
  else if(cmd == "save_resource") {
    SaveCur(args);
  }
  else if(cmd == "load_resource_dir") {
    if(LoadAll(args)) {
      SendCommand("refresh_resources","");
    }
  }
  else if(cmd == "save_resource_dir") {
    SaveAll(args);
  }
  else if(cmd == "convert") {
    ResourcePtr r=CurrentResource();
    ResourcePtr res = CastResource(r,args.c_str());
    if(!res) {
      fprintf(stderr,"Conversion failed\n");
      return true;
    }
    res->name = r->name;
    Add(res);
    SetLastActive();
  }
  else if(cmd == "extract") {
    ResourcePtr r=CurrentResource();
    if(!r) return true;
    vector<ResourcePtr> res = ExtractResources(r,args.c_str());
    for(size_t i=0;i<res.size();i++){
      Add(res[i]);
    }
    if(!res.empty())
      SetLastActive();
  }
  else if(cmd == "set_path_time") {
    stringstream ss(args);
    double time;
    ss>>time;
    SetPathTime(time);
  }
  else {
    return WorldGUIBackend::OnCommand(cmd,args);
  }
  return true;
}

ResourcePtr ResourceGUIBackend::CurrentResource()
{
    if(resources && resources->selected)
        return resources->selected->resource;
    else return 0;
}

void ResourceGUIBackend::RenderCurResource()
{
  /*
    ResourcePtr current = CurrentResource();
    if(current)
    viewResource.DrawGL(current);
  */
  //separate open?
  viewResource.SetRobot(world->robots[0].robot);
  if(resources && resources->selected)
    viewResource.DrawGL(resources->selected->resource);
}
<|endoftext|>