1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-14 15:32:29 +00:00
soil/server.py

223 lines
8.4 KiB
Python
Raw Permalink Normal View History

2018-02-05 11:47:56 +00:00
import io
import logging
import networkx as nx
2017-12-15 16:59:50 +00:00
import os
2018-02-05 11:47:56 +00:00
import threading
2017-12-15 16:59:50 +00:00
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.escape
import tornado.gen
import yaml
2018-02-05 11:47:56 +00:00
import webbrowser
2017-12-20 17:10:14 +00:00
from contextlib import contextmanager
2018-02-05 11:47:56 +00:00
from time import sleep
2018-04-12 16:00:44 +00:00
from xml.etree.ElementTree import tostring
2017-12-20 17:10:14 +00:00
2017-12-15 16:59:50 +00:00
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class PageHandler(tornado.web.RequestHandler):
2018-02-01 18:37:10 +00:00
""" Handler for the HTML template which holds the visualization. """
2017-12-15 16:59:50 +00:00
2018-02-01 18:37:10 +00:00
def get(self):
self.render('index.html', port=self.application.port,
2018-04-16 08:55:27 +00:00
name=self.application.name)
2017-12-15 16:59:50 +00:00
class SocketHandler(tornado.websocket.WebSocketHandler):
2018-02-05 11:47:56 +00:00
""" Handler for websocket. """
2017-12-15 16:59:50 +00:00
2018-02-01 18:37:10 +00:00
def open(self):
if self.application.verbose:
logger.info('Socket opened!')
def check_origin(self, origin):
return True
def on_message(self, message):
""" Receiving a message from the websocket, parse, and act accordingly. """
msg = tornado.escape.json_decode(message)
if msg['type'] == 'config_file':
if self.application.verbose:
print(msg['data'])
self.config = list(yaml.load_all(msg['data']))
if len(self.config) > 1:
error = 'Please, provide only one configuration.'
if self.application.verbose:
logger.error(error)
self.write_message({'type': 'error',
'error': error})
return
else:
self.config = self.config[0]
2018-04-16 08:55:27 +00:00
self.send_log('INFO.' + self.application.simulator.name, 'Using config: {name}'.format(name=self.config['name']))
2018-02-01 18:37:10 +00:00
2018-04-10 16:26:24 +00:00
if 'visualization_params' in self.config:
self.write_message({'type': 'visualization_params',
'data': self.config['visualization_params']})
2018-02-01 18:37:10 +00:00
self.name = self.config['name']
self.run_simulation()
settings = []
for key in self.config['environment_params']:
2018-04-11 17:40:59 +00:00
if type(self.config['environment_params'][key]) == float or type(self.config['environment_params'][key]) == int:
if self.config['environment_params'][key] <= 1:
setting_type = 'number'
else:
setting_type = 'great_number'
2018-02-01 18:37:10 +00:00
elif type(self.config['environment_params'][key]) == bool:
setting_type = 'boolean'
else:
setting_type = 'undefined'
settings.append({
'label': key,
'type': setting_type,
'value': self.config['environment_params'][key]
})
self.write_message({'type': 'settings',
'data': settings})
elif msg['type'] == 'get_trial':
if self.application.verbose:
logger.info('Trial {} requested!'.format(msg['data']))
2018-04-16 09:17:15 +00:00
self.send_log('INFO.' + __name__, 'Trial {} requested!'.format(msg['data']))
2018-02-01 18:37:10 +00:00
self.write_message({'type': 'get_trial',
2018-04-12 16:00:44 +00:00
'data': self.get_trial( int(msg['data']) ) })
2018-02-01 18:37:10 +00:00
elif msg['type'] == 'run_simulation':
2018-02-05 11:47:56 +00:00
if self.application.verbose:
logger.info('Running new simulation for {name}'.format(name=self.config['name']))
2018-04-16 08:55:27 +00:00
self.send_log('INFO.' + self.application.simulator.name, 'Running new simulation for {name}'.format(name=self.config['name']))
2018-02-01 18:37:10 +00:00
self.config['environment_params'] = msg['data']
self.run_simulation()
2018-04-12 16:00:44 +00:00
elif msg['type'] == 'download_gexf':
G = self.simulation[ int(msg['data']) ].history_to_graph()
for node in G.nodes():
if 'pos' in G.node[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos'])
writer = nx.readwrite.gexf.GEXFWriter(version='1.2draft')
writer.add_graph(G)
self.write_message({'type': 'download_gexf',
'filename': self.config['name'] + '_trial_' + str(msg['data']),
'data': tostring(writer.xml).decode(writer.encoding) })
elif msg['type'] == 'download_json':
G = self.simulation[ int(msg['data']) ].history_to_graph()
for node in G.nodes():
if 'pos' in G.node[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos'])
self.write_message({'type': 'download_json',
'filename': self.config['name'] + '_trial_' + str(msg['data']),
'data': nx.node_link_data(G) })
2018-02-01 18:37:10 +00:00
else:
if self.application.verbose:
logger.info('Unexpected message!')
def update_logging(self):
try:
if (not self.log_capture_string.closed and self.log_capture_string.getvalue()):
2018-02-02 15:12:43 +00:00
for i in range(len(self.log_capture_string.getvalue().split('\n')) - 1):
2018-04-16 08:55:27 +00:00
self.send_log('INFO.' + self.application.simulator.name, self.log_capture_string.getvalue().split('\n')[i])
2018-02-01 18:37:10 +00:00
self.log_capture_string.truncate(0)
self.log_capture_string.seek(0)
finally:
if self.capture_logging:
2018-02-02 15:12:43 +00:00
thread = threading.Timer(0.01, self.update_logging)
2018-02-01 18:37:10 +00:00
thread.start()
def on_close(self):
2018-02-05 11:47:56 +00:00
if self.application.verbose:
logger.info('Socket closed!')
2018-02-01 18:37:10 +00:00
def send_log(self, logger, logging):
self.write_message({'type': 'log',
'logger': logger,
'logging': logging })
def run_simulation(self):
# Run simulation and capture logs
2018-04-10 16:26:24 +00:00
if 'visualization_params' in self.config:
del self.config['visualization_params']
2018-04-16 08:55:27 +00:00
with self.logging(self.application.simulator.name):
2018-04-10 16:26:24 +00:00
try:
2018-04-16 08:55:27 +00:00
self.simulation = self.application.simulator.run(self.config)
2018-04-16 09:17:15 +00:00
trials = []
for i in range(self.config['num_trials']):
trials.append('{}_trial_{}'.format(self.name, i))
self.write_message({'type': 'trials',
'data': trials })
2018-04-10 16:26:24 +00:00
except:
error = 'Something went wrong. Please, try again.'
self.write_message({'type': 'error',
'error': error})
2018-04-16 09:17:15 +00:00
self.send_log('ERROR.' + self.application.simulator.name, error)
2018-02-01 18:37:10 +00:00
2018-02-02 14:01:17 +00:00
def get_trial(self, trial):
G = self.simulation[trial].history_to_graph()
return nx.node_link_data(G)
2018-02-01 18:37:10 +00:00
@contextmanager
def logging(self, logger):
self.capture_logging = True
self.logger_application = logging.getLogger(logger)
self.log_capture_string = io.StringIO()
ch = logging.StreamHandler(self.log_capture_string)
self.logger_application.addHandler(ch)
self.update_logging()
yield self.capture_logging
2018-02-02 15:12:43 +00:00
sleep(0.2)
2018-02-01 18:37:10 +00:00
self.log_capture_string.close()
self.logger_application.removeHandler(ch)
self.capture_logging = False
return self.capture_logging
2017-12-20 17:10:14 +00:00
2017-12-15 16:59:50 +00:00
class ModularServer(tornado.web.Application):
2018-02-01 18:37:10 +00:00
""" Main visualization application. """
port = 8001
page_handler = (r'/', PageHandler)
socket_handler = (r'/ws', SocketHandler)
static_handler = (r'/(.*)', tornado.web.StaticFileHandler,
{'path': 'templates'})
local_handler = (r'/local/(.*)', tornado.web.StaticFileHandler,
{'path': ''})
handlers = [page_handler, socket_handler, static_handler, local_handler]
settings = {'debug': True,
'template_path': os.path.dirname(__file__) + '/templates'}
2018-04-16 08:55:27 +00:00
def __init__(self, simulator, name='SOIL', verbose=True, *args, **kwargs):
2018-02-01 18:37:10 +00:00
self.verbose = verbose
2018-04-16 08:55:27 +00:00
self.name = name
self.simulator = simulator
2018-02-01 18:37:10 +00:00
# Initializing the application itself:
super().__init__(self.handlers, **self.settings)
def launch(self, port=None):
""" Run the app. """
if port is not None:
self.port = port
url = 'http://127.0.0.1:{PORT}'.format(PORT=self.port)
print('Interface starting at {url}'.format(url=url))
self.listen(self.port)
2018-04-10 16:26:24 +00:00
# webbrowser.open(url)
2018-02-01 18:37:10 +00:00
tornado.ioloop.IOLoop.instance().start()