#!/usr/bin/env perl
use strict;
use 5.020;
use utf8;
our $VERSION = '0.18';
use List::Util qw(min);
my $developer_mode = 0;
binmode( STDOUT, ':encoding(utf-8)' );
sub show_help {
my ($code) = @_;
say "Usage: db-wagenreihung <station> <train number>";
exit $code;
}
sub show_version {
say "db-wagenreihung version ${VERSION}";
exit 0;
}
GetOptions(
'h|help' => sub { show_help(0) },
'devmode' => \$developer_mode,
'version' => sub { show_version() },
) or show_help(1);
if ( @ARGV != 2 ) {
show_help(1);
}
my ( $station, $train_number ) = @ARGV;
my $col_first = "\e[38;5;11m";
my $col_mixed = "\e[38;5;208m";
my $col_second = "\e[0m"; #"\e[38;5;9m";
my $col_reset = "\e[0m";
my $res = Travel::Status::DE::IRIS->new(
developer_mode => $developer_mode,
station => $station,
with_related => 1
);
if ( $res->errstr ) {
say STDERR $res->errstr;
exit 1;
}
my @trains = grep { $_->train_no eq $train_number } $res->results;
if ( @trains != 1 ) {
say STDERR "Unable to find train in reported departures";
exit 1;
}
my $wr = Travel::Status::DE::DBWagenreihung->new(
developer_mode => $developer_mode,
departure => $trains[0]->sched_departure || $trains[0]->sched_arrival,
eva => $trains[0]->station_eva,
train_type => $trains[0]->type,
train_number => $train_number,
);
if ( $wr->errstr ) {
say STDERR $wr->errstr;
exit 2;
}
printf(
"%s → %s\n",
join(
' / ', map { $wr->train_type . ' ' . $_->{name} } $wr->train_numbers
),
join(
' / ',
map {
sprintf( '%s (%s)', $_->{name}, join( q{}, @{ $_->{sectors} } ) )
} $wr->destinations
),
);
printf( "Gleis %s\n\n", $wr->platform );
for my $sector ( $wr->sectors ) {
my $sector_length = $sector->length_percent;
my $spacing_left = int( ( $sector_length - 2 ) / 2 ) - 1;
my $spacing_right = int( ( $sector_length - 2 ) / 2 );
if ( $sector_length % 2 ) {
$spacing_left++;
}
printf( "▏%s%s%s▕",
( $spacing_left >= 0 ) ? ' ' x $spacing_left : q{},
$sector->name, ( $spacing_right >= 0 ) ? ' ' x $spacing_right : q{} );
}
print "\n";
my @start_percentages = map { $_->start_percent } $wr->carriages;
if ( my $min_percentage = min @start_percentages ) {
print ' ' x ( $min_percentage - 1 );
}
print $wr->direction == 100 ? '>' : '<';
for my $wagon ( $wr->carriages ) {
my $wagon_length = $wagon->length_percent;
my $spacing_left = int( $wagon_length / 2 ) - 2;
my $spacing_right = int( $wagon_length / 2 ) - 1;
if ( $wagon_length % 2 ) {
$spacing_left++;
}
my $wagon_desc = $wagon->number || '?';
if ( $wagon->is_closed ) {
$wagon_desc = 'X';
}
if ( $wagon->is_locomotive or $wagon->is_powercar ) {
$wagon_desc = ' ■ ';
}
my $class_color = '';
if ( $wagon->class_type == 1 ) {
$class_color = $col_first;
}
elsif ( $wagon->class_type == 2 ) {
$class_color = $col_second;
}
elsif ( $wagon->class_type == 12 ) {
$class_color = $col_mixed;
}
printf( "%s%s%3s%s%s",
' ' x $spacing_left, $class_color, $wagon_desc,
$col_reset, ' ' x $spacing_right );
}
print $wr->direction == 100 ? '>' : '<';
print "\n\n";
for my $group ( $wr->groups ) {
printf( "%s%s%s\n",
$group->description || 'Zug',
$group->designation ? ' „' . $group->designation . '“' : q{},
$group->has_sectors ? ' (' . join( q{}, $group->sectors ) . ')' : q{} );
printf( "%s %s → %s\n\n",
$group->train_type, $group->train_no, $group->destination );
for my $wagon ( $group->carriages ) {
printf(
"%3s: %3s %10s %s\n",
$wagon->is_closed ? 'X'
: $wagon->is_locomotive ? 'Lok'
: $wagon->number || '?',
$wagon->model || '???',
$wagon->type,
join( q{ }, $wagon->attributes )
);
}
say "";
}
__END__
=head1 NAME
db-wagenreihung - Interface to Deutsche Bahn carriage formation API
=head1 SYNOPSIS
B<db-wagenreihung> I<station> I<train-number>
=head1 VERSION
version 0.18
This is beta software: API and output format may change without notice.
=head1 DESCRIPTION
db-wagenreihung shows the carriage formation (also known as coach sequence) of
train I<train-number> at station I<station> (must be a name or DS100 code) as
reported by the Deutsche Bahn Wagenreihung API. As of April 2024, it has mature
support for long-distance (IC/EC/ICE) trains and a growing number of regional
transport providers that also offer mostly correct carriage formation data.
It is not possible to request the carriage formation at a train's terminus
station. This is a known limitation.
The departure of I<train-number> must be in the time range between now and
two hours in the future.
=head1 EXAMPLES
=over
=item db-wagenreihung 'Essen Hbf' 723
Show carriage formation of ICE 723 at Essen Hbf
=item db-wagenreihung TS 3259
Show carriage formation of IRE 3259 at Stuttgart Hbf
=back
=head1 DEPENDENCIES
=over
=item * JSON(3pm)
=item * LWP::UserAgent(3pm)
=item * Travel::Status::DE::IRIS(3pm)
=back
=head1 AUTHOR
Copyright (C) 2018-2024 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
This program is licensed under the same terms as Perl itself.