fix: Implement dynamic age calculation for aircraft tracking

Resolves issue #3 where aircraft age field was always showing 0 seconds.

Backend Changes:
- Fix MarshalJSON in merger.go to calculate age dynamically using time.Since(a.LastUpdate)
- Add comprehensive tests for age calculation in merger_test.go

Frontend Changes:
- Add calculateAge() utility method to both aircraft-manager.js and ui-manager.js
- Update popup content and aircraft table to use client-side age calculation
- Implement real-time age updates every second in app.js
- Add updateOpenPopupAges() function to refresh popup displays

Key Benefits:
- Aircraft age now shows actual seconds since last transmission
- Real-time updates in UI without requiring new WebSocket data
- Proper data staleness indicators for aviation tracking
- No additional server load - client-side calculation using last_update timestamp

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2025-09-01 10:29:35 +02:00
commit 10508c2dc6
5 changed files with 145 additions and 3 deletions

View file

@ -491,7 +491,7 @@ export class AircraftManager {
<strong>Messages:</strong> ${aircraft.TotalMessages || 0}
</div>
<div class="detail-row">
<strong>Age:</strong> ${aircraft.Age ? aircraft.Age.toFixed(1) : '0'}s
<strong>Age:</strong> ${this.calculateAge(aircraft).toFixed(1)}s
</div>
</div>
</div>
@ -513,6 +513,16 @@ export class AircraftManager {
return minDistance === Infinity ? null : minDistance;
}
calculateAge(aircraft) {
if (!aircraft.last_update) return 0;
const lastUpdate = new Date(aircraft.last_update);
const now = new Date();
const ageMs = now.getTime() - lastUpdate.getTime();
return Math.max(0, ageMs / 1000); // Return age in seconds, minimum 0
}
// Enhance callsign display in popup after it's created
async enhanceCallsignDisplay(popupElement) {
if (!this.callsignManager) return;

View file

@ -129,7 +129,7 @@ export class UIManager {
<td>${aircraft.Track || '-'}°</td>
<td>${sources}</td>
<td><span class="${this.getSignalClass(bestSignal)}">${bestSignal ? bestSignal.toFixed(1) : '-'}</span></td>
<td>${aircraft.Age ? aircraft.Age.toFixed(0) : '0'}s</td>
<td>${this.calculateAge(aircraft).toFixed(0)}s</td>
`;
row.addEventListener('click', () => {
@ -161,6 +161,16 @@ export class UIManager {
return 'commercial';
}
calculateAge(aircraft) {
if (!aircraft.last_update) return 0;
const lastUpdate = new Date(aircraft.last_update);
const now = new Date();
const ageMs = now.getTime() - lastUpdate.getTime();
return Math.max(0, ageMs / 1000); // Return age in seconds, minimum 0
}
getBestSignalFromSources(sources) {
if (!sources) return null;
let bestSignal = -999;