Logo Search packages:      
Sourcecode: kboincspy version File versions  Download package

kbsboincmonitor.cpp

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

#include <qsocket.h>

#include <kapplication.h>
#include <klocale.h>
#include <knotifyclient.h>

#include <kbsboincmonitor.h>
#include <kbsdocument.h>
#include <kbsprojectmonitor.h>
#include <kbsprojectplugin.h>
#include <kbsrpcmonitor.h>
#include <kbstaskmonitor.h>
#include <kbstree.h>

const QString BOINCClientStateFile = "client_state.xml";

const QString BOINCAccountFilePrefix = "account_";
const QString BOINCAccountFileSuffix = ".xml";

const unsigned BOINCAccountFilePrefixLength = BOINCAccountFilePrefix.length();
const unsigned BOINCAccountFileSuffixLength = BOINCAccountFileSuffix.length();

QString KBSLocation::defaultHost(const KURL &url)
{
  const QString host = url.host();
  return host.isEmpty() ? "localhost" : host;
}

unsigned KBSLocation::defaultPort = 0;

QString KBSLocation::defaultHost() const
{
  return defaultHost(url);
}

bool operator==(const KBSLocation &location1, const KBSLocation &location2)
{
  return(location1.url == location2.url);
}

KBSBOINCMonitor::KBSBOINCMonitor(const KBSLocation &location, KBSTreeNode *parent, const char *name)
               : KBSDataMonitor(location.url, parent, name), m_location(location),
                 m_rpcMonitor(new KBSRPCMonitor(location.host, this)), m_startup(true)
{
  m_rpcMonitor->setPort(location.port);
  
  m_accounts.setAutoDelete(true);
  m_projectMonitors.setAutoDelete(true);
  m_taskMonitors.setAutoDelete(true);
  
  connect(this, SIGNAL(fileUpdated(const QString &)),
          this, SLOT(updateFile(const QString &)));
          
  connect(this, SIGNAL(projectsAdded(const QStringList &)),
          this, SLOT(addAccounts(const QStringList &)));
  connect(this, SIGNAL(projectsRemoved(const QStringList &)),
          this, SLOT(removeAccounts(const QStringList &)));
  
  connect(this, SIGNAL(projectsAdded(const QStringList &)),
          this, SLOT(addProjectMonitors(const QStringList &)));
  connect(this, SIGNAL(projectsRemoved(const QStringList &)),
          this, SLOT(removeProjectMonitors(const QStringList &)));
  
  connect(this, SIGNAL(resultActivated(unsigned, const QString &, bool)),
          this, SLOT(updateTaskMonitor(unsigned, const QString &, bool)));
  
  addFile(BOINCClientStateFile);
}

bool KBSBOINCMonitor::isLocal() const
{
  return(m_location.host == "localhost" || m_location.host == "127.0.0.1");
}

KBSLocation KBSBOINCMonitor::location() const
{
  return m_location;
}

const BOINCClientState *KBSBOINCMonitor::state() const
{
  return file(BOINCClientStateFile)->ok ? &m_state : NULL;
}
    
const BOINCAccount *KBSBOINCMonitor::account(const QString &project) const
{
  return file(formatFileName(project))-> ok ? m_accounts.find(project) : NULL;
}

QString KBSBOINCMonitor::project(const BOINCProject &project) const
{
  return parseProjectName(project.master_url);
}

QString KBSBOINCMonitor::project(const BOINCApp &app) const
{
  if(!m_state.app_version.contains(app.name) || m_state.app_version[app.name].isEmpty())
    return QString::null;
  
  BOINCAppVersion app_version = m_state.app_version[app.name].first();
  
  QStringList domains;
  for(QValueList<BOINCFileRef>::const_iterator file_ref = app_version.file_ref.begin();
      file_ref != app_version.file_ref.end(); ++file_ref)
  {
    if(!m_state.file_info.contains((*file_ref).file_name)) continue;
    
    const QValueList<KURL> urls = m_state.file_info[(*file_ref).file_name].url;
    for(QValueList<KURL>::const_iterator url = urls.begin(); url != urls.end(); ++url)
    {
      if(!(*url).isValid()) continue;
      
      QString domain = (*url).host();
      while(domain.contains('.') > 1)
        domain = domain.mid(domain.find('.') + 1);
      domains << domain;
    }
  }
  if(domains.isEmpty()) return QString::null;

  QValueList<BOINCProject> projects = m_state.project.values();
  for(QValueList<BOINCProject>::const_iterator project = projects.begin();
      project != projects.end(); ++project)
    for(QStringList::const_iterator domain = domains.begin();
        domain != domains.end(); ++domain)
      if((*project).scheduler_url.host().endsWith(*domain))
        return this->project(*project);
  
  return QString::null;
}
    
QString KBSBOINCMonitor::project(const BOINCWorkunit &workunit) const
{
  QStringList domains;
  for(QValueList<BOINCFileRef>::const_iterator file_ref = workunit.file_ref.begin();
      file_ref != workunit.file_ref.end(); ++file_ref)
  {
    if(!m_state.file_info.contains((*file_ref).file_name)) continue;
    
    const QValueList<KURL> urls = m_state.file_info[(*file_ref).file_name].url;
    for(QValueList<KURL>::const_iterator url = urls.begin(); url != urls.end(); ++url)
    {
      if(!(*url).isValid()) continue;
      
      QString domain = (*url).host();
      while(domain.contains('.') > 1)
        domain = domain.mid(domain.find('.') + 1);
      domains << domain;
    }
  }
  if(domains.isEmpty()) return this->project(m_state.app[workunit.app_name]);
  
  QValueList<BOINCProject> projects = m_state.project.values();
  for(QValueList<BOINCProject>::const_iterator project = projects.begin();
      project != projects.end(); ++project)
    for(QStringList::const_iterator domain = domains.begin();
        domain != domains.end(); ++domain)
      if((*project).scheduler_url.host().endsWith(*domain))
        return this->project(*project);
  
  return this->project(m_state.app[workunit.app_name]);
}

QString KBSBOINCMonitor::project(const BOINCResult &result) const
{
  if(m_state.workunit.contains(result.wu_name))
    return project(m_state.workunit[result.wu_name]);
  else
    return QString::null;
}

QString KBSBOINCMonitor::project(const BOINCActiveTask &task) const
{
  return parseProjectName(task.project_master_url);
}

QString KBSBOINCMonitor::project(const BOINCAccount &account) const
{
  return parseProjectName(account.master_url);
}

QString KBSBOINCMonitor::app(const BOINCApp &app) const
{
  return app.name;
}

QString KBSBOINCMonitor::app(const BOINCWorkunit &workunit) const
{
  return workunit.app_name;
}

QString KBSBOINCMonitor::app(const BOINCResult &result) const
{
  if(m_state.workunit.contains(result.wu_name))
    return app(m_state.workunit[result.wu_name]);
  else
    return QString::null;
}

QString KBSBOINCMonitor::app(const BOINCActiveTask &task) const
{
  if(m_state.result.contains(task.result_name))
    return app(m_state.result[task.result_name]);
  else
    return QString::null;
}
    
QString KBSBOINCMonitor::workunit(const BOINCWorkunit &workunit) const
{
  return workunit.name;
}

QString KBSBOINCMonitor::workunit(const BOINCResult &result) const
{
  return result.wu_name;
}

QString KBSBOINCMonitor::workunit(const BOINCActiveTask &task) const
{
  if(m_state.result.contains(task.result_name))
    return workunit(m_state.result[task.result_name]);
  else
    return QString::null;
}
    
QString KBSBOINCMonitor::result(const BOINCResult &result) const
{
  return result.name;
}

QString KBSBOINCMonitor::result(const BOINCActiveTask &task) const
{
  return task.result_name;
}

KBSRPCMonitor *KBSBOINCMonitor::rpcMonitor() const
{
  return m_rpcMonitor;
}

KBSProjectMonitor *KBSBOINCMonitor::projectMonitor(const QString &project) const
{
  return m_projectMonitors.find(project);
}

KBSTaskMonitor *KBSBOINCMonitor::taskMonitor(unsigned task) const
{
  return m_taskMonitors.find(task);
}

bool KBSBOINCMonitor::parseFile(KBSFileInfo *file, const QString &fileName)
{
  qDebug("Parsing file %s...", file->fileName.latin1());
  
  QDomDocument document(file->fileName);
  if(!readFile(fileName, document)) return false;
  
  if(BOINCClientStateFile == file->fileName)
    return parseClientStateDocument(document);
  else {
    const QString project = parseFileName(file->fileName);
    if(project.isNull()) return false;
   
    BOINCAccount *account = m_accounts.find(project);
    if(NULL == account) return false;
    
    return parseAccountDocument(document, *account);
  } 
}

QString KBSBOINCMonitor::formatFileName(const QString &project)
{
  return(BOINCAccountFilePrefix + project + BOINCAccountFileSuffix);
  QString out(project);
  
  return out.prepend(BOINCAccountFilePrefix).append(BOINCAccountFileSuffix);
}

QString KBSBOINCMonitor::parseFileName(const QString &fileName)
{
  if(fileName.startsWith(BOINCAccountFilePrefix) && fileName.endsWith(BOINCAccountFileSuffix))
    return fileName.mid(BOINCAccountFilePrefixLength,
                        fileName.length() - BOINCAccountFilePrefixLength
                                          - BOINCAccountFileSuffixLength); 
  else
    return QString::null;
}

bool KBSBOINCMonitor::parseClientStateDocument(const QDomDocument &document)
{
  QDomNode child = document.firstChild();
  while(!child.isNull()) {
    if(child.isElement()) {
      QDomElement element = child.toElement();
      
      if(element.nodeName() == "client_state") {
        if(!m_state.parse(element)) return false;
      }
    }
    child = child.nextSibling();
  }
  
  if(!validateResults()) return false;
  
  qDebug("... parse OK");
  
  return true;
}
  
bool KBSBOINCMonitor::parseAccountDocument(const QDomDocument &document, BOINCAccount &account)
{
  QDomNode child = document.firstChild();
  while(!child.isNull()) {
    if(child.isElement()) {
      QDomElement element = child.toElement();
      
      if(element.nodeName() == "account") {
        if(!account.parse(element)) return false;
      }
    }
    child = child.nextSibling();
  }
  
  emit accountUpdated(project(account));
  
  qDebug("... parse OK");
  return true;
}

bool KBSBOINCMonitor::validateResults()
{
  QStringList workunits = m_state.workunit.keys();
  for(QStringList::const_iterator it = workunits.constBegin();
      it != workunits.constEnd(); ++it)
    m_state.workunit[*it].result_name = QString::null;
  
  QStringList results = m_state.result.keys();
  for(QStringList::const_iterator it = results.constBegin();
      it != results.constEnd(); ++it)
  {
    const QString wu_name = m_state.result[*it].wu_name;
    if(!wu_name.isEmpty())
      if(workunits.contains(wu_name))
        m_state.workunit[wu_name].result_name = *it;
      else
        return false;
  }
  
  return true;
}

void KBSBOINCMonitor::notify(const QString &message, const QString &text)
{
  KNotifyClient::event(kapp->mainWidget()->winId(), message,
                       i18n("Host %1: %2").arg(m_location.host).arg(text));
}

void KBSBOINCMonitor::addAccounts(const QStringList &projects)
{
  for(QStringList::const_iterator project = projects.constBegin();
      project != projects.constEnd(); ++project)
  {
    m_accounts.insert(*project, new BOINCAccount());
    
    const QString fileName = formatFileName(*project);
    addFile(fileName);
    setMonitoring(fileName, false);
  }
}

void KBSBOINCMonitor::removeAccounts(const QStringList &projects)
{
  for(QStringList::const_iterator project = projects.constBegin();
      project != projects.constEnd(); ++project)
  {
    m_accounts.remove(*project);
    removeFile(formatFileName(*project));
  }
}
        
void KBSBOINCMonitor::addProjectMonitors(const QStringList &projects)
{
  KBSTreeNode *node = static_cast<KBSTreeNode*>(parent());
  if(NULL == node) return;
  
  KBSDocument *document = static_cast<KBSDocument*>(node->findAncestor("KBSDocument"));
  if(NULL == document) return;
  
  for(QStringList::const_iterator project = projects.constBegin();
      project != projects.constEnd(); ++project)
  {
    KBSProjectPlugin *plugin = document->plugin(*project);
    if(NULL == plugin) continue;
    
    KBSProjectMonitor *monitor = plugin->createProjectMonitor(*project, this);
    if(NULL == monitor) continue;
    
    m_projectMonitors.insert(*project, monitor);
  }
}

void KBSBOINCMonitor::removeProjectMonitors(const QStringList &projects)
{
  for(QStringList::const_iterator project = projects.constBegin();
      project != projects.constEnd(); ++project)
    m_projectMonitors.remove(*project);
}

void KBSBOINCMonitor::updateTaskMonitor(unsigned task, const QString &result, bool add)
{
  if(add) {
    const QString project = this->project(m_state.result[result]);
    if(project.isEmpty()) return;
  
    KBSTreeNode *node = static_cast<KBSTreeNode*>(parent());
    if(NULL == node) return;
  
    KBSDocument *document = static_cast<KBSDocument*>(node->findAncestor("KBSDocument"));
    if(NULL == document) return;
    
    KBSProjectPlugin *plugin = document->plugin(project);
    if(NULL == plugin) return;
    
    KBSTaskMonitor *monitor = plugin->createTaskMonitor(task, this);
    if(NULL == monitor) return;
    
    m_taskMonitors.insert(task, monitor);
  } else
    m_taskMonitors.remove(task);
}

void KBSBOINCMonitor::updateFile(const QString &fileName)
{
  if(!(file(fileName)->ok)) return;
  
  if(BOINCClientStateFile == fileName)
  {
    {
      const unsigned core_client_version =
        m_state.core_client.major_version * 100 + m_state.core_client.minor_version;
    
      if(0 == m_location.port)
        m_rpcMonitor->setPort(core_client_version >= 420 ? 1043 : 31416);
    }
    {
      QStringList added, names, removed = m_projects;
     
      m_projects = m_state.project.keys();
      for(QStringList::iterator project = m_projects.begin();
          project != m_projects.end(); ++project)
        if(removed.contains(*project))
          removed.remove(*project);
        else {
          added << *project;
          names << m_state.project[*project].project_name;
        }
      if(!added.isEmpty()) emit projectsAdded(added);
      if(!removed.isEmpty()) emit projectsRemoved(removed);
      
      if(!(m_startup || names.isEmpty()))
        notify("ProjectAttached",
               i18n("project %1 has been attached",
                    "projects %1 have been attached",
                    added.count())
                 .arg(names.join(", ")));
    }
    {
      QStringList added, removed = m_apps;
      
      m_apps = m_state.app.keys();
      for(QStringList::iterator app = m_apps.begin();
          app != m_apps.end(); ++app)
        if(removed.contains(*app))
          removed.remove(*app);
        else
          added << *app;
      if(!added.isEmpty()) emit appsAdded(added);
      if(!removed.isEmpty()) emit appsRemoved(removed);
    }
    {
      QStringList added, removed = m_workunits;
                  
      m_workunits = m_state.workunit.keys();
      for(QStringList::iterator workunit = m_workunits.begin();
          workunit != m_workunits.end(); ++workunit)
        if(removed.contains(*workunit))
          removed.remove(*workunit);
        else
          added << *workunit;
      if(!added.isEmpty()) emit workunitsAdded(added);
      if(!removed.isEmpty()) emit workunitsRemoved(removed);
      
      if(!(m_startup || added.isEmpty()))
        notify("WorkunitDownloading",
               i18n("work unit %1 is being downloaded",
                    "workunits %1 are being downloaded",
                    added.count())
                 .arg(added.join(", ")));
    }
    {
      QStringList added, removed = m_results;
      
      m_results = m_state.result.keys();
      for(QStringList::iterator result = m_results.begin();
          result != m_results.end(); ++result)
        if(removed.contains(*result))
          removed.remove(*result);
        else
          added << *result;
      if(!added.isEmpty()) emit resultsAdded(added);
      if(!removed.isEmpty()) emit resultsRemoved(removed);
    }
    {
      QStringList added, workunits, removed = m_complete;
      
      m_complete.clear();
      for(QStringList::iterator result = m_results.begin();
          result != m_results.end(); ++result)
        if(m_state.result[*result].ready_to_report)
        {
          m_complete << *result;
          if(removed.contains(*result))
            removed.remove(*result);
          else {
            added << *result;
            workunits << m_state.result[*result].wu_name;
          }
        }
      if(!(m_startup || added.isEmpty())) emit resultsCompleted(added);
      
      if(!(m_startup || workunits.isEmpty()))
        notify("WorkunitCompleted",
               i18n("work unit %1 has been completed",
                    "workunits %1 have been completed",
                    workunits.count())
                 .arg(workunits.join(", "))); 
    }
    {
      QMap<unsigned,BOINCActiveTask> removed = m_tasks;
      QValueList<unsigned> running;
      QStringList switched;
      
      m_tasks = m_state.active_task_set.active_task;
      for(QMap<unsigned,BOINCActiveTask>::iterator task = m_tasks.begin();
          task != m_tasks.end(); ++task)
      {
        const QString newResult = (*task).result_name;
        
        if((*task).scheduler_state > 1)
          running << task.key();
        
        if(removed.contains(task.key()))
        {
          const QString oldResult = removed[task.key()].result_name;
        
          if(oldResult != newResult)
          {
            emit workunitActivated(task.key(), m_state.result[oldResult].wu_name, false);
            emit workunitActivated(task.key(), m_state.result[newResult].wu_name, true);
            emit resultActivated(task.key(), oldResult, false);
            emit resultActivated(task.key(), newResult, true);
            
            switched << QString::number(task.key());
          }
          else if(m_running.contains(task.key()) ^ running.contains(task.key()))
            switched << QString::number(task.key());
          
          removed.remove(task.key());
        }
        else
        {
          emit workunitActivated(task.key(), m_state.result[newResult].wu_name, true);
          emit resultActivated(task.key(), newResult, true);
            
          switched << QString::number(task.key());
        }
      }
      for(QMap<unsigned,BOINCActiveTask>::iterator task = removed.begin();
          task != removed.end(); ++task)
      {
        const QString oldResult = removed[task.key()].result_name;
          
        emit workunitActivated(task.key(), m_state.result[oldResult].wu_name, false);
        emit resultActivated(task.key(), oldResult, false);
           
        switched << QString::number(task.key());
      }
      
      if(!(m_startup || switched.isEmpty()))
        notify("TaskSwitch",
               i18n("there was a task switch on slot %1",
                    "there were task switches involving slots %1",
                    switched.count())
                 .arg(switched.join(", "))); 
        
      m_running = running;
    }
    
    m_startup = false;
    
    emit stateUpdated();
  }
  else
  {
    QString project = parseFileName(fileName);
    if(NULL == m_accounts.find(project)) return;
    
    emit accountUpdated(project);
  }
}

#include "kbsboincmonitor.moc"

Generated by  Doxygen 1.6.0   Back to index