<commit_before>/*
*  Author(s):
*  - Cedric Gestes <gestes@aldebaran-robotics.com>
*  - Herve Cuche <hcuche@aldebaran-robotics.com>
*
*  Copyright (C) 2012 Aldebaran Robotics
*/

#include <qimessaging/session.hpp>
#include <qimessaging/datastream.hpp>
#include <qimessaging/transport_socket.hpp>
#include <qimessaging/object.hpp>
#include <qimessaging/service_info.hpp>
#include "src/remoteobject_p.hpp"
#include "src/network_thread.hpp"
#include "src/session_p.hpp"

namespace qi {

  SessionPrivate::SessionPrivate(qi::Session *session)
    : _serviceSocket(new qi::TransportSocket()),
      _networkThread(new qi::NetworkThread()),
      _self(session)
  {
  }

  SessionPrivate::~SessionPrivate() {
    delete _networkThread;
    delete _serviceSocket;
  }

  bool SessionPrivate::connect(const qi::Url &serviceDirectoryURL)
  {
    return _serviceSocket->connect(_self, serviceDirectoryURL);
  }

  std::vector<ServiceInfo> SessionPrivate::services()
  {
    std::vector<ServiceInfo> result;

    qi::Message msg;
    msg.setType(qi::Message::Type_Call);
    msg.setService(qi::Message::Service_ServiceDirectory);
    msg.setPath(qi::Message::Path_Main);
    msg.setFunction(qi::Message::ServiceDirectoryFunction_Services);

    _serviceSocket->send(msg);

    if (!_serviceSocket->waitForId(msg.id()))
      return result;
    qi::Message ans;
    _serviceSocket->read(msg.id(), &ans);

    qi::DataStream d(ans.buffer());
    d >> result;

    return result;
  }

  qi::TransportSocket* SessionPrivate::serviceSocket(const std::string &name,
                                                     unsigned int      *idx,
                                                     qi::Url::Protocol  type)
  {
    qi::Message    msg;
    qi::Buffer     buf;
    qi::DataStream dr(buf);
    msg.setBuffer(buf);
    dr << name;
    msg.setType(qi::Message::Type_Call);
    msg.setService(qi::Message::Service_ServiceDirectory);
    msg.setPath(qi::Message::Path_Main);
    msg.setFunction(qi::Message::ServiceDirectoryFunction_Service);
    _serviceSocket->send(msg);

    if (!_serviceSocket->waitForId(msg.id()))
      return 0;
    qi::Message ans;
    _serviceSocket->read(msg.id(), &ans);

    qi::ServiceInfo si;
    qi::DataStream d(ans.buffer());
    d >> si;
    *idx = si.serviceId();

    for (std::vector<std::string>::const_iterator it = si.endpoints().begin();
         it != si.endpoints().end();
         ++it)
    {
      qi::Url url(*it);

      if (type == qi::Url::Protocol_Any ||
          type == url.protocol())
      {
        qi::TransportSocket* ts = NULL;
        ts = new qi::TransportSocket();
        ts->connect(_self, url);
        ts->setCallbacks(this);
        ts->waitForConnected();
        return ts;
      }
    }

    return 0;
  }

  qi::Object* SessionPrivate::service(const std::string &service,
                                      qi::Url::Protocol  type)
  {
    qi::Object          *obj;
    unsigned int serviceId = 0;
    qi::TransportSocket *ts = serviceSocket(service, &serviceId, type);

    if (ts == 0)
    {
      return 0;
    }

    qi::Message msg;
    msg.setType(qi::Message::Type_Call);
    msg.setService(serviceId);
    msg.setPath(qi::Message::Path_Main);
    msg.setFunction(qi::Message::Function_MetaObject);

    ts->send(msg);
    ts->waitForId(msg.id());

    qi::Message ret;
    ts->read(msg.id(), &ret);

    qi::MetaObject *mo = new qi::MetaObject;

    qi::DataStream ds(ret.buffer());

    ds >> *mo;

    qi::RemoteObject *robj = new qi::RemoteObject(ts, serviceId, mo);
    obj = robj;
    return obj;
  }

  // ###### Session

  Session::Session()
    : _p(new SessionPrivate(this))
  {
  }

  Session::~Session()
  {
    delete _p;
  }

  bool Session::connect(const qi::Url &serviceDirectoryURL)
  {
    return _p->connect(serviceDirectoryURL);
  }

  bool Session::disconnect()
  {
    _p->_serviceSocket->disconnect();
    return true;
  }

  bool Session::waitForConnected(int msecs)
  {
    return _p->_serviceSocket->waitForConnected(msecs);
  }

  bool Session::waitForDisconnected(int msecs)
  {
    return _p->_serviceSocket->waitForDisconnected(msecs);
  }

  qi::Future< std::vector<ServiceInfo> > Session::services()
  {
    qi::Promise< std::vector<ServiceInfo> > promise;
    promise.setValue(_p->services());
    return promise.future();
  }

  qi::Future< qi::TransportSocket* > Session::serviceSocket(const std::string &name,
                                              unsigned int      *idx,
                                              qi::Url::Protocol  type)
  {
    qi::Promise< qi::TransportSocket* > promise;
    promise.setValue(_p->serviceSocket(name, idx, type));
    return promise.future();
  }

  qi::Future< qi::Object* > Session::service(const std::string &service,
                                             qi::Url::Protocol  type)
  {
    qi::Promise< qi::Object * > promise;
    promise.setValue(_p->service(service, type));
    return promise.future();
  }

  bool Session::join()
  {
    _p->_networkThread->join();
    return true;
  }

}
<commit_msg>session: log<commit_after>/*
*  Author(s):
*  - Cedric Gestes <gestes@aldebaran-robotics.com>
*  - Herve Cuche <hcuche@aldebaran-robotics.com>
*
*  Copyright (C) 2012 Aldebaran Robotics
*/

#include <qimessaging/session.hpp>
#include <qimessaging/datastream.hpp>
#include <qimessaging/transport_socket.hpp>
#include <qimessaging/object.hpp>
#include <qimessaging/service_info.hpp>
#include "src/remoteobject_p.hpp"
#include "src/network_thread.hpp"
#include "src/session_p.hpp"

namespace qi {

  SessionPrivate::SessionPrivate(qi::Session *session)
    : _serviceSocket(new qi::TransportSocket()),
      _networkThread(new qi::NetworkThread()),
      _self(session)
  {
  }

  SessionPrivate::~SessionPrivate() {
    delete _networkThread;
    delete _serviceSocket;
  }

  bool SessionPrivate::connect(const qi::Url &serviceDirectoryURL)
  {
    return _serviceSocket->connect(_self, serviceDirectoryURL);
  }

  std::vector<ServiceInfo> SessionPrivate::services()
  {
    std::vector<ServiceInfo> result;

    qi::Message msg;
    msg.setType(qi::Message::Type_Call);
    msg.setService(qi::Message::Service_ServiceDirectory);
    msg.setPath(qi::Message::Path_Main);
    msg.setFunction(qi::Message::ServiceDirectoryFunction_Services);

    _serviceSocket->send(msg);

    if (!_serviceSocket->waitForId(msg.id()))
      return result;
    qi::Message ans;
    _serviceSocket->read(msg.id(), &ans);

    qi::DataStream d(ans.buffer());
    d >> result;

    return result;
  }

  qi::TransportSocket* SessionPrivate::serviceSocket(const std::string &name,
                                                     unsigned int      *idx,
                                                     qi::Url::Protocol  type)
  {
    qi::Message    msg;
    qi::Buffer     buf;
    qi::DataStream dr(buf);
    msg.setBuffer(buf);
    dr << name;
    msg.setType(qi::Message::Type_Call);
    msg.setService(qi::Message::Service_ServiceDirectory);
    msg.setPath(qi::Message::Path_Main);
    msg.setFunction(qi::Message::ServiceDirectoryFunction_Service);
    _serviceSocket->send(msg);

    if (!_serviceSocket->waitForId(msg.id()))
      return 0;
    qi::Message ans;
    _serviceSocket->read(msg.id(), &ans);

    qi::ServiceInfo si;
    qi::DataStream d(ans.buffer());
    d >> si;
    *idx = si.serviceId();

    for (std::vector<std::string>::const_iterator it = si.endpoints().begin();
         it != si.endpoints().end();
         ++it)
    {
      qi::Url url(*it);

      if (type == qi::Url::Protocol_Any ||
          type == url.protocol())
      {
        qi::TransportSocket* ts = NULL;
        ts = new qi::TransportSocket();
        ts->connect(_self, url);
        ts->setCallbacks(this);
        ts->waitForConnected();
        return ts;
      }
    }

    return 0;
  }

  qi::Object* SessionPrivate::service(const std::string &service,
                                      qi::Url::Protocol  type)
  {
    qi::Object          *obj;
    unsigned int serviceId = 0;
    qi::TransportSocket *ts = serviceSocket(service, &serviceId, type);

    if (ts == 0)
    {
      qiLogError("qi::Session") << "service not found: " << service;
      return 0;
    }

    qi::Message msg;
    msg.setType(qi::Message::Type_Call);
    msg.setService(serviceId);
    msg.setPath(qi::Message::Path_Main);
    msg.setFunction(qi::Message::Function_MetaObject);

    ts->send(msg);
    ts->waitForId(msg.id());

    qi::Message ret;
    ts->read(msg.id(), &ret);

    qi::MetaObject *mo = new qi::MetaObject;

    qi::DataStream ds(ret.buffer());

    ds >> *mo;

    qi::RemoteObject *robj = new qi::RemoteObject(ts, serviceId, mo);
    obj = robj;
    return obj;
  }

  // ###### Session

  Session::Session()
    : _p(new SessionPrivate(this))
  {
  }

  Session::~Session()
  {
    delete _p;
  }

  bool Session::connect(const qi::Url &serviceDirectoryURL)
  {
    return _p->connect(serviceDirectoryURL);
  }

  bool Session::disconnect()
  {
    _p->_serviceSocket->disconnect();
    return true;
  }

  bool Session::waitForConnected(int msecs)
  {
    return _p->_serviceSocket->waitForConnected(msecs);
  }

  bool Session::waitForDisconnected(int msecs)
  {
    return _p->_serviceSocket->waitForDisconnected(msecs);
  }

  qi::Future< std::vector<ServiceInfo> > Session::services()
  {
    qi::Promise< std::vector<ServiceInfo> > promise;
    promise.setValue(_p->services());
    return promise.future();
  }

  qi::Future< qi::TransportSocket* > Session::serviceSocket(const std::string &name,
                                              unsigned int      *idx,
                                              qi::Url::Protocol  type)
  {
    qi::Promise< qi::TransportSocket* > promise;
    promise.setValue(_p->serviceSocket(name, idx, type));
    return promise.future();
  }

  qi::Future< qi::Object* > Session::service(const std::string &service,
                                             qi::Url::Protocol  type)
  {
    qi::Promise< qi::Object * > promise;
    promise.setValue(_p->service(service, type));
    return promise.future();
  }

  bool Session::join()
  {
    _p->_networkThread->join();
    return true;
  }

}
<|endoftext|>


<commit_before>#include <bts/blockchain/asset.hpp>
#include <bts/blockchain/config.hpp>
#include <bts/blockchain/exceptions.hpp>

#include <fc/reflect/variant.hpp>
#include <fc/uint128.hpp>
#include <fc/real128.hpp>

#include <sstream>

namespace bts { namespace blockchain {

  price operator *( const price& l, const price& r )
  { try {
     FC_ASSERT( l.quote_asset_id == r.quote_asset_id );
     FC_ASSERT( l.base_asset_id  == r.base_asset_id );

     // infinity times zero is undefined / exception
     // infinity times anything else is infinity
     if( l.is_infinite() )
     {
         if( r.ratio == fc::uint128_t(0) )
             FC_THROW_EXCEPTION( price_multiplication_undefined, "price multiplication undefined (0 * inf)" );
         return l;
     }
     if( r.is_infinite() )
     {
         if( l.ratio == fc::uint128_t(0) )
             FC_THROW_EXCEPTION( price_multiplication_undefined, "price multiplication undefined (inf * 0)" );
         return r;
     }

     // zero times anything is zero (infinity is already handled above)
     if( l.ratio == fc::uint128_t(0) )
         return l;
     if( r.ratio == fc::uint128_t(0) )
         return r;

     fc::bigint product = l.ratio;
     product *= r.ratio;
     product /= FC_REAL128_PRECISION;

     // if the quotient is zero, it means there was an underflow
     //    (i.e. the result is nonzero but too small to represent)
     if( product == fc::bigint() )
         FC_THROW_EXCEPTION( price_multiplication_underflow, "price multiplication underflow" );

     static fc::bigint bi_infinity = price::infinite();

     // if the quotient is infinity or bigger, then we have a finite
     //    result that is too big to represent (overflow)
     if( product >= bi_infinity )
         FC_THROW_EXCEPTION( price_multiplication_overflow, "price multiplication overflow" );

     // NB we throw away the low bits, thus this function always rounds down

     price result( fc::uint128( product ), l.quote_asset_id, l.base_asset_id );
     return result;
  } FC_CAPTURE_AND_RETHROW( (l)(r) ) }

  asset::operator std::string()const
  {
     return fc::to_string(amount);
  }

  asset& asset::operator += ( const asset& o )
  { try {
     FC_ASSERT( this->asset_id == o.asset_id, "", ("*this",*this)("o",o) );

     if (((o.amount > 0) && (amount > (INT64_MAX - o.amount))) ||
         ((o.amount < 0) && (amount < (INT64_MIN - o.amount))))
     {
       FC_THROW_EXCEPTION( addition_overflow, "asset addition overflow  ${a} + ${b}",
                            ("a", *this)("b",o) );
     }

     amount += o.amount;
     return *this;
  } FC_CAPTURE_AND_RETHROW( (*this)(o) ) }

  asset& asset::operator -= ( const asset& o )
  {
     FC_ASSERT( asset_id == o.asset_id );

     if ((o.amount > 0 && amount < INT64_MIN + o.amount) ||
         (o.amount < 0 && amount > INT64_MAX + o.amount))
     {
        FC_THROW_EXCEPTION( subtraction_overflow, "asset subtraction underflow  ${a} - ${b}",
                            ("a", *this)("b",o) );
     }

     amount -= o.amount;
     return *this;
  }

  const fc::uint128& price::one()
  {
     static fc::uint128_t o = fc::uint128(1,0);
     return o;
  }

  const fc::uint128& price::infinite()
  {
      static fc::uint128 i(-1);
      return i;
  }

  bool price::is_infinite() const
  {
      static fc::uint128 infinity = infinite();
      return (this->ratio == infinity);
  }

  price::price( const std::string& s )
  { try {
     int pos = set_ratio_from_string( s );
     std::stringstream ss( s.substr( pos ) );

     char div;
     ss >> quote_asset_id.value >> div >> base_asset_id.value;

     FC_ASSERT( quote_asset_id > base_asset_id, "${quote} > ${base}", ("quote",quote_asset_id)("base",base_asset_id) );
  } FC_RETHROW_EXCEPTIONS( warn, "" ) }

  int price::set_ratio_from_string( const std::string& ratio_str )
  {
    const char* c = ratio_str.c_str();
    int digit = *c - '0';
    if (digit >= 0 && digit <= 9)
    {
      uint64_t int_part = digit;
      ++c;
      digit = *c - '0';
      while (digit >= 0 && digit <= 9)
      {
        int_part = int_part * 10 + digit;
        ++c;
        digit = *c - '0';
      }
      ratio = fc::uint128(int_part) * FC_REAL128_PRECISION;
    }
    else
    {
      // if the string doesn't look like "123.45" or ".45", this code isn't designed to parse it correctly
      // in particular, we don't try to handle leading whitespace or '+'/'-' indicators at the beginning
      FC_ASSERT( (*c) == '.' );
      ratio = fc::uint128();
    }

    if (*c == '.')
    {
      c++;
      digit = *c - '0';
      if (digit >= 0 && digit <= 9)
      {
        int64_t frac_part = digit;
        int64_t frac_magnitude = 10;
        ++c;
        digit = *c - '0';
        while (digit >= 0 && digit <= 9)
        {
          frac_part = frac_part * 10 + digit;
          frac_magnitude *= 10;
          ++c;
          digit = *c - '0';
        }
        ratio += fc::uint128(frac_part) * (FC_REAL128_PRECISION / frac_magnitude);
      }
    }

    return c - ratio_str.c_str();
  }

  std::string price::ratio_string()const
  {
     std::string full_int = std::string( ratio );
     std::stringstream ss;
     ss <<std::string( ratio / FC_REAL128_PRECISION );
     ss << '.';
     ss << std::string( (ratio % FC_REAL128_PRECISION) + FC_REAL128_PRECISION ).substr(1);

     auto number = ss.str();
     while(  number.back() == '0' ) number.pop_back();

     return number;
  }

  price::operator std::string()const
  { try {
     auto number = ratio_string();

     number += ' ' + fc::to_string(int64_t(quote_asset_id.value));
     number +=  '/' + fc::to_string(int64_t(base_asset_id.value));

     return number;
  } FC_RETHROW_EXCEPTIONS( warn, "" )}

  /**
   *  A price will reorder the asset types such that the
   *  asset type with the lower enum value is always the
   *  denominator.  Therefore  bts/usd and  usd/bts will
   *  always result in a price measured in usd/bts because
   *  asset::bts <  asset::usd.
   */
  price operator / ( const asset& a, const asset& b )
  {
    try
    {
        if( a.asset_id == b.asset_id )
           FC_CAPTURE_AND_THROW( asset_divide_by_self );

        price p;
        auto l = a; auto r = b;
        if( l.asset_id < r.asset_id ) { std::swap(l,r); }
        //ilog( "${a} / ${b}", ("a",l)("b",r) );

        if( r.amount == 0 )
           FC_CAPTURE_AND_THROW( asset_divide_by_zero, (r) );

        p.base_asset_id = r.asset_id;
        p.quote_asset_id = l.asset_id;

        fc::bigint bl = l.amount;
        fc::bigint br = r.amount;
        fc::bigint result = (bl * fc::bigint(FC_REAL128_PRECISION)) / br;

        p.ratio = result;
        return p;
    } FC_RETHROW_EXCEPTIONS( warn, "${a} / ${b}", ("a",a)("b",b) );
  }

  asset operator / ( const asset& a, const price& p )
  {
        return a * p;
  }

  /**
   *  Assuming a.type is either the numerator.type or denominator.type in
   *  the price equation, return the number of the other asset type that
   *  could be exchanged at price p.
   *
   *  ie:  p = 3 usd/bts & a = 4 bts then result = 12 usd
   *  ie:  p = 3 usd/bts & a = 4 usd then result = 1.333 bts
   */
  asset operator * ( const asset& a, const price& p )
  {
    try {
        if( a.asset_id == p.base_asset_id )
        {
            fc::bigint ba( a.amount ); // 64.64
            fc::bigint r( p.ratio ); // 64.64

            auto amnt = ba * r; //  128.128
            amnt /= FC_REAL128_PRECISION; // 128.64
            auto lg2 = amnt.log2();
            if( lg2 >= 128 )
            {
               FC_THROW_EXCEPTION( addition_overflow, "overflow ${a} * ${p}", ("a",a)("p",p) );
            }

            asset rtn;
            rtn.amount = amnt.to_int64();
            rtn.asset_id = p.quote_asset_id;

            //ilog( "${a} * ${p} => ${rtn}", ("a", a)("p",p )("rtn",rtn) );
            return rtn;
        }
        else if( a.asset_id == p.quote_asset_id )
        {
            fc::bigint amt( a.amount ); // 64.64
            amt *= FC_REAL128_PRECISION; //<<= 64;  // 64.128
            fc::bigint pri( p.ratio ); // 64.64

            auto result = amt / pri;  // 64.64
//            ilog( "amt: ${amt} / ${pri}", ("amt",string(amt))("pri",string(pri) ) );
 //           ilog( "${r}", ("r",string(result) ) );

            auto lg2 = result.log2();
            if( lg2 >= 128 )
            {
             //  wlog( "." );
               FC_THROW_EXCEPTION( addition_overflow,
                                    "overflow ${a} / ${p} = ${r} lg2 = ${l}",
                                    ("a",a)("p",p)("r", std::string(result)  )("l",lg2) );
            }
          //  result += 5000000000; // TODO: evaluate this rounding factor..
            asset r;
            r.amount    = result.to_int64();
            r.asset_id  = p.base_asset_id;
           // ilog( "r.amount = ${r}", ("r",r.amount) );
           // ilog( "${a} * ${p} => ${rtn}", ("a", a)("p",p )("rtn",r) );
            return r;
        }
        FC_THROW_EXCEPTION( asset_type_mismatch, "type mismatch multiplying asset ${a} by price ${p}",
                                            ("a",a)("p",p) );
    } FC_RETHROW_EXCEPTIONS( warn, "type mismatch multiplying asset ${a} by price ${p}",
                                        ("a",a)("p",p) );

  }

} } // bts::blockchain

namespace fc
{

/*
   void to_variant( const bts::blockchain::asset& var,  variant& vo )
   {
     vo = std::string(var);
   }
   void from_variant( const variant& var,  bts::blockchain::asset& vo )
   {
     vo = bts::blockchain::asset( var.as_string() );
   }
*/
   void to_variant( const bts::blockchain::price& var,  variant& vo )
   {
      fc::mutable_variant_object obj("ratio",var.ratio_string());
      obj( "quote_asset_id", var.quote_asset_id );
      obj( "base_asset_id", var.base_asset_id );
      vo = std::move(obj);
   }
   void from_variant( const variant& var,  bts::blockchain::price& vo )
   {
     auto obj = var.get_object();
     vo.set_ratio_from_string( obj["ratio"].as_string() );
     from_variant( obj["quote_asset_id"], vo.quote_asset_id );
     from_variant( obj["base_asset_id"], vo.base_asset_id );
   }

} // fc
<commit_msg>Attempt to fix crash when marekt order is submitted with too much precision (not yet tested).<commit_after>#include <bts/blockchain/asset.hpp>
#include <bts/blockchain/config.hpp>
#include <bts/blockchain/exceptions.hpp>

#include <fc/reflect/variant.hpp>
#include <fc/uint128.hpp>
#include <fc/real128.hpp>

#include <sstream>

namespace bts { namespace blockchain {

  price operator *( const price& l, const price& r )
  { try {
     FC_ASSERT( l.quote_asset_id == r.quote_asset_id );
     FC_ASSERT( l.base_asset_id  == r.base_asset_id );

     // infinity times zero is undefined / exception
     // infinity times anything else is infinity
     if( l.is_infinite() )
     {
         if( r.ratio == fc::uint128_t(0) )
             FC_THROW_EXCEPTION( price_multiplication_undefined, "price multiplication undefined (0 * inf)" );
         return l;
     }
     if( r.is_infinite() )
     {
         if( l.ratio == fc::uint128_t(0) )
             FC_THROW_EXCEPTION( price_multiplication_undefined, "price multiplication undefined (inf * 0)" );
         return r;
     }

     // zero times anything is zero (infinity is already handled above)
     if( l.ratio == fc::uint128_t(0) )
         return l;
     if( r.ratio == fc::uint128_t(0) )
         return r;

     fc::bigint product = l.ratio;
     product *= r.ratio;
     product /= FC_REAL128_PRECISION;

     // if the quotient is zero, it means there was an underflow
     //    (i.e. the result is nonzero but too small to represent)
     if( product == fc::bigint() )
         FC_THROW_EXCEPTION( price_multiplication_underflow, "price multiplication underflow" );

     static fc::bigint bi_infinity = price::infinite();

     // if the quotient is infinity or bigger, then we have a finite
     //    result that is too big to represent (overflow)
     if( product >= bi_infinity )
         FC_THROW_EXCEPTION( price_multiplication_overflow, "price multiplication overflow" );

     // NB we throw away the low bits, thus this function always rounds down

     price result( fc::uint128( product ), l.quote_asset_id, l.base_asset_id );
     return result;
  } FC_CAPTURE_AND_RETHROW( (l)(r) ) }

  asset::operator std::string()const
  {
     return fc::to_string(amount);
  }

  asset& asset::operator += ( const asset& o )
  { try {
     FC_ASSERT( this->asset_id == o.asset_id, "", ("*this",*this)("o",o) );

     if (((o.amount > 0) && (amount > (INT64_MAX - o.amount))) ||
         ((o.amount < 0) && (amount < (INT64_MIN - o.amount))))
     {
       FC_THROW_EXCEPTION( addition_overflow, "asset addition overflow  ${a} + ${b}",
                            ("a", *this)("b",o) );
     }

     amount += o.amount;
     return *this;
  } FC_CAPTURE_AND_RETHROW( (*this)(o) ) }

  asset& asset::operator -= ( const asset& o )
  {
     FC_ASSERT( asset_id == o.asset_id );

     if ((o.amount > 0 && amount < INT64_MIN + o.amount) ||
         (o.amount < 0 && amount > INT64_MAX + o.amount))
     {
        FC_THROW_EXCEPTION( subtraction_overflow, "asset subtraction underflow  ${a} - ${b}",
                            ("a", *this)("b",o) );
     }

     amount -= o.amount;
     return *this;
  }

  const fc::uint128& price::one()
  {
     static fc::uint128_t o = fc::uint128(1,0);
     return o;
  }

  const fc::uint128& price::infinite()
  {
      static fc::uint128 i(-1);
      return i;
  }

  bool price::is_infinite() const
  {
      static fc::uint128 infinity = infinite();
      return (this->ratio == infinity);
  }

  price::price( const std::string& s )
  { try {
     int pos = set_ratio_from_string( s );
     std::stringstream ss( s.substr( pos ) );

     char div;
     ss >> quote_asset_id.value >> div >> base_asset_id.value;

     FC_ASSERT( quote_asset_id > base_asset_id, "${quote} > ${base}", ("quote",quote_asset_id)("base",base_asset_id) );
  } FC_RETHROW_EXCEPTIONS( warn, "" ) }

  int price::set_ratio_from_string( const std::string& ratio_str )
  {
    const char* c = ratio_str.c_str();
    int digit = *c - '0';
    if (digit >= 0 && digit <= 9)
    {
      uint64_t int_part = digit;
      ++c;
      digit = *c - '0';
      while (digit >= 0 && digit <= 9)
      {
        //if number is greater max supported value
        FC_ASSERT(int_part <= (UINT64_MAX - digit) / 10, "Number too big to store as price.");
        int_part = int_part * 10 + digit;
        ++c;
        digit = *c - '0';
      }
      ratio = fc::uint128(int_part) * FC_REAL128_PRECISION;
    }
    else
    {
      // if the string doesn't look like "123.45" or ".45", this code isn't designed to parse it correctly
      // in particular, we don't try to handle leading whitespace or '+'/'-' indicators at the beginning
      FC_ASSERT( (*c) == '.' );
      ratio = fc::uint128();
    }

    if (*c == '.')
    {
      c++;
      digit = *c - '0';
      if (digit >= 0 && digit <= 9)
      {
        int64_t frac_part = digit;
        int64_t frac_magnitude = 10;
        ++c;
        digit = *c - '0';
        while (digit >= 0 && digit <= 9)
        {
          //if we've reached the limits of precision, ignore remaining digits
          if (frac_magnitude > FC_REAL128_PRECISION / 10)
          {
            do
            {
              ++c;
              digit = *c - '0';
            }
            while (digit >= 0 && digit <= 9);
            break;
          }
          frac_magnitude *= 10;
          frac_part = frac_part * 10 + digit;
          ++c;
          digit = *c - '0';
        }
        ratio += fc::uint128(frac_part) * (FC_REAL128_PRECISION / frac_magnitude);
        //skip over remaining digits since they are too precise
      }
    }

    return int(c - ratio_str.c_str()); //return end point of digits
  }

  std::string price::ratio_string()const
  {
     std::string full_int = std::string( ratio );
     std::stringstream ss;
     ss <<std::string( ratio / FC_REAL128_PRECISION );
     ss << '.';
     ss << std::string( (ratio % FC_REAL128_PRECISION) + FC_REAL128_PRECISION ).substr(1);

     auto number = ss.str();
     while(  number.back() == '0' ) number.pop_back();

     return number;
  }

  price::operator std::string()const
  { try {
     auto number = ratio_string();

     number += ' ' + fc::to_string(int64_t(quote_asset_id.value));
     number +=  '/' + fc::to_string(int64_t(base_asset_id.value));

     return number;
  } FC_RETHROW_EXCEPTIONS( warn, "" )}

  /**
   *  A price will reorder the asset types such that the
   *  asset type with the lower enum value is always the
   *  denominator.  Therefore  bts/usd and  usd/bts will
   *  always result in a price measured in usd/bts because
   *  asset::bts <  asset::usd.
   */
  price operator / ( const asset& a, const asset& b )
  {
    try
    {
        if( a.asset_id == b.asset_id )
           FC_CAPTURE_AND_THROW( asset_divide_by_self );

        price p;
        auto l = a; auto r = b;
        if( l.asset_id < r.asset_id ) { std::swap(l,r); }
        //ilog( "${a} / ${b}", ("a",l)("b",r) );

        if( r.amount == 0 )
           FC_CAPTURE_AND_THROW( asset_divide_by_zero, (r) );

        p.base_asset_id = r.asset_id;
        p.quote_asset_id = l.asset_id;

        fc::bigint bl = l.amount;
        fc::bigint br = r.amount;
        fc::bigint result = (bl * fc::bigint(FC_REAL128_PRECISION)) / br;

        p.ratio = result;
        return p;
    } FC_RETHROW_EXCEPTIONS( warn, "${a} / ${b}", ("a",a)("b",b) );
  }

  asset operator / ( const asset& a, const price& p )
  {
        return a * p;
  }

  /**
   *  Assuming a.type is either the numerator.type or denominator.type in
   *  the price equation, return the number of the other asset type that
   *  could be exchanged at price p.
   *
   *  ie:  p = 3 usd/bts & a = 4 bts then result = 12 usd
   *  ie:  p = 3 usd/bts & a = 4 usd then result = 1.333 bts
   */
  asset operator * ( const asset& a, const price& p )
  {
    try {
        if( a.asset_id == p.base_asset_id )
        {
            fc::bigint ba( a.amount ); // 64.64
            fc::bigint r( p.ratio ); // 64.64

            auto amnt = ba * r; //  128.128
            amnt /= FC_REAL128_PRECISION; // 128.64
            auto lg2 = amnt.log2();
            if( lg2 >= 128 )
            {
               FC_THROW_EXCEPTION( addition_overflow, "overflow ${a} * ${p}", ("a",a)("p",p) );
            }

            asset rtn;
            rtn.amount = amnt.to_int64();
            rtn.asset_id = p.quote_asset_id;

            //ilog( "${a} * ${p} => ${rtn}", ("a", a)("p",p )("rtn",rtn) );
            return rtn;
        }
        else if( a.asset_id == p.quote_asset_id )
        {
            fc::bigint amt( a.amount ); // 64.64
            amt *= FC_REAL128_PRECISION; //<<= 64;  // 64.128
            fc::bigint pri( p.ratio ); // 64.64

            auto result = amt / pri;  // 64.64
//            ilog( "amt: ${amt} / ${pri}", ("amt",string(amt))("pri",string(pri) ) );
 //           ilog( "${r}", ("r",string(result) ) );

            auto lg2 = result.log2();
            if( lg2 >= 128 )
            {
             //  wlog( "." );
               FC_THROW_EXCEPTION( addition_overflow,
                                    "overflow ${a} / ${p} = ${r} lg2 = ${l}",
                                    ("a",a)("p",p)("r", std::string(result)  )("l",lg2) );
            }
          //  result += 5000000000; // TODO: evaluate this rounding factor..
            asset r;
            r.amount    = result.to_int64();
            r.asset_id  = p.base_asset_id;
           // ilog( "r.amount = ${r}", ("r",r.amount) );
           // ilog( "${a} * ${p} => ${rtn}", ("a", a)("p",p )("rtn",r) );
            return r;
        }
        FC_THROW_EXCEPTION( asset_type_mismatch, "type mismatch multiplying asset ${a} by price ${p}",
                                            ("a",a)("p",p) );
    } FC_RETHROW_EXCEPTIONS( warn, "type mismatch multiplying asset ${a} by price ${p}",
                                        ("a",a)("p",p) );

  }

} } // bts::blockchain

namespace fc
{

/*
   void to_variant( const bts::blockchain::asset& var,  variant& vo )
   {
     vo = std::string(var);
   }
   void from_variant( const variant& var,  bts::blockchain::asset& vo )
   {
     vo = bts::blockchain::asset( var.as_string() );
   }
*/
   void to_variant( const bts::blockchain::price& var,  variant& vo )
   {
      fc::mutable_variant_object obj("ratio",var.ratio_string());
      obj( "quote_asset_id", var.quote_asset_id );
      obj( "base_asset_id", var.base_asset_id );
      vo = std::move(obj);
   }
   void from_variant( const variant& var,  bts::blockchain::price& vo )
   {
     auto obj = var.get_object();
     vo.set_ratio_from_string( obj["ratio"].as_string() );
     from_variant( obj["quote_asset_id"], vo.quote_asset_id );
     from_variant( obj["base_asset_id"], vo.base_asset_id );
   }

} // fc
<|endoftext|>


<commit_before>#include "views.h"

using namespace std;

namespace rviews {

void login(JsonValue * message, ServerHandler * handler) {
    JsonDict * dictMessage = JDICT(message);

    if (dictMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON dict");
    }

    bool success = getBool(dictMessage, "success");
    if (success) {
        emit handler->getWindow()->loginSuccess();
    }
    else {
        emit handler->getWindow()->loginFailure(getInt(dictMessage, "code"));
    }
}

void signup(JsonValue * message, ServerHandler * handler) {
    JsonDict * dictMessage = JDICT(message);

    if (dictMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON dict");
    }

    bool success = getBool(dictMessage, "success");
    if (success) {
        emit handler->getWindow()->registerSuccess();
    }
    else {
        emit handler->getWindow()->registerFailure(getInt(dictMessage, "code"));
    }
}

void userlist(JsonValue * message, ServerHandler * handler) {
    JsonList * listMessage = JLIST(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    vector<string> * ulist = new vector<string>;
    for (int i = 0; i < listMessage->size(); i++) {
        ulist->push_back(getString(listMessage, i));
    }
    emit handler->getWindow()->userList(ulist);
}

void playerlist(JsonValue * message, ServerHandler * handler) {
    JsonList * listMessage = JLIST(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    vector<NonFieldPlayer *> * plist = new vector<NonFieldPlayer *>;
    for (int i = 0; i < listMessage->size(); i++) {
        plist->push_back(new NonFieldPlayer((*listMessage)[i]));
    }
    emit handler->getWindow()->playerList(plist);
}

void startMatch(JsonValue * message, ServerHandler * handler) {
    JsonDict * listMessage = JDICT(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    Match * match = new Match(listMessage);
    emit handler->getWindow()->startMatch(match);
}

void updateMatch(JsonValue * message, ServerHandler * handler) {
    JsonDict * listMessage = JDICT(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    Match * match = new Match(listMessage);
    emit handler->getWindow()->updateMatch(match);
}

}
<commit_msg>Get isGuest from json in startMatch.<commit_after>#include "views.h"

using namespace std;

namespace rviews {

void login(JsonValue * message, ServerHandler * handler) {
    JsonDict * dictMessage = JDICT(message);

    if (dictMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON dict");
    }

    bool success = getBool(dictMessage, "success");
    if (success) {
        emit handler->getWindow()->loginSuccess();
    }
    else {
        emit handler->getWindow()->loginFailure(getInt(dictMessage, "code"));
    }
}

void signup(JsonValue * message, ServerHandler * handler) {
    JsonDict * dictMessage = JDICT(message);

    if (dictMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON dict");
    }

    bool success = getBool(dictMessage, "success");
    if (success) {
        emit handler->getWindow()->registerSuccess();
    }
    else {
        emit handler->getWindow()->registerFailure(getInt(dictMessage, "code"));
    }
}

void userlist(JsonValue * message, ServerHandler * handler) {
    JsonList * listMessage = JLIST(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    vector<string> * ulist = new vector<string>;
    for (int i = 0; i < listMessage->size(); i++) {
        ulist->push_back(getString(listMessage, i));
    }
    emit handler->getWindow()->userList(ulist);
}

void playerlist(JsonValue * message, ServerHandler * handler) {
    JsonList * listMessage = JLIST(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    vector<NonFieldPlayer *> * plist = new vector<NonFieldPlayer *>;
    for (int i = 0; i < listMessage->size(); i++) {
        plist->push_back(new NonFieldPlayer((*listMessage)[i]));
    }
    emit handler->getWindow()->playerList(plist);
}

void startMatch(JsonValue * message, ServerHandler * handler) {
    JsonDict * listMessage = JDICT(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    bool isGuest = getBool(listMessage, "guest");

    Match * match = new Match(listMessage);
    emit handler->getWindow()->startMatch(match, isGuest);
}

void updateMatch(JsonValue * message, ServerHandler * handler) {
    JsonDict * listMessage = JDICT(message);

    if (listMessage == NULL) {
        throw BadRequest("Malformatted request. Need a JSON list");
    }

    Match * match = new Match(listMessage);
    emit handler->getWindow()->updateMatch(match);
}

}
<|endoftext|>


<commit_before>/**
 * blocked.hpp -
 * @author: Jonathan Beard
 * @version: Sun Jun 29 14:06:10 2014
 *
 * Copyright 2014 Jonathan Beard
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef BLOCKED_HPP
#define BLOCKED_HPP  1
#include <cstdint>
#include <cassert>

/**
 * FIXME move thos code to the defs.hpp file
 * useful for other things, no point in duplicating
 * elsewhere
 */
#ifdef _MSC_VER
#    if (_MSC_VER >= 1800)
#        define __alignas_is_defined 1
#    endif
#    if (_MSC_VER >= 1900)
#        define __alignof_is_defined 1
#    endif
#else
#    ifndef __APPLE__
#    include <cstdalign>   // __alignas/of_is_defined directly from the implementation
#    endif
#endif

/**
 * should be included, but just in case there are some
 * compilers with only experimental C++11 support still
 * running around, check macro..turns out it's #ifdef out
 * on GNU G++ so checking __cplusplus flag as indicated
 * by https://goo.gl/JD4Gng
 */
#if ( __alignas_is_defined == 1 ) || ( __cplusplus >= 201103L )
#    define ALIGN(X) alignas(X)
#else
#    pragma message("C++11 alignas unsupported :( Falling back to compiler attributes")
#    ifdef __GNUG__
#        define ALIGN(X) __attribute__ ((aligned(X)))
#    elif defined(_MSC_VER)
#        define ALIGN(X) __declspec(align(X))
#    else
#        error Unknown compiler, unknown alignment attribute!
#    endif
#endif

#if ( __alignas_is_defined == 1 ) || ( __cplusplus >= 201103L )
#    define ALIGNOF(X) alignof(x)
#else
#    pragma message("C++11 alignof unsupported :( Falling back to compiler attributes")
#    ifdef __GNUG__
#        define ALIGNOF(X) __alignof__ (X)
#    elif defined(_MSC_VER)
#        define ALIGNOF(X) __alignof(X)
#    else
#        error Unknown compiler, unknown alignment attribute!
#    endif
#endif

/**
 * FIXME...should probably align these to cache line then
 * zero extend pad for producer/consumer.
 */
struct ALIGN(64) Blocked
{
    using value_type = std::uint32_t;
    using whole_type = std::uint64_t;

    static_assert( sizeof( value_type ) * 2 == sizeof( whole_type ),
                   "Error, the whole type must be double the size of the half type" );
    Blocked() = default;

    Blocked( const Blocked &other ) : all( other.all ){}

    Blocked& operator += ( const Blocked &rhs )
    {
       if( ! rhs.bec.blocked )
       {
          (this)->bec.count += rhs.bec.count;
       }
       return( *this );
    }
    struct blocked_and_counter
    {
       value_type   blocked;
       value_type    count;
    };

    union
    {
        blocked_and_counter bec;
        whole_type          all = 0;
    };

    char pad[ L1D_CACHE_LINE_SIZE - sizeof( whole_type ) ];
}

;

#endif /* END BLOCKED_HPP */
<commit_msg>moved aligncode to its own file<commit_after>/**
 * blocked.hpp -
 * @author: Jonathan Beard
 * @version: Sun Jun 29 14:06:10 2014
 *
 * Copyright 2014 Jonathan Beard
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef BLOCKED_HPP
#define BLOCKED_HPP  1
#include <cstdint>
#include <cassert>
#include "internaldefs.hpp"

/**
 * FIXME...should probably align these to cache line then
 * zero extend pad for producer/consumer.
 */
struct ALIGN(64) Blocked
{
    using value_type = std::uint32_t;
    using whole_type = std::uint64_t;

    static_assert( sizeof( value_type ) * 2 == sizeof( whole_type ),
                   "Error, the whole type must be double the size of the half type" );
    Blocked() = default;

    Blocked( const Blocked &other ) : all( other.all ){}

    Blocked& operator += ( const Blocked &rhs )
    {
       if( ! rhs.bec.blocked )
       {
          (this)->bec.count += rhs.bec.count;
       }
       return( *this );
    }
    struct blocked_and_counter
    {
       value_type   blocked;
       value_type    count;
    };

    union
    {
        blocked_and_counter bec;
        whole_type          all = 0;
    };

    char pad[ L1D_CACHE_LINE_SIZE - sizeof( whole_type ) ];
}

;

#endif /* END BLOCKED_HPP */
<|endoftext|>


<commit_before>//=============================================================================================================
/**
 * @file     icp.cpp
 * @author   Ruben Dörfel <doerfelruben@aol.com>
 * @since    0.1.0
 * @date     July, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. 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 MNE-CPP authors 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.
 *
 *
 * @brief    ICP class definition.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "icp.h"
#include <iostream>

#include "fiff/fiff_coord_trans.h"
#include "mne/mne_project_to_surface.h"

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QSharedPointer>
#include <QDebug>

//=============================================================================================================
// EIGEN INCLUDES
//=============================================================================================================

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#include <Eigen/Geometry>

//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace MNELIB;
using namespace RTPROCESSINGLIB;
using namespace Eigen;
using namespace FIFFLIB;

//=============================================================================================================
// DEFINE GLOBAL METHODS
//=============================================================================================================

//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

ICP::ICP()
{
}

//=============================================================================================================

bool RTPROCESSINGLIB::icp(const MNEProjectToSurface::SPtr mneSurfacePoints,
                          const Eigen::MatrixXf& matDstPoint,
                          FiffCoordTrans& transFromTo,
                          const int iMaxIter,
                          const float fTol,
                          const VectorXf& vecWeitgths)
/**
 * Follow notation of P.J. Besl and N.D. McKay, A Method for
 * Registration of 3-D Shapes, IEEE Trans. Patt. Anal. Machine Intell., 14,
 * 239 - 255, 1992.
 */
{
    // Initialization
    int iNP = matDstPoint.rows();               // The number of points
    float fMSEPrev,fMSE = 0.0;                  // The mean square error
    float fScale = 1.0;
    float bScale = true;
    MatrixXf matP0 = matDstPoint;               // Initial Set of points
    MatrixXf matPk = matP0;                     // Transformed Set of points
    MatrixXf matYk(matPk.rows(),matPk.cols());  // Iterative losest points on the surface
    MatrixXf matDiff = matYk;
    VectorXf vecSE(matDiff.rows());
    Matrix4f matTrans;                          // the transformation matrix
    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    // Initial transformation - apply inverse because we are computing in Model space
    FiffCoordTrans transToFrom = transFromTo;
    transToFrom.invert_transform();
    matPk = transToFrom.apply_trans(matPk);

    // Icp algorithm:
    for(int iIter = 0; iIter < iMaxIter; ++iIter) {

        // Step a: compute the closest point on the surface; eq 29
        if(!mneSurfacePoints->mne_find_closest_on_surface(matPk, iNP, matYk, vecNearest, vecDist)) {
            qWarning() << "RTPROCESSINGLIB::icp: mne_find_closest_on_surface was not sucessfull.";
            return false;
        }

        // Step b: compute the registration; eq 30
        if(!fitMatched(matP0, matYk, matTrans, fScale, bScale, vecWeitgths)) {
            qWarning() << "RTPROCESSINGLIB::icp: point cloud registration not succesfull";
        }

        // Step c: apply registration
        transToFrom.trans = matTrans;
        matPk = transToFrom.apply_trans(matP0);

        // step d: compute mean-square-error and terminate if below fTol
        fMSE = vecDist.sum() / iNP;
        if(std::fabs(fMSE - fMSEPrev) < fTol) {
            transToFrom.invert_transform();
            transFromTo = transToFrom;
            qInfo() << "RTPROCESSINGLIB::icp: ICP was succesfull and exceeded after " << iIter << " Iterations with MSE: " << fMSE << ".";
            return true;
        }
        fMSEPrev = fMSE;
        qInfo() << "RTPROCESSINGLIB::icp: ICP iteration " << iIter << " with MSE: " << fMSE << ".";

    }

    qWarning() << "RTPROCESSINGLIB::icp: ICP was not succesfull and exceeded after " << iMaxIter << " Iterations with MSE: " << fMSE << ".";
    return false;
}

//=============================================================================================================

bool RTPROCESSINGLIB::fitMatched(const MatrixXf& matSrcPoint,
                                 const MatrixXf& matDstPoint,
                                 Eigen::Matrix4f& matTrans,
                                 float fScale,
                                 const bool bScale,
                                 const VectorXf& vecWeitgths)
/**
 * Follow notation of P.J. Besl and N.D. McKay, A Method for
 * Registration of 3-D Shapes, IEEE Trans. Patt. Anal. Machine Intell., 14,
 * 239 - 255, 1992.
 *
 * The code is further adapted from MNE Python function _fitMatched_points(...).
 */
{
    // init values
    MatrixXf matP = matSrcPoint;
    MatrixXf matX = matDstPoint;
    VectorXf vecW = vecWeitgths;
    VectorXf vecMuP;                                // column wise mean - center of mass
    VectorXf vecMuX;                                // column wise mean - center of mass
    MatrixXf matDot;
    MatrixXf matSigmaPX;                            // cross-covariance
    MatrixXf matAij;                                // Anti-Symmetric matrix
    Vector3f vecDelta;                              // column vector, elements of matAij
    Matrix4f matQ = Matrix4f::Identity(4,4);
    Matrix3f matScale = Matrix3f::Identity(3,3);    // scaling matrix
    Matrix3f matRot = Matrix3f::Identity(3,3);
    Vector3f vecTrans;
    float fTrace = 0.0;
    fScale = 1.0;

    // test size of point clouds
    if(matSrcPoint.size() != matDstPoint.size()) {
        qWarning() << "RTPROCESSINGLIB::fitMatched: Point clouds do not match.";
        return false;
    }

    // get center of mass
    if(vecWeitgths.isZero()) {
        vecMuP = matP.colwise().mean(); // eq 23
        vecMuX = matX.colwise().mean();
        matDot = matP.transpose() * matX;
        matDot = matDot / matP.rows();
    } else {
        vecW = vecWeitgths / vecWeitgths.sum();
        vecMuP = vecW.transpose() * matP;
        vecMuX = vecW.transpose() * matX;

        MatrixXf matXWeighted = matX;
        for(int i = 0; i < (vecW.size()); ++i) {
            matXWeighted.row(i) = matXWeighted.row(i) * vecW(i);
        }
        matDot = matP.transpose() * (matXWeighted);
    }

    // get cross-covariance
    matSigmaPX = matDot - (vecMuP * vecMuX.transpose());  // eq 24
    matAij = matSigmaPX - matSigmaPX.transpose();
    vecDelta(0) = matAij(1,2); vecDelta(1) = matAij(2,0); vecDelta(2) = matAij(0,1);
    fTrace = matSigmaPX.trace();
    matQ(0,0) = fTrace; // eq 25
    matQ.block(0,1,1,3) = vecDelta.transpose();
    matQ.block(1,0,3,1) = vecDelta;
    matQ.block(1,1,3,3) = matSigmaPX + matSigmaPX.transpose() - fTrace * MatrixXf::Identity(3,3);

    // unit eigenvector coresponding to maximum eigenvalue of matQ is selected as optimal rotation quaterions q0,q1,q2,q3
    SelfAdjointEigenSolver<MatrixXf> es(matQ);
    Vector4f vecEigVec = es.eigenvectors().col(matQ.cols()-1);  // only take last Eigen-Vector since this corresponds to the maximum Eigenvalue

    // quatRot(w,x,y,z)
    Quaternionf quatRot(vecEigVec(0),vecEigVec(1),vecEigVec(2),vecEigVec(3));
    quatRot.normalize();
    matRot = quatRot.matrix();

    // get scaling factor and matrix
    if(bScale) {
        MatrixXf matDevX = matX.rowwise() - vecMuX.transpose();
        MatrixXf matDevP = matP.rowwise() - vecMuP.transpose();
        matDevX = matDevX.cwiseProduct(matDevX);
        matDevP = matDevP.cwiseProduct(matDevP);

        if(!vecWeitgths.isZero()) {
            for(int i = 0; i < (vecW.size()); ++i) {
                matDevX.row(i) = matDevX.row(i) * vecW(i);
                matDevP.row(i) = matDevP.row(i) * vecW(i);
            }
        }
        // get scaling factor and set scaling matrix
        fScale = std::sqrt(matDevX.sum() / matDevP.sum());
        matScale *= fScale;
    }

    // get translation and Rotation
    vecTrans = vecMuX - fScale * matRot * vecMuP;
    matRot *= matScale;

    matTrans.block<3,3>(0,0) = matRot;
    matTrans.block<3,1>(0,3) = vecTrans;
    matTrans(3,3) = 1.0f;
    return true;
}

//=========================================================================================================

bool RTPROCESSINGLIB::discardOutliers(const QSharedPointer<MNELIB::MNEProjectToSurface> mneSurfacePoints,
                                      const MatrixXf& matDstPoint,
                                      const FiffCoordTrans& transFromTo,
                                      VectorXi& vecTake,
                                      MatrixXf& matTakePoint,
                                      const float fMaxDist)
{
    // Initialization
    int iNP = matDstPoint.rows();               // The number of points
    MatrixXf matP = matDstPoint;                // Initial Set of points
    MatrixXf matYk(matDstPoint.rows(),matDstPoint.cols());  // Iterative losest points on the surface
    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    // Initial transformation - apply inverse because we are computing in Model space
    FiffCoordTrans transToFrom = transFromTo;
    transToFrom.invert_transform();
    matP = transToFrom.apply_trans(matP);

    int iDiscarded = 0;

    // discard outliers if necessary
    if(fMaxDist > 0.0) {
        if(!mneSurfacePoints->mne_find_closest_on_surface(matP, iNP, matYk, vecNearest, vecDist)) {
            qWarning() << "RTPROCESSINGLIB::icp: mne_find_closest_on_surface was not sucessfull.";
            return false;
        }

        for(int i = 0; i < vecDist.size(); ++i) {
            if(std::fabs(vecDist(i)) < fMaxDist) {
                vecTake.conservativeResize(vecTake.size()+1);
                vecTake(vecTake.size()-1) = i;
                matTakePoint.conservativeResize(matTakePoint.rows()+1,3);
                matTakePoint.row(matTakePoint.rows()-1) = matDstPoint.row(i);
            } else {
                iDiscarded++;
            }
        }
    }
    qInfo() << "RTPROCESSINGLIB::discardOutliers: " << iDiscarded << "digitizers discarded.";
}
<commit_msg>Maint: use correct implementation of RMSE<commit_after>//=============================================================================================================
/**
 * @file     icp.cpp
 * @author   Ruben Dörfel <doerfelruben@aol.com>
 * @since    0.1.0
 * @date     July, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. 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 MNE-CPP authors 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.
 *
 *
 * @brief    ICP class definition.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "icp.h"
#include <iostream>

#include "fiff/fiff_coord_trans.h"
#include "mne/mne_project_to_surface.h"

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QSharedPointer>
#include <QDebug>

//=============================================================================================================
// EIGEN INCLUDES
//=============================================================================================================

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#include <Eigen/Geometry>

//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace MNELIB;
using namespace RTPROCESSINGLIB;
using namespace Eigen;
using namespace FIFFLIB;

//=============================================================================================================
// DEFINE GLOBAL METHODS
//=============================================================================================================

//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

ICP::ICP()
{
}

//=============================================================================================================

bool RTPROCESSINGLIB::icp(const MNEProjectToSurface::SPtr mneSurfacePoints,
                          const Eigen::MatrixXf& matDstPoint,
                          FiffCoordTrans& transFromTo,
                          const int iMaxIter,
                          const float fTol,
                          const VectorXf& vecWeitgths)
/**
 * Follow notation of P.J. Besl and N.D. McKay, A Method for
 * Registration of 3-D Shapes, IEEE Trans. Patt. Anal. Machine Intell., 14,
 * 239 - 255, 1992.
 */
{
    // Initialization
    int iNP = matDstPoint.rows();               // The number of points
    float fMSEPrev,fMSE = 0.0;                  // The mean square error
    float fScale = 1.0;
    float bScale = true;
    MatrixXf matP0 = matDstPoint;               // Initial Set of points
    MatrixXf matPk = matP0;                     // Transformed Set of points
    MatrixXf matYk(matPk.rows(),matPk.cols());  // Iterative losest points on the surface
    MatrixXf matDiff = matYk;
    VectorXf vecSE(matDiff.rows());
    Matrix4f matTrans;                          // the transformation matrix
    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    // Initial transformation - apply inverse because we are computing in Model space
    FiffCoordTrans transToFrom = transFromTo;
    transToFrom.invert_transform();
    matPk = transToFrom.apply_trans(matPk);

    // Icp algorithm:
    for(int iIter = 0; iIter < iMaxIter; ++iIter) {

        // Step a: compute the closest point on the surface; eq 29
        if(!mneSurfacePoints->mne_find_closest_on_surface(matPk, iNP, matYk, vecNearest, vecDist)) {
            qWarning() << "RTPROCESSINGLIB::icp: mne_find_closest_on_surface was not sucessfull.";
            return false;
        }

        // Step b: compute the registration; eq 30
        if(!fitMatched(matP0, matYk, matTrans, fScale, bScale, vecWeitgths)) {
            qWarning() << "RTPROCESSINGLIB::icp: point cloud registration not succesfull";
        }

        // Step c: apply registration
        transToFrom.trans = matTrans;
        matPk = transToFrom.apply_trans(matP0);

        // step d: compute mean-square-error and terminate if below fTol
        vecDist = vecDist.cwiseProduct(vecDist);
        fMSE = vecDist.sum() / iNP;
        if(std::fabs(fMSE - fMSEPrev) < fTol) {
            transToFrom.invert_transform();
            transFromTo = transToFrom;
            qInfo() << "RTPROCESSINGLIB::icp: ICP was succesfull and exceeded after " << iIter +1 << " Iterations with MSE dist: " << fMSE << " mm.";
            return true;
        }
        fMSEPrev = fMSE;
        qInfo() << "RTPROCESSINGLIB::icp: ICP iteration " << iIter + 1 << " with MSE: " << fMSE << ".";

    }

    qWarning() << "RTPROCESSINGLIB::icp: ICP was not succesfull and exceeded after " << iMaxIter << " Iterations with MSE: " << fMSE << ".";
    return false;
}

//=============================================================================================================

bool RTPROCESSINGLIB::fitMatched(const MatrixXf& matSrcPoint,
                                 const MatrixXf& matDstPoint,
                                 Eigen::Matrix4f& matTrans,
                                 float fScale,
                                 const bool bScale,
                                 const VectorXf& vecWeitgths)
/**
 * Follow notation of P.J. Besl and N.D. McKay, A Method for
 * Registration of 3-D Shapes, IEEE Trans. Patt. Anal. Machine Intell., 14,
 * 239 - 255, 1992.
 *
 * The code is further adapted from MNE Python function _fitMatched_points(...).
 */
{
    // init values
    MatrixXf matP = matSrcPoint;
    MatrixXf matX = matDstPoint;
    VectorXf vecW = vecWeitgths;
    VectorXf vecMuP;                                // column wise mean - center of mass
    VectorXf vecMuX;                                // column wise mean - center of mass
    MatrixXf matDot;
    MatrixXf matSigmaPX;                            // cross-covariance
    MatrixXf matAij;                                // Anti-Symmetric matrix
    Vector3f vecDelta;                              // column vector, elements of matAij
    Matrix4f matQ = Matrix4f::Identity(4,4);
    Matrix3f matScale = Matrix3f::Identity(3,3);    // scaling matrix
    Matrix3f matRot = Matrix3f::Identity(3,3);
    Vector3f vecTrans;
    float fTrace = 0.0;
    fScale = 1.0;

    // test size of point clouds
    if(matSrcPoint.size() != matDstPoint.size()) {
        qWarning() << "RTPROCESSINGLIB::fitMatched: Point clouds do not match.";
        return false;
    }

    // get center of mass
    if(vecWeitgths.isZero()) {
        vecMuP = matP.colwise().mean(); // eq 23
        vecMuX = matX.colwise().mean();
        matDot = matP.transpose() * matX;
        matDot = matDot / matP.rows();
    } else {
        vecW = vecWeitgths / vecWeitgths.sum();
        vecMuP = vecW.transpose() * matP;
        vecMuX = vecW.transpose() * matX;

        MatrixXf matXWeighted = matX;
        for(int i = 0; i < (vecW.size()); ++i) {
            matXWeighted.row(i) = matXWeighted.row(i) * vecW(i);
        }
        matDot = matP.transpose() * (matXWeighted);
    }

    // get cross-covariance
    matSigmaPX = matDot - (vecMuP * vecMuX.transpose());  // eq 24
    matAij = matSigmaPX - matSigmaPX.transpose();
    vecDelta(0) = matAij(1,2); vecDelta(1) = matAij(2,0); vecDelta(2) = matAij(0,1);
    fTrace = matSigmaPX.trace();
    matQ(0,0) = fTrace; // eq 25
    matQ.block(0,1,1,3) = vecDelta.transpose();
    matQ.block(1,0,3,1) = vecDelta;
    matQ.block(1,1,3,3) = matSigmaPX + matSigmaPX.transpose() - fTrace * MatrixXf::Identity(3,3);

    // unit eigenvector coresponding to maximum eigenvalue of matQ is selected as optimal rotation quaterions q0,q1,q2,q3
    SelfAdjointEigenSolver<MatrixXf> es(matQ);
    Vector4f vecEigVec = es.eigenvectors().col(matQ.cols()-1);  // only take last Eigen-Vector since this corresponds to the maximum Eigenvalue

    // quatRot(w,x,y,z)
    Quaternionf quatRot(vecEigVec(0),vecEigVec(1),vecEigVec(2),vecEigVec(3));
    quatRot.normalize();
    matRot = quatRot.matrix();

    // get scaling factor and matrix
    if(bScale) {
        MatrixXf matDevX = matX.rowwise() - vecMuX.transpose();
        MatrixXf matDevP = matP.rowwise() - vecMuP.transpose();
        matDevX = matDevX.cwiseProduct(matDevX);
        matDevP = matDevP.cwiseProduct(matDevP);

        if(!vecWeitgths.isZero()) {
            for(int i = 0; i < (vecW.size()); ++i) {
                matDevX.row(i) = matDevX.row(i) * vecW(i);
                matDevP.row(i) = matDevP.row(i) * vecW(i);
            }
        }
        // get scaling factor and set scaling matrix
        fScale = std::sqrt(matDevX.sum() / matDevP.sum());
        matScale *= fScale;
    }

    // get translation and Rotation
    vecTrans = vecMuX - fScale * matRot * vecMuP;
    matRot *= matScale;

    matTrans.block<3,3>(0,0) = matRot;
    matTrans.block<3,1>(0,3) = vecTrans;
    matTrans(3,3) = 1.0f;
    matTrans.block<1,3>(3,0) = MatrixXf::Zero(1,3);
    return true;
}

//=========================================================================================================

bool RTPROCESSINGLIB::discardOutliers(const QSharedPointer<MNELIB::MNEProjectToSurface> mneSurfacePoints,
                                      const MatrixXf& matDstPoint,
                                      const FiffCoordTrans& transFromTo,
                                      VectorXi& vecTake,
                                      MatrixXf& matTakePoint,
                                      const float fMaxDist)
{
    // Initialization
    int iNP = matDstPoint.rows();               // The number of points
    MatrixXf matP = matDstPoint;                // Initial Set of points
    MatrixXf matYk(matDstPoint.rows(),matDstPoint.cols());  // Iterative losest points on the surface
    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    // Initial transformation - apply inverse because we are computing in Model space
    FiffCoordTrans transToFrom = transFromTo;
    transToFrom.invert_transform();
    matP = transToFrom.apply_trans(matP);

    int iDiscarded = 0;

    // discard outliers if necessary
    if(fMaxDist > 0.0) {
        if(!mneSurfacePoints->mne_find_closest_on_surface(matP, iNP, matYk, vecNearest, vecDist)) {
            qWarning() << "RTPROCESSINGLIB::icp: mne_find_closest_on_surface was not sucessfull.";
            return false;
        }

        for(int i = 0; i < vecDist.size(); ++i) {
            if(std::fabs(vecDist(i)) < fMaxDist) {
                vecTake.conservativeResize(vecTake.size()+1);
                vecTake(vecTake.size()-1) = i;
                matTakePoint.conservativeResize(matTakePoint.rows()+1,3);
                matTakePoint.row(matTakePoint.rows()-1) = matDstPoint.row(i);
            } else {
                iDiscarded++;
            }
        }
    }
    qInfo() << "RTPROCESSINGLIB::discardOutliers: " << iDiscarded << "digitizers discarded.";
}
<|endoftext|>


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

#include <znc/User.h>
#include <znc/znc.h>
#include <time.h>

using std::map;
using std::pair;
using std::multimap;

class CLastSeenMod : public CModule {
  private:
    time_t GetTime(const CUser* pUser) {
        return GetNV(pUser->GetUserName()).ToULong();
    }

    void SetTime(const CUser* pUser) {
        SetNV(pUser->GetUserName(), CString(time(nullptr)));
    }

    const CString FormatLastSeen(const CUser* pUser,
                                 const char* sDefault = "") {
        time_t last = GetTime(pUser);
        if (last < 1) {
            return sDefault;
        } else {
            char buf[1024];
            strftime(buf, sizeof(buf) - 1, "%c", localtime(&last));
            return buf;
        }
    }

    typedef multimap<time_t, CUser*> MTimeMulti;
    typedef map<CString, CUser*> MUsers;

    // Shows all users as well as the time they were last seen online
    void ShowCommand(const CString& sLine) {
        if (!GetUser()->IsAdmin()) {
            PutModule("Access denied");
            return;
        }

        const MUsers& mUsers = CZNC::Get().GetUserMap();
        MUsers::const_iterator it;
        CTable Table;

        Table.AddColumn("User");
        Table.AddColumn("Last Seen");

        for (it = mUsers.begin(); it != mUsers.end(); ++it) {
            Table.AddRow();
            Table.SetCell("User", it->first);
            Table.SetCell("Last Seen", FormatLastSeen(it->second, "never"));
        }

        PutModule(Table);
    }

  public:
    MODCONSTRUCTOR(CLastSeenMod) {
        AddHelpCommand();
        AddCommand("Show", static_cast<CModCommand::ModCmdFunc>(
                               &CLastSeenMod::ShowCommand),
                   "", "Shows list of users and when they last logged in");
    }

    ~CLastSeenMod() override {}

    // Event stuff:

    void OnClientLogin() override { SetTime(GetUser()); }

    void OnClientDisconnect() override { SetTime(GetUser()); }

    EModRet OnDeleteUser(CUser& User) override {
        DelNV(User.GetUserName());
        return CONTINUE;
    }

    // Web stuff:

    bool WebRequiresAdmin() override { return true; }
    CString GetWebMenuTitle() override { return "Last Seen"; }

    // Provides GUI to configure this module by adding a widget to user page in webadmin.
    bool OnWebRequest(CWebSock& WebSock, const CString& sPageName,
                      CTemplate& Tmpl) override {
        if (sPageName == "index") {
            CModules& GModules = CZNC::Get().GetModules();
            Tmpl["WebAdminLoaded"] =
                CString(GModules.FindModule("webadmin") != nullptr);

            MTimeMulti mmSorted;
            const MUsers& mUsers = CZNC::Get().GetUserMap();

            for (MUsers::const_iterator uit = mUsers.begin();
                 uit != mUsers.end(); ++uit) {
                mmSorted.insert(
                    pair<time_t, CUser*>(GetTime(uit->second), uit->second));
            }

            for (MTimeMulti::const_iterator it = mmSorted.begin();
                 it != mmSorted.end(); ++it) {
                CUser* pUser = it->second;
                CTemplate& Row = Tmpl.AddRow("UserLoop");

                Row["Username"] = pUser->GetUserName();
                Row["IsSelf"] =
                    CString(pUser == WebSock.GetSession()->GetUser());
                Row["LastSeen"] = FormatLastSeen(pUser, "never");
            }

            return true;
        }

        return false;
    }

    bool OnEmbeddedWebRequest(CWebSock& WebSock, const CString& sPageName,
                              CTemplate& Tmpl) override {
        if (sPageName == "webadmin/user" && WebSock.GetSession()->IsAdmin()) {
            CUser* pUser = CZNC::Get().FindUser(Tmpl["Username"]);
            if (pUser) {
                Tmpl["LastSeen"] = FormatLastSeen(pUser);
            }
            return true;
        }

        return false;
    }
};

template <>
void TModInfo<CLastSeenMod>(CModInfo& Info) {
    Info.SetWikiPage("lastseen");
}

GLOBALMODULEDEFS(CLastSeenMod,
                 "Collects data about when a user last logged in.")
<commit_msg>updating comments<commit_after>/*
 * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/User.h>
#include <znc/znc.h>
#include <time.h>

using std::map;
using std::pair;
using std::multimap;

class CLastSeenMod : public CModule {
  private:
    time_t GetTime(const CUser* pUser) {
        return GetNV(pUser->GetUserName()).ToULong();
    }

    void SetTime(const CUser* pUser) {
        SetNV(pUser->GetUserName(), CString(time(nullptr)));
    }

    const CString FormatLastSeen(const CUser* pUser,
                                 const char* sDefault = "") {
        time_t last = GetTime(pUser);
        if (last < 1) {
            return sDefault;
        } else {
            char buf[1024];
            strftime(buf, sizeof(buf) - 1, "%c", localtime(&last));
            return buf;
        }
    }

    typedef multimap<time_t, CUser*> MTimeMulti;
    typedef map<CString, CUser*> MUsers;

    // Shows all users as well as the time they were last seen online
    void ShowCommand(const CString& sLine) {
        if (!GetUser()->IsAdmin()) {
            PutModule("Access denied");
            return;
        }

        const MUsers& mUsers = CZNC::Get().GetUserMap();
        MUsers::const_iterator it;
        CTable Table;

        Table.AddColumn("User");
        Table.AddColumn("Last Seen");

        for (it = mUsers.begin(); it != mUsers.end(); ++it) {
            Table.AddRow();
            Table.SetCell("User", it->first);
            Table.SetCell("Last Seen", FormatLastSeen(it->second, "never"));
        }

        PutModule(Table);
    }

  public:
    MODCONSTRUCTOR(CLastSeenMod) {
        AddHelpCommand();
        AddCommand("Show", static_cast<CModCommand::ModCmdFunc>(
                               &CLastSeenMod::ShowCommand),
                   "", "Shows list of users and when they last logged in");
    }

    ~CLastSeenMod() override {}

    // Event stuff:

    void OnClientLogin() override { SetTime(GetUser()); }

    void OnClientDisconnect() override { SetTime(GetUser()); }

    EModRet OnDeleteUser(CUser& User) override {
        DelNV(User.GetUserName());
        return CONTINUE;
    }

    // Web stuff:

    bool WebRequiresAdmin() override { return true; }
    CString GetWebMenuTitle() override { return "Last Seen"; }

    bool OnWebRequest(CWebSock& WebSock, const CString& sPageName,
                      CTemplate& Tmpl) override {
        if (sPageName == "index") {
            CModules& GModules = CZNC::Get().GetModules();
            Tmpl["WebAdminLoaded"] =
                CString(GModules.FindModule("webadmin") != nullptr);

            MTimeMulti mmSorted;
            const MUsers& mUsers = CZNC::Get().GetUserMap();

            for (MUsers::const_iterator uit = mUsers.begin();
                 uit != mUsers.end(); ++uit) {
                mmSorted.insert(
                    pair<time_t, CUser*>(GetTime(uit->second), uit->second));
            }

            for (MTimeMulti::const_iterator it = mmSorted.begin();
                 it != mmSorted.end(); ++it) {
                CUser* pUser = it->second;
                CTemplate& Row = Tmpl.AddRow("UserLoop");

                Row["Username"] = pUser->GetUserName();
                Row["IsSelf"] =
                    CString(pUser == WebSock.GetSession()->GetUser());
                Row["LastSeen"] = FormatLastSeen(pUser, "never");
            }

            return true;
        }

        return false;
    }

    bool OnEmbeddedWebRequest(CWebSock& WebSock, const CString& sPageName,
                              CTemplate& Tmpl) override {
        if (sPageName == "webadmin/user" && WebSock.GetSession()->IsAdmin()) {
            CUser* pUser = CZNC::Get().FindUser(Tmpl["Username"]);
            if (pUser) {
                Tmpl["LastSeen"] = FormatLastSeen(pUser);
            }
            return true;
        }

        return false;
    }
};

template <>
void TModInfo<CLastSeenMod>(CModInfo& Info) {
    Info.SetWikiPage("lastseen");
}

GLOBALMODULEDEFS(CLastSeenMod,
                 "Collects data about when a user last logged in.")
<|endoftext|>


<commit_before>#include "mbed.h"
#include "max32630fthr.h"
#include "cmsis.h"          // register base address definitions
#include "pwrseq_regs.h"    // power sequence register definitions
#include "rtc_regs.h"       //RTC register definitions
#include "SDFileSystem.h"

MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);

DigitalOut rLed(LED1);
DigitalOut gLed(LED2);
DigitalOut bLed(LED3);

DigitalOut CTS(P0_2);

Serial dap(P2_1, P2_0);
Serial hci(P0_0,P0_1); //BT Tx and Rx (For mcu)

SDFileSystem sd(P0_5, P0_6, P0_4, P0_7, "sd");  // mosi, miso, sclk, cs

int val;
int x = 0;
int y = 0;
int z = 0;

void reg_cfg(){  //Configure registers

    dap.printf("Start\r\n");

    if ((MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN) != MXC_PWRSEQ->reg0){ //If the register isn't set, set it
        MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN;       //Enable 32k RTC
        x = 1;
        }
    dap.printf("x = %d\r\n", x);
    //dap.printf("RTC_En = %d\r\n\r\n", RTC_En);
    Thread::wait(100);

    if ((MXC_PWRSEQ->reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN) != MXC_PWRSEQ->reg4){
        MXC_PWRSEQ->reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN;    //Output RTC to P1_7
        y = 1;
        }
    dap.printf("y = %d\r\n",y);
    //dap.printf("ClkOut_En = %d\r\n\r\n", ClkOut_En);
    Thread::wait(100);

    //if ((MXC_UART0->ctrl |= MXC_F_UART_CTRL_CTS_EN) != MXC_UART0->ctrl){
        MXC_UART0->ctrl |= MXC_F_UART_CTRL_CTS_EN; //Enable Hardware Output flow control
        MXC_UART0->ctrl |= MXC_F_UART_CTRL_CTS_POLARITY;
        MXC_UART0->ctrl ^= MXC_F_UART_CTRL_RTS_EN; //Enable Hardware Input flow control (not enabled)
        MXC_UART0->ctrl ^= MXC_F_UART_CTRL_RTS_POLARITY;
        MXC_IOMAN->uart0_req ^= MXC_F_IOMAN_UART0_REQ_IO_MAP; //Properly map RX & TX
        MXC_IOMAN->uart0_req |= MXC_F_IOMAN_UART0_REQ_CTS_MAP; //Properly map CTS
        MXC_IOMAN->uart0_req |= MXC_F_IOMAN_UART0_REQ_RTS_MAP; //Properly map RTS
        z = 1;
       // }

    if((x == 1) | (y == 1)){
        x = 2;
        y = 2;
        z = 2;
        dap.printf("Z = %d", z);
        NVIC_SystemReset();
        }

        dap.printf("Z = %d", z);
        dap.printf("Made it through cfg\r\n");

}

void ServicePack(){

    dap.printf("In service pack function\r\n");

    unsigned char buff[4];
    unsigned char buff2[1];
    int num = 0;

    FILE *packet;
    packet = fopen("/sd/packet.txt", "r");

    while(num < 9176 ){     //55054
        for(int i = 0; i<3;i++){
            buff[i] = getc(packet);
            }
            buff2 = buff[0]+buff[1]+buff[2]+buff[3];
            dap.putc(buff2);
            num++;
        }
    fclose(packet);
gLed = 0;

}

int main()
{
    dap.baud(115200);
    dap.printf("Literally just starting up.\r\n");

    reg_cfg();

    //Config Interrupts
    //RTS.rise(&flowControl);
    //

    rLed = 1;
    bLed = 1;
    gLed = 1;
    CTS = 1;

    dap.printf("Starting rest\r\n");

    //val = RTS.read();
    //dap.printf("RTS = %d\r\n\r\n", val);

    /*
    for(int i = 0; i < 10; i++)
    {
        rLed = 1;
        Thread::wait(500);
        rLed = 0;
        val = RTS.read();
        dap.printf("RTS = %i\r\n", val);
        Thread::wait(500); //Something to do/see
        }

        dap.printf("\r\n");
        rLed = 1;
    */ //Test to see RTS value. RTS is low on proper initialization.

    //HCI Serial setup
    hci.baud(115200);
    //hci.format(8,Serial::None,1);         //Explicitly define 8n1 serial
    //hci.set_flow_control(Serial::RTSCTS,P0_2,P0_3);   //Enable RTS,CTS flow control  <--- Flow control doesn't work: causes break
    //^^ I did it mysyelf. See reg_cfg. RTS control only.
    //End setup

    bLed = 0;
    Thread::wait(250);
    bLed = 1;

    //Test: Output Service Pack

    dap.printf("Sending Service Pack...\r\n");

    ServicePack();

    dap.printf("Service Pack uploaded\r\n");

    //val = RTS.read();
    //dap.printf("RTS = %d", val);

    /* Test: Transparent Mode
     * This will take data from the daplink and output
     * it to the BT module and vice versa. Should allow
     * use with TI HCI Tool.
     */

    dap.printf("Begin Transparent Mode\r\n");

    CTS = 0;

    while(1) {

        //If terminal data available send through hci
        if(dap.readable()) {
            hci.putc(dap.getc());
        }
        //If dap gets data send to terminal
        if(hci.readable()) {
            dap.putc(hci.getc());
        }
    }

    /* Not Yet. Need service pack.
    //Write
    hci.putc(0x01); //Send command
    hci.putc(0x09);//Command LSB, Read_BD_ADDR
    hci.putc(0x10);
    hci.putc(0x00);//Null parameter length

    //Read
    CTS = 0;
    for(int n = 0; n < 32; n++){ //if there's data
        buff[n]=hci.getc();
        }

    for(int i=0; i< sizeof(buff); i++){
        dap.printf("buff[%i] = %c", i, buff[i]);
        }
    */
}
<commit_msg>Update main.cpp<commit_after>#include "mbed.h"
#include "max32630fthr.h"
#include "cmsis.h"          // register base address definitions
#include "pwrseq_regs.h"    // power sequence register definitions
#include "rtc_regs.h"       //RTC register definitions
#include "SDFileSystem.h"

MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);

DigitalOut rLed(LED1);
DigitalOut gLed(LED2);
DigitalOut bLed(LED3);

DigitalOut CTS(P0_2);

Serial dap(P2_1, P2_0);
Serial hci(P0_0,P0_1); //BT Tx and Rx (For mcu)

SDFileSystem sd(P0_5, P0_6, P0_4, P0_7, "sd");  // mosi, miso, sclk, cs

int val;
int x = 0;
int y = 0;
int z = 0;

void reg_cfg(){  //Configure registers

    dap.printf("Start\r\n");

    if ((MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN) != MXC_PWRSEQ->reg0){ //If the register isn't set, set it
        MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN;       //Enable 32k RTC
        x = 1;
        }
    dap.printf("x = %d\r\n", x);
    //dap.printf("RTC_En = %d\r\n\r\n", RTC_En);
    Thread::wait(100);

    if ((MXC_PWRSEQ->reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN) != MXC_PWRSEQ->reg4){
        MXC_PWRSEQ->reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN;    //Output RTC to P1_7
        y = 1;
        }
    dap.printf("y = %d\r\n",y);
    //dap.printf("ClkOut_En = %d\r\n\r\n", ClkOut_En);
    Thread::wait(100);

    //if ((MXC_UART0->ctrl |= MXC_F_UART_CTRL_CTS_EN) != MXC_UART0->ctrl){

        //Enable Hardware Output flow control
        MXC_UART0->ctrl |= MXC_F_UART_CTRL_CTS_EN;
        MXC_UART0->ctrl |= MXC_F_UART_CTRL_CTS_POLARITY;

        //Attempts to use RTS causes runtime crash. Don't use.
        MXC_UART0->ctrl ^= MXC_F_UART_CTRL_RTS_EN; //Enable Hardware Input flow control (not enabled)
        MXC_UART0->ctrl ^= MXC_F_UART_CTRL_RTS_POLARITY;

        MXC_IOMAN->uart0_req ^= MXC_F_IOMAN_UART0_REQ_IO_MAP; //Properly map RX & TX
        MXC_IOMAN->uart0_req |= MXC_F_IOMAN_UART0_REQ_CTS_MAP; //Properly map CTS
        MXC_IOMAN->uart0_req |= MXC_F_IOMAN_UART0_REQ_RTS_MAP; //Properly map RTS
        z = 1;
       // }

    if((x == 1) | (y == 1)){
        x = 2;
        y = 2;
        z = 2;
        dap.printf("Z = %d", z);
        NVIC_SystemReset();
        }

        dap.printf("Z = %d", z);
        dap.printf("Made it through cfg\r\n");

}

/*
 * Attempt to load TI Service Pack for the PAN1326b(CC2564b) from SD card,
 * loading from memory causes runtime error.
 * This code currently doesn't work.
 */

void ServicePack(){

    dap.printf("In service pack function\r\n");

    unsigned char buff[4];
    unsigned char buff2[1];
    int num = 0;

    FILE *packet;
    packet = fopen("/sd/packet.txt", "r");

    while(num < 9176 ){     //55054
        for(int i = 0; i<3;i++){
            buff[i] = getc(packet);
            }
            buff2 = buff[0]+buff[1]+buff[2]+buff[3];
            dap.putc(buff2);
            num++;
        }
    fclose(packet);
gLed = 0;

}

int main()
{
    dap.baud(115200);
    dap.printf("Literally just starting up.\r\n");

    reg_cfg();

    rLed = 1;
    bLed = 1;
    gLed = 1;
    CTS = 1;

    dap.printf("Starting rest\r\n");

    //HCI Serial setup
    hci.baud(115200);
    //hci.set_flow_control(Serial::RTSCTS,P0_2,P0_3);   //Enable RTS,CTS flow control  <--- Flow control doesn't work: causes break
    //End setup

    bLed = 0;
    Thread::wait(250);
    bLed = 1;

    //Test: Output Service Pack

    dap.printf("Sending Service Pack...\r\n");

    //Currently doesn't work. Comment out to manually load service pack.
    ServicePack();

    dap.printf("Service Pack uploaded\r\n");

    /* Test: Transparent Mode
     * This will take data from the daplink and output
     * it to the BT module and vice versa. Should allow
     * use with TI HCI Tool.
     */

    dap.printf("Begin Transparent Mode\r\n");

    CTS = 0;

    while(1) {

        //If terminal data available send through hci
        if(dap.readable()) {
            hci.putc(dap.getc());
        }
        //If dap gets data send to terminal
        if(hci.readable()) {
            dap.putc(hci.getc());
        }
    }
}
<|endoftext|>