<commit_before>/**
 * libhmsbeagle plugin system
 * @author Aaron E. Darling
 * Based on code found in "Dynamic Plugins for C++" by Arthur J. Musgrove
 * and published in Dr. Dobbs Journal, July 1, 2004.
 */

#include "libhmsbeagle/GPU/BeagleGPUImpl.h"
#include "libhmsbeagle/GPU/CUDAPlugin.h"

namespace beagle {
namespace gpu {

CUDAPlugin::CUDAPlugin() :
Plugin("CUDA", "GPU")
{
        GPUInterface gpu;
        if (gpu.Initialize()) {
            int gpuDeviceCount = gpu.GetDeviceCount();
            for (int i = 0; i < gpuDeviceCount; i++) {
                char* dName = (char*) malloc(sizeof(char) * 100);
                char* dDesc = (char*) malloc(sizeof(char) * 100);
                gpu.GetDeviceName(i, dName, 100);
                gpu.GetDeviceDescription(i, dDesc);
		BeagleResource resource;
                resource.name = dName;
                resource.description = dDesc;
                resource.supportFlags = BEAGLE_FLAG_COMPUTATION_SYNCH |
                                                     BEAGLE_FLAG_PRECISION_SINGLE |
                                                     BEAGLE_FLAG_SCALING_MANUAL | BEAGLE_FLAG_SCALING_ALWAYS | BEAGLE_FLAG_SCALING_AUTO |
                                                     BEAGLE_FLAG_THREADING_NONE |
                                                     BEAGLE_FLAG_VECTOR_NONE |
                                                     BEAGLE_FLAG_PROCESSOR_GPU |
                                                     BEAGLE_FLAG_SCALERS_LOG | BEAGLE_FLAG_SCALERS_RAW |
                                                     BEAGLE_FLAG_EIGEN_COMPLEX | BEAGLE_FLAG_EIGEN_REAL;
                resource.requiredFlags = BEAGLE_FLAG_PROCESSOR_GPU;
		beagleResources.push_back(resource);
            }
        }

	// Optional for plugins: check if the hardware is compatible and only populate
	// list with compatible factories
	if(beagleResources.size() > 0)
		beagleFactories.push_back(new beagle::gpu::BeagleGPUImplFactory());
}

CUDAPlugin::~CUDAPlugin()
{
	// Destory GPU kernel info
	if(beagleResources.size()>0)
	{
		GPUInterface gpu;
		gpu.DestroyKernelMap();
	}
}

}	// namespace gpu
}	// namespace beagle

extern "C" {
void* plugin_init(void){
	return new beagle::gpu::CUDAPlugin();
}
}

<commit_msg>fixed small issue with initial CUDA device query<commit_after>/**
 * libhmsbeagle plugin system
 * @author Aaron E. Darling
 * Based on code found in "Dynamic Plugins for C++" by Arthur J. Musgrove
 * and published in Dr. Dobbs Journal, July 1, 2004.
 */

#include "libhmsbeagle/GPU/BeagleGPUImpl.h"
#include "libhmsbeagle/GPU/CUDAPlugin.h"

namespace beagle {
namespace gpu {

CUDAPlugin::CUDAPlugin() :
Plugin("CUDA", "GPU")
{
        GPUInterface gpu;
        if (gpu.Initialize()) {
            int gpuDeviceCount = gpu.GetDeviceCount();
            for (int i = 0; i < gpuDeviceCount; i++) {
                int nameDescSize = 256;
                char* dName = (char*) malloc(sizeof(char) * nameDescSize);
                char* dDesc = (char*) malloc(sizeof(char) * nameDescSize);
                gpu.GetDeviceName(i, dName, nameDescSize);
                gpu.GetDeviceDescription(i, dDesc);
		BeagleResource resource;
                resource.name = dName;
                resource.description = dDesc;
                resource.supportFlags = BEAGLE_FLAG_COMPUTATION_SYNCH |
                                                     BEAGLE_FLAG_PRECISION_SINGLE |
                                                     BEAGLE_FLAG_SCALING_MANUAL | BEAGLE_FLAG_SCALING_ALWAYS | BEAGLE_FLAG_SCALING_AUTO |
                                                     BEAGLE_FLAG_THREADING_NONE |
                                                     BEAGLE_FLAG_VECTOR_NONE |
                                                     BEAGLE_FLAG_PROCESSOR_GPU |
                                                     BEAGLE_FLAG_SCALERS_LOG | BEAGLE_FLAG_SCALERS_RAW |
                                                     BEAGLE_FLAG_EIGEN_COMPLEX | BEAGLE_FLAG_EIGEN_REAL;
                resource.requiredFlags = BEAGLE_FLAG_PROCESSOR_GPU;
		beagleResources.push_back(resource);
            }
        }

	// Optional for plugins: check if the hardware is compatible and only populate
	// list with compatible factories
	if(beagleResources.size() > 0)
		beagleFactories.push_back(new beagle::gpu::BeagleGPUImplFactory());
}

CUDAPlugin::~CUDAPlugin()
{
	// Destory GPU kernel info
	if(beagleResources.size()>0)
	{
		GPUInterface gpu;
		gpu.DestroyKernelMap();
	}
}

}	// namespace gpu
}	// namespace beagle

extern "C" {
void* plugin_init(void){
	return new beagle::gpu::CUDAPlugin();
}
}

<|endoftext|>


<commit_before>/*
  Simple Addressbook for KMail
  Copyright Stefan Taferner <taferner@kde.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "kaddrbookexternal.h"
#include "kaddressbookcore_interface.h"

#ifdef KDEPIM_NEW_DISTRLISTS
#include "distributionlist.h"
#else
#include <kabc/distributionlist.h>
#endif
#include <kabc/resource.h>
#include <kabc/stdaddressbook.h>
#include <kabc/vcardconverter.h>
#include <kabc/errorhandler.h>
#include <kresources/selectdialog.h>

#include <kdefakes.h> // usleep
#include <KDebug>
#include <KLocale>
#include <KMessageBox>
#include <KToolInvocation>

#include <QApplication>
#include <QEventLoop>
#include <QList>
#include <QRegExp>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnection>

#include <unistd.h>

using namespace KPIM;

//-----------------------------------------------------------------------------
void KAddrBookExternal::openEmail( const QString &email, const QString &addr, QWidget *) {
  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
  KABC::Addressee::List addresseeList = addressBook->findByEmail(email);

  // If KAddressbook is running, talk to it, otherwise start it.
  QDBusInterface abinterface( "org.kde.KAddressbook", "/kaddressbook_PimApplication",
                              "org.kde.KUniqueApplication" );

  if ( abinterface.isValid() ) {
    //make sure kaddressbook is loaded, otherwise showContactEditor
    //won't work as desired, see bug #87233
    abinterface.call("newInstance", QByteArray(), QByteArray());
  } else {
    KToolInvocation::startServiceByDesktopName( "kaddressbook" );
  }

  OrgKdeKAddressbookCoreInterface interface( "org.kde.KAddressbook",
                                             "/KAddressbook",
                                             QDBusConnection::sessionBus() );
  if( !addresseeList.isEmpty() ) {
    interface.showContactEditor( addresseeList.first().uid() );
  } else {
    interface.addEmail( addr );
  }
}

//-----------------------------------------------------------------------------
void KAddrBookExternal::addEmail( const QString &addr, QWidget *parent )
{
  QString email;
  QString name;

  KABC::Addressee::parseEmailAddress( addr, name, email );
  KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
  ab->setErrorHandler( new KABC::GuiErrorHandler( parent ) );

  // force a reload of the address book file so that changes that were made
  // by other programs are loaded
  ab->asyncLoad();

  KABC::Addressee::List addressees = ab->findByEmail( email );

  if ( addressees.isEmpty() ) {
    KABC::Addressee a;
    a.setNameFromString( name );
    a.insertEmail( email, true );

    if ( KAddrBookExternal::addAddressee( a ) ) {
      QString text = i18n( "<qt>The email address %1 was added to your "
                           "addressbook; you can add more information to this "
                           "entry by opening the addressbook.</qt>", addr );
      KMessageBox::information( parent, text, QString(), "addedtokabc" );
    }
  } else {
    QString text =
      i18n( "<qt>The email address %1 is already in your addressbook.</qt>", addr );
    KMessageBox::information( parent, text, QString(), "alreadyInAddressBook" );
  }
  ab->setErrorHandler( 0 );
}

void KAddrBookExternal::openAddressBook( QWidget * )
{
  KToolInvocation::startServiceByDesktopName( "kaddressbook" );
}

void KAddrBookExternal::addNewAddressee( QWidget * )
{
  KToolInvocation::startServiceByDesktopName( "kaddressbook" );
  OrgKdeKAddressbookCoreInterface interface( "org.kde.KAddressbook",
                                             "/KAddressbook",
                                             QDBusConnection::sessionBus() );
  interface.newContact();
}

bool KAddrBookExternal::addVCard( const KABC::Addressee &addressee, QWidget *parent )
{
  KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
  bool inserted = false;

  ab->setErrorHandler( new KABC::GuiErrorHandler( parent ) );

  KABC::Addressee::List addressees =
    ab->findByEmail( addressee.preferredEmail() );

  if ( addressees.isEmpty() ) {
    if ( KAddrBookExternal::addAddressee( addressee ) ) {
      QString text = i18n( "The VCard was added to your addressbook; "
                           "you can add more information to this "
                           "entry by opening the addressbook." );
      KMessageBox::information( parent, text, QString(), "addedtokabc" );
      inserted = true;
    }
  } else {
    QString text = i18n( "The VCard's primary email address is already in "
                         "your addressbook; however, you may save the VCard "
                         "into a file and import it into the addressbook manually." );
    KMessageBox::information( parent, text );
    inserted = true;
  }

  ab->setErrorHandler( 0 );
  return inserted;
}

bool KAddrBookExternal::addAddressee( const KABC::Addressee &addr )
{
  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );

  // PORT. FIXME: This ugly hack will be removed in 4.0
  while ( !addressBook->loadingHasFinished() ) {
    qApp->processEvents( QEventLoop::ExcludeUserInputEvents );

    // use sleep here to reduce cpu usage
    usleep( 100 );
  }

  // Select a resource
  QList<KABC::Resource*> kabcResources = addressBook->resources();
  QList<KRES::Resource*> kresResources;
  QListIterator<KABC::Resource*> resIt( kabcResources );
  KABC::Resource *kabcResource;
  while ( resIt.hasNext() ) {
    kabcResource = resIt.next();
    if ( !kabcResource->readOnly() ) {
      KRES::Resource *res = static_cast<KRES::Resource*>( kabcResource );
      if ( res ) {
        kresResources.append( res );
      }
    }
  }

  kabcResource =
    static_cast<KABC::Resource*>( KRES::SelectDialog::getResource( kresResources, 0 ) );

  KABC::Ticket *ticket = addressBook->requestSaveTicket( kabcResource );
  bool saved = false;
  if ( ticket ) {
    KABC::Addressee addressee( addr );
    addressee.setResource( kabcResource );
    addressBook->insertAddressee( addressee );
    saved = addressBook->save( ticket );
    if ( !saved ) {
      addressBook->releaseSaveTicket( ticket );
    }
  }

  addressBook->emitAddressBookChanged();

  return saved;
}

QString KAddrBookExternal::expandDistributionList( const QString &listName )
{
  if ( listName.isEmpty() ) {
    return QString();
  }

  const QString lowerListName = listName.toLower();
  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
#ifdef KDEPIM_NEW_DISTRLISTS
  KPIM::DistributionList distrList =
    KPIM::DistributionList::findByName( addressBook, lowerListName, false );
  if ( !distrList.isEmpty() ) {
    return distrList.emails( addressBook ).join( ", " );
  }
#else
  KABC::DistributionList* list = addressBook->findDistributionListByName( listName, Qt::CaseInsensitive );

  if ( list ) {
    return list->emails().join( ", " );
  }
#endif
  return QString();
}
<commit_msg>backport SVN commit 884354 by tmcguire:<commit_after>/*
  Simple Addressbook for KMail
  Copyright Stefan Taferner <taferner@kde.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "kaddrbookexternal.h"
#include "kaddressbookcore_interface.h"

#ifdef KDEPIM_NEW_DISTRLISTS
#include "distributionlist.h"
#else
#include <kabc/distributionlist.h>
#endif
#include <kabc/resource.h>
#include <kabc/stdaddressbook.h>
#include <kabc/vcardconverter.h>
#include <kabc/errorhandler.h>
#include <kresources/selectdialog.h>

#include <kdefakes.h> // usleep
#include <KDebug>
#include <KLocale>
#include <KMessageBox>
#include <KToolInvocation>

#include <QApplication>
#include <QEventLoop>
#include <QList>
#include <QRegExp>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnection>

#include <unistd.h>

using namespace KPIM;

//-----------------------------------------------------------------------------
void KAddrBookExternal::openEmail( const QString &email, const QString &addr, QWidget *) {
  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
  KABC::Addressee::List addresseeList = addressBook->findByEmail(email);

  // If KAddressbook is running, talk to it, otherwise start it.
  QDBusInterface abinterface( "org.kde.kaddressbook", "/kaddressbook_PimApplication",
                              "org.kde.KUniqueApplication" );

  if ( abinterface.isValid() ) {
    //make sure kaddressbook is loaded, otherwise showContactEditor
    //won't work as desired, see bug #87233
    abinterface.call("newInstance", QByteArray(), QByteArray());
  } else {
    KToolInvocation::startServiceByDesktopName( "kaddressbook" );
  }

  OrgKdeKAddressbookCoreInterface interface( "org.kde.kaddressbook",
                                             "/KAddressBook",
                                             QDBusConnection::sessionBus() );
  if( !addresseeList.isEmpty() ) {
    interface.showContactEditor( addresseeList.first().uid() );
  } else {
    interface.addEmail( addr );
  }
}

//-----------------------------------------------------------------------------
void KAddrBookExternal::addEmail( const QString &addr, QWidget *parent )
{
  QString email;
  QString name;

  KABC::Addressee::parseEmailAddress( addr, name, email );
  KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
  ab->setErrorHandler( new KABC::GuiErrorHandler( parent ) );

  // force a reload of the address book file so that changes that were made
  // by other programs are loaded
  ab->asyncLoad();

  KABC::Addressee::List addressees = ab->findByEmail( email );

  if ( addressees.isEmpty() ) {
    KABC::Addressee a;
    a.setNameFromString( name );
    a.insertEmail( email, true );

    if ( KAddrBookExternal::addAddressee( a ) ) {
      QString text = i18n( "<qt>The email address %1 was added to your "
                           "addressbook; you can add more information to this "
                           "entry by opening the addressbook.</qt>", addr );
      KMessageBox::information( parent, text, QString(), "addedtokabc" );
    }
  } else {
    QString text =
      i18n( "<qt>The email address %1 is already in your addressbook.</qt>", addr );
    KMessageBox::information( parent, text, QString(), "alreadyInAddressBook" );
  }
  ab->setErrorHandler( 0 );
}

void KAddrBookExternal::openAddressBook( QWidget * )
{
  KToolInvocation::startServiceByDesktopName( "kaddressbook" );
}

void KAddrBookExternal::addNewAddressee( QWidget * )
{
  KToolInvocation::startServiceByDesktopName( "kaddressbook" );
  OrgKdeKAddressbookCoreInterface interface( "org.kde.kaddressbook",
                                             "/KAddressBook",
                                             QDBusConnection::sessionBus() );
  interface.newContact();
}

bool KAddrBookExternal::addVCard( const KABC::Addressee &addressee, QWidget *parent )
{
  KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
  bool inserted = false;

  ab->setErrorHandler( new KABC::GuiErrorHandler( parent ) );

  KABC::Addressee::List addressees =
    ab->findByEmail( addressee.preferredEmail() );

  if ( addressees.isEmpty() ) {
    if ( KAddrBookExternal::addAddressee( addressee ) ) {
      QString text = i18n( "The VCard was added to your addressbook; "
                           "you can add more information to this "
                           "entry by opening the addressbook." );
      KMessageBox::information( parent, text, QString(), "addedtokabc" );
      inserted = true;
    }
  } else {
    QString text = i18n( "The VCard's primary email address is already in "
                         "your addressbook; however, you may save the VCard "
                         "into a file and import it into the addressbook manually." );
    KMessageBox::information( parent, text );
    inserted = true;
  }

  ab->setErrorHandler( 0 );
  return inserted;
}

bool KAddrBookExternal::addAddressee( const KABC::Addressee &addr )
{
  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );

  // PORT. FIXME: This ugly hack will be removed in 4.0
  while ( !addressBook->loadingHasFinished() ) {
    qApp->processEvents( QEventLoop::ExcludeUserInputEvents );

    // use sleep here to reduce cpu usage
    usleep( 100 );
  }

  // Select a resource
  QList<KABC::Resource*> kabcResources = addressBook->resources();
  QList<KRES::Resource*> kresResources;
  QListIterator<KABC::Resource*> resIt( kabcResources );
  KABC::Resource *kabcResource;
  while ( resIt.hasNext() ) {
    kabcResource = resIt.next();
    if ( !kabcResource->readOnly() ) {
      KRES::Resource *res = static_cast<KRES::Resource*>( kabcResource );
      if ( res ) {
        kresResources.append( res );
      }
    }
  }

  kabcResource =
    static_cast<KABC::Resource*>( KRES::SelectDialog::getResource( kresResources, 0 ) );

  KABC::Ticket *ticket = addressBook->requestSaveTicket( kabcResource );
  bool saved = false;
  if ( ticket ) {
    KABC::Addressee addressee( addr );
    addressee.setResource( kabcResource );
    addressBook->insertAddressee( addressee );
    saved = addressBook->save( ticket );
    if ( !saved ) {
      addressBook->releaseSaveTicket( ticket );
    }
  }

  addressBook->emitAddressBookChanged();

  return saved;
}

QString KAddrBookExternal::expandDistributionList( const QString &listName )
{
  if ( listName.isEmpty() ) {
    return QString();
  }

  const QString lowerListName = listName.toLower();
  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
#ifdef KDEPIM_NEW_DISTRLISTS
  KPIM::DistributionList distrList =
    KPIM::DistributionList::findByName( addressBook, lowerListName, false );
  if ( !distrList.isEmpty() ) {
    return distrList.emails( addressBook ).join( ", " );
  }
#else
  KABC::DistributionList* list = addressBook->findDistributionListByName( listName, Qt::CaseInsensitive );

  if ( list ) {
    return list->emails().join( ", " );
  }
#endif
  return QString();
}
<|endoftext|>


<commit_before><commit_msg>EXP-893 EXTRA -- Single order purchases not sorted with most recent item delivered at top of list<commit_after><|endoftext|>


<commit_before>

#include <unistd.h>
#include <sstream>
#include <execinfo.h>

#include "daemon/beanstalk.hpp"
#include "video/logging_videobuffer.h"

#include "tclap/CmdLine.h"
#include "alpr.h"
#include "openalpr/simpleini/simpleini.h"
#include "openalpr/cjson.h"
#include "support/tinythread.h"
#include <curl/curl.h>
#include "support/timing.h"

#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h>

using namespace alpr;

// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);
bool uploadPost(std::string url, std::string data);
void dataUploadThread(void* arg);

// Constants
const std::string ALPRD_CONFIG_FILE_NAME="alprd.conf";
const std::string OPENALPR_CONFIG_FILE_NAME="openalpr.conf";
const std::string DEFAULT_LOG_FILE_PATH="/var/log/alprd.log";

const std::string BEANSTALK_QUEUE_HOST="127.0.0.1";
const int BEANSTALK_PORT=11300;
const std::string BEANSTALK_TUBE_NAME="alprd";

struct CaptureThreadData
{
  std::string stream_url;
  std::string site_id;
  int camera_id;

  bool clock_on;

  std::string config_file;
  std::string country_code;
  bool output_images;
  std::string output_image_folder;
  int top_n;
};

struct UploadThreadData
{
  std::string upload_url;
};

void segfault_handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

bool daemon_active;

static log4cplus::Logger logger;

int main( int argc, const char** argv )
{
  signal(SIGSEGV, segfault_handler);   // install our segfault handler
  daemon_active = true;

  bool noDaemon = false;
  bool clockOn = false;
  std::string logFile;

  std::string configDir;

  TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion());

  TCLAP::ValueArg<std::string> configDirArg("","config","Path to the openalpr config directory that contains alprd.conf and openalpr.conf. (Default: /etc/openalpr/)",false, "/etc/openalpr/" ,"config_file");
  TCLAP::ValueArg<std::string> logFileArg("l","log","Log file to write to.  Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN");

  TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging.  Disables forking the process as a daemon and runs in the foreground.  Default=off", cmd, false);
  TCLAP::SwitchArg clockSwitch("","clock","Display timing information to log.  Default=off", cmd, false);

  try
  {

    cmd.add( configDirArg );
    cmd.add( logFileArg );

    if (cmd.parse( argc, argv ) == false)
    {
      // Error occured while parsing.  Exit now.
      return 1;
    }

    // Make sure configDir ends in a slash
    configDir = configDirArg.getValue();
    if (hasEnding(configDir, "/") == false)
      configDir = configDir + "/";

    logFile = logFileArg.getValue();
    noDaemon = daemonOffSwitch.getValue();
    clockOn = clockSwitch.getValue();
  }
  catch (TCLAP::ArgException &e)    // catch any exceptions
  {
    std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    return 1;
  }

  std::string openAlprConfigFile = configDir + OPENALPR_CONFIG_FILE_NAME;
  std::string daemonConfigFile = configDir + ALPRD_CONFIG_FILE_NAME;

  // Validate that the configuration files exist
  if (fileExists(openAlprConfigFile.c_str()) == false)
  {
    std::cerr << "error, openalpr.conf file does not exist at: " << openAlprConfigFile << std::endl;
    return 1;
  }
  if (fileExists(daemonConfigFile.c_str()) == false)
  {
    std::cerr << "error, alprd.conf file does not exist at: " << daemonConfigFile << std::endl;
    return 1;
  }

  log4cplus::BasicConfigurator config;
  config.configure();

  if (noDaemon == false)
  {
    // Fork off into a separate daemon
    daemon(0, 0);

    log4cplus::SharedAppenderPtr myAppender(new log4cplus::RollingFileAppender(logFile));
    myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode.");
  }
  else
  {
    //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender());
    //myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    //logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground.");
  }

  CSimpleIniA ini;
  ini.SetMultiKey();

  ini.LoadFile(daemonConfigFile.c_str());

  std::vector<std::string> stream_urls;

  CSimpleIniA::TNamesDepend values;
  ini.GetAllValues("daemon", "stream", values);

  // sort the values into the original load order
  values.sort(CSimpleIniA::Entry::LoadOrder());

  // output all of the items
  CSimpleIniA::TNamesDepend::const_iterator i;
  for (i = values.begin(); i != values.end(); ++i) {
      stream_urls.push_back(i->pItem);
  }

  if (stream_urls.size() == 0)
  {
    LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration.");
    return 1;
  }

  std::string country = ini.GetValue("daemon", "country", "us");
  int topn = ini.GetLongValue("daemon", "topn", 20);

  bool storePlates = ini.GetBoolValue("daemon", "store_plates", false);
  std::string imageFolder = ini.GetValue("daemon", "store_plates_location", "/tmp/");
  bool uploadData = ini.GetBoolValue("daemon", "upload_data", false);
  std::string upload_url = ini.GetValue("daemon", "upload_address", "");
  std::string site_id = ini.GetValue("daemon", "site_id", "");

  LOG4CPLUS_INFO(logger, "Using: " << daemonConfigFile << " for daemon configuration");
  LOG4CPLUS_INFO(logger, "Using: " << imageFolder << " for storing valid plate images");

  pid_t pid;

  for (int i = 0; i < stream_urls.size(); i++)
  {
    pid = fork();
    if (pid == (pid_t) 0)
    {
      // This is the child process, kick off the capture data and upload threads
      CaptureThreadData* tdata = new CaptureThreadData();
      tdata->stream_url = stream_urls[i];
      tdata->camera_id = i + 1;
      tdata->config_file = openAlprConfigFile;
      tdata->output_images = storePlates;
      tdata->output_image_folder = imageFolder;
      tdata->country_code = country;
      tdata->site_id = site_id;
      tdata->top_n = topn;
      tdata->clock_on = clockOn;

      tthread::thread* thread_recognize = new tthread::thread(streamRecognitionThread, (void*) tdata);

      if (uploadData)
      {
	// Kick off the data upload thread
	UploadThreadData* udata = new UploadThreadData();
	udata->upload_url = upload_url;
	tthread::thread* thread_upload = new tthread::thread(dataUploadThread, (void*) udata );
      }

      break;
    }

    // Parent process will continue and spawn more children
  }

  while (daemon_active)
  {
    usleep(30000);
  }

}

void streamRecognitionThread(void* arg)
{
  CaptureThreadData* tdata = (CaptureThreadData*) arg;

  LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file );
  LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url);

  Alpr alpr(tdata->country_code, tdata->config_file);
  alpr.setTopN(tdata->top_n);

  int framenum = 0;

  LoggingVideoBuffer videoBuffer(logger);

  videoBuffer.connect(tdata->stream_url, 5);

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

  LOG4CPLUS_INFO(logger, "Starting camera " << tdata->camera_id);

  while (daemon_active)
  {
    std::vector<cv::Rect> regionsOfInterest;
    int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest);

    if (response != -1)
    {

      timespec startTime;
      getTime(&startTime);

      std::vector<AlprRegionOfInterest> regionsOfInterest;
      regionsOfInterest.push_back(AlprRegionOfInterest(0,0, latestFrame.cols, latestFrame.rows));

      AlprResults results = alpr.recognize(latestFrame.data, latestFrame.elemSize(), latestFrame.cols, latestFrame.rows, regionsOfInterest);

      timespec endTime;
      getTime(&endTime);
      double totalProcessingTime = diffclock(startTime, endTime);

      if (tdata->clock_on)
      {
	LOG4CPLUS_INFO(logger, "Camera " << tdata->camera_id << " processed frame in: " << totalProcessingTime << " ms.");
      }

      if (results.plates.size() > 0)
      {
        long epoch_time = getEpochTime();

        std::stringstream uuid_ss;
        uuid_ss << tdata->site_id << "-cam" << tdata->camera_id << "-" << epoch_time;
	std::string uuid = uuid_ss.str();

	// Save the image to disk (using the UUID)
	if (tdata->output_images)
	{
	  std::stringstream ss;
	  ss << tdata->output_image_folder << "/" << uuid << ".jpg";

	  cv::imwrite(ss.str(), latestFrame);
	}

	// Update the JSON content to include UUID and camera ID

	std::string json = alpr.toJson(results);

	cJSON *root = cJSON_Parse(json.c_str());
	cJSON_AddStringToObject(root,	"uuid",		uuid.c_str());
	cJSON_AddNumberToObject(root,	"camera_id",	tdata->camera_id);
	cJSON_AddStringToObject(root, 	"site_id", 	tdata->site_id.c_str());
	cJSON_AddNumberToObject(root,	"img_width",	latestFrame.cols);
	cJSON_AddNumberToObject(root,	"img_height",	latestFrame.rows);

	char *out;
	out=cJSON_PrintUnformatted(root);
	cJSON_Delete(root);

	std::string response(out);

	free(out);

	// Push the results to the Beanstalk queue
	for (int j = 0; j < results.plates.size(); j++)
	{
	  LOG4CPLUS_DEBUG(logger, "Writing plate " << results.plates[j].bestPlate.characters << " (" <<  uuid << ") to queue.");
	}

	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

bool writeToQueue(std::string jsonResult)
{
  try
  {
    Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);
    client.use(BEANSTALK_TUBE_NAME);

    int id = client.put(jsonResult);

    if (id <= 0)
    {
      LOG4CPLUS_ERROR(logger, "Failed to write data to queue");
      return false;
    }

    LOG4CPLUS_DEBUG(logger, "put job id: " << id );

  }
  catch (const std::runtime_error& error)
  {
    LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Result has not been saved.");
    return false;
  }
  return true;
}

void dataUploadThread(void* arg)
{
  CURL *curl;

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  UploadThreadData* udata = (UploadThreadData*) arg;

  while(daemon_active)
  {
    try
    {
      /* get a curl handle */
      curl = curl_easy_init();
      Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);

      client.watch(BEANSTALK_TUBE_NAME);

      while (daemon_active)
      {
	Beanstalk::Job job;

	client.reserve(job);

	if (job.id() > 0)
	{
	  //LOG4CPLUS_DEBUG(logger, job.body() );
	  if (uploadPost(curl, udata->upload_url, job.body()))
	  {
	    client.del(job.id());
	    LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" );
	    // Wait 10ms
	    sleep_ms(10);
	  }
	  else
	  {
	    client.release(job);
	    LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload.  Will retry." );
	    // Wait 2 seconds
	    sleep_ms(2000);
	  }
	}

      }

      /* always cleanup */
      curl_easy_cleanup(curl);
    }
    catch (const std::runtime_error& error)
    {
      LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Will retry." );
    }
    // wait 5 seconds
    usleep(5000000);
  }

  curl_global_cleanup();
}

bool uploadPost(CURL* curl, std::string url, std::string data)
{
  bool success = true;
  CURLcode res;
  struct curl_slist *headers=NULL; // init to NULL is important

  /* Add the required headers */
  headers = curl_slist_append(headers,  "Accept: application/json");
  headers = curl_slist_append( headers, "Content-Type: application/json");
  headers = curl_slist_append( headers, "charsets: utf-8");

  if(curl) {
	/* Add the headers */
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* Now specify the POST data */
    //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
    //curl_free(escaped_data);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      success = false;
    }

  }

  return success;

}<commit_msg>updated uploadPost prototype<commit_after>

#include <unistd.h>
#include <sstream>
#include <execinfo.h>

#include "daemon/beanstalk.hpp"
#include "video/logging_videobuffer.h"

#include "tclap/CmdLine.h"
#include "alpr.h"
#include "openalpr/simpleini/simpleini.h"
#include "openalpr/cjson.h"
#include "support/tinythread.h"
#include <curl/curl.h>
#include "support/timing.h"

#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h>

using namespace alpr;

// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);
bool uploadPost(CURL* curl, std::string url, std::string data);
void dataUploadThread(void* arg);

// Constants
const std::string ALPRD_CONFIG_FILE_NAME="alprd.conf";
const std::string OPENALPR_CONFIG_FILE_NAME="openalpr.conf";
const std::string DEFAULT_LOG_FILE_PATH="/var/log/alprd.log";

const std::string BEANSTALK_QUEUE_HOST="127.0.0.1";
const int BEANSTALK_PORT=11300;
const std::string BEANSTALK_TUBE_NAME="alprd";

struct CaptureThreadData
{
  std::string stream_url;
  std::string site_id;
  int camera_id;

  bool clock_on;

  std::string config_file;
  std::string country_code;
  bool output_images;
  std::string output_image_folder;
  int top_n;
};

struct UploadThreadData
{
  std::string upload_url;
};

void segfault_handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

bool daemon_active;

static log4cplus::Logger logger;

int main( int argc, const char** argv )
{
  signal(SIGSEGV, segfault_handler);   // install our segfault handler
  daemon_active = true;

  bool noDaemon = false;
  bool clockOn = false;
  std::string logFile;

  std::string configDir;

  TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion());

  TCLAP::ValueArg<std::string> configDirArg("","config","Path to the openalpr config directory that contains alprd.conf and openalpr.conf. (Default: /etc/openalpr/)",false, "/etc/openalpr/" ,"config_file");
  TCLAP::ValueArg<std::string> logFileArg("l","log","Log file to write to.  Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN");

  TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging.  Disables forking the process as a daemon and runs in the foreground.  Default=off", cmd, false);
  TCLAP::SwitchArg clockSwitch("","clock","Display timing information to log.  Default=off", cmd, false);

  try
  {

    cmd.add( configDirArg );
    cmd.add( logFileArg );

    if (cmd.parse( argc, argv ) == false)
    {
      // Error occured while parsing.  Exit now.
      return 1;
    }

    // Make sure configDir ends in a slash
    configDir = configDirArg.getValue();
    if (hasEnding(configDir, "/") == false)
      configDir = configDir + "/";

    logFile = logFileArg.getValue();
    noDaemon = daemonOffSwitch.getValue();
    clockOn = clockSwitch.getValue();
  }
  catch (TCLAP::ArgException &e)    // catch any exceptions
  {
    std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    return 1;
  }

  std::string openAlprConfigFile = configDir + OPENALPR_CONFIG_FILE_NAME;
  std::string daemonConfigFile = configDir + ALPRD_CONFIG_FILE_NAME;

  // Validate that the configuration files exist
  if (fileExists(openAlprConfigFile.c_str()) == false)
  {
    std::cerr << "error, openalpr.conf file does not exist at: " << openAlprConfigFile << std::endl;
    return 1;
  }
  if (fileExists(daemonConfigFile.c_str()) == false)
  {
    std::cerr << "error, alprd.conf file does not exist at: " << daemonConfigFile << std::endl;
    return 1;
  }

  log4cplus::BasicConfigurator config;
  config.configure();

  if (noDaemon == false)
  {
    // Fork off into a separate daemon
    daemon(0, 0);

    log4cplus::SharedAppenderPtr myAppender(new log4cplus::RollingFileAppender(logFile));
    myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode.");
  }
  else
  {
    //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender());
    //myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    //logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground.");
  }

  CSimpleIniA ini;
  ini.SetMultiKey();

  ini.LoadFile(daemonConfigFile.c_str());

  std::vector<std::string> stream_urls;

  CSimpleIniA::TNamesDepend values;
  ini.GetAllValues("daemon", "stream", values);

  // sort the values into the original load order
  values.sort(CSimpleIniA::Entry::LoadOrder());

  // output all of the items
  CSimpleIniA::TNamesDepend::const_iterator i;
  for (i = values.begin(); i != values.end(); ++i) {
      stream_urls.push_back(i->pItem);
  }

  if (stream_urls.size() == 0)
  {
    LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration.");
    return 1;
  }

  std::string country = ini.GetValue("daemon", "country", "us");
  int topn = ini.GetLongValue("daemon", "topn", 20);

  bool storePlates = ini.GetBoolValue("daemon", "store_plates", false);
  std::string imageFolder = ini.GetValue("daemon", "store_plates_location", "/tmp/");
  bool uploadData = ini.GetBoolValue("daemon", "upload_data", false);
  std::string upload_url = ini.GetValue("daemon", "upload_address", "");
  std::string site_id = ini.GetValue("daemon", "site_id", "");

  LOG4CPLUS_INFO(logger, "Using: " << daemonConfigFile << " for daemon configuration");
  LOG4CPLUS_INFO(logger, "Using: " << imageFolder << " for storing valid plate images");

  pid_t pid;

  for (int i = 0; i < stream_urls.size(); i++)
  {
    pid = fork();
    if (pid == (pid_t) 0)
    {
      // This is the child process, kick off the capture data and upload threads
      CaptureThreadData* tdata = new CaptureThreadData();
      tdata->stream_url = stream_urls[i];
      tdata->camera_id = i + 1;
      tdata->config_file = openAlprConfigFile;
      tdata->output_images = storePlates;
      tdata->output_image_folder = imageFolder;
      tdata->country_code = country;
      tdata->site_id = site_id;
      tdata->top_n = topn;
      tdata->clock_on = clockOn;

      tthread::thread* thread_recognize = new tthread::thread(streamRecognitionThread, (void*) tdata);

      if (uploadData)
      {
	// Kick off the data upload thread
	UploadThreadData* udata = new UploadThreadData();
	udata->upload_url = upload_url;
	tthread::thread* thread_upload = new tthread::thread(dataUploadThread, (void*) udata );
      }

      break;
    }

    // Parent process will continue and spawn more children
  }

  while (daemon_active)
  {
    usleep(30000);
  }

}

void streamRecognitionThread(void* arg)
{
  CaptureThreadData* tdata = (CaptureThreadData*) arg;

  LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file );
  LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url);

  Alpr alpr(tdata->country_code, tdata->config_file);
  alpr.setTopN(tdata->top_n);

  int framenum = 0;

  LoggingVideoBuffer videoBuffer(logger);

  videoBuffer.connect(tdata->stream_url, 5);

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

  LOG4CPLUS_INFO(logger, "Starting camera " << tdata->camera_id);

  while (daemon_active)
  {
    std::vector<cv::Rect> regionsOfInterest;
    int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest);

    if (response != -1)
    {

      timespec startTime;
      getTime(&startTime);

      std::vector<AlprRegionOfInterest> regionsOfInterest;
      regionsOfInterest.push_back(AlprRegionOfInterest(0,0, latestFrame.cols, latestFrame.rows));

      AlprResults results = alpr.recognize(latestFrame.data, latestFrame.elemSize(), latestFrame.cols, latestFrame.rows, regionsOfInterest);

      timespec endTime;
      getTime(&endTime);
      double totalProcessingTime = diffclock(startTime, endTime);

      if (tdata->clock_on)
      {
	LOG4CPLUS_INFO(logger, "Camera " << tdata->camera_id << " processed frame in: " << totalProcessingTime << " ms.");
      }

      if (results.plates.size() > 0)
      {
        long epoch_time = getEpochTime();

        std::stringstream uuid_ss;
        uuid_ss << tdata->site_id << "-cam" << tdata->camera_id << "-" << epoch_time;
	std::string uuid = uuid_ss.str();

	// Save the image to disk (using the UUID)
	if (tdata->output_images)
	{
	  std::stringstream ss;
	  ss << tdata->output_image_folder << "/" << uuid << ".jpg";

	  cv::imwrite(ss.str(), latestFrame);
	}

	// Update the JSON content to include UUID and camera ID

	std::string json = alpr.toJson(results);

	cJSON *root = cJSON_Parse(json.c_str());
	cJSON_AddStringToObject(root,	"uuid",		uuid.c_str());
	cJSON_AddNumberToObject(root,	"camera_id",	tdata->camera_id);
	cJSON_AddStringToObject(root, 	"site_id", 	tdata->site_id.c_str());
	cJSON_AddNumberToObject(root,	"img_width",	latestFrame.cols);
	cJSON_AddNumberToObject(root,	"img_height",	latestFrame.rows);

	char *out;
	out=cJSON_PrintUnformatted(root);
	cJSON_Delete(root);

	std::string response(out);

	free(out);

	// Push the results to the Beanstalk queue
	for (int j = 0; j < results.plates.size(); j++)
	{
	  LOG4CPLUS_DEBUG(logger, "Writing plate " << results.plates[j].bestPlate.characters << " (" <<  uuid << ") to queue.");
	}

	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

bool writeToQueue(std::string jsonResult)
{
  try
  {
    Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);
    client.use(BEANSTALK_TUBE_NAME);

    int id = client.put(jsonResult);

    if (id <= 0)
    {
      LOG4CPLUS_ERROR(logger, "Failed to write data to queue");
      return false;
    }

    LOG4CPLUS_DEBUG(logger, "put job id: " << id );

  }
  catch (const std::runtime_error& error)
  {
    LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Result has not been saved.");
    return false;
  }
  return true;
}

void dataUploadThread(void* arg)
{
  CURL *curl;

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  UploadThreadData* udata = (UploadThreadData*) arg;

  while(daemon_active)
  {
    try
    {
      /* get a curl handle */
      curl = curl_easy_init();
      Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);

      client.watch(BEANSTALK_TUBE_NAME);

      while (daemon_active)
      {
	Beanstalk::Job job;

	client.reserve(job);

	if (job.id() > 0)
	{
	  //LOG4CPLUS_DEBUG(logger, job.body() );
	  if (uploadPost(curl, udata->upload_url, job.body()))
	  {
	    client.del(job.id());
	    LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" );
	    // Wait 10ms
	    sleep_ms(10);
	  }
	  else
	  {
	    client.release(job);
	    LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload.  Will retry." );
	    // Wait 2 seconds
	    sleep_ms(2000);
	  }
	}

      }

      /* always cleanup */
      curl_easy_cleanup(curl);
    }
    catch (const std::runtime_error& error)
    {
      LOG4CPLUS_WARN(logger, "Error connecting to Beanstalk.  Will retry." );
    }
    // wait 5 seconds
    usleep(5000000);
  }

  curl_global_cleanup();
}

bool uploadPost(CURL* curl, std::string url, std::string data)
{
  bool success = true;
  CURLcode res;
  struct curl_slist *headers=NULL; // init to NULL is important

  /* Add the required headers */
  headers = curl_slist_append(headers,  "Accept: application/json");
  headers = curl_slist_append( headers, "Content-Type: application/json");
  headers = curl_slist_append( headers, "charsets: utf-8");

  if(curl) {
	/* Add the headers */
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* Now specify the POST data */
    //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
    //curl_free(escaped_data);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      success = false;
    }

  }

  return success;

}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 */

#include <list>

#include "base/misc.hh"
#include "dev/ps2.hh"
#include "x11keysym/keysym.h"

namespace Ps2 {

/** Table to convert simple key symbols (0x00XX) into ps2 bytes. Lower byte
 * is the scan code to send and upper byte is if a modifier is required to
 * generate it. The table generates us keyboard codes, (e.g. the guest is
 * supposed to recognize the keyboard as en_US). A new table would be required
 * for another locale.
 */

static const uint16_t keySymToPs2Byte[128] = {
// 0 / 8   1 / 9   2 / A   3 / B   4 / C   5 / D   6 / E   7 / F
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00-0x07
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x08-0x0f
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x10-0x17
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x18-0x1f
   0x0029, 0x0116, 0x0152, 0x0126, 0x0125, 0x012e, 0x013d, 0x0052, // 0x20-0x27
   0x0146, 0x0145, 0x013e, 0x0155, 0x0041, 0x004e, 0x0049, 0x004a, // 0x28-0x2f
   0x0045, 0x0016, 0x001e, 0x0026, 0x0025, 0x002e, 0x0036, 0x003d, // 0x30-0x37
   0x003e, 0x0046, 0x014c, 0x004c, 0x0141, 0x0055, 0x0149, 0x014a, // 0x38-0x3f
   0x011e, 0x011c, 0x0132, 0x0121, 0x0123, 0x0124, 0x012b, 0x0134, // 0x40-0x47
   0x0133, 0x0143, 0x013b, 0x0142, 0x014b, 0x013a, 0x0131, 0x0144, // 0x48-0x4f
   0x014d, 0x0115, 0x012d, 0x011b, 0x012c, 0x013c, 0x012a, 0x011d, // 0x50-0x57
   0x0122, 0x0135, 0x011a, 0x0054, 0x005d, 0x005b, 0x0136, 0x014e, // 0x58-0x5f
   0x000e, 0x001c, 0x0032, 0x0021, 0x0023, 0x0024, 0x002b, 0x0034, // 0x60-0x67
   0x0033, 0x0043, 0x003b, 0x0042, 0x004b, 0x003a, 0x0031, 0x0044, // 0x68-0x6f
   0x004d, 0x0015, 0x002d, 0x001b, 0x002c, 0x003c, 0x002a, 0x001d, // 0x70-0x77
   0x0022, 0x0035, 0x001a, 0x0154, 0x015d, 0x015b, 0x010e, 0x0000  // 0x78-0x7f
};

const uint8_t ShiftKey = 0x12;
const uint8_t BreakKey = 0xf0;
const uint8_t ExtendedKey = 0xe0;
const uint32_t UpperKeys = 0xff00;

void
keySymToPs2(uint32_t key, bool down, bool &cur_shift,
        std::list<uint8_t> &keys)
{
    if (key <= XK_asciitilde) {
        uint16_t tmp = keySymToPs2Byte[key];
        uint8_t code = tmp & 0xff;
        bool shift = tmp >> 8;

        if (down) {
            if (!cur_shift && shift) {
                keys.push_back(ShiftKey);
                cur_shift = true;
            }
            keys.push_back(code);
        } else {
            if (cur_shift && !shift) {
                keys.push_back(BreakKey);
                keys.push_back(ShiftKey);
                cur_shift = false;
            }
            keys.push_back(BreakKey);
            keys.push_back(code);
        }
    } else {
        if ((key & UpperKeys) == UpperKeys) {
            bool extended = false;
            switch (key) {
              case XK_BackSpace:
                keys.push_back(0x66);
                break;
              case XK_Tab:
                keys.push_back(0x0d);
                break;
              case XK_Return:
                keys.push_back(0x5a);
                break;
             case XK_Escape:
                keys.push_back(0x76);
                break;
             case XK_Delete:
                extended = true;
                keys.push_back(0x71);
                break;
             case XK_Home:
                extended = true;
                keys.push_back(0x6c);
                break;
             case XK_Left:
                extended = true;
                keys.push_back(0x6b);
                break;
             case XK_Right:
                extended = true;
                keys.push_back(0x74);
                break;
             case XK_Down:
                extended = true;
                keys.push_back(0x72);
                break;
             case XK_Up:
                extended = true;
                keys.push_back(0x75);
                break;
             case XK_Page_Up:
                extended = true;
                keys.push_back(0x7d);
                break;
             case XK_Page_Down:
                extended = true;
                keys.push_back(0x7a);
                break;
             case XK_End:
                extended = true;
                keys.push_back(0x69);
                break;
             case XK_Shift_L:
                keys.push_back(0x12);
                if (down)
                    cur_shift = true;
                else
                    cur_shift = false;
                break;
             case XK_Shift_R:
                keys.push_back(0x59);
                if (down)
                    cur_shift = true;
                else
                    cur_shift = false;
                break;
             case XK_Control_L:
                keys.push_back(0x14);
                break;
             case XK_Control_R:
                extended = true;
                keys.push_back(0x14);
                break;
             default:
               warn("Unknown extended key %#x\n", key);
               return;
            }

            if (extended) {
                if (down) {
                    keys.push_front(ExtendedKey);
                } else {
                    keys.push_front(BreakKey);
                    keys.push_front(ExtendedKey);
                }
            } else {
                if (!down)
                    keys.push_front(BreakKey);
            }
        } // upper keys
    } // extended keys
    return;
}

} /* namespace Ps2 */

<commit_msg>dev: Support translating left and right ALT keys.<commit_after>/*
 * Copyright (c) 2011 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 */

#include <list>

#include "base/misc.hh"
#include "dev/ps2.hh"
#include "x11keysym/keysym.h"

namespace Ps2 {

/** Table to convert simple key symbols (0x00XX) into ps2 bytes. Lower byte
 * is the scan code to send and upper byte is if a modifier is required to
 * generate it. The table generates us keyboard codes, (e.g. the guest is
 * supposed to recognize the keyboard as en_US). A new table would be required
 * for another locale.
 */

static const uint16_t keySymToPs2Byte[128] = {
// 0 / 8   1 / 9   2 / A   3 / B   4 / C   5 / D   6 / E   7 / F
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00-0x07
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x08-0x0f
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x10-0x17
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x18-0x1f
   0x0029, 0x0116, 0x0152, 0x0126, 0x0125, 0x012e, 0x013d, 0x0052, // 0x20-0x27
   0x0146, 0x0145, 0x013e, 0x0155, 0x0041, 0x004e, 0x0049, 0x004a, // 0x28-0x2f
   0x0045, 0x0016, 0x001e, 0x0026, 0x0025, 0x002e, 0x0036, 0x003d, // 0x30-0x37
   0x003e, 0x0046, 0x014c, 0x004c, 0x0141, 0x0055, 0x0149, 0x014a, // 0x38-0x3f
   0x011e, 0x011c, 0x0132, 0x0121, 0x0123, 0x0124, 0x012b, 0x0134, // 0x40-0x47
   0x0133, 0x0143, 0x013b, 0x0142, 0x014b, 0x013a, 0x0131, 0x0144, // 0x48-0x4f
   0x014d, 0x0115, 0x012d, 0x011b, 0x012c, 0x013c, 0x012a, 0x011d, // 0x50-0x57
   0x0122, 0x0135, 0x011a, 0x0054, 0x005d, 0x005b, 0x0136, 0x014e, // 0x58-0x5f
   0x000e, 0x001c, 0x0032, 0x0021, 0x0023, 0x0024, 0x002b, 0x0034, // 0x60-0x67
   0x0033, 0x0043, 0x003b, 0x0042, 0x004b, 0x003a, 0x0031, 0x0044, // 0x68-0x6f
   0x004d, 0x0015, 0x002d, 0x001b, 0x002c, 0x003c, 0x002a, 0x001d, // 0x70-0x77
   0x0022, 0x0035, 0x001a, 0x0154, 0x015d, 0x015b, 0x010e, 0x0000  // 0x78-0x7f
};

const uint8_t ShiftKey = 0x12;
const uint8_t BreakKey = 0xf0;
const uint8_t ExtendedKey = 0xe0;
const uint32_t UpperKeys = 0xff00;

void
keySymToPs2(uint32_t key, bool down, bool &cur_shift,
        std::list<uint8_t> &keys)
{
    if (key <= XK_asciitilde) {
        uint16_t tmp = keySymToPs2Byte[key];
        uint8_t code = tmp & 0xff;
        bool shift = tmp >> 8;

        if (down) {
            if (!cur_shift && shift) {
                keys.push_back(ShiftKey);
                cur_shift = true;
            }
            keys.push_back(code);
        } else {
            if (cur_shift && !shift) {
                keys.push_back(BreakKey);
                keys.push_back(ShiftKey);
                cur_shift = false;
            }
            keys.push_back(BreakKey);
            keys.push_back(code);
        }
    } else {
        if ((key & UpperKeys) == UpperKeys) {
            bool extended = false;
            switch (key) {
              case XK_BackSpace:
                keys.push_back(0x66);
                break;
              case XK_Tab:
                keys.push_back(0x0d);
                break;
              case XK_Return:
                keys.push_back(0x5a);
                break;
             case XK_Escape:
                keys.push_back(0x76);
                break;
             case XK_Delete:
                extended = true;
                keys.push_back(0x71);
                break;
             case XK_Home:
                extended = true;
                keys.push_back(0x6c);
                break;
             case XK_Left:
                extended = true;
                keys.push_back(0x6b);
                break;
             case XK_Right:
                extended = true;
                keys.push_back(0x74);
                break;
             case XK_Down:
                extended = true;
                keys.push_back(0x72);
                break;
             case XK_Up:
                extended = true;
                keys.push_back(0x75);
                break;
             case XK_Page_Up:
                extended = true;
                keys.push_back(0x7d);
                break;
             case XK_Page_Down:
                extended = true;
                keys.push_back(0x7a);
                break;
             case XK_End:
                extended = true;
                keys.push_back(0x69);
                break;
             case XK_Shift_L:
                keys.push_back(0x12);
                if (down)
                    cur_shift = true;
                else
                    cur_shift = false;
                break;
             case XK_Shift_R:
                keys.push_back(0x59);
                if (down)
                    cur_shift = true;
                else
                    cur_shift = false;
                break;
             case XK_Control_L:
                keys.push_back(0x14);
                break;
             case XK_Control_R:
                extended = true;
                keys.push_back(0x14);
                break;
             case XK_Alt_L:
                keys.push_back(0x11);
                break;
             case XK_Alt_R:
                extended = true;
                keys.push_back(0x11);
                break;
             default:
               warn("Unknown extended key %#x\n", key);
               return;
            }

            if (extended) {
                if (down) {
                    keys.push_front(ExtendedKey);
                } else {
                    keys.push_front(BreakKey);
                    keys.push_front(ExtendedKey);
                }
            } else {
                if (!down)
                    keys.push_front(BreakKey);
            }
        } // upper keys
    } // extended keys
    return;
}

} /* namespace Ps2 */

<|endoftext|>


<commit_before>#include <iostream>
#include <cmath>
#include "integration_test_helper.h"
#include "dronecode_sdk.h"
#include "plugins/action/action.h"
#include "plugins/telemetry/telemetry.h"
#include "plugins/offboard/offboard.h"
#include "plugins/mission/mission.h"

using namespace dronecode_sdk;

TEST_F(SitlTest, OffboardPositionNED)
{
    DronecodeSDK dc;

    ConnectionResult ret = dc.add_udp_connection();
    ASSERT_EQ(ConnectionResult::SUCCESS, ret);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    ASSERT_TRUE(dc.system().has_autopilot());

    System &system = dc.system();
    auto telemetry = std::make_shared<Telemetry>(system);
    auto action = std::make_shared<Action>(system);
    auto offboard = std::make_shared<Offboard>(system);
    auto mission = std::make_shared<Mission>(system);

    while (!telemetry->health_all_ok()) {
        std::cout << "waiting for system to be ready" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    Action::Result action_ret = action->arm();
    ASSERT_EQ(Action::Result::SUCCESS, action_ret);

    action_ret = action->takeoff();
    ASSERT_EQ(Action::Result::SUCCESS, action_ret);

    std::this_thread::sleep_for(std::chrono::seconds(5));

    // Send it once before starting offboard, otherwise it will be rejected.
    offboard->set_position_ned({0.0f, 0.0f, -10.0f, 0.0f});

    Offboard::Result offboard_result = offboard->start();

    EXPECT_EQ(offboard_result, Offboard::Result::SUCCESS);

    offboard->set_position_ned({0.0f, 0.0f, -10.0f, 90.0f});
    std::this_thread::sleep_for(std::chrono::seconds(1));

    {
        const float radius = 10.0f;
        const float step = 0.01f;
        for (float i = 0.0f; i <= 2 * static_cast<float>(M_PI); i += step) {
            float x = radius * cosf(i);
            float y = radius * sinf(i);
            offboard->set_position_ned({x, y, -10.0f, 90.0f});
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
        offboard->set_position_ned({0.0f, 0.0f, -10.0f, 90.0f});
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }

    // Let's make sure that offboard knows it is active.
    EXPECT_TRUE(offboard->is_active());

    // Then randomly, we just interfere with a mission command to pause it.
    mission->pause_mission_async(nullptr);
    // This needs some time to propagate.
    std::this_thread::sleep_for(std::chrono::seconds(2));
    // Now it should be inactive.
    EXPECT_FALSE(offboard->is_active());

    // So we start it yet again.
    offboard_result = offboard->start();

    // It should complain because no setpoint is set.
    EXPECT_EQ(offboard_result, Offboard::Result::NO_SETPOINT_SET);

    // Alright, set one then.
    offboard->set_position_ned({0.0f, 0.0f, 0.0f, 270.0f});
    // And start again.
    offboard_result = offboard->start();
    // Now it should work.
    EXPECT_EQ(offboard_result, Offboard::Result::SUCCESS);
    EXPECT_TRUE(offboard->is_active());

    // Ok let's carry on.
    offboard->set_position_ned({10.0f, 0.0f, -10.0f, 270.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({0.0f, -10.0f, -20.0f, 180.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({-10.0f, 0.0f, -30.0f, 90.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({0.0f, 10.0f, -20.0f, 0.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({0.0f, 00.0f, -10.0f, 0.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard_result = offboard->stop();
    EXPECT_EQ(offboard_result, Offboard::Result::SUCCESS);

    action_ret = action->land();
    EXPECT_EQ(action_ret, Action::Result::SUCCESS);
}
<commit_msg>Offboard_position integration test for-loop variable changed<commit_after>#include <iostream>
#include <cmath>
#include "integration_test_helper.h"
#include "dronecode_sdk.h"
#include "plugins/action/action.h"
#include "plugins/telemetry/telemetry.h"
#include "plugins/offboard/offboard.h"
#include "plugins/mission/mission.h"

using namespace dronecode_sdk;

TEST_F(SitlTest, OffboardPositionNED)
{
    DronecodeSDK dc;

    ConnectionResult ret = dc.add_udp_connection();
    ASSERT_EQ(ConnectionResult::SUCCESS, ret);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    ASSERT_TRUE(dc.system().has_autopilot());

    System &system = dc.system();
    auto telemetry = std::make_shared<Telemetry>(system);
    auto action = std::make_shared<Action>(system);
    auto offboard = std::make_shared<Offboard>(system);
    auto mission = std::make_shared<Mission>(system);

    while (!telemetry->health_all_ok()) {
        std::cout << "waiting for system to be ready" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    Action::Result action_ret = action->arm();
    ASSERT_EQ(Action::Result::SUCCESS, action_ret);

    action_ret = action->takeoff();
    ASSERT_EQ(Action::Result::SUCCESS, action_ret);

    std::this_thread::sleep_for(std::chrono::seconds(5));

    // Send it once before starting offboard, otherwise it will be rejected.
    offboard->set_position_ned({0.0f, 0.0f, -10.0f, 0.0f});

    Offboard::Result offboard_result = offboard->start();

    EXPECT_EQ(offboard_result, Offboard::Result::SUCCESS);

    offboard->set_position_ned({0.0f, 0.0f, -10.0f, 90.0f});
    std::this_thread::sleep_for(std::chrono::seconds(1));

    {
        const float radius = 10.0f;
        const float step = 0.01f;
        for (float angle = 0.0f; angle <= 2.0f * M_PI_F; angle += step) {
            float x = radius * cosf(angle);
            float y = radius * sinf(angle);
            offboard->set_position_ned({x, y, -10.0f, 90.0f});
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
        offboard->set_position_ned({0.0f, 0.0f, -10.0f, 90.0f});
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }

    // Let's make sure that offboard knows it is active.
    EXPECT_TRUE(offboard->is_active());

    // Then randomly, we just interfere with a mission command to pause it.
    mission->pause_mission_async(nullptr);
    // This needs some time to propagate.
    std::this_thread::sleep_for(std::chrono::seconds(2));
    // Now it should be inactive.
    EXPECT_FALSE(offboard->is_active());

    // So we start it yet again.
    offboard_result = offboard->start();

    // It should complain because no setpoint is set.
    EXPECT_EQ(offboard_result, Offboard::Result::NO_SETPOINT_SET);

    // Alright, set one then.
    offboard->set_position_ned({0.0f, 0.0f, 0.0f, 270.0f});
    // And start again.
    offboard_result = offboard->start();
    // Now it should work.
    EXPECT_EQ(offboard_result, Offboard::Result::SUCCESS);
    EXPECT_TRUE(offboard->is_active());

    // Ok let's carry on.
    offboard->set_position_ned({10.0f, 0.0f, -10.0f, 270.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({0.0f, -10.0f, -20.0f, 180.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({-10.0f, 0.0f, -30.0f, 90.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({0.0f, 10.0f, -20.0f, 0.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard->set_position_ned({0.0f, 00.0f, -10.0f, 0.0f});
    std::this_thread::sleep_for(std::chrono::seconds(5));

    offboard_result = offboard->stop();
    EXPECT_EQ(offboard_result, Offboard::Result::SUCCESS);

    action_ret = action->land();
    EXPECT_EQ(action_ret, Action::Result::SUCCESS);
}
<|endoftext|>


<commit_before><commit_msg>Add the offset to the sequence copied by the TBranchElement even for base classes. This fixes: https://savannah.cern.ch/bugs/index.php?79235<commit_after><|endoftext|>


<commit_before>// Copyright 2014 BVLC and contributors.

#include <google/protobuf/text_format.h>
#include <leveldb/db.h>
#include <sstream>
#include <string>

#include "gtest/gtest.h"
#include "caffe/common.hpp"
#include "caffe/net.hpp"
#include "caffe/test/test_gradient_check_util.hpp"

#include "caffe/test/test_caffe_main.hpp"

namespace caffe {

template <typename Dtype>
class NetTest : public ::testing::Test {
 protected:
  NetTest() : filename(NULL) {
  }

  virtual void SetUp() {  // Create the leveldb
    filename = tmpnam(NULL);  // get temp name
    LOG(INFO) << "Using temporary leveldb " << filename;
    leveldb::DB* db;
    leveldb::Options options;
    options.error_if_exists = true;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, filename, &db);
    CHECK(status.ok());
    for (int i = 0; i < 5; ++i) {
      Datum datum;
      datum.set_label(i);
      datum.set_channels(2);
      datum.set_height(3);
      datum.set_width(4);
      std::string* data = datum.mutable_data();
      for (int j = 0; j < 24; ++j) {
        data->push_back((uint8_t)i);
      }
      std::stringstream ss;
      ss << i;
      db->Put(leveldb::WriteOptions(), ss.str(), datum.SerializeAsString());
    }
    delete db;

    const string& proto_prefix =
        "name: 'TestNetwork' "
        "layers: { "
        "  layer { "
        "    name: 'data' "
        "    type: 'data' ";
    const string& proto_suffix =
        "    batchsize: 1 "
        "  } "
        "  top: 'data' "
        "  top: 'label' "
        "} "
        "layers: { "
        "  layer { "
        "    name: 'innerproduct' "
        "    type: 'innerproduct' "
        "    num_output: 1000 "
        "    weight_filler { "
        "      type: 'gaussian' "
        "      std: 0.01 "
        "    } "
        "    bias_filler { "
        "      type: 'constant' "
        "      value: 0 "
        "    } "
        "    blobs_lr: 1. "
        "    blobs_lr: 2. "
        "    weight_decay: 1. "
        "    weight_decay: 0. "
        "  } "
        "  bottom: 'data' "
        "  top: 'innerproduct' "
        "} "
        "layers: { "
        "  layer { "
        "    name: 'loss' "
        "    type: 'softmax_loss' "
        "  } "
        "  bottom: 'innerproduct' "
        "  bottom: 'label' "
        "} ";
    proto = proto_prefix + "source: '" + string(this->filename) +
        "' " + proto_suffix;
  }

  virtual ~NetTest() {
  }

  char* filename;
  string proto;
};

typedef ::testing::Types<float, double> Dtypes;
TYPED_TEST_CASE(NetTest, Dtypes);

TYPED_TEST(NetTest, TestHasBlob) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto,
                                                      &param));
  Net<TypeParam> net(param);
  EXPECT_TRUE(net.has_blob("data"));
  EXPECT_TRUE(net.has_blob("label"));
  EXPECT_TRUE(net.has_blob("innerproduct"));
  EXPECT_FALSE(net.has_blob("loss"));
}

TYPED_TEST(NetTest, TestGetBlob) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto,
                                                      &param));
  Net<TypeParam> net(param);
  EXPECT_EQ(net.blob_by_name("data"), net.blobs()[0]);
  EXPECT_EQ(net.blob_by_name("label"), net.blobs()[1]);
  EXPECT_EQ(net.blob_by_name("innerproduct"), net.blobs()[2]);
  EXPECT_FALSE(net.blob_by_name("loss"));
}

TYPED_TEST(NetTest, TestHasLayer) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto,
                                                      &param));
  Net<TypeParam> net(param);
  EXPECT_TRUE(net.has_layer("data"));
  EXPECT_TRUE(net.has_layer("innerproduct"));
  EXPECT_TRUE(net.has_layer("loss"));
  EXPECT_FALSE(net.has_layer("label"));
}

TYPED_TEST(NetTest, TestGetLayerByName) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto,
                                                      &param));
  Net<TypeParam> net(param);
  EXPECT_EQ(net.layer_by_name("data"), net.layers()[0]);
  EXPECT_EQ(net.layer_by_name("innerproduct"), net.layers()[1]);
  EXPECT_EQ(net.layer_by_name("loss"), net.layers()[2]);
  EXPECT_FALSE(net.layer_by_name("label"));
}

}  // namespace caffe
<commit_msg>fix test_net for refactor<commit_after>// Copyright 2014 BVLC and contributors.

#include <google/protobuf/text_format.h>
#include <leveldb/db.h>
#include <sstream>
#include <string>

#include "gtest/gtest.h"
#include "caffe/common.hpp"
#include "caffe/net.hpp"
#include "caffe/test/test_gradient_check_util.hpp"

#include "caffe/test/test_caffe_main.hpp"

namespace caffe {

template <typename Dtype>
class NetTest : public ::testing::Test {
 protected:
  virtual void SetUp() {  // Create the leveldb
    filename_ = tmpnam(NULL);  // get temp name
    LOG(INFO) << "Using temporary leveldb " << filename_;
    leveldb::DB* db;
    leveldb::Options options;
    options.error_if_exists = true;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, filename_, &db);
    CHECK(status.ok());
    for (int i = 0; i < 5; ++i) {
      Datum datum;
      datum.set_label(i);
      datum.set_channels(2);
      datum.set_height(3);
      datum.set_width(4);
      std::string* data = datum.mutable_data();
      for (int j = 0; j < 24; ++j) {
        data->push_back((uint8_t)i);
      }
      std::stringstream ss;
      ss << i;
      db->Put(leveldb::WriteOptions(), ss.str(), datum.SerializeAsString());
    }
    delete db;

    const string& proto_prefix =
        "name: 'TestNetwork' "
        "layers: { "
        "  name: 'data' "
        "  type: DATA "
        "  data_param { ";
    const string& proto_suffix =
        "    batch_size: 1 "
        "  } "
        "  top: 'data' "
        "  top: 'label' "
        "} "
        "layers: { "
        "  name: 'innerproduct' "
        "  type: INNER_PRODUCT "
        "  inner_product_param { "
        "    num_output: 1000 "
        "    weight_filler { "
        "      type: 'gaussian' "
        "      std: 0.01 "
        "    } "
        "    bias_filler { "
        "      type: 'constant' "
        "      value: 0 "
        "    } "
        "  } "
        "  blobs_lr: 1. "
        "  blobs_lr: 2. "
        "  weight_decay: 1. "
        "  weight_decay: 0. "
        "  bottom: 'data' "
        "  top: 'innerproduct' "
        "} "
        "layers: { "
        "  name: 'loss' "
        "  type: SOFTMAX_LOSS "
        "  bottom: 'innerproduct' "
        "  bottom: 'label' "
        "} ";
    proto_ = proto_prefix + "source: '" + string(this->filename_) +
        "' " + proto_suffix;
  }

  char* filename_;
  string proto_;
};

typedef ::testing::Types<float, double> Dtypes;
TYPED_TEST_CASE(NetTest, Dtypes);

TYPED_TEST(NetTest, TestHasBlob) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto_, &param));
  Net<TypeParam> net(param);
  EXPECT_TRUE(net.has_blob("data"));
  EXPECT_TRUE(net.has_blob("label"));
  EXPECT_TRUE(net.has_blob("innerproduct"));
  EXPECT_FALSE(net.has_blob("loss"));
}

TYPED_TEST(NetTest, TestGetBlob) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto_, &param));
  Net<TypeParam> net(param);
  EXPECT_EQ(net.blob_by_name("data"), net.blobs()[0]);
  EXPECT_EQ(net.blob_by_name("label"), net.blobs()[1]);
  EXPECT_EQ(net.blob_by_name("innerproduct"), net.blobs()[2]);
  EXPECT_FALSE(net.blob_by_name("loss"));
}

TYPED_TEST(NetTest, TestHasLayer) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto_, &param));
  Net<TypeParam> net(param);
  EXPECT_TRUE(net.has_layer("data"));
  EXPECT_TRUE(net.has_layer("innerproduct"));
  EXPECT_TRUE(net.has_layer("loss"));
  EXPECT_FALSE(net.has_layer("label"));
}

TYPED_TEST(NetTest, TestGetLayerByName) {
  NetParameter param;
  CHECK(google::protobuf::TextFormat::ParseFromString(this->proto_, &param));
  Net<TypeParam> net(param);
  EXPECT_EQ(net.layer_by_name("data"), net.layers()[0]);
  EXPECT_EQ(net.layer_by_name("innerproduct"), net.layers()[1]);
  EXPECT_EQ(net.layer_by_name("loss"), net.layers()[2]);
  EXPECT_FALSE(net.layer_by_name("label"));
}

}  // namespace caffe
<|endoftext|>


<commit_before>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2006/03/23
// Author: Sriram Rao
//
// Copyright 2008 Quantcast Corp.
// Copyright 2006-2008 Kosmix Corp.
//
// This file is part of Kosmos File System (KFS).
//
// 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.
//
//
//----------------------------------------------------------------------------

// order of #includes here is critical.  don't change it
#include "libkfsIO/Counter.h"
#include "libkfsIO/Globals.h"

#include "ChunkServer.h"
#include "Utils.h"

#include <netdb.h>
#include <arpa/inet.h>

using std::list;
using std::string;

using namespace KFS;
using namespace KFS::libkfsio;

// single network thread that manages connections and net I/O
static MetaThread netProcessor;

ChunkServer KFS::gChunkServer;

static void *
netWorker(void *dummy)
{
    globals().netManager.MainLoop();
    return NULL;
}

static void
StartNetProcessor()
{
    netProcessor.start(netWorker, NULL);
}

void
ChunkServer::Init()
{
    InitParseHandlers();
    // Register the counters
    RegisterCounters();

    // setup the telemetry stuff...
    struct ip_mreq imreq;
    string srvIp = "10.2.0.10";
    int srvPort = 12000;
    int multicastPort = 13000;

    imreq.imr_multiaddr.s_addr = inet_addr("226.0.0.1");
    imreq.imr_interface.s_addr = INADDR_ANY; // use DEFAULT interface

    mTelemetryReporter.Init(imreq, multicastPort, srvIp, srvPort);
}

void
ChunkServer::SendTelemetryReport(KfsOp_t op, double timeSpent)
{
    if (op != CMD_WRITE)
        return;
    mTelemetryReporter.publish(timeSpent, "WRITE");
}

void
ChunkServer::MainLoop(int clientAcceptPort)
{
#if !defined (__sun__)
    static const int MAXHOSTNAMELEN = 256;
#endif
    char hostname[MAXHOSTNAMELEN];

    mClientAcceptPort = clientAcceptPort;
    if (gethostname(hostname, MAXHOSTNAMELEN)) {
        perror("gethostname: ");
        exit(-1);
    }

    // convert to IP address
    struct hostent *hent = gethostbyname(hostname);
    in_addr ipaddr;

    memcpy(&ipaddr, hent->h_addr, hent->h_length);

    mLocation.Reset(inet_ntoa(ipaddr), clientAcceptPort);

    gClientManager.StartAcceptor(clientAcceptPort);
    gLogger.Start();
    gChunkManager.Start();
    // gMetaServerSM.SendHello(clientAcceptPort);
    gMetaServerSM.Init(clientAcceptPort);

    // gChunkManager.DumpChunkMap();
    StartNetProcessor();

    netProcessor.join();

}

void
KFS::verifyExecutingOnNetProcessor()
{
    assert(netProcessor.isEqual(pthread_self()));
    if (!netProcessor.isEqual(pthread_self())) {
        die("FATAL: Not executing on net processor");
    }
}

void
KFS::StopNetProcessor(int status)
{
    netProcessor.exit(status);
}

RemoteSyncSMPtr ChunkServer::FindServer(const ServerLocation &location, bool connect)
{
    return KFS::FindServer(mRemoteSyncers, location, connect);
}

void ChunkServer::RemoveServer(RemoteSyncSM *target)
{
    KFS::RemoveServer(mRemoteSyncers, target);
}
<commit_msg> -- Committing the fix for issue 2473767.  Fix submitted by Eric Holmberg.  Print out a log message if there are multiple chunkservers on 127.x.x.x<commit_after>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2006/03/23
// Author: Sriram Rao
//
// Copyright 2008 Quantcast Corp.
// Copyright 2006-2008 Kosmix Corp.
//
// This file is part of Kosmos File System (KFS).
//
// 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.
//
//
//----------------------------------------------------------------------------

// order of #includes here is critical.  don't change it
#include "libkfsIO/Counter.h"
#include "libkfsIO/Globals.h"

#include "ChunkServer.h"
#include "Utils.h"

#include <netdb.h>
#include <arpa/inet.h>

using std::list;
using std::string;

using namespace KFS;
using namespace KFS::libkfsio;

// single network thread that manages connections and net I/O
static MetaThread netProcessor;

ChunkServer KFS::gChunkServer;

static void *
netWorker(void *dummy)
{
    globals().netManager.MainLoop();
    return NULL;
}

static void
StartNetProcessor()
{
    netProcessor.start(netWorker, NULL);
}

void
ChunkServer::Init()
{
    InitParseHandlers();
    // Register the counters
    RegisterCounters();

    // setup the telemetry stuff...
    struct ip_mreq imreq;
    string srvIp = "10.2.0.10";
    int srvPort = 12000;
    int multicastPort = 13000;

    imreq.imr_multiaddr.s_addr = inet_addr("226.0.0.1");
    imreq.imr_interface.s_addr = INADDR_ANY; // use DEFAULT interface

    mTelemetryReporter.Init(imreq, multicastPort, srvIp, srvPort);
}

void
ChunkServer::SendTelemetryReport(KfsOp_t op, double timeSpent)
{
    if (op != CMD_WRITE)
        return;
    mTelemetryReporter.publish(timeSpent, "WRITE");
}

void
ChunkServer::MainLoop(int clientAcceptPort)
{
#if !defined (__sun__)
    static const int MAXHOSTNAMELEN = 256;
#endif
    char hostname[MAXHOSTNAMELEN];

    mClientAcceptPort = clientAcceptPort;
    if (gethostname(hostname, MAXHOSTNAMELEN)) {
        perror("gethostname: ");
        exit(-1);
    }

    // convert to IP address
    struct hostent *hent = gethostbyname(hostname);
    in_addr ipaddr;

    memcpy(&ipaddr, hent->h_addr, hent->h_length);

    // Warn user if resolved address is the local loopback address which may
    // cause duplicate chunk server issues.
    if ( (ipaddr.s_addr >> 0 & 0xFF) == 127) {
        KFS_LOG_VA_INFO("gethostname returned: %s", hostname);
        KFS_LOG_VA_INFO("hostname resolved to: %s", inet_ntoa(ipaddr));
        KFS_LOG_WARN("WARNING: IP resolved to 127.x.x.x address - "
                     "check 'hosts' line in /etc/nsswitch.conf to make sure that 'dns'"
                     " is before 'files'");
    }

    mLocation.Reset(inet_ntoa(ipaddr), clientAcceptPort);

    gClientManager.StartAcceptor(clientAcceptPort);
    gLogger.Start();
    gChunkManager.Start();
    // gMetaServerSM.SendHello(clientAcceptPort);
    gMetaServerSM.Init(clientAcceptPort);

    // gChunkManager.DumpChunkMap();
    StartNetProcessor();

    netProcessor.join();

}

void
KFS::verifyExecutingOnNetProcessor()
{
    assert(netProcessor.isEqual(pthread_self()));
    if (!netProcessor.isEqual(pthread_self())) {
        die("FATAL: Not executing on net processor");
    }
}

void
KFS::StopNetProcessor(int status)
{
    netProcessor.exit(status);
}

RemoteSyncSMPtr ChunkServer::FindServer(const ServerLocation &location, bool connect)
{
    return KFS::FindServer(mRemoteSyncers, location, connect);
}

void ChunkServer::RemoveServer(RemoteSyncSM *target)
{
    KFS::RemoveServer(mRemoteSyncers, target);
}
<|endoftext|>


<commit_before>/*
 * Convert an input and an output pipe to a duplex socket.
 *
 * This code is used in the test cases to convert stdin/stdout to a
 * single socket.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "duplex.hxx"
#include "system/fd-util.h"
#include "system/fd_util.h"
#include "event/event2.h"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "buffered_io.hxx"
#include "pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"

#include <inline/compiler.h>
#include <daemon/log.h>

#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

class FallbackEvent : public Event {
public:
    void Add() {
        if (!Event::Add())
            MakeActive(EV_TIMEOUT);
    }
};

struct Duplex {
    int read_fd;
    int write_fd;
    int sock_fd;
    bool sock_eof = false;

    SliceFifoBuffer from_read, to_write;

    FallbackEvent read_event, write_event;
    struct event2 sock_event;

    Duplex(int _read_fd, int _write_fd, int _sock_fd)
        :read_fd(_read_fd), write_fd(_write_fd), sock_fd(_sock_fd) {
        from_read.Allocate(fb_pool_get());
        to_write.Allocate(fb_pool_get());

        /* if read_fd is a regular file, read repeatedly using
           EV_TIMEOUT with a zero timeout, because we can't use
           EV_READ on regular files */
        read_event.Set(read_fd, EV_READ,
                       MakeSimpleEventCallback(Duplex, ReadEventCallback),
                       this);
        read_event.Add();

        write_event.Set(write_fd, EV_WRITE,
                        MakeSimpleEventCallback(Duplex, WriteEventCallback),
                        this);
        write_event.Add();

        event2_init(&sock_event, sock_fd,
                    MakeEventCallback(Duplex, SocketEventCallback), this,
                    nullptr);
        event2_persist(&sock_event);
        event2_set(&sock_event, EV_READ);
    }

    void CloseRead() {
        assert(read_fd >= 0);

        read_event.Delete();

        if (read_fd > 2)
            close(read_fd);

        read_fd = -1;
    }

    void CloseWrite() {
        assert(write_fd >= 0);

        write_event.Delete();

        if (write_fd > 2)
            close(write_fd);

        write_fd = -1;
    }

    void CloseSocket() {
        assert(sock_fd >= 0);

        event2_set(&sock_event, 0);
        event2_commit(&sock_event);

        close(sock_fd);
        sock_fd = -1;
    }

    void Destroy();
    bool CheckDestroy();

    void ReadEventCallback();
    void WriteEventCallback();
    void SocketEventCallback(evutil_socket_t fd, short events);
};

void
Duplex::Destroy()
{
    if (read_fd >= 0)
        CloseRead();

    if (write_fd >= 0)
        CloseWrite();

    if (sock_fd >= 0)
        CloseSocket();

    from_read.Free(fb_pool_get());
    to_write.Free(fb_pool_get());
}

bool
Duplex::CheckDestroy()
{
    if (read_fd < 0 && sock_eof && from_read.IsEmpty() && to_write.IsEmpty()) {
        Destroy();
        return true;
    } else
        return false;
}

inline void
Duplex::ReadEventCallback()
{
    ssize_t nbytes = read_to_buffer(read_fd, from_read, INT_MAX);
    if (nbytes == -1) {
        daemon_log(1, "failed to read: %s\n", strerror(errno));
        Destroy();
        return;
    }

    if (nbytes == 0) {
        CloseRead();
        CheckDestroy();
        return;
    }

    event2_or(&sock_event, EV_WRITE);

    if (!from_read.IsFull())
        read_event.Add();
}

inline void
Duplex::WriteEventCallback()
{
    ssize_t nbytes = write_from_buffer(write_fd, to_write);
    if (nbytes == -1) {
        Destroy();
        return;
    }

    if (nbytes > 0 && !sock_eof)
        event2_or(&sock_event, EV_READ);

    if (!to_write.IsEmpty())
        write_event.Add();
    else
        CheckDestroy();
}

inline void
Duplex::SocketEventCallback(evutil_socket_t fd, short events)
{
    event2_lock(&sock_event);
    event2_occurred_persist(&sock_event, events);

    if ((events & EV_READ) != 0) {
        ssize_t nbytes = recv_to_buffer(fd, to_write, INT_MAX);
        if (nbytes == -1) {
            daemon_log(1, "failed to read: %s\n", strerror(errno));
            Destroy();
            return;
        }

        if (likely(nbytes > 0)) {
            write_event.Add();
            if (!to_write.IsFull())
                event2_or(&sock_event, EV_READ);
        } else {
            sock_eof = true;
            if (CheckDestroy())
                return;
        }
    }

    if ((events & EV_WRITE) != 0) {
        ssize_t nbytes = send_from_buffer(fd, from_read);
        if (nbytes == -1) {
            Destroy();
            return;
        }

        if (nbytes > 0 && read_fd >= 0)
            read_event.Add();

        if (!from_read.IsEmpty())
            event2_or(&sock_event, EV_WRITE);
    }

    event2_unlock(&sock_event);
}

int
duplex_new(struct pool *pool, int read_fd, int write_fd)
{
    assert(pool != nullptr);
    assert(read_fd >= 0);
    assert(write_fd >= 0);

    int fds[2];
    if (socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
        return -1;

    if (fd_set_nonblock(fds[1], 1) < 0) {
        int save_errno = errno;
        close(fds[0]);
        close(fds[1]);
        errno = save_errno;
        return -1;
    }

    NewFromPool<Duplex>(*pool, read_fd, write_fd, fds[0]);
    return fds[1];
}
<commit_msg>duplex: don't schedule write_event in constructor<commit_after>/*
 * Convert an input and an output pipe to a duplex socket.
 *
 * This code is used in the test cases to convert stdin/stdout to a
 * single socket.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "duplex.hxx"
#include "system/fd-util.h"
#include "system/fd_util.h"
#include "event/event2.h"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "buffered_io.hxx"
#include "pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"

#include <inline/compiler.h>
#include <daemon/log.h>

#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

class FallbackEvent : public Event {
public:
    void Add() {
        if (!Event::Add())
            MakeActive(EV_TIMEOUT);
    }
};

struct Duplex {
    int read_fd;
    int write_fd;
    int sock_fd;
    bool sock_eof = false;

    SliceFifoBuffer from_read, to_write;

    FallbackEvent read_event, write_event;
    struct event2 sock_event;

    Duplex(int _read_fd, int _write_fd, int _sock_fd)
        :read_fd(_read_fd), write_fd(_write_fd), sock_fd(_sock_fd) {
        from_read.Allocate(fb_pool_get());
        to_write.Allocate(fb_pool_get());

        /* if read_fd is a regular file, read repeatedly using
           EV_TIMEOUT with a zero timeout, because we can't use
           EV_READ on regular files */
        read_event.Set(read_fd, EV_READ,
                       MakeSimpleEventCallback(Duplex, ReadEventCallback),
                       this);
        read_event.Add();

        write_event.Set(write_fd, EV_WRITE,
                        MakeSimpleEventCallback(Duplex, WriteEventCallback),
                        this);

        event2_init(&sock_event, sock_fd,
                    MakeEventCallback(Duplex, SocketEventCallback), this,
                    nullptr);
        event2_persist(&sock_event);
        event2_set(&sock_event, EV_READ);
    }

    void CloseRead() {
        assert(read_fd >= 0);

        read_event.Delete();

        if (read_fd > 2)
            close(read_fd);

        read_fd = -1;
    }

    void CloseWrite() {
        assert(write_fd >= 0);

        write_event.Delete();

        if (write_fd > 2)
            close(write_fd);

        write_fd = -1;
    }

    void CloseSocket() {
        assert(sock_fd >= 0);

        event2_set(&sock_event, 0);
        event2_commit(&sock_event);

        close(sock_fd);
        sock_fd = -1;
    }

    void Destroy();
    bool CheckDestroy();

    void ReadEventCallback();
    void WriteEventCallback();
    void SocketEventCallback(evutil_socket_t fd, short events);
};

void
Duplex::Destroy()
{
    if (read_fd >= 0)
        CloseRead();

    if (write_fd >= 0)
        CloseWrite();

    if (sock_fd >= 0)
        CloseSocket();

    from_read.Free(fb_pool_get());
    to_write.Free(fb_pool_get());
}

bool
Duplex::CheckDestroy()
{
    if (read_fd < 0 && sock_eof && from_read.IsEmpty() && to_write.IsEmpty()) {
        Destroy();
        return true;
    } else
        return false;
}

inline void
Duplex::ReadEventCallback()
{
    ssize_t nbytes = read_to_buffer(read_fd, from_read, INT_MAX);
    if (nbytes == -1) {
        daemon_log(1, "failed to read: %s\n", strerror(errno));
        Destroy();
        return;
    }

    if (nbytes == 0) {
        CloseRead();
        CheckDestroy();
        return;
    }

    event2_or(&sock_event, EV_WRITE);

    if (!from_read.IsFull())
        read_event.Add();
}

inline void
Duplex::WriteEventCallback()
{
    ssize_t nbytes = write_from_buffer(write_fd, to_write);
    if (nbytes == -1) {
        Destroy();
        return;
    }

    if (nbytes > 0 && !sock_eof)
        event2_or(&sock_event, EV_READ);

    if (!to_write.IsEmpty())
        write_event.Add();
    else
        CheckDestroy();
}

inline void
Duplex::SocketEventCallback(evutil_socket_t fd, short events)
{
    event2_lock(&sock_event);
    event2_occurred_persist(&sock_event, events);

    if ((events & EV_READ) != 0) {
        ssize_t nbytes = recv_to_buffer(fd, to_write, INT_MAX);
        if (nbytes == -1) {
            daemon_log(1, "failed to read: %s\n", strerror(errno));
            Destroy();
            return;
        }

        if (likely(nbytes > 0)) {
            write_event.Add();
            if (!to_write.IsFull())
                event2_or(&sock_event, EV_READ);
        } else {
            sock_eof = true;
            if (CheckDestroy())
                return;
        }
    }

    if ((events & EV_WRITE) != 0) {
        ssize_t nbytes = send_from_buffer(fd, from_read);
        if (nbytes == -1) {
            Destroy();
            return;
        }

        if (nbytes > 0 && read_fd >= 0)
            read_event.Add();

        if (!from_read.IsEmpty())
            event2_or(&sock_event, EV_WRITE);
    }

    event2_unlock(&sock_event);
}

int
duplex_new(struct pool *pool, int read_fd, int write_fd)
{
    assert(pool != nullptr);
    assert(read_fd >= 0);
    assert(write_fd >= 0);

    int fds[2];
    if (socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
        return -1;

    if (fd_set_nonblock(fds[1], 1) < 0) {
        int save_errno = errno;
        close(fds[0]);
        close(fds[1]);
        errno = save_errno;
        return -1;
    }

    NewFromPool<Duplex>(*pool, read_fd, write_fd, fds[0]);
    return fds[1];
}
<|endoftext|>