Skip to content

Commit

Permalink
Add websocket support
Browse files Browse the repository at this point in the history
  • Loading branch information
haslinghuis committed Sep 24, 2024
1 parent 2631c90 commit a885768
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 17 deletions.
39 changes: 24 additions & 15 deletions src/components/port-picker/PortOverrideOption.vue
Original file line number Diff line number Diff line change
@@ -1,39 +1,48 @@
<template>
<div
v-if="isManual"
id="port-override-option"
style="display: none"
:style="{ display: isManual ? 'flex' : 'none' }"
>
<label
for="port-override"
><span>{{ $t("portOverrideText") }}</span>
<input
id="port-override"
type="text"
value="/dev/rfcomm0"
:value="value"
@change="inputValueChanged($event)"
></label>
</div>
</template>

<script>
import { set as setConfig } from '../../js/ConfigStorage';
import { set as setConfig } from "../../js/ConfigStorage";
export default {
props: {
isManual: {
type: Boolean,
default: true,
},
props: {
value: {
type: String,
default: '/dev/rfcomm0',
},
isManual: {
type: Boolean,
default: true,
},
},
methods: {
inputValueChanged(event) {
setConfig({'portOverride': event.target.value});
this.$emit('input', event.target.value);
},
},
};
</script>

<style lang="less" scoped>
#port-override-option {
label {
display: flex;
align-items: center;
gap: 0.5rem;
}
label {
display: flex;
align-items: center;
gap: 0.5rem;
}
}
</style>
1 change: 1 addition & 0 deletions src/js/data_storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const CONFIGURATOR = {

connectionValid: false,
connectionValidCliOnly: false,
manualMode: false,
virtualMode: false,
virtualApiVersion: '0.0.1',
cliActive: false,
Expand Down
145 changes: 145 additions & 0 deletions src/js/protocols/websocket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
class WebsocketSerial extends EventTarget {
constructor() {
super();

this.connected = false;
this.connectionInfo = null;

this.bitrate = 0;
this.bytesSent = 0;
this.bytesReceived = 0;
this.failed = 0;

this.logHead = "[WEBSOCKET] ";

this.address = "ws://localhost:5761";

this.ws = null;

this.connect = this.connect.bind(this);
}

handleReceiveBytes(info) {
this.bytesReceived += info.detail.byteLength;
}

handleDisconnect() {
this.disconnect();
}

createPort(url) {
this.address = url;
return {
path: url,
displayName: `Betaflight SITL`,
vendorId: 0,
productId: 0,
port: 0,
};
}

getConnectedPort() {
return {
path: this.address,
displayName: `Betaflight SITL`,
vendorId: 0,
productId: 0,
port: 0,
};
}

async getDevices() {
return [];
}

async blob2uint(blob) {
const buffer = await new Response(blob).arrayBuffer();
return new Uint8Array(buffer);
}

waitForConnection(socket) {
return new Promise((resolve) => {
const interval = setInterval(() => {
if (socket.connected) {
clearInterval(interval); // Stop checking
resolve(); // Resolve the promise
}
}, 100); // Check every 100ms, adjust as needed
});
}

async connect(path, options) {
this.address = path;
console.log(`${this.logHead} Connecting to ${this.address}`);

this.ws = new WebSocket(this.address, "wsSerial");
let socket = this;

this.ws.onopen = function(e) {
console.log(`${socket.logHead} Connected: `, e);
socket.connected = true;
socket.dispatchEvent(
new CustomEvent("connect", { detail: {
socketId: socket.address,
}}),
);
};

await this.waitForConnection(socket);

this.ws.onclose = function(e) {
console.log(`${socket.logHead} Connection closed: `, e);

socket.disconnect(() => {
socket.dispatchEvent(new CustomEvent("disconnect", this.disconnect.bind(this)));
});
};

this.ws.onerror = function(e) {
console.error(`${socket.logHead} Connection error: `, e);

socket.disconnect(() => {
socket.dispatchEvent(new CustomEvent("disconnect", this.disconnect.bind(this)));
});
};

this.ws.onmessage = async function(msg) {
let uint8Chunk = await socket.blob2uint(msg.data);
socket.dispatchEvent(
new CustomEvent("receive", { detail: uint8Chunk }),
);
};
}

async disconnect() {
this.connected = false;
this.bytesReceived = 0;
this.bytesSent = 0;

if (this.ws) {
try {
this.ws.close();
} catch (e) {
console.error(`${this.logHead}Failed to close socket: ${e}`);
}
}
}

async send(data, cb) {
if (this.ws) {
try {
this.ws.send(data);
this.bytesSent += data.byteLength;
}
catch(e) {
console.error(`${this.logHead}Failed to send data e: ${e}`);
}
}

return {
bytesSent: data.byteLength,
};
}
}

export default new WebsocketSerial();
12 changes: 11 additions & 1 deletion src/js/serial_backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ function connectDisconnect() {
GUI.configuration_loaded = false;

const baudRate = PortHandler.portPicker.selectedBauds;
const selectedPort = portName;

if (!isConnected) {
// prevent connection when we do not have permission
Expand All @@ -124,13 +123,24 @@ function connectDisconnect() {

CONFIGURATOR.virtualMode = selectedPort === 'virtual';
CONFIGURATOR.bluetoothMode = selectedPort.startsWith('bluetooth');
CONFIGURATOR.manualMode = selectedPort === 'manual';

if (CONFIGURATOR.virtualMode) {
CONFIGURATOR.virtualApiVersion = PortHandler.portPicker.virtualMspVersion;

// Hack to get virtual working on the web
serial = serialShim();
serial.connect(onOpenVirtual);
} else if (selectedPort === 'manual') {
serial = serialShim();
// Explicitly disconnect the event listeners before attaching the new ones.
serial.removeEventListener('connect', connectHandler);
serial.addEventListener('connect', connectHandler);

serial.removeEventListener('disconnect', disconnectHandler);
serial.addEventListener('disconnect', disconnectHandler);

serial.connect(portName, { baudRate });
} else {
CONFIGURATOR.virtualMode = false;
serial = serialShim();
Expand Down
14 changes: 13 additions & 1 deletion src/js/serial_shim.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import CONFIGURATOR from "./data_storage";
import serialWeb from "./webSerial.js";
import BT from "./protocols/bluetooth.js";
import websocketSerial from "./protocols/websocket.js";
import virtualSerial from "./virtualSerial.js";

export let serialShim = () => CONFIGURATOR.virtualMode ? virtualSerial: CONFIGURATOR.bluetoothMode ? BT : serialWeb;
export const serialShim = () => {
if (CONFIGURATOR.virtualMode) {
return virtualSerial;
}
if (CONFIGURATOR.manualMode) {
return websocketSerial;
}
if (CONFIGURATOR.bluetoothMode) {
return BT;
}
return serialWeb;
};

0 comments on commit a885768

Please sign in to comment.