ruby-serialport
ruby-serialport copied to clipboard
#read_timeout= problem
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
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
Could you send me a functional test to duplicate this? Just a small file where I pass in the port address should be fine.
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
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:
- read_timeout should return the value that underlying system is actually set to (not what was just passed in)
- there should be some default behavior for an invalid timeout (exception, retain old value, set lowest valid timeout), but what it's doing now is very wrong IMO.
Open to suggestions since changing either or both these things has the potential to break systems out there.
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.
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' => #<0x1010004e0>0x1010004e0>
<0x41bafa0>0x41bafa0>
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?