# Command queue structure help?

**URL:** <https://www.ruby-forum.com/t/command-queue-structure-help/225959>\
**Category:** Ruby\
**Created:** [July 25, 2007, 8:45am UTC](https://www.ruby-forum.com/t/command-queue-structure-help/225959 "2007-07-25T08:45:55Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![mouse\_059](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e95f7d/32.png) [@mouse\_059](https://www.ruby-forum.com/u/mouse_059)\
**Post date:** [July 25, 2007, 8:45am UTC](https://www.ruby-forum.com/t/command-queue-structure-help/225959/1 "2007-07-25T08:45:55Z")

</div>

Greetings. I have been studying Ruby, have the pickaxe and Ruby Way  
(Fulton). Studying for a long time as I am a diehard C/assembly  
programmer and it is very hard to get out of the data-driven mindset.  
I believe to have finally found my killer application for Ruby (not  
rails, or the web.) I am however unfamiliar with OOP-isms and am  
trying to come to grips with them. Any assistance or direction anyone  
could help me with, I appreciate!

PS: The first time I coded this, it worked without a hitch. A real  
testament to Ruby.

The application: Picture a grid of NTSC monitors controlled by Apple  
II computers. Each Apple II has a serial card in it, hooked up to a  
portmaster, connected to a LAN and showing up on the local system (the  
Console) as virtual ttys a la /dev/ttyr1, etc. The Apples are running  
custom assembly routines, triggered by a write to the serial port from  
the Console to the tty port. (I figured Ruby wouldn’t be able to  
handle this kind of IO. Dead wrong, me!) The routines make the Apple  
monitor do different things - I can Fill the screen with a letter,  
Draw a horizontal or vertical line, Display a compressed image stored  
on the machine, etc. For synchronization, when the Apple is done, it  
sends a ACK bit to the serial port.

Here is what I have now, that works:  
#name #port #y value #x value  
MATRIX1 =[  
[“iip1”, “/dev/ttyr2”, 0, 0],  
[“iip2”, “/dev/ttyr3”, 0, 1],  
[“iic”, “/dev/ttyr1”, 0, 2] ]

MATRIX2 = [[“iie”, “/dev/ttyr5”] ]

MATRIXES = [MATRIX1, MATRIX2]

class Matrixes is a simple array (@store = []) and holds each  
DisplayMatrix. Each DisplayMatrix has its own group of computers and  
may have different properties - for example the first three machines  
are black and white, and in a row, so they make up a x,y display (40 \*  
3) by (24 \* 1). The second matrix has one machine in it and is  
color. A visualization can receive any number of these matrixes.

class Matrixes  
def initialize  
@store = []  
end

def \<\<(data)  
@store \<\< data  
end

def each  
@store.each{ |mx| yield mx }  
end

def flush\_and\_wait  
threads = []  
@store.each{ |mx| threads \<\< Thread.new{ mx.flush\_and\_wait } }  
threads.each{ |t| t.join }  
end  
end

class DisplayMatrix holds an array of machines and has routines to  
flush an entire matrix, and wait for all machines to be finished.

class DisplayMatrix  
def initialize(machines)  
@machines = machines  
end

def flush\_and\_wait  
threads = []  
@machines.each{ |m| threads \<\< Thread.new{ m.flush\_and\_wait } }  
threads.each{ |t| t.join }  
end

class Machine is one machine, and does all the communication with the  
serial port and keeps the object in a current state. It receives  
command requests and stores them on a queue.

class Machine  
def initialize(name, port)  
@name = name  
@dev = open\_and\_init(port) # calls open, also uses Termios library

```
@q = []
@q.extend(MonitorMixin)

```

end

def enqueue(data)  
@q.synchronize{ @q \<\< data }  
end

def flush\_and\_wait  
@q.synchronize do  
@q.each{ |q| @dev.syswrite(q) }  
@q.clear

```
  readyfd = select([@dev], nil, nil)
  raise "select on our fd didn't yield it?" if not

```

readyfd.flatten.include? @dev  
@dev.sysread(100) #read & toss acknowlegement, right now simply a  
SPACE  
end  
end

Here is how I have a visualization currently. You will see I can make  
a visualization with custom parameters - in this case how many times  
to loop it. Other variables I can think of are how long to flash it,  
which character to flash it with, etc. So I must “create” a new  
FlashVis object based on custom parameters and how I want to run it.

Queueing the letter F, and then an ascii SPACE, makes that Apple II  
have a white on black screen.  
Queueing the letter F, and then hex A0, blanks the Apple II screen (it  
is an apple II normal space).

When I do a flush on the entire visualization matrix I wait for all  
Apples to be done. This creates O(n) threads as you can see, but  
seems to have negligable performance impact.

# BlinkVis simply blinks the Apple II screens

class BlinkVis  
def initialize(count)  
@count = count  
end

def run(mxs)  
return Thread.new do  
@count.times do  
mxs.each{ |mx| mx.queue\_to\_all("F ") }  
mxs.flush\_and\_wait

```
    mxs.each{ |mx| mx.queue_to_all("F\xa0") }
    mxs.flush_and_wait
  end
end

```

end  
end

And then the main program where you can see all the objects created  
cleanly:  
begin  
mxs = Matrixes.new

MATRIXES.each do |matrix\_def|  
ms = []  
matrix\_def.each{ |name, port| ms \<\< Machine.new(name, port) }  
mxs \<\< DisplayMatrix.new(ms)  
end

vis = BlinkVis.new(3)  
while true do  
thr = vis.run(mxs)  
thr.join  
end  
end

\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* (end of program code)  
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*8

A few things are apparent about this and I must say - FIrst of all  
this code does exactly what my C code does with about 10x less the  
amount of lines due to ruby’s excellent hierarchical error passing and  
excellent array support, as you can imagine eliminating each “if fcntl  
\< 0 then error… if tcgetattr \< 0 then error… if select \< 0 then  
error… if read \< 0 then error…” took a crap load of code away.  
Secondly the code is extremely easy to parse.

Now that it does what my server coded in C does, I would like to  
know: What is the best way to abstract away the “intention” of the  
command from the “actual” command I’m sending to the Apple? (eg,

machine.enqueue( FLASH, A2\_SPACE | A2\_INVERSE ) instead of  
machine.enqueue(“F\xa0”)  
machine.enqueue( FLASH, A2\_SPACE | A2\_NORMAL ) instead of  
machine.enqueue("F ")

EG: The 6502 code also prints vertical lines “V” and horizontal lines  
“H” with three parameters: x from 0 to 39, y from 0 to 23, and  
length. It prints a compressed text screen block “E” with parameters  
which char to use for the light bits, which char for the dark bits,  
and the block. It will make a kaleidoscope in lo-res graphics by  
passing “K”. I can picture many more modes I will program in 6502 -  
for example, printing text in varying sizes and fonts on the hi-res  
screen,  
etc.

machine.enqueue( KALEIDOSCOPE, optional starting\_color )  
machine.enqueue( HLINE, starty, startx, length, optional color )  
machine.enqueue( TEXT, “The quick brown fox”, FONT\_BLA, 16 )

So with all these different commands, and parameters, what is the best  
way to “enqueue” one of these commands to the machine for sending/  
updating? And I would like to be able to handle different kinds of  
Machine in the future - it could be an Apple II, or it could be a dumb  
terminal, or possibly a NES system with custom cartridge & serial  
connection. In each case the output to the machine would be  
different. The way I have done it so far seems to imply I can do what  
I am asking here. (And a big pain in the butt in C, which is why I  
stopped and did this!!) Ruby example code would be helpful!

PS: If any of you are II fans and would like to use this I will be  
puting a webpage together with the assembly and all necessary  
materials. II Infinitum!

-m

---

<div class="post-metadata">

**Author:** ![mouse\_059](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e95f7d/32.png) [@mouse\_059](https://www.ruby-forum.com/u/mouse_059)\
**Post date:** [July 25, 2007, 4:55pm UTC](https://www.ruby-forum.com/t/command-queue-structure-help/225959/2 "2007-07-25T16:55:50Z")

</div>

# you could do this

def enqueue( command, \*args )  
data =  
case command  
when KALEIDOSCOPE then kaleidoscope( \*args )  
when HLINE then hline( \*args )  
else fail “unknown command”  
end

@q.synchronize{ @q \<\< data }  
end

#but then why wouldn’t you just write  
machine.enqueue( kaleidoscope( optional\_starting\_color ) )

# optional argument

def kaleidosope( starting\_color=WHITE )

# …

end

#also you may want to simplify your code  
class Matrixes \<\< Array  
def flush\_and\_wait  
threads = []  
each{ |mx| threads \<\<  
Thread.new{ mx.flush\_and\_wait } }  
threads.each{ |t| t.join }  
end  
end
