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.h

643 lines
25 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.
*/
#ifndef KPTNODE_H
#define KPTNODE_H
#include "kptrelation.h"
#include "kptduration.h"
#include "kptdatetime.h"
#include "kptschedule.h"
#include <qintdict.h>
#include <qrect.h>
#include <qptrlist.h>
#include <qstring.h>
#include <qcanvas.h>
#include <vector>
class QDomElement;
namespace KPlato
{
class Account;
class Project;
class Appointment;
class ResourceGroup;
class Resource;
class ResourceGroupRequest;
class Effort;
class WBSDefinition;
class EffortCostMap;
/**
* This class represents any node in the project, a node can be a project or
* a subproject or any task.
* This class is basically an abstract interface to make the design more OO.
*/
class Node {
public:
enum ConstraintType { ASAP, ALAP, MustStartOn, MustFinishOn, StartNotEarlier, FinishNotLater, FixedInterval };
Node(Node *parent = 0);
Node(Node &node, Node *parent = 0);
// Declare the class abstract
virtual ~Node() = 0;
bool setId(QString id);
QString id() const { return m_id; } // unique identity
enum NodeTypes {
Type_Node = 0,
Type_Project = 1,
Type_Subproject = 2,
Type_Task = 3,
Type_Milestone = 4,
Type_Periodic = 5,
Type_Summarytask = 6
};
virtual int type() const = 0;
/**
* Returns a pointer to the project node (main- or sub-project)
* Returns 0 if no project exists.
*/
virtual Node *projectNode();
// The load and save methods
virtual bool load(QDomElement &) { return true; }
virtual bool load(QDomElement &, Project &) { return true; }
virtual void save(QDomElement &element) const = 0;
/// Save my and my childrens relations.
virtual void saveRelations(QDomElement &element) const;
// simple child node management
// Child nodes are things like subtasks, basically a task can exists of
// several sub-tasks. Creating a table has 4 subtasks, 1) measuring
// 2) cutting 3) building 4) painting.
Node *getParent() const { return m_parent; }
void setParent( Node* newParent ) { m_parent = newParent;}
const QPtrList<Node> &childNodeIterator() const { return m_nodes; }
int numChildren() const { return m_nodes.count(); }
virtual void addChildNode(Node *node, Node *after=0);
virtual void insertChildNode(unsigned int index, Node *node);
void delChildNode(Node *node, bool remove=true);
void delChildNode(int number, bool remove=true);
Node* getChildNode(int number) { return m_nodes.at(number); }
const Node* getChildNode(int number) const;
int findChildNode( Node* node );
// Time-dependent child-node-management.
// list all nodes that are dependent upon this one.
// Building a house requires the table to be finished, therefore the
// house-building is time dependent on the table-building. So a child
// of the table-building node is the house-building node.
int numDependChildNodes() const { return m_dependChildNodes.count(); }
/// Adds relation to both this node and address node
virtual void addDependChildNode( Node *node, Relation::Type p=Relation::FinishStart);
/// Adds relation to both this node and address node
virtual void addDependChildNode( Node *node, Relation::Type p, Duration lag);
/// Adds relation only to this node
virtual bool addDependChildNode( Relation *relation);
/// Inserts relation to this node at index address index and appends relation to address node
virtual void insertDependChildNode( unsigned int index, Node *node, Relation::Type p=Relation::FinishStart);
void delDependChildNode( Node *node, bool remove=false);
void delDependChildNode( Relation *rel, bool remove=false);
void delDependChildNode( int number, bool remove=false);
Relation *getDependChildNode( int number) {
return m_dependChildNodes.at(number);
}
QPtrList<Relation> &dependChildNodes() { return m_dependChildNodes; }
/**
* Takes the relation rel from this node only.
* Never deletes even when autoDelete = true.
*/
void takeDependChildNode(Relation *rel);
int numDependParentNodes() const { return m_dependParentNodes.count(); }
/// Adds relation to both this node and node
virtual void addDependParentNode(Node *node, Relation::Type p=Relation::FinishStart);
/// Adds relation to both this node and node
virtual void addDependParentNode( Node *node, Relation::Type p, Duration lag);
/// Adds relation only to this node
virtual bool addDependParentNode( Relation *relation);
/// Inserts relation to this node at index and appends relation to node
virtual void insertDependParentNode( unsigned int index, Node *node, Relation::Type p=Relation::FinishStart);
void delDependParentNode( Node *node, bool remove=false);
void delDependParentNode( Relation *rel, bool remove=false);
void delDependParentNode( int number, bool remove=false);
Relation *getDependParentNode( int number) {
return m_dependParentNodes.at(number);
}
QPtrList<Relation> &dependParentNodes() { return m_dependParentNodes; }
/**
* Takes the relation rel from this node only.
* Never deletes even when autoDelete = true.
*/
void takeDependParentNode(Relation *rel);
bool isParentOf(Node *node);
bool isDependChildOf(Node *node);
Relation *findParentRelation(Node *node);
Relation *findChildRelation(Node *node);
Relation *findRelation(Node *node);
void setStartTime(DateTime startTime);
/// Return the scheduled start time
virtual DateTime startTime() const
{ return m_currentSchedule ? m_currentSchedule->startTime : DateTime(); }
const QDate &startDate() const { return m_dateOnlyStartDate; }
void setEndTime(DateTime endTime);
/// Return the scheduled end time
virtual DateTime endTime() const
{ return m_currentSchedule ? m_currentSchedule->endTime : DateTime(); }
const QDate &endDate() const { return m_dateOnlyEndDate; }
void setEffort(Effort* e) { m_effort = e; }
Effort* effort() const { return m_effort; }
/**
* Returns the (previously) calculated duration.
*/
virtual Duration *getExpectedDuration() = 0;
/**
* Instead of using the expected duration, generate a random value using
* the Distribution of each Task. This can be used for Monte-Carlo
* estimation of Project duration.
*/
virtual Duration *getRandomDuration() = 0;
/**
* Calculate the delay of this node.
* It is the difference between the actual startTime and scheduled startTime.
*/
Duration *getDelay();
/**
* getEarliestStart() returns earliest time this node can start
* given the constraints of the network.
* @see earliestStart
*/
DateTime getEarliestStart() const
{ return m_currentSchedule ? m_currentSchedule->earliestStart : DateTime(); }
/**
* setEarliestStart() sets earliest time this node can start
* @see earliestStart
*/
void setEarliestStart(const DateTime &dt)
{ if (m_currentSchedule) m_currentSchedule->earliestStart = dt; }
/**
* getLatestFinish() returns latest time this node can finish
* @see latestFinish
*/
DateTime getLatestFinish() const
{ return m_currentSchedule ? m_currentSchedule->latestFinish : DateTime(); }
/**
* setLatestFinish() sets latest time this node can finish
* given the constraints of the network.
* @see latestFinish
*/
void setLatestFinish(const DateTime &dt)
{ if (m_currentSchedule) m_currentSchedule->latestFinish = dt; }
QString &name() { return m_name; }
QString &leader() { return m_leader; }
QString &description() { return m_description; }
const QString &name() const { return m_name; }
const QString &leader() const { return m_leader; }
const QString &description() const { return m_description; }
void setName(const QString &n) { m_name = n; }
void setLeader(const QString &l) { m_leader = l; }
void setDescription(const QString &d) { m_description = d; }
virtual void setConstraint(Node::ConstraintType type) { m_constraint = type; }
void setConstraint(QString &type);
int constraint() const { return m_constraint; }
QString constraintToString() const;
virtual void setConstraintStartTime(QDateTime time) { m_constraintStartTime = time; }
virtual void setConstraintEndTime(QDateTime time) { m_constraintEndTime = time; }
virtual DateTime constraintStartTime() const { return m_constraintStartTime; }
virtual DateTime constraintEndTime() const { return m_constraintEndTime; }
virtual DateTime startNotEarlier() const { return m_constraintStartTime; }
virtual DateTime finishNotLater() const { return m_constraintEndTime; }
virtual DateTime mustStartOn() const { return m_constraintStartTime; }
virtual DateTime mustFinishOn() const { return m_constraintEndTime; }
virtual ResourceGroupRequest *resourceRequest(ResourceGroup */*group*/) const { return 0; }
virtual void makeAppointments();
/// EffortType == Effort, but no resource is requested
bool resourceError() const
{ return m_currentSchedule ? m_currentSchedule->resourceError : false; }
/// The assigned resource is overbooked
virtual bool resourceOverbooked() const
{ return m_currentSchedule ? m_currentSchedule->resourceOverbooked : false; }
/// Return a list of overbooked resources
virtual QStringList overbookedResources() const;
/// Calculates if the assigned resource is overbooked
/// within the duration of this node
virtual void calcResourceOverbooked();
/// The requested resource is not available
bool resourceNotAvailable() const
{ return m_currentSchedule ? m_currentSchedule->resourceNotAvailable : false; }
/// The task cannot be scheduled to fullfill all the constraints
virtual bool schedulingError() const
{ return m_currentSchedule ? m_currentSchedule->schedulingError : false; }
/// The node has not been scheduled
bool notScheduled() const
{ return m_currentSchedule == 0 || m_currentSchedule->isDeleted() || m_currentSchedule->notScheduled; }
virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end) const=0;
/// Returns the total planned effort for this task (or subtasks)
virtual Duration plannedEffort() { return Duration::zeroDuration; }
/// Returns the total planned effort for this task (or subtasks) on date
virtual Duration plannedEffort(const QDate &) { return Duration::zeroDuration; }
/// Returns the planned effort up to and including date
virtual Duration plannedEffortTo(const QDate &) { return Duration::zeroDuration; }
/// Returns the total actual effort for this task (or subtasks)
virtual Duration actualEffort() { return Duration::zeroDuration; }
/// Returns the total actual effort for this task (or subtasks) on date
virtual Duration actualEffort(const QDate &/*date*/) { return Duration::zeroDuration; }
/// Returns the total actual effort for this task (or subtasks) up to and including date
virtual Duration actualEffortTo(const QDate &/*date*/) { return Duration::zeroDuration; }
/**
* Planned cost is the sum total of all resources and other costs
* planned for this node.
*/
virtual double plannedCost() { return 0; }
/// Planned cost on date
virtual double plannedCost(const QDate &/*date*/) { return 0; }
/**
* Planned cost from start of activity up to and including date
* is the sum of all resource costs and other costs planned for this node.
*/
virtual double plannedCostTo(const QDate &/*date*/) { return 0; }
/**
* Actual cost is the sum total of the reported costs actually used
* for this node.
*/
virtual double actualCost() { return 0; }
/// Actual cost on date
virtual double actualCost(const QDate &/*date*/) { return 0; }
/// Actual cost up to and including date
virtual double actualCostTo(const QDate &/*date*/) { return 0; }
/// Effort based performance index
double effortPerformanceIndex(const QDate &/*date*/, bool */*error=0*/) { return 0.0; }
/// Cost performance index
double costPerformanceIndex(const QDate &/*date*/, bool */*error=0*/) { return 0.0; }
virtual void initiateCalculationLists(QPtrList<Node> &startnodes, QPtrList<Node> &endnodes, QPtrList<Node> &summarytasks) = 0;
virtual DateTime calculateForward(int /*use*/) = 0;
virtual DateTime calculateBackward(int /*use*/) = 0;
virtual DateTime scheduleForward(const DateTime &, int /*use*/) = 0;
virtual DateTime scheduleBackward(const DateTime &, int /*use*/) = 0;
virtual void adjustSummarytask() = 0;
virtual void initiateCalculation(Schedule &sch);
virtual void resetVisited();
void propagateEarliestStart(DateTime &time);
void propagateLatestFinish(DateTime &time);
void moveEarliestStart(DateTime &time);
void moveLatestFinish(DateTime &time);
// Reimplement this
virtual Duration summarytaskDurationForward(const DateTime &/*time*/)
{ return Duration::zeroDuration; }
// Reimplement this
virtual DateTime summarytaskEarliestStart()
{ return DateTime(); }
// Reimplement this
virtual Duration summarytaskDurationBackward(const DateTime &/*time*/)
{ return Duration::zeroDuration; }
// Reimplement this
virtual DateTime summarytaskLatestFinish()
{ return DateTime(); }
// Returns the (previously) calculated duration
const Duration &duration()
{ return m_currentSchedule ? m_currentSchedule->duration : Duration::zeroDuration; }
/**
* Calculates and returns the duration of the node.
* Uses the correct expected-, optimistic- or pessimistic effort
* dependent on use.
* @param time Where to start calculation.
* @param use Calculate using expected-, optimistic- or pessimistic estimate.
* @param backward If true, time specifies when the task should end.
*/
Duration duration(const DateTime &time, int use, bool backward);
// Reimplement this
virtual Duration calcDuration(const DateTime &/*time*/, const Duration &/*effort*/, bool /*backward*/) { return Duration::zeroDuration;}
Node *siblingBefore();
Node *childBefore(Node *node);
Node *siblingAfter();
Node *childAfter(Node *node);
bool moveChildUp(Node *node);
bool moveChildDown(Node *node);
/// Check if this node can be linked to node
bool legalToLink(Node *node);
/// Check if node par can be linked to node child. (Reimplement)
virtual bool legalToLink(Node *, Node *) { return false; }
/// Check if this node has any dependent child nodes
virtual bool isEndNode() const;
/// Check if this node has any dependent parent nodes
virtual bool isStartNode() const;
virtual void clearProxyRelations() {}
virtual void addParentProxyRelations(QPtrList<Relation> &) {}
virtual void addChildProxyRelations(QPtrList<Relation> &) {}
virtual void addParentProxyRelation(Node *, const Relation *) {}
virtual void addChildProxyRelation(Node *, const Relation *) {}
/// Save appointments for schedule with id
virtual void saveAppointments(QDomElement &element, long id) const;
///Return the list of appointments for current schedule.
QPtrList<Appointment> appointments();
/// Return appointment this node have with resource
// Appointment *findAppointment(Resource *resource);
/// Adds appointment to this node only (not to resource)
virtual bool addAppointment(Appointment *appointment);
/// Adds appointment to this node only (not to resource)
virtual bool addAppointment(Appointment *appointment, Schedule &main);
/// Adds appointment to both this node and resource
virtual void addAppointment(ResourceSchedule *resource, DateTime &start, DateTime &end, double load=100);
/// Find the node with my id
virtual Node *findNode() const { return findNode(m_id); }
/// Find the node with identity id
virtual Node *findNode(const QString &id) const
{ return (m_parent ? m_parent->findNode(id) : 0); }
/// Remove myself from the id register
virtual bool removeId() { return removeId(m_id); }
/// Remove the registered identity id
virtual bool removeId(const QString &id)
{ return (m_parent ? m_parent->removeId(id) : false); }
/// Insert myself into the id register
virtual void insertId(const QString &id) { insertId(id, this); }
/// Insert node with identity id into the register
virtual void insertId(const QString &id, const Node *node)
{ if (m_parent) m_parent->insertId(id, node); }
/**
* This is when work can start on this node in accordance with
* the calendar of allocated resources. Normally this is the same
* as @ref startTime(), but may differ if timing constraints are set.
*/
virtual DateTime workStartTime() const
{ return m_currentSchedule ? m_currentSchedule->workStartTime : DateTime(); }
void setWorkStartTime(const DateTime &dt)
{ if (m_currentSchedule) m_currentSchedule->workStartTime = dt; }
/**
* This is when work can finish on this node in accordance with
* the calendar of allocated resources. Normally this is the same
* as @ref endTime(), but may differ if timing constraints are set.
*/
virtual DateTime workEndTime() const
{ return m_currentSchedule ? m_currentSchedule->workEndTime : DateTime(); }
void setWorkEndTime(const DateTime &dt)
{ if (m_currentSchedule) m_currentSchedule->workEndTime = dt; }
virtual bool isCritical() const { return false; }
virtual bool inCriticalPath() const
{ return m_currentSchedule ? m_currentSchedule->inCriticalPath : false; }
virtual bool calcCriticalPath(bool fromEnd);
/// Returns the level this node is in the hierarchy. Top node is level 0.
virtual int level();
/// Generate WBS
virtual void generateWBS(int count, WBSDefinition &def, QString wbs=QString());
QString wbs() const { return m_wbs; }
double startupCost() const { return m_startupCost; }
void setStartupCost(double cost) { m_startupCost = cost; }
Account *startupAccount() const { return m_startupAccount; }
void setStartupAccount(Account *acc) { m_startupAccount = acc; }
double shutdownCost() const { return m_shutdownCost; }
void setShutdownCost(double cost) { m_shutdownCost = cost; }
Account *shutdownAccount() const { return m_shutdownAccount; }
void setShutdownAccount(Account *acc) { m_shutdownAccount = acc; }
Account *runningAccount() const { return m_runningAccount; }
void setRunningAccount(Account *acc) { m_runningAccount = acc; }
Schedule *currentSchedule() const { return m_currentSchedule; }
/// Set current schedule to schedule with identity id, for me and my children
virtual void setCurrentSchedule(long id);
// NOTE: Cannot use setCurrentSchedule() due to overload/casting problems
void setCurrentSchedulePtr(Schedule *schedule) { m_currentSchedule = schedule; }
QIntDict<Schedule> &schedules() { return m_schedules; }
/// Find schedule matching name and type. Does not return deleted schedule.
Schedule *findSchedule(const QString name, const Schedule::Type type) const;
/// Find schedule matching type. Does not return deleted schedule.
Schedule *findSchedule(const Schedule::Type type) const;
/// Find schedule matching id. Also returns deleted schedule.
Schedule *findSchedule(long id) const { return m_schedules[id]; }
/// Take, don't delete (as in destruct).
void takeSchedule(const Schedule *schedule);
/// Add schedule to list, replace if schedule with same id allready exists.
void addSchedule(Schedule *schedule);
/// Create a new schedule.
Schedule *createSchedule(QString name, Schedule::Type type, long id);
/// Create a new schedule.
Schedule *createSchedule(Schedule *parent);
/// Set deleted = onoff for schedule with id
void setScheduleDeleted(long id, bool onoff);
/// Set parent schedule recursivly
virtual void setParentSchedule(Schedule *sch);
DateTime startTime()
{ return m_currentSchedule ? m_currentSchedule->startTime : DateTime(); }
DateTime endTime()
{ return m_currentSchedule ? m_currentSchedule->endTime : DateTime(); }
protected:
QPtrList<Node> m_nodes;
QPtrList<Relation> m_dependChildNodes;
QPtrList<Relation> m_dependParentNodes;
Node *m_parent;
QString m_id; // unique id
QString m_name; // Name of this node
QString m_leader; // Person or group responsible for this node
QString m_description; // Description of this node
Effort* m_effort;
ConstraintType m_constraint;
/**
* m_constraintTime is used if any of the constraints
* FixedInterval, StartNotEarlier, MustStartOn or FixedInterval is selected
*/
DateTime m_constraintStartTime;
/**
* m_constraintEndTime is used if any of the constraints
* FixedInterval, FinishNotLater, MustFinishOn or FixedInterval is selected
*/
DateTime m_constraintEndTime;
bool m_visitedForward;
bool m_visitedBackward;
Duration m_durationForward;
Duration m_durationBackward;
QDate m_dateOnlyStartDate;
QDate m_dateOnlyEndDate;
Duration m_dateOnlyDuration;
QIntDict<Schedule> m_schedules;
Schedule *m_currentSchedule;
QString m_wbs;
double m_startupCost;
Account *m_startupAccount;
double m_shutdownCost;
Account *m_shutdownAccount;
Account *m_runningAccount;
private:
void init();
#ifndef NDEBUG
public:
virtual void printDebug(bool children, QCString indent);
#endif
};
//////////////////////////////// Effort ////////////////////////////////
/**
* Any @ref Node will store how much time it takes to complete the node
* (typically a @ref Task) in the traditional scheduling software the
* effort which is needed to complete the node is not simply a timespan but
* is stored as an optimistic, a pessimistic and an expected timespan.
*/
class Effort {
public:
Effort ( Duration e = Duration::zeroDuration, Duration p = Duration::zeroDuration,
Duration o = Duration::zeroDuration );
Effort ( double e, double p = 0, double o = 0);
Effort (const Effort &effort);
~Effort();
enum Type { Type_Effort = 0, // Changing amount of resources changes the task duration
Type_FixedDuration = 1 // Changing amount of resources will not change the tasks duration
};
Type type() const { return m_type; }
void setType(Type type) { m_type = type; }
void setType(QString type);
QString typeToString() const;
enum Risktype { Risk_None, Risk_Low, Risk_High };
Risktype risktype() const { return m_risktype; }
void setRisktype(Risktype type) { m_risktype = type; }
void setRisktype(QString type);
QString risktypeToString() const;
enum Use { Use_Expected=0, Use_Optimistic=1, Use_Pessimistic=2 };
Duration effort(int use) const;
const Duration& optimistic() const {return m_optimisticEffort;}
const Duration& pessimistic() const {return m_pessimisticEffort;}
const Duration& expected() const {return m_expectedEffort;}
void set( Duration e, Duration p = Duration::zeroDuration, Duration o = Duration::zeroDuration );
void set( int e, int p = -1, int o = -1 );
void set(unsigned days, unsigned hours, unsigned minutes);
void expectedEffort(unsigned *days, unsigned *hours, unsigned *minutes);
bool load(QDomElement &element);
void save(QDomElement &element) const;
/**
* Set the optimistic duration
* @param percent should be a negativ value.
*/
void setOptimisticRatio(int percent);
/**
* Return the "optimistic" duration as deviation from "expected" in percent.
* This should be a negativ value.
*/
int optimisticRatio() const;
/**
* Set the pessimistic duration
* @param percent should be a positive value.
*/
void setPessimisticRatio(int percent);
/**
* Return the "pessimistic" duration as the deviation from "expected" in percent.
* This should be a positive value.
*/
int pessimisticRatio() const;
/**
* No effort.
*/
static const Effort zeroEffort;
Duration variance() const;
Duration pertExpected() const;
Duration pertOptimistic() const;
Duration pertPessimistic() const;
private:
Duration m_optimisticEffort;
Duration m_pessimisticEffort;
Duration m_expectedEffort;
Type m_type;
Risktype m_risktype;
#ifndef NDEBUG
public:
void printDebug(QCString indent);
#endif
};
} //KPlato namespace
#endif