Re: resource allocation behaviour
Lee Baylis <lee-gZngeWTYz/[email protected]> Fri, 28 Nov 2008 04:56:49 +0000
| Newsgroups | gmane.comp.gnome.apps.planner.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi, >>>> I would also like to extend the mrp-resource data schema with a >>>> 'maximum >>>> allocatable >>>> units' field definable on each resource. > > What is the definition of this number of units and how does it differ > from the time that a resource has at a certain moment according to its > calendar? When one assigns a task to a resource in the GUI, a dialog appears offering the option to assign a number of units (the default presented in the current release of planner is 100, implying 100% - this is already configurable by the user). In the resource usage view, the number of these units assigned at any one time is added up, and if it goes over 100 (again, implying 100%), then the resource is classed as overallocated and marked in red. Here, the figure of 100 was not configurable by the user, but was hard-coded in the planner-usage-row file. The idea, as far as I can tell from reading discussions in bugzilla, is that people should be able to choose whether or not they actually want 100(%) to be hard-coded as a maximum. Enabling this property on the resource allows a user to choose some other figure. Once set, usage would have to go above this new figure before the resource was marked red in the resource usage view and classed as overallocated. Also, different resources can be given different limits. One example I have been using this for when talking to people is if someone only works part time, but as you say, you can already get around this by changing the calendar for the resource. However, it is also usable to represent other circumstances - if you know one resource is more efficient than another, for example. Then resource 1, an experienced worker, could have a maximum of 100, and resource 2, an apprentice, could have a maximum of maybe 65. Another scenario may be if you want to allocate with more precision than 1/100 units - 1/1000, say, or one in a million. In any case, I think having something which is configurable with a sensible default is better than having something which is hard-coded, as is currently the case. >> Only the option on the project to set overload behaviour is left, so >> I will >> use a flag for now, then look at adding it to the schema when I start >> introducing algorithms to choose from - at that point I will have a >> better >> idea as to what behaviour this option will need to hold. > > I'm not sure what you mean by flag. Do you mean a custom project > property? I have been working with a #define variable in the mrp-project.h file, which if set to true turns on overload checking, and false turns it off and goes back to 'normal'. We can leave it set to false by default in any further patches I release until we feel it is time to add it to the schema. The other possibility is having it as a compile-time option, and this might be worth looking into if it looks like putting through a schema change is going to take any length of time. >> I reviewed everything and in general it looks pretty good. Most of my > comments below are about rather minor issues. Most of them I would just > fix myself before committing, but I'll mention them anyway. The number > in front is the line number. OK, thanks - I have attached revised patches for most of your comments and a changelog entry. Further discussion: > The first is that units_in_use is sort of a caching variable. There are > times when the values stored in them are not up-to-date. Some functions > have as a precondition that those values are up-to-date, so I would like > to have them verify that precondition. To enable them to do that, some > other functions should 'invalidate' that cache somehow. I hadn't covered this, mainly because it's not just that units_in_use is a cached variable - the larger issue is that GLists of MrpAssignmentEdges also effectively cache information. Anything one does within the project which invalidates units_in_use probably invalidates at least part of any edges GList in existence at the same time. For that reason, the life cycle of all the functions I have written is to build, analyse and then immediately free edges GLists, also freeing any cached units_in_use values. Any change to the project which requires these functions to be called again in order to recalculate units_in_use then requires a rebuilding of the edges GList. This is pretty much the same behaviour which was already present in planner in the resource usage view. I think this problem intellectually boils down to a design choice when planner was originally written, regarding whether the edge or the task was chosen as the fundamental object detailing project entries. For whatever reason, the task won, so all this current work with edges is made more difficult, as they have to be derived from tasks when needed. On the other hand, who knows what might be made more difficult if the edge had been chosen as the fundamental object (a task becoming more of a 'container' for edge objects). In any case, planner is pretty advanced now as it is, working with tasks. Coming back to your point, I agree, as it stands, another developer could still use the functions to build and analyse an edges GList without freeing it, then make the mistake of assuming it represented live data and not a cache. I could either get around this: - using a similar technique to the one you describe above, although I have no feeling right now whether it would be better to store a stale-cache gboolean within the edge objects, or within some other entity (the resource?) which then acts as a cache manager. I think, to be honest, this approach would not completely remove the problem - any other developer could still accidentally ignore a stale-cache gboolean when implementing other routines. - or, alternatively, provide for a permanent GList of edge objects to be bound to a resource, and make provision in the code for this list to be updated (presumably via signals) every time the resource's assignments are updated. This is much more work, and would require my delving further into the guts of planner, but might work out more efficient in the end since the edges list wouldn't have to be rebuilt every time a calculation is made. I did think about doing something like this latter, but I rejected it as it felt too much like trying to rewrite planner so that edges became a more fundamental quantity, the down side being duplication of information between edges and tasks, and confusion as to which type to use in different scenarios. >> mrp-assignment: > > 276: this function is hard to read (because of things like using the > enum in a subtraction and as a boolean condition). I think it can be > made more readable and faster at the same time by not splitting out the > 'common' part of mrp_assignment_edges_remove_to_last_before and > mrp_assignment_edges_remove_from_first_after. Point taken. I have removed it and the enum, and reworked the before and after functions as you suggested. However, the functions don't quite perform in the same way as the examples you gave, so they remain a little more long-winded. Now that they are two separate functions, however, I have ditched the two g_list_reverses, and used g_list_last instead. > 911: I don't see why mrp_resource_test_if_overallocated() should exist. > The MrpResourceAllocation enum is visible from the same places as this > function is, so people could just call mrp_resource_allocation_status > and compare. This function doesn't just call mrp_resource_allocation_status - it makes a call to mrp_resource_get_max_units_used_between as well. In effect, it first finds out what the max units in use are, then looks up that number in a 'table' of overallocation conditions to see whether overallocation has occurred. People would have to make both these calls and the comparison every time they wanted to check a resource's overallocation status. _______________________________________________ Planner-dev-list mailing list [email protected] http://mail.gnome.org/mailman/listinfo/planner-dev-list
ChangeLog
(text/plain, 133 KB) - not displayed
mrp-assignment.c.patch
(text/x-diff, 8.9 KB)
Index: libplanner/mrp-assignment.c =================================================================== --- libplanner/mrp-assignment.c (revision 940) +++ libplanner/mrp-assignment.c (working copy) @@ -3,6 +3,7 @@ * Copyright (C) 2001-2003 CodeFactory AB * Copyright (C) 2001-2003 Richard Hult <[email protected]> * Copyright (C) 2001-2002 Mikael Hallendal <[email protected]> + * Copyright (C) 2008 Lee Baylis <lee-gZngeWTYz/[email protected]> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -43,19 +44,20 @@ PROP_UNITS }; +static void assignment_class_init (MrpAssignmentClass *klass); +static void assignment_init (MrpAssignment *assignment); +static void assignment_finalize (GObject *object); +static void assignment_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void assignment_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static gint assignment_edge_compare (gconstpointer edge1, + gconstpointer edge2); -static void assignment_class_init (MrpAssignmentClass *klass); -static void assignment_init (MrpAssignment *assignment); -static void assignment_finalize (GObject *object); -static void assignment_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void assignment_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - static MrpObjectClass *parent_class; GType @@ -226,6 +228,33 @@ } } +static gint +assignment_edge_compare (gconstpointer edge1, + gconstpointer edge2) +{ + const MrpAssignmentEdge *a, *b; + + a = edge1; + b = edge2; + + if (a->time < b->time) { + return -1; + } + else if (a->time == b->time) { + /* if times are equal, overallocation calculations require that end_edges are sorted before start_edges */ + if (a->type < b->type) { + return 1; + } + else if (a->type == b->type) { + return 0; + } else { + return -1; + } + } else { + return 1; + } +} + /** * mrp_assignment_new: * @@ -267,8 +296,8 @@ * * Retrieves the #MrpResource associated with @assignment. * - * Return value: the resource associated with the assignment object. The reference - * count of the resource is not increased. + * Return value: the resource associated with the assignment object. + * The reference count of the resource is not increased. **/ MrpResource * mrp_assignment_get_resource (MrpAssignment *assignment) @@ -294,3 +323,212 @@ return assignment->priv->units; } + +/** + * mrp_assignment_edges_insert_sorted_from_assignment: + * @edges: a #GList optionally containing #MrpAssignmentEdge objects + * @assignment: an #MrpAssignment + * + * Determines the start and end edges (essentially times + assignment units) + * from an assignment's associated task, and inserts them into the list of + * edges sorted by mrptime. Mostly useful for tracking a MrpResource's + * total assignment units whilst scanning across a project. + * + * Return Value: pointer to the updated edges GList. + **/ +GList * +mrp_assignment_edges_insert_sorted_from_assignment (GList *edges, + MrpAssignment *assignment) +{ + MrpTask *task; + gint units; + MrpAssignmentEdge *start_edge, *end_edge; + + g_return_val_if_fail (MRP_IS_ASSIGNMENT (assignment), edges); + + task = mrp_assignment_get_task (assignment); + units = mrp_assignment_get_units (assignment); + + start_edge = g_new0 (MrpAssignmentEdge, 1); + start_edge->type = START_EDGE; + start_edge->time = mrp_task_get_work_start(task); + start_edge->units = units; + start_edge->assignment = assignment; + start_edge->task = task; + + edges = g_list_insert_sorted (edges, start_edge, assignment_edge_compare); + + end_edge = g_new0 (MrpAssignmentEdge, 1); + end_edge->type = END_EDGE; + end_edge->time = mrp_task_get_finish(task); + end_edge->units = units; + end_edge->assignment = assignment; + end_edge->task = task; + + edges = g_list_insert_sorted (edges, end_edge, assignment_edge_compare); + + return edges; +} + +/** + * mrp_assignment_edges_insert_sorted_from_assignment_list: + * @edges: a #GList containing #MrpAssignmentEdge objects + * @assignments: a #GList contianing #MrpAssignment objects + * + * Determines the sorted start and end edges for all passed assignments. + * Mostly useful for scanning a #MrpResource object's total assignment + * units across a project. + * + * Return Value: pointer to the updated edges #GList. + **/ +GList * +mrp_assignment_edges_insert_sorted_from_assignment_list (GList *edges, + GList *assignments) +{ + GList *a; + MrpAssignment *assignment; + + for (a = assignments; a; a = a->next) { + assignment = a->data; + edges = mrp_assignment_edges_insert_sorted_from_assignment(edges, assignment); + } + return edges; +} + +/** + * mrp_assignment_edges_plot_units_in_use: + * @edges: a #GList containing #MrpAssignmentEdge objects + * + * Iterates the #GList of #MrpAssignmentEdge objects calculating a + * running total for each #MrpAssignmentEdge by adding units for + * start edges and subtracting them for end edges. Mostly useful for + * calculating a #MrpResource object's allocation within a period. + * + * Return Value: pointer to the updated edges #GList. + **/ +GList * +mrp_assignment_edges_plot_units_in_use (GList *edges) +{ + GList *e; + MrpAssignmentEdge *edge; + gint units = 0; + + for (e = edges; e; e = e->next) { + edge = e->data; + units += mrp_assignment_edge_get_delta_units(edge); + edge->units_in_use = units; + } + return edges; +} + +/** + * mrp_assignment_edges_calculate_max_units_in_use: + * @edges: a #GList containing #MrpAssignmentEdge objects + * + * Iterates the #GList of #MrpAssignmentEdge objects consulting the + * running total for each #MrpAssignmentEdge and storing the maximum. + * + * Return Value: #gint containing the maximum units in use. + **/ +gint +mrp_assignment_edges_calculate_max_units_in_use (GList *edges) +{ + GList *e; + MrpAssignmentEdge *edge; + gint units = 0; + + for (e = edges; e; e = e->next){ + edge = e->data; + if (edge->units_in_use > units) { + units = edge->units_in_use; + } + } + return units; +} + +/** + * mrp_assignment_edges_remove_to_last_before: + * @edges: a #GList containing #MrpAssignmentEdge objects + * @time: a #mrptime + * + * Removes all but the last #MrpAssignmentEdge object before @time from @edges. + * Mostly useful for isolating time periods for later calculation. + * + * Return Value: pointer to the updated edges #GList. + **/ +GList * +mrp_assignment_edges_remove_to_last_before(GList *edges, + mrptime time) +{ + GList *e; + MrpAssignmentEdge *edge; + + if (edges){ + e = edges->next; + if (e){ + edge = e->data; + while (edge->time <= time){ + edges = g_list_delete_link(edges, edges); + e = edges->next; + if (!e) {break;} + edge = e->data; + } + } + } + return edges; +} + +/** + * mrp_assignment_edges_remove_from_first_after: + * @edges: a #GList containing #MrpAssignmentEdge objects + * @time: a #mrptime + * + * Removes all but the last #MrpAssignmentEdge object after @time from @edges. + * Mostly useful for isolating time periods for later calculation. + * + * Return Value: pointer to the updated edges #GList. + **/ +GList * +mrp_assignment_edges_remove_from_first_after(GList *edges, + mrptime time) +{ + GList *e, *last; + MrpAssignmentEdge *edge; + + if (edges){ + last = g_list_last(edges); + e = last->prev; + if (e){ + edge = e->data; + while (time <= edge->time){ + edges = g_list_delete_link(edges, last); + last = g_list_last(edges); + e = last->prev; + if (!e) {break;} + edge = e->data; + } + } + } + + return edges; +} + +/** + * mrp_assignment_edge_get_delta_units: + * @edge: a #MrpAssignmentEdge + * + * Edge-sensing function: returns the change in units associated with + * an assignment, either signed positive for a start edge, or negative + * for an end edge. + * + * Return Value: gint representing the change in assignment units at the edge. + **/ +gint +mrp_assignment_edge_get_delta_units (MrpAssignmentEdge *edge) +{ + if (edge->type == START_EDGE) { + return edge->units; + } else { + return 0 - edge->units; + } +}
mrp-assignment.h.patch
(text/x-diff, 2.9 KB)
Index: libplanner/mrp-assignment.h =================================================================== --- libplanner/mrp-assignment.h (revision 940) +++ libplanner/mrp-assignment.h (working copy) @@ -3,6 +3,7 @@ * Copyright (C) 2001-2002 CodeFactory AB * Copyright (C) 2001-2002 Richard Hult <[email protected]> * Copyright (C) 2001-2002 Mikael Hallendal <[email protected]> + * Copyright (C) 2008 Lee Baylis <lee-gZngeWTYz/[email protected]> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -36,6 +37,8 @@ typedef struct _MrpAssignmentClass MrpAssignmentClass; typedef struct _MrpAssignmentPriv MrpAssignmentPriv; +typedef enum _MrpAssignmentEdgeType MrpAssignmentEdgeType; +typedef struct _MrpAssignmentEdge MrpAssignmentEdge; struct _MrpAssignment { MrpObject parent; @@ -47,12 +50,36 @@ MrpObjectClass parent_class; }; -GType mrp_assignment_get_type (void) G_GNUC_CONST; +enum _MrpAssignmentEdgeType{ + START_EDGE, + END_EDGE +}; -MrpAssignment *mrp_assignment_new (void); +struct _MrpAssignmentEdge { + MrpAssignmentEdgeType type; + mrptime time; + gint units; + gint units_in_use; + MrpAssignment *assignment; + MrpTask *task; +}; -MrpTask *mrp_assignment_get_task (MrpAssignment *assignment); -MrpResource *mrp_assignment_get_resource (MrpAssignment *assignment); -gint mrp_assignment_get_units (MrpAssignment *assignment); +GType mrp_assignment_get_type (void) G_GNUC_CONST; +MrpAssignment *mrp_assignment_new (void); + +MrpTask *mrp_assignment_get_task (MrpAssignment *assignment); +MrpResource *mrp_assignment_get_resource (MrpAssignment *assignment); +gint mrp_assignment_get_units (MrpAssignment *assignment); +GList * mrp_assignment_edges_insert_sorted_from_assignment (GList *edges, + MrpAssignment *assignment); +GList * mrp_assignment_edges_insert_sorted_from_assignment_list (GList *edges, + GList *assignments); +GList * mrp_assignment_edges_plot_units_in_use (GList *edges); +gint mrp_assignment_edges_calculate_max_units_in_use (GList *edges); +GList * mrp_assignment_edges_remove_to_last_before (GList *edges, + mrptime time); +GList * mrp_assignment_edges_remove_from_first_after (GList *edges, + mrptime time); +gint mrp_assignment_edge_get_delta_units (MrpAssignmentEdge *edge); #endif /* __MRP_ASSIGNMENT_H__ */
mrp-resource.c.patch
(text/x-diff, 9.4 KB)
Index: libplanner/mrp-resource.c =================================================================== --- libplanner/mrp-resource.c (revision 940) +++ libplanner/mrp-resource.c (working copy) @@ -5,6 +5,7 @@ * Copyright (C) 2002-2003 Richard Hult <[email protected]> * Copyright (C) 2002 Mikael Hallendal <[email protected]> * Copyright (C) 2004 Alvaro del Castillo <[email protected]> + * Copyright (C) 2008 Lee Baylis <lee-gZngeWTYz/[email protected]> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -32,6 +33,9 @@ #include "mrp-task.h" #include "mrp-resource.h" +/* Constant for usage calculations +FIXME: need to alter the UI so that users can set the priv->units value per resource, then can remove this constant */ +#define USAGE_MAX_UNITS 100 struct _MrpResourcePriv { gchar *name; @@ -69,25 +73,27 @@ }; -static void resource_class_init (MrpResourceClass *klass); -static void resource_init (MrpResource *resource); -static void resource_finalize (GObject *object); -static void resource_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void resource_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void resource_calendar_changed (MrpCalendar *calendar, - MrpResource *resource); -static void resource_removed (MrpObject *object); -static void resource_invalidate_task_costs (MrpResource *resource); -static void resource_assignment_removed_cb (MrpAssignment *assignment, - MrpResource *resource); -static void resource_group_removed_cb (MrpGroup *group, - MrpResource *resource); +static void resource_class_init (MrpResourceClass *klass); +static void resource_init (MrpResource *resource); +static void resource_finalize (GObject *object); +static void resource_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void resource_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void resource_calendar_changed (MrpCalendar *calendar, + MrpResource *resource); +static void resource_removed (MrpObject *object); +static void resource_invalidate_task_costs (MrpResource *resource); +static void resource_assignment_removed_cb (MrpAssignment *assignment, + MrpResource *resource); +static void resource_group_removed_cb (MrpGroup *group, + MrpResource *resource); +static GList *resource_get_edges_with_units_in_use (MrpResource *resource, + GList *edges); static MrpObjectClass *parent_class; @@ -826,3 +832,206 @@ g_object_set (resource, "calendar", calendar, NULL); } + +/* There are two ways to look for overallocation: + + - the first is to chart a running total of a resource's allocation + across the whole project, and scan through it looking for total values + of allocation at either a time or some condition. + + This is the technique previously adopted in planner-usage-row, + now moved here and embellished upon. + + - The second is to scan a resource's allocations for those which are + active at a given time, and add up the usage. Whilst this is likely a + more efficient method for larger projects, we are rarely interested in + an individual time, more usually a period between two times, and so it + is not clear right now whether implementing this second will be of any use. +*/ + +/** + * mrp_resource_get_assignment_edges: + * @resource: a #MrpResource + * @edges: a #GList optionally containing #MrpAssignmentEdge objects + * + * Adds all the assignment edges for the resource to the edges glist, + * sorted by mrptime. Usually you want this list to be empty before + * calling this function. Free the edges list when done. Mostly useful + * for calculating total resource allocation over the project. + * + * Return Value: the updated edges GList + **/ +GList * +mrp_resource_get_assignment_edges (MrpResource *resource, + GList *edges) +{ + GList *assignments; + g_return_val_if_fail (MRP_IS_RESOURCE (resource), edges); + + assignments = mrp_resource_get_assignments (resource); + edges = mrp_assignment_edges_insert_sorted_from_assignment_list(edges, assignments); + + return edges; +} + +/** + * mrp_resource_allocation_status: + * @resource: a #MrpResource + * @units: a #gint + * + * Lookup table for allocation status based on the number of units of a + * resource which are allocated + * + * Return Value: an entry from the MrpResourceAllocation enum + **/ +MrpResourceAllocation +mrp_resource_allocation_status (MrpResource *resource, gint units) +{ + g_return_val_if_fail (MRP_IS_RESOURCE (resource), MRP_RESOURCE_OVERALLOCATED); +/* FIXME: replace USAGE_MAX_UNITS on a per-resource basis rather than a defined constant */ + if (units == 0) { + return MRP_RESOURCE_FREE; + } + else if (units < USAGE_MAX_UNITS) { + return MRP_RESOURCE_UNDERALLOCATED; + } + else if (units == USAGE_MAX_UNITS) { + return MRP_RESOURCE_ALLOCATED; + } else { + return MRP_RESOURCE_OVERALLOCATED; + } +} + +/** + * mrp_resource_test_if_overallocated: + * @resource: a #MrpResource + * + * Scans the project duration for the resource and determines if it is + * ever overallocated. + * + * Return Value: gboolean inidcating whether resource is ever overallocated + **/ +gboolean +mrp_resource_test_if_overallocated (MrpResource *resource) +{ + gint units; + + g_return_val_if_fail (MRP_IS_RESOURCE (resource), TRUE); + + units = mrp_resource_get_max_units_used_between(resource, NULL, NULL); + + if (mrp_resource_allocation_status(resource, units) == MRP_RESOURCE_OVERALLOCATED){ + return TRUE; + } + return FALSE; +} + +/** + * resource_get_edges_with_units_in_use: + * @resource: a #MrpResource + * + * Return Value: a GList of MrpAssignmentEdge objects with the running total + * of units in use calculated. + * + **/ +GList * +resource_get_edges_with_units_in_use (MrpResource *resource, + GList *edges) +{ + g_return_val_if_fail (MRP_IS_RESOURCE (resource), edges); + + edges = mrp_resource_get_assignment_edges(resource, edges); + edges = mrp_assignment_edges_plot_units_in_use(edges); + + return edges; +} + + +/** + * mrp_resource_get_max_units_used_between: + * @resource: a #MrpResource + * @start: a #mrptime marking the start of a period, or project start if #NULL + * @end: a #mrptime marking the end of a period, or project end if #NULL + * + * Plots cumulative allocation for the resource across the project, + * then scans between the start and end times given, and calculates + * the maximum allocation in this period. + * + * Return Value: gint indicating the maximum units which have been allocated + * to the resource in this period. + **/ + +gint +mrp_resource_get_max_units_used_between (MrpResource *resource, + mrptime start, + mrptime end) +{ + GList *edges = NULL; + gint units; + + /* FIXME: replace USAGE_MAX_UNITS on a per-resource basis rather than a defined constant */ + g_return_val_if_fail (MRP_IS_RESOURCE (resource), USAGE_MAX_UNITS + 1); + + edges = resource_get_edges_with_units_in_use(resource, edges); + + if (start){ + edges = mrp_assignment_edges_remove_to_last_before(edges, start); + } + if (end){ + edges = mrp_assignment_edges_remove_from_first_after(edges, end); + } + + units = mrp_assignment_edges_calculate_max_units_in_use(edges); + + g_list_free(edges); + + return units; +} + +/** + * mrp_resource_get_available_units_between: + * @resource: a #MrpResource + * @start: a #mrptime + * @end: a #mrptime + * + * Return Value: gint indicating the maximum units which are available + * for the resource in this period without causing overallocation. + **/ +gint +mrp_resource_get_available_units_between (MrpResource *resource, + mrptime start, + mrptime end) +{ +/* FIXME: replace USAGE_MAX_UNITS on a per-resource basis rather than a defined constant */ + g_return_val_if_fail (MRP_IS_RESOURCE (resource), -1); + /* gint available; + + available =*/ + return USAGE_MAX_UNITS - mrp_resource_get_max_units_used_between(resource, start, end); + + /* if (available < 0){ + available = 0; + } + return available;*/ +} + +/** + * mrp_resource_get_available_units_during: + * @resource: a #MrpResource + * @task: a #MrpTask + * + * Return Value: gint indicating the maximum units which are available + * for the resource between the times the task is running, without + * causing overallocation. + **/ +gint +mrp_resource_get_available_units_during (MrpResource *resource, + MrpTask *task) +{ + g_return_val_if_fail (MRP_IS_RESOURCE (resource), -1); + g_return_val_if_fail (MRP_IS_TASK (task), -1); + + return mrp_resource_get_available_units_between(resource, + mrp_task_get_work_start(task), + mrp_task_get_finish(task)); +}
planner-usage-row.c.patch
(text/x-diff, 13.9 KB)
Index: src/planner-usage-row.c
===================================================================
--- src/planner-usage-row.c (revision 940)
+++ src/planner-usage-row.c (working copy)
@@ -30,6 +30,7 @@
#include <stdio.h>
#include <libplanner/mrp-project.h>
#include <libplanner/mrp-resource.h>
+#include <libplanner/mrp-assignment.h>
#include <libplanner/mrp-task.h>
#include <glib/gi18n.h>
#include <libgnomecanvas/gnome-canvas.h>
@@ -175,23 +176,15 @@
static GdkBitmap *break_stipple = NULL;
static gchar break_stipple_pattern[] = { 0x03 };
-static GdkColor color_normal;
-static GdkColor color_normal_light;
-static GdkColor color_normal_dark;
+struct _PlannerUsageRowColorScheme {
+ GdkColor plain;
+ GdkColor light;
+ GdkColor dark;
+};
+typedef struct _PlannerUsageRowColorScheme PlannerUsageRowColorScheme;
-static GdkColor color_free;
-static GdkColor color_free_light;
-static GdkColor color_free_dark;
+static PlannerUsageRowColorScheme color_schemes[MRP_RESOURCE_LAST_ALLOCATION];
-static GdkColor color_underuse;
-static GdkColor color_underuse_light;
-static GdkColor color_underuse_dark;
-
-static GdkColor color_overuse;
-static GdkColor color_overuse_light;
-static GdkColor color_overuse_dark;
-
-
GType
planner_usage_row_get_type (void)
{
@@ -750,22 +743,21 @@
g_object_add_weak_pointer (G_OBJECT (complete_stipple),
(gpointer) & complete_stipple);
-
- gnome_canvas_get_color (item->canvas, "LightSkyBlue3", &color_normal);
- gnome_canvas_get_color (item->canvas, "#9ac7e0", &color_normal_light);
- gnome_canvas_get_color (item->canvas, "#7da1b5", &color_normal_dark);
+ gnome_canvas_get_color (item->canvas, "LightSkyBlue3", &color_schemes[MRP_RESOURCE_ALLOCATED].plain);
+ gnome_canvas_get_color (item->canvas, "#9ac7e0", &color_schemes[MRP_RESOURCE_ALLOCATED].light);
+ gnome_canvas_get_color (item->canvas, "#7da1b5", &color_schemes[MRP_RESOURCE_ALLOCATED].dark);
- gnome_canvas_get_color (item->canvas, "indian red", &color_overuse);
- gnome_canvas_get_color (item->canvas, "#de6464", &color_overuse_light);
- gnome_canvas_get_color (item->canvas, "#ba5454", &color_overuse_dark);
+ gnome_canvas_get_color (item->canvas, "indian red", &color_schemes[MRP_RESOURCE_OVERALLOCATED].plain);
+ gnome_canvas_get_color (item->canvas, "#de6464", &color_schemes[MRP_RESOURCE_OVERALLOCATED].light);
+ gnome_canvas_get_color (item->canvas, "#ba5454", &color_schemes[MRP_RESOURCE_OVERALLOCATED].dark);
- gnome_canvas_get_color (item->canvas, "grey", &color_underuse);
- gnome_canvas_get_color (item->canvas, "#d6d6d6", &color_underuse_light);
- gnome_canvas_get_color (item->canvas, "#a8a8a8", &color_underuse_dark);
+ gnome_canvas_get_color (item->canvas, "grey", &color_schemes[MRP_RESOURCE_UNDERALLOCATED].plain);
+ gnome_canvas_get_color (item->canvas, "#d6d6d6", &color_schemes[MRP_RESOURCE_UNDERALLOCATED].light);
+ gnome_canvas_get_color (item->canvas, "#a8a8a8", &color_schemes[MRP_RESOURCE_UNDERALLOCATED].dark);
- gnome_canvas_get_color (item->canvas, "medium sea green", &color_free);
- gnome_canvas_get_color (item->canvas, "#43c77e", &color_free_light);
- gnome_canvas_get_color (item->canvas, "#359e64", &color_free_dark);
+ gnome_canvas_get_color (item->canvas, "medium sea green", &color_schemes[MRP_RESOURCE_FREE].plain);
+ gnome_canvas_get_color (item->canvas, "#43c77e", &color_schemes[MRP_RESOURCE_FREE].light);
+ gnome_canvas_get_color (item->canvas, "#359e64", &color_schemes[MRP_RESOURCE_FREE].dark);
} else {
g_object_ref (complete_stipple);
}
@@ -829,45 +821,6 @@
}
typedef enum {
- START_ASSIGN,
- END_ASSIGN
-} date_type;
-
-typedef struct {
- date_type type;
- mrptime time;
- gint units;
- MrpAssignment *assignment;
- MrpTask *task;
-} Date;
-
-static gint
-usage_row_date_compare (gconstpointer date1,
- gconstpointer date2)
-{
- const Date *a, *b;
-
- a = date1;
- b = date2;
-
- if (a->time < b->time) {
- return -1;
- }
- else if (a->time == b->time) {
- if (a->type < b->type) {
- return -1;
- }
- else if (a->type == b->type) {
- return 0;
- } else {
- return 1;
- }
- } else {
- return 1;
- }
-}
-
-typedef enum {
ROW_MIDDLE = 0,
ROW_START = 1 << 0,
ROW_END = 1 << 1,
@@ -875,16 +828,16 @@
} RowChunk;
static void
-usage_row_draw_resource_ival (mrptime start,
- mrptime end,
- gint units,
- RowChunk chunk,
- GdkDrawable *drawable,
- GnomeCanvasItem *item,
- gint x,
- gint y,
- gint width,
- gint height)
+usage_row_draw_resource_ival (mrptime start,
+ mrptime end,
+ PlannerUsageRowColorScheme *color_scheme,
+ RowChunk chunk,
+ GdkDrawable *drawable,
+ GnomeCanvasItem *item,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
{
PlannerUsageRow *row;
PlannerUsageRowPriv *priv;
@@ -972,17 +925,7 @@
return;
}
- if (units == 0) {
- gdk_gc_set_foreground (priv->fill_gc, &color_free);
- }
- else if (units < 100) {
- gdk_gc_set_foreground (priv->fill_gc, &color_underuse);
- }
- else if (units == 100) {
- gdk_gc_set_foreground (priv->fill_gc, &color_normal);
- } else {
- gdk_gc_set_foreground (priv->fill_gc, &color_overuse);
- }
+ gdk_gc_set_foreground (priv->fill_gc, &color_scheme->plain);
/* Draw the central part of the chunk */
if (rr_xend >= rr_xstart && rr_yend >= rr_ystart) {
@@ -993,20 +936,10 @@
rr_xend - rr_xstart + 1, rr_yend - rr_ystart + 1);
}
- if (units == 0) {
- gdk_gc_set_foreground (priv->fill_gc, &color_free_light);
- }
- else if (units < 100) {
- gdk_gc_set_foreground (priv->fill_gc, &color_underuse_light);
- }
- else if (units == 100) {
- gdk_gc_set_foreground (priv->fill_gc, &color_normal_light);
- } else {
- gdk_gc_set_foreground (priv->fill_gc, &color_overuse_light);
- }
-
//gdk_gc_set_foreground (priv->fill_gc, &color_high);
+ gdk_gc_set_foreground (priv->fill_gc, &color_scheme->light);
+
/* Top of the shadow. */
if (cs_ystart == rs_ystart) {
gdk_draw_line (drawable, priv->fill_gc, r_xstart, rs_ystart,
@@ -1019,20 +952,10 @@
rs_xstart, cs_yend);
}
- if (units == 0) {
- gdk_gc_set_foreground (priv->fill_gc, &color_free_dark);
- }
- else if (units < 100) {
- gdk_gc_set_foreground (priv->fill_gc, &color_underuse_dark);
- }
- else if (units == 100) {
- gdk_gc_set_foreground (priv->fill_gc, &color_normal_dark);
- } else {
- gdk_gc_set_foreground (priv->fill_gc, &color_overuse_dark);
- }
-
//gdk_gc_set_foreground (priv->fill_gc, &color_shadow);
+ gdk_gc_set_foreground (priv->fill_gc, &color_scheme->dark);
+
/* Bottom of the shadow. */
if (cs_yend == rs_yend) {
gdk_draw_line (drawable, priv->fill_gc, r_xstart, rs_yend,
@@ -1076,7 +999,7 @@
r_yend);
}
}
-
+
static void
usage_row_draw_resource (PlannerUsageRow *row,
GdkDrawable *drawable,
@@ -1086,52 +1009,24 @@
gint width,
gint height)
{
- GList *dates;
- MrpResource *resource;
- MrpTask *root;
- MrpAssignment *assignment;
- MrpTask *task;
- MrpProject *project;
- GList *assignments;
- GList *a, *d;
- Date *date, *date0, *date1;
- mrptime work_start, finish, previous_time;
- gint units;
- RowChunk chunk;
+ GList *e, *edges;
+ MrpResource *resource;
+ MrpTask *root;
+ MrpProject *project;
+ MrpAssignmentEdge *edge;
+ mrptime finish, previous_time;
+ gint units;
+ RowChunk chunk;
+ MrpResourceAllocation status;
+ PlannerUsageRowColorScheme *color_scheme;
resource = row->priv->resource;
- dates = NULL;
-
project = mrp_object_get_project (MRP_OBJECT (resource));
- assignments = mrp_resource_get_assignments (resource);
- for (a = assignments; a; a = a->next) {
- assignment = a->data;
+ edges = NULL;
+ edges = mrp_resource_get_assignment_edges(resource, edges);
- task = mrp_assignment_get_task (assignment);
- work_start = mrp_task_get_work_start (task);
- finish = mrp_task_get_finish (task);
-
- units = mrp_assignment_get_units (assignment);
-
- date0 = g_new0 (Date, 1);
- date0->type = START_ASSIGN;
- date0->time = work_start;
- date0->units = units;
- date0->assignment = assignment;
- date0->task = task;
-
- date1 = g_new0 (Date, 1);
- date1->type = END_ASSIGN;
- date1->time = finish;
- date1->units = units;
- date1->assignment = assignment;
- date1->task = task;
- dates = g_list_insert_sorted (dates, date0, usage_row_date_compare);
- dates = g_list_insert_sorted (dates, date1, usage_row_date_compare);
- }
-
units = 0;
previous_time = mrp_project_get_project_start (project);
@@ -1140,39 +1035,37 @@
chunk = ROW_START;
- for (d = dates; d; d = d->next) {
- date = d->data;
+ for (e = edges; e; e = e->next) {
+ edge = e->data;
- if (date->time != previous_time) {
- if (date->time == finish) {
+ if (edge->time != previous_time) {
+ if (edge->time == finish) {
chunk |= ROW_END;
}
+ /* Set the color scheme pointer based on allocation units */
+
+ status = mrp_resource_allocation_status(resource, units);
+ color_scheme = color_schemes + (int)status;
usage_row_draw_resource_ival (previous_time,
- date->time,
- units,
- chunk,
- drawable, item,
- x, y, width, height);
+ edge->time,
+ color_scheme,
+ chunk,
+ drawable, item,
+ x, y, width, height);
chunk &= ~ROW_START;
- previous_time = date->time;
+ previous_time = edge->time;
}
-
- if (date->type == START_ASSIGN) {
- units += date->units;
- } else {
- units -= date->units;
- }
- g_free (date);
+ units += mrp_assignment_edge_get_delta_units(edge);
}
- g_list_free (dates);
+ g_list_free (edges);
if (!(chunk & ROW_END)) {
chunk |= ROW_END;
usage_row_draw_resource_ival (previous_time,
finish,
- units,
+ &color_schemes[MRP_RESOURCE_FREE],
chunk,
drawable, item,
x, y, width, height);
@@ -1257,7 +1150,7 @@
priv->complete_gc);
}
- gdk_gc_set_foreground (priv->fill_gc, &color_normal);
+ gdk_gc_set_foreground (priv->fill_gc, &color_schemes[MRP_RESOURCE_ALLOCATED].plain);
gdk_draw_rectangle (drawable,
priv->fill_gc,
@@ -1276,7 +1169,7 @@
gdk_draw_line (drawable, priv->frame_gc, rx1, cy1, rx2, cy1);
gdk_draw_line (drawable, priv->frame_gc, rx1, cy2, rx2, cy2);
- gdk_gc_set_foreground (priv->fill_gc, &color_normal_light);
+ gdk_gc_set_foreground (priv->fill_gc, &color_schemes[MRP_RESOURCE_ALLOCATED].light);
gdk_draw_line (drawable,
priv->fill_gc,
rx1 + 0, cy1 + 1, rx2 - 0, cy1 + 1);
@@ -1287,7 +1180,7 @@
rx1 + 1, cy1 + 1, rx1 + 1, cy2 - 1);
}
- gdk_gc_set_foreground (priv->fill_gc, &color_normal_dark);
+ gdk_gc_set_foreground (priv->fill_gc, &color_schemes[MRP_RESOURCE_ALLOCATED].dark);
gdk_draw_line (drawable,
priv->fill_gc,
rx1 + 0, cy2 - 1, rx2 - 0, cy2 - 1);