Start of svelte socket rewrite

This commit is contained in:
Niklas Jensen
2025-12-30 01:15:03 +01:00
committed by nikguin04
parent 5459d0edd4
commit 41d1b8e56d
7 changed files with 1769 additions and 31 deletions
File diff suppressed because it is too large Load Diff
+29 -18
View File
@@ -1,30 +1,38 @@
import { writable } from 'svelte/store'
import { encode, decode } from '@msgpack/msgpack'
import { WebsocketMessage } from '$lib/platform_shared/websocket_message'
const socketEvents = ['open', 'close', 'error', 'message', 'unresponsive'] as const
type SocketEvent = (typeof socketEvents)[number]
type SocketMessage = [string, ArrayBuffer]
type TaggedSocketMessage = [string, WebsocketMessage]
let useBinary = false
const decodeMessage = (data: ArrayBuffer): SocketMessage | null => {
const decodeMessage = (data: ArrayBuffer): TaggedSocketMessage => {
try {
const view = new Uint8Array(data);
let comma_index: number = 0;
let tag: string = "";
for (comma_index = 0; view[comma_index] != 0x2c; comma_index++) { // 0x2c is the ascii code for a comma!
tag += String.fromCharCode(view[comma_index]);
if (comma_index >= data.byteLength) { throw new RangeError("Comma index exceeded")}
}
return [ tag, data.slice(comma_index+1) ]
} catch (error) {
console.error(`Could not decode data: ${new Uint8Array(data as ArrayBuffer)} - ${error}`)
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")
}
const [tag, value] = values[0]
return [tag, decoded]
// try {
// const view = new Uint8Array(data);
// let comma_index: number = 0;
// let tag: string = "";
// for (comma_index = 0; view[comma_index] != 0x2c; comma_index++) { // 0x2c is the ascii code for a comma!
// tag += String.fromCharCode(view[comma_index]);
// if (comma_index >= data.byteLength) { throw new RangeError("Comma index exceeded")}
// }
// return [ tag, data.slice(comma_index+1) ]
// } catch (error) {
// console.error(`Could not decode data: ${new Uint8Array(data as ArrayBuffer)} - ${error}`)
// }
return null
}
@@ -76,10 +84,8 @@ function createWebSocket() {
}
ws.onmessage = frame => {
resetUnresponsiveCheck()
const message = decodeMessage(frame.data)
if (!message) return
const [event, payload] = message
if (event) listeners.get(event)?.forEach(listener => listener(payload))
const [tag, message] = decodeMessage(frame.data)
if (tag) listeners.get(tag)?.forEach(listener => listener(message))
}
ws.onerror = ev => disconnect('error', ev)
ws.onclose = ev => disconnect('close', ev)
@@ -143,6 +149,11 @@ function createWebSocket() {
sendEvent,
init,
on: <T>(event: string, listener: (data: T) => void): (() => void) => {
if (event !in Object.keys(WebsocketMessage.create())) {
throw new Error("Event not found in 'WebsocketMessage' type, message can never be decoded, and the listener has not been added");
}
let eventListeners = listeners.get(event)
if (!eventListeners) {
if (!socketEvents.includes(event as SocketEvent)) {
+10
View File
@@ -5,6 +5,16 @@ Make sure to actually create the output directories before executing the command
TS:
protoc --plugin="protoc-gen-ts_proto=$(Resolve-Path app\node_modules\.bin\protoc-gen-ts_proto.CMD)" --ts_proto_out="./app/src/lib" "platform_shared\example.proto"
NEW TS (USING PROTOBUFJS):
cd app
pnpm install protobufjs-cli
pnpm install -D protobufjs-cli
pnpm exec pbjs -t static-module -w es6 -o src\lib\platform_shared\compiled.js ..\platform_shared\websocket_message.proto
pnpm exec pbts -o src\lib\platform_shared\compiled.d.ts src\lib\platform_shared\compiled.js
pnpm exec pbjs -t json-module -o src\lib\platform_shared\compiled.d.json ..\platform_shared\websocket_message.proto
C++:
protoc -I=platform_shared --cpp_out=esp32/src/platform_shared platform_shared/example.proto
@@ -0,0 +1,60 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-1.0.0-dev */
#include "platform_shared/websocket_message.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(IMUData, IMUData, AUTO)
PB_BIND(IMUCalibrateData, IMUCalibrateData, AUTO)
PB_BIND(ModeData, ModeData, AUTO)
PB_BIND(InputData, InputData, AUTO)
PB_BIND(AnalyticsData, AnalyticsData, AUTO)
PB_BIND(PositionData, PositionData, AUTO)
PB_BIND(AnglesData, AnglesData, AUTO)
PB_BIND(I2CScanData, I2CScanData, AUTO)
PB_BIND(PeripheralSettingsData, PeripheralSettingsData, AUTO)
PB_BIND(OTAStatusData, OTAStatusData, AUTO)
PB_BIND(GaitData, GaitData, AUTO)
PB_BIND(ServoStateData, ServoStateData, AUTO)
PB_BIND(ServoPWMData, ServoPWMData, AUTO)
PB_BIND(WifiSettingsData, WifiSettingsData, AUTO)
PB_BIND(SonarData, SonarData, AUTO)
PB_BIND(RSSIData, RSSIData, AUTO)
PB_BIND(WebsocketMessage, WebsocketMessage, 2)
@@ -0,0 +1,352 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-1.0.0-dev */
#ifndef PB_PLATFORM_SHARED_WEBSOCKET_MESSAGE_PB_H_INCLUDED
#define PB_PLATFORM_SHARED_WEBSOCKET_MESSAGE_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Struct definitions */
/* Individual message data types */
typedef struct _IMUData {
float x;
float y;
float z;
float temp;
} IMUData;
typedef struct _IMUCalibrateData {
char dummy_field;
} IMUCalibrateData;
typedef struct _ModeData {
int32_t mode;
} ModeData;
typedef struct _InputData {
char dummy_field;
} InputData;
typedef struct _AnalyticsData {
char dummy_field;
} AnalyticsData;
typedef struct _PositionData {
char dummy_field;
} PositionData;
typedef struct _AnglesData {
pb_callback_t angles;
} AnglesData;
typedef struct _I2CScanData {
char dummy_field;
} I2CScanData;
typedef struct _PeripheralSettingsData {
char dummy_field;
} PeripheralSettingsData;
typedef struct _OTAStatusData {
char dummy_field;
} OTAStatusData;
typedef struct _GaitData {
char dummy_field;
} GaitData;
typedef struct _ServoStateData {
char dummy_field;
} ServoStateData;
typedef struct _ServoPWMData {
char dummy_field;
} ServoPWMData;
typedef struct _WifiSettingsData {
char dummy_field;
} WifiSettingsData;
typedef struct _SonarData {
char dummy_field;
} SonarData;
typedef struct _RSSIData {
int32_t rssi;
} RSSIData;
/* WebSocket message wrapper
Only ONE field will be set at a time (oneof ensures this) */
typedef struct _WebsocketMessage {
pb_size_t which_message;
union _WebsocketMessage_message {
IMUData imu;
IMUCalibrateData imu_calibrate;
ModeData mode;
InputData input;
AnalyticsData analytics;
PositionData position;
AnglesData angles;
I2CScanData i2c_scan;
PeripheralSettingsData peripheral_settings;
OTAStatusData ota_status;
GaitData gait;
ServoStateData servo_state;
ServoPWMData servo_pwm;
WifiSettingsData wifi_settings;
SonarData sonar;
RSSIData rssi;
} message;
} WebsocketMessage;
#ifdef __cplusplus
extern "C" {
#endif
/* Initializer values for message structs */
#define IMUData_init_default {0, 0, 0, 0}
#define IMUCalibrateData_init_default {0}
#define ModeData_init_default {0}
#define InputData_init_default {0}
#define AnalyticsData_init_default {0}
#define PositionData_init_default {0}
#define AnglesData_init_default {{{NULL}, NULL}}
#define I2CScanData_init_default {0}
#define PeripheralSettingsData_init_default {0}
#define OTAStatusData_init_default {0}
#define GaitData_init_default {0}
#define ServoStateData_init_default {0}
#define ServoPWMData_init_default {0}
#define WifiSettingsData_init_default {0}
#define SonarData_init_default {0}
#define RSSIData_init_default {0}
#define WebsocketMessage_init_default {0, {IMUData_init_default}}
#define IMUData_init_zero {0, 0, 0, 0}
#define IMUCalibrateData_init_zero {0}
#define ModeData_init_zero {0}
#define InputData_init_zero {0}
#define AnalyticsData_init_zero {0}
#define PositionData_init_zero {0}
#define AnglesData_init_zero {{{NULL}, NULL}}
#define I2CScanData_init_zero {0}
#define PeripheralSettingsData_init_zero {0}
#define OTAStatusData_init_zero {0}
#define GaitData_init_zero {0}
#define ServoStateData_init_zero {0}
#define ServoPWMData_init_zero {0}
#define WifiSettingsData_init_zero {0}
#define SonarData_init_zero {0}
#define RSSIData_init_zero {0}
#define WebsocketMessage_init_zero {0, {IMUData_init_zero}}
/* Field tags (for use in manual encoding/decoding) */
#define IMUData_x_tag 1
#define IMUData_y_tag 2
#define IMUData_z_tag 3
#define IMUData_temp_tag 4
#define ModeData_mode_tag 1
#define AnglesData_angles_tag 1
#define RSSIData_rssi_tag 1
#define WebsocketMessage_imu_tag 10
#define WebsocketMessage_imu_calibrate_tag 20
#define WebsocketMessage_mode_tag 30
#define WebsocketMessage_input_tag 40
#define WebsocketMessage_analytics_tag 50
#define WebsocketMessage_position_tag 60
#define WebsocketMessage_angles_tag 70
#define WebsocketMessage_i2c_scan_tag 80
#define WebsocketMessage_peripheral_settings_tag 90
#define WebsocketMessage_ota_status_tag 100
#define WebsocketMessage_gait_tag 110
#define WebsocketMessage_servo_state_tag 120
#define WebsocketMessage_servo_pwm_tag 130
#define WebsocketMessage_wifi_settings_tag 140
#define WebsocketMessage_sonar_tag 150
#define WebsocketMessage_rssi_tag 160
/* Struct field encoding specification for nanopb */
#define IMUData_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FLOAT, x, 1) \
X(a, STATIC, SINGULAR, FLOAT, y, 2) \
X(a, STATIC, SINGULAR, FLOAT, z, 3) \
X(a, STATIC, SINGULAR, FLOAT, temp, 4)
#define IMUData_CALLBACK NULL
#define IMUData_DEFAULT NULL
#define IMUCalibrateData_FIELDLIST(X, a) \
#define IMUCalibrateData_CALLBACK NULL
#define IMUCalibrateData_DEFAULT NULL
#define ModeData_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, INT32, mode, 1)
#define ModeData_CALLBACK NULL
#define ModeData_DEFAULT NULL
#define InputData_FIELDLIST(X, a) \
#define InputData_CALLBACK NULL
#define InputData_DEFAULT NULL
#define AnalyticsData_FIELDLIST(X, a) \
#define AnalyticsData_CALLBACK NULL
#define AnalyticsData_DEFAULT NULL
#define PositionData_FIELDLIST(X, a) \
#define PositionData_CALLBACK NULL
#define PositionData_DEFAULT NULL
#define AnglesData_FIELDLIST(X, a) \
X(a, CALLBACK, REPEATED, FLOAT, angles, 1)
#define AnglesData_CALLBACK pb_default_field_callback
#define AnglesData_DEFAULT NULL
#define I2CScanData_FIELDLIST(X, a) \
#define I2CScanData_CALLBACK NULL
#define I2CScanData_DEFAULT NULL
#define PeripheralSettingsData_FIELDLIST(X, a) \
#define PeripheralSettingsData_CALLBACK NULL
#define PeripheralSettingsData_DEFAULT NULL
#define OTAStatusData_FIELDLIST(X, a) \
#define OTAStatusData_CALLBACK NULL
#define OTAStatusData_DEFAULT NULL
#define GaitData_FIELDLIST(X, a) \
#define GaitData_CALLBACK NULL
#define GaitData_DEFAULT NULL
#define ServoStateData_FIELDLIST(X, a) \
#define ServoStateData_CALLBACK NULL
#define ServoStateData_DEFAULT NULL
#define ServoPWMData_FIELDLIST(X, a) \
#define ServoPWMData_CALLBACK NULL
#define ServoPWMData_DEFAULT NULL
#define WifiSettingsData_FIELDLIST(X, a) \
#define WifiSettingsData_CALLBACK NULL
#define WifiSettingsData_DEFAULT NULL
#define SonarData_FIELDLIST(X, a) \
#define SonarData_CALLBACK NULL
#define SonarData_DEFAULT NULL
#define RSSIData_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, INT32, rssi, 1)
#define RSSIData_CALLBACK NULL
#define RSSIData_DEFAULT NULL
#define WebsocketMessage_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (message,imu,message.imu), 10) \
X(a, STATIC, ONEOF, MESSAGE, (message,imu_calibrate,message.imu_calibrate), 20) \
X(a, STATIC, ONEOF, MESSAGE, (message,mode,message.mode), 30) \
X(a, STATIC, ONEOF, MESSAGE, (message,input,message.input), 40) \
X(a, STATIC, ONEOF, MESSAGE, (message,analytics,message.analytics), 50) \
X(a, STATIC, ONEOF, MESSAGE, (message,position,message.position), 60) \
X(a, STATIC, ONEOF, MESSAGE, (message,angles,message.angles), 70) \
X(a, STATIC, ONEOF, MESSAGE, (message,i2c_scan,message.i2c_scan), 80) \
X(a, STATIC, ONEOF, MESSAGE, (message,peripheral_settings,message.peripheral_settings), 90) \
X(a, STATIC, ONEOF, MESSAGE, (message,ota_status,message.ota_status), 100) \
X(a, STATIC, ONEOF, MESSAGE, (message,gait,message.gait), 110) \
X(a, STATIC, ONEOF, MESSAGE, (message,servo_state,message.servo_state), 120) \
X(a, STATIC, ONEOF, MESSAGE, (message,servo_pwm,message.servo_pwm), 130) \
X(a, STATIC, ONEOF, MESSAGE, (message,wifi_settings,message.wifi_settings), 140) \
X(a, STATIC, ONEOF, MESSAGE, (message,sonar,message.sonar), 150) \
X(a, STATIC, ONEOF, MESSAGE, (message,rssi,message.rssi), 160)
#define WebsocketMessage_CALLBACK NULL
#define WebsocketMessage_DEFAULT NULL
#define WebsocketMessage_message_imu_MSGTYPE IMUData
#define WebsocketMessage_message_imu_calibrate_MSGTYPE IMUCalibrateData
#define WebsocketMessage_message_mode_MSGTYPE ModeData
#define WebsocketMessage_message_input_MSGTYPE InputData
#define WebsocketMessage_message_analytics_MSGTYPE AnalyticsData
#define WebsocketMessage_message_position_MSGTYPE PositionData
#define WebsocketMessage_message_angles_MSGTYPE AnglesData
#define WebsocketMessage_message_i2c_scan_MSGTYPE I2CScanData
#define WebsocketMessage_message_peripheral_settings_MSGTYPE PeripheralSettingsData
#define WebsocketMessage_message_ota_status_MSGTYPE OTAStatusData
#define WebsocketMessage_message_gait_MSGTYPE GaitData
#define WebsocketMessage_message_servo_state_MSGTYPE ServoStateData
#define WebsocketMessage_message_servo_pwm_MSGTYPE ServoPWMData
#define WebsocketMessage_message_wifi_settings_MSGTYPE WifiSettingsData
#define WebsocketMessage_message_sonar_MSGTYPE SonarData
#define WebsocketMessage_message_rssi_MSGTYPE RSSIData
extern const pb_msgdesc_t IMUData_msg;
extern const pb_msgdesc_t IMUCalibrateData_msg;
extern const pb_msgdesc_t ModeData_msg;
extern const pb_msgdesc_t InputData_msg;
extern const pb_msgdesc_t AnalyticsData_msg;
extern const pb_msgdesc_t PositionData_msg;
extern const pb_msgdesc_t AnglesData_msg;
extern const pb_msgdesc_t I2CScanData_msg;
extern const pb_msgdesc_t PeripheralSettingsData_msg;
extern const pb_msgdesc_t OTAStatusData_msg;
extern const pb_msgdesc_t GaitData_msg;
extern const pb_msgdesc_t ServoStateData_msg;
extern const pb_msgdesc_t ServoPWMData_msg;
extern const pb_msgdesc_t WifiSettingsData_msg;
extern const pb_msgdesc_t SonarData_msg;
extern const pb_msgdesc_t RSSIData_msg;
extern const pb_msgdesc_t WebsocketMessage_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define IMUData_fields &IMUData_msg
#define IMUCalibrateData_fields &IMUCalibrateData_msg
#define ModeData_fields &ModeData_msg
#define InputData_fields &InputData_msg
#define AnalyticsData_fields &AnalyticsData_msg
#define PositionData_fields &PositionData_msg
#define AnglesData_fields &AnglesData_msg
#define I2CScanData_fields &I2CScanData_msg
#define PeripheralSettingsData_fields &PeripheralSettingsData_msg
#define OTAStatusData_fields &OTAStatusData_msg
#define GaitData_fields &GaitData_msg
#define ServoStateData_fields &ServoStateData_msg
#define ServoPWMData_fields &ServoPWMData_msg
#define WifiSettingsData_fields &WifiSettingsData_msg
#define SonarData_fields &SonarData_msg
#define RSSIData_fields &RSSIData_msg
#define WebsocketMessage_fields &WebsocketMessage_msg
/* Maximum encoded size of messages (where known) */
/* AnglesData_size depends on runtime parameters */
/* WebsocketMessage_size depends on runtime parameters */
#define AnalyticsData_size 0
#define GaitData_size 0
#define I2CScanData_size 0
#define IMUCalibrateData_size 0
#define IMUData_size 20
#define InputData_size 0
#define ModeData_size 11
#define OTAStatusData_size 0
#define PLATFORM_SHARED_WEBSOCKET_MESSAGE_PB_H_MAX_SIZE IMUData_size
#define PeripheralSettingsData_size 0
#define PositionData_size 0
#define RSSIData_size 11
#define ServoPWMData_size 0
#define ServoStateData_size 0
#define SonarData_size 0
#define WifiSettingsData_size 0
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
-13
View File
@@ -1,13 +0,0 @@
syntax = "proto3";
enum IMUType {
IMU_NONE = 0;
IMU_ACCEL = 1;
IMU_GYRO = 2;
}
message IMUReport {
IMUType type = 1;
double x_val = 2;
}
+49
View File
@@ -0,0 +1,49 @@
syntax = "proto3";
// Individual message data types
message IMUData {
float x = 1;
float y = 2;
float z = 3;
float temp = 4;
}
message IMUCalibrateData {}
message ModeData { int32 mode = 1; }
message InputData {}
message AnalyticsData {}
message PositionData {}
message AnglesData { repeated float angles = 1; }
message I2CScanData {}
message PeripheralSettingsData {}
message OTAStatusData {}
message GaitData {}
message ServoStateData {}
message ServoPWMData {}
message WifiSettingsData {}
message SonarData {}
message RSSIData { int32 rssi = 1; }
// WebSocket message wrapper
// Only ONE field will be set at a time (oneof ensures this)
message WebsocketMessage {
oneof message {
IMUData imu = 10;
IMUCalibrateData imu_calibrate = 20;
ModeData mode = 30;
InputData input = 40;
AnalyticsData analytics = 50;
PositionData position = 60;
AnglesData angles = 70;
I2CScanData i2c_scan = 80;
PeripheralSettingsData peripheral_settings = 90;
OTAStatusData ota_status = 100;
GaitData gait = 110;
ServoStateData servo_state = 120;
ServoPWMData servo_pwm = 130;
WifiSettingsData wifi_settings = 140;
SonarData sonar = 150;
RSSIData rssi = 160;
}
}