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

Running new simulations

This commit is contained in:
Tasio Mendez 2018-02-01 19:37:10 +01:00
parent c93f3fafc7
commit 05c1b5c003
7 changed files with 501 additions and 422 deletions

305
server.py
View File

@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
class VisualizationElement: class VisualizationElement:
""" """
Defines an element of the visualization. Defines an element of the visualization.
Attributes: Attributes:
package_includes: A list of external JavaScript files to include that package_includes: A list of external JavaScript files to include that
@ -32,190 +32,197 @@ class VisualizationElement:
to the client. to the client.
""" """
package_includes = [] package_includes = []
local_includes = [] local_includes = []
js_code = '' js_code = ''
render_args = {} render_args = {}
def __init__(self): def __init__(self):
pass pass
def render(self, model): def render(self, model):
return '<b>VisualizationElement goes here</b>.' return '<b>VisualizationElement goes here</b>.'
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. """
def get(self): def get(self):
self.render('index.html', port=self.application.port, self.render('index.html', port=self.application.port,
model_name=self.application.model_name, model_name=self.application.model_name,
package_includes=self.application.package_includes, package_includes=self.application.package_includes,
local_includes=self.application.local_includes, local_includes=self.application.local_includes,
scripts=self.application.js_code) scripts=self.application.js_code)
class SocketHandler(tornado.websocket.WebSocketHandler): class SocketHandler(tornado.websocket.WebSocketHandler):
def open(self): def open(self):
if self.application.verbose: if self.application.verbose:
logger.info('Socket opened!') logger.info('Socket opened!')
def check_origin(self, origin): def check_origin(self, origin):
return True return True
def on_message(self, message): def on_message(self, message):
""" Receiving a message from the websocket, parse, and act accordingly. """ """ Receiving a message from the websocket, parse, and act accordingly. """
msg = tornado.escape.json_decode(message) msg = tornado.escape.json_decode(message)
if msg['type'] == 'config_file': if msg['type'] == 'config_file':
if self.application.verbose: if self.application.verbose:
print(msg['data']) print(msg['data'])
config = list(yaml.load_all(msg['data'])) self.config = list(yaml.load_all(msg['data']))
if len(config) > 1: if len(self.config) > 1:
error = 'Please, provide only one configuration.' error = 'Please, provide only one configuration.'
if self.application.verbose: if self.application.verbose:
logger.error(error) logger.error(error)
self.write_message({'type': 'error', self.write_message({'type': 'error',
'error': error}) 'error': error})
return return
else: else:
config = config[0] self.config = self.config[0]
self.send_log('INFO.soil', 'Using config: {name}'.format(name=config['name'])) self.send_log('INFO.soil', 'Using config: {name}'.format(name=self.config['name']))
self.name = config['name'] self.name = self.config['name']
settings = [] self.run_simulation()
for key in config['environment_params']:
if type(config['environment_params'][key]) == float:
setting_type = 'number'
elif type(config['environment_params'][key]) == bool:
setting_type = 'boolean'
else:
setting_type = 'undefined'
settings.append({ settings = []
'label': key, for key in self.config['environment_params']:
'type': setting_type, if type(self.config['environment_params'][key]) == float:
'value': config['environment_params'][key] setting_type = 'number'
}) elif type(self.config['environment_params'][key]) == bool:
setting_type = 'boolean'
else:
setting_type = 'undefined'
self.write_message({'type': 'settings', settings.append({
'data': settings}) 'label': key,
'type': setting_type,
# Run simulation and capture logs 'value': self.config['environment_params'][key]
with self.logging(self.application.model.name): })
self.application.model.run(config)
trials = [] self.write_message({'type': 'settings',
for i in range(config['num_trials']): 'data': settings})
trials.append('{}_trial_{}'.format(self.name, i))
self.write_message({'type': 'trials',
'data': trials })
elif msg['type'] == 'get_trial': elif msg['type'] == 'get_trial':
if self.application.verbose: if self.application.verbose:
logger.info('Trial {} requested!'.format(msg['data'])) logger.info('Trial {} requested!'.format(msg['data']))
self.send_log('INFO.user', 'Trial {} requested!'.format(msg['data'])) self.send_log('INFO.user', 'Trial {} requested!'.format(msg['data']))
self.write_message({'type': 'get_trial', self.write_message({'type': 'get_trial',
'data': self.application.model.get_trial(self.name, msg['data']) }) 'data': self.application.model.get_trial(self.name, msg['data']) })
else: elif msg['type'] == 'run_simulation':
if self.application.verbose: self.send_log('INFO.soil', 'Running new simulation for {name}'.format(name=self.config['name']))
logger.info('Unexpected message!') self.config['environment_params'] = msg['data']
self.run_simulation()
def update_logging(self): else:
try: if self.application.verbose:
if (not self.log_capture_string.closed and self.log_capture_string.getvalue()): logger.info('Unexpected message!')
self.send_log('INFO.soil', self.log_capture_string.getvalue())
self.log_capture_string.truncate(0)
self.log_capture_string.seek(0)
finally:
if self.capture_logging:
thread = threading.Timer(0.001, self.update_logging)
thread.start()
def on_close(self): def update_logging(self):
logger.info('Socket closed!') try:
if (not self.log_capture_string.closed and self.log_capture_string.getvalue()):
self.send_log('INFO.soil', self.log_capture_string.getvalue())
self.log_capture_string.truncate(0)
self.log_capture_string.seek(0)
finally:
if self.capture_logging:
thread = threading.Timer(0.001, self.update_logging)
thread.start()
def send_log(self, logger, logging): def on_close(self):
self.write_message({'type': 'log', logger.info('Socket closed!')
'logger': logger,
'logging': logging })
@contextmanager def send_log(self, logger, logging):
def logging(self, logger): self.write_message({'type': 'log',
self.capture_logging = True 'logger': logger,
self.logger_application = logging.getLogger(logger) 'logging': logging })
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
self.log_capture_string.close() def run_simulation(self):
self.logger_application.removeHandler(ch) # Run simulation and capture logs
self.capture_logging = False with self.logging(self.application.model.name):
return self.capture_logging self.application.model.run(self.config)
trials = []
for i in range(self.config['num_trials']):
trials.append('{}_trial_{}'.format(self.name, i))
self.write_message({'type': 'trials',
'data': trials })
@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
self.log_capture_string.close()
self.logger_application.removeHandler(ch)
self.capture_logging = False
return self.capture_logging
class ModularServer(tornado.web.Application): class ModularServer(tornado.web.Application):
""" Main visualization application. """ """ Main visualization application. """
portrayal_method = None portrayal_method = None
port = 8001 port = 8001
model_args = () model_args = ()
model_kwargs = {} model_kwargs = {}
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': 'templates'})
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': os.path.dirname(__file__) + '/templates'}
def __init__(self, model, visualization_element, name='SOIL Model', verbose=True, def __init__(self, model, visualization_element, name='SOIL Model', verbose=True,
*args, **kwargs): *args, **kwargs):
self.verbose = verbose self.verbose = verbose
self.package_includes = set() self.package_includes = set()
self.local_includes = set() self.local_includes = set()
self.js_code = [] self.js_code = []
self.visualization_element = visualization_element self.visualization_element = visualization_element
self.model_name = name self.model_name = name
self.model = model self.model = model
self.model_args = args self.model_args = args
self.model_kwargs = kwargs self.model_kwargs = kwargs
#self.reset_model() #self.reset_model()
# Initializing the application itself: # Initializing the application itself:
super().__init__(self.handlers, **self.settings) super().__init__(self.handlers, **self.settings)
''' '''
def reset_model(self): def reset_model(self):
self.model = self.model_cls(*self.model_args, **self.model_kwargs) self.model = self.model_cls(*self.model_args, **self.model_kwargs)
''' '''
def render_model(self): def render_model(self):
return self.visualization_element.render(self.model) return self.visualization_element.render(self.model)
def launch(self, port=None): def launch(self, port=None):
""" Run the app. """ """ Run the app. """
if port is not None: if port is not None:
self.port = port self.port = port
url = 'http://127.0.0.1:{PORT}'.format(PORT=self.port) url = 'http://127.0.0.1:{PORT}'.format(PORT=self.port)
print('Interface starting at {url}'.format(url=url)) print('Interface starting at {url}'.format(url=url))
self.listen(self.port) self.listen(self.port)
# webbrowser.open(url) # webbrowser.open(url)
tornado.ioloop.IOLoop.instance().start() tornado.ioloop.IOLoop.instance().start()

View File

@ -24,6 +24,10 @@ html, body {
margin-right: 10px !important; margin-right: 10px !important;
} }
.nav.navbar-right a {
outline: none !important;
}
.dropdown-menu > li > a:hover { .dropdown-menu > li > a:hover {
background-color: #d4d3d3; background-color: #d4d3d3;
cursor: pointer; cursor: pointer;
@ -398,3 +402,9 @@ table#link-distance .max {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
/** MODAL **/
.modal-footer,
.modal-header {
border: none !important;
}

View File

@ -97,40 +97,38 @@
$('.config-item #properties').change(function() { $('.config-item #properties').change(function() {
self.GraphVisualization.update_graph($(this).val(), slider.value(), function() { self.GraphVisualization.update_graph($(this).val(), slider.value(), function() {
update_statistics_table(); update_statistics_table();
}) });
}); });
chart_nodes = c3.generate({ // Run simulation
size: { $('#simulation_modal .btn-success').click(function() {
width: width_chart, if ( !jQuery.isEmptyObject(run_simulation()) ) {
height: height_chart self.GraphVisualization.reset();
}, $('#load').show().addClass('loader');;
data: { _socket.send(run_simulation(), 'run_simulation');
columns: [], $('.console').append('<br/>');
type: 'area-spline' }
}, $('#simulation_modal').modal('hide')
axis: {
x: { label: 'Time' },
y: { label: 'Number of nodes' }
},
point: { show: false },
bindto: '#chart_nodes'
}); });
chart_attrs = c3.generate({
size: { chart_nodes = create_chart(width_chart, height_chart, 'Time', 'Number of nodes', '#chart_nodes');
width: width_chart, chart_attrs = create_chart(width_chart, height_chart, 'Time', 'Attributes', '#chart_attrs');
height: height_chart
}, // Fill modal window
data: { $('#simulation_modal').on('show.bs.modal', function(e) {
columns: [], var variables = run_simulation()
type: 'area-spline' var x = 0,
}, row;
axis: { for (var i in variables) {
x: { label: 'Time' }, if ( x % 2 === 0 ) row = $('<tr>').appendTo('#simulation_modal table tbody');
y: { label: 'Attributes' } $('<td>').text(i).appendTo(row);
}, $('<td>').text(variables[i]).appendTo(row);
point: { show: false }, x++;
bindto: '#chart_attrs' }
});
$('#simulation_modal').on('hide.bs.modal', function(e) {
$('#simulation_modal table tbody').empty();
}); });
} }
@ -190,7 +188,7 @@
</li> </li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li><a href="#">Run simulation</a></li> <li><a href="#" id="run_simulation" role="button">Run simulation</a></li>
</ul> </ul>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
@ -319,6 +317,29 @@
</div> </div>
<div class="modal fade" tabindex="-1" role="dialog" id="simulation_modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">New simulation</h4>
</div>
<div class="modal-body">
<p>You are going to run a new simulation, all charts and trials are going to be lost. A new ones will be available after the simulation.</p>
<p>Check your new environment variables for this simulation.</p>
<table class="table">
<thead><tr><th>Variable</th><th>Value</th><th>Variable</th><th>Value</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success">Run</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</body> </body>
</html> </html>

View File

@ -9,123 +9,128 @@ ws.onopen = function() {
// Receive data from server // Receive data from server
ws.onmessage = function(message) { ws.onmessage = function(message) {
//console.log('Message received!'); //console.log('Message received!');
var msg = JSON.parse(message.data); var msg = JSON.parse(message.data);
switch(msg['type']) { switch(msg['type']) {
case 'trials': case 'trials':
$('#load').removeClass('loader'); $('#load').removeClass('loader');
reset_trials(); reset_trials();
set_trials(msg['data']); set_trials(msg['data']);
break; break;
case 'get_trial': case 'get_trial':
console.log(msg['data']); console.log(msg['data']);
GraphVisualization.import(convertJSON(msg['data']['graph']), msg['data']['models'], function() { GraphVisualization.import(convertJSON(msg['data']['graph']), msg['data']['models'], function() {
$('#load').hide(); $('#load').hide();
reset_configuration(); reset_configuration();
set_configuration(); set_configuration();
$('#home_menu').click(function() { $('#home_menu').click(function() {
setTimeout(function() { setTimeout(function() {
reset_timeline(); reset_timeline();
set_timeline(msg['data']['graph']); set_timeline(msg['data']['graph']);
}, 1000); }, 1000);
}); });
reset_timeline(); reset_timeline();
set_timeline(msg['data']['graph']); set_timeline(msg['data']['graph']);
}); });
$('#charts .chart').removeClass('no-data'); $('#charts .chart').removeClass('no-data');
set_chart_nodes(msg['data']['graph'], chart_nodes) set_chart_nodes(msg['data']['graph'], chart_nodes)
set_chart_attrs(msg['data']['graph'], chart_attrs, $('.config-item #properties').val()) set_chart_attrs(msg['data']['graph'], chart_attrs, $('.config-item #properties').val())
break; $('.config-item #properties').change(function() {
chart_attrs.destroy();
chart_attrs = create_chart(width_chart, height_chart, 'Time', 'Attributes', '#chart_attrs');
set_chart_attrs(msg['data']['graph'], chart_attrs, $('.config-item #properties').val())
});
break;
case 'settings': case 'settings':
$('#wrapper-settings').empty().removeClass('none'); $('#wrapper-settings').empty().removeClass('none');
initGUI(msg['data']); initGUI(msg['data']);
break; break;
case 'error': case 'error':
console.log(msg['error']); console.log(msg['error']);
_socket.error(msg['error']); _socket.error(msg['error']);
$('#load').removeClass('loader'); $('#load').removeClass('loader');
break; break;
case 'log': case 'log':
$('.console').append('$ ' + msg['logger'] + ': ' + msg['logging'] + '<br/>'); $('.console').append('$ ' + msg['logger'] + ': ' + msg['logging'] + '<br/>');
$('.console').animate({ scrollTop: $('.console')[0].scrollHeight }, 'slow'); $('.console').animate({ scrollTop: $('.console')[0].scrollHeight }, 'slow');
break; break;
default: default:
console.log('Unexpected message!') console.log('Unexpected message!')
} }
} }
var _socket = { var _socket = {
send: function(message, type) { send: function(message, type) {
var json = {} var json = {}
json['type'] = type json['type'] = type
json['data'] = message json['data'] = message
ws.send(JSON.stringify(json)) ws.send(JSON.stringify(json))
}, },
error: function(message) { error: function(message) {
$('#error-message').text(message); $('#error-message').text(message);
$('.alert.alert-danger').show(); $('.alert.alert-danger').show();
} }
}; };
var set_trials = function(trials) { var set_trials = function(trials) {
for ( i in trials ) { for ( i in trials ) {
var list_item = $('<li>').appendTo('.dropdown#trials .dropdown-menu'); var list_item = $('<li>').appendTo('.dropdown#trials .dropdown-menu');
$('<a>').val(i).text(trials[i]).appendTo(list_item); $('<a>').val(i).text(trials[i]).appendTo(list_item);
} }
// Select 'trials' // Select 'trials'
$('.dropdown#trials li a').click(function() { $('.dropdown#trials li a').click(function() {
var a = $('.dropdown-toggle .caret'); var a = $('.dropdown-toggle .caret');
$('.dropdown-toggle').text($(this).text() + ' ').append(a); $('.dropdown-toggle').text($(this).text() + ' ').append(a);
_socket.send($(this).val(), 'get_trial'); _socket.send($(this).val(), 'get_trial');
}); });
// Request first trial as default // Request first trial as default
_socket.send(0, 'get_trial') _socket.send(0, 'get_trial')
}; };
var reset_trials = function() { var reset_trials = function() {
// 'Trials' selector // 'Trials' selector
$('.dropdown-menu').empty(); $('.dropdown-menu').empty();
var a = $('.dropdown-toggle .caret'); var a = $('.dropdown-toggle .caret');
$('.dropdown-toggle').text('Trials ').append(a); $('.dropdown-toggle').text('Trials ').append(a);
} }
var convertJSON = function(json) { var convertJSON = function(json) {
json.links.forEach(function(link) { json.links.forEach(function(link) {
link.source = json.nodes[link.source] link.source = json.nodes[link.source]
link.target = json.nodes[link.target] link.target = json.nodes[link.target]
}); });
// Fix spells for nodes // Fix spells for nodes
json.nodes.forEach(function(node) { json.nodes.forEach(function(node) {
for (i in node.spells) { for (i in node.spells) {
if (node.spells[i][0] > node.spells[i][1]) { if (node.spells[i][0] > node.spells[i][1]) {
aux = node.spells[i][0]; aux = node.spells[i][0];
node.spells[i][0] = node.spells[i][1]; node.spells[i][0] = node.spells[i][1];
node.spells[i][1] = aux; node.spells[i][1] = aux;
} }
} }
}); });
return json; return json;
} }
var update_statistics_table = function() { var update_statistics_table = function() {
$('#percentTable tbody').empty() $('#percentTable tbody').empty()
var statisticsSorted = Object.keys(self.GraphVisualization.statistics).sort(function(a,b) { var statisticsSorted = Object.keys(self.GraphVisualization.statistics).sort(function(a,b) {
return self.GraphVisualization.statistics[b] - self.GraphVisualization.statistics[a]; return self.GraphVisualization.statistics[b] - self.GraphVisualization.statistics[a];
}); });
for ( var i in statisticsSorted ) { for ( var i in statisticsSorted ) {
if ( i <= 5 ) { if ( i <= 5 ) {
// Draw table // Draw table
var appendTo = '#percentTable > tbody tr:nth-child(' + Number(parseInt(i) + 1) + ')'; var appendTo = '#percentTable > tbody tr:nth-child(' + Number(parseInt(i) + 1) + ')';
var propertyName = (statisticsSorted[i].includes('class')) ? var propertyName = (statisticsSorted[i].includes('class')) ?
statisticsSorted[i].split('.').pop().split('\'')[0] : statisticsSorted[i]; statisticsSorted[i].split('.').pop().split('\'')[0] : statisticsSorted[i];
@ -134,12 +139,12 @@ var update_statistics_table = function() {
$('<td>').css('background-color', self.GraphVisualization.color(statisticsSorted[i])).addClass('col-sm-1').appendTo(appendTo); $('<td>').css('background-color', self.GraphVisualization.color(statisticsSorted[i])).addClass('col-sm-1').appendTo(appendTo);
$('<td>').addClass('text-left col-sm-4').text(self.GraphVisualization.statistics[statisticsSorted[i]] + ' %').appendTo(appendTo); $('<td>').addClass('text-left col-sm-4').text(self.GraphVisualization.statistics[statisticsSorted[i]] + ' %').appendTo(appendTo);
$('<td>').addClass('text-right col-sm-6 property-name').text(propertyName).appendTo(appendTo); $('<td>').addClass('text-right col-sm-6 property-name').text(propertyName).appendTo(appendTo);
} }
} }
} }
var set_configuration = function() { var set_configuration = function() {
// Number of nodes and links info table // Number of nodes and links info table
$('<tr>').appendTo('#info-graph > tbody'); $('<tr>').appendTo('#info-graph > tbody');
$('<th>').text('Nodes:').appendTo('#info-graph > tbody tr:nth-child(1)'); $('<th>').text('Nodes:').appendTo('#info-graph > tbody tr:nth-child(1)');
$('<th>').text(self.GraphVisualization.nodes).addClass('text-right').appendTo('#info-graph > tbody tr:nth-child(1)'); $('<th>').text(self.GraphVisualization.nodes).addClass('text-right').appendTo('#info-graph > tbody tr:nth-child(1)');
@ -151,31 +156,34 @@ var set_configuration = function() {
// Options of 'Select' // Options of 'Select'
for ( var i in self.GraphVisualization.model['dynamic'] ) { for ( var i in self.GraphVisualization.model['dynamic'] ) {
$('<option>').val(self.GraphVisualization.model['dynamic'][i].title) $('<option>').val(self.GraphVisualization.model['dynamic'][i].title)
.text(self.GraphVisualization.model['dynamic'][i].title).appendTo('#properties-dynamic'); .text(self.GraphVisualization.model['dynamic'][i].title).appendTo('#properties-dynamic');
} }
for ( var i in self.GraphVisualization.model['static'] ) { for ( var i in self.GraphVisualization.model['static'] ) {
$('<option>').val(self.GraphVisualization.model['static'][i].title) $('<option>').val(self.GraphVisualization.model['static'][i].title)
.text(self.GraphVisualization.model['static'][i].title).appendTo('#properties-static'); .text(self.GraphVisualization.model['static'][i].title).appendTo('#properties-static');
} }
// Hide optgroups if they are empty // Hide optgroups if they are empty
if ( $('#properties-dynamic').children().length === 0 ) $('#properties-dynamic').hide(); if ( $('#properties-dynamic').children().length === 0 ) $('#properties-dynamic').hide();
if ( $('#properties-static').children().length === 0 ) $('#properties-static').hide(); if ( $('#properties-static').children().length === 0 ) $('#properties-static').hide();
update_statistics_table(); update_statistics_table();
// Enable 'Link Distance' slider // Enable 'Link Distance' slider
$('#link-distance-slider').slider('enable').on('change', function(value) { $('#link-distance-slider').slider('enable').on('change', function(value) {
self.GraphVisualization.set_link_distance(value.value.newValue); self.GraphVisualization.set_link_distance(value.value.newValue);
}); });
// Enable 'Run configuration' button
$('#run_simulation').attr('data-toggle', 'modal').attr('data-target', '#simulation_modal');
} }
var reset_configuration = function() { var reset_configuration = function() {
// Information table about the graph // Information table about the graph
$('#info-graph > tbody').empty(); $('#info-graph > tbody').empty();
// 'Select' for properties // 'Select' for properties
$('#properties-dynamic').empty().show(); $('#properties-dynamic').empty().show();
$('#properties-static').empty().show(); $('#properties-static').empty().show();
// 'Link Distance' slider // 'Link Distance' slider
@ -183,35 +191,35 @@ var reset_configuration = function() {
} }
var set_timeline = function(graph) { var set_timeline = function(graph) {
// 'Timeline' slider // 'Timeline' slider
var [min, max] = get_limits(graph); var [min, max] = get_limits(graph);
var stepUnix = (max - min) / 200; var stepUnix = (max - min) / 200;
var minUnix = (min !== Math.min()) ? min : 0; var minUnix = (min !== Math.min()) ? min : 0;
var maxUnix = (max !== Math.max()) ? max : minUnix + 20; var maxUnix = (max !== Math.max()) ? max : minUnix + 20;
slider = d3.slider(); slider = d3.slider();
d3.select('#slider3').attr('width', width).call( d3.select('#slider3').attr('width', width).call(
slider.axis(true).min(minUnix).max(maxUnix).step(stepUnix).value(maxUnix) slider.axis(true).min(minUnix).max(maxUnix).step(stepUnix).value(maxUnix)
.on('slide', function(evt, value) { .on('slide', function(evt, value) {
self.GraphVisualization.update_graph($('.config-item #properties').val(), value, function() { self.GraphVisualization.update_graph($('.config-item #properties').val(), value, function() {
update_statistics_table(); update_statistics_table();
}); });
}) })
); );
// Draw graph for the first time // Draw graph for the first time
self.GraphVisualization.update_graph($('.config-item #properties').val(), maxUnix, function() { self.GraphVisualization.update_graph($('.config-item #properties').val(), maxUnix, function() {
update_statistics_table(); update_statistics_table();
}); });
// 'Speed' slider // 'Speed' slider
$('#speed-slider').slider('enable').on('change', function(value) { $('#speed-slider').slider('enable').on('change', function(value) {
speed = value.value.newValue; speed = value.value.newValue;
}); });
// Button 'Play' // Button 'Play'
$('button#button_play').on('click', function() { $('button#button_play').on('click', function() {
$('button#button_play').addClass('pressed').prop("disabled", true); $('button#button_play').addClass('pressed').prop("disabled", true);
$('#speed-slider').slider('disable'); $('#speed-slider').slider('disable');
@ -220,8 +228,8 @@ var set_timeline = function(graph) {
if (slider.value() >= maxUnix) { if (slider.value() >= maxUnix) {
slider.value(minUnix); slider.value(minUnix);
self.GraphVisualization.update_graph($('.config-item #properties').val(), slider.value(), function() { self.GraphVisualization.update_graph($('.config-item #properties').val(), slider.value(), function() {
update_statistics_table(); update_statistics_table();
}); });
setTimeout(player, 1000); setTimeout(player, 1000);
} else { } else {
player(); player();
@ -231,9 +239,9 @@ var set_timeline = function(graph) {
function player() { function player() {
clearInterval(play); clearInterval(play);
play = setInterval(function() { play = setInterval(function() {
self.GraphVisualization.update_graph($('.config-item #properties').val(), slider.value(), function() { self.GraphVisualization.update_graph($('.config-item #properties').val(), slider.value(), function() {
update_statistics_table(); update_statistics_table();
}); });
if (slider.value() + slider.step() >= maxUnix) { if (slider.value() + slider.step() >= maxUnix) {
slider.value(maxUnix); slider.value(maxUnix);
@ -250,93 +258,130 @@ var set_timeline = function(graph) {
} }
}); });
// Button 'Pause' // Button 'Pause'
$('button#button_pause').on('click', function() { $('button#button_pause').on('click', function() {
clearInterval(play); clearInterval(play);
slider.step(stepUnix); slider.step(stepUnix);
$('button#button_play').removeClass('pressed').prop("disabled", false); $('button#button_play').removeClass('pressed').prop("disabled", false);
$('#speed-slider').slider('enable'); $('#speed-slider').slider('enable');
}); });
// Button 'Zoom to Fit' // Button 'Zoom to Fit'
$('button#button_zoomFit').click(function() { self.GraphVisualization.fit(); }); $('button#button_zoomFit').click(function() { self.GraphVisualization.fit(); });
} }
var reset_timeline = function() { var reset_timeline = function() {
// 'Timeline' slider // 'Timeline' slider
$('#slider3').html(''); $('#slider3').html('');
// 'Speed' slider // 'Speed' slider
$('#speed-slider').slider('disable').slider('setValue', 1000); $('#speed-slider').slider('disable').slider('setValue', 1000);
// Buttons // Buttons
clearInterval(play); clearInterval(play);
$('button#button_play').off().removeClass('pressed').prop("disabled", false); $('button#button_play').off().removeClass('pressed').prop("disabled", false);
$('button#button_pause').off(); $('button#button_pause').off();
$('button#button_zoomFit').off(); $('button#button_zoomFit').off();
} }
var get_limits = function(graph) { var get_limits = function(graph) {
var max = Math.max(); var max = Math.max();
var min = Math.min() var min = Math.min()
graph.links.forEach(function(link) { graph.links.forEach(function(link) {
if (link.end > max) max = link.end if (link.end > max) max = link.end
if (link.start > max) max = link.start if (link.start > max) max = link.start
if (link.end < min) min = link.end if (link.end < min) min = link.end
if (link.start < min) min = link.start if (link.start < min) min = link.start
}); });
graph.nodes.forEach(function(node) { graph.nodes.forEach(function(node) {
for (property in node) { for (property in node) {
if ( Array.isArray(node[property]) ) { if ( Array.isArray(node[property]) ) {
for (i in node[property]) { for (i in node[property]) {
for (j in node[property][i]) { for (j in node[property][i]) {
if (node[property][i][j] > max) max = node[property][i][j]; if (node[property][i][j] > max) max = node[property][i][j];
if (node[property][i][j] < min) min = node[property][i][j]; if (node[property][i][j] < min) min = node[property][i][j];
} }
} }
} }
} }
}) })
return [min, max]; return [min, max];
} }
var set_chart_nodes = function(graph, chart) { var set_chart_nodes = function(graph, chart) {
var [min, max] = get_limits(graph); var [min, max] = get_limits(graph);
var data = ['nodes'] var data = ['nodes']
for (var i = min; i <= max; i++) { for (var i = min; i <= max; i++) {
data.push(this.GraphVisualization.get_nodes(i)); data.push(this.GraphVisualization.get_nodes(i));
} }
chart.load({ chart.load({
unload: true, unload: true,
columns: [data] columns: [data]
}); });
} }
var set_chart_attrs = function(graph, chart, property) { var set_chart_attrs = function(graph, chart, property) {
var [min, max] = get_limits(graph); var [min, max] = get_limits(graph);
var data_tmp = {} var data_tmp = {}
for (var i = min; i <= max; i++) { for (var i = min; i <= max; i++) {
this.GraphVisualization.get_attributes(property, i, function(object) { this.GraphVisualization.get_attributes(property, i, function(object) {
for (var value in object) { for (var value in object) {
if (!data_tmp[value]) { if (!data_tmp[value]) {
var time = 0 var time = 0
for (var done in data_tmp) for (var done in data_tmp)
time = (data_tmp[done].length > time) ? data_tmp[done].length - 1 : time time = (data_tmp[done].length > time) ? data_tmp[done].length - 1 : time
data_tmp[value] = Array(time).fill(0); data_tmp[value] = Array(time).fill(0);
} }
data_tmp[value].push(object[value]); data_tmp[value].push(object[value]);
} }
}); });
} }
var data = $.map(data_tmp, function(value, index) { var data = $.map(data_tmp, function(value, index) {
value.splice(0,0,index); value.splice(0,0,index);
return [value]; return [value];
}); });
chart.load({ chart.load({
unload: true, unload: true,
columns: data columns: data
}); });
chart.axis.labels({y: property}); chart.axis.labels({y: property});
}
var create_chart = function(width, height, label_x, label_y, bind_to) {
return c3.generate({
size: {
width: width,
height: height
},
data: {
columns: [],
type: 'area-spline'
},
axis: {
x: { label: label_x },
y: { label: label_y }
},
point: { show: false },
bindto: bind_to
});
}
var run_simulation = function() {
var environment_variables = {}
$('#wrapper-settings input').each(function() {
switch(this.type) {
case 'text':
environment_variables[this.id] = Number(this.value);
break;
case 'checkbox':
environment_variables[this.id] = ($(this).is(':checked')) ? true : false;
break;
default:
break;
}
});
return environment_variables;
} }

View File

@ -1,10 +1,6 @@
// Add model parameters that can be edited prior to a model run // Add model parameters that can be edited prior to a model run
var initGUI = function(model_params) { var initGUI = function(model_params) {
var onSubmitCallback = function(param_name, value) {
// SEND SOCKET REQUEST
};
var addBooleanInput = function(name, value) { var addBooleanInput = function(name, value) {
var checked = (value) ? 'checked' : 'value'; var checked = (value) ? 'checked' : 'value';
@ -79,14 +75,14 @@ var initGUI = function(model_params) {
var param_str = String(option); var param_str = String(option);
switch (model_params[option]['type']) { switch (model_params[option]['type']) {
case "boolean": case 'boolean':
addBooleanInput(model_params[option]['label'], model_params[option]['value']); addBooleanInput(model_params[option]['label'], model_params[option]['value']);
break; break;
case "number": case 'number':
addSliderInput(model_params[option]['label'], model_params[option]['value']); addSliderInput(model_params[option]['label'], model_params[option]['value']);
break; break;
case "object": default:
addParamInput(param_str, model_params[option]); // catch-all for params that use Option class console.log('Input type not defined!');
break; break;
} }
} }

View File

@ -294,17 +294,17 @@
function importJSON(json, attributes, callback) { function importJSON(json, attributes, callback) {
reset() reset()
graph = json; graph = json;
model = attributes model = attributes;
// Create the graph itself // Create the graph itself
Graph(); Graph();
self.GraphVisualization.nodes = graph.nodes.length; self.GraphVisualization.nodes = graph.nodes.length;
self.GraphVisualization.links = graph.links.length; self.GraphVisualization.links = graph.links.length;
self.GraphVisualization.model = model self.GraphVisualization.model = model;
// Draw graph with default property and time for the first time // Draw graph with default property and time for the first time
update_data(model.dynamic[0].title, 0) update_data(model.dynamic[0].title, 0);
if (callback) { callback(); } if (callback) { callback(); }
} }

View File

@ -7,63 +7,63 @@ from xml.etree import ElementTree
class Model(): class Model():
def __init__(self, dump=True, dir_path='output'): def __init__(self, dump=True, dir_path='output'):
self.name = 'soil' self.name = 'soil'
self.dump = dump self.dump = dump
self.dir_path = dir_path self.dir_path = dir_path
def run(self, config): def run(self, config):
name = config['name'] name = config['name']
print('Using config(s): {name}'.format(name=name)) print('Using config(s): {name}'.format(name=name))
sim = SoilSimulation(**config) sim = SoilSimulation(**config)
sim.dir_path = os.path.join(self.dir_path, name) sim.dir_path = os.path.join(self.dir_path, name)
sim.dump = self.dump sim.dump = self.dump
print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump)) print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump))
sim.run_simulation() sim.run_simulation()
def get_trial(self, name, trial): def get_trial(self, name, trial):
graph = nx.read_gexf(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial))) graph = nx.read_gexf(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial)))
attributes = self.get_attributes(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial))) attributes = self.get_attributes(os.path.join(self.dir_path, name, '{}_trial_{}.gexf'.format(name, trial)))
json = {} json = {}
json['graph'] = nx.node_link_data(graph) json['graph'] = nx.node_link_data(graph)
json['models'] = attributes json['models'] = attributes
return json return json
def reset(self): def reset(self):
pass pass
def get_attributes(self, path_gexf): def get_attributes(self, path_gexf):
attributes = {} attributes = {}
tree = ElementTree.parse(path_gexf) tree = ElementTree.parse(path_gexf)
root = tree.getroot() root = tree.getroot()
ns = { 'gexf': 'http://www.gexf.net/1.2draft' } ns = { 'gexf': 'http://www.gexf.net/1.2draft' }
for mode in root[0].findall('gexf:attributes', ns): for mode in root[0].findall('gexf:attributes', ns):
attributes[mode.attrib['mode']] = [] attributes[mode.attrib['mode']] = []
for attribute in mode: for attribute in mode:
values = { values = {
'id' : attribute.attrib['id'], 'id' : attribute.attrib['id'],
'title' : attribute.attrib['title'], 'title' : attribute.attrib['title'],
'type' : attribute.attrib['type'] 'type' : attribute.attrib['type']
} }
attributes[mode.attrib['mode']].append(values) attributes[mode.attrib['mode']].append(values)
return attributes return attributes
class GraphVisualization(VisualizationElement): class GraphVisualization(VisualizationElement):
package_includes = [] package_includes = []
# TODO: esta por definir todos los ajustes de simulacion # TODO: esta por definir todos los ajustes de simulacion
def __init__(self, params=None): def __init__(self, params=None):
new_element = ("new funcion()") new_element = ("new funcion()")
self.js_code = "elements.push(" + new_element + ");" self.js_code = "elements.push(" + new_element + ");"
def render(self, model): def render(self, model):
pass pass