<commit_before>#include "communique/Server.h"

#define _WEBSOCKETPP_CPP11_STL_ // Make sure websocketpp uses c++11 features in preference to boost ones
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio.hpp>
#include <list>
#include "communique/impl/Connection.h"

//
// Declaration of the pimple
//
namespace communique
{
	class ServerPrivateMembers
	{
	public:
		typedef websocketpp::server<websocketpp::config::asio_tls> server_type;
		server_type server_;
		std::thread ioThread_;
		std::list< std::shared_ptr<communique::impl::Connection> > currentConnections_;
		mutable std::mutex currentConnectionsMutex_;
		std::string certificateChainFile_;
		std::string privateKeyFile_;
		std::string verifyFile_;
		std::string diffieHellmanParamsFile_;

		websocketpp::lib::shared_ptr<boost::asio::ssl::context> on_tls_init( websocketpp::connection_hdl hdl );
		//void on_message( websocketpp::connection_hdl hdl, server_type::message_ptr msg );
		void on_http( websocketpp::connection_hdl hdl );
		void on_open( websocketpp::connection_hdl hdl );
		void on_close( websocketpp::connection_hdl hdl );
		void on_interrupt( websocketpp::connection_hdl hdl );

		std::function<void(const std::string&)> defaultInfoHandler_;
		std::function<void(const std::string&,communique::IConnection*)> defaultInfoHandlerAdvanced_;
		std::function<std::string(const std::string&)> defaultRequestHandler_;
		std::function<std::string(const std::string&,communique::IConnection*)> defaultRequestHandlerAdvanced_;
	};
}

communique::Server::Server()
	: pImple_( new ServerPrivateMembers )
{
	pImple_->server_.set_access_channels(websocketpp::log::alevel::none);
	//pImple_->server_.set_error_channels(websocketpp::log::elevel::all ^ websocketpp::log::elevel::info);
	pImple_->server_.set_error_channels(websocketpp::log::elevel::none);
	pImple_->server_.set_tls_init_handler( std::bind( &ServerPrivateMembers::on_tls_init, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.set_http_handler( std::bind( &ServerPrivateMembers::on_http, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.init_asio();
	//pImple_->server_.set_message_handler( std::bind( &ServerPrivateMembers::on_message, pImple_.get(), std::placeholders::_1, std::placeholders::_2 ) );
	pImple_->server_.set_open_handler( std::bind( &ServerPrivateMembers::on_open, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.set_close_handler( std::bind( &ServerPrivateMembers::on_close, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.set_interrupt_handler( std::bind( &ServerPrivateMembers::on_interrupt, pImple_.get(), std::placeholders::_1 ) );
}

communique::Server::~Server()
{
	try
	{
		stop();
	}
	catch(...) { /* Make sure no exceptions propagate out */ }
}

bool communique::Server::listen( size_t port )
{
	try
	{
		if( pImple_->ioThread_.joinable() ) stop(); // If already running stop the current IO

		pImple_->server_.listen(port);
		pImple_->server_.start_accept();

		pImple_->ioThread_=std::thread( &ServerPrivateMembers::server_type::run, &pImple_->server_ );

		return true;
	}
	catch( std::exception& error )
	{
		throw;
	}
	catch(...)
	{
		throw std::runtime_error( "Unknown exception in communique::Server::listen" );
	}
}

void communique::Server::stop()
{
	if( pImple_->server_.is_listening() ) pImple_->server_.stop_listening();

	{ // Block to limit lifetime of the lock_guard
		std::lock_guard<std::mutex> myMutex( pImple_->currentConnectionsMutex_ );
		for( auto& pConnection : pImple_->currentConnections_ )
		{
			//pImple_->server_.get_con_from_hdl(connection_hdl)->get_raw_socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
			pConnection->close();
		}
	}

	if( pImple_->ioThread_.joinable() ) pImple_->ioThread_.join();
}

void communique::Server::setCertificateChainFile( const std::string& filename )
{
	pImple_->certificateChainFile_=filename;
}

void communique::Server::setPrivateKeyFile( const std::string& filename )
{
	pImple_->privateKeyFile_=filename;
}

void communique::Server::setVerifyFile( const std::string& filename )
{
	pImple_->verifyFile_=filename;
}

void communique::Server::setDiffieHellmanParamsFile( const std::string& filename )
{
	pImple_->diffieHellmanParamsFile_=filename;
}

void communique::Server::setDefaultInfoHandler( std::function<void(const std::string&)> infoHandler )
{
	pImple_->defaultInfoHandler_=infoHandler;
	pImple_->defaultInfoHandlerAdvanced_=nullptr;
}

void communique::Server::setDefaultInfoHandler( std::function<void(const std::string&,communique::IConnection*)> infoHandler )
{
	pImple_->defaultInfoHandler_=nullptr;
	pImple_->defaultInfoHandlerAdvanced_=infoHandler;
}

void communique::Server::setDefaultRequestHandler( std::function<const std::string(const std::string&)> requestHandler )
{
	pImple_->defaultRequestHandler_=requestHandler;
	pImple_->defaultRequestHandlerAdvanced_=nullptr;
}

void communique::Server::setDefaultRequestHandler( std::function<const std::string(const std::string&,communique::IConnection*)> requestHandler )
{
	pImple_->defaultRequestHandler_=nullptr;
	pImple_->defaultRequestHandlerAdvanced_=requestHandler;
}

std::vector<std::shared_ptr<communique::IConnection> > communique::Server::currentConnections()
{
	std::lock_guard<std::mutex> myMutex( pImple_->currentConnectionsMutex_ );
	return std::vector<std::shared_ptr<communique::IConnection> >( pImple_->currentConnections_.begin(), pImple_->currentConnections_.end() );
}

void communique::Server::setErrorLogLocation( std::ostream& outputStream )
{
	pImple_->server_.get_elog().set_ostream( &outputStream );
}

void communique::Server::setErrorLogLevel( uint32_t level )
{
	pImple_->server_.set_error_channels(level);
}

void communique::Server::setAccessLogLocation( std::ostream& outputStream )
{
	pImple_->server_.get_alog().set_ostream( &outputStream );
}

void communique::Server::setAccessLogLevel( uint32_t level )
{
	pImple_->server_.set_access_channels(level);
}

websocketpp::lib::shared_ptr<boost::asio::ssl::context> communique::ServerPrivateMembers::on_tls_init( websocketpp::connection_hdl hdl )
{
	websocketpp::lib::shared_ptr<boost::asio::ssl::context> pContext( new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) );
	pContext->set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use );
	//pContext->set_password_callback( boost::bind( &server::get_password, this ) );
	if( !certificateChainFile_.empty() ) pContext->use_certificate_chain_file( certificateChainFile_ );
	if( !privateKeyFile_.empty() ) pContext->use_private_key_file( privateKeyFile_, boost::asio::ssl::context::pem );
	if( !verifyFile_.empty() )
	{
		pContext->set_verify_mode( boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert );
		pContext->load_verify_file( verifyFile_ );
	}
	if( !diffieHellmanParamsFile_.empty() ) pContext->use_tmp_dh_file( diffieHellmanParamsFile_ );

	return pContext;
}

void communique::ServerPrivateMembers::on_http( websocketpp::connection_hdl hdl )
{
	server_type::connection_ptr con = server_.get_con_from_hdl(hdl);
	//con->set_body("Hello World!\n");
	con->set_status(websocketpp::http::status_code::ok);
}

void communique::ServerPrivateMembers::on_open( websocketpp::connection_hdl hdl )
{
	std::lock_guard<std::mutex> myMutex( currentConnectionsMutex_ );
	currentConnections_.emplace_back( new communique::impl::Connection( server_.get_con_from_hdl(hdl), defaultInfoHandler_, defaultRequestHandler_ ) );
	// If "advanced" handlers have beed set (just has IConnection* as additional argument)
	// change the handler to these.
	if( defaultInfoHandlerAdvanced_ ) currentConnections_.back()->setInfoHandler(defaultInfoHandlerAdvanced_);
	if( defaultRequestHandlerAdvanced_ ) currentConnections_.back()->setRequestHandler(defaultRequestHandlerAdvanced_);
}

void communique::ServerPrivateMembers::on_close( websocketpp::connection_hdl hdl )
{
	std::lock_guard<std::mutex> myMutex( currentConnectionsMutex_ );
	auto pRawConnection=server_.get_con_from_hdl(hdl);
	auto findResult=std::find_if( currentConnections_.begin(), currentConnections_.end(), [&pRawConnection](std::shared_ptr<communique::impl::Connection>& other){return pRawConnection==other->underlyingPointer();} );
	if( findResult!=currentConnections_.end() ) currentConnections_.erase( findResult );
	else std::cout << "Couldn't find connection to remove" << std::endl;

}

void communique::ServerPrivateMembers::on_interrupt( websocketpp::connection_hdl hdl )
{
	std::cout << "Connection has been interrupted" << std::endl;
//	auto findResult=std::find_if( currentConnections_.begin(), currentConnections_.end(), [&hdl](websocketpp::connection_hdl& other){return !other.owner_before(hdl) && !hdl.owner_before(other);} );
//	if( findResult!=currentConnections_.end() ) currentConnections_.erase( findResult );
}
<commit_msg>Add a certificate verification function that adds more log messages about handshakes<commit_after>#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "communique/Server.h"

#define _WEBSOCKETPP_CPP11_STL_ // Make sure websocketpp uses c++11 features in preference to boost ones
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio.hpp>
#include <list>
#include "communique/impl/Connection.h"
#include "communique/impl/openssltools/X509CertificateDetails.h"

//
// Declaration of the pimple
//
namespace communique
{
	class ServerPrivateMembers
	{
	public:
		typedef websocketpp::server<websocketpp::config::asio_tls> server_type;
		server_type server_;
		std::thread ioThread_;
		std::list< std::shared_ptr<communique::impl::Connection> > currentConnections_;
		mutable std::mutex currentConnectionsMutex_;
		std::string certificateChainFile_;
		std::string privateKeyFile_;
		std::string verifyFile_;
		std::string diffieHellmanParamsFile_;

		websocketpp::lib::shared_ptr<boost::asio::ssl::context> on_tls_init( websocketpp::connection_hdl hdl );
		//void on_message( websocketpp::connection_hdl hdl, server_type::message_ptr msg );
		void on_http( websocketpp::connection_hdl hdl );
		void on_open( websocketpp::connection_hdl hdl );
		void on_close( websocketpp::connection_hdl hdl );
		void on_interrupt( websocketpp::connection_hdl hdl );
		bool verify_certificate( bool preverified, boost::asio::ssl::verify_context& context );

		std::function<void(const std::string&)> defaultInfoHandler_;
		std::function<void(const std::string&,communique::IConnection*)> defaultInfoHandlerAdvanced_;
		std::function<std::string(const std::string&)> defaultRequestHandler_;
		std::function<std::string(const std::string&,communique::IConnection*)> defaultRequestHandlerAdvanced_;
	};
}

communique::Server::Server()
	: pImple_( new ServerPrivateMembers )
{
	pImple_->server_.set_access_channels(websocketpp::log::alevel::none);
	//pImple_->server_.set_error_channels(websocketpp::log::elevel::all ^ websocketpp::log::elevel::info);
	pImple_->server_.set_error_channels(websocketpp::log::elevel::none);
	pImple_->server_.set_tls_init_handler( std::bind( &ServerPrivateMembers::on_tls_init, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.set_http_handler( std::bind( &ServerPrivateMembers::on_http, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.init_asio();
	//pImple_->server_.set_message_handler( std::bind( &ServerPrivateMembers::on_message, pImple_.get(), std::placeholders::_1, std::placeholders::_2 ) );
	pImple_->server_.set_open_handler( std::bind( &ServerPrivateMembers::on_open, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.set_close_handler( std::bind( &ServerPrivateMembers::on_close, pImple_.get(), std::placeholders::_1 ) );
	pImple_->server_.set_interrupt_handler( std::bind( &ServerPrivateMembers::on_interrupt, pImple_.get(), std::placeholders::_1 ) );
}

communique::Server::~Server()
{
	try
	{
		stop();
	}
	catch(...) { /* Make sure no exceptions propagate out */ }
}

bool communique::Server::listen( size_t port )
{
	try
	{
		if( pImple_->ioThread_.joinable() ) stop(); // If already running stop the current IO

		pImple_->server_.listen(port);
		pImple_->server_.start_accept();

		pImple_->ioThread_=std::thread( &ServerPrivateMembers::server_type::run, &pImple_->server_ );

		return true;
	}
	catch( std::exception& error )
	{
		throw;
	}
	catch(...)
	{
		throw std::runtime_error( "Unknown exception in communique::Server::listen" );
	}
}

void communique::Server::stop()
{
	if( pImple_->server_.is_listening() ) pImple_->server_.stop_listening();

	{ // Block to limit lifetime of the lock_guard
		std::lock_guard<std::mutex> myMutex( pImple_->currentConnectionsMutex_ );
		for( auto& pConnection : pImple_->currentConnections_ )
		{
			//pImple_->server_.get_con_from_hdl(connection_hdl)->get_raw_socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
			pConnection->close();
		}
	}

	if( pImple_->ioThread_.joinable() ) pImple_->ioThread_.join();
}

void communique::Server::setCertificateChainFile( const std::string& filename )
{
	pImple_->certificateChainFile_=filename;
}

void communique::Server::setPrivateKeyFile( const std::string& filename )
{
	pImple_->privateKeyFile_=filename;
}

void communique::Server::setVerifyFile( const std::string& filename )
{
	pImple_->verifyFile_=filename;
}

void communique::Server::setDiffieHellmanParamsFile( const std::string& filename )
{
	pImple_->diffieHellmanParamsFile_=filename;
}

void communique::Server::setDefaultInfoHandler( std::function<void(const std::string&)> infoHandler )
{
	pImple_->defaultInfoHandler_=infoHandler;
	pImple_->defaultInfoHandlerAdvanced_=nullptr;
}

void communique::Server::setDefaultInfoHandler( std::function<void(const std::string&,communique::IConnection*)> infoHandler )
{
	pImple_->defaultInfoHandler_=nullptr;
	pImple_->defaultInfoHandlerAdvanced_=infoHandler;
}

void communique::Server::setDefaultRequestHandler( std::function<const std::string(const std::string&)> requestHandler )
{
	pImple_->defaultRequestHandler_=requestHandler;
	pImple_->defaultRequestHandlerAdvanced_=nullptr;
}

void communique::Server::setDefaultRequestHandler( std::function<const std::string(const std::string&,communique::IConnection*)> requestHandler )
{
	pImple_->defaultRequestHandler_=nullptr;
	pImple_->defaultRequestHandlerAdvanced_=requestHandler;
}

std::vector<std::shared_ptr<communique::IConnection> > communique::Server::currentConnections()
{
	std::lock_guard<std::mutex> myMutex( pImple_->currentConnectionsMutex_ );
	return std::vector<std::shared_ptr<communique::IConnection> >( pImple_->currentConnections_.begin(), pImple_->currentConnections_.end() );
}

void communique::Server::setErrorLogLocation( std::ostream& outputStream )
{
	pImple_->server_.get_elog().set_ostream( &outputStream );
}

void communique::Server::setErrorLogLevel( uint32_t level )
{
	pImple_->server_.set_error_channels(level);
}

void communique::Server::setAccessLogLocation( std::ostream& outputStream )
{
	pImple_->server_.get_alog().set_ostream( &outputStream );
}

void communique::Server::setAccessLogLevel( uint32_t level )
{
	pImple_->server_.set_access_channels(level);
}

websocketpp::lib::shared_ptr<boost::asio::ssl::context> communique::ServerPrivateMembers::on_tls_init( websocketpp::connection_hdl hdl )
{
	websocketpp::lib::shared_ptr<boost::asio::ssl::context> pContext( new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) );
	pContext->set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use );
	//pContext->set_password_callback( boost::bind( &server::get_password, this ) );
	if( !certificateChainFile_.empty() ) pContext->use_certificate_chain_file( certificateChainFile_ );
	if( !privateKeyFile_.empty() ) pContext->use_private_key_file( privateKeyFile_, boost::asio::ssl::context::pem );
	if( !verifyFile_.empty() )
	{
		pContext->set_verify_mode( boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert );
		pContext->load_verify_file( verifyFile_ );
		pContext->set_verify_callback( std::bind( &ServerPrivateMembers::verify_certificate, this, std::placeholders::_1, std::placeholders::_2 ) );
	}
	else pContext->set_verify_mode( boost::asio::ssl::verify_none );
	if( !diffieHellmanParamsFile_.empty() ) pContext->use_tmp_dh_file( diffieHellmanParamsFile_ );

	return pContext;
}

bool communique::ServerPrivateMembers::verify_certificate( bool preverified, boost::asio::ssl::verify_context& context )
{
	constexpr int maxDepth=8;
	constexpr bool verbose=true;

	X509_STORE_CTX *pRawContext=context.native_handle();

	int errorCode=X509_STORE_CTX_get_error( pRawContext );
	int depth=X509_STORE_CTX_get_error_depth( pRawContext );

	X509* pCurrentCertificate=X509_STORE_CTX_get_current_cert( pRawContext );
	communique::impl::openssltools::X509CertificateDetails certificateDetails(pCurrentCertificate);

	/*
	 * Catch a too long certificate chain. The depth limit set using
	 * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so
	 * that whenever the "depth>verify_depth" condition is met, we
	 * have violated the limit and want to log this error condition.
	 * We must do it here, because the CHAIN_TOO_LONG error would not
	 * be found explicitly; only errors introduced by cutting off the
	 * additional certificates would be logged.
	 */
	if( depth > maxDepth )
	{
		preverified=0;
		errorCode=X509_V_ERR_CERT_CHAIN_TOO_LONG;
		X509_STORE_CTX_set_error( pRawContext, errorCode );
	}

	if( !preverified )
	{
		std::string errorMessage="X509 verification error:"+std::to_string(errorCode)+":"+X509_verify_cert_error_string(errorCode)+":depth="+std::to_string(depth)+":subject="+certificateDetails.subject();
		if( errorCode==X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT ) errorMessage+=":issuer="+certificateDetails.issuer();
		server_.get_alog().write( websocketpp::log::alevel::debug_handshake, errorMessage );
	}
	else if( verbose )
	{
		server_.get_alog().write( websocketpp::log::alevel::debug_handshake, "X509 verification depth="+std::to_string(depth)+":"+certificateDetails.subject() );
	}

	return preverified;
}

void communique::ServerPrivateMembers::on_http( websocketpp::connection_hdl hdl )
{
	server_type::connection_ptr con = server_.get_con_from_hdl(hdl);
	//con->set_body("Hello World!\n");
	con->set_status(websocketpp::http::status_code::ok);
}

void communique::ServerPrivateMembers::on_open( websocketpp::connection_hdl hdl )
{
	std::lock_guard<std::mutex> myMutex( currentConnectionsMutex_ );
	currentConnections_.emplace_back( new communique::impl::Connection( server_.get_con_from_hdl(hdl), defaultInfoHandler_, defaultRequestHandler_ ) );
	// If "advanced" handlers have beed set (just has IConnection* as additional argument)
	// change the handler to these.
	if( defaultInfoHandlerAdvanced_ ) currentConnections_.back()->setInfoHandler(defaultInfoHandlerAdvanced_);
	if( defaultRequestHandlerAdvanced_ ) currentConnections_.back()->setRequestHandler(defaultRequestHandlerAdvanced_);
}

void communique::ServerPrivateMembers::on_close( websocketpp::connection_hdl hdl )
{
	std::lock_guard<std::mutex> myMutex( currentConnectionsMutex_ );
	auto pRawConnection=server_.get_con_from_hdl(hdl);
	auto findResult=std::find_if( currentConnections_.begin(), currentConnections_.end(), [&pRawConnection](std::shared_ptr<communique::impl::Connection>& other){return pRawConnection==other->underlyingPointer();} );
	if( findResult!=currentConnections_.end() ) currentConnections_.erase( findResult );
	else std::cout << "Couldn't find connection to remove" << std::endl;

}

void communique::ServerPrivateMembers::on_interrupt( websocketpp::connection_hdl hdl )
{
	std::cout << "Connection has been interrupted" << std::endl;
//	auto findResult=std::find_if( currentConnections_.begin(), currentConnections_.end(), [&hdl](websocketpp::connection_hdl& other){return !other.owner_before(hdl) && !hdl.owner_before(other);} );
//	if( findResult!=currentConnections_.end() ) currentConnections_.erase( findResult );
}
<|endoftext|>


<commit_before>#include <iostream>

#include "SqliteTools.h"

bool SqliteTools::createTable( sqlite3 *db, const char* request )
{
    sqlite3_stmt* stmt;
    int res = sqlite3_prepare_v2( db, request, -1, &stmt, NULL );
    if ( res != SQLITE_OK )
    {
        std::cerr << "Failed to execute request: " << request << std::endl;
        std::cerr << sqlite3_errmsg( db ) << std::endl;
        return false;
    }
    res = sqlite3_step( stmt );
    while ( res != SQLITE_DONE && res != SQLITE_ERROR )
        res = sqlite3_step( stmt );
    if ( res == SQLITE_ERROR )
    {
        std::cerr << "Failed to execute request: " << request << std::endl;
        std::cerr << sqlite3_errmsg( db ) << std::endl;
    }
    return res == SQLITE_DONE;
}
<commit_msg>SqliteTools: Fix memory leak<commit_after>#include <iostream>

#include "SqliteTools.h"

bool SqliteTools::createTable( sqlite3 *db, const char* request )
{
    auto stmt = executeRequest( db, request );
    int res = sqlite3_step( stmt.get() );
    while ( res != SQLITE_DONE && res != SQLITE_ERROR )
        res = sqlite3_step( stmt.get() );
    if ( res == SQLITE_ERROR )
    {
        std::cerr << "Failed to execute request: " << request << std::endl;
        std::cerr << sqlite3_errmsg( db ) << std::endl;
    }
    return res == SQLITE_DONE;
}
<|endoftext|>


<commit_before>/**
 * \file StatusFrame.cpp
 * \brief Main Windows Class
 */

#include "StatusFrame.h"

BEGIN_EVENT_TABLE(StatusFrame,wxFrame)
    EVT_MENU(-1, StatusFrame::SelectDevice)
    EVT_CLOSE(StatusFrame::OnClose)
END_EVENT_TABLE()

StatusFrame::StatusFrame(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style)
: wxFrame(parent, id, title, position, size, style) {
    /**
    *   Constructor for the Main frame.
    */
    deviceId = "Please select a device from the menu.";

    CreateGUIControls();
}

StatusFrame::~StatusFrame() {
    /**
    *   Destructor for the Main form.
    */
}

void StatusFrame::Update() {
    /**
     * Updates the GUI with new database information
     */
    wxMenuBar *menubar = new wxMenuBar;
    wxMenu *devices = new wxMenu;

    deviceIds = DB_getAllDevices();

    menubar->Append(devices, wxT("&Devices"));

    for(unsigned int i = 0; i < deviceIds.size(); i++) {
        devices->Append(10000+i, deviceIds[i]);
    }
    SetMenuBar(menubar);

    map<string, string> gps_info;
    gps_info = DB_getMostRecentGPS(atoi(deviceId.c_str()));

    wxString info = wxString::Format(wxT(""
        "Device: %s\n"
        "
\n"
        "Location\n
"
        "Latitude: %s\n
"
        "Longitude: %s\n
"
        "Altitude (M): %s\n
"
        "Altitude (Ft): \n
"
        "Speed (Knots): %s\n
"
        "Speed (M/S): \n
"
        "Bearing: %s\n
"
        "Climb: \n
"
        "GPS Status: %s\n"
        "\n
"
        "Status\n
"
        "Battery 1 (V): \n
"
        "Battery 2 (V): \n
"
        "Buss (V): \n
"
        "Signal (%%): \n
"
        "Temperature Int. (c): \n
"
        "Temperature Ext. (c): \n
"
        "Pressure (HPA): \n
"
        "RH (%%): \n"
        ),
        deviceId.c_str(),
        gps_info["Latitude"].c_str(),
        gps_info["Longitude"].c_str(),
        gps_info["Altitude"].c_str(),
        gps_info["Spd"].c_str(),
        gps_info["Hdg"].c_str(),
        gps_info["Status"].c_str()
        );

    deviceInfo->SetPage(info);
}

void StatusFrame::CreateGUIControls() {
   /**
    *   Creates all of the GUI controls on the main form.
    */

    // Set window properties and title bar
    SetTitle(wxT("HawkEye"));
    SetIcon(wxNullIcon);

    mainPanel = new wxPanel(this, wxID_ANY);
    mainSizer = new wxBoxSizer(wxVERTICAL);
    mainPanel->SetSizer(mainSizer);

    deviceInfo = new wxHtmlWindow(mainPanel);
    mainSizer->Add(deviceInfo, 1, wxEXPAND | wxALL);

    Update();

    //wxComboBox *device_dropdown = new wxComboBox(panel, wxID_ANY, wxT("Device"), wxDefaultPosition, wxDefaultSize);
    //sizer->Add(device_dropdown, 1, wxEXPAND | wxALL);

    /*
	// Create a mpFXYVector layer
	mpFXYVector* vectorLayer = new mpFXYVector(_(""));
	// Create two vectors for x,y and fill them with data
	std::vector<double> vectorx, vectory;
	double xcoord;
	for (unsigned int p = 0; p < 100; p++) {
		xcoord = (double)(p*1000.0);
		vectorx.push_back(25-0.001*pow(xcoord/1000, 2));
		vectory.push_back(xcoord);
	}
	vectorLayer->SetData(vectorx, vectory);
	vectorLayer->SetContinuity(true);
	wxPen vectorpen(*wxBLUE, 2, wxSOLID);
	vectorLayer->SetPen(vectorpen);
	vectorLayer->SetDrawOutsideMargins(false);

	wxFont graphFont(11, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
    m_plot = new mpWindow( this, -1, wxPoint(0,0), wxSize(100,100), wxSUNKEN_BORDER );
    mpScaleX* xaxis = new mpScaleX(wxT("Temperature"), mpALIGN_BOTTOM, true, mpX_NORMAL);
    mpScaleY* yaxis = new mpScaleY(wxT("Altitude"), mpALIGN_LEFT, true);
    xaxis->SetFont(graphFont);
    yaxis->SetFont(graphFont);
    xaxis->SetDrawOutsideMargins(false);
    yaxis->SetDrawOutsideMargins(false);
	xaxis->SetLabelFormat(wxT("%.2f"));
	yaxis->SetLabelFormat(wxT("%.1f"));
    m_plot->SetMargins(30, 30, 50, 100);
    m_plot->AddLayer(     xaxis );
    m_plot->AddLayer(     yaxis );
	m_plot->AddLayer(     vectorLayer );
    //m_plot->AddLayer(     new mpText(wxT("Altitude vs Temperature"), 10, 10) );
    mpInfoLegend* leg;
    m_plot->AddLayer( leg = new mpInfoLegend(wxRect(200,20,40,40), wxTRANSPARENT_BRUSH)); //&hatch2));
    leg->SetVisible(true);

    m_plot->Fit();
    */
}

void StatusFrame::OnClose(wxCloseEvent& event) {
    /**
    *   Event handler for the form closing event
    *   Exit the ChaosConnect Program
    */
    exit(0);
}

void StatusFrame::SelectDevice( wxCommandEvent& event ) {
    /**
     * Selects a device from the menu
     *
     * This is a catchall menu event handler becasue I couldn't think
     * of a better way to do this because the menu is dynamic
     */

    int id = event.GetId();

    if(id > 10000) {
        /// We're selecting a device
        deviceId = deviceIds[id - 10000];
    }

    Update();
}
<commit_msg>- changed title to device status<commit_after>/**
 * \file StatusFrame.cpp
 * \brief Main Windows Class
 */

#include "StatusFrame.h"

BEGIN_EVENT_TABLE(StatusFrame,wxFrame)
    EVT_MENU(-1, StatusFrame::SelectDevice)
    EVT_CLOSE(StatusFrame::OnClose)
END_EVENT_TABLE()

StatusFrame::StatusFrame(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style)
: wxFrame(parent, id, title, position, size, style) {
    /**
    *   Constructor for the Main frame.
    */
    deviceId = "Please select a device from the menu.";

    CreateGUIControls();
}

StatusFrame::~StatusFrame() {
    /**
    *   Destructor for the Main form.
    */
}

void StatusFrame::Update() {
    /**
     * Updates the GUI with new database information
     */
    wxMenuBar *menubar = new wxMenuBar;
    wxMenu *devices = new wxMenu;

    deviceIds = DB_getAllDevices();

    menubar->Append(devices, wxT("&Devices"));

    for(unsigned int i = 0; i < deviceIds.size(); i++) {
        devices->Append(10000+i, deviceIds[i]);
    }
    SetMenuBar(menubar);

    map<string, string> gps_info;
    gps_info = DB_getMostRecentGPS(atoi(deviceId.c_str()));

    wxString info = wxString::Format(wxT(""
        "Device: %s\n"
        "
\n"
        "Location\n
"
        "Latitude: %s\n
"
        "Longitude: %s\n
"
        "Altitude (M): %s\n
"
        "Altitude (Ft): \n
"
        "Speed (Knots): %s\n
"
        "Speed (M/S): \n
"
        "Bearing: %s\n
"
        "Climb: \n
"
        "GPS Status: %s\n"
        "\n
"
        "Status\n
"
        "Battery 1 (V): \n
"
        "Battery 2 (V): \n
"
        "Buss (V): \n
"
        "Signal (%%): \n
"
        "Temperature Int. (c): \n
"
        "Temperature Ext. (c): \n
"
        "Pressure (HPA): \n
"
        "RH (%%): \n"
        ),
        deviceId.c_str(),
        gps_info["Latitude"].c_str(),
        gps_info["Longitude"].c_str(),
        gps_info["Altitude"].c_str(),
        gps_info["Spd"].c_str(),
        gps_info["Hdg"].c_str(),
        gps_info["Status"].c_str()
        );

    deviceInfo->SetPage(info);
}

void StatusFrame::CreateGUIControls() {
   /**
    *   Creates all of the GUI controls on the main form.
    */

    // Set window properties and title bar
    SetTitle(wxT("Device Status"));
    SetIcon(wxNullIcon);

    mainPanel = new wxPanel(this, wxID_ANY);
    mainSizer = new wxBoxSizer(wxVERTICAL);
    mainPanel->SetSizer(mainSizer);

    deviceInfo = new wxHtmlWindow(mainPanel);
    mainSizer->Add(deviceInfo, 1, wxEXPAND | wxALL);

    Update();

    //wxComboBox *device_dropdown = new wxComboBox(panel, wxID_ANY, wxT("Device"), wxDefaultPosition, wxDefaultSize);
    //sizer->Add(device_dropdown, 1, wxEXPAND | wxALL);

    /*
	// Create a mpFXYVector layer
	mpFXYVector* vectorLayer = new mpFXYVector(_(""));
	// Create two vectors for x,y and fill them with data
	std::vector<double> vectorx, vectory;
	double xcoord;
	for (unsigned int p = 0; p < 100; p++) {
		xcoord = (double)(p*1000.0);
		vectorx.push_back(25-0.001*pow(xcoord/1000, 2));
		vectory.push_back(xcoord);
	}
	vectorLayer->SetData(vectorx, vectory);
	vectorLayer->SetContinuity(true);
	wxPen vectorpen(*wxBLUE, 2, wxSOLID);
	vectorLayer->SetPen(vectorpen);
	vectorLayer->SetDrawOutsideMargins(false);

	wxFont graphFont(11, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
    m_plot = new mpWindow( this, -1, wxPoint(0,0), wxSize(100,100), wxSUNKEN_BORDER );
    mpScaleX* xaxis = new mpScaleX(wxT("Temperature"), mpALIGN_BOTTOM, true, mpX_NORMAL);
    mpScaleY* yaxis = new mpScaleY(wxT("Altitude"), mpALIGN_LEFT, true);
    xaxis->SetFont(graphFont);
    yaxis->SetFont(graphFont);
    xaxis->SetDrawOutsideMargins(false);
    yaxis->SetDrawOutsideMargins(false);
	xaxis->SetLabelFormat(wxT("%.2f"));
	yaxis->SetLabelFormat(wxT("%.1f"));
    m_plot->SetMargins(30, 30, 50, 100);
    m_plot->AddLayer(     xaxis );
    m_plot->AddLayer(     yaxis );
	m_plot->AddLayer(     vectorLayer );
    //m_plot->AddLayer(     new mpText(wxT("Altitude vs Temperature"), 10, 10) );
    mpInfoLegend* leg;
    m_plot->AddLayer( leg = new mpInfoLegend(wxRect(200,20,40,40), wxTRANSPARENT_BRUSH)); //&hatch2));
    leg->SetVisible(true);

    m_plot->Fit();
    */
}

void StatusFrame::OnClose(wxCloseEvent& event) {
    /**
    *   Event handler for the form closing event
    *   Exit the ChaosConnect Program
    */
    exit(0);
}

void StatusFrame::SelectDevice( wxCommandEvent& event ) {
    /**
     * Selects a device from the menu
     *
     * This is a catchall menu event handler becasue I couldn't think
     * of a better way to do this because the menu is dynamic
     */

    int id = event.GetId();

    if(id > 10000) {
        /// We're selecting a device
        deviceId = deviceIds[id - 10000];
    }

    Update();
}
<|endoftext|>


<commit_before>#ifndef SIMAPP_HPP
#define SIMAPP_HPP

#include <stdio.h>
#include <string>
#include <OGRE/Ogre.h>
#include "Planet.hpp"
#include "PlanetManager.hpp"

using namespace Ogre;
using namespace std;

class SimApp //: public IEventReceiver
{
public:
	SimApp();
	~SimApp();

	void start();
	void shutdown();

	//virtual bool OnEvent(const SEvent& event);

protected:
	bool init(const String wndTitle);
	void createScene();
	void updateScene();

	Root*				m_pRoot;
	RenderWindow*		m_pWindow;
	SceneManager*		m_pSMgr;
	Camera*				m_pCamera;
	Viewport*			m_pView;

	//ILogger*			m_pLog;

	PlanetManager*		m_pPlanetManager;

private:
	bool		m_bSettingsMode;
    bool 		m_bQuit;

    bool		m_bRMouseDown;
    bool		m_bLMouseDown;
};

#endif
<commit_msg>Adding framelistener<commit_after>#ifndef SIMAPP_HPP
#define SIMAPP_HPP

#include <stdio.h>
#include <string>
#include <OGRE/Ogre.h>
#include "Planet.hpp"
#include "PlanetManager.hpp"

using namespace Ogre;
using namespace std;

class SimApp : public FrameListeners
{
public:
	SimApp();
	~SimApp();

	void start();
	void shutdown();

	//virtual bool OnEvent(const SEvent& event);

protected:
	bool init(const String wndTitle);
	void createScene();
	void updateScene();

	Root*				m_pRoot;
	RenderWindow*		m_pWindow;
	SceneManager*		m_pSMgr;
	Camera*				m_pCamera;
	Viewport*			m_pView;

	//ILogger*			m_pLog;

	PlanetManager*		m_pPlanetManager;

private:
	bool		m_bSettingsMode;
    bool 		m_bQuit;

    bool		m_bRMouseDown;
    bool		m_bLMouseDown;
};

#endif
<|endoftext|>


<commit_before>#include <boost/timer.hpp>

#include <iostream>
#include <fstream>

#include "search/BucketPriorityQueue.hpp"
#include "search/astar/AStar.hpp"
#include "search/hastar/HAStar.hpp"
#include "search/switchback/Switchback.hpp"
#include "tiles/Tiles.hpp"

using namespace std;
using namespace boost;

typedef AStar<TilesInstance15, TilesNode15> TilesAStar;
typedef HAStar<TilesInstance15, TilesNode15> TilesHAStar;
typedef Switchback<TilesInstance15, TilesNode15> TilesSwitchback;

void print_build_info(ostream &o)
{
  o << "tiles state hash caching is "
#ifdef CACHE_TILES_HASH_VALUE
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "tiles state blank index caching is "
#ifdef CACHE_TILES_BLANK_INDEX
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "node f-value caching is "
#ifdef CACHE_NODE_F_VALUE
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "Hierarchical A*'s H* caching is "
#ifdef HIERARCHICAL_A_STAR_CACHE_H_STAR
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "Hierarchical A*'s P-g caching is "
#ifdef HIERARCHICAL_A_STAR_CACHE_P_MINUS_G
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "Hierarchical A*'s optimal path caching is "
#ifdef HIERARCHICAL_A_STAR_CACHE_OPTIMAL_PATHS
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "NDEBUG is "
#ifdef NDEBUG
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif
}

void print_size_info(ostream &o)
{
  o << "sizeof(unsigned) is " << sizeof(unsigned) << endl
    << "sizeof(Tile) is " << sizeof(Tile) << endl
    << "sizeof(TileIndex) is " << sizeof(TileIndex) << endl
    << "sizeof(TileArray) is " << sizeof(TileArray) << endl
    << "sizeof(TileCost) is " << sizeof(TileCost) << endl
    << "sizeof(TilesState15) is " << sizeof(TilesState15) << endl
    << "sizeof(TilesNode15) is " << sizeof(TilesNode15) << endl
    << "sizeof(size_t) is " << sizeof(size_t) << endl
    << "sizeof(BucketPriorityQueue<TilesNode15>::ItemPointer) is "
    << sizeof(BucketPriorityQueue<TilesNode15>::ItemPointer) << endl;
}

void print_usage(ostream &o, const char *prog_name)
{
  o << "usage: " << prog_name << " ALGORITHM [FILE]" << endl
    << "where" << endl
    << "  ALGORITHM is one of {astar, hastar, switchback}" << endl
    << "  FILE is the optional instance file to read from" << endl
    << endl
    << "If no file is specified, the instance is read from stdin." << endl;

  o << endl << endl;

  o << "Build Information:" << endl;
  print_build_info(o);
  o << endl;
  print_size_info(o);
  o << endl;
}

template <class Searcher>
void search(Searcher &searcher)
{
  timer search_timer;
  searcher.search();

  const TilesNode15 *goal = searcher.get_goal();
  if (goal == NULL) {
    cout << "no solution found!" << endl;
  }
  else {
    cout << "found a solution:" << endl;
    cout << *goal << endl;
    assert(goal->num_nodes_to_start() == goal->get_g() + 1u);
  }

  const double seconds_elapsed = search_timer.elapsed();
  const unsigned exp_per_second = searcher.get_num_expanded() / seconds_elapsed;
  const unsigned gen_per_second = searcher.get_num_expanded() / seconds_elapsed;

  cout << searcher.get_num_expanded() << " expanded ("
       << exp_per_second << "/s)" << endl
       << searcher.get_num_generated() << " generated ("
       << gen_per_second << "/s)" << endl
       << search_timer.elapsed() << "s" << endl;
}

int main(int argc, char * argv[])
{
  if (argc < 2 || argc > 3) {
    print_usage(cerr, argv[0]);
    return 1;
  }

  TilesInstance15 *instance;
  if (argc == 3) {
    ifstream infile(argv[2]);
    instance = readTilesInstance15(infile);
  }
  else {
    instance = readTilesInstance15(cin);
  }

  if (instance == NULL) {
    cerr << "error reading instance!" << endl;
    return 1;
  }

  cout << "######## The Instance ########" << endl;
  cout << *instance << endl;
  cout << endl;

  cout << "######## Search Results ########" << endl;

  const string alg_string(argv[1]);
  if (alg_string == "astar") {
    TilesAStar &astar = *new TilesAStar(*instance);
    search(astar);
  }
  else if (alg_string == "hastar") {
    TilesHAStar &hastar = *new TilesHAStar(*instance);
    search(hastar);
  }
  else if (alg_string == "switchback") {
    TilesSwitchback &switchback = *new TilesSwitchback(*instance);
    search(switchback);
  }
  else {
    cerr << "error: invalid algorithm specified" << endl;
    print_usage(cerr, argv[0]);
    return 1;
  }

  return 0;
}
<commit_msg>Output more build information when no args given.<commit_after>#include <boost/timer.hpp>

#include <iostream>
#include <fstream>

#include "search/BucketPriorityQueue.hpp"
#include "search/Constants.hpp"
#include "search/astar/AStar.hpp"
#include "search/hastar/HAStar.hpp"
#include "search/switchback/Switchback.hpp"
#include "tiles/Tiles.hpp"

using namespace std;
using namespace boost;

typedef AStar<TilesInstance15, TilesNode15> TilesAStar;
typedef HAStar<TilesInstance15, TilesNode15> TilesHAStar;
typedef Switchback<TilesInstance15, TilesNode15> TilesSwitchback;

void print_build_info(ostream &o)
{
  o << "tiles state hash caching is "
#ifdef CACHE_TILES_HASH_VALUE
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "tiles state blank index caching is "
#ifdef CACHE_TILES_BLANK_INDEX
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "node f-value caching is "
#ifdef CACHE_NODE_F_VALUE
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "Hierarchical A*'s H* caching is "
#ifdef HIERARCHICAL_A_STAR_CACHE_H_STAR
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "Hierarchical A*'s P-g caching is "
#ifdef HIERARCHICAL_A_STAR_CACHE_P_MINUS_G
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "Hierarchical A*'s optimal path caching is "
#ifdef HIERARCHICAL_A_STAR_CACHE_OPTIMAL_PATHS
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "NDEBUG is "
#ifdef NDEBUG
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "OUTPUT_SEARCH_PROGRESS is "
#ifdef OUTPUT_SEARCH_PROGRESS
    << "enabled" << endl;
#else
    << "disabled" << endl;
#endif

  o << "INITIAL_CLOSED_SET_SIZE is " << INITIAL_CLOSED_SET_SIZE << endl;
}

void print_size_info(ostream &o)
{
  o << "sizeof(unsigned) is " << sizeof(unsigned) << endl
    << "sizeof(Tile) is " << sizeof(Tile) << endl
    << "sizeof(TileIndex) is " << sizeof(TileIndex) << endl
    << "sizeof(TileArray) is " << sizeof(TileArray) << endl
    << "sizeof(TileCost) is " << sizeof(TileCost) << endl
    << "sizeof(TilesState15) is " << sizeof(TilesState15) << endl
    << "sizeof(TilesNode15) is " << sizeof(TilesNode15) << endl
    << "sizeof(size_t) is " << sizeof(size_t) << endl
    << "sizeof(BucketPriorityQueue<TilesNode15>::ItemPointer) is "
    << sizeof(BucketPriorityQueue<TilesNode15>::ItemPointer) << endl;
}

void print_usage(ostream &o, const char *prog_name)
{
  o << "usage: " << prog_name << " ALGORITHM [FILE]" << endl
    << "where" << endl
    << "  ALGORITHM is one of {astar, hastar, switchback}" << endl
    << "  FILE is the optional instance file to read from" << endl
    << endl
    << "If no file is specified, the instance is read from stdin." << endl;

  o << endl << endl;

  o << "Build Information:" << endl;
  print_build_info(o);
  o << endl;
  print_size_info(o);
  o << endl;
}

template <class Searcher>
void search(Searcher &searcher)
{
  timer search_timer;
  searcher.search();

  const TilesNode15 *goal = searcher.get_goal();
  if (goal == NULL) {
    cout << "no solution found!" << endl;
  }
  else {
    cout << "found a solution:" << endl;
    cout << *goal << endl;
    assert(goal->num_nodes_to_start() == goal->get_g() + 1u);
  }

  const double seconds_elapsed = search_timer.elapsed();
  const unsigned exp_per_second = searcher.get_num_expanded() / seconds_elapsed;
  const unsigned gen_per_second = searcher.get_num_expanded() / seconds_elapsed;

  cout << searcher.get_num_expanded() << " expanded ("
       << exp_per_second << "/s)" << endl
       << searcher.get_num_generated() << " generated ("
       << gen_per_second << "/s)" << endl
       << search_timer.elapsed() << "s" << endl;
}

int main(int argc, char * argv[])
{
  if (argc < 2 || argc > 3) {
    print_usage(cerr, argv[0]);
    return 1;
  }

  TilesInstance15 *instance;
  if (argc == 3) {
    ifstream infile(argv[2]);
    instance = readTilesInstance15(infile);
  }
  else {
    instance = readTilesInstance15(cin);
  }

  if (instance == NULL) {
    cerr << "error reading instance!" << endl;
    return 1;
  }

  cout << "######## The Instance ########" << endl;
  cout << *instance << endl;
  cout << endl;

  cout << "######## Search Results ########" << endl;

  const string alg_string(argv[1]);
  if (alg_string == "astar") {
    TilesAStar &astar = *new TilesAStar(*instance);
    search(astar);
  }
  else if (alg_string == "hastar") {
    TilesHAStar &hastar = *new TilesHAStar(*instance);
    search(hastar);
  }
  else if (alg_string == "switchback") {
    TilesSwitchback &switchback = *new TilesSwitchback(*instance);
    search(switchback);
  }
  else {
    cerr << "error: invalid algorithm specified" << endl;
    print_usage(cerr, argv[0]);
    return 1;
  }

  return 0;
}
<|endoftext|>


<commit_before>#include <iostream>

#include <pangolin/pangolin.h>

using namespace pangolin;
using namespace std;

#include <pangolin/display_internal.h>

int main( int /*argc*/, char* argv[] )
{
  // Create OpenGL window in single line thanks to GLUT
  pangolin::CreateWindowAndBind("Main",640,480);

  // Data logger object
  DataLog log;
  const double tinc = 0.01;

  // OpenGL 'view' of data. We might have many views of the same data.
  Plotter plotter(&log,0,M_PI/tinc,-2,2,M_PI/(4*tinc),0.5);
  plotter.SetBounds(0.0, 1.0, 0.0, 1.0);
  DisplayBase().AddDisplay(plotter);

  double t = 0;

  // Optionally add named labels
  vector<std::string> labels;
  labels.push_back(std::string("sin(t)"));
  labels.push_back(std::string("cos(t)"));
  labels.push_back(std::string("tan(t)"));
  labels.push_back(std::string("sin(t)+cos(t)"));
  log.SetLabels(labels);

  // Default hooks for exiting (Esc) and fullscreen (tab).
  while( !pangolin::ShouldQuit() )
  {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    log.Log(sin(t),cos(t),tan(t),sin(t)+cos(t));
    t += tinc;

    // Render graph, Swap frames and Process Events
    pangolin::FinishFrame();
  }

  return 0;
}
<commit_msg>SimplePlot: Enable tracking in sample.<commit_after>#include <iostream>

#include <pangolin/pangolin.h>

using namespace pangolin;
using namespace std;

#include <pangolin/display_internal.h>

int main( int /*argc*/, char* argv[] )
{
  // Create OpenGL window in single line thanks to GLUT
  pangolin::CreateWindowAndBind("Main",640,480);

  // Data logger object
  DataLog log;
  const double tinc = 0.01;

  // OpenGL 'view' of data. We might have many views of the same data.
  Plotter plotter(&log,0,4*M_PI/tinc,-2,2,M_PI/(4*tinc),0.5);
  plotter.SetBounds(0.0, 1.0, 0.0, 1.0);
  plotter.Track("$i");

  DisplayBase().AddDisplay(plotter);

  double t = 0;

  // Optionally add named labels
  vector<std::string> labels;
  labels.push_back(std::string("sin(t)"));
  labels.push_back(std::string("cos(t)"));
  labels.push_back(std::string("tan(t)"));
  labels.push_back(std::string("sin(t)+cos(t)"));
  log.SetLabels(labels);

  // Default hooks for exiting (Esc) and fullscreen (tab).
  while( !pangolin::ShouldQuit() )
  {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    log.Log(sin(t),cos(t),tan(t),sin(t)+cos(t));
    t += tinc;

    // Render graph, Swap frames and Process Events
    pangolin::FinishFrame();
  }

  return 0;
}
<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2016, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

/** @file
 * Implementation of unit tests for SDRClassifier
 */

#include <iostream>
#include <sstream>

#include <gtest/gtest.h>

#include <nupic/algorithms/ClassifierResult.hpp>
#include <nupic/algorithms/SDRClassifier.hpp>
#include <nupic/math/StlIo.hpp>
#include <nupic/types/Types.hpp>
#include <nupic/utils/Log.hpp>

using namespace std;
using namespace nupic;
using namespace nupic::algorithms::cla_classifier;
using namespace nupic::algorithms::sdr_classifier;

namespace
{

  TEST(SDRClassifierTest, Basic)
  {
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result1;
    c.compute(0, input1, 4, 34.7, false, true, true, &result1);

    // Create a vector of input bit indices
    vector<UInt> input2;
    input2.push_back(1);
    input2.push_back(5);
    input2.push_back(9);
    ClassifierResult result2;
    c.compute(1, input2, 4, 34.7, false, true, true, &result2);

    {
      bool foundMinus1 = false;
      bool found1 = false;
      for (auto it = result2.begin();
           it != result2.end(); ++it)
      {
        if (it->first == -1)
        {
          // The -1 key is used for the actual values
          ASSERT_EQ(false, foundMinus1)
            << "Already found key -1 in classifier result";
          foundMinus1 = true;
          ASSERT_EQ(5, it->second->size())
            << "Expected five buckets since it has only seen bucket 4 (so it "
            << "Has buckets 0-4).";
          ASSERT_TRUE(fabs(it->second->at(4) - 34.7) < 0.000001)
            << "Incorrect actual value for bucket 4";
        } else if (it->first == 1) {
          // Check the one-step prediction
          ASSERT_EQ(false, found1)
            << "Already found key 1 in classifier result";
          found1 = true;
          ASSERT_EQ(5, it->second->size())
            << "Expected five bucket predictions";
          ASSERT_LT(fabs(it->second->at(0) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 0";
          ASSERT_LT(fabs(it->second->at(1) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 1";
          ASSERT_LT(fabs(it->second->at(2) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 2";
          ASSERT_LT(fabs(it->second->at(3) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 3";
          ASSERT_LT(fabs(it->second->at(4) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 4";
        }
      }
      ASSERT_TRUE(foundMinus1) << "Key -1 not found in classifier result";
      ASSERT_TRUE(found1) << "key 1 not found in classifier result";
    }
  }

  TEST(SDRClassifierTest, SingleValue)
  {
    // Feed the same input 10 times, the corresponding probability should be
    // very high
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result1;
    for (UInt i = 0; i < 10; ++i)
    {
      ClassifierResult result1;
      c.compute(i, input1, 4, 34.7, false, true, true, &result1);
    }

    {
      for (auto it = result1.begin();
           it != result1.end(); ++it)
      {
        if (it->first == -1)
        {
          ASSERT_TRUE(fabs(it->second->at(4) - 10.0) < 0.000001)
            << "Incorrect actual value for bucket 4";
        } else if (it->first == 1) {
          ASSERT_GT(it->second->at(4), 0.9)
            << "Incorrect prediction for bucket 4";
        }
      }
    }

  }

  TEST(SDRClassifierTest, ComputeComplex)
  {
    // More complex classification
    // This test is ported from the Python unit test
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c = SDRClassifier(steps, 1.0, 0.1, 0);

    // Create a input vectors
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);

    // Create a input vectors
    vector<UInt> input2;
    input2.push_back(0);
    input2.push_back(6);
    input2.push_back(9);
    input2.push_back(11);

    // Create a input vectors
    vector<UInt> input3;
    input3.push_back(6);
    input3.push_back(9);

    ClassifierResult result1;
    c.compute(0, input1, 4, 34.7, false, true, true, &result1);

    ClassifierResult result2;
    c.compute(1, input2, 5, 41.7, false, true, true, &result2);

    ClassifierResult result3;
    c.compute(2, input3, 5, 44.9, false, true, true, &result3);

    ClassifierResult result4;
    c.compute(3, input1, 4, 42.9, false, true, true, &result4);

    ClassifierResult result5;
    c.compute(4, input1, 4, 34.7, false, true, true, &result5);

    {
      bool foundMinus1 = false;
      bool found1 = false;
      for (auto it = result5.begin(); it != result5.end(); ++it)
      {
        ASSERT_TRUE(it->first == -1 || it->first == 1)
          << "Result vector should only have -1 or 1 as key";
        if (it->first == -1)
        {
          // The -1 key is used for the actual values
          ASSERT_EQ(false, foundMinus1)
            << "Already found key -1 in classifier result";
          foundMinus1 = true;
          ASSERT_EQ(6, it->second->size())
            << "Expected six buckets since it has only seen bucket 4-5 (so it "
            << "has buckets 0-5).";
          ASSERT_TRUE(fabs(it->second->at(4) - 35.520000457763672) < 0.000001)
            << "Incorrect actual value for bucket 4";
          ASSERT_TRUE(fabs(it->second->at(5) - 42.020000457763672) < 0.000001)
            << "Incorrect actual value for bucket 5";
        } else if (it->first == 1) {
          // Check the one-step prediction
          ASSERT_EQ(false, found1)
            << "Already found key 1 in classifier result";
          found1 = true;

          ASSERT_EQ(6, it->second->size())
            << "Expected six bucket predictions";
          ASSERT_LT(fabs(it->second->at(0) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 0";
          ASSERT_LT(fabs(it->second->at(1) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 1";
          ASSERT_LT(fabs(it->second->at(2) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 2";
          ASSERT_LT(fabs(it->second->at(3) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 3";
          ASSERT_LT(fabs(it->second->at(4) - 0.093058), 0.000001)
            << "Incorrect prediction for bucket 4";
          ASSERT_LT(fabs(it->second->at(5) - 0.770004), 0.000001)
            << "Incorrect prediction for bucket 5";
        }
      }
      ASSERT_TRUE(foundMinus1) << "Key -1 not found in classifier result";
      ASSERT_TRUE(found1) << "Key 1 not found in classifier result";
    }

  }

  TEST(SDRClassifierTest, SaveLoad)
  {
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c1 = SDRClassifier(steps, 0.1, 0.1, 0);
    SDRClassifier c2 = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result;
    c1.compute(0, input1, 4, 34.7, false, true, true, &result);

    {
      stringstream ss;
      c1.save(ss);
      c2.load(ss);
    }

    ASSERT_TRUE(c1 == c2);

    ClassifierResult result1, result2;
    c1.compute(1, input1, 4, 35.7, false, true, true, &result1);
    c2.compute(1, input1, 4, 35.7, false, true, true, &result2);

    ASSERT_TRUE(result1 == result2);
  }

  TEST(SDRClassifierTest, WriteRead)
  {
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c1 = SDRClassifier(steps, 0.1, 0.1, 0);
    SDRClassifier c2 = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result;
    c1.compute(0, input1, 4, 34.7, false, true, true, &result);

    {
      stringstream ss;
      c1.write(ss);
      c2.read(ss);
    }

    ASSERT_TRUE(c1 == c2);

    ClassifierResult result1, result2;
    c1.compute(1, input1, 4, 35.7, false, true, true, &result1);
    c2.compute(1, input1, 4, 35.7, false, true, true, &result2);

    ASSERT_TRUE(result1 == result2);
  }

} // end namespace
<commit_msg>Added unit test for multi-step serialization<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2016, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

/** @file
 * Implementation of unit tests for SDRClassifier
 */

#include <iostream>
#include <sstream>

#include <gtest/gtest.h>

#include <nupic/algorithms/ClassifierResult.hpp>
#include <nupic/algorithms/SDRClassifier.hpp>
#include <nupic/math/StlIo.hpp>
#include <nupic/types/Types.hpp>
#include <nupic/utils/Log.hpp>

using namespace std;
using namespace nupic;
using namespace nupic::algorithms::cla_classifier;
using namespace nupic::algorithms::sdr_classifier;

namespace
{

  TEST(SDRClassifierTest, Basic)
  {
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result1;
    c.compute(0, input1, 4, 34.7, false, true, true, &result1);

    // Create a vector of input bit indices
    vector<UInt> input2;
    input2.push_back(1);
    input2.push_back(5);
    input2.push_back(9);
    ClassifierResult result2;
    c.compute(1, input2, 4, 34.7, false, true, true, &result2);

    {
      bool foundMinus1 = false;
      bool found1 = false;
      for (auto it = result2.begin();
           it != result2.end(); ++it)
      {
        if (it->first == -1)
        {
          // The -1 key is used for the actual values
          ASSERT_EQ(false, foundMinus1)
            << "Already found key -1 in classifier result";
          foundMinus1 = true;
          ASSERT_EQ(5, it->second->size())
            << "Expected five buckets since it has only seen bucket 4 (so it "
            << "Has buckets 0-4).";
          ASSERT_TRUE(fabs(it->second->at(4) - 34.7) < 0.000001)
            << "Incorrect actual value for bucket 4";
        } else if (it->first == 1) {
          // Check the one-step prediction
          ASSERT_EQ(false, found1)
            << "Already found key 1 in classifier result";
          found1 = true;
          ASSERT_EQ(5, it->second->size())
            << "Expected five bucket predictions";
          ASSERT_LT(fabs(it->second->at(0) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 0";
          ASSERT_LT(fabs(it->second->at(1) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 1";
          ASSERT_LT(fabs(it->second->at(2) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 2";
          ASSERT_LT(fabs(it->second->at(3) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 3";
          ASSERT_LT(fabs(it->second->at(4) - 0.2), 0.000001)
            << "Incorrect prediction for bucket 4";
        }
      }
      ASSERT_TRUE(foundMinus1) << "Key -1 not found in classifier result";
      ASSERT_TRUE(found1) << "key 1 not found in classifier result";
    }
  }

  TEST(SDRClassifierTest, SingleValue)
  {
    // Feed the same input 10 times, the corresponding probability should be
    // very high
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result1;
    for (UInt i = 0; i < 10; ++i)
    {
      ClassifierResult result1;
      c.compute(i, input1, 4, 34.7, false, true, true, &result1);
    }

    {
      for (auto it = result1.begin();
           it != result1.end(); ++it)
      {
        if (it->first == -1)
        {
          ASSERT_TRUE(fabs(it->second->at(4) - 10.0) < 0.000001)
            << "Incorrect actual value for bucket 4";
        } else if (it->first == 1) {
          ASSERT_GT(it->second->at(4), 0.9)
            << "Incorrect prediction for bucket 4";
        }
      }
    }

  }

  TEST(SDRClassifierTest, ComputeComplex)
  {
    // More complex classification
    // This test is ported from the Python unit test
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c = SDRClassifier(steps, 1.0, 0.1, 0);

    // Create a input vectors
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);

    // Create a input vectors
    vector<UInt> input2;
    input2.push_back(0);
    input2.push_back(6);
    input2.push_back(9);
    input2.push_back(11);

    // Create a input vectors
    vector<UInt> input3;
    input3.push_back(6);
    input3.push_back(9);

    ClassifierResult result1;
    c.compute(0, input1, 4, 34.7, false, true, true, &result1);

    ClassifierResult result2;
    c.compute(1, input2, 5, 41.7, false, true, true, &result2);

    ClassifierResult result3;
    c.compute(2, input3, 5, 44.9, false, true, true, &result3);

    ClassifierResult result4;
    c.compute(3, input1, 4, 42.9, false, true, true, &result4);

    ClassifierResult result5;
    c.compute(4, input1, 4, 34.7, false, true, true, &result5);

    {
      bool foundMinus1 = false;
      bool found1 = false;
      for (auto it = result5.begin(); it != result5.end(); ++it)
      {
        ASSERT_TRUE(it->first == -1 || it->first == 1)
          << "Result vector should only have -1 or 1 as key";
        if (it->first == -1)
        {
          // The -1 key is used for the actual values
          ASSERT_EQ(false, foundMinus1)
            << "Already found key -1 in classifier result";
          foundMinus1 = true;
          ASSERT_EQ(6, it->second->size())
            << "Expected six buckets since it has only seen bucket 4-5 (so it "
            << "has buckets 0-5).";
          ASSERT_TRUE(fabs(it->second->at(4) - 35.520000457763672) < 0.000001)
            << "Incorrect actual value for bucket 4";
          ASSERT_TRUE(fabs(it->second->at(5) - 42.020000457763672) < 0.000001)
            << "Incorrect actual value for bucket 5";
        } else if (it->first == 1) {
          // Check the one-step prediction
          ASSERT_EQ(false, found1)
            << "Already found key 1 in classifier result";
          found1 = true;

          ASSERT_EQ(6, it->second->size())
            << "Expected six bucket predictions";
          ASSERT_LT(fabs(it->second->at(0) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 0";
          ASSERT_LT(fabs(it->second->at(1) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 1";
          ASSERT_LT(fabs(it->second->at(2) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 2";
          ASSERT_LT(fabs(it->second->at(3) - 0.034234), 0.000001)
            << "Incorrect prediction for bucket 3";
          ASSERT_LT(fabs(it->second->at(4) - 0.093058), 0.000001)
            << "Incorrect prediction for bucket 4";
          ASSERT_LT(fabs(it->second->at(5) - 0.770004), 0.000001)
            << "Incorrect prediction for bucket 5";
        }
      }
      ASSERT_TRUE(foundMinus1) << "Key -1 not found in classifier result";
      ASSERT_TRUE(found1) << "Key 1 not found in classifier result";
    }

  }

  TEST(SDRClassifierTest, SaveLoad)
  {
    vector<UInt> steps;
    steps.push_back(1);
    SDRClassifier c1 = SDRClassifier(steps, 0.1, 0.1, 0);
    SDRClassifier c2 = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result;
    c1.compute(0, input1, 4, 34.7, false, true, true, &result);

    {
      stringstream ss;
      c1.save(ss);
      c2.load(ss);
    }

    ASSERT_TRUE(c1 == c2);

    ClassifierResult result1, result2;
    c1.compute(1, input1, 4, 35.7, false, true, true, &result1);
    c2.compute(1, input1, 4, 35.7, false, true, true, &result2);

    ASSERT_TRUE(result1 == result2);
  }

  TEST(SDRClassifierTest, WriteRead)
  {
    vector<UInt> steps;
    steps.push_back(1);
    steps.push_back(2);
    SDRClassifier c1 = SDRClassifier(steps, 0.1, 0.1, 0);
    SDRClassifier c2 = SDRClassifier(steps, 0.1, 0.1, 0);

    // Create a vector of input bit indices
    vector<UInt> input1;
    input1.push_back(1);
    input1.push_back(5);
    input1.push_back(9);
    ClassifierResult result;
    c1.compute(0, input1, 4, 34.7, false, true, true, &result);

        // Create a vector of input bit indices
    vector<UInt> input2;
    input2.push_back(0);
    input2.push_back(8);
    input2.push_back(9);
    c1.compute(1, input2, 2, 24.7, false, true, true, &result);

    {
      stringstream ss;
      c1.write(ss);
      c2.read(ss);
    }

    ASSERT_TRUE(c1 == c2);

    ClassifierResult result1, result2;
    c1.compute(2, input1, 4, 35.7, false, true, true, &result1);
    c2.compute(2, input1, 4, 35.7, false, true, true, &result2);

    ASSERT_TRUE(result1 == result2);
  }

} // end namespace
<|endoftext|>


<commit_before>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/timer/Timer.h>

#include <list>
#include <map>

#include <stingraykit/diagnostics/ExecutorsProfiler.h>
#include <stingraykit/function/CancellableFunction.h>
#include <stingraykit/function/bind.h>
#include <stingraykit/function/function_name_getter.h>
#include <stingraykit/log/Logger.h>
#include <stingraykit/time/ElapsedTime.h>
#include <stingraykit/TaskLifeToken.h>

namespace stingray
{

	namespace Detail
	{
		struct ITimerConnectionImpl
		{
			virtual ~ITimerConnectionImpl() { }

			virtual void Disconnect() = 0;
		};
		STINGRAYKIT_DECLARE_PTR(ITimerConnectionImpl);
	}

	class TimerConnectionToken : public virtual IToken
	{
		friend class Timer;

	private:
		Detail::ITimerConnectionImplPtr		_impl;

	public:
		TimerConnectionToken(const Detail::ITimerConnectionImplPtr& impl) : _impl(impl)
		{ }

		~TimerConnectionToken()
		{
			if (_impl)
			{
				_impl->Disconnect();
				_impl.reset();
			}
		}
	};

	class Timer::CallbackQueue
	{
		typedef std::list<CallbackInfoPtr>					ContainerInternal;
		typedef std::map<TimeDuration, ContainerInternal>	Container;

	private:
		Mutex			_mutex;
		Container		_container;

	public:
		typedef ContainerInternal::iterator iterator;

		inline Mutex &Sync()
		{ return _mutex; }

		inline bool IsEmpty() const
		{
			MutexLock l(_mutex);
			return _container.empty();
		}

		CallbackInfoPtr Top() const;
		void Push(CallbackInfoPtr ci);
		void Erase(const iterator & it);
		CallbackInfoPtr Pop();
	};

	class Timer::CallbackInfo : public virtual Detail::ITimerConnectionImpl
	{
		STINGRAYKIT_NONCOPYABLE(CallbackInfo);
		typedef function<void()>		FuncT;

	private:
		FuncT						_func;
		TimeDuration				_timeToTrigger;
		optional<TimeDuration>		_period;
		TaskLifeToken				_token;
		CallbackQueueWeakPtr		_queue;
		CallbackQueue::iterator		_iterator;
		bool						_iteratorIsValid;

	private:
		friend class CallbackQueue;

		void SetIterator(const CallbackQueue::iterator& it)		{ _iterator = it; _iteratorIsValid = true; }
		void ResetIterator()									{ _iteratorIsValid = false; }

	public:
		CallbackInfo(const FuncT& func, const TimeDuration& timeToTrigger, const optional<TimeDuration>& period, const CallbackQueuePtr& queue)
			: _func(func), _timeToTrigger(timeToTrigger), _period(period), _queue(queue), _iteratorIsValid(false)
		{ }

		const FuncT& GetFunc() const							{ return _func; }
		FutureExecutionTester GetExecutionTester() const 		{ return _token.GetExecutionTester(); }

		bool IsPeriodic() const									{ return _period.is_initialized(); }
		void Restart(const TimeDuration& currentTime)
		{
			STINGRAYKIT_CHECK(_period, "Timer::CallbackInfo::Restart internal error: _period is set!");
			_timeToTrigger = currentTime + *_period;
		}
		TimeDuration GetTimeToTrigger() const					{ return _timeToTrigger; }

		virtual void Disconnect()
		{
			CallbackQueuePtr qm = _queue.lock();
			if (qm)
			{
				MutexLock l(qm->Sync());
				if (_iteratorIsValid)
				{
					qm->Erase(_iterator);
					ResetIterator();
				}
			}
			_token.Release();
		}
	};

	Timer::CallbackInfoPtr Timer::CallbackQueue::Top() const
	{
		MutexLock l(_mutex);
		if (!_container.empty())
		{
			const ContainerInternal& listForTop = _container.begin()->second;
			STINGRAYKIT_CHECK(!listForTop.empty(), "try to get callback from empty list");
			return listForTop.front();
		}
		else
			return null;
	}

	void Timer::CallbackQueue::Push(CallbackInfoPtr ci)
	{
		MutexLock l(_mutex);
		ContainerInternal& listToInsert = _container[ci->GetTimeToTrigger()];
		ci->SetIterator(listToInsert.insert(listToInsert.end(), ci));
	}

	void Timer::CallbackQueue::Erase(const iterator & it)
	{
		MutexLock l(_mutex);
		TimeDuration keyToErase = (*it)->GetTimeToTrigger();
		ContainerInternal& listToErase = _container[keyToErase];
		listToErase.erase(it);
		if (listToErase.empty())
			_container.erase(keyToErase);
	}

	Timer::CallbackInfoPtr Timer::CallbackQueue::Pop()
	{
		MutexLock l(_mutex);
		STINGRAYKIT_CHECK(!_container.empty(), "popping callback from empty map");
		ContainerInternal& listToPop = _container.begin()->second;
		STINGRAYKIT_CHECK(!listToPop.empty(), "popping callback from empty list");

		CallbackInfoPtr ci = listToPop.front();
		listToPop.pop_front();
		if (listToPop.empty())
			_container.erase(ci->GetTimeToTrigger());
		ci->ResetIterator();
		return ci;
	}

	STINGRAYKIT_DEFINE_NAMED_LOGGER(Timer);

	Timer::Timer(const std::string& timerName, const ExceptionHandler& exceptionHandler, bool profileCalls) :
		_timerName(timerName), _alive(true), _queue(new CallbackQueue), _exceptionHandler(exceptionHandler), _profileCalls(profileCalls)
	{
		_worker.reset(new Thread(timerName, bind(&Timer::ThreadFunc, this, not_using(_1))));
	}

	Timer::~Timer()
	{
		Shutdown();

		bool has_tasks = false;
		while(!_queue->IsEmpty())
		{
			CallbackInfoPtr i = _queue->Pop();
			LocalExecutionGuard guard(i->GetExecutionTester());
			if (guard)
			{
				has_tasks = true;
				break;
			}
		}

		if (has_tasks)
			Logger::Warning() << "[Timer] Killing timer " << _timerName << " which still has some functions to execute";
	}

	void Timer::Shutdown()
	{
		{
			MutexLock l(_queue->Sync());
			_alive = false;
			_cond.Broadcast();
		}
		_worker.reset();
	}

	Token Timer::SetTimeout(const TimeDuration& timeout, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, null, _queue);
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<TimerConnectionToken>(ci);
	}

	Token Timer::SetTimer(const TimeDuration& interval, const function<void()>& func)
	{ return SetTimer(interval, interval, func); }

	Token Timer::SetTimer(const TimeDuration& timeout, const TimeDuration& interval, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, interval, _queue);
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<TimerConnectionToken>(ci);
	}

	void Timer::AddTask(const function<void()>& task, const FutureExecutionTester& tester)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(MakeCancellableFunction(task, tester), _monotonic.Elapsed(), null, _queue);
		_queue->Push(ci);
		_cond.Broadcast();
	}

	std::string Timer::GetProfilerMessage(const function<void()>& func)
	{ return StringBuilder() % get_function_name(func) % " in Timer '" % _timerName % "'"; }

	void Timer::ThreadFunc()
	{
		MutexLock l(_queue->Sync());

		while (_alive)
		{
			if (_queue->IsEmpty())
			{
				_cond.Wait(_queue->Sync());
				continue;
			}

			CallbackInfoPtr top = _queue->Top();
			if (top->GetTimeToTrigger() <= _monotonic.Elapsed())
			{
				_queue->Pop();

				{
					LocalExecutionGuard guard(top->GetExecutionTester());
					if (!guard)
					{
						top.reset();
						continue;
					}

					MutexUnlock ul(l);
					if (top->IsPeriodic())
						top->Restart(_monotonic.Elapsed());

					try
					{
						if (_profileCalls)
						{
							AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
							(top->GetFunc())();
						}
						else
							(top->GetFunc())();
					}
					catch(const std::exception &ex)
					{ _exceptionHandler(ex); }

					if (!top->IsPeriodic())
						top.reset();
				}

				if (top)
					_queue->Push(top);
			}
			else //top timer not triggered
			{
				const TimeDuration waitTime = top->GetTimeToTrigger() - _monotonic.Elapsed();
				top.reset();
				if (waitTime > TimeDuration())
					_cond.TimedWait(_queue->Sync(), waitTime);
			}
		}

		const TimeDuration currentTime = _monotonic.Elapsed();
		while (!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			if (top->GetTimeToTrigger() <= currentTime)
			{
				LocalExecutionGuard guard(top->GetExecutionTester());
				if (!guard)
					continue;

				MutexUnlock ul(l);
				try
				{
					if (_profileCalls)
					{
						AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
						(top->GetFunc())();
					}
					else
						(top->GetFunc())();
				}
				catch(const std::exception &ex)
				{ _exceptionHandler(ex); }
			}
			else
				break;
		}
	}

	void Timer::DefaultExceptionHandler(const std::exception& ex)
	{ s_logger.Error() << "Timer func exception: " << ex; }

}
<commit_msg>Timer: invert if condition<commit_after>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/timer/Timer.h>

#include <list>
#include <map>

#include <stingraykit/diagnostics/ExecutorsProfiler.h>
#include <stingraykit/function/CancellableFunction.h>
#include <stingraykit/function/bind.h>
#include <stingraykit/function/function_name_getter.h>
#include <stingraykit/log/Logger.h>
#include <stingraykit/time/ElapsedTime.h>
#include <stingraykit/TaskLifeToken.h>

namespace stingray
{

	namespace Detail
	{
		struct ITimerConnectionImpl
		{
			virtual ~ITimerConnectionImpl() { }

			virtual void Disconnect() = 0;
		};
		STINGRAYKIT_DECLARE_PTR(ITimerConnectionImpl);
	}

	class TimerConnectionToken : public virtual IToken
	{
		friend class Timer;

	private:
		Detail::ITimerConnectionImplPtr		_impl;

	public:
		TimerConnectionToken(const Detail::ITimerConnectionImplPtr& impl) : _impl(impl)
		{ }

		~TimerConnectionToken()
		{
			if (_impl)
			{
				_impl->Disconnect();
				_impl.reset();
			}
		}
	};

	class Timer::CallbackQueue
	{
		typedef std::list<CallbackInfoPtr>					ContainerInternal;
		typedef std::map<TimeDuration, ContainerInternal>	Container;

	private:
		Mutex			_mutex;
		Container		_container;

	public:
		typedef ContainerInternal::iterator iterator;

		inline Mutex &Sync()
		{ return _mutex; }

		inline bool IsEmpty() const
		{
			MutexLock l(_mutex);
			return _container.empty();
		}

		CallbackInfoPtr Top() const;
		void Push(CallbackInfoPtr ci);
		void Erase(const iterator & it);
		CallbackInfoPtr Pop();
	};

	class Timer::CallbackInfo : public virtual Detail::ITimerConnectionImpl
	{
		STINGRAYKIT_NONCOPYABLE(CallbackInfo);
		typedef function<void()>		FuncT;

	private:
		FuncT						_func;
		TimeDuration				_timeToTrigger;
		optional<TimeDuration>		_period;
		TaskLifeToken				_token;
		CallbackQueueWeakPtr		_queue;
		CallbackQueue::iterator		_iterator;
		bool						_iteratorIsValid;

	private:
		friend class CallbackQueue;

		void SetIterator(const CallbackQueue::iterator& it)		{ _iterator = it; _iteratorIsValid = true; }
		void ResetIterator()									{ _iteratorIsValid = false; }

	public:
		CallbackInfo(const FuncT& func, const TimeDuration& timeToTrigger, const optional<TimeDuration>& period, const CallbackQueuePtr& queue)
			: _func(func), _timeToTrigger(timeToTrigger), _period(period), _queue(queue), _iteratorIsValid(false)
		{ }

		const FuncT& GetFunc() const							{ return _func; }
		FutureExecutionTester GetExecutionTester() const 		{ return _token.GetExecutionTester(); }

		bool IsPeriodic() const									{ return _period.is_initialized(); }
		void Restart(const TimeDuration& currentTime)
		{
			STINGRAYKIT_CHECK(_period, "Timer::CallbackInfo::Restart internal error: _period is set!");
			_timeToTrigger = currentTime + *_period;
		}
		TimeDuration GetTimeToTrigger() const					{ return _timeToTrigger; }

		virtual void Disconnect()
		{
			CallbackQueuePtr qm = _queue.lock();
			if (qm)
			{
				MutexLock l(qm->Sync());
				if (_iteratorIsValid)
				{
					qm->Erase(_iterator);
					ResetIterator();
				}
			}
			_token.Release();
		}
	};

	Timer::CallbackInfoPtr Timer::CallbackQueue::Top() const
	{
		MutexLock l(_mutex);
		if (!_container.empty())
		{
			const ContainerInternal& listForTop = _container.begin()->second;
			STINGRAYKIT_CHECK(!listForTop.empty(), "try to get callback from empty list");
			return listForTop.front();
		}
		else
			return null;
	}

	void Timer::CallbackQueue::Push(CallbackInfoPtr ci)
	{
		MutexLock l(_mutex);
		ContainerInternal& listToInsert = _container[ci->GetTimeToTrigger()];
		ci->SetIterator(listToInsert.insert(listToInsert.end(), ci));
	}

	void Timer::CallbackQueue::Erase(const iterator & it)
	{
		MutexLock l(_mutex);
		TimeDuration keyToErase = (*it)->GetTimeToTrigger();
		ContainerInternal& listToErase = _container[keyToErase];
		listToErase.erase(it);
		if (listToErase.empty())
			_container.erase(keyToErase);
	}

	Timer::CallbackInfoPtr Timer::CallbackQueue::Pop()
	{
		MutexLock l(_mutex);
		STINGRAYKIT_CHECK(!_container.empty(), "popping callback from empty map");
		ContainerInternal& listToPop = _container.begin()->second;
		STINGRAYKIT_CHECK(!listToPop.empty(), "popping callback from empty list");

		CallbackInfoPtr ci = listToPop.front();
		listToPop.pop_front();
		if (listToPop.empty())
			_container.erase(ci->GetTimeToTrigger());
		ci->ResetIterator();
		return ci;
	}

	STINGRAYKIT_DEFINE_NAMED_LOGGER(Timer);

	Timer::Timer(const std::string& timerName, const ExceptionHandler& exceptionHandler, bool profileCalls) :
		_timerName(timerName), _alive(true), _queue(new CallbackQueue), _exceptionHandler(exceptionHandler), _profileCalls(profileCalls)
	{
		_worker.reset(new Thread(timerName, bind(&Timer::ThreadFunc, this, not_using(_1))));
	}

	Timer::~Timer()
	{
		Shutdown();

		bool has_tasks = false;
		while(!_queue->IsEmpty())
		{
			CallbackInfoPtr i = _queue->Pop();
			LocalExecutionGuard guard(i->GetExecutionTester());
			if (guard)
			{
				has_tasks = true;
				break;
			}
		}

		if (has_tasks)
			Logger::Warning() << "[Timer] Killing timer " << _timerName << " which still has some functions to execute";
	}

	void Timer::Shutdown()
	{
		{
			MutexLock l(_queue->Sync());
			_alive = false;
			_cond.Broadcast();
		}
		_worker.reset();
	}

	Token Timer::SetTimeout(const TimeDuration& timeout, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, null, _queue);
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<TimerConnectionToken>(ci);
	}

	Token Timer::SetTimer(const TimeDuration& interval, const function<void()>& func)
	{ return SetTimer(interval, interval, func); }

	Token Timer::SetTimer(const TimeDuration& timeout, const TimeDuration& interval, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, interval, _queue);
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<TimerConnectionToken>(ci);
	}

	void Timer::AddTask(const function<void()>& task, const FutureExecutionTester& tester)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(MakeCancellableFunction(task, tester), _monotonic.Elapsed(), null, _queue);
		_queue->Push(ci);
		_cond.Broadcast();
	}

	std::string Timer::GetProfilerMessage(const function<void()>& func)
	{ return StringBuilder() % get_function_name(func) % " in Timer '" % _timerName % "'"; }

	void Timer::ThreadFunc()
	{
		MutexLock l(_queue->Sync());

		while (_alive)
		{
			if (_queue->IsEmpty())
			{
				_cond.Wait(_queue->Sync());
				continue;
			}

			CallbackInfoPtr top = _queue->Top();
			if (top->GetTimeToTrigger() <= _monotonic.Elapsed())
			{
				_queue->Pop();

				{
					LocalExecutionGuard guard(top->GetExecutionTester());
					if (!guard)
					{
						top.reset();
						continue;
					}

					MutexUnlock ul(l);
					if (top->IsPeriodic())
						top->Restart(_monotonic.Elapsed());

					try
					{
						if (_profileCalls)
						{
							AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
							(top->GetFunc())();
						}
						else
							(top->GetFunc())();
					}
					catch(const std::exception &ex)
					{ _exceptionHandler(ex); }

					if (!top->IsPeriodic())
						top.reset();
				}

				if (top)
					_queue->Push(top);
			}
			else //top timer not triggered
			{
				const TimeDuration waitTime = top->GetTimeToTrigger() - _monotonic.Elapsed();
				top.reset();
				if (waitTime > TimeDuration())
					_cond.TimedWait(_queue->Sync(), waitTime);
			}
		}

		const TimeDuration currentTime = _monotonic.Elapsed();
		while (!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			if (top->GetTimeToTrigger() <= currentTime)
				break;

			LocalExecutionGuard guard(top->GetExecutionTester());
			if (!guard)
				continue;

			MutexUnlock ul(l);
			try
			{
				if (_profileCalls)
				{
					AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
					(top->GetFunc())();
				}
				else
					(top->GetFunc())();
			}
			catch(const std::exception &ex)
			{ _exceptionHandler(ex); }
		}
	}

	void Timer::DefaultExceptionHandler(const std::exception& ex)
	{ s_logger.Error() << "Timer func exception: " << ex; }

}
<|endoftext|>


<commit_before>/*
 *  OpenALBackend.cpp
 *  openc2e
 *
 *  Created by Bryan Donlan on Sun Aug 12 2007.
 *  Copyright (c) 2007 Bryan Donlan. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */

#include "OpenALBackend.h"
#include <alut.h>
#include <iostream>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <boost/lambda/bind.hpp>
#include "SDL.h"
#include "exceptions.h"
#include "World.h"

#ifndef OPENAL_SUPPORT
#error OPENAL_SUPPORT isn't set, so this file shouldn't be being compiled
#endif

const ALfloat zdist = -1.0;
const ALfloat plnemul = 0.0;
const ALfloat scale = 1.0/100;

static const ALfloat null_vec[] = { 0.0, 0.0, 0.0 };

static std::string al_error_str(ALenum err) {
	switch (err) {
		case AL_NO_ERROR:
			return std::string("No error");
		case AL_INVALID_NAME:
			return std::string("Invalid name");
		case AL_INVALID_ENUM:
			return std::string("Invalid enum");
		case AL_INVALID_VALUE:
			return std::string("Invalid value");
		case AL_INVALID_OPERATION:
			return std::string("Invalid operation");
		case AL_OUT_OF_MEMORY:
			return std::string("Out of memory (we're crashing soon)");
		default:
			return boost::str(boost::format("Unknown error %d") % (int)err);
	}
}

static void al_throw_maybe() {
	ALenum err = alGetError();
	if (err == AL_NO_ERROR) return;
	throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err % al_error_str(err)));
}

void OpenALBackend::init() {
	alGetError();
	alutGetError();

	if (!alutInitWithoutContext(NULL, NULL)) {
		ALenum err = alutGetError();
		throw creaturesException(boost::str(
			boost::format("Failed to init OpenAL/ALUT: %s") % alutGetErrorString(err)
		));
	}

	// It seems that relying on hardware drivers is an awful idea - always use software if it's available.
	// TODO: we should probably store this so we can (eventually) close stuff properly
	ALCdevice *device = alcOpenDevice("Generic Software");
	if (!device) {
		device = alcOpenDevice(NULL);
		if (!device) {
			al_throw_maybe();
		}
	}
	ALCcontext *context = alcCreateContext(device, NULL);
	if (!context) {
		al_throw_maybe();
	}
	alcMakeContextCurrent(context);

	setMute(false);

	static const ALfloat init_ori[6] = {
		0.0, 0.0, 1.0, /* at */
		0.0, -1.0, 0.0  /* up */
	};
	static const ALfloat init_vel[3] = {
		0.0, 0.0, 0.0
	};
	static const ALfloat init_pos[3] = {
		0.0, 0.0, 0.0
	};
	memcpy((void *)ListenerOri, (void *)init_ori, sizeof init_ori);
	memcpy((void *)ListenerVel, (void *)init_vel, sizeof init_vel);
	memcpy((void *)ListenerPos, (void *)init_pos, sizeof init_pos);

	ListenerPos[2] = zdist;
	updateListener();
}

void OpenALBackend::updateListener() {
	alListenerfv(AL_POSITION, ListenerPos);
	alListenerfv(AL_VELOCITY, ListenerVel);
	alListenerfv(AL_ORIENTATION, ListenerOri);
}

void OpenALBackend::setViewpointCenter(float x, float y) {
	ListenerPos[0] = x * scale;
	ListenerPos[1] = y * scale;
	updateListener();
}

void OpenALBackend::shutdown() {
	alutExit();
}

void OpenALBackend::setMute(bool m) {
	if (m)
		alListenerf(AL_GAIN, 0.0f);
	else
		alListenerf(AL_GAIN, 1.0f);
	muted = m;
}

boost::shared_ptr<AudioSource> OpenALBackend::newSource() {
	return boost::shared_ptr<AudioSource>(new OpenALSource(shared_from_this()));
}

AudioClip OpenALBackend::loadClip(const std::string &filename) {
	std::string fname = world.findFile(std::string("/Sounds/") + filename + ".wav");
	if (fname.size() == 0) return AudioClip();

	alGetError();
	ALuint buf = alutCreateBufferFromFile(fname.c_str());
	if (!buf) {
		ALenum err = alGetError();
		throw creaturesException(boost::str(
					boost::format("Failed to load %s: %s") % fname % alutGetErrorString(err)
					));
	}

	AudioClip clip(new OpenALBuffer(shared_from_this(), buf));
	return clip;
}

void OpenALBackend::begin() {
	alcSuspendContext(alcGetCurrentContext());
}

void OpenALBackend::commit() {
	alcProcessContext(alcGetCurrentContext());
}

OpenALSource::OpenALSource(boost::shared_ptr<class OpenALBackend> backend) {
	this->backend = backend;
	alGetError();
	alGenSources(1, &source);
	alSourcef(source, AL_PITCH, 1.0f);
	alSourcef(source, AL_GAIN, 1.0f);
	alSourcefv(source, AL_POSITION, null_vec);
	alSourcefv(source, AL_VELOCITY, null_vec);
	alSourcei(source, AL_LOOPING, 0);
	al_throw_maybe();
}

OpenALBuffer::OpenALBuffer(boost::shared_ptr<class OpenALBackend> backend, ALuint handle) {
	this->backend = backend;
	buffer = handle;
}

unsigned int OpenALBuffer::length_samples() {
	ALint bits;
	ALint channels;
	ALint size;
	alGetBufferi(buffer, AL_BITS, &bits);
	alGetBufferi(buffer, AL_CHANNELS, &channels);
	alGetBufferi(buffer, AL_SIZE, &size);
	return size / (bits / 8 * channels);
}

unsigned int OpenALBuffer::length_ms() {
	ALint freq;
	alGetBufferi(buffer, AL_FREQUENCY, &freq);
	return length_samples() * 1000 / freq;
}

AudioClip OpenALSource::getClip() {
	AudioClip clip(static_cast<AudioBuffer *>(this->clip.get()));
	return clip;
}

void OpenALSource::setClip(AudioClip &clip_) {
	OpenALBuffer *obp = dynamic_cast<OpenALBuffer *>(clip_.get());
	assert(obp);
	stop();
	this->clip = OpenALClip(obp);
	if (clip) {
		alSourcei(source, AL_BUFFER, clip->buffer);
	}
}

SourceState OpenALSource::getState() {
	int state;
	alGetSourcei(source, AL_SOURCE_STATE, &state);
	switch (state) {
		case AL_INITIAL: case AL_STOPPED: return SS_STOP;
		case AL_PLAYING: return SS_PLAY; /* XXX: AL_INITIAL? */
		case AL_PAUSED:  return SS_PAUSE;
		default:
		{
			std::cerr << "Unknown openal state, stopping stream: " << state << std::endl;
			stop();
			return SS_STOP;
		}
	}
}

void OpenALSource::play() {
	assert(clip);
	alSourcePlay(source);
}

void OpenALSource::stop() {
	alSourceStop(source);
}

void OpenALSource::pause() {
	alSourcePause(source);
}

static void fadeSource(boost::weak_ptr<AudioSource> s) {
	int fadeout = 500; // TODO: good value?

	/* We adjust gain every 10ms until we reach fadeout ms. */
	const int steps = fadeout / 10;
	const float adjust = 1.0 / (float)steps;
	for (int i = 0; i < steps; i++) {
		boost::shared_ptr<AudioSource> p = s.lock();
		if (!p) return;
		p->setVolume(1.0f - adjust * (float)i);
		SDL_Delay(10); // XXX: do we have some other portable sleeping primitive we can use?
		// boost::thread::sleep is a bit iffy, it seems
	}
	boost::shared_ptr<AudioSource> p = s.lock();
	if (p)
		p->stop();
}

void OpenALSource::fadeOut() {
	boost::thread th(boost::lambda::bind<void>(fadeSource, shared_from_this()));
}

void OpenALSource::setPos(float x, float y, float plane) {
	if (this->x == x && this->y == y && this->z == plane) return;
	this->SkeletonAudioSource::setPos(x, y, plane);
	alSource3f(source, AL_POSITION, x * scale, y * scale, plane * plnemul);
}

void OpenALSource::setVelocity(float x, float y) {
	// experiment with this later
	x = y = 0;
	alSource3f(source, AL_VELOCITY, x * scale, y * scale, 0);
}

bool OpenALSource::isLooping() {
	int l;
	alGetSourcei(source, AL_LOOPING, &l);
	return l == AL_TRUE;
}

void OpenALSource::setLooping(bool l) {
	alSourcei(source, AL_LOOPING, l ? AL_TRUE : AL_FALSE);
}

void OpenALSource::setVolume(float v) {
	if (v == this->volume) return;
	this->SkeletonAudioSource::setVolume(v);
	alSourcef(source, AL_GAIN, getEffectiveVolume());
}

void OpenALSource::setMute(bool m) {
	if (m == muted) return;
	this->SkeletonAudioSource::setMute(m);
	alSourcef(source, AL_GAIN, getEffectiveVolume());
}

/* vim: set noet: */
<commit_msg>OpenALBackend: don't use al_throw_maybe when we should always throw<commit_after>/*
 *  OpenALBackend.cpp
 *  openc2e
 *
 *  Created by Bryan Donlan on Sun Aug 12 2007.
 *  Copyright (c) 2007 Bryan Donlan. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */

#include "OpenALBackend.h"
#include <alut.h>
#include <iostream>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <boost/lambda/bind.hpp>
#include "SDL.h"
#include "exceptions.h"
#include "World.h"

#ifndef OPENAL_SUPPORT
#error OPENAL_SUPPORT isn't set, so this file shouldn't be being compiled
#endif

const ALfloat zdist = -1.0;
const ALfloat plnemul = 0.0;
const ALfloat scale = 1.0/100;

static const ALfloat null_vec[] = { 0.0, 0.0, 0.0 };

static std::string al_error_str(ALenum err) {
	switch (err) {
		case AL_NO_ERROR:
			return std::string("No error");
		case AL_INVALID_NAME:
			return std::string("Invalid name");
		case AL_INVALID_ENUM:
			return std::string("Invalid enum");
		case AL_INVALID_VALUE:
			return std::string("Invalid value");
		case AL_INVALID_OPERATION:
			return std::string("Invalid operation");
		case AL_OUT_OF_MEMORY:
			return std::string("Out of memory (we're crashing soon)");
		default:
			return boost::str(boost::format("Unknown error %d") % (int)err);
	}
}

static void al_throw_maybe() {
	ALenum err = alGetError();
	if (err == AL_NO_ERROR) return;
	throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err % al_error_str(err)));
}

void OpenALBackend::init() {
	alGetError();
	alutGetError();

	if (!alutInitWithoutContext(NULL, NULL)) {
		ALenum err = alutGetError();
		throw creaturesException(boost::str(
			boost::format("Failed to init OpenAL/ALUT: %s") % alutGetErrorString(err)
		));
	}

	// It seems that relying on hardware drivers is an awful idea - always use software if it's available.
	// TODO: we should probably store this so we can (eventually) close stuff properly
	ALCdevice *device = alcOpenDevice("Generic Software");
	if (!device) {
		device = alcOpenDevice(NULL);
		if (!device) {
			ALenum err = alGetError();
			throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err % al_error_str(err)));
		}
	}
	ALCcontext *context = alcCreateContext(device, NULL);
	if (!context) {
		ALenum err = alGetError();
		throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err % al_error_str(err)));
	}
	alcMakeContextCurrent(context);

	setMute(false);

	static const ALfloat init_ori[6] = {
		0.0, 0.0, 1.0, /* at */
		0.0, -1.0, 0.0  /* up */
	};
	static const ALfloat init_vel[3] = {
		0.0, 0.0, 0.0
	};
	static const ALfloat init_pos[3] = {
		0.0, 0.0, 0.0
	};
	memcpy((void *)ListenerOri, (void *)init_ori, sizeof init_ori);
	memcpy((void *)ListenerVel, (void *)init_vel, sizeof init_vel);
	memcpy((void *)ListenerPos, (void *)init_pos, sizeof init_pos);

	ListenerPos[2] = zdist;
	updateListener();
}

void OpenALBackend::updateListener() {
	alListenerfv(AL_POSITION, ListenerPos);
	alListenerfv(AL_VELOCITY, ListenerVel);
	alListenerfv(AL_ORIENTATION, ListenerOri);
}

void OpenALBackend::setViewpointCenter(float x, float y) {
	ListenerPos[0] = x * scale;
	ListenerPos[1] = y * scale;
	updateListener();
}

void OpenALBackend::shutdown() {
	alutExit();
}

void OpenALBackend::setMute(bool m) {
	if (m)
		alListenerf(AL_GAIN, 0.0f);
	else
		alListenerf(AL_GAIN, 1.0f);
	muted = m;
}

boost::shared_ptr<AudioSource> OpenALBackend::newSource() {
	return boost::shared_ptr<AudioSource>(new OpenALSource(shared_from_this()));
}

AudioClip OpenALBackend::loadClip(const std::string &filename) {
	std::string fname = world.findFile(std::string("/Sounds/") + filename + ".wav");
	if (fname.size() == 0) return AudioClip();

	alGetError();
	ALuint buf = alutCreateBufferFromFile(fname.c_str());
	if (!buf) {
		ALenum err = alGetError();
		throw creaturesException(boost::str(
					boost::format("Failed to load %s: %s") % fname % alutGetErrorString(err)
					));
	}

	AudioClip clip(new OpenALBuffer(shared_from_this(), buf));
	return clip;
}

void OpenALBackend::begin() {
	alcSuspendContext(alcGetCurrentContext());
}

void OpenALBackend::commit() {
	alcProcessContext(alcGetCurrentContext());
}

OpenALSource::OpenALSource(boost::shared_ptr<class OpenALBackend> backend) {
	this->backend = backend;
	alGetError();
	alGenSources(1, &source);
	alSourcef(source, AL_PITCH, 1.0f);
	alSourcef(source, AL_GAIN, 1.0f);
	alSourcefv(source, AL_POSITION, null_vec);
	alSourcefv(source, AL_VELOCITY, null_vec);
	alSourcei(source, AL_LOOPING, 0);
	al_throw_maybe();
}

OpenALBuffer::OpenALBuffer(boost::shared_ptr<class OpenALBackend> backend, ALuint handle) {
	this->backend = backend;
	buffer = handle;
}

unsigned int OpenALBuffer::length_samples() {
	ALint bits;
	ALint channels;
	ALint size;
	alGetBufferi(buffer, AL_BITS, &bits);
	alGetBufferi(buffer, AL_CHANNELS, &channels);
	alGetBufferi(buffer, AL_SIZE, &size);
	return size / (bits / 8 * channels);
}

unsigned int OpenALBuffer::length_ms() {
	ALint freq;
	alGetBufferi(buffer, AL_FREQUENCY, &freq);
	return length_samples() * 1000 / freq;
}

AudioClip OpenALSource::getClip() {
	AudioClip clip(static_cast<AudioBuffer *>(this->clip.get()));
	return clip;
}

void OpenALSource::setClip(AudioClip &clip_) {
	OpenALBuffer *obp = dynamic_cast<OpenALBuffer *>(clip_.get());
	assert(obp);
	stop();
	this->clip = OpenALClip(obp);
	if (clip) {
		alSourcei(source, AL_BUFFER, clip->buffer);
	}
}

SourceState OpenALSource::getState() {
	int state;
	alGetSourcei(source, AL_SOURCE_STATE, &state);
	switch (state) {
		case AL_INITIAL: case AL_STOPPED: return SS_STOP;
		case AL_PLAYING: return SS_PLAY; /* XXX: AL_INITIAL? */
		case AL_PAUSED:  return SS_PAUSE;
		default:
		{
			std::cerr << "Unknown openal state, stopping stream: " << state << std::endl;
			stop();
			return SS_STOP;
		}
	}
}

void OpenALSource::play() {
	assert(clip);
	alSourcePlay(source);
}

void OpenALSource::stop() {
	alSourceStop(source);
}

void OpenALSource::pause() {
	alSourcePause(source);
}

static void fadeSource(boost::weak_ptr<AudioSource> s) {
	int fadeout = 500; // TODO: good value?

	/* We adjust gain every 10ms until we reach fadeout ms. */
	const int steps = fadeout / 10;
	const float adjust = 1.0 / (float)steps;
	for (int i = 0; i < steps; i++) {
		boost::shared_ptr<AudioSource> p = s.lock();
		if (!p) return;
		p->setVolume(1.0f - adjust * (float)i);
		SDL_Delay(10); // XXX: do we have some other portable sleeping primitive we can use?
		// boost::thread::sleep is a bit iffy, it seems
	}
	boost::shared_ptr<AudioSource> p = s.lock();
	if (p)
		p->stop();
}

void OpenALSource::fadeOut() {
	boost::thread th(boost::lambda::bind<void>(fadeSource, shared_from_this()));
}

void OpenALSource::setPos(float x, float y, float plane) {
	if (this->x == x && this->y == y && this->z == plane) return;
	this->SkeletonAudioSource::setPos(x, y, plane);
	alSource3f(source, AL_POSITION, x * scale, y * scale, plane * plnemul);
}

void OpenALSource::setVelocity(float x, float y) {
	// experiment with this later
	x = y = 0;
	alSource3f(source, AL_VELOCITY, x * scale, y * scale, 0);
}

bool OpenALSource::isLooping() {
	int l;
	alGetSourcei(source, AL_LOOPING, &l);
	return l == AL_TRUE;
}

void OpenALSource::setLooping(bool l) {
	alSourcei(source, AL_LOOPING, l ? AL_TRUE : AL_FALSE);
}

void OpenALSource::setVolume(float v) {
	if (v == this->volume) return;
	this->SkeletonAudioSource::setVolume(v);
	alSourcef(source, AL_GAIN, getEffectiveVolume());
}

void OpenALSource::setMute(bool m) {
	if (m == muted) return;
	this->SkeletonAudioSource::setMute(m);
	alSourcef(source, AL_GAIN, getEffectiveVolume());
}

/* vim: set noet: */
<|endoftext|>