<commit_before>#include <Game/Field/cUnderGround.h>
#include <Resource/TextureManager.h>

using namespace ci;
using namespace ci::app;

namespace Game
{
namespace Field
{
cUnderGround::cUnderGround()
{
}
cUnderGround::~cUnderGround()
{
}
void cUnderGround::setup()
{
    TEX.set( "dirt", "dirt.jpg" );

    num = 32;
    height = 4;
    offset = 0.0f;
    scale = 1.0f;
    uint count = 0;
    auto offset_position = vec3( -scale / 2 );

    for (int z = 0; z < num; z++)
    {
        std::vector<std::vector<std::shared_ptr<cBlock>>> temps;
        for (int y = 0; y < height; y++)
        {
            std::vector<std::shared_ptr<cBlock>> temp;
            for (int x = 0; x < num; x++)
            {
                auto position = vec3( x * scale + x * offset,
                                      y * -scale + y * offset,
                                      z * scale + z * offset ) + offset_position;
                std::shared_ptr<cBlock> block = std::make_shared<cBlock>( position, scale, count );

                // Ƃ肠[ȊO̕`؂
                //bool is_active = false;
                //if (z == num - 1 || z == 0 ||
                //     y == num - 1 || y == 0 ||
                //     x == num - 1 || x == 0)
                //    is_active = true;

                block->mIsActive = true;

                // bV𐶐߂
                //std::vector<int> draw_side;
                //if (z == num - 1)
                //    draw_side.push_back( 0 );
                //if (z == 0)
                //    draw_side.push_back( 1 );
                //if (x == num - 1)
                //    draw_side.push_back( 2 );
                //if (x == 0)
                //    draw_side.push_back( 3 );
                //if (y == num - 1)
                //    draw_side.push_back( 4 );
                //if (y == 0)
                //    draw_side.push_back( 5 );

                //block->mDrawSide = draw_side;
                //blockMeshBlend( block );

                temp.emplace_back( std::move( block ) );
            }
            temps.emplace_back( std::move( temp ) );
            temp.clear();
        }
        blocks.emplace_back( std::move( temps ) );
        temps.clear();
    }

    createMesh();
    //blocks[3][0][0]->mIsActive = false;
    //blockDigged( ivec3( 0, 0, 3 ) );

    //blocks[3][0][3]->mIsActive = false;
    //blockDigged( ivec3( 3, 0, 3 ) );

    //blocks[4][num - 1][4]->mIsActive = false;
    //blockDigged( ivec3( 4, num - 1, 4 ) );

    //blocks[num - 1][4][4]->mIsActive = false;
    //blockDigged( ivec3( 4, 4, num - 1 ) );

    //blocks[num - 1][num - 1][num - 1]->mIsActive = false;
    //blockDigged( ivec3( num - 1, num - 1, num - 1 ) );
}
void cUnderGround::update()
{
}
void cUnderGround::draw()
{
    auto texture = TEX.get( "dirt" );
    if (!texture)
        return;
    using namespace ci::gl;

    auto ctx = context();

    Rectf texRect = texture->getAreaTexCoords( Area( vec2( 0 ), texture->getSize() ) );

    ScopedVao vaoScp( ctx->getDrawTextureVao() );
    ScopedBuffer vboScp( ctx->getDrawTextureVbo() );
    ScopedTextureBind texBindScope( texture );

    auto glsl = getStockShader( ShaderDef().uniformBasedPosAndTexCoord().color().texture( texture ) );
    ScopedGlslProg glslScp( glsl );
    glsl->uniform( "uTex0", 0 );
    glsl->uniform( "uPositionOffset", vec2( 0 ) );
    glsl->uniform( "uPositionScale", vec2( 1 ) );
    glsl->uniform( "uTexCoordOffset", texRect.getUpperLeft() );
    glsl->uniform( "uTexCoordScale", texRect.getSize() );

    for (uint z = 0; z < blocks.size(); z++)
        for (uint y = 0; y < blocks[z].size(); y++)
            for (uint x = 0; x < blocks[z][y].size(); x++)
                blocks[z][y][x]->draw();

    //gl::draw( mVboMesh );
}
bool cUnderGround::isOutOfRange( const ci::ivec3& c )
{
    return  (uint) c.z < 0 || (uint) c.z > blocks.size() - 1 ||
        (uint) c.y < 0 || (uint) c.y > blocks[c.z].size() - 1 ||
        (uint) c.x < 0 || (uint) c.x > blocks[c.z][c.y].size() - 1;
}
bool cUnderGround::blockDigged( const ci::ivec3& c )
{
    if (isOutOfRange( c ))
        return false;

    // Ƃ肠ꂩtB[h蒼
    //blockClear();

    ivec3 dir[] = {
        ivec3( 0, 0, 1 ),
        ivec3( 0, 0,-1 ) ,
        ivec3( 1, 0, 0 ) ,
        ivec3( -1, 0, 0 ) ,
        ivec3( 0, 1, 0 ) ,
        ivec3( 0,-1, 0 ) };

    std::vector<ivec3> change_block;

    // ubN̎6}X̃ubÑbV蒼
    for (int i = 0; i < 6; i++)
    {
        auto cell = c + dir[i];
        if (isOutOfRange( cell ))
            continue;

        // ubN݂ĂȂ͂
        auto b = blocks[cell.z][cell.y][cell.x];
        if (b->mIsActive == false)
            continue;

        std::vector<int> side;
        for (int k = 0; k < 6; k++)
        {
            auto search_cell = cell + dir[k];
            if (isOutOfRange( search_cell ))
                side.push_back( k );
            else if (blocks[search_cell.z][search_cell.y][search_cell.x]->mIsActive == false)
                side.push_back( k );
        }
        b->clear();
        b->mDrawSide = side;
        // bVɕύXubNo^
        change_block.push_back( cell );
    }

    for (uint i = 0; i < change_block.size(); i++)
    {
        auto x = change_block[i].x, y = change_block[i].y, z = change_block[i].z;
        blockMeshBlend( blocks[z][y][x] );
    }

    //for (uint z = 0; z < blocks.size(); z++)
    //    for (uint y = 0; y < blocks[z].size(); y++)
    //        for (uint x = 0; x < blocks[z][y].size(); x++)
    //        {
    //            if (blocks[z][y][x]->mIsActive)
    //                blockMeshBlend( blocks[z][y][x],
    //                                  std::any_of( change_block.begin(), change_block.end(),
    //                                               [&]( ivec3 n ) { return n == ivec3( x, y, z ); } ) );
    //        }
    createMesh();
    //updateVboMesh();
    return true;
}
void cUnderGround::blockMeshBlend( std::shared_ptr<cBlock> b, bool is_side_create )
{
    int max = 0;
    if (mIndices.size() > 0)
        max = *std::max_element( mIndices.begin(), mIndices.end() ) + 1;
    if (is_side_create)
        b->createSide( max );

    // vertices, indices, uvs, normals@킹
    std::copy( b->mVertices.begin(), b->mVertices.end(), std::back_inserter( mVertices ) );
    std::copy( b->mIndices.begin(), b->mIndices.end(), std::back_inserter( mIndices ) );
    std::copy( b->mUv.begin(), b->mUv.end(), std::back_inserter( mUv ) );
    std::copy( b->mNormals.begin(), b->mNormals.end(), std::back_inserter( mNormals ) );
}
bool cUnderGround::createMesh()
{
    mMesh = ci::TriMesh::create();
    if (mVertices.size() > 0)
        mMesh->appendPositions( &mVertices[0], mVertices.size() );
    if (mIndices.size() > 0)
        mMesh->appendIndices( &mIndices[0], mIndices.size() );
    if (mUv.size() > 0)
        mMesh->appendTexCoords0( &mUv[0], mUv.size() );
    if (mNormals.size() > 0)
        mMesh->appendNormals( &mNormals[0], mNormals.size() );
    mVboMesh = gl::VboMesh::create( *mMesh );
    return true;
}
void cUnderGround::blockClear()
{
    mVertices.clear();
    mIndices.clear();
    mUv.clear();
    mNormals.clear();
    mMesh->clear();
}
void cUnderGround::updateVboMesh()
{
    mVboMesh->updateNumIndices( mIndices.size() );
    mVboMesh->updateNumVertices( mVertices.size() );
}
bool cUnderGround::blockBreak( const ci::vec3& position, const float& radius )
{
    auto p = ivec3( position / scale );

    if (isOutOfRange( p ))
        return false;
    blocks[p.z][p.y][p.x]->toBreak();
    //blockDigged( p );

    auto r = ivec3( int( radius / scale ) );
    auto s = p - r;
    auto e = p + r;

    for (int z = s.z; z < e.z; z++)
        for (int y = s.y; y < e.y; y++)
            for (int x = s.x; x < e.x; x++)
            {
                if (isOutOfRange( ivec3( x, y, z ) ))
                    continue;
                blocks[z][y][x]->toBreak();
                //blockDigged( ivec3( x, y, z ) );
            }
    return true;
}
}
}
<commit_msg>重かったのでブロックの数を16×1×1×16にしました<commit_after>#include <Game/Field/cUnderGround.h>
#include <Resource/TextureManager.h>

using namespace ci;
using namespace ci::app;

namespace Game
{
namespace Field
{
cUnderGround::cUnderGround()
{
}
cUnderGround::~cUnderGround()
{
}
void cUnderGround::setup()
{
    TEX.set( "dirt", "dirt.jpg" );

    num = 16;
    height = 1;
    offset = 0.0f;
    scale = 1.0f;
    uint count = 0;
    auto offset_position = vec3( -scale / 2 );

    for (int z = 0; z < num; z++)
    {
        std::vector<std::vector<std::shared_ptr<cBlock>>> temps;
        for (int y = 0; y < height; y++)
        {
            std::vector<std::shared_ptr<cBlock>> temp;
            for (int x = 0; x < num; x++)
            {
                auto position = vec3( x * scale + x * offset,
                                      y * -scale + y * offset,
                                      z * scale + z * offset ) + offset_position;
                std::shared_ptr<cBlock> block = std::make_shared<cBlock>( position, scale, count );

                // Ƃ肠[ȊO̕`؂
                //bool is_active = false;
                //if (z == num - 1 || z == 0 ||
                //     y == num - 1 || y == 0 ||
                //     x == num - 1 || x == 0)
                //    is_active = true;

                block->mIsActive = true;

                // bV𐶐߂
                //std::vector<int> draw_side;
                //if (z == num - 1)
                //    draw_side.push_back( 0 );
                //if (z == 0)
                //    draw_side.push_back( 1 );
                //if (x == num - 1)
                //    draw_side.push_back( 2 );
                //if (x == 0)
                //    draw_side.push_back( 3 );
                //if (y == num - 1)
                //    draw_side.push_back( 4 );
                //if (y == 0)
                //    draw_side.push_back( 5 );

                //block->mDrawSide = draw_side;
                //blockMeshBlend( block );

                temp.emplace_back( std::move( block ) );
            }
            temps.emplace_back( std::move( temp ) );
            temp.clear();
        }
        blocks.emplace_back( std::move( temps ) );
        temps.clear();
    }

    createMesh();
    //blocks[3][0][0]->mIsActive = false;
    //blockDigged( ivec3( 0, 0, 3 ) );

    //blocks[3][0][3]->mIsActive = false;
    //blockDigged( ivec3( 3, 0, 3 ) );

    //blocks[4][num - 1][4]->mIsActive = false;
    //blockDigged( ivec3( 4, num - 1, 4 ) );

    //blocks[num - 1][4][4]->mIsActive = false;
    //blockDigged( ivec3( 4, 4, num - 1 ) );

    //blocks[num - 1][num - 1][num - 1]->mIsActive = false;
    //blockDigged( ivec3( num - 1, num - 1, num - 1 ) );
}
void cUnderGround::update()
{
}
void cUnderGround::draw()
{
    auto texture = TEX.get( "dirt" );
    if (!texture)
        return;
    using namespace ci::gl;

    auto ctx = context();

    Rectf texRect = texture->getAreaTexCoords( Area( vec2( 0 ), texture->getSize() ) );

    ScopedVao vaoScp( ctx->getDrawTextureVao() );
    ScopedBuffer vboScp( ctx->getDrawTextureVbo() );
    ScopedTextureBind texBindScope( texture );

    auto glsl = getStockShader( ShaderDef().uniformBasedPosAndTexCoord().color().texture( texture ) );
    ScopedGlslProg glslScp( glsl );
    glsl->uniform( "uTex0", 0 );
    glsl->uniform( "uPositionOffset", vec2( 0 ) );
    glsl->uniform( "uPositionScale", vec2( 1 ) );
    glsl->uniform( "uTexCoordOffset", texRect.getUpperLeft() );
    glsl->uniform( "uTexCoordScale", texRect.getSize() );

    for (uint z = 0; z < blocks.size(); z++)
        for (uint y = 0; y < blocks[z].size(); y++)
            for (uint x = 0; x < blocks[z][y].size(); x++)
                blocks[z][y][x]->draw();

    //gl::draw( mVboMesh );
}
bool cUnderGround::isOutOfRange( const ci::ivec3& c )
{
    return  (uint) c.z < 0 || (uint) c.z > blocks.size() - 1 ||
        (uint) c.y < 0 || (uint) c.y > blocks[c.z].size() - 1 ||
        (uint) c.x < 0 || (uint) c.x > blocks[c.z][c.y].size() - 1;
}
bool cUnderGround::blockDigged( const ci::ivec3& c )
{
    if (isOutOfRange( c ))
        return false;

    // Ƃ肠ꂩtB[h蒼
    //blockClear();

    ivec3 dir[] = {
        ivec3( 0, 0, 1 ),
        ivec3( 0, 0,-1 ) ,
        ivec3( 1, 0, 0 ) ,
        ivec3( -1, 0, 0 ) ,
        ivec3( 0, 1, 0 ) ,
        ivec3( 0,-1, 0 ) };

    std::vector<ivec3> change_block;

    // ubN̎6}X̃ubÑbV蒼
    for (int i = 0; i < 6; i++)
    {
        auto cell = c + dir[i];
        if (isOutOfRange( cell ))
            continue;

        // ubN݂ĂȂ͂
        auto b = blocks[cell.z][cell.y][cell.x];
        if (b->mIsActive == false)
            continue;

        std::vector<int> side;
        for (int k = 0; k < 6; k++)
        {
            auto search_cell = cell + dir[k];
            if (isOutOfRange( search_cell ))
                side.push_back( k );
            else if (blocks[search_cell.z][search_cell.y][search_cell.x]->mIsActive == false)
                side.push_back( k );
        }
        b->clear();
        b->mDrawSide = side;
        // bVɕύXubNo^
        change_block.push_back( cell );
    }

    for (uint i = 0; i < change_block.size(); i++)
    {
        auto x = change_block[i].x, y = change_block[i].y, z = change_block[i].z;
        blockMeshBlend( blocks[z][y][x] );
    }

    //for (uint z = 0; z < blocks.size(); z++)
    //    for (uint y = 0; y < blocks[z].size(); y++)
    //        for (uint x = 0; x < blocks[z][y].size(); x++)
    //        {
    //            if (blocks[z][y][x]->mIsActive)
    //                blockMeshBlend( blocks[z][y][x],
    //                                  std::any_of( change_block.begin(), change_block.end(),
    //                                               [&]( ivec3 n ) { return n == ivec3( x, y, z ); } ) );
    //        }
    createMesh();
    //updateVboMesh();
    return true;
}
void cUnderGround::blockMeshBlend( std::shared_ptr<cBlock> b, bool is_side_create )
{
    int max = 0;
    if (mIndices.size() > 0)
        max = *std::max_element( mIndices.begin(), mIndices.end() ) + 1;
    if (is_side_create)
        b->createSide( max );

    // vertices, indices, uvs, normals@킹
    std::copy( b->mVertices.begin(), b->mVertices.end(), std::back_inserter( mVertices ) );
    std::copy( b->mIndices.begin(), b->mIndices.end(), std::back_inserter( mIndices ) );
    std::copy( b->mUv.begin(), b->mUv.end(), std::back_inserter( mUv ) );
    std::copy( b->mNormals.begin(), b->mNormals.end(), std::back_inserter( mNormals ) );
}
bool cUnderGround::createMesh()
{
    mMesh = ci::TriMesh::create();
    if (mVertices.size() > 0)
        mMesh->appendPositions( &mVertices[0], mVertices.size() );
    if (mIndices.size() > 0)
        mMesh->appendIndices( &mIndices[0], mIndices.size() );
    if (mUv.size() > 0)
        mMesh->appendTexCoords0( &mUv[0], mUv.size() );
    if (mNormals.size() > 0)
        mMesh->appendNormals( &mNormals[0], mNormals.size() );
    mVboMesh = gl::VboMesh::create( *mMesh );
    return true;
}
void cUnderGround::blockClear()
{
    mVertices.clear();
    mIndices.clear();
    mUv.clear();
    mNormals.clear();
    mMesh->clear();
}
void cUnderGround::updateVboMesh()
{
    mVboMesh->updateNumIndices( mIndices.size() );
    mVboMesh->updateNumVertices( mVertices.size() );
}
bool cUnderGround::blockBreak( const ci::vec3& position, const float& radius )
{
    auto p = ivec3( position / scale );

    if (isOutOfRange( p ))
        return false;
    blocks[p.z][p.y][p.x]->toBreak();
    //blockDigged( p );

    auto r = ivec3( int( radius / scale ) );
    auto s = p - r;
    auto e = p + r;

    for (int z = s.z; z < e.z; z++)
        for (int y = s.y; y < e.y; y++)
            for (int x = s.x; x < e.x; x++)
            {
                if (isOutOfRange( ivec3( x, y, z ) ))
                    continue;
                blocks[z][y][x]->toBreak();
                //blockDigged( ivec3( x, y, z ) );
            }
    return true;
}
}
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>

#include "ac.h"
#include "ac_util.hpp"
#include "test_base.hpp"

using namespace std;

namespace {
typedef struct {
    const char* str;
    const char* match;
} StrPair;

typedef enum {
    MV_FIRST_MATCH = 0,
    MV_LEFT_LONGEST = 1,
} MatchVariant;

typedef struct {
    const char* name;
    const char** dict;
    StrPair* strpairs;
    int dict_len;
    int strpair_num;
    MatchVariant match_variant;
} TestingCase;

class Tests {
public:
    Tests(const char* name,
          const char* dict[], int dict_len,
          StrPair strpairs[], int strpair_num,
          MatchVariant mv = MV_FIRST_MATCH) {
        if (!_tests)
            _tests = new vector<TestingCase>;

        TestingCase tc;
        tc.name = name;
        tc.dict = dict;
        tc.strpairs = strpairs;
        tc.dict_len = dict_len;
        tc.strpair_num = strpair_num;
        tc.match_variant = mv;
        _tests->push_back(tc);
    }

    static vector<TestingCase>* Get_Tests() { return _tests; }
    static void Erase_Tests() { delete _tests; _tests = 0; }

private:
    static vector<TestingCase> *_tests;
};

class LeftLongestTests : public Tests {
public:
    LeftLongestTests (const char* name, const char* dict[], int dict_len,
                      StrPair strpairs[], int strpair_num):
        Tests(name, dict, dict_len, strpairs, strpair_num, MV_LEFT_LONGEST) {
    }
};

vector<TestingCase>* Tests::_tests = 0;

class ACTestSimple: public ACTestBase {
public:
    ACTestSimple(const char* banner) : ACTestBase(banner) {}
    virtual bool Run();

private:
    void PrintSummary(int total, int fail)  {
        fprintf(stdout, "Test count : %d, fail: %d\n", total, fail);
        fflush(stdout);
    }
};
}

bool
ACTestSimple::Run() {
    int total = 0;
    int fail = 0;

    vector<TestingCase> *tests = Tests::Get_Tests();
    if (!tests) {
        PrintSummary(0, 0);
        return true;
    }

    for (vector<TestingCase>::iterator i = tests->begin(), e = tests->end();
            i != e; i++) {
        TestingCase& t = *i;
        int dict_len = t.dict_len;
        unsigned int* strlen_v = new unsigned int[dict_len];

        fprintf(stdout, ">Testing %s\nDictionary:[ ", t.name);
        for (int i = 0, need_break=0; i < dict_len; i++) {
            const char* s = t.dict[i];
            fprintf(stdout, "%s, ", s);
            strlen_v[i] = strlen(s);
            if (need_break++ == 16) {
                fputs("\n  ", stdout);
                need_break = 0;
            }
        }
        fputs("]\n", stdout);

        /* Create the dictionary */
        ac_t* ac = ac_create(t.dict, strlen_v, dict_len);
        delete[] strlen_v;

        for (int ii = 0, ee = t.strpair_num; ii < ee; ii++, total++) {
            const StrPair& sp = t.strpairs[ii];
            const char *str = sp.str; // the string to be matched
            const char *match = sp.match;

            fprintf(stdout, "[%3d] Testing '%s' : ", total, str);

            int len = strlen(str);
            ac_result_t r;
            if (t.match_variant == MV_FIRST_MATCH)
                r = ac_match(ac, str, len);
            else if (t.match_variant == MV_LEFT_LONGEST)
                r = ac_match_longest_l(ac, str, len);
            else {
                ASSERT(false && "Unknown variant");
            }

            int m_b = r.match_begin;
            int m_e = r.match_end;

            // The return value per se is insane.
            if (m_b > m_e ||
                ((m_b < 0 || m_e < 0) && (m_b != -1 || m_e != -1))) {
                fprintf(stdout, "Insane return value (%d, %d)\n", m_b, m_e);
                fail ++;
                continue;
            }

            // If the string is not supposed to match the dictionary.
            if (!match) {
                if (m_b != -1 || m_e != -1) {
                    fail ++;
                    fprintf(stdout, "Not Supposed to match (%d, %d) \n",
                            m_b, m_e);
                } else
                    fputs("Pass\n", stdout);
                continue;
            }

            // The string or its substring is match the dict.
            if (m_b >= len || m_b >= len) {
                fail ++;
                fprintf(stdout,
                        "Return value >= the length of the string (%d, %d)\n",
                        m_b, m_e);
                continue;
            } else {
                int mlen = strlen(match);
                if ((mlen != m_e - m_b + 1) ||
                    strncmp(str + m_b, match, mlen)) {
                    fail ++;
                    fprintf(stdout, "Fail\n");
                } else
                    fprintf(stdout, "Pass\n");
            }
        }
        fputs("\n", stdout);
        ac_free(ac);
    }

    PrintSummary(total, fail);
    return fail == 0;
}

bool
Run_AC_Simple_Test() {
    ACTestSimple t("AC Simple test");
    t.PrintBanner();
    return t.Run();
}

//////////////////////////////////////////////////////////////////////////////
//
//    Testing cases for first-match variant (i.e. test ac_match())
//
//////////////////////////////////////////////////////////////////////////////
//

/* test 1*/
const char *dict1[] = {"he", "she", "his", "her"};
StrPair strpair1[] = {
    {"he", "he"}, {"she", "she"}, {"his", "his"},
    {"hers", "he"}, {"ahe", "he"}, {"shhe", "he"},
    {"shis2", "his"}, {"ahhe", "he"}
};
Tests test1("test 1",
            dict1, sizeof(dict1)/sizeof(dict1[0]),
            strpair1, sizeof(strpair1)/sizeof(strpair1[0]));

/* test 2*/
const char *dict2[] = {"poto", "poto"}; /* duplicated strings*/
StrPair strpair2[] = {{"The pot had a handle", 0}};
Tests test2("test 2", dict2, 2, strpair2, 1);

/* test 3*/
const char *dict3[] = {"The"};
StrPair strpair3[] = {{"The pot had a handle", "The"}};
Tests test3("test 3", dict3, 1, strpair3, 1);

/* test 4*/
const char *dict4[] = {"pot"};
StrPair strpair4[] = {{"The pot had a handle", "pot"}};
Tests test4("test 4", dict4, 1, strpair4, 1);

/* test 5*/
const char *dict5[] = {"pot "};
StrPair strpair5[] = {{"The pot had a handle", "pot "}};
Tests test5("test 5", dict5, 1, strpair5, 1);

/* test 6*/
const char *dict6[] = {"ot h"};
StrPair strpair6[] = {{"The pot had a handle", "ot h"}};
Tests test6("test 6", dict6, 1, strpair6, 1);

/* test 7*/
const char *dict7[] = {"andle"};
StrPair strpair7[] = {{"The pot had a handle", "andle"}};
Tests test7("test 7", dict7, 1, strpair7, 1);

const char *dict8[] = {"aaab"};
StrPair strpair8[] = {{"aaaaaaab", "aaab"}};
Tests test8("test 8", dict8, 1, strpair8, 1);

const char *dict9[] = {"haha", "z"};
StrPair strpair9[] = {{"aaaaz", "z"}, {"z", "z"}};
Tests test9("test 9", dict9, 2, strpair9, 2);

/* test the case when input string dosen't contain even a single char
 * of the pattern in dictionary.
 */
const char *dict10[] = {"abc"};
StrPair strpair10[] = {{"cde", 0}};
Tests test10("test 10", dict10, 1, strpair10, 1);

//////////////////////////////////////////////////////////////////////////////
//
//    Testing cases for first longest match variant (i.e.
// test ac_match_longest_l())
//
//////////////////////////////////////////////////////////////////////////////
//

// This was actually first motivation for left-longest-match
const char *dict100[] = {"Mozilla", "Mozilla Mobile"};
StrPair strpair100[] = {{"User Agent containing string Mozilla Mobile", "Mozilla Mobile"}};
LeftLongestTests test100("l_test 100", dict100, 2, strpair100, 1);

// Dict with single char is tricky
const char *dict101[] = {"a", "abc"};
StrPair strpair101[] = {{"abcdef", "abc"}};
LeftLongestTests test101("l_test 101", dict101, 2, strpair101, 1);
<commit_msg>Add a unit-test for longest-match-variant.<commit_after>#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>

#include "ac.h"
#include "ac_util.hpp"
#include "test_base.hpp"

using namespace std;

namespace {
typedef struct {
    const char* str;
    const char* match;
} StrPair;

typedef enum {
    MV_FIRST_MATCH = 0,
    MV_LEFT_LONGEST = 1,
} MatchVariant;

typedef struct {
    const char* name;
    const char** dict;
    StrPair* strpairs;
    int dict_len;
    int strpair_num;
    MatchVariant match_variant;
} TestingCase;

class Tests {
public:
    Tests(const char* name,
          const char* dict[], int dict_len,
          StrPair strpairs[], int strpair_num,
          MatchVariant mv = MV_FIRST_MATCH) {
        if (!_tests)
            _tests = new vector<TestingCase>;

        TestingCase tc;
        tc.name = name;
        tc.dict = dict;
        tc.strpairs = strpairs;
        tc.dict_len = dict_len;
        tc.strpair_num = strpair_num;
        tc.match_variant = mv;
        _tests->push_back(tc);
    }

    static vector<TestingCase>* Get_Tests() { return _tests; }
    static void Erase_Tests() { delete _tests; _tests = 0; }

private:
    static vector<TestingCase> *_tests;
};

class LeftLongestTests : public Tests {
public:
    LeftLongestTests (const char* name, const char* dict[], int dict_len,
                      StrPair strpairs[], int strpair_num):
        Tests(name, dict, dict_len, strpairs, strpair_num, MV_LEFT_LONGEST) {
    }
};

vector<TestingCase>* Tests::_tests = 0;

class ACTestSimple: public ACTestBase {
public:
    ACTestSimple(const char* banner) : ACTestBase(banner) {}
    virtual bool Run();

private:
    void PrintSummary(int total, int fail)  {
        fprintf(stdout, "Test count : %d, fail: %d\n", total, fail);
        fflush(stdout);
    }
};
}

bool
ACTestSimple::Run() {
    int total = 0;
    int fail = 0;

    vector<TestingCase> *tests = Tests::Get_Tests();
    if (!tests) {
        PrintSummary(0, 0);
        return true;
    }

    for (vector<TestingCase>::iterator i = tests->begin(), e = tests->end();
            i != e; i++) {
        TestingCase& t = *i;
        int dict_len = t.dict_len;
        unsigned int* strlen_v = new unsigned int[dict_len];

        fprintf(stdout, ">Testing %s\nDictionary:[ ", t.name);
        for (int i = 0, need_break=0; i < dict_len; i++) {
            const char* s = t.dict[i];
            fprintf(stdout, "%s, ", s);
            strlen_v[i] = strlen(s);
            if (need_break++ == 16) {
                fputs("\n  ", stdout);
                need_break = 0;
            }
        }
        fputs("]\n", stdout);

        /* Create the dictionary */
        ac_t* ac = ac_create(t.dict, strlen_v, dict_len);
        delete[] strlen_v;

        for (int ii = 0, ee = t.strpair_num; ii < ee; ii++, total++) {
            const StrPair& sp = t.strpairs[ii];
            const char *str = sp.str; // the string to be matched
            const char *match = sp.match;

            fprintf(stdout, "[%3d] Testing '%s' : ", total, str);

            int len = strlen(str);
            ac_result_t r;
            if (t.match_variant == MV_FIRST_MATCH)
                r = ac_match(ac, str, len);
            else if (t.match_variant == MV_LEFT_LONGEST)
                r = ac_match_longest_l(ac, str, len);
            else {
                ASSERT(false && "Unknown variant");
            }

            int m_b = r.match_begin;
            int m_e = r.match_end;

            // The return value per se is insane.
            if (m_b > m_e ||
                ((m_b < 0 || m_e < 0) && (m_b != -1 || m_e != -1))) {
                fprintf(stdout, "Insane return value (%d, %d)\n", m_b, m_e);
                fail ++;
                continue;
            }

            // If the string is not supposed to match the dictionary.
            if (!match) {
                if (m_b != -1 || m_e != -1) {
                    fail ++;
                    fprintf(stdout, "Not Supposed to match (%d, %d) \n",
                            m_b, m_e);
                } else
                    fputs("Pass\n", stdout);
                continue;
            }

            // The string or its substring is match the dict.
            if (m_b >= len || m_b >= len) {
                fail ++;
                fprintf(stdout,
                        "Return value >= the length of the string (%d, %d)\n",
                        m_b, m_e);
                continue;
            } else {
                int mlen = strlen(match);
                if ((mlen != m_e - m_b + 1) ||
                    strncmp(str + m_b, match, mlen)) {
                    fail ++;
                    fprintf(stdout, "Fail\n");
                } else
                    fprintf(stdout, "Pass\n");
            }
        }
        fputs("\n", stdout);
        ac_free(ac);
    }

    PrintSummary(total, fail);
    return fail == 0;
}

bool
Run_AC_Simple_Test() {
    ACTestSimple t("AC Simple test");
    t.PrintBanner();
    return t.Run();
}

//////////////////////////////////////////////////////////////////////////////
//
//    Testing cases for first-match variant (i.e. test ac_match())
//
//////////////////////////////////////////////////////////////////////////////
//

/* test 1*/
const char *dict1[] = {"he", "she", "his", "her"};
StrPair strpair1[] = {
    {"he", "he"}, {"she", "she"}, {"his", "his"},
    {"hers", "he"}, {"ahe", "he"}, {"shhe", "he"},
    {"shis2", "his"}, {"ahhe", "he"}
};
Tests test1("test 1",
            dict1, sizeof(dict1)/sizeof(dict1[0]),
            strpair1, sizeof(strpair1)/sizeof(strpair1[0]));

/* test 2*/
const char *dict2[] = {"poto", "poto"}; /* duplicated strings*/
StrPair strpair2[] = {{"The pot had a handle", 0}};
Tests test2("test 2", dict2, 2, strpair2, 1);

/* test 3*/
const char *dict3[] = {"The"};
StrPair strpair3[] = {{"The pot had a handle", "The"}};
Tests test3("test 3", dict3, 1, strpair3, 1);

/* test 4*/
const char *dict4[] = {"pot"};
StrPair strpair4[] = {{"The pot had a handle", "pot"}};
Tests test4("test 4", dict4, 1, strpair4, 1);

/* test 5*/
const char *dict5[] = {"pot "};
StrPair strpair5[] = {{"The pot had a handle", "pot "}};
Tests test5("test 5", dict5, 1, strpair5, 1);

/* test 6*/
const char *dict6[] = {"ot h"};
StrPair strpair6[] = {{"The pot had a handle", "ot h"}};
Tests test6("test 6", dict6, 1, strpair6, 1);

/* test 7*/
const char *dict7[] = {"andle"};
StrPair strpair7[] = {{"The pot had a handle", "andle"}};
Tests test7("test 7", dict7, 1, strpair7, 1);

const char *dict8[] = {"aaab"};
StrPair strpair8[] = {{"aaaaaaab", "aaab"}};
Tests test8("test 8", dict8, 1, strpair8, 1);

const char *dict9[] = {"haha", "z"};
StrPair strpair9[] = {{"aaaaz", "z"}, {"z", "z"}};
Tests test9("test 9", dict9, 2, strpair9, 2);

/* test the case when input string dosen't contain even a single char
 * of the pattern in dictionary.
 */
const char *dict10[] = {"abc"};
StrPair strpair10[] = {{"cde", 0}};
Tests test10("test 10", dict10, 1, strpair10, 1);

//////////////////////////////////////////////////////////////////////////////
//
//    Testing cases for first longest match variant (i.e.
// test ac_match_longest_l())
//
//////////////////////////////////////////////////////////////////////////////
//

// This was actually first motivation for left-longest-match
const char *dict100[] = {"Mozilla", "Mozilla Mobile"};
StrPair strpair100[] = {{"User Agent containing string Mozilla Mobile", "Mozilla Mobile"}};
LeftLongestTests test100("l_test 100", dict100, 2, strpair100, 1);

// Dict with single char is tricky
const char *dict101[] = {"a", "abc"};
StrPair strpair101[] = {{"abcdef", "abc"}};
LeftLongestTests test101("l_test 101", dict101, 2, strpair101, 1);

// Testing case with partially overlapping patterns. The purpose is to
// check if the fail-link leading from terminal state is correct.
//
// The fail-link leading from terminal-state does not matter in
// match-first-occurrence variant, as it stop when a terminal is hit.
//
const char *dict102[] = {"abc", "bcdef"};
StrPair strpair102[] = {{"abcdef", "bcdef"}};
LeftLongestTests test102("l_test 102", dict102, 2, strpair102, 1);
<|endoftext|>


<commit_before>/**
 * @author pinkasfeld joseph
 * Copyright (c) Aldebaran Robotics 2010, 2011 All Rights Reserved.
 */

#include "allaser.h"

#include <iostream>
#include <alcommon/alproxy.h>
#include <alproxies/almemoryproxy.h>
#include <boost/shared_ptr.hpp>
#include <alcommon/albroker.h>
#include <alcommon/almodule.h>
#include <sstream>

#include <pthread.h>
#include <signal.h>
#include <cmath>

#include <qi/log.hpp>

#if defined (__linux__)
#include <sys/prctl.h>
#endif

extern "C" {
# include "urg_ctrl.h"
# include "scip_handler.h"
}

#define MODE_ON 0
#define MODE_OFF 1
#define SEND_MODE_ON 2
#define SEND_MODE_OFF 3

#define MIDDLE_ANGLE 384
#define DEFAULT_MIN_ANGLE 43
#define DEFAULT_MAX_ANGLE 725
#define MIN_ANGLE_LASER 43
#define MAX_ANGLE_LASER 725
#define RESOLUTION_LASER 1024
#define MIN_LENGTH_LASER 20
#define MAX_LENGTH_LASER 5600

#include <sys/time.h>

using namespace AL;
using namespace std;
pthread_t urgThreadId;
boost::shared_ptr<ALMemoryProxy> gSTM;
static int mode = MODE_ON;
static urg_t urg;
static int angle_min = DEFAULT_MIN_ANGLE;
static int angle_max = DEFAULT_MAX_ANGLE;
static int length_min = MIN_LENGTH_LASER;
static int length_max = MAX_LENGTH_LASER;

static void urg_exit(urg_t *urg, const char *message) {
  qiLogInfo("hardware.allaser", "%s: %s\n", message, urg_error(urg));
  urg_disconnect(urg);
  qiLogInfo("hardware.allaser", "urg_exit\n");
  pthread_exit((void *)NULL);
}

#define URG_DEFAULT_SPEED (19200)
#define URG_FAST_SPEED (500000)

void * urgThread(void * arg) {

#if defined (__linux__)
  // thread name
  prctl(PR_SET_NAME, "ALLaser::urgThread", 0, 0, 0);
#endif

  ALValue urgdata;
  long *data = NULL;
  int data_max;
  int ret;
  int n;
  int i,imemory,refTime,end,sampleTime, beginThread;
  std::string valueName="Device/Laser/Value";

  /* Connection */

  connectToLaser();

  /* Reserve the Receive data buffer */
  data_max = urg_dataMax(&urg);
  data = (long*)malloc(sizeof(long) * data_max);
  if (data == NULL) {
    perror("data buffer");
    pthread_exit((void *)NULL);
  }
  /* prepare ALValue for ALMemory*/
  urgdata.arraySetSize(data_max);
  for (i=0; i< data_max;i++)
  {
    urgdata[i].arraySetSize(4);
    urgdata[i][0]= (int)0;
    urgdata[i][1]= (double)0.0;
    urgdata[i][2]= (int)0;
    urgdata[i][3]= (int)0;
  }
  /* insert ALvalue in ALMemory*/

  gSTM->insertData(valueName,urgdata);

  gSTM->insertData("Device/Laser/LaserEnable",(bool)1);
  gSTM->insertData("Device/Laser/MinAngle",(float)((2.0 * M_PI)*(DEFAULT_MIN_ANGLE - MIDDLE_ANGLE) / RESOLUTION_LASER));
  gSTM->insertData("Device/Laser/MaxAngle",(float)((2.0 * M_PI)*(DEFAULT_MAX_ANGLE - MIDDLE_ANGLE) / RESOLUTION_LASER));
  gSTM->insertData("Device/Laser/MinLength",(float)(length_min));
  gSTM->insertData("Device/Laser/MaxLength",(float)(length_max));

  printf("ALLASER Running\n");

  while(1)
  {
    if(mode==SEND_MODE_ON){
      ret = urg_laserOn(&urg);
      if (ret < 0) {
        urg_exit(&urg, "urg_requestData()");
      }
      mode=MODE_ON;
      /* Connection */
      connectToLaser();
    }
    if(mode==SEND_MODE_OFF){
      scip_qt(&urg.serial_, NULL, ScipNoWaitReply);
      mode=MODE_OFF;
    }
    if(mode==MODE_ON){
      /* Request Data using GD-Command */
      ret = urg_requestData(&urg, URG_GD, angle_min, angle_max);
      if (ret < 0) {
        urg_exit(&urg, "urg_requestData()");
      }

      refTime = getLocalTime();
      /* Obtain Data */
      n = urg_receiveData(&urg, data, data_max);
      if (n < 0) {
        urg_exit(&urg, "urg_receiveData()");
      }
      end= getLocalTime();
      sampleTime=end-refTime;

      imemory=0;
      for (i = 0; i < n; ++i) {
        int x, y;
        double angle=index2rad(i);
        int length = data[i];

        if((length>=length_min)&&(length<=length_max)){
          x = (int)((double)length * cos(angle));
          y = (int)((double)length * sin(angle));
          urgdata[imemory][0]= length;
          urgdata[imemory][1]= angle;
          urgdata[imemory][2]= x;
          urgdata[imemory][3]= y;
          imemory++;
        }
      }
      for(;imemory<data_max;imemory++){
        urgdata[imemory][0]= 0;
        urgdata[imemory][1]= 0;
        urgdata[imemory][2]= 0;
        urgdata[imemory][3]= 0;
      }
      gSTM->insertData(valueName,urgdata);
      usleep(1000);
    }else{
      usleep(10000);
    }
  }
  urg_disconnect(&urg);

  free(data);
}

//______________________________________________
// constructor
//______________________________________________
ALLaser::ALLaser(boost::shared_ptr<ALBroker> pBroker, const std::string& pName ): ALModule(pBroker, pName )
{
  setModuleDescription( "Allow control over Hokuyo laser when available on Nao's head." );

  functionName("laserOFF", "ALLaser", "Disable laser light");
  BIND_METHOD( ALLaser::laserOFF );

  functionName("laserON", "ALLaser", "Enable laser light and sampling");
  BIND_METHOD( ALLaser::laserON );

  functionName("setOpeningAngle", "ALLaser", "Set openning angle of the laser");
  addParam("angle_min_f", "float containing the min value in rad, this value must be upper than -2.35619449 ");
  addParam("angle_max_f", "float containing the max value in rad, this value must be lower than 2.092349795 ");
  addMethodExample( "python",
        "# Set the opening angle at -90/90 degres\n"
        "laser = ALProxy(\"ALLaser\",\"127.0.0.1\",9559)\n"
        "laser.setOpeningAngle(-1.570796327,1.570796327)\n"
      );
  BIND_METHOD( ALLaser::setOpeningAngle );

  functionName("setDetectingLength", "ALLaser", "Set detection threshold of the laser");
  addParam("length_min_l", "int containing the min length that the laser will detect(mm), this value must be upper than 20 mm");
  addParam("length_max_l", "int containing the max length that the laser will detect(mm), this value must be lower than 5600 mm");
  addMethodExample( "python",
        "# Set detection threshold at 500/3000 mm\n"
        "laser = ALProxy(\"ALLaser\",\"127.0.0.1\",9559)\n"
        "laser.setDetectingLength(500,3000)\n"
      );
  BIND_METHOD( ALLaser::setDetectingLength );

  // get broker on DCM and ALMemory
  try {
    gSTM = getParentBroker()->getMemoryProxy();
  } catch(ALError& e) {
    qiLogError("hardware.allaser") << "Could not connect to Memory. Error: " << e.what() << std::endl;
  }

  pthread_create(&urgThreadId, NULL, urgThread, NULL);
}

//______________________________________________
// destructor
//______________________________________________
ALLaser::~ALLaser()
{
  pthread_cancel(urgThreadId);
}

void ALLaser::laserOFF(void){
  mode = SEND_MODE_OFF;
  gSTM->insertData("Device/Laser/LaserEnable",(bool)0);
}

void ALLaser::laserON(void){
  mode = SEND_MODE_ON;
  gSTM->insertData("Device/Laser/LaserEnable",(bool)1);
}

void ALLaser::setOpeningAngle(const AL::ALValue& angle_min_f, const AL::ALValue& angle_max_f){
  angle_min = MIDDLE_ANGLE + (int)(RESOLUTION_LASER*(float)angle_min_f / (2.0 * M_PI));
  angle_max = MIDDLE_ANGLE + (int)(RESOLUTION_LASER*(float)angle_max_f / (2.0 * M_PI));
  if(angle_min<MIN_ANGLE_LASER) angle_min = MIN_ANGLE_LASER;
  if(angle_max>MAX_ANGLE_LASER) angle_max = MAX_ANGLE_LASER;
  if(angle_min>=angle_max){
    angle_min = MIN_ANGLE_LASER;
    angle_max = MAX_ANGLE_LASER;
  }
  gSTM->insertData("Device/Laser/MinAngle",(float)((2.0 * M_PI) *(angle_min - MIDDLE_ANGLE) / RESOLUTION_LASER));
  gSTM->insertData("Device/Laser/MaxAngle",(float)((2.0 * M_PI) *(angle_max - MIDDLE_ANGLE) / RESOLUTION_LASER));
}

void ALLaser::setDetectingLength(const AL::ALValue& length_min_l,const AL::ALValue& length_max_l){
  length_min=(int)length_min_l;
  length_max=(int)length_max_l;
  if(length_min<MIN_LENGTH_LASER) length_min = MIN_LENGTH_LASER;
  if(length_max>MAX_LENGTH_LASER) length_min = MAX_LENGTH_LASER;
  if(length_min>=length_max){
    length_min = MIN_LENGTH_LASER;
    length_max = MAX_LENGTH_LASER;
  }
  gSTM->insertData("Device/Laser/MinLength",(int)length_min);
  gSTM->insertData("Device/Laser/MaxLength",(int)length_max);
}

//_________________________________________________
// Service functions
//_________________________________________________

int connectToLaserViaUSB(void) {
  int success = -1;
  const char deviceUSB[] = "/dev/ttyUSB0"; /* Example when using Linux  */
  // Try to connect at low speed.
  success = urg_connect(&urg, deviceUSB, URG_DEFAULT_SPEED);
  if (success < 0) {
    std::stringstream ss;
    ss << "Connection failure to " << deviceUSB << " at " << URG_DEFAULT_SPEED << ". " << urg_error(&urg);
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    return success;
  }
  else {
    // Try to switch to full speed.
    scip_ss(&urg.serial_, URG_FAST_SPEED);
    urg_disconnect(&urg);
    success = urg_connect(&urg, deviceUSB, URG_FAST_SPEED);
    if (success < 0) {
      std::stringstream ss;
      ss << "Connection failure to " << deviceUSB << " at " << URG_FAST_SPEED << ". " << urg_error(&urg);
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
      // Fall back to low speed.
      success = urg_connect(&urg, deviceUSB, URG_DEFAULT_SPEED);
      return success;
    }
    else {
      return success;
    }
  }
}

int connectToLaserViaACM(void) {
  int success = -1;
  const char deviceACM[] = "/dev/ttyACM0"; /* Example when using Linux  */
  success = urg_connect(&urg, deviceACM, URG_DEFAULT_SPEED);
  if (success < 0) {
    std::stringstream ss;
    ss << "Connection failure to " << deviceACM << " at " << URG_DEFAULT_SPEED << ". " << urg_error(&urg);
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    return success;
  }
  else {
    // Try to switch to full speed.
    scip_ss(&urg.serial_, URG_FAST_SPEED);
    urg_disconnect(&urg);
    success = urg_connect(&urg, deviceACM, URG_FAST_SPEED);
    if (success < 0)
    {
      std::stringstream ss;
      ss << "Connection failure from " << deviceACM << " at " << URG_FAST_SPEED << ". " << urg_error(&urg);
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
      // Fall back to low speed.
      success = urg_connect(&urg, deviceACM, URG_DEFAULT_SPEED);
      return success;
    }
    else {
      return success;
    }
  }
}

void connectToLaser(void){
  int success = connectToLaserViaUSB();
  if (success < 0) {
    std::stringstream ss;
    ss << "Connection failure to USB, trying ACM... ";
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    success = connectToLaserViaACM();
    if (success < 0) {
      std::stringstream ss;
      ss << "Connection to laser failed";
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
      pthread_exit((void *)NULL);
    }
    else {
      std::stringstream ss;
      ss << "Connection to laser via ACM";
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    }
  }
  else {
    std::stringstream ss;
    ss << "Connection to laser via USB";
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
  }
}

unsigned int getLocalTime(void){
  struct timeval tv;
  unsigned int val;

  gettimeofday(&tv, NULL);

  val = (unsigned int)((unsigned int)(tv.tv_usec/1000) + (unsigned int)(tv.tv_sec*1000));

  // Time in ms
  return val;
}

double index2rad(int index){

  double radian = (2.0 * M_PI) *
                  (index - MIDDLE_ANGLE + angle_min) / RESOLUTION_LASER;

  return radian;
}
<commit_msg>ALLASER: replaced a printf by a qiLogInfo<commit_after>/**
 * @author pinkasfeld joseph
 * Copyright (c) Aldebaran Robotics 2010, 2011 All Rights Reserved.
 */

#include "allaser.h"

#include <iostream>
#include <alcommon/alproxy.h>
#include <alproxies/almemoryproxy.h>
#include <boost/shared_ptr.hpp>
#include <alcommon/albroker.h>
#include <alcommon/almodule.h>
#include <sstream>

#include <pthread.h>
#include <signal.h>
#include <cmath>

#include <qi/log.hpp>

#if defined (__linux__)
#include <sys/prctl.h>
#endif

extern "C" {
# include "urg_ctrl.h"
# include "scip_handler.h"
}

#define MODE_ON 0
#define MODE_OFF 1
#define SEND_MODE_ON 2
#define SEND_MODE_OFF 3

#define MIDDLE_ANGLE 384
#define DEFAULT_MIN_ANGLE 43
#define DEFAULT_MAX_ANGLE 725
#define MIN_ANGLE_LASER 43
#define MAX_ANGLE_LASER 725
#define RESOLUTION_LASER 1024
#define MIN_LENGTH_LASER 20
#define MAX_LENGTH_LASER 5600

#include <sys/time.h>

using namespace AL;
using namespace std;
pthread_t urgThreadId;
boost::shared_ptr<ALMemoryProxy> gSTM;
static int mode = MODE_ON;
static urg_t urg;
static int angle_min = DEFAULT_MIN_ANGLE;
static int angle_max = DEFAULT_MAX_ANGLE;
static int length_min = MIN_LENGTH_LASER;
static int length_max = MAX_LENGTH_LASER;

static void urg_exit(urg_t *urg, const char *message) {
  qiLogInfo("hardware.allaser", "%s: %s\n", message, urg_error(urg));
  urg_disconnect(urg);
  qiLogInfo("hardware.allaser", "urg_exit\n");
  pthread_exit((void *)NULL);
}

#define URG_DEFAULT_SPEED (19200)
#define URG_FAST_SPEED (500000)

void * urgThread(void * arg) {

#if defined (__linux__)
  // thread name
  prctl(PR_SET_NAME, "ALLaser::urgThread", 0, 0, 0);
#endif

  ALValue urgdata;
  long *data = NULL;
  int data_max;
  int ret;
  int n;
  int i,imemory,refTime,end,sampleTime, beginThread;
  std::string valueName="Device/Laser/Value";

  /* Connection */

  connectToLaser();

  /* Reserve the Receive data buffer */
  data_max = urg_dataMax(&urg);
  data = (long*)malloc(sizeof(long) * data_max);
  if (data == NULL) {
    perror("data buffer");
    pthread_exit((void *)NULL);
  }
  /* prepare ALValue for ALMemory*/
  urgdata.arraySetSize(data_max);
  for (i=0; i< data_max;i++)
  {
    urgdata[i].arraySetSize(4);
    urgdata[i][0]= (int)0;
    urgdata[i][1]= (double)0.0;
    urgdata[i][2]= (int)0;
    urgdata[i][3]= (int)0;
  }
  /* insert ALvalue in ALMemory*/

  gSTM->insertData(valueName,urgdata);

  gSTM->insertData("Device/Laser/LaserEnable",(bool)1);
  gSTM->insertData("Device/Laser/MinAngle",(float)((2.0 * M_PI)*(DEFAULT_MIN_ANGLE - MIDDLE_ANGLE) / RESOLUTION_LASER));
  gSTM->insertData("Device/Laser/MaxAngle",(float)((2.0 * M_PI)*(DEFAULT_MAX_ANGLE - MIDDLE_ANGLE) / RESOLUTION_LASER));
  gSTM->insertData("Device/Laser/MinLength",(float)(length_min));
  gSTM->insertData("Device/Laser/MaxLength",(float)(length_max));

  stringstream ss;
  ss << "ALLaser running";
  qiLogInfo("hardware.laser") << ss.str() << std::endl;

  while(1)
  {
    if(mode==SEND_MODE_ON){
      ret = urg_laserOn(&urg);
      if (ret < 0) {
        urg_exit(&urg, "urg_requestData()");
      }
      mode=MODE_ON;
      /* Connection */
      connectToLaser();
    }
    if(mode==SEND_MODE_OFF){
      scip_qt(&urg.serial_, NULL, ScipNoWaitReply);
      mode=MODE_OFF;
    }
    if(mode==MODE_ON){
      /* Request Data using GD-Command */
      ret = urg_requestData(&urg, URG_GD, angle_min, angle_max);
      if (ret < 0) {
        urg_exit(&urg, "urg_requestData()");
      }

      refTime = getLocalTime();
      /* Obtain Data */
      n = urg_receiveData(&urg, data, data_max);
      if (n < 0) {
        urg_exit(&urg, "urg_receiveData()");
      }
      end= getLocalTime();
      sampleTime=end-refTime;

      imemory=0;
      for (i = 0; i < n; ++i) {
        int x, y;
        double angle=index2rad(i);
        int length = data[i];

        if((length>=length_min)&&(length<=length_max)){
          x = (int)((double)length * cos(angle));
          y = (int)((double)length * sin(angle));
          urgdata[imemory][0]= length;
          urgdata[imemory][1]= angle;
          urgdata[imemory][2]= x;
          urgdata[imemory][3]= y;
          imemory++;
        }
      }
      for(;imemory<data_max;imemory++){
        urgdata[imemory][0]= 0;
        urgdata[imemory][1]= 0;
        urgdata[imemory][2]= 0;
        urgdata[imemory][3]= 0;
      }
      gSTM->insertData(valueName,urgdata);
      usleep(1000);
    }else{
      usleep(10000);
    }
  }
  urg_disconnect(&urg);

  free(data);
}

//______________________________________________
// constructor
//______________________________________________
ALLaser::ALLaser(boost::shared_ptr<ALBroker> pBroker, const std::string& pName ): ALModule(pBroker, pName )
{
  setModuleDescription( "Allow control over Hokuyo laser when available on Nao's head." );

  functionName("laserOFF", "ALLaser", "Disable laser light");
  BIND_METHOD( ALLaser::laserOFF );

  functionName("laserON", "ALLaser", "Enable laser light and sampling");
  BIND_METHOD( ALLaser::laserON );

  functionName("setOpeningAngle", "ALLaser", "Set openning angle of the laser");
  addParam("angle_min_f", "float containing the min value in rad, this value must be upper than -2.35619449 ");
  addParam("angle_max_f", "float containing the max value in rad, this value must be lower than 2.092349795 ");
  addMethodExample( "python",
        "# Set the opening angle at -90/90 degres\n"
        "laser = ALProxy(\"ALLaser\",\"127.0.0.1\",9559)\n"
        "laser.setOpeningAngle(-1.570796327,1.570796327)\n"
      );
  BIND_METHOD( ALLaser::setOpeningAngle );

  functionName("setDetectingLength", "ALLaser", "Set detection threshold of the laser");
  addParam("length_min_l", "int containing the min length that the laser will detect(mm), this value must be upper than 20 mm");
  addParam("length_max_l", "int containing the max length that the laser will detect(mm), this value must be lower than 5600 mm");
  addMethodExample( "python",
        "# Set detection threshold at 500/3000 mm\n"
        "laser = ALProxy(\"ALLaser\",\"127.0.0.1\",9559)\n"
        "laser.setDetectingLength(500,3000)\n"
      );
  BIND_METHOD( ALLaser::setDetectingLength );

  // get broker on DCM and ALMemory
  try {
    gSTM = getParentBroker()->getMemoryProxy();
  } catch(ALError& e) {
    qiLogError("hardware.allaser") << "Could not connect to Memory. Error: " << e.what() << std::endl;
  }

  pthread_create(&urgThreadId, NULL, urgThread, NULL);
}

//______________________________________________
// destructor
//______________________________________________
ALLaser::~ALLaser()
{
  pthread_cancel(urgThreadId);
}

void ALLaser::laserOFF(void){
  mode = SEND_MODE_OFF;
  gSTM->insertData("Device/Laser/LaserEnable",(bool)0);
}

void ALLaser::laserON(void){
  mode = SEND_MODE_ON;
  gSTM->insertData("Device/Laser/LaserEnable",(bool)1);
}

void ALLaser::setOpeningAngle(const AL::ALValue& angle_min_f, const AL::ALValue& angle_max_f){
  angle_min = MIDDLE_ANGLE + (int)(RESOLUTION_LASER*(float)angle_min_f / (2.0 * M_PI));
  angle_max = MIDDLE_ANGLE + (int)(RESOLUTION_LASER*(float)angle_max_f / (2.0 * M_PI));
  if(angle_min<MIN_ANGLE_LASER) angle_min = MIN_ANGLE_LASER;
  if(angle_max>MAX_ANGLE_LASER) angle_max = MAX_ANGLE_LASER;
  if(angle_min>=angle_max){
    angle_min = MIN_ANGLE_LASER;
    angle_max = MAX_ANGLE_LASER;
  }
  gSTM->insertData("Device/Laser/MinAngle",(float)((2.0 * M_PI) *(angle_min - MIDDLE_ANGLE) / RESOLUTION_LASER));
  gSTM->insertData("Device/Laser/MaxAngle",(float)((2.0 * M_PI) *(angle_max - MIDDLE_ANGLE) / RESOLUTION_LASER));
}

void ALLaser::setDetectingLength(const AL::ALValue& length_min_l,const AL::ALValue& length_max_l){
  length_min=(int)length_min_l;
  length_max=(int)length_max_l;
  if(length_min<MIN_LENGTH_LASER) length_min = MIN_LENGTH_LASER;
  if(length_max>MAX_LENGTH_LASER) length_min = MAX_LENGTH_LASER;
  if(length_min>=length_max){
    length_min = MIN_LENGTH_LASER;
    length_max = MAX_LENGTH_LASER;
  }
  gSTM->insertData("Device/Laser/MinLength",(int)length_min);
  gSTM->insertData("Device/Laser/MaxLength",(int)length_max);
}

//_________________________________________________
// Service functions
//_________________________________________________

int connectToLaserViaUSB(void) {
  int success = -1;
  const char deviceUSB[] = "/dev/ttyUSB0"; /* Example when using Linux  */
  // Try to connect at low speed.
  success = urg_connect(&urg, deviceUSB, URG_DEFAULT_SPEED);
  if (success < 0) {
    std::stringstream ss;
    ss << "Connection failure to " << deviceUSB << " at " << URG_DEFAULT_SPEED << ". " << urg_error(&urg);
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    return success;
  }
  else {
    // Try to switch to full speed.
    scip_ss(&urg.serial_, URG_FAST_SPEED);
    urg_disconnect(&urg);
    success = urg_connect(&urg, deviceUSB, URG_FAST_SPEED);
    if (success < 0) {
      std::stringstream ss;
      ss << "Connection failure to " << deviceUSB << " at " << URG_FAST_SPEED << ". " << urg_error(&urg);
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
      // Fall back to low speed.
      success = urg_connect(&urg, deviceUSB, URG_DEFAULT_SPEED);
      return success;
    }
    else {
      return success;
    }
  }
}

int connectToLaserViaACM(void) {
  int success = -1;
  const char deviceACM[] = "/dev/ttyACM0"; /* Example when using Linux  */
  success = urg_connect(&urg, deviceACM, URG_DEFAULT_SPEED);
  if (success < 0) {
    std::stringstream ss;
    ss << "Connection failure to " << deviceACM << " at " << URG_DEFAULT_SPEED << ". " << urg_error(&urg);
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    return success;
  }
  else {
    // Try to switch to full speed.
    scip_ss(&urg.serial_, URG_FAST_SPEED);
    urg_disconnect(&urg);
    success = urg_connect(&urg, deviceACM, URG_FAST_SPEED);
    if (success < 0)
    {
      std::stringstream ss;
      ss << "Connection failure from " << deviceACM << " at " << URG_FAST_SPEED << ". " << urg_error(&urg);
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
      // Fall back to low speed.
      success = urg_connect(&urg, deviceACM, URG_DEFAULT_SPEED);
      return success;
    }
    else {
      return success;
    }
  }
}

void connectToLaser(void){
  int success = connectToLaserViaUSB();
  if (success < 0) {
    std::stringstream ss;
    ss << "Connection failure to USB, trying ACM... ";
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    success = connectToLaserViaACM();
    if (success < 0) {
      std::stringstream ss;
      ss << "Connection to laser failed";
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
      pthread_exit((void *)NULL);
    }
    else {
      std::stringstream ss;
      ss << "Connection to laser via ACM";
      qiLogDebug("hardware.allaser") << ss.str() << std::endl;
    }
  }
  else {
    std::stringstream ss;
    ss << "Connection to laser via USB";
    qiLogDebug("hardware.allaser") << ss.str() << std::endl;
  }
}

unsigned int getLocalTime(void){
  struct timeval tv;
  unsigned int val;

  gettimeofday(&tv, NULL);

  val = (unsigned int)((unsigned int)(tv.tv_usec/1000) + (unsigned int)(tv.tv_sec*1000));

  // Time in ms
  return val;
}

double index2rad(int index){

  double radian = (2.0 * M_PI) *
                  (index - MIDDLE_ANGLE + angle_min) / RESOLUTION_LASER;

  return radian;
}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "balancesdialog.h"
#include "ui_balancesdialog.h"

#include "clientmodel.h"
#include "walletmodel.h"
#include "guiutil.h"

#include "omnicore/omnicore.h"
#include "omnicore/sp.h"

#include "amount.h"
#include "sync.h"
#include "ui_interface.h"
#include "wallet_ismine.h"

#include <stdint.h>
#include <map>
#include <sstream>
#include <string>

#include <QAbstractItemView>
#include <QAction>
#include <QDialog>
#include <QHeaderView>
#include <QMenu>
#include <QModelIndex>
#include <QPoint>
#include <QResizeEvent>
#include <QString>
#include <QTableWidgetItem>
#include <QWidget>

using std::ostringstream;
using std::string;
using namespace mastercore;

BalancesDialog::BalancesDialog(QWidget *parent) :
    QDialog(parent), ui(new Ui::balancesDialog), clientModel(0), walletModel(0)
{
    // setup
    ui->setupUi(this);
    ui->balancesTable->setColumnCount(4);
    ui->balancesTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Property ID"));
    ui->balancesTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Property Name"));
    ui->balancesTable->setHorizontalHeaderItem(2, new QTableWidgetItem("Reserved"));
    ui->balancesTable->setHorizontalHeaderItem(3, new QTableWidgetItem("Available"));
    borrowedColumnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(ui->balancesTable,100,100);
    // note neither resizetocontents or stretch allow user to adjust - go interactive then manually set widths
    #if QT_VERSION < 0x050000
       ui->balancesTable->horizontalHeader()->setResizeMode(0, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setResizeMode(1, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setResizeMode(2, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setResizeMode(3, QHeaderView::Interactive);
    #else
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Interactive);
    #endif
    ui->balancesTable->setAlternatingRowColors(true);

    // do an initial population
    UpdatePropSelector();
    PopulateBalances(2147483646); // 2147483646 = summary (last possible ID for test eco props)

    // initial resizing
    ui->balancesTable->resizeColumnToContents(0);
    ui->balancesTable->resizeColumnToContents(2);
    ui->balancesTable->resizeColumnToContents(3);
    borrowedColumnResizingFixer->stretchColumnWidth(1);
    ui->balancesTable->verticalHeader()->setVisible(false);
    ui->balancesTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->balancesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->balancesTable->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->balancesTable->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    ui->balancesTable->setTabKeyNavigation(false);
    ui->balancesTable->setContextMenuPolicy(Qt::CustomContextMenu);
    ui->balancesTable->setEditTriggers(QAbstractItemView::NoEditTriggers);

    // Actions
    QAction *balancesCopyIDAction = new QAction(tr("Copy property ID"), this);
    QAction *balancesCopyNameAction = new QAction(tr("Copy property name"), this);
    QAction *balancesCopyAddressAction = new QAction(tr("Copy address"), this);
    QAction *balancesCopyLabelAction = new QAction(tr("Copy label"), this);
    QAction *balancesCopyReservedAmountAction = new QAction(tr("Copy reserved amount"), this);
    QAction *balancesCopyAvailableAmountAction = new QAction(tr("Copy available amount"), this);

    contextMenu = new QMenu();
    contextMenu->addAction(balancesCopyLabelAction);
    contextMenu->addAction(balancesCopyAddressAction);
    contextMenu->addAction(balancesCopyReservedAmountAction);
    contextMenu->addAction(balancesCopyAvailableAmountAction);
    contextMenuSummary = new QMenu();
    contextMenuSummary->addAction(balancesCopyIDAction);
    contextMenuSummary->addAction(balancesCopyNameAction);
    contextMenuSummary->addAction(balancesCopyReservedAmountAction);
    contextMenuSummary->addAction(balancesCopyAvailableAmountAction);

    // Connect actions
    connect(ui->balancesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
    connect(ui->propSelectorWidget, SIGNAL(activated(int)), this, SLOT(propSelectorChanged()));
    connect(balancesCopyIDAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol0()));
    connect(balancesCopyNameAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol1()));
    connect(balancesCopyLabelAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol0()));
    connect(balancesCopyAddressAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol1()));
    connect(balancesCopyReservedAmountAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol2()));
    connect(balancesCopyAvailableAmountAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol3()));
}

BalancesDialog::~BalancesDialog()
{
    delete ui;
}

void BalancesDialog::setClientModel(ClientModel *model)
{
    this->clientModel = model;
    if (model != NULL) {
        connect(model, SIGNAL(refreshOmniState()), this, SLOT(balancesUpdated()));
    }
}

void BalancesDialog::setWalletModel(WalletModel *model)
{
    this->walletModel = model;
    if (model != NULL) { } // do nothing, signals from walletModel no longer needed
}

void BalancesDialog::UpdatePropSelector()
{
    // don't waste time updating if there are no new properties
    if ((uint32_t)ui->propSelectorWidget->count() > global_wallet_property_list.size()) return;

    // a new property has been added to the wallet, update the property selector
    QString spId = ui->propSelectorWidget->itemData(ui->propSelectorWidget->currentIndex()).toString();
    ui->propSelectorWidget->clear();
    ui->propSelectorWidget->addItem("Wallet Totals (Summary)","2147483646"); //use last possible ID for summary for now
    // populate property selector
    for (std::set<uint32_t>::iterator it = global_wallet_property_list.begin() ; it != global_wallet_property_list.end(); ++it) {
        uint32_t propertyId = *it;
        std::string spId = static_cast<ostringstream*>( &(ostringstream() << propertyId) )->str();
        std::string spName = getPropertyName(propertyId).c_str();
        if(spName.size()>20) spName=spName.substr(0,20)+"...";
        spName += " (#" + spId + ")";
        ui->propSelectorWidget->addItem(spName.c_str(), spId.c_str());
    }
    int propIdx = ui->propSelectorWidget->findData(spId);
    if (propIdx != -1) { ui->propSelectorWidget->setCurrentIndex(propIdx); }
}

void BalancesDialog::AddRow(const std::string& label, const std::string& address, const std::string& reserved, const std::string& available)
{
    int workingRow = ui->balancesTable->rowCount();
    ui->balancesTable->insertRow(workingRow);
    QTableWidgetItem *labelCell = new QTableWidgetItem(QString::fromStdString(label));
    QTableWidgetItem *addressCell = new QTableWidgetItem(QString::fromStdString(address));
    QTableWidgetItem *reservedCell = new QTableWidgetItem(QString::fromStdString(reserved));
    QTableWidgetItem *availableCell = new QTableWidgetItem(QString::fromStdString(available));
    labelCell->setTextAlignment(Qt::AlignLeft + Qt::AlignVCenter);
    addressCell->setTextAlignment(Qt::AlignLeft + Qt::AlignVCenter);
    reservedCell->setTextAlignment(Qt::AlignRight + Qt::AlignVCenter);
    availableCell->setTextAlignment(Qt::AlignRight + Qt::AlignVCenter);
    ui->balancesTable->setItem(workingRow, 0, labelCell);
    ui->balancesTable->setItem(workingRow, 1, addressCell);
    ui->balancesTable->setItem(workingRow, 2, reservedCell);
    ui->balancesTable->setItem(workingRow, 3, availableCell);
}

void BalancesDialog::PopulateBalances(unsigned int propertyId)
{
    ui->balancesTable->setRowCount(0); // fresh slate (note this will automatically cleanup all existing QWidgetItems in the table)
    //are we summary?
    if(propertyId==2147483646) {
        ui->balancesTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Property ID"));
        ui->balancesTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Property Name"));

        // loop over the wallet property list and add the wallet totals
        for (std::set<uint32_t>::iterator it = global_wallet_property_list.begin() ; it != global_wallet_property_list.end(); ++it) {
            uint32_t propertyId = *it;
            std::string spId = static_cast<ostringstream*>( &(ostringstream() << propertyId) )->str();
            std::string spName = getPropertyName(propertyId).c_str();
            std::string available = FormatMP(propertyId, global_balance_money[propertyId]);
            std::string reserved = FormatMP(propertyId, global_balance_reserved[propertyId]);
            AddRow(spId, spName, reserved, available);
        }
    } else {
        ui->balancesTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Label"));
        ui->balancesTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Address"));
        LOCK(cs_tally);
        bool propertyIsDivisible = isPropertyDivisible(propertyId); // only fetch the SP once, not for every address

        // iterate mp_tally_map looking for addresses that hold a balance in propertyId
        for(std::map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it) {
            string address = my_it->first.c_str();
            my_it->second.init();
            uint32_t id;
            bool watchAddress = false, includeAddress = false;
            while (0 != (id = (my_it->second).next())) {
                if (id == propertyId) {
                    includeAddress = true;
                    break;
                }
            }
            if (!includeAddress) continue; //ignore this address, has never transacted in this propertyId

            // determine if this address is in the wallet
            int addressIsMine = IsMyAddress(address);
            if (!addressIsMine) continue; // ignore this address, not in wallet
            if (addressIsMine != ISMINE_SPENDABLE) watchAddress = true;

            // obtain the balances for the address
            int64_t available = getUserAvailableMPbalance(address, propertyId);
            int64_t reserved = getMPbalance(address, propertyId, METADEX_RESERVE);
            if (propertyId<3) {
                reserved += getMPbalance(address, propertyId, ACCEPT_RESERVE);
                reserved += getMPbalance(address, propertyId, SELLOFFER_RESERVE);
            }

            // format the balances
            string reservedStr, availableStr;
            if (propertyIsDivisible) {
                reservedStr = FormatDivisibleMP(reserved);
                availableStr = FormatDivisibleMP(available);
            } else {
                reservedStr = FormatIndivisibleMP(reserved);
                availableStr = FormatIndivisibleMP(available);
            }

            // add the row
            if (!watchAddress) {
                AddRow(getLabel(my_it->first), address, reservedStr, availableStr);
            } else {
                AddRow(getLabel(my_it->first), address + " (watch-only)", reservedStr, availableStr);
            }
        }
    }
}

void BalancesDialog::propSelectorChanged()
{
    QString spId = ui->propSelectorWidget->itemData(ui->propSelectorWidget->currentIndex()).toString();
    unsigned int propertyId = spId.toUInt();
    PopulateBalances(propertyId);
}

void BalancesDialog::contextualMenu(const QPoint &point)
{
    QModelIndex index = ui->balancesTable->indexAt(point);
    if(index.isValid())
    {
        QString spId = ui->propSelectorWidget->itemData(ui->propSelectorWidget->currentIndex()).toString();
        unsigned int propertyId = spId.toUInt();
        if (propertyId == 2147483646) {
            contextMenuSummary->exec(QCursor::pos());
        } else {
            contextMenu->exec(QCursor::pos());
        }
    }
}

void BalancesDialog::balancesCopyCol0()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),0)->text());
}

void BalancesDialog::balancesCopyCol1()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),1)->text());
}

void BalancesDialog::balancesCopyCol2()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),2)->text());
}

void BalancesDialog::balancesCopyCol3()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),3)->text());
}

void BalancesDialog::balancesUpdated()
{
    UpdatePropSelector();
    propSelectorChanged(); // refresh the table with the currently selected property ID
}

// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void BalancesDialog::resizeEvent(QResizeEvent* event)
{
    QWidget::resizeEvent(event);
    borrowedColumnResizingFixer->stretchColumnWidth(1);
}

<commit_msg>UI: balancesDialog - use tally directly (thanks @dexX7)<commit_after>// Copyright (c) 2011-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "balancesdialog.h"
#include "ui_balancesdialog.h"

#include "clientmodel.h"
#include "walletmodel.h"
#include "guiutil.h"

#include "omnicore/omnicore.h"
#include "omnicore/sp.h"

#include "amount.h"
#include "sync.h"
#include "ui_interface.h"
#include "wallet_ismine.h"

#include <stdint.h>
#include <map>
#include <sstream>
#include <string>

#include <QAbstractItemView>
#include <QAction>
#include <QDialog>
#include <QHeaderView>
#include <QMenu>
#include <QModelIndex>
#include <QPoint>
#include <QResizeEvent>
#include <QString>
#include <QTableWidgetItem>
#include <QWidget>

using std::ostringstream;
using std::string;
using namespace mastercore;

BalancesDialog::BalancesDialog(QWidget *parent) :
    QDialog(parent), ui(new Ui::balancesDialog), clientModel(0), walletModel(0)
{
    // setup
    ui->setupUi(this);
    ui->balancesTable->setColumnCount(4);
    ui->balancesTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Property ID"));
    ui->balancesTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Property Name"));
    ui->balancesTable->setHorizontalHeaderItem(2, new QTableWidgetItem("Reserved"));
    ui->balancesTable->setHorizontalHeaderItem(3, new QTableWidgetItem("Available"));
    borrowedColumnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(ui->balancesTable,100,100);
    // note neither resizetocontents or stretch allow user to adjust - go interactive then manually set widths
    #if QT_VERSION < 0x050000
       ui->balancesTable->horizontalHeader()->setResizeMode(0, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setResizeMode(1, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setResizeMode(2, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setResizeMode(3, QHeaderView::Interactive);
    #else
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive);
       ui->balancesTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Interactive);
    #endif
    ui->balancesTable->setAlternatingRowColors(true);

    // do an initial population
    UpdatePropSelector();
    PopulateBalances(2147483646); // 2147483646 = summary (last possible ID for test eco props)

    // initial resizing
    ui->balancesTable->resizeColumnToContents(0);
    ui->balancesTable->resizeColumnToContents(2);
    ui->balancesTable->resizeColumnToContents(3);
    borrowedColumnResizingFixer->stretchColumnWidth(1);
    ui->balancesTable->verticalHeader()->setVisible(false);
    ui->balancesTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->balancesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->balancesTable->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->balancesTable->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    ui->balancesTable->setTabKeyNavigation(false);
    ui->balancesTable->setContextMenuPolicy(Qt::CustomContextMenu);
    ui->balancesTable->setEditTriggers(QAbstractItemView::NoEditTriggers);

    // Actions
    QAction *balancesCopyIDAction = new QAction(tr("Copy property ID"), this);
    QAction *balancesCopyNameAction = new QAction(tr("Copy property name"), this);
    QAction *balancesCopyAddressAction = new QAction(tr("Copy address"), this);
    QAction *balancesCopyLabelAction = new QAction(tr("Copy label"), this);
    QAction *balancesCopyReservedAmountAction = new QAction(tr("Copy reserved amount"), this);
    QAction *balancesCopyAvailableAmountAction = new QAction(tr("Copy available amount"), this);

    contextMenu = new QMenu();
    contextMenu->addAction(balancesCopyLabelAction);
    contextMenu->addAction(balancesCopyAddressAction);
    contextMenu->addAction(balancesCopyReservedAmountAction);
    contextMenu->addAction(balancesCopyAvailableAmountAction);
    contextMenuSummary = new QMenu();
    contextMenuSummary->addAction(balancesCopyIDAction);
    contextMenuSummary->addAction(balancesCopyNameAction);
    contextMenuSummary->addAction(balancesCopyReservedAmountAction);
    contextMenuSummary->addAction(balancesCopyAvailableAmountAction);

    // Connect actions
    connect(ui->balancesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
    connect(ui->propSelectorWidget, SIGNAL(activated(int)), this, SLOT(propSelectorChanged()));
    connect(balancesCopyIDAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol0()));
    connect(balancesCopyNameAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol1()));
    connect(balancesCopyLabelAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol0()));
    connect(balancesCopyAddressAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol1()));
    connect(balancesCopyReservedAmountAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol2()));
    connect(balancesCopyAvailableAmountAction, SIGNAL(triggered()), this, SLOT(balancesCopyCol3()));
}

BalancesDialog::~BalancesDialog()
{
    delete ui;
}

void BalancesDialog::setClientModel(ClientModel *model)
{
    this->clientModel = model;
    if (model != NULL) {
        connect(model, SIGNAL(refreshOmniState()), this, SLOT(balancesUpdated()));
    }
}

void BalancesDialog::setWalletModel(WalletModel *model)
{
    this->walletModel = model;
    if (model != NULL) { } // do nothing, signals from walletModel no longer needed
}

void BalancesDialog::UpdatePropSelector()
{
    // don't waste time updating if there are no new properties
    if ((uint32_t)ui->propSelectorWidget->count() > global_wallet_property_list.size()) return;

    // a new property has been added to the wallet, update the property selector
    QString spId = ui->propSelectorWidget->itemData(ui->propSelectorWidget->currentIndex()).toString();
    ui->propSelectorWidget->clear();
    ui->propSelectorWidget->addItem("Wallet Totals (Summary)","2147483646"); //use last possible ID for summary for now
    // populate property selector
    for (std::set<uint32_t>::iterator it = global_wallet_property_list.begin() ; it != global_wallet_property_list.end(); ++it) {
        uint32_t propertyId = *it;
        std::string spId = static_cast<ostringstream*>( &(ostringstream() << propertyId) )->str();
        std::string spName = getPropertyName(propertyId).c_str();
        if(spName.size()>20) spName=spName.substr(0,20)+"...";
        spName += " (#" + spId + ")";
        ui->propSelectorWidget->addItem(spName.c_str(), spId.c_str());
    }
    int propIdx = ui->propSelectorWidget->findData(spId);
    if (propIdx != -1) { ui->propSelectorWidget->setCurrentIndex(propIdx); }
}

void BalancesDialog::AddRow(const std::string& label, const std::string& address, const std::string& reserved, const std::string& available)
{
    int workingRow = ui->balancesTable->rowCount();
    ui->balancesTable->insertRow(workingRow);
    QTableWidgetItem *labelCell = new QTableWidgetItem(QString::fromStdString(label));
    QTableWidgetItem *addressCell = new QTableWidgetItem(QString::fromStdString(address));
    QTableWidgetItem *reservedCell = new QTableWidgetItem(QString::fromStdString(reserved));
    QTableWidgetItem *availableCell = new QTableWidgetItem(QString::fromStdString(available));
    labelCell->setTextAlignment(Qt::AlignLeft + Qt::AlignVCenter);
    addressCell->setTextAlignment(Qt::AlignLeft + Qt::AlignVCenter);
    reservedCell->setTextAlignment(Qt::AlignRight + Qt::AlignVCenter);
    availableCell->setTextAlignment(Qt::AlignRight + Qt::AlignVCenter);
    ui->balancesTable->setItem(workingRow, 0, labelCell);
    ui->balancesTable->setItem(workingRow, 1, addressCell);
    ui->balancesTable->setItem(workingRow, 2, reservedCell);
    ui->balancesTable->setItem(workingRow, 3, availableCell);
}

void BalancesDialog::PopulateBalances(unsigned int propertyId)
{
    ui->balancesTable->setRowCount(0); // fresh slate (note this will automatically cleanup all existing QWidgetItems in the table)
    //are we summary?
    if(propertyId==2147483646) {
        ui->balancesTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Property ID"));
        ui->balancesTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Property Name"));

        // loop over the wallet property list and add the wallet totals
        for (std::set<uint32_t>::iterator it = global_wallet_property_list.begin() ; it != global_wallet_property_list.end(); ++it) {
            uint32_t propertyId = *it;
            std::string spId = static_cast<ostringstream*>( &(ostringstream() << propertyId) )->str();
            std::string spName = getPropertyName(propertyId).c_str();
            std::string available = FormatMP(propertyId, global_balance_money[propertyId]);
            std::string reserved = FormatMP(propertyId, global_balance_reserved[propertyId]);
            AddRow(spId, spName, reserved, available);
        }
    } else {
        ui->balancesTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Label"));
        ui->balancesTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Address"));
        LOCK(cs_tally);
        bool propertyIsDivisible = isPropertyDivisible(propertyId); // only fetch the SP once, not for every address

        // iterate mp_tally_map looking for addresses that hold a balance in propertyId
        for(std::map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it) {
            const std::string& address = my_it->first;
            CMPTally& tally = my_it->second;
            tally.init();

            uint32_t id;
            bool watchAddress = false, includeAddress = false;
            while (0 != (id = (tally.next()))) {
                if (id == propertyId) {
                    includeAddress = true;
                    break;
                }
            }
            if (!includeAddress) continue; //ignore this address, has never transacted in this propertyId

            // determine if this address is in the wallet
            int addressIsMine = IsMyAddress(address);
            if (!addressIsMine) continue; // ignore this address, not in wallet
            if (addressIsMine != ISMINE_SPENDABLE) watchAddress = true;

            // obtain the balances for the address directly form tally
            int64_t available = tally.getMoney(propertyId, BALANCE);
            available += tally.getMoney(propertyId, PENDING);
            int64_t reserved = tally.getMoney(propertyId, SELLOFFER_RESERVE);
            reserved += tally.getMoney(propertyId, ACCEPT_RESERVE);
            reserved += tally.getMoney(propertyId, METADEX_RESERVE);

            // format the balances
            string reservedStr, availableStr;
            if (propertyIsDivisible) {
                reservedStr = FormatDivisibleMP(reserved);
                availableStr = FormatDivisibleMP(available);
            } else {
                reservedStr = FormatIndivisibleMP(reserved);
                availableStr = FormatIndivisibleMP(available);
            }

            // add the row
            if (!watchAddress) {
                AddRow(getLabel(my_it->first), address, reservedStr, availableStr);
            } else {
                AddRow(getLabel(my_it->first), address + " (watch-only)", reservedStr, availableStr);
            }
        }
    }
}

void BalancesDialog::propSelectorChanged()
{
    QString spId = ui->propSelectorWidget->itemData(ui->propSelectorWidget->currentIndex()).toString();
    unsigned int propertyId = spId.toUInt();
    PopulateBalances(propertyId);
}

void BalancesDialog::contextualMenu(const QPoint &point)
{
    QModelIndex index = ui->balancesTable->indexAt(point);
    if(index.isValid())
    {
        QString spId = ui->propSelectorWidget->itemData(ui->propSelectorWidget->currentIndex()).toString();
        unsigned int propertyId = spId.toUInt();
        if (propertyId == 2147483646) {
            contextMenuSummary->exec(QCursor::pos());
        } else {
            contextMenu->exec(QCursor::pos());
        }
    }
}

void BalancesDialog::balancesCopyCol0()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),0)->text());
}

void BalancesDialog::balancesCopyCol1()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),1)->text());
}

void BalancesDialog::balancesCopyCol2()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),2)->text());
}

void BalancesDialog::balancesCopyCol3()
{
    GUIUtil::setClipboard(ui->balancesTable->item(ui->balancesTable->currentRow(),3)->text());
}

void BalancesDialog::balancesUpdated()
{
    UpdatePropSelector();
    propSelectorChanged(); // refresh the table with the currently selected property ID
}

// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void BalancesDialog::resizeEvent(QResizeEvent* event)
{
    QWidget::resizeEvent(event);
    borrowedColumnResizingFixer->stretchColumnWidth(1);
}

<|endoftext|>


<commit_before>// Copyright (C) 2012-2017 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include <autowiring/autowiring.h>
#include <autowiring/CoreContext.h>
#include <autowiring/CurrentContextPusher.h>
#include <thread>

class CurrentContextPusherTest:
  public testing::Test
{};

using namespace std;

TEST_F(CurrentContextPusherTest, VerifyResetsDuringThrow) {
  std::shared_ptr<CoreContext> current = CoreContext::CurrentContext();

  try {
    AutoCreateContext subContext(current);
    CurrentContextPusher test(subContext.get());
    throw exception();
  } catch(exception&) {
  }

  ASSERT_EQ(current.get(), CoreContext::CurrentContext().get());
}

TEST_F(CurrentContextPusherTest, NoUnexpectedGlobalHold) {
  AutoCreateContext ctxt;
  AutoGlobalContext global;

  int initUses = global.use_count();
  std::thread rs(
    [&ctxt] {
      CurrentContextPusher pshr(ctxt);
    }
  );
  rs.join();
  ASSERT_TRUE(ctxt.unique()) << "The current context pointer was not correctly cleaned up on thread exit";
  ASSERT_EQ(initUses, global.use_count()) << "A global reference was unexpectedly leaked by the pusher";
}
<commit_msg>Add another use_count workaround<commit_after>// Copyright (C) 2012-2017 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "autotesting/AutowiringEnclosure.h"
#include <autowiring/autowiring.h>
#include <autowiring/CoreContext.h>
#include <autowiring/CurrentContextPusher.h>
#include <thread>

class CurrentContextPusherTest:
  public testing::Test
{};

using namespace std;

TEST_F(CurrentContextPusherTest, VerifyResetsDuringThrow) {
  std::shared_ptr<CoreContext> current = CoreContext::CurrentContext();

  try {
    AutoCreateContext subContext(current);
    CurrentContextPusher test(subContext.get());
    throw exception();
  } catch(exception&) {
  }

  ASSERT_EQ(current.get(), CoreContext::CurrentContext().get());
}

TEST_F(CurrentContextPusherTest, NoUnexpectedGlobalHold) {
  AutoCreateContext ctxt;
  AutoGlobalContext global;

  int initUses = global.use_count();
  std::thread rs(
    [&ctxt] {
      CurrentContextPusher pshr(ctxt);
    }
  );
  rs.join();
  ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(ctxt, 1L, std::chrono::seconds(5))) << "The current context pointer was not correctly cleaned up on thread exit";
  ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(global, initUses, std::chrono::seconds(5))) << "A global reference was unexpectedly leaked by the pusher";
}
<|endoftext|>