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.
@@ -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