[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

fsmgen 0.1

Mark Probert

11/2/2004 8:53:00 PM

Hi, all.

I am pleased to announce the release of fsmgen. This is a class library
and generator for creating simple finite state machine. The application
is to make simple TCP / UDP servers easy to write. This is done using a
YAML file to define the server and then using a generator to create a
default server and client template. These templates are then modified to
add "real" responses, rather than the default of sending strings back.

An example of a simple config file is:

Server:
name: SimpleSvr # we need a name
type: TCP # default server type [TCP|UDP]
port: 8045 # mandatory field

Events:
HELO: A client wants to connect
CMD: The client sends in a command

Actions:
INIT: [ init, "Initializing the system." ]
ACK: [ acknowledge, "HELO" ]
LIST: [ list, "Show available commands" ]
RUN: [ run, "Run a command" ]

States:
Start: [ INIT, Idle ]
Idle:
HELO: [ ACK, Ready ]
CMD:
Ready:
CMD: [ RUN, Idle ]
LIST: [ LIST, Ready ]
HELO:


I don't have a website up at the moment, so, if you are interested,
please contact me for the code.

Regards,
-mark. (probertm at acm dot org)
13 Answers

Ara.T.Howard

11/2/2004 9:19:00 PM

0

Carl Youngblood

11/3/2004 12:24:00 AM

0

Why don't you post it on rubyforge? Plenty of free space there
(thanks to those who are paying for it). This sounds like a great
tool!

On Wed, 3 Nov 2004 05:53:48 +0900, Mark Probert <probertm@nospam-acm.org> wrote:
> Hi, all.
>
> I am pleased to announce the release of fsmgen. This is a class library
> and generator for creating simple finite state machine. The application
> is to make simple TCP / UDP servers easy to write. This is done using a
> YAML file to define the server and then using a generator to create a
> default server and client template. These templates are then modified to
> add "real" responses, rather than the default of sending strings back.


Mark Probert

11/3/2004 3:14:00 AM

0

Hi ..

Carl Youngblood <carl.youngblood@gmail.com> wrote:
>
> Why don't you post it on rubyforge? Plenty of free space there
> (thanks to those who are paying for it). This sounds like a great
> tool!
>
Thanks, Carl. I forgot about rubyforge (doh!). I am setting up the
account now and will post when it is done.

-mark.


vruz

11/3/2004 3:49:00 AM

0

> I am pleased to announce the release of fsmgen. This is a class library
> and generator for creating simple finite state machine. The application
> is to make simple TCP / UDP servers easy to write. This is done using a
> YAML file to define the server and then using a generator to create a
> default server and client template. These templates are then modified to
> add "real" responses, rather than the default of sending strings back.


Great work, this is a nice addition that could well be (after polished
and debugged to death, of course) part of the stdlib.

That YAML code is the FSM definition, now...
how do you jump from one state to another ?
all in Ruby code ?

Taking as an example this vending machine (sorry, in C# + XML)
http://www.codeproject.com/csharp/...

What would be the corresponding code in Ruby + YAML ?
Thanks for your work,

best,

vruz


Mark Probert

11/3/2004 4:44:00 AM

0

Hi ..

vruz <horacio.lopez@gmail.com> wrote:
>
> Great work, this is a nice addition that could well be (after polished
> and debugged to death, of course) part of the stdlib.
>
Thank you. It needs polishing and debugging, that is for sure. It does
work, though I am not 100% on all of the failure paths.

> That YAML code is the FSM definition, now...
> how do you jump from one state to another ?
> all in Ruby code ?
>
Fairly easily (Ruby is great for this kind of thing). I take states,
actions and events and create hashes. The actions are keyed off an MD5
hash of state and action (this is really a DFA machine -- for an action,
each of the events must be unique). When an incoming message (event)
matches for a given action, the action callback is fired. By default, this
returns a message on the socket.

> Taking as an example this vending machine (sorry, in C# + XML)
> http://www.codeproject.com/csharp/...
> What would be the corresponding code in Ruby + YAML ?
>
This is in fact the example I have included :-), though I took the example
courtesy of Robert C Martin's article 'UML Tutorial: Finite State
Machines' from C++ Report

http://www.objectmentor.com/publications/...

The example, without comments, is as follows (if you want the comments,
then I can send the package):

# --------------------( turnstile.yaml )
Server:
name: Turnstile # we need a name
type: TCP # default server type [TCP|UDP]
port: 13345 # mandatory field

Events:
COIN: Coin is placed in the turnstile
PASS: The turnstile has been passed
RESET: Turn off the alarm
READY: Ready for normal operation

Actions:
LOCK: [ lock, "The turnstile is now locked." ]
UNLOCK: [ unlock, "The turnstile is now unlocked." ]
THANKS: [ donation, "Thank you for your donation." ]
ALARM_ON: [ alarm_on, "Woop! Woop!" ]
ALARM_OFF: [ alarm_off, "The alarm is now turned off." ]
READY: [ ready, "The system is now ready." ]

States:
Start: [ LOCK, Locked ]
Locked:
COIN: [ UNLOCK, Unlocked ]
PASS: [ ALARM_ON, Violation ]
Unlocked:
COIN: [ THANKS, Unlocked ]
PASS: [ LOCK, Locked ]
Violation:
RESET: [ ALARM_OFF, Violation ]
READY: [ [ ALARM_OFF, LOCK ], Locked ]
PASS:
COIN:

You run 'fsmgen' on the YAML file to produce the server and a simple test
client. The basic server looks like:

# -----------------( Turnstile.rb )
require 'FSM'
class Turnstile < FSM::DFA

# -----( alarm_off )
# Action for event: ALARM_OFF
#
def alarm_off ( params )
@sess.puts "The alarm is now turned off."
end

# -----( unlock )
# Action for event: UNLOCK
#
def unlock ( params )
val = params.to_i
if val < 25
@sess.puts "Not enough bud! (#{val}c) You need 25c."
@change_state = false
else
@sess.puts "The turnstile is now unlocked."
end
end
# ... etc
end
# -----( done )

class TurnstileSvr < FSM::SimpleFSMServer
# -----
# Basic startup
#
def initialize(cfg, verbose=false)
super(cfg, verbose)
end

# -----
# Basic server startup
#
def start
srv = TCPServer.new(@port)
puts "server started"
while (session = srv.accept)
Thread.new(session) do |s|
domian, port, ipname, ipaddr = s.peeraddr
str = "connection from #{ipname}"
puts str; @log.info str
@fsm = Turnstile.new(@config, @log)
@fsm.verbose = @verbose
while true do
event = s.gets.chomp
@log.info "client(#{ipname}) event(#{event})"
@fsm.handle_response(s, event)
end
end
end
end
end # ----------( server )

# ---------------------------------- #
# MAIN -- Server Start #
# ---------------------------------- #

def end_program()
puts " *** Terminating *** "
exit
end

trap("SIGINT") { end_program }
trap("SIGKILL") { end_program }

svr = TurnstileSvr.new("turnstile.yaml", true)
svr.start

# --------------------( done )

And a basic test client like:

# -----------------( Turnstile_client.rb )
require 'socket'

PORT = 13345
HOST = ARGV[0] || "localhost"

def test_events(sess, evts)
evts.each do |evt|
sess.puts evt
s = sess.gets
puts "sent(#{evt}) response --> #{s}"
end
end

puts "Testing:"
session = TCPSocket.new(HOST, PORT)

puts "Correct sequence .."
evts = ["PASS", "RESET", "COIN 25", "PASS", "READY"]
test_events(session, evts)

session.close
puts "...done"

I hope that this helps a little in explaining what I have done.

Regards,
-mark.

vruz

11/3/2004 12:14:00 PM

0

[snip]
> This is in fact the example I have included :-), though I took the example
> courtesy of Robert C Martin's article 'UML Tutorial: Finite State
> Machines' from C++ Report
> The example, without comments, is as follows

Now it's a lot more clear, thank you

>(if you want the comments,
> then I can send the package):

No hurries, I can wait until it's finally publicly published

> I hope that this helps a little in explaining what I have done.
> Regards,
> -mark.

best,
vruz


Eivind Eklund

11/3/2004 4:34:00 PM

0

On Wed, 3 Nov 2004 05:53:48 +0900, Mark Probert <probertm@nospam-acm.org> wrote:
> Hi, all.
>
> I am pleased to announce the release of fsmgen. This is a class library
> and generator for creating simple finite state machine. The application
> is to make simple TCP / UDP servers easy to write. This is done using a
> YAML file to define the server and then using a generator to create a
> default server and client template. These templates are then modified to
> add "real" responses, rather than the default of sending strings back.
>
> An example of a simple config file is:

Wouldn't this be better implemented as meta-programming in Ruby, so
you don't generate Ruby code from it once - that's just
meta-programmed every time you execute the program? It would seem to
be easier to modify things that way (for the case where the initial
state machine wasn't quite right, something I find to happen about 9
times out of 10).

Apart from that, I like that you're helping abstract state machines -
too often, they end up non-normalized and buried.

Eivind.
--
Hazzle free packages for Ruby?
RPA is available from http://www.rubyar...


Mark Probert

11/3/2004 5:07:00 PM

0

Hi ..

Eivind Eklund <eeklund@gmail.com> wrote:
>
> Wouldn't this be better implemented as meta-programming in Ruby, so
> you don't generate Ruby code from it once - that's just
> meta-programmed every time you execute the program?
>
This is a very good point and one I am not sure of the correct design
approach. The problem with the current generator style is that if you do
work on the state machine action specifics, then change the machine, you
have to merge back all the changes. That is a pain.

On the plus side, using a generator gives you a lot more freedom. Don't
like the specifics of the server? Change it. Need to modify some
behaviour on the fly? Not a problem. The only thing hidden is the
event-action selection mechanism.

For me, I tend to have simple state machines, so the config files doesn't
change too much once it settles down.

I am going to be puting the source up on Ruby Forge today under a PD
licence, so you are more than welcome to have a look and modify away. :-)

>
> Apart from that, I like that you're helping abstract state machines -
> too often, they end up non-normalized and buried.
>
Thank you.

Regards,
-mark.

Mark Probert

11/3/2004 6:40:00 PM

0

Hi ..

vruz <horacio.lopez@gmail.com> wrote:
>
> Now it's a lot more clear, thank you
>
You are welcome.

>
> No hurries, I can wait until it's finally publicly published
>
It is now available on Ruby Forge, linked via RAA

http://raa.ruby-lang.org/proje...

Or at

http://www.codeforpeople.com/lib/ru...

Regards,

-mark.

Jim Weirich

11/4/2004 12:59:00 AM

0

On Wednesday 03 November 2004 11:34 am, Eivind Eklund wrote:
> Wouldn't this be better implemented as meta-programming in Ruby,

Funny you should mention this ... I was playing with ruby state machines about
two weeks ago (even using the turnstile example in my unit tests). This is
what I came up with ....

class TurnStileFSM < StateMachine
state_machine do
state :locked do
start_state
event :coin, :unlocked, :unlock
event :pass, :violation, :alarm_on
end
state :unlocked do
event :coin, :unlocked, :thank_you
event :pass, :locked, :lock
end
state :violation do
event :reset, :violation
event :ready, :locked, :alarm_off, :lock
event :pass
event :coin
end
end
end

To use, you just inherit from the FSM and implement the actions (e.g. unlock,
lock, alarm_on). (Or I suppose you could implement the actions directly in
the FSM class).

--
-- Jim Weirich jim@weirichhouse.org http://onest...
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)