OSDN Git Service

Separating "over resource limits load" from free load in graphical charts.
authorGrégoire Barbier <g@g76r.eu>
Tue, 4 Aug 2009 07:19:01 +0000 (09:19 +0200)
committerChris Schlaeger <cs@kde.org>
Sun, 23 Aug 2009 12:27:51 +0000 (14:27 +0200)
Until now, the time that is not allocatable to a resource due to
its usage limits was reported as free load with green bars on Gantt
and resource charts. This is misleading or at less hard to read for
part time resources.

Now this time is reported separatly with another bar in another
color.

TaskJugglerUI/TjGanttChart.cpp
TaskJugglerUI/TjResourceReport.cpp
TestSuite/Syntax/Correct/RatioLimits.tjp
taskjuggler/Resource.cpp
taskjuggler/Resource.h

index 87f4ca2..f81c940 100644 (file)
@@ -71,6 +71,7 @@ TjGanttChart::TjGanttChart(QObject* obj)
     colors["taskLoadCol"] = QColor("#FD13C6");
     colors["otherLoadCol"] = QColor("#FF8D13");
     colors["freeLoadCol"] = QColor("#00AC00");
+    colors["overLimitCol"] = QColor("#505050");
     colors["loadFrameCol"] = Qt::black;
     colors["hightlightCol"] = Qt::white;
     colors["errorCol"] = Qt::red;
@@ -302,6 +303,7 @@ TjGanttChart::calcLegendHeight(int width)
     legendLabels.append(i18n("Completed Task"));
     legendLabels.append(i18n("Task Work Load"));
     legendLabels.append(i18n("Other Work Load"));
+    legendLabels.append(i18n("Over Resource Usage Limits"));
     legendLabels.append(i18n("Free Load"));
 
     maxLegendLabelWidth = 0;
@@ -496,7 +498,13 @@ TjGanttChart::generateLegend(int width, int height)
                             symbolWidth, legendLabelHeight,
                             "otherLoadCol", Qt::Dense4Pattern, legend);
                 break;
-            case 7:     // Free time
+            case 7:     // Over limit time
+                drawLoadBar(x + margin,
+                            (int) (legendLabelHeight * (1.5 * row + 0.5)),
+                            symbolWidth, legendLabelHeight,
+                            "overLimitCol", Qt::Dense4Pattern, legend);
+                break;
+            case 8:     // Free time
                 drawLoadBar(x + margin,
                             (int) (legendLabelHeight * (1.5 * row + 0.5)),
                             symbolWidth, legendLabelHeight,
@@ -1743,27 +1751,30 @@ TjGanttChart::drawResourceLoadColum(Resource* r, const Task* t,
     // Now we are calculating the load of the resource with respect to this
     // task, to all tasks, and we calculate the not yet allocated load.
     Interval period(start, end);
-    double freeLoad = r->getEffectiveFreeLoad(scenario, period);
+    double freeLoad;
+    double overLoad;
     double taskLoad;
     double load;
     Qt::BrushStyle pattern1 , pattern2, pattern3;
 
     if (r->getEfficiency() > 0.0)
     {
-        freeLoad = r->getEffectiveFreeLoad(scenario, period);
+        freeLoad = r->getFreeLoad(scenario, period, true);
+        overLoad = r->getOverLimitLoad(scenario, period, true);
         taskLoad = r->getEffectiveLoad(scenario, period, AllAccounts, t);
         load = r->getEffectiveLoad(scenario, period, AllAccounts);
         pattern1 = pattern2 = pattern3 = Qt::Dense4Pattern;
     }
     else
     {
-        freeLoad = r->getAvailableTimeLoad(scenario, period);
+        freeLoad = r->getFreeLoad(scenario, period, false);
+        overLoad = r->getOverLimitLoad(scenario, period, false);
         taskLoad = r->getAllocatedTimeLoad(scenario, period, AllAccounts, t);
         load = r->getAllocatedTimeLoad(scenario, period, AllAccounts);
         pattern1 = pattern2 = pattern3 = Qt::Dense5Pattern;
     }
     double otherLoad = load - taskLoad;
-    double maxLoad = load + freeLoad;
+    double maxLoad = load + freeLoad + overLoad;
     if (maxLoad <= 0.0)
         return;
 
@@ -1774,6 +1785,8 @@ TjGanttChart::drawResourceLoadColum(Resource* r, const Task* t,
                                             (taskLoad / maxLoad));
     int colOtherLoadTop = colBottom - (int) ((colBottom - colTop) *
                                              (load / maxLoad));
+    int colOverLimitTop = colBottom - (int) ((colBottom - colTop) *
+                                             ((load + overLoad) / maxLoad)); 
 
     // Now we draw the columns. But only if the load is larger than 0.
     if (taskLoad > 0.0)
@@ -1796,10 +1809,16 @@ TjGanttChart::drawResourceLoadColum(Resource* r, const Task* t,
                     "otherLoadCol", pattern2, chart);
     }
 
+    if (overLoad > 0.0)
+    {
+        drawLoadBar(cx, colOverLimitTop, cw, colOtherLoadTop - colOverLimitTop,
+                    "overLimitCol", pattern2, chart);
+    }
+
     if (freeLoad > 0.0)
     {
         // Unallocated load.
-        drawLoadBar(cx, colTop, cw, colOtherLoadTop - colTop, "freeLoadCol",
+        drawLoadBar(cx, colTop, cw, colOverLimitTop - colTop, "freeLoadCol",
                     pattern3, chart);
     }
 }
index 1057779..eef8fce 100644 (file)
@@ -252,8 +252,8 @@ TjResourceReport::generateStatusBarText(const QPoint& pos,
         double load = r->getEffectiveLoad(scenario, iv, AllAccounts);
         double allocatedTimeLoad = r->getAllocatedTimeLoad
             (scenario, iv, AllAccounts);
-        double freeLoad = r->getEffectiveFreeLoad(scenario, iv);
-        double freeTimeLoad = r->getAvailableTimeLoad (scenario, iv);
+        double freeLoad = r->getFreeLoad(scenario, iv, true);
+        double freeTimeLoad = r->getFreeLoad (scenario, iv, false);
         double totalLoad = load + freeLoad;
         double totalTimeLoad = allocatedTimeLoad + freeTimeLoad;
         text = i18n("%1(%2) - %3 -  Effort: %4 (%5%)  "
index ab0143b..ca0b366 100644 (file)
@@ -17,12 +17,35 @@ resource r2 "R2" {
   vacation 2009-1-6 +1d
 }
 
+resource r3 "R3" {
+}
+
+resource equip "equipement" {
+  resource r4 "R4 (efficiency 0)" {
+    efficiency 0.0
+    limits {dailymax 7h}
+  }
+}
+
 task t2 "T2" {
   allocate r2 { limits { weeklymax 5d } }
+  allocate equip { mandatory limits {dailymax 5h} }
+}
+
+task t3 "T3" {
+  allocate r3 { limits {dailymax 2h} }
+  allocate equip { mandatory limits {dailymax 2h} }
+}
+
+task t4 "T4" {
+  priority 499
+  effort 10d
+  allocate r3
 }
 
 taskreport "Gantt" {
   columns id,start,end,maxeffort,chart
+  sorttasks tree,sequenceup
 }
 
 htmltaskreport "/tmp/tjweekly.html" {
@@ -36,3 +59,7 @@ htmltaskreport "/tmp/tjmonthly.html" {
   loadunit days
   hideresource 0
 }
+
+resourcereport "Resource" {
+  columns chart
+}
\ No newline at end of file
index 75eea1d..fe7ee7e 100644 (file)
@@ -57,6 +57,7 @@ Resource::Resource(Project* p, const QString& i, const QString& n,
     specifiedBookings(new SbBooking**[p->getMaxScenarios()]),
     scoreboards(new SbBooking**[p->getMaxScenarios()]),
     scenarios(new ResourceScenario[p->getMaxScenarios()]),
+    frozenScenarios(new bool[p->getMaxScenarios()]),
     allocationProbability(new double[p->getMaxScenarios()])
 {
     vacations.setAutoDelete(true);
@@ -68,6 +69,7 @@ Resource::Resource(Project* p, const QString& i, const QString& n,
     {
         scoreboards[sc] = 0;
         specifiedBookings[sc] = 0;
+        frozenScenarios[sc] = false;
     }
 
     for (int i = 0; i < p->getMaxScenarios(); ++i)
@@ -398,6 +400,9 @@ Resource::getBooking(time_t date)
     if (!limits)
         return BOOKING_FREE;
 
+    // TODO: stop here if the scenario is frozen (but how should I guess which
+    // is the current scenario?)
+
     if ((limits && limits->getDailyMax() > 0))
     {
         // Now check that the resource is not overloaded on this day.
@@ -573,6 +578,8 @@ Resource::bookSlot(uint idx, SbBooking* nb, int overtime)
 bool
 Resource::bookInterval(Booking* nb, int sc, int sloppy, int overtime)
 {
+    assert(frozenScenarios[sc] == false);
+
     uint sIdx = sbIndex(nb->getStart());
     uint eIdx = sbIndex(nb->getEnd());
 
@@ -649,6 +656,8 @@ Resource::bookInterval(Booking* nb, int sc, int sloppy, int overtime)
 bool
 Resource::addBooking(int sc, Booking* nb, int sloppy, int overtime)
 {
+    assert(frozenScenarios[sc] == false);
+
     SbBooking** tmp = scoreboard;
 
     if (scoreboards[sc])
@@ -1017,7 +1026,7 @@ Resource::getAllocatedSlots(int sc, uint startIdx, uint endIdx,
 }
 
 double
-Resource::getEffectiveFreeLoad(int sc, const Interval& period)
+Resource::getFreeLoad(int sc, const Interval& period, bool effective)
 {
     double load = 0.0;
     Interval iv(period);
@@ -1027,7 +1036,7 @@ Resource::getEffectiveFreeLoad(int sc, const Interval& period)
     if (hasSubs())
     {
         for (ResourceListIterator rli(*sub); *rli != 0; ++rli)
-            load += (*rli)->getEffectiveFreeLoad(sc, iv);
+            load += (*rli)->getFreeLoad(sc, iv, effective);
     }
     else
     {
@@ -1035,28 +1044,59 @@ Resource::getEffectiveFreeLoad(int sc, const Interval& period)
         uint endIdx = sbIndex(iv.getEnd());
         load = project->convertToDailyLoad
             (getAvailableSlots(sc, startIdx, endIdx) *
-             project->getScheduleGranularity()) * efficiency;
+             project->getScheduleGranularity())
+            * ((!effective || efficiency == 0.0) ? 1.0 : efficiency);
     }
 
     return load;
 }
 
 double
-Resource::getAvailableTimeLoad(int sc, const Interval& period)
-{
-    return project->convertToDailyLoad(getAvailableTime(sc, period));
-}
-
-long
-Resource::getAvailableTime(int sc, const Interval& period)
+Resource::getOverLimitLoad(int sc, const Interval& period, bool effective)
 {
+    double load = 0.0;
     Interval iv(period);
     if (!iv.overlap(Interval(project->getStart(), project->getEnd())))
-        return 0;
+        return 0.0;
 
-    return getAvailableSlots(sc, sbIndex(iv.getStart()),
-                             sbIndex(iv.getEnd())) *
-        project->getScheduleGranularity();
+    if (hasSubs())
+    {
+        for (ResourceListIterator rli(*sub); *rli != 0; ++rli)
+            load += (*rli)->getOverLimitLoad(sc, iv, effective);
+    }
+    else
+    {
+        uint startIdx = sbIndex(iv.getStart());
+        uint endIdx = sbIndex(iv.getEnd());
+        load = project->convertToDailyLoad
+            (getOverLimitSlots(sc, startIdx, endIdx) *
+             project->getScheduleGranularity())
+            * ((!effective || efficiency == 0.0) ? 1.0 : efficiency);
+    }
+
+    return load;
+}
+
+void
+Resource::freezeScenario(int sc)
+{
+    if (frozenScenarios[sc])
+        return;
+    // browse all booking slots for a given scenario, to force
+    // evaluation of resource usage limits a last time before using
+    // scoreboard content in getAvailableSlots() et getOverLimitSlots()
+    SbBooking** tmp = scoreboard;
+    scoreboard = scoreboards[sc];
+    if (!scoreboard)
+    {
+        initScoreboard();
+        scoreboards[sc] = scoreboard;
+    }
+    for (time_t d = project->getStart(); d < project->getEnd();
+         d += project->getScheduleGranularity())
+        getBooking(d);
+    scoreboard = tmp;
+    frozenScenarios[sc] = true;
 }
 
 long
@@ -1071,21 +1111,36 @@ Resource::getAvailableSlots(int sc, uint startIdx, uint endIdx)
     }
     else
     {
-        if (!scoreboards[sc])
-        {
-            scoreboard = scoreboards[sc];
-            initScoreboard();
-            scoreboards[sc] = scoreboard;
-        }
-
+        freezeScenario(sc);
         for (uint i = startIdx; i <= endIdx; i++)
-            if (scoreboards[sc][i] == 0)
+            if (scoreboards[sc][i] == SB_FREE)
                 availSlots++;
     }
 
     return availSlots;
 }
 
+long
+Resource::getOverLimitSlots(int sc, uint startIdx, uint endIdx)
+{
+    long overSlots = 0;
+
+    if (!sub->isEmpty())
+    {
+        for (ResourceListIterator rli(*sub); *rli != 0; ++rli)
+            overSlots += (*rli)->getOverLimitSlots(sc, startIdx, endIdx);
+    }
+    else
+    {
+        freezeScenario(sc);
+        for (uint i = startIdx; i <= endIdx; i++)
+            if (scoreboards[sc][i] == SB_OVERLIMIT)
+                overSlots++;
+    }
+
+    return overSlots;
+}
+
 double
 Resource::getCredits(int sc, const Interval& period, AccountType acctType,
                      const Task* task) const
@@ -1479,6 +1534,3 @@ QDomElement Resource::xmlIDElement( QDomDocument& doc ) const
 
    return( elem );
 }
-
-
-
index 5dd319f..c32953b 100644 (file)
@@ -107,7 +107,6 @@ public:
 
     bool book(Booking* b);
 
-    bool bookSlot(uint idx, SbBooking* nb, int overtime = 0);
     bool bookInterval(Booking* b, int sc, int sloppy = 0, int overtime = 0);
     bool addBooking(int sc, Booking* b, int sloppy = 0, int overtime = 0);
 
@@ -134,11 +133,14 @@ public:
 
     /***
      * Return the unallocated load of the resource and its children wheighted
-     * by their efficiency.
+     * by their efficiency if effective == true and efficiency != 0.0.
      */
-    double getEffectiveFreeLoad(int sc, const Interval& period);
-    double getAvailableTimeLoad(int sc, const Interval& period);
-    long getAvailableTime(int sc, const Interval& period);
+    double getFreeLoad(int sc, const Interval& period, bool effective);
+    inline double getEffectiveFreeLoad(int sc, const Interval& period)
+    {
+        return getFreeLoad(sc, period, true);
+    }
+    double getOverLimitLoad(int sc, const Interval& period, bool effective);
 
     double getCredits(int sc, const Interval& i, AccountType acctType,
                       const Task* task = 0) const;
@@ -192,12 +194,16 @@ private:
 
     void initScoreboard();
 
+    bool bookSlot(uint idx, SbBooking* nb, int overtime = 0);
+
     long getCurrentLoadSub(uint startIdx, uint endIdx, const Task* task) const;
 
     long getAllocatedSlots(int sc, uint startIdx, uint endIdx,
                            AccountType acctType, const Task* task) const;
 
     long getAvailableSlots(int sc, uint startIdx, uint endIdx);
+    long getOverLimitSlots(int sc, uint startIdx, uint endIdx);
+    void freezeScenario(int sc);
 
     bool isAllocatedSub(int sc, uint startIdx, uint endIdx, const QString&
                         prjId) const;
@@ -273,6 +279,8 @@ private:
 
     ResourceScenario* scenarios;
 
+    bool* frozenScenarios;
+
     /**
      * The allocation probability is calculated prior to scheduling a
      * scenario. It is the expected average effort the resource has to deliver