added shunt readout and slave timeout

This commit is contained in:
Karlsson Winkels 2024-08-09 20:01:20 +02:00
parent 826318652d
commit f1ca9ed5ff
6 changed files with 5665 additions and 27 deletions

5475
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
export interface AMSMessage {
type: 'error' | 'status' | 'slaveStatus' | 'slaveLog';
type: 'error' | 'status' | 'slaveStatus' | 'slaveLog' | 'shuntCurrentLog' | 'shuntVoltage1Log' | 'shuntVoltage2Log';
}
export interface AMSError extends AMSMessage {
@ -68,3 +68,9 @@ export interface SlaveLogTemperature extends SlaveLog {
startIndex: number;
temperatures: [number, number, number, number, number, number, number, number];
}
export interface ShuntLog extends AMSMessage {
type: 'shuntCurrentLog' | 'shuntVoltage1Log' | 'shuntVoltage2Log';
value: number;
}

View File

@ -1,17 +1,21 @@
<script lang="ts">
import type { AMSError, AMSMessage, AMSStatus, SlaveLog, SlaveStatus } from '$lib/messages';
import type { AMSError, AMSMessage, AMSStatus, SlaveLog, SlaveStatus, ShuntLog } 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';
// const value = source('/data').select('message');
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)) {
@ -43,14 +47,25 @@
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;
}
});
</script>
<div class="wrapper">
<MasterErrorDisplay {error} />
<MasterStatusDisplay status={amsStatus} />
<div class="vertical-wrapper">
<MasterErrorDisplay {error} />
<MasterStatusDisplay status={amsStatus} />
<ShuntStatusDisplay currentLogData={shuntCurrentLog} voltage1LogData={shuntVoltage1Log} voltage2LogData={shuntVoltage2Log} />
</div>
{#each Object.entries(slaveStatus) as [id, status]}
<SlaveStatusDisplay {id} {status} logData={slaveLog[id]} />
{/each}
@ -63,4 +78,13 @@
justify-content: center;
align-items: center;
}
.vertical-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
}
</style>

View File

@ -8,7 +8,8 @@ import type {
SlaveLogLastCell,
SlaveLogTemperature,
SlaveLogVoltage,
SlaveStatus
SlaveStatus,
ShuntLog
} from '$lib/messages';
import { N_CELLS_PER_SLAVE, N_TEMP_SENSORS_PER_SLAVE } from '$lib/defs';
@ -18,6 +19,9 @@ const CAN_ID_AMS_SLAVE_STATUS_BASE = 0x080;
const CAN_ID_AMS_SLAVE_STATUS_MASK = 0xff0;
const CAN_ID_AMS_SLAVE_LOG_BASE = 0x600;
const CAN_ID_AMS_SLAVE_LOG_MASK = 0xf00;
const CAN_ID_SHUNT_CURRENT = 0x521;
const CAN_ID_SHUNT_VOLTAGE_1 = 0x522;
const CAN_ID_SHUNT_VOLTAGE_2 = 0x523;
type RawMessage = {
ext?: boolean;
@ -132,6 +136,21 @@ function decodeSlaveLog(msg: RawMessage): SlaveLog | null {
}
}
function decodeShunt(msg: RawMessage, type: String): ShuntLog | null {
if (msg.data.length != 6) {
console.warn('invalid shunt log frame', msg);
return null;
}
const data = msg.data;
return {
type: type,
value: data.readInt32BE(2) * 1e-3,
};
}
export function POST() {
return produce(async function start({ emit }) {
const network = can.createRawChannel('can0');
@ -139,7 +158,10 @@ export function POST() {
{ id: CAN_ID_AMS_ERROR, mask: 0xfff },
{ id: CAN_ID_AMS_STATUS, mask: 0xfff },
{ id: CAN_ID_AMS_SLAVE_STATUS_BASE, mask: CAN_ID_AMS_SLAVE_STATUS_MASK },
{ id: CAN_ID_AMS_SLAVE_LOG_BASE, mask: CAN_ID_AMS_SLAVE_LOG_MASK }
{ id: CAN_ID_AMS_SLAVE_LOG_BASE, mask: CAN_ID_AMS_SLAVE_LOG_MASK },
{ id: CAN_ID_SHUNT_CURRENT, mask: 0xfff },
{ id: CAN_ID_SHUNT_VOLTAGE_1, mask: 0xfff },
{ id: CAN_ID_SHUNT_VOLTAGE_2, mask: 0xfff }
]);
network.addListener('onMessage', (msg: RawMessage) => {
@ -161,8 +183,15 @@ export function POST() {
case CAN_ID_AMS_STATUS:
message = decodeStatus(msg);
break;
default:
console.warn('unknown frame', msg);
case CAN_ID_SHUNT_CURRENT:
message = decodeShunt(msg, 'shuntCurrentLog');
break;
case CAN_ID_SHUNT_VOLTAGE_1:
message = decodeShunt(msg, 'shuntVoltage1Log');
break;
case CAN_ID_SHUNT_VOLTAGE_2:
message = decodeShunt(msg, 'shuntVoltage2Log');
break;
}
}

View File

@ -0,0 +1,63 @@
<script lang="ts">
import type { ShuntLog } from '$lib/messages';
export let currentLogData: ShuntLog | undefined;
export let voltage1LogData: ShuntLog | undefined;
export let voltage2LogData: ShuntLog | undefined;
</script>
<div class="shunt-content">
<h2><b>Shunt</b></h2>
<div class="log-collumn">
<div class="log-entry">
<div>Current:</div>
<div><b>{Math.round(currentLogData?.value*10)/10}A</b></div>
</div>
</div>
<div class="log-collumn">
<div class="log-entry">
<div>Voltage 1:</div>
<div><b>{Math.round(voltage1LogData?.value*100)/100}V</b></div>
</div>
</div>
<div class="log-collumn">
<div class="log-entry">
<div>Voltage 2:</div>
<div><b>{Math.round(voltage2LogData?.value*100)/100}V</b></div>
</div>
</div>
</div>
<style>
.shunt-content {
display: flex;
flex-direction: column;
width: 12vw;
padding: 2px;
margin: 2px;
border: 2px solid black;
font-size: 14px;
margin-top: 10px;
background-color: hsl(0, 60%, var(--lightness));
}
.log-entry {
display: grid;
grid-template-columns: 5fr 3fr;
margin: 2px;
padding: 2px;
background-color: hsl(var(--hue), 80%, 80%);
}
.log-collumn {
display: flex;
flex-direction: column;
background-color: hsl(0, 60%, var(--lightness));
}
h2 {
text-align: center;
}
</style>

View File

@ -1,39 +1,79 @@
<script lang="ts">
import type { SlaveStatus } from '$lib/messages';
import type { SlaveLogData } from '$lib/slave-log';
import { onMount } from 'svelte';
import type { SlaveStatus } from '$lib/messages';
import { SlaveLogData } from '$lib/slave-log';
export let id: number;
export let status: SlaveStatus;
export let logData: SlaveLogData | undefined;
let interval;
let lastMsgTime: number = 0;
let timeDifference: number = 0;
onMount(() => {
interval = setInterval(() => {
timeDifference = Date.now()-lastMsgTime;
}, 50);
});
$: totalVoltage = logData?.voltages.slice(0, 15).reduce((acc, x) => acc + x, 0);
$: {
slavePing(logData);
}
function slavePing(logData: SlaveLogData | undefined): void {
lastMsgTime = Date.now();
}
</script>
<div class="slave-content" style:--lightness={status.error ? "60%" : "100%"}>
<h2><b>Slave #{id}</b></h2>
<div class="log-collumn">
<div class="log-entry"><div>Error:</div><div><b>{status.error}</b></div></div>
<div class="log-entry"><div>Min. cell voltage:</div><div><b>{Math.round(status.minCellVolt*100)/100}V</b></div></div>
<div class="log-entry"><div>Max. cell voltage:</div><div><b>{Math.round(status.maxCellVolt*100)/100}V</b></div></div>
<div class="log-entry"><div>Max. temperature:</div><div><b>{Math.round(status.maxTemp*100)/100}°</b></div></div>
<div class="slave-content" style:--lightness={status.error ? '70%' : '100%'}>
<h2><b>Slave #{id} Δrx_t: {Math.round(timeDifference / 1000)}s</b></h2>
<div class="log-collumn" style:--lightness={'' + Math.max(100 - ((timeDifference-200) / 150), 70) + '%'}>
<div class="log-entry">
<div>Error:</div>
<div><b>{status.error}</b></div>
</div>
<div class="log-entry">
<div>Min. cell voltage:</div>
<div><b>{Math.round(status.minCellVolt * 1000) / 1000}V</b></div>
</div>
<div class="log-entry">
<div>Max. cell voltage:</div>
<div><b>{Math.round(status.maxCellVolt * 1000) / 1000}V</b></div>
</div>
<div class="log-entry">
<div>Max. temperature:</div>
<div><b>{Math.round(status.maxTemp * 100) / 100}°</b></div>
</div>
<div class="log-entry">
<div>Total voltage:</div>
<div><b>{Math.round(totalVoltage * 100) / 100}V</b></div>
</div>
<!-- <div>SoC</div>
<div>{status.soc}</div> -->
<!-- <div>Failed temperature sensors</div>
<div>{logData?.failedTempSensors ?? 0}</div> -->
</div>
{#if logData}
<div class="log-collumn">
{#each logData.voltages as volt, i}
{#if i < 15}
<div class="log-entry" style:--hue={Math.max(((volt-2.5)*70.5), 0)}>
<div>V{i}:</div><div><b>{Math.round(volt*100)/100}V</b></div>
<div class="log-entry" style:--hue={Math.max((volt - 2.5) * 70.5, 0)}>
<div>V{i}:</div>
<div><b>{Math.round(volt * 1000) / 1000}V</b></div>
</div>
{/if}
{/each}
{#each logData.temperatures as temp, i}
{#if i > 33 && i < 44}
<div class="log-entry" style:--hue={210-(temp*3.5)}>
<div>T{i}:</div><div><b>{temp}°</b></div>
<div class="log-entry" style:--hue={210 - temp * 3.5}>
<div>T{i}:</div>
<div><b>{temp}°</b></div>
</div>
{/if}
{/each}
@ -51,9 +91,9 @@
border: 2px solid black;
font-size: 14px;
margin-top: 10px;
background-color: hsl(0, 60%, --lightness);
background-color: hsl(0, 60%, var(--lightness));
}
.log-entry {
display: grid;
grid-template-columns: 5fr 3fr;
@ -65,9 +105,10 @@
.log-collumn {
display: flex;
flex-direction: column;
background-color: hsl(0, 60%, var(--lightness));
}
h2 {
text-align: center;
}
</style>
</style>