v3.14.0: event form object-set fixes + the slices single-envelope mandate
An audit of every event form against the pinned specs (31.111 7.5.x/9.3, 102 223 8.87, TS 101 220's master tag table) found three missing objects, three malformed/wrong ones, and settled the slice-data scope. - 0x00 MT call: the IMS URI object is cut at 100 bytes and now carries the URI truncated object (8.135, F3 00) when the cut happened. - 0x1F Slices: the form gains the Partial NSSAI object (8.154, F9, rel 18); a single envelope carrying it is marked as the Last Envelope (8.79, F0 00) automatically (7.5.27.2). - 0x06 Card reader status and 0x07 Language: the builders now emit their object tags (A0 01 ..., AD ...) - they emitted bare values before. - 0x18 Incoming IMS data: the IARI tag is F6 (76/F6), not F7 (which is the IMPU list tag - the two-column 9.3 table had been misread). - Single-envelope mandate: the event data is capped at 244 bytes (inner 252; the D6 long-form length and the APDU Lc stay in one byte). The server refuses more with a 400 and the PWA shows the size error before sending. Chained (multi-envelope) delivery, the More Data object and envelope sequences are not implemented - documented in the help and the repo AGENTS. - Tests: event form vectors (URI truncation, the restored tags, partial / Last Envelope, the size helper) and the server guard; the release reference and its PWA/code mirrors gain the three objects and the corrected IARI key. - sw cache simple-v375.
This commit is contained in:
+46
-6
@@ -1877,7 +1877,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.13.4';
|
||||
const SIMPLE_VERSION = '3.14.0';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== Tab switching =====
|
||||
@@ -9136,10 +9136,11 @@ const CAT_RELEASE = {
|
||||
'56/D6 CSG ID': 9,
|
||||
'57/D7 HNB name': 9,
|
||||
'31/B1 IMS URI': 10,
|
||||
'77/F7 IARI': 10,
|
||||
'76/F6 IARI': 10,
|
||||
'77/F7 IMPU list': 11,
|
||||
'78/F8 IMS status code': 11,
|
||||
'7E/FE Media Type': 12,
|
||||
'73/F3 URI truncated': 12,
|
||||
'09/89 PLMN ID': 4,
|
||||
'3F/BF Access technology': 4,
|
||||
'B4 Supported Radio Access Technologies': 13,
|
||||
@@ -9159,6 +9160,8 @@ const CAT_RELEASE = {
|
||||
'77/F7 Allowed slices with S-NSSAI mapping': 18,
|
||||
'D7 Rejected slices with S-NSSAI mapping': 18,
|
||||
'B1 Rejected slices information': 18,
|
||||
'79/F9 Partial NSSAI': 18,
|
||||
'70/F0 Last Envelope': 18,
|
||||
},
|
||||
pli: {
|
||||
'00': 4, '01': 4, '02': 4, '03': 4, '04': 4, '06': 4, '07': 4, '08': 6,
|
||||
@@ -13897,8 +13900,12 @@ const EVENT_FORMS = {
|
||||
d += '88' + (h.length / 2).toString(16).padStart(2, '0') + h;
|
||||
}
|
||||
if (v.ims_uri) {
|
||||
const b = new TextEncoder().encode(v.ims_uri).slice(0, 100);
|
||||
let b = new TextEncoder().encode(v.ims_uri);
|
||||
const truncated = b.length > 100;
|
||||
b = b.slice(0, 100);
|
||||
d += 'B1' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b));
|
||||
// 8.135: the object is included when the URI was truncated
|
||||
if (truncated) d += 'F300';
|
||||
}
|
||||
if (v.media !== '' && v.media !== undefined)
|
||||
d += 'FE01' + ((parseInt(v.media) || 0) & 0xFF).toString(16).padStart(2, '0');
|
||||
@@ -13973,13 +13980,13 @@ const EVENT_FORMS = {
|
||||
{ id: 'status', label: 'Status', type: 'select',
|
||||
opts: [{v:0,l:'Card reader removed'},{v:1,l:'Card reader inserted'}] }
|
||||
],
|
||||
build(v) { return '' + (v.status|0).toString(16).padStart(2,'0'); }
|
||||
build(v) { return 'A001' + ((parseInt(v.status) || 0) & 0xFF).toString(16).padStart(2,'0'); }
|
||||
},
|
||||
0x07: { title: 'Language selection',
|
||||
fields: [
|
||||
{ id: 'lang', label: 'Language code', type: 'text', placeholder: 'ISO 639, e.g. en, ru', pattern: '[a-zA-Z]{2,3}' }
|
||||
],
|
||||
build(v) { const b = new TextEncoder().encode(v.lang||'en'); return b.length.toString(16).padStart(2,'0') + bytesToHex(Array.from(b)); }
|
||||
build(v) { const b = new TextEncoder().encode(v.lang||'en'); return 'AD' + b.length.toString(16).padStart(2,'0') + bytesToHex(Array.from(b)); }
|
||||
},
|
||||
0x08: { title: 'Browser termination',
|
||||
fields: [
|
||||
@@ -14240,7 +14247,7 @@ const EVENT_FORMS = {
|
||||
build(v) {
|
||||
if (!v.iari) return '';
|
||||
const b = new TextEncoder().encode(v.iari).slice(0, 100);
|
||||
return ('F7' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b))).toUpperCase();
|
||||
return ('F6' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b))).toUpperCase();
|
||||
}
|
||||
},
|
||||
0x1C: { title: 'Poll interval negotiation',
|
||||
@@ -14357,6 +14364,7 @@ const EVENT_FORMS = {
|
||||
{ id: 'allowed', label: 'Allowed slices (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{8,80}', placeholder: '4 bytes per S-NSSAI (SST + SD)' },
|
||||
{ id: 'rejected_map', label: 'Rejected slices with S-NSSAI mapping (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 elements + rejection cause', rel: 18 },
|
||||
{ id: 'rejected', label: 'Rejected slices (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 rejected S-NSSAI elements', rel: 18 },
|
||||
{ id: 'partial', label: 'Partial NSSAI (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 partial NSSAI elements', rel: 18 },
|
||||
],
|
||||
build(v) {
|
||||
let d = '';
|
||||
@@ -14377,6 +14385,11 @@ const EVENT_FORMS = {
|
||||
list(v.allowed, 'F8');
|
||||
list(v.rejected_map, 'D7');
|
||||
list(v.rejected, 'B1');
|
||||
list(v.partial, 'F9');
|
||||
// 7.5.27.2: a single envelope carrying the partial NSSAI is the
|
||||
// last one - the chaining marker goes with it (multi-envelope
|
||||
// delivery is not implemented; the data must fit one envelope)
|
||||
if ((v.partial || '').replace(/[^0-9a-fA-F]/g, '')) d += 'F000';
|
||||
return d.toUpperCase();
|
||||
}
|
||||
},
|
||||
@@ -14799,6 +14812,24 @@ function eventFieldsForRelease(cfg, rel) {
|
||||
return { fields: fields, hidden: hidden };
|
||||
}
|
||||
|
||||
// The single-ENVELOPE budget: the inner data (event list + device identities +
|
||||
// the event objects) is wrapped as D6 81 <len> + inner, and the APDU Lc must
|
||||
// stay within one byte (255) - so the inner data is capped at 252 bytes
|
||||
// (8 header bytes + at most 244 bytes of event data). Chained
|
||||
// (multi-envelope) delivery is not implemented: the slice/NSSAI data must fit
|
||||
// one envelope (see the help).
|
||||
const EVENT_INNER_MAX = 252;
|
||||
|
||||
// The ENVELOPE inner size of an event-data hex string (pure).
|
||||
function eventEnvelopeSize(eventDataHex) {
|
||||
return 8 + Math.floor((eventDataHex || '').length / 2);
|
||||
}
|
||||
|
||||
// Whether the event data fits one ENVELOPE (pure).
|
||||
function eventFitsOneEnvelope(eventDataHex) {
|
||||
return eventEnvelopeSize(eventDataHex) <= EVENT_INNER_MAX;
|
||||
}
|
||||
|
||||
function pysimEventSendForm(eventType) {
|
||||
const modal = document.getElementById('event-send-modal');
|
||||
const title = document.getElementById('event-send-title');
|
||||
@@ -14925,6 +14956,13 @@ async function pysimEventSend() {
|
||||
err.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
if (eventData && !eventFitsOneEnvelope(eventData)) {
|
||||
err.textContent = t('The event data does not fit one ENVELOPE') + ' (' +
|
||||
eventEnvelopeSize(eventData) + ' > ' + EVENT_INNER_MAX + ' B). ' +
|
||||
t('Multi-envelope (chained) delivery is not implemented - reduce the slice lists.');
|
||||
err.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
@@ -20738,6 +20776,8 @@ const LANG_RU = {
|
||||
'Event sent.': 'Событие отправлено.',
|
||||
'Event data sent.': 'Данные события отправлены.',
|
||||
'Sending...': 'Отправка...',
|
||||
'The event data does not fit one ENVELOPE': 'Данные события не помещаются в один ENVELOPE',
|
||||
'Multi-envelope (chained) delivery is not implemented - reduce the slice lists.': 'Доставка в несколько ENVELOPE (цепочкой) не реализована — сократите списки срезов.',
|
||||
'Yes': 'Да',
|
||||
'No': 'Нет',
|
||||
'Help': 'Справка',
|
||||
|
||||
Reference in New Issue
Block a user