Files
fhd_scan_web_app/frontend/js/app.js
T
gardentech f34817d5b6 Initial commit: scan_web integrated recording UI
FastAPI backend + vanilla JS frontend for LiDAR/camera startup, camera
settings, and rosbag recording, unifying the previous scan_gui/scan_gui_dual/
scan_gui_triple desktop tools into one web app.
2026-08-07 14:26:00 +09:00

133 lines
4.5 KiB
JavaScript

import { startStatusSocket } from "/js/status-panel.js";
import { buildCameraGrid, setPreviewActive } from "/js/camera-preview.js";
import { buildCameraTabs } from "/js/camera-settings.js";
import { initRecordingPanel } from "/js/recording-panel.js";
import { initLogPanel } from "/js/log-panel.js";
import { initPointcloudView } from "/js/pointcloud-view.js";
async function getJSON(url, opts) {
const res = await fetch(url, opts);
if (!res.ok) throw new Error(`${url}: ${res.status} ${await res.text()}`);
return res.json();
}
const OVERLAY_IDS = ["overlay-settings", "overlay-recording", "overlay-log"];
let pointcloudView = null;
function anyOverlayOpen() {
return OVERLAY_IDS.some((id) => !document.getElementById(id).classList.contains("hidden"));
}
// The point-cloud render loop keeps running (cheap-ish) but there's no
// reason to keep painting frames the operator can't see behind a full-screen
// overlay — pause on open, resume once every overlay is closed.
function syncPointcloudPaused() {
if (pointcloudView) pointcloudView.setPaused(anyOverlayOpen());
}
function openOverlay(id) {
document.getElementById(id).classList.remove("hidden");
syncPointcloudPaused();
}
function closeOverlay(id) {
document.getElementById(id).classList.add("hidden");
syncPointcloudPaused();
}
// Camera MJPEG only needs to stream while its tab is actually visible inside
// the open Settings overlay — otherwise it's wasted phone battery/bandwidth
// behind a closed overlay (same reasoning M3's point-cloud stream will need
// for the scan view itself).
function syncPreviewActive() {
const overlaySettings = document.getElementById("overlay-settings");
const previewPane = document.getElementById("pane-camera-preview");
const active = !overlaySettings.classList.contains("hidden") && previewPane.classList.contains("active");
setPreviewActive(active);
}
function setupOverlays() {
document.querySelectorAll("[data-close]").forEach((el) => {
el.onclick = () => {
closeOverlay(el.dataset.close);
syncPreviewActive();
};
});
document.getElementById("btn-settings").onclick = () => {
openOverlay("overlay-settings");
syncPreviewActive();
};
document.getElementById("btn-rec-open").onclick = () => openOverlay("overlay-recording");
document.getElementById("btn-open-log").onclick = () => openOverlay("overlay-log");
}
function setupPaneTabs() {
const btns = document.querySelectorAll(".pane-tab-btn[data-pane]");
btns.forEach((btn) => {
btn.onclick = () => {
btns.forEach((b) => b.classList.remove("active"));
document.querySelectorAll(".pane-tab").forEach((p) => p.classList.remove("active"));
btn.classList.add("active");
document.getElementById(btn.dataset.pane).classList.add("active");
syncPreviewActive();
};
});
}
async function main() {
setupOverlays();
setupPaneTabs();
pointcloudView = initPointcloudView();
const status = await getJSON("/api/status");
await buildCameraGrid(status.cameras);
await buildCameraTabs(status.cameras);
initRecordingPanel(status.default_save_dir);
initLogPanel();
document.getElementById("btn-lidar-start").onclick = async () => {
await getJSON("/api/lidar/start", { method: "POST" });
};
document.getElementById("btn-lidar-stop").onclick = async () => {
await getJSON("/api/lidar/stop", { method: "POST" });
};
document.getElementById("btn-estop").onclick = async () => {
if (!confirm("모든 프로세스(LiDAR/카메라/녹화)를 즉시 정지합니다. 계속할까요?")) return;
await getJSON("/api/system/estop", { method: "POST" });
};
document.getElementById("btn-fastlio-test-start").onclick = async () => {
try {
await getJSON("/api/fastlio/start", { method: "POST" });
} catch (e) {
alert(String(e));
}
};
document.getElementById("btn-fastlio-test-stop").onclick = async () => {
try {
await getJSON("/api/fastlio/stop", { method: "POST" });
} catch (e) {
alert(String(e));
}
};
const btnViewOrbit = document.getElementById("btn-view-orbit");
const btnViewLidar = document.getElementById("btn-view-lidar");
btnViewOrbit.onclick = () => {
pointcloudView.setViewMode("orbit");
btnViewOrbit.className = "btn btn-blue";
btnViewLidar.className = "btn btn-gray";
};
btnViewLidar.onclick = () => {
pointcloudView.setViewMode("lidar");
btnViewLidar.className = "btn btn-blue";
btnViewOrbit.className = "btn btn-gray";
};
startStatusSocket();
}
main().catch((e) => {
console.error(e);
alert("초기화 실패: " + e);
});