Skip to content

Commit

Permalink
trying to accomodate ip override
Browse files Browse the repository at this point in the history
  • Loading branch information
pmalacho-mit committed Apr 9, 2024
1 parent be85fe9 commit 339eb61
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 29 deletions.
49 changes: 44 additions & 5 deletions extensions/src/doodlebot/Connect.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import type Extension from ".";
import { ReactiveInvoke, reactiveInvoke, color } from "$common";
import { onMount } from "svelte";
import Doodlebot from "./Doodlebot";
export let extension: Extension;
Expand All @@ -16,11 +15,15 @@
const storageKeys = {
ssid: "doodlebot-ssid",
password: "doodlebot-password",
ip: "doodlebot-ip",
};
const ipPrefix = "192.168.0.";
let error: string;
let ssid = localStorage.getItem(storageKeys.ssid) ?? "";
let password = localStorage.getItem(storageKeys.password) ?? "";
let ip = "";
const inputs = {
ssid: null as HTMLInputElement,
Expand All @@ -29,7 +32,11 @@
const createConnection = async () => {
try {
const doodlebot = await Doodlebot.tryCreate(ssid, password, bluetooth);
const doodlebot = await Doodlebot.tryCreate(
{ ssid, password, ipOverride: ip ? ipPrefix + ip : undefined },
bluetooth,
);
invoke("setDoodlebot", doodlebot);
localStorage.setItem(storageKeys.ssid, ssid);
localStorage.setItem(storageKeys.password, password);
Expand All @@ -49,7 +56,9 @@
};
const updateNetworkCredentials = () =>
extension.doodlebot.connectToWebsocket({ ssid, password });
extension.doodlebot.connectToWebsocket({ ssid, password, ipOverride: ip });
let showAdvanced = false;
</script>

<div
Expand Down Expand Up @@ -86,6 +95,27 @@
placeholder="e.g. 12345"
/>
</p>
<div>
<button
class="collapser"
on:click={() => (showAdvanced = !showAdvanced)}
>
{showAdvanced ? "" : ""} Advanced
</button>
<div
style:overflow="hidden"
style:max-height={showAdvanced ? "fit-content" : "0"}
>
<p>
IP: {ipPrefix}<input
bind:this={inputs.password}
bind:value={ip}
type="text"
placeholder="e.g. 12345"
/>
</p>
</div>
</div>
</div>
<div>
<h3>2. Select bluetooth device</h3>
Expand All @@ -108,8 +138,9 @@
value={credentials.password}
/>
<button
disabled={credentials.ssid === ssid &&
credentials.password === password}
disabled={(credentials.ssid === ssid &&
credentials.password === password) ||
!ip}
on:click={updateNetworkCredentials}
>
Update</button
Expand Down Expand Up @@ -138,4 +169,12 @@
text-align: center;
border-radius: 5px;
}
.collapser {
background-color: inherit;
cursor: pointer;
border: none;
text-align: left;
outline: none;
}
</style>
51 changes: 29 additions & 22 deletions extensions/src/doodlebot/Doodlebot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type Vector3D = { x: number, y: number, z: number };
export type Color = { red: number, green: number, blue: number, alpha: number };
export type SensorReading = number | Vector3D | Bumper | Color;
export type SensorData = Doodlebot["sensorData"];
export type NetworkCredentials = { ssid: string, password: string };
export type NetworkCredentials = { ssid: string, password: string, ipOverride?: string };
export type NetworkConnection = { ip: string, hostname: string };

type Pending = Record<"motor" | "wifi" | "websocket", Promise<any> | undefined>;
Expand Down Expand Up @@ -95,11 +95,11 @@ export default class Doodlebot {
* @throws
* @returns
*/
static async tryCreate(ssid: string, password: string, ble: Bluetooth, ...filters: BluetoothLEScanFilter[]) {
static async tryCreate({ ssid, password, ipOverride }: NetworkCredentials, ble: Bluetooth, ...filters: BluetoothLEScanFilter[]) {
const robot = await Doodlebot.requestRobot(ble, ...filters);
const services = await Doodlebot.getServices(robot);
if (!services) throw new Error("Unable to connect to doodlebot's UART service");
return new Doodlebot(robot, services, ssid, password);
return new Doodlebot(robot, services, ssid, password, ipOverride);
}

private pending: Pending = { motor: undefined, wifi: undefined, websocket: undefined };
Expand Down Expand Up @@ -139,10 +139,10 @@ export default class Doodlebot {
light: false
};

constructor(private device: BluetoothDevice, private services: Services, private ssid: string, private wifiPassword: string) {
constructor(private device: BluetoothDevice, private services: Services, private ssid: string, private wifiPassword: string, private ip: string | undefined = undefined) {
this.subscribe(services.uartService, "receiveText", this.receiveTextBLE.bind(this));
this.subscribe(device, "gattserverdisconnected", this.handleBleDisconnect.bind(this));
this.connectToWebsocket({ ssid, password: wifiPassword });
this.connectToWebsocket({ ssid, password: wifiPassword, ipOverride: ip });
}

private subscribe<T extends SubscriptionTarget>(target: T, event: Subscription<T>["event"], listener: Subscription<T>["listener"]) {
Expand All @@ -162,11 +162,6 @@ export default class Doodlebot {
});
}

private sendBLECommand(command: Command, ...args: (string | number)[]) {
const { uartService } = this.services;
return uartService.sendText(this.formCommand(command, ...args));
}

private async sendWebsocketCommand(command: Command, ...args: (string | number)[]) {
await this.connectToWebsocket();
this.websocket.send(this.formCommand(command, ...args));
Expand Down Expand Up @@ -359,9 +354,12 @@ export default class Doodlebot {

if (this.connection) return;

await this.untilFinishedPending("wifi", new Promise(async (resolve) => {
await this.untilFinishedPending("wifi", new Promise<void>(async (resolve) => {
await this.sendBLECommand(command.wifi, this.ssid, this.wifiPassword);
this.onNetwork.once(events.connect, resolve)
this.onNetwork.once(events.connect, () => {
console.log("connected to wifi");
resolve();
})
}));
}

Expand All @@ -376,16 +374,14 @@ export default class Doodlebot {
* @param credentials
*/
async connectToWebsocket(credentials?: NetworkCredentials) {
debugger
await this.connectToWifi(credentials);
debugger
const { pending: { websocket: pending } } = this;
debugger
if (pending) await pending;
debugger
if (this.websocket) return;
debugger
this.websocket = new WebSocket(`ws://${this.connection.ip}:${port.websocket}`);
if (!credentials?.ipOverride && !this.ip) {
await this.connectToWifi(credentials);
const { pending: { websocket: pending } } = this;
if (pending) await pending;
if (this.websocket) return;
}
const ip = credentials?.ipOverride ?? this.ip ?? this.connection.ip;
this.websocket = new WebSocket(`ws://${ip}:${port.websocket}`);
await this.untilFinishedPending("websocket", new Promise<void>((resolve) => {
const resolveAndRemove = () => {
this.websocket.removeEventListener("open", resolveAndRemove);
Expand All @@ -404,4 +400,15 @@ export default class Doodlebot {
getNetworkCredentials(): NetworkCredentials {
return { ssid: this.ssid, password: this.wifiPassword };
}

/**
* NOTE: Consider making private
* @param command
* @param args
* @returns
*/
sendBLECommand(command: Command, ...args: (string | number)[]) {
const { uartService } = this.services;
return uartService.sendText(this.formCommand(command, ...args));
}
}
3 changes: 2 additions & 1 deletion extensions/src/doodlebot/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export const command = {
arc: "t",
wifi: "k",
lowPower: "q",
display: "w"
display: "w",
pen: "u",
} as const;

export type CommandKey = keyof typeof command;
Expand Down
33 changes: 32 additions & 1 deletion extensions/src/doodlebot/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Environment, ExtensionMenuDisplayDetails, extension, block, buttonBlock } from "$common";
import { DisplayKey, displayKeys } from "./enums";
import { DisplayKey, displayKeys, command, type Command } from "./enums";
import Doodlebot from "./Doodlebot";

const details: ExtensionMenuDisplayDetails = {
Expand All @@ -10,6 +10,24 @@ const details: ExtensionMenuDisplayDetails = {
tags: ["Made by PRG"]
};

/**
*
* @param input
* @example splitArgsString("hello ahoy"); // Output: ["hello", "ahoy"]
* @example splitArgsString("hello,ahoy"); // Output: ["hello", "ahoy"]
* @example splitArgsString("hello, ahoy"); // Output: ["hello", "ahoy"]
* @example splitArgsString("hello"); // Output: ["hello"]
* @example splitArgsString(""); // Output: []
* @returns
*/
const splitArgsString = (input: string): string[] => {
if (!input) return [];
// Regular expression to split the string by either comma followed by optional space characters or by space characters alone
const regex = /,\s*|\s+/;
const words = input.split(regex);
return words;
}

export default class DoodlebotBlocks extends extension(details, "ui", "indicators") {
doodlebot: Doodlebot;
private indicator: Promise<{ close(): void; }>
Expand Down Expand Up @@ -61,6 +79,19 @@ export default class DoodlebotBlocks extends extension(details, "ui", "indicator

// #region BLE-based commands

@block({
type: "command",
text: (_command, args) => `send (${_command}, ${args}) over BLE`,
args: [{ type: "string", defaultValue: "u" }, { type: "string", defaultValue: "0" }]
})
async sendBLEMessage(_command: string, args: string) {
const candidates = Object.values(command).filter((entry) => entry === _command)

if (candidates.length === 0) return console.error(`Command ${command} not found`);

await this.doodlebot?.sendBLECommand(candidates[0], ...splitArgsString(args));
}

@block({
type: "command",
text: (direction, steps) => `drive ${direction} for ${steps} steps`,
Expand Down

0 comments on commit 339eb61

Please sign in to comment.