hparra / ruby-serialport

ruby-serialport is a Ruby library that provides a class for using RS-232 serial ports
http://rubygems.org/gems/serialport
GNU General Public License v2.0
246 stars 58 forks source link

#read_timeout= problem #20

Open atimin opened 14 years ago

atimin commented 14 years ago

Very strange behavior:

irb(main):003:0> sp.read_timeout => 0 irb(main):004:0> sp.read_timeout = 5 => 5 irb(main):005:0> sp.read_timeout => -1 irb(main):006:0> sp.read_timeout = 100 => 100 irb(main):007:0> sp.read_timeout => 100 irb(main):008:0> sp.read_timeout = 101 => 101 irb(main):009:0> sp.read_timeout => 0 Ubuntu 10.04 Ruby 1.8.7

hparra commented 14 years ago

flipback: Very strange. Are you just running through a loop? Can someone confirm on another POSIX system, e.g. Mac OS X, and Windows? HGP

hparra commented 14 years ago

Could you send me a functional test to duplicate this? Just a small file where I pass in the port address should be fine.

zeeed commented 14 years ago

confirmed on OS X 10.6.4 w/ 1.0.4

sp = SerialPort.new '/dev/tty.usbserial'

=> #SerialPort:0x10053f7e0

sp.read_timeout

=> 0

sp.read_timeout = 4

=> 4

sp.read_timeout

=> -1

sp.read_timeout= 4

=> 4

sp.read_timeout

=> -1

sp.read_timeout=7

=> 7

sp.read_timeout

=> -1

hparra commented 14 years ago

I think I see what's happening here. Poor design. Please confirm.

The documentation specifies that you can pass a millisecond value, but the truth is the underlying structure (POSIX anyway) can only receive tenths of a second (deciseconds), so there is some poor arithmetic done internally (strange magic number included). You need to pass in a value >= 100. The value that the ruby method returns is the same exact one you passed in, so it doesn't really tell you anything.

So to document flipback's output...

irb(main):003:0> sp.read_timeout => 0 # default value of timeout, VMIN = 1 & VTIME = 0

irb(main):004:0> sp.read_timeout = 5 => 5 # VMIN = 0 but VTIME = 0 due to poor arithmetic so ... irb(main):005:0> sp.read_timeout => -1 # timeout = -1

irb(main):006:0> sp.read_timeout = 100 => 100 # 100 / 100 = 1 tenth of a second irb(main):007:0> sp.read_timeout => 100 # so it works...

irb(main):008:0> sp.read_timeout = 101 => 101 # ... but ... irb(main):009:0> sp.read_timeout => 0 # I can't explain this!!!

I see AT LEAST two corrections here:

Open to suggestions since changing either or both these things has the potential to break systems out there.

hparra commented 14 years ago

I'm interested in the Windows behavior as well. If anyone can check I would appreciate it, but in looking at the code the millisecond value is passed straight to Windows, so it will be responsible for default behaviors.

zeeed commented 14 years ago

I can confirm your observations. The value appears to get rounded to deciseconds indeed. I do see a different outcome with my adapter when attempting to set a 101ms timeout.

OS X:

sp = SerialPort.new '/dev/tty.usbserial'
=> #
>> sp.read_timeout = 100
=> 100
>> sp.read_timeout
=> 100
>> sp.read_timeout = 101
=> 101
>> sp.read_timeout
=> 100
>> sp.read_timeout = 151
=> 151
>> sp.read_timeout
=> 200

Win 7

irb(main):004:0> sp = SerialPort.new(2)
=> #
irb(main):005:0> sp.read_timeout
=> -1
irb(main):006:0> sp.read_timeout=5
=> 5
irb(main):007:0> sp.read_timeout
=> 5
irb(main):008:0> sp.read_timeout=100
=> 100
irb(main):009:0> sp.read_timeout
=> 100
irb(main):010:0> sp.read_timeout=101
=> 101
irb(main):011:0> sp.read_timeout
=> 101
irb(main):012:0> sp.read_timeout=151
=> 151
irb(main):013:0> sp.read_timeout
=> 151
irb(main):014:0>
hparra commented 14 years ago

Yes, the magic number with internal arithmetic was 50 - used to round up to the nearest whole decisecond, so 150-249 ms became 200ms. I'm not sure how I feel about this, but changing the return value of the method to be the actual timeout value set would make even more sense because of it.

I'm curious why termios works this way. Seems like a legacy boo boo, or it unrealistic to set such precise timeouts?