aemo 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/aemo.gemspec +4 -5
- data/lib/aemo.rb +2 -0
- data/lib/aemo/msats.rb +222 -0
- data/lib/aemo/nem12.rb +47 -15
- data/lib/aemo/nmi.rb +492 -280
- data/lib/aemo/region.rb +5 -1
- data/lib/data/TNI-MLF-Codes.csv +1 -0
- data/lib/data/aemo-dlf-dnsp.csv +1 -0
- data/lib/data/aemo-dlf.json +1 -0
- data/lib/data/aemo-dlf.xml +53877 -0
- data/lib/data/aemo-tni.json +1 -0
- data/lib/data/aemo-tni.xml +13072 -0
- data/lib/data/xml-to-json.rb +54 -0
- data/spec/aemo/nem12_spec.rb +0 -1
- data/spec/aemo/nmi_spec.rb +131 -0
- data/spec/spec_helper.rb +2 -1
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a401245a1f7b25723a1ae670079c4d58a444f8f6
|
4
|
+
data.tar.gz: c48680094298ec88e7cfe69d2e849c061177a2ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc19ba3f91983da75207c8d6328ab4ecce573b849bbaddfa4720b73ce05e4db00a1f8604ad3adebe18b88755d9801894055820339d18890bc820eef6f9315e71
|
7
|
+
data.tar.gz: 3151c7067018f9d059ddfe27d361172aef4a2a66ad301a25a0690d336e7a95c606ff6df699434c02d1cbc325a311fb2dfadbc490682b5139eb9f808124b12941
|
data/.gitignore
ADDED
data/aemo.gemspec
CHANGED
@@ -3,16 +3,15 @@ $:.push File.expand_path('../lib', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'aemo'
|
6
|
-
s.version = '0.1.
|
6
|
+
s.version = '0.1.5'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.date = '
|
8
|
+
s.date = '2015-02-07'
|
9
9
|
s.summary = 'AEMO Gem'
|
10
10
|
s.description = 'Gem providing functionality for the Australian Energy Market Operator data'
|
11
11
|
s.authors = ['Joel Courtney']
|
12
12
|
s.email = ['jcourtney@cozero.com.au']
|
13
|
-
s.homepage =
|
14
|
-
|
15
|
-
s.license = 'MIT'
|
13
|
+
s.homepage = 'https://github.com/jufemaiz/aemo'
|
14
|
+
s.license = 'MIT'
|
16
15
|
|
17
16
|
s.required_ruby_version = '>= 1.9.3'
|
18
17
|
|
data/lib/aemo.rb
CHANGED
data/lib/aemo/msats.rb
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'aemo'
|
3
|
+
require 'zip'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'digest/sha1'
|
6
|
+
|
7
|
+
module AEMO
|
8
|
+
|
9
|
+
class MSATS
|
10
|
+
# Globally set request headers
|
11
|
+
HEADERS = {
|
12
|
+
'User-Agent' => "Ruby.AEMO.MSATS.Api",
|
13
|
+
'Accept' => 'text/xml',
|
14
|
+
'Content-Type' => 'text/xml'
|
15
|
+
}
|
16
|
+
|
17
|
+
# We like to party
|
18
|
+
include HTTParty
|
19
|
+
# We like to debug
|
20
|
+
# debug_output $stdout
|
21
|
+
|
22
|
+
# We like to SSLv3
|
23
|
+
ssl_version :SSLv3
|
24
|
+
|
25
|
+
# Where we like to party
|
26
|
+
base_uri 'https://msats.prod.nemnet.net.au/msats/ws/'
|
27
|
+
|
28
|
+
def initialize(options = {})
|
29
|
+
@@auth = {username: nil, password: nil}
|
30
|
+
@retailer = 'COZEROER'
|
31
|
+
|
32
|
+
@@auth[:username] = options[:username] if options[:username].is_a?(String)
|
33
|
+
@@auth[:password] = options[:password] if options[:password].is_a?(String)
|
34
|
+
@@participant_id = options[:participant_id] if options[:participant_id].is_a?(String)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Single NMI Master (C4) Report
|
38
|
+
# /C4/PARTICIPANT_IDENTIFIER?transactionId=XXX&nmi=XXX&checksum=X&type=XXX&reason=XXX
|
39
|
+
#
|
40
|
+
# @param [String,AEMO::NMI] nmi the NMI to run the master report against
|
41
|
+
# @param [Date,String] from_date the from date for the master report
|
42
|
+
# @param [Date,String] to_date the from date for the master report
|
43
|
+
# @return [Hash] A hashed view of the NMI Standing Data for a given NMI
|
44
|
+
def self.c4(nmi, from_date,to_date,as_at_date, options = {})
|
45
|
+
|
46
|
+
nmi = AEMO::NMI.new(nmi) if nmi.is_a?(String)
|
47
|
+
from_date = Date.parse(from_date) if from_date.is_a?(String)
|
48
|
+
to_date = Date.parse(to_date) if to_date.is_a?(String)
|
49
|
+
as_at_date = Date.parse(as_at_date) if as_at_date.is_a?(String)
|
50
|
+
|
51
|
+
options[:participantId] ||= nil
|
52
|
+
options[:roleId] ||= nil
|
53
|
+
options[:inittransId] ||= nil
|
54
|
+
|
55
|
+
query = {
|
56
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
57
|
+
NMI: nmi.nmi, # Note: AEMO is a bunch of idiots, has case sensitivity but no consistency across requests. Muppets.
|
58
|
+
fromDate: from_date,
|
59
|
+
toDate: to_date,
|
60
|
+
asatDate: as_at_date,
|
61
|
+
participantId: @@participant_id,
|
62
|
+
roleId: options[:role_id],
|
63
|
+
inittransId: options[:init_trans_id],
|
64
|
+
}
|
65
|
+
|
66
|
+
response = self.get( "/C4/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
67
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['ReportResponse']['ReportResults']
|
68
|
+
end
|
69
|
+
|
70
|
+
# MSATS Limits
|
71
|
+
# /MSATSLimits/PARTICIPANT_IDENTIFIER?transactionId=XXX
|
72
|
+
#
|
73
|
+
# @return [Hash] The report results from the MSATS Limits web service query
|
74
|
+
def self.msats_limits
|
75
|
+
query = {
|
76
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
77
|
+
}
|
78
|
+
response = self.get( "/MSATSLimits/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
79
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['ReportResponse']['ReportResults']
|
80
|
+
end
|
81
|
+
|
82
|
+
# NMI Discovery - By Delivery Point Identifier
|
83
|
+
#
|
84
|
+
# @param [String] jurisdiction_code The Jurisdiction Code
|
85
|
+
# @param [Integer] delivery_point_identifier Delivery Point Iddentifier
|
86
|
+
# @return [Hash] The response
|
87
|
+
def self.nmi_discovery_by_delivery_point_identifier(jurisdiction_code,delivery_point_identifier)
|
88
|
+
raise ArgumentError, 'jurisdiction_code is not valid' unless %w(ACT NEM NSW QLD SA VIC TAS).include?(jurisdiction_code)
|
89
|
+
raise ArgumentError, 'delivery_point_identifier is not valid' unless delivery_point_identifier.respond_to?("to_i")
|
90
|
+
raise ArgumentError, 'delivery_point_identifier is not valid' if( delivery_point_identifier.to_i < 10000000 || delivery_point_identifier.to_i > 99999999)
|
91
|
+
|
92
|
+
query = {
|
93
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
94
|
+
jurisdictionCode: jurisdiction_code,
|
95
|
+
deliveryPointIdentifier: delivery_point_identifier.to_i
|
96
|
+
}
|
97
|
+
|
98
|
+
response = self.get( "/NMIDiscovery/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
99
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['NMIDiscoveryResponse']['NMIStandingData']
|
100
|
+
end
|
101
|
+
|
102
|
+
# NMI Discovery - By Meter Serial Numner
|
103
|
+
#
|
104
|
+
# @param [String] jurisdiction_code The Jurisdiction Code
|
105
|
+
# @param [Integer] meter_serial_number The meter's serial number
|
106
|
+
# @return [Hash] The response
|
107
|
+
def self.nmi_discovery_by_meter_serial_number(jurisdiction_code,meter_serial_number)
|
108
|
+
raise ArgumentError, 'jurisdiction_code is not valid' unless %w(ACT NEM NSW QLD SA VIC TAS).include?(jurisdiction_code)
|
109
|
+
|
110
|
+
query = {
|
111
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
112
|
+
jurisdictionCode: jurisdiction_code,
|
113
|
+
meterSerialNumber: meter_serial_number.to_i
|
114
|
+
}
|
115
|
+
|
116
|
+
response = self.get( "/NMIDiscovery/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
117
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['NMIDiscoveryResponse']['NMIStandingData']
|
118
|
+
end
|
119
|
+
|
120
|
+
# NMI Discovery - By Address
|
121
|
+
#
|
122
|
+
# @param [String] jurisdiction_code The Jurisdiction Code
|
123
|
+
# @param [Integer] meter_serial_number The meter's serial number
|
124
|
+
# @return [Hash] The response
|
125
|
+
def self.nmi_discovery_by_address(jurisdiction_code,options = {})
|
126
|
+
raise ArgumentError, 'jurisdiction_code is not valid' unless %w(ACT NEM NSW QLD SA VIC TAS).include?(jurisdiction_code)
|
127
|
+
|
128
|
+
options[:building_or_property_name] ||= nil
|
129
|
+
options[:location_descriptor] ||= nil
|
130
|
+
options[:lot_number] ||= nil
|
131
|
+
options[:flat_or_unit_number] ||= nil
|
132
|
+
options[:flat_or_unit_type] ||= nil
|
133
|
+
options[:floor_or_level_number] ||= nil
|
134
|
+
options[:floor_or_level_type] ||= nil
|
135
|
+
options[:house_number] ||= nil
|
136
|
+
options[:house_number_suffix] ||= nil
|
137
|
+
options[:street_name] ||= nil
|
138
|
+
options[:street_suffix] ||= nil
|
139
|
+
options[:street_type] ||= nil
|
140
|
+
options[:suburb_or_place_or_locality] ||= nil
|
141
|
+
options[:postcode] ||= nil
|
142
|
+
options[:state_or_territory] ||= jurisdiction_code
|
143
|
+
|
144
|
+
query = {
|
145
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
146
|
+
jurisdictionCode: jurisdiction_code,
|
147
|
+
buildingOrPropertyName: options[:building_or_property_name],
|
148
|
+
locationDescriptor: options[:location_descriptor],
|
149
|
+
lotNumber: options[:lot_number],
|
150
|
+
flatOrUnitNumber: options[:flat_or_unit_number],
|
151
|
+
flatOrUnitType: options[:flat_or_unit_type],
|
152
|
+
floorOrLevelNumber: options[:floor_or_level_number],
|
153
|
+
floorOrLevelType: options[:floor_or_level_type],
|
154
|
+
houseNumber: options[:house_number],
|
155
|
+
houseNumberSuffix: options[:house_number_suffix],
|
156
|
+
streetName: options[:street_name],
|
157
|
+
streetSuffix: options[:street_suffix],
|
158
|
+
streetType: options[:street_type],
|
159
|
+
suburbOrPlaceOrLocality: options[:suburb_or_place_or_locality],
|
160
|
+
postcode: options[:postcode],
|
161
|
+
stateOrTerritory: options[:state_or_territory]
|
162
|
+
}
|
163
|
+
|
164
|
+
response = self.get( "/NMIDiscovery/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
165
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['NMIDiscoveryResponse']['NMIStandingData']
|
166
|
+
end
|
167
|
+
|
168
|
+
# NMI Detail
|
169
|
+
# /NMIDetail/PARTICIPANT_IDENTIFIER?transactionId=XXX&nmi=XXX&checksum=X&type=XXX&reason=XXX
|
170
|
+
#
|
171
|
+
# @return [Hash] A hashed view of the NMI Standing Data for a given NMI
|
172
|
+
def self.nmi_detail(nmi, options = {})
|
173
|
+
if nmi.is_a?(String)
|
174
|
+
nmi = AEMO::NMI.new(nmi)
|
175
|
+
end
|
176
|
+
options[:type] ||= nil
|
177
|
+
options[:reason] ||= nil
|
178
|
+
|
179
|
+
query = {
|
180
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
181
|
+
nmi: nmi.nmi,
|
182
|
+
checksum: nmi.checksum,
|
183
|
+
type: options[:type],
|
184
|
+
reason: options[:reason]
|
185
|
+
}
|
186
|
+
|
187
|
+
response = self.get( "/NMIDetail/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
188
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['NMIStandingDataResponse']['NMIStandingData']
|
189
|
+
end
|
190
|
+
|
191
|
+
# Participant System Status
|
192
|
+
# /ParticipantSystemStatus/PARTICIPANT_IDENTIFIER?transactionId=XXX
|
193
|
+
#
|
194
|
+
# @return [Hash] The report results from the Participant System Status web service query
|
195
|
+
def self.system_status
|
196
|
+
query = {
|
197
|
+
transactionId: Digest::SHA1.hexdigest(Time.now.to_s)[0..35],
|
198
|
+
}
|
199
|
+
response = self.get( "/ParticipantSystemStatus/#{@@participant_id}", basic_auth: @@auth, headers: { 'Accept' => 'text/xml', 'Content-Type' => 'text/xml'}, query: query )
|
200
|
+
response.parsed_response['aseXML']['Transactions']['Transaction']['ReportResponse']['ReportResults']
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
# Sets the authentication credentials in a class variable.
|
205
|
+
#
|
206
|
+
# @param [String] email cl.ly email
|
207
|
+
# @param [String] password cl.ly password
|
208
|
+
# @return [Hash] authentication credentials
|
209
|
+
def self.authorize(participant_id,username,password)
|
210
|
+
@@participant_id = participant_id
|
211
|
+
@@auth = {username: username, password: password}
|
212
|
+
end
|
213
|
+
|
214
|
+
# Check if credentials are available to use
|
215
|
+
#
|
216
|
+
# @return [Boolean] true/false if credentials are available
|
217
|
+
def self.can_authenticate?
|
218
|
+
!(@@participant_id.nil? || @@auth[:username].nil? || @@auth[:username].nil?)
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
end
|
data/lib/aemo/nem12.rb
CHANGED
@@ -385,7 +385,21 @@ module AEMO
|
|
385
385
|
end
|
386
386
|
end
|
387
387
|
|
388
|
-
|
388
|
+
# Deal with flags if necessary
|
389
|
+
flag = nil
|
390
|
+
# Based on QualityMethod and ReasonCode
|
391
|
+
if csv[intervals_offset + 0].length == 3 || !csv[intervals_offset + 1].nil?
|
392
|
+
flag ||= { quality_flag: nil, method_flag: nil, reason_code: nil }
|
393
|
+
if csv[intervals_offset + 0].length == 3
|
394
|
+
flag[:quality_flag] = csv[intervals_offset + 0][0]
|
395
|
+
flag[:method_flag] = csv[intervals_offset + 0][1,2]
|
396
|
+
end
|
397
|
+
unless csv[intervals_offset + 1].nil?
|
398
|
+
flag[:reason_code] = csv[intervals_offset + 1]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
base_interval = { data_details: @data_details.last, datetime: Time.parse("#{csv[1]}000000+1000"), value: nil, flag: flag}
|
389
403
|
|
390
404
|
intervals = []
|
391
405
|
(2..(number_of_intervals+1)).each do |i|
|
@@ -458,34 +472,52 @@ module AEMO
|
|
458
472
|
def parse_nem12_900(line,options={})
|
459
473
|
end
|
460
474
|
|
461
|
-
#
|
462
|
-
#
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
475
|
+
# Turns the flag to a string
|
476
|
+
#
|
477
|
+
# @param [Hash] the object of a flag
|
478
|
+
# @return [String] a hyphenated string for the flag
|
479
|
+
def flag_to_s(flag)
|
480
|
+
flag_to_s = []
|
481
|
+
unless flag.nil?
|
482
|
+
flag_to_s << QUALITY_FLAGS[flag[:quality_flag]]
|
483
|
+
flag_to_s << METHOD_FLAGS[flag[:method_flag]][:short_descriptor]
|
484
|
+
flag_to_s << REASON_CODES[flag[:reason_code]]
|
485
|
+
end
|
486
|
+
flag_to_s.join(" - ")
|
471
487
|
end
|
472
|
-
|
488
|
+
|
473
489
|
# @return [Array] array of a NEM12 file a given Meter + Data Stream for easy reading
|
474
490
|
def to_a
|
475
491
|
values = @interval_data.map do |d|
|
476
492
|
uom = UOM[UOM_NON_SPEC_MAPPING[d[:data_details][:uom].upcase]]
|
477
|
-
|
478
|
-
|
493
|
+
[ d[:data_details][:nmi],
|
494
|
+
d[:data_details][:nmi_suffix].upcase,
|
495
|
+
d[:data_details][:uom],
|
496
|
+
d[:datetime],
|
497
|
+
d[:value],
|
498
|
+
flag_to_s(d[:flag])]
|
479
499
|
end
|
480
500
|
values
|
481
501
|
end
|
482
502
|
|
483
503
|
# @return [Array] CSV of a NEM12 file a given Meter + Data Stream for easy reading
|
484
504
|
def to_csv
|
485
|
-
headers = ['nmi','suffix','units','datetime','value']
|
505
|
+
headers = ['nmi','suffix','units','datetime','value','flags']
|
486
506
|
([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")
|
487
507
|
end
|
488
508
|
|
509
|
+
# @param nmi [String] a NMI that is to be checked for validity
|
510
|
+
# @return [Boolean] determines if the NMI is valid
|
511
|
+
def self.valid_nmi?(nmi)
|
512
|
+
(nmi.class == String && nmi.length == 10 && !nmi.match(/^[A-Z1-9][A-Z0-9]{9}$/).nil?)
|
513
|
+
end
|
514
|
+
|
515
|
+
# @param path_to_file [String] the path to a file
|
516
|
+
# @return [] NEM12 object
|
517
|
+
def self.parse_nem12_file(path_to_file, strict = false)
|
518
|
+
parse_nem12(File.read(path_to_file),strict)
|
519
|
+
end
|
520
|
+
|
489
521
|
# @param contents [String] the path to a file
|
490
522
|
# @return [Array[AEMO::NEM12]] An array of NEM12 objects
|
491
523
|
def self.parse_nem12(contents, strict=false)
|
data/lib/aemo/nmi.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'json'
|
3
|
+
require 'time'
|
4
|
+
require 'ostruct'
|
1
5
|
module AEMO
|
6
|
+
# AEMO::NMI acts as an object to simplify access to data and information about a NMI and provide verification of the NMI value
|
2
7
|
class NMI
|
8
|
+
# Operational Regions for the NMI
|
3
9
|
REGIONS = {
|
4
10
|
'ACT' => 'Australian Capital Territory',
|
5
11
|
'NSW' => 'New South Wales',
|
@@ -10,366 +16,572 @@ module AEMO
|
|
10
16
|
'WA' => 'Western Australia',
|
11
17
|
'NT' => 'Northern Territory'
|
12
18
|
}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
/^
|
19
|
+
# NMI_ALLOCATIONS as per AEMO Documentation at http://aemo.com.au/Electricity/Policies-and-Procedures/Retail-and-Metering/~/media/Files/Other/Retail%20and%20Metering/NMI_Allocation_List_v7_June_2012.ashx
|
20
|
+
# Last accessed 2015-02-04
|
21
|
+
NMI_ALLOCATIONS = {
|
22
|
+
'ACTEWP' => {
|
23
|
+
title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
|
24
|
+
friendly_title: 'ACTEWAgl',
|
25
|
+
state: 'ACT',
|
26
|
+
type: 'electricity',
|
27
|
+
includes: [
|
28
|
+
(/^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
29
|
+
(/^(7001\d{6})$/)
|
30
|
+
],
|
31
|
+
excludes: [
|
23
32
|
]
|
24
33
|
},
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
/^
|
33
|
-
/^
|
34
|
+
'CNRGYP' => {
|
35
|
+
title: 'Essential Energy',
|
36
|
+
friendly_title: 'Essential Energy',
|
37
|
+
state: 'NSW',
|
38
|
+
type: 'electricity',
|
39
|
+
includes: [
|
40
|
+
(/^(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
|
+
],
|
49
|
+
excludes: [
|
34
50
|
]
|
35
51
|
},
|
36
|
-
{
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
/^
|
43
|
-
/^
|
52
|
+
'ENERGYAP' => {
|
53
|
+
title: 'Ausgrid',
|
54
|
+
friendly_title: 'Ausgrid',
|
55
|
+
state: 'NSW',
|
56
|
+
type: 'electricity',
|
57
|
+
includes: [
|
58
|
+
(/^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
59
|
+
(/^(410[234]\d{6})$/)
|
60
|
+
],
|
61
|
+
excludes: [
|
44
62
|
]
|
45
63
|
},
|
46
|
-
{
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
/^
|
53
|
-
/^
|
54
|
-
|
55
|
-
|
56
|
-
/^4001[0-9]{6}$/,
|
57
|
-
/^4508[0-9]{6}$/,
|
58
|
-
/^4204[0-9]{6}$/,
|
59
|
-
/^4407[0-9]{6}$/
|
64
|
+
'INTEGP' => {
|
65
|
+
title: 'Endeavour Energy',
|
66
|
+
friendly_title: 'Endeavour Energy',
|
67
|
+
state: 'NSW',
|
68
|
+
type: 'electricity',
|
69
|
+
includes: [
|
70
|
+
(/^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
71
|
+
(/^(431\d{7})$/)
|
72
|
+
],
|
73
|
+
excludes: [
|
60
74
|
]
|
61
75
|
},
|
62
|
-
{
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
/^
|
69
|
-
/^
|
76
|
+
'TRANSGP' => {
|
77
|
+
title: 'TransGrid',
|
78
|
+
friendly_title: 'TransGrid',
|
79
|
+
state: 'NSW',
|
80
|
+
type: 'electricity',
|
81
|
+
includes: [
|
82
|
+
(/^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
83
|
+
(/^(460810[0-8]\d{3})$/)
|
84
|
+
],
|
85
|
+
excludes: [
|
70
86
|
]
|
71
87
|
},
|
72
|
-
{
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
/^4708109
|
88
|
+
'SNOWY' => {
|
89
|
+
title: 'Snowy Hydro Ltd',
|
90
|
+
friendly_title: 'Snowy Hydro',
|
91
|
+
state: 'NSW',
|
92
|
+
type: 'electricity',
|
93
|
+
includes: [
|
94
|
+
(/^(4708109\d{3})$/)
|
95
|
+
],
|
96
|
+
excludes: [
|
79
97
|
]
|
80
98
|
},
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
99
|
+
'NT_RESERVED' => {
|
100
|
+
title: 'Northern Territory Reserved Block',
|
101
|
+
friendly_title: 'Northern Territory Reserved Block',
|
102
|
+
state: 'NT',
|
103
|
+
type: 'electricity',
|
104
|
+
includes: [
|
105
|
+
(/^(250\d{7})$/)
|
106
|
+
],
|
107
|
+
excludes: [
|
89
108
|
]
|
90
109
|
},
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
/^
|
99
|
-
/^
|
100
|
-
/^
|
101
|
-
/^
|
102
|
-
/^
|
103
|
-
/^
|
104
|
-
|
110
|
+
'ERGONETP' => {
|
111
|
+
title: 'Ergon Energy Corporation',
|
112
|
+
friendly_title: 'Ergon Energy',
|
113
|
+
state: '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: [
|
105
125
|
]
|
106
126
|
},
|
107
|
-
{
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
/^
|
114
|
-
/^31
|
127
|
+
'ENERGEXP' => {
|
128
|
+
title: 'ENERGEX Limited',
|
129
|
+
friendly_title: 'Energex',
|
130
|
+
state: 'QLD',
|
131
|
+
type: 'electricity',
|
132
|
+
includes: [
|
133
|
+
(/^(QBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
134
|
+
(/^(31\d{8})$/)
|
135
|
+
],
|
136
|
+
excludes: [
|
115
137
|
]
|
116
138
|
},
|
117
|
-
{
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
/^
|
124
|
-
/^320200
|
139
|
+
'PLINKP' => {
|
140
|
+
title: 'Qld Electricity Transmission Corp (Powerlink)',
|
141
|
+
friendly_title: 'Powerlink',
|
142
|
+
state: '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: [
|
125
149
|
]
|
126
150
|
},
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
/^
|
135
|
-
/^
|
136
|
-
|
151
|
+
'UMPLP' => {
|
152
|
+
title: 'SA Power Networks',
|
153
|
+
friendly_title: 'SA Power Networks',
|
154
|
+
state: '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: [
|
137
162
|
]
|
138
163
|
},
|
139
|
-
{
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
/^
|
146
|
-
/^
|
164
|
+
'TRANSEND' => {
|
165
|
+
title: 'ElectraNet SA',
|
166
|
+
friendly_title: 'ElectraNet SA',
|
167
|
+
state: '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: [
|
147
174
|
]
|
148
175
|
},
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
/^
|
157
|
-
/^
|
158
|
-
|
176
|
+
'AURORAP' => {
|
177
|
+
title: 'TasNetworks',
|
178
|
+
friendly_title: 'TasNetworks',
|
179
|
+
state: '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: [
|
159
187
|
]
|
160
188
|
},
|
161
|
-
{
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
/^
|
189
|
+
'TRANSEND' => {
|
190
|
+
title: 'TasNetworks',
|
191
|
+
friendly_title: 'TasNetworks',
|
192
|
+
state: 'TAS',
|
193
|
+
type: 'electricity',
|
194
|
+
includes: [
|
195
|
+
(/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/),
|
196
|
+
],
|
197
|
+
excludes: [
|
168
198
|
]
|
169
199
|
},
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
/^
|
178
|
-
|
200
|
+
'CITIPP' => {
|
201
|
+
title: 'CitiPower',
|
202
|
+
friendly_title: 'CitiPower',
|
203
|
+
state: 'VIC',
|
204
|
+
type: 'electricity',
|
205
|
+
includes: [
|
206
|
+
(/^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
207
|
+
(/^(610[23]\d{6})$/)
|
208
|
+
],
|
209
|
+
excludes: [
|
179
210
|
]
|
180
211
|
},
|
181
|
-
{
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
/^VBBB[A-HJ-NP-VX-
|
188
|
-
/^630[
|
212
|
+
'EASTERN' => {
|
213
|
+
title: 'SP AusNet',
|
214
|
+
friendly_title: 'SP AusNet DNSP',
|
215
|
+
state: 'VIC',
|
216
|
+
type: 'electricity',
|
217
|
+
includes: [
|
218
|
+
(/^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
219
|
+
(/^(630[56]\d{6})$/)
|
220
|
+
],
|
221
|
+
excludes: [
|
189
222
|
]
|
190
223
|
},
|
191
|
-
{
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
/^VCCC[A-HJ-NP-VX-
|
198
|
-
/^620[
|
224
|
+
'POWCP' => {
|
225
|
+
title: 'PowerCor Australia',
|
226
|
+
friendly_title: 'PowerCor',
|
227
|
+
state: 'VIC',
|
228
|
+
type: 'electricity',
|
229
|
+
includes: [
|
230
|
+
(/^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
231
|
+
(/^(620[34]\d{6})$/)
|
232
|
+
],
|
233
|
+
excludes: [
|
199
234
|
]
|
200
235
|
},
|
201
|
-
{
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
/^VDDD[A-HJ-NP-VX-
|
208
|
-
/^6001
|
236
|
+
'SOLARISP' => {
|
237
|
+
title: 'Jemena Electricity Networks (VIC)',
|
238
|
+
friendly_title: 'Jemena',
|
239
|
+
state: 'VIC',
|
240
|
+
type: 'electricity',
|
241
|
+
includes: [
|
242
|
+
(/^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
243
|
+
(/^(6001\d{6})$/)
|
244
|
+
],
|
245
|
+
excludes: [
|
209
246
|
]
|
210
247
|
},
|
211
|
-
{
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
/^VEEE[A-HJ-NP-VX-
|
218
|
-
/^640[
|
248
|
+
'UNITED' => {
|
249
|
+
title: 'United Energy Distribution',
|
250
|
+
friendly_title: 'United Energy',
|
251
|
+
state: 'VIC',
|
252
|
+
type: 'electricity',
|
253
|
+
includes: [
|
254
|
+
(/^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
255
|
+
(/^(640[78]\d{6})$/)
|
256
|
+
],
|
257
|
+
excludes: [
|
219
258
|
]
|
220
259
|
},
|
221
|
-
{
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
/^
|
228
|
-
/^650900
|
260
|
+
'GPUPP' => {
|
261
|
+
title: 'SP AusNet TNSP',
|
262
|
+
friendly_title: 'SP AusNet TNSP',
|
263
|
+
state: 'VIC',
|
264
|
+
type: 'electricity',
|
265
|
+
includes: [
|
266
|
+
(/^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/),
|
267
|
+
(/^(650900\d{4})$/)
|
268
|
+
],
|
269
|
+
excludes: [
|
229
270
|
]
|
230
271
|
},
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
/^
|
239
|
-
/^801
|
240
|
-
/^8020
|
272
|
+
'WESTERNPOWER' => {
|
273
|
+
title: 'Western Power',
|
274
|
+
friendly_title: 'Western Power',
|
275
|
+
state: 'WA',
|
276
|
+
type: 'electricity',
|
277
|
+
includes: [
|
278
|
+
(/^(WAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
279
|
+
(/^(800[1-9]\d{6})$/),
|
280
|
+
(/^(801\d{7})$/),
|
281
|
+
(/^(8020\d{6})$/)
|
282
|
+
],
|
283
|
+
excludes: [
|
241
284
|
]
|
242
285
|
},
|
243
|
-
{
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
/^8021
|
286
|
+
'HORIZONPOWER' => {
|
287
|
+
title: 'Horizon Power',
|
288
|
+
friendly_title: 'Horizon Power',
|
289
|
+
state: 'WA',
|
290
|
+
type: 'electricity',
|
291
|
+
includes: [
|
292
|
+
(/^(8021\d{6})$/)
|
293
|
+
],
|
294
|
+
excludes: [
|
250
295
|
]
|
251
296
|
},
|
252
|
-
|
253
|
-
|
297
|
+
'GAS_NSW' => {
|
298
|
+
title: 'GAS NSW',
|
299
|
+
friendly_title: 'GAS NSW',
|
300
|
+
state: 'NSW',
|
254
301
|
type: 'gas',
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
/^52[0-9]{8}$/
|
302
|
+
includes: [
|
303
|
+
(/^(52\d{8})$/)
|
304
|
+
],
|
305
|
+
excludes: [
|
260
306
|
]
|
261
307
|
},
|
262
|
-
{
|
308
|
+
'GAS_VIC' => {
|
309
|
+
title: 'GAS VIC',
|
310
|
+
friendly_title: 'GAS VIC',
|
311
|
+
state: 'VIC',
|
263
312
|
type: 'gas',
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
/^53[0-9]{8}$/
|
313
|
+
includes: [
|
314
|
+
(/^(53\d{8})$/)
|
315
|
+
],
|
316
|
+
excludes: [
|
269
317
|
]
|
270
318
|
},
|
271
|
-
{
|
319
|
+
'GAS_QLD' => {
|
320
|
+
title: 'GAS QLD',
|
321
|
+
friendly_title: 'GAS QLD',
|
322
|
+
state: 'QLD',
|
272
323
|
type: 'gas',
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
/^54[0-9]{8}$/
|
324
|
+
includes: [
|
325
|
+
(/^(54\d{8})$/)
|
326
|
+
],
|
327
|
+
excludes: [
|
278
328
|
]
|
279
329
|
},
|
280
|
-
{
|
330
|
+
'GAS_SA' => {
|
331
|
+
title: 'GAS SA',
|
332
|
+
friendly_title: 'GAS SA',
|
333
|
+
state: 'SA',
|
281
334
|
type: 'gas',
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
/^55[0-9]{8}$/
|
335
|
+
includes: [
|
336
|
+
(/^(55\d{8})$/)
|
337
|
+
],
|
338
|
+
excludes: [
|
287
339
|
]
|
288
340
|
},
|
289
|
-
{
|
341
|
+
'GAS_WA' => {
|
342
|
+
title: 'GAS WA',
|
343
|
+
friendly_title: 'GAS WA',
|
344
|
+
state: 'WA',
|
290
345
|
type: 'gas',
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
/^56[0-9]{8}$/
|
346
|
+
includes: [
|
347
|
+
(/^(56\d{8})$/)
|
348
|
+
],
|
349
|
+
excludes: [
|
296
350
|
]
|
297
351
|
},
|
298
|
-
{
|
352
|
+
'GAS_TAS' => {
|
353
|
+
title: 'GAS TAS',
|
354
|
+
friendly_title: 'GAS TAS',
|
355
|
+
state: 'TAS',
|
299
356
|
type: 'gas',
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
/^57[0-9]{8}$/
|
357
|
+
includes: [
|
358
|
+
(/^(57\d{8})$/)
|
359
|
+
],
|
360
|
+
excludes: [
|
305
361
|
]
|
306
362
|
},
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
363
|
+
'FEDAIRPORTS' => {
|
364
|
+
title: 'Federal Airports Corporation (Sydney Airport)',
|
365
|
+
friendly_title: 'Sydney Airport',
|
366
|
+
state: 'NSW',
|
367
|
+
type: 'electricity',
|
368
|
+
includes: [
|
369
|
+
(/^(NJJJNR[A-HJ-NP-Z\d]{4})$/)
|
370
|
+
],
|
371
|
+
excludes: [
|
315
372
|
]
|
316
373
|
},
|
317
|
-
{
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
/^
|
324
|
-
/^7102
|
374
|
+
'EXEMPTNETWORKS' => {
|
375
|
+
title: 'Exempt Networks - various',
|
376
|
+
friendly_title: 'Exempt Networks - various',
|
377
|
+
state: '',
|
378
|
+
type: 'electricity',
|
379
|
+
includes: [
|
380
|
+
(/^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/),
|
381
|
+
(/^(7102\d{6})$/)
|
382
|
+
],
|
383
|
+
excludes: [
|
325
384
|
]
|
326
385
|
},
|
327
|
-
{
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
/^880[1-5]
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
type: 'electrical',
|
338
|
-
key: nil,
|
339
|
-
states: [],
|
340
|
-
names: ['AEMO Reserved Block 1'],
|
341
|
-
nmi_blocks: [
|
342
|
-
/^9[0-9]{9}$/
|
386
|
+
'AEMORESERVED' => {
|
387
|
+
title: 'AEMO Reserved',
|
388
|
+
friendly_title: 'AEMO Reserved',
|
389
|
+
state: '',
|
390
|
+
type: 'electricity',
|
391
|
+
includes: [
|
392
|
+
(/^(880[1-5]\d{6})$/),
|
393
|
+
(/^(9\d{9})$/)
|
394
|
+
],
|
395
|
+
excludes: [
|
343
396
|
]
|
344
397
|
}
|
345
|
-
|
398
|
+
}
|
399
|
+
# Transmission Node Identifier Codes are loaded from a json file
|
400
|
+
# Obtained from http://www.nemweb.com.au/
|
401
|
+
#
|
402
|
+
# See /lib/data for further data manipulation required
|
403
|
+
TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__),'..','data','aemo-tni.json')))
|
404
|
+
# Distribution Loss Factor Codes are loaded from a json file
|
405
|
+
# Obtained from MSATS, matching to DNSP from file http://www.aemo.com.au/Electricity/Market-Operations/Loss-Factors-and-Regional-Boundaries/~/media/Files/Other/loss%20factors/DLF_FINAL_V2_2014_2015.ashx
|
406
|
+
# Last accessed 2015-02-06
|
407
|
+
# See /lib/data for further data manipulation required
|
408
|
+
DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__),'..','data','aemo-dlf.json')))
|
409
|
+
|
410
|
+
# [String] National Meter Identifier
|
411
|
+
@nmi = nil
|
412
|
+
@msats_detail = nil
|
413
|
+
@tni = nil
|
414
|
+
@dlf = nil
|
415
|
+
@customer_classification_code = nil
|
416
|
+
@customer_threshold_code = nil
|
417
|
+
@jurisdiction_code = nil
|
418
|
+
@classification_code = nil
|
419
|
+
@status = nil
|
420
|
+
@address = nil
|
421
|
+
@meters = nil
|
422
|
+
@roles = nil
|
423
|
+
|
424
|
+
attr_accessor :nmi, :msats_detail, :tni, :dlf, :customer_classification_code, :customer_threshold_code, :jurisdiction_code, :classification_code, :status, :address, :meters, :roles
|
346
425
|
|
347
|
-
|
426
|
+
# Initialize a NEM12 file
|
427
|
+
#
|
428
|
+
# @param nmi [String] the National Meter Identifier (NMI)
|
429
|
+
# @param options [Hash] a hash of options
|
430
|
+
# @return [AEMO::NMI] an instance of AEMO::NMI is returned
|
431
|
+
def initialize(nmi,options={})
|
432
|
+
raise ArgumentError.new("NMI is not a string") unless nmi.is_a?(String)
|
433
|
+
raise ArgumentError.new("NMI is not 10 characters") unless nmi.length == 10
|
434
|
+
raise ArgumentError.new("NMI is not constructed with valid characters") unless AEMO::NMI.valid_nmi?(nmi)
|
435
|
+
|
436
|
+
@nmi = nmi
|
437
|
+
@meters = {}
|
438
|
+
@roles = {}
|
439
|
+
end
|
348
440
|
|
349
|
-
|
350
|
-
|
351
|
-
|
441
|
+
# A function to validate the instance's nmi value
|
442
|
+
#
|
443
|
+
# @return [Boolean] whether or not the nmi is valid
|
444
|
+
def valid_nmi?
|
445
|
+
AEMO::NMI.valid_nmi?(@nmi)
|
446
|
+
end
|
447
|
+
|
448
|
+
# Find the Network of NMI
|
449
|
+
#
|
450
|
+
# @returns [Hash] The Network information
|
451
|
+
def network
|
452
|
+
AEMO::NMI.network(@nmi)
|
453
|
+
end
|
454
|
+
|
455
|
+
# A function to calculate the checksum value for a given National Meter Identifier
|
456
|
+
#
|
457
|
+
# @param checksum_value [Integer] the checksum value to check against the current National Meter Identifier's checksum value
|
458
|
+
# @return [Boolean] whether or not the checksum is valid
|
459
|
+
def valid_checksum?(checksum_value)
|
460
|
+
checksum_value == self.checksum
|
461
|
+
end
|
462
|
+
|
463
|
+
# Checksum is a function to calculate the checksum value for a given National Meter Identifier
|
464
|
+
#
|
465
|
+
# @return [Integer] the checksum value for the current National Meter Identifier
|
466
|
+
def checksum
|
467
|
+
summation = 0
|
468
|
+
@nmi.reverse.split(//).each_index do |i|
|
469
|
+
value = nmi[nmi.length - i - 1].ord
|
470
|
+
if(i % 2 == 0)
|
471
|
+
value = value * 2
|
472
|
+
end
|
473
|
+
value = value.to_s.split(//).map{|i| i.to_i}.reduce(:+)
|
474
|
+
summation += value
|
475
|
+
end
|
476
|
+
checksum = (10 - (summation % 10)) % 10
|
477
|
+
checksum
|
352
478
|
end
|
353
479
|
|
480
|
+
# Provided MSATS is configured, gets the MSATS data for the NMI
|
481
|
+
#
|
482
|
+
# @return [Hash] MSATS NMI Detail data
|
483
|
+
def raw_msats_nmi_detail
|
484
|
+
raise ArgumentError, 'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate?
|
485
|
+
|
486
|
+
AEMO::MSATS.nmi_detail(@nmi)
|
487
|
+
end
|
354
488
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
489
|
+
# Provided MSATS is configured, uses the raw MSATS data to augment NMI information
|
490
|
+
#
|
491
|
+
# @return [self] returns self
|
492
|
+
def update_from_msats!
|
493
|
+
# Update local cache
|
494
|
+
@msats_detail = raw_msats_nmi_detail
|
495
|
+
# Set the details
|
496
|
+
@tni = @msats_detail['MasterData']['TransmissionNodeIdentifier']
|
497
|
+
@dlf = @msats_detail['MasterData']['DistributionLossFactorCode']
|
498
|
+
@customer_classification_code = @msats_detail['MasterData']['CustomerClassificationCode']
|
499
|
+
@customer_threshold_code = @msats_detail['MasterData']['CustomerThresholdCode']
|
500
|
+
@jurisdiction_code = @msats_detail['MasterData']['JurisdictionCode']
|
501
|
+
@classification_code = @msats_detail['MasterData']['NMIClassificationCode']
|
502
|
+
@status = @msats_detail['MasterData']['Status']
|
503
|
+
@address = @msats_detail['MasterData']['Address']
|
504
|
+
@meters ||= {}
|
505
|
+
# Meters
|
506
|
+
unless @msats_detail['MeterRegister'].nil?
|
507
|
+
@msats_detail['MeterRegister']['Meter'].select{|x| !x['Status'].nil? }.each do |meter|
|
508
|
+
m = OpenStruct.new(
|
509
|
+
status: meter['Status'],
|
510
|
+
installation_type_code: meter['InstallationTypeCode'],
|
511
|
+
next_scheduled_read_date: meter['NextScheduledReadDate'],
|
512
|
+
read_type_code: meter['ReadTypeCode'],
|
513
|
+
registers: []
|
514
|
+
)
|
515
|
+
puts "@meters.inspect: #{@meters.inspect}"
|
516
|
+
@meters[meter['SerialNumber']] = m
|
517
|
+
end
|
518
|
+
@msats_detail['MeterRegister']['Meter'].select{|x| x['Status'].nil? }.each do |registers|
|
519
|
+
register = OpenStruct.new(
|
520
|
+
controlled_load: (registers['RegisterConfiguration']['Register']['ControlledLoad'] == 'Y'),
|
521
|
+
dial_format: registers['RegisterConfiguration']['Register']['DialFormat'],
|
522
|
+
multiplier: registers['RegisterConfiguration']['Register']['Multiplier'],
|
523
|
+
network_tariff_code: registers['RegisterConfiguration']['Register']['NetworkTariffCode'],
|
524
|
+
register_id: registers['RegisterConfiguration']['Register']['RegisterID'],
|
525
|
+
status: registers['RegisterConfiguration']['Register']['Status'],
|
526
|
+
time_of_day: registers['RegisterConfiguration']['Register']['TimeOfDay'],
|
527
|
+
unit_of_measure: registers['RegisterConfiguration']['Register']['UnitOfMeasure']
|
528
|
+
)
|
529
|
+
@meters[registers['SerialNumber']].registers << register
|
530
|
+
end
|
531
|
+
end
|
532
|
+
# Roles
|
533
|
+
unless @msats_detail['RoleAssignments'].nil?
|
534
|
+
@msats_detail['RoleAssignments']['RoleAssignment'].each do |role|
|
535
|
+
@roles[role['Role']] = role['Party']
|
360
536
|
end
|
361
|
-
break unless nmi_network.nil?
|
362
537
|
end
|
363
|
-
|
538
|
+
|
539
|
+
self
|
364
540
|
end
|
365
|
-
|
541
|
+
|
542
|
+
# A function to validate the NMI provided
|
543
|
+
#
|
544
|
+
# @param nmi [String] the nmi to be checked
|
545
|
+
# @return [Boolean] whether or not the nmi is valid
|
546
|
+
def self.valid_nmi?(nmi)
|
547
|
+
((nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?)
|
548
|
+
end
|
549
|
+
|
550
|
+
# A function to calculate the checksum value for a given National Meter Identifier
|
551
|
+
#
|
552
|
+
# @param nmi [String] the NMI to check the checksum against
|
553
|
+
# @param checksum_value [Integer] the checksum value to check against the current National Meter Identifier's checksum value
|
554
|
+
# @return [Boolean] whether or not the checksum is valid
|
555
|
+
def self.valid_checksum?(nmi,checksum_value)
|
556
|
+
nmi = AEMO::NMI.new(nmi)
|
557
|
+
nmi.valid_checksum?(checksum_value)
|
558
|
+
end
|
559
|
+
|
560
|
+
# Find the Network for a given NMI
|
561
|
+
#
|
562
|
+
# @param nmi [String] NMI
|
563
|
+
# @returns [Hash] The Network information
|
564
|
+
def self.network(nmi)
|
565
|
+
network = nil
|
566
|
+
AEMO::NMI::NMI_ALLOCATIONS.each_pair do |identifier, details|
|
567
|
+
details[:includes].each do |pattern|
|
568
|
+
if nmi.match(pattern)
|
569
|
+
network = { identifier => details }
|
570
|
+
break
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
network
|
575
|
+
end
|
576
|
+
|
366
577
|
# ######### #
|
367
578
|
protected
|
368
579
|
# ######### #
|
369
|
-
|
580
|
+
|
370
581
|
def is_valid_region?(region)
|
371
582
|
REGIONS.keys.include?(region)
|
372
583
|
end
|
373
|
-
|
584
|
+
|
374
585
|
end
|
375
|
-
|
586
|
+
|
587
|
+
end
|