aemo 0.1.4 → 0.1.5
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/.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
|