Commit Graph

8 Commits

Author SHA1 Message Date
catarrh 513f16d9d2 PLI: return the 7-byte location object, bound every proactive chain
release / release (push) Has been cancelled
The PLI answer used the 5-byte form 13 05 <MCC/MNC> <LAC> with LAC 0000
and no Cell ID.  That is the *Network Rejection event download* form
(TS 31.111 8.19.1); a PLI location for GERAN is 7 bytes - MCC/MNC, LAC
and Cell ID.  A card applet that re-reads its location (the Alfa
MultiIMSI applet, after a *LOCI status change) treated it as empty and
re-requested PLI on every STATUS tick.

The default is now 13 07 00F110 0001 0001: PLMN 001/01 (the test
network), LAC 0001, Cell ID 0001.  _build_tr injects it after the
Result; the TR data is pinned by a test.

While verifying a live PLI, the manual /api/status-poll chain turned
out to be unbounded: it looped on the PLI storm while holding
_CARD_LOCK, so the request never returned and every card operation
waited.  _handle_proactive_chain's max_rounds now defaults to
_POLL_CHAIN_MAX (the background poll always passed it; the manual poll,
ENVELOPE, menu and TP chains did not), and the manual poll passes it
explicitly.  A card that re-queues a command can no longer wedge the
card lock from any call site.

Tests: the PLI default + the built TR, the default chain bound and the
manual-poll call pin.  1061 Python + 1069 frontend; sw cache simple-v477.
2026-10-24 01:51:35 +03:00
catarrh 2181ef6bbf v3.37.0: load testing for test scripts and suites
POST /api/test/run takes an optional iterations count (the PWA's
Iterations field): the run repeats, successful iterations are discarded
(steps/log cleared, only load.done and the timing grow), the first error
stops the loop and keeps that iteration's report, a stop ends it after
the current iteration, and a card session change between iterations is
an error.  The SCP80 counter state is shared across iterations, so
packets continue from what the previous run consumed.

The status gains a load block (iteration/done/failed_iteration/
elapsed_ms/avg_ms; disabled for a normal run).  The suite runner was
split into a per-iteration body (_test_suite_execute_once) + the
single-run finaliser, and two load workers own the loops.

Review fixes: a raised runner error finalises as error (no stuck
running state), a suite-internal session change is an error, a stopped
member ends the suite loop, the final status keeps a warning from any
iteration, avg_ms covers the attempted iterations, and each suite
iteration's counters_before is read fresh.

1041 Python + 1069 frontend; simple-v475.
2026-10-23 23:44:42 +03:00
catarrh 8d39904d2c v3.25.3: the card's BIP commands during test runs
The runner's three FETCH/TR sites (the proactive-drain action, the
expectation, the cleanup drain) built every TERMINAL RESPONSE from the
script, so a card-announced OPEN CHANNEL during a run was answered
"performed successfully" without opening a channel - SEND DATA was
dropped and RECEIVE DATA returned nothing (silent data loss, broken
applet state).

_test_step_tr now routes 0x40-0x44 through _handle_bip_command: the
terminal answers for real, so the card's channels work inside a run.
The script keeps an escape hatch - a respond with `raw` TLVs or a
non-ok result wins (the applet error-path tests).

The BIP events flow during a run again for the channels the card
itself opened: _bip_data_available and _bip_flush_channel_events no
longer stop at the run guard (a channel only exists because the card
opened it) and the runner flushes the link events after each TR, so an
applet's own HTTP/BIP conversation completes inside a scripted run.
BIP control stays blocked (409).

Tests: the drain/expect/cleanup routing, the raw and non-ok-result
overrides, the run-time data-available and link events.  883 frontend
+ 873 Python; sw cache simple-v427.
2026-10-11 14:02:29 +03:00
catarrh af00932aca v3.25.1: the run-ownership fix for the proactive auto-handling
A card that re-queues a command immediately after each accepted TR
(live 2026-10-11: the EP BAP card re-issued the same PROVIDE LOCAL
INFORMATION 1100 times) turned the background poll's FETCH/TR chain
into an unbounded loop that held _CARD_LOCK forever - every card
operation, a test run's start included, blocked behind it.

The chain now yields:
- _handle_proactive_chain aborts before the next FETCH while a test run
  owns the card (_TEST_RUNNING) - the pending command stays for the
  script - and takes max_rounds; the background poll passes
  _POLL_CHAIN_MAX = 10, so a chatty card cannot hold the lock.
- _do_status_poll re-arms on its run skip (a heartbeat that never
  fetches); _timer_expired and the BIP Data-available event wait during
  a run; _bip_flush_channel_events stays quiet too.
- _sim_resume_poll() resumes polling at every run end - the two worker
  bodies, their exception paths and the spawn failure.

Also the TR command-details qualifier echo (TS 102 223 6.8.1: the
object shall be identical to the command's): _build_tr,
testscript.build_tr and the interactive _menu_send_response echoed a
hardcoded 00 - harmless for the loop's qual-00 PLI, wrong for any
other qualifier.  The menu pendings now carry cmd_qual (parsed in the
on_fetch handler).

Tests: the chain bound/abort (a fake card re-queueing a command), the
poll/timer guards, the resume wiring, the qualifier echo in both
builders.  883 frontend + 865 Python; sw cache simple-v425.
2026-10-11 12:23:12 +03:00
catarrh 4c3fa64c95 feat: polling on by default and an effective-state toggle (v3.5.10)
- Background STATUS polling is enabled by default, per the spec's idle
  polling rule (TS 102 221 14.6.2): `_POLL_ENABLED = True`, __main__ runs
  `_poll_enable()` regardless of card presence (--poll-interval 0 still
  disables) and `_do_status_poll` keeps ticking while enabled even without
  a session, so a cardless start resumes as soon as a card appears.
- A new card session clears a POLLING OFF from the previous card in
  `_apply_equipped_card` before re-enabling polling.
- The Phone-tab toggle now shows the *effective* state: OFF in amber
  ("card disabled polling (POLLING OFF)") while the card suspended
  proactive polling, back to ON on the next POLL INTERVAL.  The 5 s
  background poll now passes `card_disabled` through (it was dropped, so
  an autonomously received POLLING OFF never reached the UI).
- Tests: poll_ui updated (button OFF while suspended, warning path),
  test_poll +2 (cardless ticking, module default via a subprocess check).
- Help EN/RU, docs/api.md, AGENTS; version 3.5.10; sw cache simple-v263.

573 frontend / 441 Python green.
2026-09-26 13:09:44 +03:00
catarrh 25f001a778 feat: card-driven STATUS polling and the Poll Interval Negotiation event (v3.5.9)
- EVENT_NAMES (server + PWA) corrected against TS 102 223 v18.3.0 8.25:
  0x14 is "Access technology change (multiple)" (not Change of UICC
  Access), 0x19 Profile container, 0x1A Void, 0x1B Secured profile
  container, 0x1C Poll interval negotiation, 0x20-0x22 reserved.  Values
  the CAT spec leaves "Reserved for 3GPP" now carry the concrete TS 31.111
  event name + clause (0x11 (I-)WLAN access status, 0x12 Network
  rejection, 0x15 CSG cell selection, 0x17 IMS registration, 0x18 Incoming
  IMS data, 0x1D Data connection status change, 0x1E CAG cell selection,
  0x1F Slices status change).
- Poll Interval Negotiation (0x1C) in the Phone tab: the form proposes a
  Duration (unit + interval) and the UICC's answer (TS 102 223 8.97
  accepted / rejected / modified + optional Duration) is decoded and
  shown; a "modified" duration becomes the background poll interval.  The
  form appears when the card subscribed to the event, like every other.
- Polling emulation is card-driven: a POLL INTERVAL adopts its Duration
  (minutes/seconds/tenths -> 1..255 s, logged) for the background poll,
  is echoed in the TERMINAL RESPONSE (6.8.4) and clears a POLLING OFF
  suspension; POLLING OFF (6.4.14) suspends proactive polling until a new
  POLL INTERVAL - the manual Send STATUS button and presence detection are
  unaffected.  /api/poll-status and /api/poll-toggle report card_disabled
  (+ a warning when enabling while suspended); the PWA shows it in amber.
- Tests: tests/test_poll.py +7, test_proactive_names.py +3, frontend
  event_forms +2 and poll_ui (4).  Help EN/RU, docs/api.md, AGENTS.

573 frontend / 439 Python green; version 3.5.9; sw cache simple-v262.
2026-09-26 12:56:18 +03:00
catarrh ac18c3e2cf rename OTAMan → SIMple (v2.3.0)
- package pysim_otaman_server → pysim_simple_server; distribution and
  console script pysim-otaman-server → pysim-simple-server
- PWA branding SIMple: title, header, manifest, help EN/RU, GitHub links
- localStorage keys otaman_* → simple_* (clean break, no migration)
- export filenames simple-cards.json / simple-custom-files.json
- service worker cache simple-v189
- README/docs/setup/start scripts updated; no 'otaman' left in the tree
2026-09-19 08:25:20 +03:00
catarrh 895d0b7b36 server: serialize card access, fix poll-interval 0, add --timing
--poll-interval 0 now really disables background STATUS polling (it was
clamped to 1s, and the equip branch force-enabled it anyway). Request
handlers and the poll thread now share _CARD_LOCK so a poll can never
interleave a FETCH/TERMINAL RESPONSE pair — the baseline log showed
AUTO-STATUS chains and duplicate FETCHes inside the equip TP chain.

--timing adds elapsed timestamps, per-reset logging (RESET #n) and
phase durations for startup (init_reader, card_init, pysim_app,
terminal_profile_drain) and equip (onecmd, terminal profile).

AGENTS.md documents the proactive-TR invariant: never fetch without
answering, never fetch twice, one APDU conversation at a time.
2026-09-12 12:25:07 +03:00