Show absolute wall-clock time in the APDU detail heading
Next to the relative elapsed time, the detail-view header now shows the absolute local time (YYYY-MM-DD HH:MM:SS.mmm) computed as session start + message elapsed offset. - STATE.sessionStarted populated from /api/capture/start (live capture), data.session.started (session view), and PCAP imports (sessions.started already derives from the first packet timestamp when real). - Graceful fallback to relative-only when the session start is unknown; applies to all detail header variants (gap, no-decoded, state change, Fi/Di, ATR/PPS, APDU). Version 1.17.0, SW cache simtrace-analyser-v24.
This commit is contained in:
1 parent
4541cc66c5
commit
07deab9c26
5 files changed
+31
-11
No files matched your search
+27
-7
@@ -372,6 +372,7 @@ const STATE = {
|
||||
currentSessionId: null,
|
||||
currentSessionName: null,
|
||||
sessionViewId: null,
|
||||
sessionStarted: null,
|
||||
livePoller: null,
|
||||
messages: [],
|
||||
typeCounts: [],
|
||||
@@ -467,6 +468,23 @@ function fmt_elapsed(e) {
|
||||
return `${String(s).padStart(4, '0')}.${String(ms).padStart(3, '0')}`;
|
||||
}
|
||||
|
||||
function fmt_absolute(e) {
|
||||
// Absolute wall-clock time of a message: session/capture start
|
||||
// (sessions.started, local time) + message elapsed offset.
|
||||
if (!STATE.sessionStarted) return null;
|
||||
const t = new Date(STATE.sessionStarted);
|
||||
if (isNaN(t)) return null;
|
||||
t.setMilliseconds(t.getMilliseconds() + Math.round(e * 1000));
|
||||
const p = (n, w) => String(n).padStart(w || 2, '0');
|
||||
return `${t.getFullYear()}-${p(t.getMonth() + 1)}-${p(t.getDate())} ` +
|
||||
`${p(t.getHours())}:${p(t.getMinutes())}:${p(t.getSeconds())}.${p(t.getMilliseconds(), 3)}`;
|
||||
}
|
||||
|
||||
function detail_time_html(msg) {
|
||||
const abs = fmt_absolute(msg.elapsed);
|
||||
return abs ? `${fmt_elapsed(msg.elapsed)} · ${abs}` : fmt_elapsed(msg.elapsed);
|
||||
}
|
||||
|
||||
function fmt_data(hex, limit) {
|
||||
limit = limit || 32;
|
||||
const pairs = (hex || '').match(/.{1,2}/g) || [];
|
||||
@@ -920,19 +938,19 @@ function showDetail(msg, entry) {
|
||||
const decoded = msg.decoded;
|
||||
|
||||
if (msg.type === 'gap') {
|
||||
d.innerHTML = `<div class="detail-header"><strong>Capture Gap</strong><span style="color:var(--text-muted)">${fmt_elapsed(msg.elapsed)}</span></div>
|
||||
d.innerHTML = `<div class="detail-header"><strong>Capture Gap</strong><span style="color:var(--text-muted)">${detail_time_html(msg)}</span></div>
|
||||
<div class="detail-body" style="color:var(--warn);">⚠ ${t('The device disconnected and reconnected — some APDUs may be missing from this trace.')}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decoded) {
|
||||
d.innerHTML = `<div class="detail-header"><strong>${ins_label(msg)}</strong><span style="color:var(--text-muted)">${fmt_elapsed(msg.elapsed)}</span></div>
|
||||
d.innerHTML = `<div class="detail-header"><strong>${ins_label(msg)}</strong><span style="color:var(--text-muted)">${detail_time_html(msg)}</span></div>
|
||||
<div class="detail-body" style="color:var(--text-muted);">No decoded data available for this message type.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (decoded.type === 'change') {
|
||||
d.innerHTML = `<div class="detail-header"><strong>State Change</strong><span style="color:var(--text-muted)">${fmt_elapsed(msg.elapsed)}</span></div>
|
||||
d.innerHTML = `<div class="detail-header"><strong>State Change</strong><span style="color:var(--text-muted)">${detail_time_html(msg)}</span></div>
|
||||
<div class="detail-body">
|
||||
<table>
|
||||
<tr><td class="label">Flags</td><td class="value">${decoded.flags.join(', ') || 'no changes'}</td></tr>
|
||||
@@ -942,7 +960,7 @@ function showDetail(msg, entry) {
|
||||
}
|
||||
|
||||
if (decoded.type === 'fidi') {
|
||||
d.innerHTML = `<div class="detail-header"><strong>Fi/Di Change</strong><span style="color:var(--text-muted)">${fmt_elapsed(msg.elapsed)}</span></div>
|
||||
d.innerHTML = `<div class="detail-header"><strong>Fi/Di Change</strong><span style="color:var(--text-muted)">${detail_time_html(msg)}</span></div>
|
||||
<div class="detail-body">
|
||||
<table>
|
||||
<tr><td class="label">Fi</td><td class="value">${decoded.fi_val} (index ${decoded.fi})</td></tr>
|
||||
@@ -955,7 +973,7 @@ function showDetail(msg, entry) {
|
||||
|
||||
if (decoded.type === 'atr' || decoded.type === 'pps') {
|
||||
const rows = atrPpsRows(decoded);
|
||||
d.innerHTML = `<div class="detail-header"><strong>${decoded.type.toUpperCase()}</strong><span style="color:var(--text-muted)">${fmt_elapsed(msg.elapsed)}</span></div>
|
||||
d.innerHTML = `<div class="detail-header"><strong>${decoded.type.toUpperCase()}</strong><span style="color:var(--text-muted)">${detail_time_html(msg)}</span></div>
|
||||
<div class="detail-body">
|
||||
<table>
|
||||
${rows.map(([l, v]) => `<tr><td class="label">${l}</td><td class="hex">${v}</td></tr>`).join('')}
|
||||
@@ -1024,7 +1042,7 @@ function showDetail(msg, entry) {
|
||||
|
||||
d.innerHTML = `<div class="detail-header">
|
||||
<strong>${decoded.cat_command || decoded.ins_name}</strong>
|
||||
<span style="color:var(--text-muted)">${fmt_elapsed(msg.elapsed)}</span>
|
||||
<span style="color:var(--text-muted)">${detail_time_html(msg)}</span>
|
||||
</div>
|
||||
<div class="detail-body">
|
||||
<table>
|
||||
@@ -1466,6 +1484,7 @@ async function startCapture() {
|
||||
const data = await API.startCapture();
|
||||
STATE.captureActive = true;
|
||||
STATE.currentSessionId = data.session_id;
|
||||
STATE.sessionStarted = data.started;
|
||||
STATE.messages = [];
|
||||
_lastAfter = 0;
|
||||
STATE.mapExpanded.clear();
|
||||
@@ -1623,6 +1642,7 @@ async function loadSession(id) {
|
||||
|
||||
const data = await API.getSession(id, 0);
|
||||
STATE.sessionViewId = id;
|
||||
STATE.sessionStarted = data.session.started;
|
||||
STATE.captureActive = false;
|
||||
STATE.currentSessionName = data.session.name || t('Session #') + id;
|
||||
STATE.messages = data.messages;
|
||||
@@ -1881,7 +1901,7 @@ function onFilterChange() {
|
||||
}
|
||||
|
||||
// ──────────────────── Versioning ────────────────────
|
||||
const PWA_VERSION = '1.16.1';
|
||||
const PWA_VERSION = '1.17.0';
|
||||
const MIN_SERVER_VERSION = '1.0.0';
|
||||
|
||||
function parseVersion(v) {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simtrace-analyser-v23';
|
||||
const CACHE = 'simtrace-analyser-v24';
|
||||
|
||||
const PRECACHE = [
|
||||
'index.html',
|
||||
|
||||
Reference in new issue
Block a user