change format settings

This commit is contained in:
2024-12-04 20:00:18 -08:00
parent 07b585f956
commit 4c2b0f454b
14 changed files with 691 additions and 665 deletions

View File

@@ -1,7 +1,8 @@
{ {
"$schema": "https://json.schemastore.org/prettierrc", "$schema": "https://json.schemastore.org/prettierrc",
"semi": false, "semi": true,
"singleQuote": true, "singleQuote": true,
"arrowParens": "avoid" "arrowParens": "always",
"tabWidth": 4
} }

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router';
</script> </script>
<template> <template>

View File

@@ -1,55 +1,55 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, provide, ref, toValue, watch } from 'vue' import { computed, inject, provide, ref, toValue, watch } from 'vue';
import { AXIS_DATA, type AxisData, AxisSide, AxisType } from '@/graph/axis' import { AXIS_DATA, type AxisData, AxisSide, AxisType } from '@/graph/axis';
import { useNow } from '@/composables/ticker' import { useNow } from '@/composables/ticker';
import { GRAPH_DATA, type GraphData } from '@/graph/graph' import { GRAPH_DATA, type GraphData } from '@/graph/graph';
const props = defineProps<{ const props = defineProps<{
y_limits?: [number, number] y_limits?: [number, number];
side?: AxisSide side?: AxisSide;
type?: AxisType type?: AxisType;
}>() }>();
const minor_tick_length = computed(() => 4) const minor_tick_length = computed(() => 4);
const major_tick_length = computed(() => 8) const major_tick_length = computed(() => 8);
const text_offset = computed(() => 5) const text_offset = computed(() => 5);
const side = computed(() => props.side || AxisSide.Right) const side = computed(() => props.side || AxisSide.Right);
const type = computed(() => props.type || AxisType.Linear) const type = computed(() => props.type || AxisType.Linear);
const ticker_locations = [Math.log10(1), Math.log10(2), Math.log10(5)] const ticker_locations = [Math.log10(1), Math.log10(2), Math.log10(5)];
ticker_locations.reverse() ticker_locations.reverse();
const ticker = useNow(33) const ticker = useNow(33);
const min_y = ref(Infinity) const min_y = ref(Infinity);
const max_y = ref(-Infinity) const max_y = ref(-Infinity);
const raw_min_y = ref(Infinity) const raw_min_y = ref(Infinity);
const raw_max_y = ref(-Infinity) const raw_max_y = ref(-Infinity);
const axis_update_watch = ref(0) const axis_update_watch = ref(0);
watch([ticker], () => { watch([ticker], () => {
axis_update_watch.value++ axis_update_watch.value++;
min_y.value = raw_min_y.value min_y.value = raw_min_y.value;
max_y.value = raw_max_y.value max_y.value = raw_max_y.value;
raw_min_y.value = Infinity raw_min_y.value = Infinity;
raw_max_y.value = -Infinity raw_max_y.value = -Infinity;
}) });
function update_min_y(y: number) { function update_min_y(y: number) {
if (y < min_y.value) { if (y < min_y.value) {
min_y.value = y min_y.value = y;
} }
if (y < raw_min_y.value) { if (y < raw_min_y.value) {
raw_min_y.value = y raw_min_y.value = y;
} }
} }
function update_max_y(y: number) { function update_max_y(y: number) {
if (y > max_y.value) { if (y > max_y.value) {
max_y.value = y max_y.value = y;
} }
if (y > raw_max_y.value) { if (y > raw_max_y.value) {
raw_max_y.value = y raw_max_y.value = y;
} }
} }
@@ -58,104 +58,104 @@ function update_max_y(y: number) {
const min_y_value = computed(() => { const min_y_value = computed(() => {
if (props.y_limits) { if (props.y_limits) {
return props.y_limits[0] return props.y_limits[0];
} }
if (type.value == AxisType.Linear) { if (type.value == AxisType.Linear) {
if (max_y.value > min_y.value) { if (max_y.value > min_y.value) {
const half_diff_y_value = (max_y.value - min_y.value) / 2.0 const half_diff_y_value = (max_y.value - min_y.value) / 2.0;
const average_y_value = min_y.value + half_diff_y_value const average_y_value = min_y.value + half_diff_y_value;
return average_y_value - half_diff_y_value * 1.05 return average_y_value - half_diff_y_value * 1.05;
} else { } else {
return -1.0 return -1.0;
} }
} else { } else {
if (max_y.value > min_y.value) { if (max_y.value > min_y.value) {
const half_diff_y_value = const half_diff_y_value =
(Math.log(max_y.value) - Math.log(min_y.value)) / 2.0 (Math.log(max_y.value) - Math.log(min_y.value)) / 2.0;
const average_y_value = Math.log(min_y.value) + half_diff_y_value const average_y_value = Math.log(min_y.value) + half_diff_y_value;
return Math.exp(average_y_value - half_diff_y_value * 1.05) return Math.exp(average_y_value - half_diff_y_value * 1.05);
} else { } else {
return 0.0 return 0.0;
} }
} }
}) });
const max_y_value = computed(() => { const max_y_value = computed(() => {
if (props.y_limits) { if (props.y_limits) {
return props.y_limits[1] return props.y_limits[1];
} }
if (type.value == AxisType.Linear) { if (type.value == AxisType.Linear) {
if (max_y.value > min_y.value) { if (max_y.value > min_y.value) {
const half_diff_y_value = (max_y.value - min_y.value) / 2.0 const half_diff_y_value = (max_y.value - min_y.value) / 2.0;
const average_y_value = min_y.value + half_diff_y_value const average_y_value = min_y.value + half_diff_y_value;
return average_y_value + half_diff_y_value * 1.05 return average_y_value + half_diff_y_value * 1.05;
} else { } else {
return 1.0 return 1.0;
} }
} else { } else {
if (max_y.value > min_y.value) { if (max_y.value > min_y.value) {
const half_diff_y_value = const half_diff_y_value =
(Math.log(max_y.value) - Math.log(min_y.value)) / 2.0 (Math.log(max_y.value) - Math.log(min_y.value)) / 2.0;
const average_y_value = Math.log(min_y.value) + half_diff_y_value const average_y_value = Math.log(min_y.value) + half_diff_y_value;
return Math.exp(average_y_value + half_diff_y_value * 1.05) return Math.exp(average_y_value + half_diff_y_value * 1.05);
} else { } else {
return 1.0 return 1.0;
} }
} }
}) });
const graph_data = inject<GraphData>(GRAPH_DATA)! const graph_data = inject<GraphData>(GRAPH_DATA)!;
const y_map = (y: number) => { const y_map = (y: number) => {
const height = toValue(graph_data.height) const height = toValue(graph_data.height);
const border_top_bottom = toValue(graph_data.border_top_bottom) const border_top_bottom = toValue(graph_data.border_top_bottom);
let max_value = toValue(max_y_value) let max_value = toValue(max_y_value);
let min_value = toValue(min_y_value) let min_value = toValue(min_y_value);
let y_value = y let y_value = y;
if (type.value == AxisType.Logarithmic) { if (type.value == AxisType.Logarithmic) {
max_value = Math.log(max_value) max_value = Math.log(max_value);
min_value = Math.log(min_value) min_value = Math.log(min_value);
y_value = Math.log(y_value) y_value = Math.log(y_value);
}
const diff_y = max_value - min_value
return height * (1 - (y_value - min_value) / diff_y) + border_top_bottom
} }
const diff_y = max_value - min_value;
return height * (1 - (y_value - min_value) / diff_y) + border_top_bottom;
};
provide<AxisData>(AXIS_DATA, { provide<AxisData>(AXIS_DATA, {
axis_update_watch: axis_update_watch, axis_update_watch: axis_update_watch,
min_y_callback: update_min_y, min_y_callback: update_min_y,
max_y_callback: update_max_y, max_y_callback: update_max_y,
y_map: y_map, y_map: y_map,
}) });
const lines = computed(() => { const lines = computed(() => {
const diff_y_val = (max_y_value.value - min_y_value.value) / 2 const diff_y_val = (max_y_value.value - min_y_value.value) / 2;
const diff_log10 = Math.log10(diff_y_val) const diff_log10 = Math.log10(diff_y_val);
const diff_log10_fraction = diff_log10 - Math.floor(diff_log10) const diff_log10_fraction = diff_log10 - Math.floor(diff_log10);
const ticker_location = ticker_locations.find( const ticker_location = ticker_locations.find(
location => location < diff_log10_fraction, (location) => location < diff_log10_fraction,
)! )!;
const grid_spread = Math.pow(10, Math.floor(diff_log10) + ticker_location) const grid_spread = Math.pow(10, Math.floor(diff_log10) + ticker_location);
const major_spread = grid_spread / 2 const major_spread = grid_spread / 2;
const minor_spread = major_spread / 2 const minor_spread = major_spread / 2;
const minor_ticks = [] const minor_ticks = [];
const major_ticks = [] const major_ticks = [];
const grid_lines = [] const grid_lines = [];
for ( for (
let i = Math.floor(min_y_value.value / grid_spread); let i = Math.floor(min_y_value.value / grid_spread);
i <= Math.ceil(max_y_value.value / grid_spread); i <= Math.ceil(max_y_value.value / grid_spread);
i++ i++
) { ) {
const y = i * grid_spread const y = i * grid_spread;
grid_lines.push(y) grid_lines.push(y);
} }
for ( for (
let i = Math.floor(min_y_value.value / major_spread); let i = Math.floor(min_y_value.value / major_spread);
i <= Math.ceil(max_y_value.value / major_spread); i <= Math.ceil(max_y_value.value / major_spread);
i++ i++
) { ) {
const y = i * major_spread const y = i * major_spread;
if (grid_lines.indexOf(y) < 0) { if (grid_lines.indexOf(y) < 0) {
major_ticks.push(y) major_ticks.push(y);
} }
} }
for ( for (
@@ -163,13 +163,13 @@ const lines = computed(() => {
i <= Math.ceil(max_y_value.value / minor_spread); i <= Math.ceil(max_y_value.value / minor_spread);
i++ i++
) { ) {
const y = i * minor_spread const y = i * minor_spread;
if (grid_lines.indexOf(y) < 0 && major_ticks.indexOf(y) < 0) { if (grid_lines.indexOf(y) < 0 && major_ticks.indexOf(y) < 0) {
minor_ticks.push(y) minor_ticks.push(y);
} }
} }
return [minor_ticks, major_ticks, grid_lines] return [minor_ticks, major_ticks, grid_lines];
}) });
</script> </script>
<template> <template>
@@ -181,7 +181,10 @@ const lines = computed(() => {
></polyline> ></polyline>
<text <text
class="right_edge middle_text" class="right_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.max_x)) + text_offset" :x="
graph_data.x_map(toValue(graph_data.max_x)) +
text_offset
"
:y="y_map(tick)" :y="y_map(tick)"
>{{ tick.toFixed(2) }}</text >{{ tick.toFixed(2) }}</text
> >
@@ -194,7 +197,10 @@ const lines = computed(() => {
></polyline> ></polyline>
<text <text
class="right_edge middle_text" class="right_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.max_x)) + text_offset" :x="
graph_data.x_map(toValue(graph_data.max_x)) +
text_offset
"
:y="y_map(tick)" :y="y_map(tick)"
>{{ tick.toFixed(1) }}</text >{{ tick.toFixed(1) }}</text
> >
@@ -207,7 +213,10 @@ const lines = computed(() => {
></polyline> ></polyline>
<text <text
class="right_edge middle_text" class="right_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.max_x)) + text_offset" :x="
graph_data.x_map(toValue(graph_data.max_x)) +
text_offset
"
:y="y_map(tick)" :y="y_map(tick)"
>{{ tick.toFixed(0) }}</text >{{ tick.toFixed(0) }}</text
> >
@@ -222,7 +231,10 @@ const lines = computed(() => {
></polyline> ></polyline>
<text <text
class="left_edge middle_text" class="left_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.min_x)) - text_offset" :x="
graph_data.x_map(toValue(graph_data.min_x)) -
text_offset
"
:y="y_map(tick)" :y="y_map(tick)"
>{{ tick.toFixed(2) }}</text >{{ tick.toFixed(2) }}</text
> >
@@ -235,7 +247,10 @@ const lines = computed(() => {
></polyline> ></polyline>
<text <text
class="left_edge middle_text" class="left_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.min_x)) - text_offset" :x="
graph_data.x_map(toValue(graph_data.min_x)) -
text_offset
"
:y="y_map(tick)" :y="y_map(tick)"
>{{ tick.toFixed(1) }}</text >{{ tick.toFixed(1) }}</text
> >
@@ -248,14 +263,17 @@ const lines = computed(() => {
></polyline> ></polyline>
<text <text
class="left_edge middle_text" class="left_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.min_x)) - text_offset" :x="
graph_data.x_map(toValue(graph_data.min_x)) -
text_offset
"
:y="y_map(tick)" :y="y_map(tick)"
>{{ tick.toFixed(0) }}</text >{{ tick.toFixed(0) }}</text
> >
</template> </template>
</g> </g>
</template> </template>
<g clip-path="url(#content)"> <g>
<slot></slot> <slot></slot>
</g> </g>
</template> </template>

View File

@@ -1,22 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, provide } from 'vue' import { computed, provide } from 'vue';
import { useNow } from '@/composables/ticker' import { useNow } from '@/composables/ticker';
import { GRAPH_DATA, type GraphData } from '@/graph/graph' import { GRAPH_DATA, type GraphData } from '@/graph/graph';
const props = defineProps<{ const props = defineProps<{
width: number width: number;
height: number height: number;
border_left_right?: number border_left_right?: number;
border_top_bottom?: number border_top_bottom?: number;
// min_x?: number // min_x?: number
// max_x?: number // max_x?: number
}>() }>();
const svg_viewbox = computed(() => { const svg_viewbox = computed(() => {
return `0 0 ${props.width} ${props.height}` return `0 0 ${props.width} ${props.height}`;
}) });
const now = useNow(33) const now = useNow(33);
const window_duration = 30 * 1000 // 30 seconds const window_duration = 30 * 1000; // 30 seconds
const time_lines = [ const time_lines = [
1, // 1ms 1, // 1ms
@@ -36,23 +36,24 @@ const time_lines = [
432000000, // 12h 432000000, // 12h
864000000, // 1d 864000000, // 1d
6048000000, // 1w 6048000000, // 1w
] ];
time_lines.reverse() time_lines.reverse();
const text_offset = computed(() => 5) const text_offset = computed(() => 5);
const border_left_right = computed(() => props.border_left_right || 0) const border_left_right = computed(() => props.border_left_right || 0);
const border_top_bottom = computed(() => props.border_top_bottom || 0) const border_top_bottom = computed(() => props.border_top_bottom || 0);
const max_x = now const max_x = now;
const min_x = computed(() => max_x.value - window_duration) const min_x = computed(() => max_x.value - window_duration);
const x_map = (x: number) => { const x_map = (x: number) => {
const diff_x = max_x.value - min_x.value const diff_x = max_x.value - min_x.value;
return ( return (
((props.width - 2 * border_left_right.value) * (x - min_x.value)) / diff_x + ((props.width - 2 * border_left_right.value) * (x - min_x.value)) /
diff_x +
border_left_right.value border_left_right.value
) );
} };
provide<GraphData>(GRAPH_DATA, { provide<GraphData>(GRAPH_DATA, {
border_top_bottom: border_top_bottom, border_top_bottom: border_top_bottom,
@@ -61,22 +62,22 @@ provide<GraphData>(GRAPH_DATA, {
width: () => props.width - 2 * border_left_right.value, width: () => props.width - 2 * border_left_right.value,
height: () => props.height - 2 * border_top_bottom.value, height: () => props.height - 2 * border_top_bottom.value,
x_map: x_map, x_map: x_map,
}) });
const lines = computed(() => { const lines = computed(() => {
const diff_x = max_x.value - min_x.value const diff_x = max_x.value - min_x.value;
const duration = time_lines.find(duration => diff_x / duration >= 3)! const duration = time_lines.find((duration) => diff_x / duration >= 3)!;
const result = [] const result = [];
for ( for (
let i = Math.ceil(max_x.value / duration); let i = Math.ceil(max_x.value / duration);
i >= Math.ceil(min_x.value / duration) - 5; i >= Math.ceil(min_x.value / duration) - 5;
i-- i--
) { ) {
const x = i * duration const x = i * duration;
result.push(x) result.push(x);
} }
return result return result;
}) });
</script> </script>
<template> <template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useTelemetry } from '@/composables/telemetry' import { useTelemetry } from '@/composables/telemetry';
import { import {
computed, computed,
inject, inject,
@@ -8,109 +8,110 @@ import {
type ShallowRef, type ShallowRef,
toValue, toValue,
watch, watch,
} from 'vue' } from 'vue';
import { import {
type TelemetryDataItem, type TelemetryDataItem,
WEBSOCKET_SYMBOL, WEBSOCKET_SYMBOL,
type WebsocketHandle, type WebsocketHandle,
} from '@/composables/websocket' } from '@/composables/websocket';
import { GRAPH_DATA, type GraphData } from '@/graph/graph' import { GRAPH_DATA, type GraphData } from '@/graph/graph';
import { AXIS_DATA, type AxisData } from '@/graph/axis' import { AXIS_DATA, type AxisData } from '@/graph/axis';
const props = defineProps<{ const props = defineProps<{
data: string data: string;
color: string color: string;
}>() }>();
const smoothing_distance = 0.15 * 1000 const smoothing_distance = 0.15 * 1000;
const { data } = useTelemetry(() => props.data) const { data } = useTelemetry(() => props.data);
const websocket = inject<ShallowRef<WebsocketHandle>>(WEBSOCKET_SYMBOL)! const websocket = inject<ShallowRef<WebsocketHandle>>(WEBSOCKET_SYMBOL)!;
const value = websocket.value.listen_to_telemetry(data) const value = websocket.value.listen_to_telemetry(data);
const graph_data = inject<GraphData>(GRAPH_DATA)! const graph_data = inject<GraphData>(GRAPH_DATA)!;
const axis_data = inject<AxisData>(AXIS_DATA)! const axis_data = inject<AxisData>(AXIS_DATA)!;
const min = ref(Infinity) const min = ref(Infinity);
const max = ref(-Infinity) const max = ref(-Infinity);
const memo = shallowRef<TelemetryDataItem[]>([]) const memo = shallowRef<TelemetryDataItem[]>([]);
watch([value], ([val]) => { watch([value], ([val]) => {
const min_x = toValue(graph_data.min_x) const min_x = toValue(graph_data.min_x);
if (val) { if (val) {
const new_memo = [val].concat(memo.value) const new_memo = [val].concat(memo.value);
while ( while (
new_memo.length > 2 && new_memo.length > 2 &&
Date.parse(new_memo[new_memo.length - 2].timestamp) < min_x Date.parse(new_memo[new_memo.length - 2].timestamp) < min_x
) { ) {
new_memo.pop() new_memo.pop();
} }
memo.value = new_memo memo.value = new_memo;
let min_val = Infinity let min_val = Infinity;
let max_val = -Infinity let max_val = -Infinity;
for (const item of new_memo) { for (const item of new_memo) {
const item_val = item.value[data.value!.data_type] as number const item_val = item.value[data.value!.data_type] as number;
min_val = Math.min(min_val, item_val) min_val = Math.min(min_val, item_val);
max_val = Math.max(max_val, item_val) max_val = Math.max(max_val, item_val);
} }
max.value = max_val max.value = max_val;
min.value = min_val min.value = min_val;
} }
}) });
watch( watch(
[min, axis_data.axis_update_watch], [min, axis_data.axis_update_watch],
([min_val]) => { ([min_val]) => {
axis_data.min_y_callback(min_val) axis_data.min_y_callback(min_val);
}, },
{ {
immediate: true, immediate: true,
}, },
) );
watch( watch(
[max, axis_data.axis_update_watch], [max, axis_data.axis_update_watch],
([max_val]) => { ([max_val]) => {
axis_data.max_y_callback(max_val) axis_data.max_y_callback(max_val);
}, },
{ {
immediate: true, immediate: true,
}, },
) );
const points = computed(() => { const points = computed(() => {
let points = '' let points = '';
if (memo.value.length == 0 || data.value == null) { if (memo.value.length == 0 || data.value == null) {
return '' return '';
} }
let last_x = graph_data.x_map(toValue(graph_data.max_x)) let last_x = graph_data.x_map(toValue(graph_data.max_x));
let last_t = toValue(graph_data.max_x) + smoothing_distance let last_t = toValue(graph_data.max_x) + smoothing_distance;
for (const data_item of memo.value) { for (const data_item of memo.value) {
const t = Date.parse(data_item.timestamp) const t = Date.parse(data_item.timestamp);
const v = data_item.value[data.value.data_type] const v = data_item.value[data.value.data_type];
const x = graph_data.x_map(t) const x = graph_data.x_map(t);
const y = axis_data.y_map(v) const y = axis_data.y_map(v);
if (last_t - t < smoothing_distance) { if (last_t - t < smoothing_distance) {
points += ` ${x},${y}` points += ` ${x},${y}`;
} else { } else {
points += ` ${last_x},${y} ${x},${y}` points += ` ${last_x},${y} ${x},${y}`;
} }
last_x = x last_x = x;
last_t = t last_t = t;
if (last_x <= 0.0) { if (last_x <= 0.0) {
break break;
} }
} }
return points return points;
}) });
</script> </script>
<template> <template>
<polyline <polyline
fill="none" fill="none"
clip-path="url(#content)"
:stroke="color" :stroke="color"
stroke-width="1" stroke-width="1"
:points="points" :points="points"

View File

@@ -1,29 +1,29 @@
import { ref, toValue, watchEffect } from 'vue' import { ref, toValue, watchEffect } from 'vue';
import { type MaybeRefOrGetter } from 'vue' import { type MaybeRefOrGetter } from 'vue';
export interface TelemetryDefinition { export interface TelemetryDefinition {
uuid: string uuid: string;
name: string name: string;
data_type: string data_type: string;
} }
export function useTelemetry(name: MaybeRefOrGetter<string>) { export function useTelemetry(name: MaybeRefOrGetter<string>) {
const data = ref<TelemetryDefinition | null>(null) const data = ref<TelemetryDefinition | null>(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = ref<any | null>(null) const error = ref<any | null>(null);
watchEffect(async () => { watchEffect(async () => {
const name_value = toValue(name) const name_value = toValue(name);
try { try {
const res = await fetch(`/api/tlm/${name_value}`) const res = await fetch(`/api/tlm/${name_value}`);
data.value = await res.json() data.value = await res.json();
error.value = null error.value = null;
} catch (e) { } catch (e) {
data.value = null data.value = null;
error.value = e error.value = e;
} }
}) });
return { data, error } return { data, error };
} }

View File

@@ -1,19 +1,19 @@
import { onMounted, onUnmounted, ref, shallowRef } from 'vue' import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
export function useNow(update_ms: number) { export function useNow(update_ms: number) {
const handle = shallowRef<number | undefined>(undefined) const handle = shallowRef<number | undefined>(undefined);
const now = ref(Date.now()) const now = ref(Date.now());
onMounted(() => { onMounted(() => {
handle.value = setInterval(() => { handle.value = setInterval(() => {
now.value = Date.now() now.value = Date.now();
}, update_ms) }, update_ms);
}) });
onUnmounted(() => { onUnmounted(() => {
if (handle.value) { if (handle.value) {
clearInterval(handle.value) clearInterval(handle.value);
} }
}) });
return now return now;
} }

View File

@@ -8,97 +8,102 @@ import {
shallowRef, shallowRef,
toValue, toValue,
watch, watch,
} from 'vue' } from 'vue';
import type { TelemetryDefinition } from '@/composables/telemetry' import type { TelemetryDefinition } from '@/composables/telemetry';
export interface TelemetryDataItem { export interface TelemetryDataItem {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any value: any;
timestamp: string timestamp: string;
} }
interface TlmValue { interface TlmValue {
uuid: string uuid: string;
value: TelemetryDataItem value: TelemetryDataItem;
} }
export class WebsocketHandle { export class WebsocketHandle {
websocket: WebSocket | null websocket: WebSocket | null;
should_be_connected: boolean should_be_connected: boolean;
connected: Ref<boolean> connected: Ref<boolean>;
on_telem_value: Map<string, Array<(value: TelemetryDataItem) => void>> on_telem_value: Map<string, Array<(value: TelemetryDataItem) => void>>;
constructor() { constructor() {
this.websocket = null this.websocket = null;
this.should_be_connected = false this.should_be_connected = false;
this.connected = ref(false) this.connected = ref(false);
this.on_telem_value = new Map() this.on_telem_value = new Map();
} }
connect() { connect() {
this.should_be_connected = true this.should_be_connected = true;
if (this.websocket != null) { if (this.websocket != null) {
return return;
} }
this.websocket = new WebSocket( this.websocket = new WebSocket(
location.protocol.replace('http', 'ws') + '//' + location.host + '/ws', location.protocol.replace('http', 'ws') +
) '//' +
location.host +
'/ws',
);
this.websocket.addEventListener('open', () => { this.websocket.addEventListener('open', () => {
this.connected.value = true this.connected.value = true;
}) });
this.websocket.addEventListener('close', () => { this.websocket.addEventListener('close', () => {
if (this.should_be_connected) { if (this.should_be_connected) {
this.disconnect() this.disconnect();
setTimeout(() => { setTimeout(() => {
this.connect() this.connect();
}, 1000) }, 1000);
} }
}) });
this.websocket.addEventListener('error', () => { this.websocket.addEventListener('error', () => {
if (this.should_be_connected) { if (this.should_be_connected) {
this.disconnect() this.disconnect();
setTimeout(() => { setTimeout(() => {
this.connect() this.connect();
}, 1000) }, 1000);
} }
}) });
this.websocket.addEventListener('message', event => { this.websocket.addEventListener('message', (event) => {
const message = JSON.parse(event.data) const message = JSON.parse(event.data);
if (message['TlmValue']) { if (message['TlmValue']) {
const tlm_value = message['TlmValue'] as TlmValue const tlm_value = message['TlmValue'] as TlmValue;
const listeners = this.on_telem_value.get(tlm_value.uuid) const listeners = this.on_telem_value.get(tlm_value.uuid);
if (listeners) { if (listeners) {
listeners.forEach(listener => { listeners.forEach((listener) => {
listener(tlm_value.value) listener(tlm_value.value);
}) });
} }
} }
}) });
} }
disconnect() { disconnect() {
this.should_be_connected = false this.should_be_connected = false;
if (this.websocket == null) { if (this.websocket == null) {
return return;
} }
this.connected.value = false this.connected.value = false;
this.websocket.close() this.websocket.close();
this.websocket = null this.websocket = null;
this.on_telem_value.clear() this.on_telem_value.clear();
} }
listen_to_telemetry(telemetry: MaybeRefOrGetter<TelemetryDefinition | null>) { listen_to_telemetry(
const value_result = ref<TelemetryDataItem | null>(null) telemetry: MaybeRefOrGetter<TelemetryDefinition | null>,
) {
const value_result = ref<TelemetryDataItem | null>(null);
const uuid = computed(() => { const uuid = computed(() => {
const tlm = toValue(telemetry) const tlm = toValue(telemetry);
if (tlm) { if (tlm) {
return tlm.uuid return tlm.uuid;
} }
return null return null;
}) });
watch([uuid, this.connected], ([uuid_value, connected]) => { watch([uuid, this.connected], ([uuid_value, connected]) => {
if (connected && uuid_value) { if (connected && uuid_value) {
@@ -108,31 +113,31 @@ export class WebsocketHandle {
uuid: uuid_value, uuid: uuid_value,
}, },
}), }),
) );
if (!this.on_telem_value.has(uuid_value)) { if (!this.on_telem_value.has(uuid_value)) {
this.on_telem_value.set(uuid_value, []) this.on_telem_value.set(uuid_value, []);
} }
this.on_telem_value.get(uuid_value)?.push(value => { this.on_telem_value.get(uuid_value)?.push((value) => {
value_result.value = value value_result.value = value;
}) });
} }
}) });
return value_result return value_result;
} }
} }
export const WEBSOCKET_SYMBOL = Symbol() export const WEBSOCKET_SYMBOL = Symbol();
export function useWebsocket() { export function useWebsocket() {
const handle = shallowRef<WebsocketHandle>(new WebsocketHandle()) const handle = shallowRef<WebsocketHandle>(new WebsocketHandle());
onMounted(() => { onMounted(() => {
handle.value.connect() handle.value.connect();
}) });
onUnmounted(() => { onUnmounted(() => {
handle.value.disconnect() handle.value.disconnect();
}) });
return handle return handle;
} }

View File

@@ -1,4 +1,4 @@
import type { Ref } from 'vue' import type { Ref } from 'vue';
export enum AxisSide { export enum AxisSide {
Right, Right,
@@ -11,10 +11,10 @@ export enum AxisType {
Logarithmic, Logarithmic,
} }
export const AXIS_DATA = Symbol() export const AXIS_DATA = Symbol();
export interface AxisData { export interface AxisData {
axis_update_watch: Ref<number> axis_update_watch: Ref<number>;
max_y_callback: (y: number) => void max_y_callback: (y: number) => void;
min_y_callback: (y: number) => void min_y_callback: (y: number) => void;
y_map: (y: number) => number y_map: (y: number) => number;
} }

View File

@@ -1,12 +1,12 @@
import type { MaybeRefOrGetter } from 'vue' import type { MaybeRefOrGetter } from 'vue';
export const GRAPH_DATA = Symbol() export const GRAPH_DATA = Symbol();
export interface GraphData { export interface GraphData {
border_top_bottom: MaybeRefOrGetter<number> border_top_bottom: MaybeRefOrGetter<number>;
min_x: MaybeRefOrGetter<number> min_x: MaybeRefOrGetter<number>;
max_x: MaybeRefOrGetter<number> max_x: MaybeRefOrGetter<number>;
width: MaybeRefOrGetter<number> width: MaybeRefOrGetter<number>;
height: MaybeRefOrGetter<number> height: MaybeRefOrGetter<number>;
x_map: (x: number) => number x_map: (x: number) => number;
} }

View File

@@ -1,11 +1,11 @@
import './assets/main.scss' import './assets/main.scss';
import { createApp } from 'vue' import { createApp } from 'vue';
import App from './App.vue' import App from './App.vue';
import router from './router' import router from './router';
const app = createApp(App) const app = createApp(App);
app.use(router) app.use(router);
app.mount('#app') app.mount('#app');

View File

@@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@@ -9,6 +9,6 @@ const router = createRouter({
component: () => import('../views/HomeView.vue'), component: () => import('../views/HomeView.vue'),
}, },
], ],
}) });
export default router export default router;

View File

@@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWebsocket, WEBSOCKET_SYMBOL } from '@/composables/websocket' import { useWebsocket, WEBSOCKET_SYMBOL } from '@/composables/websocket';
import { provide } from 'vue' import { provide } from 'vue';
import Graph from '@/components/SvgGraph.vue' import Graph from '@/components/SvgGraph.vue';
import Axis from '@/components/GraphAxis.vue' import Axis from '@/components/GraphAxis.vue';
import Line from '@/components/TelemetryLine.vue' import Line from '@/components/TelemetryLine.vue';
const websocket = useWebsocket() const websocket = useWebsocket();
provide(WEBSOCKET_SYMBOL, websocket) provide(WEBSOCKET_SYMBOL, websocket);
</script> </script>
<template> <template>