diff --git a/logbook/log.css b/logbook/log.css index 81d6a30..c82137f 100644 --- a/logbook/log.css +++ b/logbook/log.css @@ -7,15 +7,21 @@ table { margin-bottom: 2em; } thead th, -thead td { +thead td, +tfoot th, +tfoot td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -thead td { +thead td, +tfoot td { min-width: 42vw; max-width: 42vw; } +tfoot { + font-size: smaller; +} td, th { padding: 2px; border: 0; diff --git a/logbook/render.js b/logbook/render.js index 9b0698b..c667232 100644 --- a/logbook/render.js +++ b/logbook/render.js @@ -1,67 +1,92 @@ const { render } = require('mustache'); const { Point } = require('where'); -const data = require('./2022-logs.json'); +const { load } = require('js-yaml'); const { readFile, writeFile } = require('node:fs/promises'); -let previousState = null; +function isEnd(entry, nextEntry) { + if (entry.text.indexOf('Anchored') !== -1) { + return true; + } + if (!entry.end) { + return false; + } + if (!nextEntry) { + return true; + } + const current = new Date(entry.datetime); + const next = new Date(nextEntry.datetime); + const hours = (next - current) / 3600000; + if (hours < 3) { + return false; + } + return true; +} -readFile('template.html', 'utf-8') - .then(template => { - return render(template, { - trips: data, - formatDate: () => (tmpl, render) => { - const d = new Date(render(tmpl)); - return `${d.getUTCDate()}.${d.getMonth()+1}. ${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')}Z`; - }, - formatTime: () => (tmpl, render) => { - const d = new Date(render(tmpl)); - return `${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')}`; - }, - formatWind: () => (tmpl, render) => { - const wind = render(tmpl); - if (wind === '0.0kt 000°') { - return 'n/a'; - } - return wind; - }, - formatBarometer: () => (tmpl, render) => { - const baro = render(tmpl); - return baro.split('.')[0]; - }, - formatCoordinates: () => (tmpl, render) => { - const [ lat, lon ] = render(tmpl).split(' '); - return new Point(parseFloat(lat), parseFloat(lon)).toString(); - }, - formatState: () => (tmpl, render) => { - const prev = previousState; - const state = render(tmpl); - previousState = state; - switch (state) { - case 'sailing': - if (prev === 'motoring') { - return 'Motor stopped, sails up'; - } - return 'Sails up'; - case 'motoring': - if (prev === 'sailing') { - return 'Motor started, sails down'; - } - if (prev === 'anchored') { - return 'Motor started, anchor up'; - } - return 'Motor started'; - case 'moored': - return 'Vessel stopped'; - case 'anchored': - return 'Anchored'; - default: - return state; - } - }, +readFile('2023-cruise.yml', 'utf-8') + .then((yml) => load(yml)) + .then((data) => { + const trips = []; + let currentTrip = { + events: [], + sailing: 0, + }; + let sailing = false; + data.forEach((entry, idx) => { + if (currentTrip.events.length === 0) { + currentTrip.start = entry.datetime; + } + if (entry.text.indexOf('sailing') !== -1) { + sailing = true; + } + if (entry.text.indexOf('Started main engine') !== -1) { + sailing = false; + } + currentTrip.events.push(entry); + if (isEnd(entry, data[idx + 1])) { + currentTrip.end = entry.datetime; + currentTrip.engineHours = (entry.engine.hours - currentTrip.events[0].engine.hours) + .toFixed(1); + currentTrip.miles = (entry.log - currentTrip.events[0].log).toFixed(1); + trips.push(currentTrip); + currentTrip = { + events: [], + sailing: 0, + }; + } }); + return readFile('template.html', 'utf-8') + .then(template => { + return render(template, { + trips, + formatDate: () => (tmpl, rdr) => { + const d = new Date(rdr(tmpl)); + return `${d.getUTCDate()}.${d.getMonth() + 1}. ${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')}Z`; + }, + formatTime: () => (tmpl, render) => { + const d = new Date(render(tmpl)); + return `${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')}`; + }, + formatWind: () => (tmpl, render) => { + const wind = render(tmpl); + if (wind === 'kt °') { + return 'n/a'; + } + return wind; + }, + formatBarometer: () => (tmpl, render) => { + const baro = render(tmpl); + return baro.split('.')[0]; + }, + formatCoordinates: () => (tmpl, render) => { + const [ lat, lon ] = render(tmpl).split(' '); + return new Point(parseFloat(lat), parseFloat(lon)).toString(); + }, + }); + }); }) .then(output => { - return writeFile('log.html', output, 'utf-8'); + writeFile('log.html', output, 'utf-8'); + console.log('Done'); }) .catch(e => { console.error(e.message); diff --git a/logbook/template.html b/logbook/template.html index 9082802..1d9f8b1 100644 --- a/logbook/template.html +++ b/logbook/template.html @@ -20,7 +20,7 @@ Course Speed Log - Wind + Weather Baro Coordinates Fix @@ -29,19 +29,35 @@ {{#events}} - - {{#formatTime}}{{ time }}{{/formatTime}} + + {{#formatTime}}{{ datetime }}{{/formatTime}} {{ heading }}° - {{ speed }}kt + {{ speed.sog }}kt {{ log }}NM - {{#formatWind}}{{ windSpeed }}kt {{ windDirection }}°{{/formatWind}} + + Wind {{#formatWind}}{{ wind.speed }}kt {{ wind.direction }}°{{/formatWind}} + {{#observations.seaState}}
Sea state {{observations.seaState}}{{/observations.seaState}} + {{#observations.cloudCoverage}}
Clouds {{observations.cloudCoverage}}/8{{/observations.cloudCoverage}} + {{#observations.visibility}}
Visibility {{observations.visibility}}{{/observations.visibility}} + {{#formatBarometer}}{{ barometer }}{{/formatBarometer}} hPa - {{#formatCoordinates}}{{ position.lat }} {{ position.lon }}{{/formatCoordinates}} - {{ fixType }} - {{^hourly}}{{#formatState}}{{ state }}{{/formatState}}{{/hourly}} + {{#formatCoordinates}}{{ position.latitude }} {{ position.longitude }}{{/formatCoordinates}} + {{ position.source }} + + {{#text}}{{ text }} {{#author}}
-{{author}}{{/author}}{{/text}} + {{^text}}Hourly entry{{/text}} + {{/events}} + + + Distance + {{ miles }}NM + Engine hours + {{ engineHours }}h + + {{/trips}}