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/kptnode.cc

1012 lines
30 KiB

/* This file is part of the KDE project
Copyright (C) 2001 Thomas zander <zander@kde.org>
Copyright (C) 2004, 2005 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 "kptnode.h"
#include "kptappointment.h"
#include "kptaccount.h"
#include "kptwbsdefinition.h"
#include "kptresource.h"
#include "kptschedule.h"
#include <qptrlist.h>
#include <qdom.h>
#include <klocale.h>
#include <kdebug.h>
namespace KPlato
{
Node::Node(Node *parent) : m_nodes(), m_dependChildNodes(), m_dependParentNodes() {
//kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
m_parent = parent;
init();
m_id = QString(); // Not mapped
}
Node::Node(Node &node, Node *parent)
: m_nodes(),
m_dependChildNodes(),
m_dependParentNodes() {
//kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
m_parent = parent;
init();
m_name = node.name();
m_leader = node.leader();
m_description = node.description();
m_constraint = (ConstraintType) node.constraint();
m_constraintStartTime = node.constraintStartTime();
m_constraintEndTime = node.constraintEndTime();
m_dateOnlyStartDate = node.startDate();
m_dateOnlyEndDate = node.endDate();
m_runningAccount = node.runningAccount();
m_startupAccount = node.startupAccount();
m_shutdownAccount = node.shutdownAccount();
m_startupCost = node.startupCost();
m_shutdownCost = node.shutdownCost();
m_schedules.setAutoDelete(node.m_schedules.autoDelete());
}
Node::~Node() {
if (findNode() == this) {
removeId(); // only remove myself (I may be just a working copy)
}
Relation *rel = 0;
while ((rel = m_dependParentNodes.getFirst())) {
delete rel;
}
while ((rel = m_dependChildNodes.getFirst())) {
delete rel;
}
if (m_runningAccount)
m_runningAccount->removeRunning(*this);
if (m_startupAccount)
m_startupAccount->removeStartup(*this);
if (m_shutdownAccount)
m_shutdownAccount->removeShutdown(*this);
}
void Node::init() {
m_currentSchedule = 0;
m_nodes.setAutoDelete(true);
m_name="";
m_constraint = Node::ASAP;
m_effort = 0;
m_visitedForward = false;
m_visitedBackward = false;
m_dateOnlyStartDate = m_dateOnlyEndDate = QDate::currentDate();
m_dateOnlyDuration.addDays(1);
m_runningAccount = 0;
m_startupAccount = 0;
m_shutdownAccount = 0;
m_startupCost = 0.0;
m_shutdownCost = 0.0;
}
Node *Node::projectNode() {
if ((type() == Type_Project) || (type() == Type_Subproject)) {
return this;
}
if (m_parent)
return m_parent->projectNode();
kdError()<<k_funcinfo<<"Ooops, no parent and no project found"<<endl;
return 0;
}
void Node::delChildNode( Node *node, bool remove) {
//kdDebug()<<k_funcinfo<<"find="<<m_nodes.findRef(node)<<endl;
if ( m_nodes.findRef(node) != -1 ) {
if(remove)
m_nodes.remove();
else
m_nodes.take();
}
node->setParent(0);
}
void Node::delChildNode( int number, bool remove) {
Node *n = m_nodes.at(number);
//kdDebug()<<k_funcinfo<<(n?n->id():"null")<<" : "<<(n?n->name():"")<<endl;
if(remove)
m_nodes.remove(number);
else
m_nodes.take(number);
if (n) {
n->setParent(0);
}
}
void Node::insertChildNode( unsigned int index, Node *node) {
//kdDebug()<<k_funcinfo<<"insert id="<<node->id()<<": "<<node->name()<<endl;
m_nodes.insert(index,node);
node->setParent(this);
}
void Node::addChildNode( Node *node, Node *after) {
//kdDebug()<<k_funcinfo<<endl;
int index = m_nodes.findRef(after);
if (index == -1) {
//kdDebug()<<k_funcinfo<<"append id="<<node->id()<<": "<<node->name()<<endl;
m_nodes.append(node);
node->setParent(this);
return;
}
//kdDebug()<<k_funcinfo<<"insert id="<<node->id()<<": "<<node->name()<<endl;
m_nodes.insert(index+1, node);
node->setParent(this);
}
int Node::findChildNode( Node* node )
{
return m_nodes.findRef( node );
}
const Node* Node::getChildNode(int number) const {
// Work around missing const at() method in QPtrList
const QPtrList<Node> &nodes = m_nodes;
return (const_cast<QPtrList<Node> &>(nodes)).at(number);
}
Duration *Node::getDelay() {
/* TODO
Calculate the delay of this node. Use the calculated startTime and the setted startTime.
*/
return 0L;
}
void Node::addDependChildNode( Node *node, Relation::Type p) {
addDependChildNode(node,p,Duration());
}
void Node::addDependChildNode( Node *node, Relation::Type p, Duration lag) {
Relation *relation = new Relation(this, node, p, lag);
if (node->addDependParentNode(relation))
m_dependChildNodes.append(relation);
else
delete relation;
}
void Node::insertDependChildNode( unsigned int index, Node *node, Relation::Type p) {
Relation *relation = new Relation(this, node, p, Duration());
if (node->addDependParentNode(relation))
m_dependChildNodes.insert(index, relation);
else
delete relation;
}
bool Node::addDependChildNode( Relation *relation) {
if(m_dependChildNodes.findRef(relation) != -1)
return false;
m_dependChildNodes.append(relation);
return true;
}
// These delDepend... methods look suspicious to me, can someone review?
void Node::delDependChildNode( Node *node, bool remove) {
if ( m_nodes.findRef(node) != -1 ) {
if(remove)
m_dependChildNodes.remove();
else
m_dependChildNodes.take();
}
}
void Node::delDependChildNode( Relation *rel, bool remove) {
if ( m_dependChildNodes.findRef(rel) != -1 ) {
if(remove)
m_dependChildNodes.remove();
else
m_dependChildNodes.take();
}
}
void Node::delDependChildNode( int number, bool remove) {
if(remove)
m_dependChildNodes.remove(number);
else
m_dependChildNodes.take(number);
}
void Node::takeDependChildNode(Relation *rel) {
if (m_dependChildNodes.findRef(rel) != -1) {
m_dependChildNodes.take();
}
}
void Node::addDependParentNode( Node *node, Relation::Type p) {
addDependParentNode(node,p,Duration());
}
void Node::addDependParentNode( Node *node, Relation::Type p, Duration lag) {
Relation *relation = new Relation(node, this, p, lag);
if (node->addDependChildNode(relation))
m_dependParentNodes.append(relation);
else
delete relation;
}
void Node::insertDependParentNode( unsigned int index, Node *node, Relation::Type p) {
Relation *relation = new Relation(this, node, p, Duration());
if (node->addDependChildNode(relation))
m_dependParentNodes.insert(index,relation);
else
delete relation;
}
bool Node::addDependParentNode( Relation *relation) {
if(m_dependParentNodes.findRef(relation) != -1)
return false;
m_dependParentNodes.append(relation);
return true;
}
// These delDepend... methods look suspicious to me, can someone review?
void Node::delDependParentNode( Node *node, bool remove) {
if ( m_nodes.findRef(node) != -1 ) {
if(remove)
m_dependParentNodes.remove();
else
m_dependParentNodes.take();
}
}
void Node::delDependParentNode( Relation *rel, bool remove) {
if ( m_dependParentNodes.findRef(rel) != -1 ) {
if(remove)
m_dependParentNodes.remove();
else
m_dependParentNodes.take();
}
}
void Node::delDependParentNode( int number, bool remove) {
if(remove)
m_dependParentNodes.remove(number);
else
m_dependParentNodes.take(number);
}
void Node::takeDependParentNode(Relation *rel) {
if (m_dependParentNodes.findRef(rel) != -1) {
rel = m_dependParentNodes.take();
}
}
bool Node::isParentOf(Node *node) {
if (m_nodes.findRef(node) != -1)
return true;
QPtrListIterator<Node> nit(childNodeIterator());
for ( ; nit.current(); ++nit ) {
if (nit.current()->isParentOf(node))
return true;
}
return false;
}
Relation *Node::findParentRelation(Node *node) {
for (int i=0; i<numDependParentNodes(); i++) {
Relation *rel = getDependParentNode(i);
if (rel->parent() == node)
return rel;
}
return (Relation *)0;
}
Relation *Node::findChildRelation(Node *node) {
for (int i=0; i<numDependChildNodes(); i++) {
Relation *rel = getDependChildNode(i);
if (rel->child() == node)
return rel;
}
return (Relation *)0;
}
Relation *Node::findRelation(Node *node) {
Relation *rel = findParentRelation(node);
if (!rel)
rel = findChildRelation(node);
return rel;
}
bool Node::isDependChildOf(Node *node) {
//kdDebug()<<k_funcinfo<<" '"<<m_name<<"' checking against '"<<node->name()<<"'"<<endl;
for (int i=0; i<numDependParentNodes(); i++) {
Relation *rel = getDependParentNode(i);
if (rel->parent() == node)
return true;
if (rel->parent()->isDependChildOf(node))
return true;
}
return false;
}
Duration Node::duration(const DateTime &time, int use, bool backward) {
//kdDebug()<<k_funcinfo<<endl;
// TODO: handle risc
if (!time.isValid()) {
kdError()<<k_funcinfo<<"Time is invalid"<<endl;
return Duration::zeroDuration;
}
if (m_effort == 0) {
kdError()<<k_funcinfo<<"m_effort == 0"<<endl;
return Duration::zeroDuration;
}
if (m_currentSchedule == 0) {
return Duration::zeroDuration;
kdError()<<k_funcinfo<<"No current schedule"<<endl;
}
kdDebug()<<k_funcinfo<<m_name<<": Use="<<use<<endl;
return calcDuration(time, m_effort->effort(use), backward);
}
void Node::makeAppointments() {
QPtrListIterator<Node> nit(m_nodes);
for ( ; nit.current(); ++nit ) {
nit.current()->makeAppointments();
}
}
void Node::calcResourceOverbooked() {
QPtrListIterator<Node> nit(m_nodes);
for ( ; nit.current(); ++nit ) {
nit.current()->calcResourceOverbooked();
}
}
QStringList Node::overbookedResources() const {
return m_currentSchedule ? m_currentSchedule->overbookedResources() : QStringList();
}
void Node::saveRelations(QDomElement &element) const {
QPtrListIterator<Relation> it(m_dependChildNodes);
for (; it.current(); ++it) {
it.current()->save(element);
}
QPtrListIterator<Node> nodes(m_nodes);
for ( ; nodes.current(); ++nodes ) {
nodes.current()->saveRelations(element);
}
}
void Node::setConstraint(QString &type) {
// Do not i18n these, they are used in load()
if (type == "ASAP")
setConstraint(ASAP);
else if (type == "ALAP")
setConstraint(ALAP);
else if (type == "StartNotEarlier")
setConstraint(StartNotEarlier);
else if (type == "FinishNotLater")
setConstraint(FinishNotLater);
else if (type == "MustStartOn")
setConstraint(MustStartOn);
else if (type == "MustFinishOn")
setConstraint(MustFinishOn);
else if (type == "FixedInterval")
setConstraint(FixedInterval);
else
setConstraint(ASAP); // default
}
QString Node::constraintToString() const {
// Do not i18n these, they are used in save()
if (m_constraint == ASAP)
return QString("ASAP");
else if (m_constraint == ALAP)
return QString("ALAP");
else if (m_constraint == StartNotEarlier)
return QString("StartNotEarlier");
else if (m_constraint == FinishNotLater)
return QString("FinishNotLater");
else if (m_constraint == MustStartOn)
return QString("MustStartOn");
else if (m_constraint == MustFinishOn)
return QString("MustFinishOn");
else if (m_constraint == FixedInterval)
return QString("FixedInterval");
return QString();
}
void Node::propagateEarliestStart(DateTime &time) {
if (m_currentSchedule == 0)
return;
m_currentSchedule->earliestStart = time;
//kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->earliestStart.toString()<<endl;
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->propagateEarliestStart(time);
}
}
void Node::propagateLatestFinish(DateTime &time) {
if (m_currentSchedule == 0)
return;
m_currentSchedule->latestFinish = time;
//kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->latestFinish<<endl;
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->propagateLatestFinish(time);
}
}
void Node::moveEarliestStart(DateTime &time) {
if (m_currentSchedule == 0)
return;
if (m_currentSchedule->earliestStart < time)
m_currentSchedule->earliestStart = time;
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->moveEarliestStart(time);
}
}
void Node::moveLatestFinish(DateTime &time) {
if (m_currentSchedule == 0)
return;
if (m_currentSchedule->latestFinish > time)
m_currentSchedule->latestFinish = time;
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->moveLatestFinish(time);
}
}
void Node::initiateCalculation(Schedule &sch) {
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->initiateCalculation(sch);
}
}
void Node::resetVisited() {
m_visitedForward = false;
m_visitedBackward = false;
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->resetVisited();
}
}
Node *Node::siblingBefore() {
//kdDebug()<<k_funcinfo<<endl;
if (getParent())
return getParent()->childBefore(this);
return 0;
}
Node *Node::childBefore(Node *node) {
//kdDebug()<<k_funcinfo<<endl;
int index = m_nodes.findRef(node);
if (index > 0){
return m_nodes.at(index-1);
}
return 0;
}
Node *Node::siblingAfter() {
//kdDebug()<<k_funcinfo<<endl;
if (getParent())
return getParent()->childAfter(this);
return 0;
}
Node *Node::childAfter(Node *node)
{
//kdDebug()<<k_funcinfo<<endl;
uint index = m_nodes.findRef(node);
if (index < m_nodes.count()-1) {
return m_nodes.at(index+1); }
return 0;
}
bool Node::moveChildUp(Node* node)
{
if (findChildNode(node) == -1)
return false; // not my node!
Node *sib = node->siblingBefore();
if (!sib)
return false;
sib = sib->siblingBefore();
delChildNode(node, false);
if (sib) {
addChildNode(node, sib);
} else {
insertChildNode(0, node);
}
return true;
}
bool Node::moveChildDown(Node* node)
{
if (findChildNode(node) == -1)
return false; // not my node!
Node *sib = node->siblingAfter();
if (!sib)
return false;
delChildNode(node, false);
addChildNode(node, sib);
return true;
}
bool Node::legalToLink(Node *node) {
Node *p = projectNode();
if (p)
return p->legalToLink(this, node);
return false;
}
bool Node::isEndNode() const {
return m_dependChildNodes.isEmpty();
}
bool Node::isStartNode() const {
return m_dependParentNodes.isEmpty();
}
bool Node::setId(QString id) {
//kdDebug()<<k_funcinfo<<id<<endl;
if (id.isEmpty()) {
kdError()<<k_funcinfo<<"id is empty"<<endl;
m_id = id;
return false;
}
if (!m_id.isEmpty()) {
Node *n = findNode();
if (n == this) {
//kdDebug()<<k_funcinfo<<"My id found, remove it"<<endl;
removeId();
} else if (n) {
//Hmmm, shouldn't happen
kdError()<<k_funcinfo<<"My id '"<<m_id<<"' already used for different node: "<<n->name()<<endl;
}
}
if (findNode(id)) {
kdError()<<k_funcinfo<<"id '"<<id<<"' is already used for different node: "<<findNode(id)->name()<<endl;
m_id = QString(); // hmmm
return false;
}
m_id = id;
insertId(id);
//kdDebug()<<k_funcinfo<<m_name<<": inserted id="<<id<<endl;
return true;
}
void Node::setStartTime(DateTime startTime) {
if (m_currentSchedule)
m_currentSchedule->startTime = startTime;
m_dateOnlyStartDate = startTime.date();
}
void Node::setEndTime(DateTime endTime) {
if (m_currentSchedule)
m_currentSchedule->endTime = endTime;
m_dateOnlyEndDate = endTime.date();
if (endTime.time().isNull() && m_dateOnlyEndDate > m_dateOnlyStartDate)
m_dateOnlyEndDate = m_dateOnlyEndDate.addDays(-1);
}
void Node::saveAppointments(QDomElement &element, long id) const {
//kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
QPtrListIterator<Node> it(m_nodes);
for (; it.current(); ++it ) {
it.current()->saveAppointments(element, id);
}
}
QPtrList<Appointment> Node::appointments() {
QPtrList<Appointment> lst;
if (m_currentSchedule)
lst = m_currentSchedule->appointments();
return lst;
}
// Appointment *Node::findAppointment(Resource *resource) {
// if (m_currentSchedule)
// return m_currentSchedule->findAppointment(resource);
// return 0;
// }
bool Node::addAppointment(Appointment *appointment) {
if (m_currentSchedule)
return m_currentSchedule->add(appointment);
return false;
}
bool Node::addAppointment(Appointment *appointment, Schedule &main) {
//kdDebug()<<k_funcinfo<<this<<endl;
Schedule *s = findSchedule(main.id());
if (s == 0) {
s = createSchedule(&main);
}
appointment->setNode(s);
return s->add(appointment);
}
void Node::addAppointment(ResourceSchedule *resource, DateTime &start, DateTime &end, double load) {
Schedule *node = findSchedule(resource->id());
if (node == 0) {
node = createSchedule(resource->parent());
}
node->addAppointment(resource, start, end, load);
}
void Node::takeSchedule(const Schedule *schedule) {
if (schedule == 0)
return;
if (m_currentSchedule == schedule)
m_currentSchedule = 0;
m_schedules.take(schedule->id());
}
void Node::addSchedule(Schedule *schedule) {
if (schedule == 0)
return;
m_schedules.replace(schedule->id(), schedule);
}
Schedule *Node::createSchedule(QString name, Schedule::Type type, long id) {
//kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl;
NodeSchedule *sch = new NodeSchedule(this, name, type, id);
addSchedule(sch);
return sch;
}
Schedule *Node::createSchedule(Schedule *parent) {
//kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl;
NodeSchedule *sch = new NodeSchedule(parent, this);
addSchedule(sch);
return sch;
}
Schedule *Node::findSchedule(const QString name, const Schedule::Type type) const {
QIntDictIterator<Schedule> it = m_schedules;
for (; it.current(); ++it) {
if (!it.current()->isDeleted() &&
it.current()->name() == name && it.current()->type() == type)
return it.current();
}
return 0;
}
Schedule *Node::findSchedule(const Schedule::Type type) const {
//kdDebug()<<k_funcinfo<<m_name<<" find type="<<type<<" nr="<<m_schedules.count()<<endl;
QIntDictIterator<Schedule> it = m_schedules;
for (; it.current(); ++it) {
if (!it.current()->isDeleted() && it.current()->type() == type) {
return it.current();
}
}
return 0;
}
void Node::setScheduleDeleted(long id, bool on) {
Schedule *ns = findSchedule(id);
if (ns == 0) {
kdError()<<k_funcinfo<<m_name<<" Could not find schedule with id="<<id<<endl;
} else {
ns->setDeleted(on);
}
}
void Node::setParentSchedule(Schedule *sch) {
Schedule *s = findSchedule(sch->id());
if (s) {
s->setParent(sch);
}
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->setParentSchedule(sch);
}
}
bool Node::calcCriticalPath(bool fromEnd) {
if (m_currentSchedule == 0)
return false;
//kdDebug()<<k_funcinfo<<m_name<<endl;
if (!isCritical()) {
return false;
}
if (!fromEnd && isStartNode()) {
m_currentSchedule->inCriticalPath = true;
return true;
}
if (fromEnd && isEndNode()) {
m_currentSchedule->inCriticalPath = true;
return true;
}
QPtrListIterator<Relation> pit(m_dependParentNodes);
for (; pit.current(); ++pit) {
if (pit.current()->parent()->calcCriticalPath(fromEnd)) {
m_currentSchedule->inCriticalPath = true;
}
}
return m_currentSchedule->inCriticalPath;
}
int Node::level() {
Node *n = getParent();
return n ? n->level() + 1 : 0;
}
void Node::generateWBS(int count, WBSDefinition &def, QString wbs) {
m_wbs = wbs + def.code(count, level());
//kdDebug()<<k_funcinfo<<m_name<<" wbs: "<<m_wbs<<endl;
QString w = wbs + def.wbs(count, level());
QPtrListIterator<Node> it = m_nodes;
for (int i=0; it.current(); ++it) {
it.current()->generateWBS(++i, def, w);
}
}
void Node::setCurrentSchedule(long id) {
QPtrListIterator<Node> it = m_nodes;
for (; it.current(); ++it) {
it.current()->setCurrentSchedule(id);
}
//kdDebug()<<k_funcinfo<<m_name<<" id: "<<id<<"="<<m_currentSchedule<<endl;
}
////////////////////////// Effort /////////////////////////////////
Effort::Effort( Duration e, Duration p, Duration o) {
m_expectedEffort = e;
m_pessimisticEffort = p;
m_optimisticEffort = o;
m_type = Type_Effort;
m_risktype = Risk_None;
}
Effort::Effort(const Effort &effort) {
set(effort.expected(), effort.pessimistic(), effort.optimistic());
setType(effort.type());
setRisktype(effort.risktype());
}
Effort::~Effort() {
}
const Effort Effort::zeroEffort( Duration::zeroDuration,
Duration::zeroDuration,
Duration::zeroDuration );
void Effort::set( Duration e, Duration p, Duration o ) {
m_expectedEffort = e;
m_pessimisticEffort = (p == Duration::zeroDuration) ? e : p;
m_optimisticEffort = (o == Duration::zeroDuration) ? e : o;
//kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.toString()<<endl;
}
void Effort::set( int e, int p, int o ) {
m_expectedEffort = Duration(e);
m_pessimisticEffort = (p < 0) ? Duration(e) : Duration(p);
m_optimisticEffort = (o < 0) ? Duration(e) : Duration(o);
//kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.toString()<<endl;
//kdDebug()<<k_funcinfo<<" Optimistic: "<<m_optimisticEffort.toString()<<endl;
//kdDebug()<<k_funcinfo<<" Pessimistic: "<<m_pessimisticEffort.toString()<<endl;
//kdDebug()<<k_funcinfo<<" Expected: "<<m_expectedEffort.duration()<<" manseconds"<<endl;
}
//TODO (?): effort is not really a duration, should maybe not use Duration for storage
void Effort::set(unsigned days, unsigned hours, unsigned minutes) {
Duration dur(days, hours, minutes);
set(dur);
//kdDebug()<<k_funcinfo<<"effort="<<dur.toString()<<endl;
}
void Effort::expectedEffort(unsigned *days, unsigned *hours, unsigned *minutes) {
m_expectedEffort.get(days, hours, minutes);
}
Duration Effort::variance() const {
return (m_pessimisticEffort - m_optimisticEffort)/6;
}
Duration Effort::pertExpected() const {
if (m_risktype == Risk_Low) {
return (m_optimisticEffort + m_pessimisticEffort + (m_expectedEffort*4))/6;
} else if (m_risktype == Risk_High) {
return (m_optimisticEffort + (m_pessimisticEffort*2) + (m_expectedEffort*4))/7;
}
return m_expectedEffort; // risk==none
}
Duration Effort::pertOptimistic() const {
if (m_risktype != Risk_None) {
return pertExpected() - variance();
}
return m_optimisticEffort;
}
Duration Effort::pertPessimistic() const {
if (m_risktype != Risk_None) {
return pertExpected() + variance();
}
return m_pessimisticEffort;
}
Duration Effort::effort(int use) const {
if (use == Effort::Use_Expected) {
return pertExpected();
} else if (use == Effort::Use_Optimistic) {
return pertOptimistic();
} else if (use == Effort::Use_Pessimistic)
return pertPessimistic();
return m_expectedEffort; // default
}
bool Effort::load(QDomElement &element) {
m_expectedEffort = Duration::fromString(element.attribute("expected"));
m_optimisticEffort = Duration::fromString(element.attribute("optimistic"));
m_pessimisticEffort = Duration::fromString(element.attribute("pessimistic"));
setType(element.attribute("type", "WorkBased"));
setRisktype(element.attribute("risk"));
return true;
}
void Effort::save(QDomElement &element) const {
QDomElement me = element.ownerDocument().createElement("effort");
element.appendChild(me);
me.setAttribute("expected", m_expectedEffort.toString());
me.setAttribute("optimistic", m_optimisticEffort.toString());
me.setAttribute("pessimistic", m_pessimisticEffort.toString());
me.setAttribute("type", typeToString());
me.setAttribute("risk", risktypeToString());
}
QString Effort::typeToString() const {
if (m_type == Type_Effort)
return QString("Effort");
if (m_type == Type_FixedDuration)
return QString("Type_FixedDuration");
return QString();
}
void Effort::setType(QString type) {
if (type == "Effort")
setType(Type_Effort);
else if (type == "Type_FixedDuration")
setType(Type_FixedDuration);
else
setType(Type_Effort); // default
}
QString Effort::risktypeToString() const {
if (m_risktype == Risk_None)
return QString("None");
if (m_risktype == Risk_Low)
return QString("Low");
if (m_risktype == Risk_High)
return QString("High");
return QString();
}
void Effort::setRisktype(QString type) {
if (type == "High")
setRisktype(Risk_High);
else if (type == "Low")
setRisktype(Risk_Low);
else
setRisktype(Risk_None); // default
}
void Effort::setOptimisticRatio(int percent)
{
int p = percent>0 ? -percent : percent;
m_optimisticEffort = m_expectedEffort*(100+p)/100;
}
int Effort::optimisticRatio() const {
if (m_expectedEffort == Duration::zeroDuration)
return 0;
return (m_optimisticEffort.milliseconds()*100/m_expectedEffort.milliseconds())-100;
}
void Effort::setPessimisticRatio(int percent)
{
int p = percent<0 ? -percent : percent;
m_pessimisticEffort = m_expectedEffort*(100+p)/100;
}
int Effort::pessimisticRatio() const {
if (m_expectedEffort == Duration::zeroDuration)
return 0;
return m_pessimisticEffort.milliseconds()*100/m_expectedEffort.milliseconds()-100;
}
// Debugging
#ifndef NDEBUG
void Node::printDebug(bool children, QCString indent) {
kdDebug()<<indent<<" Unique node identity="<<m_id<<endl;
if (m_effort) m_effort->printDebug(indent);
QString s = " Constraint: " + constraintToString();
if (m_constraint == MustStartOn || m_constraint == StartNotEarlier || m_constraint == FixedInterval)
kdDebug()<<indent<<s<<" ("<<constraintStartTime().toString()<<")"<<endl;
if (m_constraint == MustFinishOn || m_constraint == FinishNotLater || m_constraint == FixedInterval)
kdDebug()<<indent<<s<<" ("<<constraintEndTime().toString()<<")"<<endl;
Schedule *cs = m_currentSchedule;
if (cs) {
kdDebug()<<indent<<" Current schedule: "<<"id="<<cs->id()<<" '"<<cs->name()<<"' type: "<<cs->type()<<endl;
} else {
kdDebug()<<indent<<" Current schedule: None"<<endl;
}
QIntDictIterator<Schedule> it = m_schedules;
for (; it.current(); ++it) {
it.current()->printDebug(indent+" ");
}
kdDebug()<<indent<<" Parent: "<<(m_parent ? m_parent->name() : QString("None"))<<endl;
kdDebug()<<indent<<" Level: "<<level()<<endl;
kdDebug()<<indent<<" No of predecessors: "<<m_dependParentNodes.count()<<endl;
QPtrListIterator<Relation> pit(m_dependParentNodes);
//kdDebug()<<indent<<" Dependant parents="<<pit.count()<<endl;
if (pit.count() > 0) {
for ( ; pit.current(); ++pit ) {
pit.current()->printDebug(indent);
}
}
kdDebug()<<indent<<" No of successors: "<<m_dependChildNodes.count()<<endl;
QPtrListIterator<Relation> cit(m_dependChildNodes);
//kdDebug()<<indent<<" Dependant children="<<cit.count()<<endl;
if (cit.count() > 0) {
for ( ; cit.current(); ++cit ) {
cit.current()->printDebug(indent);
}
}
//kdDebug()<<indent<<endl;
indent += " ";
if (children) {
QPtrListIterator<Node> it(m_nodes);
for ( ; it.current(); ++it ) {
it.current()->printDebug(true,indent);
}
}
}
#endif
#ifndef NDEBUG
void Effort::printDebug(QCString indent) {
kdDebug()<<indent<<" Effort:"<<endl;
indent += " ";
kdDebug()<<indent<<" Expected: "<<m_expectedEffort.toString()<<endl;
kdDebug()<<indent<<" Optimistic: "<<m_optimisticEffort.toString()<<endl;
kdDebug()<<indent<<" Pessimistic: "<<m_pessimisticEffort.toString()<<endl;
kdDebug()<<indent<<" Risk: "<<risktypeToString()<<endl;
kdDebug()<<indent<<" Pert expected: "<<pertExpected().toString()<<endl;
kdDebug()<<indent<<" Pert optimistic: "<<pertOptimistic().toString()<<endl;
kdDebug()<<indent<<" Pert pessimistic: "<<pertPessimistic().toString()<<endl;
kdDebug()<<indent<<" Pert variance: "<<variance().toString()<<endl;
}
#endif
} //KPlato namespace