<commit_before>/**
 *  For conditions of distribution and use, see copyright notice in license.txt
 *
 *  @file   JavascriptInstance.cpp
 *  @brief  Javascript script instance used wit EC_Script.
 */

#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "MemoryLeakCheck.h"
#include "JavascriptInstance.h"
#include "JavascriptModule.h"
#include "ScriptMetaTypeDefines.h"
#include "ScriptCoreTypeDefines.h"
#include "EC_Script.h"
#include "ScriptAsset.h"
#include "IModule.h"
#include "AssetAPI.h"
#include "IAssetStorage.h"

#include "LoggingFunctions.h"

#include <QFile>
#include <sstream>

#include <QScriptClass>
Q_DECLARE_METATYPE(QScriptClass*)

//#ifndef QT_NO_SCRIPTTOOLS
//#include <QScriptEngineDebugger>
//#endif

#include "MemoryLeakCheck.h"

JavascriptInstance::JavascriptInstance(const QString &fileName, JavascriptModule *module) :
    engine_(0),
    sourceFile(fileName),
    module_(module),
    evaluated(false)
{
    CreateEngine();
    Load();
}

JavascriptInstance::JavascriptInstance(ScriptAssetPtr scriptRef, JavascriptModule *module) :
    engine_(0),
    module_(module),
    evaluated(false)
{
    // Make sure we do not push null or empty script assets as sources
    if (scriptRef && !scriptRef->scriptContent.isEmpty())
        scriptRefs_.push_back(scriptRef);

    CreateEngine();
    Load();
}

JavascriptInstance::JavascriptInstance(const std::vector<ScriptAssetPtr>& scriptRefs, JavascriptModule *module) :
    engine_(0),
    module_(module),
    evaluated(false)
{
    // Make sure we do not push null or empty script assets as sources
    for (unsigned i = 0; i < scriptRefs.size(); ++i)
        if (scriptRefs[i] && !scriptRefs[i]->scriptContent.isEmpty()) scriptRefs_.push_back(scriptRefs[i]);

    CreateEngine();
    Load();
}

JavascriptInstance::~JavascriptInstance()
{
    DeleteEngine();
}

void JavascriptInstance::Load()
{
    if (!engine_)
        CreateEngine();

    if (sourceFile.isEmpty() && scriptRefs_.empty())
    {
        LogError("JavascriptInstance::Load: No script content to load!");
        return;
    }
    // Can't specify both a file source and an Asset API source.
    if (!sourceFile.isEmpty() && !scriptRefs_.empty())
    {
        LogError("JavascriptInstance::Load: Cannot specify both an local input source file and a list of script refs to load!");
        return;
    }

    bool useAssetAPI = !scriptRefs_.empty();
    unsigned numScripts = useAssetAPI ? scriptRefs_.size() : 1;

    // Determine based on code origin whether it can be trusted with system access or not
    if (useAssetAPI)
    {
        trusted_ = true;
        for(unsigned i = 0; i < scriptRefs_.size(); ++i)
        {
            AssetStoragePtr storage = scriptRefs_[i]->GetAssetStorage();
            trusted_ = trusted_ && storage && storage->Trusted();
        }
    }
    else // Local file: always trusted.
    {
        program_ = LoadScript(sourceFile);
        trusted_ = true; // This is a file on the local filesystem. We are making an assumption nobody can inject untrusted code here.
        // Actually, we are assuming the attacker does not know the absolute location of the asset cache locally here, since if he makes
        // the client to load a script into local cache, he could use this code path to automatically load that unsafe script from cache, and make it trusted. -jj.
    }

    // Check the validity of the syntax in the input.
    for (unsigned i = 0; i < numScripts; ++i)
    {
        QString scriptSourceFilename = (useAssetAPI ? scriptRefs_[i]->Name() : sourceFile);
        QString &scriptContent = (useAssetAPI ? scriptRefs_[i]->scriptContent : program_);

        QScriptSyntaxCheckResult syntaxResult = engine_->checkSyntax(scriptContent);
        if (syntaxResult.state() != QScriptSyntaxCheckResult::Valid)
        {
            LogError("Syntax error in script " + scriptSourceFilename + "," + QString::number(syntaxResult.errorLineNumber()) +
                ": " + syntaxResult.errorMessage());

            // Delete our loaded script content (if any exists).
            program_ == "";
        }
    }
}

QString JavascriptInstance::LoadScript(const QString &fileName)
{
    QString filename = fileName.trimmed();

    // First check if the include was supposed to go through the Asset API.
    if (module_)
    {
        ScriptAssetPtr asset = boost::dynamic_pointer_cast<ScriptAsset>(module_->GetFramework()->Asset()->GetAsset(fileName));
        if (asset)
            return asset->scriptContent;
    }

    // Otherwise, treat fileName as a local file to load up.

    QFile scriptFile(filename);
    if (!scriptFile.open(QIODevice::ReadOnly))
    {
        LogError("JavascriptInstance::LoadScript: Failed to load script from file " + filename + "!");
        return "";
    }

    QString result = scriptFile.readAll();
    scriptFile.close();

    QString trimmedResult = result.trimmed();
    if (trimmedResult.isEmpty())
    {
        LogWarning("JavascriptInstance::LoadScript: Warning Loaded script from file " + filename + ", but the content was empty.");
        return "";
    }
    return result;
}

void JavascriptInstance::Unload()
{
    DeleteEngine();
}

void JavascriptInstance::Run()
{
    // Need to have either absolute file path source or an Asset API source.
    if (scriptRefs_.empty() && program_.isEmpty())
    {
        LogError("JavascriptInstance::Run: Cannot run, no script reference loaded.");
        return;
    }

    // Can't specify both a file source and an Asset API source.
    assert(sourceFile.isEmpty() || scriptRefs_.empty());

    // If we've already evaluated this script once before, create a new script engine to run it again, or otherwise
    // the effects would stack (we'd possibly register into signals twice, or other odd side effects).
    // We never allow a script to be run twice in this kind of "stacking" manner.
    if (evaluated)
    {
        Unload();
        Load();
    }

    if (!engine_)
    {
        LogError("JavascriptInstance::Run: Cannot run, script engine not loaded.");
        return;
    }

    // If no script specified at all, we'll have to abort.
    if (program_.isEmpty() && scriptRefs_.empty())
        return;

    bool useAssets = !scriptRefs_.empty();
    unsigned numScripts = useAssets ? scriptRefs_.size() : 1;
    includedFiles.clear();

    for (unsigned i = 0; i < numScripts; ++i)
    {
        QString scriptSourceFilename = (useAssets ? scriptRefs_[i]->Name() : sourceFile);
        QString &scriptContent = (useAssets ? scriptRefs_[i]->scriptContent : program_);

        QScriptValue result = engine_->evaluate(scriptContent, scriptSourceFilename);
        CheckAndPrintException("In run/evaluate: ", result);
    }

    evaluated = true;
    emit ScriptEvaluated();
}

void JavascriptInstance::RegisterService(QObject *serviceObject, const QString &name)
{
    if (!engine_)
    {
        LogError("JavascriptInstance::RegisterService: No Qt script engine created when trying to register service to js script instance.");
        return;
    }
    if (!serviceObject)
    {
        LogError("JavascriptInstance::RegisterService: Trying to pass a null service object pointer to RegisterService!");
        return;
    }

    QScriptValue scriptValue = engine_->newQObject(serviceObject);
    engine_->globalObject().setProperty(name, scriptValue);
}

void JavascriptInstance::IncludeFile(const QString &path)
{
    for(uint i = 0; i < includedFiles.size(); ++i)
        if (includedFiles[i].toLower() == path.toLower())
        {
            LogDebug("JavascriptInstance::IncludeFile: Not including already included file " + path);
            return;
        }

    QString script = LoadScript(path);

    QScriptContext *context = engine_->currentContext();
    assert(context);
    if (!context)
    {
        LogError("JavascriptInstance::IncludeFile: QScriptEngine::currentContext() returned null!");
        return;
    }

    QScriptContext *parent = context->parentContext();
    if (!parent)
    {
        LogError("JavascriptInstance::IncludeFile: QScriptEngine::parentContext() returned null!");
        return;
    }

    context->setActivationObject(context->parentContext()->activationObject());
    context->setThisObject(context->parentContext()->thisObject());

    QScriptSyntaxCheckResult syntaxResult = engine_->checkSyntax(script);
    if(syntaxResult.state() != QScriptSyntaxCheckResult::Valid)
    {
        LogError("JavascriptInstance::IncludeFile: Syntax error in " + path + ". " + syntaxResult.errorMessage() +
            " In line:" + QString::number(syntaxResult.errorLineNumber()));
        return;
    }

    QScriptValue result = engine_->evaluate(script);

    includedFiles.push_back(path);

    if (engine_->hasUncaughtException())
        LogError(result.toString());
}

void JavascriptInstance::ImportExtension(const QString &scriptExtensionName)
{
    assert(engine_);
    if (!engine_)
    {
        LogWarning("JavascriptInstance::ImportExtension(" + scriptExtensionName + ") failed, QScriptEngine == null!");
        return;
    }

    QStringList qt_extension_whitelist;
    QStringList qt_class_blacklist;

    /// Allowed extension imports
    qt_extension_whitelist << "qt.core" << "qt.gui" << "qt.xml" << "qt.xmlpatterns" << "qt.opengl" << "qt.webkit";

    /// qt.core and qt.gui: Classes that may be harmful to your system from untrusted scripts
    qt_class_blacklist << "QLibrary" << "QPluginLoader" << "QProcess"               // process and library access
                       << "QFile" << "QDir" << "QFileSystemModel" << "QDirModel"    // file system access
                       << "QFileDialog" << "QFileSystemWatcher" << "QFileInfo"
                       << "QFileOpenEvent" << "QFileSystemModel"
                       << "QClipboard" << "QDesktopServices";                       // "system" access

    /// qt.webkit: Initial blacklist, enabling some of these can be discussed.
    /// Availble classes: QWebView, QGraphicsWebView, QWebPage, QWebFrame
    qt_class_blacklist << "QWebDatabase" << "QWebElement" << "QWebElementCollection" << "QWebHistory" << "QWebHistoryInterface" << "QWebHistoryItem"
                       << "QWebHitTestResult" << "QWebInspector" << "QWebPluginFactory" << "QWebSecurityOrigin" << "QWebSettings";

    if (!trusted_ && !qt_extension_whitelist.contains(scriptExtensionName, Qt::CaseInsensitive))
    {
        LogWarning("JavascriptInstance::ImportExtension: refusing to load a QtScript plugin for an untrusted instance: " + scriptExtensionName);
        return;
    }

    QScriptValue success = engine_->importExtension(scriptExtensionName);
    if (!success.isUndefined()) // Yes, importExtension returns undefinedValue if the import succeeds. http://doc.qt.nokia.com/4.7/qscriptengine.html#importExtension
        LogWarning("JavascriptInstance::ImportExtension: Failed to load " + scriptExtensionName + " plugin for QtScript!");

    if (!trusted_)
    {
        QScriptValue exposed;
        foreach (const QString &blacktype, qt_class_blacklist)
        {
            exposed = engine_->globalObject().property(blacktype);
            if (exposed.isValid())
            {
                engine_->globalObject().setProperty(blacktype, QScriptValue()); //passing an invalid val removes the property, http://doc.qt.nokia.com/4.6/qscriptvalue.html#setProperty
                //LogInfo("JavascriptInstance::ImportExtension: removed a type from the untrusted context: " + blacktype);
            }
        }
    }
}

bool JavascriptInstance::CheckAndPrintException(const QString& message, const QScriptValue& result)
{
    if (engine_->hasUncaughtException())
    {
        LogError(message + result.toString());
        foreach(const QString &error, engine_->uncaughtExceptionBacktrace())
            LogError(error);
        LogError("Line " + QString::number(engine_->uncaughtExceptionLineNumber()) + ".");
        engine_->clearExceptions();
        return true;
    }
    return false;
}

void JavascriptInstance::CreateEngine()
{
    if (engine_)
        DeleteEngine();
    engine_ = new QScriptEngine;
    connect(engine_, SIGNAL(signalHandlerException(const QScriptValue &)), SLOT(OnSignalHandlerException(const QScriptValue &)));
//#ifndef QT_NO_SCRIPTTOOLS
//    debugger_ = new QScriptEngineDebugger();
//    debugger.attachTo(engine_);
////  debugger_->action(QScriptEngineDebugger::InterruptAction)->trigger();
//#endif

    ExposeQtMetaTypes(engine_);
    ExposeCoreTypes(engine_);
    ExposeCoreApiMetaTypes(engine_);

    EC_Script *ec = dynamic_cast<EC_Script *>(owner_.lock().get());
    module_->PrepareScriptInstance(this, ec);
    evaluated = false;
}

void JavascriptInstance::DeleteEngine()
{
    if (!engine_)
        return;

    program_ = "";
    engine_->abortEvaluation();

    // As a convention, we call a function 'OnScriptDestroyed' for each JS script
    // so that they can clean up their data before the script is removed from the object,
    // or when the system is unloading.

    emit ScriptUnloading();

    QScriptValue destructor = engine_->globalObject().property("OnScriptDestroyed");
    if (!destructor.isUndefined())
    {
        QScriptValue result = destructor.call();
        CheckAndPrintException("In script destructor: ", result);
    }

    SAFE_DELETE(engine_);
    //SAFE_DELETE(debugger_);
}

void JavascriptInstance::OnSignalHandlerException(const QScriptValue& exception)
{
    LogError(exception.toString());
    foreach(const QString &error, engine_->uncaughtExceptionBacktrace())
        LogError(error);
    LogError("Line " + QString::number(engine_->uncaughtExceptionLineNumber()) + ".");
}
<commit_msg>Added an error print to detect odd situations where script assets exist in Asset API without a source storage.<commit_after>/**
 *  For conditions of distribution and use, see copyright notice in license.txt
 *
 *  @file   JavascriptInstance.cpp
 *  @brief  Javascript script instance used wit EC_Script.
 */

#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "MemoryLeakCheck.h"
#include "JavascriptInstance.h"
#include "JavascriptModule.h"
#include "ScriptMetaTypeDefines.h"
#include "ScriptCoreTypeDefines.h"
#include "EC_Script.h"
#include "ScriptAsset.h"
#include "IModule.h"
#include "AssetAPI.h"
#include "IAssetStorage.h"

#include "LoggingFunctions.h"

#include <QFile>
#include <sstream>

#include <QScriptClass>
Q_DECLARE_METATYPE(QScriptClass*)

//#ifndef QT_NO_SCRIPTTOOLS
//#include <QScriptEngineDebugger>
//#endif

#include "MemoryLeakCheck.h"

JavascriptInstance::JavascriptInstance(const QString &fileName, JavascriptModule *module) :
    engine_(0),
    sourceFile(fileName),
    module_(module),
    evaluated(false)
{
    CreateEngine();
    Load();
}

JavascriptInstance::JavascriptInstance(ScriptAssetPtr scriptRef, JavascriptModule *module) :
    engine_(0),
    module_(module),
    evaluated(false)
{
    // Make sure we do not push null or empty script assets as sources
    if (scriptRef && !scriptRef->scriptContent.isEmpty())
        scriptRefs_.push_back(scriptRef);

    CreateEngine();
    Load();
}

JavascriptInstance::JavascriptInstance(const std::vector<ScriptAssetPtr>& scriptRefs, JavascriptModule *module) :
    engine_(0),
    module_(module),
    evaluated(false)
{
    // Make sure we do not push null or empty script assets as sources
    for (unsigned i = 0; i < scriptRefs.size(); ++i)
        if (scriptRefs[i] && !scriptRefs[i]->scriptContent.isEmpty()) scriptRefs_.push_back(scriptRefs[i]);

    CreateEngine();
    Load();
}

JavascriptInstance::~JavascriptInstance()
{
    DeleteEngine();
}

void JavascriptInstance::Load()
{
    if (!engine_)
        CreateEngine();

    if (sourceFile.isEmpty() && scriptRefs_.empty())
    {
        LogError("JavascriptInstance::Load: No script content to load!");
        return;
    }
    // Can't specify both a file source and an Asset API source.
    if (!sourceFile.isEmpty() && !scriptRefs_.empty())
    {
        LogError("JavascriptInstance::Load: Cannot specify both an local input source file and a list of script refs to load!");
        return;
    }

    bool useAssetAPI = !scriptRefs_.empty();
    unsigned numScripts = useAssetAPI ? scriptRefs_.size() : 1;

    // Determine based on code origin whether it can be trusted with system access or not
    if (useAssetAPI)
    {
        trusted_ = true;
        for(unsigned i = 0; i < scriptRefs_.size(); ++i)
        {
            AssetStoragePtr storage = scriptRefs_[i]->GetAssetStorage();
            if (!storage)
                LogError("Error: Script asset \"" + scriptRefs_[i]->Name() + "\" does not have a source asset storage!");
            trusted_ = trusted_ && storage && storage->Trusted();
        }
    }
    else // Local file: always trusted.
    {
        program_ = LoadScript(sourceFile);
        trusted_ = true; // This is a file on the local filesystem. We are making an assumption nobody can inject untrusted code here.
        // Actually, we are assuming the attacker does not know the absolute location of the asset cache locally here, since if he makes
        // the client to load a script into local cache, he could use this code path to automatically load that unsafe script from cache, and make it trusted. -jj.
    }

    // Check the validity of the syntax in the input.
    for (unsigned i = 0; i < numScripts; ++i)
    {
        QString scriptSourceFilename = (useAssetAPI ? scriptRefs_[i]->Name() : sourceFile);
        QString &scriptContent = (useAssetAPI ? scriptRefs_[i]->scriptContent : program_);

        QScriptSyntaxCheckResult syntaxResult = engine_->checkSyntax(scriptContent);
        if (syntaxResult.state() != QScriptSyntaxCheckResult::Valid)
        {
            LogError("Syntax error in script " + scriptSourceFilename + "," + QString::number(syntaxResult.errorLineNumber()) +
                ": " + syntaxResult.errorMessage());

            // Delete our loaded script content (if any exists).
            program_ == "";
        }
    }
}

QString JavascriptInstance::LoadScript(const QString &fileName)
{
    QString filename = fileName.trimmed();

    // First check if the include was supposed to go through the Asset API.
    if (module_)
    {
        ScriptAssetPtr asset = boost::dynamic_pointer_cast<ScriptAsset>(module_->GetFramework()->Asset()->GetAsset(fileName));
        if (asset)
            return asset->scriptContent;
    }

    // Otherwise, treat fileName as a local file to load up.

    QFile scriptFile(filename);
    if (!scriptFile.open(QIODevice::ReadOnly))
    {
        LogError("JavascriptInstance::LoadScript: Failed to load script from file " + filename + "!");
        return "";
    }

    QString result = scriptFile.readAll();
    scriptFile.close();

    QString trimmedResult = result.trimmed();
    if (trimmedResult.isEmpty())
    {
        LogWarning("JavascriptInstance::LoadScript: Warning Loaded script from file " + filename + ", but the content was empty.");
        return "";
    }
    return result;
}

void JavascriptInstance::Unload()
{
    DeleteEngine();
}

void JavascriptInstance::Run()
{
    // Need to have either absolute file path source or an Asset API source.
    if (scriptRefs_.empty() && program_.isEmpty())
    {
        LogError("JavascriptInstance::Run: Cannot run, no script reference loaded.");
        return;
    }

    // Can't specify both a file source and an Asset API source.
    assert(sourceFile.isEmpty() || scriptRefs_.empty());

    // If we've already evaluated this script once before, create a new script engine to run it again, or otherwise
    // the effects would stack (we'd possibly register into signals twice, or other odd side effects).
    // We never allow a script to be run twice in this kind of "stacking" manner.
    if (evaluated)
    {
        Unload();
        Load();
    }

    if (!engine_)
    {
        LogError("JavascriptInstance::Run: Cannot run, script engine not loaded.");
        return;
    }

    // If no script specified at all, we'll have to abort.
    if (program_.isEmpty() && scriptRefs_.empty())
        return;

    bool useAssets = !scriptRefs_.empty();
    unsigned numScripts = useAssets ? scriptRefs_.size() : 1;
    includedFiles.clear();

    for (unsigned i = 0; i < numScripts; ++i)
    {
        QString scriptSourceFilename = (useAssets ? scriptRefs_[i]->Name() : sourceFile);
        QString &scriptContent = (useAssets ? scriptRefs_[i]->scriptContent : program_);

        QScriptValue result = engine_->evaluate(scriptContent, scriptSourceFilename);
        CheckAndPrintException("In run/evaluate: ", result);
    }

    evaluated = true;
    emit ScriptEvaluated();
}

void JavascriptInstance::RegisterService(QObject *serviceObject, const QString &name)
{
    if (!engine_)
    {
        LogError("JavascriptInstance::RegisterService: No Qt script engine created when trying to register service to js script instance.");
        return;
    }
    if (!serviceObject)
    {
        LogError("JavascriptInstance::RegisterService: Trying to pass a null service object pointer to RegisterService!");
        return;
    }

    QScriptValue scriptValue = engine_->newQObject(serviceObject);
    engine_->globalObject().setProperty(name, scriptValue);
}

void JavascriptInstance::IncludeFile(const QString &path)
{
    for(uint i = 0; i < includedFiles.size(); ++i)
        if (includedFiles[i].toLower() == path.toLower())
        {
            LogDebug("JavascriptInstance::IncludeFile: Not including already included file " + path);
            return;
        }

    QString script = LoadScript(path);

    QScriptContext *context = engine_->currentContext();
    assert(context);
    if (!context)
    {
        LogError("JavascriptInstance::IncludeFile: QScriptEngine::currentContext() returned null!");
        return;
    }

    QScriptContext *parent = context->parentContext();
    if (!parent)
    {
        LogError("JavascriptInstance::IncludeFile: QScriptEngine::parentContext() returned null!");
        return;
    }

    context->setActivationObject(context->parentContext()->activationObject());
    context->setThisObject(context->parentContext()->thisObject());

    QScriptSyntaxCheckResult syntaxResult = engine_->checkSyntax(script);
    if(syntaxResult.state() != QScriptSyntaxCheckResult::Valid)
    {
        LogError("JavascriptInstance::IncludeFile: Syntax error in " + path + ". " + syntaxResult.errorMessage() +
            " In line:" + QString::number(syntaxResult.errorLineNumber()));
        return;
    }

    QScriptValue result = engine_->evaluate(script);

    includedFiles.push_back(path);

    if (engine_->hasUncaughtException())
        LogError(result.toString());
}

void JavascriptInstance::ImportExtension(const QString &scriptExtensionName)
{
    assert(engine_);
    if (!engine_)
    {
        LogWarning("JavascriptInstance::ImportExtension(" + scriptExtensionName + ") failed, QScriptEngine == null!");
        return;
    }

    QStringList qt_extension_whitelist;
    QStringList qt_class_blacklist;

    /// Allowed extension imports
    qt_extension_whitelist << "qt.core" << "qt.gui" << "qt.xml" << "qt.xmlpatterns" << "qt.opengl" << "qt.webkit";

    /// qt.core and qt.gui: Classes that may be harmful to your system from untrusted scripts
    qt_class_blacklist << "QLibrary" << "QPluginLoader" << "QProcess"               // process and library access
                       << "QFile" << "QDir" << "QFileSystemModel" << "QDirModel"    // file system access
                       << "QFileDialog" << "QFileSystemWatcher" << "QFileInfo"
                       << "QFileOpenEvent" << "QFileSystemModel"
                       << "QClipboard" << "QDesktopServices";                       // "system" access

    /// qt.webkit: Initial blacklist, enabling some of these can be discussed.
    /// Availble classes: QWebView, QGraphicsWebView, QWebPage, QWebFrame
    qt_class_blacklist << "QWebDatabase" << "QWebElement" << "QWebElementCollection" << "QWebHistory" << "QWebHistoryInterface" << "QWebHistoryItem"
                       << "QWebHitTestResult" << "QWebInspector" << "QWebPluginFactory" << "QWebSecurityOrigin" << "QWebSettings";

    if (!trusted_ && !qt_extension_whitelist.contains(scriptExtensionName, Qt::CaseInsensitive))
    {
        LogWarning("JavascriptInstance::ImportExtension: refusing to load a QtScript plugin for an untrusted instance: " + scriptExtensionName);
        return;
    }

    QScriptValue success = engine_->importExtension(scriptExtensionName);
    if (!success.isUndefined()) // Yes, importExtension returns undefinedValue if the import succeeds. http://doc.qt.nokia.com/4.7/qscriptengine.html#importExtension
        LogWarning("JavascriptInstance::ImportExtension: Failed to load " + scriptExtensionName + " plugin for QtScript!");

    if (!trusted_)
    {
        QScriptValue exposed;
        foreach (const QString &blacktype, qt_class_blacklist)
        {
            exposed = engine_->globalObject().property(blacktype);
            if (exposed.isValid())
            {
                engine_->globalObject().setProperty(blacktype, QScriptValue()); //passing an invalid val removes the property, http://doc.qt.nokia.com/4.6/qscriptvalue.html#setProperty
                //LogInfo("JavascriptInstance::ImportExtension: removed a type from the untrusted context: " + blacktype);
            }
        }
    }
}

bool JavascriptInstance::CheckAndPrintException(const QString& message, const QScriptValue& result)
{
    if (engine_->hasUncaughtException())
    {
        LogError(message + result.toString());
        foreach(const QString &error, engine_->uncaughtExceptionBacktrace())
            LogError(error);
        LogError("Line " + QString::number(engine_->uncaughtExceptionLineNumber()) + ".");
        engine_->clearExceptions();
        return true;
    }
    return false;
}

void JavascriptInstance::CreateEngine()
{
    if (engine_)
        DeleteEngine();
    engine_ = new QScriptEngine;
    connect(engine_, SIGNAL(signalHandlerException(const QScriptValue &)), SLOT(OnSignalHandlerException(const QScriptValue &)));
//#ifndef QT_NO_SCRIPTTOOLS
//    debugger_ = new QScriptEngineDebugger();
//    debugger.attachTo(engine_);
////  debugger_->action(QScriptEngineDebugger::InterruptAction)->trigger();
//#endif

    ExposeQtMetaTypes(engine_);
    ExposeCoreTypes(engine_);
    ExposeCoreApiMetaTypes(engine_);

    EC_Script *ec = dynamic_cast<EC_Script *>(owner_.lock().get());
    module_->PrepareScriptInstance(this, ec);
    evaluated = false;
}

void JavascriptInstance::DeleteEngine()
{
    if (!engine_)
        return;

    program_ = "";
    engine_->abortEvaluation();

    // As a convention, we call a function 'OnScriptDestroyed' for each JS script
    // so that they can clean up their data before the script is removed from the object,
    // or when the system is unloading.

    emit ScriptUnloading();

    QScriptValue destructor = engine_->globalObject().property("OnScriptDestroyed");
    if (!destructor.isUndefined())
    {
        QScriptValue result = destructor.call();
        CheckAndPrintException("In script destructor: ", result);
    }

    SAFE_DELETE(engine_);
    //SAFE_DELETE(debugger_);
}

void JavascriptInstance::OnSignalHandlerException(const QScriptValue& exception)
{
    LogError(exception.toString());
    foreach(const QString &error, engine_->uncaughtExceptionBacktrace())
        LogError(error);
    LogError("Line " + QString::number(engine_->uncaughtExceptionLineNumber()) + ".");
}
<|endoftext|>


<commit_before>/*
 * Copyright 2010,
 * François Bleibel,
 * Olivier Stasse,
 *
 * CNRS/AIST
 *
 * This file is part of sot-dynamic.
 * sot-dynamic 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 3 of
 * the License, or (at your option) any later version.
 * sot-dynamic is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.  You should
 * have received a copy of the GNU Lesser General Public License along
 * with sot-dynamic.  If not, see <http://www.gnu.org/licenses/>.
 */

/** \mainpage
\section sec_intro Introduction

The sot-dynamic package is a bridge between the stack of tasks framework and the dynamicsJRLJapan library.
It provides an inverse dynamic model of the robot through dynamic-graph entities.
More precisely it wraps the newton euler algorithm implemented by the dynamicsJRLJapan library
to make it accessible for the stack of tasks controllers
(in the Stack of Tasks Framework as defined in \ref Mansard2007.)

This package depends on the following packages:
\li dynamicsJRLJapan
\li sot-core
\li dynamic-graph
Optional packages (for specific support of the hrp2-N robots)
\li hrp210optimized
\li hrp2-dynamics

See the JRL umi3218's page on github for instructions on how to download and
install these packages at https://github.com/jrl-umi3218.

\section overview API overview
As most packages based on the dynamic-graph framework (see https://github.com/jrl-umi3218/dynamic-graph),
the functionnality is exposed through entities. Hence .so or .dll (dynamic-link) libraries are
generated in the dynamic-graph plugins directory.

The following entities are created by this package:\n
(all entites are placed in the namespace sot::)
\li sot::ZmprefFromCom
\li sot::ForceCompensation
\li sot::IntegratorForceExact
\li sot::MassApparent
\li sot::IntegratorForceRk4
\li sot::IntegratorForce
\li sot::AngleEstimator
\li sot::WaistAttitudeFromSensor
\li sot::Dynamic - provides the inverse dynamics computations for of a humanoid robot

Optionally, if the packages in brackets are installed, the following entities
are made available:
\li sot::DynamicHrp2 - same as sot::Dynamic, but specialized for hrp2 robots [needs hrp2-dynamics]
\li sot::DynamicHrp2_10 - same as previous, optimized for the hrp2-10 robot [needs hrp210optimized]
\li sot::DynamicHrp2_10_old [needs hrp210optimized]

See each entity's documentation page for more information (when available).

\section References
\anchor Mansard2007

"Task sequencing for sensor-based control",
N. Mansard, F. Chaumette,
IEEE Trans. on Robotics, 23(1):60-72, February 2007

**/
<commit_msg>Update doxygen main page and reference sphinx-documentation.<commit_after>/*
 * Copyright 2010,
 * François Bleibel,
 * Olivier Stasse,
 *
 * CNRS/AIST
 *
 * This file is part of sot-dynamic.
 * sot-dynamic 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 3 of
 * the License, or (at your option) any later version.
 * sot-dynamic is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.  You should
 * have received a copy of the GNU Lesser General Public License along
 * with sot-dynamic.  If not, see <http://www.gnu.org/licenses/>.
 */

/** \mainpage
\section sot_dynamic_section_introduction Introduction

The sot-dynamic package is a bridge between the stack of tasks framework and the dynamicsJRLJapan library.
It provides an inverse dynamic model of the robot through dynamic-graph entities.
More precisely it wraps the newton euler algorithm implemented by the dynamicsJRLJapan library
to make it accessible for the stack of tasks controllers
(in the Stack of Tasks Framework as defined in \ref Mansard2007.)

This package depends on the following packages:
\li dynamicsJRLJapan
\li sot-core
\li dynamic-graph
\li dynamic-graph-python
Optional packages (for specific support of the hrp2-N robots)
\li hrp210optimized
\li hrp2-dynamics

See the JRL umi3218's page on github for instructions on how to download and
install these packages at https://github.com/jrl-umi3218.

\section python_bindings Python bindings

As most packages based on the dynamic-graph framework (see https://github.com/jrl-umi3218/dynamic-graph),
the functionnality is exposed through entities. Hence python sub-modules of dynamic_graph  are generated. See sphinx documentation for more details.

The following entities are created by this package:\n
(all entites are placed in the namespace sot::)
\li sot::ZmprefFromCom
\li sot::ForceCompensation
\li sot::IntegratorForceExact
\li sot::MassApparent
\li sot::IntegratorForceRk4
\li sot::IntegratorForce
\li sot::AngleEstimator
\li sot::WaistAttitudeFromSensor
\li sot::Dynamic - provides the inverse dynamics computations for of a humanoid robot

Optionally, if the packages in brackets are installed, the following entities
are made available:
\li sot::DynamicHrp2 - same as sot::Dynamic, but specialized for hrp2 robots [needs hrp2-dynamics]
\li sot::DynamicHrp2_10 - same as previous, optimized for the hrp2-10 robot [needs hrp210optimized]
\li sot::DynamicHrp2_10_old [needs hrp210optimized]

See each entity's documentation page for more information (when available).

\section References
\anchor Mansard2007

"Task sequencing for sensor-based control",
N. Mansard, F. Chaumette,
IEEE Trans. on Robotics, 23(1):60-72, February 2007

**/
<|endoftext|>


<commit_before>/// HEADER
#include <csapex/utility/subprocess.h>
#include <csapex/utility/assert.h>

/// SYSTEM
#include <iostream>
#include <boost/optional.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <unistd.h>
#include <fstream>

using namespace csapex;

namespace detail
{
Subprocess* g_sp_instance = nullptr;

void sp_signal_handler(int signal)
{
    if(g_sp_instance) {
        g_sp_instance->handleSignal(signal);
    }
    std::quick_exit(0);
}
}

Subprocess::Subprocess(const std::string& name_space)
    : in(name_space + "_in", false, 65536),
      out(name_space + "_out", false, 65536),
      ctrl_in(name_space + "_ctrl", true, 1024),
      ctrl_out(name_space + "_ctrl", true, 1024),
      pid_(-1),
      is_shutdown(false),
      return_code(0)

{
    if (pipe(pipe_in)) {
        throw std::runtime_error("cannot create pipe for stdin");
    }
    if (pipe(pipe_out)) {
        close(pipe_in[0]);
        close(pipe_in[1]);
        throw std::runtime_error("cannot create pipe for stdout");
    }
    if (pipe(pipe_err)) {
        close(pipe_out[0]);
        close(pipe_out[1]);
        close(pipe_in[0]);
        close(pipe_in[1]);
        throw std::runtime_error("cannot create pipe for stderr");
    }

    active_ = true;
}

Subprocess::~Subprocess()
{
    if(isParent()) {
        while(ctrl_out.hasMessage()) {
            readCtrlOut();
        }
        if(!isChildShutdown()) {
            ctrl_in.write({SubprocessChannel::MessageType::SHUTDOWN, "shutdown"});
        }

        close(pipe_err[1]);
        close(pipe_out[0]);
        close(pipe_in[0]);

        join();
    }
}

bool Subprocess::isChild() const
{
    return pid_ == 0;
}

bool Subprocess::isParent() const
{
    return pid_ > 0;
}

void Subprocess::handleSignal(int signal)
{
    if(active_) {
        flush();

        close(pipe_in[0]);
        close(pipe_out[1]);
        close(pipe_err[1]);

        out.write({SubprocessChannel::MessageType::CHILD_SIGNAL, std::to_string(signal)});
        ctrl_out.write({SubprocessChannel::MessageType::CHILD_SIGNAL, std::to_string(signal)});
    }
}

pid_t Subprocess::fork(std::function<int()> child)
{
    pid_ = ::fork();
    if(pid_ == 0) {
        close(pipe_in[1]);
        close(pipe_out[0]);
        close(pipe_err[0]);

        //        dup2(pipe_in[0], 0);
        //        dup2(pipe_out[1], 1);
        //        dup2(pipe_err[1], 2);

        detail::g_sp_instance = this;
        std::signal(SIGHUP	 , detail::sp_signal_handler);
        std::signal(SIGINT	 , detail::sp_signal_handler);
        std::signal(SIGQUIT	 , detail::sp_signal_handler);
        std::signal(SIGILL	 , detail::sp_signal_handler);
        std::signal(SIGTRAP	 , detail::sp_signal_handler);
        std::signal(SIGABRT	 , detail::sp_signal_handler);
        std::signal(SIGIOT	 , detail::sp_signal_handler);
        std::signal(SIGBUS	 , detail::sp_signal_handler);
        std::signal(SIGFPE	 , detail::sp_signal_handler);
        std::signal(SIGKILL	 , detail::sp_signal_handler);
        std::signal(SIGUSR1	 , detail::sp_signal_handler);
        std::signal(SIGSEGV	 , detail::sp_signal_handler);
        std::signal(SIGUSR2	 , detail::sp_signal_handler);
        std::signal(SIGPIPE	 , detail::sp_signal_handler);
        std::signal(SIGALRM	 , detail::sp_signal_handler);
        std::signal(SIGTERM	 , detail::sp_signal_handler);
        std::signal(SIGSTKFLT, detail::sp_signal_handler);
        //std::signal(SIGCLD	 , detail::sp_signal_handler);
        //std::signal(SIGCHLD	 , detail::sp_signal_handler);
        //std::signal(SIGCONT	 , detail::sp_signal_handler);
        //std::signal(SIGSTOP	 , detail::sp_signal_handler);
        //std::signal(SIGTSTP	 , detail::sp_signal_handler);
        //std::signal(SIGTTIN	 , detail::sp_signal_handler);
        //std::signal(SIGTTOU	 , detail::sp_signal_handler);
        //std::signal(SIGURG	 , detail::sp_signal_handler);
        std::signal(SIGXCPU	 , detail::sp_signal_handler);
        std::signal(SIGXFSZ	 , detail::sp_signal_handler);
        std::signal(SIGVTALRM, detail::sp_signal_handler);
        std::signal(SIGPROF	 , detail::sp_signal_handler);
        //std::signal(SIGWINCH , detail::sp_signal_handler);
        std::signal(SIGPOLL	 , detail::sp_signal_handler);
        std::signal(SIGIO	 , detail::sp_signal_handler);
        std::signal(SIGPWR	 , detail::sp_signal_handler);
        std::signal(SIGSYS	 , detail::sp_signal_handler);
        std::signal(SIGUNUSED, detail::sp_signal_handler);

        subprocess_worker_ = std::thread([this](){
            while(active_) {
                try {
                    SubprocessChannel::Message m = ctrl_in.read();
                    switch(m.type)
                    {
                    case SubprocessChannel::MessageType::SHUTDOWN:
                        active_ = false;
                        in.shutdown();
                        out.shutdown();
                        break;
                    }
                } catch(const SubprocessChannel::ShutdownException& e) {
                    active_ = false;
                }
            }
        });

        int return_code = 0;
        try {
            return_code = child();

        } catch(const std::exception& e) {
            if (active_) {
                out.write({SubprocessChannel::MessageType::CHILD_ERROR, e.what()});
            }
            return_code = -1;

        } catch(...) {
            if (active_) {
                out.write({SubprocessChannel::MessageType::CHILD_ERROR, "unknown error"});
            }

            return_code = -2;
        }

        active_ = false;
        ctrl_in.shutdown();
        subprocess_worker_.join();

        close(pipe_in[0]);
        close(pipe_out[1]);
        close(pipe_err[1]);

        // then send the end of program signal
        ctrl_out.write({SubprocessChannel::MessageType::CHILD_EXIT, std::to_string(return_code)});

        std::quick_exit(0);

    } else {
        close(pipe_in[0]);
        close(pipe_out[1]);
        close(pipe_err[1]);

        const std::size_t N = 32;

        // TODO: extract "pipe" class
        parent_worker_cout_ = std::thread([&]() {
            while(active_) {
                char buf[N+1];
                int r;
                while((r = read(pipe_out[0], &buf, N)) > 0) {
                    buf[r] = 0;
                    child_cout << buf;
                }
            }
        });

        parent_worker_cerr_ = std::thread([&]() {
            while(active_) {
                char buf[N+1];
                int r;
                while((r = read(pipe_err[0], &buf, N)) > 0) {
                    buf[r] = 0;
                    child_cerr << buf;
                }
            }
        });
    }

    return pid_;
}

void Subprocess::flush()
{
    if(isChild()) {
        fflush(stdout);
        fflush(stderr);
    } else {
        apex_assert_hard(active_);
    }
}

std::string Subprocess::getChildStdOut() const
{
    return child_cout.str();
}
std::string Subprocess::getChildStdErr() const
{
    return child_cerr.str();
}

bool Subprocess::isChildShutdown() const
{
    return is_shutdown;
}

void Subprocess::readCtrlOut()
{
    SubprocessChannel::Message message = ctrl_out.read();
    switch(message.type) {
    case SubprocessChannel::MessageType::CHILD_EXIT:
    case SubprocessChannel::MessageType::CHILD_SIGNAL:
    {
        is_shutdown = true;
        std::stringstream ss(message.toString());
        ss >> return_code;
    }
        break;
    case SubprocessChannel::MessageType::CHILD_ERROR:
        is_shutdown = true;
        return_code = 64;
        break;
    default:
        std::cout << "read an unknown message: " << message.toString() << std::endl;
        break;
    }
}

int Subprocess::join()
{
    apex_assert_hard(isParent());

    while(!isChildShutdown()) {
        readCtrlOut();
    }

    close(pipe_out[0]);
    close(pipe_err[0]);

    active_ = false;
    if(parent_worker_cerr_.joinable()) {
        parent_worker_cerr_.join();
    }
    if(parent_worker_cout_.joinable()) {
        parent_worker_cout_.join();
    }

    return return_code;
}

bool Subprocess::isActive() const
{
    return active_;
}
<commit_msg>Reenable subprocess output stream syphoning<commit_after>/// HEADER
#include <csapex/utility/subprocess.h>
#include <csapex/utility/assert.h>

/// SYSTEM
#include <iostream>
#include <boost/optional.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <unistd.h>
#include <fstream>

using namespace csapex;

namespace detail
{
Subprocess* g_sp_instance = nullptr;

void sp_signal_handler(int signal)
{
    if(g_sp_instance) {
        g_sp_instance->handleSignal(signal);
    }
    std::quick_exit(0);
}
}

Subprocess::Subprocess(const std::string& name_space)
    : in(name_space + "_in", false, 65536),
      out(name_space + "_out", false, 65536),
      ctrl_in(name_space + "_ctrl", true, 1024),
      ctrl_out(name_space + "_ctrl", true, 1024),
      pid_(-1),
      is_shutdown(false),
      return_code(0)

{
    if (pipe(pipe_in)) {
        throw std::runtime_error("cannot create pipe for stdin");
    }
    if (pipe(pipe_out)) {
        close(pipe_in[0]);
        close(pipe_in[1]);
        throw std::runtime_error("cannot create pipe for stdout");
    }
    if (pipe(pipe_err)) {
        close(pipe_out[0]);
        close(pipe_out[1]);
        close(pipe_in[0]);
        close(pipe_in[1]);
        throw std::runtime_error("cannot create pipe for stderr");
    }

    active_ = true;
}

Subprocess::~Subprocess()
{
    if(isParent()) {
        while(ctrl_out.hasMessage()) {
            readCtrlOut();
        }
        if(!isChildShutdown()) {
            ctrl_in.write({SubprocessChannel::MessageType::SHUTDOWN, "shutdown"});
        }

        close(pipe_err[1]);
        close(pipe_out[0]);
        close(pipe_in[0]);

        join();
    }
}

bool Subprocess::isChild() const
{
    return pid_ == 0;
}

bool Subprocess::isParent() const
{
    return pid_ > 0;
}

void Subprocess::handleSignal(int signal)
{
    if(active_) {
        flush();

        close(pipe_in[0]);
        close(pipe_out[1]);
        close(pipe_err[1]);

        out.write({SubprocessChannel::MessageType::CHILD_SIGNAL, std::to_string(signal)});
        ctrl_out.write({SubprocessChannel::MessageType::CHILD_SIGNAL, std::to_string(signal)});
    }
}

pid_t Subprocess::fork(std::function<int()> child)
{
    pid_ = ::fork();
    if(pid_ == 0) {
        close(pipe_in[1]);
        close(pipe_out[0]);
        close(pipe_err[0]);

        dup2(pipe_in[0], 0);
        dup2(pipe_out[1], 1);
        dup2(pipe_err[1], 2);

        detail::g_sp_instance = this;
        std::signal(SIGHUP	 , detail::sp_signal_handler);
        std::signal(SIGINT	 , detail::sp_signal_handler);
        std::signal(SIGQUIT	 , detail::sp_signal_handler);
        std::signal(SIGILL	 , detail::sp_signal_handler);
        std::signal(SIGTRAP	 , detail::sp_signal_handler);
        std::signal(SIGABRT	 , detail::sp_signal_handler);
        std::signal(SIGIOT	 , detail::sp_signal_handler);
        std::signal(SIGBUS	 , detail::sp_signal_handler);
        std::signal(SIGFPE	 , detail::sp_signal_handler);
        std::signal(SIGKILL	 , detail::sp_signal_handler);
        std::signal(SIGUSR1	 , detail::sp_signal_handler);
        std::signal(SIGSEGV	 , detail::sp_signal_handler);
        std::signal(SIGUSR2	 , detail::sp_signal_handler);
        std::signal(SIGPIPE	 , detail::sp_signal_handler);
        std::signal(SIGALRM	 , detail::sp_signal_handler);
        std::signal(SIGTERM	 , detail::sp_signal_handler);
        std::signal(SIGSTKFLT, detail::sp_signal_handler);
        //std::signal(SIGCLD	 , detail::sp_signal_handler);
        //std::signal(SIGCHLD	 , detail::sp_signal_handler);
        //std::signal(SIGCONT	 , detail::sp_signal_handler);
        //std::signal(SIGSTOP	 , detail::sp_signal_handler);
        //std::signal(SIGTSTP	 , detail::sp_signal_handler);
        //std::signal(SIGTTIN	 , detail::sp_signal_handler);
        //std::signal(SIGTTOU	 , detail::sp_signal_handler);
        //std::signal(SIGURG	 , detail::sp_signal_handler);
        std::signal(SIGXCPU	 , detail::sp_signal_handler);
        std::signal(SIGXFSZ	 , detail::sp_signal_handler);
        std::signal(SIGVTALRM, detail::sp_signal_handler);
        std::signal(SIGPROF	 , detail::sp_signal_handler);
        //std::signal(SIGWINCH , detail::sp_signal_handler);
        std::signal(SIGPOLL	 , detail::sp_signal_handler);
        std::signal(SIGIO	 , detail::sp_signal_handler);
        std::signal(SIGPWR	 , detail::sp_signal_handler);
        std::signal(SIGSYS	 , detail::sp_signal_handler);
        std::signal(SIGUNUSED, detail::sp_signal_handler);

        subprocess_worker_ = std::thread([this](){
            while(active_) {
                try {
                    SubprocessChannel::Message m = ctrl_in.read();
                    switch(m.type)
                    {
                    case SubprocessChannel::MessageType::SHUTDOWN:
                        active_ = false;
                        in.shutdown();
                        out.shutdown();
                        break;
                    }
                } catch(const SubprocessChannel::ShutdownException& e) {
                    active_ = false;
                }
            }
        });

        int return_code = 0;
        try {
            return_code = child();

        } catch(const std::exception& e) {
            if (active_) {
                out.write({SubprocessChannel::MessageType::CHILD_ERROR, e.what()});
            }
            return_code = -1;

        } catch(...) {
            if (active_) {
                out.write({SubprocessChannel::MessageType::CHILD_ERROR, "unknown error"});
            }

            return_code = -2;
        }

        active_ = false;
        ctrl_in.shutdown();
        subprocess_worker_.join();

        close(pipe_in[0]);
        close(pipe_out[1]);
        close(pipe_err[1]);

        // then send the end of program signal
        ctrl_out.write({SubprocessChannel::MessageType::CHILD_EXIT, std::to_string(return_code)});

        std::quick_exit(0);

    } else {
        close(pipe_in[0]);
        close(pipe_out[1]);
        close(pipe_err[1]);

        const std::size_t N = 32;

        // TODO: extract "pipe" class
        parent_worker_cout_ = std::thread([&]() {
            while(active_) {
                char buf[N+1];
                int r;
                while((r = read(pipe_out[0], &buf, N)) > 0) {
                    buf[r] = 0;
                    child_cout << buf;
                }
            }
        });

        parent_worker_cerr_ = std::thread([&]() {
            while(active_) {
                char buf[N+1];
                int r;
                while((r = read(pipe_err[0], &buf, N)) > 0) {
                    buf[r] = 0;
                    child_cerr << buf;
                }
            }
        });
    }

    return pid_;
}

void Subprocess::flush()
{
    if(isChild()) {
        fflush(stdout);
        fflush(stderr);
    } else {
        apex_assert_hard(active_);
    }
}

std::string Subprocess::getChildStdOut() const
{
    return child_cout.str();
}
std::string Subprocess::getChildStdErr() const
{
    return child_cerr.str();
}

bool Subprocess::isChildShutdown() const
{
    return is_shutdown;
}

void Subprocess::readCtrlOut()
{
    SubprocessChannel::Message message = ctrl_out.read();
    switch(message.type) {
    case SubprocessChannel::MessageType::CHILD_EXIT:
    case SubprocessChannel::MessageType::CHILD_SIGNAL:
    {
        is_shutdown = true;
        std::stringstream ss(message.toString());
        ss >> return_code;
    }
        break;
    case SubprocessChannel::MessageType::CHILD_ERROR:
        is_shutdown = true;
        return_code = 64;
        break;
    default:
        std::cout << "read an unknown message: " << message.toString() << std::endl;
        break;
    }
}

int Subprocess::join()
{
    apex_assert_hard(isParent());

    while(!isChildShutdown()) {
        readCtrlOut();
    }

    close(pipe_out[0]);
    close(pipe_err[0]);

    active_ = false;
    if(parent_worker_cerr_.joinable()) {
        parent_worker_cerr_.join();
    }
    if(parent_worker_cout_.joinable()) {
        parent_worker_cout_.join();
    }

    return return_code;
}

bool Subprocess::isActive() const
{
    return active_;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2013, Project OSRM, Dennis Luxen, others
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include <osrm/Coordinate.h>

#include "DouglasPeucker.h"
#include "../DataStructures/SegmentInformation.h"

#include <boost/assert.hpp>

#include <cmath>

#include <limits>

DouglasPeucker::DouglasPeucker()
    : douglas_peucker_thresholds({262144., // z0
                                  131072., // z1
                                  65536.,  // z2
                                  32768.,  // z3
                                  16384.,  // z4
                                  8192.,   // z5
                                  4096.,   // z6
                                  2048.,   // z7
                                  960.,    // z8
                                  480.,    // z9
                                  240.,    // z10
                                  90.,     // z11
                                  50.,     // z12
                                  25.,     // z13
                                  15.,     // z14
                                  5.,      // z15
                                  .65,     // z16
                                  .5,      // z17
                                  .35      // z18
      })
{
}

void DouglasPeucker::Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level)
{
    BOOST_ASSERT_MSG(!input_geometry.empty(), "geometry invalid");
    if (input_geometry.size() <= 2)
    {
        return;
    }

    {
        BOOST_ASSERT_MSG(zoom_level < 19, "unsupported zoom level");
        unsigned left_border = 0;
        unsigned right_border = 1;
        // Sweep over array and identify those ranges that need to be checked
        do
        {
            BOOST_ASSERT_MSG(input_geometry[left_border].necessary,
                             "left border must be necessary");
            BOOST_ASSERT_MSG(input_geometry.back().necessary, "right border must be necessary");

            if (input_geometry[right_border].necessary)
            {
                recursion_stack.emplace(left_border, right_border);
                left_border = right_border;
            }
            ++right_border;
        } while (right_border < input_geometry.size());
    }
    while (!recursion_stack.empty())
    {
        // pop next element
        const GeometryRange pair = recursion_stack.top();
        recursion_stack.pop();
        BOOST_ASSERT_MSG(input_geometry[pair.first].necessary, "left border mus be necessary");
        BOOST_ASSERT_MSG(input_geometry[pair.second].necessary, "right border must be necessary");
        BOOST_ASSERT_MSG(pair.second < input_geometry.size(), "right border outside of geometry");
        BOOST_ASSERT_MSG(pair.first < pair.second, "left border on the wrong side");
        double max_distance = std::numeric_limits<double>::min();

        unsigned farthest_element_index = pair.second;
        // find index idx of element with max_distance
        for (unsigned i = pair.first + 1; i < pair.second; ++i)
        {
            const double temp_dist = FixedPointCoordinate::ComputePerpendicularDistance(
                input_geometry[i].location,
                input_geometry[pair.first].location,
                input_geometry[pair.second].location);
            const double distance = std::abs(temp_dist);
            if (distance > douglas_peucker_thresholds[zoom_level] && distance > max_distance)
            {
                farthest_element_index = i;
                max_distance = distance;
            }
        }
        if (max_distance > douglas_peucker_thresholds[zoom_level])
        {
            //  mark idx as necessary
            input_geometry[farthest_element_index].necessary = true;
            if (1 < (farthest_element_index - pair.first))
            {
                recursion_stack.emplace(pair.first, farthest_element_index);
            }
            if (1 < (pair.second - farthest_element_index))
            {
                recursion_stack.emplace(farthest_element_index, pair.second);
            }
        }
    }
}
<commit_msg>fix regression in debug build<commit_after>/*

Copyright (c) 2013, Project OSRM, Dennis Luxen, others
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include <osrm/Coordinate.h>

#include "DouglasPeucker.h"
#include "../DataStructures/SegmentInformation.h"
#include "../Util/SimpleLogger.h"

#include <boost/assert.hpp>

#include <cmath>

#include <limits>

DouglasPeucker::DouglasPeucker()
    : douglas_peucker_thresholds({262144., // z0
                                  131072., // z1
                                  65536.,  // z2
                                  32768.,  // z3
                                  16384.,  // z4
                                  8192.,   // z5
                                  4096.,   // z6
                                  2048.,   // z7
                                  960.,    // z8
                                  480.,    // z9
                                  240.,    // z10
                                  90.,     // z11
                                  50.,     // z12
                                  25.,     // z13
                                  15.,     // z14
                                  5.,      // z15
                                  .65,     // z16
                                  .5,      // z17
                                  .35      // z18
      })
{
}

void DouglasPeucker::Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level)
{
    input_geometry.front().necessary = true;
    input_geometry.back().necessary = true;

    BOOST_ASSERT_MSG(!input_geometry.empty(), "geometry invalid");
    if (input_geometry.size() < 2)
    {
        return;
    }

    SimpleLogger().Write() << "input_geometry.size()=" << input_geometry.size();

    {
        BOOST_ASSERT_MSG(zoom_level < 19, "unsupported zoom level");
        unsigned left_border = 0;
        unsigned right_border = 1;
        // Sweep over array and identify those ranges that need to be checked
        do
        {
            if (!input_geometry[left_border].necessary)
            {
              SimpleLogger().Write() << "broken interval [" << left_border << "," << right_border << "]";
            }
            BOOST_ASSERT_MSG(input_geometry[left_border].necessary,
                             "left border must be necessary");
            BOOST_ASSERT_MSG(input_geometry.back().necessary, "right border must be necessary");

            if (input_geometry[right_border].necessary)
            {
                recursion_stack.emplace(left_border, right_border);
                left_border = right_border;
            }
            ++right_border;
        } while (right_border < input_geometry.size());
    }
    while (!recursion_stack.empty())
    {
        // pop next element
        const GeometryRange pair = recursion_stack.top();
        recursion_stack.pop();
        BOOST_ASSERT_MSG(input_geometry[pair.first].necessary, "left border mus be necessary");
        BOOST_ASSERT_MSG(input_geometry[pair.second].necessary, "right border must be necessary");
        BOOST_ASSERT_MSG(pair.second < input_geometry.size(), "right border outside of geometry");
        BOOST_ASSERT_MSG(pair.first < pair.second, "left border on the wrong side");
        double max_distance = std::numeric_limits<double>::min();

        unsigned farthest_element_index = pair.second;
        // find index idx of element with max_distance
        for (unsigned i = pair.first + 1; i < pair.second; ++i)
        {
            const double temp_dist = FixedPointCoordinate::ComputePerpendicularDistance(
                input_geometry[i].location,
                input_geometry[pair.first].location,
                input_geometry[pair.second].location);
            const double distance = std::abs(temp_dist);
            if (distance > douglas_peucker_thresholds[zoom_level] && distance > max_distance)
            {
                farthest_element_index = i;
                max_distance = distance;
            }
        }
        if (max_distance > douglas_peucker_thresholds[zoom_level])
        {
            //  mark idx as necessary
            input_geometry[farthest_element_index].necessary = true;
            if (1 < (farthest_element_index - pair.first))
            {
                recursion_stack.emplace(pair.first, farthest_element_index);
            }
            if (1 < (pair.second - farthest_element_index))
            {
                recursion_stack.emplace(farthest_element_index, pair.second);
            }
        }
    }
}
<|endoftext|>


<commit_before>#include <iostream>
#include <Poco/Logger.h>
#include <Poco/Message.h>
#include <Poco/File.h>
#include <Poco/Util/Option.h>
#include <Poco/Util/OptionSet.h>
#include <Poco/Util/HelpFormatter.h>
#include <Poco/Util/ServerApplication.h>

#include "server/MongooseServer.h"
#include "di/DependencyInjector.h"
#include "UIServerModule.h"

using namespace std;
using namespace Poco;
using namespace Poco::Util;
using namespace BeeeOn;

#define DEFAULT_PORT 8000

#define LOCAL_CONFIG_FILE  "logging.ini"
#define SYSTEM_CONFIG_FILE "/etc/beeeon/ui-server/logging.ini"

#define LOCAL_SERVICES_FILE  "services.xml"
#define SYSTEM_SERVICES_FILE "/etc/beeeon/ui-server/services.xml"

static Option optServices("services", "s",
		"services configuration file to be used (xml, ini, properties)",
		false,
		"<file>",
		true);
static Option optLogging("logging", "l",
		"logging configuration file to be used (xml, ini, properties)",
		false,
		"<file>",
		true);
static Option optPort("port", "p",
		"server port to listen on",
		false,
		"<port>",
		true);
static Option optHelp("help", "h", "print help");

class Startup : public ServerApplication {
public:
	Startup():
		m_printHelp(false),
		m_serverPort(DEFAULT_PORT)
	{
	}

protected:
	void handleOption(const string &name, const string &value)
	{
		if (name == "services")
			m_userServices = value;
		if (name == "logging")
			m_userLogging = value;
		if (name == "help")
			m_printHelp = true;
		if (name == "port")
			m_serverPort = stoi(value);

		Application::handleOption(name, value);
	}

	void findAndLoadLogging()
	{
		File user(m_userLogging);
		File local(LOCAL_CONFIG_FILE);
		File system(SYSTEM_CONFIG_FILE);

		if (!m_userLogging.empty() && user.exists())
			loadConfiguration(user.path());
		if (local.exists())
			loadConfiguration(local.path());
		else if (system.exists())
			loadConfiguration(system.path());
	}

	void findAndLoadServices()
	{
		File user(m_userServices);
		File local(LOCAL_SERVICES_FILE);
		File system(SYSTEM_SERVICES_FILE);

		if (!m_userServices.empty() && user.exists()) {
			logger().notice(
				"loading configuration from " + user.path(),
				__FILE__, __LINE__);
			loadConfiguration(user.path());
		}
		else if (local.exists()) {
			logger().notice(
				"loading configuration from " + local.path(),
				__FILE__, __LINE__);
			loadConfiguration(local.path());
		}
		else if (system.exists()) {
			logger().notice(
				"loading configuration from " + system.path(),
				__FILE__, __LINE__);
			loadConfiguration(system.path());
		}
	}

	void initialize(Application &app)
	{
		Logger::root().setLevel(Logger::parseLevel("trace"));
		findAndLoadLogging();
		Application::initialize(app);
		findAndLoadServices();
	}

	void defineOptions(OptionSet &options)
	{
		options.addOption(optServices);
		options.addOption(optLogging);
		options.addOption(optPort);
		options.addOption(optHelp);
		Application::defineOptions(options);
	}

	int printHelp()
	{
		HelpFormatter help(options());
		help.setCommand(config().getString("application.baseName"));
		help.setUnixStyle(true);
		help.setWidth(80);
		help.setUsage("[-h] ...");
		help.format(cout);

		return EXIT_OK;
	}

	int main(const std::vector <std::string> &args)
	{
		if (m_printHelp)
			return printHelp();

		if (logger().debug())
			ManifestSingleton::reportInfo(logger());

		DependencyInjector injector(config().createView("services"));
		UIServerModule *module = injector
					.create<UIServerModule>("main");

		module->createServer(m_serverPort);
		module->server().start();

		waitForTerminationRequest();
		module->server().stop();
		return EXIT_OK;
	}

private:
	bool m_printHelp;
	unsigned int m_serverPort;
	string m_userLogging;
	string m_userServices;
};

int main(int argc, char **argv)
{
	Startup server;
	server.setUnixOptions(true);

	try {
		return server.run(argc, argv);
	} catch(Exception &e) {
		cerr << e.displayText() << endl;
	} catch(exception &e) {
		cerr << e.what() << endl;
	} catch(const char *s) {
		cerr << s << endl;
	}
}
<commit_msg>ui: fix default logging config file names<commit_after>#include <iostream>
#include <Poco/Logger.h>
#include <Poco/Message.h>
#include <Poco/File.h>
#include <Poco/Util/Option.h>
#include <Poco/Util/OptionSet.h>
#include <Poco/Util/HelpFormatter.h>
#include <Poco/Util/ServerApplication.h>

#include "server/MongooseServer.h"
#include "di/DependencyInjector.h"
#include "UIServerModule.h"

using namespace std;
using namespace Poco;
using namespace Poco::Util;
using namespace BeeeOn;

#define DEFAULT_PORT 8000

#define LOCAL_LOGGING_FILE  "logging.ini"
#define SYSTEM_LOGGING_FILE "/etc/beeeon/ui-server/logging.ini"

#define LOCAL_SERVICES_FILE  "services.xml"
#define SYSTEM_SERVICES_FILE "/etc/beeeon/ui-server/services.xml"

static Option optServices("services", "s",
		"services configuration file to be used (xml, ini, properties)",
		false,
		"<file>",
		true);
static Option optLogging("logging", "l",
		"logging configuration file to be used (xml, ini, properties)",
		false,
		"<file>",
		true);
static Option optPort("port", "p",
		"server port to listen on",
		false,
		"<port>",
		true);
static Option optHelp("help", "h", "print help");

class Startup : public ServerApplication {
public:
	Startup():
		m_printHelp(false),
		m_serverPort(DEFAULT_PORT)
	{
	}

protected:
	void handleOption(const string &name, const string &value)
	{
		if (name == "services")
			m_userServices = value;
		if (name == "logging")
			m_userLogging = value;
		if (name == "help")
			m_printHelp = true;
		if (name == "port")
			m_serverPort = stoi(value);

		Application::handleOption(name, value);
	}

	void findAndLoadLogging()
	{
		File user(m_userLogging);
		File local(LOCAL_LOGGING_FILE);
		File system(SYSTEM_LOGGING_FILE);

		if (!m_userLogging.empty() && user.exists())
			loadConfiguration(user.path());
		if (local.exists())
			loadConfiguration(local.path());
		else if (system.exists())
			loadConfiguration(system.path());
	}

	void findAndLoadServices()
	{
		File user(m_userServices);
		File local(LOCAL_SERVICES_FILE);
		File system(SYSTEM_SERVICES_FILE);

		if (!m_userServices.empty() && user.exists()) {
			logger().notice(
				"loading configuration from " + user.path(),
				__FILE__, __LINE__);
			loadConfiguration(user.path());
		}
		else if (local.exists()) {
			logger().notice(
				"loading configuration from " + local.path(),
				__FILE__, __LINE__);
			loadConfiguration(local.path());
		}
		else if (system.exists()) {
			logger().notice(
				"loading configuration from " + system.path(),
				__FILE__, __LINE__);
			loadConfiguration(system.path());
		}
	}

	void initialize(Application &app)
	{
		Logger::root().setLevel(Logger::parseLevel("trace"));
		findAndLoadLogging();
		Application::initialize(app);
		findAndLoadServices();
	}

	void defineOptions(OptionSet &options)
	{
		options.addOption(optServices);
		options.addOption(optLogging);
		options.addOption(optPort);
		options.addOption(optHelp);
		Application::defineOptions(options);
	}

	int printHelp()
	{
		HelpFormatter help(options());
		help.setCommand(config().getString("application.baseName"));
		help.setUnixStyle(true);
		help.setWidth(80);
		help.setUsage("[-h] ...");
		help.format(cout);

		return EXIT_OK;
	}

	int main(const std::vector <std::string> &args)
	{
		if (m_printHelp)
			return printHelp();

		if (logger().debug())
			ManifestSingleton::reportInfo(logger());

		DependencyInjector injector(config().createView("services"));
		UIServerModule *module = injector
					.create<UIServerModule>("main");

		module->createServer(m_serverPort);
		module->server().start();

		waitForTerminationRequest();
		module->server().stop();
		return EXIT_OK;
	}

private:
	bool m_printHelp;
	unsigned int m_serverPort;
	string m_userLogging;
	string m_userServices;
};

int main(int argc, char **argv)
{
	Startup server;
	server.setUnixOptions(true);

	try {
		return server.run(argc, argv);
	} catch(Exception &e) {
		cerr << e.displayText() << endl;
	} catch(exception &e) {
		cerr << e.what() << endl;
	} catch(const char *s) {
		cerr << s << endl;
	}
}
<|endoftext|>


<commit_before>//=====================================================================//
/*!	@file
	@brief	VS1063a を使った、オーディオプレイヤー @n
			・P73/SO01 (26) ---> VS1063:SI     (29) @n
			・P74/SI01 (25) ---> VS1063:SO     (30) @n
			・P75/SCK01(24) ---> VS1063:SCLK   (28) @n
			・P52      (35) ---> VS1063:/xCS   (23) @n
			・P53      (36) ---> VS1063:/xDCS  (13) @n
			・P54      (37) ---> VS1063:/DREQ  ( 8) @n
			・/RESET   ( 6) ---> VS1063:/xRESET( 3)
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <cstdint>
#include "common/port_utils.hpp"
#include "common/tau_io.hpp"
#include "common/fifo.hpp"
#include "common/uart_io.hpp"
#include "common/itimer.hpp"
#include "common/format.hpp"
#include "common/delay.hpp"
#include "common/csi_io.hpp"
#include "common/sdc_io.hpp"
#include "common/command.hpp"
#include "chip/VS1063.hpp"

namespace {

	// 送信、受信バッファの定義
	typedef utils::fifo<uint8_t, 32> buffer;
	// UART の定義（SAU02、SAU03）
	device::uart_io<device::SAU02, device::SAU03, buffer, buffer> uart_;

	// インターバル・タイマー
	device::itimer<uint8_t> itm_;

	// SDC CSI(SPI) の定義、CSI00 の通信では、「SAU00」を利用、０ユニット、チャネル０
	typedef device::csi_io<device::SAU00> csi0;
	csi0 csi0_;

	// FatFS インターフェースの定義
	typedef device::PORT<device::port_no::P0,  device::bitpos::B0> card_select;	///< カード選択信号
	typedef device::PORT<device::port_no::P0,  device::bitpos::B1> card_power;	///< カード電源制御
	typedef device::PORT<device::port_no::P14, device::bitpos::B6> card_detect;	///< カード検出
	utils::sdc_io<csi0, card_select, card_power, card_detect> sdc_(csi0_);

	utils::command<64> command_;

	// VS1053 SPI の定義 CSI01「SAU01」
	typedef device::csi_io<device::SAU01> csi1;
	csi1 csi1_;
	typedef device::PORT<device::port_no::P5,  device::bitpos::B2> vs1063_sel;	///< VS1063 /CS
	typedef device::PORT<device::port_no::P5,  device::bitpos::B3> vs1063_dcs;	///< VS1063 /DCS
	typedef device::PORT<device::port_no::P5,  device::bitpos::B4> vs1063_req;	///< VS1063 DREQ
	chip::VS1063<csi1, vs1063_sel, vs1063_dcs, vs1063_req> vs1063_(csi1_);
}

const void* ivec_[] __attribute__ ((section (".ivec"))) = {
	/*  0 */  nullptr,
	/*  1 */  nullptr,
	/*  2 */  nullptr,
	/*  3 */  nullptr,
	/*  4 */  nullptr,
	/*  5 */  nullptr,
	/*  6 */  nullptr,
	/*  7 */  nullptr,
	/*  8 */  nullptr,
	/*  9 */  nullptr,
	/* 10 */  nullptr,
	/* 11 */  nullptr,
	/* 12 */  nullptr,
	/* 13 */  nullptr,
	/* 14 */  nullptr,
	/* 15 */  nullptr,
	/* 16 */  reinterpret_cast<void*>(uart_.send_task),
	/* 17 */  reinterpret_cast<void*>(uart_.recv_task),
	/* 18 */  reinterpret_cast<void*>(uart_.error_task),
	/* 19 */  nullptr,
	/* 20 */  nullptr,
	/* 21 */  nullptr,
	/* 22 */  nullptr,
	/* 23 */  nullptr,
	/* 24 */  nullptr,
	/* 25 */  nullptr,
	/* 26 */  reinterpret_cast<void*>(itm_.task),
};

extern "C" {
	void sci_putch(char ch)
	{
		uart_.putch(ch);
	}

	void sci_puts(const char* str)
	{
		uart_.puts(str);
	}

	char sci_getch(void)
	{
		return uart_.getch();
	}

	uint16_t sci_length()
	{
		return uart_.recv_length();
	}

	DSTATUS disk_initialize(BYTE drv) {
		return sdc_.at_mmc().disk_initialize(drv);
	}

	DSTATUS disk_status(BYTE drv) {
		return sdc_.at_mmc().disk_status(drv);
	}

	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_read(drv, buff, sector, count);
	}

	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_write(drv, buff, sector, count);
	}

	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdc_.at_mmc().disk_ioctl(drv, ctrl, buff);
	}

	DWORD get_fattime(void) {
		return 0;
	}
};

namespace {

	void play_(const char* fname)
	{
		if(!sdc_.get_mount()) {
			utils::format("SD Card unmount.\n");
			return;
		}

		utils::format("Play: '%s'\n") % fname;

		FIL fil;
		if(sdc_.open(&fil, fname, FA_READ) != FR_OK) {
			utils::format("Can't open input file: '%s'\n") % fname;
			return;
		}

		vs1063_.play(&fil);
	}

	void play_loop_(const char*);
	void play_loop_func_(const char* name, const FILINFO* fi, bool dir)
	{
		if(dir) {
			play_loop_(name);
		} else {
			play_(name);
		}
	}

	void play_loop_(const char* root)
	{
		sdc_.dir_loop(root, play_loop_func_);
	}
}

int main(int argc, char* argv[])
{
	utils::port::pullup_all();  ///< 安全の為、全ての入力をプルアップ

	device::PM4.B3 = 0;  // output

	// インターバル・タイマー開始
	{
		uint8_t intr_level = 1;
		itm_.start(60, intr_level);
	}

	// UART 開始
	{
		uint8_t intr_level = 1;
		uart_.start(115200, intr_level);
	}

	// SD カード・サービス開始
	sdc_.initialize();

	// VS1063 初期化
	{
		vs1063_.start();
	}

	uart_.puts("Start RL78/G13 VS1053 player sample\n");

	command_.set_prompt("# ");

	uint8_t n = 0;
	FIL fil;
	char tmp[64];
	while(1) {
		itm_.sync();

		sdc_.service();

		// コマンド入力と、コマンド解析
		if(command_.service()) {
			auto cmdn = command_.get_words();
			if(cmdn >= 1) {
				if(command_.cmp_word(0, "dir")) {
					if(!sdc_.get_mount()) {
						utils::format("SD Card unmount.\n");
					} else {
						if(cmdn >= 2) {
							command_.get_word(1, sizeof(tmp), tmp);
							sdc_.dir(tmp);
						} else {
							sdc_.dir("");
						}
					}
				} else if(command_.cmp_word(0, "cd")) {  // cd [xxx]
					if(cmdn >= 2) {
						command_.get_word(1, sizeof(tmp), tmp);
						sdc_.cd(tmp);
					} else {
						sdc_.cd("/");
					}
				} else if(command_.cmp_word(0, "pwd")) { // pwd
					utils::format("%s\n") % sdc_.get_current();
				} else if(command_.cmp_word(0, "play")) {  // play [xxx]
					if(cmdn >= 2) {
						command_.get_word(1, sizeof(tmp), tmp);
						if(std::strcmp(tmp, "*") == 0) {
							play_loop_("");
						} else {
							play_(tmp);
						}
					} else {
						play_loop_("");
					}
				} else {
					utils::format("dir ---> directory file\n");
					utils::format("play file-name ---> play file\n");
					utils::format("play * ---> play file all\n");
				}
			}
		}

		++n;
		if(n >= 30) n = 0;
		device::P4.B3 = n < 10 ? false : true;
	}
}
<commit_msg>fix file open<commit_after>//=====================================================================//
/*!	@file
	@brief	VS1063a を使った、オーディオプレイヤー @n
			・P73/SO01 (26) ---> VS1063:SI     (29) @n
			・P74/SI01 (25) ---> VS1063:SO     (30) @n
			・P75/SCK01(24) ---> VS1063:SCLK   (28) @n
			・P52      (35) ---> VS1063:/xCS   (23) @n
			・P53      (36) ---> VS1063:/xDCS  (13) @n
			・P54      (37) ---> VS1063:/DREQ  ( 8) @n
			・/RESET   ( 6) ---> VS1063:/xRESET( 3)
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <cstdint>
#include "common/port_utils.hpp"
#include "common/tau_io.hpp"
#include "common/fifo.hpp"
#include "common/uart_io.hpp"
#include "common/itimer.hpp"
#include "common/format.hpp"
#include "common/delay.hpp"
#include "common/csi_io.hpp"
#include "common/sdc_io.hpp"
#include "common/command.hpp"
#include "chip/VS1063.hpp"

namespace {

	// 送信、受信バッファの定義
	typedef utils::fifo<uint8_t, 32> buffer;
	// UART の定義（SAU02、SAU03）
	device::uart_io<device::SAU02, device::SAU03, buffer, buffer> uart_;

	// インターバル・タイマー
	device::itimer<uint8_t> itm_;

	// SDC CSI(SPI) の定義、CSI00 の通信では、「SAU00」を利用、０ユニット、チャネル０
	typedef device::csi_io<device::SAU00> csi0;
	csi0 csi0_;

	// FatFS インターフェースの定義
	typedef device::PORT<device::port_no::P0,  device::bitpos::B0> card_select;	///< カード選択信号
	typedef device::PORT<device::port_no::P0,  device::bitpos::B1> card_power;	///< カード電源制御
	typedef device::PORT<device::port_no::P14, device::bitpos::B6> card_detect;	///< カード検出
	utils::sdc_io<csi0, card_select, card_power, card_detect> sdc_(csi0_);

	utils::command<64> command_;

	// VS1053 SPI の定義 CSI01「SAU01」
	typedef device::csi_io<device::SAU01> csi1;
	csi1 csi1_;
	typedef device::PORT<device::port_no::P5,  device::bitpos::B2> vs1063_sel;	///< VS1063 /CS
	typedef device::PORT<device::port_no::P5,  device::bitpos::B3> vs1063_dcs;	///< VS1063 /DCS
	typedef device::PORT<device::port_no::P5,  device::bitpos::B4> vs1063_req;	///< VS1063 DREQ
	chip::VS1063<csi1, vs1063_sel, vs1063_dcs, vs1063_req> vs1063_(csi1_);
}

const void* ivec_[] __attribute__ ((section (".ivec"))) = {
	/*  0 */  nullptr,
	/*  1 */  nullptr,
	/*  2 */  nullptr,
	/*  3 */  nullptr,
	/*  4 */  nullptr,
	/*  5 */  nullptr,
	/*  6 */  nullptr,
	/*  7 */  nullptr,
	/*  8 */  nullptr,
	/*  9 */  nullptr,
	/* 10 */  nullptr,
	/* 11 */  nullptr,
	/* 12 */  nullptr,
	/* 13 */  nullptr,
	/* 14 */  nullptr,
	/* 15 */  nullptr,
	/* 16 */  reinterpret_cast<void*>(uart_.send_task),
	/* 17 */  reinterpret_cast<void*>(uart_.recv_task),
	/* 18 */  reinterpret_cast<void*>(uart_.error_task),
	/* 19 */  nullptr,
	/* 20 */  nullptr,
	/* 21 */  nullptr,
	/* 22 */  nullptr,
	/* 23 */  nullptr,
	/* 24 */  nullptr,
	/* 25 */  nullptr,
	/* 26 */  reinterpret_cast<void*>(itm_.task),
};

extern "C" {
	void sci_putch(char ch)
	{
		uart_.putch(ch);
	}

	void sci_puts(const char* str)
	{
		uart_.puts(str);
	}

	char sci_getch(void)
	{
		return uart_.getch();
	}

	uint16_t sci_length()
	{
		return uart_.recv_length();
	}

	DSTATUS disk_initialize(BYTE drv) {
		return sdc_.at_mmc().disk_initialize(drv);
	}

	DSTATUS disk_status(BYTE drv) {
		return sdc_.at_mmc().disk_status(drv);
	}

	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_read(drv, buff, sector, count);
	}

	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_write(drv, buff, sector, count);
	}

	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdc_.at_mmc().disk_ioctl(drv, ctrl, buff);
	}

	DWORD get_fattime(void) {
		return 0;
	}
};

namespace {

	void play_(const char* fname)
	{
		if(!sdc_.get_mount()) {
			utils::format("SD Card unmount.\n");
			return;
		}

		utils::format("Play: '%s'\n") % fname;

		FIL fil;
		if(!sdc_.open(&fil, fname, FA_READ)) {
			utils::format("Can't open input file: '%s'\n") % fname;
			return;
		}

		vs1063_.play(&fil);
	}

	void play_loop_(const char*);
	void play_loop_func_(const char* name, const FILINFO* fi, bool dir)
	{
		if(dir) {
			play_loop_(name);
		} else {
			play_(name);
		}
	}

	void play_loop_(const char* root)
	{
		sdc_.dir_loop(root, play_loop_func_);
	}
}

int main(int argc, char* argv[])
{
	utils::port::pullup_all();  ///< 安全の為、全ての入力をプルアップ

	device::PM4.B3 = 0;  // output

	// インターバル・タイマー開始
	{
		uint8_t intr_level = 1;
		itm_.start(60, intr_level);
	}

	// UART 開始
	{
		uint8_t intr_level = 1;
		uart_.start(115200, intr_level);
	}

	// SD カード・サービス開始
	sdc_.initialize();

	// VS1063 初期化
	{
		vs1063_.start();
	}

	uart_.puts("Start RL78/G13 VS1053 player sample\n");

	command_.set_prompt("# ");

	uint8_t n = 0;
	FIL fil;
	char tmp[64];
	while(1) {
		itm_.sync();

		sdc_.service();

		// コマンド入力と、コマンド解析
		if(command_.service()) {
			auto cmdn = command_.get_words();
			if(cmdn >= 1) {
				if(command_.cmp_word(0, "dir")) {
					if(!sdc_.get_mount()) {
						utils::format("SD Card unmount.\n");
					} else {
						if(cmdn >= 2) {
							command_.get_word(1, sizeof(tmp), tmp);
							sdc_.dir(tmp);
						} else {
							sdc_.dir("");
						}
					}
				} else if(command_.cmp_word(0, "cd")) {  // cd [xxx]
					if(cmdn >= 2) {
						command_.get_word(1, sizeof(tmp), tmp);
						sdc_.cd(tmp);
					} else {
						sdc_.cd("/");
					}
				} else if(command_.cmp_word(0, "pwd")) { // pwd
					utils::format("%s\n") % sdc_.get_current();
				} else if(command_.cmp_word(0, "play")) {  // play [xxx]
					if(cmdn >= 2) {
						command_.get_word(1, sizeof(tmp), tmp);
						if(std::strcmp(tmp, "*") == 0) {
							play_loop_("");
						} else {
							play_(tmp);
						}
					} else {
						play_loop_("");
					}
				} else {
					utils::format("dir ---> directory file\n");
					utils::format("play file-name ---> play file\n");
					utils::format("play * ---> play file all\n");
				}
			}
		}

		++n;
		if(n >= 30) n = 0;
		device::P4.B3 = n < 10 ? false : true;
	}
}
<|endoftext|>