parse trip.scheduledDays

This commit is contained in:
Jannis R 2022-11-16 15:18:12 +01:00
parent b6900a3ddb
commit a60083f8d1
No known key found for this signature in database
GPG key ID: 0FE83946296A88A5
7 changed files with 126 additions and 42 deletions

View file

@ -490,7 +490,8 @@ const createClient = (profile, userAgent, opt = {}) => {
polyline: false, // return a track shape? polyline: false, // return a track shape?
subStops: true, // parse & expose sub-stops of stations? subStops: true, // parse & expose sub-stops of stations?
entrances: true, // parse & expose entrances of stops/stations? entrances: true, // parse & expose entrances of stops/stations?
remarks: true // parse & expose hints & warnings? remarks: true, // parse & expose hints & warnings?
scheduledDays: false, // parse & expose dates trip is valid on?
}, opt) }, opt)
const req = profile.formatTripReq({profile, opt}, id, lineName) const req = profile.formatTripReq({profile, opt}, id, lineName)

View file

@ -19,6 +19,7 @@ const parseProductsBitmask = require('../parse/products-bitmask')
const parseIcon = require('../parse/icon') const parseIcon = require('../parse/icon')
const parseWhen = require('../parse/when') const parseWhen = require('../parse/when')
const parsePrognosisType = require('../parse/prognosis-type') const parsePrognosisType = require('../parse/prognosis-type')
const parseScheduledDays = require('../parse/scheduled-days')
const parseDeparture = require('../parse/departure') const parseDeparture = require('../parse/departure')
const parseArrival = require('../parse/arrival') const parseArrival = require('../parse/arrival')
const parseTrip = require('../parse/trip') const parseTrip = require('../parse/trip')
@ -85,6 +86,7 @@ const defaultProfile = {
parseIcon, parseIcon,
parseWhen, parseWhen,
parsePrognosisType, parsePrognosisType,
parseScheduledDays,
parseDeparture, parseDeparture,
parseArrival, parseArrival,
parseTrip, parseTrip,

View file

@ -1,40 +1,7 @@
'use strict' 'use strict'
const {DateTime} = require('luxon')
const findRemarks = require('./find-remarks') const findRemarks = require('./find-remarks')
// todo: DRY with parse/date-time.js
const parseDate = (date) => {
const res = {
year: parseInt(date.substr(-8, 4)),
month: parseInt(date.substr(-4, 2)),
day: parseInt(date.substr(-2, 2)),
}
if (!Number.isInteger(res.year) || !Number.isInteger(res.month) || !Number.isInteger(res.day)) {
throw new Error('invalid date format: ' + date)
}
return res
}
const parseScheduledDays = (sDaysB, fpB, fpE, profile) => {
sDaysB = Buffer.from(sDaysB, 'hex')
const res = Object.create(null)
const _fpB = parseDate(fpB)
let d = DateTime.fromObject({
zone: profile.timezone, locale: profile.locale,
year: _fpB.year, month: _fpB.month, day: _fpB.day,
hour: 0, minute: 0, second: 0, millisecond: 0
})
for (let b = 0; b < sDaysB.length; b++) {
for (let i = 0; i < 8; i++) {
res[d.toISODate()] = (sDaysB[b] & Math.pow(2, 7 - i)) > 0
d = d.plus({days: 1})
}
}
return res
}
// todo: c.conSubscr (e.g. `F`) // todo: c.conSubscr (e.g. `F`)
// todo: c.trfRes x vbb-parse-ticket // todo: c.trfRes x vbb-parse-ticket
// todo: c.sotRating, c.isSotCon, c.sotCtxt // todo: c.sotRating, c.isSotCon, c.sotCtxt
@ -70,13 +37,9 @@ const parseJourney = (ctx, j) => { // j = raw jouney
res.remarks = findRemarks(j.msgL).map(([remark]) => remark) res.remarks = findRemarks(j.msgL).map(([remark]) => remark)
} }
if (opt.scheduledDays) { if (opt.scheduledDays && j.sDays) {
// sDaysB is a bitmap mapping all days from fpB (first date of schedule) to fpE (last date in schedule). // todo [breaking]: rename to scheduledDates
const {sDaysB} = j.sDays res.scheduledDays = profile.parseScheduledDays(ctx, j.sDays)
const {fpB, fpE} = ctx.res
if (sDaysB && fpB && fpE) {
res.scheduledDays = parseScheduledDays(sDaysB, fpB, fpE, profile)
}
} }
return res return res

43
parse/scheduled-days.js Normal file
View file

@ -0,0 +1,43 @@
'use strict'
const {DateTime} = require('luxon')
// todo: DRY with parse/date-time.js
const parseDate = (date) => {
const res = {
year: parseInt(date.substr(-8, 4)),
month: parseInt(date.substr(-4, 2)),
day: parseInt(date.substr(-2, 2)),
}
if (!Number.isInteger(res.year) || !Number.isInteger(res.month) || !Number.isInteger(res.day)) {
throw new Error('invalid date format: ' + date)
}
return res
}
const parseScheduledDays = (ctx, sDays) => {
const {profile} = ctx
// sDaysB is a bitmap mapping all days from fpB (first date of schedule) to fpE (last date in schedule).
const {fpB, fpE} = ctx.res
if (!sDays.sDaysB || !fpB || !fpE) return null
const sDaysB = Buffer.from(sDays.sDaysB, 'hex')
const res = Object.create(null)
const _fpB = parseDate(fpB)
let d = DateTime.fromObject({
zone: profile.timezone, locale: profile.locale,
year: _fpB.year, month: _fpB.month, day: _fpB.day,
hour: 0, minute: 0, second: 0, millisecond: 0
})
for (let b = 0; b < sDaysB.length; b++) {
for (let i = 0; i < 8; i++) {
res[d.toISODate()] = (sDaysB[b] & Math.pow(2, 7 - i)) > 0
d = d.plus({days: 1})
}
}
return res
}
module.exports = parseScheduledDays

View file

@ -5,7 +5,7 @@ const maxBy = require('lodash/maxBy')
const last = require('lodash/last') const last = require('lodash/last')
const parseTrip = (ctx, t) => { // t = raw trip const parseTrip = (ctx, t) => { // t = raw trip
const {profile, res} = ctx const {profile, opt, res} = ctx
// pretend the trip is a leg in a journey // pretend the trip is a leg in a journey
const fakeLeg = { const fakeLeg = {
@ -29,6 +29,20 @@ const parseTrip = (ctx, t) => { // t = raw trip
delete trip.tripId delete trip.tripId
// todo [breaking]: delete trip.reachable // todo [breaking]: delete trip.reachable
if (opt.scheduledDays) {
const nrOfStopovers = t.stopL.length
// trips seem to use sDaysL[], journeys use sDays
const sDaysL = Array.isArray(t.sDaysL) ? t.sDaysL : []
const matchingSDays = sDaysL.filter((sDays) => {
return sDays.fLocIdx === 0 && sDays.tLocIdx === (nrOfStopovers - 1)
})
// if there are >1 sDays, we don't know how to interpret them
const sDays = matchingSDays.length === 1 ? matchingSDays[0] : null
// todo [breaking]: rename to scheduledDates
trip.scheduledDays = profile.parseScheduledDays(ctx, sDays)
}
if (res.planrtTS) { if (res.planrtTS) {
// todo [breaking]: remove here // todo [breaking]: remove here
trip.realtimeDataUpdatedAt = parseInt(res.planrtTS) trip.realtimeDataUpdatedAt = parseInt(res.planrtTS)

View file

@ -16,6 +16,7 @@ const opt = {
subStops: false, subStops: false,
entrances: true, entrances: true,
remarks: true, remarks: true,
scheduledDays: true,
when: '2021-10-28T09:28:00+02:00', when: '2021-10-28T09:28:00+02:00',
} }

View file

@ -44,6 +44,66 @@ module.exports = {
} }
], ],
occupancy: 'medium', occupancy: 'medium',
scheduledDays: {
'2021-10-22': true,
'2021-10-23': false,
'2021-10-24': false,
'2021-10-25': true,
'2021-10-26': true,
'2021-10-27': true,
'2021-10-28': true,
'2021-10-29': true,
'2021-10-30': false,
'2021-10-31': false,
'2021-11-01': true,
'2021-11-02': true,
'2021-11-03': true,
'2021-11-04': true,
'2021-11-05': true,
'2021-11-06': false,
'2021-11-07': false,
'2021-11-08': true,
'2021-11-09': true,
'2021-11-10': true,
'2021-11-11': true,
'2021-11-12': true,
'2021-11-13': false,
'2021-11-14': false,
'2021-11-15': true,
'2021-11-16': true,
'2021-11-17': true,
'2021-11-18': true,
'2021-11-19': true,
'2021-11-20': false,
'2021-11-21': false,
'2021-11-22': true,
'2021-11-23': true,
'2021-11-24': true,
'2021-11-25': true,
'2021-11-26': true,
'2021-11-27': false,
'2021-11-28': false,
'2021-11-29': true,
'2021-11-30': true,
'2021-12-01': true,
'2021-12-02': true,
'2021-12-03': true,
'2021-12-04': false,
'2021-12-05': false,
'2021-12-06': true,
'2021-12-07': true,
'2021-12-08': true,
'2021-12-09': true,
'2021-12-10': true,
'2021-12-11': false,
'2021-12-12': false,
'2021-12-13': false,
'2021-12-14': false,
'2021-12-15': false,
'2021-12-16': false,
},
realtimeDataUpdatedAt: 1635406146, realtimeDataUpdatedAt: 1635406146,
origin: { origin: {