rail_feeds 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +31 -0
  5. data/.travis.yml +26 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +6 -0
  8. data/Guardfile +25 -0
  9. data/LICENSE.md +32 -0
  10. data/README.md +77 -0
  11. data/Rakefile +3 -0
  12. data/doc/guides/Logging.md +13 -0
  13. data/doc/guides/Network Rail/CORPUS.md +34 -0
  14. data/doc/guides/Network Rail/SMART.md +39 -0
  15. data/doc/guides/Network Rail/Schedule.md +138 -0
  16. data/file +0 -0
  17. data/lib/rail_feeds/credentials.rb +45 -0
  18. data/lib/rail_feeds/logging.rb +51 -0
  19. data/lib/rail_feeds/network_rail/corpus.rb +77 -0
  20. data/lib/rail_feeds/network_rail/credentials.rb +22 -0
  21. data/lib/rail_feeds/network_rail/http_client.rb +57 -0
  22. data/lib/rail_feeds/network_rail/schedule/association.rb +208 -0
  23. data/lib/rail_feeds/network_rail/schedule/data.rb +215 -0
  24. data/lib/rail_feeds/network_rail/schedule/days.rb +95 -0
  25. data/lib/rail_feeds/network_rail/schedule/fetcher.rb +193 -0
  26. data/lib/rail_feeds/network_rail/schedule/header/cif.rb +102 -0
  27. data/lib/rail_feeds/network_rail/schedule/header/json.rb +79 -0
  28. data/lib/rail_feeds/network_rail/schedule/header.rb +22 -0
  29. data/lib/rail_feeds/network_rail/schedule/parser/cif.rb +141 -0
  30. data/lib/rail_feeds/network_rail/schedule/parser/json.rb +87 -0
  31. data/lib/rail_feeds/network_rail/schedule/parser.rb +108 -0
  32. data/lib/rail_feeds/network_rail/schedule/stp_indicator.rb +72 -0
  33. data/lib/rail_feeds/network_rail/schedule/tiploc.rb +100 -0
  34. data/lib/rail_feeds/network_rail/schedule/train_schedule/change_en_route.rb +158 -0
  35. data/lib/rail_feeds/network_rail/schedule/train_schedule/location/intermediate.rb +119 -0
  36. data/lib/rail_feeds/network_rail/schedule/train_schedule/location/origin.rb +91 -0
  37. data/lib/rail_feeds/network_rail/schedule/train_schedule/location/terminating.rb +72 -0
  38. data/lib/rail_feeds/network_rail/schedule/train_schedule/location.rb +76 -0
  39. data/lib/rail_feeds/network_rail/schedule/train_schedule.rb +392 -0
  40. data/lib/rail_feeds/network_rail/schedule.rb +33 -0
  41. data/lib/rail_feeds/network_rail/smart.rb +186 -0
  42. data/lib/rail_feeds/network_rail/stomp_client.rb +77 -0
  43. data/lib/rail_feeds/network_rail.rb +16 -0
  44. data/lib/rail_feeds/version.rb +14 -0
  45. data/lib/rail_feeds.rb +10 -0
  46. data/rail_feeds.gemspec +32 -0
  47. data/spec/fixtures/network_rail/schedule/data/full.yaml +60 -0
  48. data/spec/fixtures/network_rail/schedule/data/starting.yaml +131 -0
  49. data/spec/fixtures/network_rail/schedule/data/update-gap.yaml +10 -0
  50. data/spec/fixtures/network_rail/schedule/data/update-next.yaml +13 -0
  51. data/spec/fixtures/network_rail/schedule/data/update-old.yaml +10 -0
  52. data/spec/fixtures/network_rail/schedule/data/update.yaml +112 -0
  53. data/spec/fixtures/network_rail/schedule/parser/train_create.json +1 -0
  54. data/spec/fixtures/network_rail/schedule/parser/train_delete.json +1 -0
  55. data/spec/fixtures/network_rail/schedule/train_schedule/json-data.yaml +67 -0
  56. data/spec/rail_feeds/credentials_spec.rb +46 -0
  57. data/spec/rail_feeds/logging_spec.rb +81 -0
  58. data/spec/rail_feeds/network_rail/corpus_spec.rb +92 -0
  59. data/spec/rail_feeds/network_rail/credentials_spec.rb +22 -0
  60. data/spec/rail_feeds/network_rail/http_client_spec.rb +88 -0
  61. data/spec/rail_feeds/network_rail/schedule/association_spec.rb +205 -0
  62. data/spec/rail_feeds/network_rail/schedule/data_spec.rb +219 -0
  63. data/spec/rail_feeds/network_rail/schedule/days_shared.rb +99 -0
  64. data/spec/rail_feeds/network_rail/schedule/days_spec.rb +4 -0
  65. data/spec/rail_feeds/network_rail/schedule/fetcher_spec.rb +228 -0
  66. data/spec/rail_feeds/network_rail/schedule/header/cif_spec.rb +72 -0
  67. data/spec/rail_feeds/network_rail/schedule/header/json_spec.rb +51 -0
  68. data/spec/rail_feeds/network_rail/schedule/header_spec.rb +19 -0
  69. data/spec/rail_feeds/network_rail/schedule/parser/cif_spec.rb +197 -0
  70. data/spec/rail_feeds/network_rail/schedule/parser/json_spec.rb +172 -0
  71. data/spec/rail_feeds/network_rail/schedule/parser_spec.rb +34 -0
  72. data/spec/rail_feeds/network_rail/schedule/stp_indicator_shared.rb +49 -0
  73. data/spec/rail_feeds/network_rail/schedule/stp_indicator_spec.rb +4 -0
  74. data/spec/rail_feeds/network_rail/schedule/tiploc_spec.rb +77 -0
  75. data/spec/rail_feeds/network_rail/schedule/train_schedule/change_en_route_spec.rb +121 -0
  76. data/spec/rail_feeds/network_rail/schedule/train_schedule/location/intermediate_spec.rb +95 -0
  77. data/spec/rail_feeds/network_rail/schedule/train_schedule/location/origin_spec.rb +87 -0
  78. data/spec/rail_feeds/network_rail/schedule/train_schedule/location/terminating_spec.rb +81 -0
  79. data/spec/rail_feeds/network_rail/schedule/train_schedule/location_spec.rb +35 -0
  80. data/spec/rail_feeds/network_rail/schedule/train_schedule_spec.rb +284 -0
  81. data/spec/rail_feeds/network_rail/schedule_spec.rb +41 -0
  82. data/spec/rail_feeds/network_rail/smart_spec.rb +194 -0
  83. data/spec/rail_feeds/network_rail/stomp_client_spec.rb +151 -0
  84. data/spec/rail_feeds/network_rail_spec.rb +7 -0
  85. data/spec/rail_feeds_spec.rb +11 -0
  86. data/spec/spec_helper.rb +47 -0
  87. metadata +282 -0
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'parser/cif'
4
+ require_relative 'parser/json'
5
+
6
+ module RailFeeds
7
+ module NetworkRail
8
+ module Schedule
9
+ # A parent class for parsing schedule data read from schedule file(s).
10
+ # Children need to implement a parse_line method.
11
+ class Parser
12
+ include Logging
13
+
14
+ # rubocop:disable Metrics/ParameterLists
15
+ # Initialize a new data.
16
+ # @param [Logger, nil] logger
17
+ # The logger for outputting events, if nil the global logger will be used.
18
+ # @param [Proc, #call] on_header
19
+ # The proc to call when the header is received.
20
+ # Passes self and a RailFeeds::NetworkRail::Schedule::Header.
21
+ # @param [Proc, #call] on_tiploc_create
22
+ # The proc to call when a tiploc insertion is received.
23
+ # Passes self and a RailFeeds::NetworkRail::Schedule::Tiploc::Insert.
24
+ # @param [Proc, #call] on_tiploc_update
25
+ # The proc to call when an amendment to an existing tiploc is received.
26
+ # Passes self, tiploc_id and a RailFeeds::NetworkRail::Schedule::Tiploc::Ammend.
27
+ # @param [Proc, #call] on_tiploc_delete
28
+ # The proc to call when an existing tiploc should be deleted.
29
+ # Passes self and a tiploc_id.
30
+ # @param [Proc, #call] on_association_create
31
+ # The proc to call when a new association is received.
32
+ # Passes self and a RailFeeds::NetworkRail::Schedule::Association.
33
+ # @param [Proc, #call] on_association_update
34
+ # The proc to call when a revision to an existing association is received.
35
+ # Passes self and a RailFeeds::NetworkRail::Schedule::Association.
36
+ # @param [Proc, #call] on_association_delete
37
+ # The proc to call when an existing association should be deleted.
38
+ # Passes self and a RailFeeds::NetworkRail::Schedule::Association.
39
+ # @param [Proc, #call] on_train_schedule_create
40
+ # The proc to call when a new train schedule is received.
41
+ # Passes self and a RailFeeds::NetworkRail::Schedule::TrainSchedule::New.
42
+ # @param [Proc, #call] on_train_schedule_update
43
+ # The proc to call when a revision to an existing train schedule is received.
44
+ # Passes self and a RailFeeds::NetworkRail::Schedule::TrainSchedule::Revise.
45
+ # @param [Proc, #call] on_train_schedule_delete
46
+ # The proc to call when an existing train schedule should be deleted.
47
+ # Passes self and a RailFeeds::NetworkRail::Schedule::TrainSchedule::Delete.
48
+ # @param [Proc, #call] on_trailer
49
+ # The proc to call when the trailer (end of file record) is received.
50
+ # Passes self.
51
+ # @param [Proc, #call] on_comment
52
+ # The proc to call when a comment is received.
53
+ # Passes self and a String.
54
+ def initialize(
55
+ logger: nil,
56
+ on_header: nil, on_trailer: nil, on_comment: nil,
57
+ on_tiploc_create: nil, on_tiploc_update: nil, on_tiploc_delete: nil,
58
+ on_association_create: nil, on_association_update: nil,
59
+ on_association_delete: nil, on_train_schedule_create: nil,
60
+ on_train_schedule_update: nil, on_train_schedule_delete: nil
61
+ )
62
+ self.logger = logger unless logger.nil?
63
+ @on_header = on_header
64
+ @on_trailer = on_trailer
65
+ @on_tiploc_create = on_tiploc_create
66
+ @on_tiploc_update = on_tiploc_update
67
+ @on_tiploc_delete = on_tiploc_delete
68
+ @on_association_create = on_association_create
69
+ @on_association_update = on_association_update
70
+ @on_association_delete = on_association_delete
71
+ @on_train_schedule_create = on_train_schedule_create
72
+ @on_train_schedule_update = on_train_schedule_update
73
+ @on_train_schedule_delete = on_train_schedule_delete
74
+ @on_comment = on_comment
75
+ end
76
+ # rubocop:enable Metrics/ParameterLists
77
+
78
+ # Parse the data in CIF file.
79
+ # @param [IO] file
80
+ # The file to load data from.
81
+ def parse_file(file)
82
+ @file_ended = false
83
+ @stop_parsing = false
84
+
85
+ file.each_line do |line|
86
+ parse_line line
87
+
88
+ if @stop_parsing
89
+ logger.debug "Parsing of file #{file} was stopped."
90
+ break
91
+ end
92
+ end
93
+
94
+ fail "File is incomplete. #{file}" unless @stop_parsing || @file_ended
95
+ end
96
+
97
+ def parse_line(_line)
98
+ fail 'parse_file MUST be implemented in the child class.'
99
+ end
100
+
101
+ # Stop parsing the current file.
102
+ def stop_parsing
103
+ @stop_parsing = true
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ module NetworkRail
5
+ module Schedule
6
+ # A collection of methods for working with Short Term Planning indicators.
7
+ # Provides an stp_indicator attribute to the class.
8
+ module STPIndicator
9
+ STP_CIF_MAP = [
10
+ [:permanent, 'P'],
11
+ [:stp_new, 'N'],
12
+ [:stp_overlay, 'O'],
13
+ [:stp_cancellation, 'C']
14
+ ].freeze
15
+ private_constant :STP_CIF_MAP
16
+
17
+ def self.included(base)
18
+ base.extend ClassMethods
19
+ end
20
+
21
+ # @return [Symbol, nil] Whether (and what kind) of STP record this is:
22
+ # * :permanent - This is a permanent (not STP) record
23
+ # * :stp_new - This is a new record (not an overlay)
24
+ # * :stp_overlay - This record should be overlayed on the permanent one
25
+ # * :stp_cancellation - This is an STP cancellation of the permanaent record
26
+ def stp_indicator
27
+ @stp_indicator ||= ' '
28
+ end
29
+
30
+ # @param [Symbol, #to_s] value Whether (and what kind) of STP record this is:
31
+ # * :permanent, 'P' - This is a permanent (not STP) record
32
+ # * :stp_new, 'N' - This is a new record (not an overlay)
33
+ # * :stp_overlay, 'O' - This record should be overlayed on the permanent one
34
+ # * :stp_cancellation, 'C' - This is an STP cancellation of the permanaent record
35
+ def stp_indicator=(value)
36
+ if STP_CIF_MAP.map(&:last).include?(value.to_s)
37
+ # Convert String / to_s value to relevant Symbol
38
+ value = stp_indicator_from_cif(value)
39
+ end
40
+
41
+ unless STP_CIF_MAP.map(&:first).include?(value)
42
+ fail ArgumentError, "value (#{value.inspect}) is invalid, must be any of: " +
43
+ STP_CIF_MAP.flatten.map(&:inspect).join(', ')
44
+ end
45
+
46
+ @stp_indicator = value
47
+ end
48
+
49
+ protected
50
+
51
+ def stp_indicator_to_cif
52
+ self.class.stp_indicator_to_cif stp_indicator
53
+ end
54
+
55
+ def stp_indicator_from_cif(value)
56
+ self.class.stp_indicator_from_cif(value)
57
+ end
58
+
59
+ module ClassMethods # :nodoc:
60
+ def stp_indicator_to_cif(value)
61
+ STP_CIF_MAP.find { |i| i.first.eql?(value) }&.last
62
+ end
63
+
64
+ def stp_indicator_from_cif(value)
65
+ STP_CIF_MAP.find { |i| i.last.eql?(value) }&.first || ' '
66
+ end
67
+ end
68
+ private_constant :ClassMethods
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ module NetworkRail
5
+ module Schedule
6
+ # A class for holding information about a particular tiploc record
7
+ class Tiploc
8
+ include Comparable
9
+
10
+ # @!attribute [rw] tiploc
11
+ # @return [String] The timing point location code.
12
+ # @!attribute [rw] nlc
13
+ # @return [String] The national location code.
14
+ # @!attribute [rw] nlc_description
15
+ # @return [String] Description of location used in CAPRI.
16
+ # @!attribute [rw] tps_description
17
+ # @return [String] Description of location.
18
+ # @!attribute [rw] stanox
19
+ # @return [Integer] The TOPS location code.
20
+ # @!attribute [rw] crs
21
+ # @return [String] The CRS / 3 Alpha code for the location.
22
+
23
+ attr_accessor :tiploc, :nlc, :nlc_description, :tps_description, :stanox, :crs
24
+
25
+ def initialize(**attributes)
26
+ attributes.each do |attribute, value|
27
+ send "#{attribute}=", value
28
+ end
29
+ end
30
+
31
+ # Initialize a new tiploc from a CIF file line
32
+ def self.from_cif(line)
33
+ unless %w[TI TA TD].include?(line[0..1])
34
+ fail ArgumentError, "Invalid line:\n#{line}"
35
+ end
36
+
37
+ new(
38
+ tiploc: line[2..8].strip,
39
+ nlc: Schedule.nil_or_i(line[11..16]),
40
+ nlc_description: line[56..71].strip,
41
+ tps_description: line[18..43].strip,
42
+ stanox: Schedule.nil_or_i(line[44..48]),
43
+ crs: line[53..55].strip
44
+ )
45
+ end
46
+
47
+ # Initialize a new tiploc from a JSON file line
48
+ def self.from_json(line)
49
+ data = ::JSON.parse(line)['TiplocV1']
50
+
51
+ new(
52
+ tiploc: data['tiploc_code'],
53
+ nlc: Schedule.nil_or_i(data['nalco']),
54
+ stanox: Schedule.nil_or_i(data['stanox']),
55
+ crs: data['crs_code'],
56
+ nlc_description: Schedule.nil_or_strip(data['description']),
57
+ tps_description: data['tps_description']
58
+ )
59
+ end
60
+
61
+ def <=>(other)
62
+ hash <=> other&.hash
63
+ end
64
+
65
+ def hash
66
+ tiploc.dup
67
+ end
68
+
69
+ def to_cif
70
+ format('%-80.80s', [
71
+ 'TI',
72
+ format('%-7.7s', tiploc),
73
+ ' ',
74
+ format('%-6.6s', nlc),
75
+ ' ',
76
+ format('%-26.26s', tps_description),
77
+ format('%-5.5s', stanox),
78
+ ' ',
79
+ format('%-3.3s', crs),
80
+ format('%-16.16s', nlc_description)
81
+ ].join) + "\n"
82
+ end
83
+
84
+ def to_json
85
+ {
86
+ 'TiplocV1' => {
87
+ 'transaction_type' => 'Create',
88
+ 'tiploc_code' => tiploc,
89
+ 'nalco' => nlc.to_s,
90
+ 'stanox' => stanox.to_s,
91
+ 'crs_code' => crs,
92
+ 'description' => nlc_description,
93
+ 'tps_description' => tps_description
94
+ }
95
+ }.to_json
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ module NetworkRail
5
+ module Schedule
6
+ class TrainSchedule
7
+ # A class for holding information about a particular train's change en route
8
+ class ChangeEnRoute
9
+ # @!attribute [rw] tiploc
10
+ # @return [String] The location where the change occurs.
11
+ # @!attribute [rw] tiploc_suffix
12
+ # @return [String]
13
+ # @!attribute [rw] category
14
+ # @return [String] The train's new category.
15
+ # @!attribute [rw] signalling_headcode
16
+ # @return [String, nil] The train's new signalling_headcode.
17
+ # @!attribute [rw] reservation_headcode
18
+ # @return [Integer, nil] The train's new reservation_headcode.
19
+ # @!attribute [rw] service_code
20
+ # @return [String] The train's new service_code.
21
+ # @!attribute [rw] portion_id
22
+ # @return [String, nil] The train's new portion_id.
23
+ # @!attribute [rw] power_type
24
+ # @return [String] The train's new power_type.
25
+ # @!attribute [rw] timing_load
26
+ # @return [String, nil] The train's new timing_load.
27
+ # @!attribute [rw] speed
28
+ # @return [Integer] The train's new speed.
29
+ # @!attribute [rw] operating_characteristics
30
+ # @return [String, nil] The train's new operating_characteristics.
31
+ # @!attribute [rw] seating_class
32
+ # @return [String, nil] The train's new seating_class.
33
+ # @!attribute [rw] sleeping_class
34
+ # @return [String, nil] The train's new sleeping_class.
35
+ # @!attribute [rw] reservations
36
+ # @return [String, nil] The train's new reservations.
37
+ # @!attribute [rw] catering
38
+ # @return [String, nil] The train's new catering.
39
+ # @!attribute [rw] branding
40
+ # @return [String, nil] The train's new branding.
41
+ # @!attribute [rw] uic_code
42
+ # @return [Integer, nil] The train's new uic_code.
43
+
44
+ attr_accessor :tiploc, :tiploc_suffix, :category, :signalling_headcode,
45
+ :reservation_headcode, :service_code, :portion_id, :power_type,
46
+ :timing_load, :speed, :operating_characteristics,
47
+ :seating_class, :sleeping_class, :reservations, :catering,
48
+ :branding, :uic_code
49
+
50
+ def initialize(**attributes)
51
+ attributes.each do |attribute, value|
52
+ send "#{attribute}=", value
53
+ end
54
+ end
55
+
56
+ # rubocop:disable Metrics/AbcSize
57
+ # rubocop:disable Metrics/MethodLength
58
+ # Initialize a new change en route from a CIF file line
59
+ def self.from_cif(line)
60
+ fail ArgumentError, "Invalid line:\n#{line}" unless line[0..1].eql?('CR')
61
+
62
+ new(
63
+ tiploc: line[2..8].strip,
64
+ tiploc_suffix: line[9].to_i,
65
+ category: line[10..11].strip,
66
+ signalling_headcode: line[12..15].strip,
67
+ reservation_headcode: Schedule.nil_or_i(line[16..19]),
68
+ service_code: Schedule.nil_or_i(line[21..28]),
69
+ portion_id: Schedule.nil_or_strip(line[29]),
70
+ power_type: line[30..32].strip,
71
+ timing_load: Schedule.nil_or_strip(line[33..36]),
72
+ speed: Schedule.nil_or_i(line[37..39]),
73
+ operating_characteristics: line[40..45].strip,
74
+ seating_class: Schedule.nil_or_strip(line[46]),
75
+ sleeping_class: Schedule.nil_or_strip(line[47]),
76
+ reservations: Schedule.nil_or_strip(line[48]),
77
+ catering: line[50..53].strip,
78
+ branding: Schedule.nil_or_strip(line[54..57]),
79
+ uic_code: Schedule.nil_or_strip(line[62..66])
80
+ )
81
+ end
82
+ # rubocop:enable Metrics/AbcSize
83
+ # rubocop:enable Metrics/MethodLength
84
+
85
+ # rubocop:disable Metrics/AbcSize
86
+ # rubocop:disable Metrics/MethodLength
87
+ # Apply these changes to a train.
88
+ # @param [RailFeeds::NetworkRail::Schedule::TrainSchedule] train
89
+ # The train to apply the changes to.
90
+ # @return [RailFeeds::NetworkRail::Schedule::TrainSchedule]
91
+ # The train the changes were applied to.
92
+ def apply_to(train)
93
+ train.category = category
94
+ train.signalling_headcode = signalling_headcode
95
+ train.reservation_headcode = reservation_headcode
96
+ train.service_code = service_code
97
+ train.portion_id = portion_id
98
+ train.power_type = power_type
99
+ train.timing_load = timing_load
100
+ train.speed = speed
101
+ train.operating_characteristics = operating_characteristics
102
+ train.seating_class = seating_class
103
+ train.sleeping_class = sleeping_class
104
+ train.reservations = reservations
105
+ train.catering = catering
106
+ train.branding = branding
107
+ train.uic_code = uic_code
108
+ train
109
+ end
110
+ # rubocop:enable Metrics/AbcSize
111
+ # rubocop:enable Metrics/MethodLength
112
+
113
+ def ==(other)
114
+ hash == other&.hash
115
+ end
116
+
117
+ # rubocop:disable Metrics/AbcSize
118
+ # rubocop:disable Metrics/MethodLength
119
+ def to_cif
120
+ format('%-80.80s', [
121
+ 'CR',
122
+ format('%-7.7s', tiploc),
123
+ format('%-1.1s', tiploc_suffix),
124
+ format('%-2.2s', category),
125
+ format('%-4.4s', signalling_headcode),
126
+ format('%-4.4s', reservation_headcode),
127
+ ' ',
128
+ format('%-8.8s', service_code),
129
+ format('%-1.1s', portion_id),
130
+ format('%-3.3s', power_type),
131
+ format('%-4.4s', timing_load),
132
+ format('%-3.3s', speed),
133
+ format('%-6.6s', operating_characteristics),
134
+ format('%-1.1s', seating_class),
135
+ format('%-1.1s', sleeping_class),
136
+ format('%-1.1s', reservations),
137
+ ' ',
138
+ format('%-4.4s', catering),
139
+ format('%-4.4s', branding),
140
+ ' ',
141
+ format('%-5.5s', uic_code)
142
+ ].join) + "\n"
143
+ end
144
+ # rubocop:enable Metrics/AbcSize
145
+ # rubocop:enable Metrics/MethodLength
146
+
147
+ def hash
148
+ "#{tiploc}-#{tiploc_suffix}"
149
+ end
150
+
151
+ def to_hash_for_json
152
+ nil
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ module NetworkRail
5
+ module Schedule
6
+ class TrainSchedule
7
+ class Location
8
+ # A class for holding information about a particular train's particular location
9
+ class Intermediate < Location
10
+ # @!attribute [rw] line
11
+ # @return [String]
12
+ # @!attribute [rw] path
13
+ # @return [String]
14
+ # @!attribute [rw] scheduled_arrival
15
+ # @return [String] The scheduled time for arriving at the location.
16
+ # @!attribute [rw] scheduled_pass
17
+ # @return [String] The scheduled time for passing the location.
18
+ # @!attribute [rw] scheduled_departure
19
+ # @return [String] The sheduled time for departing from the location.
20
+ # @!attribute [rw] public_arrival
21
+ # @return [String] The public arrival time (HHMM).
22
+ # @!attribute [rw] public_departure
23
+ # @return [String] The public departure time (HHMM).
24
+ # @!attribute [rw] engineering_allowance
25
+ # @return [Float] Number of minutes.
26
+ # @!attribute [rw] pathing_allowance
27
+ # @return [Float] Number of minutes.
28
+ # @!attribute [rw] performance_allowance
29
+ # @return [Float] Number of minutes.
30
+
31
+ attr_accessor :line, :path,
32
+ :scheduled_arrival, :scheduled_departure, :scheduled_pass,
33
+ :public_arrival, :public_departure,
34
+ :engineering_allowance, :pathing_allowance,
35
+ :performance_allowance
36
+
37
+ def initialize(**attributes)
38
+ attributes.each do |attribute, value|
39
+ send "#{attribute}=", value
40
+ end
41
+ end
42
+
43
+ # rubocop:disable Metrics/AbcSize
44
+ # rubocop:disable Metrics/MethodLength
45
+ # Initialize a new intermediate location from a CIF file line
46
+ def self.from_cif(line)
47
+ fail ArgumentError, "Invalid line:\n#{line}" unless line[0..1].eql?('LI')
48
+
49
+ new(
50
+ tiploc: line[2..8].strip,
51
+ tiploc_suffix: line[9].to_i,
52
+ scheduled_arrival: line[10..14].strip,
53
+ scheduled_departure: line[15..19].strip,
54
+ scheduled_pass: line[20..24].strip,
55
+ public_arrival: line[25..28].strip,
56
+ public_departure: line[29..32].strip,
57
+ platform: line[33..35].strip,
58
+ line: line[36..38].strip,
59
+ path: line[39..41].strip,
60
+ activity: line[42..53].strip,
61
+ engineering_allowance: parse_allowance(line[54..55].strip),
62
+ pathing_allowance: parse_allowance(line[56..57].strip),
63
+ performance_allowance: parse_allowance(line[58..59].strip)
64
+ )
65
+ end
66
+ # rubocop:enable Metrics/AbcSize
67
+ # rubocop:enable Metrics/MethodLength
68
+
69
+ # rubocop:disable Metrics/AbcSize
70
+ # rubocop:disable Metrics/MethodLength
71
+ def to_cif
72
+ format('%-80.80s', [
73
+ 'LI',
74
+ format('%-7.7s', tiploc),
75
+ format('%-1.1s', tiploc_suffix),
76
+ format('%-5.5s', scheduled_arrival),
77
+ format('%-5.5s', scheduled_departure),
78
+ format('%-5.5s', scheduled_pass),
79
+ format('%-4.4s', public_arrival),
80
+ format('%-4.4s', public_departure),
81
+ format('%-3.3s', platform),
82
+ format('%-3.3s', line),
83
+ format('%-3.3s', path),
84
+ format('%-12.12s', activity),
85
+ format('%-2.2s', allowance_cif(engineering_allowance)),
86
+ format('%-2.2s', allowance_cif(pathing_allowance)),
87
+ format('%-2.2s', allowance_cif(performance_allowance))
88
+ ].join) + "\n"
89
+ end
90
+ # rubocop:enable Metrics/AbcSize
91
+ # rubocop:enable Metrics/MethodLength
92
+
93
+ # rubocop:disable Metrics/MethodLength
94
+ def to_hash_for_json
95
+ {
96
+ location_type: 'LI',
97
+ record_identity: 'LI',
98
+ tiploc_code: tiploc,
99
+ tiploc_instance: tiploc_suffix,
100
+ arrival: scheduled_arrival,
101
+ departure: scheduled_departure,
102
+ pass: scheduled_pass,
103
+ public_arrival: public_arrival,
104
+ public_departure: public_departure,
105
+ platform: platform,
106
+ line: line,
107
+ path: path,
108
+ engineering_allowance: allowance_json(engineering_allowance),
109
+ pathing_allowance: allowance_json(pathing_allowance),
110
+ performance_allowance: allowance_json(performance_allowance)
111
+ }
112
+ end
113
+ # rubocop:enable Metrics/MethodLength
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ module NetworkRail
5
+ module Schedule
6
+ class TrainSchedule
7
+ class Location
8
+ # A class for holding information about a particular train's origin location
9
+ class Origin < Location
10
+ # @!attribute [rw] line
11
+ # @return [String]
12
+ # @!attribute [rw] scheduled_departure
13
+ # @return [String] The sheduled time for departing from the location.
14
+ # @!attribute [rw] public_departure
15
+ # @return [String] The public departure time (HHMM).
16
+ # @!attribute [rw] engineering_allowance
17
+ # @return [Float] Number of minutes.
18
+ # @!attribute [rw] pathing_allowance
19
+ # @return [Float] Number of minutes.
20
+ # @!attribute [rw] performance_allowance
21
+ # @return [Float] Number of minutes.
22
+
23
+ attr_accessor :scheduled_departure, :public_departure, :line,
24
+ :engineering_allowance, :pathing_allowance,
25
+ :performance_allowance
26
+
27
+ def initialize(**attributes)
28
+ attributes.each do |attribute, value|
29
+ send "#{attribute}=", value
30
+ end
31
+ end
32
+
33
+ # rubocop:disable Metrics/AbcSize
34
+ # Initialize a new origin location from a CIF file line
35
+ def self.from_cif(line)
36
+ fail ArgumentError, "Invalid line:\n#{line}" unless line[0..1].eql?('LO')
37
+
38
+ new(
39
+ tiploc: line[2..8].strip,
40
+ tiploc_suffix: line[9].to_i,
41
+ scheduled_departure: line[10..14].strip,
42
+ public_departure: line[15..18].strip,
43
+ platform: line[19..21].strip,
44
+ line: line[22..24].strip,
45
+ activity: line[29..40].strip,
46
+ engineering_allowance: parse_allowance(line[25..26].strip),
47
+ pathing_allowance: parse_allowance(line[27..28].strip),
48
+ performance_allowance: parse_allowance(line[41..42].strip)
49
+ )
50
+ end
51
+ # rubocop:enable Metrics/AbcSize
52
+
53
+ # rubocop:disable Metrics/AbcSize
54
+ def to_cif
55
+ format('%-80.80s', [
56
+ 'LO',
57
+ format('%-7.7s', tiploc),
58
+ format('%-1.1s', tiploc_suffix),
59
+ format('%-5.5s', scheduled_departure),
60
+ format('%-4.4s', public_departure),
61
+ format('%-3.3s', platform),
62
+ format('%-3.3s', line),
63
+ format('%-2.2s', allowance_cif(engineering_allowance)),
64
+ format('%-2.2s', allowance_cif(pathing_allowance)),
65
+ format('%-12.12s', activity),
66
+ format('%-2.2s', allowance_cif(performance_allowance))
67
+ ].join) + "\n"
68
+ end
69
+ # rubocop:enable Metrics/AbcSize
70
+
71
+ def to_hash_for_json
72
+ {
73
+ location_type: 'LO',
74
+ record_identity: 'LO',
75
+ tiploc_code: tiploc,
76
+ tiploc_instance: tiploc_suffix,
77
+ departure: scheduled_departure,
78
+ public_departure: public_departure,
79
+ platform: platform,
80
+ line: line,
81
+ engineering_allowance: allowance_json(engineering_allowance),
82
+ pathing_allowance: allowance_json(pathing_allowance),
83
+ performance_allowance: allowance_json(performance_allowance)
84
+ }
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end