diff options
Diffstat (limited to 'lib/DBInfoscreen/Controller/Stationboard.pm')
-rw-r--r-- | lib/DBInfoscreen/Controller/Stationboard.pm | 533 |
1 files changed, 460 insertions, 73 deletions
diff --git a/lib/DBInfoscreen/Controller/Stationboard.pm b/lib/DBInfoscreen/Controller/Stationboard.pm index f81074b..8c8365f 100644 --- a/lib/DBInfoscreen/Controller/Stationboard.pm +++ b/lib/DBInfoscreen/Controller/Stationboard.pm @@ -242,8 +242,9 @@ sub result_has_via { my ( $result, $via ) = @_; my @route - = $result->can('route_post') ? $result->route_post : map { $_->loc->name } - $result->route; + = $result->can('route_post') + ? ( $result->route_post, $result->sched_route_post ) + : map { $_->loc->name } $result->route; my $eq_result = List::MoreUtils::any { lc eq lc($via) } @route; @@ -363,6 +364,8 @@ sub get_results_p { return Travel::Status::DE::EFA->new_p( service => $service, name => $station, + full_routes => 1, + cache => $opt{cache_iris_rt}, lwp_options => { timeout => 10, agent => 'dbf.finalrewind.org/2' @@ -511,6 +514,7 @@ sub handle_request { $self->param( mode => $template ); if ( not $station ) { + $self->param( rt => 1 ); $self->render( 'landingpage', show_intro => 1 ); return; } @@ -772,23 +776,29 @@ sub render_train { my @requests = ( $wagonorder_req, $occupancy_req, $stationinfo_req, $route_req ); - if ( $departure->{wr_link} ) { - $self->wagonorder->get_p( $result->train_no, $departure->{wr_link} ) - ->then( + if ( $departure->{wr_dt} ) { + $self->wagonorder->get_p( + train_type => $result->type, + train_number => $result->train_no, + datetime => $departure->{wr_dt}, + eva => $departure->{eva} + )->then( sub { - my ($wr_json) = @_; + my ( $wr_json, $wr_param ) = @_; eval { my $wr = Travel::Status::DE::DBWagenreihung->new( from_json => $wr_json ); $departure->{wr} = $wr; + $departure->{wr_link} = join( '&', + map { $_ . '=' . $wr_param->{$_} } keys %{$wr_param} ); $departure->{wr_text} = join( q{ • }, map { $_->desc_short } grep { $_->desc_short } $wr->groups ); my $first = 0; for my $group ( $wr->groups ) { my $had_entry = 0; - for my $wagon ( $group->wagons ) { + for my $wagon ( $group->carriages ) { if ( not( $wagon->is_locomotive or $wagon->is_powercar ) @@ -807,14 +817,24 @@ sub render_train { $entry = 'X'; $class = 'closed'; } + elsif ( $wagon->number ) { + $entry = $wagon->number; + } else { - $entry = $wagon->number - || ( - $wagon->type =~ m{AB} ? '½' - : $wagon->type =~ m{A} ? '1.' - : $wagon->type =~ m{B} ? '2.' - : $wagon->type - ); + if ( $wagon->has_first_class ) { + if ( $wagon->has_second_class ) { + $entry = '½'; + } + else { + $entry = '1.'; + } + } + elsif ( $wagon->has_second_class ) { + $entry = '2.'; + } + else { + $entry = $wagon->type; + } } if ( $group->train_no ne $departure->{train_no} ) @@ -837,7 +857,7 @@ sub render_train { return; }, sub { - $departure->{wr_link} = undef; + $departure->{wr_dt} = undef; return; } )->finally( @@ -912,6 +932,8 @@ sub render_train { } elsif ( $platform_info->{direction} ) { $departure->{wr_direction} = 'a' . $platform_info->{direction}; + $departure->{wr_direction_num} + = $platform_info->{direction} eq 'l' ? 0 : 100; } return; @@ -1159,9 +1181,7 @@ sub station_train_details { map { $_->type . q{ } . $_->train_no } $result->replacement_for ], - wr_link => $result->sched_departure - ? $result->sched_departure->strftime('%Y%m%d%H%M') - : undef, + wr_dt => $result->sched_departure, eva => $result->station_uic, start => $result->start, }; @@ -1200,11 +1220,204 @@ sub station_train_details { )->wait; } +sub train_details_efa { + my ($self) = @_; + my $trip_id = $self->stash('train'); + + my $stopseq; + if ( $trip_id =~ m{ ^ ([^@]*) @ ([^@]*) [(] ([^)]*) [)] (.*) $ }x ) { + $stopseq = { + stateless => $1, + stop_id => $2, + date => $3, + key => $4 + }; + } + else { + $self->render( 'not_found', status => 404 ); + return; + } + + $self->render_later; + + Travel::Status::DE::EFA->new_p( + service => $self->param('efa'), + stopseq => $stopseq, + cache => $self->app->cache_iris_rt, + lwp_options => { + timeout => 10, + agent => 'dbf.finalrewind.org/2' + }, + promise => 'Mojo::Promise', + user_agent => Mojo::UserAgent->new, + )->then( + sub { + my ($efa) = @_; + my $trip = $efa->result; + + my $now = DateTime->now( time_zone => 'Europe/Berlin' ); + my $res = { + trip_id => $trip_id, + train_type => $trip->type, + train_line => $trip->line, + train_no => $trip->number, + origin => ( $trip->route )[0]->full_name, + destination => ( $trip->route )[-1]->full_name, + operators => [ $trip->operator ], + linetype => lc( $trip->product ) =~ tr{a-z}{}cdr, + route_pre_diff => [], + route_post_diff => [], + moreinfo => [], + replaced_by => [], + replacement_for => [], + }; + + if ( $res->{linetype} =~ m{strab|stra.?enbahn} ) { + $res->{linetype} = 'tram'; + } + elsif ( $res->{linetype} =~ m{bus} ) { + $res->{linetype} = 'bus'; + } + + my $station_is_past = 1; + for my $stop ( $trip->route ) { + + push( + @{ $res->{route_post_diff} }, + { + name => $stop->full_name, + id => $stop->stop_id, + sched_arr => $stop->sched_arr, + sched_dep => $stop->sched_dep, + rt_arr => $stop->rt_arr, + rt_dep => $stop->rt_dep, + arr_delay => $stop->arr_delay, + dep_delay => $stop->dep_delay, + platform => $stop->platform, + } + ); + if ( + $station_is_past + and $now->epoch < ( + $res->{route_post_diff}[-1]{rt_arr} + // $res->{route_post_diff}[-1]{rt_dep} + // $res->{route_post_diff}[-1]{sched_arr} + // $res->{route_post_diff}[-1]{sched_dep} // $now + )->epoch + ) + { + $station_is_past = 0; + } + $res->{route_post_diff}[-1]{isPast} = $station_is_past; + } + + if ( my $req_id = $self->param('highlight') ) { + my $split; + for my $i ( 0 .. $#{ $res->{route_post_diff} } ) { + if ( $res->{route_post_diff}[$i]{id} eq $req_id ) { + $split = $i; + last; + } + } + if ( defined $split ) { + $self->stash( + station_name => $res->{route_post_diff}[$split]{name} ); + for my $i ( 0 .. $split - 1 ) { + push( + @{ $res->{route_pre_diff} }, + shift( @{ $res->{route_post_diff} } ) + ); + } + my $station_info = shift( @{ $res->{route_post_diff} } ); + $res->{eva} = $station_info->{eva}; + if ( $station_info->{sched_arr} ) { + $res->{sched_arrival} + = $station_info->{sched_arr}->strftime('%H:%M'); + } + if ( $station_info->{rt_arr} ) { + $res->{arrival} + = $station_info->{rt_arr}->strftime('%H:%M'); + } + if ( $station_info->{sched_dep} ) { + $res->{sched_departure} + = $station_info->{sched_dep}->strftime('%H:%M'); + } + if ( $station_info->{rt_dep} ) { + $res->{departure} + = $station_info->{rt_dep}->strftime('%H:%M'); + } + $res->{arrival_is_cancelled} + = $station_info->{arr_cancelled}; + $res->{departure_is_cancelled} + = $station_info->{dep_cancelled}; + $res->{is_cancelled} = $res->{arrival_is_cancelled} + || $res->{arrival_is_cancelled}; + $res->{tz_offset} = $station_info->{tz_offset}; + $res->{local_dt_da} = $station_info->{local_dt_da}; + $res->{local_sched_arr} = $station_info->{local_sched_arr}; + $res->{local_sched_dep} = $station_info->{local_sched_dep}; + $res->{is_annotated} = $station_info->{is_annotated}; + $res->{prod_name} = $station_info->{prod_name}; + $res->{direction} = $station_info->{direction}; + $res->{operator} = $station_info->{operator}; + $res->{platform} = $station_info->{platform}; + $res->{scheduled_platform} + = $station_info->{sched_platform}; + } + } + + $self->respond_to( + json => { + json => { + journey => $trip, + }, + }, + any => { + template => $self->param('ajax') + ? '_train_details' + : 'train_details', + description => sprintf( + '%s %s%s%s nach %s', + $res->{train_type}, + $res->{train_line} // $res->{train_no}, + $res->{origin} ? ' von ' : q{}, + $res->{origin} // q{}, + $res->{destination} // 'unbekannt' + ), + departure => $res, + linetype => $res->{linetype}, + dt_now => DateTime->now( time_zone => 'Europe/Berlin' ), + }, + ); + } + )->catch( + sub { + my ($e) = @_; + $self->respond_to( + json => { + json => { + error => $e, + }, + status => 500, + }, + any => { + template => 'exception', + message => $e, + exception => undef, + snapshot => {}, + status => 500, + }, + ); + } + )->wait; +} + # /z/:train sub train_details { my ($self) = @_; my $train = $self->stash('train'); my $hafas = $self->param('hafas'); + my $efa = $self->param('efa'); # TODO error handling @@ -1215,6 +1428,10 @@ sub train_details { $self->stash( departures => [] ); $self->stash( title => 'DBF' ); + if ($efa) { + return $self->train_details_efa; + } + my $res = { train_type => undef, train_line => undef, @@ -1301,19 +1518,19 @@ sub train_details { my $prod = $self->class_to_product($hafas_obj)->{ $product->class } // q{}; - if ( $prod eq 'ice' or $prod eq 'ic_ec' ) { + if ( $prod =~ m{ ^ ice? | inter-?cit }ix ) { $linetype = 'fern'; } - elsif ( $prod eq 's' ) { + elsif ( $prod =~ m{ s-bahn | urban | rapid }ix ) { $linetype = 'sbahn'; } - elsif ( $prod eq 'bus' ) { + elsif ( $prod =~ m{ bus }ix ) { $linetype = 'bus'; } - elsif ( $prod eq 'u' ) { + elsif ( $prod =~ m{ metro | u-bahn | subway }ix ) { $linetype = 'ubahn'; } - elsif ( $prod eq 'tram' ) { + elsif ( $prod =~ m{ tram }ix ) { $linetype = 'tram'; } } @@ -1478,6 +1695,11 @@ sub handle_efa { for my $result ( $efa->results ) { my $time; + if ( $template eq 'json' ) { + push( @departures, $result ); + next; + } + if ( $show_realtime and $result->rt_datetime ) { $time = $result->rt_datetime->strftime('%H:%M'); } @@ -1486,21 +1708,27 @@ sub handle_efa { } my $linetype = $result->mot_name // 'bahn'; - if ( $linetype eq 's-bahn' ) { + if ( $linetype =~ m{ s-bahn | urban | rapid }ix ) { $linetype = 'sbahn'; } - elsif ( $linetype eq 'u-bahn' ) { + elsif ( $linetype =~ m{ metro | u-bahn | subway }ix ) { $linetype = 'ubahn'; } - elsif ( $linetype =~ m{bus} ) { + elsif ( $linetype =~ m{ bus }ix ) { $linetype = 'bus'; } - elsif ( $linetype eq 'zug' ) { - $linetype = 'bahn'; + elsif ( $linetype =~ m{ tram }ix ) { + $linetype = 'tram'; + } + elsif ( $linetype =~ m{ ^ ice? | inter-?cit }ix ) { + $linetype = 'fern'; } elsif ( $linetype eq 'sonstige' ) { $linetype = 'ext'; } + + my $delay = $result->delay; + push( @departures, { @@ -1509,44 +1737,66 @@ sub handle_efa { departure => $result->rt_datetime ? $result->rt_datetime->strftime('%H:%M') : undef, - train => $result->line, - train_type => q{}, - train_line => $result->line, - train_no => $result->train_no, - via => [], - origin => $result->origin, - destination => $result->destination, - platform => $result->platform, - is_cancelled => $result->is_cancelled, - linetype => $linetype, - delay => $result->delay, + train => $result->line, + train_type => q{}, + train_line => $result->line, + train_no => $result->train_no, + journey_id => sprintf( '%s@%d(%s)%d', + $result->stateless, + scalar $result->route_pre ? ( $result->route_pre )[0]->id + : $result->stop_id, + $result->sched_datetime->strftime('%Y%m%d'), + $result->key ), + via => [ map { $_->name } $result->route_interesting ], + origin => $result->origin, + destination => $result->destination, + platform => $result->platform, + is_cancelled => $result->is_cancelled, + linetype => $linetype, + delay => $delay, + is_bit_delayed => + ( $delay and $delay > 0 and $delay < 5 ? 1 : 0 ), + is_delayed => ( $delay and $delay >= 5 ? 1 : 0 ), + has_realtime => defined $delay ? 1 : 0, occupancy => $result->occupancy, + station => $efa->stop->id, replaced_by => [], replacement_for => [], - route_pre => [], - route_post => [], - wr_link => undef, + route_pre => [ map { $_->full_name } $result->route_pre ], + route_post => [ map { $_->full_name } $result->route_post ], + wr_dt => undef, } ); } - $self->render( - $template, - description => "Abfahrtstafel $station_name", - departures => \@departures, - station => $station_name, - version => $self->config->{version}, - title => $station_name, - refresh_interval => $template eq 'app' ? 0 : 120, - hide_opts => $hide_opts, - hide_low_delay => $hide_low_delay, - show_realtime => $show_realtime, - load_marquee => ( - $template eq 'single' - or $template eq 'multi' - ), - force_mobile => ( $template eq 'app' ), - ); + if ( $template eq 'json' ) { + $self->res->headers->access_control_allow_origin(q{*}); + my $json = { + departures => \@departures, + }; + $self->render( + json => $json, + ); + } + else { + $self->render( + $template, + description => "Abfahrtstafel $station_name", + departures => \@departures, + station => $efa->stop->name, + version => $self->config->{version}, + title => $efa->stop->name // $station_name, + refresh_interval => $template eq 'app' ? 0 : 120, + hide_opts => $hide_opts, + hide_low_delay => $hide_low_delay, + show_realtime => $show_realtime, + load_marquee => ( + $template eq 'single' + or $template eq 'multi' + ), + force_mobile => ( $template eq 'app' ), + ); + } } sub handle_result { @@ -1658,19 +1908,19 @@ sub handle_result { } elsif ( $result->can('class') ) { my $prod = $class_to_product->{ $result->class } // q{}; - if ( $prod eq 'ice' or $prod eq 'ic_ec' ) { + if ( $prod =~ m{ ^ ice? | inter-?cit }ix ) { $linetype = 'fern'; } - elsif ( $prod eq 's' ) { + elsif ( $prod =~ m{ s-bahn | urban | rapid }ix ) { $linetype = 'sbahn'; } - elsif ( $prod eq 'bus' ) { + elsif ( $prod =~ m{ bus }ix ) { $linetype = 'bus'; } - elsif ( $prod eq 'u' ) { + elsif ( $prod =~ m{ metro | u-bahn | subway }ix ) { $linetype = 'ubahn'; } - elsif ( $prod eq 'tram' ) { + elsif ( $prod =~ m{ tram }ix ) { $linetype = 'tram'; } } @@ -1727,6 +1977,9 @@ sub handle_result { ); return; } + elsif ( $apiver eq 'raw' ) { + push( @departures, $result ); + } else { # apiver == 3 if ( $result->isa('Travel::Status::DE::IRIS::Result') ) { my ( $delay_arr, $delay_dep, $sched_arr, $sched_dep ); @@ -1900,9 +2153,8 @@ sub handle_result { map { $_->type . q{ } . $_->train_no } $result->replacement_for ], - wr_link => $result->sched_departure - ? $result->sched_departure->strftime('%Y%m%d%H%M') - : undef, + wr_dt => $result->sched_departure, + eva => $result->station_uic, } ); } @@ -1954,9 +2206,8 @@ sub handle_result { : [], route_post => $admode eq 'arr' ? [] : [ map { $_->loc->name } $result->route ], - wr_link => $result->sched_datetime - ? $result->sched_datetime->strftime('%Y%m%d%H%M') - : undef, + wr_dt => $result->sched_datetime, + eva => $result->station_uic, } ); } @@ -2014,6 +2265,11 @@ sub handle_result { { $params->param( hafas => 'ÖBB' ); } + elsif ( $data->{station_eva} >= 8500000 + and $data->{station_eva} < 8600000 ) + { + $params->param( hafas => 'BLS' ); + } $api_link = '/' . $data->{station_eva} . '?' . $params->to_string; $api_text = 'Auf Nahverkehr wechseln'; $api_icon = 'train'; @@ -2062,9 +2318,10 @@ sub handle_result { sub stations_by_coordinates { my $self = shift; - my $lon = $self->param('lon'); - my $lat = $self->param('lat'); - my $hafas = $self->param('hafas'); + my $lon = $self->param('lon'); + my $lat = $self->param('lat'); + my $efa_service = $self->param('efa'); + my $hafas = $self->param('hafas'); if ( not $lon or not $lat ) { $self->render( json => { error => 'Invalid lon/lat received' } ); @@ -2081,6 +2338,46 @@ sub stations_by_coordinates { $self->render_later; + if ($efa_service) { + Travel::Status::DE::EFA->new_p( + promise => 'Mojo::Promise', + user_agent => $self->ua, + service => $efa_service, + coord => { + lat => $lat, + lon => $lon + } + )->then( + sub { + my ($efa) = @_; + my @efa = map { + { + name => $_->full_name, + eva => $_->id =~ s{:}{%3A}gr, + distance => $_->distance_m / 1000, + efa => $efa_service, + } + } $efa->results; + $self->render( + json => { + candidates => [@efa], + } + ); + } + )->catch( + sub { + my ($err) = @_; + $self->render( + json => { + candidates => [], + warning => $err, + } + ); + } + )->wait; + return; + } + my @iris = map { { ds100 => $_->[0][0], @@ -2140,6 +2437,89 @@ sub stations_by_coordinates { )->wait; } +sub backend_list { + my ($self) = @_; + + my %place_map = ( + AT => 'Österreich', + CH => 'Schweiz', + 'CH-BE' => 'Kanton Bern', + 'CH-GE' => 'Kanton Genf', + 'CH-LU' => 'Kanton Luzern', + 'CH-ZH' => 'Kanton Zürich', + DE => 'Deutschland', + 'DE-BB' => 'Brandenburg', + 'DE-BW' => 'Baden-Württemberg', + 'DE-BE' => 'Berlin', + 'DE-BY' => 'Bayern', + 'DE-HB' => 'Bremen', + 'DE-HE' => 'Hessen', + 'DE-MV' => 'Mecklenburg-Vorpommern', + 'DE-NI' => 'Niedersachsen', + 'DE-NW' => 'Nordrhein-Westfalen', + 'DE-RP' => 'Rheinland-Pfalz', + 'DE-SH' => 'Schleswig-Holstein', + 'DE-ST' => 'Sachsen-Anhalt', + 'DE-TH' => 'Thüringen', + DK => 'Dänemark', + 'GB-NIR' => 'Nordirland', + LI => 'Liechtenstein', + LU => 'Luxembourg', + IE => 'Irland', + 'US-CA' => 'California', + 'US-TX' => 'Texas', + ); + + my @backends = ( + { + name => 'Deutsche Bahn', + type => 'IRIS-TTS', + } + ); + + for my $backend ( Travel::Status::DE::EFA::get_services() ) { + push( + @backends, + { + name => $backend->{name}, + shortname => $backend->{shortname}, + homepage => $backend->{homepage}, + regions => [ + map { $place_map{$_} // $_ } + @{ $backend->{coverage}{regions} } + ], + has_area => $backend->{coverage}{area} ? 1 : 0, + type => 'EFA', + efa => 1, + } + ); + } + + for my $backend ( Travel::Status::DE::HAFAS::get_services() ) { + push( + @backends, + { + name => $backend->{name}, + shortname => $backend->{shortname}, + homepage => $backend->{homepage}, + regions => [ + map { $place_map{$_} // $_ } + @{ $backend->{coverage}{regions} } + ], + has_area => $backend->{coverage}{area} ? 1 : 0, + type => 'HAFAS', + hafas => 1, + } + ); + } + + $self->render( + 'select_backend', + backends => \@backends, + hide_opts => 1 + ); +} + sub autocomplete { my ($self) = @_; @@ -2188,11 +2568,18 @@ sub redirect_to_station { $params = $params->to_string; $self->redirect_to("/z/${input}?${params}"); } + elsif ( $params->param('efa') ) { + $params->remove('hafas'); + $params = $params->to_string; + $self->redirect_to("/${input}?${params}"); + } elsif ( $params->param('hafas') and $params->param('hafas') ne '1' ) { + $params->remove('efa'); $params = $params->to_string; $self->redirect_to("/${input}?${params}"); } else { + $params->remove('efa'); my @candidates = Travel::Status::DE::IRIS::Stations::get_station($input); if ( |