<commit_before>/*------------------------------------------------------------------------
* (The MIT License)
*
* Copyright (c) 2008-2011 Rhomobile, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* http://rhomobile.com
*------------------------------------------------------------------------*/

#include "RhoProfiler.h"
#include "logging/RhoLog.h"
#include "common/RhoTime.h"
#include <map>

namespace rho {
class CProfiler
{
    DEFINE_LOGCLASS;

    class CCounter
    {
        common::CTimeInterval m_startTime;
        bool m_bWasStarted;
    public:
        CCounter(bool bStart=true) : m_bWasStarted(false){
            if ( bStart )
                start();
        }

        virtual bool isGlobal()const{ return false;}

        virtual void start(){ m_startTime = common::CTimeInterval::getCurrentTime(); m_bWasStarted = true; }
        virtual common::CTimeInterval stop(){
            if ( m_startTime.isEmpty() )
                return m_startTime;

            common::CTimeInterval res = common::CTimeInterval::getCurrentTime()-m_startTime;
            m_startTime = common::CTimeInterval();
            return res;
        }
        virtual common::CTimeInterval flush(){ return stop(); }

        bool isWasStarted()const{ return m_bWasStarted;}
    };

    class CGlobalCounter : public CCounter
    {
        common::CTimeInterval m_sumGlobal;
    public:
        CGlobalCounter() : CCounter(false){ }
        virtual bool isGlobal()const{ return true;}

        virtual common::CTimeInterval stop(){
            m_sumGlobal += CCounter::stop();

            return m_sumGlobal;
        }
        virtual common::CTimeInterval flush(){
            common::CTimeInterval res = stop();
            m_sumGlobal = common::CTimeInterval();
            return res;
        }

    };

    std::map<String,CCounter*> m_mapCounters;
public:
    CProfiler(){}
    ~CProfiler(){}

    void startCounter(const char* szCounterName);
    void startCreatedCounter(const char* szCounterName);
    void stopCounter(const char* szCounterName, bool bDestroy =false);
    void createCounter(const char* szCounterName);
    void destroyCounter(const char* szCounterName);
    void flushCounter(const char* szCounterName, const char* msg);

};
IMPLEMENT_LOGCLASS(CProfiler,"PROFILER");

void CProfiler::startCounter(const char* szCounterName)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter || !pCounter->isWasStarted() )
        LOG(INFO) + szCounterName + " : START";

    if ( !pCounter )
        m_mapCounters[szCounterName] = new CCounter();
    else
        pCounter->start();
}

void CProfiler::startCreatedCounter(const char* szCounterName)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter )
        return;

    if ( !pCounter->isWasStarted() )
        LOG(INFO) + szCounterName + " : START";

    pCounter->start();
}

void CProfiler::stopCounter(const char* szCounterName, bool bDestroy /*=false*/)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter ){
        //LOG(ERROR) + szCounterName + " : Cannot find counter.";
        return;
    }

    if ( bDestroy || !pCounter->isGlobal() )
    {
        common::CTimeInterval oInterval = pCounter->stop();
        LOG(INFO) + szCounterName + " (" + oInterval.toString() + ") : STOP";

        m_mapCounters.erase(szCounterName);
    }else
        pCounter->stop();

}

void CProfiler::flushCounter(const char* szCounterName, const char* msg)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter ){
        LOG(ERROR) + szCounterName + " : Cannot find counter.";
        return;
    }

    common::CTimeInterval oInterval = pCounter->flush();
    LOG(INFO) + szCounterName + (msg && *msg ? " - " : "" ) + (msg && *msg ? msg : "" ) +
        " (" + oInterval.toString() + ") : STOP";
}

void CProfiler::createCounter(const char* szCounterName)
{
    m_mapCounters[szCounterName] = new CGlobalCounter();
}

void CProfiler::destroyCounter(const char* szCounterName)
{
    stopCounter( szCounterName, true );
}

}

static rho::CProfiler g_oProfiler;
extern "C"{

void rhoStartProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.startCounter(szCounterName);
}

void rhoStartProfilerCreatedCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.startCreatedCounter(szCounterName);
}

void rhoStopProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.stopCounter(szCounterName);
}

void rhoCreateProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.createCounter(szCounterName);
}

void rhoDestroyProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.destroyCounter(szCounterName);
}

void rhoFlushProfilerCounter(const char* file, int line, const char* szCounterName, const char* msg )
{
    g_oProfiler.flushCounter(szCounterName, msg );
}

}
<commit_msg>profiler: flsuh counter should not stop it<commit_after>/*------------------------------------------------------------------------
* (The MIT License)
*
* Copyright (c) 2008-2011 Rhomobile, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* http://rhomobile.com
*------------------------------------------------------------------------*/

#include "RhoProfiler.h"
#include "logging/RhoLog.h"
#include "common/RhoTime.h"
#include <map>

namespace rho {
class CProfiler
{
    DEFINE_LOGCLASS;

    class CCounter
    {
        common::CTimeInterval m_startTime;
        bool m_bWasStarted;
    public:
        CCounter(bool bStart=true) : m_bWasStarted(false){
            if ( bStart )
                start();
        }

        virtual bool isGlobal()const{ return false;}

        virtual void start(){ m_startTime = common::CTimeInterval::getCurrentTime(); m_bWasStarted = true; }
        virtual common::CTimeInterval stop()
        {
            if ( m_startTime.isEmpty() )
                return m_startTime;

            common::CTimeInterval res = common::CTimeInterval::getCurrentTime()-m_startTime;
            m_startTime = common::CTimeInterval();
            return res;
        }
        virtual common::CTimeInterval flush()
        {
            if ( m_startTime.isEmpty() )
                return m_startTime;

            common::CTimeInterval res = common::CTimeInterval::getCurrentTime()-m_startTime;
            m_startTime = common::CTimeInterval::getCurrentTime();
            return res;
        }

        bool isWasStarted()const{ return m_bWasStarted;}
    };

    class CGlobalCounter : public CCounter
    {
        common::CTimeInterval m_sumGlobal;
    public:
        CGlobalCounter() : CCounter(false){ }
        virtual bool isGlobal()const{ return true;}

        virtual common::CTimeInterval stop()
        {
            m_sumGlobal += CCounter::stop();

            return m_sumGlobal;
        }
        virtual common::CTimeInterval flush()
        {
            m_sumGlobal += CCounter::flush();
            common::CTimeInterval res = m_sumGlobal;
            m_sumGlobal = common::CTimeInterval();
            return res;
        }

    };

    std::map<String,CCounter*> m_mapCounters;
public:
    CProfiler(){}
    ~CProfiler(){}

    void startCounter(const char* szCounterName);
    void startCreatedCounter(const char* szCounterName);
    void stopCounter(const char* szCounterName, bool bDestroy =false);
    void createCounter(const char* szCounterName);
    void destroyCounter(const char* szCounterName);
    void flushCounter(const char* szCounterName, const char* msg);

};
IMPLEMENT_LOGCLASS(CProfiler,"PROFILER");

void CProfiler::startCounter(const char* szCounterName)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter || !pCounter->isWasStarted() )
        LOG(INFO) + szCounterName + " : START";

    if ( !pCounter )
        m_mapCounters[szCounterName] = new CCounter();
    else
        pCounter->start();
}

void CProfiler::startCreatedCounter(const char* szCounterName)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter )
        return;

    if ( !pCounter->isWasStarted() )
        LOG(INFO) + szCounterName + " : START";

    pCounter->start();
}

void CProfiler::stopCounter(const char* szCounterName, bool bDestroy /*=false*/)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter ){
        //LOG(ERROR) + szCounterName + " : Cannot find counter.";
        return;
    }

    if ( bDestroy || !pCounter->isGlobal() )
    {
        common::CTimeInterval oInterval = pCounter->stop();
        LOG(INFO) + szCounterName + " (" + oInterval.toString() + ") : STOP";

        m_mapCounters.erase(szCounterName);
    }else
        pCounter->stop();

}

void CProfiler::flushCounter(const char* szCounterName, const char* msg)
{
    CCounter* pCounter = m_mapCounters[szCounterName];
    if ( !pCounter ){
        LOG(ERROR) + szCounterName + " : Cannot find counter.";
        return;
    }

    common::CTimeInterval oInterval = pCounter->flush();
    LOG(INFO) + szCounterName + (msg && *msg ? " - " : "" ) + (msg && *msg ? msg : "" ) +
        " (" + oInterval.toString() + ") : STOP";
}

void CProfiler::createCounter(const char* szCounterName)
{
    m_mapCounters[szCounterName] = new CGlobalCounter();
}

void CProfiler::destroyCounter(const char* szCounterName)
{
    stopCounter( szCounterName, true );
}

}

static rho::CProfiler g_oProfiler;
extern "C"{

void rhoStartProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.startCounter(szCounterName);
}

void rhoStartProfilerCreatedCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.startCreatedCounter(szCounterName);
}

void rhoStopProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.stopCounter(szCounterName);
}

void rhoCreateProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.createCounter(szCounterName);
}

void rhoDestroyProfilerCounter(const char* file, int line, const char* szCounterName )
{
    g_oProfiler.destroyCounter(szCounterName);
}

void rhoFlushProfilerCounter(const char* file, int line, const char* szCounterName, const char* msg )
{
    g_oProfiler.flushCounter(szCounterName, msg );
}

}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Nathan Osman
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <QDateTime>
#include <QHostAddress>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAddressEntry>
#include <QNetworkInterface>
#include <QSet>

#include <nitroshare/application.h>
#include <nitroshare/category.h>
#include <nitroshare/logger.h>
#include <nitroshare/message.h>
#include <nitroshare/settingsregistry.h>

#include "broadcastdevice.h"
#include "broadcastenumerator.h"

const QString MessageTag = "broadcast";

const QString BroadcastCategory = "broadcast";
const QString BroadcastInterval = "BroadcastInterval";
const QString BroadcastExpiry = "BroadcastExpiry";
const QString BroadcastPort = "BroadcastPort";

const QString TransferPort = "TransferPort";

BroadcastEnumerator::BroadcastEnumerator(Application *application)
    : mApplication(application),
      mBroadcastCategory({
          { Category::NameKey, BroadcastCategory },
          { Category::TitleKey, tr("Broadcast") },
      }),
      mBroadcastInterval({
          { Setting::TypeKey, Setting::Integer },
          { Setting::NameKey, BroadcastInterval },
          { Setting::TitleKey, tr("Broadcast Interval") },
          { Setting::CategoryKey, BroadcastCategory },
          { Setting::DefaultValueKey, 5000 }
      }),
      mBroadcastExpiry({
          { Setting::TypeKey, Setting::Integer },
          { Setting::NameKey, BroadcastExpiry },
          { Setting::TitleKey, tr("Broadcast Expiry") },
          { Setting::CategoryKey, BroadcastCategory },
          { Setting::DefaultValueKey, 30000 }
      }),
      mBroadcastPort({
          { Setting::TypeKey, Setting::Integer },
          { Setting::NameKey, BroadcastPort },
          { Setting::TitleKey, tr("Broadcast Port") },
          { Setting::CategoryKey, BroadcastCategory },
          { Setting::DefaultValueKey, 40816 }
      })
{
    connect(&mBroadcastTimer, &QTimer::timeout, this, &BroadcastEnumerator::onBroadcastTimeout);
    connect(&mExpiryTimer, &QTimer::timeout, this, &BroadcastEnumerator::onExpiryTimeout);
    connect(&mSocket, &QUdpSocket::readyRead, this, &BroadcastEnumerator::onReadyRead);
    connect(mApplication->settingsRegistry(), &SettingsRegistry::settingsChanged, this, &BroadcastEnumerator::onSettingsChanged);

    mApplication->settingsRegistry()->addCategory(&mBroadcastCategory);
    mApplication->settingsRegistry()->addSetting(&mBroadcastInterval);
    mApplication->settingsRegistry()->addSetting(&mBroadcastExpiry);
    mApplication->settingsRegistry()->addSetting(&mBroadcastPort);

    // Trigger loading the initial settings
    onSettingsChanged({ BroadcastInterval, BroadcastExpiry, BroadcastPort });
}

BroadcastEnumerator::~BroadcastEnumerator()
{
    mApplication->settingsRegistry()->removeSetting(&mBroadcastInterval);
    mApplication->settingsRegistry()->removeSetting(&mBroadcastExpiry);
    mApplication->settingsRegistry()->removeSetting(&mBroadcastPort);
    mApplication->settingsRegistry()->removeCategory(&mBroadcastCategory);

    qDeleteAll(mDevices);
}

QString BroadcastEnumerator::name() const
{
    return "broadcast";
}

void BroadcastEnumerator::onBroadcastTimeout()
{
    // Build a list of all IPv4 broadcast addresses
    QSet<QHostAddress> addresses;
    foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) {
        if (interface.flags() & QNetworkInterface::CanBroadcast) {
            foreach (QNetworkAddressEntry entry, interface.addressEntries()) {
                if (!entry.broadcast().isNull()) {
                    addresses.insert(entry.broadcast());
                }
            }
        }
    }

    // Build the packet that will be broadcast
    QJsonObject object{
        { "uuid", mApplication->deviceUuid() },
        { "name", mApplication->deviceName() },
        { "port", mApplication->settingsRegistry()->value(TransferPort).toInt() }
    };
    QByteArray data = QJsonDocument(object).toJson(QJsonDocument::Compact);

    // Broadcast the packet
    foreach (QHostAddress address, addresses) {
        mSocket.writeDatagram(data, address, mSocket.localPort());
    }
}

void BroadcastEnumerator::onExpiryTimeout()
{
    qint64 curMs = QDateTime::currentMSecsSinceEpoch();
    int timeoutMs = mApplication->settingsRegistry()->value(BroadcastExpiry).toInt();

    // Remove any devices that have expired
    for (auto i = mDevices.begin(); i != mDevices.end();) {
        if ((*i)->isExpired(curMs, timeoutMs)) {
            mDevices.erase(i);
            emit deviceRemoved(*i);
            delete *i;
        } else {
            ++i;
        }
    }
}

void BroadcastEnumerator::onReadyRead()
{
    qint64 curMs = QDateTime::currentMSecsSinceEpoch();

    // Read all of the packets
    while (mSocket.hasPendingDatagrams()) {

        // Capture both the data and the address
        QByteArray data;
        QHostAddress address;

        // Receive the packet
        data.resize(mSocket.pendingDatagramSize());
        mSocket.readDatagram(data.data(), data.size(), &address);
        QJsonObject object = QJsonDocument::fromJson(data).object();

        // Ensure the packet includes UUID
        QString uuid = object.value("uuid").toString();
        if (uuid.isNull()) {
            continue;
        }

        // Attempt to find an existing device that matches
        BroadcastDevice *device = nullptr;
        for (auto i = mDevices.constBegin(); i != mDevices.constEnd(); ++i) {
            if ((*i)->uuid() == uuid) {
                device = *i;
            }
        }

        // If no existing device was found, emit a new device
        bool deviceExisted = static_cast<bool>(device);
        if (!deviceExisted) {
            device = new BroadcastDevice;
        }

        // Update the device
        device->update(curMs, address, object);

        // Indicate that a new device was discovered
        if (!deviceExisted) {
            mDevices.append(device);
            emit deviceAdded(device);
        }
    }
}

void BroadcastEnumerator::onSettingsChanged(const QStringList &keys)
{
    if (keys.contains(BroadcastInterval)) {
        mBroadcastTimer.stop();
        mBroadcastTimer.setInterval(
            mApplication->settingsRegistry()->value(BroadcastInterval).toInt()
        );
        onBroadcastTimeout();
        mBroadcastTimer.start();
    }

    if (keys.contains(BroadcastExpiry)) {
        mExpiryTimer.stop();
        mExpiryTimer.setInterval(
            mApplication->settingsRegistry()->value(BroadcastExpiry).toInt()
        );
        onExpiryTimeout();
        mExpiryTimer.start();
    }

    if (keys.contains(BroadcastPort)) {
        mSocket.close();
        if (!mSocket.bind(QHostAddress::AnyIPv4,
                mApplication->settingsRegistry()->value(BroadcastPort).toInt())) {
            mApplication->logger()->log(new Message(
                Message::Error,
                MessageTag,
                mSocket.errorString()
            ));
        }
    }
}
<commit_msg>Fix crash caused by out-of-order statements in broadcast plugin.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Nathan Osman
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <QDateTime>
#include <QHostAddress>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAddressEntry>
#include <QNetworkInterface>
#include <QSet>

#include <nitroshare/application.h>
#include <nitroshare/category.h>
#include <nitroshare/logger.h>
#include <nitroshare/message.h>
#include <nitroshare/settingsregistry.h>

#include "broadcastdevice.h"
#include "broadcastenumerator.h"

const QString MessageTag = "broadcast";

const QString BroadcastCategory = "broadcast";
const QString BroadcastInterval = "BroadcastInterval";
const QString BroadcastExpiry = "BroadcastExpiry";
const QString BroadcastPort = "BroadcastPort";

const QString TransferPort = "TransferPort";

BroadcastEnumerator::BroadcastEnumerator(Application *application)
    : mApplication(application),
      mBroadcastCategory({
          { Category::NameKey, BroadcastCategory },
          { Category::TitleKey, tr("Broadcast") },
      }),
      mBroadcastInterval({
          { Setting::TypeKey, Setting::Integer },
          { Setting::NameKey, BroadcastInterval },
          { Setting::TitleKey, tr("Broadcast Interval") },
          { Setting::CategoryKey, BroadcastCategory },
          { Setting::DefaultValueKey, 5000 }
      }),
      mBroadcastExpiry({
          { Setting::TypeKey, Setting::Integer },
          { Setting::NameKey, BroadcastExpiry },
          { Setting::TitleKey, tr("Broadcast Expiry") },
          { Setting::CategoryKey, BroadcastCategory },
          { Setting::DefaultValueKey, 30000 }
      }),
      mBroadcastPort({
          { Setting::TypeKey, Setting::Integer },
          { Setting::NameKey, BroadcastPort },
          { Setting::TitleKey, tr("Broadcast Port") },
          { Setting::CategoryKey, BroadcastCategory },
          { Setting::DefaultValueKey, 40816 }
      })
{
    connect(&mBroadcastTimer, &QTimer::timeout, this, &BroadcastEnumerator::onBroadcastTimeout);
    connect(&mExpiryTimer, &QTimer::timeout, this, &BroadcastEnumerator::onExpiryTimeout);
    connect(&mSocket, &QUdpSocket::readyRead, this, &BroadcastEnumerator::onReadyRead);
    connect(mApplication->settingsRegistry(), &SettingsRegistry::settingsChanged, this, &BroadcastEnumerator::onSettingsChanged);

    mApplication->settingsRegistry()->addCategory(&mBroadcastCategory);
    mApplication->settingsRegistry()->addSetting(&mBroadcastInterval);
    mApplication->settingsRegistry()->addSetting(&mBroadcastExpiry);
    mApplication->settingsRegistry()->addSetting(&mBroadcastPort);

    // Trigger loading the initial settings
    onSettingsChanged({ BroadcastInterval, BroadcastExpiry, BroadcastPort });
}

BroadcastEnumerator::~BroadcastEnumerator()
{
    mApplication->settingsRegistry()->removeSetting(&mBroadcastInterval);
    mApplication->settingsRegistry()->removeSetting(&mBroadcastExpiry);
    mApplication->settingsRegistry()->removeSetting(&mBroadcastPort);
    mApplication->settingsRegistry()->removeCategory(&mBroadcastCategory);

    qDeleteAll(mDevices);
}

QString BroadcastEnumerator::name() const
{
    return "broadcast";
}

void BroadcastEnumerator::onBroadcastTimeout()
{
    // Build a list of all IPv4 broadcast addresses
    QSet<QHostAddress> addresses;
    foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) {
        if (interface.flags() & QNetworkInterface::CanBroadcast) {
            foreach (QNetworkAddressEntry entry, interface.addressEntries()) {
                if (!entry.broadcast().isNull()) {
                    addresses.insert(entry.broadcast());
                }
            }
        }
    }

    // Build the packet that will be broadcast
    QJsonObject object{
        { "uuid", mApplication->deviceUuid() },
        { "name", mApplication->deviceName() },
        { "port", mApplication->settingsRegistry()->value(TransferPort).toInt() }
    };
    QByteArray data = QJsonDocument(object).toJson(QJsonDocument::Compact);

    // Broadcast the packet
    foreach (QHostAddress address, addresses) {
        mSocket.writeDatagram(data, address, mSocket.localPort());
    }
}

void BroadcastEnumerator::onExpiryTimeout()
{
    qint64 curMs = QDateTime::currentMSecsSinceEpoch();
    int timeoutMs = mApplication->settingsRegistry()->value(BroadcastExpiry).toInt();

    // Remove any devices that have expired
    for (auto i = mDevices.begin(); i != mDevices.end();) {
        if ((*i)->isExpired(curMs, timeoutMs)) {
            emit deviceRemoved(*i);
            mDevices.erase(i);
            delete *i;
        } else {
            ++i;
        }
    }
}

void BroadcastEnumerator::onReadyRead()
{
    qint64 curMs = QDateTime::currentMSecsSinceEpoch();

    // Read all of the packets
    while (mSocket.hasPendingDatagrams()) {

        // Capture both the data and the address
        QByteArray data;
        QHostAddress address;

        // Receive the packet
        data.resize(mSocket.pendingDatagramSize());
        mSocket.readDatagram(data.data(), data.size(), &address);
        QJsonObject object = QJsonDocument::fromJson(data).object();

        // Ensure the packet includes UUID
        QString uuid = object.value("uuid").toString();
        if (uuid.isNull()) {
            continue;
        }

        // Attempt to find an existing device that matches
        BroadcastDevice *device = nullptr;
        for (auto i = mDevices.constBegin(); i != mDevices.constEnd(); ++i) {
            if ((*i)->uuid() == uuid) {
                device = *i;
            }
        }

        // If no existing device was found, emit a new device
        bool deviceExisted = static_cast<bool>(device);
        if (!deviceExisted) {
            device = new BroadcastDevice;
        }

        // Update the device
        device->update(curMs, address, object);

        // Indicate that a new device was discovered
        if (!deviceExisted) {
            mDevices.append(device);
            emit deviceAdded(device);
        }
    }
}

void BroadcastEnumerator::onSettingsChanged(const QStringList &keys)
{
    if (keys.contains(BroadcastInterval)) {
        mBroadcastTimer.stop();
        mBroadcastTimer.setInterval(
            mApplication->settingsRegistry()->value(BroadcastInterval).toInt()
        );
        onBroadcastTimeout();
        mBroadcastTimer.start();
    }

    if (keys.contains(BroadcastExpiry)) {
        mExpiryTimer.stop();
        mExpiryTimer.setInterval(
            mApplication->settingsRegistry()->value(BroadcastExpiry).toInt()
        );
        onExpiryTimeout();
        mExpiryTimer.start();
    }

    if (keys.contains(BroadcastPort)) {
        mSocket.close();
        if (!mSocket.bind(QHostAddress::AnyIPv4,
                mApplication->settingsRegistry()->value(BroadcastPort).toInt())) {
            mApplication->logger()->log(new Message(
                Message::Error,
                MessageTag,
                mSocket.errorString()
            ));
        }
    }
}
<|endoftext|>


<commit_before>/** @file

  A brief file description

  @section license License

  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.
 */

/* tcp_info.cc:  logs the tcp_info data struture to a file
 */

#include <stdio.h>
#include <stdlib.h>
#include <ts/ts.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/time.h>
#include <arpa/inet.h>

struct Config {
  int sample;
  const char* log_file;
  int log_fd;
  int log_level;
  int hook;
};
static Config config;

static void
load_config() {
  char config_file[PATH_MAX];
  config.sample = 1000;
  config.log_level = 1;
  config.log_file = NULL;
  config.hook = 1;

  // get the install directory
  const char* install_dir = TSInstallDirGet();

  // figure out the config file and open it
  snprintf(config_file, sizeof(config_file), "%s/%s/%s", install_dir, "etc", "tcp_info.config");
  FILE *file = fopen(config_file, "r");
  if (file == NULL) {
    snprintf(config_file, sizeof(config_file), "%s/%s/%s", install_dir, "conf", "tcp_info.config");
    file = fopen(config_file, "r");
  }
  TSDebug("tcp_info", "config file name: %s", config_file);
  assert(file != NULL);

  // read and parse the lines
  char line[256];
  while (fgets(line, sizeof(line), file) != NULL) {
    char *pos = strchr(line, '=');
    *pos = '\0';
    char *value = pos + 1;

    // remove the new line
    pos = strchr(value, '\n');
    if (pos != NULL) {
      *pos = '\0';
    }

    if (value != NULL) {
      TSDebug("tcp_info", "config key: %s", line);
      TSDebug("tcp_info", "config value: %s", value);
      if (strcmp(line, "sample") == 0) {
        config.sample = atoi(value);
      } else if (strcmp(line, "log_file") == 0) {
        config.log_file = strdup(value);
      } else if (strcmp(line, "log_level") == 0) {
        config.log_level = atoi(value);
      } else if (strcmp(line, "hook") == 0) {
        config.hook = atoi(value);
      }
    }
  }

  TSDebug("tcp_info", "sample: %d", config.sample);
  TSDebug("tcp_info", "log filename: %s", config.log_file);
  TSDebug("tcp_info", "log_level: %d", config.log_level);
  TSDebug("tcp_info", "hook: %d", config.hook);

  config.log_fd = open(config.log_file, O_APPEND | O_CREAT | O_RDWR, 0666);
  assert(config.log_fd > 0);
}

static void
log_tcp_info(const char* event_name, const char* client_ip, const char* server_ip, struct tcp_info &info) {
  char buffer[256];

  // get the current time
  struct timeval now;
  gettimeofday(&now, NULL);

  int bytes = 0;
  if (config.log_level == 2) {
#if !defined(freebsd) || defined(__GLIBC__)
    bytes = snprintf(buffer, sizeof(buffer), "%s %u %u %s %s %u %u %u %u %u %u %u %u %u %u %u %u\n",
                     event_name,
                     (uint32_t)now.tv_sec,
                     (uint32_t)now.tv_usec,
                     client_ip,
                     server_ip,
                     info.tcpi_last_data_sent,
                     info.tcpi_last_data_recv,
                     info.tcpi_snd_cwnd,
                     info.tcpi_snd_ssthresh,
                     info.tcpi_rcv_ssthresh,
                     info.tcpi_rtt,
                     info.tcpi_rttvar,
                     info.tcpi_unacked,
                     info.tcpi_sacked,
                     info.tcpi_lost,
                     info.tcpi_retrans,
                     info.tcpi_fackets
      );
#else
        bytes = snprintf(buffer, sizeof(buffer), "%s %u %u %s %s %u %u %u %u %u %u %u %u %u %u %u %u\n",
                     event_name,
                     (uint32_t)now.tv_sec,
                     (uint32_t)now.tv_usec,
                     client_ip,
                     server_ip,
                     info.__tcpi_last_data_sent,
                     info.tcpi_last_data_recv,
                     info.tcpi_snd_cwnd,
                     info.tcpi_snd_ssthresh,
                     info.__tcpi_rcv_ssthresh,
                     info.tcpi_rtt,
                     info.tcpi_rttvar,
                     info.__tcpi_unacked,
                     info.__tcpi_sacked,
                     info.__tcpi_lost,
                     info.__tcpi_retrans,
                     info.__tcpi_fackets
      );
#endif
  } else {
    bytes = snprintf(buffer, sizeof(buffer), "%s %u %s %s %u\n",
                     event_name,
                     (uint32_t)now.tv_sec,
                     client_ip,
                     server_ip,
                     info.tcpi_rtt
    );
  }

  ssize_t wrote = write(config.log_fd, buffer, bytes);
  assert(wrote == bytes);
  TSDebug("tcp_info", "wrote: %d bytes to file: %s", bytes, config.log_file);
  TSDebug("tcp_info", "logging: %s", buffer);
}

static int
tcp_info_hook(TSCont /* contp ATS_UNUSED */, TSEvent event, void *edata)
{
  TSHttpSsn ssnp = NULL;
  TSHttpTxn txnp = NULL;

  const char *event_name;
  switch (event) {
  case TS_EVENT_HTTP_SSN_START:
    ssnp = (TSHttpSsn)edata;
    event_name = "ssn_start";
    break;
  case TS_EVENT_HTTP_TXN_START:
    txnp = (TSHttpTxn)edata;
    ssnp = TSHttpTxnSsnGet(txnp);
    event_name = "txn_start";
    break;
  case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
    txnp = (TSHttpTxn)edata;
    ssnp = TSHttpTxnSsnGet(txnp);
    event_name = "send_resp_hdr";
    break;
  case TS_EVENT_HTTP_SSN_CLOSE:
    ssnp = (TSHttpSsn)edata;
    event_name = "ssn_close";
  default:
    return 0;
  }

  TSDebug("tcp_info", "tcp_info_hook called, event: %s", event_name);

  struct tcp_info tcp_info;
  int tcp_info_len = sizeof(tcp_info);
  int fd;

  if (TSHttpSsnClientFdGet(ssnp, &fd) != TS_SUCCESS) {
    TSDebug("tcp_info", "error getting the client socket fd");
    goto done;
  }

  // get the tcp info structure
  if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_len) == 0) {
    // the structure is the correct size
    if (tcp_info_len == sizeof(tcp_info)) {
      // no need to run rand if we are always going log (100%)
      int random = 0;
      if (config.sample < 1000) {
        random = rand() % 1000;
        TSDebug("tcp_info", "random: %d, config.sample: %d", random, config.sample);
      }

      if (random < config.sample) {
        TSDebug("tcp_info", "got the tcp_info struture and now logging");

        // get the client address
        const struct sockaddr *client_addr = TSHttpSsnClientAddrGet(ssnp);
        const struct sockaddr *server_addr = TSHttpSsnIncomingAddrGet(ssnp);
        if (client_addr == NULL || server_addr == NULL)
          goto done;
        struct sockaddr_in *client_in_addr = (struct sockaddr_in *)client_addr;
        struct sockaddr_in *server_in_addr = (struct sockaddr_in *)server_addr;
        char client_str[INET_ADDRSTRLEN];
        char server_str[INET_ADDRSTRLEN];

        // convert ip to string
        inet_ntop(client_addr->sa_family, &(client_in_addr->sin_addr), client_str, INET_ADDRSTRLEN);
        inet_ntop(server_addr->sa_family, &(server_in_addr->sin_addr), server_str, INET_ADDRSTRLEN);

        log_tcp_info(event_name, client_str, server_str, tcp_info);
      }
    } else {
      TSDebug("tcp_info", "tcp_info length is the wrong size");
    }
  } else {
    TSDebug("tcp_info", "error calling getsockopt()");
  }

done:
  if (txnp != NULL) {
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  } else if (ssnp != NULL) {
    TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
  }
  return 0;
}

void
TSPluginInit(int, const char *[]) // int argc, const char *argv[]
{
  TSPluginRegistrationInfo info;

  info.plugin_name = (char*)"tcp_info";
  info.vendor_name = (char*)"Apache Software Foundation";
  info.support_email = (char*)"dev@trafficserver.apache.org";

  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS)
    TSError("Plugin registration failed. \n");

  // load the configuration file
  load_config();

  // add a hook to the state machine
  // TODO: need another hook before the socket is closed, keeping it in for now because it will be easier to change if or when another hook is added to ATS
  if ((config.hook & 1) != 0) {
    TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the start of the TCP connection");
  }
  if ((config.hook & 2) != 0) {
    TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the close of the transaction");
  }
  if ((config.hook & 4) != 0) {
    TSHttpHookAdd(TS_HTTP_SEND_RESPONSE_HDR_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the sending of the headers");
  }
  if ((config.hook & 8) != 0) {
    TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the close of the TCP connection");
  }

  TSDebug("tcp_info", "tcp info module registered");
}
<commit_msg>tcp_info: remove whitespace<commit_after>/** @file

  A brief file description

  @section license License

  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.
 */

/* tcp_info.cc:  logs the tcp_info data struture to a file
 */

#include <stdio.h>
#include <stdlib.h>
#include <ts/ts.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/time.h>
#include <arpa/inet.h>

struct Config {
  int sample;
  const char* log_file;
  int log_fd;
  int log_level;
  int hook;
};
static Config config;

static void
load_config() {
  char config_file[PATH_MAX];
  config.sample = 1000;
  config.log_level = 1;
  config.log_file = NULL;
  config.hook = 1;

  // get the install directory
  const char* install_dir = TSInstallDirGet();

  // figure out the config file and open it
  snprintf(config_file, sizeof(config_file), "%s/%s/%s", install_dir, "etc", "tcp_info.config");
  FILE *file = fopen(config_file, "r");
  if (file == NULL) {
    snprintf(config_file, sizeof(config_file), "%s/%s/%s", install_dir, "conf", "tcp_info.config");
    file = fopen(config_file, "r");
  }
  TSDebug("tcp_info", "config file name: %s", config_file);
  assert(file != NULL);

  // read and parse the lines
  char line[256];
  while (fgets(line, sizeof(line), file) != NULL) {
    char *pos = strchr(line, '=');
    *pos = '\0';
    char *value = pos + 1;

    // remove the new line
    pos = strchr(value, '\n');
    if (pos != NULL) {
      *pos = '\0';
    }

    if (value != NULL) {
      TSDebug("tcp_info", "config key: %s", line);
      TSDebug("tcp_info", "config value: %s", value);
      if (strcmp(line, "sample") == 0) {
        config.sample = atoi(value);
      } else if (strcmp(line, "log_file") == 0) {
        config.log_file = strdup(value);
      } else if (strcmp(line, "log_level") == 0) {
        config.log_level = atoi(value);
      } else if (strcmp(line, "hook") == 0) {
        config.hook = atoi(value);
      }
    }
  }

  TSDebug("tcp_info", "sample: %d", config.sample);
  TSDebug("tcp_info", "log filename: %s", config.log_file);
  TSDebug("tcp_info", "log_level: %d", config.log_level);
  TSDebug("tcp_info", "hook: %d", config.hook);

  config.log_fd = open(config.log_file, O_APPEND | O_CREAT | O_RDWR, 0666);
  assert(config.log_fd > 0);
}

static void
log_tcp_info(const char* event_name, const char* client_ip, const char* server_ip, struct tcp_info &info) {
  char buffer[256];

  // get the current time
  struct timeval now;
  gettimeofday(&now, NULL);

  int bytes = 0;
  if (config.log_level == 2) {
#if !defined(freebsd) || defined(__GLIBC__)
    bytes = snprintf(buffer, sizeof(buffer), "%s %u %u %s %s %u %u %u %u %u %u %u %u %u %u %u %u\n",
                     event_name,
                     (uint32_t)now.tv_sec,
                     (uint32_t)now.tv_usec,
                     client_ip,
                     server_ip,
                     info.tcpi_last_data_sent,
                     info.tcpi_last_data_recv,
                     info.tcpi_snd_cwnd,
                     info.tcpi_snd_ssthresh,
                     info.tcpi_rcv_ssthresh,
                     info.tcpi_rtt,
                     info.tcpi_rttvar,
                     info.tcpi_unacked,
                     info.tcpi_sacked,
                     info.tcpi_lost,
                     info.tcpi_retrans,
                     info.tcpi_fackets
      );
#else
        bytes = snprintf(buffer, sizeof(buffer), "%s %u %u %s %s %u %u %u %u %u %u %u %u %u %u %u %u\n",
                     event_name,
                     (uint32_t)now.tv_sec,
                     (uint32_t)now.tv_usec,
                     client_ip,
                     server_ip,
                     info.__tcpi_last_data_sent,
                     info.tcpi_last_data_recv,
                     info.tcpi_snd_cwnd,
                     info.tcpi_snd_ssthresh,
                     info.__tcpi_rcv_ssthresh,
                     info.tcpi_rtt,
                     info.tcpi_rttvar,
                     info.__tcpi_unacked,
                     info.__tcpi_sacked,
                     info.__tcpi_lost,
                     info.__tcpi_retrans,
                     info.__tcpi_fackets
      );
#endif
  } else {
    bytes = snprintf(buffer, sizeof(buffer), "%s %u %s %s %u\n",
                     event_name,
                     (uint32_t)now.tv_sec,
                     client_ip,
                     server_ip,
                     info.tcpi_rtt
    );
  }

  ssize_t wrote = write(config.log_fd, buffer, bytes);
  assert(wrote == bytes);
  TSDebug("tcp_info", "wrote: %d bytes to file: %s", bytes, config.log_file);
  TSDebug("tcp_info", "logging: %s", buffer);
}

static int
tcp_info_hook(TSCont /* contp ATS_UNUSED */, TSEvent event, void *edata)
{
  TSHttpSsn ssnp = NULL;
  TSHttpTxn txnp = NULL;

  const char *event_name;
  switch (event) {
  case TS_EVENT_HTTP_SSN_START:
    ssnp = (TSHttpSsn)edata;
    event_name = "ssn_start";
    break;
  case TS_EVENT_HTTP_TXN_START:
    txnp = (TSHttpTxn)edata;
    ssnp = TSHttpTxnSsnGet(txnp);
    event_name = "txn_start";
    break;
  case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
    txnp = (TSHttpTxn)edata;
    ssnp = TSHttpTxnSsnGet(txnp);
    event_name = "send_resp_hdr";
    break;
  case TS_EVENT_HTTP_SSN_CLOSE:
    ssnp = (TSHttpSsn)edata;
    event_name = "ssn_close";
  default:
    return 0;
  }

  TSDebug("tcp_info", "tcp_info_hook called, event: %s", event_name);

  struct tcp_info tcp_info;
  int tcp_info_len = sizeof(tcp_info);
  int fd;

  if (TSHttpSsnClientFdGet(ssnp, &fd) != TS_SUCCESS) {
    TSDebug("tcp_info", "error getting the client socket fd");
    goto done;
  }

  // get the tcp info structure
  if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_len) == 0) {
    // the structure is the correct size
    if (tcp_info_len == sizeof(tcp_info)) {
      // no need to run rand if we are always going log (100%)
      int random = 0;
      if (config.sample < 1000) {
        random = rand() % 1000;
        TSDebug("tcp_info", "random: %d, config.sample: %d", random, config.sample);
      }

      if (random < config.sample) {
        TSDebug("tcp_info", "got the tcp_info struture and now logging");

        // get the client address
        const struct sockaddr *client_addr = TSHttpSsnClientAddrGet(ssnp);
        const struct sockaddr *server_addr = TSHttpSsnIncomingAddrGet(ssnp);
        if (client_addr == NULL || server_addr == NULL)
          goto done;
        struct sockaddr_in *client_in_addr = (struct sockaddr_in *)client_addr;
        struct sockaddr_in *server_in_addr = (struct sockaddr_in *)server_addr;
        char client_str[INET_ADDRSTRLEN];
        char server_str[INET_ADDRSTRLEN];

        // convert ip to string
        inet_ntop(client_addr->sa_family, &(client_in_addr->sin_addr), client_str, INET_ADDRSTRLEN);
        inet_ntop(server_addr->sa_family, &(server_in_addr->sin_addr), server_str, INET_ADDRSTRLEN);

        log_tcp_info(event_name, client_str, server_str, tcp_info);
      }
    } else {
      TSDebug("tcp_info", "tcp_info length is the wrong size");
    }
  } else {
    TSDebug("tcp_info", "error calling getsockopt()");
  }

done:
  if (txnp != NULL) {
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  } else if (ssnp != NULL) {
    TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
  }
  return 0;
}

void
TSPluginInit(int, const char *[]) // int argc, const char *argv[]
{
  TSPluginRegistrationInfo info;

  info.plugin_name = (char*)"tcp_info";
  info.vendor_name = (char*)"Apache Software Foundation";
  info.support_email = (char*)"dev@trafficserver.apache.org";

  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS)
    TSError("Plugin registration failed. \n");

  // load the configuration file
  load_config();

  // add a hook to the state machine
  // TODO: need another hook before the socket is closed, keeping it in for now because it will be easier to change if or when another hook is added to ATS
  if ((config.hook & 1) != 0) {
    TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the start of the TCP connection");
  }
  if ((config.hook & 2) != 0) {
    TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the close of the transaction");
  }
  if ((config.hook & 4) != 0) {
    TSHttpHookAdd(TS_HTTP_SEND_RESPONSE_HDR_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the sending of the headers");
  }
  if ((config.hook & 8) != 0) {
    TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, TSContCreate(tcp_info_hook, NULL));
    TSDebug("tcp_info", "added hook to the close of the TCP connection");
  }

  TSDebug("tcp_info", "tcp info module registered");
}
<|endoftext|>


<commit_before>/*
 * A wrapper for hstock/stock that allows multiple users of one
 * stock_item.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "mstock.h"
#include "hstock.h"
#include "stock.h"
#include "hashmap.h"
#include "pool.h"
#include "lease.h"

#include <daemon/log.h>

#include <map>
#include <list>
#include <string>

#include <assert.h>

struct mstock {};

class MultiStock : public mstock {
    class Domain;
    typedef std::map<std::string, Domain> DomainMap;

    class Domain {
        class Item;
        typedef std::list<Item> ItemList;

        class Item {
            struct Lease : list_head {
                static const struct lease lease;

                const ItemList::iterator item;

                Lease(ItemList::iterator _item):item(_item) {}

                void Release(bool reuse) {
                    item->DeleteLease(this, reuse);
                }

                static void Release(bool reuse, void *ctx) {
                    ((Lease *)ctx)->Release(reuse);
                }
            };

            DomainMap::iterator domain;

            stock_item &item;

            unsigned n_leases;
            list_head leases;

            bool reuse;

        public:
            Item(DomainMap::iterator _domain, stock_item &_item)
                :domain(_domain), item(_item), n_leases(0), reuse(true) {
                list_init(&leases);
            }

            Item(const Item &) = delete;

            ~Item() {
                assert(n_leases == 0);
                assert(list_empty(&leases));

                domain->second.Put(domain->first.c_str(), item, reuse);
            }

            bool IsFull() const {
                // TODO: dynamic limit
                return n_leases >= 4;
            }

            bool CanUse() const {
                return reuse && !IsFull();
            }

        private:
            Lease &AddLease(ItemList::iterator i) {
                assert(&*i == this);

                ++n_leases;

                Lease *lease = new Lease(i);
                list_add(lease, &leases);
                return *lease;
            }

        public:
            void AddLease(ItemList::iterator i,
                          const stock_get_handler &handler, void *ctx,
                          struct lease_ref &lease_ref) {
                assert(&*i == this);

                Lease &lease = AddLease(i);
                lease_ref_set(&lease_ref, &Lease::lease, &lease);

                handler.ready(&item, ctx);
            }

            stock_item *AddLease(ItemList::iterator i,
                                 struct lease_ref &lease_ref) {
                assert(&*i == this);

                Lease &lease = AddLease(i);
                lease_ref_set(&lease_ref, &Lease::lease, &lease);
                return &item;
            }

            void DeleteLease(Lease *lease, bool _reuse) {
                reuse &= _reuse;

                auto ii = lease->item;

                assert(n_leases > 0);
                list_remove(lease);
                delete lease;

                --n_leases;

                if (n_leases == 0)
                    domain->second.DeleteItem(ii);
            }
        };

        MultiStock &stock;

        struct pool *pool;

        ItemList items;

    public:
        Domain(MultiStock &_stock, struct pool *_pool)
            :stock(_stock), pool(_pool) {
        }

        Domain(Domain &&other)
            :stock(other.stock), pool(other.pool) {
            assert(other.items.empty());

            other.pool = nullptr;
        }

        Domain(const Domain &) = delete;

        ~Domain() {
            assert(items.empty());

            if (pool != nullptr)
                pool_unref(pool);
        }

        ItemList::iterator FindUsableItem() {
            for (auto i = items.begin(), end = items.end(); i != end; ++i)
                if (i->CanUse())
                    return i;

            return items.end();
        }

        ItemList::iterator AddItem(DomainMap::iterator di, stock_item &si) {
            assert(&di->second == this);

            items.emplace_front(di, si);
            return items.begin();
        }

        stock_item *GetNow(DomainMap::iterator di,
                           struct pool *caller_pool,
                           const char *uri, void *info,
                           struct lease_ref &lease_ref,
                           GError **error_r);

        void DeleteItem(ItemList::iterator &ii) {
            ii = items.erase(ii);
        }

        void Put(const char *uri, stock_item &item, bool reuse) {
            stock.Put(uri, item, reuse);
        }
    };

    struct pool *pool;

    DomainMap domains;

    struct hstock *hstock;

public:
    MultiStock(struct pool *_pool, struct hstock *_hstock)
        :pool(pool_new_libc(_pool, "mstock")),
         hstock(_hstock) {}

    MultiStock(const MultiStock &) = delete;

    ~MultiStock() {
        hstock_free(hstock);
        pool_unref(pool);
    }

    void AddStats(stock_stats &data) const {
        hstock_add_stats(hstock, &data);
    }

    stock_item *GetNow(struct pool *caller_pool, const char *uri, void *info,
                       struct lease_ref &lease_ref,
                       GError **error_r);

    void Put(const char *uri, stock_item &item, bool reuse) {
        hstock_put(hstock, uri, &item, !reuse);
    }
};

const lease MultiStock::Domain::Item::Lease::lease = {
    Release,
};

stock_item *
MultiStock::Domain::GetNow(DomainMap::iterator di,
                           struct pool *caller_pool,
                           const char *uri, void *info,
                           struct lease_ref &lease_ref,
                           GError **error_r)
{
    auto i = FindUsableItem();
    if (i == items.end()) {
        stock_item *item =
            hstock_get_now(stock.hstock, caller_pool, uri, info,
                           error_r);
        items.emplace_front(di, *item);
        i = items.begin();
    }

    return i->AddLease(i, lease_ref);
}

inline stock_item *
MultiStock::GetNow(struct pool *caller_pool, const char *uri, void *info,
                   struct lease_ref &lease_ref,
                   GError **error_r)
{
    struct pool *domain_pool = pool_new_libc(pool, "mstock_domain");
    auto di = domains.insert(std::make_pair(uri, Domain(*this, domain_pool)))
        .first;
    return di->second.GetNow(di, caller_pool, uri, info, lease_ref, error_r);
}

/*
 * constructor
 *
 */

struct mstock *
mstock_new(struct pool *pool, struct hstock *hstock)
{
    return new MultiStock(pool, hstock);
}

void
mstock_free(struct mstock *_m)
{
    MultiStock *m = (MultiStock *)_m;
    delete m;
}

void
mstock_add_stats(const struct mstock *_m, stock_stats *data)
{
    const MultiStock &m = *(const MultiStock *)_m;
    m.AddStats(*data);
}

struct stock_item *
mstock_get_now(struct mstock *_m, struct pool *caller_pool,
               const char *uri, void *info,
               struct lease_ref *lease_ref,
               GError **error_r)
{
    MultiStock &m = *(MultiStock *)_m;
    return m.GetNow(caller_pool, uri, info, *lease_ref, error_r);
}
<commit_msg>mstock: make Item::domain const<commit_after>/*
 * A wrapper for hstock/stock that allows multiple users of one
 * stock_item.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "mstock.h"
#include "hstock.h"
#include "stock.h"
#include "hashmap.h"
#include "pool.h"
#include "lease.h"

#include <daemon/log.h>

#include <map>
#include <list>
#include <string>

#include <assert.h>

struct mstock {};

class MultiStock : public mstock {
    class Domain;
    typedef std::map<std::string, Domain> DomainMap;

    class Domain {
        class Item;
        typedef std::list<Item> ItemList;

        class Item {
            struct Lease : list_head {
                static const struct lease lease;

                const ItemList::iterator item;

                Lease(ItemList::iterator _item):item(_item) {}

                void Release(bool reuse) {
                    item->DeleteLease(this, reuse);
                }

                static void Release(bool reuse, void *ctx) {
                    ((Lease *)ctx)->Release(reuse);
                }
            };

            const DomainMap::iterator domain;

            stock_item &item;

            unsigned n_leases;
            list_head leases;

            bool reuse;

        public:
            Item(DomainMap::iterator _domain, stock_item &_item)
                :domain(_domain), item(_item), n_leases(0), reuse(true) {
                list_init(&leases);
            }

            Item(const Item &) = delete;

            ~Item() {
                assert(n_leases == 0);
                assert(list_empty(&leases));

                domain->second.Put(domain->first.c_str(), item, reuse);
            }

            bool IsFull() const {
                // TODO: dynamic limit
                return n_leases >= 4;
            }

            bool CanUse() const {
                return reuse && !IsFull();
            }

        private:
            Lease &AddLease(ItemList::iterator i) {
                assert(&*i == this);

                ++n_leases;

                Lease *lease = new Lease(i);
                list_add(lease, &leases);
                return *lease;
            }

        public:
            void AddLease(ItemList::iterator i,
                          const stock_get_handler &handler, void *ctx,
                          struct lease_ref &lease_ref) {
                assert(&*i == this);

                Lease &lease = AddLease(i);
                lease_ref_set(&lease_ref, &Lease::lease, &lease);

                handler.ready(&item, ctx);
            }

            stock_item *AddLease(ItemList::iterator i,
                                 struct lease_ref &lease_ref) {
                assert(&*i == this);

                Lease &lease = AddLease(i);
                lease_ref_set(&lease_ref, &Lease::lease, &lease);
                return &item;
            }

            void DeleteLease(Lease *lease, bool _reuse) {
                reuse &= _reuse;

                auto ii = lease->item;

                assert(n_leases > 0);
                list_remove(lease);
                delete lease;

                --n_leases;

                if (n_leases == 0)
                    domain->second.DeleteItem(ii);
            }
        };

        MultiStock &stock;

        struct pool *pool;

        ItemList items;

    public:
        Domain(MultiStock &_stock, struct pool *_pool)
            :stock(_stock), pool(_pool) {
        }

        Domain(Domain &&other)
            :stock(other.stock), pool(other.pool) {
            assert(other.items.empty());

            other.pool = nullptr;
        }

        Domain(const Domain &) = delete;

        ~Domain() {
            assert(items.empty());

            if (pool != nullptr)
                pool_unref(pool);
        }

        ItemList::iterator FindUsableItem() {
            for (auto i = items.begin(), end = items.end(); i != end; ++i)
                if (i->CanUse())
                    return i;

            return items.end();
        }

        ItemList::iterator AddItem(DomainMap::iterator di, stock_item &si) {
            assert(&di->second == this);

            items.emplace_front(di, si);
            return items.begin();
        }

        stock_item *GetNow(DomainMap::iterator di,
                           struct pool *caller_pool,
                           const char *uri, void *info,
                           struct lease_ref &lease_ref,
                           GError **error_r);

        void DeleteItem(ItemList::iterator &ii) {
            ii = items.erase(ii);
        }

        void Put(const char *uri, stock_item &item, bool reuse) {
            stock.Put(uri, item, reuse);
        }
    };

    struct pool *pool;

    DomainMap domains;

    struct hstock *hstock;

public:
    MultiStock(struct pool *_pool, struct hstock *_hstock)
        :pool(pool_new_libc(_pool, "mstock")),
         hstock(_hstock) {}

    MultiStock(const MultiStock &) = delete;

    ~MultiStock() {
        hstock_free(hstock);
        pool_unref(pool);
    }

    void AddStats(stock_stats &data) const {
        hstock_add_stats(hstock, &data);
    }

    stock_item *GetNow(struct pool *caller_pool, const char *uri, void *info,
                       struct lease_ref &lease_ref,
                       GError **error_r);

    void Put(const char *uri, stock_item &item, bool reuse) {
        hstock_put(hstock, uri, &item, !reuse);
    }
};

const lease MultiStock::Domain::Item::Lease::lease = {
    Release,
};

stock_item *
MultiStock::Domain::GetNow(DomainMap::iterator di,
                           struct pool *caller_pool,
                           const char *uri, void *info,
                           struct lease_ref &lease_ref,
                           GError **error_r)
{
    auto i = FindUsableItem();
    if (i == items.end()) {
        stock_item *item =
            hstock_get_now(stock.hstock, caller_pool, uri, info,
                           error_r);
        items.emplace_front(di, *item);
        i = items.begin();
    }

    return i->AddLease(i, lease_ref);
}

inline stock_item *
MultiStock::GetNow(struct pool *caller_pool, const char *uri, void *info,
                   struct lease_ref &lease_ref,
                   GError **error_r)
{
    struct pool *domain_pool = pool_new_libc(pool, "mstock_domain");
    auto di = domains.insert(std::make_pair(uri, Domain(*this, domain_pool)))
        .first;
    return di->second.GetNow(di, caller_pool, uri, info, lease_ref, error_r);
}

/*
 * constructor
 *
 */

struct mstock *
mstock_new(struct pool *pool, struct hstock *hstock)
{
    return new MultiStock(pool, hstock);
}

void
mstock_free(struct mstock *_m)
{
    MultiStock *m = (MultiStock *)_m;
    delete m;
}

void
mstock_add_stats(const struct mstock *_m, stock_stats *data)
{
    const MultiStock &m = *(const MultiStock *)_m;
    m.AddStats(*data);
}

struct stock_item *
mstock_get_now(struct mstock *_m, struct pool *caller_pool,
               const char *uri, void *info,
               struct lease_ref *lease_ref,
               GError **error_r)
{
    MultiStock &m = *(MultiStock *)_m;
    return m.GetNow(caller_pool, uri, info, *lease_ref, error_r);
}
<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    AMRResamplingToUniformGrid.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.

=========================================================================*/
// .NAME AMRResamplingToUniformGrid.cxx -- Resamples AMR data to a uniform grid.
//
// .SECTION Description
//  A simple code that resample an AMR dataset to a uniform grid. The resampled
//  may then be used for volume rendering for example.
//

#include <iostream>
#include <cassert>
#include <cmath>

#include "vtkAMREnzoReader.h"
#include "vtkHierarchicalBoxDataSet.h"
#include "vtkUniformGrid.h"
#include "vtkAMRBox.h"
#include "AMRCommon.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkCellData.h"
#include "vtkPointData.h"

// Description:
// Resamples the given AMR dataset to a uniform grid with resolution  equivalent
// the maxLevel of resolution
vtkUniformGrid* ReSample(vtkHierarchicalBoxDataSet *amrds, const int maxLevel);

// Description:
// Transfers the solution from the AMR dataset to the given re-sampled grid.
void TransferSolution( vtkUniformGrid *g, vtkHierarchicalBoxDataSet *amrds );

// Description:
// Checks if a donor cell can be found
bool FoundDonor(
  const double nx,const double ny,const double nz,
  vtkUniformGrid *donorGrid, float &yvel);

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

int main( int argc, char **argv )
{
  if( argc != 3 )
    {
      std::cout << "Usage: ARMResamplingToUniformGrid <file> <max-resolution> \n";
      std::cout.flush();
      return( -1 );
    }

  // STEP 0: Read AMR Data
  std::cout << "Reading input data...";
  std::cout.flush();

  vtkAMREnzoReader *myReader = vtkAMREnzoReader::New();
  myReader->SetMaxLevel( atoi(argv[2] )  );
  myReader->SetFileName( argv[1] );
  myReader->Update();

  myReader->SetCellArrayStatus( "y-velocity", 1 );
  myReader->Update();

  assert( myReader->GetCellArrayStatus( "y-velocity" ) == 1 );

  vtkHierarchicalBoxDataSet *amrds = myReader->GetOutput();
  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 1: Re-sample AMR data to pre-scribed resolution
  std::cout << "Re-sampling AMR data to uniform grid...";
  std::cout.flush();

  vtkUniformGrid *rsampledGrid = ReSample( amrds, atoi( argv[2] ) );

  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 3: Transfer solution
  std::cout << "Transfer the solution...";
  std::cout.flush();

  TransferSolution( rsampledGrid, amrds );
  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 4: Write re-sampled dataset
  double h[3];
  rsampledGrid->GetSpacing( h );
  h[ 0 ] *= 100;
  h[ 1 ] *= 100;
  h[ 2 ] *= 100;
  rsampledGrid->SetSpacing( h );
  std::cout << "Writing re-sampled grid...";
  std::cout.flush();
  AMRCommon::WriteUniformGrid( rsampledGrid, "RESAMPLED_GRID" );
  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 3: CleanUp
  rsampledGrid->Delete();
//  amrds->Delete();
//  myReader->Delete();
  return( 0 );
}

//------------------------------------------------------------------------------
bool FoundDonor(
  const double nx,const double ny,const double nz,
  vtkUniformGrid *donorGrid, float &yvel)
{

  double x[3];
  x[0] = nx;
  x[1] = ny;
  x[2] = nz;

  int ijk[3];
  double pcoords[3];
  int status = donorGrid->ComputeStructuredCoordinates(x, ijk, pcoords );
  if( status == 1 )
    {
      vtkIdType cellIdx = vtkStructuredData::ComputeCellId(
          donorGrid->GetDimensions(), ijk );
      yvel = donorGrid->GetCellData()->
          GetArray( "y-velocity" )->GetComponent( cellIdx, 0 );
      return true;
    }

  return false;
}

//------------------------------------------------------------------------------
void TransferSolution( vtkUniformGrid *g, vtkHierarchicalBoxDataSet *amrds )
{
  assert( "pre: Uniform grid is NULL" && ( g != NULL )  );
  assert( "pre: AMR datasets are NULL" && ( amrds != NULL )  );

  // NOTE: We assume y-velocity as the only attribute
  vtkFloatArray *da = vtkFloatArray::New();
  da->SetName( "y-velocity" );
  da->SetNumberOfComponents( 1 );
  da->SetNumberOfTuples( g->GetNumberOfPoints() );
  g->GetPointData()->AddArray( da );

  vtkIdType pIdx = 0;
  for( ; pIdx < g->GetNumberOfPoints(); ++pIdx )
    {

      double centroid[3];
      g->GetPoint( pIdx, centroid );

      bool found            = false;
      unsigned int levelIdx = 0;
      for( ; !found && levelIdx < amrds->GetNumberOfLevels(); ++levelIdx )
        {
          unsigned int dataIdx = 0;
          for( ; !found && dataIdx < amrds->GetNumberOfDataSets( levelIdx ); ++dataIdx )
            {
              vtkUniformGrid *donorGrid = amrds->GetDataSet( levelIdx, dataIdx );
              assert( "pre: donor grid should not be NULL!" &&
                  ( donorGrid != NULL ) );

              float yvel = 0.0;
              if( FoundDonor(
                    centroid[0], centroid[1], centroid[2],
                    donorGrid, yvel ) )
                {
                  da->SetComponent( pIdx, 0, yvel*100 );
                  found = true;
                  break;
                }
            } // END for all datasets
        } // END for all levels

      if( !found )
        {
          std::cerr << "Cannot find point-in-cell: ";
          std::cerr << "( " << centroid[0] << ", " << centroid[1] << ", ";
          std::cerr << centroid[2] << std::endl;
          std::cerr.flush();
        }

    } // END for all cells

  da->Delete();

}

//------------------------------------------------------------------------------
vtkUniformGrid* ReSample(vtkHierarchicalBoxDataSet *amrds, const int maxLevel )
{
  assert( "pre: AMR dataset is NULL!" && (amrds != NULL) );
  assert( "pre: level index is out-of-bounds" &&
    ( (maxLevel >= 0) && (maxLevel < amrds->GetNumberOfLevels() ) ) );
  assert( "pre: Number of datasets at requested level must be greater than zero" &&
    ( amrds->GetNumberOfDataSets(maxLevel) > 0) );

  // STEP 0: Get the level metadata
  vtkAMRBox lmd;
  amrds->GetMetaData( maxLevel, 0, lmd );

  amrds->GetGlobalAMRBoxWithSpacing(
      lmd, const_cast< double* >( lmd.GetGridSpacing() ) );

  vtkUniformGrid *myGrid = vtkUniformGrid::New();
  myGrid->Initialize();
  double origin[3];
  lmd.GetBoxOrigin( origin );
  myGrid->SetOrigin( origin );

  double h[3];
  lmd.GetGridSpacing( h );
  myGrid->SetSpacing( h );

  int ndim[3];
  lmd.GetNumberOfNodes( ndim );
  myGrid->SetDimensions( ndim );
  return( myGrid );
}
<commit_msg>ENH: VTHB file support<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    AMRResamplingToUniformGrid.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.

=========================================================================*/
// .NAME AMRResamplingToUniformGrid.cxx -- Resamples AMR data to a uniform grid.
//
// .SECTION Description
//  A simple code that resample an AMR dataset to a uniform grid. The resampled
//  may then be used for volume rendering for example.
//

#include <iostream>
#include <cassert>
#include <cmath>

#include "vtkAMREnzoReader.h"
#include "vtkHierarchicalBoxDataSet.h"
#include "vtkUniformGrid.h"
#include "vtkAMRBox.h"
#include "AMRCommon.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkCellData.h"
#include "vtkPointData.h"

// Description:
// Resamples the given AMR dataset to a uniform grid with resolution  equivalent
// the maxLevel of resolution
vtkUniformGrid* ReSample(vtkHierarchicalBoxDataSet *amrds, const int maxLevel);

// Description:
// Transfers the solution from the AMR dataset to the given re-sampled grid.
void TransferSolution( vtkUniformGrid *g, vtkHierarchicalBoxDataSet *amrds );

// Description:
// Checks if a donor cell can be found
bool FoundDonor(
  const double nx,const double ny,const double nz,
  vtkUniformGrid *donorGrid, float &yvel);

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

int main( int argc, char **argv )
{
  if( argc != 3 )
    {
      std::cout << "Usage: ARMResamplingToUniformGrid <file> <max-resolution> \n";
      std::cout.flush();
      return( -1 );
    }

  // STEP 0: Read AMR Data
  std::cout << "Reading input data...";
  std::cout.flush();

//  vtkAMREnzoReader *myReader = vtkAMREnzoReader::New();
//  myReader->SetMaxLevel( atoi(argv[2] )  );
//  myReader->SetFileName( argv[1] );
//  myReader->Update();
//
//  myReader->SetCellArrayStatus( "y-velocity", 1 );
//  myReader->Update();
//
//  assert( myReader->GetCellArrayStatus( "y-velocity" ) == 1 );
//
//  vtkHierarchicalBoxDataSet *amrds = myReader->GetOutput();

  vtkHierarchicalBoxDataSet *amrds =
      AMRCommon::ReadAMRData( std::string( argv[1] ) );

  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 1: Re-sample AMR data to pre-scribed resolution
  std::cout << "Re-sampling AMR data to uniform grid...";
  std::cout.flush();

  vtkUniformGrid *rsampledGrid = ReSample( amrds, atoi( argv[2] ) );

  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 3: Transfer solution
  std::cout << "Transfer the solution...";
  std::cout.flush();

  TransferSolution( rsampledGrid, amrds );
  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 4: Write re-sampled dataset
  double h[3];
  rsampledGrid->GetSpacing( h );
  h[ 0 ] *= 100;
  h[ 1 ] *= 100;
  h[ 2 ] *= 100;
  rsampledGrid->SetSpacing( h );
  std::cout << "Writing re-sampled grid...";
  std::cout.flush();
  AMRCommon::WriteUniformGrid( rsampledGrid, "RESAMPLED_GRID" );
  std::cout << "[DONE]\n";
  std::cout.flush();

  // STEP 3: CleanUp
  rsampledGrid->Delete();
//  amrds->Delete();
//  myReader->Delete();
  return( 0 );
}

//------------------------------------------------------------------------------
bool FoundDonor(
  const double nx,const double ny,const double nz,
  vtkUniformGrid *donorGrid, float &yvel)
{

  double x[3];
  x[0] = nx;
  x[1] = ny;
  x[2] = nz;

  int ijk[3];
  double pcoords[3];
  int status = donorGrid->ComputeStructuredCoordinates(x, ijk, pcoords );
  if( status == 1 )
    {
      vtkIdType cellIdx = vtkStructuredData::ComputeCellId(
          donorGrid->GetDimensions(), ijk );
      yvel = donorGrid->GetCellData()->
          GetArray( "GaussianPulse" )->GetComponent( cellIdx, 0 );
      return true;
    }

  return false;
}

//------------------------------------------------------------------------------
void TransferSolution( vtkUniformGrid *g, vtkHierarchicalBoxDataSet *amrds )
{
  assert( "pre: Uniform grid is NULL" && ( g != NULL )  );
  assert( "pre: AMR datasets are NULL" && ( amrds != NULL )  );

  // NOTE: We assume y-velocity as the only attribute
  vtkFloatArray *da = vtkFloatArray::New();
  da->SetName( "GaussianPulse" );
  da->SetNumberOfComponents( 1 );
  da->SetNumberOfTuples( g->GetNumberOfPoints() );
  g->GetPointData()->AddArray( da );

  vtkIdType pIdx = 0;
  for( ; pIdx < g->GetNumberOfPoints(); ++pIdx )
    {

      double centroid[3];
      g->GetPoint( pIdx, centroid );

      bool found            = false;
      unsigned int levelIdx = 0;
      for( ; !found && levelIdx < amrds->GetNumberOfLevels(); ++levelIdx )
        {
          unsigned int dataIdx = 0;
          for( ; !found && dataIdx < amrds->GetNumberOfDataSets( levelIdx ); ++dataIdx )
            {
              vtkUniformGrid *donorGrid = amrds->GetDataSet( levelIdx, dataIdx );
              assert( "pre: donor grid should not be NULL!" &&
                  ( donorGrid != NULL ) );

              float yvel = 0.0;
              if( FoundDonor(
                    centroid[0], centroid[1], centroid[2],
                    donorGrid, yvel ) )
                {
                  da->SetComponent( pIdx, 0, yvel*100 );
                  found = true;
                  break;
                }
            } // END for all datasets
        } // END for all levels

      if( !found )
        {
          std::cerr << "Cannot find point-in-cell: ";
          std::cerr << "( " << centroid[0] << ", " << centroid[1] << ", ";
          std::cerr << centroid[2] << std::endl;
          std::cerr.flush();
        }

    } // END for all cells

  da->Delete();

}

//------------------------------------------------------------------------------
vtkUniformGrid* ReSample(vtkHierarchicalBoxDataSet *amrds, const int maxLevel )
{
  assert( "pre: AMR dataset is NULL!" && (amrds != NULL) );
  assert( "pre: level index is out-of-bounds" &&
    ( (maxLevel >= 0) && (maxLevel < amrds->GetNumberOfLevels() ) ) );
  assert( "pre: Number of datasets at requested level must be greater than zero" &&
    ( amrds->GetNumberOfDataSets(maxLevel) > 0) );

  // STEP 0: Get the level metadata
  vtkAMRBox lmd;
  amrds->GetMetaData( maxLevel, 0, lmd );

  amrds->GetGlobalAMRBoxWithSpacing(
      lmd, const_cast< double* >( lmd.GetGridSpacing() ) );

  vtkUniformGrid *myGrid = vtkUniformGrid::New();
  myGrid->Initialize();
  double origin[3];
  lmd.GetBoxOrigin( origin );
  myGrid->SetOrigin( origin );

  double h[3];
  lmd.GetGridSpacing( h );
  myGrid->SetSpacing( h );

  int ndim[3];
  lmd.GetNumberOfNodes( ndim );
  myGrid->SetDimensions( ndim );
  return( myGrid );
}
<|endoftext|>