You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kplato/kpttask.cc

1543 lines
58 KiB

/* This file is part of the KDE project
Copyright (C) 2001 Thomas zander <zander@kde.org>
Copyright (C) 2004 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kpttask.h"
#include "kptproject.h"
#include "kpttaskdialog.h"
#include "kptduration.h"
#include "kptrelation.h"
#include "kptdatetime.h"
#include "kptcalendar.h"
#include "kpteffortcostmap.h"
#include "kptschedule.h"
#include <qdom.h>
#include <qbrush.h>
#include <kdebug.h>
namespace KPlato
{
Task::Task(Node *parent) : Node(parent), m_resource() {
//kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
m_resource.setAutoDelete(true);
Duration d(1, 0, 0);
m_effort = new Effort(d);
m_effort->setOptimisticRatio(-10);
m_effort->setPessimisticRatio(20);
m_requests = 0;
if (m_parent)
m_leader = m_parent->leader();
m_schedules.setAutoDelete(true);
m_parentProxyRelations.setAutoDelete(true);
m_childProxyRelations.setAutoDelete(true);
}
Task::Task(Task &task, Node *parent)
: Node(task, parent),
m_resource() {
//kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
m_resource.setAutoDelete(true);
m_parentProxyRelations.setAutoDelete(true);
m_childProxyRelations.setAutoDelete(true);
m_requests = 0;
m_effort = task.effort() ? new Effort(*(task.effort()))
: new Effort(); // Avoid crash, (shouldn't be zero)
}
Task::~Task() {
delete m_effort;
}
int Task::type() const {
if ( numChildren() > 0) {
return Node::Type_Summarytask;
}
else if ( 0 == effort()->expected().seconds() ) {
return Node::Type_Milestone;
}
else {
return Node::Type_Task;
}
}
Duration *Task::getExpectedDuration() {
//kdDebug()<<k_funcinfo<<endl;
// Duration should already be calculated
return m_currentSchedule ? new Duration(m_currentSchedule->duration) : new Duration();
}
Duration *Task::getRandomDuration() {
return 0L;
}
ResourceGroupRequest *Task::resourceGroupRequest(ResourceGroup *group) const {
if (m_requests)
return m_requests->find(group);
return 0;
}
void Task::clearResourceRequests() {
if (m_requests)
m_requests->clear();
}
void Task::addRequest(ResourceGroup *group, int numResources) {
addRequest(new ResourceGroupRequest(group, numResources));
}
void Task::addRequest(ResourceGroupRequest *request) {
if (!m_requests)
m_requests = new ResourceRequestCollection(*this);
m_requests->addRequest(request);
}
void Task::takeRequest(ResourceGroupRequest *request) {
if (m_requests) {
m_requests->takeRequest(request);
if (m_requests->isEmpty()) {
delete m_requests;
m_requests = 0;
}
}
}
int Task::units() const {
if (!m_requests)
return 0;
return m_requests->units();
}
int Task::workUnits() const {
if (!m_requests)
return 0;
return m_requests->workUnits();
}
void Task::makeAppointments() {
if (m_currentSchedule == 0)
return;
if (type() == Node::Type_Task) {
if (m_requests) {
//kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl;
m_requests->makeAppointments(m_currentSchedule);
//kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->startTime<<", "<<m_currentSchedule->endTime<<"; "<<m_currentSchedule->duration.toString()<<endl;
}
} else if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> nit(m_nodes);
for ( ; nit.current(); ++nit ) {
nit.current()->makeAppointments();
}
} else if (type() == Node::Type_Milestone) {
//kdDebug()<<k_funcinfo<<"Milestone not implemented"<<endl;
// Well, shouldn't have resources anyway...
}
}
void Task::calcResourceOverbooked() {
if (m_currentSchedule)
m_currentSchedule->calcResourceOverbooked();
}
// A new constraint means start/end times and duration must be recalculated
void Task::setConstraint(Node::ConstraintType type) {
m_constraint = type;
}
bool Task::load(QDomElement &element, Project &project) {
// Load attributes (TODO: Handle different types of tasks, milestone, summary...)
QString s;
bool ok = false;
m_id = element.attribute("id");
m_name = element.attribute("name");
m_leader = element.attribute("leader");
m_description = element.attribute("description");
//kdDebug()<<k_funcinfo<<m_name<<": id="<<m_id<<endl;
// Allow for both numeric and text
QString constraint = element.attribute("scheduling","0");
m_constraint = (Node::ConstraintType)constraint.toInt(&ok);
if (!ok)
Node::setConstraint(constraint); // hmmm, why do I need Node::?
s = element.attribute("constraint-starttime");
if (s != "")
m_constraintStartTime = DateTime::fromString(s);
s = element.attribute("constraint-endtime");
if ( s != "")
m_constraintEndTime = DateTime::fromString(s);
m_startupCost = element.attribute("startup-cost", "0.0").toDouble();
m_shutdownCost = element.attribute("shutdown-cost", "0.0").toDouble();
m_wbs = element.attribute("wbs", "");
// Load the project children
QDomNodeList list = element.childNodes();
for (unsigned int i=0; i<list.count(); ++i) {
if (list.item(i).isElement()) {
QDomElement e = list.item(i).toElement();
if (e.tagName() == "project") {
// Load the subproject
Project *child = new Project(this);
if (child->load(e)) {
if (!project.addSubTask(child, this)) {
delete child; // TODO: Complain about this
}
} else {
// TODO: Complain about this
delete child;
}
} else if (e.tagName() == "task") {
// Load the task
Task *child = new Task(this);
if (child->load(e, project)) {
if (!project.addSubTask(child, this)) {
delete child; // TODO: Complain about this
}
} else {
// TODO: Complain about this
delete child;
}
} else if (e.tagName() == "resource") {
// TODO: Load the resource (projects don't have resources yet)
} else if (e.tagName() == "effort") {
// Load the effort
m_effort->load(e);
} else if (e.tagName() == "resourcegroup-request") {
// Load the resource request
ResourceGroupRequest *r = new ResourceGroupRequest();
if (r->load(e, project)) {
addRequest(r);
} else {
kdError()<<k_funcinfo<<"Failed to load resource request"<<endl;
delete r;
}
} else if (e.tagName() == "progress") {
m_progress.started = (bool)e.attribute("started", "0").toInt();
m_progress.finished = (bool)e.attribute("finished", "0").toInt();
s = e.attribute("startTime");
if (s != "")
m_progress.startTime = DateTime::fromString(s);
s = e.attribute("finishTime");
if (s != "")
m_progress.finishTime = DateTime::fromString(s);
m_progress.percentFinished = e.attribute("percent-finished", "0").toInt();
m_progress.remainingEffort = Duration::fromString(e.attribute("remaining-effort"));
m_progress.totalPerformed = Duration::fromString(e.attribute("performed-effort"));
} else if (e.tagName() == "schedules") {
QDomNodeList lst = e.childNodes();
for (unsigned int i=0; i<lst.count(); ++i) {
if (lst.item(i).isElement()) {
QDomElement el = lst.item(i).toElement();
if (el.tagName() == "schedule") {
NodeSchedule *sch = new NodeSchedule();
if (sch->loadXML(el)) {
sch->setNode(this);
addSchedule(sch);
} else {
kdError()<<k_funcinfo<<"Failed to load schedule"<<endl;
delete sch;
}
}
}
}
}
}
}
//kdDebug()<<k_funcinfo<<m_name<<" loaded"<<endl;
return true;
}
void Task::save(QDomElement &element) const {
QDomElement me = element.ownerDocument().createElement("task");
element.appendChild(me);
//TODO: Handle different types of tasks, milestone, summary...
me.setAttribute("id", m_id);
me.setAttribute("name", m_name);
me.setAttribute("leader", m_leader);
me.setAttribute("description", m_description);
me.setAttribute("scheduling",constraintToString());
me.setAttribute("constraint-starttime",m_constraintStartTime.toString(Qt::ISODate));
me.setAttribute("constraint-endtime",m_constraintEndTime.toString(Qt::ISODate));
me.setAttribute("startup-cost", m_startupCost);
me.setAttribute("shutdown-cost", m_shutdownCost);
me.setAttribute("wbs", m_wbs);
m_effort->save(me);
QDomElement el = me.ownerDocument().createElement("progress");
me.appendChild(el);
el.setAttribute("started", m_progress.started);
el.setAttribute("finished", m_progress.finished);
el.setAttribute("startTime", m_progress.startTime.toString(Qt::ISODate));
el.setAttribute("finishTime", m_progress.finishTime.toString(Qt::ISODate));
el.setAttribute("percent-finished", m_progress.percentFinished);
el.setAttribute("remaining-effort", m_progress.remainingEffort.toString());
el.setAttribute("performed-effort", m_progress.totalPerformed.toString());
if (!m_schedules.isEmpty()) {
QDomElement schs = me.ownerDocument().createElement("schedules");
me.appendChild(schs);
QIntDictIterator<Schedule> it = m_schedules;
for (; it.current(); ++it) {
if (!it.current()->isDeleted()) {
it.current()->saveXML(schs);
}
}
}
if (m_requests) {
m_requests->save(me);
}
for (int i=0; i<numChildren(); i++) {
getChildNode(i)->save(me);
}
}
void Task::saveAppointments(QDomElement &element, long id) const {
//kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
Schedule *sch = findSchedule(id);
if (sch) {
sch->saveAppointments(element);
}
QPtrListIterator<Node> it(m_nodes);
for (; it.current(); ++it ) {
it.current()->saveAppointments(element, id);
}
}
EffortCostMap Task::plannedEffortCostPrDay(const QDate &start, const QDate &end) const {
//kdDebug()<<k_funcinfo<<m_name<<endl;
if (m_currentSchedule) {
return m_currentSchedule->plannedEffortCostPrDay(start, end);
}
return EffortCostMap();
}
// Returns the total planned effort for this task (or subtasks)
Duration Task::plannedEffort() {
//kdDebug()<<k_funcinfo<<endl;
Duration eff;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
eff += it.current()->plannedEffort();
}
} else if (m_currentSchedule) {
eff = m_currentSchedule->plannedEffort();
}
return eff;
}
// Returns the total planned effort for this task (or subtasks) on date
Duration Task::plannedEffort(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
Duration eff;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
eff += it.current()->plannedEffort(date);
}
} else if (m_currentSchedule) {
eff = m_currentSchedule->plannedEffort(date);
}
return eff;
}
// Returns the total planned effort for this task (or subtasks) upto and including date
Duration Task::plannedEffortTo(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
Duration eff;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
eff += it.current()->plannedEffortTo(date);
}
} else if (m_currentSchedule) {
eff = m_currentSchedule->plannedEffortTo(date);
}
return eff;
}
// Returns the total planned effort for this task (or subtasks)
Duration Task::actualEffort() {
//kdDebug()<<k_funcinfo<<endl;
Duration eff;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
eff += it.current()->actualEffort();
}
} else {
eff = m_progress.totalPerformed;
}
/* If we want to register pr resource...
} else if (m_currentSchedule) {
eff = m_currentSchedule->actualEffort();
}*/
return eff;
}
// Returns the total planned effort for this task (or subtasks) on date
Duration Task::actualEffort(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
Duration eff;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
eff += it.current()->actualEffort(date);
}
} else if (m_currentSchedule) {
eff = m_currentSchedule->actualEffort(date);
}
return eff;
}
// Returns the total planned effort for this task (or subtasks) on date
Duration Task::actualEffortTo(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
Duration eff;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
eff += it.current()->actualEffortTo(date);
}
} else if (m_currentSchedule) {
eff = m_currentSchedule->actualEffortTo(date);
}
return eff;
}
double Task::plannedCost() {
//kdDebug()<<k_funcinfo<<endl;
double c = 0;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
c += it.current()->plannedCost();
}
} else if (m_currentSchedule) {
c = m_currentSchedule->plannedCost();
}
return c;
}
double Task::plannedCost(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
double c = 0;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
c += it.current()->plannedCost(date);
}
} else if (m_currentSchedule) {
c = m_currentSchedule->plannedCost(date);
}
return c;
}
double Task::plannedCostTo(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
double c = 0;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
c += it.current()->plannedCostTo(date);
}
} else if (m_currentSchedule) {
c = m_currentSchedule->plannedCostTo(date);
}
return c;
}
double Task::actualCost() {
//kdDebug()<<k_funcinfo<<endl;
double c = 0;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
c += it.current()->actualCost();
}
} else if (m_currentSchedule) {
c = m_currentSchedule->actualCost();
}
return c;
}
double Task::actualCost(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
double c = 0;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
c += it.current()->actualCost(date);
}
} else if (m_currentSchedule) {
c = m_currentSchedule->actualCost(date);
}
return c;
}
double Task::actualCostTo(const QDate &date) {
//kdDebug()<<k_funcinfo<<endl;
double c = 0;
if (type() == Node::Type_Summarytask) {
QPtrListIterator<Node> it(childNodeIterator());
for (; it.current(); ++it) {
c += it.current()->actualCostTo(date);
}
} else if (m_currentSchedule) {
c = m_currentSchedule->actualCostTo(date);
}
return c;
}
//FIXME Handle summarytasks
double Task::effortPerformanceIndex(const QDate &date, bool *error) {
double res = 0.0;
Duration ae = actualEffortTo(date);
bool e = (ae == Duration::zeroDuration || m_progress.percentFinished == 0);
if (error) {
*error = e;
}
if (!e) {
res = (plannedEffortTo(date).toDouble() * ((double)m_progress.percentFinished/100.0) / ae.toDouble());
}
return res;
}
//FIXME Handle summarytasks
double Task::costPerformanceIndex(const QDate &date, bool *error) {
double res = 0.0;
Duration ac = Q_INT64(actualCostTo(date));
bool e = (ac == Duration::zeroDuration || m_progress.percentFinished == 0);
if (error) {
*error = e;
}
if (!e) {
res = (plannedCostTo(date) * m_progress.percentFinished)/(100 * actualCostTo(date));
}
return res;
}
void Task::initiateCalculation(Schedule &sch) {
//kdDebug()<<k_funcinfo<<m_name<<" schedule: "<<(sch?sch->name():"None")<<" id="<<(sch?sch->id():-1)<<endl;
m_visitedForward = false;
m_visitedBackward = false;
m_currentSchedule = createSchedule(&sch);
m_currentSchedule->initiateCalculation();
clearProxyRelations();
Node::initiateCalculation(sch);
}
void Task::initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks/*, QPtrList<Node> &milestones*/) {
//kdDebug()<<k_funcinfo<<m_name<<endl;
if (type() == Node::Type_Summarytask) {
summarytasks.append(this);
// propagate my relations to my children and dependent nodes
QPtrListIterator<Node> nodes = m_nodes;
for (; nodes.current(); ++nodes) {
if (!dependParentNodes().isEmpty())
nodes.current()->addParentProxyRelations(dependParentNodes());
if (!dependChildNodes().isEmpty())
nodes.current()->addChildProxyRelations(dependChildNodes());
nodes.current()->initiateCalculationLists(startnodes, endnodes, summarytasks);
}
} else {
if (isEndNode()) {
endnodes.append(this);
//kdDebug()<<k_funcinfo<<"endnodes append: "<<m_name<<endl;
}
if (isStartNode()) {
startnodes.append(this);
//kdDebug()<<k_funcinfo<<"startnodes append: "<<m_name<<endl;
}
}
}
DateTime Task::calculatePredeccessors(const QPtrList<Relation> &list, int use) {
DateTime time;
QPtrListIterator<Relation> it = list;
for (; it.current(); ++it) {
if (it.current()->parent()->type() == Type_Summarytask) {
//kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
continue; // skip summarytasks
}
DateTime t = it.current()->parent()->calculateForward(use); // early finish
switch (it.current()->type()) {
case Relation::StartStart:
// I can't start earlier than my predesseccor
t = it.current()->parent()->getEarliestStart() + it.current()->lag();
break;
case Relation::FinishFinish:
// I can't finish earlier than my predeccessor, so
// I can't start earlier than it's (earlyfinish+lag)- my duration
t += it.current()->lag();
t -= duration(t, use, true);
break;
default:
t += it.current()->lag();
break;
}
if (!time.isValid() || t > time)
time = t;
}
//kdDebug()<<time.toString()<<" "<<m_name<<" calculatePredeccessors() ("<<list.count()<<")"<<endl;
return time;
}
DateTime Task::calculateForward(int use) {
//kdDebug()<<k_funcinfo<<m_name<<<<endl;
if (m_currentSchedule == 0) {
return DateTime();
}
Schedule *cs = m_currentSchedule;
if (m_visitedForward) {
//kdDebug()<<earliestStart.toString()<<" + "<<m_durationBackward.toString()<<" "<<m_name<<" calculateForward() (visited)"<<endl;
return cs->earliestStart + m_durationForward;
}
// First, calculate all predecessors
if (!dependParentNodes().isEmpty()) {
DateTime time = calculatePredeccessors(dependParentNodes(), use);
if (time.isValid() && time > cs->earliestStart) {
cs->earliestStart = time;
}
}
if (!m_parentProxyRelations.isEmpty()) {
DateTime time = calculatePredeccessors(m_parentProxyRelations, use);
if (time.isValid() && time > cs->earliestStart) {
cs->earliestStart = time;
}
}
if (type() == Node::Type_Task) {
m_durationForward = m_effort->effort(use);
switch (constraint()) {
case Node::ASAP:
case Node::ALAP:
cs->earliestStart = workStartAfter(cs->earliestStart);
m_durationForward = duration(cs->earliestStart, use, false);
//kdDebug()<<k_funcinfo<<m_name<<": "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<endl;
break;
case Node::MustFinishOn:
m_durationForward = duration(m_constraintEndTime, use, true);
cs->earliestStart = m_constraintEndTime - m_durationForward;
break;
case Node::FinishNotLater:
m_durationForward = duration(cs->earliestStart, use, false);
if (cs->earliestStart + m_durationForward > m_constraintEndTime) {
m_durationForward = duration(m_constraintEndTime, use, true);
cs->earliestStart = m_constraintEndTime - m_durationForward;
}
break;
case Node::MustStartOn:
cs->earliestStart = m_constraintStartTime;
m_durationForward = duration(cs->earliestStart, use, false);
break;
case Node::StartNotEarlier:
if (cs->earliestStart < m_constraintStartTime) {
cs->earliestStart = m_constraintStartTime;
}
m_durationForward = duration(cs->earliestStart, use, false);
break;
case Node::FixedInterval: {
cs->earliestStart = m_constraintStartTime;
m_durationForward = m_constraintEndTime - m_constraintStartTime;
break;
}
}
} else if (type() == Node::Type_Milestone) {
m_durationForward = Duration::zeroDuration;
switch (constraint()) {
case Node::MustFinishOn:
cs->earliestStart = m_constraintEndTime;
break;
case Node::FinishNotLater:
if (cs->earliestStart > m_constraintEndTime) {
cs->earliestStart = m_constraintEndTime;
}
break;
case Node::MustStartOn:
cs->earliestStart = m_constraintStartTime;
break;
case Node::StartNotEarlier:
if (cs->earliestStart < m_constraintStartTime) {
cs->earliestStart = m_constraintStartTime;
}
break;
case Node::FixedInterval:
cs->earliestStart = m_constraintStartTime;
break;
default:
break;
}
//kdDebug()<<k_funcinfo<<m_name<<" "<<earliestStart.toString()<<endl
} else if (type() == Node::Type_Summarytask) {
kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
} else { // ???
m_durationForward = Duration::zeroDuration;
}
//kdDebug()<<"Earlyfinish: "<<cs->earliestStart<<"+"<<m_durationForward.toString()<<"="<<(cs->earliestStart+m_durationForward)<<" "<<m_name<<" calculateForward()"<<endl;
m_visitedForward = true;
return cs->earliestStart + m_durationForward;
}
DateTime Task::calculateSuccessors(const QPtrList<Relation> &list, int use) {
DateTime time;
QPtrListIterator<Relation> it = list;
for (; it.current(); ++it) {
if (it.current()->child()->type() == Type_Summarytask) {
//kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
continue; // skip summarytasks
}
DateTime t = it.current()->child()->calculateBackward(use);
switch (it.current()->type()) {
case Relation::StartStart:
// I must start before my successor, so
// I can't finish later than it's (starttime-lag) + my duration
t -= it.current()->lag();
t += duration(t, use, false);
break;
case Relation::FinishFinish:
// My successor cannot finish before me, so
// I can't finish later than it's latest finish - lag
t = it.current()->child()->getLatestFinish() - it.current()->lag();
break;
default:
t -= it.current()->lag();
break;
}
if (!time.isValid() || t < time)
time = t;
}
//kdDebug()<<time.toString()<<" "<<m_name<<" calculateSuccessors() ("<<list.count()<<")"<<endl;
return time;
}
DateTime Task::calculateBackward(int use) {
//kdDebug()<<k_funcinfo<<m_name<<endl;
if (m_currentSchedule == 0) {
return DateTime();
}
Schedule *cs = m_currentSchedule;
if (m_visitedBackward) {
//kdDebug()<<latestFinish.toString()<<" - "<<m_durationBackward.toString()<<" "<<m_name<<" calculateBackward() (visited)"<<endl;
return cs->latestFinish - m_durationBackward;
}
// First, calculate all successors
if (!dependChildNodes().isEmpty()) {
DateTime time = calculateSuccessors(dependChildNodes(), use);
if (time.isValid() && time < cs->latestFinish) {
cs->latestFinish = time;
}
}
if (!m_childProxyRelations.isEmpty()) {
DateTime time = calculateSuccessors(m_childProxyRelations, use);
if (time.isValid() && time < cs->latestFinish) {
cs->latestFinish = time;
}
}
//kdDebug()<<k_funcinfo<<m_name<<": latestFinish="<<cs->latestFinish<<endl;
if (type() == Node::Type_Task) {
m_durationBackward = m_effort->effort(use);
switch (constraint()) {
case Node::ASAP:
case Node::ALAP:
cs->latestFinish = workFinishBefore(cs->latestFinish);
m_durationBackward = duration(cs->latestFinish, use, true);
break;
case Node::MustStartOn:
m_durationBackward = duration(m_constraintStartTime, use, false);
cs->latestFinish = m_constraintStartTime + m_durationBackward;
break;
case Node::StartNotEarlier:
m_durationBackward = duration(cs->latestFinish, use, true);
if (cs->latestFinish - m_durationBackward < m_constraintStartTime) {
m_durationBackward = duration(m_constraintStartTime, use, false);
cs->latestFinish = m_constraintStartTime + m_durationBackward;
}
break;
case Node::MustFinishOn:
cs->latestFinish = m_constraintEndTime;
m_durationBackward = duration(cs->latestFinish, use, true);
break;
case Node::FinishNotLater:
if (cs->latestFinish > m_constraintEndTime) {
cs->latestFinish = m_constraintEndTime;
}
m_durationBackward = duration(cs->latestFinish, use, true);
break;
case Node::FixedInterval: {
cs->latestFinish = m_constraintEndTime;
m_durationBackward = m_constraintEndTime - m_constraintStartTime;
break;
}
}
} else if (type() == Node::Type_Milestone) {
m_durationBackward = Duration::zeroDuration;
switch (constraint()) {
case Node::MustFinishOn:
cs->latestFinish = m_constraintEndTime;
break;
case Node::FinishNotLater:
if (cs->latestFinish > m_constraintEndTime) {
cs->latestFinish = m_constraintEndTime;
}
break;
case Node::MustStartOn:
cs->latestFinish = m_constraintStartTime;
break;
case Node::StartNotEarlier:
if (cs->latestFinish < m_constraintStartTime) {
cs->latestFinish = m_constraintStartTime;
}
break;
case Node::FixedInterval:
cs->latestFinish = m_constraintEndTime;
break;
default:
break;
}
//kdDebug()<<k_funcinfo<<m_name<<" "<<cs->latestFinish<<endl;
} else if (type() == Node::Type_Summarytask) {
kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
} else { // ???
m_durationBackward = Duration::zeroDuration;
}
//kdDebug()<<"Latestart: "<<cs->latestFinish<<"-"<<m_durationBackward.toString()<<"="<<(cs->latestFinish-m_durationBackward).toString()<<" "<<m_name<<" calculateBackward()"<<endl;
m_visitedBackward = true;
return cs->latestFinish - m_durationBackward;
}
DateTime Task::schedulePredeccessors(const QPtrList<Relation> &list, int use) {
DateTime time;
QPtrListIterator<Relation> it = list;
for (; it.current(); ++it) {
if (it.current()->parent()->type() == Type_Summarytask) {
//kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->parent()->name()<<endl;
continue; // skip summarytasks
}
// schedule the predecessors
DateTime earliest = it.current()->parent()->getEarliestStart();
DateTime t = it.current()->parent()->scheduleForward(earliest, use);
switch (it.current()->type()) {
case Relation::StartStart:
// I can't start before my predesseccor
t = it.current()->parent()->startTime() + it.current()->lag();
break;
case Relation::FinishFinish:
// I can't end before my predecessor, so
// I can't start before it's endtime - my duration
t -= duration(t + it.current()->lag(), use, true);
break;
default:
t += it.current()->lag();
break;
}
if (!time.isValid() || t > time)
time = t;
}
//kdDebug()<<time.toString()<<" "<<m_name<<" schedulePredeccessors()"<<endl;
return time;
}
DateTime Task::scheduleForward(const DateTime &earliest, int use) {
//kdDebug()<<k_funcinfo<<m_name<<" earliest="<<earliest<<endl;
if (m_currentSchedule == 0) {
return DateTime();
}
Schedule *cs = m_currentSchedule;
if (m_visitedForward) {
return cs->endTime;
}
cs->notScheduled = false;
cs->startTime = earliest > cs->earliestStart ? earliest : cs->earliestStart;
// First, calculate all my own predecessors
DateTime time = schedulePredeccessors(dependParentNodes(), use);
if (time.isValid() && time > cs->startTime) {
cs->startTime = time;
//kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
}
// Then my parents
time = schedulePredeccessors(m_parentProxyRelations, use);
if (time.isValid() && time > cs->startTime) {
cs->startTime = time;
//kdDebug()<<k_funcinfo<<m_name<<" new startime="<<cs->startTime<<endl;
}
//kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
if(type() == Node::Type_Task) {
cs->duration = m_effort->effort(use);
switch (m_constraint) {
case Node::ASAP:
// cs->startTime calculated above
//kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
cs->startTime = workStartAfter(cs->startTime);
cs->duration = duration(cs->startTime, use, false);
cs->endTime = cs->startTime + cs->duration;
//kdDebug()<<k_funcinfo<<m_name<<" startTime="<<cs->startTime<<endl;
break;
case Node::ALAP:
// cd->startTime calculated above
cs->duration = duration(cs->latestFinish, use, true);
cs->endTime = cs->latestFinish;
cs->startTime = cs->endTime - cs->duration;
//kdDebug()<<k_funcinfo<<m_name<<" endTime="<<cs->endTime<<" latest="<<cs->latestFinish<<endl;
break;
case Node::StartNotEarlier:
// cs->startTime calculated above
//kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cd->startTime.toString()<<endl;
if (cs->startTime < m_constraintStartTime) {
cs->startTime = m_constraintStartTime;
}
cs->startTime = workStartAfter(cs->startTime);
cs->duration = duration(cs->startTime, use, false);
cs->endTime = cs->startTime + cs->duration;
if (cs->endTime > cs->latestFinish) {
cs->schedulingError = true;
}
break;
case Node::FinishNotLater:
// cs->startTime calculated above
//kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
cs->duration = duration(cs->startTime, use, false);
cs->endTime = cs->startTime + cs->duration;
if (cs->endTime > m_constraintEndTime) {
cs->schedulingError = true;
cs->endTime = m_constraintEndTime;
cs->duration = duration(cs->endTime, use, true);
cs->startTime = cs->endTime - cs->duration;
}
break;
case Node::MustStartOn:
// cs->startTime calculated above
//kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
if (m_constraintStartTime < cs->startTime ||
m_constraintStartTime > cs->latestFinish - m_durationBackward) {
cs->schedulingError = true;
}
cs->startTime = m_constraintStartTime;
cs->duration = duration(cs->startTime, use, false);
cs->endTime = cs->startTime + cs->duration;
break;
case Node::MustFinishOn:
// cs->startTime calculated above
//kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
if (m_constraintEndTime > cs->latestFinish ||
m_constraintEndTime < cs->earliestStart + m_durationForward) {
cs->schedulingError = true;
}
cs->endTime = m_constraintEndTime;
cs->duration = duration(cs->endTime, use, true);
cs->startTime = cs->endTime - cs->duration;
break;
case Node::FixedInterval: {
// cs->startTime calculated above
//kdDebug()<<"FixedInterval="<<m_constraintStartTime<<" "<<cs->startTime<<endl;
if (cs->startTime < cs->earliestStart) {
cs->schedulingError = true;
}
cs->startTime = m_constraintStartTime;
cs->endTime = m_constraintEndTime;
cs->duration = cs->endTime - cs->startTime;
cs->workStartTime = m_constraintStartTime;
cs->workEndTime = m_constraintEndTime;
//kdDebug()<<"FixedInterval="<<cs->startTime<<", "<<cs->endTime<<endl;
break;
}
default:
break;
}
if (m_requests) {
m_requests->reserve(cs->startTime, cs->duration);
}
} else if (type() == Node::Type_Milestone) {
switch (m_constraint) {
case Node::ASAP: {
cs->endTime = cs->startTime;
break;
}
case Node::ALAP: {
cs->startTime = cs->latestFinish;
cs->endTime = cs->latestFinish;
break;
}
case Node::MustStartOn:
case Node::FixedInterval:
//kdDebug()<<"Forw, MustStartOn: "<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
if (m_constraintStartTime < cs->startTime ||
m_constraintStartTime > cs->latestFinish) {
cs->schedulingError = true;
}
cs->startTime = m_constraintStartTime;
cs->endTime = m_constraintStartTime;
break;
case Node::MustFinishOn:
if (m_constraintEndTime < cs->startTime ||
m_constraintEndTime > cs->latestFinish) {
cs->schedulingError = true;
}
cs->startTime = m_constraintEndTime;
cs->endTime = m_constraintEndTime;
break;
case Node::StartNotEarlier:
if (cs->startTime < m_constraintStartTime) {
cs->schedulingError = true;
}
cs->endTime = cs->startTime;
break;
case Node::FinishNotLater:
if (cs->startTime > m_constraintEndTime) {
cs->schedulingError = true;
}
cs->endTime = cs->startTime;
break;
default:
break;
}
cs->duration = Duration::zeroDuration;
//kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime<<", "<<cs->endTime<<endl;
} else if (type() == Node::Type_Summarytask) {
//shouldn't come here
cs->endTime = cs->startTime;
cs->duration = cs->endTime - cs->startTime;
kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
}
//kdDebug()<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<" "<<m_name<<" scheduleForward()"<<endl;
m_visitedForward = true;
return cs->endTime;
}
DateTime Task::scheduleSuccessors(const QPtrList<Relation> &list, int use) {
DateTime time;
QPtrListIterator<Relation> it = list;
for (; it.current(); ++it) {
if (it.current()->child()->type() == Type_Summarytask) {
//kdDebug()<<k_funcinfo<<"Skip summarytask: "<<it.current()->child()->name()<<endl;
continue;
}
// get the successors starttime
DateTime latest = it.current()->child()->getLatestFinish();
DateTime t = it.current()->child()->scheduleBackward(latest, use);
switch (it.current()->type()) {
case Relation::StartStart:
// I can't start before my successor, so
// I can't finish later than it's starttime + my duration
t += duration(t - it.current()->lag(), use, false);
break;
case Relation::FinishFinish:
t = it.current()->child()->endTime() - it.current()->lag();
break;
default:
t -= it.current()->lag();
break;
}
if (!time.isValid() || t < time)
time = t;
}
return time;
}
DateTime Task::scheduleBackward(const DateTime &latest, int use) {
//kdDebug()<<k_funcinfo<<m_name<<": latest="<<latest<<endl;
if (m_currentSchedule == 0) {
return DateTime();
}
Schedule *cs = m_currentSchedule;
if (m_visitedBackward) {
return cs->startTime;
}
cs->notScheduled = false;
cs->endTime = latest < cs->latestFinish ? latest : cs->latestFinish;
// First, calculate all my own successors
DateTime time = scheduleSuccessors(dependChildNodes(), use);
if (time.isValid() && time < cs->endTime) {
cs->endTime = time;
}
// Then my parents
time = scheduleSuccessors(m_childProxyRelations, use);
if (time.isValid() && time < cs->endTime) {
cs->endTime = time;
}
if (type() == Node::Type_Task) {
cs->duration = m_effort->effort(use);
switch (m_constraint) {
case Node::ASAP: {
// cs->endTime calculated above
//kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" early="<<cs->earliestStart<<endl;
cs->endTime = workFinishBefore(cs->endTime);
cs->duration = duration(cs->earliestStart, use, false);
cs->startTime = cs->earliestStart;
DateTime e = cs->startTime + cs->duration;
if (e > cs->endTime) {
cs->schedulingError = true;
}
cs->endTime = e;
//kdDebug()<<k_funcinfo<<m_name<<": start="<<cs->startTime<<"+"<<cs->duration.toString()<<"="<<e<<" -> end="<<cs->endTime<<endl;
break;
}
case Node::ALAP:
// cs->endTime calculated above
//kdDebug()<<k_funcinfo<<m_name<<": end="<<cs->endTime<<" late="<<cs->latestFinish<<endl;
cs->endTime = workFinishBefore(cs->endTime);
cs->duration = duration(cs->endTime, use, true);
cs->startTime = cs->endTime - cs->duration;
//kdDebug()<<k_funcinfo<<m_name<<": lateStart="<<cs->startTime<<endl;
break;
case Node::StartNotEarlier:
// cs->endTime calculated above
//kdDebug()<<"StartNotEarlier="<<m_constraintStartTime.toString()<<" "<<cs->endTime.toString()<<endl;
cs->endTime = workFinishBefore(cs->endTime);
cs->duration = duration(cs->endTime, use, true);
cs->startTime = cs->endTime - cs->duration;
if (cs->startTime < m_constraintStartTime) {
cs->schedulingError = true;
cs->startTime = m_constraintStartTime;
cs->duration = duration(cs->startTime, use, false);
cs->endTime = cs->startTime + cs->duration;
}
break;
case Node::FinishNotLater:
// cs->endTime calculated above
//kdDebug()<<"FinishNotLater="<<m_constraintEndTime.toString()<<" "<<cs->endTime.toString()<<endl;
if (cs->endTime > m_constraintEndTime) {
cs->schedulingError = true;
cs->endTime = m_constraintEndTime;
}
cs->endTime = workFinishBefore(cs->endTime);
cs->duration = duration(cs->endTime, use, true);
cs->startTime = cs->endTime - cs->duration;
break;
case Node::MustStartOn:
// cs->endTime calculated above
//kdDebug()<<"MustStartOn="<<m_constraintStartTime.toString()<<" "<<cs->startTime.toString()<<endl;
if (m_constraintStartTime < cs->earliestStart ||
m_constraintStartTime > cs->latestFinish - m_durationBackward) {
cs->schedulingError = true;
}
cs->startTime = m_constraintStartTime;
cs->duration = duration(cs->startTime, use, false);
cs->endTime = cs->startTime + cs->duration;
break;
case Node::MustFinishOn:
// cs->endTime calculated above
//kdDebug()<<"MustFinishOn="<<m_constraintEndTime.toString()<<" "<<cs->startTime.toString()<<endl;
if (m_constraintEndTime > cs->latestFinish ||
m_constraintEndTime < cs->earliestStart + m_durationForward) {
cs->schedulingError = true;
}
cs->endTime = m_constraintEndTime;
cs->duration = duration(cs->endTime, use, true);
cs->startTime = cs->endTime - cs->duration;
break;
case Node::FixedInterval: {
// cs->endTime calculated above
//kdDebug()<<k_funcinfo<<"FixedInterval="<<m_constraintEndTime<<" "<<cs->endTime<<endl;
if (m_constraintEndTime > cs->endTime) {
cs->schedulingError = true;
//kdDebug()<<k_funcinfo<<"FixedInterval error: "<<m_constraintEndTime<<" > "<<cs->endTime<<endl;
}
cs->startTime = m_constraintStartTime;
cs->endTime = m_constraintEndTime;
cs->duration = cs->endTime - cs->startTime;
cs->workStartTime = m_constraintStartTime;
cs->workEndTime = m_constraintEndTime;
break;
}
default:
break;
}
if (m_requests) {
m_requests->reserve(cs->startTime, cs->duration);
}
} else if (type() == Node::Type_Milestone) {
switch (m_constraint) {
case Node::ASAP:
cs->startTime = cs->earliestStart;
cs->endTime = cs->earliestStart;
break;
case Node::ALAP:
cs->startTime = cs->latestFinish;
cs->endTime = cs->latestFinish;
break;
case Node::MustStartOn:
case Node::FixedInterval:
if (m_constraintStartTime < cs->earliestStart ||
m_constraintStartTime > cs->endTime) {
cs->schedulingError = true;
}
cs->startTime = cs->earliestStart;
cs->endTime = cs->earliestStart;
break;
case Node::MustFinishOn:
if (m_constraintEndTime < cs->earliestStart ||
m_constraintEndTime > cs->endTime) {
cs->schedulingError = true;
}
cs->startTime = cs->earliestStart;
cs->endTime = cs->earliestStart;
break;
case Node::StartNotEarlier:
if (m_constraintStartTime > cs->endTime) {
cs->schedulingError = true;
}
cs->startTime = cs->endTime;
break;
case Node::FinishNotLater:
if (m_constraintEndTime < cs->endTime) {
cs->schedulingError = true;
}
cs->startTime = cs->endTime;
break;
default:
break;
}
cs->duration = Duration::zeroDuration;
} else if (type() == Node::Type_Summarytask) {
//shouldn't come here
cs->startTime = cs->endTime;
cs->duration = cs->endTime - cs->startTime;
kdWarning()<<k_funcinfo<<"Summarytasks should not be calculated here: "<<m_name<<endl;
}
//kdDebug()<<k_funcinfo<<m_name<<": "<<cs->startTime.toString()<<" : "<<cs->endTime.toString()<<endl;
m_visitedBackward = true;
return cs->startTime;
}
void Task::adjustSummarytask() {
if (m_currentSchedule == 0)
return;
if (type() == Type_Summarytask) {
DateTime start = m_currentSchedule->latestFinish;
DateTime end = m_currentSchedule->earliestStart;
QPtrListIterator<Node> it(m_nodes);
for (; it.current(); ++it) {
it.current()->adjustSummarytask();
if (it.current()->startTime() < start)
start = it.current()->startTime();
if (it.current()->endTime() > end)
end = it.current()->endTime();
}
m_currentSchedule->startTime = start;
m_currentSchedule->endTime = end;
m_currentSchedule->duration = end - start;
m_currentSchedule->notScheduled = false;
//kdDebug()<<k_funcinfo<<cs->name<<": "<<m_currentSchedule->startTime.toString()<<" : "<<m_currentSchedule->endTime.toString()<<endl;
}
}
Duration Task::calcDuration(const DateTime &time, const Duration &effort, bool backward) {
//kdDebug()<<"--------> calcDuration "<<(backward?"(B) ":"(F) ")<<m_name<<" time="<<time<<" effort="<<effort.toString(Duration::Format_Day)<<endl;
// Allready checked: m_effort, m_currentSchedule and time.
Duration dur = effort; // use effort as default duration
if (m_effort->type() == Effort::Type_Effort) {
if (m_requests == 0 || m_requests->isEmpty()) {
m_currentSchedule->resourceError = true;
return effort;
}
dur = m_requests->duration(time, effort, backward);
if (dur == Duration::zeroDuration) {
kdWarning()<<k_funcinfo<<"zero duration: Resource not available"<<endl;
m_currentSchedule->resourceNotAvailable = true;
dur = effort; //???
}
return dur;
}
if (m_effort->type() == Effort::Type_FixedDuration) {
//TODO: Different types of fixed duration
return dur; //
}
kdError()<<k_funcinfo<<"Unsupported effort type: "<<m_effort->type()<<endl;
return dur;
}
void Task::clearProxyRelations() {
m_parentProxyRelations.clear();
m_childProxyRelations.clear();
}
void Task::addParentProxyRelations(QPtrList<Relation> &list) {
//kdDebug()<<k_funcinfo<<m_name<<endl;
if (type() == Type_Summarytask) {
// propagate to my children
//kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
QPtrListIterator<Node> nodes = m_nodes;
for (; nodes.current(); ++nodes) {
nodes.current()->addParentProxyRelations(list);
nodes.current()->addParentProxyRelations(dependParentNodes());
}
} else {
// add 'this' as child relation to the relations parent
//kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
QPtrListIterator<Relation> it = list;
for (; it.current(); ++it) {
it.current()->parent()->addChildProxyRelation(this, it.current());
// add a parent relation to myself
addParentProxyRelation(it.current()->parent(), it.current());
}
}
}
void Task::addChildProxyRelations(QPtrList<Relation> &list) {
//kdDebug()<<k_funcinfo<<m_name<<endl;
if (type() == Type_Summarytask) {
// propagate to my children
//kdDebug()<<k_funcinfo<<m_name<<" is summary task"<<endl;
QPtrListIterator<Node> nodes = m_nodes;
for (; nodes.current(); ++nodes) {
nodes.current()->addChildProxyRelations(list);
nodes.current()->addChildProxyRelations(dependChildNodes());
}
} else {
// add 'this' as parent relation to the relations child
//kdDebug()<<k_funcinfo<<m_name<<" is not summary task"<<endl;
QPtrListIterator<Relation> it = list;
for (; it.current(); ++it) {
it.current()->child()->addParentProxyRelation(this, it.current());
// add a child relation to myself
addChildProxyRelation(it.current()->child(), it.current());
}
}
}
void Task::addParentProxyRelation(Node *node, const Relation *rel) {
if (node->type() != Type_Summarytask) {
if (type() == Type_Summarytask) {
//kdDebug()<<"Add parent proxy from my children "<<m_name<<" to "<<node->name()<<endl;
QPtrListIterator<Node> nodes = m_nodes;
for (; nodes.current(); ++nodes) {
nodes.current()->addParentProxyRelation(node, rel);
}
} else {
//kdDebug()<<"Add parent proxy from "<<node->name()<<" to (me) "<<m_name<<endl;
m_parentProxyRelations.append(new ProxyRelation(node, this, rel->type(), rel->lag()));
}
}
}
void Task::addChildProxyRelation(Node *node, const Relation *rel) {
if (node->type() != Type_Summarytask) {
if (type() == Type_Summarytask) {
//kdDebug()<<"Add child proxy from my children "<<m_name<<" to "<<node->name()<<endl;
QPtrListIterator<Node> nodes = m_nodes;
for (; nodes.current(); ++nodes) {
nodes.current()->addChildProxyRelation(node, rel);
}
} else {
//kdDebug()<<"Add child proxy from (me) "<<m_name<<" to "<<node->name()<<endl;
m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag()));
}
}
}
bool Task::isEndNode() const {
QPtrListIterator<Relation> it = m_dependChildNodes;
for (; it.current(); ++it) {
if (it.current()->type() == Relation::FinishStart)
return false;
}
QPtrListIterator<Relation> pit = m_childProxyRelations;
for (; pit.current(); ++pit) {
if (pit.current()->type() == Relation::FinishStart)
return false;
}
return true;
}
bool Task::isStartNode() const {
QPtrListIterator<Relation> it = m_dependParentNodes;
for (; it.current(); ++it) {
if (it.current()->type() == Relation::FinishStart ||
it.current()->type() == Relation::StartStart)
return false;
}
QPtrListIterator<Relation> pit = m_parentProxyRelations;
for (; pit.current(); ++pit) {
if (pit.current()->type() == Relation::FinishStart ||
pit.current()->type() == Relation::StartStart)
return false;
}
return true;
}
DateTime Task::workStartTime() const {
if (m_currentSchedule == 0)
return DateTime();
if (m_requests)
return m_currentSchedule->workStartTime;
return m_currentSchedule->startTime;
}
DateTime Task::workEndTime() const {
if (m_currentSchedule == 0)
return DateTime();
return m_currentSchedule->endTime;
}
DateTime Task::workStartAfter(const DateTime &dt) {
if (m_requests) {
DateTime t = m_requests->availableAfter(dt);
return t.isValid() ? t : dt;
}
return dt;
}
DateTime Task::workFinishBefore(const DateTime &dt) {
if (m_requests) {
return m_requests->availableBefore(dt);
}
return dt;
}
Duration Task::positiveFloat() {
if (m_currentSchedule == 0 ||
m_currentSchedule->schedulingError ||
effortMetError()) {
return Duration::zeroDuration;
}
Duration f;
if (type() == Node::Type_Milestone) {
if (m_currentSchedule->startTime < m_currentSchedule->latestFinish) {
f = m_currentSchedule->latestFinish - m_currentSchedule->startTime;
}
} else if (m_effort->type() == Effort::Type_FixedDuration) {
if (m_currentSchedule->endTime.isValid()) {
if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
}
}
} else {
if (m_currentSchedule->workEndTime.isValid())
if (m_currentSchedule->workEndTime < m_currentSchedule->latestFinish) {
f = m_currentSchedule->latestFinish - m_currentSchedule->workEndTime;
} else if (m_currentSchedule->endTime.isValid()) {
if (m_currentSchedule->endTime < m_currentSchedule->latestFinish) {
f = m_currentSchedule->latestFinish - m_currentSchedule->endTime;
}
}
}
//kdDebug()<<k_funcinfo<<f.toString()<<endl;
return f;
}
bool Task::isCritical() {
Schedule *cs = m_currentSchedule;
if (cs == 0) {
return false;
}
return cs->earliestStart >= cs->startTime && cs->latestFinish <= cs->endTime;
}
bool Task::calcCriticalPath(bool fromEnd) {
if (m_currentSchedule == 0)
return false;
//kdDebug()<<k_funcinfo<<m_name<<" fromEnd="<<fromEnd<<" cp="<<m_currentSchedule->inCriticalPath<<endl;
if (m_currentSchedule->inCriticalPath) {
return true; // path allready calculated
}
if (!isCritical()) {
return false;
}
if (fromEnd) {
if (isEndNode()) {
m_currentSchedule->inCriticalPath = true;
//kdDebug()<<k_funcinfo<<m_name<<" end node"<<endl;
return true;
}
QPtrListIterator<Relation> it(m_childProxyRelations);
for (; it.current(); ++it) {
if (it.current()->child()->calcCriticalPath(fromEnd)) {
m_currentSchedule->inCriticalPath = true;
}
}
QPtrListIterator<Relation> pit(m_dependChildNodes);
for (; pit.current(); ++pit) {
if (pit.current()->child()->calcCriticalPath(fromEnd)) {
m_currentSchedule->inCriticalPath = true;
}
}
} else {
if (isStartNode()) {
m_currentSchedule->inCriticalPath = true;
//kdDebug()<<k_funcinfo<<m_name<<" start node"<<endl;
return true;
}
QPtrListIterator<Relation> it(m_parentProxyRelations);
for (; it.current(); ++it) {
if (it.current()->parent()->calcCriticalPath(fromEnd)) {
m_currentSchedule->inCriticalPath = true;
}
}
QPtrListIterator<Relation> pit(m_dependParentNodes);
for (; pit.current(); ++pit) {
if (pit.current()->parent()->calcCriticalPath(fromEnd)) {
m_currentSchedule->inCriticalPath = true;
}
}
}
//kdDebug()<<k_funcinfo<<m_name<<" return cp="<<m_currentSchedule->inCriticalPath<<endl;
return m_currentSchedule->inCriticalPath;
}
void Task::setCurrentSchedule(long id) {
setCurrentSchedulePtr(findSchedule(id));
Node::setCurrentSchedule(id);
}
bool Task::effortMetError() const {
if (m_currentSchedule->notScheduled) {
return false;
}
return m_currentSchedule->plannedEffort() < effort()->effort(static_cast<Effort::Use>(static_cast<int>(m_currentSchedule->type())));
}
#ifndef NDEBUG
void Task::printDebug(bool children, QCString indent) {
kdDebug()<<indent<<"+ Task node: "<<name()<<" type="<<type()<<endl;
indent += "! ";
kdDebug()<<indent<<"Requested resources (total): "<<units()<<"%"<<endl;
kdDebug()<<indent<<"Requested resources (work): "<<workUnits()<<"%"<<endl;
if (m_requests)
m_requests->printDebug(indent);
Node::printDebug(children, indent);
}
#endif
} //KPlato namespace