Fix monitor duration semantics

This commit is contained in:
seo
2026-06-15 19:33:58 +09:00
parent 7c2deb9ec1
commit 2eb396cdfe
2 changed files with 26 additions and 2 deletions
+9
View File
@@ -17,6 +17,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저
- 사용량/요금 표시와 보정 metadata
- 4.95초 UI timeout과 60초 사용량 정산 분리
- TCP 실패 reason과 마지막 수신 지연 표시
- 수집 공백을 반영한 차량 상태 경과 시간 표시
- 도어/경계/시동/공조 상태 해석
- service worker와 PWA icon 제공
@@ -27,6 +28,14 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저
- `monitor.php?mode=ajax`: monitor 상태 AJAX
- `monitor.php?mode=usage`: 데이터 사용량 AJAX
## Monitor 경과 시간
`monitor.php?mode=ajax``meta.state_duration`은 현재 화면에 표시되는 차량 상태가 연속으로 유지된 시간입니다. 시동 값만 보지 않고 주행, 시동, 원격시동 준비/실행 상태를 함께 비교하며, 중간 수집 공백이 `RECEIVE_GAP_LIMIT_SEC`를 넘으면 같은 상태로 이어진 것으로 보지 않습니다.
기존 시동 상태 기준 경과는 `meta.engine_state_duration`으로 분리했습니다. 시동이 꺼진 지 하루 이상 지난 상황에서도 화면 경과가 하루 단위로 튀지 않고, 실제 모니터링 데이터가 연속으로 유지된 시간만 보여주기 위한 구조입니다.
로그 테이블의 경과 계산은 `YYYY-MM-DD HH:MM:SS` 값을 브라우저 기본 파서에 맡기지 않고 로컬 시간으로 직접 파싱합니다. 브라우저별 UTC/로컬 해석 차이로 로그 경과가 흔들리는 문제를 줄입니다.
## 구성
- `api.php`: 차량 상태/제어 API
+17 -2
View File
@@ -572,7 +572,8 @@ if (isset($_GET['mode']) && $_GET['mode'] === 'ajax') {
'stale' => $isStale,
'stale_reason' => $staleReason,
'latest_tcp' => $latestTcp,
'state_duration' => current_engine_state_duration($pdo, $latest),
'state_duration' => current_state_duration($pdo, $latest),
'engine_state_duration' => current_engine_state_duration($pdo, $latest),
'poll_interval' => $isFastPollState ? 5 : 10,
'log_count' => count($logs),
'chart_count' => count($chart),
@@ -1775,13 +1776,27 @@ if (isset($_GET['mode']) && $_GET['mode'] === 'usage') {
});
}
function parseLocalTimestamp(value) {
const match = String(value || '').match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})/);
if (!match) return null;
return new Date(
Number(match[1]),
Number(match[2]) - 1,
Number(match[3]),
Number(match[4]),
Number(match[5]),
Number(match[6])
).getTime();
}
function updateLogs(logs) {
const tbody = document.getElementById('log-table-body');
const html = logs.map(log => {
const cmdCode = String(log.cmd || '').toLowerCase();
const cmd = CMD_MAP[cmdCode] || { label: '', icon: 'fa-terminal', bg: 'rgba(128,128,128,0.1)', color: 'var(--text-secondary)' };
const cmdText = cmd.label ? t(cmd.label) : (cmdCode.toUpperCase() || '-');
const ageSec = Math.max(0, Math.floor((Date.now() - new Date(log.ts).getTime()) / 1000));
const logMs = parseLocalTimestamp(log.ts);
const ageSec = logMs === null ? 0 : Math.max(0, Math.floor((Date.now() - logMs) / 1000));
const doors = doorSummary(log);
const remote = remoteSummary(log);
const trim = escapeHtml(log.raw_trim || '-');