338 lines
9.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<link href="www/bootstrap.min.css" rel="stylesheet">
<script src="www/bootstrap.bundle.min.js"></script>
<script defer src="www/alpinejs.min.js"></script>
<style>
[x-cloak] {
display: none !important;
}
body {
background-color: #f8f9fa;
/* Create 20px x 20px Grid */
background-image:
linear-gradient(90deg, rgba(0, 0, 0, .03) 1px, transparent 1px),
linear-gradient(rgba(0, 0, 0, .03) 1px, transparent 1px);
background-size: 20px 20px;
height: 100vh;
margin: 0;
overflow: hidden;
}
.draggable-card {
width: 450px;
z-index: 1000;
user-select: none;
}
.drag-handle {
cursor: move;
}
.card {
transition: width 0.1s ease, height 0.1s ease, left 0.1s ease, top 0.1s ease;
}
.card[style*="cursor: move"] {
transition: none;
}
</style>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store("ui", {
topZ: 1000,
getNewZ() {return ++this.topZ}
});
Alpine.store("adapters", ["can0", "can1", "vcan0"]);
Alpine.store("selected_adapter", "");
Alpine.store("selected_bitrate", "");
Alpine.store("can_connected", false);
});
function windowBox(id, initialX = 50, initialY = 50) {
// Load saved data or user defaults
const saved = JSON.parse(localStorage.getItem(`win_${id}`)) || {x: initialX, y: initialY, min: false};
return {
id: id,
pos: {x: saved.x, y: saved.y},
lastPos: {x: saved.x, y: saved.y},
dragging: false,
minimized: saved.min,
fullscreen: false,
zIndex: Alpine.store('ui').topZ,
offset: {x: 0, y: 0},
init() {
// Move window in viewport when browser is other size
this.keepInBounds();
},
focus() {
this.zIndex = Alpine.store('ui').getNewZ();
},
startDrag(e) {
if (e.target.closest('button') || this.fullscreen) return;
this.focus();
this.dragging = true;
this.offset.x = e.clientX - this.pos.x;
this.offset.y = e.clientY - this.pos.y;
},
onDrag(e) {
if (!this.dragging) return;
// Calc new position
let newX = e.clientX - this.offset.x;
let newY = e.clientY - this.offset.y;
// Boundary Check
const margin = 20;
this.pos.x = Math.max(margin - 350, Math.min(newX, window.innerWidth - 50));
this.pos.y = Math.max(0, Math.min(newY, window.innerHeight - 40));
},
stopDrag() {
this.dragging = false;
this.save();
},
keepInBounds() {
if (this.pos.x > window.innerWidth) this.pos.x = window.innerWidth - 400;
if (this.pos.y > window.innerHeight) this.pos.y = 50;
},
save() {
localStorage.setItem(`win_${this.id}`, JSON.stringify({
x: this.pos.x,
y: this.pos.y,
min: this.minimized
}));
},
reset() {
localStorage.removeItem(`win_${this.id}`);
location.reload();
},
toggleMinimize() {
this.minimized = !this.minimized;
this.save();
},
toggleFullscreen() {
if (!this.fullscreen) {
this.lastPos = {...this.pos}; // Save Position
this.pos = {x: 0, y: 0};
this.fullscreen = true;
} else {
this.pos = {...this.lastPos}; // Back to old Position
this.fullscreen = false;
}
this.focus();
}
}
}
</script>
<script>
let socket = new WebSocket("ws://localhost:8000/echo");
socket.onopen = function (e) {
console.log("[open] Connection established");
console.log("Sending to server");
};
socket.onmessage = function (event) {
console.log(`[message] Data received from server: ${event.data}`);
let mes;
try {
mes = JSON.parse(event.data)
} catch {
mes = null
}
if (mes != null) {
handleCommand(mes)
} else {
console.log(`${event.data} is not valid JSON`)
}
};
socket.onclose = function (event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('[close] Connection died');
}
};
socket.onerror = function (error) {
console.log(`[error]`);
};
function handleCommand(command) {
switch (command.cmd) {
case "value":
console.log("CHANGE VALUE");
Alpine.store(command.name, command.value);
break;
}
}
</script>
</head>
<body>
<h1>Alox Debug Tool</h1>
<div x-data="windowBox('window_id' ,100, 100)" @mousemove.window="onDrag" @mouseup.window="stopDrag"
@mousedown="focus" class="card shadow-lg position-absolute"
:class="{ 'w-100 h-100 m-0 shadow-none': fullscreen }"
:style="`left: ${pos.x}px; top: ${pos.y}px; z-index: ${zIndex}; width: ${fullscreen ? '100vw' : '400px'};`"
x-cloak>
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center drag-handle"
@mousedown="startDrag" @dblclick="toggleFullscreen">
<h6 class="mb-0">CAN Interface</h6>
<div class="d-flex align-items-center gap-1">
<span class="badge me-1"
:class="socket.readyState === 1 ? 'bg-success' : 'bg-danger'">WS</span>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleFullscreen">
<span></span>
</button>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="minimized = !minimized">
<span x-text="minimized ? '+' : ''"></span>
</button>
</div>
</div>
<div x-show="!minimized" class="flex-grow-1 overflow-auto">
<div class="card-body">
<p>HIER DER INHALT DER COMPONENT</p>
</div>
</div>
</div>
<div x-data="windowBox('can_config', 100, 100)" @mousemove.window="onDrag" @mouseup.window="stopDrag"
@mousedown="focus" class="card shadow-lg position-absolute"
:class="{ 'w-100 h-100 m-0 shadow-none': fullscreen }"
:style="`left: ${pos.x}px; top: ${pos.y}px; z-index: ${zIndex}; width: ${fullscreen ? '100vw' : '400px'};`"
x-cloak>
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center drag-handle"
@mousedown="startDrag" @dblclick="toggleFullscreen">
<h6 class="mb-0">CAN Interface</h6>
<div class="d-flex align-items-center gap-1">
<span class="badge me-1"
:class="socket.readyState === 1 ? 'bg-success' : 'bg-danger'">WS</span>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleFullscreen">
<span></span>
</button>
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="minimized = !minimized">
<span x-text="minimized ? '+' : ''"></span>
</button>
</div>
</div>
<div x-show="!minimized" class="flex-grow-1 overflow-auto">
<div class="card-body">
<label class="form-label small fw-bold text-uppercase text-muted">Interface</label>
<div class="input-group mb-3" x-data="{ open: false }" @click.outside="open = false">
<button class="btn btn-outline-secondary dropdown-toggle" type="button"
@click="open = !open" :disabled="$store.can_connected">
Adapter
</button>
<ul class="dropdown-menu" :class="{ 'show': open }" x-show="open"
style="display: block;">
<template x-for="adapter in $store.adapters">
<li>
<button class="dropdown-item" type="button"
x-text="adapter"
@click="$store.selected_adapter = adapter; open = false"></button>
</li>
</template>
</ul>
<input type="text" class="form-control bg-light" readonly
:value="$store.selected_adapter || 'Select Interface...'">
</div>
<label class="form-label small fw-bold text-uppercase text-muted">Bitrate (kbps)</label>
<div class="input-group mb-4" x-data="{ open: false }" @click.outside="open = false">
<button class="btn btn-outline-secondary dropdown-toggle" type="button"
@click="open = !open" :disabled="$store.can_connected">
Speed
</button>
<ul class="dropdown-menu" :class="{ 'show': open }" x-show="open"
style="display: block;">
<template
x-for="rate in ['10', '20', '50', '100', '125', '250', '500', '800', '1000']">
<li>
<button class="dropdown-item" type="button"
x-text="rate + ' kbps'"
@click="$store.selected_bitrate = rate; open = false"></button>
</li>
</template>
</ul>
<input type="text" class="form-control bg-light" readonly
:value="$store.selected_bitrate ? $store.selected_bitrate + ' kbps' : 'Select Speed...'">
</div>
<div class="d-grid">
<button x-show="!$store.can_connected" class="btn btn-primary btn-lg"
type="button"
:disabled="!$store.selected_adapter || !$store.selected_bitrate"
@click="socket.send(JSON.stringify({cmd: 'connect', adapter: $store.selected_adapter, bitrate: parseInt($store.selected_bitrate)}))">
Connect to CAN
</button>
<button x-show="$store.can_connected" x-cloak class="btn btn-danger btn-lg"
type="button" @click="socket.send(JSON.stringify({cmd: 'disconnect'}))">
Disconnect
</button>
</div>
</div>
</div>
</div>
<div x-data="windowBox('can_log', 500, 50)" @mousemove.window="onDrag" @mouseup.window="stopDrag"
@mousedown="focus" class="card shadow-lg position-absolute draggable-card"
:style="`left: ${pos.x}px; top: ${pos.y}px; z-index: ${zIndex}; width: ${fullscreen ? '100vw' : '500px'};`"
x-cloak>
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center drag-handle"
@mousedown="startDrag">
<h6 class="mb-0">📜 CAN Log</h6>
<div class="d-flex gap-1">
<button class="btn btn-sm btn-outline-light py-0" @click="toggleMinimize">
<span x-text="minimized ? '+' : ''"></span>
</button>
</div>
</div>
<div x-show="!minimized" class="card-body bg-dark text-success font-monospace small"
style="height: 200px; overflow-y: auto;">
<div>[0.001] ID: 0x123 Data: FF AA 00</div>
<div>[0.005] ID: 0x456 Data: 12 34 56</div>
</div>
</div>
</body>
</html>