<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $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 "camerabinimagecapture.h"
#include "camerabincapturedestination.h"
#include "camerabincapturebufferformat.h"
#include "camerabinsession.h"
#include "qgstvideobuffer.h"
#include "qvideosurfacegstsink.h"
#include "qgstutils.h"
#include <QtCore/QDebug>

//#define DEBUG_CAPTURE

#ifdef Q_WS_MAEMO_5
#define IMAGE_DONE_SIGNAL "img-done"
#else
#define IMAGE_DONE_SIGNAL "image-done"
#endif

Q_DECLARE_METATYPE(QVideoFrame)
Q_DECLARE_METATYPE(QtMultimediaKit::MetaData)

namespace
{
class CameraRegisterMetaTypes
{
public:
    CameraRegisterMetaTypes()
    {
        qRegisterMetaType<QVideoFrame>("QVideoFrame");
        qRegisterMetaType<QtMultimediaKit::MetaData>("QtMultimediaKit::MetaData");
    }
} _registerCameraMetaTypes;
}

CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
    :QCameraImageCaptureControl(session)
    , m_session(session)
    , m_ready(false)
    , m_requestId(0)
    , m_jpegEncoderElement(0)
    , m_metadataMuxerElement(0)
{
    connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState()));
    connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
    connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
    connect(m_session, SIGNAL(busMessage(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage)));

    g_signal_connect(G_OBJECT(m_session->cameraBin()), IMAGE_DONE_SIGNAL, G_CALLBACK(handleImageSaved), this);
}

CameraBinImageCapture::~CameraBinImageCapture()
{
}

bool CameraBinImageCapture::isReadyForCapture() const
{
    return m_ready;
}

int CameraBinImageCapture::capture(const QString &fileName)
{
    m_requestId++;
#ifdef DEBUG_CAPTURE
    qDebug() << Q_FUNC_INFO << requestId << fileName;
#endif
    m_session->captureImage(m_requestId, fileName);
    return m_requestId;
}

void CameraBinImageCapture::cancelCapture()
{
}

void CameraBinImageCapture::updateState()
{
    bool ready = m_session->state() == QCamera::ActiveState;
    if (m_ready != ready) {
#ifdef DEBUG_CAPTURE
        qDebug() << "readyForCaptureChanged" << ready;
#endif
        emit readyForCaptureChanged(m_ready = ready);
    }
}

gboolean CameraBinImageCapture::handleImageSaved(GstElement *camera,
                                                 const gchar *filename,
                                                 CameraBinImageCapture *self)
{
#if DEBUG_CAPTURE
    qDebug() << "Image saved" << filename;
#endif

    Q_UNUSED(camera);

    if (self->m_session->captureDestinationControl()->captureDestination() & QCameraImageCapture::CaptureToFile) {
        QMetaObject::invokeMethod(self, "imageSaved",
                                  Qt::QueuedConnection,
                                  Q_ARG(int, self->m_requestId),
                                  Q_ARG(QString, QString::fromUtf8(filename)));
    } else {
#ifdef DEBUG_CAPTURE
        qDebug() << Q_FUNC_INFO << "Dropped saving file" << filename;
#endif
        //camerabin creates an empty file when captured buffer is dropped,
        //let's remove it
        QFileInfo info(QString::fromUtf8(filename));
        if (info.isFile() &&
                info.filePath().startsWith("/home") &&
                info.size() == 0) {
            QFile(info.absoluteFilePath()).remove();
        }
    }
    return true;
}

gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self)
{
    Q_UNUSED(pad);
    CameraBinSession *session = self->m_session;

#ifdef DEBUG_CAPTURE
    qDebug() << "Uncompressed buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer));
#endif

    QCameraImageCapture::CaptureDestinations destination =
            session->captureDestinationControl()->captureDestination();
    QVideoFrame::PixelFormat format = session->captureBufferFormatControl()->bufferFormat();

    if (destination & QCameraImageCapture::CaptureToBuffer) {
        if (format != QVideoFrame::Format_Jpeg) {
            GstCaps *caps = GST_BUFFER_CAPS(buffer);
            int bytesPerLine = -1;
            QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine);
#ifdef DEBUG_CAPTURE
            qDebug() << "imageAvailable(uncompressed):" << format;
#endif
            QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, bytesPerLine);

            QVideoFrame frame(videoBuffer,
                              format.frameSize(),
                              format.pixelFormat());

            QMetaObject::invokeMethod(self, "imageAvailable",
                                      Qt::QueuedConnection,
                                      Q_ARG(int, self->m_requestId),
                                      Q_ARG(QVideoFrame, frame));
        }
    }

    //keep the buffer if capture to file or jpeg buffer capture was reuqsted
    bool keepBuffer = (destination & QCameraImageCapture::CaptureToFile) ||
            ((destination & QCameraImageCapture::CaptureToBuffer) &&
              format == QVideoFrame::Format_Jpeg);

    return keepBuffer;
}

gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self)
{
    Q_UNUSED(pad);
    CameraBinSession *session = self->m_session;

#ifdef DEBUG_CAPTURE
    qDebug() << "Jpeg buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer));
#endif

    QCameraImageCapture::CaptureDestinations destination =
            session->captureDestinationControl()->captureDestination();

    if ((destination & QCameraImageCapture::CaptureToBuffer) &&
         session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) {
        QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer,
                                                           -1); //bytesPerLine is not available for jpegs
        QVideoFrame frame(videoBuffer,
                          QGstUtils::capsCorrectedResolution(GST_BUFFER_CAPS(buffer)),
                          QVideoFrame::Format_Jpeg);

        QMetaObject::invokeMethod(self, "imageAvailable",
                                  Qt::QueuedConnection,
                                  Q_ARG(int, self->m_requestId),
                                  Q_ARG(QVideoFrame, frame));
    }

    //drop the buffer if capture to file was disabled
    return destination & QCameraImageCapture::CaptureToFile;
}

void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message)
{
    //Install buffer probes

    //The image capture pipiline is built dynamically,
    //it's necessary to wait until jpeg encoder is added to pipeline

    GstMessage *gm = message.rawMessage();
    if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) {
        GstState    oldState;
        GstState    newState;
        GstState    pending;
        gst_message_parse_state_changed(gm, &oldState, &newState, &pending);

        if (newState == GST_STATE_READY) {
            GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm));
            if (!element)
                return;

            QString elementName = QString::fromLatin1(gst_element_get_name(element));
            if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) {
                m_jpegEncoderElement = element;
                GstPad *sinkpad = gst_element_get_static_pad(element, "sink");

#ifdef DEBUG_CAPTURE
                qDebug() << "install uncompressed buffer probe";
#endif
                gst_pad_add_buffer_probe(sinkpad,
                                         G_CALLBACK(CameraBinImageCapture::uncompressedBufferProbe),
                                         this);

                gst_object_unref(sinkpad);
            } else if (elementName.contains("jifmux") && element != m_metadataMuxerElement) {
                //Jpeg encoded buffer probe is added after jifmux
                //element to ensure the resulting jpeg buffer contains capture metadata
                m_metadataMuxerElement = element;

                GstPad *srcpad = gst_element_get_static_pad(element, "src");
#ifdef DEBUG_CAPTURE
                qDebug() << "install jpeg buffer probe";
#endif
                gst_pad_add_buffer_probe(srcpad,
                                         G_CALLBACK(CameraBinImageCapture::jpegBufferProbe),
                                         this);
                gst_object_unref(srcpad);
            }
        }
    }
}
<commit_msg>Camera buffer capture fixes on camerabin/desktop<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $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 "camerabinimagecapture.h"
#include "camerabincapturedestination.h"
#include "camerabincapturebufferformat.h"
#include "camerabinsession.h"
#include "qgstvideobuffer.h"
#include "qvideosurfacegstsink.h"
#include "qgstutils.h"
#include <QtCore/qdebug.h>
#include <QtCore/qbuffer.h>
#include <QtGui/qimagereader.h>

//#define DEBUG_CAPTURE

#ifdef Q_WS_MAEMO_5
#define IMAGE_DONE_SIGNAL "img-done"
#else
#define IMAGE_DONE_SIGNAL "image-done"
#endif

Q_DECLARE_METATYPE(QVideoFrame)
Q_DECLARE_METATYPE(QtMultimediaKit::MetaData)

namespace
{
class CameraRegisterMetaTypes
{
public:
    CameraRegisterMetaTypes()
    {
        qRegisterMetaType<QVideoFrame>("QVideoFrame");
        qRegisterMetaType<QtMultimediaKit::MetaData>("QtMultimediaKit::MetaData");
    }
} _registerCameraMetaTypes;
}

CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
    :QCameraImageCaptureControl(session)
    , m_session(session)
    , m_ready(false)
    , m_requestId(0)
    , m_jpegEncoderElement(0)
    , m_metadataMuxerElement(0)
{
    connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState()));
    connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
    connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
    connect(m_session, SIGNAL(busMessage(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage)));

    g_signal_connect(G_OBJECT(m_session->cameraBin()), IMAGE_DONE_SIGNAL, G_CALLBACK(handleImageSaved), this);
}

CameraBinImageCapture::~CameraBinImageCapture()
{
}

bool CameraBinImageCapture::isReadyForCapture() const
{
    return m_ready;
}

int CameraBinImageCapture::capture(const QString &fileName)
{
    m_requestId++;
#ifdef DEBUG_CAPTURE
    qDebug() << Q_FUNC_INFO << m_requestId << fileName;
#endif
    m_session->captureImage(m_requestId, fileName);
    return m_requestId;
}

void CameraBinImageCapture::cancelCapture()
{
}

void CameraBinImageCapture::updateState()
{
    bool ready = m_session->state() == QCamera::ActiveState;
    if (m_ready != ready) {
#ifdef DEBUG_CAPTURE
        qDebug() << "readyForCaptureChanged" << ready;
#endif
        emit readyForCaptureChanged(m_ready = ready);
    }
}

gboolean CameraBinImageCapture::handleImageSaved(GstElement *camera,
                                                 const gchar *filename,
                                                 CameraBinImageCapture *self)
{
#ifdef DEBUG_CAPTURE
    qDebug() << "Image saved" << filename;
#endif

    Q_UNUSED(camera);

    if (self->m_session->captureDestinationControl()->captureDestination() & QCameraImageCapture::CaptureToFile) {
        QMetaObject::invokeMethod(self, "imageSaved",
                                  Qt::QueuedConnection,
                                  Q_ARG(int, self->m_requestId),
                                  Q_ARG(QString, QString::fromUtf8(filename)));
    } else {
#ifdef DEBUG_CAPTURE
        qDebug() << Q_FUNC_INFO << "Dropped saving file" << filename;
#endif
        //camerabin creates an empty file when captured buffer is dropped,
        //let's remove it
        QFileInfo info(QString::fromUtf8(filename));
        if (info.isFile() &&
                info.filePath().startsWith("/home") &&
                info.size() == 0) {
            QFile(info.absoluteFilePath()).remove();
        }
    }
    return true;
}

gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self)
{
    Q_UNUSED(pad);
    CameraBinSession *session = self->m_session;

#ifdef DEBUG_CAPTURE
    qDebug() << "Uncompressed buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer));
#endif

    QCameraImageCapture::CaptureDestinations destination =
            session->captureDestinationControl()->captureDestination();
    QVideoFrame::PixelFormat format = session->captureBufferFormatControl()->bufferFormat();

    if (destination & QCameraImageCapture::CaptureToBuffer) {
        if (format != QVideoFrame::Format_Jpeg) {
            GstCaps *caps = GST_BUFFER_CAPS(buffer);
            int bytesPerLine = -1;
            QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine);
#ifdef DEBUG_CAPTURE
            qDebug() << "imageAvailable(uncompressed):" << format;
#endif
            QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, bytesPerLine);

            QVideoFrame frame(videoBuffer,
                              format.frameSize(),
                              format.pixelFormat());

            QMetaObject::invokeMethod(self, "imageAvailable",
                                      Qt::QueuedConnection,
                                      Q_ARG(int, self->m_requestId),
                                      Q_ARG(QVideoFrame, frame));
        }
    }

    //keep the buffer if capture to file or jpeg buffer capture was reuqsted
    bool keepBuffer = (destination & QCameraImageCapture::CaptureToFile) ||
            ((destination & QCameraImageCapture::CaptureToBuffer) &&
              format == QVideoFrame::Format_Jpeg);

    return keepBuffer;
}

gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self)
{
    Q_UNUSED(pad);
    CameraBinSession *session = self->m_session;

#ifdef DEBUG_CAPTURE
    qDebug() << "Jpeg buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer));
#endif

    QCameraImageCapture::CaptureDestinations destination =
            session->captureDestinationControl()->captureDestination();

    if ((destination & QCameraImageCapture::CaptureToBuffer) &&
         session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) {
        QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer,
                                                           -1); //bytesPerLine is not available for jpegs

        QSize resolution = QGstUtils::capsCorrectedResolution(GST_BUFFER_CAPS(buffer));
        //if resolution is not presented in caps, try to find it from encoded jpeg data:
        if (resolution.isEmpty()) {
            QBuffer data;
            data.setData(reinterpret_cast<const char*>(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer));
            QImageReader reader(&data, "JPEG");
            resolution = reader.size();
        }

        QVideoFrame frame(videoBuffer,
                          resolution,
                          QVideoFrame::Format_Jpeg);

        QMetaObject::invokeMethod(self, "imageAvailable",
                                  Qt::QueuedConnection,
                                  Q_ARG(int, self->m_requestId),
                                  Q_ARG(QVideoFrame, frame));
    }

    //drop the buffer if capture to file was disabled
    return destination & QCameraImageCapture::CaptureToFile;
}

void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message)
{
    //Install buffer probes

    //The image capture pipiline is built dynamically,
    //it's necessary to wait until jpeg encoder is added to pipeline

    GstMessage *gm = message.rawMessage();
    if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) {
        GstState    oldState;
        GstState    newState;
        GstState    pending;
        gst_message_parse_state_changed(gm, &oldState, &newState, &pending);

        if (newState == GST_STATE_READY) {
            GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm));
            if (!element)
                return;

            QString elementName = QString::fromLatin1(gst_element_get_name(element));
            if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) {
                m_jpegEncoderElement = element;
                GstPad *sinkpad = gst_element_get_static_pad(element, "sink");

#ifdef DEBUG_CAPTURE
                qDebug() << "install uncompressed buffer probe";
#endif
                gst_pad_add_buffer_probe(sinkpad,
                                         G_CALLBACK(CameraBinImageCapture::uncompressedBufferProbe),
                                         this);

                gst_object_unref(sinkpad);
            } else if ((elementName.contains("jifmux") || elementName.startsWith("metadatamux"))
                       && element != m_metadataMuxerElement) {
                //Jpeg encoded buffer probe is added after jifmux/metadatamux
                //element to ensure the resulting jpeg buffer contains capture metadata
                m_metadataMuxerElement = element;

                GstPad *srcpad = gst_element_get_static_pad(element, "src");
#ifdef DEBUG_CAPTURE
                qDebug() << "install jpeg buffer probe";
#endif
                gst_pad_add_buffer_probe(srcpad,
                                         G_CALLBACK(CameraBinImageCapture::jpegBufferProbe),
                                         this);
                gst_object_unref(srcpad);
            }
        }
    }
}
<|endoftext|>


<commit_before>#include "Runtime/World/CScriptBallTrigger.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/World/CMorphBall.hpp"
#include "Runtime/World/CPlayer.hpp"

#include "DNAMP1/Tweaks/CTweakPlayer.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace urde {

static zeus::CAABox calculate_ball_aabox() {
  const float extent = 0.33f * g_tweakPlayer->GetPlayerBallHalfExtent();
  return {-extent, extent};
}

CScriptBallTrigger::CScriptBallTrigger(TUniqueId uid, std::string_view name, const CEntityInfo& info,
                                       const zeus::CVector3f& pos, const zeus::CVector3f& scale, bool active, float f1,
                                       float f2, float f3, const zeus::CVector3f& vec, bool b2)
: CScriptTrigger(uid, name, info, pos, calculate_ball_aabox(), CDamageInfo(CWeaponMode::Power(), 0.f, 0.f, 0.f),
                 zeus::skZero3f, ETriggerFlags::DetectMorphedPlayer, active, false, false)
, x150_force(f1)
, x154_minAngle(f2)
, x158_maxDistance(f3)
, x168_25_stopPlayer(b2) {

  if (vec.canBeNormalized()) {
    x15c_forceAngle = vec.normalized();
  }
}

void CScriptBallTrigger::Accept(IVisitor& visitor) { visitor.Visit(this); }

void CScriptBallTrigger::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
  if (msg == EScriptObjectMessage::Deactivate && GetActive()) {
    mgr.GetPlayer().GetMorphBall()->SetBallBoostState(CMorphBall::EBallBoostState::BoostAvailable);
    x168_24_canApplyForce = false;
  }

  CScriptTrigger::AcceptScriptMsg(msg, uid, mgr);
}

void CScriptBallTrigger::Think(float dt, CStateManager& mgr) {
  if (!GetActive()) {
    return;
  }

  CScriptTrigger::Think(dt, mgr);
  CPlayer& player = mgr.GetPlayer();

  if (player.GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) {
    x168_24_canApplyForce = false;
    return;
  }

  const float ballRadius = player.GetMorphBall()->GetBallRadius();
  const zeus::CVector3f radiusPosDif =
      (player.GetTranslation() - (player.GetTranslation() + zeus::CVector3f{0.f, 0.f, ballRadius}));
  const float distance = radiusPosDif.magnitude();

  if (!x168_24_canApplyForce) {
    if (distance < ballRadius) {
      x168_24_canApplyForce = true;
    } else {
      const zeus::CVector3f offset = radiusPosDif.normalized();
      if (std::cos(zeus::degToRad(x154_minAngle)) < (-offset).dot(x15c_forceAngle) && distance < x158_maxDistance) {
        const float force = zeus::min((1.f / dt * distance), x150_force * (distance * distance));
        player.ApplyForceWR(force * (player.GetMass() * offset), zeus::CAxisAngle());
      }
    }
  }

  if (x148_28_playerTriggerProc) {
    const zeus::CVector3f offset = GetTranslation() - zeus::CVector3f(0.f, 0.f, ballRadius);
    if (x168_25_stopPlayer) {
      player.Stop();
    }
    player.MoveToWR(offset, dt);
  } else {
    x168_24_canApplyForce = false;
  }
}

void CScriptBallTrigger::InhabitantAdded(CActor& act, CStateManager& /*mgr*/) {
  if (const TCastToPtr<CPlayer> player = act) {
    player->GetMorphBall()->SetBallBoostState(CMorphBall::EBallBoostState::BoostDisabled);
  }
}

void CScriptBallTrigger::InhabitantExited(CActor& act, CStateManager&) {
  if (const TCastToPtr<CPlayer> player = act) {
    player->GetMorphBall()->SetBallBoostState(CMorphBall::EBallBoostState::BoostAvailable);
    x168_24_canApplyForce = false;
  }
}

} // namespace urde
<commit_msg>CScriptBallTrigger: Apply force fixes<commit_after>#include "Runtime/World/CScriptBallTrigger.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/World/CMorphBall.hpp"
#include "Runtime/World/CPlayer.hpp"

#include "DNAMP1/Tweaks/CTweakPlayer.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace urde {

static zeus::CAABox calculate_ball_aabox() {
  const float extent = 0.33f * g_tweakPlayer->GetPlayerBallHalfExtent();
  return {-extent, extent};
}

CScriptBallTrigger::CScriptBallTrigger(TUniqueId uid, std::string_view name, const CEntityInfo& info,
                                       const zeus::CVector3f& pos, const zeus::CVector3f& scale, bool active, float f1,
                                       float f2, float f3, const zeus::CVector3f& vec, bool b2)
: CScriptTrigger(uid, name, info, pos, calculate_ball_aabox(), CDamageInfo(CWeaponMode::Power(), 0.f, 0.f, 0.f),
                 zeus::skZero3f, ETriggerFlags::DetectMorphedPlayer, active, false, false)
, x150_force(f1)
, x154_minAngle(f2)
, x158_maxDistance(f3)
, x168_25_stopPlayer(b2) {

  if (vec.canBeNormalized()) {
    x15c_forceAngle = vec.normalized();
  }
}

void CScriptBallTrigger::Accept(IVisitor& visitor) { visitor.Visit(this); }

void CScriptBallTrigger::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
  if (msg == EScriptObjectMessage::Deactivate && GetActive()) {
    mgr.GetPlayer().GetMorphBall()->SetBallBoostState(CMorphBall::EBallBoostState::BoostAvailable);
    x168_24_canApplyForce = false;
  }

  CScriptTrigger::AcceptScriptMsg(msg, uid, mgr);
}

void CScriptBallTrigger::Think(float dt, CStateManager& mgr) {
  if (!GetActive()) {
    return;
  }

  CScriptTrigger::Think(dt, mgr);
  CPlayer& player = mgr.GetPlayer();

  if (player.GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) {
    x168_24_canApplyForce = false;
    return;
  }

  const float ballRadius = player.GetMorphBall()->GetBallRadius();
  const zeus::CVector3f radiusPosDif =
      GetTranslation() - (player.GetTranslation() + zeus::CVector3f{0.f, 0.f, ballRadius});
  const float distance = radiusPosDif.magnitude();

  if (!x168_24_canApplyForce) {
    if (distance < ballRadius) {
      x168_24_canApplyForce = true;
    } else {
      const zeus::CVector3f offset = radiusPosDif.normalized();
      if (std::cos(zeus::degToRad(x154_minAngle)) < (-offset).dot(x15c_forceAngle) && distance < x158_maxDistance) {
        const float force = zeus::min((1.f / dt * distance), x150_force * (distance * distance));
        player.ApplyForceWR(force * (player.GetMass() * offset), zeus::CAxisAngle());
      }
    }
  }

  if (x148_28_playerTriggerProc) {
    const zeus::CVector3f offset = GetTranslation() - zeus::CVector3f(0.f, 0.f, ballRadius);
    if (x168_25_stopPlayer) {
      player.Stop();
    }
    player.MoveToWR(offset, dt);
  }
}

void CScriptBallTrigger::InhabitantAdded(CActor& act, CStateManager& /*mgr*/) {
  if (const TCastToPtr<CPlayer> player = act) {
    player->GetMorphBall()->SetBallBoostState(CMorphBall::EBallBoostState::BoostDisabled);
  }
}

void CScriptBallTrigger::InhabitantExited(CActor& act, CStateManager&) {
  if (const TCastToPtr<CPlayer> player = act) {
    player->GetMorphBall()->SetBallBoostState(CMorphBall::EBallBoostState::BoostAvailable);
    x168_24_canApplyForce = false;
  }
}

} // namespace urde
<|endoftext|>


<commit_before>//   Copyright 2016-2020 Jean-Francois Poilpret
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

/*
 * This is a program to help test FastArduino I2C bus support implementation.
 * It tries to connect to a ghost device (no suchd evice on the bus) and check
 * that I2C status is correct in this situation.
 *
 * Wiring:
 * - on Arduino UNO:
 *   - A4 (PC4, SDA): connected to pullup resistor (10K-22K)
 *   - A5 (PC5, SCL): connected to pullup resistor (10K-22K)
 *   - direct USB access (traces output)
 */

#include <fastarduino/array.h>
#include <fastarduino/i2c_device.h>
#include <fastarduino/uart.h>
#include <fastarduino/i2c_debug.h>

// I2C Device specific constants go here
//======================================
static constexpr const i2c::I2CMode MODE = i2c::I2CMode::FAST;
static constexpr const uint8_t DEVICE_ADDRESS = 0x77 << 1;

static constexpr const uint8_t DEBUG_SIZE = 32;
using DEBUGGER = i2c::debug::I2CDebugStatusRecorder<DEBUG_SIZE, DEBUG_SIZE>;
using MANAGER = i2c::I2CSyncStatusDebugManager<MODE, DEBUGGER&, DEBUGGER&>;
#define DEBUG(OUT) debugger.trace(OUT, false)

// Define vectors we need in the example
REGISTER_UATX_ISR(0)

// UART for traces
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[OUTPUT_BUFFER_SIZE];

// Subclass I2CDevice to make protected methods available
class FakeDevice: public i2c::I2CDevice<MANAGER>
{
	// The following type aliases will be useful for declaring proper Futures and calling I2CDevice API
	using PARENT = i2c::I2CDevice<MANAGER>;
	template<typename T> using PROXY = typename PARENT::template PROXY<T>;
	template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;

public:
	FakeDevice(MANAGER& manager): PARENT{manager, DEVICE_ADDRESS, i2c::Mode<MODE>{}, true} {}

	class WriteRegister : public FUTURE<void, containers::array<uint8_t, 2>>
	{
		using PARENT = FUTURE<void, containers::array<uint8_t, 2>>;
	public:
		WriteRegister(uint8_t address, uint8_t value) : PARENT{{address, value}} {}
		WriteRegister(WriteRegister&&) = default;
		WriteRegister& operator=(WriteRegister&&) = default;
	};

	int write_register(PROXY<WriteRegister> future)
	{
		return this->launch_commands(future, {this->write()});
	}
	bool write_register(uint8_t address, uint8_t value)
	{
		WriteRegister future{address, value};
		if (write_register(PARENT::make_proxy(future)) != 0) return false;
		return (future.await() == future::FutureStatus::READY);
	}

	class ReadRegister : public FUTURE<uint8_t, uint8_t>
	{
		using PARENT = FUTURE<uint8_t, uint8_t>;
	public:
		ReadRegister(uint8_t address) : PARENT{address} {}
		ReadRegister(ReadRegister&&) = default;
		ReadRegister& operator=(ReadRegister&&) = default;
	};

	int read_register(PROXY<ReadRegister> future)
	{
		return this->launch_commands(future, {this->write(), this->read()});
	}
	bool read_register(uint8_t address, uint8_t& value)
	{
		ReadRegister future{address};
		if (read_register(PARENT::make_proxy(future)) != 0) return false;
		return future.get(value);
	}
};

using streams::endl;
using streams::dec;
using streams::hex;

int main()
{
	board::init();
	sei();

	serial::hard::UATX<board::USART::USART0> uart{output_buffer};
	streams::ostream out = uart.out();
	uart.begin(115200);
	out.width(2);
	out << streams::boolalpha;

	// Start TWI interface
	//====================
	DEBUGGER debugger{};
	MANAGER manager{debugger, debugger};
	manager.begin();
	out << F("I2C interface started") << endl;

	FakeDevice device{manager};

	// Simulate write register
	bool ok = device.write_register(0x35, 0x23);
	out << F("write_register() = ") << ok << endl;
	DEBUG(out);

	// Simulate read register
	uint8_t value = 0;
	ok = device.read_register(0x35, value);
	out << F("read_register() = ") << ok << endl;
	out << F("value = ") << hex << value << endl;
	DEBUG(out);

	// Stop TWI interface
	//===================
	manager.end();
	out << F("End") << endl;
}
<commit_msg>Add and improve example to check I2C API behavior when no device answers.<commit_after>//   Copyright 2016-2020 Jean-Francois Poilpret
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

/*
 * This is a program to help test FastArduino I2C bus support implementation.
 * It tries to connect to a ghost device (no suchd evice on the bus) and check
 * that I2C status is correct in this situation.
 *
 * Wiring:
 * - on Arduino UNO:
 *   - A4 (PC4, SDA): connected to pullup resistor (10K-22K)
 *   - A5 (PC5, SCL): connected to pullup resistor (10K-22K)
 *   - direct USB access (traces output)
 */

#include <fastarduino/array.h>
#include <fastarduino/i2c_handler.h>
#include <fastarduino/i2c_device.h>
#include <fastarduino/i2c_debug.h>
#include <fastarduino/i2c_status.h>

//TODO adapt to allow checks also on async manager!
//TODO allow disabling debug (may impact results?)
//TODO adapt to also support ATtinyX4 (useful for testing I2C API in this context)
#if defined(ARDUINO_UNO)
#define HARDWARE_UART 1
#include <fastarduino/uart.h>
static constexpr const board::USART UART = board::USART::USART0;
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
static constexpr uint8_t I2C_BUFFER_SIZE = 32;
static constexpr const uint8_t DEBUG_SIZE = 32;
// Define vectors we need in the example
REGISTER_UATX_ISR(0)
#elif defined(BREADBOARD_ATTINYX4)
#define HARDWARE_UART 0
#include <fastarduino/soft_uart.h>
static constexpr const board::DigitalPin TX = board::DigitalPin::D8_PB0;
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
static constexpr const uint8_t DEBUG_SIZE = 32;
#else
#error "Current target is not yet supported!"
#endif

// #define DEBUG_I2C
// #define FORCE_SYNC

#ifdef DEBUG_I2C
using DEBUGGER = i2c::debug::I2CDebugStatusRecorder<DEBUG_SIZE, DEBUG_SIZE>;
#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
using MANAGER = i2c::I2CAsyncStatusDebugManager<
	i2c::I2CMode::FAST, i2c::I2CErrorPolicy::CLEAR_ALL_COMMANDS, DEBUGGER&, DEBUGGER&>;
static MANAGER::I2CCOMMAND i2c_buffer[I2C_BUFFER_SIZE];
#	else
using MANAGER = i2c::I2CSyncStatusDebugManager<i2c::I2CMode::FAST, DEBUGGER&, DEBUGGER&>;
#	endif
#define DEBUG(OUT) debugger.trace(OUT, false)
#else
using DEBUGGER = i2c::status::I2CLatestStatusHolder;
#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
using MANAGER = i2c::I2CAsyncStatusManager<
	i2c::I2CMode::FAST, i2c::I2CErrorPolicy::CLEAR_ALL_COMMANDS, DEBUGGER&>;
static MANAGER::I2CCOMMAND i2c_buffer[I2C_BUFFER_SIZE];
#	else
using MANAGER = i2c::I2CSyncStatusManager<i2c::I2CMode::FAST, DEBUGGER&>;
#	endif
#define DEBUG(OUT) OUT << streams::hex << debugger.latest_status() << streams::endl
#endif

#if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
REGISTER_I2C_ISR(MANAGER)
#endif

// UART buffer for traces
static char output_buffer[OUTPUT_BUFFER_SIZE];

// Subclass I2CDevice to make protected methods available
static constexpr const uint8_t DEVICE_ADDRESS = 0x77 << 1;
class FakeDevice: public i2c::I2CDevice<MANAGER>
{
	// The following type aliases will be useful for declaring proper Futures and calling I2CDevice API
	using PARENT = i2c::I2CDevice<MANAGER>;
	template<typename T> using PROXY = typename PARENT::template PROXY<T>;
	template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;

public:
	FakeDevice(MANAGER& manager): PARENT{manager, DEVICE_ADDRESS, i2c::I2C_FAST, true} {}

	class WriteRegister : public FUTURE<void, containers::array<uint8_t, 2>>
	{
		using PARENT = FUTURE<void, containers::array<uint8_t, 2>>;
	public:
		WriteRegister(uint8_t address, uint8_t value) : PARENT{{address, value}} {}
		WriteRegister(WriteRegister&&) = default;
		WriteRegister& operator=(WriteRegister&&) = default;
	};

	int write_register(PROXY<WriteRegister> future)
	{
		return this->launch_commands(future, {this->write()});
	}
	bool write_register(uint8_t address, uint8_t value)
	{
		WriteRegister future{address, value};
		if (write_register(PARENT::make_proxy(future)) != 0) return false;
		return (future.await() == future::FutureStatus::READY);
	}

	class ReadRegister : public FUTURE<uint8_t, uint8_t>
	{
		using PARENT = FUTURE<uint8_t, uint8_t>;
	public:
		ReadRegister(uint8_t address) : PARENT{address} {}
		ReadRegister(ReadRegister&&) = default;
		ReadRegister& operator=(ReadRegister&&) = default;
	};

	int read_register(PROXY<ReadRegister> future)
	{
		return this->launch_commands(future, {this->write(), this->read()});
	}
	bool read_register(uint8_t address, uint8_t& value)
	{
		ReadRegister future{address};
		if (read_register(PARENT::make_proxy(future)) != 0) return false;
		return future.get(value);
	}
};

using streams::endl;
using streams::dec;
using streams::hex;

int main()
{
	board::init();
	sei();

#if HARDWARE_UART
	serial::hard::UATX<UART> uart{output_buffer};
#else
	serial::soft::UATX<TX> uart{output_buffer};
#endif
	streams::ostream out = uart.out();
	uart.begin(115200);
	out.width(2);
	out << streams::boolalpha;

	// Start TWI interface
	//====================
	DEBUGGER debugger{};
#if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
#	ifdef DEBUG_I2C
	MANAGER manager{i2c_buffer, debugger, debugger};
#	else
	MANAGER manager{i2c_buffer, debugger};
#	endif
#else
#	ifdef DEBUG_I2C
	MANAGER manager{debugger, debugger};
#	else
	MANAGER manager{debugger};
#	endif
#endif
	manager.begin();
	out << F("I2C interface started") << endl;

	FakeDevice device{manager};

	// Simulate write register
	bool ok = device.write_register(0x35, 0x23);
	out << F("write_register() = ") << ok << endl;
	DEBUG(out);

	// Simulate read register
	uint8_t value = 0;
	ok = device.read_register(0x35, value);
	out << F("read_register() = ") << ok << endl;
	out << F("value = ") << hex << value << endl;
	DEBUG(out);

	// Stop TWI interface
	//===================
	manager.end();
	out << F("End") << endl;
}
<|endoftext|>


<commit_before>/*
** Copyright 2015 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/

#include <cstdlib>
#include <QCoreApplication>
#include <QString>
#include <sstream>
#include "com/centreon/broker/json/json_writer.hh"
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/extcmd/command_request.hh"
#include "com/centreon/broker/extcmd/command_result.hh"
#include "com/centreon/broker/extcmd/json_command_parser.hh"
#include "com/centreon/broker/logging/logging.hh"

using namespace com::centreon::broker;
using namespace com::centreon::broker::extcmd;

/**
 *  Find a value or throw a exception.
 *
 *  @param[in] val  The key of the param to find.
 *  @param[in] it   A valid iterator.
 *
 *  @return  The value.
 */
std::string find_or_except(
              std::string const& val,
              json::json_iterator const& it) {
  json::json_iterator found = it.find_child(val);
  if (found.is_null())
    throw (exceptions::msg() << "couldn't find '" << val << "'");
  return (found.get_string());
}

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  Constructor.
 *
 *  @param[in,out] listener       Command listener.
 */
json_command_parser::json_command_parser(command_listener& listener)
  : command_parser(listener) {}

/**
 *  Destructor.
 */
json_command_parser::~json_command_parser() {}

/**
 *  Parse the string given in argument.
 *
 *  @param[in] buffer     The buffer to parse.
 *  @param[out] res       The command result.
 *  @param[out] request   The parsed command request. Can be null for none.
 *
 * @return  The number of characters parsed succesfully, 0 for none.
 */
unsigned int json_command_parser::parse(
               std::string const& buffer,
               command_result& res,
               misc::shared_ptr<command_request>& request) {
  res = command_result();
  request.clear();

  // Try to find a valid json snippet.
  size_t parsed = 0;
  size_t level = 0;
  try {
    for (parsed = 0; parsed < buffer.size(); ++parsed) {
      if (buffer[parsed] == '{') {
        ++level;
        break ;
      }
      else if (buffer[parsed] != ' ' && buffer[parsed] != '\n')
        throw (exceptions::msg() << "expected '{'");
    }
    // Didn't find opening '{'.
    if (level == 0)
      return (0);
    for (; parsed < buffer.size(); ++parsed) {
      if (buffer[parsed] == '{')
        ++level;
      else if (buffer[parsed] == '}')
        --level;
      if (level == 0)
        break ;
    }
    // Didn't find closing '}'
    if (parsed == buffer.size())
      return (0);

    // Found a (hopefully) valid json snippet. Try to parse it.
    _parser.parse(buffer.substr(0, parsed + 1));
    json::json_iterator it = _parser.begin();
    std::string command_type = find_or_except("command_type", it);
    if (command_type == "status") {
      res = _listener.command_status(
                        QString::fromStdString(find_or_except("command_id", it)));
    }
    else if (command_type == "execute") {
      request = misc::make_shared(new command_request);
      request->cmd = QString::fromStdString(find_or_except("command", it));
      request->destination_id =
        QString::fromStdString(find_or_except("broker_id", it)).toUInt();
      request->endp = QString::fromStdString(find_or_except("endpoint", it));
      request->with_partial_result
        = it.find_child("with_partial_result").get_bool();
      logging::debug(logging::high)
        << "command: sending request " << request->uuid << " ('" << request->cmd
        << "') to endpoint '" << request->endp
        << "' of Centreon Broker instance " << request->destination_id;
      _listener.write(request);
      res = _listener.command_status(request->uuid);
    }
    else {
      throw (exceptions::msg()
             << "invalid command type: expected 'execute' or 'status' ");
    }
  } catch (std::exception const& e) {
    // At this point, command request was not written to the command
    // listener, so it not necessary to write command result either.
    res.uuid = QString();
    res.code = -1;
    res.msg = e.what();
  }
  return (parsed);
}

/**
 *  Write a command result into a string.
 *
 *  @param[in] res  The command result.
 *
 *  @return         The string.
 */
std::string json_command_parser::write(command_result const& res) {
  json::json_writer writer;
  writer.open_object();
  writer.add_key("command_id");
  writer.add_string(res.uuid.toStdString());
  writer.add_key("command_code");
  writer.add_number(res.code);
  writer.add_key("command_output");
  writer.add_string(res.msg.toStdString());
  writer.close_object();
  return (writer.get_string());
}
<commit_msg>Extcmd: small fix.<commit_after>/*
** Copyright 2015 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/

#include <cstdlib>
#include <QCoreApplication>
#include <QString>
#include <sstream>
#include "com/centreon/broker/json/json_writer.hh"
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/extcmd/command_request.hh"
#include "com/centreon/broker/extcmd/command_result.hh"
#include "com/centreon/broker/extcmd/json_command_parser.hh"
#include "com/centreon/broker/logging/logging.hh"

using namespace com::centreon::broker;
using namespace com::centreon::broker::extcmd;

/**
 *  Find a value or throw a exception.
 *
 *  @param[in] val  The key of the param to find.
 *  @param[in] it   A valid iterator.
 *
 *  @return  The value.
 */
std::string find_or_except(
              std::string const& val,
              json::json_iterator const& it) {
  json::json_iterator found = it.find_child(val).enter_children();
  if (found.is_null())
    throw (exceptions::msg() << "couldn't find '" << val << "'");
  return (found.get_string());
}

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  Constructor.
 *
 *  @param[in,out] listener       Command listener.
 */
json_command_parser::json_command_parser(command_listener& listener)
  : command_parser(listener) {}

/**
 *  Destructor.
 */
json_command_parser::~json_command_parser() {}

/**
 *  Parse the string given in argument.
 *
 *  @param[in] buffer     The buffer to parse.
 *  @param[out] res       The command result.
 *  @param[out] request   The parsed command request. Can be null for none.
 *
 * @return  The number of characters parsed succesfully, 0 for none.
 */
unsigned int json_command_parser::parse(
               std::string const& buffer,
               command_result& res,
               misc::shared_ptr<command_request>& request) {
  res = command_result();
  request.clear();

  // Try to find a valid json snippet.
  size_t parsed = 0;
  size_t level = 0;
  try {
    for (parsed = 0; parsed < buffer.size(); ++parsed) {
      if (buffer[parsed] == '{') {
        ++level;
        break ;
      }
      else if (buffer[parsed] != ' ' && buffer[parsed] != '\n')
        throw (exceptions::msg() << "expected '{'");
    }
    // Didn't find opening '{'.
    if (level == 0)
      return (0);
    for (++parsed; parsed < buffer.size(); ++parsed) {
      if (buffer[parsed] == '{')
        ++level;
      else if (buffer[parsed] == '}')
        --level;
      if (level == 0)
        break ;
    }
    // Didn't find closing '}'
    if (level != 0)
      return (0);

    // Found a (hopefully) valid json snippet. Try to parse it.
    _parser.parse(buffer.substr(0, parsed + 1));
    json::json_iterator it = _parser.begin();
    std::string command_type = find_or_except("command_type", it);
    if (command_type == "status") {
      res = _listener.command_status(
                        QString::fromStdString(find_or_except("command_id", it)));
    }
    else if (command_type == "execute") {
      request = misc::make_shared(new command_request);
      request->cmd = QString::fromStdString(find_or_except("command", it));
      request->destination_id =
        QString::fromStdString(find_or_except("broker_id", it)).toUInt();
      request->endp = QString::fromStdString(find_or_except("endpoint", it));
      request->with_partial_result
        = it.find_child("with_partial_result").get_bool();
      logging::debug(logging::high)
        << "command: sending request " << request->uuid << " ('" << request->cmd
        << "') to endpoint '" << request->endp
        << "' of Centreon Broker instance " << request->destination_id;
      _listener.write(request);
      res = _listener.command_status(request->uuid);
    }
    else {
      throw (exceptions::msg()
             << "invalid command type: expected 'execute' or 'status' ");
    }
  } catch (std::exception const& e) {
    // At this point, command request was not written to the command
    // listener, so it not necessary to write command result either.
    res.uuid = QString();
    res.code = -1;
    res.msg = e.what();
  }
  return (parsed);
}

/**
 *  Write a command result into a string.
 *
 *  @param[in] res  The command result.
 *
 *  @return         The string.
 */
std::string json_command_parser::write(command_result const& res) {
  json::json_writer writer;
  writer.open_object();
  writer.add_key("command_id");
  writer.add_string(res.uuid.toStdString());
  writer.add_key("command_code");
  writer.add_number(res.code);
  writer.add_key("command_output");
  writer.add_string(res.msg.toStdString());
  writer.close_object();
  return (writer.get_string());
}
<|endoftext|>


<commit_before>template<typename T>
class SkipList {
    struct SkipListNode {
        const T& _data;
        SkipListNode _next[];

        SkipListNode(const T& data, const int level):
            _data(data),
            _next(new SkipListNode[level+1])
        { }
    };

    private:
        const int _maxLevel;
    SkipListNode _head;

    public:
    SkipList(const int maxLevel):
        _maxLevel(maxLevel)
    {
        _head = new SkipListNode(nullptr, _maxLevel);
        SkipListNode sentinel = new SkipListNode(nullptr, maxLevel);
        for (int i = 0; i <= maxLevel; ++i) {
            _head.next[i] = sentinel;
        }
    }

    T find(T key) {
        SkipListNode current = _head;

        for(int i = _maxLevel; i >= 0; --i) {
            SkipListNode next = current.next[i];
            while (next._data < key) {
                current = next;
                next = current._next[i];
            }

            current = current._next[0];
            if(current.data == key) return current.data;
            else return nullptr;
        }
    }

};
<commit_msg>[skip-list] Removing a redundant file.<commit_after><|endoftext|>


<commit_before>#include "mainwindow.h"
#include "pagefactory.h"
#include "dcppage.h"
#include <duideviceprofile.h>
#include <QtDebug>
#include "dcpappletpage.h"

MainWindow::MainWindow():m_CurrentPage(NULL)
{
   connect(PageFactory::instance(), SIGNAL(changePage(DcpPage*)),
        this, SLOT(appletChangePage(DcpPage*)));
   Pages::Handle handle = {Pages::MAIN, ""};
   changePage(handle);
}

void MainWindow::homeClicked()
{
    /*
   Pages::Handle handle = {Pages::MAIN, ""};
   changePage(handle);
*/
   onRotateClicked();
}

void MainWindow::backClicked()
{
    Q_ASSERT(m_CurrentPage);
    bool change = true;
    if (m_CurrentPage->handle().id == Pages::APPLET
        || m_CurrentPage->handle().id == Pages::APPLETFROMMOSTUSED)
         change = PageFactory::instance()->backFromApplet();
    if (change)
        changePage(m_CurrentPage->referer());
}

MainWindow::~MainWindow()
{
}

void
MainWindow::changePage(Pages::Handle handle)
{
    if (handle.id == Pages::NOPAGE)
        return;

    DcpPage* page = PageFactory::instance()->create(handle.id, handle.param);
    qDebug() << "XXXX changePage" << (void*) page;
    connect (page, SIGNAL(openSubPage(Pages::Handle)), this,
        SLOT(changePage(Pages::Handle)));
    connect(page, SIGNAL(backButtonClicked()), this, SLOT(backClicked()));
    page->appearNow(DuiSceneWindow::DestroyWhenDone);
    if (m_CurrentPage) {
        if (page->referer().id == Pages::NOPAGE)
            page->setReferer(m_CurrentPage->handle());
    }

    m_CurrentPage = page;
}

void MainWindow::onRotateClicked()
{
    DuiDeviceProfile *profile = DuiDeviceProfile::instance();

    if ( profile->orientation() == Dui::Portrait ) {
        qDebug() << "mode changes to Angle0";
        profile->setOrientationAngle (DuiDeviceProfile::Angle0);
    } else {
        qDebug() << "mode changes to Angle90";
        profile->setOrientationAngle (DuiDeviceProfile::Angle90);
    }
}

void
MainWindow::appletChangePage(DcpPage *page)
{
    Q_ASSERT(page);
    m_CurrentPage = page;
    qDebug() << "XXXX appletChangePage" << (void*) page;
    connect(page, SIGNAL(backButtonClicked()), this, SLOT(backClicked()));
    page->appearNow(DuiSceneWindow::DestroyWhenDone);
}

<commit_msg>Restored KeepWhenDone until we find solution for the segfault<commit_after>#include "mainwindow.h"
#include "pagefactory.h"
#include "dcppage.h"
#include <duideviceprofile.h>
#include <QtDebug>
#include "dcpappletpage.h"

MainWindow::MainWindow():m_CurrentPage(NULL)
{
   connect(PageFactory::instance(), SIGNAL(changePage(DcpPage*)),
        this, SLOT(appletChangePage(DcpPage*)));
   Pages::Handle handle = {Pages::MAIN, ""};
   changePage(handle);
}

void MainWindow::homeClicked()
{
    /*
   Pages::Handle handle = {Pages::MAIN, ""};
   changePage(handle);
*/
   onRotateClicked();
}

void MainWindow::backClicked()
{
    Q_ASSERT(m_CurrentPage);
    bool change = true;
    if (m_CurrentPage->handle().id == Pages::APPLET
        || m_CurrentPage->handle().id == Pages::APPLETFROMMOSTUSED)
         change = PageFactory::instance()->backFromApplet();
    if (change)
        changePage(m_CurrentPage->referer());
}

MainWindow::~MainWindow()
{
}

void
MainWindow::changePage(Pages::Handle handle)
{
    if (handle.id == Pages::NOPAGE)
        return;

    DcpPage* page = PageFactory::instance()->create(handle.id, handle.param);
    qDebug() << "XXXX changePage" << (void*) page;
    connect (page, SIGNAL(openSubPage(Pages::Handle)), this,
        SLOT(changePage(Pages::Handle)));
    connect(page, SIGNAL(backButtonClicked()), this, SLOT(backClicked()));
//    page->appearNow(DuiSceneWindow::DestroyWhenDone); TODO
    page->appearNow(DuiSceneWindow::KeepWhenDone);
    if (m_CurrentPage) {
        if (page->referer().id == Pages::NOPAGE)
            page->setReferer(m_CurrentPage->handle());
    }

    m_CurrentPage = page;
}

void MainWindow::onRotateClicked()
{
    DuiDeviceProfile *profile = DuiDeviceProfile::instance();

    if ( profile->orientation() == Dui::Portrait ) {
        qDebug() << "mode changes to Angle0";
        profile->setOrientationAngle (DuiDeviceProfile::Angle0);
    } else {
        qDebug() << "mode changes to Angle90";
        profile->setOrientationAngle (DuiDeviceProfile::Angle90);
    }
}

void
MainWindow::appletChangePage(DcpPage *page)
{
    Q_ASSERT(page);
    m_CurrentPage = page;
    qDebug() << "XXXX appletChangePage" << (void*) page;
    connect(page, SIGNAL(backButtonClicked()), this, SLOT(backClicked()));
//    page->appearNow(DuiSceneWindow::DestroyWhenDone); TODO
    page->appearNow(DuiSceneWindow::KeepWhenDone);
}

<|endoftext|>


<commit_before>/*
// $Id$
// Fennel is a relational database kernel.
// Copyright (C) 1999-2004 John V. Sichi.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either version 2.1
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "fennel/common/CommonPreamble.h"
#include "fennel/ftrs/FtrsTableWriterExecStream.h"
#include "fennel/ftrs/FtrsTableWriterFactory.h"
#include "fennel/txn/LogicalTxn.h"
#include "fennel/tuple/StandardTypeDescriptor.h"
#include "fennel/exec/ExecStreamBufAccessor.h"
#include "fennel/exec/ExecStreamGraph.h"

FENNEL_BEGIN_CPPFILE("$Id$");

FtrsTableWriterExecStream::FtrsTableWriterExecStream()
{
    pActionMutex = NULL;
    svptId = NULL_SVPT_ID;
}

void FtrsTableWriterExecStream::prepare(
    FtrsTableWriterExecStreamParams const &params)
{
    ConduitExecStream::prepare(params);
    pTableWriter = params.pTableWriterFactory->newTableWriter(params);
    actionType = params.actionType;
    pActionMutex = params.pActionMutex;
    assert(pActionMutex);
}

void FtrsTableWriterExecStream::getResourceRequirements(
    ExecStreamResourceQuantity &minQuantity,
    ExecStreamResourceQuantity &optQuantity)
{
    ConduitExecStream::getResourceRequirements(minQuantity,optQuantity);

    // REVIEW:  update/delete resources

    // This is to account for total number of pages needed to perform an
    // update on a single index.  Pages are only locked for the duration of
    // one index update, so they don't need to be charged per index (unless
    // we start parallelizing index updates).  REVIEW: determine the correct
    // number; 4 is just a guess.
    minQuantity.nCachePages += 4;

    // each BTreeWriter currently needs a private scratch page
    minQuantity.nCachePages += pTableWriter->getIndexCount();

    optQuantity = minQuantity;
}

void FtrsTableWriterExecStream::open(bool restart)
{
    ConduitExecStream::open(restart);
    assert(pGraph->getTxn());
    // REVIEW:  close/restart?
    pGraph->getTxn()->addParticipant(pTableWriter);
    nTuples = 0;
    pTableWriter->openIndexWriters();
    isDone = false;
}

ExecStreamResult FtrsTableWriterExecStream::execute(
    ExecStreamQuantum const &quantum)
{
    if (isDone) {
        // already returned final result
        pOutAccessor->markEOS();
        return EXECRC_EOS;
    }

    if (pInAccessor->getState() == EXECBUF_EOS) {
        // we've processed all input,  so commit what we've written
        // and return row count as our output
        commitSavepoint();
        // REVIEW: The format for a 1-tuple with a single non-null uint64_t
        // value is just the value itself (assuming alignment size no greater
        // than 64-bit), which is why this works.  But it would be cleaner to
        // set up a proper TupleAccessor.
        pOutAccessor->provideBufferForConsumption(
            reinterpret_cast<PConstBuffer>(&nTuples),
            reinterpret_cast<PConstBuffer>((&nTuples) + 1));
        isDone = true;
        return EXECRC_BUF_OVERFLOW;
    }

    ExecStreamResult rc = precheckConduitBuffers();
    if (rc != EXECRC_YIELD) {
        return rc;
    }

    if (svptId == NULL_SVPT_ID) {
        createSavepoint();
    }

    try {
        nTuples += pTableWriter->execute(
            quantum, *pInAccessor, actionType, *pActionMutex);
    } catch (...) {
        try {
            rollbackSavepoint();
        } catch (...) {
            // TODO:  trace failed rollback
        }
        throw;
    }

    if (!pInAccessor->isConsumptionPossible()) {
        return EXECRC_BUF_UNDERFLOW;
    } else {
        return EXECRC_QUANTUM_EXPIRED;
    }
}

ExecStreamBufProvision
    FtrsTableWriterExecStream::getOutputBufProvision() const
{
    return BUFPROV_PRODUCER;
}

void FtrsTableWriterExecStream::closeImpl()
{
    if (svptId != NULL_SVPT_ID) {
        rollbackSavepoint();
    }
    ConduitExecStream::closeImpl();
    if (pTableWriter) {
        pTableWriter->closeIndexWriters();
    }
}

void FtrsTableWriterExecStream::createSavepoint()
{
    // block checkpoints while creating savepoint
    SXMutexSharedGuard actionMutexGuard(*pActionMutex);
    svptId = pGraph->getTxn()->createSavepoint();
}

void FtrsTableWriterExecStream::commitSavepoint()
{
    SavepointId svptIdCopy = svptId;
    svptId = NULL_SVPT_ID;

    // block checkpoints while committing savepoint
    SXMutexSharedGuard actionMutexGuard(*pActionMutex);
    pGraph->getTxn()->commitSavepoint(svptIdCopy);
}

void FtrsTableWriterExecStream::rollbackSavepoint()
{
    SavepointId svptIdCopy = svptId;
    svptId = NULL_SVPT_ID;

    // block checkpoints while rolling back savepoint
    SXMutexSharedGuard actionMutexGuard(*pActionMutex);
    pGraph->getTxn()->rollback(&svptIdCopy);
    pGraph->getTxn()->commitSavepoint(svptIdCopy);
}

FENNEL_END_CPPFILE("$Id$");

// End FtrsTableWriterExecStream.cpp
<commit_msg>FENNEL:  fix savepoint bug with empty DML<commit_after>/*
// $Id$
// Fennel is a relational database kernel.
// Copyright (C) 1999-2004 John V. Sichi.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either version 2.1
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "fennel/common/CommonPreamble.h"
#include "fennel/ftrs/FtrsTableWriterExecStream.h"
#include "fennel/ftrs/FtrsTableWriterFactory.h"
#include "fennel/txn/LogicalTxn.h"
#include "fennel/tuple/StandardTypeDescriptor.h"
#include "fennel/exec/ExecStreamBufAccessor.h"
#include "fennel/exec/ExecStreamGraph.h"

FENNEL_BEGIN_CPPFILE("$Id$");

FtrsTableWriterExecStream::FtrsTableWriterExecStream()
{
    pActionMutex = NULL;
    svptId = NULL_SVPT_ID;
}

void FtrsTableWriterExecStream::prepare(
    FtrsTableWriterExecStreamParams const &params)
{
    ConduitExecStream::prepare(params);
    pTableWriter = params.pTableWriterFactory->newTableWriter(params);
    actionType = params.actionType;
    pActionMutex = params.pActionMutex;
    assert(pActionMutex);
}

void FtrsTableWriterExecStream::getResourceRequirements(
    ExecStreamResourceQuantity &minQuantity,
    ExecStreamResourceQuantity &optQuantity)
{
    ConduitExecStream::getResourceRequirements(minQuantity,optQuantity);

    // REVIEW:  update/delete resources

    // This is to account for total number of pages needed to perform an
    // update on a single index.  Pages are only locked for the duration of
    // one index update, so they don't need to be charged per index (unless
    // we start parallelizing index updates).  REVIEW: determine the correct
    // number; 4 is just a guess.
    minQuantity.nCachePages += 4;

    // each BTreeWriter currently needs a private scratch page
    minQuantity.nCachePages += pTableWriter->getIndexCount();

    optQuantity = minQuantity;
}

void FtrsTableWriterExecStream::open(bool restart)
{
    ConduitExecStream::open(restart);
    assert(pGraph->getTxn());
    // REVIEW:  close/restart?
    pGraph->getTxn()->addParticipant(pTableWriter);
    nTuples = 0;
    pTableWriter->openIndexWriters();
    isDone = false;
}

ExecStreamResult FtrsTableWriterExecStream::execute(
    ExecStreamQuantum const &quantum)
{
    if (isDone) {
        // already returned final result
        pOutAccessor->markEOS();
        return EXECRC_EOS;
    }

    if (pInAccessor->getState() == EXECBUF_EOS) {
        // we've processed all input,  so commit what we've written
        // and return row count as our output
        commitSavepoint();
        // REVIEW: The format for a 1-tuple with a single non-null uint64_t
        // value is just the value itself (assuming alignment size no greater
        // than 64-bit), which is why this works.  But it would be cleaner to
        // set up a proper TupleAccessor.
        pOutAccessor->provideBufferForConsumption(
            reinterpret_cast<PConstBuffer>(&nTuples),
            reinterpret_cast<PConstBuffer>((&nTuples) + 1));
        isDone = true;
        return EXECRC_BUF_OVERFLOW;
    }

    ExecStreamResult rc = precheckConduitBuffers();
    if (rc != EXECRC_YIELD) {
        return rc;
    }

    if (svptId == NULL_SVPT_ID) {
        createSavepoint();
    }

    try {
        nTuples += pTableWriter->execute(
            quantum, *pInAccessor, actionType, *pActionMutex);
    } catch (...) {
        try {
            rollbackSavepoint();
        } catch (...) {
            // TODO:  trace failed rollback
        }
        throw;
    }

    if (!pInAccessor->isConsumptionPossible()) {
        return EXECRC_BUF_UNDERFLOW;
    } else {
        return EXECRC_QUANTUM_EXPIRED;
    }
}

ExecStreamBufProvision
    FtrsTableWriterExecStream::getOutputBufProvision() const
{
    return BUFPROV_PRODUCER;
}

void FtrsTableWriterExecStream::closeImpl()
{
    if (svptId != NULL_SVPT_ID) {
        rollbackSavepoint();
    }
    ConduitExecStream::closeImpl();
    if (pTableWriter) {
        pTableWriter->closeIndexWriters();
    }
}

void FtrsTableWriterExecStream::createSavepoint()
{
    // block checkpoints while creating savepoint
    SXMutexSharedGuard actionMutexGuard(*pActionMutex);
    svptId = pGraph->getTxn()->createSavepoint();
}

void FtrsTableWriterExecStream::commitSavepoint()
{
    if (svptId == NULL_SVPT_ID) {
        return;
    }

    SavepointId svptIdCopy = svptId;
    svptId = NULL_SVPT_ID;

    // block checkpoints while committing savepoint
    SXMutexSharedGuard actionMutexGuard(*pActionMutex);
    pGraph->getTxn()->commitSavepoint(svptIdCopy);
}

void FtrsTableWriterExecStream::rollbackSavepoint()
{
    if (svptId == NULL_SVPT_ID) {
        return;
    }

    SavepointId svptIdCopy = svptId;
    svptId = NULL_SVPT_ID;

    // block checkpoints while rolling back savepoint
    SXMutexSharedGuard actionMutexGuard(*pActionMutex);
    pGraph->getTxn()->rollback(&svptIdCopy);
    pGraph->getTxn()->commitSavepoint(svptIdCopy);
}

FENNEL_END_CPPFILE("$Id$");

// End FtrsTableWriterExecStream.cpp
<|endoftext|>


<commit_before>
//snippet-sourcedescription:[get_item.cpp demonstrates how to retrieve an item from an Amazon DynamoDB table.]
//snippet-keyword:[C++]
//snippet-keyword:[Code Sample]
//snippet-keyword:[Amazon DynamoDB]
//snippet-service:[dynamodb]
//snippet-sourcetype:[full-example]
//snippet-sourcedate:[]
//snippet-sourceauthor:[AWS]

/*
Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.

This file is licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License. A copy of
the License is located at

http://aws.amazon.com/apache2.0/

This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
//snippet-start:[dynamodb.cpp.get_item.inc]
#include <aws/core/Aws.h>
#include <aws/core/utils/Outcome.h>
#include <aws/dynamodb/DynamoDBClient.h>
#include <aws/dynamodb/model/AttributeDefinition.h>
#include <aws/dynamodb/model/GetItemRequest.h>
#include <iostream>
//snippet-end:[dynamodb.cpp.get_item.inc]

/**
* Get an item from a DynamoDB table.
*
* Takes the name of the table and the name of the item to retrieve from it.
*
* The primary key searched is "Name", and the value contained by the field
* "Greeting" will be returned.
*
* This code expects that you have AWS credentials set up per:
* http://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html
*/
int main(int argc, char** argv)
{
    const std::string USAGE = "\n" \
        "Usage:\n"
        "    get_item <table> <name> [projection_expression]\n\n"
        "Where:\n"
        "    table - the table to get an item from.\n"
        "    name  - the item to get.\n\n"
        "You can add an optional projection expression (a quote-delimited,\n"
        "comma-separated list of attributes to retrieve) to limit the\n"
        "fields returned from the table.\n\n"
        "Example:\n"
        "    get_item HelloTable World\n"
        "    get_item SiteColors text \"default, bold\"\n";

    if (argc < 2)
    {
        std::cout << USAGE;
        return 1;
    }

    Aws::SDKOptions options;

    Aws::InitAPI(options);
    {
        const Aws::String table(argv[1]);
        const Aws::String name(argv[2]);
        const Aws::String projection(argc > 3 ? argv[3] : "");

        // snippet-start:[dynamodb.cpp.get_item.code]
        Aws::Client::ClientConfiguration clientConfig;
        Aws::DynamoDB::DynamoDBClient dynamoClient(clientConfig);

        Aws::DynamoDB::Model::GetItemRequest req;

        if (!projection.empty())
            req.SetProjectionExpression(projection);

        Aws::DynamoDB::Model::AttributeValue haskKey;
        haskKey.SetS(name);
        req.AddKey("Name", haskKey);

        req.SetTableName(table);

        const Aws::DynamoDB::Model::GetItemOutcome& result = dynamoClient.GetItem(req);
        if (result.IsSuccess())
        {
            const Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue>& item = result.GetResult().GetItem();
            if (item.size() > 0)
            {
                for (const auto& i : item)
                    std::cout << i.first << ": " << i.second.GetS() << std::endl;
            }
            else
            {
                std::cout << "No item found with the key " << name << std::endl;
            }

        }
        else
        {
            std::cout << "Failed to get item: " << result.GetError().GetMessage();
        }
        // snippet-end:[dynamodb.cpp.get_item.code]
    }
    Aws::ShutdownAPI(options);
    return 0;
}<commit_msg>Repair and polish C++ DynamoDB get_item<commit_after>
//snippet-sourcedescription:[get_item.cpp demonstrates how to retrieve an item from an Amazon DynamoDB table.]
//snippet-service:[dynamodb]
//snippet-keyword:[Amazon DynamoDB]
//snippet-keyword:[C++]
//snippet-keyword:[Code Sample]
//snippet-sourcetype:[full-example]
//snippet-sourcedate:[2019-04-09]
//snippet-sourceauthor:[AWS]

/*
Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.

This file is licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License. A copy of
the License is located at

http://aws.amazon.com/apache2.0/

This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/

//snippet-start:[dynamodb.cpp.get_item.inc]
#include <aws/core/Aws.h>
#include <aws/core/utils/Outcome.h>
#include <aws/dynamodb/DynamoDBClient.h>
#include <aws/dynamodb/model/AttributeDefinition.h>
#include <aws/dynamodb/model/GetItemRequest.h>
#include <iostream>
//snippet-end:[dynamodb.cpp.get_item.inc]

/**
* Get an item from a DynamoDB table.
*
* Takes the name of the table and the name of the item to retrieve from it.
*
* The primary key "Name" is searched. By default, all fields and values
* contained in the item are returned. If an optional projection expression is
* specified on the command line, only the specified fields and values are
* returned.
*
*/
int main(int argc, char** argv)
{
    const std::string USAGE = "\n" \
        "Usage:\n"
        "    get_item <table> <name> [projection_expression]\n\n"
        "Where:\n"
        "    table - the table to get an item from.\n"
        "    name  - the item to get.\n\n"
        "You can add an optional projection expression (a quote-delimited,\n"
        "comma-separated list of attributes to retrieve) to limit the\n"
        "fields returned from the table.\n\n"
        "Example:\n"
        "    get_item HelloTable World\n"
        "    get_item SiteColors text \"default, bold\"\n";

    if (argc < 3)
    {
        std::cout << USAGE;
        return 1;
    }

    Aws::SDKOptions options;

    Aws::InitAPI(options);
    {
        const Aws::String table(argv[1]);
        const Aws::String name(argv[2]);
        const Aws::String projection(argc > 3 ? argv[3] : "");

        // snippet-start:[dynamodb.cpp.get_item.code]
        Aws::Client::ClientConfiguration clientConfig;
        Aws::DynamoDB::DynamoDBClient dynamoClient(clientConfig);
        Aws::DynamoDB::Model::GetItemRequest req;

        // Set up the request
        req.SetTableName(table);
        Aws::DynamoDB::Model::AttributeValue hashKey;
        hashKey.SetS(name);
        req.AddKey("Name", hashKey);
        if (!projection.empty())
            req.SetProjectionExpression(projection);

        // Retrieve the item's fields and values
        const Aws::DynamoDB::Model::GetItemOutcome& result = dynamoClient.GetItem(req);
        if (result.IsSuccess())
        {
            // Reference the retrieved fields/values
            const Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue>& item = result.GetResult().GetItem();
            if (item.size() > 0)
            {
                // Output each retrieved field and its value
                for (const auto& i : item)
                    std::cout << i.first << ": " << i.second.GetS() << std::endl;
            }
            else
            {
                std::cout << "No item found with the key " << name << std::endl;
            }

        }
        else
        {
            std::cout << "Failed to get item: " << result.GetError().GetMessage();
        }
        // snippet-end:[dynamodb.cpp.get_item.code]
    }
    Aws::ShutdownAPI(options);
    return 0;
}<|endoftext|>


<commit_before>/** \brief Updates Zeder (via Ingo's SQL database) w/ the last N issues of harvested articles for each journal.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018-2020 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include "Compiler.h"
#include "DbConnection.h"
#include "DnsUtil.h"
#include "IniFile.h"
#include "MARC.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "util.h"
#include "Zeder.h"

namespace {

// Please note that Zeder PPN entries are separated by spaces and, unlike what the column names "print_ppn" and
// "online_ppn" imply may in rare cases contain space-separated lists of PPN's.
inline auto SplitZederPPNs(const std::string &ppns) {
    std::vector<std::string> individual_ppns;
    StringUtil::Split(ppns, ' ', &individual_ppns);
    return individual_ppns;
}

struct ZederIdAndPPNType {
    unsigned zeder_id_;
    char type_; // 'p' or 'e' for "print" or "electronic"
public:
    ZederIdAndPPNType(const unsigned zeder_id, const char type)
        : zeder_id_(zeder_id), type_(type) { }
};

std::unordered_map<std::string, ZederIdAndPPNType> GetPPNsToZederIdsAndTypesMap(const std::string &system_type) {
    const Zeder::SimpleZeder zeder(system_type == "ixtheo" ? Zeder::IXTHEO : Zeder::KRIMDOK, { "eppn", "pppn" });
    if (unlikely(zeder.empty()))
        LOG_ERROR("found no Zeder entries matching any of our requested columns!"
                  " (This *should* not happen as we included the column ID!)");

    std::unordered_map<std::string, ZederIdAndPPNType> ppns_to_zeder_ids_and_types_map;
    unsigned included_journal_count(0);
    std::set<std::string> bundle_ppns; // We use a std::set because it is automatically being sorted for us.
    for (const auto &journal : zeder) {
        if (journal.empty())
            continue;
        ++included_journal_count;

        const auto print_ppns(SplitZederPPNs(journal.lookup("pppn")));
        const auto online_ppns(SplitZederPPNs(journal.lookup("eppn")));

        if (print_ppns.empty() and online_ppns.empty()) {
            --included_journal_count;
            LOG_WARNING("Zeder entry #" + std::to_string(journal.getId()) + " is missing print and online PPN's!");
            continue;
        }

        for (const auto &print_ppn : print_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(print_ppn, ZederIdAndPPNType(journal.getId(), 'p'));

        for (const auto &online_ppn : online_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(online_ppn, ZederIdAndPPNType(journal.getId(), 'e'));
    }

    LOG_INFO("downloaded information for " + std::to_string(included_journal_count) + " journal(s) from Zeder.");

    return ppns_to_zeder_ids_and_types_map;
}

// \return the year as a small integer or 0 if we could not parse it.
unsigned short YearStringToShort(const std::string &year_as_string) {
    unsigned short year_as_unsigned_short;
    if (not StringUtil::ToUnsignedShort(year_as_string, &year_as_unsigned_short))
        return 0;
    return year_as_unsigned_short;
}

struct DbEntry {
    std::string jahr_;
    std::string band_;
    std::string heft_;
    std::string seitenbereich_;
public:
    DbEntry(const std::string &jahr, const std::string &band, const std::string &heft, const std::string &seitenbereich)
        : jahr_(jahr), band_(band), heft_(heft), seitenbereich_(seitenbereich) { }
    DbEntry() = default;
    DbEntry(const DbEntry &other) = default;

    /** \return True if the current entry represents a more recent article than "other".  If the entry is in the same issue
        we use the page numbers as an arbitrary tie breaker. */
    bool isNewerThan(const DbEntry &other) const;
};

inline bool DbEntry::isNewerThan(const DbEntry &other) const {
    if (jahr_ < other.jahr_)
        return false;
    if (jahr_ > other.jahr_)
        return true;
    if (band_ < other.band_)
        return false;
    if (band_ > other.band_)
        return true;
    if (heft_ < other.heft_)
        return false;
    if (heft_ > other.heft_)
        return true;
    return seitenbereich_ > other.seitenbereich_; // Somewhat nonsensical, but useful nonetheless.
}

size_t GetExistingDbEntries(const IniFile &ini_file, const std::string &hostname, const std::string &system_type,
                            std::unordered_map<std::string, DbEntry> * const existing_entries)
{
    DbConnection db_connection_select(ini_file, "DatabaseSelect");

    db_connection_select.queryOrDie("SELECT MAX(timestamp),Zeder_ID,PPN,Jahr,Band,Heft,Seitenbereich"
                                    " FROM zeder.erschliessung WHERE Quellrechner='" + hostname + "' AND Systemtyp='"
                                    + system_type + "' GROUP BY Zeder_ID,PPN,Jahr,Band,Heft,Seitenbereich");
    auto result_set(db_connection_select.getLastResultSet());
    while (const auto row = result_set.getNextRow()) {
        const DbEntry db_entry(row["Jahr"], row["Band"], row["Heft"], row["Seitenbereich"]);
        const auto key(row["Zeder_ID"] + "+" + row["PPN"]);
        auto key_and_entry(existing_entries->find(key));
        if (key_and_entry == existing_entries->end() or db_entry.isNewerThan(key_and_entry->second))
            (*existing_entries)[key] = db_entry;
    }

    return existing_entries->size();
}

// \return True if "test_entry" either does not exist in the databse or is newer than the newest existing entry.
bool NewerThanWhatExistsInDB(const std::unordered_map<std::string, DbEntry> &existing_entries,
                                const std::string &zeder_id, const std::string &ppn, const DbEntry &test_entry)
{
    const auto key_and_entry(existing_entries.find(zeder_id + "+" + ppn));
    if (key_and_entry == existing_entries.cend())
        return true;

    return test_entry.isNewerThan(key_and_entry->second);
}

void CollectMostRecentEntries(const IniFile &ini_file, MARC::Reader * const reader, MARC::Writer * const writer,
                              const std::string &system_type, const std::string &hostname,
                              const std::unordered_map<std::string, ZederIdAndPPNType> &ppns_to_zeder_ids_and_types_map,
                              std::unordered_map<std::string, DbEntry> * const ppns_to_most_recent_entries_map)
{
    std::unordered_map<std::string, DbEntry> existing_entries;
    LOG_INFO("Found " + std::to_string(GetExistingDbEntries(ini_file, hostname, system_type, &existing_entries))
             + " existing database entries.");

    unsigned total_count(0);
    while (const auto record = reader->read()) {
        ++total_count;
        writer->write(record); // For the next pipeline phase.

        const std::string superior_control_number(record.getSuperiorControlNumber());
        if (superior_control_number.empty())
            continue;

        const auto ppn_and_zeder_id_and_ppn_type(ppns_to_zeder_ids_and_types_map.find(superior_control_number));
        if (ppn_and_zeder_id_and_ppn_type == ppns_to_zeder_ids_and_types_map.cend())
            continue;

        const auto _936_field(record.findTag("936"));
        if (_936_field == record.end())
            continue;

        const std::string pages(_936_field->getFirstSubfieldWithCode('h'));
        std::string volume;
        std::string issue(_936_field->getFirstSubfieldWithCode('e'));
        if (issue.empty())
            issue = _936_field->getFirstSubfieldWithCode('d');
        else
            volume = _936_field->getFirstSubfieldWithCode('d');
        const std::string year(_936_field->getFirstSubfieldWithCode('j'));

        const std::string zeder_id(std::to_string(ppn_and_zeder_id_and_ppn_type->second.zeder_id_));
        const std::string ppn_type(1, ppn_and_zeder_id_and_ppn_type->second.type_);
        const std::string year_as_string(std::to_string(YearStringToShort(year)));

        const DbEntry new_db_entry(year_as_string, volume, issue, pages);
        if (NewerThanWhatExistsInDB(existing_entries, zeder_id, superior_control_number, new_db_entry))
            (*ppns_to_most_recent_entries_map)[superior_control_number] = new_db_entry;
    }

    LOG_INFO("Processed " + std::to_string(total_count) + " MARC records and found "
             + std::to_string(ppns_to_most_recent_entries_map->size()) + " new entries to insert .");
}

void UpdateDatabase(const IniFile &ini_file, const std::string &system_type, const std::string &hostname,
                    const std::unordered_map<std::string, ZederIdAndPPNType> &ppns_to_zeder_ids_and_types_map,
                    const std::unordered_map<std::string, DbEntry> &ppns_to_most_recent_entries_map)
{
    const auto JOB_START_TIME(std::to_string(std::time(nullptr)));

    DbConnection db_connection_insert(ini_file, "DatabaseInsert");
    const unsigned SQL_INSERT_BATCH_SIZE(20);
    const std::vector<std::string> COLUMN_NAMES{ "timestamp", "Quellrechner", "Systemtyp", "Zeder_ID", "PPN_Typ",
                                                 "PPN", "Jahr", "Band", "Heft", "Seitenbereich" };
    std::vector<std::vector<std::optional<std::string>>> column_values;

    for (const auto &[ppn, db_entry] : ppns_to_most_recent_entries_map) {
        const auto ppn_and_zeder_id_and_ppn_type(ppns_to_zeder_ids_and_types_map.find(ppn));
        if (unlikely(ppn_and_zeder_id_and_ppn_type == ppns_to_zeder_ids_and_types_map.cend()))
            LOG_ERROR("this should *never* happen!");

        const std::vector<std::optional<std::string>> new_column_values{
            { /* timestamp */     JOB_START_TIME                                                  },
            { /* Quellrechner */  hostname                                                        },
            { /* Systemtyp */     system_type,                                                    },
            { /* Zeder_ID */      std::to_string(ppn_and_zeder_id_and_ppn_type->second.zeder_id_) },
            { /* PPN_Typ */       std::string(1, ppn_and_zeder_id_and_ppn_type->second.type_)     },
            { /* PPN */           ppn                                                             },
            { /* Jahr */          db_entry.jahr_                                                  },
            { /* Band */          db_entry.band_                                                  },
            { /* Heft */          db_entry.heft_                                                  },
            { /* Seitenbereich */ db_entry.seitenbereich_                                         },
        };
        column_values.emplace_back(new_column_values);

        if (column_values.size() == SQL_INSERT_BATCH_SIZE) {
            db_connection_insert.insertIntoTableOrDie("zeder.erschliessung", COLUMN_NAMES, column_values);
            column_values.clear();
        }
    }
    if (not column_values.empty())
            db_connection_insert.insertIntoTableOrDie("zeder.erschliessung", COLUMN_NAMES, column_values);

    LOG_INFO("Inserted " + std::to_string(ppns_to_most_recent_entries_map.size()) + " entries into Ingo's database.");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 4)
        ::Usage("[--min-log-level=log_level] system_type marc_input marc_output\n"
                "\twhere \"system_type\" must be one of ixtheo|krimdok");

    std::string system_type;
    if (__builtin_strcmp("ixtheo", argv[1]) == 0)
        system_type = "ixtheo";
    else if (__builtin_strcmp("krimdok", argv[1]) == 0)
        system_type = "krimdok";
    else
        LOG_ERROR("system_type must be one of ixtheo|krimdok!");

    const auto ppns_to_zeder_ids_and_types_map(GetPPNsToZederIdsAndTypesMap(system_type));

    const IniFile ini_file;
    const auto HOSTNAME(DnsUtil::GetHostname());
    const auto marc_reader(MARC::Reader::Factory(argv[2]));
    const auto marc_writer(MARC::Writer::Factory(argv[3]));
    std::unordered_map<std::string, DbEntry> ppns_to_most_recent_entries_map;
    CollectMostRecentEntries(ini_file, marc_reader.get(), marc_writer.get(), system_type, HOSTNAME,
                             ppns_to_zeder_ids_and_types_map, &ppns_to_most_recent_entries_map);
    UpdateDatabase(ini_file, system_type, HOSTNAME, ppns_to_zeder_ids_and_types_map,
                   ppns_to_most_recent_entries_map);

    return EXIT_SUCCESS;
}
<commit_msg>determine most recent article<commit_after>/** \brief Updates Zeder (via Ingo's SQL database) w/ the last N issues of harvested articles for each journal.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018-2020 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include "Compiler.h"
#include "DbConnection.h"
#include "DnsUtil.h"
#include "IniFile.h"
#include "MARC.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "util.h"
#include "Zeder.h"

namespace {

// Please note that Zeder PPN entries are separated by spaces and, unlike what the column names "print_ppn" and
// "online_ppn" imply may in rare cases contain space-separated lists of PPN's.
inline auto SplitZederPPNs(const std::string &ppns) {
    std::vector<std::string> individual_ppns;
    StringUtil::Split(ppns, ' ', &individual_ppns);
    return individual_ppns;
}

struct ZederIdAndPPNType {
    unsigned zeder_id_;
    char type_; // 'p' or 'e' for "print" or "electronic"
public:
    ZederIdAndPPNType(const unsigned zeder_id, const char type)
        : zeder_id_(zeder_id), type_(type) { }
};

std::unordered_map<std::string, ZederIdAndPPNType> GetPPNsToZederIdsAndTypesMap(const std::string &system_type) {
    const Zeder::SimpleZeder zeder(system_type == "ixtheo" ? Zeder::IXTHEO : Zeder::KRIMDOK, { "eppn", "pppn" });
    if (unlikely(zeder.empty()))
        LOG_ERROR("found no Zeder entries matching any of our requested columns!"
                  " (This *should* not happen as we included the column ID!)");

    std::unordered_map<std::string, ZederIdAndPPNType> ppns_to_zeder_ids_and_types_map;
    unsigned included_journal_count(0);
    std::set<std::string> bundle_ppns; // We use a std::set because it is automatically being sorted for us.
    for (const auto &journal : zeder) {
        if (journal.empty())
            continue;
        ++included_journal_count;

        const auto print_ppns(SplitZederPPNs(journal.lookup("pppn")));
        const auto online_ppns(SplitZederPPNs(journal.lookup("eppn")));

        if (print_ppns.empty() and online_ppns.empty()) {
            --included_journal_count;
            LOG_WARNING("Zeder entry #" + std::to_string(journal.getId()) + " is missing print and online PPN's!");
            continue;
        }

        for (const auto &print_ppn : print_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(print_ppn, ZederIdAndPPNType(journal.getId(), 'p'));

        for (const auto &online_ppn : online_ppns)
            ppns_to_zeder_ids_and_types_map.emplace(online_ppn, ZederIdAndPPNType(journal.getId(), 'e'));
    }

    LOG_INFO("downloaded information for " + std::to_string(included_journal_count) + " journal(s) from Zeder.");

    return ppns_to_zeder_ids_and_types_map;
}

// \return the year as a small integer or 0 if we could not parse it.
unsigned short YearStringToShort(const std::string &year_as_string) {
    unsigned short year_as_unsigned_short;
    if (not StringUtil::ToUnsignedShort(year_as_string, &year_as_unsigned_short))
        return 0;
    return year_as_unsigned_short;
}

struct DbEntry {
    std::string jahr_;
    std::string band_;
    std::string heft_;
    std::string seitenbereich_;
public:
    DbEntry(const std::string &jahr, const std::string &band, const std::string &heft, const std::string &seitenbereich)
        : jahr_(jahr), band_(band), heft_(heft), seitenbereich_(seitenbereich) { }
    DbEntry() = default;
    DbEntry(const DbEntry &other) = default;

    /** \return True if the current entry represents a more recent article than "other".  If the entry is in the same issue
        we use the page numbers as an arbitrary tie breaker. */
    bool isNewerThan(const DbEntry &other) const;
};

inline bool DbEntry::isNewerThan(const DbEntry &other) const {
    if (jahr_ < other.jahr_)
        return false;
    if (jahr_ > other.jahr_)
        return true;
    if (band_ < other.band_)
        return false;
    if (band_ > other.band_)
        return true;
    if (heft_ < other.heft_)
        return false;
    if (heft_ > other.heft_)
        return true;
    return seitenbereich_ > other.seitenbereich_; // Somewhat nonsensical, but useful nonetheless.
}

size_t GetExistingDbEntries(const IniFile &ini_file, const std::string &hostname, const std::string &system_type,
                            std::unordered_map<std::string, DbEntry> * const existing_entries)
{
    DbConnection db_connection_select(ini_file, "DatabaseSelect");

    db_connection_select.queryOrDie("SELECT MAX(timestamp),Zeder_ID,PPN,Jahr,Band,Heft,Seitenbereich"
                                    " FROM zeder.erschliessung WHERE Quellrechner='" + hostname + "' AND Systemtyp='"
                                    + system_type + "' GROUP BY Zeder_ID,PPN,Jahr,Band,Heft,Seitenbereich");
    auto result_set(db_connection_select.getLastResultSet());
    while (const auto row = result_set.getNextRow()) {
        const DbEntry db_entry(row["Jahr"], row["Band"], row["Heft"], row["Seitenbereich"]);
        const auto key(row["Zeder_ID"] + "+" + row["PPN"]);
        auto key_and_entry(existing_entries->find(key));
        if (key_and_entry == existing_entries->end() or db_entry.isNewerThan(key_and_entry->second))
            (*existing_entries)[key] = db_entry;
    }

    return existing_entries->size();
}

// \return True if "test_entry" either does not exist in the databse or is newer than the newest existing entry.
bool NewerThanWhatExistsInDB(const std::unordered_map<std::string, DbEntry> &existing_entries,
                             const std::string &zeder_id, const std::string &ppn, const DbEntry &test_entry)
{
    const auto key_and_entry(existing_entries.find(zeder_id + "+" + ppn));
    if (key_and_entry == existing_entries.cend())
        return true;

    return test_entry.isNewerThan(key_and_entry->second);
}

void CollectMostRecentEntries(const IniFile &ini_file, MARC::Reader * const reader, MARC::Writer * const writer,
                              const std::string &system_type, const std::string &hostname,
                              const std::unordered_map<std::string, ZederIdAndPPNType> &ppns_to_zeder_ids_and_types_map,
                              std::unordered_map<std::string, DbEntry> * const ppns_to_most_recent_entries_map)
{
    std::unordered_map<std::string, DbEntry> existing_entries;
    LOG_INFO("Found " + std::to_string(GetExistingDbEntries(ini_file, hostname, system_type, &existing_entries))
             + " existing database entries.");

    unsigned total_count(0);
    while (const auto record = reader->read()) {
        ++total_count;
        writer->write(record); // For the next pipeline phase.

        const std::string superior_control_number(record.getSuperiorControlNumber());
        if (superior_control_number.empty())
            continue;

        const auto ppn_and_zeder_id_and_ppn_type(ppns_to_zeder_ids_and_types_map.find(superior_control_number));
        if (ppn_and_zeder_id_and_ppn_type == ppns_to_zeder_ids_and_types_map.cend())
            continue;

        const auto _936_field(record.findTag("936"));
        if (_936_field == record.end())
            continue;

        const std::string pages(_936_field->getFirstSubfieldWithCode('h'));
        std::string volume;
        std::string issue(_936_field->getFirstSubfieldWithCode('e'));
        if (issue.empty())
            issue = _936_field->getFirstSubfieldWithCode('d');
        else
            volume = _936_field->getFirstSubfieldWithCode('d');
        const std::string year(_936_field->getFirstSubfieldWithCode('j'));

        const std::string zeder_id(std::to_string(ppn_and_zeder_id_and_ppn_type->second.zeder_id_));
        const std::string ppn_type(1, ppn_and_zeder_id_and_ppn_type->second.type_);
        const std::string year_as_string(std::to_string(YearStringToShort(year)));

        const DbEntry new_db_entry(year_as_string, volume, issue, pages);
        if (NewerThanWhatExistsInDB(existing_entries, zeder_id, superior_control_number, new_db_entry)) {
            (*ppns_to_most_recent_entries_map)[superior_control_number] = new_db_entry;
            const auto superior_control_number_and_entry(ppns_to_most_recent_entries_map->find(superior_control_number));
            if (superior_control_number_and_entry == ppns_to_most_recent_entries_map->end() or new_db_entry.isNewerThan(superior_control_number_and_entry->second))
                (*ppns_to_most_recent_entries_map)[superior_control_number] = new_db_entry;
        }
    }

    LOG_INFO("Processed " + std::to_string(total_count) + " MARC records and found "
             + std::to_string(ppns_to_most_recent_entries_map->size()) + " new entries to insert .");
}

void UpdateDatabase(const IniFile &ini_file, const std::string &system_type, const std::string &hostname,
                    const std::unordered_map<std::string, ZederIdAndPPNType> &ppns_to_zeder_ids_and_types_map,
                    const std::unordered_map<std::string, DbEntry> &ppns_to_most_recent_entries_map)
{
    const auto JOB_START_TIME(std::to_string(std::time(nullptr)));

    DbConnection db_connection_insert(ini_file, "DatabaseInsert");
    const unsigned SQL_INSERT_BATCH_SIZE(20);
    const std::vector<std::string> COLUMN_NAMES{ "timestamp", "Quellrechner", "Systemtyp", "Zeder_ID", "PPN_Typ",
                                                 "PPN", "Jahr", "Band", "Heft", "Seitenbereich" };
    std::vector<std::vector<std::optional<std::string>>> column_values;

    for (const auto &[ppn, db_entry] : ppns_to_most_recent_entries_map) {
        const auto ppn_and_zeder_id_and_ppn_type(ppns_to_zeder_ids_and_types_map.find(ppn));
        if (unlikely(ppn_and_zeder_id_and_ppn_type == ppns_to_zeder_ids_and_types_map.cend()))
            LOG_ERROR("this should *never* happen!");

        const std::vector<std::optional<std::string>> new_column_values{
            { /* timestamp */     JOB_START_TIME                                                  },
            { /* Quellrechner */  hostname                                                        },
            { /* Systemtyp */     system_type,                                                    },
            { /* Zeder_ID */      std::to_string(ppn_and_zeder_id_and_ppn_type->second.zeder_id_) },
            { /* PPN_Typ */       std::string(1, ppn_and_zeder_id_and_ppn_type->second.type_)     },
            { /* PPN */           ppn                                                             },
            { /* Jahr */          db_entry.jahr_                                                  },
            { /* Band */          db_entry.band_                                                  },
            { /* Heft */          db_entry.heft_                                                  },
            { /* Seitenbereich */ db_entry.seitenbereich_                                         },
        };
        column_values.emplace_back(new_column_values);

        if (column_values.size() == SQL_INSERT_BATCH_SIZE) {
            db_connection_insert.insertIntoTableOrDie("zeder.erschliessung", COLUMN_NAMES, column_values);
            column_values.clear();
        }
    }
    if (not column_values.empty())
            db_connection_insert.insertIntoTableOrDie("zeder.erschliessung", COLUMN_NAMES, column_values);

    LOG_INFO("Inserted " + std::to_string(ppns_to_most_recent_entries_map.size()) + " entries into Ingo's database.");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 4)
        ::Usage("[--min-log-level=log_level] system_type marc_input marc_output\n"
                "\twhere \"system_type\" must be one of ixtheo|krimdok");

    std::string system_type;
    if (__builtin_strcmp("ixtheo", argv[1]) == 0)
        system_type = "ixtheo";
    else if (__builtin_strcmp("krimdok", argv[1]) == 0)
        system_type = "krimdok";
    else
        LOG_ERROR("system_type must be one of ixtheo|krimdok!");

    const auto ppns_to_zeder_ids_and_types_map(GetPPNsToZederIdsAndTypesMap(system_type));

    const IniFile ini_file;
    const auto HOSTNAME(DnsUtil::GetHostname());
    const auto marc_reader(MARC::Reader::Factory(argv[2]));
    const auto marc_writer(MARC::Writer::Factory(argv[3]));
    std::unordered_map<std::string, DbEntry> ppns_to_most_recent_entries_map;
    CollectMostRecentEntries(ini_file, marc_reader.get(), marc_writer.get(), system_type, HOSTNAME,
                             ppns_to_zeder_ids_and_types_map, &ppns_to_most_recent_entries_map);
    UpdateDatabase(ini_file, system_type, HOSTNAME, ppns_to_zeder_ids_and_types_map,
                   ppns_to_most_recent_entries_map);

    return EXIT_SUCCESS;
}
<|endoftext|>