Class: AEMO::MeterData::Flag
- Inherits:
-
Object
- Object
- AEMO::MeterData::Flag
- Defined in:
- lib/aemo/meter_data/flag.rb,
lib/aemo/meter_data/flag/method.rb,
lib/aemo/meter_data/flag/quality.rb,
lib/aemo/meter_data/flag/reason_code.rb
Overview
Represents a NEM12/NEM13 quality flag with validation and conversion capabilities
Constant Summary collapse
- METHOD_FLAGS =
Method flags for NEM12/NEM13 meter data
{ 11 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Check', description: '' }, 12 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Calculated', description: '' }, 13 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'SCADA', description: '' }, 14 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Retrospective Like Day', description: 'Updated v7.8' }, 15 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Retrospective Average Like Day', description: 'Updated v7.8' }, 16 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Agreed', description: '[OBSOLETE] v7.8' }, 17 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Linear', description: '' }, 18 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Alternate', description: '' }, 19 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Zero', description: '' }, 20 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Prospective Like Day', description: 'Updated v7.8' }, 21 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Five-minute No Historical Data', description: 'Added v7.8' }, 22 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Prospective Ave Like Day', description: 'Added v7.8' }, 23 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Previous Year', description: 'Added v7.8' }, 24 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Data Scaling', description: 'Added v7.8' }, 25 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'ADL', description: 'Added v7.8' }, 51 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Previous Year', description: '' }, 52 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Previous Read', description: '' }, 53 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Revision', description: '' }, 54 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Linear', description: '' }, 55 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Agreed', description: '' }, 56 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Prior to First Read - Agreed', description: '' }, 57 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Customer Class', description: '' }, 58 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Zero', description: '' }, 59 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Five-minute No Historical Data', description: '' }, 61 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Previous Year', description: '' }, 62 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Previous Read', description: '' }, 63 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Customer Class', description: '' }, 64 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Agreed', description: '' }, 65 => { type: %w[EST], installation_type: 6, short_descriptor: 'ADL', description: '' }, 66 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Revision', description: '' }, 67 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Customer Read', description: '' }, 68 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Zero', description: '' }, 69 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Linear extrapolation', description: '' }, 71 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Recalculation', description: '' }, 72 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Revised Table', description: '' }, 73 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Revised Algorithm', description: '' }, 74 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Agreed', description: '' }, 75 => { type: %w[EST], installation_type: 7, short_descriptor: 'Existing Table', description: '' } }.freeze
- QUALITY_FLAGS =
Quality flags for NEM12/NEM13 meter data
{ 'A' => 'Actual Data', 'E' => 'Forward Estimated Data', 'F' => 'Final Substituted Data', 'N' => 'Null Data', 'S' => 'Substituted Data', 'V' => 'Variable Data' }.freeze
- REASON_CODES =
Reason codes for NEM12/NEM13 meter data
{ 0 => 'Free Text Description', 1 => 'Meter/Equipment Changed', 2 => 'Extreme Weather/Wet', 3 => 'Quarantine', 4 => 'Savage Dog', 5 => 'Meter/Equipment Changed', 6 => 'Extreme Weather/Wet', 7 => 'Unable To Locate Meter', 8 => 'Vacant Premise', 9 => 'Meter/Equipment Changed', 10 => 'Lock Damaged/Seized', 11 => 'In Wrong Walk', 12 => 'Locked Premises', 13 => 'Locked Gate', 14 => 'Locked Meter Box', 15 => 'Access - Overgrown', 16 => 'Noxious Weeds', 17 => 'Unsafe Equipment/Location', 18 => 'Read Below Previous', 19 => 'Consumer Wanted', 20 => 'Damaged Equipment/Panel', 21 => 'Switched Off', 22 => 'Meter/Equipment Seals Missing', 23 => 'Meter/Equipment Seals Missing', 24 => 'Meter/Equipment Seals Missing', 25 => 'Meter/Equipment Seals Missing', 26 => 'Meter/Equipment Seals Missing', 27 => 'Meter/Equipment Seals Missing', 28 => 'Damaged Equipment/Panel', 29 => 'Relay Faulty/Damaged', 30 => 'Meter Stop Switch On', 31 => 'Meter/Equipment Seals Missing', 32 => 'Damaged Equipment/Panel', 33 => 'Relay Faulty/Damaged', 34 => 'Meter Not In Handheld', 35 => 'Timeswitch Faulty/Reset Required', 36 => 'Meter High/Ladder Required', 37 => 'Meter High/Ladder Required', 38 => 'Unsafe Equipment/Location', 39 => 'Reverse Energy Observed', 40 => 'Timeswitch Faulty/Reset Required', 41 => 'Faulty Equipment Display/Dials', 42 => 'Faulty Equipment Display/Dials', 43 => 'Power Outage', 44 => 'Unsafe Equipment/Location', 45 => 'Readings Failed To Validate', 46 => 'Extreme Weather/Hot', 47 => 'Refused Access', 48 => 'Timeswitch Faulty/Reset Required', 49 => 'Wet Paint', 50 => 'Wrong Tariff', 51 => 'Installation Demolished', 52 => 'Access - Blocked', 53 => 'Bees/Wasp In Meter Box', 54 => 'Meter Box Damaged/Faulty', 55 => 'Faulty Equipment Display/Dials', 56 => 'Meter Box Damaged/Faulty', 57 => 'Timeswitch Faulty/Reset Required', 58 => 'Meter Ok - Supply Failure', 59 => 'Faulty Equipment Display/Dials', 60 => 'Illegal Connection/Equipment Tampered', 61 => 'Meter Box Damaged/Faulty', 62 => 'Damaged Equipment/Panel', 63 => 'Illegal Connection/Equipment Tampered', 64 => 'Key Required', 65 => 'Wrong Key Provided', 66 => 'Lock Damaged/Seized', 67 => 'Extreme Weather/Wet', 68 => 'Zero Consumption', 69 => 'Reading Exceeds Estimate', 70 => 'Probe Reports Tampering', 71 => 'Probe Read Error', 72 => 'Meter/Equipment Changed', 73 => 'Low Consumption', 74 => 'High Consumption', 75 => 'Customer Read', 76 => 'Communications Fault', 77 => 'Estimation Forecast', 78 => 'Null Data', 79 => 'Power Outage Alarm', 80 => 'Short Interval Alarm', 81 => 'Long Interval Alarm', 82 => 'CRC Error', 83 => 'RAM Checksum Error', 84 => 'ROM Checksum Error', 85 => 'Data Missing Alarm', 86 => 'Clock Error Alarm', 87 => 'Reset Occurred', 88 => 'Watchdog Timeout Alarm', 89 => 'Time Reset Occurred', 90 => 'Test Mode', 91 => 'Load Control', 92 => 'Added Interval (Data Correction)', 93 => 'Replaced Interval (Data Correction)', 94 => 'Estimated Interval (Data Correction)', 95 => 'Pulse Overflow Alarm', 96 => 'Data Out Of Limits', 97 => 'Excluded Data', 98 => 'Parity Error', 99 => 'Energy Type (Register Changed)' }.freeze
Instance Attribute Summary collapse
- #method_flag ⇒ Object readonly
- #quality_flag ⇒ Object readonly
- #reason_code ⇒ Object readonly
Class Method Summary collapse
-
.from_hash(hash, validate: false) ⇒ Flag?
Create a Flag from a hash (backward compatibility).
-
.from_quality_method_reason_code(quality_method:, reason_code:, validate: false) ⇒ Flag
Create a Flag from a quality method string and reason code.
-
.normalize(flag_or_hash) ⇒ Flag
Normalize a flag (apply NEM12 specification rules).
-
.valid_method_flag?(method_flag) ⇒ Boolean
Validate a method flag value.
-
.valid_quality_flag?(quality_flag) ⇒ Boolean
Validate a quality flag value.
-
.valid_reason_code?(reason_code) ⇒ Boolean
Validate a reason code value.
-
.validate!(flag_or_hash) ⇒ void
Validate a flag and raise an error if invalid.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Equality comparison.
-
#hash ⇒ Integer
Hash code for use in hashes and sets.
-
#initialize(quality_flag: nil, method_flag: nil, reason_code: nil, validate: true) ⇒ Flag
constructor
Initialize a new Flag instance.
-
#to_h ⇒ Hash
Convert flag to hash (backward compatibility).
-
#to_quality_method ⇒ String
Convert flag to quality method string.
-
#to_s ⇒ String?
Convert flag to human-readable string.
-
#validate! ⇒ void
Validate the flag and raise an error if invalid.
Constructor Details
#initialize(quality_flag: nil, method_flag: nil, reason_code: nil, validate: true) ⇒ Flag
Initialize a new Flag instance
20 21 22 23 24 25 26 27 |
# File 'lib/aemo/meter_data/flag.rb', line 20 def initialize(quality_flag: nil, method_flag: nil, reason_code: nil, validate: true) @quality_flag = quality_flag @method_flag = method_flag @reason_code = reason_code # Validate the flag if requested self.class.validate!(self) if validate end |
Instance Attribute Details
#method_flag ⇒ Object (readonly)
12 13 14 |
# File 'lib/aemo/meter_data/flag.rb', line 12 def method_flag @method_flag end |
#quality_flag ⇒ Object (readonly)
12 13 14 |
# File 'lib/aemo/meter_data/flag.rb', line 12 def quality_flag @quality_flag end |
#reason_code ⇒ Object (readonly)
12 13 14 |
# File 'lib/aemo/meter_data/flag.rb', line 12 def reason_code @reason_code end |
Class Method Details
.from_hash(hash, validate: false) ⇒ Flag?
Create a Flag from a hash (backward compatibility)
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/aemo/meter_data/flag.rb', line 35 def from_hash(hash, validate: false) return nil if hash.nil? return hash if hash.is_a?(Flag) new( quality_flag: hash[:quality_flag], method_flag: hash[:method_flag], reason_code: hash[:reason_code], validate: validate ) end |
.from_quality_method_reason_code(quality_method:, reason_code:, validate: false) ⇒ Flag
Create a Flag from a quality method string and reason code
53 54 55 56 57 58 59 60 61 62 |
# File 'lib/aemo/meter_data/flag.rb', line 53 def from_quality_method_reason_code(quality_method:, reason_code:, validate: false) quality_flag = quality_method[0] method_flag = quality_method[1, 2].to_i if quality_method.length == 3 new( quality_flag:, method_flag:, reason_code: reason_code.blank? ? nil : reason_code.to_i, validate: ) end |
.normalize(flag_or_hash) ⇒ Flag
Normalize a flag (apply NEM12 specification rules)
Normalization rules:
-
nil flags become ‘A’ (Actual Data)
-
‘A’, ‘N’ flags: Remove method_flag if present
-
‘V’ flags: Remove both method_flag and reason_code if present
-
Other flags: Preserve as-is
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/aemo/meter_data/flag.rb', line 74 def normalize(flag_or_hash) # Handle nil input return new(quality_flag: 'A', validate: false) if flag_or_hash.nil? # Convert to Flag if needed flag = flag_or_hash.is_a?(Flag) ? flag_or_hash : from_hash(flag_or_hash) normalized_quality = flag.quality_flag || 'A' normalized_method = flag.method_flag normalized_reason = flag.reason_code # Apply normalization rules based on quality flag case normalized_quality when 'A', 'N' # A and N: Remove method_flag (reason_code is optional) normalized_method = nil when 'V' # V: Remove both method_flag and reason_code normalized_method = nil normalized_reason = nil end new( quality_flag: normalized_quality, method_flag: normalized_method, reason_code: normalized_reason, validate: false ) end |
.valid_method_flag?(method_flag) ⇒ Boolean
Validate a method flag value
191 192 193 |
# File 'lib/aemo/meter_data/flag.rb', line 191 def valid_method_flag?(method_flag) method_flag.nil? || METHOD_FLAGS.key?(method_flag) end |
.valid_quality_flag?(quality_flag) ⇒ Boolean
Validate a quality flag value
183 184 185 |
# File 'lib/aemo/meter_data/flag.rb', line 183 def valid_quality_flag?(quality_flag) quality_flag.nil? || QUALITY_FLAGS.key?(quality_flag) end |
.valid_reason_code?(reason_code) ⇒ Boolean
Validate a reason code value
199 200 201 |
# File 'lib/aemo/meter_data/flag.rb', line 199 def valid_reason_code?(reason_code) reason_code.nil? || REASON_CODES.key?(reason_code) end |
.validate!(flag_or_hash) ⇒ void
This method returns an undefined value.
Validate a flag and raise an error if invalid
NEM12 Specification Requirements:
-
‘A’ (Actual Data): No method flag, optional reason code
-
‘N’ (Null Data): No method flag, optional reason code DEPRECATED
-
‘V’ (Variable Data): No method flag, no reason code
-
‘E’ (Forward Estimated Data): Requires method flag, optional reason code
-
‘F’ (Final Substituted Data): Requires method flag AND reason code
-
‘S’ (Substituted Data): Requires method flag AND reason code
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/aemo/meter_data/flag.rb', line 117 def validate!(flag_or_hash) return if flag_or_hash.nil? flag = from_hash(flag_or_hash) quality_flag = flag.quality_flag method_flag = flag.method_flag reason_code = flag.reason_code # Validate individual components unless valid_quality_flag?(quality_flag) raise ArgumentError, "Invalid quality flag: #{quality_flag.inspect}" end unless valid_method_flag?(method_flag) raise ArgumentError, "Invalid method flag: #{method_flag.inspect}" end unless valid_reason_code?(reason_code) raise ArgumentError, "Invalid reason code: #{reason_code.inspect}" end # Validate flag combinations according to NEM12 specification case quality_flag when 'A', 'N' # A and N: Should not have method flags, may have reason codes unless method_flag.nil? raise ArgumentError, "Quality flag '#{quality_flag}' should not have a method flag" end when 'V' # V: Should not have method flags or reason codes unless method_flag.nil? raise ArgumentError, "Quality flag 'V' should not have a method flag" end unless reason_code.nil? raise ArgumentError, "Quality flag 'V' should not have a reason code" end when 'E' # E: Requires method flag, may have reason code (optional) if method_flag.nil? raise ArgumentError, "Quality flag 'E' requires a method flag" end when 'F', 'S' # F and S: Require both method flag and reason code if method_flag.nil? raise ArgumentError, "Quality flag '#{quality_flag}' requires a method flag" end if reason_code.nil? raise ArgumentError, "Quality flag '#{quality_flag}' requires a reason code" end end end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Equality comparison
247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/aemo/meter_data/flag.rb', line 247 def ==(other) if other.is_a?(Flag) quality_flag == other.quality_flag && method_flag == other.method_flag && reason_code == other.reason_code elsif other.is_a?(Hash) quality_flag == other[:quality_flag] && method_flag == other[:method_flag] && reason_code == other[:reason_code] else false end end |
#hash ⇒ Integer
Hash code for use in hashes and sets
264 265 266 |
# File 'lib/aemo/meter_data/flag.rb', line 264 def hash [quality_flag, method_flag, reason_code].hash end |
#to_h ⇒ Hash
Convert flag to hash (backward compatibility)
235 236 237 238 239 240 241 |
# File 'lib/aemo/meter_data/flag.rb', line 235 def to_h { quality_flag: @quality_flag, method_flag: @method_flag, reason_code: @reason_code } end |
#to_quality_method ⇒ String
Convert flag to quality method string
207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/aemo/meter_data/flag.rb', line 207 def to_quality_method return 'A' if quality_flag.nil? qf = quality_flag || 'A' mf = method_flag # Build quality method string (quality flag + optional method flag) if mf.nil? qf else "#{qf}#{format('%02d', mf)}" end end |
#to_s ⇒ String?
Convert flag to human-readable string
224 225 226 227 228 229 230 |
# File 'lib/aemo/meter_data/flag.rb', line 224 def to_s parts = [] parts << QUALITY_FLAGS[quality_flag] unless QUALITY_FLAGS[quality_flag].nil? parts << METHOD_FLAGS[method_flag][:short_descriptor] unless METHOD_FLAGS[method_flag].nil? parts << REASON_CODES[reason_code] unless REASON_CODES[reason_code].nil? parts.empty? ? nil : parts.join(' - ') end |
#validate! ⇒ void
This method returns an undefined value.
Validate the flag and raise an error if invalid
272 273 274 |
# File 'lib/aemo/meter_data/flag.rb', line 272 def validate! self.class.validate!(self) end |