fix(ui): map sensing websocket port for docker (#572)
This commit is contained in:
parent
ea62ec4667
commit
4698f54fa0
|
|
@ -9,11 +9,25 @@
|
||||||
* emit simulated frames so the UI can clearly distinguish live vs. fallback data.
|
* emit simulated frames so the UI can clearly distinguish live vs. fallback data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Derive WebSocket URL from the page origin so it works on any port.
|
const SENSING_WS_PORT_BY_HTTP_PORT = {
|
||||||
// The /ws/sensing endpoint is available on the same HTTP port (3000).
|
// Docker image: HTTP UI/API on 3000, sensing stream on 3001.
|
||||||
const _wsProto = (typeof window !== 'undefined' && window.location.protocol === 'https:') ? 'wss:' : 'ws:';
|
'3000': '3001',
|
||||||
const _wsHost = (typeof window !== 'undefined' && window.location.host) ? window.location.host : 'localhost:3000';
|
// Python sensing stack: UI on 8080, sensing stream on 8765.
|
||||||
const SENSING_WS_URL = `${_wsProto}//${_wsHost}/ws/sensing`;
|
'8080': '8765',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildSensingWsUrl(locationLike = (typeof window !== 'undefined' ? window.location : null)) {
|
||||||
|
const protocol = locationLike && locationLike.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
const host = locationLike && locationLike.host ? locationLike.host : 'localhost:3001';
|
||||||
|
const hostname = locationLike && locationLike.hostname ? locationLike.hostname : host.split(':')[0];
|
||||||
|
const port = locationLike && locationLike.port ? locationLike.port : '';
|
||||||
|
const wsPort = SENSING_WS_PORT_BY_HTTP_PORT[port];
|
||||||
|
const wsHost = wsPort ? `${hostname}:${wsPort}` : host;
|
||||||
|
|
||||||
|
return `${protocol}//${wsHost}/ws/sensing`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SENSING_WS_URL = buildSensingWsUrl();
|
||||||
const RECONNECT_DELAYS = [1000, 2000, 4000, 8000, 16000];
|
const RECONNECT_DELAYS = [1000, 2000, 4000, 8000, 16000];
|
||||||
const MAX_RECONNECT_ATTEMPTS = 20;
|
const MAX_RECONNECT_ATTEMPTS = 20;
|
||||||
// Number of failed attempts that must occur before simulation starts.
|
// Number of failed attempts that must occur before simulation starts.
|
||||||
|
|
|
||||||
|
|
@ -136,9 +136,22 @@ export class WebSocketService {
|
||||||
|
|
||||||
// Set up WebSocket event handlers
|
// Set up WebSocket event handlers
|
||||||
setupEventHandlers(url, ws, handlers) {
|
setupEventHandlers(url, ws, handlers) {
|
||||||
const connection = this.connections.get(url);
|
const getConnection = (eventName) => {
|
||||||
|
const connection = this.connections.get(url);
|
||||||
|
if (!connection) {
|
||||||
|
this.logger.warn(`Ignoring WebSocket ${eventName} for unregistered connection`, {
|
||||||
|
url,
|
||||||
|
readyState: ws.readyState
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
};
|
||||||
|
|
||||||
ws.onopen = (event) => {
|
ws.onopen = (event) => {
|
||||||
|
const connection = getConnection('open');
|
||||||
|
if (!connection) return;
|
||||||
|
|
||||||
const connectionTime = Date.now() - connection.connectionStartTime;
|
const connectionTime = Date.now() - connection.connectionStartTime;
|
||||||
this.logger.info(`WebSocket connected successfully`, { url, connectionTime });
|
this.logger.info(`WebSocket connected successfully`, { url, connectionTime });
|
||||||
|
|
||||||
|
|
@ -158,6 +171,9 @@ export class WebSocketService {
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
|
const connection = getConnection('message');
|
||||||
|
if (!connection) return;
|
||||||
|
|
||||||
connection.lastActivity = Date.now();
|
connection.lastActivity = Date.now();
|
||||||
connection.messageCount++;
|
connection.messageCount++;
|
||||||
|
|
||||||
|
|
@ -188,6 +204,9 @@ export class WebSocketService {
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (event) => {
|
ws.onerror = (event) => {
|
||||||
|
const connection = getConnection('error');
|
||||||
|
if (!connection) return;
|
||||||
|
|
||||||
connection.errorCount++;
|
connection.errorCount++;
|
||||||
this.logger.error(`WebSocket error occurred`, {
|
this.logger.error(`WebSocket error occurred`, {
|
||||||
url,
|
url,
|
||||||
|
|
@ -208,6 +227,9 @@ export class WebSocketService {
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = (event) => {
|
ws.onclose = (event) => {
|
||||||
|
const connection = getConnection('close');
|
||||||
|
if (!connection) return;
|
||||||
|
|
||||||
const { code, reason, wasClean } = event;
|
const { code, reason, wasClean } = event;
|
||||||
this.logger.info(`WebSocket closed`, { url, code, reason, wasClean });
|
this.logger.info(`WebSocket closed`, { url, code, reason, wasClean });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { API_CONFIG, buildApiUrl, buildWsUrl } from '../config/api.config.js';
|
import { API_CONFIG, buildApiUrl, buildWsUrl } from '../config/api.config.js';
|
||||||
import { apiService } from '../services/api.service.js';
|
import { apiService } from '../services/api.service.js';
|
||||||
import { wsService } from '../services/websocket.service.js';
|
import { wsService } from '../services/websocket.service.js';
|
||||||
|
import { buildSensingWsUrl } from '../services/sensing.service.js';
|
||||||
import { poseService } from '../services/pose.service.js';
|
import { poseService } from '../services/pose.service.js';
|
||||||
import { healthService } from '../services/health.service.js';
|
import { healthService } from '../services/health.service.js';
|
||||||
import { TabManager } from '../components/TabManager.js';
|
import { TabManager } from '../components/TabManager.js';
|
||||||
|
|
@ -232,6 +233,17 @@ testRunner.test('buildWsUrl constructs WebSocket URLs', 'apiConfig', () => {
|
||||||
testRunner.assert(url.includes('token=test-token'), 'URL should contain token parameter');
|
testRunner.assert(url.includes('token=test-token'), 'URL should contain token parameter');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testRunner.test('buildSensingWsUrl maps Docker UI port to sensing WebSocket port', 'apiConfig', () => {
|
||||||
|
const url = buildSensingWsUrl({
|
||||||
|
protocol: 'http:',
|
||||||
|
host: '192.168.28.147:3000',
|
||||||
|
hostname: '192.168.28.147',
|
||||||
|
port: '3000',
|
||||||
|
});
|
||||||
|
|
||||||
|
testRunner.assertEqual(url, 'ws://192.168.28.147:3001/ws/sensing');
|
||||||
|
});
|
||||||
|
|
||||||
// API Service Tests
|
// API Service Tests
|
||||||
testRunner.test('apiService has required methods', 'apiService', () => {
|
testRunner.test('apiService has required methods', 'apiService', () => {
|
||||||
testRunner.assert(typeof apiService.get === 'function', 'get method should exist');
|
testRunner.assert(typeof apiService.get === 'function', 'get method should exist');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue