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