adds telemetry list
This commit is contained in:
13
frontend/src/components/FlexDivider.vue
Normal file
13
frontend/src/components/FlexDivider.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/assets/variables';
|
||||
div {
|
||||
align-self: stretch;
|
||||
border: 1px solid variables.$major-tick;
|
||||
}
|
||||
</style>
|
||||
31
frontend/src/components/PanelHeirarchy.vue
Normal file
31
frontend/src/components/PanelHeirarchy.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
type PanelHeirarchyChildren,
|
||||
PanelHeirarchyType,
|
||||
} from '@/panels/panel';
|
||||
|
||||
defineProps<{
|
||||
heirarchy: PanelHeirarchyChildren;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="child in heirarchy" :key="child.name">
|
||||
<RouterLink
|
||||
v-if="child.type == PanelHeirarchyType.LEAF"
|
||||
:to="child.to"
|
||||
>
|
||||
{{ child.name }}
|
||||
</RouterLink>
|
||||
<template v-if="child.type == PanelHeirarchyType.FOLDER">
|
||||
{{ child.name }}
|
||||
<PanelHeirarchy :heirarchy="child.children"> </PanelHeirarchy>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/assets/variables';
|
||||
</style>
|
||||
@@ -35,6 +35,7 @@ const width = ref(0);
|
||||
const height = ref(0);
|
||||
|
||||
const controls_height = 32;
|
||||
const min_time_label_separation = 250;
|
||||
|
||||
const resize_observer = new ResizeObserver((elements) => {
|
||||
for (const element of elements) {
|
||||
@@ -80,6 +81,7 @@ const time_lines = [
|
||||
10000, // 10s
|
||||
30000, // 30s
|
||||
60000, // 1m
|
||||
150000, // 2.5m
|
||||
300000, // 5m
|
||||
6000000, // 10m
|
||||
18000000, // 30m
|
||||
@@ -92,7 +94,7 @@ const time_lines = [
|
||||
1728000000, // 2d
|
||||
6048000000, // 1w
|
||||
];
|
||||
time_lines.reverse();
|
||||
// time_lines.reverse();
|
||||
const text_offset = computed(() => 5);
|
||||
|
||||
const legend_width = 160;
|
||||
@@ -192,15 +194,20 @@ const legend_y_stride = computed(() => 16);
|
||||
const legend_width_output = computed(() => legend_width - 8);
|
||||
|
||||
const line_duration = computed(() => {
|
||||
const width_px = width.value;
|
||||
const diff_x = max_x.value - min_x.value;
|
||||
return time_lines.find((duration) => diff_x / duration >= 2)!;
|
||||
return time_lines.find((duration) => {
|
||||
const line_count = diff_x / duration;
|
||||
const width_per_line = width_px / line_count;
|
||||
return width_per_line >= min_time_label_separation;
|
||||
})!;
|
||||
});
|
||||
|
||||
const lines = computed(() => {
|
||||
const result = [];
|
||||
for (
|
||||
let i = Math.ceil(max_x.value / line_duration.value);
|
||||
i >= Math.ceil(min_x.value / line_duration.value) - 5;
|
||||
i >= Math.floor(min_x.value / line_duration.value);
|
||||
i--
|
||||
) {
|
||||
const x = i * line_duration.value;
|
||||
@@ -356,6 +363,7 @@ provide<GraphData>(GRAPH_DATA, {
|
||||
:timestamp="tick"
|
||||
:utc="props.utc"
|
||||
:show_millis="line_duration < 1000"
|
||||
:key="tick"
|
||||
></TimeText>
|
||||
</template>
|
||||
</g>
|
||||
@@ -425,8 +433,4 @@ div.controls-header {
|
||||
gap: 0 1em;
|
||||
margin: 0 1em 0 1em;
|
||||
}
|
||||
|
||||
div.controls-header > div.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
62
frontend/src/components/TelemetryInfo.vue
Normal file
62
frontend/src/components/TelemetryInfo.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import type { TelemetryDefinition } from '@/composables/telemetry';
|
||||
import Line from '@/components/TelemetryLine.vue';
|
||||
import SvgGraph from '@/components/SvgGraph.vue';
|
||||
import Axis from '@/components/GraphAxis.vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
telemetry_definition: TelemetryDefinition | null;
|
||||
secondary: TelemetryDefinition | null;
|
||||
}>();
|
||||
|
||||
const lines = computed(() => {
|
||||
const result = [];
|
||||
if (props.secondary) {
|
||||
result.push(props.secondary);
|
||||
}
|
||||
if (props.telemetry_definition) {
|
||||
result.push(props.telemetry_definition);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="row">
|
||||
<span>
|
||||
{{ telemetry_definition?.name || 'No Telemetry Selected' }}
|
||||
</span>
|
||||
</div>
|
||||
<div :class="`row ${telemetry_definition == null ? 'hidden' : ''}`">
|
||||
<span>
|
||||
{{ telemetry_definition?.uuid || '0' }}
|
||||
</span>
|
||||
</div>
|
||||
<div :class="`row ${telemetry_definition == null ? 'hidden' : ''}`">
|
||||
<span>
|
||||
{{ telemetry_definition?.data_type || '0' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row graph grow no-min-height no-basis">
|
||||
<SvgGraph right_axis>
|
||||
<Axis>
|
||||
<Line
|
||||
v-for="line in lines"
|
||||
:data="line.name"
|
||||
:key="line.uuid"
|
||||
></Line>
|
||||
</Axis>
|
||||
</SvgGraph>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.graph {
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -525,21 +525,8 @@ rect.legend:has(~ .legend:hover) {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
div.column {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
div.column,
|
||||
div.row {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
|
||||
104
frontend/src/components/TelemetryList.vue
Normal file
104
frontend/src/components/TelemetryList.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
type TelemetryDefinition,
|
||||
useAllTelemetry,
|
||||
} from '@/composables/telemetry';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
search?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(
|
||||
e: 'select',
|
||||
tlm_entry: [TelemetryDefinition | null, TelemetryDefinition | null],
|
||||
): void;
|
||||
}>();
|
||||
|
||||
const search_value = computed(() => (props.search || '').toLowerCase());
|
||||
|
||||
const { data: telemetry_data } = useAllTelemetry();
|
||||
|
||||
const sorted_tlm_data = computed(() => {
|
||||
const tlm_data = telemetry_data.value;
|
||||
if (tlm_data != null) {
|
||||
return tlm_data
|
||||
.filter((entry) =>
|
||||
entry.name.toLowerCase().includes(search_value.value),
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const mousedover = ref<TelemetryDefinition | null>(null);
|
||||
const selected = ref<TelemetryDefinition | null>(null);
|
||||
|
||||
function onMouseover(tlm_entry: TelemetryDefinition) {
|
||||
mousedover.value = tlm_entry;
|
||||
}
|
||||
|
||||
function onMouseleave() {
|
||||
mousedover.value = null;
|
||||
}
|
||||
|
||||
function onClick(tlm_entry: TelemetryDefinition) {
|
||||
selected.value = tlm_entry;
|
||||
}
|
||||
|
||||
watch([mousedover, selected], ([mousedover_val, selected_val]) => {
|
||||
if (mousedover_val) {
|
||||
emit('select', [
|
||||
mousedover_val,
|
||||
mousedover_val.uuid != selected_val?.uuid ? selected_val : null,
|
||||
]);
|
||||
} else if (selected_val) {
|
||||
emit('select', [selected_val, null]);
|
||||
} else {
|
||||
emit('select', [null, null]);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="sorted_tlm_data.length > 0">
|
||||
<div
|
||||
v-for="tlm_entry in sorted_tlm_data"
|
||||
:class="`row data ${selected?.uuid == tlm_entry.uuid ? 'selected' : ''}`"
|
||||
:key="tlm_entry.uuid"
|
||||
@mouseover="() => onMouseover(tlm_entry)"
|
||||
@mouseleave="() => onMouseleave()"
|
||||
@click="() => onClick(tlm_entry)"
|
||||
>
|
||||
<span>
|
||||
{{ tlm_entry.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="row">
|
||||
<span> No Matches Found </span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/assets/variables';
|
||||
div {
|
||||
padding: 0.3em;
|
||||
border: 0;
|
||||
border-bottom: variables.$gray-3 solid 1px;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.data.selected:has(~ .data:hover),
|
||||
.data:hover ~ .data.selected {
|
||||
background-color: variables.$light-background-color;
|
||||
}
|
||||
|
||||
.data.selected,
|
||||
.data:hover {
|
||||
background-color: variables.$light2-background-color;
|
||||
}
|
||||
</style>
|
||||
47
frontend/src/components/TextInput.vue
Normal file
47
frontend/src/components/TextInput.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, useTemplateRef } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
autofocus?: boolean;
|
||||
placeholder?: string;
|
||||
}>();
|
||||
|
||||
const model = defineModel();
|
||||
const emit = defineEmits<{
|
||||
(e: 'enter'): void;
|
||||
}>();
|
||||
|
||||
const inputRef = useTemplateRef<HTMLInputElement>('input-ref');
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
inputRef.value?.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input
|
||||
ref="input-ref"
|
||||
class="grow"
|
||||
v-model="model"
|
||||
:placeholder="placeholder"
|
||||
v-on:keyup.enter="emit('enter')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/assets/variables';
|
||||
|
||||
input {
|
||||
appearance: none;
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
border: 0 solid variables.$gray-3;
|
||||
border-bottom: 1px solid variables.$gray-3;
|
||||
padding: 0.5ex;
|
||||
font-size: inherit;
|
||||
outline: none;
|
||||
min-width: 5em;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user