Main Page   Namespace List   Class Hierarchy   Compound List   File List   Compound Members   File Members  

sched.cc

Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 2000 Idan Shoham <idan@m-tech.ab.ca>
00003 **  
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 ** 
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 ** 
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software 
00016 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017 */
00018 #ifdef HAVE_CONFIG_H
00019 #include <config.h>
00020 #endif
00021 
00022 #include <cmath>
00023 #include <iostream>
00024 
00025 using namespace std;
00026 
00027 #include "project.h"
00028 
00029 int Project::ResourceIsUsedForTask( RESOURCE *r, TASK *t, int dayNo )
00030 {
00031     for( ResourceTimeBlockIterator tb = r->begin_booked() ; 
00032          tb != r->end_booked() ; tb++ )
00033     {
00034         if ( ( dayNo >= tb->start() ) 
00035              && ( dayNo <= tb->finish() ) 
00036              && ( tb->task() == t ) )
00037         {
00038             Debug("ResourceIsUsedForTask(%s,%s,%d) - yes",
00039                   r->id(), t->id(), dayNo);
00040             return 1;
00041         }
00042     }
00043 
00044     Debug("ResourceIsUsedForTask(%s,%s,%d) - no", r->id(), t->id(), dayNo);
00045     return 0;
00046 }
00047 
00048 
00049 int Project::FirstFreeTime(TASK *task, RESOURCE *r, int earliest, int duration)
00050 {
00051     Debug("FirstFreeTime(%s,%d/%s,%d)",
00052                 r->id(),earliest,days[earliest].s,duration);
00053 
00054     for ( int i = 0; earliest+i+duration<MAX_TIME; ++i )
00055     {
00056         if ( r->IsAvailable( earliest+i, earliest+i+duration-1 ) )
00057             return earliest+i;
00058     }
00059 
00060     Error("Resource %s not available to do %s at time >= %s",
00061           r->id(), task->id(), days[earliest].s);
00062 
00063     // never reached
00064     return -1;
00065 }
00066 
00067 
00068 void Project::BookResource(TASK *t, RESOURCE *parent, RESOURCE *r,
00069                            int tstart, int tfinish, TimeBlock::Type type)
00070 {
00071   Debug("BookResource(%s,%s,%s,%d,%d)", t->id(),
00072         parent==NULL?"NULL":parent->id(),
00073         r==NULL?"NULL":r->id(),
00074         tstart, tfinish);
00075   
00076   if ( ! r->IsAvailable(tstart, tfinish) )
00077     {
00078         bool only_vac = true;
00079         Warning("Double booking! %s / %s / %s-%s", r->id(), t->id(),
00080                 days[tstart].s, days[tfinish].s);
00081         for ( ResourceTimeBlockIterator tb = r->begin_booked() ; 
00082               tb != r->end_booked() ; 
00083               tb++ )
00084         {
00085             if ( tb->overlap(tstart, tfinish) )
00086             {
00087                 Warning("Overlap: %s %s (%s-%s) / %s %s %s-%s",
00088                         tb->task()->id(),
00089                         tb->task()->assigned() == NULL ? "(unassigned)" :
00090                         tb->task()->assigned()->id(),
00091                         days[tb->task()->start()].s, days[tb->task()->finish()].s,
00092                         t->id(),
00093                         t->assigned()==NULL?"(unassigned)":t->assigned()->id(),
00094                         days[tb->start()].s, days[tb->finish()].s);
00095                 if ( ! tb->task()->isVacation() || ! t->isVacation() )
00096                     only_vac = false;
00097             }
00098         }
00099         if ( only_vac )
00100             Warning("You have double-booked vacations for %s.  Ignoring.",
00101                     r->id());
00102         else
00103             Error("You cannot double-book tasks.");
00104     }
00105 
00106 //    AddTimeBlock(t, &(t->when), tstart, tfinish, type);
00107     t->addTimeBlock(r, tstart, tfinish, type);
00108     r->addTimeBlock(t, tstart, tfinish, type);
00109 
00110     if ( r->is_group )
00111     {
00112         for ( RPLCI rl = r->contains.begin() ; rl != r->contains.end() ; rl++ )
00113         {
00114             if ( ( *rl != parent ) && ( *rl != r ) )
00115                 (*rl)->addTimeBlock(t, tstart, tfinish, type);
00116         }
00117     }
00118     else
00119     {
00120         for ( RPLCI rl = r->belongs_to.begin() ; rl != r->belongs_to.end() ; rl++ )
00121         {
00122             if ( ( *rl != parent ) && ( *rl != r ) )
00123                 (*rl)->addTimeBlock(t, tstart, tfinish, type);
00124         }
00125     }
00126 }
00127 
00128 
00129 void Project::ReverseBookResource(TASK *t, RESOURCE *parent, RESOURCE *r,
00130                                   int tstart, int tfinish, TimeBlock::Type type)
00131 {
00132     Debug("ReverseBookResource(%s,%s,%s,%d,%d)", t->id(),
00133           parent==NULL?"NULL":parent->id(),
00134           r==NULL?"NULL":r->id(),
00135           tstart, tfinish);
00136 
00137     if ( ! r->reverseIsAvailable(tstart, tfinish) )
00138       {
00139         bool only_vac = true;
00140         Warning("Reverse Double booking! %s / %s / %s-%s", r->id(), t->id(),
00141                 days[tstart].s, days[tfinish].s);
00142         for ( ResourceTimeBlockIterator tb = r->begin_rbooked() ; 
00143               tb != r->end_rbooked() ; 
00144               tb++ )
00145         {
00146             if ( tb->overlap(tstart, tfinish) )
00147             {
00148                 Warning("Overlap: %s %s (%s-%s) / %s %s %s-%s",
00149                         tb->task()->id(),
00150                         tb->task()->assigned() == NULL ? "(unassigned)" :
00151                         tb->task()->assigned()->id(),
00152                         days[tb->task()->start()].s, days[tb->task()->finish()].s,
00153                         t->id(),
00154                         t->assigned()==NULL?"(unassigned)":t->assigned()->id(),
00155                         days[tb->start()].s, days[tb->finish()].s);
00156                 if ( ! tb->task()->isVacation() || ! t->isVacation() )
00157                     only_vac = false;
00158             }
00159         }
00160         if ( only_vac )
00161             Warning("ReverseBookResource: You have double-booked vacations for %s.  Ignoring.",
00162                     r->id());
00163         else
00164             Error("ReverseBookResource: You cannot double-book tasks.");
00165     }
00166 
00167 //    AddTimeBlock(t, &(t->rwhen), tstart, tfinish, type);
00168     t->addReverseTimeBlock(r, tstart, tfinish, type);
00169 
00170     r->addReverseTimeBlock(t, tstart, tfinish, type);
00171 
00172     if ( r->is_group )
00173     {
00174         for ( RPLCI rl = r->contains.begin() ; rl != r->contains.end() ; rl++ )
00175         {
00176             if ( ( *rl != parent ) && ( *rl != r ) )
00177                 (*rl)->addReverseTimeBlock(t, tstart, tfinish, type);
00178         }
00179     }
00180     else
00181     {
00182         for ( RPLCI rl = r->belongs_to.begin() ; rl != r->belongs_to.end() ; rl++ )
00183         {
00184             if ( ( *rl != parent ) && ( *rl != r ) )
00185                 (*rl)->addReverseTimeBlock(t, tstart, tfinish, type);
00186         }
00187     }
00188 }
00189 
00190 
00191 void Project::AssignResource(TASK *t, RESOURCE *r, int tstart)
00192 {
00193   int real_remaining;
00194 
00195     Debug("AssignResource %s/%s start=%d", t->id(), r->id(), tstart);
00196 
00197     Debug("AssignResource(%s,%s,%d)", t->id(), r->id(), tstart);
00198 
00199     if ( t->scheduled )
00200         Error("Trying to double-book (%s,%s,%d)", t->id(), r->id(), tstart);
00201 
00202     if ( t->assigned() == NULL )
00203     {
00204         if ( t->remaining == 0.0 )
00205             Error("AssignResource: t->remaining == 0 but no assigned resource");
00206         t->setAssigned( r) ;
00207     }
00208     else
00209     {
00210         if ( t->assigned() != r )
00211             Error("AssignResource trying to assign second resource to task %s",
00212                   t->id() );
00213     }
00214     /* t->when = NULL; */
00215 
00216     if ( t->remaining == -1 )
00217     {
00218         // fullduration is now calculated in PredictOverruns()
00219         t->remaining = t->fullduration();
00220     }
00221     else
00222     {
00223       Debug("AssignResource(%s,%s): remaining pre-set to %d",
00224             t->id(), r->id(), t->remaining);
00225     }
00226 
00227     //  time remaining for resource to do the task
00228     // 100% efficient during vacation !!
00229     if ( !t->isVacation() )
00230         {
00231           real_remaining = int(t->remaining/r->efficiency()+.5);
00232           Debug("%s %d %f %d",
00233                 t->id(), t->remaining, r->efficiency(), real_remaining);
00234         }
00235     else
00236         {
00237           real_remaining = t->remaining;
00238         }
00239 
00240     if ( t->block() )
00241     {
00242         while ( r->IsAvailable(tstart, tstart+real_remaining-1) == 0 )
00243             ++tstart;
00244         BookResource(t, NULL, r, tstart, tstart+real_remaining-1,
00245                      TimeBlock::AUTO_SCHEDULE);
00246         if ( ( t->start() == INVALIDDAYNO )  || ( tstart < t->start() ) )
00247             t->setStart( tstart );
00248         tstart += real_remaining;
00249         t->remaining = 0;
00250         t->scheduled = 1;
00251     }
00252     else
00253     {
00254         // book individual days
00255       while ( real_remaining > 0 )
00256         {
00257             // still some days to book
00258             while ( ! r->IsAvailable(tstart, tstart) )
00259                 ++tstart;
00260 
00261             if ( ( t->start() == INVALIDDAYNO )  || ( tstart < t->start() ) )
00262                 t->setStart( tstart );
00263 
00264             for ( int i = real_remaining ; i > 0 ; --i )
00265             {
00266                 if ( r->IsAvailable(tstart, tstart + i - 1) )
00267                 {
00268                     Debug("AddTimeBlock(%s,%s,%d,%d)", t->id(), r->id(),
00269                           tstart, tstart + i - 1);
00270                     BookResource(t, NULL, r, tstart, tstart + i - 1,
00271                                  TimeBlock::AUTO_SCHEDULE);
00272                     real_remaining -= i;
00273                     tstart += i;
00274                     break;
00275                 }
00276             }
00277         } // still some days to book
00278       t->remaining = 0;
00279     } // book individual days
00280 
00281     --tstart;
00282     Debug("%s finish = %d", t->id(), tstart);
00283     t->setFinish( tstart );
00284     t->scheduled = 1;
00285 }
00286 
00287 
00288 void Project::ReverseAssignResource(TASK *t, RESOURCE *r, int tfinish)
00289 {
00290     int i;
00291     int real_rremaining;
00292 
00293     Debug("ReverseAssignResource(%s,%s,%d)", t->id(), r->id(), tfinish);
00294 
00295     if ( t->rscheduled )
00296     {
00297         Error("Trying to double-book (%s,%s,%d)", t->id(), r->id(), tfinish);
00298         return;
00299     }
00300 
00301     if ( t->assigned() == NULL )
00302         Error("ReverseAssignResource: t->assigned == NULL");
00303     if ( t->assigned() != r )
00304         Error("ReverseAssignResource trying to assign second resource to task %s",
00305               t->id());
00306 
00307     if ( t->rremaining == -1 )
00308         t->rremaining = t->fullduration();
00309 
00310     Debug("ReverseAssignResource t->rremaining = %d", t->rremaining);
00311 
00312 #if 0
00313 
00314     t->lfinish = tfinish;
00315     tfinish -= t->rremaining;
00316     t->rremaining = 0;
00317 
00318 #else
00319 
00320     //  time remaining for resource to do the task
00321     // 100% efficient during vacation !!
00322     if ( !t->isVacation() )
00323         {
00324           real_rremaining = int(t->rremaining/r->efficiency()+.5);
00325         }
00326     else
00327         {
00328           real_rremaining = t->rremaining;
00329         }
00330     if ( t->block() )
00331     {
00332         while ( ! r->reverseIsAvailable(tfinish-real_rremaining+1, tfinish) )
00333             --tfinish;
00334         ReverseBookResource(t, NULL, r, tfinish-real_rremaining+1, tfinish,
00335                             TimeBlock::AUTO_SCHEDULE);
00336         if ( ( t->lfinish() == INVALIDDAYNO )  || ( tfinish > t->lfinish() ) )
00337             t->setLfinish( tfinish );
00338         tfinish -= real_rremaining;     /* to calc start later */
00339         t->rremaining = 0;
00340     }
00341     else
00342     {
00343         while (real_rremaining > 0 )
00344         {
00345 
00346           Debug("ReverseAssignResource tfinish=%d", tfinish);
00347 
00348           // book individual days
00349           
00350           // still some days to book
00351           while ( ! r->reverseIsAvailable(tfinish, tfinish) )
00352             --tfinish;
00353 
00354           Debug("ReverseAssignResource got tfinish=%d", tfinish);
00355 
00356 
00357           if ( ( t->lfinish() == INVALIDDAYNO )  || ( tfinish > t->lfinish() ) )
00358             t->setLfinish( tfinish );
00359 
00360           for ( i = real_rremaining ; i > 0 ; --i )
00361             {
00362                 if ( r->reverseIsAvailable(tfinish - i + 1, tfinish) )
00363                 {
00364                     Debug("Reverse AddTimeBlock(%s,%s,%d,%d)",
00365                           t->id(), r->id(),
00366                           tfinish -i + 1, tfinish);
00367                     ReverseBookResource(t, NULL, r, tfinish - i + 1, tfinish,
00368                                         TimeBlock::AUTO_SCHEDULE);
00369                     real_rremaining -= i;
00370                     tfinish -= i;
00371                     break;
00372                 }
00373             }
00374         } // still some days to book
00375         t->rremaining = 0;
00376     } // book individual days
00377 
00378 #endif
00379     
00380     ++tfinish ;
00381     Debug("Reverse %s start = %d", t->id(), tfinish);
00382     t->setLstart( tfinish );
00383     t->rscheduled = 1;
00384 }
00385 
00386 
00387 int EarliestByDependency(TASK *t)
00388 {
00389     int t_earliest;
00390 
00391     if ( t->start() == INVALIDDAYNO )
00392         t_earliest = 0;
00393     else
00394         t_earliest = t->start();
00395 
00396     for ( TaskTimeBlockIterator tb = t->begin_when(); tb != t->end_when(); tb++ )
00397     {
00398         if ( tb->finish() + 1 > t_earliest )
00399         {
00400             t_earliest = tb->finish() + 1;
00401             //QQQ
00402         }
00403     }
00404     
00405     for ( TASK::PTRLIST::const_iterator pt = t->begin_depends(); 
00406           pt != t->end_depends(); 
00407           pt++ )
00408     {
00409         if ( (*pt)->finish() + 1 > t_earliest )
00410         {
00411             t_earliest = (*pt)->finish() + 1;
00412         }
00413     }
00414     return t_earliest;
00415 }
00416 
00417 RESOURCE *Project::FindEarliestResource(TASK *t, int t_earliest)
00418 {
00419     RESOURCE *r = NULL;
00420     RESOURCE *r2 = NULL;
00421     int tmin = MAX_TIME, tr;
00422     int real_remaining;
00423 
00424     for ( RPLCI rl = t->begin_cando(); rl != t->end_cando(); rl++ )
00425     {
00426         r2 = *rl;
00427         real_remaining = int(t->remaining/r2->efficiency()+.5);
00428         tr = FirstFreeTime(t, *rl, t_earliest, t->block() ? real_remaining : 1 );
00429         if( tmin > tr )
00430         {
00431             tmin = tr;
00432             r = *rl;
00433         }
00434     }
00435 
00436     return r;
00437 }
00438 
00439 
00440 void Project::AssignTask(TASK *t)
00441 {
00442     Debug("AssignTask(%s)", t->id());
00443 
00444     if ( t->scheduled )
00445     {
00446       Debug("AssignTask(%s) -- already scheduled.", t->id());
00447       return;                           /* well actually never reached */
00448     }
00449 
00450     // Trivial assignment case:
00451     if ( ( t->assigned() == NULL ) && ( t->numCandidates() == 1 ) )
00452         t->setAssigned( *(t->begin_cando()) );
00453     
00454     // If no-one assigned, find the best resource:
00455     if ( t->assigned() == NULL )
00456     {
00457         RESOURCE *r;
00458         int t_earliest = EarliestByDependency(t);
00459 
00460         if ( t->start() != INVALIDDAYNO )
00461         {
00462             if ( t_earliest <= t->start() )
00463                 t_earliest = t->start();
00464             else
00465                 Warning("Cannot start task %s at %s - moving to %s",
00466                         t->id(), days[t->start()].s, days[t_earliest].s);
00467         }
00468 
00469         r = FindEarliestResource(t, t_earliest);
00470         Debug("AssignTask(1)");
00471         AssignResource(t, r, t_earliest );
00472     }
00473     else // someone already assigned
00474     {
00475         if ( t->start() == INVALIDDAYNO )
00476           {
00477             int t_earliest = EarliestByDependency(t);
00478             Debug("AssignTask(2)");
00479             AssignResource(t, t->assigned(), t_earliest );
00480           }
00481         else
00482           {
00483             Debug("AssignTask(3)");
00484             int t_earliest = t->start();
00485             for ( TaskTimeBlockIterator tb=t->begin_when(); tb != t->end_when(); tb++ )
00486             {
00487                 if ( tb->finish() + 1 > t_earliest )
00488                 {
00489                     t_earliest = tb->finish() + 1;
00490                 }
00491             }
00492             AssignResource(t, t->assigned(), t_earliest);
00493         }
00494     }
00495     if ( t->start() == INVALIDDAYNO )
00496         Error("t->start == INVALIDDAYNO for task id %s", t->id());
00497 }
00498 
00499 
00500 int EligibleTask(TASK *t)
00501 {
00502     // if already scheduled, it is not eligible
00503     if ( t->scheduled )
00504         return 0;
00505 
00506     // this clause says a task is not eligible if all depends have resources
00507     // assigned. But the "past" command lets us assign a resource for only
00508     // part of the time so this test is not good enough!
00509     // for ( tl = t->depends; tl != NULL; tl = tl->next )
00510     //    if ( tl->task->assigned == NULL ) 
00511     //            return 0;
00512     // Instead a task is not eligible until all depends have been scheduled
00513     for ( TASK::PTRLIST::const_iterator pt = t->begin_depends(); 
00514           pt != t->end_depends(); 
00515           pt++ )
00516         if ( !(*pt)->scheduled ) 
00517             return 0;   
00518 
00519     return 1;
00520 }
00521 
00522 
00523 void Project::ReverseAssignTask(TASK *t)
00524 {
00525     int t_latest;
00526 
00527     Debug("ReverseAssignTask(%s)", t->id());
00528 
00529     if ( t->rscheduled )
00530     {
00531         Debug("ReverseAssignTask(%s) -- already scheduled.", t->id());
00532         return;                         /* well actually never reached */
00533     }
00534 
00535     // assume that the same resource will be used on the reverse schedule
00536     // as the forward, hence a resource must be assigned
00537     if ( t->assigned() == NULL )
00538         Error("ReverseAssignTask(%s) -- no resource assigned.", t->id());
00539 
00540     if ( t->lfinish() == INVALIDDAYNO )
00541     {
00542         t_latest = finishDay;
00543         for ( TASK::PTRLIST::const_iterator pt = t->begin_follows(); 
00544               pt != t->end_follows(); 
00545               pt++ )
00546             if ( (*pt)->lstart() <= t_latest )
00547                 t_latest = (*pt)->lstart() - 1;
00548         ReverseAssignResource(t, t->assigned(), t_latest );
00549     }
00550     else
00551     {
00552         t_latest = t->lfinish();
00553         for ( TaskTimeBlockIterator tb = t->begin_when(); tb != t->end_when(); tb++ )
00554             if ( tb->start() - 1 < t_latest )
00555                 t_latest = tb->start() - 1;
00556         ReverseAssignResource(t, t->assigned(), t_latest);
00557     }
00558 
00559     if ( t->lfinish() == INVALIDDAYNO )
00560         Error("t->lfinish == INVALIDDAYNO for task id %s", t->id());
00561 }
00562 
00563 
00564 int ReverseEligibleTask(TASK *t)
00565 {
00566     // if already scheduled, it is not eligible
00567     if ( t->rscheduled )
00568         return 0;
00569 
00570     for ( TASK::PTRLIST::const_iterator pt = t->begin_follows(); 
00571           pt != t->end_follows(); 
00572           pt++ )
00573         if ( !(*pt)->rscheduled ) 
00574             return 0;   
00575 
00576     return 1;
00577 }
00578 
00579 
00580 void Project::PredictOverruns()
00581 {
00582     int duration;
00583     char buf[1000];
00584 
00585     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00586     {
00587         TASK * t = *pt;
00588         float relative_days;
00589 
00590         /* copy the original full duration so that we can use it later
00591            when generating the slippage reports */
00592         t->setOrigfullduration();
00593 
00594         if ( t->percent_complete() == 0 )
00595             continue;                   /* no work done yet */
00596 
00597         if ( ( t->begin_when() == t->end_when() ) && ( t->nDays() == 0 ) )
00598             continue;                   /* no info on when work done */
00599 
00600         if ( t->begin_when() != t->end_when() )
00601         {
00602           relative_days = 0.;
00603             for ( TaskTimeBlockIterator tb = t->begin_when() ; tb != t->end_when(); tb++ )
00604             {
00605                 if ( tb->type() == TimeBlock::WORK_DONE )
00606                 {
00607                    relative_days += (tb->finish() - tb->start() + 1 )*tb->resource()->efficiency();
00608                 }
00609             }
00610         t->setDaysDone ( (int)(relative_days+0.5));
00611         }
00612 
00613         // Calculate the new duration   
00614         duration = (int)((double)t->nDays() / (0.01 * t->percent_complete()) + 0.5);
00615 
00616         // Apply the new duration to the task:
00617         if ( duration != t->fullduration() )
00618         {
00619             sprintf(buf,
00620                     "%s changed from %d to %d days, based on completion of %g%% in %d day%s.",
00621                     t->id(), t->fullduration(), duration, t->percent_complete(),
00622                     t->nDays(), t->nDays()>1?"s":"");
00623             t->setFullduration( duration );
00624             //QQQ
00625             if ( t->begin_when() != t->end_when() )
00626                 t->remaining = duration - t->nDays();
00627             if ( t->remaining > 0 )
00628                 t->scheduled = 0;
00629             t->setOverrun(buf);
00630         }
00631     }
00632 }
00633 
00634 
00635 void Project::ReverseScheduleTasks()
00636 {
00637     TASK *preferred;
00638     int parents;
00639     int schedFinish;
00640     int finish;
00641 
00642     /* if the last scheduled finish is after the manually assigned finish
00643        then extend the finish date */
00644     schedFinish = finishDay;
00645     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00646     {
00647         if ( (*pt)->isVacation() )
00648             continue;
00649         if ( ( schedFinish == INVALIDDAYNO )
00650              || ( (*pt)->finish() > schedFinish ) )
00651             schedFinish = (*pt)->finish();
00652     }
00653     if ( finishDay == INVALIDDAYNO )
00654     {
00655         finishDay = schedFinish;
00656     }
00657     else if ( schedFinish > finishDay )
00658     {
00659         Warning("Scheduled finish day (%s) is after assigned finish day (%s), ignoring latter",
00660                 days[schedFinish].s, days[finishDay].s);
00661         finishDay = schedFinish;
00662     }
00663 
00664     /* fprintf(stderr, "finishDay = %d\n", finishDay); */
00665 
00666     /* any task that is complete or partially complete must retain the
00667        same schedule as forward schedule */
00668     /* similiarly all vacations must have same "schedule" */
00669     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00670         if ( ( (*pt)->percent_complete() > 0.0 ) || (*pt)->isVacation() )
00671         {
00672             (*pt)->rscheduled = 1;
00673             (*pt)->setLstart( (*pt)->start() );
00674             (*pt)->setLfinish( (*pt)->finish() );
00675             (*pt)->copyWhenToReverseWhen();
00676             for (TaskTimeBlockIterator tb = (*pt)->begin_rwhen(); 
00677                  tb != (*pt)->end_rwhen() ; tb++ )
00678                 (*pt)->assigned()->addReverseTimeBlock((*pt), 
00679                                                        tb->start(), 
00680                                                        tb->finish(), 
00681                                                        tb->type());
00682         }
00683     
00684     do
00685     {
00686         // find the task with the latest finish
00687         finish = -1;
00688         parents = -1;
00689         preferred = NULL;
00690         for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00691         {
00692             if ( !ReverseEligibleTask(*pt) )
00693                 continue;
00694             else
00695             {
00696                 if( (*pt)->finish() > finish )
00697                 {
00698                     finish = (*pt)->finish();
00699                     preferred = (*pt);
00700                 }
00701             }
00702         }
00703 
00704         if ( preferred != NULL )
00705             ReverseAssignTask(preferred);
00706         
00707     }
00708     while ( preferred != NULL );
00709 
00710     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00711         if( !(*pt)->rscheduled )
00712             Error("Task %s not reverse scheduled - do you have a dependency loop?",
00713                   (*pt)->id());
00714 
00715     /* calculate slack */
00716     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00717         (*pt)->setSlack( (*pt)->lstart() - (*pt)->start() );
00718 
00719 #if 0
00720     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00721         if ( !(*pt)->isVacation() )
00722             fprintf(stderr, "task %s lstart %d lfinish %d slack %d\n",
00723                     (*pt)->id,
00724                     (*pt)->lstart,
00725                     (*pt)->lfinish,
00726                     (*pt)->slack);
00727 #endif
00728 }
00729 
00730 
00731 void Project::ScheduleMilestones()
00732 {
00733     /* assume task scheduling has been done */
00734     for ( MPLCI ml = beginMilestoneList() ; ml != endMilestoneList() ; ml++ )
00735     {
00736         MILESTONE *m = *ml;
00737         int day = INVALIDDAYNO;
00738         TASK *ct = NULL;
00739 
00740         for ( TPLCI tl = m->begin_depends() ; tl != m->end_depends() ; tl++ )
00741         {
00742             TASK *t = *tl;
00743             if ( ( day == INVALIDDAYNO ) || ( t->finish() > day ) )
00744             {
00745                 day = t->finish();
00746                 ct = t;
00747             }
00748         }
00749 
00750         m->setDay( day );
00751         m->setCritical( ct );
00752     }
00753 }
00754 
00755 
00756 void Project::ScheduleTasks()
00757 {
00758     TASK *preferred;
00759     int children;
00760 
00761     Debug("Scheduling tasks");
00762 
00763     do
00764     {
00765         children = -1;
00766         preferred = NULL;
00767         for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00768         {
00769             if ( !EligibleTask(*pt) )
00770                 continue;
00771 
00772             if( (*pt)->children() > children )
00773             {
00774                 children = (*pt)->children();
00775                 preferred = (*pt);
00776             }
00777         }
00778 
00779         if ( preferred != NULL )
00780             AssignTask(preferred);
00781         
00782     }
00783     while ( preferred != NULL );
00784 
00785     for ( TPLCI pt = mTaskList.begin() ; pt != mTaskList.end() ; pt++ )
00786         if( !(*pt)->scheduled )
00787             Error("Task %s not scheduled - do you have a dependency loop?",
00788                   (*pt)->id());
00789 }
00790 
00791 
00792 void Project::DoScheduling()
00793 {
00794     Debug("Doing scheduling");
00795 
00796     LoadDays();
00797 
00798     PredictOverruns();
00799 
00800     ScheduleTasks();
00801 
00802     ReverseScheduleTasks();
00803 
00804     ScheduleMilestones();
00805 }
00806 
00807 
00808 void Project::WorkBlock( TASK *t, RESOURCE * r, int d1, int d2, 
00809                          TimeBlock::Type type )
00810 {
00811     if ( t->nDays() != 0 )
00812         Error("Can't use past/future and done at same time(2)");
00813 
00814     if ( t->assigned() == NULL )
00815     {
00816         t->setAssigned(r);
00817     }
00818     else
00819     {
00820         if ( t->assigned() != r )
00821             Error("Two resources assigned to same task %s", t->id());
00822     }
00823 
00824     if ( t->start() == INVALIDDAYNO )
00825         t->setStart( d1 );
00826 
00827     if ( d1 < t->start() )
00828         Error("Out of order assignment to task %s", t->id());
00829 
00830     BookResource( t, NULL, r, d1, d2, static_cast<TimeBlock::Type>(type) );
00831 
00832     if ( t->remaining == -1 )
00833     {
00834       t->remaining = t->duration();
00835       t->setFullduration( t->remaining );
00836     }
00837     
00838     t->remaining -= (int)((d2-d1+1)*r->efficiency()+0.5);
00839     Debug("%d %d",(d2-d1+1), (int)((d2-d1+1)*r->efficiency()+0.5));
00840     if ( t->remaining <= 0 )
00841     {
00842         t->setFinish( d2 );
00843         t->scheduled = 1;
00844         if ( !t->isVacation() )
00845           Warning("Completed task %s",t->id());
00846     }
00847 }
00848 
00849 
00850 
00851 void Project::StartTask( TASK *t, int d, int type )
00852 {
00853    switch(type)
00854    {
00855        case TNORM:
00856            t->setStart(d);
00857            
00858            cerr << "StartTask: Invoking garbage - tread with care!\n";
00859 
00860            // printf("Setting %s->start to %d\n",t->id,d);
00861            if(  t->finish() == INVALIDDAYNO )
00862            {
00863               t-> setFinish( t->start() + t->duration() );
00864                // printf("Setting %s->finish to %d\n",t->id,t->finish);
00865            }
00866            else
00867                t->setDuration( t->finish() - t->start() );
00868 
00869            // single candidate?  just do it!
00870            if ( t->numCandidates() == 1 )
00871            {
00872                t->setAssigned( *(t->begin_cando()) );
00873                BookResource( t, NULL, t->assigned(), t->start(), t->start(), 
00874                              TimeBlock::AUTO_SCHEDULE );
00875                t->setDuration( t->duration() - 1 );
00876            }
00877 
00878            // fix the start of the reverse schedule too!
00879            t->setLstart(d);
00880            
00881            break;
00882            
00883        case TACTUAL:
00884            t->setAstart(d);
00885            // as for finish we just record the actual at this time
00886            // (no auto setting of the finish
00887            break;
00888            
00889        case TBASE:
00890            t->setBstart( d );
00891            // as for finish we just record the actual at this time
00892            // (no auto setting of the finish
00893            break;
00894            
00895        default:
00896            Error("Internal Error, unknown start-finish type; check that patchs are complete");
00897            break;
00898    }
00899 }
00900 
00901 
00902 
00903 void Project::FinishTask( TASK *t, int d, int type )
00904 {
00905     switch(type)
00906     {
00907         case TNORM:
00908             t->setFinish( d );
00909 
00910             cerr << "TASK::FinishTask: Invoking garbage - tread with care!\n";
00911             
00912             if ( t->start() == INVALIDDAYNO )
00913             {
00914                 t->setStart( t->finish() - t->duration() );
00915                 if ( t->start() < 0 )
00916                     Error("Finish task date generates too early start date");
00917             }
00918             else
00919             {
00920                 t->setDuration( t->finish() - t->start() );
00921                 if ( t->duration() <= 0 )
00922                     Error("Finish task date generates duration <= 0");
00923             }
00924   
00925             if ( t->assigned() != NULL )
00926             {
00927                 BookResource( t, NULL, t->assigned(), t->start(), t->finish(), 
00928                               TimeBlock::AUTO_SCHEDULE );
00929             }
00930             break;
00931 
00932         case TACTUAL:
00933             // for right now all we do is record the time,
00934             // later on we can work with actual duration
00935             t->setAfinish( d );
00936             break;
00937 
00938         case TBASE:
00939             t->setBfinish( d );
00940             break;
00941 
00942         default:
00943             Error("Internal Error, unknown start-finish type; check that patchs are complete");
00944             break;
00945     }
00946 }
00947 
00948 

Generated on Wed Feb 18 22:23:54 2004 for Opensched by doxygen1.2.15