main.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. import matplotlib
  2. matplotlib.use("TkAgg")
  3. from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  4. from matplotlib.figure import Figure
  5. from matplotlib import rcParams
  6. rcParams.update({'figure.autolayout': True})
  7. from Tkinter import *
  8. from PIL import Image, ImageTk
  9. import tkSimpleDialog
  10. import urllib
  11. import urllib2
  12. import json
  13. import time
  14. JUMP = 50
  15. MAX_WIDTH = 10 * JUMP
  16. MAX_HEIGHT = 10 * JUMP
  17. CLICK_TOLERANCE = 5
  18. address = "http://127.0.0.1:8001"
  19. taskname = "test"
  20. root = Tk()
  21. names = {}
  22. event_entry = StringVar()
  23. setting_initial = False
  24. request_new_state = False
  25. canvas = Canvas(root, width=MAX_WIDTH, height=MAX_HEIGHT, bg="white")
  26. name = 0
  27. class FakeLayer():
  28. def __init__(self, address):
  29. self.types = {}
  30. self.sources = {}
  31. self.targets = {}
  32. self.attrs = {}
  33. def send_event(self, event):
  34. pass
  35. def read_available_attributes(self, name):
  36. return [("name", "String")]
  37. def read_attribute(self, name, attr):
  38. return self.attrs.get(name, {}).get(attr, None)
  39. def set_attribute(self, name, attr, value):
  40. self.attrs.setdefault(name, {})[attr] = value
  41. def instantiate_element(self, name, block_type):
  42. self.types[name] = block_type
  43. def instantiate_link(self, name, link_type, source, target):
  44. self.types[name] = link_type
  45. self.sources[name] = source
  46. self.targets[name] = target
  47. self.attrs[name] = {}
  48. def simulate(self):
  49. pass
  50. def pause(self):
  51. pass
  52. def delete(self):
  53. pass
  54. def switch_initial(self, name):
  55. pass
  56. def auto_sanitize(self, auto):
  57. pass
  58. def set_current(self, name):
  59. pass
  60. def poll(self):
  61. pass
  62. attribute = []
  63. available_attrs = []
  64. inp_evts = []
  65. state = []
  66. outp_evts = []
  67. #inp_evts = [(2, "arm"), (6, "detected")]
  68. #state = [(0, "idle"), (2, "armed"), (6, "detected"), (8, "idle")]
  69. #outp_evts = [(6, "soundAlarm"), ]
  70. do_color = "grey"
  71. def poll(address):
  72. simulation_time = None
  73. working_available_attrs = []
  74. global do_color
  75. global request_new_state
  76. while 1:
  77. returnvalue = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname}))).read())
  78. print("Process " + str(returnvalue))
  79. if (returnvalue.startswith("AVAILABLE_ATTR_VALUE")):
  80. working_available_attrs.append([json.loads(returnvalue.split(" ", 1)[1]), None])
  81. elif (returnvalue.startswith("AVAILABLE_ATTR_TYPE")):
  82. working_available_attrs[-1][1] = json.loads(returnvalue.split(" ", 1)[1])
  83. elif (returnvalue.startswith("AVAILABLE_ATTR_END")):
  84. available_attrs.append(working_available_attrs)
  85. working_available_attrs = []
  86. elif (returnvalue.startswith("ATTR_VALUE")):
  87. v = returnvalue.split(" ", 1)[1]
  88. if v == "None":
  89. v = None
  90. else:
  91. v = json.loads(v)
  92. attribute.append(v)
  93. elif (returnvalue.startswith("SIM_TIME")):
  94. simulation_time = json.loads(returnvalue.split(" ", 1)[1])
  95. elif (returnvalue.startswith("SIM_STATE")):
  96. state.append((simulation_time, returnvalue.split(" ", 1)[1]))
  97. elif (returnvalue.startswith("SIM_EVENT")):
  98. inp_evts.append((simulation_time, returnvalue.split(" ", 1)[1]))
  99. elif (returnvalue.startswith("SIM_RAISE")):
  100. outp_evts.append((simulation_time, returnvalue.split(" ", 1)[1]))
  101. elif (returnvalue.startswith("CONFORMANCE_OK")):
  102. do_color = "grey"
  103. elif (returnvalue.startswith("CONFORMANCE_FAIL")):
  104. do_color = "red"
  105. elif (returnvalue.startswith("REQUEST_CURRENT_STATE")):
  106. do_color = "blue"
  107. request_new_state = True
  108. else:
  109. print("Error: got unknown result: " + returnvalue)
  110. class MvLayer():
  111. def __init__(self, address):
  112. import threading
  113. self.address = address
  114. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % taskname, "taskname": "task_manager"}))).read()
  115. thrd = threading.Thread(target=poll, args=[address])
  116. thrd.daemon = True
  117. thrd.start()
  118. def color():
  119. while 1:
  120. global do_color
  121. root.configure(bg=do_color)
  122. time.sleep(0.1)
  123. thrd = threading.Thread(target=color)
  124. thrd.daemon = True
  125. thrd.start()
  126. def read_available_attributes(self, name):
  127. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"read_available_attributes"', "taskname": taskname}))).read()
  128. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % name, "taskname": taskname}))).read()
  129. while 1:
  130. try:
  131. print("Attribute: " + str(available_attrs))
  132. return available_attrs.pop(0)
  133. except IndexError:
  134. time.sleep(0.1)
  135. def read_attribute(self, name, attr):
  136. print("Sending read_attribute")
  137. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"read_attribute"', "taskname": taskname}))).read()
  138. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % name, "taskname": taskname}))).read()
  139. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % attr, "taskname": taskname}))).read()
  140. print("Waiting for attribute")
  141. while 1:
  142. try:
  143. print("Attribute: " + str(attribute))
  144. return attribute.pop(0)
  145. except IndexError:
  146. time.sleep(0.1)
  147. def set_attribute(self, name, attr, value):
  148. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"set_attribute"', "taskname": taskname}))).read()
  149. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % name, "taskname": taskname}))).read()
  150. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % attr, "taskname": taskname}))).read()
  151. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": json.dumps(value), "taskname": taskname}))).read()
  152. def instantiate_element(self, name, block_type):
  153. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"instantiate_node"', "taskname": taskname}))).read()
  154. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (block_type), "taskname": taskname}))).read()
  155. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (name), "taskname": taskname}))).read()
  156. def instantiate_link(self, name, link_type, source, target):
  157. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"instantiate_association"', "taskname": taskname}))).read()
  158. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (link_type), "taskname": taskname}))).read()
  159. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (name), "taskname": taskname}))).read()
  160. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (source), "taskname": taskname}))).read()
  161. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (target), "taskname": taskname}))).read()
  162. def simulate(self):
  163. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"simulate"', "taskname": taskname}))).read()
  164. def pause(self):
  165. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"pause"', "taskname": taskname}))).read()
  166. def send_event(self, event):
  167. print("SENDING EVENT")
  168. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"event"', "taskname": taskname}))).read()
  169. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (event), "taskname": taskname}))).read()
  170. def delete(self, block):
  171. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"delete_element"', "taskname": taskname}))).read()
  172. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (block), "taskname": taskname}))).read()
  173. def switch_initial(self, name):
  174. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"switch_initial"', "taskname": taskname}))).read()
  175. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (name), "taskname": taskname}))).read()
  176. def auto_sanitize(self, auto):
  177. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"auto_sanitize"', "taskname": taskname}))).read()
  178. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": "true" if auto else "false", "taskname": taskname}))).read()
  179. def set_current(self, name):
  180. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"%s"' % (name), "taskname": taskname}))).read()
  181. def poll(self):
  182. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": '"poll"', "taskname": taskname}))).read()
  183. def lower(value):
  184. return value / JUMP * JUMP
  185. def upper(value):
  186. return (value / JUMP + 1) * JUMP
  187. def avg(a, b):
  188. return float(a + b) / 2
  189. class InterfaceCore():
  190. drawn = set()
  191. refs = dict()
  192. mv = MvLayer(address)
  193. #mv = FakeLayer(address)
  194. def delete(self, x, y):
  195. lname = self.find((x, y))
  196. self.mv.delete(lname)
  197. for i in self.refs[lname]:
  198. canvas.delete(i)
  199. del self.refs[lname]
  200. if lname in names:
  201. self.canvas.delete(names[lname])
  202. del names[lname]
  203. self.drawn = set([e for e in self.drawn if e[4] != lname])
  204. def clicked(self, event):
  205. if self.find((event.x, event.y)):
  206. # Something already there, so don't add, but modify
  207. lname = self.find((event.x, event.y))
  208. global request_new_state
  209. if request_new_state:
  210. request_new_state = True
  211. self.mv.set_current(lname)
  212. else:
  213. attrs = self.mv.read_available_attributes(lname)
  214. print("Processing attributes " + str(attrs))
  215. for attr, t in attrs:
  216. print("Reading attribute " + str(attr))
  217. old_value = self.mv.read_attribute(lname, attr)
  218. print("Got value " + str(old_value))
  219. if old_value == "None":
  220. old_value = None
  221. new_value = tkSimpleDialog.askstring("Attribute modification", attr, initialvalue=old_value)
  222. canvas.focus_set()
  223. if t == "Float":
  224. new_value = float(new_value)
  225. elif t == "String":
  226. new_value = str(new_value)
  227. elif t == "Natural":
  228. new_value = int(new_value)
  229. else:
  230. print("Got unknown type: " + str(t))
  231. self.mv.set_attribute(lname, attr, new_value)
  232. print("Set value")
  233. if attr in ["name", "event"]:
  234. if lname in names:
  235. self.canvas.delete(names[lname])
  236. del names[lname]
  237. entry = [x for x in self.drawn if str(x[4]) == str(lname)][0]
  238. xc, xy = avg(entry[0], entry[2]), avg(entry[1], entry[3])
  239. names[lname] = self.canvas.create_text(xc, xy, text=new_value)
  240. else:
  241. global name
  242. x = event.x
  243. y = event.y
  244. self.mv.instantiate_element(str(name), "State")
  245. r = canvas.create_oval(lower(x), lower(y), upper(x), upper(y), fill="white")
  246. b = (lower(x), lower(y), upper(x), upper(y), str(name), "NODE")
  247. self.drawn.add(b)
  248. self.refs[str(name)] = [r]
  249. name += 1
  250. def switch_initial(self, evt):
  251. if self.find((evt.x, evt.y)):
  252. lname = self.find((evt.x, evt.y))
  253. self.mv.switch_initial(lname)
  254. # Update visual representation
  255. for f in self.refs.values():
  256. self.canvas.itemconfigure(f, width=1)
  257. self.canvas.itemconfigure(self.refs[lname], width=4)
  258. def find(self, location):
  259. def sqrt(a):
  260. return a**0.5
  261. x, y = location
  262. matches = []
  263. for e in self.drawn:
  264. x1, y1, x2, y2, x0, y0 = e[0], e[1], e[2], e[3], x, y
  265. if e[5] == "LINK":
  266. distance = abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1) / sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2) - CLICK_TOLERANCE
  267. elif e[5] == "NODE":
  268. if x0 <= x2 and x0 > x1 and y0 <= y2 and y0 > y1:
  269. distance = 0.0
  270. else:
  271. distance = float("inf")
  272. matches.append((distance, e[4]))
  273. if matches:
  274. minimal_distance, name = sorted(matches)[0]
  275. if minimal_distance <= 0:
  276. return name
  277. return None
  278. def draw(self, start, end):
  279. source = self.find(start)
  280. target = self.find(end)
  281. if source and target:
  282. global name
  283. self.mv.instantiate_link(str(name), "Transition", source, target)
  284. self.refs[str(name)] = [canvas.create_line(start[0], start[1], end[0], end[1], fill="black", arrow=LAST)]
  285. self.drawn.add((start[0], start[1], end[0], end[1], str(name), "LINK"))
  286. name += 1
  287. def add_event(self, event):
  288. self.mv.send_event(event)
  289. core = InterfaceCore()
  290. def clicked(event):
  291. if setting_initial:
  292. core.switch_initial(event)
  293. else:
  294. core.clicked(event)
  295. def draw(event):
  296. global start_location
  297. start_location = (event.x, event.y)
  298. def release(event):
  299. core.draw(start_location, (event.x, event.y))
  300. def simulate():
  301. core.mv.simulate()
  302. def pause():
  303. core.mv.pause()
  304. def delete(event):
  305. core.delete(event.x, event.y)
  306. def add_event():
  307. core.add_event(event_entry.get())
  308. def control_released(evt):
  309. global setting_initial
  310. setting_initial = False
  311. def control_pressed(evt):
  312. global setting_initial
  313. setting_initial = True
  314. def auto_sanitize():
  315. core.mv.auto_sanitize(True)
  316. def manual_sanitize():
  317. core.mv.auto_sanitize(False)
  318. buttons = [
  319. Button(root, text="START", command=simulate),
  320. Button(root, text="PAUSE", command=pause),
  321. Button(root, text="AUTO", command=auto_sanitize),
  322. Button(root, text="MANUAL", command=manual_sanitize),
  323. Entry(root, textvariable=event_entry),
  324. Button(root, text="EVENT", command=add_event),
  325. ]
  326. for i, b in enumerate(buttons):
  327. b.grid(row=0, column=i)
  328. canvas.grid(row=1,column=0,columnspan=len(buttons))
  329. core.canvas = canvas
  330. for i in range(JUMP, MAX_HEIGHT, JUMP):
  331. canvas.create_line(0, i, MAX_HEIGHT, i, fill="grey")
  332. for i in range(JUMP, MAX_WIDTH, JUMP):
  333. canvas.create_line(i, 0, i, MAX_WIDTH, fill="grey")
  334. canvas.focus_set()
  335. canvas.bind("<KeyPress-Control_L>", control_pressed)
  336. canvas.bind("<KeyRelease-Control_L>", control_released)
  337. canvas.bind("<Button-1>", clicked)
  338. canvas.bind("<Button-2>", delete)
  339. canvas.bind("<Button-3>", draw)
  340. canvas.bind("<ButtonRelease-3>", release)
  341. visual = Toplevel(root)
  342. # Example:
  343. # simulation = [(1, "A"), (3, "B"), (4, "A")]
  344. #inp_evts = [(2, "arm"), (6, "detected")]
  345. #state = [(0, "idle"), (2, "armed"), (6, "detected"), (8, "idle")]
  346. #outp_evts = [(6, "soundAlarm"), ]
  347. inp_evts = []
  348. state = []
  349. outp_evts = []
  350. frame = Frame(visual)
  351. frame.pack(fill=BOTH,expand=True)
  352. figsize = (6, 3)
  353. f_inp = Figure(figsize=figsize)
  354. a_inp = f_inp.add_subplot(111)
  355. a_inp.plot([], [])
  356. f_inp.suptitle("Inputs")
  357. fcanvas_inp = FigureCanvasTkAgg(f_inp, frame)
  358. fcanvas_inp.show()
  359. fcanvas_inp.get_tk_widget().pack()
  360. f_state = Figure(figsize=figsize)
  361. a_state = f_state.add_subplot(111)
  362. a_state.plot([], [])
  363. f_state.suptitle("State")
  364. fcanvas_state = FigureCanvasTkAgg(f_state, frame)
  365. fcanvas_state.show()
  366. fcanvas_state.get_tk_widget().pack()
  367. f_outp = Figure(figsize=figsize)
  368. a_outp = f_outp.add_subplot(111)
  369. a_outp.plot([], [])
  370. f_outp.suptitle("Outputs")
  371. fcanvas_outp = FigureCanvasTkAgg(f_outp, frame)
  372. fcanvas_outp.show()
  373. fcanvas_outp.get_tk_widget().pack()
  374. reverse_lookup_inp = {}
  375. reverse_lookup_state = {}
  376. reverse_lookup_outp = {}
  377. glob_max_x = 0.0
  378. def update_graphs():
  379. if state:
  380. max_x = state[-1][0]
  381. else:
  382. max_x = 0.0001
  383. global glob_max_x
  384. glob_max_x = max(max_x, glob_max_x)
  385. core.mv.poll()
  386. # Input events
  387. times = [x[0] for x in inp_evts]
  388. events = [reverse_lookup_inp.setdefault(x[1], len(reverse_lookup_inp)) for x in inp_evts]
  389. lookup = [None] * len(reverse_lookup_inp)
  390. for k, v in reverse_lookup_inp.items():
  391. lookup[v] = k
  392. a_inp.clear()
  393. a_inp.plot(times, events, linestyle="none", marker="o")
  394. f_inp.get_axes()[0].set_xbound(lower=0.0, upper=glob_max_x)
  395. f_inp.get_axes()[0].set_yticks(range(-2, len(lookup) + 2))
  396. f_inp.get_axes()[0].set_yticklabels(["", ""] + lookup + ["", ""])
  397. fcanvas_inp.draw()
  398. # States
  399. times = [x[0] for x in state]
  400. events = [reverse_lookup_state.setdefault(x[1], len(reverse_lookup_state)) for x in state]
  401. lookup = [None] * len(reverse_lookup_state)
  402. for k, v in reverse_lookup_state.items():
  403. lookup[v] = k
  404. a_state.clear()
  405. a_state.plot(times, events, linestyle="solid", drawstyle="steps-post")
  406. f_state.get_axes()[0].set_xbound(lower=0.0, upper=glob_max_x)
  407. f_state.get_axes()[0].set_yticks(range(-2, len(lookup) + 2))
  408. f_state.get_axes()[0].set_yticklabels(["", ""] + lookup + ["", ""])
  409. fcanvas_state.draw()
  410. # Input events
  411. times = [x[0] for x in outp_evts]
  412. events = [reverse_lookup_outp.setdefault(x[1], len(reverse_lookup_outp)) for x in outp_evts]
  413. lookup = [None] * len(reverse_lookup_outp)
  414. for k, v in reverse_lookup_outp.items():
  415. lookup[v] = k
  416. a_outp.clear()
  417. a_outp.plot(times, events, linestyle="none", marker="o")
  418. f_outp.get_axes()[0].set_xbound(lower=0.0, upper=glob_max_x)
  419. f_outp.get_axes()[0].set_yticks(range(-2, len(lookup) + 2))
  420. f_outp.get_axes()[0].set_yticklabels(["", ""] + lookup + ["", ""])
  421. fcanvas_outp.draw()
  422. root.after(500, update_graphs)
  423. root.after(1, update_graphs)
  424. root.mainloop()