aemo 0.2.1 → 0.3.0.pre.rc1

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