From 167e9d0a010d1e7a87cd7234394e3c8198f180f0 Mon Sep 17 00:00:00 2001 From: Sergey Savelyev Date: Sat, 3 Jan 2026 08:37:26 -0800 Subject: [PATCH] Implement Integral Data Types (#13) **Rationale:** Integral Types were missing and are needed for Project Nautilus. **Changes:** - Implements Integral Data Types - u64 and i64 implemented through bigint Reviewed-on: https://gitea.sergeysav.com/sergeysav/telemetry_visualization/pulls/13 Co-authored-by: Sergey Savelyev Co-committed-by: Sergey Savelyev --- Cargo.lock | 1 + api-core/src/command.rs | 13 +++ api-core/src/data_type.rs | 34 ++++++++ api-core/src/data_value.rs | 16 ++++ frontend/src/components/CommandInput.vue | 83 ++++++++++++++++++- .../CommandParameterListConfigurator.vue | 17 +++- frontend/src/components/CommandSender.vue | 3 +- frontend/src/components/DynamicComponent.vue | 3 +- frontend/src/components/NumericText.vue | 5 +- frontend/src/composables/command.ts | 9 +- frontend/src/composables/dynamic.ts | 73 +++++++++++++++- frontend/src/composables/json.ts | 22 +++++ frontend/src/composables/websocket.ts | 7 +- frontend/src/views/PanelEditorView.vue | 11 +-- frontend/src/views/PanelView.vue | 3 +- server/Cargo.toml | 1 + server/src/command/service.rs | 33 +++++++- server/src/http/api/cmd.rs | 6 +- server/src/http/error.rs | 3 + server/src/telemetry/history.rs | 48 ++++++----- server/src/telemetry/management_service.rs | 8 +- 21 files changed, 343 insertions(+), 56 deletions(-) create mode 100644 frontend/src/composables/json.ts diff --git a/Cargo.lock b/Cargo.lock index 8c355ce..81e2822 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1953,6 +1953,7 @@ dependencies = [ "fern", "futures-util", "log", + "num-traits", "papaya", "serde", "serde_json", diff --git a/api-core/src/command.rs b/api-core/src/command.rs index e8a210e..ea17322 100644 --- a/api-core/src/command.rs +++ b/api-core/src/command.rs @@ -45,3 +45,16 @@ pub trait IntoCommandDefinition: Sized { fn parse(command: Command) -> Result; } + +impl IntoCommandDefinition for () { + fn create(name: String) -> CommandDefinition { + CommandDefinition { + name, + parameters: vec![], + } + } + + fn parse(_: Command) -> Result { + Ok(()) + } +} diff --git a/api-core/src/data_type.rs b/api-core/src/data_type.rs index 807e084..177fe4e 100644 --- a/api-core/src/data_type.rs +++ b/api-core/src/data_type.rs @@ -7,6 +7,32 @@ pub enum DataType { Float32, Float64, Boolean, + Int8, + Int16, + Int32, + Int64, + Unsigned8, + Unsigned16, + Unsigned32, + Unsigned64, +} + +impl DataType { + pub fn get_data_length(self) -> u64 { + match self { + DataType::Float32 => 4, + DataType::Float64 => 8, + DataType::Boolean => 1, + DataType::Int8 => 1, + DataType::Int16 => 2, + DataType::Int32 => 4, + DataType::Int64 => 8, + DataType::Unsigned8 => 1, + DataType::Unsigned16 => 2, + DataType::Unsigned32 => 4, + DataType::Unsigned64 => 8, + } + } } pub trait ToDataType: Into { @@ -24,3 +50,11 @@ macro_rules! impl_to_data_type { impl_to_data_type!(f32, DataType::Float32); impl_to_data_type!(f64, DataType::Float64); impl_to_data_type!(bool, DataType::Boolean); +impl_to_data_type!(i8, DataType::Int8); +impl_to_data_type!(i16, DataType::Int16); +impl_to_data_type!(i32, DataType::Int32); +impl_to_data_type!(i64, DataType::Int64); +impl_to_data_type!(u8, DataType::Unsigned8); +impl_to_data_type!(u16, DataType::Unsigned16); +impl_to_data_type!(u32, DataType::Unsigned32); +impl_to_data_type!(u64, DataType::Unsigned64); diff --git a/api-core/src/data_value.rs b/api-core/src/data_value.rs index 9e14bd7..f08c79b 100644 --- a/api-core/src/data_value.rs +++ b/api-core/src/data_value.rs @@ -7,6 +7,14 @@ pub enum DataValue { Float32(f32), Float64(f64), Boolean(bool), + Int8(i8), + Int16(i16), + Int32(i32), + Int64(i64), + Unsigned8(u8), + Unsigned16(u16), + Unsigned32(u32), + Unsigned64(u64), } impl DataValue { @@ -15,6 +23,14 @@ impl DataValue { DataValue::Float32(_) => DataType::Float32, DataValue::Float64(_) => DataType::Float64, DataValue::Boolean(_) => DataType::Boolean, + DataValue::Int8(_) => DataType::Int8, + DataValue::Int16(_) => DataType::Int16, + DataValue::Int32(_) => DataType::Int32, + DataValue::Int64(_) => DataType::Int64, + DataValue::Unsigned8(_) => DataType::Unsigned8, + DataValue::Unsigned16(_) => DataType::Unsigned16, + DataValue::Unsigned32(_) => DataType::Unsigned32, + DataValue::Unsigned64(_) => DataType::Unsigned64, } } } diff --git a/frontend/src/components/CommandInput.vue b/frontend/src/components/CommandInput.vue index d41f532..11d6644 100644 --- a/frontend/src/components/CommandInput.vue +++ b/frontend/src/components/CommandInput.vue @@ -3,8 +3,10 @@ import { computed, onMounted } from 'vue'; import { type AnyTypeId, type DynamicDataType, + getLimits, isBooleanType, isNumericType, + type NumericLimits, } from '@/composables/dynamic.ts'; const props = defineProps<{ @@ -17,6 +19,18 @@ const is_numeric = computed(() => { return isNumericType(props.type); }); +const numeric_limit = computed(() => { + if (isNumericType(props.type)) { + return getLimits(props.type); + } + return { + min: 0, + max: 0, + integer: false, + is_big: false, + } as NumericLimits; +}); + const is_boolean = computed(() => { return isBooleanType(props.type); }); @@ -25,18 +39,81 @@ const is_boolean = computed(() => { onMounted(() => { if (model.value === undefined) { if (is_numeric.value) { - model.value = 0.0; + if (numeric_limit.value.integer) { + if (numeric_limit.value.is_big) { + model.value = 0n; + } else { + model.value = 0; + } + } else { + model.value = 0.0; + } } else if (is_boolean.value) { debugger; model.value = false; } } }); + +const value = computed({ + get: () => { + if (is_numeric.value) { + if (numeric_limit.value.is_big) { + return ((model.value || 0n) as bigint).toString(); + } else { + return model.value as number; + } + } else if (is_boolean.value) { + // Force it into a boolean + return !!model.value; + } + return undefined; + }, + set: (value) => { + if (is_numeric.value) { + if (numeric_limit.value.is_big) { + let result = BigInt(value as string); + if (result > (numeric_limit.value.max as bigint)) { + result = numeric_limit.value.max as bigint; + } else if (result < (numeric_limit.value.min as bigint)) { + result = numeric_limit.value.min as bigint; + } + model.value = result; + } else { + let result_value = value as number; + if (numeric_limit.value.integer) { + result_value = Math.round(value as number); + } + if (result_value > (numeric_limit.value.max as number)) { + result_value = numeric_limit.value.max as number; + } else if (result_value < (numeric_limit.value.min as number)) { + result_value = numeric_limit.value.min as number; + } + model.value = result_value; + } + } else if (is_boolean.value) { + model.value = value as boolean; + } + }, +}); diff --git a/frontend/src/components/CommandParameterListConfigurator.vue b/frontend/src/components/CommandParameterListConfigurator.vue index 9eb0dc0..f5a76c0 100644 --- a/frontend/src/components/CommandParameterListConfigurator.vue +++ b/frontend/src/components/CommandParameterListConfigurator.vue @@ -2,6 +2,7 @@ import { type CommandParameterData, type DynamicDataType, + getLimits, isBooleanType, isNumericType, } from '@/composables/dynamic.ts'; @@ -18,7 +19,9 @@ const model = defineModel<{ [key: string]: CommandParameterData }>({ required: true, }); -const { data: command_info } = useCommand(props.command_name); +const { data: command_info, error: command_error } = useCommand( + props.command_name, +); watch([command_info], ([cmd_info]) => { if (cmd_info == null) { @@ -40,7 +43,8 @@ watch([command_info], ([cmd_info]) => { switch (model_param_value.type) { case 'constant': if ( - typeof model_param_value.value == 'number' && + (typeof model_param_value.value == 'number' || + typeof model_param_value.value == 'bigint') && !isNumericType(param.data_type) ) { model_param_value = undefined; @@ -59,7 +63,11 @@ watch([command_info], ([cmd_info]) => { if (model_param_value === undefined) { let default_value: DynamicDataType = 0; if (isNumericType(param.data_type)) { - default_value = 0; + if (getLimits(param.data_type).is_big) { + default_value = 0n; + } else { + default_value = 0; + } } else if (isBooleanType(param.data_type)) { default_value = false; } @@ -91,6 +99,9 @@ watch([command_info], ([cmd_info]) => { > + diff --git a/frontend/src/components/CommandSender.vue b/frontend/src/components/CommandSender.vue index ef301e2..42096af 100644 --- a/frontend/src/components/CommandSender.vue +++ b/frontend/src/components/CommandSender.vue @@ -4,6 +4,7 @@ import { ref } from 'vue'; import CommandParameter from '@/components/CommandParameter.vue'; import FlexDivider from '@/components/FlexDivider.vue'; import type { DynamicDataType } from '@/composables/dynamic.ts'; +import { toJsonString } from '@/composables/json.ts'; const props = defineProps<{ command: CommandDefinition | null; @@ -27,7 +28,7 @@ async function sendCommand() { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(params), + body: toJsonString(params), }); if (response.ok) { result.value = await response.json(); diff --git a/frontend/src/components/DynamicComponent.vue b/frontend/src/components/DynamicComponent.vue index e8d30ab..f33c00c 100644 --- a/frontend/src/components/DynamicComponent.vue +++ b/frontend/src/components/DynamicComponent.vue @@ -7,6 +7,7 @@ import { } from '@/composables/dynamic.ts'; import { computed, defineAsyncComponent, inject, type Ref, ref } from 'vue'; import CommandParameterListConfigurator from '@/components/CommandParameterListConfigurator.vue'; +import { toJsonString } from '@/composables/json.ts'; const TelemetryValue = defineAsyncComponent( () => import('@/components/TelemetryValue.vue'), @@ -166,7 +167,7 @@ async function sendCommand(command: { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(params), + body: toJsonString(params), }); busy.value = false; } diff --git a/frontend/src/components/NumericText.vue b/frontend/src/components/NumericText.vue index 82e1f1e..a08d742 100644 --- a/frontend/src/components/NumericText.vue +++ b/frontend/src/components/NumericText.vue @@ -3,7 +3,7 @@ import { computed, watch } from 'vue'; import CopyableDynamicSpan from '@/components/CopyableDynamicSpan.vue'; const props = defineProps<{ - value: number; + value: number | bigint; max_width: number; copyable?: boolean; include_span?: boolean; @@ -23,6 +23,9 @@ const display_value = computed(() => { if (props.value == 0) { return '0'; } + if (typeof props.value === 'bigint') { + return props.value.toString(); + } let precision = props.value.toPrecision(props.max_width - 3); // Chop off the last character as long as it is a 0 while ( diff --git a/frontend/src/composables/command.ts b/frontend/src/composables/command.ts index 4798dc8..06167f5 100644 --- a/frontend/src/composables/command.ts +++ b/frontend/src/composables/command.ts @@ -41,8 +41,13 @@ export function useCommand(name: MaybeRefOrGetter) { try { const res = await fetch(`/api/cmd/${name_value}`); - data.value = await res.json(); - error.value = null; + if (res.ok) { + data.value = await res.json(); + error.value = null; + } else { + data.value = null; + error.value = await res.text(); + } } catch (e) { data.value = null; error.value = e; diff --git a/frontend/src/composables/dynamic.ts b/frontend/src/composables/dynamic.ts index 2e9e409..452887f 100644 --- a/frontend/src/composables/dynamic.ts +++ b/frontend/src/composables/dynamic.ts @@ -1,5 +1,74 @@ -export const NumericTypes = ['Float32', 'Float64'] as const; +export const NumericTypes = [ + 'Float32', + 'Float64', + 'Int8', + 'Int16', + 'Int32', + 'Int64', + 'Unsigned8', + 'Unsigned16', + 'Unsigned32', + 'Unsigned64', +] as const; export type NumericTypeId = (typeof NumericTypes)[number]; + +export type NumericLimits = { + min: number | bigint; + max: number | bigint; + integer: boolean; + is_big: boolean; +}; + +export function getLimits(numeric_type: NumericTypeId): NumericLimits { + switch (numeric_type) { + case 'Float32': + return { + integer: false, + is_big: false, + min: -3.40282347e38, + max: 3.40282347e38, + }; + case 'Float64': + return { + integer: false, + is_big: false, + min: -1.7976931348623157e308, + max: 1.7976931348623157e308, + }; + case 'Int8': + return { integer: true, is_big: false, min: -128, max: 127 }; + case 'Int16': + return { integer: true, is_big: false, min: -32768, max: 32767 }; + case 'Int32': + return { + integer: true, + is_big: false, + min: -2147483648, + max: 2147483647, + }; + case 'Int64': + return { + integer: true, + is_big: true, + min: -9223372036854775808n, + max: 9223372036854775807n, + }; + case 'Unsigned8': + return { integer: true, is_big: false, min: 0, max: 255 }; + case 'Unsigned16': + return { integer: true, is_big: false, min: 0, max: 65535 }; + case 'Unsigned32': + return { integer: true, is_big: false, min: 0, max: 4294967295 }; + case 'Unsigned64': + return { + integer: true, + is_big: true, + min: 0, + max: 18446744073709551615n, + }; + } +} + export const BooleanTypes = ['Boolean'] as const; export type BooleanTypeId = (typeof BooleanTypes)[number]; export const AnyTypes = [...NumericTypes, ...BooleanTypes] as const; @@ -12,7 +81,7 @@ export function isBooleanType(type: AnyTypeId): type is BooleanTypeId { return BooleanTypes.some((it) => it == type); } -export type DynamicDataType = number | boolean; +export type DynamicDataType = bigint | number | boolean; export type CommandParameterData = | { diff --git a/frontend/src/composables/json.ts b/frontend/src/composables/json.ts new file mode 100644 index 0000000..0e63f14 --- /dev/null +++ b/frontend/src/composables/json.ts @@ -0,0 +1,22 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function toJsonString(data: any): string { + return JSON.stringify(data, (_key, value) => { + if (typeof value == 'bigint') { + // @ts-expect-error TS2339 + return JSON.rawJSON(value.toString()); + } + return value; + }); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function parseJsonString(data: string): any { + // @ts-expect-error TS2345, TS7006 + return JSON.parse(data, (key, value, context) => { + if (key === 'Int64' || key == 'Unsigned64') { + // Or use the constructor of your custom high-precision number library + return BigInt(context.source); + } + return value; + }); +} diff --git a/frontend/src/composables/websocket.ts b/frontend/src/composables/websocket.ts index ae890b6..374f503 100644 --- a/frontend/src/composables/websocket.ts +++ b/frontend/src/composables/websocket.ts @@ -12,6 +12,7 @@ import { } from 'vue'; import type { TelemetryDefinition } from '@/composables/telemetry'; import { onDocumentVisibilityChange } from '@/composables/document.ts'; +import { parseJsonString, toJsonString } from '@/composables/json.ts'; export interface TelemetryDataItem { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -71,7 +72,7 @@ export class WebsocketHandle { } }); this.websocket.addEventListener('message', (event) => { - const message = JSON.parse(event.data); + const message = parseJsonString(event.data); if (message['TlmValue']) { const tlm_value = message['TlmValue'] as TlmValue; const listeners = this.on_telem_value.get(tlm_value.uuid); @@ -126,7 +127,7 @@ export class WebsocketHandle { ([uuid_value, connected, enabled, min_sep, live_value]) => { if (connected && enabled && uuid_value && live_value) { this.websocket?.send( - JSON.stringify({ + toJsonString({ RegisterTlmListener: { uuid: uuid_value, minimum_separation_ms: min_sep, @@ -142,7 +143,7 @@ export class WebsocketHandle { this.on_telem_value.get(uuid_value)?.push(callback_fn); onWatcherCleanup(() => { this.websocket?.send( - JSON.stringify({ + toJsonString({ UnregisterTlmListener: { uuid: uuid_value, }, diff --git a/frontend/src/views/PanelEditorView.vue b/frontend/src/views/PanelEditorView.vue index 79dc236..5840d01 100644 --- a/frontend/src/views/PanelEditorView.vue +++ b/frontend/src/views/PanelEditorView.vue @@ -2,6 +2,7 @@ import DynamicComponent from '@/components/DynamicComponent.vue'; import type { OptionalDynamicComponentData } from '@/composables/dynamic.ts'; import { ref, watchEffect } from 'vue'; +import { parseJsonString, toJsonString } from '@/composables/json.ts'; const data = ref({ type: 'none' }); @@ -29,7 +30,7 @@ async function load(id: string) { loading.value = true; const panel_data = await fetch(`/api/panel/${id}`); const panel_json_value = await panel_data.json(); - data.value = JSON.parse( + data.value = parseJsonString( panel_json_value['data'], ) as OptionalDynamicComponentData; panel_name.value = panel_json_value['name']; @@ -50,9 +51,9 @@ async function save() { if (panel_id_value) { const res = await fetch(`/api/panel/${panel_id_value}`, { method: 'PUT', - body: JSON.stringify({ + body: toJsonString({ name: panel_name.value, - data: JSON.stringify(data.value), + data: toJsonString(data.value), }), headers: { 'Content-Type': 'application/json', @@ -62,9 +63,9 @@ async function save() { } else { const res = await fetch('/api/panel', { method: 'POST', - body: JSON.stringify({ + body: toJsonString({ name: panel_name.value, - data: JSON.stringify(data.value), + data: toJsonString(data.value), }), headers: { 'Content-Type': 'application/json', diff --git a/frontend/src/views/PanelView.vue b/frontend/src/views/PanelView.vue index 2e6aea3..b14a74d 100644 --- a/frontend/src/views/PanelView.vue +++ b/frontend/src/views/PanelView.vue @@ -6,6 +6,7 @@ import type { } from '@/composables/dynamic.ts'; import { computed, provide, ref, watchEffect } from 'vue'; import { useRoute } from 'vue-router'; +import { parseJsonString } from '@/composables/json.ts'; const route = useRoute(); @@ -22,7 +23,7 @@ provide('inputs', inputs); watchEffect(async () => { const panel_data = await fetch(`/api/panel/${id.value}`); const panel_json_value = await panel_data.json(); - panel.value = JSON.parse( + panel.value = parseJsonString( panel_json_value['data'], ) as OptionalDynamicComponentData; }); diff --git a/server/Cargo.toml b/server/Cargo.toml index 2865572..369a7f5 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -15,6 +15,7 @@ derive_more = { workspace = true, features = ["from"] } fern = { workspace = true, features = ["colored"] } futures-util = { workspace = true } log = { workspace = true } +num-traits = { workspace = true } papaya = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/server/src/command/service.rs b/server/src/command/service.rs index 15a284e..4b17ba9 100644 --- a/server/src/command/service.rs +++ b/server/src/command/service.rs @@ -11,6 +11,7 @@ use api::messages::command::{Command, CommandDefinition, CommandHeader, CommandR use api::messages::ResponseMessage; use chrono::Utc; use log::error; +use num_traits::FromPrimitive; use papaya::HashMap; use std::collections::HashMap as StdHashMap; use tokio::sync::oneshot; @@ -102,10 +103,34 @@ impl CommandManagementService { let Some(param_value) = parameters.get(¶meter.name) else { return Err(MisingParameter(parameter.name.clone())); }; - let Some(param_value) = (match parameter.data_type { - DataType::Float32 => param_value.as_f64().map(|v| DataValue::Float32(v as f32)), - DataType::Float64 => param_value.as_f64().map(DataValue::Float64), - DataType::Boolean => param_value.as_bool().map(DataValue::Boolean), + let Some(Some(param_value)) = (match parameter.data_type { + DataType::Float32 => param_value + .as_f64() + .map(|v| Some(DataValue::Float32(v as f32))), + DataType::Float64 => param_value.as_f64().map(DataValue::Float64).map(Some), + DataType::Boolean => param_value.as_bool().map(DataValue::Boolean).map(Some), + DataType::Int8 => param_value + .as_i64() + .map(|v| Some(DataValue::Int8(i8::from_i64(v)?))), + DataType::Int16 => param_value + .as_i64() + .map(|v| Some(DataValue::Int16(i16::from_i64(v)?))), + DataType::Int32 => param_value + .as_i64() + .map(|v| Some(DataValue::Int32(i32::from_i64(v)?))), + DataType::Int64 => param_value.as_i64().map(|v| Some(DataValue::Int64(v))), + DataType::Unsigned8 => param_value + .as_u64() + .map(|v| Some(DataValue::Unsigned8(u8::from_u64(v)?))), + DataType::Unsigned16 => param_value + .as_u64() + .map(|v| Some(DataValue::Unsigned16(u16::from_u64(v)?))), + DataType::Unsigned32 => param_value + .as_u64() + .map(|v| Some(DataValue::Unsigned32(u32::from_u64(v)?))), + DataType::Unsigned64 => { + param_value.as_u64().map(|v| Some(DataValue::Unsigned64(v))) + } }) else { return Err(WrongParameterType { name: parameter.name.clone(), diff --git a/server/src/http/api/cmd.rs b/server/src/http/api/cmd.rs index d9f61ea..8eccd57 100644 --- a/server/src/http/api/cmd.rs +++ b/server/src/http/api/cmd.rs @@ -29,6 +29,10 @@ pub(super) async fn get_one( name: web::Path, ) -> Result { Ok(web::Json( - command_service.get_command_definition(&name.to_string()), + command_service + .get_command_definition(&name.to_string()) + .ok_or_else(|| HttpServerResultError::CmdNotFound { + cmd: name.to_string(), + })?, )) } diff --git a/server/src/http/error.rs b/server/src/http/error.rs index a37fff9..3ce54fb 100644 --- a/server/src/http/error.rs +++ b/server/src/http/error.rs @@ -23,6 +23,8 @@ pub enum HttpServerResultError { PanelUuidNotFound { uuid: Uuid }, #[error(transparent)] Command(#[from] crate::command::error::Error), + #[error("Command Not Found: {cmd}")] + CmdNotFound { cmd: String }, } impl ResponseError for HttpServerResultError { @@ -36,6 +38,7 @@ impl ResponseError for HttpServerResultError { HttpServerResultError::InternalError { .. } => StatusCode::INTERNAL_SERVER_ERROR, HttpServerResultError::PanelUuidNotFound { .. } => StatusCode::NOT_FOUND, HttpServerResultError::Command(inner) => inner.status_code(), + HttpServerResultError::CmdNotFound { .. } => StatusCode::NOT_FOUND, } } fn error_response(&self) -> HttpResponse { diff --git a/server/src/telemetry/history.rs b/server/src/telemetry/history.rs index ab5802f..6b08200 100644 --- a/server/src/telemetry/history.rs +++ b/server/src/telemetry/history.rs @@ -196,9 +196,17 @@ impl HistorySegmentFile { // Write all the values for value in &data.values { match value { - DataValue::Float32(value) => file.write_data::(*value)?, - DataValue::Float64(value) => file.write_data::(*value)?, - DataValue::Boolean(value) => file.write_data::(*value)?, + DataValue::Float32(value) => file.write_data(*value)?, + DataValue::Float64(value) => file.write_data(*value)?, + DataValue::Boolean(value) => file.write_data(*value)?, + DataValue::Int8(value) => file.write_data(*value)?, + DataValue::Int16(value) => file.write_data(*value)?, + DataValue::Int32(value) => file.write_data(*value)?, + DataValue::Int64(value) => file.write_data(*value)?, + DataValue::Unsigned8(value) => file.write_data(*value)?, + DataValue::Unsigned16(value) => file.write_data(*value)?, + DataValue::Unsigned32(value) => file.write_data(*value)?, + DataValue::Unsigned64(value) => file.write_data(*value)?, } } @@ -332,20 +340,20 @@ impl HistorySegmentFile { } fn read_telemetry_item(&mut self, telemetry_data_type: DataType) -> anyhow::Result { - match telemetry_data_type { - DataType::Float32 => { - self.file_position += 4; - Ok(DataValue::Float32(self.file.read_data::()?)) - } - DataType::Float64 => { - self.file_position += 8; - Ok(DataValue::Float64(self.file.read_data::()?)) - } - DataType::Boolean => { - self.file_position += 1; - Ok(DataValue::Boolean(self.file.read_data::()?)) - } - } + self.file_position += telemetry_data_type.get_data_length() as i64; + Ok(match telemetry_data_type { + DataType::Float32 => self.file.read_data::()?.into(), + DataType::Float64 => self.file.read_data::()?.into(), + DataType::Boolean => self.file.read_data::()?.into(), + DataType::Int8 => self.file.read_data::()?.into(), + DataType::Int16 => self.file.read_data::()?.into(), + DataType::Int32 => self.file.read_data::()?.into(), + DataType::Int64 => self.file.read_data::()?.into(), + DataType::Unsigned8 => self.file.read_data::()?.into(), + DataType::Unsigned16 => self.file.read_data::()?.into(), + DataType::Unsigned32 => self.file.read_data::()?.into(), + DataType::Unsigned64 => self.file.read_data::()?.into(), + }) } fn get_telemetry_item( @@ -353,11 +361,7 @@ impl HistorySegmentFile { index: u64, telemetry_data_type: DataType, ) -> anyhow::Result { - let item_length = match telemetry_data_type { - DataType::Float32 => 4, - DataType::Float64 => 8, - DataType::Boolean => 1, - }; + let item_length = telemetry_data_type.get_data_length(); let desired_position = Self::HEADER_LENGTH + self.length * Self::TIMESTAMP_LENGTH + index * item_length; let seek_amount = desired_position as i64 - self.file_position; diff --git a/server/src/telemetry/management_service.rs b/server/src/telemetry/management_service.rs index b4478b7..38b873d 100644 --- a/server/src/telemetry/management_service.rs +++ b/server/src/telemetry/management_service.rs @@ -3,8 +3,6 @@ use crate::telemetry::data_item::TelemetryDataItem; use crate::telemetry::definition::TelemetryDefinition; use crate::telemetry::history::{TelemetryHistory, TelemetryHistoryService}; use anyhow::bail; -use api::data_type::DataType; -use api::data_value::DataValue; use api::messages::telemetry_definition::{ TelemetryDefinitionRequest, TelemetryDefinitionResponse, }; @@ -144,11 +142,7 @@ impl TelemetryManagementService { bail!("Telemetry Item Not Found"); }; - let expected_type = match &tlm_item.value { - DataValue::Float32(_) => DataType::Float32, - DataValue::Float64(_) => DataType::Float64, - DataValue::Boolean(_) => DataType::Boolean, - }; + let expected_type = tlm_item.value.to_data_type(); if expected_type != tlm_data.data.definition.data_type { bail!("Data Type Mismatch"); };