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                     addChildNode(child);
00212                 } else {
00213                     // TODO: Complain about this
00214                     delete child;
00215                 }
00216             } else if (e.tagName() == "task") {
00217                 // Load the task
00218                 Task *child = new Task(this);
00219                 if (child->load(e, project)) {
00220                     addChildNode(child);
00221                 } else {
00222                     // TODO: Complain about this
00223                     delete child;
00224                 }
00225             } else if (e.tagName() == "resource") {
00226                 // TODO: Load the resource (projects don't have resources yet)
00227             } else if (e.tagName() == "effort") {
00228                 //  Load the effort
00229                 m_effort->load(e);
00230             } else if (e.tagName() == "resourcegroup-request") {
00231                 // Load the resource request
00232                 ResourceGroupRequest *r = new ResourceGroupRequest();
00233                 if (r->load(e, project)) {
00234                     addRequest(r);
00235                 } else {
00236                     kdError()<<k_funcinfo<<"Failed to load resource request"<<endl;
00237                     delete r;
00238                 }
00239             } else if (e.tagName() == "progress") {
00240                 m_progress.started = (bool)e.attribute("started", "0").toInt();
00241                 m_progress.finished = (bool)e.attribute("finished", "0").toInt();
00242                 
00243                 s = e.attribute("startTime");
00244                 if (s != "")
00245                     m_progress.startTime = DateTime::fromString(s);
00246                 s = e.attribute("finishTime");
00247                 if (s != "")
00248                     m_progress.finishTime = DateTime::fromString(s);
00249                 m_progress.percentFinished = e.attribute("percent-finished", "0").toInt();
00250                 m_progress.remainingEffort = Duration::fromString(e.attribute("remaining-effort"));
00251                 m_progress.totalPerformed = Duration::fromString(e.attribute("performed-effort"));
00252             } else if (e.tagName() == "schedules") {
00253                 QDomNodeList lst = e.childNodes();
00254                 for (unsigned int i=0; i<lst.count(); ++i) {
00255                     if (lst.item(i).isElement()) {
00256                         QDomElement el = lst.item(i).toElement();
00257                         if (el.tagName() == "schedule") {
00258                             NodeSchedule *sch = new NodeSchedule();
00259                             if (sch->loadXML(el)) {
00260                                 sch->setNode(this);
00261                                 addSchedule(sch);
00262                             } else {
00263                                 kdError()<<k_funcinfo<<"Failed to load schedule"<<endl;
00264                                 delete sch;
00265                             }
00266                         }
00267                     }
00268                 }
00269             }
00270         }
00271     }
00272     //kdDebug()<<k_funcinfo<<m_name<<" loaded"<<endl;
00273     return true;
00274 }
00275 
00276 
00277 void Task::save(QDomElement &element)  const {
00278     QDomElement me = element.ownerDocument().createElement("task");
00279     element.appendChild(me);
00280 
00281     //TODO: Handle different types of tasks, milestone, summary...
00282     me.setAttribute("id", m_id);
00283     me.setAttribute("name", m_name);
00284     me.setAttribute("leader", m_leader);
00285     me.setAttribute("description", m_description);
00286 
00287     me.setAttribute("scheduling",constraintToString());
00288     me.setAttribute("constraint-starttime",m_constraintStartTime.toString(Qt::ISODate));
00289     me.setAttribute("constraint-endtime",m_constraintEndTime.toString(Qt::ISODate));    
00290 
00291     me.setAttribute("startup-cost", m_startupCost);
00292     me.setAttribute("shutdown-cost", m_shutdownCost);
00293     
00294     me.setAttribute("wbs", m_wbs);
00295     
00296     m_effort->save(me);
00297 
00298     QDomElement el = me.ownerDocument().createElement("progress");
00299     me.appendChild(el);
00300     el.setAttribute("started", m_progress.started);
00301     el.setAttribute("finished", m_progress.finished);
00302     el.setAttribute("startTime", m_progress.startTime.toString(Qt::ISODate));
00303     el.setAttribute("finishTime", m_progress.finishTime.toString(Qt::ISODate));
00304     el.setAttribute("percent-finished", m_progress.percentFinished);
00305     el.setAttribute("remaining-effort", m_progress.remainingEffort.toString());
00306     el.setAttribute("performed-effort", m_progress.totalPerformed.toString());
00307     
00308     if (!m_schedules.isEmpty()) {
00309         QDomElement schs = me.ownerDocument().createElement("schedules");
00310         me.appendChild(schs);
00311         QIntDictIterator<Schedule> it = m_schedules;
00312         for (; it.current(); ++it) {
00313             if (!it.current()->isDeleted()) {
00314                 it.current()->saveXML(schs);
00315             }
00316         }
00317     }
00318     if (m_requests) {
00319         m_requests->save(me);
00320     }
00321     for (int i=0; i<numChildren(); i++) {
00322         getChildNode(i)->save(me);
00323     }
00324 }
00325 
00326 void Task::saveAppointments(QDomElement &element, long id) const {
00327     //kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
00328     Schedule *sch = findSchedule(id);
00329     if (sch) {
00330         sch->saveAppointments(element);
00331     }
00332     QPtrListIterator<Node> it(m_nodes);
00333     for (; it.current(); ++it ) {
00334         it.current()->saveAppointments(element, id);
00335     }
00336 }
00337 
00338 EffortCostMap Task::plannedEffortCostPrDay(const QDate &start, const QDate &end) const {
00339     //kdDebug()<<k_funcinfo<<m_name<<endl;
00340     if (m_currentSchedule) {
00341         return m_currentSchedule->plannedEffortCostPrDay(start, end);
00342     }
00343     return EffortCostMap();
00344 }
00345 
00346 // Returns the total planned effort for this task (or subtasks) 
00347 Duration Task::plannedEffort() {
00348    //kdDebug()<<k_funcinfo<<endl;
00349     Duration eff;
00350     if (type() == Node::Type_Summarytask) {
00351         QPtrListIterator<Node> it(childNodeIterator());
00352         for (; it.current(); ++it) {
00353             eff += it.current()->plannedEffort();
00354         }
00355     } else if (m_currentSchedule) {
00356         eff = m_currentSchedule->plannedEffort();
00357     }
00358     return eff;
00359 }
00360 
00361 // Returns the total planned effort for this task (or subtasks) on date
00362 Duration Task::plannedEffort(const QDate &date) {
00363    //kdDebug()<<k_funcinfo<<endl;
00364     Duration eff;
00365     if (type() == Node::Type_Summarytask) {
00366         QPtrListIterator<Node> it(childNodeIterator());
00367         for (; it.current(); ++it) {
00368             eff += it.current()->plannedEffort(date);
00369         }
00370     } else if (m_currentSchedule) {
00371         eff = m_currentSchedule->plannedEffort(date);
00372     }
00373     return eff;
00374 }
00375 
00376 // Returns the total planned effort for this task (or subtasks) upto and including date
00377 Duration Task::plannedEffortTo(const QDate &date) {
00378     //kdDebug()<<k_funcinfo<<endl;
00379     Duration eff;
00380     if (type() == Node::Type_Summarytask) {
00381         QPtrListIterator<Node> it(childNodeIterator());
00382         for (; it.current(); ++it) {
00383             eff += it.current()->plannedEffortTo(date);
00384         }
00385     } else if (m_currentSchedule) {
00386         eff = m_currentSchedule->plannedEffortTo(date);
00387     }
00388     return eff;
00389 }
00390 
00391 // Returns the total planned effort for this task (or subtasks) 
00392 Duration Task::actualEffort() {
00393    //kdDebug()<<k_funcinfo<<endl;
00394     Duration eff;
00395     if (type() == Node::Type_Summarytask) {
00396         QPtrListIterator<Node> it(childNodeIterator());
00397         for (; it.current(); ++it) {
00398             eff += it.current()->actualEffort();
00399         }
00400     } else {
00401         eff = m_progress.totalPerformed;
00402     }
00403     /* If we want to register pr resource...
00404     } else if (m_currentSchedule) {
00405         eff = m_currentSchedule->actualEffort();
00406     }*/
00407     return eff;
00408 }
00409 
00410 // Returns the total planned effort for this task (or subtasks) on date
00411 Duration Task::actualEffort(const QDate &date) {
00412    //kdDebug()<<k_funcinfo<<endl;
00413     Duration eff;
00414     if (type() == Node::Type_Summarytask) {
00415         QPtrListIterator<Node> it(childNodeIterator());
00416         for (; it.current(); ++it) {
00417             eff += it.current()->actualEffort(date);
00418         }
00419     } else if (m_currentSchedule) {
00420         eff = m_currentSchedule->actualEffort(date);
00421     }
00422     return eff;
00423 }
00424 
00425 // Returns the total planned effort for this task (or subtasks) on date
00426 Duration Task::actualEffortTo(const QDate &date) {
00427    //kdDebug()<<k_funcinfo<<endl;
00428     Duration eff;
00429     if (type() == Node::Type_Summarytask) {
00430         QPtrListIterator<Node> it(childNodeIterator());
00431         for (; it.current(); ++it) {
00432             eff += it.current()->actualEffortTo(date);
00433         }
00434     } else if (m_currentSchedule) {
00435         eff = m_currentSchedule->actualEffortTo(date);
00436     }
00437     return eff;
00438 }
00439 
00440 double Task::plannedCost() {
00441     //kdDebug()<<k_funcinfo<<endl;
00442     double c = 0;
00443     if (type() == Node::Type_Summarytask) {
00444         QPtrListIterator<Node> it(childNodeIterator());
00445         for (; it.current(); ++it) {
00446             c += it.current()->plannedCost();
00447         }
00448     } else if (m_currentSchedule) {
00449         c = m_currentSchedule->plannedCost();
00450     }
00451     return c;
00452 }
00453 
00454 double Task::plannedCost(const QDate &date) {
00455     //kdDebug()<<k_funcinfo<<endl;
00456     double c = 0;
00457     if (type() == Node::Type_Summarytask) {
00458         QPtrListIterator<Node> it(childNodeIterator());
00459         for (; it.current(); ++it) {
00460             c += it.current()->plannedCost(date);
00461         }
00462     } else if (m_currentSchedule) {
00463         c = m_currentSchedule->plannedCost(date);
00464     }
00465     return c;
00466 }
00467 
00468 double Task::plannedCostTo(const QDate &date) {
00469     //kdDebug()<<k_funcinfo<<endl;
00470     double c = 0;
00471     if (type() == Node::Type_Summarytask) {
00472         QPtrListIterator<Node> it(childNodeIterator());
00473         for (; it.current(); ++it) {
00474             c += it.current()->plannedCostTo(date);
00475         }
00476     } else if (m_currentSchedule) {
00477         c = m_currentSchedule->plannedCostTo(date);
00478     }
00479     return c;
00480 }
00481 
00482 double Task::actualCost() {
00483     //kdDebug()<<k_funcinfo<<endl;
00484     double c = 0;
00485     if (type() == Node::Type_Summarytask) {
00486         QPtrListIterator<Node> it(childNodeIterator());
00487         for (; it.current(); ++it) {
00488             c += it.current()->actualCost();
00489         }
00490     } else if (m_currentSchedule) {
00491         c = m_currentSchedule->actualCost();
00492     }
00493     return c;
00494 }
00495 
00496 double Task::actualCost(const QDate &date) {
00497     //kdDebug()<<k_funcinfo<<endl;
00498     double c = 0;
00499     if (type() == Node::Type_Summarytask) {
00500         QPtrListIterator<Node> it(childNodeIterator());
00501         for (; it.current(); ++it) {
00502             c += it.current()->actualCost(date);
00503         }
00504     } else if (m_currentSchedule) {
00505         c = m_currentSchedule->actualCost(date);
00506     }
00507     return c;
00508 }
00509 
00510 double Task::actualCostTo(const QDate &date) {
00511     //kdDebug()<<k_funcinfo<<endl;
00512     double c = 0;
00513     if (type() == Node::Type_Summarytask) {
00514         QPtrListIterator<Node> it(childNodeIterator());
00515         for (; it.current(); ++it) {
00516             c += it.current()->actualCostTo(date);
00517         }
00518     } else if (m_currentSchedule) {
00519         c = m_currentSchedule->actualCostTo(date);
00520     }
00521     return c;
00522 }
00523 
00524 //FIXME Handle summarytasks
00525 double Task::effortPerformanceIndex(const QDate &date, bool *error) {
00526     double res = 0.0;
00527     Duration ae = actualEffortTo(date);
00528     
00529     bool e = (ae == Duration::zeroDuration || m_progress.percentFinished == 0);
00530     if (error) {
00531         *error = e;
00532     }
00533     if (!e) {
00534         res = (plannedEffortTo(date).toDouble() * ((double)m_progress.percentFinished/100.0) / ae.toDouble());
00535     }
00536     return res;
00537 }
00538 
00539 //FIXME Handle summarytasks
00540 double Task::costPerformanceIndex(const QDate &date, bool *error) {
00541     double res = 0.0;
00542     Duration ac = Q_INT64(actualCostTo(date));
00543     
00544     bool e = (ac == Duration::zeroDuration || m_progress.percentFinished == 0);
00545     if (error) {
00546         *error = e;
00547     }
00548     if (!e) {
00549         res = (plannedCostTo(date) * m_progress.percentFinished)/(100 * actualCostTo(date));
00550     }
00551     return res;
00552 }
00553 
00554 void Task::initiateCalculation(Schedule &sch) {
00555     //kdDebug()<<k_funcinfo<<m_name<<" schedule: "<<(sch?sch->name():"None")<<" id="<<(sch?sch->id():-1)<<endl;
00556     m_visitedForward = false;
00557     m_visitedBackward = false;
00558     m_currentSchedule = createSchedule(&sch);
00559     m_currentSchedule->initiateCalculation();
00560     clearProxyRelations();
00561     Node::initiateCalculation(sch);
00562 }
00563 
00564 
00565 void Task::initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks/*, QPtrList<Node> &milestones*/) {
00566     //kdDebug()<<k_funcinfo<<m_name<<endl;
00567     if (type() == Node::Type_Summarytask) {
00568         summarytasks.append(this);
00569         // propagate my relations to my children and dependent nodes
00570         
00571         QPtrListIterator<Node> nodes = m_nodes;
00572         for (; nodes.current(); ++nodes) {
00573             if (!dependParentNodes().isEmpty()) 
00574                 nodes.current()->addParentProxyRelations(dependParentNodes());
00575             if (!dependChildNodes().isEmpty()) 
00576                 nodes.current()->addChildProxyRelations(dependChildNodes());
00577             nodes.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
00578         }        
00579     } else {
00580         if (isEndNode()) {
00581             endnodes.append(this);
00582             //kdDebug()<<k_funcinfo<<"endnodes append: "<<m_name<<endl;
00583         }
00584         if (isStartNode()) {
00585             startnodes.append(this);
00586             //kdDebug()<<k_funcinfo<<"startnodes append: "<<m_name<<endl;
00587         }
00588     }
00589 }
00590 
00591 DateTime Task::calculatePredeccessors(const QPtrList<Relation> &list, int use) {
00592     DateTime time;
00593     QPtrListIterator<Relation> it = list;
00594     for (; it.current(); ++it) {
00595         if (it.current()->parent()->type() == Type_Summarytask) {
00596             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
00597             continue; // skip summarytasks
00598         }
00599         DateTime t = it.current()->parent()->calculateForward(use); // early finish
00600         switch (it.current()->type()) {
00601             case Relation::StartStart:
00602                 // I can't start earlier than my predesseccor
00603                 t = it.current()->parent()->getEarliestStart() + it.current()->lag();
00604                 break;
00605             case Relation::FinishFinish:
00606                 // I can't finish earlier than my predeccessor, so
00607                 // I can't start earlier than it's (earlyfinish+lag)- my duration
00608                 t += it.current()->lag();
00609                 t -= duration(t, use, true);
00610                 break;
00611             default:
00612                 t += it.current()->lag();
00613                 break;
00614         }
00615         if (!time.isValid() || t > time)
00616             time = t;
00617     }
00618     //kdDebug()<<time.toString()<<"                  "<<m_name<<" calculatePredeccessors() ("<<list.count()<<")"<<endl;
00619     return time;
00620 }
00621 DateTime Task::calculateForward(int use) {
00622     //kdDebug()<<k_funcinfo<<m_name<<<<endl;
00623     if (m_currentSchedule == 0) {
00624         return DateTime();
00625     }
00626     Schedule *cs = m_currentSchedule;
00627     if (m_visitedForward) {
00628     //kdDebug()<<earliestStart.toString()<<" + "<<m_durationBackward.toString()<<" "<<m_name<<" calculateForward() (visited)"<<endl;
00629         return cs->earliestStart + m_durationForward;
00630     }
00631     // First, calculate all predecessors
00632     if (!dependParentNodes().isEmpty()) {
00633         DateTime time = calculatePredeccessors(dependParentNodes(), use);
00634         if (time.isValid() && time > cs->earliestStart) {
00635             cs->earliestStart = time;
00636         }
00637     }
00638     if (!m_parentProxyRelations.isEmpty()) {
00639         DateTime time = calculatePredeccessors(m_parentProxyRelations, use);
00640         if (time.isValid() && time > cs->earliestStart) {
00641             cs->earliestStart = time;
00642         }
00643     }
00644     if (type() == Node::Type_Task) {
00645         m_durationForward = m_effort->effort(use);
00646         switch (constraint()) {
00647             case Node::ASAP:
00648             case Node::ALAP:
00649                 if (m_effort->type() == Effort::Type_Effort) {
00650                     DateTime t = workStartAfter(cs->earliestStart);
00651                     if (t.isValid())
00652                          cs->earliestStart = t;
00653                 }
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                 if (m_effort->type() == Effort::Type_Effort) {
00782                     DateTime t = workFinishBefore(cs->latestFinish);
00783                     //kdDebug()<<k_funcinfo<<m_name<<": latestFinish="<<cs->latestFinish<<" t="<<t<<endl;
00784                     if (t.isValid()) {
00785                         cs->latestFinish = t;
00786                     }
00787                 }
00788                 m_durationBackward = duration(cs->latestFinish, use, true);
00789                 break;
00790             case Node::MustStartOn:
00791                 m_durationBackward = duration(m_constraintStartTime, use, false);
00792                 cs->latestFinish = m_constraintStartTime + m_durationBackward;
00793                 break;
00794             case Node::StartNotEarlier:
00795                 m_durationBackward = duration(cs->latestFinish, use, true);
00796                 if (cs->latestFinish - m_durationBackward < m_constraintStartTime) {
00797                     m_durationBackward = duration(m_constraintStartTime, use, false);
00798                     cs->latestFinish = m_constraintStartTime + m_durationBackward;
00799                 }
00800                 break;
00801             case Node::MustFinishOn:
00802                 cs->latestFinish = m_constraintEndTime;
00803                 m_durationBackward = duration(cs->latestFinish, use, true);
00804                 break;
00805             case Node::FinishNotLater:
00806                 if (cs->latestFinish > m_constraintEndTime) {
00807                     cs->latestFinish = m_constraintEndTime;
00808                 }
00809                 m_durationBackward = duration(cs->latestFinish, use, true);
00810                 break;
00811             case Node::FixedInterval: {
00812                 cs->latestFinish = m_constraintEndTime;
00813                 m_durationBackward = m_constraintEndTime - m_constraintStartTime;
00814                 break;
00815             }
00816         }
00817     } else if (type() == Node::Type_Milestone) {
00818         m_durationBackward = Duration::zeroDuration;
00819         switch (constraint()) {
00820             case Node::MustFinishOn:
00821                 cs->latestFinish = m_constraintEndTime;
00822                 break;
00823             case Node::FinishNotLater:
00824                 if (cs->latestFinish > m_constraintEndTime) {
00825                     cs->latestFinish = m_constraintEndTime;
00826                 }
00827                 break;
00828             case Node::MustStartOn:
00829                 cs->latestFinish = m_constraintStartTime;
00830                 break;
00831             case Node::StartNotEarlier:
00832                 if (cs->latestFinish < m_constraintStartTime) {
00833                     cs->latestFinish = m_constraintStartTime;
00834                 }
00835                 break;
00836             case Node::FixedInterval:
00837                 cs->latestFinish = m_constraintEndTime;
00838                 break;
00839             default:
00840                 break;
00841         }
00842         //kdDebug()<<k_funcinfo<<m_name<<" "<<cs->latestFinish<<endl;
00843     } else if (type() == Node::Type_Summarytask) {
00844         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
00845     } else { // ???
00846         m_durationBackward = Duration::zeroDuration;
00847     }
00848     //kdDebug()<<"Latestart: "<<cs->latestFinish<<"-"<<m_durationBackward.toString()<<"="<<(cs->latestFinish-m_durationBackward).toString()<<" "<<m_name<<" calculateBackward()"<<endl;
00849     m_visitedBackward = true;
00850     return cs->latestFinish - m_durationBackward;
00851 }
00852 
00853 DateTime Task::schedulePredeccessors(const QPtrList<Relation> &list, int use) {
00854     DateTime time;
00855     QPtrListIterator<Relation> it = list;
00856     for (; it.current(); ++it) {
00857         if (it.current()->parent()->type() == Type_Summarytask) {
00858             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
00859             continue; // skip summarytasks
00860         }
00861         // schedule the predecessors
00862         DateTime earliest = it.current()->parent()->getEarliestStart();
00863         DateTime t = it.current()->parent()->scheduleForward(earliest, use);
00864         switch (it.current()->type()) {
00865             case Relation::StartStart:
00866                 // I can't start before my predesseccor
00867                 t = it.current()->parent()->startTime() + it.current()->lag();
00868                 break;
00869             case Relation::FinishFinish:
00870                 // I can't end before my predecessor, so
00871                 // I can't start before it's endtime - my duration
00872                 t -= duration(t + it.current()->lag(), use, true);
00873                 break;
00874             default:
00875                 t += it.current()->lag();
00876                 break;
00877         }
00878         if (!time.isValid() || t > time)
00879             time = t;
00880     }
00881     //kdDebug()<<time.toString()<<" "<<m_name<<" schedulePredeccessors()"<<endl;
00882     return time;
00883 }
00884 
00885 DateTime Task::scheduleForward(const DateTime &earliest, int use) {
00886     //kdDebug()<<k_funcinfo<<m_name<<" earliest="<<earliest<<endl;
00887     if (m_currentSchedule == 0) {
00888         return DateTime();
00889     }
00890     Schedule *cs = m_currentSchedule;
00891     if (m_visitedForward) {
00892         return cs->endTime;
00893     }
00894     cs->notScheduled = false;
00895     cs->startTime = earliest > cs->earliestStart ? earliest : cs->earliestStart;
00896     // First, calculate all my own predecessors
00897     DateTime time = schedulePredeccessors(dependParentNodes(), 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     // Then my parents
00903     time = schedulePredeccessors(m_parentProxyRelations, use);
00904     if (time.isValid() && time > cs->startTime) {
00905         cs->startTime = time;
00906         //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
00907     }
00908     //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
00909     if(type() == Node::Type_Task) {
00910         cs->duration = m_effort->effort(use);
00911         switch (m_constraint) {
00912         case Node::ASAP:
00913             // cs->startTime calculated above
00914             //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
00915             if (m_effort->type() == Effort::Type_Effort) {
00916                 DateTime t = workStartAfter(cs->startTime);
00917                 if (t.isValid())
00918                     cs->startTime = t;
00919             }
00920             cs->duration = duration(cs->startTime, use, false);
00921             cs->endTime = cs->startTime + cs->duration;
00922             //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
00923             break;
00924         case Node::ALAP:
00925             // cd->startTime calculated above
00926             cs->duration = duration(cs->latestFinish, use, true);
00927             cs->endTime = cs->latestFinish;
00928             cs->startTime = cs->endTime - cs->duration;
00929             //kdDebug()<<k_funcinfo<<m_name<<" endTime="<<cs->endTime<<" latest="<<cs->latestFinish<<endl;
00930             break;
00931         case Node::StartNotEarlier:
00932             // cs->startTime calculated above
00933             //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cd->startTime.toString()<<endl;            
00934             if (cs->startTime < m_constraintStartTime) {
00935                 cs->startTime = m_constraintStartTime;
00936             }
00937             if (m_effort->type() == Effort::Type_Effort) {
00938                 DateTime t = workStartAfter(cs->startTime);
00939                 if (t.isValid())
00940                     cs->startTime = t;
00941             }
00942             cs->duration = duration(cs->startTime, use, false);
00943             cs->endTime = cs->startTime + cs->duration;
00944             if (cs->endTime > cs->latestFinish) {
00945                 cs->schedulingError = true;
00946             }
00947             break;
00948         case Node::FinishNotLater:
00949             // cs->startTime calculated above
00950             //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
00951             cs->duration = duration(cs->startTime, use, false);
00952             cs->endTime = cs->startTime + cs->duration;
00953             if (cs->endTime > m_constraintEndTime) {
00954                 cs->schedulingError = true;
00955                 cs->endTime = m_constraintEndTime;
00956                 cs->duration = duration(cs->endTime, use, true);
00957                 cs->startTime = cs->endTime - cs->duration;
00958             }
00959             break;
00960         case Node::MustStartOn:
00961             // cs->startTime calculated above
00962             //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
00963             if (m_constraintStartTime < cs->startTime ||
00964                 m_constraintStartTime > cs->latestFinish - m_durationBackward) {
00965                 cs->schedulingError = true;
00966             }
00967             cs->startTime = m_constraintStartTime;
00968             cs->duration = duration(cs->startTime, use, false);
00969             cs->endTime = cs->startTime + cs->duration;
00970             break;
00971         case Node::MustFinishOn:
00972             // cs->startTime calculated above
00973             //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
00974             if (m_constraintEndTime > cs->latestFinish ||
00975                 m_constraintEndTime < cs->earliestStart + m_durationForward) {
00976                 cs->schedulingError = true;
00977             }
00978             cs->endTime = m_constraintEndTime;
00979             cs->duration = duration(cs->endTime, use, true);
00980             cs->startTime = cs->endTime - cs->duration;
00981             break;
00982         case Node::FixedInterval: {
00983             // cs->startTime calculated above
00984             //kdDebug()<<"FixedInterval="<<m_constraintStartTime<<" "<<cs->startTime<<endl;
00985             if (cs->startTime < cs->earliestStart) {
00986                 cs->schedulingError = true;
00987             }
00988             cs->startTime = m_constraintStartTime;
00989             cs->endTime = m_constraintEndTime;
00990             cs->duration = cs->endTime - cs->startTime;
00991             cs->workStartTime = m_constraintStartTime;
00992             cs->workEndTime = m_constraintEndTime;
00993             //kdDebug()<<"FixedInterval="<<cs->startTime<<", "<<cs->endTime<<endl;
00994             break;
00995         }
00996         default:
00997             break;
00998         }
00999         if (m_requests) {
01000             m_requests->reserve(cs->startTime, cs->duration);
01001         }
01002     } else if (type() == Node::Type_Milestone) {
01003         switch (m_constraint) {
01004         case Node::ASAP: {
01005             cs->endTime = cs->startTime;
01006             break;
01007         }
01008         case Node::ALAP: {
01009             cs->startTime = cs->latestFinish;
01010             cs->endTime = cs->latestFinish;
01011             break;
01012         }
01013         case Node::MustStartOn:
01014         case Node::FixedInterval:
01015             //kdDebug()<<"Forw, MustStartOn: "<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
01016             if (m_constraintStartTime < cs->startTime ||
01017                 m_constraintStartTime > cs->latestFinish) {
01018                 cs->schedulingError = true;
01019             }
01020             cs->startTime = m_constraintStartTime;
01021             cs->endTime = m_constraintStartTime;
01022             break;
01023         case Node::MustFinishOn:
01024             if (m_constraintEndTime < cs->startTime ||
01025                 m_constraintEndTime > cs->latestFinish) {
01026                 cs->schedulingError = true;
01027             }
01028             cs->startTime = m_constraintEndTime;
01029             cs->endTime = m_constraintEndTime;
01030             break;
01031         case Node::StartNotEarlier:
01032             if (cs->startTime < m_constraintStartTime) {
01033                 cs->schedulingError = true;
01034             }
01035             cs->endTime = cs->startTime;
01036             break;
01037         case Node::FinishNotLater:
01038             if (cs->startTime > m_constraintEndTime) {
01039                 cs->schedulingError = true;
01040             }
01041             cs->endTime = cs->startTime;
01042             break;
01043         default:
01044             break;
01045         }
01046         cs->duration = Duration::zeroDuration;
01047         //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime<<", "<<cs->endTime<<endl;
01048     } else if (type() == Node::Type_Summarytask) {
01049         //shouldn't come here
01050         cs->endTime = cs->startTime;
01051         cs->duration = cs->endTime - cs->startTime;
01052         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
01053     }
01054     //kdDebug()<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<" "<<m_name<<" scheduleForward()"<<endl;
01055     m_visitedForward = true;
01056     return cs->endTime;
01057 }
01058 
01059 DateTime Task::scheduleSuccessors(const QPtrList<Relation> &list, int use) {
01060     DateTime time;
01061     QPtrListIterator<Relation> it = list;
01062     for (; it.current(); ++it) {
01063         if (it.current()->child()->type() == Type_Summarytask) {
01064             //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->child()->name()<<endl;
01065             continue;
01066         }
01067         // get the successors starttime
01068         DateTime latest = it.current()->child()->getLatestFinish();
01069         DateTime t = it.current()->child()->scheduleBackward(latest, use);
01070         switch (it.current()->type()) {
01071             case Relation::StartStart:
01072                 // I can't start before my successor, so
01073                 // I can't finish later than it's starttime + my duration
01074                 t += duration(t - it.current()->lag(), use, false);
01075                 break;
01076             case Relation::FinishFinish:
01077                 t = it.current()->child()->endTime() - it.current()->lag();
01078                 break;
01079             default:
01080                 t -= it.current()->lag();
01081                 break;
01082         }
01083         if (!time.isValid() || t < time)
01084             time = t;
01085    }
01086    return time;
01087 }
01088 DateTime Task::scheduleBackward(const DateTime &latest, int use) {
01089     //kdDebug()<<k_funcinfo<<m_name<<": latest="<<latest<<endl;
01090     if (m_currentSchedule == 0) {
01091         return DateTime();
01092     }
01093     Schedule *cs = m_currentSchedule;
01094     if (m_visitedBackward) {
01095         return cs->startTime;
01096     }
01097     cs->notScheduled = false;
01098     cs->endTime = latest < cs->latestFinish ? latest : cs->latestFinish;
01099     // First, calculate all my own successors
01100     DateTime time = scheduleSuccessors(dependChildNodes(), use);
01101     if (time.isValid() && time < cs->endTime) {
01102         cs->endTime = time;
01103     }
01104     // Then my parents
01105     time = scheduleSuccessors(m_childProxyRelations, use);
01106     if (time.isValid() && time < cs->endTime) {
01107         cs->endTime = time;
01108     }
01109     if (type() == Node::Type_Task) {
01110         cs->duration = m_effort->effort(use);
01111         switch (m_constraint) {
01112         case Node::ASAP: {
01113             // cs->endTime calculated above
01114             //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<"  early="<<cs->earliestStart<<endl;
01115             if (m_effort->type() == Effort::Type_Effort) {
01116                 DateTime t = workFinishBefore(cs->endTime);
01117                 //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" t="<<t<<endl;
01118                 if (t.isValid())
01119                     cs->endTime = t;
01120             }
01121             cs->duration = duration(cs->earliestStart, use, false);
01122             cs->startTime = cs->earliestStart;
01123             DateTime e = cs->startTime + cs->duration;
01124             if (e > cs->endTime) {
01125                 cs->schedulingError = true;
01126             }
01127             cs->endTime = e;
01128             //kdDebug()<<k_funcinfo<<m_name<<": start="<<cs->startTime<<"+"<<cs->duration.toString()<<"="<<e<<" -> end="<<cs->endTime<<endl;
01129             break;
01130         }
01131         case Node::ALAP:
01132             // cs->endTime calculated above
01133             //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<"  late="<<cs->latestFinish<<endl;
01134             if (m_effort->type() == Effort::Type_Effort) {
01135                 DateTime t = workFinishBefore(cs->endTime);
01136                 //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" t="<<t<<endl;
01137                 if (t.isValid())
01138                     cs->endTime = t;
01139             }
01140             cs->duration = duration(cs->endTime, use, true);
01141             cs->startTime = cs->endTime - cs->duration;
01142             //kdDebug()<<k_funcinfo<<m_name<<": lateStart="<<cs->startTime<<endl;
01143             break;
01144         case Node::StartNotEarlier:
01145             // cs->endTime calculated above
01146             //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cs->endTime.toString()<<endl;
01147             if (m_effort->type() == Effort::Type_Effort) {
01148                 DateTime t = workFinishBefore(cs->endTime);
01149                 //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" t="<<t<<endl;
01150                 if (t.isValid())
01151                     cs->endTime = t;
01152             }
01153             cs->duration = duration(cs->endTime, use, true);
01154             cs->startTime = cs->endTime - cs->duration;
01155             if (cs->startTime < m_constraintStartTime) {
01156                 cs->schedulingError = true;
01157                 cs->startTime = m_constraintStartTime;
01158                 cs->duration = duration(cs->startTime, use, false);
01159                 cs->endTime = cs->startTime + cs->duration;
01160             }
01161             break;
01162         case Node::FinishNotLater:
01163             // cs->endTime calculated above
01164             //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->endTime.toString()<<endl;            
01165             if (cs->endTime > m_constraintEndTime) {
01166                 cs->schedulingError = true;
01167                 cs->endTime = m_constraintEndTime;
01168             }
01169             if (m_effort->type() == Effort::Type_Effort) {
01170                 DateTime t = workFinishBefore(cs->endTime);
01171                 //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" t="<<t<<endl;
01172                 if (t.isValid())
01173                     cs->endTime = t;
01174             }
01175             cs->duration = duration(cs->endTime, use, true);
01176             cs->startTime = cs->endTime - cs->duration;
01177             break;
01178         case Node::MustStartOn:
01179             // cs->endTime calculated above
01180             //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
01181             if (m_constraintStartTime < cs->earliestStart ||
01182                 m_constraintStartTime > cs->latestFinish - m_durationBackward) {
01183                 cs->schedulingError = true;
01184             }
01185             cs->startTime = m_constraintStartTime;
01186             cs->duration = duration(cs->startTime, use, false);
01187             cs->endTime = cs->startTime + cs->duration;
01188             break;
01189         case Node::MustFinishOn:
01190             // cs->endTime calculated above
01191             //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
01192             if (m_constraintEndTime > cs->latestFinish ||
01193                 m_constraintEndTime < cs->earliestStart + m_durationForward) {
01194                 cs->schedulingError = true;
01195             }
01196             cs->endTime = m_constraintEndTime;
01197             cs->duration = duration(cs->endTime, use, true);
01198             cs->startTime = cs->endTime - cs->duration;
01199             break;
01200         case Node::FixedInterval: {
01201             // cs->endTime calculated above
01202             //kdDebug()<<k_funcinfo<<"FixedInterval="<<m_constraintEndTime<<" "<<cs->endTime<<endl;
01203             if (m_constraintEndTime > cs->endTime) {
01204                 cs->schedulingError = true;
01205                 //kdDebug()<<k_funcinfo<<"FixedInterval error: "<<m_constraintEndTime<<" >  "<<cs->endTime<<endl;
01206             }
01207             cs->startTime = m_constraintStartTime;
01208             cs->endTime = m_constraintEndTime;
01209             cs->duration = cs->endTime - cs->startTime;
01210             cs->workStartTime = m_constraintStartTime;
01211             cs->workEndTime = m_constraintEndTime;
01212             break;
01213         }
01214         default:
01215             break;
01216         }
01217         if (m_requests) {
01218             m_requests->reserve(cs->startTime, cs->duration);
01219         }
01220     } else if (type() == Node::Type_Milestone) {
01221         switch (m_constraint) {
01222         case Node::ASAP:
01223             cs->startTime = cs->earliestStart;
01224             cs->endTime = cs->earliestStart;
01225             break;
01226         case Node::ALAP:
01227             cs->startTime = cs->latestFinish;
01228             cs->endTime = cs->latestFinish;
01229             break;
01230         case Node::MustStartOn:
01231         case Node::FixedInterval:
01232             if (m_constraintStartTime < cs->earliestStart ||
01233                 m_constraintStartTime > cs->endTime) {
01234                 cs->schedulingError = true;
01235             }
01236             cs->startTime = cs->earliestStart;
01237             cs->endTime = cs->earliestStart;
01238             break;
01239         case Node::MustFinishOn:
01240             if (m_constraintEndTime < cs->earliestStart ||
01241                 m_constraintEndTime > cs->endTime) {
01242                 cs->schedulingError = true;
01243             }
01244             cs->startTime = cs->earliestStart;
01245             cs->endTime = cs->earliestStart;
01246             break;
01247         case Node::StartNotEarlier:
01248             if (m_constraintStartTime > cs->endTime) {
01249                 cs->schedulingError = true;
01250             }
01251             cs->startTime = cs->endTime;
01252             break;
01253         case Node::FinishNotLater:
01254             if (m_constraintEndTime < cs->endTime) {
01255                 cs->schedulingError = true;
01256             }
01257             cs->startTime = cs->endTime;
01258             break;
01259         default:
01260             break;
01261         }
01262         cs->duration = Duration::zeroDuration;
01263     } else if (type() == Node::Type_Summarytask) {
01264         //shouldn't come here
01265         cs->startTime = cs->endTime;
01266         cs->duration = cs->endTime - cs->startTime;
01267         kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
01268     }
01269     //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<endl;
01270     m_visitedBackward = true;
01271     return cs->startTime;
01272 }
01273 
01274 void Task::adjustSummarytask() {
01275     if (m_currentSchedule == 0)
01276         return;
01277     if (type() == Type_Summarytask) {
01278         DateTime start = m_currentSchedule->latestFinish;
01279         DateTime end = m_currentSchedule->earliestStart;
01280         QPtrListIterator<Node> it(m_nodes);
01281         for (; it.current(); ++it) {
01282             it.current()->adjustSummarytask();
01283             if (it.current()->startTime() < start)
01284                 start = it.current()->startTime();
01285             if (it.current()->endTime() > end)
01286                 end = it.current()->endTime();
01287         }
01288         m_currentSchedule->startTime = start;
01289         m_currentSchedule->endTime = end;
01290         m_currentSchedule->duration = end - start;
01291         m_currentSchedule->notScheduled = false;
01292         //kdDebug()<<k_funcinfo<<cs->name<<": "<<m_currentSchedule->startTime.toString()<<" : "<<m_currentSchedule->endTime.toString()<<endl;
01293     }
01294 }
01295 
01296 Duration Task::calcDuration(const DateTime &time, const Duration &effort, bool backward) {
01297     //kdDebug()<<"--------> calcDuration "<<(backward?"(B) ":"(F) ")<<m_name<<" time="<<time<<" effort="<<effort.toString(Duration::Format_Day)<<endl;
01298     
01299     // Allready checked: m_effort, m_currentSchedule and time.
01300     Duration dur = effort; // use effort as default duration
01301     if (m_effort->type() == Effort::Type_Effort) {
01302         if (m_requests == 0 || m_requests->isEmpty()) {
01303             m_currentSchedule->resourceError = true;
01304             return effort;
01305         }
01306         dur = m_requests->duration(time, effort, backward);
01307         if (dur == Duration::zeroDuration) {
01308             kdWarning()<<k_funcinfo<<"zero duration: Resource not available"<<endl;
01309             m_currentSchedule->resourceNotAvailable = true;
01310             dur = effort; //???
01311         }
01312         return dur;
01313     }
01314     if (m_effort->type() == Effort::Type_FixedDuration) {
01315         //TODO: Different types of fixed duration
01316         return dur; //
01317     }
01318     kdError()<<k_funcinfo<<"Unsupported effort type: "<<m_effort->type()<<endl;
01319     return dur;
01320 }
01321 
01322 void Task::clearProxyRelations() {
01323     m_parentProxyRelations.clear();
01324     m_childProxyRelations.clear();
01325 }
01326 
01327 void Task::addParentProxyRelations(QPtrList<Relation> &list) {
01328     //kdDebug()<<k_funcinfo<<m_name<<endl;
01329     if (type() == Type_Summarytask) {
01330         // propagate to my children
01331         //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
01332         QPtrListIterator<Node> nodes = m_nodes;
01333         for (; nodes.current(); ++nodes) {
01334             nodes.current()->addParentProxyRelations(list);
01335             nodes.current()->addParentProxyRelations(dependParentNodes());
01336         }        
01337     } else {
01338         // add 'this' as child relation to the relations parent
01339         //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
01340         QPtrListIterator<Relation> it = list;
01341         for (; it.current(); ++it) {
01342             it.current()->parent()->addChildProxyRelation(this, it.current());
01343             // add a parent relation to myself
01344             addParentProxyRelation(it.current()->parent(), it.current());
01345         }
01346     }
01347 }
01348 
01349 void Task::addChildProxyRelations(QPtrList<Relation> &list) {
01350     //kdDebug()<<k_funcinfo<<m_name<<endl;
01351     if (type() == Type_Summarytask) {
01352         // propagate to my children
01353         //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
01354         QPtrListIterator<Node> nodes = m_nodes;
01355         for (; nodes.current(); ++nodes) {
01356             nodes.current()->addChildProxyRelations(list);
01357             nodes.current()->addChildProxyRelations(dependChildNodes());
01358         }        
01359     } else {
01360         // add 'this' as parent relation to the relations child
01361         //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
01362         QPtrListIterator<Relation> it = list;
01363         for (; it.current(); ++it) {
01364             it.current()->child()->addParentProxyRelation(this, it.current());
01365             // add a child relation to myself
01366             addChildProxyRelation(it.current()->child(), it.current());
01367         }
01368     }
01369 }
01370 
01371 void Task::addParentProxyRelation(Node *node, const Relation *rel) {
01372     if (node->type() != Type_Summarytask) {
01373         if (type() == Type_Summarytask) {
01374             //kdDebug()<<"Add parent proxy from my children "<<m_name<<" to "<<node->name()<<endl;
01375             QPtrListIterator<Node> nodes = m_nodes;
01376             for (; nodes.current(); ++nodes) {
01377                 nodes.current()->addParentProxyRelation(node, rel);
01378             }
01379         } else {
01380             //kdDebug()<<"Add parent proxy from "<<node->name()<<" to (me) "<<m_name<<endl;
01381             m_parentProxyRelations.append(new ProxyRelation(node, this, rel->type(), rel->lag()));
01382         }
01383     }
01384 }
01385 
01386 void Task::addChildProxyRelation(Node *node, const Relation *rel) {
01387     if (node->type() != Type_Summarytask) {
01388         if (type() == Type_Summarytask) {
01389             //kdDebug()<<"Add child proxy from my children "<<m_name<<" to "<<node->name()<<endl;
01390             QPtrListIterator<Node> nodes = m_nodes;
01391             for (; nodes.current(); ++nodes) {
01392                 nodes.current()->addChildProxyRelation(node, rel);
01393             }
01394         } else {
01395             //kdDebug()<<"Add child proxy from (me) "<<m_name<<" to "<<node->name()<<endl;
01396             m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag()));
01397         }
01398     }
01399 }
01400 
01401 bool Task::isEndNode() const {
01402     QPtrListIterator<Relation> it = m_dependChildNodes;
01403     for (; it.current(); ++it) {
01404         if (it.current()->type() == Relation::FinishStart)
01405             return false;
01406     }
01407     QPtrListIterator<Relation> pit = m_childProxyRelations;
01408     for (; pit.current(); ++pit) {
01409         if (pit.current()->type() == Relation::FinishStart)
01410             return false;
01411     }
01412     return true;
01413 }
01414 bool Task::isStartNode() const {
01415     QPtrListIterator<Relation> it = m_dependParentNodes;
01416     for (; it.current(); ++it) {
01417         if (it.current()->type() == Relation::FinishStart ||
01418             it.current()->type() == Relation::StartStart)
01419             return false;
01420     }
01421     QPtrListIterator<Relation> pit = m_parentProxyRelations;
01422     for (; pit.current(); ++pit) {
01423         if (pit.current()->type() == Relation::FinishStart ||
01424             pit.current()->type() == Relation::StartStart)
01425             return false;
01426     }
01427     return true;
01428 }
01429 
01430 DateTime Task::workStartTime() const {
01431     if (m_currentSchedule == 0)
01432          return DateTime();
01433     if (m_requests)
01434         return m_currentSchedule->workStartTime;
01435     return m_currentSchedule->startTime;
01436 }
01437 
01438 DateTime Task::workEndTime() const {
01439     if (m_currentSchedule == 0)
01440          return DateTime();
01441     return m_currentSchedule->endTime;
01442 }
01443 
01444 DateTime Task::workStartAfter(const DateTime &dt) {
01445     if (m_requests) {
01446         DateTime t = m_requests->availableAfter(dt);
01447         return t.isValid() ? t : dt;
01448     }
01449     return dt;
01450 }
01451 
01452 DateTime Task::workFinishBefore(const DateTime &dt) {
01453     if (m_requests) {
01454         return m_requests->availableBefore(dt);
01455     }
01456     return dt;
01457 }
01458 
01459 Duration Task::positiveFloat() {
01460     if (m_currentSchedule == 0)
01461         return Duration::zeroDuration;
01462     Duration f;
01463     if (type() == Node::Type_Milestone) {
01464         if (m_currentSchedule->startTime < m_currentSchedule->latestFinish) {
01465             f = m_currentSchedule->latestFinish - m_currentSchedule->startTime;
01466         }
01467     } else {
01468         if (m_currentSchedule->workEndTime.isValid())
01469             if (m_currentSchedule->workEndTime < m_currentSchedule->latestFinish) {
01470             f = m_currentSchedule->latestFinish - m_currentSchedule->workEndTime;
01471         } else if (m_currentSchedule->endTime.isValid()) {
01472             if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
01473                 f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
01474             }
01475         }
01476     }
01477     //kdDebug()<<k_funcinfo<<f.toString()<<endl;
01478     return f;
01479 }
01480 
01481 bool Task::isCritical() {
01482     Schedule *cs = m_currentSchedule;
01483     if (cs == 0) {
01484         return false;
01485     }
01486     return cs->earliestStart >= cs->startTime && cs->latestFinish <= cs->endTime;
01487 }
01488 
01489 bool Task::calcCriticalPath(bool fromEnd) {
01490     if (m_currentSchedule == 0)
01491         return false;
01492     //kdDebug()<<k_funcinfo<<m_name<<" fromEnd="<<fromEnd<<" cp="<<m_currentSchedule->inCriticalPath<<endl;
01493     if (m_currentSchedule->inCriticalPath) {
01494         return true; // path allready calculated
01495     }
01496     if (!isCritical()) {
01497         return false;
01498     }
01499     if (fromEnd) {
01500         if (isEndNode()) {
01501             m_currentSchedule->inCriticalPath = true;
01502             //kdDebug()<<k_funcinfo<<m_name<<" end node"<<endl;
01503             return true;
01504         }
01505         QPtrListIterator<Relation> it(m_childProxyRelations);
01506         for (; it.current(); ++it) {
01507             if (it.current()->child()->calcCriticalPath(fromEnd)) {
01508                 m_currentSchedule->inCriticalPath = true;
01509             }
01510         }
01511         QPtrListIterator<Relation> pit(m_dependChildNodes);
01512         for (; pit.current(); ++pit) {
01513             if (pit.current()->child()->calcCriticalPath(fromEnd)) {
01514                 m_currentSchedule->inCriticalPath = true;
01515             }
01516         }
01517     } else {
01518         if (isStartNode()) {
01519             m_currentSchedule->inCriticalPath = true;
01520             //kdDebug()<<k_funcinfo<<m_name<<" start node"<<endl;
01521             return true;
01522         }
01523         QPtrListIterator<Relation> it(m_parentProxyRelations);
01524         for (; it.current(); ++it) {
01525             if (it.current()->parent()->calcCriticalPath(fromEnd)) {
01526                 m_currentSchedule->inCriticalPath = true;
01527             }
01528         }
01529         QPtrListIterator<Relation> pit(m_dependParentNodes);
01530         for (; pit.current(); ++pit) {
01531             if (pit.current()->parent()->calcCriticalPath(fromEnd)) {
01532                 m_currentSchedule->inCriticalPath = true;
01533             }
01534         }
01535     }
01536     //kdDebug()<<k_funcinfo<<m_name<<" return cp="<<m_currentSchedule->inCriticalPath<<endl;
01537     return m_currentSchedule->inCriticalPath;
01538 }
01539 
01540 void Task::setCurrentSchedule(long id) {
01541     setCurrentSchedulePtr(findSchedule(id));
01542     Node::setCurrentSchedule(id);
01543 }
01544 
01545 
01546 #ifndef NDEBUG
01547 void Task::printDebug(bool children, QCString indent) {
01548     kdDebug()<<indent<<"+ Task node: "<<name()<<" type="<<type()<<endl;
01549     indent += "!  ";
01550     kdDebug()<<indent<<"Requested resources (total): "<<units()<<"%"<<endl;
01551     kdDebug()<<indent<<"Requested resources (work): "<<workUnits()<<"%"<<endl;
01552     if (m_requests)
01553         m_requests->printDebug(indent);
01554     
01555     Node::printDebug(children, indent);
01556 
01557 }
01558 
01559 #endif
01560 
01561 }  //KPlato namespace
KDE Home | KDE Accessibility Home | Description of Access Keys