ekm-omnimeter 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -25
- data/README.md +8 -8
- data/ekm-omnimeter.gemspec +3 -1
- data/lib/ekm-omnimeter.rb +1 -0
- data/lib/ekm-omnimeter/crc16.rb +54 -0
- data/lib/ekm-omnimeter/meter.rb +64 -88
- data/lib/ekm-omnimeter/version.rb +1 -1
- metadata +2 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8683533fedec1693dfa676899772275fe0ae881a
|
4
|
+
data.tar.gz: 0e2a8333af1cdedd8e41a4e8f5313f49124cd104
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 732818ce1410339b035a8c070bc8aab692aec256b29c9ecb7d0f4f46e92b6689097a26147706585767d5e0d72296f52db39cb736ce412a0ac3f6cdcb313b3843
|
7
|
+
data.tar.gz: ff897d4aa8a55847833cb7c6775f0947347ca0e48b30e04580139174fd570d95c638f66fe8088667bed2d6d2402c5d830cc86a9ab22407c60c267dd9c20dc8de
|
data/Gemfile.lock
CHANGED
@@ -1,26 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ekm-omnimeter (0.2.
|
4
|
+
ekm-omnimeter (0.2.4)
|
5
5
|
log4r (~> 1.1)
|
6
|
-
xively-rb-connector (~> 0.1)
|
7
6
|
|
8
7
|
GEM
|
9
8
|
remote: https://rubygems.org/
|
10
9
|
specs:
|
11
|
-
bigdecimal (1.2.5)
|
12
10
|
diff-lcs (1.2.5)
|
13
|
-
httparty (0.13.0)
|
14
|
-
json (~> 1.8)
|
15
|
-
multi_xml (>= 0.5.2)
|
16
|
-
json (1.8.1)
|
17
11
|
log4r (1.1.10)
|
18
|
-
mini_portile (0.5.3)
|
19
|
-
multi_json (1.9.2)
|
20
|
-
multi_xml (0.5.5)
|
21
|
-
nokogiri (1.6.1)
|
22
|
-
mini_portile (~> 0.5.0)
|
23
|
-
ox (2.1.1)
|
24
12
|
rake (10.2.2)
|
25
13
|
rspec (2.14.1)
|
26
14
|
rspec-core (~> 2.14.0)
|
@@ -30,18 +18,6 @@ GEM
|
|
30
18
|
rspec-expectations (2.14.5)
|
31
19
|
diff-lcs (>= 1.1.3, < 2.0)
|
32
20
|
rspec-mocks (2.14.6)
|
33
|
-
xively-rb (0.2.10)
|
34
|
-
httparty (>= 0.10.0)
|
35
|
-
multi_json (>= 1.3.6)
|
36
|
-
multi_xml (>= 0.5.2)
|
37
|
-
nokogiri (>= 1.5.6)
|
38
|
-
ox (>= 1.5.9)
|
39
|
-
yajl-ruby (>= 1.1.0)
|
40
|
-
xively-rb-connector (0.1.3)
|
41
|
-
bigdecimal (~> 1.2)
|
42
|
-
log4r (~> 1.1)
|
43
|
-
xively-rb (~> 0.2)
|
44
|
-
yajl-ruby (1.2.0)
|
45
21
|
|
46
22
|
PLATFORMS
|
47
23
|
ruby
|
data/README.md
CHANGED
@@ -49,16 +49,16 @@ m = EkmOmnimeter::Meter.new(
|
|
49
49
|
:remote_port => 50000) # The port on which your iSerial device is listening
|
50
50
|
|
51
51
|
# These values change based on the value of :power_configuration in the constructor
|
52
|
+
m.meter_number # 000300000234
|
53
|
+
m.remote_address # 192.168.0.125
|
54
|
+
m.remote_port # 50000
|
52
55
|
m.volts # 248.7
|
53
56
|
m.amps # 21.6
|
54
57
|
m.watts # 2664
|
55
58
|
|
56
|
-
# Warning: Dates seem off
|
57
|
-
# My computer clock said 2014-04-03 3:32PM when these outputs came out
|
58
|
-
m.meter_timestamp # 2014-04-03 05:15:32
|
59
|
-
m.computer_timestamp # Time.now() = 2014-04-03 15:32:10 -0400
|
60
|
-
|
61
59
|
# Read meter values
|
60
|
+
m.meter_timestamp # 2014-04-04 12:48:06
|
61
|
+
m.last_read_timestamp # 2014-04-04 12:48:17 -0400
|
62
62
|
m.remote_address # 192.168.1.125
|
63
63
|
m.remote_port # 50000
|
64
64
|
m.meter_number # 000300000001
|
@@ -85,9 +85,9 @@ m.watts_l1 # 2276
|
|
85
85
|
m.watts_l2 # 384
|
86
86
|
m.watts_l3 # 0
|
87
87
|
m.watts_total # 2664
|
88
|
-
m.power_factor_1
|
89
|
-
m.power_factor_2
|
90
|
-
m.power_factor_3
|
88
|
+
m.power_factor_1 # 1.0
|
89
|
+
m.power_factor_2 # C0.99
|
90
|
+
m.power_factor_3 # C0.0
|
91
91
|
m.maximum_demand # 22640.0
|
92
92
|
m.maximum_demand_period # 15 (in minutes, can be 15, 30 or 60)
|
93
93
|
m.ct_ratio # 400
|
data/ekm-omnimeter.gemspec
CHANGED
@@ -25,7 +25,9 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
# Runtime dependencies
|
27
27
|
spec.add_runtime_dependency "log4r", "~> 1.1"
|
28
|
-
spec.add_runtime_dependency "xively-rb-connector", "~> 0.1"
|
29
28
|
|
29
|
+
#spec.add_runtime_dependency "xively-rb-connector", "~> 0.1"
|
30
|
+
#spec.add_runtime_dependency "trollop"
|
31
|
+
#spec.add_runtime_dependency "daemons"
|
30
32
|
|
31
33
|
end
|
data/lib/ekm-omnimeter.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Crc16
|
2
|
+
|
3
|
+
# Returns boolean true or false if checksum of string s matches expected value
|
4
|
+
def self.check_crc16(s, expecting)
|
5
|
+
(Crc16.crc16(s) == expecting)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Calculates CRC16 checksum of string buf
|
9
|
+
def self.crc16(buf)
|
10
|
+
crc = 0x00
|
11
|
+
buf.each_byte do |b|
|
12
|
+
crc = ((crc >> 8) & 0xff) ^ CRC_LOOKUP[(crc ^ b) & 0xff]
|
13
|
+
end
|
14
|
+
crc
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
CRC_LOOKUP = [
|
20
|
+
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
21
|
+
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
22
|
+
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
23
|
+
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
24
|
+
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
25
|
+
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
26
|
+
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
27
|
+
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
28
|
+
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
29
|
+
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
30
|
+
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
31
|
+
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
32
|
+
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
33
|
+
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
34
|
+
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
35
|
+
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
36
|
+
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
37
|
+
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
38
|
+
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
39
|
+
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
40
|
+
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
41
|
+
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
42
|
+
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
43
|
+
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
44
|
+
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
45
|
+
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
46
|
+
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
47
|
+
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
48
|
+
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
49
|
+
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
50
|
+
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
51
|
+
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
|
52
|
+
]
|
53
|
+
|
54
|
+
end
|
data/lib/ekm-omnimeter/meter.rb
CHANGED
@@ -13,7 +13,7 @@ module EkmOmnimeter
|
|
13
13
|
VALID_POWER_CONFIGURATIONS = [:single_phase_2wire, :single_phase_3wire, :three_phase_3wire, :three_phase_4wire]
|
14
14
|
|
15
15
|
# Initialization attributes
|
16
|
-
attr_reader :meter_number, :remote_address, :remote_port, :power_configuration, :last_read_timestamp
|
16
|
+
attr_reader :meter_number, :remote_address, :remote_port, :verify_checksums, :power_configuration, :last_read_timestamp
|
17
17
|
|
18
18
|
# Request A
|
19
19
|
#attr_reader :meter_type, :meter_firmware, :address, :total_active_kwh, :total_kvarh, :total_rev_kwh, :three_phase_kwh, :three_phase_rev_kwh, :resettable_kwh, :resettable_reverse_kwh, :volts_l1, :volts_l2, :volts_l3, :amps_l1, :amps_l2, :amps_l3, :watts_l1, :watts_l2, :watts_l3, :watts_total, :cosϴ_l1, :cosϴ_l2, :cosϴ_l3, :var_l1, :var_l2, :var_l3, :var_total, :freq, :pulse_count_1, :pulse_count_2, :pulse_count_3, :pulse_input_hilo, :direction_of_current, :outputs_onoff, :kwh_data_decimal_places,
|
@@ -21,6 +21,9 @@ module EkmOmnimeter
|
|
21
21
|
# Request B
|
22
22
|
#attr_reader :t1_t2_t3_t4_kwh, :t1_t2_t3_t4_rev_kwh, :maximum_demand, :maximum_demand_time, :pulse_ratio_1, :pulse_ratio_2, :pulse_ratio_3, :ct_ratio, :auto_reset_md, :settable_imp_per_kWh_constant
|
23
23
|
|
24
|
+
# iSerial v4 Spec From http://documents.ekmmetering.com/Omnimeter-Pulse-v.4-Protocol.pdf
|
25
|
+
# %w(01 52 31 02 30 30 31 31 28 29 03 13 16).map{|a| a.to_i(16).chr}.join
|
26
|
+
|
24
27
|
# Mix in the ability to log
|
25
28
|
include Logging
|
26
29
|
|
@@ -32,16 +35,19 @@ module EkmOmnimeter
|
|
32
35
|
@logger.info "Initializing Meter"
|
33
36
|
|
34
37
|
# Prepend the meter number with the correct amount of leading zeros
|
35
|
-
@meter_number
|
36
|
-
@remote_address
|
37
|
-
@remote_port
|
38
|
+
@meter_number = options[:meter_number].to_s.rjust(12, '0')
|
39
|
+
@remote_address = options[:remote_address] || '192.168.0.125'
|
40
|
+
@remote_port = options[:remote_port] || 50000
|
41
|
+
@verify_checksums = options[:verify_checksums] || false
|
38
42
|
@logger.debug "meter_number: #{meter_number}"
|
39
43
|
@logger.debug "remote_address: #{remote_address}"
|
40
44
|
@logger.debug "remote_port: #{remote_port}"
|
45
|
+
@logger.debug "verify_checksums: #{verify_checksums}"
|
41
46
|
|
42
47
|
# Collect the power configurations
|
43
48
|
if VALID_POWER_CONFIGURATIONS.index(options[:power_configuration])
|
44
49
|
@power_configuration = options[:power_configuration]
|
50
|
+
@logger.debug "power_configuration: #{@power_configuration}"
|
45
51
|
else
|
46
52
|
raise EkmOmnimeterError, "Invalid power configuration #{options[:power_configuration]}. Valid values are #{VALID_POWER_CONFIGURATIONS.join(', ')}"
|
47
53
|
end
|
@@ -68,29 +74,21 @@ module EkmOmnimeter
|
|
68
74
|
@values
|
69
75
|
end
|
70
76
|
|
71
|
-
# Formatted datetime reported by meter during last read
|
72
|
-
def meter_timestamp
|
73
|
-
"20#{current_time[0,2]}-#{current_time[2,2]}-#{current_time[4,2]} #{current_time[6,2]}:#{current_time[ 8,2]}:#{current_time[10,2]}"
|
74
|
-
end
|
75
77
|
|
76
78
|
# Attribute handler that delegates attribute reads to the values hash
|
77
79
|
def method_missing(method_sym, *arguments, &block)
|
78
|
-
|
79
|
-
#@logger.debug "method_missing #{method_sym.inspect}"
|
80
|
-
|
81
80
|
# Only refresh data if its more than 0.25 seconds old
|
82
81
|
et = @last_read_timestamp.nil? ? 0 : (Time.now - @last_read_timestamp)
|
83
|
-
|
82
|
+
@logger.debug "Elapsed time since last read #{et}"
|
84
83
|
if et > 250
|
85
|
-
@logger.info "More than 250 milliseconds have passed
|
84
|
+
@logger.info "More than 250 milliseconds have passed since last read. Triggering refresh."
|
86
85
|
read()
|
87
86
|
end
|
88
87
|
|
89
88
|
if @values.include? method_sym
|
90
|
-
#logger.debug "Found #{method_sym}"
|
91
89
|
@values[method_sym]
|
92
90
|
else
|
93
|
-
|
91
|
+
@logger.debug "method_missing failed to find #{method_sym} in the Meter.values cache"
|
94
92
|
super
|
95
93
|
end
|
96
94
|
end
|
@@ -105,59 +103,6 @@ module EkmOmnimeter
|
|
105
103
|
end
|
106
104
|
|
107
105
|
|
108
|
-
## Request A
|
109
|
-
#d[:meter_type] # 2 Byte Meter Type
|
110
|
-
#d[:meter_firmware] # 1 Byte Meter Firmware
|
111
|
-
#d[:address] # 12 Bytes Address
|
112
|
-
#d[:total_active_kwh] # 8 Bytes total Active kWh
|
113
|
-
#d[:total_kvarh] # 8 Bytes Total kVARh
|
114
|
-
#d[:total_rev_kwh] # 8 Bytes Total Rev.kWh
|
115
|
-
#d[:three_phase_kwh] # 24 Bytes 3 phase kWh
|
116
|
-
#d[:three_phase_rev_kwh] # 24 Bytes 3 phase Rev.kWh
|
117
|
-
#d[:resettable_kwh] # 8 Bytes Resettable kWh
|
118
|
-
#d[:resettable_reverse_kwh] # 8 bytes Resettable Reverse kWh
|
119
|
-
#d[:volts_l1] # 4 Bytes Volts L1
|
120
|
-
#d[:volts_l2] # 4 Bytes Volts L2
|
121
|
-
#d[:volts_l3] # 4 Bytes Volts L3
|
122
|
-
#d[:amps_l1] # 5 Bytes Amps L1
|
123
|
-
#d[:amps_l2] # 5 Bytes Amps L2
|
124
|
-
#d[:amps_l3] # 5 Bytes Amps L3
|
125
|
-
#d[:watts_l1] # 7 Bytes Watts L1
|
126
|
-
#d[:watts_l2] # 7 Bytes Watts L2
|
127
|
-
#d[:watts_l3] # 7 Bytes Watts L3
|
128
|
-
#d[:watts_total] # 7 Bytes Watts Total
|
129
|
-
#d[:cosϴ_l1] # 4 Bytes Cosϴ L1
|
130
|
-
#d[:cosϴ_l2] # 4 Bytes Cosϴ L2
|
131
|
-
#d[:cosϴ_l3] # 4 Bytes Cosϴ L3
|
132
|
-
#d[:var_l1] # 7 Bytes VAR L1
|
133
|
-
#d[:var_l2] # 7 Bytes VAR L2
|
134
|
-
#d[:var_l3] # 7 Bytes VAR L3
|
135
|
-
#d[:var_total] # 7 Bytes VAR Total
|
136
|
-
#d[:freq] # 4 Bytes Freq
|
137
|
-
#d[:pulse_count_1] # 8 Bytes Pulse Count 1
|
138
|
-
#d[:pulse_count_2] # 8 Bytes Pulse Count 2
|
139
|
-
#d[:pulse_count_3] # 8 Bytes Pulse Count 3
|
140
|
-
#d[:pulse_input_hilo] # 1 Byte Pulse Input Hi/Lo
|
141
|
-
#d[:direction_of_current] # 1 Bytes direction of current
|
142
|
-
#d[:outputs_onoff] # 1 Byte Outputs On/Off
|
143
|
-
#d[:kwh_data_decimal_places] # 1 Byte kWh Data Decimal Places
|
144
|
-
|
145
|
-
## Request B
|
146
|
-
#d[:t1_t2_t3_t4_kwh] # 32 Bytes T1, T2, T3, T4 kwh
|
147
|
-
#d[:t1_t2_t3_t4_rev_kwh] # 32 Bytes T1, T2, T3, T4 Rev kWh
|
148
|
-
#d[:maximum_demand] # 8 Bytes Maximum Demand
|
149
|
-
#d[:maximum_demand_time] # 1 Byte Maximum Demand Time
|
150
|
-
#d[:pulse_ratio_1] # 4 Bytes Pulse Ratio 1
|
151
|
-
#d[:pulse_ratio_2] # 4 Bytes Pulse Ratio 2
|
152
|
-
#d[:pulse_ratio_3] # 4 Bytes Pulse Ratio 3
|
153
|
-
#d[:ct_ratio] # 4 Bytes CT Ratio
|
154
|
-
#d[:auto_reset_md] # 1 Bytes Auto Reset MD
|
155
|
-
#d[:settable_imp_per_kWh_constant] # 4 Bytes Settable Imp/kWh Constant
|
156
|
-
|
157
|
-
|
158
|
-
# iSerial v4 Spec From http://documents.ekmmetering.com/Omnimeter-Pulse-v.4-Protocol.pdf
|
159
|
-
# %w(01 52 31 02 30 30 31 31 28 29 03 13 16).map{|a| a.to_i(16).chr}.join
|
160
|
-
|
161
106
|
# Returns the correct measurement for voltage, current, and power based on the corresponding power_configuration
|
162
107
|
def calculate_measurement(m1, m2, m3)
|
163
108
|
if power_configuration == :single_phase_2wire
|
@@ -185,6 +130,20 @@ module EkmOmnimeter
|
|
185
130
|
end
|
186
131
|
end
|
187
132
|
|
133
|
+
# Power factor values come back as C099 which need to be cast to C0.99
|
134
|
+
def cast_power_factor(s)
|
135
|
+
"#{s[0]}#{s[1,3].to_f / 100.0}"
|
136
|
+
end
|
137
|
+
|
138
|
+
# Formatted datetime reported by meter during last read.
|
139
|
+
# Raw string is formatted as YYMMDDWWHHMMSS where YY is year without century, and WW is week day with Sunday as the first day of the week
|
140
|
+
#"20#{current_time[0,2]}-#{current_time[2,2]}-#{current_time[4,2]} #{current_time[8,2]}:#{current_time[ 10,2]}:#{current_time[12,2]}"
|
141
|
+
def as_datetime(s)
|
142
|
+
DateTime.new("20#{s[0,2]}".to_i, s[2,2].to_i, s[4,2].to_i, s[8,2].to_i, s[10,2].to_i, s[12,2].to_i, '-4')
|
143
|
+
end
|
144
|
+
|
145
|
+
# All values are returned without decimals. This method loops over all
|
146
|
+
# the values and sets them to the correct precision
|
188
147
|
def cast_response_to_correct_types(d)
|
189
148
|
|
190
149
|
# Integers
|
@@ -224,10 +183,7 @@ module EkmOmnimeter
|
|
224
183
|
end
|
225
184
|
|
226
185
|
# Floats with precision 2
|
227
|
-
[:
|
228
|
-
:power_factor_2,
|
229
|
-
:power_factor_3,
|
230
|
-
:frequency
|
186
|
+
[:frequency
|
231
187
|
].each do |k|
|
232
188
|
logger.debug "Casting #{k}"
|
233
189
|
d[k] = to_f_with_decimal_places(d[k], 2) if d.has_key?(k)
|
@@ -329,20 +285,31 @@ module EkmOmnimeter
|
|
329
285
|
d[:outputs_onoff] = a.shift(1) # 1 Byte Outputs On/Off
|
330
286
|
d[:kwh_data_decimal_places] = a.shift(1) # 1 Byte kWh Data Decimal Places
|
331
287
|
a.shift(2) # 2 Bytes Reserved
|
332
|
-
|
288
|
+
meter_timestamp = a.shift(14).join('') # 14 Bytes Current Time
|
333
289
|
a.shift(6) # 30 30 21 0D 0A 03
|
334
|
-
d[:
|
290
|
+
d[:checksum] = a.shift(2) # 2 Bytes CRC16
|
335
291
|
|
336
292
|
# Smash arrays into strungs
|
337
293
|
d.each {|k,v| d[k] = v.join('')}
|
338
294
|
|
295
|
+
if verify_checksums
|
296
|
+
if Crc16.check_crc16(response, d[:checksum])
|
297
|
+
@logger.debug "Checksum matches"
|
298
|
+
else
|
299
|
+
@logger.error "CRC16 Checksum doesn't match. Expecting #{d[:checksum]} but was #{Crc16.crc16(response)}"
|
300
|
+
#raise EkmOmnimeterError, "Checksum doesn't match"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
339
304
|
# Cast types
|
340
305
|
@values[:kwh_data_decimal_places] = d[:kwh_data_decimal_places].to_i
|
306
|
+
d[:power_factor_1] = cast_power_factor(d[:power_factor_1])
|
307
|
+
d[:power_factor_2] = cast_power_factor(d[:power_factor_2])
|
308
|
+
d[:power_factor_3] = cast_power_factor(d[:power_factor_3])
|
309
|
+
d[:meter_timestamp] = as_datetime(meter_timestamp)
|
341
310
|
cast_response_to_correct_types(d)
|
342
311
|
|
343
312
|
# Lookup mapped values
|
344
|
-
puts "d[:pulse_input_hilo] = #{d[:pulse_input_hilo].inspect}"
|
345
|
-
|
346
313
|
d[:pulse_1_input], d[:pulse_2_input], d[:pulse_3_input] = lookup_pulse_input_states(d[:pulse_input_hilo])
|
347
314
|
d[:current_direction_l1], d[:current_direction_l2], d[:current_direction_l3] = lookup_direction_of_current(d[:direction_of_current])
|
348
315
|
d[:output_1], d[:output_2] = lookup_output_states(d[:outputs_onoff])
|
@@ -352,7 +319,6 @@ module EkmOmnimeter
|
|
352
319
|
@last_read_timestamp = Time.now
|
353
320
|
|
354
321
|
# Calculate totals based on wiring configuration
|
355
|
-
@values[:meter_timestamp] = meter_timestamp
|
356
322
|
@values[:volts] = calculate_measurement(volts_l1, volts_l2, volts_l3)
|
357
323
|
@values[:amps] = calculate_measurement(amps_l1, amps_l2, amps_l3)
|
358
324
|
@values[:watts] = calculate_measurement(watts_l1, watts_l2, watts_l3)
|
@@ -364,7 +330,6 @@ module EkmOmnimeter
|
|
364
330
|
|
365
331
|
end
|
366
332
|
|
367
|
-
|
368
333
|
# Request B
|
369
334
|
# TODO: Instead of pre-parsing and casting everything, refactor this so that only the response string gets saved, and parse out values that are accessed.
|
370
335
|
def request_b
|
@@ -428,14 +393,27 @@ module EkmOmnimeter
|
|
428
393
|
d[:settable_pulse_per_kwh_ratio] = a.shift(4) # 4 Bytes Settable Imp/kWh Constant
|
429
394
|
# Diff from request A end
|
430
395
|
a.shift(56) # 56 Bytes Reserved
|
431
|
-
|
396
|
+
meter_timestamp = a.shift(14).join('') # 14 Bytes Current Time
|
432
397
|
a.shift(6) # 30 30 21 0D 0A 03
|
433
|
-
d[:checksum] = a.shift(2)
|
398
|
+
d[:checksum] = a.shift(2) # 2 Bytes CRC16
|
434
399
|
|
435
400
|
# Smash arrays into strungs
|
436
401
|
d.each {|k,v| d[k] = v.join('')}
|
437
402
|
|
403
|
+
if verify_checksums
|
404
|
+
if Crc16.check_crc16(response, d[:checksum])
|
405
|
+
@logger.debug "Checksum matches"
|
406
|
+
else
|
407
|
+
@logger.error "CRC16 Checksum doesn't match. Expecting #{d[:checksum]} but was #{Crc16.crc16(response)}"
|
408
|
+
#raise EkmOmnimeterError, "Checksum doesn't match"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
438
412
|
# Cast types
|
413
|
+
d[:power_factor_1] = cast_power_factor(d[:power_factor_1])
|
414
|
+
d[:power_factor_2] = cast_power_factor(d[:power_factor_2])
|
415
|
+
d[:power_factor_3] = cast_power_factor(d[:power_factor_3])
|
416
|
+
d[:meter_timestamp] = as_datetime(meter_timestamp)
|
439
417
|
cast_response_to_correct_types(d)
|
440
418
|
|
441
419
|
# Lookup mapped values
|
@@ -447,7 +425,6 @@ module EkmOmnimeter
|
|
447
425
|
@last_read_timestamp = Time.now
|
448
426
|
|
449
427
|
# Calculate totals based on wiring configuration
|
450
|
-
@values[:meter_timestamp] = meter_timestamp
|
451
428
|
@values[:volts] = calculate_measurement(volts_l1, volts_l2, volts_l3)
|
452
429
|
@values[:amps] = calculate_measurement(amps_l1, amps_l2, amps_l3)
|
453
430
|
@values[:watts] = calculate_measurement(watts_l1, watts_l2, watts_l3)
|
@@ -458,7 +435,6 @@ module EkmOmnimeter
|
|
458
435
|
end
|
459
436
|
|
460
437
|
|
461
|
-
|
462
438
|
# Gets remote EKM meter data using iSerial defaults
|
463
439
|
# meter_number is the meters serial number. leading 0s not required.
|
464
440
|
# remote_address is the IP address of the ethernet-RS485 converter
|
@@ -473,24 +449,24 @@ module EkmOmnimeter
|
|
473
449
|
begin
|
474
450
|
|
475
451
|
socket = TCPSocket.new(remote_address, remote_port)
|
476
|
-
logger.debug "Socket open"
|
452
|
+
@logger.debug "Socket open"
|
477
453
|
|
478
454
|
# Send request to the meter
|
479
|
-
logger.debug "Request: #{request}"
|
455
|
+
@logger.debug "Request: #{request}"
|
480
456
|
socket.write(request)
|
481
457
|
|
482
458
|
# Receive a response of 255 bytes
|
483
459
|
response = socket.read(read_bytes)
|
484
|
-
logger.debug "Socket response #{response.length}"
|
485
|
-
logger.debug response
|
460
|
+
@logger.debug "Socket response #{response.length}"
|
461
|
+
@logger.debug response
|
486
462
|
|
487
463
|
rescue Exception => ex
|
488
|
-
logger.error "Exception\n#{ex.message}\n#{ex.backtrace.join("\n")}"
|
464
|
+
@logger.error "Exception\n#{ex.message}\n#{ex.backtrace.join("\n")}"
|
489
465
|
ensure
|
490
466
|
# EKM Meter software sends this just before closing the connection, so we will too
|
491
467
|
socket.write "\x0a\x03\x32\x3d"
|
492
468
|
socket.close
|
493
|
-
logger.debug "Socket closed"
|
469
|
+
@logger.debug "Socket closed"
|
494
470
|
end
|
495
471
|
|
496
472
|
return response
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ekm-omnimeter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Duggan
|
@@ -66,20 +66,6 @@ dependencies:
|
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.1'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: xively-rb-connector
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ~>
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0.1'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ~>
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0.1'
|
83
69
|
description: Ruby interface to the EKM Omnimeter Pulse
|
84
70
|
email:
|
85
71
|
- Jordan.Duggan@gmail.com
|
@@ -96,6 +82,7 @@ files:
|
|
96
82
|
- ekm-omnimeter.gemspec
|
97
83
|
- lib/ekm-omnimeter.rb
|
98
84
|
- lib/ekm-omnimeter/configuration.rb
|
85
|
+
- lib/ekm-omnimeter/crc16.rb
|
99
86
|
- lib/ekm-omnimeter/logging.rb
|
100
87
|
- lib/ekm-omnimeter/meter.rb
|
101
88
|
- lib/ekm-omnimeter/version.rb
|