ui: decode and abbreviate long PLMN lists in the network monitor (v2.7.16)
EF.HPLMNwAcT fell back to raw hex: efFindDecoder uppercases the requested
name but compared it against the mixed-case registry names
('EF.HPLMNwAcT'), and the monitor passes no FID, so the lookup failed.
Name matching is now case-insensitive, which also fixes EF.PLMNwAcT,
EF.OPLMNwAcT, EF.PLMNsel, EF.KcGPRS and EF.LOCIGPRS for name-only callers.
The decoded HPLMNwAcT list can still be long (one entry per PLMN/AcT
combination), so netStatePlmnList keeps the first three networks and
appends a '… +N' counter; the row tooltip keeps the full decoded list.
- tests: registry case-insensitivity, netStatePlmnList limit, HPLMNwAcT
summary abbreviation; help EN/RU.
This commit is contained in:
@@ -492,7 +492,7 @@
|
||||
<p class="text-sm mb-3"><strong>Параметры</strong> (свёрнуты) задают оператора (поиск по мировому списку MCC/MNC с сервера плюс выбор случайного роуминг-оператора), LAC/Cell ID/TAC/RAC, необязательные идентификаторы (пусто = случайно: TMSI, GUTI, KSI, KASME, Kc, счётчики NAS, алгоритм, RAND/AUTN), переключатели сценария (включая <strong>«Отказ: записать FPLMN»</strong>) и число циклов/задержку. Журнал шагов показывает каждую запись, ENVELOPE и AUTHENTICATE с их SW. Отправляются только UPDATE BINARY/RECORD, ENVELOPE и AUTHENTICATE — FPLMN записывается только сценарием постоянного отказа (TS 31.102 §4.2.16), а 5GS location-файлы не записываются; записи меняют карту и видны в последующих сравнениях снимков.</p>
|
||||
|
||||
<h4 id="network-state" class="font-medium mb-1">Монитор сетевого состояния</h4>
|
||||
<p class="text-sm mb-3">Рядом с кнопками симуляции компактная панель <strong>«Сетевое состояние»</strong> показывает, что сейчас хранит карта и что было сэмулировано последним. В заголовке — <strong>сэмулированное состояние сервиса</strong>: <em>Не определено</em>, пока его не задаст сценарий или событие Location status, затем <em>Обычный сервис</em> (зелёный), <em>Ограниченный сервис</em> (жёлтый) или <em>Нет сервиса</em> (красный), с красной пометкой <em>PLMN не разрешён</em>, если location-файлы или EF.FPLMN указывают на отказ регистрации — плюс текущее <strong>местоположение</strong>: PLMN, страна и оператор (из необязательного мирового списка MCC/MNC, если он загружен), LAI/RAI/TAI и <strong>класс роуминга</strong> (<em>Домашняя сеть</em>, если PLMN совпадает с HPLMN; <em>Эквивалентная домашней</em>, если он есть в EF.EHPLMN; иначе <em>Гостевая (роуминг)</em>). Ниже — по одной компактной строке на контролируемый файл (IMSI, EHPLMN, SPDI, HPLMNwAcT, LOCI, PSLOCI, EPSLOCI, EPSNSC, CBMI, CBMIR, SMSstatus, FPLMN) с декодированной сводкой и признаком последнего обновления (<code class="font-mono text-sm">init</code>, <code class="font-mono text-sm">write</code>, <code class="font-mono text-sm">read</code>, <code class="font-mono text-sm">refresh</code>); при наведении — все декодированные поля. Панель читает файлы один раз при подключении карты (только если ICCID читается), обновляет их на месте по записанным симулятором байтам, перечитывает EF.IMSI после каждого сценария и события Location status (мульти-IMSI апплеты) и никогда не опрашивает карту — кнопка <strong>«Обновить»</strong> перечитывает все файлы по требованию.</p>
|
||||
<p class="text-sm mb-3">Рядом с кнопками симуляции компактная панель <strong>«Сетевое состояние»</strong> показывает, что сейчас хранит карта и что было сэмулировано последним. В заголовке — <strong>сэмулированное состояние сервиса</strong>: <em>Не определено</em>, пока его не задаст сценарий или событие Location status, затем <em>Обычный сервис</em> (зелёный), <em>Ограниченный сервис</em> (жёлтый) или <em>Нет сервиса</em> (красный), с красной пометкой <em>PLMN не разрешён</em>, если location-файлы или EF.FPLMN указывают на отказ регистрации — плюс текущее <strong>местоположение</strong>: PLMN, страна и оператор (из необязательного мирового списка MCC/MNC, если он загружен), LAI/RAI/TAI и <strong>класс роуминга</strong> (<em>Домашняя сеть</em>, если PLMN совпадает с HPLMN; <em>Эквивалентная домашней</em>, если он есть в EF.EHPLMN; иначе <em>Гостевая (роуминг)</em>). Ниже — по одной компактной строке на контролируемый файл (IMSI, EHPLMN, SPDI, HPLMNwAcT, LOCI, PSLOCI, EPSLOCI, EPSNSC, CBMI, CBMIR, SMSstatus, FPLMN) с декодированной сводкой и признаком последнего обновления (<code class="font-mono text-sm">init</code>, <code class="font-mono text-sm">write</code>, <code class="font-mono text-sm">read</code>, <code class="font-mono text-sm">refresh</code>); при наведении — все декодированные поля; длинные списки PLMN (например, EF.HPLMNwAcT) сокращаются до первых сетей с пометкой <code class="font-mono text-sm">… +N</code>. Панель читает файлы один раз при подключении карты (только если ICCID читается), обновляет их на месте по записанным симулятором байтам, перечитывает EF.IMSI после каждого сценария и события Location status (мульти-IMSI апплеты) и никогда не опрашивает карту — кнопка <strong>«Обновить»</strong> перечитывает все файлы по требованию.</p>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
+1
-1
@@ -492,7 +492,7 @@
|
||||
<p class="text-sm mb-3"><strong>Parameters</strong> (collapsed) provide the operator (searchable worldwide MCC/MNC list served from the server, plus a random roaming picker), LAC/Cell ID/TAC/RAC, optional identity values (empty = random: TMSI, GUTI, KSI, KASME, Kc, NAS counts, algorithm, RAND/AUTN), the scenario toggles (including <strong>Rejection: write FPLMN</strong>) and the churn count/delay. The step log lists every write, ENVELOPE and AUTHENTICATE with its SW. Only UPDATE BINARY/RECORD, ENVELOPE and AUTHENTICATE are sent — FPLMN is written only by the permanent rejection scenario (TS 31.102 §4.2.16) and the 5GS location files are never written; the writes change the card and are visible to later snapshot comparisons.</p>
|
||||
|
||||
<h4 id="network-state" class="font-medium mb-1">Network state monitor</h4>
|
||||
<p class="text-sm mb-3">Next to the simulation buttons a compact <strong>Network state</strong> panel shows what the card currently holds and what was last simulated. Its header carries the <strong>simulated service state</strong> — <em>Undefined</em> until a scenario or a Location status event sets it, then <em>Normal service</em> (green), <em>Limited service</em> (amber) or <em>No service</em> (red), with a red <em>PLMN not allowed</em> marker when the location files or EF.FPLMN show a rejection — plus the current <strong>location</strong>: PLMN, country and operator (from the optional worldwide MCC/MNC list when loaded), the LAI/RAI/TAI, and the <strong>roaming class</strong> (<em>Home</em> when the PLMN equals the HPLMN, <em>Home equivalent</em> when it is in EF.EHPLMN, otherwise <em>Guest</em>). Below it, one compact line per monitored file (IMSI, EHPLMN, SPDI, HPLMNwAcT, LOCI, PSLOCI, EPSLOCI, EPSNSC, CBMI, CBMIR, SMSstatus, FPLMN) with its decoded summary and how it was last updated (<code class="font-mono text-sm">init</code>, <code class="font-mono text-sm">write</code>, <code class="font-mono text-sm">read</code>, <code class="font-mono text-sm">refresh</code>); hover for the full decoded fields. The panel reads the files once at equip (only when the ICCID was readable), updates them in place from the bytes the simulator wrote, re-reads EF.IMSI after every scenario and Location-status event (multi-IMSI applets) and never polls the card — use <strong>Refresh</strong> to re-read all files on demand.</p>
|
||||
<p class="text-sm mb-3">Next to the simulation buttons a compact <strong>Network state</strong> panel shows what the card currently holds and what was last simulated. Its header carries the <strong>simulated service state</strong> — <em>Undefined</em> until a scenario or a Location status event sets it, then <em>Normal service</em> (green), <em>Limited service</em> (amber) or <em>No service</em> (red), with a red <em>PLMN not allowed</em> marker when the location files or EF.FPLMN show a rejection — plus the current <strong>location</strong>: PLMN, country and operator (from the optional worldwide MCC/MNC list when loaded), the LAI/RAI/TAI, and the <strong>roaming class</strong> (<em>Home</em> when the PLMN equals the HPLMN, <em>Home equivalent</em> when it is in EF.EHPLMN, otherwise <em>Guest</em>). Below it, one compact line per monitored file (IMSI, EHPLMN, SPDI, HPLMNwAcT, LOCI, PSLOCI, EPSLOCI, EPSNSC, CBMI, CBMIR, SMSstatus, FPLMN) with its decoded summary and how it was last updated (<code class="font-mono text-sm">init</code>, <code class="font-mono text-sm">write</code>, <code class="font-mono text-sm">read</code>, <code class="font-mono text-sm">refresh</code>); hover for the full decoded fields — long PLMN lists (e.g. EF.HPLMNwAcT) are abbreviated to the first few networks plus a <code class="font-mono text-sm">… +N</code> counter. The panel reads the files once at equip (only when the ICCID was readable), updates them in place from the bytes the simulator wrote, re-reads EF.IMSI after every scenario and Location-status event (multi-IMSI applets) and never polls the card — use <strong>Refresh</strong> to re-read all files on demand.</p>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
+11
-7
@@ -1413,7 +1413,7 @@
|
||||
// ===== Version =====
|
||||
// Single source of truth for the PWA version: shown in the header and used
|
||||
// by the server version check in pysimConnect().
|
||||
const SIMPLE_VERSION = '2.7.15';
|
||||
const SIMPLE_VERSION = '2.7.16';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== Tab switching =====
|
||||
@@ -12522,7 +12522,7 @@ const EF_DECODERS = [
|
||||
function efFindDecoder(name, fid) {
|
||||
let n = (name || '').toUpperCase();
|
||||
if (n.includes('/')) n = n.split('/').pop();
|
||||
for (const d of EF_DECODERS) if (d.name === n) return d;
|
||||
for (const d of EF_DECODERS) if (d.name.toUpperCase() === n) return d;
|
||||
const f = (fid || '').toLowerCase();
|
||||
if (f) for (const d of EF_DECODERS) if (d.fid === f) return d;
|
||||
return null;
|
||||
@@ -12772,9 +12772,13 @@ function netStateFlattenTitle(decoded) {
|
||||
return efFlatten(data).map(r => (r[0] ? r[0] + ': ' : '') + r[1]).join(' · ');
|
||||
}
|
||||
|
||||
function netStatePlmnList(d, withTech) {
|
||||
return (d.plmns || []).map(p => p.mcc + '-' + p.mnc +
|
||||
(withTech && p.access_tech ? ' (' + p.access_tech + ')' : '')).join(', ');
|
||||
// Compact PLMN list for the monitor rows: `max` keeps the row short (the
|
||||
// full decoded list stays in the row's title tooltip).
|
||||
function netStatePlmnList(d, withTech, max) {
|
||||
const list = (d.plmns || []).map(p => p.mcc + '-' + p.mnc +
|
||||
(withTech && p.access_tech ? ' (' + p.access_tech + ')' : ''));
|
||||
if (max && list.length > max) return list.slice(0, max).join(', ') + ' … +' + (list.length - max);
|
||||
return list.join(', ');
|
||||
}
|
||||
|
||||
function netStateSummary(key, entry) {
|
||||
@@ -12797,9 +12801,9 @@ function netStateSummary(key, entry) {
|
||||
if (key === 'imsi') {
|
||||
text = d.imsi || '';
|
||||
} else if (key === 'ehplmn' || key === 'spdi' || key === 'fplmn') {
|
||||
text = netStatePlmnList(d);
|
||||
text = netStatePlmnList(d, false, 3);
|
||||
} else if (key === 'hplmnwact') {
|
||||
text = netStatePlmnList(d, true);
|
||||
text = netStatePlmnList(d, true, 3);
|
||||
} else if (NET_STATE_AREA[key]) {
|
||||
const [area, statusKey] = NET_STATE_AREA[key];
|
||||
const plmn = (d.mcc && d.mnc) ? d.mcc + '-' + d.mnc : null;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v218';
|
||||
const CACHE = 'simple-v219';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -159,6 +159,9 @@ test('decoder registry resolves by name first, then FID; unknown returns null',
|
||||
assert.strictEqual(efFindDecoder('ADF.USIM/EF.LOCI', null).name, 'EF.LOCI');
|
||||
assert.strictEqual(efFindDecoder('EF.SUME', null).fid, '6f54');
|
||||
assert.strictEqual(efFindDecoder('EF.NOPE', 'ABCD'), null);
|
||||
// mixed-case registry names resolve case-insensitively without a FID
|
||||
assert.strictEqual(efFindDecoder('EF.HPLMNwAcT', null).fid, '6f62');
|
||||
assert.strictEqual(efFindDecoder('ef.plmnwact', null).name, 'EF.PLMNwAcT');
|
||||
assert.strictEqual(efFidFromPath('ADF.USIM/6F07'), '6F07');
|
||||
assert.strictEqual(efFidFromPath('MF/7F10/6F3A'), '6F3A');
|
||||
assert.strictEqual(efFidFromPath('ADF.USIM'), null);
|
||||
|
||||
@@ -89,6 +89,30 @@ test('netStateSummary marks rejection statuses', () => {
|
||||
assert.strictEqual(loci.text, 'LAI 246-81/FFFE · PLMN not allowed');
|
||||
});
|
||||
|
||||
test('netStatePlmnList limits long lists with a count', () => {
|
||||
const d = { plmns: [
|
||||
{ mcc: '262', mnc: '01' }, { mcc: '246', mnc: '81' },
|
||||
{ mcc: '204', mnc: '04' }, { mcc: '234', mnc: '15' },
|
||||
] };
|
||||
assert.strictEqual(netStatePlmnList(d, false, 3), '262-01, 246-81, 204-04 … +1');
|
||||
assert.strictEqual(netStatePlmnList(d, false), '262-01, 246-81, 204-04, 234-15');
|
||||
});
|
||||
|
||||
test('netStateSummary abbreviates a long HPLMNwAcT list', () => {
|
||||
setup();
|
||||
decoder({
|
||||
'EF.HPLMNwAcT': { plmns: [
|
||||
{ mcc: '250', mnc: '99', access_tech: 'LTE' },
|
||||
{ mcc: '250', mnc: '32', access_tech: 'LTE' },
|
||||
{ mcc: '250', mnc: '54', access_tech: 'LTE' },
|
||||
{ mcc: '262', mnc: '01', access_tech: 'GSM' },
|
||||
{ mcc: '246', mnc: '81', access_tech: 'GSM' },
|
||||
] },
|
||||
});
|
||||
const s = netStateSummary('hplmnwact', { present: true, kind: 'transparent', data: 'AA' });
|
||||
assert.strictEqual(s.text, '250-99 (LTE), 250-32 (LTE), 250-54 (LTE) … +2');
|
||||
});
|
||||
|
||||
test('netStateRender paints the service badge, location and rows', () => {
|
||||
const els = setup();
|
||||
decoder({ 'EF.IMSI': { imsi: '262011234567890' } });
|
||||
|
||||
Reference in New Issue
Block a user