Increase timeout.rb timer within block - ruby

I'm communicating with an RS232 keypad that has an LCD. On each keypress I write the key that was pressed to the LCD to provide feedback to the user.
If no key is pressed within 10 seconds I'd like to abandon waiting for input.
I've written some code that will timeout if the user isn't done entering a multi-character value within 10 seconds, what I'd rather do is give the user another 10 seconds to complete input after each keypress.
Is this possible using timeout.rb?
require 'rubygems'
require 'serialport'
require 'timeout'
sp = SerialPort.new('/dev/tty.usbserial', 9600, 8, 1, SerialPort::NONE)
sp.write("Input:")
begin
timeout(10) do
input = ""
sp.each_byte do |byte|
#call to increase timeout.rb timer would go here
input << byte.chr
sp.write("Input:" + input)
end
end
rescue Timeout::Error
puts "Timed out!"
exit
end
puts input

I found an alternative way of achieving the desired effect by using Threads instead of timeout.rb but would be interested to learn alternative methods.
require 'rubygems'
require 'serialport'
sp = SerialPort.new('/dev/tty.usbserial', 9600, 8, 1, SerialPort::NONE)
sp.write("Input:")
input = ""
timer = Time.now + 10
t = Thread.new do
sp.each_byte do |byte|
timer = Time.now + 10
input << byte.chr
sp.write("Input:" + input)
end
end
t.kill unless Time.now < timer while t.alive?

Related

repeatedly read Ruby IO until X bytes have been read, Y seconds have elapsed, or EOF, whichever comes first

I want to forward logs from an IO pipe to an API. Ideally, there would be no more than e.g. 10 seconds of latency (so humans watching the log don't get impatient).
A naive way to accomplish this would be to use IO.each_byte and send each byte to the API as soon as it becomes available, but the overhead of processing a request per byte causes additional latency.
IO#each(limit) also gets close to what I want, but if the limit is 50 kB and after 10 seconds, only 20 kB has been read, I want to go ahead and send that 20 kB without waiting for more. How can I apply both a time and size limit simultaneously?
A naïve approach would be to use IO#each_byte enumerator.
The contrived, not tested example:
enum = io.each_byte
now = Time.now
res = while Time.now - now < 20 do
begin
send_byte enum.next
rescue e => StopIteration
# no more data
break :closed
end
end
puts "NO MORE DATA" if res == :closed
Here's what I ended up with. Simpler solutions still appreciated!
def read_chunks(io, byte_interval: 200 * 1024, time_interval: 5)
buffer = last = nil
reset = lambda do
buffer = ''
last = Time.now
end
reset.call
mutex = Mutex.new
cv = ConditionVariable.new
[
lambda do
IO.select [io]
mutex.synchronize do
begin
chunk = io.readpartial byte_interval
buffer.concat chunk
rescue EOFError
raise StopIteration
ensure
cv.signal
end
end
end,
lambda do
mutex.synchronize do
until io.eof? || Time.now > (last + time_interval) || buffer.length > byte_interval
cv.wait mutex, time_interval
end
unless buffer.empty?
buffer_io = StringIO.new buffer
yield buffer_io.read byte_interval until buffer_io.eof?
reset.call
end
raise StopIteration if io.eof?
end
end,
].map do |function|
Thread.new { loop { function.call } }
end.each(&:join)
end

Firefox Webdriver take so long to open on multithread

I Trying to open multiple instance of Firefox browser from my ruby code.
I use selenium 2.53.4 and firefox 47.0.2
The problem is after the thread has been created, the driver not initiate imidiately. it's took so long time until it's opened. And the second driver will opened after the first one is almost finished, this make multithread useless.
Here is my code
require "selenium-webdriver"
th = Array.new
i = 0
limit = 3
while i < 10
if(Thread.list.count <= 3)
th[i] = Thread.new(i){ |index|
start = Time.new
puts "#{index} - Start Initiate at #{start}"
driver = Selenium::WebDriver.for :firefox
finish = Time.new
puts "#{index} - Finish Initiate at #{finish}"
driver.get "http://google.com"
sleep(10)
driver.quit
puts "#{index} - Finished"
}
i = i + 1
puts "Thread - #{i} Created"
end # end if
end # end while
th.each{|t|
if(!t.nil?)
t.join
end
}
Did I code it properly? or it's firefox limitation? or selenium?
Note :
When I remove the thread it's take lesser time (about 6 s until navigate to intended URL)
it's work great using chrome driver

Timeout if no user input for `x` seconds

How would I program a chunk of code in Ruby, which would timeout or exit if no user input is entered for x amount of time?
I don't have a half completed script too better convey my question, or even a pseudo-code concept algorithm.
You can use the Timeout module that is included in the standard libraries. It will raise a Timeout::Error on timeout if you want to rescue it.
require 'timeout'
x = 10
begin
status = Timeout::timeout(x) {
printf "Input: "
gets
}
puts "Got: #{status}"
rescue Timeout::Error
puts "Input timed out after #{x} seconds"
end
require "timeout"
Timeout.timeout(x) do
s = gets
...
end

Put contents of array all at once

I don't understand why this won't do what the title states.
#!/usr/bin/env ruby
require 'socket'
require 'timeout'
class Scanner
def initialize(host, port)
#host = host
#port = port
end
def popen
begin
array = []
sock = Socket.new(:INET, :STREAM)
sockaddr = Socket.sockaddr_in(#port, #host)
Timeout::timeout(5) do
array.push("Port #{#port}: Open") if sock.connect(sockaddr)
end
puts array
rescue Timeout::Error
puts "Port #{#port}: Filtered"
rescue Errno::ECONNREFUSED
end
end
end # end Scanner
def main
begin
p = 1
case ARGV[0]
when '-p'
eport = ARGV[1]
host = ARGV[2]
else
eport = 65535
host = ARGV[0]
end
t1 = Time.now
puts "\n"
puts "-" * 70
puts "Scanning #{host}..."
puts "-" * 70
while p <= eport.to_i do
scan = Scanner.new(host, p)
scan.popen
p += 1
end
t2 = Time.now
time = t2 - t1
puts "\nScan completed: #{host} scanned in #{time} seconds."
rescue Errno::EHOSTUNREACH
puts "This host appears to be unreachable"
rescue Interrupt
puts "onnection terminated."
end
end
main
What I'm trying to achieve is an output similar to nmap, in the way that it scans everything, and then shows all open or closed ports at the end. Instead what happens is that it prints them out as it discovers them. I figured pushing the output into an array then printing the array would achieve such an output, yet it still prints out the ports one at a time. Why is this happening?
Also, I apologize for the formatting, the code tags are a little weird.
Your loop calls popen once per iteration. Your popen method sets array = [] each time it is called, then populates it with one item, then you print it with puts. On the next loop iteration, you reset array to [] and do it all again.
You only asked "why," but – you could solve this by setting array just once in the body of main and then passing it to popen (or any number of ways).

Ruby Pause thread

In ruby, is it possible to cause a thread to pause from a different concurrently running thread.
Below is the code that I've written so far. I want the user to be able to type 'pause thread' and the sample500 thread to pause.
#!/usr/bin/env ruby
# Creates a new thread executes the block every intervalSec for durationSec.
def DoEvery(thread, intervalSec, durationSec)
thread = Thread.new do
start = Time.now
timeTakenToComplete = 0
loopCounter = 0
while(timeTakenToComplete < durationSec && loopCounter += 1)
yield
finish = Time.now
timeTakenToComplete = finish - start
sleep(intervalSec*loopCounter - timeTakenToComplete)
end
end
end
# User input loop.
exit = nil
while(!exit)
userInput = gets
case userInput
when "start thread\n"
sample500 = Thread
beginTime = Time.now
DoEvery(sample500, 0.5, 30) {File.open('abc', 'a') {|file| file.write("a\n")}}
when "pause thread\n"
sample500.stop
when "resume thread"
sample500.run
when "exit\n"
exit = TRUE
end
end
Passing Thread object as argument to DoEvery function makes no sense because you immediately overwrite it with Thread.new, check out this modified version:
def DoEvery(intervalSec, durationSec)
thread = Thread.new do
start = Time.now
Thread.current["stop"] = false
timeTakenToComplete = 0
loopCounter = 0
while(timeTakenToComplete < durationSec && loopCounter += 1)
if Thread.current["stop"]
Thread.current["stop"] = false
puts "paused"
Thread.stop
end
yield
finish = Time.now
timeTakenToComplete = finish - start
sleep(intervalSec*loopCounter - timeTakenToComplete)
end
end
thread
end
# User input loop.
exit = nil
while(!exit)
userInput = gets
case userInput
when "start thread\n"
sample500 = DoEvery(0.5, 30) {File.open('abc', 'a') {|file| file.write("a\n")} }
when "pause thread\n"
sample500["stop"] = true
when "resume thread\n"
sample500.run
when "exit\n"
exit = TRUE
end
end
Here DoEvery returns new thread object. Also note that Thread.stop called inside running thread, you can't directly stop one thread from another because it is not safe.
You may be able to better able to accomplish what you are attempting using Ruby Fiber object, and likely achieve better efficiency on the running system.
Fibers are primitives for implementing light weight cooperative
concurrency in Ruby. Basically they are a means of creating code
blocks that can be paused and resumed, much like threads. The main
difference is that they are never preempted and that the scheduling
must be done by the programmer and not the VM.
Keeping in mind the current implementation of MRI Ruby does not offer any concurrent running threads and the best you are able to accomplish is a green threaded program, the following is a nice example:
require "fiber"
f1 = Fiber.new { |f2| f2.resume Fiber.current; while true; puts "A"; f2.transfer; end }
f2 = Fiber.new { |f1| f1.transfer; while true; puts "B"; f1.transfer; end }
f1.resume f2 # =>
# A
# B
# A
# B
# .
# .
# .

Resources