diff options
Diffstat (limited to 'bin/db-iris')
-rwxr-xr-x | bin/db-iris | 189 |
1 files changed, 142 insertions, 47 deletions
diff --git a/bin/db-iris b/bin/db-iris index 870c0ca..08906e2 100755 --- a/bin/db-iris +++ b/bin/db-iris @@ -1,19 +1,18 @@ -#!/usr/bin/env perl +#!perl use strict; use warnings; use 5.014; use utf8; -no if $] >= 5.018, warnings => 'experimental::smartmatch'; - -our $VERSION = '1.28'; +our $VERSION = '1.96'; use DateTime; use DateTime::Format::Strptime; -use Encode qw(decode); +use Encode qw(decode); use Getopt::Long qw(:config no_ignore_case bundling); -use List::Util qw(first max); -use List::MoreUtils qw(none); +use JSON; +use List::Util qw(first max); +use List::MoreUtils qw(any none); use Travel::Status::DE::IRIS; use Travel::Status::DE::IRIS::Stations; @@ -23,9 +22,12 @@ my $developer_mode = 0; my $lookahead = 2 * 60; my $realtime = 0; my $with_related = 1; -my ( $filter_via, $track_via, $status_via ); -my ( @grep_class, @grep_type, @grep_platform ); -my ( %edata, @edata_pre ); +my $json_output = 0; +my $use_cache = 1; +my ( $schedule_cache, $realtime_cache ); +my ( $filter_via, $track_via, $status_via ); +my ( @grep_class, @grep_type, @grep_platform ); +my ( %edata, @edata_pre ); my @output; @@ -46,7 +48,9 @@ GetOptions( 'v|via=s' => \$filter_via, 'V|track-via=s' => \$track_via, 'x|exact|no-related' => sub { $with_related = 0 }, + 'cache!' => \$use_cache, 'devmode' => \$developer_mode, + 'json' => \$json_output, 'version' => \&show_version, ) or show_help(1); @@ -115,19 +119,40 @@ if ($time) { } for my $efield (@edata_pre) { - given ($efield) { - when ('a') { $edata{additional} = 1 } - when ('c') { $edata{canceled} = 1 } - when ('d') { $edata{delay} = 1 } - when ('D') { $edata{delays} = 1 } - when ('f') { $edata{fullroute} = 1 } - when ('m') { $edata{messages} = 1 } - when ('q') { $edata{qos} = 1 } - when ('r') { $edata{route} = 1 } - when ('R') { $edata{replacements} = 1 } - when ('t') { $edata{times} = 1 } - when ('!') { $edata{debug} = 1 } - default { $edata{$efield} = 1 } + if ( $efield eq 'a' ) { $edata{additional} = 1 } + elsif ( $efield eq 'c' ) { $edata{canceled} = 1 } + elsif ( $efield eq 'd' ) { $edata{delay} = 1 } + elsif ( $efield eq 'D' ) { $edata{delays} = 1 } + elsif ( $efield eq 'f' ) { $edata{fullroute} = 1 } + elsif ( $efield eq 'm' ) { $edata{messages} = 1 } + elsif ( $efield eq 'q' ) { $edata{qos} = 1 } + elsif ( $efield eq 'r' ) { $edata{route} = 1 } + elsif ( $efield eq 'R' ) { $edata{replacements} = 1 } + elsif ( $efield eq 't' ) { $edata{times} = 1 } + elsif ( $efield eq '!' ) { $edata{debug} = 1 } + else { $edata{$efield} = 1 } +} + +if ($use_cache) { + my $cache_path = $ENV{XDG_CACHE_HOME} // "$ENV{HOME}/.cache"; + my $schedule_cache_path = "${cache_path}/db-iris-schedule"; + my $realtime_cache_path = "${cache_path}/db-iris-realtime"; + eval { + use Cache::File; + $schedule_cache = Cache::File->new( + cache_root => $schedule_cache_path, + default_expires => '6 hours', + lock_level => Cache::File::LOCK_LOCAL(), + ); + $realtime_cache = Cache::File->new( + cache_root => $realtime_cache_path, + default_expires => '180 seconds', + lock_level => Cache::File::LOCK_LOCAL(), + ); + }; + if ($@) { + $schedule_cache = undef; + $realtime_cache = undef; } } @@ -135,14 +160,18 @@ my $status = Travel::Status::DE::IRIS->new( datetime => $datetime, developer_mode => $developer_mode, lookahead => $lookahead, + main_cache => $schedule_cache, + realtime_cache => $realtime_cache, station => $station, with_related => $with_related, ); if ($track_via) { $status_via = Travel::Status::DE::IRIS->new( - datetime => $datetime, - station => $track_via, - lookahead => $lookahead + 3 * 60, + datetime => $datetime, + lookahead => $lookahead + 3 * 60, + main_cache => $schedule_cache, + realtime_cache => $realtime_cache, + station => $track_via, ); } @@ -206,7 +235,7 @@ sub get_station { exit(1); } elsif ( @stations == 1 ) { - return $stations[0][0]; + return $stations[0][2]; } else { say STDERR "The input '$input_name' is ambiguous. Please choose one " @@ -260,6 +289,9 @@ sub format_delay { elsif ( $d->departure_is_cancelled ) { $delay .= ' ⊖'; } + elsif ( $d->start < $datetime and not $d->has_realtime ) { + $delay = ' ?'; + } return $delay; } @@ -413,6 +445,11 @@ if ( $status_via and $status_via->warnstr ) { sanitize_options(); +if ($json_output) { + say JSON->new->convert_blessed->encode( [ $status->results ] ); + exit 0; +} + for my $d ( $status->results() ) { my @via; @@ -420,15 +457,33 @@ for my $d ( $status->results() ) { # route may be incomplete, so check route_end as well @via = ( $d->route_post, $d->route_end ); - if ( ( $filter_via and not( first { $_ =~ m{$filter_via}io } @via ) ) - or ( @grep_class and none { $_ ~~ \@grep_class } $d->classes ) - or ( @grep_platform and not( $d->platform ~~ \@grep_platform ) ) - or ( @grep_type and not( $d->type ~~ \@grep_type ) ) + if ( ( $filter_via and not( first { $_ =~ m{$filter_via}io } @via ) ) or $d->is_wing ) { next; } + if ( @grep_platform and none { $d->platform eq $_ } @grep_platform ) { + next; + } + + if ( @grep_type and none { $d->type eq $_ } @grep_type ) { + next; + } + + if (@grep_class) { + my $skip = 1; + for my $class ( $d->classes ) { + if ( any { $class eq $_ } @grep_class ) { + $skip = 0; + last; + } + } + if ($skip) { + next; + } + } + my $delay = format_delay($d); my $platformstr = $d->platform // q{}; @@ -441,8 +496,7 @@ for my $d ( $status->results() ) { my $d_via = first { $_->train_id eq $d->train_id or ( $_->old_train_id and $_->old_train_id eq $d->train_id ); - } - $status_via->results; + } $status_via->results; if ( not $d_via or not $d_via->sched_arrival or $d_via->sched_arrival < $d->departure ) @@ -494,7 +548,7 @@ for my $d ( $status->results() ) { push( @processed_wings, $wing->wing_id ); } for my $wing ( $d->arrival_wings ) { - if ( not $wing->wing_id ~~ \@processed_wings ) { + if ( none { $wing->wing_id eq $_ } @processed_wings ) { my $wingdelay = format_delay($wing); push( @output, @@ -533,24 +587,38 @@ B<db-iris> [B<-rx>] [B<-d> I<date>] [B<-o> I<output-flags>] =head1 VERSION -version 1.28 +version 1.96 =head1 DESCRIPTION db-iris is an interface to the DeutscheBahn departure monitor available at L<https://iris.noncd.db.de/wbt/js/index.html>. -It requests all departures at I<station> and lists them on stdout, similar to -the big departure screens installed at most main stations. I<station> can be -a DS100 station code (such as "EE"), a normal station name -(such as "Essen Hbf" or "Dortmund UniversitE<auml>t"), or an IBNR / european station -number (such as 8000098). If no exact match is found, B<db-iris> will try to -find station names similar to I<station>. +It requests all trains departing from (or arriving at) I<station> in the next +two hours and lists them on stdout. I<station> can be a DS100 station code +(such as "EE"), a normal station name (such as "Essen Hbf" or "Dortmund +UniversitE<auml>t"), or an IBNR / european station number (such as 8000098). If +no exact match is found, B<db-iris> will try to find station names similar to +I<station>. + +By default, db-iris shows the following data for each train: + +=over + +=item * scheduled departure time (see also B<-ot>, B<-r>). + +=item * delay in minutes, cancellation, or a question mark (C<< ? >>) +indicating that no real-time data is available. -An exclamation mark (C<< ! >>) next to a destination indicates that at least -one stop on the train's route has been canceled. Use B<-oc> to see the canceled -stops. An exclamation mark next to a platform indicates that it is not the -scheduled one. +=item * train line or number. + +=item * destination (see also B<-or>). An exclamation mark (C<< ! >>) +indicates that at least one stop has been cancelled (see B<-oc>). + +=item * platform. An exclamation mark (C<< ! >>) indicates that it is not +the scheduled one. + +=back =head1 OPTIONS @@ -575,16 +643,35 @@ or I<dd>.I<mm>.I<YYYY> format, or C<< tomorrow >>. Note that typically only slight (a few hours max) deviations from the current time are supported by the IRIS backend, larger ones will not return data. +=item B<--json> + +List results as JSON, see Travel::Status::DE::IRIS::Result(3pm) for a partial +documentation of arrival/departure keys. The B<--output> option has no effect +when using B<--json>. + +Note that JSON entries not mentioned in Travel::Status::DE::IRIS::Result(3pm) +are NOT guaranteed to be compatible between releases. Their structure is not +part of the db-iris / Travel::Status::DE::IRIS versioning scheme; it may change +in backwards-incompatible ways anytime. + =item B<-l>, B<--lookahead> I<int> Do not return results which are more than I<int> minutes in the future. -Defaults to 240 (4 hours). +Defaults to 120 (2 hours). Note that this is only an upper limit, not a guarantee to get every train with a departure in less than I<int> minutes. This guarantee holds only for I<int> below 120. However, any non-negative number is accepted for this option. +=item B<--no-cache> + +If the Cache::File module is available, server replies are cached in +F<~/.cache/db-iris-schedule> and F<~/.cache/db-iris-realtime> (or paths +relative to C<$XDG_CACHE_HOME>, if set). Use this option to disable caching +altogether. Note that this will significantly decrease db-iris responsiveness, +especially on mobile networks such as WifiOnICE. + =item B<-o>, B<--output> I<outputtypes> For each result, output I<outputtypes> in addition to the normal time, delay, @@ -757,13 +844,21 @@ None. =back +=head1 RECOMMENDS + +=over + +=item * Cache::File(3pm) + +=back + =head1 BUGS AND LIMITATIONS There are no known bugs at the moment. =head1 AUTHOR -Copyright (C) 2013-2019 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt> +Copyright (C) 2013-2024 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt> The station data used by this script is provided by DB Station&Service AG, Europaplatz 1, 10557 Berlin, Germany and available |