fix(gateway): keep in-browser WiFi boards reachable through the IoT gateway
The ESP32 examples now default to the in-browser JS engines, whose lwIP serves the sketch's HTTP server from the simulation tab itself. Opening the IoT gateway in a new tab backgrounds that tab, the browser throttles its timers, and the in-chip server can't answer before the proxy's timeout — the exact failure the Pico W iframe path already solved. - WifiStatus grows an inBrowser flag (set by the overlay's JS bridges); the serial-monitor link and the WiFi badge now open the in-app iframe for any board that carries it, not just pi-pico-w. - /api/gateway polls ~1s before answering 404: the net bridge dials on 'got_ip', which races the sketch printing its URL, so a fast click used to land before the bridge registered.
This commit is contained in:
parent
efabac0a05
commit
8391cc7fa8
|
|
@ -10,6 +10,7 @@ URL pattern:
|
|||
/api/gateway/{client_id}/{path}
|
||||
→ http://127.0.0.1:{hostfwd_port}/{path}
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
|
||||
|
|
@ -66,17 +67,25 @@ async def gateway_proxy(client_id: str, path: str, request: Request) -> Response
|
|||
media_type='application/json',
|
||||
)
|
||||
|
||||
# ── ESP32: the server runs in QEMU, reachable via slirp hostfwd. ──
|
||||
inst = esp_lib_manager.get_instance(client_id)
|
||||
if inst and inst.wifi_enabled and inst.wifi_hostfwd_port != 0:
|
||||
return await _proxy_esp32(inst, path, request)
|
||||
# Registration races the sketch printing its URL: the in-browser engines
|
||||
# dial their net bridge only on 'got_ip', which lands at the same moment
|
||||
# "Server started at: ..." appears in the serial monitor — a fast click
|
||||
# reaches here before the bridge exists. Poll briefly before giving up.
|
||||
for attempt in range(5):
|
||||
if attempt:
|
||||
await asyncio.sleep(0.25)
|
||||
|
||||
# ── Pico W (and any other overlay-provided board): the server runs in the
|
||||
# browser-side lwIP, reachable only by the overlay proxying TCP into the
|
||||
# chip over the WS bridge. OSS has no resolver -> falls through to 404. ──
|
||||
overlay_resp = await dispatch_gateway_proxy(client_id, path, request)
|
||||
if overlay_resp is not None:
|
||||
return overlay_resp
|
||||
# ── ESP32: the server runs in QEMU, reachable via slirp hostfwd. ──
|
||||
inst = esp_lib_manager.get_instance(client_id)
|
||||
if inst and inst.wifi_enabled and inst.wifi_hostfwd_port != 0:
|
||||
return await _proxy_esp32(inst, path, request)
|
||||
|
||||
# ── Pico W (and any other overlay-provided board): the server runs in
|
||||
# the browser-side lwIP, reachable only by the overlay proxying TCP
|
||||
# into the chip over the WS bridge. OSS has no resolver -> None. ──
|
||||
overlay_resp = await dispatch_gateway_proxy(client_id, path, request)
|
||||
if overlay_resp is not None:
|
||||
return overlay_resp
|
||||
|
||||
return Response(
|
||||
content='{"error":"No WiFi-enabled board found for this client. Make sure your sketch connected to WiFi and started a server on port 80."}',
|
||||
|
|
|
|||
|
|
@ -311,7 +311,15 @@ export const SerialMonitor: React.FC = () => {
|
|||
const gatewayUrl = `${backendBase}/gateway/${clientId}${path}`;
|
||||
|
||||
parts.push(text.slice(lastIdx, start));
|
||||
const isPicoW = activeBoard.boardKind === 'pi-pico-w';
|
||||
// Boards whose network stack lives in THIS tab (Pico W, and
|
||||
// any ESP32 on the in-browser JS engine — wifiStatus carries
|
||||
// inBrowser) must open in the in-app iframe: a new tab
|
||||
// backgrounds this one, the emulation gets timer-throttled,
|
||||
// and the in-chip server times out. QEMU boards run
|
||||
// backend-side, so a new tab is fine there.
|
||||
const servesInTab =
|
||||
activeBoard.boardKind === 'pi-pico-w' ||
|
||||
activeBoard.wifiStatus?.inBrowser === true;
|
||||
parts.push(
|
||||
<a
|
||||
key={i}
|
||||
|
|
@ -319,10 +327,8 @@ export const SerialMonitor: React.FC = () => {
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={
|
||||
isPicoW
|
||||
servesInTab
|
||||
? (e) => {
|
||||
// Pico W runs in this tab; a new tab freezes the
|
||||
// emulation. Show the page in an in-app iframe.
|
||||
e.preventDefault();
|
||||
openDeviceGateway(gatewayUrl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2806,9 +2806,16 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
|
|||
}
|
||||
).__velxio_iot_gateway_open_gate__;
|
||||
if (gate && gate()) return;
|
||||
// Pico W runs in THIS tab — a new tab would background and
|
||||
// freeze the emulation. Show the page in an in-app iframe.
|
||||
if (activeBoard.boardKind === 'pi-pico-w') {
|
||||
// Boards whose network stack lives in THIS tab (Pico W,
|
||||
// and any ESP32 on the in-browser JS engine — wifiStatus
|
||||
// carries inBrowser) open in the in-app iframe: a new tab
|
||||
// backgrounds this one, the emulation gets timer-throttled,
|
||||
// and the in-chip server times out. QEMU boards run
|
||||
// backend-side, so a new tab is fine there.
|
||||
if (
|
||||
activeBoard.boardKind === 'pi-pico-w' ||
|
||||
activeBoard.wifiStatus?.inBrowser === true
|
||||
) {
|
||||
openDeviceGateway(gatewayUrl);
|
||||
} else {
|
||||
window.open(gatewayUrl, '_blank');
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ export interface WifiStatus {
|
|||
status: string;
|
||||
ssid?: string;
|
||||
ip?: string;
|
||||
/** True when the board's network stack lives in THIS browser tab (in-browser
|
||||
* JS engine). The IoT gateway must then open in the in-app iframe: a new
|
||||
* tab backgrounds this one, the emulation gets timer-throttled, and the
|
||||
* in-chip HTTP server can't answer. Unset = server runs backend-side. */
|
||||
inBrowser?: boolean;
|
||||
}
|
||||
export interface BleStatus {
|
||||
status: string;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ export interface WifiStatus {
|
|||
status: string; // 'initializing' | 'connected' | 'got_ip' | 'disconnected'
|
||||
ssid?: string;
|
||||
ip?: string;
|
||||
/** True when the board's network stack lives in THIS browser tab (in-browser
|
||||
* JS engine). The IoT gateway must then open in the in-app iframe: a new
|
||||
* tab backgrounds this one, the emulation gets timer-throttled, and the
|
||||
* in-chip HTTP server can't answer. Unset = server runs backend-side. */
|
||||
inBrowser?: boolean;
|
||||
}
|
||||
|
||||
export interface BleStatus {
|
||||
|
|
|
|||
Loading…
Reference in New Issue