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 +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
|