summaryrefslogtreecommitdiff
path: root/lib/DBInfoscreen
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2020-09-06 09:22:40 +0200
committerDaniel Friesel <derf@finalrewind.org>2020-09-06 09:22:40 +0200
commit9bdae29a17e9342eb85db11d97935789492e7dc7 (patch)
treea59809ea476577b217b9352e44623b4eb62fe4aa /lib/DBInfoscreen
parentc8383c697ac9108b195696a5ca371e79d224225f (diff)
Move HAFAS helpers to a separate helper class
Diffstat (limited to 'lib/DBInfoscreen')
-rw-r--r--lib/DBInfoscreen/Controller/Stationboard.pm283
-rw-r--r--lib/DBInfoscreen/Helper/HAFAS.pm293
2 files changed, 296 insertions, 280 deletions
diff --git a/lib/DBInfoscreen/Controller/Stationboard.pm b/lib/DBInfoscreen/Controller/Stationboard.pm
index 253cbcb..c1d952c 100644
--- a/lib/DBInfoscreen/Controller/Stationboard.pm
+++ b/lib/DBInfoscreen/Controller/Stationboard.pm
@@ -120,44 +120,6 @@ sub check_wagonorder_with_wings {
return;
}
-sub get_hafas_trip_id {
- my ( $ua, $cache, $train ) = @_;
-
- my $eva = $train->station_uic;
- my $dep_ts = DateTime->now( time_zone => 'Europe/Berlin' );
- my $url
- = "https://2.db.transport.rest/stations/${eva}/departures?duration=5&when=$dep_ts";
- if ( $train->sched_departure ) {
- $dep_ts = $train->sched_departure->epoch;
- $url
- = "https://2.db.transport.rest/stations/${eva}/departures?duration=5&when=$dep_ts";
- }
- elsif ( $train->sched_arrival ) {
- $dep_ts = $train->sched_arrival->epoch;
- $url
- = "https://2.db.transport.rest/stations/${eva}/arrivals?duration=5&when=$dep_ts";
- }
-
- my $json = hafas_rest_req( $ua, $cache, $url );
-
- #say "looking for " . $train->train_no . " in $url";
- for my $result ( @{ $json // [] } ) {
- my $trip_id = $result->{tripId};
- my $fahrt = $result->{line}{fahrtNr};
-
- #say "checking $fahrt";
- if ( $result->{line} and $result->{line}{fahrtNr} == $train->train_no )
- {
- #say "Trip ID is $trip_id";
- return $trip_id;
- }
- else {
- #say "unmatched Trip ID $trip_id";
- }
- }
- return;
-}
-
sub check_wagonorder {
my ( $ua, $cache, $train_no, $wr_link ) = @_;
@@ -184,240 +146,6 @@ sub check_wagonorder {
}
}
-sub hafas_rest_req {
- my ( $ua, $cache, $url ) = @_;
-
- if ( my $content = $cache->thaw($url) ) {
- return $content;
- }
-
- my $res = eval {
- $ua->get(
- $url => { 'User-Agent' => "dbf.finalrewind.org/${dbf_version}" } )
- ->result;
- };
-
- if ($@) {
- return;
- }
- if ( $res->is_error ) {
- return;
- }
-
- my $json = decode_json( $res->body );
-
- $cache->freeze( $url, $json );
-
- return $json;
-}
-
-sub hafas_json_req {
- my ( $ua, $cache, $url ) = @_;
-
- if ( my $content = $cache->thaw($url) ) {
- return $content;
- }
-
- my $res = eval { $ua->get($url)->result };
-
- if ($@) {
- return;
- }
- if ( $res->is_error ) {
- return;
- }
-
- my $body = encode( 'utf-8', decode( 'ISO-8859-15', $res->body ) );
-
- $body =~ s{^TSLs[.]sls = }{};
- $body =~ s{;$}{};
- $body =~ s{&#x0028;}{(}g;
- $body =~ s{&#x0029;}{)}g;
-
- my $json = decode_json($body);
-
- $cache->freeze( $url, $json );
-
- return $json;
-}
-
-sub hafas_xml_req {
- my ( $ua, $cache, $url ) = @_;
-
- if ( my $content = $cache->thaw($url) ) {
- return $content;
- }
-
- my $res = eval { $ua->get($url)->result };
-
- if ($@) {
- return;
- }
- if ( $res->is_error ) {
- $cache->freeze( $url, {} );
- return;
- }
-
- my $body = decode( 'ISO-8859-15', $res->body );
-
- # <SDay text="... &gt; ..."> is invalid HTML, but present
- # regardless. As it is the last tag, we just throw it away.
- $body =~ s{<SDay [^>]*/>}{}s;
-
- my $tree;
-
- eval { $tree = XML::LibXML->load_xml( string => $body ) };
-
- if ($@) {
- $cache->freeze( $url, {} );
- return;
- }
-
- my $ret = {
- station => {},
- stations => [],
- messages => [],
- };
-
- for my $station ( $tree->findnodes('/Journey/St') ) {
- my $name = $station->getAttribute('name');
- my $adelay = $station->getAttribute('adelay');
- my $ddelay = $station->getAttribute('ddelay');
- push( @{ $ret->{stations} }, $name );
- $ret->{station}{$name} = {
- adelay => $adelay,
- ddelay => $ddelay,
- };
- }
-
- for my $message ( $tree->findnodes('/Journey/HIMMessage') ) {
- my $header = $message->getAttribute('header');
- my $lead = $message->getAttribute('lead');
- my $display = $message->getAttribute('display');
- push(
- @{ $ret->{messages} },
- {
- header => $header,
- lead => $lead,
- display => $display
- }
- );
- }
-
- $cache->freeze( $url, $ret );
-
- return $ret;
-}
-
-# quick&dirty, will be cleaned up later
-sub get_route_timestamps {
- my ( $ua, $cache_main, $cache_rt, $opt ) = @_;
-
- $ua->request_timeout(3);
-
- my $base
- = 'https://reiseauskunft.bahn.de/bin/trainsearch.exe/dn?L=vs_json&start=yes&rt=1';
- my ( $date_yy, $date_yyyy, $train_no, $train_origin );
-
- if ( $opt->{train} ) {
- $date_yy = $opt->{train}->start->strftime('%d.%m.%y');
- $date_yyyy = $opt->{train}->start->strftime('%d.%m.%Y');
- $train_no = $opt->{train}->type . ' ' . $opt->{train}->train_no;
- $train_origin = $opt->{train}->origin;
- }
- else {
- my $now = DateTime->now( time_zone => 'Europe/Berlin' );
- $date_yy = $now->strftime('%d.%m.%y');
- $date_yyyy = $now->strftime('%d.%m.%Y');
- $train_no = $opt->{train_no};
- }
-
- my $trainsearch = hafas_json_req( $ua, $cache_main,
- "${base}&date=${date_yy}&trainname=${train_no}" );
-
- if ( not $trainsearch ) {
- return;
- }
-
- # Fallback: Take first result
- my $trainlink = $trainsearch->{suggestions}[0]{trainLink};
-
- # Try finding a result for the current date
- for my $suggestion ( @{ $trainsearch->{suggestions} // [] } ) {
-
- # Drunken API, sail with care. Both date formats are used interchangeably
- if (
- exists $suggestion->{depDate}
- and ( $suggestion->{depDate} eq $date_yy
- or $suggestion->{depDate} eq $date_yyyy )
- )
- {
- # Train numbers are not unique, e.g. IC 149 refers both to the
- # InterCity service Amsterdam -> Berlin and to the InterCity service
- # Koebenhavns Lufthavn st -> Aarhus. One workaround is making
- # requests with the stationFilter=80 parameter. Checking the origin
- # station seems to be the more generic solution, so we do that
- # instead.
- if ( $train_origin and $suggestion->{dep} eq $train_origin ) {
- $trainlink = $suggestion->{trainLink};
- last;
- }
- }
- }
-
- if ( not $trainlink ) {
- return;
- }
-
- $base = 'https://reiseauskunft.bahn.de/bin/traininfo.exe/dn';
-
- my $traininfo = hafas_json_req( $ua, $cache_rt,
- "${base}/${trainlink}?rt=1&date=${date_yy}&L=vs_json" );
-
- if ( not $traininfo or $traininfo->{error} ) {
- return;
- }
-
- my $traindelay = hafas_xml_req( $ua, $cache_rt,
- "${base}/${trainlink}?rt=1&date=${date_yy}&L=vs_java3" );
-
- my $ret = {};
-
- my $strp = DateTime::Format::Strptime->new(
- pattern => '%d.%m.%y %H:%M',
- time_zone => 'Europe/Berlin',
- );
-
- for my $station ( @{ $traininfo->{suggestions}[0]{locations} // [] } ) {
- my $name = $station->{name};
- my $arr = $station->{arrDate} . ' ' . $station->{arrTime};
- my $dep = $station->{depDate} . ' ' . $station->{depTime};
- $ret->{$name} = {
- sched_arr => scalar $strp->parse_datetime($arr),
- sched_dep => scalar $strp->parse_datetime($dep),
- };
- if ( exists $traindelay->{station}{$name} ) {
- my $delay = $traindelay->{station}{$name};
- if ( $ret->{$name}{sched_arr}
- and $delay->{adelay}
- and $delay->{adelay} =~ m{^\d+$} )
- {
- $ret->{$name}{rt_arr} = $ret->{$name}{sched_arr}
- ->clone->add( minutes => $delay->{adelay} );
- }
- if ( $ret->{$name}{sched_dep}
- and $delay->{ddelay}
- and $delay->{ddelay} =~ m{^\d+$} )
- {
- $ret->{$name}{rt_dep} = $ret->{$name}{sched_dep}
- ->clone->add( minutes => $delay->{ddelay} );
- }
- }
- }
-
- return ( $ret, $traindelay // {} );
-}
-
sub get_results_for {
my ( $backend, $station, %opt ) = @_;
my $data;
@@ -725,8 +453,7 @@ sub render_train {
)
];
- $departure->{trip_id}
- = get_hafas_trip_id( $self->ua, $self->app->cache_iris_main, $result );
+ $departure->{trip_id} = $self->hafas->get_tripid($result);
if (
$departure->{wr_link}
@@ -739,12 +466,8 @@ sub render_train {
$departure->{wr_link} = undef;
}
- my ( $route_ts, $route_info ) = get_route_timestamps(
- $self->ua,
- $self->app->cache_iris_main,
- $self->app->cache_iris_rt,
- { train => $result }
- );
+ my ( $route_ts, $route_info )
+ = $self->hafas->get_route_timestamps( train => $result );
# If a train number changes on the way, IRIS routes are incomplete,
# whereas HAFAS data has all stops -> merge HAFAS stops into IRIS
diff --git a/lib/DBInfoscreen/Helper/HAFAS.pm b/lib/DBInfoscreen/Helper/HAFAS.pm
new file mode 100644
index 0000000..18a3eda
--- /dev/null
+++ b/lib/DBInfoscreen/Helper/HAFAS.pm
@@ -0,0 +1,293 @@
+package DBInfoscreen::Helper::HAFAS;
+
+use strict;
+use warnings;
+use 5.020;
+
+use DateTime;
+use Encode qw(decode encode);
+use Mojo::JSON qw(decode_json);
+use XML::LibXML;
+
+sub new {
+ my ( $class, %opt ) = @_;
+
+ my $version = $opt{version};
+
+ $opt{header}
+ = { 'User-Agent' =>
+ "dbf/${version} +https://finalrewind.org/projects/db-fakedisplay" };
+
+ return bless( \%opt, $class );
+
+}
+
+sub hafas_rest_req {
+ my ( $self, $cache, $url ) = @_;
+
+ if ( my $content = $cache->thaw($url) ) {
+ return $content;
+ }
+
+ my $res = eval { $self->{user_agent}->get($url)->result; };
+
+ if ($@) {
+ return;
+ }
+ if ( $res->is_error ) {
+ return;
+ }
+
+ my $json = decode_json( $res->body );
+
+ $cache->freeze( $url, $json );
+
+ return $json;
+}
+
+sub hafas_json_req {
+ my ( $self, $cache, $url ) = @_;
+
+ if ( my $content = $cache->thaw($url) ) {
+ return $content;
+ }
+
+ my $res = eval { $self->{user_agent}->get($url)->result };
+
+ if ($@) {
+ return;
+ }
+ if ( $res->is_error ) {
+ return;
+ }
+
+ my $body = encode( 'utf-8', decode( 'ISO-8859-15', $res->body ) );
+
+ $body =~ s{^TSLs[.]sls = }{};
+ $body =~ s{;$}{};
+ $body =~ s{&#x0028;}{(}g;
+ $body =~ s{&#x0029;}{)}g;
+
+ my $json = decode_json($body);
+
+ $cache->freeze( $url, $json );
+
+ return $json;
+}
+
+sub hafas_xml_req {
+ my ( $self, $cache, $url ) = @_;
+
+ if ( my $content = $cache->thaw($url) ) {
+ return $content;
+ }
+
+ my $res = eval { $self->{user_agent}->get($url)->result };
+
+ if ($@) {
+ return;
+ }
+ if ( $res->is_error ) {
+ $cache->freeze( $url, {} );
+ return;
+ }
+
+ my $body = decode( 'ISO-8859-15', $res->body );
+
+ # <SDay text="... &gt; ..."> is invalid HTML, but present
+ # regardless. As it is the last tag, we just throw it away.
+ $body =~ s{<SDay [^>]*/>}{}s;
+
+ my $tree;
+
+ eval { $tree = XML::LibXML->load_xml( string => $body ) };
+
+ if ($@) {
+ $cache->freeze( $url, {} );
+ return;
+ }
+
+ my $ret = {
+ station => {},
+ stations => [],
+ messages => [],
+ };
+
+ for my $station ( $tree->findnodes('/Journey/St') ) {
+ my $name = $station->getAttribute('name');
+ my $adelay = $station->getAttribute('adelay');
+ my $ddelay = $station->getAttribute('ddelay');
+ push( @{ $ret->{stations} }, $name );
+ $ret->{station}{$name} = {
+ adelay => $adelay,
+ ddelay => $ddelay,
+ };
+ }
+
+ for my $message ( $tree->findnodes('/Journey/HIMMessage') ) {
+ my $header = $message->getAttribute('header');
+ my $lead = $message->getAttribute('lead');
+ my $display = $message->getAttribute('display');
+ push(
+ @{ $ret->{messages} },
+ {
+ header => $header,
+ lead => $lead,
+ display => $display
+ }
+ );
+ }
+
+ $cache->freeze( $url, $ret );
+
+ return $ret;
+}
+
+sub get_route_timestamps {
+ my ( $self, %opt ) = @_;
+
+ my $base
+ = 'https://reiseauskunft.bahn.de/bin/trainsearch.exe/dn?L=vs_json&start=yes&rt=1';
+ my ( $date_yy, $date_yyyy, $train_no, $train_origin );
+
+ if ( $opt{train} ) {
+ $date_yy = $opt{train}->start->strftime('%d.%m.%y');
+ $date_yyyy = $opt{train}->start->strftime('%d.%m.%Y');
+ $train_no = $opt{train}->type . ' ' . $opt{train}->train_no;
+ $train_origin = $opt{train}->origin;
+ }
+ else {
+ my $now = DateTime->now( time_zone => 'Europe/Berlin' );
+ $date_yy = $now->strftime('%d.%m.%y');
+ $date_yyyy = $now->strftime('%d.%m.%Y');
+ $train_no = $opt{train_no};
+ }
+
+ my $trainsearch = $self->hafas_json_req( $self->{main_cache},
+ "${base}&date=${date_yy}&trainname=${train_no}" );
+
+ if ( not $trainsearch ) {
+ return;
+ }
+
+ # Fallback: Take first result
+ my $trainlink = $trainsearch->{suggestions}[0]{trainLink};
+
+ # Try finding a result for the current date
+ for my $suggestion ( @{ $trainsearch->{suggestions} // [] } ) {
+
+ # Drunken API, sail with care. Both date formats are used interchangeably
+ if (
+ exists $suggestion->{depDate}
+ and ( $suggestion->{depDate} eq $date_yy
+ or $suggestion->{depDate} eq $date_yyyy )
+ )
+ {
+ # Train numbers are not unique, e.g. IC 149 refers both to the
+ # InterCity service Amsterdam -> Berlin and to the InterCity service
+ # Koebenhavns Lufthavn st -> Aarhus. One workaround is making
+ # requests with the stationFilter=80 parameter. Checking the origin
+ # station seems to be the more generic solution, so we do that
+ # instead.
+ if ( $train_origin and $suggestion->{dep} eq $train_origin ) {
+ $trainlink = $suggestion->{trainLink};
+ last;
+ }
+ }
+ }
+
+ if ( not $trainlink ) {
+ return;
+ }
+
+ $base = 'https://reiseauskunft.bahn.de/bin/traininfo.exe/dn';
+
+ my $traininfo = $self->hafas_json_req( $self->{realtime_cache},
+ "${base}/${trainlink}?rt=1&date=${date_yy}&L=vs_json" );
+
+ if ( not $traininfo or $traininfo->{error} ) {
+ return;
+ }
+
+ my $traindelay = $self->hafas_xml_req( $self->{realtime_cache},
+ "${base}/${trainlink}?rt=1&date=${date_yy}&L=vs_java3" );
+
+ my $ret = {};
+
+ my $strp = DateTime::Format::Strptime->new(
+ pattern => '%d.%m.%y %H:%M',
+ time_zone => 'Europe/Berlin',
+ );
+
+ for my $station ( @{ $traininfo->{suggestions}[0]{locations} // [] } ) {
+ my $name = $station->{name};
+ my $arr = $station->{arrDate} . ' ' . $station->{arrTime};
+ my $dep = $station->{depDate} . ' ' . $station->{depTime};
+ $ret->{$name} = {
+ sched_arr => scalar $strp->parse_datetime($arr),
+ sched_dep => scalar $strp->parse_datetime($dep),
+ };
+ if ( exists $traindelay->{station}{$name} ) {
+ my $delay = $traindelay->{station}{$name};
+ if ( $ret->{$name}{sched_arr}
+ and $delay->{adelay}
+ and $delay->{adelay} =~ m{^\d+$} )
+ {
+ $ret->{$name}{rt_arr} = $ret->{$name}{sched_arr}
+ ->clone->add( minutes => $delay->{adelay} );
+ }
+ if ( $ret->{$name}{sched_dep}
+ and $delay->{ddelay}
+ and $delay->{ddelay} =~ m{^\d+$} )
+ {
+ $ret->{$name}{rt_dep} = $ret->{$name}{sched_dep}
+ ->clone->add( minutes => $delay->{ddelay} );
+ }
+ }
+ }
+
+ return ( $ret, $traindelay // {} );
+}
+
+sub get_tripid {
+ my ( $self, $train ) = @_;
+
+ my $cache = $self->{main_cache};
+ my $eva = $train->station_uic;
+
+ my $dep_ts = DateTime->now( time_zone => 'Europe/Berlin' );
+ my $url
+ = "https://2.db.transport.rest/stations/${eva}/departures?duration=5&when=$dep_ts";
+
+ if ( $train->sched_departure ) {
+ $dep_ts = $train->sched_departure->epoch;
+ $url
+ = "https://2.db.transport.rest/stations/${eva}/departures?duration=5&when=$dep_ts";
+ }
+ elsif ( $train->sched_arrival ) {
+ $dep_ts = $train->sched_arrival->epoch;
+ $url
+ = "https://2.db.transport.rest/stations/${eva}/arrivals?duration=5&when=$dep_ts";
+ }
+
+ my $json = $self->hafas_rest_req( $cache, $url );
+
+ #say "looking for " . $train->train_no . " in $url";
+ for my $result ( @{ $json // [] } ) {
+ my $trip_id = $result->{tripId};
+ my $fahrt = $result->{line}{fahrtNr};
+
+ #say "checking $fahrt";
+ if ( $result->{line} and $result->{line}{fahrtNr} == $train->train_no )
+ {
+ #say "Trip ID is $trip_id";
+ return $trip_id;
+ }
+ else {
+ #say "unmatched Trip ID $trip_id";
+ }
+ }
+ return;
+}
+
+1;