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 +4 -4
- data/lib/aemo/exceptions/invalid_nmi_allocation_type.rb +21 -0
- data/lib/aemo/market/interval.rb +1 -3
- data/lib/aemo/market/node.rb +1 -3
- data/lib/aemo/market.rb +2 -2
- data/lib/aemo/nem12.rb +9 -27
- data/lib/aemo/nmi/allocation.rb +468 -0
- data/lib/aemo/nmi.rb +93 -359
- data/lib/aemo/version.rb +1 -1
- data/lib/aemo.rb +2 -0
- data/lib/data/xml_to_json.rb +4 -4
- data/spec/lib/aemo/market/interval_spec.rb +3 -3
- data/spec/lib/aemo/msats_spec.rb +4 -8
- data/spec/lib/aemo/nmi/allocation_spec.rb +50 -0
- data/spec/lib/aemo/nmi_spec.rb +16 -8
- metadata +43 -39
data/lib/aemo/nmi.rb
CHANGED
@@ -4,315 +4,39 @@ require 'csv'
|
|
4
4
|
require 'json'
|
5
5
|
require 'time'
|
6
6
|
require 'ostruct'
|
7
|
+
|
7
8
|
module AEMO
|
9
|
+
# [AEMO::NMI]
|
10
|
+
#
|
8
11
|
# AEMO::NMI acts as an object to simplify access to data and information
|
9
|
-
#
|
12
|
+
# about a NMI and provide verification of the NMI value
|
13
|
+
#
|
14
|
+
# @author Joel Courtney
|
15
|
+
# @abstract Model for a National Metering Identifier.
|
16
|
+
# @since 2014-12-05
|
10
17
|
class NMI
|
11
18
|
# Operational Regions for the NMI
|
12
|
-
REGIONS = {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# NMI_ALLOCATIONS as per AEMO Documentation at
|
22
|
-
# https://www.aemo.com.au/-/media/Files/Electricity/NEM/Retail_and_Metering/
|
23
|
-
# Metering-Procedures/NMI-Allocation-List.pdf
|
24
|
-
# Last accessed 2017-08-01
|
25
|
-
NMI_ALLOCATIONS = {
|
26
|
-
'ACTEWP' => {
|
27
|
-
title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
|
28
|
-
friendly_title: 'ACTEWAgl',
|
29
|
-
state: AEMO::Region.new('ACT'),
|
30
|
-
type: 'electricity',
|
31
|
-
includes: [/^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
32
|
-
/^(7001\d{6})$/],
|
33
|
-
excludes: []
|
34
|
-
},
|
35
|
-
'CNRGYP' => {
|
36
|
-
title: 'Essential Energy',
|
37
|
-
friendly_title: 'Essential Energy',
|
38
|
-
state: AEMO::Region.new('NSW'),
|
39
|
-
type: 'electricity',
|
40
|
-
includes: [/^(NAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
41
|
-
/^(NBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
42
|
-
/^(NDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
43
|
-
/^(NFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
44
|
-
/^(4001\d{6})$/,
|
45
|
-
/^(4508\d{6})$/,
|
46
|
-
/^(4204\d{6})$/,
|
47
|
-
/^(4407\d{6})$/],
|
48
|
-
excludes: []
|
49
|
-
},
|
50
|
-
'ENERGYAP' => {
|
51
|
-
title: 'Ausgrid',
|
52
|
-
friendly_title: 'Ausgrid',
|
53
|
-
state: AEMO::Region.new('NSW'),
|
54
|
-
type: 'electricity',
|
55
|
-
includes: [/^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
56
|
-
/^(410[234]\d{6})$/],
|
57
|
-
excludes: []
|
58
|
-
},
|
59
|
-
'INTEGP' => {
|
60
|
-
title: 'Endeavour Energy',
|
61
|
-
friendly_title: 'Endeavour Energy',
|
62
|
-
state: AEMO::Region.new('NSW'),
|
63
|
-
type: 'electricity',
|
64
|
-
includes: [/^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
65
|
-
/^(431\d{7})$/],
|
66
|
-
excludes: []
|
67
|
-
},
|
68
|
-
'TRANSGP' => {
|
69
|
-
title: 'TransGrid',
|
70
|
-
friendly_title: 'TransGrid',
|
71
|
-
state: AEMO::Region.new('NSW'),
|
72
|
-
type: 'electricity',
|
73
|
-
includes: [/^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
74
|
-
/^(460810[0-8]\d{3})$/],
|
75
|
-
excludes: []
|
76
|
-
},
|
77
|
-
'SNOWY' => {
|
78
|
-
title: 'Snowy Hydro Ltd',
|
79
|
-
friendly_title: 'Snowy Hydro',
|
80
|
-
state: AEMO::Region.new('NSW'),
|
81
|
-
type: 'electricity',
|
82
|
-
includes: [/^(4708109\d{3})$/],
|
83
|
-
excludes: []
|
84
|
-
},
|
85
|
-
'NT_RESERVED' => {
|
86
|
-
title: 'Northern Territory Reserved Block',
|
87
|
-
friendly_title: 'Northern Territory Reserved Block',
|
88
|
-
state: AEMO::Region.new('NT'),
|
89
|
-
type: 'electricity',
|
90
|
-
includes: [/^(250\d{7})$/],
|
91
|
-
excludes: []
|
92
|
-
},
|
93
|
-
'ERGONETP' => {
|
94
|
-
title: 'Ergon Energy Corporation',
|
95
|
-
friendly_title: 'Ergon Energy',
|
96
|
-
state: AEMO::Region.new('QLD'),
|
97
|
-
type: 'electricity',
|
98
|
-
includes: [/^(QAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
99
|
-
/^(QCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
100
|
-
/^(QDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
101
|
-
/^(QEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
102
|
-
/^(QFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
103
|
-
/^(QGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
104
|
-
/^(30\d{8})$/],
|
105
|
-
excludes: []
|
106
|
-
},
|
107
|
-
'ENERGEXP' => {
|
108
|
-
title: 'ENERGEX Limited',
|
109
|
-
friendly_title: 'Energex',
|
110
|
-
state: AEMO::Region.new('QLD'),
|
111
|
-
type: 'electricity',
|
112
|
-
includes: [/^(QB\d{2}[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
113
|
-
/^(31\d{8})$/],
|
114
|
-
excludes: []
|
115
|
-
},
|
116
|
-
'PLINKP' => {
|
117
|
-
title: 'Qld Electricity Transmission Corp (Powerlink)',
|
118
|
-
friendly_title: 'Powerlink',
|
119
|
-
state: AEMO::Region.new('QLD'),
|
120
|
-
type: 'electricity',
|
121
|
-
includes: [/^(Q[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
|
122
|
-
/^(320200\d{4})$/],
|
123
|
-
excludes: []
|
124
|
-
},
|
125
|
-
'UMPLP' => {
|
126
|
-
title: 'SA Power Networks',
|
127
|
-
friendly_title: 'SA Power Networks',
|
128
|
-
state: AEMO::Region.new('SA'),
|
129
|
-
type: 'electricity',
|
130
|
-
includes: [/^(SAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
131
|
-
/^(SASMPL[\d]{4})$/,
|
132
|
-
/^(200[12]\d{6})$/],
|
133
|
-
excludes: []
|
134
|
-
},
|
135
|
-
'ETSATP' => {
|
136
|
-
title: 'ElectraNet SA',
|
137
|
-
friendly_title: 'ElectraNet SA',
|
138
|
-
state: AEMO::Region.new('SA'),
|
139
|
-
type: 'electricity',
|
140
|
-
includes: [/^(S[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
|
141
|
-
/^(210200\d{4})$/],
|
142
|
-
excludes: []
|
143
|
-
},
|
144
|
-
'AURORAP' => {
|
145
|
-
title: 'TasNetworks',
|
146
|
-
friendly_title: 'TasNetworks',
|
147
|
-
state: AEMO::Region.new('TAS'),
|
148
|
-
type: 'electricity',
|
149
|
-
includes: [/^(T000000(([0-4]\d{3})|(500[01])))$/,
|
150
|
-
/^(8000\d{6})$/,
|
151
|
-
/^(8590[23]\d{5})$/],
|
152
|
-
excludes: []
|
153
|
-
},
|
154
|
-
'TRANSEND' => {
|
155
|
-
title: 'TasNetworks',
|
156
|
-
friendly_title: 'TasNetworks',
|
157
|
-
state: AEMO::Region.new('TAS'),
|
158
|
-
type: 'electricity',
|
159
|
-
includes: [/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/],
|
160
|
-
excludes: []
|
161
|
-
},
|
162
|
-
'CITIPP' => {
|
163
|
-
title: 'CitiPower',
|
164
|
-
friendly_title: 'CitiPower',
|
165
|
-
state: AEMO::Region.new('VIC'),
|
166
|
-
type: 'electricity',
|
167
|
-
includes: [/^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
168
|
-
/^(610[23]\d{6})$/],
|
169
|
-
excludes: []
|
170
|
-
},
|
171
|
-
'EASTERN' => {
|
172
|
-
title: 'SP AusNet',
|
173
|
-
friendly_title: 'SP AusNet DNSP',
|
174
|
-
state: AEMO::Region.new('VIC'),
|
175
|
-
type: 'electricity',
|
176
|
-
includes: [/^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
177
|
-
/^(630[56]\d{6})$/],
|
178
|
-
excludes: []
|
179
|
-
},
|
180
|
-
'POWCP' => {
|
181
|
-
title: 'PowerCor Australia',
|
182
|
-
friendly_title: 'PowerCor',
|
183
|
-
state: AEMO::Region.new('VIC'),
|
184
|
-
type: 'electricity',
|
185
|
-
includes: [/^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
186
|
-
/^(620[34]\d{6})$/],
|
187
|
-
excludes: []
|
188
|
-
},
|
189
|
-
'SOLARISP' => {
|
190
|
-
title: 'Jemena Electricity Networks (VIC)',
|
191
|
-
friendly_title: 'Jemena',
|
192
|
-
state: AEMO::Region.new('VIC'),
|
193
|
-
type: 'electricity',
|
194
|
-
includes: [/^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
195
|
-
/^(6001\d{6})$/],
|
196
|
-
excludes: []
|
197
|
-
},
|
198
|
-
'UNITED' => {
|
199
|
-
title: 'United Energy Distribution',
|
200
|
-
friendly_title: 'United Energy',
|
201
|
-
state: AEMO::Region.new('VIC'),
|
202
|
-
type: 'electricity',
|
203
|
-
includes: [/^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
204
|
-
/^(640[78]\d{6})$/],
|
205
|
-
excludes: []
|
206
|
-
},
|
207
|
-
'GPUPP' => {
|
208
|
-
title: 'SP AusNet TNSP',
|
209
|
-
friendly_title: 'SP AusNet TNSP',
|
210
|
-
state: AEMO::Region.new('VIC'),
|
211
|
-
type: 'electricity',
|
212
|
-
includes: [/^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
|
213
|
-
/^(650900\d{4})$/],
|
214
|
-
excludes: []
|
215
|
-
},
|
216
|
-
'WESTERNPOWER' => {
|
217
|
-
title: 'Western Power',
|
218
|
-
friendly_title: 'Western Power',
|
219
|
-
state: AEMO::Region.new('WA'),
|
220
|
-
type: 'electricity',
|
221
|
-
includes: [/^(WAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
222
|
-
/^(800[1-9]\d{6})$/,
|
223
|
-
/^(801\d{7})$/,
|
224
|
-
/^(8020\d{6})$/],
|
225
|
-
excludes: []
|
226
|
-
},
|
227
|
-
'HORIZONPOWER' => {
|
228
|
-
title: 'Horizon Power',
|
229
|
-
friendly_title: 'Horizon Power',
|
230
|
-
state: AEMO::Region.new('WA'),
|
231
|
-
type: 'electricity',
|
232
|
-
includes: [/^(8021\d{6})$/],
|
233
|
-
excludes: []
|
234
|
-
},
|
235
|
-
'GAS_NSW' => {
|
236
|
-
title: 'GAS NSW',
|
237
|
-
friendly_title: 'GAS NSW',
|
238
|
-
state: AEMO::Region.new('NSW'),
|
239
|
-
type: 'gas',
|
240
|
-
includes: [/^(52\d{8})$/],
|
241
|
-
excludes: []
|
242
|
-
},
|
243
|
-
'GAS_VIC' => {
|
244
|
-
title: 'GAS VIC',
|
245
|
-
friendly_title: 'GAS VIC',
|
246
|
-
state: AEMO::Region.new('VIC'),
|
247
|
-
type: 'gas',
|
248
|
-
includes: [/^(53\d{8})$/],
|
249
|
-
excludes: []
|
250
|
-
},
|
251
|
-
'GAS_QLD' => {
|
252
|
-
title: 'GAS QLD',
|
253
|
-
friendly_title: 'GAS QLD',
|
254
|
-
state: AEMO::Region.new('QLD'),
|
255
|
-
type: 'gas',
|
256
|
-
includes: [/^(54\d{8})$/],
|
257
|
-
excludes: []
|
258
|
-
},
|
259
|
-
'GAS_SA' => {
|
260
|
-
title: 'GAS SA',
|
261
|
-
friendly_title: 'GAS SA',
|
262
|
-
state: AEMO::Region.new('SA'),
|
263
|
-
type: 'gas',
|
264
|
-
includes: [/^(55\d{8})$/],
|
265
|
-
excludes: []
|
266
|
-
},
|
267
|
-
'GAS_WA' => {
|
268
|
-
title: 'GAS WA',
|
269
|
-
friendly_title: 'GAS WA',
|
270
|
-
state: AEMO::Region.new('WA'),
|
271
|
-
type: 'gas',
|
272
|
-
includes: [/^(56\d{8})$/],
|
273
|
-
excludes: []
|
274
|
-
},
|
275
|
-
'GAS_TAS' => {
|
276
|
-
title: 'GAS TAS',
|
277
|
-
friendly_title: 'GAS TAS',
|
278
|
-
state: AEMO::Region.new('TAS'),
|
279
|
-
type: 'gas',
|
280
|
-
includes: [/^(57\d{8})$/],
|
281
|
-
excludes: []
|
282
|
-
},
|
283
|
-
'FEDAIRPORTS' => {
|
284
|
-
title: 'Federal Airports Corporation (Sydney Airport)',
|
285
|
-
friendly_title: 'Sydney Airport',
|
286
|
-
state: AEMO::Region.new('NSW'),
|
287
|
-
type: 'electricity',
|
288
|
-
includes: [/^(NJJJNR[A-HJ-NP-Z\d]{4})$/],
|
289
|
-
excludes: []
|
290
|
-
},
|
291
|
-
'EXEMPTNETWORKS' => {
|
292
|
-
title: 'Exempt Networks - various',
|
293
|
-
friendly_title: 'Exempt Networks - various',
|
294
|
-
state: '',
|
295
|
-
type: 'electricity',
|
296
|
-
includes: [/^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
297
|
-
/^(7102\d{6})$/],
|
298
|
-
excludes: []
|
299
|
-
},
|
300
|
-
'AEMORESERVED' => {
|
301
|
-
title: 'AEMO Reserved',
|
302
|
-
friendly_title: 'AEMO Reserved',
|
303
|
-
state: '',
|
304
|
-
type: 'electricity',
|
305
|
-
includes: [/^(880[1-5]\d{6})$/,
|
306
|
-
/^(9\d{9})$/],
|
307
|
-
excludes: []
|
308
|
-
}
|
19
|
+
REGIONS = {
|
20
|
+
'ACT' => 'Australian Capital Territory',
|
21
|
+
'NSW' => 'New South Wales',
|
22
|
+
'QLD' => 'Queensland',
|
23
|
+
'SA' => 'South Australia',
|
24
|
+
'TAS' => 'Tasmania',
|
25
|
+
'VIC' => 'Victoria',
|
26
|
+
'WA' => 'Western Australia',
|
27
|
+
'NT' => 'Northern Territory'
|
309
28
|
}.freeze
|
29
|
+
|
310
30
|
# Transmission Node Identifier Codes are loaded from a json file
|
311
31
|
# Obtained from http://www.nemweb.com.au/
|
312
32
|
#
|
313
33
|
# See /lib/data for further data manipulation required
|
314
|
-
TNI_CODES = JSON.parse(
|
315
|
-
|
34
|
+
TNI_CODES = JSON.parse(
|
35
|
+
File.read(
|
36
|
+
File.join(File.dirname(__FILE__), '..', 'data', 'aemo-tni.json')
|
37
|
+
)
|
38
|
+
).freeze
|
39
|
+
|
316
40
|
# Distribution Loss Factor Codes are loaded from a json file
|
317
41
|
# Obtained from MSATS, matching to DNSP from file
|
318
42
|
# https://www.aemo.com.au/-/media/Files/Electricity/NEM/
|
@@ -321,8 +45,11 @@ module AEMO
|
|
321
45
|
#
|
322
46
|
# Last accessed 2017-08-01
|
323
47
|
# See /lib/data for further data manipulation required
|
324
|
-
DLF_CODES = JSON.parse(
|
325
|
-
|
48
|
+
DLF_CODES = JSON.parse(
|
49
|
+
File.read(
|
50
|
+
File.join(File.dirname(__FILE__), '..', 'data', 'aemo-dlf.json')
|
51
|
+
)
|
52
|
+
).freeze
|
326
53
|
|
327
54
|
# [String] National Meter Identifier
|
328
55
|
@nmi = nil
|
@@ -344,6 +71,38 @@ module AEMO
|
|
344
71
|
:jurisdiction_code, :classification_code, :status, :address,
|
345
72
|
:meters, :roles, :data_streams
|
346
73
|
|
74
|
+
class << self
|
75
|
+
# A function to validate the NMI provided
|
76
|
+
#
|
77
|
+
# @param [String] nmi the nmi to be checked
|
78
|
+
# @return [Boolean] whether or not the nmi is valid
|
79
|
+
def valid_nmi?(nmi)
|
80
|
+
((nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?)
|
81
|
+
end
|
82
|
+
|
83
|
+
# A function to calculate the checksum value for a given National Meter
|
84
|
+
# Identifier
|
85
|
+
#
|
86
|
+
# @param [String] nmi the NMI to check the checksum against
|
87
|
+
# @param [Integer] checksum_value the checksum value to check against the
|
88
|
+
# current National Meter Identifier's checksum value
|
89
|
+
# @return [Boolean] whether or not the checksum is valid
|
90
|
+
def valid_checksum?(nmi, checksum_value)
|
91
|
+
nmi = AEMO::NMI.new(nmi)
|
92
|
+
nmi.valid_checksum?(checksum_value)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Find the Network for a given NMI
|
96
|
+
#
|
97
|
+
# @param [String] nmi NMI
|
98
|
+
# @returns [AEMO::NMI::Allocation] The Network information
|
99
|
+
def network(nmi)
|
100
|
+
AEMO::NMI::Allocation.find_by_nmi(nmi)
|
101
|
+
end
|
102
|
+
|
103
|
+
alias allocation network
|
104
|
+
end
|
105
|
+
|
347
106
|
# Initialize a NMI file
|
348
107
|
#
|
349
108
|
# @param [String] nmi the National Meter Identifier (NMI)
|
@@ -354,9 +113,7 @@ module AEMO
|
|
354
113
|
def initialize(nmi, options = {})
|
355
114
|
raise ArgumentError, 'NMI is not a string' unless nmi.is_a?(String)
|
356
115
|
raise ArgumentError, 'NMI is not 10 characters' unless nmi.length == 10
|
357
|
-
unless AEMO::NMI.valid_nmi?(nmi)
|
358
|
-
raise ArgumentError, 'NMI is not constructed with valid characters'
|
359
|
-
end
|
116
|
+
raise ArgumentError, 'NMI is not constructed with valid characters' unless AEMO::NMI.valid_nmi?(nmi)
|
360
117
|
|
361
118
|
@nmi = nmi
|
362
119
|
@meters = []
|
@@ -381,6 +138,8 @@ module AEMO
|
|
381
138
|
AEMO::NMI.network(@nmi)
|
382
139
|
end
|
383
140
|
|
141
|
+
alias allocation network
|
142
|
+
|
384
143
|
# A function to calculate the checksum value for a given
|
385
144
|
# National Meter Identifier
|
386
145
|
#
|
@@ -412,9 +171,7 @@ module AEMO
|
|
412
171
|
#
|
413
172
|
# @return [Hash] MSATS NMI Detail data
|
414
173
|
def raw_msats_nmi_detail(options = {})
|
415
|
-
unless AEMO::MSATS.can_authenticate?
|
416
|
-
raise ArgumentError, 'MSATS has no authentication credentials'
|
417
|
-
end
|
174
|
+
raise ArgumentError, 'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate?
|
418
175
|
AEMO::MSATS.nmi_detail(@nmi, options)
|
419
176
|
end
|
420
177
|
|
@@ -530,54 +287,18 @@ module AEMO
|
|
530
287
|
|
531
288
|
# The current annual load in MWh
|
532
289
|
#
|
290
|
+
# @todo Use TimeDifference for more accurate annualised load
|
533
291
|
# @return [Integer] the current annual load for the meter in MWh
|
534
292
|
def current_annual_load
|
535
293
|
(current_daily_load * 365.2425 / 1000).to_i
|
536
294
|
end
|
537
295
|
|
538
|
-
# A function to validate the NMI provided
|
539
|
-
#
|
540
|
-
# @param [String] nmi the nmi to be checked
|
541
|
-
# @return [Boolean] whether or not the nmi is valid
|
542
|
-
def self.valid_nmi?(nmi)
|
543
|
-
((nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?)
|
544
|
-
end
|
545
|
-
|
546
|
-
# A function to calculate the checksum value for a given National Meter
|
547
|
-
# Identifier
|
548
|
-
#
|
549
|
-
# @param [String] nmi the NMI to check the checksum against
|
550
|
-
# @param [Integer] checksum_value the checksum value to check against the
|
551
|
-
# current National Meter Identifier's checksum value
|
552
|
-
# @return [Boolean] whether or not the checksum is valid
|
553
|
-
def self.valid_checksum?(nmi, checksum_value)
|
554
|
-
nmi = AEMO::NMI.new(nmi)
|
555
|
-
nmi.valid_checksum?(checksum_value)
|
556
|
-
end
|
557
|
-
|
558
|
-
# Find the Network for a given NMI
|
559
|
-
#
|
560
|
-
# @param [String] nmi NMI
|
561
|
-
# @returns [Hash] The Network information
|
562
|
-
def self.network(nmi)
|
563
|
-
network = nil
|
564
|
-
AEMO::NMI::NMI_ALLOCATIONS.each_pair do |identifier, details|
|
565
|
-
details[:includes].each do |pattern|
|
566
|
-
if nmi.match(pattern)
|
567
|
-
network = { identifier => details }
|
568
|
-
break
|
569
|
-
end
|
570
|
-
end
|
571
|
-
end
|
572
|
-
network
|
573
|
-
end
|
574
|
-
|
575
296
|
# A function to return the distribution loss factor value for a given date
|
576
297
|
#
|
577
298
|
# @param [DateTime, Time] datetime the date for the distribution loss factor
|
578
299
|
# value
|
579
300
|
# @return [nil, float] the distribution loss factor value
|
580
|
-
def dlfc_value(datetime =
|
301
|
+
def dlfc_value(datetime = Time.now)
|
581
302
|
if @dlf.nil?
|
582
303
|
raise 'No DLF set, ensure that you have set the value either via the' \
|
583
304
|
'update_from_msats! function or manually'
|
@@ -585,8 +306,8 @@ module AEMO
|
|
585
306
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
586
307
|
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
587
308
|
possible_values = DLF_CODES[@dlf].select do |x|
|
588
|
-
|
589
|
-
|
309
|
+
Time.parse(x['FromDate']) <= datetime &&
|
310
|
+
Time.parse(x['ToDate']) >= datetime
|
590
311
|
end
|
591
312
|
if possible_values.empty?
|
592
313
|
nil
|
@@ -600,13 +321,16 @@ module AEMO
|
|
600
321
|
# @param [DateTime, Time] start the date for the distribution loss factor value
|
601
322
|
# @param [DateTime, Time] finish the date for the distribution loss factor value
|
602
323
|
# @return [Array(Hash)] array of hashes of start, finish and value
|
603
|
-
def dlfc_values(start =
|
604
|
-
|
324
|
+
def dlfc_values(start = Time.now, finish = Time.now)
|
325
|
+
if @dlf.nil?
|
326
|
+
raise 'No DLF set, ensure that you have set the value either via the '\
|
327
|
+
'update_from_msats! function or manually'
|
328
|
+
end
|
605
329
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
606
330
|
raise 'Invalid start' unless [DateTime, Time].include?(start.class)
|
607
331
|
raise 'Invalid finish' unless [DateTime, Time].include?(finish.class)
|
608
332
|
raise 'start cannot be after finish' if start > finish
|
609
|
-
DLF_CODES[@dlf].reject { |x| start >
|
333
|
+
DLF_CODES[@dlf].reject { |x| start > Time.parse(x['ToDate']) || finish < Time.parse(x['FromDate']) }
|
610
334
|
.map { |x| { 'start' => x['FromDate'], 'finish' => x['ToDate'], 'value' => x['Value'].to_f } }
|
611
335
|
end
|
612
336
|
|
@@ -614,13 +338,16 @@ module AEMO
|
|
614
338
|
#
|
615
339
|
# @param [DateTime, Time] datetime the date for the distribution loss factor value
|
616
340
|
# @return [nil, float] the transmission node identifier loss factor value
|
617
|
-
def tni_value(datetime =
|
618
|
-
|
341
|
+
def tni_value(datetime = Time.now)
|
342
|
+
if @tni.nil?
|
343
|
+
raise 'No TNI set, ensure that you have set the value either via the '\
|
344
|
+
'update_from_msats! function or manually'
|
345
|
+
end
|
619
346
|
raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni)
|
620
347
|
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
621
|
-
possible_values = TNI_CODES[@tni].select { |x|
|
348
|
+
possible_values = TNI_CODES[@tni].select { |x| Time.parse(x['FromDate']) <= datetime && datetime <= Time.parse(x['ToDate']) }
|
622
349
|
return nil if possible_values.empty?
|
623
|
-
possible_values = possible_values.first['mlf_data']['loss_factors'].select { |x|
|
350
|
+
possible_values = possible_values.first['mlf_data']['loss_factors'].select { |x| Time.parse(x['start']) <= datetime && datetime <= Time.parse(x['finish']) }
|
624
351
|
return nil if possible_values.empty?
|
625
352
|
possible_values.first['value'].to_f
|
626
353
|
end
|
@@ -630,14 +357,21 @@ module AEMO
|
|
630
357
|
# @param [DateTime, Time] start the date for the distribution loss factor value
|
631
358
|
# @param [DateTime, Time] finish the date for the distribution loss factor value
|
632
359
|
# @return [Array(Hash)] array of hashes of start, finish and value
|
633
|
-
def tni_values(start =
|
634
|
-
|
360
|
+
def tni_values(start = Time.now, finish = Time.now)
|
361
|
+
if @tni.nil?
|
362
|
+
raise 'No TNI set, ensure that you have set the value either via the '\
|
363
|
+
'update_from_msats! function or manually'
|
364
|
+
end
|
635
365
|
raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni)
|
636
366
|
raise 'Invalid start' unless [DateTime, Time].include?(start.class)
|
637
367
|
raise 'Invalid finish' unless [DateTime, Time].include?(finish.class)
|
638
368
|
raise 'start cannot be after finish' if start > finish
|
639
|
-
|
640
|
-
|
369
|
+
|
370
|
+
possible_values = TNI_CODES[@tni].reject do |tni_code|
|
371
|
+
start > Time.parse(tni_code['ToDate']) ||
|
372
|
+
finish < Time.parse(tni_code['FromDate'])
|
373
|
+
end
|
374
|
+
|
641
375
|
return nil if possible_values.empty?
|
642
376
|
possible_values.map { |x| x['mlf_data']['loss_factors'] }
|
643
377
|
end
|
data/lib/aemo/version.rb
CHANGED
@@ -24,7 +24,7 @@
|
|
24
24
|
# @author Joel Courtney <euphemize@gmail.com>
|
25
25
|
module AEMO
|
26
26
|
# aemo version
|
27
|
-
VERSION = '0.
|
27
|
+
VERSION = '0.3.0-rc1'
|
28
28
|
|
29
29
|
# aemo version split amongst different revisions
|
30
30
|
MAJOR_VERSION, MINOR_VERSION, REVISION = VERSION.split('.').map(&:to_i)
|
data/lib/aemo.rb
CHANGED
@@ -11,9 +11,11 @@ require 'aemo/market/node.rb'
|
|
11
11
|
require 'aemo/meter.rb'
|
12
12
|
require 'aemo/nem12.rb'
|
13
13
|
require 'aemo/nmi.rb'
|
14
|
+
require 'aemo/nmi/allocation.rb'
|
14
15
|
require 'aemo/msats.rb'
|
15
16
|
require 'aemo/register.rb'
|
16
17
|
require 'aemo/version.rb'
|
18
|
+
require 'aemo/exceptions/invalid_nmi_allocation_type.rb'
|
17
19
|
|
18
20
|
# AEMO Module to encapsulate all AEMO classes
|
19
21
|
module AEMO
|
data/lib/data/xml_to_json.rb
CHANGED
@@ -24,8 +24,8 @@ CSV.parse(file_contents, headers: true, converters: :numeric).each do |row|
|
|
24
24
|
row.headers.select { |x| x =~ /^FY\d{2}$/ }.sort.reverse.each do |fin_year|
|
25
25
|
year = "20#{fin_year.match(/FY(\d{2})/)[1]}".to_i
|
26
26
|
@mlf_data[row['TNI']][:loss_factors] << {
|
27
|
-
start:
|
28
|
-
finish:
|
27
|
+
start: Time.parse("#{year - 1}-07-01T00:00:00+1000"),
|
28
|
+
finish: Time.parse("#{year}-07-01T00:00:00+1000"),
|
29
29
|
value: row[fin_year]
|
30
30
|
}
|
31
31
|
end
|
@@ -62,8 +62,8 @@ end
|
|
62
62
|
unless @mlf_data[code].nil?
|
63
63
|
output_data_instance[:mlf_data] = @mlf_data[code].deep_dup
|
64
64
|
output_data_instance[:mlf_data][:loss_factors].reject! do |x|
|
65
|
-
|
66
|
-
|
65
|
+
Time.parse(output_data_instance['ToDate']) < x[:start] ||
|
66
|
+
Time.parse(output_data_instance['FromDate']) >= x[:finish]
|
67
67
|
end
|
68
68
|
puts 'output_data_instance[:mlf_data][:loss_factors]: ' \
|
69
69
|
"#{output_data_instance[:mlf_data][:loss_factors].inspect}"
|
@@ -16,14 +16,14 @@ describe AEMO::Market::Interval do
|
|
16
16
|
expect { AEMO::Market::Interval.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23, 'RRP' => 76.54, 'PERIODTYPE' => 'TRADING') }.not_to raise_error
|
17
17
|
end
|
18
18
|
it 'has a trailing datetime' do
|
19
|
-
expect(@interval.datetime).to eq(
|
19
|
+
expect(@interval.datetime).to eq(Time.parse('2016-03-01T00:30:00+1000'))
|
20
20
|
end
|
21
21
|
it 'has a leading datetime' do
|
22
|
-
expect(@interval.datetime(false)).to eq(
|
22
|
+
expect(@interval.datetime(false)).to eq(Time.parse('2016-03-01T00:00:00+1000'))
|
23
23
|
end
|
24
24
|
it 'has a leading datetime for dispatch' do
|
25
25
|
@interval = AEMO::Market::Interval.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23, 'RRP' => 76.54, 'PERIODTYPE' => '')
|
26
|
-
expect(@interval.datetime(false)).to eq(
|
26
|
+
expect(@interval.datetime(false)).to eq(Time.parse('2016-03-01T00:25:00+1000'))
|
27
27
|
end
|
28
28
|
it 'has an interval length' do
|
29
29
|
expect(@interval.interval_length).to eq(Time.at(300))
|
data/spec/lib/aemo/msats_spec.rb
CHANGED
@@ -48,15 +48,11 @@ describe AEMO::MSATS do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'should return a hash of information' do
|
51
|
-
expect(AEMO::MSATS.c4('4001234567',
|
51
|
+
expect(AEMO::MSATS.c4('4001234567', Time.now, Time.now, Time.now)).to be_a(Hash)
|
52
52
|
end
|
53
53
|
it 'should raise an error for a bad nmi' do
|
54
54
|
expect { AEMO::MSATS.c4('BOBISAFISH') }.to raise_error(ArgumentError)
|
55
55
|
end
|
56
|
-
it 'should return a hash of information' do
|
57
|
-
# AEMO::MSATS.c4('4001234566', DateTime.now, DateTime.now, DateTime.now)
|
58
|
-
# TODO workout what the different errors are here...
|
59
|
-
end
|
60
56
|
end
|
61
57
|
|
62
58
|
describe 'invalid MSATS account' do
|
@@ -67,9 +63,9 @@ describe AEMO::MSATS do
|
|
67
63
|
it 'should return response' do
|
68
64
|
expect(AEMO::MSATS.c4(
|
69
65
|
'4001234567',
|
70
|
-
|
71
|
-
|
72
|
-
|
66
|
+
Time.now,
|
67
|
+
Time.now,
|
68
|
+
Time.now
|
73
69
|
).class).to eq(HTTParty::Response)
|
74
70
|
end
|
75
71
|
end
|