🚧 Make analog indicators clickable
This commit is contained in:
Vendored
+90
@@ -913,6 +913,96 @@
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "wago-sim: pytest barclick",
|
||||
"type": "shell",
|
||||
"command": ".venv\\Scripts\\python.exe",
|
||||
"args": [
|
||||
"-m",
|
||||
"pytest",
|
||||
"tests",
|
||||
"-q",
|
||||
"--tb=short"
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "wago-sim: serve barclick",
|
||||
"type": "shell",
|
||||
"command": ".venv\\Scripts\\python.exe",
|
||||
"args": [
|
||||
"-m",
|
||||
"wago_sim.cli",
|
||||
"serve",
|
||||
"--node",
|
||||
"config/pump_node.yaml",
|
||||
"--host",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
"5020",
|
||||
"--web-port",
|
||||
"8088"
|
||||
],
|
||||
"isBackground": true,
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "kill ports 4",
|
||||
"type": "shell",
|
||||
"command": ".venv\\Scripts\\python.exe",
|
||||
"args": [
|
||||
"-c",
|
||||
"import subprocess; subprocess.run(['powershell','-NoProfile','-Command','Get-NetTCPConnection -LocalPort 5020,8088 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique | ForEach-Object { Stop-Process -Id $_ -Force }'], capture_output=True); print('killed')"
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "wago-sim: serve barclick2",
|
||||
"type": "shell",
|
||||
"command": ".venv\\Scripts\\python.exe",
|
||||
"args": [
|
||||
"-m",
|
||||
"wago_sim.cli",
|
||||
"serve",
|
||||
"--node",
|
||||
"config/pump_node.yaml",
|
||||
"--host",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
"5020",
|
||||
"--web-port",
|
||||
"8088"
|
||||
],
|
||||
"isBackground": true,
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "wago-sim: pytest webui final",
|
||||
"type": "shell",
|
||||
"command": ".venv\\Scripts\\python.exe",
|
||||
"args": [
|
||||
"-m",
|
||||
"pytest",
|
||||
"tests/test_webui.py",
|
||||
"-q",
|
||||
"--tb=short"
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "stop barclick serve",
|
||||
"type": "shell",
|
||||
"command": ".venv\\Scripts\\python.exe",
|
||||
"args": [
|
||||
"-c",
|
||||
"import subprocess; subprocess.run(['powershell','-NoProfile','-Command','Get-NetTCPConnection -LocalPort 5020,8088 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique | ForEach-Object { Stop-Process -Id $_ -Force }'], capture_output=True); print('stopped')"
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -910,6 +910,10 @@ Terminal monitors (§9) suffice for single engineers, but FAT benches are
|
||||
defined); `pulse` (with duration) and `ramp` (target + duration) actions
|
||||
on DI/DO/AI. All writes go through the datastore's Modbus-equivalent
|
||||
paths (FR-9.2.6) so mirrors, watchdog, and statistics stay consistent.
|
||||
Clicking an analog **bar indicator** (requested 2026-10-04) sets the
|
||||
value proportionally to the click position within the channel's
|
||||
display range (FR-10.2.14) — AI = field side, AO = master-side force;
|
||||
exact values remain enterable via the channel list.
|
||||
- FR-10.2.5: **Diagnostics view**: watchdog status (0x1006), statistics
|
||||
block (0x1029, with reset button), module diagnosis (0x1050), LED error
|
||||
registers (0x1020/0x1021) — mirroring WBM pages of the real device —
|
||||
|
||||
@@ -312,7 +312,8 @@ function renderRail() {
|
||||
html += `<span class="abar-cell">
|
||||
<span class="abar-label top-label">${chNo}</span>
|
||||
<span class="lim-led lim-over" id="${id}-over" title="overflow (> ${rg.max})"></span>
|
||||
<span class="abar" id="${id}" title="${ch.name} (${ch.address}, MB ${ch.modbus})">
|
||||
<span class="abar" id="${id}" title="${ch.name} (${ch.address}, MB ${ch.modbus})"
|
||||
onclick="clickBar('${ch.kind}', ${mod.index}, ${channelNo(ch.address)}, event)">
|
||||
<span class="abar-fill"></span></span>
|
||||
<span class="lim-led lim-under" id="${id}-under" title="underflow (< ${rg.min})"></span>
|
||||
<span class="abar-label bottom-label">${chNo}</span>
|
||||
@@ -392,6 +393,28 @@ async function clickChannel(kind, module, channel) {
|
||||
}
|
||||
}
|
||||
|
||||
async function clickBar(kind, module, channel, ev) {
|
||||
if (READONLY) return;
|
||||
const bar = ev.currentTarget;
|
||||
const rect = bar.getBoundingClientRect();
|
||||
// click height -> fraction of the bar: bottom = range min, top = max
|
||||
const frac = Math.min(1, Math.max(0, 1 - (ev.clientY - rect.top) / rect.height));
|
||||
const chInfo = findChannel(kind, module, channel);
|
||||
const rg = (chInfo && chInfo.range) || {min: 0, max: 4095};
|
||||
const value = Math.round(rg.min + frac * (rg.max - rg.min));
|
||||
try {
|
||||
await api(`/api/channels/${kind}/${module}/${channel}`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({action: "set", value}),
|
||||
});
|
||||
const res = await api("/api/values");
|
||||
applyValues(res.values);
|
||||
} catch (e) {
|
||||
console.warn("write rejected:", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- diagnostics ----------------
|
||||
|
||||
async function renderDiagnostics(diag) {
|
||||
|
||||
@@ -190,6 +190,7 @@ h2 { margin: 4px 0 14px; }
|
||||
border-radius: 3px;
|
||||
overflow: hidden; /* clip the fill inside the rounded bar */
|
||||
display: inline-block;
|
||||
cursor: pointer; /* clickable: sets value at click height */
|
||||
}
|
||||
.abar-fill {
|
||||
position: absolute; bottom: 0; left: 0; right: 0; height: 0%;
|
||||
|
||||
Reference in New Issue
Block a user