<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/io/p9_io_obus_image_build.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file   p9_io_obus_image_build.C
/// @brief  Implements HWP that builds the Hcode image in IO Obus PPE Sram.
///----------------------------------------------------------------------------
/// *HWP HWP Owner        : Chris Steffen <cwsteffen@us.ibm.com>
/// *HWP HPW Backup Owner : Gary Peterson <garyp@us.ibm.com>
/// *HWP FW Owner         : Jamie Knight <rjknight@us.ibm.com>
/// *HWP Team             : IO
/// *HWP Level            : 1
/// *HWP Consumed by      : FSP:HB
///----------------------------------------------------------------------------

//--------------------------------------------------------------------------
// Includes
//--------------------------------------------------------------------------
#include <p9_io_obus_image_build.H>
#include "p9_xip_image.h"

//---------------------------------------------------------------------------
fapi2::ReturnCode extractPpeImgObus(void* const iImagePtr, uint8_t*& oObusImgPtr, uint32_t& oSize)
{
    FAPI_IMP("Entering getObusImageFromHwImage.");
    P9XipSection ppeSection;
    ppeSection.iv_offset = 0;
    ppeSection.iv_size = 0;

    FAPI_ASSERT(iImagePtr != NULL ,
                fapi2::P9_IO_PPE_OBUS_IMG_PTR_ERROR().set_HW_IMG_PTR(iImagePtr),
                "Bad pointer to HW Image.");

    // Pulls the IO PPE Section from the HW/XIP Image
    // XIP(Execution In Place) -- Points to Seeprom
    FAPI_TRY(p9_xip_get_section(iImagePtr, P9_XIP_SECTION_HW_IOPPE, &ppeSection));

    // Point to the I/O PPE Section in the HW/XIP Image
    oObusImgPtr = ppeSection.iv_offset + (uint8_t*)(iImagePtr);

    // From the I/O Section, lets pull the IOO Nvlink Image.
    FAPI_TRY(p9_xip_get_section(oObusImgPtr, P9_XIP_SECTION_IOPPE_IOO_NV, &ppeSection));

    // Point to the IOO PPE Image of the I/O PPE Section
    oObusImgPtr = ppeSection.iv_offset + (uint8_t*)(oObusImgPtr);

    // Set the Size of the IOO Image
    oSize = ppeSection.iv_size;

fapi_try_exit:
    FAPI_IMP("Exiting getObusImageFromHwImage.");
    return fapi2::current_err;
}

//---------------------------------------------------------------------------
fapi2::ReturnCode scomWrite(CONST_OBUS& iTgt, const uint64_t iAddr, const uint64_t iData)
{
    fapi2::buffer<uint64_t> data64(iData);
    // Xscom -- Scom from core in Hostboot mode
    return fapi2::putScom(iTgt, iAddr, data64);
}

//---------------------------------------------------------------------------
fapi2::ReturnCode p9_io_obus_image_build(CONST_OBUS& iTgt, void* const iHwImagePtr)
{
    FAPI_IMP("Entering p9_io_obus_image_build.");

    const uint64_t SRAM_BASE_ADDR   = 0xFFFF000000000000ull;
    const uint64_t AUTOINC_EN       = 0x8000000000000000ull;
    const uint64_t AUTOINC_DIS      = 0x0000000000000000ull;
    const uint64_t HARD_RESET       = 0x6000000000000000ull; // xcr cmd=110
    const uint64_t RESUME_FROM_HALT = 0x2000000000000000ull; // xcr cmd=010
    // PPE Address
    const uint64_t BASE_ADDR    = 0x0000000006010C00ull;
    const uint64_t MEM_ARB_CSAR = 0x000000000000000Dull | BASE_ADDR; // Sram Address Reg
    const uint64_t MEM_ARB_SCR  = 0x000000000000000Aull | BASE_ADDR; // Sram Source Control Reg
    const uint64_t MEM_ARB_CSDR = 0x000000000000000Eull | BASE_ADDR; // Sram Data Reg
    const uint64_t XCR_NONE     = 0x0000000000000010ull | BASE_ADDR; // External Control Reg

    uint64_t data      = 0;
    uint8_t* pObusImg = NULL;
    uint32_t imgSize   = 0;

    FAPI_TRY(extractPpeImgObus(iHwImagePtr, pObusImg, imgSize), "Extract PPE Image Failed.");

    // PPE Reset
    FAPI_TRY(scomWrite(iTgt, XCR_NONE, HARD_RESET), "Hard Reset Failed.");

    // Set PPE Base Address
    FAPI_TRY(scomWrite(iTgt, MEM_ARB_CSAR, SRAM_BASE_ADDR), "Set Base Address Failed.");

    // Set PPE into Autoincrement Mode
    FAPI_TRY(scomWrite(iTgt, MEM_ARB_SCR, AUTOINC_EN), "Auto-Increment Enable Failed.");

    for(uint32_t i = 0; i < imgSize; i += 8)
    {
        data = (((uint64_t) * (pObusImg + i + 0) << 56) & 0xFF00000000000000ull) |
               (((uint64_t) * (pObusImg + i + 1) << 48) & 0x00FF000000000000ull) |
               (((uint64_t) * (pObusImg + i + 2) << 40) & 0x0000FF0000000000ull) |
               (((uint64_t) * (pObusImg + i + 3) << 32) & 0x000000FF00000000ull) |
               (((uint64_t) * (pObusImg + i + 4) << 24) & 0x00000000FF000000ull) |
               (((uint64_t) * (pObusImg + i + 5) << 16) & 0x0000000000FF0000ull) |
               (((uint64_t) * (pObusImg + i + 6) <<  8) & 0x000000000000FF00ull) |
               (((uint64_t) * (pObusImg + i + 7) <<  0) & 0x00000000000000FFull);

        // Write Data, as the address will be autoincremented.
        FAPI_TRY(scomWrite(iTgt, MEM_ARB_CSDR, data), "Data Write Failed.");
    }

    // Disable Auto Increment
    FAPI_TRY(scomWrite(iTgt, MEM_ARB_SCR, AUTOINC_DIS), "Auto-Increment Disable Failed.");

    // PPE Reset
    FAPI_TRY(scomWrite(iTgt, XCR_NONE, HARD_RESET), "Hard Reset Failed.");

    // PPE Resume From Halt
    FAPI_TRY(scomWrite(iTgt, XCR_NONE, RESUME_FROM_HALT), "Resume From Halt Failed.");

fapi_try_exit:
    FAPI_IMP("Exit p9_io_obus_image_build.");
    return fapi2::current_err;
}
<commit_msg>Obus I/O Image Build Fix<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/io/p9_io_obus_image_build.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file   p9_io_obus_image_build.C
/// @brief  Implements HWP that builds the Hcode image in IO Obus PPE Sram.
///----------------------------------------------------------------------------
/// *HWP HWP Owner        : Chris Steffen <cwsteffen@us.ibm.com>
/// *HWP HPW Backup Owner : Gary Peterson <garyp@us.ibm.com>
/// *HWP FW Owner         : Jamie Knight <rjknight@us.ibm.com>
/// *HWP Team             : IO
/// *HWP Level            : 1
/// *HWP Consumed by      : FSP:HB
///----------------------------------------------------------------------------

//--------------------------------------------------------------------------
// Includes
//--------------------------------------------------------------------------
#include <p9_io_obus_image_build.H>
#include "p9_xip_image.h"

//---------------------------------------------------------------------------
fapi2::ReturnCode extractPpeImgObus(void* const iImagePtr, uint8_t*& oObusImgPtr, uint32_t& oSize)
{
    FAPI_IMP("Entering getObusImageFromHwImage.");
    P9XipSection ppeSection;
    ppeSection.iv_offset = 0;
    ppeSection.iv_size = 0;

    FAPI_ASSERT(iImagePtr != NULL ,
                fapi2::P9_IO_PPE_OBUS_IMG_PTR_ERROR().set_HW_IMG_PTR(iImagePtr),
                "Bad pointer to HW Image.");

    // Pulls the IO PPE Section from the HW/XIP Image
    // XIP(Execution In Place) -- Points to Seeprom
    FAPI_TRY(p9_xip_get_section(iImagePtr, P9_XIP_SECTION_HW_IOPPE, &ppeSection));

    // Point to the I/O PPE Section in the HW/XIP Image
    oObusImgPtr = ppeSection.iv_offset + (uint8_t*)(iImagePtr);

    // From the I/O Section, lets pull the IOO Nvlink Image.
    FAPI_TRY(p9_xip_get_section(oObusImgPtr, P9_XIP_SECTION_IOPPE_IOO_NV, &ppeSection));

    // Point to the IOO PPE Image of the I/O PPE Section
    oObusImgPtr = ppeSection.iv_offset + (uint8_t*)(oObusImgPtr);

    // Set the Size of the IOO Image
    oSize = ppeSection.iv_size;

fapi_try_exit:
    FAPI_IMP("Exiting getObusImageFromHwImage.");
    return fapi2::current_err;
}

//---------------------------------------------------------------------------
fapi2::ReturnCode scomWrite(CONST_OBUS& iTgt, const uint64_t iAddr, const uint64_t iData)
{
    fapi2::buffer<uint64_t> data64(iData);
    // Xscom -- Scom from core in Hostboot mode
    return fapi2::putScom(iTgt, iAddr, data64);
}

//---------------------------------------------------------------------------
fapi2::ReturnCode p9_io_obus_image_build(CONST_OBUS& iTgt, void* const iHwImagePtr)
{
    FAPI_IMP("Entering p9_io_obus_image_build.");

    const uint64_t SRAM_BASE_ADDR   = 0xFFFF000000000000ull;
    const uint64_t AUTOINC_EN       = 0x8000000000000000ull;
    const uint64_t AUTOINC_DIS      = 0x0000000000000000ull;
    const uint64_t HARD_RESET       = 0x6000000000000000ull; // xcr cmd=110
    const uint64_t RESUME_FROM_HALT = 0x2000000000000000ull; // xcr cmd=010
    // PPE Address
    const uint64_t BASE_ADDR    = 0x0000000009011040ull;
    const uint64_t MEM_ARB_CSAR = 0x000000000000000Dull | BASE_ADDR; // Sram Address Reg
    const uint64_t MEM_ARB_SCR  = 0x000000000000000Aull | BASE_ADDR; // Sram Source Control Reg
    const uint64_t MEM_ARB_CSDR = 0x000000000000000Eull | BASE_ADDR; // Sram Data Reg
    const uint64_t XCR_NONE     = 0x0000000000000010ull | BASE_ADDR; // External Control Reg

    uint64_t data      = 0;
    uint8_t* pObusImg = NULL;
    uint32_t imgSize   = 0;

    FAPI_TRY(extractPpeImgObus(iHwImagePtr, pObusImg, imgSize), "Extract PPE Image Failed.");

    // PPE Reset
    FAPI_TRY(scomWrite(iTgt, XCR_NONE, HARD_RESET), "Hard Reset Failed.");

    // Set PPE Base Address
    FAPI_TRY(scomWrite(iTgt, MEM_ARB_CSAR, SRAM_BASE_ADDR), "Set Base Address Failed.");

    // Set PPE into Autoincrement Mode
    FAPI_TRY(scomWrite(iTgt, MEM_ARB_SCR, AUTOINC_EN), "Auto-Increment Enable Failed.");

    for(uint32_t i = 0; i < imgSize; i += 8)
    {
        data = (((uint64_t) * (pObusImg + i + 0) << 56) & 0xFF00000000000000ull) |
               (((uint64_t) * (pObusImg + i + 1) << 48) & 0x00FF000000000000ull) |
               (((uint64_t) * (pObusImg + i + 2) << 40) & 0x0000FF0000000000ull) |
               (((uint64_t) * (pObusImg + i + 3) << 32) & 0x000000FF00000000ull) |
               (((uint64_t) * (pObusImg + i + 4) << 24) & 0x00000000FF000000ull) |
               (((uint64_t) * (pObusImg + i + 5) << 16) & 0x0000000000FF0000ull) |
               (((uint64_t) * (pObusImg + i + 6) <<  8) & 0x000000000000FF00ull) |
               (((uint64_t) * (pObusImg + i + 7) <<  0) & 0x00000000000000FFull);

        // Write Data, as the address will be autoincremented.
        FAPI_TRY(scomWrite(iTgt, MEM_ARB_CSDR, data), "Data Write Failed.");
    }

    // Disable Auto Increment
    FAPI_TRY(scomWrite(iTgt, MEM_ARB_SCR, AUTOINC_DIS), "Auto-Increment Disable Failed.");

    // PPE Reset
    FAPI_TRY(scomWrite(iTgt, XCR_NONE, HARD_RESET), "Hard Reset Failed.");

    // PPE Resume From Halt
    FAPI_TRY(scomWrite(iTgt, XCR_NONE, RESUME_FROM_HALT), "Resume From Halt Failed.");

fapi_try_exit:
    FAPI_IMP("Exit p9_io_obus_image_build.");
    return fapi2::current_err;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (C) 2010 Dan Leinir Turthra Jensen <admin@leinir.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "scriptingengine.h"

#include "game.h"
#include "scriptingasset.h"
#include "scriptingcomponent.h"

#include <core/gluonobjectfactory.h>
#include <core/scriptengine.h>

#include <QtScript>
// #include <QScriptEngineDebugger>

namespace GluonEngine
{
    class ScriptingEngine::Private
    {
        public:
            Private()
                : theEngine( 0 )
            {
            }

            void resetEngine()
            {
                theEngine = 0;
                GluonCore::ScriptEngine::instance()->resetEngine();
            }
            QScriptEngine* engine()
            {
                if( !theEngine )
                {
                    theEngine = GluonCore::ScriptEngine::instance()->scriptEngine();

                    QScriptEngine::QObjectWrapOptions wrapOptions = QScriptEngine::AutoCreateDynamicProperties | QScriptEngine::ExcludeDeleteLater | QScriptEngine::PreferExistingWrapperObject;
                    QScriptEngine::ValueOwnership ownership = QScriptEngine::QtOwnership;
                    QScriptValue game = ScriptingEngine::instance()->scriptEngine()->newQObject( GluonEngine::Game::instance(), ownership, wrapOptions );
                    theEngine->globalObject().setProperty( "Game", game );
                }
                if( !theEngine )
                {
                    DEBUG_BLOCK
                    DEBUG_TEXT( "Somehow we do not have a scripting engine. This will cause crashes!" );
                }
                return theEngine;
            }
            QScriptEngine* theEngine;
            //             QScriptEngineDebugger* debugger;

            // A QString with the name of the class represented by a ScriptAsset
            QHash<const ScriptingAsset*, QString> classNames;
            // A QScriptValue per script class definition
            QHash<const ScriptingAsset*, QScriptValue> scriptInstances;

            // Contains the full code of all scripts including class wrapping
            // We are going to have a problem with debugging...
            QString script;
            void buildScript();
            void appendScript( const ScriptingAsset* asset, QString name );
    };
}

using namespace GluonEngine;

GLUON_DEFINE_SINGLETON( ScriptingEngine )

ScriptingEngine::ScriptingEngine( QObject* /* parent */ )
    : d( new Private() )
{
}

ScriptingEngine::~ScriptingEngine()
{
    delete d;
}

QScriptSyntaxCheckResult
ScriptingEngine::registerAsset( const ScriptingAsset* asset )
{
    DEBUG_BLOCK

    // Own QScriptSyntaxCheckResult instances and set the values?!

    // Dumb...
    if( !asset )
    {
        DEBUG_TEXT( "Asset is empty" );
        return d->engine()->checkSyntax( QString( ')' ) );
    }
    // This is even dumberer...
    if( d->classNames.contains( asset ) )
    {
        DEBUG_TEXT( "Asset is already registered" );
        return d->engine()->checkSyntax( QString( '}' ) );
    }

    // Check the script for syntax
    QScriptSyntaxCheckResult result = d->engine()->checkSyntax( asset->data()->text() );
    if( result.state() == QScriptSyntaxCheckResult::Valid )
    {
        // Fix up the asset's name so as to be useable as a class name
        QString className( asset->fullyQualifiedName().remove( ' ' ).replace( '/', '_' ).replace( '-', '_' ) );
        // Add that to the classes listing
        d->classNames.insert( asset, className );
        // Build the new code
        d->appendScript( asset, className );
        //d->buildScript();
    }
    else
    {
        asset->debug( QString( "This script didn't pass the syntax checker (%2)\n" )
                      .arg( asset->fullyQualifiedName() )
                      .arg( result.errorMessage() ) );
    }

    return result;
}

void
ScriptingEngine::Private::buildScript()
{
    script.clear();
    resetEngine();

    QHash<const ScriptingAsset*, QString>::const_iterator i;
    for( i = classNames.constBegin(); i != classNames.constEnd(); ++i )
    {
        appendScript( i.key(), i.value() );
    }
}

void
ScriptingEngine::Private::appendScript( const GluonEngine::ScriptingAsset* asset, QString name )
{
    // Build the bit of script to add
    QString tmpScript = QString( "%2 = function() {\n%1};\n" ).arg( asset->data()->text() ).arg( name );
    QUrl tmpUrl = asset->file();
    QScriptValue evaluated = engine()->evaluate( tmpScript, tmpUrl.toLocalFile(), 0 );
    scriptInstances.insert( asset, evaluated );
    /// \TODO Add all those lines to the reverse map...
}

bool
ScriptingEngine::unregisterAsset( const ScriptingAsset* asset ) const
{
    if( !asset )
        return false;
    if( !d->classNames.contains( asset ) )
        return false;

    d->classNames.remove( asset );
    d->scriptInstances.remove( asset );
    if( d->scriptInstances.count() < 1 )
        d->resetEngine();
    else
        d->buildScript();

    return true;
}

bool
ScriptingEngine::isRegistered( const ScriptingAsset* asset ) const
{
    return d->classNames.contains( asset );
}

QScriptValue
ScriptingEngine::instantiateClass( const ScriptingAsset* asset ) const
{
    DEBUG_BLOCK
    // Ensure the asset exists...
    if( d->scriptInstances.contains( asset ) )
    {
        QScriptValue val = d->engine()->globalObject().property( d->classNames.value( asset ) );

        QScriptValue instance = val.construct();
        if( d->engine()->hasUncaughtException() )
        {
            QScriptValue exception = d->engine()->uncaughtException();
            asset->debug( QString( "Exception on class instantiation: %2\n at %1" )
                          .arg( d->engine()->uncaughtExceptionBacktrace().join( " --> " ) )
                          .arg( exception.toString() ) );
        }

        return instance;
    }
    // If we have got this far, that means we should be returning an invalid QScriptValue
    return QScriptValue();
}

QScriptValue
ScriptingEngine::instantiateClass( const QString& className ) const
{
    DEBUG_BLOCK
    QScriptValue val = d->engine()->globalObject().property( className );

    QScriptValue instance = val.construct();
    if( d->engine()->hasUncaughtException() )
    {
        const ScriptingAsset* asset = d->classNames.key( className );
        QScriptValue exception = d->engine()->uncaughtException();
        asset->debug( QString( "Exception on class instantiation: %2\n at %1" )
                      .arg( d->engine()->uncaughtExceptionBacktrace().join( " --> " ) )
                      .arg( exception.toString() ) );
    }

    return instance;
}

QString
ScriptingEngine::className( const ScriptingAsset* asset ) const
{
    return d->classNames.value( asset );
}

QScriptEngine*
ScriptingEngine::scriptEngine() const
{
    return instance()->d->engine();
}

#include "scriptingengine.moc"
<commit_msg>Engine: Prevent an infinite recursion in ScriptingComponent.<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (C) 2010 Dan Leinir Turthra Jensen <admin@leinir.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "scriptingengine.h"

#include "game.h"
#include "scriptingasset.h"
#include "scriptingcomponent.h"

#include <core/gluonobjectfactory.h>
#include <core/scriptengine.h>

#include <QtScript>
// #include <QScriptEngineDebugger>

namespace GluonEngine
{
    class ScriptingEngine::Private
    {
        public:
            Private()
                : theEngine( 0 )
            {
            }

            void resetEngine()
            {
                theEngine = 0;
                GluonCore::ScriptEngine::instance()->resetEngine();
            }
            QScriptEngine* engine()
            {
                if( !theEngine )
                {
                    theEngine = GluonCore::ScriptEngine::instance()->scriptEngine();

                    QScriptEngine::QObjectWrapOptions wrapOptions = QScriptEngine::AutoCreateDynamicProperties | QScriptEngine::ExcludeDeleteLater | QScriptEngine::PreferExistingWrapperObject;
                    QScriptEngine::ValueOwnership ownership = QScriptEngine::QtOwnership;
                    QScriptValue game = ScriptingEngine::instance()->scriptEngine()->newQObject( GluonEngine::Game::instance(), ownership, wrapOptions );
                    theEngine->globalObject().setProperty( "Game", game );
                }
                if( !theEngine )
                {
                    DEBUG_BLOCK
                    DEBUG_TEXT( "Somehow we do not have a scripting engine. This will cause crashes!" );
                }
                return theEngine;
            }
            QScriptEngine* theEngine;
            //             QScriptEngineDebugger* debugger;

            // A QString with the name of the class represented by a ScriptAsset
            QHash<const ScriptingAsset*, QString> classNames;
            // A QScriptValue per script class definition
            QHash<const ScriptingAsset*, QScriptValue> scriptInstances;

            // Contains the full code of all scripts including class wrapping
            // We are going to have a problem with debugging...
            QString script;
            void buildScript();
            void appendScript( const ScriptingAsset* asset, QString name );
    };
}

using namespace GluonEngine;

GLUON_DEFINE_SINGLETON( ScriptingEngine )

ScriptingEngine::ScriptingEngine( QObject* /* parent */ )
    : d( new Private() )
{
}

ScriptingEngine::~ScriptingEngine()
{
    delete d;
}

QScriptSyntaxCheckResult
ScriptingEngine::registerAsset( const ScriptingAsset* asset )
{
    DEBUG_BLOCK

    // Own QScriptSyntaxCheckResult instances and set the values?!

    // Dumb...
    if( !asset )
    {
        DEBUG_TEXT( "Asset is empty" );
        return d->engine()->checkSyntax( QString( ')' ) );
    }
    // This is even dumberer...
    if( d->classNames.contains( asset ) )
    {
        DEBUG_TEXT( "Asset is already registered" );
        return d->engine()->checkSyntax( QString( '}' ) );
    }

    // Check the script for syntax
    QScriptSyntaxCheckResult result = d->engine()->checkSyntax( asset->data()->text() );
    if( result.state() == QScriptSyntaxCheckResult::Valid )
    {
        // Fix up the asset's name so as to be useable as a class name
        QString className( asset->fullyQualifiedName().remove( ' ' ).replace( '/', '_' ).replace( '-', '_' ) );
        // Add that to the classes listing
        d->classNames.insert( asset, className );
        // Build the new code
        d->appendScript( asset, className );
        //d->buildScript();
    }
    else
    {
        asset->debug( QString( "This script didn't pass the syntax checker (%2)\n" )
                      .arg( asset->fullyQualifiedName() )
                      .arg( result.errorMessage() ) );
    }

    return result;
}

void
ScriptingEngine::Private::buildScript()
{
    script.clear();
    resetEngine();

    QHash<const ScriptingAsset*, QString>::const_iterator i;
    for( i = classNames.constBegin(); i != classNames.constEnd(); ++i )
    {
        appendScript( i.key(), i.value() );
    }
}

void
ScriptingEngine::Private::appendScript( const GluonEngine::ScriptingAsset* asset, QString name )
{
    // Build the bit of script to add
    QString tmpScript = QString( "%2 = function() {\n%1};\n" ).arg( asset->data()->text() ).arg( name );
    QUrl tmpUrl = asset->file();
    QScriptValue evaluated = engine()->evaluate( tmpScript, tmpUrl.toLocalFile(), 0 );
    scriptInstances.insert( asset, evaluated );
    /// \TODO Add all those lines to the reverse map...
}

bool
ScriptingEngine::unregisterAsset( const ScriptingAsset* asset ) const
{
    if( !asset )
        return false;
    if( !d->classNames.contains( asset ) )
        return false;

    d->classNames.remove( asset );
    d->scriptInstances.remove( asset );
    if( d->scriptInstances.count() < 1 )
        d->resetEngine();
    else
        d->buildScript();

    return true;
}

bool
ScriptingEngine::isRegistered( const ScriptingAsset* asset ) const
{
    return d->classNames.contains( asset );
}

QScriptValue
ScriptingEngine::instantiateClass( const ScriptingAsset* asset ) const
{
    DEBUG_BLOCK
    // Ensure the asset exists...
    if( d->scriptInstances.contains( asset ) )
    {
        QScriptValue val = d->engine()->globalObject().property( d->classNames.value( asset ) );

        QScriptValue instance = val.construct();
        if( d->engine()->hasUncaughtException() )
        {
            QScriptValue exception = d->engine()->uncaughtException();
            asset->debug( QString( "Exception on class instantiation: %2\n at %1" )
                          .arg( d->engine()->uncaughtExceptionBacktrace().join( " --> " ) )
                          .arg( exception.toString() ) );
        }

        return instance;
    }
    // If we have got this far, that means we should be returning an invalid QScriptValue
    return QScriptValue();
}

QScriptValue
ScriptingEngine::instantiateClass( const QString& className ) const
{
    DEBUG_BLOCK
    QScriptValue val = d->engine()->globalObject().property( className );

    QScriptValue instance = val.construct();
    if( d->engine()->hasUncaughtException() )
    {
        const ScriptingAsset* asset = d->classNames.key( className );
        QScriptValue exception = d->engine()->uncaughtException();
        asset->debug( QString( "Exception on class instantiation: %2\n at %1" )
                      .arg( d->engine()->uncaughtExceptionBacktrace().join( " --> " ) )
                      .arg( exception.toString() ) );
    }

    return instance;
}

QString
ScriptingEngine::className( const ScriptingAsset* asset ) const
{
    return d->classNames.value( asset );
}

QScriptEngine*
ScriptingEngine::scriptEngine() const
{
    return d->engine();
}

#include "scriptingengine.moc"
<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <dynamic_reconfigure/server.h>
#include <geometry_msgs/Point.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/Pose.h>
#include <geometry_msgs/PoseArray.h>
#include <image_transport/image_transport.h>
#include <kinect_tomato_searcher/SearchConfig.h>
#include <sensor_msgs/image_encodings.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>

class ShowImage {
public:
  ShowImage()
    : m_gamma(2048),
      RGB_WINDOW_NAME {"RGB image"},
      DEPTH_WINDOW_NAME {"Depth image"},
      BINARY_WINDOW_NAME {"Binary image"}
  {
    for(int i = 0; i < 2048; i++) {
      float v = i / 2048.0;
      v = std::pow(v, 3) * 6;
      m_gamma[i] = v * 6 * 256;
    }

    cv::namedWindow(RGB_WINDOW_NAME);
    cv::namedWindow(DEPTH_WINDOW_NAME);
    cv::namedWindow(BINARY_WINDOW_NAME);
  }

  ~ShowImage() {
    cv::destroyWindow(RGB_WINDOW_NAME);
    cv::destroyWindow(DEPTH_WINDOW_NAME);
    cv::destroyWindow(BINARY_WINDOW_NAME);
  }

  void showRGB(const cv::Mat& rgb) {
    imshow(RGB_WINDOW_NAME, rgb);
  }

  void showBinary(const cv::Mat& binary) {
    imshow(BINARY_WINDOW_NAME, binary);
  }

  void showDepth(const cv::Mat& depth) {
    cv::Mat buf = cv::Mat::zeros(depth.size(), CV_16UC1);
    cv::Mat output = cv::Mat::zeros(depth.size(), CV_8UC3);
    cv::normalize(depth, buf, 0, 2013, cv::NORM_MINMAX);

    for (int i = 0; i < depth.rows * depth.cols; i++) {
      int y = (int)(i / depth.cols);
      int x = (int)(i % depth.cols);
      int pval = m_gamma[buf.at<uint16_t>(i)];
      int lb = pval & 0xff;

      switch(pval >> 8) {
      case 0:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      case 1:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = lb;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 2:
        output.at<cv::Vec3b>(y,x)[2] = 255-lb;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 3:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = lb;
        break;
      case 4:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255;
        break;
      case 5:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      default:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      }
    }
    imshow(DEPTH_WINDOW_NAME, output);
  }

private:
  std::vector<uint16_t> m_gamma;
  const std::string RGB_WINDOW_NAME;
  const std::string DEPTH_WINDOW_NAME;
  const std::string BINARY_WINDOW_NAME;
};

class SearchTomato
{
public:
  SearchTomato()
    : tomato_contours {},
      siObj {},
      server {},
      f(std::bind(&dynamic_reconfigure_callback, std::placeholders::_1, std::placeholders::_2))
  {
    server.setCallback(f);
  }

  void update(const cv::Mat& capture_rgb) {
    siObj.showRGB(capture_rgb);
    cv::Mat binary_mat = cv::Mat::zeros(capture_rgb.size(), CV_8UC1);
    imageProsessing(capture_rgb, binary_mat);
    siObj.showBinary(binary_mat);
    searchTomatoContours(binary_mat, tomato_contours);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::Point& tomato_point) {
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    cv::drawContours(mask, tomato_contours, -1, cv::Scalar(255), CV_FILLED, 8);
    siObj.showBinary(mask);

    capture_depth.copyTo(maskedDepth, mask);
    return findClosePoint(maskedDepth, tomato_point);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::PoseArray& tomato_poses) {
    tomato_poses.poses.clear();
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    for (std::size_t i {0}; i < tomato_contours.size(); ++i) {
      cv::drawContours(mask, tomato_contours, i, cv::Scalar(255), CV_FILLED, 8);
      siObj.showBinary(mask);

      capture_depth.copyTo(maskedDepth, mask);
      findClosePoint(maskedDepth, tomato_poses);
    }
    return tomato_poses.poses.empty() ? false : true;
  }

private:
  static void dynamic_reconfigure_callback(kinect_tomato_searcher::SearchConfig& config, uint32_t level)
  {
    ROS_INFO("%d", config.int_param);
  }

  void imageProsessing(const cv::Mat& rgb, cv::Mat& binary) {
    cv::Mat blur, hsv;
    cv::GaussianBlur(rgb, blur, cv::Size(5, 5), 4.0, 4.0);
    cv::cvtColor(blur, hsv, CV_BGR2HSV);
    redFilter(hsv, binary);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 2);
    cv::erode(binary, binary, cv::Mat(), cv::Point(-1, -1), 4);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 1);
  }

  void searchTomatoContours(const cv::Mat& binary, std::vector<std::vector<cv::Point> >& tomato_contours) {
    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Point> contour;
    cv::Rect bounding_box;
    float ratio_balance;

    cv::findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    tomato_contours.clear();
    while (!contours.empty()) {
      contour = contours.back();
      contours.pop_back();

      bounding_box = cv::boundingRect(contour);
      ratio_balance = (float)bounding_box.width / (float)bounding_box.height;
      if (ratio_balance > 1.0f) ratio_balance = 1.0f / ratio_balance;

      // delete mismach. that is smaller or spier or empty
      if (bounding_box.area() >= 400 && bounding_box.area() < cv::contourArea(contour)*2 && ratio_balance > 0.4f)
        tomato_contours.push_back(contour);
    }
  }

  void redFilter(const cv::Mat& hsv, cv::Mat& binary) {
    int a, x, y;
    for(y = 0; y < hsv.rows; y++) {
      for(x = 0; x < hsv.cols; x++) {
        a = hsv.step*y+(x*3);
        if((hsv.data[a] <= 15 || hsv.data[a] >= 300) && hsv.data[a+1] >= 30 && hsv.data[a+2] >= 50)
          binary.at<unsigned char>(y,x) = 255;
        else
          binary.at<unsigned char>(y,x) = 0;
      }
    }
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::Point& tomato_point) {
    uint16_t depth;
    constexpr uint16_t OVER_RANGE = 3000;
    uint16_t close_depth = OVER_RANGE;

    for (int y = 0; y < maskedDepth.rows; y++) {
      const uint16_t* line_point = maskedDepth.ptr<uint16_t>(y);
      for (int x = 0; x < maskedDepth.cols; x++) {
        depth = line_point[x];
        if (512 < depth && depth <  close_depth) {
          tomato_point.x = depth; // for ros coordinate system
          tomato_point.y = x - maskedDepth.cols / 2; // for ros coordinate system
          tomato_point.z = -(y - maskedDepth.rows / 2); // for ros coordinate system
          close_depth = depth;
        }
      }
    }
    return (close_depth != OVER_RANGE ? true : false);
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::PoseArray& tomato_poses) {
    geometry_msgs::Point tomato_point {};
    if (findClosePoint(maskedDepth, tomato_point)) {
      geometry_msgs::Pose tomato_pose {};
      tomato_pose.position = tomato_point;
      tomato_pose.orientation.w = 1.0;
      tomato_poses.poses.push_back(tomato_pose);
      return true;
    }
    return false;
  }

  static uint16_t h_min_;
  static uint16_t h_max_;
  static uint16_t s_min_;
  static uint16_t s_max_;
  static uint16_t v_min_;
  static uint16_t v_max_;

  std::vector<std::vector<cv::Point> > tomato_contours;
  ShowImage siObj;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig> server;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig>::CallbackType f;
};

class ImageConverter {
private:
  ros::NodeHandle nh;
  ros::NodeHandle tomapo_nh;
  ros::Publisher poses_pub;
  geometry_msgs::PoseArray pub_msg;
  image_transport::ImageTransport it;
  image_transport::Subscriber rgb_sub;
  image_transport::Subscriber depth_sub;
  cv_bridge::CvImageConstPtr rgb_ptr;
  cv_bridge::CvImageConstPtr depth_ptr;
  SearchTomato stObj;
  const std::size_t height_;
  const double angle_x_per_piccell_;
  const double angle_y_per_piccell_;
  static constexpr double PI {3.141592653589793};

public:
  ImageConverter(const std::size_t width, const std::size_t height, const double angle_of_view_x, const double angle_of_view_y)
    : nh {},
      tomapo_nh {nh, "tomato_point"},
      poses_pub {tomapo_nh.advertise<geometry_msgs::PoseArray>("array", 1)},
      it {nh},
      rgb_sub {it.subscribe("color", 1, &ImageConverter::rgbCb, this)},
      depth_sub {it.subscribe("depth", 1, &ImageConverter::depthCb, this)},
      stObj {},
      height_ {height},
      angle_x_per_piccell_ {(angle_of_view_x * PI / 180.0) / width},
      angle_y_per_piccell_ {(angle_of_view_y * PI / 180.0) / height}
  {
    pub_msg.header.frame_id = "kinect";
  }

  /**
   * search tomato function.
   * get rgb image, and search tomato.
   * will tomato_point have data.
   *
   * @author Yusuke Doi
   */
  void rgbCb(const sensor_msgs::ImageConstPtr& msg) {
    // get rgb image on kinect
    try {
      rgb_ptr = cv_bridge::toCvShare(msg, "bgr8");
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by rgb: %s", e.what());
    }

    stObj.update(rgb_ptr->image);

    cv::waitKey(100);
  }

  void depthCb(const sensor_msgs::ImageConstPtr& msg) {
    try {
      depth_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_16UC1);
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by depth: %s", e.what());
    }

    if (stObj.searchTomato(depth_ptr->image, pub_msg)) {
      pub_msg.header.stamp = ros::Time::now();
      for (auto& tomato_pose : pub_msg.poses) {
        tomato_pose.position.y = tomato_pose.position.x * tan(angle_x_per_piccell_ * tomato_pose.position.y) / 1000.0; // convert to m
        tomato_pose.position.z = tomato_pose.position.x * tan(angle_y_per_piccell_ * tomato_pose.position.z) / 1000.0; // convert to m
        tomato_pose.position.x = tomato_pose.position.x / 1000.0; // convert to m
      }
      poses_pub.publish(pub_msg);
    }

    cv::waitKey(100);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "kinect_tomato_searcher_node");
  ImageConverter ic {512, 424, 70, 60};
  ros::spin();
  return 0;
}
<commit_msg>Update cpp for hsv parameter<commit_after>#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <dynamic_reconfigure/server.h>
#include <geometry_msgs/Point.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/Pose.h>
#include <geometry_msgs/PoseArray.h>
#include <image_transport/image_transport.h>
#include <kinect_tomato_searcher/SearchConfig.h>
#include <sensor_msgs/image_encodings.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>

class ShowImage {
public:
  ShowImage()
    : m_gamma(2048),
      RGB_WINDOW_NAME {"RGB image"},
      DEPTH_WINDOW_NAME {"Depth image"},
      BINARY_WINDOW_NAME {"Binary image"}
  {
    for(int i = 0; i < 2048; i++) {
      float v = i / 2048.0;
      v = std::pow(v, 3) * 6;
      m_gamma[i] = v * 6 * 256;
    }

    cv::namedWindow(RGB_WINDOW_NAME);
    cv::namedWindow(DEPTH_WINDOW_NAME);
    cv::namedWindow(BINARY_WINDOW_NAME);
  }

  ~ShowImage() {
    cv::destroyWindow(RGB_WINDOW_NAME);
    cv::destroyWindow(DEPTH_WINDOW_NAME);
    cv::destroyWindow(BINARY_WINDOW_NAME);
  }

  void showRGB(const cv::Mat& rgb) {
    imshow(RGB_WINDOW_NAME, rgb);
  }

  void showBinary(const cv::Mat& binary) {
    imshow(BINARY_WINDOW_NAME, binary);
  }

  void showDepth(const cv::Mat& depth) {
    cv::Mat buf = cv::Mat::zeros(depth.size(), CV_16UC1);
    cv::Mat output = cv::Mat::zeros(depth.size(), CV_8UC3);
    cv::normalize(depth, buf, 0, 2013, cv::NORM_MINMAX);

    for (int i = 0; i < depth.rows * depth.cols; i++) {
      int y = (int)(i / depth.cols);
      int x = (int)(i % depth.cols);
      int pval = m_gamma[buf.at<uint16_t>(i)];
      int lb = pval & 0xff;

      switch(pval >> 8) {
      case 0:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      case 1:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = lb;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 2:
        output.at<cv::Vec3b>(y,x)[2] = 255-lb;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 3:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = lb;
        break;
      case 4:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255;
        break;
      case 5:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      default:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      }
    }
    imshow(DEPTH_WINDOW_NAME, output);
  }

private:
  std::vector<uint16_t> m_gamma;
  const std::string RGB_WINDOW_NAME;
  const std::string DEPTH_WINDOW_NAME;
  const std::string BINARY_WINDOW_NAME;
};

class SearchTomato
{
public:
  SearchTomato()
    : tomato_contours {},
      siObj {},
      server {},
      f(std::bind(&dynamic_reconfigure_callback, std::placeholders::_1, std::placeholders::_2))
  {
    server.setCallback(f);
  }

  void update(const cv::Mat& capture_rgb) {
    siObj.showRGB(capture_rgb);
    cv::Mat binary_mat = cv::Mat::zeros(capture_rgb.size(), CV_8UC1);
    imageProsessing(capture_rgb, binary_mat);
    siObj.showBinary(binary_mat);
    searchTomatoContours(binary_mat, tomato_contours);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::Point& tomato_point) {
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    cv::drawContours(mask, tomato_contours, -1, cv::Scalar(255), CV_FILLED, 8);
    siObj.showBinary(mask);

    capture_depth.copyTo(maskedDepth, mask);
    return findClosePoint(maskedDepth, tomato_point);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::PoseArray& tomato_poses) {
    tomato_poses.poses.clear();
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    for (std::size_t i {0}; i < tomato_contours.size(); ++i) {
      cv::drawContours(mask, tomato_contours, i, cv::Scalar(255), CV_FILLED, 8);
      siObj.showBinary(mask);

      capture_depth.copyTo(maskedDepth, mask);
      findClosePoint(maskedDepth, tomato_poses);
    }
    return tomato_poses.poses.empty() ? false : true;
  }

private:
  static void dynamic_reconfigure_callback(kinect_tomato_searcher::SearchConfig& config, uint32_t level)
  {
    h_min_ = config.h_min;
    h_max_ = config.h_max;
    s_min_ = config.s_min;
    s_max_ = config.s_max;
    v_min_ = config.v_min;
    v_max_ = config.v_max;
  }

  void imageProsessing(const cv::Mat& rgb, cv::Mat& binary) {
    cv::Mat blur, hsv;
    cv::GaussianBlur(rgb, blur, cv::Size(5, 5), 4.0, 4.0);
    cv::cvtColor(blur, hsv, CV_BGR2HSV);
    redFilter(hsv, binary);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 2);
    cv::erode(binary, binary, cv::Mat(), cv::Point(-1, -1), 4);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 1);
  }

  void searchTomatoContours(const cv::Mat& binary, std::vector<std::vector<cv::Point> >& tomato_contours) {
    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Point> contour;
    cv::Rect bounding_box;
    float ratio_balance;

    cv::findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    tomato_contours.clear();
    while (!contours.empty()) {
      contour = contours.back();
      contours.pop_back();

      bounding_box = cv::boundingRect(contour);
      ratio_balance = (float)bounding_box.width / (float)bounding_box.height;
      if (ratio_balance > 1.0f) ratio_balance = 1.0f / ratio_balance;

      // delete mismach. that is smaller or spier or empty
      if (bounding_box.area() >= 400 && bounding_box.area() < cv::contourArea(contour)*2 && ratio_balance > 0.4f)
        tomato_contours.push_back(contour);
    }
  }

  void redFilter(const cv::Mat& hsv, cv::Mat& binary) {
    int a, x, y;
    for(y = 0; y < hsv.rows; y++) {
      for(x = 0; x < hsv.cols; x++) {
        a = hsv.step*y+(x*3);
        if((hsv.data[a] <= 15 || hsv.data[a] >= 300) && hsv.data[a+1] >= 30 && hsv.data[a+2] >= 50)
          binary.at<unsigned char>(y,x) = 255;
        else
          binary.at<unsigned char>(y,x) = 0;
      }
    }
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::Point& tomato_point) {
    uint16_t depth;
    constexpr uint16_t OVER_RANGE = 3000;
    uint16_t close_depth = OVER_RANGE;

    for (int y = 0; y < maskedDepth.rows; y++) {
      const uint16_t* line_point = maskedDepth.ptr<uint16_t>(y);
      for (int x = 0; x < maskedDepth.cols; x++) {
        depth = line_point[x];
        if (512 < depth && depth <  close_depth) {
          tomato_point.x = depth; // for ros coordinate system
          tomato_point.y = x - maskedDepth.cols / 2; // for ros coordinate system
          tomato_point.z = -(y - maskedDepth.rows / 2); // for ros coordinate system
          close_depth = depth;
        }
      }
    }
    return (close_depth != OVER_RANGE ? true : false);
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::PoseArray& tomato_poses) {
    geometry_msgs::Point tomato_point {};
    if (findClosePoint(maskedDepth, tomato_point)) {
      geometry_msgs::Pose tomato_pose {};
      tomato_pose.position = tomato_point;
      tomato_pose.orientation.w = 1.0;
      tomato_poses.poses.push_back(tomato_pose);
      return true;
    }
    return false;
  }

  static uint16_t h_min_;
  static uint16_t h_max_;
  static uint16_t s_min_;
  static uint16_t s_max_;
  static uint16_t v_min_;
  static uint16_t v_max_;

  std::vector<std::vector<cv::Point> > tomato_contours;
  ShowImage siObj;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig> server;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig>::CallbackType f;
};
uint16_t SearchTomato::h_min_;
uint16_t SearchTomato::h_max_;
uint16_t SearchTomato::s_min_;
uint16_t SearchTomato::s_max_;
uint16_t SearchTomato::v_min_;
uint16_t SearchTomato::v_max_;

class ImageConverter {
private:
  ros::NodeHandle nh;
  ros::NodeHandle tomapo_nh;
  ros::Publisher poses_pub;
  geometry_msgs::PoseArray pub_msg;
  image_transport::ImageTransport it;
  image_transport::Subscriber rgb_sub;
  image_transport::Subscriber depth_sub;
  cv_bridge::CvImageConstPtr rgb_ptr;
  cv_bridge::CvImageConstPtr depth_ptr;
  SearchTomato stObj;
  const std::size_t height_;
  const double angle_x_per_piccell_;
  const double angle_y_per_piccell_;
  static constexpr double PI {3.141592653589793};

public:
  ImageConverter(const std::size_t width, const std::size_t height, const double angle_of_view_x, const double angle_of_view_y)
    : nh {},
      tomapo_nh {nh, "tomato_point"},
      poses_pub {tomapo_nh.advertise<geometry_msgs::PoseArray>("array", 1)},
      it {nh},
      rgb_sub {it.subscribe("color", 1, &ImageConverter::rgbCb, this)},
      depth_sub {it.subscribe("depth", 1, &ImageConverter::depthCb, this)},
      stObj {},
      height_ {height},
      angle_x_per_piccell_ {(angle_of_view_x * PI / 180.0) / width},
      angle_y_per_piccell_ {(angle_of_view_y * PI / 180.0) / height}
  {
    pub_msg.header.frame_id = "kinect";
  }

  /**
   * search tomato function.
   * get rgb image, and search tomato.
   * will tomato_point have data.
   *
   * @author Yusuke Doi
   */
  void rgbCb(const sensor_msgs::ImageConstPtr& msg) {
    // get rgb image on kinect
    try {
      rgb_ptr = cv_bridge::toCvShare(msg, "bgr8");
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by rgb: %s", e.what());
    }

    stObj.update(rgb_ptr->image);

    cv::waitKey(100);
  }

  void depthCb(const sensor_msgs::ImageConstPtr& msg) {
    try {
      depth_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_16UC1);
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by depth: %s", e.what());
    }

    if (stObj.searchTomato(depth_ptr->image, pub_msg)) {
      pub_msg.header.stamp = ros::Time::now();
      for (auto& tomato_pose : pub_msg.poses) {
        tomato_pose.position.y = tomato_pose.position.x * tan(angle_x_per_piccell_ * tomato_pose.position.y) / 1000.0; // convert to m
        tomato_pose.position.z = tomato_pose.position.x * tan(angle_y_per_piccell_ * tomato_pose.position.z) / 1000.0; // convert to m
        tomato_pose.position.x = tomato_pose.position.x / 1000.0; // convert to m
      }
      poses_pub.publish(pub_msg);
    }

    cv::waitKey(100);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "kinect_tomato_searcher_node");
  ImageConverter ic {512, 424, 70, 60};
  ros::spin();
  return 0;
}
<|endoftext|>


<commit_before>#ifndef __TEXTURE_STRUCT_HPP_INCLUDED
#define __TEXTURE_STRUCT_HPP_INCLUDED

typedef struct TextureStruct
{
    TextureStruct()
    {
        // constructor.
        //
    }

    std::size_t texture_width;
    std::size_t texture_height;
} TextureStruct;

#endif
<commit_msg>Bugfix: added missing `#include` line.<commit_after>#ifndef __TEXTURE_STRUCT_HPP_INCLUDED
#define __TEXTURE_STRUCT_HPP_INCLUDED

// Include standard headers
#include <cstddef>  // std::size_t

typedef struct TextureStruct
{
    TextureStruct()
    {
        // constructor.
        //
    }

    std::size_t texture_width;
    std::size_t texture_height;
} TextureStruct;

#endif
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <activemq/connector/openwire/OpenWireFormatFactory.h>
#include <activemq/connector/openwire/OpenWireFormat.h>

#include <activemq/util/Boolean.h>

using namespace std;
using namespace activemq;
using namespace activemq::util;
using namespace activemq::wireformat;
using namespace activemq::connector;
using namespace activemq::transport;
using namespace activemq::exceptions;
using namespace activemq::connector::openwire;
using namespace activemq::connector::openwire::commands;

////////////////////////////////////////////////////////////////////////////////
WireFormat* OpenWireFormatFactory::createWireFormat(
    const util::Properties& properties )
        throw ( exceptions::IllegalStateException ) {

    WireFormatInfo* info = new WireFormatInfo();

    // Configure the version to use
    info->setVersion( 2 );

    // parse params out of the properties
    info->setStackTraceEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.stackTraceEnabled",
                                "false" ) ) );
    info->setCacheEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.cacheEnabled",
                                "false" ) ) );
    info->setTcpNoDelayEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.tcpNoDelayEnabled",
                                "false" ) ) );
    info->setTightEncodingEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.tightEncodingEnabled",
                                "false" ) ) );
    info->setSizePrefixDisabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.sizePrefixDisabled",
                                "false" ) ) );

    // Create the Openwire Format Object
    OpenWireFormat* f = new OpenWireFormat( properties );

    // give the format object the ownership
    f->setPreferedWireFormatInfo( info );

    return f;
}

<commit_msg>https://issues.apache.org/activemq/browse/AMQCPP-142<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <activemq/connector/openwire/OpenWireFormatFactory.h>
#include <activemq/connector/openwire/OpenWireFormat.h>

#include <activemq/util/Boolean.h>
#include <activemq/util/Integer.h>

using namespace std;
using namespace activemq;
using namespace activemq::util;
using namespace activemq::wireformat;
using namespace activemq::connector;
using namespace activemq::transport;
using namespace activemq::exceptions;
using namespace activemq::connector::openwire;
using namespace activemq::connector::openwire::commands;

////////////////////////////////////////////////////////////////////////////////
WireFormat* OpenWireFormatFactory::createWireFormat(
    const util::Properties& properties )
        throw ( exceptions::IllegalStateException ) {

    WireFormatInfo* info = new WireFormatInfo();

    // Configure the version to use
    info->setVersion( Integer::parseInt(
            properties.getProperty( "wireFormat.version", "2" ) ) );

    // parse params out of the properties
    info->setStackTraceEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.stackTraceEnabled",
                                "false" ) ) );
    info->setCacheEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.cacheEnabled",
                                "false" ) ) );
    info->setTcpNoDelayEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.tcpNoDelayEnabled",
                                "false" ) ) );
    info->setTightEncodingEnabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.tightEncodingEnabled",
                                "false" ) ) );
    info->setSizePrefixDisabled( Boolean::parseBoolean(
        properties.getProperty( "wireFormat.sizePrefixDisabled",
                                "false" ) ) );

    // Create the Openwire Format Object
    OpenWireFormat* f = new OpenWireFormat( properties );

    // give the format object the ownership
    f->setPreferedWireFormatInfo( info );

    return f;
}

<|endoftext|>


<commit_before>/*=========================================================================

Program:   Visualization Toolkit
Module:    TestRandomPContingencyStatisticsMPI.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*
 * Copyright 2009 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */
// .SECTION Thanks
// Thanks to Philippe Pebay for implementing this test.

#include <mpi.h>

#include "vtkContingencyStatistics.h"
#include "vtkPContingencyStatistics.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkMath.h"
#include "vtkMPIController.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStdString.h"
#include "vtkTable.h"
#include "vtkTimerLog.h"
#include "vtkVariantArray.h"

struct RandomContingencyStatisticsArgs
{
  int nVals;
  double stdev;
  double absTol;
  int* retVal;
  int ioRank;
  int argc;
  char** argv;
};

// This will be called by all processes
void RandomContingencyStatistics( vtkMultiProcessController* controller, void* arg )
{
  // Get test parameters
  RandomContingencyStatisticsArgs* args = reinterpret_cast<RandomContingencyStatisticsArgs*>( arg );
  *(args->retVal) = 0;

  // Get MPI communicator
  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // Get local rank
  int myRank = com->GetLocalProcessId();

  // Seed random number generator
  vtkMath::RandomSeed( static_cast<int>( vtkTimerLog::GetUniversalTime() ) * ( myRank + 1 ) );

  // Generate an input table that contains samples of mutually independent discrete random variables
  int nVariables = 2;
  vtkIntArray* intArray[2];
  vtkStdString columnNames[] = { "Rounded Normal 0",
                                 "Rounded Normal 1" };

  vtkTable* inputData = vtkTable::New();
  // Discrete rounded normal samples
  for ( int c = 0; c < nVariables; ++ c )
    {
    intArray[c] = vtkIntArray::New();
    intArray[c]->SetNumberOfComponents( 1 );
    intArray[c]->SetName( columnNames[c] );

    for ( int r = 0; r < args->nVals; ++ r )
      {
      intArray[c]->InsertNextValue( static_cast<int>( vtkMath::Round( vtkMath::Gaussian() * args->stdev ) ) );
      }

    inputData->AddColumn( intArray[c] );
    intArray[c]->Delete();
    }

  // Entropies in the summary table should normally be retrieved as follows:
  //   column 2: H(X,Y)
  //   column 3: H(Y|X)
  //   column 4: H(X|Y)
  int iEntropies[] = { 2,
                       3,
                       4 };
  int nEntropies = 3; // correct number of entropies reported in the summary table

  // ************************** Contingency Statistics **************************

  // Synchronize and start clock
  com->Barrier();
  vtkTimerLog *timer=vtkTimerLog::New();
  timer->StartTimer();

  // Instantiate a parallel contingency statistics engine and set its ports
  vtkPContingencyStatistics* pcs = vtkPContingencyStatistics::New();
  pcs->SetInput( vtkStatisticsAlgorithm::INPUT_DATA, inputData );
  vtkMultiBlockDataSet* outputMetaDS = vtkMultiBlockDataSet::SafeDownCast( pcs->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  // Select column pairs (uniform vs. uniform, normal vs. normal)
  pcs->AddColumnPair( columnNames[0], columnNames[1] );

  // Test (in parallel) with Learn, Derive, and Assess options turned on
  pcs->SetLearnOption( true );
  pcs->SetDeriveOption( true );
  pcs->SetAssessOption( true );
  pcs->Update();

  // Synchronize and stop clock
  com->Barrier();
  timer->StopTimer();

  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Completed parallel calculation of contingency statistics (with assessment):\n"
         << "   Wall time: "
         << timer->GetElapsedTime()
         << " sec.\n";
    }

  // Now perform verifications
  vtkTable* outputSummary = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 0 ) );
  vtkTable* outputContingency = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 1 ) );

  vtkIdType nRowSumm = outputSummary->GetNumberOfRows();
  int testIntValue;
  double testDoubleValue1;
  double testDoubleValue2;
  int numProcs = controller->GetNumberOfProcesses();

  // Verify that all processes have the same grand total and contingency tables size
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that all processes have the same grand total and contingency tables size.\n";
    }

  // Gather all grand totals
  int GT_l = outputContingency->GetValueByName( 0, "Cardinality" ).ToInt();
  int* GT_g = new int[numProcs];
  com->AllGather( &GT_l,
                  GT_g,
                  1 );

  // Use the first grand total as reference (as they all must be equal)
  testIntValue = GT_g[0];

  // Print out all grand totals
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( int i = 0; i < numProcs; ++ i )
      {
      cout << "     On process "
           << i
           << ", grand total = "
           << GT_g[i]
           << ", contingency table size = "
           << outputContingency->GetNumberOfRows()
           << "\n";

      if ( GT_g[i] != testIntValue )
        {
        vtkGenericWarningMacro("Incorrect CDF.");
        *(args->retVal) = 1;
        }
      }
    }

  // Verify that information entropies on all processes make sense
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that information entropies are consistent on all processes.\n";
    }

  testIntValue = outputSummary->GetNumberOfColumns();

  if ( testIntValue != nEntropies + 2 )
    {
    vtkGenericWarningMacro("Reported an incorrect number of columns in the summary table: "
                           << testIntValue
                           << " != "
                           << nEntropies + 2
                           << ".");
    *(args->retVal) = 1;
    }
  else
    {
    // For each row in the summary table, fetch variable names and information entropies
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get local information entropies from summary table
      double* H_l = new double[nEntropies];
      for ( vtkIdType c = 0; c < nEntropies; ++ c )
        {
        H_l[c] = outputSummary->GetValue( k, iEntropies[c] ).ToDouble();
        }

      // Gather all local entropies
      double* H_g = new double[nEntropies * numProcs];
      com->AllGather( H_l,
                      H_g,
                      nEntropies );

      // Print out all entropies
      if ( com->GetLocalProcessId() == args->ioRank )
        {
        // Get variable names
        cout << "   (X,Y) = ("
             << outputSummary->GetValue( k, 0 ).ToString()
             << ", "
             << outputSummary->GetValue( k, 1 ).ToString()
             << "):\n";

        for ( int i = 0; i < numProcs; ++ i )
          {
          cout << "     On process "
               << i;

          for ( vtkIdType c = 0; c < nEntropies; ++ c )
            {
            cout << ", "
                 << outputSummary->GetColumnName( iEntropies[c] )
                 << " = "
                 << H_g[nEntropies * i + c];
            }

          cout << "\n";

          // Make sure that H(X,Y) >= H(Y|X)+ H(X|Y)
          testDoubleValue1 = H_g[nEntropies * i + 1] + H_g[nEntropies * i + 2]; // H(Y|X)+ H(X|Y)

          if ( testDoubleValue1 > H_g[nEntropies * i] )
            {
            vtkGenericWarningMacro("Reported inconsistent information entropies: H(X,Y) = "
                                   << H_g[nEntropies * i]
                                   << " < "
                                   << testDoubleValue1
                                   << " = H(Y|X)+ H(X|Y).");
            *(args->retVal) = 1;
            }
          }
        cout << "   where H(X,Y) = - Sum_{x,y} p(x,y) log p(x,y) and H(X|Y) = - Sum_{x,y} p(x,y) log p(x|y).\n";
        }

      // Clean up
      delete [] H_l;
      delete [] H_g;
      }
    }

  // Verify that the local and global CDFs sum to 1 within presribed relative tolerance
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that local and global CDFs sum to 1 (within "
         << args->absTol
         << " relative tolerance).\n";
    }

  vtkIdTypeArray* keys = vtkIdTypeArray::SafeDownCast( outputContingency->GetColumnByName( "Key" ) );
  if ( ! keys )
    {
    cout << "*** Error: "
         << "Empty contingency table column 'Key' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  vtkStdString proName = "P";
  vtkDoubleArray* prob = vtkDoubleArray::SafeDownCast( outputContingency->GetColumnByName( proName ) );
  if ( ! prob )
    {
    cout << "*** Error: "
         << "Empty contingency table column '"
         << proName
         << "' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  // Calculate local CDFs
  double* cdf_l = new double[nRowSumm];
  for ( vtkIdType k = 0; k < nRowSumm; ++ k )
    {
    cdf_l[k] = 0;
    }

  int n = outputContingency->GetNumberOfRows();

  // Skip first entry which is reserved for the cardinality
  for ( vtkIdType r = 1; r < n; ++ r )
    {
    cdf_l[keys->GetValue( r )] += prob->GetValue( r );
    }

  // Gather all local CDFs
  double* cdf_g = new double[nRowSumm * numProcs];
  com->AllGather( cdf_l,
                  cdf_g,
                  nRowSumm );

  // Print out all local and global CDFs
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get variable names
      cout << "   (X,Y) = ("
           << outputSummary->GetValue( k, 0 ).ToString()
           << ", "
           << outputSummary->GetValue( k, 1 ).ToString()
           << "):\n";

      for ( int i = 0; i < numProcs; ++ i )
        {
        testDoubleValue1 = cdf_l[k];
        testDoubleValue2 = cdf_g[i * nRowSumm + k];

        cout << "     On process "
             << i
             << ", local CDF = "
             << testDoubleValue1
             << ", global CDF = "
             << testDoubleValue2
             << "\n";

        // Verify that local CDF = 1 (within absTol)
        if ( fabs ( 1. - testDoubleValue1 ) > args->absTol )
          {
          vtkGenericWarningMacro("Incorrect local CDF.");
          *(args->retVal) = 1;
          }

        // Verify that global CDF = 1 (within absTol)
        if ( fabs ( 1. - testDoubleValue2 ) > args->absTol )
          {
          vtkGenericWarningMacro("Incorrect global CDF.");
          *(args->retVal) = 1;
          }
        }
      }
    }

  // Clean up
  delete [] cdf_l;
  delete [] cdf_g;
  pcs->Delete();
  inputData->Delete();
  timer->Delete();
}

//----------------------------------------------------------------------------
int main( int argc, char** argv )
{
  // **************************** MPI Initialization ***************************
  vtkMPIController* controller = vtkMPIController::New();
  controller->Initialize( &argc, &argv );

  // If an MPI controller was not created, terminate in error.
  if ( ! controller->IsA( "vtkMPIController" ) )
    {
    vtkGenericWarningMacro("Failed to initialize a MPI controller.");
    controller->Delete();
    return 1;
    }

  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // ************************** Find an I/O node ********************************
  int* ioPtr;
  int ioRank;
  int flag;

  MPI_Attr_get( MPI_COMM_WORLD,
                MPI_IO,
                &ioPtr,
                &flag );

  if ( ( ! flag ) || ( *ioPtr == MPI_PROC_NULL ) )
    {
    // Getting MPI attributes did not return any I/O node found.
    ioRank = MPI_PROC_NULL;
    vtkGenericWarningMacro("No MPI I/O nodes found.");

    // As no I/O node was found, we need an unambiguous way to report the problem.
    // This is the only case when a testValue of -1 will be returned
    controller->Finalize();
    controller->Delete();

    return -1;
    }
  else
    {
    if ( *ioPtr == MPI_ANY_SOURCE )
      {
      // Anyone can do the I/O trick--just pick node 0.
      ioRank = 0;
      }
    else
      {
      // Only some nodes can do I/O. Make sure everyone agrees on the choice (min).
      com->AllReduce( ioPtr,
                      &ioRank,
                      1,
                      vtkCommunicator::MIN_OP );
      }
    }

  // ************************** Initialize test *********************************
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Process "
         << ioRank
         << " will be the I/O node.\n";
    }

  // Parameters for regression test.
  int testValue = 0;
  RandomContingencyStatisticsArgs args;

  args.nVals = 1000000;
  args.stdev = 5.;
  args.absTol = 1.e-6;
  args.retVal = &testValue;
  args.ioRank = ioRank;
  args.argc = argc;
  args.argv = argv;

  // Check how many processes have been made available
  int numProcs = controller->GetNumberOfProcesses();
  if ( controller->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Running test with "
         << numProcs
         << " processes and standard deviation = "
         << args.stdev
         << ".\n";
    }

  // Execute the function named "process" on both processes
  controller->SetSingleMethod( RandomContingencyStatistics, &args );
  controller->SingleMethodExecute();

  // Clean up and exit
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Test completed.\n\n";
    }

  controller->Finalize();
  controller->Delete();

  return testValue;
}
<commit_msg>BUG: (inconsequential so far) forgot a delete []<commit_after>/*=========================================================================

Program:   Visualization Toolkit
Module:    TestRandomPContingencyStatisticsMPI.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*
 * Copyright 2009 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */
// .SECTION Thanks
// Thanks to Philippe Pebay for implementing this test.

#include <mpi.h>

#include "vtkContingencyStatistics.h"
#include "vtkPContingencyStatistics.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkMath.h"
#include "vtkMPIController.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStdString.h"
#include "vtkTable.h"
#include "vtkTimerLog.h"
#include "vtkVariantArray.h"

struct RandomContingencyStatisticsArgs
{
  int nVals;
  double stdev;
  double absTol;
  int* retVal;
  int ioRank;
  int argc;
  char** argv;
};

// This will be called by all processes
void RandomContingencyStatistics( vtkMultiProcessController* controller, void* arg )
{
  // Get test parameters
  RandomContingencyStatisticsArgs* args = reinterpret_cast<RandomContingencyStatisticsArgs*>( arg );
  *(args->retVal) = 0;

  // Get MPI communicator
  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // Get local rank
  int myRank = com->GetLocalProcessId();

  // Seed random number generator
  vtkMath::RandomSeed( static_cast<int>( vtkTimerLog::GetUniversalTime() ) * ( myRank + 1 ) );

  // Generate an input table that contains samples of mutually independent discrete random variables
  int nVariables = 2;
  vtkIntArray* intArray[2];
  vtkStdString columnNames[] = { "Rounded Normal 0",
                                 "Rounded Normal 1" };

  vtkTable* inputData = vtkTable::New();
  // Discrete rounded normal samples
  for ( int c = 0; c < nVariables; ++ c )
    {
    intArray[c] = vtkIntArray::New();
    intArray[c]->SetNumberOfComponents( 1 );
    intArray[c]->SetName( columnNames[c] );

    for ( int r = 0; r < args->nVals; ++ r )
      {
      intArray[c]->InsertNextValue( static_cast<int>( vtkMath::Round( vtkMath::Gaussian() * args->stdev ) ) );
      }

    inputData->AddColumn( intArray[c] );
    intArray[c]->Delete();
    }

  // Entropies in the summary table should normally be retrieved as follows:
  //   column 2: H(X,Y)
  //   column 3: H(Y|X)
  //   column 4: H(X|Y)
  int iEntropies[] = { 2,
                       3,
                       4 };
  int nEntropies = 3; // correct number of entropies reported in the summary table

  // ************************** Contingency Statistics **************************

  // Synchronize and start clock
  com->Barrier();
  vtkTimerLog *timer=vtkTimerLog::New();
  timer->StartTimer();

  // Instantiate a parallel contingency statistics engine and set its ports
  vtkPContingencyStatistics* pcs = vtkPContingencyStatistics::New();
  pcs->SetInput( vtkStatisticsAlgorithm::INPUT_DATA, inputData );
  vtkMultiBlockDataSet* outputMetaDS = vtkMultiBlockDataSet::SafeDownCast( pcs->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  // Select column pairs (uniform vs. uniform, normal vs. normal)
  pcs->AddColumnPair( columnNames[0], columnNames[1] );

  // Test (in parallel) with Learn, Derive, and Assess options turned on
  pcs->SetLearnOption( true );
  pcs->SetDeriveOption( true );
  pcs->SetAssessOption( true );
  pcs->Update();

  // Synchronize and stop clock
  com->Barrier();
  timer->StopTimer();

  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Completed parallel calculation of contingency statistics (with assessment):\n"
         << "   Wall time: "
         << timer->GetElapsedTime()
         << " sec.\n";
    }

  // Now perform verifications
  vtkTable* outputSummary = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 0 ) );
  vtkTable* outputContingency = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 1 ) );

  vtkIdType nRowSumm = outputSummary->GetNumberOfRows();
  int testIntValue;
  double testDoubleValue1;
  double testDoubleValue2;
  int numProcs = controller->GetNumberOfProcesses();

  // Verify that all processes have the same grand total and contingency tables size
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that all processes have the same grand total and contingency tables size.\n";
    }

  // Gather all grand totals
  int GT_l = outputContingency->GetValueByName( 0, "Cardinality" ).ToInt();
  int* GT_g = new int[numProcs];
  com->AllGather( &GT_l,
                  GT_g,
                  1 );

  // Use the first grand total as reference (as they all must be equal)
  testIntValue = GT_g[0];

  // Print out all grand totals
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( int i = 0; i < numProcs; ++ i )
      {
      cout << "     On process "
           << i
           << ", grand total = "
           << GT_g[i]
           << ", contingency table size = "
           << outputContingency->GetNumberOfRows()
           << "\n";

      if ( GT_g[i] != testIntValue )
        {
        vtkGenericWarningMacro("Incorrect CDF.");
        *(args->retVal) = 1;
        }
      }
    }

  // Verify that information entropies on all processes make sense
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that information entropies are consistent on all processes.\n";
    }

  testIntValue = outputSummary->GetNumberOfColumns();

  if ( testIntValue != nEntropies + 2 )
    {
    vtkGenericWarningMacro("Reported an incorrect number of columns in the summary table: "
                           << testIntValue
                           << " != "
                           << nEntropies + 2
                           << ".");
    *(args->retVal) = 1;
    }
  else
    {
    // For each row in the summary table, fetch variable names and information entropies
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get local information entropies from summary table
      double* H_l = new double[nEntropies];
      for ( vtkIdType c = 0; c < nEntropies; ++ c )
        {
        H_l[c] = outputSummary->GetValue( k, iEntropies[c] ).ToDouble();
        }

      // Gather all local entropies
      double* H_g = new double[nEntropies * numProcs];
      com->AllGather( H_l,
                      H_g,
                      nEntropies );

      // Print out all entropies
      if ( com->GetLocalProcessId() == args->ioRank )
        {
        // Get variable names
        cout << "   (X,Y) = ("
             << outputSummary->GetValue( k, 0 ).ToString()
             << ", "
             << outputSummary->GetValue( k, 1 ).ToString()
             << "):\n";

        for ( int i = 0; i < numProcs; ++ i )
          {
          cout << "     On process "
               << i;

          for ( vtkIdType c = 0; c < nEntropies; ++ c )
            {
            cout << ", "
                 << outputSummary->GetColumnName( iEntropies[c] )
                 << " = "
                 << H_g[nEntropies * i + c];
            }

          cout << "\n";

          // Make sure that H(X,Y) >= H(Y|X)+ H(X|Y)
          testDoubleValue1 = H_g[nEntropies * i + 1] + H_g[nEntropies * i + 2]; // H(Y|X)+ H(X|Y)

          if ( testDoubleValue1 > H_g[nEntropies * i] )
            {
            vtkGenericWarningMacro("Reported inconsistent information entropies: H(X,Y) = "
                                   << H_g[nEntropies * i]
                                   << " < "
                                   << testDoubleValue1
                                   << " = H(Y|X)+ H(X|Y).");
            *(args->retVal) = 1;
            }
          }
        cout << "   where H(X,Y) = - Sum_{x,y} p(x,y) log p(x,y) and H(X|Y) = - Sum_{x,y} p(x,y) log p(x|y).\n";
        }

      // Clean up
      delete [] H_l;
      delete [] H_g;
      }
    }

  // Verify that the local and global CDFs sum to 1 within presribed relative tolerance
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that local and global CDFs sum to 1 (within "
         << args->absTol
         << " relative tolerance).\n";
    }

  vtkIdTypeArray* keys = vtkIdTypeArray::SafeDownCast( outputContingency->GetColumnByName( "Key" ) );
  if ( ! keys )
    {
    cout << "*** Error: "
         << "Empty contingency table column 'Key' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  vtkStdString proName = "P";
  vtkDoubleArray* prob = vtkDoubleArray::SafeDownCast( outputContingency->GetColumnByName( proName ) );
  if ( ! prob )
    {
    cout << "*** Error: "
         << "Empty contingency table column '"
         << proName
         << "' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  // Calculate local CDFs
  double* cdf_l = new double[nRowSumm];
  for ( vtkIdType k = 0; k < nRowSumm; ++ k )
    {
    cdf_l[k] = 0;
    }

  int n = outputContingency->GetNumberOfRows();

  // Skip first entry which is reserved for the cardinality
  for ( vtkIdType r = 1; r < n; ++ r )
    {
    cdf_l[keys->GetValue( r )] += prob->GetValue( r );
    }

  // Gather all local CDFs
  double* cdf_g = new double[nRowSumm * numProcs];
  com->AllGather( cdf_l,
                  cdf_g,
                  nRowSumm );

  // Print out all local and global CDFs
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get variable names
      cout << "   (X,Y) = ("
           << outputSummary->GetValue( k, 0 ).ToString()
           << ", "
           << outputSummary->GetValue( k, 1 ).ToString()
           << "):\n";

      for ( int i = 0; i < numProcs; ++ i )
        {
        testDoubleValue1 = cdf_l[k];
        testDoubleValue2 = cdf_g[i * nRowSumm + k];

        cout << "     On process "
             << i
             << ", local CDF = "
             << testDoubleValue1
             << ", global CDF = "
             << testDoubleValue2
             << "\n";

        // Verify that local CDF = 1 (within absTol)
        if ( fabs ( 1. - testDoubleValue1 ) > args->absTol )
          {
          vtkGenericWarningMacro("Incorrect local CDF.");
          *(args->retVal) = 1;
          }

        // Verify that global CDF = 1 (within absTol)
        if ( fabs ( 1. - testDoubleValue2 ) > args->absTol )
          {
          vtkGenericWarningMacro("Incorrect global CDF.");
          *(args->retVal) = 1;
          }
        }
      }
    }

  // Clean up
  delete [] GT_g;
  delete [] cdf_l;
  delete [] cdf_g;
  pcs->Delete();
  inputData->Delete();
  timer->Delete();
}

//----------------------------------------------------------------------------
int main( int argc, char** argv )
{
  // **************************** MPI Initialization ***************************
  vtkMPIController* controller = vtkMPIController::New();
  controller->Initialize( &argc, &argv );

  // If an MPI controller was not created, terminate in error.
  if ( ! controller->IsA( "vtkMPIController" ) )
    {
    vtkGenericWarningMacro("Failed to initialize a MPI controller.");
    controller->Delete();
    return 1;
    }

  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // ************************** Find an I/O node ********************************
  int* ioPtr;
  int ioRank;
  int flag;

  MPI_Attr_get( MPI_COMM_WORLD,
                MPI_IO,
                &ioPtr,
                &flag );

  if ( ( ! flag ) || ( *ioPtr == MPI_PROC_NULL ) )
    {
    // Getting MPI attributes did not return any I/O node found.
    ioRank = MPI_PROC_NULL;
    vtkGenericWarningMacro("No MPI I/O nodes found.");

    // As no I/O node was found, we need an unambiguous way to report the problem.
    // This is the only case when a testValue of -1 will be returned
    controller->Finalize();
    controller->Delete();

    return -1;
    }
  else
    {
    if ( *ioPtr == MPI_ANY_SOURCE )
      {
      // Anyone can do the I/O trick--just pick node 0.
      ioRank = 0;
      }
    else
      {
      // Only some nodes can do I/O. Make sure everyone agrees on the choice (min).
      com->AllReduce( ioPtr,
                      &ioRank,
                      1,
                      vtkCommunicator::MIN_OP );
      }
    }

  // ************************** Initialize test *********************************
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Process "
         << ioRank
         << " will be the I/O node.\n";
    }

  // Parameters for regression test.
  int testValue = 0;
  RandomContingencyStatisticsArgs args;

  args.nVals = 1000000;
  args.stdev = 5.;
  args.absTol = 1.e-6;
  args.retVal = &testValue;
  args.ioRank = ioRank;
  args.argc = argc;
  args.argv = argv;

  // Check how many processes have been made available
  int numProcs = controller->GetNumberOfProcesses();
  if ( controller->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Running test with "
         << numProcs
         << " processes and standard deviation = "
         << args.stdev
         << ".\n";
    }

  // Execute the function named "process" on both processes
  controller->SetSingleMethod( RandomContingencyStatistics, &args );
  controller->SingleMethodExecute();

  // Clean up and exit
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Test completed.\n\n";
    }

  controller->Finalize();
  controller->Delete();

  return testValue;
}
<|endoftext|>


<commit_before>/*
 *  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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * HealthCheckedConnection.cpp
 * Copyright (C) 2012 Simon Newton
 */

#include "ola/Logging.h"
#include "ola/network/HealthCheckedConnection.h"
#include "ola/thread/SchedulerInterface.h"

namespace ola {
namespace network {

HealthCheckedConnection::HealthCheckedConnection(
  ola::thread::SchedulerInterface *scheduler,
  const ola::TimeInterval timeout_interval)
    : m_scheduler(scheduler),
      m_heartbeat_interval(timeout_interval),
      m_send_timeout_id(ola::thread::INVALID_TIMEOUT),
      m_receive_timeout_id(ola::thread::INVALID_TIMEOUT) {
}

HealthCheckedConnection::~HealthCheckedConnection() {
  if (m_send_timeout_id != ola::thread::INVALID_TIMEOUT)
    m_scheduler->RemoveTimeout(m_send_timeout_id);
  if (m_receive_timeout_id != ola::thread::INVALID_TIMEOUT)
    m_scheduler->RemoveTimeout(m_receive_timeout_id);
}

bool HealthCheckedConnection::Setup() {
  // setup the RX timeout
  ResumeTimer();

  // send a heartbeat now and setup the TX timer
  SendHeartbeat();
  HeartbeatSent();
  return true;
}

/**
 * Reset the send timer
 */
void HealthCheckedConnection::HeartbeatSent() {
  if (m_send_timeout_id != ola::thread::INVALID_TIMEOUT)
    m_scheduler->RemoveTimeout(m_send_timeout_id);
  m_send_timeout_id = m_scheduler->RegisterRepeatingTimeout(
    m_heartbeat_interval,
    NewCallback(this, &HealthCheckedConnection::SendNextHeartbeat));
}

/**
 * Reset the RX timer
 */
void HealthCheckedConnection::HeartbeatReceived() {
  m_scheduler->RemoveTimeout(m_receive_timeout_id);
  UpdateReceiveTimer();
}

/**
 * Pause the receive timer
 */
void HealthCheckedConnection::PauseTimer() {
  if (m_receive_timeout_id != ola::thread::INVALID_TIMEOUT) {
    m_scheduler->RemoveTimeout(m_receive_timeout_id);
    m_receive_timeout_id = ola::thread::INVALID_TIMEOUT;
  }
}

/**
 * Resume the receive timer
 */
void HealthCheckedConnection::ResumeTimer() {
  if (m_receive_timeout_id == ola::thread::INVALID_TIMEOUT)
    UpdateReceiveTimer();
}

bool HealthCheckedConnection::SendNextHeartbeat() {
  SendHeartbeat();
  return true;
}

void HealthCheckedConnection::UpdateReceiveTimer() {
  TimeInterval timeout_interval(2.5 * m_heartbeat_interval.AsInt());
  m_receive_timeout_id = m_scheduler->RegisterSingleTimeout(
    timeout_interval,
    NewSingleCallback(this, &HealthCheckedConnection::HeartbeatTimeout));
}
}  // network
}  // ola
<commit_msg> * fix another warning<commit_after>/*
 *  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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * HealthCheckedConnection.cpp
 * Copyright (C) 2012 Simon Newton
 */

#include "ola/Logging.h"
#include "ola/network/HealthCheckedConnection.h"
#include "ola/thread/SchedulerInterface.h"

namespace ola {
namespace network {

HealthCheckedConnection::HealthCheckedConnection(
  ola::thread::SchedulerInterface *scheduler,
  const ola::TimeInterval timeout_interval)
    : m_scheduler(scheduler),
      m_heartbeat_interval(timeout_interval),
      m_send_timeout_id(ola::thread::INVALID_TIMEOUT),
      m_receive_timeout_id(ola::thread::INVALID_TIMEOUT) {
}

HealthCheckedConnection::~HealthCheckedConnection() {
  if (m_send_timeout_id != ola::thread::INVALID_TIMEOUT)
    m_scheduler->RemoveTimeout(m_send_timeout_id);
  if (m_receive_timeout_id != ola::thread::INVALID_TIMEOUT)
    m_scheduler->RemoveTimeout(m_receive_timeout_id);
}

bool HealthCheckedConnection::Setup() {
  // setup the RX timeout
  ResumeTimer();

  // send a heartbeat now and setup the TX timer
  SendHeartbeat();
  HeartbeatSent();
  return true;
}

/**
 * Reset the send timer
 */
void HealthCheckedConnection::HeartbeatSent() {
  if (m_send_timeout_id != ola::thread::INVALID_TIMEOUT)
    m_scheduler->RemoveTimeout(m_send_timeout_id);
  m_send_timeout_id = m_scheduler->RegisterRepeatingTimeout(
    m_heartbeat_interval,
    NewCallback(this, &HealthCheckedConnection::SendNextHeartbeat));
}

/**
 * Reset the RX timer
 */
void HealthCheckedConnection::HeartbeatReceived() {
  m_scheduler->RemoveTimeout(m_receive_timeout_id);
  UpdateReceiveTimer();
}

/**
 * Pause the receive timer
 */
void HealthCheckedConnection::PauseTimer() {
  if (m_receive_timeout_id != ola::thread::INVALID_TIMEOUT) {
    m_scheduler->RemoveTimeout(m_receive_timeout_id);
    m_receive_timeout_id = ola::thread::INVALID_TIMEOUT;
  }
}

/**
 * Resume the receive timer
 */
void HealthCheckedConnection::ResumeTimer() {
  if (m_receive_timeout_id == ola::thread::INVALID_TIMEOUT)
    UpdateReceiveTimer();
}

bool HealthCheckedConnection::SendNextHeartbeat() {
  SendHeartbeat();
  return true;
}

void HealthCheckedConnection::UpdateReceiveTimer() {
  TimeInterval timeout_interval(static_cast<int>(
        2.5 * m_heartbeat_interval.AsInt()));
  m_receive_timeout_id = m_scheduler->RegisterSingleTimeout(
    timeout_interval,
    NewSingleCallback(this, &HealthCheckedConnection::HeartbeatTimeout));
}
}  // network
}  // ola
<|endoftext|>