mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(layers): keep the Local ADS-B receiver error in English
The Local ADS-B layer copied the SDR controller's `message`, which is Radio card text in the UI language, into its `error` stat. The controller now publishes an English `errorText` next to the translated `message` for every receiver error, and the layer reports that. A receiver is any object with getState(); one that does not publish errorText still shows its error from `message`, as before. The Radio card text is unchanged, and the Layers panel still shows the error translated.
This commit is contained in:
1 parent
4d2a71c1b2
commit
3d64a54a21
4 files changed
+75
-38
No files matched your search
@@ -66,10 +66,12 @@ const DEFAULT_DISPLAY = Object.freeze({
|
|||||||
* Local ADS-B layer: draws aircraft heard by the user's own receivers.
|
* Local ADS-B layer: draws aircraft heard by the user's own receivers.
|
||||||
*
|
*
|
||||||
* Two inputs feed it. The receiver is any object with `getState()` returning
|
* Two inputs feed it. The receiver is any object with `getState()` returning
|
||||||
* `{ connected, status, message, mode, webUsbSupported, aircraft, messagesPerSecond }`,
|
* `{ connected, status, message, errorText, mode, webUsbSupported, aircraft, messagesPerSecond }`,
|
||||||
* `subscribe(listener)` and `setMode(mode)`; `aircraft` holds records from
|
* `subscribe(listener)` and `setMode(mode)`; `aircraft` holds records from
|
||||||
* `src/sources/adsbRecords.js`. The browser WebUSB session is one such
|
* `src/sources/adsbRecords.js`, and the optional `errorText` is the English
|
||||||
* receiver. The optional `feeds` session (`./feeds.js`) supplies records read
|
* text of an `error` status (`message`, the UI-language one, stands in for a
|
||||||
|
* receiver without it). The browser WebUSB session is one such receiver.
|
||||||
|
* The optional `feeds` session (`./feeds.js`) supplies records read
|
||||||
* by the server from local 1090/978 MHz decoders; it polls only while this
|
* by the server from local 1090/978 MHz decoders; it polls only while this
|
||||||
* layer is enabled. Records are merged by ICAO (`mergeLocalAdsbRecords`).
|
* layer is enabled. Records are merged by ICAO (`mergeLocalAdsbRecords`).
|
||||||
* Aircraft heard only on 978 MHz UAT carry a thin ring around the marker.
|
* Aircraft heard only on 978 MHz UAT carry a thin ring around the marker.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function feedsConfigured(feedState) {
|
|||||||
|
|
||||||
function webUsbStatus(receiver, heard, feedState) {
|
function webUsbStatus(receiver, heard, feedState) {
|
||||||
if (receiver.status === 'error')
|
if (receiver.status === 'error')
|
||||||
return { status: 'error', error: receiver.message };
|
return { status: 'error', error: receiver.errorText ?? receiver.message };
|
||||||
if (receiver.status === 'connecting' || receiver.status === 'tuning')
|
if (receiver.status === 'connecting' || receiver.status === 'tuning')
|
||||||
return { loading: true, loadingLabel: msg('opening receiver') };
|
return { loading: true, loadingLabel: msg('opening receiver') };
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
// "USB error", feed problems, even as the whole error) read in the UI language.
|
// "USB error", feed problems, even as the whole error) read in the UI language.
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import { setLocale } from '../../i18n/index.js';
|
import { setLocale, t } from '../../i18n/index.js';
|
||||||
|
import { SdrController } from '../../sdr/controller.js';
|
||||||
import { LayerPanel } from '../../ui/layerPanel.js';
|
import { LayerPanel } from '../../ui/layerPanel.js';
|
||||||
import { localAdsbStatus } from './status.js';
|
import { localAdsbStatus } from './status.js';
|
||||||
|
|
||||||
@@ -66,3 +67,41 @@ for (const locale of ['es', 'ru']) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A receiver error is English data for the layer (stats, and what the voice
|
||||||
|
// tools and the analyst read); the Radio card and the row show it translated.
|
||||||
|
for (const locale of ['es', 'ru']) {
|
||||||
|
test(`${locale}: a WebUSB receiver error stays English in stats and reads translated`, async () => {
|
||||||
|
const receiver = new SdrController({
|
||||||
|
storage: null,
|
||||||
|
webUsb: {},
|
||||||
|
providerFactory: () => ({
|
||||||
|
get: async () => {
|
||||||
|
throw new Error('No device was selected.');
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setLocale(locale);
|
||||||
|
try {
|
||||||
|
assert.equal(await receiver.connect('adsb'), false);
|
||||||
|
const state = receiver.getState();
|
||||||
|
assert.equal(state.status, 'error');
|
||||||
|
assert.equal(state.errorText, 'No RTL-SDR device selected');
|
||||||
|
assert.equal(state.message, t('No RTL-SDR device selected'));
|
||||||
|
assert.notEqual(state.message, state.errorText);
|
||||||
|
|
||||||
|
const status = localAdsbStatus({
|
||||||
|
receiver: state,
|
||||||
|
feedState: null,
|
||||||
|
heard: 0,
|
||||||
|
});
|
||||||
|
assert.equal(status.error, 'No RTL-SDR device selected');
|
||||||
|
const row = rowFor(status, 0);
|
||||||
|
assert.ok(row.includes(state.message), row);
|
||||||
|
assert.doesNotMatch(row, /No RTL-SDR device selected/, row);
|
||||||
|
} finally {
|
||||||
|
setLocale('en');
|
||||||
|
await receiver.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
+29
-33
@@ -283,22 +283,32 @@ function clampFmFrequency(value) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A failed connection in English; errorState() shows it translated. */
|
||||||
function describeConnectionError(error) {
|
function describeConnectionError(error) {
|
||||||
const message = String(error?.message || error || '').trim();
|
const message = String(error?.message || error || '').trim();
|
||||||
if (/No device was selected|NotFoundError/i.test(message))
|
if (/No device was selected|NotFoundError/i.test(message))
|
||||||
return t('No RTL-SDR device selected');
|
return msg('No RTL-SDR device selected');
|
||||||
if (
|
if (
|
||||||
/does not support the WebUSB|usb/i.test(message) &&
|
/does not support the WebUSB|usb/i.test(message) &&
|
||||||
/support/i.test(message)
|
/support/i.test(message)
|
||||||
) {
|
) {
|
||||||
return t('WebUSB requires desktop Chrome or Edge on localhost/HTTPS');
|
return msg('WebUSB requires desktop Chrome or Edge on localhost/HTTPS');
|
||||||
}
|
}
|
||||||
if (/access|permission|claim|busy/i.test(message)) {
|
if (/access|permission|claim|busy/i.test(message)) {
|
||||||
return t(
|
return msg(
|
||||||
'RTL-SDR access failed; close other SDR apps and check USB permissions',
|
'RTL-SDR access failed; close other SDR apps and check USB permissions',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return message ? translateKnown(message) : t('RTL-SDR connection failed');
|
return message || msg('RTL-SDR connection failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State fields for a receiver error: `message` in the UI language for the
|
||||||
|
* Radio card, `errorText` as English data for the Local ADS-B layer's stats.
|
||||||
|
* @param {string} text English error text (ours or the browser's).
|
||||||
|
*/
|
||||||
|
function errorState(text) {
|
||||||
|
return { status: 'error', message: translateKnown(text), errorText: text };
|
||||||
}
|
}
|
||||||
|
|
||||||
function streamingMessage(mode) {
|
function streamingMessage(mode) {
|
||||||
@@ -555,10 +565,7 @@ export class SdrController {
|
|||||||
// A failed worker (e.g. its module did not load) never recovers: drop
|
// A failed worker (e.g. its module did not load) never recovers: drop
|
||||||
// it so the next session builds and configures a fresh one.
|
// it so the next session builds and configures a fresh one.
|
||||||
this._discardWorker();
|
this._discardWorker();
|
||||||
this._setState({
|
this._setState(errorState(msg('SDR decoder worker failed')));
|
||||||
status: 'error',
|
|
||||||
message: t('SDR decoder worker failed'),
|
|
||||||
});
|
|
||||||
if (this._device) void this.stop({ preserveMessage: true });
|
if (this._device) void this.stop({ preserveMessage: true });
|
||||||
};
|
};
|
||||||
return worker;
|
return worker;
|
||||||
@@ -779,16 +786,16 @@ export class SdrController {
|
|||||||
// Published before stopping; stop() preserves it, and nothing is set
|
// Published before stopping; stop() preserves it, and nothing is set
|
||||||
// afterwards, so a reconnect started during teardown keeps its own state.
|
// afterwards, so a reconnect started during teardown keeps its own state.
|
||||||
this._setState({
|
this._setState({
|
||||||
status: 'error',
|
...errorState(
|
||||||
|
reason === 'startup'
|
||||||
|
? msg('SDR decoder did not start; receiver stopped')
|
||||||
|
: reason === 'stalled'
|
||||||
|
? msg('SDR decoder stopped responding; receiver stopped')
|
||||||
|
: msg('SDR decoder fell behind; receiver stopped'),
|
||||||
|
),
|
||||||
connected: false,
|
connected: false,
|
||||||
decoderOverload: reason,
|
decoderOverload: reason,
|
||||||
decoderOverloadSnapshot: snapshot,
|
decoderOverloadSnapshot: snapshot,
|
||||||
message:
|
|
||||||
reason === 'startup'
|
|
||||||
? t('SDR decoder did not start; receiver stopped')
|
|
||||||
: reason === 'stalled'
|
|
||||||
? t('SDR decoder stopped responding; receiver stopped')
|
|
||||||
: t('SDR decoder fell behind; receiver stopped'),
|
|
||||||
});
|
});
|
||||||
void this.stop({ preserveMessage: true });
|
void this.stop({ preserveMessage: true });
|
||||||
}
|
}
|
||||||
@@ -1085,7 +1092,7 @@ export class SdrController {
|
|||||||
// consumes the click activation.
|
// consumes the click activation.
|
||||||
if (nextMode === 'fm' && !(await this._ensureAudio())) {
|
if (nextMode === 'fm' && !(await this._ensureAudio())) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
t('Browser audio is suspended; press Connect again to resume it'),
|
msg('Browser audio is suspended; press Connect again to resume it'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (superseded()) return false;
|
if (superseded()) return false;
|
||||||
@@ -1153,8 +1160,7 @@ export class SdrController {
|
|||||||
if (superseded()) return false;
|
if (superseded()) return false;
|
||||||
this._setState({
|
this._setState({
|
||||||
connected: false,
|
connected: false,
|
||||||
status: 'error',
|
...errorState(describeConnectionError(error)),
|
||||||
message: describeConnectionError(error),
|
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1507,8 +1513,7 @@ export class SdrController {
|
|||||||
console.warn('[SDR] Sample read failed:', error);
|
console.warn('[SDR] Sample read failed:', error);
|
||||||
this._setState({
|
this._setState({
|
||||||
connected: false,
|
connected: false,
|
||||||
status: 'error',
|
...errorState(msg('RTL-SDR sample stream stopped')),
|
||||||
message: t('RTL-SDR sample stream stopped'),
|
|
||||||
});
|
});
|
||||||
await this.stop({ preserveMessage: true });
|
await this.stop({ preserveMessage: true });
|
||||||
break;
|
break;
|
||||||
@@ -1553,7 +1558,7 @@ export class SdrController {
|
|||||||
try {
|
try {
|
||||||
if (nextMode === 'fm' && !(await this._ensureAudio())) {
|
if (nextMode === 'fm' && !(await this._ensureAudio())) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
t('Browser audio is suspended; select FM again to resume it'),
|
msg('Browser audio is suspended; select FM again to resume it'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// A stall teardown, stop() or reconnect during the switch supersedes
|
// A stall teardown, stop() or reconnect during the switch supersedes
|
||||||
@@ -1578,10 +1583,7 @@ export class SdrController {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!this._owns(token, device)) return false;
|
if (!this._owns(token, device)) return false;
|
||||||
console.warn('[SDR] Mode switch failed:', error);
|
console.warn('[SDR] Mode switch failed:', error);
|
||||||
this._setState({
|
this._setState(errorState(describeConnectionError(error)));
|
||||||
status: 'error',
|
|
||||||
message: describeConnectionError(error),
|
|
||||||
});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1622,10 +1624,7 @@ export class SdrController {
|
|||||||
if (targetMode === 'adsb') this._setState(this._resetAdsbCounters());
|
if (targetMode === 'adsb') this._setState(this._resetAdsbCounters());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[SDR] Gain change failed:', error);
|
console.warn('[SDR] Gain change failed:', error);
|
||||||
this._setState({
|
this._setState(errorState(msg('RTL-SDR gain change failed')));
|
||||||
status: 'error',
|
|
||||||
message: t('RTL-SDR gain change failed'),
|
|
||||||
});
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return setting;
|
return setting;
|
||||||
@@ -1679,10 +1678,7 @@ export class SdrController {
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._setState({
|
this._setState(errorState(describeConnectionError(error)));
|
||||||
status: 'error',
|
|
||||||
message: describeConnectionError(error),
|
|
||||||
});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user