kplato

kpttask.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2004 Dag Andersen <danders@get2net.dk>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "kpttask.h"
22#include "kptproject.h"
23#include "kpttaskdialog.h"
24#include "kptduration.h"
25#include "kptrelation.h"
26#include "kptdatetime.h"
27#include "kptcalendar.h"
28#include "kpteffortcostmap.h"
29#include "kptschedule.h"
30
31#include <tqdom.h>
32#include <tqbrush.h>
33#include <kdebug.h>
34
35namespace KPlato
36{
37
38Task::Task(Node *parent) : Node(parent), m_resource() {
39 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
40 m_resource.setAutoDelete(true);
41 Duration d(1, 0, 0);
42 m_effort = new Effort(d);
43 m_effort->setOptimisticRatio(-10);
44 m_effort->setPessimisticRatio(20);
45 m_requests = 0;
46
47 if (m_parent)
48 m_leader = m_parent->leader();
49
50 m_schedules.setAutoDelete(true);
51 m_parentProxyRelations.setAutoDelete(true);
52 m_childProxyRelations.setAutoDelete(true);
53}
54
55Task::Task(Task &task, Node *parent)
56 : Node(task, parent),
57 m_resource() {
58 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
59 m_resource.setAutoDelete(true);
60
61 m_parentProxyRelations.setAutoDelete(true);
62 m_childProxyRelations.setAutoDelete(true);
63 m_requests = 0;
64
65 m_effort = task.effort() ? new Effort(*(task.effort()))
66 : new Effort(); // Avoid crash, (shouldn't be zero)
67}
68
69
70Task::~Task() {
71 delete m_effort;
72}
73
74int Task::type() const {
75 if ( numChildren() > 0) {
76 return Node::Type_Summarytask;
77 }
78 else if ( 0 == effort()->expected().seconds() ) {
79 return Node::Type_Milestone;
80 }
81 else {
82 return Node::Type_Task;
83 }
84}
85
86
87
89 //kdDebug()<<k_funcinfo<<endl;
90 // Duration should already be calculated
91 return m_currentSchedule ? new Duration(m_currentSchedule->duration) : new Duration();
92}
93
95 return 0L;
96}
97
98ResourceGroupRequest *Task::resourceGroupRequest(ResourceGroup *group) const {
99 if (m_requests)
100 return m_requests->find(group);
101 return 0;
102}
103
104void Task::clearResourceRequests() {
105 if (m_requests)
106 m_requests->clear();
107}
108
109void Task::addRequest(ResourceGroup *group, int numResources) {
110 addRequest(new ResourceGroupRequest(group, numResources));
111}
112
113void Task::addRequest(ResourceGroupRequest *request) {
114 if (!m_requests)
115 m_requests = new ResourceRequestCollection(*this);
116 m_requests->addRequest(request);
117}
118
119void Task::takeRequest(ResourceGroupRequest *request) {
120 if (m_requests) {
121 m_requests->takeRequest(request);
122 if (m_requests->isEmpty()) {
123 delete m_requests;
124 m_requests = 0;
125 }
126 }
127}
128
129int Task::units() const {
130 if (!m_requests)
131 return 0;
132 return m_requests->units();
133}
134
135int Task::workUnits() const {
136 if (!m_requests)
137 return 0;
138 return m_requests->workUnits();
139}
140
141void Task::makeAppointments() {
142 if (m_currentSchedule == 0)
143 return;
144 if (type() == Node::Type_Task) {
145 if (m_requests) {
146 //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl;
147 m_requests->makeAppointments(m_currentSchedule);
148 //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl;
149 }
150 } else if (type() == Node::Type_Summarytask) {
151 TQPtrListIterator<Node> nit(m_nodes);
152 for ( ; nit.current(); ++nit ) {
153 nit.current()->makeAppointments();
154 }
155 } else if (type() == Node::Type_Milestone) {
156 //kdDebug()<<k_funcinfo<<"Milestone not implemented"<<endl;
157 // Well, shouldn't have resources anyway...
158 }
159}
160
162 if (m_currentSchedule)
163 m_currentSchedule->calcResourceOverbooked();
164}
165
166// A new constraint means start/end times and duration must be recalculated
167void Task::setConstraint(Node::ConstraintType type) {
168 m_constraint = type;
169}
170
171
172bool Task::load(TQDomElement &element, Project &project) {
173 // Load attributes (TODO: Handle different types of tasks, milestone, summary...)
174 TQString s;
175 bool ok = false;
176 m_id = element.attribute("id");
177
178 m_name = element.attribute("name");
179 m_leader = element.attribute("leader");
180 m_description = element.attribute("description");
181 //kdDebug()<<k_funcinfo<<m_name<<": id="<<m_id<<endl;
182
183 // Allow for both numeric and text
184 TQString constraint = element.attribute("scheduling","0");
185 m_constraint = (Node::ConstraintType)constraint.toInt(&ok);
186 if (!ok)
187 Node::setConstraint(constraint); // hmmm, why do I need Node::?
188
189 s = element.attribute("constraint-starttime");
190 if (s != "")
191 m_constraintStartTime = DateTime::fromString(s);
192 s = element.attribute("constraint-endtime");
193 if ( s != "")
194 m_constraintEndTime = DateTime::fromString(s);
195
196 m_startupCost = element.attribute("startup-cost", "0.0").toDouble();
197 m_shutdownCost = element.attribute("shutdown-cost", "0.0").toDouble();
198
199 m_wbs = element.attribute("wbs", "");
200
201 // Load the project children
202 TQDomNodeList list = element.childNodes();
203 for (unsigned int i=0; i<list.count(); ++i) {
204 if (list.item(i).isElement()) {
205 TQDomElement e = list.item(i).toElement();
206
207 if (e.tagName() == "project") {
208 // Load the subproject
209 Project *child = new Project(this);
210 if (child->load(e)) {
211 if (!project.addSubTask(child, this)) {
212 delete child; // TODO: Complain about this
213 }
214 } else {
215 // TODO: Complain about this
216 delete child;
217 }
218 } else if (e.tagName() == "task") {
219 // Load the task
220 Task *child = new Task(this);
221 if (child->load(e, project)) {
222 if (!project.addSubTask(child, this)) {
223 delete child; // TODO: Complain about this
224 }
225 } else {
226 // TODO: Complain about this
227 delete child;
228 }
229 } else if (e.tagName() == "resource") {
230 // TODO: Load the resource (projects don't have resources yet)
231 } else if (e.tagName() == "effort") {
232 // Load the effort
233 m_effort->load(e);
234 } else if (e.tagName() == "resourcegroup-request") {
235 // Load the resource request
236 ResourceGroupRequest *r = new ResourceGroupRequest();
237 if (r->load(e, project)) {
238 addRequest(r);
239 } else {
240 kdError()<<k_funcinfo<<"Failed to load resource request"<<endl;
241 delete r;
242 }
243 } else if (e.tagName() == "progress") {
244 m_progress.started = (bool)e.attribute("started", "0").toInt();
245 m_progress.finished = (bool)e.attribute("finished", "0").toInt();
246
247 s = e.attribute("startTime");
248 if (s != "")
249 m_progress.startTime = DateTime::fromString(s);
250 s = e.attribute("finishTime");
251 if (s != "")
252 m_progress.finishTime = DateTime::fromString(s);
253 m_progress.percentFinished = e.attribute("percent-finished", "0").toInt();
254 m_progress.remainingEffort = Duration::fromString(e.attribute("remaining-effort"));
255 m_progress.totalPerformed = Duration::fromString(e.attribute("performed-effort"));
256 } else if (e.tagName() == "schedules") {
257 TQDomNodeList lst = e.childNodes();
258 for (unsigned int i=0; i<lst.count(); ++i) {
259 if (lst.item(i).isElement()) {
260 TQDomElement el = lst.item(i).toElement();
261 if (el.tagName() == "schedule") {
262 NodeSchedule *sch = new NodeSchedule();
263 if (sch->loadXML(el)) {
264 sch->setNode(this);
265 addSchedule(sch);
266 } else {
267 kdError()<<k_funcinfo<<"Failed to load schedule"<<endl;
268 delete sch;
269 }
270 }
271 }
272 }
273 }
274 }
275 }
276 //kdDebug()<<k_funcinfo<<m_name<<" loaded"<<endl;
277 return true;
278}
279
280
281void Task::save(TQDomElement &element) const {
282 TQDomElement me = element.ownerDocument().createElement("task");
283 element.appendChild(me);
284
285 //TODO: Handle different types of tasks, milestone, summary...
286 me.setAttribute("id", m_id);
287 me.setAttribute("name", m_name);
288 me.setAttribute("leader", m_leader);
289 me.setAttribute("description", m_description);
290
291 me.setAttribute("scheduling",constraintToString());
292 me.setAttribute("constraint-starttime",m_constraintStartTime.toString(TQt::ISODate));
293 me.setAttribute("constraint-endtime",m_constraintEndTime.toString(TQt::ISODate));
294
295 me.setAttribute("startup-cost", m_startupCost);
296 me.setAttribute("shutdown-cost", m_shutdownCost);
297
298 me.setAttribute("wbs", m_wbs);
299
300 m_effort->save(me);
301
302 TQDomElement el = me.ownerDocument().createElement("progress");
303 me.appendChild(el);
304 el.setAttribute("started", m_progress.started);
305 el.setAttribute("finished", m_progress.finished);
306 el.setAttribute("startTime", m_progress.startTime.toString(TQt::ISODate));
307 el.setAttribute("finishTime", m_progress.finishTime.toString(TQt::ISODate));
308 el.setAttribute("percent-finished", m_progress.percentFinished);
309 el.setAttribute("remaining-effort", m_progress.remainingEffort.toString());
310 el.setAttribute("performed-effort", m_progress.totalPerformed.toString());
311
312 if (!m_schedules.isEmpty()) {
313 TQDomElement schs = me.ownerDocument().createElement("schedules");
314 me.appendChild(schs);
315 TQIntDictIterator<Schedule> it = m_schedules;
316 for (; it.current(); ++it) {
317 if (!it.current()->isDeleted()) {
318 it.current()->saveXML(schs);
319 }
320 }
321 }
322 if (m_requests) {
323 m_requests->save(me);
324 }
325 for (int i=0; i<numChildren(); i++) {
326 getChildNode(i)->save(me);
327 }
328}
329
330void Task::saveAppointments(TQDomElement &element, long id) const {
331 //kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
332 Schedule *sch = findSchedule(id);
333 if (sch) {
334 sch->saveAppointments(element);
335 }
336 TQPtrListIterator<Node> it(m_nodes);
337 for (; it.current(); ++it ) {
338 it.current()->saveAppointments(element, id);
339 }
340}
341
342EffortCostMap Task::plannedEffortCostPrDay(const TQDate &start, const TQDate &end) const {
343 //kdDebug()<<k_funcinfo<<m_name<<endl;
344 if (m_currentSchedule) {
345 return m_currentSchedule->plannedEffortCostPrDay(start, end);
346 }
347 return EffortCostMap();
348}
349
350// Returns the total planned effort for this task (or subtasks)
352 //kdDebug()<<k_funcinfo<<endl;
353 Duration eff;
354 if (type() == Node::Type_Summarytask) {
355 TQPtrListIterator<Node> it(childNodeIterator());
356 for (; it.current(); ++it) {
357 eff += it.current()->plannedEffort();
358 }
359 } else if (m_currentSchedule) {
360 eff = m_currentSchedule->plannedEffort();
361 }
362 return eff;
363}
364
365// Returns the total planned effort for this task (or subtasks) on date
366Duration Task::plannedEffort(const TQDate &date) {
367 //kdDebug()<<k_funcinfo<<endl;
368 Duration eff;
369 if (type() == Node::Type_Summarytask) {
370 TQPtrListIterator<Node> it(childNodeIterator());
371 for (; it.current(); ++it) {
372 eff += it.current()->plannedEffort(date);
373 }
374 } else if (m_currentSchedule) {
375 eff = m_currentSchedule->plannedEffort(date);
376 }
377 return eff;
378}
379
380// Returns the total planned effort for this task (or subtasks) upto and including date
381Duration Task::plannedEffortTo(const TQDate &date) {
382 //kdDebug()<<k_funcinfo<<endl;
383 Duration eff;
384 if (type() == Node::Type_Summarytask) {
385 TQPtrListIterator<Node> it(childNodeIterator());
386 for (; it.current(); ++it) {
387 eff += it.current()->plannedEffortTo(date);
388 }
389 } else if (m_currentSchedule) {
390 eff = m_currentSchedule->plannedEffortTo(date);
391 }
392 return eff;
393}
394
395// Returns the total planned effort for this task (or subtasks)
397 //kdDebug()<<k_funcinfo<<endl;
398 Duration eff;
399 if (type() == Node::Type_Summarytask) {
400 TQPtrListIterator<Node> it(childNodeIterator());
401 for (; it.current(); ++it) {
402 eff += it.current()->actualEffort();
403 }
404 } else {
405 eff = m_progress.totalPerformed;
406 }
407 /* If we want to register pr resource...
408 } else if (m_currentSchedule) {
409 eff = m_currentSchedule->actualEffort();
410 }*/
411 return eff;
412}
413
414// Returns the total planned effort for this task (or subtasks) on date
415Duration Task::actualEffort(const TQDate &date) {
416 //kdDebug()<<k_funcinfo<<endl;
417 Duration eff;
418 if (type() == Node::Type_Summarytask) {
419 TQPtrListIterator<Node> it(childNodeIterator());
420 for (; it.current(); ++it) {
421 eff += it.current()->actualEffort(date);
422 }
423 } else if (m_currentSchedule) {
424 eff = m_currentSchedule->actualEffort(date);
425 }
426 return eff;
427}
428
429// Returns the total planned effort for this task (or subtasks) on date
430Duration Task::actualEffortTo(const TQDate &date) {
431 //kdDebug()<<k_funcinfo<<endl;
432 Duration eff;
433 if (type() == Node::Type_Summarytask) {
434 TQPtrListIterator<Node> it(childNodeIterator());
435 for (; it.current(); ++it) {
436 eff += it.current()->actualEffortTo(date);
437 }
438 } else if (m_currentSchedule) {
439 eff = m_currentSchedule->actualEffortTo(date);
440 }
441 return eff;
442}
443
445 //kdDebug()<<k_funcinfo<<endl;
446 double c = 0;
447 if (type() == Node::Type_Summarytask) {
448 TQPtrListIterator<Node> it(childNodeIterator());
449 for (; it.current(); ++it) {
450 c += it.current()->plannedCost();
451 }
452 } else if (m_currentSchedule) {
453 c = m_currentSchedule->plannedCost();
454 }
455 return c;
456}
457
458double Task::plannedCost(const TQDate &date) {
459 //kdDebug()<<k_funcinfo<<endl;
460 double c = 0;
461 if (type() == Node::Type_Summarytask) {
462 TQPtrListIterator<Node> it(childNodeIterator());
463 for (; it.current(); ++it) {
464 c += it.current()->plannedCost(date);
465 }
466 } else if (m_currentSchedule) {
467 c = m_currentSchedule->plannedCost(date);
468 }
469 return c;
470}
471
472double Task::plannedCostTo(const TQDate &date) {
473 //kdDebug()<<k_funcinfo<<endl;
474 double c = 0;
475 if (type() == Node::Type_Summarytask) {
476 TQPtrListIterator<Node> it(childNodeIterator());
477 for (; it.current(); ++it) {
478 c += it.current()->plannedCostTo(date);
479 }
480 } else if (m_currentSchedule) {
481 c = m_currentSchedule->plannedCostTo(date);
482 }
483 return c;
484}
485
487 //kdDebug()<<k_funcinfo<<endl;
488 double c = 0;
489 if (type() == Node::Type_Summarytask) {
490 TQPtrListIterator<Node> it(childNodeIterator());
491 for (; it.current(); ++it) {
492 c += it.current()->actualCost();
493 }
494 } else if (m_currentSchedule) {
495 c = m_currentSchedule->actualCost();
496 }
497 return c;
498}
499
500double Task::actualCost(const TQDate &date) {
501 //kdDebug()<<k_funcinfo<<endl;
502 double c = 0;
503 if (type() == Node::Type_Summarytask) {
504 TQPtrListIterator<Node> it(childNodeIterator());
505 for (; it.current(); ++it) {
506 c += it.current()->actualCost(date);
507 }
508 } else if (m_currentSchedule) {
509 c = m_currentSchedule->actualCost(date);
510 }
511 return c;
512}
513
514double Task::actualCostTo(const TQDate &date) {
515 //kdDebug()<<k_funcinfo<<endl;
516 double c = 0;
517 if (type() == Node::Type_Summarytask) {
518 TQPtrListIterator<Node> it(childNodeIterator());
519 for (; it.current(); ++it) {
520 c += it.current()->actualCostTo(date);
521 }
522 } else if (m_currentSchedule) {
523 c = m_currentSchedule->actualCostTo(date);
524 }
525 return c;
526}
527
528//FIXME Handle summarytasks
529double Task::effortPerformanceIndex(const TQDate &date, bool *error) {
530 double res = 0.0;
531 Duration ae = actualEffortTo(date);
532
533 bool e = (ae == Duration::zeroDuration || m_progress.percentFinished == 0);
534 if (error) {
535 *error = e;
536 }
537 if (!e) {
538 res = (plannedEffortTo(date).toDouble() * ((double)m_progress.percentFinished/100.0) / ae.toDouble());
539 }
540 return res;
541}
542
543//FIXME Handle summarytasks
544double Task::costPerformanceIndex(const TQDate &date, bool *error) {
545 double res = 0.0;
546 Duration ac = TQ_INT64(actualCostTo(date));
547
548 bool e = (ac == Duration::zeroDuration || m_progress.percentFinished == 0);
549 if (error) {
550 *error = e;
551 }
552 if (!e) {
553 res = (plannedCostTo(date) * m_progress.percentFinished)/(100 * actualCostTo(date));
554 }
555 return res;
556}
557
558void Task::initiateCalculation(Schedule &sch) {
559 //kdDebug()<<k_funcinfo<<m_name<<" schedule: "<<(sch?sch->name():"None")<<" id="<<(sch?sch->id():-1)<<endl;
560 m_visitedForward = false;
561 m_visitedBackward = false;
562 m_currentSchedule = createSchedule(&sch);
563 m_currentSchedule->initiateCalculation();
564 clearProxyRelations();
565 Node::initiateCalculation(sch);
566}
567
568
569void Task::initiateCalculationLists(TQPtrList<Node> &startnodes, TQPtrList<Node> &endnodes, TQPtrList<Node> &summarytasks/*, TQPtrList<Node> &milestones*/) {
570 //kdDebug()<<k_funcinfo<<m_name<<endl;
571 if (type() == Node::Type_Summarytask) {
572 summarytasks.append(this);
573 // propagate my relations to my children and dependent nodes
574
575 TQPtrListIterator<Node> nodes = m_nodes;
576 for (; nodes.current(); ++nodes) {
577 if (!dependParentNodes().isEmpty())
578 nodes.current()->addParentProxyRelations(dependParentNodes());
579 if (!dependChildNodes().isEmpty())
580 nodes.current()->addChildProxyRelations(dependChildNodes());
581 nodes.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
582 }
583 } else {
584 if (isEndNode()) {
585 endnodes.append(this);
586 //kdDebug()<<k_funcinfo<<"endnodes append: "<<m_name<<endl;
587 }
588 if (isStartNode()) {
589 startnodes.append(this);
590 //kdDebug()<<k_funcinfo<<"startnodes append: "<<m_name<<endl;
591 }
592 }
593}
594
595DateTime Task::calculatePredeccessors(const TQPtrList<Relation> &list, int use) {
596 DateTime time;
597 TQPtrListIterator<Relation> it = list;
598 for (; it.current(); ++it) {
599 if (it.current()->parent()->type() == Type_Summarytask) {
600 //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
601 continue; // skip summarytasks
602 }
603 DateTime t = it.current()->parent()->calculateForward(use); // early finish
604 switch (it.current()->type()) {
605 case Relation::StartStart:
606 // I can't start earlier than my predesseccor
607 t = it.current()->parent()->getEarliestStart() + it.current()->lag();
608 break;
609 case Relation::FinishFinish:
610 // I can't finish earlier than my predeccessor, so
611 // I can't start earlier than it's (earlyfinish+lag)- my duration
612 t += it.current()->lag();
613 t -= duration(t, use, true);
614 break;
615 default:
616 t += it.current()->lag();
617 break;
618 }
619 if (!time.isValid() || t > time)
620 time = t;
621 }
622 //kdDebug()<<time.toString()<<" "<<m_name<<" calculatePredeccessors() ("<<list.count()<<")"<<endl;
623 return time;
624}
626 //kdDebug()<<k_funcinfo<<m_name<<<<endl;
627 if (m_currentSchedule == 0) {
628 return DateTime();
629 }
630 Schedule *cs = m_currentSchedule;
631 if (m_visitedForward) {
632 //kdDebug()<<earliestStart.toString()<<" + "<<m_durationBackward.toString()<<" "<<m_name<<" calculateForward() (visited)"<<endl;
633 return cs->earliestStart + m_durationForward;
634 }
635 // First, calculate all predecessors
636 if (!dependParentNodes().isEmpty()) {
637 DateTime time = calculatePredeccessors(dependParentNodes(), use);
638 if (time.isValid() && time > cs->earliestStart) {
639 cs->earliestStart = time;
640 }
641 }
642 if (!m_parentProxyRelations.isEmpty()) {
643 DateTime time = calculatePredeccessors(m_parentProxyRelations, use);
644 if (time.isValid() && time > cs->earliestStart) {
645 cs->earliestStart = time;
646 }
647 }
648 if (type() == Node::Type_Task) {
649 m_durationForward = m_effort->effort(use);
650 switch (constraint()) {
651 case Node::ASAP:
652 case Node::ALAP:
653 cs->earliestStart = workStartAfter(cs->earliestStart);
654 m_durationForward = duration(cs->earliestStart, use, false);
655 //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<endl;
656 break;
657 case Node::MustFinishOn:
658 m_durationForward = duration(m_constraintEndTime, use, true);
659 cs->earliestStart = m_constraintEndTime - m_durationForward;
660 break;
661 case Node::FinishNotLater:
662 m_durationForward = duration(cs->earliestStart, use, false);
663 if (cs->earliestStart + m_durationForward > m_constraintEndTime) {
664 m_durationForward = duration(m_constraintEndTime, use, true);
665 cs->earliestStart = m_constraintEndTime - m_durationForward;
666 }
667 break;
668 case Node::MustStartOn:
670 m_durationForward = duration(cs->earliestStart, use, false);
671 break;
672 case Node::StartNotEarlier:
675 }
676 m_durationForward = duration(cs->earliestStart, use, false);
677 break;
678 case Node::FixedInterval: {
680 m_durationForward = m_constraintEndTime - m_constraintStartTime;
681 break;
682 }
683 }
684 } else if (type() == Node::Type_Milestone) {
685 m_durationForward = Duration::zeroDuration;
686 switch (constraint()) {
687 case Node::MustFinishOn:
689 break;
690 case Node::FinishNotLater:
693 }
694 break;
695 case Node::MustStartOn:
697 break;
698 case Node::StartNotEarlier:
701 }
702 break;
703 case Node::FixedInterval:
705 break;
706 default:
707 break;
708 }
709 //kdDebug()<<k_funcinfo<<m_name<<" "<<earliestStart.toString()<<endl
710 } else if (type() == Node::Type_Summarytask) {
711 kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
712 } else { // ???
713 m_durationForward = Duration::zeroDuration;
714 }
715
716 //kdDebug()<<"Earlyfinish: "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<" "<<m_name<<" calculateForward()"<<endl;
717 m_visitedForward = true;
718 return cs->earliestStart + m_durationForward;
719}
720
721DateTime Task::calculateSuccessors(const TQPtrList<Relation> &list, int use) {
722 DateTime time;
723 TQPtrListIterator<Relation> it = list;
724 for (; it.current(); ++it) {
725 if (it.current()->child()->type() == Type_Summarytask) {
726 //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
727 continue; // skip summarytasks
728 }
729 DateTime t = it.current()->child()->calculateBackward(use);
730 switch (it.current()->type()) {
731 case Relation::StartStart:
732 // I must start before my successor, so
733 // I can't finish later than it's (starttime-lag) + my duration
734 t -= it.current()->lag();
735 t += duration(t, use, false);
736 break;
737 case Relation::FinishFinish:
738 // My successor cannot finish before me, so
739 // I can't finish later than it's latest finish - lag
740 t = it.current()->child()->getLatestFinish() - it.current()->lag();
741 break;
742 default:
743 t -= it.current()->lag();
744 break;
745 }
746 if (!time.isValid() || t < time)
747 time = t;
748 }
749 //kdDebug()<<time.toString()<<" "<<m_name<<" calculateSuccessors() ("<<list.count()<<")"<<endl;
750 return time;
751}
753 //kdDebug()<<k_funcinfo<<m_name<<endl;
754 if (m_currentSchedule == 0) {
755 return DateTime();
756 }
757 Schedule *cs = m_currentSchedule;
758 if (m_visitedBackward) {
759 //kdDebug()<<latestFinish.toString()<<" - "<<m_durationBackward.toString()<<" "<<m_name<<" calculateBackward() (visited)"<<endl;
760 return cs->latestFinish - m_durationBackward;
761 }
762 // First, calculate all successors
763 if (!dependChildNodes().isEmpty()) {
764 DateTime time = calculateSuccessors(dependChildNodes(), use);
765 if (time.isValid() && time < cs->latestFinish) {
766 cs->latestFinish = time;
767 }
768 }
769 if (!m_childProxyRelations.isEmpty()) {
770 DateTime time = calculateSuccessors(m_childProxyRelations, use);
771 if (time.isValid() && time < cs->latestFinish) {
772 cs->latestFinish = time;
773 }
774 }
775 //kdDebug()<<k_funcinfo<<m_name<<": latestFinish="<<cs->latestFinish<<endl;
776 if (type() == Node::Type_Task) {
777 m_durationBackward = m_effort->effort(use);
778 switch (constraint()) {
779 case Node::ASAP:
780 case Node::ALAP:
781 cs->latestFinish = workFinishBefore(cs->latestFinish);
782 m_durationBackward = duration(cs->latestFinish, use, true);
783 break;
784 case Node::MustStartOn:
785 m_durationBackward = duration(m_constraintStartTime, use, false);
786 cs->latestFinish = m_constraintStartTime + m_durationBackward;
787 break;
788 case Node::StartNotEarlier:
789 m_durationBackward = duration(cs->latestFinish, use, true);
790 if (cs->latestFinish - m_durationBackward < m_constraintStartTime) {
791 m_durationBackward = duration(m_constraintStartTime, use, false);
792 cs->latestFinish = m_constraintStartTime + m_durationBackward;
793 }
794 break;
795 case Node::MustFinishOn:
797 m_durationBackward = duration(cs->latestFinish, use, true);
798 break;
799 case Node::FinishNotLater:
802 }
803 m_durationBackward = duration(cs->latestFinish, use, true);
804 break;
805 case Node::FixedInterval: {
807 m_durationBackward = m_constraintEndTime - m_constraintStartTime;
808 break;
809 }
810 }
811 } else if (type() == Node::Type_Milestone) {
812 m_durationBackward = Duration::zeroDuration;
813 switch (constraint()) {
814 case Node::MustFinishOn:
816 break;
817 case Node::FinishNotLater:
820 }
821 break;
822 case Node::MustStartOn:
824 break;
825 case Node::StartNotEarlier:
828 }
829 break;
830 case Node::FixedInterval:
832 break;
833 default:
834 break;
835 }
836 //kdDebug()<<k_funcinfo<<m_name<<" "<<cs->latestFinish<<endl;
837 } else if (type() == Node::Type_Summarytask) {
838 kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
839 } else { // ???
840 m_durationBackward = Duration::zeroDuration;
841 }
842 //kdDebug()<<"Latestart: "<<cs->latestFinish<<"-"<<m_durationBackward.toString()<<"="<<(cs->latestFinish-m_durationBackward).toString()<<" "<<m_name<<" calculateBackward()"<<endl;
843 m_visitedBackward = true;
844 return cs->latestFinish - m_durationBackward;
845}
846
847DateTime Task::schedulePredeccessors(const TQPtrList<Relation> &list, int use) {
848 DateTime time;
849 TQPtrListIterator<Relation> it = list;
850 for (; it.current(); ++it) {
851 if (it.current()->parent()->type() == Type_Summarytask) {
852 //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
853 continue; // skip summarytasks
854 }
855 // schedule the predecessors
856 DateTime earliest = it.current()->parent()->getEarliestStart();
857 DateTime t = it.current()->parent()->scheduleForward(earliest, use);
858 switch (it.current()->type()) {
859 case Relation::StartStart:
860 // I can't start before my predesseccor
861 t = it.current()->parent()->startTime() + it.current()->lag();
862 break;
863 case Relation::FinishFinish:
864 // I can't end before my predecessor, so
865 // I can't start before it's endtime - my duration
866 t -= duration(t + it.current()->lag(), use, true);
867 break;
868 default:
869 t += it.current()->lag();
870 break;
871 }
872 if (!time.isValid() || t > time)
873 time = t;
874 }
875 //kdDebug()<<time.toString()<<" "<<m_name<<" schedulePredeccessors()"<<endl;
876 return time;
877}
878
879DateTime Task::scheduleForward(const DateTime &earliest, int use) {
880 //kdDebug()<<k_funcinfo<<m_name<<" earliest="<<earliest<<endl;
881 if (m_currentSchedule == 0) {
882 return DateTime();
883 }
884 Schedule *cs = m_currentSchedule;
885 if (m_visitedForward) {
886 return cs->endTime;
887 }
888 cs->notScheduled = false;
889 cs->startTime = earliest > cs->earliestStart ? earliest : cs->earliestStart;
890 // First, calculate all my own predecessors
891 DateTime time = schedulePredeccessors(dependParentNodes(), use);
892 if (time.isValid() && time > cs->startTime) {
893 cs->startTime = time;
894 //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
895 }
896 // Then my parents
897 time = schedulePredeccessors(m_parentProxyRelations, use);
898 if (time.isValid() && time > cs->startTime) {
899 cs->startTime = time;
900 //kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
901 }
902 //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
903 if(type() == Node::Type_Task) {
904 cs->duration = m_effort->effort(use);
905 switch (m_constraint) {
906 case Node::ASAP:
907 // cs->startTime calculated above
908 //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
909 cs->startTime = workStartAfter(cs->startTime);
910 cs->duration = duration(cs->startTime, use, false);
911 cs->endTime = cs->startTime + cs->duration;
912 //kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
913 break;
914 case Node::ALAP:
915 // cd->startTime calculated above
916 cs->duration = duration(cs->latestFinish, use, true);
917 cs->endTime = cs->latestFinish;
918 cs->startTime = cs->endTime - cs->duration;
919 //kdDebug()<<k_funcinfo<<m_name<<" endTime="<<cs->endTime<<" latest="<<cs->latestFinish<<endl;
920 break;
921 case Node::StartNotEarlier:
922 // cs->startTime calculated above
923 //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cd->startTime.toString()<<endl;
926 }
927 cs->startTime = workStartAfter(cs->startTime);
928 cs->duration = duration(cs->startTime, use, false);
929 cs->endTime = cs->startTime + cs->duration;
930 if (cs->endTime > cs->latestFinish) {
931 cs->schedulingError = true;
932 }
933 break;
934 case Node::FinishNotLater:
935 // cs->startTime calculated above
936 //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
937 cs->duration = duration(cs->startTime, use, false);
938 cs->endTime = cs->startTime + cs->duration;
939 if (cs->endTime > m_constraintEndTime) {
940 cs->schedulingError = true;
942 cs->duration = duration(cs->endTime, use, true);
943 cs->startTime = cs->endTime - cs->duration;
944 }
945 break;
946 case Node::MustStartOn:
947 // cs->startTime calculated above
948 //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
949 if (m_constraintStartTime < cs->startTime ||
950 m_constraintStartTime > cs->latestFinish - m_durationBackward) {
951 cs->schedulingError = true;
952 }
954 cs->duration = duration(cs->startTime, use, false);
955 cs->endTime = cs->startTime + cs->duration;
956 break;
957 case Node::MustFinishOn:
958 // cs->startTime calculated above
959 //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
961 m_constraintEndTime < cs->earliestStart + m_durationForward) {
962 cs->schedulingError = true;
963 }
965 cs->duration = duration(cs->endTime, use, true);
966 cs->startTime = cs->endTime - cs->duration;
967 break;
968 case Node::FixedInterval: {
969 // cs->startTime calculated above
970 //kdDebug()<<"FixedInterval="<<m_constraintStartTime<<" "<<cs->startTime<<endl;
971 if (cs->startTime < cs->earliestStart) {
972 cs->schedulingError = true;
973 }
976 cs->duration = cs->endTime - cs->startTime;
977 cs->workStartTime = m_constraintStartTime;
978 cs->workEndTime = m_constraintEndTime;
979 //kdDebug()<<"FixedInterval="<<cs->startTime<<", "<<cs->endTime<<endl;
980 break;
981 }
982 default:
983 break;
984 }
985 if (m_requests) {
986 m_requests->reserve(cs->startTime, cs->duration);
987 }
988 } else if (type() == Node::Type_Milestone) {
989 switch (m_constraint) {
990 case Node::ASAP: {
991 cs->endTime = cs->startTime;
992 break;
993 }
994 case Node::ALAP: {
995 cs->startTime = cs->latestFinish;
996 cs->endTime = cs->latestFinish;
997 break;
998 }
999 case Node::MustStartOn:
1000 case Node::FixedInterval:
1001 //kdDebug()<<"Forw, MustStartOn: "<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
1002 if (m_constraintStartTime < cs->startTime ||
1004 cs->schedulingError = true;
1005 }
1008 break;
1009 case Node::MustFinishOn:
1010 if (m_constraintEndTime < cs->startTime ||
1012 cs->schedulingError = true;
1013 }
1016 break;
1017 case Node::StartNotEarlier:
1018 if (cs->startTime < m_constraintStartTime) {
1019 cs->schedulingError = true;
1020 }
1021 cs->endTime = cs->startTime;
1022 break;
1023 case Node::FinishNotLater:
1024 if (cs->startTime > m_constraintEndTime) {
1025 cs->schedulingError = true;
1026 }
1027 cs->endTime = cs->startTime;
1028 break;
1029 default:
1030 break;
1031 }
1033 //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime<<", "<<cs->endTime<<endl;
1034 } else if (type() == Node::Type_Summarytask) {
1035 //shouldn't come here
1036 cs->endTime = cs->startTime;
1037 cs->duration = cs->endTime - cs->startTime;
1038 kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
1039 }
1040 //kdDebug()<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<" "<<m_name<<" scheduleForward()"<<endl;
1041 m_visitedForward = true;
1042 return cs->endTime;
1043}
1044
1045DateTime Task::scheduleSuccessors(const TQPtrList<Relation> &list, int use) {
1046 DateTime time;
1047 TQPtrListIterator<Relation> it = list;
1048 for (; it.current(); ++it) {
1049 if (it.current()->child()->type() == Type_Summarytask) {
1050 //kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->child()->name()<<endl;
1051 continue;
1052 }
1053 // get the successors starttime
1054 DateTime latest = it.current()->child()->getLatestFinish();
1055 DateTime t = it.current()->child()->scheduleBackward(latest, use);
1056 switch (it.current()->type()) {
1057 case Relation::StartStart:
1058 // I can't start before my successor, so
1059 // I can't finish later than it's starttime + my duration
1060 t += duration(t - it.current()->lag(), use, false);
1061 break;
1062 case Relation::FinishFinish:
1063 t = it.current()->child()->endTime() - it.current()->lag();
1064 break;
1065 default:
1066 t -= it.current()->lag();
1067 break;
1068 }
1069 if (!time.isValid() || t < time)
1070 time = t;
1071 }
1072 return time;
1073}
1075 //kdDebug()<<k_funcinfo<<m_name<<": latest="<<latest<<endl;
1076 if (m_currentSchedule == 0) {
1077 return DateTime();
1078 }
1079 Schedule *cs = m_currentSchedule;
1080 if (m_visitedBackward) {
1081 return cs->startTime;
1082 }
1083 cs->notScheduled = false;
1084 cs->endTime = latest < cs->latestFinish ? latest : cs->latestFinish;
1085 // First, calculate all my own successors
1086 DateTime time = scheduleSuccessors(dependChildNodes(), use);
1087 if (time.isValid() && time < cs->endTime) {
1088 cs->endTime = time;
1089 }
1090 // Then my parents
1091 time = scheduleSuccessors(m_childProxyRelations, use);
1092 if (time.isValid() && time < cs->endTime) {
1093 cs->endTime = time;
1094 }
1095 if (type() == Node::Type_Task) {
1096 cs->duration = m_effort->effort(use);
1097 switch (m_constraint) {
1098 case Node::ASAP: {
1099 // cs->endTime calculated above
1100 //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" early="<<cs->earliestStart<<endl;
1101 cs->endTime = workFinishBefore(cs->endTime);
1102 cs->duration = duration(cs->earliestStart, use, false);
1103 cs->startTime = cs->earliestStart;
1104 DateTime e = cs->startTime + cs->duration;
1105 if (e > cs->endTime) {
1106 cs->schedulingError = true;
1107 }
1108 cs->endTime = e;
1109 //kdDebug()<<k_funcinfo<<m_name<<": start="<<cs->startTime<<"+"<<cs->duration.toString()<<"="<<e<<" -> end="<<cs->endTime<<endl;
1110 break;
1111 }
1112 case Node::ALAP:
1113 // cs->endTime calculated above
1114 //kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" late="<<cs->latestFinish<<endl;
1115 cs->endTime = workFinishBefore(cs->endTime);
1116 cs->duration = duration(cs->endTime, use, true);
1117 cs->startTime = cs->endTime - cs->duration;
1118 //kdDebug()<<k_funcinfo<<m_name<<": lateStart="<<cs->startTime<<endl;
1119 break;
1120 case Node::StartNotEarlier:
1121 // cs->endTime calculated above
1122 //kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cs->endTime.toString()<<endl;
1123 cs->endTime = workFinishBefore(cs->endTime);
1124 cs->duration = duration(cs->endTime, use, true);
1125 cs->startTime = cs->endTime - cs->duration;
1126 if (cs->startTime < m_constraintStartTime) {
1127 cs->schedulingError = true;
1129 cs->duration = duration(cs->startTime, use, false);
1130 cs->endTime = cs->startTime + cs->duration;
1131 }
1132 break;
1133 case Node::FinishNotLater:
1134 // cs->endTime calculated above
1135 //kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->endTime.toString()<<endl;
1136 if (cs->endTime > m_constraintEndTime) {
1137 cs->schedulingError = true;
1139 }
1140 cs->endTime = workFinishBefore(cs->endTime);
1141 cs->duration = duration(cs->endTime, use, true);
1142 cs->startTime = cs->endTime - cs->duration;
1143 break;
1144 case Node::MustStartOn:
1145 // cs->endTime calculated above
1146 //kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
1147 if (m_constraintStartTime < cs->earliestStart ||
1148 m_constraintStartTime > cs->latestFinish - m_durationBackward) {
1149 cs->schedulingError = true;
1150 }
1152 cs->duration = duration(cs->startTime, use, false);
1153 cs->endTime = cs->startTime + cs->duration;
1154 break;
1155 case Node::MustFinishOn:
1156 // cs->endTime calculated above
1157 //kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
1159 m_constraintEndTime < cs->earliestStart + m_durationForward) {
1160 cs->schedulingError = true;
1161 }
1163 cs->duration = duration(cs->endTime, use, true);
1164 cs->startTime = cs->endTime - cs->duration;
1165 break;
1166 case Node::FixedInterval: {
1167 // cs->endTime calculated above
1168 //kdDebug()<<k_funcinfo<<"FixedInterval="<<m_constraintEndTime<<" "<<cs->endTime<<endl;
1169 if (m_constraintEndTime > cs->endTime) {
1170 cs->schedulingError = true;
1171 //kdDebug()<<k_funcinfo<<"FixedInterval error: "<<m_constraintEndTime<<" > "<<cs->endTime<<endl;
1172 }
1175 cs->duration = cs->endTime - cs->startTime;
1176 cs->workStartTime = m_constraintStartTime;
1177 cs->workEndTime = m_constraintEndTime;
1178 break;
1179 }
1180 default:
1181 break;
1182 }
1183 if (m_requests) {
1184 m_requests->reserve(cs->startTime, cs->duration);
1185 }
1186 } else if (type() == Node::Type_Milestone) {
1187 switch (m_constraint) {
1188 case Node::ASAP:
1189 cs->startTime = cs->earliestStart;
1190 cs->endTime = cs->earliestStart;
1191 break;
1192 case Node::ALAP:
1193 cs->startTime = cs->latestFinish;
1194 cs->endTime = cs->latestFinish;
1195 break;
1196 case Node::MustStartOn:
1197 case Node::FixedInterval:
1198 if (m_constraintStartTime < cs->earliestStart ||
1200 cs->schedulingError = true;
1201 }
1202 cs->startTime = cs->earliestStart;
1203 cs->endTime = cs->earliestStart;
1204 break;
1205 case Node::MustFinishOn:
1206 if (m_constraintEndTime < cs->earliestStart ||
1208 cs->schedulingError = true;
1209 }
1210 cs->startTime = cs->earliestStart;
1211 cs->endTime = cs->earliestStart;
1212 break;
1213 case Node::StartNotEarlier:
1214 if (m_constraintStartTime > cs->endTime) {
1215 cs->schedulingError = true;
1216 }
1217 cs->startTime = cs->endTime;
1218 break;
1219 case Node::FinishNotLater:
1220 if (m_constraintEndTime < cs->endTime) {
1221 cs->schedulingError = true;
1222 }
1223 cs->startTime = cs->endTime;
1224 break;
1225 default:
1226 break;
1227 }
1229 } else if (type() == Node::Type_Summarytask) {
1230 //shouldn't come here
1231 cs->startTime = cs->endTime;
1232 cs->duration = cs->endTime - cs->startTime;
1233 kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
1234 }
1235 //kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<endl;
1236 m_visitedBackward = true;
1237 return cs->startTime;
1238}
1239
1241 if (m_currentSchedule == 0)
1242 return;
1243 if (type() == Type_Summarytask) {
1244 DateTime start = m_currentSchedule->latestFinish;
1245 DateTime end = m_currentSchedule->earliestStart;
1246 TQPtrListIterator<Node> it(m_nodes);
1247 for (; it.current(); ++it) {
1248 it.current()->adjustSummarytask();
1249 if (it.current()->startTime() < start)
1250 start = it.current()->startTime();
1251 if (it.current()->endTime() > end)
1252 end = it.current()->endTime();
1253 }
1254 m_currentSchedule->startTime = start;
1255 m_currentSchedule->endTime = end;
1256 m_currentSchedule->duration = end - start;
1257 m_currentSchedule->notScheduled = false;
1258 //kdDebug()<<k_funcinfo<<cs->name<<": "<<m_currentSchedule->startTime.toString()<<" : "<<m_currentSchedule->endTime.toString()<<endl;
1259 }
1260}
1261
1262Duration Task::calcDuration(const DateTime &time, const Duration &effort, bool backward) {
1263 //kdDebug()<<"--------> calcDuration "<<(backward?"(B) ":"(F) ")<<m_name<<" time="<<time<<" effort="<<effort.toString(Duration::Format_Day)<<endl;
1264
1265 // Allready checked: m_effort, m_currentSchedule and time.
1266 Duration dur = effort; // use effort as default duration
1267 if (m_effort->type() == Effort::Type_Effort) {
1268 if (m_requests == 0 || m_requests->isEmpty()) {
1269 m_currentSchedule->resourceError = true;
1270 return effort;
1271 }
1272 dur = m_requests->duration(time, effort, backward);
1273 if (dur == Duration::zeroDuration) {
1274 kdWarning()<<k_funcinfo<<"zero duration: Resource not available"<<endl;
1275 m_currentSchedule->resourceNotAvailable = true;
1276 dur = effort; //???
1277 }
1278 return dur;
1279 }
1280 if (m_effort->type() == Effort::Type_FixedDuration) {
1281 //TODO: Different types of fixed duration
1282 return dur; //
1283 }
1284 kdError()<<k_funcinfo<<"Unsupported effort type: "<<m_effort->type()<<endl;
1285 return dur;
1286}
1287
1288void Task::clearProxyRelations() {
1289 m_parentProxyRelations.clear();
1290 m_childProxyRelations.clear();
1291}
1292
1293void Task::addParentProxyRelations(TQPtrList<Relation> &list) {
1294 //kdDebug()<<k_funcinfo<<m_name<<endl;
1295 if (type() == Type_Summarytask) {
1296 // propagate to my children
1297 //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
1298 TQPtrListIterator<Node> nodes = m_nodes;
1299 for (; nodes.current(); ++nodes) {
1300 nodes.current()->addParentProxyRelations(list);
1301 nodes.current()->addParentProxyRelations(dependParentNodes());
1302 }
1303 } else {
1304 // add 'this' as child relation to the relations parent
1305 //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
1306 TQPtrListIterator<Relation> it = list;
1307 for (; it.current(); ++it) {
1308 it.current()->parent()->addChildProxyRelation(this, it.current());
1309 // add a parent relation to myself
1310 addParentProxyRelation(it.current()->parent(), it.current());
1311 }
1312 }
1313}
1314
1315void Task::addChildProxyRelations(TQPtrList<Relation> &list) {
1316 //kdDebug()<<k_funcinfo<<m_name<<endl;
1317 if (type() == Type_Summarytask) {
1318 // propagate to my children
1319 //kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
1320 TQPtrListIterator<Node> nodes = m_nodes;
1321 for (; nodes.current(); ++nodes) {
1322 nodes.current()->addChildProxyRelations(list);
1323 nodes.current()->addChildProxyRelations(dependChildNodes());
1324 }
1325 } else {
1326 // add 'this' as parent relation to the relations child
1327 //kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
1328 TQPtrListIterator<Relation> it = list;
1329 for (; it.current(); ++it) {
1330 it.current()->child()->addParentProxyRelation(this, it.current());
1331 // add a child relation to myself
1332 addChildProxyRelation(it.current()->child(), it.current());
1333 }
1334 }
1335}
1336
1337void Task::addParentProxyRelation(Node *node, const Relation *rel) {
1338 if (node->type() != Type_Summarytask) {
1339 if (type() == Type_Summarytask) {
1340 //kdDebug()<<"Add parent proxy from my children "<<m_name<<" to "<<node->name()<<endl;
1341 TQPtrListIterator<Node> nodes = m_nodes;
1342 for (; nodes.current(); ++nodes) {
1343 nodes.current()->addParentProxyRelation(node, rel);
1344 }
1345 } else {
1346 //kdDebug()<<"Add parent proxy from "<<node->name()<<" to (me) "<<m_name<<endl;
1347 m_parentProxyRelations.append(new ProxyRelation(node, this, rel->type(), rel->lag()));
1348 }
1349 }
1350}
1351
1352void Task::addChildProxyRelation(Node *node, const Relation *rel) {
1353 if (node->type() != Type_Summarytask) {
1354 if (type() == Type_Summarytask) {
1355 //kdDebug()<<"Add child proxy from my children "<<m_name<<" to "<<node->name()<<endl;
1356 TQPtrListIterator<Node> nodes = m_nodes;
1357 for (; nodes.current(); ++nodes) {
1358 nodes.current()->addChildProxyRelation(node, rel);
1359 }
1360 } else {
1361 //kdDebug()<<"Add child proxy from (me) "<<m_name<<" to "<<node->name()<<endl;
1362 m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag()));
1363 }
1364 }
1365}
1366
1367bool Task::isEndNode() const {
1368 TQPtrListIterator<Relation> it = m_dependChildNodes;
1369 for (; it.current(); ++it) {
1370 if (it.current()->type() == Relation::FinishStart)
1371 return false;
1372 }
1373 TQPtrListIterator<Relation> pit = m_childProxyRelations;
1374 for (; pit.current(); ++pit) {
1375 if (pit.current()->type() == Relation::FinishStart)
1376 return false;
1377 }
1378 return true;
1379}
1380bool Task::isStartNode() const {
1381 TQPtrListIterator<Relation> it = m_dependParentNodes;
1382 for (; it.current(); ++it) {
1383 if (it.current()->type() == Relation::FinishStart ||
1384 it.current()->type() == Relation::StartStart)
1385 return false;
1386 }
1387 TQPtrListIterator<Relation> pit = m_parentProxyRelations;
1388 for (; pit.current(); ++pit) {
1389 if (pit.current()->type() == Relation::FinishStart ||
1390 pit.current()->type() == Relation::StartStart)
1391 return false;
1392 }
1393 return true;
1394}
1395
1397 if (m_currentSchedule == 0)
1398 return DateTime();
1399 if (m_requests)
1400 return m_currentSchedule->workStartTime;
1401 return m_currentSchedule->startTime;
1402}
1403
1405 if (m_currentSchedule == 0)
1406 return DateTime();
1407 return m_currentSchedule->endTime;
1408}
1409
1410DateTime Task::workStartAfter(const DateTime &dt) {
1411 if (m_requests) {
1412 DateTime t = m_requests->availableAfter(dt);
1413 return t.isValid() ? t : dt;
1414 }
1415 return dt;
1416}
1417
1418DateTime Task::workFinishBefore(const DateTime &dt) {
1419 if (m_requests) {
1420 return m_requests->availableBefore(dt);
1421 }
1422 return dt;
1423}
1424
1426 if (m_currentSchedule == 0 ||
1427 m_currentSchedule->schedulingError ||
1428 effortMetError()) {
1430 }
1431 Duration f;
1432 if (type() == Node::Type_Milestone) {
1433 if (m_currentSchedule->startTime < m_currentSchedule->latestFinish) {
1434 f = m_currentSchedule->latestFinish - m_currentSchedule->startTime;
1435 }
1436 } else if (m_effort->type() == Effort::Type_FixedDuration) {
1437 if (m_currentSchedule->endTime.isValid()) {
1438 if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
1439 f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
1440 }
1441 }
1442 } else {
1443 if (m_currentSchedule->workEndTime.isValid())
1444 if (m_currentSchedule->workEndTime < m_currentSchedule->latestFinish) {
1445 f = m_currentSchedule->latestFinish - m_currentSchedule->workEndTime;
1446 } else if (m_currentSchedule->endTime.isValid()) {
1447 if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
1448 f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
1449 }
1450 }
1451 }
1452 //kdDebug()<<k_funcinfo<<f.toString()<<endl;
1453 return f;
1454}
1455
1457 Schedule *cs = m_currentSchedule;
1458 if (cs == 0) {
1459 return false;
1460 }
1461 return cs->earliestStart >= cs->startTime && cs->latestFinish <= cs->endTime;
1462}
1463
1464bool Task::calcCriticalPath(bool fromEnd) {
1465 if (m_currentSchedule == 0)
1466 return false;
1467 //kdDebug()<<k_funcinfo<<m_name<<" fromEnd="<<fromEnd<<" cp="<<m_currentSchedule->inCriticalPath<<endl;
1468 if (m_currentSchedule->inCriticalPath) {
1469 return true; // path allready calculated
1470 }
1471 if (!isCritical()) {
1472 return false;
1473 }
1474 if (fromEnd) {
1475 if (isEndNode()) {
1476 m_currentSchedule->inCriticalPath = true;
1477 //kdDebug()<<k_funcinfo<<m_name<<" end node"<<endl;
1478 return true;
1479 }
1480 TQPtrListIterator<Relation> it(m_childProxyRelations);
1481 for (; it.current(); ++it) {
1482 if (it.current()->child()->calcCriticalPath(fromEnd)) {
1483 m_currentSchedule->inCriticalPath = true;
1484 }
1485 }
1486 TQPtrListIterator<Relation> pit(m_dependChildNodes);
1487 for (; pit.current(); ++pit) {
1488 if (pit.current()->child()->calcCriticalPath(fromEnd)) {
1489 m_currentSchedule->inCriticalPath = true;
1490 }
1491 }
1492 } else {
1493 if (isStartNode()) {
1494 m_currentSchedule->inCriticalPath = true;
1495 //kdDebug()<<k_funcinfo<<m_name<<" start node"<<endl;
1496 return true;
1497 }
1498 TQPtrListIterator<Relation> it(m_parentProxyRelations);
1499 for (; it.current(); ++it) {
1500 if (it.current()->parent()->calcCriticalPath(fromEnd)) {
1501 m_currentSchedule->inCriticalPath = true;
1502 }
1503 }
1504 TQPtrListIterator<Relation> pit(m_dependParentNodes);
1505 for (; pit.current(); ++pit) {
1506 if (pit.current()->parent()->calcCriticalPath(fromEnd)) {
1507 m_currentSchedule->inCriticalPath = true;
1508 }
1509 }
1510 }
1511 //kdDebug()<<k_funcinfo<<m_name<<" return cp="<<m_currentSchedule->inCriticalPath<<endl;
1512 return m_currentSchedule->inCriticalPath;
1513}
1514
1516 setCurrentSchedulePtr(findSchedule(id));
1518}
1519
1520bool Task::effortMetError() const {
1521 if (m_currentSchedule->notScheduled) {
1522 return false;
1523 }
1524 return m_currentSchedule->plannedEffort() < effort()->effort(static_cast<Effort::Use>(static_cast<int>(m_currentSchedule->type())));
1525}
1526
1527#ifndef NDEBUG
1528void Task::printDebug(bool children, TQCString indent) {
1529 kdDebug()<<indent<<"+ Task node: "<<name()<<" type="<<type()<<endl;
1530 indent += "! ";
1531 kdDebug()<<indent<<"Requested resources (total): "<<units()<<"%"<<endl;
1532 kdDebug()<<indent<<"Requested resources (work): "<<workUnits()<<"%"<<endl;
1533 if (m_requests)
1534 m_requests->printDebug(indent);
1535
1536 Node::printDebug(children, indent);
1537
1538}
1539
1540#endif
1541
1542} //KPlato namespace
DateTime is a TQDateTime which knows about Duration.
Definition kptdatetime.h:33
The duration class can be used to store a timespan in a convenient format.
Definition kptduration.h:34
static const Duration zeroDuration
This is useful for occasions where we need a zero duration.
NodeSchedule holds scheduling information for a node (task).
This class represents any node in the project, a node can be a project or a subproject or any task.
Definition kptnode.h:57
Schedule * findSchedule(const TQString name, const Schedule::Type type) const
Find schedule matching name and type. Does not return deleted schedule.
Definition kptnode.cpp:688
DateTime m_constraintEndTime
m_constraintEndTime is used if any of the constraints FixedInterval, FinishNotLater,...
Definition kptnode.h:517
void addSchedule(Schedule *schedule)
Add schedule to list, replace if schedule with same id allready exists.
Definition kptnode.cpp:668
virtual void setCurrentSchedule(long id)
Set current schedule to schedule with identity id, for me and my children.
Definition kptnode.cpp:769
DateTime m_constraintStartTime
m_constraintTime is used if any of the constraints FixedInterval, StartNotEarlier,...
Definition kptnode.h:512
virtual DateTime startTime() const
Return the scheduled start time.
Definition kptnode.h:174
virtual DateTime endTime() const
Return the scheduled end time.
Definition kptnode.h:179
Project is the main node in a project, it contains child nodes and possibly sub-projects.
Definition kptproject.h:52
This class represents a group of similar resources to be assigned to a task e.g.
Definition kptresource.h:66
The Schedule class holds data calculated during project calculation and scheduling,...
Definition kptschedule.h:55
DateTime startTime
startTime is the scheduled start time.
Duration duration
duration is the scheduled duration which depends on e.g.
DateTime latestFinish
latestFinish is calculated by PERT/CPM.
bool notScheduled
Set if the node has not been scheduled.
DateTime endTime
m_endTime is the scheduled finish time.
DateTime earliestStart
earliestStart is calculated by PERT/CPM.
bool schedulingError
Set if the task cannot be scheduled to fullfill all the constraints.
virtual Duration plannedEffortTo(const TQDate &date)
Returns the planned effort up to and including date.
Definition kpttask.cpp:381
virtual double plannedCost()
Returns the total planned cost for this task (or subtasks).
Definition kpttask.cpp:444
bool isStartNode() const
Check if this node has any dependent parent nodes.
Definition kpttask.cpp:1380
virtual EffortCostMap plannedEffortCostPrDay(const TQDate &start, const TQDate &end) const
Returns a list of planned effort and cost for this task for the interval start, end inclusive.
Definition kpttask.cpp:342
DateTime scheduleForward(const DateTime &earliest, int use)
Schedules the task within the limits of earliestStart and latestFinish.
Definition kpttask.cpp:879
Duration * getExpectedDuration()
Returns the (previously) calculated duration.
Definition kpttask.cpp:88
virtual bool calcCriticalPath(bool fromEnd)
Calculate critical path.
Definition kpttask.cpp:1464
virtual int type() const
Return task type. Can be Type_Task, Type_Summarytask ot Type_Milestone.
Definition kpttask.cpp:74
virtual void save(TQDomElement &element) const
Save to document.
Definition kpttask.cpp:281
virtual bool isCritical()
A task is critical if there is no positive float.
Definition kpttask.cpp:1456
bool isEndNode() const
Check if this node has any dependent child nodes.
Definition kpttask.cpp:1367
virtual Duration plannedEffort()
Returns the total planned effort for this task (or subtasks).
Definition kpttask.cpp:351
DateTime calculateBackward(int use)
Calculates ref m_durationBackward from ref latestFinish and returns the resulting start time,...
Definition kpttask.cpp:752
virtual double plannedCostTo(const TQDate &)
Planned cost up to and including date.
Definition kpttask.cpp:472
virtual void saveAppointments(TQDomElement &element, long id) const
Save appointments for schedule with id.
Definition kpttask.cpp:330
virtual Duration actualEffortTo(const TQDate &date)
Returns the actual effort up to and including date.
Definition kpttask.cpp:430
virtual void setCurrentSchedule(long id)
Set current schedule to schedule with identity id, for me nd my children.
Definition kpttask.cpp:1515
Duration positiveFloat()
Return the duration that an activity's start can be delayed without affecting the project completion ...
Definition kpttask.cpp:1425
void calcResourceOverbooked()
Calculates if the assigned resource is overbooked within the duration of this task.
Definition kpttask.cpp:161
virtual double actualCost()
Returns the actaually reported cost for this task (or subtasks).
Definition kpttask.cpp:486
Duration * getRandomDuration()
Instead of using the expected duration, generate a random value using the Distribution of each Task.
Definition kpttask.cpp:94
virtual DateTime workStartTime() const
Return the time when work can actually start on this task.
Definition kpttask.cpp:1396
ResourceGroupRequest * resourceGroupRequest(ResourceGroup *group) const
Return the resource request made to group (There should be only one).
Definition kpttask.cpp:98
virtual double actualCostTo(const TQDate &)
Actual cost up to and including date.
Definition kpttask.cpp:514
virtual bool load(TQDomElement &element, Project &project)
Load from document.
Definition kpttask.cpp:172
DateTime calculateForward(int use)
Calculates ref m_durationForward from ref earliestStart and returns the resulting end time,...
Definition kpttask.cpp:625
virtual Duration actualEffort()
Returns the total actual effort for this task (or subtasks).
Definition kpttask.cpp:396
void initiateCalculationLists(TQPtrList< Node > &startnodes, TQPtrList< Node > &endnodes, TQPtrList< Node > &summarytasks)
Sets up the lists used for calculation.
Definition kpttask.cpp:569
double costPerformanceIndex(const TQDate &date, bool *error=0)
Cost performance index.
Definition kpttask.cpp:544
double effortPerformanceIndex(const TQDate &date, bool *error=0)
Effort based performance index.
Definition kpttask.cpp:529
Duration calcDuration(const DateTime &time, const Duration &effort, bool backward)
Return the duration calculated on bases of the requested resources.
Definition kpttask.cpp:1262
virtual DateTime workEndTime() const
Return the time when work can actually finish on this task.
Definition kpttask.cpp:1404
void adjustSummarytask()
Summarytasks (with milestones) need special treatment because milestones are always 'glued' to their ...
Definition kpttask.cpp:1240
DateTime scheduleBackward(const DateTime &latest, int use)
Schedules the task within the limits of earliestStart and latestFinish.
Definition kpttask.cpp:1074