ui: stop showing the all-FF record marker as a field; label it in the diff (v3.1.4)

A profile check comparing an empty PNN record against a populated one
showed a cryptic 'Empty: yes' row: the decoders return {empty: true} for
an all-FF record and efFlatten() rendered that marker as a pseudo-field
('empty' prettified to 'Empty', the boolean as 'yes').

- efFlatten() skips the marker (the decoded views already show their own
  translated 'empty' note next to the record).
- efDiffData() fills the cells of a side that decodes to an all-FF record
  with 'Empty (all FF)' (RU: 'Пусто (только FF)') instead of leaving them
  blank; a field merely missing from a non-empty record stays blank.
- tests: efFlatten marker, one-sided and both-sided empty diffs, and the
  PNN case from the report; help EN/RU note the new text.
- sw.js simple-v239.
This commit is contained in:
2026-09-22 22:26:20 +03:00
parent fa600cb45d
commit f72054f61f
7 changed files with 44 additions and 7 deletions
+1 -1
View File
@@ -421,7 +421,7 @@
<li><strong>Атрибуты файла</strong> — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).</li>
<li><strong>Проверить содержимое</strong> (опционально) — <strong>Точное</strong> (точное совпадение hex) или <strong>Маска</strong>, где <code class="font-mono text-sm">?</code> — шаблон на один полубайт (маска без <code class="font-mono text-sm">?</code> — совпадение префикса, например <code class="font-mono text-sm">0891</code> для MCC/MNC из IMSI). Для record-файлов хранится список по записям.</li>
</ul>
<p class="text-sm mb-3"><strong>Проверить карту</strong> выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, <em>тип файла и размер, содержимое</em> или <em>полный FCI</em>); если часть проверок прошла, а часть нет — каждый аспект помечается (<em>тип файла ✓, размер ✗, содержимое ✓</em>), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; расхождения содержимого дополнительно показывают для файлов с декодером декодированное сравнение по полям (только отличающиеся поля, с заголовками колонок «ожидаемое/фактическое» над значениями; если декодированные значения совпадают, вместо таблицы выводится пометка об этом), а для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка <em>совпадающие записи: 1-5, 7-10</em> со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны <em>ожидалось (имя профиля)</em> и <em>фактически (ICCID карты)</em>, а в заголовке отчёта выводится <em>Результаты проверки профиля: &lt;профиль&gt; &rarr; &lt;ICCID карты&gt;</em>. Опция <strong>«Только расхождения»</strong> в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.</p>
<p class="text-sm mb-3"><strong>Проверить карту</strong> выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, <em>тип файла и размер, содержимое</em> или <em>полный FCI</em>); если часть проверок прошла, а часть нет — каждый аспект помечается (<em>тип файла ✓, размер ✗, содержимое ✓</em>), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; расхождения содержимого дополнительно показывают для файлов с декодером декодированное сравнение по полям (только отличающиеся поля, с заголовками колонок «ожидаемое/фактическое» над значениями; если декодированные значения совпадают, вместо таблицы выводится пометка об этом, а сторона, чья запись целиком из FF, показывает в своих ячейках <em>Пусто (только FF)</em>), а для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка <em>совпадающие записи: 1-5, 7-10</em> со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны <em>ожидалось (имя профиля)</em> и <em>фактически (ICCID карты)</em>, а в заголовке отчёта выводится <em>Результаты проверки профиля: &lt;профиль&gt; &rarr; &lt;ICCID карты&gt;</em>. Опция <strong>«Только расхождения»</strong> в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.</p>
<h4 class="font-medium mb-1">Опции сканирования &laquo;Профиль с карты&raquo;</h4>
<p class="text-sm mb-2">Диалог сканирования запрашивает имя профиля и предлагает селектор <strong>&laquo;Проверка FCP/FCI&raquo;</strong> (те же три режима, по умолчанию <strong>Тип файла + размер (FCP)</strong>), применяемый ко всем создаваемым правилам, а также список <strong>&laquo;Игнорировать содержимое файлов&raquo;</strong> (все отмечены по умолчанию, кроме <code class="font-mono text-sm">EF.ARR</code>; флажок в заголовке отмечает или снимает весь список) часто перезаписываемых файлов, содержимое которых пропускается: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Ещё две отмеченные по умолчанию опции <strong>&laquo;Сравнивать первые 4 байта для&raquo;</strong> <code class="font-mono text-sm">EF.IMSI</code> и <code class="font-mono text-sm">EF.ICCID</code> захватывают содержимое этих файлов как маску только первых 4 байт (снимите для точного сравнения). Строка прогресса показывает <em>N / всего файлов</em> с текущим путём файла во время сканирования; при сканировании опции скрываются, а кнопки блокируются. Правила создаются только для файлов, которые реально существуют на карте (возвращён FCP-шаблон); отсутствующие файлы пропускаются. Пользовательские файлы из подвкладки <strong>&laquo;Пользовательские файлы&raquo;</strong> включаются с той же проверкой существования. Тот же диалог и опции использует <strong>&laquo;Профиль из снимка&raquo;</strong>: вместо карты обходятся захваченные файлы выбранного снимка; для файлов, содержимое которых не было захвачено, правило создаётся без проверки содержимого (при последующей проверке профиля они помечаются как непроверяемые).</p>
+1 -1
View File
@@ -421,7 +421,7 @@
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
</ul>
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; content mismatches additionally show, for files with a decoder, a decoded per-field comparison (only the differing fields, with expected/actual column headers above the side-by-side values; when the decoded values match, a note says so instead of the table), and FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled <em>expected (profile name)</em> and <em>actual (card ICCID)</em>, and the results header reads <em>Profile verification results for: &lt;profile&gt; &rarr; &lt;card ICCID&gt;</em>. An <strong>Only mismatches</strong> option in the results header hides all passing files and keeps only failures and errors.</p>
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; content mismatches additionally show, for files with a decoder, a decoded per-field comparison (only the differing fields, with expected/actual column headers above the side-by-side values; when the decoded values match, a note says so instead of the table, and a side whose record is all-FF shows <em>Empty (all FF)</em> in its cells), and FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled <em>expected (profile name)</em> and <em>actual (card ICCID)</em>, and the results header reads <em>Profile verification results for: &lt;profile&gt; &rarr; &lt;card ICCID&gt;</em>. An <strong>Only mismatches</strong> option in the results header hides all passing files and keeps only failures and errors.</p>
<h4 class="font-medium mb-1">&ldquo;Profile from card&rdquo; scan options</h4>
<p class="text-sm mb-2">The scan dialog asks for a profile name and offers a <strong>&ldquo;FCP/FCI check&rdquo;</strong> selector (the same three modes above, default <strong>Filetype + size</strong>) applied to every generated rule, plus an <strong>&ldquo;Ignore contents of files&rdquo;</strong> checklist (all checked by default except <code class="font-mono text-sm">EF.ARR</code>; the header checkbox checks or unchecks the whole list) of frequently-overwritten files whose contents are skipped: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Two further checked-by-default options <strong>&ldquo;Match first 4 bytes for&rdquo;</strong> <code class="font-mono text-sm">EF.IMSI</code> and <code class="font-mono text-sm">EF.ICCID</code> capture those files&rsquo; contents as a mask of only the first 4 bytes (uncheck for exact matching). A progress line shows <em>N / total files</em> with the current file path while scanning; during the scan the options are hidden and the buttons are locked. Rules are created only for files that actually exist on the card (a FCP template is returned); missing files are skipped. Custom files from the <strong>Custom files</strong> sub-tab are included under the same existence check. The same dialog and options are used by <strong>Profile from snapshot</strong>, which walks the selected snapshot&rsquo;s captured files instead of the card; rules for files whose contents were not captured during the scan get no content check (they are reported as unverifiable when the profile is later checked).</p>
+15 -2
View File
@@ -1436,7 +1436,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 = '3.1.3';
const SIMPLE_VERSION = '3.1.4';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -12964,6 +12964,10 @@ function efFlatten(data) {
const rows = [];
if (data === null || data === undefined) return rows;
if (typeof data !== 'object') { rows.push(['', efPrimitive(data)]); return rows; }
// `empty: true` marks an all-FF (unused) record - a marker, not a field.
// The decoded views show their own "empty" note; the comparison shows
// 'Empty (all FF)' for the empty side of a diff.
if (data.empty === true) return rows;
const arrayRows = (label, arr) => {
if (!arr.length) return;
if (typeof arr[0] !== 'object' || arr[0] === null) {
@@ -12997,10 +13001,18 @@ function efDiffData(expData, actData) {
for (const [k, v] of efFlatten(expData)) e[k] = v;
for (const [k, v] of efFlatten(actData)) a[k] = v;
const labels = [...new Set([...Object.keys(e), ...Object.keys(a)])];
// A side that decodes to an all-FF record has no fields at all: say so in
// its cells instead of leaving them blank. A field merely absent from a
// non-empty record still shows as blank.
const expEmpty = expData && expData.empty === true ? t('Empty (all FF)') : '';
const actEmpty = actData && actData.empty === true ? t('Empty (all FF)') : '';
const rows = [];
for (const l of labels) {
const ev = e[l], av = a[l];
if (ev !== av) rows.push([l, ev === undefined ? '' : ev, av === undefined ? '' : av]);
if (ev === av) continue;
rows.push([l,
ev === undefined ? expEmpty : ev,
av === undefined ? actEmpty : av]);
}
return rows;
}
@@ -14036,6 +14048,7 @@ const LANG_RU = {
'not updated': 'не обновлён',
'not present': 'отсутствует',
'empty': 'пусто',
'Empty (all FF)': 'Пусто (только FF)',
'No data': 'Нет данных',
'Home': 'Домашняя сеть',
'Home equivalent': 'Эквивалентная домашней',
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v238';
const CACHE = 'simple-v239';
const URLS = [
'index.html',
'help.html',
+24
View File
@@ -226,6 +226,30 @@ test('efDiffData reports only differing fields; efContentDiff decodes both sides
assert.strictEqual(efContentDiff('EF.NOPE', null, '00', '01'), null);
});
test('efFlatten skips the all-FF empty marker', () => {
assert.deepStrictEqual(efFlatten({ empty: true }), []);
});
test('efDiffData names an all-FF side Empty (all FF)', () => {
assert.deepStrictEqual(efDiffData({ empty: true }, { full: 'Miran' }),
[['Full name', 'Empty (all FF)', 'Miran']]);
assert.deepStrictEqual(efDiffData({ full: 'Miran' }, { empty: true }),
[['Full name', 'Miran', 'Empty (all FF)']]);
assert.deepStrictEqual(efDiffData({ empty: true }, { empty: true }), []);
// a field merely missing from a non-empty record stays blank
assert.deepStrictEqual(efDiffData({ full: 'Miran' }, { full: 'Miran', short: 'Mir' }),
[['Short name', '', 'Mir']]);
});
test('efContentDiff reports an empty PNN record side', () => {
const expected = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const actual = '430887CDB43CEC2687FFFFFFFFFFFFFFFFFFFFFF';
assert.deepStrictEqual(efDecPnn(efBytes(actual)), { full: 'Miran' });
const dd = efContentDiff('EF.PNN', '6fc5', expected, actual);
assert.ok(dd, 'decoded diff expected');
assert.deepStrictEqual(dd.rows, [['Full name', 'Empty (all FF)', 'Miran']]);
});
test('efDataSummary produces one-line summaries', () => {
assert.strictEqual(efDataSummary(efDecImsi(efBytes('082982608200002080'))), 'IMSI 228062800000208');
assert.strictEqual(efDataSummary(efDecIccid(efBytes('988812010000400310f0'))), 'ICCID 8988211000000430010');