v3.22.0: the event action uses the Simulator's forms (all modelled events)
The test-script event action edited its fields as a JSON textarea and only accepted the four events events.py builds; every other event needed the raw envelope action with hand-copied hex. It now renders the same EVENT_FORMS form the Phone tab's send dialog uses, for every modelled event (29; no release gating - a script may target another card). - PWA: the field renderer/collector is shared (eventFormFieldsHtml / eventFormValues / eventFormPatternError / eventFormSrc / eventFieldGroupsUpdate); the Phone tab keeps its ids, its release gating and its onchange updaters, the step editor uses test-f-ev- ids, the generic group updater and a live Event data (hex) preview with the one-ENVELOPE check. The event select lists all EVENT_FORMS events (the numeric keys are decimal strings - parseInt(k, 10)); the action stores the collected fields, the built data and the event's source (fixed cfg.src or the cfg.srcField input - the action never carried src before, so MT call / IMS registration / slices could not be network-sourced). The JSON textarea is gone. - engine: the `event` action keeps `fields` validation for the four server-built events (a stray data hex is ignored) and accepts the form-built `data` for every other event - hex-validated and checked against events.EVENT_DATA_MAX (244, the one-ENVELOPE budget; server.py's _EVENT_DATA_MAX now reads it from events.py). The runner builds the four at run time (fresh host clock) and sends the stored data for the rest. - tests: the engine's data path (accepted / missing / malformed / oversized, the four still fields-built), the runner sending the data (fake scc), the editor's collect/prefill/patterns/source (Node), the shared helpers. Docs: test-scripts.md, api.md, help EN/RU. Tests: 838 frontend + 797 Python; sw cache simple-v401.
This commit is contained in:
@@ -30,6 +30,12 @@ EVENT_ACCESS_TECH = 0x0B
|
||||
EVENT_NETWORK_REJECTION = 0x12
|
||||
EVENT_DATA_CONNECTION = 0x1D
|
||||
|
||||
# The single-ENVELOPE budget of the event data (TS 102 223: the inner data is
|
||||
# capped at 252 bytes, 8 of them the event-list + device-identities header;
|
||||
# chained delivery is not implemented). server.py's _EVENT_DATA_MAX and the
|
||||
# PWA's EVENT_INNER_MAX mirror this value.
|
||||
EVENT_DATA_MAX = 244
|
||||
|
||||
# The event code from a script's `event` value (a hex byte, or one of these
|
||||
# names - the PWA's display names and short keywords).
|
||||
EVENT_NAMES = {
|
||||
|
||||
@@ -4885,7 +4885,7 @@ def _verify_adm(scc, app, adm_hex):
|
||||
# Chained (multi-envelope) delivery is not implemented - the slice/S-NSSAI
|
||||
# data must fit one envelope (see the help).
|
||||
_EVENT_INNER_MAX = 252
|
||||
_EVENT_DATA_MAX = _EVENT_INNER_MAX - 8
|
||||
_EVENT_DATA_MAX = events.EVENT_DATA_MAX # = 252 - 8; events.py owns the value
|
||||
|
||||
|
||||
def _send_event_download(scc, event_type, event_data=None, drain=True, src=None,
|
||||
@@ -6544,8 +6544,13 @@ def _test_run_action(server, step, ctx):
|
||||
% (sent, p.get('src') or '82', p['data'] or '(none)', sw or '(none)',
|
||||
(' RESP=%s' % data) if data else ''))
|
||||
elif kind == 'event':
|
||||
# The semantic event download: the builder owns the byte layout.
|
||||
data_hex = events.build(p['event'], p.get('fields') or {})
|
||||
# The semantic event download: the server builds the events it models
|
||||
# (run time, so the data-connection host clock is fresh); every other
|
||||
# event carries the PWA form's built hex (validated at load).
|
||||
if p.get('data') is not None:
|
||||
data_hex = p['data']
|
||||
else:
|
||||
data_hex = events.build(p['event'], p.get('fields') or {})
|
||||
data, sw = _send_event_download(scc, p['event'], bytes.fromhex(data_hex),
|
||||
drain=False, src=p.get('src'), log=False)
|
||||
sent = 'ENVELOPE(Event Download) type=0x%02X (%s) data=%s' % (
|
||||
|
||||
@@ -243,23 +243,34 @@ def _normalise_params(kind, p, command_resolver=None):
|
||||
out['src'] = src
|
||||
return out
|
||||
if kind == 'event':
|
||||
# The semantic event download: the builder owns the field set and the
|
||||
# byte layout (events.py mirrors the PWA's forms for the supported
|
||||
# events; every other event uses the raw `envelope` action).
|
||||
# The semantic event download: the same forms the Simulator uses
|
||||
# (EVENT_FORMS). The four events events.py models are built from
|
||||
# `fields` (validated here, so a script fails before the run starts);
|
||||
# every other event carries the form-built `data` hex the PWA
|
||||
# produces - the single-envelope budget is checked here too.
|
||||
code = events.resolve_event(p.get('event'))
|
||||
if code is None:
|
||||
raise ScriptError('event: a supported event name or a hex byte is '
|
||||
'required (supported: %s)'
|
||||
% ', '.join('%s/0x%02X' % (events.event_name(c), c)
|
||||
for c in sorted(events.BUILDERS)))
|
||||
raise ScriptError('event: a hex byte or a known event name is required')
|
||||
raw = p.get('fields')
|
||||
if raw is not None and not isinstance(raw, dict):
|
||||
raise ScriptError('event: fields must be an object')
|
||||
try:
|
||||
fields = events.normalise(code, raw or {})
|
||||
except events.EventError as e:
|
||||
raise ScriptError('event 0x%02X: %s' % (code, e))
|
||||
out = {'event': code, 'fields': fields}
|
||||
out = {'event': code}
|
||||
if code in events.BUILDERS:
|
||||
try:
|
||||
out['fields'] = events.normalise(code, raw or {})
|
||||
except events.EventError as e:
|
||||
raise ScriptError('event 0x%02X: %s' % (code, e))
|
||||
else:
|
||||
data = re.sub(r'\s', '', str(p.get('data') or '')).upper()
|
||||
if not re.fullmatch(r'(?:[0-9A-F]{2})+', data):
|
||||
raise ScriptError('event 0x%02X: no server-side builder for this '
|
||||
'event - pass the form-built `data` hex (or '
|
||||
'use the raw envelope action)' % code)
|
||||
if len(data) // 2 > events.EVENT_DATA_MAX:
|
||||
raise ScriptError('event 0x%02X: the event data does not fit one '
|
||||
'ENVELOPE (max %d bytes; chained delivery is '
|
||||
'not implemented)' % (code, events.EVENT_DATA_MAX))
|
||||
out['data'] = data
|
||||
src = str(p.get('src') or '').strip().upper()
|
||||
if src:
|
||||
if not re.fullmatch(r'[0-9A-F]{2}', src):
|
||||
|
||||
Reference in New Issue
Block a user