aemo 0.1.27 → 0.1.28

Sign up to get free protection for your applications and to get access to all the features.
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