v3.23.8: the command-grouped TERMINAL RESPONSE result list
The step editor's result select offers the full TS 102 223 8.12.0 list; the
spec's applicability table (Tables 6.1/6.2/6.3, "Proactive commands versus
possible terminal response") now groups it by the selected command: the
command-relevant values first (an <optgroup> "relevant for <command>"), the
rest after ("all other results").
The grouping is ordering-only - nothing is hidden, the manual hex entry
covers everything, and ANY (the drain's respond/first too) stays a flat list,
so a stored result always matches an option and real-card deviations remain
reachable.
TR_RESULT_BY_COMMAND is parsed from the pinned PDF (41 commands) and pinned
by frontend/tests/tr_result_by_command.json; trCommandCode() resolves the
editor's command names to their type codes (the first code per name,
matching testCommandOptions) and testFormSelect gained the optgroup support.
Tests: the map vs the parsed fixture, the grouping and the union (nothing
lost), the optgroup rendering and the call-site pin. 861 frontend + 842
Python; sw cache simple-v421.
This commit is contained in:
@@ -473,7 +473,11 @@ identities, **Result**, Duration, Text string, Item identifier, …):
|
|||||||
- `result` - a script alias or the two-digit hex value. The editor offers
|
- `result` - a script alias or the two-digit hex value. The editor offers
|
||||||
the **full TS 102 223 §8.12.0 list** (`00`–`3F`), labelled
|
the **full TS 102 223 §8.12.0 list** (`00`–`3F`), labelled
|
||||||
`<hex> - <alias>` where an alias exists, else `<hex> - <name>`, plus a
|
`<hex> - <alias>` where an alias exists, else `<hex> - <name>`, plus a
|
||||||
**manual** hex entry for any other value. The aliases:
|
**manual** hex entry for any other value. With a command selected (other
|
||||||
|
than `ANY`) the list is **grouped** by the spec's applicability table
|
||||||
|
(TS 102 223 Tables 6.1/6.2/6.3, "proactive commands versus possible terminal
|
||||||
|
response"): the command-relevant values first, the rest after - nothing is
|
||||||
|
hidden, the manual entry covers everything. The aliases:
|
||||||
`ok`=00, `partial`=01, `missing`=02, `modified`=07, `cancel`=10,
|
`ok`=00, `partial`=01, `missing`=02, `modified`=07, `cancel`=10,
|
||||||
`back`=11, `timeout`=12, `refused`=22, `not_understood`=32 (and
|
`back`=11, `timeout`=12, `refused`=22, `not_understood`=32 (and
|
||||||
`no_response`=12, kept for compatibility only).
|
`no_response`=12, kept for compatibility only).
|
||||||
|
|||||||
+100
-8
@@ -1905,7 +1905,7 @@
|
|||||||
// ===== Version =====
|
// ===== Version =====
|
||||||
// Single source of truth for the PWA version: shown in the header and used
|
// Single source of truth for the PWA version: shown in the header and used
|
||||||
// by the server version check in pysimConnect().
|
// by the server version check in pysimConnect().
|
||||||
const SIMPLE_VERSION = '3.23.7';
|
const SIMPLE_VERSION = '3.23.8';
|
||||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||||
|
|
||||||
// ===== view history (the browser Back/Forward buttons) =====
|
// ===== view history (the browser Back/Forward buttons) =====
|
||||||
@@ -8028,6 +8028,58 @@ const TR_RESULT_ADD_INFO = [
|
|||||||
{v: '0A', name: 'No NAA active'},
|
{v: '0A', name: 'No NAA active'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// TS 102 223 v18.3.0 Tables 6.1/6.2 ('Proactive commands versus possible
|
||||||
|
// terminal response'): the result values the spec lists for each proactive
|
||||||
|
// command, keyed by the command type code. The step editor GROUPS the result
|
||||||
|
// list with it - the command-relevant values first, the rest after; nothing
|
||||||
|
// is hidden (the manual entry covers everything). The fixture
|
||||||
|
// frontend/tests/tr_result_by_command.json is the pinned copy of the parsed
|
||||||
|
// table; some entries follow the printed table even where it looks surprising
|
||||||
|
// (e.g. the icon result listed for REFRESH).
|
||||||
|
const TR_RESULT_BY_COMMAND = {
|
||||||
|
'01': ['00', '01', '02', '03', '04', '08', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'02': ['00', '01', '02', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'03': ['00', '01', '02', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'04': ['00', '01', '02', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'05': ['00', '01', '02', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'10': ['00', '01', '02', '04', '05', '10', '14', '20', '21', '22', '23', '25', '30', '31', '32', '33', '34', '36', '39', '3C'],
|
||||||
|
'14': ['00', '01', '02', '04', '10', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'15': ['00', '01', '02', '20', '21', '26', '30', '31', '32', '33', '3C'],
|
||||||
|
'20': ['00', '01', '02', '04', '09', '10', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'21': ['00', '01', '02', '04', '10', '11', '12', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'22': ['00', '01', '02', '04', '10', '11', '12', '13', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'23': ['00', '01', '02', '04', '10', '11', '12', '13', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'24': ['00', '01', '02', '04', '10', '11', '12', '13', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'25': ['00', '01', '02', '04', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'26': ['00', '01', '02', '06', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'27': ['00', '01', '02', '20', '24', '30', '31', '32', '33', '36'],
|
||||||
|
'28': ['00', '01', '02', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'30': ['00', '01', '02', '20', '30', '31', '32', '33', '36', '38'],
|
||||||
|
'31': ['00', '01', '02', '20', '30', '31', '32', '33', '36', '38'],
|
||||||
|
'32': ['00', '01', '02', '20', '30', '31', '32', '33', '36', '38'],
|
||||||
|
'33': ['00', '01', '02', '20', '30', '31', '32', '33', '36', '38'],
|
||||||
|
'34': ['00', '01', '02', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'35': ['00', '01', '02', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'40': ['00', '01', '02', '04', '07', '10', '20', '21', '22', '25', '28', '30', '31', '32', '33', '36', '3A', '3B', '3C'],
|
||||||
|
'41': ['00', '01', '02', '04', '10', '20', '30', '31', '32', '33', '36', '3A', '3C'],
|
||||||
|
'42': ['00', '01', '02', '04', '10', '20', '30', '31', '32', '33', '36', '3A', '3C'],
|
||||||
|
'43': ['00', '01', '02', '04', '10', '20', '21', '22', '30', '31', '32', '33', '36', '3A', '3C'],
|
||||||
|
'44': ['00', '01', '02', '04', '10', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'45': ['00', '01', '04', '10', '20', '30', '31', '32', '33', '36', '3A', '3B', '3C'],
|
||||||
|
'46': ['00', '01', '04', '10', '20', '30', '31', '32', '33', '36', '3A', '3B', '3C'],
|
||||||
|
'47': ['00', '01', '02', '07', '20', '30', '31', '32', '33', '36', '3A'],
|
||||||
|
'50': ['00', '01', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'51': ['00', '01', '20', '30', '31', '32', '33', '36', '3C'],
|
||||||
|
'60': ['00', '01', '02', '27', '30', '31', '32', '33', '36', '3D'],
|
||||||
|
'61': ['00', '01', '02', '27', '30', '31', '32', '33', '36', '3D'],
|
||||||
|
'62': ['00', '01', '02', '10', '27', '30', '31', '32', '33', '36', '3D'],
|
||||||
|
'70': ['00', '01', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'71': ['00', '01', '20', '30', '31', '32', '33'],
|
||||||
|
'72': ['00', '01', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'73': ['00', '01', '20', '30', '31', '32', '33', '36'],
|
||||||
|
'79': ['00', '01', '20', '30', '31', '32', '36'],
|
||||||
|
};
|
||||||
|
|
||||||
// The script authoring aliases (the script may also use the hex value).
|
// The script authoring aliases (the script may also use the hex value).
|
||||||
// `no_response` is kept for backward compatibility (12) but is not offered
|
// `no_response` is kept for backward compatibility (12) but is not offered
|
||||||
// by the editor - 12's canonical alias is `timeout`.
|
// by the editor - 12's canonical alias is `timeout`.
|
||||||
@@ -8036,12 +8088,40 @@ const TR_RESULT_ALIASES = {
|
|||||||
modified: '07', cancel: '10', back: '11', timeout: '12', no_response: '12',
|
modified: '07', cancel: '10', back: '11', timeout: '12', no_response: '12',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The command type code of a command name (the editor's select values are the
|
||||||
|
// names; CMD_NAMES maps the codes) - the first code wins, matching
|
||||||
|
// testCommandOptions' de-duplication.
|
||||||
|
function trCommandCode(name) {
|
||||||
|
const want = String(name === undefined || name === null ? '' : name).toUpperCase();
|
||||||
|
for (const k in CMD_NAMES) {
|
||||||
|
if (CMD_NAMES[k] === want) return parseInt(k, 16);
|
||||||
|
}
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
|
||||||
// The result select's options: 'hex - alias' where an alias exists, else
|
// The result select's options: 'hex - alias' where an alias exists, else
|
||||||
// 'hex - spec name', plus a manual hex entry.
|
// 'hex - spec name', plus a manual hex entry. With opts.command (a command
|
||||||
function trResultOptions() {
|
// name other than ANY) the list is GROUPED by TS 102 223 Tables 6.1/6.2: the
|
||||||
const opts = TR_RESULTS.map(r => ({v: r.alias || r.v, l: r.v + ' - ' + (r.alias || r.name)}));
|
// command-relevant values first, the rest after - every value stays (the
|
||||||
opts.push({v: 'manual', l: t('manual input')});
|
// grouping is ordering-only).
|
||||||
return opts;
|
function trResultOptions(opts) {
|
||||||
|
const label = r => ({v: r.alias || r.v, l: r.v + ' - ' + (r.alias || r.name)});
|
||||||
|
const code = opts && opts.command ? trCommandCode(opts.command) : NaN;
|
||||||
|
const key = Number.isInteger(code) ? code.toString(16).toUpperCase().padStart(2, '0') : '';
|
||||||
|
const relevant = key && TR_RESULT_BY_COMMAND[key];
|
||||||
|
let out;
|
||||||
|
if (relevant && relevant.length) {
|
||||||
|
const rel = TR_RESULTS.filter(r => relevant.indexOf(r.v) >= 0);
|
||||||
|
const rest = TR_RESULTS.filter(r => relevant.indexOf(r.v) < 0);
|
||||||
|
out = [{group: t('relevant for') + ' ' + opts.command}]
|
||||||
|
.concat(rel.map(label))
|
||||||
|
.concat([{group: t('all other results')}])
|
||||||
|
.concat(rest.map(label));
|
||||||
|
} else {
|
||||||
|
out = TR_RESULTS.map(label);
|
||||||
|
}
|
||||||
|
out.push({v: 'manual', l: t('manual input')});
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A stored result -> the select's value and the manual hex ('' unless the
|
// A stored result -> the select's value and the manual hex ('' unless the
|
||||||
@@ -8091,7 +8171,7 @@ function testTrResultChanged(sel, wrapId) {
|
|||||||
function trResultRowHtml(selId, stored, addInfo, opts) {
|
function trResultRowHtml(selId, stored, addInfo, opts) {
|
||||||
const v = trResultFormValue(stored);
|
const v = trResultFormValue(stored);
|
||||||
const wrapId = selId + '-hexwrap';
|
const wrapId = selId + '-hexwrap';
|
||||||
const options = (opts && opts.empty ? [{v: '', l: t(opts.empty)}] : []).concat(trResultOptions());
|
const options = (opts && opts.empty ? [{v: '', l: t(opts.empty)}] : []).concat(trResultOptions(opts));
|
||||||
let html = '<div class="flex gap-2">'
|
let html = '<div class="flex gap-2">'
|
||||||
+ testFormSelect(selId, v.value, options, "testTrResultChanged(this, '" + wrapId + "')")
|
+ testFormSelect(selId, v.value, options, "testTrResultChanged(this, '" + wrapId + "')")
|
||||||
+ '<div id="' + wrapId + '" class="w-24' + (v.value === 'manual' ? '' : ' hidden') + '">'
|
+ '<div id="' + wrapId + '" class="w-24' + (v.value === 'manual' ? '' : ' hidden') + '">'
|
||||||
@@ -8121,9 +8201,19 @@ function testFormInput(id, value, placeholder, extra) {
|
|||||||
|
|
||||||
function testFormSelect(id, value, options, onchange) {
|
function testFormSelect(id, value, options, onchange) {
|
||||||
let html = '<select id="' + id + '"' + (onchange ? ' onchange="' + onchange + '"' : '') + ' class="w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800">';
|
let html = '<select id="' + id + '"' + (onchange ? ' onchange="' + onchange + '"' : '') + ' class="w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800">';
|
||||||
|
// a {group: label} entry opens an <optgroup> (the flat callers are
|
||||||
|
// unaffected; the result select groups by the command's applicability)
|
||||||
|
let open = false;
|
||||||
options.forEach(o => {
|
options.forEach(o => {
|
||||||
|
if (o.group !== undefined) {
|
||||||
|
if (open) html += '</optgroup>';
|
||||||
|
html += '<optgroup label="' + esc(String(o.group)) + '">';
|
||||||
|
open = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
html += '<option value="' + esc(String(o.v)) + '"' + (String(o.v) === String(value) ? ' selected' : '') + '>' + esc(o.l) + '</option>';
|
html += '<option value="' + esc(String(o.v)) + '"' + (String(o.v) === String(value) ? ' selected' : '') + '>' + esc(o.l) + '</option>';
|
||||||
});
|
});
|
||||||
|
if (open) html += '</optgroup>';
|
||||||
return html + '</select>';
|
return html + '</select>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8274,7 +8364,7 @@ function testStepRender() {
|
|||||||
const r = step.respond || {};
|
const r = step.respond || {};
|
||||||
html += testFormRow('TERMINAL RESPONSE result',
|
html += testFormRow('TERMINAL RESPONSE result',
|
||||||
trResultRowHtml('test-f-result', r.result === undefined ? 'ok' : r.result, r.additional_info,
|
trResultRowHtml('test-f-result', r.result === undefined ? 'ok' : r.result, r.additional_info,
|
||||||
{list: true}));
|
{list: true, command: step.command || 'ANY'}));
|
||||||
html += testFormRow('Item identifier (SELECT ITEM / SET UP MENU)', testFormInput('test-f-ritem', r.item_id, '1..255'));
|
html += testFormRow('Item identifier (SELECT ITEM / SET UP MENU)', testFormInput('test-f-ritem', r.item_id, '1..255'));
|
||||||
html += testFormRow('Text (GET INKEY / GET INPUT)', '<div class="flex gap-2">'
|
html += testFormRow('Text (GET INKEY / GET INPUT)', '<div class="flex gap-2">'
|
||||||
+ testFormInput('test-f-rtext', r.text, 'text')
|
+ testFormInput('test-f-rtext', r.text, 'text')
|
||||||
@@ -23065,6 +23155,8 @@ const LANG_RU = {
|
|||||||
'Text': 'Текст',
|
'Text': 'Текст',
|
||||||
'id': 'id',
|
'id': 'id',
|
||||||
'text': 'текст',
|
'text': 'текст',
|
||||||
|
'relevant for': 'подходит для',
|
||||||
|
'all other results': 'остальные результаты',
|
||||||
'TERMINAL PROFILE re-sent \u2014 new CAT session': 'TERMINAL PROFILE отправлен заново \u2014 новая CAT-сессия',
|
'TERMINAL PROFILE re-sent \u2014 new CAT session': 'TERMINAL PROFILE отправлен заново \u2014 новая CAT-сессия',
|
||||||
'manual input': 'ручной ввод',
|
'manual input': 'ручной ввод',
|
||||||
'Additional information (optional hex byte)': 'Дополнительная информация (необязательный hex-байт)',
|
'Additional information (optional hex byte)': 'Дополнительная информация (необязательный hex-байт)',
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simple-v420';
|
const CACHE = 'simple-v421';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
@@ -62,8 +62,10 @@ eval(extractFunc(html, 'testFormInput'));
|
|||||||
eval(extractFunc(html, 'testFormSelect'));
|
eval(extractFunc(html, 'testFormSelect'));
|
||||||
// the TERMINAL RESPONSE result tables + the shared result-row helpers
|
// the TERMINAL RESPONSE result tables + the shared result-row helpers
|
||||||
eval(extractBlock('const TR_RESULTS = [', 'const TR_RESULT_ADD_INFO = [').replace(/^const /gm, 'var '));
|
eval(extractBlock('const TR_RESULTS = [', 'const TR_RESULT_ADD_INFO = [').replace(/^const /gm, 'var '));
|
||||||
eval(extractBlock('const TR_RESULT_ADD_INFO = [', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
eval(extractBlock('const TR_RESULT_ADD_INFO = [', '// TS 102 223 v18.3.0 Tables 6.1/6.2').replace(/^const /gm, 'var '));
|
||||||
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trResultOptions').replace(/^const /gm, 'var '));
|
eval(extractBlock('const TR_RESULT_BY_COMMAND = {', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
||||||
|
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trCommandCode').replace(/^const /gm, 'var '));
|
||||||
|
eval(extractFunc(html, 'trCommandCode'));
|
||||||
eval(extractFunc(html, 'trResultOptions'));
|
eval(extractFunc(html, 'trResultOptions'));
|
||||||
eval(extractFunc(html, 'trResultFormValue'));
|
eval(extractFunc(html, 'trResultFormValue'));
|
||||||
eval(extractFunc(html, 'trResultValid'));
|
eval(extractFunc(html, 'trResultValid'));
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"_note": "TS 102 223 v18.3.0 Tables 6.1/6.2/6.3 \"Proactive commands versus possible terminal response\", parsed from the pinned PDF (docs/uicc/, workspace copy) - which result values the spec lists for each proactive command. The PWA's TR_RESULT_BY_COMMAND mirrors this file (frontend/tests/tr_results.test.js asserts it); the list is used for GROUPING only (the command-relevant values first, the rest after - nothing is hidden). Regenerate with a pdftotext -layout parse of the table pages if the spec pin changes.",
|
||||||
|
"commands": {
|
||||||
|
"01": ["00", "01", "02", "03", "04", "08", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"02": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"03": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"04": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"05": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"10": ["00", "01", "02", "04", "05", "10", "14", "20", "21", "22", "23", "25", "30", "31", "32", "33", "34", "36", "39", "3C"],
|
||||||
|
"14": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"15": ["00", "01", "02", "20", "21", "26", "30", "31", "32", "33", "3C"],
|
||||||
|
"20": ["00", "01", "02", "04", "09", "10", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"21": ["00", "01", "02", "04", "10", "11", "12", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"22": ["00", "01", "02", "04", "10", "11", "12", "13", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"23": ["00", "01", "02", "04", "10", "11", "12", "13", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"24": ["00", "01", "02", "04", "10", "11", "12", "13", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"25": ["00", "01", "02", "04", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"26": ["00", "01", "02", "06", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"27": ["00", "01", "02", "20", "24", "30", "31", "32", "33", "36"],
|
||||||
|
"28": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"30": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||||
|
"31": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||||
|
"32": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||||
|
"33": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||||
|
"34": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"35": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"40": ["00", "01", "02", "04", "07", "10", "20", "21", "22", "25", "28", "30", "31", "32", "33", "36", "3A", "3B", "3C"],
|
||||||
|
"41": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3C"],
|
||||||
|
"42": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3C"],
|
||||||
|
"43": ["00", "01", "02", "04", "10", "20", "21", "22", "30", "31", "32", "33", "36", "3A", "3C"],
|
||||||
|
"44": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"45": ["00", "01", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3B", "3C"],
|
||||||
|
"46": ["00", "01", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3B", "3C"],
|
||||||
|
"47": ["00", "01", "02", "07", "20", "30", "31", "32", "33", "36", "3A"],
|
||||||
|
"50": ["00", "01", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"51": ["00", "01", "20", "30", "31", "32", "33", "36", "3C"],
|
||||||
|
"60": ["00", "01", "02", "27", "30", "31", "32", "33", "36", "3D"],
|
||||||
|
"61": ["00", "01", "02", "27", "30", "31", "32", "33", "36", "3D"],
|
||||||
|
"62": ["00", "01", "02", "10", "27", "30", "31", "32", "33", "36", "3D"],
|
||||||
|
"70": ["00", "01", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"71": ["00", "01", "20", "30", "31", "32", "33"],
|
||||||
|
"72": ["00", "01", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"73": ["00", "01", "20", "30", "31", "32", "33", "36"],
|
||||||
|
"79": ["00", "01", "20", "30", "31", "32", "36"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,9 +31,12 @@ function extractFunc(src, name) {
|
|||||||
|
|
||||||
// Rewrite top-level const -> var so the tables leak out of sloppy-mode eval.
|
// Rewrite top-level const -> var so the tables leak out of sloppy-mode eval.
|
||||||
eval(extractBlock('const TR_RESULTS = [', 'const TR_RESULT_ADD_INFO = [').replace(/^const /gm, 'var '));
|
eval(extractBlock('const TR_RESULTS = [', 'const TR_RESULT_ADD_INFO = [').replace(/^const /gm, 'var '));
|
||||||
eval(extractBlock('const TR_RESULT_ADD_INFO = [', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
eval(extractBlock('const TR_RESULT_ADD_INFO = [', '// TS 102 223 v18.3.0 Tables 6.1/6.2').replace(/^const /gm, 'var '));
|
||||||
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trResultOptions').replace(/^const /gm, 'var '));
|
eval(extractBlock('const TR_RESULT_BY_COMMAND = {', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
||||||
|
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trCommandCode').replace(/^const /gm, 'var '));
|
||||||
|
eval(extractBlock('const CMD_NAMES = {', 'function cmdQualifierShort').replace(/^const /gm, 'var '));
|
||||||
eval(extractFunc(html, 'esc'));
|
eval(extractFunc(html, 'esc'));
|
||||||
|
eval(extractFunc(html, 'trCommandCode'));
|
||||||
eval(extractFunc(html, 'trResultOptions'));
|
eval(extractFunc(html, 'trResultOptions'));
|
||||||
eval(extractFunc(html, 'trResultFormValue'));
|
eval(extractFunc(html, 'trResultFormValue'));
|
||||||
eval(extractFunc(html, 'trResultValid'));
|
eval(extractFunc(html, 'trResultValid'));
|
||||||
@@ -110,7 +113,7 @@ test('the step editor offers the full list for all three result fields', () => {
|
|||||||
assert.ok(form.includes("{empty: 'same as above'}"), 'the first keeps its empty option');
|
assert.ok(form.includes("{empty: 'same as above'}"), 'the first keeps its empty option');
|
||||||
// the suggestion list is rendered by the first result row of each form
|
// the suggestion list is rendered by the first result row of each form
|
||||||
// (the drain's respond and the expectation), never twice in one form
|
// (the drain's respond and the expectation), never twice in one form
|
||||||
assert.strictEqual((form.match(/\{list: true\}/g) || []).length, 2, form);
|
assert.strictEqual((form.match(/\{list: true/g) || []).length, 2, form);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('trResultRowHtml renders the manual entry and the additional info', () => {
|
test('trResultRowHtml renders the manual entry and the additional info', () => {
|
||||||
@@ -135,3 +138,59 @@ test('trResultRowHtml renders the manual entry and the additional info', () => {
|
|||||||
+ trResultRowHtml('test-f-pfirst', '', '', { empty: 'same as above' });
|
+ trResultRowHtml('test-f-pfirst', '', '', { empty: 'same as above' });
|
||||||
assert.strictEqual((twoRows.match(/id="tr-addinfo-list"/g) || []).length, 1, twoRows);
|
assert.strictEqual((twoRows.match(/id="tr-addinfo-list"/g) || []).length, 1, twoRows);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('the command/result grouping table matches the parsed spec fixture', () => {
|
||||||
|
const fixture = JSON.parse(fs.readFileSync(path.join(__dirname, 'tr_result_by_command.json'), 'utf8'));
|
||||||
|
assert.deepStrictEqual(TR_RESULT_BY_COMMAND, fixture.commands);
|
||||||
|
// the editor's command names resolve to their type codes (the first code
|
||||||
|
// wins, matching testCommandOptions' de-duplication)
|
||||||
|
assert.strictEqual(trCommandCode('REFRESH'), 0x01);
|
||||||
|
assert.strictEqual(trCommandCode('PLAY TONE'), 0x20);
|
||||||
|
assert.strictEqual(trCommandCode('TIMER MANAGEMENT'), 0x27);
|
||||||
|
assert.strictEqual(trCommandCode('OPEN CHANNEL'), 0x40);
|
||||||
|
assert.ok(Number.isNaN(trCommandCode('ANY')));
|
||||||
|
assert.ok(Number.isNaN(trCommandCode('NONSENSE')));
|
||||||
|
// every mapped code is a known proactive command
|
||||||
|
for (const code of Object.keys(TR_RESULT_BY_COMMAND)) {
|
||||||
|
assert.ok(CMD_NAMES[code], 'the mapped code 0x' + code + ' must be a known command');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('trResultOptions groups the list by the selected command', () => {
|
||||||
|
const flat = trResultOptions();
|
||||||
|
assert.strictEqual(flat.filter(o => o.group).length, 0, 'no groups without a command');
|
||||||
|
assert.strictEqual(flat.length, TR_RESULTS.length + 1);
|
||||||
|
assert.strictEqual(trResultOptions({ command: 'ANY' }).filter(o => o.group).length, 0,
|
||||||
|
'ANY stays flat');
|
||||||
|
const grouped = trResultOptions({ command: 'REFRESH' });
|
||||||
|
assert.deepStrictEqual(grouped.filter(o => o.group).map(o => o.group),
|
||||||
|
['relevant for REFRESH', 'all other results']);
|
||||||
|
// the first group carries the spec's REFRESH set in the spec order
|
||||||
|
const split = grouped.findIndex(o => o.group === 'all other results');
|
||||||
|
const first = grouped.slice(1, split);
|
||||||
|
assert.deepStrictEqual(first.map(o => o.v),
|
||||||
|
TR_RESULT_BY_COMMAND['01'].map(hex => (TR_RESULTS.find(r => r.v === hex) || {}).alias || hex));
|
||||||
|
// nothing is hidden: both groups together are the full list + the manual
|
||||||
|
// entry, and the stored value always matches an option
|
||||||
|
const values = grouped.filter(o => !o.group).map(o => o.v).sort();
|
||||||
|
const all = TR_RESULTS.map(r => r.alias || r.v).concat(['manual']).sort();
|
||||||
|
assert.deepStrictEqual(values, all);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('testFormSelect renders the optgroups', () => {
|
||||||
|
const out = testFormSelect('x', 'ok', [{ group: 'relevant for REFRESH' }, { v: 'ok', l: '00 - ok' },
|
||||||
|
{ group: 'all other results' }, { v: '1F', l: '1F - Slices status change' }]);
|
||||||
|
assert.ok(out.includes('<optgroup label="relevant for REFRESH">'), out);
|
||||||
|
assert.ok(out.includes('<optgroup label="all other results">'), out);
|
||||||
|
assert.strictEqual((out.match(/<optgroup/g) || []).length, 2, out);
|
||||||
|
assert.strictEqual((out.match(/<\/optgroup>/g) || []).length, 2, out);
|
||||||
|
assert.ok(out.includes('<option value="ok" selected>00 - ok</option>'), out);
|
||||||
|
// the flat form is unchanged
|
||||||
|
assert.ok(!testFormSelect('y', 'ok', [{ v: 'ok', l: '00 - ok' }]).includes('optgroup'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the expectation result row passes the selected command', () => {
|
||||||
|
const form = extractFunc(html, 'testStepRender');
|
||||||
|
assert.ok(/trResultRowHtml\('test-f-result'[\s\S]*?command: step\.command \|\| 'ANY'/.test(form),
|
||||||
|
'the expectation result must group by the selected command');
|
||||||
|
});
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pysim-simple-server"
|
name = "pysim-simple-server"
|
||||||
version = "3.23.7"
|
version = "3.23.8"
|
||||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE
|
|||||||
from osmocom.utils import rpad
|
from osmocom.utils import rpad
|
||||||
|
|
||||||
|
|
||||||
VERSION = '3.23.7'
|
VERSION = '3.23.8'
|
||||||
|
|
||||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user