<commit_before>/*
 *  VHDL code generation for LPM devices.
 *
 *  Copyright (C) 2008  Nick Gasson (nick@nickg.me.uk)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "vhdl_target.h"

#include <iostream>
#include <cassert>

/*
 * Return the base of a part select.
 */
static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_expr *off;
   ivl_nexus_t base = ivl_lpm_data(lpm, 1);
   if (base != NULL)
      off = nexus_to_var_ref(scope, base);
   else
      off = new vhdl_const_int(ivl_lpm_base(lpm));

   // Array indexes must be integers
   vhdl_type integer(VHDL_TYPE_INTEGER);
   return off->cast(&integer);
}

vhdl_var_ref *lpm_output(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm, 0));
   if (NULL == out) {
      vhdl_type *type =
         vhdl_type::type_for(ivl_lpm_width(lpm),
                             ivl_lpm_signed(lpm) != 0);
      string name("LPM");
      name += ivl_lpm_basename(lpm);
      name += "_Out";

      if (!scope->have_declared(name)) {
         scope->add_decl
            (new vhdl_signal_decl(name.c_str(), new vhdl_type(*type)));
      }

      out = new vhdl_var_ref(name.c_str(), type);
   }

   if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) {
      vhdl_expr *off = part_select_base(scope, lpm);
      assert(off);

      out->set_slice(off, ivl_lpm_width(lpm) - 1);
   }

   return out;
}

static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_type *result_type =
      vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
   vhdl_binop_expr *expr =
      new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type);

   for (int i = ivl_lpm_selects(lpm) - 1; i >= 0; i--) {
      vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == e)
         return NULL;

      expr->add_expr(e);
   }

   return expr;
}

static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
   unsigned out_width = ivl_lpm_width(lpm);
   vhdl_type *result_type =
      vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0);
   vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type);

   for (int i = 0; i < 2; i++) {
      vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == e)
         return NULL;

      expr->add_expr(e->cast(result_type));
   }

   if (op == VHDL_BINOP_MULT) {
      // Need to resize the output to the desired size,
      // as this does not happen automatically in VHDL

      vhdl_fcall *resize =
         new vhdl_fcall("Resize", vhdl_type::nsigned(out_width));
      resize->add_expr(expr);
      resize->add_expr(new vhdl_const_int(out_width));

      return resize;
   }
   else
      return expr;
}

static vhdl_expr *rel_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
   vhdl_binop_expr *expr = new vhdl_binop_expr(op, vhdl_type::boolean());

   for (int i = 0; i < 2; i++) {
      vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == e)
         return NULL;

      expr->add_expr(e);
   }

   return expr;
}

static vhdl_expr *part_select_vp_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_var_ref *selfrom = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   if (NULL == selfrom)
      return NULL;

   vhdl_expr *off = part_select_base(scope, lpm);;
   if (NULL == off)
      return NULL;

   selfrom->set_slice(off, ivl_lpm_width(lpm) - 1);
   return selfrom;
}

static vhdl_expr *part_select_pv_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   return nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
}

static vhdl_expr *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_fcall *fcall = new vhdl_fcall(ivl_lpm_basename(lpm), NULL);

   for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) {
      vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == ref)
         return NULL;

      fcall->add_expr(ref);
   }

   return fcall;
}

static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
                                     const char *rfunc, bool invert)
{
   vhdl_fcall *fcall = new vhdl_fcall(rfunc, vhdl_type::std_logic());

   vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   if (NULL == ref)
      return NULL;

   fcall->add_expr(ref);

   if (invert)
      return new vhdl_unaryop_expr
         (VHDL_UNARYOP_NOT, fcall, vhdl_type::std_logic());
   else
      return fcall;
}

static vhdl_expr *sign_extend_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_expr *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   if (ref)
      return ref->resize(ivl_lpm_width(lpm));
   else
      return NULL;
}

static vhdl_expr *array_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   ivl_signal_t array = ivl_lpm_array(lpm);
   if (!seen_signal_before(array))
      return NULL;

   const char *renamed = get_renamed_signal(array).c_str();

   vhdl_decl *adecl = scope->get_decl(renamed);
   assert(adecl);

   vhdl_type *atype = new vhdl_type(*adecl->get_type());

   vhdl_expr *select = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
   if (NULL == select)
      return NULL;

   vhdl_var_ref *ref = new vhdl_var_ref(renamed, atype);
   vhdl_type integer(VHDL_TYPE_INTEGER);
   ref->set_slice(select->cast(&integer));

   return ref;
}

static vhdl_expr *shift_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
                                    vhdl_binop_t shift_op)
{
   vhdl_expr *lhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   vhdl_expr *rhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
   if (!lhs || !rhs)
      return NULL;

   // The RHS must be an integer
   vhdl_type integer(VHDL_TYPE_INTEGER);
   vhdl_expr *r_cast = rhs->cast(&integer);

   vhdl_type *rtype = new vhdl_type(*lhs->get_type());
   return new vhdl_binop_expr(lhs, shift_op, r_cast, rtype);
}

static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   switch (ivl_lpm_type(lpm)) {
   case IVL_LPM_ADD:
      return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD);
   case IVL_LPM_SUB:
      return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB);
   case IVL_LPM_MULT:
      return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT);
   case IVL_LPM_CONCAT:
      return concat_lpm_to_expr(scope, lpm);
   case IVL_LPM_CMP_GE:
      return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ);
   case IVL_LPM_CMP_EQ:
   case IVL_LPM_CMP_EEQ:
      return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_EQ);
   case IVL_LPM_PART_VP:
      return part_select_vp_lpm_to_expr(scope, lpm);
   case IVL_LPM_PART_PV:
      return part_select_pv_lpm_to_expr(scope, lpm);
   case IVL_LPM_UFUNC:
      return ufunc_lpm_to_expr(scope, lpm);
   case IVL_LPM_RE_AND:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_AND", false);
   case IVL_LPM_RE_NAND:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_AND", true);
   case IVL_LPM_RE_NOR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_OR", true);
   case IVL_LPM_RE_OR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_OR", false);
   case IVL_LPM_RE_XOR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_XOR", false);
   case IVL_LPM_RE_XNOR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_XNOR", false);
   case IVL_LPM_SIGN_EXT:
      return sign_extend_lpm_to_expr(scope, lpm);
   case IVL_LPM_ARRAY:
      return array_lpm_to_expr(scope, lpm);
   case IVL_LPM_SHIFTL:
      return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SL);
   case IVL_LPM_SHIFTR:
      return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SR);
   default:
      error("Unsupported LPM type: %d", ivl_lpm_type(lpm));
      return NULL;
   }
}

int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
{
   vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm);
   if (NULL == f)
      return 0;

   vhdl_var_ref *out = lpm_output(arch->get_scope(), lpm);
   arch->add_stmt(new vhdl_cassign_stmt(out, f));

   return 0;
}

<commit_msg>Fix implementation of IVL_LPM_UFUNC<commit_after>/*
 *  VHDL code generation for LPM devices.
 *
 *  Copyright (C) 2008  Nick Gasson (nick@nickg.me.uk)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "vhdl_target.h"

#include <iostream>
#include <cassert>

/*
 * Return the base of a part select.
 */
static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_expr *off;
   ivl_nexus_t base = ivl_lpm_data(lpm, 1);
   if (base != NULL)
      off = nexus_to_var_ref(scope, base);
   else
      off = new vhdl_const_int(ivl_lpm_base(lpm));

   // Array indexes must be integers
   vhdl_type integer(VHDL_TYPE_INTEGER);
   return off->cast(&integer);
}

vhdl_var_ref *lpm_output(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm, 0));
   if (NULL == out) {
      vhdl_type *type =
         vhdl_type::type_for(ivl_lpm_width(lpm),
                             ivl_lpm_signed(lpm) != 0);
      string name("LPM");
      name += ivl_lpm_basename(lpm);
      name += "_Out";

      if (!scope->have_declared(name)) {
         scope->add_decl
            (new vhdl_signal_decl(name.c_str(), new vhdl_type(*type)));
      }

      out = new vhdl_var_ref(name.c_str(), type);
   }

   if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) {
      vhdl_expr *off = part_select_base(scope, lpm);
      assert(off);

      out->set_slice(off, ivl_lpm_width(lpm) - 1);
   }

   return out;
}

static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_type *result_type =
      vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
   vhdl_binop_expr *expr =
      new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type);

   for (int i = ivl_lpm_selects(lpm) - 1; i >= 0; i--) {
      vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == e)
         return NULL;

      expr->add_expr(e);
   }

   return expr;
}

static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
   unsigned out_width = ivl_lpm_width(lpm);
   vhdl_type *result_type =
      vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0);
   vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type);

   for (int i = 0; i < 2; i++) {
      vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == e)
         return NULL;

      expr->add_expr(e->cast(result_type));
   }

   if (op == VHDL_BINOP_MULT) {
      // Need to resize the output to the desired size,
      // as this does not happen automatically in VHDL

      vhdl_fcall *resize =
         new vhdl_fcall("Resize", vhdl_type::nsigned(out_width));
      resize->add_expr(expr);
      resize->add_expr(new vhdl_const_int(out_width));

      return resize;
   }
   else
      return expr;
}

static vhdl_expr *rel_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
   vhdl_binop_expr *expr = new vhdl_binop_expr(op, vhdl_type::boolean());

   for (int i = 0; i < 2; i++) {
      vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == e)
         return NULL;

      expr->add_expr(e);
   }

   return expr;
}

static vhdl_expr *part_select_vp_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_var_ref *selfrom = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   if (NULL == selfrom)
      return NULL;

   vhdl_expr *off = part_select_base(scope, lpm);;
   if (NULL == off)
      return NULL;

   selfrom->set_slice(off, ivl_lpm_width(lpm) - 1);
   return selfrom;
}

static vhdl_expr *part_select_pv_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   return nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
}

static vhdl_expr *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   ivl_scope_t f_scope = ivl_lpm_define(lpm);
   vhdl_fcall *fcall = new vhdl_fcall(ivl_scope_basename(f_scope), NULL);

   for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) {
      vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
      if (NULL == ref)
         return NULL;

      fcall->add_expr(ref);
   }

   return fcall;
}

static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
                                     const char *rfunc, bool invert)
{
   vhdl_fcall *fcall = new vhdl_fcall(rfunc, vhdl_type::std_logic());

   vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   if (NULL == ref)
      return NULL;

   fcall->add_expr(ref);

   if (invert)
      return new vhdl_unaryop_expr
         (VHDL_UNARYOP_NOT, fcall, vhdl_type::std_logic());
   else
      return fcall;
}

static vhdl_expr *sign_extend_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   vhdl_expr *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   if (ref)
      return ref->resize(ivl_lpm_width(lpm));
   else
      return NULL;
}

static vhdl_expr *array_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   ivl_signal_t array = ivl_lpm_array(lpm);
   if (!seen_signal_before(array))
      return NULL;

   const char *renamed = get_renamed_signal(array).c_str();

   vhdl_decl *adecl = scope->get_decl(renamed);
   assert(adecl);

   vhdl_type *atype = new vhdl_type(*adecl->get_type());

   vhdl_expr *select = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
   if (NULL == select)
      return NULL;

   vhdl_var_ref *ref = new vhdl_var_ref(renamed, atype);
   vhdl_type integer(VHDL_TYPE_INTEGER);
   ref->set_slice(select->cast(&integer));

   return ref;
}

static vhdl_expr *shift_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
                                    vhdl_binop_t shift_op)
{
   vhdl_expr *lhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
   vhdl_expr *rhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
   if (!lhs || !rhs)
      return NULL;

   // The RHS must be an integer
   vhdl_type integer(VHDL_TYPE_INTEGER);
   vhdl_expr *r_cast = rhs->cast(&integer);

   vhdl_type *rtype = new vhdl_type(*lhs->get_type());
   return new vhdl_binop_expr(lhs, shift_op, r_cast, rtype);
}

static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
   switch (ivl_lpm_type(lpm)) {
   case IVL_LPM_ADD:
      return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD);
   case IVL_LPM_SUB:
      return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB);
   case IVL_LPM_MULT:
      return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT);
   case IVL_LPM_CONCAT:
      return concat_lpm_to_expr(scope, lpm);
   case IVL_LPM_CMP_GE:
      return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ);
   case IVL_LPM_CMP_EQ:
   case IVL_LPM_CMP_EEQ:
      return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_EQ);
   case IVL_LPM_PART_VP:
      return part_select_vp_lpm_to_expr(scope, lpm);
   case IVL_LPM_PART_PV:
      return part_select_pv_lpm_to_expr(scope, lpm);
   case IVL_LPM_UFUNC:
      return ufunc_lpm_to_expr(scope, lpm);
   case IVL_LPM_RE_AND:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_AND", false);
   case IVL_LPM_RE_NAND:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_AND", true);
   case IVL_LPM_RE_NOR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_OR", true);
   case IVL_LPM_RE_OR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_OR", false);
   case IVL_LPM_RE_XOR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_XOR", false);
   case IVL_LPM_RE_XNOR:
      return reduction_lpm_to_expr(scope, lpm, "Reduce_XNOR", false);
   case IVL_LPM_SIGN_EXT:
      return sign_extend_lpm_to_expr(scope, lpm);
   case IVL_LPM_ARRAY:
      return array_lpm_to_expr(scope, lpm);
   case IVL_LPM_SHIFTL:
      return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SL);
   case IVL_LPM_SHIFTR:
      return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SR);
   default:
      error("Unsupported LPM type: %d", ivl_lpm_type(lpm));
      return NULL;
   }
}

int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
{
   vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm);
   if (NULL == f)
      return 0;

   vhdl_var_ref *out = lpm_output(arch->get_scope(), lpm);
   arch->add_stmt(new vhdl_cassign_stmt(out, f));

   return 0;
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please
** contact Nokia at http://qt.nokia.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/
#ifdef QMESSAGING_OPTIONAL_FOLDER
#include "qmessagefoldersortkey.h"
#include "qmessagefoldersortkey_p.h"

QMessageFolderSortKey::QMessageFolderSortKey()
{
}

QMessageFolderSortKey::QMessageFolderSortKey(const QMessageFolderSortKey &other)
{
    Q_UNUSED(other)
}

QMessageFolderSortKey::~QMessageFolderSortKey()
{
    delete d_ptr;
    d_ptr = 0;
}

bool QMessageFolderSortKey::isEmpty() const
{
    return false; // stub
}

QMessageFolderSortKey QMessageFolderSortKey::operator+(const QMessageFolderSortKey& other) const
{
    Q_UNUSED(other)
    return QMessageFolderSortKey(); // stub
}

QMessageFolderSortKey& QMessageFolderSortKey::operator+=(const QMessageFolderSortKey& other)
{
    Q_UNUSED(other)
    return *this; // stub
}

bool QMessageFolderSortKey::operator==(const QMessageFolderSortKey& other) const
{
    Q_UNUSED(other)
    return false; // stub
}

const QMessageFolderSortKey& QMessageFolderSortKey::operator=(const QMessageFolderSortKey& other)
{
    Q_UNUSED(other)
    return *this; // stub
}

QMessageFolderSortKey QMessageFolderSortKey::displayName(Qt::SortOrder order)
{
    Q_UNUSED(order)
    return QMessageFolderSortKey(); // stub
}

QMessageFolderSortKey QMessageFolderSortKey::path(Qt::SortOrder order)
{
    Q_UNUSED(order)
    return QMessageFolderSortKey(); // stub
}
#endif
<commit_msg>Avoid segfault.<commit_after>/****************************************************************************
**
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please
** contact Nokia at http://qt.nokia.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/
#ifdef QMESSAGING_OPTIONAL_FOLDER
#include "qmessagefoldersortkey.h"
#include "qmessagefoldersortkey_p.h"

QMessageFolderSortKey::QMessageFolderSortKey()
    : d_ptr(new QMessageFolderSortKeyPrivate(this))
{
}

QMessageFolderSortKey::QMessageFolderSortKey(const QMessageFolderSortKey &other)
    : d_ptr(new QMessageFolderSortKeyPrivate(this))
{
    Q_UNUSED(other)
}

QMessageFolderSortKey::~QMessageFolderSortKey()
{
    delete d_ptr;
    d_ptr = 0;
}

bool QMessageFolderSortKey::isEmpty() const
{
    return false; // stub
}

QMessageFolderSortKey QMessageFolderSortKey::operator+(const QMessageFolderSortKey& other) const
{
    Q_UNUSED(other)
    return QMessageFolderSortKey(); // stub
}

QMessageFolderSortKey& QMessageFolderSortKey::operator+=(const QMessageFolderSortKey& other)
{
    Q_UNUSED(other)
    return *this; // stub
}

bool QMessageFolderSortKey::operator==(const QMessageFolderSortKey& other) const
{
    Q_UNUSED(other)
    return false; // stub
}

const QMessageFolderSortKey& QMessageFolderSortKey::operator=(const QMessageFolderSortKey& other)
{
    Q_UNUSED(other)
    return *this; // stub
}

QMessageFolderSortKey QMessageFolderSortKey::displayName(Qt::SortOrder order)
{
    Q_UNUSED(order)
    return QMessageFolderSortKey(); // stub
}

QMessageFolderSortKey QMessageFolderSortKey::path(Qt::SortOrder order)
{
    Q_UNUSED(order)
    return QMessageFolderSortKey(); // stub
}
#endif
<|endoftext|>


<commit_before>/*********************************************************************
 * thread-sleep: Force Node.js to sleep
 *
 * Copyright (c) 2015 Forbes Lindesay
 * Copyright (c) 2015 Tiancheng "Timothy" Gu
 *
 * MIT License
 ********************************************************************/

#include <nan.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <ctime>
#endif

using v8::FunctionTemplate;
using Nan::GetFunction;
using Nan::New;
using Nan::Set;

void SleepSync(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // expect a number as the first argument
  int millisec = args[0]->Uint32Value();

#ifdef _WIN32
  Sleep(millisec);
#else
  struct timespec req;
  req.tv_sec = millisec / 1000;
  req.tv_nsec = (millisec % 1000) * 1000000L;
  nanosleep(&req, (struct timespec *)NULL);
#endif

  info.GetReturnValue().Set(millisec);
}

// Expose SleepSync() as sleep() in JS
NAN_MODULE_INIT(InitAll) {
  Set(target, New("sleep").ToLocalChecked(),
    GetFunction(New<FunctionTemplate>(SleepSync)).ToLocalChecked());
}

NODE_MODULE(thread_sleep, InitAll)
<commit_msg>Use correct type for millisec<commit_after>/*********************************************************************
 * thread-sleep: Force Node.js to sleep
 *
 * Copyright (c) 2015 Forbes Lindesay
 * Copyright (c) 2015 Tiancheng "Timothy" Gu
 *
 * MIT License
 ********************************************************************/

#include <nan.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <ctime>
#endif

using v8::FunctionTemplate;
using Nan::GetFunction;
using Nan::New;
using Nan::Set;

void SleepSync(const Nan::FunctionCallbackInfo<v8::Value>& info) {
#ifdef _WIN32
  // expect a number as the first argument
  DWORD millisec = info[0]->Uint32Value();

  Sleep(millisec);
#else
  // expect a number as the first argument
  uint32_t millisec = info[0]->Uint32Value();

  struct timespec req;
  req.tv_sec = millisec / 1000;
  req.tv_nsec = (millisec % 1000) * 1000000L;
  nanosleep(&req, (struct timespec *)NULL);
#endif

  info.GetReturnValue().Set(millisec);
}

// Expose SleepSync() as sleep() in JS
NAN_MODULE_INIT(InitAll) {
  Set(target, New("sleep").ToLocalChecked(),
    GetFunction(New<FunctionTemplate>(SleepSync)).ToLocalChecked());
}

NODE_MODULE(thread_sleep, InitAll)
<|endoftext|>


<commit_before>// @Alan @pezy
//
// Exercise 10.34:
// Use reverse_iterators to print a vector in reverse order.
//
// Exercise 10.35:
// Now print the elements in reverse order using ordinary iterators.
//
// Exercise 10.36:
// Use find to find the last element in a list of ints with value 0.
//
// Exercise 10.37:
// Given a vector that has ten elements, copy the elements from positions
// 3 through 7 in reverse order to a list.
//

#include <iostream>
#include <algorithm>
#include <list>
#include <vector>
#include <iterator>

int main()
{
    std::vector<int> vec = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    // 10.34
    for (auto rit = vec.crbegin(); rit != vec.crend(); ++rit)
        std::cout << *rit << " ";
    std::cout << std::endl;

    // 10.35
    for (auto it = std::prev(vec.cend()); true; --it) {
        std::cout << *it << " ";
        if (it == vec.cbegin()) break;
    }
    std::cout << std::endl;

    // 10.36
    std::list<int> lst = { 1, 2, 3, 4, 0, 5, 6 };
    auto found_0 = std::find(lst.crbegin(), lst.crend(), 0);
    std::cout << std::distance(found_0, lst.crend()) << std::endl;

    // 10.37
    std::list<int> ret_lst(8 - 3);
    std::copy(vec.cbegin() + 3, vec.cbegin() + 8, ret_lst.rbegin());
    //     0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    //    ^                  ^
    // rend                  rbegin
    // @note: std::copy copies the range [first, last) into result.
    //        hence, the arguments here denote:
    //        [6 5 4 3 2 1)
    //                   ^ this one is specified but not included.
    for (auto i : ret_lst) std::cout << i << " ";
    std::cout << std::endl;
}
<commit_msg>fix the wrong comment in 10.37<commit_after>// @Alan @pezy
//
// Exercise 10.34:
// Use reverse_iterators to print a vector in reverse order.
//
// Exercise 10.35:
// Now print the elements in reverse order using ordinary iterators.
//
// Exercise 10.36:
// Use find to find the last element in a list of ints with value 0.
//
// Exercise 10.37:
// Given a vector that has ten elements, copy the elements from positions
// 3 through 7 in reverse order to a list.
//

#include <iostream>
#include <algorithm>
#include <list>
#include <vector>
#include <iterator>

int main()
{
    std::vector<int> vec = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    // 10.34
    for (auto rit = vec.crbegin(); rit != vec.crend(); ++rit)
        std::cout << *rit << " ";
    std::cout << std::endl;

    // 10.35
    for (auto it = std::prev(vec.cend()); true; --it) {
        std::cout << *it << " ";
        if (it == vec.cbegin()) break;
    }
    std::cout << std::endl;

    // 10.36
    std::list<int> lst = { 1, 2, 3, 4, 0, 5, 6 };
    auto found_0 = std::find(lst.crbegin(), lst.crend(), 0);
    std::cout << std::distance(found_0, lst.crend()) << std::endl;

    // 10.37
    std::list<int> ret_lst(8 - 3);
    std::copy(vec.cbegin() + 3, vec.cbegin() + 8, ret_lst.rbegin());
    //     0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    //           ^              ^
    //          rend          rbegin
    // @note: std::copy copies the range [first, last) into result.
    //        hence, the arguments here denote:
    //        [7 6 5 4 3 2)
    //                   ^ this one is specified but not included.
    for (auto i : ret_lst) std::cout << i << " ";
    std::cout << std::endl;
}
<|endoftext|>


<commit_before>#include "ExampleGame/Components/GameScripts/Units/Thief.h"
#include "ExampleGame/Components/Grid/GameGrid.h"
#include "ExampleGame/Components/Grid/GridCell.h"
#include "ExampleGame/Components/Grid/GridConstants.h"
#include "ExampleGame/Components/Grid/GridManager.h"
#include "ExampleGame/Components/Grid/GridNavigator.h"
#include "ExampleGame/GameConstants/GameConstants.h"
#include "ExampleGame/GameSingletons/GameSingletons.h"
#include "ExampleGame/Messages/Declarations.h"

#include "Vajra/Engine/Components/DerivedComponents/Renderer/SpriteRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/Input/Input.h"
#include "Vajra/Engine/MessageHub/MessageHub.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Engine/Tween/Tween.h"
#include "Vajra/Framework/DeviceUtils/FileSystemUtils/FileSystemUtils.h"

// Tween callbacks
void thiefTweenCallback(ObjectIdType gameObjectId , std::string /* tweenClipName */) {
	GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(gameObjectId);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* pUnit = go->GetComponent<Thief>();
		ASSERT(pUnit != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(pUnit != nullptr) {
			pUnit->onSpecialEnd();
		}
	}

}

void thiefNumberTweenCallback(float /* fromNumber */, float /* toNumber */, float /*currentNumber*/, std::string /*tweenClipName*/, MessageData1S1I1F* /*userParams*/) {
	/*GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(userParams->i);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* thief = go->GetComponent<Thief>();
		ASSERT(thief != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(thief != nullptr) {
			if(tweenClipName == "targetsIn") {
				for(GameObject* go : thief->targetIndicatorsRef ) {

				}

			}
		}
	}*/
}

Thief::Thief() : PlayerUnit() {
	this->init();
}

Thief::Thief(Object* object_) : PlayerUnit(object_) {
	this->init();
}

Thief::~Thief() {
	this->destroy();
}

void Thief::init() {
	this->unitType = UnitType::UNIT_TYPE_THIEF;
}

void Thief::destroy() {
	this->deleteTargets();
}

bool Thief::isSpecialTouch(int touchId) {
	if(this->getTouchNearUnit()) {
		Touch touch = ENGINE->GetInput()->GetTouch(touchId);
		if(touch.timeDown >= GetFloatGameConstant(GAME_CONSTANT_long_press_length_in_seconds) && glm::distance(touch.pos, this->touchStartPos) <= GetFloatGameConstant(GAME_CONSTANT_allowed_finger_movement_in_press)) {
			this->targetedCell = nullptr;
			this->SetTouchIndicatorVisible(false);
			this->gridNavRef->HaltMovement();
			this->updateLegalTagets();
			return true;
		}
	}
	return false;
}

void Thief::onSpecialTouch(int touchId) {
	Touch touch = ENGINE->GetInput()->GetTouch(touchId);
	if(touch.phase == TouchPhase::Ended) {
		if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->targetedCell) != this->legalTargets.end()) {
			this->startSpecial();
		} else {
			this->targetedCell = this->gridNavRef->GetCurrentCell();
			this->cancelSpecial();
		}
		this->tweenOutTargets();
	} else if(touch.phase == TouchPhase::Moved) {
		this->aimSpecial(touchId);
	}
}

void Thief::startSpecial() {
	PlayerUnit::startSpecial();
	// Remove the indicator at the selected position
	GameObject* selectedTargetIndicator = this->targetIndicatorsRef[this->targetedCell];
	this->targetIndicatorsRef.erase(this->targetedCell);
	delete selectedTargetIndicator;

	this->SetTouchIndicatorCell(this->targetedCell);
	this->startTouchIndicatorPulse();
	this->SetTouchIndicatorSprite(THIEF_SPECIAL);
	this->SetTouchIndicatorVisible(true);

	float jumpDist = glm::distance(this->targetedCell->center, this->gameObjectRef->GetTransform()->GetPosition());
	float jumpTweenTime = jumpDist / GetFloatGameConstant(GAME_CONSTANT_jump_speed_in_units_per_second);
	ENGINE->GetTween()->TweenPosition(this->gameObjectRef->GetId(),
									  this->gameObjectRef->GetTransform()->GetPosition(),
									  this->targetedCell->center,
									  jumpTweenTime,
									  false,
									  TWEEN_TRANSLATION_CURVE_TYPE_PARABOLA,
									  false,
									  thiefTweenCallback);

}

void Thief::onSpecialEnd() {
	PlayerUnit::onSpecialEnd();
	this->gridNavRef->SetGridPosition(this->targetedCell);

	// Broadcast an attack message
	MessageChunk attackMessage = ENGINE->GetMessageHub()->GetOneFreeMessage();
	attackMessage->SetMessageType(MESSAGE_TYPE_UNIT_SPECIAL_HIT);
	attackMessage->messageData.iv1.x = this->targetedCell->x;
	attackMessage->messageData.iv1.y = this->targetedCell->y;
	attackMessage->messageData.iv1.z = this->targetedCell->z;
	attackMessage->messageData.fv1 = this->specialStartPos;
	ENGINE->GetMessageHub()->SendMulticastMessage(attackMessage, this->GetObject()->GetId());
}

void Thief::cancelSpecial() {
	PlayerUnit::cancelSpecial();
	this->tweenOutTargets();

}
void Thief::aimSpecial(int touchId) {
	GridCell* prevTargetCell = this->targetedCell;
	this->targetedCell = this->GetCurrentTouchedCell();

	if(this->targetIndicatorsRef[prevTargetCell] && this->targetIndicatorsRef[this->targetedCell]) {
		if(this->targetedCell != prevTargetCell) {
			this->scaleUpIndicator(this->targetedCell);
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if (!this->targetIndicatorsRef[prevTargetCell] && !this->targetIndicatorsRef[this->targetedCell]) {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				this->scaleUpIndicator(nearCell);
			}
			this->targetedCell = nearCell;
		}
	} else if(this->targetIndicatorsRef[prevTargetCell]) {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				if(nearCell != prevTargetCell) {
					this->scaleUpIndicator(nearCell);
					this->scaleDownIndicator(prevTargetCell);
				}
			} else {
				this->scaleDownIndicator(prevTargetCell);
			}
			this->targetedCell = nearCell;
		} else {
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if(this->targetIndicatorsRef[this->targetedCell]) {
		this->scaleUpIndicator(this->targetedCell);
	}

	if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->GetCurrentTouchedCell()) != this->legalTargets.end()) {
		this->SetTouchIndicatorSprite(GOOD_TOUCH);
	} else {
		this->SetTouchIndicatorSprite(BAD_TOUCH);
	}
}

void Thief::updateLegalTagets() {
	this->legalTargets.clear();
	this->deleteTargets();
	GridCell* currentCell = this->gridNavRef->GetCurrentCell();
	int elevation = SINGLETONS->GetGridManager()->GetGrid()->GetElevationFromWorldY(currentCell->center.y);

	std::list<GridCell*> cellsInRange;
	SINGLETONS->GetGridManager()->GetGrid()->GetNeighborCells(cellsInRange, this->gridNavRef->GetCurrentCell(),
															  GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)
															  + elevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier));

	for ( GridCell* c : cellsInRange) {
		int cellElevation = 0;
		for(int i = 0; i < NUM_ELEVATIONS; ++i) {
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, i)) {
				cellElevation = i;
				break;
			}

		}

		// The thief can jump to a maximum of one elevation level above her
		if (cellElevation <= (elevation + 1)) {
			if ((cellElevation > elevation) || (SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation))) {
				if (SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
					int elevationDiff = elevation - cellElevation;
					if (elevationDiff < 0) {
						elevationDiff = 0;
					}

					float maxRange = fmax(GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + elevationDiff * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier), 1.0f);
					float dist = SINGLETONS->GetGridManager()->GetGrid()->GetGroundDistanceBetweenCells(currentCell, c);
					if (dist <= maxRange) {
						this->legalTargets.push_back(c);
					}
				}
			}
		}
		/*
		if(cellElevation == elevation) { // is the cell on the same height
			if(this->gridNavRef->CanReachDestination(c, GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)) && SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation) ) {
				this->legalTargets.push_back(c);
			}
		}
		else if(cellElevation <= elevation + 1) { // is the cell below it
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
				float dist = glm::distance(glm::vec2(c->x, c->z), glm::vec2(currentCell->x, currentCell->z));
				if(dist <= GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + cellElevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier)) {
					this->legalTargets.push_back(c);
				}
			}
		}
		*/
	}
	this->createTargets();
	this->tweenInTargets();
}

void Thief::tweenInTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenPosition(contents.second->GetId(), pos, pos + glm::vec3(0.0f, GetFloatGameConstant(GAME_CONSTANT_target_indicator_offset), 0.0f), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::tweenOutTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenScale(contents.second->GetId(), glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)), glm::vec3(0), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::createTargets() {
	for( GridCell* c : this->legalTargets ) {
		GameObject* indicator = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = indicator->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Thief_Jump_cyan.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		indicator->GetTransform()->SetPosition(c->center);
		indicator->GetTransform()->SetScale( glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)));
		indicator->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->targetIndicatorsRef[c] = indicator;
	}
}

void Thief::deleteTargets() {
	this->targetIndicatorsRef.clear();
}

void Thief::scaleUpIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

void Thief::scaleDownIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

GridCell* Thief::getNearCellTowardsUnit(int touchId) {
	glm::vec3 unitPos = this->gridNavRef->GetCurrentCell()->center;
	glm::vec3 touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->gridNavRef->GetCurrentCell()->y);
	glm::vec3 dirTowardsThief = glm::normalize(unitPos - touchPos);
	dirTowardsThief *= .55f;
	glm::vec3 cellLocation = touchPos + dirTowardsThief;
	return SINGLETONS->GetGridManager()->GetGrid()->GetCell(cellLocation);
}<commit_msg>Improved touch offset<commit_after>#include "ExampleGame/Components/GameScripts/Units/Thief.h"
#include "ExampleGame/Components/Grid/GameGrid.h"
#include "ExampleGame/Components/Grid/GridCell.h"
#include "ExampleGame/Components/Grid/GridConstants.h"
#include "ExampleGame/Components/Grid/GridManager.h"
#include "ExampleGame/Components/Grid/GridNavigator.h"
#include "ExampleGame/GameConstants/GameConstants.h"
#include "ExampleGame/GameSingletons/GameSingletons.h"
#include "ExampleGame/Messages/Declarations.h"

#include "Vajra/Engine/Components/DerivedComponents/Renderer/SpriteRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/Input/Input.h"
#include "Vajra/Engine/MessageHub/MessageHub.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Engine/Tween/Tween.h"
#include "Vajra/Framework/DeviceUtils/FileSystemUtils/FileSystemUtils.h"

// Tween callbacks
void thiefTweenCallback(ObjectIdType gameObjectId , std::string /* tweenClipName */) {
	GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(gameObjectId);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* pUnit = go->GetComponent<Thief>();
		ASSERT(pUnit != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(pUnit != nullptr) {
			pUnit->onSpecialEnd();
		}
	}

}

void thiefNumberTweenCallback(float /* fromNumber */, float /* toNumber */, float /*currentNumber*/, std::string /*tweenClipName*/, MessageData1S1I1F* /*userParams*/) {
	/*GameObject* go = ENGINE->GetSceneGraph3D()->GetGameObjectById(userParams->i);
	ASSERT(go != nullptr, "Game object id passed into playerUnitNuumberTweenCallback is not valid");
	if(go != nullptr) {
		Thief* thief = go->GetComponent<Thief>();
		ASSERT(thief != nullptr, "Game object passed into playerUnitNuumberTweenCallback doesn't have a player unit");
		if(thief != nullptr) {
			if(tweenClipName == "targetsIn") {
				for(GameObject* go : thief->targetIndicatorsRef ) {

				}

			}
		}
	}*/
}

Thief::Thief() : PlayerUnit() {
	this->init();
}

Thief::Thief(Object* object_) : PlayerUnit(object_) {
	this->init();
}

Thief::~Thief() {
	this->destroy();
}

void Thief::init() {
	this->unitType = UnitType::UNIT_TYPE_THIEF;
}

void Thief::destroy() {
	this->deleteTargets();
}

bool Thief::isSpecialTouch(int touchId) {
	if(this->getTouchNearUnit()) {
		Touch touch = ENGINE->GetInput()->GetTouch(touchId);
		if(touch.timeDown >= GetFloatGameConstant(GAME_CONSTANT_long_press_length_in_seconds) && glm::distance(touch.pos, this->touchStartPos) <= GetFloatGameConstant(GAME_CONSTANT_allowed_finger_movement_in_press)) {
			this->targetedCell = nullptr;
			this->SetTouchIndicatorVisible(false);
			this->gridNavRef->HaltMovement();
			this->updateLegalTagets();
			return true;
		}
	}
	return false;
}

void Thief::onSpecialTouch(int touchId) {
	Touch touch = ENGINE->GetInput()->GetTouch(touchId);
	if(touch.phase == TouchPhase::Ended) {
		if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->targetedCell) != this->legalTargets.end()) {
			this->startSpecial();
		} else {
			this->targetedCell = this->gridNavRef->GetCurrentCell();
			this->cancelSpecial();
		}
		this->tweenOutTargets();
	} else if(touch.phase == TouchPhase::Moved) {
		this->aimSpecial(touchId);
	}
}

void Thief::startSpecial() {
	PlayerUnit::startSpecial();
	// Remove the indicator at the selected position
	GameObject* selectedTargetIndicator = this->targetIndicatorsRef[this->targetedCell];
	this->targetIndicatorsRef.erase(this->targetedCell);
	delete selectedTargetIndicator;

	this->SetTouchIndicatorCell(this->targetedCell);
	this->startTouchIndicatorPulse();
	this->SetTouchIndicatorSprite(THIEF_SPECIAL);
	this->SetTouchIndicatorVisible(true);

	float jumpDist = glm::distance(this->targetedCell->center, this->gameObjectRef->GetTransform()->GetPosition());
	float jumpTweenTime = jumpDist / GetFloatGameConstant(GAME_CONSTANT_jump_speed_in_units_per_second);
	ENGINE->GetTween()->TweenPosition(this->gameObjectRef->GetId(),
									  this->gameObjectRef->GetTransform()->GetPosition(),
									  this->targetedCell->center,
									  jumpTweenTime,
									  false,
									  TWEEN_TRANSLATION_CURVE_TYPE_PARABOLA,
									  false,
									  thiefTweenCallback);

}

void Thief::onSpecialEnd() {
	PlayerUnit::onSpecialEnd();
	this->gridNavRef->SetGridPosition(this->targetedCell);

	// Broadcast an attack message
	MessageChunk attackMessage = ENGINE->GetMessageHub()->GetOneFreeMessage();
	attackMessage->SetMessageType(MESSAGE_TYPE_UNIT_SPECIAL_HIT);
	attackMessage->messageData.iv1.x = this->targetedCell->x;
	attackMessage->messageData.iv1.y = this->targetedCell->y;
	attackMessage->messageData.iv1.z = this->targetedCell->z;
	attackMessage->messageData.fv1 = this->specialStartPos;
	ENGINE->GetMessageHub()->SendMulticastMessage(attackMessage, this->GetObject()->GetId());
}

void Thief::cancelSpecial() {
	PlayerUnit::cancelSpecial();
	this->tweenOutTargets();

}
void Thief::aimSpecial(int touchId) {
	GridCell* prevTargetCell = this->targetedCell;
	this->targetedCell = this->GetCurrentTouchedCell();

	if(this->targetIndicatorsRef[prevTargetCell] && this->targetIndicatorsRef[this->targetedCell]) {
		if(this->targetedCell != prevTargetCell) {
			this->scaleUpIndicator(this->targetedCell);
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if (!this->targetIndicatorsRef[prevTargetCell] && !this->targetIndicatorsRef[this->targetedCell]) {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				this->scaleUpIndicator(nearCell);
			}
			this->targetedCell = nearCell;
		}
	} else if(this->targetIndicatorsRef[prevTargetCell]) {
		GridCell* nearCell = this->getNearCellTowardsUnit(touchId);
		if(nearCell != nullptr) {
			if(this->targetIndicatorsRef[nearCell]) {
				if(nearCell != prevTargetCell) {
					this->scaleUpIndicator(nearCell);
					this->scaleDownIndicator(prevTargetCell);
				}
			} else {
				this->scaleDownIndicator(prevTargetCell);
			}
			this->targetedCell = nearCell;
		} else {
			this->scaleDownIndicator(prevTargetCell);
		}
	} else if(this->targetIndicatorsRef[this->targetedCell]) {
		this->scaleUpIndicator(this->targetedCell);
	}

	if(std::find(this->legalTargets.begin(), this->legalTargets.end(), this->GetCurrentTouchedCell()) != this->legalTargets.end()) {
		this->SetTouchIndicatorSprite(GOOD_TOUCH);
	} else {
		this->SetTouchIndicatorSprite(BAD_TOUCH);
	}
}

void Thief::updateLegalTagets() {
	this->legalTargets.clear();
	this->deleteTargets();
	GridCell* currentCell = this->gridNavRef->GetCurrentCell();
	int elevation = SINGLETONS->GetGridManager()->GetGrid()->GetElevationFromWorldY(currentCell->center.y);

	std::list<GridCell*> cellsInRange;
	SINGLETONS->GetGridManager()->GetGrid()->GetNeighborCells(cellsInRange, this->gridNavRef->GetCurrentCell(),
															  GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)
															  + elevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier));

	for ( GridCell* c : cellsInRange) {
		int cellElevation = 0;
		for(int i = 0; i < NUM_ELEVATIONS; ++i) {
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, i)) {
				cellElevation = i;
				break;
			}

		}

		// The thief can jump to a maximum of one elevation level above her
		if (cellElevation <= (elevation + 1)) {
			if ((cellElevation > elevation) || (SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation))) {
				if (SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
					int elevationDiff = elevation - cellElevation;
					if (elevationDiff < 0) {
						elevationDiff = 0;
					}

					float maxRange = fmax(GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + elevationDiff * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier), 1.0f);
					float dist = SINGLETONS->GetGridManager()->GetGrid()->GetGroundDistanceBetweenCells(currentCell, c);
					if (dist <= maxRange) {
						this->legalTargets.push_back(c);
					}
				}
			}
		}
		/*
		if(cellElevation == elevation) { // is the cell on the same height
			if(this->gridNavRef->CanReachDestination(c, GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units)) && SINGLETONS->GetGridManager()->GetGrid()->HasLineOfSight(currentCell, c, elevation) ) {
				this->legalTargets.push_back(c);
			}
		}
		else if(cellElevation <= elevation + 1) { // is the cell below it
			if(SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, cellElevation)) {
				float dist = glm::distance(glm::vec2(c->x, c->z), glm::vec2(currentCell->x, currentCell->z));
				if(dist <= GetFloatGameConstant(GAME_CONSTANT_jump_distance_in_units) + cellElevation * GetFloatGameConstant(GAME_CONSTANT_jump_elevation_multiplier)) {
					this->legalTargets.push_back(c);
				}
			}
		}
		*/
	}
	this->createTargets();
	this->tweenInTargets();
}

void Thief::tweenInTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenPosition(contents.second->GetId(), pos, pos + glm::vec3(0.0f, GetFloatGameConstant(GAME_CONSTANT_target_indicator_offset), 0.0f), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::tweenOutTargets() {
	glm::vec3 pos;
	for(auto contents : this->targetIndicatorsRef ) {
		if(contents.second != nullptr) {
			pos = contents.second->GetTransform()->GetPosition();
			ENGINE->GetTween()->TweenScale(contents.second->GetId(), glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)), glm::vec3(0), GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
		}
	}
}

void Thief::createTargets() {
	for( GridCell* c : this->legalTargets ) {
		GameObject* indicator = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = indicator->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Thief_Jump_cyan.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		indicator->GetTransform()->SetPosition(c->center);
		indicator->GetTransform()->SetScale( glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)));
		indicator->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->targetIndicatorsRef[c] = indicator;
	}
}

void Thief::deleteTargets() {
	this->targetIndicatorsRef.clear();
}

void Thief::scaleUpIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

void Thief::scaleDownIndicator(GridCell* cell) {
	if(cell != nullptr && this->targetIndicatorsRef[cell] != nullptr) {
		ENGINE->GetTween()->TweenScale(this->targetIndicatorsRef[cell]->GetId(),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale_hover)),
									   glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)),
									   GetFloatGameConstant(GAME_CONSTANT_target_tween_time));
	}
}

GridCell* Thief::getNearCellTowardsUnit(int touchId) {
	glm::vec3 unitPos = this->gridNavRef->GetCurrentCell()->center;
	glm::vec3 touchPos;
	if(this->GetCurrentTouchedCell()) {
		touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->GetCurrentTouchedCell()->y);
	}  else {
		touchPos = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->gridNavRef->GetCurrentCell()->y);
	}
	glm::vec3 dirTowardsThief = glm::normalize(unitPos - touchPos);
	dirTowardsThief *= .55f;
	glm::vec3 cellLocation = touchPos + dirTowardsThief;
	return SINGLETONS->GetGridManager()->GetGrid()->GetCell(cellLocation);
}<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#ifndef UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
#define UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED

#include <uavcan/build_config.hpp>
#include <uavcan/util/multiset.hpp>
#include <uavcan/node/generic_publisher.hpp>
#include <uavcan/node/generic_subscriber.hpp>

#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
# error UAVCAN_CPP_VERSION
#endif

#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
# include <functional>
#endif

namespace uavcan
{

template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
{
public: // so much templating it hurts
    typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
                                                         NumStaticReceiversAndBuffers,
                                                         NumStaticReceiversAndBuffers,
                                                         TransferListenerWithFilter>::Type Type;
};

/**
 * This struct describes a pending service call.
 * Refer to @ref ServiceClient to learn more about service calls.
 */
struct ServiceCallID
{
    NodeID server_node_id;
    TransferID transfer_id;

    ServiceCallID() { }

    ServiceCallID(NodeID arg_server_node_id, TransferID arg_transfer_id)
        : server_node_id(arg_server_node_id)
        , transfer_id(arg_transfer_id)
    { }

    bool operator==(const ServiceCallID rhs) const
    {
        return (rhs.server_node_id == server_node_id) &&
               (rhs.transfer_id == transfer_id);
    }

    bool isValid() const { return server_node_id.isUnicast(); }
};

/**
 * Object of this type will be returned to the application as a result of service call.
 * Note that application ALWAYS gets this result, even when it times out or fails because of some other reason.
 */
template <typename DataType>
class UAVCAN_EXPORT ServiceCallResult
{
public:
    typedef ReceivedDataStructure<typename DataType::Response> ResponseFieldType;

    enum Status { Success, ErrorTimeout };

private:
    const Status status_;               ///< Whether successful or not. Failure to decode the response causes timeout.
    ServiceCallID call_id_;             ///< Identifies the call
    ResponseFieldType& response_;       ///< Returned data structure. Value undefined if the service call has failed.

public:
    ServiceCallResult(Status arg_status, ServiceCallID arg_call_id, ResponseFieldType& arg_response)
        : status_(arg_status)
        , call_id_(arg_call_id)
        , response_(arg_response)
    {
        UAVCAN_ASSERT(call_id_.isValid());
        UAVCAN_ASSERT((status_ == Success) || (status_ == ErrorTimeout));
    }

    /**
     * Shortcut to quickly check whether the call was successful.
     */
    bool isSuccessful() const { return status_ == Success; }

    Status getStatus() const { return status_; }

    ServiceCallID getCallID() const { return call_id_; }

    const ResponseFieldType& getResponse() const { return response_; }
    ResponseFieldType& getResponse() { return response_; }
};

/**
 * This operator neatly prints the service call result prepended with extra data like Server Node ID.
 * The extra data will be represented as YAML comment.
 */
template <typename Stream, typename DataType>
static Stream& operator<<(Stream& s, const ServiceCallResult<DataType>& scr)
{
    s << "# Service call result [" << DataType::getDataTypeFullName() << "] "
      << (scr.isSuccessful() ? "OK" : "FAILURE")
      << " server_node_id=" << int(scr.getCallID().server_node_id.get())
      << " tid=" << int(scr.getCallID().transfer_id.get()) << "\n";
    if (scr.isSuccessful())
    {
        s << scr.getResponse();
    }
    else
    {
        s << "# (no data)";
    }
    return s;
}

/**
 * Do not use directly.
 */
class ServiceClientBase : public ITransferAcceptanceFilter, Noncopyable
{
    const DataTypeDescriptor* data_type_descriptor_;  ///< This will be initialized at the time of first call

protected:
    class CallState : DeadlineHandler
    {
        ServiceClientBase& owner_;
        const ServiceCallID id_;

        virtual void handleDeadline(MonotonicTime);

    public:
        CallState(INode& node, ServiceClientBase& owner, ServiceCallID call_id)
            : DeadlineHandler(node.getScheduler())
            , owner_(owner)
            , id_(call_id)
        {
            UAVCAN_ASSERT(id_.isValid());
            DeadlineHandler::startWithDelay(owner_.request_timeout_);
        }

        bool doesMatch(ServiceCallID call_id) const { return call_id == id_; }

        bool operator==(const CallState& rhs) const
        {
            return (&owner_ == &rhs.owner_) && (id_ == rhs.id_);
        }
    };

    struct CallStateMatchingPredicate
    {
        const ServiceCallID id;
        CallStateMatchingPredicate(ServiceCallID reference) : id(reference) { }
        bool operator()(const CallState& state) const { return state.doesMatch(id); }
    };

    MonotonicDuration request_timeout_;

    ServiceClientBase()
        : data_type_descriptor_(NULL)
        , request_timeout_(getDefaultRequestTimeout())
    { }

    virtual ~ServiceClientBase() { }

    int prepareToCall(INode& node, const char* dtname, NodeID server_node_id, ServiceCallID& out_call_id);

    virtual void handleTimeout(ServiceCallID call_id) = 0;

public:
    /**
     * It's not recommended to override default timeouts.
     * Change of this value will not affect pending calls.
     */
    static MonotonicDuration getDefaultRequestTimeout() { return MonotonicDuration::fromMSec(500); }
    static MonotonicDuration getMinRequestTimeout() { return MonotonicDuration::fromMSec(10); }
    static MonotonicDuration getMaxRequestTimeout() { return MonotonicDuration::fromMSec(60000); }
};

/**
 * Use this class to invoke services on remote nodes.
 *
 * @tparam DataType_        Service data type.
 *
 * @tparam Callback_        Service response will be delivered through the callback of this type.
 *                          In C++11 mode this type defaults to std::function<>.
 *                          In C++03 mode this type defaults to a plain function pointer; use binder to
 *                          call member functions as callbacks.
 */
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
          typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>,
#else
          typename Callback_ = void (*)(const ServiceCallResult<DataType_>&),
#endif
          unsigned NumStaticCalls_ = 1
          >
class UAVCAN_EXPORT ServiceClient
    : public GenericSubscriber<DataType_,
                               typename DataType_::Response,
                               typename ServiceResponseTransferListenerInstantiationHelper<DataType_,
                                                                                NumStaticCalls_>::Type>
    , public ServiceClientBase
{
public:
    typedef DataType_ DataType;
    typedef typename DataType::Request RequestType;
    typedef typename DataType::Response ResponseType;
    typedef ServiceCallResult<DataType> ServiceCallResultType;
    typedef Callback_ Callback;

    enum { NumStaticCalls = NumStaticCalls_ };

private:
    typedef ServiceClient<DataType, Callback> SelfType;
    typedef GenericPublisher<DataType, RequestType> PublisherType;
    typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType, NumStaticCalls>::Type
            TransferListenerType;
    typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;

    typedef Multiset<CallState, NumStaticCalls> CallRegistry;
    CallRegistry call_registry_;

    PublisherType publisher_;
    Callback callback_;

    virtual bool shouldAcceptFrame(const RxFrame& frame) const; // Called from the transfer listener

    void invokeCallback(ServiceCallResultType& result);

    virtual void handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response);

    virtual void handleTimeout(ServiceCallID call_id);

    int addCallState(ServiceCallID call_id);

public:
    /**
     * @param node      Node instance this client will be registered with.
     * @param callback  Callback instance. Optional, can be assigned later.
     */
    explicit ServiceClient(INode& node, const Callback& callback = Callback())
        : SubscriberType(node)
        , call_registry_(node.getAllocator())
        , publisher_(node, getDefaultRequestTimeout())
        , callback_(callback)
    {
        setRequestTimeout(getDefaultRequestTimeout());
#if UAVCAN_DEBUG
        UAVCAN_ASSERT(getRequestTimeout() == getDefaultRequestTimeout());  // Making sure default values are OK
#endif
    }

    virtual ~ServiceClient() { cancelAll(); }

    /**
     * Shall be called before first use.
     * Returns negative error code.
     */
    int init()
    {
        return publisher_.init();
    }

    /**
     * Performs non-blocking service call.
     * This method transmits the service request and returns immediately.
     *
     * Service response will be delivered into the application via the callback.
     * Note that the callback will ALWAYS be called even if the service call has timed out; the
     * actual result of the call (success/failure) will be passed to the callback as well.
     *
     * Returns negative error code.
     */
    int call(NodeID server_node_id, const RequestType& request);

    /**
     * Same as plain @ref call() above, but this overload also returns the call ID of the new call.
     */
    int call(NodeID server_node_id, const RequestType& request, ServiceCallID& out_call_id);

    /**
     * Cancels certain call referred via call ID structure.
     */
    void cancel(ServiceCallID call_id);

    /**
     * Cancels all pending calls.
     */
    void cancelAll();

    /**
     * Service response callback must be set prior service call.
     */
    const Callback& getCallback() const { return callback_; }
    void setCallback(const Callback& cb) { callback_ = cb; }

    /**
     * Complexity is O(N) of number of pending calls.
     */
    unsigned getNumPendingCalls() const { return call_registry_.getSize(); }

    /**
     * Complexity is O(1).
     */
    bool hasPendingCalls() const { return !call_registry_.isEmpty(); }

    /**
     * Returns the number of failed attempts to decode received response. Generally, a failed attempt means either:
     * - Transient failure in the transport layer.
     * - Incompatible data types.
     */
    uint32_t getResponseFailureCount() const { return SubscriberType::getFailureCount(); }

    /**
     * Request timeouts.
     * There is no such config as TX timeout - TX timeouts are configured automagically according to request timeouts.
     * Not recommended to change.
     */
    MonotonicDuration getRequestTimeout() const { return request_timeout_; }
    void setRequestTimeout(MonotonicDuration timeout)
    {
        timeout = max(timeout, getMinRequestTimeout());
        timeout = min(timeout, getMaxRequestTimeout());

        publisher_.setTxTimeout(timeout);
        request_timeout_ = max(timeout, publisher_.getTxTimeout());  // No less than TX timeout
    }
};

// ----------------------------------------------------------------------------

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(ServiceCallResultType& result)
{
    if (try_implicit_cast<bool>(callback_, true))
    {
        callback_(result);
    }
    else
    {
        handleFatalError("Srv client clbk");
    }
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(const RxFrame& frame) const
{
    UAVCAN_ASSERT(frame.getTransferType() == TransferTypeServiceResponse); // Other types filtered out by dispatcher

    return NULL != call_registry_.find(CallStateMatchingPredicate(ServiceCallID(frame.getSrcNodeID(),
                                                                                frame.getTransferID())));

}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::
handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
{
    UAVCAN_ASSERT(response.getTransferType() == TransferTypeServiceResponse);

    ServiceCallID call_id(response.getSrcNodeID(), response.getTransferID());
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::Success, call_id, response);    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleTimeout(ServiceCallID call_id)
{
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::ErrorTimeout, call_id,
                                 SubscriberType::getReceivedStructStorage());    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCallID call_id)
{
    if (call_registry_.isEmpty())
    {
        const int subscriber_res = SubscriberType::startAsServiceResponseListener();
        if (subscriber_res < 0)
        {
            UAVCAN_TRACE("ServiceClient", "Failed to start the subscriber, error: %i", subscriber_res);
            return subscriber_res;
        }
    }

    if (NULL == call_registry_.template emplace<INode&, ServiceClientBase&, ServiceCallID>(SubscriberType::getNode(),
                                                                                *this, call_id))
    {
        SubscriberType::stop();
        return -ErrMemory;
    }

    return 0;
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request)
{
   ServiceCallID dummy;
   return call(server_node_id, request, dummy);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request,
                                                               ServiceCallID& out_call_id)
{
    if (!try_implicit_cast<bool>(callback_, true))
    {
        UAVCAN_TRACE("ServiceClient", "Invalid callback");
        return -ErrInvalidConfiguration;
    }

    /*
     * Common procedures that don't depend on the struct data type
     */
    TransferID transfer_id;
    const int prep_res =
        prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, out_call_id);
    if (prep_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to prepare the call, error: %i", prep_res);
        return prep_res;
    }

    /*
     * Initializing the call state - this will start the subscriber ad-hoc
     */
    const int call_state_res = addCallState(out_call_id);
    if (call_state_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to add call state, error: %i", call_state_res);
        return call_state_res;
    }

    /*
     * Configuring the listener so it will accept only the matching responses
     * TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
     */
    TransferListenerType* const tl = SubscriberType::getTransferListener();
    if (tl == NULL)
    {
        UAVCAN_ASSERT(0);  // Must have been created
        cancel(out_call_id);
        return -ErrLogic;
    }
    tl->installAcceptanceFilter(this);

    /*
     * Publishing the request
     */
    const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id, transfer_id);
    if (publisher_res < 0)
    {
        cancel(out_call_id);
        return publisher_res;
    }

    return publisher_res;
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancel(ServiceCallID call_id)
{
    call_registry_.removeFirstWhere(CallStateMatchingPredicate(call_id));
    if (call_registry_.isEmpty())
    {
        SubscriberType::stop();
    }
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelAll()
{
    call_registry_.clear();
    SubscriberType::stop();
}

}

#endif // UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
<commit_msg>Nasty bug in ServiceClient<>::call()<commit_after>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#ifndef UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
#define UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED

#include <uavcan/build_config.hpp>
#include <uavcan/util/multiset.hpp>
#include <uavcan/node/generic_publisher.hpp>
#include <uavcan/node/generic_subscriber.hpp>

#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
# error UAVCAN_CPP_VERSION
#endif

#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
# include <functional>
#endif

namespace uavcan
{

template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
{
public: // so much templating it hurts
    typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
                                                         NumStaticReceiversAndBuffers,
                                                         NumStaticReceiversAndBuffers,
                                                         TransferListenerWithFilter>::Type Type;
};

/**
 * This struct describes a pending service call.
 * Refer to @ref ServiceClient to learn more about service calls.
 */
struct ServiceCallID
{
    NodeID server_node_id;
    TransferID transfer_id;

    ServiceCallID() { }

    ServiceCallID(NodeID arg_server_node_id, TransferID arg_transfer_id)
        : server_node_id(arg_server_node_id)
        , transfer_id(arg_transfer_id)
    { }

    bool operator==(const ServiceCallID rhs) const
    {
        return (rhs.server_node_id == server_node_id) &&
               (rhs.transfer_id == transfer_id);
    }

    bool isValid() const { return server_node_id.isUnicast(); }
};

/**
 * Object of this type will be returned to the application as a result of service call.
 * Note that application ALWAYS gets this result, even when it times out or fails because of some other reason.
 */
template <typename DataType>
class UAVCAN_EXPORT ServiceCallResult
{
public:
    typedef ReceivedDataStructure<typename DataType::Response> ResponseFieldType;

    enum Status { Success, ErrorTimeout };

private:
    const Status status_;               ///< Whether successful or not. Failure to decode the response causes timeout.
    ServiceCallID call_id_;             ///< Identifies the call
    ResponseFieldType& response_;       ///< Returned data structure. Value undefined if the service call has failed.

public:
    ServiceCallResult(Status arg_status, ServiceCallID arg_call_id, ResponseFieldType& arg_response)
        : status_(arg_status)
        , call_id_(arg_call_id)
        , response_(arg_response)
    {
        UAVCAN_ASSERT(call_id_.isValid());
        UAVCAN_ASSERT((status_ == Success) || (status_ == ErrorTimeout));
    }

    /**
     * Shortcut to quickly check whether the call was successful.
     */
    bool isSuccessful() const { return status_ == Success; }

    Status getStatus() const { return status_; }

    ServiceCallID getCallID() const { return call_id_; }

    const ResponseFieldType& getResponse() const { return response_; }
    ResponseFieldType& getResponse() { return response_; }
};

/**
 * This operator neatly prints the service call result prepended with extra data like Server Node ID.
 * The extra data will be represented as YAML comment.
 */
template <typename Stream, typename DataType>
static Stream& operator<<(Stream& s, const ServiceCallResult<DataType>& scr)
{
    s << "# Service call result [" << DataType::getDataTypeFullName() << "] "
      << (scr.isSuccessful() ? "OK" : "FAILURE")
      << " server_node_id=" << int(scr.getCallID().server_node_id.get())
      << " tid=" << int(scr.getCallID().transfer_id.get()) << "\n";
    if (scr.isSuccessful())
    {
        s << scr.getResponse();
    }
    else
    {
        s << "# (no data)";
    }
    return s;
}

/**
 * Do not use directly.
 */
class ServiceClientBase : public ITransferAcceptanceFilter, Noncopyable
{
    const DataTypeDescriptor* data_type_descriptor_;  ///< This will be initialized at the time of first call

protected:
    class CallState : DeadlineHandler
    {
        ServiceClientBase& owner_;
        const ServiceCallID id_;

        virtual void handleDeadline(MonotonicTime);

    public:
        CallState(INode& node, ServiceClientBase& owner, ServiceCallID call_id)
            : DeadlineHandler(node.getScheduler())
            , owner_(owner)
            , id_(call_id)
        {
            UAVCAN_ASSERT(id_.isValid());
            DeadlineHandler::startWithDelay(owner_.request_timeout_);
        }

        bool doesMatch(ServiceCallID call_id) const { return call_id == id_; }

        bool operator==(const CallState& rhs) const
        {
            return (&owner_ == &rhs.owner_) && (id_ == rhs.id_);
        }
    };

    struct CallStateMatchingPredicate
    {
        const ServiceCallID id;
        CallStateMatchingPredicate(ServiceCallID reference) : id(reference) { }
        bool operator()(const CallState& state) const { return state.doesMatch(id); }
    };

    MonotonicDuration request_timeout_;

    ServiceClientBase()
        : data_type_descriptor_(NULL)
        , request_timeout_(getDefaultRequestTimeout())
    { }

    virtual ~ServiceClientBase() { }

    int prepareToCall(INode& node, const char* dtname, NodeID server_node_id, ServiceCallID& out_call_id);

    virtual void handleTimeout(ServiceCallID call_id) = 0;

public:
    /**
     * It's not recommended to override default timeouts.
     * Change of this value will not affect pending calls.
     */
    static MonotonicDuration getDefaultRequestTimeout() { return MonotonicDuration::fromMSec(500); }
    static MonotonicDuration getMinRequestTimeout() { return MonotonicDuration::fromMSec(10); }
    static MonotonicDuration getMaxRequestTimeout() { return MonotonicDuration::fromMSec(60000); }
};

/**
 * Use this class to invoke services on remote nodes.
 *
 * @tparam DataType_        Service data type.
 *
 * @tparam Callback_        Service response will be delivered through the callback of this type.
 *                          In C++11 mode this type defaults to std::function<>.
 *                          In C++03 mode this type defaults to a plain function pointer; use binder to
 *                          call member functions as callbacks.
 */
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
          typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>,
#else
          typename Callback_ = void (*)(const ServiceCallResult<DataType_>&),
#endif
          unsigned NumStaticCalls_ = 1
          >
class UAVCAN_EXPORT ServiceClient
    : public GenericSubscriber<DataType_,
                               typename DataType_::Response,
                               typename ServiceResponseTransferListenerInstantiationHelper<DataType_,
                                                                                NumStaticCalls_>::Type>
    , public ServiceClientBase
{
public:
    typedef DataType_ DataType;
    typedef typename DataType::Request RequestType;
    typedef typename DataType::Response ResponseType;
    typedef ServiceCallResult<DataType> ServiceCallResultType;
    typedef Callback_ Callback;

    enum { NumStaticCalls = NumStaticCalls_ };

private:
    typedef ServiceClient<DataType, Callback> SelfType;
    typedef GenericPublisher<DataType, RequestType> PublisherType;
    typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType, NumStaticCalls>::Type
            TransferListenerType;
    typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;

    typedef Multiset<CallState, NumStaticCalls> CallRegistry;
    CallRegistry call_registry_;

    PublisherType publisher_;
    Callback callback_;

    virtual bool shouldAcceptFrame(const RxFrame& frame) const; // Called from the transfer listener

    void invokeCallback(ServiceCallResultType& result);

    virtual void handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response);

    virtual void handleTimeout(ServiceCallID call_id);

    int addCallState(ServiceCallID call_id);

public:
    /**
     * @param node      Node instance this client will be registered with.
     * @param callback  Callback instance. Optional, can be assigned later.
     */
    explicit ServiceClient(INode& node, const Callback& callback = Callback())
        : SubscriberType(node)
        , call_registry_(node.getAllocator())
        , publisher_(node, getDefaultRequestTimeout())
        , callback_(callback)
    {
        setRequestTimeout(getDefaultRequestTimeout());
#if UAVCAN_DEBUG
        UAVCAN_ASSERT(getRequestTimeout() == getDefaultRequestTimeout());  // Making sure default values are OK
#endif
    }

    virtual ~ServiceClient() { cancelAll(); }

    /**
     * Shall be called before first use.
     * Returns negative error code.
     */
    int init()
    {
        return publisher_.init();
    }

    /**
     * Performs non-blocking service call.
     * This method transmits the service request and returns immediately.
     *
     * Service response will be delivered into the application via the callback.
     * Note that the callback will ALWAYS be called even if the service call has timed out; the
     * actual result of the call (success/failure) will be passed to the callback as well.
     *
     * Returns negative error code.
     */
    int call(NodeID server_node_id, const RequestType& request);

    /**
     * Same as plain @ref call() above, but this overload also returns the call ID of the new call.
     */
    int call(NodeID server_node_id, const RequestType& request, ServiceCallID& out_call_id);

    /**
     * Cancels certain call referred via call ID structure.
     */
    void cancel(ServiceCallID call_id);

    /**
     * Cancels all pending calls.
     */
    void cancelAll();

    /**
     * Service response callback must be set prior service call.
     */
    const Callback& getCallback() const { return callback_; }
    void setCallback(const Callback& cb) { callback_ = cb; }

    /**
     * Complexity is O(N) of number of pending calls.
     */
    unsigned getNumPendingCalls() const { return call_registry_.getSize(); }

    /**
     * Complexity is O(1).
     */
    bool hasPendingCalls() const { return !call_registry_.isEmpty(); }

    /**
     * Returns the number of failed attempts to decode received response. Generally, a failed attempt means either:
     * - Transient failure in the transport layer.
     * - Incompatible data types.
     */
    uint32_t getResponseFailureCount() const { return SubscriberType::getFailureCount(); }

    /**
     * Request timeouts.
     * There is no such config as TX timeout - TX timeouts are configured automagically according to request timeouts.
     * Not recommended to change.
     */
    MonotonicDuration getRequestTimeout() const { return request_timeout_; }
    void setRequestTimeout(MonotonicDuration timeout)
    {
        timeout = max(timeout, getMinRequestTimeout());
        timeout = min(timeout, getMaxRequestTimeout());

        publisher_.setTxTimeout(timeout);
        request_timeout_ = max(timeout, publisher_.getTxTimeout());  // No less than TX timeout
    }
};

// ----------------------------------------------------------------------------

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(ServiceCallResultType& result)
{
    if (try_implicit_cast<bool>(callback_, true))
    {
        callback_(result);
    }
    else
    {
        handleFatalError("Srv client clbk");
    }
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(const RxFrame& frame) const
{
    UAVCAN_ASSERT(frame.getTransferType() == TransferTypeServiceResponse); // Other types filtered out by dispatcher

    return NULL != call_registry_.find(CallStateMatchingPredicate(ServiceCallID(frame.getSrcNodeID(),
                                                                                frame.getTransferID())));

}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::
handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
{
    UAVCAN_ASSERT(response.getTransferType() == TransferTypeServiceResponse);

    ServiceCallID call_id(response.getSrcNodeID(), response.getTransferID());
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::Success, call_id, response);    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleTimeout(ServiceCallID call_id)
{
    cancel(call_id);
    ServiceCallResultType result(ServiceCallResultType::ErrorTimeout, call_id,
                                 SubscriberType::getReceivedStructStorage());    // Mutable!
    invokeCallback(result);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCallID call_id)
{
    if (call_registry_.isEmpty())
    {
        const int subscriber_res = SubscriberType::startAsServiceResponseListener();
        if (subscriber_res < 0)
        {
            UAVCAN_TRACE("ServiceClient", "Failed to start the subscriber, error: %i", subscriber_res);
            return subscriber_res;
        }
    }

    if (NULL == call_registry_.template emplace<INode&, ServiceClientBase&, ServiceCallID>(SubscriberType::getNode(),
                                                                                *this, call_id))
    {
        SubscriberType::stop();
        return -ErrMemory;
    }

    return 0;
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request)
{
   ServiceCallID dummy;
   return call(server_node_id, request, dummy);
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request,
                                                               ServiceCallID& out_call_id)
{
    if (!try_implicit_cast<bool>(callback_, true))
    {
        UAVCAN_TRACE("ServiceClient", "Invalid callback");
        return -ErrInvalidConfiguration;
    }

    /*
     * Common procedures that don't depend on the struct data type
     */
    const int prep_res =
        prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, out_call_id);
    if (prep_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to prepare the call, error: %i", prep_res);
        return prep_res;
    }

    /*
     * Initializing the call state - this will start the subscriber ad-hoc
     */
    const int call_state_res = addCallState(out_call_id);
    if (call_state_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to add call state, error: %i", call_state_res);
        return call_state_res;
    }

    /*
     * Configuring the listener so it will accept only the matching responses
     * TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
     */
    TransferListenerType* const tl = SubscriberType::getTransferListener();
    if (tl == NULL)
    {
        UAVCAN_ASSERT(0);  // Must have been created
        cancel(out_call_id);
        return -ErrLogic;
    }
    tl->installAcceptanceFilter(this);

    /*
     * Publishing the request
     */
    const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id,
                                                 out_call_id.transfer_id);
    if (publisher_res < 0)
    {
        cancel(out_call_id);
        return publisher_res;
    }

    UAVCAN_ASSERT(server_node_id == out_call_id.server_node_id);
    return publisher_res;
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancel(ServiceCallID call_id)
{
    call_registry_.removeFirstWhere(CallStateMatchingPredicate(call_id));
    if (call_registry_.isEmpty())
    {
        SubscriberType::stop();
    }
}

template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelAll()
{
    call_registry_.clear();
    SubscriberType::stop();
}

}

#endif // UAVCAN_NODE_SERVICE_CLIENT_HPP_INCLUDED
<|endoftext|>