完善样式系统与调试链路底座
This commit is contained in:
@@ -11,7 +11,9 @@ npm run mock-gps-sim
|
||||
启动后:
|
||||
|
||||
- 控制台页面: `http://127.0.0.1:17865/`
|
||||
- 小程序接收地址: `ws://127.0.0.1:17865/mock-gps`
|
||||
- 小程序定位模拟地址: `ws://127.0.0.1:17865/mock-gps`
|
||||
- 小程序心率模拟地址: `ws://127.0.0.1:17865/mock-hr`
|
||||
- 小程序调试日志地址: `ws://127.0.0.1:17865/debug-log`
|
||||
- 资源代理: `http://127.0.0.1:17865/proxy?url=<remote-url>`
|
||||
|
||||
## 当前能力
|
||||
@@ -28,6 +30,43 @@ npm run mock-gps-sim
|
||||
- 路径回放
|
||||
- 速度、频率、精度调节
|
||||
- 可选桥接到新实时网关
|
||||
- 接收小程序侧 `debug-log` 调试日志
|
||||
|
||||
## 调试日志
|
||||
|
||||
调试日志 websocket 独立地址:
|
||||
|
||||
```text
|
||||
ws://127.0.0.1:17865/debug-log
|
||||
```
|
||||
|
||||
发送消息格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "debug-log",
|
||||
"timestamp": 1712345678901,
|
||||
"scope": "gps-logo",
|
||||
"level": "info",
|
||||
"message": "wx.getImageInfo success",
|
||||
"payload": {
|
||||
"src": "https://example.com/logo.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当前 UI 会通过独立日志通道把这类消息显示到“调试日志”区域。
|
||||
|
||||
第一阶段主要用于承接:
|
||||
|
||||
- `gps-logo`
|
||||
|
||||
后面可以继续扩到:
|
||||
|
||||
- `compass`
|
||||
- `h5`
|
||||
- `content-card`
|
||||
- `heart-rate`
|
||||
|
||||
## 桥接到新网关
|
||||
|
||||
@@ -35,7 +74,9 @@ npm run mock-gps-sim
|
||||
|
||||
默认行为:
|
||||
|
||||
- 小程序仍可继续连接 `ws://127.0.0.1:17865/mock-gps`
|
||||
- 小程序定位模拟继续连接 `ws://127.0.0.1:17865/mock-gps`
|
||||
- 小程序心率模拟继续连接 `ws://127.0.0.1:17865/mock-hr`
|
||||
- 调试日志单独连接 `ws://127.0.0.1:17865/debug-log`
|
||||
- 页面里可以直接配置并启用新网关桥接
|
||||
- 环境变量只作为服务启动时的默认值
|
||||
|
||||
@@ -184,8 +225,20 @@ http://127.0.0.1:17865/bridge-config
|
||||
ws://192.168.1.23:17865/mock-gps
|
||||
```
|
||||
|
||||
心率模拟地址应配置为:
|
||||
|
||||
```text
|
||||
ws://192.168.1.23:17865/mock-hr
|
||||
```
|
||||
|
||||
同理,浏览器里的模拟器页面也建议用电脑局域网地址打开,例如:
|
||||
|
||||
```text
|
||||
http://192.168.1.23:17865/
|
||||
```
|
||||
|
||||
如果你要在小程序里看调试日志,Logger 地址应配置为:
|
||||
|
||||
```text
|
||||
ws://192.168.1.23:17865/debug-log
|
||||
```
|
||||
|
||||
@@ -210,10 +210,18 @@
|
||||
<div class="group__title">日志</div>
|
||||
<div id="log" class="log"></div>
|
||||
</section>
|
||||
|
||||
</aside>
|
||||
|
||||
<main class="map-shell">
|
||||
<div id="map"></div>
|
||||
<section class="floating-debug-log">
|
||||
<div class="floating-debug-log__header">
|
||||
<div class="floating-debug-log__title">调试日志</div>
|
||||
<button id="clearDebugLogBtn" class="floating-debug-log__clear" type="button">清空</button>
|
||||
</div>
|
||||
<div id="debugLog" class="log log--debug log--floating"></div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
const DEFAULT_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/wxmini/test/game.json'
|
||||
const DEFAULT_TILE_URL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
||||
const PROXY_BASE_URL = `${location.origin}/proxy?url=`
|
||||
const WS_URL = `ws://${location.hostname}:17865/mock-gps`
|
||||
const GPS_WS_URL = `ws://${location.hostname}:17865/mock-gps`
|
||||
const HEART_RATE_WS_URL = `ws://${location.hostname}:17865/mock-hr`
|
||||
const DEBUG_LOG_WS_URL = `ws://${location.hostname}:17865/debug-log`
|
||||
const DEFAULT_GATEWAY_BRIDGE_URL = 'ws://127.0.0.1:18080/ws'
|
||||
const LEGACY_GATEWAY_BRIDGE_URLS = new Set([
|
||||
'ws://127.0.0.1:8080/ws',
|
||||
@@ -11,6 +13,7 @@
|
||||
])
|
||||
const BRIDGE_CONFIG_STORAGE_KEY = 'mock-gps-sim.bridge-config'
|
||||
const BRIDGE_PRESETS_STORAGE_KEY = 'mock-gps-sim.bridge-presets'
|
||||
const MAX_DEBUG_LOG_LINES = 400
|
||||
|
||||
const map = L.map('map').setView(DEFAULT_CENTER, 16)
|
||||
let tileLayer = createTileLayer(DEFAULT_TILE_URL, {
|
||||
@@ -37,8 +40,13 @@
|
||||
const pathPoints = []
|
||||
const state = {
|
||||
socket: null,
|
||||
heartRateSocket: null,
|
||||
debugSocket: null,
|
||||
connected: false,
|
||||
heartRateConnected: false,
|
||||
socketConnecting: false,
|
||||
heartRateSocketConnecting: false,
|
||||
debugSocketConnecting: false,
|
||||
streaming: false,
|
||||
heartRateStreaming: false,
|
||||
heartRateSampleMode: false,
|
||||
@@ -134,6 +142,8 @@
|
||||
headingText: document.getElementById('headingText'),
|
||||
pathCountText: document.getElementById('pathCountText'),
|
||||
log: document.getElementById('log'),
|
||||
debugLog: document.getElementById('debugLog'),
|
||||
clearDebugLogBtn: document.getElementById('clearDebugLogBtn'),
|
||||
}
|
||||
|
||||
elements.configUrlInput.value = DEFAULT_CONFIG_URL
|
||||
@@ -150,6 +160,29 @@
|
||||
elements.log.textContent = `[${time}] ${message}\n` + elements.log.textContent
|
||||
}
|
||||
|
||||
function logDebug(entry) {
|
||||
if (!elements.debugLog) {
|
||||
return
|
||||
}
|
||||
|
||||
const time = new Date(entry.timestamp || Date.now()).toLocaleTimeString()
|
||||
const scope = String(entry.scope || 'app')
|
||||
const level = String(entry.level || 'info').toUpperCase()
|
||||
const message = String(entry.message || '')
|
||||
const payloadText = entry.payload ? ` ${JSON.stringify(entry.payload)}` : ''
|
||||
const nextText = `[${time}] [${scope}] [${level}] ${message}${payloadText}\n${elements.debugLog.textContent || ''}`
|
||||
elements.debugLog.textContent = nextText
|
||||
.split('\n')
|
||||
.slice(0, MAX_DEBUG_LOG_LINES)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
function clearDebugLog() {
|
||||
if (elements.debugLog) {
|
||||
elements.debugLog.textContent = ''
|
||||
}
|
||||
}
|
||||
|
||||
function setResourceStatus(message, tone) {
|
||||
elements.resourceStatus.textContent = message
|
||||
elements.resourceStatus.className = 'hint'
|
||||
@@ -191,10 +224,10 @@
|
||||
elements.streamBtn.classList.toggle('is-active', state.streaming)
|
||||
elements.streamBtn.disabled = !state.connected || state.streaming
|
||||
elements.stopStreamBtn.disabled = !state.streaming
|
||||
elements.sendHeartRateOnceBtn.disabled = !state.connected
|
||||
elements.sendHeartRateOnceBtn.disabled = !state.heartRateConnected
|
||||
elements.startHeartRateStreamBtn.textContent = state.heartRateStreaming ? '发送中' : '开始连续发送'
|
||||
elements.startHeartRateStreamBtn.classList.toggle('is-active', state.heartRateStreaming)
|
||||
elements.startHeartRateStreamBtn.disabled = !state.connected || state.heartRateStreaming
|
||||
elements.startHeartRateStreamBtn.disabled = !state.heartRateConnected || state.heartRateStreaming
|
||||
elements.stopHeartRateStreamBtn.disabled = !state.heartRateStreaming
|
||||
elements.toggleHeartRateSampleBtn.textContent = state.heartRateSampleMode ? '关闭真实样本' : '模拟真实样本'
|
||||
elements.toggleHeartRateSampleBtn.classList.toggle('is-active', state.heartRateSampleMode)
|
||||
@@ -250,13 +283,13 @@
|
||||
elements.realtimeStatus.textContent = '桥接未连接'
|
||||
}
|
||||
|
||||
if (state.connected && state.heartRateStreaming) {
|
||||
if (state.heartRateConnected && state.heartRateStreaming) {
|
||||
elements.heartRateStatus.textContent = state.heartRateSampleMode
|
||||
? `桥接已连接,正在以 ${elements.heartRateHzSelect.value} Hz 发送真实心率样本`
|
||||
: `桥接已连接,正在以 ${elements.heartRateHzSelect.value} Hz 连续发送心率`
|
||||
} else if (state.connected) {
|
||||
} else if (state.heartRateConnected) {
|
||||
elements.heartRateStatus.textContent = state.heartRateSampleMode ? '真实心率样本待命' : '心率模拟待命'
|
||||
} else if (state.socketConnecting) {
|
||||
} else if (state.heartRateSocketConnecting) {
|
||||
elements.heartRateStatus.textContent = '桥接连接中'
|
||||
} else {
|
||||
elements.heartRateStatus.textContent = '桥接未连接'
|
||||
@@ -476,12 +509,12 @@
|
||||
return
|
||||
}
|
||||
|
||||
const socket = new WebSocket(WS_URL)
|
||||
const socket = new WebSocket(GPS_WS_URL)
|
||||
state.socket = socket
|
||||
state.socketConnecting = true
|
||||
setSocketBadge(false)
|
||||
updateUiState()
|
||||
log(`连接 ${WS_URL}`)
|
||||
log(`连接 ${GPS_WS_URL}`)
|
||||
|
||||
socket.addEventListener('open', () => {
|
||||
state.connected = true
|
||||
@@ -495,7 +528,6 @@
|
||||
state.connected = false
|
||||
state.socketConnecting = false
|
||||
stopStream()
|
||||
stopHeartRateStream()
|
||||
setSocketBadge(false)
|
||||
updateUiState()
|
||||
log('桥接已断开')
|
||||
@@ -505,13 +537,91 @@
|
||||
state.connected = false
|
||||
state.socketConnecting = false
|
||||
stopStream()
|
||||
stopHeartRateStream()
|
||||
setSocketBadge(false)
|
||||
updateUiState()
|
||||
log('桥接连接失败')
|
||||
})
|
||||
}
|
||||
|
||||
function connectHeartRateSocket() {
|
||||
if (state.heartRateSocket && (state.heartRateSocket.readyState === WebSocket.OPEN || state.heartRateSocket.readyState === WebSocket.CONNECTING)) {
|
||||
return
|
||||
}
|
||||
|
||||
const socket = new WebSocket(HEART_RATE_WS_URL)
|
||||
state.heartRateSocket = socket
|
||||
state.heartRateSocketConnecting = true
|
||||
updateUiState()
|
||||
log(`连接心率模拟 ${HEART_RATE_WS_URL}`)
|
||||
|
||||
socket.addEventListener('open', () => {
|
||||
state.heartRateConnected = true
|
||||
state.heartRateSocketConnecting = false
|
||||
updateUiState()
|
||||
log('心率模拟已连接')
|
||||
})
|
||||
|
||||
socket.addEventListener('close', () => {
|
||||
state.heartRateConnected = false
|
||||
state.heartRateSocketConnecting = false
|
||||
state.heartRateSocket = null
|
||||
stopHeartRateStream()
|
||||
updateUiState()
|
||||
log('心率模拟已断开')
|
||||
})
|
||||
|
||||
socket.addEventListener('error', () => {
|
||||
state.heartRateConnected = false
|
||||
state.heartRateSocketConnecting = false
|
||||
state.heartRateSocket = null
|
||||
stopHeartRateStream()
|
||||
updateUiState()
|
||||
log('心率模拟连接失败')
|
||||
})
|
||||
}
|
||||
|
||||
function connectDebugSocket() {
|
||||
if (state.debugSocket && (state.debugSocket.readyState === WebSocket.OPEN || state.debugSocket.readyState === WebSocket.CONNECTING)) {
|
||||
return
|
||||
}
|
||||
|
||||
const socket = new WebSocket(DEBUG_LOG_WS_URL)
|
||||
state.debugSocket = socket
|
||||
state.debugSocketConnecting = true
|
||||
log(`连接日志通道 ${DEBUG_LOG_WS_URL}`)
|
||||
|
||||
socket.addEventListener('message', (event) => {
|
||||
let parsed = null
|
||||
try {
|
||||
parsed = JSON.parse(String(event.data || ''))
|
||||
} catch (_error) {
|
||||
return
|
||||
}
|
||||
|
||||
if (parsed && parsed.type === 'debug-log') {
|
||||
logDebug(parsed)
|
||||
}
|
||||
})
|
||||
|
||||
socket.addEventListener('open', () => {
|
||||
state.debugSocketConnecting = false
|
||||
log('日志通道已连接')
|
||||
})
|
||||
|
||||
socket.addEventListener('close', () => {
|
||||
state.debugSocketConnecting = false
|
||||
state.debugSocket = null
|
||||
log('日志通道已断开')
|
||||
window.setTimeout(connectDebugSocket, 1500)
|
||||
})
|
||||
|
||||
socket.addEventListener('error', () => {
|
||||
state.debugSocketConnecting = false
|
||||
state.debugSocket = null
|
||||
log('日志通道连接失败')
|
||||
})
|
||||
}
|
||||
|
||||
async function refreshGatewayBridgeStatus() {
|
||||
try {
|
||||
const response = await fetch('/bridge-status', {
|
||||
@@ -1159,8 +1269,8 @@
|
||||
}
|
||||
|
||||
function sendCurrentHeartRate() {
|
||||
if (!state.socket || state.socket.readyState !== WebSocket.OPEN) {
|
||||
log('未连接桥接,无法发送心率')
|
||||
if (!state.heartRateSocket || state.heartRateSocket.readyState !== WebSocket.OPEN) {
|
||||
log('未连接心率模拟,无法发送心率')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1169,7 +1279,7 @@
|
||||
timestamp: Date.now(),
|
||||
bpm: state.heartRateSampleMode ? getSampleHeartRateBpm() : getHeartRateBpm(),
|
||||
}
|
||||
state.socket.send(JSON.stringify(payload))
|
||||
state.heartRateSocket.send(JSON.stringify(payload))
|
||||
state.lastHeartRateSentText = `${formatClockTime(payload.timestamp)} @ ${payload.bpm} bpm`
|
||||
updateUiState()
|
||||
}
|
||||
@@ -1690,6 +1800,9 @@
|
||||
stopPlayback()
|
||||
log('已暂停回放')
|
||||
})
|
||||
if (elements.clearDebugLogBtn) {
|
||||
elements.clearDebugLogBtn.addEventListener('click', clearDebugLog)
|
||||
}
|
||||
|
||||
updateReadout()
|
||||
setSocketBadge(false)
|
||||
@@ -1715,4 +1828,6 @@
|
||||
refreshGatewayBridgeStatus()
|
||||
window.setInterval(refreshGatewayBridgeStatus, 3000)
|
||||
connectSocket()
|
||||
connectHeartRateSocket()
|
||||
connectDebugSocket()
|
||||
})()
|
||||
|
||||
@@ -199,6 +199,20 @@ body {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.log--debug {
|
||||
max-height: 280px;
|
||||
background: #111917;
|
||||
color: #d6f3df;
|
||||
font-family: Consolas, "SFMono-Regular", monospace;
|
||||
}
|
||||
|
||||
.log--floating {
|
||||
min-height: 260px;
|
||||
max-height: min(44vh, 420px);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.jump-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -232,6 +246,49 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.floating-debug-log {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
z-index: 600;
|
||||
width: min(460px, calc(100vw - 480px));
|
||||
min-width: 360px;
|
||||
max-width: 520px;
|
||||
padding: 14px;
|
||||
border-radius: 22px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
border: 1px solid rgba(255, 255, 255, 0.52);
|
||||
box-shadow: 0 22px 60px rgba(17, 33, 26, 0.22);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.floating-debug-log__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.floating-debug-log__title {
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
color: #4a6a5e;
|
||||
}
|
||||
|
||||
.floating-debug-log__clear {
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: rgba(17, 33, 26, 0.1);
|
||||
color: #244132;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
|
||||
@@ -6,7 +6,9 @@ const { WebSocketServer } = WebSocket
|
||||
|
||||
const HOST = '0.0.0.0'
|
||||
const PORT = 17865
|
||||
const WS_PATH = '/mock-gps'
|
||||
const GPS_WS_PATH = '/mock-gps'
|
||||
const HEART_RATE_WS_PATH = '/mock-hr'
|
||||
const DEBUG_LOG_WS_PATH = '/debug-log'
|
||||
const PROXY_PATH = '/proxy'
|
||||
const BRIDGE_STATUS_PATH = '/bridge-status'
|
||||
const BRIDGE_CONFIG_PATH = '/bridge-config'
|
||||
@@ -91,6 +93,14 @@ function isMockHeartRatePayload(payload) {
|
||||
&& Number.isFinite(payload.bpm)
|
||||
}
|
||||
|
||||
function isDebugLogPayload(payload) {
|
||||
return payload
|
||||
&& payload.type === 'debug-log'
|
||||
&& typeof payload.scope === 'string'
|
||||
&& typeof payload.level === 'string'
|
||||
&& typeof payload.message === 'string'
|
||||
}
|
||||
|
||||
async function handleProxyRequest(request, response) {
|
||||
const requestUrl = new URL(request.url || '/', `http://127.0.0.1:${PORT}`)
|
||||
const targetUrl = requestUrl.searchParams.get('url')
|
||||
@@ -497,9 +507,11 @@ const server = http.createServer((request, response) => {
|
||||
serveStatic(request.url || '/', response)
|
||||
})
|
||||
|
||||
const wss = new WebSocketServer({ noServer: true })
|
||||
const gpsWss = new WebSocketServer({ noServer: true })
|
||||
const heartRateWss = new WebSocketServer({ noServer: true })
|
||||
const debugLogWss = new WebSocketServer({ noServer: true })
|
||||
|
||||
wss.on('connection', (socket) => {
|
||||
gpsWss.on('connection', (socket) => {
|
||||
socket.on('message', (rawMessage) => {
|
||||
const text = String(rawMessage)
|
||||
let parsed
|
||||
@@ -509,51 +521,126 @@ wss.on('connection', (socket) => {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isMockGpsPayload(parsed) && !isMockHeartRatePayload(parsed)) {
|
||||
if (!isMockGpsPayload(parsed)) {
|
||||
return
|
||||
}
|
||||
|
||||
const serialized = isMockGpsPayload(parsed)
|
||||
? JSON.stringify({
|
||||
type: 'mock_gps',
|
||||
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
||||
lat: Number(parsed.lat),
|
||||
lon: Number(parsed.lon),
|
||||
accuracyMeters: Number.isFinite(parsed.accuracyMeters) ? Number(parsed.accuracyMeters) : 6,
|
||||
speedMps: Number.isFinite(parsed.speedMps) ? Number(parsed.speedMps) : 0,
|
||||
headingDeg: Number.isFinite(parsed.headingDeg) ? Number(parsed.headingDeg) : 0,
|
||||
})
|
||||
: JSON.stringify({
|
||||
type: 'mock_heart_rate',
|
||||
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
||||
bpm: Math.max(1, Math.round(Number(parsed.bpm))),
|
||||
})
|
||||
const outgoing = JSON.stringify({
|
||||
type: 'mock_gps',
|
||||
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
||||
lat: Number(parsed.lat),
|
||||
lon: Number(parsed.lon),
|
||||
accuracyMeters: Number.isFinite(parsed.accuracyMeters) ? Number(parsed.accuracyMeters) : 6,
|
||||
speedMps: Number.isFinite(parsed.speedMps) ? Number(parsed.speedMps) : 0,
|
||||
headingDeg: Number.isFinite(parsed.headingDeg) ? Number(parsed.headingDeg) : 0,
|
||||
})
|
||||
gatewayBridge.publish(JSON.parse(outgoing))
|
||||
|
||||
gatewayBridge.publish(JSON.parse(serialized))
|
||||
|
||||
wss.clients.forEach((client) => {
|
||||
gpsWss.clients.forEach((client) => {
|
||||
if (client.readyState === client.OPEN) {
|
||||
client.send(serialized)
|
||||
client.send(outgoing)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
heartRateWss.on('connection', (socket) => {
|
||||
socket.on('message', (rawMessage) => {
|
||||
const text = String(rawMessage)
|
||||
let parsed
|
||||
try {
|
||||
parsed = JSON.parse(text)
|
||||
} catch (_error) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isMockHeartRatePayload(parsed)) {
|
||||
return
|
||||
}
|
||||
|
||||
const outgoing = JSON.stringify({
|
||||
type: 'mock_heart_rate',
|
||||
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
||||
bpm: Math.max(1, Math.round(Number(parsed.bpm))),
|
||||
})
|
||||
gatewayBridge.publish(JSON.parse(outgoing))
|
||||
|
||||
heartRateWss.clients.forEach((client) => {
|
||||
if (client.readyState === client.OPEN) {
|
||||
client.send(outgoing)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
debugLogWss.on('connection', (socket) => {
|
||||
socket.on('message', (rawMessage) => {
|
||||
const text = String(rawMessage)
|
||||
let parsed
|
||||
try {
|
||||
parsed = JSON.parse(text)
|
||||
} catch (_error) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isDebugLogPayload(parsed)) {
|
||||
return
|
||||
}
|
||||
|
||||
const outgoing = JSON.stringify({
|
||||
type: 'debug-log',
|
||||
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
||||
scope: String(parsed.scope || 'app').slice(0, 64),
|
||||
level: parsed.level === 'warn' || parsed.level === 'error' ? parsed.level : 'info',
|
||||
message: String(parsed.message || '').slice(0, 400),
|
||||
...(parsed.payload && typeof parsed.payload === 'object'
|
||||
? { payload: parsed.payload }
|
||||
: {}),
|
||||
})
|
||||
|
||||
debugLogWss.clients.forEach((client) => {
|
||||
if (client.readyState === client.OPEN) {
|
||||
client.send(outgoing)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
server.on('upgrade', (request, socket, head) => {
|
||||
if (!request.url || !request.url.startsWith(WS_PATH)) {
|
||||
socket.destroy()
|
||||
const requestUrl = request.url || ''
|
||||
if (requestUrl.startsWith(GPS_WS_PATH)) {
|
||||
gpsWss.handleUpgrade(request, socket, head, (ws) => {
|
||||
gpsWss.emit('connection', ws, request)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
wss.emit('connection', ws, request)
|
||||
})
|
||||
if (requestUrl.startsWith(HEART_RATE_WS_PATH)) {
|
||||
heartRateWss.handleUpgrade(request, socket, head, (ws) => {
|
||||
heartRateWss.emit('connection', ws, request)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (requestUrl.startsWith(DEBUG_LOG_WS_PATH)) {
|
||||
debugLogWss.handleUpgrade(request, socket, head, (ws) => {
|
||||
debugLogWss.emit('connection', ws, request)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!requestUrl) {
|
||||
socket.destroy()
|
||||
}
|
||||
socket.destroy()
|
||||
})
|
||||
|
||||
server.listen(PORT, HOST, () => {
|
||||
console.log(`Mock GPS simulator running:`)
|
||||
console.log(` UI: http://127.0.0.1:${PORT}/`)
|
||||
console.log(` WS: ws://127.0.0.1:${PORT}${WS_PATH}`)
|
||||
console.log(` GPS WS: ws://127.0.0.1:${PORT}${GPS_WS_PATH}`)
|
||||
console.log(` HR WS: ws://127.0.0.1:${PORT}${HEART_RATE_WS_PATH}`)
|
||||
console.log(` Logger WS: ws://127.0.0.1:${PORT}${DEBUG_LOG_WS_PATH}`)
|
||||
console.log(` Proxy: http://127.0.0.1:${PORT}${PROXY_PATH}?url=<remote-url>`)
|
||||
console.log(` Bridge status: http://127.0.0.1:${PORT}${BRIDGE_STATUS_PATH}`)
|
||||
console.log(` Bridge config: http://127.0.0.1:${PORT}${BRIDGE_CONFIG_PATH}`)
|
||||
|
||||
Reference in New Issue
Block a user