🎨 Format and simplify controls
This commit is contained in:
@@ -19,14 +19,21 @@
|
||||
jointNames,
|
||||
currentKinematic,
|
||||
walkGait,
|
||||
kinematicData,
|
||||
kinematicData
|
||||
} from '$lib/stores'
|
||||
import { populateModelCache, getToeWorldPositions } from '$lib/utilities'
|
||||
import SceneBuilder from '$lib/sceneBuilder'
|
||||
import { lerp, degToRad } from 'three/src/math/MathUtils'
|
||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
||||
import { type body_state_t } from '$lib/kinematic'
|
||||
import { BezierState, CalibrationState, GaitState, IdleState, RestState, StandState } from '$lib/gait'
|
||||
import {
|
||||
BezierState,
|
||||
CalibrationState,
|
||||
GaitState,
|
||||
IdleState,
|
||||
RestState,
|
||||
StandState
|
||||
} from '$lib/gait'
|
||||
import { radToDeg } from 'three/src/math/MathUtils.js'
|
||||
import type { URDFRobot } from 'urdf-loader'
|
||||
import { get } from 'svelte/store'
|
||||
@@ -50,10 +57,12 @@
|
||||
|
||||
let sceneManager = $state(new SceneBuilder())
|
||||
let canvas: HTMLCanvasElement
|
||||
const NUM_ANGLES = 12 // TODO: This number should come from the robot
|
||||
|
||||
// TODO: This assumes that we have 12 angles (valid for the spot robot) but this should not be a static number defined in each individual data set
|
||||
let currentModelAngles: AnglesData = AnglesData.create({ angles: new Array(12).fill(0) })
|
||||
let modelTargetAngles: AnglesData = AnglesData.create({ angles: new Array(12).fill(0) })
|
||||
let currentModelAngles: AnglesData = AnglesData.create({
|
||||
angles: new Array(NUM_ANGLES).fill(0)
|
||||
})
|
||||
let modelTargetAngles: AnglesData = AnglesData.create({ angles: new Array(NUM_ANGLES).fill(0) })
|
||||
let gui_panel: GUI
|
||||
const SMOOTH_AMOUNT = 0.2
|
||||
|
||||
@@ -63,8 +72,7 @@
|
||||
|
||||
let kinematic = get(currentKinematic)
|
||||
|
||||
// Incredibly ugly but cant be bothered to fix this or statement right now, we cant key on GaitState objects, only the class extensions themselves (which we dont use here)
|
||||
const planners: Record<ModesEnum, IdleState | CalibrationState | RestState | StandState | BezierState> = {
|
||||
const planners: Record<ModesEnum, GaitState> = {
|
||||
[ModesEnum.DEACTIVATED]: new IdleState(),
|
||||
[ModesEnum.IDLE]: new IdleState(),
|
||||
[ModesEnum.CALIBRATION]: new CalibrationState(),
|
||||
@@ -119,7 +127,9 @@
|
||||
walkGait.subscribe(gait => {
|
||||
const walkPlanner = planners[ModesEnum.WALK]
|
||||
if (!(walkPlanner instanceof BezierState)) {
|
||||
throw new Error(`Expected BezierState for WALK mode, got ${walkPlanner.constructor.name}`)
|
||||
throw new Error(
|
||||
`Expected BezierState for WALK mode, got ${walkPlanner.constructor.name}`
|
||||
)
|
||||
}
|
||||
walkPlanner.set_mode(gait.gait)
|
||||
})
|
||||
@@ -163,14 +173,16 @@
|
||||
}
|
||||
|
||||
const updateKinematicPosition = () => {
|
||||
kinematicData.set(KinematicData.create({
|
||||
omega: settings.omega,
|
||||
phi: settings.phi,
|
||||
psi: settings.psi,
|
||||
xm: settings.xm,
|
||||
ym: settings.ym,
|
||||
zm: settings.zm
|
||||
}))
|
||||
kinematicData.set(
|
||||
KinematicData.create({
|
||||
omega: settings.omega,
|
||||
phi: settings.phi,
|
||||
psi: settings.psi,
|
||||
xm: settings.xm,
|
||||
ym: settings.ym,
|
||||
zm: settings.zm
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const setSceneBackground = (c: string | null) => (sceneManager.scene.background = new Color(c!))
|
||||
@@ -178,7 +190,9 @@
|
||||
const updateAngles = (name: string, angle: number) => {
|
||||
modelTargetAngles.angles[$jointNames.indexOf(name)] = angle * (180 / Math.PI)
|
||||
servoAnglesOut.set(
|
||||
AnglesData.create({ angles: modelTargetAngles.angles.map(num => Math.round(num)) })
|
||||
AnglesData.create({
|
||||
angles: modelTargetAngles.angles.map(num => Math.round(num))
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -282,7 +296,7 @@
|
||||
|
||||
const update_gait = () => {
|
||||
if (sceneManager.isDragging || !settings['Internal kinematic']) return
|
||||
const controlData = get(outControllerData)
|
||||
const controlData = get(input)
|
||||
|
||||
let planner = planners[get(mode).mode]
|
||||
const delta = performance.now() - lastTick
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ModeData, ModesEnum } from '$lib/platform_shared/websocket_message'
|
||||
import { mode, modes } from '$lib/stores'
|
||||
import { mode } from '$lib/stores'
|
||||
|
||||
const deactivate = async () => {
|
||||
mode.set(ModeData.create({ mode: ModesEnum.DEACTIVATED }))
|
||||
|
||||
@@ -169,8 +169,6 @@ export class BezierState extends GaitState {
|
||||
}
|
||||
|
||||
set_mode(mode: WalkGaits, duty?: number, order?: [number, number, number, number]) {
|
||||
console.log('BezierState set_mode', mode)
|
||||
|
||||
this.mode = mode
|
||||
if (mode === WalkGaits.CRAWL) {
|
||||
this.speed_factor = 0.5
|
||||
|
||||
@@ -5,163 +5,167 @@
|
||||
// source: platform_shared/imu_report.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
import { BinaryReader, BinaryWriter } from '@bufbuild/protobuf/wire'
|
||||
|
||||
export const protobufPackage = "";
|
||||
export const protobufPackage = ''
|
||||
|
||||
export interface IMUReport {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
temp: number;
|
||||
success: boolean;
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
temp: number
|
||||
success: boolean
|
||||
}
|
||||
|
||||
function createBaseIMUReport(): IMUReport {
|
||||
return { x: 0, y: 0, z: 0, temp: 0, success: false };
|
||||
return { x: 0, y: 0, z: 0, temp: 0, success: false }
|
||||
}
|
||||
|
||||
export const IMUReport: MessageFns<IMUReport> = {
|
||||
encode(message: IMUReport, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.x !== 0) {
|
||||
writer.uint32(13).float(message.x);
|
||||
}
|
||||
if (message.y !== 0) {
|
||||
writer.uint32(21).float(message.y);
|
||||
}
|
||||
if (message.z !== 0) {
|
||||
writer.uint32(29).float(message.z);
|
||||
}
|
||||
if (message.temp !== 0) {
|
||||
writer.uint32(37).float(message.temp);
|
||||
}
|
||||
if (message.success !== false) {
|
||||
writer.uint32(40).bool(message.success);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): IMUReport {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseIMUReport();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 13) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.x = reader.float();
|
||||
continue;
|
||||
encode(message: IMUReport, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.x !== 0) {
|
||||
writer.uint32(13).float(message.x)
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 21) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.y = reader.float();
|
||||
continue;
|
||||
if (message.y !== 0) {
|
||||
writer.uint32(21).float(message.y)
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 29) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.z = reader.float();
|
||||
continue;
|
||||
if (message.z !== 0) {
|
||||
writer.uint32(29).float(message.z)
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 37) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.temp = reader.float();
|
||||
continue;
|
||||
if (message.temp !== 0) {
|
||||
writer.uint32(37).float(message.temp)
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.success = reader.bool();
|
||||
continue;
|
||||
if (message.success !== false) {
|
||||
writer.uint32(40).bool(message.success)
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
return writer
|
||||
},
|
||||
|
||||
fromJSON(object: any): IMUReport {
|
||||
return {
|
||||
x: isSet(object.x) ? globalThis.Number(object.x) : 0,
|
||||
y: isSet(object.y) ? globalThis.Number(object.y) : 0,
|
||||
z: isSet(object.z) ? globalThis.Number(object.z) : 0,
|
||||
temp: isSet(object.temp) ? globalThis.Number(object.temp) : 0,
|
||||
success: isSet(object.success) ? globalThis.Boolean(object.success) : false,
|
||||
};
|
||||
},
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): IMUReport {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
const end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseIMUReport()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 13) {
|
||||
break
|
||||
}
|
||||
|
||||
toJSON(message: IMUReport): unknown {
|
||||
const obj: any = {};
|
||||
if (message.x !== 0) {
|
||||
obj.x = message.x;
|
||||
}
|
||||
if (message.y !== 0) {
|
||||
obj.y = message.y;
|
||||
}
|
||||
if (message.z !== 0) {
|
||||
obj.z = message.z;
|
||||
}
|
||||
if (message.temp !== 0) {
|
||||
obj.temp = message.temp;
|
||||
}
|
||||
if (message.success !== false) {
|
||||
obj.success = message.success;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
message.x = reader.float()
|
||||
continue
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 21) {
|
||||
break
|
||||
}
|
||||
|
||||
create<I extends Exact<DeepPartial<IMUReport>, I>>(base?: I): IMUReport {
|
||||
return IMUReport.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<IMUReport>, I>>(object: I): IMUReport {
|
||||
const message = createBaseIMUReport();
|
||||
message.x = object.x ?? 0;
|
||||
message.y = object.y ?? 0;
|
||||
message.z = object.z ?? 0;
|
||||
message.temp = object.temp ?? 0;
|
||||
message.success = object.success ?? false;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
message.y = reader.float()
|
||||
continue
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 29) {
|
||||
break
|
||||
}
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
||||
message.z = reader.float()
|
||||
continue
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 37) {
|
||||
break
|
||||
}
|
||||
|
||||
export type DeepPartial<T> = T extends Builtin ? T
|
||||
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>;
|
||||
message.temp = reader.float()
|
||||
continue
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break
|
||||
}
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||
export type Exact<P, I extends P> = P extends Builtin ? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
|
||||
message.success = reader.bool()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(object: any): IMUReport {
|
||||
return {
|
||||
x: isSet(object.x) ? globalThis.Number(object.x) : 0,
|
||||
y: isSet(object.y) ? globalThis.Number(object.y) : 0,
|
||||
z: isSet(object.z) ? globalThis.Number(object.z) : 0,
|
||||
temp: isSet(object.temp) ? globalThis.Number(object.temp) : 0,
|
||||
success: isSet(object.success) ? globalThis.Boolean(object.success) : false
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: IMUReport): unknown {
|
||||
const obj: any = {}
|
||||
if (message.x !== 0) {
|
||||
obj.x = message.x
|
||||
}
|
||||
if (message.y !== 0) {
|
||||
obj.y = message.y
|
||||
}
|
||||
if (message.z !== 0) {
|
||||
obj.z = message.z
|
||||
}
|
||||
if (message.temp !== 0) {
|
||||
obj.temp = message.temp
|
||||
}
|
||||
if (message.success !== false) {
|
||||
obj.success = message.success
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<IMUReport>, I>>(base?: I): IMUReport {
|
||||
return IMUReport.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<IMUReport>, I>>(object: I): IMUReport {
|
||||
const message = createBaseIMUReport()
|
||||
message.x = object.x ?? 0
|
||||
message.y = object.y ?? 0
|
||||
message.z = object.z ?? 0
|
||||
message.temp = object.temp ?? 0
|
||||
message.success = object.success ?? false
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined
|
||||
|
||||
export type DeepPartial<T> =
|
||||
T extends Builtin ? T
|
||||
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
export type Exact<P, I extends P> =
|
||||
P extends Builtin ? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
|
||||
[K in Exclude<keyof I, KeysOfUnion<P>>]: never
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined;
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
export interface MessageFns<T> {
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
||||
fromJSON(object: any): T;
|
||||
toJSON(message: T): unknown;
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type { AnalyticsData } from "$lib/platform_shared/websocket_message";
|
||||
|
||||
export type vector = { x: number; y: number }
|
||||
|
||||
|
||||
export type GithubRelease = {
|
||||
message: string
|
||||
tag_name: string
|
||||
@@ -12,13 +9,10 @@ export type GithubRelease = {
|
||||
}>
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type NetworkList = {
|
||||
networks: NetworkItem[]
|
||||
}
|
||||
|
||||
|
||||
export type NetworkItem = {
|
||||
rssi: number
|
||||
ssid: string
|
||||
@@ -46,14 +40,11 @@ export type ApSettings = {
|
||||
subnet_mask: string
|
||||
}
|
||||
|
||||
|
||||
export type Rssi = {
|
||||
rssi: number
|
||||
ssid: string
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type CameraSettings = {
|
||||
framesize: number
|
||||
quality: number
|
||||
|
||||
@@ -21,7 +21,17 @@
|
||||
useFeatureFlags,
|
||||
walkGait
|
||||
} from '$lib/stores'
|
||||
import { AnalyticsData, AnglesData, DownloadOTAData, HumanInputData, KinematicData, ModeData, RSSIData, SonarData, WalkGaitData } from '$lib/platform_shared/websocket_message'
|
||||
import {
|
||||
AnalyticsData,
|
||||
AnglesData,
|
||||
DownloadOTAData,
|
||||
HumanInputData,
|
||||
KinematicData,
|
||||
ModeData,
|
||||
RSSIData,
|
||||
SonarData,
|
||||
WalkGaitData
|
||||
} from '$lib/platform_shared/websocket_message'
|
||||
import { Throttler } from '$lib/utilities'
|
||||
|
||||
interface Props {
|
||||
@@ -39,10 +49,14 @@
|
||||
|
||||
addEventListeners()
|
||||
|
||||
outControllerData.subscribe(data => socket.sendEvent(HumanInputData, data))
|
||||
input.subscribe(data =>
|
||||
throttler.throttle(() => socket.sendEvent(HumanInputData, data), 100)
|
||||
)
|
||||
mode.subscribe(data => socket.sendEvent(ModeData, data))
|
||||
walkGait.subscribe(data => socket.sendEvent(WalkGaitData, data))
|
||||
servoAnglesOut.subscribe(data => socket.sendEvent(AnglesData, data))
|
||||
servoAnglesOut.subscribe(data =>
|
||||
throttler.throttle(() => socket.sendEvent(AnglesData, data), 100)
|
||||
)
|
||||
kinematicData.subscribe(data => socket.sendEvent(KinematicData, data))
|
||||
})
|
||||
|
||||
@@ -50,26 +64,35 @@
|
||||
removeEventListeners()
|
||||
})
|
||||
|
||||
const eventListeners: (() => void)[] = [];
|
||||
const eventListeners: (() => void)[] = []
|
||||
const addEventListeners = () => {
|
||||
eventListeners.push(...[
|
||||
socket.onEvent('open', handleOpen),
|
||||
socket.onEvent('close', handleClose),
|
||||
socket.onEvent('error', handleError),
|
||||
socket.on(RSSIData, (data) => telemetry.setRSSI(data)),
|
||||
socket.on(ModeData, (data) => mode.set(data)),
|
||||
socket.on(AnalyticsData, (data) => {analytics.addData(data)}),
|
||||
socket.on(AnglesData, (data) => {servoAngles.set(data)})
|
||||
])
|
||||
eventListeners.push(
|
||||
...[
|
||||
socket.onEvent('open', handleOpen),
|
||||
socket.onEvent('close', handleClose),
|
||||
socket.onEvent('error', handleError),
|
||||
socket.on(RSSIData, data => telemetry.setRSSI(data)),
|
||||
socket.on(ModeData, data => mode.set(data)),
|
||||
socket.on(AnalyticsData, data => {
|
||||
analytics.addData(data)
|
||||
}),
|
||||
socket.on(AnglesData, data => {
|
||||
servoAngles.set(data)
|
||||
})
|
||||
]
|
||||
)
|
||||
features.subscribe(data => {
|
||||
if (data?.download_firmware) eventListeners.push( socket.on(DownloadOTAData, (data) => telemetry.setDownloadOTA(data)) )
|
||||
if (data?.sonar) eventListeners.push( socket.on(SonarData, (data) => console.log(data)) )
|
||||
if (data?.download_firmware)
|
||||
eventListeners.push(
|
||||
socket.on(DownloadOTAData, data => telemetry.setDownloadOTA(data))
|
||||
)
|
||||
if (data?.sonar) eventListeners.push(socket.on(SonarData, data => console.log(data)))
|
||||
})
|
||||
}
|
||||
|
||||
const removeEventListeners = () => {
|
||||
for (let offFunction of eventListeners) {
|
||||
offFunction();
|
||||
offFunction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +102,7 @@
|
||||
|
||||
const handleClose = () => {
|
||||
notifications.error('Connection to device lost', 5000)
|
||||
telemetry.setRSSI( RSSIData.create({rssi: 0}) )
|
||||
telemetry.setRSSI(RSSIData.create({ rssi: 0 }))
|
||||
}
|
||||
|
||||
const handleError = (data: unknown) => console.error(data)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
import { gamepadAxes, gamepadButtonsEdges, hasGamepad } from '$lib/stores/gamepad'
|
||||
import { notifications } from '$lib/components/toasts/notifications'
|
||||
import {
|
||||
HumanInputData,
|
||||
ModeData,
|
||||
ModesEnum,
|
||||
WalkGaitData,
|
||||
@@ -25,14 +24,6 @@
|
||||
let left: nipplejs.JoystickManager
|
||||
let right: nipplejs.JoystickManager
|
||||
|
||||
let data: HumanInputData = HumanInputData.create({
|
||||
left: { x: 0, y: 0 },
|
||||
right: { x: 0, y: 0 },
|
||||
height: 0,
|
||||
s1: 0,
|
||||
speed: 0
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if ($hasGamepad) {
|
||||
notifications.success('🎮 Gamepad connected', 3000)
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
import { socket } from '$lib/stores'
|
||||
import { Connection } from '$lib/components/icons'
|
||||
import I2CSetting from './i2cSetting.svelte'
|
||||
import { I2CDevice, I2CScanData, I2CScanDataRequest } from '$lib/platform_shared/websocket_message'
|
||||
import {
|
||||
I2CDevice,
|
||||
I2CScanData,
|
||||
I2CScanDataRequest
|
||||
} from '$lib/platform_shared/websocket_message'
|
||||
|
||||
// TODO: Delete this completely, this should be done on esp side, as it decides what addresses are actually valid, as for example ICM20948 and MPU6050 can have same address
|
||||
// const i2cDevices = [
|
||||
@@ -66,7 +70,7 @@
|
||||
<div>No I2C devices found</div>
|
||||
{:else}
|
||||
{#each active_devices as device (device.address)}
|
||||
<div>[{device.address.toString(16)}] {device.part_number} - {device.name}</div>
|
||||
<div>[{device.address.toString(16)}] {device.partNumber} - {device.name}</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
import { onMount } from 'svelte'
|
||||
import { modals } from 'svelte-modals'
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
|
||||
import { PeripheralSettingsData, PeripheralSettingsDataRequest } from '$lib/platform_shared/websocket_message'
|
||||
import {
|
||||
PeripheralSettingsData,
|
||||
PeripheralSettingsDataRequest
|
||||
} from '$lib/platform_shared/websocket_message'
|
||||
|
||||
let settings: PeripheralSettingsData | null = $state(null)
|
||||
let isEditing = $state(false)
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
import { useFeatureFlags } from '$lib/stores/featureFlags'
|
||||
import { Rotate3d } from '$lib/components/icons'
|
||||
|
||||
import { IMUReport } from '$lib/platform_shared/imu_report';
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
import { IMUCalibrateData, IMUCalibrateExecute, IMUData } from '$lib/platform_shared/websocket_message'
|
||||
import {
|
||||
IMUCalibrateData,
|
||||
IMUCalibrateExecute,
|
||||
IMUData
|
||||
} from '$lib/platform_shared/websocket_message'
|
||||
|
||||
Chart.register(...registerables)
|
||||
|
||||
@@ -189,9 +191,9 @@
|
||||
const updateData = () => {
|
||||
if ($features.imu) {
|
||||
const x = $imu.map(datapoint => datapoint.x)
|
||||
const y= $imu.map(datapoint => datapoint.y)
|
||||
const y = $imu.map(datapoint => datapoint.y)
|
||||
const z = $imu.map(datapoint => datapoint.z)
|
||||
|
||||
|
||||
angleChart.data.labels = Array.from({ length: $imu.length }, (_, i) => i + 1)
|
||||
angleChart.data.datasets[0].data = x
|
||||
angleChart.data.datasets[1].data = y
|
||||
@@ -204,23 +206,31 @@
|
||||
}
|
||||
|
||||
if ($features.bmp) {
|
||||
updateChartData(tempChart, $imu.map(datapoint => datapoint.bmpTemp))
|
||||
updateChartData(altitudeChart, $imu.map(datapoint => datapoint.altitude))
|
||||
updateChartData(
|
||||
tempChart,
|
||||
$imu.map(datapoint => datapoint.bmpTemp)
|
||||
)
|
||||
updateChartData(
|
||||
altitudeChart,
|
||||
$imu.map(datapoint => datapoint.altitude)
|
||||
)
|
||||
}
|
||||
}
|
||||
const eventListeners: (() => void)[] = [];
|
||||
const eventListeners: (() => void)[] = []
|
||||
onMount(() => {
|
||||
eventListeners.push(...[
|
||||
socket.on(IMUData, (data) => {
|
||||
console.log(data)
|
||||
imu.addData(data)
|
||||
}),
|
||||
eventListeners.push(
|
||||
...[
|
||||
socket.on(IMUData, data => {
|
||||
console.log(data)
|
||||
imu.addData(data)
|
||||
}),
|
||||
|
||||
socket.on(IMUCalibrateData, (data) => {
|
||||
isCalibrating = false
|
||||
calibrationResult = data
|
||||
})
|
||||
])
|
||||
socket.on(IMUCalibrateData, data => {
|
||||
isCalibrating = false
|
||||
calibrationResult = data
|
||||
})
|
||||
]
|
||||
)
|
||||
|
||||
initializeCharts()
|
||||
intervalId = setInterval(updateData, 200)
|
||||
@@ -228,7 +238,7 @@
|
||||
|
||||
onDestroy(() => {
|
||||
for (let offFunction of eventListeners) {
|
||||
offFunction();
|
||||
offFunction()
|
||||
}
|
||||
clearInterval(intervalId)
|
||||
})
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
const throttler = new Throttler()
|
||||
|
||||
const activateServo = () => {
|
||||
socket.sendEvent(ServoStateData, ServoStateData.create({active: true}))
|
||||
socket.sendEvent(ServoStateData, ServoStateData.create({ active: true }))
|
||||
}
|
||||
|
||||
const deactivateServo = () => {
|
||||
socket.sendEvent(ServoStateData, ServoStateData.create({active: false}))
|
||||
socket.sendEvent(ServoStateData, ServoStateData.create({ active: false }))
|
||||
}
|
||||
|
||||
const updatePWM = () => {
|
||||
throttler.throttle(() => {
|
||||
socket.sendEvent(ServoPWMData, ServoPWMData.create({ servoId: servoId, servoPwm: pwm }))
|
||||
socket.sendEvent(ServoPWMData, ServoPWMData.create({ servoId: servoId, servoPwm: pwm }))
|
||||
}, 10)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { socket } from '$lib/stores'
|
||||
|
||||
|
||||
//import { IMUReport, IMUType } from '$lib/platform_shared/example';
|
||||
//import { IMUReport, IMUType } from '$lib/platform_shared/example';
|
||||
import { AnglesData, WebsocketMessage, IMUData } from '$lib/platform_shared/websocket_message'
|
||||
|
||||
// const imu_report: IMUReport = {type: IMUType.IMU_ACCEL, xVal: 4}
|
||||
// const writer = IMUReport.encode(imu_report);
|
||||
// const imu_report: IMUReport = {type: IMUType.IMU_ACCEL, xVal: 4}
|
||||
// const writer = IMUReport.encode(imu_report);
|
||||
// const bytes = writer.finish();
|
||||
// // Convert bytes to hex
|
||||
// const hex = Array.from(bytes)
|
||||
// .map((b) => b.toString(16).padStart(2, '0'))
|
||||
// .join(' ');
|
||||
// // Convert bytes to hex
|
||||
// const hex = Array.from(bytes)
|
||||
// .map((b) => b.toString(16).padStart(2, '0'))
|
||||
// .join(' ');
|
||||
|
||||
// const wmd: WebsocketMessage = { imu: {temp: 0, x: 0, y: 0, z: 1}, angles: {angles: [2]}}
|
||||
// const wmd: WebsocketMessage = { imu: {temp: 0, x: 0, y: 0, z: 0} }
|
||||
const wmd: WebsocketMessage = { rssi: {rssi: 16} }
|
||||
const wmd: WebsocketMessage = { rssi: { rssi: 16 } }
|
||||
// const wmd: WebsocketMessage = { imu: {temp: 1, x: 2, y: 4, z: 5} }
|
||||
// const wmd: WebsocketMessage = { angles: {angles: [1,2,3,4]} }
|
||||
const writer = WebsocketMessage.encode(wmd);
|
||||
const bytes = writer.finish();
|
||||
// Convert bytes to hex
|
||||
const hex = Array.from(bytes)
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join(' ');
|
||||
const writer = WebsocketMessage.encode(wmd)
|
||||
const bytes = writer.finish()
|
||||
// Convert bytes to hex
|
||||
const hex = Array.from(bytes)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join(' ')
|
||||
|
||||
// const decodedmsg: WebsocketMessage = WebsocketMessage.decode(bytes);
|
||||
// const objects = Object.entries(decodedmsg)
|
||||
@@ -46,17 +44,13 @@
|
||||
// }
|
||||
|
||||
const handleData = (data: IMUData) => {
|
||||
|
||||
console.log(data);
|
||||
console.log(data)
|
||||
}
|
||||
onMount(() => {
|
||||
return socket.on(IMUData, handleData)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<h1>Hexadecimal Output</h1>
|
||||
|
||||
<p><strong>Hex output:</strong> {hex}</p>
|
||||
|
||||
@@ -51,9 +51,11 @@
|
||||
|
||||
const postSleep = async () => await api.post('api/sleep')
|
||||
|
||||
let unsub: (() => void) | undefined = undefined;
|
||||
onMount(() => unsub = socket.on(AnalyticsData, handleSystemData))
|
||||
onDestroy(() => { if (unsub) unsub() })
|
||||
let unsub: (() => void) | undefined = undefined
|
||||
onMount(() => (unsub = socket.on(AnalyticsData, handleSystemData)))
|
||||
onDestroy(() => {
|
||||
if (unsub) unsub()
|
||||
})
|
||||
|
||||
const handleSystemData = (data: AnalyticsData) => {
|
||||
if (systemInformation) {
|
||||
@@ -179,7 +181,9 @@
|
||||
icon={Speed}
|
||||
title="CPU Frequency"
|
||||
description={`${systemInformation.staticSystemInformation?.cpuFreqMhz} MHz ${
|
||||
systemInformation.staticSystemInformation?.cpuCores == 2 ? 'Dual Core' : 'Single Core'
|
||||
systemInformation.staticSystemInformation?.cpuCores == 2 ?
|
||||
'Dual Core'
|
||||
: 'Single Core'
|
||||
}`}
|
||||
/>
|
||||
|
||||
@@ -199,11 +203,14 @@
|
||||
icon={Sketch}
|
||||
title="Sketch (Used / Free)"
|
||||
description={`${(
|
||||
(systemInformation.staticSystemInformation!.sketchSize / systemInformation.staticSystemInformation!.freeSketchSpace) *
|
||||
(systemInformation.staticSystemInformation!.sketchSize /
|
||||
systemInformation.staticSystemInformation!.freeSketchSpace) *
|
||||
100
|
||||
).toFixed(1)} % of
|
||||
${systemInformation.staticSystemInformation!.freeSketchSpace / 1000000} MB used (${
|
||||
(systemInformation.staticSystemInformation!.freeSketchSpace - systemInformation.staticSystemInformation!.sketchSize) / 1000000
|
||||
(systemInformation.staticSystemInformation!.freeSketchSpace -
|
||||
systemInformation.staticSystemInformation!.sketchSize) /
|
||||
1000000
|
||||
} MB free)`}
|
||||
/>
|
||||
|
||||
@@ -219,10 +226,15 @@
|
||||
icon={Folder}
|
||||
title="File System (Used / Total)"
|
||||
description={`${(
|
||||
(systemInformation.analyticsData!.fsUsed / systemInformation.analyticsData!.fsTotal) *
|
||||
(systemInformation.analyticsData!.fsUsed /
|
||||
systemInformation.analyticsData!.fsTotal) *
|
||||
100
|
||||
).toFixed(1)} % of ${systemInformation.analyticsData!.fsTotal / 1000000} MB used (${
|
||||
(systemInformation.analyticsData!.fsTotal - systemInformation.analyticsData!.fsUsed) / 1000000
|
||||
).toFixed(
|
||||
1
|
||||
)} % of ${systemInformation.analyticsData!.fsTotal / 1000000} MB used (${
|
||||
(systemInformation.analyticsData!.fsTotal -
|
||||
systemInformation.analyticsData!.fsUsed) /
|
||||
1000000
|
||||
}
|
||||
MB free)`}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
import { KnownNetworkItem } from '$lib/platform_shared/websocket_message'
|
||||
import { WifiSettings, type WifiStatus } from '$lib/platform_shared/rest_message'
|
||||
|
||||
let networkEditable: KnownNetworkItem = $state( KnownNetworkItem.create() )
|
||||
let networkEditable: KnownNetworkItem = $state(KnownNetworkItem.create())
|
||||
|
||||
let static_ip_config = $state(false)
|
||||
|
||||
@@ -84,15 +84,17 @@
|
||||
return wifiSettings
|
||||
}
|
||||
|
||||
let unsub_obj: (() => void) | undefined = undefined;
|
||||
let unsub_obj: (() => void) | undefined = undefined
|
||||
onMount(() => {
|
||||
unsub_obj = socket.on<WifiSettings>(WifiSettings, data => {
|
||||
wifiSettings = data
|
||||
dndNetworkList = wifiSettings.wifiNetworks
|
||||
})
|
||||
})
|
||||
|
||||
onDestroy(() => { if (unsub_obj) unsub_obj() } )
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsub_obj) unsub_obj()
|
||||
})
|
||||
async function postWiFiSettings(data: WifiSettings) {
|
||||
const result = await api.post<WifiSettings>('/api/wifi/sta/settings', data)
|
||||
if (result.isErr()) {
|
||||
|
||||
Reference in New Issue
Block a user