🎨 Format and simplify controls
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
|
||||
import { AnalyticsData } from '$lib/platform_shared/websocket_message'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
const analytics_data: AnalyticsData[] = [];
|
||||
const analytics_data: AnalyticsData[] = []
|
||||
|
||||
const maxAnalyticsData = 100
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { writable } from 'svelte/store'
|
||||
import { IMUData } from '$lib/platform_shared/websocket_message'
|
||||
|
||||
const imu_data: IMUData[] = [];
|
||||
const imu_data: IMUData[] = []
|
||||
const maxIMUData = 100
|
||||
|
||||
|
||||
export const imu = (() => {
|
||||
const { subscribe, update } = writable(imu_data)
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { HumanInputData, KinematicData, ModeData, ModesEnum, WalkGaitData, WalkGaits } from '$lib/platform_shared/websocket_message'
|
||||
import Kinematic from '$lib/kinematic'
|
||||
import {
|
||||
HumanInputData,
|
||||
KinematicData,
|
||||
ModeData,
|
||||
ModesEnum,
|
||||
WalkGaitData,
|
||||
WalkGaits
|
||||
} from '$lib/platform_shared/websocket_message'
|
||||
import { persistentStore } from '$lib/utilities/svelte-utilities'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
|
||||
@@ -10,36 +18,39 @@ export const model = writable()
|
||||
|
||||
export const mode: Writable<ModeData> = writable(ModeData.create({ mode: ModesEnum.DEACTIVATED }))
|
||||
|
||||
export const walkGait: Writable<WalkGaitData> = writable( WalkGaitData.create({gait: WalkGaits.TROT }) )
|
||||
|
||||
export const outControllerData = writable( HumanInputData.create( {left: {x:0,y:0}, right: {x:0,y:0}, height:0, s1:0, speed:0} ) )
|
||||
export const walkGait: Writable<WalkGaitData> = writable(
|
||||
WalkGaitData.create({ gait: WalkGaits.TROT })
|
||||
)
|
||||
|
||||
export const kinematicData = writable(KinematicData.create())
|
||||
|
||||
export const input: Writable<HumanInputData> = writable( HumanInputData.create( {left: {x:0,y:0}, right: {x:0,y:0}, height:0, s1:0, speed:0} ) )
|
||||
export const input: Writable<HumanInputData> = writable(
|
||||
HumanInputData.create({
|
||||
left: { x: 0, y: 0 },
|
||||
right: { x: 0, y: 0 },
|
||||
height: 0.7,
|
||||
s1: 0.5,
|
||||
speed: 0.5
|
||||
})
|
||||
)
|
||||
|
||||
function enumToValuesAndLabels<T extends number>(enumObj: Record<string, T | string>) {
|
||||
const entries = Object.entries(enumObj).filter(
|
||||
([key, v]) => typeof v === 'number' && key !== 'UNRECOGNIZED'
|
||||
) as [string, T][]
|
||||
|
||||
// Following code is generated from CLAUDE CODE
|
||||
// Auto-generate modes array from ModesEnum (excluding UNRECOGNIZED)
|
||||
export const modes = Object.values(ModesEnum)
|
||||
.filter((v): v is ModesEnum => typeof v === 'number' && v !== ModesEnum.UNRECOGNIZED)
|
||||
return {
|
||||
values: entries.map(([, v]) => v),
|
||||
labels: Object.fromEntries(
|
||||
entries.map(([k, v]) => [v, k.charAt(0) + k.slice(1).toLowerCase()])
|
||||
) as Record<T, string>
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-generate mode labels from enum keys
|
||||
export const modeLabels: Record<ModesEnum, string> = Object.entries(ModesEnum)
|
||||
.filter(([_, v]) => typeof v === 'number' && v !== ModesEnum.UNRECOGNIZED)
|
||||
.reduce((acc, [key, value]) => {
|
||||
acc[value as ModesEnum] = key.charAt(0) + key.slice(1).toLowerCase()
|
||||
return acc
|
||||
}, {} as Record<ModesEnum, string>)
|
||||
const modesData = enumToValuesAndLabels<ModesEnum>(ModesEnum)
|
||||
export const modes = modesData.values
|
||||
export const modeLabels = modesData.labels
|
||||
|
||||
// Auto-generate walk gaits array from WalkGaits enum (excluding UNRECOGNIZED)
|
||||
export const walkGaits = Object.values(WalkGaits)
|
||||
.filter((v): v is WalkGaits => typeof v === 'number' && v !== WalkGaits.UNRECOGNIZED)
|
||||
|
||||
// Auto-generate walk gait labels from enum keys
|
||||
export const walkGaitLabels: Record<WalkGaits, string> = Object.entries(WalkGaits)
|
||||
.filter(([_, v]) => typeof v === 'number' && v !== WalkGaits.UNRECOGNIZED)
|
||||
.reduce((acc, [key, value]) => {
|
||||
acc[value as WalkGaits] = key.charAt(0) + key.slice(1).toLowerCase()
|
||||
return acc
|
||||
}, {} as Record<WalkGaits, string>)
|
||||
const walkGaitsData = enumToValuesAndLabels<WalkGaits>(WalkGaits)
|
||||
export const walkGaits = walkGaitsData.values
|
||||
export const walkGaitLabels = walkGaitsData.labels
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { AnglesData } from '$lib/platform_shared/websocket_message'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
|
||||
export const servoAnglesOut: Writable<AnglesData> = writable(AnglesData.create({angles: [0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90]}))
|
||||
export const servoAngles: Writable<AnglesData> = writable(AnglesData.create({angles: [0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90]}))
|
||||
export const servoAnglesOut: Writable<AnglesData> = writable(
|
||||
AnglesData.create({ angles: [0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90] })
|
||||
)
|
||||
export const servoAngles: Writable<AnglesData> = writable(
|
||||
AnglesData.create({ angles: [0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90] })
|
||||
)
|
||||
|
||||
export const logs = writable([] as string[])
|
||||
export const mpu = writable({ heading: 0 })
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { writable } from 'svelte/store'
|
||||
import { encode, decode } from '@msgpack/msgpack'
|
||||
import { WebsocketMessage, type MessageFns, protoMetadata as websocket_md } from '$lib/platform_shared/websocket_message'
|
||||
import {
|
||||
WebsocketMessage,
|
||||
type MessageFns,
|
||||
protoMetadata as websocket_md
|
||||
} from '$lib/platform_shared/websocket_message'
|
||||
import * as WebsocketMessages from '$lib/platform_shared/websocket_message'
|
||||
import type { BinaryWriter } from '@bufbuild/protobuf/wire'
|
||||
|
||||
|
||||
// -------- START PARSING PROTO DATA --------
|
||||
// Auto-build reverse mapping from MessageFns to event key and tag
|
||||
export const MESSAGE_TYPE_TO_KEY = new Map<MessageFns<any>, string>()
|
||||
@@ -13,7 +16,7 @@ export const MESSAGE_KEY_TO_TAG = new Map<string, number>()
|
||||
|
||||
// Build the mapping using references from metadata
|
||||
const websocketMessageType = websocket_md.fileDescriptor.messageType?.find(
|
||||
( msg: { name: string } ) => msg.name === 'WebsocketMessage'
|
||||
(msg: { name: string }) => msg.name === 'WebsocketMessage'
|
||||
)
|
||||
|
||||
if (websocketMessageType?.field) {
|
||||
@@ -33,7 +36,9 @@ if (websocketMessageType?.field) {
|
||||
function get_name_from_messagetype(event_type: MessageFns<any>): string {
|
||||
const event = MESSAGE_TYPE_TO_KEY.get(event_type)
|
||||
if (!event) {
|
||||
throw new Error("Event type not found in 'WebsocketMessage'. The MessageFns you passed doesn't correspond to any WebsocketMessage field.");
|
||||
throw new Error(
|
||||
"Event type not found in 'WebsocketMessage'. The MessageFns you passed doesn't correspond to any WebsocketMessage field."
|
||||
)
|
||||
}
|
||||
return event
|
||||
}
|
||||
@@ -42,7 +47,9 @@ function get_name_from_messagetype(event_type: MessageFns<any>): string {
|
||||
function get_tag_from_messagetype(event_type: MessageFns<any>): number {
|
||||
const fieldNumber = MESSAGE_TYPE_TO_TAG.get(event_type)
|
||||
if (fieldNumber === undefined) {
|
||||
throw new Error("Tag not found in 'WebsocketMessage'. The MessageFns you passed doesn't correspond to any WebsocketMessage field.");
|
||||
throw new Error(
|
||||
"Tag not found in 'WebsocketMessage'. The MessageFns you passed doesn't correspond to any WebsocketMessage field."
|
||||
)
|
||||
}
|
||||
return fieldNumber
|
||||
}
|
||||
@@ -52,29 +59,26 @@ function get_tag_from_messagetype(event_type: MessageFns<any>): number {
|
||||
const socketEvents = ['open', 'close', 'error', 'message', 'unresponsive'] as const
|
||||
type SocketEvent = (typeof socketEvents)[number]
|
||||
|
||||
type TaggedSocketMessage = {"tag": number, "msg": WebsocketMessage}
|
||||
|
||||
|
||||
type TaggedSocketMessage = { tag: number; msg: WebsocketMessage }
|
||||
|
||||
// Only exported for socket test
|
||||
export const decodeMessage = (data: ArrayBuffer): TaggedSocketMessage => {
|
||||
|
||||
const decoded = WebsocketMessage.decode(new Uint8Array(data));
|
||||
const decoded = WebsocketMessage.decode(new Uint8Array(data))
|
||||
const values = Object.entries(decoded).filter(([, value]) => value !== undefined) // Filter all values which are not undefined
|
||||
if (values.length != 1) {
|
||||
throw new Error("Message included either 0 or more than 1 data point")
|
||||
throw new Error('Message included either 0 or more than 1 data point')
|
||||
}
|
||||
const fieldName = values[0][0]
|
||||
const tag = MESSAGE_KEY_TO_TAG.get(fieldName)
|
||||
if (tag === undefined) {
|
||||
throw new Error(`Tag not found for field: ${fieldName}`)
|
||||
}
|
||||
return {"tag": tag, "msg": decoded}
|
||||
return { tag: tag, msg: decoded }
|
||||
}
|
||||
|
||||
export const encodeMessage = (data: WebsocketMessage): Uint8Array<ArrayBuffer> => {
|
||||
const encoded = WebsocketMessage.encode(data).finish();
|
||||
return encoded;
|
||||
const encoded = WebsocketMessage.encode(data).finish()
|
||||
return encoded
|
||||
}
|
||||
|
||||
function createWebSocket() {
|
||||
@@ -92,22 +96,21 @@ function createWebSocket() {
|
||||
connect()
|
||||
}
|
||||
|
||||
function getMsgListeners<MT>(event_type: MessageFns<MT>): Set<(data?: unknown) => void> {
|
||||
function getMsgListeners<MT>(event_type: MessageFns<MT>): Set<(data?: unknown) => void> {
|
||||
const type_tag = get_tag_from_messagetype(event_type)
|
||||
|
||||
const type_listeners = message_listeners.get(type_tag);
|
||||
const type_listeners = message_listeners.get(type_tag)
|
||||
if (type_listeners == undefined) {
|
||||
return new Set()
|
||||
}
|
||||
return type_listeners;
|
||||
return type_listeners
|
||||
}
|
||||
function getListeners<MT>(event: string): Set<(data?: unknown) => void> {
|
||||
|
||||
const event_listeners_forevent = event_listeners.get(event);
|
||||
function getListeners<MT>(event: string): Set<(data?: unknown) => void> {
|
||||
const event_listeners_forevent = event_listeners.get(event)
|
||||
if (event_listeners_forevent == undefined) {
|
||||
return new Set()
|
||||
}
|
||||
return event_listeners_forevent;
|
||||
return event_listeners_forevent
|
||||
}
|
||||
|
||||
function disconnect(reason: SocketEvent, event?: Event) {
|
||||
@@ -135,7 +138,7 @@ function createWebSocket() {
|
||||
}
|
||||
ws.onmessage = frame => {
|
||||
resetUnresponsiveCheck()
|
||||
const {tag, msg} = decodeMessage(frame.data)
|
||||
const { tag, msg } = decodeMessage(frame.data)
|
||||
if (tag) message_listeners.get(tag)?.forEach(listener => listener(msg))
|
||||
}
|
||||
ws.onerror = ev => disconnect('error', ev)
|
||||
@@ -149,14 +152,13 @@ function createWebSocket() {
|
||||
|
||||
// TODO: This looks like it deletes an individual listener, but unsubscribe unsubscribes for everyone. Not sure what it is supposed to do right now
|
||||
message_listeners_totag?.delete(listener as (data?: unknown) => void)
|
||||
if (message_listeners_totag.size == 0) { // No more listeners, so we can unsubscribe
|
||||
if (message_listeners_totag.size == 0) {
|
||||
// No more listeners, so we can unsubscribe
|
||||
unsubscribeToMessageFromServer(event_type)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function unsubscribe_event(event_type: SocketEvent, listener: (data: unknown) => void) {
|
||||
|
||||
const message_listeners_totag = event_listeners.get(event_type)
|
||||
if (!message_listeners_totag) return
|
||||
|
||||
@@ -171,38 +173,38 @@ function createWebSocket() {
|
||||
// T must extend a type of WebsocketMessages
|
||||
function sendEvent<T>(event: MessageFns<T>, data: T) {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return
|
||||
const type = get_name_from_messagetype(event);
|
||||
const wsm = WebsocketMessage.create();
|
||||
(wsm as any)[type] = data
|
||||
const type = get_name_from_messagetype(event)
|
||||
const wsm = WebsocketMessage.create()
|
||||
;(wsm as any)[type] = data
|
||||
send(wsm)
|
||||
}
|
||||
|
||||
function unsubscribeToMessageFromServer<T>(event_type: MessageFns<T>) {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return
|
||||
const event = get_name_from_messagetype(event_type);
|
||||
const unsub_msg = WebsocketMessages.UnsubscribeNotification.create(
|
||||
{tag: get_tag_from_messagetype(event_type)}
|
||||
);
|
||||
send(WebsocketMessage.create({unsubNotif: unsub_msg}));
|
||||
const event = get_name_from_messagetype(event_type)
|
||||
const unsub_msg = WebsocketMessages.UnsubscribeNotification.create({
|
||||
tag: get_tag_from_messagetype(event_type)
|
||||
})
|
||||
send(WebsocketMessage.create({ unsubNotif: unsub_msg }))
|
||||
}
|
||||
|
||||
function subscribeToEvent<T>(event_type: MessageFns<T>) {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return
|
||||
const event = get_name_from_messagetype(event_type);
|
||||
const sub_msg = WebsocketMessages.SubscribeNotification.create(
|
||||
{tag: get_tag_from_messagetype(event_type)}
|
||||
);
|
||||
send(WebsocketMessage.create({subNotif: sub_msg}));
|
||||
const event = get_name_from_messagetype(event_type)
|
||||
const sub_msg = WebsocketMessages.SubscribeNotification.create({
|
||||
tag: get_tag_from_messagetype(event_type)
|
||||
})
|
||||
send(WebsocketMessage.create({ subNotif: sub_msg }))
|
||||
}
|
||||
|
||||
function send(data: WebsocketMessage) {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return
|
||||
const encoded = encodeMessage(data);
|
||||
ws.send(encoded);
|
||||
const encoded = encodeMessage(data)
|
||||
ws.send(encoded)
|
||||
}
|
||||
|
||||
function ping() {
|
||||
send(WebsocketMessage.create({pingmsg: {}}))
|
||||
send(WebsocketMessage.create({ pingmsg: {} }))
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -210,7 +212,7 @@ function createWebSocket() {
|
||||
sendEvent,
|
||||
init,
|
||||
on: <MT>(event_type: MessageFns<MT>, listener: (data: MT) => void): (() => void) => {
|
||||
const tag = get_tag_from_messagetype(event_type);
|
||||
const tag = get_tag_from_messagetype(event_type)
|
||||
|
||||
let message_listeners_totag = message_listeners.get(tag)
|
||||
if (!message_listeners_totag) {
|
||||
@@ -225,7 +227,6 @@ function createWebSocket() {
|
||||
}
|
||||
},
|
||||
onEvent: (event_type: SocketEvent, listener: (data: unknown) => void): (() => void) => {
|
||||
|
||||
return () => {
|
||||
unsubscribe_event(event_type, listener)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ import { DownloadOTAData, RSSIData } from '$lib/platform_shared/websocket_messag
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
type telemetry_data_type = {
|
||||
rssi: RSSIData;
|
||||
download_ota: DownloadOTAData;
|
||||
rssi: RSSIData
|
||||
download_ota: DownloadOTAData
|
||||
}
|
||||
const telemetry_data: telemetry_data_type = { rssi: RSSIData.create(), download_ota: DownloadOTAData.create() }; // Note: perhaps init these as null instead of an undefined create()
|
||||
const telemetry_data: telemetry_data_type = {
|
||||
rssi: RSSIData.create(),
|
||||
download_ota: DownloadOTAData.create()
|
||||
} // Note: perhaps init these as null instead of an undefined create()
|
||||
|
||||
function createTelemetry() {
|
||||
const { subscribe, update } = writable(telemetry_data)
|
||||
@@ -13,10 +16,16 @@ function createTelemetry() {
|
||||
return {
|
||||
subscribe,
|
||||
setRSSI: (data: RSSIData) => {
|
||||
update(telemetry_data => { telemetry_data.rssi = data; return telemetry_data })
|
||||
update(telemetry_data => {
|
||||
telemetry_data.rssi = data
|
||||
return telemetry_data
|
||||
})
|
||||
},
|
||||
setDownloadOTA: (data: DownloadOTAData) => {
|
||||
update(telemetry_data => { telemetry_data.download_ota = data; return telemetry_data })
|
||||
update(telemetry_data => {
|
||||
telemetry_data.download_ota = data
|
||||
return telemetry_data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user