feat: the remaining spec-defined event forms (data/display/search/frames, IMS/CAG/slices)

Group 1 (TS 102 223): Data available (Channel status B8 + channel data length
B7), Display parameters changed (rows/columns + sizing/fonts/effects, C0),
Network search mode change (manual/automatic, E5), Browsing status (browser
error code, E4) and Frames information change (default frame + per-frame info,
E7).
Group 2 (TS 31.111): IMS registration (IMPU list as 80-tagged URIs + IMS status
code), Incoming IMS data (IARI), CAG cell selection (status + mechanism, access
technology, CAG information list) and Slices status change (allowed / rejected
/ served / partially, access technology, served S-NSSAIs) - all with source
network.

Still unimplemented by design: 0x0D Local connection, 0x16 Contactless state
request (3GPP: not required) and the eCAT containers 0x19/0x1B; 0x04/0x05/0x13/
0x1A are data-free, where the one-click send is spec-correct.

Tests: the new build vectors.  Help EN/RU, both AGENTS files; sw simple-v349.
This commit is contained in:
2026-10-01 22:30:19 +03:00
parent a26a2b6648
commit 7061f42781
5 changed files with 186 additions and 7 deletions
+2
View File
@@ -506,6 +506,8 @@
<li><strong>Network Rejection</strong> — полная адаптивная форма: тип регистрации (LU / GPRS / EPS / 5GS), поля местоположения (MCC, MNC, LAC, RAC, TAC), технология доступа и единый выпадающий список из 53 кодов причин (EMM, GMM, 5GMM и LU)</li> <li><strong>Network Rejection</strong> — полная адаптивная форма: тип регистрации (LU / GPRS / EPS / 5GS), поля местоположения (MCC, MNC, LAC, RAC, TAC), технология доступа и единый выпадающий список из 53 кодов причин (EMM, GMM, 5GMM и LU)</li>
<li><strong>События звонка</strong> &mdash; MT call (transaction identifier, номер вызывающего + TON &rarr; Address, subaddress, IMS URI, media type; источник <em>network</em>), Call connected и Call disconnected (transaction identifier, источник near/far, media type и причина &mdash; отсутствует, нулевая форма radio link timeout или свои байты)</li> <li><strong>События звонка</strong> &mdash; MT call (transaction identifier, номер вызывающего + TON &rarr; Address, subaddress, IMS URI, media type; источник <em>network</em>), Call connected и Call disconnected (transaction identifier, источник near/far, media type и причина &mdash; отсутствует, нулевая форма radio link timeout или свои байты)</li>
<li><strong>Browser termination</strong> (причина user / error), <strong>(I-)WLAN access status</strong> (нет покрытия / покрытие / соединение) и <strong>CSG cell selection</strong> (статус + механизм выбора, access technology, CSG ID, HNB name, PLMN ID; источник <em>network</em>)</li> <li><strong>Browser termination</strong> (причина user / error), <strong>(I-)WLAN access status</strong> (нет покрытия / покрытие / соединение) и <strong>CSG cell selection</strong> (статус + механизм выбора, access technology, CSG ID, HNB name, PLMN ID; источник <em>network</em>)</li>
<li><strong>Data available</strong> (канал + состояние звена + инфо, и длина данных канала, теги <code class="font-mono text-sm">B8</code>/<code class="font-mono text-sm">B7</code>), <strong>Display parameters changed</strong> (строки/столбцы + флаги sizing/fonts/effects, тег <code class="font-mono text-sm">C0</code>), <strong>Network search mode change</strong> (manual/automatic, тег <code class="font-mono text-sm">E5</code>), <strong>Browsing status</strong> (код ошибки браузера, тег <code class="font-mono text-sm">E4</code>) и <strong>Frames information change</strong> (фрейм по умолчанию + инфо по фреймам, тег <code class="font-mono text-sm">E7</code>)</li>
<li><strong>IMS registration</strong> (IMPU URI + IMS status code) и <strong>Incoming IMS data</strong> (IARI) &mdash; источник <em>network</em>; <strong>CAG cell selection</strong> (статус + механизм, access technology, CAG information list) и <strong>Slices status change</strong> (allowed / rejected / served / partially, access technology, served S-NSSAIs) &mdash; источник <em>network</em></li>
<li><strong>Data Connection Status Change</strong> (0x1D) &mdash; статус (successful / rejected / dropped-deactivated), тип соединения (PDP / PDN / PDU), необязательная причина (E/5G)SM, transaction identifier и location status, а также необязательные date-time, access technology, APN, тип PDP/PDN/PDU и location information (MCC/MNC + LAC + Cell ID) &mdash; TS 131 111 &sect;7.5.25</li> <li><strong>Data Connection Status Change</strong> (0x1D) &mdash; статус (successful / rejected / dropped-deactivated), тип соединения (PDP / PDN / PDU), необязательная причина (E/5G)SM, transaction identifier и location status, а также необязательные date-time, access technology, APN, тип PDP/PDN/PDU и location information (MCC/MNC + LAC + Cell ID) &mdash; TS 131 111 &sect;7.5.25</li>
<li><strong>Poll Interval Negotiation</strong> (0x1C) — предлагает интервал опроса в виде Duration (единица + значение); после отправки показывается ответ UICC (принято / отклонено / изменено + длительность), а значение <em>изменено</em> становится интервалом фонового опроса (TS 102 223 &sect;7.5.22). Форма появляется, когда карта подписана на это событие.</li> <li><strong>Poll Interval Negotiation</strong> (0x1C) — предлагает интервал опроса в виде Duration (единица + значение); после отправки показывается ответ UICC (принято / отклонено / изменено + длительность), а значение <em>изменено</em> становится интервалом фонового опроса (TS 102 223 &sect;7.5.22). Форма появляется, когда карта подписана на это событие.</li>
</ul> </ul>
+2
View File
@@ -505,6 +505,8 @@
<li><strong>Network Rejection</strong> — full adaptive form: registration type (LU / GPRS / EPS / 5GS), location fields (MCC, MNC, LAC, RAC, TAC), access technology, and a 53-cause unified rejection cause dropdown covering EMM, GMM, 5GMM and LU causes</li> <li><strong>Network Rejection</strong> — full adaptive form: registration type (LU / GPRS / EPS / 5GS), location fields (MCC, MNC, LAC, RAC, TAC), access technology, and a 53-cause unified rejection cause dropdown covering EMM, GMM, 5GMM and LU causes</li>
<li><strong>Call events</strong> &mdash; MT call (transaction identifier, calling party number + TON &rarr; Address, subaddress, IMS URI, media type; source <em>network</em>), Call connected and Call disconnected (transaction identifier, near/far-end source, media type, and a cause &mdash; absent, the zero-length radio-link-timeout form, or custom bytes)</li> <li><strong>Call events</strong> &mdash; MT call (transaction identifier, calling party number + TON &rarr; Address, subaddress, IMS URI, media type; source <em>network</em>), Call connected and Call disconnected (transaction identifier, near/far-end source, media type, and a cause &mdash; absent, the zero-length radio-link-timeout form, or custom bytes)</li>
<li><strong>Browser termination</strong> (user / error cause), <strong>(I-)WLAN access status</strong> (no coverage / coverage / connection on-going) and <strong>CSG cell selection</strong> (status + selection mechanism, access technology, CSG ID, HNB name, PLMN ID; source <em>network</em>)</li> <li><strong>Browser termination</strong> (user / error cause), <strong>(I-)WLAN access status</strong> (no coverage / coverage / connection on-going) and <strong>CSG cell selection</strong> (status + selection mechanism, access technology, CSG ID, HNB name, PLMN ID; source <em>network</em>)</li>
<li><strong>Data available</strong> (channel + link state + channel info, and the channel data length, tags <code class="font-mono text-sm">B8</code>/<code class="font-mono text-sm">B7</code>), <strong>Display parameters changed</strong> (rows/columns + sizing/fonts/effects flags, tag <code class="font-mono text-sm">C0</code>), <strong>Network search mode change</strong> (manual/automatic, tag <code class="font-mono text-sm">E5</code>), <strong>Browsing status</strong> (browser error code, tag <code class="font-mono text-sm">E4</code>) and <strong>Frames information change</strong> (default frame + per-frame info, tag <code class="font-mono text-sm">E7</code>)</li>
<li><strong>IMS registration</strong> (IMPU URIs + IMS status code) and <strong>Incoming IMS data</strong> (IARI) &mdash; source <em>network</em>; <strong>CAG cell selection</strong> (status + mechanism, access technology, CAG information list) and <strong>Slices status change</strong> (allowed / rejected / served / partially status, access technology, served S-NSSAIs) &mdash; source <em>network</em></li>
<li><strong>Data Connection Status Change</strong> (0x1D) &mdash; status (successful / rejected / dropped-deactivated), connection type (PDP / PDN / PDU), optional (E/5G)SM cause, transaction identifier and location status, plus optional date-time, access technology, APN, PDP/PDN/PDU type and location information (MCC/MNC + LAC + Cell ID) &mdash; TS 131 111 &sect;7.5.25</li> <li><strong>Data Connection Status Change</strong> (0x1D) &mdash; status (successful / rejected / dropped-deactivated), connection type (PDP / PDN / PDU), optional (E/5G)SM cause, transaction identifier and location status, plus optional date-time, access technology, APN, PDP/PDN/PDU type and location information (MCC/MNC + LAC + Cell ID) &mdash; TS 131 111 &sect;7.5.25</li>
<li><strong>Poll Interval Negotiation</strong> (0x1C) — proposes a poll interval as a Duration (unit + value); after sending, the UICC's answer is shown (accepted / rejected / modified + duration), and a <em>modified</em> duration becomes the background poll interval (TS 102 223 &sect;7.5.22). The form appears when the card subscribed to this event.</li> <li><strong>Poll Interval Negotiation</strong> (0x1C) — proposes a poll interval as a Duration (unit + value); after sending, the UICC's answer is shown (accepted / rejected / modified + duration), and a <em>modified</em> duration becomes the background poll interval (TS 102 223 &sect;7.5.22). The form appears when the card subscribed to this event.</li>
</ul> </ul>
+151 -6
View File
@@ -13170,7 +13170,28 @@ const EVENT_FORMS = {
], ],
build(v) { return 'B401' + ((parseInt(v.cause) || 0) & 0xFF).toString(16).padStart(2, '0'); } build(v) { return 'B401' + ((parseInt(v.cause) || 0) & 0xFF).toString(16).padStart(2, '0'); }
}, },
0x09: null, 0x09: { title: 'Data available',
fields: [
{ id: 'channel', label: 'Channel', type: 'select',
opts: [{v:0,l:'0 \u2014 no channel available'},
{v:1,l:'Channel 1'},{v:2,l:'Channel 2'},{v:3,l:'Channel 3'},
{v:4,l:'Channel 4'},{v:5,l:'Channel 5'},{v:6,l:'Channel 6'},{v:7,l:'Channel 7'}] },
{ id: 'state', label: 'Link state', type: 'select',
opts: [{v:0x00,l:'Link not established / TCP closed'},
{v:0x40,l:'TCP in LISTEN state (UICC server mode)'},
{v:0x80,l:'Link established / TCP established'}] },
{ id: 'info', label: 'Channel info', type: 'select',
opts: [{v:0x00,l:'No further info can be given'},
{v:0x05,l:'Link dropped (network failure or user cancellation)'}] },
{ id: 'len', label: 'Channel data length', type: 'text', pattern: '[0-9A-Fa-f]{2}', placeholder: 'hex byte, FF = more than 255' },
],
build(v) {
const st = (parseInt(v.state) || 0) | (parseInt(v.channel) || 0);
let d = 'B802' + st.toString(16).padStart(2, '0') + ((parseInt(v.info) || 0) & 0xFF).toString(16).padStart(2, '0');
d += 'B701' + (v.len || 'FF').replace(/[^0-9a-fA-F]/g, '').padStart(2, '0').slice(0, 2);
return d.toUpperCase();
}
},
0x0A: { title: 'Channel status', 0x0A: { title: 'Channel status',
fields: [ fields: [
{ id: 'channel', label: 'Channel', type: 'select', { id: 'channel', label: 'Channel', type: 'select',
@@ -13197,11 +13218,53 @@ const EVENT_FORMS = {
], ],
build(v) { return 'BF01' + (v.tech|0).toString(16).padStart(2,'0'); } build(v) { return 'BF01' + (v.tech|0).toString(16).padStart(2,'0'); }
}, },
0x0C: null, 0x0C: { title: 'Display parameters changed',
fields: [
{ id: 'rows', label: 'Characters down (screen height)', type: 'number', min: '0', max: '31', placeholder: '0\u201331' },
{ id: 'cols', label: 'Characters across (screen width)', type: 'number', min: '0', max: '31', placeholder: '0\u201331' },
{ id: 'sizing', label: 'Screen sizing parameters supported', type: 'select',
opts: [{v:0,l:'No'},{v:1,l:'Yes'}] },
{ id: 'fonts', label: 'Variable size fonts supported', type: 'select',
opts: [{v:0,l:'No'},{v:1,l:'Yes'}] },
{ id: 'effects', label: 'Screen effects (hex byte)', type: 'text', pattern: '[0-9A-Fa-f]{2}', placeholder: 'b1 resizable, b2 wrapping, b3 scrolling, b6 menu width' },
],
build(v) {
const b1 = ((parseInt(v.rows) || 0) & 0x1F) | (parseInt(v.sizing) ? 0x80 : 0);
const b2 = ((parseInt(v.cols) || 0) & 0x1F) | (parseInt(v.fonts) ? 0x40 : 0);
const b3 = parseInt((v.effects || '00').replace(/[^0-9a-fA-F]/g, '').padStart(2, '0').slice(0, 2), 16) || 0;
return ('C003' + b1.toString(16).padStart(2, '0') + b2.toString(16).padStart(2, '0') +
b3.toString(16).padStart(2, '0')).toUpperCase();
}
},
0x0D: null, 0x0D: null,
0x0E: null, 0x0E: { title: 'Network search mode change',
0x0F: null, fields: [
0x10: null, { id: 'mode', label: 'Network search mode', type: 'select',
opts: [{v:0,l:'00 \u2014 manual'},{v:1,l:'01 \u2014 automatic'}] },
],
build(v) { return 'E501' + ((parseInt(v.mode) || 0) & 0xFF).toString(16).padStart(2, '0'); }
},
0x0F: { title: 'Browsing status',
fields: [
{ id: 'status', label: 'Browsing status (hex bytes)', type: 'text', pattern: '[0-9A-Fa-f]{2,40}', placeholder: 'browser error code, e.g. 00' },
],
build(v) {
const h = (v.status || '00').replace(/[^0-9a-fA-F]/g, '');
return ('E4' + (h.length / 2).toString(16).padStart(2, '0') + h).toUpperCase();
}
},
0x10: { title: 'Frames information change',
fields: [
{ id: 'frame', label: 'Default frame ID', type: 'text', pattern: '[0-9A-Fa-f]{2}', placeholder: '01\u20130F, 00 = no frames' },
{ id: 'list', label: 'Frame information list (hex, optional)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: '2 bytes per frame: down / across' },
],
build(v) {
const id = (v.frame || '00').replace(/[^0-9a-fA-F]/g, '').padStart(2, '0').slice(0, 2);
const h = (v.list || '').replace(/[^0-9a-fA-F]/g, '');
const value = id + h;
return ('E7' + (value.length / 2).toString(16).padStart(2, '0') + value).toUpperCase();
}
},
0x11: { title: 'I-WLAN Access Status', 0x11: { title: 'I-WLAN Access Status',
fields: [ fields: [
{ id: 'status', label: 'Access status', type: 'select', { id: 'status', label: 'Access status', type: 'select',
@@ -13297,7 +13360,39 @@ const EVENT_FORMS = {
} }
}, },
0x16: null, 0x16: null,
0x17: null, 0x17: { title: 'IMS registration', src: '83',
fields: [
{ id: 'impu', label: 'IMPU URI(s) (optional)', type: 'text', placeholder: 'one or more, comma-separated' },
{ id: 'code', label: 'IMS status code (optional)', type: 'text', pattern: '\\d{3}', placeholder: 'e.g. 200' },
],
build(v) {
let d = '';
const uris = (v.impu || '').split(/[\s,;]+/).filter(Boolean);
if (uris.length) {
let inner = '';
uris.forEach(u => {
const b = new TextEncoder().encode(u).slice(0, 100);
inner += '80' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b));
});
d += 'F7' + (inner.length / 2).toString(16).padStart(2, '0') + inner;
}
if (v.code) {
const b = new TextEncoder().encode(v.code);
d += 'F8' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b));
}
return d.toUpperCase();
}
},
0x18: { title: 'Incoming IMS data', src: '83',
fields: [
{ id: 'iari', label: 'IARI (optional)', type: 'text', placeholder: 'e.g. urn:urn-7:3gpp-service.ims.icsi.mmtel' },
],
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();
}
},
0x1C: { title: 'Poll interval negotiation', 0x1C: { title: 'Poll interval negotiation',
fields: [ fields: [
{ id: 'unit', label: 'Time unit', type: 'select', { id: 'unit', label: 'Time unit', type: 'select',
@@ -13364,6 +13459,56 @@ const EVENT_FORMS = {
return d.toUpperCase(); return d.toUpperCase();
} }
}, },
0x1E: { title: 'CAG cell selection', src: '83',
fields: [
{ id: 'status', label: 'CAG cell selection status', type: 'select',
opts: [{v:0,l:'00 \u2014 not camped on a CAG cell'},
{v:1,l:'01 \u2014 camped on a CAG cell'}] },
{ id: 'mech', label: 'Selection mechanism', type: 'select',
opts: [{v:'',l:'\u2014 none \u2014'},{v:'manual',l:'manual CAG selection'},
{v:'other',l:'another CAG selection type'}] },
{ id: 'tech', label: 'Access Technology (optional)', type: 'select',
opts: [{v:'',l:'\u2014 not present \u2014'}].concat(ACCESSTECH) },
{ id: 'list', label: 'CAG information list (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'CAG identity list' },
],
build(v) {
let d = '';
if (v.tech !== '' && v.tech !== undefined)
d += 'BF01' + ((parseInt(v.tech) || 0) & 0xFF).toString(16).padStart(2, '0');
const mech = v.mech === 'manual' ? 0x40 : (v.mech === 'other' ? 0x80 : 0x00);
d += 'D502' + ((parseInt(v.status) || 0) & 0xFF).toString(16).padStart(2, '0') +
(0x01 | mech).toString(16).padStart(2, '0');
if (v.list) {
const h = v.list.replace(/[^0-9a-fA-F]/g, '');
d += 'D6' + (h.length / 2).toString(16).padStart(2, '0') + h;
}
return d.toUpperCase();
}
},
0x1F: { title: 'Slices status change', src: '83',
fields: [
{ id: 'status', label: 'Slices status', type: 'select',
opts: [{v:0x01,l:'01 \u2014 allowed'},{v:0x02,l:'02 \u2014 rejected'},
{v:0x04,l:'04 \u2014 served'},{v:0x81,l:'81 \u2014 partially allowed'},
{v:0x82,l:'82 \u2014 partially rejected'}] },
{ id: 'tech', label: 'Access Technology (optional)', type: 'select',
opts: [{v:'',l:'\u2014 not present \u2014'}].concat(ACCESSTECH) },
{ id: 'served', label: 'Served S-NSSAIs (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{8,80}', placeholder: '4 bytes per S-NSSAI (SST + SD)' },
],
build(v) {
let d = '';
if (v.tech !== '' && v.tech !== undefined)
d += 'BF01' + ((parseInt(v.tech) || 0) & 0xFF).toString(16).padStart(2, '0');
d += 'D501' + ((parseInt(v.status) || 1) & 0xFF).toString(16).padStart(2, '0');
const h = (v.served || '').replace(/[^0-9a-fA-F]/g, '');
if (h) {
const n = Math.floor(h.length / 8);
d += 'D6' + (1 + n * 4).toString(16).padStart(2, '0') +
n.toString(16).padStart(2, '0') + h.slice(0, n * 8);
}
return d.toUpperCase();
}
},
}; };
const PLI_QUALIFIERS = [ const PLI_QUALIFIERS = [
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v348'; const CACHE = 'simple-v349';
const URLS = [ const URLS = [
'index.html', 'index.html',
'help.html', 'help.html',
+30
View File
@@ -186,3 +186,33 @@ test('the browser, WLAN and CSG events build their object sets', () => {
test('every event type has a parameter form now', () => { test('every event type has a parameter form now', () => {
assert.ok(!html.includes("note: 'not_yet'"), 'no event may stay unimplemented'); assert.ok(!html.includes("note: 'not_yet'"), 'no event may stay unimplemented');
}); });
test('the data/display/search/frames events build their object sets', () => {
// Data available: Channel status (B8) + Channel data length (B7)
assert.strictEqual(EVENT_FORMS[0x09].build({ channel: '2', state: '128', info: '0', len: 'FF' }),
'B8028200B701FF');
// Display parameters: rows/cols + sizing/fonts/effects bytes
assert.strictEqual(EVENT_FORMS[0x0C].build({ rows: '4', cols: '16', sizing: '1', fonts: '0', effects: '00' }),
'C003841000');
assert.strictEqual(EVENT_FORMS[0x0E].build({ mode: '1' }), 'E50101');
assert.strictEqual(EVENT_FORMS[0x0F].build({ status: '12' }), 'E40112');
assert.strictEqual(EVENT_FORMS[0x0F].build({ status: '' }), 'E40100');
assert.strictEqual(EVENT_FORMS[0x10].build({ frame: '01', list: '0410' }), 'E703010410');
});
test('the IMS, CAG and slices events build their object sets', () => {
// IMS registration: source network, IMPU list (80-tagged URIs) + status code
assert.strictEqual(EVENT_FORMS[0x17].src, '83');
assert.strictEqual(EVENT_FORMS[0x17].build({ impu: 'sip:a@b', code: '200' }),
'F70980077369703A614062F803323030');
assert.strictEqual(EVENT_FORMS[0x18].src, '83');
assert.strictEqual(EVENT_FORMS[0x18].build({ iari: 'a' }), 'F70161');
// CAG cell selection: tech + status/mechanism + information list
assert.strictEqual(EVENT_FORMS[0x1E].src, '83');
assert.strictEqual(EVENT_FORMS[0x1E].build({ status: '1', mech: 'manual', tech: '8', list: '00000001' }),
'BF0108D5020141D60400000001');
// Slices status change: tech + status + served S-NSSAIs (count + 4 bytes each)
assert.strictEqual(EVENT_FORMS[0x1F].src, '83');
assert.strictEqual(EVENT_FORMS[0x1F].build({ status: '1', tech: '8', served: '01020304' }),
'BF0108D50101D6050101020304');
});