feat(logs): Maschinenstunden pro Reisetag und Verbrauch pro Stunde
Maschinenstunden sind im Journal erfassbar; der Kraftstoffverbrauch pro Maschinenstunde wird aus Tagesverbrauch und Maschinenstunden berechnet und in Journal sowie Statistik als Read-only angezeigt. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -80,7 +80,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
const headers = [
|
||||
'Date', 'Day of Travel', 'Departure Port', 'Destination Port',
|
||||
'Skipper Signature', 'Crew Signature',
|
||||
'Track Distance (nm)', 'Track Max Speed (kn)', 'Track Avg Speed (kn)',
|
||||
'Track Distance (nm)', 'Track Max Speed (kn)', 'Track Avg Speed (kn)', 'Motor Hours (h)',
|
||||
'Event Time', 'MgK Course', 'RwK Course',
|
||||
'Wind Dir', 'Wind Str', 'Barometer (hPa)', 'Sea State',
|
||||
'Current', 'Heel Angle', 'Sails/Motor', 'Log (nm)', 'Distance (nm)',
|
||||
@@ -113,6 +113,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
const trackDist = entry.trackDistanceNm ?? '';
|
||||
const trackMax = entry.trackSpeedMaxKn ?? '';
|
||||
const trackAvg = entry.trackSpeedAvgKn ?? '';
|
||||
const motorH = entry.motorHours ?? '';
|
||||
const fwM = entry.freshwater?.morning ?? '';
|
||||
const fwR = entry.freshwater?.refilled ?? '';
|
||||
const fwE = entry.freshwater?.evening ?? '';
|
||||
@@ -128,7 +129,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
rows.push([
|
||||
dateVal, travelDay, dep, dest,
|
||||
signS, signC,
|
||||
trackDist, trackMax, trackAvg,
|
||||
trackDist, trackMax, trackAvg, motorH,
|
||||
'', '', '',
|
||||
'', '', '', '',
|
||||
'', '', '', '', '',
|
||||
@@ -144,7 +145,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
rows.push([
|
||||
dateVal, travelDay, dep, dest,
|
||||
signS, signC,
|
||||
trackDist, trackMax, trackAvg,
|
||||
trackDist, trackMax, trackAvg, motorH,
|
||||
ev.time || '', ev.mgk || '', ev.rwk || '',
|
||||
ev.windDirection || '', ev.windStrength || '', ev.windPressure || '', ev.seaState || '',
|
||||
ev.current || '', ev.heel || '', ev.sailsOrMotor || '', ev.logReading || '', ev.distance || '',
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface DemoDaySpec {
|
||||
filename: string
|
||||
freshwater: { morning: number; refilled: number; evening: number; consumption: number }
|
||||
fuel: { morning: number; refilled: number; evening: number; consumption: number }
|
||||
motorHours?: number
|
||||
events: Array<Record<string, string>>
|
||||
}
|
||||
|
||||
@@ -100,6 +101,7 @@ export function buildDemoDays(): DemoDaySpec[] {
|
||||
filename: 'laboe-damp.gpx',
|
||||
freshwater: { morning: 105, refilled: 25, evening: 110, consumption: 20 },
|
||||
fuel: { morning: 78, refilled: 0, evening: 70, consumption: 8 },
|
||||
motorHours: 1.5,
|
||||
events: [
|
||||
{
|
||||
time: '09:00',
|
||||
@@ -247,6 +249,9 @@ export function buildPublicDemoFixture(): PublicDemoFixture {
|
||||
entryPayload.trackSpeedMaxKn = stats.speedMaxKn
|
||||
entryPayload.trackSpeedAvgKn = stats.speedAvgKn
|
||||
}
|
||||
if (day.motorHours != null && day.motorHours > 0) {
|
||||
entryPayload.motorHours = day.motorHours
|
||||
}
|
||||
|
||||
entries.push(entryPayload as PublicDemoFixture['entries'][number])
|
||||
|
||||
@@ -303,6 +308,9 @@ export function buildDemoEntryPayloads(): Array<{
|
||||
entryPayload.trackSpeedMaxKn = stats.speedMaxKn
|
||||
entryPayload.trackSpeedAvgKn = stats.speedAvgKn
|
||||
}
|
||||
if (day.motorHours != null && day.motorHours > 0) {
|
||||
entryPayload.motorHours = day.motorHours
|
||||
}
|
||||
|
||||
return {
|
||||
entryId,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
parseEventDistanceNm,
|
||||
splitDistanceByPropulsion
|
||||
} from '../utils/propulsionStats.js'
|
||||
import { computeFuelPerMotorHour } from '../utils/fuelStats.js'
|
||||
|
||||
export type DistanceSource = 'gps' | 'events' | 'none'
|
||||
|
||||
@@ -27,6 +28,8 @@ export interface TravelDayStats {
|
||||
sailDistanceNm: number
|
||||
motorDistanceNm: number
|
||||
unknownPropulsionNm: number
|
||||
motorHours: number
|
||||
fuelPerMotorHourL: number | null
|
||||
hasGpsTrack: boolean
|
||||
}
|
||||
|
||||
@@ -59,12 +62,15 @@ export interface StatsTotals {
|
||||
sailDistanceNm: number
|
||||
motorDistanceNm: number
|
||||
unknownPropulsionNm: number
|
||||
totalMotorHours: number
|
||||
totalFuelL: number
|
||||
totalFreshwaterL: number
|
||||
avgDistancePerDayNm: number
|
||||
avgMotorHoursPerDay: number
|
||||
avgFuelPerDayL: number
|
||||
avgFreshwaterPerDayL: number
|
||||
fuelPerNmL: number | null
|
||||
fuelPerMotorHourL: number | null
|
||||
}
|
||||
|
||||
const TRACK_COLORS = [
|
||||
@@ -102,6 +108,7 @@ function buildTotals(days: TravelDayStats[]): StatsTotals {
|
||||
const sailDistanceNm = days.reduce((sum, d) => sum + d.sailDistanceNm, 0)
|
||||
const motorDistanceNm = days.reduce((sum, d) => sum + d.motorDistanceNm, 0)
|
||||
const unknownPropulsionNm = days.reduce((sum, d) => sum + d.unknownPropulsionNm, 0)
|
||||
const totalMotorHours = days.reduce((sum, d) => sum + d.motorHours, 0)
|
||||
const totalFuelL = days.reduce((sum, d) => sum + d.fuelConsumptionL, 0)
|
||||
const totalFreshwaterL = days.reduce((sum, d) => sum + d.freshwaterConsumptionL, 0)
|
||||
|
||||
@@ -112,10 +119,13 @@ function buildTotals(days: TravelDayStats[]): StatsTotals {
|
||||
sailDistanceNm: Number(sailDistanceNm.toFixed(2)),
|
||||
motorDistanceNm: Number(motorDistanceNm.toFixed(2)),
|
||||
unknownPropulsionNm: Number(unknownPropulsionNm.toFixed(2)),
|
||||
totalMotorHours: Number(totalMotorHours.toFixed(1)),
|
||||
totalFuelL: Number(totalFuelL.toFixed(1)),
|
||||
totalFreshwaterL: Number(totalFreshwaterL.toFixed(1)),
|
||||
avgDistancePerDayNm:
|
||||
travelDayCount > 0 ? Number((totalDistanceNm / travelDayCount).toFixed(2)) : 0,
|
||||
avgMotorHoursPerDay:
|
||||
travelDayCount > 0 ? Number((totalMotorHours / travelDayCount).toFixed(1)) : 0,
|
||||
avgFuelPerDayL:
|
||||
travelDayCount > 0 ? Number((totalFuelL / travelDayCount).toFixed(1)) : 0,
|
||||
avgFreshwaterPerDayL:
|
||||
@@ -123,7 +133,8 @@ function buildTotals(days: TravelDayStats[]): StatsTotals {
|
||||
fuelPerNmL:
|
||||
totalDistanceNm > 0 && totalFuelL > 0
|
||||
? Number((totalFuelL / totalDistanceNm).toFixed(2))
|
||||
: null
|
||||
: null,
|
||||
fuelPerMotorHourL: computeFuelPerMotorHour(totalFuelL, totalMotorHours)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +191,9 @@ async function loadTravelDaysForLogbook(
|
||||
hasGpsTrack = !!(await db.gpsTracks.get(entry.payloadId))
|
||||
}
|
||||
|
||||
const fuelConsumptionL = Number(payload.fuel?.consumption) || 0
|
||||
const motorHours = Number(payload.motorHours) || 0
|
||||
|
||||
days.push({
|
||||
entryId: entry.payloadId,
|
||||
logbookId,
|
||||
@@ -189,11 +203,13 @@ async function loadTravelDaysForLogbook(
|
||||
destination: payload.destination || '',
|
||||
distanceNm,
|
||||
distanceSource,
|
||||
fuelConsumptionL: Number(payload.fuel?.consumption) || 0,
|
||||
fuelConsumptionL,
|
||||
freshwaterConsumptionL: Number(payload.freshwater?.consumption) || 0,
|
||||
sailDistanceNm: propulsion.sailDistanceNm,
|
||||
motorDistanceNm: propulsion.motorDistanceNm,
|
||||
unknownPropulsionNm: propulsion.unknownPropulsionNm,
|
||||
motorHours,
|
||||
fuelPerMotorHourL: computeFuelPerMotorHour(fuelConsumptionL, motorHours),
|
||||
hasGpsTrack
|
||||
})
|
||||
}
|
||||
@@ -249,3 +265,7 @@ export function formatNm(value: number): string {
|
||||
export function formatLiters(value: number): string {
|
||||
return Number.isInteger(value) ? String(value) : value.toFixed(1)
|
||||
}
|
||||
|
||||
export function formatHours(value: number): string {
|
||||
return Number.isInteger(value) ? String(value) : value.toFixed(1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user