Implement Commanding #6
40
frontend/src/components/CommandInput.vue
Normal file
40
frontend/src/components/CommandInput.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted } from 'vue';
|
||||||
|
import {
|
||||||
|
type AnyTypeId,
|
||||||
|
type DynamicDataType,
|
||||||
|
isBooleanType,
|
||||||
|
isNumericType,
|
||||||
|
} from '@/composables/dynamic.ts';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
type: AnyTypeId;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const model = defineModel<DynamicDataType>();
|
||||||
|
|
||||||
|
const is_numeric = computed(() => {
|
||||||
|
return isNumericType(props.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
const is_boolean = computed(() => {
|
||||||
|
return isBooleanType(props.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize the parameter to some value:
|
||||||
|
onMounted(() => {
|
||||||
|
if (is_numeric.value) {
|
||||||
|
model.value = 0.0;
|
||||||
|
} else if (is_boolean.value) {
|
||||||
|
model.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input v-if="is_numeric" type="number" v-model="model" />
|
||||||
|
<input v-else-if="is_boolean" type="checkbox" v-model="model" />
|
||||||
|
<span v-else>UNKNOWN INPUT</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -1,36 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CommandParameterDefinition } from '@/composables/command.ts';
|
import type { CommandParameterDefinition } from '@/composables/command.ts';
|
||||||
import { computed, onMounted } from 'vue';
|
import { type DynamicDataType } from '@/composables/dynamic.ts';
|
||||||
|
import CommandInput from '@/components/CommandInput.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
parameter: CommandParameterDefinition;
|
parameter: CommandParameterDefinition;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const model = defineModel<any>(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
const model = defineModel<DynamicDataType>();
|
||||||
|
|
||||||
const is_numeric = computed(() => {
|
|
||||||
return ['Float32', 'Float64'].some((x) => x == props.parameter.data_type);
|
|
||||||
});
|
|
||||||
|
|
||||||
const is_boolean = computed(() => {
|
|
||||||
return 'Boolean' == props.parameter.data_type;
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (is_numeric.value) {
|
|
||||||
model.value = 0.0;
|
|
||||||
} else if (is_boolean.value) {
|
|
||||||
model.value = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label> {{ parameter.name }} </label>
|
<label> {{ parameter.name }} </label>
|
||||||
<input v-if="is_numeric" type="number" v-model="model" />
|
<CommandInput
|
||||||
<input v-else-if="is_boolean" type="checkbox" v-model="model" />
|
:type="parameter.data_type"
|
||||||
<span v-else>UNKNOWN INPUT</span>
|
v-model="model"
|
||||||
|
></CommandInput>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
29
frontend/src/components/CommandParameterDataConfigurator.vue
Normal file
29
frontend/src/components/CommandParameterDataConfigurator.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type CommandParameterData } from '@/composables/dynamic.ts';
|
||||||
|
import { type CommandParameterDefinition } from '@/composables/command.ts';
|
||||||
|
import CommandInput from '@/components/CommandInput.vue';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
param: CommandParameterDefinition;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const model = defineModel<CommandParameterData>({
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="model.type == 'constant'" class="row">
|
||||||
|
<label>Value:</label>
|
||||||
|
<CommandInput
|
||||||
|
:type="param.data_type"
|
||||||
|
v-model="model.value"
|
||||||
|
></CommandInput>
|
||||||
|
</div>
|
||||||
|
<div v-if="model.type == 'input'" class="row">
|
||||||
|
<label>ID:</label>
|
||||||
|
<input type="text" v-model="model.id" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
99
frontend/src/components/CommandParameterListConfigurator.vue
Normal file
99
frontend/src/components/CommandParameterListConfigurator.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
type CommandParameterData,
|
||||||
|
type DynamicDataType,
|
||||||
|
isBooleanType,
|
||||||
|
isNumericType,
|
||||||
|
} from '@/composables/dynamic.ts';
|
||||||
|
import { useCommand } from '@/composables/command.ts';
|
||||||
|
import { watch } from 'vue';
|
||||||
|
import FlexDivider from '@/components/FlexDivider.vue';
|
||||||
|
import CommandParameterDataConfigurator from '@/components/CommandParameterDataConfigurator.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
command_name: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const model = defineModel<{ [key: string]: CommandParameterData }>({
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: command_info } = useCommand(props.command_name);
|
||||||
|
|
||||||
|
watch([command_info], ([cmd_info]) => {
|
||||||
|
if (cmd_info == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const model_value = model.value;
|
||||||
|
for (const key in model_value) {
|
||||||
|
const is_valid_param = cmd_info.parameters.some(
|
||||||
|
(param) => param.name == key,
|
||||||
|
);
|
||||||
|
if (!is_valid_param) {
|
||||||
|
delete model_value[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const param of cmd_info.parameters) {
|
||||||
|
let model_param_value: CommandParameterData | undefined =
|
||||||
|
model_value[param.name];
|
||||||
|
if (model_param_value) {
|
||||||
|
switch (model_param_value.type) {
|
||||||
|
case 'constant':
|
||||||
|
if (
|
||||||
|
typeof model_param_value.value == 'number' &&
|
||||||
|
!isNumericType(param.data_type)
|
||||||
|
) {
|
||||||
|
model_param_value = undefined;
|
||||||
|
} else if (
|
||||||
|
typeof model_param_value.value == 'boolean' &&
|
||||||
|
!isBooleanType(param.data_type)
|
||||||
|
) {
|
||||||
|
model_param_value = undefined;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'input':
|
||||||
|
// Nothing to do
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!model_param_value) {
|
||||||
|
let default_value: DynamicDataType = 0;
|
||||||
|
if (isNumericType(param.data_type)) {
|
||||||
|
default_value = 0;
|
||||||
|
} else if (isBooleanType(param.data_type)) {
|
||||||
|
default_value = false;
|
||||||
|
}
|
||||||
|
model_param_value = {
|
||||||
|
type: 'constant',
|
||||||
|
value: default_value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
model_value[param.name] = model_param_value;
|
||||||
|
}
|
||||||
|
model.value = model_value;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="command_info">
|
||||||
|
<template v-for="param in command_info.parameters" :key="param.name">
|
||||||
|
<FlexDivider></FlexDivider>
|
||||||
|
<div class="row">
|
||||||
|
<label>{{ param.name }}</label>
|
||||||
|
<select v-model="model[param.name].type">
|
||||||
|
<option value="constant">Constant</option>
|
||||||
|
<option value="input">Input</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<CommandParameterDataConfigurator
|
||||||
|
:param="param"
|
||||||
|
v-model="model[param.name]"
|
||||||
|
></CommandParameterDataConfigurator>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span> Loading... </span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -3,12 +3,13 @@ import type { CommandDefinition } from '@/composables/command.ts';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import CommandParameter from '@/components/CommandParameter.vue';
|
import CommandParameter from '@/components/CommandParameter.vue';
|
||||||
import FlexDivider from '@/components/FlexDivider.vue';
|
import FlexDivider from '@/components/FlexDivider.vue';
|
||||||
|
import type { DynamicDataType } from '@/composables/dynamic.ts';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
command: CommandDefinition | null;
|
command: CommandDefinition | null;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const parameters = ref<any>({}); // eslint-disable-line @typescript-eslint/no-explicit-any
|
const parameters = ref<{ [key: string]: DynamicDataType }>({});
|
||||||
const busy = ref(false);
|
const busy = ref(false);
|
||||||
const result = ref('');
|
const result = ref('');
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { OptionalDynamicComponentData } from '@/composables/dynamic.ts';
|
import {
|
||||||
import { computed, defineAsyncComponent } from 'vue';
|
AnyTypes,
|
||||||
|
type CommandParameterData,
|
||||||
|
type DynamicDataType,
|
||||||
|
type OptionalDynamicComponentData,
|
||||||
|
} from '@/composables/dynamic.ts';
|
||||||
|
import { computed, defineAsyncComponent, inject, type Ref, ref } from 'vue';
|
||||||
|
import CommandParameterListConfigurator from '@/components/CommandParameterListConfigurator.vue';
|
||||||
|
|
||||||
const TelemetryValue = defineAsyncComponent(
|
const TelemetryValue = defineAsyncComponent(
|
||||||
() => import('@/components/TelemetryValue.vue'),
|
() => import('@/components/TelemetryValue.vue'),
|
||||||
@@ -8,6 +14,9 @@ const TelemetryValue = defineAsyncComponent(
|
|||||||
const GridLayout = defineAsyncComponent(
|
const GridLayout = defineAsyncComponent(
|
||||||
() => import('@/components/layout/GridLayout.vue'),
|
() => import('@/components/layout/GridLayout.vue'),
|
||||||
);
|
);
|
||||||
|
const CommandInput = defineAsyncComponent(
|
||||||
|
() => import('@/components/CommandInput.vue'),
|
||||||
|
);
|
||||||
|
|
||||||
const model = defineModel<OptionalDynamicComponentData>('data', {
|
const model = defineModel<OptionalDynamicComponentData>('data', {
|
||||||
required: true,
|
required: true,
|
||||||
@@ -19,13 +28,26 @@ const props = defineProps<{
|
|||||||
editable: boolean;
|
editable: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const busy = ref(false);
|
||||||
|
|
||||||
|
// Provide a fallback option
|
||||||
|
const inputs = inject<Ref<{ [id: string]: DynamicDataType }>>(
|
||||||
|
'inputs',
|
||||||
|
ref({}),
|
||||||
|
);
|
||||||
|
|
||||||
const thisSymbol = Symbol();
|
const thisSymbol = Symbol();
|
||||||
|
|
||||||
const isSelected = computed(() => {
|
const isSelected = computed(() => {
|
||||||
return selection.value == thisSymbol && props.editable;
|
return selection.value == thisSymbol && props.editable;
|
||||||
});
|
});
|
||||||
|
|
||||||
function selectThis() {
|
function selectThis(e: Event) {
|
||||||
|
if (props.editable) {
|
||||||
|
// Only do this when we are editable
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
selection.value = thisSymbol;
|
selection.value = thisSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +122,54 @@ function deleteColumn() {
|
|||||||
model.value = grid;
|
model.value = grid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeInput() {
|
||||||
|
model.value = {
|
||||||
|
type: 'input',
|
||||||
|
id: [...Array(32)]
|
||||||
|
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||||
|
.join(''),
|
||||||
|
data_type: 'Float32',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCommandButton() {
|
||||||
|
model.value = {
|
||||||
|
type: 'command_button',
|
||||||
|
text: 'Button Text',
|
||||||
|
command_name: '',
|
||||||
|
parameters: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendCommand(command: {
|
||||||
|
command_name: string;
|
||||||
|
parameters: { [key: string]: CommandParameterData };
|
||||||
|
}) {
|
||||||
|
busy.value = true;
|
||||||
|
|
||||||
|
const params: { [key: string]: DynamicDataType } = {};
|
||||||
|
for (const param_name in command.parameters) {
|
||||||
|
const parameter = command.parameters[param_name];
|
||||||
|
switch (parameter.type) {
|
||||||
|
case 'constant':
|
||||||
|
params[param_name] = parameter.value;
|
||||||
|
break;
|
||||||
|
case 'input':
|
||||||
|
params[param_name] = inputs.value[parameter.id];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetch(`/api/cmd/${command.command_name}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(params),
|
||||||
|
});
|
||||||
|
busy.value = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -123,20 +193,30 @@ function deleteColumn() {
|
|||||||
<button v-if="model.type != 'grid'" @click.stop.prevent="makeGrid">
|
<button v-if="model.type != 'grid'" @click.stop.prevent="makeGrid">
|
||||||
Make Grid
|
Make Grid
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="model.type != 'input'"
|
||||||
|
@click.stop.prevent="makeInput"
|
||||||
|
>
|
||||||
|
Make Input
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="model.type != 'command_button'"
|
||||||
|
@click.stop.prevent="makeCommandButton"
|
||||||
|
>
|
||||||
|
Make Command Button
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<template v-if="model.type == 'none'">
|
<template v-if="model.type == 'none'">
|
||||||
<!-- Intentionally Left Empty -->
|
|
||||||
<span
|
<span
|
||||||
v-if="editable"
|
|
||||||
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
@click.stop.prevent="selectThis"
|
@click="selectThis"
|
||||||
></span>
|
></span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="model.type == 'text'">
|
<template v-else-if="model.type == 'text'">
|
||||||
<span
|
<span
|
||||||
:class="`${model.justify_right ? 'justify-right' : ''} ${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
:class="`${model.justify_right ? 'justify-right' : ''} ${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
@click.stop.prevent="selectThis"
|
@click="selectThis"
|
||||||
>
|
>
|
||||||
{{ model.text }}
|
{{ model.text }}
|
||||||
</span>
|
</span>
|
||||||
@@ -155,7 +235,7 @@ function deleteColumn() {
|
|||||||
<span
|
<span
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
@click.stop.prevent="selectThis"
|
@click="selectThis"
|
||||||
>
|
>
|
||||||
{{ '{' }} {{ model.data }} {{ '}' }}
|
{{ '{' }} {{ model.data }} {{ '}' }}
|
||||||
</span>
|
</span>
|
||||||
@@ -163,7 +243,7 @@ function deleteColumn() {
|
|||||||
v-else
|
v-else
|
||||||
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
:data="model.data"
|
:data="model.data"
|
||||||
@click.stop.prevent="selectThis"
|
@click="selectThis"
|
||||||
></TelemetryValue>
|
></TelemetryValue>
|
||||||
<Teleport v-if="isSelected" to="#inspector">
|
<Teleport v-if="isSelected" to="#inspector">
|
||||||
<label>Telemetry Item: </label>
|
<label>Telemetry Item: </label>
|
||||||
@@ -175,7 +255,7 @@ function deleteColumn() {
|
|||||||
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
:cols="model.columns"
|
:cols="model.columns"
|
||||||
:equal_col_width="model.equal_width"
|
:equal_col_width="model.equal_width"
|
||||||
@click.stop.prevent="selectThis"
|
@click="selectThis"
|
||||||
>
|
>
|
||||||
<template v-for="x in model.cells.length" :key="x">
|
<template v-for="x in model.cells.length" :key="x">
|
||||||
<template v-for="y in model.columns" :key="y">
|
<template v-for="y in model.columns" :key="y">
|
||||||
@@ -210,6 +290,60 @@ function deleteColumn() {
|
|||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="model.type == 'input'">
|
||||||
|
<span
|
||||||
|
v-if="editable"
|
||||||
|
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
|
@click="selectThis"
|
||||||
|
>
|
||||||
|
{{ '[' }} {{ model.id }} {{ ']' }}
|
||||||
|
</span>
|
||||||
|
<CommandInput
|
||||||
|
v-else
|
||||||
|
:type="model.data_type"
|
||||||
|
v-model="inputs[model.id]"
|
||||||
|
></CommandInput>
|
||||||
|
<Teleport v-if="isSelected" to="#inspector">
|
||||||
|
<div class="row">
|
||||||
|
<label>Input ID: </label>
|
||||||
|
<input v-model="model.id" />
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label>Data Type: </label>
|
||||||
|
<select v-model="model.data_type">
|
||||||
|
<option v-for="type in AnyTypes" :key="type" :value="type">
|
||||||
|
{{ type }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="model.type == 'command_button'">
|
||||||
|
<button
|
||||||
|
:disabled="busy"
|
||||||
|
:class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
|
||||||
|
@click.stop.prevent="
|
||||||
|
(e) => (editable ? selectThis(e) : sendCommand(model as any))
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ model.text }}
|
||||||
|
</button>
|
||||||
|
<Teleport v-if="isSelected" to="#inspector">
|
||||||
|
<div class="row">
|
||||||
|
<label>Button Text: </label>
|
||||||
|
<input v-model="model.text" />
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label>Command: </label>
|
||||||
|
<input v-model="model.command_name" />
|
||||||
|
</div>
|
||||||
|
<CommandParameterListConfigurator
|
||||||
|
:key="model.command_name"
|
||||||
|
:command_name="model.command_name"
|
||||||
|
v-model="model.parameters"
|
||||||
|
></CommandParameterListConfigurator>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
<template v-else> ERROR: Unknown data: {{ model }} </template>
|
<template v-else> ERROR: Unknown data: {{ model }} </template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ import { AXIS_DATA, type AxisData } from '@/graph/axis';
|
|||||||
import ValueLabel from '@/components/ValueLabel.vue';
|
import ValueLabel from '@/components/ValueLabel.vue';
|
||||||
import { type Point, PointLine } from '@/graph/line';
|
import { type Point, PointLine } from '@/graph/line';
|
||||||
import TooltipDialog from '@/components/TooltipDialog.vue';
|
import TooltipDialog from '@/components/TooltipDialog.vue';
|
||||||
|
import {
|
||||||
|
type DynamicDataType,
|
||||||
|
isBooleanType,
|
||||||
|
isNumericType,
|
||||||
|
} from '@/composables/dynamic.ts';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: string;
|
data: string;
|
||||||
@@ -98,13 +103,9 @@ watch([value], ([val]) => {
|
|||||||
if (val_t >= min_x) {
|
if (val_t >= min_x) {
|
||||||
const raw_item_val = val.value[telemetry_data.value!.data_type];
|
const raw_item_val = val.value[telemetry_data.value!.data_type];
|
||||||
let item_val = 0;
|
let item_val = 0;
|
||||||
if (
|
if (isNumericType(telemetry_data.value!.data_type)) {
|
||||||
['Float32', 'Float64'].some(
|
|
||||||
(e) => e == telemetry_data.value!.data_type,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
item_val = raw_item_val as number;
|
item_val = raw_item_val as number;
|
||||||
} else if (telemetry_data.value!.data_type == 'Boolean') {
|
} else if (isBooleanType(telemetry_data.value!.data_type)) {
|
||||||
item_val = (raw_item_val as boolean) ? 1 : 0;
|
item_val = (raw_item_val as boolean) ? 1 : 0;
|
||||||
}
|
}
|
||||||
const new_item = {
|
const new_item = {
|
||||||
@@ -140,15 +141,13 @@ watch(
|
|||||||
const response = (await res.json()) as TelemetryDataItem[];
|
const response = (await res.json()) as TelemetryDataItem[];
|
||||||
for (const data_item of response) {
|
for (const data_item of response) {
|
||||||
const val_t = Date.parse(data_item.timestamp);
|
const val_t = Date.parse(data_item.timestamp);
|
||||||
const raw_item_val = data_item.value[type];
|
const raw_item_val = data_item.value[
|
||||||
|
type
|
||||||
|
] as DynamicDataType;
|
||||||
let item_val = 0;
|
let item_val = 0;
|
||||||
if (
|
if (isNumericType(type)) {
|
||||||
['Float32', 'Float64'].some(
|
|
||||||
(e) => e == telemetry_data.value!.data_type,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
item_val = raw_item_val as number;
|
item_val = raw_item_val as number;
|
||||||
} else if (type == 'Boolean') {
|
} else if (isBooleanType(type)) {
|
||||||
item_val = (raw_item_val as boolean) ? 1 : 0;
|
item_val = (raw_item_val as boolean) ? 1 : 0;
|
||||||
}
|
}
|
||||||
const new_item = {
|
const new_item = {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { ref, toValue, watchEffect } from 'vue';
|
import { ref, toValue, watchEffect } from 'vue';
|
||||||
import { type MaybeRefOrGetter } from 'vue';
|
import { type MaybeRefOrGetter } from 'vue';
|
||||||
|
import type { AnyTypeId } from '@/composables/dynamic.ts';
|
||||||
|
|
||||||
export interface CommandParameterDefinition {
|
export interface CommandParameterDefinition {
|
||||||
name: string;
|
name: string;
|
||||||
data_type: string;
|
data_type: AnyTypeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandDefinition {
|
export interface CommandDefinition {
|
||||||
@@ -30,7 +31,7 @@ export function useAllCommands() {
|
|||||||
return { data, error };
|
return { data, error };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ueCommand(name: MaybeRefOrGetter<string>) {
|
export function useCommand(name: MaybeRefOrGetter<string>) {
|
||||||
const data = ref<CommandDefinition | null>(null);
|
const data = ref<CommandDefinition | 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);
|
||||||
|
|||||||
@@ -1,3 +1,29 @@
|
|||||||
|
export const NumericTypes = ['Float32', 'Float64'] as const;
|
||||||
|
export type NumericTypeId = (typeof NumericTypes)[number];
|
||||||
|
export const BooleanTypes = ['Boolean'] as const;
|
||||||
|
export type BooleanTypeId = (typeof BooleanTypes)[number];
|
||||||
|
export const AnyTypes = [...NumericTypes, ...BooleanTypes] as const;
|
||||||
|
export type AnyTypeId = (typeof AnyTypes)[number];
|
||||||
|
|
||||||
|
export function isNumericType(type: AnyTypeId): type is NumericTypeId {
|
||||||
|
return NumericTypes.some((it) => it == type);
|
||||||
|
}
|
||||||
|
export function isBooleanType(type: AnyTypeId): type is BooleanTypeId {
|
||||||
|
return BooleanTypes.some((it) => it == type);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DynamicDataType = number | boolean;
|
||||||
|
|
||||||
|
export type CommandParameterData =
|
||||||
|
| {
|
||||||
|
type: 'constant';
|
||||||
|
value: DynamicDataType;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'input';
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type DynamicComponentData =
|
export type DynamicComponentData =
|
||||||
| { type: 'text'; text: string; justify_right: boolean }
|
| { type: 'text'; text: string; justify_right: boolean }
|
||||||
| { type: 'telemetry'; data: string }
|
| { type: 'telemetry'; data: string }
|
||||||
@@ -6,6 +32,17 @@ export type DynamicComponentData =
|
|||||||
columns: number;
|
columns: number;
|
||||||
equal_width: boolean;
|
equal_width: boolean;
|
||||||
cells: OptionalDynamicComponentData[][];
|
cells: OptionalDynamicComponentData[][];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'input';
|
||||||
|
id: string;
|
||||||
|
data_type: AnyTypeId;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'command_button';
|
||||||
|
text: string;
|
||||||
|
command_name: string;
|
||||||
|
parameters: { [key: string]: CommandParameterData };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OptionalDynamicComponentData =
|
export type OptionalDynamicComponentData =
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { ref, toValue, watchEffect } from 'vue';
|
import { ref, toValue, watchEffect } from 'vue';
|
||||||
import { type MaybeRefOrGetter } from 'vue';
|
import { type MaybeRefOrGetter } from 'vue';
|
||||||
|
import type { AnyTypeId } from '@/composables/dynamic.ts';
|
||||||
|
|
||||||
export interface TelemetryDefinition {
|
export interface TelemetryDefinition {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
name: string;
|
name: string;
|
||||||
data_type: string;
|
data_type: AnyTypeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAllTelemetry() {
|
export function useAllTelemetry() {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DynamicComponent from '@/components/DynamicComponent.vue';
|
import DynamicComponent from '@/components/DynamicComponent.vue';
|
||||||
import type { OptionalDynamicComponentData } from '@/composables/dynamic.ts';
|
import type {
|
||||||
import { computed, ref, watchEffect } from 'vue';
|
DynamicDataType,
|
||||||
|
OptionalDynamicComponentData,
|
||||||
|
} from '@/composables/dynamic.ts';
|
||||||
|
import { computed, provide, ref, watchEffect } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -12,6 +15,10 @@ const panel = ref<OptionalDynamicComponentData>({
|
|||||||
type: 'none',
|
type: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const inputs = ref<{ [id: string]: DynamicDataType }>({});
|
||||||
|
|
||||||
|
provide('inputs', inputs);
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
const panel_data = await fetch(`/api/panel/${id.value}`);
|
const panel_data = await fetch(`/api/panel/${id.value}`);
|
||||||
const panel_json_value = await panel_data.json();
|
const panel_json_value = await panel_data.json();
|
||||||
|
|||||||
Reference in New Issue
Block a user