aemo 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/aemo/nem12.rb CHANGED
@@ -6,7 +6,9 @@ module AEMO
6
6
  # @since 0.1.4
7
7
  class NEM12
8
8
  # As per AEMO NEM12 Specification
9
- # http://www.aemo.com.au/Consultations/National-Electricity-Market/Open/~/media/Files/Other/consultations/nem/Meter%20Data%20File%20Format%20Specification%20NEM12_NEM13/MDFF_Specification_NEM12_NEM13_Final_v102_clean.ashx
9
+ # http://www.aemo.com.au/Consultations/National-Electricity-Market/Open/~/media/
10
+ # Files/Other/consultations/nem/Meter% 20Data% 20File% 20Format% 20Specification% 20
11
+ # NEM12_NEM13/MDFF_Specification_NEM12_NEM13_Final_v102_clean.ashx
10
12
  RECORD_INDICATORS = {
11
13
  100 => 'Header',
12
14
  200 => 'NMI Data Details',
@@ -14,7 +16,7 @@ module AEMO
14
16
  400 => 'Interval Event',
15
17
  500 => 'B2B Details',
16
18
  900 => 'End'
17
- }
19
+ }.freeze
18
20
 
19
21
  TRANSACTION_CODE_FLAGS = {
20
22
  'A' => 'Alteration',
@@ -26,33 +28,33 @@ module AEMO
26
28
  'O' => 'Other',
27
29
  'S' => 'Special Read',
28
30
  'R' => 'Removal of Meter'
29
- }
31
+ }.freeze
30
32
 
31
33
  UOM = {
32
- 'MWh' => { :name => 'Megawatt Hour', :multiplier => 1e6 },
33
- 'kWh' => { :name => 'Kilowatt Hour', :multiplier => 1e3 },
34
- 'Wh' => { :name => 'Watt Hour', :multiplier => 1 },
35
- 'MW' => { :name => 'Megawatt', :multiplier => 1e6 },
36
- 'kW' => { :name => 'Kilowatt', :multiplier => 1e3 },
37
- 'W' => { :name => 'Watt', :multiplier => 1 },
38
- 'MVArh' => { :name => 'Megavolt Ampere Reactive Hour', :multiplier => 1e6 },
39
- 'kVArh' => { :name => 'Kilovolt Ampere Reactive Hour', :multiplier => 1e3 },
40
- 'VArh' => { :name => 'Volt Ampere Reactive Hour', :multiplier => 1 },
41
- 'MVAr' => { :name => 'Megavolt Ampere Reactive', :multiplier => 1e6 },
42
- 'kVAr' => { :name => 'Kilovolt Ampere Reactive', :multiplier => 1e3 },
43
- 'VAr' => { :name => 'Volt Ampere Reactive', :multiplier => 1 },
44
- 'MVAh' => { :name => 'Megavolt Ampere Hour', :multiplier => 1e6 },
45
- 'kVAh' => { :name => 'Kilovolt Ampere Hour', :multiplier => 1e3 },
46
- 'VAh' => { :name => 'Volt Ampere Hour', :multiplier => 1 },
47
- 'MVA' => { :name => 'Megavolt Ampere', :multiplier => 1e6 },
48
- 'kVA' => { :name => 'Kilovolt Ampere', :multiplier => 1e3 },
49
- 'VA' => { :name => 'Volt Ampere', :multiplier => 1 },
50
- 'kV' => { :name => 'Kilovolt', :multiplier => 1e3 },
51
- 'V' => { :name => 'Volt', :multiplier => 1 },
52
- 'kA' => { :name => 'Kiloampere', :multiplier => 1e3 },
53
- 'A' => { :name => 'Ampere', :multiplier => 1 },
54
- 'pf' => { :name => 'Power Factor', :multiplier => 1 }
55
- }
34
+ 'MWh' => { name: 'Megawatt Hour', multiplier: 1e6 },
35
+ 'kWh' => { name: 'Kilowatt Hour', multiplier: 1e3 },
36
+ 'Wh' => { name: 'Watt Hour', multiplier: 1 },
37
+ 'MW' => { name: 'Megawatt', multiplier: 1e6 },
38
+ 'kW' => { name: 'Kilowatt', multiplier: 1e3 },
39
+ 'W' => { name: 'Watt', multiplier: 1 },
40
+ 'MVArh' => { name: 'Megavolt Ampere Reactive Hour', multiplier: 1e6 },
41
+ 'kVArh' => { name: 'Kilovolt Ampere Reactive Hour', multiplier: 1e3 },
42
+ 'VArh' => { name: 'Volt Ampere Reactive Hour', multiplier: 1 },
43
+ 'MVAr' => { name: 'Megavolt Ampere Reactive', multiplier: 1e6 },
44
+ 'kVAr' => { name: 'Kilovolt Ampere Reactive', multiplier: 1e3 },
45
+ 'VAr' => { name: 'Volt Ampere Reactive', multiplier: 1 },
46
+ 'MVAh' => { name: 'Megavolt Ampere Hour', multiplier: 1e6 },
47
+ 'kVAh' => { name: 'Kilovolt Ampere Hour', multiplier: 1e3 },
48
+ 'VAh' => { name: 'Volt Ampere Hour', multiplier: 1 },
49
+ 'MVA' => { name: 'Megavolt Ampere', multiplier: 1e6 },
50
+ 'kVA' => { name: 'Kilovolt Ampere', multiplier: 1e3 },
51
+ 'VA' => { name: 'Volt Ampere', multiplier: 1 },
52
+ 'kV' => { name: 'Kilovolt', multiplier: 1e3 },
53
+ 'V' => { name: 'Volt', multiplier: 1 },
54
+ 'kA' => { name: 'Kiloampere', multiplier: 1e3 },
55
+ 'A' => { name: 'Ampere', multiplier: 1 },
56
+ 'pf' => { name: 'Power Factor', multiplier: 1 }
57
+ }.freeze
56
58
 
57
59
  UOM_NON_SPEC_MAPPING = {
58
60
  'MWH' => 'MWh',
@@ -78,7 +80,7 @@ module AEMO
78
80
  'KA' => 'kA',
79
81
  'A' => 'A',
80
82
  'PF' => 'pf'
81
- }
83
+ }.freeze
82
84
 
83
85
  QUALITY_FLAGS = {
84
86
  'A' => 'Actual Data',
@@ -86,41 +88,41 @@ module AEMO
86
88
  'F' => 'Final Substituted Data',
87
89
  'N' => 'Null Data',
88
90
  'S' => 'Substituted Data',
89
- 'V' => 'Variable Data',
90
- }
91
+ 'V' => 'Variable Data'
92
+ }.freeze
91
93
 
92
94
  METHOD_FLAGS = {
93
- 11 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Check", description: "" },
94
- 12 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Calculated", description: "" },
95
- 13 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "SCADA", description: "" },
96
- 14 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Like Day", description: "" },
97
- 15 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Average Like Day", description: "" },
98
- 16 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Agreed", description: "" },
99
- 17 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Linear", description: "" },
100
- 18 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Alternate", description: "" },
101
- 19 => { type: ["SUB"], installation_type: [1,2,3,4], short_descriptor: "Zero", description: "" },
102
- 51 => { type: ["EST","SUB"], installation_type: 5, short_descriptor: "Previous Year", description: "" },
103
- 52 => { type: ["EST","SUB"], installation_type: 5, short_descriptor: "Previous Read", description: "" },
104
- 53 => { type: ["SUB"], installation_type: 5, short_descriptor: "Revision", description: "" },
105
- 54 => { type: ["SUB"], installation_type: 5, short_descriptor: "Linear", description: "" },
106
- 55 => { type: ["SUB"], installation_type: 5, short_descriptor: "Agreed", description: "" },
107
- 56 => { type: ["EST","SUB"], installation_type: 5, short_descriptor: "Prior to First Read - Agreed", description: "" },
108
- 57 => { type: ["EST","SUB"], installation_type: 5, short_descriptor: "Customer Class", description: "" },
109
- 58 => { type: ["EST","SUB"], installation_type: 5, short_descriptor: "Zero", description: "" },
110
- 61 => { type: ["EST","SUB"], installation_type: 6, short_descriptor: "Previous Year", description: "" },
111
- 62 => { type: ["EST","SUB"], installation_type: 6, short_descriptor: "Previous Read", description: "" },
112
- 63 => { type: ["EST","SUB"], installation_type: 6, short_descriptor: "Customer Class", description: "" },
113
- 64 => { type: ["SUB"], installation_type: 6, short_descriptor: "Agreed", description: "" },
114
- 65 => { type: ["EST"], installation_type: 6, short_descriptor: "ADL", description: "" },
115
- 66 => { type: ["SUB"], installation_type: 6, short_descriptor: "Revision", description: "" },
116
- 67 => { type: ["SUB"], installation_type: 6, short_descriptor: "Customer Read", description: "" },
117
- 68 => { type: ["EST","SUB"], installation_type: 6, short_descriptor: "Zero", description: "" },
118
- 71 => { type: ["SUB"], installation_type: 7, short_descriptor: "Recalculation", description: "" },
119
- 72 => { type: ["SUB"], installation_type: 7, short_descriptor: "Revised Table", description: "" },
120
- 73 => { type: ["SUB"], installation_type: 7, short_descriptor: "Revised Algorithm", description: "" },
121
- 74 => { type: ["SUB"], installation_type: 7, short_descriptor: "Agreed", description: "" },
122
- 75 => { type: ["EST"], installation_type: 7, short_descriptor: "Existing Table", description: "" }
123
- }
95
+ 11 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Check', description: '' },
96
+ 12 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Calculated', description: '' },
97
+ 13 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'SCADA', description: '' },
98
+ 14 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Like Day', description: '' },
99
+ 15 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Average Like Day', description: '' },
100
+ 16 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Agreed', description: '' },
101
+ 17 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Linear', description: '' },
102
+ 18 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Alternate', description: '' },
103
+ 19 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Zero', description: '' },
104
+ 51 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Previous Year', description: '' },
105
+ 52 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Previous Read', description: '' },
106
+ 53 => { type: %w(SUB), installation_type: 5, short_descriptor: 'Revision', description: '' },
107
+ 54 => { type: %w(SUB), installation_type: 5, short_descriptor: 'Linear', description: '' },
108
+ 55 => { type: %w(SUB), installation_type: 5, short_descriptor: 'Agreed', description: '' },
109
+ 56 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Prior to First Read - Agreed', description: '' },
110
+ 57 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Customer Class', description: '' },
111
+ 58 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Zero', description: '' },
112
+ 61 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Previous Year', description: '' },
113
+ 62 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Previous Read', description: '' },
114
+ 63 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Customer Class', description: '' },
115
+ 64 => { type: %w(SUB), installation_type: 6, short_descriptor: 'Agreed', description: '' },
116
+ 65 => { type: %w(EST), installation_type: 6, short_descriptor: 'ADL', description: '' },
117
+ 66 => { type: %w(SUB), installation_type: 6, short_descriptor: 'Revision', description: '' },
118
+ 67 => { type: %w(SUB), installation_type: 6, short_descriptor: 'Customer Read', description: '' },
119
+ 68 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Zero', description: '' },
120
+ 71 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Recalculation', description: '' },
121
+ 72 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Revised Table', description: '' },
122
+ 73 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Revised Algorithm', description: '' },
123
+ 74 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Agreed', description: '' },
124
+ 75 => { type: %w(EST), installation_type: 7, short_descriptor: 'Existing Table', description: '' }
125
+ }.freeze
124
126
 
125
127
  REASON_CODES = {
126
128
  0 => 'Free Text Description',
@@ -223,40 +225,39 @@ module AEMO
223
225
  97 => 'Excluded Data',
224
226
  98 => 'Parity Error',
225
227
  99 => 'Energy Type (Register Changed)'
226
-
227
- }
228
+ }.freeze
228
229
 
229
230
  DATA_STREAM_SUFFIX = {
230
231
  # Averaged Data Streams
231
- 'A' => { :stream => 'Average', :description => 'Import', :units => 'kWh' },
232
- 'D' => { :stream => 'Average', :description => 'Export', :units => 'kWh' },
233
- 'J' => { :stream => 'Average', :description => 'Import', :units => 'kVArh' },
234
- 'P' => { :stream => 'Average', :description => 'Export', :units => 'kVArh' },
235
- 'S' => { :stream => 'Average', :description => '', :units => 'kVAh' },
232
+ 'A' => { stream: 'Average', description: 'Import', units: 'kWh' },
233
+ 'D' => { stream: 'Average', description: 'Export', units: 'kWh' },
234
+ 'J' => { stream: 'Average', description: 'Import', units: 'kVArh' },
235
+ 'P' => { stream: 'Average', description: 'Export', units: 'kVArh' },
236
+ 'S' => { stream: 'Average', description: '', units: 'kVAh' },
236
237
  # Master Data Streams
237
- 'B' => { :stream => 'Master', :description => 'Import', :units => 'kWh' },
238
- 'E' => { :stream => 'Master', :description => 'Export', :units => 'kWh' },
239
- 'K' => { :stream => 'Master', :description => 'Import', :units => 'kVArh' },
240
- 'Q' => { :stream => 'Master', :description => 'Export', :units => 'kVArh' },
241
- 'T' => { :stream => 'Master', :description => '', :units => 'kVAh' },
242
- 'G' => { :stream => 'Master', :description => 'Power Factor', :units => 'PF' },
243
- 'H' => { :stream => 'Master', :description => 'Q Metering', :units => 'Qh' },
244
- 'M' => { :stream => 'Master', :description => 'Par Metering', :units => 'parh' },
245
- 'V' => { :stream => 'Master', :description => 'Volts or V2h or Amps or A2h', :units => '' },
238
+ 'B' => { stream: 'Master', description: 'Import', units: 'kWh' },
239
+ 'E' => { stream: 'Master', description: 'Export', units: 'kWh' },
240
+ 'K' => { stream: 'Master', description: 'Import', units: 'kVArh' },
241
+ 'Q' => { stream: 'Master', description: 'Export', units: 'kVArh' },
242
+ 'T' => { stream: 'Master', description: '', units: 'kVAh' },
243
+ 'G' => { stream: 'Master', description: 'Power Factor', units: 'PF' },
244
+ 'H' => { stream: 'Master', description: 'Q Metering', units: 'Qh' },
245
+ 'M' => { stream: 'Master', description: 'Par Metering', units: 'parh' },
246
+ 'V' => { stream: 'Master', description: 'Volts or V2h or Amps or A2h', units: '' },
246
247
  # Check Meter Streams
247
- 'C' => { :stream => 'Check', :description => 'Import', :units => 'kWh' },
248
- 'F' => { :stream => 'Check', :description => 'Export', :units => 'kWh' },
249
- 'L' => { :stream => 'Check', :description => 'Import', :units => 'kVArh' },
250
- 'R' => { :stream => 'Check', :description => 'Export', :units => 'kVArh' },
251
- 'U' => { :stream => 'Check', :description => '', :units => 'kVAh' },
252
- 'Y' => { :stream => 'Check', :description => 'Q Metering', :units => 'Qh' },
253
- 'W' => { :stream => 'Check', :description => 'Par Metering Path', :units => '' },
254
- 'Z' => { :stream => 'Check', :description => 'Volts or V2h or Amps or A2h', :units => '' },
248
+ 'C' => { stream: 'Check', description: 'Import', units: 'kWh' },
249
+ 'F' => { stream: 'Check', description: 'Export', units: 'kWh' },
250
+ 'L' => { stream: 'Check', description: 'Import', units: 'kVArh' },
251
+ 'R' => { stream: 'Check', description: 'Export', units: 'kVArh' },
252
+ 'U' => { stream: 'Check', description: '', units: 'kVAh' },
253
+ 'Y' => { stream: 'Check', description: 'Q Metering', units: 'Qh' },
254
+ 'W' => { stream: 'Check', description: 'Par Metering Path', units: '' },
255
+ 'Z' => { stream: 'Check', description: 'Volts or V2h or Amps or A2h', units: '' },
255
256
  # Net Meter Streams
256
257
  # AEMO: NOTE THAT D AND J ARE PREVIOUSLY DEFINED
257
- # 'D' => { :stream => 'Net', :description => 'Net', :units => 'kWh' },
258
- # 'J' => { :stream => 'Net', :description => 'Net', :units => 'kVArh' }
259
- }
258
+ # 'D' => { stream: 'Net', description: 'Net', units: 'kWh' },
259
+ # 'J' => { stream: 'Net', description: 'Net', units: 'kVArh' }
260
+ }.freeze
260
261
 
261
262
  @file_contents = nil
262
263
  @header = nil
@@ -267,19 +268,18 @@ module AEMO
267
268
  @interval_data = []
268
269
  @interval_events = []
269
270
 
270
-
271
271
  attr_accessor :nmi, :file_contents
272
272
  attr_reader :data_details, :interval_data, :interval_events
273
273
  attr_accessor :file_contents, :header, :nmi_data_details, :nmi
274
274
 
275
275
  # Initialize a NEM12 file
276
- def initialize(nmi,options={})
276
+ def initialize(nmi, options = {})
277
277
  @nmi = AEMO::NMI.new(nmi) unless nmi.empty?
278
278
  @data_details = []
279
279
  @interval_data = []
280
280
  @interval_events = []
281
281
  options.keys.each do |key|
282
- eval "self.#{key} = #{options[key]}"
282
+ send 'key=', options[key]
283
283
  end
284
284
  end
285
285
 
@@ -291,39 +291,39 @@ module AEMO
291
291
  # Parses the header record
292
292
  # @param line [String] A single line in string format
293
293
  # @return [Hash] the line parsed into a hash of information
294
- def self.parse_nem12_100(line,options={})
294
+ def self.parse_nem12_100(line, options = {})
295
295
  csv = line.parse_csv
296
296
 
297
297
  raise ArgumentError, 'RecordIndicator is not 100' if csv[0] != '100'
298
298
  raise ArgumentError, 'VersionHeader is not NEM12' if csv[1] != 'NEM12'
299
- if options[:strict]
300
- raise ArgumentError, 'DateTime is not valid' if csv[2].match(/\d{12}/).nil? || csv[2] != Time.parse("#{csv[2]}00").strftime('%Y%m%d%H%M')
299
+ if options[:strict] && (csv[2].match(/\d{12}/).nil? || csv[2] != Time.parse("#{csv[2]}00").strftime('%Y%m%d%H%M'))
300
+ raise ArgumentError, 'DateTime is not valid'
301
301
  end
302
302
  raise ArgumentError, 'FromParticispant is not valid' if csv[3].match(/.{1,10}/).nil?
303
303
  raise ArgumentError, 'ToParticispant is not valid' if csv[4].match(/.{1,10}/).nil?
304
304
 
305
- nem12_100 = {
306
- :record_indicator => csv[0].to_i,
307
- :version_header => csv[1],
308
- :datetime => Time.parse("#{csv[2]}+1000"),
309
- :from_participant => csv[3],
310
- :to_participant => csv[4]
305
+ {
306
+ record_indicator: csv[0].to_i,
307
+ version_header: csv[1],
308
+ datetime: Time.parse("#{csv[2]}+1000"),
309
+ from_participant: csv[3],
310
+ to_participant: csv[4]
311
311
  }
312
312
  end
313
313
 
314
314
  # Parses the NMI Data Details
315
315
  # @param line [String] A single line in string format
316
316
  # @return [Hash] the line parsed into a hash of information
317
- def parse_nem12_200(line,options={})
317
+ def parse_nem12_200(line, _options = {})
318
318
  csv = line.parse_csv
319
319
 
320
320
  raise ArgumentError, 'RecordIndicator is not 200' if csv[0] != '200'
321
- raise ArgumentError, 'NMI is not valid' if !AEMO::NMI.valid_nmi?(csv[1])
321
+ raise ArgumentError, 'NMI is not valid' unless AEMO::NMI.valid_nmi?(csv[1])
322
322
  raise ArgumentError, 'NMIConfiguration is not valid' if csv[2].match(/.{1,240}/).nil?
323
- unless csv[3].nil?
324
- raise ArgumentError, 'RegisterID is not valid' if csv[3].match(/.{1,10}/).nil?
323
+ if !csv[3].nil? && csv[3].match(/.{1,10}/).nil?
324
+ raise ArgumentError, 'RegisterID is not valid'
325
325
  end
326
- raise ArgumentError, 'NMISuffix is not valid' if csv[4].match(/[A-HJ-NP-Z][1-9A-HJ-NP-Z]/).nil?
326
+ raise ArgumentError, 'NMISuffix is not valid' if csv[4].match(/[A-HJ-NP-Z][1-9A-HJ-NP-Z]/).nil?
327
327
  if !csv[5].nil? && !csv[5].empty? && !csv[5].match(/^\s*$/)
328
328
  raise ArgumentError, 'MDMDataStreamIdentifier is not valid' if csv[5].match(/[A-Z0-9]{2}/).nil?
329
329
  end
@@ -331,58 +331,60 @@ module AEMO
331
331
  raise ArgumentError, 'MeterSerialNumber is not valid' if csv[6].match(/[A-Z0-9]{2}/).nil?
332
332
  end
333
333
  raise ArgumentError, 'UOM is not valid' if csv[7].upcase.match(/[A-Z0-9]{2}/).nil?
334
- raise ArgumentError, 'UOM is not valid' unless UOM.keys.map{|k| k.upcase}.include?(csv[7].upcase)
334
+ raise ArgumentError, 'UOM is not valid' unless UOM.keys.map(&:upcase).include?(csv[7].upcase)
335
335
  raise ArgumentError, 'IntervalLength is not valid' unless %w(1 5 10 15 30).include?(csv[8])
336
- # raise ArgumentError, 'NextScheduledReadDate is not valid' if csv[9].match(/\d{8}/).nil? || csv[9] != Time.parse("#{csv[9]}").strftime('%Y%m%d')
336
+ # raise ArgumentError, 'NextScheduledReadDate is not valid' if csv[9].match(/\d{8}/).nil? || csv[9] != Time.parse('#{csv[9]}').strftime('%Y%m%d')
337
337
 
338
338
  @nmi = AEMO::NMI.new(csv[1])
339
339
 
340
340
  # Push onto the stack
341
341
  @data_details << {
342
- :record_indicator => csv[0].to_i,
343
- :nmi => csv[1],
344
- :nmi_configuration => csv[2],
345
- :register_id => csv[3],
346
- :nmi_suffix => csv[4],
347
- :mdm_data_streaming_identifier => csv[5],
348
- :meter_serial_nubmer => csv[6],
349
- :uom => csv[7].upcase,
350
- :interval_length => csv[8].to_i,
351
- :next_scheduled_read_date => csv[9],
342
+ record_indicator: csv[0].to_i,
343
+ nmi: csv[1],
344
+ nmi_configuration: csv[2],
345
+ register_id: csv[3],
346
+ nmi_suffix: csv[4],
347
+ mdm_data_streaming_identifier: csv[5],
348
+ meter_serial_nubmer: csv[6],
349
+ uom: csv[7].upcase,
350
+ interval_length: csv[8].to_i,
351
+ next_scheduled_read_date: csv[9]
352
352
  }
353
353
  end
354
354
 
355
355
  # @param line [String] A single line in string format
356
356
  # @return [Array of hashes] the line parsed into a hash of information
357
- def parse_nem12_300(line,options={})
357
+ def parse_nem12_300(line, options = {})
358
358
  csv = line.parse_csv
359
359
 
360
360
  raise TypeError, 'Expected NMI Data Details to exist with IntervalLength specified' if @data_details.last.nil? || @data_details.last[:interval_length].nil?
361
361
  number_of_intervals = 1440 / @data_details.last[:interval_length]
362
362
  intervals_offset = number_of_intervals + 2
363
363
 
364
- raise ArgumentError, 'RecordIndicator is not 300' if csv[0] != '300'
365
- raise ArgumentError, 'IntervalDate is not valid' if csv[1].match(/\d{8}/).nil? || csv[1] != Time.parse("#{csv[1]}").strftime('%Y%m%d')
366
- (2..(number_of_intervals+1)).each do |i|
367
- raise ArgumentError, "Interval number #{i-1} is not valid" if csv[i].match(/\d+(\.\d+)?/).nil?
364
+ raise ArgumentError, 'RecordIndicator is not 300' if csv[0] != '300'
365
+ raise ArgumentError, 'IntervalDate is not valid' if csv[1].match(/\d{8}/).nil? || csv[1] != Time.parse(csv[1].to_s).strftime('%Y%m%d')
366
+ (2..(number_of_intervals + 1)).each do |i|
367
+ raise ArgumentError, "Interval number #{i - 1} is not valid" if csv[i].match(/\d+(\.\d+)?/).nil?
368
368
  end
369
- raise ArgumentError, 'QualityMethod is not valid' unless csv[intervals_offset + 0].class == String
370
- raise ArgumentError, 'QualityMethod does not have valid length' unless [1,3].include?(csv[intervals_offset + 0].length)
371
- raise ArgumentError, 'QualityMethod does not have valid QualityFlag' unless QUALITY_FLAGS.keys.include?(csv[intervals_offset + 0][0])
369
+ raise ArgumentError, 'QualityMethod is not valid' unless csv[intervals_offset + 0].class == String
370
+ raise ArgumentError, 'QualityMethod does not have valid length' unless [1, 3].include?(csv[intervals_offset + 0].length)
371
+ raise ArgumentError, 'QualityMethod does not have valid QualityFlag' unless QUALITY_FLAGS.keys.include?(csv[intervals_offset + 0][0])
372
372
  unless %w(A N V).include?(csv[intervals_offset + 0][0])
373
- raise ArgumentError, 'QualityMethod does not have valid length' unless csv[intervals_offset + 0].length == 3
374
- raise ArgumentError, 'QualityMethod does not have valid MethodFlag' unless METHOD_FLAGS.keys.include?(csv[intervals_offset + 0][1..2].to_i)
373
+ raise ArgumentError, 'QualityMethod does not have valid length' unless csv[intervals_offset + 0].length == 3
374
+ raise ArgumentError, 'QualityMethod does not have valid MethodFlag' unless METHOD_FLAGS.keys.include?(csv[intervals_offset + 0][1..2].to_i)
375
375
  end
376
376
  unless %w(A N E).include?(csv[intervals_offset + 0][0])
377
- raise ArgumentError, 'ReasonCode is not valid' unless REASON_CODES.keys.include?(csv[intervals_offset + 1].to_i)
377
+ raise ArgumentError, 'ReasonCode is not valid' unless REASON_CODES.keys.include?(csv[intervals_offset + 1].to_i)
378
378
  end
379
379
  if !csv[intervals_offset + 1].nil? && csv[intervals_offset + 1].to_i == 0
380
- raise ArgumentError, 'ReasonDescription is not valid' unless csv[intervals_offset + 2].class == String && csv[intervals_offset + 2].length > 0
380
+ raise ArgumentError, 'ReasonDescription is not valid' unless csv[intervals_offset + 2].class == String && !csv[intervals_offset + 2].empty?
381
381
  end
382
382
  if options[:strict]
383
- raise ArgumentError, 'UpdateDateTime is not valid' if csv[intervals_offset + 3].match(/\d{14}/).nil? || csv[intervals_offset + 3] != Time.parse("#{csv[intervals_offset + 3]}").strftime('%Y%m%d%H%M%S')
384
- unless csv[intervals_offset + 4].nil?
385
- raise ArgumentError, 'MSATSLoadDateTime is not valid' if csv[intervals_offset + 4].match(/\d{14}/).nil? || csv[intervals_offset + 4] != Time.parse("#{csv[intervals_offset + 4]}").strftime('%Y%m%d%H%M%S')
383
+ if csv[intervals_offset + 3].match(/\d{14}/).nil? || csv[intervals_offset + 3] != Time.parse(csv[intervals_offset + 3].to_s).strftime('%Y%m%d%H%M%S')
384
+ raise ArgumentError, 'UpdateDateTime is not valid'
385
+ end
386
+ if !csv[intervals_offset + 4].nil? && csv[intervals_offset + 4].match(/\d{14}/).nil? || csv[intervals_offset + 4] != Time.parse(csv[intervals_offset + 4].to_s).strftime('%Y%m%d%H%M%S')
387
+ raise ArgumentError, 'MSATSLoadDateTime is not valid'
386
388
  end
387
389
  end
388
390
 
@@ -393,19 +395,19 @@ module AEMO
393
395
  flag ||= { quality_flag: nil, method_flag: nil, reason_code: nil }
394
396
  if csv[intervals_offset + 0].length == 3
395
397
  flag[:quality_flag] = csv[intervals_offset + 0][0]
396
- flag[:method_flag] = csv[intervals_offset + 0][1,2].to_i
398
+ flag[:method_flag] = csv[intervals_offset + 0][1, 2].to_i
397
399
  end
398
400
  unless csv[intervals_offset + 1].nil?
399
401
  flag[:reason_code] = csv[intervals_offset + 1].to_i
400
402
  end
401
403
  end
402
404
 
403
- base_interval = { data_details: @data_details.last, datetime: Time.parse("#{csv[1]}000000+1000"), value: nil, flag: flag}
405
+ base_interval = { data_details: @data_details.last, datetime: Time.parse("#{csv[1]}000000+1000"), value: nil, flag: flag }
404
406
 
405
407
  intervals = []
406
- (2..(number_of_intervals+1)).each do |i|
408
+ (2..(number_of_intervals + 1)).each do |i|
407
409
  interval = base_interval.dup
408
- interval[:datetime] += (i-1) * interval[:data_details][:interval_length] * 60
410
+ interval[:datetime] += (i - 1) * interval[:data_details][:interval_length] * 60
409
411
  interval[:value] = csv[i].to_f
410
412
  intervals << interval
411
413
  end
@@ -437,19 +439,19 @@ module AEMO
437
439
  # Interval Numbers are 1-indexed
438
440
  ((csv[1].to_i)..(csv[2].to_i)).each do |i|
439
441
  interval_event = base_interval_event.dup
440
- interval_event[:datetime] = @interval_data[interval_start_point + (i-1)][:datetime]
442
+ interval_event[:datetime] = @interval_data[interval_start_point + (i - 1)][:datetime]
441
443
  interval_events << interval_event
442
444
  # Create flag details
443
445
  flag ||= { quality_flag: nil, method_flag: nil, reason_code: nil }
444
446
  unless interval_event[:quality_method].nil?
445
447
  flag[:quality_flag] = interval_event[:quality_method][0]
446
- flag[:method_flag] = interval_event[:quality_method][1,2].to_i
448
+ flag[:method_flag] = interval_event[:quality_method][1, 2].to_i
447
449
  end
448
450
  unless interval_event[:reason_code].nil?
449
451
  flag[:reason_code] = interval_event[:reason_code]
450
452
  end
451
453
  # Update with flag details
452
- @interval_data[interval_start_point + (i-1)][:flag] = flag
454
+ @interval_data[interval_start_point + (i - 1)][:flag] = flag
453
455
  end
454
456
  @interval_events += interval_events
455
457
  end
@@ -458,18 +460,18 @@ module AEMO
458
460
 
459
461
  # @param line [String] A single line in string format
460
462
  # @return [Hash] the line parsed into a hash of information
461
- def parse_nem12_500(line,options={})
463
+ def parse_nem12_500(_line, _options = {})
462
464
  end
463
465
 
464
466
  # @param line [String] A single line in string format
465
467
  # @return [Hash] the line parsed into a hash of information
466
- def parse_nem12_900(line,options={})
468
+ def parse_nem12_900(_line, _options = {})
467
469
  end
468
470
 
469
471
  # Turns the flag to a string
470
472
  #
471
473
  # @param [Hash] the object of a flag
472
- # @return [nil,String] a hyphenated string for the flag or nil
474
+ # @return [nil, String] a hyphenated string for the flag or nil
473
475
  def flag_to_s(flag)
474
476
  flag_to_s = []
475
477
  unless flag.nil?
@@ -477,44 +479,48 @@ module AEMO
477
479
  flag_to_s << METHOD_FLAGS[flag[:method_flag]][:short_descriptor] unless METHOD_FLAGS[flag[:method_flag]].nil?
478
480
  flag_to_s << REASON_CODES[flag[:reason_code]] unless REASON_CODES[flag[:reason_code]].nil?
479
481
  end
480
- (flag_to_s.length > 0) ? flag_to_s.join(" - ") : nil
482
+ flag_to_s.empty? ? nil : flag_to_s.join(' - ')
481
483
  end
482
484
 
483
485
  # @return [Array] array of a NEM12 file a given Meter + Data Stream for easy reading
484
486
  def to_a
485
- values = @interval_data.map do |d|
486
- uom = UOM[UOM_NON_SPEC_MAPPING[d[:data_details][:uom].upcase]]
487
- [ d[:data_details][:nmi],
487
+ @interval_data.map do |d|
488
+ [
489
+ d[:data_details][:nmi],
488
490
  d[:data_details][:nmi_suffix].upcase,
489
491
  d[:data_details][:uom],
490
492
  d[:datetime],
491
493
  d[:value],
492
- flag_to_s(d[:flag])]
494
+ flag_to_s(d[:flag])
495
+ ]
493
496
  end
494
- values
495
497
  end
496
498
 
497
499
  # @return [Array] CSV of a NEM12 file a given Meter + Data Stream for easy reading
498
500
  def to_csv
499
- headers = ['nmi','suffix','units','datetime','value','flags']
500
- ([headers]+self.to_a.map{|row| row[3]=row[3].strftime("%Y%m%d%H%M%S%z"); row}).map{|row| row.join(',')}.join("\n")
501
+ headers = %w(nmi suffix units datetime value flags)
502
+ ([headers] + to_a.map do |row|
503
+ row[3] = row[3].strftime('%Y%m%d%H%M%S%z')
504
+ row
505
+ end).map do |row|
506
+ row.join(', ')
507
+ end.join('\n')
501
508
  end
502
509
 
503
510
  # @param path_to_file [String] the path to a file
504
511
  # @return [] NEM12 object
505
512
  def self.parse_nem12_file(path_to_file, strict = false)
506
- parse_nem12(File.read(path_to_file),strict)
513
+ parse_nem12(File.read(path_to_file), strict)
507
514
  end
508
515
 
509
516
  # @param contents [String] the path to a file
510
517
  # @return [Array[AEMO::NEM12]] An array of NEM12 objects
511
- def self.parse_nem12(contents, strict=false)
512
- file_contents = contents.gsub(/\r/,"\n").gsub(/\n\n/,"\n").split("\n").delete_if{|line| line.empty? }
518
+ def self.parse_nem12(contents, _strict = false)
519
+ file_contents = contents.tr('\r', '\n').tr('\n\n', '\n').split('\n').delete_if(&:empty?)
513
520
  raise ArgumentError, 'First row should be have a RecordIndicator of 100 and be of type Header Record' unless file_contents.first.parse_csv[0] == '100'
514
521
 
515
522
  nem12s = []
516
- nem12_100 = AEMO::NEM12.parse_nem12_100(file_contents.first,:strict => strict)
517
- nem12 = nil
523
+ AEMO::NEM12.parse_nem12_100(file_contents.first, strict: strict)
518
524
  file_contents.each do |line|
519
525
  case line[0..2].to_i
520
526
  when 200
@@ -524,15 +530,14 @@ module AEMO
524
530
  nem12s.last.parse_nem12_300(line)
525
531
  when 400
526
532
  nem12s.last.parse_nem12_400(line)
527
- # when 500
528
- # nem12s.last.parse_nem12_500(line)
529
- # when 900
530
- # nem12s.last.parse_nem12_900(line)
533
+ # when 500
534
+ # nem12s.last.parse_nem12_500(line)
535
+ # when 900
536
+ # nem12s.last.parse_nem12_900(line)
531
537
  end
532
538
  end
533
539
  # Return the array of NEM12 groups
534
540
  nem12s
535
541
  end
536
-
537
542
  end
538
543
  end