Compare commits
3 Commits
9879675df2
...
30969f8f04
| Author | SHA1 | Date | |
|---|---|---|---|
| 30969f8f04 | |||
| 569f308cec | |||
| 61ab7bc304 |
+83
-12
@@ -6731,6 +6731,24 @@ function profilerMaskPrefix4(hex) {
|
||||
return h.slice(0, 8) + '?'.repeat(h.length - 8);
|
||||
}
|
||||
|
||||
// Which size/record fields apply to a given file type.
|
||||
function profilerFileFields(ft) {
|
||||
if (ft === 'linear_fixed' || ft === 'cyclic') return { size: false, records: true };
|
||||
if (ft === 'df') return { size: false, records: false };
|
||||
if (ft === 'transparent' || ft === 'ber_tlv') return { size: true, records: false };
|
||||
return { size: true, records: true };
|
||||
}
|
||||
|
||||
// Content representation ('record' vs 'transparent') for a given file type.
|
||||
function profilerContentKindForFileType(ft) {
|
||||
return (ft === 'linear_fixed' || ft === 'cyclic') ? 'record' : 'transparent';
|
||||
}
|
||||
|
||||
// A fresh record content object seeded with one empty record row.
|
||||
function profilerEmptyRecordContent(mode) {
|
||||
return { mode: mode, kind: 'record', records: [{ num: 1, data: '' }] };
|
||||
}
|
||||
|
||||
function profilerValidateProfile(obj) {
|
||||
if (!obj || typeof obj !== 'object') return 'Not an object';
|
||||
if (!obj.name) return 'Missing name';
|
||||
@@ -6890,11 +6908,12 @@ async function profilerBuildFileRule(path, c, ignoreFids) {
|
||||
sel = await pysimFetch('/api/select', { path: path });
|
||||
} catch (e) { return null; }
|
||||
if (!sel || sel.exists !== true) return null;
|
||||
const isRecord = profilerContentKindForFileType(sel.file_type) === 'record';
|
||||
const rule = {
|
||||
type: 'file',
|
||||
path: path,
|
||||
fileType: sel.file_type || null,
|
||||
fileSize: (sel.file_size === null || sel.file_size === undefined) ? null : sel.file_size,
|
||||
fileSize: isRecord ? null : ((sel.file_size === null || sel.file_size === undefined) ? null : sel.file_size),
|
||||
recordLen: (sel.record_len === null || sel.record_len === undefined) ? null : sel.record_len,
|
||||
numRecords: (sel.num_of_rec === null || sel.num_of_rec === undefined) ? null : sel.num_of_rec,
|
||||
content: null,
|
||||
@@ -7014,17 +7033,24 @@ function profilerRenderRule(i, r) {
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Path')) + '</label>' +
|
||||
'<input value="' + escHtml(r.path || '') + '" oninput="profilerUpdateRule(' + i + ',\'path\',this.value)" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="MF/7F10/6F3A"></div>';
|
||||
html += '</div>';
|
||||
const vis = profilerFileFields(r.fileType);
|
||||
html += '<div class="grid grid-cols-2 md:grid-cols-4 gap-2 mb-2">';
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('File type')) + '</label>' +
|
||||
'<select onchange="profilerUpdateRule(' + i + ',\'fileType\',this.value||null)" class="w-full border border-gray-300 dark:border-slate-600 text-xs rounded px-2 py-1 dark:bg-slate-800">' + ftOpts + '</select></div>';
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Size')) + '</label>' +
|
||||
'<input type="number" value="' + (r.fileSize === null ? '' : r.fileSize) + '" oninput="profilerUpdateRule(' + i + ',\'fileSize\',this.value===\'\'?null:parseInt(this.value))" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="—"></div>';
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Record length')) + '</label>' +
|
||||
'<input type="number" value="' + (r.recordLen === null ? '' : r.recordLen) + '" oninput="profilerUpdateRule(' + i + ',\'recordLen\',this.value===\'\'?null:parseInt(this.value))" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="—"></div>';
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Record count')) + '</label>' +
|
||||
'<input type="number" value="' + (r.numRecords === null ? '' : r.numRecords) + '" oninput="profilerUpdateRule(' + i + ',\'numRecords\',this.value===\'\'?null:parseInt(this.value))" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="—"></div>';
|
||||
'<select onchange="profilerUpdateRuleFileType(' + i + ',this.value||null)" class="w-full border border-gray-300 dark:border-slate-600 text-xs rounded px-2 py-1 dark:bg-slate-800">' + ftOpts + '</select></div>';
|
||||
if (vis.size) {
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Size')) + '</label>' +
|
||||
'<input type="number" value="' + (r.fileSize === null ? '' : r.fileSize) + '" oninput="profilerUpdateRule(' + i + ',\'fileSize\',this.value===\'\'?null:parseInt(this.value))" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="—"></div>';
|
||||
}
|
||||
if (vis.records) {
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Record length')) + '</label>' +
|
||||
'<input type="number" value="' + (r.recordLen === null ? '' : r.recordLen) + '" oninput="profilerUpdateRule(' + i + ',\'recordLen\',this.value===\'\'?null:parseInt(this.value))" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="—"></div>';
|
||||
html += '<div><label class="block text-xs text-gray-600 dark:text-slate-400 mb-0.5">' + esc(t('Record count')) + '</label>' +
|
||||
'<input type="number" value="' + (r.numRecords === null ? '' : r.numRecords) + '" oninput="profilerUpdateRule(' + i + ',\'numRecords\',this.value===\'\'?null:parseInt(this.value))" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800" placeholder="—"></div>';
|
||||
}
|
||||
html += '</div>';
|
||||
html += profilerRenderContentEditor(i, r);
|
||||
if (r.fileType !== 'df') {
|
||||
html += profilerRenderContentEditor(i, r);
|
||||
}
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
@@ -7044,9 +7070,14 @@ function profilerRenderContentEditor(i, r) {
|
||||
for (let ri = 0; ri < c.records.length; ri++) {
|
||||
const rec = c.records[ri];
|
||||
html += '<div class="flex items-center gap-2"><span class="text-xs text-gray-500 w-8">' + rec.num + '</span>' +
|
||||
'<input value="' + escHtml(rec.data) + '" oninput="profilerUpdateRecord(' + i + ',' + ri + ',this.value)" class="flex-1 font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800"></div>';
|
||||
'<input value="' + escHtml(rec.data) + '" oninput="profilerUpdateRecord(' + i + ',' + ri + ',this.value)" class="flex-1 font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800">';
|
||||
if (c.records.length > 1) {
|
||||
html += '<button onclick="profilerDeleteRecord(' + i + ',' + ri + ')" class="text-red-400 hover:text-red-600 text-xs px-1">✕</button>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
html += '<button onclick="profilerAddRecord(' + i + ')" class="mt-1 text-xs px-2 py-0.5 rounded bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300">+ Record</button>';
|
||||
} else {
|
||||
html += '<textarea oninput="profilerUpdateRuleContent(' + i + ',this.value)" rows="2" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800">' + escHtml(c.expected || '') + '</textarea>';
|
||||
}
|
||||
@@ -7059,6 +7090,24 @@ function profilerUpdateRule(i, field, value) {
|
||||
profilerDraft.rules[i][field] = value;
|
||||
}
|
||||
|
||||
function profilerUpdateRuleFileType(i, value) {
|
||||
if (!profilerDraft) return;
|
||||
const r = profilerDraft.rules[i];
|
||||
r.fileType = value;
|
||||
const vis = profilerFileFields(value);
|
||||
if (!vis.size) r.fileSize = null;
|
||||
if (!vis.records) { r.recordLen = null; r.numRecords = null; }
|
||||
if (r.content) {
|
||||
const kind = profilerContentKindForFileType(value);
|
||||
if (r.content.kind !== kind) {
|
||||
r.content = kind === 'record'
|
||||
? profilerEmptyRecordContent(r.content.mode)
|
||||
: { mode: r.content.mode, kind: 'transparent', expected: '' };
|
||||
}
|
||||
}
|
||||
profilerRenderEditor();
|
||||
}
|
||||
|
||||
function profilerSetContent(i, mode) {
|
||||
if (!profilerDraft) return;
|
||||
const r = profilerDraft.rules[i];
|
||||
@@ -7067,7 +7116,10 @@ function profilerSetContent(i, mode) {
|
||||
} else if (r.content) {
|
||||
r.content.mode = mode;
|
||||
} else {
|
||||
r.content = { mode: mode, kind: 'transparent', expected: '' };
|
||||
const kind = profilerContentKindForFileType(r.fileType);
|
||||
r.content = kind === 'record'
|
||||
? profilerEmptyRecordContent(mode)
|
||||
: { mode: mode, kind: 'transparent', expected: '' };
|
||||
}
|
||||
profilerRenderEditor();
|
||||
}
|
||||
@@ -7084,6 +7136,24 @@ function profilerUpdateRecord(i, ri, value) {
|
||||
if (c && c.records) c.records[ri].data = value;
|
||||
}
|
||||
|
||||
function profilerAddRecord(i) {
|
||||
if (!profilerDraft) return;
|
||||
const c = profilerDraft.rules[i].content;
|
||||
if (!c || c.kind !== 'record') return;
|
||||
const lastNum = c.records.length ? c.records[c.records.length - 1].num : 0;
|
||||
c.records.push({ num: lastNum + 1, data: '' });
|
||||
profilerRenderEditor();
|
||||
}
|
||||
|
||||
function profilerDeleteRecord(i, ri) {
|
||||
if (!profilerDraft) return;
|
||||
const c = profilerDraft.rules[i].content;
|
||||
if (!c || c.kind !== 'record') return;
|
||||
if (c.records.length <= 1) return;
|
||||
c.records.splice(ri, 1);
|
||||
profilerRenderEditor();
|
||||
}
|
||||
|
||||
function profilerSaveProfile() {
|
||||
if (!profilerDraft) return;
|
||||
profilerDraft.name = document.getElementById('profiler-name').value.trim();
|
||||
@@ -7143,7 +7213,8 @@ async function profilerRunRule(r) {
|
||||
if (!ok) res.status = 'fail';
|
||||
res.checks.push({ label: 'fileType', expected: r.fileType, actual: sel.file_type, ok: ok });
|
||||
}
|
||||
if (r.fileSize !== null && r.fileSize !== undefined) {
|
||||
const isRecord = profilerContentKindForFileType(r.fileType) === 'record';
|
||||
if (!isRecord && r.fileSize !== null && r.fileSize !== undefined) {
|
||||
const ok = sel.file_size === r.fileSize;
|
||||
if (!ok) { res.status = 'fail'; sizeMismatch = true; }
|
||||
res.checks.push({ label: 'fileSize', expected: r.fileSize, actual: sel.file_size, ok: ok });
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'otaman-v44';
|
||||
const CACHE = 'otaman-v47';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -21,7 +21,7 @@ function extractFunc(src, name) {
|
||||
return src.slice(m.index, i + 1);
|
||||
}
|
||||
|
||||
const FNS = ['profilerNormHex', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerValidateProfile'];
|
||||
const FNS = ['profilerNormHex', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile'];
|
||||
let code = '';
|
||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||
eval(code);
|
||||
@@ -74,6 +74,32 @@ test('profilerMaskPrefix4 keeps first 4 bytes and masks the rest', () => {
|
||||
assert.strictEqual(profilerMaskPrefix4('1234'), '1234');
|
||||
});
|
||||
|
||||
test('profilerFileFields maps file type to applicable fields', () => {
|
||||
assert.deepStrictEqual(profilerFileFields('transparent'), { size: true, records: false });
|
||||
assert.deepStrictEqual(profilerFileFields('ber_tlv'), { size: true, records: false });
|
||||
assert.deepStrictEqual(profilerFileFields('linear_fixed'), { size: false, records: true });
|
||||
assert.deepStrictEqual(profilerFileFields('cyclic'), { size: false, records: true });
|
||||
assert.deepStrictEqual(profilerFileFields('df'), { size: false, records: false });
|
||||
assert.deepStrictEqual(profilerFileFields(null), { size: true, records: true });
|
||||
assert.deepStrictEqual(profilerFileFields(''), { size: true, records: true });
|
||||
});
|
||||
|
||||
test('profilerContentKindForFileType', () => {
|
||||
assert.strictEqual(profilerContentKindForFileType('linear_fixed'), 'record');
|
||||
assert.strictEqual(profilerContentKindForFileType('cyclic'), 'record');
|
||||
assert.strictEqual(profilerContentKindForFileType('transparent'), 'transparent');
|
||||
assert.strictEqual(profilerContentKindForFileType('ber_tlv'), 'transparent');
|
||||
assert.strictEqual(profilerContentKindForFileType('df'), 'transparent');
|
||||
});
|
||||
|
||||
test('profilerEmptyRecordContent seeds one empty record row', () => {
|
||||
const c = profilerEmptyRecordContent('exact');
|
||||
assert.strictEqual(c.kind, 'record');
|
||||
assert.strictEqual(c.mode, 'exact');
|
||||
assert.strictEqual(c.records.length, 1);
|
||||
assert.deepStrictEqual(c.records[0], { num: 1, data: '' });
|
||||
});
|
||||
|
||||
test('profile validation', () => {
|
||||
assert.strictEqual(profilerValidateProfile(null), 'Not an object');
|
||||
assert.strictEqual(profilerValidateProfile({ name: 'x' }), 'Missing rules array');
|
||||
|
||||
Reference in New Issue
Block a user