Skip to content

Commit

Permalink
Basic example working.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dinodev24 committed Feb 8, 2025
1 parent 8f9a04c commit 611dc22
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 0 deletions.
139 changes: 139 additions & 0 deletions cace/cace_web.py
Original file line number Diff line number Diff line change
@@ -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)
35 changes: 35 additions & 0 deletions cace/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="static/style.css" />
</head>
<body>
<form method="POST" action="/runsim">
<table>
<thead>
<tr>
<th>Select</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
<td class="checkbox-column">
<input
type="checkbox"
name="selected_params"
value="{{ row.name }}"
/>
</td>
<td>{{ row.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Submit</button>
</form>
</body>
</html>
67 changes: 67 additions & 0 deletions cace/web/runsim.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="static/style.css" />
</head>
<body>
<table>
<thead>
<tr>
<th>Select</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{% for param in params %}
<tr>
<td>{{ param }}</td>
<td>
<progress id="{{param}}" value="0" max="100"></progress>
</td>
</tr>
{% endfor %}
</tbody>
</table>

<button onclick="window.open('http://localhost:5000/simresults','_blank')">Simulation Results</button>
<p>Click the button above after all simulations have finished</p>

<script>
// Connect to the SSE stream
const eventSource = new EventSource("/stream");

// Listen for messages from the server
eventSource.onmessage = function (event) {
let data = JSON.parse(event.data);
let task = data.task;
if (task == "close") {
eventSource.close();
} else if (task == "start") {
console.log("start");
let outputDiv = document.getElementById(data.param);
outputDiv.max = data.steps;
} else if (task == "step") {
console.log("step");
let outputDiv = document.getElementById(data.param);
outputDiv.value++;
} else if (task == "cancel") {
} else if (task == "end") {
}
};

window.onbeforeunload = function () {
// Send a POST request to the /ping endpoint and ignore the response
fetch(`${window.location.origin}/end_stream`, {
method: "POST",
});
eventSource.close();
};

eventSource.onerror = function () {
console.error("Error occurred while receiving SSE.");
};
</script>
</body>
</html>
42 changes: 42 additions & 0 deletions cace/web/simresults.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="static/style.css" />
</head>
<body>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Tool</th>
<th>Result</th>
<th>Minimum Limit</th>
<th>Minimum Value</th>
<th>Typical Limit</th>
<th>Typical Value</th>
<th>Maximum Limit</th>
<th>Maximum Value</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
<td>{{ row.parameter_str }}</td>
<td>{{ row.tool_str }}</td>
<td>{{ row.result_str }}</td>
<td>{{ row.min_limit_str }}</td>
<td>{{ row.min_value_str }}</td>
<td>{{ row.max_limit_str }}</td>
<td>{{ row.max_value_str }}</td>
<td>{{ row.typ_limit_str }}</td>
<td>{{ row.typ_value_str }}</td>
<td>{{ row.status_str }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
17 changes: 17 additions & 0 deletions cace/web/static/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
table {
border-collapse: collapse;
margin: 25px 0;
font-size: 18px;
text-align: left;
}
table th,
table td {
padding: 8px;
border: 1px solid #ddd;
}
table th {
background-color: #f2f2f2;
}
progress {
width: 300px;
}

0 comments on commit 611dc22

Please sign in to comment.