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.
1543 lines
58 KiB
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
|