File Coverage

blib/lib/Travel/Status/DE/HAFAS/Stop.pm
Criterion Covered Total %
statement 70 88 79.5
branch 15 24 62.5
condition 38 58 65.5
subroutine 6 8 75.0
pod 2 4 50.0
total 131 182 71.9


line stmt bran cond sub pod time code
1             package Travel::Status::DE::HAFAS::Stop;
2              
3             # vim:foldmethod=marker
4              
5 5     5   29 use strict;
  5         8  
  5         158  
6 5     5   22 use warnings;
  5         29  
  5         242  
7 5     5   84 use 5.014;
  5         16  
8              
9 5     5   28 use parent 'Class::Accessor';
  5         9  
  5         31  
10              
11             our $VERSION = '6.25';
12              
13             Travel::Status::DE::HAFAS::Stop->mk_ro_accessors(
14             qw(loc
15             rt_arr sched_arr arr arr_delay arr_cancelled prod_arr
16             rt_dep sched_dep dep dep_delay dep_cancelled prod_dep
17             delay direction
18             rt_platform sched_platform platform is_changed_platform
19             is_additional tz_offset
20             load
21             )
22             );
23              
24             # {{{ Constructor
25              
26             sub new {
27 85     85 1 2386 my ( $obj, %opt ) = @_;
28              
29 85         265 my $stop = $opt{stop};
30 85         198 my $common = $opt{common};
31 85         182 my $prodL = $opt{prodL};
32 85         178 my $date = $opt{date};
33 85         155 my $datetime_ref = $opt{datetime_ref};
34 85         154 my $hafas = $opt{hafas};
35 85         204 my $strp_obj = $opt{hafas}{strptime_obj};
36              
37             my $prod_arr
38 85 100       333 = defined $stop->{aProdX} ? $prodL->[ $stop->{aProdX} ] : undef;
39             my $prod_dep
40 85 100       241 = defined $stop->{dProdX} ? $prodL->[ $stop->{dProdX} ] : undef;
41              
42             # dIn. / aOut. -> may passengers enter / exit the train?
43              
44             my $sched_platform = $stop->{aPlatfS} // $stop->{dPlatfS}
45 85   100     511 // $stop->{aPltfS}{txt} // $stop->{dPltfS}{txt};
      66        
      33        
46             my $rt_platform = $stop->{aPlatfR} // $stop->{dPlatfR}
47 85   66     689 // $stop->{aPltfR}{txt} // $stop->{dPltfR}{txt};
      66        
      33        
48 85   66     358 my $changed_platform = $stop->{aPlatfCh} // $stop->{dPlatfCh};
49              
50 85         170 my $arr_cancelled = $stop->{aCncl};
51 85         144 my $dep_cancelled = $stop->{dCncl};
52 85         167 my $is_additional = $stop->{isAdd};
53              
54             my $ref = {
55             loc => $opt{loc},
56             direction => $stop->{dDirTxt},
57 85   100     1072 sched_platform => $sched_platform,
58             rt_platform => $rt_platform,
59             is_changed_platform => $changed_platform,
60             platform => $rt_platform // $sched_platform,
61             arr_cancelled => $arr_cancelled,
62             dep_cancelled => $dep_cancelled,
63             is_additional => $is_additional,
64             prod_arr => $prod_arr,
65             prod_dep => $prod_dep,
66             };
67              
68 85         247 bless( $ref, $obj );
69              
70             my $sched_arr = $ref->handle_day_change(
71             input => $stop->{aTimeS},
72             offset => $stop->{aTZOffset},
73 85         385 date => $date,
74             strp_obj => $strp_obj,
75             ref => $datetime_ref
76             );
77              
78             my $rt_arr = $ref->handle_day_change(
79             input => $stop->{aTimeR},
80             offset => $stop->{aTZOffset},
81 85         538 date => $date,
82             strp_obj => $strp_obj,
83             ref => $datetime_ref
84             );
85              
86             my $sched_dep = $ref->handle_day_change(
87             input => $stop->{dTimeS},
88             offset => $stop->{dTZOffset},
89 85         412 date => $date,
90             strp_obj => $strp_obj,
91             ref => $datetime_ref
92             );
93              
94             my $rt_dep = $ref->handle_day_change(
95             input => $stop->{dTimeR},
96             offset => $stop->{dTZOffset},
97 85         465 date => $date,
98             strp_obj => $strp_obj,
99             ref => $datetime_ref
100             );
101              
102             $ref->{arr_delay}
103 85 100 100     542 = ( $sched_arr and $rt_arr )
104             ? ( $rt_arr->epoch - $sched_arr->epoch ) / 60
105             : undef;
106              
107             $ref->{dep_delay}
108 85 100 100     1390 = ( $sched_dep and $rt_dep )
109             ? ( $rt_dep->epoch - $sched_dep->epoch ) / 60
110             : undef;
111              
112 85   66     1126 $ref->{delay} = $ref->{dep_delay} // $ref->{arr_delay};
113              
114 85         238 $ref->{sched_arr} = $sched_arr;
115 85         177 $ref->{sched_dep} = $sched_dep;
116 85         193 $ref->{rt_arr} = $rt_arr;
117 85         201 $ref->{rt_dep} = $rt_dep;
118 85   100     366 $ref->{arr} = $rt_arr // $sched_arr;
119 85   100     657 $ref->{dep} = $rt_dep // $sched_dep;
120              
121 85         259 my @messages;
122 85   100     162 for my $msg ( @{ $stop->{msgL} // [] } ) {
  85         567  
123 7 50 33     80 if ( $msg->{type} eq 'REM' and defined $msg->{remX} ) {
    0 0        
124             push( @messages,
125 7         49 $hafas->add_message( $opt{common}{remL}[ $msg->{remX} ] ) );
126             }
127             elsif ( $msg->{type} eq 'HIM' and defined $msg->{himX} ) {
128             push( @messages,
129 0         0 $hafas->add_message( $opt{common}{himL}[ $msg->{himX} ], 1 ) );
130             }
131             else {
132 0         0 say "Unknown message type $msg->{type}";
133             }
134             }
135 85         272 $ref->{messages} = \@messages;
136              
137 85         345 $ref->{load} = {};
138 85   100     157 for my $tco_id ( @{ $stop->{dTrnCmpSX}{tcocX} // [] } ) {
  85         412  
139 60         159 my $tco_kv = $common->{tcocL}[$tco_id];
140              
141             # BVG has rRT (real-time?) and r (prognosed?); others only have r
142 60   33     275 my $load = $tco_kv->{rRT} // $tco_kv->{r};
143              
144             # BVG uses 11 .. 13 rather than 1 .. 4
145 60 50 33     231 if ( defined $load and $load > 10 ) {
146 0         0 $load -= 10;
147             }
148              
149 60         239 $ref->{load}{ $tco_kv->{c} } = $load;
150             }
151              
152 85         740 return $ref;
153             }
154              
155             # }}}
156              
157             sub handle_day_change {
158 400     400 0 2236 my ( $self, %opt ) = @_;
159 400         852 my $date = $opt{date};
160 400         848 my $timestr = $opt{input};
161 400         739 my $offset = $opt{offset};
162              
163 400 100       975 if ( not defined $timestr ) {
164 190         560 return;
165             }
166              
167 210 100       523 if ( length($timestr) == 8 ) {
168              
169             # arrival time includes a day offset
170 2         12 my $offset_date = $opt{ref}->clone;
171 2         43 $offset_date->add( days => substr( $timestr, 0, 2, q{} ) );
172 2         3912 $offset_date = $offset_date->strftime('%Y%m%d');
173 2         162 $timestr = $opt{strp_obj}->parse_datetime("${offset_date}T${timestr}");
174             }
175             else {
176 208         1125 $timestr = $opt{strp_obj}->parse_datetime("${date}T${timestr}");
177             }
178              
179 210 50 33     244897 if ( defined $offset and $offset != $timestr->offset / 60 ) {
180 0         0 $self->{tz_offset} = $offset - $timestr->offset / 60;
181 0         0 $timestr->subtract( minutes => $self->{tz_offset} );
182             }
183              
184 210         23099 return $timestr;
185             }
186              
187             sub messages {
188 0     0 1   my ($self) = @_;
189              
190 0 0         if ( $self->{messages} ) {
191 0           return @{ $self->{messages} };
  0            
192             }
193 0           return;
194             }
195              
196             sub TO_JSON {
197 0     0 0   my ($self) = @_;
198              
199 0           my $ret = { %{$self} };
  0            
200              
201 0           for my $k ( keys %{$ret} ) {
  0            
202 0 0         if ( ref( $ret->{$k} ) eq 'DateTime' ) {
203 0           $ret->{$k} = $ret->{$k}->epoch;
204             }
205             }
206              
207 0           return $ret;
208             }
209              
210             1;
211              
212             __END__
213              
214             =head1 NAME
215              
216             Travel::Status::DE::HAFAS::Stop - Information about a HAFAS stop.
217              
218             =head1 SYNOPSIS
219              
220             # in journey mode
221             for my $stop ($journey->route) {
222             printf(
223             %5s -> %5s %s\n",
224             $stop->arr ? $stop->arr->strftime('%H:%M') : '--:--',
225             $stop->dep ? $stop->dep->strftime('%H:%M') : '--:--',
226             $stop->loc->name
227             );
228             }
229              
230             =head1 VERSION
231              
232             version 6.25
233              
234             =head1 DESCRIPTION
235              
236             Travel::Status::DE::HAFAS::Stop describes a
237             Travel::Status::DE::HAFAS::Journey(3pm)'s stop at a given
238             Travel::Status::DE::HAFAS::Location(3pm) with arrival/departure time,
239             platform, etc.
240              
241             All date and time entries refer to the backend time zone (Europe/Berlin in most
242             cases) and do not take local time into account; see B<tz_offset> for the
243             latter.
244              
245             =head1 METHODS
246              
247             =head2 ACCESSORS
248              
249             =over
250              
251             =item $stop->loc
252              
253             Travel::Status::DE::HAFAS::Location(3pm) instance describing stop name, EVA
254             ID, et cetera.
255              
256             =item $stop->rt_arr
257              
258             DateTime object for actual arrival.
259              
260             =item $stop->sched_arr
261              
262             DateTime object for scheduled arrival.
263              
264             =item $stop->arr
265              
266             DateTime object for actual or scheduled arrival.
267              
268             =item $stop->arr_delay
269              
270             Arrival delay in minutes.
271              
272             =item $stop->arr_cancelled
273              
274             Arrival is cancelled.
275              
276             =item $stop->rt_dep
277              
278             DateTime object for actual departure.
279              
280             =item $stop->sched_dep
281              
282             DateTime object for scheduled departure.
283              
284             =item $stop->dep
285              
286             DateTIme object for actual or scheduled departure.
287              
288             =item $stop->dep_delay
289              
290             Departure delay in minutes.
291              
292             =item $stop->dep_cancelled
293              
294             Departure is cancelled.
295              
296             =item $stop->tz_offset
297              
298             Offset between backend time zone (default: Europe/Berlin) and this stop's time
299             zone in minutes, if any. For instance, if the backend uses UTC+2 (CEST) and the
300             stop uses UTC+1 (IST), tz_offset is -60. Returns undef if both use the same
301             time zone (or rather, the same UTC offset).
302              
303             =item $stop->delay
304              
305             Departure or arrival delay in minutes.
306              
307             =item $stop->direction
308              
309             Direction signage from this stop on, undef if unchanged.
310              
311             =item $stop->messages
312              
313             List of Travel::Status::DE::HAFAS::Message(3pm) instances related to this stop.
314             These typically refer to delay reasons, platform changes, or changes in the
315             line number / direction heading.
316              
317             =item $stop->prod_arr
318              
319             Travel::Status::DE::HAFAS::Product(3pm) instance describing the transit product
320             (name, type, line number, operator, ...) upon arrival at this stop.
321              
322             =item $stop->prod_dep
323              
324             Travel::Status::DE::HAFAS::Product(3pm) instance describing the transit product
325             (name, type, line number, operator, ...) upon departure from this stop.
326              
327             =item $stop->rt_platform
328              
329             Actual platform.
330              
331             =item $stop->sched_platform
332              
333             Scheduled platform.
334              
335             =item $stop->platform
336              
337             Actual or scheduled platform.
338              
339             =item $stop->is_changed_platform
340              
341             True if real-time and scheduled platform disagree.
342              
343             =item $stop->is_additional
344              
345             True if the stop is an unscheduled addition to the train's route.
346              
347             =item $stop->load
348              
349             Expected utilization / passenger load from this stop on.
350              
351             =back
352              
353             =head1 DIAGNOSTICS
354              
355             None.
356              
357             =head1 DEPENDENCIES
358              
359             =over
360              
361             =item Class::Accessor(3pm)
362              
363             =back
364              
365             =head1 BUGS AND LIMITATIONS
366              
367             None known.
368              
369             =head1 SEE ALSO
370              
371             Travel::Status::DE::HAFAS(3pm).
372              
373             =head1 AUTHOR
374              
375             Copyright (C) 2023 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
376              
377             =head1 LICENSE
378              
379             This module is licensed under the same terms as Perl itself.