Compare commits
1 Commits
rule-compl
...
Shunt-inte
| Author | SHA1 | Date | |
|---|---|---|---|
| bbb2534987 |
6779
package-lock.json
generated
6779
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -33,8 +33,7 @@
|
|||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.0.3",
|
"vite": "^5.0.3"
|
||||||
"vite-plugin-node-polyfills": "^0.22.0"
|
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,80 +0,0 @@
|
|||||||
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,44 +1,5 @@
|
|||||||
export interface MarshalledMessage {
|
|
||||||
id: number;
|
|
||||||
ext: boolean;
|
|
||||||
canfd: boolean;
|
|
||||||
data: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RawMessage = {
|
|
||||||
ext?: boolean;
|
|
||||||
canfd?: boolean;
|
|
||||||
id: number;
|
|
||||||
data: Buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function marshalMessage(msg: RawMessage): MarshalledMessage {
|
|
||||||
return {
|
|
||||||
id: msg.id,
|
|
||||||
ext: msg.ext ?? false,
|
|
||||||
canfd: msg.canfd ?? false,
|
|
||||||
data: Array.from(msg.data)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unmarshalMessage(msg: MarshalledMessage): RawMessage {
|
|
||||||
return {
|
|
||||||
id: msg.id,
|
|
||||||
ext: msg.ext,
|
|
||||||
canfd: msg.canfd,
|
|
||||||
data: Buffer.from(msg.data)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AMSMessage {
|
export interface AMSMessage {
|
||||||
type:
|
type: 'error' | 'status' | 'shunt' | 'slaveStatus' | 'slaveLog';
|
||||||
| 'error'
|
|
||||||
| 'status'
|
|
||||||
| 'slaveStatus'
|
|
||||||
| 'slaveLog'
|
|
||||||
| 'shuntCurrentLog'
|
|
||||||
| 'shuntVoltage1Log'
|
|
||||||
| 'shuntVoltage2Log';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AMSError extends AMSMessage {
|
export interface AMSError extends AMSMessage {
|
||||||
@ -68,6 +29,30 @@ export interface AMSStatus extends AMSMessage {
|
|||||||
// TODO: IMD state & R_iso
|
// TODO: IMD state & R_iso
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Shunt extends AMSMessage {
|
||||||
|
type: 'shunt';
|
||||||
|
|
||||||
|
logType: 'current' | 'voltage1' | 'voltage2';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShuntCurrent extends Shunt {
|
||||||
|
logType: 'current';
|
||||||
|
|
||||||
|
current: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShuntVoltage1 extends Shunt {
|
||||||
|
logType: 'voltage1';
|
||||||
|
|
||||||
|
voltage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShuntVoltage2 extends Shunt {
|
||||||
|
logType: 'voltage2';
|
||||||
|
|
||||||
|
voltage: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SlaveStatus extends AMSMessage {
|
export interface SlaveStatus extends AMSMessage {
|
||||||
type: 'slaveStatus';
|
type: 'slaveStatus';
|
||||||
|
|
||||||
@ -107,176 +92,3 @@ export interface SlaveLogTemperature extends SlaveLog {
|
|||||||
startIndex: number;
|
startIndex: number;
|
||||||
temperatures: [number, number, number, number, number, number, number, number];
|
temperatures: [number, number, number, number, number, number, number, number];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShuntLog extends AMSMessage {
|
|
||||||
type: 'shuntCurrentLog' | 'shuntVoltage1Log' | 'shuntVoltage2Log';
|
|
||||||
|
|
||||||
value: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
import { N_CELLS_PER_SLAVE, N_TEMP_SENSORS_PER_SLAVE } from '$lib/defs';
|
|
||||||
|
|
||||||
export const CAN_ID_AMS_ERROR = 0x00c;
|
|
||||||
export const CAN_ID_AMS_STATUS = 0x00a;
|
|
||||||
export const CAN_ID_AMS_SLAVE_STATUS_BASE = 0x080;
|
|
||||||
export const CAN_ID_AMS_SLAVE_STATUS_MASK = 0xff0;
|
|
||||||
export const CAN_ID_AMS_SLAVE_LOG_BASE = 0x600;
|
|
||||||
export const CAN_ID_AMS_SLAVE_LOG_MASK = 0xf00;
|
|
||||||
export const CAN_ID_SHUNT_CURRENT = 0x521;
|
|
||||||
export const CAN_ID_SHUNT_VOLTAGE_1 = 0x522;
|
|
||||||
export const CAN_ID_SHUNT_VOLTAGE_2 = 0x523;
|
|
||||||
|
|
||||||
function decodeError(msg: RawMessage): AMSError | null {
|
|
||||||
if (msg.data.length != 2) {
|
|
||||||
console.warn('invalid error frame', msg);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('error', msg);
|
|
||||||
const data = msg.data;
|
|
||||||
return {
|
|
||||||
type: 'error',
|
|
||||||
kind: data[0],
|
|
||||||
arg: data[1]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeStatus(msg: RawMessage): AMSStatus | null {
|
|
||||||
if (msg.data.length != 8) {
|
|
||||||
console.warn('invalid status frame', msg);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = msg.data;
|
|
||||||
return {
|
|
||||||
type: 'status',
|
|
||||||
state: data[0] & 0x7f,
|
|
||||||
sdcClosed: !!(data[0] & 0x80),
|
|
||||||
soc: data[1],
|
|
||||||
minCellVolt: data.readUInt16BE(2) * 1e-3,
|
|
||||||
maxCellTemp: data.readUInt16BE(4) * 0.0625,
|
|
||||||
imdOK: !!(data[6] & 0x80)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeSlaveStatus(msg: RawMessage): SlaveStatus | null {
|
|
||||||
if (msg.data.length != 8) {
|
|
||||||
console.warn('invalid slave status frame', msg);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = msg.data;
|
|
||||||
return {
|
|
||||||
type: 'slaveStatus',
|
|
||||||
|
|
||||||
slaveId: data[0] & 0x7f,
|
|
||||||
error: !!(data[0] & 0x80),
|
|
||||||
soc: data[1],
|
|
||||||
minCellVolt: data.readUInt16BE(2) * 1e-3,
|
|
||||||
maxCellVolt: data.readUInt16BE(4) * 1e-3,
|
|
||||||
maxTemp: data.readUInt16BE(6) * 0.0625
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeSlaveLog(msg: RawMessage): SlaveLog | null {
|
|
||||||
if (msg.data.length != 8) {
|
|
||||||
console.warn('invalid slave log frame', msg);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slaveId = (msg.id & 0xf0) >> 4;
|
|
||||||
const logIndex = msg.id & 0x00f;
|
|
||||||
const data = msg.data;
|
|
||||||
if (logIndex < N_CELLS_PER_SLAVE / 4) {
|
|
||||||
const startIndex = logIndex * 4;
|
|
||||||
const voltages = [];
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
voltages.push(data.readUInt16BE(2 * i) * 1e-3);
|
|
||||||
}
|
|
||||||
const msg: SlaveLogVoltage = {
|
|
||||||
type: 'slaveLog',
|
|
||||||
slaveId,
|
|
||||||
|
|
||||||
logType: 'voltage',
|
|
||||||
startIndex,
|
|
||||||
voltages: voltages as [number, number, number, number]
|
|
||||||
};
|
|
||||||
return msg;
|
|
||||||
} else if (logIndex == N_CELLS_PER_SLAVE / 4) {
|
|
||||||
const msg: SlaveLogLastCell = {
|
|
||||||
type: 'slaveLog',
|
|
||||||
slaveId,
|
|
||||||
|
|
||||||
logType: 'lastCell',
|
|
||||||
voltage: data.readUInt16BE(0) * 1e-3,
|
|
||||||
failed_temp_sensors: data.readInt32BE(2)
|
|
||||||
};
|
|
||||||
return msg;
|
|
||||||
} else if (logIndex < N_CELLS_PER_SLAVE / 4 + 1 + N_TEMP_SENSORS_PER_SLAVE / 8) {
|
|
||||||
const temperatures = [];
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
temperatures.push(data.readInt8(i) * 1);
|
|
||||||
}
|
|
||||||
const msg: SlaveLogTemperature = {
|
|
||||||
type: 'slaveLog',
|
|
||||||
slaveId,
|
|
||||||
|
|
||||||
logType: 'temperature',
|
|
||||||
startIndex: (logIndex - N_CELLS_PER_SLAVE / 4 - 1) * 8,
|
|
||||||
temperatures: temperatures as [number, number, number, number, number, number, number, number]
|
|
||||||
};
|
|
||||||
return msg;
|
|
||||||
} else {
|
|
||||||
console.warn('Unknown slave log index', msg.id);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeShunt(msg: RawMessage, type: ShuntLog['type']): ShuntLog | null {
|
|
||||||
if (msg.data.length != 6) {
|
|
||||||
console.warn('invalid shunt log frame', msg);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: type,
|
|
||||||
value: msg.data.readInt32BE(2) * 1e-3
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decodeMessage(msg: RawMessage): AMSMessage | null {
|
|
||||||
if (msg.ext || msg.canfd) {
|
|
||||||
console.warn('invalid message', msg);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let message: AMSMessage | null = null;
|
|
||||||
|
|
||||||
if ((msg.id & CAN_ID_AMS_SLAVE_STATUS_MASK) == CAN_ID_AMS_SLAVE_STATUS_BASE) {
|
|
||||||
message = decodeSlaveStatus(msg);
|
|
||||||
} else if ((msg.id & CAN_ID_AMS_SLAVE_LOG_MASK) == CAN_ID_AMS_SLAVE_LOG_BASE) {
|
|
||||||
message = decodeSlaveLog(msg);
|
|
||||||
} else {
|
|
||||||
switch (msg.id) {
|
|
||||||
case CAN_ID_AMS_ERROR:
|
|
||||||
message = decodeError(msg);
|
|
||||||
break;
|
|
||||||
case CAN_ID_AMS_STATUS:
|
|
||||||
message = decodeStatus(msg);
|
|
||||||
break;
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
console.warn(`unknown message id ${msg.id}`, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import '../app.css';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<slot />
|
|
||||||
@ -1,28 +1,67 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { AMSError, AMSMessage, AMSStatus, Shunt, ShuntCurrent, ShuntVoltage1, ShuntVoltage2, SlaveLog, SlaveStatus } from '$lib/messages';
|
||||||
|
import { source } from 'sveltekit-sse';
|
||||||
import MasterStatusDisplay from './master-status-display.svelte';
|
import MasterStatusDisplay from './master-status-display.svelte';
|
||||||
import SlaveStatusDisplay from './slave-status-display.svelte';
|
import SlaveStatusDisplay from './slave-status-display.svelte';
|
||||||
import MasterErrorDisplay from './master-error-display.svelte';
|
|
||||||
import ShuntStatusDisplay from './shunt-status-display.svelte';
|
import ShuntStatusDisplay from './shunt-status-display.svelte';
|
||||||
import Overview from './overview.svelte';
|
import '../app.css';
|
||||||
import { amsStatus, error, shunt, slaveLog, slaveStatus } from '$lib/client';
|
import MasterErrorDisplay from './master-error-display.svelte';
|
||||||
|
import { SlaveLogData } from '$lib/slave-log';
|
||||||
|
// const value = source('/data').select('message');
|
||||||
|
|
||||||
|
let error: AMSError | undefined;
|
||||||
|
let amsStatus: AMSStatus | undefined;
|
||||||
|
let shunt: Shunt | undefined;
|
||||||
|
let shuntA: ShuntCurrent | undefined
|
||||||
|
let shuntV1: ShuntVoltage1 | undefined;
|
||||||
|
let shuntV2: ShuntVoltage2 | undefined;
|
||||||
|
let slaveStatus: Record<number, SlaveStatus> = {};
|
||||||
|
let slaveLog: Record<number, SlaveLogData> = {};
|
||||||
|
|
||||||
|
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 = JSON.parse(value) as AMSMessage;
|
||||||
|
switch (msg.type) {
|
||||||
|
case 'error':
|
||||||
|
error = msg as AMSError;
|
||||||
|
break;
|
||||||
|
case 'status':
|
||||||
|
amsStatus = msg as AMSStatus;
|
||||||
|
break;
|
||||||
|
case 'shunt':
|
||||||
|
shunt = msg as Shunt;
|
||||||
|
break;
|
||||||
|
case 'slaveStatus': {
|
||||||
|
const status = msg as SlaveStatus;
|
||||||
|
slaveStatus[status.slaveId] = status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'slaveLog':
|
||||||
|
handleSlaveLog(msg as SlaveLog);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<div class="wrapper">
|
||||||
<title>Charging</title>
|
<MasterErrorDisplay {error} />
|
||||||
</svelte:head>
|
<MasterStatusDisplay status={amsStatus} />
|
||||||
|
|
||||||
<div class="vertical-wrapper">
|
{#each Object.entries(slaveStatus) as [id, status]}
|
||||||
<Overview amsStatus={$amsStatus} shunt={$shunt} showPopout={true} />
|
<SlaveStatusDisplay {id} {status} logData={slaveLog[id]} />
|
||||||
<div class="wrapper">
|
{/each}
|
||||||
<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}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -32,13 +71,4 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
.vertical-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,18 +1,181 @@
|
|||||||
import { produce } from 'sveltekit-sse';
|
import { produce } from 'sveltekit-sse';
|
||||||
import * as can from 'socketcan';
|
import * as can from 'socketcan';
|
||||||
import {
|
import type {
|
||||||
type RawMessage,
|
AMSError,
|
||||||
marshalMessage,
|
AMSMessage,
|
||||||
CAN_ID_AMS_ERROR,
|
AMSStatus,
|
||||||
CAN_ID_AMS_SLAVE_STATUS_MASK,
|
Shunt,
|
||||||
CAN_ID_AMS_SLAVE_LOG_MASK,
|
ShuntCurrent,
|
||||||
CAN_ID_AMS_SLAVE_LOG_BASE,
|
ShuntVoltage1,
|
||||||
CAN_ID_AMS_SLAVE_STATUS_BASE,
|
ShuntVoltage2,
|
||||||
CAN_ID_AMS_STATUS,
|
SlaveLog,
|
||||||
CAN_ID_SHUNT_CURRENT,
|
SlaveLogLastCell,
|
||||||
CAN_ID_SHUNT_VOLTAGE_1,
|
SlaveLogTemperature,
|
||||||
CAN_ID_SHUNT_VOLTAGE_2
|
SlaveLogVoltage,
|
||||||
|
SlaveStatus
|
||||||
} from '$lib/messages';
|
} from '$lib/messages';
|
||||||
|
import { N_CELLS_PER_SLAVE, N_TEMP_SENSORS_PER_SLAVE } from '$lib/defs';
|
||||||
|
|
||||||
|
const CAN_ID_AMS_ERROR = 0x00c;
|
||||||
|
const CAN_ID_AMS_STATUS = 0x00a;
|
||||||
|
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_AMS_SHUNT_BASE = 0x520;
|
||||||
|
const CAN_ID_AMS_SHUNT_MASK = 0xff0;
|
||||||
|
|
||||||
|
type RawMessage = {
|
||||||
|
ext?: boolean;
|
||||||
|
canfd?: boolean;
|
||||||
|
id: number;
|
||||||
|
data: Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
function decodeError(msg: RawMessage): AMSError | null {
|
||||||
|
if (msg.data.length != 2) {
|
||||||
|
console.warn('invalid error frame', msg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('error', msg);
|
||||||
|
const data = msg.data;
|
||||||
|
return {
|
||||||
|
type: 'error',
|
||||||
|
kind: data[0],
|
||||||
|
arg: data[1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeStatus(msg: RawMessage): AMSStatus | null {
|
||||||
|
if (msg.data.length != 8) {
|
||||||
|
console.warn('invalid status frame', msg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = msg.data;
|
||||||
|
return {
|
||||||
|
type: 'status',
|
||||||
|
state: data[0] & 0x7f,
|
||||||
|
sdcClosed: !!(data[0] & 0x80),
|
||||||
|
soc: data[1],
|
||||||
|
minCellVolt: data.readUInt16BE(2) * 1e-3,
|
||||||
|
maxCellTemp: data.readUInt16BE(4) * 0.0625,
|
||||||
|
imdOK: !!(data[6] & 0x80)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeShunt(msg: RawMessage): Shunt | null {
|
||||||
|
if (msg.data.length != 6) {
|
||||||
|
console.warn( 'invalid shunt frame', msg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = msg.id & 0x00f;
|
||||||
|
const data = msg.data;
|
||||||
|
if (index == 1) {
|
||||||
|
const msg: ShuntCurrent = {
|
||||||
|
type: 'shunt',
|
||||||
|
|
||||||
|
logType: 'current',
|
||||||
|
current: data.readInt32BE(2) * 1e-3,
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
} else if (index == 2) {
|
||||||
|
const msg: ShuntVoltage1 = {
|
||||||
|
type: 'shunt',
|
||||||
|
|
||||||
|
logType: 'voltage1',
|
||||||
|
voltage: data.readInt32BE(2) * 1e-3,
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
} else if (index == 3) {
|
||||||
|
const msg: ShuntVoltage2 = {
|
||||||
|
type: 'shunt',
|
||||||
|
|
||||||
|
logType: 'voltage2',
|
||||||
|
voltage: data.readInt32BE(2) * 1e-3,
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
} else {
|
||||||
|
console.warn('Unknown shunt index', msg.id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeSlaveStatus(msg: RawMessage): SlaveStatus | null {
|
||||||
|
if (msg.data.length != 8) {
|
||||||
|
console.warn('invalid slave status frame', msg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = msg.data;
|
||||||
|
return {
|
||||||
|
type: 'slaveStatus',
|
||||||
|
|
||||||
|
slaveId: data[0] & 0x7f,
|
||||||
|
error: !!(data[0] & 0x80),
|
||||||
|
soc: data[1],
|
||||||
|
minCellVolt: data.readUInt16BE(2) * 1e-3,
|
||||||
|
maxCellVolt: data.readUInt16BE(4) * 1e-3,
|
||||||
|
maxTemp: data.readUInt16BE(6) * 0.0625
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeSlaveLog(msg: RawMessage): SlaveLog | null {
|
||||||
|
if (msg.data.length != 8) {
|
||||||
|
console.warn('invalid slave log frame', msg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slaveId = (msg.id & 0xf0) >> 4;
|
||||||
|
const logIndex = msg.id & 0x00f;
|
||||||
|
const data = msg.data;
|
||||||
|
if (logIndex < N_CELLS_PER_SLAVE / 4) {
|
||||||
|
const startIndex = logIndex * 4;
|
||||||
|
const voltages = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
voltages.push(data.readUInt16BE(2 * i) * 1e-3);
|
||||||
|
}
|
||||||
|
const msg: SlaveLogVoltage = {
|
||||||
|
type: 'slaveLog',
|
||||||
|
slaveId,
|
||||||
|
|
||||||
|
logType: 'voltage',
|
||||||
|
startIndex,
|
||||||
|
voltages: voltages as [number, number, number, number]
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
} else if (logIndex == N_CELLS_PER_SLAVE / 4) {
|
||||||
|
const msg: SlaveLogLastCell = {
|
||||||
|
type: 'slaveLog',
|
||||||
|
slaveId,
|
||||||
|
|
||||||
|
logType: 'lastCell',
|
||||||
|
voltage: data.readUInt16BE(0) * 1e-3,
|
||||||
|
failed_temp_sensors: data.readInt32BE(2)
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
} else if (logIndex < N_CELLS_PER_SLAVE / 4 + 1 + N_TEMP_SENSORS_PER_SLAVE / 8) {
|
||||||
|
const temperatures = [];
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
temperatures.push(data.readInt8(i) * 1);
|
||||||
|
}
|
||||||
|
const msg: SlaveLogTemperature = {
|
||||||
|
type: 'slaveLog',
|
||||||
|
slaveId,
|
||||||
|
|
||||||
|
logType: 'temperature',
|
||||||
|
startIndex: (logIndex - N_CELLS_PER_SLAVE / 4 - 1) * 8,
|
||||||
|
temperatures: temperatures as [number, number, number, number, number, number, number, number]
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
} else {
|
||||||
|
console.warn('Unknown slave log index', msg.id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function POST() {
|
export function POST() {
|
||||||
return produce(async function start({ emit }) {
|
return produce(async function start({ emit }) {
|
||||||
@ -22,13 +185,39 @@ export function POST() {
|
|||||||
{ id: CAN_ID_AMS_STATUS, 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_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_AMS_SHUNT_BASE, mask:
|
||||||
{ id: CAN_ID_SHUNT_VOLTAGE_1, mask: 0xfff },
|
CAN_ID_AMS_SHUNT_MASK }
|
||||||
{ id: CAN_ID_SHUNT_VOLTAGE_2, mask: 0xfff }
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
network.addListener('onMessage', (msg: RawMessage) => {
|
network.addListener('onMessage', (msg: RawMessage) => {
|
||||||
emit('message', JSON.stringify(marshalMessage(msg)));
|
if (msg.ext || msg.canfd) {
|
||||||
|
console.warn('invalid frame', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let message: AMSMessage | null = null;
|
||||||
|
if ((msg.id & CAN_ID_AMS_SLAVE_STATUS_MASK) == CAN_ID_AMS_SLAVE_STATUS_BASE) {
|
||||||
|
message = decodeSlaveStatus(msg);
|
||||||
|
} else if ((msg.id & CAN_ID_AMS_SLAVE_LOG_MASK) == CAN_ID_AMS_SLAVE_LOG_BASE) {
|
||||||
|
message = decodeSlaveLog(msg);
|
||||||
|
} else if ((msg.id & CAN_ID_AMS_SHUNT_MASK) == CAN_ID_AMS_SHUNT_BASE) {
|
||||||
|
message = decodeShunt(msg);
|
||||||
|
} else {
|
||||||
|
switch (msg.id) {
|
||||||
|
case CAN_ID_AMS_ERROR:
|
||||||
|
message = decodeError(msg);
|
||||||
|
break;
|
||||||
|
case CAN_ID_AMS_STATUS:
|
||||||
|
message = decodeStatus(msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('unknown frame', msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
emit('message', JSON.stringify(message));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
network.start();
|
network.start();
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AMSState, type AMSStatus } from '$lib/messages';
|
import type { AMSStatus } from '$lib/messages';
|
||||||
|
|
||||||
export let status: AMSStatus | undefined;
|
export let status: AMSStatus | undefined;
|
||||||
</script>
|
</script>
|
||||||
@ -8,9 +8,9 @@
|
|||||||
{#if status}
|
{#if status}
|
||||||
<h2><b>Master Status</b></h2>
|
<h2><b>Master Status</b></h2>
|
||||||
<div class="log-collumn">
|
<div class="log-collumn">
|
||||||
<div>State: {AMSState[status.state]}</div>
|
<div>State: {status.state}</div>
|
||||||
<div>SDC Closed: {status.sdcClosed}</div>
|
<div>SDC Closed: {status.sdcClosed}</div>
|
||||||
<div>SoC: {status.soc}%</div>
|
<div>SoC: {status.soc}</div>
|
||||||
<div>Min. cell voltage: {Math.round(status.minCellVolt * 100) / 100}</div>
|
<div>Min. cell voltage: {Math.round(status.minCellVolt * 100) / 100}</div>
|
||||||
<div>Max. cell temperature: {Math.round(status.maxCellTemp * 100) / 100}</div>
|
<div>Max. cell temperature: {Math.round(status.maxCellTemp * 100) / 100}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { ShuntData } from '$lib/client';
|
|
||||||
import { AMSState, type AMSStatus } from '$lib/messages';
|
|
||||||
|
|
||||||
export let amsStatus: AMSStatus | undefined;
|
|
||||||
export let shunt: ShuntData;
|
|
||||||
export let showPopout: boolean = false;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<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">{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>
|
|
||||||
<div class="text-center">
|
|
||||||
<button
|
|
||||||
class="w-1/2 bg-green-600 text-white font-bold h-8 rounded-md hover:bg-green-800 disabled:bg-gray-600"
|
|
||||||
disabled={amsStatus.state != AMSState.TS_INACTIVE}
|
|
||||||
on:click={() => {
|
|
||||||
fetch('/start-charging', { method: 'POST' });
|
|
||||||
}}>Start Charging</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.overview {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 24vw;
|
|
||||||
border: 2px solid black;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress {
|
|
||||||
height: 3em;
|
|
||||||
width: 100%;
|
|
||||||
background-color: white;
|
|
||||||
position: relative;
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
.progress:before {
|
|
||||||
content: attr(data-label);
|
|
||||||
font-weight: bold;
|
|
||||||
color: darkgray;
|
|
||||||
font-size: 1em;
|
|
||||||
position: absolute;
|
|
||||||
text-align: center;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
.progress .value {
|
|
||||||
background-color: green;
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
<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} />
|
|
||||||
@ -1,30 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ShuntData } from '$lib/client';
|
import type { Shunt, ShuntCurrent, ShuntVoltage1, ShuntVoltage2 } from "$lib/messages";
|
||||||
|
export let shuntData: Shunt | undefined;
|
||||||
export let data: ShuntData;
|
export let shuntA: ShuntCurrent | undefined;
|
||||||
|
export let shuntV1: ShuntVoltage1 | undefined;
|
||||||
|
export let shuntV2: ShuntVoltage2 | undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="shunt-content">
|
<div class="shunt-content">
|
||||||
<h2><b>Shunt Status</b></h2>
|
{#if shuntData}
|
||||||
|
<h2><b>Shunt Data</b></h2>
|
||||||
<div class="log-collumn">
|
<div class="log-collumn">
|
||||||
<div class="log-entry">
|
<div>Current: {Math.round(shuntA.current * 100) / 100} </div>
|
||||||
<div>Current:</div>
|
<div>Battery side voltage: {Math.round(shuntV1.voltage * 100) / 100}</div>
|
||||||
<div><b>{data.current.toFixed(1)} A</b></div>
|
<div>Vehicle side voltage: {Math.round(shuntV2.voltage * 100) / 100}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
<div class="log-collumn">
|
|
||||||
<div class="log-entry">
|
|
||||||
<div>Voltage Accu:</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>{data.voltage2.toFixed(2)} V</b></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -32,29 +22,17 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 12vw;
|
width: 12vw;
|
||||||
padding: 2px;
|
|
||||||
margin: 2px;
|
|
||||||
border: 2px solid black;
|
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;
|
padding: 2px;
|
||||||
background-color: hsl(var(--hue), 80%, 80%);
|
margin: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-collumn {
|
.log-collumn {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: hsl(0, 60%, var(--lightness));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,79 +1,39 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
|
||||||
import type { SlaveStatus } from '$lib/messages';
|
import type { SlaveStatus } from '$lib/messages';
|
||||||
import { SlaveLogData } from '$lib/slave-log';
|
import type { SlaveLogData } from '$lib/slave-log';
|
||||||
|
|
||||||
export let id: number;
|
export let id: number;
|
||||||
export let status: SlaveStatus;
|
export let status: SlaveStatus;
|
||||||
export let logData: SlaveLogData | undefined;
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="slave-content" style:--lightness={status.error ? '70%' : '100%'}>
|
<div class="slave-content" style:--lightness={status.error ? "60%" : "100%"}>
|
||||||
<h2><b>Slave #{id} Δrx_t: {Math.round(timeDifference / 1000)}s</b></h2>
|
<h2><b>Slave #{id}</b></h2>
|
||||||
|
|
||||||
<div class="log-collumn" style:--lightness={'' + Math.max(100 - ((timeDifference-200) / 150), 70) + '%'}>
|
<div class="log-collumn">
|
||||||
<div class="log-entry">
|
<div class="log-entry"><div>Error:</div><div><b>{status.error}</b></div></div>
|
||||||
<div>Error:</div>
|
<div class="log-entry"><div>Min. cell voltage:</div><div><b>{Math.round(status.minCellVolt*100)/100}V</b></div></div>
|
||||||
<div><b>{status.error}</b></div>
|
<div class="log-entry"><div>Max. cell voltage:</div><div><b>{Math.round(status.maxCellVolt*100)/100}V</b></div></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>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>SoC</div>
|
||||||
<div>{status.soc}</div> -->
|
<div>{status.soc}</div> -->
|
||||||
<!-- <div>Failed temperature sensors</div>
|
<!-- <div>Failed temperature sensors</div>
|
||||||
<div>{logData?.failedTempSensors ?? 0}</div> -->
|
<div>{logData?.failedTempSensors ?? 0}</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if logData}
|
{#if logData}
|
||||||
<div class="log-collumn">
|
<div class="log-collumn">
|
||||||
{#each logData.voltages as volt, i}
|
{#each logData.voltages as volt, i}
|
||||||
{#if i < 15}
|
{#if i < 15}
|
||||||
<div class="log-entry" style:--hue={Math.max((volt - 2.5) * 70.5, 0)}>
|
<div class="log-entry" style:--hue={Math.max(((volt-2.5)*70.5), 0)}>
|
||||||
<div>V{i}:</div>
|
<div>V{i}:</div><div><b>{Math.round(volt*100)/100}V</b></div>
|
||||||
<div><b>{Math.round(volt * 1000) / 1000}V</b></div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{#each logData.temperatures as temp, i}
|
{#each logData.temperatures as temp, i}
|
||||||
{#if i > 33 && i < 44}
|
{#if i > 33 && i < 44}
|
||||||
<div class="log-entry" style:--hue={210 - temp * 3.5}>
|
<div class="log-entry" style:--hue={210-(temp*3.5)}>
|
||||||
<div>T{i}:</div>
|
<div>T{i}:</div><div><b>{temp}°</b></div>
|
||||||
<div><b>{temp}°</b></div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@ -91,9 +51,9 @@
|
|||||||
border: 2px solid black;
|
border: 2px solid black;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
background-color: hsl(0, 60%, var(--lightness));
|
background-color: hsl(0, 60%, --lightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-entry {
|
.log-entry {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 5fr 3fr;
|
grid-template-columns: 5fr 3fr;
|
||||||
@ -105,10 +65,9 @@
|
|||||||
.log-collumn {
|
.log-collumn {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: hsl(0, 60%, var(--lightness));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { json } from '@sveltejs/kit';
|
|
||||||
import * as can from 'socketcan';
|
|
||||||
|
|
||||||
export function POST() {
|
|
||||||
const network = can.createRawChannel('can0');
|
|
||||||
network.start();
|
|
||||||
network.send({ id: 0x00b, data: Buffer.from([0x01]), ext: false, rtr: false });
|
|
||||||
network.stop();
|
|
||||||
return json({ success: true }, { status: 201 });
|
|
||||||
}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import { nodePolyfills } from 'vite-plugin-node-polyfills';
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [sveltekit(), nodePolyfills()]
|
plugins: [sveltekit()]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user