NorthStar-Endurance-TestBench/single-valve-bench/static/index.html

183 lines
5.4 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<style>
.container {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.plots {
flex: 1;
}
.pid-panel {
margin-left: 20px;
padding: 10px;
border: 1px solid #aaa;
border-radius: 8px;
width: 220px;
background-color: #f9f9f9;
}
.pid-panel label {
display: block;
margin-top: 8px;
}
</style>
</head>
<body>
<h2>Valve Controller</h2>
<button onclick="connectValve()">Connect</button>
<br><br>
<label>Setpoint:
<input type="number" id="setpointInput" value="0" step="0.1">
<button onclick="setSetpoint()">Send</button>
</label>
<label style="margin-left: 15px;">
<input type="checkbox" id="setpointEnable"> Enable
</label>
<br><br>
<label>Speed:
<input type="number" id="speedInput" value="0" step="0.01">
<button onclick="setSpeed()">Send</button>
</label>
<br><br>
<button onclick="startPRBS()">PRBS</button>
<button onclick="startRecording()">Start Recording</button>
<button onclick="stopRecording()">Stop Recording</button>
<br><br>
<div class="container">
<!-- Left: plots -->
<div class="plots">
<div id="plot_pos" style="width:100%;height:300px;"></div>
<div id="plot_speed" style="width:100%;height:300px;"></div>
</div>
<!-- Right: PID menu -->
<div class="pid-panel">
<h3>PID Coefficients</h3>
<label>Kp:
<input type="number" id="kpInput" value="10.0" step="0.01">
</label>
<label>Ki:
<input type="number" id="kiInput" value="0.05" step="0.01">
</label>
<label>Tolerance:
<input type="number" id="kdInput" value="0.005" step="0.01">
</label>
<br>
<button onclick="sendPID()">Send PID</button>
</div>
</div>
<script>
let layout_pos = {
title: 'Live Valve Position',
xaxis: { title: 'Time (s)' },
yaxis: { title: 'Position / Setpoint ' }
};
Plotly.newPlot('plot_pos', [
{ x: [], y: [], name: 'Position', mode: 'lines', line: { color: 'blue' } },
{ x: [], y: [], name: 'Setpoint', mode: 'lines', line: { color: 'red', dash: 'dash' } },
], layout_pos);
let layout_speed = {
title: 'Live Valve Speed',
xaxis: { title: 'Time (s)' },
yaxis: { title: 'Setpoint / Speed' }
};
Plotly.newPlot('plot_speed', [
{ x: [], y: [], name: 'Speed', mode: 'lines', line: { color: 'orange' } },
{ x: [], y: [], name: 'Speed_sp', mode: 'lines', line: { color: 'green', dash: 'dash' } },
{ x: [], y: [], name: 'Kp_term', mode: 'lines' },
{ x: [], y: [], name: 'Ki_term', mode: 'lines' },
{ x: [], y: [], name: 'Kd_term', mode: 'lines' }
], layout_speed);
async function connectValve() {
const res = await fetch('/connect', { method: 'POST' });
const data = await res.json();
alert("Valve connected: " + data.status);
}
async function fetchData() {
const res = await fetch('/position');
const data = await res.json();
const t0 = data.time.length > 0 ? data.time[0] : Date.now() / 1000;
const t = data.time.map(ts => ts - t0); // relative time
Plotly.react('plot_pos', [
{ x: t, y: data.position, name: 'Position', mode: 'lines', line: { color: 'blue' } },
{ x: t, y: data.setpoint, name: 'Setpoint', mode: 'lines', line: { color: 'red', dash: 'dash' } },
], layout_pos);
Plotly.react('plot_speed', [
{ x: t, y: data.speed, name: 'Speed', mode: 'lines', line: { color: 'orange'} },
{ x: t, y: data.speed_sp, name: 'Speed_sp', mode: 'lines', line: { color: 'green', dash: 'dash' } },
{ x: t, y: data.Kp_term, name: 'Kp_term', mode: 'lines'},
{ x: t, y: data.Ki_term, name: 'Ki_term', mode: 'lines'},
{ x: t, y: data.Kd_term, name: 'Kd_term', mode: 'lines'}
], layout_speed);
}
setInterval(fetchData, 50); // 20 Hz update
async function setSetpoint() {
let sp = parseFloat(document.getElementById("setpointInput").value);
let enabled = document.getElementById("setpointEnable").checked;
await fetch('/setpoint', {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ setpoint: sp, enabled: enabled })
});
}
async function setSpeed() {
let sp = parseFloat(document.getElementById("speedInput").value);
await fetch('/speed', {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ speed_sp: sp })
});
}
async function sendPID() {
let kp = parseFloat(document.getElementById("kpInput").value);
let ki = parseFloat(document.getElementById("kiInput").value);
let tol = parseFloat(document.getElementById("kdInput").value);
await fetch('/pid', {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ kp: kp, ki: ki, tol: tol })
});
}
async function startRecording() {
await fetch('/start_recording', { method: 'POST' });
}
async function startPRBS() {
await fetch('/prbs', { method: 'POST' });
}
async function stopRecording() {
const res = await fetch('/stop_recording', { method: 'POST' });
const data = await res.json();
alert("Recording stopped. CSV saved at:\n" + data.file);
}
</script>
</body>
</html>