I'm trying to use ruby to do a simple read + write operation to a serial port.
This is the code I've got so far. I'm using the serialport gem.
require 'rubygems'
require 'serialport'
ser = SerialPort.new("/dev/ttyACM0", 9600, 8, 1, SerialPort::NONE)
ser.write "ab\r\n"
puts ser.read
But the script hangs when it is run.
I had the problem to. It's because using ser.read tells Ruby to keep reading forever and Ruby never stops reading and thus hangs the script. The solution is to only read a particular amount of characters.
So for example:
ser.readline(5)
To echo what user968243 said, that ser.read call is going to wait for EOF. If your device is not sending EOF, you will wait forever. You can read only a certain number of characters as suggested.
Your device may be ending every response with an end of line character. Try reading up to the next carriage return:
response = ser.readline("\r")
response.chomp!
print "#{response}\n"
I ran into this same problem and found a solution. Use the Ruby IO method readlines.
puts ser.readlines
Maybe your device is waiting for some input. Check this answer and see if it helps: https://stackoverflow.com/a/10534407/1006863
Try setting the #read_timeout value for the serial port. Note that the same thing can be done for a write operation using the #write_timeout value.
Related
I have a console application (command response type) that I want to control its IO so I can feed it with a script from file and then continue the execution manually when the script execution is finished.
my script is this:
ARGF.readlines = commands
IO.popen('ConsoleApplication') { |io|
begin
puts io.gets
sleep 0.1
end while ????
}
As I observed IO.popen connects the input of io object to the process IO but the output should be read. If by some means the output was displayed automatically or I could detect when the process is waiting for input where ???? is I think I could consider this matter as done!
My further workaround results:
Expect do not work on widows because windows do not support pseudo terminals(PTY) so I can't use ruby_expect like libraries for windows!
I tried IO.select in combination with IO.read_nonblock which encounters "bad file pointer". Some people said that non-blocking IO is not supported in windows as well
So I used some special strings to synchronize the read-write sequences myself and the task is done.
I can't figure out why IO methods won't work on STDIN, when properly set to non-blocking mode:
require 'fcntl'
stdin_flags = STDIN.fcntl(Fcntl::F_GETFL)
p stdin_flags #32770
p STDIN.fcntl(Fcntl::F_SETFL, stdin_flags | Fcntl::O_NONBLOCK) # 0
p STDIN.fcntl(Fcntl::F_GETFL) # 34818
#at_exit { STDIN.fcntl(Fcntl::F_SETFL, stdin_flags & ~Fcntl::O_NONBLOCK) }
STDIN.readline # this call blocks, IO::EAGAINWaitReadable expected
exit
IO.fcntl successfully sets non-blocking mode but all IO functions like read, readline, gets, readchar ignore the mode and hang at reading when no input has been received.
Setting sync mode to true has no effect.
If I replace STDIN.readline with the shell call system('read line') it does work correctly. It won't wait or would wait for input depending if non-blocking mode was set.
I'm aware of IO.read_nonblock but looking for an efficient way how to read newline terminated strings. Calling read_nonblock for each single character is painfully slow.
Can anybody explain this (mis)behavior?
It's a bit unfortunate but standard functions from IO module seem to not respect status flags associated with a file descriptor.
One of the working solutions is use of IO.select class method for input polling, then read the data with regular methods as they become available.
Be aware when line processing methods are used, code may hang until terminating newline character gets consumed. It's advisable to enclose polling code in Timeout block when things are going out of control.
In cases when amount of characters/bytes is known beforehand, stock IO.read_nonblock would simply serve well.
I would like to do 2 things.
Recognize when someone is calling - In terminal will appear RING and to answer I have to send command ATA. But How can I recognize it when I am doing something else. Should I use new thread and read port until send RING? Is there any beter solution?
What is a symbol of end of response? I'm reading char using for(), but I do not know number of signs. Example below doesn't work properly
while(readCharUART()!=10) {};
while(readCharUART()!=13)
{
getchar() = ..
}
You are on the right track.
For RING then yes, the correct way to do it is to have one thread just read modem responses until you get the Unsolicited result code RING. If you from time to time want to run AT commands (say ATA), then you should let this thread do that as well, e.g. you have one thread that takes care of both issuing AT commands and monitor for UR codes.
Regarding formatting of responses from the modem, this is well described in chapter 5.7.1 Responses in the ITU V.250 standard. Short summary (reading the spec is highly recommended!):
<header>RING<trailer>
where header and trailer is both "\r\n" (unless the modem is configured strangely).
I'm writing a snake game for the terminal, i.e. output via print.
The following works just fine:
while status[snake_monad] do
print to_string draw canvas, compose_all([
frame,
specs,
snake_to_hash(snake[snake_monad])
])
turn! snake_monad, get_dir
move! snake_monad, specs
sleep 0.25
end
But I don't want the turn!ing to block, of course. So I put it into a new Thread and let it loop:
Thread.new do
loop do
turn! snake_monad, get_dir
end
end
while status[snake_monad] do
...
# no turn! here
...
end
Which also works logically (the snake is turning), but the output is somehow interspersed with newlines. As soon as I kill the input thread (^C) it looks normal again.
So why and how does the thread have any effect on my output?
And how do I work around this issue? (I don't know much about threads, even less about them in ruby. Input and output concurrently on the same terminal make the matter worse, I guess...)
Also (not really important): Wanting my program as pure as possible, would it be somewhat easily possible to get the input non-blockingly while passing everything around?
Thank you!
You don't want non-blocking IO - you want unbuffered IO.
There's no need for threads, here - you just need to put your terminal into the right mode and then wait for the keypress on the main event loop.
Here's a completely ridiculous example.
require 'io/wait'
def ping
term = `stty -g`
`stty raw -echo cbreak`
loop do
if STDIN.ready?
#command thy snake!
ret = STDIN.getc
end
if ret
#process the snake command if there was one
STDOUT.write("you told the snake to #{ret}\n")
else
#slither around bitin' fools and hustling apples.
end
end
ensure
`stty #{term}`
end
I found a dirty solution:
I just had to add a carriage return (\r) after each newline in the to_string function, so everything that gets printed after it will be overwritten. Since it was a newline, it loses its effect. Everything seems fine now.
But I'd much rather know why this happens and fix it (if possible) cleanly.
Greetings, all,
I need to run a potentially long-running process from Ruby 1.9.2 on Windows and subsequently capture and parse the data from the external process's standard output and error. A large amount of data can be sent to each, but I am only necessarily interested in one line at a time (not capturing and storing the whole of the output).
After a bit of research, I found that the Open3 class would take care of executing the process and giving me IO objects connected to the process's standard output and error (via popen3).
Open3.popen3("external-program.bat") do |stdin, out, err, thread|
# Step3.profit() ?
end
However, I'm not sure how to continually read from both streams without blocking the program. Since calling IO#readlines on out or err when a lot of data has been sent results in a memory allocation error, I'm trying to continuously check both streams for available input, but not having much luck with any of my implementations.
Thanks in advance for any advice!
After a lot of different trial and error attempts, I eventually came up with using two threads, one to read from each stream (generator.rb is just a script I wrote to output things to standard out and err):
require 'open3'
data = {}
Open3.popen3("ruby generator.rb") do |stdin, out, err, external|
# Create a thread to read from each stream
{ :out => out, :err => err }.each do |key, stream|
Thread.new do
until (line = stream.gets).nil? do
data[key] = line
end
end
end
# Don't exit until the external process is done
external.join
end
puts data[:out]
puts data[:err]
It simply outputs the last line sent to standard output and error by the calling program, but could obviously be extended to do additional processing (with different logic in each thread). A method I was using before I finally came up with this was resulting in some failures due to race conditions; I don't know if this code is still vulnerable, but I've yet to experience a similar failure.