diff options
Diffstat (limited to 'bin/efa-m')
-rwxr-xr-x | bin/efa-m | 223 |
1 files changed, 152 insertions, 71 deletions
@@ -4,25 +4,22 @@ use warnings; use 5.010; use utf8; -no if $] >= 5.018, warnings => 'experimental::smartmatch'; - -our $VERSION = '1.17'; +our $VERSION = '2.00'; binmode( STDOUT, ':encoding(utf-8)' ); -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::Util qw(first max none); use Travel::Status::DE::EFA; my $efa_url = 'https://efa.vrr.de/vrr/XSLT_DM_REQUEST'; my $efa_encoding; -my ( $date, $time, $input_type, $list_lines, $offset, $relative_times ); -my ($full_routes); -my ( $filter_via, $track_via ); -my ( $timeout, $developer_mode ); -my ( @grep_lines, @grep_platforms, @grep_mots ); -my ( %edata, @edata_pre ); +my ( $date, $time, $input_type, $list_lines, $offset, $relative_times ); +my ( $full_routes, $filter_via ); +my ( $timeout, $developer_mode ); +my ( @grep_lines, @grep_platforms, @grep_mots ); +my ( %edata, @edata_pre ); my ( $list_services, $service, $discover_and_print, $discover ); my $efa; @@ -46,7 +43,7 @@ GetOptions( 'timeout=i' => \$timeout, 'u|efa-url=s' => \$efa_url, 'v|via=s' => \$filter_via, - 'V|track-via=s' => sub { $filter_via = $track_via = $_[1] }, + 'V|track-via=s' => \$filter_via, 'version' => \&show_version, 'devmode' => \$developer_mode, @@ -56,7 +53,7 @@ if ($list_services) { show_services(); } -if ( @ARGV != 2 ) { +if ( @ARGV < 1 or @ARGV > 2 ) { show_help(1); } @@ -66,20 +63,26 @@ if ( @ARGV != 2 ) { @grep_mots = split( qr{,}, join( q{,}, @grep_mots ) ); @grep_platforms = split( qr{,}, join( q{,}, @grep_platforms ) ); -my ( $place, $input ) = @ARGV; +my ( $place, $input ); -if ( $input =~ s{ ^ (?<type> address|poi|stop) : }{}x ) { +if ( @ARGV == 1 ) { + $input = $ARGV[0]; +} +else { + ( $place, $input ) = @ARGV; +} + +if ( $input =~ s{ ^ (?<type> address|poi|stop|stopID) : }{}x ) { $input_type = $+{type}; } for my $efield (@edata_pre) { - given ($efield) { - when ('a') { $edata{route_after} = 1; $full_routes = 1 } - when ('b') { $edata{route_before} = 1; $full_routes = 1 } - when ('f') { $edata{fullroute} = 1; $full_routes = 1 } - when ('r') { $edata{route} = 1; $full_routes = 1 } - default { $edata{$efield} = 1 } - } + if ( $efield eq 'a' ) { $edata{route_after} = 1; $full_routes = 1 } + elsif ( $efield eq 'b' ) { $edata{route_before} = 1; $full_routes = 1 } + elsif ( $efield eq 'f' ) { $edata{fullroute} = 1; $full_routes = 1 } + elsif ( $efield eq 'r' ) { $edata{route} = 1; $full_routes = 1 } + elsif ( $efield eq 'm' ) { $edata{messages} = 1 } + else { $edata{$efield} = 1 } } if ($filter_via) { $full_routes = 1; @@ -87,7 +90,7 @@ if ($filter_via) { if ($service) { my $service_ref = first { lc( $_->{shortname} ) eq lc($service) } - Travel::Status::DE::EFA::get_efa_urls(); + Travel::Status::DE::EFA::get_efa_urls(); if ( not $service_ref ) { printf STDERR ( "Error: Unknown service '%s'. See 'efa-m --list' for a " @@ -121,7 +124,7 @@ sub new_efa_by_url { sub show_help { my ($code) = @_; - print "Usage: efa-m [-d <dd.mm.yyyy>] [-t <hh:mm>] <city> <station>\n" + print "Usage: efa-m [-d <dd.mm.yyyy>] [-t <hh:mm>] [place] <station>\n" . "See also: man efa-m\n"; exit $code; @@ -142,6 +145,14 @@ sub show_version { exit 0; } +sub format_delay { + my ( $delay, $len ) = @_; + if ( $delay and $len ) { + return sprintf( "(%+${len}d)", $delay ); + } + return q{}; +} + sub format_route { my (@route) = @_; @@ -152,23 +163,35 @@ sub format_route { say 'BUG'; next; } - if ( not defined $stop->arr_time ) { - $output .= sprintf( " %5s %40s %s\n", - $stop->dep_time, $stop->name, $stop->platform, ); - } - elsif ( not defined $stop->dep_time ) { - $output .= sprintf( "%5s %40s %s\n", - $stop->arr_time, $stop->name, $stop->platform, ); + if ( defined $stop->arr and defined $stop->dep ) { + if ( $stop->arr->epoch == $stop->dep->epoch ) { + $output .= sprintf( + " %5s %40s %s\n", + $stop->arr->strftime('%H:%M'), + $stop->name, $stop->platform, + ); + } + else { + $output .= sprintf( + "%5s → %5s %40s %s\n", + $stop->arr->strftime('%H:%M'), + $stop->dep->strftime('%H:%M'), + $stop->name, $stop->platform, + ); + } } - elsif ( $stop->arr_time eq $stop->dep_time ) { - $output .= sprintf( " %5s %40s %s\n", - $stop->dep_time, $stop->name, $stop->platform, ); + elsif ( defined $stop->arr ) { + $output .= sprintf( + "%5s %40s %s\n", + $stop->arr->strftime('%H:%M'), + $stop->name, $stop->platform, + ); } - else { + elsif ( defined $stop->dep ) { $output .= sprintf( - "%5s → %5s %40s %s\n", - $stop->arr_time, $stop->dep_time, - $stop->name, $stop->platform, + " %5s %40s %s\n", + $stop->dep->strftime('%H:%M'), + $stop->name, $stop->platform, ); } } @@ -190,7 +213,7 @@ sub display_result { for my $line (@lines) { - if ( length( $line->[5] ) ) { + if ( $edata{messages} and length( $line->[5] ) ) { $line->[5] =~ tr{\n\x0d}{ }s; chomp $line->[5]; print "\n"; @@ -217,13 +240,13 @@ sub show_lines { for my $l ( $efa->lines ) { - if ( ( @grep_lines and not( $l->name ~~ \@grep_lines ) ) - or ( @grep_mots and not( $l->mot_name ~~ \@grep_mots ) ) ) + if ( ( @grep_lines and none { $l->name eq $_ } @grep_lines ) + or ( @grep_mots and none { $l->mot_name eq $_ } @grep_mots ) ) { next; } - if ( @grep_mots and not( $l->mot_name ~~ \@grep_mots ) ) { + if ( @grep_mots and none { $l->mot_name eq $_ } @grep_mots ) { next; } @@ -240,6 +263,17 @@ sub show_lines { sub show_results { my @output; + my $delay_len = 0; + my $delay_fmt = 0; + for my $d ( $efa->results ) { + if ( $d->delay ) { + $delay_len = max( $delay_len, length( $d->delay ) + 1 ); + } + } + if ($delay_len) { + $delay_fmt = $delay_len + 3; + } + for my $d ( $efa->results ) { my @output_line; @@ -247,7 +281,7 @@ sub show_results { my $dtime = ( $relative_times ? sprintf( '%2d min', $d->countdown ) - : $d->sched_time + : $d->datetime->strftime('%H:%M') ); if ( $d->platform_db ) { @@ -255,10 +289,10 @@ sub show_results { } if ( - ( @grep_lines and not( $d->line ~~ \@grep_lines ) ) - or ( @grep_mots and not( $d->mot_name ~~ \@grep_mots ) ) + ( @grep_lines and none { $d->line eq $_ } @grep_lines ) + or ( @grep_mots and none { $d->mot_name eq $_ } @grep_mots ) or ( @grep_platforms - and not( $platform ~~ \@grep_platforms ) ) + and none { $platform eq $_ } @grep_platforms ) or ( $offset and $d->countdown < $offset ) or ( $filter_via and @@ -273,29 +307,46 @@ sub show_results { next; } else { - $dtime .= ' CANCELED'; + $dtime = '--:--'; } } - elsif ($track_via) { + elsif ($filter_via) { my $via = first { $_->name =~ m{$filter_via}io } $d->route_post; - $dtime .= ' → ' . $via->arr_time; + $dtime + .= ' → ' + . $via->arr->clone->add( minutes => $d->delay // 0 ) + ->strftime('%H:%M'); } if ( $d->delay ) { - if ($relative_times) { - $dtime .= ' (+' . $d->delay . ')'; - } - else { - $dtime .= ' +' . $d->delay; - } + $dtime .= ' ' . format_delay( $d->delay, $delay_len ); + } + + my $line = $d->line; + if ( length($line) > 10 and $d->train_type and $d->train_no ) { + $line = $d->train_type . ' ' . $d->train_no; } @output_line - = ( $dtime, $platform, $d->line, q{}, $d->destination, $d->info ); + = ( $dtime, $platform, $line, q{}, $d->destination, $d->info ); if ( $edata{route} ) { $output_line[3] = join( q{ }, map { $_->name_suf } $d->route_interesting ); } + elsif ( $d->occupancy ) { + if ( $d->occupancy eq 'MANY_SEATS' ) { + $output_line[3] = '_'; + } + elsif ( $d->occupancy eq 'FEW_SEATS' ) { + $output_line[3] = '*'; + } + elsif ( $d->occupancy eq 'STANDING_ONLY' ) { + $output_line[3] = '!'; + } + else { + $output_line[3] = '?'; + } + } if ( $edata{fullroute} ) { $output_line[6] @@ -371,17 +422,38 @@ efa-m - Unofficial interface to the efa.vrr.de departure monitor B<efa-m> [B<-Lr>] [B<-d> I<dd.mm.yyyy>] [B<-t> I<hh:mm>] [B<-l> I<lines>] [B<-p> I<platforms>] [B<-u> I<url>] -I<city> [I<type>B<:>]I<name> +[I<city>] [I<type>B<:>]I<name> =head1 VERSION -version 1.17 +version 2.00 =head1 DESCRIPTION -B<efa-m> lists scheduled tram, bus and train departures at the location I<name> -in I<city>. Realtime data (i.e. delays) is included if available, it's -visible in the output as a "+x" remark (meaning a delay of x minutes). +B<efa-m> lists scheduled tram, bus and train departures at the location I<name>. + +For each departure, it shows + +=over + +=item * scheduled departure time, + +=item * delay in minutes, + +=item * platform, + +=item * line, + +=item * expected occupation (from _ to !, if available), and + +=item * destination. + +=back + +If I<city> is specified, I<name> refers to a location within I<city>. Otherwise, +I<name> must be self-contained. I.e., both C<< efa Essen Hbf >> and +C<< efa "Essen Hbf" >> are valid. Note, however, than C<< efa E Hbf >> works, +but C<< efa "E Hbf" >> does not. By default, I<name> refers to a stop, this can be changed by specifying I<type>. Supported types are B<address> and B<poi> (point of interest). @@ -471,6 +543,12 @@ Show up to three stops between the requested station and the departure's destination. B<efa-m> tries to display the three most important stops, however these are heuristically determined and may not be optimal. +=item m / messages + +Show free-text messages associated with individual departures. These can +include generic information such is bicycle transportation options or Wi-Fi +availability, delay reasons, and more. + =back =item B<-p>, B<--platform> I<platforms> @@ -507,15 +585,10 @@ value to disable it. =item B<-v>, B<--via> I<station> -Only show trains serving I<station> after the requseted stop. I<station> -is matched against the "I<city> I<stop>" fields in each line's route. -Regular expressions are also supported. - -=item B<-V>, B<--track-via> I<station> - -Lik B<--via>: Only show trains serving I<station> after the requseted stop. -Also, show the arrival time at I<station> after the departure time at the -current stop. +Only show trains serving I<station> after the requseted stop, and show the +arrival time at I<station> after the departure time at the current stop. +I<station> is matched against the "I<city> I<stop>" fields in each line's +route. Regular expressions are also supported. =item B<--version> @@ -555,9 +628,17 @@ choose the appropriate EFA URL for these by itself. In these cases, you should find an appropriate EFA service using the B<-D>/B<--discover> option and then use B<-s> I<service> when making requests. +=over + +=item * EFA does not provide real-time data for the routes of requested +departures. Hence, B<--via> estimates the arrival time from scheduled +departure and departure delay + +=back + =head1 AUTHOR -Copyright (C) 2011-2020 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt> +Copyright (C) 2011-2023 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt> =head1 LICENSE |