v3.25.2: the TR tag-form alignment with TS 102 223 6.8.0/6.8.1
The TR's response objects are all Min = N in the spec's own table, and 6.8.0 says such objects "should set the CR flag to comprehension not required" - so the builders now emit the base tag forms: - device identities 82 -> 02 (every builder) - PLI data 93/94/AD/AE/BF/E2/E5 -> 13/14/2D/2E/3F/62/65 (26/63 already base); the PWA inline codecs follow - POLL INTERVAL Duration 84 -> 04 - GET INKEY/INPUT Text string 8D -> 0D - SELECT ITEM Item identifier 90 -> 10 - Timer identifier/value A4/A5 -> 24/25 (_bip_tr already used the reference traces' 01/02/03.) The command details now echo the command's tag form (01/81) besides its qualifier - 6.8.1 requires the object to be identical to the command's "including the comprehension required flag": the parser returns cd_tag and _build_tr, _handle_timer_command, _handle_bip_command, the menu pendings and testscript.build_tr carry it. The Result keeps 03/83 (Min = Y). _tr_data_only strips both tag styles and the POLL Duration decode accepts 04. Tests: the base forms per builder + the echo + the PWA drift guards; 883 frontend + 866 Python; sw cache simple-v426.
This commit is contained in:
+17
-17
@@ -1905,7 +1905,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.25.1';
|
||||
const SIMPLE_VERSION = '3.25.2';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== view history (the browser Back/Forward buttons) =====
|
||||
@@ -18140,24 +18140,24 @@ function encImeisvPli(digits) {
|
||||
}
|
||||
|
||||
const PLI_CODECS = {
|
||||
'00': { tlv: '9305',
|
||||
'00': { tlv: '1305',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 10) return null;
|
||||
const pl = decPlmn(v); return { mcc: pl[0], mnc: pl[1], lac: v.slice(6,10).toUpperCase() };
|
||||
},
|
||||
encode(v) { return '9305' + encPlmn(v.mcc||'250',v.mnc||'01') + ((v.lac||'0000').toUpperCase()); },
|
||||
encode(v) { return '1305' + encPlmn(v.mcc||'250',v.mnc||'01') + ((v.lac||'0000').toUpperCase()); },
|
||||
fields: [
|
||||
{id:'mcc',label:'MCC',type:'text',pattern:'\\d{3}',placeholder:'e.g. 250'},
|
||||
{id:'mnc',label:'MNC',type:'text',pattern:'\\d{2,3}',placeholder:'e.g. 01'},
|
||||
{id:'lac',label:'LAC/TAC',type:'text',pattern:'[0-9A-Fa-f]{4}',placeholder:'hex'},
|
||||
],
|
||||
},
|
||||
'01': { tlv: '9408',
|
||||
'01': { tlv: '1408',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 16) return null;
|
||||
return { imei: decImei(v) };
|
||||
},
|
||||
encode(v) { const imei = encImeiPli(v.imei||''); return imei ? '9408'+imei : ''; },
|
||||
encode(v) { const imei = encImeiPli(v.imei||''); return imei ? '1408'+imei : ''; },
|
||||
fields: [
|
||||
{id:'imei',label:'IMEI',type:'text',pattern:'\\d{15}',placeholder:'15 digits'},
|
||||
],
|
||||
@@ -18210,39 +18210,39 @@ const PLI_CODECS = {
|
||||
{id:'tz',label:'TZ offset',type:'text',pattern:'[-+]\\d{2}:?\\d{2}',placeholder:'+00:00'},
|
||||
],
|
||||
},
|
||||
'04': { tlv: 'AD02',
|
||||
'04': { tlv: '2D02',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 4) return null;
|
||||
return { lang: String.fromCharCode(parseInt(v[0]+v[1],16), parseInt(v[2]+v[3],16)) };
|
||||
},
|
||||
encode(v) {
|
||||
const lang = (v.lang||'en').slice(0,2);
|
||||
return 'AD02' + lang.charCodeAt(0).toString(16).padStart(2,'0') + lang.charCodeAt(1).toString(16).padStart(2,'0');
|
||||
return '2D02' + lang.charCodeAt(0).toString(16).padStart(2,'0') + lang.charCodeAt(1).toString(16).padStart(2,'0');
|
||||
},
|
||||
fields: [
|
||||
{id:'lang',label:'Language',type:'text',pattern:'[a-zA-Z]{2,3}',placeholder:'en'},
|
||||
],
|
||||
},
|
||||
'05': { tlv: 'AE02',
|
||||
'05': { tlv: '2E02',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 4) return null;
|
||||
return { status: parseInt(v[0]+v[1],16).toString(), ta: parseInt(v[2]+v[3],16).toString() };
|
||||
},
|
||||
encode(v) { return 'AE02' + (parseInt(v.status)||0).toString(16).padStart(2,'0') + (parseInt(v.ta)||0).toString(16).padStart(2,'0'); },
|
||||
encode(v) { return '2E02' + (parseInt(v.status)||0).toString(16).padStart(2,'0') + (parseInt(v.ta)||0).toString(16).padStart(2,'0'); },
|
||||
fields: [
|
||||
{id:'status',label:'ME Status',type:'number',min:'0',max:'255',placeholder:'0'},
|
||||
{id:'ta',label:'Timing Advance',type:'number',min:'0',max:'255',placeholder:'0'},
|
||||
],
|
||||
},
|
||||
'06': { tlv: 'BF01',
|
||||
'06': { tlv: '3F01',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 2) return null;
|
||||
return { tech: parseInt(v[0]+v[1],16).toString() };
|
||||
},
|
||||
encode(v) { return 'BF01' + (parseInt(v.tech)||0).toString(16).padStart(2,'0'); },
|
||||
encode(v) { return '3F01' + (parseInt(v.tech)||0).toString(16).padStart(2,'0'); },
|
||||
fields: [{id:'tech',label:'Access Technology',type:'select',opts:ACCESSTECH}],
|
||||
},
|
||||
'08': { tlv: 'E208',
|
||||
'08': { tlv: '6208',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 16) return null;
|
||||
return { imeisv: decImei(v) + v.slice(14,16).toUpperCase() };
|
||||
@@ -18253,17 +18253,17 @@ const PLI_CODECS = {
|
||||
let h = '';
|
||||
for (let i = 0; i < 16; i += 2)
|
||||
h += (parseInt(raw[i+1])<<4 | parseInt(raw[i])).toString(16).padStart(2,'0');
|
||||
return 'E208' + h;
|
||||
return '6208' + h;
|
||||
},
|
||||
fields: [{id:'imeisv',label:'IMEISV',type:'text',pattern:'\\d{16}',placeholder:'16 digits'}],
|
||||
},
|
||||
'09': { tlv: 'E501',
|
||||
'09': { tlv: '6501',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 2) return null;
|
||||
return { mode: parseInt(v[0]+v[1],16).toString() };
|
||||
},
|
||||
encode(v) {
|
||||
return 'E501' + (parseInt(v.mode)||0).toString(16).padStart(2,'0');
|
||||
return '6501' + (parseInt(v.mode)||0).toString(16).padStart(2,'0');
|
||||
},
|
||||
fields: [{id:'mode',label:'Search Mode',type:'select',
|
||||
opts:[{v:0,l:'Manual'},{v:1,l:'Automatic'}]}],
|
||||
@@ -18281,7 +18281,7 @@ const PLI_CODECS = {
|
||||
opts:[{v:0,l:'Very low'},{v:1,l:'Low'},{v:2,l:'Average'},{v:3,l:'Good'},
|
||||
{v:4,l:'Full'},{v:0xFE,l:'Not battery powered'},{v:0xFF,l:'Unknown / charging'}]}],
|
||||
},
|
||||
'0E': { tlv: 'BF',
|
||||
'0E': { tlv: '3F',
|
||||
decode(hex) {
|
||||
const v = hex.replace(/\s/g, '').slice(4); if (v.length < 2) return null;
|
||||
const techs = []; for (let i = 0; i < v.length; i += 2) techs.push(parseInt(v[i]+v[i+1],16));
|
||||
@@ -18291,7 +18291,7 @@ const PLI_CODECS = {
|
||||
const techs = (v.techs||'').split(',').filter(t => t !== '').map(t => parseInt(t)&0xFF);
|
||||
if (!techs.length) return '';
|
||||
const len = techs.length.toString(16).padStart(2,'0');
|
||||
return 'BF' + len + techs.map(t => t.toString(16).padStart(2,'0')).join('');
|
||||
return '3F' + len + techs.map(t => t.toString(16).padStart(2,'0')).join('');
|
||||
},
|
||||
fields: [
|
||||
{id:'techs',label:'Access Technologies (comma-separated)',type:'text',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v425';
|
||||
const CACHE = 'simple-v426';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -38,22 +38,31 @@ test('the PLI date/time codec uses the 8.39 TP-SCTS coding', () => {
|
||||
assert.strictEqual(c.decode('2607123'), null);
|
||||
});
|
||||
|
||||
test('the PLI codec tags follow TS 101 220 Table 7.23', () => {
|
||||
// 05 Timing Advance: 2E/AE (C6 is a 3GPP2 tag)
|
||||
assert.strictEqual(PLI_CODECS['05'].tlv, 'AE02');
|
||||
assert.strictEqual(PLI_CODECS['05'].encode({ status: '0', ta: '0' }), 'AE020000');
|
||||
// 08 IMEISV: 62/E2 (94 is the IMEI tag)
|
||||
assert.strictEqual(PLI_CODECS['08'].tlv, 'E208');
|
||||
test('the PLI codec tags follow TS 101 220 Table 7.23, base (CR=0) form', () => {
|
||||
// the TR's response objects are Min = N, so TS 102 223 6.8.0 says they
|
||||
// "should set the CR flag to comprehension not required" (v3.25.2):
|
||||
// the base tag values (13/14/2D/2E/3F/62/65/63/26), not the CR-set forms
|
||||
assert.strictEqual(PLI_CODECS['00'].tlv, '1305');
|
||||
assert.strictEqual(PLI_CODECS['01'].tlv, '1408');
|
||||
assert.strictEqual(PLI_CODECS['01'].encode({ imei: '490154203237518' }), '140894104502237315f8');
|
||||
assert.strictEqual(PLI_CODECS['04'].tlv, '2D02');
|
||||
assert.strictEqual(PLI_CODECS['06'].tlv, '3F01');
|
||||
// 05 Timing Advance: 2E (C6 is a 3GPP2 tag)
|
||||
assert.strictEqual(PLI_CODECS['05'].tlv, '2E02');
|
||||
assert.strictEqual(PLI_CODECS['05'].encode({ status: '0', ta: '0' }), '2E020000');
|
||||
// 08 IMEISV: 62 (94 is the IMEI tag)
|
||||
assert.strictEqual(PLI_CODECS['08'].tlv, '6208');
|
||||
assert.strictEqual(PLI_CODECS['08'].encode({ imeisv: '4901542032375108' }),
|
||||
'E2089410450223731580');
|
||||
// 09 Network search mode: 65/E5, '00' Manual / '01' Automatic (8.75)
|
||||
assert.strictEqual(PLI_CODECS['09'].tlv, 'E501');
|
||||
'62089410450223731580');
|
||||
// 09 Network search mode: 65, '00' Manual / '01' Automatic (8.75)
|
||||
assert.strictEqual(PLI_CODECS['09'].tlv, '6501');
|
||||
assert.strictEqual(PLI_CODECS['09'].fields[0].opts[0].l, 'Manual');
|
||||
assert.strictEqual(PLI_CODECS['09'].fields[0].opts[1].l, 'Automatic');
|
||||
// 0A Battery state: 63/E3 is an enum, not a percentage (8.76)
|
||||
assert.strictEqual(PLI_CODECS['0A'].tlv, '6301');
|
||||
assert.strictEqual(PLI_CODECS['0A'].encode({ charge: '4' }), '630104');
|
||||
assert.strictEqual(PLI_CODECS['0A'].encode({ charge: '0' }), '630100');
|
||||
assert.strictEqual(PLI_CODECS['0E'].tlv, '3F');
|
||||
});
|
||||
|
||||
test('the IMEI encoder pads the odd 15th digit with F (TS 124 008)', () => {
|
||||
@@ -65,16 +74,16 @@ test('the IMEI encoder pads the odd 15th digit with F (TS 124 008)', () => {
|
||||
test('the server PLI defaults decode through their codecs', () => {
|
||||
// mirrors PLI_DEFAULTS in server.py (v3.23.1; a drift guard for the codecs)
|
||||
const defaults = {
|
||||
'00': '930500F1100000', '01': '940894104502237315F8',
|
||||
'04': 'AD02656E', '05': 'AE020000', '06': 'BF0108',
|
||||
'08': 'E2089410450223731580', '09': 'E50101',
|
||||
'0A': '630104', '0E': 'BF020308',
|
||||
'00': '130500F1100000', '01': '140894104502237315F8',
|
||||
'04': '2D02656E', '05': '2E020000', '06': '3F0108',
|
||||
'08': '62089410450223731580', '09': '650101',
|
||||
'0A': '630104', '0E': '3F020308',
|
||||
};
|
||||
for (const [code, hex] of Object.entries(defaults)) {
|
||||
assert.ok(PLI_CODECS[code].decode(hex), code + ' must decode');
|
||||
}
|
||||
assert.strictEqual(PLI_CODECS['01'].decode('940894104502237315F8').imei,
|
||||
assert.strictEqual(PLI_CODECS['01'].decode('140894104502237315F8').imei,
|
||||
'490154203237518');
|
||||
assert.deepStrictEqual(PLI_CODECS['09'].decode('E50101'), { mode: '1' });
|
||||
assert.deepStrictEqual(PLI_CODECS['09'].decode('650101'), { mode: '1' });
|
||||
assert.deepStrictEqual(PLI_CODECS['0A'].decode('630104'), { charge: '4' });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user