model.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. # Copyright 2014 Modelling, Simulation and Design Lab (MSDL) at
  2. # McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import sys
  16. # Import code for DEVS model representation:
  17. from DEVS import *
  18. from infinity import INFINITY
  19. class TrafficLightMode:
  20. """
  21. Encapsulates the system's state
  22. """
  23. def __init__(self, current="red"):
  24. """
  25. Constructor (parameterizable).
  26. """
  27. self.set(current)
  28. def set(self, value="red"):
  29. self.colour=value
  30. def get(self):
  31. return self.colour
  32. def __str__(self):
  33. return self.get()
  34. class TrafficLight(AtomicDEVS):
  35. """
  36. A traffic light
  37. """
  38. def __init__(self, name=None):
  39. """
  40. Constructor (parameterizable).
  41. """
  42. # Always call parent class' constructor FIRST:
  43. AtomicDEVS.__init__(self, name)
  44. # STATE:
  45. # Define 'state' attribute (initial sate):
  46. self.state = TrafficLightMode("red")
  47. # PORTS:
  48. # Declare as many input and output ports as desired
  49. # (usually store returned references in local variables):
  50. self.INTERRUPT = self.addInPort(name="INTERRUPT")
  51. self.OBSERVED = self.addOutPort(name="OBSERVED")
  52. def extTransition(self, inputs):
  53. """
  54. External Transition Function.
  55. """
  56. # Compute the new state 'Snew' based (typically) on current
  57. # State, Elapsed time parameters and calls to 'self.peek(self.IN)'.
  58. input = inputs.get(self.INTERRUPT)[0]
  59. state = self.state.get()
  60. if input == "toManual":
  61. if state == "manual":
  62. # staying in manual mode
  63. return TrafficLightMode("manual")
  64. elif state in ("red", "green", "yellow"):
  65. return TrafficLightMode("manual")
  66. elif input == "toAutonomous":
  67. if state == "manual":
  68. return TrafficLightMode("red")
  69. elif state in ("red", "green", "yellow"):
  70. # If toAutonomous is given while still autonomous, just stay in this state
  71. return self.state
  72. raise DEVSException(\
  73. "unknown state <%s> in TrafficLight external transition function"\
  74. % state)
  75. def intTransition(self):
  76. """
  77. Internal Transition Function.
  78. """
  79. state = self.state.get()
  80. if state == "red":
  81. return TrafficLightMode("green")
  82. elif state == "green":
  83. return TrafficLightMode("yellow")
  84. elif state == "yellow":
  85. return TrafficLightMode("red")
  86. else:
  87. raise DEVSException(\
  88. "unknown state <%s> in TrafficLight internal transition function"\
  89. % state)
  90. def outputFnc(self):
  91. """
  92. Output Funtion.
  93. """
  94. # A colourblind observer sees "grey" instead of "red" or "green".
  95. # BEWARE: ouput is based on the OLD state
  96. # and is produced BEFORE making the transition.
  97. # We'll encode an "observation" of the state the
  98. # system will transition to !
  99. # Send messages (events) to a subset of the atomic-DEVS'
  100. # output ports by means of the 'poke' method, i.e.:
  101. # The content of the messages is based (typically) on current State.
  102. state = self.state.get()
  103. if state == "red":
  104. return {self.OBSERVED: ["grey"]}
  105. elif state == "green":
  106. return {self.OBSERVED: ["yellow"]}
  107. elif state == "yellow":
  108. return {self.OBSERVED: ["grey"]}
  109. else:
  110. raise DEVSException(\
  111. "unknown state <%s> in TrafficLight external transition function"\
  112. % state)
  113. def timeAdvance(self):
  114. """
  115. Time-Advance Function.
  116. """
  117. # Compute 'ta', the time to the next scheduled internal transition,
  118. # based (typically) on current State.
  119. state = self.state.get()
  120. if state == "red":
  121. return 60
  122. elif state == "green":
  123. return 50
  124. elif state == "yellow":
  125. return 10
  126. elif state == "manual":
  127. return INFINITY
  128. else:
  129. raise DEVSException(\
  130. "unknown state <%s> in TrafficLight time advance transition function"\
  131. % state)
  132. class PolicemanMode:
  133. """
  134. Encapsulates the Policeman's state
  135. """
  136. def __init__(self, current="idle"):
  137. """
  138. Constructor (parameterizable).
  139. """
  140. self.set(current)
  141. def set(self, value="idle"):
  142. self.__mode=value
  143. def get(self):
  144. return self.__mode
  145. def __str__(self):
  146. return self.get()
  147. class Policeman(AtomicDEVS):
  148. """
  149. A policeman producing "toManual" and "toAutonomous" events:
  150. "toManual" when going from "idle" to "working" mode
  151. "toAutonomous" when going from "working" to "idle" mode
  152. """
  153. def __init__(self, name=None):
  154. """
  155. Constructor (parameterizable).
  156. """
  157. # Always call parent class' constructor FIRST:
  158. AtomicDEVS.__init__(self, name)
  159. # STATE:
  160. # Define 'state' attribute (initial sate):
  161. self.state = PolicemanMode("idle")
  162. # ELAPSED TIME:
  163. # Initialize 'elapsed time' attribute if required
  164. # (by default, value is 0.0):
  165. self.elapsed = 0
  166. # PORTS:
  167. # Declare as many input and output ports as desired
  168. # (usually store returned references in local variables):
  169. self.OUT = self.addOutPort(name="OUT")
  170. def intTransition(self):
  171. """
  172. Internal Transition Function.
  173. The policeman works forever, so only one mode.
  174. """
  175. state = self.state.get()
  176. if state == "idle":
  177. return PolicemanMode("working")
  178. elif state == "working":
  179. return PolicemanMode("idle")
  180. else:
  181. raise DEVSException(\
  182. "unknown state <%s> in Policeman internal transition function"\
  183. % state)
  184. def outputFnc(self):
  185. """
  186. Output Funtion.
  187. """
  188. # Send messages (events) to a subset of the atomic-DEVS'
  189. # output ports by means of the 'poke' method, i.e.:
  190. # The content of the messages is based (typically) on current State.
  191. state = self.state.get()
  192. if state == "idle":
  193. return {self.OUT: ["toManual"]}
  194. elif state == "working":
  195. return {self.OUT: ["toAutonomous"]}
  196. else:
  197. raise DEVSException(\
  198. "unknown state <%s> in Policeman output function"\
  199. % state)
  200. def timeAdvance(self):
  201. """
  202. Time-Advance Function.
  203. """
  204. # Compute 'ta', the time to the next scheduled internal transition,
  205. # based (typically) on current State.
  206. state = self.state.get()
  207. if state == "idle":
  208. return 200
  209. elif state == "working":
  210. return 100
  211. else:
  212. raise DEVSException(\
  213. "unknown state <%s> in Policeman time advance function"\
  214. % state)
  215. class Root(CoupledDEVS):
  216. def __init__(self, name="TrafficLight"):
  217. """
  218. A simple traffic system consisting of a Policeman and a TrafficLight.
  219. """
  220. # Always call parent class' constructor FIRST:
  221. CoupledDEVS.__init__(self, name)
  222. # Declare the coupled model's output ports:
  223. # Autonomous, so no output ports
  224. # Declare the coupled model's sub-models:
  225. # The Policeman generating interrupts
  226. self.policeman = self.addSubModel(Policeman(name="policeman"))
  227. # The TrafficLight
  228. self.trafficLight = self.addSubModel(TrafficLight(name="trafficLight"))
  229. # Only connect ...
  230. self.connectPorts(self.policeman.OUT, self.trafficLight.INTERRUPT)