<commit_before>// //////////////////////////////////////////////////////////////////////
// Import section
// //////////////////////////////////////////////////////////////////////
// STL
#include <cassert>
// StdAir
#include <stdair/stdair_exceptions.hpp>
#include <stdair/basic/BasConst_Event.hpp>
#include <stdair/bom/EventStruct.hpp>
#include <stdair/service/Logger.hpp>
// SEvMgr
#include <sevmgr/basic/BasConst_EventQueueManager.hpp>
#include <sevmgr/bom/EventQueue.hpp>

namespace SEVMGR {

  // //////////////////////////////////////////////////////////////////////
  EventQueue::EventQueue()
    : _key (DEFAULT_EVENT_QUEUE_ID), _parent (NULL),
      _progressStatus (stdair::DEFAULT_PROGRESS_STATUS,
                       stdair::DEFAULT_PROGRESS_STATUS) {
  }

  // //////////////////////////////////////////////////////////////////////
  EventQueue::EventQueue (const Key_T& iKey)
    : _key (iKey), _parent (NULL),
      _progressStatus (stdair::DEFAULT_PROGRESS_STATUS,
                       stdair::DEFAULT_PROGRESS_STATUS) {
  }

  // //////////////////////////////////////////////////////////////////////
  EventQueue::EventQueue (const EventQueue& iEventQueue)
    : _key (DEFAULT_EVENT_QUEUE_ID), _parent (NULL),
      _progressStatus (stdair::DEFAULT_PROGRESS_STATUS,
                       stdair::DEFAULT_PROGRESS_STATUS) {
    assert (false);
  }

  // //////////////////////////////////////////////////////////////////////
  EventQueue::~EventQueue() {
    _eventList.clear();
  }

  // //////////////////////////////////////////////////////////////////////
  std::string EventQueue::toString() const {
    std::ostringstream oStr;
    oStr << "(" << _eventList.size() << ") "
         << _progressStatus.getCurrentNb() << "/{"
         << _progressStatus.getExpectedNb() << ","
         << _progressStatus.getActualNb() << "}";
    return oStr.str();
  }

  // //////////////////////////////////////////////////////////////////////
  std::string EventQueue::display() const {
    std::ostringstream oStr;

    oStr << toString();

    return oStr.str();
  }

  // //////////////////////////////////////////////////////////////////////
  std::string EventQueue::list () const {
    std::ostringstream oStr;
    oStr << describeKey () << "\n"
	 << toString() << "\n";

    // Browse the events
    for (stdair::EventList_T::const_iterator itEvent = _eventList.begin();
	 itEvent != _eventList.end(); ++itEvent) {
      const stdair::EventListElement_T* lEventListElement_ptr = &(*itEvent);
      assert (lEventListElement_ptr != NULL);
      const stdair::EventListElement_T& lEventListElement =
	*lEventListElement_ptr;
      const stdair::EventStruct& lEvent = lEventListElement.second;

      // Delegate the JSON export to the dedicated service
      oStr << lEvent.describe() << "\n";
    }

    return oStr.str();
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::Count_T EventQueue::getQueueSize () const {
    return _eventList.size();
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::isQueueEmpty () const {
    return _eventList.empty();
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::isQueueDone () const {
    const bool isQueueEmpty = _eventList.empty();
    return isQueueEmpty;
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::reset () {
    // Reset only the current number of events, not the expected one
    _progressStatus.reset();

    // Empty the list of events
    _eventList.clear();

    // Reset the progress statuses for all the event types
    for (ProgressStatusMap_T::iterator itProgressStatus =
           _progressStatusMap.begin();
         itProgressStatus != _progressStatusMap.end(); ++itProgressStatus) {
      stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
      lProgressStatus.reset();
    }
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::
  hasProgressStatus (const stdair::EventType::EN_EventType& iType) const {

    bool hasProgressStatus = true;

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_DEBUG ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());

      hasProgressStatus = false;
    }

    return hasProgressStatus;
  }

  // //////////////////////////////////////////////////////////////////////
  const stdair::Count_T& EventQueue::
  getCurrentNbOfEvents (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_ERROR ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());
      assert (false);
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.getCurrentNb();
  }

  // //////////////////////////////////////////////////////////////////////
  const stdair::Count_T& EventQueue::
  getExpectedTotalNbOfEvents (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      std::ostringstream oStr;
      oStr << "No ProgressStatus structure can be retrieved in the EventQueue '"
           << display() << "'. The EventQueue should be initialised, e.g., by "
           << "calling a buildSampleBom() method.";
      //
      STDAIR_LOG_ERROR (oStr.str());
      throw EventQueueException (oStr.str());
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.getExpectedNb();
  }

  // //////////////////////////////////////////////////////////////////////
  const stdair::Count_T& EventQueue::
  getActualTotalNbOfEvents (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_ERROR ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());
      assert (false);
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.getActualNb();
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::updateStatus (const stdair::EventType::EN_EventType& iType,
                                 const stdair::ProgressStatus& iProgressStatus) {

    // Retrieve, if existing, the ProgressStatus structure
    // corresponding to the given event type
    ProgressStatusMap_T::iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      const bool hasInsertBeenSuccessful =
        _progressStatusMap.insert (ProgressStatusMap_T::
                                   value_type (iType, iProgressStatus)).second;

      if (hasInsertBeenSuccessful == false) {
        STDAIR_LOG_ERROR ("No progress_status can be inserted "
                          << "for the following event type: "
                          << stdair::EventType::getLabel(iType)
                          << ". EventQueue: " << toString());
        throw stdair::EventException ("No progress_status can be inserted for the "
				      "following event type: "
				      + stdair::EventType::getLabel(iType)
				      + ". EventQueue: " + toString());
      }

      return;
    }

    stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;

    // Update the progress status
    const stdair::Count_T& lCurrentNb = iProgressStatus.getCurrentNb();
    lProgressStatus.setCurrentNb (lCurrentNb);

    const stdair::Count_T& lExpectedNb = iProgressStatus.getExpectedNb();
    lProgressStatus.setExpectedNb(lProgressStatus.getExpectedNb() + lExpectedNb);

    const stdair::Count_T& lActualNb = iProgressStatus.getActualNb();
    lProgressStatus.setActualNb (lProgressStatus.getActualNb() + lActualNb);
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::
  addStatus (const stdair::EventType::EN_EventType& iType,
             const stdair::NbOfEvents_T& iExpectedTotalNbOfEvents) {

    // Initialise the progress status object
    const stdair::Count_T lExpectedTotalNbOfEventsInt =
      static_cast<const stdair::Count_T> (std::floor (iExpectedTotalNbOfEvents));
    const stdair::ProgressStatus lProgressStatus (lExpectedTotalNbOfEventsInt);

    // Update the progress status for the given event type
    updateStatus (iType, lProgressStatus);

    // Update the overall progress status
    const stdair::Count_T lExpectedNb =
      static_cast<const stdair::Count_T> (_progressStatus.getExpectedNb()
				  + iExpectedTotalNbOfEvents);
    _progressStatus.setExpectedNb (lExpectedNb);

    const stdair::Count_T lActualNb =
      static_cast<const stdair::Count_T> (_progressStatus.getActualNb()
				  + iExpectedTotalNbOfEvents);
    _progressStatus.setActualNb (lActualNb);

  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::updateStatus (const stdair::EventType::EN_EventType& iType,
                                 const stdair::NbOfEvents_T& iActualNbOfEvents) {

    // Initialise the progress status object for the type key
    stdair:: Count_T lActualNbOfEventsInt =
      static_cast<const  stdair::Count_T> (std::floor (iActualNbOfEvents));

    // Update the progress status for the corresponding content type key
    ProgressStatusMap_T::iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus != _progressStatusMap.end()) {

      //
      stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;

      // Update the overall progress status
      const stdair::Count_T lActualEventTypeNb = lProgressStatus.getActualNb();
      const stdair::Count_T lActualTotalNb = _progressStatus.getActualNb();
      _progressStatus.setActualNb (lActualTotalNb + iActualNbOfEvents - lActualEventTypeNb);

      // Update the progress status for the corresponding type key
      lProgressStatus.setActualNb (lActualNbOfEventsInt);
    }
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::setStatus (const stdair::EventType::EN_EventType& iType,
                              const stdair::ProgressStatus& iProgressStatus) {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::iterator itProgressStatus =
      _progressStatusMap.find (iType);
    // assert (itProgressStatus != _progressStatusMap.end());
    if (itProgressStatus != _progressStatusMap.end()) {
      // Update the ProgressStatus structure
      itProgressStatus->second = iProgressStatus;
    }
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::ProgressStatus EventQueue::
  getStatus (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus != _progressStatusMap.end()) {
      const stdair::ProgressStatus& oProgressStatus = itProgressStatus->second;
      return oProgressStatus;
    }

    return stdair::ProgressStatus();
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::ProgressPercentage_T EventQueue::
  calculateProgress (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_ERROR ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());
      assert (false);
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.progress();
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::ProgressStatusSet EventQueue::popEvent (stdair::EventStruct& ioEventStruct) {

    if (_eventList.empty() == true) {
      std::ostringstream oStr;
      oStr << "The event queue '" << describeKey() << "' is empty. "
	   << "No event can be popped.";
      //
      STDAIR_LOG_ERROR (oStr.str());
      throw EventQueueException (oStr.str());
    }

    /**
     * 1. Update the event queue itself.
     */
    // Get an iterator on the first event (sorted by date-time stamps)
    stdair::EventList_T::iterator itEvent = _eventList.begin();

    /**
     * Extract (a copy of) the corresponding Event structure. We make
     * a copy here, as the original EventStruct structure is removed
     * from the list (and erased). Moreover, the resulting EventStruct
     * structure will be returned by this method.
     */
    ioEventStruct = itEvent->second;
    // Retrieve the event type
    const stdair::EventType::EN_EventType& lEventType = ioEventStruct.getEventType();
    stdair::ProgressStatusSet oProgressStatusSet (lEventType);

    // Update the (current number part of the) overall progress status,
    // to account for the event that is being popped out of the event
    // queue.
    ++_progressStatus;

    // Remove the event, which has just been retrieved
    _eventList.erase (itEvent);

    /**
     * 2. Update the progress statuses held by the EventStruct structure.
     *
     * 2.1. Update the progress status specific to the event type (e.g.,
     *      booking request, optimisation notification).
     */

    // Retrieve the progress status specific to that event type
    stdair::ProgressStatus lEventTypeProgressStatus = getStatus (lEventType);

    // Increase the current number of events
    ++lEventTypeProgressStatus;

    // Store back the progress status
    setStatus (lEventType, lEventTypeProgressStatus);

    // Update the progress status of the progress status set, specific to
    // the event type.
    oProgressStatusSet.setTypeSpecificStatus (lEventTypeProgressStatus);

    /**
     * 2.2. Update the overall progress status.
     */
    // Update the overall progress status of the progress status set.
    oProgressStatusSet.setOverallStatus (_progressStatus);

    //
    return oProgressStatusSet;
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::addEvent (stdair::EventStruct& ioEventStruct) {
    bool insertionSucceeded =
      _eventList.insert (stdair::EventListElement_T (ioEventStruct.getEventTimeStamp(),
						     ioEventStruct)).second;

    /**
     * If the insertion has not been successful, try repeatedly until
     * the insertion becomes successful.
     *
     * The date-time is counted in milliseconds (1e-3 second). Hence,
     * one thousand (1e3) of attempts correspond to 1 second.
     *
     * The check on one thousand (1e3) is made in order to avoid
     * potential infinite loops. In such case, however, an assertion
     * will fail: it is always better that an assertion fails rather
     * than entering an infinite loop.
     */
    const unsigned int idx = 0;
    while (insertionSucceeded == false && idx != 1e3) {
      // Increment the date-time stamp (expressed in milliseconds)
      ioEventStruct.incrementEventTimeStamp();

      // Retry to insert into the event queue
      insertionSucceeded =
        _eventList.insert (stdair::EventListElement_T (ioEventStruct.getEventTimeStamp(),
						       ioEventStruct)).second;
    }
    assert (idx != 1e3);

    return insertionSucceeded;
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::hasEventDateTime (const stdair::DateTime_T& iDateTime) {

    bool hasSearchEventBeenSucessful = true;

    /**
     * Compute the number of milliseconds between the
     * date-time of the event and DEFAULT_EVENT_OLDEST_DATETIME
     * (as of Feb. 2011, that date is set to Jan. 1, 2010).
     */
    const stdair::Duration_T lDuration =
      iDateTime - stdair::DEFAULT_EVENT_OLDEST_DATETIME;
    const stdair::LongDuration_T lDateTimeStamp =
      lDuration.total_milliseconds();

    // Searches the container for an element with iDateTime as key
    stdair::EventList_T::iterator itEvent =
      _eventList.find (lDateTimeStamp);

    // An iterator to map::end means the specified key has not found in the
    // container.
    if (itEvent == _eventList.end()) {
      hasSearchEventBeenSucessful = false;
    }

    return hasSearchEventBeenSucessful;

  }

}
<commit_msg>[Dev] Removed the display of rm events and snap shots for a lighter display of the queue.<commit_after>// //////////////////////////////////////////////////////////////////////
// Import section
// //////////////////////////////////////////////////////////////////////
// STL
#include <cassert>
// StdAir
#include <stdair/stdair_exceptions.hpp>
#include <stdair/basic/BasConst_Event.hpp>
#include <stdair/bom/EventStruct.hpp>
#include <stdair/service/Logger.hpp>
// SEvMgr
#include <sevmgr/basic/BasConst_EventQueueManager.hpp>
#include <sevmgr/bom/EventQueue.hpp>

namespace SEVMGR {

  // //////////////////////////////////////////////////////////////////////
  EventQueue::EventQueue()
    : _key (DEFAULT_EVENT_QUEUE_ID), _parent (NULL),
      _progressStatus (stdair::DEFAULT_PROGRESS_STATUS,
                       stdair::DEFAULT_PROGRESS_STATUS) {
  }

  // //////////////////////////////////////////////////////////////////////
  EventQueue::EventQueue (const Key_T& iKey)
    : _key (iKey), _parent (NULL),
      _progressStatus (stdair::DEFAULT_PROGRESS_STATUS,
                       stdair::DEFAULT_PROGRESS_STATUS) {
  }

  // //////////////////////////////////////////////////////////////////////
  EventQueue::EventQueue (const EventQueue& iEventQueue)
    : _key (DEFAULT_EVENT_QUEUE_ID), _parent (NULL),
      _progressStatus (stdair::DEFAULT_PROGRESS_STATUS,
                       stdair::DEFAULT_PROGRESS_STATUS) {
    assert (false);
  }

  // //////////////////////////////////////////////////////////////////////
  EventQueue::~EventQueue() {
    _eventList.clear();
  }

  // //////////////////////////////////////////////////////////////////////
  std::string EventQueue::toString() const {
    std::ostringstream oStr;
    oStr << "(" << _eventList.size() << ") "
         << _progressStatus.getCurrentNb() << "/{"
         << _progressStatus.getExpectedNb() << ","
         << _progressStatus.getActualNb() << "}";
    return oStr.str();
  }

  // //////////////////////////////////////////////////////////////////////
  std::string EventQueue::display() const {
    std::ostringstream oStr;

    oStr << toString();

    return oStr.str();
  }

  // //////////////////////////////////////////////////////////////////////
  std::string EventQueue::list () const {
    std::ostringstream oStr;
    oStr << describeKey () << "\n"
	 << toString() << "\n";

    // Browse the events
    for (stdair::EventList_T::const_iterator itEvent = _eventList.begin();
	 itEvent != _eventList.end(); ++itEvent) {
      const stdair::EventListElement_T* lEventListElement_ptr = &(*itEvent);
      assert (lEventListElement_ptr != NULL);
      const stdair::EventListElement_T& lEventListElement =
	*lEventListElement_ptr;
      const stdair::EventStruct& lEvent = lEventListElement.second;
      const stdair::EventType::EN_EventType& lEventType = lEvent.getEventType();

      switch (lEventType) {
      case stdair::EventType::BKG_REQ:
      case stdair::EventType::CX:
      case stdair::EventType::BRK_PT: {
        // Delegate the JSON export to the dedicated service
        oStr << lEvent.describe();
        break;
      }
      case stdair::EventType::OPT_NOT_4_FD:
      case stdair::EventType::SNAPSHOT:
      case stdair::EventType::RM:
      default:
        break;
      }

    }
    return oStr.str();
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::Count_T EventQueue::getQueueSize () const {
    return _eventList.size();
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::isQueueEmpty () const {
    return _eventList.empty();
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::isQueueDone () const {
    const bool isQueueEmpty = _eventList.empty();
    return isQueueEmpty;
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::reset () {
    // Reset only the current number of events, not the expected one
    _progressStatus.reset();

    // Empty the list of events
    _eventList.clear();

    // Reset the progress statuses for all the event types
    for (ProgressStatusMap_T::iterator itProgressStatus =
           _progressStatusMap.begin();
         itProgressStatus != _progressStatusMap.end(); ++itProgressStatus) {
      stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
      lProgressStatus.reset();
    }
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::
  hasProgressStatus (const stdair::EventType::EN_EventType& iType) const {

    bool hasProgressStatus = true;

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_DEBUG ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());

      hasProgressStatus = false;
    }

    return hasProgressStatus;
  }

  // //////////////////////////////////////////////////////////////////////
  const stdair::Count_T& EventQueue::
  getCurrentNbOfEvents (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_ERROR ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());
      assert (false);
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.getCurrentNb();
  }

  // //////////////////////////////////////////////////////////////////////
  const stdair::Count_T& EventQueue::
  getExpectedTotalNbOfEvents (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      std::ostringstream oStr;
      oStr << "No ProgressStatus structure can be retrieved in the EventQueue '"
           << display() << "'. The EventQueue should be initialised, e.g., by "
           << "calling a buildSampleBom() method.";
      //
      STDAIR_LOG_ERROR (oStr.str());
      throw EventQueueException (oStr.str());
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.getExpectedNb();
  }

  // //////////////////////////////////////////////////////////////////////
  const stdair::Count_T& EventQueue::
  getActualTotalNbOfEvents (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_ERROR ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());
      assert (false);
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.getActualNb();
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::updateStatus (const stdair::EventType::EN_EventType& iType,
                                 const stdair::ProgressStatus& iProgressStatus) {

    // Retrieve, if existing, the ProgressStatus structure
    // corresponding to the given event type
    ProgressStatusMap_T::iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      const bool hasInsertBeenSuccessful =
        _progressStatusMap.insert (ProgressStatusMap_T::
                                   value_type (iType, iProgressStatus)).second;

      if (hasInsertBeenSuccessful == false) {
        STDAIR_LOG_ERROR ("No progress_status can be inserted "
                          << "for the following event type: "
                          << stdair::EventType::getLabel(iType)
                          << ". EventQueue: " << toString());
        throw stdair::EventException ("No progress_status can be inserted for the "
				      "following event type: "
				      + stdair::EventType::getLabel(iType)
				      + ". EventQueue: " + toString());
      }

      return;
    }

    stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;

    // Update the progress status
    const stdair::Count_T& lCurrentNb = iProgressStatus.getCurrentNb();
    lProgressStatus.setCurrentNb (lCurrentNb);

    const stdair::Count_T& lExpectedNb = iProgressStatus.getExpectedNb();
    lProgressStatus.setExpectedNb(lProgressStatus.getExpectedNb() + lExpectedNb);

    const stdair::Count_T& lActualNb = iProgressStatus.getActualNb();
    lProgressStatus.setActualNb (lProgressStatus.getActualNb() + lActualNb);
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::
  addStatus (const stdair::EventType::EN_EventType& iType,
             const stdair::NbOfEvents_T& iExpectedTotalNbOfEvents) {

    // Initialise the progress status object
    const stdair::Count_T lExpectedTotalNbOfEventsInt =
      static_cast<const stdair::Count_T> (std::floor (iExpectedTotalNbOfEvents));
    const stdair::ProgressStatus lProgressStatus (lExpectedTotalNbOfEventsInt);

    // Update the progress status for the given event type
    updateStatus (iType, lProgressStatus);

    // Update the overall progress status
    const stdair::Count_T lExpectedNb =
      static_cast<const stdair::Count_T> (_progressStatus.getExpectedNb()
				  + iExpectedTotalNbOfEvents);
    _progressStatus.setExpectedNb (lExpectedNb);

    const stdair::Count_T lActualNb =
      static_cast<const stdair::Count_T> (_progressStatus.getActualNb()
				  + iExpectedTotalNbOfEvents);
    _progressStatus.setActualNb (lActualNb);

  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::updateStatus (const stdair::EventType::EN_EventType& iType,
                                 const stdair::NbOfEvents_T& iActualNbOfEvents) {

    // Initialise the progress status object for the type key
    stdair:: Count_T lActualNbOfEventsInt =
      static_cast<const  stdair::Count_T> (std::floor (iActualNbOfEvents));

    // Update the progress status for the corresponding content type key
    ProgressStatusMap_T::iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus != _progressStatusMap.end()) {

      //
      stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;

      // Update the overall progress status
      const stdair::Count_T lActualEventTypeNb = lProgressStatus.getActualNb();
      const stdair::Count_T lActualTotalNb = _progressStatus.getActualNb();
      _progressStatus.setActualNb (lActualTotalNb + iActualNbOfEvents - lActualEventTypeNb);

      // Update the progress status for the corresponding type key
      lProgressStatus.setActualNb (lActualNbOfEventsInt);
    }
  }

  // //////////////////////////////////////////////////////////////////////
  void EventQueue::setStatus (const stdair::EventType::EN_EventType& iType,
                              const stdair::ProgressStatus& iProgressStatus) {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::iterator itProgressStatus =
      _progressStatusMap.find (iType);
    // assert (itProgressStatus != _progressStatusMap.end());
    if (itProgressStatus != _progressStatusMap.end()) {
      // Update the ProgressStatus structure
      itProgressStatus->second = iProgressStatus;
    }
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::ProgressStatus EventQueue::
  getStatus (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus != _progressStatusMap.end()) {
      const stdair::ProgressStatus& oProgressStatus = itProgressStatus->second;
      return oProgressStatus;
    }

    return stdair::ProgressStatus();
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::ProgressPercentage_T EventQueue::
  calculateProgress (const stdair::EventType::EN_EventType& iType) const {

    // Retrieve the ProgressStatus structure corresponding to the
    // given event type
    ProgressStatusMap_T::const_iterator itProgressStatus =
      _progressStatusMap.find (iType);
    if (itProgressStatus == _progressStatusMap.end()) {
      //
      STDAIR_LOG_ERROR ("No ProgressStatus structure can be retrieved in the "
                        << "EventQueue: " << display());
      assert (false);
    }

    const stdair::ProgressStatus& lProgressStatus = itProgressStatus->second;
    return lProgressStatus.progress();
  }

  // //////////////////////////////////////////////////////////////////////
  stdair::ProgressStatusSet EventQueue::popEvent (stdair::EventStruct& ioEventStruct) {

    if (_eventList.empty() == true) {
      std::ostringstream oStr;
      oStr << "The event queue '" << describeKey() << "' is empty. "
	   << "No event can be popped.";
      //
      STDAIR_LOG_ERROR (oStr.str());
      throw EventQueueException (oStr.str());
    }

    /**
     * 1. Update the event queue itself.
     */
    // Get an iterator on the first event (sorted by date-time stamps)
    stdair::EventList_T::iterator itEvent = _eventList.begin();

    /**
     * Extract (a copy of) the corresponding Event structure. We make
     * a copy here, as the original EventStruct structure is removed
     * from the list (and erased). Moreover, the resulting EventStruct
     * structure will be returned by this method.
     */
    ioEventStruct = itEvent->second;
    // Retrieve the event type
    const stdair::EventType::EN_EventType& lEventType = ioEventStruct.getEventType();
    stdair::ProgressStatusSet oProgressStatusSet (lEventType);

    // Update the (current number part of the) overall progress status,
    // to account for the event that is being popped out of the event
    // queue.
    ++_progressStatus;

    // Remove the event, which has just been retrieved
    _eventList.erase (itEvent);

    /**
     * 2. Update the progress statuses held by the EventStruct structure.
     *
     * 2.1. Update the progress status specific to the event type (e.g.,
     *      booking request, optimisation notification).
     */

    // Retrieve the progress status specific to that event type
    stdair::ProgressStatus lEventTypeProgressStatus = getStatus (lEventType);

    // Increase the current number of events
    ++lEventTypeProgressStatus;

    // Store back the progress status
    setStatus (lEventType, lEventTypeProgressStatus);

    // Update the progress status of the progress status set, specific to
    // the event type.
    oProgressStatusSet.setTypeSpecificStatus (lEventTypeProgressStatus);

    /**
     * 2.2. Update the overall progress status.
     */
    // Update the overall progress status of the progress status set.
    oProgressStatusSet.setOverallStatus (_progressStatus);

    //
    return oProgressStatusSet;
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::addEvent (stdair::EventStruct& ioEventStruct) {
    bool insertionSucceeded =
      _eventList.insert (stdair::EventListElement_T (ioEventStruct.getEventTimeStamp(),
						     ioEventStruct)).second;

    /**
     * If the insertion has not been successful, try repeatedly until
     * the insertion becomes successful.
     *
     * The date-time is counted in milliseconds (1e-3 second). Hence,
     * one thousand (1e3) of attempts correspond to 1 second.
     *
     * The check on one thousand (1e3) is made in order to avoid
     * potential infinite loops. In such case, however, an assertion
     * will fail: it is always better that an assertion fails rather
     * than entering an infinite loop.
     */
    const unsigned int idx = 0;
    while (insertionSucceeded == false && idx != 1e3) {
      // Increment the date-time stamp (expressed in milliseconds)
      ioEventStruct.incrementEventTimeStamp();

      // Retry to insert into the event queue
      insertionSucceeded =
        _eventList.insert (stdair::EventListElement_T (ioEventStruct.getEventTimeStamp(),
						       ioEventStruct)).second;
    }
    assert (idx != 1e3);

    return insertionSucceeded;
  }

  // //////////////////////////////////////////////////////////////////////
  bool EventQueue::hasEventDateTime (const stdair::DateTime_T& iDateTime) {

    bool hasSearchEventBeenSucessful = true;

    /**
     * Compute the number of milliseconds between the
     * date-time of the event and DEFAULT_EVENT_OLDEST_DATETIME
     * (as of Feb. 2011, that date is set to Jan. 1, 2010).
     */
    const stdair::Duration_T lDuration =
      iDateTime - stdair::DEFAULT_EVENT_OLDEST_DATETIME;
    const stdair::LongDuration_T lDateTimeStamp =
      lDuration.total_milliseconds();

    // Searches the container for an element with iDateTime as key
    stdair::EventList_T::iterator itEvent =
      _eventList.find (lDateTimeStamp);

    // An iterator to map::end means the specified key has not found in the
    // container.
    if (itEvent == _eventList.end()) {
      hasSearchEventBeenSucessful = false;
    }

    return hasSearchEventBeenSucessful;

  }

}
<|endoftext|>


<commit_before>/*

 * code.cpp
 *
 *  Created on: Oct 30, 2015
 *      Author: (Bu)nn

 TODO

 * Background Subtration to get just the hand (binarized image)
 *

 */

#include <core/cvdef.h>
#include <core/cvstd.hpp>
#include <core/cvstd.inl.hpp>
#include <core/mat.hpp>
#include <core/mat.inl.hpp>
#include <core/matx.hpp>
#include <core/ptr.inl.hpp>
#include <core/types.hpp>
#include <core.hpp>
#include <highgui.hpp>
#include <imgproc/types_c.h>
#include <imgproc.hpp>
#include <video/background_segm.hpp>
#include <videoio.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <utility>
#include <vector>

using namespace cv;
using namespace std;

Mat frame; //current frame
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
Mat back;
Ptr<BackgroundSubtractorMOG2> pMOG2; //MOG2 Background subtractor
vector<pair<Point, double>> palm_centers;

//This function returns the square of the euclidean distance between 2 points.
double dist(Point x, Point y) {
  return (x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y);
}

Mat pre_processing(Mat frame) {

  GaussianBlur(frame, frame, Size(7, 7), 10, 10);
  Mat gray_scale;
  cvtColor(frame, gray_scale, COLOR_BGR2GRAY, 1);

  Mat element = (Mat_<uchar>(3, 3) << 0, 1, 0, 1, 1, 1, 0, 1, 0);
  morphologyEx(gray_scale, gray_scale, MORPH_OPEN, element);

  return gray_scale;
}

Mat bg_subtraction(VideoCapture cap, Mat frame) {
  //update the background model
  pMOG2->apply(frame, fgMaskMOG2);

  //Get background image to display it
  pMOG2->getBackgroundImage(back);
  pMOG2->setDetectShadows(0);
  pMOG2->setNMixtures(3);
  return fgMaskMOG2;
}

//This function returns the radius and the center of the circle given 3 points
//If a circle cannot be formed , it returns a zero radius circle centered at (0,0)
pair<Point, double> circleFromPoints(Point p1, Point p2, Point p3) {
  double offset = pow(p2.x, 2) + pow(p2.y, 2);
  double bc = (pow(p1.x, 2) + pow(p1.y, 2) - offset) / 2.0;
  double cd = (offset - pow(p3.x, 2) - pow(p3.y, 2)) / 2.0;
  double det = (p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p2.y);
  double TOL = 0.0000001;
  if (abs(det) < TOL) {
    cout << "POINTS TOO CLOSE" << endl;
    return make_pair(Point(0, 0), 0);
  }

  double idet = 1 / det;
  double centerx = (bc * (p2.y - p3.y) - cd * (p1.y - p2.y)) * idet;
  double centery = (cd * (p1.x - p2.x) - bc * (p2.x - p3.x)) * idet;
  double radius = sqrt(pow(p2.x - centerx, 2) + pow(p2.y - centery, 2));

  return make_pair(Point(centerx, centery), radius);
}

Mat contouring(Mat binarized, Mat pre_processed) {
  vector<vector<Point>> contours;

  //Find the contours in the foreground

  findContours(binarized, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
  imshow("Binary", binarized);

  for (int i = 0; i < contours.size(); i++)
    //Ignore all small insignificant areas
    if (contourArea(contours[i]) >= 5000) {
      //Draw contour
      vector<vector<Point>> tcontours;
      tcontours.push_back(contours[i]);
      drawContours(pre_processed, tcontours, -1, Scalar(0, 0, 255), 2);

      //Detect Hull in current contour
      vector<vector<Point> > hulls(1);
      vector<vector<int> > hullsI(1);
      convexHull(Mat(tcontours[0]), hulls[0], false);
      convexHull(Mat(tcontours[0]), hullsI[0], false);
      drawContours(pre_processed, hulls, -1, Scalar(0, 255, 0), 2);

      //Find minimum area rectangle to enclose hand
      RotatedRect rect = minAreaRect(Mat(tcontours[0]));

      //Find Convex Defects
      vector<Vec4i> defects;
      if (hullsI[0].size() > 0) {
        Point2f rect_points[4];
        rect.points(rect_points);
        for (int j = 0; j < 4; j++)
          line(pre_processed, rect_points[j], rect_points[(j + 1) % 4],
              Scalar(255, 0, 0), 1, 8);
        Point rough_palm_center;
        convexityDefects(tcontours[0], hullsI[0], defects);
        if (defects.size() >= 3) {
          vector<Point> palm_points;
          for (int j = 0; j < defects.size(); j++) {
            int startidx = defects[j][0];
            Point ptStart(tcontours[0][startidx]);
            int endidx = defects[j][1];
            Point ptEnd(tcontours[0][endidx]);
            int faridx = defects[j][2];
            Point ptFar(tcontours[0][faridx]);
            //Sum up all the hull and defect points to compute average
            rough_palm_center += ptFar + ptStart + ptEnd;
            palm_points.push_back(ptFar);
            palm_points.push_back(ptStart);
            palm_points.push_back(ptEnd);
          }

          //Get palm center by 1st getting the average of all defect points, this is the rough palm center,
          //Then U chose the closest 3 points ang get the circle radius and center formed from them which is the palm center.
          rough_palm_center.x /= defects.size() * 3;
          rough_palm_center.y /= defects.size() * 3;
          Point closest_pt = palm_points[0];
          vector<pair<double, int> > distvec;
          for (int i = 0; i < palm_points.size(); i++)
            distvec.push_back(
                make_pair(dist(rough_palm_center, palm_points[i]), i));
          sort(distvec.begin(), distvec.end());

          //Keep choosing 3 points till you find a circle with a valid radius
          //As there is a high chance that the closes points might be in a linear line or too close that it forms a very large circle
          pair<Point, double> soln_circle;
          for (int i = 0; i + 2 < distvec.size(); i++) {
            Point p1 = palm_points[distvec[i + 0].second];
            Point p2 = palm_points[distvec[i + 1].second];
            Point p3 = palm_points[distvec[i + 2].second];
            soln_circle = circleFromPoints(p1, p2, p3); //Final palm center,radius
            if (soln_circle.second != 0)
              break;
          }

          //Find avg palm centers for the last few frames to stabilize its centers, also find the avg radius
          palm_centers.push_back(soln_circle);
          if (palm_centers.size() > 10)
            palm_centers.erase(palm_centers.begin());

          Point palm_center;
          double radius = 0;
          for (int i = 0; i < palm_centers.size(); i++) {
            palm_center += palm_centers[i].first;
            radius += palm_centers[i].second;
          }
          palm_center.x /= palm_centers.size();
          palm_center.y /= palm_centers.size();
          radius /= palm_centers.size();

          //Draw the palm center and the palm circle
          //The size of the palm gives the depth of the hand
          circle(frame, palm_center, 5, Scalar(144, 144, 255), 3);
          circle(frame, palm_center, radius, Scalar(144, 144, 255), 2);

          //Detect fingers by finding points that form an almost isosceles triangle with certain thesholds
          int no_of_fingers = 0;
          for (int j = 0; j < defects.size(); j++) {
            int startidx = defects[j][0];
            Point ptStart(tcontours[0][startidx]);
            int endidx = defects[j][1];
            Point ptEnd(tcontours[0][endidx]);
            int faridx = defects[j][2];
            Point ptFar(tcontours[0][faridx]);
            //X o--------------------------o Y
            double Xdist = sqrt(dist(palm_center, ptFar));
            double Ydist = sqrt(dist(palm_center, ptStart));
            double length = sqrt(dist(ptFar, ptStart));

            double retLength = sqrt(dist(ptEnd, ptFar));
            //Play with these thresholds to improve performance
            if (length <= 3 * radius && Ydist >= 0.4 * radius && length >= 10
                && retLength >= 10
                && max(length, retLength) / min(length, retLength) >= 0.8)
              if (min(Xdist, Ydist) / max(Xdist, Ydist) <= 0.8) {
                if ((Xdist >= 0.1 * radius && Xdist <= 1.3 * radius
                    && Xdist < Ydist)
                    || (Ydist >= 0.1 * radius && Ydist <= 1.3 * radius
                        && Xdist > Ydist))
                  line(frame, ptEnd, ptFar, Scalar(0, 255, 0), 1), no_of_fingers++;
              }
          }
           no_of_fingers = min(5, no_of_fingers);
//           cout << "NO OF FINGERS: " << no_of_fingers << endl;

           if(no_of_fingers == 1){
             cout << "NO OF FINGERS: " << no_of_fingers << endl;
             // Draw a line
             line( frame, Point( 15, 20 ), Point( 70, 50), Scalar( 110, 220, 0 ),  2, 8 );
//             imshow("Line",frame);
           }
           }
      }

    }

  return pre_processed;
}

int process_video() {

  VideoCapture cap(0); // open the default camera
  if (!cap.isOpened())  // check if we succeeded
    return -1;

  for (;;) {

    //Capture the Frame and convert it to Grayscale
    cap >> frame; // get a new frame from camera

    Mat foreground;
    Mat i1 = pre_processing(frame);
    Mat bg_sub = bg_subtraction(cap, i1);
    absdiff(i1, back, foreground);

    Mat fg_binarized;
    threshold(foreground, fg_binarized, 0, 255, THRESH_BINARY | THRESH_OTSU);

    Mat contour = contouring(fg_binarized,frame);

    imshow("Frame", contour);
//    imshow("FG Mask MOG 2",bg_sub);
//    imshow("Background",back);
    if (waitKey(30) >= 0)
      break;
  }

  return 0;
}

int main(int, char**) {
  //create Background Subtractor objects
  pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
  int res = process_video();
  return 0;
}

<commit_msg>mouse<commit_after>/*

 * code.cpp
 *
 *  Created on: Oct 30, 2015
 *      Author: (Bu)nn

 TODO

 * Background Subtration to get just the hand (binarized image)
 *

 */

#include <core/cvdef.h>
#include <core/cvstd.hpp>
#include <core/cvstd.inl.hpp>
#include <core/mat.hpp>
#include <core/mat.inl.hpp>
#include <core/matx.hpp>
#include <core/ptr.inl.hpp>
#include <core/types.hpp>
#include <core.hpp>
#include <highgui.hpp>
#include <imgproc/types_c.h>
#include <imgproc.hpp>
#include <video/background_segm.hpp>
#include <videoio.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <utility>
#include <vector>

using namespace cv;
using namespace std;

Mat frame; //current frame
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
Mat back;
Ptr<BackgroundSubtractorMOG2> pMOG2; //MOG2 Background subtractor
vector<pair<Point, double>> palm_centers;

//This function returns the square of the euclidean distance between 2 points.
double dist(Point x, Point y) {
  return (x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y);
}

/*
void CallBackFunc(int event, int x, int y, int d, void *ptr)
{
     if  ( event == EVENT_LBUTTONDOWN )
     {
          cout << "Left button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
     }
     else if  ( event == EVENT_RBUTTONDOWN )
     {
          cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
     }
     else if  ( event == EVENT_MBUTTONDOWN )
     {
          cout << "Middle button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
     }
     else if ( event == EVENT_MOUSEMOVE )
     {
          cout << "Mouse move over the window - position (" << x << ", " << y << ")" << endl;

     }

     Point*p = (Point*)ptr;
     p->x = x;
     p->y = y;
}
*/

void on_mouse( int e, int x, int y, int d, void *ptr )
{
    Point*p = (Point*)ptr;
    p->x = x;
    p->y = y;
}

Mat pre_processing(Mat frame) {

  GaussianBlur(frame, frame, Size(7, 7), 10, 10);
  Mat gray_scale;
  cvtColor(frame, gray_scale, COLOR_BGR2GRAY, 1);

  Mat element = (Mat_<uchar>(3, 3) << 0, 1, 0, 1, 1, 1, 0, 1, 0);
  morphologyEx(gray_scale, gray_scale, MORPH_OPEN, element);

  return gray_scale;
}

Mat bg_subtraction(VideoCapture cap, Mat frame) {
  //update the background model
  pMOG2->apply(frame, fgMaskMOG2);

  //Get background image to display it
  pMOG2->getBackgroundImage(back);
  pMOG2->setDetectShadows(0);
  pMOG2->setNMixtures(3);
  return fgMaskMOG2;
}

//This function returns the radius and the center of the circle given 3 points
//If a circle cannot be formed , it returns a zero radius circle centered at (0,0)
pair<Point, double> circleFromPoints(Point p1, Point p2, Point p3) {
  double offset = pow(p2.x, 2) + pow(p2.y, 2);
  double bc = (pow(p1.x, 2) + pow(p1.y, 2) - offset) / 2.0;
  double cd = (offset - pow(p3.x, 2) - pow(p3.y, 2)) / 2.0;
  double det = (p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p2.y);
  double TOL = 0.0000001;
  if (abs(det) < TOL) {
    cout << "POINTS TOO CLOSE" << endl;
    return make_pair(Point(0, 0), 0);
  }

  double idet = 1 / det;
  double centerx = (bc * (p2.y - p3.y) - cd * (p1.y - p2.y)) * idet;
  double centery = (cd * (p1.x - p2.x) - bc * (p2.x - p3.x)) * idet;
  double radius = sqrt(pow(p2.x - centerx, 2) + pow(p2.y - centery, 2));

  return make_pair(Point(centerx, centery), radius);
}

Mat contouring(Mat binarized, Mat pre_processed) {
  vector<vector<Point>> contours;

  //Find the contours in the foreground

  findContours(binarized, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
  imshow("Binary", binarized);

  for (int i = 0; i < contours.size(); i++)
    //Ignore all small insignificant areas
    if (contourArea(contours[i]) >= 5000) {
      //Draw contour
      vector<vector<Point>> tcontours;
      tcontours.push_back(contours[i]);
      drawContours(pre_processed, tcontours, -1, Scalar(0, 0, 255), 2);

      //Detect Hull in current contour
      vector<vector<Point> > hulls(1);
      vector<vector<int> > hullsI(1);
      convexHull(Mat(tcontours[0]), hulls[0], false);
      convexHull(Mat(tcontours[0]), hullsI[0], false);
      drawContours(pre_processed, hulls, -1, Scalar(0, 255, 0), 2);

      //Find minimum area rectangle to enclose hand
      RotatedRect rect = minAreaRect(Mat(tcontours[0]));

      //Find Convex Defects
      vector<Vec4i> defects;
      if (hullsI[0].size() > 0) {
        Point2f rect_points[4];
        rect.points(rect_points);
        for (int j = 0; j < 4; j++)
          line(pre_processed, rect_points[j], rect_points[(j + 1) % 4],
              Scalar(255, 0, 0), 1, 8);
        Point rough_palm_center;
        convexityDefects(tcontours[0], hullsI[0], defects);
        if (defects.size() >= 3) {
          vector<Point> palm_points;
          for (int j = 0; j < defects.size(); j++) {
            int startidx = defects[j][0];
            Point ptStart(tcontours[0][startidx]);
            int endidx = defects[j][1];
            Point ptEnd(tcontours[0][endidx]);
            int faridx = defects[j][2];
            Point ptFar(tcontours[0][faridx]);
            //Sum up all the hull and defect points to compute average
            rough_palm_center += ptFar + ptStart + ptEnd;
            palm_points.push_back(ptFar);
            palm_points.push_back(ptStart);
            palm_points.push_back(ptEnd);
          }

          //Get palm center by 1st getting the average of all defect points, this is the rough palm center,
          //Then U chose the closest 3 points ang get the circle radius and center formed from them which is the palm center.
          rough_palm_center.x /= defects.size() * 3;
          rough_palm_center.y /= defects.size() * 3;
          Point closest_pt = palm_points[0];
          vector<pair<double, int> > distvec;
          for (int i = 0; i < palm_points.size(); i++)
            distvec.push_back(
                make_pair(dist(rough_palm_center, palm_points[i]), i));
          sort(distvec.begin(), distvec.end());

          //Keep choosing 3 points till you find a circle with a valid radius
          //As there is a high chance that the closes points might be in a linear line or too close that it forms a very large circle
          pair<Point, double> soln_circle;
          for (int i = 0; i + 2 < distvec.size(); i++) {
            Point p1 = palm_points[distvec[i + 0].second];
            Point p2 = palm_points[distvec[i + 1].second];
            Point p3 = palm_points[distvec[i + 2].second];
            soln_circle = circleFromPoints(p1, p2, p3); //Final palm center,radius
            if (soln_circle.second != 0)
              break;
          }

          //Find avg palm centers for the last few frames to stabilize its centers, also find the avg radius
          palm_centers.push_back(soln_circle);
          if (palm_centers.size() > 10)
            palm_centers.erase(palm_centers.begin());

          Point palm_center;
          double radius = 0;

          //averaging all palm centres
          for (int i = 0; i < palm_centers.size(); i++) {
            palm_center += palm_centers[i].first;
            radius += palm_centers[i].second;
          }
          palm_center.x /= palm_centers.size();
          palm_center.y /= palm_centers.size();
          radius /= palm_centers.size();

          //Draw the palm center and the palm circle
          //The size of the palm gives the depth of the hand
          circle(frame, palm_center, 5, Scalar(144, 144, 255), 3);
          circle(frame, palm_center, radius, Scalar(144, 144, 255), 2);

          //Detect fingers by finding points that form an almost isosceles triangle with certain thesholds
          int no_of_fingers = 0;
          for (int j = 0; j < defects.size(); j++) {
            int startidx = defects[j][0];
            Point ptStart(tcontours[0][startidx]);
            int endidx = defects[j][1];
            Point ptEnd(tcontours[0][endidx]);
            int faridx = defects[j][2];
            Point ptFar(tcontours[0][faridx]);
            //X o--------------------------o Y
            double Xdist = sqrt(dist(palm_center, ptFar));
            double Ydist = sqrt(dist(palm_center, ptStart));
            double length = sqrt(dist(ptFar, ptStart));

            double retLength = sqrt(dist(ptEnd, ptFar));
            //Play with these thresholds to improve performance
            if (length <= 3 * radius && Ydist >= 0.4 * radius && length >= 10
                && retLength >= 10
                && max(length, retLength) / min(length, retLength) >= 0.8)
              if (min(Xdist, Ydist) / max(Xdist, Ydist) <= 0.8) {
                if ((Xdist >= 0.1 * radius && Xdist <= 1.3 * radius
                    && Xdist < Ydist)
                    || (Ydist >= 0.1 * radius && Ydist <= 1.3 * radius
                        && Xdist > Ydist))
                  line(frame, ptEnd, ptFar, Scalar(0, 255, 0), 1), no_of_fingers++;
              }
          }
           no_of_fingers = min(5, no_of_fingers);
           cout << "NO OF FINGERS: " << no_of_fingers << endl;

           setMouseCallback("Frame", on_mouse, &palm_center);

/*           if(no_of_fingers == 1){
             cout << "NO OF FINGERS: " << no_of_fingers << endl;
             // Draw a line
             line( frame, palm_center,palm_centers[10].first, Scalar( 110, 220, 0 ),  2, 8 );

//             imshow("Line",frame);
           }*/
           }
      }

    }

  return pre_processed;
}

int process_video() {

  VideoCapture cap(0); // open the default camera
  if (!cap.isOpened())  // check if we succeeded
    return -1;

  for (;;) {

    //Capture the Frame and convert it to Grayscale
    cap >> frame; // get a new frame from camera

    Mat foreground;
    Mat i1 = pre_processing(frame);
    Mat bg_sub = bg_subtraction(cap, i1);
    absdiff(i1, back, foreground);

    Mat fg_binarized;
    threshold(foreground, fg_binarized, 0, 255, THRESH_BINARY | THRESH_OTSU);

    Mat contour = contouring(fg_binarized,frame);

    imshow("Frame", contour);
//    imshow("FG Mask MOG 2",bg_sub);
//    imshow("Background",back);
    if (waitKey(30) >= 0)
      break;
  }

  return 0;
}

int main(int, char**) {
  //create Background Subtractor objects
  pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
  int res = process_video();
  //Create a window
  namedWindow("Frame", 1);
  return 0;
}

<|endoftext|>


<commit_before>#include "vga/timing.h"

namespace vga {

Timing const timing_vesa_640x480_60hz = {
  {
    8000000,  // external crystal Hz
    8,        // divide down to 1Mhz
    201,      // multiply up to 201MHz VCO
    2,        // divide by 2 for 100.5MHz CPU clock
    3,        // divide by 3 for 48MHz-ish SDIO clock
    1,        // divide CPU clock by 1 for 100.5MHz AHB clock
    4,        // divide CPU clock by 4 for 25.125MHz APB1 clock.
    2,        // divide CPU clock by 2 for 50.25MHz APB2 clock.

    3,        // 3 wait states for 100.5MHz at 3.3V.
  },

  800,   // line_pixels
  96,    // sync_pixels
  48,    // back_porch_pixels
  22,    // video_lead
  640,   // video_pixels,
  Timing::Polarity::negative,

  10,
  10 + 2,
  10 + 2 + 33,
  10 + 2 + 33 + 480,
  Timing::Polarity::negative,
};

Timing const timing_vesa_800x600_60hz = {
  {
    8000000,  // external crystal Hz
    8,        // divide down to 1Mhz
    320,      // multiply up to 320MHz VCO
    2,        // divide by 2 for 160MHz CPU clock
    7,        // divide by 7 for 48MHz-ish SDIO clock
    1,        // divide CPU clock by 1 for 160MHz AHB clock
    4,        // divide CPU clock by 4 for 40MHz APB1 clock.
    2,        // divide CPU clock by 2 for 80MHz APB2 clock.

    5,        // 5 wait states for 160MHz at 3.3V.
  },

  1056,  // line_pixels
  128,   // sync_pixels
  88,    // back_porch_pixels
  22,    // video_lead
  800,   // video_pixels,
  Timing::Polarity::positive,

  1,
  1 + 4,
  1 + 4 + 23,
  1 + 4 + 23 + 600,
  Timing::Polarity::positive,
};

}  // namespace vga
<commit_msg>vga: adjust 800x600 timing.<commit_after>#include "vga/timing.h"

namespace vga {

Timing const timing_vesa_640x480_60hz = {
  {
    8000000,  // external crystal Hz
    8,        // divide down to 1Mhz
    201,      // multiply up to 201MHz VCO
    2,        // divide by 2 for 100.5MHz CPU clock
    3,        // divide by 3 for 48MHz-ish SDIO clock
    1,        // divide CPU clock by 1 for 100.5MHz AHB clock
    4,        // divide CPU clock by 4 for 25.125MHz APB1 clock.
    2,        // divide CPU clock by 2 for 50.25MHz APB2 clock.

    3,        // 3 wait states for 100.5MHz at 3.3V.
  },

  800,   // line_pixels
  96,    // sync_pixels
  48,    // back_porch_pixels
  22,    // video_lead
  640,   // video_pixels,
  Timing::Polarity::negative,

  10,
  10 + 2,
  10 + 2 + 33,
  10 + 2 + 33 + 480,
  Timing::Polarity::negative,
};

Timing const timing_vesa_800x600_60hz = {
  {
    8000000,  // external crystal Hz
    8,        // divide down to 1Mhz
    320,      // multiply up to 320MHz VCO
    2,        // divide by 2 for 160MHz CPU clock
    7,        // divide by 7 for 48MHz-ish SDIO clock
    1,        // divide CPU clock by 1 for 160MHz AHB clock
    4,        // divide CPU clock by 4 for 40MHz APB1 clock.
    2,        // divide CPU clock by 2 for 80MHz APB2 clock.

    5,        // 5 wait states for 160MHz at 3.3V.
  },

  1056,  // line_pixels
  128,   // sync_pixels
  88,    // back_porch_pixels
  19,    // video_lead
  800,   // video_pixels,
  Timing::Polarity::positive,

  1,
  1 + 4,
  1 + 4 + 23,
  1 + 4 + 23 + 600,
  Timing::Polarity::positive,
};

}  // namespace vga
<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright 2015 Cloudius Systems
 */

#ifndef APPS_HTTPD_HTTPD_HH_
#define APPS_HTTPD_HTTPD_HH_

#include "http/request_parser.hh"
#include "http/request.hh"
#include "core/reactor.hh"
#include "core/sstring.hh"
#include <experimental/string_view>
#include "core/app-template.hh"
#include "core/circular_buffer.hh"
#include "core/distributed.hh"
#include "core/queue.hh"
#include "core/future-util.hh"
#include "core/scollectd.hh"
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <boost/intrusive/list.hpp>
#include "reply.hh"
#include "http/routes.hh"

namespace httpd {

class http_server;
class http_stats;

using namespace std::chrono_literals;

class http_stats {
    scollectd::registrations _regs;
public:
    http_stats(http_server& server);
};

class http_server {
    std::vector<server_socket> _listeners;
    http_stats _stats { *this };
    uint64_t _total_connections = 0;
    uint64_t _current_connections = 0;
    uint64_t _requests_served = 0;
    uint64_t _connections_being_accepted = 0;
    sstring _date = http_date();
    timer<> _date_format_timer { [this] {_date = http_date();} };
    bool _stopping = false;
    promise<> _all_connections_stopped;
    future<> _stopped = _all_connections_stopped.get_future();
private:
    void maybe_idle() {
        if (_stopping && !_connections_being_accepted && !_current_connections) {
            _all_connections_stopped.set_value();
        }
    }
public:
    routes _routes;

    http_server() {
        _date_format_timer.arm_periodic(1s);
    }
    future<> listen(ipv4_addr addr) {
        listen_options lo;
        lo.reuse_address = true;
        _listeners.push_back(engine().listen(make_ipv4_address(addr), lo));
        _stopped = when_all(std::move(_stopped), do_accepts(_listeners.size() - 1)).discard_result();
        return make_ready_future<>();
    }
    future<> stop() {
        _stopping = true;
        for (auto&& l : _listeners) {
            l.abort_accept();
        }
        for (auto&& c : _connections) {
            c.shutdown();
        }
        return std::move(_stopped);
    }
    future<> do_accepts(int which) {
        ++_connections_being_accepted;
        return _listeners[which].accept().then_wrapped(
                [this, which] (future<connected_socket, socket_address> f_cs_sa) mutable {
                    --_connections_being_accepted;
                    if (_stopping) {
                        maybe_idle();
                        return;
                    }
                    auto cs_sa = f_cs_sa.get();
                    auto conn = new connection(*this, std::get<0>(std::move(cs_sa)), std::get<1>(std::move(cs_sa)));
                    conn->process().then_wrapped([this, conn] (auto&& f) {
                                delete conn;
                                try {
                                    f.get();
                                } catch (std::exception& ex) {
                                    std::cerr << "request error " << ex.what() << std::endl;
                                }
                            });
                    do_accepts(which);
                }).then_wrapped([] (auto f) {
            try {
                f.get();
            } catch (std::exception& ex) {
                std::cerr << "accept failed: " << ex.what() << std::endl;
            }
        });
    }
    class connection : public boost::intrusive::list_base_hook<> {
        http_server& _server;
        connected_socket _fd;
        input_stream<char> _read_buf;
        output_stream<char> _write_buf;
        static constexpr size_t limit = 4096;
        using tmp_buf = temporary_buffer<char>;
        http_request_parser _parser;
        std::unique_ptr<request> _req;
        std::unique_ptr<reply> _resp;
        // null element marks eof
        queue<std::unique_ptr<reply>> _replies { 10 };bool _done = false;
    public:
        connection(http_server& server, connected_socket&& fd,
                socket_address addr)
                : _server(server), _fd(std::move(fd)), _read_buf(_fd.input()), _write_buf(
                        _fd.output()) {
            ++_server._total_connections;
            ++_server._current_connections;
            _server._connections.push_back(*this);
        }
        ~connection() {
            --_server._current_connections;
            _server._connections.erase(_server._connections.iterator_to(*this));
            _server.maybe_idle();
        }
        future<> process() {
            // Launch read and write "threads" simultaneously:
            return when_all(read(), respond()).then(
                    [] (std::tuple<future<>, future<>> joined) {
                        // FIXME: notify any exceptions in joined?
                        return make_ready_future<>();
                    });
        }
        void shutdown() {
            _fd.shutdown_input();
            _fd.shutdown_output();
        }
        future<> read() {
            return do_until([this] {return _done;}, [this] {
                return read_one();
            }).then_wrapped([this] (future<> f) {
                // swallow error
                // FIXME: count it?
                    return _replies.push_eventually( {});
                });
        }
        future<> read_one() {
            _parser.init();
            return _read_buf.consume(_parser).then([this] () mutable {
                if (_parser.eof()) {
                    _done = true;
                    return make_ready_future<>();
                }
                ++_server._requests_served;
                std::unique_ptr<httpd::request> req = _parser.get_parsed_request();

                return _replies.not_full().then([req = std::move(req), this] () mutable {
                    return generate_reply(std::move(req));
                }).then([this](bool done) {
                    _done = done;
                });
            });
        }
        future<> respond() {
            return _replies.pop_eventually().then(
                    [this] (std::unique_ptr<reply> resp) {
                        if (!resp) {
                            // eof
                            return make_ready_future<>();
                        }
                        _resp = std::move(resp);
                        return start_response().then([this] {
                                    return respond();
                                });
                    });
        }
        future<> start_response() {
            _resp->_headers["Server"] = "Seastar httpd";
            _resp->_headers["Date"] = _server._date;
            _resp->_headers["Content-Length"] = to_sstring(
                    _resp->_content.size());
            return _write_buf.write(_resp->_response_line.begin(),
                    _resp->_response_line.size()).then([this] {
                return write_reply_headers(_resp->_headers.begin());
            }).then([this] {
                return _write_buf.write("\r\n", 2);
            }).then([this] {
                return write_body();
            }).then([this] {
                return _write_buf.flush();
            }).then([this] {
                _resp.reset();
            });
        }
        future<> write_reply_headers(
                std::unordered_map<sstring, sstring>::iterator hi) {
            if (hi == _resp->_headers.end()) {
                return make_ready_future<>();
            }
            return _write_buf.write(hi->first.begin(), hi->first.size()).then(
                    [this] {
                        return _write_buf.write(": ", 2);
                    }).then([hi, this] {
                return _write_buf.write(hi->second.begin(), hi->second.size());
            }).then([this] {
                return _write_buf.write("\r\n", 2);
            }).then([hi, this] () mutable {
                return write_reply_headers(++hi);
            });
        }

        static short hex_to_byte(char c) {
            if (c >='a' && c <= 'z') {
                return c - 'a' + 10;
            } else if (c >='A' && c <= 'Z') {
                return c - 'A' + 10;
            }
            return c - '0';
        }

        /**
         * Convert a hex encoded 2 bytes substring to char
         */
        static char hexstr_to_char(const std::experimental::string_view& in, size_t from) {

            return static_cast<char>(hex_to_byte(in[from]) * 16 + hex_to_byte(in[from + 1]));
        }

        /**
         * URL_decode a substring and place it in the given out sstring
         */
        static bool url_decode(const std::experimental::string_view& in, sstring& out) {
            size_t pos = 0;
            char buff[in.length()];
            for (size_t i = 0; i < in.length(); ++i) {
                if (in[i] == '%') {
                    if (i + 3 <= in.size()) {
                        buff[pos++] = hexstr_to_char(in, i + 1);
                        i += 2;
                    } else {
                        return false;
                    }
                } else if (in[i] == '+') {
                    buff[pos++] = ' ';
                } else {
                    buff[pos++] = in[i];
                }
            }
            out = sstring(buff, pos);
            return true;
        }

        /**
         * Add a single query parameter to the parameter list
         */
        static void add_param(request& req, const std::experimental::string_view& param) {
            size_t split = param.find('=');

            if (split >= param.length() - 1) {
                sstring key;
                if (url_decode(param.substr(0,split) , key)) {
                    req.query_parameters[key] = "";
                }
            } else {
                sstring key;
                sstring value;
                if (url_decode(param.substr(0,split), key)
                        && url_decode(param.substr(split + 1), value)) {
                    req.query_parameters[key] = value;
                }
            }

        }

        /**
         * Set the query parameters in the request objects.
         * query param appear after the question mark and are separated
         * by the ampersand sign
         */
        static sstring set_query_param(request& req) {
            size_t pos = req._url.find('?');
            if (pos == sstring::npos) {
                return req._url;
            }
            size_t curr = pos + 1;
            size_t end_param;
            std::experimental::string_view url = req._url;
            while ((end_param = req._url.find('&', curr)) != sstring::npos) {
                add_param(req, url.substr(curr, end_param - curr) );
                curr = end_param + 1;
            }
            add_param(req, url.substr(curr));
            return req._url.substr(0, pos);
        }

        future<bool> generate_reply(std::unique_ptr<request> req) {
            auto resp = std::make_unique<reply>();
            bool conn_keep_alive = false;
            bool conn_close = false;
            auto it = req->_headers.find("Connection");
            if (it != req->_headers.end()) {
                if (it->second == "Keep-Alive") {
                    conn_keep_alive = true;
                } else if (it->second == "Close") {
                    conn_close = true;
                }
            }
            bool should_close;
            // TODO: Handle HTTP/2.0 when it releases
            resp->set_version(req->_version);

            if (req->_version == "1.0") {
                if (conn_keep_alive) {
                    resp->_headers["Connection"] = "Keep-Alive";
                }
                should_close = !conn_keep_alive;
            } else if (req->_version == "1.1") {
                should_close = conn_close;
            } else {
                // HTTP/0.9 goes here
                should_close = true;
            }
            sstring url = set_query_param(*req.get());
            return _server._routes.handle(url, std::move(req), std::move(resp)).
            // Caller guarantees enough room
            then([this, should_close](std::unique_ptr<reply> rep) {
                this->_replies.push(std::move(rep));
                return make_ready_future<bool>(should_close);
            });
        }
        future<> write_body() {
            return _write_buf.write(_resp->_content.begin(),
                    _resp->_content.size());
        }
    };
    uint64_t total_connections() const {
        return _total_connections;
    }
    uint64_t current_connections() const {
        return _current_connections;
    }
    uint64_t requests_served() const {
        return _requests_served;
    }
    static sstring http_date() {
        auto t = ::time(nullptr);
        struct tm tm;
        gmtime_r(&t, &tm);
        char tmp[100];
        strftime(tmp, sizeof(tmp), "%d %b %Y %H:%M:%S GMT", &tm);
        return tmp;
    }
private:
    boost::intrusive::list<connection> _connections;
};

/*
 * A helper class to start, set and listen an http server
 * typical use would be:
 *
 * auto server = new http_server_control();
 *                 server->start().then([server] {
 *                 server->set_routes(set_routes);
 *              }).then([server, port] {
 *                  server->listen(port);
 *              }).then([port] {
 *                  std::cout << "Seastar HTTP server listening on port " << port << " ...\n";
 *              });
 */
class http_server_control {
    distributed<http_server>* _server_dist;
public:
    http_server_control() : _server_dist(new distributed<http_server>) {
    }

    future<> start() {
        return _server_dist->start();
    }

    future<> stop() {
        return _server_dist->stop();
    }

    future<> set_routes(std::function<void(routes& r)> fun) {
        return _server_dist->invoke_on_all([fun](http_server& server) {
            fun(server._routes);
        });
    }

    future<> listen(ipv4_addr addr) {
        return _server_dist->invoke_on_all(&http_server::listen, addr);
    }

    distributed<http_server>& server() {
        return *_server_dist;
    }
};

}

#endif /* APPS_HTTPD_HTTPD_HH_ */
<commit_msg>http: All http replies should have version set<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright 2015 Cloudius Systems
 */

#ifndef APPS_HTTPD_HTTPD_HH_
#define APPS_HTTPD_HTTPD_HH_

#include "http/request_parser.hh"
#include "http/request.hh"
#include "core/reactor.hh"
#include "core/sstring.hh"
#include <experimental/string_view>
#include "core/app-template.hh"
#include "core/circular_buffer.hh"
#include "core/distributed.hh"
#include "core/queue.hh"
#include "core/future-util.hh"
#include "core/scollectd.hh"
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <boost/intrusive/list.hpp>
#include "reply.hh"
#include "http/routes.hh"

namespace httpd {

class http_server;
class http_stats;

using namespace std::chrono_literals;

class http_stats {
    scollectd::registrations _regs;
public:
    http_stats(http_server& server);
};

class http_server {
    std::vector<server_socket> _listeners;
    http_stats _stats { *this };
    uint64_t _total_connections = 0;
    uint64_t _current_connections = 0;
    uint64_t _requests_served = 0;
    uint64_t _connections_being_accepted = 0;
    sstring _date = http_date();
    timer<> _date_format_timer { [this] {_date = http_date();} };
    bool _stopping = false;
    promise<> _all_connections_stopped;
    future<> _stopped = _all_connections_stopped.get_future();
private:
    void maybe_idle() {
        if (_stopping && !_connections_being_accepted && !_current_connections) {
            _all_connections_stopped.set_value();
        }
    }
public:
    routes _routes;

    http_server() {
        _date_format_timer.arm_periodic(1s);
    }
    future<> listen(ipv4_addr addr) {
        listen_options lo;
        lo.reuse_address = true;
        _listeners.push_back(engine().listen(make_ipv4_address(addr), lo));
        _stopped = when_all(std::move(_stopped), do_accepts(_listeners.size() - 1)).discard_result();
        return make_ready_future<>();
    }
    future<> stop() {
        _stopping = true;
        for (auto&& l : _listeners) {
            l.abort_accept();
        }
        for (auto&& c : _connections) {
            c.shutdown();
        }
        return std::move(_stopped);
    }
    future<> do_accepts(int which) {
        ++_connections_being_accepted;
        return _listeners[which].accept().then_wrapped(
                [this, which] (future<connected_socket, socket_address> f_cs_sa) mutable {
                    --_connections_being_accepted;
                    if (_stopping) {
                        maybe_idle();
                        return;
                    }
                    auto cs_sa = f_cs_sa.get();
                    auto conn = new connection(*this, std::get<0>(std::move(cs_sa)), std::get<1>(std::move(cs_sa)));
                    conn->process().then_wrapped([this, conn] (auto&& f) {
                                delete conn;
                                try {
                                    f.get();
                                } catch (std::exception& ex) {
                                    std::cerr << "request error " << ex.what() << std::endl;
                                }
                            });
                    do_accepts(which);
                }).then_wrapped([] (auto f) {
            try {
                f.get();
            } catch (std::exception& ex) {
                std::cerr << "accept failed: " << ex.what() << std::endl;
            }
        });
    }
    class connection : public boost::intrusive::list_base_hook<> {
        http_server& _server;
        connected_socket _fd;
        input_stream<char> _read_buf;
        output_stream<char> _write_buf;
        static constexpr size_t limit = 4096;
        using tmp_buf = temporary_buffer<char>;
        http_request_parser _parser;
        std::unique_ptr<request> _req;
        std::unique_ptr<reply> _resp;
        // null element marks eof
        queue<std::unique_ptr<reply>> _replies { 10 };bool _done = false;
    public:
        connection(http_server& server, connected_socket&& fd,
                socket_address addr)
                : _server(server), _fd(std::move(fd)), _read_buf(_fd.input()), _write_buf(
                        _fd.output()) {
            ++_server._total_connections;
            ++_server._current_connections;
            _server._connections.push_back(*this);
        }
        ~connection() {
            --_server._current_connections;
            _server._connections.erase(_server._connections.iterator_to(*this));
            _server.maybe_idle();
        }
        future<> process() {
            // Launch read and write "threads" simultaneously:
            return when_all(read(), respond()).then(
                    [] (std::tuple<future<>, future<>> joined) {
                        // FIXME: notify any exceptions in joined?
                        return make_ready_future<>();
                    });
        }
        void shutdown() {
            _fd.shutdown_input();
            _fd.shutdown_output();
        }
        future<> read() {
            return do_until([this] {return _done;}, [this] {
                return read_one();
            }).then_wrapped([this] (future<> f) {
                // swallow error
                // FIXME: count it?
                    return _replies.push_eventually( {});
                });
        }
        future<> read_one() {
            _parser.init();
            return _read_buf.consume(_parser).then([this] () mutable {
                if (_parser.eof()) {
                    _done = true;
                    return make_ready_future<>();
                }
                ++_server._requests_served;
                std::unique_ptr<httpd::request> req = _parser.get_parsed_request();

                return _replies.not_full().then([req = std::move(req), this] () mutable {
                    return generate_reply(std::move(req));
                }).then([this](bool done) {
                    _done = done;
                });
            });
        }
        future<> respond() {
            return _replies.pop_eventually().then(
                    [this] (std::unique_ptr<reply> resp) {
                        if (!resp) {
                            // eof
                            return make_ready_future<>();
                        }
                        _resp = std::move(resp);
                        return start_response().then([this] {
                                    return respond();
                                });
                    });
        }
        future<> start_response() {
            _resp->_headers["Server"] = "Seastar httpd";
            _resp->_headers["Date"] = _server._date;
            _resp->_headers["Content-Length"] = to_sstring(
                    _resp->_content.size());
            return _write_buf.write(_resp->_response_line.begin(),
                    _resp->_response_line.size()).then([this] {
                return write_reply_headers(_resp->_headers.begin());
            }).then([this] {
                return _write_buf.write("\r\n", 2);
            }).then([this] {
                return write_body();
            }).then([this] {
                return _write_buf.flush();
            }).then([this] {
                _resp.reset();
            });
        }
        future<> write_reply_headers(
                std::unordered_map<sstring, sstring>::iterator hi) {
            if (hi == _resp->_headers.end()) {
                return make_ready_future<>();
            }
            return _write_buf.write(hi->first.begin(), hi->first.size()).then(
                    [this] {
                        return _write_buf.write(": ", 2);
                    }).then([hi, this] {
                return _write_buf.write(hi->second.begin(), hi->second.size());
            }).then([this] {
                return _write_buf.write("\r\n", 2);
            }).then([hi, this] () mutable {
                return write_reply_headers(++hi);
            });
        }

        static short hex_to_byte(char c) {
            if (c >='a' && c <= 'z') {
                return c - 'a' + 10;
            } else if (c >='A' && c <= 'Z') {
                return c - 'A' + 10;
            }
            return c - '0';
        }

        /**
         * Convert a hex encoded 2 bytes substring to char
         */
        static char hexstr_to_char(const std::experimental::string_view& in, size_t from) {

            return static_cast<char>(hex_to_byte(in[from]) * 16 + hex_to_byte(in[from + 1]));
        }

        /**
         * URL_decode a substring and place it in the given out sstring
         */
        static bool url_decode(const std::experimental::string_view& in, sstring& out) {
            size_t pos = 0;
            char buff[in.length()];
            for (size_t i = 0; i < in.length(); ++i) {
                if (in[i] == '%') {
                    if (i + 3 <= in.size()) {
                        buff[pos++] = hexstr_to_char(in, i + 1);
                        i += 2;
                    } else {
                        return false;
                    }
                } else if (in[i] == '+') {
                    buff[pos++] = ' ';
                } else {
                    buff[pos++] = in[i];
                }
            }
            out = sstring(buff, pos);
            return true;
        }

        /**
         * Add a single query parameter to the parameter list
         */
        static void add_param(request& req, const std::experimental::string_view& param) {
            size_t split = param.find('=');

            if (split >= param.length() - 1) {
                sstring key;
                if (url_decode(param.substr(0,split) , key)) {
                    req.query_parameters[key] = "";
                }
            } else {
                sstring key;
                sstring value;
                if (url_decode(param.substr(0,split), key)
                        && url_decode(param.substr(split + 1), value)) {
                    req.query_parameters[key] = value;
                }
            }

        }

        /**
         * Set the query parameters in the request objects.
         * query param appear after the question mark and are separated
         * by the ampersand sign
         */
        static sstring set_query_param(request& req) {
            size_t pos = req._url.find('?');
            if (pos == sstring::npos) {
                return req._url;
            }
            size_t curr = pos + 1;
            size_t end_param;
            std::experimental::string_view url = req._url;
            while ((end_param = req._url.find('&', curr)) != sstring::npos) {
                add_param(req, url.substr(curr, end_param - curr) );
                curr = end_param + 1;
            }
            add_param(req, url.substr(curr));
            return req._url.substr(0, pos);
        }

        future<bool> generate_reply(std::unique_ptr<request> req) {
            auto resp = std::make_unique<reply>();
            bool conn_keep_alive = false;
            bool conn_close = false;
            auto it = req->_headers.find("Connection");
            if (it != req->_headers.end()) {
                if (it->second == "Keep-Alive") {
                    conn_keep_alive = true;
                } else if (it->second == "Close") {
                    conn_close = true;
                }
            }
            bool should_close;
            // TODO: Handle HTTP/2.0 when it releases
            resp->set_version(req->_version);

            if (req->_version == "1.0") {
                if (conn_keep_alive) {
                    resp->_headers["Connection"] = "Keep-Alive";
                }
                should_close = !conn_keep_alive;
            } else if (req->_version == "1.1") {
                should_close = conn_close;
            } else {
                // HTTP/0.9 goes here
                should_close = true;
            }
            sstring url = set_query_param(*req.get());
            sstring version = req->_version;
            return _server._routes.handle(url, std::move(req), std::move(resp)).
            // Caller guarantees enough room
            then([this, should_close, version = std::move(version)](std::unique_ptr<reply> rep) {
                rep->set_version(version).done();
                this->_replies.push(std::move(rep));
                return make_ready_future<bool>(should_close);
            });
        }
        future<> write_body() {
            return _write_buf.write(_resp->_content.begin(),
                    _resp->_content.size());
        }
    };
    uint64_t total_connections() const {
        return _total_connections;
    }
    uint64_t current_connections() const {
        return _current_connections;
    }
    uint64_t requests_served() const {
        return _requests_served;
    }
    static sstring http_date() {
        auto t = ::time(nullptr);
        struct tm tm;
        gmtime_r(&t, &tm);
        char tmp[100];
        strftime(tmp, sizeof(tmp), "%d %b %Y %H:%M:%S GMT", &tm);
        return tmp;
    }
private:
    boost::intrusive::list<connection> _connections;
};

/*
 * A helper class to start, set and listen an http server
 * typical use would be:
 *
 * auto server = new http_server_control();
 *                 server->start().then([server] {
 *                 server->set_routes(set_routes);
 *              }).then([server, port] {
 *                  server->listen(port);
 *              }).then([port] {
 *                  std::cout << "Seastar HTTP server listening on port " << port << " ...\n";
 *              });
 */
class http_server_control {
    distributed<http_server>* _server_dist;
public:
    http_server_control() : _server_dist(new distributed<http_server>) {
    }

    future<> start() {
        return _server_dist->start();
    }

    future<> stop() {
        return _server_dist->stop();
    }

    future<> set_routes(std::function<void(routes& r)> fun) {
        return _server_dist->invoke_on_all([fun](http_server& server) {
            fun(server._routes);
        });
    }

    future<> listen(ipv4_addr addr) {
        return _server_dist->invoke_on_all(&http_server::listen, addr);
    }

    distributed<http_server>& server() {
        return *_server_dist;
    }
};

}

#endif /* APPS_HTTPD_HTTPD_HH_ */
<|endoftext|>