kplato

kptnode.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Thomas zander <zander@kde.org>
3 Copyright (C) 2004, 2005 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#include "kptnode.h"
21
22#include "kptappointment.h"
23#include "kptaccount.h"
24#include "kptwbsdefinition.h"
25#include "kptresource.h"
26#include "kptschedule.h"
27
28#include <tqptrlist.h>
29#include <tqdom.h>
30
31#include <tdelocale.h>
32#include <kdebug.h>
33
34namespace KPlato
35{
36
37Node::Node(Node *parent) : m_nodes(), m_dependChildNodes(), m_dependParentNodes() {
38 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
39 m_parent = parent;
40 init();
41 m_id = TQString(); // Not mapped
42}
43
44Node::Node(Node &node, Node *parent)
45 : m_nodes(),
46 m_dependChildNodes(),
47 m_dependParentNodes() {
48 //kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
49 m_parent = parent;
50 init();
51 m_name = node.name();
52 m_leader = node.leader();
53 m_description = node.description();
54 m_constraint = (ConstraintType) node.constraint();
55 m_constraintStartTime = node.constraintStartTime();
56 m_constraintEndTime = node.constraintEndTime();
57
58 m_dateOnlyStartDate = node.startDate();
59 m_dateOnlyEndDate = node.endDate();
60
61 m_runningAccount = node.runningAccount();
62 m_startupAccount = node.startupAccount();
63 m_shutdownAccount = node.shutdownAccount();
64
65 m_startupCost = node.startupCost();
66 m_shutdownCost = node.shutdownCost();
67
68 m_schedules.setAutoDelete(node.m_schedules.autoDelete());
69}
70
71Node::~Node() {
72 if (findNode() == this) {
73 removeId(); // only remove myself (I may be just a working copy)
74 }
75 Relation *rel = 0;
76 while ((rel = m_dependParentNodes.getFirst())) {
77 delete rel;
78 }
79 while ((rel = m_dependChildNodes.getFirst())) {
80 delete rel;
81 }
82 if (m_runningAccount)
83 m_runningAccount->removeRunning(*this);
84 if (m_startupAccount)
85 m_startupAccount->removeStartup(*this);
86 if (m_shutdownAccount)
87 m_shutdownAccount->removeShutdown(*this);
88}
89
90void Node::init() {
91 m_currentSchedule = 0;
92 m_nodes.setAutoDelete(true);
93 m_name="";
94 m_constraint = Node::ASAP;
95 m_effort = 0;
96 m_visitedForward = false;
97 m_visitedBackward = false;
98
99 m_dateOnlyStartDate = m_dateOnlyEndDate = TQDate::currentDate();
100 m_dateOnlyDuration.addDays(1);
101
102 m_runningAccount = 0;
103 m_startupAccount = 0;
104 m_shutdownAccount = 0;
105 m_startupCost = 0.0;
106 m_shutdownCost = 0.0;
107}
108
109Node *Node::projectNode() {
110 if ((type() == Type_Project) || (type() == Type_Subproject)) {
111 return this;
112 }
113 if (m_parent)
114 return m_parent->projectNode();
115
116 kdError()<<k_funcinfo<<"Ooops, no parent and no project found"<<endl;
117 return 0;
118}
119
120void Node::delChildNode( Node *node, bool remove) {
121 //kdDebug()<<k_funcinfo<<"find="<<m_nodes.findRef(node)<<endl;
122 if ( m_nodes.findRef(node) != -1 ) {
123 if(remove)
124 m_nodes.remove();
125 else
126 m_nodes.take();
127 }
128 node->setParent(0);
129}
130
131void Node::delChildNode( int number, bool remove) {
132 Node *n = m_nodes.at(number);
133 //kdDebug()<<k_funcinfo<<(n?n->id():"null")<<" : "<<(n?n->name():"")<<endl;
134 if(remove)
135 m_nodes.remove(number);
136 else
137 m_nodes.take(number);
138
139 if (n) {
140 n->setParent(0);
141 }
142}
143
144void Node::insertChildNode( unsigned int index, Node *node) {
145 //kdDebug()<<k_funcinfo<<"insert id="<<node->id()<<": "<<node->name()<<endl;
146 m_nodes.insert(index,node);
147 node->setParent(this);
148}
149
150void Node::addChildNode( Node *node, Node *after) {
151 //kdDebug()<<k_funcinfo<<endl;
152 int index = m_nodes.findRef(after);
153 if (index == -1) {
154 //kdDebug()<<k_funcinfo<<"append id="<<node->id()<<": "<<node->name()<<endl;
155 m_nodes.append(node);
156 node->setParent(this);
157 return;
158 }
159 //kdDebug()<<k_funcinfo<<"insert id="<<node->id()<<": "<<node->name()<<endl;
160 m_nodes.insert(index+1, node);
161 node->setParent(this);
162}
163
164int Node::findChildNode( Node* node )
165{
166 return m_nodes.findRef( node );
167}
168
169
170const Node* Node::getChildNode(int number) const {
171 // Work around missing const at() method in TQPtrList
172 const TQPtrList<Node> &nodes = m_nodes;
173 return (const_cast<TQPtrList<Node> &>(nodes)).at(number);
174}
175
176Duration *Node::getDelay() {
177 /* TODO
178 Calculate the delay of this node. Use the calculated startTime and the setted startTime.
179 */
180 return 0L;
181}
182
183void Node::addDependChildNode( Node *node, Relation::Type p) {
184 addDependChildNode(node,p,Duration());
185}
186
187void Node::addDependChildNode( Node *node, Relation::Type p, Duration lag) {
188 Relation *relation = new Relation(this, node, p, lag);
189 if (node->addDependParentNode(relation))
190 m_dependChildNodes.append(relation);
191 else
192 delete relation;
193}
194
195void Node::insertDependChildNode( unsigned int index, Node *node, Relation::Type p) {
196 Relation *relation = new Relation(this, node, p, Duration());
197 if (node->addDependParentNode(relation))
198 m_dependChildNodes.insert(index, relation);
199 else
200 delete relation;
201}
202
203bool Node::addDependChildNode( Relation *relation) {
204 if(m_dependChildNodes.findRef(relation) != -1)
205 return false;
206 m_dependChildNodes.append(relation);
207 return true;
208}
209
210// These delDepend... methods look suspicious to me, can someone review?
211void Node::delDependChildNode( Node *node, bool remove) {
212 if ( m_nodes.findRef(node) != -1 ) {
213 if(remove)
214 m_dependChildNodes.remove();
215 else
216 m_dependChildNodes.take();
217 }
218}
219
220void Node::delDependChildNode( Relation *rel, bool remove) {
221 if ( m_dependChildNodes.findRef(rel) != -1 ) {
222 if(remove)
223 m_dependChildNodes.remove();
224 else
225 m_dependChildNodes.take();
226 }
227}
228
229void Node::delDependChildNode( int number, bool remove) {
230 if(remove)
231 m_dependChildNodes.remove(number);
232 else
233 m_dependChildNodes.take(number);
234}
235
236void Node::takeDependChildNode(Relation *rel) {
237 if (m_dependChildNodes.findRef(rel) != -1) {
238 m_dependChildNodes.take();
239 }
240}
241
242void Node::addDependParentNode( Node *node, Relation::Type p) {
243 addDependParentNode(node,p,Duration());
244}
245
246void Node::addDependParentNode( Node *node, Relation::Type p, Duration lag) {
247 Relation *relation = new Relation(node, this, p, lag);
248 if (node->addDependChildNode(relation))
249 m_dependParentNodes.append(relation);
250 else
251 delete relation;
252}
253
254void Node::insertDependParentNode( unsigned int index, Node *node, Relation::Type p) {
255 Relation *relation = new Relation(this, node, p, Duration());
256 if (node->addDependChildNode(relation))
257 m_dependParentNodes.insert(index,relation);
258 else
259 delete relation;
260}
261
262bool Node::addDependParentNode( Relation *relation) {
263 if(m_dependParentNodes.findRef(relation) != -1)
264 return false;
265 m_dependParentNodes.append(relation);
266 return true;
267}
268
269// These delDepend... methods look suspicious to me, can someone review?
270void Node::delDependParentNode( Node *node, bool remove) {
271 if ( m_nodes.findRef(node) != -1 ) {
272 if(remove)
273 m_dependParentNodes.remove();
274 else
275 m_dependParentNodes.take();
276 }
277}
278
279void Node::delDependParentNode( Relation *rel, bool remove) {
280 if ( m_dependParentNodes.findRef(rel) != -1 ) {
281 if(remove)
282 m_dependParentNodes.remove();
283 else
284 m_dependParentNodes.take();
285 }
286}
287
288void Node::delDependParentNode( int number, bool remove) {
289 if(remove)
290 m_dependParentNodes.remove(number);
291 else
292 m_dependParentNodes.take(number);
293}
294
295void Node::takeDependParentNode(Relation *rel) {
296 if (m_dependParentNodes.findRef(rel) != -1) {
297 rel = m_dependParentNodes.take();
298 }
299}
300
301bool Node::isParentOf(Node *node) {
302 if (m_nodes.findRef(node) != -1)
303 return true;
304
305 TQPtrListIterator<Node> nit(childNodeIterator());
306 for ( ; nit.current(); ++nit ) {
307 if (nit.current()->isParentOf(node))
308 return true;
309 }
310 return false;
311}
312
313Relation *Node::findParentRelation(Node *node) {
314 for (int i=0; i<numDependParentNodes(); i++) {
315 Relation *rel = getDependParentNode(i);
316 if (rel->parent() == node)
317 return rel;
318 }
319 return (Relation *)0;
320}
321
322Relation *Node::findChildRelation(Node *node) {
323 for (int i=0; i<numDependChildNodes(); i++) {
324 Relation *rel = getDependChildNode(i);
325 if (rel->child() == node)
326 return rel;
327 }
328 return (Relation *)0;
329}
330
331Relation *Node::findRelation(Node *node) {
332 Relation *rel = findParentRelation(node);
333 if (!rel)
334 rel = findChildRelation(node);
335 return rel;
336}
337
338bool Node::isDependChildOf(Node *node) {
339 //kdDebug()<<k_funcinfo<<" '"<<m_name<<"' checking against '"<<node->name()<<"'"<<endl;
340 for (int i=0; i<numDependParentNodes(); i++) {
341 Relation *rel = getDependParentNode(i);
342 if (rel->parent() == node)
343 return true;
344 if (rel->parent()->isDependChildOf(node))
345 return true;
346 }
347 return false;
348}
349
350Duration Node::duration(const DateTime &time, int use, bool backward) {
351 //kdDebug()<<k_funcinfo<<endl;
352 // TODO: handle risc
353 if (!time.isValid()) {
354 kdError()<<k_funcinfo<<"Time is invalid"<<endl;
355 return Duration::zeroDuration;
356 }
357 if (m_effort == 0) {
358 kdError()<<k_funcinfo<<"m_effort == 0"<<endl;
359 return Duration::zeroDuration;
360 }
361 if (m_currentSchedule == 0) {
362 return Duration::zeroDuration;
363 kdError()<<k_funcinfo<<"No current schedule"<<endl;
364 }
365 kdDebug()<<k_funcinfo<<m_name<<": Use="<<use<<endl;
366 return calcDuration(time, m_effort->effort(use), backward);
367}
368
369void Node::makeAppointments() {
370 TQPtrListIterator<Node> nit(m_nodes);
371 for ( ; nit.current(); ++nit ) {
372 nit.current()->makeAppointments();
373 }
374}
375
376void Node::calcResourceOverbooked() {
377 TQPtrListIterator<Node> nit(m_nodes);
378 for ( ; nit.current(); ++nit ) {
379 nit.current()->calcResourceOverbooked();
380 }
381}
382
383TQStringList Node::overbookedResources() const {
384 return m_currentSchedule ? m_currentSchedule->overbookedResources() : TQStringList();
385}
386
387void Node::saveRelations(TQDomElement &element) const {
388 TQPtrListIterator<Relation> it(m_dependChildNodes);
389 for (; it.current(); ++it) {
390 it.current()->save(element);
391 }
392 TQPtrListIterator<Node> nodes(m_nodes);
393 for ( ; nodes.current(); ++nodes ) {
394 nodes.current()->saveRelations(element);
395 }
396}
397
398void Node::setConstraint(TQString &type) {
399 // Do not i18n these, they are used in load()
400 if (type == "ASAP")
401 setConstraint(ASAP);
402 else if (type == "ALAP")
403 setConstraint(ALAP);
404 else if (type == "StartNotEarlier")
405 setConstraint(StartNotEarlier);
406 else if (type == "FinishNotLater")
407 setConstraint(FinishNotLater);
408 else if (type == "MustStartOn")
409 setConstraint(MustStartOn);
410 else if (type == "MustFinishOn")
411 setConstraint(MustFinishOn);
412 else if (type == "FixedInterval")
413 setConstraint(FixedInterval);
414 else
415 setConstraint(ASAP); // default
416}
417
418TQString Node::constraintToString() const {
419 // Do not i18n these, they are used in save()
420 if (m_constraint == ASAP)
421 return TQString("ASAP");
422 else if (m_constraint == ALAP)
423 return TQString("ALAP");
424 else if (m_constraint == StartNotEarlier)
425 return TQString("StartNotEarlier");
426 else if (m_constraint == FinishNotLater)
427 return TQString("FinishNotLater");
428 else if (m_constraint == MustStartOn)
429 return TQString("MustStartOn");
430 else if (m_constraint == MustFinishOn)
431 return TQString("MustFinishOn");
432 else if (m_constraint == FixedInterval)
433 return TQString("FixedInterval");
434
435 return TQString();
436}
437
438void Node::propagateEarliestStart(DateTime &time) {
439 if (m_currentSchedule == 0)
440 return;
441 m_currentSchedule->earliestStart = time;
442 //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->earliestStart.toString()<<endl;
443 TQPtrListIterator<Node> it = m_nodes;
444 for (; it.current(); ++it) {
445 it.current()->propagateEarliestStart(time);
446 }
447}
448
449void Node::propagateLatestFinish(DateTime &time) {
450 if (m_currentSchedule == 0)
451 return;
452 m_currentSchedule->latestFinish = time;
453 //kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->latestFinish<<endl;
454 TQPtrListIterator<Node> it = m_nodes;
455 for (; it.current(); ++it) {
456 it.current()->propagateLatestFinish(time);
457 }
458}
459
460void Node::moveEarliestStart(DateTime &time) {
461 if (m_currentSchedule == 0)
462 return;
463 if (m_currentSchedule->earliestStart < time)
464 m_currentSchedule->earliestStart = time;
465 TQPtrListIterator<Node> it = m_nodes;
466 for (; it.current(); ++it) {
467 it.current()->moveEarliestStart(time);
468 }
469}
470
471void Node::moveLatestFinish(DateTime &time) {
472 if (m_currentSchedule == 0)
473 return;
474 if (m_currentSchedule->latestFinish > time)
475 m_currentSchedule->latestFinish = time;
476 TQPtrListIterator<Node> it = m_nodes;
477 for (; it.current(); ++it) {
478 it.current()->moveLatestFinish(time);
479 }
480}
481
482void Node::initiateCalculation(Schedule &sch) {
483 TQPtrListIterator<Node> it = m_nodes;
484 for (; it.current(); ++it) {
485 it.current()->initiateCalculation(sch);
486 }
487}
488
489void Node::resetVisited() {
490 m_visitedForward = false;
491 m_visitedBackward = false;
492 TQPtrListIterator<Node> it = m_nodes;
493 for (; it.current(); ++it) {
494 it.current()->resetVisited();
495 }
496}
497
498Node *Node::siblingBefore() {
499 //kdDebug()<<k_funcinfo<<endl;
500 if (getParent())
501 return getParent()->childBefore(this);
502 return 0;
503}
504
505Node *Node::childBefore(Node *node) {
506 //kdDebug()<<k_funcinfo<<endl;
507 int index = m_nodes.findRef(node);
508 if (index > 0){
509 return m_nodes.at(index-1);
510 }
511 return 0;
512}
513
514Node *Node::siblingAfter() {
515 //kdDebug()<<k_funcinfo<<endl;
516 if (getParent())
517 return getParent()->childAfter(this);
518 return 0;
519}
520
521Node *Node::childAfter(Node *node)
522{
523 //kdDebug()<<k_funcinfo<<endl;
524 uint index = m_nodes.findRef(node);
525 if (index < m_nodes.count()-1) {
526 return m_nodes.at(index+1); }
527 return 0;
528}
529
530bool Node::moveChildUp(Node* node)
531{
532 if (findChildNode(node) == -1)
533 return false; // not my node!
534 Node *sib = node->siblingBefore();
535 if (!sib)
536 return false;
537 sib = sib->siblingBefore();
538 delChildNode(node, false);
539 if (sib) {
540 addChildNode(node, sib);
541 } else {
542 insertChildNode(0, node);
543 }
544 return true;
545}
546
547bool Node::moveChildDown(Node* node)
548{
549 if (findChildNode(node) == -1)
550 return false; // not my node!
551 Node *sib = node->siblingAfter();
552 if (!sib)
553 return false;
554 delChildNode(node, false);
555 addChildNode(node, sib);
556 return true;
557}
558
559bool Node::legalToLink(Node *node) {
560 Node *p = projectNode();
561 if (p)
562 return p->legalToLink(this, node);
563 return false;
564}
565
566bool Node::isEndNode() const {
567 return m_dependChildNodes.isEmpty();
568}
569bool Node::isStartNode() const {
570 return m_dependParentNodes.isEmpty();
571}
572
573bool Node::setId(TQString id) {
574 //kdDebug()<<k_funcinfo<<id<<endl;
575 if (id.isEmpty()) {
576 kdError()<<k_funcinfo<<"id is empty"<<endl;
577 m_id = id;
578 return false;
579 }
580 if (!m_id.isEmpty()) {
581 Node *n = findNode();
582 if (n == this) {
583 //kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl;
584 removeId();
585 } else if (n) {
586 //Hmmm, shouldn't happen
587 kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different node: "<<n->name()<<endl;
588 }
589 }
590 if (findNode(id)) {
591 kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different node: "<<findNode(id)->name()<<endl;
592 m_id = TQString(); // hmmm
593 return false;
594 }
595 m_id = id;
596 insertId(id);
597 //kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl;
598 return true;
599}
600
601void Node::setStartTime(DateTime startTime) {
602 if (m_currentSchedule)
603 m_currentSchedule->startTime = startTime;
604 m_dateOnlyStartDate = startTime.date();
605}
606
607void Node::setEndTime(DateTime endTime) {
608 if (m_currentSchedule)
609 m_currentSchedule->endTime = endTime;
610
611 m_dateOnlyEndDate = endTime.date();
612 if (endTime.time().isNull() && m_dateOnlyEndDate > m_dateOnlyStartDate)
613 m_dateOnlyEndDate = m_dateOnlyEndDate.addDays(-1);
614}
615
616void Node::saveAppointments(TQDomElement &element, long id) const {
617 //kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
618 TQPtrListIterator<Node> it(m_nodes);
619 for (; it.current(); ++it ) {
620 it.current()->saveAppointments(element, id);
621 }
622}
623
624TQPtrList<Appointment> Node::appointments() {
625 TQPtrList<Appointment> lst;
626 if (m_currentSchedule)
627 lst = m_currentSchedule->appointments();
628 return lst;
629}
630
631// Appointment *Node::findAppointment(Resource *resource) {
632// if (m_currentSchedule)
633// return m_currentSchedule->findAppointment(resource);
634// return 0;
635// }
636bool Node::addAppointment(Appointment *appointment) {
637 if (m_currentSchedule)
638 return m_currentSchedule->add(appointment);
639 return false;
640}
641
642bool Node::addAppointment(Appointment *appointment, Schedule &main) {
643 //kdDebug()<<k_funcinfo<<this<<endl;
644 Schedule *s = findSchedule(main.id());
645 if (s == 0) {
646 s = createSchedule(&main);
647 }
648 appointment->setNode(s);
649 return s->add(appointment);
650}
651
652void Node::addAppointment(ResourceSchedule *resource, DateTime &start, DateTime &end, double load) {
653 Schedule *node = findSchedule(resource->id());
654 if (node == 0) {
655 node = createSchedule(resource->parent());
656 }
657 node->addAppointment(resource, start, end, load);
658}
659
660void Node::takeSchedule(const Schedule *schedule) {
661 if (schedule == 0)
662 return;
663 if (m_currentSchedule == schedule)
664 m_currentSchedule = 0;
665 m_schedules.take(schedule->id());
666}
667
668void Node::addSchedule(Schedule *schedule) {
669 if (schedule == 0)
670 return;
671 m_schedules.replace(schedule->id(), schedule);
672}
673
674Schedule *Node::createSchedule(TQString name, Schedule::Type type, long id) {
675 //kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl;
676 NodeSchedule *sch = new NodeSchedule(this, name, type, id);
677 addSchedule(sch);
678 return sch;
679}
680
681Schedule *Node::createSchedule(Schedule *parent) {
682 //kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl;
683 NodeSchedule *sch = new NodeSchedule(parent, this);
684 addSchedule(sch);
685 return sch;
686}
687
688Schedule *Node::findSchedule(const TQString name, const Schedule::Type type) const {
689 TQIntDictIterator<Schedule> it = m_schedules;
690 for (; it.current(); ++it) {
691 if (!it.current()->isDeleted() &&
692 it.current()->name() == name && it.current()->type() == type)
693 return it.current();
694 }
695 return 0;
696}
697
698Schedule *Node::findSchedule(const Schedule::Type type) const {
699 //kdDebug()<<k_funcinfo<<m_name<<" find type="<<type<<" nr="<<m_schedules.count()<<endl;
700 TQIntDictIterator<Schedule> it = m_schedules;
701 for (; it.current(); ++it) {
702 if (!it.current()->isDeleted() && it.current()->type() == type) {
703 return it.current();
704 }
705 }
706 return 0;
707}
708
709void Node::setScheduleDeleted(long id, bool on) {
710 Schedule *ns = findSchedule(id);
711 if (ns == 0) {
712 kdError()<<k_funcinfo<<m_name<<" Could not find schedule with id="<<id<<endl;
713 } else {
714 ns->setDeleted(on);
715 }
716}
717
718void Node::setParentSchedule(Schedule *sch) {
719 Schedule *s = findSchedule(sch->id());
720 if (s) {
721 s->setParent(sch);
722 }
723 TQPtrListIterator<Node> it = m_nodes;
724 for (; it.current(); ++it) {
725 it.current()->setParentSchedule(sch);
726 }
727}
728
729bool Node::calcCriticalPath(bool fromEnd) {
730 if (m_currentSchedule == 0)
731 return false;
732 //kdDebug()<<k_funcinfo<<m_name<<endl;
733 if (!isCritical()) {
734 return false;
735 }
736 if (!fromEnd && isStartNode()) {
737 m_currentSchedule->inCriticalPath = true;
738 return true;
739 }
740 if (fromEnd && isEndNode()) {
741 m_currentSchedule->inCriticalPath = true;
742 return true;
743 }
744 TQPtrListIterator<Relation> pit(m_dependParentNodes);
745 for (; pit.current(); ++pit) {
746 if (pit.current()->parent()->calcCriticalPath(fromEnd)) {
747 m_currentSchedule->inCriticalPath = true;
748 }
749 }
750 return m_currentSchedule->inCriticalPath;
751}
752
753int Node::level() {
754 Node *n = getParent();
755 return n ? n->level() + 1 : 0;
756}
757
758void Node::generateWBS(int count, WBSDefinition &def, TQString wbs) {
759 m_wbs = wbs + def.code(count, level());
760 //kdDebug()<<k_funcinfo<<m_name<<" wbs: "<<m_wbs<<endl;
761 TQString w = wbs + def.wbs(count, level());
762 TQPtrListIterator<Node> it = m_nodes;
763 for (int i=0; it.current(); ++it) {
764 it.current()->generateWBS(++i, def, w);
765 }
766
767}
768
769void Node::setCurrentSchedule(long id) {
770 TQPtrListIterator<Node> it = m_nodes;
771 for (; it.current(); ++it) {
772 it.current()->setCurrentSchedule(id);
773 }
774 //kdDebug()<<k_funcinfo<<m_name<<" id: "<<id<<"="<<m_currentSchedule<<endl;
775}
777
778Effort::Effort( Duration e, Duration p, Duration o) {
779 m_expectedEffort = e;
780 m_pessimisticEffort = p;
781 m_optimisticEffort = o;
782 m_type = Type_Effort;
783 m_risktype = Risk_None;
784}
785
786Effort::Effort(const Effort &effort) {
787 set(effort.expected(), effort.pessimistic(), effort.optimistic());
788 setType(effort.type());
789 setRisktype(effort.risktype());
790}
791
792Effort::~Effort() {
793}
794
795const Effort Effort::zeroEffort( Duration::zeroDuration,
796 Duration::zeroDuration,
797 Duration::zeroDuration );
798
799void Effort::set( Duration e, Duration p, Duration o ) {
800 m_expectedEffort = e;
801 m_pessimisticEffort = (p == Duration::zeroDuration) ? e : p;
802 m_optimisticEffort = (o == Duration::zeroDuration) ? e : o;
803 //kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.toString()<<endl;
804}
805
806void Effort::set( int e, int p, int o ) {
807 m_expectedEffort = Duration(e);
808 m_pessimisticEffort = (p < 0) ? Duration(e) : Duration(p);
809 m_optimisticEffort = (o < 0) ? Duration(e) : Duration(o);
810 //kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.toString()<<endl;
811 //kdDebug()<<k_funcinfo<<" Optimistic: "<<m_optimisticEffort.toString()<<endl;
812 //kdDebug()<<k_funcinfo<<" Pessimistic: "<<m_pessimisticEffort.toString()<<endl;
813
814 //kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.duration()<<" manseconds"<<endl;
815}
816
817//TODO (?): effort is not really a duration, should maybe not use Duration for storage
818void Effort::set(unsigned days, unsigned hours, unsigned minutes) {
819 Duration dur(days, hours, minutes);
820 set(dur);
821 //kdDebug()<<k_funcinfo<<"effort="<<dur.toString()<<endl;
822}
823
824void Effort::expectedEffort(unsigned *days, unsigned *hours, unsigned *minutes) {
825 m_expectedEffort.get(days, hours, minutes);
826}
827
828Duration Effort::variance() const {
829 return (m_pessimisticEffort - m_optimisticEffort)/6;
830}
831Duration Effort::pertExpected() const {
832 if (m_risktype == Risk_Low) {
833 return (m_optimisticEffort + m_pessimisticEffort + (m_expectedEffort*4))/6;
834 } else if (m_risktype == Risk_High) {
835 return (m_optimisticEffort + (m_pessimisticEffort*2) + (m_expectedEffort*4))/7;
836 }
837 return m_expectedEffort; // risk==none
838}
839Duration Effort::pertOptimistic() const {
840 if (m_risktype != Risk_None) {
841 return pertExpected() - variance();
842 }
843 return m_optimisticEffort;
844}
845Duration Effort::pertPessimistic() const {
846 if (m_risktype != Risk_None) {
847 return pertExpected() + variance();
848 }
849 return m_pessimisticEffort;
850}
851
852Duration Effort::effort(int use) const {
853 if (use == Effort::Use_Expected) {
854 return pertExpected();
855 } else if (use == Effort::Use_Optimistic) {
856 return pertOptimistic();
857 } else if (use == Effort::Use_Pessimistic)
858 return pertPessimistic();
859
860 return m_expectedEffort; // default
861}
862
863bool Effort::load(TQDomElement &element) {
864 m_expectedEffort = Duration::fromString(element.attribute("expected"));
865 m_optimisticEffort = Duration::fromString(element.attribute("optimistic"));
866 m_pessimisticEffort = Duration::fromString(element.attribute("pessimistic"));
867 setType(element.attribute("type", "WorkBased"));
868 setRisktype(element.attribute("risk"));
869 return true;
870}
871
872void Effort::save(TQDomElement &element) const {
873 TQDomElement me = element.ownerDocument().createElement("effort");
874 element.appendChild(me);
875 me.setAttribute("expected", m_expectedEffort.toString());
876 me.setAttribute("optimistic", m_optimisticEffort.toString());
877 me.setAttribute("pessimistic", m_pessimisticEffort.toString());
878 me.setAttribute("type", typeToString());
879 me.setAttribute("risk", risktypeToString());
880}
881
882TQString Effort::typeToString() const {
883 if (m_type == Type_Effort)
884 return TQString("Effort");
885 if (m_type == Type_FixedDuration)
886 return TQString("Type_FixedDuration");
887
888 return TQString();
889}
890
891void Effort::setType(TQString type) {
892 if (type == "Effort")
893 setType(Type_Effort);
894 else if (type == "Type_FixedDuration")
895 setType(Type_FixedDuration);
896 else
897 setType(Type_Effort); // default
898}
899
900TQString Effort::risktypeToString() const {
901 if (m_risktype == Risk_None)
902 return TQString("None");
903 if (m_risktype == Risk_Low)
904 return TQString("Low");
905 if (m_risktype == Risk_High)
906 return TQString("High");
907
908 return TQString();
909}
910
911void Effort::setRisktype(TQString type) {
912 if (type == "High")
913 setRisktype(Risk_High);
914 else if (type == "Low")
915 setRisktype(Risk_Low);
916 else
917 setRisktype(Risk_None); // default
918}
919
920void Effort::setOptimisticRatio(int percent)
921{
922 int p = percent>0 ? -percent : percent;
923 m_optimisticEffort = m_expectedEffort*(100+p)/100;
924}
925
926int Effort::optimisticRatio() const {
927 if (m_expectedEffort == Duration::zeroDuration)
928 return 0;
929 return (m_optimisticEffort.milliseconds()*100/m_expectedEffort.milliseconds())-100;
930}
931
932void Effort::setPessimisticRatio(int percent)
933{
934 int p = percent<0 ? -percent : percent;
935 m_pessimisticEffort = m_expectedEffort*(100+p)/100;
936}
937int Effort::pessimisticRatio() const {
938 if (m_expectedEffort == Duration::zeroDuration)
939 return 0;
940 return m_pessimisticEffort.milliseconds()*100/m_expectedEffort.milliseconds()-100;
941}
942
943// Debugging
944#ifndef NDEBUG
945void Node::printDebug(bool children, TQCString indent) {
946 kdDebug()<<indent<<" Unique node identity="<<m_id<<endl;
947 if (m_effort) m_effort->printDebug(indent);
948 TQString s = " Constraint: " + constraintToString();
949 if (m_constraint == MustStartOn || m_constraint == StartNotEarlier || m_constraint == FixedInterval)
950 kdDebug()<<indent<<s<<" ("<<constraintStartTime().toString()<<")"<<endl;
951 if (m_constraint == MustFinishOn || m_constraint == FinishNotLater || m_constraint == FixedInterval)
952 kdDebug()<<indent<<s<<" ("<<constraintEndTime().toString()<<")"<<endl;
953 Schedule *cs = m_currentSchedule;
954 if (cs) {
955 kdDebug()<<indent<<" Current schedule: "<<"id="<<cs->id()<<" '"<<cs->name()<<"' type: "<<cs->type()<<endl;
956 } else {
957 kdDebug()<<indent<<" Current schedule: None"<<endl;
958 }
959 TQIntDictIterator<Schedule> it = m_schedules;
960 for (; it.current(); ++it) {
961 it.current()->printDebug(indent+" ");
962 }
963 kdDebug()<<indent<<" Parent: "<<(m_parent ? m_parent->name() : TQString("None"))<<endl;
964 kdDebug()<<indent<<" Level: "<<level()<<endl;
965 kdDebug()<<indent<<" No of predecessors: "<<m_dependParentNodes.count()<<endl;
966 TQPtrListIterator<Relation> pit(m_dependParentNodes);
967 //kdDebug()<<indent<<" Dependant parents="<<pit.count()<<endl;
968 if (pit.count() > 0) {
969 for ( ; pit.current(); ++pit ) {
970 pit.current()->printDebug(indent);
971 }
972 }
973 kdDebug()<<indent<<" No of successors: "<<m_dependChildNodes.count()<<endl;
974 TQPtrListIterator<Relation> cit(m_dependChildNodes);
975 //kdDebug()<<indent<<" Dependant children="<<cit.count()<<endl;
976 if (cit.count() > 0) {
977 for ( ; cit.current(); ++cit ) {
978 cit.current()->printDebug(indent);
979 }
980 }
981
982 //kdDebug()<<indent<<endl;
983 indent += " ";
984 if (children) {
985 TQPtrListIterator<Node> it(m_nodes);
986 for ( ; it.current(); ++it ) {
987 it.current()->printDebug(true,indent);
988 }
989 }
990
991}
992#endif
993
994
995#ifndef NDEBUG
996void Effort::printDebug(TQCString indent) {
997 kdDebug()<<indent<<" Effort:"<<endl;
998 indent += " ";
999 kdDebug()<<indent<<" Expected: "<<m_expectedEffort.toString()<<endl;
1000 kdDebug()<<indent<<" Optimistic: "<<m_optimisticEffort.toString()<<endl;
1001 kdDebug()<<indent<<" Pessimistic: "<<m_pessimisticEffort.toString()<<endl;
1002
1003 kdDebug()<<indent<<" Risk: "<<risktypeToString()<<endl;
1004 kdDebug()<<indent<<" Pert expected: "<<pertExpected().toString()<<endl;
1005 kdDebug()<<indent<<" Pert optimistic: "<<pertOptimistic().toString()<<endl;
1006 kdDebug()<<indent<<" Pert pessimistic: "<<pertPessimistic().toString()<<endl;
1007 kdDebug()<<indent<<" Pert variance: "<<variance().toString()<<endl;
1008}
1009#endif
1010
1011} //KPlato namespace
A resource (Resource) can be scheduled to be used at any time, this is represented internally with Ap...
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
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
virtual void addDependChildNode(Node *node, Relation::Type p=Relation::FinishStart)
Adds relation to both this node and address node.
Definition kptnode.cpp:183
virtual int level()
Returns the level this node is in the hierarchy. Top node is level 0.
Definition kptnode.cpp:753
bool legalToLink(Node *node)
Check if this node can be linked to node.
Definition kptnode.cpp:559
virtual Node * projectNode()
Returns a pointer to the project node (main- or sub-project) Returns 0 if no project exists.
Definition kptnode.cpp:109
virtual void addDependParentNode(Node *node, Relation::Type p=Relation::FinishStart)
Adds relation to both this node and node.
Definition kptnode.cpp:242
The relation class couples a 2 nodes together which are dependent on each other.
Definition kptrelation.h:45
ResourceSchedule holds scheduling information for a resource.
The Schedule class holds data calculated during project calculation and scheduling,...
Definition kptschedule.h:55
virtual bool add(Appointment *appointment)
Adds appointment to this schedule only.
virtual void addAppointment(Schedule *, DateTime &, DateTime &, double=100)
Adds appointment to both this resource schedule and node schedule.
Definition kptschedule.h:94