Skip to content
Browse files

Swapped log4r out and replaced with logging gem. Added example ussage…

…. Disabled CRC16 check, because its not working.
  • Loading branch information...
1 parent e4857d8 commit b62a2e62209c330a090c78b855cdb44db8f0ea30 @jwtd committed Apr 10, 2014
View
93 examples/read_ekm_meter.rb
@@ -0,0 +1,93 @@
+# :stopdoc:
+#
+# This is the code I use to generate the property list in the readme.
+
+require 'ekm-omnimeter'
+
+# Connect to a meter
+m = EkmOmnimeter::Meter.new(
+ :power_configuration => :single_phase_3wire,
+ :meter_number=>300000234,
+ :remote_address=>'192.168.0.125',
+ :remote_port => 50000)
+
+# Read some parameters
+puts "m.meter_number\t\t\t# #{m.meter_number}"
+puts "m.remote_address\t\t# #{m.remote_address}"
+puts "m.remote_port\t\t\t# #{m.remote_port}"
+puts "m.volts\t\t\t\t# #{m.volts}"
+puts "m.amps\t\t\t\t# #{m.amps}"
+puts "m.watts\t\t\t\t# #{m.watts}"
+puts "m.meter_timestamp\t\t# #{m.meter_timestamp}"
+puts "m.last_read_timestamp\t\t# #{m.last_read_timestamp}"
+puts "m.meter_type\t\t\t# #{m.meter_type}"
+puts "m.meter_firmware\t\t# #{m.meter_firmware}"
+puts "m.address\t\t\t# #{m.address}"
+puts "m.total_kwh\t\t\t# #{m.total_kwh}"
+puts "m.total_forward_kwh\t\t# #{m.total_forward_kwh}"
+puts "m.total_reverse_kwh\t\t# #{m.total_reverse_kwh}"
+puts "m.net_kwh\t\t\t# #{m.net_kwh}"
+puts "m.total_kwh_t1\t\t\t# #{m.total_kwh_t1}"
+puts "m.total_kwh_t2\t\t\t# #{m.total_kwh_t2}"
+puts "m.total_kwh_t3\t\t\t# #{m.total_kwh_t3}"
+puts "m.total_kwh_t4\t\t\t# #{m.total_kwh_t4}"
+puts "m.reverse_kwh_t1\t\t# #{m.reverse_kwh_t1}"
+puts "m.reverse_kwh_t2\t\t# #{m.reverse_kwh_t2}"
+puts "m.reverse_kwh_t3\t\t# #{m.reverse_kwh_t3}"
+puts "m.reverse_kwh_t4\t\t# #{m.reverse_kwh_t4}"
+puts "m.volts_l1\t\t\t# #{m.volts_l1}"
+puts "m.volts_l2\t\t\t# #{m.volts_l2}"
+puts "m.volts_l3\t\t\t# #{m.volts_l3}"
+puts "m.amps_l1\t\t\t# #{m.amps_l1}"
+puts "m.amps_l2\t\t\t# #{m.amps_l2}"
+puts "m.amps_l3\t\t\t# #{m.amps_l3}"
+puts "m.watts_l1\t\t\t# #{m.watts_l1}"
+puts "m.watts_l2\t\t\t# #{m.watts_l2}"
+puts "m.watts_l3\t\t\t# #{m.watts_l3}"
+puts "m.watts_total\t\t\t# #{m.watts_total}"
+puts "m.power_factor_1\t\t# #{m.power_factor_1}"
+puts "m.power_factor_2\t\t# #{m.power_factor_2}"
+puts "m.power_factor_3\t\t# #{m.power_factor_3}"
+puts "m.maximum_demand\t\t# #{m.maximum_demand}"
+puts "m.maximum_demand_period\t\t# #{m.maximum_demand_period}"
+puts "m.ct_ratio\t\t\t# #{m.ct_ratio}"
+puts "m.pulse_1_count\t\t\t# #{m.pulse_1_count}"
+puts "m.pulse_1_ratio\t\t\t# #{m.pulse_1_ratio}"
+puts "m.pulse_2_count\t\t\t# #{m.pulse_2_count}"
+puts "m.pulse_2_ratio\t\t\t# #{m.pulse_2_ratio}"
+puts "m.pulse_3_count\t\t\t# #{m.pulse_3_count}"
+puts "m.pulse_3_ratio\t\t\t# #{m.pulse_3_ratio}"
+puts "m.reactive_kwh_kvarh\t\t# #{m.reactive_kwh_kvarh}"
+puts "m.total_kwh_l1\t\t\t# #{m.total_kwh_l1}"
+puts "m.total_kwh_l2\t\t\t# #{m.total_kwh_l2}"
+puts "m.total_kwh_l3\t\t\t# #{m.total_kwh_l3}"
+puts "m.reverse_kwh_l1\t\t# #{m.reverse_kwh_l1}"
+puts "m.reverse_kwh_l2\t\t# #{m.reverse_kwh_l2}"
+puts "m.reverse_kwh_l3\t\t# #{m.reverse_kwh_l3}"
+puts "m.resettable_total_kwh\t\t# #{m.resettable_total_kwh}"
+puts "m.resettable_reverse_kwh\t# #{m.resettable_reverse_kwh}"
+puts "m.reactive_power_1\t\t# #{m.reactive_power_1}"
+puts "m.reactive_power_2\t\t# #{m.reactive_power_2}"
+puts "m.reactive_power_3\t\t# #{m.reactive_power_3}"
+puts "m.total_reactive_power\t\t# #{m.total_reactive_power}"
+puts "m.frequency\t\t\t# #{m.frequency}"
+# puts "m.pulse_input_hilo\t# #{m.pulse_input_hilo}" # Pulse input state 1 ????
+puts "m.pulse_1_input\t\t\t# #{m.pulse_1_input}" # Open / Closed
+puts "m.pulse_2_input\t\t\t# #{m.pulse_2_input}" # Open / Closed
+puts "m.pulse_3_input\t\t\t# #{m.pulse_3_input}" # Open / Closed
+# puts "m.direction_of_current\t# #{m.direction_of_current}" # 1 = Forward
+puts "m.current_direction_l1\t\t# #{m.current_direction_l1}"
+puts "m.current_direction_l2\t\t# #{m.current_direction_l2}"
+puts "m.current_direction_l3\t\t# #{m.current_direction_l3}"
+# puts "m.outputs_onoff\t# #{m.outputs_onoff}"
+puts "m.output_1\t\t\t# #{m.output_1}"
+puts "m.output_2\t\t\t# #{m.output_2}"
+puts "m.kwh_data_decimal_places\t# #{m.kwh_data_decimal_places}"
+puts "m.auto_reset_max_demand\t\t# #{m.auto_reset_max_demand}"
+puts "m.settable_pulse_per_kwh_ratio\t# #{m.settable_pulse_per_kwh_ratio}"
+ puts "m.checksum\t\t\t# #{m.checksum}"
+#puts "m.checksum_calculated\t# #{m.checksum_calculated}"
+
+
+
+# :startdoc:
View
2 lib/ekm-omnimeter.rb
@@ -4,6 +4,6 @@ class EkmOmnimeterError < ::Exception; end
end
require "ekm-omnimeter/version"
-require "ekm-omnimeter/logging"
+require "ekm-omnimeter/logger"
require "ekm-omnimeter/crc16"
require "ekm-omnimeter/meter"
View
22 lib/ekm-omnimeter/crc16.rb
@@ -2,38 +2,45 @@ module Crc16
# Returns boolean true or false if checksum of string s matches expected value
def self.ekm_crc16_matches?(s, expecting)
- puts "Expecting:\t#{expecting.inspect} -> #{expecting.unpack('H*')[0].inspect}"
+ #puts "Response snippet:\n#{s.inspect}"
+ #puts "Expecting:\t#{expecting.inspect} -> #{expecting.unpack('H*')[0].inspect}"
+ #puts "Response snippet using s.bytes\n#{s.bytes}"
+ #puts "Response snippet using s.split("").map{ |i| i.unpack('H*')[0]}\n#{s.split("").map{ |i| i.unpack('H*')[0]}}"
+ #puts "Response snippet using Array(s).pack('H*')\n#{Array(s).pack('H*').inspect}"
(Crc16.ekm_checksum(s) == expecting)
end
# According to http://forum.ekmmetering.com/viewtopic.php?f=4&t=5#p557
def self.ekm_checksum(s)
crc = Crc16.crc16(s) # Start with a normal CRC16
- puts "Normal CRC:\t#{crc.inspect}"
- crc = Crc16.convertLEBytesToNative(crc) # Little Endian processors need to reverse the Bigendian
- puts "LE CRC:\t#{crc.inspect}"
+ #puts "Normal CRC:\t#{crc.inspect}"
+ #crc = Crc16.convertLEBytesToNative(crc) # Little Endian processors need to reverse the Bigendian
+ #puts "LE CRC:\t#{crc.inspect}"
crc = crc & 0x7F7F # Get a CRC14 by masking out the 7th and 15th bits
- puts "Masked LE CRC:\t#{crc.inspect}"
+ #puts "Masked LE CRC:\t#{crc.inspect}"
crc
end
# Normal CRC16 calculation
def self.crc16(buf)
crc = 0x00
buf.each_byte do |b|
+ #puts "Byte #{b.inspect}"
crc = ((crc >> 8) & 0xff) ^ CRC_LOOKUP[(crc ^ b) & 0xff]
end
crc
end
# Converts BigEndian to LittleEndian
def self.convertLEBytesToNative( bytes )
- puts "bytes:\n#{bytes.inspect}"
+ # Check to see if this machine is already little endian
if ( [1].pack('V').unpack('l').first == 1 )
- # machine is already little endian
+ # Machine is already little endian"
+ #puts "Machine is already little endian"
bytes.unpack('l')
else
# machine is big endian
+ #puts "Machine is big endian, so convert"
convertLEToNative( Array(bytes.unpack('l')))
end
end
@@ -45,6 +52,7 @@ def self.convertLEBytesToNative( bytes )
# opposite effect (converting from little-endian to big-endian), which is what we want. In both cases, the
# unpack('l') just produces a signed integer from those bytes, in the machine's native endianess.
def self.convertLEToNative( num )
+ #puts "Converting to little endian"
Array(num).pack('V').unpack('l')
end
View
213 lib/ekm-omnimeter/logger.rb
@@ -0,0 +1,213 @@
+require 'logging'
+
+module EkmOmnimeter
+
+ @@global_logger = nil
+
+ # Returns true or false, reflecting whether a global logger is configured
+ def self.global_logger_configured?
+ (not @@global_logger.nil?)
+ end
+
+ # Returns true or false, reflecting whether a global logger is configured
+ def self.configure_global_logger(options={})
+
+ # Collect and validate log level
+ log_level = options[:log_level] || :debug
+ raise "Can not initialize global logger, because #{log_level.inspect} is an unrecognized log level." unless [:off, :all, :debug, :info, :warn, :error, :fatal].include?(log_level)
+ Logging.logger.root.level = log_level
+
+ # When set to true backtraces will be written to the logs
+ trace_exceptions = options[:trace_exceptions] || true
+ Logging.logger.root.trace = trace_exceptions
+
+ # Setup colorized output for stdout (this scheme was setup for a terminal with a black background)
+ # :black, :red, :green, :yellow, :blue, :magenta, :cyan, :white
+ # :on_black, :on_red, :on_green, :on_yellow, :on_blue, :on_magenta, :on_cyan, :on_white
+ # :blink, :bold, :underline, :underscore
+ stdout_colors = options[:stdout_colors] || {:levels => {
+ :debug => :white,
+ :info => [:white, :on_blue, :bold],
+ :warn => [:black, :on_yellow, :bold] ,
+ :error => [:white, :on_red, :bold],
+ :fatal => [:white, :on_red, :bold, :blink]
+ },
+ :date => :yellow,
+ :logger => :cyan,
+ :message => :white}
+ Logging.color_scheme('stdout_colors', stdout_colors)
+
+ # Always log info to stdout
+ log_to_stdout = options[:log_to_stdout] || true
+ if log_to_stdout
+ Logging.logger.root.add_appenders Logging.appenders.stdout(
+ 'stdout',
+ :layout => Logging.layouts.pattern(
+ #:pattern => '[%d] %-5l %c: %m\n',
+ :color_scheme => 'stdout_colors'
+ )
+ )
+ end
+
+ if options[:log_file]
+
+ # Make sure log directory exists
+ log_file = File.expand_path(options[:log_file])
+ log_dir = File.dirname(log_file)
+ raise "The log file can not be created, because its directory does not exist #{log_dir}" if Dir.exists?(log_dir)
+
+ # Determine layout. The available layouts are :basic, :json, :yaml, or a pattern such as '[%d] %-5l: %m\n'
+ layout = options[:log_file_layout] || :basic
+ if options[:log_file_layout]
+ if layout == :basic
+ use_layout = Logging.layouts.basic
+ elsif layout == :json
+ use_layout = Logging.layouts.json
+ elsif layout == :yaml
+ use_layout = Logging.layouts.yaml
+ else
+ use_layout = Logging.layouts.pattern(:pattern => layout) # '[%d] %-5l: %m\n'
+ end
+ end
+
+ # Determine if this should be a single or rolling log file
+ rolling = options[:rolling_log_file_age] || false
+
+ # Build the file appender
+ if rolling
+ Logging.logger.root.add_appenders Logging.appenders.rolling_file(
+ 'development.log',
+ :age => rolling,
+ :layout => use_layout
+ )
+ else
+ # Non-rolling log file
+ Logging.logger.root.add_appenders Logging.appenders.file(log_file, :layout => use_layout)
+ end
+
+ # Growl on error
+ growl_on_error = options[:growl_on_error] || false
+ if options[:growl_on_error]
+ Logging.logger.root.add_appenders Logging.appenders.growl(
+ 'growl',
+ :level => :error,
+ :layout => Logging.layouts.pattern(:pattern => '[%d] %-5l: %m\n')
+ )
+ end
+
+ end
+ # Return the root logger
+ Logging.logger.root
+ end
+
+
+ module Logger
+
+ def logger
+ @logger ||= EkmOmnimeter::Logger.logger
+ end
+
+ def self.logger
+ @logger ||= self.configure_logger_for(self.class.name)
+ end
+
+ def self.configure_logger_for(classname)
+ EkmOmnimeter.configure_global_logger() unless EkmOmnimeter.global_logger_configured?
+ l = Logging.logger[classname]
+ end
+
+ #def self.configure_logger_for(classname, options={})
+ #
+ # # Create the logger
+ # l = Logging.logger[classname]
+ #
+ # # Collect and validate log level
+ # log_level = options[:log_level] || :debug
+ # raise "Can not initalize global logger, because #{log_level.inspect} is an unrecognized log level." unless [:off, :all, :debug, :info, :warn, :error, :fatal].include?(log_level)
+ # Logging.logger.root.level = log_level
+ #
+ # # When set to true backtraces will be written to the logs
+ # trace_exceptions = options[:trace_exceptions] || true
+ # l.trace = trace_exceptions
+ #
+ # # Setup colorized output for stdout (this scheme was setup for a terminal with a black background)
+ # # :black, :red, :green, :yellow, :blue, :magenta, :cyan, :white
+ # # :on_black, :on_red, :on_green, :on_yellow, :on_blue, :on_magenta, :on_cyan, :on_white
+ # # :blink, :bold, :underline, :underscore
+ # stdout_colors = options[:stdout_colors] || {:levels => {
+ # :debug => :white,
+ # :info => [:white, :on_blue, :bold],
+ # :warn => [:black, :on_yellow, :bold] ,
+ # :error => [:white, :on_red, :bold],
+ # :fatal => [:white, :on_red, :bold, :blink]
+ # },
+ # :date => :yellow,
+ # :logger => :cyan,
+ # :message => :white}
+ # Logging.color_scheme('stdout_colors', stdout_colors)
+ #
+ # # Always log info to stdout
+ # log_to_stdout = options[:log_to_stdout] || true
+ # if log_to_stdout
+ # l.add_appenders Logging.appenders.stdout(
+ # 'stdout',
+ # :layout => Logging.layouts.pattern(
+ # #:pattern => '[%d] %-5l %c: %m\n',
+ # :color_scheme => 'stdout_colors'
+ # )
+ # )
+ # end
+ #
+ # if options[:log_file]
+ #
+ # # Make sure log directory exists
+ # log_file = File.expand_path(options[:log_file])
+ # log_dir = File.dirname(log_file)
+ # raise "The log file can not be created, because its directory does not exist #{log_dir}" if Dir.exists?(log_dir)
+ #
+ # # Determine layout. The available layouts are :basic, :json, :yaml, or a pattern such as '[%d] %-5l: %m\n'
+ # layout = options[:log_file_layout] || :basic
+ # if options[:log_file_layout]
+ # if layout == :basic
+ # use_layout = Logging.layouts.basic
+ # elsif layout == :json
+ # use_layout = Logging.layouts.json
+ # elsif layout == :yaml
+ # use_layout = Logging.layouts.yaml
+ # else
+ # use_layout = Logging.layouts.pattern(:pattern => layout) # '[%d] %-5l: %m\n'
+ # end
+ # end
+ #
+ # # Determine if this should be a single or rolling log file
+ # rolling = options[:rolling_log_file_age] || false
+ #
+ # # Build the file appender
+ # if rolling
+ # l.add_appenders Logging.appenders.rolling_file(
+ # 'development.log',
+ # :age => rolling,
+ # :layout => use_layout
+ # )
+ # else
+ # # Non-rolling log file
+ # l.add_appenders Logging.appenders.file(log_file, :layout => use_layout)
+ # end
+ #
+ # # Growl on error
+ # growl_on_error = options[:growl_on_error] || false
+ # if options[:growl_on_error]
+ # l.add_appenders Logging.appenders.growl(
+ # 'growl',
+ # :level => :error,
+ # :layout => Logging.layouts.pattern(:pattern => '[%d] %-5l: %m\n')
+ # )
+ # end
+ #
+ # end
+ #
+ # l
+ #
+ end
+
+end
View
25 lib/ekm-omnimeter/logging.rb
@@ -1,25 +0,0 @@
-require 'log4r'
-include Log4r
-module EkmOmnimeter
-
- module Logging
-
- def logger
- @logger ||= EkmOmnimeter::Logging.logger
- end
-
- def self.logger
- @logger ||= self.configure_logger_for(self.class.name)
- end
-
- def self.configure_logger_for(classname)
- l = Logger.new(classname)
- l.level = ERROR
- l.trace = false
- l.add Log4r::Outputter.stderr
- l
- end
-
- end
-
-end
View
13 lib/ekm-omnimeter/meter.rb
@@ -82,21 +82,22 @@ class Meter
attr_reader :meter_number, :remote_address, :remote_port, :verify_checksums, :power_configuration, :last_read_timestamp
# Mix in the ability to log
- include Logging
+ include Logger
def initialize(options)
+ puts "Initializing Meter"
+
@logger = logger || options[:logger]
- @logger.info "Initializing Meter"
+ puts "@logger = #{@logger}"
# Prepend the meter number with the correct amount of leading zeros
@meter_number = options[:meter_number].to_s.rjust(12, '0')
@remote_address = options[:remote_address] || '192.168.0.125'
@remote_port = options[:remote_port] || 50000
- @verify_checksums = options[:verify_checksums] || true
- @logger.debug "meter_number: #{meter_number}"
- @logger.debug "remote_address: #{remote_address}"
- @logger.debug "remote_port: #{remote_port}"
+ @logger.debug "Initialize meter #{meter_number} at #{remote_address}:#{remote_port}"
+
+ @verify_checksums = options[:verify_checksums] || false # TODO: CRC Checksum isn't working, so default is off
@logger.debug "verify_checksums: #{verify_checksums}"
# Collect the power configurations

0 comments on commit b62a2e6

Please sign in to comment.
Something went wrong with that request. Please try again.