<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtTest/QtTest>
#include <QtGui/QtGui>
#include <QtNetwork/QtNetwork>
#include "../../shared/util.h"
#include "../network-settings.h"

#define TESTFILE QString("http://%1/qtest/cgi-bin/").arg(QtNetworkSettings::serverName())

class tst_QAbstractNetworkCache : public QObject
{
    Q_OBJECT

public:
    tst_QAbstractNetworkCache();
    virtual ~tst_QAbstractNetworkCache();

private slots:
    void expires_data();
    void expires();

    void lastModified_data();
    void lastModified();

    void etag_data();
    void etag();

    void cacheControl_data();
    void cacheControl();

    void deleteCache();

private:
    void check();
};

class NetworkDiskCache : public QNetworkDiskCache
{
    Q_OBJECT
public:
    NetworkDiskCache(QObject *parent = 0)
        : QNetworkDiskCache(parent)
        , gotData(false)
    {
        QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation)
                                    + QLatin1String("/qnetworkdiskcache/");
        setCacheDirectory(location);
        clear();
    }

    QIODevice *data(const QUrl &url)
    {
        gotData = true;
        return QNetworkDiskCache::data(url);
    }

    bool gotData;
};

tst_QAbstractNetworkCache::tst_QAbstractNetworkCache()
{
    Q_SET_DEFAULT_IAP

    QCoreApplication::setOrganizationName(QLatin1String("Trolltech"));
    QCoreApplication::setApplicationName(QLatin1String("autotest_qabstractnetworkcache"));
    QCoreApplication::setApplicationVersion(QLatin1String("1.0"));
}

tst_QAbstractNetworkCache::~tst_QAbstractNetworkCache()
{
}

static bool AlwaysTrue = true;
static bool AlwaysFalse = false;

Q_DECLARE_METATYPE(QNetworkRequest::CacheLoadControl)

void tst_QAbstractNetworkCache::expires_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");

    QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires304.cgi" << AlwaysFalse;
    QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires304.cgi" << true;
    QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires304.cgi" << AlwaysTrue;
    QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_expires304.cgi" << true;

    QTest::newRow("500-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires500.cgi" << AlwaysFalse;
    QTest::newRow("500-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires500.cgi" << true;
    QTest::newRow("500-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires500.cgi" << AlwaysTrue;
    QTest::newRow("500-3") << QNetworkRequest::PreferCache << "httpcachetest_expires500.cgi" << true;

    QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires200.cgi" << AlwaysFalse;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires200.cgi" << false;
    QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires200.cgi" << AlwaysTrue;
    QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_expires200.cgi" << true;
}

void tst_QAbstractNetworkCache::expires()
{
    check();
}

void tst_QAbstractNetworkCache::lastModified_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");

    QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified304.cgi" << AlwaysFalse;
    QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified304.cgi" << true;
    QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified304.cgi" << AlwaysTrue;
    QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified304.cgi" << true;

    QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified200.cgi" << AlwaysFalse;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified200.cgi" << false;
    QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified200.cgi" << AlwaysTrue;
    QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified200.cgi" << true;
}

void tst_QAbstractNetworkCache::lastModified()
{
    check();
}

void tst_QAbstractNetworkCache::etag_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");

    QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag304.cgi" << AlwaysFalse;
    QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag304.cgi" << true;
    QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag304.cgi" << AlwaysTrue;
    QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_etag304.cgi" << true;

    QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag200.cgi" << AlwaysFalse;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag200.cgi" << false;
    QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag200.cgi" << AlwaysTrue;
    QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_etag200.cgi" << true;
}

void tst_QAbstractNetworkCache::etag()
{
    check();
}

void tst_QAbstractNetworkCache::cacheControl_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");
    QTest::newRow("200-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=-1" << true;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol-expire.cgi" << false;

    QTest::newRow("200-2") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << AlwaysFalse;
    QTest::newRow("200-3") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << false;
    QTest::newRow("200-4") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;//AlwaysTrue;
    QTest::newRow("200-5") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;

    QTest::newRow("304-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000" << true;

    QTest::newRow("304-1") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysFalse;
    QTest::newRow("304-2") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
    QTest::newRow("304-3") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysTrue;
    QTest::newRow("304-4") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
}

void tst_QAbstractNetworkCache::cacheControl()
{
    check();
}

void tst_QAbstractNetworkCache::check()
{
    QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl);
    QFETCH(QString, url);
    QFETCH(bool, fetchFromCache);

    QNetworkAccessManager manager;
    NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
    manager.setCache(diskCache);
    QCOMPARE(diskCache->gotData, false);

    QNetworkRequest request(QUrl(TESTFILE + url));

    // prime the cache
    QNetworkReply *reply = manager.get(request);
    QSignalSpy downloaded1(reply, SIGNAL(finished()));
    QTRY_COMPARE(downloaded1.count(), 1);
    QCOMPARE(diskCache->gotData, false);
    QByteArray goodData = reply->readAll();

    request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl);

    // should be in the cache now
    QNetworkReply *reply2 = manager.get(request);
    QSignalSpy downloaded2(reply2, SIGNAL(finished()));
    QTRY_COMPARE(downloaded2.count(), 1);

    QByteArray secondData = reply2->readAll();
    if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
        QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError);
        QCOMPARE(secondData, QByteArray());
    } else {
        if (reply2->error() != QNetworkReply::NoError)
            qDebug() << reply2->errorString();
        QCOMPARE(reply2->error(), QNetworkReply::NoError);
        QCOMPARE(QString(secondData), QString(goodData));
        QCOMPARE(secondData, goodData);
        QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
    }

    if (fetchFromCache) {
        QList<QByteArray> rawHeaderList = reply->rawHeaderList();
        QList<QByteArray> rawHeaderList2 = reply2->rawHeaderList();
        qSort(rawHeaderList);
        qSort(rawHeaderList2);

        // headers can change
        for (int i = 0; i < rawHeaderList.count(); ++i) {
            //qDebug() << i << rawHeaderList.value(i) << reply->rawHeader(rawHeaderList.value(i));
            //qDebug() << i << rawHeaderList2.value(i) << reply2->rawHeader(rawHeaderList2.value(i));
            //QCOMPARE(QString(rawHeaderList.value(i)), QString(rawHeaderList2.value(i)));
            //QCOMPARE(QString(reply->rawHeader(rawHeaderList.value(i))), QString(reply2->rawHeader(rawHeaderList2.value(i))));
        }
        //QCOMPARE(rawHeaderList.count(), rawHeaderList2.count());

    }
    QCOMPARE(diskCache->gotData, fetchFromCache);
}

void tst_QAbstractNetworkCache::deleteCache()
{
    QNetworkAccessManager manager;
    NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
    manager.setCache(diskCache);

    QString url = "httpcachetest_cachecontrol.cgi?max-age=1000";
    QNetworkRequest request(QUrl(TESTFILE + url));
    QNetworkReply *reply = manager.get(request);
    QSignalSpy downloaded1(reply, SIGNAL(finished()));
    manager.setCache(0);
    QTRY_COMPARE(downloaded1.count(), 1);
}

QTEST_MAIN(tst_QAbstractNetworkCache)
#include "tst_qabstractnetworkcache.moc"

<commit_msg>Autotest: the cache code no longer returns expired pages.<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtTest/QtTest>
#include <QtGui/QtGui>
#include <QtNetwork/QtNetwork>
#include "../../shared/util.h"
#include "../network-settings.h"

#define TESTFILE QString("http://%1/qtest/cgi-bin/").arg(QtNetworkSettings::serverName())

class tst_QAbstractNetworkCache : public QObject
{
    Q_OBJECT

public:
    tst_QAbstractNetworkCache();
    virtual ~tst_QAbstractNetworkCache();

private slots:
    void expires_data();
    void expires();

    void lastModified_data();
    void lastModified();

    void etag_data();
    void etag();

    void cacheControl_data();
    void cacheControl();

    void deleteCache();

private:
    void check();
};

class NetworkDiskCache : public QNetworkDiskCache
{
    Q_OBJECT
public:
    NetworkDiskCache(QObject *parent = 0)
        : QNetworkDiskCache(parent)
        , gotData(false)
    {
        QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation)
                                    + QLatin1String("/qnetworkdiskcache/");
        setCacheDirectory(location);
        clear();
    }

    QIODevice *data(const QUrl &url)
    {
        gotData = true;
        return QNetworkDiskCache::data(url);
    }

    bool gotData;
};

tst_QAbstractNetworkCache::tst_QAbstractNetworkCache()
{
    Q_SET_DEFAULT_IAP

    QCoreApplication::setOrganizationName(QLatin1String("Trolltech"));
    QCoreApplication::setApplicationName(QLatin1String("autotest_qabstractnetworkcache"));
    QCoreApplication::setApplicationVersion(QLatin1String("1.0"));
}

tst_QAbstractNetworkCache::~tst_QAbstractNetworkCache()
{
}

static bool AlwaysTrue = true;
static bool AlwaysFalse = false;

Q_DECLARE_METATYPE(QNetworkRequest::CacheLoadControl)

void tst_QAbstractNetworkCache::expires_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");

    QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires304.cgi" << AlwaysFalse;
    QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires304.cgi" << true;
    QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires304.cgi" << AlwaysTrue;
    QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_expires304.cgi" << true;

    QTest::newRow("500-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires500.cgi" << AlwaysFalse;
    QTest::newRow("500-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires500.cgi" << true;
    QTest::newRow("500-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires500.cgi" << AlwaysTrue;
    QTest::newRow("500-3") << QNetworkRequest::PreferCache << "httpcachetest_expires500.cgi" << true;

    QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires200.cgi" << AlwaysFalse;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires200.cgi" << false;
    QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires200.cgi" << AlwaysTrue;
    QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_expires200.cgi" << false;
}

void tst_QAbstractNetworkCache::expires()
{
    check();
}

void tst_QAbstractNetworkCache::lastModified_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");

    QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified304.cgi" << AlwaysFalse;
    QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified304.cgi" << true;
    QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified304.cgi" << AlwaysTrue;
    QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified304.cgi" << true;

    QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified200.cgi" << AlwaysFalse;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified200.cgi" << false;
    QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified200.cgi" << AlwaysTrue;
    QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified200.cgi" << false;
}

void tst_QAbstractNetworkCache::lastModified()
{
    check();
}

void tst_QAbstractNetworkCache::etag_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");

    QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag304.cgi" << AlwaysFalse;
    QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag304.cgi" << true;
    QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag304.cgi" << AlwaysTrue;
    QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_etag304.cgi" << true;

    QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag200.cgi" << AlwaysFalse;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag200.cgi" << false;
    QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag200.cgi" << AlwaysTrue;
    QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_etag200.cgi" << false;
}

void tst_QAbstractNetworkCache::etag()
{
    check();
}

void tst_QAbstractNetworkCache::cacheControl_data()
{
    QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
    QTest::addColumn<QString>("url");
    QTest::addColumn<bool>("fetchFromCache");
    QTest::newRow("200-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=-1" << true;
    QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol-expire.cgi" << false;

    QTest::newRow("200-2") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << AlwaysFalse;
    QTest::newRow("200-3") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << false;
    QTest::newRow("200-4") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;//AlwaysTrue;
    QTest::newRow("200-5") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;

    QTest::newRow("304-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000" << true;

    QTest::newRow("304-1") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysFalse;
    QTest::newRow("304-2") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
    QTest::newRow("304-3") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysTrue;
    QTest::newRow("304-4") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
}

void tst_QAbstractNetworkCache::cacheControl()
{
    check();
}

void tst_QAbstractNetworkCache::check()
{
    QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl);
    QFETCH(QString, url);
    QFETCH(bool, fetchFromCache);

    QNetworkAccessManager manager;
    NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
    manager.setCache(diskCache);
    QCOMPARE(diskCache->gotData, false);

    QNetworkRequest request(QUrl(TESTFILE + url));

    // prime the cache
    QNetworkReply *reply = manager.get(request);
    QSignalSpy downloaded1(reply, SIGNAL(finished()));
    QTRY_COMPARE(downloaded1.count(), 1);
    QCOMPARE(diskCache->gotData, false);
    QByteArray goodData = reply->readAll();

    request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl);

    // should be in the cache now
    QNetworkReply *reply2 = manager.get(request);
    QSignalSpy downloaded2(reply2, SIGNAL(finished()));
    QTRY_COMPARE(downloaded2.count(), 1);

    QByteArray secondData = reply2->readAll();
    if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
        QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError);
        QCOMPARE(secondData, QByteArray());
    } else {
        if (reply2->error() != QNetworkReply::NoError)
            qDebug() << reply2->errorString();
        QCOMPARE(reply2->error(), QNetworkReply::NoError);
        QCOMPARE(QString(secondData), QString(goodData));
        QCOMPARE(secondData, goodData);
        QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
    }

    if (fetchFromCache) {
        QList<QByteArray> rawHeaderList = reply->rawHeaderList();
        QList<QByteArray> rawHeaderList2 = reply2->rawHeaderList();
        qSort(rawHeaderList);
        qSort(rawHeaderList2);

        // headers can change
        for (int i = 0; i < rawHeaderList.count(); ++i) {
            //qDebug() << i << rawHeaderList.value(i) << reply->rawHeader(rawHeaderList.value(i));
            //qDebug() << i << rawHeaderList2.value(i) << reply2->rawHeader(rawHeaderList2.value(i));
            //QCOMPARE(QString(rawHeaderList.value(i)), QString(rawHeaderList2.value(i)));
            //QCOMPARE(QString(reply->rawHeader(rawHeaderList.value(i))), QString(reply2->rawHeader(rawHeaderList2.value(i))));
        }
        //QCOMPARE(rawHeaderList.count(), rawHeaderList2.count());

    }
    QCOMPARE(diskCache->gotData, fetchFromCache);
}

void tst_QAbstractNetworkCache::deleteCache()
{
    QNetworkAccessManager manager;
    NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
    manager.setCache(diskCache);

    QString url = "httpcachetest_cachecontrol.cgi?max-age=1000";
    QNetworkRequest request(QUrl(TESTFILE + url));
    QNetworkReply *reply = manager.get(request);
    QSignalSpy downloaded1(reply, SIGNAL(finished()));
    manager.setCache(0);
    QTRY_COMPARE(downloaded1.count(), 1);
}

QTEST_MAIN(tst_QAbstractNetworkCache)
#include "tst_qabstractnetworkcache.moc"

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2003-2009, John Wiegley.  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.
 *
 * - Neither the name of New Artisans LLC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "session.h"
#include "report.h"
#include "iterators.h"
#include "filters.h"

namespace ledger {

void set_session_context(session_t * session)
{
  if (session) {
    amount_t::initialize(session->commodity_pool);

    // jww (2009-02-04): Is amount_t the right place for parse_conversion to
    // happen?
    amount_t::parse_conversion("1.0m", "60s");
    amount_t::parse_conversion("1.0h", "60m");

    value_t::initialize();
  }
  else if (! session) {
    value_t::shutdown();
    amount_t::shutdown();
  }
}

session_t::session_t()
  : flush_on_next_data_file(false),

    current_year(CURRENT_DATE().year()),

    commodity_pool(new commodity_pool_t),
    master(new account_t),
    journal(new journal_t(master.get()))
{
  TRACE_CTOR(session_t, "");

  if (const char * home_var = std::getenv("HOME"))
    HANDLER(price_db_).on((path(home_var) / ".pricedb").string());
  else
    HANDLER(price_db_).on(path("./.pricedb").string());

  // Add time commodity conversions, so that timelog's may be parsed
  // in terms of seconds, but reported as minutes or hours.
  if (commodity_t * commodity = commodity_pool->create("s"))
    commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
  else
    assert(false);
}

std::size_t session_t::read_journal(std::istream& in,
				    const path&	  pathname,
				    account_t *   master)
{
  if (! master)
    master = journal->master;
  std::size_t count = journal->parse(in, *this, master, &pathname);
  clean_accounts();		// remove calculated totals
  return count;
}

std::size_t session_t::read_journal(const path& pathname,
				    account_t * master)
{
  if (! exists(pathname))
    throw_(std::logic_error, "Cannot read file" << pathname);

  ifstream stream(pathname);
  return read_journal(stream, pathname, master);
}

std::size_t session_t::read_data(const string& master_account)
{
  if (HANDLER(file_).data_files.empty())
    throw_(parse_error, "No journal file was specified (please use -f)");

  std::size_t entry_count = 0;

  account_t * acct = journal->master;
  if (! master_account.empty())
    acct = journal->find_account(master_account);

  if (HANDLED(price_db_) && exists(path(HANDLER(price_db_).str()))) {
    if (read_journal(HANDLER(price_db_).str()))
      throw_(parse_error, "Entries not allowed in price history file");
  }

  foreach (const path& pathname, HANDLER(file_).data_files) {
    if (pathname == "-") {
      // To avoid problems with stdin and pipes, etc., we read the entire
      // file in beforehand into a memory buffer, and then parcel it out
      // from there.
      std::ostringstream buffer;

      while (std::cin.good() && ! std::cin.eof()) {
	char line[8192];
	std::cin.read(line, 8192);
	std::streamsize count = std::cin.gcount();
	buffer.write(line, count);
      }
      buffer.flush();

      std::istringstream buf_in(buffer.str());

      entry_count += read_journal(buf_in, "/dev/stdin", acct);
    }
    else if (exists(pathname)) {
      entry_count += read_journal(pathname, acct);
    }
    else {
      throw_(parse_error, "Could not open journal file '" << pathname << "'");
    }
  }

  VERIFY(journal->valid());

  return entry_count;
}

void session_t::read_journal_files()
{
  INFO_START(journal, "Read journal file");

  string master_account;
  if (HANDLED(account_))
    master_account = HANDLER(account_).str();

  std::size_t count = read_data(master_account);
  if (count == 0)
    throw_(parse_error, "Failed to locate any journal entries; "
	   "did you specify a valid file with -f?");

  INFO_FINISH(journal);

  INFO("Found " << count << " entries");
}

void session_t::close_journal_files()
{
  journal.reset();
  master.reset();
  commodity_pool.reset();
  amount_t::shutdown();

  commodity_pool.reset(new commodity_pool_t);
  amount_t::initialize(commodity_pool);
  master.reset(new account_t);
  journal.reset(new journal_t(master.get()));
}

void session_t::clean_xacts()
{
  journal_xacts_iterator walker(*journal.get());
  pass_down_xacts(xact_handler_ptr(new clear_xact_xdata), walker);
}

void session_t::clean_xacts(entry_t& entry)
{
  entry_xacts_iterator walker(entry);
  pass_down_xacts(xact_handler_ptr(new clear_xact_xdata), walker);
}

void session_t::clean_accounts()
{
  basic_accounts_iterator acct_walker(*master);
  pass_down_accounts(acct_handler_ptr(new clear_account_xdata), acct_walker);
  master->clear_xdata();
}

option_t<session_t> * session_t::lookup_option(const char * p)
{
  switch (*p) {
  case 'a':
    OPT_(account_); // -a
    break;
  case 'd':
    OPT(download); // -Q
    break;
  case 'f':
    OPT_(file_); // -f
    break;
  case 'i':
    OPT(input_date_format_);
    break;
  case 'l':
    OPT(leeway_);
    break;
  case 'p':
    OPT(price_db_);
    break;
  case 'Q':
    OPT_CH(download); // -Q
    break;
  }
  return NULL;
}

expr_t::ptr_op_t session_t::lookup(const string& name)
{
  const char * p = name.c_str();
  switch (*p) {
  case 'o':
    if (WANT_OPT()) { p += OPT_PREFIX_LEN;
      if (option_t<session_t> * handler = lookup_option(p))
	return MAKE_OPT_HANDLER(session_t, handler);
    }
    break;
  }

  return symbol_scope_t::lookup(name);
}

} // namespace ledger
<commit_msg>Always perform tilde expansion on input pathnames<commit_after>/*
 * Copyright (c) 2003-2009, John Wiegley.  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.
 *
 * - Neither the name of New Artisans LLC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "session.h"
#include "report.h"
#include "iterators.h"
#include "filters.h"

namespace ledger {

void set_session_context(session_t * session)
{
  if (session) {
    amount_t::initialize(session->commodity_pool);

    // jww (2009-02-04): Is amount_t the right place for parse_conversion to
    // happen?
    amount_t::parse_conversion("1.0m", "60s");
    amount_t::parse_conversion("1.0h", "60m");

    value_t::initialize();
  }
  else if (! session) {
    value_t::shutdown();
    amount_t::shutdown();
  }
}

session_t::session_t()
  : flush_on_next_data_file(false),

    current_year(CURRENT_DATE().year()),

    commodity_pool(new commodity_pool_t),
    master(new account_t),
    journal(new journal_t(master.get()))
{
  TRACE_CTOR(session_t, "");

  if (const char * home_var = std::getenv("HOME"))
    HANDLER(price_db_).on((path(home_var) / ".pricedb").string());
  else
    HANDLER(price_db_).on(path("./.pricedb").string());

  // Add time commodity conversions, so that timelog's may be parsed
  // in terms of seconds, but reported as minutes or hours.
  if (commodity_t * commodity = commodity_pool->create("s"))
    commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
  else
    assert(false);
}

std::size_t session_t::read_journal(std::istream& in,
				    const path&	  pathname,
				    account_t *   master)
{
  if (! master)
    master = journal->master;
  std::size_t count = journal->parse(in, *this, master, &pathname);
  clean_accounts();		// remove calculated totals
  return count;
}

std::size_t session_t::read_journal(const path& pathname,
				    account_t * master)
{
  if (! exists(pathname))
    throw_(std::logic_error, "Cannot read file" << pathname);

  ifstream stream(pathname);
  return read_journal(stream, pathname, master);
}

std::size_t session_t::read_data(const string& master_account)
{
  if (HANDLER(file_).data_files.empty())
    throw_(parse_error, "No journal file was specified (please use -f)");

  std::size_t entry_count = 0;

  account_t * acct = journal->master;
  if (! master_account.empty())
    acct = journal->find_account(master_account);

  if (HANDLED(price_db_)) {
    path price_db_path = resolve_path(HANDLER(price_db_).str());
    if (exists(price_db_path) && read_journal(price_db_path) > 0)
	throw_(parse_error, "Entries not allowed in price history file");
  }

  foreach (const path& pathname, HANDLER(file_).data_files) {
    path filename = resolve_path(pathname);
    if (filename == "-") {
      // To avoid problems with stdin and pipes, etc., we read the entire
      // file in beforehand into a memory buffer, and then parcel it out
      // from there.
      std::ostringstream buffer;

      while (std::cin.good() && ! std::cin.eof()) {
	char line[8192];
	std::cin.read(line, 8192);
	std::streamsize count = std::cin.gcount();
	buffer.write(line, count);
      }
      buffer.flush();

      std::istringstream buf_in(buffer.str());

      entry_count += read_journal(buf_in, "/dev/stdin", acct);
    }
    else if (exists(filename)) {
      entry_count += read_journal(filename, acct);
    }
    else {
      throw_(parse_error, "Could not read journal file '" << filename << "'");
    }
  }

  VERIFY(journal->valid());

  return entry_count;
}

void session_t::read_journal_files()
{
  INFO_START(journal, "Read journal file");

  string master_account;
  if (HANDLED(account_))
    master_account = HANDLER(account_).str();

  std::size_t count = read_data(master_account);
  if (count == 0)
    throw_(parse_error, "Failed to locate any journal entries; "
	   "did you specify a valid file with -f?");

  INFO_FINISH(journal);

  INFO("Found " << count << " entries");
}

void session_t::close_journal_files()
{
  journal.reset();
  master.reset();
  commodity_pool.reset();
  amount_t::shutdown();

  commodity_pool.reset(new commodity_pool_t);
  amount_t::initialize(commodity_pool);
  master.reset(new account_t);
  journal.reset(new journal_t(master.get()));
}

void session_t::clean_xacts()
{
  journal_xacts_iterator walker(*journal.get());
  pass_down_xacts(xact_handler_ptr(new clear_xact_xdata), walker);
}

void session_t::clean_xacts(entry_t& entry)
{
  entry_xacts_iterator walker(entry);
  pass_down_xacts(xact_handler_ptr(new clear_xact_xdata), walker);
}

void session_t::clean_accounts()
{
  basic_accounts_iterator acct_walker(*master);
  pass_down_accounts(acct_handler_ptr(new clear_account_xdata), acct_walker);
  master->clear_xdata();
}

option_t<session_t> * session_t::lookup_option(const char * p)
{
  switch (*p) {
  case 'a':
    OPT_(account_); // -a
    break;
  case 'd':
    OPT(download); // -Q
    break;
  case 'f':
    OPT_(file_); // -f
    break;
  case 'i':
    OPT(input_date_format_);
    break;
  case 'l':
    OPT(leeway_);
    break;
  case 'p':
    OPT(price_db_);
    break;
  case 'Q':
    OPT_CH(download); // -Q
    break;
  }
  return NULL;
}

expr_t::ptr_op_t session_t::lookup(const string& name)
{
  const char * p = name.c_str();
  switch (*p) {
  case 'o':
    if (WANT_OPT()) { p += OPT_PREFIX_LEN;
      if (option_t<session_t> * handler = lookup_option(p))
	return MAKE_OPT_HANDLER(session_t, handler);
    }
    break;
  }

  return symbol_scope_t::lookup(name);
}

} // namespace ledger
<|endoftext|>


<commit_before>

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

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

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

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

using namespace alpr;

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

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

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

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

  bool clock_on;

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

struct UploadThreadData
{
  std::string upload_url;
};

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

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

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

bool daemon_active;

static log4cplus::Logger logger;

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

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

  std::string configDir;

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

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

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

  try
  {

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

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

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

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

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

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

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

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

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

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

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

  CSimpleIniA ini;
  ini.SetMultiKey();

  ini.LoadFile(daemonConfigFile.c_str());

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

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

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

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

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

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

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

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

  pid_t pid;

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

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

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

      break;
    }

    // Parent process will continue and spawn more children
  }

  while (daemon_active)
  {
    usleep(30000);
  }

}

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

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

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

  int framenum = 0;

  LoggingVideoBuffer videoBuffer(logger);

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

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

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

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

    if (response != -1)
    {

      timespec startTime;
      getTimeMonotonic(&startTime);

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

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

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

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

      if (results.plates.size() > 0)
      {

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

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

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

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

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

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

        // Add the company ID to the output if configured
        if (tdata->company_id.length() > 0)
          cJSON_AddStringToObject(root, 	"company_id", 	tdata->company_id.c_str());

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

	std::string response(out);

	free(out);

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

	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

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

    int id = client.put(jsonResult);

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

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

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

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

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

  UploadThreadData* udata = (UploadThreadData*) arg;

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

      client.watch(BEANSTALK_TUBE_NAME);

      while (daemon_active)
      {
	Beanstalk::Job job;

	client.reserve(job);

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

      }

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

  curl_global_cleanup();
}

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

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

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

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

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

  }

  return success;

}<commit_msg>Removed unsafe call from segfault catcher<commit_after>

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

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

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

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

using namespace alpr;

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

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

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

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

  bool clock_on;

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

struct UploadThreadData
{
  std::string upload_url;
};

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

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

  // print out all the frames to stderr
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

bool daemon_active;

static log4cplus::Logger logger;

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

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

  std::string configDir;

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

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

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

  try
  {

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

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

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

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

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

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

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

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

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

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

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

  CSimpleIniA ini;
  ini.SetMultiKey();

  ini.LoadFile(daemonConfigFile.c_str());

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

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

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

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

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

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

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

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

  pid_t pid;

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

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

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

      break;
    }

    // Parent process will continue and spawn more children
  }

  while (daemon_active)
  {
    usleep(30000);
  }

}

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

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

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

  int framenum = 0;

  LoggingVideoBuffer videoBuffer(logger);

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

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

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

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

    if (response != -1)
    {

      timespec startTime;
      getTimeMonotonic(&startTime);

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

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

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

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

      if (results.plates.size() > 0)
      {

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

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

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

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

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

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

        // Add the company ID to the output if configured
        if (tdata->company_id.length() > 0)
          cJSON_AddStringToObject(root, 	"company_id", 	tdata->company_id.c_str());

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

	std::string response(out);

	free(out);

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

	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

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

    int id = client.put(jsonResult);

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

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

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

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

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

  UploadThreadData* udata = (UploadThreadData*) arg;

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

      client.watch(BEANSTALK_TUBE_NAME);

      while (daemon_active)
      {
	Beanstalk::Job job;

	client.reserve(job);

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

      }

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

  curl_global_cleanup();
}

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

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

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

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

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

  }

  return success;

}<|endoftext|>


<commit_before>/*     ----------------------------------------------------------------
*
*                               sgp4io.cpp
*
*    this file contains a function to read two line element sets. while
*    not formerly part of the sgp4 mathematical theory, it is
*    required for practical implemenation.
*
*                            companion code for
*               fundamentals of astrodynamics and applications
*                                    2007
*                              by david vallado
*
*       (w) 719-573-2600, email dvallado@agi.com
*
*    current :
*              27 Aug 10  david vallado
*                           fix input format and delete unused variables in twoline2rv
*    changes :
*               3 sep 08  david vallado
*                           add operationmode for afspc (a) or improved (i)
*               9 may 07  david vallado
*                           fix year correction to 57
*              27 mar 07  david vallado
*                           misc fixes to manual inputs
*              14 aug 06  david vallado
*                           original baseline
*       ----------------------------------------------------------------      */

#include "sgp4io.h"
#include "stdlib.h"

/* -----------------------------------------------------------------------------
*
*                           function twoline2rv
*
*  this function converts the two line element set character string data to
*    variables and initializes the sgp4 variables. several intermediate varaibles
*    and quantities are determined. note that the result is a structure so multiple
*    satellites can be processed simultaneously without having to reinitialize. the
*    verification mode is an important option that permits quick checks of any
*    changes to the underlying technical theory. this option works using a
*    modified tle file in which the start, stop, and delta time values are
*    included at the end of the second line of data. this only works with the
*    verification mode. the catalog mode simply propagates from -1440 to 1440 min
*    from epoch and is useful when performing entire catalog runs.
*
*  author        : david vallado                  719-573-2600    1 mar 2001
*
*  inputs        :
*    longstr1    - first line of the tle
*    longstr2    - second line of the tle
*    typerun     - type of run                    verification 'v', catalog 'c',
*                                                 manual 'm'
*    typeinput   - type of manual input           mfe 'm', epoch 'e', dayofyr 'd'
*    opsmode     - mode of operation afspc or improved 'a', 'i'
*    whichconst  - which set of constants to use  72, 84
*
*  outputs       :
*    satrec      - structure containing all the sgp4 satellite information
*
*  coupling      :
*    getgravconst-
*    days2mdhms  - conversion of days to month, day, hour, minute, second
*    jday        - convert day month year hour minute second into julian date
*    sgp4init    - initialize the sgp4 variables
*
*  references    :
*    norad spacetrack report #3
*    vallado, crawford, hujsak, kelso  2006
  --------------------------------------------------------------------------- */

void twoline2rv
     (
      char      longstr1[130], char longstr2[130],
      char opsmode, gravconsttype       whichconst,
      elsetrec& satrec
     )
     {
       char tempstr[12];
       const double deg2rad  =   pi / 180.0;         //   0.0174532925199433
       const double xpdotp   =  1440.0 / (2.0 *pi);  // 229.1831180523293

       double sec, mu, radiusearthkm, tumin, xke, j2, j3, j4, j3oj2;
       double startsec, stopsec, startdayofyr, stopdayofyr, jdstart, jdstop;
       int startyear, stopyear, startmon, stopmon, startday, stopday,
           starthr, stophr, startmin, stopmin;
       int cardnumb, numb, j;
       long revnum = 0, elnum = 0;
       char classification, intldesg[11];
       int year = 0;
       int mon, day, hr, minute, nexp, ibexp;

       getgravconst( whichconst, tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 );

       satrec.error = 0;

       // set the implied decimal points since doing a formated read
       // fixes for bad input data values (missing, ...)
       for (j = 10; j <= 15; j++)
           if (longstr1[j] == ' ')
               longstr1[j] = '_';

       if (longstr1[44] != ' ')
           longstr1[43] = longstr1[44];
       longstr1[44] = '.';
       if (longstr1[7] == ' ')
           longstr1[7] = 'U';
       if (longstr1[9] == ' ')
           longstr1[9] = '.';
       for (j = 45; j <= 49; j++)
           if (longstr1[j] == ' ')
               longstr1[j] = '0';
       if (longstr1[51] == ' ')
           longstr1[51] = '0';
       if (longstr1[53] != ' ')
           longstr1[52] = longstr1[53];
       longstr1[53] = '.';
       longstr2[25] = '.';
       for (j = 26; j <= 32; j++)
           if (longstr2[j] == ' ')
               longstr2[j] = '0';
       if (longstr1[62] == ' ')
           longstr1[62] = '0';
       if (longstr1[68] == ' ')
           longstr1[68] = '0';

       /**
       sscanf(longstr1,"%2d %5ld %1c %10s %2d %12lf %11lf %7lf %2d %7lf %2d %2d %6ld ",
                       &cardnumb,&satrec.satnum,&classification, intldesg, &satrec.epochyr,
                       &satrec.epochdays,&satrec.ndot, &satrec.nddot, &nexp, &satrec.bstar,
                       &ibexp, &numb, &elnum );
       **/

       //memcpy( tempstr, &longstr1[0] , 2); tempstr[1] = '\0'; satrec.cardnumb = atoi(tempstr);
       memcpy( tempstr, &longstr1[2] , 5); tempstr[5] = '\0'; satrec.satnum = atol(tempstr);
       //classification = longstr1[7];
       //memcpy( intldesg, &longstr1[8] , 10); intldesg[10] = '\0';
       memcpy( tempstr, &longstr1[18] , 2); tempstr[2] = '\0'; satrec.epochyr = atoi(tempstr);
       memcpy( tempstr, &longstr1[20] , 12); tempstr[12] = '\0'; satrec.epochdays = atof(tempstr);
       memcpy( tempstr, &longstr1[32] , 11); tempstr[11] = '\0'; satrec.ndot = atof(tempstr);
       memcpy( tempstr, &longstr1[43] , 7); tempstr[7] = '\0'; satrec.nddot = atof(tempstr);
       memcpy( tempstr, &longstr1[50] , 2); tempstr[2] = '\0'; nexp = atoi(tempstr);
       memcpy( tempstr, &longstr1[52] , 7); tempstr[7] = '\0'; satrec.bstar = atof(tempstr);
       memcpy( tempstr, &longstr1[59] , 2); tempstr[2] = '\0'; ibexp = atoi(tempstr);
       //memcpy( tempstr, &longstr1[61] , 2); tempstr[2] = '\0'; numb = atoi(tempstr);
       //memcpy( tempstr, &longstr1[63] , 6); tempstr[6] = '\0'; elnum = atol(tempstr);

       memcpy( tempstr, &longstr2[2] , 5); tempstr[5] = '\0'; satrec.satnum = atol(tempstr);
       memcpy( tempstr, &longstr2[7] , 9); tempstr[9] = '\0'; satrec.inclo = atof(tempstr);
       memcpy( tempstr, &longstr2[16] , 9); tempstr[9] = '\0'; satrec.nodeo = atof(tempstr);
       memcpy( tempstr, &longstr2[25] , 8); tempstr[8] = '\0'; satrec.ecco = atof(tempstr);
       memcpy( tempstr, &longstr2[33] , 9); tempstr[9] = '\0'; satrec.argpo = atof(tempstr);
       memcpy( tempstr, &longstr2[42] , 9); tempstr[9] = '\0'; satrec.mo = atof(tempstr);
       memcpy( tempstr, &longstr2[51] , 11); tempstr[10] = '\0'; satrec.no = atof(tempstr);
       //memcpy( tempstr, &longstr2[63] , 6); tempstr[6] = '\0'; revnum = atol(tempstr);

       // ---- find no, ndot, nddot ----
       satrec.no   = satrec.no / xpdotp; //* rad/min
       satrec.nddot= satrec.nddot * pow(10.0, nexp);
       satrec.bstar= satrec.bstar * pow(10.0, ibexp);

       // ---- convert to sgp4 units ----
       satrec.a    = pow( satrec.no*tumin , (-2.0/3.0) );
       satrec.ndot = satrec.ndot  / (xpdotp*1440.0);  //* ? * minperday
       satrec.nddot= satrec.nddot / (xpdotp*1440.0*1440);

       // ---- find standard orbital elements ----
       satrec.inclo = satrec.inclo  * deg2rad;
       satrec.nodeo = satrec.nodeo  * deg2rad;
       satrec.argpo = satrec.argpo  * deg2rad;
       satrec.mo    = satrec.mo     * deg2rad;

       satrec.alta = satrec.a*(1.0 + satrec.ecco) - 1.0;
       satrec.altp = satrec.a*(1.0 - satrec.ecco) - 1.0;

       // ----------------------------------------------------------------
       // find sgp4epoch time of element set
       // remember that sgp4 uses units of days from 0 jan 1950 (sgp4epoch)
       // and minutes from the epoch (time)
       // ----------------------------------------------------------------

       // ---------------- temp fix for years from 1957-2056 -------------------
       // --------- correct fix will occur when year is 4-digit in tle ---------
       if (satrec.epochyr < 57)
           year= satrec.epochyr + 2000;
         else
           year= satrec.epochyr + 1900;

       days2mdhms ( year,satrec.epochdays, mon,day,hr,minute,sec );
       jday( year,mon,day,hr,minute,sec, 0,false,satrec.jdsatepoch );

       /***
       // ---- input start stop times manually
       if ((typerun != 'v') && (typerun != 'c'))
         {
         // ------------- enter start/stop ymd hms values --------------------
           if (typeinput == 'e')
             {
               printf("input start prop year mon day hr min sec \n");
               // make sure there is no space at the end of the format specifiers in scanf!
               scanf( "%i %i %i %i %i %lf",&startyear, &startmon, &startday, &starthr, &startmin, &startsec);
               fflush(stdin);
               jday( startyear,startmon,startday,starthr,startmin,startsec, jdstart );

               printf("input stop prop year mon day hr min sec \n");
               scanf( "%i %i %i %i %i %lf",&stopyear, &stopmon, &stopday, &stophr, &stopmin, &stopsec);
               fflush(stdin);
               jday( stopyear,stopmon,stopday,stophr,stopmin,stopsec, jdstop );

               startmfe = (jdstart - satrec.jdsatepoch) * 1440.0;
               stopmfe  = (jdstop - satrec.jdsatepoch) * 1440.0;

               printf("input time step in minutes \n");
               scanf( "%lf",&deltamin );
             }
           // -------- enter start/stop year and days of year values -----------
           if (typeinput == 'd')
             {
               printf("input start year dayofyr \n");
               scanf( "%i %lf",&startyear, &startdayofyr );
               printf("input stop year dayofyr \n");
               scanf( "%i %lf",&stopyear, &stopdayofyr );

               days2mdhms ( startyear,startdayofyr, mon,day,hr,minute,sec );
               jday( startyear,mon,day,hr,minute,sec, jdstart );
               days2mdhms ( stopyear,stopdayofyr, mon,day,hr,minute,sec );
               jday( stopyear,mon,day,hr,minute,sec, jdstop );

               startmfe = (jdstart - satrec.jdsatepoch) * 1440.0;
               stopmfe  = (jdstop - satrec.jdsatepoch) * 1440.0;

               printf("input time step in minutes \n");
               scanf( "%lf",&deltamin );
             }
           // ------------------ enter start/stop mfe values -------------------
           if (typeinput == 'm')
             {
               printf("input start min from epoch \n");
               scanf( "%lf",&startmfe );
               printf("input stop min from epoch \n");
               scanf( "%lf",&stopmfe );
               printf("input time step in minutes \n");
               scanf( "%lf",&deltamin );
             }
         }
       ***/
       // ---------------- initialize the orbit at sgp4epoch -------------------
       sgp4init( whichconst, opsmode, satrec.satnum, satrec.jdsatepoch-2433281.5, satrec.bstar,
                 satrec.ecco, satrec.argpo, satrec.inclo, satrec.mo, satrec.no,
                 satrec.nodeo, satrec);
    } // end twoline2rv

/* -----------------------------------------------------------------------------
*
*                           function twolineChecksum
*
*  this function checks if the checksum on the end of the line is correct.
*
*  author        : Hopperpop                  6 mar 2016
*
*  inputs        :
*    longstr    -  line of the tle
*
*  outputs       :
*    bool        -  true if test was succesfull
*
*  references    :
*    https://en.wikipedia.org/wiki/Two-line_element_set
  --------------------------------------------------------------------------- */
bool twolineChecksum
     (
      const char      longstr[80]
     )
{

    unsigned int cks = 0;
    const char* checksumpointer = longstr + 68;

    for (const char* p = longstr; p < checksumpointer; p++){

        switch(*p){
            case 0:
              return false;
              break;
            case '-':
              cks++;
              break;
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
              cks += *p - '0';
              break;
        }

    }
    cks %= 10;   //modulo 10
    if (cks + '0' == *checksumpointer)
      return true;

    return false;
}
<commit_msg>Update sgp4io.cpp<commit_after>/*     ----------------------------------------------------------------
*
*                               sgp4io.cpp
*
*    this file contains a function to read two line element sets. while
*    not formerly part of the sgp4 mathematical theory, it is
*    required for practical implemenation.
*
*                            companion code for
*               fundamentals of astrodynamics and applications
*                                    2007
*                              by david vallado
*
*       (w) 719-573-2600, email dvallado@agi.com
*
*    current :
*              27 Aug 10  david vallado
*                           fix input format and delete unused variables in twoline2rv
*    changes :
*               3 sep 08  david vallado
*                           add operationmode for afspc (a) or improved (i)
*               9 may 07  david vallado
*                           fix year correction to 57
*              27 mar 07  david vallado
*                           misc fixes to manual inputs
*              14 aug 06  david vallado
*                           original baseline
*       ----------------------------------------------------------------      */

#include "sgp4io.h"
#include "stdlib.h"

/* -----------------------------------------------------------------------------
*
*                           function twoline2rv
*
*  this function converts the two line element set character string data to
*    variables and initializes the sgp4 variables. several intermediate varaibles
*    and quantities are determined. note that the result is a structure so multiple
*    satellites can be processed simultaneously without having to reinitialize. the
*    verification mode is an important option that permits quick checks of any
*    changes to the underlying technical theory. this option works using a
*    modified tle file in which the start, stop, and delta time values are
*    included at the end of the second line of data. this only works with the
*    verification mode. the catalog mode simply propagates from -1440 to 1440 min
*    from epoch and is useful when performing entire catalog runs.
*
*  author        : david vallado                  719-573-2600    1 mar 2001
*
*  inputs        :
*    longstr1    - first line of the tle
*    longstr2    - second line of the tle
*    typerun     - type of run                    verification 'v', catalog 'c',
*                                                 manual 'm'
*    typeinput   - type of manual input           mfe 'm', epoch 'e', dayofyr 'd'
*    opsmode     - mode of operation afspc or improved 'a', 'i'
*    whichconst  - which set of constants to use  72, 84
*
*  outputs       :
*    satrec      - structure containing all the sgp4 satellite information
*
*  coupling      :
*    getgravconst-
*    days2mdhms  - conversion of days to month, day, hour, minute, second
*    jday        - convert day month year hour minute second into julian date
*    sgp4init    - initialize the sgp4 variables
*
*  references    :
*    norad spacetrack report #3
*    vallado, crawford, hujsak, kelso  2006
  --------------------------------------------------------------------------- */

void twoline2rv
     (
      char      longstr1[130], char longstr2[130],
      char opsmode, gravconsttype       whichconst,
      elsetrec& satrec
     )
     {
       char tempstr[13];
       const double deg2rad  =   pi / 180.0;         //   0.0174532925199433
       const double xpdotp   =  1440.0 / (2.0 *pi);  // 229.1831180523293

       double sec, mu, radiusearthkm, tumin, xke, j2, j3, j4, j3oj2;
       double startsec, stopsec, startdayofyr, stopdayofyr, jdstart, jdstop;
       int startyear, stopyear, startmon, stopmon, startday, stopday,
           starthr, stophr, startmin, stopmin;
       int cardnumb, numb, j;
       long revnum = 0, elnum = 0;
       char classification, intldesg[11];
       int year = 0;
       int mon, day, hr, minute, nexp, ibexp;

       getgravconst( whichconst, tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 );

       satrec.error = 0;

       // set the implied decimal points since doing a formated read
       // fixes for bad input data values (missing, ...)
       for (j = 10; j <= 15; j++)
           if (longstr1[j] == ' ')
               longstr1[j] = '_';

       if (longstr1[44] != ' ')
           longstr1[43] = longstr1[44];
       longstr1[44] = '.';
       if (longstr1[7] == ' ')
           longstr1[7] = 'U';
       if (longstr1[9] == ' ')
           longstr1[9] = '.';
       for (j = 45; j <= 49; j++)
           if (longstr1[j] == ' ')
               longstr1[j] = '0';
       if (longstr1[51] == ' ')
           longstr1[51] = '0';
       if (longstr1[53] != ' ')
           longstr1[52] = longstr1[53];
       longstr1[53] = '.';
       longstr2[25] = '.';
       for (j = 26; j <= 32; j++)
           if (longstr2[j] == ' ')
               longstr2[j] = '0';
       if (longstr1[62] == ' ')
           longstr1[62] = '0';
       if (longstr1[68] == ' ')
           longstr1[68] = '0';

       /**
       sscanf(longstr1,"%2d %5ld %1c %10s %2d %12lf %11lf %7lf %2d %7lf %2d %2d %6ld ",
                       &cardnumb,&satrec.satnum,&classification, intldesg, &satrec.epochyr,
                       &satrec.epochdays,&satrec.ndot, &satrec.nddot, &nexp, &satrec.bstar,
                       &ibexp, &numb, &elnum );
       **/

       //memcpy( tempstr, &longstr1[0] , 2); tempstr[1] = '\0'; satrec.cardnumb = atoi(tempstr);
       memcpy( tempstr, &longstr1[2] , 5); tempstr[5] = '\0'; satrec.satnum = atol(tempstr);
       //classification = longstr1[7];
       //memcpy( intldesg, &longstr1[8] , 10); intldesg[10] = '\0';
       memcpy( tempstr, &longstr1[18] , 2); tempstr[2] = '\0'; satrec.epochyr = atoi(tempstr);
       memcpy( tempstr, &longstr1[20] , 12); tempstr[12] = '\0'; satrec.epochdays = atof(tempstr);
       memcpy( tempstr, &longstr1[32] , 11); tempstr[11] = '\0'; satrec.ndot = atof(tempstr);
       memcpy( tempstr, &longstr1[43] , 7); tempstr[7] = '\0'; satrec.nddot = atof(tempstr);
       memcpy( tempstr, &longstr1[50] , 2); tempstr[2] = '\0'; nexp = atoi(tempstr);
       memcpy( tempstr, &longstr1[52] , 7); tempstr[7] = '\0'; satrec.bstar = atof(tempstr);
       memcpy( tempstr, &longstr1[59] , 2); tempstr[2] = '\0'; ibexp = atoi(tempstr);
       //memcpy( tempstr, &longstr1[61] , 2); tempstr[2] = '\0'; numb = atoi(tempstr);
       //memcpy( tempstr, &longstr1[63] , 6); tempstr[6] = '\0'; elnum = atol(tempstr);

       memcpy( tempstr, &longstr2[2] , 5); tempstr[5] = '\0'; satrec.satnum = atol(tempstr);
       memcpy( tempstr, &longstr2[7] , 9); tempstr[9] = '\0'; satrec.inclo = atof(tempstr);
       memcpy( tempstr, &longstr2[16] , 9); tempstr[9] = '\0'; satrec.nodeo = atof(tempstr);
       memcpy( tempstr, &longstr2[25] , 8); tempstr[8] = '\0'; satrec.ecco = atof(tempstr);
       memcpy( tempstr, &longstr2[33] , 9); tempstr[9] = '\0'; satrec.argpo = atof(tempstr);
       memcpy( tempstr, &longstr2[42] , 9); tempstr[9] = '\0'; satrec.mo = atof(tempstr);
       memcpy( tempstr, &longstr2[51] , 11); tempstr[10] = '\0'; satrec.no = atof(tempstr);
       //memcpy( tempstr, &longstr2[63] , 6); tempstr[6] = '\0'; revnum = atol(tempstr);

       // ---- find no, ndot, nddot ----
       satrec.no   = satrec.no / xpdotp; //* rad/min
       satrec.nddot= satrec.nddot * pow(10.0, nexp);
       satrec.bstar= satrec.bstar * pow(10.0, ibexp);

       // ---- convert to sgp4 units ----
       satrec.a    = pow( satrec.no*tumin , (-2.0/3.0) );
       satrec.ndot = satrec.ndot  / (xpdotp*1440.0);  //* ? * minperday
       satrec.nddot= satrec.nddot / (xpdotp*1440.0*1440);

       // ---- find standard orbital elements ----
       satrec.inclo = satrec.inclo  * deg2rad;
       satrec.nodeo = satrec.nodeo  * deg2rad;
       satrec.argpo = satrec.argpo  * deg2rad;
       satrec.mo    = satrec.mo     * deg2rad;

       satrec.alta = satrec.a*(1.0 + satrec.ecco) - 1.0;
       satrec.altp = satrec.a*(1.0 - satrec.ecco) - 1.0;

       // ----------------------------------------------------------------
       // find sgp4epoch time of element set
       // remember that sgp4 uses units of days from 0 jan 1950 (sgp4epoch)
       // and minutes from the epoch (time)
       // ----------------------------------------------------------------

       // ---------------- temp fix for years from 1957-2056 -------------------
       // --------- correct fix will occur when year is 4-digit in tle ---------
       if (satrec.epochyr < 57)
           year= satrec.epochyr + 2000;
         else
           year= satrec.epochyr + 1900;

       days2mdhms ( year,satrec.epochdays, mon,day,hr,minute,sec );
       jday( year,mon,day,hr,minute,sec, 0,false,satrec.jdsatepoch );

       /***
       // ---- input start stop times manually
       if ((typerun != 'v') && (typerun != 'c'))
         {
         // ------------- enter start/stop ymd hms values --------------------
           if (typeinput == 'e')
             {
               printf("input start prop year mon day hr min sec \n");
               // make sure there is no space at the end of the format specifiers in scanf!
               scanf( "%i %i %i %i %i %lf",&startyear, &startmon, &startday, &starthr, &startmin, &startsec);
               fflush(stdin);
               jday( startyear,startmon,startday,starthr,startmin,startsec, jdstart );

               printf("input stop prop year mon day hr min sec \n");
               scanf( "%i %i %i %i %i %lf",&stopyear, &stopmon, &stopday, &stophr, &stopmin, &stopsec);
               fflush(stdin);
               jday( stopyear,stopmon,stopday,stophr,stopmin,stopsec, jdstop );

               startmfe = (jdstart - satrec.jdsatepoch) * 1440.0;
               stopmfe  = (jdstop - satrec.jdsatepoch) * 1440.0;

               printf("input time step in minutes \n");
               scanf( "%lf",&deltamin );
             }
           // -------- enter start/stop year and days of year values -----------
           if (typeinput == 'd')
             {
               printf("input start year dayofyr \n");
               scanf( "%i %lf",&startyear, &startdayofyr );
               printf("input stop year dayofyr \n");
               scanf( "%i %lf",&stopyear, &stopdayofyr );

               days2mdhms ( startyear,startdayofyr, mon,day,hr,minute,sec );
               jday( startyear,mon,day,hr,minute,sec, jdstart );
               days2mdhms ( stopyear,stopdayofyr, mon,day,hr,minute,sec );
               jday( stopyear,mon,day,hr,minute,sec, jdstop );

               startmfe = (jdstart - satrec.jdsatepoch) * 1440.0;
               stopmfe  = (jdstop - satrec.jdsatepoch) * 1440.0;

               printf("input time step in minutes \n");
               scanf( "%lf",&deltamin );
             }
           // ------------------ enter start/stop mfe values -------------------
           if (typeinput == 'm')
             {
               printf("input start min from epoch \n");
               scanf( "%lf",&startmfe );
               printf("input stop min from epoch \n");
               scanf( "%lf",&stopmfe );
               printf("input time step in minutes \n");
               scanf( "%lf",&deltamin );
             }
         }
       ***/
       // ---------------- initialize the orbit at sgp4epoch -------------------
       sgp4init( whichconst, opsmode, satrec.satnum, satrec.jdsatepoch-2433281.5, satrec.bstar,
                 satrec.ecco, satrec.argpo, satrec.inclo, satrec.mo, satrec.no,
                 satrec.nodeo, satrec);
    } // end twoline2rv

/* -----------------------------------------------------------------------------
*
*                           function twolineChecksum
*
*  this function checks if the checksum on the end of the line is correct.
*
*  author        : Hopperpop                  6 mar 2016
*
*  inputs        :
*    longstr    -  line of the tle
*
*  outputs       :
*    bool        -  true if test was succesfull
*
*  references    :
*    https://en.wikipedia.org/wiki/Two-line_element_set
  --------------------------------------------------------------------------- */
bool twolineChecksum
     (
      const char      longstr[80]
     )
{

    unsigned int cks = 0;
    const char* checksumpointer = longstr + 68;

    for (const char* p = longstr; p < checksumpointer; p++){

        switch(*p){
            case 0:
              return false;
              break;
            case '-':
              cks++;
              break;
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
              cks += *p - '0';
              break;
        }

    }
    cks %= 10;   //modulo 10
    if (cks + '0' == *checksumpointer)
      return true;

    return false;
}
<|endoftext|>


<commit_before>/*
 * This is part of the FL library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2014 Jan Issac (jan.issac@gmail.com)
 * Copyright (c) 2014 Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 * Max-Planck Institute for Intelligent Systems, AMD Lab
 * University of Southern California, CLMC Lab
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file joint_process_model_iid.hpp
 * \date January 2015
 * \author Jan Issac (jan.issac@gmail.com)
 */

#ifndef FL__MODEL__PROCESS__JOINT_PROCESS_MODEL_IID_HPP
#define FL__MODEL__PROCESS__JOINT_PROCESS_MODEL_IID_HPP

#include <Eigen/Dense>

#include <utility>

#include <fl/util/traits.hpp>
#include <fl/util/meta.hpp>

#include <fl/model/process/process_model_interface.hpp>

namespace fl
{

// Forward declarations
template <typename ... Models> class JointProcessModel;

/**
 * Traits of JointProcessModel<MultipleOf<ProcessModel, Count>>
 */
template <
    typename ProcessModel,
    int Count
>
struct Traits<
           JointProcessModel<MultipleOf<ProcessModel, Count>>
        >
{
    enum : signed int { ModelCount = Count };

    typedef ProcessModel LocalProcessModel;
    typedef typename Traits<ProcessModel>::Scalar Scalar;
    typedef typename Traits<ProcessModel>::State LocalState;
    typedef typename Traits<ProcessModel>::Input LocalInput;
    typedef typename Traits<ProcessModel>::Noise LocalNoise;

    enum : signed int
    {
        StateDim = ExpandSizes<LocalState::SizeAtCompileTime, Count>::Size,
        NoiseDim = ExpandSizes<LocalNoise::SizeAtCompileTime, Count>::Size,
        InputDim = ExpandSizes<LocalInput::SizeAtCompileTime, Count>::Size
    };

    typedef Eigen::Matrix<Scalar, StateDim, 1> State;
    typedef Eigen::Matrix<Scalar, NoiseDim, 1> Noise;
    typedef Eigen::Matrix<Scalar, InputDim, 1> Input;

    typedef ProcessModelInterface<
                State,
                Noise,
                Input
            > ProcessModelBase;
};

/**
 * \ingroup process_models
 */
template <
    typename LocalProcessModel,
    int Count
>
class JointProcessModel<MultipleOf<LocalProcessModel, Count>>
    : public Traits<
                 JointProcessModel<MultipleOf<LocalProcessModel, Count>>
             >::ProcessModelBase
{
public:
    typedef JointProcessModel<MultipleOf<LocalProcessModel, Count>> This;

    typedef typename Traits<This>::State State;
    typedef typename Traits<This>::Noise Noise;
    typedef typename Traits<This>::Input Input;

public:
    JointProcessModel(LocalProcessModel&& local_process_model,
                      int count = ToDimension<Count>())
        : local_process_model_(std::move(local_process_model)),
          count_(count)
    {
        assert(count_ > 0);
    }

    JointProcessModel(const LocalProcessModel& local_process_model,
                      int count = ToDimension<Count>())
        : local_process_model_(local_process_model),
          count_(count)
    {
        assert(count_ > 0);
    }

    JointProcessModel(MultipleOf<LocalProcessModel, Count>&& mof)
        : local_process_model_(mof.instance),
          count_(mof.count)
    {
        assert(count_ > 0);
    }

    JointProcessModel(const MultipleOf<LocalProcessModel, Count>& mof)
        : local_process_model_(mof.instance),
          count_(mof.count)
    {
        assert(count_ > 0);
    }

    ~JointProcessModel() { }

    virtual State predict_state(double delta_time,
                                const State& state,
                                const Noise& noise,
                                const Input& input)
    {
        State x = State::Zero(state_dimension(), 1);

        int state_dim = local_process_model_.state_dimension();
        int noise_dim = local_process_model_.noise_dimension();
        int input_dim = local_process_model_.input_dimension();

        for (int i = 0; i < count_; ++i)
        {
            x.middleRows(i * state_dim, state_dim) =
                local_process_model_.predict_state(
                    delta_time,
                    state.middleRows(i * state_dim, state_dim),
                    noise.middleRows(i * noise_dim, noise_dim),
                    input.middleRows(i * input_dim, input_dim));
        }

        return x;
    }

    virtual int state_dimension() const
    {
        return local_process_model_.state_dimension() * count_;
    }

    virtual int noise_dimension() const
    {
        return local_process_model_.noise_dimension() * count_;
    }

    virtual int input_dimension() const
    {
        return local_process_model_.input_dimension() * count_;
    }

    LocalProcessModel& local_process_model()
    {
        return local_process_model_;
    }

protected:
    LocalProcessModel local_process_model_;
    int count_;
};

}

#endif

<commit_msg>Minor update<commit_after>/*
 * This is part of the FL library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2014 Jan Issac (jan.issac@gmail.com)
 * Copyright (c) 2014 Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 * Max-Planck Institute for Intelligent Systems, AMD Lab
 * University of Southern California, CLMC Lab
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file joint_process_model_iid.hpp
 * \date January 2015
 * \author Jan Issac (jan.issac@gmail.com)
 */

#ifndef FL__MODEL__PROCESS__JOINT_PROCESS_MODEL_IID_HPP
#define FL__MODEL__PROCESS__JOINT_PROCESS_MODEL_IID_HPP

#include <Eigen/Dense>

#include <utility>

#include <fl/util/traits.hpp>
#include <fl/util/meta.hpp>

#include <fl/model/process/process_model_interface.hpp>

namespace fl
{

// Forward declarations
template <typename ... Models> class JointProcessModel;

/**
 * Traits of JointProcessModel<MultipleOf<ProcessModel, Count>>
 */
template <
    typename ProcessModel,
    int Count
>
struct Traits<
           JointProcessModel<MultipleOf<ProcessModel, Count>>
        >
{
    enum : signed int { ModelCount = Count };

    typedef ProcessModel LocalProcessModel;
    typedef typename Traits<ProcessModel>::Scalar Scalar;
    typedef typename Traits<ProcessModel>::State LocalState;
    typedef typename Traits<ProcessModel>::Input LocalInput;
    typedef typename Traits<ProcessModel>::Noise LocalNoise;

    enum : signed int
    {
        StateDim = ExpandSizes<LocalState::SizeAtCompileTime, Count>::Size,
        NoiseDim = ExpandSizes<LocalNoise::SizeAtCompileTime, Count>::Size,
        InputDim = ExpandSizes<LocalInput::SizeAtCompileTime, Count>::Size
    };

    typedef Eigen::Matrix<Scalar, StateDim, 1> State;
    typedef Eigen::Matrix<Scalar, NoiseDim, 1> Noise;
    typedef Eigen::Matrix<Scalar, InputDim, 1> Input;

    typedef ProcessModelInterface<
                State,
                Noise,
                Input
            > ProcessModelBase;
};

/**
 * \ingroup process_models
 */
template <
    typename LocalProcessModel,
    int Count
>
class JointProcessModel<MultipleOf<LocalProcessModel, Count>>
    : public Traits<
                 JointProcessModel<MultipleOf<LocalProcessModel, Count>>
             >::ProcessModelBase
{
public:
    typedef JointProcessModel This;

    typedef typename Traits<This>::State State;
    typedef typename Traits<This>::Noise Noise;
    typedef typename Traits<This>::Input Input;

public:
    JointProcessModel(LocalProcessModel&& local_process_model,
                      int count = ToDimension<Count>())
        : local_process_model_(std::move(local_process_model)),
          count_(count)
    {
        assert(count_ > 0);
    }

    JointProcessModel(const LocalProcessModel& local_process_model,
                      int count = ToDimension<Count>())
        : local_process_model_(local_process_model),
          count_(count)
    {
        assert(count_ > 0);
    }

    JointProcessModel(MultipleOf<LocalProcessModel, Count>&& mof)
        : local_process_model_(mof.instance),
          count_(mof.count)
    {
        assert(count_ > 0);
    }

    JointProcessModel(const MultipleOf<LocalProcessModel, Count>& mof)
        : local_process_model_(mof.instance),
          count_(mof.count)
    {
        assert(count_ > 0);
    }

    ~JointProcessModel() { }

    virtual State predict_state(double delta_time,
                                const State& state,
                                const Noise& noise,
                                const Input& input)
    {
        State x = State::Zero(state_dimension(), 1);

        int state_dim = local_process_model_.state_dimension();
        int noise_dim = local_process_model_.noise_dimension();
        int input_dim = local_process_model_.input_dimension();

        for (int i = 0; i < count_; ++i)
        {
            x.middleRows(i * state_dim, state_dim) =
                local_process_model_.predict_state(
                    delta_time,
                    state.middleRows(i * state_dim, state_dim),
                    noise.middleRows(i * noise_dim, noise_dim),
                    input.middleRows(i * input_dim, input_dim));
        }

        return x;
    }

    virtual int state_dimension() const
    {
        return local_process_model_.state_dimension() * count_;
    }

    virtual int noise_dimension() const
    {
        return local_process_model_.noise_dimension() * count_;
    }

    virtual int input_dimension() const
    {
        return local_process_model_.input_dimension() * count_;
    }

    LocalProcessModel& local_process_model()
    {
        return local_process_model_;
    }

protected:
    LocalProcessModel local_process_model_;
    int count_;
};

}

#endif

<|endoftext|>