Fix web inspector: endianness (LE), ECONNRESET, persistent discovery
- protocol.mjs: all reads/writes switched to LE to match serial.h - node_client.mjs: persistent error handler prevents ECONNRESET crash - discovery.mjs: remove unnecessary SO_REUSEPORT - server.mjs: discovery runs at startup (not per SSE open); uses EventEmitter + known_peers Map so SSE replays existing peers on connect Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,9 +35,10 @@ function read_str8(buf, offset) {
|
||||
}
|
||||
|
||||
function read_i64(buf, offset) {
|
||||
/* Returns a JS number — safe for the int32 values V4L2 uses in practice. */
|
||||
const hi = buf.readInt32BE(offset);
|
||||
const lo = buf.readUInt32BE(offset + 4);
|
||||
/* Little-endian i64: low 4 bytes first, high 4 bytes second.
|
||||
* Returns a JS number — safe for the int32 values V4L2 uses in practice. */
|
||||
const lo = buf.readUInt32LE(offset);
|
||||
const hi = buf.readInt32LE(offset + 4);
|
||||
return hi * 0x100000000 + lo;
|
||||
}
|
||||
|
||||
@@ -47,8 +48,8 @@ function read_i64(buf, offset) {
|
||||
|
||||
export function build_frame(msg_type, payload) {
|
||||
const frame = Buffer.allocUnsafe(FRAME_HEADER_SIZE + payload.length);
|
||||
frame.writeUInt16BE(msg_type, 0);
|
||||
frame.writeUInt32BE(payload.length, 2);
|
||||
frame.writeUInt16LE(msg_type, 0);
|
||||
frame.writeUInt32LE(payload.length, 2);
|
||||
payload.copy(frame, FRAME_HEADER_SIZE);
|
||||
return frame;
|
||||
}
|
||||
@@ -60,36 +61,35 @@ export function build_frame(msg_type, payload) {
|
||||
|
||||
export function encode_enum_devices(request_id) {
|
||||
const p = Buffer.allocUnsafe(4);
|
||||
p.writeUInt16BE(request_id, 0);
|
||||
p.writeUInt16BE(CMD_ENUM_DEVICES, 2);
|
||||
p.writeUInt16LE(request_id, 0);
|
||||
p.writeUInt16LE(CMD_ENUM_DEVICES, 2);
|
||||
return build_frame(MSG_CONTROL_REQUEST, p);
|
||||
}
|
||||
|
||||
export function encode_enum_controls(request_id, device_index) {
|
||||
const p = Buffer.allocUnsafe(6);
|
||||
p.writeUInt16BE(request_id, 0);
|
||||
p.writeUInt16BE(CMD_ENUM_CONTROLS, 2);
|
||||
p.writeUInt16BE(device_index, 4);
|
||||
p.writeUInt16LE(request_id, 0);
|
||||
p.writeUInt16LE(CMD_ENUM_CONTROLS, 2);
|
||||
p.writeUInt16LE(device_index, 4);
|
||||
return build_frame(MSG_CONTROL_REQUEST, p);
|
||||
}
|
||||
|
||||
export function encode_get_control(request_id, device_index, control_id) {
|
||||
const p = Buffer.allocUnsafe(10);
|
||||
p.writeUInt16BE(request_id, 0);
|
||||
p.writeUInt16BE(CMD_GET_CONTROL, 2);
|
||||
p.writeUInt16BE(device_index, 4);
|
||||
p.writeUInt32BE(control_id, 6);
|
||||
p.writeUInt16LE(request_id, 0);
|
||||
p.writeUInt16LE(CMD_GET_CONTROL, 2);
|
||||
p.writeUInt16LE(device_index, 4);
|
||||
p.writeUInt32LE(control_id, 6);
|
||||
return build_frame(MSG_CONTROL_REQUEST, p);
|
||||
}
|
||||
|
||||
export function encode_set_control(request_id, device_index, control_id, value) {
|
||||
const p = Buffer.allocUnsafe(14);
|
||||
p.writeUInt16BE(request_id, 0);
|
||||
p.writeUInt16BE(CMD_SET_CONTROL, 2);
|
||||
p.writeUInt16BE(device_index, 4);
|
||||
p.writeUInt16BE(0, 6);
|
||||
p.writeUInt32BE(control_id, 6);
|
||||
p.writeInt32BE(value, 10);
|
||||
p.writeUInt16LE(request_id, 0);
|
||||
p.writeUInt16LE(CMD_SET_CONTROL, 2);
|
||||
p.writeUInt16LE(device_index, 4);
|
||||
p.writeUInt32LE(control_id, 6);
|
||||
p.writeInt32LE(value, 10);
|
||||
return build_frame(MSG_CONTROL_REQUEST, p);
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ export function encode_set_control(request_id, device_index, control_id, value)
|
||||
|
||||
export function decode_response_header(payload) {
|
||||
return {
|
||||
request_id: payload.readUInt16BE(0),
|
||||
status: payload.readUInt16BE(2),
|
||||
request_id: payload.readUInt16LE(0),
|
||||
status: payload.readUInt16LE(2),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ export function decode_enum_devices_response(payload) {
|
||||
if (hdr.status !== STATUS_OK) { return { ...hdr, media: [], standalone: [] }; }
|
||||
|
||||
let pos = 4;
|
||||
const media_count = payload.readUInt16BE(pos); pos += 2;
|
||||
const media_count = payload.readUInt16LE(pos); pos += 2;
|
||||
const media = [];
|
||||
|
||||
for (let i = 0; i < media_count; i++) {
|
||||
@@ -125,9 +125,9 @@ export function decode_enum_devices_response(payload) {
|
||||
for (let j = 0; j < vcount; j++) {
|
||||
s = read_str8(payload, pos); pos += s.size; const vpath = s.value;
|
||||
s = read_str8(payload, pos); pos += s.size; const entity_name = s.value;
|
||||
const entity_type = payload.readUInt32BE(pos); pos += 4;
|
||||
const entity_flags = payload.readUInt32BE(pos); pos += 4;
|
||||
const device_caps = payload.readUInt32BE(pos); pos += 4;
|
||||
const entity_type = payload.readUInt32LE(pos); pos += 4;
|
||||
const entity_flags = payload.readUInt32LE(pos); pos += 4;
|
||||
const device_caps = payload.readUInt32LE(pos); pos += 4;
|
||||
const pad_flags = payload.readUInt8(pos); pos++;
|
||||
const is_capture = payload.readUInt8(pos); pos++;
|
||||
video_nodes.push({ path: vpath, entity_name, entity_type,
|
||||
@@ -137,7 +137,7 @@ export function decode_enum_devices_response(payload) {
|
||||
media.push({ path, driver, model, bus_info, video_nodes });
|
||||
}
|
||||
|
||||
const standalone_count = payload.readUInt16BE(pos); pos += 2;
|
||||
const standalone_count = payload.readUInt16LE(pos); pos += 2;
|
||||
const standalone = [];
|
||||
for (let i = 0; i < standalone_count; i++) {
|
||||
let s;
|
||||
@@ -154,25 +154,25 @@ export function decode_enum_controls_response(payload) {
|
||||
if (hdr.status !== STATUS_OK) { return { ...hdr, controls: [] }; }
|
||||
|
||||
let pos = 4;
|
||||
const count = payload.readUInt16BE(pos); pos += 2;
|
||||
const count = payload.readUInt16LE(pos); pos += 2;
|
||||
const controls = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const id = payload.readUInt32BE(pos); pos += 4;
|
||||
const id = payload.readUInt32LE(pos); pos += 4;
|
||||
const type = payload.readUInt8(pos); pos++;
|
||||
const flags = payload.readUInt32BE(pos); pos += 4;
|
||||
const flags = payload.readUInt32LE(pos); pos += 4;
|
||||
const s = read_str8(payload, pos); pos += s.size;
|
||||
const name = s.value;
|
||||
const min = payload.readInt32BE(pos); pos += 4;
|
||||
const max = payload.readInt32BE(pos); pos += 4;
|
||||
const step = payload.readInt32BE(pos); pos += 4;
|
||||
const def = payload.readInt32BE(pos); pos += 4;
|
||||
const cur = payload.readInt32BE(pos); pos += 4;
|
||||
const min = payload.readInt32LE(pos); pos += 4;
|
||||
const max = payload.readInt32LE(pos); pos += 4;
|
||||
const step = payload.readInt32LE(pos); pos += 4;
|
||||
const def = payload.readInt32LE(pos); pos += 4;
|
||||
const cur = payload.readInt32LE(pos); pos += 4;
|
||||
const menu_count = payload.readUInt8(pos); pos++;
|
||||
|
||||
const menu_items = [];
|
||||
for (let j = 0; j < menu_count; j++) {
|
||||
const midx = payload.readUInt32BE(pos); pos += 4;
|
||||
const midx = payload.readUInt32LE(pos); pos += 4;
|
||||
const ms = read_str8(payload, pos); pos += ms.size;
|
||||
const mval = read_i64(payload, pos); pos += 8;
|
||||
menu_items.push({ index: midx, name: ms.value, int_value: mval });
|
||||
@@ -187,7 +187,7 @@ export function decode_enum_controls_response(payload) {
|
||||
|
||||
export function decode_get_control_response(payload) {
|
||||
const hdr = decode_response_header(payload);
|
||||
const value = (hdr.status === STATUS_OK) ? payload.readInt32BE(4) : 0;
|
||||
const value = (hdr.status === STATUS_OK) ? payload.readInt32LE(4) : 0;
|
||||
return { ...hdr, value };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user