1
2
3
4 """SimulationRT 2.1 Provides synchronization of real time and SimPy simulation time.
5 Implements SimPy Processes, resources, and the backbone simulation scheduling
6 by coroutine calls.
7 Based on generators (Python 2.3 and later; not 3.0)
8
9 LICENSE:
10 Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010 Klaus G. Muller, Tony Vignaux
11 mailto: kgmuller at xs4all.nl and Tony.Vignaux at vuw.ac.nz
12
13 This library is free software; you can redistribute it and / or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 - 1307 USA
26 END OF LICENSE
27 """
28 import time
29
30 from SimPy.Simulation import *
31
32
33 __TESTING = False
34 version = __version__ = '2.1 $Revision: 485 $ $Date: 2010-05-10 09:23:24 +0200 (Mon, 10 May 2010) $'
35 if __TESTING:
36 print 'SimPy.SimulationRT %s' %__version__,
37 if __debug__:
38 print '__debug__ on'
39 else:
40 print
41
44 if sys.platform == 'win32':
45 self.wallclock = time.clock
46 else:
47 self.wallclock = time.time
48 Simulation.__init__(self)
49
51 return self.wallclock() - self.rtstart
52
53 - def rtset(self, rel_speed=1):
54 """
55 Resets the ratio simulation time over clock time(seconds).
56 """
57
58 self.rel_speed = float(rel_speed)
59
60 - def simulate(self, until=0, real_time=False, rel_speed=1):
61 """
62 Simulates until simulation time reaches ``until``. If ``real_time`` is
63 ``True`` a simulation time unit is matched with real time by the factor
64 1 / ``rel_speed``.
65 """
66 try:
67 self.rtstart = self.wallclock()
68 self.rtset(rel_speed)
69
70 while self._timestamps and not self._stop:
71 next_event_time = self.peek()
72 if next_event_time > until: break
73
74 if real_time:
75 delay = (
76 next_event_time / self.rel_speed -
77 (self.wallclock() - self.rtstart)
78 )
79 if delay > 0: time.sleep(delay)
80
81 self.step()
82
83
84
85
86 if not self._stop and self._timestamps:
87 self._t = until
88 return 'SimPy: Normal exit'
89 else:
90 return 'SimPy: No activities scheduled'
91 except Simerror, error:
92 return 'SimPy: ' + error.value
93 finally:
94 self._stop = True
95
96
97
98
99 Globals.sim = SimulationRT()
100
103
104 rtset = Globals.sim.rtset
105
106 -def simulate(until = 0, real_time = False, rel_speed = 1):
107 return Globals.sim.simulate(until = until, real_time = real_time, rel_speed = rel_speed)
108
109 wallclock = Globals.sim.wallclock
110
111
112 if __name__ == '__main__':
113 print 'SimPy.SimulationRT %s' %__version__
114
116 class Aa(Process):
117 sequIn = []
118 sequOut = []
119 def __init__(self, holdtime, name):
120 Process.__init__(self, name)
121 self.holdtime = holdtime
122
123 def life(self, priority):
124 for i in range(1):
125 Aa.sequIn.append(self.name)
126 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
127 len(rrr.activeQ)
128 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
129 print 'activeQ: ',[(k.name, k._priority[rrr]) \
130 for k in rrr.activeQ]
131 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
132 'Inconsistent resource unit numbers'
133 print now(),self.name, 'requests 1 ', rrr.unitName
134 yield request, self, rrr, priority
135 print now(),self.name, 'has 1 ', rrr.unitName
136 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
137 len(rrr.activeQ)
138 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
139 len(rrr.activeQ)
140 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
141 'Inconsistent resource unit numbers'
142 yield hold, self, self.holdtime
143 print now(),self.name, 'gives up 1', rrr.unitName
144 yield release, self, rrr
145 Aa.sequOut.append(self.name)
146 print now(),self.name, 'has released 1 ', rrr.unitName
147 print 'waitQ: ',[(k.name, k._priority[rrr]) for k in rrr.waitQ]
148 print now(),rrr.name, 'waitQ:', len(rrr.waitQ),'activeQ:',\
149 len(rrr.activeQ)
150 assert rrr.n + len(rrr.activeQ) == rrr.capacity, \
151 'Inconsistent resource unit numbers'
152
153 class Observer(Process):
154 def __init__(self):
155 Process.__init__(self)
156
157 def observe(self, step, processes, res):
158 while now() < 11:
159 for i in processes:
160 print ' %s %s: act:%s, pass:%s, term: %s, interr:%s, qu:%s'\
161 %(now(),i.name, i.active(),i.passive(),i.terminated()\
162 ,i.interrupted(),i.queuing(res))
163 print
164 yield hold, self, step
165
166 print'\n+++test_demo output'
167 print '****First case == priority queue, resource service not preemptable'
168 initialize()
169 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
170 preemptable = 0)
171 procs = []
172 for i in range(10):
173 z = Aa(holdtime = i, name = 'Car ' + str(i))
174 procs.append(z)
175 activate(z, z.life(priority = i))
176 o = Observer()
177 activate(o, o.observe(1, procs, rrr))
178 a = simulate(until = 10000, real_time = True, rel_speed = 1)
179 print a
180 print 'Input sequence: ', Aa.sequIn
181 print 'Output sequence: ', Aa.sequOut
182
183 print '\n****Second case == priority queue, resource service preemptable'
184 initialize()
185 rrr = Resource(5, name = 'Parking', unitName = 'space(s)', qType = PriorityQ,
186 preemptable = 1)
187 procs = []
188 for i in range(10):
189 z = Aa(holdtime = i, name = 'Car ' + str(i))
190 procs.append(z)
191 activate(z, z.life(priority = i))
192 o = Observer()
193 activate(o, o.observe(1, procs, rrr))
194 Aa.sequIn = []
195 Aa.sequOut = []
196 a = simulate(until = 10000)
197 print a
198 print 'Input sequence: ', Aa.sequIn
199 print 'Output sequence: ', Aa.sequOut
200
202 class Bus(Process):
203 def __init__(self, name):
204 Process.__init__(self, name)
205
206 def operate(self, repairduration = 0):
207 print now(),rtnow(),'>> %s starts' % (self.name)
208 tripleft = 1000
209 while tripleft > 0:
210 yield hold, self, tripleft
211 if self.interrupted():
212 print 'interrupted by %s' %self.interruptCause.name
213 print '%s(%s): %s breaks down ' %(now(),rtnow(),self.name)
214 tripleft = self.interruptLeft
215 self.interruptReset()
216 print 'tripleft ', tripleft
217 reactivate(br, delay = repairduration)
218 yield hold, self, repairduration
219 print now(),rtnow(),' repaired'
220 else:
221 break
222 print now(),'<< %s done' % (self.name)
223
224 class Breakdown(Process):
225 def __init__(self, myBus):
226 Process.__init__(self, name = 'Breakdown ' + myBus.name)
227 self.bus = myBus
228
229 def breakBus(self, interval):
230
231 while True:
232 yield hold, self, interval
233 if self.bus.terminated(): break
234 self.interrupt(self.bus)
235
236 print'\n\n+++test_interrupt'
237 initialize()
238 b = Bus('Bus 1')
239 activate(b, b.operate(repairduration = 20))
240 br = Breakdown(b)
241 activate(br, br.breakBus(200))
242 print simulate(until = 4000, real_time = True, rel_speed = 200)
243
245 class Waiter(Process):
246 def waiting(self, theSignal):
247 while True:
248 yield waitevent, self, theSignal
249 print '%s: process \'%s\' continued after waiting for %s' % (now(),self.name, theSignal.name)
250 yield queueevent, self, theSignal
251 print '%s: process \'%s\' continued after queueing for %s' % (now(),self.name, theSignal.name)
252
253 class ORWaiter(Process):
254 def waiting(self, signals):
255 while True:
256 yield waitevent, self, signals
257 print now(),'one of %s signals occurred' % [x.name for x in signals]
258 print '\t%s (fired / param)'%[(x.name, x.signalparam) for x in self.eventsFired]
259 yield hold, self, 1
260
261 class Caller(Process):
262 def calling(self):
263 while True:
264 signal1.signal('wake up!')
265 print '%s: signal 1 has occurred'%now()
266 yield hold, self, 10
267 signal2.signal('and again')
268 signal2.signal('sig 2 again')
269 print '%s: signal1, signal2 have occurred'%now()
270 yield hold, self, 10
271 print'\n\n+++testSimEvents output'
272 initialize()
273 signal1 = SimEvent('signal 1')
274 signal2 = SimEvent('signal 2')
275 signal1.signal('startup1')
276 signal2.signal('startup2')
277 w1 = Waiter('waiting for signal 1')
278 activate(w1, w1.waiting(signal1))
279 w2 = Waiter('waiting for signal 2')
280 activate(w2, w2.waiting(signal2))
281 w3 = Waiter('also waiting for signal 2')
282 activate(w3, w3.waiting(signal2))
283 w4 = ORWaiter('waiting for either signal 1 or signal 2')
284 activate(w4, w4.waiting([signal1, signal2]),prior = True)
285 c = Caller('Caller')
286 activate(c, c.calling())
287 print simulate(until = 100)
288
290 """
291 Demo of waitUntil capability.
292
293 Scenario:
294 Three workers require sets of tools to do their jobs. Tools are shared, scarce
295 resources for which they compete.
296 """
297
298
299 class Worker(Process):
300 def __init__(self, name, heNeeds = []):
301 Process.__init__(self, name)
302 self.heNeeds = heNeeds
303 def work(self):
304
305 def workerNeeds():
306 for item in self.heNeeds:
307 if item.n == 0:
308 return False
309 return True
310
311 while now() < 8 * 60:
312 yield waituntil, self, workerNeeds
313 for item in self.heNeeds:
314 yield request, self, item
315 print '%s %s has %s and starts job' % (now(),self.name,
316 [x.name for x in self.heNeeds])
317 yield hold, self, random.uniform(10, 30)
318 for item in self.heNeeds:
319 yield release, self, item
320 yield hold, self, 2
321
322 print '\n+++\nwaituntil demo output'
323 initialize()
324 brush = Resource(capacity = 1, name = 'brush')
325 ladder = Resource(capacity = 2, name = 'ladder')
326 hammer = Resource(capacity = 1, name = 'hammer')
327 saw = Resource(capacity = 1, name = 'saw')
328 painter = Worker('painter',[brush, ladder])
329 activate(painter, painter.work())
330 roofer = Worker('roofer',[hammer, ladder, ladder])
331 activate(roofer, roofer.work())
332 treeguy = Worker('treeguy',[saw, ladder])
333 activate(treeguy, treeguy.work())
334 for who in (painter, roofer, treeguy):
335 print '%s needs %s for his job' % (who.name,[x.name for x in who.heNeeds])
336 print
337 print simulate(until = 9 * 60)
338 test_demo()
339
340 test_interrupt()
341
342
343