statecharts_core.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  1. """
  2. The classes and functions needed to run (compiled) SCCD models.
  3. """
  4. import abc
  5. import re
  6. import threading
  7. import sys
  8. try:
  9. import thread
  10. except ImportError:
  11. import threading as thread
  12. import traceback
  13. import math
  14. from heapq import heappush, heappop, heapify
  15. try:
  16. from sccd.runtime.infinity import INFINITY
  17. except ImportError:
  18. from infinity import INFINITY
  19. try:
  20. from queue import Queue, Empty
  21. except ImportError:
  22. from Queue import Queue, Empty
  23. from sccd.runtime.event_queue import EventQueue
  24. from sccd.runtime.accurate_time import AccurateTime
  25. from time import time
  26. DEBUG = False
  27. ELSE_GUARD = "ELSE_GUARD"
  28. def print_debug(msg):
  29. if DEBUG:
  30. print(msg)
  31. class RuntimeException(Exception):
  32. """
  33. Base class for runtime exceptions.
  34. """
  35. def __init__(self, message):
  36. self.message = message
  37. def __str__(self):
  38. return repr(self.message)
  39. class AssociationException(RuntimeException):
  40. """
  41. Exception class thrown when an error occurs in a CRUD operation on associations.
  42. """
  43. pass
  44. class AssociationReferenceException(RuntimeException):
  45. """
  46. Exception class thrown when an error occurs when resolving an association reference.
  47. """
  48. pass
  49. class ParameterException(RuntimeException):
  50. """
  51. Exception class thrown when an error occurs when passing parameters.
  52. """
  53. pass
  54. class InputException(RuntimeException):
  55. """
  56. Exception class thrown when an error occurs during input processing.
  57. """
  58. pass
  59. class Association(object):
  60. """
  61. Class representing an SCCD association.
  62. """
  63. def __init__(self, to_class, min_card, max_card):
  64. """
  65. Constructor
  66. :param to_class: the name of the target class
  67. :param min_card: the minimal cardinality
  68. :param max_card: the maximal cardinality
  69. """
  70. self.to_class = to_class
  71. self.min_card = min_card
  72. self.max_card = max_card
  73. self.instances = {} # maps index (as string) to instance
  74. self.instances_to_ids = {}
  75. self.size = 0
  76. self.next_id = 0
  77. def allowedToAdd(self):
  78. return self.max_card == -1 or self.size < self.max_card
  79. def allowedToRemove(self):
  80. return self.min_card == -1 or self.size > self.min_card
  81. def addInstance(self, instance):
  82. if self.allowedToAdd():
  83. new_id = self.next_id
  84. self.next_id += 1
  85. self.instances[new_id] = instance
  86. self.instances_to_ids[instance] = new_id
  87. self.size += 1
  88. return new_id
  89. else:
  90. raise AssociationException("Not allowed to add the instance to the association.")
  91. def removeInstance(self, instance):
  92. if self.allowedToRemove():
  93. index = self.instances_to_ids[instance]
  94. del self.instances[index]
  95. del self.instances_to_ids[instance]
  96. self.size -= 1
  97. return index
  98. else:
  99. raise AssociationException("Not allowed to remove the instance from the association.")
  100. def getInstance(self, index):
  101. try:
  102. return self.instances[index]
  103. except IndexError:
  104. raise AssociationException("Invalid index for fetching instance(s) from association.")
  105. # TODO: Clean this mess up. Look at all object management operations and see how they can be improved.
  106. class ObjectManagerBase(object):
  107. __metaclass__ = abc.ABCMeta
  108. def __init__(self, controller):
  109. self.controller = controller
  110. self.events = EventQueue()
  111. self.instances = set() # a set of RuntimeClassBase instances
  112. self.instance_times = []
  113. self.eventless = set()
  114. self.regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$")
  115. self.handlers = {"narrow_cast": self.handleNarrowCastEvent,
  116. "broad_cast": self.handleBroadCastEvent,
  117. "create_instance": self.handleCreateEvent,
  118. "associate_instance": self.handleAssociateEvent,
  119. "disassociate_instance": self.handleDisassociateEvent,
  120. "start_instance": self.handleStartInstanceEvent,
  121. "delete_instance": self.handleDeleteInstanceEvent,
  122. "create_and_start_instance": self.handleCreateAndStartEvent}
  123. self.lock = threading.Condition()
  124. def addEvent(self, event, time_offset = 0):
  125. self.events.add(self.controller.simulated_time + time_offset, event)
  126. # broadcast an event to all instances
  127. def broadcast(self, source, new_event, time_offset = 0):
  128. for i in self.instances:
  129. if i != source:
  130. i.addEvent(new_event, time_offset)
  131. def getEarliestEventTime(self):
  132. with self.lock:
  133. while self.instance_times and self.instance_times[0][0] < self.instance_times[0][1].earliest_event_time:
  134. heappop(self.instance_times)
  135. return min(INFINITY if not self.instance_times else self.instance_times[0][0], self.events.getEarliestTime())
  136. def stepAll(self):
  137. self.step()
  138. simulated_time = self.controller.simulated_time
  139. to_step = set()
  140. if len(self.instance_times) > (4 * len(self.instances)):
  141. new_instance_times = []
  142. for it in self.instances:
  143. if it.earliest_event_time != INFINITY:
  144. new_instance_times.append((it.earliest_event_time, it))
  145. self.instance_times = new_instance_times
  146. heapify(self.instance_times)
  147. while self.instance_times and self.instance_times[0][0] <= simulated_time:
  148. to_step.add(heappop(self.instance_times)[1])
  149. for i in to_step | self.eventless:
  150. if i.active and (i.earliest_event_time <= simulated_time or i.eventless_states):
  151. i.step()
  152. def step(self):
  153. while self.events.getEarliestTime() <= self.controller.simulated_time:
  154. if self.events:
  155. self.handleEvent(self.events.pop())
  156. def start(self):
  157. for i in self.instances:
  158. i.start()
  159. def handleEvent(self, e):
  160. self.handlers[e.getName()](e.getParameters())
  161. def processAssociationReference(self, input_string):
  162. if len(input_string) == 0:
  163. raise AssociationReferenceException("Empty association reference.")
  164. path_string = input_string.split("/")
  165. result = []
  166. for piece in path_string:
  167. match = self.regex_pattern.match(piece)
  168. if match:
  169. name = match.group(1)
  170. index = match.group(2)
  171. if index is None:
  172. index = -1
  173. result.append((name,int(index)))
  174. else:
  175. raise AssociationReferenceException("Invalid entry in association reference. Input string: " + input_string)
  176. return result
  177. def handleStartInstanceEvent(self, parameters):
  178. if len(parameters) != 2:
  179. raise ParameterException ("The start instance event needs 2 parameters.")
  180. else:
  181. source = parameters[0]
  182. traversal_list = self.processAssociationReference(parameters[1])
  183. for i in self.getInstances(source, traversal_list):
  184. i["instance"].start()
  185. source.addEvent(Event("instance_started", parameters = [parameters[1]]))
  186. def handleBroadCastEvent(self, parameters):
  187. if len(parameters) != 2:
  188. raise ParameterException ("The broadcast event needs 2 parameters (source of event and event name).")
  189. self.broadcast(parameters[0], parameters[1])
  190. def handleCreateEvent(self, parameters):
  191. if len(parameters) < 2:
  192. raise ParameterException ("The create event needs at least 2 parameters.")
  193. source = parameters[0]
  194. association_name = parameters[1]
  195. traversal_list = self.processAssociationReference(association_name)
  196. instances = self.getInstances(source, traversal_list)
  197. association = source.associations[association_name]
  198. # association = self.instances_map[source].getAssociation(association_name)
  199. if association.allowedToAdd():
  200. ''' allow subclasses to be instantiated '''
  201. class_name = association.to_class if len(parameters) == 2 else parameters[2]
  202. new_instance = self.createInstance(class_name, parameters[3:])
  203. if not new_instance:
  204. raise ParameterException("Creating instance: no such class: " + class_name)
  205. # index = association.addInstance(new_instance)
  206. try:
  207. index = association.addInstance(new_instance)
  208. except AssociationException as exception:
  209. raise RuntimeException("Error adding instance to association '" + association_name + "': " + str(exception))
  210. p = new_instance.associations.get("parent")
  211. if p:
  212. p.addInstance(source)
  213. source.addEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"]))
  214. return [source, association_name+"["+str(index)+"]"]
  215. else:
  216. source.addEvent(Event("instance_creation_error", None, [association_name]))
  217. return []
  218. def handleCreateAndStartEvent(self, parameters):
  219. params = self.handleCreateEvent(parameters)
  220. if params:
  221. self.handleStartInstanceEvent(params)
  222. def handleDeleteInstanceEvent(self, parameters):
  223. if len(parameters) < 2:
  224. raise ParameterException ("The delete event needs at least 2 parameters.")
  225. else:
  226. source = parameters[0]
  227. association_name = parameters[1]
  228. traversal_list = self.processAssociationReference(association_name)
  229. instances = self.getInstances(source, traversal_list)
  230. # association = self.instances_map[source].getAssociation(traversal_list[0][0])
  231. association = source.associations[traversal_list[0][0]]
  232. for i in instances:
  233. try:
  234. for assoc_name in i["instance"].associations:
  235. if assoc_name != 'parent':
  236. traversal_list = self.processAssociationReference(assoc_name)
  237. instances = self.getInstances(i["instance"], traversal_list)
  238. if len(instances) > 0:
  239. raise RuntimeException("Error removing instance from association %s, still %i children left connected with association %s" % (association_name, len(instances), assoc_name))
  240. del i["instance"].controller.input_ports[i["instance"].narrow_cast_port]
  241. association.removeInstance(i["instance"])
  242. self.instances.discard(i["instance"])
  243. self.eventless.discard(i["instance"])
  244. except AssociationException as exception:
  245. raise RuntimeException("Error removing instance from association '" + association_name + "': " + str(exception))
  246. i["instance"].user_defined_destructor()
  247. i["instance"].stop()
  248. source.addEvent(Event("instance_deleted", parameters = [parameters[1]]))
  249. def handleAssociateEvent(self, parameters):
  250. if len(parameters) != 3:
  251. raise ParameterException ("The associate_instance event needs 3 parameters.")
  252. else:
  253. source = parameters[0]
  254. to_copy_list = self.getInstances(source, self.processAssociationReference(parameters[1]))
  255. if len(to_copy_list) != 1:
  256. raise AssociationReferenceException ("Invalid source association reference.")
  257. wrapped_to_copy_instance = to_copy_list[0]["instance"]
  258. dest_list = self.processAssociationReference(parameters[2])
  259. if len(dest_list) == 0:
  260. raise AssociationReferenceException ("Invalid destination association reference.")
  261. last = dest_list.pop()
  262. if last[1] != -1:
  263. raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.")
  264. added_links = []
  265. for i in self.getInstances(source, dest_list):
  266. association = i["instance"].associations[last[0]]
  267. if association.allowedToAdd():
  268. index = association.addInstance(wrapped_to_copy_instance)
  269. added_links.append(i["path"] + ("" if i["path"] == "" else "/") + last[0] + "[" + str(index) + "]")
  270. source.addEvent(Event("instance_associated", parameters = [added_links]))
  271. def handleDisassociateEvent(self, parameters):
  272. if len(parameters) < 2:
  273. raise ParameterException ("The disassociate_instance event needs at least 2 parameters.")
  274. else:
  275. source = parameters[0]
  276. association_name = parameters[1]
  277. if not isinstance(association_name, list):
  278. association_name = [association_name]
  279. deleted_links = []
  280. for a_n in association_name:
  281. traversal_list = self.processAssociationReference(a_n)
  282. instances = self.getInstances(source, traversal_list)
  283. for i in instances:
  284. try:
  285. index = i['ref'].associations[i['assoc_name']].removeInstance(i["instance"])
  286. deleted_links.append(a_n + "[" + str(index) + "]")
  287. except AssociationException as exception:
  288. raise RuntimeException("Error disassociating '" + a_n + "': " + str(exception))
  289. source.addEvent(Event("instance_disassociated", parameters = [deleted_links]))
  290. def handleNarrowCastEvent(self, parameters):
  291. if len(parameters) != 3:
  292. raise ParameterException ("The narrow_cast event needs 3 parameters.")
  293. source = parameters[0]
  294. if not isinstance(parameters[1], list):
  295. targets = [parameters[1]]
  296. else:
  297. targets = parameters[1]
  298. for target in targets:
  299. traversal_list = self.processAssociationReference(target)
  300. cast_event = parameters[2]
  301. for i in self.getInstances(source, traversal_list):
  302. to_send_event = Event(cast_event.name, i["instance"].narrow_cast_port, cast_event.parameters)
  303. i["instance"].controller.addInput(to_send_event, force_internal=True)
  304. def getInstances(self, source, traversal_list):
  305. currents = [{
  306. "instance": source,
  307. "ref": None,
  308. "assoc_name": None,
  309. "assoc_index": None,
  310. "path": ""
  311. }]
  312. # currents = [source]
  313. for (name, index) in traversal_list:
  314. nexts = []
  315. for current in currents:
  316. association = current["instance"].associations[name]
  317. if (index >= 0 ):
  318. try:
  319. nexts.append({
  320. "instance": association.instances[index],
  321. "ref": current["instance"],
  322. "assoc_name": name,
  323. "assoc_index": index,
  324. "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
  325. })
  326. except KeyError:
  327. # Entry was removed, so ignore this request
  328. pass
  329. elif (index == -1):
  330. for i in association.instances:
  331. nexts.append({
  332. "instance": association.instances[i],
  333. "ref": current["instance"],
  334. "assoc_name": name,
  335. "assoc_index": index,
  336. "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
  337. })
  338. #nexts.extend( association.instances.values() )
  339. else:
  340. raise AssociationReferenceException("Incorrect index in association reference.")
  341. currents = nexts
  342. return currents
  343. @abc.abstractmethod
  344. def instantiate(self, class_name, construct_params):
  345. pass
  346. def createInstance(self, to_class, construct_params = []):
  347. instance = self.instantiate(to_class, construct_params)
  348. self.instances.add(instance)
  349. return instance
  350. class Event(object):
  351. def __init__(self, event_name, port = "", parameters = []):
  352. self.name = event_name
  353. self.parameters = parameters
  354. self.port = port
  355. #for comparisons in heaps
  356. def __lt__(self, other):
  357. s = str(self.name) + str(self.parameters) + str(self.port)
  358. return len(s)
  359. def getName(self):
  360. return self.name
  361. def getPort(self):
  362. return self.port
  363. def getParameters(self):
  364. return self.parameters
  365. def __repr__(self):
  366. representation = "(event name: " + str(self.name) + "; port: " + str(self.port)
  367. if self.parameters:
  368. representation += "; parameters: " + str(self.parameters)
  369. representation += ")"
  370. return representation
  371. class OutputListener(object):
  372. def __init__(self, port_names):
  373. if not isinstance(port_names, list):
  374. port_names = [port_names]
  375. self.port_names = [port_name.port_name if isinstance(port_name, OutputPortEntry) else port_name for port_name in port_names]
  376. self.queue = Queue()
  377. def add(self, event):
  378. if len(self.port_names) == 0 or event.getPort() in self.port_names:
  379. self.queue.put_nowait(event)
  380. """ Tries for timeout seconds to fetch an event, returns None if failed.
  381. 0 as timeout means no waiting (blocking), returns None if queue is empty.
  382. -1 as timeout means blocking until an event can be fetched. """
  383. def fetch(self, timeout = 0):
  384. if timeout < 0:
  385. timeout = INFINITY
  386. while timeout >= 0:
  387. try:
  388. # wait in chunks of 100ms because we
  389. # can't receive (keyboard)interrupts while waiting
  390. return self.queue.get(True, 0.1 if timeout > 0.1 else timeout)
  391. except Empty:
  392. timeout -= 0.1
  393. return None
  394. class InputPortEntry(object):
  395. def __init__(self, virtual_name, instance):
  396. self.virtual_name = virtual_name
  397. self.instance = instance
  398. class OutputPortEntry(object):
  399. def __init__(self, port_name, virtual_name, instance):
  400. self.port_name = port_name
  401. self.virtual_name = virtual_name
  402. self.instance = instance
  403. class ControllerBase(object):
  404. def __init__(self, object_manager):
  405. self.object_manager = object_manager
  406. self.private_port_counter = 0
  407. # keep track of input ports
  408. self.input_ports = {}
  409. self.input_queue = EventQueue()
  410. # keep track of output ports
  411. self.output_ports = {}
  412. self.output_listeners = []
  413. self.simulated_time = None
  414. self.behind = False
  415. # accurate timer
  416. self.accurate_time = AccurateTime()
  417. def getSimulatedTime(self):
  418. return self.simulated_time
  419. def getWallClockTime(self):
  420. return self.accurate_time.get_wct()
  421. def addInputPort(self, virtual_name, instance = None):
  422. if instance == None:
  423. port_name = virtual_name
  424. else:
  425. port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name
  426. self.private_port_counter += 1
  427. self.input_ports[port_name] = InputPortEntry(virtual_name, instance)
  428. return port_name
  429. def addOutputPort(self, virtual_name, instance = None):
  430. if instance == None:
  431. port_name = virtual_name
  432. else:
  433. port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name
  434. self.private_port_counter += 1
  435. self.output_ports[port_name] = OutputPortEntry(port_name, virtual_name, instance)
  436. return port_name
  437. def broadcast(self, new_event, time_offset = 0):
  438. self.object_manager.broadcast(None, new_event, time_offset)
  439. def start(self):
  440. self.accurate_time.set_start_time()
  441. self.simulated_time = 0
  442. self.object_manager.start()
  443. def stop(self):
  444. pass
  445. def addInput(self, input_event_list, time_offset = 0, force_internal=False):
  446. # force_internal is for narrow_cast events, otherwise these would arrive as external events (on the current wall-clock time)
  447. if not isinstance(input_event_list, list):
  448. input_event_list = [input_event_list]
  449. for e in input_event_list:
  450. if e.getName() == "":
  451. raise InputException("Input event can't have an empty name.")
  452. if e.getPort() not in self.input_ports:
  453. raise InputException("Input port mismatch, no such port: " + e.getPort() + ".")
  454. if force_internal:
  455. self.input_queue.add((0 if self.simulated_time is None else self.simulated_time) + time_offset, e)
  456. else:
  457. self.input_queue.add((0 if self.simulated_time is None else self.accurate_time.get_wct()) + time_offset, e)
  458. def getEarliestEventTime(self):
  459. return min(self.object_manager.getEarliestEventTime(), self.input_queue.getEarliestTime())
  460. def handleInput(self):
  461. while not self.input_queue.isEmpty():
  462. event_time = self.input_queue.getEarliestTime()
  463. e = self.input_queue.pop()
  464. input_port = self.input_ports[e.getPort()]
  465. # e.port = input_port.virtual_name
  466. target_instance = input_port.instance
  467. if target_instance == None:
  468. self.broadcast(e, event_time - self.simulated_time)
  469. else:
  470. target_instance.addEvent(e, event_time - self.simulated_time)
  471. def outputEvent(self, event):
  472. for listener in self.output_listeners:
  473. listener.add(event)
  474. def addOutputListener(self, ports):
  475. listener = OutputListener(ports)
  476. self.output_listeners.append(listener)
  477. return listener
  478. def addMyOwnOutputListener(self, listener):
  479. self.output_listeners.append(listener)
  480. def getObjectManager(self):
  481. return self.object_manager
  482. class GameLoopControllerBase(ControllerBase):
  483. def __init__(self, object_manager):
  484. ControllerBase.__init__(self, object_manager)
  485. def update(self):
  486. self.handleInput()
  487. earliest_event_time = self.getEarliestEventTime()
  488. if earliest_event_time > time():
  489. self.simulated_time = earliest_event_time
  490. self.object_manager.stepAll()
  491. class EventLoop:
  492. # parameters:
  493. # schedule - a callback scheduling another callback in the event loop
  494. # this callback should take 2 parameters: (callback, timeout) and return an ID
  495. # clear - a callback that clears a scheduled callback
  496. # this callback should take an ID that was returned by 'schedule'
  497. def __init__(self, schedule, clear):
  498. self.schedule_callback = schedule
  499. self.clear_callback = clear
  500. self.scheduled_id = None
  501. self.last_print = 0.0
  502. # schedule relative to last_time
  503. #
  504. # argument 'wait_time' is the amount of virtual (simulated) time to wait
  505. #
  506. # NOTE: if the next wakeup (in simulated time) is in the past, the timeout is '0',
  507. # but because scheduling '0' timeouts hurts performance, we don't schedule anything
  508. # and return False instead
  509. def schedule(self, f, wait_time, behind = False):
  510. if self.scheduled_id is not None:
  511. # if the following error occurs, it is probably due to a flaw in the logic of EventLoopControllerBase
  512. raise RuntimeException("EventLoop class intended to maintain at most 1 scheduled callback.")
  513. if wait_time != INFINITY:
  514. self.scheduled_id = self.schedule_callback(f, wait_time, behind)
  515. def clear(self):
  516. if self.scheduled_id is not None:
  517. self.clear_callback(self.scheduled_id)
  518. self.scheduled_id = None
  519. def bind_controller(self, controller):
  520. pass
  521. class EventLoopControllerBase(ControllerBase):
  522. def __init__(self, object_manager, event_loop, finished_callback = None, behind_schedule_callback = None):
  523. ControllerBase.__init__(self, object_manager)
  524. if not isinstance(event_loop, EventLoop):
  525. raise RuntimeException("Event loop argument must be an instance of the EventLoop class!")
  526. self.event_loop = event_loop
  527. self.finished_callback = finished_callback
  528. self.behind_schedule_callback = behind_schedule_callback
  529. self.last_print_time = 0
  530. self.running = False
  531. self.input_condition = threading.Condition()
  532. self.behind = False
  533. self.event_loop.bind_controller(self)
  534. self.event_queue = []
  535. self.main_thread = thread.get_ident()
  536. def addInput(self, input_event, time_offset = 0, force_internal=False):
  537. # import pdb; pdb.set_trace()
  538. if self.main_thread == thread.get_ident():
  539. # Running on the main thread, so just execute what we want
  540. self.simulated_time = self.accurate_time.get_wct()
  541. ControllerBase.addInput(self, input_event, time_offset, force_internal)
  542. else:
  543. # Not on the main thread, so we have to queue these events for the main thread instead
  544. self.event_queue.append((input_event, time_offset, force_internal))
  545. self.event_loop.clear()
  546. self.event_loop.schedule(self.run, 0, True)
  547. def start(self):
  548. ControllerBase.start(self)
  549. self.run()
  550. def stop(self):
  551. self.event_loop.clear()
  552. ControllerBase.stop(self)
  553. def run(self, tkinter_event=None):
  554. start_time = self.accurate_time.get_wct()
  555. try:
  556. self.running = True
  557. # Process external events first
  558. while 1:
  559. while self.event_queue:
  560. self.addInput(*self.event_queue.pop(0))
  561. if self.accurate_time.get_wct() >= self.getEarliestEventTime():
  562. self.simulated_time = self.getEarliestEventTime()
  563. else:
  564. return
  565. # clear existing timeout
  566. self.event_loop.clear()
  567. self.handleInput()
  568. self.object_manager.stepAll()
  569. # schedule next timeout
  570. earliest_event_time = self.getEarliestEventTime()
  571. if earliest_event_time == INFINITY:
  572. if self.finished_callback: self.finished_callback() # TODO: This is not necessarily correct (keep_running necessary?)
  573. return
  574. now = self.accurate_time.get_wct()
  575. if earliest_event_time - now > 0:
  576. if self.behind:
  577. self.behind = False
  578. self.event_loop.schedule(self.run, earliest_event_time - now, now - start_time > 10)
  579. else:
  580. if now - earliest_event_time > 10 and now - self.last_print_time >= 1000:
  581. if self.behind_schedule_callback:
  582. self.behind_schedule_callback(self, now - earliest_event_time)
  583. print_debug('\rrunning %ims behind schedule' % (now - earliest_event_time))
  584. self.last_print_time = now
  585. self.behind = True
  586. if not self.behind:
  587. return
  588. finally:
  589. self.running = False
  590. if self.event_queue:
  591. self.event_loop.clear()
  592. self.event_loop.schedule(self.run, 0, True)
  593. class ThreadsControllerBase(ControllerBase):
  594. def __init__(self, object_manager, keep_running, behind_schedule_callback = None):
  595. ControllerBase.__init__(self, object_manager)
  596. self.keep_running = keep_running
  597. self.behind_schedule_callback = behind_schedule_callback
  598. self.input_condition = threading.Condition()
  599. self.stop_thread = False
  600. self.last_print_time = 0
  601. def addInput(self, input_event, time_offset = 0, force_internal=False):
  602. with self.input_condition:
  603. ControllerBase.addInput(self, input_event, time_offset, force_internal)
  604. self.input_condition.notifyAll()
  605. def start(self):
  606. self.run()
  607. def stop(self):
  608. with self.input_condition:
  609. self.stop_thread = True
  610. self.input_condition.notifyAll()
  611. def run(self):
  612. ControllerBase.start(self)
  613. while 1:
  614. # simulate
  615. with self.input_condition:
  616. self.handleInput()
  617. self.object_manager.stepAll()
  618. # wait until next timeout
  619. earliest_event_time = self.getEarliestEventTime()
  620. if earliest_event_time == INFINITY and not self.keep_running:
  621. return
  622. now = self.accurate_time.get_wct()
  623. if earliest_event_time - now > 0:
  624. if self.behind:
  625. self.behind = False
  626. with self.input_condition:
  627. if earliest_event_time == self.getEarliestEventTime() and not earliest_event_time == INFINITY:
  628. self.input_condition.wait((earliest_event_time - now) / 1000.0)
  629. else:
  630. # Something happened that made the queue fill up already, but we were not yet waiting for the Condition...
  631. pass
  632. else:
  633. if now - earliest_event_time > 10 and now - self.last_print_time >= 1000:
  634. if self.behind_schedule_callback:
  635. self.behind_schedule_callback(self, now - earliest_event_time)
  636. print_debug('\rrunning %ims behind schedule' % (now - earliest_event_time))
  637. self.last_print_time = now
  638. self.behind = True
  639. with self.input_condition:
  640. earliest_event_time = self.getEarliestEventTime()
  641. if earliest_event_time == INFINITY:
  642. if self.keep_running:
  643. self.input_condition.wait()
  644. earliest_event_time = self.getEarliestEventTime()
  645. else:
  646. self.stop_thread = True
  647. if self.stop_thread:
  648. break
  649. self.simulated_time = earliest_event_time
  650. class StatechartSemantics:
  651. # Big Step Maximality
  652. TakeOne = 0
  653. TakeMany = 1
  654. # Concurrency - not implemented yet
  655. Single = 0
  656. Many = 1
  657. # Preemption - not implemented yet
  658. NonPreemptive = 0
  659. Preemptive = 1
  660. # Internal Event Lifeline
  661. Queue = 0
  662. NextSmallStep = 1
  663. NextComboStep = 2
  664. # Input Event Lifeline
  665. Whole = 0
  666. FirstSmallStep = 1
  667. FirstComboStep = 2
  668. # Priority
  669. SourceParent = 0
  670. SourceChild = 1
  671. # TODO: add Memory Protocol options
  672. def __init__(self):
  673. # default semantics:
  674. self.big_step_maximality = self.TakeMany
  675. self.internal_event_lifeline = self.Queue
  676. self.input_event_lifeline = self.FirstComboStep
  677. self.priority = self.SourceParent
  678. self.concurrency = self.Single
  679. class State:
  680. def __init__(self, state_id, name, obj):
  681. self.state_id = state_id
  682. self.name = name
  683. self.obj = obj
  684. self.ancestors = []
  685. self.descendants = []
  686. self.descendant_bitmap = 0
  687. self.children = []
  688. self.parent = None
  689. self.enter = None
  690. self.exit = None
  691. self.default_state = None
  692. self.transitions = []
  693. self.history = []
  694. self.has_eventless_transitions = False
  695. def getEffectiveTargetStates(self):
  696. targets = [self]
  697. if self.default_state:
  698. targets.extend(self.default_state.getEffectiveTargetStates())
  699. return targets
  700. def fixTree(self):
  701. for c in self.children:
  702. if isinstance(c, HistoryState):
  703. self.history.append(c)
  704. c.parent = self
  705. c.ancestors.append(self)
  706. c.ancestors.extend(self.ancestors)
  707. c.fixTree()
  708. self.descendants.extend(self.children)
  709. for c in self.children:
  710. self.descendants.extend(c.descendants)
  711. for d in self.descendants:
  712. self.descendant_bitmap |= 2**d.state_id
  713. def addChild(self, child):
  714. self.children.append(child)
  715. def addTransition(self, transition):
  716. self.transitions.append(transition)
  717. def setEnter(self, enter):
  718. self.enter = enter
  719. def setExit(self, exit):
  720. self.exit = exit
  721. def __repr__(self):
  722. return "State(%s)" % (self.state_id)
  723. class HistoryState(State):
  724. def __init__(self, state_id, name, obj):
  725. State.__init__(self, state_id, name, obj)
  726. class ShallowHistoryState(HistoryState):
  727. def __init__(self, state_id, name, obj):
  728. HistoryState.__init__(self, state_id, name, obj)
  729. def getEffectiveTargetStates(self):
  730. if self.state_id in self.obj.history_values:
  731. targets = []
  732. for hv in self.obj.history_values[self.state_id]:
  733. targets.extend(hv.getEffectiveTargetStates())
  734. return targets
  735. else:
  736. # TODO: is it correct that in this case, the parent itself is also entered?
  737. return self.parent.getEffectiveTargetStates()
  738. class DeepHistoryState(HistoryState):
  739. def __init__(self, state_id, name, obj):
  740. HistoryState.__init__(self, state_id, name, obj)
  741. def getEffectiveTargetStates(self):
  742. if self.state_id in self.obj.history_values:
  743. return self.obj.history_values[self.state_id]
  744. else:
  745. # TODO: is it correct that in this case, the parent itself is also entered?
  746. return self.parent.getEffectiveTargetStates()
  747. class ParallelState(State):
  748. def __init__(self, state_id, name, obj):
  749. State.__init__(self, state_id, name, obj)
  750. def getEffectiveTargetStates(self):
  751. targets = [self]
  752. for c in self.children:
  753. if not isinstance(c, HistoryState):
  754. targets.extend(c.getEffectiveTargetStates())
  755. return targets
  756. class Transition:
  757. def __init__(self, obj, source, targets):
  758. self.guard = None
  759. self.action = None
  760. self.trigger = None
  761. self.source = source
  762. self.targets = targets
  763. self.obj = obj
  764. self.enabled_event = None # the event that enabled this transition
  765. self.optimize()
  766. def isEnabled(self, events, enabled_transitions):
  767. if self.trigger is None:
  768. self.enabled_event = None
  769. return (self.guard is None) or (self.guard == ELSE_GUARD and not enabled_transitions) or self.guard([])
  770. else:
  771. for event in events:
  772. if (self.trigger.name == event.name and (not self.trigger.port or self.trigger.port == event.port)) and ((self.guard is None) or (self.guard == ELSE_GUARD and not enabled_transitions) or self.guard(event.parameters)):
  773. self.enabled_event = event
  774. return True
  775. # @profile
  776. def fire(self):
  777. # exit states...
  778. exit_set = self.__exitSet()
  779. for s in exit_set:
  780. # remember which state(s) we were in if a history state is present
  781. for h in s.history:
  782. f = lambda s0: s0.ancestors and s0.parent == s
  783. if isinstance(h, DeepHistoryState):
  784. f = lambda s0: not s0.descendants and s0 in s.descendants
  785. self.obj.history_values[h.state_id] = list(filter(f, self.obj.configuration))
  786. for s in exit_set:
  787. print_debug('EXIT: %s::%s' % (self.obj.__class__.__name__, s.name))
  788. self.obj.eventless_states -= s.has_eventless_transitions
  789. # execute exit action(s)
  790. if s.exit:
  791. s.exit()
  792. self.obj.configuration_bitmap &= ~2**s.state_id
  793. # combo state changed area
  794. self.obj.combo_step.changed_bitmap |= 2**self.lca.state_id
  795. self.obj.combo_step.changed_bitmap |= self.lca.descendant_bitmap
  796. # execute transition action(s)
  797. if self.action:
  798. self.action(self.enabled_event.parameters if self.enabled_event else [])
  799. # enter states...
  800. targets = self.__getEffectiveTargetStates()
  801. enter_set = self.__enterSet(targets)
  802. for s in enter_set:
  803. print_debug('ENTER: %s::%s' % (self.obj.__class__.__name__, s.name))
  804. self.obj.eventless_states += s.has_eventless_transitions
  805. self.obj.configuration_bitmap |= 2**s.state_id
  806. # execute enter action(s)
  807. if s.enter:
  808. s.enter()
  809. if self.obj.eventless_states:
  810. self.obj.controller.object_manager.eventless.add(self.obj)
  811. else:
  812. self.obj.controller.object_manager.eventless.discard(self.obj)
  813. try:
  814. self.obj.configuration = self.obj.config_mem[self.obj.configuration_bitmap]
  815. except:
  816. self.obj.configuration = self.obj.config_mem[self.obj.configuration_bitmap] = sorted([s for s in list(self.obj.states.values()) if 2**s.state_id & self.obj.configuration_bitmap], key=lambda s: s.state_id)
  817. self.enabled_event = None
  818. def __getEffectiveTargetStates(self):
  819. targets = []
  820. for target in self.targets:
  821. for e_t in target.getEffectiveTargetStates():
  822. if not e_t in targets:
  823. targets.append(e_t)
  824. return targets
  825. def __exitSet(self):
  826. return [s for s in reversed(self.lca.descendants) if (s in self.obj.configuration)]
  827. def __enterSet(self, targets):
  828. target = targets[0]
  829. for a in reversed(target.ancestors):
  830. if a in self.source.ancestors:
  831. continue
  832. else:
  833. yield a
  834. for target in targets:
  835. yield target
  836. def setGuard(self, guard):
  837. self.guard = guard
  838. def setAction(self, action):
  839. self.action = action
  840. def setTrigger(self, trigger):
  841. self.trigger = trigger
  842. if self.trigger is None:
  843. self.source.has_eventless_transitions = True
  844. def optimize(self):
  845. # the least-common ancestor can be computed statically
  846. if self.source in self.targets[0].ancestors:
  847. self.lca = self.source
  848. else:
  849. self.lca = self.source.parent
  850. target = self.targets[0]
  851. if self.source.parent != target.parent: # external
  852. for a in self.source.ancestors:
  853. if a in target.ancestors:
  854. self.lca = a
  855. break
  856. def __repr__(self):
  857. return "Transition(%s, %s)" % (self.source, self.targets[0])
  858. class RuntimeClassBase(object):
  859. __metaclass__ = abc.ABCMeta
  860. def __init__(self, controller):
  861. self.events = EventQueue()
  862. self.active = False
  863. self.controller = controller
  864. self.__set_stable(True)
  865. self.inports = {}
  866. self.outports = {}
  867. self.timers = {}
  868. self.states = {}
  869. self.eventless_states = 0
  870. self.configuration_bitmap = 0
  871. self.transition_mem = {}
  872. self.config_mem = {}
  873. self.narrow_cast_port = self.controller.addInputPort("<narrow_cast>", self)
  874. self.semantics = StatechartSemantics()
  875. #to break ties in the heap,
  876. #compare by number of events in the list
  877. def __lt__(self, other):
  878. return len(self.events.event_list) < len(other.events.event_list)
  879. def getChildren(self, link_name):
  880. traversal_list = self.controller.object_manager.processAssociationReference(link_name)
  881. return [i["instance"] for i in self.controller.object_manager.getInstances(self, traversal_list)]
  882. def getSingleChild(self, link_name):
  883. return self.getChildren(link_name)[0] # assume this will return a single child...
  884. def getOutPortName(self, port_name):
  885. return self.outports[port_name] if port_name in self.outports else port_name
  886. def getInPortName(self, port_name):
  887. return self.inports[port_name] if port_name in self.inports else port_name
  888. def start(self):
  889. self.configuration = []
  890. self.active = True
  891. self.current_state = {}
  892. self.history_values = {}
  893. self.timers = {}
  894. self.timers_to_add = {}
  895. self.big_step = BigStepState()
  896. self.combo_step = ComboStepState()
  897. self.small_step = SmallStepState()
  898. self.__set_stable(False)
  899. self.initializeStatechart()
  900. self.processBigStepOutput()
  901. def stop(self):
  902. self.active = False
  903. self.__set_stable(True)
  904. def sccd_yield(self):
  905. return max(0, (self.controller.accurate_time.get_wct() - self.controller.simulated_time) / 1000.0)
  906. def getSimulatedTime(self):
  907. return self.controller.getSimulatedTime()
  908. def getWallClockTime(self):
  909. return self.controller.getWallClockTime()
  910. def updateConfiguration(self, states):
  911. self.configuration.extend(states)
  912. self.configuration_bitmap = sum([2**s.state_id for s in states])
  913. def addTimer(self, index, timeout):
  914. self.timers_to_add[index] = (self.controller.simulated_time + int(timeout * 1000), Event("_%iafter" % index))
  915. def removeTimer(self, index):
  916. if index in self.timers_to_add:
  917. del self.timers_to_add[index]
  918. if index in self.timers:
  919. self.events.remove(self.timers[index])
  920. del self.timers[index]
  921. self.earliest_event_time = self.events.getEarliestTime()
  922. def addEvent(self, event_list, time_offset = 0):
  923. event_time = self.controller.simulated_time + time_offset
  924. if not (event_time, self) in self.controller.object_manager.instance_times:
  925. heappush(self.controller.object_manager.instance_times, (event_time, self))
  926. if event_time < self.earliest_event_time:
  927. self.earliest_event_time = event_time
  928. if not isinstance(event_list, list):
  929. event_list = [event_list]
  930. for e in event_list:
  931. self.events.add(event_time, e)
  932. def processBigStepOutput(self):
  933. for e in self.big_step.output_events_port:
  934. self.controller.outputEvent(e)
  935. for e in self.big_step.output_events_om:
  936. self.controller.object_manager.addEvent(e)
  937. def __set_stable(self, is_stable):
  938. self.is_stable = is_stable
  939. # self.earliest_event_time keeps track of the earliest time this instance will execute a transition
  940. if not is_stable:
  941. self.earliest_event_time = self.controller.simulated_time
  942. elif not self.active:
  943. self.earliest_event_time = INFINITY
  944. else:
  945. self.earliest_event_time = self.events.getEarliestTime()
  946. if self.earliest_event_time != INFINITY:
  947. if not (self.earliest_event_time, self) in self.controller.object_manager.instance_times:
  948. heappush(self.controller.object_manager.instance_times, (self.earliest_event_time, self))
  949. def step(self):
  950. is_stable = False
  951. while not is_stable:
  952. due = []
  953. if self.events.getEarliestTime() <= self.controller.simulated_time:
  954. due = [self.events.pop()]
  955. is_stable = not self.bigStep(due)
  956. self.processBigStepOutput()
  957. for index, entry in list(self.timers_to_add.items()):
  958. self.timers[index] = self.events.add(*entry)
  959. self.timers_to_add = {}
  960. self.__set_stable(True)
  961. def inState(self, state_strings):
  962. state_ids = [self.states[state_string].state_id for state_string in state_strings]
  963. for state_id in state_ids:
  964. for s in self.configuration:
  965. if s.state_id == state_id:
  966. break
  967. else:
  968. return False
  969. return True
  970. def bigStep(self, input_events):
  971. self.big_step.next(input_events)
  972. self.small_step.reset()
  973. self.combo_step.reset()
  974. while self.comboStep():
  975. self.big_step.has_stepped = True
  976. if self.semantics.big_step_maximality == StatechartSemantics.TakeOne:
  977. break # Take One -> only one combo step allowed
  978. return self.big_step.has_stepped
  979. def comboStep(self):
  980. self.combo_step.next()
  981. while self.smallStep():
  982. self.combo_step.has_stepped = True
  983. return self.combo_step.has_stepped
  984. # generate transition candidates for current small step
  985. # @profile
  986. def generateCandidates(self):
  987. changed_bitmap = self.combo_step.changed_bitmap
  988. key = (self.configuration_bitmap, changed_bitmap)
  989. try:
  990. transitions = self.transition_mem[key]
  991. except:
  992. self.transition_mem[key] = transitions = [t for s in self.configuration if not (2**s.state_id & changed_bitmap) for t in s.transitions]
  993. enabledEvents = self.getEnabledEvents()
  994. enabledTransitions = []
  995. for t in transitions:
  996. if t.isEnabled(enabledEvents, enabledTransitions):
  997. enabledTransitions.append(t)
  998. return enabledTransitions
  999. # @profile
  1000. def smallStep(self):
  1001. def __younger_than(x, y):
  1002. if x.source in y.source.ancestors:
  1003. return 1
  1004. elif y.source in x.source.ancestors:
  1005. return -1
  1006. else:
  1007. return 0
  1008. if self.small_step.has_stepped:
  1009. self.small_step.next()
  1010. candidates = self.generateCandidates()
  1011. if candidates:
  1012. to_skip = set()
  1013. conflicting = []
  1014. for c1 in candidates:
  1015. if c1 not in to_skip:
  1016. conflict = [c1]
  1017. for c2 in candidates[candidates.index(c1):]:
  1018. if c2.source in c1.source.ancestors or c1.source in c2.source.ancestors:
  1019. conflict.append(c2)
  1020. to_skip.add(c2)
  1021. if sys.version_info[0] < 3:
  1022. conflicting.append(sorted(conflict, cmp=__younger_than))
  1023. else:
  1024. import functools
  1025. conflicting.append(sorted(conflict, key=functools.cmp_to_key(__younger_than)))
  1026. if self.semantics.concurrency == StatechartSemantics.Single:
  1027. candidate = conflicting[0]
  1028. if self.semantics.priority == StatechartSemantics.SourceParent:
  1029. candidate[-1].fire()
  1030. else:
  1031. candidate[0].fire()
  1032. elif self.semantics.concurrency == StatechartSemantics.Many:
  1033. pass # TODO: implement
  1034. self.small_step.has_stepped = True
  1035. return self.small_step.has_stepped
  1036. # @profile
  1037. def getEnabledEvents(self):
  1038. result = self.small_step.current_events + self.combo_step.current_events
  1039. if self.semantics.input_event_lifeline == StatechartSemantics.Whole or (
  1040. not self.big_step.has_stepped and
  1041. (self.semantics.input_event_lifeline == StatechartSemantics.FirstComboStep or (
  1042. not self.combo_step.has_stepped and
  1043. self.semantics.input_event_lifeline == StatechartSemantics.FirstSmallStep))):
  1044. result += self.big_step.input_events
  1045. return result
  1046. def raiseInternalEvent(self, event):
  1047. if self.semantics.internal_event_lifeline == StatechartSemantics.NextSmallStep:
  1048. self.small_step.addNextEvent(event)
  1049. elif self.semantics.internal_event_lifeline == StatechartSemantics.NextComboStep:
  1050. self.combo_step.addNextEvent(event)
  1051. elif self.semantics.internal_event_lifeline == StatechartSemantics.Queue:
  1052. self.addEvent(event)
  1053. def initializeStatechart(self):
  1054. self.updateConfiguration(self.default_targets)
  1055. for state in self.default_targets:
  1056. self.eventless_states += state.has_eventless_transitions
  1057. if state.enter:
  1058. state.enter()
  1059. if self.eventless_states:
  1060. self.controller.object_manager.eventless.add(self)
  1061. class BigStepState(object):
  1062. def __init__(self):
  1063. self.input_events = [] # input events received from environment before beginning of big step (e.g. from object manager, from input port)
  1064. self.output_events_port = [] # output events to be sent to output port after big step ends.
  1065. self.output_events_om = [] # output events to be sent to object manager after big step ends.
  1066. self.has_stepped = True
  1067. def next(self, input_events):
  1068. self.input_events = input_events
  1069. self.output_events_port = []
  1070. self.output_events_om = []
  1071. self.has_stepped = False
  1072. def outputEvent(self, event):
  1073. self.output_events_port.append(event)
  1074. def outputEventOM(self, event):
  1075. self.output_events_om.append(event)
  1076. class ComboStepState(object):
  1077. def __init__(self):
  1078. self.current_events = [] # set of enabled events during combo step
  1079. self.next_events = [] # internal events that were raised during combo step
  1080. self.changed_bitmap = 0 # set of all or-states that were the arena of a triggered transition during big step.
  1081. self.has_stepped = True
  1082. def reset(self):
  1083. self.current_events = []
  1084. self.next_events = []
  1085. def next(self):
  1086. self.current_events = self.next_events
  1087. self.next_events = []
  1088. self.changed_bitmap = 0
  1089. self.has_stepped = False
  1090. def addNextEvent(self, event):
  1091. self.next_events.append(event)
  1092. class SmallStepState(object):
  1093. def __init__(self):
  1094. self.current_events = [] # set of enabled events during small step
  1095. self.next_events = [] # events to become 'current' in the next small step
  1096. self.candidates = [] # document-ordered(!) list of transitions that can potentially be executed concurrently, or preempt each other, depending on concurrency semantics. If no concurrency is used and there are multiple candidates, the first one is chosen. Source states of candidates are *always* orthogonal to each other.
  1097. self.has_stepped = True
  1098. def reset(self):
  1099. self.current_events = []
  1100. self.next_events = []
  1101. def next(self):
  1102. self.current_events = self.next_events # raised events from previous small step
  1103. self.next_events = []
  1104. self.candidates = []
  1105. self.has_stepped = False
  1106. def addNextEvent(self, event):
  1107. self.next_events.append(event)
  1108. def addCandidate(self, t, p):
  1109. self.candidates.append((t, p))
  1110. def hasCandidates(self):
  1111. return len(self.candidates) > 0