Class: AEMO::NMI

Inherits:
Object
  • Object
show all
Defined in:
lib/aemo/nmi.rb

Overview

AEMO::NMI acts as an object to simplify access to data and information about a NMI and provide verification of the NMI value

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
NMI_ALLOCATIONS =

NMI_ALLOCATIONS as per AEMO Documentation at aemo.com.au/Electricity/Policies-and-Procedures/Retail-and-Metering/~/media/Files/Other/Retail% 20and% 20Metering/NMI_Allocation_List_v7_June_2012.ashx

Last accessed 2016-05-15
{
  'ACTEWP' => {
    title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
    friendly_title: 'ACTEWAgl',
    state: 'ACT',
    type: 'electricity',
    includes: [
      /^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(7001\d{6})$/
    ],
    excludes: [
    ]
  },
  'CNRGYP' => {
    title: 'Essential Energy',
    friendly_title: 'Essential Energy',
    state: 'NSW',
    type: 'electricity',
    includes: [
      /^(NAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(NBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(NDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(NFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(4001\d{6})$/,
      /^(4508\d{6})$/,
      /^(4204\d{6})$/,
      /^(4407\d{6})$/
    ],
    excludes: [
    ]
  },
  'ENERGYAP' => {
    title: 'Ausgrid',
    friendly_title: 'Ausgrid',
    state: 'NSW',
    type: 'electricity',
    includes: [
      /^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(410[234]\d{6})$/
    ],
    excludes: [
    ]
  },
  'INTEGP' => {
    title: 'Endeavour Energy',
    friendly_title: 'Endeavour Energy',
    state: 'NSW',
    type: 'electricity',
    includes: [
      /^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(431\d{7})$/
    ],
    excludes: [
    ]
  },
  'TRANSGP' => {
    title: 'TransGrid',
    friendly_title: 'TransGrid',
    state: 'NSW',
    type: 'electricity',
    includes: [
      /^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(460810[0-8]\d{3})$/
    ],
    excludes: [
    ]
  },
  'SNOWY' => {
    title: 'Snowy Hydro Ltd',
    friendly_title: 'Snowy Hydro',
    state: 'NSW',
    type: 'electricity',
    includes: [
      /^(4708109\d{3})$/
    ],
    excludes: [
    ]
  },
  'NT_RESERVED' => {
    title: 'Northern Territory Reserved Block',
    friendly_title: 'Northern Territory Reserved Block',
    state: 'NT',
    type: 'electricity',
    includes: [
      /^(250\d{7})$/
    ],
    excludes: [
    ]
  },
  'ERGONETP' => {
    title: 'Ergon Energy Corporation',
    friendly_title: 'Ergon Energy',
    state: 'QLD',
    type: 'electricity',
    includes: [
      /^(QAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(QCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(QDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(QEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(QFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(QGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(30\d{8})$/
    ],
    excludes: [
    ]
  },
  'ENERGEXP' => {
    title: 'ENERGEX Limited',
    friendly_title: 'Energex',
    state: 'QLD',
    type: 'electricity',
    includes: [
      /^(QB\d{2}[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(31\d{8})$/
    ],
    excludes: [
    ]
  },
  'PLINKP' => {
    title: 'Qld Electricity Transmission Corp (Powerlink)',
    friendly_title: 'Powerlink',
    state: 'QLD',
    type: 'electricity',
    includes: [
      /^(Q[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
      /^(320200\d{4})$/
    ],
    excludes: [
    ]
  },
  'UMPLP' => {
    title: 'SA Power Networks',
    friendly_title: 'SA Power Networks',
    state: 'SA',
    type: 'electricity',
    includes: [
      /^(SAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(SASMPL[\d]{4})$/,
      /^(200[12]\d{6})$/
    ],
    excludes: [
    ]
  },
  'ETSATP' => {
    title: 'ElectraNet SA',
    friendly_title: 'ElectraNet SA',
    state: 'SA',
    type: 'electricity',
    includes: [
      /^(S[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
      /^(210200\d{4})$/
    ],
    excludes: [
    ]
  },
  'AURORAP' => {
    title: 'TasNetworks',
    friendly_title: 'TasNetworks',
    state: 'TAS',
    type: 'electricity',
    includes: [
      /^(T000000(([0-4]\d{3})|(500[01])))$/,
      /^(8000\d{6})$/,
      /^(8590[23]\d{5})$/
    ],
    excludes: [
    ]
  },
  'TRANSEND' => {
    title: 'TasNetworks',
    friendly_title: 'TasNetworks',
    state: 'TAS',
    type: 'electricity',
    includes: [
      /^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/
    ],
    excludes: [
    ]
  },
  'CITIPP' => {
    title: 'CitiPower',
    friendly_title: 'CitiPower',
    state: 'VIC',
    type: 'electricity',
    includes: [
      /^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(610[23]\d{6})$/
    ],
    excludes: [
    ]
  },
  'EASTERN' => {
    title: 'SP AusNet',
    friendly_title: 'SP AusNet DNSP',
    state: 'VIC',
    type: 'electricity',
    includes: [
      /^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(630[56]\d{6})$/
    ],
    excludes: [
    ]
  },
  'POWCP' => {
    title: 'PowerCor Australia',
    friendly_title: 'PowerCor',
    state: 'VIC',
    type: 'electricity',
    includes: [
      /^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(620[34]\d{6})$/
    ],
    excludes: [
    ]
  },
  'SOLARISP' => {
    title: 'Jemena  Electricity Networks (VIC)',
    friendly_title: 'Jemena',
    state: 'VIC',
    type: 'electricity',
    includes: [
      /^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(6001\d{6})$/
    ],
    excludes: [
    ]
  },
  'UNITED' => {
    title: 'United Energy Distribution',
    friendly_title: 'United Energy',
    state: 'VIC',
    type: 'electricity',
    includes: [
      /^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(640[78]\d{6})$/
    ],
    excludes: [
    ]
  },
  'GPUPP' => {
    title: 'SP AusNet TNSP',
    friendly_title: 'SP AusNet TNSP',
    state: 'VIC',
    type: 'electricity',
    includes: [
      /^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
      /^(650900\d{4})$/
    ],
    excludes: [
    ]
  },
  'WESTERNPOWER' => {
    title: 'Western Power',
    friendly_title: 'Western Power',
    state: 'WA',
    type: 'electricity',
    includes: [
      /^(WAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(800[1-9]\d{6})$/,
      /^(801\d{7})$/,
      /^(8020\d{6})$/
    ],
    excludes: [
    ]
  },
  'HORIZONPOWER' => {
    title: 'Horizon Power',
    friendly_title: 'Horizon Power',
    state: 'WA',
    type: 'electricity',
    includes: [
      /^(8021\d{6})$/
    ],
    excludes: [
    ]
  },
  'GAS_NSW' => {
    title: 'GAS NSW',
    friendly_title: 'GAS NSW',
    state: 'NSW',
    type: 'gas',
    includes: [
      /^(52\d{8})$/
    ],
    excludes: [
    ]
  },
  'GAS_VIC' => {
    title: 'GAS VIC',
    friendly_title: 'GAS VIC',
    state: 'VIC',
    type: 'gas',
    includes: [
      /^(53\d{8})$/
    ],
    excludes: [
    ]
  },
  'GAS_QLD' => {
    title: 'GAS QLD',
    friendly_title: 'GAS QLD',
    state: 'QLD',
    type: 'gas',
    includes: [
      /^(54\d{8})$/
    ],
    excludes: [
    ]
  },
  'GAS_SA' => {
    title: 'GAS SA',
    friendly_title: 'GAS SA',
    state: 'SA',
    type: 'gas',
    includes: [
      /^(55\d{8})$/
    ],
    excludes: [
    ]
  },
  'GAS_WA' => {
    title: 'GAS WA',
    friendly_title: 'GAS WA',
    state: 'WA',
    type: 'gas',
    includes: [
      /^(56\d{8})$/
    ],
    excludes: [
    ]
  },
  'GAS_TAS' => {
    title: 'GAS TAS',
    friendly_title: 'GAS TAS',
    state: 'TAS',
    type: 'gas',
    includes: [
      /^(57\d{8})$/
    ],
    excludes: [
    ]
  },
  'FEDAIRPORTS' => {
    title: 'Federal Airports Corporation (Sydney Airport)',
    friendly_title: 'Sydney Airport',
    state: 'NSW',
    type: 'electricity',
    includes: [
      /^(NJJJNR[A-HJ-NP-Z\d]{4})$/
    ],
    excludes: [
    ]
  },
  'EXEMPTNETWORKS' => {
    title: 'Exempt Networks - various',
    friendly_title: 'Exempt Networks - various',
    state: '',
    type: 'electricity',
    includes: [
      /^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
      /^(7102\d{6})$/
    ],
    excludes: [
    ]
  },
  'AEMORESERVED' => {
    title: 'AEMO Reserved',
    friendly_title: 'AEMO Reserved',
    state: '',
    type: 'electricity',
    includes: [
      /^(880[1-5]\d{6})$/,
      /^(9\d{9})$/
    ],
    excludes: [
    ]
  }
}.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 http://www.aemo.com.au/Electricity/Market-Operations/Loss-Factors-and-Regional-Boundaries/~/media/Files/Other/loss% 20factors/DLF_FINAL_V2_2014_2015.ashx
Last accessed 2015-02-06
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nmi, options = {}) ⇒ AEMO::NMI

Initialize a NMI file

Parameters:

  • nmi (String)

    the National Meter Identifier (NMI)

  • options (Hash) (defaults to: {})

    a hash of options

Options Hash (options):

  • :msats_detail (Hash)

    MSATS details as per #parse_msats_detail requirements

Raises:

  • (ArgumentError)


434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/aemo/nmi.rb', line 434

def initialize(nmi, options = {})
  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 = options[:msats_detail]

  parse_msats_detail unless @msats_detail.nil?
end

Instance Attribute Details

#addressObject

Returns the value of attribute address.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def address
  @address
end

#classification_codeObject

Returns the value of attribute classification_code.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def classification_code
  @classification_code
end

#customer_classification_codeObject

Returns the value of attribute customer_classification_code.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def customer_classification_code
  @customer_classification_code
end

#customer_threshold_codeObject

Returns the value of attribute customer_threshold_code.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def customer_threshold_code
  @customer_threshold_code
end

#data_streamsObject

Returns the value of attribute data_streams.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def data_streams
  @data_streams
end

#dlfObject

Returns the value of attribute dlf.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def dlf
  @dlf
end

#jurisdiction_codeObject

Returns the value of attribute jurisdiction_code.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def jurisdiction_code
  @jurisdiction_code
end

#metersObject

Returns the value of attribute meters.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def meters
  @meters
end

#msats_detailObject

Returns the value of attribute msats_detail.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def msats_detail
  @msats_detail
end

#nmiObject

Returns the value of attribute nmi.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def nmi
  @nmi
end

#rolesObject

Returns the value of attribute roles.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def roles
  @roles
end

#statusObject

Returns the value of attribute status.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def status
  @status
end

#tniObject

Returns the value of attribute tni.



426
427
428
# File 'lib/aemo/nmi.rb', line 426

def tni
  @tni
end

Class Method Details

.network(nmi) ⇒ Object

Find the Network for a given NMI

Parameters:

  • nmi (String)

    NMI



643
644
645
646
647
648
649
650
651
652
653
654
# File 'lib/aemo/nmi.rb', line 643

def self.network(nmi)
  network = nil
  AEMO::NMI::NMI_ALLOCATIONS.each_pair do |identifier, details|
    details[:includes].each do |pattern|
      if nmi.match(pattern)
        network = { identifier => details }
        break
      end
    end
  end
  network
end

.valid_checksum?(nmi, checksum_value) ⇒ Boolean

A function to calculate the checksum value for a given National Meter Identifier

Parameters:

  • nmi (String)

    the NMI to check the checksum against

  • checksum_value (Integer)

    the checksum value to check against the current National Meter Identifier’s checksum value

Returns:

  • (Boolean)

    whether or not the checksum is valid



634
635
636
637
# File 'lib/aemo/nmi.rb', line 634

def self.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

Parameters:

  • nmi (String)

    the nmi to be checked

Returns:

  • (Boolean)

    whether or not the nmi is valid



625
626
627
# File 'lib/aemo/nmi.rb', line 625

def self.valid_nmi?(nmi)
  ((nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?)
end

Instance Method Details

#checksumInteger

Checksum is a function to calculate the checksum value for a given National Meter Identifier

Returns:

  • (Integer)

    the checksum value for the current National Meter Identifier



473
474
475
476
477
478
479
480
481
482
483
# File 'lib/aemo/nmi.rb', line 473

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_loadInteger

The current annual load in MWh

Returns:

  • (Integer)

    the current annual load for the meter in MWh



617
618
619
# File 'lib/aemo/nmi.rb', line 617

def current_annual_load
  (current_daily_load * 365.2425 / 1000).to_i
end

#current_daily_loadInteger

The current daily load in kWh

Returns:

  • (Integer)

    the current daily load for the meter in kWh



610
611
612
# File 'lib/aemo/nmi.rb', line 610

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)

Parameters:

  • status (String) (defaults to: 'A')

    the stateus [A|I]

Returns:

  • (Array<OpenStruct>)

    Returns an array of OpenStructs for the current Meters



603
604
605
# File 'lib/aemo/nmi.rb', line 603

def data_streams_by_status(status = 'A')
  @data_streams.select { |x| x.status == status.to_s }
end

#dlfc_value(datetime = DateTime.now) ⇒ nil, float

A function to return the distribution loss factor value for a given date

Parameters:

  • datetime (DateTime, Time) (defaults to: DateTime.now)

    the date for the distribution loss factor value

Returns:

  • (nil, float)

    the distribution loss factor value



660
661
662
663
664
665
666
667
668
669
670
# File 'lib/aemo/nmi.rb', line 660

def dlfc_value(datetime = DateTime.now)
  raise 'No DLF set, ensure that you have set the value either via the update_from_msats! function or manually' if @dlf.nil?
  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 { |x| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
  if possible_values.empty?
    nil
  else
    possible_values.first['Value'].to_f
  end
end

#dlfc_values(start = DateTime.now, finish = DateTime.now) ⇒ Array(Hash)

A function to return the distribution loss factor value for a given date

Parameters:

  • start (DateTime, Time) (defaults to: DateTime.now)

    the date for the distribution loss factor value

  • finish (DateTime, Time) (defaults to: DateTime.now)

    the date for the distribution loss factor value

Returns:

  • (Array(Hash))

    array of hashes of start, finish and value



677
678
679
680
681
682
683
684
685
# File 'lib/aemo/nmi.rb', line 677

def dlfc_values(start = DateTime.now, finish = DateTime.now)
  raise 'No DLF set, ensure that you have set the value either via the update_from_msats! function or manually' if @dlf.nil?
  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 > DateTime.parse(x['ToDate']) || finish < DateTime.parse(x['FromDate']) }
                 .map { |x| { 'start' => x['FromDate'], 'finish' => x['ToDate'], 'value' => x['Value'].to_f } }
end

#friendly_addressString

Returns a nice address from the structured one AEMO sends us

Returns:

  • (String)


578
579
580
581
582
583
584
585
586
587
588
589
# File 'lib/aemo/nmi.rb', line 578

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<OpenStruct>

Returns the meter OpenStructs for the requested status (C/R)

Parameters:

  • status (String) (defaults to: 'C')

    the stateus [C|R]

Returns:

  • (Array<OpenStruct>)

    Returns an array of OpenStructs for Meters with the status provided



595
596
597
# File 'lib/aemo/nmi.rb', line 595

def meters_by_status(status = 'C')
  @meters.select { |x| x.status == status.to_s }
end

#networkObject

Find the Network of NMI



458
459
460
# File 'lib/aemo/nmi.rb', line 458

def network
  AEMO::NMI.network(@nmi)
end

#parse_msats_detailself

Turns raw MSATS junk into useful things

Returns:

  • (self)

    returns self



507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
# File 'lib/aemo/nmi.rb', line 507

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.select { |x| !x['Status'].nil? }.each do |meter|
      @meters << OpenStruct.new(
        status: meter['Status'],
        installation_type_code: meter['InstallationTypeCode'],
        next_scheduled_read_date: meter['NextScheduledReadDate'],
        read_type_code: meter['ReadTypeCode'],
        registers: [],
        serial_number: meter['SerialNumber']
      )
    end
    meters.select { |x| x['Status'].nil? }.each do |registers|
      m = @meters.find { |x| x.serial_number == registers['SerialNumber'] }
      m.registers << OpenStruct.new(
        controlled_load: (registers['RegisterConfiguration']['Register']['ControlledLoad'] == 'Y'),
        dial_format: registers['RegisterConfiguration']['Register']['DialFormat'],
        multiplier: registers['RegisterConfiguration']['Register']['Multiplier'],
        network_tariff_code: registers['RegisterConfiguration']['Register']['NetworkTariffCode'],
        register_id: registers['RegisterConfiguration']['Register']['RegisterID'],
        status: registers['RegisterConfiguration']['Register']['Status'],
        time_of_day: registers['RegisterConfiguration']['Register']['TimeOfDay'],
        unit_of_measure: registers['RegisterConfiguration']['Register']['UnitOfMeasure']
      )
    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

Returns:

  • (Hash)

    MSATS NMI Detail data

Raises:

  • (ArgumentError)


488
489
490
491
492
# File 'lib/aemo/nmi.rb', line 488

def raw_msats_nmi_detail(options = {})
  raise ArgumentError,
        'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate?
  AEMO::MSATS.nmi_detail(@nmi, options)
end

#tni_value(datetime = DateTime.now) ⇒ nil, float

A function to return the transmission node identifier loss factor value for a given date

Parameters:

  • datetime (DateTime, Time) (defaults to: DateTime.now)

    the date for the distribution loss factor value

Returns:

  • (nil, float)

    the transmission node identifier loss factor value



691
692
693
694
695
696
697
698
699
700
# File 'lib/aemo/nmi.rb', line 691

def tni_value(datetime = DateTime.now)
  raise 'No TNI set, ensure that you have set the value either via the update_from_msats! function or manually' if @tni.nil?
  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| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
  return nil if possible_values.empty?
  possible_values = possible_values.first['mlf_data']['loss_factors'].select { |x| DateTime.parse(x['start']) <= datetime && datetime <= DateTime.parse(x['finish']) }
  return nil if possible_values.empty?
  possible_values.first['value'].to_f
end

#tni_values(start = DateTime.now, finish = DateTime.now) ⇒ Array(Hash)

A function to return the transmission node identifier loss factor value for a given date

Parameters:

  • start (DateTime, Time) (defaults to: DateTime.now)

    the date for the distribution loss factor value

  • finish (DateTime, Time) (defaults to: DateTime.now)

    the date for the distribution loss factor value

Returns:

  • (Array(Hash))

    array of hashes of start, finish and value



707
708
709
710
711
712
713
714
715
716
717
718
# File 'lib/aemo/nmi.rb', line 707

def tni_values(start = DateTime.now, finish = DateTime.now)
  raise 'No TNI set, ensure that you have set the value either via the update_from_msats! function or manually' if @tni.nil?
  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 { |x| start > DateTime.parse(x['ToDate']) || finish < DateTime.parse(x['FromDate']) }
                    .reject { |x| start > DateTime.parse(x['finish']) || finish < DateTime.parse(x['start']) }
  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

Returns:

  • (self)

    returns self



497
498
499
500
501
502
# File 'lib/aemo/nmi.rb', line 497

def update_from_msats!(options = {})
  # Update local cache
  @msats_detail = raw_msats_nmi_detail(options)
  parse_msats_detail
  self
end

#valid_checksum?(checksum_value) ⇒ Boolean

A function to calculate the checksum value for a given National Meter Identifier

Parameters:

  • checksum_value (Integer)

    the checksum value to check against the current National Meter Identifier’s checksum value

Returns:

  • (Boolean)

    whether or not the checksum is valid



466
467
468
# File 'lib/aemo/nmi.rb', line 466

def valid_checksum?(checksum_value)
  checksum_value == checksum
end

#valid_nmi?Boolean

A function to validate the instance’s nmi value

Returns:

  • (Boolean)

    whether or not the nmi is valid



451
452
453
# File 'lib/aemo/nmi.rb', line 451

def valid_nmi?
  AEMO::NMI.valid_nmi?(@nmi)
end