aemo 0.2.1 → 0.3.0.pre.rc1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efb09d7c295cec4a79407b95eb12f93d5c9f3d9a1baa7a1fd615fc04309ef157
4
- data.tar.gz: 592ed41f31c71a1bb4c078283316eac3fd2d3192289567ae078e8dee3bad5368
3
+ metadata.gz: f959b4ca7bb32fece4066d2cb2bd6efe309cf1dcefd9c602d9589d175b0e082e
4
+ data.tar.gz: e3a4a60539a8b97b557b751c1a65df03ddf6f301f12e596a20d7440ab44648d2
5
5
  SHA512:
6
- metadata.gz: 920cf0a92d6643d95e04573d5bfa0f91afce18843e19befd5cd259a5ddd0a4dc77b98204fd0a630015622b03013042e6785ab86a1580e1a00c089ed4ac218ba8
7
- data.tar.gz: 0a98c8b908dc817d65a3e89f2250de10f8ae90dbc8b154692eb7749794d05ea409c0f3acee1d86e7c3b16bbabb5827e4f8045165069f9f33b4162b484e5fcd78
6
+ metadata.gz: e75333c5f79fda5575ddcf30103d449b518405f500cd72fb929fc69e3f56f03d7eafdc56e8afad90c47ac092db77991b87bcf6f9cf29d08e2f40ffa97dc24f80
7
+ data.tar.gz: 203aa713e569e35896b8d4b1ae85df3581ef2f1fbf1c42a75a67557a7264f47a36c51f2ae9b66eff1b5077f24050ac6c7a6cd631e683ce8e4cf4f643d1ace645
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AEMO
4
+ # AEMO::InvalidNMIAllocationType
5
+ #
6
+ # @author Stuart Auld
7
+ # @abstract An exception representing an invalid NMI allocation type
8
+ # @since 0.3.0
9
+ class InvalidNMIAllocationType < ArgumentError
10
+ DEFAULT_MESSAGE = 'Not a valid allocation type, try one of ' \
11
+ "#{AEMO::NMI::Allocation::SUPPORTED_TYPES.join(' |')}"
12
+
13
+ # Initialize an InvalidNMIAllocationType
14
+ #
15
+ # @param [String] msg the error message
16
+ # @return [AEMO::InvalidNMIAllocationType]
17
+ def initialize(msg = DEFAULT_MESSAGE)
18
+ super
19
+ end
20
+ end
21
+ end
@@ -72,9 +72,7 @@ module AEMO
72
72
  # @return [Float] the value of the interval in Australian Dollars
73
73
  def value
74
74
  @value ||= Float::NAN
75
- if @total_demand.class == Float && @rrp.class == Float
76
- @value = (@total_demand * @rrp).round(2)
77
- end
75
+ @value = (@total_demand * @rrp).round(2) if @total_demand.class == Float && @rrp.class == Float
78
76
  @value
79
77
  end
80
78
  end
@@ -23,9 +23,7 @@ module AEMO
23
23
  #
24
24
  # @return [Array<AEMO::Market::Interval>]
25
25
  def current_dispatch
26
- if @current_dispatch.empty? || @current_dispatch.last.datetime != (Time.now - Time.now.to_i % 300)
27
- @current_dispatch = AEMO::Market.current_dispatch(@identifier)
28
- end
26
+ @current_dispatch = AEMO::Market.current_dispatch(@identifier) if @current_dispatch.empty? || @current_dispatch.last.datetime != (Time.now - Time.now.to_i % 300)
29
27
  @current_dispatch
30
28
  end
31
29
 
data/lib/aemo/market.rb CHANGED
@@ -39,8 +39,8 @@ module AEMO
39
39
  # Return an array of historic trading values based on a start and finish
40
40
  #
41
41
  # @param [String, AEMO::Region] region AEMO::Region
42
- # @param [DateTime] start this is inclusive not exclusive
43
- # @param [DateTime] finish this is inclusive not exclusive
42
+ # @param [Time] start this is inclusive not exclusive
43
+ # @param [Time] finish this is inclusive not exclusive
44
44
  # @return [Array<AEMO::Market::Interval>]
45
45
  def historic_trading_by_range(region, start, finish)
46
46
  region = AEMO::Region.new(region) if region.is_a?(String)
data/lib/aemo/nem12.rb CHANGED
@@ -297,9 +297,7 @@ module AEMO
297
297
 
298
298
  raise ArgumentError, 'RecordIndicator is not 100' if csv[0] != '100'
299
299
  raise ArgumentError, 'VersionHeader is not NEM12' if csv[1] != 'NEM12'
300
- if options[:strict] && (csv[2].match(/\d{12}/).nil? || csv[2] != Time.parse("#{csv[2]}00").strftime('%Y%m%d%H%M'))
301
- raise ArgumentError, 'DateTime is not valid'
302
- end
300
+ raise ArgumentError, 'Time is not valid' if options[:strict] && (csv[2].match(/\d{12}/).nil? || csv[2] != Time.parse("#{csv[2]}00").strftime('%Y%m%d%H%M'))
303
301
  raise ArgumentError, 'FromParticipant is not valid' if csv[3].match(/.{1,10}/).nil?
304
302
  raise ArgumentError, 'ToParticipant is not valid' if csv[4].match(/.{1,10}/).nil?
305
303
 
@@ -320,24 +318,12 @@ module AEMO
320
318
 
321
319
  raise ArgumentError, 'RecordIndicator is not 200' if csv[0] != '200'
322
320
  raise ArgumentError, 'NMI is not valid' unless AEMO::NMI.valid_nmi?(csv[1])
323
- if options[:strict] && (csv[2].nil? || csv[2].match(/.{1,240}/).nil?)
324
- raise ArgumentError, 'NMIConfiguration is not valid'
325
- end
326
- if !csv[3].nil? && csv[3].match(/.{1,10}/).nil?
327
- raise ArgumentError, 'RegisterID is not valid'
328
- end
329
- if csv[4].nil? || csv[4].match(/[A-HJ-NP-Z][1-9A-HJ-NP-Z]/).nil?
330
- raise ArgumentError, 'NMISuffix is not valid'
331
- end
332
- if !csv[5].nil? && !csv[5].empty? && !csv[5].match(/^\s*$/)
333
- raise ArgumentError, 'MDMDataStreamIdentifier is not valid' if csv[5].match(/[A-Z0-9]{2}/).nil?
334
- end
335
- if !csv[6].nil? && !csv[6].empty? && !csv[6].match(/^\s*$/)
336
- raise ArgumentError, 'MeterSerialNumber is not valid' if csv[6].match(/[A-Z0-9]{2}/).nil?
337
- end
338
- if csv[7].nil? || csv[7].upcase.match(/[A-Z0-9]{2}/).nil?
339
- raise ArgumentError, 'UOM is not valid'
340
- end
321
+ raise ArgumentError, 'NMIConfiguration is not valid' if options[:strict] && (csv[2].nil? || csv[2].match(/.{1,240}/).nil?)
322
+ raise ArgumentError, 'RegisterID is not valid' if !csv[3].nil? && csv[3].match(/.{1,10}/).nil?
323
+ raise ArgumentError, 'NMISuffix is not valid' if csv[4].nil? || csv[4].match(/[A-HJ-NP-Z][1-9A-HJ-NP-Z]/).nil?
324
+ raise ArgumentError, 'MDMDataStreamIdentifier is not valid' if !csv[5].nil? && !csv[5].empty? && !csv[5].match(/^\s*$/) && csv[5].match(/[A-Z0-9]{2}/).nil?
325
+ raise ArgumentError, 'MeterSerialNumber is not valid' if !csv[6].nil? && !csv[6].empty? && !csv[6].match(/^\s*$/) && csv[6].match(/[A-Z0-9]{2}/).nil?
326
+ raise ArgumentError, 'UOM is not valid' if csv[7].nil? || csv[7].upcase.match(/[A-Z0-9]{2}/).nil?
341
327
  raise ArgumentError, 'UOM is not valid' unless UOM.keys.map(&:upcase).include?(csv[7].upcase)
342
328
  raise ArgumentError, 'IntervalLength is not valid' unless %w[1 5 10 15 30].include?(csv[8])
343
329
  # raise ArgumentError, 'NextScheduledReadDate is not valid' if csv[9].match(/\d{8}/).nil? || csv[9] != Time.parse('#{csv[9]}').strftime('%Y%m%d')
@@ -404,9 +390,7 @@ module AEMO
404
390
  flag[:quality_flag] = csv[intervals_offset + 0][0]
405
391
  flag[:method_flag] = csv[intervals_offset + 0][1, 2].to_i
406
392
  end
407
- unless csv[intervals_offset + 1].nil?
408
- flag[:reason_code] = csv[intervals_offset + 1].to_i
409
- end
393
+ flag[:reason_code] = csv[intervals_offset + 1].to_i unless csv[intervals_offset + 1].nil?
410
394
  end
411
395
 
412
396
  base_interval = { data_details: @data_details.last, datetime: Time.parse("#{csv[1]}000000+1000"), value: nil, flag: flag }
@@ -454,9 +438,7 @@ module AEMO
454
438
  flag[:quality_flag] = interval_event[:quality_method][0]
455
439
  flag[:method_flag] = interval_event[:quality_method][1, 2].to_i
456
440
  end
457
- unless interval_event[:reason_code].nil?
458
- flag[:reason_code] = interval_event[:reason_code]
459
- end
441
+ flag[:reason_code] = interval_event[:reason_code] unless interval_event[:reason_code].nil?
460
442
  # Update with flag details
461
443
  @interval_data[interval_start_point + (i - 1)][:flag] = flag
462
444
  end
@@ -0,0 +1,468 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AEMO
4
+ class NMI
5
+ # [AEMO::NMI::Allocation]
6
+ #
7
+ # @author Joel Courtney
8
+ # @author Stuart Auld
9
+ # @abstract An abstraction of NMI Allocation groups that kind of represents
10
+ # networks but not always.
11
+ # @since 0.3.0
12
+ #
13
+ # @attr [Array<Regexp>] exclude_nmi_patterns
14
+ # @attr [String] friendly_title
15
+ # @attr [String] identifier
16
+ # @attr [Array<Regexp>] include_nmi_patterns
17
+ # @attr [AEMO::Region] region
18
+ # @attr [String] title
19
+ # @attr [Symbol] type
20
+ class Allocation
21
+ # NMI_ALLOCATIONS as per AEMO Documentation at
22
+ # https://www.aemo.com.au/-/media/Files/Electricity/NEM/Retail_and_Metering/Metering-Procedures/NMI-Allocation-List.pdf
23
+ # Last updated 2017-08-01
24
+ ALLOCATIONS = [
25
+ {
26
+ participant_id: 'ACTEWP',
27
+ title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
28
+ friendly_title: 'ACTEWAgl',
29
+ region: 'ACT',
30
+ type: 'electricity',
31
+ includes: [
32
+ /^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
33
+ /^(7001\d{6})$/
34
+ ],
35
+ excludes: []
36
+ },
37
+ {
38
+ participant_id: 'CNRGYP',
39
+ title: 'Essential Energy',
40
+ friendly_title: 'Essential Energy',
41
+ region: 'NSW',
42
+ type: 'electricity',
43
+ includes: [
44
+ /^(NAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
45
+ /^(NBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
46
+ /^(NDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
47
+ /^(NFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
48
+ /^(4001\d{6})$/,
49
+ /^(4508\d{6})$/,
50
+ /^(4204\d{6})$/,
51
+ /^(4407\d{6})$/
52
+ ],
53
+ excludes: []
54
+ },
55
+ {
56
+ participant_id: 'ENERGYAP',
57
+ title: 'Ausgrid',
58
+ friendly_title: 'Ausgrid',
59
+ region: 'NSW',
60
+ type: 'electricity',
61
+ includes: [
62
+ /^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
63
+ /^(410[234]\d{6})$/
64
+ ],
65
+ excludes: []
66
+ },
67
+ {
68
+ participant_id: 'INTEGP',
69
+ title: 'Endeavour Energy',
70
+ friendly_title: 'Endeavour Energy',
71
+ region: 'NSW',
72
+ type: 'electricity',
73
+ includes: [
74
+ /^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
75
+ /^(431\d{7})$/
76
+ ],
77
+ excludes: []
78
+ },
79
+ {
80
+ participant_id: 'TRANSGP',
81
+ title: 'TransGrid',
82
+ friendly_title: 'TransGrid',
83
+ region: 'NSW',
84
+ type: 'electricity',
85
+ includes: [
86
+ /^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
87
+ /^(460810[0-8]\d{3})$/
88
+ ],
89
+ excludes: []
90
+ },
91
+ {
92
+ participant_id: 'SNOWY',
93
+ title: 'Snowy Hydro Ltd',
94
+ friendly_title: 'Snowy Hydro',
95
+ region: 'NSW',
96
+ type: 'electricity',
97
+ includes: [/^(4708109\d{3})$/],
98
+ excludes: []
99
+ },
100
+ {
101
+ participant_id: 'NT_RESERVED',
102
+ title: 'Northern Territory Reserved Block',
103
+ friendly_title: 'Northern Territory Reserved Block',
104
+ region: 'NT',
105
+ type: 'electricity',
106
+ includes: [/^(250\d{7})$/],
107
+ excludes: []
108
+ },
109
+ {
110
+ participant_id: 'ERGONETP',
111
+ title: 'Ergon Energy Corporation',
112
+ friendly_title: 'Ergon Energy',
113
+ region: 'QLD',
114
+ type: 'electricity',
115
+ includes: [
116
+ /^(QAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
117
+ /^(QCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
118
+ /^(QDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
119
+ /^(QEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
120
+ /^(QFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
121
+ /^(QGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
122
+ /^(30\d{8})$/
123
+ ],
124
+ excludes: []
125
+ },
126
+ {
127
+ participant_id: 'ENERGEXP',
128
+ title: 'ENERGEX Limited',
129
+ friendly_title: 'Energex',
130
+ region: 'QLD',
131
+ type: 'electricity',
132
+ includes: [
133
+ /^(QB\d{2}[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
134
+ /^(31\d{8})$/
135
+ ],
136
+ excludes: []
137
+ },
138
+ {
139
+ participant_id: 'PLINKP',
140
+ title: 'Qld Electricity Transmission Corp (Powerlink)',
141
+ friendly_title: 'Powerlink',
142
+ region: 'QLD',
143
+ type: 'electricity',
144
+ includes: [
145
+ /^(Q[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
146
+ /^(320200\d{4})$/
147
+ ],
148
+ excludes: []
149
+ },
150
+ {
151
+ participant_id: 'UMPLP',
152
+ title: 'SA Power Networks',
153
+ friendly_title: 'SA Power Networks',
154
+ region: 'SA',
155
+ type: 'electricity',
156
+ includes: [
157
+ /^(SAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
158
+ /^(SASMPL[\d]{4})$/,
159
+ /^(200[12]\d{6})$/
160
+ ],
161
+ excludes: []
162
+ },
163
+ {
164
+ participant_id: 'ETSATP',
165
+ title: 'ElectraNet SA',
166
+ friendly_title: 'ElectraNet SA',
167
+ region: 'SA',
168
+ type: 'electricity',
169
+ includes: [
170
+ /^(S[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
171
+ /^(210200\d{4})$/
172
+ ],
173
+ excludes: []
174
+ },
175
+ {
176
+ participant_id: 'AURORAP',
177
+ title: 'TasNetworks',
178
+ friendly_title: 'TasNetworks',
179
+ region: 'TAS',
180
+ type: 'electricity',
181
+ includes: [
182
+ /^(T000000(([0-4]\d{3})|(500[01])))$/,
183
+ /^(8000\d{6})$/,
184
+ /^(8590[23]\d{5})$/
185
+ ],
186
+ excludes: []
187
+ },
188
+ {
189
+ participant_id: 'TRANSEND',
190
+ title: 'TasNetworks',
191
+ friendly_title: 'TasNetworks',
192
+ region: 'TAS',
193
+ type: 'electricity',
194
+ includes: [/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/],
195
+ excludes: []
196
+ },
197
+ {
198
+ participant_id: 'CITIPP',
199
+ title: 'CitiPower',
200
+ friendly_title: 'CitiPower',
201
+ region: 'VIC',
202
+ type: 'electricity',
203
+ includes: [
204
+ /^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
205
+ /^(610[23]\d{6})$/
206
+ ],
207
+ excludes: []
208
+ },
209
+ {
210
+ participant_id: 'EASTERN',
211
+ title: 'SP AusNet',
212
+ friendly_title: 'SP AusNet DNSP',
213
+ region: 'VIC',
214
+ type: 'electricity',
215
+ includes: [
216
+ /^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
217
+ /^(630[56]\d{6})$/
218
+ ],
219
+ excludes: []
220
+ },
221
+ {
222
+ participant_id: 'POWCP',
223
+ title: 'PowerCor Australia',
224
+ friendly_title: 'PowerCor',
225
+ region: 'VIC',
226
+ type: 'electricity',
227
+ includes: [
228
+ /^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
229
+ /^(620[34]\d{6})$/
230
+ ],
231
+ excludes: []
232
+ },
233
+ {
234
+ participant_id: 'SOLARISP',
235
+ title: 'Jemena Electricity Networks (VIC)',
236
+ friendly_title: 'Jemena',
237
+ region: 'VIC',
238
+ type: 'electricity',
239
+ includes: [
240
+ /^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
241
+ /^(6001\d{6})$/
242
+ ],
243
+ excludes: []
244
+ },
245
+ {
246
+ participant_id: 'UNITED',
247
+ title: 'United Energy Distribution',
248
+ friendly_title: 'United Energy',
249
+ region: 'VIC',
250
+ type: 'electricity',
251
+ includes: [
252
+ /^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
253
+ /^(640[78]\d{6})$/
254
+ ],
255
+ excludes: []
256
+ },
257
+ {
258
+ participant_id: 'GPUPP',
259
+ title: 'SP AusNet TNSP',
260
+ friendly_title: 'SP AusNet TNSP',
261
+ region: 'VIC',
262
+ type: 'electricity',
263
+ includes: [
264
+ /^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
265
+ /^(650900\d{4})$/
266
+ ],
267
+ excludes: []
268
+ },
269
+ {
270
+ participant_id: 'WESTERNPOWER',
271
+ title: 'Western Power',
272
+ friendly_title: 'Western Power',
273
+ region: 'WA',
274
+ type: 'electricity',
275
+ includes: [
276
+ /^(WAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
277
+ /^(800[1-9]\d{6})$/,
278
+ /^(801\d{7})$/,
279
+ /^(8020\d{6})$/
280
+ ],
281
+ excludes: []
282
+ },
283
+ {
284
+ participant_id: 'HORIZONPOWER',
285
+ title: 'Horizon Power',
286
+ friendly_title: 'Horizon Power',
287
+ region: 'WA',
288
+ type: 'electricity',
289
+ includes: [/^(8021\d{6})$/],
290
+ excludes: []
291
+ },
292
+ {
293
+ participant_id: 'GAS_NSW',
294
+ title: 'GAS NSW',
295
+ friendly_title: 'GAS NSW',
296
+ region: 'NSW',
297
+ type: 'gas',
298
+ includes: [/^(52\d{8})$/],
299
+ excludes: []
300
+ },
301
+ {
302
+ participant_id: 'GAS_VIC',
303
+ title: 'GAS VIC',
304
+ friendly_title: 'GAS VIC',
305
+ region: 'VIC',
306
+ type: 'gas',
307
+ includes: [/^(53\d{8})$/],
308
+ excludes: []
309
+ },
310
+ {
311
+ participant_id: 'GAS_QLD',
312
+ title: 'GAS QLD',
313
+ friendly_title: 'GAS QLD',
314
+ region: 'QLD',
315
+ type: 'gas',
316
+ includes: [/^(54\d{8})$/],
317
+ excludes: []
318
+ },
319
+ {
320
+ participant_id: 'GAS_SA',
321
+ title: 'GAS SA',
322
+ friendly_title: 'GAS SA',
323
+ region: 'SA',
324
+ type: 'gas',
325
+ includes: [/^(55\d{8})$/],
326
+ excludes: []
327
+ },
328
+ {
329
+ participant_id: 'GAS_WA',
330
+ title: 'GAS WA',
331
+ friendly_title: 'GAS WA',
332
+ region: 'WA',
333
+ type: 'gas',
334
+ includes: [/^(56\d{8})$/],
335
+ excludes: []
336
+ },
337
+ {
338
+ participant_id: 'GAS_TAS',
339
+ title: 'GAS TAS',
340
+ friendly_title: 'GAS TAS',
341
+ region: 'TAS',
342
+ type: 'gas',
343
+ includes: [/^(57\d{8})$/],
344
+ excludes: []
345
+ },
346
+ {
347
+ participant_id: 'FEDAIRPORTS',
348
+ title: 'Federal Airports Corporation (Sydney Airport)',
349
+ friendly_title: 'Sydney Airport',
350
+ region: 'NSW',
351
+ type: 'electricity',
352
+ includes: [/^(NJJJNR[A-HJ-NP-Z\d]{4})$/],
353
+ excludes: []
354
+ },
355
+ {
356
+ participant_id: 'EXEMPTNETWORKS',
357
+ title: 'Exempt Networks - various',
358
+ friendly_title: 'Exempt Networks - various',
359
+ type: 'electricity',
360
+ includes: [
361
+ /^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
362
+ /^(7102\d{6})$/
363
+ ],
364
+ excludes: []
365
+ },
366
+ {
367
+ participant_id: 'AEMORESERVED',
368
+ title: 'AEMO Reserved',
369
+ friendly_title: 'AEMO Reserved',
370
+ type: 'electricity',
371
+ includes: [
372
+ /^(880[1-5]\d{6})$/,
373
+ /^(9\d{9})$/
374
+ ],
375
+ excludes: []
376
+ }
377
+ ].freeze
378
+
379
+ # NMI Allocations are only for Electricity and Gas metering
380
+ SUPPORTED_TYPES = %i[electricity gas].freeze
381
+
382
+ attr_accessor :exclude_nmi_patterns,
383
+ :friendly_title,
384
+ :identifier,
385
+ :include_nmi_patterns,
386
+ :region,
387
+ :title,
388
+ :type
389
+
390
+ alias state region
391
+ alias state= region=
392
+
393
+ class << self
394
+ include Enumerable
395
+
396
+ # Return all the NMI allocations
397
+ #
398
+ # @return [Array<AEMO::NMI::Allocation>]
399
+ def all
400
+ ALLOCATIONS.map do |a|
401
+ new(a.fetch(:title), a.fetch(:type), a)
402
+ end
403
+ end
404
+
405
+ # Enumerable support
406
+ #
407
+ # @return [Enumerator]
408
+ def each(&block)
409
+ all.each(&block)
410
+ end
411
+
412
+ # Finds the Allocation that encompasses a given NMI
413
+ #
414
+ # @param [string] nmi
415
+ # @return [AEMO::NMI::Allocation]
416
+ def find_by_nmi(nmi)
417
+ each do |allocation|
418
+ allocation.exclude_nmi_patterns.each do |pattern|
419
+ next if nmi.match(pattern)
420
+ end
421
+ allocation.include_nmi_patterns.each do |pattern|
422
+ return allocation if nmi.match(pattern)
423
+ end
424
+ end
425
+ nil
426
+ end
427
+ end
428
+
429
+ # Initialises an {AEMO::NMI::Allocation}
430
+ #
431
+ # @param [String] title
432
+ # @param [String] type
433
+ # @param [Hash] opts
434
+ # @option opts [String] :identifier
435
+ # @option opts [String] :friendly_title
436
+ # @option opts [Array<Regexp>] :exclude_nmi_patterns
437
+ # @option opts [Array<Regexp>] :include_nmi_patterns
438
+ # @option opts [String] :region
439
+ # @return [AEMO::NMI::Allocation]
440
+ def initialize(title, type, opts = {})
441
+ @title = title
442
+ @type = parse_allocation_type(type)
443
+ @identifier = opts.fetch(:identifier, title.upcase)
444
+ @friendly_title = opts.fetch(:friendly_title, title)
445
+ @exclude_nmi_patterns = opts.fetch(:excludes, [])
446
+ @include_nmi_patterns = opts.fetch(:includes, [])
447
+ @region = AEMO::Region.new(opts.fetch(:region)) if opts.dig(:region)
448
+ end
449
+
450
+ private
451
+
452
+ # Private method to parse an
453
+ #
454
+ # @param [#to_sym] type
455
+ # @raise [AEMO::InvalidNMIAllocationType]
456
+ # @return [Symbol]
457
+ def parse_allocation_type(type)
458
+ type_sym = type.to_sym
459
+ unless SUPPORTED_TYPES.include?(type_sym)
460
+ raise AEMO::InvalidNMIAllocationType
461
+ end
462
+ type_sym
463
+ rescue NoMethodError
464
+ raise AEMO::InvalidNMIAllocationType
465
+ end
466
+ end
467
+ end
468
+ end