Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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

…. Disabled CRC16 check, because its not working.
  • Loading branch information...
commit b62a2e62209c330a090c78b855cdb44db8f0ea30 1 parent e4857d8
@jwtd authored
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,18 +2,22 @@ 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
@@ -21,6 +25,7 @@ def self.ekm_checksum(s)
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
@@ -28,12 +33,14 @@ def self.crc16(buf)
# 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
Please sign in to comment.
Something went wrong with that request. Please try again.