File Coverage

blib/lib/Travel/Status/DE/HAFAS/Stop.pm
Criterion Covered Total %
statement 70 88 79.5
branch 15 24 62.5
condition 32 46 69.5
subroutine 6 8 75.0
pod 2 4 50.0
total 125 170 73.5


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