Skip to content

Instantly share code, notes, and snippets.

@lightcap
Created May 25, 2010 23:08
Show Gist options
  • Select an option

  • Save lightcap/413806 to your computer and use it in GitHub Desktop.

Select an option

Save lightcap/413806 to your computer and use it in GitHub Desktop.
require 'rexml/document'
require "models/prog_const.rb"
# TODO: MK Get rid of all vestiges of Dupin's global cache that doesn't cache anything.
$prog_const = ProgConst.new if $prog_const.nil?
$prog_const.check_reload
#This object holds all the required information about a PVM post the system
#will need to work with the xml message
#
class PostPackage
attr_accessor :serial_number, :text_serial, :message_type, :poll_count, :poll_interval,
:xml, :mac_address, :ip_address, :timestamp_on
#Population code, this code is made to be very quick
def populate( ip_address, mac_address, xml, timestamp_on )
#Store the basic data we are just given here
@ip_address = ip_address.gsub(/[^\.0-9]/, '')
@mac_address = mac_address.upcase.gsub(/[^A-F0-9]/, '')
@xml = xml
@timestamp_on = timestamp_on
#Figure out what the serial number of this thing is
regex = /<inverter_serial>([^>]*)<\/inverter_serial>/.match(xml) || /<serial_number>([^>]*)<\/serial_number>/.match(xml)
@serial_number = regex[1] unless regex.nil?
regex = /<text_serial>([^>]*)<\/text_serial>/.match(xml)
@text_serial = regex[1] unless regex.nil?
@firmware_version = determine_firmware
#Figure out what the message type and the poll count
if (regex = /(<message[^>]*>)/.match(xml))
mesg = regex[1]
#Get the poll count
@poll_count = regex[1].to_i if (regex =
/poll_count=['"]([0-9]+)['"]/.match(mesg))
#Get the poll interval in seconds
@poll_interval = regex[1].to_i if (regex =
/poll_interval=['"]([0-9]+)['"]/.match(mesg))
#Get the poll count
@message_type = regex[1] if (regex =
/type=['"]([a-zA-Z0-9]+)['"]/.match(mesg))
end
#check if we are valid or not
if @serial_number.nil? or @poll_count.nil? or @poll_interval.nil? or @message_type.nil? or @mac_address.nil?
@valid = false
else
@valid = true
end
self
end
def data
@data ||= load_from_xml(extract_message(self.xml))
end
def validate(package_validator = nil)
if package_validator.nil?
@post_val = PostValidator.new(self)
@post_val.validate
end
end
def errors
@post_val.errors unless @post_val.nil?
end
def safe_inv_model
return @post_val.inv_model || $prog_const.inv_models.first
end
def inv_model
@post_val.inv_model
end
#Return true, false/nil if we are valid
def valid?
validate if @post_val.nil?
return @valid && @post_val.valid?
end
def serial_number_valid?
validate if @post_val.nil?
@post_val.serial_number_valid?
end
def text_serial_valid?
validate if @post_val.nil?
@post_val.text_serial_valid?
end
def segis?
# Right now, versions aren't allowed to be released with point version numbers. So straight float comparisions will work, here.
((@firmware_version >= Gem::Version.create("2.18") && @firmware_version < Gem::Version.create("3.0") && scaling?) ||
@firmware_version >= Gem::Version.create("3.2") || string_combiner? || weather_station?)
end
def scaling?
match = /<scaling>([^>]*)<\/scaling>/.match(xml)
return @scaling = /<scaling>([^>]*)<\/scaling>/.match(xml)[1].to_f === 1 unless match.nil?
return false
end
# Returns true if xml contains both <string_combiner> and </string_combiner> tags
def string_combiner?
! /<string_combiner [^>]*>/.match(xml).nil? and ! /<\/string_combiner>/.match(xml).nil?
end
# Returns true if xml contains both <weather_station> and </weather_station> tags
def weather_station?
! /<weather_station [^>]*>/.match(xml).nil? and ! /<\/weather_station>/.match(xml).nil?
end
def extra_stuff?
string_combiner? || weather_station?
end
#dump this info
def debug
return "PackageDump, Mac Address #{@mac_address}, IP Addresss #{@ip_address}, Serial #{@serial_number}, Type #{@message_type}, Count #{@poll_count}"
end
private
def determine_firmware
ver = /<firmware_version>([^>]*)<\/firmware_version>/.match(@xml) || /<version_firmware>([^>]*)<\/version_firmware>/.match(@xml)
@firmware_version = Gem::Version.create(ver[1])
end
def extract_message(raw_xml)
unless raw_xml
@log.error{"Starlink #{@sl.mac_address} posted no data, no use going on..."}
return nil
else
doc = REXML::Document.new(raw_xml)
xml = doc.root
end
end
def load_from_xml(xml)
dx = DatacomXML.load_from_xml(xml)
end
end
require "models/prog_const.rb"
$prog_const = ProgConst.new if $prog_const.nil?
$prog_const.check_reload
class PostValidator
attr_accessor :errors
#Information that was store while these checks were conducted
attr_reader :inv_model
# When this object is created, nothing is valid, what a negative guy!
def initialize(package)
@package = package
@serial_valid = false
@msg_valid = false
@errors = Array.new
@validated = false
@inv_model = nil
end
# Check if the serial number is valid
def validate_serials
ret_status = true
@serial_number_valid = true
@text_serial_valid = true
if !((13..14) === @package.serial_number.length)
errors.push( 'Invalid serial number length' )
@serial_number_valid = false
end
if @package.serial_number[-8..-1] =~ /[0]{8,}$/
errors.push('Invalid serial number last 8 chars all zeros')
@serial_number_valid = false
end
if @package.text_serial.nil?
errors.push( 'Invalid text serial does not exist')
@text_serial_valid = false
end
unless @package.text_serial.nil?
unless @package.text_serial.length >= 10
errors.push( 'Invalid text serial length' )
@text_serial_valid = false
end
if @package.text_serial[-10..-1] =~ /[0]{10,}$/
errors.push( 'Invalid text serial last 10 chars all zeros')
@text_serial_valid = false
end
end
unless @package.serial_number.nil?
model = @package.serial_number[0..3].to_i
@inv_model = $prog_const.inv_model_hash[model]
end
#Check that this model exists
if @inv_model.nil?
errors.push( "Invalid inverter model number" )
@serial_number_valid = false
end
@serial_valid = (text_serial_valid? || serial_number_valid?)
return @serial_valid
end
# Check that the message type is valid
def validate_msg_type
return @msg_valid = true if $prog_const.msg_type_hash.has_key?(@package.message_type.to_sym)
#This thing didn't work, so quit out
@errors.push( "Invalid message type (#{@package.message_type})" )
return false
end
def validate
@serial_valid = validate_serials
@msg_valid = validate_msg_type
@validated = true
end
# Return the validity of the object
def valid?
validate unless @validated
return (@serial_valid and @msg_valid)
end
def serial_number_valid?
@serial_number_valid
end
def text_serial_valid?
@text_serial_valid
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment