Exemple #1
0
// Watch current set of sources and process events
void
XmlRpcDispatch::work(double timeoutSeconds)
{
  // Compute end time
  double timeNow = getTime();
  _endTime = (timeoutSeconds < 0.0) ? -1.0 : (timeNow + timeoutSeconds);
  _doClear = false;
  _inWork = true;

  // Only work while there is something to monitor
  while (_sources.size() > 0) {

    // Wait for and dispatch events
    if ( ! waitForAndProcessEvents(timeoutSeconds))
    {
      _inWork = false;
      return;
    }


    // Check whether to clear all sources
    if (_doClear)
    {
      SourceList sourcesToClose;
      _sources.swap(sourcesToClose);
      for (SourceList::iterator it=sourcesToClose.begin(); it!=sourcesToClose.end(); ++it)
      {
        XmlRpcSource *src = it->getSource();
        src->close();
      }

      _doClear = false;
    }

    // Check whether end time has passed or exit has been called
    if (_endTime == 0.0)        // Exit
    {
      break;
    }
    else if (_endTime > 0.0)    // Check for timeout
    {
      double t = getTime();
      if (t > _endTime)
        break;

      // Decrement timeout by elapsed time
      timeoutSeconds -= (t - timeNow);
      if (timeoutSeconds < 0.0) 
        timeoutSeconds = 0.0;    // Shouldn't happen but its fp math...
      timeNow = t;
    }
  }

  _inWork = false;
}
Exemple #2
0
// Clear all sources from the monitored sources list
void XmlRpcDispatch::clear()
{
	if (_inWork)
		_doClear = true;		 // Finish reporting current events before clearing
	else
	{
		SourceList closeList = _sources;
		_sources.clear();
		for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it)
			it->getSource()->close();
	}
}
Exemple #3
0
// Clear all sources from the monitored sources list
void
XmlRpcDispatch::clear()
{
  if (_inWork)
  {
    _doClear = true;  // Finish reporting current events before clearing
  }
  else
  {
    SourceList sourcesToClose;
    _sources.swap(sourcesToClose);
    for (SourceList::iterator it=sourcesToClose.begin(); it!=sourcesToClose.end(); ++it)
      it->getSource()->close();
  }
}
Exemple #4
0
/** \fn FillData::Run(SourceList &sourcelist)
 *  \brief Goes through the sourcelist and updates its channels with
 *         program info grabbed with the associated grabber.
 *  \return true if there were no failures
 */
bool FillData::Run(SourceList &sourcelist)
{
    SourceList::iterator it;
    SourceList::iterator it2;

    QString status, querystr;
    MSqlQuery query(MSqlQuery::InitCon());
    QDateTime GuideDataBefore, GuideDataAfter;
    int failures = 0;
    int externally_handled = 0;
    int total_sources = sourcelist.size();
    int source_channels = 0;

    QString sidStr = QString("Updating source #%1 (%2) with grabber %3");

    need_post_grab_proc = false;
    int nonewdata = 0;
    bool has_dd_source = false;

    // find all DataDirect duplicates, so we only data download once.
    for (it = sourcelist.begin(); it != sourcelist.end(); ++it)
    {
        if (!is_grabber_datadirect((*it).xmltvgrabber))
            continue;

        has_dd_source = true;
        for (it2 = sourcelist.begin(); it2 != sourcelist.end(); ++it2)
        {
            if (((*it).id           != (*it2).id)           &&
                ((*it).xmltvgrabber == (*it2).xmltvgrabber) &&
                ((*it).userid       == (*it2).userid)       &&
                ((*it).password     == (*it2).password))
            {
                (*it).dd_dups.push_back((*it2).id);
            }
        }
    }
    if (has_dd_source)
        ddprocessor.CreateTempDirectory();

    for (it = sourcelist.begin(); it != sourcelist.end(); ++it)
    {
        if (!fatalErrors.empty())
            break;

        query.prepare("SELECT MAX(endtime) FROM program p LEFT JOIN channel c "
                      "ON p.chanid=c.chanid WHERE c.sourceid= :SRCID "
                      "AND manualid = 0;");
        query.bindValue(":SRCID", (*it).id);

        if (query.exec() && query.size() > 0)
        {
            query.next();

            if (!query.isNull(0))
                GuideDataBefore = 
                    QDateTime::fromString(query.value(0).toString(),
                                          Qt::ISODate);
        }

        channel_update_run = false;
        endofdata = false;

        QString xmltv_grabber = (*it).xmltvgrabber;

        if (xmltv_grabber == "eitonly")
        {
            LOG(VB_GENERAL, LOG_INFO,
                QString("Source %1 configured to use only the "
                        "broadcasted guide data. Skipping.") .arg((*it).id));

            externally_handled++;
            updateLastRunStart(query);
            updateLastRunEnd(query);
            continue;
        }
        else if (xmltv_grabber.trimmed().isEmpty() ||
                 xmltv_grabber == "/bin/true" ||
                 xmltv_grabber == "none")
        {
            LOG(VB_GENERAL, LOG_INFO, 
                QString("Source %1 configured with no grabber. Nothing to do.")
                    .arg((*it).id));

            externally_handled++;
            updateLastRunStart(query);
            updateLastRunEnd(query);
            continue;
        }

        LOG(VB_GENERAL, LOG_INFO, sidStr.arg((*it).id)
                                  .arg((*it).name)
                                  .arg(xmltv_grabber));

        query.prepare(
            "SELECT COUNT(chanid) FROM channel WHERE sourceid = "
             ":SRCID AND xmltvid != ''");
        query.bindValue(":SRCID", (*it).id);

        if (query.exec() && query.size() > 0)
        {
            query.next();
            source_channels = query.value(0).toInt();
            if (source_channels > 0)
            {
                LOG(VB_GENERAL, LOG_INFO,
                    QString("Found %1 channels for source %2 which use grabber")
                        .arg(source_channels).arg((*it).id));
            }
            else
            {
                LOG(VB_GENERAL, LOG_INFO,
                    QString("No channels are configured to use grabber."));
            }
        }
        else
        {
            source_channels = 0;
            LOG(VB_GENERAL, LOG_INFO,
                QString("Can't get a channel count for source id %1")
                    .arg((*it).id));
        }

        bool hasprefmethod = false;

        if (is_grabber_external(xmltv_grabber))
        {
            uint flags = kMSRunShell | kMSStdOut | kMSBuffered;
            MythSystem grabber_capabilities_proc(xmltv_grabber,
                                                 QStringList("--capabilities"),
                                                 flags);
            grabber_capabilities_proc.Run(25);
            if (grabber_capabilities_proc.Wait() != GENERIC_EXIT_OK)
                LOG(VB_GENERAL, LOG_ERR,
                    QString("%1  --capabilities failed or we timed out waiting."                            " You may need to upgrade your xmltv grabber")
                        .arg(xmltv_grabber));
            else
            {
                QByteArray result = grabber_capabilities_proc.ReadAll();
                QTextStream ostream(result);
                QString capabilities;
                while (!ostream.atEnd())
                {
                    QString capability
                        = ostream.readLine().simplified();

                    if (capability.isEmpty())
                        continue;

                    capabilities += capability + ' ';

                    if (capability == "baseline")
                        (*it).xmltvgrabber_baseline = true;

                    if (capability == "manualconfig")
                        (*it).xmltvgrabber_manualconfig = true;

                    if (capability == "cache")
                        (*it).xmltvgrabber_cache = true;

                    if (capability == "preferredmethod")
                        hasprefmethod = true;
                }
                LOG(VB_GENERAL, LOG_INFO,
                    QString("Grabber has capabilities: %1") .arg(capabilities));
            }
        }

        if (hasprefmethod)
        {
            uint flags = kMSRunShell | kMSStdOut | kMSBuffered;
            MythSystem grabber_method_proc(xmltv_grabber,
                                           QStringList("--preferredmethod"),
                                           flags);
            grabber_method_proc.Run(15);
            if (grabber_method_proc.Wait() != GENERIC_EXIT_OK)
                LOG(VB_GENERAL, LOG_ERR,
                    QString("%1 --preferredmethod failed or we timed out "
                            "waiting. You may need to upgrade your xmltv "
                            "grabber").arg(xmltv_grabber));
            else
            {
                QTextStream ostream(grabber_method_proc.ReadAll());
                (*it).xmltvgrabber_prefmethod =
                                ostream.readLine().simplified();

                LOG(VB_GENERAL, LOG_INFO, QString("Grabber prefers method: %1")
                                    .arg((*it).xmltvgrabber_prefmethod));
            }
        }

        need_post_grab_proc |= !is_grabber_datadirect(xmltv_grabber);

        if (is_grabber_datadirect(xmltv_grabber) && dd_grab_all)
        {
            if (only_update_channels)
                DataDirectUpdateChannels(*it);
            else
            {
                QDate qCurrentDate = QDate::currentDate();
                if (!GrabData(*it, 0, &qCurrentDate))
                    ++failures;
            }
        }
        else if ((*it).xmltvgrabber_prefmethod == "allatonce")
        {
            if (!GrabData(*it, 0))
                ++failures;
        }
        else if ((*it).xmltvgrabber_baseline ||
                 is_grabber_datadirect(xmltv_grabber))
        {

            QDate qCurrentDate = QDate::currentDate();

            // We'll keep grabbing until it returns nothing
            // Max days currently supported is 21
            int grabdays = (is_grabber_datadirect(xmltv_grabber)) ?
                14 : REFRESH_MAX;

            grabdays = (maxDays > 0)          ? maxDays : grabdays;
            grabdays = (only_update_channels) ? 1       : grabdays;

            vector<bool> refresh_request;
            refresh_request.resize(grabdays, refresh_all);
            for (int i = 0; i < refresh_day.size(); i++)
                refresh_request[i] = refresh_day[i];

            if (is_grabber_datadirect(xmltv_grabber) && only_update_channels)
            {
                DataDirectUpdateChannels(*it);
                grabdays = 0;
            }

            for (int i = 0; i < grabdays; i++)
            {
                if (!fatalErrors.empty())
                    break;

                // We need to check and see if the current date has changed
                // since we started in this loop.  If it has, we need to adjust
                // the value of 'i' to compensate for this.
                if (QDate::currentDate() != qCurrentDate)
                {
                    QDate newDate = QDate::currentDate();
                    i += (newDate.daysTo(qCurrentDate));
                    if (i < 0)
                        i = 0;
                    qCurrentDate = newDate;
                }

                QString prevDate(qCurrentDate.addDays(i-1).toString());
                QString currDate(qCurrentDate.addDays(i).toString());

                LOG(VB_GENERAL, LOG_INFO, ""); // add a space between days
                LOG(VB_GENERAL, LOG_INFO, "Checking day @ " +
                    QString("offset %1, date: %2").arg(i).arg(currDate));

                bool download_needed = false;

                if (refresh_request[i])
                {
                    if ( i == 1 )
                    {
                        LOG(VB_GENERAL, LOG_INFO,
                            "Data Refresh always needed for tomorrow");
                    }
                    else
                    {
                        LOG(VB_GENERAL, LOG_INFO,
                            "Data Refresh needed because of user request");
                    }
                    download_needed = true;
                }
                else
                {
                    // Check to see if we already downloaded data for this date.

                    querystr = "SELECT c.chanid, COUNT(p.starttime) "
                               "FROM channel c "
                               "LEFT JOIN program p ON c.chanid = p.chanid "
                               "  AND starttime >= "
                                   "DATE_ADD(DATE_ADD(CURRENT_DATE(), "
                                   "INTERVAL '%1' DAY), INTERVAL '20' HOUR) "
                               "  AND starttime < DATE_ADD(CURRENT_DATE(), "
                                   "INTERVAL '%2' DAY) "
                               "WHERE c.sourceid = %3 AND c.xmltvid != '' "
                               "GROUP BY c.chanid;";

                    if (query.exec(querystr.arg(i-1).arg(i).arg((*it).id)) &&
                        query.isActive())
                    {
                        int prevChanCount = 0;
                        int currentChanCount = 0;
                        int previousDayCount = 0;
                        int currentDayCount = 0;

                        LOG(VB_CHANNEL, LOG_INFO,
                            QString("Checking program counts for day %1")
                                .arg(i-1));

                        while (query.next())
                        {
                            if (query.value(1).toInt() > 0)
                                prevChanCount++;
                            previousDayCount += query.value(1).toInt();

                            LOG(VB_CHANNEL, LOG_INFO,
                                QString("    chanid %1 -> %2 programs")
                                    .arg(query.value(0).toString())
                                    .arg(query.value(1).toInt()));
                        }

                        if (query.exec(querystr.arg(i).arg(i+1).arg((*it).id))
                                && query.isActive())
                        {
                            LOG(VB_CHANNEL, LOG_INFO,
                                QString("Checking program counts for day %1")
                                    .arg(i));
                            while (query.next())
                            {
                                if (query.value(1).toInt() > 0)
                                    currentChanCount++;
                                currentDayCount += query.value(1).toInt();

                                LOG(VB_CHANNEL, LOG_INFO,
                                    QString("    chanid %1 -> %2 programs")
                                                .arg(query.value(0).toString())
                                                .arg(query.value(1).toInt()));
                            }
                        }
                        else
                        {
                            LOG(VB_GENERAL, LOG_INFO,
                                QString("Data Refresh because we are unable to "
                                        "query the data for day %1 to "
                                        "determine if we have enough").arg(i));
                            download_needed = true;
                        }

                        if (currentChanCount < (prevChanCount * 0.90))
                        {
                            LOG(VB_GENERAL, LOG_INFO,
                                QString("Data refresh needed because only %1 "
                                        "out of %2 channels have at least one "
                                        "program listed for day @ offset %3 "
                                        "from 8PM - midnight.  Previous day "
                                        "had %4 channels with data in that "
                                        "time period.")
                                    .arg(currentChanCount).arg(source_channels)
                                    .arg(i).arg(prevChanCount));
                            download_needed = true;
                        }
                        else if (currentDayCount == 0)
                        {
                            LOG(VB_GENERAL, LOG_INFO,
                                QString("Data refresh needed because no data "
                                        "exists for day @ offset %1 from 8PM - "
                                        "midnight.").arg(i));
                            download_needed = true;
                        }
                        else if (previousDayCount == 0)
                        {
                            LOG(VB_GENERAL, LOG_INFO,
                                QString("Data refresh needed because no data "
                                        "exists for day @ offset %1 from 8PM - "
                                        "midnight.  Unable to calculate how "
                                        "much we should have for the current "
                                        "day so a refresh is being forced.")
                                    .arg(i-1));
                            download_needed = true;
                        }
                        else if (currentDayCount < (currentChanCount * 3))
                        {
                            LOG(VB_GENERAL, LOG_INFO,
                                QString("Data Refresh needed because offset "
                                        "day %1 has less than 3 programs "
                                        "per channel for the 8PM - midnight "
                                        "time window for channels that "
                                        "normally have data. "
                                        "We want at least %2 programs, but "
                                        "only found %3")
                                    .arg(i).arg(currentChanCount * 3)
                                    .arg(currentDayCount));
                            download_needed = true;
                        }
                        else if (currentDayCount < (previousDayCount / 2))
                        {
                            LOG(VB_GENERAL, LOG_INFO,
                                QString("Data Refresh needed because offset "
                                        "day %1 has less than half the number "
                                        "of programs as the previous day for "
                                        "the 8PM - midnight time window. "
                                        "We want at least %2 programs, but "
                                        "only found %3").arg(i)
                                    .arg(previousDayCount / 2)
                                    .arg(currentDayCount));
                            download_needed = true;
                        }
                    }
                    else
                    {
                        LOG(VB_GENERAL, LOG_INFO,
                            QString("Data Refresh needed because we are unable "
                                    "to query the data for day @ offset %1 to "
                                    "determine how much we should have for "
                                    "offset day %2.").arg(i-1).arg(i));
                        download_needed = true;
                    }
                }

                if (download_needed)
                {
                    LOG(VB_GENERAL, LOG_NOTICE,
                        QString("Refreshing data for ") + currDate);
                    if (!GrabData(*it, i, &qCurrentDate))
                    {
                        ++failures;
                        if (!fatalErrors.empty() || interrupted)
                        {
                            break;
                        }
                    }

                    if (endofdata)
                    {
                        LOG(VB_GENERAL, LOG_INFO,
                            "Grabber is no longer returning program data, "
                            "finishing");
                        break;
                    }
                }
                else
                {
                    LOG(VB_GENERAL, LOG_NOTICE,
                        QString("Data is already present for ") + currDate +
                        ", skipping");
                }
            }
            if (!fatalErrors.empty())
                break;
        }
        else
        {
            LOG(VB_GENERAL, LOG_ERR,
                QString("Grabbing XMLTV data using ") + xmltv_grabber +
                " is not supported. You may need to upgrade to"
                " the latest version of XMLTV.");
        }

        if (interrupted)
        {
            break;
        }

        query.prepare("SELECT MAX(endtime) FROM program p LEFT JOIN channel c "
                      "ON p.chanid=c.chanid WHERE c.sourceid= :SRCID "
                      "AND manualid = 0;");
        query.bindValue(":SRCID", (*it).id);

        if (query.exec() && query.size() > 0)
        {
            query.next();

            if (!query.isNull(0))
                GuideDataAfter = QDateTime::fromString(
                                     query.value(0).toString(), Qt::ISODate);
        }

        if (GuideDataAfter == GuideDataBefore)
        {
            nonewdata++;
        }
    }

    if (!fatalErrors.empty())
    {
        for (int i = 0; i < fatalErrors.size(); i++)
        {
            LOG(VB_GENERAL, LOG_CRIT, LOC + "Encountered Fatal Error: " +
                    fatalErrors[i]);
        }
        return false;
    }

    if (only_update_channels && !need_post_grab_proc)
        return true;

    if (failures == 0)
    {
        if (nonewdata > 0 &&
            (total_sources != externally_handled))
            status = QString(QObject::tr(
                     "mythfilldatabase ran, but did not insert "
                     "any new data into the Guide for %1 of %2 sources. "
                     "This can indicate a potential grabber failure."))
                     .arg(nonewdata)
                     .arg(total_sources);
        else
            status = QObject::tr("Successful.");

        updateLastRunStatus(query, status);
    }

    return (failures == 0);
}
Exemple #5
0
// Watch current set of sources and process events
void XmlRpcDispatch::work(double timeout_ms, XmlRpcClient *chunkWait)
{
	// Compute end time
	_endTime = (timeout_ms < 0.0) ? -1.0 : (getTime() + timeout_ms/1000.0);
	_doClear = false;
	_inWork = true;

	if (chunkWait)
	{
		setSourceEvents(chunkWait, ReadableEvent | WritableEvent | Exception);
	}

	// Only work while there is something to monitor
	while (_sources.size() > 0)
	{

		// Construct the sets of descriptors we are interested in
		struct pollfd fds[MAX_POLLS];
		nfds_t nfds = 0;

		addToFds (fds, nfds);
		fds[nfds].fd = -1;

		// Check for events
		int nEvents;
		if (timeout_ms < 0.0)
			nEvents = poll(fds, nfds, 0);
		else
		{
			struct timespec tv;
			tv.tv_sec = (int) floor (timeout_ms / 1000.0);
			tv.tv_nsec = (int) (fmod (timeout_ms, 1000.0) * 1000000.0);
			nEvents = ppoll(fds, nfds, &tv, NULL);
		}

		if (nEvents < 0)
		{
			XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents);
			_inWork = false;
			return;
		}

		checkFds (fds, nfds, chunkWait);

		// Check whether to clear all sources
		if (_doClear)
		{
			SourceList closeList = _sources;
			_sources.clear();
			for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it)
			{
				XmlRpcSource *src = it->getSource();
				src->close();
			}

			_doClear = false;
		}

		// Check whether end time has passed
		if (0 <= _endTime && getTime() > _endTime)
			break;

		// if chunkWait and the connection received chunk..
		if (chunkWait && chunkWait->gotChunk ())
			break;
	}

	_inWork = false;
}
// Watch current set of sources and process events
void
XmlRpcDispatch::work(double timeout)
{
  // Compute end time
  _endTime = (timeout < 0.0) ? -1.0 : (getTime() + timeout);
  _doClear = false;
  _inWork = true;

  // Only work while there is something to monitor
  while (_sources.size() > 0) {

    // Construct the sets of descriptors we are interested in
    fd_set inFd, outFd, excFd;
	  FD_ZERO(&inFd);
	  FD_ZERO(&outFd);
	  FD_ZERO(&excFd);

    int maxFd = -1;     // Not used on windows
    SourceList::iterator it;
    for (it=_sources.begin(); it!=_sources.end(); ++it) {
      int fd = it->getSource()->getfd();
      if (it->getMask() & ReadableEvent) FD_SET(fd, &inFd);
      if (it->getMask() & WritableEvent) FD_SET(fd, &outFd);
      if (it->getMask() & Exception)     FD_SET(fd, &excFd);
      if (it->getMask() && fd > maxFd)   maxFd = fd;
    }

    // Check for events
    int nEvents;
    if (timeout < 0.0)
      nEvents = select(maxFd+1, &inFd, &outFd, &excFd, NULL);
    else 
    {
      struct timeval tv;
      tv.tv_sec = (int)floor(timeout);
      tv.tv_usec = ((int)floor(1000000.0 * (timeout-floor(timeout)))) % 1000000;
      nEvents = select(maxFd+1, &inFd, &outFd, &excFd, &tv);
    }

    if (nEvents < 0)
    {
      XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents);
      _inWork = false;
      return;
    }

    // Process events
    for (it=_sources.begin(); it != _sources.end(); )
    {
      SourceList::iterator thisIt = it++;
      XmlRpcSource* src = thisIt->getSource();
      int fd = src->getfd();
      unsigned newMask = (unsigned) -1;
      if (fd <= maxFd) {
        // If you select on multiple event types this could be ambiguous
        if (FD_ISSET(fd, &inFd))
          newMask &= src->handleEvent(ReadableEvent);
        if (FD_ISSET(fd, &outFd))
          newMask &= src->handleEvent(WritableEvent);
        if (FD_ISSET(fd, &excFd))
          newMask &= src->handleEvent(Exception);

        if ( ! newMask) {
          _sources.erase(thisIt);  // Stop monitoring this one
          if ( ! src->getKeepOpen())
            src->close();
        } else if (newMask != (unsigned) -1) {
          thisIt->getMask() = newMask;
        }
      }
    }

    // Check whether to clear all sources
    if (_doClear)
    {
      SourceList closeList = _sources;
      _sources.clear();
      for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) {
	XmlRpcSource *src = it->getSource();
        src->close();
      }

      _doClear = false;
    }

    // Check whether end time has passed
    if (0 <= _endTime && getTime() > _endTime)
      break;
  }

  _inWork = false;
}
bool AcquireImages()
{
    // Create a GEV Device finder dialog
    PvDeviceFinderWnd lDeviceFinderWnd;

    // Prompt the user to select a GEV Device
    lDeviceFinderWnd.ShowModal();

    // Get the connectivity information for the selected GEV Device
    PvDeviceInfo* lDeviceInfo = lDeviceFinderWnd.GetSelected();

    // If no device is selected, abort
    if( lDeviceInfo == NULL )
    {
        cout << "No device selected." << endl;
        return false;
    }

    PvString lMACAddress = lDeviceInfo->GetMACAddress();
    PvString lIPAddress = lDeviceInfo->GetIPAddress();

    // Connect to the GEV Device
    PvDevice lDevice;
    cout << "Connecting to " << lMACAddress.GetAscii() << endl;
    // if ( !lDevice.Connect( lDeviceInfo ).IsOK() )
    if ( !lDevice.Connect( lDeviceInfo ).IsOK() )
    {
        cout << "Unable to connect to " << lMACAddress.GetAscii() << endl;
        return false;
    }
    cout << "Successfully connected to " << lMACAddress.GetAscii() << endl;

    cout << endl;

    SourceList lSources;

    // Get source selector
    PvGenEnum *lSourceSelector = lDevice.GetGenParameters()->GetEnum( "SourceSelector" );
    if ( lSourceSelector != NULL )
    {
        // Go through all sources, create source objects
        PvInt64 lCount = 0;
        lSourceSelector->GetEntriesCount( lCount );
        for ( PvInt64 i = 0; i < lCount; i++ )
        {
            // Get source enum entry
            const PvGenEnumEntry *lEE = NULL;
            lSourceSelector->GetEntryByIndex( i, &lEE );

            // If available, create source
            if ( ( lEE != NULL ) && lEE->IsAvailable() )
            {
                // Get source name
                PvString lSourceName;
                lEE->GetName( lSourceName );

                // Create source
                Source *lSource = new Source( &lDevice, lIPAddress, lSourceName );
                lSource->Open();

                // Add to sources list
                lSources.push_back( lSource );

                cout << endl;
            }
        }
    }
    else
    {
        // If no source selector, just create a single source
        Source *lSource = new Source( &lDevice, lIPAddress, "" );
        lSource->Open();

        // Add to sources list
        lSources.push_back( lSource );

        cout << endl;
    }

    // Start the acquisiton on all sources
    SourceList::iterator lIt = lSources.begin();
    while ( lIt != lSources.end() )
    {
        ( *( lIt++ ) )->StartAcquisition();
        cout << endl;
    }

    // Aggressive initial value, will be adjusted vs frame rate
    PvUInt32 lTimeout = 1;

    // Acquire images until the user instructs us to stop
    cout << "<press a key to stop streaming>" << endl;
    while ( !PvKbHit() )
    {
        double lNewTimeout = 1000.0;

        lIt = lSources.begin();
        while ( lIt != lSources.end() )
        {
            ( *lIt )->RetrieveImages( lTimeout );
            ( *lIt )->PrintStatistics();

            // Always use the smallest recommended timeout
            double lRecommendedTimeout = ( *lIt )->GetRecommendedTimeout();
            if ( lRecommendedTimeout < lNewTimeout )
            {
                lNewTimeout = lRecommendedTimeout;
            }

            lIt++;
        }

        // Update timeout for next round - smallest recommended divided by number of sources
        lTimeout = static_cast<PvUInt32>( lNewTimeout / static_cast<double>( lSources.size() ) + 0.5 );

        cout << "\r";
    }

    PvGetChar(); // Flush key buffer for next stop
    cout << endl << endl;

    // Stop the acquisiton on all sources
    lIt = lSources.begin();
    while ( lIt != lSources.end() )
    {
        ( *( lIt++ ) )->StopAcquisition();
        cout << endl;
    }

    // Close and delete sources
    lIt = lSources.begin();
    while ( lIt != lSources.end() )
    {
        ( *lIt )->Close();
        cout << endl;

        delete *lIt;

        lIt++;
    }

    // Finally disconnect the device. Optional, still nice to have
    cout << "Disconnecting device" << endl;
    lDevice.Disconnect();

    return true;
}
// Watch current set of sources and process events
void
XmlRpcDispatch::work(double timeout_in_seconds)
{
  // Compute end time
    _endTime = (timeout_in_seconds < 0.0) ? -1.0 : (getTime() + timeout_in_seconds);
  _doClear = false;
  _inWork = true;

  // Only work while there is something to monitor
  while (_sources.size() > 0) {
      // // Construct the sets of descriptors we are interested in
      // fd_set inFd, outFd, excFd;
      //       FD_ZERO(&inFd);
      //       FD_ZERO(&outFd);
      //       FD_ZERO(&excFd);

      //    int maxFd = -1;     // Not used on windows
      //    SourceList::iterator it;
    int fds_size = _sources.size();
    std::vector<struct pollfd> fds(fds_size);

    {

        int __offset = 0;
        for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
        {
            fds[__offset].fd = it->getSource()->getfd();
            fds[__offset].events = 0;
            fds[__offset].revents = 0;
            if (it->getMask() & ReadableEvent) fds[__offset].events |= POLLIN;
            if (it->getMask() & WritableEvent) fds[__offset].events |= POLLOUT;
            if (it->getMask() & Exception)     fds[__offset].events |= POLLERR;
            //  2      if (it->getMask() && fd > maxFd)   maxFd = fd;
            ++__offset;
        }
    }

    // Check for events
    returnhereoninterruptedsyscall:
    int nEvents;
    if (timeout_in_seconds < 0.0)
        nEvents = poll(&fds[0], fds_size, -1);
    else
    {
        nEvents = poll(&fds[0],fds_size, timeout_in_seconds * 1000);
    }
    if(nEvents == -1 and errno == EINTR)
        goto returnhereoninterruptedsyscall;

    if (nEvents < 0)
    {
        LOG_ERROR("Error in XmlRpcDispatch::work: error in select " <<  strerror(errno));
       //        LOG_ERROR(strerror(errno));

//        XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in select (%d).", nEvents);
        _inWork = false;
        return;
    }

    // Process events
    {

        size_t __offset = 0;

        for (SourceList::iterator it = _sources.begin(); it != _sources.end(); )
        {
            SourceList::iterator thisIt = it++;
            XmlRpcSource* src = thisIt->getSource();
            unsigned newMask = (unsigned) -1;

            // accepting a new connection (XmlRpcServer::handleEvent)
            // will add a new entry to _sources but we don't have an
            // entry in the fds around.
            if (__offset < fds.size())
            {
                assert(fds[__offset].fd == src->getfd());
                // if (fd <= maxFd)
                // {
                // If you select on multiple event types this could be ambiguous
                if (fds[__offset].revents bitand POLLIN)
                {
                    newMask &= src->handleEvent(ReadableEvent);
                }
                if (fds[__offset].revents bitand POLLOUT)
                {
                    newMask &= src->handleEvent(WritableEvent);
                }
                if (fds[__offset].revents bitand POLLERR)
                {
                    newMask &= src->handleEvent(Exception);
                }
            }

            if (newMask == 0)
            {
                _sources.erase(thisIt);  // Stop monitoring this one
                if (!src->getKeepOpen())
                {
                    src->close();
                }

            }
            else if (newMask != (unsigned) -1)
            {
                thisIt->getMask() = newMask;
            }
            __offset++;
        }
    }

    // Check whether to clear all sources
    if (_doClear)
    {
      SourceList closeList = _sources;
      _sources.clear();
      for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) {
	XmlRpcSource *src = it->getSource();
        src->close();
      }

      _doClear = false;
    }

    // Check whether end time has passed
    if (0 <= _endTime && getTime() > _endTime)
      break;
  }

  _inWork = false;
}