Showing
24 changed files
with
1019 additions
and
42 deletions
... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
3 | import sys, os | 3 | import sys, os |
4 | from distutils.core import setup | 4 | from distutils.core import setup |
5 | 5 | ||
6 | -def doc_files() : | 6 | +def doc_files () : |
7 | import os, os.path | 7 | import os, os.path |
8 | result = {} | 8 | result = {} |
9 | for root, dirs, files in os.walk("doc") : | 9 | for root, dirs, files in os.walk("doc") : |
... | @@ -15,6 +15,15 @@ def doc_files() : | ... | @@ -15,6 +15,15 @@ def doc_files() : |
15 | result[target_dir].append(os.path.join(root, name)) | 15 | result[target_dir].append(os.path.join(root, name)) |
16 | return list(result.items()) | 16 | return list(result.items()) |
17 | 17 | ||
18 | +def abcd_resources () : | ||
19 | + collected = ["*.txt", "*.html", "*.css", "*.js", "*.png", "*.jpg"] | ||
20 | + import glob, os.path | ||
21 | + result = [] | ||
22 | + for pattern in collected : | ||
23 | + for path in glob.glob("snakes/utils/abcd/resources/" + pattern) : | ||
24 | + result.append(os.path.join("resources", os.path.basename(path))) | ||
25 | + return result | ||
26 | + | ||
18 | try : | 27 | try : |
19 | long_description=open("README").read() | 28 | long_description=open("README").read() |
20 | except : | 29 | except : |
... | @@ -56,5 +65,6 @@ if __name__ == "__main__" : | ... | @@ -56,5 +65,6 @@ if __name__ == "__main__" : |
56 | "snakes.utils.abcd", | 65 | "snakes.utils.abcd", |
57 | "snakes.utils.ctlstar", | 66 | "snakes.utils.ctlstar", |
58 | ], | 67 | ], |
68 | + package_data={"snakes.utils.abcd": abcd_resources()}, | ||
59 | data_files=doc_files() + emacs, | 69 | data_files=doc_files() + emacs, |
60 | ) | 70 | ) | ... | ... |
... | @@ -906,15 +906,11 @@ class Expression (ArcAnnotation) : | ... | @@ -906,15 +906,11 @@ class Expression (ArcAnnotation) : |
906 | return Token(True) | 906 | return Token(True) |
907 | else : | 907 | else : |
908 | env = binding._dict | 908 | env = binding._dict |
909 | - # just to allow a cleaner implementation of let | 909 | + env["__binding__"] = binding._dict |
910 | - env["__binding__"] = env | ||
911 | - env["__globals__"] = self.globals | ||
912 | try : | 910 | try : |
913 | return Token(self.globals(self._expr, env)) | 911 | return Token(self.globals(self._expr, env)) |
914 | finally : | 912 | finally : |
915 | - # cleanup what was added above | ||
916 | del env["__binding__"] | 913 | del env["__binding__"] |
917 | - del env["__globals__"] | ||
918 | def __call__ (self, binding) : | 914 | def __call__ (self, binding) : |
919 | """Returns the value from `bind` (but not encapsulated in a | 915 | """Returns the value from `bind` (but not encapsulated in a |
920 | `Token`). | 916 | `Token`). | ... | ... |
... | @@ -47,7 +47,7 @@ False | ... | @@ -47,7 +47,7 @@ False |
47 | automatically loaded) | 47 | automatically loaded) |
48 | """ | 48 | """ |
49 | 49 | ||
50 | -import os, os.path, subprocess, collections | 50 | +import os, os.path, subprocess, collections, codecs |
51 | import snakes.plugins | 51 | import snakes.plugins |
52 | from snakes.plugins.clusters import Cluster | 52 | from snakes.plugins.clusters import Cluster |
53 | from snakes.compat import * | 53 | from snakes.compat import * |
... | @@ -78,7 +78,7 @@ class Graph (Cluster) : | ... | @@ -78,7 +78,7 @@ class Graph (Cluster) : |
78 | else : | 78 | else : |
79 | tag = "%s " % tag | 79 | tag = "%s " % tag |
80 | return (["%s[" % tag, | 80 | return (["%s[" % tag, |
81 | - ["%s=%s" % (key, self.escape(str(val))) | 81 | + ["%s=%s" % (key, self.escape(unicode(val))) |
82 | for key, val in attr.items()], | 82 | for key, val in attr.items()], |
83 | "];"]) | 83 | "];"]) |
84 | def _dot (self) : | 84 | def _dot (self) : |
... | @@ -97,7 +97,7 @@ class Graph (Cluster) : | ... | @@ -97,7 +97,7 @@ class Graph (Cluster) : |
97 | return lines | 97 | return lines |
98 | def _dot_text (self, lines, indent=0) : | 98 | def _dot_text (self, lines, indent=0) : |
99 | for l in lines : | 99 | for l in lines : |
100 | - if isinstance(l, str) : | 100 | + if isinstance(l, (str, unicode)) : |
101 | yield " "*indent*2 + l | 101 | yield " "*indent*2 + l |
102 | else : | 102 | else : |
103 | for x in self._dot_text(l, indent+1) : | 103 | for x in self._dot_text(l, indent+1) : |
... | @@ -105,6 +105,7 @@ class Graph (Cluster) : | ... | @@ -105,6 +105,7 @@ class Graph (Cluster) : |
105 | def dot (self) : | 105 | def dot (self) : |
106 | self.done = set() | 106 | self.done = set() |
107 | return "\n".join(self._dot_text(["digraph {", | 107 | return "\n".join(self._dot_text(["digraph {", |
108 | + 'charset="UTF-8"', | ||
108 | ['node [label="N",' | 109 | ['node [label="N",' |
109 | ' fillcolor="#FFFFFF",' | 110 | ' fillcolor="#FFFFFF",' |
110 | ' fontcolor="#000000",' | 111 | ' fontcolor="#000000",' |
... | @@ -115,13 +116,15 @@ class Graph (Cluster) : | ... | @@ -115,13 +116,15 @@ class Graph (Cluster) : |
115 | self._dot(), | 116 | self._dot(), |
116 | "}"])) | 117 | "}"])) |
117 | def escape (self, text) : | 118 | def escape (self, text) : |
118 | - return '"%s"' % text.replace('"', r'\"') | 119 | + if text.startswith("<") and text.endswith(">") : |
120 | + return text | ||
121 | + else : | ||
122 | + return '"%s"' % text.replace('"', r'\"') | ||
119 | def render (self, filename, engine="dot", debug=False) : | 123 | def render (self, filename, engine="dot", debug=False) : |
120 | if engine not in ("dot", "neato", "twopi", "circo", "fdp") : | 124 | if engine not in ("dot", "neato", "twopi", "circo", "fdp") : |
121 | raise ValueError("unknown GraphViz engine %r" % engine) | 125 | raise ValueError("unknown GraphViz engine %r" % engine) |
122 | - outfile = open(filename + ".dot", "w") | 126 | + with codecs.open(filename + ".dot", "w", "utf-8") as outfile : |
123 | - outfile.write(self.dot()) | 127 | + outfile.write(self.dot()) |
124 | - outfile.close() | ||
125 | if debug : | 128 | if debug : |
126 | dot = subprocess.Popen([engine, "-T" + filename.rsplit(".", 1)[-1], | 129 | dot = subprocess.Popen([engine, "-T" + filename.rsplit(".", 1)[-1], |
127 | "-o" + filename, outfile.name], | 130 | "-o" + filename, outfile.name], |
... | @@ -226,11 +229,10 @@ def extend (module) : | ... | @@ -226,11 +229,10 @@ def extend (module) : |
226 | self._copy_edges(nodemap, g, arc_attr) | 229 | self._copy_edges(nodemap, g, arc_attr) |
227 | if graph_attr : | 230 | if graph_attr : |
228 | graph_attr(self, g.attr) | 231 | graph_attr(self, g.attr) |
229 | - if filename is None : | 232 | + if filename is not None : |
230 | - g.nodemap = nodemap | ||
231 | - return g | ||
232 | - else : | ||
233 | g.render(filename, engine, debug) | 233 | g.render(filename, engine, debug) |
234 | + g.nodemap = nodemap | ||
235 | + return g | ||
234 | def _copy (self, nodemap, sub, cluster_attr, place_attr, trans_attr) : | 236 | def _copy (self, nodemap, sub, cluster_attr, place_attr, trans_attr) : |
235 | attr = dict(style="invis") | 237 | attr = dict(style="invis") |
236 | if cluster_attr : | 238 | if cluster_attr : |
... | @@ -249,6 +251,8 @@ def extend (module) : | ... | @@ -249,6 +251,8 @@ def extend (module) : |
249 | label="%s\\n%s" % (node.name, str(node.guard))) | 251 | label="%s\\n%s" % (node.name, str(node.guard))) |
250 | if trans_attr : | 252 | if trans_attr : |
251 | trans_attr(node, attr) | 253 | trans_attr(node, attr) |
254 | + attr["tooltip"] = node.name | ||
255 | + attr["id"] = nodemap[name] | ||
252 | graph.add_node(nodemap[name], attr) | 256 | graph.add_node(nodemap[name], attr) |
253 | for child in sub.children() : | 257 | for child in sub.children() : |
254 | graph.add_child(self._copy(nodemap, child, cluster_attr, | 258 | graph.add_child(self._copy(nodemap, child, cluster_attr, | ... | ... |
... | @@ -67,9 +67,11 @@ def unlet (expr, *names) : | ... | @@ -67,9 +67,11 @@ def unlet (expr, *names) : |
67 | return unparse(new), drop.calls | 67 | return unparse(new), drop.calls |
68 | 68 | ||
69 | class MakeLet (object) : | 69 | class MakeLet (object) : |
70 | + def __init__ (self, globals) : | ||
71 | + self.globals = globals | ||
70 | def match (self, match, binding) : | 72 | def match (self, match, binding) : |
71 | env = dict(binding) | 73 | env = dict(binding) |
72 | - env.update(env["__globals__"]) | 74 | + env.update(iter(self.globals)) |
73 | exec("", env) | 75 | exec("", env) |
74 | old = set(env) | 76 | old = set(env) |
75 | exec(match, env) | 77 | exec(match, env) |
... | @@ -93,5 +95,5 @@ def extend (module) : | ... | @@ -93,5 +95,5 @@ def extend (module) : |
93 | class PetriNet (module.PetriNet) : | 95 | class PetriNet (module.PetriNet) : |
94 | def __init__ (self, name, **args) : | 96 | def __init__ (self, name, **args) : |
95 | module.PetriNet.__init__(self, name, **args) | 97 | module.PetriNet.__init__(self, name, **args) |
96 | - self.globals["let"] = MakeLet() | 98 | + self.globals["let"] = MakeLet(self.globals) |
97 | return PetriNet, unlet | 99 | return PetriNet, unlet | ... | ... |
... | @@ -272,9 +272,6 @@ def extend (module) : | ... | @@ -272,9 +272,6 @@ def extend (module) : |
272 | else : | 272 | else : |
273 | guard = guard & snk.Expression("newpids(%s)" | 273 | guard = guard & snk.Expression("newpids(%s)" |
274 | % ", ".join(assign)) | 274 | % ", ".join(assign)) |
275 | - for pid in self.pids.killed : | ||
276 | - pidcount = vars.fresh(add=True, base="next_%s" % pid) | ||
277 | - self.pids.next[pid] = pidcount | ||
278 | snk.Transition.__init__(self, name, guard, **args) | 275 | snk.Transition.__init__(self, name, guard, **args) |
279 | def vars (self) : | 276 | def vars (self) : |
280 | return self.pids.vars() | snk.Transition.vars(self) | 277 | return self.pids.vars() | snk.Transition.vars(self) |
... | @@ -306,10 +303,6 @@ def extend (module) : | ... | @@ -306,10 +303,6 @@ def extend (module) : |
306 | prod[child] = snk.Tuple([snk.Variable(child), | 303 | prod[child] = snk.Tuple([snk.Variable(child), |
307 | snk.Value(0)]) | 304 | snk.Value(0)]) |
308 | for pid in trans.pids.killed : | 305 | for pid in trans.pids.killed : |
309 | - if pid not in trans.pids.spawned : | ||
310 | - pidcount = trans.pids.next[pid] | ||
311 | - cons[pid] = snk.Tuple([snk.Variable(pid), | ||
312 | - snk.Variable(pidcount)]) | ||
313 | prod.pop(pid, None) | 306 | prod.pop(pid, None) |
314 | if len(cons) > 1 : | 307 | if len(cons) > 1 : |
315 | self.add_input(self.nextpids, trans.name, | 308 | self.add_input(self.nextpids, trans.name, |
... | @@ -320,7 +313,7 @@ def extend (module) : | ... | @@ -320,7 +313,7 @@ def extend (module) : |
320 | if len(prod) > 1 : | 313 | if len(prod) > 1 : |
321 | self.add_output(self.nextpids, trans.name, | 314 | self.add_output(self.nextpids, trans.name, |
322 | snk.MultiArc(prod.values())) | 315 | snk.MultiArc(prod.values())) |
323 | - elif len(prod) == 1 : | 316 | + elif len(cons) == 1 : |
324 | self.add_output(self.nextpids, trans.name, | 317 | self.add_output(self.nextpids, trans.name, |
325 | iter(prod.values()).next()) | 318 | iter(prod.values()).next()) |
326 | return PetriNet, Transition, Pid, ("tPid", tPid), ("tNextPid", tNextPid) | 319 | return PetriNet, Transition, Pid, ("tPid", tPid), ("tNextPid", tNextPid) | ... | ... |
... | @@ -432,7 +432,24 @@ class Builder (object) : | ... | @@ -432,7 +432,24 @@ class Builder (object) : |
432 | self.instances.add(name) | 432 | self.instances.add(name) |
433 | path = self.path + [name] | 433 | path = self.path + [name] |
434 | builder = self.__class__(self.snk, path, self) | 434 | builder = self.__class__(self.snk, path, self) |
435 | - return builder.build(spec) | 435 | + net = builder.build(spec) |
436 | + src = (node.st.source(), | ||
437 | + node.st.srow, node.st.scol, | ||
438 | + node.st.erow, node.st.ecol) | ||
439 | + for trans in net.transition() : | ||
440 | + try : | ||
441 | + lbl = trans.label("instances") | ||
442 | + trans.label(instances=[src] + lbl) | ||
443 | + except KeyError : | ||
444 | + trans.label(instances=[src]) | ||
445 | + for place in net.place() : | ||
446 | + if place.status == self.snk.Status(None) : | ||
447 | + try : | ||
448 | + lbl = place.label("instances") | ||
449 | + place.label(instances=[src] + lbl) | ||
450 | + except KeyError : | ||
451 | + place.label(instances=[src]) | ||
452 | + return net | ||
436 | # control flow operations | 453 | # control flow operations |
437 | def build_Sequence (self, node) : | 454 | def build_Sequence (self, node) : |
438 | return self.snk.PetriNet.__and__ | 455 | return self.snk.PetriNet.__and__ | ... | ... |
snakes/utils/abcd/html.py
0 → 100644
This diff is collapsed. Click to expand it.
1 | -import sys, optparse, os.path | 1 | +import sys, optparse, os.path, webbrowser |
2 | import pdb, traceback | 2 | import pdb, traceback |
3 | import snakes.plugins | 3 | import snakes.plugins |
4 | from snakes.utils.abcd.build import Builder | 4 | from snakes.utils.abcd.build import Builder |
... | @@ -7,6 +7,7 @@ from snakes.lang.pgen import ParseError | ... | @@ -7,6 +7,7 @@ from snakes.lang.pgen import ParseError |
7 | from snakes.utils.abcd import CompilationError, DeclarationError | 7 | from snakes.utils.abcd import CompilationError, DeclarationError |
8 | from snakes.utils.abcd.simul import Simulator | 8 | from snakes.utils.abcd.simul import Simulator |
9 | from snakes.utils.abcd.checker import Checker | 9 | from snakes.utils.abcd.checker import Checker |
10 | +from snakes.utils.abcd.html import build as html | ||
10 | 11 | ||
11 | ## | 12 | ## |
12 | ## error messages | 13 | ## error messages |
... | @@ -21,10 +22,16 @@ ERR_COMPILE = 6 | ... | @@ -21,10 +22,16 @@ ERR_COMPILE = 6 |
21 | ERR_OUTPUT = 7 | 22 | ERR_OUTPUT = 7 |
22 | ERR_BUG = 255 | 23 | ERR_BUG = 255 |
23 | 24 | ||
25 | +def log (message) : | ||
26 | + sys.stdout.write("abcd: %s\n" % message.strip()) | ||
27 | + sys.stdout.flush() | ||
28 | + | ||
24 | def err (message) : | 29 | def err (message) : |
25 | sys.stderr.write("abcd: %s\n" % message.strip()) | 30 | sys.stderr.write("abcd: %s\n" % message.strip()) |
31 | + sys.stderr.flush() | ||
26 | 32 | ||
27 | def die (code, message=None) : | 33 | def die (code, message=None) : |
34 | + global options | ||
28 | if message : | 35 | if message : |
29 | err(message) | 36 | err(message) |
30 | if options.debug : | 37 | if options.debug : |
... | @@ -33,6 +40,7 @@ def die (code, message=None) : | ... | @@ -33,6 +40,7 @@ def die (code, message=None) : |
33 | sys.exit(code) | 40 | sys.exit(code) |
34 | 41 | ||
35 | def bug () : | 42 | def bug () : |
43 | + global options | ||
36 | sys.stderr.write(""" | 44 | sys.stderr.write(""" |
37 | ******************************************************************** | 45 | ******************************************************************** |
38 | *** An unexpected error ocurred. Please report this bug to *** | 46 | *** An unexpected error ocurred. Please report this bug to *** |
... | @@ -79,12 +87,19 @@ opt.add_option("--debug", | ... | @@ -79,12 +87,19 @@ opt.add_option("--debug", |
79 | opt.add_option("-s", "--simul", | 87 | opt.add_option("-s", "--simul", |
80 | dest="simul", action="store_true", default=False, | 88 | dest="simul", action="store_true", default=False, |
81 | help="launch interactive code simulator") | 89 | help="launch interactive code simulator") |
90 | +opt.add_option("--headless", | ||
91 | + dest="headless", action="store_true", default=False, | ||
92 | + help="headless code simulator (don't start browser)") | ||
93 | +opt.add_option("-H", "--html", | ||
94 | + dest="html", action="store", default=None, | ||
95 | + help="save net as HTML", | ||
96 | + metavar="OUTFILE") | ||
82 | opt.add_option("--check", | 97 | opt.add_option("--check", |
83 | dest="check", action="store_true", default=False, | 98 | dest="check", action="store_true", default=False, |
84 | help="check assertions") | 99 | help="check assertions") |
85 | 100 | ||
86 | def getopts (args) : | 101 | def getopts (args) : |
87 | - global options, abcd | 102 | + global options, abcd, tmp |
88 | (options, args) = opt.parse_args(args) | 103 | (options, args) = opt.parse_args(args) |
89 | plugins = [] | 104 | plugins = [] |
90 | for p in options.plugins : | 105 | for p in options.plugins : |
... | @@ -98,6 +113,8 @@ def getopts (args) : | ... | @@ -98,6 +113,8 @@ def getopts (args) : |
98 | if gvopt and "gv" not in plugins : | 113 | if gvopt and "gv" not in plugins : |
99 | plugins.append("gv") | 114 | plugins.append("gv") |
100 | break | 115 | break |
116 | + if (options.html or options.simul) and "gv" not in plugins : | ||
117 | + plugins.append("gv") | ||
101 | options.plugins = plugins | 118 | options.plugins = plugins |
102 | if len(args) < 1 : | 119 | if len(args) < 1 : |
103 | err("no input file provided") | 120 | err("no input file provided") |
... | @@ -112,6 +129,10 @@ def getopts (args) : | ... | @@ -112,6 +129,10 @@ def getopts (args) : |
112 | err("input file also used as output (--pnml)") | 129 | err("input file also used as output (--pnml)") |
113 | opt.print_help() | 130 | opt.print_help() |
114 | die(ERR_ARG) | 131 | die(ERR_ARG) |
132 | + if options.html == abcd : | ||
133 | + err("input file also used as output (--html)") | ||
134 | + opt.print_help() | ||
135 | + die(ERR_ARG) | ||
115 | for engine in gv_engines : | 136 | for engine in gv_engines : |
116 | if getattr(options, "gv%s" % engine) == abcd : | 137 | if getattr(options, "gv%s" % engine) == abcd : |
117 | err("input file also used as output (--%s)" % engine) | 138 | err("input file also used as output (--%s)" % engine) |
... | @@ -129,7 +150,7 @@ def place_attr (place, attr) : | ... | @@ -129,7 +150,7 @@ def place_attr (place, attr) : |
129 | elif place.status == snk.internal : | 150 | elif place.status == snk.internal : |
130 | pass | 151 | pass |
131 | elif place.status == snk.exit : | 152 | elif place.status == snk.exit : |
132 | - attr["fillcolor"] = "yellow" | 153 | + attr["fillcolor"] = "orange" |
133 | else : | 154 | else : |
134 | attr["fillcolor"] = "lightblue" | 155 | attr["fillcolor"] = "lightblue" |
135 | # fix shape | 156 | # fix shape |
... | @@ -142,9 +163,9 @@ def place_attr (place, attr) : | ... | @@ -142,9 +163,9 @@ def place_attr (place, attr) : |
142 | if count == 0 : | 163 | if count == 0 : |
143 | marking = " " | 164 | marking = " " |
144 | elif count == 1 : | 165 | elif count == 1 : |
145 | - marking = "@" | 166 | + marking = "•" |
146 | else : | 167 | else : |
147 | - marking = "%s@" % count | 168 | + marking = "%s•" % count |
148 | else : | 169 | else : |
149 | marking = str(place.tokens) | 170 | marking = str(place.tokens) |
150 | # node label | 171 | # node label |
... | @@ -167,12 +188,13 @@ def arc_attr (label, attr) : | ... | @@ -167,12 +188,13 @@ def arc_attr (label, attr) : |
167 | attr["arrowhead"] = "box" | 188 | attr["arrowhead"] = "box" |
168 | attr["label"] = " %s " % label._annotation | 189 | attr["label"] = " %s " % label._annotation |
169 | 190 | ||
170 | -def draw (net, engine, target) : | 191 | + |
192 | +def draw (net, target, engine="dot") : | ||
171 | try : | 193 | try : |
172 | - net.draw(target, engine=engine, | 194 | + return net.draw(target, engine=engine, |
173 | - place_attr=place_attr, | 195 | + place_attr=place_attr, |
174 | - trans_attr=trans_attr, | 196 | + trans_attr=trans_attr, |
175 | - arc_attr=arc_attr) | 197 | + arc_attr=arc_attr) |
176 | except : | 198 | except : |
177 | die(ERR_OUTPUT, str(sys.exc_info()[1])) | 199 | die(ERR_OUTPUT, str(sys.exc_info()[1])) |
178 | 200 | ||
... | @@ -193,7 +215,7 @@ def save_pnml (net, target) : | ... | @@ -193,7 +215,7 @@ def save_pnml (net, target) : |
193 | ## | 215 | ## |
194 | 216 | ||
195 | def main (args=sys.argv[1:], src=None) : | 217 | def main (args=sys.argv[1:], src=None) : |
196 | - global snk | 218 | + global options, snk |
197 | # get options | 219 | # get options |
198 | try: | 220 | try: |
199 | if src is None : | 221 | if src is None : |
... | @@ -232,7 +254,7 @@ def main (args=sys.argv[1:], src=None) : | ... | @@ -232,7 +254,7 @@ def main (args=sys.argv[1:], src=None) : |
232 | build = Builder(snk) | 254 | build = Builder(snk) |
233 | try : | 255 | try : |
234 | net = build.build(node) | 256 | net = build.build(node) |
235 | - net.label(srcfile=abcd) | 257 | + net.label(srcfile=abcd, snakes=snk) |
236 | except (CompilationError, DeclarationError) : | 258 | except (CompilationError, DeclarationError) : |
237 | die(ERR_COMPILE, str(sys.exc_info()[1])) | 259 | die(ERR_COMPILE, str(sys.exc_info()[1])) |
238 | except : | 260 | except : |
... | @@ -243,12 +265,24 @@ def main (args=sys.argv[1:], src=None) : | ... | @@ -243,12 +265,24 @@ def main (args=sys.argv[1:], src=None) : |
243 | for engine in gv_engines : | 265 | for engine in gv_engines : |
244 | target = getattr(options, "gv%s" % engine) | 266 | target = getattr(options, "gv%s" % engine) |
245 | if target : | 267 | if target : |
246 | - draw(net, engine, target) | 268 | + draw(net, target, engine) |
269 | + if options.html : | ||
270 | + try : | ||
271 | + html(abcd, node, net, draw(net, None), options.html) | ||
272 | + except : | ||
273 | + bug() | ||
247 | trace, lineno = [], None | 274 | trace, lineno = [], None |
248 | if options.check : | 275 | if options.check : |
249 | lineno, trace = Checker(net).run() | 276 | lineno, trace = Checker(net).run() |
250 | if options.simul : | 277 | if options.simul : |
251 | - Simulator(snk, source, net, trace, lineno).run() | 278 | + try : |
279 | + simul = Simulator(abcd, node, net, draw(net, None)) | ||
280 | + except : | ||
281 | + bug() | ||
282 | + simul.start() | ||
283 | + if not options.headless : | ||
284 | + webbrowser.open(simul.url) | ||
285 | + simul.wait() | ||
252 | elif trace : | 286 | elif trace : |
253 | if lineno is None : | 287 | if lineno is None : |
254 | print("unsafe execution:") | 288 | print("unsafe execution:") | ... | ... |
snakes/utils/abcd/resources/about.html
0 → 100644
This diff is collapsed. Click to expand it.
snakes/utils/simul/__init__.py
0 → 100644
1 | +import snakes | ||
2 | +from snakes.utils.simul.httpd import * | ||
3 | +from snakes.utils.simul.html import H | ||
4 | +import multiprocessing, time, sys, json, os.path, signal, inspect, glob | ||
5 | +import operator | ||
6 | + | ||
7 | +class StateSpace (dict) : | ||
8 | + def __init__ (self, net) : | ||
9 | + self.net = net | ||
10 | + self.current = self.add(net.get_marking()) | ||
11 | + def get (self) : | ||
12 | + return self[self.current] | ||
13 | + def add (self, marking) : | ||
14 | + if marking in self : | ||
15 | + return self[marking].num | ||
16 | + else : | ||
17 | + marking.num = len(self) / 2 | ||
18 | + self[marking] = self[marking.num] = marking | ||
19 | + self.setmodes(marking.num) | ||
20 | + return marking.num | ||
21 | + def setmodes (self, state) : | ||
22 | + marking = self[state] | ||
23 | + self.net.set_marking(marking) | ||
24 | + marking.modes = [] | ||
25 | + for trans in self.net.transition() : | ||
26 | + for mode in trans.modes() : | ||
27 | + marking.modes.append((trans, mode)) | ||
28 | + def succ (self, state, mode) : | ||
29 | + marking = self[state] | ||
30 | + trans, binding = marking.modes[mode] | ||
31 | + self.net.set_marking(marking) | ||
32 | + trans.fire(binding) | ||
33 | + self.current = self.add(self.net.get_marking()) | ||
34 | + return self.current | ||
35 | + def modes (self, state) : | ||
36 | + return self[state].modes | ||
37 | + | ||
38 | +def log (message) : | ||
39 | + sys.stderr.write("[simulator] %s\n" % message.strip()) | ||
40 | + sys.stderr.flush() | ||
41 | + | ||
42 | +shutdown = multiprocessing.Event() | ||
43 | +ping = multiprocessing.Event() | ||
44 | + | ||
45 | +class Server (multiprocessing.Process) : | ||
46 | + def __init__ (self, httpd) : | ||
47 | + multiprocessing.Process.__init__(self) | ||
48 | + self.httpd = httpd | ||
49 | + def run (self) : | ||
50 | + try : | ||
51 | + self.httpd.serve_forever() | ||
52 | + except KeyboardInterrupt : | ||
53 | + pass | ||
54 | + finally : | ||
55 | + shutdown.set() | ||
56 | + | ||
57 | +class WatchDog (multiprocessing.Process) : | ||
58 | + def __init__ (self, timeout=30) : | ||
59 | + multiprocessing.Process.__init__(self) | ||
60 | + self.timeout = timeout | ||
61 | + def run (self) : | ||
62 | + try : | ||
63 | + while True : | ||
64 | + if ping.wait(self.timeout) : | ||
65 | + ping.clear() | ||
66 | + else : | ||
67 | + log("client not active - %s\n" | ||
68 | + % time.strftime("%d/%b/%Y %H:%M:%S")) | ||
69 | + break | ||
70 | + except KeyboardInterrupt : | ||
71 | + pass | ||
72 | + finally : | ||
73 | + shutdown.set() | ||
74 | + | ||
75 | +class BaseHTTPSimulator (Node) : | ||
76 | + def __init__ (self, net, port=8000, resources=[]) : | ||
77 | + self.res = {} | ||
78 | + for cls in reversed(inspect.getmro(self.__class__)[:-2]) : | ||
79 | + path = os.path.dirname(inspect.getsourcefile(cls)) | ||
80 | + for pattern in resources + ["resources/*.js", | ||
81 | + "resources/*.css", | ||
82 | + "resources/*.html", | ||
83 | + "resources/alive.txt"] : | ||
84 | + for res in glob.glob(os.path.join(path, pattern)) : | ||
85 | + with open(res) as infile : | ||
86 | + self.res[os.path.basename(res)] = infile.read() | ||
87 | + Node.__init__(self, r=ResourceNode(self.res)) | ||
88 | + # create HTTP server | ||
89 | + self.port = port | ||
90 | + while True : | ||
91 | + try : | ||
92 | + httpd = HTTPServer(('', self.port), self) | ||
93 | + except Exception as err : | ||
94 | + self.port += 1 | ||
95 | + else : | ||
96 | + break | ||
97 | + self.server = Server(httpd) | ||
98 | + self.watchdog = WatchDog() | ||
99 | + # init data | ||
100 | + self.url = "http://127.0.0.1:%s/%s/" % (port, httpd.key) | ||
101 | + self._alive = self.res["alive.txt"].splitlines() | ||
102 | + self._ping = 0 | ||
103 | + self.states = StateSpace(net) | ||
104 | + def start (self) : | ||
105 | + log("starting at %r" % self.url) | ||
106 | + shutdown.clear() | ||
107 | + ping.clear() | ||
108 | + self.server.start() | ||
109 | + self.watchdog.start() | ||
110 | + def wait (self) : | ||
111 | + try : | ||
112 | + shutdown.wait() | ||
113 | + log("preparing to shut down...") | ||
114 | + time.sleep(2) | ||
115 | + except KeyboardInterrupt : | ||
116 | + shutdown.set() | ||
117 | + log("shuting down...") | ||
118 | + sig = getattr(signal, "CTRL_C_EVENT", | ||
119 | + getattr(signal, "SIGTERM", None)) | ||
120 | + if sig is not None : | ||
121 | + if self.server.pid : | ||
122 | + os.kill(self.server.pid, sig) | ||
123 | + if self.watchdog.pid : | ||
124 | + os.kill(self.watchdog.pid, sig) | ||
125 | + log("bye!") | ||
126 | + def getstate (self, state) : | ||
127 | + marking = self.states[state] | ||
128 | + places = ["%s = %s" % (H.span(place.name, class_="place"), | ||
129 | + H.span(marking(place.name), class_="token")) | ||
130 | + for place in sorted(self.states.net.place(), | ||
131 | + key=operator.attrgetter("name"))] | ||
132 | + modes = ["%s : %s" % (H.span(trans.name, class_="trans"), | ||
133 | + H.span(binding, class_="binding")) | ||
134 | + for trans, binding in marking.modes] | ||
135 | + return {"id" : state, | ||
136 | + "states" : [{"do" : "sethtml", | ||
137 | + "select" : "#net", | ||
138 | + "html" : H.i(self.states.net)}, | ||
139 | + {"do" : "settext", | ||
140 | + "select" : "#state", | ||
141 | + "text" : state}, | ||
142 | + {"do" : "setlist", | ||
143 | + "select" : "#marking", | ||
144 | + "items" : places}, | ||
145 | + ], | ||
146 | + "modes" : [{"select" : "#modes", | ||
147 | + "items" : modes}, | ||
148 | + ], | ||
149 | + } | ||
150 | + def init_index (self) : | ||
151 | + return {"res" : "%s/r" % self.url, | ||
152 | + "url" : self.url, | ||
153 | + "key" : self.server.httpd.key, | ||
154 | + "host" : "127.0.0.1", | ||
155 | + "port" : self.port, | ||
156 | + "about" : self.init_about(), | ||
157 | + "model" : self.init_model()} | ||
158 | + def init_model (self) : | ||
159 | + return self.res["model.html"] | ||
160 | + def init_about (self) : | ||
161 | + return self.res["about.html"] | ||
162 | + @http("text/html") | ||
163 | + def __call__ (self) : | ||
164 | + return self.res["index.html"] % self.init_index() | ||
165 | + def init_ui (self) : | ||
166 | + argv = H.code(" ".join(sys.argv)) | ||
167 | + version = (H.ul(H.li(H.b("Python: "), | ||
168 | + H.br.join(sys.version.splitlines())), | ||
169 | + H.li(H.b("SNAKES: "), snakes.version))) | ||
170 | + return [{"label" : "Versions", | ||
171 | + "id" : "ui-version", | ||
172 | + "href" : "#", | ||
173 | + "script" : "dialog(%r)" % version}, | ||
174 | + {"label" : "Argv", | ||
175 | + "id" : "ui-argv", | ||
176 | + "href" : "#", | ||
177 | + "script" : "dialog(%r)" % argv}] | ||
178 | + def init_help (self) : | ||
179 | + return {"#trace": "the states and transitions explored so far", | ||
180 | + "#model" : "the model being simulated", | ||
181 | + "#alive .ui #ui-quit" : "stop the simulator (server side)", | ||
182 | + "#alive .ui #ui-help" : "show this help", | ||
183 | + "#alive .ui #ui-about" : "show information about the simulator"} | ||
184 | + @http("application/json", state=int) | ||
185 | + def init (self, state=-1) : | ||
186 | + if state < 0 : | ||
187 | + state = self.states.current | ||
188 | + return {"ui" : self.init_ui(), | ||
189 | + "state" : self.getstate(state), | ||
190 | + "help" : self.init_help()} | ||
191 | + @http("application/json", state=int, mode=int) | ||
192 | + def succ (self, state, mode) : | ||
193 | + state = self.states.succ(state, mode) | ||
194 | + return self.getstate(state) | ||
195 | + @http("text/plain") | ||
196 | + def ping (self) : | ||
197 | + ping.set() | ||
198 | + alive = self._alive[self._ping % len(self._alive)] | ||
199 | + self._ping += 1 | ||
200 | + return alive | ||
201 | + @http("text/plain") | ||
202 | + def quit (self) : | ||
203 | + shutdown.set() | ||
204 | + return "Bye!" | ||
205 | + | ||
206 | +if __name__ == "__main__" : | ||
207 | + import snakes.nets, webbrowser | ||
208 | + net = snakes.nets.loads(sys.argv[1]) | ||
209 | + simul = BaseHTTPSimulator(net) | ||
210 | + simul.start() | ||
211 | + webbrowser.open(simul.url) | ||
212 | + simul.wait() |
snakes/utils/simul/html.py
0 → 100644
1 | +import functools | ||
2 | +import json as JSON | ||
3 | + | ||
4 | +def _tag (name) : | ||
5 | + return name.lower().strip("_") | ||
6 | + | ||
7 | +class Tag (object) : | ||
8 | + default = {"a" : {"href" : "#"}} | ||
9 | + noclose = set([None, "br"]) | ||
10 | + def __init__ (self, name, *children, **attr) : | ||
11 | + if name is not None : | ||
12 | + self.name = name.lower() | ||
13 | + else : | ||
14 | + self.name = None | ||
15 | + self.attr = dict(self._cleanup(attr)) | ||
16 | + self.children = list(children) | ||
17 | + def _cleanup (self, attr) : | ||
18 | + for key, value in attr.items() : | ||
19 | + yield _tag(key), value | ||
20 | + def __call__ (self, *children, **attr) : | ||
21 | + self.children.extend(children) | ||
22 | + self.attr.update(self._cleanup(attr)) | ||
23 | + return self | ||
24 | + def add (self, *children) : | ||
25 | + self.children.extend(children) | ||
26 | + return self | ||
27 | + def join (self, children) : | ||
28 | + if self.name not in self.noclose : | ||
29 | + raise ValueError("cannot join with tag %r" % self.name) | ||
30 | + lst = [children[0]] | ||
31 | + for c in children[1:] : | ||
32 | + lst.extend([self, c]) | ||
33 | + return self.__class__(None, *lst) | ||
34 | + def __setitem__ (self, key, value) : | ||
35 | + self(**{_tag(key): value}) | ||
36 | + def __delitem__ (self, key) : | ||
37 | + self.attr.pop(_tag(key), None) | ||
38 | + def __contains__ (self, key) : | ||
39 | + return _tag(key) in self.attr | ||
40 | + def __str__ (self) : | ||
41 | + attr = self.attr.copy() | ||
42 | + for key, value in self.default.get(self.name, {}).items() : | ||
43 | + if key not in attr : | ||
44 | + attr[key] = value | ||
45 | + if self.name is None : | ||
46 | + return "".join(str(c) for c in self.children) | ||
47 | + elif attr : | ||
48 | + ret = "<%s %s>%s" % ( | ||
49 | + self.name, | ||
50 | + " ".join("%s=%r" % a for a in attr.items()), | ||
51 | + "".join(str(c) for c in self.children)) | ||
52 | + else : | ||
53 | + ret = "<%s>%s" % ( | ||
54 | + self.name, | ||
55 | + "".join(str(c) for c in self.children)) | ||
56 | + if self.children or self.name not in self.noclose : | ||
57 | + return ret + "</%s>" % self.name | ||
58 | + else : | ||
59 | + return ret[:-1] + "/>" | ||
60 | + def __repr__ (self) : | ||
61 | + return repr(str(self)) | ||
62 | + | ||
63 | +class Factory (object) : | ||
64 | + def __getattr__ (self, name) : | ||
65 | + return Tag(_tag(name)) | ||
66 | + | ||
67 | +H = Factory() | ||
68 | + | ||
69 | +class JSONEncoder(JSON.JSONEncoder): | ||
70 | + def default(self, obj): | ||
71 | + if isinstance(obj, Tag): | ||
72 | + return str(obj) | ||
73 | + else : | ||
74 | + return JSON.JSONEncoder.default(self, obj) | ||
75 | + | ||
76 | +json = JSONEncoder().encode |
snakes/utils/simul/httpd.py
0 → 100644
1 | +import sys, os.path, httplib, cgi, urlparse, functools, mimetypes | ||
2 | +import os, signal, traceback, random, base64, inspect | ||
3 | +import BaseHTTPServer | ||
4 | +from snakes.utils.simul.html import json | ||
5 | + | ||
6 | +## | ||
7 | +## | ||
8 | +## | ||
9 | + | ||
10 | +class HTTPError (Exception) : | ||
11 | + def __init__ (self, code, reason=None, debug=None, headers={}) : | ||
12 | + self.answer = httplib.responses[code] | ||
13 | + if reason is None : | ||
14 | + message = self.answer | ||
15 | + else : | ||
16 | + message = "%s (%s)" % (self.answer, reason) | ||
17 | + Exception.__init__(self, message) | ||
18 | + self.code = code | ||
19 | + self.reason = reason | ||
20 | + self.debug = debug | ||
21 | + self.headers = headers | ||
22 | + | ||
23 | +## | ||
24 | +## | ||
25 | +## | ||
26 | + | ||
27 | +encoders = {"application/json" : json, | ||
28 | + "text/plain" : str, | ||
29 | + "text/html" : str, | ||
30 | + } | ||
31 | + | ||
32 | +def http (content_type=None, **types) : | ||
33 | + def decorator (method) : | ||
34 | + @functools.wraps(method) | ||
35 | + def wrapper (*larg, **karg) : | ||
36 | + try : | ||
37 | + args = inspect.getcallargs(method, *larg, **karg) | ||
38 | + for a, t in types.items() : | ||
39 | + if a in args : | ||
40 | + args[a] = t(args[a]) | ||
41 | + except : | ||
42 | + raise HTTPError(httplib.BAD_REQUEST, "invalid arguments") | ||
43 | + try : | ||
44 | + if content_type is None : | ||
45 | + return method(**args) | ||
46 | + else : | ||
47 | + encode = encoders.get(content_type, str) | ||
48 | + return content_type, encode(method(**args)) | ||
49 | + except HTTPError : | ||
50 | + raise | ||
51 | + except : | ||
52 | + raise HTTPError(httplib.INTERNAL_SERVER_ERROR, | ||
53 | + debug=sys.exc_info()) | ||
54 | + return wrapper | ||
55 | + return decorator | ||
56 | + | ||
57 | +class Node (object) : | ||
58 | + def __init__ (self, **children) : | ||
59 | + for child, node in children.items() : | ||
60 | + setattr(self, child, node) | ||
61 | + def __getitem__ (self, path) : | ||
62 | + path = path.strip("/") | ||
63 | + if "/" in path : | ||
64 | + head, tail = path.split("/", 1) | ||
65 | + child = getattr(self, head, None) | ||
66 | + if isinstance(child, Node) : | ||
67 | + return child[tail] | ||
68 | + else : | ||
69 | + raise KeyError(tail) | ||
70 | + elif path == "" and hasattr(self, "__call__") : | ||
71 | + return self.__call__ | ||
72 | + elif hasattr(self, path) : | ||
73 | + return getattr(self, path) | ||
74 | + else : | ||
75 | + raise KeyError(path) | ||
76 | + | ||
77 | +class DirNode (Node) : | ||
78 | + def __init__ (self, path) : | ||
79 | + self.root = os.path.realpath(path) | ||
80 | + def __getitem__ (self, path) : | ||
81 | + path = os.path.join(self.root, path.lstrip("./")) | ||
82 | + if not os.path.isfile(path) : | ||
83 | + raise HTTPError(httplib.NOT_FOUND) | ||
84 | + ct = mimetypes.guess_type(path)[0] or "application/octet-stream" | ||
85 | + @http(ct) | ||
86 | + def handler () : | ||
87 | + return open(path).read() | ||
88 | + return handler | ||
89 | + | ||
90 | +class ResourceNode (Node) : | ||
91 | + def __init__ (self, data) : | ||
92 | + self.data = data | ||
93 | + self.ct = dict((path, mimetypes.guess_type(path)[0] | ||
94 | + or "application/octet-stream") | ||
95 | + for path in self.data) | ||
96 | + def __getitem__ (self, path) : | ||
97 | + if path in self.data : | ||
98 | + @http(self.ct[path]) | ||
99 | + def handler () : | ||
100 | + return self.data[path] | ||
101 | + return handler | ||
102 | + else : | ||
103 | + raise HTTPError(httplib.NOT_FOUND) | ||
104 | + | ||
105 | +## | ||
106 | +## | ||
107 | +## | ||
108 | + | ||
109 | +class HTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler) : | ||
110 | + def do_GET (self) : | ||
111 | + try : | ||
112 | + try : | ||
113 | + url = urlparse.urlparse(self.path) | ||
114 | + except : | ||
115 | + raise HTTPError(httplib.BAD_REQUEST, "invalid URL") | ||
116 | + try : | ||
117 | + handler = self.server[url.path] | ||
118 | + except KeyError : | ||
119 | + raise HTTPError(httplib.NOT_FOUND) | ||
120 | + try : | ||
121 | + query = dict(cgi.parse_qsl(url.query)) | ||
122 | + # jQuery may add _ in query for cache control, let's drop it | ||
123 | + query.pop("_", None) | ||
124 | + except : | ||
125 | + raise HTTPError(httplib.BAD_REQUEST, "invalid query") | ||
126 | + content_type, data = handler(**query) | ||
127 | + self.send_response(httplib.OK) | ||
128 | + self.send_header("Content-type", content_type) | ||
129 | + self.send_header("Content-length", len(data)) | ||
130 | + self.end_headers() | ||
131 | + self.wfile.write(data) | ||
132 | + except HTTPError : | ||
133 | + c, v, t = sys.exc_info() | ||
134 | + self.send_response(v.code) | ||
135 | + self.send_header("Content-type", "text/html") | ||
136 | + for hname, hdata in v.headers.iteritems() : | ||
137 | + self.send_header(hname, hdata) | ||
138 | + self.end_headers() | ||
139 | + self.wfile.write("<html><title>%s</title></head>" | ||
140 | + "<body><p>%s</p></body>" % (v.answer, v.message)) | ||
141 | + if v.code == 500 : | ||
142 | + traceback.print_exception(*v.debug) | ||
143 | + | ||
144 | +class HTTPServer (BaseHTTPServer.HTTPServer): | ||
145 | + def __init__ (self, server_address, root): | ||
146 | + BaseHTTPServer.HTTPServer.__init__(self, server_address, | ||
147 | + HTTPRequestHandler) | ||
148 | + self.root = root | ||
149 | + self.key = "".join(base64.b64encode( | ||
150 | + "".join(chr(random.getrandbits(8)) for i in range(15)), | ||
151 | + "-_").split()) | ||
152 | + def __getitem__ (self, path) : | ||
153 | + try : | ||
154 | + key, path = path.lstrip("/").split("/", 1) | ||
155 | + except : | ||
156 | + raise HTTPError(httplib.FORBIDDEN) | ||
157 | + if key != self.key : | ||
158 | + raise HTTPError(httplib.FORBIDDEN) | ||
159 | + return self.root[path] | ||
160 | + | ||
161 | +## | ||
162 | +## | ||
163 | +## | ||
164 | + | ||
165 | +if __name__ == '__main__': | ||
166 | + class HelloNode (Node) : | ||
167 | + @http("text/plain") | ||
168 | + def hello (self, first, last) : | ||
169 | + return "Hello %s %s!" % (first.capitalize(), last.capitalize()) | ||
170 | + try : | ||
171 | + httpd = HTTPServer(('', 1234), HelloNode(r=DirNode("."))) | ||
172 | + httpd.serve_forever() | ||
173 | + except KeyboardInterrupt : | ||
174 | + print "\rGoobye" |
snakes/utils/simul/resources/about.css
0 → 100644
File mode changed
snakes/utils/simul/resources/about.html
0 → 100644
snakes/utils/simul/resources/alive.txt
0 → 100644
1 | +Well, you can tell by the way I use my walk, | ||
2 | +I'm a woman's man: no time to talk. | ||
3 | +Music loud and women warm, I've been kicked around | ||
4 | +Since I was born. | ||
5 | +And now it's all right. it's ok. | ||
6 | +And you may look the other way. | ||
7 | +We can try to understand | ||
8 | +The new york times effect on man. | ||
9 | +Whether you're a brother or whether you're a mother, | ||
10 | +You're stayin alive, stayin alive. | ||
11 | +Feel the city breakin and everybody shakin, | ||
12 | +And were stayin alive, stayin alive. | ||
13 | +Ah, ha, ha, ha, stayin alive, stayin alive. | ||
14 | +Ah, ha, ha, ha, stayin alive. | ||
15 | +Well now, I get low and I get high, | ||
16 | +And if I can't get either, I really try. | ||
17 | +Got the wings of heaven on my shoes. | ||
18 | +I'm a dancin man and I just can't lose. | ||
19 | +You know it's all right.its ok. | ||
20 | +I'll live to see another day. | ||
21 | +We can try to understand | ||
22 | +The new york times effect on man. | ||
23 | +Whether you're a brother or whether you're a mother, | ||
24 | +You're stayin alive, stayin alive. | ||
25 | +Feel the city breakin and everybody shakin, | ||
26 | +And were stayin alive, stayin alive. | ||
27 | +Ah, ha, ha, ha, stayin alive, stayin alive. | ||
28 | +Ah, ha, ha, ha, stayin alive. | ||
29 | +Life goin nowhere.somebody help me. | ||
30 | +Somebody help me, yeah. | ||
31 | +Life goin nowhere.somebody help me. | ||
32 | +Somebody help me, yeah. stayin alive. | ||
33 | +Well, you can tell by the way I use my walk, | ||
34 | +I'm a woman's man: no time to talk. | ||
35 | +Music loud and women warm, | ||
36 | +I've been kicked around since I was born. | ||
37 | +And now it's all right. it's ok. | ||
38 | +And you may look the other way. | ||
39 | +We can try to understand | ||
40 | +The new york times effect on man. | ||
41 | +Whether you're a brother or whether you're a mother, | ||
42 | +You're stayin alive, stayin alive. | ||
43 | +Feel the city breakin and everybody shakin, | ||
44 | +And were stayin alive, stayin alive. | ||
45 | +Ah, ha, ha, ha, stayin alive, stayin alive. | ||
46 | +Ah, ha, ha, ha, stayin alive. | ||
47 | +Life goin nowhere.somebody help me. | ||
48 | +Somebody help me, yeah. | ||
49 | +Life goin nowhere.somebody help me, yeah. | ||
50 | +I'm stayin alive. |
snakes/utils/simul/resources/index.html
0 → 100644
1 | +<html> | ||
2 | + <head> | ||
3 | + <script type="text/javascript"> | ||
4 | + var server = {key : "%(key)s", | ||
5 | + url : "%(url)s", | ||
6 | + host : "%(host)s", | ||
7 | + port : "%(port)s"}; | ||
8 | + </script> | ||
9 | + <script type="text/javascript" src="r/jquery.min.js"></script> | ||
10 | + <script type="text/javascript" src="r/jquery.periodic.js"></script> | ||
11 | + <script type="text/javascript" src="r/simulator.js"></script> | ||
12 | + <link type="text/css" href="r/simulator.css" rel="stylesheet"/> | ||
13 | + <link type="text/css" href="r/model.css" rel="stylesheet"/> | ||
14 | + <link type="text/css" href="r/trace.css" rel="stylesheet"/> | ||
15 | + <link type="text/css" href="r/about.css" rel="stylesheet"/> | ||
16 | + </head> | ||
17 | + <body> | ||
18 | + <div id="alive"> | ||
19 | + <ul class="ui"> | ||
20 | + <li><a id="ui-reset" href="#">Reset simulation</a></li> | ||
21 | + <li><a id="ui-quit" href="#">Stop server</a></li> | ||
22 | + <li><a id="ui-help" href="#">Help</a></li> | ||
23 | + <li><a id="ui-about" href="#">About</a></li> | ||
24 | + </ul> | ||
25 | + <span class="ping">Starting...</span> | ||
26 | + </div> | ||
27 | + <div id="model">%(model)s</div> | ||
28 | + <div id="trace"></div> | ||
29 | + <div id="about">%(about)s</div> | ||
30 | + </body> | ||
31 | +</html> |
snakes/utils/simul/resources/jquery.min.js
0 → 100644
This diff is collapsed. Click to expand it.
1 | +/*! | ||
2 | + * jQuery periodic plugin | ||
3 | + * | ||
4 | + * Copyright 2010, Tom Anderson | ||
5 | + * Dual licensed under the MIT or GPL Version 2 licenses. | ||
6 | + * | ||
7 | + */ | ||
8 | + | ||
9 | +jQuery.periodic = function (options, callback) { | ||
10 | + | ||
11 | + // if the first argument is a function then assume the options aren't being passed | ||
12 | + if (jQuery.isFunction(options)) { | ||
13 | + callback = options; | ||
14 | + options = {}; | ||
15 | + } | ||
16 | + | ||
17 | + // Merge passed settings with default values | ||
18 | + var settings = jQuery.extend({}, jQuery.periodic.defaults, { | ||
19 | + ajax_complete : ajaxComplete, | ||
20 | + increment : increment, | ||
21 | + reset : reset, | ||
22 | + cancel : cancel | ||
23 | + }, options); | ||
24 | + | ||
25 | + // bookkeeping variables | ||
26 | + settings.cur_period = settings.period; | ||
27 | + settings.tid = false; | ||
28 | + var prev_ajax_response = ''; | ||
29 | + | ||
30 | + run(); | ||
31 | + | ||
32 | + // return settings so user can tweak them externally | ||
33 | + return settings; | ||
34 | + | ||
35 | + // run (or restart if already running) the looping construct | ||
36 | + function run() { | ||
37 | + // clear/stop existing timer (multiple calls to run() won't result in multiple timers) | ||
38 | + cancel(); | ||
39 | + // let it rip! | ||
40 | + settings.tid = setTimeout(function() { | ||
41 | + // set the context (this) for the callback to the settings object | ||
42 | + callback.call(settings); | ||
43 | + | ||
44 | + // compute the next value for cur_period | ||
45 | + increment(); | ||
46 | + | ||
47 | + // queue up the next run | ||
48 | + if(settings.tid) | ||
49 | + run(); | ||
50 | + }, settings.cur_period); | ||
51 | + } | ||
52 | + | ||
53 | + // utility function for use with ajax calls | ||
54 | + function ajaxComplete(xhr, status) { | ||
55 | + if (status === 'success' && prev_ajax_response !== xhr.responseText) { | ||
56 | + // reset the period whenever the response changes | ||
57 | + prev_ajax_response = xhr.responseText; | ||
58 | + reset(); | ||
59 | + } | ||
60 | + } | ||
61 | + | ||
62 | + // compute the next delay | ||
63 | + function increment() { | ||
64 | + settings.cur_period *= settings.decay; | ||
65 | + if (settings.cur_period < settings.period) { | ||
66 | + // don't let it drop below the minimum | ||
67 | + reset(); | ||
68 | + } else if (settings.cur_period > settings.max_period) { | ||
69 | + settings.cur_period = settings.max_period; | ||
70 | + if (settings.on_max !== undefined) { | ||
71 | + // call the user-supplied callback if we reach max_period | ||
72 | + settings.on_max.call(settings); | ||
73 | + } | ||
74 | + } | ||
75 | + } | ||
76 | + | ||
77 | + function reset() { | ||
78 | + settings.cur_period = settings.period; | ||
79 | + // restart with the new timeout | ||
80 | + run(); | ||
81 | + } | ||
82 | + | ||
83 | + function cancel() { | ||
84 | + clearTimeout(settings.tid); | ||
85 | + settings.tid = null; | ||
86 | + } | ||
87 | + | ||
88 | + // other functions we might want to implement | ||
89 | + function pause() {} | ||
90 | + function resume() {} | ||
91 | + function log() {} | ||
92 | +}; | ||
93 | + | ||
94 | +jQuery.periodic.defaults = { | ||
95 | + period : 4000, // 4 sec. | ||
96 | + max_period : 1800000, // 30 min. | ||
97 | + decay : 1.5, // time period multiplier | ||
98 | + on_max : undefined // called if max_period is reached | ||
99 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
snakes/utils/simul/resources/model.css
0 → 100644
snakes/utils/simul/resources/model.html
0 → 100644
snakes/utils/simul/resources/simulator.css
0 → 100644
1 | +body { | ||
2 | + font-family: sans-serif; | ||
3 | +} | ||
4 | + | ||
5 | +#alive { | ||
6 | + border: solid 1px #AAA; | ||
7 | + border-radius: 5px; | ||
8 | + padding: 5px 10px; | ||
9 | + margin: 5px; | ||
10 | + background-color: #DDD; | ||
11 | + overflow:auto; | ||
12 | +} | ||
13 | + | ||
14 | +#alive .ui { | ||
15 | + display: inline; | ||
16 | + list-style: none; | ||
17 | + margin: 0px; | ||
18 | + padding: 0px; | ||
19 | +} | ||
20 | + | ||
21 | +#alive .ui li { | ||
22 | + display: inline; | ||
23 | + border: solid 1px #AAA; | ||
24 | + padding: 5px 10px; | ||
25 | + margin: 0px 3px; | ||
26 | + background-color: #EEE; | ||
27 | +} | ||
28 | + | ||
29 | +#alive .ui a { | ||
30 | + text-decoration: none; | ||
31 | + color: #333; | ||
32 | +} | ||
33 | + | ||
34 | +#alive .ui li:hover { | ||
35 | + background-color: #FFF; | ||
36 | +} | ||
37 | + | ||
38 | +#alive .ui a:hover { | ||
39 | + color: #A33; | ||
40 | +} | ||
41 | + | ||
42 | +#alive .ping { | ||
43 | + color: #DDD; | ||
44 | + float: right; | ||
45 | +} | ||
46 | + | ||
47 | +#model { | ||
48 | + border: solid 1px #AAA; | ||
49 | + border-radius: 5px; | ||
50 | + padding: 5px 10px; | ||
51 | + margin: 5px; | ||
52 | + background-color: #EEE; | ||
53 | + overflow:auto; | ||
54 | +} | ||
55 | + | ||
56 | +#trace { | ||
57 | + border: solid 1px #AAA; | ||
58 | + border-radius: 5px; | ||
59 | + padding: 5px 10px; | ||
60 | + margin: 5px; | ||
61 | + background-color: #EEE; | ||
62 | + overflow:auto; | ||
63 | +} | ||
64 | + | ||
65 | +#about { | ||
66 | + display: none; | ||
67 | +} | ||
68 | + | ||
69 | +#dialog { | ||
70 | + display: none; | ||
71 | + position: fixed; | ||
72 | + z-index: 2; | ||
73 | + top: 10%; | ||
74 | + left: 20%; | ||
75 | + width: 60%; | ||
76 | + border: solid 2px #AAA; | ||
77 | + border-radius: 5px; | ||
78 | + padding: 20px; | ||
79 | + background-color: #FFF; | ||
80 | + overflow:auto; | ||
81 | +} | ||
82 | + | ||
83 | +#dialog-bg { | ||
84 | + display: none; | ||
85 | + position: fixed; | ||
86 | + z-index: 1; | ||
87 | + top: 0px; | ||
88 | + left: 0px; | ||
89 | + width: 100%; | ||
90 | + height: 100%; | ||
91 | + background-color: #000; | ||
92 | + margin: 0px; | ||
93 | +} | ||
94 | + | ||
95 | +#dialog-close { | ||
96 | + display: inline; | ||
97 | + float: right; | ||
98 | + border: solid 1px #AAA; | ||
99 | + padding: 5px 10px; | ||
100 | + margin: 0px 3px; | ||
101 | + background-color: #EEE; | ||
102 | + text-decoration: none; | ||
103 | + color: #333; | ||
104 | +} | ||
105 | + | ||
106 | +#dialog-close:hover { | ||
107 | + background-color: #FFF; | ||
108 | + color: #A33; | ||
109 | +} | ||
110 | + | ||
111 | +.dialog p { | ||
112 | + margin: 10px 20px 0px 20px; | ||
113 | +} | ||
114 | + | ||
115 | +.dialog .title { | ||
116 | + margin: 0px 20px 5px 20px; | ||
117 | + font-weight: bold; | ||
118 | + text-align: center; | ||
119 | +} | ||
120 | + | ||
121 | +.dialog .subtitle { | ||
122 | + margin: 0px 20px 5px 20px; | ||
123 | + text-align: center; | ||
124 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
snakes/utils/simul/resources/simulator.js
0 → 100644
1 | +function addcss (css) { | ||
2 | + $("head").append("<style type='text/css'>" + css + "</style>"); | ||
3 | +} | ||
4 | + | ||
5 | +function addjs (js) { | ||
6 | + $("head").append("<script type='text/javascript'>" + js + "</script>"); | ||
7 | +} | ||
8 | + | ||
9 | +function dialog (content) { | ||
10 | + $("#dialog").html(content); | ||
11 | + $("#dialog").append("<a href='#' id='dialog-close'>close</a>"); | ||
12 | + $("#dialog-close").click(function() { | ||
13 | + $("#dialog-bg").hide(); | ||
14 | + $("#dialog").hide(); | ||
15 | + }); | ||
16 | + $("#dialog").fadeTo(50, 1); | ||
17 | + $("#dialog-bg").fadeTo(50, 0.5); | ||
18 | +} | ||
19 | + | ||
20 | +var update = { | ||
21 | + settext : function (action) { | ||
22 | + $(action.select).text(action.text); | ||
23 | + }, | ||
24 | + sethtml : function (action) { | ||
25 | + $(action.select).html(action.html); | ||
26 | + }, | ||
27 | + clear : function (action) { | ||
28 | + $(action.select).html(""); | ||
29 | + }, | ||
30 | + setlist : function (action) { | ||
31 | + ul = $(action.select); | ||
32 | + ul.html(""); | ||
33 | + $.each(action.items, function (num, item) { | ||
34 | + ul.append("<li>" + item + "</li>"); | ||
35 | + }); | ||
36 | + } | ||
37 | +} | ||
38 | + | ||
39 | +function setstate (state) { | ||
40 | + $.each(state.states, function (num, item) { | ||
41 | + update[item.do](item); | ||
42 | + }); | ||
43 | + $.each(state.modes, function (num, action) { | ||
44 | + ul = $(action.select); | ||
45 | + ul.html(""); | ||
46 | + $.each(action.items, function (pos, item) { | ||
47 | + ul.append("<li><a href='#' data-mode='" + state.id + ":" | ||
48 | + + pos + "''>" + item + "</a></li>"); | ||
49 | + a = ul.children().last().children().first(); | ||
50 | + a.click(function () { | ||
51 | + next = $(this).attr("data-mode").split(":"); | ||
52 | + text = $(this).text(); | ||
53 | + $("#trace").append("<div class='trace'><div class='state'>" | ||
54 | + + state.id + "</div><div class='mode'>" | ||
55 | + + text + "</div></div>"); | ||
56 | + $.get("succ", {state: next[0], mode: next[1]}, | ||
57 | + function (newstate) { | ||
58 | + setstate(newstate); | ||
59 | + }); | ||
60 | + }); | ||
61 | + }); | ||
62 | + }); | ||
63 | +} | ||
64 | + | ||
65 | +$(document).ready(function() { | ||
66 | + /* | ||
67 | + * ping server every 10 seconds | ||
68 | + */ | ||
69 | + $("#alive .ping").text("Stayin alive!"); | ||
70 | + $.periodic({period: 10000, decay:1, max_period: 10000}, function() { | ||
71 | + $.get("ping", function(data) { | ||
72 | + $("#alive .ping").text(data); | ||
73 | + }); | ||
74 | + }); | ||
75 | + /* | ||
76 | + * setup UI and model | ||
77 | + */ | ||
78 | + $.get("init", function(init) { | ||
79 | + /* bind reset button */ | ||
80 | + $("#ui-reset").click(function() { | ||
81 | + $.get("init", {state: 0}, function(data) { | ||
82 | + setstate(data.state); | ||
83 | + $("#trace").html(""); | ||
84 | + }); | ||
85 | + }); | ||
86 | + /* bind quit button */ | ||
87 | + $("#ui-quit").click(function() { | ||
88 | + $.get("quit", function(data) { | ||
89 | + $("#alive .ui").html("").text(data); | ||
90 | + }); | ||
91 | + }); | ||
92 | + /* dialogs */ | ||
93 | + $("body").append("<div id='dialog'>" | ||
94 | + + "</div><div id='dialog-bg'></div>"); | ||
95 | + /* build about */ | ||
96 | + $("#about").children().addClass("dialog"); | ||
97 | + $("#ui-about").click(function() { | ||
98 | + dialog($("#about").html()); | ||
99 | + }); | ||
100 | + /* extend menu */ | ||
101 | + if (init.ui != undefined) { | ||
102 | + ui = $("#alive .ui"); | ||
103 | + /* TODO: fix spacing between these buttons */ | ||
104 | + $.each(init.ui, function(num, menu) { | ||
105 | + ui.append("<li><a id='" + menu.id | ||
106 | + + "'href='" + menu.href + "'>" | ||
107 | + + menu.label + "</a></li>"); | ||
108 | + if (menu.script != undefined) { | ||
109 | + ui.children().last().click(function() { | ||
110 | + eval(menu.script); | ||
111 | + }); | ||
112 | + } | ||
113 | + }); | ||
114 | + } | ||
115 | + /* build help */ | ||
116 | + if (init.help != undefined) { | ||
117 | + $.each(init.help, function(key, text) { | ||
118 | + /* TODO: create help dic or whatever needed */ | ||
119 | + }); | ||
120 | + $("#ui-help").click(function() { | ||
121 | + /* TODO: show/hide tooltips for help */ | ||
122 | + }); | ||
123 | + } else { | ||
124 | + $("#ui-help").remove(); | ||
125 | + } | ||
126 | + /* setup initial state */ | ||
127 | + setstate(init.state); | ||
128 | + }); | ||
129 | +}); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
snakes/utils/simul/resources/trace.css
0 → 100644
-
Please register or login to post a comment