adds telemetry list
This commit is contained in:
67
frontend/src/assets/layout.scss
Normal file
67
frontend/src/assets/layout.scss
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
.column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap_half {
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
align-content: center;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen {
|
||||||
|
max-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin: 20vh 10vw;
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen.content {
|
||||||
|
width: calc(100% - 20vw);
|
||||||
|
height: calc(100vh - 40vh);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stretch {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grow2 {
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-basis {
|
||||||
|
flex-basis: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-min-height {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
@@ -4,17 +4,32 @@
|
|||||||
body {
|
body {
|
||||||
color: variables.$text-color;
|
color: variables.$text-color;
|
||||||
font-size: variables.$normal-text-size;
|
font-size: variables.$normal-text-size;
|
||||||
|
font-family: variables.$text-font;
|
||||||
background-color: variables.$background-color;
|
background-color: variables.$background-color;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
stroke-width: 0;
|
stroke-width: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
polyline {
|
polyline {
|
||||||
@@ -26,3 +41,11 @@ polyline {
|
|||||||
#{--indexed-color}: list.nth(variables.$colors, $i);
|
#{--indexed-color}: list.nth(variables.$colors, $i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:visited,
|
||||||
|
a:hover,
|
||||||
|
a:active {
|
||||||
|
text-decoration: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ $magenta-2: oklch(75% 0.2 330);
|
|||||||
|
|
||||||
$text-color: $gray-1;
|
$text-color: $gray-1;
|
||||||
$background-color: $gray-7;
|
$background-color: $gray-7;
|
||||||
|
$light2-background-color: color.adjust($background-color, $lightness: 10%);
|
||||||
$light-background-color: color.adjust($background-color, $lightness: 5%);
|
$light-background-color: color.adjust($background-color, $lightness: 5%);
|
||||||
$dark-background-color: color.adjust($background-color, $lightness: -5%);
|
$dark-background-color: color.adjust($background-color, $lightness: -5%);
|
||||||
|
$dark2-background-color: color.adjust($background-color, $lightness: -10%);
|
||||||
|
|
||||||
$cursor-tick: $gray-0;
|
$cursor-tick: $gray-0;
|
||||||
$time-tick: $gray-1;
|
$time-tick: $gray-1;
|
||||||
|
|||||||
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 height = ref(0);
|
||||||
|
|
||||||
const controls_height = 32;
|
const controls_height = 32;
|
||||||
|
const min_time_label_separation = 250;
|
||||||
|
|
||||||
const resize_observer = new ResizeObserver((elements) => {
|
const resize_observer = new ResizeObserver((elements) => {
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
@@ -80,6 +81,7 @@ const time_lines = [
|
|||||||
10000, // 10s
|
10000, // 10s
|
||||||
30000, // 30s
|
30000, // 30s
|
||||||
60000, // 1m
|
60000, // 1m
|
||||||
|
150000, // 2.5m
|
||||||
300000, // 5m
|
300000, // 5m
|
||||||
6000000, // 10m
|
6000000, // 10m
|
||||||
18000000, // 30m
|
18000000, // 30m
|
||||||
@@ -92,7 +94,7 @@ const time_lines = [
|
|||||||
1728000000, // 2d
|
1728000000, // 2d
|
||||||
6048000000, // 1w
|
6048000000, // 1w
|
||||||
];
|
];
|
||||||
time_lines.reverse();
|
// time_lines.reverse();
|
||||||
const text_offset = computed(() => 5);
|
const text_offset = computed(() => 5);
|
||||||
|
|
||||||
const legend_width = 160;
|
const legend_width = 160;
|
||||||
@@ -192,15 +194,20 @@ const legend_y_stride = computed(() => 16);
|
|||||||
const legend_width_output = computed(() => legend_width - 8);
|
const legend_width_output = computed(() => legend_width - 8);
|
||||||
|
|
||||||
const line_duration = computed(() => {
|
const line_duration = computed(() => {
|
||||||
|
const width_px = width.value;
|
||||||
const diff_x = max_x.value - min_x.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 lines = computed(() => {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (
|
for (
|
||||||
let i = Math.ceil(max_x.value / line_duration.value);
|
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--
|
i--
|
||||||
) {
|
) {
|
||||||
const x = i * line_duration.value;
|
const x = i * line_duration.value;
|
||||||
@@ -356,6 +363,7 @@ provide<GraphData>(GRAPH_DATA, {
|
|||||||
:timestamp="tick"
|
:timestamp="tick"
|
||||||
:utc="props.utc"
|
:utc="props.utc"
|
||||||
:show_millis="line_duration < 1000"
|
:show_millis="line_duration < 1000"
|
||||||
|
:key="tick"
|
||||||
></TimeText>
|
></TimeText>
|
||||||
</template>
|
</template>
|
||||||
</g>
|
</g>
|
||||||
@@ -425,8 +433,4 @@ div.controls-header {
|
|||||||
gap: 0 1em;
|
gap: 0 1em;
|
||||||
margin: 0 1em 0 1em;
|
margin: 0 1em 0 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.controls-header > div.grow {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
</style>
|
</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;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.column {
|
div.column,
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
align-content: flex-start;
|
|
||||||
gap: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.row {
|
div.row {
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
align-content: center;
|
|
||||||
gap: 0.25em;
|
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>
|
||||||
@@ -26,7 +26,6 @@ export function useAllTelemetry() {
|
|||||||
return { data, error };
|
return { data, error };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import './assets/main.scss';
|
import './assets/main.scss';
|
||||||
|
import './assets/layout.scss';
|
||||||
|
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
|
|||||||
92
frontend/src/panels/panel.ts
Normal file
92
frontend/src/panels/panel.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import type { RouteLocationRaw } from 'vue-router';
|
||||||
|
|
||||||
|
export enum PanelHeirarchyType {
|
||||||
|
LEAF,
|
||||||
|
FOLDER,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PanelHeirarchyLeaf = {
|
||||||
|
name: string;
|
||||||
|
to: RouteLocationRaw;
|
||||||
|
type: PanelHeirarchyType.LEAF;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PanelHeirarchyFolder = {
|
||||||
|
name: string;
|
||||||
|
children: PanelHeirarchyChildren;
|
||||||
|
type: PanelHeirarchyType.FOLDER;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PanelHeirarchyChildren = (
|
||||||
|
| PanelHeirarchyFolder
|
||||||
|
| PanelHeirarchyLeaf
|
||||||
|
)[];
|
||||||
|
|
||||||
|
export function getPanelHeirarchy(): PanelHeirarchyChildren {
|
||||||
|
const result: PanelHeirarchyChildren = [];
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
name: 'Graph Test',
|
||||||
|
to: { name: 'graph' },
|
||||||
|
type: PanelHeirarchyType.LEAF,
|
||||||
|
});
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
name: 'Telemetry Elements',
|
||||||
|
to: { name: 'list' },
|
||||||
|
type: PanelHeirarchyType.LEAF,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterHeirarchy(
|
||||||
|
heirarchy: PanelHeirarchyChildren,
|
||||||
|
predicate: (leaf: PanelHeirarchyLeaf) => boolean,
|
||||||
|
): PanelHeirarchyChildren {
|
||||||
|
const result: PanelHeirarchyChildren = [];
|
||||||
|
|
||||||
|
for (const element of heirarchy) {
|
||||||
|
switch (element.type) {
|
||||||
|
case PanelHeirarchyType.LEAF:
|
||||||
|
if (predicate(element)) {
|
||||||
|
result.push(element);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PanelHeirarchyType.FOLDER:
|
||||||
|
const folder_contents = filterHeirarchy(
|
||||||
|
element.children,
|
||||||
|
predicate,
|
||||||
|
);
|
||||||
|
if (folder_contents.length > 0) {
|
||||||
|
result.push({
|
||||||
|
name: element.name,
|
||||||
|
children: folder_contents,
|
||||||
|
type: PanelHeirarchyType.FOLDER,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFirstLeaf(
|
||||||
|
heirarchy: PanelHeirarchyChildren,
|
||||||
|
): PanelHeirarchyLeaf | null {
|
||||||
|
for (const element of heirarchy) {
|
||||||
|
switch (element.type) {
|
||||||
|
case PanelHeirarchyType.LEAF:
|
||||||
|
return element;
|
||||||
|
case PanelHeirarchyType.FOLDER:
|
||||||
|
const leaf = getFirstLeaf(element.children);
|
||||||
|
if (leaf != null) {
|
||||||
|
return leaf;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ const router = createRouter({
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: () => import('../views/HomeView.vue'),
|
component: () => import('../views/EmptyPanelView.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/graph',
|
path: '/graph',
|
||||||
|
|||||||
60
frontend/src/views/EmptyPanelView.vue
Normal file
60
frontend/src/views/EmptyPanelView.vue
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import {
|
||||||
|
filterHeirarchy,
|
||||||
|
getFirstLeaf,
|
||||||
|
getPanelHeirarchy,
|
||||||
|
} from '@/panels/panel';
|
||||||
|
import PanelHeirarchy from '@/components/PanelHeirarchy.vue';
|
||||||
|
import router from '@/router';
|
||||||
|
import TextInput from '@/components/TextInput.vue';
|
||||||
|
|
||||||
|
const searchValue = ref('');
|
||||||
|
|
||||||
|
const heirarchy = getPanelHeirarchy();
|
||||||
|
|
||||||
|
const filtered_heirarchy = computed(() =>
|
||||||
|
filterHeirarchy(heirarchy, (leaf) => {
|
||||||
|
return leaf.name
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchValue.value.toLowerCase());
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
function onEnter() {
|
||||||
|
const leaf = getFirstLeaf(filtered_heirarchy.value);
|
||||||
|
if (leaf != null) {
|
||||||
|
router.push(leaf.to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="column stretch screen content center no-min-height">
|
||||||
|
<div class="row">
|
||||||
|
<TextInput
|
||||||
|
autofocus
|
||||||
|
class="grow"
|
||||||
|
v-model="searchValue"
|
||||||
|
placeholder="Search"
|
||||||
|
@enter="onEnter"
|
||||||
|
></TextInput>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row grow stretch">
|
||||||
|
<div class="column grow scroll no-min-height">
|
||||||
|
<PanelHeirarchy
|
||||||
|
:heirarchy="filtered_heirarchy"
|
||||||
|
v-if="filtered_heirarchy.length > 0"
|
||||||
|
></PanelHeirarchy>
|
||||||
|
<div v-else>
|
||||||
|
<span>No Matches Found</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use '@/assets/variables';
|
||||||
|
</style>
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<RouterLink :to="{ name: 'graph' }">
|
|
||||||
Graph
|
|
||||||
</RouterLink>
|
|
||||||
|
|
||||||
<RouterLink :to="{ name: 'list' }">
|
|
||||||
Tlm List
|
|
||||||
</RouterLink>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@use '@/assets/variables';
|
|
||||||
</style>
|
|
||||||
@@ -1,25 +1,46 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import TextInput from '@/components/TextInput.vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import TelemetryList from '@/components/TelemetryList.vue';
|
||||||
|
import type { TelemetryDefinition } from '@/composables/telemetry';
|
||||||
|
import TelemetryInfo from '@/components/TelemetryInfo.vue';
|
||||||
|
import FlexDivider from '@/components/FlexDivider.vue';
|
||||||
|
|
||||||
import { useAllTelemetry } from '@/composables/telemetry';
|
const searchValue = ref('');
|
||||||
|
|
||||||
const { data: telemetry_data } = useAllTelemetry();
|
const selectValue = ref<
|
||||||
|
[TelemetryDefinition | null, TelemetryDefinition | null]
|
||||||
|
>([null, null]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="telemetry_data">
|
<div class="row stretch screen content center divider">
|
||||||
<div>
|
<div class="column grow2 stretch no-min-height no-basis">
|
||||||
<div v-for="entry in telemetry_data" :key="entry.uuid">
|
<div class="row">
|
||||||
<span>
|
<TextInput
|
||||||
{{ entry.name }}
|
autofocus
|
||||||
</span>
|
class="grow"
|
||||||
<span>
|
v-model="searchValue"
|
||||||
{{ entry.data_type }}
|
placeholder="Search"
|
||||||
</span>
|
></TextInput>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row scroll no-min-height">
|
||||||
|
<div class="column grow stretch">
|
||||||
|
<TelemetryList
|
||||||
|
:search="searchValue"
|
||||||
|
@select="(selection) => (selectValue = selection)"
|
||||||
|
></TelemetryList>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<FlexDivider></FlexDivider>
|
||||||
No Telemetry Data
|
<div class="column grow stretch no-basis">
|
||||||
|
<TelemetryInfo
|
||||||
|
:telemetry_definition="selectValue[0]"
|
||||||
|
:secondary="selectValue[1]"
|
||||||
|
></TelemetryInfo>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -79,8 +79,7 @@ async fn get_tlm_history(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_api(cfg: &mut web::ServiceConfig) {
|
pub fn setup_api(cfg: &mut web::ServiceConfig) {
|
||||||
cfg
|
cfg.service(get_all_tlm_definitions)
|
||||||
.service(get_all_tlm_definitions)
|
|
||||||
.service(get_tlm_definition)
|
.service(get_tlm_definition)
|
||||||
.service(get_tlm_history);
|
.service(get_tlm_history);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -543,7 +543,6 @@ impl TelemetryHistory {
|
|||||||
let mut disk_result = vec![];
|
let mut disk_result = vec![];
|
||||||
let mut ram_result = vec![];
|
let mut ram_result = vec![];
|
||||||
|
|
||||||
|
|
||||||
let mut from = from;
|
let mut from = from;
|
||||||
let mut to = to;
|
let mut to = to;
|
||||||
let initial_to = to;
|
let initial_to = to;
|
||||||
|
|||||||
Reference in New Issue
Block a user