Class: AEMO::NMI Abstract
- Inherits:
-
Object
- Object
- AEMO::NMI
- Defined in:
- lib/aemo/nmi.rb,
lib/aemo/nmi/allocation.rb
Overview
Model for a National Metering Identifier.
- AEMO::NMI
-
AEMO::NMI acts as an object to simplify access to data and information
about a NMI and provide verification of the NMI value
Defined Under Namespace
Classes: Allocation
Constant Summary collapse
- REGIONS =
Operational Regions for the NMI
{ 'ACT' => 'Australian Capital Territory', 'NSW' => 'New South Wales', 'QLD' => 'Queensland', 'SA' => 'South Australia', 'TAS' => 'Tasmania', 'VIC' => 'Victoria', 'WA' => 'Western Australia', 'NT' => 'Northern Territory' }.freeze
- TNI_CODES =
Transmission Node Identifier Codes are loaded from a json file
Obtained from http://www.nemweb.com.au/ See /lib/data for further data manipulation required
JSON.parse( File.read( File.join(File.dirname(__FILE__), '..', 'data', 'aemo-tni.json') ) ).freeze
- DLF_CODES =
Distribution Loss Factor Codes are loaded from a json file
Obtained from MSATS, matching to DNSP from file
www.aemo.com.au/-/media/Files/Electricity/NEM/
Security_and_Reliability/Loss_Factors_and_Regional_Boundaries/ 2016/DLF_V3_2016_2017.pdf Last accessed 2017-08-01 See /lib/data for further data manipulation required
JSON.parse( File.read( File.join(File.dirname(__FILE__), '..', 'data', 'aemo-dlf.json') ) ).freeze
Instance Attribute Summary collapse
- #address ⇒ Object
- #classification_code ⇒ Object
- #customer_classification_code ⇒ Object
- #customer_threshold_code ⇒ Object
- #data_streams ⇒ Object
- #dlf ⇒ Object
- #jurisdiction_code ⇒ Object
- #meters ⇒ Object
- #msats_detail ⇒ Object
- #nmi ⇒ Object
- #roles ⇒ Object
- #status ⇒ Object
- #tni ⇒ Object
Class Method Summary collapse
-
.allocation ⇒ Object
Find the Network for a given NMI.
-
.network(nmi) ⇒ Object
Find the Network for a given NMI.
-
.valid_checksum?(nmi, checksum_value) ⇒ Boolean
A function to calculate the checksum value for a given National Meter Identifier.
-
.valid_nmi?(nmi) ⇒ Boolean
A function to validate the NMI provided.
Instance Method Summary collapse
-
#checksum ⇒ Integer
Checksum is a function to calculate the checksum value for a given National Meter Identifier.
-
#current_annual_load ⇒ Integer
The current annual load in MWh.
-
#current_daily_load ⇒ Integer
The current daily load in kWh.
-
#data_streams_by_status(status = 'A') ⇒ Array<Struct>
Returns the data_stream Structs for the requested status (A/I).
-
#dlfc_value(datetime = ::Time.now) ⇒ nil, float
A function to return the distribution loss factor value for a given date.
-
#dlfc_values(start = ::Time.now, finish = ::Time.now) ⇒ Array(Hash)
A function to return the distribution loss factor value for a given date.
-
#friendly_address ⇒ String
Returns a nice address from the structured one AEMO sends us.
-
#initialize(nmi, options = {}) ⇒ AEMO::NMI
constructor
Initialize a NMI file.
-
#meters_by_status(status = 'C') ⇒ Array<AEMO::Meter>
Returns the meters for the requested status (C/R).
-
#network ⇒ Object
(also: #allocation)
Find the Network of NMI.
-
#parse_msats_detail ⇒ self
Turns raw MSATS junk into useful things.
-
#raw_msats_nmi_detail(options = {}) ⇒ Hash
Provided MSATS is configured, gets the MSATS data for the NMI.
-
#tni_value(datetime = ::Time.now) ⇒ nil, float
A function to return the transmission node identifier loss factor value for a given date.
-
#tni_values(start = ::Time.now, finish = ::Time.now) ⇒ Array(Hash)
A function to return the transmission node identifier loss factor value for a given date.
-
#update_from_msats!(options = {}) ⇒ self
Provided MSATS is configured, uses the raw MSATS data to augment NMI information.
-
#valid_checksum?(checksum_value) ⇒ Boolean
A function to calculate the checksum value for a given National Meter Identifier.
-
#valid_nmi? ⇒ Boolean
A function to validate the instance’s nmi value.
Constructor Details
#initialize(nmi, options = {}) ⇒ AEMO::NMI
Initialize a NMI file
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/aemo/nmi.rb', line 115 def initialize(nmi, = {}) raise ArgumentError, 'NMI is not a string' unless nmi.is_a?(String) raise ArgumentError, 'NMI is not 10 characters' unless nmi.length == 10 raise ArgumentError, 'NMI is not constructed with valid characters' unless AEMO::NMI.valid_nmi?(nmi) @nmi = nmi @meters = [] @roles = {} @data_streams = [] @msats_detail = [:msats_detail] parse_msats_detail unless @msats_detail.nil? end |
Instance Attribute Details
#address ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def address @address end |
#classification_code ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def classification_code @classification_code end |
#customer_classification_code ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def customer_classification_code @customer_classification_code end |
#customer_threshold_code ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def customer_threshold_code @customer_threshold_code end |
#data_streams ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def data_streams @data_streams end |
#dlf ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def dlf @dlf end |
#jurisdiction_code ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def jurisdiction_code @jurisdiction_code end |
#meters ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def meters @meters end |
#msats_detail ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def msats_detail @msats_detail end |
#nmi ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def nmi @nmi end |
#roles ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def roles @roles end |
#status ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def status @status end |
#tni ⇒ Object
71 72 73 |
# File 'lib/aemo/nmi.rb', line 71 def tni @tni end |
Class Method Details
.allocation ⇒ Object
Find the Network for a given NMI
105 106 107 |
# File 'lib/aemo/nmi.rb', line 105 def network(nmi) AEMO::NMI::Allocation.find_by_nmi(nmi) end |
.network(nmi) ⇒ Object
Find the Network for a given NMI
101 102 103 |
# File 'lib/aemo/nmi.rb', line 101 def network(nmi) AEMO::NMI::Allocation.find_by_nmi(nmi) end |
.valid_checksum?(nmi, checksum_value) ⇒ Boolean
A function to calculate the checksum value for a given National Meter Identifier
92 93 94 95 |
# File 'lib/aemo/nmi.rb', line 92 def valid_checksum?(nmi, checksum_value) nmi = AEMO::NMI.new(nmi) nmi.valid_checksum?(checksum_value) end |
.valid_nmi?(nmi) ⇒ Boolean
A function to validate the NMI provided
81 82 83 |
# File 'lib/aemo/nmi.rb', line 81 def valid_nmi?(nmi) (nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil? end |
Instance Method Details
#checksum ⇒ Integer
Checksum is a function to calculate the checksum value for a given National Meter Identifier
160 161 162 163 164 165 166 167 168 169 |
# File 'lib/aemo/nmi.rb', line 160 def checksum summation = 0 @nmi.reverse.chars.each_index do |i| value = nmi[nmi.length - i - 1].ord value *= 2 if i.even? value = value.to_s.chars.map(&:to_i).reduce(:+) summation += value end (10 - (summation % 10)) % 10 end |
#current_annual_load ⇒ Integer
Use TimeDifference for more accurate annualised load
The current annual load in MWh
294 295 296 |
# File 'lib/aemo/nmi.rb', line 294 def current_annual_load (current_daily_load * 365.2425 / 1000).to_i end |
#current_daily_load ⇒ Integer
The current daily load in kWh
285 286 287 288 |
# File 'lib/aemo/nmi.rb', line 285 def current_daily_load data_streams_by_status.map { |x| x.averaged_daily_load.to_i } .inject(0, :+) end |
#data_streams_by_status(status = 'A') ⇒ Array<Struct>
Returns the data_stream Structs for the requested status (A/I)
278 279 280 |
# File 'lib/aemo/nmi.rb', line 278 def data_streams_by_status(status = 'A') @data_streams.select { |x| x.status == status.to_s } end |
#dlfc_value(datetime = ::Time.now) ⇒ nil, float
A function to return the distribution loss factor value for a given date
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/aemo/nmi.rb', line 303 def dlfc_value(datetime = ::Time.now) if @dlf.nil? raise 'No DLF set, ensure that you have set the value either via the' \ 'update_from_msats! function or manually' end raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf) raise 'Invalid date' unless [DateTime, ::Time].include?(datetime.class) possible_values = DLF_CODES[@dlf].select do |x| ::Time.parse(x['FromDate']) <= datetime && ::Time.parse(x['ToDate']) >= datetime end if possible_values.empty? nil else possible_values.first['Value'].to_f end end |
#dlfc_values(start = ::Time.now, finish = ::Time.now) ⇒ Array(Hash)
A function to return the distribution loss factor value for a given date
327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/aemo/nmi.rb', line 327 def dlfc_values(start = ::Time.now, finish = ::Time.now) if @dlf.nil? raise 'No DLF set, ensure that you have set the value either via the ' \ 'update_from_msats! function or manually' end raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf) raise 'Invalid start' unless [DateTime, ::Time].include?(start.class) raise 'Invalid finish' unless [DateTime, ::Time].include?(finish.class) raise 'start cannot be after finish' if start > finish DLF_CODES[@dlf].reject { |x| start > ::Time.parse(x['ToDate']) || finish < ::Time.parse(x['FromDate']) } .map { |x| { 'start' => x['FromDate'], 'finish' => x['ToDate'], 'value' => x['Value'].to_f } } end |
#friendly_address ⇒ String
Returns a nice address from the structured one AEMO sends us
251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/aemo/nmi.rb', line 251 def friendly_address friendly_address = '' if @address.is_a?(Hash) friendly_address = @address.values.map do |x| if x.is_a?(Hash) x = x.values.map { |y| y.is_a?(Hash) ? y.values.join(' ') : y }.join(' ') end x end.join(', ') end friendly_address end |
#meters_by_status(status = 'C') ⇒ Array<AEMO::Meter>
Returns the meters for the requested status (C/R)
269 270 271 |
# File 'lib/aemo/nmi.rb', line 269 def meters_by_status(status = 'C') @meters.select { |x| x.status == status.to_s } end |
#network ⇒ Object Also known as: allocation
Find the Network of NMI
139 140 141 |
# File 'lib/aemo/nmi.rb', line 139 def network AEMO::NMI.network(@nmi) end |
#parse_msats_detail ⇒ self
Turns raw MSATS junk into useful things
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/aemo/nmi.rb', line 194 def parse_msats_detail # Set the details if there are any unless @msats_detail['MasterData'].nil? @tni = @msats_detail['MasterData']['TransmissionNodeIdentifier'] @dlf = @msats_detail['MasterData']['DistributionLossFactorCode'] @customer_classification_code = @msats_detail['MasterData']['CustomerClassificationCode'] @customer_threshold_code = @msats_detail['MasterData']['CustomerThresholdCode'] @jurisdiction_code = @msats_detail['MasterData']['JurisdictionCode'] @classification_code = @msats_detail['MasterData']['NMIClassificationCode'] @status = @msats_detail['MasterData']['Status'] @address = @msats_detail['MasterData']['Address'] end @meters ||= [] @roles ||= {} @data_streams ||= [] # Meters unless @msats_detail['MeterRegister'].nil? meters = @msats_detail['MeterRegister']['Meter'] meters = [meters] if meters.is_a?(Hash) meters.reject { |x| x['Status'].nil? }.each do |meter| @meters << AEMO::Meter.from_hash(meter) end meters.select { |x| x['Status'].nil? }.each do |registers| m = @meters.find { |x| x.serial_number == registers['SerialNumber'] } m.registers << AEMO::Register.from_hash( registers['RegisterConfiguration']['Register'] ) end end # Roles unless @msats_detail['RoleAssignments'].nil? role_assignments = @msats_detail['RoleAssignments']['RoleAssignment'] role_assignments = [role_assignments] if role_assignments.is_a?(Hash) role_assignments.each do |role| @roles[role['Role']] = role['Party'] end end # DataStreams unless @msats_detail['DataStreams'].nil? data_streams = @msats_detail['DataStreams']['DataStream'] data_streams = [data_streams] if data_streams.is_a?(Hash) # Deal with issue of only one existing data_streams.each do |stream| @data_streams << Struct::DataStream.new( suffix: stream['Suffix'], profile_name: stream['ProfileName'], averaged_daily_load: stream['AveragedDailyLoad'], data_stream_type: stream['DataStreamType'], status: stream['Status'] ) end end self end |
#raw_msats_nmi_detail(options = {}) ⇒ Hash
Provided MSATS is configured, gets the MSATS data for the NMI
174 175 176 177 178 |
# File 'lib/aemo/nmi.rb', line 174 def raw_msats_nmi_detail( = {}) raise ArgumentError, 'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate? AEMO::MSATS.nmi_detail(@nmi, ) end |
#tni_value(datetime = ::Time.now) ⇒ nil, float
A function to return the transmission node identifier loss factor value for a given date
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/aemo/nmi.rb', line 345 def tni_value(datetime = ::Time.now) if @tni.nil? raise 'No TNI set, ensure that you have set the value either via the ' \ 'update_from_msats! function or manually' end raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni) raise 'Invalid date' unless [DateTime, ::Time].include?(datetime.class) possible_values = TNI_CODES[@tni].select do |x| ::Time.parse(x['FromDate']) <= datetime && datetime <= ::Time.parse(x['ToDate']) end return nil if possible_values.empty? possible_values = possible_values.first['mlf_data']['loss_factors'].select do |x| ::Time.parse(x['start']) <= datetime && datetime <= ::Time.parse(x['finish']) end return nil if possible_values.empty? possible_values.first['value'].to_f end |
#tni_values(start = ::Time.now, finish = ::Time.now) ⇒ Array(Hash)
A function to return the transmission node identifier loss factor value for a given date
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/aemo/nmi.rb', line 371 def tni_values(start = ::Time.now, finish = ::Time.now) if @tni.nil? raise 'No TNI set, ensure that you have set the value either via the ' \ 'update_from_msats! function or manually' end raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni) raise 'Invalid start' unless [DateTime, ::Time].include?(start.class) raise 'Invalid finish' unless [DateTime, ::Time].include?(finish.class) raise 'start cannot be after finish' if start > finish possible_values = TNI_CODES[@tni].reject do |tni_code| start > ::Time.parse(tni_code['ToDate']) || finish < ::Time.parse(tni_code['FromDate']) end return nil if possible_values.empty? possible_values.map { |x| x['mlf_data']['loss_factors'] } end |
#update_from_msats!(options = {}) ⇒ self
Provided MSATS is configured, uses the raw MSATS data to augment NMI information
184 185 186 187 188 189 |
# File 'lib/aemo/nmi.rb', line 184 def update_from_msats!( = {}) # Update local cache @msats_detail = raw_msats_nmi_detail() parse_msats_detail self end |
#valid_checksum?(checksum_value) ⇒ Boolean
A function to calculate the checksum value for a given National Meter Identifier
151 152 153 |
# File 'lib/aemo/nmi.rb', line 151 def valid_checksum?(checksum_value) checksum_value == checksum end |
#valid_nmi? ⇒ Boolean
A function to validate the instance’s nmi value
132 133 134 |
# File 'lib/aemo/nmi.rb', line 132 def valid_nmi? AEMO::NMI.valid_nmi?(@nmi) end |