Implement monitoring infrastructure

This commit is contained in:
2022-09-22 19:27:01 +02:00
parent 2af0136703
commit 7fa635170a
15 changed files with 499 additions and 29 deletions

View File

@@ -9,6 +9,8 @@
import Mining from "./pages/mining/Mining.svelte";
import Footer from "./Footer.svelte";
import BaseLayout from "./BaseLayout.svelte";
import Monitoring from "./pages/monitoring/Monitoring.svelte";
import Bar from "./pages/playground/Bar.svelte";
onMount(async () => {
const res = await fetch("/user/me");
@@ -25,7 +27,7 @@
</BaseLayout>
</Route>
<Route path="/foo">
<Route path="foo">
<BaseLayout>
<section class="hero is-danger is-fullheight">
<div class="hero-body">
@@ -38,19 +40,19 @@
</BaseLayout>
</Route>
<Route path="/bar">
<BaseLayout>bar</BaseLayout>
<Route path="bar">
<BaseLayout><Bar /></BaseLayout>
</Route>
<Route path="/monitoring">
<BaseLayout>monitoring</BaseLayout>
<Route path="monitoring/*">
<BaseLayout><Monitoring /></BaseLayout>
</Route>
<Route path="/mining">
<Route path="mining">
<Mining />
</Route>
<Route path="/stats">
<Route path="stats">
<BaseLayout>stats</BaseLayout>
</Route>

View File

@@ -10,7 +10,7 @@
<Link
to={target}
class="navbar-item is-tab {$location.pathname === target
class="navbar-item is-tab {$location.pathname.startsWith(target)
? 'is-active'
: ''}"
>

View File

@@ -18,6 +18,11 @@ $fa-font-path: "@fortawesome/fontawesome-free/webfonts";
@import "@fortawesome/fontawesome-free/scss/v4-shims.scss";
// https://github.com/mefechoel/svelte-navigator#what-are-the-weird-rectangles-around-the-headings-in-my-app
h1:focus {
h1:focus,
h2:focus,
h3:focus,
h4:focus,
h5:focus,
h6:focus {
outline: none;
}
}

View File

@@ -0,0 +1,27 @@
<script lang="ts">
import { Link, Route, useParams } from "svelte-navigator";
import Viewer from "./Viewer.svelte";
const uuids = ["8b9faf9f-9470-4a50-b405-0af5f0152550"];
</script>
<section class="section">
<Route path="/">
<div class="content">
<p>Yo wassup</p>
<ul>
{#each uuids as id}
<li><Link to="./{id}">{id}</Link></li>
{/each}
</ul>
</div>
</Route>
<Route path=":id" let:params>
<div class="container">
<h1 class="title">ASSUMING DIRECT CONTROL</h1>
<Viewer uuid={params.id} />
<Link to="..">fuck go back</Link>
</div>
</Route>
</section>

View File

@@ -0,0 +1,121 @@
<script lang="ts">
import termFont from "./term_font.png";
import { loadFont, charWidth, charHeight } from "./font";
import type { ScreenContent } from "./proto";
import { onDestroy, onMount } from "svelte";
export let content: ScreenContent;
let canvas: HTMLCanvasElement;
let composeCanvas: HTMLCanvasElement;
let font: ImageBitmap[];
const paddingX = 2,
paddingY = 2;
const toColor = (n: number) => `#${n.toString(16).padStart(6, "0")}`;
// SAFETY: Only called if all of (content, canvas, composeCanvas, font) are defined
function drawChar(
charCode: number,
x: number,
y: number,
textColor: number,
backgroundColor: number
) {
let cx = canvas.getContext("2d");
let ccx = composeCanvas.getContext("2d");
const offsetX = paddingX + x * charWidth;
const offsetY = paddingY + y * charHeight;
let x0 = offsetX;
let y0 = offsetY;
let w = charWidth;
let h = charHeight;
// handle padding
if (x === 0) {
x0 -= paddingX;
w += paddingX;
} else if (x === content.width - 1) {
w += paddingX;
}
if (y === 0) {
y0 -= paddingY;
h += paddingY;
} else if (y === content.height - 1) {
h += paddingY;
}
// draw background
cx.fillStyle = toColor(content.palette[backgroundColor]);
cx.fillRect(x0, y0, w, h);
// compose foreground
ccx.globalCompositeOperation = "source-over";
ccx.clearRect(0, 0, charWidth, charHeight);
ccx.fillStyle = toColor(content.palette[textColor]);
ccx.fillRect(0, 0, charWidth, charHeight);
ccx.globalCompositeOperation = "destination-in";
ccx.drawImage(font[charCode], 0, 0);
// draw foreground
cx.drawImage(composeCanvas, offsetX, offsetY);
}
function redraw(content: ScreenContent) {
if (!canvas) return;
if (!font) return;
const cx = canvas.getContext("2d");
if (!content) {
cx.fillStyle = "#ff6600";
cx.fillRect(0, 0, canvas.width, canvas.height);
return;
}
canvas.width = content.width * charWidth + 2 * paddingX;
canvas.height = content.height * charHeight + 2 * paddingY;
cx.clearRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < content.height; ++y) {
const line = content.text[y];
const fgLine = content.fg_color[y];
const bgLine = content.bg_color[y];
for (let x = 0; x < content.width; ++x) {
drawChar(
line.charCodeAt(x),
x,
y,
parseInt(fgLine.charAt(x), 16),
parseInt(bgLine.charAt(x), 16)
);
}
}
}
$: redraw(content);
const blinkInt = setInterval(() => {
}, 400);
onMount(async () => {
font = await loadFont(termFont);
composeCanvas = document.createElement("canvas");
composeCanvas.width = charWidth;
composeCanvas.height = charHeight;
redraw(content);
});
onDestroy(() => {
composeCanvas = null;
clearInterval(blinkInt);
});
</script>
<canvas bind:this={canvas} />

View File

@@ -0,0 +1,65 @@
<script lang="ts">
export let uuid: string;
import { onDestroy, onMount } from "svelte";
import Screen from "./Screen.svelte";
const wsUrl = new URL(`/ipmi/browser/${uuid}/ws`, window.location.href);
wsUrl.protocol = wsUrl.protocol.replace("http", "ws");
const dummyData = {
x: -1,
y: -1,
width: 13,
height: 5,
blink: false,
fg: 0,
text: [
" ",
" ",
" [NO SIGNAL] ",
" ",
" ",
],
fg_color: [
"0000000000000",
"0000000000000",
"0800000000080",
"0000000000000",
"0000000000000",
],
bg_color: [
"7777777777777",
"7777777777777",
"7777777777777",
"7777777777777",
"7777777777777",
],
palette: [
15790320, 15905331, 15040472, 10072818, 14605932, 8375321, 15905484,
5000268, 10066329, 5020082, 11691749, 3368652, 8349260, 5744206,
13388876, 1118481,
],
};
let socket: WebSocket | null = null;
let data;
onMount(async () => {
socket = new WebSocket(wsUrl);
socket.addEventListener("message", (event) => {
data = JSON.parse(event.data);
});
});
onDestroy(() => {
console.log("onDestroy");
if (socket !== null) {
socket.close();
}
});
</script>
<Screen content={data || dummyData} />
<pre>{JSON.stringify(data, null, 2)}</pre>

View File

@@ -0,0 +1,25 @@
export const charWidth = 6, charHeight = 9;
export async function loadFont(src: RequestInfo | URL): Promise<ImageBitmap[]> {
const fontImg = await fetch(src);
const fontBlob = await fontImg.blob();
async function getCharBitmap(x: number, y: number) {
const fontOffsetX = 1, fontOffsetY = 1, fontPaddingX = 2, fontPaddingY = 2;
const offsetX = (charWidth + fontPaddingX) * x + fontOffsetX;
const offsetY = (charHeight + fontPaddingY) * y + fontOffsetY;
return await createImageBitmap(fontBlob, offsetX, offsetY, charWidth, charHeight);
}
const font = Array(256);
for (let y = 0; y < 16; ++y) {
for (let x = 0; x < 16; ++x) {
const i = 16 * y + x;
font[i] = getCharBitmap(x, y);
}
}
return await Promise.all(font);
}

View File

@@ -0,0 +1,12 @@
export type ScreenContent = {
x: number
y: number
width: number
height: number
blink: boolean
fg: number
text: string[]
fg_color: string[]
bg_color: string[]
palette: number[]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,21 @@
<script lang="ts">
import { onDestroy } from "svelte";
import Canvas from "./Canvas.svelte";
let colors = ["#ff6600", "#00ff66", "#6600ff"];
let i = 0;
$: color = colors[i];
let int = setInterval(() => {
i = (i + 1) % colors.length;
}, 1000);
onDestroy(() => {
clearInterval(int);
});
</script>
<section class="section">
<Canvas {color} />
</section>

View File

@@ -0,0 +1,24 @@
<script lang="ts">
import { onMount } from "svelte";
export let color: string;
let canvas: HTMLCanvasElement;
let redraws = 0;
function redraw(color) {
if (!canvas) return;
redraws += 1;
let cx = canvas.getContext("2d");
cx.fillStyle = color;
cx.fillRect(0, 0, cx.canvas.width, cx.canvas.height);
}
$: redraw(color);
</script>
<canvas bind:this={canvas} />
<pre>color: {color}
redraws: {redraws}</pre>