From 611dc223e174aa00d630068f574b0d0d03c05bf6 Mon Sep 17 00:00:00 2001 From: dinodev24 <149423946+dinodev24@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:52:53 -0800 Subject: [PATCH] Basic example working. 1. The user can choose which parameters to run. 2. Progress bars show the progress of the simulation. 3. Simulation summary is parsed from markdown. --- cace/cace_web.py | 139 ++++++++++++++++++++++++++++++++++++++ cace/web/index.html | 35 ++++++++++ cace/web/runsim.html | 67 ++++++++++++++++++ cace/web/simresults.html | 42 ++++++++++++ cace/web/static/style.css | 17 +++++ 5 files changed, 300 insertions(+) create mode 100644 cace/cace_web.py create mode 100644 cace/web/index.html create mode 100644 cace/web/runsim.html create mode 100644 cace/web/simresults.html create mode 100644 cace/web/static/style.css diff --git a/cace/cace_web.py b/cace/cace_web.py new file mode 100644 index 0000000..343e70a --- /dev/null +++ b/cace/cace_web.py @@ -0,0 +1,139 @@ +from flask import Flask, render_template, request, Response +from .parameter import ParameterManager + +import queue +import json +import os + +# TODO: These should be options on the web interface +parameter_manager = ParameterManager(max_runs=None, run_path=None, jobs=None) +parameter_manager.find_datasheet(os.getcwd()) + +any_queue = queue.Queue() + +app = Flask(__name__, template_folder='web', static_folder='web/static') + + +@app.route('/') +def homepage(): + pnames = parameter_manager.get_all_pnames() + data = [{'name': pname} for pname in pnames] + + return render_template(template_name_or_list='index.html', data=data) + + +@app.route('/runsim', methods=['POST']) +def runsim(): + parameter_manager.results = {} + parameter_manager.result_types = {} + + params = request.form.getlist('selected_params') + + for param in params: + parameter_manager.queue_parameter( + param, + start_cb=lambda param, steps: ( + any_queue.put( + {'task': 'start', 'param': param, 'steps': steps} + ) + ), + step_cb=lambda param: ( + any_queue.put({'task': 'step', 'param': param}) + ), + cancel_cb=lambda param: ( + any_queue.put({'task': 'cancel', 'param': param}) + ), + end_cb=lambda param: any_queue.put( + {'task': 'end', 'param': param} + ), + ) + + # TODO: These should be options on the web interface + parameter_manager.set_runtime_options('force', False) + parameter_manager.set_runtime_options('noplot', False) + parameter_manager.set_runtime_options('nosim', False) + parameter_manager.set_runtime_options('sequential', False) + parameter_manager.set_runtime_options('netlist_source', 'best') + parameter_manager.set_runtime_options('parallel_parameters', 4) + + parameter_manager.run_parameters_async() + return render_template(template_name_or_list='runsim.html', params=params) + + +def generate_sse(): + num_params = parameter_manager.num_parameters() + datasheet = parameter_manager.datasheet['parameters'] + + params_completed = 0 + while num_params != params_completed: + aqg = any_queue.get() + + if aqg['task'] == 'end': + params_completed += 1 + elif aqg['task'] == 'end_stream': + return + + aqg['param'] = list(datasheet.keys())[ + list(datasheet.values()).index(aqg['param']) + ] + yield f'data: {json.dumps(aqg)}\n\n' + + data = {'task': 'close'} + yield f'data: {json.dumps(data)}\n\n' + + +@app.route('/stream') +def stream(): + return Response(generate_sse(), content_type='text/event-stream') + + +@app.route('/end_stream', methods=['POST']) +def end_stream(): + any_queue.put({'task': 'end_stream'}) + return '', 200 + + +@app.route('/simresults') +def simresults(): + parameter_manager.join_parameters() + result = [] + + summary_lines = parameter_manager.summarize_datasheet().split('\n')[7:-2] + lengths = { + param: len( + list( + parameter_manager.datasheet['parameters'][param]['spec'].keys() + ) + ) + for param in parameter_manager.get_all_pnames() + } + for param in parameter_manager.get_result_types().keys(): + total = 0 + for i in parameter_manager.get_all_pnames(): + if i == param: + for j in range(lengths[param]): + row = summary_lines[total + j].split('|') + result.append( + { + 'parameter_str': row[1], + 'tool_str': row[2], + 'result_str': row[3], + 'min_limit_str': row[4], + 'min_value_str': row[5], + 'max_limit_str': row[6], + 'max_value_str': row[7], + 'typ_limit_str': row[8], + 'typ_value_str': row[9], + 'status_str': row[10], + } + ) + + total += lengths[i] + + return render_template( + template_name_or_list='simresults.html', data=result + ) + + +def web(): + app.run(debug=True) diff --git a/cace/web/index.html b/cace/web/index.html new file mode 100644 index 0000000..30051fd --- /dev/null +++ b/cace/web/index.html @@ -0,0 +1,35 @@ + + +
+ + + + + + + + diff --git a/cace/web/runsim.html b/cace/web/runsim.html new file mode 100644 index 0000000..02773e5 --- /dev/null +++ b/cace/web/runsim.html @@ -0,0 +1,67 @@ + + + + + + + + +Select | +Name | +
---|---|
{{ param }} | ++ + | +
Click the button above after all simulations have finished
+ + + + diff --git a/cace/web/simresults.html b/cace/web/simresults.html new file mode 100644 index 0000000..b6c972e --- /dev/null +++ b/cace/web/simresults.html @@ -0,0 +1,42 @@ + + + + + + + + +Parameter | +Tool | +Result | +Minimum Limit | +Minimum Value | +Typical Limit | +Typical Value | +Maximum Limit | +Maximum Value | +Status | +
---|---|---|---|---|---|---|---|---|---|
{{ row.parameter_str }} | +{{ row.tool_str }} | +{{ row.result_str }} | +{{ row.min_limit_str }} | +{{ row.min_value_str }} | +{{ row.max_limit_str }} | +{{ row.max_value_str }} | +{{ row.typ_limit_str }} | +{{ row.typ_value_str }} | +{{ row.status_str }} | +