/* This file is part of the KDE project Copyright (C) 2001 Thomas Zander zander@kde.org Copyright (C) 2004, 2005 Dag Andersen 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 #include #include #include #include #include class TQDomElement; 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(TQString id); TQString 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(TQDomElement &) { return true; } virtual bool load(TQDomElement &, Project &) { return true; } virtual void save(TQDomElement &element) const = 0; /// Save my and my childrens relations. virtual void saveRelations(TQDomElement &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 TQPtrList &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); } TQPtrList &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); } TQPtrList &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 TQDate &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 TQDate &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; } TQString &name() { return m_name; } TQString &leader() { return m_leader; } TQString &description() { return m_description; } const TQString &name() const { return m_name; } const TQString &leader() const { return m_leader; } const TQString &description() const { return m_description; } void setName(const TQString &n) { m_name = n; } void setLeader(const TQString &l) { m_leader = l; } void setDescription(const TQString &d) { m_description = d; } virtual void setConstraint(Node::ConstraintType type) { m_constraint = type; } void setConstraint(TQString &type); int constraint() const { return m_constraint; } TQString constraintToString() const; virtual void setConstraintStartTime(TQDateTime time) { m_constraintStartTime = time; } virtual void setConstraintEndTime(TQDateTime 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 TQStringList 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 TQDate &start, const TQDate &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 TQDate &) { return Duration::zeroDuration; } /// Returns the planned effort up to and including date virtual Duration plannedEffortTo(const TQDate &) { 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 TQDate &/*date*/) { return Duration::zeroDuration; } /// Returns the total actual effort for this task (or subtasks) up to and including date virtual Duration actualEffortTo(const TQDate &/*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 TQDate &/*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 TQDate &/*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 TQDate &/*date*/) { return 0; } /// Actual cost up to and including date virtual double actualCostTo(const TQDate &/*date*/) { return 0; } /// Effort based performance index double effortPerformanceIndex(const TQDate &/*date*/, bool */*error=0*/) { return 0.0; } /// Cost performance index double costPerformanceIndex(const TQDate &/*date*/, bool */*error=0*/) { return 0.0; } virtual void initiateCalculationLists(TQPtrList &startnodes, TQPtrList &endnodes, TQPtrList &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(TQPtrList &) {} virtual void addChildProxyRelations(TQPtrList &) {} virtual void addParentProxyRelation(Node *, const Relation *) {} virtual void addChildProxyRelation(Node *, const Relation *) {} /// Save appointments for schedule with id virtual void saveAppointments(TQDomElement &element, long id) const; ///Return the list of appointments for current schedule. TQPtrList 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 TQString &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 TQString &id) { return (m_parent ? m_parent->removeId(id) : false); } /// Insert myself into the id register virtual void insertId(const TQString &id) { insertId(id, this); } /// Insert node with identity id into the register virtual void insertId(const TQString &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, TQString wbs=TQString()); TQString 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; } TQIntDict &schedules() { return m_schedules; } /// Find schedule matching name and type. Does not return deleted schedule. Schedule *findSchedule(const TQString 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(TQString 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: TQPtrList m_nodes; TQPtrList m_dependChildNodes; TQPtrList m_dependParentNodes; Node *m_parent; TQString m_id; // unique id TQString m_name; // Name of this node TQString m_leader; // Person or group responsible for this node TQString 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; TQDate m_dateOnlyStartDate; TQDate m_dateOnlyEndDate; Duration m_dateOnlyDuration; TQIntDict m_schedules; Schedule *m_currentSchedule; TQString 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, TQCString 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(TQString type); TQString 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(TQString type); TQString 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(TQDomElement &element); void save(TQDomElement &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(TQCString indent); #endif }; } //KPlato namespace #endif