mirror of
https://github.com/cjfranko/NTP-Timeturner.git
synced 2025-11-08 10:22:02 +00:00
feat: add PTP UI and switch to default styling for Hachi-TimeTransformer
Co-authored-by: aider (openai/gpt-5) <aider@aider.chat>
This commit is contained in:
parent
8eb4c6f2da
commit
646e927e46
2 changed files with 113 additions and 3 deletions
|
|
@ -3,13 +3,13 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Fetch | Hachi</title>
|
<title>Hachi-TimeTransformer</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<!-- Using default browser styles -->
|
||||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<img src="assets/header.png" alt="NTP Timeturner" class="header-logo">
|
<h1>Hachi-TimeTransformer</h1>
|
||||||
|
|
||||||
<!-- Mock Data Controls (hidden by default) -->
|
<!-- Mock Data Controls (hidden by default) -->
|
||||||
<div id="mock-controls" class="card full-width" style="display: none;">
|
<div id="mock-controls" class="card full-width" style="display: none;">
|
||||||
|
|
@ -55,6 +55,15 @@
|
||||||
<p id="interfaces">--</p>
|
<p id="interfaces">--</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- PTP -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>PTP</h2>
|
||||||
|
<p><strong>Supported:</strong> <span id="ptp-supported">unknown</span></p>
|
||||||
|
<p><strong>Interface:</strong> <span id="ptp-interface">--</span></p>
|
||||||
|
<p><strong>Daemon:</strong> <span id="ptp-daemon">--</span></p>
|
||||||
|
<p><strong>Offset (ns):</strong> <span id="ptp-offset">--</span></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Controls -->
|
<!-- Controls -->
|
||||||
<div class="card full-width collapsible-card">
|
<div class="card full-width collapsible-card">
|
||||||
<div class="toggle-header" id="controls-toggle">
|
<div class="toggle-header" id="controls-toggle">
|
||||||
|
|
@ -107,6 +116,23 @@
|
||||||
<button id="nudge-up">+</button>
|
<button id="nudge-up">+</button>
|
||||||
<span id="nudge-message"></span>
|
<span id="nudge-message"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label for="ptp-interface">PTP Interface:</label>
|
||||||
|
<input type="text" id="ptp-interface-input" placeholder="e.g., eth0">
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label for="ptp-phc-index">PHC Index (optional):</label>
|
||||||
|
<input type="number" id="ptp-phc-index" min="0">
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label for="ptp-extra-args">Extra statime args (optional):</label>
|
||||||
|
<input type="text" id="ptp-extra-args" placeholder="--debug --announce-interval 1">
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<button id="start-ptp">Start PTP</button>
|
||||||
|
<button id="stop-ptp">Stop PTP</button>
|
||||||
|
<span id="ptp-message"></span>
|
||||||
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label for="date-input">Set System Date:</label>
|
<label for="date-input">Set System Date:</label>
|
||||||
<input type="text" id="date-input" placeholder="YYYY-MM-DD" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
|
<input type="text" id="date-input" placeholder="YYYY-MM-DD" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
|
||||||
|
|
@ -137,5 +163,6 @@
|
||||||
<script src="icon-map.js"></script>
|
<script src="icon-map.js"></script>
|
||||||
<script src="mock-data.js"></script>
|
<script src="mock-data.js"></script>
|
||||||
<script src="script.js"></script>
|
<script src="script.js"></script>
|
||||||
|
<script src="ptp.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
83
static/ptp.js
Normal file
83
static/ptp.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
(function () {
|
||||||
|
function $(id) { return document.getElementById(id); }
|
||||||
|
function setText(id, val) {
|
||||||
|
var el = $(id);
|
||||||
|
if (el) el.textContent = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPtpStatus() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/status');
|
||||||
|
if (!res.ok) return;
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
const supported = data.ptp_supported;
|
||||||
|
const iface = data.ptp_interface;
|
||||||
|
const running = data.ptp_daemon_running;
|
||||||
|
const offset = data.ptp_offset_ns;
|
||||||
|
|
||||||
|
if (typeof supported !== 'undefined') setText('ptp-supported', supported ? 'Yes' : 'No');
|
||||||
|
if (typeof iface !== 'undefined' && iface !== null) setText('ptp-interface', iface);
|
||||||
|
if (typeof running !== 'undefined') setText('ptp-daemon', running ? 'Running' : 'Stopped');
|
||||||
|
if (typeof offset !== 'undefined' && offset !== null) setText('ptp-offset', String(offset));
|
||||||
|
} catch (_) {
|
||||||
|
// ignore fetch errors; UI will show last known values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startPtp() {
|
||||||
|
const ifaceVal = $('ptp-interface-input')?.value.trim();
|
||||||
|
const phcIdxStr = $('ptp-phc-index')?.value;
|
||||||
|
const extraArgsStr = $('ptp-extra-args')?.value.trim();
|
||||||
|
|
||||||
|
const body = {};
|
||||||
|
if (ifaceVal) body.interface = ifaceVal;
|
||||||
|
if (phcIdxStr !== '' && phcIdxStr != null) {
|
||||||
|
const n = Number(phcIdxStr);
|
||||||
|
if (!Number.isNaN(n)) body.phcIndex = n;
|
||||||
|
}
|
||||||
|
if (extraArgsStr) {
|
||||||
|
body.args = extraArgsStr.split(/\s+/).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/ptp/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
const j = await res.json().catch(() => ({}));
|
||||||
|
setText('ptp-message', j.message || (res.ok ? 'PTP start requested' : 'Failed to start PTP'));
|
||||||
|
} catch (_) {
|
||||||
|
setText('ptp-message', 'Failed to start PTP');
|
||||||
|
}
|
||||||
|
fetchPtpStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopPtp() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/ptp/stop', { method: 'POST' });
|
||||||
|
const j = await res.json().catch(() => ({}));
|
||||||
|
setText('ptp-message', j.message || (res.ok ? 'PTP stop requested' : 'Failed to stop PTP'));
|
||||||
|
} catch (_) {
|
||||||
|
setText('ptp-message', 'Failed to stop PTP');
|
||||||
|
}
|
||||||
|
fetchPtpStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const startBtn = $('start-ptp');
|
||||||
|
const stopBtn = $('stop-ptp');
|
||||||
|
if (startBtn) startBtn.addEventListener('click', startPtp);
|
||||||
|
if (stopBtn) stopBtn.addEventListener('click', stopPtp);
|
||||||
|
|
||||||
|
fetchPtpStatus();
|
||||||
|
setInterval(fetchPtpStatus, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
Loading…
Add table
Add a link
Reference in a new issue