1# 2# The contents of this file are subject to the Mozilla Public 3# License Version 1.1 (the "License"); you may not use this file 4# except in compliance with the License. You may obtain a copy of 5# the License at http://www.mozilla.org/MPL/ 6# 7# Software distributed under the License is distributed on an "AS 8# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9# implied. See the License for the specific language governing 10# rights and limitations under the License. 11# 12# The Original Code is State Machine Compiler (SMC). 13# 14# The Initial Developer of the Original Code is Charles W. Rapp. 15# Portions created by Charles W. Rapp are 16# Copyright (C) 2000 - 2003 Charles W. Rapp. 17# All Rights Reserved. 18# 19# Contributor(s): 20# Port to Python by Francois Perrad, francois.perrad@gadz.org 21# 22# Vehicle -- 23# 24# Draws a generic vehicle on the map (a black square) which 25# moves in straight lines along the road and obeys the stop light. 26# 27# RCS ID 28# $Id: Vehicle.py,v 1.1 2005/05/28 17:48:30 cwrapp Exp $ 29# 30# CHANGE LOG 31# $Log: Vehicle.py,v $ 32# Revision 1.1 2005/05/28 17:48:30 cwrapp 33# Added Python examples 1 - 4 and 7. 34# 35# 36 37from Tkinter import * 38 39import Vehicle_sm 40 41class Vehicle: 42 43 _speed = 2 44 45 def __init__(self, stoplight, direction, canvas): 46 self._fsm = Vehicle_sm.Vehicle_sm(self) 47 48 # The canvas to draw on and the direction this vehicle is 49 # moving. 50 self._canvas = canvas 51 self._direction = direction 52 53 # The stoplight object is responsible knowing the road 54 # layout. Ask it for all relevant information. 55 self._stoplight = stoplight 56 57 # This vehicle is initially at the road's outside edge. 58 # Figure out the road's length. 59 XLength = stoplight.getRoadLengthX() 60 YLength = stoplight.getRoadLengthY() 61 LaneWidth = stoplight.getRoadWidth() / 2 62 63 # The vehicle is 12 pixels x 12 pixels. 64 self._vehicleSize = 6 65 66 # A 3 pixel separation is to be maintained between vehicles. 67 self._vehicleSeparation = 3 68 69 # How far away the vehicle is from the curb. 70 CurbOffset = (LaneWidth - self._vehicleSize) / 2 71 72 # The vehicle's current canvas location. This is the 73 # square's upper left hand corner. 74 if direction == 'north': 75 self._xpos = (XLength / 2) + CurbOffset 76 self._ypos = YLength - self._vehicleSize 77 elif direction == 'south': 78 self._xpos = (XLength / 2) - LaneWidth + CurbOffset 79 self._ypos = 0 80 elif direction == 'east': 81 self._xpos = 0 82 self._ypos = (YLength / 2) + CurbOffset 83 elif direction == 'west': 84 self._xpos = XLength - self._vehicleSize 85 self._ypos = (YLength / 2) - LaneWidth + CurbOffset 86 87 # Put the vehicle on display. 88 self._canvasID = canvas.create_rectangle( 89 self._xpos, 90 self._ypos, 91 self._xpos + self._vehicleSize, 92 self._ypos + self._vehicleSize, 93 fill='black', 94 outline='white', 95 ) 96 97 # Move this vehicle along at near movie-refresh rate. 98 self._redrawRate = 1000 / 60 99 100 # Store the after's timer ID here. 101 self._timerID = -1 102 103 # Set this flag to true when the vehicle has 104 # completed its trip. 105 self._isDoneFlag = False 106 107 # Uncomment to see debug output. 108 #self._fsm.setDebugFlag(True) 109 110 def Delete(self): 111 if self._timerID >= 0: 112 self._canvas.after_cancel(self._timerID) 113 self._timerID = -1 114 self._canvas.delete(self._canvasID) 115 116 # timeout -- 117 # 118 # If the vehicle has driven off the canvas, then 119 # delete the vehicle. 120 # Check if the vehicle is at the intersection and the 121 # light is either yellow or red. If yes, then issue a 122 # "LightRed" transition. If all is go, then keep on 123 # truckin. 124 # 125 # Arugments: 126 # None. 127 128 def timeout(self): 129 self._timerID = -1 130 if self.OffCanvas(): 131 self._fsm.TripDone() 132 elif self.AtIntersection() and self.getLight() != 'green': 133 self._fsm.LightRed() 134 else: 135 self._fsm.KeepGoing() 136 137 def getLight(self): 138 return self._stoplight.getLight(self._direction) 139 140 # lightGreen -- 141 # 142 # The light has turned green. Time to get moving again. 143 # 144 # Arguments: 145 # None 146 147 def lightGreen(self): 148 self._fsm.LightGreen() 149 150 # setSpeed -- 151 # 152 # Set speed for all vehicles. 153 # 154 # Arguments: 155 # speed In pixels. 156 157 def setSpeed(klass, speed): 158 if speed < 1 or speed > 10: 159 print "Invalid speed (%d).\n" % speed 160 else: 161 klass._speed = speed 162 163 setSpeed = classmethod(setSpeed) 164 165 # isDone -- 166 # 167 # Has this vehicle completed its trip? 168 # 169 # Arguments: 170 # None. 171 # 172 # Results: 173 # Returns true if the trip is done and false 174 # otherwise. 175 176 def isDone(self): 177 return self._isDoneFlag 178 179 # start -- 180 # 181 # Start this vehicle running. 182 # 183 # Arguments: 184 # None. 185 186 def Start(self): 187 self._fsm.Start() 188 189 # pause -- 190 # 191 # Pause this vehicles' running. 192 # 193 # Arguments: 194 # None. 195 196 def Pause(self): 197 self._fsm.Pause() 198 199 # continue -- 200 # 201 # Continue this vehicles' running. 202 # 203 # Arguments: 204 # None. 205 206 def Continue(self): 207 self._fsm.Continue() 208 209 # stop -- 210 # 211 # Stop this vehicles' running. 212 # 213 # Arguments: 214 # None. 215 # 216 217 def Stop(self): 218 self._fsm.Stop() 219 self.Delete() 220 221 # State Machine Actions 222 # 223 # The following methods are called by the state machine. 224 225 # SetTimer -- 226 # 227 # Set the timer for the next move. 228 # 229 # Arguments: 230 # None. 231 232 def SetTimer(self): 233 self._timerID = self._canvas.after(self._redrawRate, self.timeout) 234 235 # StopTimer -- 236 # 237 # Stop the vehicle's timer. 238 # 239 # Arguments: 240 # None. 241 242 def StopTimer(self): 243 if self._timerID >= 0: 244 self._canvas.after_cancel(self._timerID) 245 self._timerID = -1 246 247 # Move -- 248 # 249 # 1. Calculate the vehicle's new position. 250 # 2. Remove the vehicle from the canvas. 251 # 3. Draw the vehicles new position. 252 # 253 # Arguments: 254 # None. 255 # 256 # Results: 257 # None returned. Side affect of redrawing vehicle. 258 259 def Move(self): 260 if self._direction == 'north': 261 Xmove = 0 262 Ymove = - self._speed 263 elif self._direction == 'south': 264 Xmove = 0 265 Ymove = self._speed 266 elif self._direction == 'east': 267 Xmove = self._speed 268 Ymove = 0 269 elif self._direction == 'west': 270 Xmove = - self._speed 271 Ymove = 0 272 273 self._canvas.move(self._canvasID, Xmove, Ymove) 274 275 self._xpos += Xmove 276 self._ypos += Ymove 277 278 # RegisterWithLight -- 279 # 280 # When the light turns green, it will inform us. 281 # 282 # Arguments: 283 # None. 284 285 def RegisterWithLight(self): 286 self._stoplight.registerVehicle(self, self._direction) 287 288 # SelfDestruct -- 289 # 290 # Remove the vehicle from the canvas. 291 # 292 # Arguments: 293 # None. 294 295 def SelfDestruct(self): 296 self._canvas.delete(self._canvasID) 297 self._canvasID = -1 298 self._isDoneFlag = True 299 300 # OffCanvas -- 301 # 302 # Figure out if the vehicle has driven off the map. 303 # 304 # Arguments: 305 # None. 306 # 307 # Results: 308 # Returns true if the vehicle is off the map; otherwise 309 # false. 310 311 def OffCanvas(self): 312 if self._direction == 'north': 313 return (self._ypos - self._speed) <= 0 314 elif self._direction == 'south': 315 YLength = self._stoplight.getRoadLengthY() 316 return (self._ypos + self._speed) >= YLength 317 elif self._direction == 'east': 318 XLength = self._stoplight.getRoadLengthX() 319 return (self._xpos + self._speed) >= XLength 320 elif self._direction == 'west': 321 return (self._xpos - self._speed) <= 0 322 323 # AtIntersection -- 324 # 325 # Figure out whether this vehicile is at the intersection 326 # or not. 327 # 328 # Arguments: 329 # None. 330 # 331 # Results: 332 # Returns true if the vehicle is at the intersection; 333 # otherwise, false. 334 335 def AtIntersection(self): 336 # The vehicle is not at the intersection until proven 337 # otherwise. 338 Retval = False 339 340 XLength = self._stoplight.getRoadLengthX() 341 YLength = self._stoplight.getRoadLengthY() 342 LaneWidth = self._stoplight.getRoadWidth() / 2 343 344 # Calculate the intersections coordinates based on 345 # the vehicle's direction. Then calculate where the 346 # vehicle will end up this move. If the vehicle will 347 # move beyond the intersection stop line, then the 348 # vehicle is at the intersection. 349 # 350 # Also take into account the vehicles already waiting 351 # at the intersection. 352 # 353 # By the way, once the vehicle moves past the intersection, 354 # ignore the light. 355 NumVehicles = self._stoplight.getQueueSize(self._direction) 356 LenVehicles = (self._vehicleSize + self._vehicleSeparation) * NumVehicles 357 if self._direction == 'north': 358 YIntersection = (YLength / 2) + LaneWidth + (self._vehicleSize / 2) + LenVehicles 359 Retval = (self._ypos > YIntersection) and (self._ypos - self._speed <= YIntersection) 360 elif self._direction == 'south': 361 YIntersection = (YLength / 2) - LaneWidth - (self._vehicleSize / 2) - LenVehicles 362 Retval = (self._ypos < YIntersection) and (self._ypos + self._speed >= YIntersection) 363 elif self._direction == 'east': 364 XIntersection = (XLength / 2) - LaneWidth - (self._vehicleSize / 2) - LenVehicles 365 Retval = (self._xpos < XIntersection) and (self._xpos + self._speed >= XIntersection) 366 elif self._direction == 'west': 367 XIntersection = (XLength / 2) + LaneWidth + (self._vehicleSize / 2) + LenVehicles 368 Retval = (self._xpos > XIntersection) and (self._xpos - self._speed <= XIntersection) 369 return Retval 370