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<OpenStruct>
Returns the data_stream OpenStructs 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
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/aemo/nmi.rb', line 113 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
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def address @address end |
#classification_code ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def classification_code @classification_code end |
#customer_classification_code ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def customer_classification_code @customer_classification_code end |
#customer_threshold_code ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def customer_threshold_code @customer_threshold_code end |
#data_streams ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def data_streams @data_streams end |
#dlf ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def dlf @dlf end |
#jurisdiction_code ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def jurisdiction_code @jurisdiction_code end |
#meters ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def meters @meters end |
#msats_detail ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def msats_detail @msats_detail end |
#nmi ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def nmi @nmi end |
#roles ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def roles @roles end |
#status ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def status @status end |
#tni ⇒ Object
69 70 71 |
# File 'lib/aemo/nmi.rb', line 69 def tni @tni end |
Class Method Details
.allocation ⇒ Object
Find the Network for a given NMI
103 104 105 |
# File 'lib/aemo/nmi.rb', line 103 def network(nmi) AEMO::NMI::Allocation.find_by_nmi(nmi) end |
.network(nmi) ⇒ Object
Find the Network for a given NMI
99 100 101 |
# File 'lib/aemo/nmi.rb', line 99 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
90 91 92 93 |
# File 'lib/aemo/nmi.rb', line 90 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
79 80 81 |
# File 'lib/aemo/nmi.rb', line 79 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
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/aemo/nmi.rb', line 158 def checksum summation = 0 @nmi.reverse.split(//).each_index do |i| value = nmi[nmi.length - i - 1].ord value *= 2 if i.even? value = value.to_s.split(//).map(&:to_i).reduce(:+) summation += value end checksum = (10 - (summation % 10)) % 10 checksum end |
#current_annual_load ⇒ Integer
Use TimeDifference for more accurate annualised load
The current annual load in MWh
292 293 294 |
# File 'lib/aemo/nmi.rb', line 292 def current_annual_load (current_daily_load * 365.2425 / 1000).to_i end |
#current_daily_load ⇒ Integer
The current daily load in kWh
283 284 285 286 |
# File 'lib/aemo/nmi.rb', line 283 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<OpenStruct>
Returns the data_stream OpenStructs for the requested status (A/I)
276 277 278 |
# File 'lib/aemo/nmi.rb', line 276 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
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/aemo/nmi.rb', line 301 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
324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/aemo/nmi.rb', line 324 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
249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/aemo/nmi.rb', line 249 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)
267 268 269 |
# File 'lib/aemo/nmi.rb', line 267 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
137 138 139 |
# File 'lib/aemo/nmi.rb', line 137 def network AEMO::NMI.network(@nmi) end |
#parse_msats_detail ⇒ self
Turns raw MSATS junk into useful things
192 193 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 |
# File 'lib/aemo/nmi.rb', line 192 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 << OpenStruct.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
173 174 175 176 |
# File 'lib/aemo/nmi.rb', line 173 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
341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/aemo/nmi.rb', line 341 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 { |x| Time.parse(x['FromDate']) <= datetime && datetime <= Time.parse(x['ToDate']) } return nil if possible_values.empty? possible_values = possible_values.first['mlf_data']['loss_factors'].select { |x| Time.parse(x['start']) <= datetime && datetime <= Time.parse(x['finish']) } 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
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/aemo/nmi.rb', line 360 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
182 183 184 185 186 187 |
# File 'lib/aemo/nmi.rb', line 182 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
149 150 151 |
# File 'lib/aemo/nmi.rb', line 149 def valid_checksum?(checksum_value) checksum_value == checksum end |
#valid_nmi? ⇒ Boolean
A function to validate the instance’s nmi value
130 131 132 |
# File 'lib/aemo/nmi.rb', line 130 def valid_nmi? AEMO::NMI.valid_nmi?(@nmi) end |