#!/usr/bin/env perl use strict; use warnings; use 5.020; use utf8; our $VERSION = '0.13'; use Getopt::Long; use List::Util qw(min); use Travel::Status::DE::IRIS; use Travel::Status::DE::DBWagenreihung; my $developer_mode = 0; binmode( STDOUT, ':encoding(utf-8)' ); sub show_help { my ($code) = @_; say "Usage: db-wagenreihung "; 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( departure => $trains[0]->sched_departure || $trains[0]->sched_arrival, developer_mode => $developer_mode, train_number => $train_number, ); printf( "%s: %s → %s\n", join( ' / ', map { $wr->train_type . ' ' . $_ } $wr->train_numbers ), join( ' / ', map { sprintf( '%s (%s)', $_->{name}, join( q{}, @{ $_->{sections} } ) ) } $wr->origins ), join( ' / ', map { sprintf( '%s (%s)', $_->{name}, join( q{}, @{ $_->{sections} } ) ) } $wr->destinations ), ); printf( "%s Gleis %s\n\n", $wr->station->{name}, $wr->platform ); for my $section ( $wr->sections ) { my $section_length = $section->length_percent; my $spacing_left = int( ( $section_length - 2 ) / 2 ) - 1; my $spacing_right = int( ( $section_length - 2 ) / 2 ); if ( $section_length % 2 ) { $spacing_left++; } printf( "▏%s%s%s▕", ( $spacing_left >= 0 ) ? ' ' x $spacing_left : q{}, $section->name, ( $spacing_right >= 0 ) ? ' ' x $spacing_right : q{} ); } print "\n"; my @start_percentages = map { $_->{position}{start_percent} } $wr->wagons; if ( my $min_percentage = min @start_percentages ) { print ' ' x ( $min_percentage - 1 ); } print $wr->direction == 100 ? '>' : '<'; for my $wagon ( $wr->wagons ) { my $wagon_length = $wagon->{position}->{end_percent} - $wagon->{position}->{start_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 ) { if ( $group->has_sections ) { printf( "%s (%s)\n", $group->description || 'Zug', join( q{}, $group->sections ) ); } else { say $group->description || 'Zug'; } printf( "%s %s %s → %s\n\n", $wr->train_type, $group->train_no, $group->origin, $group->destination ); for my $wagon ( $group->wagons ) { printf( "%3s: %3s %10s %s\n", $wagon->is_closed ? 'X' : ( $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 I I =head1 VERSION version 0.13 This is beta software: API and output format may change without notice. =head1 DESCRIPTION db-wagenreihung shows the carriage formation of train I at station I (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 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 Ederf@finalrewind.orgE =head1 LICENSE This program is licensed under the same terms as Perl itself.