aemo 0.1.39 → 0.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/aemo.rb +4 -0
- data/lib/aemo/dispatchable.rb +2 -0
- data/lib/aemo/market.rb +17 -11
- data/lib/aemo/market/interval.rb +3 -1
- data/lib/aemo/market/node.rb +4 -2
- data/lib/aemo/meter.rb +37 -0
- data/lib/aemo/msats.rb +7 -6
- data/lib/aemo/nem12.rb +40 -40
- data/lib/aemo/nem13.rb +2 -0
- data/lib/aemo/nmi.rb +170 -244
- data/lib/aemo/region.rb +3 -1
- data/lib/aemo/register.rb +42 -0
- data/lib/aemo/version.rb +3 -1
- data/lib/data/TNI-MLF-Codes.csv +644 -564
- data/lib/data/xml_to_json.rb +27 -11
- data/spec/aemo_spec.rb +2 -0
- data/spec/lib/aemo/market/interval_spec.rb +2 -0
- data/spec/lib/aemo/market/node_spec.rb +2 -0
- data/spec/lib/aemo/market_spec.rb +3 -1
- data/spec/lib/aemo/meter_spec.rb +18 -0
- data/spec/lib/aemo/msats_spec.rb +2 -0
- data/spec/lib/aemo/nem12_spec.rb +21 -1
- data/spec/lib/aemo/nmi_spec.rb +4 -1
- data/spec/lib/aemo/region_spec.rb +2 -0
- data/spec/spec_helper.rb +10 -0
- metadata +22 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99c92027fe76a3d8367716e2274157b9239c48ee
|
4
|
+
data.tar.gz: 70d04cf7ed134fc1ac3aab96d8727e043c19104b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ed1e45c8450de68f0adccf41f87ccbe0bb6cf44d87c417826f75b2738ecc8c13480eb7e087b5f5b66dfc9f73bbb83d887ed88008a773b76f3c9effd62dd8cd2
|
7
|
+
data.tar.gz: 8aadfb1e0b590c4527b01fa21cba54f1b651b78e613b834b86fd21716298612647719a23c6010b0e5abc21bdfacb1ea97becded6de357b058f715dba5c2679fb
|
data/lib/aemo.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/all'
|
2
4
|
require 'httparty'
|
3
5
|
require 'csv'
|
@@ -6,9 +8,11 @@ require 'aemo/region.rb'
|
|
6
8
|
require 'aemo/market.rb'
|
7
9
|
require 'aemo/market/interval.rb'
|
8
10
|
require 'aemo/market/node.rb'
|
11
|
+
require 'aemo/meter.rb'
|
9
12
|
require 'aemo/nem12.rb'
|
10
13
|
require 'aemo/nmi.rb'
|
11
14
|
require 'aemo/msats.rb'
|
15
|
+
require 'aemo/register.rb'
|
12
16
|
require 'aemo/version.rb'
|
13
17
|
|
14
18
|
# AEMO Module to encapsulate all AEMO classes
|
data/lib/aemo/dispatchable.rb
CHANGED
data/lib/aemo/market.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AEMO
|
2
4
|
# AEMO::Market
|
3
5
|
#
|
@@ -13,7 +15,7 @@ module AEMO
|
|
13
15
|
# Return the current dispatch dataset for a region
|
14
16
|
#
|
15
17
|
# @param [String, AEMO::Region] region AEMO::Region
|
16
|
-
# @return [Array<AEMO::Market::Interval>]
|
18
|
+
# @return [Array<AEMO::Market::Interval>] the current dispatch data
|
17
19
|
def current_dispatch(region)
|
18
20
|
region = AEMO::Region.new(region) if region.is_a?(String)
|
19
21
|
|
@@ -25,7 +27,7 @@ module AEMO
|
|
25
27
|
# Description of method
|
26
28
|
#
|
27
29
|
# @param [String, AEMO::Region] region AEMO::Region
|
28
|
-
# @return [Array<AEMO::Market::Interval>]
|
30
|
+
# @return [Array<AEMO::Market::Interval>] the current trading data
|
29
31
|
def current_trading(region)
|
30
32
|
region = AEMO::Region.new(region) if region.is_a?(String)
|
31
33
|
|
@@ -44,16 +46,19 @@ module AEMO
|
|
44
46
|
region = AEMO::Region.new(region) if region.is_a?(String)
|
45
47
|
|
46
48
|
required_data = []
|
47
|
-
(start..finish).map { |d| { year: d.year, month: d.month } }
|
48
|
-
|
49
|
+
(start..finish).map { |d| { year: d.year, month: d.month } }
|
50
|
+
.uniq.each do |period|
|
51
|
+
required_data += historic_trading(region, period[:year],
|
52
|
+
period[:month])
|
49
53
|
end
|
50
54
|
|
51
|
-
required_data.select
|
55
|
+
required_data.select do |values|
|
56
|
+
values.datetime >= start && values.datetime <= finish
|
57
|
+
end
|
52
58
|
end
|
53
59
|
|
54
60
|
# Return an array of historic trading values for a Year, Month and Region
|
55
|
-
# As per the historical data
|
56
|
-
# http://www.aemo.com.au/Electricity/Data/Price-and-Demand/Aggregated-Price-and-Demand-Data-Files/Aggregated-Price-and-Demand-2011-to-2016
|
61
|
+
# As per the historical data from AEMO
|
57
62
|
#
|
58
63
|
# @param [String, AEMO::Region] region AEMO::Region
|
59
64
|
# @param [Integer] year The year for the report from AEMO
|
@@ -62,11 +67,12 @@ module AEMO
|
|
62
67
|
def historic_trading(region, year, month)
|
63
68
|
region = AEMO::Region.new(region) if region.is_a?(String)
|
64
69
|
|
65
|
-
month =
|
70
|
+
month = Kernel.format('%02d', month)
|
71
|
+
url = 'https://aemo.com.au/aemo/data/nem/priceanddemand/' \
|
72
|
+
"PRICE_AND_DEMAND_#{year}#{month}_#{region}1.csv"
|
66
73
|
|
67
|
-
response = HTTParty.get(
|
68
|
-
|
69
|
-
values
|
74
|
+
response = HTTParty.get(url)
|
75
|
+
parse_response(response)
|
70
76
|
end
|
71
77
|
|
72
78
|
protected
|
data/lib/aemo/market/interval.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AEMO
|
2
4
|
module Market
|
3
5
|
# AEMO::Market::Interval
|
@@ -14,7 +16,7 @@ module AEMO
|
|
14
16
|
attr_accessor :datetime, :region, :total_demand, :rrp, :period_type
|
15
17
|
|
16
18
|
# Create a new instance of an Interval
|
17
|
-
#
|
19
|
+
#
|
18
20
|
# @param [Time] datetime
|
19
21
|
# @param [Hash] options Hash of optional data values
|
20
22
|
# @return [AEMO::Market::Interval]
|
data/lib/aemo/market/node.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AEMO
|
2
4
|
module Market
|
3
5
|
# AEMO::Market::Interval
|
@@ -6,7 +8,7 @@ module AEMO
|
|
6
8
|
# @abstract
|
7
9
|
# @since 0.1.0
|
8
10
|
class Node
|
9
|
-
IDENTIFIERS = %w
|
11
|
+
IDENTIFIERS = %w[NSW QLD SA TAS VIC].freeze
|
10
12
|
|
11
13
|
attr_accessor :identifier
|
12
14
|
|
@@ -31,7 +33,7 @@ module AEMO
|
|
31
33
|
#
|
32
34
|
# @return [Array<AEMO::Market::Interval>]
|
33
35
|
def current_trading
|
34
|
-
if @current_trading.empty? || @current_trading.
|
36
|
+
if @current_trading.empty? || @current_trading.select { |i| i.period_type == 'TRADE' }.last.datetime != (Time.now - Time.now.to_i % 300)
|
35
37
|
@current_trading = AEMO::Market.current_trading(@identifier)
|
36
38
|
end
|
37
39
|
@current_trading
|
data/lib/aemo/meter.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AEMO
|
4
|
+
# AEMO::Meter represents a meter under a NMI
|
5
|
+
class Meter
|
6
|
+
attr_accessor :installation_type_code, :next_scheduled_read_date,
|
7
|
+
:read_type_code, :registers, :serial_number, :status
|
8
|
+
|
9
|
+
# Initialize a meter
|
10
|
+
#
|
11
|
+
# @param [Hash] opts = {} the parameters to set
|
12
|
+
# @return [AEMO::Meter] an instance of an AEMO::Meter
|
13
|
+
def initialize(opts = {})
|
14
|
+
@installation_type_code = opts[:installation_type_code]
|
15
|
+
@next_scheduled_read_date = opts[:next_scheduled_read_date]
|
16
|
+
@read_type_code = opts[:read_type_code]
|
17
|
+
@registers = opts[:registers] || []
|
18
|
+
@serial_number = opts[:serial_number]
|
19
|
+
@status = opts[:status]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Initialize a new meter from an MSATS hash
|
23
|
+
#
|
24
|
+
# @param [Hash] meter the MSATS hash
|
25
|
+
# @return [AEMO::Meter] description of returned object
|
26
|
+
def self.from_hash(meter)
|
27
|
+
AEMO::Meter.new(
|
28
|
+
installation_type_code: meter['InstallationTypeCode'],
|
29
|
+
next_scheduled_read_date: meter['NextScheduledReadDate'],
|
30
|
+
read_type_code: meter['ReadTypeCode'],
|
31
|
+
registers: [],
|
32
|
+
serial_number: meter['SerialNumber'],
|
33
|
+
status: meter['Status']
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/aemo/msats.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aemo'
|
3
|
-
# require 'zip'
|
4
|
-
require 'nokogiri'
|
5
4
|
require 'digest/sha1'
|
5
|
+
require 'httparty'
|
6
|
+
require 'nokogiri'
|
6
7
|
|
7
8
|
module AEMO
|
8
9
|
# AEMO::MSATS
|
@@ -93,7 +94,7 @@ module AEMO
|
|
93
94
|
# @param [Integer] delivery_point_identifier Delivery Point Identifier
|
94
95
|
# @return [Hash] The response
|
95
96
|
def nmi_discovery_by_delivery_point_identifier(jurisdiction_code, delivery_point_identifier, options = {})
|
96
|
-
raise ArgumentError, 'jurisdiction_code is not valid' unless %w
|
97
|
+
raise ArgumentError, 'jurisdiction_code is not valid' unless %w[ACT NEM NSW QLD SA VIC TAS].include?(jurisdiction_code)
|
97
98
|
raise ArgumentError, 'delivery_point_identifier is not valid' unless delivery_point_identifier.respond_to?('to_i')
|
98
99
|
raise ArgumentError, 'delivery_point_identifier is not valid' if delivery_point_identifier.to_i < 10_000_000 || delivery_point_identifier.to_i > 99_999_999
|
99
100
|
|
@@ -117,7 +118,7 @@ module AEMO
|
|
117
118
|
# @param [Integer] meter_serial_number The meter's serial number
|
118
119
|
# @return [Hash] The response
|
119
120
|
def nmi_discovery_by_meter_serial_number(jurisdiction_code, meter_serial_number, options = {})
|
120
|
-
raise ArgumentError, 'jurisdiction_code is not valid' unless %w
|
121
|
+
raise ArgumentError, 'jurisdiction_code is not valid' unless %w[ACT NEM NSW QLD SA VIC TAS].include?(jurisdiction_code)
|
121
122
|
|
122
123
|
query = {
|
123
124
|
transactionId: transaction_id,
|
@@ -139,7 +140,7 @@ module AEMO
|
|
139
140
|
# @param [Integer] meter_serial_number The meter's serial number
|
140
141
|
# @return [Hash] The response
|
141
142
|
def nmi_discovery_by_address(jurisdiction_code, options = {})
|
142
|
-
raise ArgumentError, 'jurisdiction_code is not valid' unless %w
|
143
|
+
raise ArgumentError, 'jurisdiction_code is not valid' unless %w[ACT NEM NSW QLD SA VIC TAS].include?(jurisdiction_code)
|
143
144
|
|
144
145
|
options[:building_or_property_name] ||= nil
|
145
146
|
options[:location_descriptor] ||= nil
|
data/lib/aemo/nem12.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'csv'
|
2
4
|
require 'time'
|
3
5
|
|
@@ -92,36 +94,36 @@ module AEMO
|
|
92
94
|
}.freeze
|
93
95
|
|
94
96
|
METHOD_FLAGS = {
|
95
|
-
11 => { type: %w
|
96
|
-
12 => { type: %w
|
97
|
-
13 => { type: %w
|
98
|
-
14 => { type: %w
|
99
|
-
15 => { type: %w
|
100
|
-
16 => { type: %w
|
101
|
-
17 => { type: %w
|
102
|
-
18 => { type: %w
|
103
|
-
19 => { type: %w
|
104
|
-
51 => { type: %w
|
105
|
-
52 => { type: %w
|
106
|
-
53 => { type: %w
|
107
|
-
54 => { type: %w
|
108
|
-
55 => { type: %w
|
109
|
-
56 => { type: %w
|
110
|
-
57 => { type: %w
|
111
|
-
58 => { type: %w
|
112
|
-
61 => { type: %w
|
113
|
-
62 => { type: %w
|
114
|
-
63 => { type: %w
|
115
|
-
64 => { type: %w
|
116
|
-
65 => { type: %w
|
117
|
-
66 => { type: %w
|
118
|
-
67 => { type: %w
|
119
|
-
68 => { type: %w
|
120
|
-
71 => { type: %w
|
121
|
-
72 => { type: %w
|
122
|
-
73 => { type: %w
|
123
|
-
74 => { type: %w
|
124
|
-
75 => { type: %w
|
97
|
+
11 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Check', description: '' },
|
98
|
+
12 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Calculated', description: '' },
|
99
|
+
13 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'SCADA', description: '' },
|
100
|
+
14 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Like Day', description: '' },
|
101
|
+
15 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Average Like Day', description: '' },
|
102
|
+
16 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Agreed', description: '' },
|
103
|
+
17 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Linear', description: '' },
|
104
|
+
18 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Alternate', description: '' },
|
105
|
+
19 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Zero', description: '' },
|
106
|
+
51 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Previous Year', description: '' },
|
107
|
+
52 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Previous Read', description: '' },
|
108
|
+
53 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Revision', description: '' },
|
109
|
+
54 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Linear', description: '' },
|
110
|
+
55 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Agreed', description: '' },
|
111
|
+
56 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Prior to First Read - Agreed', description: '' },
|
112
|
+
57 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Customer Class', description: '' },
|
113
|
+
58 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Zero', description: '' },
|
114
|
+
61 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Previous Year', description: '' },
|
115
|
+
62 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Previous Read', description: '' },
|
116
|
+
63 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Customer Class', description: '' },
|
117
|
+
64 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Agreed', description: '' },
|
118
|
+
65 => { type: %w[EST], installation_type: 6, short_descriptor: 'ADL', description: '' },
|
119
|
+
66 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Revision', description: '' },
|
120
|
+
67 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Customer Read', description: '' },
|
121
|
+
68 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Zero', description: '' },
|
122
|
+
71 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Recalculation', description: '' },
|
123
|
+
72 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Revised Table', description: '' },
|
124
|
+
73 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Revised Algorithm', description: '' },
|
125
|
+
74 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Agreed', description: '' },
|
126
|
+
75 => { type: %w[EST], installation_type: 7, short_descriptor: 'Existing Table', description: '' }
|
125
127
|
}.freeze
|
126
128
|
|
127
129
|
REASON_CODES = {
|
@@ -332,7 +334,7 @@ module AEMO
|
|
332
334
|
end
|
333
335
|
raise ArgumentError, 'UOM is not valid' if csv[7].upcase.match(/[A-Z0-9]{2}/).nil?
|
334
336
|
raise ArgumentError, 'UOM is not valid' unless UOM.keys.map(&:upcase).include?(csv[7].upcase)
|
335
|
-
raise ArgumentError, 'IntervalLength is not valid' unless %w
|
337
|
+
raise ArgumentError, 'IntervalLength is not valid' unless %w[1 5 10 15 30].include?(csv[8])
|
336
338
|
# raise ArgumentError, 'NextScheduledReadDate is not valid' if csv[9].match(/\d{8}/).nil? || csv[9] != Time.parse('#{csv[9]}').strftime('%Y%m%d')
|
337
339
|
|
338
340
|
@nmi = AEMO::NMI.new(csv[1])
|
@@ -369,14 +371,14 @@ module AEMO
|
|
369
371
|
raise ArgumentError, 'QualityMethod is not valid' unless csv[intervals_offset + 0].class == String
|
370
372
|
raise ArgumentError, 'QualityMethod does not have valid length' unless [1, 3].include?(csv[intervals_offset + 0].length)
|
371
373
|
raise ArgumentError, 'QualityMethod does not have valid QualityFlag' unless QUALITY_FLAGS.keys.include?(csv[intervals_offset + 0][0])
|
372
|
-
unless %w
|
374
|
+
unless %w[A N V].include?(csv[intervals_offset + 0][0])
|
373
375
|
raise ArgumentError, 'QualityMethod does not have valid length' unless csv[intervals_offset + 0].length == 3
|
374
376
|
raise ArgumentError, 'QualityMethod does not have valid MethodFlag' unless METHOD_FLAGS.keys.include?(csv[intervals_offset + 0][1..2].to_i)
|
375
377
|
end
|
376
|
-
unless %w
|
378
|
+
unless %w[A N E].include?(csv[intervals_offset + 0][0])
|
377
379
|
raise ArgumentError, 'ReasonCode is not valid' unless REASON_CODES.keys.include?(csv[intervals_offset + 1].to_i)
|
378
380
|
end
|
379
|
-
if !csv[intervals_offset + 1].nil? && csv[intervals_offset + 1].to_i
|
381
|
+
if !csv[intervals_offset + 1].nil? && csv[intervals_offset + 1].to_i.zero?
|
380
382
|
raise ArgumentError, 'ReasonDescription is not valid' unless csv[intervals_offset + 2].class == String && !csv[intervals_offset + 2].empty?
|
381
383
|
end
|
382
384
|
if options[:strict]
|
@@ -429,7 +431,7 @@ module AEMO
|
|
429
431
|
interval_events = []
|
430
432
|
|
431
433
|
# Only need to update flags for EFSV
|
432
|
-
unless %w
|
434
|
+
unless %w[A N].include? csv[3]
|
433
435
|
number_of_intervals = 1440 / @data_details.last[:interval_length]
|
434
436
|
interval_start_point = @interval_data.length - number_of_intervals
|
435
437
|
|
@@ -460,13 +462,11 @@ module AEMO
|
|
460
462
|
|
461
463
|
# @param [String] line A single line in string format
|
462
464
|
# @return [Hash] the line parsed into a hash of information
|
463
|
-
def parse_nem12_500(_line, _options = {})
|
464
|
-
end
|
465
|
+
def parse_nem12_500(_line, _options = {}); end
|
465
466
|
|
466
467
|
# @param [String] line A single line in string format
|
467
468
|
# @return [Hash] the line parsed into a hash of information
|
468
|
-
def parse_nem12_900(_line, _options = {})
|
469
|
-
end
|
469
|
+
def parse_nem12_900(_line, _options = {}); end
|
470
470
|
|
471
471
|
# Turns the flag to a string
|
472
472
|
#
|
@@ -498,7 +498,7 @@ module AEMO
|
|
498
498
|
|
499
499
|
# @return [Array] CSV of a NEM12 file a given Meter + Data Stream for easy reading
|
500
500
|
def to_csv
|
501
|
-
headers = %w
|
501
|
+
headers = %w[nmi suffix units datetime value flags]
|
502
502
|
([headers] + to_a.map do |row|
|
503
503
|
row[3] = row[3].strftime('%Y%m%d%H%M%S%z')
|
504
504
|
row
|
data/lib/aemo/nem13.rb
CHANGED
data/lib/aemo/nmi.rb
CHANGED
@@ -1,412 +1,328 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'csv'
|
2
4
|
require 'json'
|
3
5
|
require 'time'
|
4
6
|
require 'ostruct'
|
5
7
|
module AEMO
|
6
|
-
# AEMO::NMI acts as an object to simplify access to data and information
|
8
|
+
# AEMO::NMI acts as an object to simplify access to data and information
|
9
|
+
# about a NMI and provide verification of the NMI value
|
7
10
|
class NMI
|
8
11
|
# Operational Regions for the NMI
|
9
|
-
REGIONS = {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
'NT' => 'Northern Territory'
|
18
|
-
}.freeze
|
12
|
+
REGIONS = { 'ACT' => 'Australian Capital Territory',
|
13
|
+
'NSW' => 'New South Wales',
|
14
|
+
'QLD' => 'Queensland',
|
15
|
+
'SA' => 'South Australia',
|
16
|
+
'TAS' => 'Tasmania',
|
17
|
+
'VIC' => 'Victoria',
|
18
|
+
'WA' => 'Western Australia',
|
19
|
+
'NT' => 'Northern Territory' }.freeze
|
19
20
|
|
20
|
-
# NMI_ALLOCATIONS as per AEMO Documentation at
|
21
|
-
#
|
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
|
22
25
|
NMI_ALLOCATIONS = {
|
23
26
|
'ACTEWP' => {
|
24
27
|
title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
|
25
28
|
friendly_title: 'ACTEWAgl',
|
26
29
|
state: AEMO::Region.new('ACT'),
|
27
30
|
type: 'electricity',
|
28
|
-
includes: [
|
29
|
-
|
30
|
-
|
31
|
-
],
|
32
|
-
excludes: [
|
33
|
-
]
|
31
|
+
includes: [/^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
32
|
+
/^(7001\d{6})$/],
|
33
|
+
excludes: []
|
34
34
|
},
|
35
35
|
'CNRGYP' => {
|
36
36
|
title: 'Essential Energy',
|
37
37
|
friendly_title: 'Essential Energy',
|
38
38
|
state: AEMO::Region.new('NSW'),
|
39
39
|
type: 'electricity',
|
40
|
-
includes: [
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
],
|
50
|
-
excludes: [
|
51
|
-
]
|
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: []
|
52
49
|
},
|
53
50
|
'ENERGYAP' => {
|
54
51
|
title: 'Ausgrid',
|
55
52
|
friendly_title: 'Ausgrid',
|
56
53
|
state: AEMO::Region.new('NSW'),
|
57
54
|
type: 'electricity',
|
58
|
-
includes: [
|
59
|
-
|
60
|
-
|
61
|
-
],
|
62
|
-
excludes: [
|
63
|
-
]
|
55
|
+
includes: [/^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
56
|
+
/^(410[234]\d{6})$/],
|
57
|
+
excludes: []
|
64
58
|
},
|
65
59
|
'INTEGP' => {
|
66
60
|
title: 'Endeavour Energy',
|
67
61
|
friendly_title: 'Endeavour Energy',
|
68
62
|
state: AEMO::Region.new('NSW'),
|
69
63
|
type: 'electricity',
|
70
|
-
includes: [
|
71
|
-
|
72
|
-
|
73
|
-
],
|
74
|
-
excludes: [
|
75
|
-
]
|
64
|
+
includes: [/^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
65
|
+
/^(431\d{7})$/],
|
66
|
+
excludes: []
|
76
67
|
},
|
77
68
|
'TRANSGP' => {
|
78
69
|
title: 'TransGrid',
|
79
70
|
friendly_title: 'TransGrid',
|
80
71
|
state: AEMO::Region.new('NSW'),
|
81
72
|
type: 'electricity',
|
82
|
-
includes: [
|
83
|
-
|
84
|
-
|
85
|
-
],
|
86
|
-
excludes: [
|
87
|
-
]
|
73
|
+
includes: [/^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
74
|
+
/^(460810[0-8]\d{3})$/],
|
75
|
+
excludes: []
|
88
76
|
},
|
89
77
|
'SNOWY' => {
|
90
78
|
title: 'Snowy Hydro Ltd',
|
91
79
|
friendly_title: 'Snowy Hydro',
|
92
80
|
state: AEMO::Region.new('NSW'),
|
93
81
|
type: 'electricity',
|
94
|
-
includes: [
|
95
|
-
|
96
|
-
],
|
97
|
-
excludes: [
|
98
|
-
]
|
82
|
+
includes: [/^(4708109\d{3})$/],
|
83
|
+
excludes: []
|
99
84
|
},
|
100
85
|
'NT_RESERVED' => {
|
101
86
|
title: 'Northern Territory Reserved Block',
|
102
87
|
friendly_title: 'Northern Territory Reserved Block',
|
103
88
|
state: AEMO::Region.new('NT'),
|
104
89
|
type: 'electricity',
|
105
|
-
includes: [
|
106
|
-
|
107
|
-
],
|
108
|
-
excludes: [
|
109
|
-
]
|
90
|
+
includes: [/^(250\d{7})$/],
|
91
|
+
excludes: []
|
110
92
|
},
|
111
93
|
'ERGONETP' => {
|
112
94
|
title: 'Ergon Energy Corporation',
|
113
95
|
friendly_title: 'Ergon Energy',
|
114
96
|
state: AEMO::Region.new('QLD'),
|
115
97
|
type: 'electricity',
|
116
|
-
includes: [
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
],
|
125
|
-
excludes: [
|
126
|
-
]
|
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: []
|
127
106
|
},
|
128
107
|
'ENERGEXP' => {
|
129
108
|
title: 'ENERGEX Limited',
|
130
109
|
friendly_title: 'Energex',
|
131
110
|
state: AEMO::Region.new('QLD'),
|
132
111
|
type: 'electricity',
|
133
|
-
includes: [
|
134
|
-
|
135
|
-
|
136
|
-
],
|
137
|
-
excludes: [
|
138
|
-
]
|
112
|
+
includes: [/^(QB\d{2}[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
113
|
+
/^(31\d{8})$/],
|
114
|
+
excludes: []
|
139
115
|
},
|
140
116
|
'PLINKP' => {
|
141
117
|
title: 'Qld Electricity Transmission Corp (Powerlink)',
|
142
118
|
friendly_title: 'Powerlink',
|
143
119
|
state: AEMO::Region.new('QLD'),
|
144
120
|
type: 'electricity',
|
145
|
-
includes: [
|
146
|
-
|
147
|
-
|
148
|
-
],
|
149
|
-
excludes: [
|
150
|
-
]
|
121
|
+
includes: [/^(Q[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
|
122
|
+
/^(320200\d{4})$/],
|
123
|
+
excludes: []
|
151
124
|
},
|
152
125
|
'UMPLP' => {
|
153
126
|
title: 'SA Power Networks',
|
154
127
|
friendly_title: 'SA Power Networks',
|
155
128
|
state: AEMO::Region.new('SA'),
|
156
129
|
type: 'electricity',
|
157
|
-
includes: [
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
],
|
162
|
-
excludes: [
|
163
|
-
]
|
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: []
|
164
134
|
},
|
165
135
|
'ETSATP' => {
|
166
136
|
title: 'ElectraNet SA',
|
167
137
|
friendly_title: 'ElectraNet SA',
|
168
138
|
state: AEMO::Region.new('SA'),
|
169
139
|
type: 'electricity',
|
170
|
-
includes: [
|
171
|
-
|
172
|
-
|
173
|
-
],
|
174
|
-
excludes: [
|
175
|
-
]
|
140
|
+
includes: [/^(S[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
|
141
|
+
/^(210200\d{4})$/],
|
142
|
+
excludes: []
|
176
143
|
},
|
177
144
|
'AURORAP' => {
|
178
145
|
title: 'TasNetworks',
|
179
146
|
friendly_title: 'TasNetworks',
|
180
147
|
state: AEMO::Region.new('TAS'),
|
181
148
|
type: 'electricity',
|
182
|
-
includes: [
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
],
|
187
|
-
excludes: [
|
188
|
-
]
|
149
|
+
includes: [/^(T000000(([0-4]\d{3})|(500[01])))$/,
|
150
|
+
/^(8000\d{6})$/,
|
151
|
+
/^(8590[23]\d{5})$/],
|
152
|
+
excludes: []
|
189
153
|
},
|
190
154
|
'TRANSEND' => {
|
191
155
|
title: 'TasNetworks',
|
192
156
|
friendly_title: 'TasNetworks',
|
193
157
|
state: AEMO::Region.new('TAS'),
|
194
158
|
type: 'electricity',
|
195
|
-
includes: [
|
196
|
-
|
197
|
-
],
|
198
|
-
excludes: [
|
199
|
-
]
|
159
|
+
includes: [/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/],
|
160
|
+
excludes: []
|
200
161
|
},
|
201
162
|
'CITIPP' => {
|
202
163
|
title: 'CitiPower',
|
203
164
|
friendly_title: 'CitiPower',
|
204
165
|
state: AEMO::Region.new('VIC'),
|
205
166
|
type: 'electricity',
|
206
|
-
includes: [
|
207
|
-
|
208
|
-
|
209
|
-
],
|
210
|
-
excludes: [
|
211
|
-
]
|
167
|
+
includes: [/^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
168
|
+
/^(610[23]\d{6})$/],
|
169
|
+
excludes: []
|
212
170
|
},
|
213
171
|
'EASTERN' => {
|
214
172
|
title: 'SP AusNet',
|
215
173
|
friendly_title: 'SP AusNet DNSP',
|
216
174
|
state: AEMO::Region.new('VIC'),
|
217
175
|
type: 'electricity',
|
218
|
-
includes: [
|
219
|
-
|
220
|
-
|
221
|
-
],
|
222
|
-
excludes: [
|
223
|
-
]
|
176
|
+
includes: [/^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
177
|
+
/^(630[56]\d{6})$/],
|
178
|
+
excludes: []
|
224
179
|
},
|
225
180
|
'POWCP' => {
|
226
181
|
title: 'PowerCor Australia',
|
227
182
|
friendly_title: 'PowerCor',
|
228
183
|
state: AEMO::Region.new('VIC'),
|
229
184
|
type: 'electricity',
|
230
|
-
includes: [
|
231
|
-
|
232
|
-
|
233
|
-
],
|
234
|
-
excludes: [
|
235
|
-
]
|
185
|
+
includes: [/^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
186
|
+
/^(620[34]\d{6})$/],
|
187
|
+
excludes: []
|
236
188
|
},
|
237
189
|
'SOLARISP' => {
|
238
190
|
title: 'Jemena Electricity Networks (VIC)',
|
239
191
|
friendly_title: 'Jemena',
|
240
192
|
state: AEMO::Region.new('VIC'),
|
241
193
|
type: 'electricity',
|
242
|
-
includes: [
|
243
|
-
|
244
|
-
|
245
|
-
],
|
246
|
-
excludes: [
|
247
|
-
]
|
194
|
+
includes: [/^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
195
|
+
/^(6001\d{6})$/],
|
196
|
+
excludes: []
|
248
197
|
},
|
249
198
|
'UNITED' => {
|
250
199
|
title: 'United Energy Distribution',
|
251
200
|
friendly_title: 'United Energy',
|
252
201
|
state: AEMO::Region.new('VIC'),
|
253
202
|
type: 'electricity',
|
254
|
-
includes: [
|
255
|
-
|
256
|
-
|
257
|
-
],
|
258
|
-
excludes: [
|
259
|
-
]
|
203
|
+
includes: [/^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
204
|
+
/^(640[78]\d{6})$/],
|
205
|
+
excludes: []
|
260
206
|
},
|
261
207
|
'GPUPP' => {
|
262
208
|
title: 'SP AusNet TNSP',
|
263
209
|
friendly_title: 'SP AusNet TNSP',
|
264
210
|
state: AEMO::Region.new('VIC'),
|
265
211
|
type: 'electricity',
|
266
|
-
includes: [
|
267
|
-
|
268
|
-
|
269
|
-
],
|
270
|
-
excludes: [
|
271
|
-
]
|
212
|
+
includes: [/^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
|
213
|
+
/^(650900\d{4})$/],
|
214
|
+
excludes: []
|
272
215
|
},
|
273
216
|
'WESTERNPOWER' => {
|
274
217
|
title: 'Western Power',
|
275
218
|
friendly_title: 'Western Power',
|
276
219
|
state: AEMO::Region.new('WA'),
|
277
220
|
type: 'electricity',
|
278
|
-
includes: [
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
],
|
284
|
-
excludes: [
|
285
|
-
]
|
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: []
|
286
226
|
},
|
287
227
|
'HORIZONPOWER' => {
|
288
228
|
title: 'Horizon Power',
|
289
229
|
friendly_title: 'Horizon Power',
|
290
230
|
state: AEMO::Region.new('WA'),
|
291
231
|
type: 'electricity',
|
292
|
-
includes: [
|
293
|
-
|
294
|
-
],
|
295
|
-
excludes: [
|
296
|
-
]
|
232
|
+
includes: [/^(8021\d{6})$/],
|
233
|
+
excludes: []
|
297
234
|
},
|
298
235
|
'GAS_NSW' => {
|
299
236
|
title: 'GAS NSW',
|
300
237
|
friendly_title: 'GAS NSW',
|
301
238
|
state: AEMO::Region.new('NSW'),
|
302
239
|
type: 'gas',
|
303
|
-
includes: [
|
304
|
-
|
305
|
-
],
|
306
|
-
excludes: [
|
307
|
-
]
|
240
|
+
includes: [/^(52\d{8})$/],
|
241
|
+
excludes: []
|
308
242
|
},
|
309
243
|
'GAS_VIC' => {
|
310
244
|
title: 'GAS VIC',
|
311
245
|
friendly_title: 'GAS VIC',
|
312
246
|
state: AEMO::Region.new('VIC'),
|
313
247
|
type: 'gas',
|
314
|
-
includes: [
|
315
|
-
|
316
|
-
],
|
317
|
-
excludes: [
|
318
|
-
]
|
248
|
+
includes: [/^(53\d{8})$/],
|
249
|
+
excludes: []
|
319
250
|
},
|
320
251
|
'GAS_QLD' => {
|
321
252
|
title: 'GAS QLD',
|
322
253
|
friendly_title: 'GAS QLD',
|
323
254
|
state: AEMO::Region.new('QLD'),
|
324
255
|
type: 'gas',
|
325
|
-
includes: [
|
326
|
-
|
327
|
-
],
|
328
|
-
excludes: [
|
329
|
-
]
|
256
|
+
includes: [/^(54\d{8})$/],
|
257
|
+
excludes: []
|
330
258
|
},
|
331
259
|
'GAS_SA' => {
|
332
260
|
title: 'GAS SA',
|
333
261
|
friendly_title: 'GAS SA',
|
334
262
|
state: AEMO::Region.new('SA'),
|
335
263
|
type: 'gas',
|
336
|
-
includes: [
|
337
|
-
|
338
|
-
],
|
339
|
-
excludes: [
|
340
|
-
]
|
264
|
+
includes: [/^(55\d{8})$/],
|
265
|
+
excludes: []
|
341
266
|
},
|
342
267
|
'GAS_WA' => {
|
343
268
|
title: 'GAS WA',
|
344
269
|
friendly_title: 'GAS WA',
|
345
270
|
state: AEMO::Region.new('WA'),
|
346
271
|
type: 'gas',
|
347
|
-
includes: [
|
348
|
-
|
349
|
-
],
|
350
|
-
excludes: [
|
351
|
-
]
|
272
|
+
includes: [/^(56\d{8})$/],
|
273
|
+
excludes: []
|
352
274
|
},
|
353
275
|
'GAS_TAS' => {
|
354
276
|
title: 'GAS TAS',
|
355
277
|
friendly_title: 'GAS TAS',
|
356
278
|
state: AEMO::Region.new('TAS'),
|
357
279
|
type: 'gas',
|
358
|
-
includes: [
|
359
|
-
|
360
|
-
],
|
361
|
-
excludes: [
|
362
|
-
]
|
280
|
+
includes: [/^(57\d{8})$/],
|
281
|
+
excludes: []
|
363
282
|
},
|
364
283
|
'FEDAIRPORTS' => {
|
365
284
|
title: 'Federal Airports Corporation (Sydney Airport)',
|
366
285
|
friendly_title: 'Sydney Airport',
|
367
286
|
state: AEMO::Region.new('NSW'),
|
368
287
|
type: 'electricity',
|
369
|
-
includes: [
|
370
|
-
|
371
|
-
],
|
372
|
-
excludes: [
|
373
|
-
]
|
288
|
+
includes: [/^(NJJJNR[A-HJ-NP-Z\d]{4})$/],
|
289
|
+
excludes: []
|
374
290
|
},
|
375
291
|
'EXEMPTNETWORKS' => {
|
376
292
|
title: 'Exempt Networks - various',
|
377
293
|
friendly_title: 'Exempt Networks - various',
|
378
294
|
state: '',
|
379
295
|
type: 'electricity',
|
380
|
-
includes: [
|
381
|
-
|
382
|
-
|
383
|
-
],
|
384
|
-
excludes: [
|
385
|
-
]
|
296
|
+
includes: [/^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
|
297
|
+
/^(7102\d{6})$/],
|
298
|
+
excludes: []
|
386
299
|
},
|
387
300
|
'AEMORESERVED' => {
|
388
301
|
title: 'AEMO Reserved',
|
389
302
|
friendly_title: 'AEMO Reserved',
|
390
303
|
state: '',
|
391
304
|
type: 'electricity',
|
392
|
-
includes: [
|
393
|
-
|
394
|
-
|
395
|
-
],
|
396
|
-
excludes: [
|
397
|
-
]
|
305
|
+
includes: [/^(880[1-5]\d{6})$/,
|
306
|
+
/^(9\d{9})$/],
|
307
|
+
excludes: []
|
398
308
|
}
|
399
309
|
}.freeze
|
400
310
|
# Transmission Node Identifier Codes are loaded from a json file
|
401
311
|
# Obtained from http://www.nemweb.com.au/
|
402
312
|
#
|
403
313
|
# See /lib/data for further data manipulation required
|
404
|
-
TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..',
|
314
|
+
TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..',
|
315
|
+
'data', 'aemo-tni.json'))).freeze
|
405
316
|
# Distribution Loss Factor Codes are loaded from a json file
|
406
|
-
# Obtained from MSATS, matching to DNSP from file
|
407
|
-
#
|
317
|
+
# Obtained from MSATS, matching to DNSP from file
|
318
|
+
# https://www.aemo.com.au/-/media/Files/Electricity/NEM/
|
319
|
+
# Security_and_Reliability/Loss_Factors_and_Regional_Boundaries/
|
320
|
+
# 2016/DLF_V3_2016_2017.pdf
|
321
|
+
#
|
322
|
+
# Last accessed 2017-08-01
|
408
323
|
# See /lib/data for further data manipulation required
|
409
|
-
DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..',
|
324
|
+
DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..',
|
325
|
+
'data', 'aemo-dlf.json'))).freeze
|
410
326
|
|
411
327
|
# [String] National Meter Identifier
|
412
328
|
@nmi = nil
|
@@ -423,18 +339,24 @@ module AEMO
|
|
423
339
|
@roles = nil
|
424
340
|
@data_streams = nil
|
425
341
|
|
426
|
-
attr_accessor :nmi, :msats_detail, :tni, :dlf,
|
342
|
+
attr_accessor :nmi, :msats_detail, :tni, :dlf,
|
343
|
+
:customer_classification_code, :customer_threshold_code,
|
344
|
+
:jurisdiction_code, :classification_code, :status, :address,
|
345
|
+
:meters, :roles, :data_streams
|
427
346
|
|
428
347
|
# Initialize a NMI file
|
429
348
|
#
|
430
349
|
# @param [String] nmi the National Meter Identifier (NMI)
|
431
350
|
# @param [Hash] options a hash of options
|
432
|
-
# @option options [Hash] :msats_detail MSATS details as per
|
351
|
+
# @option options [Hash] :msats_detail MSATS details as per
|
352
|
+
# #parse_msats_detail requirements
|
433
353
|
# @return [AEMO::NMI] an instance of AEMO::NMI is returned
|
434
354
|
def initialize(nmi, options = {})
|
435
355
|
raise ArgumentError, 'NMI is not a string' unless nmi.is_a?(String)
|
436
356
|
raise ArgumentError, 'NMI is not 10 characters' unless nmi.length == 10
|
437
|
-
|
357
|
+
unless AEMO::NMI.valid_nmi?(nmi)
|
358
|
+
raise ArgumentError, 'NMI is not constructed with valid characters'
|
359
|
+
end
|
438
360
|
|
439
361
|
@nmi = nmi
|
440
362
|
@meters = []
|
@@ -459,17 +381,21 @@ module AEMO
|
|
459
381
|
AEMO::NMI.network(@nmi)
|
460
382
|
end
|
461
383
|
|
462
|
-
# A function to calculate the checksum value for a given
|
384
|
+
# A function to calculate the checksum value for a given
|
385
|
+
# National Meter Identifier
|
463
386
|
#
|
464
|
-
# @param [Integer] checksum_value the checksum value to check against the
|
387
|
+
# @param [Integer] checksum_value the checksum value to check against the
|
388
|
+
# current National Meter Identifier's checksum value
|
465
389
|
# @return [Boolean] whether or not the checksum is valid
|
466
390
|
def valid_checksum?(checksum_value)
|
467
391
|
checksum_value == checksum
|
468
392
|
end
|
469
393
|
|
470
|
-
# Checksum is a function to calculate the checksum value for a given
|
394
|
+
# Checksum is a function to calculate the checksum value for a given
|
395
|
+
# National Meter Identifier
|
471
396
|
#
|
472
|
-
# @return [Integer] the checksum value for the current National Meter
|
397
|
+
# @return [Integer] the checksum value for the current National Meter
|
398
|
+
# Identifier
|
473
399
|
def checksum
|
474
400
|
summation = 0
|
475
401
|
@nmi.reverse.split(//).each_index do |i|
|
@@ -486,12 +412,14 @@ module AEMO
|
|
486
412
|
#
|
487
413
|
# @return [Hash] MSATS NMI Detail data
|
488
414
|
def raw_msats_nmi_detail(options = {})
|
489
|
-
|
490
|
-
|
415
|
+
unless AEMO::MSATS.can_authenticate?
|
416
|
+
raise ArgumentError, 'MSATS has no authentication credentials'
|
417
|
+
end
|
491
418
|
AEMO::MSATS.nmi_detail(@nmi, options)
|
492
419
|
end
|
493
420
|
|
494
|
-
# Provided MSATS is configured, uses the raw MSATS data to augment NMI
|
421
|
+
# Provided MSATS is configured, uses the raw MSATS data to augment NMI
|
422
|
+
# information
|
495
423
|
#
|
496
424
|
# @return [self] returns self
|
497
425
|
def update_from_msats!(options = {})
|
@@ -523,27 +451,13 @@ module AEMO
|
|
523
451
|
unless @msats_detail['MeterRegister'].nil?
|
524
452
|
meters = @msats_detail['MeterRegister']['Meter']
|
525
453
|
meters = [meters] if meters.is_a?(Hash)
|
526
|
-
meters.
|
527
|
-
@meters <<
|
528
|
-
status: meter['Status'],
|
529
|
-
installation_type_code: meter['InstallationTypeCode'],
|
530
|
-
next_scheduled_read_date: meter['NextScheduledReadDate'],
|
531
|
-
read_type_code: meter['ReadTypeCode'],
|
532
|
-
registers: [],
|
533
|
-
serial_number: meter['SerialNumber']
|
534
|
-
)
|
454
|
+
meters.reject { |x| x['Status'].nil? }.each do |meter|
|
455
|
+
@meters << AEMO::Meter.from_hash(meter)
|
535
456
|
end
|
536
457
|
meters.select { |x| x['Status'].nil? }.each do |registers|
|
537
458
|
m = @meters.find { |x| x.serial_number == registers['SerialNumber'] }
|
538
|
-
m.registers <<
|
539
|
-
|
540
|
-
dial_format: registers['RegisterConfiguration']['Register']['DialFormat'],
|
541
|
-
multiplier: registers['RegisterConfiguration']['Register']['Multiplier'],
|
542
|
-
network_tariff_code: registers['RegisterConfiguration']['Register']['NetworkTariffCode'],
|
543
|
-
register_id: registers['RegisterConfiguration']['Register']['RegisterID'],
|
544
|
-
status: registers['RegisterConfiguration']['Register']['Status'],
|
545
|
-
time_of_day: registers['RegisterConfiguration']['Register']['TimeOfDay'],
|
546
|
-
unit_of_measure: registers['RegisterConfiguration']['Register']['UnitOfMeasure']
|
459
|
+
m.registers << AEMO::Register.from_hash(
|
460
|
+
registers['RegisterConfiguration']['Register']
|
547
461
|
)
|
548
462
|
end
|
549
463
|
end
|
@@ -588,10 +502,11 @@ module AEMO
|
|
588
502
|
friendly_address
|
589
503
|
end
|
590
504
|
|
591
|
-
# Returns the
|
505
|
+
# Returns the meters for the requested status (C/R)
|
592
506
|
#
|
593
507
|
# @param [String] status the stateus [C|R]
|
594
|
-
# @return [Array<
|
508
|
+
# @return [Array<AEMO::Meter>] Returns an array of AEMO::Meters with the
|
509
|
+
# status provided
|
595
510
|
def meters_by_status(status = 'C')
|
596
511
|
@meters.select { |x| x.status == status.to_s }
|
597
512
|
end
|
@@ -599,7 +514,8 @@ module AEMO
|
|
599
514
|
# Returns the data_stream OpenStructs for the requested status (A/I)
|
600
515
|
#
|
601
516
|
# @param [String] status the stateus [A|I]
|
602
|
-
# @return [Array<OpenStruct>] Returns an array of OpenStructs for the
|
517
|
+
# @return [Array<OpenStruct>] Returns an array of OpenStructs for the
|
518
|
+
# current Meters
|
603
519
|
def data_streams_by_status(status = 'A')
|
604
520
|
@data_streams.select { |x| x.status == status.to_s }
|
605
521
|
end
|
@@ -608,7 +524,8 @@ module AEMO
|
|
608
524
|
#
|
609
525
|
# @return [Integer] the current daily load for the meter in kWh
|
610
526
|
def current_daily_load
|
611
|
-
data_streams_by_status.map { |x| x.averaged_daily_load.to_i }
|
527
|
+
data_streams_by_status.map { |x| x.averaged_daily_load.to_i }
|
528
|
+
.inject(0, :+)
|
612
529
|
end
|
613
530
|
|
614
531
|
# The current annual load in MWh
|
@@ -626,10 +543,12 @@ module AEMO
|
|
626
543
|
((nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?)
|
627
544
|
end
|
628
545
|
|
629
|
-
# A function to calculate the checksum value for a given National Meter
|
546
|
+
# A function to calculate the checksum value for a given National Meter
|
547
|
+
# Identifier
|
630
548
|
#
|
631
549
|
# @param [String] nmi the NMI to check the checksum against
|
632
|
-
# @param [Integer] checksum_value the checksum value to check against the
|
550
|
+
# @param [Integer] checksum_value the checksum value to check against the
|
551
|
+
# current National Meter Identifier's checksum value
|
633
552
|
# @return [Boolean] whether or not the checksum is valid
|
634
553
|
def self.valid_checksum?(nmi, checksum_value)
|
635
554
|
nmi = AEMO::NMI.new(nmi)
|
@@ -655,13 +574,20 @@ module AEMO
|
|
655
574
|
|
656
575
|
# A function to return the distribution loss factor value for a given date
|
657
576
|
#
|
658
|
-
# @param [DateTime, Time] datetime the date for the distribution loss factor
|
577
|
+
# @param [DateTime, Time] datetime the date for the distribution loss factor
|
578
|
+
# value
|
659
579
|
# @return [nil, float] the distribution loss factor value
|
660
580
|
def dlfc_value(datetime = DateTime.now)
|
661
|
-
|
581
|
+
if @dlf.nil?
|
582
|
+
raise 'No DLF set, ensure that you have set the value either via the' \
|
583
|
+
'update_from_msats! function or manually'
|
584
|
+
end
|
662
585
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
663
586
|
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
664
|
-
possible_values = DLF_CODES[@dlf].select
|
587
|
+
possible_values = DLF_CODES[@dlf].select do |x|
|
588
|
+
DateTime.parse(x['FromDate']) <= datetime &&
|
589
|
+
DateTime.parse(x['ToDate']) >= datetime
|
590
|
+
end
|
665
591
|
if possible_values.empty?
|
666
592
|
nil
|
667
593
|
else
|