kplato

kpttask.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 Thomas zander <zander@kde.org>
00003    Copyright (C) 2004 Dag Andersen <danders@get2net.dk>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kpttask.h"
00022 #include "kptproject.h"
00023 #include "kpttaskdialog.h"
00024 #include "kptduration.h"
00025 #include "kptrelation.h"
00026 #include "kptdatetime.h"
00027 #include "kptcalendar.h"
00028 #include "kpteffortcostmap.h"
00029 #include "kptschedule.h"
00030 
00031 #include <qdom.h>
00032 #include <qbrush.h>
00033 #include <kdebug.h>
00034 
00035 namespace KPlato
00036 {
00037 
00038 Task::Task(Node *parent) : Node(parent), m_resource() {
00039     //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
00040     m_resource.setAutoDelete(true);
00041     Duration d(1, 0, 0);
00042     m_effort = new Effort(d);
00043     m_effort->setOptimisticRatio(-10);
00044     m_effort->setPessimisticRatio(20);
00045     m_requests = 0;
00046 
00047     if (m_parent)
00048         m_leader = m_parent->leader();
00049     
00050     m_schedules.setAutoDelete(true);
00051     m_parentProxyRelations.setAutoDelete(true);
00052     m_childProxyRelations.setAutoDelete(true);
00053 }
00054 
00055 Task::Task(Task &task, Node *parent) 
00056     : Node(task, parent), 
00057       m_resource() {
00058     //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
00059     m_resource.setAutoDelete(true);
00060     
00061     m_parentProxyRelations.setAutoDelete(true);
00062     m_childProxyRelations.setAutoDelete(true);
00063     m_requests = 0;
00064     
00065     m_effort = task.effort() ? new Effort(*(task.effort())) 
00066                              : new Effort(); // Avoid crash, (shouldn't be zero)
00067 }
00068 
00069 
00070 Task::~Task() {
00071     delete m_effort;
00072 }
00073 
00074 int Task::type() const {
00075     if ( numChildren() > 0) {
00076       return Node::Type_Summarytask;
00077     }
00078     else if ( 0 == effort()->expected().seconds() ) {
00079         return Node::Type_Milestone;
00080     }
00081     else {
00082         return Node::Type_Task;
00083     }
00084 }
00085 
00086 
00087 
00088 Duration *Task::getExpectedDuration() {
00089     //kdDebug()<<k_funcinfo<<endl;
00090     // Duration should already be calculated
00091     return m_currentSchedule ? new Duration(m_currentSchedule->duration) : new Duration();
00092 }
00093 
00094 Duration *Task::getRandomDuration() {
00095     return 0L;
00096 }
00097 
00098 ResourceGroupRequest *Task::resourceGroupRequest(ResourceGroup *group) const {
00099     if (m_requests)
00100         return m_requests->find(group);
00101     return 0;
00102 }
00103 
00104 void Task::clearResourceRequests() {
00105     if (m_requests)
00106         m_requests->clear();
00107 }
00108 
00109 void Task::addRequest(ResourceGroup *group, int numResources) {
00110     addRequest(new ResourceGroupRequest(group, numResources));
00111 }
00112 
00113 void Task::addRequest(ResourceGroupRequest *request) {
00114     if (!m_requests)
00115         m_requests = new ResourceRequestCollection(*this);
00116     m_requests->addRequest(request);
00117 }
00118 
00119 void Task::takeRequest(ResourceGroupRequest *request) {
00120     if (m_requests) {
00121         m_requests->takeRequest(request);
00122         if (m_requests->isEmpty()) {
00123             delete m_requests;
00124             m_requests = 0;
00125         }
00126     }
00127 }
00128 
00129 int Task::units() const {
00130     if (!m_requests)
00131         return 0;
00132     return m_requests->units();
00133 }
00134 
00135 int Task::workUnits() const {
00136     if (!m_requests)
00137         return 0;
00138     return m_requests->workUnits();
00139 }
00140 
00141 void Task::makeAppointments() {
00142     if (m_currentSchedule == 0)
00143         return;
00144     if (type() == Node::Type_Task) {
00145         if (m_requests) {
00146             //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl;
00147             m_requests->makeAppointments(m_currentSchedule);
00148             //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl;
00149         }
00150     } else if (type() == Node::Type_Summarytask) {
00151         QPtrListIterator<Node> nit(m_nodes);
00152         for ( ; nit.current(); ++nit ) {
00153             nit.current()->makeAppointments();
00154         }
00155     } else if (type() == Node::Type_Milestone) {
00156         //kdDebug()<<k_funcinfo<<"Milestone not implemented"<<endl;
00157         // Well, shouldn't have resources anyway...
00158     }
00159 }
00160 
00161 void Task::calcResourceOverbooked() {
00162     if (m_currentSchedule)
00163         m_currentSchedule->calcResourceOverbooked();
00164 }
00165 
00166 // A new constraint means start/end times and duration must be recalculated
00167 void Task::setConstraint(Node::ConstraintType type) {
00168     m_constraint = type;
00169 }
00170 
00171 
00172 bool Task::load(QDomElement &element, Project &project) {
00173     // Load attributes (TODO: Handle different types of tasks, milestone, summary...)
00174     QString s;
00175     bool ok = false;
00176     m_id = element.attribute("id");
00177     
00178     m_name = element.attribute("name");
00179     m_leader = element.attribute("leader");
00180     m_description = element.attribute("description");
00181     //kdDebug()<<k_funcinfo<<m_name<<": id="<<m_id<<endl;
00182 
00183     // Allow for both numeric and text
00184     QString constraint = element.attribute("scheduling","0");
00185     m_constraint = (Node::ConstraintType)constraint.toInt(&ok);
00186     if (!ok)
00187         Node::setConstraint(constraint); // hmmm, why do I need Node::?
00188 
00189     s = element.attribute("constraint-starttime");
00190     if (s != "")
00191         m_constraintStartTime = DateTime::fromString(s);
00192     s = element.attribute("constraint-endtime");
00193     if ( s != "")
00194         m_constraintEndTime = DateTime::fromString(s);
00195     
00196     m_startupCost = element.attribute("startup-cost", "0.0").toDouble();
00197     m_shutdownCost = element.attribute("shutdown-cost", "0.0").toDouble();
00198     
00199     m_wbs = element.attribute("wbs", "");
00200     
00201     // Load the project children
00202     QDomNodeList list = element.childNodes();
00203     for (unsigned int i=0; i<list.count(); ++i) {
00204         if (list.item(i).isElement()) {
00205             QDomElement e = list.item(i).toElement();
00206 
00207             if (e.tagName() == "project") {
00208                 // Load the subproject
00209                 Project *child = new Project(this);
00210                 if (child->load(e)) {
00211                     if (!project.addSubTask(child, this)) {
00212                         delete child;  // TODO: Complain about this
00213                     }
00214                 } else {
00215                     // TODO: Complain about this
00216                     delete child;
00217                 }
00218             } else if (e.tagName() == "task") {
00219                 // Load the task
00220                 Task *child = new Task(this);
00221                 if (child->load(e, project)) {
00222                     if (!project.addSubTask(child, this)) {
00223                         delete child;  // TODO: Complain about this
00224                     }
00225                 } else {
00226                     // TODO: Complain about this
00227                     delete child;
00228                 }
00229             } else if (e.tagName() == "resource") {
00230                 // TODO: Load the resource (projects don't have resources yet)
00231             } else if (e.tagName() == "effort") {
00232                 //  Load the effort
00233                 m_effort->load(e);
00234             } else if (e.tagName() == "resourcegroup-request") {
00235                 // Load the resource request
00236                 ResourceGroupRequest *r = new ResourceGroupRequest();
00237                 if (r->load(e, project)) {
00238                     addRequest(r);
00239                 } else {
00240                     kdError()<<k_funcinfo<<"Failed to load resource request"<<endl;
00241                     delete r;
00242                 }
00243             } else if (e.tagName() == "progress") {
00244                 m_progress.started = (bool)e.attribute("started", "0").toInt();
00245                 m_progress.finished = (bool)e.attribute("finished", "0").toInt();
00246                 
00247                 s = e.attribute("startTime");
00248                 if (s != "")
00249                     m_progress.startTime = DateTime::fromString(s);
00250                 s = e.attribute("finishTime");
00251                 if (s != "")
00252                     m_progress.finishTime = DateTime::fromString(s);
00253                 m_progress.percentFinished = e.attribute("percent-finished", "0").toInt();
00254                 m_progress.remainingEffort = Duration::fromString(e.attribute("remaining-effort"));
00255                 m_progress.totalPerformed = Duration::fromString(e.attribute("performed-effort"));
00256             } else if (e.tagName() == "schedules") {
00257                 QDomNodeList lst = e.childNodes();
00258                 for (unsigned int i=0; i<lst.count(); ++i) {
00259                     if (lst.item(i).isElement()) {
00260                         QDomElement el = lst.item(i).toElement();
00261                         if (el.tagName() == "schedule") {
00262                             NodeSchedule *sch = new NodeSchedule();
00263                             if (sch->loadXML(el)) {
00264                                 sch->setNode(this);
00265                                 addSchedule(sch);
00266                             } else {
00267                                 kdError()<<k_funcinfo<<"Failed to load schedule"<<endl;
00268                                 delete sch;
00269                             }
00270                         }
00271                     }
00272                 }
00273             }
00274         }
00275     }
00276     //kdDebug()<<k_funcinfo<<m_name<<" loaded"<<endl;
00277     return true;
00278 }
00279 
00280 
00281 void Task::save(QDomElement &element)  const {
00282     QDomElement me = element.ownerDocument().createElement("task");
00283     element.appendChild(me);
00284 
00285     //TODO: Handle different types of tasks, milestone, summary...
00286     me.setAttribute("id", m_id);
00287     me.setAttribute("name", m_name);
00288     me.setAttribute("leader", m_leader);
00289     me.setAttribute("description", m_description);
00290 
00291     me.setAttribute("scheduling",constraintToString());
00292     me.setAttribute("constraint-starttime",m_constraintStartTime.toString(Qt::ISODate));
00293     me.setAttribute("constraint-endtime",m_constraintEndTime.toString(Qt::ISODate));    
00294 
00295     me.setAttribute("startup-cost", m_startupCost);
00296     me.setAttribute("shutdown-cost", m_shutdownCost);
00297     
00298     me.setAttribute("wbs", m_wbs);
00299     
00300     m_effort->save(me);
00301 
00302     QDomElement el = me.ownerDocument().createElement("progress");
00303     me.appendChild(el);
00304     el.setAttribute("started", m_progress.started);
00305     el.setAttribute("finished", m_progress.finished);
00306     el.setAttribute("startTime", m_progress.startTime.toString(Qt::ISODate));
00307     el.setAttribute("finishTime", m_progress.finishTime.toString(Qt::ISODate));
00308     el.setAttribute("percent-finished", m_progress.percentFinished);
00309     el.setAttribute("remaining-effort", m_progress.remainingEffort.toString());
00310     el.setAttribute("performed-effort", m_progress.totalPerformed.toString());
00311     
00312     if (!m_schedules.isEmpty()) {
00313         QDomElement schs = me.ownerDocument().createElement("schedules");
00314         me.appendChild(schs);
00315         QIntDictIterator<Schedule> it = m_schedules;
00316         for (; it.current(); ++it) {
00317             if (!it.current()->isDeleted()) {
00318                 it.current()->saveXML(schs);
00319             }
00320         }
00321     }
00322     if (m_requests) {
00323         m_requests->save(me);
00324     }
00325     for (int i=0; i<numChildren(); i++) {
00326         getChildNode(i)->save(me);
00327     }
00328 }
00329 
00330 void Task::saveAppointments(QDomElement &element, long id) const {
00331     //kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
00332     Schedule *sch = findSchedule(id);
00333     if (sch) {
00334         sch->saveAppointments(element);
00335     }
00336     QPtrListIterator<Node> it(m_nodes);
00337     for (; it.current(); ++it ) {
00338         it.current()->saveAppointments(element, id);
00339     }
00340 }
00341 
00342 EffortCostMap Task::plannedEffortCostPrDay(const QDate &start, const QDate &end) const {
00343     //kdDebug()<<k_funcinfo<<m_name<<endl;
00344     if (m_currentSchedule) {
00345         return m_currentSchedule->plannedEffortCostPrDay(start, end);
00346     }
00347     return EffortCostMap();
00348 }
00349 
00350 // Returns the total planned effort for this task (or subtasks) 
00351 Duration Task::plannedEffort() {
00352    //kdDebug()<<k_funcinfo<<endl;
00353     Duration eff;
00354     if (type() == Node::Type_Summarytask) {
00355         QPtrListIterator<Node> it(childNodeIterator());
00356         for (; it.current(); ++it) {
00357             eff += it.current()->plannedEffort();
00358         }
00359     } else if (m_currentSchedule) {
00360         eff = m_currentSchedule->plannedEffort();
00361     }
00362     return eff;
00363 }
00364 
00365 // Returns the total planned effort for this task (or subtasks) on date
00366 Duration Task::plannedEffort(const QDate &date) {
00367    //kdDebug()<<k_funcinfo<<endl;
00368     Duration eff;
00369     if (type() == Node::Type_Summarytask) {
00370         QPtrListIterator<Node> it(childNodeIterator());
00371         for (; it.current(); ++it) {
00372             eff += it.current()->plannedEffort(date);
00373         }
00374     } else if (m_currentSchedule) {
00375         eff = m_currentSchedule->plannedEffort(date);
00376     }
00377     return eff;
00378 }
00379 
00380 // Returns the total planned effort for this task (or subtasks) upto and including date
00381 Duration Task::plannedEffortTo(const QDate &date) {
00382     //kdDebug()<<k_funcinfo<<endl;
00383     Duration eff;
00384     if (type() == Node::Type_Summarytask) {
00385         QPtrListIterator<Node> it(childNodeIterator());
00386         for (; it.current(); ++it) {
00387             eff += it.current()->plannedEffortTo(date);
00388         }
00389     } else if (m_currentSchedule) {
00390         eff = m_currentSchedule->plannedEffortTo(date);
00391     }
00392     return eff;
00393 }
00394 
00395 // Returns the total planned effort for this task (or subtasks) 
00396 Duration Task::actualEffort() {
00397    //kdDebug()<<k_funcinfo<<endl;
00398     Duration eff;
00399     if (type() == Node::Type_Summarytask) {
00400         QPtrListIterator<Node> it(childNodeIterator());
00401         for (; it.current(); ++it) {
00402             eff += it.current()->actualEffort();
00403         }
00404     } else {
00405         eff = m_progress.totalPerformed;
00406     }
00407     /* If we want to register pr resource...
00408     } else if (m_currentSchedule) {
00409         eff = m_currentSchedule->actualEffort();
00410     }*/
00411     return eff;
00412 }
00413 
00414 // Returns the total planned effort for this task (or subtasks) on date
00415 Duration Task::actualEffort(const QDate &date) {
00416    //kdDebug()<<k_funcinfo<<endl;
00417     Duration eff;
00418     if (type() == Node::Type_Summarytask) {
00419         QPtrListIterator<Node> it(childNodeIterator());
00420         for (; it.current(); ++it) {
00421             eff += it.current()->actualEffort(date);
00422         }
00423     } else if (m_currentSchedule) {
00424         eff = m_currentSchedule->actualEffort(date);
00425     }
00426     return eff;
00427 }
00428 
00429 // Returns the total planned effort for this task (or subtasks) on date
00430 Duration Task::actualEffortTo(const QDate &date) {
00431    //kdDebug()<<k_funcinfo<<endl;
00432     Duration eff;
00433     if (type() == Node::Type_Summarytask) {
00434         QPtrListIterator<Node> it(childNodeIterator());
00435         for (; it.current(); ++it) {
00436             eff += it.current()->actualEffortTo(date);
00437         }
00438     } else if (m_currentSchedule) {
00439         eff = m_currentSchedule->actualEffortTo(date);
00440     }
00441     return eff;
00442 }
00443 
00444 double Task::plannedCost() {
00445     //kdDebug()<<k_funcinfo<<endl;
00446     double c = 0;
00447     if (type() == Node::Type_Summarytask) {
00448         QPtrListIterator<Node> it(childNodeIterator());
00449         for (; it.current(); ++it) {
00450             c += it.current()->plannedCost();
00451         }
00452     } else if (m_currentSchedule) {
00453         c = m_currentSchedule->plannedCost();
00454     }
00455     return c;
00456 }
00457 
00458 double Task::plannedCost(const QDate &date) {
00459     //kdDebug()<<k_funcinfo<<endl;
00460     double c = 0;
00461     if (type() == Node::Type_Summarytask) {
00462         QPtrListIterator<Node> it(childNodeIterator());
00463         for (; it.current(); ++it) {
00464             c += it.current()->plannedCost(date);
00465         }
00466     } else if (m_currentSchedule) {
00467         c = m_currentSchedule->plannedCost(date);
00468     }
00469     return c;
00470 }
00471 
00472 double Task::plannedCostTo(const QDate &date) {
00473     //kdDebug()<<k_funcinfo<<endl;
00474     double c = 0;
00475     if (type() == Node::Type_Summarytask) {
00476         QPtrListIterator<Node> it(childNodeIterator());
00477         for (; it.current(); ++it) {
00478             c += it.current()->plannedCostTo(date);
00479         }
00480     } else if (m_currentSchedule) {
00481         c = m_currentSchedule->plannedCostTo(date);
00482     }
00483     return c;
00484 }
00485 
00486 double Task::actualCost() {
00487     //kdDebug()<<k_funcinfo<<endl;
00488     double c = 0;
00489     if (type() == Node::Type_Summarytask) {
00490         QPtrListIterator<Node> it(childNodeIterator());
00491         for (; it.current(); ++it) {
00492             c += it.current()->actualCost();
00493         }
00494     } else if (m_currentSchedule) {
00495         c = m_currentSchedule->actualCost();
00496     }
00497     return c;
00498 }
00499 
00500 double Task::actualCost(const QDate &date) {
00501     //kdDebug()<<k_funcinfo<<endl;
00502     double c = 0;
00503     if (type() == Node::Type_Summarytask) {
00504         QPtrListIterator<Node> it(childNodeIterator());
00505         for (; it.current(); ++it) {
00506             c += it.current()->actualCost(date);
00507         }
00508     } else if (m_currentSchedule) {
00509         c = m_currentSchedule->actualCost(date);
00510     }
00511     return c;
00512 }
00513 
00514 double Task::actualCostTo(const QDate &date) {
00515     //kdDebug()<<k_funcinfo<<endl;
00516     double c = 0;
00517     if (type() == Node::Type_Summarytask) {
00518         QPtrListIterator<Node> it(childNodeIterator());
00519         for (; it.current(); ++it) {
00520             c += it.current()->actualCostTo(date);
00521         }
00522     } else if (m_currentSchedule) {
00523         c = m_currentSchedule->actualCostTo(date);
00524     }
00525     return c;
00526 }
00527 
00528 //FIXME Handle summarytasks
00529 double Task::effortPerformanceIndex(const QDate &date, bool *error) {
00530     double res = 0.0;
00531     Duration ae = actualEffortTo(date);
00532     
00533     bool e = (ae == Duration::zeroDuration || m_progress.percentFinished == 0);
00534     if (error) {
00535         *error = e;
00536     }
00537     if (!e) {
00538         res = (plannedEffortTo(date).toDouble() * ((double)m_progress.percentFinished/100.0) / ae.toDouble());
00539     }
00540     return res;
00541 }
00542 
00543 //FIXME Handle summarytasks
00544 double Task::costPerformanceIndex(const QDate &date, bool *error) {
00545     double res = 0.0;
00546     Duration ac = Q_INT64(actualCostTo(date));
00547     
00548     bool e = (ac == Duration::zeroDuration || m_progress.percentFinished == 0);
00549     if (error) {
00550         *error = e;
00551     }
00552     if (!e) {
00553         res = (plannedCostTo(date) * m_progress.percentFinished)/(100 * actualCostTo(date));
00554     }
00555     return res;
00556 }
00557 
00558 void Task::initiateCalculation(Schedule &sch) {
00559     //kdDebug()<<k_funcinfo<<m_name<<" schedule: "<<(sch?sch->name():"None")<<" id="<<(sch?sch->id():-1)<<endl;
00560     m_visitedForward = false;
00561     m_visitedBackward = false;
00562     m_currentSchedule = createSchedule(&sch);
00563     m_currentSchedule->initiateCalculation();
00564     clearProxyRelations();
00565     Node::initiateCalculation(sch);
00566 }
00567 
00568 
00569 void Task::initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks/*, QPtrList<Node> &milestones*/) {
00570     //kdDebug()<<k_funcinfo<<m_name<<endl;
00571     if (type() == Node::Type_Summarytask) {
00572         summarytasks.append(this);
00573         // propagate my relations to my children and dependent nodes
00574         
00575         QPtrListIterator<Node> nodes = m_nodes;
00576         for (; nodes.current(); ++nodes) {
00577             if (!dependParentNodes().isEmpty()) 
00578                 nodes.current()->addParentProxyRelations(dependParentNodes());
00579             if (!dependChildNodes().isEmpty()) 
00580                 nodes.current()->addChildProxyRelations(dependChildNodes());
00581             nodes.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
00582         }        
00583     } else {
00584         if (isEndNode()) {
00585             endnodes.append(this);
00586             //kdDebug()<<k_funcinfo<<"endnodes append: "<<m_name<<endl;
00587         }
00588         if (isStartNode()) {
00589             startnodes.append(this);
00590             //kdDebug()<<k_funcinfo<<"startnodes append: "<<m_name<<endl;
00591         }
00592     }
00593 }
00594 
00595 DateTime Task::calculatePredeccessors(const QPtrList<Relation> &list, int use) {
00596     DateTime time;
00597     QPtrListIterator<Relation> it = list;
00598     for (; it.current(); ++it) {
00599         if (it.current()->parent()->type() == Type_Summarytask) {
00600             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
00601             continue; // skip summarytasks
00602         }
00603         DateTime t = it.current()->parent()->calculateForward(use); // early finish
00604         switch (it.current()->type()) {
00605             case Relation::StartStart:
00606                 // I can't start earlier than my predesseccor
00607                 t = it.current()->parent()->getEarliestStart() + it.current()->lag();
00608                 break;
00609             case Relation::FinishFinish:
00610                 // I can't finish earlier than my predeccessor, so
00611                 // I can't start earlier than it's (earlyfinish+lag)- my duration
00612                 t += it.current()->lag();
00613                 t -= duration(t, use, true);
00614                 break;
00615             default:
00616                 t += it.current()->lag();
00617                 break;
00618         }
00619         if (!time.isValid() || t > time)
00620             time = t;
00621     }
00622     //kdDebug()<<time.toString()<<"                  "<<m_name<<" calculatePredeccessors() ("<<list.count()<<")"<<endl;
00623     return time;
00624 }
00625 DateTime Task::calculateForward(int use) {
00626     //kdDebug()<<k_funcinfo<<m_name<<<<endl;
00627     if (m_currentSchedule == 0) {
00628         return DateTime();
00629     }
00630     Schedule *cs = m_currentSchedule;
00631     if (m_visitedForward) {
00632     //kdDebug()<<earliestStart.toString()<<" + "<<m_durationBackward.toString()<<" "<<m_name<<" calculateForward() (visited)"<<endl;
00633         return cs->earliestStart + m_durationForward;
00634     }
00635     // First, calculate all predecessors
00636     if (!dependParentNodes().isEmpty()) {
00637         DateTime time = calculatePredeccessors(dependParentNodes(), use);
00638         if (time.isValid() && time > cs->earliestStart) {
00639             cs->earliestStart = time;
00640         }
00641     }
00642     if (!m_parentProxyRelations.isEmpty()) {
00643         DateTime time = calculatePredeccessors(m_parentProxyRelations, use);
00644         if (time.isValid() && time > cs->earliestStart) {
00645             cs->earliestStart = time;
00646         }
00647     }
00648     if (type() == Node::Type_Task) {
00649         m_durationForward = m_effort->effort(use);
00650         switch (constraint()) {
00651             case Node::ASAP:
00652             case Node::ALAP:
00653                 cs->earliestStart = workStartAfter(cs->earliestStart);
00654                 m_durationForward = duration(cs->earliestStart, use, false);
00655                 //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<endl;
00656                 break;
00657             case Node::MustFinishOn:
00658                 m_durationForward = duration(m_constraintEndTime, use, true);
00659                 cs->earliestStart = m_constraintEndTime - m_durationForward;
00660                 break;
00661             case Node::FinishNotLater:
00662                 m_durationForward = duration(cs->earliestStart, use, false);
00663                 if (cs->earliestStart + m_durationForward > m_constraintEndTime) {
00664                     m_durationForward = duration(m_constraintEndTime, use, true);
00665                     cs->earliestStart = m_constraintEndTime - m_durationForward;
00666                 }
00667                 break;
00668             case Node::MustStartOn:
00669                 cs->earliestStart = m_constraintStartTime;
00670                 m_durationForward = duration(cs->earliestStart, use, false);
00671                 break;
00672             case Node::StartNotEarlier:
00673                 if (cs->earliestStart < m_constraintStartTime) {
00674                     cs->earliestStart = m_constraintStartTime;
00675                 }
00676                 m_durationForward = duration(cs->earliestStart, use, false);
00677                 break;
00678             case Node::FixedInterval: {
00679                 cs->earliestStart = m_constraintStartTime;
00680                 m_durationForward = m_constraintEndTime - m_constraintStartTime;
00681                 break;
00682             }
00683         }
00684     } else if (type() == Node::Type_Milestone) {
00685         m_durationForward = Duration::zeroDuration;
00686         switch (constraint()) {
00687             case Node::MustFinishOn:
00688                 cs->earliestStart = m_constraintEndTime;
00689                 break;
00690             case Node::FinishNotLater:
00691                 if (cs->earliestStart > m_constraintEndTime) {
00692                     cs->earliestStart = m_constraintEndTime;
00693                 }
00694                 break;
00695             case Node::MustStartOn:
00696                 cs->earliestStart = m_constraintStartTime;
00697                 break;
00698             case Node::StartNotEarlier:
00699                 if (cs->earliestStart < m_constraintStartTime) {
00700                     cs->earliestStart = m_constraintStartTime;
00701                 }
00702                 break;
00703             case Node::FixedInterval:
00704                 cs->earliestStart = m_constraintStartTime;
00705                 break;
00706             default:
00707                 break;
00708         }
00709         //kdDebug()<<k_funcinfo<<m_name<<" "<<earliestStart.toString()<<endl
00710     } else if (type() == Node::Type_Summarytask) {
00711         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
00712     } else { // ???
00713         m_durationForward = Duration::zeroDuration;
00714     }
00715     
00716     //kdDebug()<<"Earlyfinish: "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<" "<<m_name<<" calculateForward()"<<endl;
00717     m_visitedForward = true;
00718     return cs->earliestStart + m_durationForward;
00719 }
00720 
00721 DateTime Task::calculateSuccessors(const QPtrList<Relation> &list, int use) {
00722     DateTime time;
00723     QPtrListIterator<Relation> it = list;
00724     for (; it.current(); ++it) {
00725         if (it.current()->child()->type() == Type_Summarytask) {
00726             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
00727             continue; // skip summarytasks
00728         }
00729         DateTime t = it.current()->child()->calculateBackward(use);
00730         switch (it.current()->type()) {
00731             case Relation::StartStart:
00732                 // I must start before my successor, so
00733                 // I can't finish later than it's (starttime-lag) + my duration
00734                 t -= it.current()->lag();
00735                 t += duration(t, use, false);
00736                 break;
00737             case Relation::FinishFinish:
00738                 // My successor cannot finish before me, so
00739                 // I can't finish later than it's latest finish - lag
00740                 t = it.current()->child()->getLatestFinish() -  it.current()->lag();
00741                 break;
00742             default:
00743                 t -= it.current()->lag();
00744                 break;
00745         }
00746         if (!time.isValid() || t < time)
00747             time = t;
00748     }
00749     //kdDebug()<<time.toString()<<"                  "<<m_name<<" calculateSuccessors() ("<<list.count()<<")"<<endl;
00750     return time;
00751 }
00752 DateTime Task::calculateBackward(int use) {
00753     //kdDebug()<<k_funcinfo<<m_name<<endl;
00754     if (m_currentSchedule == 0) {
00755         return DateTime();
00756     }
00757     Schedule *cs = m_currentSchedule;
00758     if (m_visitedBackward) {
00759     //kdDebug()<<latestFinish.toString()<<" - "<<m_durationBackward.toString()<<" "<<m_name<<" calculateBackward() (visited)"<<endl;
00760         return cs->latestFinish - m_durationBackward;
00761     }
00762     // First, calculate all successors
00763     if (!dependChildNodes().isEmpty()) {
00764         DateTime time = calculateSuccessors(dependChildNodes(), use);
00765         if (time.isValid() && time < cs->latestFinish) {
00766             cs->latestFinish = time;
00767         }
00768     }
00769     if (!m_childProxyRelations.isEmpty()) {
00770         DateTime time = calculateSuccessors(m_childProxyRelations, use);
00771         if (time.isValid() && time < cs->latestFinish) {
00772             cs->latestFinish = time;
00773         }
00774     }
00775     //kdDebug()<<k_funcinfo<<m_name<<": latestFinish="<<cs->latestFinish<<endl;
00776     if (type() == Node::Type_Task) {
00777         m_durationBackward = m_effort->effort(use);
00778         switch (constraint()) {
00779             case Node::ASAP:
00780             case Node::ALAP:
00781                 cs->latestFinish = workFinishBefore(cs->latestFinish);
00782                 m_durationBackward = duration(cs->latestFinish, use, true);
00783                 break;
00784             case Node::MustStartOn:
00785                 m_durationBackward = duration(m_constraintStartTime, use, false);
00786                 cs->latestFinish = m_constraintStartTime + m_durationBackward;
00787                 break;
00788             case Node::StartNotEarlier:
00789                 m_durationBackward = duration(cs->latestFinish, use, true);
00790                 if (cs->latestFinish - m_durationBackward < m_constraintStartTime) {
00791                     m_durationBackward = duration(m_constraintStartTime, use, false);
00792                     cs->latestFinish = m_constraintStartTime + m_durationBackward;
00793                 }
00794                 break;
00795             case Node::MustFinishOn:
00796                 cs->latestFinish = m_constraintEndTime;
00797                 m_durationBackward = duration(cs->latestFinish, use, true);
00798                 break;
00799             case Node::FinishNotLater:
00800                 if (cs->latestFinish > m_constraintEndTime) {
00801                     cs->latestFinish = m_constraintEndTime;
00802                 }
00803                 m_durationBackward = duration(cs->latestFinish, use, true);
00804                 break;
00805             case Node::FixedInterval: {
00806                 cs->latestFinish = m_constraintEndTime;
00807                 m_durationBackward = m_constraintEndTime - m_constraintStartTime;
00808                 break;
00809             }
00810         }
00811     } else if (type() == Node::Type_Milestone) {
00812         m_durationBackward = Duration::zeroDuration;
00813         switch (constraint()) {
00814             case Node::MustFinishOn:
00815                 cs->latestFinish = m_constraintEndTime;
00816                 break;
00817             case Node::FinishNotLater:
00818                 if (cs->latestFinish > m_constraintEndTime) {
00819                     cs->latestFinish = m_constraintEndTime;
00820                 }
00821                 break;
00822             case Node::MustStartOn:
00823                 cs->latestFinish = m_constraintStartTime;
00824                 break;
00825             case Node::StartNotEarlier:
00826                 if (cs->latestFinish < m_constraintStartTime) {
00827                     cs->latestFinish = m_constraintStartTime;
00828                 }
00829                 break;
00830             case Node::FixedInterval:
00831                 cs->latestFinish = m_constraintEndTime;
00832                 break;
00833             default:
00834                 break;
00835         }
00836         //kdDebug()<<k_funcinfo<<m_name<<" "<<cs->latestFinish<<endl;
00837     } else if (type() == Node::Type_Summarytask) {
00838         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
00839     } else { // ???
00840         m_durationBackward = Duration::zeroDuration;
00841     }
00842     //kdDebug()<<"Latestart: "<<cs->latestFinish<<"-"<<m_durationBackward.toString()<<"="<<(cs->latestFinish-m_durationBackward).toString()<<" "<<m_name<<" calculateBackward()"<<endl;
00843     m_visitedBackward = true;
00844     return cs->latestFinish - m_durationBackward;
00845 }
00846 
00847 DateTime Task::schedulePredeccessors(const QPtrList<Relation> &list, int use) {
00848     DateTime time;
00849     QPtrListIterator<Relation> it = list;
00850     for (; it.current(); ++it) {
00851         if (it.current()->parent()->type() == Type_Summarytask) {
00852             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
00853             continue; // skip summarytasks
00854         }
00855         // schedule the predecessors
00856         DateTime earliest = it.current()->parent()->getEarliestStart();
00857         DateTime t = it.current()->parent()->scheduleForward(earliest, use);
00858         switch (it.current()->type()) {
00859             case Relation::StartStart:
00860                 // I can't start before my predesseccor
00861                 t = it.current()->parent()->startTime() + it.current()->lag();
00862                 break;
00863             case Relation::FinishFinish:
00864                 // I can't end before my predecessor, so
00865                 // I can't start before it's endtime - my duration
00866                 t -= duration(t + it.current()->lag(), use, true);
00867                 break;
00868             default:
00869                 t += it.current()->lag();
00870                 break;
00871         }
00872         if (!time.isValid() || t > time)
00873             time = t;
00874     }
00875     //kdDebug()<<time.toString()<<" "<<m_name<<" schedulePredeccessors()"<<endl;
00876     return time;
00877 }
00878 
00879 DateTime Task::scheduleForward(const DateTime &earliest, int use) {
00880     //kdDebug()<<k_funcinfo<<m_name<<" earliest="<<earliest<<endl;
00881     if (m_currentSchedule == 0) {
00882         return DateTime();
00883     }
00884     Schedule *cs = m_currentSchedule;
00885     if (m_visitedForward) {
00886         return cs->endTime;
00887     }
00888     cs->notScheduled = false;
00889     cs->startTime = earliest > cs->earliestStart ? earliest : cs->earliestStart;
00890     // First, calculate all my own predecessors
00891     DateTime time = schedulePredeccessors(dependParentNodes(), use);
00892     if (time.isValid() && time > cs->startTime) {
00893         cs->startTime = time;
00894         //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
00895     }
00896     // Then my parents
00897     time = schedulePredeccessors(m_parentProxyRelations, use);
00898     if (time.isValid() && time > cs->startTime) {
00899         cs->startTime = time;
00900         //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
00901     }
00902     //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
00903     if(type() == Node::Type_Task) {
00904         cs->duration = m_effort->effort(use);
00905         switch (m_constraint) {
00906         case Node::ASAP:
00907             // cs->startTime calculated above
00908             //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
00909             cs->startTime = workStartAfter(cs->startTime);
00910             cs->duration = duration(cs->startTime, use, false);
00911             cs->endTime = cs->startTime + cs->duration;
00912             //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
00913             break;
00914         case Node::ALAP:
00915             // cd->startTime calculated above
00916             cs->duration = duration(cs->latestFinish, use, true);
00917             cs->endTime = cs->latestFinish;
00918             cs->startTime = cs->endTime - cs->duration;
00919             //kdDebug()<<k_funcinfo<<m_name<<" endTime="<<cs->endTime<<" latest="<<cs->latestFinish<<endl;
00920             break;
00921         case Node::StartNotEarlier:
00922             // cs->startTime calculated above
00923             //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cd->startTime.toString()<<endl;            
00924             if (cs->startTime < m_constraintStartTime) {
00925                 cs->startTime = m_constraintStartTime;
00926             }
00927             cs->startTime = workStartAfter(cs->startTime);
00928             cs->duration = duration(cs->startTime, use, false);
00929             cs->endTime = cs->startTime + cs->duration;
00930             if (cs->endTime > cs->latestFinish) {
00931                 cs->schedulingError = true;
00932             }
00933             break;
00934         case Node::FinishNotLater:
00935             // cs->startTime calculated above
00936             //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
00937             cs->duration = duration(cs->startTime, use, false);
00938             cs->endTime = cs->startTime + cs->duration;
00939             if (cs->endTime > m_constraintEndTime) {
00940                 cs->schedulingError = true;
00941                 cs->endTime = m_constraintEndTime;
00942                 cs->duration = duration(cs->endTime, use, true);
00943                 cs->startTime = cs->endTime - cs->duration;
00944             }
00945             break;
00946         case Node::MustStartOn:
00947             // cs->startTime calculated above
00948             //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
00949             if (m_constraintStartTime < cs->startTime ||
00950                 m_constraintStartTime > cs->latestFinish - m_durationBackward) {
00951                 cs->schedulingError = true;
00952             }
00953             cs->startTime = m_constraintStartTime;
00954             cs->duration = duration(cs->startTime, use, false);
00955             cs->endTime = cs->startTime + cs->duration;
00956             break;
00957         case Node::MustFinishOn:
00958             // cs->startTime calculated above
00959             //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
00960             if (m_constraintEndTime > cs->latestFinish ||
00961                 m_constraintEndTime < cs->earliestStart + m_durationForward) {
00962                 cs->schedulingError = true;
00963             }
00964             cs->endTime = m_constraintEndTime;
00965             cs->duration = duration(cs->endTime, use, true);
00966             cs->startTime = cs->endTime - cs->duration;
00967             break;
00968         case Node::FixedInterval: {
00969             // cs->startTime calculated above
00970             //kdDebug()<<"FixedInterval="<<m_constraintStartTime<<" "<<cs->startTime<<endl;
00971             if (cs->startTime < cs->earliestStart) {
00972                 cs->schedulingError = true;
00973             }
00974             cs->startTime = m_constraintStartTime;
00975             cs->endTime = m_constraintEndTime;
00976             cs->duration = cs->endTime - cs->startTime;
00977             cs->workStartTime = m_constraintStartTime;
00978             cs->workEndTime = m_constraintEndTime;
00979             //kdDebug()<<"FixedInterval="<<cs->startTime<<", "<<cs->endTime<<endl;
00980             break;
00981         }
00982         default:
00983             break;
00984         }
00985         if (m_requests) {
00986             m_requests->reserve(cs->startTime, cs->duration);
00987         }
00988     } else if (type() == Node::Type_Milestone) {
00989         switch (m_constraint) {
00990         case Node::ASAP: {
00991             cs->endTime = cs->startTime;
00992             break;
00993         }
00994         case Node::ALAP: {
00995             cs->startTime = cs->latestFinish;
00996             cs->endTime = cs->latestFinish;
00997             break;
00998         }
00999         case Node::MustStartOn:
01000         case Node::FixedInterval:
01001             //kdDebug()<<"Forw, MustStartOn: "<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
01002             if (m_constraintStartTime < cs->startTime ||
01003                 m_constraintStartTime > cs->latestFinish) {
01004                 cs->schedulingError = true;
01005             }
01006             cs->startTime = m_constraintStartTime;
01007             cs->endTime = m_constraintStartTime;
01008             break;
01009         case Node::MustFinishOn:
01010             if (m_constraintEndTime < cs->startTime ||
01011                 m_constraintEndTime > cs->latestFinish) {
01012                 cs->schedulingError = true;
01013             }
01014             cs->startTime = m_constraintEndTime;
01015             cs->endTime = m_constraintEndTime;
01016             break;
01017         case Node::StartNotEarlier:
01018             if (cs->startTime < m_constraintStartTime) {
01019                 cs->schedulingError = true;
01020             }
01021             cs->endTime = cs->startTime;
01022             break;
01023         case Node::FinishNotLater:
01024             if (cs->startTime > m_constraintEndTime) {
01025                 cs->schedulingError = true;
01026             }
01027             cs->endTime = cs->startTime;
01028             break;
01029         default:
01030             break;
01031         }
01032         cs->duration = Duration::zeroDuration;
01033         //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime<<", "<<cs->endTime<<endl;
01034     } else if (type() == Node::Type_Summarytask) {
01035         //shouldn't come here
01036         cs->endTime = cs->startTime;
01037         cs->duration = cs->endTime - cs->startTime;
01038         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
01039     }
01040     //kdDebug()<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<" "<<m_name<<" scheduleForward()"<<endl;
01041     m_visitedForward = true;
01042     return cs->endTime;
01043 }
01044 
01045 DateTime Task::scheduleSuccessors(const QPtrList<Relation> &list, int use) {
01046     DateTime time;
01047     QPtrListIterator<Relation> it = list;
01048     for (; it.current(); ++it) {
01049         if (it.current()->child()->type() == Type_Summarytask) {
01050             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->child()->name()<<endl;
01051             continue;
01052         }
01053         // get the successors starttime
01054         DateTime latest = it.current()->child()->getLatestFinish();
01055         DateTime t = it.current()->child()->scheduleBackward(latest, use);
01056         switch (it.current()->type()) {
01057             case Relation::StartStart:
01058                 // I can't start before my successor, so
01059                 // I can't finish later than it's starttime + my duration
01060                 t += duration(t - it.current()->lag(), use, false);
01061                 break;
01062             case Relation::FinishFinish:
01063                 t = it.current()->child()->endTime() - it.current()->lag();
01064                 break;
01065             default:
01066                 t -= it.current()->lag();
01067                 break;
01068         }
01069         if (!time.isValid() || t < time)
01070             time = t;
01071    }
01072    return time;
01073 }
01074 DateTime Task::scheduleBackward(const DateTime &latest, int use) {
01075     //kdDebug()<<k_funcinfo<<m_name<<": latest="<<latest<<endl;
01076     if (m_currentSchedule == 0) {
01077         return DateTime();
01078     }
01079     Schedule *cs = m_currentSchedule;
01080     if (m_visitedBackward) {
01081         return cs->startTime;
01082     }
01083     cs->notScheduled = false;
01084     cs->endTime = latest < cs->latestFinish ? latest : cs->latestFinish;
01085     // First, calculate all my own successors
01086     DateTime time = scheduleSuccessors(dependChildNodes(), use);
01087     if (time.isValid() && time < cs->endTime) {
01088         cs->endTime = time;
01089     }
01090     // Then my parents
01091     time = scheduleSuccessors(m_childProxyRelations, use);
01092     if (time.isValid() && time < cs->endTime) {
01093         cs->endTime = time;
01094     }
01095     if (type() == Node::Type_Task) {
01096         cs->duration = m_effort->effort(use);
01097         switch (m_constraint) {
01098         case Node::ASAP: {
01099             // cs->endTime calculated above
01100             //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<"  early="<<cs->earliestStart<<endl;
01101             cs->endTime = workFinishBefore(cs->endTime);
01102             cs->duration = duration(cs->earliestStart, use, false);
01103             cs->startTime = cs->earliestStart;
01104             DateTime e = cs->startTime + cs->duration;
01105             if (e > cs->endTime) {
01106                 cs->schedulingError = true;
01107             }
01108             cs->endTime = e;
01109             //kdDebug()<<k_funcinfo<<m_name<<": start="<<cs->startTime<<"+"<<cs->duration.toString()<<"="<<e<<" -> end="<<cs->endTime<<endl;
01110             break;
01111         }
01112         case Node::ALAP:
01113             // cs->endTime calculated above
01114             //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<"  late="<<cs->latestFinish<<endl;
01115             cs->endTime = workFinishBefore(cs->endTime);
01116             cs->duration = duration(cs->endTime, use, true);
01117             cs->startTime = cs->endTime - cs->duration;
01118             //kdDebug()<<k_funcinfo<<m_name<<": lateStart="<<cs->startTime<<endl;
01119             break;
01120         case Node::StartNotEarlier:
01121             // cs->endTime calculated above
01122             //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cs->endTime.toString()<<endl;
01123             cs->endTime = workFinishBefore(cs->endTime);
01124             cs->duration = duration(cs->endTime, use, true);
01125             cs->startTime = cs->endTime - cs->duration;
01126             if (cs->startTime < m_constraintStartTime) {
01127                 cs->schedulingError = true;
01128                 cs->startTime = m_constraintStartTime;
01129                 cs->duration = duration(cs->startTime, use, false);
01130                 cs->endTime = cs->startTime + cs->duration;
01131             }
01132             break;
01133         case Node::FinishNotLater:
01134             // cs->endTime calculated above
01135             //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->endTime.toString()<<endl;            
01136             if (cs->endTime > m_constraintEndTime) {
01137                 cs->schedulingError = true;
01138                 cs->endTime = m_constraintEndTime;
01139             }
01140             cs->endTime = workFinishBefore(cs->endTime);
01141             cs->duration = duration(cs->endTime, use, true);
01142             cs->startTime = cs->endTime - cs->duration;
01143             break;
01144         case Node::MustStartOn:
01145             // cs->endTime calculated above
01146             //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
01147             if (m_constraintStartTime < cs->earliestStart ||
01148                 m_constraintStartTime > cs->latestFinish - m_durationBackward) {
01149                 cs->schedulingError = true;
01150             }
01151             cs->startTime = m_constraintStartTime;
01152             cs->duration = duration(cs->startTime, use, false);
01153             cs->endTime = cs->startTime + cs->duration;
01154             break;
01155         case Node::MustFinishOn:
01156             // cs->endTime calculated above
01157             //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
01158             if (m_constraintEndTime > cs->latestFinish ||
01159                 m_constraintEndTime < cs->earliestStart + m_durationForward) {
01160                 cs->schedulingError = true;
01161             }
01162             cs->endTime = m_constraintEndTime;
01163             cs->duration = duration(cs->endTime, use, true);
01164             cs->startTime = cs->endTime - cs->duration;
01165             break;
01166         case Node::FixedInterval: {
01167             // cs->endTime calculated above
01168             //kdDebug()<<k_funcinfo<<"FixedInterval="<<m_constraintEndTime<<" "<<cs->endTime<<endl;
01169             if (m_constraintEndTime > cs->endTime) {
01170                 cs->schedulingError = true;
01171                 //kdDebug()<<k_funcinfo<<"FixedInterval error: "<<m_constraintEndTime<<" >  "<<cs->endTime<<endl;
01172             }
01173             cs->startTime = m_constraintStartTime;
01174             cs->endTime = m_constraintEndTime;
01175             cs->duration = cs->endTime - cs->startTime;
01176             cs->workStartTime = m_constraintStartTime;
01177             cs->workEndTime = m_constraintEndTime;
01178             break;
01179         }
01180         default:
01181             break;
01182         }
01183         if (m_requests) {
01184             m_requests->reserve(cs->startTime, cs->duration);
01185         }
01186     } else if (type() == Node::Type_Milestone) {
01187         switch (m_constraint) {
01188         case Node::ASAP:
01189             cs->startTime = cs->earliestStart;
01190             cs->endTime = cs->earliestStart;
01191             break;
01192         case Node::ALAP:
01193             cs->startTime = cs->latestFinish;
01194             cs->endTime = cs->latestFinish;
01195             break;
01196         case Node::MustStartOn:
01197         case Node::FixedInterval:
01198             if (m_constraintStartTime < cs->earliestStart ||
01199                 m_constraintStartTime > cs->endTime) {
01200                 cs->schedulingError = true;
01201             }
01202             cs->startTime = cs->earliestStart;
01203             cs->endTime = cs->earliestStart;
01204             break;
01205         case Node::MustFinishOn:
01206             if (m_constraintEndTime < cs->earliestStart ||
01207                 m_constraintEndTime > cs->endTime) {
01208                 cs->schedulingError = true;
01209             }
01210             cs->startTime = cs->earliestStart;
01211             cs->endTime = cs->earliestStart;
01212             break;
01213         case Node::StartNotEarlier:
01214             if (m_constraintStartTime > cs->endTime) {
01215                 cs->schedulingError = true;
01216             }
01217             cs->startTime = cs->endTime;
01218             break;
01219         case Node::FinishNotLater:
01220             if (m_constraintEndTime < cs->endTime) {
01221                 cs->schedulingError = true;
01222             }
01223             cs->startTime = cs->endTime;
01224             break;
01225         default:
01226             break;
01227         }
01228         cs->duration = Duration::zeroDuration;
01229     } else if (type() == Node::Type_Summarytask) {
01230         //shouldn't come here
01231         cs->startTime = cs->endTime;
01232         cs->duration = cs->endTime - cs->startTime;
01233         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
01234     }
01235     //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<endl;
01236     m_visitedBackward = true;
01237     return cs->startTime;
01238 }
01239 
01240 void Task::adjustSummarytask() {
01241     if (m_currentSchedule == 0)
01242         return;
01243     if (type() == Type_Summarytask) {
01244         DateTime start = m_currentSchedule->latestFinish;
01245         DateTime end = m_currentSchedule->earliestStart;
01246         QPtrListIterator<Node> it(m_nodes);
01247         for (; it.current(); ++it) {
01248             it.current()->adjustSummarytask();
01249             if (it.current()->startTime() < start)
01250                 start = it.current()->startTime();
01251             if (it.current()->endTime() > end)
01252                 end = it.current()->endTime();
01253         }
01254         m_currentSchedule->startTime = start;
01255         m_currentSchedule->endTime = end;
01256         m_currentSchedule->duration = end - start;
01257         m_currentSchedule->notScheduled = false;
01258         //kdDebug()<<k_funcinfo<<cs->name<<": "<<m_currentSchedule->startTime.toString()<<" : "<<m_currentSchedule->endTime.toString()<<endl;
01259     }
01260 }
01261 
01262 Duration Task::calcDuration(const DateTime &time, const Duration &effort, bool backward) {
01263     //kdDebug()<<"--------> calcDuration "<<(backward?"(B) ":"(F) ")<<m_name<<" time="<<time<<" effort="<<effort.toString(Duration::Format_Day)<<endl;
01264     
01265     // Allready checked: m_effort, m_currentSchedule and time.
01266     Duration dur = effort; // use effort as default duration
01267     if (m_effort->type() == Effort::Type_Effort) {
01268         if (m_requests == 0 || m_requests->isEmpty()) {
01269             m_currentSchedule->resourceError = true;
01270             return effort;
01271         }
01272         dur = m_requests->duration(time, effort, backward);
01273         if (dur == Duration::zeroDuration) {
01274             kdWarning()<<k_funcinfo<<"zero duration: Resource not available"<<endl;
01275             m_currentSchedule->resourceNotAvailable = true;
01276             dur = effort; //???
01277         }
01278         return dur;
01279     }
01280     if (m_effort->type() == Effort::Type_FixedDuration) {
01281         //TODO: Different types of fixed duration
01282         return dur; //
01283     }
01284     kdError()<<k_funcinfo<<"Unsupported effort type: "<<m_effort->type()<<endl;
01285     return dur;
01286 }
01287 
01288 void Task::clearProxyRelations() {
01289     m_parentProxyRelations.clear();
01290     m_childProxyRelations.clear();
01291 }
01292 
01293 void Task::addParentProxyRelations(QPtrList<Relation> &list) {
01294     //kdDebug()<<k_funcinfo<<m_name<<endl;
01295     if (type() == Type_Summarytask) {
01296         // propagate to my children
01297         //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
01298         QPtrListIterator<Node> nodes = m_nodes;
01299         for (; nodes.current(); ++nodes) {
01300             nodes.current()->addParentProxyRelations(list);
01301             nodes.current()->addParentProxyRelations(dependParentNodes());
01302         }        
01303     } else {
01304         // add 'this' as child relation to the relations parent
01305         //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
01306         QPtrListIterator<Relation> it = list;
01307         for (; it.current(); ++it) {
01308             it.current()->parent()->addChildProxyRelation(this, it.current());
01309             // add a parent relation to myself
01310             addParentProxyRelation(it.current()->parent(), it.current());
01311         }
01312     }
01313 }
01314 
01315 void Task::addChildProxyRelations(QPtrList<Relation> &list) {
01316     //kdDebug()<<k_funcinfo<<m_name<<endl;
01317     if (type() == Type_Summarytask) {
01318         // propagate to my children
01319         //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
01320         QPtrListIterator<Node> nodes = m_nodes;
01321         for (; nodes.current(); ++nodes) {
01322             nodes.current()->addChildProxyRelations(list);
01323             nodes.current()->addChildProxyRelations(dependChildNodes());
01324         }        
01325     } else {
01326         // add 'this' as parent relation to the relations child
01327         //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
01328         QPtrListIterator<Relation> it = list;
01329         for (; it.current(); ++it) {
01330             it.current()->child()->addParentProxyRelation(this, it.current());
01331             // add a child relation to myself
01332             addChildProxyRelation(it.current()->child(), it.current());
01333         }
01334     }
01335 }
01336 
01337 void Task::addParentProxyRelation(Node *node, const Relation *rel) {
01338     if (node->type() != Type_Summarytask) {
01339         if (type() == Type_Summarytask) {
01340             //kdDebug()<<"Add parent proxy from my children "<<m_name<<" to "<<node->name()<<endl;
01341             QPtrListIterator<Node> nodes = m_nodes;
01342             for (; nodes.current(); ++nodes) {
01343                 nodes.current()->addParentProxyRelation(node, rel);
01344             }
01345         } else {
01346             //kdDebug()<<"Add parent proxy from "<<node->name()<<" to (me) "<<m_name<<endl;
01347             m_parentProxyRelations.append(new ProxyRelation(node, this, rel->type(), rel->lag()));
01348         }
01349     }
01350 }
01351 
01352 void Task::addChildProxyRelation(Node *node, const Relation *rel) {
01353     if (node->type() != Type_Summarytask) {
01354         if (type() == Type_Summarytask) {
01355             //kdDebug()<<"Add child proxy from my children "<<m_name<<" to "<<node->name()<<endl;
01356             QPtrListIterator<Node> nodes = m_nodes;
01357             for (; nodes.current(); ++nodes) {
01358                 nodes.current()->addChildProxyRelation(node, rel);
01359             }
01360         } else {
01361             //kdDebug()<<"Add child proxy from (me) "<<m_name<<" to "<<node->name()<<endl;
01362             m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag()));
01363         }
01364     }
01365 }
01366 
01367 bool Task::isEndNode() const {
01368     QPtrListIterator<Relation> it = m_dependChildNodes;
01369     for (; it.current(); ++it) {
01370         if (it.current()->type() == Relation::FinishStart)
01371             return false;
01372     }
01373     QPtrListIterator<Relation> pit = m_childProxyRelations;
01374     for (; pit.current(); ++pit) {
01375         if (pit.current()->type() == Relation::FinishStart)
01376             return false;
01377     }
01378     return true;
01379 }
01380 bool Task::isStartNode() const {
01381     QPtrListIterator<Relation> it = m_dependParentNodes;
01382     for (; it.current(); ++it) {
01383         if (it.current()->type() == Relation::FinishStart ||
01384             it.current()->type() == Relation::StartStart)
01385             return false;
01386     }
01387     QPtrListIterator<Relation> pit = m_parentProxyRelations;
01388     for (; pit.current(); ++pit) {
01389         if (pit.current()->type() == Relation::FinishStart ||
01390             pit.current()->type() == Relation::StartStart)
01391             return false;
01392     }
01393     return true;
01394 }
01395 
01396 DateTime Task::workStartTime() const {
01397     if (m_currentSchedule == 0)
01398          return DateTime();
01399     if (m_requests)
01400         return m_currentSchedule->workStartTime;
01401     return m_currentSchedule->startTime;
01402 }
01403 
01404 DateTime Task::workEndTime() const {
01405     if (m_currentSchedule == 0)
01406          return DateTime();
01407     return m_currentSchedule->endTime;
01408 }
01409 
01410 DateTime Task::workStartAfter(const DateTime &dt) {
01411     if (m_requests) {
01412         DateTime t = m_requests->availableAfter(dt);
01413         return t.isValid() ? t : dt;
01414     }
01415     return dt;
01416 }
01417 
01418 DateTime Task::workFinishBefore(const DateTime &dt) {
01419     if (m_requests) {
01420         return m_requests->availableBefore(dt);
01421     }
01422     return dt;
01423 }
01424 
01425 Duration Task::positiveFloat() {
01426     if (m_currentSchedule == 0 || 
01427         m_currentSchedule->schedulingError ||
01428         effortMetError()) {
01429         return Duration::zeroDuration;
01430     }
01431     Duration f;
01432     if (type() == Node::Type_Milestone) {
01433         if (m_currentSchedule->startTime < m_currentSchedule->latestFinish) {
01434             f = m_currentSchedule->latestFinish - m_currentSchedule->startTime;
01435         }
01436     } else if (m_effort->type() == Effort::Type_FixedDuration) {
01437         if (m_currentSchedule->endTime.isValid()) {
01438             if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
01439                 f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
01440             }
01441         }
01442     } else {
01443         if (m_currentSchedule->workEndTime.isValid())
01444             if (m_currentSchedule->workEndTime < m_currentSchedule->latestFinish) {
01445             f = m_currentSchedule->latestFinish - m_currentSchedule->workEndTime;
01446         } else if (m_currentSchedule->endTime.isValid()) {
01447             if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
01448                 f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
01449             }
01450         }
01451     }
01452     //kdDebug()<<k_funcinfo<<f.toString()<<endl;
01453     return f;
01454 }
01455 
01456 bool Task::isCritical() {
01457     Schedule *cs = m_currentSchedule;
01458     if (cs == 0) {
01459         return false;
01460     }
01461     return cs->earliestStart >= cs->startTime && cs->latestFinish <= cs->endTime;
01462 }
01463 
01464 bool Task::calcCriticalPath(bool fromEnd) {
01465     if (m_currentSchedule == 0)
01466         return false;
01467     //kdDebug()<<k_funcinfo<<m_name<<" fromEnd="<<fromEnd<<" cp="<<m_currentSchedule->inCriticalPath<<endl;
01468     if (m_currentSchedule->inCriticalPath) {
01469         return true; // path allready calculated
01470     }
01471     if (!isCritical()) {
01472         return false;
01473     }
01474     if (fromEnd) {
01475         if (isEndNode()) {
01476             m_currentSchedule->inCriticalPath = true;
01477             //kdDebug()<<k_funcinfo<<m_name<<" end node"<<endl;
01478             return true;
01479         }
01480         QPtrListIterator<Relation> it(m_childProxyRelations);
01481         for (; it.current(); ++it) {
01482             if (it.current()->child()->calcCriticalPath(fromEnd)) {
01483                 m_currentSchedule->inCriticalPath = true;
01484             }
01485         }
01486         QPtrListIterator<Relation> pit(m_dependChildNodes);
01487         for (; pit.current(); ++pit) {
01488             if (pit.current()->child()->calcCriticalPath(fromEnd)) {
01489                 m_currentSchedule->inCriticalPath = true;
01490             }
01491         }
01492     } else {
01493         if (isStartNode()) {
01494             m_currentSchedule->inCriticalPath = true;
01495             //kdDebug()<<k_funcinfo<<m_name<<" start node"<<endl;
01496             return true;
01497         }
01498         QPtrListIterator<Relation> it(m_parentProxyRelations);
01499         for (; it.current(); ++it) {
01500             if (it.current()->parent()->calcCriticalPath(fromEnd)) {
01501                 m_currentSchedule->inCriticalPath = true;
01502             }
01503         }
01504         QPtrListIterator<Relation> pit(m_dependParentNodes);
01505         for (; pit.current(); ++pit) {
01506             if (pit.current()->parent()->calcCriticalPath(fromEnd)) {
01507                 m_currentSchedule->inCriticalPath = true;
01508             }
01509         }
01510     }
01511     //kdDebug()<<k_funcinfo<<m_name<<" return cp="<<m_currentSchedule->inCriticalPath<<endl;
01512     return m_currentSchedule->inCriticalPath;
01513 }
01514 
01515 void Task::setCurrentSchedule(long id) {
01516     setCurrentSchedulePtr(findSchedule(id));
01517     Node::setCurrentSchedule(id);
01518 }
01519 
01520 bool Task::effortMetError() const {
01521     if (m_currentSchedule->notScheduled) {
01522         return false;
01523     }
01524     return m_currentSchedule->plannedEffort() < effort()->effort(static_cast<Effort::Use>(static_cast<int>(m_currentSchedule->type())));
01525 }
01526 
01527 #ifndef NDEBUG
01528 void Task::printDebug(bool children, QCString indent) {
01529     kdDebug()<<indent<<"+ Task node: "<<name()<<" type="<<type()<<endl;
01530     indent += "!  ";
01531     kdDebug()<<indent<<"Requested resources (total): "<<units()<<"%"<<endl;
01532     kdDebug()<<indent<<"Requested resources (work): "<<workUnits()<<"%"<<endl;
01533     if (m_requests)
01534         m_requests->printDebug(indent);
01535     
01536     Node::printDebug(children, indent);
01537 
01538 }
01539 
01540 #endif
01541 
01542 }  //KPlato namespace
KDE Home | KDE Accessibility Home | Description of Access Keys