@ -1,8 +1,10 @@
import io
import io
import threading
import asyncio
import logging
import logging
import networkx as nx
import networkx as nx
import os
import os
import threading
import sys
import tornado . ioloop
import tornado . ioloop
import tornado . web
import tornado . web
import tornado . websocket
import tornado . websocket
@ -14,9 +16,20 @@ from contextlib import contextmanager
from time import sleep
from time import sleep
from xml . etree . ElementTree import tostring
from xml . etree . ElementTree import tostring
from tornado . concurrent import run_on_executor
from concurrent . futures import ThreadPoolExecutor
from . . simulation import SoilSimulation
logger = logging . getLogger ( __name__ )
logger = logging . getLogger ( __name__ )
logger . setLevel ( logging . INFO )
logger . setLevel ( logging . INFO )
ROOT = os . path . abspath ( os . path . dirname ( __file__ ) )
MAX_WORKERS = 4
LOGGING_INTERVAL = 0.5
# Workaround to let Soil load the required modules
sys . path . append ( ROOT )
class PageHandler ( tornado . web . RequestHandler ) :
class PageHandler ( tornado . web . RequestHandler ) :
""" Handler for the HTML template which holds the visualization. """
""" Handler for the HTML template which holds the visualization. """
@ -28,6 +41,7 @@ class PageHandler(tornado.web.RequestHandler):
class SocketHandler ( tornado . websocket . WebSocketHandler ) :
class SocketHandler ( tornado . websocket . WebSocketHandler ) :
""" Handler for websocket. """
""" Handler for websocket. """
executor = ThreadPoolExecutor ( max_workers = MAX_WORKERS )
def open ( self ) :
def open ( self ) :
if self . application . verbose :
if self . application . verbose :
@ -55,9 +69,10 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
self . write_message ( { ' type ' : ' error ' ,
self . write_message ( { ' type ' : ' error ' ,
' error ' : error } )
' error ' : error } )
return
return
else :
self . config = self . config [ 0 ]
self . config = self . config [ 0 ]
self . send_log ( ' INFO. ' + self . application . simulator . name , ' Using config: {name} ' . format ( name = self . config [ ' name ' ] ) )
self . send_log ( ' INFO. ' + self . simulation_name ,
' Using config: {name} ' . format ( name = self . config [ ' name ' ] ) )
if ' visualization_params ' in self . config :
if ' visualization_params ' in self . config :
self . write_message ( { ' type ' : ' visualization_params ' ,
self . write_message ( { ' type ' : ' visualization_params ' ,
@ -91,17 +106,17 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
logger . info ( ' Trial {} requested! ' . format ( msg [ ' data ' ] ) )
logger . info ( ' Trial {} requested! ' . format ( msg [ ' data ' ] ) )
self . send_log ( ' INFO. ' + __name__ , ' Trial {} requested! ' . format ( msg [ ' data ' ] ) )
self . send_log ( ' INFO. ' + __name__ , ' Trial {} requested! ' . format ( msg [ ' data ' ] ) )
self . write_message ( { ' type ' : ' get_trial ' ,
self . write_message ( { ' type ' : ' get_trial ' ,
' data ' : self . get_trial ( int ( msg [ ' data ' ] ) ) } )
' data ' : self . get_trial ( int ( msg [ ' data ' ] ) ) } )
elif msg [ ' type ' ] == ' run_simulation ' :
elif msg [ ' type ' ] == ' run_simulation ' :
if self . application . verbose :
if self . application . verbose :
logger . info ( ' Running new simulation for {name} ' . format ( name = self . config [ ' name ' ] ) )
logger . info ( ' Running new simulation for {name} ' . format ( name = self . config [ ' name ' ] ) )
self . send_log ( ' INFO. ' + self . application. simulator . name, ' Running new simulation for {name} ' . format ( name = self . config [ ' name ' ] ) )
self . send_log ( ' INFO. ' + self . simulation_ name, ' Running new simulation for {name} ' . format ( name = self . config [ ' name ' ] ) )
self . config [ ' environment_params ' ] = msg [ ' data ' ]
self . config [ ' environment_params ' ] = msg [ ' data ' ]
self . run_simulation ( )
self . run_simulation ( )
elif msg [ ' type ' ] == ' download_gexf ' :
elif msg [ ' type ' ] == ' download_gexf ' :
G = self . simulation [ int ( msg [ ' data ' ] ) ] . history_to_graph ( )
G = self . trials [ int ( msg [ ' data ' ] ) ] . history_to_graph ( )
for node in G . nodes ( ) :
for node in G . nodes ( ) :
if ' pos ' in G . node [ node ] :
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 } }
G . node [ node ] [ ' viz ' ] = { " position " : { " x " : G . node [ node ] [ ' pos ' ] [ 0 ] , " y " : G . node [ node ] [ ' pos ' ] [ 1 ] , " z " : 0.0 } }
@ -113,7 +128,7 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
' data ' : tostring ( writer . xml ) . decode ( writer . encoding ) } )
' data ' : tostring ( writer . xml ) . decode ( writer . encoding ) } )
elif msg [ ' type ' ] == ' download_json ' :
elif msg [ ' type ' ] == ' download_json ' :
G = self . simulation [ int ( msg [ ' data ' ] ) ] . history_to_graph ( )
G = self . trials [ int ( msg [ ' data ' ] ) ] . history_to_graph ( )
for node in G . nodes ( ) :
for node in G . nodes ( ) :
if ' pos ' in G . node [ node ] :
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 } }
G . node [ node ] [ ' viz ' ] = { " position " : { " x " : G . node [ node ] [ ' pos ' ] [ 0 ] , " y " : G . node [ node ] [ ' pos ' ] [ 1 ] , " z " : 0.0 } }
@ -130,13 +145,13 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
try :
try :
if ( not self . log_capture_string . closed and self . log_capture_string . getvalue ( ) ) :
if ( not self . log_capture_string . closed and self . log_capture_string . getvalue ( ) ) :
for i in range ( len ( self . log_capture_string . getvalue ( ) . split ( ' \n ' ) ) - 1 ) :
for i in range ( len ( self . log_capture_string . getvalue ( ) . split ( ' \n ' ) ) - 1 ) :
self . send_log ( ' INFO. ' + self . application. simulator . name, self . log_capture_string . getvalue ( ) . split ( ' \n ' ) [ i ] )
self . send_log ( ' INFO. ' + self . simulation_ name, self . log_capture_string . getvalue ( ) . split ( ' \n ' ) [ i ] )
self . log_capture_string . truncate ( 0 )
self . log_capture_string . truncate ( 0 )
self . log_capture_string . seek ( 0 )
self . log_capture_string . seek ( 0 )
finally :
finally :
if self . capture_logging :
if self . capture_logging :
t hread = threading . Timer ( 0.01 , self . update_logging )
t ornado. ioloop . IOLoop . current ( ) . call_later ( LOGGING_INTERVAL , self . update_logging )
thread . start ( )
def on_close ( self ) :
def on_close ( self ) :
if self . application . verbose :
if self . application . verbose :
@ -144,29 +159,45 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
def send_log ( self , logger , logging ) :
def send_log ( self , logger , logging ) :
self . write_message ( { ' type ' : ' log ' ,
self . write_message ( { ' type ' : ' log ' ,
' logger ' : logger ,
' logger ' : logger ,
' logging ' : logging } )
' logging ' : logging } )
@property
def simulation_name ( self ) :
return self . config . get ( ' name ' , ' NoSimulationRunning ' )
@run_on_executor
def nonblocking ( self , config ) :
simulation = SoilSimulation ( * * config )
return simulation . run ( )
@tornado.gen.coroutine
def run_simulation ( self ) :
def run_simulation ( self ) :
# Run simulation and capture logs
# Run simulation and capture logs
logger . info ( ' Running simulation! ' )
if ' visualization_params ' in self . config :
if ' visualization_params ' in self . config :
del self . config [ ' visualization_params ' ]
del self . config [ ' visualization_params ' ]
with self . logging ( self . application . simulator . name ) :
with self . logging ( self . simulation_ name) :
try :
try :
self . simulation = self . application . simulator . run ( self . config )
config = dict ( * * self . config )
trials = [ ]
config [ ' dir_path ' ] = os . path . join ( self . application . dir_path , config [ ' name ' ] )
for i in range ( self . config [ ' num_trials ' ] ) :
config [ ' dump ' ] = self . application . dump
trials . append ( ' {} _trial_ {} ' . format ( self . name , i ) )
self . trials = yield self . nonblocking ( config )
self . write_message ( { ' type ' : ' trials ' ,
self . write_message ( { ' type ' : ' trials ' ,
' data ' : trials } )
' data ' : list ( trial . name for trial in self . trials ) } )
except :
except Exception as ex :
error = ' Something went wrong. Please, try again. '
error = ' Something went wrong: \n \t {} ' . format ( ex )
logging . info ( error )
self . write_message ( { ' type ' : ' error ' ,
self . write_message ( { ' type ' : ' error ' ,
' error ' : error } )
' error ' : error } )
self . send_log ( ' ERROR. ' + self . application. simulator . name, error )
self . send_log ( ' ERROR. ' + self . simulation_ name, error )
def get_trial ( self , trial ) :
def get_trial ( self , trial ) :
G = self . simulation [ trial ] . history_to_graph ( )
logger . info ( ' Available trials: %s ' % len ( self . trials ) )
logger . info ( ' Ask for : %s ' % trial )
trial = self . trials [ trial ]
G = trial . history_to_graph ( )
return nx . node_link_data ( G )
return nx . node_link_data ( G )
@contextmanager
@contextmanager
@ -193,19 +224,20 @@ class ModularServer(tornado.web.Application):
page_handler = ( r ' / ' , PageHandler )
page_handler = ( r ' / ' , PageHandler )
socket_handler = ( r ' /ws ' , SocketHandler )
socket_handler = ( r ' /ws ' , SocketHandler )
static_handler = ( r ' /(.*) ' , tornado . web . StaticFileHandler ,
static_handler = ( r ' /(.*) ' , tornado . web . StaticFileHandler ,
{ ' path ' : ' templates ' } )
{ ' path ' : os . path . join ( ROOT , ' static ' ) } )
local_handler = ( r ' /local/(.*) ' , tornado . web . StaticFileHandler ,
local_handler = ( r ' /local/(.*) ' , tornado . web . StaticFileHandler ,
{ ' path ' : ' ' } )
{ ' path ' : ' ' } )
handlers = [ page_handler , socket_handler , static_handler , local_handler ]
handlers = [ page_handler , socket_handler , static_handler , local_handler ]
settings = { ' debug ' : True ,
settings = { ' debug ' : True ,
' template_path ' : os. path . dirname ( __file__ ) + ' /templates ' }
' template_path ' : ROOT + ' /templates ' }
def __init__ ( self , simulator , name = ' SOIL ' , verbose = True , * args , * * kwargs ) :
def __init__ ( self , dump= False , dir_path = ' output ' , name = ' SOIL ' , verbose = True , * args , * * kwargs ) :
self . verbose = verbose
self . verbose = verbose
self . name = name
self . name = name
self . simulator = simulator
self . dump = dump
self . dir_path = dir_path
# Initializing the application itself:
# Initializing the application itself:
super ( ) . __init__ ( self . handlers , * * self . settings )
super ( ) . __init__ ( self . handlers , * * self . settings )
@ -220,3 +252,23 @@ class ModularServer(tornado.web.Application):
self . listen ( self . port )
self . listen ( self . port )
# webbrowser.open(url)
# webbrowser.open(url)
tornado . ioloop . IOLoop . instance ( ) . start ( )
tornado . ioloop . IOLoop . instance ( ) . start ( )
def run ( * args , * * kwargs ) :
asyncio . set_event_loop ( asyncio . new_event_loop ( ) )
server = ModularServer ( * args , * * kwargs )
server . launch ( )
def main ( ) :
import argparse
parser = argparse . ArgumentParser ( description = ' Visualization of a Graph Model ' )
parser . add_argument ( ' --name ' , ' -n ' , nargs = 1 , default = ' SOIL ' , help = ' name of the simulation ' )
parser . add_argument ( ' --dump ' , ' -d ' , help = ' dumping results in folder output ' , action = ' store_true ' )
parser . add_argument ( ' --port ' , ' -p ' , nargs = 1 , default = 8001 , help = ' port for launching the server ' )
parser . add_argument ( ' --verbose ' , ' -v ' , help = ' verbose mode ' , action = ' store_true ' )
args = parser . parse_args ( )
run ( name = args . name , port = ( args . port [ 0 ] if isinstance ( args . port , list ) else args . port ) , verbose = args . verbose )