dmm_util 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bin/dmm_util +127 -0
- data/dmm_util.gemspec +84 -0
- data/lib/dmm_util/cursor.rb +31 -0
- data/lib/dmm_util/fluke28x_driver.rb +440 -0
- data/lib/dmm_util/format_convertors.rb +46 -0
- data/lib/dmm_util/measurement.rb +64 -0
- data/lib/dmm_util/meter.rb +39 -0
- data/lib/dmm_util/reading.rb +64 -0
- data/lib/dmm_util/recording.rb +35 -0
- data/lib/dmm_util/recording_measurement.rb +62 -0
- data/lib/dmm_util/recording_measurement_cursor.rb +26 -0
- data/lib/dmm_util.rb +36 -0
- data/test/communication_test.rb +183 -0
- data/test/dmm_command_test.rb +489 -0
- data/test/format_convertors_test.rb +61 -0
- data/test/integration_itest.rb +290 -0
- data/test/measurement_test.rb +57 -0
- data/test/meter_test.rb +178 -0
- data/test/reading_test.rb +48 -0
- data/test/recording_measurement_test.rb +55 -0
- data/test/recording_test.rb +75 -0
- data/test/test_helper.rb +176 -0
- metadata +181 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module DmmUtil
|
4
|
+
|
5
|
+
class RecordingMeasurement
|
6
|
+
attr_reader :raw
|
7
|
+
|
8
|
+
def initialize(attrs)
|
9
|
+
@raw = attrs
|
10
|
+
end
|
11
|
+
|
12
|
+
def start_ts
|
13
|
+
raw[:start_ts]
|
14
|
+
end
|
15
|
+
|
16
|
+
def end_ts
|
17
|
+
raw[:end_ts]
|
18
|
+
end
|
19
|
+
|
20
|
+
def reading_names
|
21
|
+
(raw[:readings].keys + raw[:readings2].keys).map{|r| r.downcase.to_sym}
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
order = [:primary, :maximum, :average, :minimum, :rel_reference,
|
26
|
+
:secondary,
|
27
|
+
:db_ref, :temp_offset,
|
28
|
+
:live, :rel_live]
|
29
|
+
existing = reading_names
|
30
|
+
res = []
|
31
|
+
|
32
|
+
existing.delete(:live) if existing.include?(:live) && self.live == self.primary
|
33
|
+
|
34
|
+
(order - [:primary]).each do |name|
|
35
|
+
next unless existing.include?(name)
|
36
|
+
res << "#{name}: #{self.send(name).to_s}"
|
37
|
+
end
|
38
|
+
|
39
|
+
(existing - order).each do |name|
|
40
|
+
res << "#{name}: #{self.send(name).to_s}"
|
41
|
+
end
|
42
|
+
|
43
|
+
if res.empty?
|
44
|
+
primary.to_s
|
45
|
+
else
|
46
|
+
"#{primary.to_s} (#{res.join(", ")})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(meth, *args)
|
51
|
+
if raw[:readings].has_key?(meth.to_s.upcase)
|
52
|
+
Reading.new(raw[:readings][meth.to_s.upcase])
|
53
|
+
elsif raw[:readings2].has_key?(meth.to_s.upcase)
|
54
|
+
Reading.new(raw[:readings2][meth.to_s.upcase])
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DmmUtil
|
2
|
+
class RecordingMeasurementCursor
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(driver, recording)
|
7
|
+
@driver = driver
|
8
|
+
@recording = recording
|
9
|
+
end
|
10
|
+
|
11
|
+
def count
|
12
|
+
@recording.num_samples
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
(0..(count-1)).each do |idx|
|
17
|
+
yield(self[idx])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](idx)
|
22
|
+
RecordingMeasurement.new(@driver.qsrr(@recording.raw[:reading_index] ,idx))
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/dmm_util.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'dmm_util/format_convertors'
|
2
|
+
require 'dmm_util/fluke28x_driver'
|
3
|
+
require 'dmm_util/meter'
|
4
|
+
require 'dmm_util/cursor'
|
5
|
+
require 'dmm_util/recording'
|
6
|
+
require 'dmm_util/recording_measurement'
|
7
|
+
require 'dmm_util/recording_measurement_cursor'
|
8
|
+
require 'dmm_util/measurement'
|
9
|
+
require 'dmm_util/reading'
|
10
|
+
|
11
|
+
|
12
|
+
module DmmUtil
|
13
|
+
|
14
|
+
def self.open
|
15
|
+
driver = nil
|
16
|
+
Dir.glob("/dev/tty.usbserial*").each do |tty_path|
|
17
|
+
begin
|
18
|
+
driver = open_driver(tty_path)
|
19
|
+
rescue DmmUtil::MeterError
|
20
|
+
$stderr.write "Warning: Did not find meter at #{tty_path}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
raise "Could not find a valid meter, are you sure it is connected and turned on?" unless driver
|
24
|
+
Meter.new(driver)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.open_driver(tty_path)
|
28
|
+
port = SerialPort.new(tty_path, {"parity"=>0, "stop_bits"=>1, "baud"=>115200, "data_bits"=>8})
|
29
|
+
port.read_timeout = 1
|
30
|
+
meter = Fluke28xDriver.new(port)
|
31
|
+
|
32
|
+
raise MeterError.new("Device at #{tty_path} does not seem to be a supported DMM") unless meter.valid?
|
33
|
+
meter
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CommunicationTest < Test::Unit::TestCase
|
4
|
+
include DMMTestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@port = stub()
|
8
|
+
@port.stubs(:write)
|
9
|
+
@meter = DmmUtil::Fluke28xDriver.new(@port)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_open_driver
|
13
|
+
SerialPort.expects(:new).with("/some/path", {"parity"=>0, "stop_bits"=>1, "baud"=>115200, "data_bits"=>8}).returns(@port)
|
14
|
+
@port.expects(:read_timeout=).with(1)
|
15
|
+
DmmUtil::Fluke28xDriver.expects(:new).with(@port).returns(@meter)
|
16
|
+
@meter.expects(:valid?).returns(true)
|
17
|
+
|
18
|
+
assert_equal @meter, DmmUtil.open_driver("/some/path")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_open__invalid_meter
|
22
|
+
SerialPort.expects(:new).with("/some/path", {"parity"=>0, "stop_bits"=>1, "baud"=>115200, "data_bits"=>8}).returns(@port)
|
23
|
+
@port.expects(:read_timeout=).with(1)
|
24
|
+
DmmUtil::Fluke28xDriver.expects(:new).with(@port).returns(@meter)
|
25
|
+
@meter.expects(:valid?).returns(false)
|
26
|
+
|
27
|
+
assert_raise DmmUtil::MeterError do
|
28
|
+
DmmUtil.open_driver("/some/path")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_open__file_not_found
|
33
|
+
assert_raise Errno::ENOENT do
|
34
|
+
DmmUtil.open_driver("/some/nonexisting/path")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_valid
|
39
|
+
@meter.expects(:id).returns({:model_number => "goo", :software_version => "1.0.1", :serial_number => "12345"})
|
40
|
+
assert @meter.valid?
|
41
|
+
|
42
|
+
@meter.expects(:id).times(3).raises(DmmUtil::MeterError.new("Some error"))
|
43
|
+
assert !@meter.valid?
|
44
|
+
|
45
|
+
@meter.expects(:id).times(3).returns({:model_number => "goo", :software_version => nil, :serial_number => nil})
|
46
|
+
assert !@meter.valid?
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_valid__retry
|
50
|
+
id_seq = sequence('id_seq')
|
51
|
+
|
52
|
+
@meter.expects(:id).in_sequence(id_seq).raises(DmmUtil::MeterError.new("Some error"))
|
53
|
+
@meter.expects(:id).in_sequence(id_seq).returns({:model_number => "goo", :software_version => nil, :serial_number => nil})
|
54
|
+
@meter.expects(:id).in_sequence(id_seq).returns({:model_number => "goo", :software_version => "1.0.1", :serial_number => "12345"})
|
55
|
+
|
56
|
+
assert @meter.valid?
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_meter_command__ascii
|
60
|
+
@port.expects(:write).with("some long command\r")
|
61
|
+
@port.expects(:read).returns("0\r1,2,3\r")
|
62
|
+
|
63
|
+
assert_equal ["1", "2", "3"], @meter.meter_command("some long command")
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_meter_command__ascii_strings
|
67
|
+
@port.expects(:write).with("some other command\r")
|
68
|
+
@port.expects(:read).returns("0\r'val1',\"val2\",3\r")
|
69
|
+
|
70
|
+
assert_equal ["val1", "val2", "3"], @meter.meter_command("some other command")
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_meter_command__ascii_chopped
|
74
|
+
read_sequence = sequence(:read)
|
75
|
+
@port.expects(:read).in_sequence(read_sequence).returns("0\r'val1','val2',3")
|
76
|
+
@port.expects(:read).in_sequence(read_sequence).times(499).returns("")
|
77
|
+
@meter.stubs(:sleep)
|
78
|
+
|
79
|
+
assert_raise DmmUtil::MeterError do
|
80
|
+
@meter.meter_command("cmd")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_meter_command__tick_comma
|
85
|
+
@port.expects(:read).returns("0\r\"Valeur, - Fredrik\'s \"\r")
|
86
|
+
assert_equal ["Valeur, - Fredrik's "], @meter.meter_command("cmd")
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_meter_command__tick_and_quote
|
90
|
+
@port.expects(:read).returns("0\r'What about \"quote''s\" in string'\r")
|
91
|
+
assert_equal ["What about \"quote's\" in string"], @meter.meter_command("cmd")
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_meter_command__binary
|
95
|
+
@port.expects(:read).returns("0\r#0\0\2\3\4\5\6\r")
|
96
|
+
assert_equal "\0\2\3\4\5\6", @meter.meter_command("cmd")
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_meter_command__binary_chopped
|
100
|
+
read_sequence = sequence(:read)
|
101
|
+
@port.expects(:read).in_sequence(read_sequence).returns("0\r#0\0\2\3\4\5\6")
|
102
|
+
@port.expects(:read).in_sequence(read_sequence).times(499).returns("")
|
103
|
+
@meter.stubs(:sleep)
|
104
|
+
|
105
|
+
assert_raise DmmUtil::MeterError do
|
106
|
+
@meter.meter_command("cmd")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_meter_command__wrong_strings
|
111
|
+
assert_raise DmmUtil::MeterError do
|
112
|
+
@port.expects(:read).returns("0\r1,string'\r")
|
113
|
+
@meter.meter_command("cmd")
|
114
|
+
end
|
115
|
+
|
116
|
+
assert_raise DmmUtil::MeterError do
|
117
|
+
@port.expects(:read).returns("0\r1,string\"\r")
|
118
|
+
@meter.meter_command("cmd")
|
119
|
+
end
|
120
|
+
|
121
|
+
assert_raise DmmUtil::MeterError do
|
122
|
+
@port.expects(:read).returns("0\r1,\"str\r")
|
123
|
+
@meter.meter_command("cmd")
|
124
|
+
end
|
125
|
+
|
126
|
+
assert_raise DmmUtil::MeterError do
|
127
|
+
@port.expects(:read).returns("0\r1'str\r")
|
128
|
+
@meter.meter_command("cmd")
|
129
|
+
end
|
130
|
+
|
131
|
+
assert_raise DmmUtil::MeterError do
|
132
|
+
@port.expects(:read).returns("0\r1'str'1\r")
|
133
|
+
@meter.meter_command("cmd")
|
134
|
+
end
|
135
|
+
|
136
|
+
assert_raise DmmUtil::MeterError do
|
137
|
+
@port.expects(:read).returns("0\r1\"str\"1\r")
|
138
|
+
@meter.meter_command("cmd")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_meter_command__error
|
143
|
+
@port.expects(:read).returns("2\r")
|
144
|
+
error_raised = false
|
145
|
+
|
146
|
+
begin
|
147
|
+
@meter.meter_command("cmd")
|
148
|
+
rescue DmmUtil::MeterError => e
|
149
|
+
error_raised = true
|
150
|
+
assert_equal 2, e.status
|
151
|
+
assert_equal "Command returned error code 2", e.message
|
152
|
+
end
|
153
|
+
|
154
|
+
assert error_raised, "Error was not raised"
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_meter_command__invalid_data
|
158
|
+
@port.expects(:read).returns("invalid!")
|
159
|
+
error_raised = false
|
160
|
+
|
161
|
+
begin
|
162
|
+
@meter.meter_command("cmd")
|
163
|
+
rescue DmmUtil::MeterError => e
|
164
|
+
error_raised = true
|
165
|
+
assert_equal nil, e.status
|
166
|
+
assert_equal "Error parsing status from meter (Non-OK status with extra data on end)", e.message
|
167
|
+
end
|
168
|
+
|
169
|
+
assert error_raised, "Error was not raised"
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_meter_command__retry_on_error_8
|
173
|
+
@port.expects(:write).with("command\r").times(3)
|
174
|
+
|
175
|
+
read_seq = sequence('read_seq')
|
176
|
+
@port.expects(:read).in_sequence(read_seq).returns("8\r")
|
177
|
+
@port.expects(:read).in_sequence(read_seq).returns("8\r")
|
178
|
+
@port.expects(:read).in_sequence(read_seq).returns("0\rresult\r")
|
179
|
+
|
180
|
+
assert_equal ['result'], @meter.meter_command("command")
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|