Compare commits

...

2 Commits

Author SHA1 Message Date
Jasper Blanckenburg 917464347a Add overview popout 2024-09-06 14:04:29 +02:00
Jasper Blanckenburg 1da81476f7 Move SSE client to its own module 2024-09-06 13:42:21 +02:00
6 changed files with 147 additions and 106 deletions

80
src/lib/client.ts Normal file
View File

@ -0,0 +1,80 @@
import { source } from 'sveltekit-sse';
import {
decodeMessage,
unmarshalMessage,
type AMSError,
type AMSStatus,
type MarshalledMessage,
type ShuntLog,
type SlaveLog,
type SlaveStatus
} from './messages';
import { writable, type Writable } from 'svelte/store';
import { SlaveLogData } from './slave-log';
export type ShuntData = {
current: number;
voltage1: number;
voltage2: number;
};
export const error: Writable<AMSError | undefined> = writable(undefined);
export const amsStatus: Writable<AMSStatus | undefined> = writable(undefined);
export const slaveStatus: Writable<Record<number, SlaveStatus>> = writable({});
export const slaveLog: Writable<Record<number, SlaveLogData>> = writable({});
export const shunt: Writable<ShuntData> = writable({ current: 0, voltage1: 0, voltage2: 0 });
source('/data')
.select('message')
.subscribe((value) => {
if (!value) {
return;
}
const msg = decodeMessage(unmarshalMessage(JSON.parse(value) as MarshalledMessage));
if (!msg) {
return;
}
switch (msg.type) {
case 'error':
error.set(msg as AMSError);
break;
case 'status':
amsStatus.set(msg as AMSStatus);
break;
case 'slaveStatus': {
const status = msg as SlaveStatus;
slaveStatus.update((r) => {
r[status.slaveId] = status;
return r;
});
break;
}
case 'slaveLog':
handleSlaveLog(msg as SlaveLog);
break;
case 'shuntCurrentLog':
shunt.update((s) => {
s.current = (msg as ShuntLog).value;
return s;
});
break;
case 'shuntVoltage1Log':
shunt.update((s) => Object.assign(s, { voltage1: (msg as ShuntLog).value }));
break;
case 'shuntVoltage2Log':
shunt.update((s) => Object.assign(s, { voltage2: (msg as ShuntLog).value }));
break;
}
});
function handleSlaveLog(msg: SlaveLog) {
slaveLog.update((r) => {
if (!(msg.slaveId in r)) {
r[msg.slaveId] = new SlaveLogData();
}
const slave = r[msg.slaveId];
slave.handleMsg(msg);
return r;
});
}

View File

@ -0,0 +1,5 @@
<script lang="ts">
import '../app.css';
</script>
<slot />

View File

@ -1,93 +1,26 @@
<script lang="ts">
import {
type AMSError,
type AMSMessage,
type AMSStatus,
type SlaveLog,
type SlaveStatus,
type ShuntLog,
type MarshalledMessage,
unmarshalMessage,
decodeMessage
} from '$lib/messages';
import { source } from 'sveltekit-sse';
import MasterStatusDisplay from './master-status-display.svelte';
import SlaveStatusDisplay from './slave-status-display.svelte';
import '../app.css';
import MasterErrorDisplay from './master-error-display.svelte';
import { SlaveLogData } from '$lib/slave-log';
import ShuntStatusDisplay from './shunt-status-display.svelte';
import Overview from './overview.svelte';
let error: AMSError | undefined;
let amsStatus: AMSStatus | undefined;
let slaveStatus: Record<number, SlaveStatus> = {};
let slaveLog: Record<number, SlaveLogData> = {};
let shuntCurrentLog: ShuntLog | undefined;
let shuntVoltage1Log: ShuntLog | undefined;
let shuntVoltage2Log: ShuntLog | undefined;
function handleSlaveLog(msg: SlaveLog) {
if (!(msg.slaveId in slaveLog)) {
slaveLog[msg.slaveId] = new SlaveLogData();
}
const slave = slaveLog[msg.slaveId];
slave.handleMsg(msg);
}
source('/data')
.select('message')
.subscribe((value) => {
if (!value) {
return;
}
const msg = decodeMessage(unmarshalMessage(JSON.parse(value) as MarshalledMessage));
if (!msg) {
return;
}
switch (msg.type) {
case 'error':
error = msg as AMSError;
break;
case 'status':
amsStatus = msg as AMSStatus;
break;
case 'slaveStatus': {
const status = msg as SlaveStatus;
slaveStatus[status.slaveId] = status;
break;
}
case 'slaveLog':
handleSlaveLog(msg as SlaveLog);
break;
case 'shuntCurrentLog':
shuntCurrentLog = msg as ShuntLog;
break;
case 'shuntVoltage1Log':
shuntVoltage1Log = msg as ShuntLog;
break;
case 'shuntVoltage2Log':
shuntVoltage2Log = msg as ShuntLog;
break;
}
});
import { amsStatus, error, shunt, slaveLog, slaveStatus } from '$lib/client';
</script>
<svelte:head>
<title>Charging</title>
</svelte:head>
<div class="vertical-wrapper">
<Overview {amsStatus} current={shuntCurrentLog?.value} voltage={shuntVoltage1Log?.value} />
<Overview amsStatus={$amsStatus} shunt={$shunt} showPopout={true} />
<div class="wrapper">
<MasterErrorDisplay {error} />
<MasterStatusDisplay status={amsStatus} />
<ShuntStatusDisplay
currentLogData={shuntCurrentLog}
voltage1LogData={shuntVoltage1Log}
voltage2LogData={shuntVoltage2Log}
/>
<MasterErrorDisplay error={$error} />
<MasterStatusDisplay status={$amsStatus} />
<ShuntStatusDisplay data={$shunt} />
</div>
<div class="wrapper">
{#each Object.entries(slaveStatus) as [id, status]}
<SlaveStatusDisplay {id} {status} logData={slaveLog[id]} />
{#each Object.entries($slaveStatus) as [id, status]}
<SlaveStatusDisplay {id} {status} logData={$slaveLog[id]} />
{/each}
</div>
</div>

View File

@ -1,19 +1,34 @@
<script lang="ts">
import type { ShuntData } from '$lib/client';
import { AMSState, type AMSStatus } from '$lib/messages';
export let amsStatus: AMSStatus | undefined;
export let current: number | undefined;
export let voltage: number | undefined;
export let shunt: ShuntData;
export let showPopout: boolean = false;
</script>
<div class="overview grid grid-rows-3 gap-3">
{#if amsStatus != null && current != null && voltage != null}
<div class="flex flex-col w-[30rem] border-2 border-black p-1 m-1">
{#if showPopout}
<div class="flex flex-row justify-end mb-2">
<a
class="text-sm underline hover:no-underline"
href="/overview"
target="_blank"
on:click|preventDefault={() => {
window.open('/overview', 'newwindow', 'width=500,height=200,resizable=yes');
return false;
}}>Pop out</a
>
</div>
{/if}
<div class="grid grid-rows-3 gap-3">
{#if amsStatus != null}
<div class="progress" data-label="{amsStatus.soc}%">
<span class="value" style="width:{amsStatus.soc}%;"></span>
</div>
<div class="grid grid-cols-4 gap-2">
<span class="text-center border-r">{voltage.toFixed(1)} V</span>
<span class="text-center border-r">{current.toFixed(1)} A</span>
<span class="text-center border-r">{shunt.voltage1.toFixed(1)} V</span>
<span class="text-center border-r">{shunt.current.toFixed(1)} A</span>
<span class="text-center border-r">{amsStatus.maxCellTemp.toFixed(1)}°C</span>
<span class="text-center">SDC {amsStatus.sdcClosed ? '✅' : '❌'}</span>
</div>
@ -28,6 +43,7 @@
</div>
{/if}
</div>
</div>
<style>
.overview {

View File

@ -0,0 +1,10 @@
<script lang="ts">
import Overview from '../overview.svelte';
import { amsStatus, shunt } from '$lib/client';
</script>
<svelte:head>
<title>Charging Overview</title>
</svelte:head>
<Overview amsStatus={$amsStatus} shunt={$shunt} />

View File

@ -1,10 +1,7 @@
<script lang="ts">
import type { ShuntLog } from '$lib/messages';
export let currentLogData: ShuntLog | undefined;
export let voltage1LogData: ShuntLog | undefined;
export let voltage2LogData: ShuntLog | undefined;
import type { ShuntData } from '$lib/client';
export let data: ShuntData;
</script>
<div class="shunt-content">
@ -13,19 +10,19 @@
<div class="log-collumn">
<div class="log-entry">
<div>Current:</div>
<div><b>{Math.round(currentLogData?.value*10)/10}A</b></div>
<div><b>{data.current.toFixed(1)} A</b></div>
</div>
</div>
<div class="log-collumn">
<div class="log-entry">
<div>Voltage Accu:</div>
<div><b>{Math.round(voltage1LogData?.value*100)/100}V</b></div>
<div><b>{data.voltage1.toFixed(2)} V</b></div>
</div>
</div>
<div class="log-collumn">
<div class="log-entry">
<div>Voltage Vehicle:</div>
<div><b>{Math.round(voltage2LogData?.value*100)/100}V</b></div>
<div><b>{data.voltage2.toFixed(2)} V</b></div>
</div>
</div>
</div>