<commit_before>/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006-2009 Robert Beckebans <trebor_7@users.sourceforge.net>

This file is part of Daemon source code.

Daemon source code 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.

Daemon source code 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 Daemon source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_image.c
#include "tr_local.h"

// Work around bug in old versions of libpng
#define PNG_SKIP_SETJMP_CHECK
#include <png.h>

/*
=========================================================

PNG LOADING

=========================================================
*/
static void png_read_data( png_structp png, png_bytep data, png_size_t length )
{
	byte *io_ptr = (byte*) png_get_io_ptr( png );
	Com_Memcpy( data, io_ptr, length );
	png_init_io( png, ( png_FILE_p )( io_ptr + length ) );
}

static void png_user_warning_fn( png_structp png_ptr, png_const_charp warning_message )
{
	ri.Printf( PRINT_WARNING, "libpng warning: %s\n", warning_message );
}

static void NORETURN png_user_error_fn( png_structp png_ptr, png_const_charp error_message )
{
	ri.Printf( PRINT_ERROR, "libpng error: %s\n", error_message );
	longjmp( png_jmpbuf( png_ptr ), 0 );
}

void LoadPNG( const char *name, byte **pic, int *width, int *height,
	      int *numLayers, int *numMips, int *bits, byte alphaByte )
{
	int          bit_depth;
	int          color_type;
	png_uint_32  w;
	png_uint_32  h;
	unsigned int row;
	png_infop    info;
	png_structp  png;
	png_bytep    *row_pointers;
	byte         *data;
	byte         *out;

	// load png
	ri.FS_ReadFile( name, ( void ** ) &data );

	if ( !data )
	{
		return;
	}

	png = png_create_read_struct( PNG_LIBPNG_VER_STRING, ( png_voidp ) NULL, png_user_error_fn, png_user_warning_fn );

	if ( !png )
	{
		ri.Printf( PRINT_WARNING, "LoadPNG: png_create_write_struct() failed for (%s)\n", name );
		ri.FS_FreeFile( data );
		return;
	}

	// allocate/initialize the memory for image information.  REQUIRED
	info = png_create_info_struct( png );

	if ( !info )
	{
		ri.Printf( PRINT_WARNING, "LoadPNG: png_create_info_struct() failed for (%s)\n", name );
		ri.FS_FreeFile( data );
		png_destroy_read_struct( &png, ( png_infopp ) NULL, ( png_infopp ) NULL );
		return;
	}

	/*
	 * Set error handling if you are using the setjmp/longjmp method (this is
	 * the common method of doing things with libpng).  REQUIRED unless you
	 * set up your own error handlers in the png_create_read_struct() earlier.
	 */
	if ( setjmp( png_jmpbuf( png ) ) )
	{
		// if we get here, we had a problem reading the file
		ri.Printf( PRINT_WARNING, "LoadPNG: first exception handler called for (%s)\n", name );
		ri.FS_FreeFile( data );
		png_destroy_read_struct( &png, ( png_infopp ) & info, ( png_infopp ) NULL );
		return;
	}

	png_set_read_fn( png, data, png_read_data );

	png_set_sig_bytes( png, 0 );

	// The call to png_read_info() gives us all of the information from the
	// PNG file before the first IDAT (image data chunk).  REQUIRED
	png_read_info( png, info );

	// get picture info
	png_get_IHDR( png, info, ( png_uint_32 * ) &w, ( png_uint_32 * ) &h, &bit_depth, &color_type, NULL, NULL, NULL );

	// tell libpng to strip 16 bit/color files down to 8 bits/color
	png_set_strip_16( png );

	// expand paletted images to RGB triplets
	if ( color_type & PNG_COLOR_MASK_PALETTE )
	{
		png_set_expand( png );
	}

	// expand gray-scaled images to RGB triplets
	if ( !( color_type & PNG_COLOR_MASK_COLOR ) )
	{
		png_set_gray_to_rgb( png );
	}

	// expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel

	// expand paletted or RGB images with transparency to full alpha channels
	// so the data will be available as RGBA quartets
	if ( png_get_valid( png, info, PNG_INFO_tRNS ) )
	{
		png_set_tRNS_to_alpha( png );
	}

	// if there is no alpha information, fill with alphaByte
	if ( !( color_type & PNG_COLOR_MASK_ALPHA ) )
	{
		png_set_filler( png, alphaByte, PNG_FILLER_AFTER );
	}

	// expand pictures with less than 8bpp to 8bpp
	if ( bit_depth < 8 )
	{
		png_set_packing( png );
	}

	// update structure with the above settings
	png_read_update_info( png, info );

	// allocate the memory to hold the image
	*width = w;
	*height = h;
	*pic = out = ( byte * ) ri.Z_Malloc( w * h * 4 );

	row_pointers = ( png_bytep * ) ri.Hunk_AllocateTempMemory( sizeof( png_bytep ) * h );

	// set a new exception handler
	if ( setjmp( png_jmpbuf( png ) ) )
	{
		ri.Printf( PRINT_WARNING, "LoadPNG: second exception handler called for (%s)\n", name );
		ri.Hunk_FreeTempMemory( row_pointers );
		ri.FS_FreeFile( data );
		png_destroy_read_struct( &png, ( png_infopp ) & info, ( png_infopp ) NULL );
		return;
	}

	for ( row = 0; row < h; row++ )
	{
		row_pointers[ row ] = ( png_bytep )( out + ( row * 4 * w ) );
	}

	// read image data
	png_read_image( png, row_pointers );

	// read rest of file, and get additional chunks in info
	png_read_end( png, info );

	// clean up after the read, and free any memory allocated
	png_destroy_read_struct( &png, &info, ( png_infopp ) NULL );

	ri.Hunk_FreeTempMemory( row_pointers );
	ri.FS_FreeFile( data );
}

/*
=========================================================

PNG SAVING

=========================================================
*/
static int png_compressed_size;

static void png_write_data( png_structp png, png_bytep data, png_size_t length )
{
	byte *io_ptr = (byte*) png_get_io_ptr( png );
	Com_Memcpy( io_ptr, data, length );
	png_init_io( png, ( png_FILE_p )( io_ptr + length ) );
	png_compressed_size += length;
}

static void png_flush_data( png_structp png )
{
}

void SavePNG( const char *name, const byte *pic, int width, int height, int numBytes, qboolean flip )
{
	png_structp png;
	png_infop   info;
	int         i;
	int         row_stride;
	byte        *buffer;
	byte        *row;
	png_bytep   *row_pointers;

	png = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );

	if ( !png )
	{
		return;
	}

	// Allocate/initialize the image information data
	info = png_create_info_struct( png );

	if ( !info )
	{
		png_destroy_write_struct( &png, ( png_infopp ) NULL );
		return;
	}

	png_compressed_size = 0;
	buffer = (byte*) ri.Hunk_AllocateTempMemory( width * height * numBytes );

	// set error handling
	if ( setjmp( png_jmpbuf( png ) ) )
	{
		ri.Hunk_FreeTempMemory( buffer );
		png_destroy_write_struct( &png, &info );
		return;
	}

	png_set_write_fn( png, buffer, png_write_data, png_flush_data );

	switch ( numBytes )
	{
	default:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	case 3:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	case 2:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_GA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	case 1:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	}

	// write the file header information
	png_write_info( png, info );

	row_pointers = (png_bytep*) ri.Hunk_AllocateTempMemory( height * sizeof( png_bytep ) );

	if ( setjmp( png_jmpbuf( png ) ) )
	{
		ri.Hunk_FreeTempMemory( row_pointers );
		ri.Hunk_FreeTempMemory( buffer );
		png_destroy_write_struct( &png, &info );
		return;
	}

	row_stride = width * numBytes;
	row = ( byte * ) pic + ( height - 1 ) * row_stride;

	if ( flip )
	{
		for ( i = height - 1; i >= 0; i-- )
		{
			row_pointers[ i ] = row;
			row -= row_stride;
		}
	}
	else
	{
		for ( i = 0; i < height; i++ )
		{
			row_pointers[ i ] = row;
			row -= row_stride;
		}
	}

	png_write_image( png, row_pointers );
	png_write_end( png, info );

	// clean up after the write, and free any memory allocated
	png_destroy_write_struct( &png, &info );

	ri.Hunk_FreeTempMemory( row_pointers );

	ri.FS_WriteFile( name, buffer, png_compressed_size );

	ri.Hunk_FreeTempMemory( buffer );
}
<commit_msg>Fix png interlace handling warning<commit_after>/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006-2009 Robert Beckebans <trebor_7@users.sourceforge.net>

This file is part of Daemon source code.

Daemon source code 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.

Daemon source code 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 Daemon source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_image.c
#include "tr_local.h"

// Work around bug in old versions of libpng
#define PNG_SKIP_SETJMP_CHECK
#include <png.h>

/*
=========================================================

PNG LOADING

=========================================================
*/
static void png_read_data( png_structp png, png_bytep data, png_size_t length )
{
	byte *io_ptr = (byte*) png_get_io_ptr( png );
	Com_Memcpy( data, io_ptr, length );
	png_init_io( png, ( png_FILE_p )( io_ptr + length ) );
}

static void png_user_warning_fn( png_structp png_ptr, png_const_charp warning_message )
{
	ri.Printf( PRINT_WARNING, "libpng warning: %s\n", warning_message );
}

static void NORETURN png_user_error_fn( png_structp png_ptr, png_const_charp error_message )
{
	ri.Printf( PRINT_ERROR, "libpng error: %s\n", error_message );
	longjmp( png_jmpbuf( png_ptr ), 0 );
}

void LoadPNG( const char *name, byte **pic, int *width, int *height,
	      int *numLayers, int *numMips, int *bits, byte alphaByte )
{
	int          bit_depth;
	int          color_type;
	png_uint_32  w;
	png_uint_32  h;
	unsigned int row;
	png_infop    info;
	png_structp  png;
	png_bytep    *row_pointers;
	byte         *data;
	byte         *out;

	// load png
	ri.FS_ReadFile( name, ( void ** ) &data );

	if ( !data )
	{
		return;
	}

	png = png_create_read_struct( PNG_LIBPNG_VER_STRING, ( png_voidp ) NULL, png_user_error_fn, png_user_warning_fn );

	if ( !png )
	{
		ri.Printf( PRINT_WARNING, "LoadPNG: png_create_write_struct() failed for (%s)\n", name );
		ri.FS_FreeFile( data );
		return;
	}

	// allocate/initialize the memory for image information.  REQUIRED
	info = png_create_info_struct( png );

	if ( !info )
	{
		ri.Printf( PRINT_WARNING, "LoadPNG: png_create_info_struct() failed for (%s)\n", name );
		ri.FS_FreeFile( data );
		png_destroy_read_struct( &png, ( png_infopp ) NULL, ( png_infopp ) NULL );
		return;
	}

	/*
	 * Set error handling if you are using the setjmp/longjmp method (this is
	 * the common method of doing things with libpng).  REQUIRED unless you
	 * set up your own error handlers in the png_create_read_struct() earlier.
	 */
	if ( setjmp( png_jmpbuf( png ) ) )
	{
		// if we get here, we had a problem reading the file
		ri.Printf( PRINT_WARNING, "LoadPNG: first exception handler called for (%s)\n", name );
		ri.FS_FreeFile( data );
		png_destroy_read_struct( &png, ( png_infopp ) & info, ( png_infopp ) NULL );
		return;
	}

	png_set_read_fn( png, data, png_read_data );

	png_set_sig_bytes( png, 0 );

	// The call to png_read_info() gives us all of the information from the
	// PNG file before the first IDAT (image data chunk).  REQUIRED
	png_read_info( png, info );

	// get picture info
	png_get_IHDR( png, info, ( png_uint_32 * ) &w, ( png_uint_32 * ) &h, &bit_depth, &color_type, NULL, NULL, NULL );

	// tell libpng to strip 16 bit/color files down to 8 bits/color
	png_set_strip_16( png );

	// expand paletted images to RGB triplets
	if ( color_type & PNG_COLOR_MASK_PALETTE )
	{
		png_set_expand( png );
	}

	// expand gray-scaled images to RGB triplets
	if ( !( color_type & PNG_COLOR_MASK_COLOR ) )
	{
		png_set_gray_to_rgb( png );
	}

	// expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel

	// expand paletted or RGB images with transparency to full alpha channels
	// so the data will be available as RGBA quartets
	if ( png_get_valid( png, info, PNG_INFO_tRNS ) )
	{
		png_set_tRNS_to_alpha( png );
	}

	// if there is no alpha information, fill with alphaByte
	if ( !( color_type & PNG_COLOR_MASK_ALPHA ) )
	{
		png_set_filler( png, alphaByte, PNG_FILLER_AFTER );
	}

	// expand pictures with less than 8bpp to 8bpp
	if ( bit_depth < 8 )
	{
		png_set_packing( png );
	}

	png_set_interlace_handling( png );

	// update structure with the above settings
	png_read_update_info( png, info );

	// allocate the memory to hold the image
	*width = w;
	*height = h;
	*pic = out = ( byte * ) ri.Z_Malloc( w * h * 4 );

	row_pointers = ( png_bytep * ) ri.Hunk_AllocateTempMemory( sizeof( png_bytep ) * h );

	// set a new exception handler
	if ( setjmp( png_jmpbuf( png ) ) )
	{
		ri.Printf( PRINT_WARNING, "LoadPNG: second exception handler called for (%s)\n", name );
		ri.Hunk_FreeTempMemory( row_pointers );
		ri.FS_FreeFile( data );
		png_destroy_read_struct( &png, ( png_infopp ) & info, ( png_infopp ) NULL );
		return;
	}

	for ( row = 0; row < h; row++ )
	{
		row_pointers[ row ] = ( png_bytep )( out + ( row * 4 * w ) );
	}

	// read image data
	png_read_image( png, row_pointers );

	// read rest of file, and get additional chunks in info
	png_read_end( png, info );

	// clean up after the read, and free any memory allocated
	png_destroy_read_struct( &png, &info, ( png_infopp ) NULL );

	ri.Hunk_FreeTempMemory( row_pointers );
	ri.FS_FreeFile( data );
}

/*
=========================================================

PNG SAVING

=========================================================
*/
static int png_compressed_size;

static void png_write_data( png_structp png, png_bytep data, png_size_t length )
{
	byte *io_ptr = (byte*) png_get_io_ptr( png );
	Com_Memcpy( io_ptr, data, length );
	png_init_io( png, ( png_FILE_p )( io_ptr + length ) );
	png_compressed_size += length;
}

static void png_flush_data( png_structp png )
{
}

void SavePNG( const char *name, const byte *pic, int width, int height, int numBytes, qboolean flip )
{
	png_structp png;
	png_infop   info;
	int         i;
	int         row_stride;
	byte        *buffer;
	byte        *row;
	png_bytep   *row_pointers;

	png = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );

	if ( !png )
	{
		return;
	}

	// Allocate/initialize the image information data
	info = png_create_info_struct( png );

	if ( !info )
	{
		png_destroy_write_struct( &png, ( png_infopp ) NULL );
		return;
	}

	png_compressed_size = 0;
	buffer = (byte*) ri.Hunk_AllocateTempMemory( width * height * numBytes );

	// set error handling
	if ( setjmp( png_jmpbuf( png ) ) )
	{
		ri.Hunk_FreeTempMemory( buffer );
		png_destroy_write_struct( &png, &info );
		return;
	}

	png_set_write_fn( png, buffer, png_write_data, png_flush_data );

	switch ( numBytes )
	{
	default:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	case 3:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	case 2:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_GA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	case 1:
		png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
		              PNG_FILTER_TYPE_DEFAULT );
		break;
	}

	// write the file header information
	png_write_info( png, info );

	row_pointers = (png_bytep*) ri.Hunk_AllocateTempMemory( height * sizeof( png_bytep ) );

	if ( setjmp( png_jmpbuf( png ) ) )
	{
		ri.Hunk_FreeTempMemory( row_pointers );
		ri.Hunk_FreeTempMemory( buffer );
		png_destroy_write_struct( &png, &info );
		return;
	}

	row_stride = width * numBytes;
	row = ( byte * ) pic + ( height - 1 ) * row_stride;

	if ( flip )
	{
		for ( i = height - 1; i >= 0; i-- )
		{
			row_pointers[ i ] = row;
			row -= row_stride;
		}
	}
	else
	{
		for ( i = 0; i < height; i++ )
		{
			row_pointers[ i ] = row;
			row -= row_stride;
		}
	}

	png_write_image( png, row_pointers );
	png_write_end( png, info );

	// clean up after the write, and free any memory allocated
	png_destroy_write_struct( &png, &info );

	ri.Hunk_FreeTempMemory( row_pointers );

	ri.FS_WriteFile( name, buffer, png_compressed_size );

	ri.Hunk_FreeTempMemory( buffer );
}
<|endoftext|>


<commit_before>#include "UserStudy2.h"

UserStudy2::UserStudy2(QWidget *parent) : QWidget(parent) {
    setupEyeTracker();
    setupTimers();
    setupViews();
    setupTasks();
}

void UserStudy2::setupEyeTracker() {
    eyeTrackerThread = new QThread();
    eyeTracker       = new EyeTracker();
    eyeTracker->moveToThread(eyeTrackerThread);

    connect(eyeTrackerThread, SIGNAL(started()), eyeTracker, SLOT(start()));
    connect(eyeTracker, SIGNAL(frameReady(QImage)), this, SLOT(onCvFrameReady(QImage)));
    connect(eyeTracker, SIGNAL(blinkDetected()), this, SLOT(onBlinkDectected()));
    connect(eyeTracker, SIGNAL(finished()), eyeTrackerThread, SLOT(quit()));
    connect(eyeTrackerThread, SIGNAL(finished()), eyeTracker, SLOT(deleteLater()));
    connect(eyeTrackerThread, SIGNAL(finished()), eyeTrackerThread, SLOT(deleteLater()));
}

void UserStudy2::setupTimers() {
    fatigueTimer = new QTimer(this);
    fatigueTimer->setInterval(FATIGUE_LIMIT);
    connect(fatigueTimer, SIGNAL(timeout()), this, SLOT(onFatigueTimerTimeOut()));
}

void UserStudy2::setupViews() {
    gridLayout = new QGridLayout(this);  // 9*9 grid

    cameraView = new QLabel(this);
    cameraView->setAlignment(Qt::AlignCenter);

    maskView = new MaskView(this);
    maskView->setContext(cameraView);
    maskView->setAttribute(Qt::WA_TransparentForMouseEvents);

    gridLayout->addWidget(maskView, 1, 0, 9, 9);
    gridLayout->addWidget(cameraView, 1, 0, 9, 9);

    for (int i = 0; i < NUM_TASKS; i++) {
        QPushButton *button = new QPushButton("Task"+QString::number(i+1));
        connect(button, SIGNAL(clicked()), this, SLOT(onTaskButtonClicked()));
        gridLayout->addWidget(button, 0, i+1, 1, 1);
        taskButtons.push_back(button);
    }

    buttonStart = new QPushButton("Camera On");
    buttonPause = new QPushButton("Stop");
    buttonFinish = new QPushButton("Camera Off");

    connect(buttonStart, SIGNAL(clicked()), this, SLOT(onStartButtonClicked()));
    connect(buttonPause, SIGNAL(clicked()), this, SLOT(onPauseButtonClicked()));
    connect(buttonFinish, SIGNAL(clicked()), this, SLOT(onFinishButtonClicked()));

    gridLayout->addWidget(buttonStart, 0, 0, 1, 1);
    gridLayout->addWidget(buttonPause, 0, 7, 1, 1);
    gridLayout->addWidget(buttonFinish, 0, 8, 1, 1);

    this->setLayout(gridLayout);
    maskView->updateFboSize();
}

void UserStudy2::setupTasks() {}

void UserStudy2::onStartButtonClicked() {
    cameraViewEnabled = true;

    buttonStart->setDisabled(true);
    blinkCounter = 0;
    timestart = QDateTime::currentDateTime();

    QString taskPath = QString("./log/").append(timestart.toLocalTime().toString());
    if (!QDir(taskPath).exists())
        QDir().mkdir(taskPath);

    timestamp = timestart;
    eyeTrackerThread->start();
    outputLog(" |----------------------| ");
}

void UserStudy2::onTaskButtonClicked() {
    cameraViewEnabled = false;
}

void UserStudy2::onPauseButtonClicked() {
    cameraViewEnabled = true;

    QDateTime now = QDateTime::currentDateTime();
    float blinkRate = static_cast<float>(blinkCounter) * 60 / timestamp.secsTo(now);
    outputLog(QString(" Eye blink rate: ").append(QString::number(blinkRate)));
    outputLog(" |----------------------| ");

    blinkCounter = 0;
    timestamp = now;
}

void UserStudy2::onFinishButtonClicked() {
    eyeTracker->stop();
    fatigueTimer->stop();
    cameraView->clear();
}

void UserStudy2::onFatigueTimerTimeOut() {
    if (toFlash) {
        maskView->flash();
        fatigueTimer->start();
    }

    if (toBlur) {
        maskView->blur();
        fatigueTimer->stop();
    }

    maskView->update();
    outputLog(" stimulated ");
}

void UserStudy2::onBlinkDectected() {
    blinkCounter++;
    if (stimulusEnabled) {
        fatigueTimer->start();
        maskView->reset();
    }
    if (!cameraViewEnabled)
        outputLog(" blink detected ");
}

void UserStudy2::onCvFrameReady(QImage img) {
    if (cameraViewEnabled) {
        cameraView->setPixmap(QPixmap::fromImage(img));
    } else {
        QString fp = QString("./log/").append(timestart.toLocalTime().toString());      // task path
        fp.append("/").append(timestamp.toLocalTime().toString()).append("/");          // subtask path
        fp.append(QString::number(timestamp.msecsTo(QDateTime::currentDateTime())));    // file name
        img.scaled(320, 240, Qt::KeepAspectRatio).save(fp.append(".png"), "PNG");
    }
}

void UserStudy2::resizeEvent(QResizeEvent *) {
    maskView->updateFboSize();
}

void UserStudy2::outputLog(const QString &msg) {
    QString fn = QString("./log/").append(timestart.toLocalTime().toString());
    fn.append("/").append(timestart.toLocalTime().toString()).append(".txt");
    QFile logFile(fn);
    if (logFile.open(QFile::ReadWrite|QFile::Append|QFile::Text)) {
        QTextStream outStream(&logFile);
        outStream << QDateTime::currentDateTime().toString() << msg << "\n";
    }
}
<commit_msg>temp check in<commit_after>#include "UserStudy2.h"

UserStudy2::UserStudy2(QWidget *parent) : QWidget(parent) {
    setupEyeTracker();
    setupTimers();
    setupViews();
    setupTasks();
}

void UserStudy2::setupEyeTracker() {
    eyeTrackerThread = new QThread();
    eyeTracker       = new EyeTracker();
    eyeTracker->moveToThread(eyeTrackerThread);

    connect(eyeTrackerThread, SIGNAL(started()), eyeTracker, SLOT(start()));
    connect(eyeTracker, SIGNAL(frameReady(QImage)), this, SLOT(onCvFrameReady(QImage)));
    connect(eyeTracker, SIGNAL(blinkDetected()), this, SLOT(onBlinkDectected()));
    connect(eyeTracker, SIGNAL(finished()), eyeTrackerThread, SLOT(quit()));
    connect(eyeTrackerThread, SIGNAL(finished()), eyeTracker, SLOT(deleteLater()));
    connect(eyeTrackerThread, SIGNAL(finished()), eyeTrackerThread, SLOT(deleteLater()));
}

void UserStudy2::setupTimers() {
    fatigueTimer = new QTimer(this);
    fatigueTimer->setInterval(FATIGUE_LIMIT);
    connect(fatigueTimer, SIGNAL(timeout()), this, SLOT(onFatigueTimerTimeOut()));
}

void UserStudy2::setupViews() {
    gridLayout = new QGridLayout(this);  // 9*9 grid

    cameraView = new QLabel(this);
    cameraView->setAlignment(Qt::AlignCenter);

    maskView = new MaskView(this);
    maskView->setContext(cameraView);
    maskView->setAttribute(Qt::WA_TransparentForMouseEvents);

    gridLayout->addWidget(maskView, 1, 0, 9, 9);
    gridLayout->addWidget(cameraView, 1, 0, 9, 9);

    for (int i = 0; i < NUM_TASKS; i++) {
        QPushButton *button = new QPushButton("Task"+QString::number(i+1));
        connect(button, SIGNAL(clicked()), this, SLOT(onTaskButtonClicked()));
        gridLayout->addWidget(button, 0, i+1, 1, 1);
        taskButtons.push_back(button);
    }

    buttonStart = new QPushButton("Camera On");
    buttonPause = new QPushButton("Stop");
    buttonFinish = new QPushButton("Camera Off");

    connect(buttonStart, SIGNAL(clicked()), this, SLOT(onStartButtonClicked()));
    connect(buttonPause, SIGNAL(clicked()), this, SLOT(onPauseButtonClicked()));
    connect(buttonFinish, SIGNAL(clicked()), this, SLOT(onFinishButtonClicked()));

    gridLayout->addWidget(buttonStart, 0, 0, 1, 1);
    gridLayout->addWidget(buttonPause, 0, 7, 1, 1);
    gridLayout->addWidget(buttonFinish, 0, 8, 1, 1);

    this->setLayout(gridLayout);
    maskView->updateFboSize();
}

void UserStudy2::setupTasks() {}

void UserStudy2::onStartButtonClicked() {
    cameraViewEnabled = true;

    buttonStart->setDisabled(true);
    blinkCounter = 0;
    timestart = QDateTime::currentDateTime();

    QString taskPath = QString("./log/").append(timestart.toLocalTime().toString());
    if (!QDir(taskPath).exists())
        QDir().mkdir(taskPath);

    timestamp = timestart;
    eyeTrackerThread->start();
    fatigueTimer->start();
    outputLog(" |----------------------| ");
}

void UserStudy2::onTaskButtonClicked() {
    cameraViewEnabled = false;
}

void UserStudy2::onPauseButtonClicked() {
    cameraViewEnabled = true;

    QDateTime now = QDateTime::currentDateTime();
    float blinkRate = static_cast<float>(blinkCounter) * 60 / timestamp.secsTo(now);
    outputLog(QString(" Eye blink rate: ").append(QString::number(blinkRate)));
    outputLog(" |----------------------| ");

    blinkCounter = 0;
    timestamp = now;
}

void UserStudy2::onFinishButtonClicked() {
    eyeTracker->stop();
    fatigueTimer->stop();
    cameraView->clear();
}

void UserStudy2::onFatigueTimerTimeOut() {
    if (toFlash) {
        maskView->flash();
        fatigueTimer->start();
    }

    if (toBlur) {
        maskView->blur();
        fatigueTimer->stop();
    }

    maskView->update();
    outputLog(" stimulated ");
}

void UserStudy2::onBlinkDectected() {
    blinkCounter++;
    if (stimulusEnabled) {
        fatigueTimer->start();
        maskView->reset();
    }
    if (!cameraViewEnabled)
        outputLog(" blink detected ");
}

void UserStudy2::onCvFrameReady(QImage img) {
    if (cameraViewEnabled) {
        cameraView->setPixmap(QPixmap::fromImage(img));
    } else {
        QString fp = QString("./log/").append(timestart.toLocalTime().toString());      // task path
        fp.append("/").append(timestamp.toLocalTime().toString()).append("/");          // subtask path
        fp.append(QString::number(timestamp.msecsTo(QDateTime::currentDateTime())));    // file name
        img.scaled(320, 240, Qt::KeepAspectRatio).save(fp.append(".png"), "PNG");
    }
}

void UserStudy2::resizeEvent(QResizeEvent *) {
    maskView->updateFboSize();
}

void UserStudy2::outputLog(const QString &msg) {
    QString fn = QString("./log/").append(timestart.toLocalTime().toString());
    fn.append("/").append(timestart.toLocalTime().toString()).append(".txt");
    QFile logFile(fn);
    if (logFile.open(QFile::ReadWrite|QFile::Append|QFile::Text)) {
        QTextStream outStream(&logFile);
        outStream << QDateTime::currentDateTime().toString() << msg << "\n";
    }
}
<|endoftext|>


<commit_before>#include "EpidemicApplication.h"
#include "LocationObject.h"
#include "FileWriter.h"
#include "EpidemicPartitioner.h"
#include "Person.h"
#include "tinyxml2.h"
#include "PartitionInfo.h"
#include "RoundRobinPartitioner.h"
#include "DeserializerManager.h"
#include "WattsStrogatzModel.h"

#include <vector>
#include <map>
#include <iostream>
#include <fstream>

#define INIT_VTIME 0

using namespace std;
using namespace tinyxml2;

EpidemicApplication::EpidemicApplication(string inputFileName):
    inputFileName(inputFileName) { }

const PartitionInfo* EpidemicApplication::getPartitionInfo(
        unsigned int numberOfProcessorsAvailable,
        const std::vector<SimulationObject*>* simulationObjects) {
    const PartitionInfo* retval = myPartitioner->partition(NULL, numberOfProcessorsAvailable);
    delete myPartitioner;
    return retval;
}

std::vector<SimulationObject*>* EpidemicApplication::getSimulationObjects() {

    myPartitioner = new EpidemicPartitioner();
    int numRegions = 0, numLocations = 0, numPersons = 0;
    unsigned int pid = 0, travelTimeToHub = 0, latentDwellTime = 0,
            incubatingDwellTime = 0, infectiousDwellTime = 0,
            asymptDwellTime = 0, locStateRefreshInterval = 0,
            locDiffusionTrigInterval = 0, diseaseSeed = 0,
            diffusionSeed = 0;
    double susceptibility = 0.0;
    float transmissibility = 0.0, probULU = 0.0, probULV = 0.0,
            probURV = 0.0, probUIV = 0.0, probUIU = 0.0,
            latentInfectivity = 0.0, incubatingInfectivity = 0.0,
            infectiousInfectivity = 0.0, asymptInfectivity = 0.0;
    string locationName = "", infectionState = "", vaccinationStatus = "",
            regionName = "", model = "", dataCaptureStatus = "",
            dataCaptureFileName = "";

    Person *person;
    vector <SimulationObject*> *locObjs;
    vector <Person *> *personVec;

    vector <SimulationObject *> *simulationObjVec = new vector <SimulationObject *>;

    map <string, SimulationObject *> simulationObjMap;
    map <string, unsigned int> travelMap;

    /* open the XML file */
    XMLDocument EpidemicConfig;
    int errorID = EpidemicConfig.LoadFile(inputFileName.c_str());
    if(errorID) {
        cerr << "ERROR : XML config file failed to load. Error ID: " << errorID << endl;
        abort();
    }

    XMLElement *diffusion = NULL, *disease = NULL, *dataCapture = NULL,
               *numOfRegions = NULL, *region = NULL, *location = NULL,
               *people = NULL;

    /* Refer to README for more details */
    diffusion = EpidemicConfig.FirstChildElement()->FirstChildElement("diffusion");
    model.assign( diffusion->FirstChildElement("model")->GetText() );
    diffusion->FirstChildElement("seed")->QueryUnsignedText(&diffusionSeed);

    /* Refer to README for more details */
    disease = EpidemicConfig.FirstChildElement()->FirstChildElement("disease");
    disease->FirstChildElement("transmissibility")->QueryFloatText(&transmissibility);
    disease->FirstChildElement("latent_dwell_time")->QueryUnsignedText(&latentDwellTime);
    disease->FirstChildElement("latent_infectivity")->QueryFloatText(&latentInfectivity);
    disease->FirstChildElement("incubating_dwell_time")->QueryUnsignedText(&incubatingDwellTime );
    disease->FirstChildElement("incubating_infectivity")->QueryFloatText(&incubatingInfectivity);
    disease->FirstChildElement("infectious_dwell_time")->QueryUnsignedText(&infectiousDwellTime);
    disease->FirstChildElement("infectious_infectivity")->QueryFloatText(&infectiousInfectivity);
    disease->FirstChildElement("asympt_dwell_time")->QueryUnsignedText(&asymptDwellTime);
    disease->FirstChildElement("asympt_infectivity")->QueryFloatText(&asymptInfectivity);
    disease->FirstChildElement("prob_ul_u")->QueryFloatText(&probULU);
    disease->FirstChildElement("prob_ul_v")->QueryFloatText(&probULV);
    disease->FirstChildElement("prob_ur_v")->QueryFloatText(&probURV);
    disease->FirstChildElement("prob_ui_v")->QueryFloatText(&probUIV);
    disease->FirstChildElement("prob_ui_u")->QueryFloatText(&probUIU);
    disease->FirstChildElement("location_state_refresh_interval")->QueryUnsignedText(&locStateRefreshInterval);
    disease->FirstChildElement("seed")->QueryUnsignedText(&diseaseSeed);

    /* Refer to README for more details */
    /* Add fileWriter to the partition information if data capture is needed */
    dataCapture = EpidemicConfig.FirstChildElement()->FirstChildElement("data_capture");
    dataCaptureStatus.assign( dataCapture->FirstChildElement("is_needed")->GetText() );
    if( dataCaptureStatus == "yes" ) {
        dataCaptureFileName.assign( dataCapture->FirstChildElement("capture_file")->GetText() );
        FileWriter *fileWriter = new FileWriter("writer", dataCaptureFileName);
        simulationObjVec->push_back(fileWriter);
        locObjs = new vector<SimulationObject*>;
        locObjs->push_back(fileWriter);
        myPartitioner->addObjectGroup(locObjs);
    }

    /* Refer to README for more details */
    numOfRegions=EpidemicConfig.FirstChildElement()->FirstChildElement("number_of_regions");
    numOfRegions->QueryIntText(&numRegions);
    region=numOfRegions;

    /* For each region in the simulation, initialize the locations */
    for( int regIndex = 0; regIndex < numRegions; regIndex++ ) {
        region =region->NextSiblingElement();
        regionName.assign( region->FirstChildElement("region_name")->GetText() );

        numLocations = 0;
        region->FirstChildElement("number_of_locations")->QueryIntText(&numLocations);
        location = region->FirstChildElement("number_of_locations");
        locObjs = new vector<SimulationObject*>;

        for( int locIndex = 0; locIndex < numLocations; locIndex++ ) {
            personVec = new vector <Person *>;

            location = location->NextSiblingElement();
            locationName.assign( location->FirstChildElement("location_name")->GetText() );
            location->FirstChildElement("number_of_persons")->QueryIntText(&numPersons);
            location->FirstChildElement("travel_time_to_central_hub")->QueryUnsignedText(&travelTimeToHub);
            location->FirstChildElement("diffusion_trigger_interval")->QueryUnsignedText(&locDiffusionTrigInterval);
            people = location->FirstChildElement("diffusion_trigger_interval");

            locationName += ",";
            locationName += regionName;

            travelMap.insert( pair <string, unsigned int>(locationName, travelTimeToHub) );

            /* Read each person's details */
            for(int perIndex = 0; perIndex < numPersons; perIndex++) {
                people= people->NextSiblingElement();
                people->FirstChildElement("pid")->QueryUnsignedText(&pid);
                people->FirstChildElement("susceptibility")->QueryDoubleText(&susceptibility);
                vaccinationStatus.assign( people->FirstChildElement("is_vaccinated")->GetText() );
                infectionState.assign( people->FirstChildElement("infection_state")->GetText() );

                person = new Person( pid, susceptibility, vaccinationStatus, infectionState, INIT_VTIME, INIT_VTIME );
                personVec->push_back( person );
            }

            LocationObject *locObject = new LocationObject( locationName, transmissibility,
                                                            latentDwellTime, incubatingDwellTime,
                                                            infectiousDwellTime, asymptDwellTime,
                                                            latentInfectivity, incubatingInfectivity,
                                                            infectiousInfectivity, asymptInfectivity,
                                                            probULU, probULV, probURV, probUIV, probUIU,
                                                            (dataCaptureStatus == "yes") ? true : false,
                                                            locStateRefreshInterval, locDiffusionTrigInterval,
                                                            personVec, travelTimeToHub, diseaseSeed, diffusionSeed );
            locObjs->push_back(locObject);
            simulationObjMap.insert( pair <string, SimulationObject *>(locationName, locObject) );
            simulationObjVec->push_back(locObject);
        }

        /* Add the group of objects to the partition information */
        myPartitioner->addObjectGroup(locObjs);
    }

    /* Send the travel map to each location based on the model type */
    if( model == "FullyConnected" ) {
        /* Send travel distance of all locations except its own */
        for( map <string, SimulationObject*>::iterator mapIter = simulationObjMap.begin();
                                            mapIter != simulationObjMap.end(); mapIter++ ) {

            LocationObject *locObj = static_cast <LocationObject *> (mapIter->second);
            map <string, unsigned int> tempTravelMap = travelMap;
            tempTravelMap.erase(mapIter->first);
            locObj->populateTravelMap(tempTravelMap);
        }

    } else if( model == "WattsStrogatz" ) {
        /* Refer to README for more details */
        unsigned int k = 0;
        float beta = 0.0;
        XMLElement *wattsStrogatz = NULL;

        wattsStrogatz = diffusion->FirstChildElement("watts_strogatz");
        wattsStrogatz->FirstChildElement("k")->QueryUnsignedText(&k);
        wattsStrogatz->FirstChildElement("beta")->QueryFloatText(&beta);

        WattsStrogatzModel *wsModel = new WattsStrogatzModel(k, beta, diffusionSeed);

        vector <string> nodeVec;
        for( map <string, unsigned int>::iterator mapIter = travelMap.begin();
                                            mapIter != travelMap.end(); mapIter++ ) {
            string location = static_cast <string> (mapIter->first);
            nodeVec.push_back(location);
        }
        wsModel->populateNodes(nodeVec);
        wsModel->mapNodes();

        for( map <string, SimulationObject*>::iterator mapIter = simulationObjMap.begin();
                                            mapIter != simulationObjMap.end(); mapIter++ ) {

            LocationObject *locObj = static_cast <LocationObject *> (mapIter->second);
            vector <string> connVec = wsModel->fetchNodeLinks(mapIter->first);
            map <string, unsigned int> tempTravelMap;
            for( vector <string>::iterator connVecIter = connVec.begin();
                                    connVecIter != connVec.end(); connVecIter++ ) {

                map <string, unsigned int>::iterator travelMapIter = travelMap.find(*connVecIter);
                tempTravelMap.insert(
                    pair <string, unsigned int>(travelMapIter->first, travelMapIter->second) );
            }
            locObj->populateTravelMap(tempTravelMap);
        }
        delete wsModel;

    } else {
        cerr << "ERROR : Invalid network model" << endl;
        abort();
    }

    EpidemicConfig.SaveFile(inputFileName.c_str());

    return simulationObjVec;
}

int EpidemicApplication::finalize() {
    return 0;
}

void EpidemicApplication::registerDeserializers() {
    DeserializerManager::instance()->registerDeserializer(  EpidemicEvent::getEpidemicEventDataType(),
                                                            &EpidemicEvent::deserialize );
}
<commit_msg>removed line to save enormous epidemic config file, llamaOS does not support saving a file this large<commit_after>#include "EpidemicApplication.h"
#include "LocationObject.h"
#include "FileWriter.h"
#include "EpidemicPartitioner.h"
#include "Person.h"
#include "tinyxml2.h"
#include "PartitionInfo.h"
#include "RoundRobinPartitioner.h"
#include "DeserializerManager.h"
#include "WattsStrogatzModel.h"

#include <vector>
#include <map>
#include <iostream>
#include <fstream>

#define INIT_VTIME 0

using namespace std;
using namespace tinyxml2;

EpidemicApplication::EpidemicApplication(string inputFileName):
    inputFileName(inputFileName) { }

const PartitionInfo* EpidemicApplication::getPartitionInfo(
        unsigned int numberOfProcessorsAvailable,
        const std::vector<SimulationObject*>* simulationObjects) {
    const PartitionInfo* retval = myPartitioner->partition(NULL, numberOfProcessorsAvailable);
    delete myPartitioner;
    return retval;
}

std::vector<SimulationObject*>* EpidemicApplication::getSimulationObjects() {

    myPartitioner = new EpidemicPartitioner();
    int numRegions = 0, numLocations = 0, numPersons = 0;
    unsigned int pid = 0, travelTimeToHub = 0, latentDwellTime = 0,
            incubatingDwellTime = 0, infectiousDwellTime = 0,
            asymptDwellTime = 0, locStateRefreshInterval = 0,
            locDiffusionTrigInterval = 0, diseaseSeed = 0,
            diffusionSeed = 0;
    double susceptibility = 0.0;
    float transmissibility = 0.0, probULU = 0.0, probULV = 0.0,
            probURV = 0.0, probUIV = 0.0, probUIU = 0.0,
            latentInfectivity = 0.0, incubatingInfectivity = 0.0,
            infectiousInfectivity = 0.0, asymptInfectivity = 0.0;
    string locationName = "", infectionState = "", vaccinationStatus = "",
            regionName = "", model = "", dataCaptureStatus = "",
            dataCaptureFileName = "";

    Person *person;
    vector <SimulationObject*> *locObjs;
    vector <Person *> *personVec;

    vector <SimulationObject *> *simulationObjVec = new vector <SimulationObject *>;

    map <string, SimulationObject *> simulationObjMap;
    map <string, unsigned int> travelMap;

    /* open the XML file */
    XMLDocument EpidemicConfig;
    int errorID = EpidemicConfig.LoadFile(inputFileName.c_str());
    if(errorID) {
        cerr << "ERROR : XML config file failed to load. Error ID: " << errorID << endl;
        abort();
    }

    XMLElement *diffusion = NULL, *disease = NULL, *dataCapture = NULL,
               *numOfRegions = NULL, *region = NULL, *location = NULL,
               *people = NULL;

    /* Refer to README for more details */
    diffusion = EpidemicConfig.FirstChildElement()->FirstChildElement("diffusion");
    model.assign( diffusion->FirstChildElement("model")->GetText() );
    diffusion->FirstChildElement("seed")->QueryUnsignedText(&diffusionSeed);

    /* Refer to README for more details */
    disease = EpidemicConfig.FirstChildElement()->FirstChildElement("disease");
    disease->FirstChildElement("transmissibility")->QueryFloatText(&transmissibility);
    disease->FirstChildElement("latent_dwell_time")->QueryUnsignedText(&latentDwellTime);
    disease->FirstChildElement("latent_infectivity")->QueryFloatText(&latentInfectivity);
    disease->FirstChildElement("incubating_dwell_time")->QueryUnsignedText(&incubatingDwellTime );
    disease->FirstChildElement("incubating_infectivity")->QueryFloatText(&incubatingInfectivity);
    disease->FirstChildElement("infectious_dwell_time")->QueryUnsignedText(&infectiousDwellTime);
    disease->FirstChildElement("infectious_infectivity")->QueryFloatText(&infectiousInfectivity);
    disease->FirstChildElement("asympt_dwell_time")->QueryUnsignedText(&asymptDwellTime);
    disease->FirstChildElement("asympt_infectivity")->QueryFloatText(&asymptInfectivity);
    disease->FirstChildElement("prob_ul_u")->QueryFloatText(&probULU);
    disease->FirstChildElement("prob_ul_v")->QueryFloatText(&probULV);
    disease->FirstChildElement("prob_ur_v")->QueryFloatText(&probURV);
    disease->FirstChildElement("prob_ui_v")->QueryFloatText(&probUIV);
    disease->FirstChildElement("prob_ui_u")->QueryFloatText(&probUIU);
    disease->FirstChildElement("location_state_refresh_interval")->QueryUnsignedText(&locStateRefreshInterval);
    disease->FirstChildElement("seed")->QueryUnsignedText(&diseaseSeed);

    /* Refer to README for more details */
    /* Add fileWriter to the partition information if data capture is needed */
    dataCapture = EpidemicConfig.FirstChildElement()->FirstChildElement("data_capture");
    dataCaptureStatus.assign( dataCapture->FirstChildElement("is_needed")->GetText() );
    if( dataCaptureStatus == "yes" ) {
        dataCaptureFileName.assign( dataCapture->FirstChildElement("capture_file")->GetText() );
        FileWriter *fileWriter = new FileWriter("writer", dataCaptureFileName);
        simulationObjVec->push_back(fileWriter);
        locObjs = new vector<SimulationObject*>;
        locObjs->push_back(fileWriter);
        myPartitioner->addObjectGroup(locObjs);
    }

    /* Refer to README for more details */
    numOfRegions=EpidemicConfig.FirstChildElement()->FirstChildElement("number_of_regions");
    numOfRegions->QueryIntText(&numRegions);
    region=numOfRegions;

    /* For each region in the simulation, initialize the locations */
    for( int regIndex = 0; regIndex < numRegions; regIndex++ ) {
        region =region->NextSiblingElement();
        regionName.assign( region->FirstChildElement("region_name")->GetText() );

        numLocations = 0;
        region->FirstChildElement("number_of_locations")->QueryIntText(&numLocations);
        location = region->FirstChildElement("number_of_locations");
        locObjs = new vector<SimulationObject*>;

        for( int locIndex = 0; locIndex < numLocations; locIndex++ ) {
            personVec = new vector <Person *>;

            location = location->NextSiblingElement();
            locationName.assign( location->FirstChildElement("location_name")->GetText() );
            location->FirstChildElement("number_of_persons")->QueryIntText(&numPersons);
            location->FirstChildElement("travel_time_to_central_hub")->QueryUnsignedText(&travelTimeToHub);
            location->FirstChildElement("diffusion_trigger_interval")->QueryUnsignedText(&locDiffusionTrigInterval);
            people = location->FirstChildElement("diffusion_trigger_interval");

            locationName += ",";
            locationName += regionName;

            travelMap.insert( pair <string, unsigned int>(locationName, travelTimeToHub) );

            /* Read each person's details */
            for(int perIndex = 0; perIndex < numPersons; perIndex++) {
                people= people->NextSiblingElement();
                people->FirstChildElement("pid")->QueryUnsignedText(&pid);
                people->FirstChildElement("susceptibility")->QueryDoubleText(&susceptibility);
                vaccinationStatus.assign( people->FirstChildElement("is_vaccinated")->GetText() );
                infectionState.assign( people->FirstChildElement("infection_state")->GetText() );

                person = new Person( pid, susceptibility, vaccinationStatus, infectionState, INIT_VTIME, INIT_VTIME );
                personVec->push_back( person );
            }

            LocationObject *locObject = new LocationObject( locationName, transmissibility,
                                                            latentDwellTime, incubatingDwellTime,
                                                            infectiousDwellTime, asymptDwellTime,
                                                            latentInfectivity, incubatingInfectivity,
                                                            infectiousInfectivity, asymptInfectivity,
                                                            probULU, probULV, probURV, probUIV, probUIU,
                                                            (dataCaptureStatus == "yes") ? true : false,
                                                            locStateRefreshInterval, locDiffusionTrigInterval,
                                                            personVec, travelTimeToHub, diseaseSeed, diffusionSeed );
            locObjs->push_back(locObject);
            simulationObjMap.insert( pair <string, SimulationObject *>(locationName, locObject) );
            simulationObjVec->push_back(locObject);
        }

        /* Add the group of objects to the partition information */
        myPartitioner->addObjectGroup(locObjs);
    }

    /* Send the travel map to each location based on the model type */
    if( model == "FullyConnected" ) {
        /* Send travel distance of all locations except its own */
        for( map <string, SimulationObject*>::iterator mapIter = simulationObjMap.begin();
                                            mapIter != simulationObjMap.end(); mapIter++ ) {

            LocationObject *locObj = static_cast <LocationObject *> (mapIter->second);
            map <string, unsigned int> tempTravelMap = travelMap;
            tempTravelMap.erase(mapIter->first);
            locObj->populateTravelMap(tempTravelMap);
        }

    } else if( model == "WattsStrogatz" ) {
        /* Refer to README for more details */
        unsigned int k = 0;
        float beta = 0.0;
        XMLElement *wattsStrogatz = NULL;

        wattsStrogatz = diffusion->FirstChildElement("watts_strogatz");
        wattsStrogatz->FirstChildElement("k")->QueryUnsignedText(&k);
        wattsStrogatz->FirstChildElement("beta")->QueryFloatText(&beta);

        WattsStrogatzModel *wsModel = new WattsStrogatzModel(k, beta, diffusionSeed);

        vector <string> nodeVec;
        for( map <string, unsigned int>::iterator mapIter = travelMap.begin();
                                            mapIter != travelMap.end(); mapIter++ ) {
            string location = static_cast <string> (mapIter->first);
            nodeVec.push_back(location);
        }
        wsModel->populateNodes(nodeVec);
        wsModel->mapNodes();

        for( map <string, SimulationObject*>::iterator mapIter = simulationObjMap.begin();
                                            mapIter != simulationObjMap.end(); mapIter++ ) {

            LocationObject *locObj = static_cast <LocationObject *> (mapIter->second);
            vector <string> connVec = wsModel->fetchNodeLinks(mapIter->first);
            map <string, unsigned int> tempTravelMap;
            for( vector <string>::iterator connVecIter = connVec.begin();
                                    connVecIter != connVec.end(); connVecIter++ ) {

                map <string, unsigned int>::iterator travelMapIter = travelMap.find(*connVecIter);
                tempTravelMap.insert(
                    pair <string, unsigned int>(travelMapIter->first, travelMapIter->second) );
            }
            locObj->populateTravelMap(tempTravelMap);
        }
        delete wsModel;

    } else {
        cerr << "ERROR : Invalid network model" << endl;
        abort();
    }

    // !BAM
    // saving the config file is unnecessary and causes issues with llamaOS
    // EpidemicConfig.SaveFile(inputFileName.c_str());

    return simulationObjVec;
}

int EpidemicApplication::finalize() {
    return 0;
}

void EpidemicApplication::registerDeserializers() {
    DeserializerManager::instance()->registerDeserializer(  EpidemicEvent::getEpidemicEventDataType(),
                                                            &EpidemicEvent::deserialize );
}
<|endoftext|>


<commit_before>
/**
 * @file   actorManager.cpp
 * @brief  アクターをマネージメントする関数群が置かれています
 *
 * @author y.akira
 * @date   2016.12.14
 */

#include "precompiled.h"

//! @brief AddActorを使って登録されたアクターはこのリストにまとめられます
static std::list<shared_ptr<Actor>> g_actorsList;

/**
 * @brief     アクターを追加します
 * @param     [in] actor 登録するアクターです
 * @note      先にインスタンス化したアクターを登録することも\n
 *            その場でmake_sharedしても登録可能です
 * @attention 無条件でアクターを登録できる
 */
void AddActor(const shared_ptr<Actor>& actor) {
  g_actorsList.emplace_back(actor);
}

/**
 * @brief 登録されたアクターのupdateを呼びます
 * @brief ActorのsetupやonCollisionEnterもここで呼ばれます
 * @param [in] deltaTime 前のフレームから現在のフレームまでにかかった時間を与えます
 * @note  基本的には各シーンで使用するので
 *        そのシーンのupdateにあるdeltaTimeをそのまま渡してください
 */
void UpdateActors(float deltaTime) {
  for (auto& actor : g_actorsList) {
    // setupを実行していなければsetupを実行する
    if (!actor->hasSetup()) {
      actor->setup();
      if (actor->shouldUpdate()) {
        actor->update(deltaTime);
      }
    }
    else
    // updateが有効になっていればupdateを実行
    if (actor->shouldUpdate()) {
      actor->update(deltaTime);
    }

    // 当たり判定を実行する場合は処理を続ける
    if (!actor->shouldCollision()) { continue; }

    for (auto& c_actor : g_actorsList) {
      // 自分自信との判定は取らない
      if (*actor == *c_actor) { continue; }

      // 相手が判定を取る場合のみ処理を続ける
      if (!c_actor->shouldCollision()) { continue; }

      // 衝突していれば処理を続ける
      if (!actor->getRectangle().intersects(c_actor->getRectangle())) { continue; }

      // どちらも生きていれば処理を続ける
      if (actor->shouldDestroy() || c_actor->shouldDestroy()) { continue; }

      // 各actorに衝突したと伝える
      actor->onCollision(c_actor.get());
      c_actor->onCollision(actor.get());
    }
  }

  // 削除対象のactorは削除する
  g_actorsList.remove_if(
    [] (const shared_ptr<Actor>& act)->bool {
      return act->shouldDestroy();
    }
  );
}

/**
 * @brief 登録されたアクターのdrawを呼びます
 */
void DrawActors() {
  for (auto& actor : g_actorsList) {
    actor->draw();
  }
}

/**
 * @brief 登録されたアクターのGuiを描画します
 */
void DrawActorsGui() {
  for (auto& actor : g_actorsList) {
    if (actor->isHide()) { continue; } // 表示・非表示チェック
    actor->gui();
  }
}

/**
 * @brief 登録されたアクターを全て削除します
 */
void ClearActors() {
  g_actorsList.clear();
}

/**
 * @brief 特定のアクターを削除対象にします
 * @param [in] name 削除したいアクターの名前
 */
void DeleteActors(const string& name) {
  for (auto& act : g_actorsList) {
    if (act->getName() != name) { continue; }
    act->destroy();
  }
}

/**
 * @brief 特定のアクターを削除対象にします
 * @param [in] tag 削除したいアクターのタグ
 */
void DeleteActors(const int tag) {
  for (auto& act : g_actorsList) {
    if (act->getTag() != tag) { continue; }
    act->destroy();
  }
}

/**
 * @brief  特定のアクターを取得する
 * @param  [in] name 取得したいアクターの名前
 * @retval アクターを指すスマポを返す
 * @note   見つからなければnullptrを返す
 */
shared_ptr<Actor> FindActor(const string& name) {
  for (auto& act : g_actorsList) {
    if (act->getName() == name) {
      return act;
    }
  }

  // 見つからなければnullを返す
  return nullptr;
}

/**
 * @brief  特定のアクターを取得する
 * @param  [in] tag 取得したいアクターのダグ
 * @retval アクターを指すスマポを返す
 * @note   見つからなければnullptrを返す
 */
shared_ptr<Actor> FindActor(const int tag) {
  for (auto& act : g_actorsList) {
    if (act->getTag() == tag) {
      return act;
    }
  }

  // 見つからなければnullを返す
  return nullptr;
}

/**
 * @brief  特定のアクターを配列で取得する
 * @param  [in] name 取得したいアクターの名前
 */
list<shared_ptr<Actor>> FindActorsList(const string& name) {
  list<shared_ptr<Actor>> list;
  for (auto& act : g_actorsList) {
    if (act->getName() == name) {
      list.emplace_back(act);
    }
  }

  return list;
}

/**
 * @brief 特定のアクターを配列で取得する
 * @param [in] tag 取得したいアクターのタグ
 */
list<shared_ptr<Actor>> FindActorsList(const int tag) {
  list<shared_ptr<Actor>> list;
  for (auto& act : g_actorsList) {
    if (act->getTag() == tag) {
      list.emplace_back(act);
    }
  }

  return list;
}
<commit_msg>use std::forward_list instead of std::list<commit_after>
/**
 * @file   actorManager.cpp
 * @brief  アクターをマネージメントする関数群が置かれています
 *
 * @author y.akira
 * @date   2016.12.14
 */

#include "precompiled.h"
#include <forward_list>

//! @brief AddActorを使って登録されたアクターはこのリストにまとめられます
static std::forward_list<shared_ptr<Actor>> g_actorsList;

/**
 * @brief     アクターを追加します
 * @param     [in] actor 登録するアクターです
 * @note      先にインスタンス化したアクターを登録することも\n
 *            その場でmake_sharedしても登録可能です
 * @attention 無条件でアクターを登録できる
 */
void AddActor(const shared_ptr<Actor>& actor) {
  g_actorsList.emplace_front(actor);
}

/**
 * @brief 登録されたアクターのupdateを呼びます
 * @brief ActorのsetupやonCollisionEnterもここで呼ばれます
 * @param [in] deltaTime 前のフレームから現在のフレームまでにかかった時間を与えます
 * @note  基本的には各シーンで使用するので
 *        そのシーンのupdateにあるdeltaTimeをそのまま渡してください
 */
void UpdateActors(float deltaTime) {
  for (auto& actor : g_actorsList) {
    // setupを実行していなければsetupを実行する
    if (!actor->hasSetup()) {
      actor->setup();
      if (actor->shouldUpdate()) {
        actor->update(deltaTime);
      }
    }
    else
    // updateが有効になっていればupdateを実行
    if (actor->shouldUpdate()) {
      actor->update(deltaTime);
    }

    // 当たり判定を実行する場合は処理を続ける
    if (!actor->shouldCollision()) { continue; }

    for (auto& c_actor : g_actorsList) {
      // 自分自信との判定は取らない
      if (*actor == *c_actor) { continue; }

      // 相手が判定を取る場合のみ処理を続ける
      if (!c_actor->shouldCollision()) { continue; }

      // 衝突していれば処理を続ける
      if (!actor->getRectangle().intersects(c_actor->getRectangle())) { continue; }

      // どちらも生きていれば処理を続ける
      if (actor->shouldDestroy() || c_actor->shouldDestroy()) { continue; }

      // 各actorに衝突したと伝える
      actor->onCollision(c_actor.get());
      c_actor->onCollision(actor.get());
    }
  }

  // 削除対象のactorは削除する
  g_actorsList.remove_if(
    [] (const shared_ptr<Actor>& act)->bool {
      return act->shouldDestroy();
    }
  );
}

/**
 * @brief 登録されたアクターのdrawを呼びます
 */
void DrawActors() {
  for (auto& actor : g_actorsList) {
    actor->draw();
  }
}

/**
 * @brief 登録されたアクターのGuiを描画します
 */
void DrawActorsGui() {
  for (auto& actor : g_actorsList) {
    if (actor->isHide()) { continue; } // 表示・非表示チェック
    actor->gui();
  }
}

/**
 * @brief 登録されたアクターを全て削除します
 */
void ClearActors() {
  g_actorsList.clear();
}

/**
 * @brief 特定のアクターを削除対象にします
 * @param [in] name 削除したいアクターの名前
 */
void DeleteActors(const string& name) {
  for (auto& act : g_actorsList) {
    if (act->getName() != name) { continue; }
    act->destroy();
  }
}

/**
 * @brief 特定のアクターを削除対象にします
 * @param [in] tag 削除したいアクターのタグ
 */
void DeleteActors(const int tag) {
  for (auto& act : g_actorsList) {
    if (act->getTag() != tag) { continue; }
    act->destroy();
  }
}

/**
 * @brief  特定のアクターを取得する
 * @param  [in] name 取得したいアクターの名前
 * @retval アクターを指すスマポを返す
 * @note   見つからなければnullptrを返す
 */
shared_ptr<Actor> FindActor(const string& name) {
  for (auto& act : g_actorsList) {
    if (act->getName() == name) {
      return act;
    }
  }

  // 見つからなければnullを返す
  return nullptr;
}

/**
 * @brief  特定のアクターを取得する
 * @param  [in] tag 取得したいアクターのダグ
 * @retval アクターを指すスマポを返す
 * @note   見つからなければnullptrを返す
 */
shared_ptr<Actor> FindActor(const int tag) {
  for (auto& act : g_actorsList) {
    if (act->getTag() == tag) {
      return act;
    }
  }

  // 見つからなければnullを返す
  return nullptr;
}

/**
 * @brief  特定のアクターを配列で取得する
 * @param  [in] name 取得したいアクターの名前
 */
list<shared_ptr<Actor>> FindActorsList(const string& name) {
  list<shared_ptr<Actor>> list;
  for (auto& act : g_actorsList) {
    if (act->getName() == name) {
      list.emplace_back(act);
    }
  }

  return list;
}

/**
 * @brief 特定のアクターを配列で取得する
 * @param [in] tag 取得したいアクターのタグ
 */
list<shared_ptr<Actor>> FindActorsList(const int tag) {
  list<shared_ptr<Actor>> list;
  for (auto& act : g_actorsList) {
    if (act->getTag() == tag) {
      list.emplace_back(act);
    }
  }

  return list;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <algorithm>

#include <memory>
#include <boost/variant/variant.hpp>

#include "VariablesAnnotator.hpp"

#include "ast/Program.hpp"

#include "IsConstantVisitor.hpp"
#include "GetTypeVisitor.hpp"
#include "SemanticalException.hpp"
#include "Context.hpp"
#include "GlobalContext.hpp"
#include "FunctionContext.hpp"
#include "Types.hpp"
#include "Variable.hpp"

#include "Compiler.hpp"
#include "Options.hpp"
#include "TypeTransformer.hpp"

#include "VisitorUtils.hpp"
#include "ASTVisitor.hpp"

using namespace eddic;

struct VariablesVisitor : public boost::static_visitor<> {
    AUTO_RECURSE_PROGRAM()
    AUTO_RECURSE_FUNCTION_CALLS()
    AUTO_RECURSE_SIMPLE_LOOPS()
    AUTO_RECURSE_BRANCHES()
    AUTO_RECURSE_BINARY_CONDITION()

    void operator()(ast::FunctionDeclaration& declaration){
        //Add all the parameters to the function context
        for(auto& parameter : declaration.Content->parameters){
            Type type = boost::apply_visitor(TypeTransformer(), parameter.parameterType);

            declaration.Content->context->addParameter(parameter.parameterName, type);
        }

        visit_each(*this, declaration.Content->instructions);
    }

    void operator()(ast::GlobalVariableDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->variableName)) {
            throw SemanticalException("The global Variable " + declaration.Content->variableName + " has already been declared");
        }

        if(!boost::apply_visitor(IsConstantVisitor(), *declaration.Content->value)){
            throw SemanticalException("The value must be constant");
        }

        BaseType baseType = stringToBaseType(declaration.Content->variableType);
        Type type(baseType, declaration.Content->constant);
        declaration.Content->context->addVariable(declaration.Content->variableName, type, *declaration.Content->value);
    }

    void operator()(ast::GlobalArrayDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->arrayName)) {
            throw SemanticalException("The global Variable " + declaration.Content->arrayName + " has already been declared");
        }

        BaseType baseType = stringToBaseType(declaration.Content->arrayType);
        Type type(baseType, declaration.Content->arraySize);

        declaration.Content->context->addVariable(declaration.Content->arrayName, type);
    }

    void operator()(ast::Foreach& foreach){
        if(foreach.Content->context->exists(foreach.Content->variableName)){
            throw SemanticalException("The foreach variable " + foreach.Content->variableName  + " has already been declared");
        }

        foreach.Content->context->addVariable(foreach.Content->variableName, stringToType(foreach.Content->variableType));

        visit_each(*this, foreach.Content->instructions);
    }

    void operator()(ast::ForeachIn& foreach){
        if(foreach.Content->context->exists(foreach.Content->variableName)){
            throw SemanticalException("The foreach variable " + foreach.Content->variableName  + " has already been declared");
        }

        if(!foreach.Content->context->exists(foreach.Content->arrayName)){
            throw SemanticalException("The foreach array " + foreach.Content->arrayName  + " has not been declared");
        }

        static int generated = 0;

        foreach.Content->var = foreach.Content->context->addVariable(foreach.Content->variableName, stringToType(foreach.Content->variableType));
        foreach.Content->arrayVar = foreach.Content->context->getVariable(foreach.Content->arrayName);
        foreach.Content->iterVar = foreach.Content->context->addVariable("foreach_iter" + ++generated, stringToType("int"));

        visit_each(*this, foreach.Content->instructions);
    }

    void operator()(ast::Assignment& assignment){
        if (!assignment.Content->context->exists(assignment.Content->variableName)) {
            throw SemanticalException("Variable " + assignment.Content->variableName + " has not  been declared");
        }

        visit(*this, assignment.Content->value);

        assignment.Content->context->getVariable(assignment.Content->variableName)->addReference();
    }

    void operator()(ast::Return& return_){
        visit(*this, return_.Content->value);
    }

    void operator()(ast::ArrayAssignment& assignment){
        if (!assignment.Content->context->exists(assignment.Content->variableName)) {
            throw SemanticalException("Array " + assignment.Content->variableName + " has not  been declared");
        }

        visit(*this, assignment.Content->indexValue);
        visit(*this, assignment.Content->value);

        assignment.Content->context->getVariable(assignment.Content->variableName)->addReference();
    }

    void operator()(ast::VariableDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->variableName)) {
            throw SemanticalException("Variable " + declaration.Content->variableName + " has already been declared");
        }

        BaseType baseType = stringToBaseType(declaration.Content->variableType);
        Type type(baseType, declaration.Content->const_);

        if(type.isConst()){
            declaration.Content->context->addVariable(declaration.Content->variableName, type, *declaration.Content->value);
        } else {
            declaration.Content->context->addVariable(declaration.Content->variableName, type);
        }

        visit(*this, *declaration.Content->value);
    }

    void operator()(ast::ArrayDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->arrayName)) {
            throw SemanticalException("The variable " + declaration.Content->arrayName + " has already been declared");
        }

        BaseType baseType = stringToBaseType(declaration.Content->arrayType);
        Type type(baseType, declaration.Content->arraySize);

        declaration.Content->context->addVariable(declaration.Content->arrayName, type);
    }

    void operator()(ast::Swap& swap){
        if (swap.Content->lhs == swap.Content->rhs) {
            throw SemanticalException("Cannot swap a variable with itself");
        }

        if (!swap.Content->context->exists(swap.Content->lhs) || !swap.Content->context->exists(swap.Content->rhs)) {
            throw SemanticalException("Variable has not been declared in the swap");
        }

        swap.Content->lhs_var = swap.Content->context->getVariable(swap.Content->lhs);
        swap.Content->rhs_var = swap.Content->context->getVariable(swap.Content->rhs);

        //Reference both variables
        swap.Content->lhs_var->addReference();
        swap.Content->rhs_var->addReference();
    }

    void operator()(ast::VariableValue& variable){
        if (!variable.Content->context->exists(variable.Content->variableName)) {
            throw SemanticalException("Variable " + variable.Content->variableName + " has not been declared");
        }

        //Reference the variable
        variable.Content->var = variable.Content->context->getVariable(variable.Content->variableName);
        variable.Content->var->addReference();
    }

    void operator()(ast::ArrayValue& array){
        if (!array.Content->context->exists(array.Content->arrayName)) {
            throw SemanticalException("Array " + array.Content->arrayName + " has not been declared");
        }

        //Reference the variable
        array.Content->var = array.Content->context->getVariable(array.Content->arrayName);
        array.Content->var->addReference();

        visit(*this, array.Content->indexValue);
    }

    void operator()(ast::ComposedValue& value){
        visit(*this, value.Content->first);

        for_each(value.Content->operations.begin(), value.Content->operations.end(),
            [&](boost::tuple<char, ast::Value>& operation){ visit(*this, operation.get<1>()); });
    }

    void operator()(ast::TerminalNode&){
        //Terminal nodes have no need for variable checking
    }
};

void VariablesAnnotator::annotate(ast::Program& program) const {
    VariablesVisitor visitor;
    visit_non_variant(visitor, program);
}
<commit_msg>Enforce the constness of the value assigned to a constant variable<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <algorithm>

#include <memory>
#include <boost/variant/variant.hpp>

#include "VariablesAnnotator.hpp"

#include "ast/Program.hpp"

#include "IsConstantVisitor.hpp"
#include "GetTypeVisitor.hpp"
#include "SemanticalException.hpp"
#include "Context.hpp"
#include "GlobalContext.hpp"
#include "FunctionContext.hpp"
#include "Types.hpp"
#include "Variable.hpp"

#include "Compiler.hpp"
#include "Options.hpp"
#include "TypeTransformer.hpp"

#include "VisitorUtils.hpp"
#include "ASTVisitor.hpp"

using namespace eddic;

struct VariablesVisitor : public boost::static_visitor<> {
    AUTO_RECURSE_PROGRAM()
    AUTO_RECURSE_FUNCTION_CALLS()
    AUTO_RECURSE_SIMPLE_LOOPS()
    AUTO_RECURSE_BRANCHES()
    AUTO_RECURSE_BINARY_CONDITION()

    void operator()(ast::FunctionDeclaration& declaration){
        //Add all the parameters to the function context
        for(auto& parameter : declaration.Content->parameters){
            Type type = boost::apply_visitor(TypeTransformer(), parameter.parameterType);

            declaration.Content->context->addParameter(parameter.parameterName, type);
        }

        visit_each(*this, declaration.Content->instructions);
    }

    void operator()(ast::GlobalVariableDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->variableName)) {
            throw SemanticalException("The global Variable " + declaration.Content->variableName + " has already been declared");
        }

        if(!boost::apply_visitor(IsConstantVisitor(), *declaration.Content->value)){
            throw SemanticalException("The value must be constant");
        }

        BaseType baseType = stringToBaseType(declaration.Content->variableType);
        Type type(baseType, declaration.Content->constant);
        declaration.Content->context->addVariable(declaration.Content->variableName, type, *declaration.Content->value);
    }

    void operator()(ast::GlobalArrayDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->arrayName)) {
            throw SemanticalException("The global Variable " + declaration.Content->arrayName + " has already been declared");
        }

        BaseType baseType = stringToBaseType(declaration.Content->arrayType);
        Type type(baseType, declaration.Content->arraySize);

        declaration.Content->context->addVariable(declaration.Content->arrayName, type);
    }

    void operator()(ast::Foreach& foreach){
        if(foreach.Content->context->exists(foreach.Content->variableName)){
            throw SemanticalException("The foreach variable " + foreach.Content->variableName  + " has already been declared");
        }

        foreach.Content->context->addVariable(foreach.Content->variableName, stringToType(foreach.Content->variableType));

        visit_each(*this, foreach.Content->instructions);
    }

    void operator()(ast::ForeachIn& foreach){
        if(foreach.Content->context->exists(foreach.Content->variableName)){
            throw SemanticalException("The foreach variable " + foreach.Content->variableName  + " has already been declared");
        }

        if(!foreach.Content->context->exists(foreach.Content->arrayName)){
            throw SemanticalException("The foreach array " + foreach.Content->arrayName  + " has not been declared");
        }

        static int generated = 0;

        foreach.Content->var = foreach.Content->context->addVariable(foreach.Content->variableName, stringToType(foreach.Content->variableType));
        foreach.Content->arrayVar = foreach.Content->context->getVariable(foreach.Content->arrayName);
        foreach.Content->iterVar = foreach.Content->context->addVariable("foreach_iter" + ++generated, stringToType("int"));

        visit_each(*this, foreach.Content->instructions);
    }

    void operator()(ast::Assignment& assignment){
        if (!assignment.Content->context->exists(assignment.Content->variableName)) {
            throw SemanticalException("Variable " + assignment.Content->variableName + " has not  been declared");
        }

        visit(*this, assignment.Content->value);

        assignment.Content->context->getVariable(assignment.Content->variableName)->addReference();
    }

    void operator()(ast::Return& return_){
        visit(*this, return_.Content->value);
    }

    void operator()(ast::ArrayAssignment& assignment){
        if (!assignment.Content->context->exists(assignment.Content->variableName)) {
            throw SemanticalException("Array " + assignment.Content->variableName + " has not  been declared");
        }

        visit(*this, assignment.Content->indexValue);
        visit(*this, assignment.Content->value);

        assignment.Content->context->getVariable(assignment.Content->variableName)->addReference();
    }

    void operator()(ast::VariableDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->variableName)) {
            throw SemanticalException("Variable " + declaration.Content->variableName + " has already been declared");
        }

        visit(*this, *declaration.Content->value);

        BaseType baseType = stringToBaseType(declaration.Content->variableType);
        Type type(baseType, declaration.Content->const_);

        if(type.isConst()){
            if(!boost::apply_visitor(IsConstantVisitor(), *declaration.Content->value)){
                throw SemanticalException("The value must be constant");
            }

            declaration.Content->context->addVariable(declaration.Content->variableName, type, *declaration.Content->value);
        } else {
            declaration.Content->context->addVariable(declaration.Content->variableName, type);
        }

    }

    void operator()(ast::ArrayDeclaration& declaration){
        if (declaration.Content->context->exists(declaration.Content->arrayName)) {
            throw SemanticalException("The variable " + declaration.Content->arrayName + " has already been declared");
        }

        BaseType baseType = stringToBaseType(declaration.Content->arrayType);
        Type type(baseType, declaration.Content->arraySize);

        declaration.Content->context->addVariable(declaration.Content->arrayName, type);
    }

    void operator()(ast::Swap& swap){
        if (swap.Content->lhs == swap.Content->rhs) {
            throw SemanticalException("Cannot swap a variable with itself");
        }

        if (!swap.Content->context->exists(swap.Content->lhs) || !swap.Content->context->exists(swap.Content->rhs)) {
            throw SemanticalException("Variable has not been declared in the swap");
        }

        swap.Content->lhs_var = swap.Content->context->getVariable(swap.Content->lhs);
        swap.Content->rhs_var = swap.Content->context->getVariable(swap.Content->rhs);

        //Reference both variables
        swap.Content->lhs_var->addReference();
        swap.Content->rhs_var->addReference();
    }

    void operator()(ast::VariableValue& variable){
        if (!variable.Content->context->exists(variable.Content->variableName)) {
            throw SemanticalException("Variable " + variable.Content->variableName + " has not been declared");
        }

        //Reference the variable
        variable.Content->var = variable.Content->context->getVariable(variable.Content->variableName);
        variable.Content->var->addReference();
    }

    void operator()(ast::ArrayValue& array){
        if (!array.Content->context->exists(array.Content->arrayName)) {
            throw SemanticalException("Array " + array.Content->arrayName + " has not been declared");
        }

        //Reference the variable
        array.Content->var = array.Content->context->getVariable(array.Content->arrayName);
        array.Content->var->addReference();

        visit(*this, array.Content->indexValue);
    }

    void operator()(ast::ComposedValue& value){
        visit(*this, value.Content->first);

        for_each(value.Content->operations.begin(), value.Content->operations.end(),
            [&](boost::tuple<char, ast::Value>& operation){ visit(*this, operation.get<1>()); });
    }

    void operator()(ast::TerminalNode&){
        //Terminal nodes have no need for variable checking
    }
};

void VariablesAnnotator::annotate(ast::Program& program) const {
    VariablesVisitor visitor;
    visit_non_variant(visitor, program);
}
<|endoftext|>


<commit_before>#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <uv.h>
#include <vector>
#include <windows.h>

#include "../../helper/windows/helper.h"
#include "../../lock.h"
#include "../../log.h"
#include "../../message.h"
#include "../../message_buffer.h"
#include "../worker_platform.h"
#include "../worker_thread.h"
#include "subscription.h"

using std::default_delete;
using std::endl;
using std::make_pair;
using std::map;
using std::move;
using std::ostream;
using std::ostringstream;
using std::pair;
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::vector;
using std::wostringstream;
using std::wstring;

static void CALLBACK command_perform_helper(__in ULONG_PTR payload);

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

class WindowsWorkerPlatform : public WorkerPlatform
{
public:
  WindowsWorkerPlatform(WorkerThread *thread) : WorkerPlatform(thread), thread_handle{0}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
    freeze();
  };

  ~WindowsWorkerPlatform() override { uv_mutex_destroy(&thread_handle_mutex); }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(command_perform_helper, thread_handle, reinterpret_cast<ULONG_PTR>(this));
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(GetCurrentProcess(),  // Source process
        pseudo_handle,  // Source handle
        GetCurrentProcess(),  // Destination process
        &thread_handle,  // Destination handle
        0,  // Desired access
        FALSE,  // Inheritable by new processes
        DUPLICATE_SAME_ACCESS  // options
      );
      if (!success) {
        return windows_error_result<>("Unable to duplicate thread handle");
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    return error_result("listen loop ended unexpectedly");
  }

  Result<bool> handle_add_command(CommandID command,
    ChannelID channel,
    const string &root_path,
    bool recursive) override
  {
    // Convert the path to a wide-character string
    Result<wstring> convr = to_wchar(root_path);
    if (convr.is_error()) return convr.propagate<bool>();
    wstring &root_path_w = convr.get_value();

    // Open a directory handle
    HANDLE root = CreateFileW(root_path_w.c_str(),  // null-terminated wchar file name
      FILE_LIST_DIRECTORY,  // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,  // share mode
      NULL,  // security attributes
      OPEN_EXISTING,  // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,  // flags and attributes
      NULL  // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      return windows_error_result<bool>("Unable to open directory handle");
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path_w, recursive, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return Result<bool>::make_error(msg.str());
    }

    ostream &logline = LOGGER << "Added directory root " << root_path;
    if (!recursive) logline << " (non-recursive)";
    logline << " at channel " << channel << "." << endl;

    Result<bool> schedr = sub->schedule(&event_helper);
    if (schedr.is_error()) return schedr.propagate<bool>();
    if (!schedr.get_value()) {
      LOGGER << "Falling back to polling for watch root " << root_path << "." << endl;

      return emit(Message(CommandPayloadBuilder::add(channel, string(root_path), recursive, 1).build()))
        .propagate(false);
    }

    return ok_result(true);
  }

  Result<bool> handle_remove_command(CommandID command, ChannelID channel) override
  {
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end()) {
      LOGGER << "Channel " << channel << " was already removed." << endl;
      return ok_result(true);
    }

    Result<> r = it->second->stop(command);
    if (r.is_error()) return r.propagate<bool>();

    LOGGER << "Subscription for channel " << channel << " stopped." << endl;
    return ok_result(false);
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription *sub)
  {
    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Subscription termination.
    bool terminate = false;
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Completing termination of channel " << channel << "." << endl;
      terminate = true;
    } else if (sub->is_terminating()) {
      LOGGER << "Filesystem event encountered on terminating channel " << channel << "." << endl;
      terminate = true;
    }
    if (terminate) return remove(sub);

    // Handle errors.
    if (error_code == ERROR_INVALID_PARAMETER) {
      LOGGER << "Attempting to revert to a network-friendly buffer size." << endl;

      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return emit_fatal_error(sub, move(resize));

      return reschedule(sub);
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return reschedule(sub);
    }

    if (error_code != ERROR_SUCCESS) {
      return emit_fatal_error(sub, windows_error_result<>("Completion callback error", error_code));
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = reschedule(sub);

    // Process received events.
    MessageBuffer buffer;
    ChannelMessageBuffer messages(buffer, channel);
    bool old_path_seen = false;
    string old_path;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);

      Result<> pr = process_event_payload(info, sub, messages, old_path_seen, old_path);
      if (pr.is_error()) {
        LOGGER << "Skipping entry " << pr << "." << endl;
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) LOGGER << "Unable to emit messages: " << er << "." << endl;
    }

    return next.propagate_as_void();
  }

private:
  Result<> reschedule(Subscription *sub)
  {
    Result<bool> sch = sub->schedule(&event_helper);
    if (sch.is_error()) return emit_fatal_error(sub, sch.propagate_as_void());

    if (!sch.get_value()) {
      Result<string> root = sub->get_root_path();
      if (root.is_error()) return emit_fatal_error(sub, root.propagate_as_void());

      LOGGER << "Falling back to polling for path " << root.get_value() << " at channel " << sub->get_channel() << "."
             << endl;

      Result<> rem = remove(sub);
      rem &= emit(Message(
        CommandPayloadBuilder::add(sub->get_channel(), move(root.get_value()), sub->is_recursive(), 1).build()));
      return rem;
    }

    return ok_result();
  }

  Result<> remove(Subscription *sub)
  {
    Message response(AckPayload(sub->get_command_id(), sub->get_channel(), true, ""));

    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    subscriptions.erase(it);
    delete sub;

    if (sub->get_command_id() != NULL_COMMAND_ID) {
      return emit(move(response));
    } else {
      return ok_result();
    }
  }

  Result<> emit_fatal_error(Subscription *sub, Result<> &&r)
  {
    assert(r.is_error());

    Result<> out = emit(Message(ErrorPayload(sub->get_channel(), string(r.get_error()), true)));
    out &= remove(sub);
    return out;
  }

  Result<> process_event_payload(PFILE_NOTIFY_INFORMATION info,
    Subscription *sub,
    ChannelMessageBuffer &messages,
    bool &old_path_seen,
    string &old_path)
  {
    EntryKind kind = KIND_UNKNOWN;
    ChannelID channel = sub->get_channel();
    wstring relpathw{info->FileName, info->FileNameLength / sizeof(WCHAR)};
    wstring pathw = sub->make_absolute(move(relpathw));

    if (info->Action != FILE_ACTION_REMOVED && info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
      DWORD attrs = GetFileAttributesW(pathw.c_str());
      if (attrs == INVALID_FILE_ATTRIBUTES) {
        DWORD attr_err = GetLastError();
        if (attr_err != ERROR_FILE_NOT_FOUND && attr_err != ERROR_PATH_NOT_FOUND && attr_err != ERROR_ACCESS_DENIED) {
          LOGGER << windows_error_result<>("GetFileAttributesW failed", attr_err) << "." << endl;
        }
      } else if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
        kind = KIND_DIRECTORY;
      } else {
        kind = KIND_FILE;
      }
      // TODO check against FILE_ATTRIBUTE_REPARSE_POINT to identify symlinks
    }

    Result<string> u8r = to_utf8(pathw);
    if (u8r.is_error()) {
      LOGGER << "Unable to convert path to utf-8: " << u8r << "." << endl;
      return ok_result();
    }
    string &path = u8r.get_value();

    switch (info->Action) {
      case FILE_ACTION_ADDED: messages.created(move(path), kind); break;
      case FILE_ACTION_MODIFIED: messages.modified(move(path), kind); break;
      case FILE_ACTION_REMOVED: messages.deleted(move(path), kind); break;
      case FILE_ACTION_RENAMED_OLD_NAME:
        old_path_seen = true;
        old_path = move(path);
        break;
      case FILE_ACTION_RENAMED_NEW_NAME:
        if (old_path_seen) {
          // Old name received first
          messages.renamed(move(old_path), move(path), kind);
          old_path_seen = false;
        } else {
          // No old name. Treat it as a creation
          messages.created(move(path), kind);
        }
        break;
      default: {
        ostringstream out;
        out << "Unexpected action " << info->Action << " reported by ReadDirectoryChangesW for " << path;
        return error_result(out.str());
      } break;
    }

    return ok_result();
  }

  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription *> subscriptions;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform *>(payload);
  platform->handle_commands();
}

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription *>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}
<commit_msg>Use the RecentFileCache to read and store entry kinds on Windows<commit_after>#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <uv.h>
#include <vector>
#include <windows.h>

#include "../../helper/windows/helper.h"
#include "../../lock.h"
#include "../../log.h"
#include "../../message.h"
#include "../../message_buffer.h"
#include "../recent_file_cache.h"
#include "../worker_platform.h"
#include "../worker_thread.h"
#include "subscription.h"

using std::default_delete;
using std::endl;
using std::make_pair;
using std::map;
using std::move;
using std::ostream;
using std::ostringstream;
using std::pair;
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::vector;
using std::wostringstream;
using std::wstring;

const size_t DEFAULT_CACHE_SIZE = 4096;

const size_t DEFAULT_CACHE_PREPOPULATION = 1024;

void CALLBACK command_perform_helper(__in ULONG_PTR payload);

void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

class WindowsWorkerPlatform : public WorkerPlatform
{
public:
  WindowsWorkerPlatform(WorkerThread *thread) : WorkerPlatform(thread), thread_handle{0}, cache{DEFAULT_CACHE_SIZE}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
    freeze();
  };

  ~WindowsWorkerPlatform() override { uv_mutex_destroy(&thread_handle_mutex); }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(command_perform_helper, thread_handle, reinterpret_cast<ULONG_PTR>(this));
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(GetCurrentProcess(),  // Source process
        pseudo_handle,  // Source handle
        GetCurrentProcess(),  // Destination process
        &thread_handle,  // Destination handle
        0,  // Desired access
        FALSE,  // Inheritable by new processes
        DUPLICATE_SAME_ACCESS  // options
      );
      if (!success) {
        return windows_error_result<>("Unable to duplicate thread handle");
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    return error_result("listen loop ended unexpectedly");
  }

  Result<bool> handle_add_command(CommandID command,
    ChannelID channel,
    const string &root_path,
    bool recursive) override
  {
    // Convert the path to a wide-character string
    Result<wstring> convr = to_wchar(root_path);
    if (convr.is_error()) return convr.propagate<bool>();
    wstring &root_path_w = convr.get_value();

    // Open a directory handle
    HANDLE root = CreateFileW(root_path_w.c_str(),  // null-terminated wchar file name
      FILE_LIST_DIRECTORY,  // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,  // share mode
      NULL,  // security attributes
      OPEN_EXISTING,  // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,  // flags and attributes
      NULL  // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      return windows_error_result<bool>("Unable to open directory handle");
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path_w, recursive, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return Result<bool>::make_error(msg.str());
    }

    ostream &logline = LOGGER << "Added directory root " << root_path;
    if (!recursive) logline << " (non-recursive)";
    logline << " at channel " << channel << "." << endl;

    Result<bool> schedr = sub->schedule(&event_helper);
    if (schedr.is_error()) return schedr.propagate<bool>();
    if (!schedr.get_value()) {
      LOGGER << "Falling back to polling for watch root " << root_path << "." << endl;

      return emit(Message(CommandPayloadBuilder::add(channel, string(root_path), recursive, 1).build()))
        .propagate(false);
    }

    cache.prepopulate(root_path, DEFAULT_CACHE_PREPOPULATION);

    return ok_result(true);
  }

  Result<bool> handle_remove_command(CommandID command, ChannelID channel) override
  {
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end()) {
      LOGGER << "Channel " << channel << " was already removed." << endl;
      return ok_result(true);
    }

    Result<> r = it->second->stop(command);
    if (r.is_error()) return r.propagate<bool>();

    LOGGER << "Subscription for channel " << channel << " stopped." << endl;
    return ok_result(false);
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription *sub)
  {
    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Subscription termination.
    bool terminate = false;
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Completing termination of channel " << channel << "." << endl;
      terminate = true;
    } else if (sub->is_terminating()) {
      LOGGER << "Filesystem event encountered on terminating channel " << channel << "." << endl;
      terminate = true;
    }
    if (terminate) return remove(sub);

    // Handle errors.
    if (error_code == ERROR_INVALID_PARAMETER) {
      LOGGER << "Attempting to revert to a network-friendly buffer size." << endl;

      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return emit_fatal_error(sub, move(resize));

      return reschedule(sub);
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return reschedule(sub);
    }

    if (error_code != ERROR_SUCCESS) {
      return emit_fatal_error(sub, windows_error_result<>("Completion callback error", error_code));
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = reschedule(sub);

    // Process received events.
    MessageBuffer buffer;
    ChannelMessageBuffer messages(buffer, channel);
    size_t num_events = 0;
    bool old_path_seen = false;
    string old_path;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);
      num_events++;

      Result<> pr = process_event_payload(info, sub, messages, old_path_seen, old_path);
      if (pr.is_error()) {
        LOGGER << "Skipping entry " << pr << "." << endl;
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    cache.apply();
    cache.prune();

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      } else {
        LOGGER << "Filesystem event batch of size " << num_events << " completed. "
               << plural(messages.size(), "message") << " produced." << endl;
      }
    }

    return next.propagate_as_void();
  }

private:
  Result<> reschedule(Subscription *sub)
  {
    Result<bool> sch = sub->schedule(&event_helper);
    if (sch.is_error()) return emit_fatal_error(sub, sch.propagate_as_void());

    if (!sch.get_value()) {
      Result<string> root = sub->get_root_path();
      if (root.is_error()) return emit_fatal_error(sub, root.propagate_as_void());

      LOGGER << "Falling back to polling for path " << root.get_value() << " at channel " << sub->get_channel() << "."
             << endl;

      Result<> rem = remove(sub);
      rem &= emit(Message(
        CommandPayloadBuilder::add(sub->get_channel(), move(root.get_value()), sub->is_recursive(), 1).build()));
      return rem;
    }

    return ok_result();
  }

  Result<> remove(Subscription *sub)
  {
    Message response(AckPayload(sub->get_command_id(), sub->get_channel(), true, ""));

    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    subscriptions.erase(it);
    delete sub;

    if (sub->get_command_id() != NULL_COMMAND_ID) {
      return emit(move(response));
    } else {
      return ok_result();
    }
  }

  Result<> emit_fatal_error(Subscription *sub, Result<> &&r)
  {
    assert(r.is_error());

    Result<> out = emit(Message(ErrorPayload(sub->get_channel(), string(r.get_error()), true)));
    out &= remove(sub);
    return out;
  }

  Result<> process_event_payload(PFILE_NOTIFY_INFORMATION info,
    Subscription *sub,
    ChannelMessageBuffer &messages,
    bool &old_path_seen,
    string &old_path)
  {
    ChannelID channel = sub->get_channel();
    wstring relpathw{info->FileName, info->FileNameLength / sizeof(WCHAR)};
    wstring pathw = sub->make_absolute(move(relpathw));

    Result<string> u8r = to_utf8(pathw);
    if (u8r.is_error()) {
      LOGGER << "Unable to convert path to utf-8: " << u8r << "." << endl;
      return ok_result();
    }
    string &path = u8r.get_value();

    shared_ptr<StatResult> stat = cache.current_at_path(path, false, false);
    if (stat->is_absent()) {
      stat = cache.former_at_path(path, false, false);
    }
    EntryKind kind = stat->get_entry_kind();

    switch (info->Action) {
      case FILE_ACTION_ADDED: messages.created(move(path), kind); break;
      case FILE_ACTION_MODIFIED: messages.modified(move(path), kind); break;
      case FILE_ACTION_REMOVED: messages.deleted(move(path), kind); break;
      case FILE_ACTION_RENAMED_OLD_NAME:
        old_path_seen = true;
        old_path = move(path);
        break;
      case FILE_ACTION_RENAMED_NEW_NAME:
        if (old_path_seen) {
          // Old name received first
          messages.renamed(move(old_path), move(path), kind);
          old_path_seen = false;
        } else {
          // No old name. Treat it as a creation
          messages.created(move(path), kind);
        }
        break;
      default:
        ostringstream out;
        out << "Unexpected action " << info->Action << " reported by ReadDirectoryChangesW for " << path;
        return error_result(out.str());
        break;
    }

    return ok_result();
  }

  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription *> subscriptions;

  RecentFileCache cache;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform *>(payload);
  platform->handle_commands();
}

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription *>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}
<|endoftext|>