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

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Raw CellML view widget
//==============================================================================

#include "corecellmleditingwidget.h"
#include "filemanager.h"
#include "qscintillawidget.h"
#include "rawcellmlviewwidget.h"
#include "viewerwidget.h"
#include "xsltransformer.h"

//==============================================================================

#include "ui_rawcellmlviewwidget.h"

//==============================================================================

#include <QDesktopWidget>
#include <QLabel>
#include <QLayout>
#include <QMetaType>
#include <QSettings>
#include <QVariant>

//==============================================================================

#include "Qsci/qscilexerxml.h"

//==============================================================================

namespace OpenCOR {
namespace RawCellMLView {

//==============================================================================

RawCellmlViewWidget::RawCellmlViewWidget(QWidget *pParent) :
    ViewWidget(pParent),
    mGui(new Ui::RawCellmlViewWidget),
    mEditingWidget(0),
    mEditingWidgets(QMap<QString, CoreCellMLEditing::CoreCellmlEditingWidget *>()),
    mEditingWidgetSizes(QIntList()),
    mEditorZoomLevel(0),
    mContentMathml(QString()),
    mPresentationMathmls(QMap<QString, QString>())
{
    // Set up the GUI

    mGui->setupUi(this);

    // Create our XSL transformer and create a connection to retrieve the result
    // of its XSL transformations

    mXslTransformer = new Core::XslTransformer();

    connect(mXslTransformer, SIGNAL(done(const QString &, const QString &)),
            this, SLOT(xslTransformationDone(const QString &, const QString &)));
}

//==============================================================================

RawCellmlViewWidget::~RawCellmlViewWidget()
{
    // Delete the GUI

    delete mGui;

    // Stop our XSL transformer
    // Note: we don't need to delete it since it will be done as part of its
    //       thread being stopped...

    mXslTransformer->stop();
}

//==============================================================================

static const auto SettingsEditingWidgetSizes = QStringLiteral("EditingWidgetSizes");
static const auto SettingsEditorZoomLevel    = QStringLiteral("EditorZoomLevel");

//==============================================================================

void RawCellmlViewWidget::loadSettings(QSettings *pSettings)
{
    // Retrieve the editing widget's sizes and the editor's zoom level
    // Note #1: the viewer's default height is 19% of the desktop's height while
    //          that of the editor is as big as it can be...
    // Note #2: because the editor's default height is much bigger than that of
    //          our raw CellML view widget, the viewer's default height will
    //          effectively be less than 19% of the desktop's height, but that
    //          doesn't matter at all...

    QVariantList defaultEditingWidgetSizes = QVariantList() << 0.19*qApp->desktop()->screenGeometry().height()
                                                            << qApp->desktop()->screenGeometry().height();

    mEditingWidgetSizes = qVariantListToIntList(pSettings->value(SettingsEditingWidgetSizes, defaultEditingWidgetSizes).toList());
    mEditorZoomLevel = pSettings->value(SettingsEditorZoomLevel, 0).toInt();
}

//==============================================================================

void RawCellmlViewWidget::saveSettings(QSettings *pSettings) const
{
    // Keep track of the editing widget's sizes and the editor's zoom level

    pSettings->setValue(SettingsEditingWidgetSizes, qIntListToVariantList(mEditingWidgetSizes));
    pSettings->setValue(SettingsEditorZoomLevel, mEditorZoomLevel);
}

//==============================================================================

bool RawCellmlViewWidget::contains(const QString &pFileName) const
{
    // Return whether we know about the given file, i.e. whether we have an
    // editing widget for it

    return mEditingWidgets.value(pFileName);
}

//==============================================================================

void RawCellmlViewWidget::initialize(const QString &pFileName)
{
    // Retrieve the editing widget associated with the given file, if any

    mEditingWidget = mEditingWidgets.value(pFileName);

    if (!mEditingWidget) {
        // No editing widget exists for the given file, so create one

        QString fileContents;

        Core::readTextFromFile(pFileName, fileContents);

        mEditingWidget = new CoreCellMLEditing::CoreCellmlEditingWidget(fileContents,
                                                                        !Core::FileManager::instance()->isReadableAndWritable(pFileName),
                                                                        new QsciLexerXML(this),
                                                                        parentWidget());

        // Keep track of our editing widget's sizes when moving the splitter and
        // of changes to our editor's zoom level

        connect(mEditingWidget, SIGNAL(splitterMoved(int, int)),
                this, SLOT(splitterMoved()));

        connect(mEditingWidget->editor(), SIGNAL(SCN_ZOOM()),
                this, SLOT(editorZoomLevelChanged()));

        connect(mEditingWidget->editor(), SIGNAL(cursorPositionChanged(int, int)),
                this, SLOT(cursorPositionChanged()));

        // Keep track of our editing widget and add it to ourselves

        mEditingWidgets.insert(pFileName, mEditingWidget);

        layout()->addWidget(mEditingWidget);
    }

    // Show/hide our editing widgets and adjust our sizes

    foreach (CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget, mEditingWidgets)
        if (editingWidget == mEditingWidget) {
            // This is the editing widget we are after, so show it and update
            // its size and zoom level

            editingWidget->setSizes(mEditingWidgetSizes);
            editingWidget->editor()->zoomTo(mEditorZoomLevel);

            editingWidget->show();
        } else {
            // Not the editing widget we are after, so hide it

            editingWidget->hide();
        }

    // Set our focus proxy to our 'new' editing widget and make sure that the
    // latter immediately gets the focus
    // Note: if we were not to immediately give our 'new' editing widget the
    //       focus, then the central widget would give the focus to our 'old'
    //       editing widget (see CentralWidget::updateGui()), so...

    setFocusProxy(mEditingWidget->editor());

    mEditingWidget->editor()->setFocus();
}

//==============================================================================

void RawCellmlViewWidget::finalize(const QString &pFileName)
{
    // Remove the editing widget, should there be one for the given file

    CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget  = mEditingWidgets.value(pFileName);

    if (editingWidget) {
        // There is an editor for the given file name, so delete it and remove
        // it from our list

        delete editingWidget;

        mEditingWidgets.remove(pFileName);

        // Reset our memory of the current editor, if needed

        if (editingWidget == mEditingWidget)
            mEditingWidget = 0;
    }
}

//==============================================================================

void RawCellmlViewWidget::fileReloaded(const QString &pFileName)
{
    // The given file has been reloaded, so reload it, should it be managed

    if (contains(pFileName)) {
        finalize(pFileName);
        initialize(pFileName);
    }
}

//==============================================================================

void RawCellmlViewWidget::fileRenamed(const QString &pOldFileName,
                                      const QString &pNewFileName)
{
    // The given file has been renamed, so update our editing widgets mapping

    CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget = mEditingWidgets.value(pOldFileName);

    if (editingWidget) {
        mEditingWidgets.insert(pNewFileName, editingWidget);
        mEditingWidgets.remove(pOldFileName);
    }
}

//==============================================================================

QScintillaSupport::QScintillaWidget * RawCellmlViewWidget::editor(const QString &pFileName) const
{
    // Return the requested editor

    CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget = mEditingWidgets.value(pFileName);

    return editingWidget?editingWidget->editor():0;
}

//==============================================================================

QList<QWidget *> RawCellmlViewWidget::statusBarWidgets() const
{
    // Return our status bar widgets

    if (mEditingWidget)
        return QList<QWidget *>() << mEditingWidget->editor()->cursorPositionWidget()
                                  << mEditingWidget->editor()->editingModeWidget();
    else
        return QList<QWidget *>();
}

//==============================================================================

QString RawCellmlViewWidget::cleanUpMathml(const QString &pMathml) const
{
    // Clean up the given XML string by retrieving its DOM representation and
    // converting it back to a string with no whitespaces

    QDomDocument domDocument;

    if (!domDocument.setContent(pMathml))
        return QString();

    QDomNode domNode = domDocument.documentElement();

    cleanUpMathml(domNode);

    return domDocument.toString(-1);
}

//==============================================================================

void RawCellmlViewWidget::splitterMoved()
{
    // The splitter has moved, so keep track of the editing widget's sizes

    mEditingWidgetSizes = qobject_cast<CoreCellMLEditing::CoreCellmlEditingWidget *>(sender())->sizes();
}

//==============================================================================

void RawCellmlViewWidget::editorZoomLevelChanged()
{
    // One of our editors had its zoom level changed, so keep track of the new
    // zoom level

    mEditorZoomLevel = qobject_cast<QScintillaSupport::QScintillaWidget *>(sender())->SendScintilla(QsciScintillaBase::SCI_GETZOOM);
}

//==============================================================================

void RawCellmlViewWidget::cursorPositionChanged()
{
    // Make sure that we still have an editing widget (i.e. it hasn't been
    // closed since the signal was emitted)

    if (!mEditingWidget)
        return;

    // Retrieve the new mathematical equation, if any, around our current
    // position

    static const QString StartMathTag = "<math ";
    static const QByteArray EndMathTag = "</math>";

    QScintillaSupport::QScintillaWidget *editor = mEditingWidget->editor();
    int crtPos = editor->currentPosition();

    int crtStartMathTagPos = editor->findTextInRange(crtPos+StartMathTag.length(), 0, StartMathTag);
    int prevEndMathTagPos = editor->findTextInRange(crtPos, 0, EndMathTag);
    int crtEndMathTagPos = editor->findTextInRange(crtPos-EndMathTag.length()+1, editor->contentsSize(), EndMathTag);

    bool foundMathmlBlock = true;

    if (   (crtStartMathTagPos >= 0) && (crtEndMathTagPos >= 0)
        && (crtStartMathTagPos <= crtPos)
        && (crtPos <= crtEndMathTagPos+EndMathTag.length()-1)) {
        if (   (prevEndMathTagPos >= 0)
            && (prevEndMathTagPos > crtStartMathTagPos)
            && (prevEndMathTagPos < crtPos))
            foundMathmlBlock = false;
    } else {
        foundMathmlBlock = false;
    }

    if (foundMathmlBlock) {
        // Retrieve and clean up the content MathML

        QString contentMathml = editor->textInRange(crtStartMathTagPos, crtEndMathTagPos+EndMathTag.length());

        qDebug("---GRY---\nContent MathML found:\n%s", qPrintable(contentMathml));

        contentMathml = cleanUpMathml(contentMathml);

        qDebug("---GRY---\nCleaned up Content MathML:\n%s", qPrintable(contentMathml));

        // Check whether the current content MathML is the same as the previous
        // one

        if (!contentMathml.compare(mContentMathml)) {
            // It's the same, so leave

            return;
        } else {
            // It's a different one, so keep track of it and check whether we
            // have already retrieved its presentation MathML counterpart

            mContentMathml = contentMathml;

            QString presentationMathml = mPresentationMathmls.value(mContentMathml);

            if (!presentationMathml.isEmpty()) {
                mEditingWidget->viewer()->setContents(presentationMathml);
            } else {
                // We haven't already retrieved its presentation MathML
                // counterpart, so do it now

                static const QString CtopXsl = Core::resourceAsByteArray(":/ctop.xsl");

                mXslTransformer->transform(contentMathml, CtopXsl);
            }
        }
    } else {
        mContentMathml = QString();

        mEditingWidget->viewer()->setContents(QString());
    }
}

//==============================================================================

void RawCellmlViewWidget::xslTransformationDone(const QString &pInput,
                                                const QString &pOutput)
{
    // Make sure that we still have an editing widget (i.e. it hasn't been
    // closed since the signal was emitted)

    if (!mEditingWidget)
        return;

    // The XSL transformation is done, so update our viewer and keep track of
    // the presentation MathML

    mEditingWidget->viewer()->setContents(pOutput);

    mPresentationMathmls.insert(pInput, pOutput);

    if (pOutput.length())
        qDebug("---GRY---\nCorresponding presentation MathML:\n%s", qPrintable(pOutput));
}

//==============================================================================

}   // namespace RawCellMLView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up.<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Raw CellML view widget
//==============================================================================

#include "corecellmleditingwidget.h"
#include "filemanager.h"
#include "qscintillawidget.h"
#include "rawcellmlviewwidget.h"
#include "viewerwidget.h"
#include "xsltransformer.h"

//==============================================================================

#include "ui_rawcellmlviewwidget.h"

//==============================================================================

#include <QDesktopWidget>
#include <QLabel>
#include <QLayout>
#include <QMetaType>
#include <QSettings>
#include <QVariant>

//==============================================================================

#include "Qsci/qscilexerxml.h"

//==============================================================================

namespace OpenCOR {
namespace RawCellMLView {

//==============================================================================

RawCellmlViewWidget::RawCellmlViewWidget(QWidget *pParent) :
    ViewWidget(pParent),
    mGui(new Ui::RawCellmlViewWidget),
    mEditingWidget(0),
    mEditingWidgets(QMap<QString, CoreCellMLEditing::CoreCellmlEditingWidget *>()),
    mEditingWidgetSizes(QIntList()),
    mEditorZoomLevel(0),
    mContentMathml(QString()),
    mPresentationMathmls(QMap<QString, QString>())
{
    // Set up the GUI

    mGui->setupUi(this);

    // Create our XSL transformer and create a connection to retrieve the result
    // of its XSL transformations

    mXslTransformer = new Core::XslTransformer();

    connect(mXslTransformer, SIGNAL(done(const QString &, const QString &)),
            this, SLOT(xslTransformationDone(const QString &, const QString &)));
}

//==============================================================================

RawCellmlViewWidget::~RawCellmlViewWidget()
{
    // Delete the GUI

    delete mGui;

    // Stop our XSL transformer
    // Note: we don't need to delete it since it will be done as part of its
    //       thread being stopped...

    mXslTransformer->stop();
}

//==============================================================================

static const auto SettingsEditingWidgetSizes = QStringLiteral("EditingWidgetSizes");
static const auto SettingsEditorZoomLevel    = QStringLiteral("EditorZoomLevel");

//==============================================================================

void RawCellmlViewWidget::loadSettings(QSettings *pSettings)
{
    // Retrieve the editing widget's sizes and the editor's zoom level
    // Note #1: the viewer's default height is 19% of the desktop's height while
    //          that of the editor is as big as it can be...
    // Note #2: because the editor's default height is much bigger than that of
    //          our raw CellML view widget, the viewer's default height will
    //          effectively be less than 19% of the desktop's height, but that
    //          doesn't matter at all...

    QVariantList defaultEditingWidgetSizes = QVariantList() << 0.19*qApp->desktop()->screenGeometry().height()
                                                            << qApp->desktop()->screenGeometry().height();

    mEditingWidgetSizes = qVariantListToIntList(pSettings->value(SettingsEditingWidgetSizes, defaultEditingWidgetSizes).toList());
    mEditorZoomLevel = pSettings->value(SettingsEditorZoomLevel, 0).toInt();
}

//==============================================================================

void RawCellmlViewWidget::saveSettings(QSettings *pSettings) const
{
    // Keep track of the editing widget's sizes and the editor's zoom level

    pSettings->setValue(SettingsEditingWidgetSizes, qIntListToVariantList(mEditingWidgetSizes));
    pSettings->setValue(SettingsEditorZoomLevel, mEditorZoomLevel);
}

//==============================================================================

bool RawCellmlViewWidget::contains(const QString &pFileName) const
{
    // Return whether we know about the given file, i.e. whether we have an
    // editing widget for it

    return mEditingWidgets.value(pFileName);
}

//==============================================================================

void RawCellmlViewWidget::initialize(const QString &pFileName)
{
    // Retrieve the editing widget associated with the given file, if any

    mEditingWidget = mEditingWidgets.value(pFileName);

    if (!mEditingWidget) {
        // No editing widget exists for the given file, so create one

        QString fileContents;

        Core::readTextFromFile(pFileName, fileContents);

        mEditingWidget = new CoreCellMLEditing::CoreCellmlEditingWidget(fileContents,
                                                                        !Core::FileManager::instance()->isReadableAndWritable(pFileName),
                                                                        new QsciLexerXML(this),
                                                                        parentWidget());

        // Keep track of our editing widget's sizes when moving the splitter and
        // of changes to our editor's zoom level

        connect(mEditingWidget, SIGNAL(splitterMoved(int, int)),
                this, SLOT(splitterMoved()));

        connect(mEditingWidget->editor(), SIGNAL(SCN_ZOOM()),
                this, SLOT(editorZoomLevelChanged()));

        connect(mEditingWidget->editor(), SIGNAL(cursorPositionChanged(int, int)),
                this, SLOT(cursorPositionChanged()));

        // Keep track of our editing widget and add it to ourselves

        mEditingWidgets.insert(pFileName, mEditingWidget);

        layout()->addWidget(mEditingWidget);
    }

    // Show/hide our editing widgets and adjust our sizes

    foreach (CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget, mEditingWidgets)
        if (editingWidget == mEditingWidget) {
            // This is the editing widget we are after, so show it and update
            // its size and zoom level

            editingWidget->setSizes(mEditingWidgetSizes);
            editingWidget->editor()->zoomTo(mEditorZoomLevel);

            editingWidget->show();
        } else {
            // Not the editing widget we are after, so hide it

            editingWidget->hide();
        }

    // Set our focus proxy to our 'new' editing widget and make sure that the
    // latter immediately gets the focus
    // Note: if we were not to immediately give our 'new' editing widget the
    //       focus, then the central widget would give the focus to our 'old'
    //       editing widget (see CentralWidget::updateGui()), so...

    setFocusProxy(mEditingWidget->editor());

    mEditingWidget->editor()->setFocus();
}

//==============================================================================

void RawCellmlViewWidget::finalize(const QString &pFileName)
{
    // Remove the editing widget, should there be one for the given file

    CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget  = mEditingWidgets.value(pFileName);

    if (editingWidget) {
        // There is an editor for the given file name, so delete it and remove
        // it from our list

        delete editingWidget;

        mEditingWidgets.remove(pFileName);

        // Reset our memory of the current editor, if needed

        if (editingWidget == mEditingWidget)
            mEditingWidget = 0;
    }
}

//==============================================================================

void RawCellmlViewWidget::fileReloaded(const QString &pFileName)
{
    // The given file has been reloaded, so reload it, should it be managed

    if (contains(pFileName)) {
        finalize(pFileName);
        initialize(pFileName);
    }
}

//==============================================================================

void RawCellmlViewWidget::fileRenamed(const QString &pOldFileName,
                                      const QString &pNewFileName)
{
    // The given file has been renamed, so update our editing widgets mapping

    CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget = mEditingWidgets.value(pOldFileName);

    if (editingWidget) {
        mEditingWidgets.insert(pNewFileName, editingWidget);
        mEditingWidgets.remove(pOldFileName);
    }
}

//==============================================================================

QScintillaSupport::QScintillaWidget * RawCellmlViewWidget::editor(const QString &pFileName) const
{
    // Return the requested editor

    CoreCellMLEditing::CoreCellmlEditingWidget *editingWidget = mEditingWidgets.value(pFileName);

    return editingWidget?editingWidget->editor():0;
}

//==============================================================================

QList<QWidget *> RawCellmlViewWidget::statusBarWidgets() const
{
    // Return our status bar widgets

    if (mEditingWidget)
        return QList<QWidget *>() << mEditingWidget->editor()->cursorPositionWidget()
                                  << mEditingWidget->editor()->editingModeWidget();
    else
        return QList<QWidget *>();
}

//==============================================================================

QString RawCellmlViewWidget::cleanUpMathml(const QString &pMathml) const
{
    // Clean up the given XML string by retrieving its DOM representation and
    // converting it back to a string with no whitespaces

    QDomDocument domDocument;

    if (domDocument.setContent(pMathml))
        return domDocument.toString(-1);
    else
        return QString();
}

//==============================================================================

void RawCellmlViewWidget::splitterMoved()
{
    // The splitter has moved, so keep track of the editing widget's sizes

    mEditingWidgetSizes = qobject_cast<CoreCellMLEditing::CoreCellmlEditingWidget *>(sender())->sizes();
}

//==============================================================================

void RawCellmlViewWidget::editorZoomLevelChanged()
{
    // One of our editors had its zoom level changed, so keep track of the new
    // zoom level

    mEditorZoomLevel = qobject_cast<QScintillaSupport::QScintillaWidget *>(sender())->SendScintilla(QsciScintillaBase::SCI_GETZOOM);
}

//==============================================================================

void RawCellmlViewWidget::cursorPositionChanged()
{
    // Make sure that we still have an editing widget (i.e. it hasn't been
    // closed since the signal was emitted)

    if (!mEditingWidget)
        return;

    // Retrieve the new mathematical equation, if any, around our current
    // position

    static const QString StartMathTag = "<math ";
    static const QByteArray EndMathTag = "</math>";

    QScintillaSupport::QScintillaWidget *editor = mEditingWidget->editor();
    int crtPos = editor->currentPosition();

    int crtStartMathTagPos = editor->findTextInRange(crtPos+StartMathTag.length(), 0, StartMathTag);
    int prevEndMathTagPos = editor->findTextInRange(crtPos, 0, EndMathTag);
    int crtEndMathTagPos = editor->findTextInRange(crtPos-EndMathTag.length()+1, editor->contentsSize(), EndMathTag);

    bool foundMathmlBlock = true;

    if (   (crtStartMathTagPos >= 0) && (crtEndMathTagPos >= 0)
        && (crtStartMathTagPos <= crtPos)
        && (crtPos <= crtEndMathTagPos+EndMathTag.length()-1)) {
        if (   (prevEndMathTagPos >= 0)
            && (prevEndMathTagPos > crtStartMathTagPos)
            && (prevEndMathTagPos < crtPos))
            foundMathmlBlock = false;
    } else {
        foundMathmlBlock = false;
    }

    if (foundMathmlBlock) {
        // Retrieve and clean up the content MathML

        QString contentMathml = editor->textInRange(crtStartMathTagPos, crtEndMathTagPos+EndMathTag.length());

        qDebug("---GRY---\nContent MathML found:\n%s", qPrintable(contentMathml));

        contentMathml = cleanUpMathml(contentMathml);

        qDebug("---GRY---\nCleaned up Content MathML:\n%s", qPrintable(contentMathml));

        // Check whether the current content MathML is the same as the previous
        // one

        if (!contentMathml.compare(mContentMathml)) {
            // It's the same, so leave

            return;
        } else {
            // It's a different one, so keep track of it and check whether we
            // have already retrieved its presentation MathML counterpart

            mContentMathml = contentMathml;

            QString presentationMathml = mPresentationMathmls.value(mContentMathml);

            if (!presentationMathml.isEmpty()) {
                mEditingWidget->viewer()->setContents(presentationMathml);
            } else {
                // We haven't already retrieved its presentation MathML
                // counterpart, so do it now

                static const QString CtopXsl = Core::resourceAsByteArray(":/ctop.xsl");

                mXslTransformer->transform(contentMathml, CtopXsl);
            }
        }
    } else {
        mContentMathml = QString();

        mEditingWidget->viewer()->setContents(QString());
    }
}

//==============================================================================

void RawCellmlViewWidget::xslTransformationDone(const QString &pInput,
                                                const QString &pOutput)
{
    // Make sure that we still have an editing widget (i.e. it hasn't been
    // closed since the signal was emitted)

    if (!mEditingWidget)
        return;

    // The XSL transformation is done, so update our viewer and keep track of
    // the presentation MathML

    mEditingWidget->viewer()->setContents(pOutput);

    mPresentationMathmls.insert(pInput, pOutput);

    if (pOutput.length())
        qDebug("---GRY---\nCorresponding presentation MathML:\n%s", qPrintable(pOutput));
}

//==============================================================================

}   // namespace RawCellMLView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>/*
 * This file is part of the UnTech Editor Suite.
 * Copyright (c) 2016 - 2018, Marcus Rowe <undisbeliever@gmail.com>.
 * Distributed under The MIT License: https://opensource.org/licenses/MIT
 */

#include "combinesmalltiles.h"
#include <algorithm>
#include <climits>
#include <functional>
#include <list>

namespace UnTech {
namespace MetaSprite {
namespace Compiler {
namespace CombineSmallTiles {

// This is a simple packing algorithm
// It just pairs off the most common small tiles,
// and then pairs them again.

namespace MS = UnTech::MetaSprite::MetaSprite;

struct TileGraphItem {
    // Graph of tileId => frames that use that tile.
    // frames may be repeated if the same tile is used multiple times in the same frame (including flipped frames).

    const unsigned tileId;
    const std::vector<const MetaSprite::Frame*> frames;

    TileGraphItem()
        : tileId(INVALID_SMALL_TILE)
        , frames()
    {
    }
    TileGraphItem(unsigned t, std::vector<const MetaSprite::Frame*>&& f)
        : tileId(t)
        , frames(std::move(f))
    {
    }
};

struct FirstPassOutput {
    unsigned firstTile = UINT_MAX;
    unsigned secondTile = UINT_MAX;
    std::vector<const MS::Frame*> frames;
};

static int scoreTiles(const std::vector<const MS::Frame*>& a, const std::vector<const MS::Frame*>& b)
{
    int score = 0;
    for (const auto& f : a) {
        int count = std::count(b.begin(), b.end(), f);
        if (count > 0) {
            score += 3 * count;
        }
        else {
            score -= 1;
        }
    }
    return score;
}

// To improve packing the size of the output is always a multiple of four (4).
static std::vector<TileGraphItem> buildSmallTileGraph(const MetaSprite::FrameSet& frameSet,
                                                      const std::vector<FrameListEntry>& frameEntries)
{
    std::vector<std::vector<const MetaSprite::Frame*>> tileFrames(frameSet.smallTileset.size());
    for (const auto& fle : frameEntries) {
        if (fle.frame != nullptr) {
            for (const auto& obj : fle.frame->objects) {
                if (obj.size == ObjectSize::SMALL) {
                    tileFrames.at(obj.tileId).emplace_back(fle.frame);
                }
            }
        }
    }

    std::vector<TileGraphItem> smallTileGraph;
    smallTileGraph.reserve(int((frameSet.smallTileset.size() + 3) / 4) * 4);

    for (unsigned i = 0; i < tileFrames.size(); i++) {
        if (not tileFrames.at(i).empty()) {
            smallTileGraph.emplace_back(i, std::move(tileFrames.at(i)));
        }
    }

    while (smallTileGraph.size() % 4 != 0) {
        smallTileGraph.emplace_back();
    }

    return smallTileGraph;
}

static std::list<FirstPassOutput> firstPass(const std::vector<TileGraphItem>& smallTileGraph)
{
    assert(!smallTileGraph.empty() && smallTileGraph.size() % 4 == 0);

    std::list<FirstPassOutput> output;

    std::vector<std::reference_wrapper<const TileGraphItem>> toProcess;
    toProcess.reserve(smallTileGraph.size());
    for (const TileGraphItem& it : smallTileGraph) {
        toProcess.emplace_back(it);
    }

    // sort toProcess by popularity
    std::stable_sort(toProcess.begin(), toProcess.end(),
                     [](const TileGraphItem& a, const TileGraphItem& b) {
                         return a.frames.size() > b.frames.size();
                     });

    while (!toProcess.empty()) {
        assert(toProcess.size() % 2 == 0);

        auto mostPopularIt = toProcess.begin();
        const TileGraphItem& mostPopular = *mostPopularIt;

        auto bestMatchIt = toProcess.end();
        {
            int bestScore = INT_MIN;
            for (auto it = mostPopularIt + 1; it != toProcess.end(); ++it) {
                const TileGraphItem& tgi = *it;
                int score = scoreTiles(tgi.frames, mostPopular.frames);

                if (score > bestScore) {
                    bestMatchIt = it;
                    bestScore = score;
                }
            }
        }

        assert(bestMatchIt != toProcess.end());
        const TileGraphItem& bestMatch = *bestMatchIt;

        output.emplace_back();
        auto& o = output.back();

        o.firstTile = mostPopular.tileId;
        o.secondTile = bestMatch.tileId;

        o.frames = mostPopular.frames;
        o.frames.insert(o.frames.end(), bestMatch.frames.begin(), bestMatch.frames.end());

        toProcess.erase(bestMatchIt);
        toProcess.erase(mostPopularIt);
    }

    return output;
}

static SmallTileMap_t secondPass(std::list<FirstPassOutput>& input,
                                 const size_t nSmallTiles)
{
    assert(!input.empty() && input.size() % 2 == 0);

    SmallTileMap_t output(nSmallTiles, INVALID_SMALL_TILES_ARRAY);

    // sort the input by popularity
    // should be mostly sorted anyways
    input.sort([](const FirstPassOutput& a, const FirstPassOutput& b) {
        return a.frames.size() > b.frames.size();
    });

    while (!input.empty()) {
        auto mostPopular = input.begin();

        auto bestMatch = input.end();
        {
            int bestScore = INT_MIN;
            auto it = mostPopular;
            for (++it; it != input.end(); ++it) {
                int score = scoreTiles(mostPopular->frames, it->frames);

                if (score > bestScore) {
                    bestMatch = it;
                    bestScore = score;
                }
            }
        }

        assert(bestMatch != input.end());

        std::array<uint16_t, 4> combined;

        combined[0] = mostPopular->firstTile;
        combined[1] = mostPopular->secondTile;
        combined[2] = bestMatch->firstTile;
        combined[3] = bestMatch->secondTile;

        input.erase(bestMatch);
        input.erase(mostPopular);

        for (auto tId : combined) {
            if (tId != INVALID_SMALL_TILE) {
                output.at(tId) = combined;
            }
        }
    }

    return output;
}
}

SmallTileMap_t buildSmallTileMap(const MetaSprite::FrameSet& frameSet,
                                 const std::vector<FrameListEntry>& frameEntries)
{
    if (frameSet.smallTileset.empty()) {
        return SmallTileMap_t();
    }

    auto smallTileGraph = CombineSmallTiles::buildSmallTileGraph(frameSet, frameEntries);
    auto fp = CombineSmallTiles::firstPass(smallTileGraph);
    return CombineSmallTiles::secondPass(fp, frameSet.smallTileset.size());
}
}
}
}
<commit_msg>Improve scoring used by CombineSmallTiles<commit_after>/*
 * This file is part of the UnTech Editor Suite.
 * Copyright (c) 2016 - 2018, Marcus Rowe <undisbeliever@gmail.com>.
 * Distributed under The MIT License: https://opensource.org/licenses/MIT
 */

#include "combinesmalltiles.h"
#include <algorithm>
#include <climits>
#include <functional>
#include <list>

namespace UnTech {
namespace MetaSprite {
namespace Compiler {
namespace CombineSmallTiles {

// This is a simple packing algorithm
// It just pairs off the most common small tiles,
// and then pairs them again.

namespace MS = UnTech::MetaSprite::MetaSprite;

struct TileGraphItem {
    // Graph of tileId => frames that use that tile.
    // frames may be repeated if the same tile is used multiple times in the same frame (including flipped frames).

    const unsigned tileId;
    const std::vector<const MetaSprite::Frame*> frames;

    TileGraphItem()
        : tileId(INVALID_SMALL_TILE)
        , frames()
    {
    }
    TileGraphItem(unsigned t, std::vector<const MetaSprite::Frame*>&& f)
        : tileId(t)
        , frames(std::move(f))
    {
    }
};

struct FirstPassOutput {
    const TileGraphItem& firstTile;
    const TileGraphItem& secondTile;

    FirstPassOutput(const TileGraphItem& first, const TileGraphItem& second)
        : firstTile(first)
        , secondTile(second)
    {
    }

    size_t nFrames() const { return firstTile.frames.size() + secondTile.frames.size(); }
};

static int scoreTilesVec(const std::vector<const MetaSprite::Frame*>& a, const std::vector<const MetaSprite::Frame*>& b)
{
    int score = 0;
    for (const auto& f : a) {
        int count = std::count(b.begin(), b.end(), f);
        if (count > 0) {
            score += count;
        }
        else {
            score -= 3;
        }
    }
    return score;
}

static int scoreTiles(const TileGraphItem& a, const TileGraphItem& b)
{
    return scoreTilesVec(a.frames, b.frames)
           + scoreTilesVec(b.frames, a.frames);
}

static int scoreTiles(const FirstPassOutput& a, const FirstPassOutput& b)
{
    return scoreTiles(a.firstTile, b.firstTile)
           + scoreTiles(a.secondTile, b.firstTile)
           + scoreTiles(b.firstTile, a.secondTile)
           + scoreTiles(a.secondTile, b.secondTile);
}

// To improve packing the size of the output is always a multiple of four (4).
static std::vector<TileGraphItem> buildSmallTileGraph(const MetaSprite::FrameSet& frameSet,
                                                      const std::vector<FrameListEntry>& frameEntries)
{
    std::vector<std::vector<const MetaSprite::Frame*>> tileFrames(frameSet.smallTileset.size());
    for (const auto& fle : frameEntries) {
        if (fle.frame != nullptr) {
            for (const auto& obj : fle.frame->objects) {
                if (obj.size == ObjectSize::SMALL) {
                    tileFrames.at(obj.tileId).emplace_back(fle.frame);
                }
            }
        }
    }

    std::vector<TileGraphItem> smallTileGraph;
    smallTileGraph.reserve(int((frameSet.smallTileset.size() + 3) / 4) * 4);

    for (unsigned i = 0; i < tileFrames.size(); i++) {
        if (not tileFrames.at(i).empty()) {
            smallTileGraph.emplace_back(i, std::move(tileFrames.at(i)));
        }
    }

    while (smallTileGraph.size() % 4 != 0) {
        smallTileGraph.emplace_back();
    }

    return smallTileGraph;
}

static std::list<FirstPassOutput> firstPass(const std::vector<TileGraphItem>& smallTileGraph)
{
    assert(!smallTileGraph.empty() && smallTileGraph.size() % 4 == 0);

    std::list<FirstPassOutput> output;

    std::vector<std::reference_wrapper<const TileGraphItem>> toProcess;
    toProcess.reserve(smallTileGraph.size());
    for (const TileGraphItem& it : smallTileGraph) {
        toProcess.emplace_back(it);
    }

    // sort toProcess by popularity
    std::stable_sort(toProcess.begin(), toProcess.end(),
                     [](const TileGraphItem& a, const TileGraphItem& b) {
                         return a.frames.size() > b.frames.size();
                     });

    while (!toProcess.empty()) {
        assert(toProcess.size() % 2 == 0);

        auto mostPopularIt = toProcess.begin();

        auto bestMatchIt = toProcess.end();
        {
            int bestScore = INT_MIN;
            for (auto it = mostPopularIt + 1; it != toProcess.end(); ++it) {
                int score = scoreTiles(*it, *mostPopularIt);

                if (score > bestScore) {
                    bestMatchIt = it;
                    bestScore = score;
                }
            }
        }
        assert(bestMatchIt != toProcess.end());

        output.emplace_back(*mostPopularIt, *bestMatchIt);

        toProcess.erase(bestMatchIt);
        toProcess.erase(mostPopularIt);
    }

    return output;
}

static SmallTileMap_t secondPass(std::list<FirstPassOutput>& input,
                                 const size_t nSmallTiles)
{
    assert(!input.empty() && input.size() % 2 == 0);

    SmallTileMap_t output(nSmallTiles, INVALID_SMALL_TILES_ARRAY);

    // sort the input by popularity
    // should be mostly sorted anyways
    input.sort([](const FirstPassOutput& a, const FirstPassOutput& b) {
        return a.nFrames() > b.nFrames();
    });

    while (!input.empty()) {
        auto mostPopular = input.begin();

        auto bestMatch = input.end();
        {
            int bestScore = INT_MIN;
            auto it = mostPopular;
            for (++it; it != input.end(); ++it) {
                int score = scoreTiles(*mostPopular, *it);

                if (score > bestScore) {
                    bestMatch = it;
                    bestScore = score;
                }
            }
        }

        assert(bestMatch != input.end());

        std::array<uint16_t, 4> combined;

        combined[0] = mostPopular->firstTile.tileId;
        combined[1] = mostPopular->secondTile.tileId;
        combined[2] = bestMatch->firstTile.tileId;
        combined[3] = bestMatch->secondTile.tileId;

        input.erase(bestMatch);
        input.erase(mostPopular);

        for (auto tId : combined) {
            if (tId != INVALID_SMALL_TILE) {
                output.at(tId) = combined;
            }
        }
    }

    return output;
}
}

SmallTileMap_t buildSmallTileMap(const MetaSprite::FrameSet& frameSet,
                                 const std::vector<FrameListEntry>& frameEntries)
{
    if (frameSet.smallTileset.empty()) {
        return SmallTileMap_t();
    }

    auto smallTileGraph = CombineSmallTiles::buildSmallTileGraph(frameSet, frameEntries);
    auto fp = CombineSmallTiles::firstPass(smallTileGraph);
    return CombineSmallTiles::secondPass(fp, frameSet.smallTileset.size());
}
}
}
}
<|endoftext|>


<commit_before>#include "../../render_objects/renderables.hpp"
#include "fmt/format.h"
#include "vulkan_render_engine.hpp"

namespace nova::renderer {
    void vulkan_render_engine::create_builtin_uniform_buffers() {
        // TODO: move this to the settings
        const uint32_t static_object_estimate = 5000;
        VkBufferCreateInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
        info.size = static_object_estimate * sizeof(glm::mat4);
        info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
        info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

        const uint32_t alignment = static_cast<uint32_t>(gpu.props.limits.minUniformBufferOffsetAlignment);

        static_model_matrix_buffer = std::make_unique<auto_buffer>("NovaStaticModelUBO", vma_allocator, info, alignment, false);
    }

    result<renderable_id_t> vulkan_render_engine::add_renderable(const static_mesh_renderer_data& data) {
        return [&]() -> result<std::vector<const material_pass*>> {
            std::vector<const material_pass*> passes;

            for(const auto& [pipeline_name, materials] : material_passes_by_pipeline) {
                static_cast<void>(pipeline_name);

                for(const material_pass& pass : materials) {
                    if(pass.material_name == data.material_name) {
                        passes.push_back(&pass);
                    }
                }
            }

            if(passes.empty()) {
                return result<std::vector<const material_pass*>>(
                    nova_error(fmt::format(fmt("Could not find material {:s}"), data.material_name)));
            }
            else {
                return result<std::vector<const material_pass*>>(std::move(passes));
            }
        }()
        .map([&](const std::vector<const material_pass*>& passes) {
            static_cast<void>(passes);
            auto mesh_result =
                [&]() {
                    if(meshes.find(data.mesh) == meshes.end()) {
                        return result<const vk_mesh*>(
                            nova_error(fmt::format(fmt("Could not find mesh with id {:d}"), data.mesh)));
                    }

                    return result<const vk_mesh*>(&meshes.at(data.mesh));
                }()
                .map([](const vk_mesh* mesh) {
                    static_cast<void>(mesh);

                    // vk_static_mesh_renderable renderable = {};
                    // renderable.draw_cmd = &mesh->draw_cmd;

                    return static_cast<renderable_id_t>(3);
                });

            return static_cast<renderable_id_t>(3);
        });
    }

    void vulkan_render_engine::set_renderable_visibility(renderable_id_t id, bool is_visible) {
        static_cast<void>(id);
        static_cast<void>(is_visible);
    }

    void vulkan_render_engine::delete_renderable(renderable_id_t id) { static_cast<void>(id); }
} // namespace nova::renderer<commit_msg>Formatting fixes bug oh god this formatting is gross<commit_after>#include "../../render_objects/renderables.hpp"
#include "fmt/format.h"
#include "vulkan_render_engine.hpp"

namespace nova::renderer {
    void vulkan_render_engine::create_builtin_uniform_buffers() {
        // TODO: move this to the settings
        const uint32_t static_object_estimate = 5000;
        VkBufferCreateInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
        info.size = static_object_estimate * sizeof(glm::mat4);
        info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
        info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

        const uint32_t alignment = static_cast<uint32_t>(gpu.props.limits.minUniformBufferOffsetAlignment);

        static_model_matrix_buffer = std::make_unique<auto_buffer>("NovaStaticModelUBO", vma_allocator, info, alignment, false);
    }

    result<renderable_id_t> vulkan_render_engine::add_renderable(const static_mesh_renderer_data& data) {
        return [&]() -> result<std::vector<const material_pass*>> {
            std::vector<const material_pass*> passes;

            for(const auto& [pipeline_name, materials] : material_passes_by_pipeline) {
                static_cast<void>(pipeline_name);

                for(const material_pass& pass : materials) {
                    if(pass.material_name == data.material_name) {
                        passes.push_back(&pass);
                    }
                }
            }

            if(passes.empty()) {
                return result<std::vector<const material_pass*>>(
                    nova_error(fmt::format(fmt("Could not find material {:s}"), data.material_name)));
            }
            else {
                return result<std::vector<const material_pass*>>(std::move(passes));
            }
        }()
                .map([&](const std::vector<const material_pass*>& passes) {
                    static_cast<void>(passes);
                    auto mesh_result =
                        [&]() {
                            if(meshes.find(data.mesh) == meshes.end()) {
                                return result<const vk_mesh*>(
                                    nova_error(fmt::format(fmt("Could not find mesh with id {:d}"), data.mesh)));
                            }

                            return result<const vk_mesh*>(&meshes.at(data.mesh));
                        }()
                            .map([](const vk_mesh* mesh) {
                                static_cast<void>(mesh);

                                // vk_static_mesh_renderable renderable = {};
                                // renderable.draw_cmd = &mesh->draw_cmd;

                                return static_cast<renderable_id_t>(3);
                            });

                    return static_cast<renderable_id_t>(3);
                });
    }

    void vulkan_render_engine::set_renderable_visibility(renderable_id_t id, bool is_visible) {
        static_cast<void>(id);
        static_cast<void>(is_visible);
    }

    void vulkan_render_engine::delete_renderable(renderable_id_t id) { static_cast<void>(id); }
} // namespace nova::renderer<|endoftext|>


<commit_before>/*
* Copyright 2015 BrewPi/Elco Jacobs.
*
* This file is part of BrewPi.
*
* BrewPi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* BrewPi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <boost/test/unit_test.hpp>

#include "runner.h"

#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time, to seed rand */
#include <cstring>

#include "ActuatorInterfaces.h"
#include "ActuatorTimeLimited.h"
#include "ActuatorMocks.h"
#include "Ticks.h"
#include "ActuatorMutexGroup.h"
#include "ActuatorMutexDriver.h"

BOOST_AUTO_TEST_SUITE(ActuatorMutexTest)

BOOST_AUTO_TEST_CASE(two_actuators_belonging_to_the_same_group_cannot_be_active_at_once) {
    ActuatorDigital * act1 = new ActuatorBool();
    ActuatorDigital * act2 = new ActuatorBool();
    ActuatorMutexGroup * mutex = new ActuatorMutexGroup();

    ActuatorMutexDriver * actm1 = new ActuatorMutexDriver(act1);
    ActuatorMutexDriver * actm2 = new ActuatorMutexDriver(act2);
    actm1->setMutex(mutex);
    actm2->setMutex(mutex);

    actm1->setActive(true, 5);
    BOOST_CHECK(actm1->isActive()); // actuator is first, so is allowed to go active
    BOOST_CHECK(act1->isActive()); // target actuator is active

    actm2->setActive(true, 10);
    BOOST_CHECK(!act2->isActive()); // actuator 2 is not allowed to go active while act1 is still active

    actm1->setActive(false, 5);
    BOOST_CHECK(!act1->isActive());
    actm1->setActive(true, 5);
    BOOST_CHECK(!act1->isActive()); // second request to go active from act1 is blocked, because act2 has an open request with higher priority

    actm2->setActive(true, 10);
    BOOST_CHECK(act2->isActive()); // actuator 2 is now allowed to go active
}

BOOST_AUTO_TEST_CASE(dead_time_between_actuators_is_honored) {
    ActuatorDigital * act1 = new ActuatorBool();
    ActuatorDigital * act2 = new ActuatorBool();
    ActuatorMutexGroup * mutex = new ActuatorMutexGroup();

    ActuatorMutexDriver * actm1 = new ActuatorMutexDriver(act1);
    ActuatorMutexDriver * actm2 = new ActuatorMutexDriver(act2);
    actm1->setMutex(mutex);
    actm2->setMutex(mutex);

    mutex->setDeadTime(10000); // 10 seconds dead time

    actm1->setActive(true, 5);
    BOOST_CHECK(actm1->isActive()); // actuator is first, so is allowed to go active
    BOOST_CHECK(act1->isActive()); // target actuator is active

    delay(1000);

    actm2->setActive(true, 10);
    BOOST_CHECK(!act2->isActive()); // actuator 2 is not allowed to go active while act1 is still active

    actm1->setActive(false, 5);
    BOOST_CHECK(!actm1->isActive());
    actm1->setActive(true, 5);
    BOOST_CHECK(!actm1->isActive()); // second request to go active from act1 is blocked, because act2 has an open request with higher priority

    actm2->setActive(true, 10);
    BOOST_CHECK(!actm2->isActive()); // actuator 2 is still not allowed to go active, because of dead time

    int i;
    for(i = 1; i<20; i++){
        delay(1000);
        actm2->setActive(true, 10);
        if(actm2->isActive()){
            break;
        }

    }
    BOOST_CHECK(actm2->isActive());
    BOOST_CHECK_EQUAL(i,10); // act2 was allowed to go active after 10 seconds
}

BOOST_AUTO_TEST_SUITE_END()

<commit_msg>Added test for mutex with time limited actuators<commit_after>/*
* Copyright 2015 BrewPi/Elco Jacobs.
*
* This file is part of BrewPi.
*
* BrewPi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* BrewPi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <boost/test/unit_test.hpp>

#include "runner.h"

#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time, to seed rand */
#include <cstring>

#include "ActuatorInterfaces.h"
#include "ActuatorTimeLimited.h"
#include "ActuatorMocks.h"
#include "Ticks.h"
#include "ActuatorMutexGroup.h"
#include "ActuatorMutexDriver.h"

BOOST_AUTO_TEST_SUITE(ActuatorMutexTest)

BOOST_AUTO_TEST_CASE(two_actuators_belonging_to_the_same_group_cannot_be_active_at_once) {
    ActuatorDigital * act1 = new ActuatorBool();
    ActuatorDigital * act2 = new ActuatorBool();
    ActuatorMutexGroup * mutex = new ActuatorMutexGroup();

    ActuatorMutexDriver * actm1 = new ActuatorMutexDriver(act1);
    ActuatorMutexDriver * actm2 = new ActuatorMutexDriver(act2);
    actm1->setMutex(mutex);
    actm2->setMutex(mutex);

    actm1->setActive(true, 5);
    BOOST_CHECK(actm1->isActive()); // actuator is first, so is allowed to go active
    BOOST_CHECK(act1->isActive()); // target actuator is active

    actm2->setActive(true, 10);
    BOOST_CHECK(!act2->isActive()); // actuator 2 is not allowed to go active while act1 is still active

    actm1->setActive(false, 5);
    BOOST_CHECK(!act1->isActive());
    actm1->setActive(true, 5);
    BOOST_CHECK(!act1->isActive()); // second request to go active from act1 is blocked, because act2 has an open request with higher priority

    actm2->setActive(true, 10);
    BOOST_CHECK(act2->isActive()); // actuator 2 is now allowed to go active
}

BOOST_AUTO_TEST_CASE(dead_time_between_actuators_is_honored) {
    ActuatorDigital * act1 = new ActuatorBool();
    ActuatorDigital * act2 = new ActuatorBool();
    ActuatorMutexGroup * mutex = new ActuatorMutexGroup();

    ActuatorMutexDriver * actm1 = new ActuatorMutexDriver(act1);
    ActuatorMutexDriver * actm2 = new ActuatorMutexDriver(act2);
    actm1->setMutex(mutex);
    actm2->setMutex(mutex);

    mutex->setDeadTime(10000); // 10 seconds dead time

    actm1->setActive(true, 5);
    BOOST_CHECK(actm1->isActive()); // actuator is first, so is allowed to go active
    BOOST_CHECK(act1->isActive()); // target actuator is active

    delay(1000);

    actm2->setActive(true, 10);
    BOOST_CHECK(!act2->isActive()); // actuator 2 is not allowed to go active while act1 is still active

    actm1->setActive(false, 5);
    BOOST_CHECK(!actm1->isActive());
    actm1->setActive(true, 5);
    BOOST_CHECK(!actm1->isActive()); // second request to go active from act1 is blocked, because act2 has an open request with higher priority

    actm2->setActive(true, 10);
    BOOST_CHECK(!actm2->isActive()); // actuator 2 is still not allowed to go active, because of dead time

    int i;
    for(i = 1; i<20; i++){
        delay(1000);
        actm2->setActive(true, 10);
        if(actm2->isActive()){
            break;
        }

    }
    BOOST_CHECK(actm2->isActive());
    BOOST_CHECK_EQUAL(i,10); // act2 was allowed to go active after 10 seconds
}

BOOST_AUTO_TEST_CASE(mutex_works_with_time_limited_actuator) {
    ActuatorDigital * act1 = new ActuatorBool();
    ActuatorTimeLimited * act1tl = new ActuatorTimeLimited(act1, 10, 20);
    ActuatorDigital * act2 = new ActuatorBool();

    delay(20000); // let initial minimum off time pass

    ActuatorMutexGroup * mutex = new ActuatorMutexGroup();

    ActuatorMutexDriver * actm1 = new ActuatorMutexDriver(act1tl);
    ActuatorMutexDriver * actm2 = new ActuatorMutexDriver(act2);
    actm1->setMutex(mutex);
    actm2->setMutex(mutex);

    actm1->setActive(true, 5);
    BOOST_CHECK(act1->isActive()); // target actuator is active

    for(int i=0; i<200; i++){
        mutex->update(); // does not pass time
    }

    actm1->setActive(false, 5);
    BOOST_CHECK(act1->isActive()); // target actuator is still active due to time limit

    for(int i=0; i<200; i++){
        mutex->update();
    }
    actm2->setActive(true, 10);
    BOOST_CHECK(!act2->isActive()); // actuator 2 is not allowed to go active while act1 is still active

}
BOOST_AUTO_TEST_SUITE_END()

<|endoftext|>


<commit_before>// The comment lines containing FRAGMENT(fragment-line) are there for the
// documentation system.  You can ignore them when reading this file.
// FRAGMENT(includes)
#include <iostream>
#include <seqan/sequence.h>
#include <seqan/file.h>

int main()
{
// FRAGMENT(metafunctions)
	seqan::String<char> str = "admn";
	seqan::Iterator<seqan::String<char> >::Type it = begin(str);
	seqan::Iterator<seqan::String<char> >::Type itEnd = end(str);
// FRAGMENT(iterators)
	while (it != itEnd) {
		::std::cout << *it;
		++it;
	}
	::std::cout << ::std::endl;
// FRAGMENT(rooted-iterators)
	seqan::Iterator<seqan::String<char>, seqan::Rooted >::Type it2 = begin(str);
	for (goBegin(it2); !atEnd(it2); goNext(it2)){
		++value(it2);
	}
// FRAGMENT(iterator-reverse)
	goEnd(it2);
	while (!atBegin(it2)) {
		goPrevious(it2);
		::std::cout << getValue(it2);
	}
	::std::cout << ::std::endl;
// FRAGMENT(assign-value)
	assignValue(begin(str), 'X');
	::std::cout << str << ::std::endl;

	return 0;
}
<commit_msg>Sequence Iterator Demo, adjusting braces.<commit_after>// The comment lines containing FRAGMENT(fragment-line) are there for the
// documentation system.  You can ignore them when reading this file.
// FRAGMENT(includes)
#include <iostream>
#include <seqan/sequence.h>
#include <seqan/file.h>

int main() {
// FRAGMENT(metafunctions)
	seqan::String<char> str = "admn";
	seqan::Iterator<seqan::String<char> >::Type it = begin(str);
	seqan::Iterator<seqan::String<char> >::Type itEnd = end(str);
// FRAGMENT(iterators)
	while (it != itEnd) {
		::std::cout << *it;
		++it;
	}
	::std::cout << ::std::endl;
// FRAGMENT(rooted-iterators)
	seqan::Iterator<seqan::String<char>, seqan::Rooted >::Type it2 = begin(str);
	for (goBegin(it2); !atEnd(it2); goNext(it2)){
		++value(it2);
	}
// FRAGMENT(iterator-reverse)
	goEnd(it2);
	while (!atBegin(it2)) {
		goPrevious(it2);
		::std::cout << getValue(it2);
	}
	::std::cout << ::std::endl;
// FRAGMENT(assign-value)
	assignValue(begin(str), 'X');
	::std::cout << str << ::std::endl;

	return 0;
}
<|endoftext|>


<commit_before>#include "marian.h"
#include "translator/beam_search.h"
#include "translator/translator.h"

#include "3rd_party/simple-websocket-server/server_ws.hpp"

typedef SimpleWeb::SocketServer<SimpleWeb::WS> WsServer;

int main(int argc, char** argv) {
  using namespace marian;

  auto options = New<Config>(argc, argv, ConfigMode::translating);
  auto task = New<TranslateLoopMultiGPU<BeamSearch>>(options);

  WsServer server_;
  server_.config.port = 1234;

  auto &echo = server_.endpoint["^/translate/?$"];

  echo.on_message = [&task](std::shared_ptr<WsServer::Connection> connection,
                       std::shared_ptr<WsServer::Message> message) {
    auto message_str = message->string();

    std::cout << "Server: Message received: \"" << message_str << "\" from "
              << connection.get() << std::endl;
    std::cout << "Server: Sending message \"" << message_str << "\" to "
              << connection.get() << std::endl;

    auto send_stream = std::make_shared<WsServer::SendStream>();
    boost::timer::cpu_timer timer;
    for(auto& transl : task->run({message_str})) {
      *send_stream << transl << std::endl;
    }
    LOG(info)->info("Search took: {}", timer.format(5, "%ws"));

    // connection->send is an asynchronous function
    connection->send(send_stream, [](const SimpleWeb::error_code &ec) {
      if(ec) {
        std::cout << "Server: Error sending message. " <<
            // Error Codes for error code meanings:
            // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html
            "Error: " << ec << ", error message: " << ec.message() << std::endl;
      }
    });

    // Alternatively, using a convenience function:
    // connection->send(message_str, [](const SimpleWeb::error_code & /*ec*/) {
    // /*handle error*/ });
  };

  echo.on_open = [](std::shared_ptr<WsServer::Connection> connection) {
    std::cout << "Server: Opened connection " << connection.get() << std::endl;
  };

  // See RFC 6455 7.4.1. for status codes
  echo.on_close = [](std::shared_ptr<WsServer::Connection> connection,
                     int status,
                     const std::string & /*reason*/) {
    std::cout << "Server: Closed connection " << connection.get()
              << " with status code " << status << std::endl;
  };

  // Error Codes for error code meanings
  // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html,
  echo.on_error = [](std::shared_ptr<WsServer::Connection> connection,
                     const SimpleWeb::error_code &ec) {
    std::cout << "Server: Error in connection " << connection.get() << ". "
              << "Error: " << ec << ", error message: " << ec.message()
              << std::endl;
  };

  std::thread server_thread([&server_]() {
    LOG(info)->info("Server started");
    server_.start();
  });

  server_thread.join();

  return 0;
}
<commit_msg>Refactorize server code<commit_after>#include "marian.h"
#include "translator/beam_search.h"
#include "translator/translator.h"

#include "3rd_party/simple-websocket-server/server_ws.hpp"

typedef SimpleWeb::SocketServer<SimpleWeb::WS> WsServer;

int main(int argc, char **argv) {
  using namespace marian;

  auto options = New<Config>(argc, argv, ConfigMode::translating);
  auto task = New<TranslateLoopMultiGPU<BeamSearch>>(options);

  WsServer server_;
  server_.config.port = 1234;

  auto &translate = server_.endpoint["^/translate/?$"];

  translate.on_message = [&task](Ptr<WsServer::Connection> connection,
                                 Ptr<WsServer::Message> message) {
    auto message_str = message->string();
    LOG(info)->info("Message received: \"" + message_str + "\"");

    auto send_stream = std::make_shared<WsServer::SendStream>();
    boost::timer::cpu_timer timer;
    for(auto &transl : task->run({message_str})) {
      *send_stream << transl << std::endl;
    }
    LOG(info)->info("Search took: {}", timer.format(5, "%ws"));

    connection->send(send_stream, [](const SimpleWeb::error_code &ec) {
      if(ec) {
        auto ec_str = std::to_string(ec.value());
        LOG(warn)
            ->warn("Error sending message: (" + ec_str + ") " + ec.message());
      }
    });
  };

  // Error Codes for error code meanings
  // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html
  translate.on_error = [](Ptr<WsServer::Connection> connection,
                          const SimpleWeb::error_code &ec) {
    auto ec_str = std::to_string(ec.value());
    LOG(warn)->warn("Connection error: (" + ec_str + ") " + ec.message());
  };

  std::thread server_thread([&server_]() {
    LOG(info)->info("Server started");
    server_.start();
  });

  server_thread.join();

  return 0;
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Tim Sander <tim@krieglstein.org>
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "baremetalruncontrolfactory.h"
#include "baremetalgdbcommandsdeploystep.h"
#include "baremetalrunconfiguration.h"
#include "baremetaldevice.h"

#include <debugger/debuggerplugin.h>
#include <debugger/debuggerrunner.h>
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerkitinformation.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/project.h>
#include <projectexplorer/buildconfiguration.h>
#include <analyzerbase/analyzerstartparameters.h>
#include <analyzerbase/analyzermanager.h>
#include <analyzerbase/analyzerruncontrol.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/target.h>
#include <utils/portlist.h>
#include <utils/qtcassert.h>

using namespace Analyzer;
using namespace Debugger;
using namespace ProjectExplorer;

namespace BareMetal {
namespace Internal {

BareMetalRunControlFactory::BareMetalRunControlFactory(QObject *parent) :
    ProjectExplorer::IRunControlFactory(parent)
{
}

BareMetalRunControlFactory::~BareMetalRunControlFactory()
{
}

bool BareMetalRunControlFactory::canRun(RunConfiguration *runConfiguration, RunMode mode) const
{
    if (mode != NormalRunMode && mode != DebugRunMode && mode != DebugRunModeWithBreakOnMain
            && mode != QmlProfilerRunMode) {
        return false;
    }

    const QByteArray idStr = runConfiguration->id().name();
    return runConfiguration->isEnabled() && idStr.startsWith(BareMetalRunConfiguration::IdPrefix);
}

DebuggerStartParameters BareMetalRunControlFactory::startParameters(const BareMetalRunConfiguration *runConfig)
{
    DebuggerStartParameters params;
    Target *target = runConfig->target();
    Kit *k = target->kit();
    const BareMetalDevice::ConstPtr device = qSharedPointerCast<const BareMetalDevice>(DeviceKitInformation::device(k));
    QTC_ASSERT(device, return params);
    params.sysRoot = SysRootKitInformation::sysRoot(k).toString();
    params.debuggerCommand = DebuggerKitInformation::debuggerCommand(k).toString();
    if (ToolChain *tc = ToolChainKitInformation::toolChain(k))
        params.toolChainAbi = tc->targetAbi();
    params.languages |= CppLanguage;
    params.processArgs = runConfig->arguments();
    params.startMode = AttachToRemoteServer;
    params.executable = Utils::HostOsInfo::withExecutableSuffix(runConfig->localExecutableFilePath());
    params.remoteSetupNeeded = false; //FIXME probably start debugserver with that, how?
    params.displayName = runConfig->displayName();
    if (const Project *project = target->project()) {
        params.projectSourceDirectory = project->projectDirectory();
        if (const BuildConfiguration *buildConfig = target->activeBuildConfiguration())
            params.projectBuildDirectory = buildConfig->buildDirectory().toString();
        params.projectSourceFiles = project->files(Project::ExcludeGeneratedFiles);
    }
    params.remoteChannel = device->sshParameters().host + QLatin1String(":") + QString::number(device->sshParameters().port);
    params.remoteSetupNeeded = false; // qml stuff, not needed
    params.commandsAfterConnect = device->getGdbInitCommands().toLatin1();
    BuildConfiguration *bc = target->activeBuildConfiguration();
    BuildStepList *bsl = bc->stepList(BareMetalGdbCommandsDeployStep::stepId());
    if (bsl) {
        foreach (BuildStep *bs,bsl->steps()) {
            BareMetalGdbCommandsDeployStep *ds = qobject_cast<BareMetalGdbCommandsDeployStep *>(bs);
            if (ds) {
                if (!params.commandsAfterConnect.endsWith("\n"))
                    params.commandsAfterConnect.append("\n");
                params.commandsAfterConnect.append(ds->gdbCommands().toLocal8Bit());
            }
        }
    }
    params.useContinueInsteadOfRun = true; //we can't execute as its always running
    return params;
}

RunControl *BareMetalRunControlFactory::create(RunConfiguration *runConfiguration,
                                               RunMode mode,
                                               QString *errorMessage)
{
    QTC_ASSERT(canRun(runConfiguration, mode), return 0);

    BareMetalRunConfiguration *rc = qobject_cast<BareMetalRunConfiguration *>(runConfiguration);
    QTC_ASSERT(rc, return 0);
    IDevice::ConstPtr dev = DeviceKitInformation::device(rc->target()->kit());
    if (!dev) {
        *errorMessage = tr("Cannot debug: Kit has no device.");
        return 0;
    }
    DebuggerStartParameters sp = startParameters(rc);
    return DebuggerPlugin::createDebugger(sp,runConfiguration,errorMessage);
}

} // namespace Internal
} // namespace BareMetal
<commit_msg>baremetal: don't append executable suffix<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Tim Sander <tim@krieglstein.org>
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "baremetalruncontrolfactory.h"
#include "baremetalgdbcommandsdeploystep.h"
#include "baremetalrunconfiguration.h"
#include "baremetaldevice.h"

#include <debugger/debuggerplugin.h>
#include <debugger/debuggerrunner.h>
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerkitinformation.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/project.h>
#include <projectexplorer/buildconfiguration.h>
#include <analyzerbase/analyzerstartparameters.h>
#include <analyzerbase/analyzermanager.h>
#include <analyzerbase/analyzerruncontrol.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/target.h>
#include <utils/portlist.h>
#include <utils/qtcassert.h>

using namespace Analyzer;
using namespace Debugger;
using namespace ProjectExplorer;

namespace BareMetal {
namespace Internal {

BareMetalRunControlFactory::BareMetalRunControlFactory(QObject *parent) :
    ProjectExplorer::IRunControlFactory(parent)
{
}

BareMetalRunControlFactory::~BareMetalRunControlFactory()
{
}

bool BareMetalRunControlFactory::canRun(RunConfiguration *runConfiguration, RunMode mode) const
{
    if (mode != NormalRunMode && mode != DebugRunMode && mode != DebugRunModeWithBreakOnMain
            && mode != QmlProfilerRunMode) {
        return false;
    }

    const QByteArray idStr = runConfiguration->id().name();
    return runConfiguration->isEnabled() && idStr.startsWith(BareMetalRunConfiguration::IdPrefix);
}

DebuggerStartParameters BareMetalRunControlFactory::startParameters(const BareMetalRunConfiguration *runConfig)
{
    DebuggerStartParameters params;
    Target *target = runConfig->target();
    Kit *k = target->kit();
    const BareMetalDevice::ConstPtr device = qSharedPointerCast<const BareMetalDevice>(DeviceKitInformation::device(k));
    QTC_ASSERT(device, return params);
    params.sysRoot = SysRootKitInformation::sysRoot(k).toString();
    params.debuggerCommand = DebuggerKitInformation::debuggerCommand(k).toString();
    if (ToolChain *tc = ToolChainKitInformation::toolChain(k))
        params.toolChainAbi = tc->targetAbi();
    params.languages |= CppLanguage;
    params.processArgs = runConfig->arguments();
    params.startMode = AttachToRemoteServer;
    params.executable = runConfig->localExecutableFilePath();
    params.remoteSetupNeeded = false; //FIXME probably start debugserver with that, how?
    params.displayName = runConfig->displayName();
    if (const Project *project = target->project()) {
        params.projectSourceDirectory = project->projectDirectory();
        if (const BuildConfiguration *buildConfig = target->activeBuildConfiguration())
            params.projectBuildDirectory = buildConfig->buildDirectory().toString();
        params.projectSourceFiles = project->files(Project::ExcludeGeneratedFiles);
    }
    params.remoteChannel = device->sshParameters().host + QLatin1String(":") + QString::number(device->sshParameters().port);
    params.remoteSetupNeeded = false; // qml stuff, not needed
    params.commandsAfterConnect = device->getGdbInitCommands().toLatin1();
    BuildConfiguration *bc = target->activeBuildConfiguration();
    BuildStepList *bsl = bc->stepList(BareMetalGdbCommandsDeployStep::stepId());
    if (bsl) {
        foreach (BuildStep *bs,bsl->steps()) {
            BareMetalGdbCommandsDeployStep *ds = qobject_cast<BareMetalGdbCommandsDeployStep *>(bs);
            if (ds) {
                if (!params.commandsAfterConnect.endsWith("\n"))
                    params.commandsAfterConnect.append("\n");
                params.commandsAfterConnect.append(ds->gdbCommands().toLocal8Bit());
            }
        }
    }
    params.useContinueInsteadOfRun = true; //we can't execute as its always running
    return params;
}

RunControl *BareMetalRunControlFactory::create(RunConfiguration *runConfiguration,
                                               RunMode mode,
                                               QString *errorMessage)
{
    QTC_ASSERT(canRun(runConfiguration, mode), return 0);

    BareMetalRunConfiguration *rc = qobject_cast<BareMetalRunConfiguration *>(runConfiguration);
    QTC_ASSERT(rc, return 0);
    IDevice::ConstPtr dev = DeviceKitInformation::device(rc->target()->kit());
    if (!dev) {
        *errorMessage = tr("Cannot debug: Kit has no device.");
        return 0;
    }
    DebuggerStartParameters sp = startParameters(rc);
    return DebuggerPlugin::createDebugger(sp,runConfiguration,errorMessage);
}

} // namespace Internal
} // namespace BareMetal
<|endoftext|>