123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- '''
- TODO:
- V Realtime interrupts of the visualization need to be propagated to the simulation.
- V Change visualization to show in same browser window, not a pop-up.
- V Reset
- V Communication debugging interface -> visualization interface (for reset for example, or step back).
- V State visualization in debugging interface (on hover?).
- V Communication visualization interface -> debugging interface (for 'select an entity', for example).
- V As fast as possible simulation.
- V Visualization of "current small step" (1-8).
- V Breakpoints.
- V Inject events.
- V God events.
- V Fix bug: no visualization when small stepping.
- V Highlight transitioning nodes.
- V Display simulation time.
- V Realtime scale.
- V Termination condition.
- V Handling of multiple connections for a single output port when a message is routed.
- - Remove and add element/port/port connection. (? validity)
- - Proper serialization of state: now I only serialize primitive values, and ignore composite ones. Needs to have a translation between Python and JS.
- - Parameters for simulation initialization.
- - Robustness.
- - Each client has their own debugger?
- - Integrate with new version of SCCD (see Joeri's mail).
- '''
- import socket, sys, json, threading, time, pickle, copy, os
- os.chdir("NetLogo")
- sys.path.append(".")
- import simulator
- from sccd.runtime.statecharts_core import Event
- HOST, PORT = "127.0.0.1", 9999
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect((HOST, PORT))
- '''
- This function actually receives messages from the debugging interface (or instrumented visualizatin interface).
- The communication protocol is very basic: all messages are terminated with a new line. This function makes sure
- only 'full' messages are returned.
- '''
- end='\n'
- def recv_end(the_socket):
- total_data = []
- data = ''
- while True:
- data = the_socket.recv(8192)
- if end in data:
- total_data.append(data[:data.find(end)])
- break
- total_data.append(data)
- if len(total_data) > 1:
- last_pair = total_data[-2] + total_data[-1]
- if end in last_pair:
- total_data[-2]=last_pair[:last_pair.find(end)]
- total_data.pop()
- break
- return ''.join(total_data)
- '''
- Send a message to the communication layer (which will then be routed to either the debugging interface or the
- visualization server, based on the first entry in the sent array). Each message needs to end with a new line,
- as the protocol specifies.
- '''
- def send_msg(msg):
- try:
- sock.send(msg + '\n')
- except socket.error:
- sys.exit(0)
- '''
- Handle output messages of the simulation on the pos, color, and time ports, respectively.
- def handle_output_pos(msgs):
- for msg_contents in msgs:
- send_msg(json.dumps(["VIS", "POS_OUT", msg_contents]))
- def handle_output_color(msgs):
- for msg_contents in msgs:
- send_msg(json.dumps(["VIS", "COLOR_OUT", msg_contents]))
- def handle_output_time(msgs):
- for msg_contents in msgs:
- send_msg(json.dumps(["VIS", "TIME_OUT", "%.2f" % (float(msg_contents / 30.0))]))
- '''
- '''
- Serializes a Python object to a form where it is ready for JSON serialization.
- '''
- def todict(obj, classkey=None):
- if isinstance(obj, dict):
- data = {}
- for (k, v) in obj.items():
- if v.__class__.__name__.endswith("State") or not hasattr(v, '__dict__'): # TODO: UGLY!!!
- data[k] = todict(v, classkey)
- return data
- elif hasattr(obj, "__iter__"):
- return [todict(v, classkey) for v in copy.copy(obj) if (v.__class__.__name__.endswith("State") or not hasattr(v, '__dict__'))] # TODO: UGLY!!!
- elif hasattr(obj, "__dict__"):
- data = dict([(key, todict(value, classkey))
- for key, value in copy.copy(obj.__dict__).iteritems()
- if not callable(value) and not key.startswith('_') and (value.__class__.__name__.endswith("State") or not hasattr(value, '__dict__'))]) # TODO: UGLY!!!
- if classkey is not None and hasattr(obj, "__class__"):
- data[classkey] = obj.__class__.__name__
- return data
- else:
- return obj
- '''
- Blocks until a message is received on the specified port. Immediately sends that message (which needs to be
- forwarded to the debugging interface).
- '''
- def poll_port(port):
- while 1:
- msg = port.fetch(-1)
- #print msg
- send_msg(json.dumps(["DBG", todict(msg)]))
- def wait_for_dbg_msg():
- print 'Waiting for commands...'
-
- # Main loop: receive a message from the debugging interface (or the instrumented visualization server) and send it to the debugger.
- while 1:
- try:
- msg = recv_end(sock)
- except socket.error:
- break
- #print msg
- msg_head, msg_body = msg[:4], msg[4:]
- if msg_head == 'dbg_':
- decoded_event = json.loads(msg_body);
- parameters = decoded_event["parameters"]
- if decoded_event["name"] == "god_event":
- event = Event("god_event", "request", parameters)
- elif decoded_event["name"] == "add_breakpoint":
- event = Event("add_breakpoint", "request", parameters[0])
- elif decoded_event["name"] == "toggle_breakpoint":
- event = Event("toggle_breakpoint", "request", parameters)
- elif decoded_event["name"] == "del_breakpoint":
- event = Event("del_breakpoint", "request", parameters)
- elif decoded_event["name"] == "reset":
- event = Event("reset", "request", parameters)
- elif parameters[0] == "realtime":
- event = Event(parameters[0], "request", [{"realtime_scale": float(parameters[1]["realtime_scale"])}])
- elif parameters[0] == "big_step":
- event = Event("step", "request", [])
- elif parameters[0] == "simulate":
- event = Event("continuous", "request", [])
- else:
- event = Event(parameters[0], "request", [])
- sim_controller.addInput(event)
- '''
- elif msg_head == 'vis_':
- event = Event("realtime_interrupt", "request", [msg_body])
- sim_controller.addInput(event)
- '''
-
- if __name__ == '__main__':
- # Simulation parameters.
- '''
- resolution = (800, 600)
- framerate = 30
- spawn_chance = 0.2
- die_chance = 0.01
- # Instantiate model to simulate.
- model = Root(Canvas(resolution), framerate, spawn_chance, die_chance)
- '''
- # Instantiate and start our debugger (compiled Statecharts model).
- sim_controller = simulator.Controller('BouncingBalls.nlogo')
-
- # Forward debugging messages to debugging server (in a separate thread).
- reply_port = sim_controller.addOutputListener('reply')
- thrd = threading.Thread(target=poll_port,args=[reply_port])
- thrd.daemon = True
- thrd.start()
-
- # Listen for simulation output on specified DEVS ports.
- '''
- sim_controller.addInput(Event("set_listen_ports", "request", ["POS_OUT", handle_output_pos]))
- sim_controller.addInput(Event("set_listen_ports", "request", ["COLOR_OUT", handle_output_color]))
- sim_controller.addInput(Event("set_listen_ports", "request", ["TIME_OUT", handle_output_time]))
- '''
- thrd = threading.Thread(target=wait_for_dbg_msg)
- thrd.daemon = True
- thrd.start()
- sim_controller.start()
|