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.
1012 lines
30 KiB
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 <tqptrlist.h>
|
|
#include <tqdom.h>
|
|
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
|
|
namespace KPlato
|
|
{
|
|
|
|
Node::Node(Node *tqparent) : m_nodes(), m_dependChildNodes(), m_dependParentNodes() {
|
|
//kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
|
|
m_parent = tqparent;
|
|
init();
|
|
m_id = TQString(); // Not mapped
|
|
}
|
|
|
|
Node::Node(Node &node, Node *tqparent)
|
|
: m_nodes(),
|
|
m_dependChildNodes(),
|
|
m_dependParentNodes() {
|
|
//kdDebug()<<k_funcinfo<<"("<<this<<")"<<endl;
|
|
m_parent = tqparent;
|
|
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 = TQDate::tqcurrentDate();
|
|
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 tqparent and no project found"<<endl;
|
|
return 0;
|
|
}
|
|
|
|
void Node::delChildNode( Node *node, bool remove) {
|
|
//kdDebug()<<k_funcinfo<<"tqfind="<<m_nodes.tqfindRef(node)<<endl;
|
|
if ( m_nodes.tqfindRef(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.tqfindRef(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.tqfindRef( node );
|
|
}
|
|
|
|
|
|
const Node* Node::getChildNode(int number) const {
|
|
// Work around missing const at() method in TQPtrList
|
|
const TQPtrList<Node> &nodes = m_nodes;
|
|
return (const_cast<TQPtrList<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.tqfindRef(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.tqfindRef(node) != -1 ) {
|
|
if(remove)
|
|
m_dependChildNodes.remove();
|
|
else
|
|
m_dependChildNodes.take();
|
|
}
|
|
}
|
|
|
|
void Node::delDependChildNode( Relation *rel, bool remove) {
|
|
if ( m_dependChildNodes.tqfindRef(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.tqfindRef(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.tqfindRef(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.tqfindRef(node) != -1 ) {
|
|
if(remove)
|
|
m_dependParentNodes.remove();
|
|
else
|
|
m_dependParentNodes.take();
|
|
}
|
|
}
|
|
|
|
void Node::delDependParentNode( Relation *rel, bool remove) {
|
|
if ( m_dependParentNodes.tqfindRef(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.tqfindRef(rel) != -1) {
|
|
rel = m_dependParentNodes.take();
|
|
}
|
|
}
|
|
|
|
bool Node::isParentOf(Node *node) {
|
|
if (m_nodes.tqfindRef(node) != -1)
|
|
return true;
|
|
|
|
TQPtrListIterator<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->tqparent() == 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->tqparent() == node)
|
|
return true;
|
|
if (rel->tqparent()->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() {
|
|
TQPtrListIterator<Node> nit(m_nodes);
|
|
for ( ; nit.current(); ++nit ) {
|
|
nit.current()->makeAppointments();
|
|
}
|
|
}
|
|
|
|
void Node::calcResourceOverbooked() {
|
|
TQPtrListIterator<Node> nit(m_nodes);
|
|
for ( ; nit.current(); ++nit ) {
|
|
nit.current()->calcResourceOverbooked();
|
|
}
|
|
}
|
|
|
|
TQStringList Node::overbookedResources() const {
|
|
return m_currentSchedule ? m_currentSchedule->overbookedResources() : TQStringList();
|
|
}
|
|
|
|
void Node::saveRelations(TQDomElement &element) const {
|
|
TQPtrListIterator<Relation> it(m_dependChildNodes);
|
|
for (; it.current(); ++it) {
|
|
it.current()->save(element);
|
|
}
|
|
TQPtrListIterator<Node> nodes(m_nodes);
|
|
for ( ; nodes.current(); ++nodes ) {
|
|
nodes.current()->saveRelations(element);
|
|
}
|
|
}
|
|
|
|
void Node::setConstraint(TQString &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
|
|
}
|
|
|
|
TQString Node::constraintToString() const {
|
|
// Do not i18n these, they are used in save()
|
|
if (m_constraint == ASAP)
|
|
return TQString("ASAP");
|
|
else if (m_constraint == ALAP)
|
|
return TQString("ALAP");
|
|
else if (m_constraint == StartNotEarlier)
|
|
return TQString("StartNotEarlier");
|
|
else if (m_constraint == FinishNotLater)
|
|
return TQString("FinishNotLater");
|
|
else if (m_constraint == MustStartOn)
|
|
return TQString("MustStartOn");
|
|
else if (m_constraint == MustFinishOn)
|
|
return TQString("MustFinishOn");
|
|
else if (m_constraint == FixedInterval)
|
|
return TQString("FixedInterval");
|
|
|
|
return TQString();
|
|
}
|
|
|
|
void Node::propagateEarliestStart(DateTime &time) {
|
|
if (m_currentSchedule == 0)
|
|
return;
|
|
m_currentSchedule->earliestStart = time;
|
|
//kdDebug()<<k_funcinfo<<m_name<<": "<<m_currentSchedule->earliestStart.toString()<<endl;
|
|
TQPtrListIterator<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;
|
|
TQPtrListIterator<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;
|
|
TQPtrListIterator<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;
|
|
TQPtrListIterator<Node> it = m_nodes;
|
|
for (; it.current(); ++it) {
|
|
it.current()->moveLatestFinish(time);
|
|
}
|
|
}
|
|
|
|
void Node::initiateCalculation(Schedule &sch) {
|
|
TQPtrListIterator<Node> it = m_nodes;
|
|
for (; it.current(); ++it) {
|
|
it.current()->initiateCalculation(sch);
|
|
}
|
|
}
|
|
|
|
void Node::resetVisited() {
|
|
m_visitedForward = false;
|
|
m_visitedBackward = false;
|
|
TQPtrListIterator<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.tqfindRef(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.tqfindRef(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(TQString 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 = TQString(); // 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(TQDomElement &element, long id) const {
|
|
//kdDebug()<<k_funcinfo<<m_name<<" id="<<id<<endl;
|
|
TQPtrListIterator<Node> it(m_nodes);
|
|
for (; it.current(); ++it ) {
|
|
it.current()->saveAppointments(element, id);
|
|
}
|
|
}
|
|
|
|
TQPtrList<Appointment> Node::appointments() {
|
|
TQPtrList<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->tqparent());
|
|
}
|
|
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.tqreplace(schedule->id(), schedule);
|
|
}
|
|
|
|
Schedule *Node::createSchedule(TQString 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 *tqparent) {
|
|
//kdDebug()<<k_funcinfo<<name<<" type="<<type<<" id="<<(int)id<<endl;
|
|
NodeSchedule *sch = new NodeSchedule(tqparent, this);
|
|
addSchedule(sch);
|
|
return sch;
|
|
}
|
|
|
|
Schedule *Node::findSchedule(const TQString name, const Schedule::Type type) const {
|
|
TQIntDictIterator<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;
|
|
TQIntDictIterator<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);
|
|
}
|
|
TQPtrListIterator<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;
|
|
}
|
|
TQPtrListIterator<Relation> pit(m_dependParentNodes);
|
|
for (; pit.current(); ++pit) {
|
|
if (pit.current()->tqparent()->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, TQString wbs) {
|
|
m_wbs = wbs + def.code(count, level());
|
|
//kdDebug()<<k_funcinfo<<m_name<<" wbs: "<<m_wbs<<endl;
|
|
TQString w = wbs + def.wbs(count, level());
|
|
TQPtrListIterator<Node> it = m_nodes;
|
|
for (int i=0; it.current(); ++it) {
|
|
it.current()->generateWBS(++i, def, w);
|
|
}
|
|
|
|
}
|
|
|
|
void Node::setCurrentSchedule(long id) {
|
|
TQPtrListIterator<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(TQDomElement &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(TQDomElement &element) const {
|
|
TQDomElement 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());
|
|
}
|
|
|
|
TQString Effort::typeToString() const {
|
|
if (m_type == Type_Effort)
|
|
return TQString("Effort");
|
|
if (m_type == Type_FixedDuration)
|
|
return TQString("Type_FixedDuration");
|
|
|
|
return TQString();
|
|
}
|
|
|
|
void Effort::setType(TQString type) {
|
|
if (type == "Effort")
|
|
setType(Type_Effort);
|
|
else if (type == "Type_FixedDuration")
|
|
setType(Type_FixedDuration);
|
|
else
|
|
setType(Type_Effort); // default
|
|
}
|
|
|
|
TQString Effort::risktypeToString() const {
|
|
if (m_risktype == Risk_None)
|
|
return TQString("None");
|
|
if (m_risktype == Risk_Low)
|
|
return TQString("Low");
|
|
if (m_risktype == Risk_High)
|
|
return TQString("High");
|
|
|
|
return TQString();
|
|
}
|
|
|
|
void Effort::setRisktype(TQString 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 tqchildren, TQCString indent) {
|
|
kdDebug()<<indent<<" Unique node identity="<<m_id<<endl;
|
|
if (m_effort) m_effort->printDebug(indent);
|
|
TQString 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;
|
|
}
|
|
TQIntDictIterator<Schedule> it = m_schedules;
|
|
for (; it.current(); ++it) {
|
|
it.current()->printDebug(indent+" ");
|
|
}
|
|
kdDebug()<<indent<<" Parent: "<<(m_parent ? m_parent->name() : TQString("None"))<<endl;
|
|
kdDebug()<<indent<<" Level: "<<level()<<endl;
|
|
kdDebug()<<indent<<" No of predecessors: "<<m_dependParentNodes.count()<<endl;
|
|
TQPtrListIterator<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;
|
|
TQPtrListIterator<Relation> cit(m_dependChildNodes);
|
|
//kdDebug()<<indent<<" Dependant tqchildren="<<cit.count()<<endl;
|
|
if (cit.count() > 0) {
|
|
for ( ; cit.current(); ++cit ) {
|
|
cit.current()->printDebug(indent);
|
|
}
|
|
}
|
|
|
|
//kdDebug()<<indent<<endl;
|
|
indent += " ";
|
|
if (tqchildren) {
|
|
TQPtrListIterator<Node> it(m_nodes);
|
|
for ( ; it.current(); ++it ) {
|
|
it.current()->printDebug(true,indent);
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef NDEBUG
|
|
void Effort::printDebug(TQCString 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
|