precedence 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,80 +1,60 @@
1
+ # Requirements from Ruby standard library
1
2
  require('erb')
3
+ require('yaml')
2
4
 
3
- module Precedence
4
-
5
- # An overwritten Hash that has all keys as strings
6
- class ActivityHash < Hash #:nodoc:
7
- def[]=(ref,value)
8
- super(ref.to_s, value)
9
- end
10
-
11
- def[](ref)
12
- super(ref.to_s)
13
- end
14
- end
5
+ module Precedence
15
6
 
16
- # Represents an entire precedence network.
7
+ # Represents an entire precedence network. It wraps around an underlying
8
+ # directed graph of Activity objects and adds a number of utility methods
9
+ # and extra functionality that would not be available if one just used
10
+ # Activity objects.
17
11
  class Network
18
12
 
19
13
  # A hashed collection of all the activities in the network. The index
20
14
  # is the reference of the activity.
21
15
  attr_reader :activities
22
16
 
17
+ # The reference for the StartActivity in the network.
18
+ START = Precedence::StartActivity::REFERENCE
19
+
20
+ # The reference for the FinishActivity in the network.
21
+ FINISH = Precedence::FinishActivity::REFERENCE
22
+
23
23
  # Initialises the precedence network. The descriptions for the start
24
24
  # and finish activities can be set in the parameters. The start and
25
25
  # finish activities of the network have as references 'start' and
26
- # 'finish' respectively (and as such these references are reserved)
26
+ # 'finish' (held in constants Network::START and Network::FINISH)
27
+ # respectively (and as such these references are reserved)
27
28
  # and a duration of 0.
28
- def initialize(startDescription='Start',
29
- finishDescription='Finish')
30
- @start = Precedence::StartFinishActivity.new('start',
31
- startDescription)
32
- @finish = Precedence::StartFinishActivity.new('finish',
33
- finishDescription)
34
- @activities = Precedence::ActivityHash.new
35
- @activities[@start.reference] = @start
36
- @activities[@finish.reference] = @finish
29
+ #
30
+ # Example usage:
31
+ # precNetwork = Precedence::Network.new('Begin','End')
32
+ # will create a new precedence network with the start and finish
33
+ # activities having descriptions of 'Begin' and 'End' respectively.
34
+ def initialize()
35
+ @start = Precedence::StartActivity.new
36
+ @finish = Precedence::FinishActivity.new
37
+ @activities = Precedence::Utilities::ActivityHash.new(@start,@finish)
38
+ #@activities[START] = @start
39
+ #@activities[FINISH] = @finish
37
40
  end
38
41
 
42
+
39
43
  # Creates a new activity and adds it to the precedence network. The
40
- # reference is the only mandatory parameter. The post_activities and
41
- # pre_activities parameters should contain the references to activities
42
- # already defined within the network and not the actual
43
- # Precedence::Activity object.
44
+ # reference is the only mandatory parameter. This method will take the
45
+ # block given and pass it to the Activity.new method.
44
46
  #
45
47
  # Exapmple usage:
46
- # precNetwork.new_activity('a2',3,'Activity2',['a1'])
48
+ # precNetwork.new_activity('a2') do |activity|
49
+ # activity.duration = 3
50
+ # activity.description = 'Activity2'
51
+ # end
52
+ # precNetwork.connect('a2','a1')
47
53
  # will create a new activity in the network with referecne 'a2' and with
48
- # activity 'a1' as a post-activity.
49
- def new_activity(reference,duration=0,description="",
50
- post_activities=[],pre_activities=[])
51
-
52
- post_activities.each do |post_activity_ref|
53
- unless @activities[post_activity_ref]
54
- raise "Post-activity with reference #{post_activity_ref} "+
55
- "was not found."
56
- end
57
- end
58
-
59
- pre_activities.each do |pre_activity_ref|
60
- unless @activities[pre_activity_ref]
61
- raise "Pre-activity with reference #{pre_activity_ref} "+
62
- "was not found."
63
- end
64
- end
65
-
66
- activity =
67
- Precedence::Activity.new(reference,duration,description)
68
-
69
- post_activities.each do |post_activity_ref|
70
- activity.add_post_activities(@activities[post_activity_ref])
71
- end
72
-
73
- pre_activities.each do |pre_activity_ref|
74
- activity.add_pre_activities(@activities[pre_activity_ref])
75
- end
76
-
77
- @activities[reference]=activity
54
+ # activity 'a1' as a post-activity.
55
+ def new_activity(reference,&block)
56
+ activity = Precedence::Activity.new(reference,&block)
57
+ activities[reference]=activity
78
58
  end
79
59
 
80
60
  # Adds a Precedence::Activity object to the network. This should be a
@@ -86,21 +66,20 @@ module Precedence
86
66
  # precNetwork.add_activity(activity)
87
67
  # will add the existing activity 'a1' to the network.
88
68
  def add_activity(activity)
89
- if @activities[activity.reference]
69
+ if (reference_exists?(activity.reference))
90
70
  raise "Activity #{activity.reference} already exists in the "+
91
71
  "network."
92
72
  end
93
73
 
94
- unless activity.post_activities == []
74
+ unless (activity.post_activities == [])
95
75
  raise "Can not add an activity with post activities."
96
76
  end
97
77
 
98
- unless activity.pre_activities == []
78
+ unless (activity.pre_activities == [])
99
79
  raise "Can not add an activity with pre activities."
100
80
  end
101
81
 
102
- @activities[activity.reference] = activity
103
-
82
+ @activities[activity.reference] = activity
104
83
  end
105
84
 
106
85
  # Connects two or more activities together. The pre_ref activity will
@@ -111,43 +90,208 @@ module Precedence
111
90
  # precNetwork.connect(:h1,:h2,:h3)
112
91
  # will add activity 'h1' as a pre-activity to activities 'h2' and 'h3'.
113
92
  def connect(pre_ref,*post_refs)
114
- unless @activities[pre_ref]
93
+ unless reference_exists?(pre_ref)
115
94
  raise "Pre-activity with reference #{pre_ref} "+
116
95
  "was not found."
117
96
  end
118
97
 
119
98
  post_refs.each do |post_ref|
120
- unless @activities[post_ref]
99
+ unless reference_exists?(post_ref)
121
100
  raise "Post-activity with reference #{post_ref} "+
122
101
  "was not found."
123
102
  end
124
103
  end
125
104
 
126
105
  post_refs.each do |post_ref|
127
- @activities[pre_ref].add_post_activities(@activities[post_ref])
106
+ activities[pre_ref].add_post_activities(activities[post_ref])
128
107
  end
129
108
  end
130
109
 
110
+ # Disconnects an activity from one or more post activities.
111
+ #
112
+ # Example usage:
113
+ # precNetwork.disconnect(:h1,:h2,:h3)
114
+ # will remove activity 'h1' as a pre-activity to activities 'h2' and
115
+ # 'h3'.
116
+ def disconnect(pre_ref,*post_refs)
117
+ unless reference_exists?(pre_ref)
118
+ raise "Pre-activity with reference #{pre_ref} "+
119
+ "was not found."
120
+ end
121
+
122
+ post_refs.each do |post_ref|
123
+ unless reference_exists?(post_ref)
124
+ raise "Post-activity with reference #{post_ref} "+
125
+ "was not found."
126
+ end
127
+ end
128
+
129
+ post_refs.each do |post_ref|
130
+ activities[pre_ref].remove_post_activities(activities[post_ref])
131
+ end
132
+ end
133
+
134
+ # Returns true if the reference is currently in the network. This
135
+ # includes START and FINISH.
136
+ def reference_exists?(reference)
137
+ if ((!activities[reference].nil?) ||
138
+ (reference == START) ||
139
+ (reference == FINISH))
140
+ return true
141
+ end
142
+ return false
143
+ end
144
+
131
145
  # Ensures that the network is properly connected by connecting any
132
146
  # activity without pre-activities to the start node and to the finish
133
- # node if it has no post-activities.
147
+ # node if it has no post-activities. Must be called before any analysis
148
+ # on the network is done.
149
+ #
150
+ # Example usage:
151
+ # precNetwork.connect('h1','h2')
152
+ # precNetwork.connect('h2','h3','h4)
153
+ # precNetwork.fix_connections!
154
+ # precNetwork.finish
155
+ # precNetwork.activities['h2'].earliest_finish
134
156
  def fix_connections!
135
- @activities.each do |ref,activity|
136
- next if (ref == 'start' or ref == 'finish')
157
+ activities.each do |ref,activity|
137
158
  if activity.pre_activities.size == 0
138
- connect(:start,ref)
159
+ connect(START,ref)
139
160
  end
140
161
 
141
162
  if activity.post_activities.size == 0
142
- connect(ref,:finish)
163
+ connect(ref,FINISH)
164
+ end
165
+ end
166
+ end
167
+
168
+ # Returns a dot file capable of being rendered by the Dot graph renderer
169
+ # available from GraphViz (http://www.graphviz.org).
170
+ #
171
+ # Example usage:
172
+ # File.open('test_to_dot.dot',File::CREAT|File::TRUNC|File::WRONLY) do|f|
173
+ # f.puts(net.to_dot)
174
+ # end
175
+ def to_dot(template=nil)
176
+ unless template
177
+ template = Precedence::Network.get_rdot_template
178
+ end
179
+ return ERB.new(template).result(binding)
180
+ end
181
+
182
+ # Returns a YAML representation of the network.For more information on
183
+ # YAML go to http://yaml.org
184
+ #
185
+ # Example usage:
186
+ # File.open('test_to_yaml.yaml',File::CREAT|File::TRUNC|File::WRONLY) do|f|
187
+ # f.puts(net.to_yaml)
188
+ # end
189
+ def to_yaml
190
+ (activities.values.collect do |activity|
191
+ activity.to_yaml
192
+ end).join("\n")
193
+ end
194
+
195
+
196
+ # Returns a Precedence::Network object that is represented by the
197
+ # YAML document.
198
+ #
199
+ # Example usage:
200
+ # precNetwork = nil
201
+ # File.open('precnetwork.yaml',File::RDONLY) do |f|
202
+ # precNetwork = Precedence::Network.from_yaml(f)
203
+ # end
204
+ def self.from_yaml(yaml)
205
+ activities = {}
206
+ connections = {}
207
+
208
+ YAML::load_documents(yaml) do |doc|
209
+ activity = Precedence::Activity.from_yaml_object(doc)
210
+ activities[activity.reference] = activity
211
+ connections[activity.reference] =
212
+ doc[activity.reference]['post activities']
213
+
214
+ end
215
+
216
+ network = Precedence::Network.new()
217
+
218
+ activities.values.each do |activity|
219
+ unless ((activity.reference == START) or
220
+ (activity.reference == FINISH))
221
+ network.add_activity(activity)
143
222
  end
223
+ end
224
+
225
+ connections.each do |ref,connections|
226
+ connections.to_a.each do |post_ref|
227
+ network.connect(ref,post_ref)
228
+ end
144
229
  end
230
+ return network
145
231
  end
146
232
 
233
+ # The time it will take to finish all activities in the network.
234
+ # (Shortcut to precNetwork.activities['finish'].finish)
235
+ #
236
+ # Example usage:
237
+ # precNetwork.finish
238
+ def finish
239
+ return @finish.finish
240
+ end
241
+
242
+ # Iterates over the networks duration yielding an array of activities
243
+ # that are active at that time to a block. The size of the time jumps
244
+ # can be set using the tick parameter. The duration starts at time 0
245
+ # and ends at time network.finish - tick.
246
+ #
247
+ # Example usage:
248
+ # precNetwork.each_time_period do |activities|
249
+ # coffeeUsed == 0
250
+ # activities.each do |activity|
251
+ # coffeeUsed += activity.resources['coffee']
252
+ # end
253
+ # puts "Used #{coffeeUsed} units of coffee in #{activities.size} activities."
254
+ # end
255
+ def each_time_period(tick=1)
256
+ 0.step(finish-tick,tick) do |current_time|
257
+ yield(activities_at_time(current_time))
258
+ end
259
+ end
260
+
261
+ # The same functionality as each_time_period except that the time is
262
+ # yielded along with the array of active activities.
263
+ #
264
+ # Example usage:
265
+ # precNetwork.each_time_period_with_index do |activities,time|
266
+ # puts "At time #{time}, #{activities.size} activities were active."
267
+ # end
268
+ def each_time_period_with_index(tick=1)
269
+ 0.step(finish-tick,tick) do |current_time|
270
+ yield(activities_at_time(current_time),current_time)
271
+ end
272
+ end
273
+
274
+ # Returns an array of activities with each activity being active at
275
+ # the time of the parameter.
276
+ def activities_at_time(time)
277
+ return (@activities.values.find_all do |activity|
278
+ activity.active_on?(time)
279
+ end)
280
+ end
281
+
282
+ # Returns an array of activities with each acitivity being active
283
+ # deuring the time range of the parameter
284
+ def activities_during_time(range)
285
+ return (@activities.values.find_all do |activity|
286
+ activity.active_during?(range)
287
+ end)
288
+ end
289
+
290
+
147
291
  # Returns the rdot (Ruby embedded in a dot file) template that is used
148
292
  # by default when generating the precedence network diagrams.
149
- def get_rdot_template
150
- return <<EOS
293
+ def self.get_rdot_template #:nodoc:
294
+ return <<END_OF_STRING
151
295
  /* Generated by Precedence on <%= Time.now.to_s %> */
152
296
  digraph network {
153
297
  rankdir=LR;
@@ -155,12 +299,12 @@ digraph network {
155
299
 
156
300
  /* Activities */
157
301
  <% @activities.each do |ref,activity| %>
158
- <% case ref == 'start'
159
- when 'start'%>
302
+ <% case ref
303
+ when START%>
160
304
  "<%= ref %>" [label="<%= activity.description %>"];
161
- <% when 'finish' %>
305
+ <% when FINISH %>
162
306
  "<%= ref %>" [label="{<%= activity.description%>|<%= activity.earliest_finish %>}"];
163
- <% else %>
307
+ <% else %>
164
308
  "<%= ref %>" [label="<%=ref%>|{{<%=activity.earliest_start%>|<%=activity.latest_start%>}|{<%=activity.description%>|{<%=activity.total_float%>|<%=activity.early_float%>}}|{<%=activity.earliest_finish%>|<%=activity.latest_finish%>}}|<%=activity.duration%>"];
165
309
  <% end %>
166
310
  <% end %>
@@ -168,20 +312,11 @@ digraph network {
168
312
  /* Dependencies */
169
313
  <% @activities.each do |ref,activity|%>
170
314
  <% activity.post_activities.each do |post_activity| %>
171
- "<%= activity.reference %>" -> "<%= post_activity.reference %>" <% if (activity.on_critical_path? and post_activity.on_critical_path?)%>[style=bold];<% else %>;<% end %>
315
+ "<%= activity.reference %>" -> "<%= post_activity.reference %>" <% if (activity.on_critical_path? and post_activity.on_critical_path?)%>[style=bold]<% end %>;
172
316
  <% end %>
173
317
  <% end %>
174
318
  }
175
- EOS
176
- end
177
-
178
- # Returns a dot file capable of being rendered by the Dot graph renderer
179
- # (available from GraphViz http://www.graphviz.org).
180
- def to_dot(template=nil)
181
- unless template
182
- template = get_rdot_template
183
- end
184
- return ERB.new(template).result(binding)
319
+ END_OF_STRING
185
320
  end
186
321
  end
187
322
  end
@@ -0,0 +1,44 @@
1
+ module Precedence
2
+ module Utilities #:nodoc:
3
+ # A modified Hash that has all keys as strings
4
+ class ActivityHash < Hash #:nodoc:
5
+
6
+ def initialize(startActivity,finishActivity)
7
+ super(nil)
8
+ @start = startActivity
9
+ @finish = finishActivity
10
+ end
11
+
12
+ def[]=(ref,value)
13
+ if ((ref != @start.reference) && (ref != @finish.reference))
14
+ super(ref.to_s, value)
15
+ end
16
+ end
17
+
18
+ def[](ref)
19
+ if (ref.to_s == @start.reference)
20
+ return @start
21
+ elsif (ref.to_s == @finish.reference)
22
+ return @finish
23
+ else
24
+ return super(ref.to_s)
25
+ end
26
+ end
27
+ end
28
+
29
+ # A modified Hash that has all keys as strings and all values as floats
30
+ class ResourceHash < Hash #:nodoc:
31
+ def initialze
32
+ super(0.0)
33
+ end
34
+
35
+ def [](resource)
36
+ return super(resource.to_s)
37
+ end
38
+
39
+ def []=(resource,value)
40
+ return super(resource.to_s,value.to_f)
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/precedence.rb CHANGED
@@ -1,2 +1,3 @@
1
+ require('precedence/utilities')
1
2
  require('precedence/activity')
2
3
  require('precedence/network')