1 /* This file is part of the KDE project
2  * Copyright (C) 2005 - 2011, 2012 Dag Andersen <danders@get2net.dk>
3  * Copyright (C) 2016 Dag Andersen <danders@get2net.dk>
4  * Copyright (C) 2019 Dag Andersen <danders@get2net.dk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 // clazy:excludeall=qstring-arg
23 #include "kptschedule.h"
24 
25 #include "kptappointment.h"
26 #include "kptdatetime.h"
27 #include "kptduration.h"
28 #include "kptnode.h"
29 #include "kptproject.h"
30 #include "kpttask.h"
31 #include "kptxmlloaderobject.h"
32 #include "kptschedulerplugin.h"
33 #include "kptdebug.h"
34 
35 #include <KoXmlReader.h>
36 
37 #include <KLocalizedString>
38 
39 #include <QStringList>
40 
41 
42 namespace KPlato
43 {
44 
45 class ScheduleManager;
46 
Log(const Node * n,int sev,const QString & msg,int ph)47 Schedule::Log::Log(const Node *n, int sev, const QString &msg, int ph)
48     : node(n), resource(0), message(msg), severity(sev), phase(ph)
49 {
50     Q_ASSERT(n);
51 //     debugPlan<<*this<<nodeId;
52 }
53 
Log(const Node * n,const Resource * r,int sev,const QString & msg,int ph)54 Schedule::Log::Log(const Node *n, const Resource *r, int sev, const QString &msg, int ph)
55     : node(n), resource(r), message(msg), severity(sev), phase(ph)
56 {
57     Q_ASSERT(r);
58 //     debugPlan<<*this<<resourceId;
59 }
60 
Log(const Log & other)61 Schedule::Log::Log(const Log &other)
62 {
63     node = other.node;
64     resource = other.resource;
65     message = other.message;
66     severity = other.severity;
67     phase = other.phase;
68 }
69 
operator =(const Schedule::Log & other)70 Schedule::Log &Schedule::Log::operator=(const Schedule::Log &other)
71 {
72     node = other.node;
73     resource = other.resource;
74     message = other.message;
75     severity = other.severity;
76     phase = other.phase;
77     return *this;
78 }
79 
Schedule()80 Schedule::Schedule()
81         : m_type(Expected),
82         m_id(0),
83         m_deleted(false),
84         m_parent(0),
85         m_obstate(OBS_Parent),
86         m_calculationMode(Schedule::Scheduling),
87         notScheduled(true)
88 {
89     initiateCalculation();
90 }
91 
Schedule(Schedule * parent)92 Schedule::Schedule(Schedule *parent)
93         : m_type(Expected),
94         m_id(0),
95         m_deleted(false),
96         m_parent(parent),
97         m_obstate(OBS_Parent),
98         m_calculationMode(Schedule::Scheduling),
99         notScheduled(true)
100 {
101 
102     if (parent) {
103         m_name = parent->name();
104         m_type = parent->type();
105         m_id = parent->id();
106     }
107     initiateCalculation();
108     //debugPlan<<"("<<this<<") Name: '"<<name<<"' Type="<<type<<" id="<<id;
109 }
110 
Schedule(const QString & name,Type type,long id)111 Schedule::Schedule(const QString& name, Type type, long id)
112         : m_name(name),
113         m_type(type),
114         m_id(id),
115         m_deleted(false),
116         m_parent(0),
117         m_obstate(OBS_Parent),
118         m_calculationMode(Schedule::Scheduling),
119         notScheduled(true)
120 {
121     //debugPlan<<"("<<this<<") Name: '"<<name<<"' Type="<<type<<" id="<<id;
122     initiateCalculation();
123 }
124 
~Schedule()125 Schedule::~Schedule()
126 {
127 }
128 
setParent(Schedule * parent)129 void Schedule::setParent(Schedule *parent)
130 {
131     m_parent = parent;
132 }
133 
setDeleted(bool on)134 void Schedule::setDeleted(bool on)
135 {
136     //debugPlan<<"deleted="<<on;
137     m_deleted = on;
138     //changed(this); don't do this!
139 }
140 
isDeleted() const141 bool Schedule::isDeleted() const
142 {
143     return m_parent == 0 ? m_deleted : m_parent->isDeleted();
144 }
145 
setType(const QString & type)146 void Schedule::setType(const QString& type)
147 {
148     m_type = Expected;
149     if (type == "Expected")
150         m_type = Expected;
151 }
152 
typeToString(bool translate) const153 QString Schedule::typeToString(bool translate) const
154 {
155     if (translate) {
156         return i18n("Expected");
157     } else {
158         return "Expected";
159     }
160 }
161 
state() const162 QStringList Schedule::state() const
163 {
164     QStringList lst;
165     if (m_deleted)
166         lst << SchedulingState::deleted();
167     if (notScheduled)
168         lst << SchedulingState::notScheduled();
169     if (constraintError)
170         lst << SchedulingState::constraintsNotMet();
171     if (resourceError)
172         lst << SchedulingState::resourceNotAllocated();
173     if (resourceNotAvailable)
174         lst << SchedulingState::resourceNotAvailable();
175     if (resourceOverbooked)
176         lst << SchedulingState::resourceOverbooked();
177     if (effortNotMet)
178         lst << SchedulingState::effortNotMet();
179     if (schedulingError)
180         lst << SchedulingState::schedulingError();
181     if (lst.isEmpty())
182         lst << SchedulingState::scheduled();
183     return lst;
184 }
185 
isBaselined() const186 bool Schedule::isBaselined() const
187 {
188     if (m_parent) {
189         return m_parent->isBaselined();
190     }
191     return false;
192 }
193 
usePert() const194 bool Schedule::usePert() const
195 {
196     if (m_parent) {
197         return m_parent->usePert();
198     }
199     return false;
200 }
201 
setAllowOverbookingState(Schedule::OBState state)202 void Schedule::setAllowOverbookingState(Schedule::OBState state)
203 {
204     m_obstate = state;
205 }
206 
allowOverbookingState() const207 Schedule::OBState Schedule::allowOverbookingState() const
208 {
209     return m_obstate;
210 }
211 
allowOverbooking() const212 bool Schedule::allowOverbooking() const
213 {
214     if (m_obstate == OBS_Parent && m_parent) {
215         return m_parent->allowOverbooking();
216     }
217     return m_obstate == OBS_Allow;
218 }
219 
checkExternalAppointments() const220 bool Schedule::checkExternalAppointments() const
221 {
222     if (m_parent) {
223         return m_parent->checkExternalAppointments();
224     }
225     return false;
226 }
227 
setScheduled(bool on)228 void Schedule::setScheduled(bool on)
229 {
230     notScheduled = !on;
231     changed(this);
232 }
233 
effort(const DateTimeInterval & interval) const234 Duration Schedule::effort(const DateTimeInterval &interval) const
235 {
236     return interval.second - interval.first;
237 }
238 
available(const DateTimeInterval & interval) const239 DateTimeInterval Schedule::available(const DateTimeInterval &interval) const
240 {
241     return DateTimeInterval(interval.first, interval.second);
242 }
243 
initiateCalculation()244 void Schedule::initiateCalculation()
245 {
246     resourceError = false;
247     resourceOverbooked = false;
248     resourceNotAvailable = false;
249     constraintError = false;
250     schedulingError = false;
251     inCriticalPath = false;
252     effortNotMet = false;
253     workStartTime = DateTime();
254     workEndTime = DateTime();
255 }
256 
calcResourceOverbooked()257 void Schedule::calcResourceOverbooked()
258 {
259     resourceOverbooked = false;
260     foreach(Appointment *a, m_appointments) {
261         if (a->resource() ->isOverbooked(a->startTime(), a->endTime())) {
262             resourceOverbooked = true;
263             break;
264         }
265     }
266 }
267 
firstBookedInterval(const DateTimeInterval & interval,const Schedule * node) const268 DateTimeInterval Schedule::firstBookedInterval(const DateTimeInterval &interval, const Schedule *node) const
269 {
270     QList<Appointment*> lst = m_appointments;
271     switch (m_calculationMode) {
272         case CalculateForward: lst = m_forward; break;
273         case CalculateBackward: lst = m_backward; break;
274         default: break;
275     }
276     foreach (Appointment *a, lst) {
277         if (a->node() == node) {
278             AppointmentIntervalList i = a->intervals(interval.first, interval.second);
279             if (i.isEmpty()) {
280                 break;
281             }
282             return DateTimeInterval(i.map().values().first().startTime(), i.map().values().first().endTime());
283         }
284     }
285     return DateTimeInterval();
286 }
287 
overbookedResources() const288 QStringList Schedule::overbookedResources() const
289 {
290     QStringList rl;
291     foreach(Appointment *a, m_appointments) {
292         if (a->resource() ->isOverbooked(a->startTime(), a->endTime())) {
293             rl += a->resource() ->resource() ->name();
294         }
295     }
296     return rl;
297 }
298 
resourceNameList() const299 QStringList Schedule::resourceNameList() const
300 {
301     return QStringList();
302 }
303 
resources() const304 QList<Resource*> Schedule::resources() const
305 {
306     return QList<Resource*>();
307 }
308 
loadXML(const KoXmlElement & sch,XMLLoaderObject &)309 bool Schedule::loadXML(const KoXmlElement &sch, XMLLoaderObject &)
310 {
311     m_name = sch.attribute("name");
312     setType(sch.attribute("type"));
313     m_id = sch.attribute("id").toLong();
314 
315     return true;
316 }
317 
saveXML(QDomElement & element) const318 void Schedule::saveXML(QDomElement &element) const
319 {
320     QDomElement sch = element.ownerDocument().createElement("schedule");
321     element.appendChild(sch);
322     saveCommonXML(sch);
323 }
324 
saveCommonXML(QDomElement & element) const325 void Schedule::saveCommonXML(QDomElement &element) const
326 {
327     //debugPlan<<m_name<<" save schedule";
328     element.setAttribute("name", m_name);
329     element.setAttribute("type", typeToString());
330     element.setAttribute("id", QString::number(qlonglong(m_id)));
331 }
332 
saveAppointments(QDomElement & element) const333 void Schedule::saveAppointments(QDomElement &element) const
334 {
335     //debugPlan;
336     QListIterator<Appointment*> it = m_appointments;
337     while (it.hasNext()) {
338         it.next() ->saveXML(element);
339     }
340 }
341 
insertForwardNode(Node * node)342 void Schedule::insertForwardNode(Node *node)
343 {
344     if (m_parent) {
345         m_parent->insertForwardNode(node);
346     }
347 }
348 
insertBackwardNode(Node * node)349 void Schedule::insertBackwardNode(Node *node)
350 {
351     if (m_parent) {
352         m_parent->insertBackwardNode(node);
353     }
354 }
355 
356 // used (directly) when appointment wants to attach itself again
attach(Appointment * appointment)357 bool Schedule::attach(Appointment *appointment)
358 {
359     int mode = appointment->calculationMode();
360     //debugPlan<<appointment<<mode;
361     if (mode == Scheduling) {
362         if (m_appointments.indexOf(appointment) != -1) {
363             errorPlan << "Appointment already exists" << endl;
364             return false;
365         }
366         m_appointments.append(appointment);
367         //if (resource()) debugPlan<<appointment<<" For resource '"<<resource()->name()<<"'"<<" count="<<m_appointments.count();
368         //if (node()) debugPlan<<"("<<this<<")"<<appointment<<" For node '"<<node()->name()<<"'"<<" count="<<m_appointments.count();
369         return true;
370     }
371     if (mode == CalculateForward) {
372         if (m_forward.indexOf(appointment) != -1) {
373             errorPlan << "Appointment already exists" << endl;
374             return false;
375         }
376         m_forward.append(appointment);
377         //if (resource()) debugPlan<<"For resource '"<<resource()->name()<<"'";
378         //if (node()) debugPlan<<"For node '"<<node()->name()<<"'";
379         return true;
380     }
381     if (mode == CalculateBackward) {
382         if (m_backward.indexOf(appointment) != -1) {
383             errorPlan << "Appointment already exists" << endl;
384             return false;
385         }
386         m_backward.append(appointment);
387         //if (resource()) debugPlan<<"For resource '"<<resource()->name()<<"'";
388         //if (node()) debugPlan<<"For node '"<<node()->name()<<"'";
389         return true;
390     }
391     errorPlan<<"Unknown mode: "<<m_calculationMode<<endl;
392     return false;
393 }
394 
395 // used to add new schedules
add(Appointment * appointment)396 bool Schedule::add(Appointment *appointment)
397 {
398     //debugPlan<<this;
399     appointment->setCalculationMode(m_calculationMode);
400     return attach(appointment);
401 }
402 
takeAppointment(Appointment * appointment,int mode)403 void Schedule::takeAppointment(Appointment *appointment, int mode)
404 {
405     Q_UNUSED(mode);
406     //debugPlan<<"("<<this<<")"<<mode<<":"<<appointment<<","<<appointment->calculationMode();
407     int i = m_forward.indexOf(appointment);
408     if (i != -1) {
409         m_forward.removeAt(i);
410         Q_ASSERT(mode == CalculateForward);
411     }
412     i = m_backward.indexOf(appointment);
413     if (i != -1) {
414         m_backward.removeAt(i);
415         Q_ASSERT(mode == CalculateBackward);
416     }
417     i = m_appointments.indexOf(appointment);
418     if (i != -1) {
419         m_appointments.removeAt(i);
420         Q_ASSERT(mode == Scheduling);
421     }
422 }
423 
findAppointment(Schedule * resource,Schedule * node,int mode)424 Appointment *Schedule::findAppointment(Schedule *resource, Schedule *node, int mode)
425 {
426     //debugPlan<<this<<" ("<<resourceError<<","<<node<<")"<<mode;
427     if (mode == Scheduling) {
428         foreach(Appointment *a,  m_appointments) {
429             if (a->node() == node && a->resource() == resource) {
430                 return a;
431             }
432         }
433         return 0;
434     } else if (mode == CalculateForward) {
435         foreach(Appointment *a,  m_forward) {
436             if (a->node() == node && a->resource() == resource) {
437                 return a;
438             }
439         }
440     } else if (mode == CalculateBackward) {
441         foreach(Appointment *a,  m_backward) {
442             if (a->node() == node && a->resource() == resource) {
443                 Q_ASSERT(mode == CalculateBackward);
444                 return a;
445             }
446         }
447     } else {
448         Q_ASSERT(false); // unknown mode
449     }
450     return 0;
451 }
452 
appointmentStartTime() const453 DateTime Schedule::appointmentStartTime() const
454 {
455     DateTime dt;
456     foreach (const Appointment *a, m_appointments) {
457         if (! dt.isValid() || dt > a->startTime()) {
458             dt = a->startTime();
459         }
460     }
461     return dt;
462 }
appointmentEndTime() const463 DateTime Schedule::appointmentEndTime() const
464 {
465     DateTime dt;
466     foreach (const Appointment *a, m_appointments) {
467         if (! dt.isValid() || dt > a->endTime()) {
468             dt = a->endTime();
469         }
470     }
471     return dt;
472 }
473 
hasAppointments(int which) const474 bool Schedule::hasAppointments(int which) const
475 {
476     if (which == CalculateForward) {
477         return m_forward.isEmpty();
478     } else if (which == CalculateBackward) {
479         return m_backward.isEmpty();
480     }
481     return m_appointments.isEmpty();
482 }
483 
appointments(int which) const484 QList<Appointment*> Schedule::appointments(int which) const
485 {
486     if (which == CalculateForward) {
487         return m_forward;
488     } else if (which == CalculateBackward) {
489         return m_backward;
490     }
491     return m_appointments;
492 }
493 
appointmentIntervals(int which,const DateTimeInterval & interval) const494 Appointment Schedule::appointmentIntervals(int which, const DateTimeInterval &interval) const
495 {
496     Appointment app;
497     if (which == Schedule::CalculateForward) {
498         //debugPlan<<"list == CalculateForward";
499         foreach (Appointment *a, m_forward) {
500             app += interval.isValid() ? a->extractIntervals(interval) : *a;
501         }
502         return app;
503     } else if (which == Schedule::CalculateBackward) {
504         //debugPlan<<"list == CalculateBackward";
505         foreach (Appointment *a, m_backward) {
506             app += interval.isValid() ? a->extractIntervals(interval) : *a;
507         }
508         //debugPlan<<"list == CalculateBackward:"<<m_backward.count();
509         return app;
510     }
511     foreach (Appointment *a, m_appointments) {
512         app += interval.isValid() ? a->extractIntervals(interval) : *a;
513     }
514     return app;
515 }
516 
copyAppointments(Schedule::CalculationMode from,Schedule::CalculationMode to)517 void Schedule::copyAppointments(Schedule::CalculationMode from, Schedule::CalculationMode to)
518 {
519     switch (to) {
520         case Scheduling:
521             m_appointments.clear();
522             switch(from) {
523                 case CalculateForward:
524                     m_appointments = m_forward;
525                     break;
526                 case CalculateBackward:
527                     m_appointments = m_backward;
528                     break;
529                 default:
530                     break;
531             }
532             break;
533         case CalculateForward: break;
534         case CalculateBackward: break;
535     }
536 }
537 
bcwsPrDay(EffortCostCalculationType type) const538 EffortCostMap Schedule::bcwsPrDay(EffortCostCalculationType type) const
539 {
540     return const_cast<Schedule*>(this)->bcwsPrDay(type);
541 }
542 
bcwsPrDay(EffortCostCalculationType type)543 EffortCostMap Schedule::bcwsPrDay(EffortCostCalculationType type)
544 {
545     //debugPlan<<m_name<<m_appointments;
546     EffortCostCache &ec = m_bcwsPrDay[ (int)type ];
547     if (! ec.cached) {
548         foreach (Appointment *a, m_appointments) {
549             ec.effortcostmap += a->plannedPrDay(a->startTime().date(), a->endTime().date(), type);
550         }
551     }
552     return ec.effortcostmap;
553 }
554 
plannedEffortCostPrDay(const QDate & start,const QDate & end,EffortCostCalculationType type) const555 EffortCostMap Schedule::plannedEffortCostPrDay(const QDate &start, const QDate &end, EffortCostCalculationType type) const
556 {
557     //debugPlan<<m_name<<m_appointments;
558     EffortCostMap ec;
559     QListIterator<Appointment*> it(m_appointments);
560     while (it.hasNext()) {
561         //debugPlan<<m_name;
562         ec += it.next() ->plannedPrDay(start, end, type);
563     }
564     return ec;
565 }
566 
plannedEffortCostPrDay(const Resource * resource,const QDate & start,const QDate & end,EffortCostCalculationType type) const567 EffortCostMap Schedule::plannedEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, EffortCostCalculationType type) const
568 {
569     //debugPlan<<m_name<<m_appointments;
570     EffortCostMap ec;
571     foreach (Appointment *a, m_appointments) {
572         if (a->resource() && a->resource()->resource() == resource) {
573             ec += a->plannedPrDay(start, end, type);
574             break;
575         }
576     }
577     return ec;
578 }
579 
plannedEffort(const Resource * resource,EffortCostCalculationType type) const580 Duration Schedule::plannedEffort(const Resource *resource, EffortCostCalculationType type) const
581 {
582     //debugPlan;
583     Duration eff;
584     QListIterator<Appointment*> it(m_appointments);
585     while (it.hasNext()) {
586         eff += it.next() ->plannedEffort(resource, type);
587     }
588     return eff;
589 }
590 
plannedEffort(EffortCostCalculationType type) const591 Duration Schedule::plannedEffort(EffortCostCalculationType type) const
592 {
593     //debugPlan;
594     Duration eff;
595     QListIterator<Appointment*> it(m_appointments);
596     while (it.hasNext()) {
597         eff += it.next() ->plannedEffort(type);
598     }
599     return eff;
600 }
601 
plannedEffort(const QDate & date,EffortCostCalculationType type) const602 Duration Schedule::plannedEffort(const QDate &date, EffortCostCalculationType type) const
603 {
604     //debugPlan;
605     Duration eff;
606     QListIterator<Appointment*> it(m_appointments);
607     while (it.hasNext()) {
608         eff += it.next() ->plannedEffort(date, type);
609     }
610     return eff;
611 }
612 
plannedEffort(const Resource * resource,const QDate & date,EffortCostCalculationType type) const613 Duration Schedule::plannedEffort(const Resource *resource, const QDate &date, EffortCostCalculationType type) const
614 {
615     //debugPlan;
616     Duration eff;
617     QListIterator<Appointment*> it(m_appointments);
618     while (it.hasNext()) {
619         eff += it.next() ->plannedEffort(resource, date, type);
620     }
621     return eff;
622 }
623 
plannedEffortTo(const QDate & date,EffortCostCalculationType type) const624 Duration Schedule::plannedEffortTo(const QDate &date, EffortCostCalculationType type) const
625 {
626     //debugPlan;
627     Duration eff;
628     QListIterator<Appointment*> it(m_appointments);
629     while (it.hasNext()) {
630         eff += it.next() ->plannedEffortTo(date, type);
631     }
632     return eff;
633 }
634 
plannedEffortTo(const Resource * resource,const QDate & date,EffortCostCalculationType type) const635 Duration Schedule::plannedEffortTo(const Resource *resource, const QDate &date, EffortCostCalculationType type) const
636 {
637     //debugPlan;
638     Duration eff;
639     QListIterator<Appointment*> it(m_appointments);
640     while (it.hasNext()) {
641         eff += it.next() ->plannedEffortTo(resource, date, type);
642     }
643     return eff;
644 }
645 
plannedEffortTo(const QDateTime & time,EffortCostCalculationType type) const646 Duration Schedule::plannedEffortTo(const QDateTime &time, EffortCostCalculationType type) const
647 {
648     //debugPlan;
649     Duration eff;
650     QListIterator<Appointment*> it(m_appointments);
651     while (it.hasNext()) {
652         eff += it.next() ->plannedEffortTo(time, type);
653     }
654     return eff;
655 }
656 
plannedCost(EffortCostCalculationType type) const657 EffortCost Schedule::plannedCost(EffortCostCalculationType type) const
658 {
659     //debugPlan;
660     EffortCost c;
661     QListIterator<Appointment*> it(m_appointments);
662     while (it.hasNext()) {
663         c += it.next() ->plannedCost(type);
664     }
665     return c;
666 }
667 
plannedCost(const QDate & date,EffortCostCalculationType type) const668 double Schedule::plannedCost(const QDate &date, EffortCostCalculationType type) const
669 {
670     //debugPlan;
671     double c = 0;
672     QListIterator<Appointment*> it(m_appointments);
673     while (it.hasNext()) {
674         c += it.next() ->plannedCost(date, type);
675     }
676     return c;
677 }
678 
plannedCostTo(const QDate & date,EffortCostCalculationType type) const679 double Schedule::plannedCostTo(const QDate &date, EffortCostCalculationType type) const
680 {
681     //debugPlan;
682     double c = 0;
683     QListIterator<Appointment*> it(m_appointments);
684     while (it.hasNext()) {
685         c += it.next() ->plannedCostTo(date, type);
686     }
687     return c;
688 }
689 
addLog(const Schedule::Log & log)690 void Schedule::addLog(const Schedule::Log &log)
691 {
692     if (m_parent) {
693         m_parent->addLog(log);
694     }
695 }
696 
formatMsg() const697 QString Schedule::Log::formatMsg() const
698  {
699     QString s;
700     s += node ? QString("%1 ").arg(node->name(), -8) : "";
701     s += resource ? QString("%1 ").arg(resource->name(), -8) : "";
702     s += message;
703     return s;
704 }
705 
clearPerformanceCache()706 void Schedule::clearPerformanceCache()
707 {
708     m_bcwsPrDay.clear();
709     m_bcwpPrDay.clear();
710     m_acwp.clear();
711 }
712 
713 //-------------------------------------------------
NodeSchedule()714 NodeSchedule::NodeSchedule()
715         : Schedule(),
716         m_node(0)
717 {
718     //debugPlan<<"("<<this<<")";
719     init();
720 }
721 
NodeSchedule(Node * node,const QString & name,Schedule::Type type,long id)722 NodeSchedule::NodeSchedule(Node *node, const QString& name, Schedule::Type type, long id)
723         : Schedule(name, type, id),
724         m_node(node)
725 {
726     //debugPlan<<"node name:"<<node->name();
727     init();
728 }
729 
NodeSchedule(Schedule * parent,Node * node)730 NodeSchedule::NodeSchedule(Schedule *parent, Node *node)
731         : Schedule(parent),
732         m_node(node)
733 {
734 
735     //debugPlan<<"node name:"<<node->name();
736     init();
737 }
738 
~NodeSchedule()739 NodeSchedule::~NodeSchedule()
740 {
741     //debugPlan<<this<<""<<m_appointments.count();
742     while (!m_appointments.isEmpty()) {
743         Appointment *a = m_appointments.takeFirst();
744         a->setNode(0);
745         delete a;
746     }
747     //debugPlan<<"forw"<<m_forward.count();
748     while (!m_forward.isEmpty()) {
749         Appointment *a = m_forward.takeFirst();
750         a->setNode(0);
751         delete a;
752     }
753     //debugPlan<<"backw"<<m_backward.count();
754     while (!m_backward.isEmpty()) {
755         Appointment *a = m_backward.takeFirst();
756         a->setNode(0);
757         delete a;
758     }
759 }
760 
init()761 void NodeSchedule::init()
762 {
763     resourceError = false;
764     resourceOverbooked = false;
765     resourceNotAvailable = false;
766     constraintError = false;
767     schedulingError = false;
768     notScheduled = true;
769     inCriticalPath = false;
770     m_calculationMode = Schedule::Scheduling;
771     positiveFloat = Duration::zeroDuration;
772     negativeFloat = Duration::zeroDuration;
773     freeFloat = Duration::zeroDuration;
774 }
775 
setDeleted(bool on)776 void NodeSchedule::setDeleted(bool on)
777 {
778     //debugPlan<<"deleted="<<on;
779     m_deleted = on;
780     // set deleted also for possible resource schedules
781     QListIterator<Appointment*> it = m_appointments;
782     while (it.hasNext()) {
783         Appointment * a = it.next();
784         if (a->resource()) {
785             a->resource() ->setDeleted(on);
786         }
787     }
788 }
789 
loadXML(const KoXmlElement & sch,XMLLoaderObject & status)790 bool NodeSchedule::loadXML(const KoXmlElement &sch, XMLLoaderObject &status)
791 {
792     //debugPlan;
793     QString s;
794     Schedule::loadXML(sch, status);
795     s = sch.attribute("earlystart");
796     if (s.isEmpty()) { // try version < 0.6
797         s = sch.attribute("earlieststart");
798     }
799     if (!s.isEmpty()) {
800         earlyStart = DateTime::fromString(s, status.projectTimeZone());
801     }
802     s = sch.attribute("latefinish");
803     if (s.isEmpty()) { // try version < 0.6
804         s = sch.attribute("latestfinish");
805     }
806     if (!s.isEmpty()) {
807         lateFinish = DateTime::fromString(s, status.projectTimeZone());
808     }
809     s = sch.attribute("latestart");
810     if (!s.isEmpty()) {
811         lateStart = DateTime::fromString(s, status.projectTimeZone());
812     }
813     s = sch.attribute("earlyfinish");
814     if (!s.isEmpty()) {
815         earlyFinish = DateTime::fromString(s, status.projectTimeZone());
816     }
817     s = sch.attribute("start");
818     if (!s.isEmpty())
819         startTime = DateTime::fromString(s, status.projectTimeZone());
820     s = sch.attribute("end");
821     if (!s.isEmpty())
822         endTime = DateTime::fromString(s, status.projectTimeZone());
823     s = sch.attribute("start-work");
824     if (!s.isEmpty())
825         workStartTime = DateTime::fromString(s, status.projectTimeZone());
826     s = sch.attribute("end-work");
827     if (!s.isEmpty())
828         workEndTime = DateTime::fromString(s, status.projectTimeZone());
829     duration = Duration::fromString(sch.attribute("duration"));
830 
831     inCriticalPath = sch.attribute("in-critical-path", "0").toInt();
832     resourceError = sch.attribute("resource-error", "0").toInt();
833     resourceOverbooked = sch.attribute("resource-overbooked", "0").toInt();
834     resourceNotAvailable = sch.attribute("resource-not-available", "0").toInt();
835     constraintError = sch.attribute("scheduling-conflict", "0").toInt();
836     schedulingError = sch.attribute("scheduling-error", "0").toInt();
837     notScheduled = sch.attribute("not-scheduled", "1").toInt();
838 
839     positiveFloat = Duration::fromString(sch.attribute("positive-float"));
840     negativeFloat = Duration::fromString(sch.attribute("negative-float"));
841     freeFloat = Duration::fromString(sch.attribute("free-float"));
842 
843     return true;
844 }
845 
saveXML(QDomElement & element) const846 void NodeSchedule::saveXML(QDomElement &element) const
847 {
848     //debugPlan;
849     QDomElement sch = element.ownerDocument().createElement("schedule");
850     element.appendChild(sch);
851     saveCommonXML(sch);
852 
853     if (earlyStart.isValid()) {
854         sch.setAttribute("earlystart", earlyStart.toString(Qt::ISODate));
855     }
856     if (lateStart.isValid()) {
857         sch.setAttribute("latestart", lateStart.toString(Qt::ISODate));
858     }
859     if (earlyFinish.isValid()) {
860         sch.setAttribute("earlyfinish", earlyFinish.toString(Qt::ISODate));
861     }
862     if (lateFinish.isValid()) {
863         sch.setAttribute("latefinish", lateFinish.toString(Qt::ISODate));
864     }
865     if (startTime.isValid())
866         sch.setAttribute("start", startTime.toString(Qt::ISODate));
867     if (endTime.isValid())
868         sch.setAttribute("end", endTime.toString(Qt::ISODate));
869     if (workStartTime.isValid())
870         sch.setAttribute("start-work", workStartTime.toString(Qt::ISODate));
871     if (workEndTime.isValid())
872         sch.setAttribute("end-work", workEndTime.toString(Qt::ISODate));
873 
874     sch.setAttribute("duration", duration.toString());
875 
876     sch.setAttribute("in-critical-path", QString::number(inCriticalPath));
877     sch.setAttribute("resource-error", QString::number(resourceError));
878     sch.setAttribute("resource-overbooked", QString::number(resourceOverbooked));
879     sch.setAttribute("resource-not-available", QString::number(resourceNotAvailable));
880     sch.setAttribute("scheduling-conflict", QString::number(constraintError));
881     sch.setAttribute("scheduling-error", QString::number(schedulingError));
882     sch.setAttribute("not-scheduled", QString::number(notScheduled));
883 
884     sch.setAttribute("positive-float", positiveFloat.toString());
885     sch.setAttribute("negative-float", negativeFloat.toString());
886     sch.setAttribute("free-float", freeFloat.toString());
887 }
888 
addAppointment(Schedule * resource,const DateTime & start,const DateTime & end,double load)889 void NodeSchedule::addAppointment(Schedule *resource, const DateTime &start, const DateTime &end, double load)
890 {
891     //debugPlan;
892     Appointment * a = findAppointment(resource, this, m_calculationMode);
893     if (a != 0) {
894         //debugPlan<<"Add interval to existing"<<a;
895         a->addInterval(start, end, load);
896         return ;
897     }
898     a = new Appointment(resource, this, start, end, load);
899     bool result = add(a);
900     Q_ASSERT (result);
901     result = resource->add(a);
902     Q_ASSERT (result);
903     Q_UNUSED (result); // cheating the compiler in release mode to not warn about unused-but-set-variable
904     //debugPlan<<"Added interval to new"<<a;
905 }
906 
takeAppointment(Appointment * appointment,int mode)907 void NodeSchedule::takeAppointment(Appointment *appointment, int mode)
908 {
909     Schedule::takeAppointment(appointment, mode);
910     appointment->setNode(0); // not my appointment anymore
911     //debugPlan<<"Taken:"<<appointment;
912     if (appointment->resource())
913         appointment->resource() ->takeAppointment(appointment);
914 }
915 
resources() const916 QList<Resource*> NodeSchedule::resources() const
917 {
918     QList<Resource*> rl;
919     foreach(Appointment *a, m_appointments) {
920         rl += a->resource() ->resource();
921     }
922     return rl;
923 }
924 
resourceNameList() const925 QStringList NodeSchedule::resourceNameList() const
926 {
927     QStringList rl;
928     foreach(Appointment *a, m_appointments) {
929         rl += a->resource() ->resource() ->name();
930     }
931     return rl;
932 }
933 
logError(const QString & msg,int phase)934 void NodeSchedule::logError(const QString &msg, int phase)
935 {
936     Schedule::Log log(m_node, Log::Type_Error, msg, phase);
937     if (m_parent) {
938         m_parent->addLog(log);
939     } else {
940         addLog(log);
941     }
942 }
943 
logWarning(const QString & msg,int phase)944 void NodeSchedule::logWarning(const QString &msg, int phase)
945 {
946     Schedule::Log log(m_node, Log::Type_Warning, msg, phase);
947     if (m_parent) {
948         m_parent->addLog(log);
949     } else {
950         addLog(log);
951     }
952 }
953 
logInfo(const QString & msg,int phase)954 void NodeSchedule::logInfo(const QString &msg, int phase)
955 {
956     Schedule::Log log(m_node, Log::Type_Info, msg, phase);
957     if (m_parent) {
958         m_parent->addLog(log);
959     } else {
960         addLog(log);
961     }
962 }
963 
logDebug(const QString & msg,int phase)964 void NodeSchedule::logDebug(const QString &msg, int phase)
965 {
966     Schedule::Log log(m_node, Log::Type_Debug, msg, phase);
967     if (m_parent) {
968         m_parent->addLog(log);
969     } else {
970         addLog(log);
971     }
972 }
973 
974 //-----------------------------------------------
ResourceSchedule()975 ResourceSchedule::ResourceSchedule()
976         : Schedule(),
977         m_resource(0)
978 {
979     //debugPlan<<"("<<this<<")";
980 }
981 
ResourceSchedule(Resource * resource,const QString & name,Schedule::Type type,long id)982 ResourceSchedule::ResourceSchedule(Resource *resource, const QString& name, Schedule::Type type, long id)
983         : Schedule(name, type, id),
984         m_resource(resource),
985         m_parent(0),
986         m_nodeSchedule(0)
987 {
988     //debugPlan<<"resource:"<<resource->name();
989 }
990 
ResourceSchedule(Schedule * parent,Resource * resource)991 ResourceSchedule::ResourceSchedule(Schedule *parent, Resource *resource)
992         : Schedule(parent),
993         m_resource(resource),
994         m_parent(parent),
995         m_nodeSchedule(0)
996 {
997     //debugPlan<<"resource:"<<resource->name();
998 }
999 
~ResourceSchedule()1000 ResourceSchedule::~ResourceSchedule()
1001 {
1002     //debugPlan<<this<<""<<m_appointments.count();
1003     while (!m_appointments.isEmpty()) {
1004         Appointment *a = m_appointments.takeFirst();
1005         a->setResource(0);
1006         delete a;
1007     }
1008     //debugPlan<<"forw"<<m_forward.count();
1009     while (!m_forward.isEmpty()) {
1010         Appointment *a = m_forward.takeFirst();
1011         a->setResource(0);
1012         delete a;
1013     }
1014     //debugPlan<<"backw"<<m_backward.count();
1015     while (!m_backward.isEmpty()) {
1016         Appointment *a = m_backward.takeFirst();
1017         a->setResource(0);
1018         delete a;
1019     }
1020 }
1021 
1022 // called from the resource
addAppointment(Schedule * node,const DateTime & start,const DateTime & end,double load)1023 void ResourceSchedule::addAppointment(Schedule *node, const DateTime &start, const DateTime &end, double load)
1024 {
1025     Q_ASSERT(start < end);
1026     //debugPlan<<"("<<this<<")"<<node<<","<<m_calculationMode;
1027     Appointment * a = findAppointment(this, node, m_calculationMode);
1028     if (a != 0) {
1029         //debugPlan<<"Add interval to existing"<<a;
1030         a->addInterval(start, end, load);
1031         return ;
1032     }
1033     a = new Appointment(this, node, start, end, load);
1034     bool result = add(a);
1035     Q_ASSERT (result == true);
1036     result = node->add(a);
1037     Q_ASSERT (result == true);
1038     Q_UNUSED (result);  //don't warn about unused-but-set-variable in release mode
1039     //debugPlan<<"Added interval to new"<<a;
1040 }
1041 
takeAppointment(Appointment * appointment,int mode)1042 void ResourceSchedule::takeAppointment(Appointment *appointment, int mode)
1043 {
1044     Schedule::takeAppointment(appointment, mode);
1045     appointment->setResource(0);
1046     //debugPlan<<"Taken:"<<appointment;
1047     if (appointment->node())
1048         appointment->node() ->takeAppointment(appointment);
1049 }
1050 
isOverbooked() const1051 bool ResourceSchedule::isOverbooked() const
1052 {
1053     return false;
1054 }
1055 
isOverbooked(const DateTime & start,const DateTime & end) const1056 bool ResourceSchedule::isOverbooked(const DateTime &start, const DateTime &end) const
1057 {
1058     if (m_resource == 0)
1059         return false;
1060     //debugPlan<<start.toString()<<" -"<<end.toString();
1061     Appointment a = appointmentIntervals();
1062     foreach (const AppointmentInterval &i, a.intervals().map()) {
1063         if ((!end.isValid() || i.startTime() < end) &&
1064                 (!start.isValid() || i.endTime() > start)) {
1065             if (i.load() > m_resource->units()) {
1066                 //debugPlan<<m_name<<" overbooked";
1067                 return true;
1068             }
1069         }
1070         if (i.startTime() >= end)
1071             break;
1072     }
1073     //debugPlan<<m_name<<" not overbooked";
1074     return false;
1075 }
1076 
normalRatePrHour() const1077 double ResourceSchedule::normalRatePrHour() const
1078 {
1079     return m_resource ? m_resource->normalRate() : 0.0;
1080 }
1081 
1082 //TODO change to return the booked effort
effort(const DateTimeInterval & interval) const1083 Duration ResourceSchedule::effort(const DateTimeInterval &interval) const
1084 {
1085     Duration eff = interval.second - interval.first;
1086     if (allowOverbooking()) {
1087         return eff;
1088     }
1089     Appointment a;
1090     if (checkExternalAppointments()) {
1091         a.setIntervals(m_resource->externalAppointments());
1092     }
1093     a.merge(appointmentIntervals(m_calculationMode));
1094     if (a.isEmpty() || a.startTime() >= interval.second || a.endTime() <= interval.first) {
1095         return eff;
1096     }
1097     foreach (const AppointmentInterval &i, a.intervals().map()) {
1098         if (interval.second <= i.startTime()) {
1099             break;
1100         }
1101         if (interval.first >= i.startTime()) {
1102             DateTime et = i.endTime() < interval.second ? i.endTime() : interval.second;
1103             eff -= (et - interval.first) * ((double)i.load()/100.0);
1104         } else {
1105             DateTime et = i.endTime() < interval.second ? i.endTime() : interval.second;
1106             eff -= (et - i.startTime()) * ((double)i.load()/100.0);
1107         }
1108     }
1109     return eff;
1110 }
1111 
available(const DateTimeInterval & interval) const1112 DateTimeInterval ResourceSchedule::available(const DateTimeInterval &interval) const
1113 {
1114     //const_cast<ResourceSchedule*>(this)->logDebug(QString("Schedule available id=%1, Mode=%2: interval=%3 - %4").arg(m_id).arg(m_calculationMode).arg(interval.first.toString()).arg(interval.second.toString()));
1115     if (allowOverbooking()) {
1116         return DateTimeInterval(interval.first, interval.second);
1117     }
1118     QTimeZone projectTimeZone = QTimeZone::systemTimeZone();
1119     if (m_resource) {
1120         projectTimeZone = m_resource->project()->timeZone();
1121     }
1122     DateTimeInterval ci(interval.first.toTimeZone(projectTimeZone), interval.second.toTimeZone(projectTimeZone));
1123     Appointment a;
1124     if (checkExternalAppointments()) {
1125         a.setIntervals(m_resource->externalAppointments(ci));
1126     }
1127     a.merge(appointmentIntervals(m_calculationMode, ci));
1128     if (a.isEmpty() || a.startTime() >= ci.second || a.endTime() <= ci.first) {
1129         //debugPlan<<this<<"id="<<m_id<<"Mode="<<m_calculationMode<<""<<interval.first<<","<<interval.second<<" FREE";
1130         return DateTimeInterval(interval.first, interval.second); // just return the interval
1131     }
1132     //debugPlan<<"available:"<<interval<<endl<<a.intervals();
1133     DateTimeInterval res;
1134     int units = m_resource ? m_resource->units() : 100;
1135     foreach (const AppointmentInterval &i, a.intervals().map()) {
1136         //const_cast<ResourceSchedule*>(this)->logDebug(QString("Schedule available check interval=%1 - %2").arg(i.startTime().toString()).arg(i.endTime().toString()));
1137         if (i.startTime() < ci.second && i.endTime() > ci.first) {
1138             // interval intersects appointment
1139             if (ci.first >= i.startTime() && ci.second <= i.endTime()) {
1140                 // interval within appointment
1141                 if (i.load() < units) {
1142                     if (! res.first.isValid()) {
1143                         res.first = qMax(ci.first, i.startTime());
1144                     }
1145                     res.second = qMin(ci.second, i.endTime());
1146                 } else {
1147                     // fully booked
1148                     if (res.first.isValid()) {
1149                         res.second = i.startTime();
1150                         if (res.first >= res.second) {
1151                             res = DateTimeInterval();
1152                         }
1153                     }
1154                 }
1155                 //debugPlan<<"available within:"<<interval<<i<<":"<<ci<<res;
1156                 break;
1157             }
1158             DateTime t = i.startTime();
1159             if (ci.first < t) {
1160                 // Interval starts before appointment, so free from interval start
1161                 //debugPlan<<"available before:"<<interval<<i<<":"<<ci<<res;
1162                 //const_cast<ResourceSchedule*>(this)->logDebug(QString("Schedule available t>first: returns interval=%1 - %2").arg(ci.first.toString()).arg(t.toString()));
1163                 if (! res.first.isValid()) {
1164                     res.first = ci.first;
1165                 }
1166                 res.second = t;
1167                 if (i.load() < units) {
1168                     res.second = qMin(ci.second, i.endTime());
1169                     if (ci.second > i.endTime()) {
1170                         ci.first = i.endTime();
1171                         //debugPlan<<"available next 1:"<<interval<<i<<":"<<ci<<res;
1172                         continue; // check next appointment
1173                     }
1174                 }
1175                 //debugPlan<<"available:"<<interval<<i<<":"<<ci<<res;
1176                 break;
1177             }
1178             // interval start >= appointment start
1179             t = i.endTime();
1180             if (t < ci.second) {
1181                 // check if rest of appointment is free
1182                 if (units <= i.load()) {
1183                     ci.first = t; // fully booked, so move forward to appointment end
1184                 }
1185                 res = ci;
1186                 //debugPlan<<"available next 2:"<<interval<<i<<":"<<ci<<res;
1187                 continue;
1188             }
1189             //debugPlan<<"available:"<<interval<<i<<":"<<ci<<res;
1190             Q_ASSERT(false);
1191         } else if (i.startTime() >= interval.second) {
1192             // no more overlaps
1193             break;
1194         }
1195     }
1196     //debugPlan<<"available: result="<<interval<<":"<<res;
1197     return DateTimeInterval(res.first.toTimeZone(interval.first.timeZone()), res.second.toTimeZone(interval.second.timeZone()));
1198 }
1199 
logError(const QString & msg,int phase)1200 void ResourceSchedule::logError(const QString &msg, int phase)
1201 {
1202     if (m_parent) {
1203         Schedule::Log log(m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Error, msg, phase);
1204         m_parent->addLog(log);
1205     }
1206 }
1207 
logWarning(const QString & msg,int phase)1208 void ResourceSchedule::logWarning(const QString &msg, int phase)
1209 {
1210     if (m_parent) {
1211         Schedule::Log log(m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Warning, msg, phase);
1212         m_parent->addLog(log);
1213     }
1214 }
1215 
logInfo(const QString & msg,int phase)1216 void ResourceSchedule::logInfo(const QString &msg, int phase)
1217 {
1218     if (m_parent) {
1219         Schedule::Log log(m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Info, msg, phase);
1220         m_parent->addLog(log);
1221     }
1222 }
1223 
logDebug(const QString & msg,int phase)1224 void ResourceSchedule::logDebug(const QString &msg, int phase)
1225 {
1226     if (m_parent) {
1227         Schedule::Log log(m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Debug, msg, phase);
1228         m_parent->addLog(log);
1229     }
1230 }
1231 
1232 //--------------------------------------
MainSchedule()1233 MainSchedule::MainSchedule()
1234     : NodeSchedule(),
1235 
1236     m_manager(0)
1237 {
1238     //debugPlan<<"("<<this<<")";
1239     init();
1240 }
1241 
MainSchedule(Node * node,const QString & name,Schedule::Type type,long id)1242 MainSchedule::MainSchedule(Node *node, const QString& name, Schedule::Type type, long id)
1243     : NodeSchedule(node, name, type, id),
1244       criticalPathListCached(false),
1245       m_manager(0),
1246       m_currentCriticalPath(0)
1247 {
1248     //debugPlan<<"node name:"<<node->name();
1249     init();
1250 }
1251 
~MainSchedule()1252 MainSchedule::~MainSchedule()
1253 {
1254     //debugPlan<<"("<<this<<")";
1255 }
1256 
incProgress()1257 void MainSchedule::incProgress()
1258 {
1259     if (m_manager) m_manager->incProgress();
1260 }
1261 
isBaselined() const1262 bool MainSchedule::isBaselined() const
1263 {
1264     return m_manager == 0 ? false : m_manager->isBaselined();
1265 }
1266 
usePert() const1267 bool MainSchedule::usePert() const
1268 {
1269     return m_manager == 0 ? false : m_manager->usePert();
1270 }
1271 
allowOverbooking() const1272 bool MainSchedule::allowOverbooking() const
1273 {
1274     return m_manager == 0 ? false : m_manager->allowOverbooking();
1275 }
1276 
checkExternalAppointments() const1277 bool MainSchedule::checkExternalAppointments() const
1278 {
1279     return m_manager == 0 ? false : m_manager->checkExternalAppointments();
1280 }
1281 
changed(Schedule * sch)1282 void MainSchedule::changed(Schedule *sch)
1283 {
1284     if (m_manager) {
1285         m_manager->scheduleChanged(static_cast<MainSchedule*>(sch));
1286     }
1287 }
1288 
insertHardConstraint(Node * node)1289 void MainSchedule::insertHardConstraint(Node *node)
1290 {
1291     m_hardconstraints.append(node);
1292 }
1293 
hardConstraints() const1294 QList<Node*> MainSchedule::hardConstraints() const
1295 {
1296     return m_hardconstraints;
1297 }
1298 
insertSoftConstraint(Node * node)1299 void MainSchedule::insertSoftConstraint(Node *node)
1300 {
1301     m_softconstraints.insert(-node->priority(), node);
1302 }
1303 
softConstraints() const1304 QList<Node*> MainSchedule::softConstraints() const
1305 {
1306     return m_softconstraints.values();
1307 }
1308 
forwardNodes() const1309 QList<Node*> MainSchedule::forwardNodes() const
1310 {
1311     return m_forwardnodes;
1312 }
1313 
insertForwardNode(Node * node)1314 void MainSchedule::insertForwardNode(Node *node)
1315 {
1316     m_forwardnodes.append(node);
1317 }
1318 
backwardNodes() const1319 QList<Node*> MainSchedule::backwardNodes() const
1320 {
1321     return m_backwardnodes;
1322 }
1323 
insertBackwardNode(Node * node)1324 void MainSchedule::insertBackwardNode(Node *node)
1325 {
1326     m_backwardnodes.append(node);
1327 }
1328 
insertStartNode(Node * node)1329 void MainSchedule::insertStartNode(Node *node)
1330 {
1331     m_startNodes.insert(-node->priority(), node);
1332 }
1333 
startNodes() const1334 QList<Node*> MainSchedule::startNodes() const
1335 {
1336     return m_startNodes.values();
1337 }
1338 
insertEndNode(Node * node)1339 void MainSchedule::insertEndNode(Node *node)
1340 {
1341     m_endNodes.insert(-node->priority(), node);
1342 }
1343 
endNodes() const1344 QList<Node*> MainSchedule::endNodes() const
1345 {
1346     return m_endNodes.values();
1347 }
1348 
insertSummaryTask(Node * node)1349 void MainSchedule::insertSummaryTask(Node *node)
1350 {
1351     m_summarytasks.append(node);
1352 }
1353 
summaryTasks() const1354 QList<Node*> MainSchedule::summaryTasks() const
1355 {
1356     return m_summarytasks;
1357 }
1358 
loadXML(const KoXmlElement & sch,XMLLoaderObject & status)1359 bool MainSchedule::loadXML(const KoXmlElement &sch, XMLLoaderObject &status)
1360 {
1361     //debugPlan;
1362     QString s;
1363     Schedule::loadXML(sch, status);
1364 
1365     s = sch.attribute("start");
1366     if (!s.isEmpty())
1367         startTime = DateTime::fromString(s, status.projectTimeZone());
1368     s = sch.attribute("end");
1369     if (!s.isEmpty())
1370         endTime = DateTime::fromString(s, status.projectTimeZone());
1371 
1372     duration = Duration::fromString(sch.attribute("duration"));
1373     constraintError = sch.attribute("scheduling-conflict", "0").toInt();
1374     schedulingError = sch.attribute("scheduling-error", "0").toInt();
1375     //NOTE: we use "scheduled" as default to match old format without "not-scheduled" element
1376     notScheduled = sch.attribute("not-scheduled", "0").toInt();
1377 
1378     KoXmlNode n = sch.firstChild();
1379     for (; ! n.isNull(); n = n.nextSibling()) {
1380         if (! n.isElement()) {
1381             continue;
1382         }
1383         KoXmlElement el = n.toElement();
1384         if (el.tagName() == "appointment") {
1385             // Load the appointments.
1386             // Resources and tasks must already be loaded
1387             Appointment * child = new Appointment();
1388             if (!child->loadXML(el, status, *this)) {
1389                 // TODO: Complain about this
1390                 errorPlan << "Failed to load appointment" << endl;
1391                 delete child;
1392             }
1393         } else if (el.tagName() == "criticalpath-list") {
1394             // Tasks must already be loaded
1395             for (KoXmlNode n1 = el.firstChild(); ! n1.isNull(); n1 = n1.nextSibling()) {
1396                 if (! n1.isElement()) {
1397                     continue;
1398                 }
1399                 KoXmlElement e1 = n1.toElement();
1400                 if (e1.tagName() != "criticalpath") {
1401                     continue;
1402                 }
1403                 QList<Node*> lst;
1404                 for (KoXmlNode n2 = e1.firstChild(); ! n2.isNull(); n2 = n2.nextSibling()) {
1405                     if (! n2.isElement()) {
1406                         continue;
1407                     }
1408                     KoXmlElement e2 = n2.toElement();
1409                     if (e2.tagName() != "node") {
1410                         continue;
1411                     }
1412                     QString s = e2.attribute("id");
1413                     Node *node = status.project().findNode(s);
1414                     if (node) {
1415                         lst.append(node);
1416                     } else {
1417                         errorPlan<<"Failed to find node id="<<s;
1418                     }
1419                 }
1420                 m_pathlists.append(lst);
1421             }
1422             criticalPathListCached = true;
1423         }
1424     }
1425     return true;
1426 }
1427 
saveXML(QDomElement & element) const1428 void MainSchedule::saveXML(QDomElement &element) const
1429 {
1430     saveCommonXML(element);
1431 
1432     element.setAttribute("start", startTime.toString(Qt::ISODate));
1433     element.setAttribute("end", endTime.toString(Qt::ISODate));
1434     element.setAttribute("duration", duration.toString());
1435     element.setAttribute("scheduling-conflict", QString::number(constraintError));
1436     element.setAttribute("scheduling-error", QString::number(schedulingError));
1437     element.setAttribute("not-scheduled", QString::number(notScheduled));
1438 
1439     if (! m_pathlists.isEmpty()) {
1440         QDomElement lists = element.ownerDocument().createElement("criticalpath-list");
1441         element.appendChild(lists);
1442         foreach (const QList<Node*> &l, m_pathlists) {
1443             if (l.isEmpty()) {
1444                 continue;
1445             }
1446             QDomElement list = lists.ownerDocument().createElement("criticalpath");
1447             lists.appendChild(list);
1448             foreach (Node *n, l) {
1449                 QDomElement el = list.ownerDocument().createElement("node");
1450                 list.appendChild(el);
1451                 el.setAttribute("id", n->id());
1452             }
1453         }
1454     }
1455 }
1456 
calculateForward(int use)1457 DateTime MainSchedule::calculateForward(int use)
1458 {
1459     DateTime late;
1460     foreach(Node *n, m_backwardnodes) {
1461         DateTime t = n->calculateForward(use);
1462         if (!late.isValid() || late < t) {
1463             late = t;
1464         }
1465     }
1466     return late;
1467 }
1468 
calculateBackward(int use)1469 DateTime MainSchedule::calculateBackward(int use)
1470 {
1471     DateTime early;
1472     foreach(Node *n, m_forwardnodes) {
1473         DateTime t = n->calculateBackward(use);
1474         if (!early.isValid() || early > t) {
1475             early = t;
1476         }
1477     }
1478     return early;
1479 }
1480 
scheduleForward(const DateTime & earliest,int use)1481 DateTime MainSchedule::scheduleForward(const DateTime &earliest, int use)
1482 {
1483     DateTime end;
1484     foreach(Node *n, m_forwardnodes) {
1485         DateTime t = n->scheduleForward(earliest, use);
1486         if (!end.isValid() || end < t) {
1487             end = t;
1488         }
1489     }
1490     return end;
1491 }
1492 
scheduleBackward(const DateTime & latest,int use)1493 DateTime MainSchedule::scheduleBackward(const DateTime &latest, int use)
1494 {
1495     DateTime start;
1496     foreach(Node *n, m_backwardnodes) {
1497         DateTime t = n->scheduleBackward(latest, use);
1498         if (!start.isValid() || start > t) {
1499             start = t;
1500         }
1501     }
1502     return start;
1503 }
1504 
recalculate() const1505 bool MainSchedule::recalculate() const
1506 {
1507     return m_manager == 0 ? false : m_manager->recalculate();
1508 }
1509 
recalculateFrom() const1510 DateTime MainSchedule::recalculateFrom() const
1511 {
1512     return m_manager == 0 ? DateTime() : m_manager->recalculateFrom();
1513 }
1514 
parentScheduleId() const1515 long MainSchedule::parentScheduleId() const
1516 {
1517     return m_manager == 0 ? -2 : m_manager->parentScheduleId();
1518 }
1519 
clearCriticalPathList()1520 void MainSchedule::clearCriticalPathList()
1521 {
1522     m_pathlists.clear();
1523     m_currentCriticalPath = 0;
1524     criticalPathListCached = false;
1525 }
1526 
currentCriticalPath() const1527 QList<Node*> *MainSchedule::currentCriticalPath() const
1528 {
1529     return m_currentCriticalPath;
1530 }
1531 
addCriticalPath(QList<Node * > * lst)1532 void MainSchedule::addCriticalPath(QList<Node*> *lst)
1533 {
1534     QList<Node*> l;
1535     if (lst) {
1536         l = *lst;
1537     }
1538     m_pathlists.append(l);
1539     m_currentCriticalPath = &(m_pathlists.last());
1540 }
1541 
addCriticalPathNode(Node * node)1542 void MainSchedule::addCriticalPathNode(Node *node)
1543 {
1544     if (m_currentCriticalPath == 0) {
1545         errorPlan<<"No currentCriticalPath"<<endl;
1546         return;
1547     }
1548     m_currentCriticalPath->append(node);
1549 }
1550 
logs() const1551 QVector<Schedule::Log> MainSchedule::logs() const
1552 {
1553     return m_log;
1554 }
1555 
addLog(const KPlato::Schedule::Log & log)1556 void MainSchedule::addLog(const KPlato::Schedule::Log &log)
1557 {
1558     Q_ASSERT(log.resource || log.node);
1559 #ifndef NDEBUG
1560     if (log.resource) {
1561         Q_ASSERT(manager()->project().findResource(log.resource->id()) == log.resource);
1562     } else if (log.node) {
1563         Q_ASSERT(manager()->project().findNode(log.node->id()) == log.node);
1564     }
1565 #endif
1566     const int phaseToSet = (log.phase == -1 && ! m_log.isEmpty()) ? m_log.last().phase : -1;
1567     m_log.append(log);
1568 
1569     if (phaseToSet != -1) {
1570         m_log.last().phase = phaseToSet;
1571     }
1572     if (m_manager) {
1573         m_manager->logAdded(m_log.last());
1574     }
1575 }
1576 
1577 //static
logSeverity(int severity)1578 QString MainSchedule::logSeverity(int severity)
1579 {
1580     switch (severity) {
1581         case Log::Type_Debug: return "Debug";//FIXME i18n("Debug");
1582         case Log::Type_Info: return i18n("Info");
1583         case Log::Type_Warning: return i18n("Warning");
1584         case Log::Type_Error: return i18n("Error");
1585         default: break;
1586     }
1587     return QString("Severity %1").arg(severity);
1588 }
1589 
logMessages() const1590 QStringList MainSchedule::logMessages() const
1591 {
1592     QStringList lst;
1593     foreach (const Schedule::Log &l, m_log) {
1594         lst << l.formatMsg();
1595     }
1596     return lst;
1597 }
1598 
1599 //-----------------------------------------
ScheduleManager(Project & project,const QString name)1600 ScheduleManager::ScheduleManager(Project &project, const QString name)
1601     : m_project(project),
1602     m_parent(0),
1603     m_name(name),
1604     m_baselined(false),
1605     m_allowOverbooking(false),
1606     m_checkExternalAppointments(true),
1607     m_usePert(false),
1608     m_recalculate(false),
1609     m_schedulingDirection(false),
1610     m_scheduling(false),
1611     m_progress(0),
1612     m_maxprogress(0),
1613     m_expected(nullptr),
1614     m_calculationresult(0),
1615     m_schedulingMode(false)
1616 {
1617     //debugPlan<<name;
1618 }
1619 
~ScheduleManager()1620 ScheduleManager::~ScheduleManager()
1621 {
1622     qDeleteAll(m_children);
1623     setParentManager(0);
1624 }
1625 
setParentManager(ScheduleManager * sm,int index)1626 void ScheduleManager::setParentManager(ScheduleManager *sm, int index)
1627 {
1628     if (m_parent) {
1629         m_parent->removeChild(this);
1630     }
1631     m_parent = sm;
1632     if (sm) {
1633         sm->insertChild(this, index);
1634     }
1635 }
1636 
removeChild(const ScheduleManager * sm)1637 int ScheduleManager::removeChild(const ScheduleManager *sm)
1638 {
1639     int i = m_children.indexOf(const_cast<ScheduleManager*>(sm));
1640     if (i != -1) {
1641         m_children.removeAt(i);
1642     }
1643     return i;
1644 }
1645 
insertChild(ScheduleManager * sm,int index)1646 void ScheduleManager::insertChild(ScheduleManager *sm, int index)
1647 {
1648     //debugPlan<<m_name<<", insert"<<sm->name()<<","<<index;
1649     if (index == -1) {
1650         m_children.append(sm);
1651     } else {
1652         m_children.insert(index, sm);
1653     }
1654 }
1655 
createSchedules()1656 void ScheduleManager::createSchedules()
1657 {
1658     setExpected(m_project.createSchedule(m_name, Schedule::Expected));
1659 }
1660 
indexOf(const ScheduleManager * child) const1661 int ScheduleManager::indexOf(const ScheduleManager *child) const
1662 {
1663     //debugPlan<<this<<","<<child;
1664     return m_children.indexOf(const_cast<ScheduleManager*>(child));
1665 }
1666 
findManager(const QString & name) const1667 ScheduleManager *ScheduleManager::findManager(const QString& name) const
1668 {
1669     if (m_name == name) {
1670         return const_cast<ScheduleManager*>(this);
1671     }
1672     foreach (ScheduleManager *sm, m_children) {
1673         ScheduleManager *m = sm->findManager(name);
1674         if (m) {
1675             return m;
1676         }
1677     }
1678     return 0;
1679 }
1680 
allChildren() const1681 QList<ScheduleManager*> ScheduleManager::allChildren() const
1682 {
1683     QList<ScheduleManager*> lst;
1684     foreach (ScheduleManager *sm, m_children) {
1685         lst << sm;
1686         lst << sm->allChildren();
1687     }
1688     return lst;
1689 }
1690 
isParentOf(const ScheduleManager * sm) const1691 bool ScheduleManager::isParentOf(const ScheduleManager *sm) const
1692 {
1693     if (indexOf(sm) >= 0) {
1694         return true;
1695     }
1696     foreach (ScheduleManager *p, m_children) {
1697         if (p->isParentOf(sm)) {
1698             return true;
1699         }
1700     }
1701     return false;
1702 }
1703 
setName(const QString & name)1704 void ScheduleManager::setName(const QString& name)
1705 {
1706     m_name = name;
1707 #ifndef NDEBUG
1708     setObjectName(name);
1709 #endif
1710     if (m_expected) {
1711         m_expected->setName(name);
1712         m_project.changed(m_expected);
1713     }
1714     m_project.changed(this);
1715 }
1716 
isChildBaselined() const1717 bool ScheduleManager::isChildBaselined() const
1718 {
1719     //debugPlan<<on;
1720     foreach (ScheduleManager *sm, m_children) {
1721         if (sm->isBaselined() || sm->isChildBaselined()) {
1722             return true;
1723         }
1724     }
1725     return false;
1726 }
1727 
setBaselined(bool on)1728 void ScheduleManager::setBaselined(bool on)
1729 {
1730     //debugPlan<<on;
1731     m_baselined = on;
1732     m_project.changed(this);
1733 }
1734 
setAllowOverbooking(bool on)1735 void ScheduleManager::setAllowOverbooking(bool on)
1736 {
1737     //debugPlan<<on;
1738     m_allowOverbooking = on;
1739     m_project.changed(this, OverbookProperty);
1740 }
1741 
allowOverbooking() const1742 bool ScheduleManager::allowOverbooking() const
1743 {
1744     //debugPlan<<m_name<<"="<<m_allowOverbooking;
1745     return m_allowOverbooking;
1746 }
1747 
checkExternalAppointments() const1748 bool ScheduleManager::checkExternalAppointments() const
1749 {
1750     //debugPlan<<m_name<<"="<<m_allowOverbooking;
1751     return m_checkExternalAppointments;
1752 }
1753 
setCheckExternalAppointments(bool on)1754 void ScheduleManager::setCheckExternalAppointments(bool on)
1755 {
1756     //debugPlan<<m_name<<"="<<m_checkExternalAppointments;
1757     m_checkExternalAppointments = on;
1758 }
1759 
scheduleChanged(MainSchedule * sch)1760 void ScheduleManager::scheduleChanged(MainSchedule *sch)
1761 {
1762     m_project.changed(sch);
1763     m_project.changed(this); //hmmm, due to aggregated info
1764 }
1765 
setUsePert(bool on)1766 void ScheduleManager::setUsePert(bool on)
1767 {
1768     m_usePert = on;
1769     m_project.changed(this, DistributionProperty);
1770 }
1771 
setSchedulingDirection(bool on)1772 void ScheduleManager::setSchedulingDirection(bool on)
1773 {
1774     //debugPlan<<on;
1775     m_schedulingDirection = on;
1776     m_project.changed(this, DirectionProperty);
1777 }
1778 
setScheduling(bool on)1779 void ScheduleManager::setScheduling(bool on)
1780 {
1781     m_scheduling = on;
1782     if (! on) {
1783         m_project.setProgress(0, this);
1784     }
1785     m_project.changed(this);
1786 }
1787 
schedulerPlugins() const1788 const QList<SchedulerPlugin*> ScheduleManager::schedulerPlugins() const
1789 {
1790     return m_project.schedulerPlugins().values();
1791 }
1792 
schedulerPluginId() const1793 QString ScheduleManager::schedulerPluginId() const
1794 {
1795     return m_schedulerPluginId;
1796 }
1797 
setSchedulerPluginId(const QString & id)1798 void ScheduleManager::setSchedulerPluginId(const QString &id)
1799 {
1800     m_schedulerPluginId = id;
1801     m_project.changed(this);
1802 }
1803 
schedulerPlugin() const1804 SchedulerPlugin *ScheduleManager::schedulerPlugin() const
1805 {
1806     if (m_schedulerPluginId.isEmpty() || !m_project.schedulerPlugins().contains(m_schedulerPluginId)) {
1807         // try to avoid crash
1808         return m_project.schedulerPlugins().value(m_project.schedulerPlugins().keys().value(0));
1809     }
1810     return m_project.schedulerPlugins().value(m_schedulerPluginId);
1811 }
1812 
schedulerPluginNames() const1813 QStringList ScheduleManager::schedulerPluginNames() const
1814 {
1815     QStringList lst;
1816     QMap<QString, SchedulerPlugin*>::const_iterator it = m_project.schedulerPlugins().constBegin();
1817     QMap<QString, SchedulerPlugin*>::const_iterator end = m_project.schedulerPlugins().constEnd();
1818     for (; it != end; ++it) {
1819         lst << it.value()->name();
1820     }
1821     return lst;
1822 }
1823 
schedulerPluginIndex() const1824 int ScheduleManager::schedulerPluginIndex() const
1825 {
1826     if (m_schedulerPluginId.isEmpty()) {
1827         return 0;
1828     }
1829     return m_project.schedulerPlugins().keys().indexOf(m_schedulerPluginId);
1830 }
1831 
setSchedulerPlugin(int index)1832 void ScheduleManager::setSchedulerPlugin(int index)
1833 {
1834     if (schedulerPlugin()) {
1835         schedulerPlugin()->stopCalculation(this); // in case...
1836     }
1837 
1838     m_schedulerPluginId = m_project.schedulerPlugins().keys().value(index);
1839     debugPlan<<index<<m_schedulerPluginId;
1840     m_project.changed(this);
1841 }
1842 
calculateSchedule()1843 void ScheduleManager::calculateSchedule()
1844 {
1845     m_calculationresult = CalculationRunning;
1846     if (schedulerPlugin()) {
1847         schedulerPlugin()->calculate(m_project, this);
1848     }
1849 }
1850 
stopCalculation()1851 void ScheduleManager::stopCalculation()
1852 {
1853     if (schedulerPlugin()) {
1854         schedulerPlugin()->stopCalculation(this);
1855     }
1856 }
1857 
haltCalculation()1858 void ScheduleManager::haltCalculation()
1859 {
1860     if (schedulerPlugin()) {
1861         schedulerPlugin()->haltCalculation(this);
1862     }
1863 }
1864 
setMaxProgress(int value)1865 void ScheduleManager::setMaxProgress(int value)
1866 {
1867     m_maxprogress = value;
1868     emit maxProgressChanged(value);
1869     m_project.changed(this);
1870 }
1871 
setProgress(int value)1872 void ScheduleManager::setProgress(int value)
1873 {
1874     m_progress = value;
1875     emit progressChanged(value);
1876     m_project.changed(this);
1877 }
1878 
setDeleted(bool on)1879 void ScheduleManager::setDeleted(bool on)
1880 {
1881     if (m_expected) {
1882         m_expected->setDeleted(on);
1883     }
1884     m_project.changed(this);
1885 }
1886 
setExpected(MainSchedule * sch)1887 void ScheduleManager::setExpected(MainSchedule *sch)
1888 {
1889     //debugPlan<<m_expected<<","<<sch;
1890     if (m_expected) {
1891         m_project.sendScheduleToBeRemoved(m_expected);
1892         m_expected->setDeleted(true);
1893         m_project.sendScheduleRemoved(m_expected);
1894     }
1895     m_expected = sch;
1896     if (sch) {
1897         m_project.sendScheduleToBeAdded(this, 0);
1898         sch->setManager(this);
1899         m_expected->setDeleted(false);
1900         m_project.sendScheduleAdded(sch);
1901     }
1902     m_project.changed(this);
1903 }
1904 
state() const1905 QStringList ScheduleManager::state() const
1906 {
1907     QStringList lst;
1908     if (isBaselined()) {
1909         return lst << i18n("Baselined");
1910     }
1911     if (m_scheduling) {
1912         return lst << i18n("Scheduling");
1913     }
1914     if (m_expected == 0) {
1915         return lst << i18n("Not scheduled");
1916     }
1917     if (Schedule *s = m_expected) {
1918         if (s->resourceError || s->resourceOverbooked || s->resourceNotAvailable || s->constraintError || s->schedulingError) {
1919             return lst << i18n("Error");
1920         }
1921         return s->state();
1922     }
1923     return lst;
1924 }
1925 
supportedGranularities() const1926 QList<long unsigned int> ScheduleManager::supportedGranularities() const
1927 {
1928     QList<long unsigned int> lst;
1929     if (schedulerPlugin()) {
1930         lst = schedulerPlugin()->granularities();
1931     }
1932     return lst;
1933 }
1934 
granularity() const1935 int ScheduleManager::granularity() const
1936 {
1937     if (schedulerPlugin()) {
1938         return schedulerPlugin()->granularity();
1939     }
1940     return 0;
1941 }
1942 
setGranularity(int duration)1943 void ScheduleManager::setGranularity(int duration)
1944 {
1945     if (schedulerPlugin()) {
1946         schedulerPlugin()->setGranularity(duration);
1947     }
1948     m_project.changed(this, GranularityProperty);
1949 }
1950 
schedulingMode() const1951 bool ScheduleManager::schedulingMode() const
1952 {
1953     return m_schedulingMode;
1954 }
1955 
setSchedulingMode(int mode)1956 void ScheduleManager::setSchedulingMode(int mode)
1957 {
1958     m_schedulingMode = mode;
1959     m_project.changed(this, SchedulingModeProperty);
1960 }
1961 
incProgress()1962 void ScheduleManager::incProgress()
1963 {
1964     m_project.incProgress();
1965 }
1966 
logAdded(const Schedule::Log & log)1967 void ScheduleManager::logAdded(const Schedule::Log &log)
1968 {
1969     emit sigLogAdded(log);
1970     int row = expected()->logs().count() - 1;
1971     emit logInserted(expected(), row, row);
1972 }
1973 
slotAddLog(const QVector<KPlato::Schedule::Log> & log)1974 void ScheduleManager::slotAddLog(const QVector<KPlato::Schedule::Log> &log)
1975 {
1976     if (expected() && ! log.isEmpty()) {
1977         int first = expected()->logs().count();
1978         int last = first + log.count() - 1;
1979         Q_UNUSED(last);
1980         foreach (const KPlato::Schedule::Log &l, log) {
1981             expected()->addLog(l);
1982         }
1983     }
1984 }
1985 
phaseNames() const1986 QMap< int, QString > ScheduleManager::phaseNames() const
1987 {
1988     if (expected()) {
1989         return expected()->phaseNames();
1990     }
1991     return QMap<int, QString>();
1992 }
1993 
setPhaseNames(const QMap<int,QString> & phasenames)1994 void ScheduleManager::setPhaseNames(const QMap<int, QString> &phasenames)
1995 {
1996     if (expected()) {
1997         expected()->setPhaseNames(phasenames);
1998     }
1999 }
2000 
2001 
loadXML(KoXmlElement & element,XMLLoaderObject & status)2002 bool ScheduleManager::loadXML(KoXmlElement &element, XMLLoaderObject &status)
2003 {
2004     MainSchedule *sch = 0;
2005     if (status.version() <= "0.5") {
2006         m_usePert = false;
2007         sch = loadMainSchedule(element, status);
2008         if (sch) {
2009             sch->setManager(this);
2010             switch (sch->type()) {
2011                 case Schedule::Expected: setExpected(sch); break;
2012             }
2013         }
2014         return true;
2015     }
2016     setName(element.attribute("name"));
2017     m_id = element.attribute("id");
2018     m_usePert = (element.attribute("distribution").toInt()) == 1;
2019     m_allowOverbooking = (bool)(element.attribute("overbooking").toInt());
2020     m_checkExternalAppointments = (bool)(element.attribute("check-external-appointments").toInt());
2021     m_schedulingDirection = (bool)(element.attribute("scheduling-direction").toInt());
2022     m_baselined = (bool)(element.attribute("baselined").toInt());
2023     m_schedulerPluginId = element.attribute("scheduler-plugin-id");
2024     if (status.project().schedulerPlugins().contains(m_schedulerPluginId)) {
2025         // atm we only load for current plugin
2026         int g = element.attribute("granularity", "0").toInt();
2027         status.project().schedulerPlugins().value(m_schedulerPluginId)->setGranularity(g);
2028     }
2029     m_recalculate = (bool)(element.attribute("recalculate").toInt());
2030     m_recalculateFrom = DateTime::fromString(element.attribute("recalculate-from"), status.projectTimeZone());
2031     if (element.hasAttribute("scheduling-mode")) {
2032         m_schedulingMode = element.attribute("scheduling-mode").toInt();
2033     }
2034 
2035     KoXmlNode n = element.firstChild();
2036     for (; ! n.isNull(); n = n.nextSibling()) {
2037         if (! n.isElement()) {
2038             continue;
2039         }
2040         KoXmlElement e = n.toElement();
2041         //debugPlan<<e.tagName();
2042         if (e.tagName() == "schedule") {
2043             sch = loadMainSchedule(e, status);
2044             if (sch) {
2045                 sch->setManager(this);
2046                 switch (sch->type()) {
2047                     case Schedule::Expected: setExpected(sch); break;
2048                 }
2049             }
2050         } else if (e.tagName() == "plan") {
2051             ScheduleManager *sm = new ScheduleManager(status.project());
2052             if (sm->loadXML(e, status)) {
2053                 m_project.addScheduleManager(sm, this);
2054             } else {
2055                 errorPlan<<"Failed to load schedule manager"<<endl;
2056                 delete sm;
2057             }
2058         }
2059     }
2060     return true;
2061 }
2062 
loadMainSchedule(KoXmlElement & element,XMLLoaderObject & status)2063 MainSchedule *ScheduleManager::loadMainSchedule(KoXmlElement &element, XMLLoaderObject &status) {
2064     MainSchedule *sch = new MainSchedule();
2065     if (sch->loadXML(element, status)) {
2066         status.project().addSchedule(sch);
2067         sch->setNode(&(status.project()));
2068         status.project().setParentSchedule(sch);
2069     } else {
2070         errorPlan << "Failed to load schedule" << endl;
2071         delete sch;
2072         sch = 0;
2073     }
2074     return sch;
2075 }
2076 
loadMainSchedule(MainSchedule * schedule,KoXmlElement & element,XMLLoaderObject & status)2077 bool ScheduleManager::loadMainSchedule(MainSchedule *schedule, KoXmlElement &element, XMLLoaderObject &status) {
2078     long sid = schedule->id();
2079     if (schedule->loadXML(element, status)) {
2080         if (sid != schedule->id() && status.project().findSchedule(sid)) {
2081             status.project().takeSchedule(schedule);
2082         }
2083         if (! status.project().findSchedule(schedule->id())) {
2084             status.project().addSchedule(schedule);
2085         }
2086         schedule->setNode(&(status.project()));
2087         status.project().setParentSchedule(schedule);
2088         return true;
2089     }
2090     return false;
2091 }
2092 
saveXML(QDomElement & element) const2093 void ScheduleManager::saveXML(QDomElement &element) const
2094 {
2095     QDomElement el = element.ownerDocument().createElement("plan");
2096     element.appendChild(el);
2097     el.setAttribute("name", m_name);
2098     el.setAttribute("id", m_id);
2099     el.setAttribute("distribution", QString::number(m_usePert ? 1 : 0));
2100     el.setAttribute("overbooking", QString::number(m_allowOverbooking));
2101     el.setAttribute("check-external-appointments", QString::number(m_checkExternalAppointments));
2102     el.setAttribute("scheduling-direction", QString::number(m_schedulingDirection));
2103     el.setAttribute("baselined", QString::number(m_baselined));
2104     el.setAttribute("scheduler-plugin-id", m_schedulerPluginId);
2105     if (schedulerPlugin()) {
2106         // atm we only save for current plugin
2107         el.setAttribute("granularity", QString::number(schedulerPlugin()->granularity()));
2108     }
2109     el.setAttribute("recalculate", QString::number(m_recalculate));
2110     el.setAttribute("recalculate-from", m_recalculateFrom.toString(Qt::ISODate));
2111     el.setAttribute("scheduling-mode", m_schedulingMode);
2112     if (m_expected && ! m_expected->isDeleted()) {
2113         QDomElement schs = el.ownerDocument().createElement("schedule");
2114         el.appendChild(schs);
2115         m_expected->saveXML(schs);
2116         m_project.saveAppointments(schs, m_expected->id());
2117     }
2118     foreach (ScheduleManager *sm, m_children) {
2119         sm->saveXML(el);
2120     }
2121 
2122 }
2123 
saveWorkPackageXML(QDomElement & element,const Node & node) const2124 void ScheduleManager::saveWorkPackageXML(QDomElement &element, const Node &node) const
2125 {
2126     QDomElement el = element.ownerDocument().createElement("plan");
2127     element.appendChild(el);
2128     el.setAttribute("name", m_name);
2129     el.setAttribute("id", m_id);
2130     el.setAttribute("distribution", QString::number(m_usePert ? 1 : 0));
2131     el.setAttribute("overbooking", QString::number(m_allowOverbooking));
2132     el.setAttribute("check-external-appointments", QString::number(m_checkExternalAppointments));
2133     el.setAttribute("scheduling-direction", QString::number(m_schedulingDirection));
2134     el.setAttribute("baselined", QString::number(m_baselined));
2135     if (m_expected && ! m_expected->isDeleted()) { // TODO: should we check isScheduled() ?
2136         QDomElement schs = el.ownerDocument().createElement("schedule");
2137         el.appendChild(schs);
2138         m_expected->saveXML(schs);
2139         Schedule *s = node.findSchedule(m_expected->id());
2140         if (s && ! s->isDeleted()) {
2141             s->saveAppointments(schs);
2142         }
2143     }
2144 }
2145 
2146 
2147 } //namespace KPlato
2148 
operator <<(QDebug dbg,const KPlato::Schedule * s)2149 QDebug operator<<(QDebug dbg, const KPlato::Schedule *s)
2150 {
2151     if (s) {
2152         return dbg<<(*s);
2153     }
2154     return dbg<<"Schedule(0x0)";
2155 }
operator <<(QDebug dbg,const KPlato::Schedule & s)2156 QDebug operator<<(QDebug dbg, const KPlato::Schedule &s)
2157 {
2158     dbg.nospace()<<"Schedule["<<s.id();
2159     if (s.isDeleted()) {
2160         dbg.nospace()<<": Deleted";
2161     } else {
2162         dbg.nospace()<<": "<<s.name();
2163     }
2164     dbg.nospace()<<"]";
2165     return dbg.space();
2166 }
2167 
operator <<(QDebug dbg,const KPlato::Schedule::Log & log)2168 QDebug operator<<(QDebug dbg, const KPlato::Schedule::Log &log)
2169 {
2170     dbg.nospace()<<"Schedule::Log: "<<log.formatMsg();
2171     return dbg.space();
2172 }
2173