diff --git a/frontend/help-ru.html b/frontend/help-ru.html index 9a4655e..0d5ad46 100644 --- a/frontend/help-ru.html +++ b/frontend/help-ru.html @@ -514,7 +514,7 @@

Отправка события использует ENVELOPE(Event Download) по TS 102 223 / TS 131 111. Форма скрывает параметры, появившиеся после действующего релиза CAT — релиза пресета подключённой карты (совпадение по ICCID) или Rel-12, если он не указан, — а примечание под формой называет источник и то, что было скрыто (релиз задаётся на вкладке «Карты»). Каждая форма также показывает точные данные события (hex), которые будут отправлены, обновляя их по мере правки полей — скопируйте их в сырой шаг Envelope тест-скрипта, если у события нет семантической формы. Информация о срезах отправляется в одном ENVELOPE (не более 252 байт внутренних данных) — доставка цепочкой из нескольких ENVELOPE не реализована, поэтому данные должны помещаться в один конверт; конверт с partial NSSAI автоматически помечается как Last Envelope.

8.3 Журнал проактивных команд

-

Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Раскрытие строки показывает декодированную команду: текст и пункты DISPLAY TEXT / SELECT ITEM / SET UP MENU, параметры BIP-каналов, действия TIMER MANAGEMENT, названия квалификаторов PROVIDE LOCAL INFORMATION (все стандартные, включая ESN 07, MEID 0B и Supported RATs 1A) и SEND SHORT MESSAGE — разбирается SMS TPDU (тип, TP-MR, TP-DA, TP-PID, TP-DCS, срок действия, TP-UDL), а пользовательские данные показываются как текст для текстовых кодировок (GSM 7-бит, UCS2, 8-бит) или как secured-пакет (TS 31.115) при TP-PID = 7F (SIM data download); заголовки конкатенации UDH декодируются. Для команд с данными ответа показывается строка Ответ: с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.

+

Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Повторная отправка TERMINAL PROFILE (Отправить или Rescue) начинает новую CAT-сессию, но список сохраняется: её отмечает разделительная строка, а отсчёт времени начинается заново (подключение другой карты список очищает). Раскрытие строки показывает декодированную команду: текст и пункты DISPLAY TEXT / SELECT ITEM / SET UP MENU, параметры BIP-каналов, действия TIMER MANAGEMENT, названия квалификаторов PROVIDE LOCAL INFORMATION (все стандартные, включая ESN 07, MEID 0B и Supported RATs 1A) и SEND SHORT MESSAGE — разбирается SMS TPDU (тип, TP-MR, TP-DA, TP-PID, TP-DCS, срок действия, TP-UDL), а пользовательские данные показываются как текст для текстовых кодировок (GSM 7-бит, UCS2, 8-бит) или как secured-пакет (TS 31.115) при TP-PID = 7F (SIM data download); заголовки конкатенации UDH декодируются. Для команд с данными ответа показывается строка Ответ: с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.

8.4 TERMINAL PROFILE

Блок TERMINAL PROFILE (рядом с «STATUS и опрос») содержит кнопки «Отправить» (повторная отправка, как «Спасение») и «Настроить». В диалоге настройки: селектор пресетов (модели устройств, например профиль BIP-совместимого аппарата из этого проекта), поле hex и форма с флажком на каждый бит профиля по TS 102 223 §5.2 / TS 31.111 §5.2 (байты 1–39; для битов 3GPP используются имена из TS 31.111); каждый байт — вертикальный список битов в порядке спецификации (b1 — младший бит, первая строка таблицы); биты, образующие числовое поле по спецификации (байт 11 — число soft keys, байт 13 — число BIP-каналов, байты 14/15 — высота/ширина экрана, байт 16 — уменьшение ширины в меню, байт 19 — версия TIA/EIA-136, байт 24 — максимум кадров), показываются одним числовым полем на весь диапазон битов, а в подсказке байта 11 отмечено зарезервированное значение 'FF'; блоки размещены фиксированными группами (байты 1–12 в 2 колонки, 13–16 в 4, 17–18 в 2, 19–21 в 3, 22–25 в 2, 26–28 в 3, 29–30 в 2, далее по одному в строке); переключение бита обновляет hex, а правка hex перерисовывает форму — поле hex основное, неизвестные байты и биты сохраняются. Кнопка «Применить» отправляет новое значение на сервер (и далее на карту), сбрасывая STK-сессию как «Спасение»; изменение хранится только в памяти (значение --terminal-profile — стартовое по умолчанию). Правила вывода характеристик устройства из TP (поколение связи, бездисплейные модемы, семейство стандартов) собраны в docs/TERMINAL_PROFILE.md.

diff --git a/frontend/help.html b/frontend/help.html index 17bfa90..5d125d0 100644 --- a/frontend/help.html +++ b/frontend/help.html @@ -513,7 +513,7 @@

Sending an event uses ENVELOPE(Event Download) per TS 102 223 / TS 131 111. The form hides the parameters introduced after the effective CAT release — the equipped card preset's CAT release (matched by ICCID), or Rel-12 when unspecified — and a note under the form names the source and what was hidden (the release is set in the Cards tab). Each form also shows the exact event data (hex) it will send, updated as the fields change — copy it into a raw Envelope test-script step when an event has no semantic form. Slice information is sent in a single ENVELOPE (max 252 bytes of inner data) — chained (multi-envelope) delivery is not implemented, so the data must fit one envelope; an envelope carrying the partial NSSAI is marked as the Last Envelope automatically.

8.3 Proactive command log

-

Chronological list of fetched proactive commands. Each row shows the elapsed time, type code, name, and a decoded qualifier (for commands that have one). Expanding a row shows the decoded command: DISPLAY TEXT / SELECT ITEM / SET UP MENU text and items, BIP channel parameters, TIMER MANAGEMENT actions, PROVIDE LOCAL INFORMATION qualifier names (all standard qualifiers, including ESN 07, MEID 0B and Supported RATs 1A), and SEND SHORT MESSAGE — the SMS TPDU is parsed (type, TP-MR, TP-DA, TP-PID, TP-DCS, validity period, TP-UDL) with the user data shown as text for text codings (GSM 7-bit, UCS2, 8-bit) or as a secured packet (TS 31.115) when TP-PID = 7F (SIM data download); UDH concatenation headers are decoded. Commands with response data show a Response: line with the TERMINAL RESPONSE bytes (boilerplate TLVs stripped); PROVIDE LOCAL INFORMATION responses are decoded using the PLI data dictionary decoders.

+

Chronological list of fetched proactive commands. Each row shows the elapsed time, type code, name, and a decoded qualifier (for commands that have one). Re-sending the TERMINAL PROFILE (Send or Rescue) starts a new CAT session but keeps the list: a boundary line marks it and the elapsed times restart (equipping another card clears the list). Expanding a row shows the decoded command: DISPLAY TEXT / SELECT ITEM / SET UP MENU text and items, BIP channel parameters, TIMER MANAGEMENT actions, PROVIDE LOCAL INFORMATION qualifier names (all standard qualifiers, including ESN 07, MEID 0B and Supported RATs 1A), and SEND SHORT MESSAGE — the SMS TPDU is parsed (type, TP-MR, TP-DA, TP-PID, TP-DCS, validity period, TP-UDL) with the user data shown as text for text codings (GSM 7-bit, UCS2, 8-bit) or as a secured packet (TS 31.115) when TP-PID = 7F (SIM data download); UDH concatenation headers are decoded. Commands with response data show a Response: line with the TERMINAL RESPONSE bytes (boilerplate TLVs stripped); PROVIDE LOCAL INFORMATION responses are decoded using the PLI data dictionary decoders.

8.4 TERMINAL PROFILE

The TERMINAL PROFILE block (next to STATUS and Polling) offers Send (re-sends it, like Rescue) and Configure. The Configure dialog has a preset selector (device models, e.g. this project's BIP-capable handset profile), a hex field and a form with one checkbox per profile bit decoded per TS 102 223 §5.2 / TS 31.111 §5.2 (bytes 1–39, 3GPP-defined bits use the TS 31.111 names), each byte is a vertical list of its bits in spec order (b1 is the least significant bit and the first table row); bits that form a numeric value field per the spec (byte 11 soft keys, byte 13 BIP channels, byte 14/15 screen height/width, byte 16 menu width reduction, byte 19 TIA/EIA-136 version, byte 24 max frames) are shown as a single number input for the whole bit range, with the byte 11 'FF' reserved value noted in the hint; placed in fixed column groups (bytes 1-12 in 2 columns, 13-16 in 4, 17-18 in 2, 19-21 in 3, 22-25 in 2, 26-28 in 3, 29-30 in 2, later bytes one per row); toggling a bit updates the hex and editing the hex re-renders the form — the hex field is authoritative and unknown bytes/bits are preserved. Apply sends the new value to the server (and on to the card), resetting the STK session like Rescue; the change is in-memory only (the --terminal-profile CLI value is the startup default). The rules for inferring device characteristics from a TP (radio generation, headless modems, standards family) are collected in docs/TERMINAL_PROFILE.md.

diff --git a/frontend/index.html b/frontend/index.html index cdfcbcf..76743c1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1905,7 +1905,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.23.6'; +const SIMPLE_VERSION = '3.23.7'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== view history (the browser Back/Forward buttons) ===== @@ -16859,6 +16859,13 @@ async function pysimProactiveLogRender() { } let html = ''; log.forEach((e, i) => { + // a session boundary marker (a TERMINAL PROFILE re-send keeps the + // history): everything above is the previous CAT session + if (e.marker) { + html += '
' + + '\u2014 ' + esc(t('TERMINAL PROFILE re-sent \u2014 new CAT session')) + ' \u2014
'; + return; + } const name = CMD_NAMES[e.type_hex] || (e.type_name && e.type_name !== 'UNKNOWN' ? e.type_name : 'Cmd 0x' + e.type_hex.toUpperCase()); let qual = ''; @@ -23058,6 +23065,7 @@ const LANG_RU = { 'Text': 'Текст', 'id': 'id', 'text': 'текст', + 'TERMINAL PROFILE re-sent \u2014 new CAT session': 'TERMINAL PROFILE отправлен заново \u2014 новая CAT-сессия', 'manual input': 'ручной ввод', 'Additional information (optional hex byte)': 'Дополнительная информация (необязательный hex-байт)', 'TERMINAL RESPONSE result must be two hex digits': 'Результат TERMINAL RESPONSE — ровно два hex-символа', diff --git a/frontend/sw.js b/frontend/sw.js index 851169d..65cf044 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v419'; +const CACHE = 'simple-v420'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/sim.test.js b/frontend/tests/sim.test.js index 8384967..27d2bf8 100644 --- a/frontend/tests/sim.test.js +++ b/frontend/tests/sim.test.js @@ -226,3 +226,14 @@ test('SELECT by FID SIM (CLA A0): no Le anywhere', () => { const apdu = setup('sim', { cmd: 'select', selMethod: 'fid', fid: '6FC5' }); assert.strictEqual(apdu, 'A0A40000026FC5'); }); + +test('the proactive log renders a session boundary marker', () => { + // v3.23.7: a TERMINAL PROFILE re-send keeps the log history and appends a + // marker entry - the renderer draws it as a boundary line, not a command + // row (and the marker branch must run before the normal row builder) + const src = extractFunc(html, 'pysimProactiveLogRender'); + assert.ok(src.includes('if (e.marker)'), src.slice(0, 300)); + assert.ok(src.includes("t('TERMINAL PROFILE re-sent \\u2014 new CAT session')"), src); + assert.ok(src.indexOf('if (e.marker)') < src.indexOf('CMD_NAMES[e.type_hex]'), + 'the marker branch must run before the normal row'); +}); diff --git a/pyproject.toml b/pyproject.toml index 5c6741c..495d698 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.23.6" +version = "3.23.7" description = "HTTP REST server wrapping pysim for the SIMple PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index 0e1ce29..6daa482 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE from osmocom.utils import rpad -VERSION = '3.23.6' +VERSION = '3.23.7' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE @@ -4880,6 +4880,22 @@ def _reset_proactive_log(): _PROACTIVE_SESSION_START = time.time() +def _mark_proactive_session(reason='terminal-profile'): + """A new CAT session without losing the history: restart the elapsed + timer and append a boundary marker entry. Used by the TERMINAL PROFILE + re-send (/api/terminal-profile and /api/rescue) - a card change still + clears the log (_reset_proactive_log).""" + global _PROACTIVE_SESSION_START, _PROACTIVE_ENTRY_ID + _PROACTIVE_SESSION_START = time.time() + _PROACTIVE_ENTRY_ID += 1 + _PROACTIVE_LOG.append({ + 'id': _PROACTIVE_ENTRY_ID, + 'marker': reason, + 'elapsed': 0.0, + 'bytes': 0, + }) + + def _send_status(scc): """STATUS (F2) with correct P3 per card type: SIM=0x23, UICC=0x00. P2=0C mirrors phone behavior - no FCP of the selected DF returned @@ -5657,12 +5673,14 @@ def _validate_tp_hex(hex_str): def _resend_terminal_profile(server, scc): """Re-send the current TERMINAL PROFILE and reset the STK session state. Shared by /api/rescue and the runtime profile update; the value lives in - server.terminal_profile (CLI default, overridable at runtime).""" + server.terminal_profile (CLI default, overridable at runtime). The + proactive log history is kept - a boundary marker entry records the new + session (v3.23.7).""" server.stk_pending = None server.menu_active = False _cancel_menu_timeout() server.event_list = None - _reset_proactive_log() + _mark_proactive_session('terminal-profile') sm, el = _send_terminal_profile(scc, server.terminal_profile) server.sim_menu = sm server.event_list = el diff --git a/tests/test_ota_helpers.py b/tests/test_ota_helpers.py index 1308c0e..f55ac80 100644 --- a/tests/test_ota_helpers.py +++ b/tests/test_ota_helpers.py @@ -1066,6 +1066,46 @@ class TestProactiveDecode(unittest.TestCase): _record_tr(entry, bytes.fromhex('810303260082028181')) self.assertNotIn('tr_result', entry) + def test_the_session_marker_keeps_the_proactive_history(self): + # v3.23.7: a TERMINAL PROFILE re-send keeps the log and appends a + # boundary marker; only the elapsed timer restarts + import time as _time + import pysim_simple_server.server as srv + srv._PROACTIVE_LOG.clear() + srv._init_proactive_session() + srv._log_proactive(0x21, b'\x00') + before = len(srv._PROACTIVE_LOG) + t0 = srv._PROACTIVE_SESSION_START + _time.sleep(0.01) + srv._mark_proactive_session('terminal-profile') + self.assertEqual(len(srv._PROACTIVE_LOG), before + 1) + marker = srv._PROACTIVE_LOG[-1] + self.assertEqual(marker['marker'], 'terminal-profile') + self.assertEqual(marker['elapsed'], 0.0) + self.assertGreater(srv._PROACTIVE_SESSION_START, t0) + # the new session's entries restart their elapsed values + srv._log_proactive(0x21, b'\x00') + self.assertLess(srv._PROACTIVE_LOG[-1]['elapsed'], 1.0) + srv._reset_proactive_log() + + def test_the_log_reset_still_clears(self): + # equip / disconnect keep the full reset (a card change) + import pysim_simple_server.server as srv + srv._PROACTIVE_LOG.clear() + srv._init_proactive_session() + srv._log_proactive(0x21, b'\x00') + srv._reset_proactive_log() + self.assertEqual(srv._PROACTIVE_LOG, []) + + def test_the_terminal_profile_resend_marks_the_session(self): + # the Send button / Rescue share _resend_terminal_profile: it must + # mark (not clear) - a source pin, since calling it needs a card + import inspect + import pysim_simple_server.server as srv + src = inspect.getsource(srv._resend_terminal_profile) + self.assertIn("_mark_proactive_session('terminal-profile')", src) + self.assertNotIn('_reset_proactive_log()', src) + class TestEventDownload(unittest.TestCase): """ENVELOPE (EVENT DOWNLOAD) assembly, TS 102 223 7.5.11."""