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:
@@ -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>События звонка</strong> — MT call (transaction identifier, номер вызывающего + TON → Address, subaddress, IMS URI, media type; источник <em>network</em>), Call connected и Call disconnected (transaction identifier, источник near/far, media type и причина — отсутствует, нулевая форма 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>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) — источник <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) — источник <em>network</em></li>
|
||||
<li><strong>Data Connection Status Change</strong> (0x1D) — статус (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) — TS 131 111 §7.5.25</li>
|
||||
<li><strong>Poll Interval Negotiation</strong> (0x1C) — предлагает интервал опроса в виде Duration (единица + значение); после отправки показывается ответ UICC (принято / отклонено / изменено + длительность), а значение <em>изменено</em> становится интервалом фонового опроса (TS 102 223 §7.5.22). Форма появляется, когда карта подписана на это событие.</li>
|
||||
</ul>
|
||||
|
||||
@@ -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>Call events</strong> — MT call (transaction identifier, calling party number + TON → 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 — 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>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) — 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) — source <em>network</em></li>
|
||||
<li><strong>Data Connection Status Change</strong> (0x1D) — 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) — TS 131 111 §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 §7.5.22). The form appears when the card subscribed to this event.</li>
|
||||
</ul>
|
||||
|
||||
+151
-6
@@ -13170,7 +13170,28 @@ const EVENT_FORMS = {
|
||||
],
|
||||
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',
|
||||
fields: [
|
||||
{ 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'); }
|
||||
},
|
||||
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,
|
||||
0x0E: null,
|
||||
0x0F: null,
|
||||
0x10: null,
|
||||
0x0E: { title: 'Network search mode change',
|
||||
fields: [
|
||||
{ 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',
|
||||
fields: [
|
||||
{ id: 'status', label: 'Access status', type: 'select',
|
||||
@@ -13297,7 +13360,39 @@ const EVENT_FORMS = {
|
||||
}
|
||||
},
|
||||
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',
|
||||
fields: [
|
||||
{ id: 'unit', label: 'Time unit', type: 'select',
|
||||
@@ -13364,6 +13459,56 @@ const EVENT_FORMS = {
|
||||
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 = [
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v348';
|
||||
const CACHE = 'simple-v349';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -186,3 +186,33 @@ test('the browser, WLAN and CSG events build their object sets', () => {
|
||||
test('every event type has a parameter form now', () => {
|
||||
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');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user